summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk276
-rw-r--r--AndroidManifest.xml437
-rw-r--r--CONTRIBUTING27
-rw-r--r--InCallUI/AndroidManifest.xml24
-rw-r--r--InCallUI/build.gradle14
-rw-r--r--InCallUI/proguard.flags14
-rw-r--r--InCallUI/res/anim/activity_open_enter.xml35
-rw-r--r--InCallUI/res/anim/activity_open_exit.xml26
-rw-r--r--InCallUI/res/anim/call_status_pulse.xml22
-rw-r--r--InCallUI/res/color/selectable_icon_tint.xml24
-rw-r--r--InCallUI/res/drawable-hdpi/fab_blue.pngbin2805 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/fab_ic_call.pngbin875 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/fab_ic_end_call.pngbin852 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/fab_ic_message.pngbin617 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/fab_red.pngbin2783 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_business_white_24dp.pngbin152 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_call_white_24dp.pngbin451 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_lockscreen_glowdot.pngbin738 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_question_mark.pngbin941 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_toolbar_add_call.pngbin1230 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_toolbar_arrow_whitespace.pngbin489 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_toolbar_audio_bluetooth.pngbin833 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_toolbar_audio_headphones.pngbin1142 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_toolbar_audio_phone.pngbin1301 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_toolbar_dialpad.pngbin624 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_toolbar_hold.pngbin511 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_toolbar_merge.pngbin772 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_toolbar_mic_off.pngbin1155 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_toolbar_speaker_on.pngbin1118 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_toolbar_swap.pngbin1110 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_toolbar_video.pngbin711 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_toolbar_video_off.pngbin932 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-hdpi/ic_toolbar_video_switch.pngbin972 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-land/rounded_call_card_background.xml23
-rw-r--r--InCallUI/res/drawable-mdpi/fab_blue.pngbin1841 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/fab_ic_call.pngbin698 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/fab_ic_end_call.pngbin668 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/fab_ic_message.pngbin561 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/fab_red.pngbin1843 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_business_white_24dp.pngbin105 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_call_white_24dp.pngbin348 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_lockscreen_glowdot.pngbin538 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_question_mark.pngbin619 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_toolbar_add_call.pngbin883 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_toolbar_arrow_whitespace.pngbin431 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_toolbar_audio_bluetooth.pngbin630 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_toolbar_audio_headphones.pngbin885 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_toolbar_audio_phone.pngbin921 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_toolbar_dialpad.pngbin527 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_toolbar_hold.pngbin455 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_toolbar_merge.pngbin669 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_toolbar_mic_off.pngbin822 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_toolbar_speaker_on.pngbin847 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_toolbar_swap.pngbin808 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_toolbar_video.pngbin607 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_toolbar_video_off.pngbin797 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/ic_toolbar_video_switch.pngbin776 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/fab_blue.pngbin4085 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/fab_ic_call.pngbin1266 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/fab_ic_end_call.pngbin1215 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/fab_ic_message.pngbin795 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/fab_red.pngbin4047 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_business_white_24dp.pngbin112 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_call_white_24dp.pngbin535 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_lockscreen_glowdot.pngbin964 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_question_mark.pngbin1170 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_toolbar_add_call.pngbin1549 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_toolbar_arrow_whitespace.pngbin543 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_toolbar_audio_bluetooth.pngbin882 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_toolbar_audio_headphones.pngbin1479 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_toolbar_audio_phone.pngbin1837 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_toolbar_dialpad.pngbin709 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_toolbar_hold.pngbin565 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_toolbar_merge.pngbin921 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_toolbar_mic_off.pngbin1454 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_toolbar_speaker_on.pngbin1505 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_toolbar_swap.pngbin1487 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_toolbar_video.pngbin830 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_toolbar_video_off.pngbin1160 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/ic_toolbar_video_switch.pngbin1120 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/fab_blue.pngbin7009 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/fab_ic_call.pngbin2320 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/fab_ic_end_call.pngbin2227 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/fab_ic_message.pngbin1556 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/fab_red.pngbin6965 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_business_white_24dp.pngbin119 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_call_white_24dp.pngbin750 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_lockscreen_glowdot.pngbin1907 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_question_mark.pngbin1774 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_toolbar_add_call.pngbin1874 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_toolbar_arrow_whitespace.pngbin1188 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_toolbar_audio_bluetooth.pngbin1528 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_toolbar_audio_headphones.pngbin1858 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_toolbar_audio_phone.pngbin2285 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_toolbar_dialpad.pngbin1449 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_toolbar_hold.pngbin1143 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_toolbar_merge.pngbin1385 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_toolbar_mic_off.pngbin1956 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_toolbar_speaker_on.pngbin2065 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_toolbar_swap.pngbin1970 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_toolbar_video.pngbin1347 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_toolbar_video_off.pngbin1538 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxhdpi/ic_toolbar_video_switch.pngbin1534 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/fab_blue.pngbin9807 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/fab_ic_end_call.pngbin2567 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/fab_ic_message.pngbin1850 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/fab_red.pngbin9802 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_business_white_24dp.pngbin114 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_question_mark.pngbin2370 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_toolbar_add_call.pngbin2271 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_toolbar_arrow_whitespace.pngbin1262 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_toolbar_audio_bluetooth.pngbin1728 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_toolbar_audio_headphones.pngbin2158 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_toolbar_audio_phone.pngbin2830 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_toolbar_dialpad.pngbin1651 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_toolbar_hold.pngbin1179 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_toolbar_merge.pngbin1444 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_toolbar_mic_off.pngbin2284 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_toolbar_speaker_on.pngbin2532 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_toolbar_swap.pngbin2370 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_toolbar_video.pngbin1394 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_toolbar_video_off.pngbin1703 -> 0 bytes
-rw-r--r--InCallUI/res/drawable-xxxhdpi/ic_toolbar_video_switch.pngbin1610 -> 0 bytes
-rw-r--r--InCallUI/res/drawable/btn_add.xml30
-rw-r--r--InCallUI/res/drawable/btn_background.xml33
-rw-r--r--InCallUI/res/drawable/btn_change_to_video.xml31
-rw-r--r--InCallUI/res/drawable/btn_change_to_voice.xml31
-rw-r--r--InCallUI/res/drawable/btn_compound_audio.xml93
-rw-r--r--InCallUI/res/drawable/btn_compound_background.xml35
-rw-r--r--InCallUI/res/drawable/btn_compound_dialpad.xml32
-rw-r--r--InCallUI/res/drawable/btn_compound_hold.xml32
-rw-r--r--InCallUI/res/drawable/btn_compound_mute.xml31
-rw-r--r--InCallUI/res/drawable/btn_compound_video_off.xml33
-rw-r--r--InCallUI/res/drawable/btn_compound_video_switch.xml33
-rw-r--r--InCallUI/res/drawable/btn_merge.xml30
-rw-r--r--InCallUI/res/drawable/btn_overflow.xml30
-rw-r--r--InCallUI/res/drawable/btn_selected.xml25
-rw-r--r--InCallUI/res/drawable/btn_selected_focused.xml29
-rw-r--r--InCallUI/res/drawable/btn_swap.xml30
-rw-r--r--InCallUI/res/drawable/btn_unselected.xml25
-rw-r--r--InCallUI/res/drawable/btn_unselected_focused.xml28
-rw-r--r--InCallUI/res/drawable/conference_ripple.xml25
-rw-r--r--InCallUI/res/drawable/end_call_background.xml25
-rw-r--r--InCallUI/res/drawable/ic_incall_audio_handle.xml40
-rw-r--r--InCallUI/res/drawable/ic_incall_video_handle.xml41
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_answer.xml27
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_answer_activated_layer.xml25
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_answer_normal_layer.xml33
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_answer_video.xml28
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_answer_video_activated_layer.xml26
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_answer_video_normal_layer.xml34
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_decline.xml27
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_decline_activated_layer.xml24
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_decline_normal_layer.xml32
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_decline_video.xml28
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_decline_video_activated_layer.xml26
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_decline_video_normal_layer.xml34
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_outerring.xml22
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_text.xml27
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_text_activated_layer.xml32
-rw-r--r--InCallUI/res/drawable/ic_lockscreen_text_normal_layer.xml33
-rw-r--r--InCallUI/res/drawable/incoming_sms_background.xml25
-rw-r--r--InCallUI/res/drawable/outgoing_sms_background.xml25
-rw-r--r--InCallUI/res/drawable/spam_notification_icon.xml31
-rw-r--r--InCallUI/res/drawable/subject_bubble.xml22
-rw-r--r--InCallUI/res/drawable/unknown_notification_icon.xml31
-rw-r--r--InCallUI/res/layout-h400dp/call_card_fragment.xml172
-rw-r--r--InCallUI/res/layout-h600dp/manage_conference_call_button.xml61
-rw-r--r--InCallUI/res/layout-w500dp-land/call_card_fragment.xml158
-rw-r--r--InCallUI/res/layout-w600dp-land/manage_conference_call_button.xml61
-rw-r--r--InCallUI/res/layout/accessible_answer_fragment.xml104
-rw-r--r--InCallUI/res/layout/answer_fragment.xml42
-rw-r--r--InCallUI/res/layout/business_contact_context_list_header.xml40
-rw-r--r--InCallUI/res/layout/business_context_info_list_item.xml48
-rw-r--r--InCallUI/res/layout/call_button_fragment.xml171
-rw-r--r--InCallUI/res/layout/call_card_fragment.xml158
-rw-r--r--InCallUI/res/layout/caller_in_conference.xml116
-rw-r--r--InCallUI/res/layout/conference_manager_fragment.xml36
-rw-r--r--InCallUI/res/layout/incall_dialpad_fragment.xml24
-rw-r--r--InCallUI/res/layout/incall_screen.xml23
-rw-r--r--InCallUI/res/layout/manage_conference_call_button.xml72
-rw-r--r--InCallUI/res/layout/outgoing_call_animation.xml22
-rw-r--r--InCallUI/res/layout/person_context_info_list_item.xml40
-rw-r--r--InCallUI/res/layout/primary_call_info.xml231
-rw-r--r--InCallUI/res/layout/secondary_call_info.xml105
-rw-r--r--InCallUI/res/layout/video_call_fragment.xml28
-rw-r--r--InCallUI/res/layout/video_call_views.xml66
-rw-r--r--InCallUI/res/menu/incall_audio_mode_menu.xml39
-rw-r--r--InCallUI/res/values-af/strings.xml195
-rw-r--r--InCallUI/res/values-am/strings.xml195
-rw-r--r--InCallUI/res/values-ar/strings.xml195
-rw-r--r--InCallUI/res/values-az/strings.xml195
-rw-r--r--InCallUI/res/values-b+sr+Latn/strings.xml195
-rw-r--r--InCallUI/res/values-be/strings.xml195
-rw-r--r--InCallUI/res/values-bg/strings.xml195
-rw-r--r--InCallUI/res/values-bn/strings.xml195
-rw-r--r--InCallUI/res/values-bs/strings.xml195
-rw-r--r--InCallUI/res/values-ca/strings.xml195
-rw-r--r--InCallUI/res/values-cs/strings.xml195
-rw-r--r--InCallUI/res/values-da/strings.xml195
-rw-r--r--InCallUI/res/values-de/strings.xml195
-rw-r--r--InCallUI/res/values-el/strings.xml195
-rw-r--r--InCallUI/res/values-en-rAU/strings.xml195
-rw-r--r--InCallUI/res/values-en-rGB/strings.xml195
-rw-r--r--InCallUI/res/values-en-rIN/strings.xml195
-rw-r--r--InCallUI/res/values-es-rUS/strings.xml195
-rw-r--r--InCallUI/res/values-es/strings.xml195
-rw-r--r--InCallUI/res/values-et/strings.xml195
-rw-r--r--InCallUI/res/values-eu/strings.xml195
-rw-r--r--InCallUI/res/values-fa/strings.xml195
-rw-r--r--InCallUI/res/values-fi/strings.xml195
-rw-r--r--InCallUI/res/values-fr-rCA/strings.xml195
-rw-r--r--InCallUI/res/values-fr/strings.xml195
-rw-r--r--InCallUI/res/values-gl/strings.xml195
-rw-r--r--InCallUI/res/values-gu/strings.xml195
-rw-r--r--InCallUI/res/values-h400dp/dimens.xml31
-rw-r--r--InCallUI/res/values-hi/strings.xml195
-rw-r--r--InCallUI/res/values-hr/strings.xml195
-rw-r--r--InCallUI/res/values-hu/strings.xml195
-rw-r--r--InCallUI/res/values-hy/strings.xml195
-rw-r--r--InCallUI/res/values-in/strings.xml195
-rw-r--r--InCallUI/res/values-is/strings.xml195
-rw-r--r--InCallUI/res/values-it/strings.xml195
-rw-r--r--InCallUI/res/values-iw/strings.xml195
-rw-r--r--InCallUI/res/values-ja/strings.xml195
-rw-r--r--InCallUI/res/values-ka/strings.xml195
-rw-r--r--InCallUI/res/values-kk/strings.xml195
-rw-r--r--InCallUI/res/values-km/strings.xml195
-rw-r--r--InCallUI/res/values-kn/strings.xml195
-rw-r--r--InCallUI/res/values-ko/strings.xml195
-rw-r--r--InCallUI/res/values-ky/strings.xml195
-rw-r--r--InCallUI/res/values-lo/strings.xml195
-rw-r--r--InCallUI/res/values-lt/strings.xml195
-rw-r--r--InCallUI/res/values-lv/strings.xml195
-rw-r--r--InCallUI/res/values-mk/strings.xml195
-rw-r--r--InCallUI/res/values-ml/strings.xml195
-rw-r--r--InCallUI/res/values-mn/strings.xml195
-rw-r--r--InCallUI/res/values-mr/strings.xml195
-rw-r--r--InCallUI/res/values-ms/strings.xml195
-rw-r--r--InCallUI/res/values-my/strings.xml195
-rw-r--r--InCallUI/res/values-nb/strings.xml195
-rw-r--r--InCallUI/res/values-ne/strings.xml195
-rw-r--r--InCallUI/res/values-nl/strings.xml195
-rw-r--r--InCallUI/res/values-pa/strings.xml195
-rw-r--r--InCallUI/res/values-pl/strings.xml195
-rw-r--r--InCallUI/res/values-pt-rBR/strings.xml195
-rw-r--r--InCallUI/res/values-pt-rPT/strings.xml195
-rw-r--r--InCallUI/res/values-pt/strings.xml195
-rw-r--r--InCallUI/res/values-ro/strings.xml195
-rw-r--r--InCallUI/res/values-ru/strings.xml195
-rw-r--r--InCallUI/res/values-si/strings.xml195
-rw-r--r--InCallUI/res/values-sk/strings.xml195
-rw-r--r--InCallUI/res/values-sl/strings.xml195
-rw-r--r--InCallUI/res/values-sq/strings.xml195
-rw-r--r--InCallUI/res/values-sr/strings.xml195
-rw-r--r--InCallUI/res/values-sv/strings.xml195
-rw-r--r--InCallUI/res/values-sw/strings.xml195
-rw-r--r--InCallUI/res/values-sw360dp/dimens.xml35
-rw-r--r--InCallUI/res/values-sw410dp/config.xml21
-rw-r--r--InCallUI/res/values-ta/strings.xml195
-rw-r--r--InCallUI/res/values-te/strings.xml195
-rw-r--r--InCallUI/res/values-th/strings.xml195
-rw-r--r--InCallUI/res/values-tl/strings.xml195
-rw-r--r--InCallUI/res/values-tr/strings.xml195
-rw-r--r--InCallUI/res/values-uk/strings.xml195
-rw-r--r--InCallUI/res/values-ur/strings.xml195
-rw-r--r--InCallUI/res/values-uz/strings.xml195
-rw-r--r--InCallUI/res/values-vi/strings.xml195
-rw-r--r--InCallUI/res/values-w500dp-land/colors.xml21
-rw-r--r--InCallUI/res/values-w500dp-land/dimens.xml35
-rw-r--r--InCallUI/res/values-zh-rCN/strings.xml195
-rw-r--r--InCallUI/res/values-zh-rHK/strings.xml195
-rw-r--r--InCallUI/res/values-zh-rTW/strings.xml195
-rw-r--r--InCallUI/res/values-zu/strings.xml195
-rw-r--r--InCallUI/res/values/animation_constants.xml22
-rw-r--r--InCallUI/res/values/array.xml135
-rw-r--r--InCallUI/res/values/attrs.xml71
-rw-r--r--InCallUI/res/values/colors.xml133
-rw-r--r--InCallUI/res/values/config.xml27
-rw-r--r--InCallUI/res/values/dimens.xml150
-rw-r--r--InCallUI/res/values/ids.xml20
-rw-r--r--InCallUI/res/values/strings.xml540
-rw-r--r--InCallUI/res/values/styles.xml100
-rw-r--r--InCallUI/src/com/android/incallui/AccelerometerListener.java169
-rw-r--r--InCallUI/src/com/android/incallui/AccessibleAnswerFragment.java157
-rw-r--r--InCallUI/src/com/android/incallui/AnswerFragment.java307
-rw-r--r--InCallUI/src/com/android/incallui/AnswerPresenter.java312
-rw-r--r--InCallUI/src/com/android/incallui/AudioModeProvider.java105
-rw-r--r--InCallUI/src/com/android/incallui/BaseFragment.java84
-rw-r--r--InCallUI/src/com/android/incallui/Call.java1023
-rw-r--r--InCallUI/src/com/android/incallui/CallButtonFragment.java819
-rw-r--r--InCallUI/src/com/android/incallui/CallButtonPresenter.java486
-rw-r--r--InCallUI/src/com/android/incallui/CallCardFragment.java1510
-rw-r--r--InCallUI/src/com/android/incallui/CallCardPresenter.java1181
-rw-r--r--InCallUI/src/com/android/incallui/CallList.java695
-rw-r--r--InCallUI/src/com/android/incallui/CallTimer.java90
-rw-r--r--InCallUI/src/com/android/incallui/CallerInfo.java585
-rw-r--r--InCallUI/src/com/android/incallui/CallerInfoAsyncQuery.java599
-rw-r--r--InCallUI/src/com/android/incallui/CallerInfoUtils.java234
-rw-r--r--InCallUI/src/com/android/incallui/CircularRevealFragment.java170
-rw-r--r--InCallUI/src/com/android/incallui/ConferenceManagerFragment.java139
-rw-r--r--InCallUI/src/com/android/incallui/ConferenceManagerPresenter.java144
-rw-r--r--InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java533
-rw-r--r--InCallUI/src/com/android/incallui/ContactInfoCache.java699
-rw-r--r--InCallUI/src/com/android/incallui/ContactUtils.java48
-rw-r--r--InCallUI/src/com/android/incallui/ContactsAsyncHelper.java258
-rw-r--r--InCallUI/src/com/android/incallui/ContactsPreferencesFactory.java61
-rw-r--r--InCallUI/src/com/android/incallui/DialpadFragment.java563
-rw-r--r--InCallUI/src/com/android/incallui/DialpadPresenter.java84
-rw-r--r--InCallUI/src/com/android/incallui/DistanceHelper.java37
-rw-r--r--InCallUI/src/com/android/incallui/ExternalCallList.java105
-rw-r--r--InCallUI/src/com/android/incallui/ExternalCallNotifier.java406
-rw-r--r--InCallUI/src/com/android/incallui/FragmentDisplayManager.java23
-rw-r--r--InCallUI/src/com/android/incallui/GlowPadAnswerFragment.java155
-rw-r--r--InCallUI/src/com/android/incallui/GlowPadWrapper.java158
-rw-r--r--InCallUI/src/com/android/incallui/InCallActivity.java980
-rw-r--r--InCallUI/src/com/android/incallui/InCallAnimationUtils.java184
-rw-r--r--InCallUI/src/com/android/incallui/InCallCameraManager.java184
-rw-r--r--InCallUI/src/com/android/incallui/InCallContactInteractions.java399
-rw-r--r--InCallUI/src/com/android/incallui/InCallDateUtils.java53
-rw-r--r--InCallUI/src/com/android/incallui/InCallOrientationEventListener.java178
-rw-r--r--InCallUI/src/com/android/incallui/InCallPresenter.java1938
-rw-r--r--InCallUI/src/com/android/incallui/InCallServiceImpl.java100
-rw-r--r--InCallUI/src/com/android/incallui/InCallServiceListener.java41
-rw-r--r--InCallUI/src/com/android/incallui/InCallUIMaterialColorMapUtils.java55
-rw-r--r--InCallUI/src/com/android/incallui/InCallVideoCallCallback.java156
-rw-r--r--InCallUI/src/com/android/incallui/InCallVideoCallCallbackNotifier.java284
-rw-r--r--InCallUI/src/com/android/incallui/LatencyReport.java145
-rw-r--r--InCallUI/src/com/android/incallui/Log.java176
-rw-r--r--InCallUI/src/com/android/incallui/NeededForReflection.java30
-rw-r--r--InCallUI/src/com/android/incallui/NotificationBroadcastReceiver.java82
-rw-r--r--InCallUI/src/com/android/incallui/PostCharDialogFragment.java95
-rw-r--r--InCallUI/src/com/android/incallui/Presenter.java59
-rw-r--r--InCallUI/src/com/android/incallui/ProximitySensor.java317
-rw-r--r--InCallUI/src/com/android/incallui/StatusBarNotifier.java793
-rw-r--r--InCallUI/src/com/android/incallui/TelecomAdapter.java226
-rw-r--r--InCallUI/src/com/android/incallui/VideoCallFragment.java901
-rw-r--r--InCallUI/src/com/android/incallui/VideoCallPresenter.java1306
-rw-r--r--InCallUI/src/com/android/incallui/VideoPauseController.java420
-rw-r--r--InCallUI/src/com/android/incallui/VideoUtils.java109
-rw-r--r--InCallUI/src/com/android/incallui/async/PausableExecutor.java61
-rw-r--r--InCallUI/src/com/android/incallui/async/PausableExecutorImpl.java42
-rw-r--r--InCallUI/src/com/android/incallui/ringtone/DialerRingtoneManager.java140
-rw-r--r--InCallUI/src/com/android/incallui/ringtone/InCallTonePlayer.java168
-rw-r--r--InCallUI/src/com/android/incallui/ringtone/ToneGeneratorFactory.java36
-rw-r--r--InCallUI/src/com/android/incallui/service/PhoneNumberService.java67
-rw-r--r--InCallUI/src/com/android/incallui/spam/SpamCallListListener.java117
-rw-r--r--InCallUI/src/com/android/incallui/util/AccessibilityUtil.java30
-rw-r--r--InCallUI/src/com/android/incallui/util/TelecomCallUtil.java53
-rw-r--r--InCallUI/src/com/android/incallui/widget/multiwaveview/Ease.java132
-rw-r--r--InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java1473
-rw-r--r--InCallUI/src/com/android/incallui/widget/multiwaveview/PointCloud.java235
-rw-r--r--InCallUI/src/com/android/incallui/widget/multiwaveview/TargetDrawable.java250
-rw-r--r--InCallUI/src/com/android/incallui/widget/multiwaveview/Tweener.java178
-rw-r--r--InCallUI/src/com/android/incalluibind/ObjectFactory.java59
-rw-r--r--InCallUI/tests/src/com/android/incallui/CallCardPresenterTest.java121
-rw-r--r--InCallUI/tests/src/com/android/incallui/CallTest.java125
-rw-r--r--InCallUI/tests/src/com/android/incallui/CallerInfoUtilsTest.java31
-rw-r--r--InCallUI/tests/src/com/android/incallui/ContactsPreferencesFactoryTest.java51
-rw-r--r--InCallUI/tests/src/com/android/incallui/ExternalCallListTest.java144
-rw-r--r--InCallUI/tests/src/com/android/incallui/ExternalCallNotifierTest.java214
-rw-r--r--InCallUI/tests/src/com/android/incallui/InCallContactInteractionsTest.java325
-rw-r--r--InCallUI/tests/src/com/android/incallui/InCallPresenterTest.java198
-rw-r--r--InCallUI/tests/src/com/android/incallui/LatencyReportTest.java59
-rw-r--r--InCallUI/tests/src/com/android/incallui/MockCallListWrapper.java80
-rw-r--r--InCallUI/tests/src/com/android/incallui/ProximitySensorTest.java66
-rw-r--r--InCallUI/tests/src/com/android/incallui/StatusBarNotifierTest.java98
-rw-r--r--InCallUI/tests/src/com/android/incallui/TestTelecomCall.java161
-rw-r--r--InCallUI/tests/src/com/android/incallui/async/SingleProdThreadExecutor.java69
-rw-r--r--InCallUI/tests/src/com/android/incallui/ringtone/DialerRingtoneManagerTest.java219
-rw-r--r--InCallUI/tests/src/com/android/incallui/ringtone/InCallTonePlayerTest.java148
-rw-r--r--InCallUI/tests/src/com/android/incallui/spam/SpamCallListListenerTest.java206
-rw-r--r--LICENSE190
-rw-r--r--assets/product/res/drawable-hdpi/product_logo_avatar_anonymous_color_120.pngbin0 -> 2526 bytes
-rw-r--r--assets/product/res/drawable-hdpi/product_logo_avatar_anonymous_white_color_120.pngbin0 -> 1132 bytes
-rw-r--r--assets/product/res/drawable-mdpi/product_logo_avatar_anonymous_color_120.pngbin0 -> 1615 bytes
-rw-r--r--assets/product/res/drawable-mdpi/product_logo_avatar_anonymous_white_color_120.pngbin0 -> 752 bytes
-rw-r--r--assets/product/res/drawable-xhdpi/product_logo_avatar_anonymous_color_120.pngbin0 -> 3400 bytes
-rw-r--r--assets/product/res/drawable-xhdpi/product_logo_avatar_anonymous_white_color_120.pngbin0 -> 1508 bytes
-rw-r--r--assets/product/res/drawable-xxhdpi/product_logo_avatar_anonymous_color_120.pngbin0 -> 5093 bytes
-rw-r--r--assets/product/res/drawable-xxhdpi/product_logo_avatar_anonymous_white_color_120.pngbin0 -> 2339 bytes
-rw-r--r--assets/product/res/drawable-xxxhdpi/product_logo_avatar_anonymous_color_120.pngbin0 -> 6753 bytes
-rw-r--r--assets/product/res/drawable-xxxhdpi/product_logo_avatar_anonymous_white_color_120.pngbin0 -> 3234 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_arrow_back_white_24.pngbin0 -> 148 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_arrow_drop_down_white_18.pngbin0 -> 121 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_bluetooth_audio_grey600_24.pngbin0 -> 448 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_bluetooth_audio_white_36.pngbin0 -> 579 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_call_end_white_24.pngbin0 -> 314 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_call_end_white_36.pngbin0 -> 424 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_call_merge_white_36.pngbin0 -> 258 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_call_white_18.pngbin0 -> 276 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_call_white_24.pngbin0 -> 340 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_camera_alt_white_24.pngbin0 -> 364 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_camera_alt_white_48.pngbin0 -> 666 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_camera_front_white_36.pngbin0 -> 417 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_camera_rear_white_36.pngbin0 -> 344 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_check_black_24.pngbin0 -> 169 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_check_black_36.pngbin0 -> 223 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_check_white_48.pngbin0 -> 276 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_close_white_24.pngbin0 -> 221 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_close_white_36.pngbin0 -> 302 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_dialpad_white_36.pngbin0 -> 549 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_forward_white_24.png (renamed from InCallUI/res/drawable-hdpi/ic_forward_white_24dp.png)bin139 -> 139 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_fullscreen_exit_white_48.pngbin0 -> 105 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_fullscreen_white_36.pngbin0 -> 132 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_fullscreen_white_48.pngbin0 -> 107 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_group_white_36.pngbin0 -> 393 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_hd_white_24.png (renamed from InCallUI/res/drawable-hdpi/ic_hd_24dp.png)bin236 -> 236 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_headset_grey600_24.pngbin0 -> 371 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_headset_white_36.pngbin0 -> 511 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_message_white_24.pngbin0 -> 167 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_mic_off_black_24.pngbin0 -> 402 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_mic_off_white_36.pngbin0 -> 578 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_network_wifi_white_24.pngbin0 -> 427 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_pause_white_36.pngbin0 -> 124 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_person_add_white_24.pngbin0 -> 289 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_photo_library_white_24.pngbin0 -> 249 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_photo_white_24.pngbin0 -> 261 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_photo_white_48.pngbin0 -> 450 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_report_white_18.pngbin0 -> 212 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_send_black_24.pngbin0 -> 290 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_send_white_24.pngbin0 -> 293 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_swap_calls_white_36.pngbin0 -> 447 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_switch_camera_white_36.pngbin0 -> 318 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_switch_video_white_36.pngbin0 -> 288 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_undo_white_48.pngbin0 -> 445 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_videocam_off_white_24.pngbin0 -> 271 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_videocam_off_white_36.pngbin0 -> 360 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_videocam_white_18.pngbin0 -> 155 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_videocam_white_24.pngbin0 -> 173 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_videocam_white_36.pngbin0 -> 222 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_voicemail_white_18.pngbin0 -> 372 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_volume_up_grey600_24.pngbin0 -> 375 bytes
-rw-r--r--assets/quantum/res/drawable-hdpi/quantum_ic_volume_up_white_36.pngbin0 -> 518 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_arrow_back_white_24.pngbin0 -> 149 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_send_black_24.pngbin0 -> 295 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_send_white_24.pngbin0 -> 295 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_undo_white_48.pngbin0 -> 447 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_arrow_back_white_24.pngbin0 -> 119 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_send_black_24.pngbin0 -> 229 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_send_white_24.pngbin0 -> 237 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_undo_white_48.pngbin0 -> 325 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_arrow_back_white_24.pngbin0 -> 140 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_send_black_24.pngbin0 -> 341 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_send_white_24.pngbin0 -> 352 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_undo_white_48.pngbin0 -> 571 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_arrow_back_white_24.pngbin0 -> 195 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_send_black_24.pngbin0 -> 458 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_send_white_24.pngbin0 -> 452 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_undo_white_48.pngbin0 -> 823 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_arrow_back_white_24.pngbin0 -> 200 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_send_black_24.pngbin0 -> 586 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_send_white_24.pngbin0 -> 580 bytes
-rw-r--r--assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_undo_white_48.pngbin0 -> 1087 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_arrow_back_white_24.pngbin0 -> 115 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_arrow_drop_down_white_18.pngbin0 -> 89 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_bluetooth_audio_grey600_24.pngbin0 -> 307 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_bluetooth_audio_white_36.pngbin0 -> 438 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_call_end_white_24.pngbin0 -> 235 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_call_end_white_36.pngbin0 -> 314 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_call_merge_white_36.pngbin0 -> 208 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_call_white_18.pngbin0 -> 202 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_call_white_24.pngbin0 -> 246 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_camera_alt_white_24.pngbin0 -> 240 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_camera_alt_white_48.pngbin0 -> 446 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_camera_front_white_36.pngbin0 -> 274 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_camera_rear_white_36.pngbin0 -> 224 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_check_black_24.pngbin0 -> 128 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_check_black_36.pngbin0 -> 169 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_check_white_48.pngbin0 -> 199 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_close_white_24.pngbin0 -> 175 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_close_white_36.pngbin0 -> 221 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_dialpad_white_36.pngbin0 -> 264 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_forward_white_24.png (renamed from InCallUI/res/drawable-mdpi/ic_forward_white_24dp.png)bin117 -> 117 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_fullscreen_exit_white_48.pngbin0 -> 101 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_fullscreen_white_36.pngbin0 -> 101 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_fullscreen_white_48.pngbin0 -> 101 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_group_white_36.png (renamed from res/drawable-hdpi/ic_people_24dp.png)bin299 -> 299 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_hd_white_24.png (renamed from InCallUI/res/drawable-mdpi/ic_hd_24dp.png)bin154 -> 154 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_headset_grey600_24.pngbin0 -> 245 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_headset_white_36.pngbin0 -> 350 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_message_white_24.pngbin0 -> 130 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_mic_off_black_24.pngbin0 -> 271 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_mic_off_white_36.pngbin0 -> 428 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_network_wifi_white_24.pngbin0 -> 299 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_pause_white_36.png (renamed from res/drawable-hdpi/ic_pause_24dp.png)bin105 -> 105 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_person_add_white_24.pngbin0 -> 204 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_photo_library_white_24.pngbin0 -> 193 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_photo_white_24.pngbin0 -> 185 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_photo_white_48.pngbin0 -> 304 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_report_white_18.pngbin0 -> 163 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_send_black_24.pngbin0 -> 240 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_send_white_24.pngbin0 -> 237 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_swap_calls_white_36.pngbin0 -> 314 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_switch_camera_white_36.pngbin0 -> 234 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_switch_video_white_36.pngbin0 -> 225 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_undo_white_48.pngbin0 -> 321 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_videocam_off_white_24.pngbin0 -> 198 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_videocam_off_white_36.pngbin0 -> 271 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_videocam_white_18.pngbin0 -> 133 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_videocam_white_24.pngbin0 -> 131 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_videocam_white_36.pngbin0 -> 173 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_voicemail_white_18.pngbin0 -> 259 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_volume_up_grey600_24.pngbin0 -> 256 bytes
-rw-r--r--assets/quantum/res/drawable-mdpi/quantum_ic_volume_up_white_36.png (renamed from res/drawable-hdpi/ic_volume_up_24dp.png)bin365 -> 365 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_arrow_back_white_24.pngbin0 -> 131 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_arrow_drop_down_white_18.pngbin0 -> 123 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_bluetooth_audio_grey600_24.pngbin0 -> 518 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_bluetooth_audio_white_36.pngbin0 -> 778 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_call_end_white_24.pngbin0 -> 389 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_call_end_white_36.pngbin0 -> 553 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_call_merge_white_36.pngbin0 -> 287 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_call_white_18.pngbin0 -> 340 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_call_white_24.pngbin0 -> 420 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_camera_alt_white_24.pngbin0 -> 446 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_camera_alt_white_48.pngbin0 -> 894 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_camera_front_white_36.pngbin0 -> 444 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_camera_rear_white_36.pngbin0 -> 377 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_check_black_24.pngbin0 -> 188 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_check_black_36.pngbin0 -> 254 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_check_white_48.pngbin0 -> 308 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_close_white_24.pngbin0 -> 257 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_close_white_36.pngbin0 -> 347 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_dialpad_white_36.pngbin0 -> 362 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_forward_white_24.png (renamed from InCallUI/res/drawable-xhdpi/ic_forward_white_24dp.png)bin159 -> 159 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_fullscreen_exit_white_48.pngbin0 -> 106 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_fullscreen_white_36.pngbin0 -> 107 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_fullscreen_white_48.pngbin0 -> 109 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_group_white_36.png (renamed from res/drawable-xxhdpi/ic_people_24dp.png)bin488 -> 488 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_hd_white_24.png (renamed from InCallUI/res/drawable-xhdpi/ic_hd_24dp.png)bin201 -> 201 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_headset_grey600_24.pngbin0 -> 440 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_headset_white_36.pngbin0 -> 610 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_message_white_24.pngbin0 -> 204 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_mic_off_black_24.pngbin0 -> 454 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_mic_off_white_36.pngbin0 -> 713 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_network_wifi_white_24.pngbin0 -> 538 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_pause_white_36.png (renamed from res/drawable-xxhdpi/ic_pause_24dp.png)bin92 -> 92 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_person_add_white_24.pngbin0 -> 329 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_photo_library_white_24.pngbin0 -> 309 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_photo_white_24.pngbin0 -> 304 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_photo_white_48.pngbin0 -> 570 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_report_white_18.png (renamed from InCallUI/res/drawable-mdpi/ic_report_white_36dp.png)bin240 -> 240 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_send_black_24.pngbin0 -> 333 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_send_white_24.pngbin0 -> 344 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_swap_calls_white_36.pngbin0 -> 484 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_switch_camera_white_36.pngbin0 -> 379 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_switch_video_white_36.pngbin0 -> 309 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_undo_white_48.pngbin0 -> 563 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_off_white_24.pngbin0 -> 296 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_off_white_36.pngbin0 -> 412 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_white_18.pngbin0 -> 173 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_white_24.pngbin0 -> 178 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_white_36.pngbin0 -> 234 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_voicemail_white_18.png (renamed from res/drawable-hdpi/ic_voicemail_24dp.png)bin478 -> 478 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_volume_up_grey600_24.pngbin0 -> 459 bytes
-rw-r--r--assets/quantum/res/drawable-xhdpi/quantum_ic_volume_up_white_36.png (renamed from res/drawable-xxhdpi/ic_volume_up_24dp.png)bin654 -> 654 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_arrow_back_white_24.pngbin0 -> 191 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_arrow_drop_down_white_18.pngbin0 -> 126 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_bluetooth_audio_grey600_24.pngbin0 -> 794 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_bluetooth_audio_white_36.pngbin0 -> 1080 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_call_end_white_24.pngbin0 -> 553 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_call_end_white_36.pngbin0 -> 778 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_call_merge_white_36.pngbin0 -> 388 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_call_white_18.pngbin0 -> 491 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_call_white_24.pngbin0 -> 597 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_alt_white_24.pngbin0 -> 666 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_alt_white_48.pngbin0 -> 1309 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_front_white_36.pngbin0 -> 676 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_rear_white_36.pngbin0 -> 568 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_check_black_24.pngbin0 -> 254 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_check_black_36.pngbin0 -> 295 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_check_white_48.pngbin0 -> 386 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_close_white_24.pngbin0 -> 347 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_close_white_36.pngbin0 -> 454 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_dialpad_white_36.pngbin0 -> 452 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_forward_white_24.png (renamed from InCallUI/res/drawable-xxhdpi/ic_forward_white_24dp.png)bin204 -> 204 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_fullscreen_exit_white_48.pngbin0 -> 123 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_fullscreen_white_36.pngbin0 -> 114 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_fullscreen_white_48.pngbin0 -> 123 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_group_white_36.pngbin0 -> 705 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_hd_white_24.png (renamed from InCallUI/res/drawable-xxhdpi/ic_hd_24dp.png)bin290 -> 290 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_headset_grey600_24.pngbin0 -> 635 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_headset_white_36.pngbin0 -> 936 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_message_white_24.pngbin0 -> 269 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_mic_off_black_24.pngbin0 -> 671 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_mic_off_white_36.pngbin0 -> 1044 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_network_wifi_white_24.pngbin0 -> 786 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_pause_white_36.pngbin0 -> 158 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_person_add_white_24.pngbin0 -> 464 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_photo_library_white_24.pngbin0 -> 431 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_photo_white_24.pngbin0 -> 450 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_photo_white_48.pngbin0 -> 859 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_report_white_18.png (renamed from InCallUI/res/drawable-hdpi/ic_report_white_36dp.png)bin312 -> 312 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_send_black_24.pngbin0 -> 455 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_send_white_24.pngbin0 -> 446 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_swap_calls_white_36.pngbin0 -> 827 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_switch_camera_white_36.pngbin0 -> 544 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_switch_video_white_36.pngbin0 -> 469 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_undo_white_48.pngbin0 -> 815 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_off_white_24.pngbin0 -> 412 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_off_white_36.pngbin0 -> 570 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_white_18.pngbin0 -> 222 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_white_24.pngbin0 -> 234 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_white_36.pngbin0 -> 350 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_voicemail_white_18.pngbin0 -> 701 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_volume_up_grey600_24.pngbin0 -> 673 bytes
-rw-r--r--assets/quantum/res/drawable-xxhdpi/quantum_ic_volume_up_white_36.pngbin0 -> 998 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_arrow_back_white_24.pngbin0 -> 194 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_arrow_drop_down_white_18.pngbin0 -> 152 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_bluetooth_audio_grey600_24.pngbin0 -> 952 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_bluetooth_audio_white_36.pngbin0 -> 1391 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_end_white_24.pngbin0 -> 712 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_end_white_36.pngbin0 -> 1039 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_merge_white_36.pngbin0 -> 435 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_white_18.pngbin0 -> 597 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_white_24.pngbin0 -> 778 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_alt_white_24.pngbin0 -> 894 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_alt_white_48.pngbin0 -> 1837 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_front_white_36.pngbin0 -> 875 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_rear_white_36.pngbin0 -> 745 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_check_black_24.pngbin0 -> 277 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_check_black_36.pngbin0 -> 345 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_check_white_48.pngbin0 -> 466 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_close_white_24.pngbin0 -> 436 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_close_white_36.pngbin0 -> 524 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_dialpad_white_36.pngbin0 -> 754 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_forward_white_24.png (renamed from InCallUI/res/drawable-xxxhdpi/ic_forward_white_24dp.png)bin236 -> 236 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_fullscreen_exit_white_48.pngbin0 -> 125 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_fullscreen_white_36.pngbin0 -> 123 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_fullscreen_white_48.pngbin0 -> 124 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_group_white_36.pngbin0 -> 980 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_hd_white_24.png (renamed from InCallUI/res/drawable-xxxhdpi/ic_hd_24dp.png)bin348 -> 348 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_headset_grey600_24.pngbin0 -> 856 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_headset_white_36.pngbin0 -> 1246 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_message_white_24.pngbin0 -> 342 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_mic_off_black_24.pngbin0 -> 832 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_mic_off_white_36.pngbin0 -> 1326 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_network_wifi_white_24.pngbin0 -> 1043 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_pause_white_36.pngbin0 -> 110 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_person_add_white_24.pngbin0 -> 610 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_photo_library_white_24.pngbin0 -> 553 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_photo_white_24.pngbin0 -> 570 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_photo_white_48.pngbin0 -> 1178 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_report_white_18.png (renamed from InCallUI/res/drawable-xhdpi/ic_report_white_36dp.png)bin340 -> 340 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_send_black_24.pngbin0 -> 585 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_send_white_24.pngbin0 -> 576 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_swap_calls_white_36.pngbin0 -> 1008 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_switch_camera_white_36.pngbin0 -> 713 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_switch_video_white_36.pngbin0 -> 569 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_undo_white_48.pngbin0 -> 1072 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_off_white_24.pngbin0 -> 495 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_off_white_36.pngbin0 -> 716 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_white_18.pngbin0 -> 234 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_white_24.png (renamed from res/drawable-xxxhdpi/ic_videocam_24dp.png)bin290 -> 290 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_white_36.pngbin0 -> 437 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_voicemail_white_18.png (renamed from res/drawable-xxhdpi/ic_voicemail_24dp.png)bin625 -> 625 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_volume_up_grey600_24.pngbin0 -> 895 bytes
-rw-r--r--assets/quantum/res/drawable-xxxhdpi/quantum_ic_volume_up_white_36.pngbin0 -> 1304 bytes
-rw-r--r--build-app.gradle39
-rw-r--r--build-library.gradle39
-rw-r--r--java/com/android/contacts/common/AndroidManifest.xml39
-rw-r--r--java/com/android/contacts/common/Bindings.java52
-rw-r--r--java/com/android/contacts/common/ClipboardUtils.java55
-rw-r--r--java/com/android/contacts/common/Collapser.java95
-rw-r--r--java/com/android/contacts/common/ContactPhotoManager.java487
-rw-r--r--java/com/android/contacts/common/ContactPhotoManagerImpl.java1262
-rw-r--r--java/com/android/contacts/common/ContactPresenceIconUtil.java46
-rw-r--r--java/com/android/contacts/common/ContactStatusUtil.java44
-rw-r--r--java/com/android/contacts/common/ContactTileLoaderFactory.java64
-rw-r--r--java/com/android/contacts/common/ContactsUtils.java265
-rw-r--r--java/com/android/contacts/common/GeoUtil.java55
-rw-r--r--java/com/android/contacts/common/GroupMetaData.java76
-rw-r--r--java/com/android/contacts/common/MoreContactUtils.java251
-rw-r--r--java/com/android/contacts/common/bindings/ContactsCommonBindings.java25
-rw-r--r--java/com/android/contacts/common/bindings/ContactsCommonBindingsFactory.java24
-rw-r--r--java/com/android/contacts/common/bindings/ContactsCommonBindingsStub.java27
-rw-r--r--java/com/android/contacts/common/compat/CallCompat.java45
-rw-r--r--java/com/android/contacts/common/compat/CallableCompat.java36
-rw-r--r--java/com/android/contacts/common/compat/ContactsCompat.java57
-rw-r--r--java/com/android/contacts/common/compat/DirectoryCompat.java51
-rw-r--r--java/com/android/contacts/common/compat/PhoneAccountCompat.java104
-rw-r--r--java/com/android/contacts/common/compat/PhoneCompat.java36
-rw-r--r--java/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java174
-rw-r--r--java/com/android/contacts/common/compat/TelephonyManagerCompat.java213
-rw-r--r--java/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java302
-rw-r--r--java/com/android/contacts/common/database/ContactUpdateUtils.java49
-rw-r--r--java/com/android/contacts/common/database/EmptyCursor.java84
-rw-r--r--java/com/android/contacts/common/database/NoNullCursorAsyncQueryHandler.java73
-rw-r--r--java/com/android/contacts/common/dialog/CallSubjectDialog.java607
-rw-r--r--java/com/android/contacts/common/dialog/ClearFrequentsDialog.java88
-rw-r--r--java/com/android/contacts/common/extensions/PhoneDirectoryExtender.java28
-rw-r--r--java/com/android/contacts/common/extensions/PhoneDirectoryExtenderAccessor.java45
-rw-r--r--java/com/android/contacts/common/extensions/PhoneDirectoryExtenderFactory.java27
-rw-r--r--java/com/android/contacts/common/extensions/PhoneDirectoryExtenderStub.java29
-rw-r--r--java/com/android/contacts/common/format/FormatUtils.java181
-rw-r--r--java/com/android/contacts/common/format/TextHighlighter.java93
-rw-r--r--java/com/android/contacts/common/format/testing/SpannedTestUtils.java85
-rw-r--r--java/com/android/contacts/common/lettertiles/LetterTileDrawable.java382
-rw-r--r--java/com/android/contacts/common/list/AutoScrollListView.java125
-rw-r--r--java/com/android/contacts/common/list/ContactEntry.java57
-rw-r--r--java/com/android/contacts/common/list/ContactEntryListAdapter.java742
-rw-r--r--java/com/android/contacts/common/list/ContactEntryListFragment.java862
-rw-r--r--java/com/android/contacts/common/list/ContactListAdapter.java232
-rw-r--r--java/com/android/contacts/common/list/ContactListFilter.java297
-rw-r--r--java/com/android/contacts/common/list/ContactListFilterController.java170
-rw-r--r--java/com/android/contacts/common/list/ContactListItemView.java1513
-rw-r--r--java/com/android/contacts/common/list/ContactListPinnedHeaderView.java70
-rw-r--r--java/com/android/contacts/common/list/ContactTileView.java171
-rw-r--r--java/com/android/contacts/common/list/ContactsSectionIndexer.java119
-rw-r--r--java/com/android/contacts/common/list/DefaultContactListAdapter.java216
-rw-r--r--java/com/android/contacts/common/list/DirectoryListLoader.java201
-rw-r--r--java/com/android/contacts/common/list/DirectoryPartition.java179
-rw-r--r--java/com/android/contacts/common/list/IndexerListAdapter.java214
-rw-r--r--java/com/android/contacts/common/list/OnPhoneNumberPickerActionListener.java39
-rw-r--r--java/com/android/contacts/common/list/PhoneNumberListAdapter.java583
-rw-r--r--java/com/android/contacts/common/list/PhoneNumberPickerFragment.java402
-rw-r--r--java/com/android/contacts/common/list/PinnedHeaderListAdapter.java159
-rw-r--r--java/com/android/contacts/common/list/PinnedHeaderListView.java563
-rw-r--r--java/com/android/contacts/common/list/ViewPagerTabStrip.java109
-rw-r--r--java/com/android/contacts/common/list/ViewPagerTabs.java317
-rw-r--r--java/com/android/contacts/common/location/CountryDetector.java221
-rw-r--r--java/com/android/contacts/common/location/UpdateCountryService.java104
-rw-r--r--java/com/android/contacts/common/model/AccountTypeManager.java813
-rw-r--r--java/com/android/contacts/common/model/BuilderWrapper.java53
-rw-r--r--java/com/android/contacts/common/model/CPOWrapper.java50
-rw-r--r--java/com/android/contacts/common/model/Contact.java384
-rw-r--r--java/com/android/contacts/common/model/ContactLoader.java998
-rw-r--r--java/com/android/contacts/common/model/RawContact.java351
-rw-r--r--java/com/android/contacts/common/model/account/AccountType.java501
-rw-r--r--java/com/android/contacts/common/model/account/AccountTypeWithDataSet.java103
-rw-r--r--java/com/android/contacts/common/model/account/AccountWithDataSet.java229
-rw-r--r--java/com/android/contacts/common/model/account/BaseAccountType.java1890
-rw-r--r--java/com/android/contacts/common/model/account/ExchangeAccountType.java365
-rw-r--r--java/com/android/contacts/common/model/account/ExternalAccountType.java443
-rw-r--r--java/com/android/contacts/common/model/account/FallbackAccountType.java77
-rw-r--r--java/com/android/contacts/common/model/account/GoogleAccountType.java206
-rw-r--r--java/com/android/contacts/common/model/account/SamsungAccountType.java235
-rw-r--r--java/com/android/contacts/common/model/dataitem/DataItem.java258
-rw-r--r--java/com/android/contacts/common/model/dataitem/DataKind.java132
-rw-r--r--java/com/android/contacts/common/model/dataitem/EmailDataItem.java47
-rw-r--r--java/com/android/contacts/common/model/dataitem/EventDataItem.java62
-rw-r--r--java/com/android/contacts/common/model/dataitem/GroupMembershipDataItem.java40
-rw-r--r--java/com/android/contacts/common/model/dataitem/IdentityDataItem.java39
-rw-r--r--java/com/android/contacts/common/model/dataitem/ImDataItem.java109
-rw-r--r--java/com/android/contacts/common/model/dataitem/NicknameDataItem.java39
-rw-r--r--java/com/android/contacts/common/model/dataitem/NoteDataItem.java35
-rw-r--r--java/com/android/contacts/common/model/dataitem/OrganizationDataItem.java64
-rw-r--r--java/com/android/contacts/common/model/dataitem/PhoneDataItem.java76
-rw-r--r--java/com/android/contacts/common/model/dataitem/PhotoDataItem.java39
-rw-r--r--java/com/android/contacts/common/model/dataitem/RelationDataItem.java62
-rw-r--r--java/com/android/contacts/common/model/dataitem/SipAddressDataItem.java40
-rw-r--r--java/com/android/contacts/common/model/dataitem/StructuredNameDataItem.java100
-rw-r--r--java/com/android/contacts/common/model/dataitem/StructuredPostalDataItem.java68
-rw-r--r--java/com/android/contacts/common/model/dataitem/WebsiteDataItem.java39
-rw-r--r--java/com/android/contacts/common/preference/ContactsPreferences.java269
-rw-r--r--java/com/android/contacts/common/preference/DisplayOrderPreference.java89
-rw-r--r--java/com/android/contacts/common/preference/SortOrderPreference.java89
-rw-r--r--java/com/android/contacts/common/res/color/popup_menu_color.xml20
-rw-r--r--java/com/android/contacts/common/res/color/tab_text_color.xml (renamed from res/color/tab_text_color.xml)4
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_ab_search.pngbin0 -> 1115 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_arrow_back_24dp.pngbin0 -> 612 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_business_white_120dp.pngbin0 -> 2477 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_call_24dp.pngbin0 -> 340 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_call_note_white_24dp.pngbin0 -> 373 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_close_dk.pngbin0 -> 609 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_create_24dp.pngbin0 -> 370 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_group_white_24dp.pngbin0 -> 389 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_history_white_drawable_24dp.pngbin0 -> 525 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_info_outline_24dp.pngbin0 -> 485 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_menu_back.pngbin0 -> 799 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_menu_group_dk.pngbin0 -> 1954 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_menu_group_lt.pngbin0 -> 1922 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_menu_overflow_lt.pngbin0 -> 220 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_menu_person_dk.pngbin0 -> 1439 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_menu_person_lt.pngbin0 -> 1416 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_menu_remove_field_holo_light.pngbin0 -> 515 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_dk.pngbin0 -> 1438 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_holo_light.pngbin0 -> 1211 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_lt.pngbin0 -> 1414 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_message_24dp.pngbin0 -> 167 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_person_24dp.pngbin0 -> 273 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_person_add_24dp.pngbin0 -> 289 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_phone_attach.pngbin0 -> 828 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_rx_videocam.pngbin0 -> 413 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_scroll_handle.pngbin0 -> 544 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_tx_videocam.pngbin0 -> 370 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_videocam.pngbin0 -> 269 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/ic_voicemail_avatar.pngbin0 -> 3607 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/list_activated_holo.9.pngbin0 -> 154 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/list_background_holo.9.pngbin0 -> 224 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/list_focused_holo.9.pngbin0 -> 235 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/list_longpressed_holo_light.9.pngbin0 -> 158 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/list_pressed_holo_light.9.pngbin0 -> 159 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/list_section_divider_holo_custom.9.pngbin0 -> 205 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-hdpi/list_title_holo.9.pngbin0 -> 267 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_background_holo.9.pngbin0 -> 219 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_focused_holo.9.pngbin0 -> 234 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_section_divider_holo_custom.9.pngbin0 -> 191 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_title_holo.9.pngbin0 -> 258 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_background_holo.9.pngbin0 -> 178 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_focused_holo.9.pngbin0 -> 234 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_section_divider_holo_custom.9.pngbin0 -> 180 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_title_holo.9.pngbin0 -> 186 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-hdpi/list_activated_holo.9.pngbin0 -> 1666 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-mdpi/list_activated_holo.9.pngbin0 -> 1034 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-xhdpi/list_activated_holo.9.pngbin0 -> 2486 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_background_holo.9.pngbin0 -> 243 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_focused_holo.9.pngbin0 -> 234 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_section_divider_holo_custom.9.pngbin0 -> 196 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_title_holo.9.pngbin0 -> 255 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_ab_search.pngbin0 -> 781 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_arrow_back_24dp.pngbin0 -> 578 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_business_white_120dp.pngbin0 -> 2040 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_call_24dp.pngbin0 -> 246 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_call_note_white_24dp.pngbin0 -> 266 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_close_dk.pngbin0 -> 572 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_create_24dp.pngbin0 -> 290 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_group_white_24dp.pngbin0 -> 297 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_history_white_drawable_24dp.pngbin0 -> 340 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_info_outline_24dp.pngbin0 -> 320 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_menu_back.pngbin0 -> 607 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_menu_group_dk.pngbin0 -> 1266 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_menu_group_lt.pngbin0 -> 1270 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_menu_overflow_lt.pngbin0 -> 171 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_menu_person_dk.pngbin0 -> 1052 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_menu_person_lt.pngbin0 -> 1021 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_menu_remove_field_holo_light.pngbin0 -> 424 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_menu_star_dk.pngbin0 -> 1034 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_menu_star_lt.pngbin0 -> 1018 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_message_24dp.pngbin0 -> 130 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_person_24dp.pngbin0 -> 188 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_person_add_24dp.pngbin0 -> 204 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_phone_attach.pngbin0 -> 476 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_rx_videocam.pngbin0 -> 299 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_scroll_handle.pngbin0 -> 504 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_tx_videocam.pngbin0 -> 265 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_videocam.pngbin0 -> 216 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/ic_voicemail_avatar.pngbin0 -> 2120 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/list_activated_holo.9.pngbin0 -> 151 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/list_background_holo.9.pngbin0 -> 188 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/list_focused_holo.9.pngbin0 -> 235 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/list_longpressed_holo_light.9.pngbin0 -> 155 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/list_pressed_holo_light.9.pngbin0 -> 158 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/list_section_divider_holo_custom.9.pngbin0 -> 198 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-mdpi/list_title_holo.9.pngbin0 -> 199 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-sw600dp-hdpi/list_activated_holo.9.pngbin0 -> 1659 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-sw600dp-mdpi/list_activated_holo.9.pngbin0 -> 1005 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-sw600dp-xhdpi/list_activated_holo.9.pngbin0 -> 2478 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_ab_search.pngbin0 -> 1451 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_arrow_back_24dp.pngbin0 -> 765 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_business_white_120dp.pngbin0 -> 2916 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_call_24dp.pngbin0 -> 420 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_call_note_white_24dp.pngbin0 -> 449 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_close_dk.pngbin0 -> 814 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_create_24dp.pngbin0 -> 426 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_group_white_24dp.pngbin0 -> 461 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_history_white_drawable_24dp.pngbin0 -> 659 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_info_outline_24dp.pngbin0 -> 655 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_back.pngbin0 -> 1034 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_group_dk.pngbin0 -> 2650 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_group_lt.pngbin0 -> 2632 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_overflow_lt.pngbin0 -> 287 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_person_dk.pngbin0 -> 1844 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_person_lt.pngbin0 -> 1815 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_remove_field_holo_light.pngbin0 -> 593 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_star_dk.pngbin0 -> 1830 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_star_holo_light.pngbin0 -> 1607 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_star_lt.pngbin0 -> 1827 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_message_24dp.pngbin0 -> 204 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_person_24dp.pngbin0 -> 312 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_person_add_24dp.pngbin0 -> 329 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_phone_attach.pngbin0 -> 1009 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_rx_videocam.pngbin0 -> 439 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_scroll_handle.pngbin0 -> 620 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_tx_videocam.pngbin0 -> 405 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_videocam.pngbin0 -> 301 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/ic_voicemail_avatar.pngbin0 -> 4894 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/list_activated_holo.9.pngbin0 -> 158 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/list_background_holo.9.pngbin0 -> 245 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/list_focused_holo.9.pngbin0 -> 235 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/list_longpressed_holo_light.9.pngbin0 -> 162 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/list_pressed_holo_light.9.pngbin0 -> 163 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/list_section_divider_holo_custom.9.pngbin0 -> 210 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xhdpi/list_title_holo.9.pngbin0 -> 267 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_ab_search.pngbin0 -> 2100 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_arrow_back_24dp.pngbin0 -> 1376 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_business_white_120dp.pngbin0 -> 2541 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_call_24dp.pngbin0 -> 597 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_call_note_white_24dp.pngbin0 -> 647 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_close_dk.pngbin0 -> 1465 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_create_24dp.pngbin0 -> 668 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_group_white_24dp.pngbin0 -> 604 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_history_white_drawable_24dp.pngbin0 -> 971 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_info_outline_24dp.pngbin0 -> 953 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_back.pngbin0 -> 1546 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_group_dk.pngbin0 -> 3338 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_group_lt.pngbin0 -> 3381 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_overflow_lt.pngbin0 -> 414 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_person_dk.pngbin0 -> 2357 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_person_lt.pngbin0 -> 2363 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_remove_field_holo_light.pngbin0 -> 1381 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_star_dk.pngbin0 -> 2111 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_star_holo_light.pngbin0 -> 2119 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_star_lt.pngbin0 -> 2117 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_message_24dp.pngbin0 -> 269 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_person_24dp.pngbin0 -> 440 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_person_add_24dp.pngbin0 -> 464 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_phone_attach.pngbin0 -> 1517 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_rx_videocam.pngbin0 -> 603 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_scroll_handle.pngbin0 -> 837 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_tx_videocam.pngbin0 -> 551 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_videocam.pngbin0 -> 398 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/ic_voicemail_avatar.pngbin0 -> 7976 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/list_activated_holo.9.pngbin0 -> 1140 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/list_focused_holo.9.pngbin0 -> 1147 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/list_longpressed_holo_light.9.pngbin0 -> 1051 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/list_pressed_holo_light.9.pngbin0 -> 1051 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxhdpi/list_title_holo.9.pngbin0 -> 465 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_ab_search.pngbin0 -> 2571 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_arrow_back_24dp.pngbin0 -> 1512 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_business_white_120dp.pngbin0 -> 2915 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_call_24dp.pngbin0 -> 778 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_call_note_white_24dp.pngbin0 -> 853 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_close_dk.pngbin0 -> 1688 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_create_24dp.pngbin0 -> 612 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_history_white_drawable_24dp.pngbin0 -> 1311 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_info_outline_24dp.pngbin0 -> 1279 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_message_24dp.pngbin0 -> 342 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_person_24dp.pngbin0 -> 577 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_person_add_24dp.pngbin0 -> 610 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_phone_attach.pngbin0 -> 2135 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_scroll_handle.pngbin0 -> 1579 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_videocam.pngbin0 -> 481 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable-xxxhdpi/ic_voicemail_avatar.pngbin0 -> 11277 bytes
-rw-r--r--java/com/android/contacts/common/res/drawable/dialog_background_material.xml23
-rw-r--r--java/com/android/contacts/common/res/drawable/fastscroll_thumb.xml19
-rw-r--r--java/com/android/contacts/common/res/drawable/ic_back_arrow.xml20
-rw-r--r--java/com/android/contacts/common/res/drawable/ic_call.xml19
-rw-r--r--java/com/android/contacts/common/res/drawable/ic_message_24dp.xml19
-rw-r--r--java/com/android/contacts/common/res/drawable/ic_more_vert.xml9
-rw-r--r--java/com/android/contacts/common/res/drawable/ic_person_add_tinted_24dp.xml20
-rw-r--r--java/com/android/contacts/common/res/drawable/ic_scroll_handle_default.xml20
-rw-r--r--java/com/android/contacts/common/res/drawable/ic_scroll_handle_pressed.xml20
-rw-r--r--java/com/android/contacts/common/res/drawable/ic_search_add_contact.xml20
-rw-r--r--java/com/android/contacts/common/res/drawable/ic_search_video_call.xml21
-rw-r--r--java/com/android/contacts/common/res/drawable/ic_tab_all.xml21
-rw-r--r--java/com/android/contacts/common/res/drawable/ic_tab_groups.xml21
-rw-r--r--java/com/android/contacts/common/res/drawable/ic_tab_starred.xml21
-rw-r--r--java/com/android/contacts/common/res/drawable/ic_work_profile.xml16
-rw-r--r--java/com/android/contacts/common/res/drawable/item_background_material_borderless_dark.xml19
-rw-r--r--java/com/android/contacts/common/res/drawable/item_background_material_dark.xml23
-rw-r--r--java/com/android/contacts/common/res/drawable/item_background_material_light.xml23
-rw-r--r--java/com/android/contacts/common/res/drawable/list_item_activated_background.xml20
-rw-r--r--java/com/android/contacts/common/res/drawable/list_selector_background_transition_holo_light.xml20
-rw-r--r--java/com/android/contacts/common/res/drawable/searchedittext_custom_cursor.xml7
-rw-r--r--java/com/android/contacts/common/res/drawable/unread_count_background.xml21
-rw-r--r--java/com/android/contacts/common/res/drawable/view_pager_tab_background.xml22
-rw-r--r--java/com/android/contacts/common/res/layout-ldrtl/unread_count_tab.xml48
-rw-r--r--java/com/android/contacts/common/res/layout/account_filter_header.xml44
-rw-r--r--java/com/android/contacts/common/res/layout/account_selector_list_item.xml57
-rw-r--r--java/com/android/contacts/common/res/layout/account_selector_list_item_condensed.xml56
-rw-r--r--java/com/android/contacts/common/res/layout/call_subject_history.xml33
-rw-r--r--java/com/android/contacts/common/res/layout/call_subject_history_list_item.xml29
-rw-r--r--java/com/android/contacts/common/res/layout/contact_detail_list_padding.xml27
-rw-r--r--java/com/android/contacts/common/res/layout/contact_list_card.xml39
-rw-r--r--java/com/android/contacts/common/res/layout/contact_list_content.xml61
-rw-r--r--java/com/android/contacts/common/res/layout/default_account_checkbox.xml36
-rw-r--r--java/com/android/contacts/common/res/layout/dialog_call_subject.xml159
-rw-r--r--java/com/android/contacts/common/res/layout/directory_header.xml55
-rw-r--r--java/com/android/contacts/common/res/layout/list_separator.xml27
-rw-r--r--java/com/android/contacts/common/res/layout/search_bar_expanded.xml62
-rw-r--r--java/com/android/contacts/common/res/layout/select_account_list_item.xml56
-rw-r--r--java/com/android/contacts/common/res/layout/unread_count_tab.xml43
-rw-r--r--java/com/android/contacts/common/res/mipmap-hdpi/ic_contacts_launcher.pngbin0 -> 3169 bytes
-rw-r--r--java/com/android/contacts/common/res/mipmap-mdpi/ic_contacts_launcher.pngbin0 -> 2062 bytes
-rw-r--r--java/com/android/contacts/common/res/mipmap-xhdpi/ic_contacts_launcher.pngbin0 -> 4430 bytes
-rw-r--r--java/com/android/contacts/common/res/mipmap-xxhdpi/ic_contacts_launcher.pngbin0 -> 7228 bytes
-rw-r--r--java/com/android/contacts/common/res/mipmap-xxxhdpi/ic_contacts_launcher.pngbin0 -> 10065 bytes
-rw-r--r--java/com/android/contacts/common/res/values-ja/donottranslate_config.xml20
-rw-r--r--java/com/android/contacts/common/res/values-ko/donottranslate_config.xml17
-rw-r--r--java/com/android/contacts/common/res/values-land/integers.xml22
-rw-r--r--java/com/android/contacts/common/res/values-sw600dp-land/integers.xml22
-rw-r--r--java/com/android/contacts/common/res/values-sw600dp/dimens.xml29
-rw-r--r--java/com/android/contacts/common/res/values-sw600dp/integers.xml24
-rw-r--r--java/com/android/contacts/common/res/values-sw720dp-land/integers.xml22
-rw-r--r--java/com/android/contacts/common/res/values-sw720dp/integers.xml22
-rw-r--r--java/com/android/contacts/common/res/values-zh-rCN/donottranslate_config.xml17
-rw-r--r--java/com/android/contacts/common/res/values-zh-rTW/donottranslate_config.xml17
-rw-r--r--java/com/android/contacts/common/res/values/animation_constants.xml19
-rw-r--r--java/com/android/contacts/common/res/values/attrs.xml83
-rw-r--r--java/com/android/contacts/common/res/values/colors.xml158
-rw-r--r--java/com/android/contacts/common/res/values/dimens.xml161
-rw-r--r--java/com/android/contacts/common/res/values/donottranslate_config.xml95
-rw-r--r--java/com/android/contacts/common/res/values/ids.xml30
-rw-r--r--java/com/android/contacts/common/res/values/integers.xml39
-rw-r--r--java/com/android/contacts/common/res/values/strings.xml798
-rw-r--r--java/com/android/contacts/common/res/values/styles.xml97
-rw-r--r--java/com/android/contacts/common/testing/InjectedServices.java65
-rw-r--r--java/com/android/contacts/common/util/AccountFilterUtil.java125
-rw-r--r--java/com/android/contacts/common/util/BitmapUtil.java167
-rw-r--r--java/com/android/contacts/common/util/CommonDateUtils.java37
-rw-r--r--java/com/android/contacts/common/util/Constants.java28
-rw-r--r--java/com/android/contacts/common/util/ContactDisplayUtils.java307
-rw-r--r--java/com/android/contacts/common/util/ContactListViewUtils.java89
-rw-r--r--java/com/android/contacts/common/util/ContactLoaderUtils.java78
-rw-r--r--java/com/android/contacts/common/util/DateUtils.java283
-rw-r--r--java/com/android/contacts/common/util/FabUtil.java71
-rw-r--r--java/com/android/contacts/common/util/MaterialColorMapUtils.java181
-rw-r--r--java/com/android/contacts/common/util/NameConverter.java242
-rw-r--r--java/com/android/contacts/common/util/SearchUtil.java198
-rw-r--r--java/com/android/contacts/common/util/StopWatch.java100
-rw-r--r--java/com/android/contacts/common/util/TelephonyManagerUtils.java45
-rw-r--r--java/com/android/contacts/common/util/TrafficStatsTags.java22
-rw-r--r--java/com/android/contacts/common/util/UriUtils.java90
-rw-r--r--java/com/android/contacts/common/widget/ActivityTouchLinearLayout.java43
-rw-r--r--java/com/android/contacts/common/widget/FloatingActionButtonController.java226
-rw-r--r--java/com/android/contacts/common/widget/LayoutSuppressingImageView.java39
-rw-r--r--java/com/android/contacts/common/widget/SelectPhoneAccountDialogFragment.java297
-rw-r--r--java/com/android/dialer/animation/AnimUtils.java247
-rw-r--r--java/com/android/dialer/animation/AnimationListenerAdapter.java39
-rw-r--r--java/com/android/dialer/app/AndroidManifest.xml116
-rw-r--r--java/com/android/dialer/app/Bindings.java77
-rw-r--r--java/com/android/dialer/app/CallDetailActivity.java480
-rw-r--r--java/com/android/dialer/app/DialerApplication.java77
-rw-r--r--java/com/android/dialer/app/DialtactsActivity.java1484
-rw-r--r--java/com/android/dialer/app/FloatingActionButtonBehavior.java50
-rw-r--r--java/com/android/dialer/app/PhoneCallDetails.java207
-rw-r--r--java/com/android/dialer/app/SpecialCharSequenceMgr.java493
-rw-r--r--java/com/android/dialer/app/alert/AlertManager.java30
-rw-r--r--java/com/android/dialer/app/bindings/DialerBindings.java25
-rw-r--r--java/com/android/dialer/app/bindings/DialerBindingsFactory.java26
-rw-r--r--java/com/android/dialer/app/bindings/DialerBindingsStub.java48
-rw-r--r--java/com/android/dialer/app/calllog/BlockReportSpamListener.java212
-rw-r--r--java/com/android/dialer/app/calllog/CallDetailHistoryAdapter.java214
-rw-r--r--java/com/android/dialer/app/calllog/CallLogAdapter.java915
-rw-r--r--java/com/android/dialer/app/calllog/CallLogAlertManager.java90
-rw-r--r--java/com/android/dialer/app/calllog/CallLogAsync.java96
-rw-r--r--java/com/android/dialer/app/calllog/CallLogAsyncTaskUtil.java376
-rw-r--r--java/com/android/dialer/app/calllog/CallLogFragment.java528
-rw-r--r--java/com/android/dialer/app/calllog/CallLogGroupBuilder.java274
-rw-r--r--java/com/android/dialer/app/calllog/CallLogListItemHelper.java277
-rw-r--r--java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java966
-rw-r--r--java/com/android/dialer/app/calllog/CallLogModalAlertManager.java74
-rw-r--r--java/com/android/dialer/app/calllog/CallLogNotificationsHelper.java299
-rw-r--r--java/com/android/dialer/app/calllog/CallLogNotificationsService.java203
-rw-r--r--java/com/android/dialer/app/calllog/CallLogReceiver.java77
-rw-r--r--java/com/android/dialer/app/calllog/CallTypeHelper.java136
-rw-r--r--java/com/android/dialer/app/calllog/CallTypeIconsView.java221
-rw-r--r--java/com/android/dialer/app/calllog/ClearCallLogDialog.java98
-rw-r--r--java/com/android/dialer/app/calllog/DefaultVoicemailNotifier.java273
-rw-r--r--java/com/android/dialer/app/calllog/GroupingListAdapter.java153
-rw-r--r--java/com/android/dialer/app/calllog/IntentProvider.java198
-rw-r--r--java/com/android/dialer/app/calllog/MissedCallNotificationReceiver.java50
-rw-r--r--java/com/android/dialer/app/calllog/MissedCallNotifier.java330
-rw-r--r--java/com/android/dialer/app/calllog/PhoneAccountUtils.java104
-rw-r--r--java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java352
-rw-r--r--java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java75
-rw-r--r--java/com/android/dialer/app/calllog/PhoneNumberDisplayUtil.java85
-rw-r--r--java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java132
-rw-r--r--java/com/android/dialer/app/calllog/VoicemailQueryHandler.java74
-rw-r--r--java/com/android/dialer/app/calllog/calllogcache/CallLogCache.java105
-rw-r--r--java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipop.java74
-rw-r--r--java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipopMr1.java116
-rw-r--r--java/com/android/dialer/app/contactinfo/ContactInfoCache.java357
-rw-r--r--java/com/android/dialer/app/contactinfo/ContactInfoRequest.java122
-rw-r--r--java/com/android/dialer/app/contactinfo/ContactPhotoLoader.java129
-rw-r--r--java/com/android/dialer/app/contactinfo/ExpirableCacheHeadlessFragment.java67
-rw-r--r--java/com/android/dialer/app/contactinfo/NumberWithCountryIso.java57
-rw-r--r--java/com/android/dialer/app/dialpad/DialpadFragment.java1689
-rw-r--r--java/com/android/dialer/app/dialpad/PseudoEmergencyAnimator.java161
-rw-r--r--java/com/android/dialer/app/dialpad/SmartDialCursorLoader.java202
-rw-r--r--java/com/android/dialer/app/dialpad/UnicodeDialerKeyListener.java56
-rw-r--r--java/com/android/dialer/app/filterednumber/BlockedNumbersAdapter.java97
-rw-r--r--java/com/android/dialer/app/filterednumber/BlockedNumbersFragment.java271
-rw-r--r--java/com/android/dialer/app/filterednumber/BlockedNumbersSettingsActivity.java146
-rw-r--r--java/com/android/dialer/app/filterednumber/NumbersAdapter.java138
-rw-r--r--java/com/android/dialer/app/filterednumber/ViewNumbersToImportAdapter.java56
-rw-r--r--java/com/android/dialer/app/filterednumber/ViewNumbersToImportFragment.java130
-rw-r--r--java/com/android/dialer/app/legacybindings/DialerLegacyBindings.java47
-rw-r--r--java/com/android/dialer/app/legacybindings/DialerLegacyBindingsFactory.java26
-rw-r--r--java/com/android/dialer/app/legacybindings/DialerLegacyBindingsStub.java53
-rw-r--r--java/com/android/dialer/app/list/AllContactsFragment.java209
-rw-r--r--java/com/android/dialer/app/list/BlockedListSearchAdapter.java84
-rw-r--r--java/com/android/dialer/app/list/BlockedListSearchFragment.java245
-rw-r--r--java/com/android/dialer/app/list/ContentChangedFilter.java56
-rw-r--r--java/com/android/dialer/app/list/DialerPhoneNumberListAdapter.java228
-rw-r--r--java/com/android/dialer/app/list/DragDropController.java106
-rw-r--r--java/com/android/dialer/app/list/ListsFragment.java587
-rw-r--r--java/com/android/dialer/app/list/OnDragDropListener.java58
-rw-r--r--java/com/android/dialer/app/list/OnListFragmentScrolledListener.java (renamed from src/com/android/dialer/list/OnListFragmentScrolledListener.java)9
-rw-r--r--java/com/android/dialer/app/list/PhoneFavoriteListView.java315
-rw-r--r--java/com/android/dialer/app/list/PhoneFavoriteSquareTileView.java119
-rw-r--r--java/com/android/dialer/app/list/PhoneFavoriteTileView.java155
-rw-r--r--java/com/android/dialer/app/list/PhoneFavoritesTileAdapter.java627
-rw-r--r--java/com/android/dialer/app/list/RegularSearchFragment.java146
-rw-r--r--java/com/android/dialer/app/list/RegularSearchListAdapter.java126
-rw-r--r--java/com/android/dialer/app/list/RemoveView.java105
-rw-r--r--java/com/android/dialer/app/list/SearchFragment.java425
-rw-r--r--java/com/android/dialer/app/list/SmartDialNumberListAdapter.java117
-rw-r--r--java/com/android/dialer/app/list/SmartDialSearchFragment.java120
-rw-r--r--java/com/android/dialer/app/list/SpeedDialFragment.java512
-rw-r--r--java/com/android/dialer/app/manifests/activities/AndroidManifest.xml129
-rw-r--r--java/com/android/dialer/app/res/color/settings_text_color_primary.xml (renamed from res/color/settings_text_color_primary.xml)4
-rw-r--r--java/com/android/dialer/app/res/color/settings_text_color_secondary.xml (renamed from res/color/settings_text_color_secondary.xml)4
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/empty_call_log.png (renamed from res/drawable-hdpi/empty_call_log.png)bin3538 -> 3538 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/empty_contacts.png (renamed from res/drawable-hdpi/empty_contacts.png)bin2461 -> 2461 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/empty_speed_dial.png (renamed from res/drawable-hdpi/empty_speed_dial.png)bin6041 -> 6041 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/fab_ic_dial.png (renamed from res/drawable-hdpi/fab_ic_dial.png)bin1028 -> 1028 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_archive_white_24dp.png (renamed from res/drawable-hdpi/ic_archive_white_24dp.png)bin247 -> 247 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_call_arrow.png (renamed from res/drawable-hdpi/ic_call_arrow.png)bin538 -> 538 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_content_copy_24dp.png (renamed from res/drawable-hdpi/ic_content_copy_24dp.png)bin203 -> 203 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_delete_24dp.png (renamed from res/drawable-hdpi/ic_delete_24dp.png)bin242 -> 242 bytes
-rw-r--r--[-rwxr-xr-x]java/com/android/dialer/app/res/drawable-hdpi/ic_dialer_fork_add_call.png (renamed from res/drawable-hdpi/ic_dialer_fork_add_call.png)bin1649 -> 1649 bytes
-rw-r--r--[-rwxr-xr-x]java/com/android/dialer/app/res/drawable-hdpi/ic_dialer_fork_current_call.png (renamed from res/drawable-hdpi/ic_dialer_fork_current_call.png)bin2305 -> 2305 bytes
-rw-r--r--[-rwxr-xr-x]java/com/android/dialer/app/res/drawable-hdpi/ic_dialer_fork_tt_keypad.png (renamed from res/drawable-hdpi/ic_dialer_fork_tt_keypad.png)bin2419 -> 2419 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_grade_24dp.png (renamed from res/drawable-hdpi/ic_grade_24dp.png)bin370 -> 370 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_handle.png (renamed from res/drawable-hdpi/ic_handle.png)bin543 -> 543 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_menu_history_lt.png (renamed from res/drawable-hdpi/ic_menu_history_lt.png)bin1565 -> 1565 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_mic_grey600.png (renamed from res/drawable-hdpi/ic_mic_grey600.png)bin377 -> 377 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_more_vert_24dp.png (renamed from res/drawable-hdpi/ic_more_vert_24dp.png)bin134 -> 134 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_not_interested_googblue_24dp.png (renamed from res/drawable-hdpi/ic_not_interested_googblue_24dp.png)bin565 -> 565 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_not_spam.pngbin0 -> 858 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_pause_24dp.pngbin0 -> 105 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_people_24dp.pngbin0 -> 299 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_phone_24dp.png (renamed from res/drawable-hdpi/ic_phone_24dp.png)bin347 -> 347 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_play_arrow_24dp.png (renamed from res/drawable-hdpi/ic_play_arrow_24dp.png)bin195 -> 195 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_remove.png (renamed from res/drawable-hdpi/ic_remove.png)bin884 -> 884 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_results_phone.png (renamed from res/drawable-hdpi/ic_results_phone.png)bin1084 -> 1084 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_schedule_24dp.png (renamed from res/drawable-hdpi/ic_schedule_24dp.png)bin575 -> 575 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_share_white_24dp.png (renamed from res/drawable-hdpi/ic_share_white_24dp.png)bin397 -> 397 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_star.png (renamed from res/drawable-hdpi/ic_star.png)bin732 -> 732 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_unblock.png (renamed from res/drawable-hdpi/ic_unblock.png)bin1049 -> 1049 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_vm_sound_off_dis.png (renamed from res/drawable-hdpi/ic_vm_sound_off_dis.png)bin1339 -> 1339 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_vm_sound_off_dk.png (renamed from res/drawable-hdpi/ic_vm_sound_off_dk.png)bin1337 -> 1337 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_vm_sound_on_dis.png (renamed from res/drawable-hdpi/ic_vm_sound_on_dis.png)bin1755 -> 1755 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_vm_sound_on_dk.png (renamed from res/drawable-hdpi/ic_vm_sound_on_dk.png)bin1750 -> 1750 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_voicemail_24dp.pngbin0 -> 478 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_volume_down_24dp.png (renamed from res/drawable-hdpi/ic_volume_down_24dp.png)bin186 -> 186 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/ic_volume_up_24dp.pngbin0 -> 365 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/search_shadow.9.png (renamed from res/drawable-hdpi/search_shadow.9.png)bin183 -> 183 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-hdpi/shadow_contact_photo.png (renamed from res/drawable-hdpi/shadow_contact_photo.png)bin960 -> 960 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/empty_call_log.png (renamed from res/drawable-mdpi/empty_call_log.png)bin2463 -> 2463 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/empty_contacts.png (renamed from res/drawable-mdpi/empty_contacts.png)bin1778 -> 1778 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/empty_speed_dial.png (renamed from res/drawable-mdpi/empty_speed_dial.png)bin4119 -> 4119 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/fab_ic_dial.png (renamed from res/drawable-mdpi/fab_ic_dial.png)bin905 -> 905 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_archive_white_24dp.png (renamed from res/drawable-mdpi/ic_archive_white_24dp.png)bin181 -> 181 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_call_arrow.png (renamed from res/drawable-mdpi/ic_call_arrow.png)bin455 -> 455 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_content_copy_24dp.png (renamed from res/drawable-mdpi/ic_content_copy_24dp.png)bin134 -> 134 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_delete_24dp.png (renamed from res/drawable-mdpi/ic_delete_24dp.png)bin195 -> 195 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_dialer_fork_add_call.png (renamed from res/drawable-mdpi/ic_dialer_fork_add_call.png)bin1309 -> 1309 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_dialer_fork_current_call.png (renamed from res/drawable-mdpi/ic_dialer_fork_current_call.png)bin1581 -> 1581 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_dialer_fork_tt_keypad.png (renamed from res/drawable-mdpi/ic_dialer_fork_tt_keypad.png)bin1586 -> 1586 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_grade_24dp.png (renamed from res/drawable-mdpi/ic_grade_24dp.png)bin271 -> 271 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_handle.png (renamed from res/drawable-mdpi/ic_handle.png)bin454 -> 454 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_menu_history_lt.png (renamed from res/drawable-mdpi/ic_menu_history_lt.png)bin1086 -> 1086 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_mic_grey600.png (renamed from res/drawable-mdpi/ic_mic_grey600.png)bin252 -> 252 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_more_vert_24dp.png (renamed from res/drawable-mdpi/ic_more_vert_24dp.png)bin112 -> 112 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_not_interested_googblue_24dp.png (renamed from res/drawable-mdpi/ic_not_interested_googblue_24dp.png)bin377 -> 377 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_not_spam.pngbin0 -> 627 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_pause_24dp.png (renamed from res/drawable-mdpi/ic_pause_24dp.png)bin83 -> 83 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_people_24dp.png (renamed from res/drawable-mdpi/ic_people_24dp.png)bin210 -> 210 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_phone_24dp.png (renamed from res/drawable-mdpi/ic_phone_24dp.png)bin262 -> 262 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_play_arrow_24dp.png (renamed from res/drawable-mdpi/ic_play_arrow_24dp.png)bin157 -> 157 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_remove.png (renamed from res/drawable-mdpi/ic_remove.png)bin728 -> 728 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_results_phone.png (renamed from res/drawable-mdpi/ic_results_phone.png)bin801 -> 801 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_schedule_24dp.png (renamed from res/drawable-mdpi/ic_schedule_24dp.png)bin377 -> 377 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_share_white_24dp.png (renamed from res/drawable-mdpi/ic_share_white_24dp.png)bin268 -> 268 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_star.png (renamed from res/drawable-mdpi/ic_star.png)bin531 -> 531 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_unblock.png (renamed from res/drawable-mdpi/ic_unblock.png)bin746 -> 746 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_vm_sound_off_dis.png (renamed from res/drawable-mdpi/ic_vm_sound_off_dis.png)bin948 -> 948 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_vm_sound_off_dk.png (renamed from res/drawable-mdpi/ic_vm_sound_off_dk.png)bin945 -> 945 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_vm_sound_on_dis.png (renamed from res/drawable-mdpi/ic_vm_sound_on_dis.png)bin1166 -> 1166 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_vm_sound_on_dk.png (renamed from res/drawable-mdpi/ic_vm_sound_on_dk.png)bin1192 -> 1192 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_voicemail_24dp.png (renamed from res/drawable-mdpi/ic_voicemail_24dp.png)bin221 -> 221 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_volume_down_24dp.png (renamed from res/drawable-mdpi/ic_volume_down_24dp.png)bin139 -> 139 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/ic_volume_up_24dp.png (renamed from res/drawable-mdpi/ic_volume_up_24dp.png)bin251 -> 251 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/search_shadow.9.png (renamed from res/drawable-mdpi/search_shadow.9.png)bin159 -> 159 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-mdpi/shadow_contact_photo.png (renamed from res/drawable-mdpi/shadow_contact_photo.png)bin948 -> 948 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/empty_call_log.png (renamed from res/drawable-xhdpi/empty_call_log.png)bin4860 -> 4860 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/empty_contacts.png (renamed from res/drawable-xhdpi/empty_contacts.png)bin3352 -> 3352 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/empty_speed_dial.png (renamed from res/drawable-xhdpi/empty_speed_dial.png)bin8689 -> 8689 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/fab_ic_dial.png (renamed from res/drawable-xhdpi/fab_ic_dial.png)bin1699 -> 1699 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_archive_white_24dp.png (renamed from res/drawable-xhdpi/ic_archive_white_24dp.png)bin267 -> 267 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_call_arrow.png (renamed from res/drawable-xhdpi/ic_call_arrow.png)bin627 -> 627 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_content_copy_24dp.png (renamed from res/drawable-xhdpi/ic_content_copy_24dp.png)bin188 -> 188 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_delete_24dp.png (renamed from res/drawable-xhdpi/ic_delete_24dp.png)bin271 -> 271 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_dialer_fork_add_call.png (renamed from res/drawable-xhdpi/ic_dialer_fork_add_call.png)bin2150 -> 2150 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_dialer_fork_current_call.png (renamed from res/drawable-xhdpi/ic_dialer_fork_current_call.png)bin3154 -> 3154 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_dialer_fork_tt_keypad.png (renamed from res/drawable-xhdpi/ic_dialer_fork_tt_keypad.png)bin3298 -> 3298 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_grade_24dp.png (renamed from res/drawable-xhdpi/ic_grade_24dp.png)bin479 -> 479 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_handle.png (renamed from res/drawable-xhdpi/ic_handle.png)bin681 -> 681 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_menu_history_lt.png (renamed from res/drawable-xhdpi/ic_menu_history_lt.png)bin2237 -> 2237 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_mic_grey600.png (renamed from res/drawable-xhdpi/ic_mic_grey600.png)bin454 -> 454 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_more_vert_24dp.png (renamed from res/drawable-xhdpi/ic_more_vert_24dp.png)bin158 -> 158 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_not_interested_googblue_24dp.png (renamed from res/drawable-xhdpi/ic_not_interested_googblue_24dp.png)bin755 -> 755 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_not_spam.pngbin0 -> 996 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_pause_24dp.png (renamed from res/drawable-xhdpi/ic_pause_24dp.png)bin90 -> 90 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_people_24dp.png (renamed from res/drawable-xhdpi/ic_people_24dp.png)bin368 -> 368 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_phone_24dp.png (renamed from res/drawable-xhdpi/ic_phone_24dp.png)bin439 -> 439 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_play_arrow_24dp.png (renamed from res/drawable-xhdpi/ic_play_arrow_24dp.png)bin220 -> 220 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_remove.png (renamed from res/drawable-xhdpi/ic_remove.png)bin1237 -> 1237 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_results_phone.png (renamed from res/drawable-xhdpi/ic_results_phone.png)bin1376 -> 1376 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_schedule_24dp.png (renamed from res/drawable-xhdpi/ic_schedule_24dp.png)bin737 -> 737 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_share_white_24dp.png (renamed from res/drawable-xhdpi/ic_share_white_24dp.png)bin496 -> 496 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_star.png (renamed from res/drawable-xhdpi/ic_star.png)bin889 -> 889 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_unblock.png (renamed from res/drawable-xhdpi/ic_unblock.png)bin1356 -> 1356 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_vm_sound_off_dis.png (renamed from res/drawable-xhdpi/ic_vm_sound_off_dis.png)bin1794 -> 1794 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_vm_sound_off_dk.png (renamed from res/drawable-xhdpi/ic_vm_sound_off_dk.png)bin1794 -> 1794 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_vm_sound_on_dis.png (renamed from res/drawable-xhdpi/ic_vm_sound_on_dis.png)bin2354 -> 2354 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_vm_sound_on_dk.png (renamed from res/drawable-xhdpi/ic_vm_sound_on_dk.png)bin2339 -> 2339 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_voicemail_24dp.png (renamed from res/drawable-xhdpi/ic_voicemail_24dp.png)bin487 -> 487 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_volume_down_24dp.png (renamed from res/drawable-xhdpi/ic_volume_down_24dp.png)bin212 -> 212 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/ic_volume_up_24dp.png (renamed from res/drawable-xhdpi/ic_volume_up_24dp.png)bin455 -> 455 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/search_shadow.9.png (renamed from res/drawable-xhdpi/search_shadow.9.png)bin198 -> 198 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xhdpi/shadow_contact_photo.png (renamed from res/drawable-xhdpi/shadow_contact_photo.png)bin965 -> 965 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/empty_call_log.png (renamed from res/drawable-xxhdpi/empty_call_log.png)bin6226 -> 6226 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/empty_contacts.png (renamed from res/drawable-xxhdpi/empty_contacts.png)bin3686 -> 3686 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/empty_speed_dial.png (renamed from res/drawable-xxhdpi/empty_speed_dial.png)bin11039 -> 11039 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/fab_ic_dial.png (renamed from res/drawable-xxhdpi/fab_ic_dial.png)bin3042 -> 3042 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_archive_white_24dp.png (renamed from res/drawable-xxhdpi/ic_archive_white_24dp.png)bin390 -> 390 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_call_arrow.png (renamed from res/drawable-xxhdpi/ic_call_arrow.png)bin1203 -> 1203 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_content_copy_24dp.png (renamed from res/drawable-xxhdpi/ic_content_copy_24dp.png)bin266 -> 266 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_delete_24dp.png (renamed from res/drawable-xxhdpi/ic_delete_24dp.png)bin323 -> 323 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_dialer_fork_add_call.png (renamed from res/drawable-xxhdpi/ic_dialer_fork_add_call.png)bin2583 -> 2583 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_dialer_fork_current_call.png (renamed from res/drawable-xxhdpi/ic_dialer_fork_current_call.png)bin3622 -> 3622 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_dialer_fork_tt_keypad.png (renamed from res/drawable-xxhdpi/ic_dialer_fork_tt_keypad.png)bin3229 -> 3229 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_grade_24dp.png (renamed from res/drawable-xxhdpi/ic_grade_24dp.png)bin676 -> 676 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_handle.png (renamed from res/drawable-xxhdpi/ic_handle.png)bin1431 -> 1431 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_menu_history_lt.png (renamed from res/drawable-xxhdpi/ic_menu_history_lt.png)bin2945 -> 2945 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_mic_grey600.png (renamed from res/drawable-xxhdpi/ic_mic_grey600.png)bin631 -> 631 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_more_vert_24dp.png (renamed from res/drawable-xxhdpi/ic_more_vert_24dp.png)bin216 -> 216 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_not_interested_googblue_24dp.png (renamed from res/drawable-xxhdpi/ic_not_interested_googblue_24dp.png)bin1112 -> 1112 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_not_spam.pngbin0 -> 1340 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_pause_24dp.pngbin0 -> 92 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_people_24dp.pngbin0 -> 488 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_phone_24dp.png (renamed from res/drawable-xxhdpi/ic_phone_24dp.png)bin619 -> 619 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_play_arrow_24dp.png (renamed from res/drawable-xxhdpi/ic_play_arrow_24dp.png)bin283 -> 283 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_remove.png (renamed from res/drawable-xxhdpi/ic_remove.png)bin1942 -> 1942 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_results_phone.png (renamed from res/drawable-xxhdpi/ic_results_phone.png)bin2090 -> 2090 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_schedule_24dp.png (renamed from res/drawable-xxhdpi/ic_schedule_24dp.png)bin1107 -> 1107 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_share_white_24dp.png (renamed from res/drawable-xxhdpi/ic_share_white_24dp.png)bin698 -> 698 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_star.png (renamed from res/drawable-xxhdpi/ic_star.png)bin1539 -> 1539 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_unblock.png (renamed from res/drawable-xxhdpi/ic_unblock.png)bin1990 -> 1990 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_vm_sound_off_dis.png (renamed from res/drawable-xxhdpi/ic_vm_sound_off_dis.png)bin2316 -> 2316 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_vm_sound_off_dk.png (renamed from res/drawable-xxhdpi/ic_vm_sound_off_dk.png)bin2319 -> 2319 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_vm_sound_on_dis.png (renamed from res/drawable-xxhdpi/ic_vm_sound_on_dis.png)bin2878 -> 2878 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_vm_sound_on_dk.png (renamed from res/drawable-xxhdpi/ic_vm_sound_on_dk.png)bin2879 -> 2879 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_voicemail_24dp.pngbin0 -> 625 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_volume_down_24dp.png (renamed from res/drawable-xxhdpi/ic_volume_down_24dp.png)bin291 -> 291 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/ic_volume_up_24dp.pngbin0 -> 654 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/search_shadow.9.png (renamed from res/drawable-xxhdpi/search_shadow.9.png)bin1148 -> 1148 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxhdpi/shadow_contact_photo.png (renamed from res/drawable-xxhdpi/shadow_contact_photo.png)bin970 -> 970 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/empty_call_log.png (renamed from res/drawable-xxxhdpi/empty_call_log.png)bin8761 -> 8761 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/empty_contacts.png (renamed from res/drawable-xxxhdpi/empty_contacts.png)bin5204 -> 5204 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/fab_ic_dial.png (renamed from res/drawable-xxxhdpi/fab_ic_dial.png)bin3800 -> 3800 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_archive_white_24dp.png (renamed from res/drawable-xxxhdpi/ic_archive_white_24dp.png)bin489 -> 489 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_call_arrow.png (renamed from res/drawable-xxxhdpi/ic_call_arrow.png)bin1344 -> 1344 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_content_copy_24dp.png (renamed from res/drawable-xxxhdpi/ic_content_copy_24dp.png)bin329 -> 329 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_delete_24dp.png (renamed from res/drawable-xxxhdpi/ic_delete_24dp.png)bin1394 -> 1394 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_grade_24dp.png (renamed from res/drawable-xxxhdpi/ic_grade_24dp.png)bin887 -> 887 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_handle.png (renamed from res/drawable-xxxhdpi/ic_handle.png)bin1687 -> 1687 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_mic_grey600.png (renamed from res/drawable-xxxhdpi/ic_mic_grey600.png)bin853 -> 853 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_more_vert_24dp.png (renamed from res/drawable-xxxhdpi/ic_more_vert_24dp.png)bin305 -> 305 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_not_interested_googblue_24dp.png (renamed from res/drawable-xxxhdpi/ic_not_interested_googblue_24dp.png)bin1458 -> 1458 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_not_spam.pngbin0 -> 1752 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_pause_24dp.png (renamed from res/drawable-xxxhdpi/ic_pause_24dp.png)bin94 -> 94 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_people_24dp.png (renamed from res/drawable-xxxhdpi/ic_people_24dp.png)bin636 -> 636 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_phone_24dp.png (renamed from res/drawable-xxxhdpi/ic_phone_24dp.png)bin837 -> 837 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_play_arrow_24dp.png (renamed from res/drawable-xxxhdpi/ic_play_arrow_24dp.png)bin343 -> 343 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_results_phone.png (renamed from res/drawable-xxxhdpi/ic_results_phone.png)bin2281 -> 2281 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_schedule_24dp.png (renamed from res/drawable-xxxhdpi/ic_schedule_24dp.png)bin1478 -> 1478 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_share_white_24dp.png (renamed from res/drawable-xxxhdpi/ic_share_white_24dp.png)bin938 -> 938 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_unblock.png (renamed from res/drawable-xxxhdpi/ic_unblock.png)bin1389 -> 1389 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_voicemail_24dp.png (renamed from res/drawable-xxxhdpi/ic_voicemail_24dp.png)bin971 -> 971 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_volume_down_24dp.png (renamed from res/drawable-xxxhdpi/ic_volume_down_24dp.png)bin356 -> 356 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/ic_volume_up_24dp.png (renamed from res/drawable-xxxhdpi/ic_volume_up_24dp.png)bin878 -> 878 bytes
-rw-r--r--java/com/android/dialer/app/res/drawable/background_dial_holo_dark.xml (renamed from res/drawable/background_dial_holo_dark.xml)8
-rw-r--r--java/com/android/dialer/app/res/drawable/floating_action_button.xml (renamed from res/drawable/floating_action_button.xml)12
-rw-r--r--java/com/android/dialer/app/res/drawable/ic_call_detail_content_copy.xml (renamed from res/drawable/ic_call_detail_content_copy.xml)4
-rw-r--r--java/com/android/dialer/app/res/drawable/ic_call_detail_edit.xml (renamed from res/drawable/ic_call_detail_edit.xml)4
-rw-r--r--java/com/android/dialer/app/res/drawable/ic_call_detail_report.xml (renamed from res/drawable/ic_call_detail_report.xml)4
-rw-r--r--java/com/android/dialer/app/res/drawable/ic_call_detail_unblock.xml (renamed from res/drawable/ic_call_detail_unblock.xml)4
-rw-r--r--java/com/android/dialer/app/res/drawable/ic_pause.xml31
-rw-r--r--java/com/android/dialer/app/res/drawable/ic_play_arrow.xml32
-rw-r--r--java/com/android/dialer/app/res/drawable/ic_search_phone.xml (renamed from res/drawable/ic_search_phone.xml)4
-rw-r--r--java/com/android/dialer/app/res/drawable/ic_speakerphone_off.xml (renamed from res/drawable/ic_speakerphone_off.xml)4
-rw-r--r--java/com/android/dialer/app/res/drawable/ic_speakerphone_on.xml (renamed from res/drawable/ic_speakerphone_on.xml)4
-rw-r--r--java/com/android/dialer/app/res/drawable/ic_voicemail_seek_handle.xml (renamed from res/drawable/ic_voicemail_seek_handle.xml)4
-rw-r--r--java/com/android/dialer/app/res/drawable/ic_voicemail_seek_handle_disabled.xml (renamed from res/drawable/ic_voicemail_seek_handle_disabled.xml)4
-rw-r--r--java/com/android/dialer/app/res/drawable/oval_ripple.xml (renamed from res/drawable/oval_ripple.xml)12
-rw-r--r--java/com/android/dialer/app/res/drawable/overflow_menu.xml (renamed from res/drawable/overflow_menu.xml)6
-rw-r--r--java/com/android/dialer/app/res/drawable/rounded_corner.xml (renamed from res/drawable/rounded_corner.xml)6
-rw-r--r--java/com/android/dialer/app/res/drawable/seekbar_drawable.xml63
-rw-r--r--java/com/android/dialer/app/res/drawable/selectable_primary_flat_button.xml (renamed from res/drawable/selectable_primary_flat_button.xml)12
-rw-r--r--java/com/android/dialer/app/res/drawable/shadow_fade_left.xml (renamed from res/drawable/shadow_fade_left.xml)12
-rw-r--r--java/com/android/dialer/app/res/drawable/shadow_fade_up.xml (renamed from res/drawable/shadow_fade_up.xml)12
-rw-r--r--java/com/android/dialer/app/res/layout-land/dialpad_fragment.xml90
-rw-r--r--java/com/android/dialer/app/res/layout-land/empty_content_view_dialpad_search.xml71
-rw-r--r--java/com/android/dialer/app/res/layout/account_filter_header_for_phone_favorite.xml47
-rw-r--r--java/com/android/dialer/app/res/layout/all_contacts_activity.xml (renamed from res/layout/all_contacts_activity.xml)11
-rw-r--r--java/com/android/dialer/app/res/layout/all_contacts_fragment.xml54
-rw-r--r--java/com/android/dialer/app/res/layout/blocked_number_footer.xml38
-rw-r--r--java/com/android/dialer/app/res/layout/blocked_number_fragment.xml30
-rw-r--r--java/com/android/dialer/app/res/layout/blocked_number_header.xml220
-rw-r--r--java/com/android/dialer/app/res/layout/blocked_number_item.xml72
-rw-r--r--java/com/android/dialer/app/res/layout/blocked_numbers_activity.xml (renamed from res/layout/blocked_numbers_activity.xml)8
-rw-r--r--java/com/android/dialer/app/res/layout/call_detail.xml32
-rw-r--r--java/com/android/dialer/app/res/layout/call_detail_footer.xml52
-rw-r--r--java/com/android/dialer/app/res/layout/call_detail_header.xml89
-rw-r--r--java/com/android/dialer/app/res/layout/call_detail_history_item.xml56
-rw-r--r--java/com/android/dialer/app/res/layout/call_log_alert_item.xml22
-rw-r--r--java/com/android/dialer/app/res/layout/call_log_fragment.xml48
-rw-r--r--java/com/android/dialer/app/res/layout/call_log_list_item.xml176
-rw-r--r--java/com/android/dialer/app/res/layout/call_log_list_item_actions.xml230
-rw-r--r--java/com/android/dialer/app/res/layout/dialpad_chooser_list_item.xml38
-rw-r--r--java/com/android/dialer/app/res/layout/dialpad_fragment.xml78
-rw-r--r--java/com/android/dialer/app/res/layout/dialtacts_activity.xml73
-rw-r--r--java/com/android/dialer/app/res/layout/empty_content_view.xml54
-rw-r--r--java/com/android/dialer/app/res/layout/empty_content_view_dialpad_search.xml56
-rw-r--r--java/com/android/dialer/app/res/layout/keyguard_preview.xml30
-rw-r--r--java/com/android/dialer/app/res/layout/lists_fragment.xml98
-rw-r--r--java/com/android/dialer/app/res/layout/phone_favorite_tile_view.xml128
-rw-r--r--java/com/android/dialer/app/res/layout/search_edittext.xml71
-rw-r--r--java/com/android/dialer/app/res/layout/speed_dial_fragment.xml51
-rw-r--r--java/com/android/dialer/app/res/layout/view_numbers_to_import_fragment.xml58
-rw-r--r--java/com/android/dialer/app/res/layout/voicemail_playback_layout.xml115
-rw-r--r--java/com/android/dialer/app/res/menu/dialpad_options.xml30
-rw-r--r--java/com/android/dialer/app/res/menu/dialtacts_options.xml28
-rw-r--r--java/com/android/dialer/app/res/mipmap-hdpi/ic_launcher_phone.png (renamed from res/mipmap-hdpi/ic_launcher_phone.png)bin2780 -> 2780 bytes
-rw-r--r--java/com/android/dialer/app/res/mipmap-mdpi/ic_launcher_phone.png (renamed from res/mipmap-mdpi/ic_launcher_phone.png)bin1778 -> 1778 bytes
-rw-r--r--java/com/android/dialer/app/res/mipmap-xhdpi/ic_launcher_phone.png (renamed from res/mipmap-xhdpi/ic_launcher_phone.png)bin3939 -> 3939 bytes
-rw-r--r--java/com/android/dialer/app/res/mipmap-xxhdpi/ic_launcher_phone.png (renamed from res/mipmap-xxhdpi/ic_launcher_phone.png)bin6251 -> 6251 bytes
-rw-r--r--java/com/android/dialer/app/res/mipmap-xxxhdpi/ic_launcher_phone.png (renamed from res/mipmap-xxxhdpi/ic_launcher_phone.png)bin8793 -> 8793 bytes
-rw-r--r--java/com/android/dialer/app/res/values/animation_constants.xml30
-rw-r--r--java/com/android/dialer/app/res/values/attrs.xml21
-rw-r--r--java/com/android/dialer/app/res/values/colors.xml115
-rw-r--r--java/com/android/dialer/app/res/values/dimens.xml148
-rw-r--r--java/com/android/dialer/app/res/values/donottranslate_config.xml37
-rw-r--r--java/com/android/dialer/app/res/values/ids.xml28
-rw-r--r--java/com/android/dialer/app/res/values/strings.xml960
-rw-r--r--java/com/android/dialer/app/res/values/styles.xml279
-rw-r--r--java/com/android/dialer/app/res/xml/display_options_settings.xml31
-rw-r--r--java/com/android/dialer/app/res/xml/file_paths.xml24
-rw-r--r--java/com/android/dialer/app/res/xml/searchable.xml22
-rw-r--r--java/com/android/dialer/app/res/xml/sound_settings.xml46
-rw-r--r--java/com/android/dialer/app/settings/AppCompatPreferenceActivity.java155
-rw-r--r--java/com/android/dialer/app/settings/DefaultRingtonePreference.java64
-rw-r--r--java/com/android/dialer/app/settings/DialerSettingsActivity.java187
-rw-r--r--java/com/android/dialer/app/settings/DisplayOptionsSettingsFragment.java (renamed from src/com/android/dialer/settings/DisplayOptionsSettingsFragment.java)15
-rw-r--r--java/com/android/dialer/app/settings/SoundSettingsFragment.java242
-rw-r--r--java/com/android/dialer/app/voicemail/VoicemailAudioManager.java252
-rw-r--r--java/com/android/dialer/app/voicemail/VoicemailErrorManager.java129
-rw-r--r--java/com/android/dialer/app/voicemail/VoicemailPlaybackLayout.java449
-rw-r--r--java/com/android/dialer/app/voicemail/VoicemailPlaybackPresenter.java1050
-rw-r--r--java/com/android/dialer/app/voicemail/WiredHeadsetManager.java88
-rw-r--r--java/com/android/dialer/app/voicemail/error/AndroidManifest.xml5
-rw-r--r--java/com/android/dialer/app/voicemail/error/OmtpVoicemailMessageCreator.java177
-rw-r--r--java/com/android/dialer/app/voicemail/error/VoicemailErrorAlert.java165
-rw-r--r--java/com/android/dialer/app/voicemail/error/VoicemailErrorMessage.java178
-rw-r--r--java/com/android/dialer/app/voicemail/error/VoicemailErrorMessageCreator.java45
-rw-r--r--java/com/android/dialer/app/voicemail/error/VoicemailStatus.java260
-rw-r--r--java/com/android/dialer/app/voicemail/error/VoicemailStatusCorruptionHandler.java114
-rw-r--r--java/com/android/dialer/app/voicemail/error/VoicemailStatusReader.java25
-rw-r--r--java/com/android/dialer/app/voicemail/error/VoicemailTosMessage.java25
-rw-r--r--java/com/android/dialer/app/voicemail/error/Vvm3VoicemailMessageCreator.java428
-rw-r--r--java/com/android/dialer/app/voicemail/error/res/layout/voicemai_error_message_fragment.xml114
-rw-r--r--java/com/android/dialer/app/voicemail/error/res/layout/voicemail_tos_fragment.xml72
-rw-r--r--java/com/android/dialer/app/voicemail/error/res/values/dimens.xml12
-rw-r--r--java/com/android/dialer/app/voicemail/error/res/values/strings.xml176
-rw-r--r--java/com/android/dialer/app/voicemail/error/res/values/styles.xml26
-rw-r--r--java/com/android/dialer/app/widget/ActionBarController.java247
-rw-r--r--java/com/android/dialer/app/widget/DialpadSearchEmptyContentView.java43
-rw-r--r--java/com/android/dialer/app/widget/EmptyContentView.java121
-rw-r--r--java/com/android/dialer/app/widget/SearchEditTextLayout.java324
-rw-r--r--java/com/android/dialer/backup/AndroidManifest.xml27
-rw-r--r--java/com/android/dialer/backup/DialerBackupAgent.java276
-rw-r--r--java/com/android/dialer/backup/DialerBackupUtils.java320
-rw-r--r--java/com/android/dialer/backup/proto/VoicemailInfo.java377
-rw-r--r--java/com/android/dialer/blocking/AndroidManifest.xml13
-rw-r--r--java/com/android/dialer/blocking/BlockNumberDialogFragment.java328
-rw-r--r--java/com/android/dialer/blocking/BlockReportSpamDialogs.java305
-rw-r--r--java/com/android/dialer/blocking/BlockedNumbersAutoMigrator.java110
-rw-r--r--java/com/android/dialer/blocking/BlockedNumbersMigrator.java159
-rw-r--r--java/com/android/dialer/blocking/FilteredNumberAsyncQueryHandler.java428
-rw-r--r--java/com/android/dialer/blocking/FilteredNumberCompat.java320
-rw-r--r--java/com/android/dialer/blocking/FilteredNumberProvider.java176
-rw-r--r--java/com/android/dialer/blocking/FilteredNumbersUtil.java380
-rw-r--r--java/com/android/dialer/blocking/MigrateBlockedNumbersDialogFragment.java113
-rw-r--r--java/com/android/dialer/blocking/res/drawable-hdpi/ic_block_24dp.png (renamed from res/drawable-hdpi/ic_block_24dp.png)bin478 -> 478 bytes
-rw-r--r--java/com/android/dialer/blocking/res/drawable-hdpi/ic_report_24dp.png (renamed from res/drawable-hdpi/ic_report_24dp.png)bin240 -> 240 bytes
-rw-r--r--java/com/android/dialer/blocking/res/drawable-hdpi/ic_report_white_36dp.pngbin0 -> 312 bytes
-rw-r--r--java/com/android/dialer/blocking/res/drawable-mdpi/ic_block_24dp.png (renamed from res/drawable-mdpi/ic_block_24dp.png)bin335 -> 335 bytes
-rw-r--r--java/com/android/dialer/blocking/res/drawable-mdpi/ic_report_24dp.png (renamed from res/drawable-mdpi/ic_report_24dp.png)bin174 -> 174 bytes
-rw-r--r--java/com/android/dialer/blocking/res/drawable-mdpi/ic_report_white_36dp.pngbin0 -> 240 bytes
-rw-r--r--java/com/android/dialer/blocking/res/drawable-xhdpi/ic_block_24dp.png (renamed from res/drawable-xhdpi/ic_block_24dp.png)bin665 -> 665 bytes
-rw-r--r--java/com/android/dialer/blocking/res/drawable-xhdpi/ic_report_24dp.png (renamed from res/drawable-xhdpi/ic_report_24dp.png)bin272 -> 272 bytes
-rw-r--r--java/com/android/dialer/blocking/res/drawable-xhdpi/ic_report_white_36dp.png (renamed from res/drawable-xxhdpi/ic_report_24dp.png)bin340 -> 340 bytes
-rw-r--r--java/com/android/dialer/blocking/res/drawable-xxhdpi/ic_block_24dp.png (renamed from res/drawable-xxhdpi/ic_block_24dp.png)bin973 -> 973 bytes
-rw-r--r--java/com/android/dialer/blocking/res/drawable-xxhdpi/ic_report_24dp.pngbin0 -> 340 bytes
-rw-r--r--java/com/android/dialer/blocking/res/drawable-xxhdpi/ic_report_white_36dp.png (renamed from InCallUI/res/drawable-xxhdpi/ic_report_white_36dp.png)bin522 -> 522 bytes
-rw-r--r--java/com/android/dialer/blocking/res/drawable-xxxhdpi/ic_block_24dp.png (renamed from res/drawable-xxxhdpi/ic_block_24dp.png)bin1295 -> 1295 bytes
-rw-r--r--java/com/android/dialer/blocking/res/drawable-xxxhdpi/ic_report_24dp.png (renamed from res/drawable-xxxhdpi/ic_report_24dp.png)bin450 -> 450 bytes
-rw-r--r--java/com/android/dialer/blocking/res/drawable-xxxhdpi/ic_report_white_36dp.png (renamed from InCallUI/res/drawable-xxxhdpi/ic_report_white_36dp.png)bin649 -> 649 bytes
-rw-r--r--java/com/android/dialer/blocking/res/drawable/blocked_contact.xml36
-rw-r--r--java/com/android/dialer/blocking/res/layout/block_report_spam_dialog.xml36
-rw-r--r--java/com/android/dialer/blocking/res/values/colors.xml24
-rw-r--r--java/com/android/dialer/blocking/res/values/dimens.xml18
-rw-r--r--java/com/android/dialer/blocking/res/values/strings.xml122
-rw-r--r--java/com/android/dialer/buildtype/BuildType.java62
-rw-r--r--java/com/android/dialer/buildtype/BuildTypeAccessor.java31
-rw-r--r--java/com/android/dialer/buildtype/dogfood/BuildTypeAccessorImpl.java30
-rw-r--r--java/com/android/dialer/callcomposer/AndroidManifest.xml28
-rw-r--r--java/com/android/dialer/callcomposer/CallComposerActivity.java728
-rw-r--r--java/com/android/dialer/callcomposer/CallComposerFragment.java125
-rw-r--r--java/com/android/dialer/callcomposer/CallComposerPagerAdapter.java57
-rw-r--r--java/com/android/dialer/callcomposer/CameraComposerFragment.java378
-rw-r--r--java/com/android/dialer/callcomposer/GalleryComposerFragment.java256
-rw-r--r--java/com/android/dialer/callcomposer/GalleryCursorLoader.java54
-rw-r--r--java/com/android/dialer/callcomposer/GalleryGridAdapter.java118
-rw-r--r--java/com/android/dialer/callcomposer/GalleryGridItemData.java91
-rw-r--r--java/com/android/dialer/callcomposer/GalleryGridItemView.java126
-rw-r--r--java/com/android/dialer/callcomposer/MessageComposerFragment.java143
-rw-r--r--java/com/android/dialer/callcomposer/camera/AndroidManifest.xml16
-rw-r--r--java/com/android/dialer/callcomposer/camera/CameraManager.java822
-rw-r--r--java/com/android/dialer/callcomposer/camera/CameraPreview.java177
-rw-r--r--java/com/android/dialer/callcomposer/camera/HardwareCameraPreview.java125
-rw-r--r--java/com/android/dialer/callcomposer/camera/ImagePersistTask.java143
-rw-r--r--java/com/android/dialer/callcomposer/camera/SoftwareCameraPreview.java120
-rw-r--r--java/com/android/dialer/callcomposer/camera/camerafocus/AndroidManifest.xml16
-rw-r--r--java/com/android/dialer/callcomposer/camera/camerafocus/FocusIndicator.java28
-rw-r--r--java/com/android/dialer/callcomposer/camera/camerafocus/FocusOverlayManager.java482
-rw-r--r--java/com/android/dialer/callcomposer/camera/camerafocus/OverlayRenderer.java97
-rw-r--r--java/com/android/dialer/callcomposer/camera/camerafocus/PieItem.java179
-rw-r--r--java/com/android/dialer/callcomposer/camera/camerafocus/PieRenderer.java816
-rw-r--r--java/com/android/dialer/callcomposer/camera/camerafocus/RenderOverlay.java153
-rw-r--r--java/com/android/dialer/callcomposer/camera/camerafocus/res/values/dimens.xml26
-rw-r--r--java/com/android/dialer/callcomposer/camera/exif/CountedDataInputStream.java129
-rw-r--r--java/com/android/dialer/callcomposer/camera/exif/ExifData.java89
-rw-r--r--java/com/android/dialer/callcomposer/camera/exif/ExifInterface.java374
-rw-r--r--java/com/android/dialer/callcomposer/camera/exif/ExifInvalidFormatException.java24
-rw-r--r--java/com/android/dialer/callcomposer/camera/exif/ExifParser.java846
-rw-r--r--java/com/android/dialer/callcomposer/camera/exif/ExifReader.java81
-rw-r--r--java/com/android/dialer/callcomposer/camera/exif/ExifTag.java619
-rw-r--r--java/com/android/dialer/callcomposer/camera/exif/IfdData.java126
-rw-r--r--java/com/android/dialer/callcomposer/camera/exif/IfdId.java28
-rw-r--r--java/com/android/dialer/callcomposer/camera/exif/JpegHeader.java38
-rw-r--r--java/com/android/dialer/callcomposer/camera/exif/Rational.java70
-rw-r--r--java/com/android/dialer/callcomposer/cameraui/AndroidManifest.xml16
-rw-r--r--java/com/android/dialer/callcomposer/cameraui/CameraMediaChooserView.java107
-rw-r--r--java/com/android/dialer/callcomposer/cameraui/res/drawable-hdpi/ic_capture.pngbin0 -> 2690 bytes
-rw-r--r--java/com/android/dialer/callcomposer/cameraui/res/drawable-mdpi/ic_capture.pngbin0 -> 1851 bytes
-rw-r--r--java/com/android/dialer/callcomposer/cameraui/res/drawable-xhdpi/ic_capture.pngbin0 -> 3636 bytes
-rw-r--r--java/com/android/dialer/callcomposer/cameraui/res/drawable-xxhdpi/ic_capture.pngbin0 -> 5449 bytes
-rw-r--r--java/com/android/dialer/callcomposer/cameraui/res/drawable-xxxhdpi/ic_capture.pngbin0 -> 7354 bytes
-rw-r--r--java/com/android/dialer/callcomposer/cameraui/res/drawable/transparent_button_background.xml26
-rw-r--r--java/com/android/dialer/callcomposer/cameraui/res/layout/camera_view.xml121
-rw-r--r--java/com/android/dialer/callcomposer/cameraui/res/values/colors.xml4
-rw-r--r--java/com/android/dialer/callcomposer/cameraui/res/values/dimens.xml22
-rw-r--r--java/com/android/dialer/callcomposer/cameraui/res/values/strings.xml17
-rw-r--r--java/com/android/dialer/callcomposer/nano/CallComposerContact.java220
-rw-r--r--java/com/android/dialer/callcomposer/res/drawable/call_composer_contact_border.xml30
-rw-r--r--java/com/android/dialer/callcomposer/res/drawable/gallery_background.xml22
-rw-r--r--java/com/android/dialer/callcomposer/res/drawable/gallery_grid_checkbox_background.xml22
-rw-r--r--java/com/android/dialer/callcomposer/res/drawable/gallery_grid_item_view_background.xml22
-rw-r--r--java/com/android/dialer/callcomposer/res/drawable/gallery_item_selected_drawable.xml37
-rw-r--r--java/com/android/dialer/callcomposer/res/layout/call_composer_activity.xml147
-rw-r--r--java/com/android/dialer/callcomposer/res/layout/fragment_camera_composer.xml33
-rw-r--r--java/com/android/dialer/callcomposer/res/layout/fragment_gallery_composer.xml38
-rw-r--r--java/com/android/dialer/callcomposer/res/layout/fragment_message_composer.xml79
-rw-r--r--java/com/android/dialer/callcomposer/res/layout/gallery_grid_item_view.xml57
-rw-r--r--java/com/android/dialer/callcomposer/res/layout/permission_view.xml52
-rw-r--r--java/com/android/dialer/callcomposer/res/values/colors.xml24
-rw-r--r--java/com/android/dialer/callcomposer/res/values/dimens.xml63
-rw-r--r--java/com/android/dialer/callcomposer/res/values/strings.xml42
-rw-r--r--java/com/android/dialer/callcomposer/res/values/styles.xml50
-rw-r--r--java/com/android/dialer/callcomposer/util/CopyAndResizeImageTask.java124
-rw-r--r--java/com/android/dialer/callintent/CallIntentBuilder.java108
-rw-r--r--java/com/android/dialer/callintent/CallIntentParser.java54
-rw-r--r--java/com/android/dialer/callintent/Constants.java31
-rw-r--r--java/com/android/dialer/callintent/nano/CallInitiationType.java101
-rw-r--r--java/com/android/dialer/callintent/nano/CallSpecificAppData.java143
-rw-r--r--java/com/android/dialer/common/AndroidManifest.xml3
-rw-r--r--java/com/android/dialer/common/Assert.java185
-rw-r--r--java/com/android/dialer/common/AsyncTaskExecutor.java51
-rw-r--r--java/com/android/dialer/common/AsyncTaskExecutors.java91
-rw-r--r--java/com/android/dialer/common/AutoValue_FallibleAsyncTask_FallibleTaskResult.java79
-rw-r--r--java/com/android/dialer/common/ConfigProvider.java27
-rw-r--r--java/com/android/dialer/common/ConfigProviderBindings.java68
-rw-r--r--java/com/android/dialer/common/ConfigProviderFactory.java26
-rw-r--r--java/com/android/dialer/common/DpUtil.java31
-rw-r--r--java/com/android/dialer/common/FallibleAsyncTask.java94
-rw-r--r--java/com/android/dialer/common/FragmentUtils.java98
-rw-r--r--java/com/android/dialer/common/LogUtil.java214
-rw-r--r--java/com/android/dialer/common/MathUtil.java57
-rw-r--r--java/com/android/dialer/common/NetworkUtil.java192
-rw-r--r--java/com/android/dialer/common/UiUtil.java41
-rw-r--r--java/com/android/dialer/common/res/values/strings.xml5
-rw-r--r--java/com/android/dialer/compat/ActivityCompat.java29
-rw-r--r--java/com/android/dialer/compat/AppCompatConstants.java33
-rw-r--r--java/com/android/dialer/compat/CompatUtils.java222
-rw-r--r--java/com/android/dialer/compat/PathInterpolatorCompat.java120
-rw-r--r--java/com/android/dialer/compat/SdkVersionOverride.java43
-rw-r--r--java/com/android/dialer/constants/Constants.java47
-rw-r--r--java/com/android/dialer/constants/ScheduledJobIds.java31
-rw-r--r--java/com/android/dialer/constants/aospdialer/ConstantsImpl.java37
-rw-r--r--java/com/android/dialer/database/CallLogQueryHandler.java369
-rw-r--r--java/com/android/dialer/database/Database.java49
-rw-r--r--java/com/android/dialer/database/DatabaseBindings.java25
-rw-r--r--java/com/android/dialer/database/DatabaseBindingsFactory.java26
-rw-r--r--java/com/android/dialer/database/DatabaseBindingsStub.java35
-rw-r--r--java/com/android/dialer/database/DialerDatabaseHelper.java1242
-rw-r--r--java/com/android/dialer/database/FilteredNumberContract.java137
-rw-r--r--java/com/android/dialer/database/VoicemailStatusQuery.java91
-rw-r--r--java/com/android/dialer/debug/AndroidManifest.xml3
-rw-r--r--java/com/android/dialer/debug/bindings/impl/DebugBindings.java32
-rw-r--r--java/com/android/dialer/debug/impl/AndroidManifest.xml18
-rw-r--r--java/com/android/dialer/debug/impl/DebugConnection.java55
-rw-r--r--java/com/android/dialer/debug/impl/DebugConnectionService.java103
-rw-r--r--java/com/android/dialer/dialpadview/AndroidManifest.xml3
-rw-r--r--java/com/android/dialer/dialpadview/DialpadKeyButton.java231
-rw-r--r--java/com/android/dialer/dialpadview/DialpadTextView.java71
-rw-r--r--java/com/android/dialer/dialpadview/DialpadView.java464
-rw-r--r--java/com/android/dialer/dialpadview/DigitsEditText.java57
-rw-r--r--java/com/android/dialer/dialpadview/res/anim/dialpad_slide_in_bottom.xml19
-rw-r--r--java/com/android/dialer/dialpadview/res/anim/dialpad_slide_in_left.xml22
-rw-r--r--java/com/android/dialer/dialpadview/res/anim/dialpad_slide_in_right.xml20
-rw-r--r--java/com/android/dialer/dialpadview/res/anim/dialpad_slide_out_bottom.xml19
-rw-r--r--java/com/android/dialer/dialpadview/res/anim/dialpad_slide_out_left.xml22
-rw-r--r--java/com/android/dialer/dialpadview/res/anim/dialpad_slide_out_right.xml20
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-hdpi/dialer_fab.pngbin0 -> 3273 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-hdpi/fab_green.pngbin0 -> 2798 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-hdpi/fab_ic_call.png (renamed from res/drawable-hdpi/fab_ic_call.png)bin875 -> 875 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_close_black_24dp.pngbin0 -> 207 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_dialpad_delete.pngbin0 -> 805 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_dialpad_voicemail.pngbin0 -> 623 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_overflow_menu.pngbin0 -> 503 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-mdpi/dialer_fab.pngbin0 -> 1945 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-mdpi/fab_green.pngbin0 -> 1845 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-mdpi/fab_ic_call.png (renamed from res/drawable-mdpi/fab_ic_call.png)bin698 -> 698 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_close_black_24dp.pngbin0 -> 164 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_dialpad_delete.pngbin0 -> 669 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_dialpad_voicemail.pngbin0 -> 504 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_overflow_menu.pngbin0 -> 424 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xhdpi/dialer_fab.pngbin0 -> 4872 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xhdpi/fab_green.pngbin0 -> 4092 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xhdpi/fab_ic_call.png (renamed from res/drawable-xhdpi/fab_ic_call.png)bin1266 -> 1266 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_close_black_24dp.pngbin0 -> 235 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_dialpad_delete.pngbin0 -> 1110 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_dialpad_voicemail.pngbin0 -> 787 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_overflow_menu.pngbin0 -> 550 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xxhdpi/dialer_fab.pngbin0 -> 8621 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xxhdpi/fab_green.pngbin0 -> 7004 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xxhdpi/fab_ic_call.png (renamed from res/drawable-xxhdpi/fab_ic_call.png)bin2321 -> 2321 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_close_black_24dp.pngbin0 -> 309 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_dialpad_delete.pngbin0 -> 1745 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_dialpad_voicemail.pngbin0 -> 1578 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_overflow_menu.pngbin0 -> 1384 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/dialer_fab.pngbin0 -> 12782 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/fab_green.pngbin0 -> 9900 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/fab_ic_call.png (renamed from InCallUI/res/drawable-xxxhdpi/fab_ic_call.png)bin2921 -> 2921 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_close_black_24dp.pngbin0 -> 377 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_dialpad_delete.pngbin0 -> 2128 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_dialpad_voicemail.pngbin0 -> 1829 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_overflow_menu.pngbin0 -> 1785 bytes
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable/btn_dialpad_key.xml18
-rw-r--r--java/com/android/dialer/dialpadview/res/drawable/dialpad_scrim.xml7
-rw-r--r--java/com/android/dialer/dialpadview/res/layout-land/dialpad_key.xml44
-rw-r--r--java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_one.xml44
-rw-r--r--java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_pound.xml33
-rw-r--r--java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_star.xml33
-rw-r--r--java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_zero.xml44
-rw-r--r--java/com/android/dialer/dialpadview/res/layout/dialpad.xml99
-rw-r--r--java/com/android/dialer/dialpadview/res/layout/dialpad_key.xml35
-rw-r--r--java/com/android/dialer/dialpadview/res/layout/dialpad_key_one.xml41
-rw-r--r--java/com/android/dialer/dialpadview/res/layout/dialpad_key_pound.xml26
-rw-r--r--java/com/android/dialer/dialpadview/res/layout/dialpad_key_star.xml26
-rw-r--r--java/com/android/dialer/dialpadview/res/layout/dialpad_key_zero.xml37
-rw-r--r--java/com/android/dialer/dialpadview/res/layout/dialpad_view.xml23
-rw-r--r--java/com/android/dialer/dialpadview/res/layout/dialpad_view_unthemed.xml153
-rw-r--r--java/com/android/dialer/dialpadview/res/values-land/dimens.xml27
-rw-r--r--java/com/android/dialer/dialpadview/res/values-land/styles.xml37
-rw-r--r--java/com/android/dialer/dialpadview/res/values/animation_constants.xml20
-rw-r--r--java/com/android/dialer/dialpadview/res/values/attrs.xml39
-rw-r--r--java/com/android/dialer/dialpadview/res/values/colors.xml27
-rw-r--r--java/com/android/dialer/dialpadview/res/values/dimens.xml48
-rw-r--r--java/com/android/dialer/dialpadview/res/values/strings.xml53
-rw-r--r--java/com/android/dialer/dialpadview/res/values/styles.xml118
-rw-r--r--java/com/android/dialer/disabled_lint_checks.txt1
-rw-r--r--java/com/android/dialer/enrichedcall/AutoValue_EnrichedCallCapabilities.java76
-rw-r--r--java/com/android/dialer/enrichedcall/AutoValue_OutgoingCallComposerData.java127
-rw-r--r--java/com/android/dialer/enrichedcall/EnrichedCallCapabilities.java36
-rw-r--r--java/com/android/dialer/enrichedcall/EnrichedCallManager.java225
-rw-r--r--java/com/android/dialer/enrichedcall/EnrichedCallManagerStub.java84
-rw-r--r--java/com/android/dialer/enrichedcall/OutgoingCallComposerData.java94
-rw-r--r--java/com/android/dialer/enrichedcall/Session.java63
-rw-r--r--java/com/android/dialer/enrichedcall/StubEnrichedCallModule.java32
-rw-r--r--java/com/android/dialer/enrichedcall/extensions/StateExtension.java54
-rw-r--r--java/com/android/dialer/inject/ApplicationModule.java39
-rw-r--r--java/com/android/dialer/inject/DialerAppComponent.java29
-rw-r--r--java/com/android/dialer/interactions/AndroidManifest.xml20
-rw-r--r--java/com/android/dialer/interactions/ContactUpdateService.java48
-rw-r--r--java/com/android/dialer/interactions/PhoneNumberInteraction.java557
-rw-r--r--java/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java107
-rw-r--r--java/com/android/dialer/interactions/res/layout/phone_disambig_item.xml43
-rw-r--r--java/com/android/dialer/interactions/res/layout/set_primary_checkbox.xml32
-rw-r--r--java/com/android/dialer/interactions/res/values/strings.xml29
-rw-r--r--java/com/android/dialer/logging/Logger.java49
-rw-r--r--java/com/android/dialer/logging/LoggingBindings.java59
-rw-r--r--java/com/android/dialer/logging/LoggingBindingsFactory.java24
-rw-r--r--java/com/android/dialer/logging/LoggingBindingsStub.java36
-rw-r--r--java/com/android/dialer/logging/nano/ContactLookupResult.java91
-rw-r--r--java/com/android/dialer/logging/nano/ContactSource.java90
-rw-r--r--java/com/android/dialer/logging/nano/DialerImpression.java178
-rw-r--r--java/com/android/dialer/logging/nano/InteractionEvent.java95
-rw-r--r--java/com/android/dialer/logging/nano/ReportingLocation.java87
-rw-r--r--java/com/android/dialer/logging/nano/ScreenEvent.java104
-rw-r--r--java/com/android/dialer/multimedia/AutoValue_MultimediaData.java165
-rw-r--r--java/com/android/dialer/multimedia/MultimediaData.java100
-rw-r--r--java/com/android/dialer/p13n/inference/P13nRanking.java75
-rw-r--r--java/com/android/dialer/p13n/inference/protocol/P13nRanker.java75
-rw-r--r--java/com/android/dialer/p13n/inference/protocol/P13nRankerFactory.java26
-rw-r--r--java/com/android/dialer/p13n/logging/P13nLogger.java35
-rw-r--r--java/com/android/dialer/p13n/logging/P13nLoggerFactory.java29
-rw-r--r--java/com/android/dialer/p13n/logging/P13nLogging.java60
-rw-r--r--java/com/android/dialer/phonenumbercache/CachedNumberLookupService.java77
-rw-r--r--java/com/android/dialer/phonenumbercache/CallLogQuery.java107
-rw-r--r--java/com/android/dialer/phonenumbercache/ContactInfo.java165
-rw-r--r--java/com/android/dialer/phonenumbercache/ContactInfoHelper.java586
-rw-r--r--java/com/android/dialer/phonenumbercache/PhoneLookupUtil.java40
-rw-r--r--java/com/android/dialer/phonenumbercache/PhoneNumberCache.java50
-rw-r--r--java/com/android/dialer/phonenumbercache/PhoneNumberCacheBindings.java26
-rw-r--r--java/com/android/dialer/phonenumbercache/PhoneNumberCacheBindingsFactory.java26
-rw-r--r--java/com/android/dialer/phonenumbercache/PhoneNumberCacheBindingsStub.java29
-rw-r--r--java/com/android/dialer/phonenumbercache/PhoneQuery.java96
-rw-r--r--java/com/android/dialer/phonenumberutil/AndroidManifest.xml3
-rw-r--r--java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java276
-rw-r--r--java/com/android/dialer/phonenumberutil/res/values/strings.xml27
-rw-r--r--java/com/android/dialer/proguard/UsedByReflection.java34
-rw-r--r--java/com/android/dialer/protos/ProtoParsers.java167
-rw-r--r--java/com/android/dialer/shortcuts/AndroidManifest.xml50
-rw-r--r--java/com/android/dialer/shortcuts/AutoValue_DialerShortcut.java161
-rw-r--r--java/com/android/dialer/shortcuts/CallContactActivity.java133
-rw-r--r--java/com/android/dialer/shortcuts/DialerShortcut.java190
-rw-r--r--java/com/android/dialer/shortcuts/DynamicShortcuts.java243
-rw-r--r--java/com/android/dialer/shortcuts/IconFactory.java112
-rw-r--r--java/com/android/dialer/shortcuts/PeriodicJobService.java118
-rw-r--r--java/com/android/dialer/shortcuts/PinnedShortcuts.java159
-rw-r--r--java/com/android/dialer/shortcuts/RefreshShortcutsTask.java71
-rw-r--r--java/com/android/dialer/shortcuts/ShortcutInfoFactory.java100
-rw-r--r--java/com/android/dialer/shortcuts/ShortcutRefresher.java86
-rw-r--r--java/com/android/dialer/shortcuts/ShortcutUsageReporter.java132
-rw-r--r--java/com/android/dialer/shortcuts/Shortcuts.java34
-rw-r--r--java/com/android/dialer/shortcuts/ShortcutsJobScheduler.java48
-rw-r--r--java/com/android/dialer/shortcuts/res/drawable/ic_shortcut_add_contact.xml39
-rw-r--r--java/com/android/dialer/shortcuts/res/values/colors.xml20
-rw-r--r--java/com/android/dialer/shortcuts/res/values/dimens.xml19
-rw-r--r--java/com/android/dialer/shortcuts/res/values/strings.xml37
-rw-r--r--java/com/android/dialer/shortcuts/res/values/themes.xml39
-rw-r--r--java/com/android/dialer/shortcuts/res/xml/shortcuts.xml31
-rw-r--r--java/com/android/dialer/simulator/Simulator.java27
-rw-r--r--java/com/android/dialer/simulator/impl/AndroidManifest.xml18
-rw-r--r--java/com/android/dialer/simulator/impl/AutoValue_SimulatorCallLog_CallEntry.java160
-rw-r--r--java/com/android/dialer/simulator/impl/AutoValue_SimulatorContacts_Contact.java231
-rw-r--r--java/com/android/dialer/simulator/impl/AutoValue_SimulatorVoicemail_Voicemail.java184
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorActionProvider.java88
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorCallLog.java139
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorConnection.java56
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorConnectionService.java87
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorContacts.java319
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorModule.java34
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java47
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorVoicemail.java154
-rw-r--r--java/com/android/dialer/smartdial/LatinSmartDialMap.java784
-rw-r--r--java/com/android/dialer/smartdial/SmartDialMap.java60
-rw-r--r--java/com/android/dialer/smartdial/SmartDialMatchPosition.java70
-rw-r--r--java/com/android/dialer/smartdial/SmartDialNameMatcher.java434
-rw-r--r--java/com/android/dialer/smartdial/SmartDialPrefix.java605
-rw-r--r--java/com/android/dialer/spam/Spam.java49
-rw-r--r--java/com/android/dialer/spam/SpamBindings.java146
-rw-r--r--java/com/android/dialer/spam/SpamBindingsFactory.java26
-rw-r--r--java/com/android/dialer/spam/SpamBindingsStub.java92
-rw-r--r--java/com/android/dialer/telecom/TelecomUtil.java212
-rw-r--r--java/com/android/dialer/theme/AndroidManifest.xml3
-rw-r--r--java/com/android/dialer/theme/res/anim/front_back_switch_button_animation.xml14
-rw-r--r--java/com/android/dialer/theme/res/animator/activated_button_elevation.xml21
-rw-r--r--java/com/android/dialer/theme/res/animator/button_elevation.xml21
-rw-r--r--java/com/android/dialer/theme/res/drawable/front_back_switch_button.xml75
-rw-r--r--java/com/android/dialer/theme/res/drawable/front_back_switch_button_animation.xml8
-rw-r--r--java/com/android/dialer/theme/res/values/colors.xml64
-rw-r--r--java/com/android/dialer/theme/res/values/dimens.xml28
-rw-r--r--java/com/android/dialer/theme/res/values/strings.xml27
-rw-r--r--java/com/android/dialer/theme/res/values/styles.xml56
-rw-r--r--java/com/android/dialer/theme/res/values/themes.xml21
-rw-r--r--java/com/android/dialer/util/AndroidManifest.xml3
-rw-r--r--java/com/android/dialer/util/CallUtil.java135
-rw-r--r--java/com/android/dialer/util/DialerUtils.java246
-rw-r--r--java/com/android/dialer/util/DrawableConverter.java97
-rw-r--r--java/com/android/dialer/util/ExpirableCache.java269
-rw-r--r--java/com/android/dialer/util/IntentUtil.java78
-rw-r--r--java/com/android/dialer/util/MoreStrings.java64
-rw-r--r--java/com/android/dialer/util/OrientationUtil.java30
-rw-r--r--java/com/android/dialer/util/PermissionsUtil.java121
-rw-r--r--java/com/android/dialer/util/SettingsUtil.java95
-rw-r--r--java/com/android/dialer/util/TouchPointManager.java60
-rw-r--r--java/com/android/dialer/util/TransactionSafeActivity.java64
-rw-r--r--java/com/android/dialer/util/ViewUtil.java129
-rw-r--r--java/com/android/dialer/util/res/values/strings.xml42
-rw-r--r--java/com/android/dialer/voicemailstatus/AndroidManifest.xml3
-rw-r--r--java/com/android/dialer/voicemailstatus/VisualVoicemailEnabledChecker.java111
-rw-r--r--java/com/android/dialer/voicemailstatus/VoicemailStatusHelper.java96
-rw-r--r--java/com/android/dialer/voicemailstatus/VoicemailStatusHelperImpl.java278
-rw-r--r--java/com/android/dialer/voicemailstatus/res/values/strings.xml41
-rw-r--r--java/com/android/dialer/widget/AndroidManifest.xml3
-rw-r--r--java/com/android/dialer/widget/ResizingTextEditText.java51
-rw-r--r--java/com/android/dialer/widget/ResizingTextTextView.java51
-rw-r--r--java/com/android/dialer/widget/res/values/attrs.xml23
-rw-r--r--java/com/android/incallui/AccelerometerListener.java173
-rw-r--r--java/com/android/incallui/AndroidManifest.xml121
-rw-r--r--java/com/android/incallui/AnswerScreenPresenter.java110
-rw-r--r--java/com/android/incallui/AnswerScreenPresenterStub.java44
-rw-r--r--java/com/android/incallui/AudioModeProvider.java69
-rw-r--r--java/com/android/incallui/Bindings.java52
-rw-r--r--java/com/android/incallui/CallButtonPresenter.java515
-rw-r--r--java/com/android/incallui/CallCardPresenter.java1110
-rw-r--r--java/com/android/incallui/CallerInfo.java573
-rw-r--r--java/com/android/incallui/CallerInfoAsyncQuery.java638
-rw-r--r--java/com/android/incallui/CallerInfoUtils.java279
-rw-r--r--java/com/android/incallui/ConferenceManagerFragment.java106
-rw-r--r--java/com/android/incallui/ConferenceManagerPresenter.java139
-rw-r--r--java/com/android/incallui/ConferenceParticipantListAdapter.java523
-rw-r--r--java/com/android/incallui/ContactInfoCache.java759
-rw-r--r--java/com/android/incallui/ContactsAsyncHelper.java269
-rw-r--r--java/com/android/incallui/ContactsPreferencesFactory.java56
-rw-r--r--java/com/android/incallui/DialpadFragment.java461
-rw-r--r--java/com/android/incallui/DialpadPresenter.java91
-rw-r--r--java/com/android/incallui/ExternalCallNotifier.java465
-rw-r--r--java/com/android/incallui/InCallActivity.java756
-rw-r--r--java/com/android/incallui/InCallActivityCommon.java820
-rw-r--r--java/com/android/incallui/InCallCameraManager.java173
-rw-r--r--java/com/android/incallui/InCallOrientationEventListener.java194
-rw-r--r--java/com/android/incallui/InCallPresenter.java1679
-rw-r--r--java/com/android/incallui/InCallServiceImpl.java99
-rw-r--r--java/com/android/incallui/InCallUIMaterialColorMapUtils.java67
-rw-r--r--java/com/android/incallui/Log.java145
-rw-r--r--java/com/android/incallui/ManageConferenceActivity.java86
-rw-r--r--java/com/android/incallui/NotificationBroadcastReceiver.java165
-rw-r--r--java/com/android/incallui/PostCharDialogFragment.java96
-rw-r--r--java/com/android/incallui/ProximitySensor.java292
-rw-r--r--java/com/android/incallui/StatusBarNotifier.java842
-rw-r--r--java/com/android/incallui/ThemeColorManager.java142
-rw-r--r--java/com/android/incallui/TransactionSafeFragmentActivity.java64
-rw-r--r--java/com/android/incallui/VideoCallPresenter.java1289
-rw-r--r--java/com/android/incallui/VideoPauseController.java416
-rw-r--r--java/com/android/incallui/answer/bindings/AnswerBindings.java29
-rw-r--r--java/com/android/incallui/answer/impl/AffordanceHolderLayout.java178
-rw-r--r--java/com/android/incallui/answer/impl/AndroidManifest.xml3
-rw-r--r--java/com/android/incallui/answer/impl/AnswerFragment.java981
-rw-r--r--java/com/android/incallui/answer/impl/AnswerVideoCallScreen.java127
-rw-r--r--java/com/android/incallui/answer/impl/CreateCustomSmsDialogFragment.java137
-rw-r--r--java/com/android/incallui/answer/impl/PillDrawable.java43
-rw-r--r--java/com/android/incallui/answer/impl/SmsBottomSheetFragment.java136
-rw-r--r--java/com/android/incallui/answer/impl/affordance/AndroidManifest.xml3
-rw-r--r--java/com/android/incallui/answer/impl/affordance/SwipeButtonHelper.java642
-rw-r--r--java/com/android/incallui/answer/impl/affordance/SwipeButtonView.java505
-rw-r--r--java/com/android/incallui/answer/impl/affordance/res/values/dimens.xml23
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/AndroidManifest.xml3
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/AnswerMethod.java45
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/AnswerMethodFactory.java52
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/AnswerMethodHolder.java47
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/FlingUpDownMethod.java1149
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/FlingUpDownTouchHandler.java496
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/TwoButtonMethod.java268
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/res/drawable/call_answer.xml19
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/res/drawable/circular_background.xml6
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/res/layout/swipe_up_down_method.xml115
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/res/layout/two_button_method.xml97
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/res/values-h240dp/values.xml20
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/res/values-h280dp/dimens.xml21
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/res/values-h480dp/dimens.xml20
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/res/values/dimens.xml27
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/res/values/ids.xml5
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/res/values/strings.xml14
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/res/values/styles.xml7
-rw-r--r--java/com/android/incallui/answer/impl/answermethod/res/values/values.xml25
-rw-r--r--java/com/android/incallui/answer/impl/classifier/AccelerationClassifier.java99
-rw-r--r--java/com/android/incallui/answer/impl/classifier/AnglesClassifier.java193
-rw-r--r--java/com/android/incallui/answer/impl/classifier/AnglesPercentageEvaluator.java33
-rw-r--r--java/com/android/incallui/answer/impl/classifier/AnglesVarianceEvaluator.java42
-rw-r--r--java/com/android/incallui/answer/impl/classifier/Classifier.java35
-rw-r--r--java/com/android/incallui/answer/impl/classifier/ClassifierData.java96
-rw-r--r--java/com/android/incallui/answer/impl/classifier/DirectionClassifier.java37
-rw-r--r--java/com/android/incallui/answer/impl/classifier/DirectionEvaluator.java23
-rw-r--r--java/com/android/incallui/answer/impl/classifier/DurationCountClassifier.java35
-rw-r--r--java/com/android/incallui/answer/impl/classifier/DurationCountEvaluator.java39
-rw-r--r--java/com/android/incallui/answer/impl/classifier/EndPointLengthClassifier.java36
-rw-r--r--java/com/android/incallui/answer/impl/classifier/EndPointLengthEvaluator.java42
-rw-r--r--java/com/android/incallui/answer/impl/classifier/EndPointRatioClassifier.java43
-rw-r--r--java/com/android/incallui/answer/impl/classifier/EndPointRatioEvaluator.java42
-rw-r--r--java/com/android/incallui/answer/impl/classifier/FalsingManager.java140
-rw-r--r--java/com/android/incallui/answer/impl/classifier/GestureClassifier.java31
-rw-r--r--java/com/android/incallui/answer/impl/classifier/HistoryEvaluator.java115
-rw-r--r--java/com/android/incallui/answer/impl/classifier/HumanInteractionClassifier.java142
-rw-r--r--java/com/android/incallui/answer/impl/classifier/LengthCountClassifier.java39
-rw-r--r--java/com/android/incallui/answer/impl/classifier/LengthCountEvaluator.java45
-rw-r--r--java/com/android/incallui/answer/impl/classifier/Point.java95
-rw-r--r--java/com/android/incallui/answer/impl/classifier/PointerCountClassifier.java51
-rw-r--r--java/com/android/incallui/answer/impl/classifier/PointerCountEvaluator.java23
-rw-r--r--java/com/android/incallui/answer/impl/classifier/ProximityClassifier.java97
-rw-r--r--java/com/android/incallui/answer/impl/classifier/ProximityEvaluator.java28
-rw-r--r--java/com/android/incallui/answer/impl/classifier/SpeedAnglesClassifier.java147
-rw-r--r--java/com/android/incallui/answer/impl/classifier/SpeedAnglesPercentageEvaluator.java33
-rw-r--r--java/com/android/incallui/answer/impl/classifier/SpeedClassifier.java40
-rw-r--r--java/com/android/incallui/answer/impl/classifier/SpeedEvaluator.java36
-rw-r--r--java/com/android/incallui/answer/impl/classifier/SpeedRatioEvaluator.java39
-rw-r--r--java/com/android/incallui/answer/impl/classifier/SpeedVarianceEvaluator.java36
-rw-r--r--java/com/android/incallui/answer/impl/classifier/Stroke.java72
-rw-r--r--java/com/android/incallui/answer/impl/classifier/StrokeClassifier.java28
-rw-r--r--java/com/android/incallui/answer/impl/hint/AndroidManifest.xml13
-rw-r--r--java/com/android/incallui/answer/impl/hint/AnswerHint.java46
-rw-r--r--java/com/android/incallui/answer/impl/hint/AnswerHintFactory.java133
-rw-r--r--java/com/android/incallui/answer/impl/hint/DotAnswerHint.java283
-rw-r--r--java/com/android/incallui/answer/impl/hint/EmptyAnswerHint.java39
-rw-r--r--java/com/android/incallui/answer/impl/hint/EventAnswerHint.java235
-rw-r--r--java/com/android/incallui/answer/impl/hint/EventPayloadLoader.java30
-rw-r--r--java/com/android/incallui/answer/impl/hint/EventPayloadLoaderImpl.java118
-rw-r--r--java/com/android/incallui/answer/impl/hint/EventSecretCodeListener.java67
-rw-r--r--java/com/android/incallui/answer/impl/hint/res/drawable/answer_hint_large.xml4
-rw-r--r--java/com/android/incallui/answer/impl/hint/res/drawable/answer_hint_mid.xml4
-rw-r--r--java/com/android/incallui/answer/impl/hint/res/drawable/answer_hint_small.xml5
-rw-r--r--java/com/android/incallui/answer/impl/hint/res/layout/dot_hint.xml30
-rw-r--r--java/com/android/incallui/answer/impl/hint/res/layout/event_hint.xml36
-rw-r--r--java/com/android/incallui/answer/impl/hint/res/values/dimens.xml12
-rw-r--r--java/com/android/incallui/answer/impl/hint/res/values/strings.xml5
-rw-r--r--java/com/android/incallui/answer/impl/res/anim/incoming_unlocked_icon_entry.xml19
-rw-r--r--java/com/android/incallui/answer/impl/res/anim/incoming_unlocked_text_entry.xml9
-rw-r--r--java/com/android/incallui/answer/impl/res/layout/fragment_avatar.xml26
-rw-r--r--java/com/android/incallui/answer/impl/res/layout/fragment_custom_sms_dialog.xml14
-rw-r--r--java/com/android/incallui/answer/impl/res/layout/fragment_incoming_call.xml152
-rw-r--r--java/com/android/incallui/answer/impl/res/values-h240dp/dimens.xml21
-rw-r--r--java/com/android/incallui/answer/impl/res/values-h300dp/dimens.xml20
-rw-r--r--java/com/android/incallui/answer/impl/res/values-h480dp/dimens.xml22
-rw-r--r--java/com/android/incallui/answer/impl/res/values-h540dp/dimens.xml21
-rw-r--r--java/com/android/incallui/answer/impl/res/values/dimens.xml25
-rw-r--r--java/com/android/incallui/answer/impl/res/values/strings.xml26
-rw-r--r--java/com/android/incallui/answer/impl/utils/FlingAnimationUtils.java293
-rw-r--r--java/com/android/incallui/answer/impl/utils/Interpolators.java30
-rw-r--r--java/com/android/incallui/answer/protocol/AnswerScreen.java38
-rw-r--r--java/com/android/incallui/answer/protocol/AnswerScreenDelegate.java44
-rw-r--r--java/com/android/incallui/answer/protocol/AnswerScreenDelegateFactory.java23
-rw-r--r--java/com/android/incallui/answerproximitysensor/AnswerProximitySensor.java150
-rw-r--r--java/com/android/incallui/answerproximitysensor/AnswerProximityWakeLock.java37
-rw-r--r--java/com/android/incallui/answerproximitysensor/PseudoProximityWakeLock.java85
-rw-r--r--java/com/android/incallui/answerproximitysensor/PseudoScreenState.java66
-rw-r--r--java/com/android/incallui/answerproximitysensor/SystemProximityWakeLock.java90
-rw-r--r--java/com/android/incallui/async/PausableExecutor.java56
-rw-r--r--java/com/android/incallui/async/PausableExecutorImpl.java40
-rw-r--r--java/com/android/incallui/audioroute/AndroidManifest.xml3
-rw-r--r--java/com/android/incallui/audioroute/AudioRouteSelectorDialogFragment.java114
-rw-r--r--java/com/android/incallui/audioroute/res/drawable-hdpi/ic_phone_audio_grey600_24dp.pngbin0 -> 990 bytes
-rw-r--r--java/com/android/incallui/audioroute/res/drawable-mdpi/ic_phone_audio_grey600_24dp.pngbin0 -> 632 bytes
-rw-r--r--java/com/android/incallui/audioroute/res/drawable-xhdpi/ic_phone_audio_grey600_24dp.pngbin0 -> 1297 bytes
-rw-r--r--java/com/android/incallui/audioroute/res/drawable-xxhdpi/ic_phone_audio_grey600_24dp.pngbin0 -> 1979 bytes
-rw-r--r--java/com/android/incallui/audioroute/res/layout/audioroute_selector.xml37
-rw-r--r--java/com/android/incallui/audioroute/res/values/strings.xml7
-rw-r--r--java/com/android/incallui/audioroute/res/values/styles.xml14
-rw-r--r--java/com/android/incallui/autoresizetext/AndroidManifest.xml25
-rw-r--r--java/com/android/incallui/autoresizetext/AutoResizeTextView.java316
-rw-r--r--java/com/android/incallui/autoresizetext/res/values/attrs.xml47
-rw-r--r--java/com/android/incallui/baseui/BaseFragment.java75
-rw-r--r--java/com/android/incallui/baseui/Presenter.java54
-rw-r--r--java/com/android/incallui/baseui/Ui.java (renamed from InCallUI/src/com/android/incallui/Ui.java)10
-rw-r--r--java/com/android/incallui/bindings/ContactUtils.java33
-rw-r--r--java/com/android/incallui/bindings/DistanceHelper.java36
-rw-r--r--java/com/android/incallui/bindings/InCallUiBindings.java48
-rw-r--r--java/com/android/incallui/bindings/InCallUiBindingsFactory.java26
-rw-r--r--java/com/android/incallui/bindings/InCallUiBindingsStub.java81
-rw-r--r--java/com/android/incallui/bindings/PhoneNumberService.java77
-rw-r--r--java/com/android/incallui/call/CallList.java763
-rw-r--r--java/com/android/incallui/call/DialerCall.java1401
-rw-r--r--java/com/android/incallui/call/DialerCallDelegate.java25
-rw-r--r--java/com/android/incallui/call/DialerCallListener.java39
-rw-r--r--java/com/android/incallui/call/ExternalCallList.java136
-rw-r--r--java/com/android/incallui/call/InCallServiceListener.java40
-rw-r--r--java/com/android/incallui/call/InCallUiLegacyBindings.java26
-rw-r--r--java/com/android/incallui/call/InCallUiLegacyBindingsFactory.java26
-rw-r--r--java/com/android/incallui/call/InCallUiLegacyBindingsStub.java24
-rw-r--r--java/com/android/incallui/call/InCallVideoCallCallback.java197
-rw-r--r--java/com/android/incallui/call/InCallVideoCallCallbackNotifier.java279
-rw-r--r--java/com/android/incallui/call/TelecomAdapter.java160
-rw-r--r--java/com/android/incallui/call/VideoUtils.java151
-rw-r--r--java/com/android/incallui/commontheme/AndroidManifest.xml3
-rw-r--r--java/com/android/incallui/commontheme/res/animator/button_state.xml30
-rw-r--r--java/com/android/incallui/commontheme/res/animator/disabled_alpha.xml22
-rw-r--r--java/com/android/incallui/commontheme/res/color/incall_button_ripple.xml5
-rw-r--r--java/com/android/incallui/commontheme/res/color/incall_button_white.xml5
-rw-r--r--java/com/android/incallui/commontheme/res/drawable-hdpi/ic_phone_audio_white_36dp.pngbin0 -> 1010 bytes
-rw-r--r--java/com/android/incallui/commontheme/res/drawable-mdpi/ic_phone_audio_white_36dp.pngbin0 -> 682 bytes
-rw-r--r--java/com/android/incallui/commontheme/res/drawable-xhdpi/ic_phone_audio_white_36dp.pngbin0 -> 1362 bytes
-rw-r--r--java/com/android/incallui/commontheme/res/drawable-xxhdpi/ic_phone_audio_white_36dp.pngbin0 -> 2259 bytes
-rw-r--r--java/com/android/incallui/commontheme/res/drawable-xxxhdpi/ic_phone_audio_white_36dp.pngbin0 -> 3156 bytes
-rw-r--r--java/com/android/incallui/commontheme/res/drawable/answer_answer_background.xml10
-rw-r--r--java/com/android/incallui/commontheme/res/drawable/answer_decline_background.xml10
-rw-r--r--java/com/android/incallui/commontheme/res/drawable/incall_end_call_background.xml10
-rw-r--r--java/com/android/incallui/commontheme/res/values-w260dp-h520dp/dimens.xml21
-rw-r--r--java/com/android/incallui/commontheme/res/values-w520dp-h260dp-land/dimens.xml21
-rw-r--r--java/com/android/incallui/commontheme/res/values/colors.xml5
-rw-r--r--java/com/android/incallui/commontheme/res/values/dimens.xml22
-rw-r--r--java/com/android/incallui/commontheme/res/values/strings.xml35
-rw-r--r--java/com/android/incallui/commontheme/res/values/styles.xml58
-rw-r--r--java/com/android/incallui/contactgrid/AndroidManifest.xml3
-rw-r--r--java/com/android/incallui/contactgrid/BottomRow.java142
-rw-r--r--java/com/android/incallui/contactgrid/ContactGridManager.java315
-rw-r--r--java/com/android/incallui/contactgrid/TopRow.java168
-rw-r--r--java/com/android/incallui/contactgrid/res/layout/incall_contactgrid_bottom_row.xml71
-rw-r--r--java/com/android/incallui/contactgrid/res/layout/incall_contactgrid_top_row.xml26
-rw-r--r--java/com/android/incallui/contactgrid/res/values/ids.xml31
-rw-r--r--java/com/android/incallui/contactgrid/res/values/strings.xml69
-rw-r--r--java/com/android/incallui/hold/AndroidManifest.xml3
-rw-r--r--java/com/android/incallui/hold/OnHoldFragment.java102
-rw-r--r--java/com/android/incallui/hold/res/layout/incall_on_hold_banner.xml46
-rw-r--r--java/com/android/incallui/hold/res/values/strings.xml6
-rw-r--r--java/com/android/incallui/incall/bindings/InCallBindings.java28
-rw-r--r--java/com/android/incallui/incall/impl/AndroidManifest.xml3
-rw-r--r--java/com/android/incallui/incall/impl/AutoValue_MappedButtonConfig_MappingInfo.java135
-rw-r--r--java/com/android/incallui/incall/impl/ButtonChooser.java114
-rw-r--r--java/com/android/incallui/incall/impl/ButtonChooserFactory.java100
-rw-r--r--java/com/android/incallui/incall/impl/ButtonController.java584
-rw-r--r--java/com/android/incallui/incall/impl/CheckableLabeledButton.java286
-rw-r--r--java/com/android/incallui/incall/impl/InCallButtonGridFragment.java137
-rw-r--r--java/com/android/incallui/incall/impl/InCallFragment.java501
-rw-r--r--java/com/android/incallui/incall/impl/InCallPagerAdapter.java59
-rw-r--r--java/com/android/incallui/incall/impl/MappedButtonConfig.java193
-rw-r--r--java/com/android/incallui/incall/impl/res/animator/incall_button_elevation.xml31
-rw-r--r--java/com/android/incallui/incall/impl/res/color/incall_button_icon.xml5
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-mdpi/ic_addcall_white.pngbin0 -> 708 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xhdpi/ic_addcall_white.pngbin0 -> 1259 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable/incall_button_background.xml22
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable/incall_button_background_checked.xml5
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable/incall_button_background_more.xml30
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable/incall_button_background_unchecked.xml5
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable/incall_ic_add_call.xml4
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable/incall_ic_dialpad.xml4
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable/incall_ic_manage.xml4
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable/incall_ic_merge.xml4
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable/incall_ic_pause.xml4
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable/tab_indicator_default.xml12
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable/tab_indicator_selected.xml12
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable/tab_selector.xml6
-rw-r--r--java/com/android/incallui/incall/impl/res/layout/call_composer_data_fragment.xml15
-rw-r--r--java/com/android/incallui/incall/impl/res/layout/frag_incall_voice.xml104
-rw-r--r--java/com/android/incallui/incall/impl/res/layout/incall_button_grid.xml77
-rw-r--r--java/com/android/incallui/incall/impl/res/values-h320dp/dimens.xml5
-rw-r--r--java/com/android/incallui/incall/impl/res/values-h385dp/dimens.xml5
-rw-r--r--java/com/android/incallui/incall/impl/res/values-h480dp/dimens.xml4
-rw-r--r--java/com/android/incallui/incall/impl/res/values-h580dp/dimens.xml4
-rw-r--r--java/com/android/incallui/incall/impl/res/values-h580dp/styles.xml24
-rw-r--r--java/com/android/incallui/incall/impl/res/values-w260dp-h520dp/dimens.xml7
-rw-r--r--java/com/android/incallui/incall/impl/res/values-w300dp-h540dp/dimens.xml5
-rw-r--r--java/com/android/incallui/incall/impl/res/values/attrs.xml8
-rw-r--r--java/com/android/incallui/incall/impl/res/values/dimens.xml17
-rw-r--r--java/com/android/incallui/incall/impl/res/values/ids.xml6
-rw-r--r--java/com/android/incallui/incall/impl/res/values/strings.xml56
-rw-r--r--java/com/android/incallui/incall/impl/res/values/styles.xml23
-rw-r--r--java/com/android/incallui/incall/protocol/ContactPhotoType.java35
-rw-r--r--java/com/android/incallui/incall/protocol/InCallButtonIds.java59
-rw-r--r--java/com/android/incallui/incall/protocol/InCallButtonIdsExtension.java61
-rw-r--r--java/com/android/incallui/incall/protocol/InCallButtonUi.java50
-rw-r--r--java/com/android/incallui/incall/protocol/InCallButtonUiDelegate.java67
-rw-r--r--java/com/android/incallui/incall/protocol/InCallButtonUiDelegateFactory.java23
-rw-r--r--java/com/android/incallui/incall/protocol/InCallScreen.java53
-rw-r--r--java/com/android/incallui/incall/protocol/InCallScreenDelegate.java43
-rw-r--r--java/com/android/incallui/incall/protocol/InCallScreenDelegateFactory.java23
-rw-r--r--java/com/android/incallui/incall/protocol/PrimaryCallState.java114
-rw-r--r--java/com/android/incallui/incall/protocol/PrimaryInfo.java112
-rw-r--r--java/com/android/incallui/incall/protocol/SecondaryInfo.java109
-rw-r--r--java/com/android/incallui/latencyreport/LatencyReport.java140
-rw-r--r--java/com/android/incallui/legacyblocking/BlockedNumberContentObserver.java105
-rw-r--r--java/com/android/incallui/legacyblocking/DeleteBlockedCallTask.java124
-rw-r--r--java/com/android/incallui/maps/StaticMapBinding.java51
-rw-r--r--java/com/android/incallui/maps/StaticMapFactory.java28
-rw-r--r--java/com/android/incallui/res/anim/activity_open_enter.xml43
-rw-r--r--java/com/android/incallui/res/anim/activity_open_exit.xml31
-rw-r--r--java/com/android/incallui/res/anim/decelerate_cubic.xml (renamed from InCallUI/res/anim/decelerate_cubic.xml)2
-rw-r--r--java/com/android/incallui/res/anim/decelerate_quint.xml (renamed from InCallUI/res/anim/decelerate_quint.xml)2
-rw-r--r--java/com/android/incallui/res/anim/on_going_call.xml31
-rw-r--r--java/com/android/incallui/res/color/ota_title_color.xml (renamed from InCallUI/res/color/ota_title_color.xml)2
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_block_grey600_24dp.png (renamed from InCallUI/res/drawable-hdpi/ic_block_grey600_24dp.png)bin518 -> 518 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_call_end_white_24dp.png (renamed from InCallUI/res/drawable-hdpi/ic_call_end_white_24dp.png)bin454 -> 454 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_call_split_white_24dp.png (renamed from InCallUI/res/drawable-hdpi/ic_call_split_white_24dp.png)bin326 -> 326 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_close_grey600_24dp.png (renamed from InCallUI/res/drawable-hdpi/ic_close_grey600_24dp.png)bin225 -> 225 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_location_on_white_24dp.png (renamed from InCallUI/res/drawable-hdpi/ic_location_on_white_24dp.png)bin371 -> 371 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_01.pngbin0 -> 577 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_02.pngbin0 -> 650 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_03.pngbin0 -> 803 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_04.pngbin0 -> 1009 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_05.pngbin0 -> 946 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_06.pngbin0 -> 856 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_07.pngbin0 -> 577 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_08.pngbin0 -> 577 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_09.pngbin0 -> 577 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_person_add_grey600_24dp.png (renamed from InCallUI/res/drawable-hdpi/ic_person_add_grey600_24dp.png)bin300 -> 300 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_phone_paused_white_24dp.png (renamed from InCallUI/res/drawable-hdpi/ic_phone_paused_white_24dp.png)bin458 -> 458 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_question_mark.pngbin0 -> 845 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/ic_schedule_white_24dp.png (renamed from InCallUI/res/drawable-hdpi/ic_schedule_white_24dp.png)bin575 -> 575 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/img_business.png (renamed from InCallUI/res/drawable-hdpi/img_business.png)bin3311 -> 3311 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/img_conference.png (renamed from InCallUI/res/drawable-hdpi/img_conference.png)bin7037 -> 7037 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/img_no_image.png (renamed from InCallUI/res/drawable-hdpi/img_no_image.png)bin5362 -> 5362 bytes
-rw-r--r--java/com/android/incallui/res/drawable-hdpi/img_phone.png (renamed from InCallUI/res/drawable-hdpi/img_phone.png)bin6157 -> 6157 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_block_grey600_24dp.png (renamed from InCallUI/res/drawable-mdpi/ic_block_grey600_24dp.png)bin348 -> 348 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_call_end_white_24dp.png (renamed from InCallUI/res/drawable-mdpi/ic_call_end_white_24dp.png)bin315 -> 315 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_call_split_white_24dp.png (renamed from InCallUI/res/drawable-mdpi/ic_call_split_white_24dp.png)bin256 -> 256 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_close_grey600_24dp.png (renamed from InCallUI/res/drawable-mdpi/ic_close_grey600_24dp.png)bin178 -> 178 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_location_on_white_24dp.png (renamed from InCallUI/res/drawable-mdpi/ic_location_on_white_24dp.png)bin265 -> 265 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_01.pngbin0 -> 375 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_02.pngbin0 -> 401 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_03.pngbin0 -> 501 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_04.pngbin0 -> 638 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_05.pngbin0 -> 572 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_06.pngbin0 -> 548 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_07.pngbin0 -> 375 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_08.pngbin0 -> 375 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_09.pngbin0 -> 375 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_person_add_grey600_24dp.png (renamed from InCallUI/res/drawable-mdpi/ic_person_add_grey600_24dp.png)bin211 -> 211 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_phone_paused_white_24dp.png (renamed from InCallUI/res/drawable-mdpi/ic_phone_paused_white_24dp.png)bin346 -> 346 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_question_mark.pngbin0 -> 569 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/ic_schedule_white_24dp.png (renamed from InCallUI/res/drawable-mdpi/ic_schedule_white_24dp.png)bin377 -> 377 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/img_business.png (renamed from InCallUI/res/drawable-mdpi/img_business.png)bin2240 -> 2240 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/img_conference.png (renamed from InCallUI/res/drawable-mdpi/img_conference.png)bin4629 -> 4629 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/img_no_image.png (renamed from InCallUI/res/drawable-mdpi/img_no_image.png)bin3509 -> 3509 bytes
-rw-r--r--java/com/android/incallui/res/drawable-mdpi/img_phone.png (renamed from InCallUI/res/drawable-mdpi/img_phone.png)bin3798 -> 3798 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_block_grey600_24dp.png (renamed from InCallUI/res/drawable-xhdpi/ic_block_grey600_24dp.png)bin690 -> 690 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_call_end_white_24dp.png (renamed from InCallUI/res/drawable-xhdpi/ic_call_end_white_24dp.png)bin534 -> 534 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_call_split_white_24dp.png (renamed from InCallUI/res/drawable-xhdpi/ic_call_split_white_24dp.png)bin377 -> 377 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_close_grey600_24dp.png (renamed from InCallUI/res/drawable-xhdpi/ic_close_grey600_24dp.png)bin261 -> 261 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_location_on_white_24dp.png (renamed from InCallUI/res/drawable-xhdpi/ic_location_on_white_24dp.png)bin456 -> 456 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_01.pngbin0 -> 730 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_02.pngbin0 -> 806 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_03.pngbin0 -> 1017 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_04.pngbin0 -> 1313 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_05.pngbin0 -> 1218 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_06.pngbin0 -> 1098 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_07.pngbin0 -> 730 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_08.pngbin0 -> 730 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_09.pngbin0 -> 730 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_person_add_grey600_24dp.png (renamed from InCallUI/res/drawable-xhdpi/ic_person_add_grey600_24dp.png)bin341 -> 341 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_phone_paused_white_24dp.png (renamed from InCallUI/res/drawable-xhdpi/ic_phone_paused_white_24dp.png)bin584 -> 584 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_question_mark.pngbin0 -> 1094 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/ic_schedule_white_24dp.png (renamed from InCallUI/res/drawable-xhdpi/ic_schedule_white_24dp.png)bin737 -> 737 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/img_business.png (renamed from InCallUI/res/drawable-xhdpi/img_business.png)bin4759 -> 4759 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/img_conference.png (renamed from InCallUI/res/drawable-xhdpi/img_conference.png)bin9517 -> 9517 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/img_no_image.png (renamed from InCallUI/res/drawable-xhdpi/img_no_image.png)bin7369 -> 7369 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xhdpi/img_phone.png (renamed from InCallUI/res/drawable-xhdpi/img_phone.png)bin8189 -> 8189 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_block_grey600_24dp.png (renamed from InCallUI/res/drawable-xxhdpi/ic_block_grey600_24dp.png)bin1029 -> 1029 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_call_end_white_24dp.png (renamed from InCallUI/res/drawable-xxhdpi/ic_call_end_white_24dp.png)bin736 -> 736 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_call_split_white_24dp.png (renamed from InCallUI/res/drawable-xxhdpi/ic_call_split_white_24dp.png)bin461 -> 461 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_close_grey600_24dp.png (renamed from InCallUI/res/drawable-xxhdpi/ic_close_grey600_24dp.png)bin353 -> 353 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_location_on_white_24dp.png (renamed from InCallUI/res/drawable-xxhdpi/ic_location_on_white_24dp.png)bin675 -> 675 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_01.pngbin0 -> 1051 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_02.pngbin0 -> 1198 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_03.pngbin0 -> 1524 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_04.pngbin0 -> 2045 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_05.pngbin0 -> 1900 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_06.pngbin0 -> 1675 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_07.pngbin0 -> 1051 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_08.pngbin0 -> 1051 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_09.pngbin0 -> 1051 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_person_add_grey600_24dp.png (renamed from InCallUI/res/drawable-xxhdpi/ic_person_add_grey600_24dp.png)bin485 -> 485 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_phone_paused_white_24dp.png (renamed from InCallUI/res/drawable-xxhdpi/ic_phone_paused_white_24dp.png)bin842 -> 842 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_question_mark.pngbin0 -> 1686 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/ic_schedule_white_24dp.png (renamed from InCallUI/res/drawable-xxhdpi/ic_schedule_white_24dp.png)bin1107 -> 1107 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/img_business.png (renamed from InCallUI/res/drawable-xxhdpi/img_business.png)bin6499 -> 6499 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/img_conference.png (renamed from InCallUI/res/drawable-xxhdpi/img_conference.png)bin16306 -> 16306 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/img_no_image.png (renamed from InCallUI/res/drawable-xxhdpi/img_no_image.png)bin9850 -> 9850 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxhdpi/img_phone.png (renamed from InCallUI/res/drawable-xxhdpi/img_phone.png)bin10848 -> 10848 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxxhdpi/ic_block_grey600_24dp.png (renamed from InCallUI/res/drawable-xxxhdpi/ic_block_grey600_24dp.png)bin1353 -> 1353 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxxhdpi/ic_call_end_white_24dp.png (renamed from InCallUI/res/drawable-xxxhdpi/ic_call_end_white_24dp.png)bin929 -> 929 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxxhdpi/ic_call_split_white_24dp.png (renamed from InCallUI/res/drawable-xxxhdpi/ic_call_split_white_24dp.png)bin646 -> 646 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxxhdpi/ic_close_grey600_24dp.png (renamed from InCallUI/res/drawable-xxxhdpi/ic_close_grey600_24dp.png)bin444 -> 444 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxxhdpi/ic_location_on_white_24dp.png (renamed from InCallUI/res/drawable-xxxhdpi/ic_location_on_white_24dp.png)bin869 -> 869 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxxhdpi/ic_person_add_grey600_24dp.png (renamed from InCallUI/res/drawable-xxxhdpi/ic_person_add_grey600_24dp.png)bin638 -> 638 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxxhdpi/ic_question_mark.pngbin0 -> 2304 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxxhdpi/ic_schedule_white_24dp.png (renamed from InCallUI/res/drawable-xxxhdpi/ic_schedule_white_24dp.png)bin1478 -> 1478 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxxhdpi/img_business.png (renamed from InCallUI/res/drawable-xxxhdpi/img_business.png)bin10730 -> 10730 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxxhdpi/img_conference.png (renamed from InCallUI/res/drawable-xxxhdpi/img_conference.png)bin19584 -> 19584 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxxhdpi/img_no_image.png (renamed from InCallUI/res/drawable-xxxhdpi/img_no_image.png)bin16251 -> 16251 bytes
-rw-r--r--java/com/android/incallui/res/drawable-xxxhdpi/img_phone.png (renamed from InCallUI/res/drawable-xxxhdpi/img_phone.png)bin18635 -> 18635 bytes
-rw-r--r--java/com/android/incallui/res/drawable/img_conference_automirrored.xml (renamed from InCallUI/res/drawable/img_conference_automirrored.xml)4
-rw-r--r--java/com/android/incallui/res/drawable/img_no_image_automirrored.xml (renamed from InCallUI/res/drawable/img_no_image_automirrored.xml)4
-rw-r--r--java/com/android/incallui/res/drawable/incall_background_gradient.xml8
-rw-r--r--java/com/android/incallui/res/drawable/spam_notification_icon.xml34
-rw-r--r--java/com/android/incallui/res/drawable/unknown_notification_icon.xml34
-rw-r--r--java/com/android/incallui/res/layout/activity_manage_conference.xml6
-rw-r--r--java/com/android/incallui/res/layout/caller_in_conference.xml119
-rw-r--r--java/com/android/incallui/res/layout/conference_manager_fragment.xml33
-rw-r--r--java/com/android/incallui/res/layout/incall_dialpad_fragment.xml24
-rw-r--r--java/com/android/incallui/res/layout/incall_screen.xml33
-rw-r--r--java/com/android/incallui/res/layout/video_call_lte_to_wifi_failed.xml28
-rw-r--r--java/com/android/incallui/res/values-sw360dp/dimens.xml32
-rw-r--r--java/com/android/incallui/res/values-w500dp-land/colors.xml21
-rw-r--r--java/com/android/incallui/res/values-w500dp-land/dimens.xml23
-rw-r--r--java/com/android/incallui/res/values/animation_constants.xml19
-rw-r--r--java/com/android/incallui/res/values/colors.xml92
-rw-r--r--java/com/android/incallui/res/values/config.xml23
-rw-r--r--java/com/android/incallui/res/values/dimens.xml66
-rw-r--r--java/com/android/incallui/res/values/strings.xml367
-rw-r--r--java/com/android/incallui/res/values/styles.xml80
-rw-r--r--java/com/android/incallui/ringtone/DialerRingtoneManager.java134
-rw-r--r--java/com/android/incallui/ringtone/InCallTonePlayer.java168
-rw-r--r--java/com/android/incallui/ringtone/ToneGeneratorFactory.java34
-rw-r--r--java/com/android/incallui/sessiondata/AndroidManifest.xml18
-rw-r--r--java/com/android/incallui/sessiondata/AvatarPresenter.java31
-rw-r--r--java/com/android/incallui/sessiondata/MultimediaFragment.java231
-rw-r--r--java/com/android/incallui/sessiondata/res/drawable/answer_data_background.xml22
-rw-r--r--java/com/android/incallui/sessiondata/res/layout/fragment_composer_frag.xml42
-rw-r--r--java/com/android/incallui/sessiondata/res/layout/fragment_composer_image.xml50
-rw-r--r--java/com/android/incallui/sessiondata/res/layout/fragment_composer_image_frag.xml59
-rw-r--r--java/com/android/incallui/sessiondata/res/layout/fragment_composer_text.xml43
-rw-r--r--java/com/android/incallui/sessiondata/res/layout/fragment_composer_text_frag.xml61
-rw-r--r--java/com/android/incallui/sessiondata/res/layout/fragment_composer_text_image.xml62
-rw-r--r--java/com/android/incallui/sessiondata/res/layout/fragment_composer_text_image_frag.xml78
-rw-r--r--java/com/android/incallui/sessiondata/res/values/dimens.xml21
-rw-r--r--java/com/android/incallui/sessiondata/res/values/ids.xml23
-rw-r--r--java/com/android/incallui/sessiondata/res/values/styles.xml24
-rw-r--r--java/com/android/incallui/spam/NumberInCallHistoryTask.java107
-rw-r--r--java/com/android/incallui/spam/SpamCallListListener.java364
-rw-r--r--java/com/android/incallui/spam/SpamNotificationActivity.java483
-rw-r--r--java/com/android/incallui/spam/SpamNotificationService.java132
-rw-r--r--java/com/android/incallui/util/AccessibilityUtil.java35
-rw-r--r--java/com/android/incallui/util/TelecomCallUtil.java51
-rw-r--r--java/com/android/incallui/video/bindings/VideoBindings.java28
-rw-r--r--java/com/android/incallui/video/impl/AndroidManifest.xml3
-rw-r--r--java/com/android/incallui/video/impl/CameraPermissionDialogFragment.java62
-rw-r--r--java/com/android/incallui/video/impl/CheckableImageButton.java222
-rw-r--r--java/com/android/incallui/video/impl/SpeakerButtonController.java118
-rw-r--r--java/com/android/incallui/video/impl/SwitchOnHoldCallController.java91
-rw-r--r--java/com/android/incallui/video/impl/VideoCallFragment.java1215
-rw-r--r--java/com/android/incallui/video/impl/res/color/videocall_button_icon_tint.xml5
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-hdpi/ic_switch_camera.pngbin0 -> 1930 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_checked.pngbin0 -> 3103 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_checked_disabled.pngbin0 -> 3304 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_checked_pressed.pngbin0 -> 4836 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_default.pngbin0 -> 4209 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_disabled.pngbin0 -> 4022 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_pressed.pngbin0 -> 5695 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-mdpi/ic_switch_camera.pngbin0 -> 1293 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_checked.pngbin0 -> 1426 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_checked_disabled.pngbin0 -> 1715 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_checked_pressed.pngbin0 -> 2724 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_default.pngbin0 -> 2155 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_disabled.pngbin0 -> 1990 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_pressed.pngbin0 -> 3188 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-xhdpi/ic_switch_camera.pngbin0 -> 2518 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_checked.pngbin0 -> 4603 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_checked_disabled.pngbin0 -> 4957 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_checked_pressed.pngbin0 -> 7213 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_default.pngbin0 -> 6352 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_disabled.pngbin0 -> 6054 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_pressed.pngbin0 -> 8418 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-xxhdpi/ic_switch_camera.pngbin0 -> 4001 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_checked.pngbin0 -> 9032 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_checked_disabled.pngbin0 -> 8611 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_checked_pressed.pngbin0 -> 13529 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_default.pngbin0 -> 11101 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_disabled.pngbin0 -> 10736 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_pressed.pngbin0 -> 15167 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable-xxxhdpi/ic_switch_camera.pngbin0 -> 2424 bytes
-rw-r--r--java/com/android/incallui/video/impl/res/drawable/videocall_background_circle_white.xml10
-rw-r--r--java/com/android/incallui/video/impl/res/drawable/videocall_video_button_background.xml27
-rw-r--r--java/com/android/incallui/video/impl/res/layout-v21/switch_camera_button.xml6
-rw-r--r--java/com/android/incallui/video/impl/res/layout/frag_videocall.xml114
-rw-r--r--java/com/android/incallui/video/impl/res/layout/frag_videocall_land.xml111
-rw-r--r--java/com/android/incallui/video/impl/res/layout/switch_camera_button.xml6
-rw-r--r--java/com/android/incallui/video/impl/res/layout/video_contact_grid.xml33
-rw-r--r--java/com/android/incallui/video/impl/res/layout/videocall_controls.xml113
-rw-r--r--java/com/android/incallui/video/impl/res/layout/videocall_controls_land.xml115
-rw-r--r--java/com/android/incallui/video/impl/res/values-h580dp/dimens.xml7
-rw-r--r--java/com/android/incallui/video/impl/res/values-w460dp/dimens.xml7
-rw-r--r--java/com/android/incallui/video/impl/res/values/attrs.xml8
-rw-r--r--java/com/android/incallui/video/impl/res/values/dimens.xml10
-rw-r--r--java/com/android/incallui/video/impl/res/values/strings.xml28
-rw-r--r--java/com/android/incallui/video/impl/res/values/styles.xml11
-rw-r--r--java/com/android/incallui/video/protocol/VideoCallScreen.java36
-rw-r--r--java/com/android/incallui/video/protocol/VideoCallScreenDelegate.java48
-rw-r--r--java/com/android/incallui/video/protocol/VideoCallScreenDelegateFactory.java23
-rw-r--r--java/com/android/incallui/videosurface/bindings/VideoSurfaceBindings.java44
-rw-r--r--java/com/android/incallui/videosurface/impl/VideoScale.java147
-rw-r--r--java/com/android/incallui/videosurface/impl/VideoSurfaceTextureImpl.java249
-rw-r--r--java/com/android/incallui/videosurface/protocol/VideoSurfaceDelegate.java29
-rw-r--r--java/com/android/incallui/videosurface/protocol/VideoSurfaceTexture.java57
-rw-r--r--java/com/android/incallui/wifi/AndroidManifest.xml3
-rw-r--r--java/com/android/incallui/wifi/EnableWifiCallingPrompt.java82
-rw-r--r--java/com/android/incallui/wifi/res/values/strings.xml9
-rw-r--r--java/com/android/voicemailomtp/ActivationTask.java305
-rw-r--r--java/com/android/voicemailomtp/AndroidManifest.xml105
-rw-r--r--java/com/android/voicemailomtp/Assert.java62
-rw-r--r--java/com/android/voicemailomtp/DefaultOmtpEventHandler.java202
-rw-r--r--java/com/android/voicemailomtp/NeededForTesting.java25
-rw-r--r--java/com/android/voicemailomtp/OmtpConstants.java248
-rw-r--r--java/com/android/voicemailomtp/OmtpEvents.java156
-rw-r--r--java/com/android/voicemailomtp/OmtpService.java65
-rw-r--r--java/com/android/voicemailomtp/OmtpVvmCarrierConfigHelper.java423
-rw-r--r--java/com/android/voicemailomtp/SubscriptionInfoHelper.java75
-rw-r--r--java/com/android/voicemailomtp/TelephonyManagerStub.java80
-rw-r--r--java/com/android/voicemailomtp/TelephonyVvmConfigManager.java154
-rw-r--r--java/com/android/voicemailomtp/VisualVoicemailPreferences.java143
-rw-r--r--java/com/android/voicemailomtp/Voicemail.java330
-rw-r--r--java/com/android/voicemailomtp/VoicemailStatus.java158
-rw-r--r--java/com/android/voicemailomtp/VvmLog.java179
-rw-r--r--java/com/android/voicemailomtp/VvmPackageInstallReceiver.java70
-rw-r--r--java/com/android/voicemailomtp/VvmPhoneStateListener.java103
-rw-r--r--java/com/android/voicemailomtp/fetch/FetchVoicemailReceiver.java219
-rw-r--r--java/com/android/voicemailomtp/fetch/VoicemailFetchedCallback.java101
-rw-r--r--java/com/android/voicemailomtp/imap/ImapHelper.java711
-rw-r--r--java/com/android/voicemailomtp/imap/VoicemailPayload.java38
-rw-r--r--java/com/android/voicemailomtp/mail/Address.java541
-rw-r--r--java/com/android/voicemailomtp/mail/AuthenticationFailedException.java33
-rw-r--r--java/com/android/voicemailomtp/mail/Base64Body.java62
-rw-r--r--java/com/android/voicemailomtp/mail/Body.java25
-rw-r--r--java/com/android/voicemailomtp/mail/BodyPart.java24
-rw-r--r--java/com/android/voicemailomtp/mail/CertificateValidationException.java29
-rw-r--r--java/com/android/voicemailomtp/mail/FetchProfile.java84
-rw-r--r--java/com/android/voicemailomtp/mail/Fetchable.java23
-rw-r--r--java/com/android/voicemailomtp/mail/FixedLengthInputStream.java79
-rw-r--r--java/com/android/voicemailomtp/mail/Flag.java29
-rw-r--r--java/com/android/voicemailomtp/mail/MailTransport.java344
-rw-r--r--java/com/android/voicemailomtp/mail/MeetingInfo.java29
-rw-r--r--java/com/android/voicemailomtp/mail/Message.java144
-rw-r--r--java/com/android/voicemailomtp/mail/MessageDateComparator.java34
-rw-r--r--java/com/android/voicemailomtp/mail/MessagingException.java139
-rw-r--r--java/com/android/voicemailomtp/mail/Multipart.java62
-rw-r--r--java/com/android/voicemailomtp/mail/PackedString.java175
-rw-r--r--java/com/android/voicemailomtp/mail/Part.java51
-rw-r--r--java/com/android/voicemailomtp/mail/PeekableInputStream.java80
-rw-r--r--java/com/android/voicemailomtp/mail/TempDirectory.java41
-rw-r--r--java/com/android/voicemailomtp/mail/internet/BinaryTempFileBody.java91
-rw-r--r--java/com/android/voicemailomtp/mail/internet/MimeBodyPart.java207
-rw-r--r--java/com/android/voicemailomtp/mail/internet/MimeHeader.java161
-rw-r--r--java/com/android/voicemailomtp/mail/internet/MimeMessage.java675
-rw-r--r--java/com/android/voicemailomtp/mail/internet/MimeMultipart.java112
-rw-r--r--java/com/android/voicemailomtp/mail/internet/MimeUtility.java416
-rw-r--r--java/com/android/voicemailomtp/mail/internet/TextBody.java63
-rw-r--r--java/com/android/voicemailomtp/mail/store/ImapConnection.java413
-rw-r--r--java/com/android/voicemailomtp/mail/store/ImapFolder.java784
-rw-r--r--java/com/android/voicemailomtp/mail/store/ImapStore.java176
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/DigestMd5Utils.java335
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapConstants.java144
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapElement.java120
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapList.java235
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapMemoryLiteral.java76
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapResponse.java158
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapResponseParser.java432
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapSimpleString.java62
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapString.java192
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapTempFileLiteral.java123
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapUtility.java125
-rw-r--r--java/com/android/voicemailomtp/mail/utility/CountingOutputStream.java48
-rw-r--r--java/com/android/voicemailomtp/mail/utility/EOLConvertingOutputStream.java48
-rw-r--r--java/com/android/voicemailomtp/mail/utils/LogUtils.java413
-rw-r--r--java/com/android/voicemailomtp/mail/utils/Utility.java80
-rw-r--r--java/com/android/voicemailomtp/permissions.xml21
-rw-r--r--java/com/android/voicemailomtp/protocol/CvvmProtocol.java59
-rw-r--r--java/com/android/voicemailomtp/protocol/OmtpProtocol.java37
-rw-r--r--java/com/android/voicemailomtp/protocol/ProtocolHelper.java43
-rw-r--r--java/com/android/voicemailomtp/protocol/VisualVoicemailProtocol.java100
-rw-r--r--java/com/android/voicemailomtp/protocol/VisualVoicemailProtocolFactory.java47
-rw-r--r--java/com/android/voicemailomtp/protocol/Vvm3EventHandler.java271
-rw-r--r--java/com/android/voicemailomtp/protocol/Vvm3Protocol.java301
-rw-r--r--java/com/android/voicemailomtp/protocol/Vvm3Subscriber.java326
-rw-r--r--java/com/android/voicemailomtp/res/layout/voicemail_change_pin.xml97
-rw-r--r--java/com/android/voicemailomtp/res/values/arrays.xml19
-rw-r--r--java/com/android/voicemailomtp/res/values/attrs.xml20
-rw-r--r--java/com/android/voicemailomtp/res/values/colors.xml19
-rw-r--r--java/com/android/voicemailomtp/res/values/config.xml19
-rw-r--r--java/com/android/voicemailomtp/res/values/dimens.xml19
-rw-r--r--java/com/android/voicemailomtp/res/values/ids.xml20
-rw-r--r--java/com/android/voicemailomtp/res/values/strings.xml86
-rw-r--r--java/com/android/voicemailomtp/res/values/styles.xml19
-rw-r--r--java/com/android/voicemailomtp/res/xml/voicemail_settings.xml27
-rw-r--r--java/com/android/voicemailomtp/res/xml/vvm_config.xml134
-rw-r--r--java/com/android/voicemailomtp/scheduling/BaseTask.java206
-rw-r--r--java/com/android/voicemailomtp/scheduling/BlockerTask.java55
-rw-r--r--java/com/android/voicemailomtp/scheduling/MinimalIntervalPolicy.java69
-rw-r--r--java/com/android/voicemailomtp/scheduling/Policy.java36
-rw-r--r--java/com/android/voicemailomtp/scheduling/PostponePolicy.java69
-rw-r--r--java/com/android/voicemailomtp/scheduling/RetryPolicy.java117
-rw-r--r--java/com/android/voicemailomtp/scheduling/Task.java133
-rw-r--r--java/com/android/voicemailomtp/scheduling/TaskSchedulerService.java392
-rw-r--r--java/com/android/voicemailomtp/settings/VisualVoicemailSettingsUtil.java77
-rw-r--r--java/com/android/voicemailomtp/settings/VoicemailChangePinActivity.java634
-rw-r--r--java/com/android/voicemailomtp/settings/VoicemailSettingsActivity.java222
-rw-r--r--java/com/android/voicemailomtp/sms/LegacyModeSmsHandler.java67
-rw-r--r--java/com/android/voicemailomtp/sms/OmtpCvvmMessageSender.java55
-rw-r--r--java/com/android/voicemailomtp/sms/OmtpMessageReceiver.java162
-rw-r--r--java/com/android/voicemailomtp/sms/OmtpMessageSender.java89
-rw-r--r--java/com/android/voicemailomtp/sms/OmtpStandardMessageSender.java119
-rw-r--r--java/com/android/voicemailomtp/sms/StatusMessage.java209
-rw-r--r--java/com/android/voicemailomtp/sms/StatusSmsFetcher.java162
-rw-r--r--java/com/android/voicemailomtp/sms/SyncMessage.java166
-rw-r--r--java/com/android/voicemailomtp/sms/Vvm3MessageSender.java56
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/commons/io/IOUtils.java1202
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/BodyDescriptor.java392
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/CloseShieldInputStream.java129
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/ContentHandler.java177
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/EOLConvertingInputStream.java139
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/Log.java114
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/LogFactory.java29
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/MimeBoundaryInputStream.java184
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/MimeStreamParser.java324
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/RootInputStream.java111
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/codec/EncoderUtil.java630
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/Base64InputStream.java151
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/ByteQueue.java62
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/DecoderUtil.java284
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/QuotedPrintableInputStream.java229
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/UnboundedFifoByteBuffer.java272
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/AddressListField.java65
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/ContentTransferEncodingField.java88
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/ContentTypeField.java259
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/DateTimeField.java73
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/DefaultFieldParser.java45
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/DelegatingFieldParser.java47
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/Field.java192
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/FieldParser.java21
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/MailboxField.java70
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/MailboxListField.java67
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/UnstructuredField.java49
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Address.java52
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/AddressList.java138
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Builder.java243
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/DomainList.java76
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Group.java75
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Mailbox.java121
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/MailboxList.java71
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/NamedMailbox.java71
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTaddr_spec.java19
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTaddress.java19
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTaddress_list.java19
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTangle_addr.java19
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTdomain.java19
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTgroup_body.java19
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTlocal_part.java19
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTmailbox.java19
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTname_addr.java19
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTphrase.java19
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTroute.java19
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParser.java977
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParser.jj595
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserConstants.java76
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserTokenManager.java1009
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserTreeConstants.java35
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserVisitor.java19
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/BaseNode.java30
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/JJTAddressListParserState.java123
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/Node.java37
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ParseException.java207
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/SimpleCharStream.java454
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/SimpleNode.java87
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/Token.java96
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/TokenMgrError.java148
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParser.java268
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserConstants.java62
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserTokenManager.java877
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ParseException.java207
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/SimpleCharStream.java454
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/Token.java96
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/TokenMgrError.java148
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/DateTime.java127
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParser.java570
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserConstants.java86
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserTokenManager.java882
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/ParseException.java207
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/SimpleCharStream.java454
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/Token.java96
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/TokenMgrError.java148
-rw-r--r--java/com/android/voicemailomtp/src/org/apache/james/mime4j/util/CharsetUtil.java1249
-rw-r--r--java/com/android/voicemailomtp/sync/OmtpVvmSourceManager.java120
-rw-r--r--java/com/android/voicemailomtp/sync/OmtpVvmSyncReceiver.java61
-rw-r--r--java/com/android/voicemailomtp/sync/OmtpVvmSyncService.java278
-rw-r--r--java/com/android/voicemailomtp/sync/SyncOneTask.java82
-rw-r--r--java/com/android/voicemailomtp/sync/SyncTask.java79
-rw-r--r--java/com/android/voicemailomtp/sync/UploadTask.java68
-rw-r--r--java/com/android/voicemailomtp/sync/VoicemailProviderChangeReceiver.java41
-rw-r--r--java/com/android/voicemailomtp/sync/VoicemailStatusQueryHelper.java113
-rw-r--r--java/com/android/voicemailomtp/sync/VoicemailsQueryHelper.java244
-rw-r--r--java/com/android/voicemailomtp/sync/VvmNetworkRequest.java118
-rw-r--r--java/com/android/voicemailomtp/sync/VvmNetworkRequestCallback.java171
-rw-r--r--java/com/android/voicemailomtp/utils/IndentingPrintWriter.java160
-rw-r--r--java/com/android/voicemailomtp/utils/VoicemailDatabaseUtil.java90
-rw-r--r--java/com/android/voicemailomtp/utils/VvmDumpHandler.java46
-rw-r--r--java/com/android/voicemailomtp/utils/XmlUtils.java245
-rw-r--r--proguard.flags79
-rw-r--r--res/drawable-hdpi/fab_blue.pngbin2805 -> 0 bytes
-rw-r--r--res/drawable-hdpi/ic_videocam_24dp.pngbin267 -> 0 bytes
-rw-r--r--res/drawable-mdpi/fab_blue.pngbin1841 -> 0 bytes
-rw-r--r--res/drawable-mdpi/ic_videocam_24dp.pngbin215 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/fab_blue.pngbin4085 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/ic_videocam_24dp.pngbin257 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/fab_blue.pngbin7009 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/ic_videocam_24dp.pngbin340 -> 0 bytes
-rw-r--r--res/drawable-xxxhdpi/fab_blue.pngbin9807 -> 0 bytes
-rw-r--r--res/drawable-xxxhdpi/fab_ic_call.pngbin2921 -> 0 bytes
-rw-r--r--res/drawable/blocked_contact.xml33
-rw-r--r--res/drawable/ic_call_detail_block.xml20
-rw-r--r--res/drawable/ic_pause.xml31
-rw-r--r--res/drawable/ic_play_arrow.xml32
-rw-r--r--res/drawable/seekbar_drawable.xml63
-rw-r--r--res/layout-land/dialpad_fragment.xml87
-rw-r--r--res/layout/account_filter_header_for_phone_favorite.xml47
-rw-r--r--res/layout/all_contacts_fragment.xml54
-rw-r--r--res/layout/block_report_spam_dialog.xml35
-rw-r--r--res/layout/blocked_number_footer.xml37
-rw-r--r--res/layout/blocked_number_fragment.xml29
-rw-r--r--res/layout/blocked_number_header.xml217
-rw-r--r--res/layout/blocked_number_item.xml72
-rw-r--r--res/layout/call_detail.xml32
-rw-r--r--res/layout/call_detail_footer.xml56
-rw-r--r--res/layout/call_detail_header.xml89
-rw-r--r--res/layout/call_detail_history_item.xml56
-rw-r--r--res/layout/call_log_activity.xml40
-rw-r--r--res/layout/call_log_fragment.xml39
-rw-r--r--res/layout/call_log_list_item.xml174
-rw-r--r--res/layout/call_log_list_item_actions.xml202
-rw-r--r--res/layout/dialpad_chooser_list_item.xml36
-rw-r--r--res/layout/dialpad_fragment.xml76
-rw-r--r--res/layout/dialtacts_activity.xml73
-rw-r--r--res/layout/empty_content_view.xml54
-rw-r--r--res/layout/keyguard_preview.xml30
-rw-r--r--res/layout/lists_fragment.xml98
-rwxr-xr-xres/layout/phone_disambig_item.xml43
-rw-r--r--res/layout/phone_favorite_tile_view.xml128
-rw-r--r--res/layout/search_edittext.xml71
-rw-r--r--res/layout/set_primary_checkbox.xml32
-rw-r--r--res/layout/speed_dial_fragment.xml51
-rw-r--r--res/layout/view_numbers_to_import_fragment.xml56
-rw-r--r--res/layout/voicemail_playback_layout.xml138
-rw-r--r--res/layout/voicemail_promo_card.xml99
-rw-r--r--res/menu/call_log_options.xml22
-rw-r--r--res/menu/dialpad_options.xml30
-rw-r--r--res/menu/dialtacts_options.xml39
-rw-r--r--res/values-af/strings.xml288
-rw-r--r--res/values-am/strings.xml288
-rw-r--r--res/values-ar/strings.xml296
-rw-r--r--res/values-az/strings.xml288
-rw-r--r--res/values-b+sr+Latn/strings.xml290
-rw-r--r--res/values-be/strings.xml292
-rw-r--r--res/values-bg/strings.xml288
-rw-r--r--res/values-bn/strings.xml288
-rw-r--r--res/values-bs/strings.xml290
-rw-r--r--res/values-ca/strings.xml288
-rw-r--r--res/values-cs/strings.xml292
-rw-r--r--res/values-da/strings.xml288
-rw-r--r--res/values-de/strings.xml288
-rw-r--r--res/values-el/strings.xml288
-rw-r--r--res/values-en-rAU/strings.xml288
-rw-r--r--res/values-en-rGB/strings.xml288
-rw-r--r--res/values-en-rIN/strings.xml288
-rw-r--r--res/values-es-rUS/strings.xml288
-rw-r--r--res/values-es/strings.xml288
-rw-r--r--res/values-et/strings.xml288
-rw-r--r--res/values-eu/strings.xml288
-rw-r--r--res/values-fa/strings.xml288
-rw-r--r--res/values-fi/strings.xml288
-rw-r--r--res/values-fr-rCA/strings.xml288
-rw-r--r--res/values-fr/strings.xml288
-rw-r--r--res/values-gl/strings.xml288
-rw-r--r--res/values-gu/strings.xml288
-rw-r--r--res/values-hi/strings.xml288
-rw-r--r--res/values-hr/strings.xml290
-rw-r--r--res/values-hu/strings.xml288
-rw-r--r--res/values-hy/strings.xml288
-rw-r--r--res/values-in/strings.xml288
-rw-r--r--res/values-is/strings.xml288
-rw-r--r--res/values-it/strings.xml288
-rw-r--r--res/values-iw/strings.xml292
-rw-r--r--res/values-ja/strings.xml288
-rw-r--r--res/values-ka/strings.xml288
-rw-r--r--res/values-kk/strings.xml288
-rw-r--r--res/values-km/strings.xml288
-rw-r--r--res/values-kn/strings.xml289
-rw-r--r--res/values-ko/strings.xml288
-rw-r--r--res/values-ky/strings.xml288
-rw-r--r--res/values-lo/strings.xml288
-rw-r--r--res/values-lt/strings.xml292
-rw-r--r--res/values-lv/strings.xml290
-rw-r--r--res/values-mk/strings.xml288
-rw-r--r--res/values-ml/strings.xml288
-rw-r--r--res/values-mn/strings.xml288
-rw-r--r--res/values-mr/strings.xml288
-rw-r--r--res/values-ms/strings.xml288
-rw-r--r--res/values-my/strings.xml288
-rw-r--r--res/values-nb/strings.xml288
-rw-r--r--res/values-ne/strings.xml288
-rw-r--r--res/values-nl/strings.xml288
-rw-r--r--res/values-pa/strings.xml288
-rw-r--r--res/values-pl/strings.xml292
-rw-r--r--res/values-pt-rBR/strings.xml288
-rw-r--r--res/values-pt-rPT/strings.xml288
-rw-r--r--res/values-pt/strings.xml288
-rw-r--r--res/values-ro/strings.xml290
-rw-r--r--res/values-ru/strings.xml292
-rw-r--r--res/values-si/strings.xml288
-rw-r--r--res/values-sk/strings.xml292
-rw-r--r--res/values-sl/strings.xml292
-rw-r--r--res/values-sq/strings.xml288
-rw-r--r--res/values-sr/strings.xml290
-rw-r--r--res/values-sv/strings.xml288
-rw-r--r--res/values-sw/strings.xml288
-rw-r--r--res/values-ta/strings.xml288
-rw-r--r--res/values-te/strings.xml288
-rw-r--r--res/values-th/strings.xml288
-rw-r--r--res/values-tl/strings.xml288
-rw-r--r--res/values-tr/strings.xml288
-rw-r--r--res/values-uk/strings.xml292
-rw-r--r--res/values-ur/strings.xml288
-rw-r--r--res/values-uz/strings.xml288
-rw-r--r--res/values-vi/strings.xml288
-rw-r--r--res/values-zh-rCN/strings.xml288
-rw-r--r--res/values-zh-rHK/strings.xml288
-rw-r--r--res/values-zh-rTW/strings.xml288
-rw-r--r--res/values-zu/strings.xml288
-rw-r--r--res/values/animation_constants.xml30
-rw-r--r--res/values/attrs.xml36
-rw-r--r--res/values/colors.xml142
-rw-r--r--res/values/dimens.xml176
-rw-r--r--res/values/donottranslate_config.xml39
-rw-r--r--res/values/ids.xml25
-rw-r--r--res/values/strings.xml1116
-rw-r--r--res/values/styles.xml346
-rw-r--r--res/xml/display_options_settings.xml31
-rw-r--r--res/xml/file_paths.xml22
-rw-r--r--res/xml/searchable.xml22
-rw-r--r--res/xml/sound_settings.xml46
-rw-r--r--src-N/com/android/dialer/SdkSelectionUtils.java35
-rw-r--r--src-N/com/android/dialer/compat/BlockedNumbersSdkCompat.java37
-rw-r--r--src-N/com/android/dialer/compat/CallsSdkCompat.java25
-rw-r--r--src-N/com/android/dialer/compat/UserManagerSdkCompat.java39
-rw-r--r--src-pre-N/com/android/dialer/SdkSelectionUtils.java35
-rw-r--r--src-pre-N/com/android/dialer/compat/BlockedNumbersSdkCompat.java35
-rw-r--r--src-pre-N/com/android/dialer/compat/CallsSdkCompat.java25
-rw-r--r--src-pre-N/com/android/dialer/compat/UserManagerSdkCompat.java34
-rw-r--r--src/com/android/dialer/CallDetailActivity.java507
-rw-r--r--src/com/android/dialer/DialerApplication.java58
-rw-r--r--src/com/android/dialer/DialerBackupAgent.java38
-rw-r--r--src/com/android/dialer/DialtactsActivity.java1413
-rw-r--r--src/com/android/dialer/FloatingActionButtonBehavior.java47
-rw-r--r--src/com/android/dialer/NeededForReflection.java30
-rw-r--r--src/com/android/dialer/PhoneCallDetails.java187
-rw-r--r--src/com/android/dialer/SpecialCharSequenceMgr.java495
-rw-r--r--src/com/android/dialer/TransactionSafeActivity.java65
-rw-r--r--src/com/android/dialer/calllog/BlockReportSpamListener.java124
-rw-r--r--src/com/android/dialer/calllog/CallDetailHistoryAdapter.java166
-rw-r--r--src/com/android/dialer/calllog/CallLogActivity.java235
-rw-r--r--src/com/android/dialer/calllog/CallLogAdapter.java959
-rw-r--r--src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java505
-rw-r--r--src/com/android/dialer/calllog/CallLogFragment.java530
-rw-r--r--src/com/android/dialer/calllog/CallLogGroupBuilder.java300
-rw-r--r--src/com/android/dialer/calllog/CallLogListItemHelper.java268
-rw-r--r--src/com/android/dialer/calllog/CallLogListItemViewHolder.java776
-rw-r--r--src/com/android/dialer/calllog/CallLogNotificationsHelper.java353
-rw-r--r--src/com/android/dialer/calllog/CallLogNotificationsService.java194
-rw-r--r--src/com/android/dialer/calllog/CallLogQuery.java115
-rw-r--r--src/com/android/dialer/calllog/CallLogQueryHandler.java354
-rw-r--r--src/com/android/dialer/calllog/CallLogReceiver.java44
-rw-r--r--src/com/android/dialer/calllog/CallTypeHelper.java134
-rw-r--r--src/com/android/dialer/calllog/CallTypeIconsView.java227
-rw-r--r--src/com/android/dialer/calllog/ClearCallLogDialog.java98
-rw-r--r--src/com/android/dialer/calllog/ContactInfo.java108
-rw-r--r--src/com/android/dialer/calllog/ContactInfoHelper.java499
-rw-r--r--src/com/android/dialer/calllog/DefaultVoicemailNotifier.java269
-rw-r--r--src/com/android/dialer/calllog/GroupingListAdapter.java171
-rw-r--r--src/com/android/dialer/calllog/IntentProvider.java206
-rw-r--r--src/com/android/dialer/calllog/MissedCallNotificationReceiver.java53
-rw-r--r--src/com/android/dialer/calllog/MissedCallNotifier.java292
-rw-r--r--src/com/android/dialer/calllog/PhoneAccountUtils.java117
-rw-r--r--src/com/android/dialer/calllog/PhoneCallDetailsHelper.java359
-rw-r--r--src/com/android/dialer/calllog/PhoneCallDetailsViews.java73
-rw-r--r--src/com/android/dialer/calllog/PhoneNumberDisplayUtil.java83
-rw-r--r--src/com/android/dialer/calllog/PhoneQuery.java96
-rw-r--r--src/com/android/dialer/calllog/PromoCardViewHolder.java83
-rw-r--r--src/com/android/dialer/calllog/VisualVoicemailCallLogFragment.java87
-rw-r--r--src/com/android/dialer/calllog/VoicemailQueryHandler.java70
-rw-r--r--src/com/android/dialer/calllog/calllogcache/CallLogCache.java96
-rw-r--r--src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipop.java73
-rw-r--r--src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipopMr1.java110
-rw-r--r--src/com/android/dialer/compat/DialerCompatUtils.java31
-rw-r--r--src/com/android/dialer/compat/FilteredNumberCompat.java393
-rw-r--r--src/com/android/dialer/compat/SettingsCompat.java47
-rw-r--r--src/com/android/dialer/compat/UserManagerCompat.java71
-rw-r--r--src/com/android/dialer/contact/ContactUpdateService.java51
-rw-r--r--src/com/android/dialer/contactinfo/ContactInfoCache.java333
-rw-r--r--src/com/android/dialer/contactinfo/ContactInfoRequest.java65
-rw-r--r--src/com/android/dialer/contactinfo/ContactPhotoLoader.java120
-rw-r--r--src/com/android/dialer/contactinfo/NumberWithCountryIso.java53
-rw-r--r--src/com/android/dialer/database/DialerDatabaseHelper.java1169
-rw-r--r--src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java273
-rw-r--r--src/com/android/dialer/database/FilteredNumberContract.java163
-rw-r--r--src/com/android/dialer/database/FilteredNumberProvider.java211
-rw-r--r--src/com/android/dialer/database/VoicemailArchiveContract.java203
-rw-r--r--src/com/android/dialer/database/VoicemailArchiveProvider.java218
-rw-r--r--src/com/android/dialer/dialpad/DialpadFragment.java1695
-rw-r--r--src/com/android/dialer/dialpad/LatinSmartDialMap.java413
-rw-r--r--src/com/android/dialer/dialpad/PseudoEmergencyAnimator.java160
-rw-r--r--src/com/android/dialer/dialpad/SmartDialCursorLoader.java193
-rw-r--r--src/com/android/dialer/dialpad/SmartDialMap.java43
-rw-r--r--src/com/android/dialer/dialpad/SmartDialMatchPosition.java70
-rw-r--r--src/com/android/dialer/dialpad/SmartDialNameMatcher.java439
-rw-r--r--src/com/android/dialer/dialpad/SmartDialPrefix.java608
-rw-r--r--src/com/android/dialer/dialpad/UnicodeDialerKeyListener.java54
-rw-r--r--src/com/android/dialer/filterednumber/BlockNumberDialogFragment.java320
-rw-r--r--src/com/android/dialer/filterednumber/BlockedNumbersAdapter.java96
-rw-r--r--src/com/android/dialer/filterednumber/BlockedNumbersAutoMigrator.java101
-rw-r--r--src/com/android/dialer/filterednumber/BlockedNumbersFragment.java264
-rw-r--r--src/com/android/dialer/filterednumber/BlockedNumbersMigrator.java135
-rw-r--r--src/com/android/dialer/filterednumber/BlockedNumbersSettingsActivity.java162
-rw-r--r--src/com/android/dialer/filterednumber/FilteredNumbersUtil.java369
-rw-r--r--src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragment.java110
-rw-r--r--src/com/android/dialer/filterednumber/NumbersAdapter.java137
-rw-r--r--src/com/android/dialer/filterednumber/ViewNumbersToImportAdapter.java57
-rw-r--r--src/com/android/dialer/filterednumber/ViewNumbersToImportFragment.java133
-rw-r--r--src/com/android/dialer/interactions/PhoneNumberInteraction.java516
-rw-r--r--src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java109
-rw-r--r--src/com/android/dialer/list/AllContactsFragment.java198
-rw-r--r--src/com/android/dialer/list/BlockedListSearchAdapter.java90
-rw-r--r--src/com/android/dialer/list/BlockedListSearchFragment.java244
-rw-r--r--src/com/android/dialer/list/ContentChangedFilter.java40
-rw-r--r--src/com/android/dialer/list/DialerPhoneNumberListAdapter.java220
-rw-r--r--src/com/android/dialer/list/DragDropController.java95
-rw-r--r--src/com/android/dialer/list/ListsFragment.java487
-rw-r--r--src/com/android/dialer/list/OnDragDropListener.java41
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteListView.java326
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteSquareTileView.java112
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteTileView.java155
-rw-r--r--src/com/android/dialer/list/PhoneFavoritesTileAdapter.java696
-rw-r--r--src/com/android/dialer/list/RegularSearchFragment.java151
-rw-r--r--src/com/android/dialer/list/RegularSearchListAdapter.java130
-rw-r--r--src/com/android/dialer/list/RemoveView.java94
-rw-r--r--src/com/android/dialer/list/SearchFragment.java399
-rw-r--r--src/com/android/dialer/list/SmartDialNumberListAdapter.java130
-rw-r--r--src/com/android/dialer/list/SmartDialSearchFragment.java134
-rw-r--r--src/com/android/dialer/list/SpeedDialFragment.java504
-rw-r--r--src/com/android/dialer/logging/InteractionEvent.java76
-rw-r--r--src/com/android/dialer/logging/Logger.java85
-rw-r--r--src/com/android/dialer/logging/ScreenEvent.java172
-rw-r--r--src/com/android/dialer/service/CachedNumberLookupService.java61
-rw-r--r--src/com/android/dialer/service/ExtendedCallInfoService.java79
-rw-r--r--src/com/android/dialer/settings/AppCompatPreferenceActivity.java155
-rw-r--r--src/com/android/dialer/settings/DefaultRingtonePreference.java65
-rw-r--r--src/com/android/dialer/settings/DialerSettingsActivity.java190
-rw-r--r--src/com/android/dialer/settings/SoundSettingsFragment.java245
-rw-r--r--src/com/android/dialer/util/AppCompatConstants.java30
-rw-r--r--src/com/android/dialer/util/Assert.java36
-rw-r--r--src/com/android/dialer/util/AsyncTaskExecutor.java48
-rw-r--r--src/com/android/dialer/util/AsyncTaskExecutors.java100
-rw-r--r--src/com/android/dialer/util/BlockReportSpamDialogs.java293
-rw-r--r--src/com/android/dialer/util/DialerUtils.java195
-rw-r--r--src/com/android/dialer/util/EmptyLoader.java60
-rw-r--r--src/com/android/dialer/util/ExpirableCache.java266
-rw-r--r--src/com/android/dialer/util/IntentUtil.java162
-rw-r--r--src/com/android/dialer/util/MoreStrings.java43
-rw-r--r--src/com/android/dialer/util/OrientationUtil.java34
-rw-r--r--src/com/android/dialer/util/PhoneLookupUtil.java40
-rw-r--r--src/com/android/dialer/util/PhoneNumberUtil.java138
-rw-r--r--src/com/android/dialer/util/TelecomUtil.java229
-rw-r--r--src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java103
-rw-r--r--src/com/android/dialer/voicemail/VoicemailArchiveActivity.java160
-rw-r--r--src/com/android/dialer/voicemail/VoicemailArchivePlaybackPresenter.java90
-rw-r--r--src/com/android/dialer/voicemail/VoicemailAsyncTaskUtil.java346
-rw-r--r--src/com/android/dialer/voicemail/VoicemailAudioManager.java200
-rw-r--r--src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java638
-rw-r--r--src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java1010
-rw-r--r--src/com/android/dialer/voicemail/VoicemailStatusHelper.java91
-rw-r--r--src/com/android/dialer/voicemail/VoicemailStatusHelperImpl.java272
-rw-r--r--src/com/android/dialer/voicemail/WiredHeadsetManager.java88
-rw-r--r--src/com/android/dialer/widget/ActionBarController.java243
-rw-r--r--src/com/android/dialer/widget/EmptyContentView.java118
-rw-r--r--src/com/android/dialer/widget/SearchEditTextLayout.java321
-rw-r--r--src/com/android/dialerbind/DatabaseHelperManager.java28
-rw-r--r--src/com/android/dialerbind/ObjectFactory.java96
-rw-r--r--tests/Android.mk28
-rw-r--r--tests/AndroidManifest.xml69
-rw-r--r--tests/assets/README.txt3
-rw-r--r--tests/assets/quick_test_recording.mp3bin30591 -> 0 bytes
-rw-r--r--tests/proguard.flags20
-rw-r--r--tests/res/drawable/phone_icon.pngbin3621 -> 0 bytes
-rw-r--r--tests/res/layout/fill_call_log_test.xml267
-rw-r--r--tests/res/values/donottranslate_strings.xml60
-rw-r--r--tests/res/xml/iconset.xml23
-rw-r--r--tests/src/com/android/dialer/CallDetailActivityTest.java183
-rw-r--r--tests/src/com/android/dialer/DialerLaunchPerformance.java50
-rw-r--r--tests/src/com/android/dialer/calllog/BlockReportSpamListenerTest.java72
-rw-r--r--tests/src/com/android/dialer/calllog/CallLogAdapterTest.java918
-rw-r--r--tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java470
-rw-r--r--tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java304
-rw-r--r--tests/src/com/android/dialer/calllog/CallLogNotificationsHelperTest.java137
-rw-r--r--tests/src/com/android/dialer/calllog/CallLogQueryTestUtils.java46
-rw-r--r--tests/src/com/android/dialer/calllog/ContactInfoHelperTest.java160
-rw-r--r--tests/src/com/android/dialer/calllog/GroupingListAdapterTests.java173
-rw-r--r--tests/src/com/android/dialer/calllog/PhoneAccountUtilsTest.java104
-rw-r--r--tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java581
-rw-r--r--tests/src/com/android/dialer/calllog/PhoneCallDetailsTest.java63
-rw-r--r--tests/src/com/android/dialer/calllog/calllogcache/TestTelecomCallLogCache.java65
-rw-r--r--tests/src/com/android/dialer/compat/FilteredNumberCompatInstrumentationTest.java92
-rw-r--r--tests/src/com/android/dialer/compat/FilteredNumberCompatTest.java292
-rw-r--r--tests/src/com/android/dialer/compat/UserManagerCompatTest.java44
-rw-r--r--tests/src/com/android/dialer/contactinfo/ContactPhotoLoaderTest.java106
-rw-r--r--tests/src/com/android/dialer/database/DatabaseTestUtils.java82
-rw-r--r--tests/src/com/android/dialer/database/DialerDatabaseHelperTest.java154
-rw-r--r--tests/src/com/android/dialer/database/FilteredNumberAsyncQueryHandlerTest.java457
-rw-r--r--tests/src/com/android/dialer/database/FilteredNumberProviderTest.java232
-rw-r--r--tests/src/com/android/dialer/database/SmartDialPrefixTest.java523
-rw-r--r--tests/src/com/android/dialer/database/VoicemailArchiveProviderTest.java306
-rw-r--r--tests/src/com/android/dialer/dialpad/DialpadFragmentInstrumentationTest.java121
-rw-r--r--tests/src/com/android/dialer/dialpad/DialpadFragmentTest.java111
-rw-r--r--tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java275
-rw-r--r--tests/src/com/android/dialer/dialpad/UnicodeDialerKeyListenerTest.java74
-rw-r--r--tests/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigratorTest.java201
-rw-r--r--tests/src/com/android/dialer/filterednumber/BlockedNumbersFragmentInstrumentationTest.java93
-rw-r--r--tests/src/com/android/dialer/filterednumber/BlockedNumbersMigratorTest.java160
-rw-r--r--tests/src/com/android/dialer/filterednumber/FilteredNumbersUtilTest.java132
-rw-r--r--tests/src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragmentInstrumentationTest.java93
-rw-r--r--tests/src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragmentTest.java61
-rw-r--r--tests/src/com/android/dialer/interactions/PhoneNumberInteractionTest.java262
-rw-r--r--tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java301
-rw-r--r--tests/src/com/android/dialer/tests/calllog/FillCallLogTestActivity.java658
-rw-r--r--tests/src/com/android/dialer/util/DialerUtilsTest.java78
-rw-r--r--tests/src/com/android/dialer/util/ExpirableCacheTest.java125
-rw-r--r--tests/src/com/android/dialer/util/FakeAsyncTaskExecutor.java231
-rw-r--r--tests/src/com/android/dialer/util/LocaleTestUtils.java119
-rw-r--r--tests/src/com/android/dialer/util/TestConstants.java5
-rw-r--r--tests/src/com/android/dialer/voicemail/VoicemailActivityInstrumentationTestCase2.java227
-rw-r--r--tests/src/com/android/dialer/voicemail/VoicemailArchiveTest.java116
-rw-r--r--tests/src/com/android/dialer/voicemail/VoicemailAsyncTaskUtilTest.java388
-rw-r--r--tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java145
-rw-r--r--tests/src/com/android/dialer/voicemail/VoicemailStatusHelperImplTest.java274
-rw-r--r--tests/src/com/android/dialer/widget/ActionBarControllerTest.java181
-rw-r--r--tools/gradle/android.properties2
-rwxr-xr-xtools/gradle/gradlew204
-rw-r--r--tools/gradle/repositories.properties1
-rw-r--r--tools/gradle/settings.gradle63
2739 files changed, 162553 insertions, 118040 deletions
diff --git a/Android.mk b/Android.mk
index 0978bec05..f7219302a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,73 +1,251 @@
+# Local modifications:
+# * All location/maps code has been removed from the incallui.
+# * Precompiled AutoValue classes have been included.
+# * Precompiled Dagger classes have been included.
+# * All autovalue imports and annotations have been stripped.
+# * Precompiled proto classes have been included.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
-
-incallui_dir := InCallUI
-contacts_common_dir := ../ContactsCommon
-phone_common_dir := ../PhoneCommon
-
ifeq ($(TARGET_BUILD_APPS),)
support_library_root_dir := frameworks/support
else
support_library_root_dir := prebuilts/sdk/current/support
endif
-src_dirs := src \
- $(incallui_dir)/src \
- $(contacts_common_dir)/src \
- $(phone_common_dir)/src
+# The base directory for Dialer sources.
+BASE_DIR := java/com/android
-res_dirs := res \
- $(incallui_dir)/res \
- $(contacts_common_dir)/res \
- $(contacts_common_dir)/icons/res \
- $(phone_common_dir)/res
+# Primary dialer module sources.
+SRC_DIRS := \
+ $(BASE_DIR)/contacts/common \
+ $(BASE_DIR)/dialer \
+ $(BASE_DIR)/incallui \
+ $(BASE_DIR)/voicemailomtp
-src_dirs += \
- src-N
+# All Dialers resources.
+# find . -type d -name "res" | uniq | sort
+RES_DIRS := \
+ assets/product/res \
+ assets/quantum/res \
+ $(BASE_DIR)/contacts/common/res \
+ $(BASE_DIR)/dialer/app/res \
+ $(BASE_DIR)/dialer/app/voicemail/error/res \
+ $(BASE_DIR)/dialer/blocking/res \
+ $(BASE_DIR)/dialer/callcomposer/camera/camerafocus/res \
+ $(BASE_DIR)/dialer/callcomposer/cameraui/res \
+ $(BASE_DIR)/dialer/callcomposer/res \
+ $(BASE_DIR)/dialer/common/res \
+ $(BASE_DIR)/dialer/dialpadview/res \
+ $(BASE_DIR)/dialer/interactions/res \
+ $(BASE_DIR)/dialer/phonenumberutil/res \
+ $(BASE_DIR)/dialer/shortcuts/res \
+ $(BASE_DIR)/dialer/theme/res \
+ $(BASE_DIR)/dialer/util/res \
+ $(BASE_DIR)/dialer/voicemailstatus/res \
+ $(BASE_DIR)/dialer/widget/res \
+ $(BASE_DIR)/incallui/answer/impl/affordance/res \
+ $(BASE_DIR)/incallui/answer/impl/answermethod/res \
+ $(BASE_DIR)/incallui/answer/impl/hint/res \
+ $(BASE_DIR)/incallui/answer/impl/res \
+ $(BASE_DIR)/incallui/audioroute/res \
+ $(BASE_DIR)/incallui/autoresizetext/res \
+ $(BASE_DIR)/incallui/commontheme/res \
+ $(BASE_DIR)/incallui/contactgrid/res \
+ $(BASE_DIR)/incallui/hold/res \
+ $(BASE_DIR)/incallui/incall/impl/res \
+ $(BASE_DIR)/incallui/res \
+ $(BASE_DIR)/incallui/sessiondata/res \
+ $(BASE_DIR)/incallui/video/impl/res \
+ $(BASE_DIR)/incallui/wifi/res \
+ $(BASE_DIR)/voicemailomtp/res
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs)) \
- $(support_library_root_dir)/v7/cardview/res \
- $(support_library_root_dir)/v7/recyclerview/res \
- $(support_library_root_dir)/v7/appcompat/res \
- $(support_library_root_dir)/design/res \
- $(support_library_root_dir)/transition/res
+# Dialer manifest files to merge.
+# find . -type f -name "AndroidManifest.xml" | uniq | sort
+DIALER_MANIFEST_FILES += \
+ $(BASE_DIR)/contacts/common/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/app/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/app/manifests/activities/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/app/voicemail/error/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/backup/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/blocking/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/callcomposer/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/callcomposer/camera/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/callcomposer/camera/camerafocus/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/callcomposer/cameraui/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/common/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/debug/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/debug/impl/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/dialpadview/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/interactions/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/phonenumberutil/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/shortcuts/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/simulator/impl/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/theme/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/util/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/voicemailstatus/AndroidManifest.xml \
+ $(BASE_DIR)/dialer/widget/AndroidManifest.xml \
+ $(BASE_DIR)/incallui/AndroidManifest.xml \
+ $(BASE_DIR)/incallui/answer/impl/affordance/AndroidManifest.xml \
+ $(BASE_DIR)/incallui/answer/impl/AndroidManifest.xml \
+ $(BASE_DIR)/incallui/answer/impl/answermethod/AndroidManifest.xml \
+ $(BASE_DIR)/incallui/answer/impl/hint/AndroidManifest.xml \
+ $(BASE_DIR)/incallui/audioroute/AndroidManifest.xml \
+ $(BASE_DIR)/incallui/autoresizetext/AndroidManifest.xml \
+ $(BASE_DIR)/incallui/commontheme/AndroidManifest.xml \
+ $(BASE_DIR)/incallui/contactgrid/AndroidManifest.xml \
+ $(BASE_DIR)/incallui/hold/AndroidManifest.xml \
+ $(BASE_DIR)/incallui/incall/impl/AndroidManifest.xml \
+ $(BASE_DIR)/incallui/sessiondata/AndroidManifest.xml \
+ $(BASE_DIR)/incallui/video/impl/AndroidManifest.xml \
+ $(BASE_DIR)/incallui/wifi/AndroidManifest.xml \
+ $(BASE_DIR)/voicemailomtp/AndroidManifest.xml
+# Merge all manifest files.
+LOCAL_FULL_LIBS_MANIFEST_FILES := \
+ $(addprefix $(LOCAL_PATH)/, $(DIALER_MANIFEST_FILES))
+LOCAL_SRC_FILES := $(call all-java-files-under, $(SRC_DIRS))
+LOCAL_RESOURCE_DIR := \
+ $(addprefix $(LOCAL_PATH)/, $(RES_DIRS)) \
+ $(support_library_root_dir)/design/res \
+ $(support_library_root_dir)/v7/appcompat/res \
+ $(support_library_root_dir)/v7/cardview/res \
+ $(support_library_root_dir)/v7/recyclerview/res
+
+# We specify each package explicitly to glob resource files.
LOCAL_AAPT_FLAGS := \
- --auto-add-overlay \
- --extra-packages android.support.v7.appcompat \
- --extra-packages android.support.v7.cardview \
- --extra-packages android.support.v7.recyclerview \
- --extra-packages android.support.design \
- --extra-packages android.support.transition \
- --extra-packages com.android.incallui \
- --extra-packages com.android.contacts.common \
- --extra-packages com.android.phone.common
+ --auto-add-overlay \
+ --extra-packages android.support.design \
+ --extra-packages android.support.transition \
+ --extra-packages android.support.v7.appcompat \
+ --extra-packages android.support.v7.cardview \
+ --extra-packages android.support.v7.recyclerview \
+ --extra-packages com.android.contacts.common \
+ --extra-packages com.android.dialer.app \
+ --extra-packages com.android.dialer.app.voicemail.error \
+ --extra-packages com.android.dialer.blocking \
+ --extra-packages com.android.dialer.callcomposer \
+ --extra-packages com.android.dialer.callcomposer \
+ --extra-packages com.android.dialer.callcomposer.camera \
+ --extra-packages com.android.dialer.callcomposer.camera.camerafocus \
+ --extra-packages com.android.dialer.callcomposer.cameraui \
+ --extra-packages com.android.dialer.common \
+ --extra-packages com.android.dialer.dialpadview \
+ --extra-packages com.android.dialer.interactions \
+ --extra-packages com.android.dialer.phonenumberutil \
+ --extra-packages com.android.dialer.shortcuts \
+ --extra-packages com.android.dialer.util \
+ --extra-packages com.android.dialer.voicemailstatus \
+ --extra-packages com.android.dialer.widget \
+ --extra-packages com.android.incallui \
+ --extra-packages com.android.incallui.answer.impl \
+ --extra-packages com.android.incallui.answer.impl.affordance \
+ --extra-packages com.android.incallui.answer.impl.affordance \
+ --extra-packages com.android.incallui.answer.impl.answermethod \
+ --extra-packages com.android.incallui.answer.impl.hint \
+ --extra-packages com.android.incallui.audioroute \
+ --extra-packages com.android.incallui.autoresizetext \
+ --extra-packages com.android.incallui.commontheme \
+ --extra-packages com.android.incallui.contactgrid \
+ --extra-packages com.android.incallui.hold \
+ --extra-packages com.android.incallui.incall.impl \
+ --extra-packages com.android.incallui.sessiondata \
+ --extra-packages com.android.incallui.video \
+ --extra-packages com.android.incallui.video.impl \
+ --extra-packages com.android.incallui.wifi \
+ --extra-packages com.android.phone.common \
+ --extra-packages com.android.voicemailomtp \
+ --extra-packages com.android.voicemailomtp.settings \
+ --extra-packages me.leolin.shortcutbadger
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-common \
- android-support-v13 \
- android-support-v4 \
- android-support-v7-appcompat \
- android-support-v7-cardview \
- android-support-v7-recyclerview \
- android-support-design \
- android-support-transition \
- com.android.vcard \
- guava \
- libphonenumber
+ android-common \
+ android-support-design \
+ android-support-v13 \
+ android-support-v4 \
+ android-support-v7-appcompat \
+ android-support-v7-cardview \
+ android-support-v7-recyclerview \
+ com.android.vcard \
+ dailer-dagger2-compiler \
+ dialer-dagger2 \
+ dialer-dagger2-producers \
+ dialer-glide \
+ dialer-javax-annotation-api \
+ dialer-javax-inject \
+ dialer-libshortcutbadger \
+ jsr305 \
+ libphonenumber \
+ libprotobuf-java-nano \
+ org.apache.http.legacy.boot \
+ volley
+
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations \
+ android-support-transition \
+ dailer-dagger2-compiler \
+ dialer-dagger2 \
+ dialer-dagger2-producers \
+ dialer-glide \
+ dialer-guava \
+ dialer-javax-annotation-api \
+ dialer-javax-inject \
+ dialer-libshortcutbadger \
+ jsr305 \
+ libprotobuf-java-nano
+
+# Libraries needed by the compiler (JACK) to generate code.
+PROCESSOR_LIBRARIES_TARGET := \
+ dailer-dagger2-compiler \
+ dialer-dagger2 \
+ dialer-dagger2-producers \
+ dialer-guava \
+ dialer-javax-annotation-api \
+ dialer-javax-inject
+
+# TODO: Include when JACK properly supports AutoValue b/35360557
+# (builders not generated successfully, javac duplicate issues) in
+# LOCAL_STATIC_JAVA_LIBRARIES, LOCAL_JAVA_LIBRARIES, PROCESSOR_LIBRARIES_TARGET
+# dialer-auto-value
+
+# Resolve the jar paths.
+PROCESSOR_JARS := $(call java-lib-deps, $(PROCESSOR_LIBRARIES_TARGET))
+LOCAL_ADDITIONAL_DEPENDENCIES += $(PROCESSOR_JARS)
+
+LOCAL_JACK_FLAGS += --processorpath $(call normalize-path-list,$(PROCESSOR_JARS))
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags $(incallui_dir)/proguard.flags
+LOCAL_SDK_VERSION := current
+LOCAL_MODULE_TAGS := optional
LOCAL_PACKAGE_NAME := Dialer
LOCAL_CERTIFICATE := shared
LOCAL_PRIVILEGED_MODULE := true
+include $(BUILD_PACKAGE)
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags $(incallui_dir)/proguard.flags
+# Cleanup local state
+BASE_DIR :=
+SRC_DIRS :=
+RES_DIRS :=
+DIALER_MANIFEST_FILES :=
+PROCESSOR_LIBRARIES_TARGET :=
+PROCESSOR_JARS :=
-LOCAL_SDK_VERSION := current
+# Create references to prebuilt libraries.
+include $(CLEAR_VARS)
-include $(BUILD_PACKAGE)
+LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
+ dailer-dagger2-compiler:../../../prebuilts/tools/common/m2/repository/com/google/dagger/dagger-compiler/2.6/dagger-compiler-2.6$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ dialer-auto-common:../../../prebuilts/tools/common/m2/repository/com/google/auto/auto-common/0.6/auto-common-0.6$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ dialer-auto-value:../../../prebuilts/tools/common/m2/repository/com/google/auto/value/auto-value/1.3/auto-value-1.3$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ dialer-dagger2:../../../prebuilts/tools/common/m2/repository/com/google/dagger/dagger/2.6/dagger-2.6$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ dialer-dagger2-producers:../../../prebuilts/tools/common/m2/repository/com/google/dagger/dagger-producers/2.6/dagger-producers-2.6$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ dialer-glide:../../../prebuilts/maven_repo/bumptech/com/github/bumptech/glide/glide/4.0.0-SNAPSHOT/glide-4.0.0-SNAPSHOT$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ dialer-guava:../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/20.0/guava-20.0$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ dialer-javax-annotation-api:../../../prebuilts/tools/common/m2/repository/javax/annotation/javax.annotation-api/1.2/javax.annotation-api-1.2$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ dialer-javax-inject:../../../prebuilts/tools/common/m2/repository/javax/inject/javax.inject/1/javax.inject-1$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ dialer-libshortcutbadger:../../../prebuilts/tools/common/m2/repository/me/leolin/ShortcutBadger/1.1.13/ShortcutBadger-1.1.13$(COMMON_JAVA_PACKAGE_SUFFIX)
-# Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(BUILD_MULTI_PREBUILT)
+
+include $(CLEAR_VARS)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d83080376..85ed1981c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2006 The Android Open Source Project
+<!-- 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.
@@ -13,340 +12,106 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
+<!-- From java/com/android/dialer/binary/aosp/AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.dialer"
- coreApp="true"
- android:versionCode="30000"
- android:versionName="3.00.00">
-
- <uses-sdk
- android:minSdkVersion="23"
- android:targetSdkVersion="23" />
-
- <uses-permission android:name="android.permission.CALL_PHONE" />
- <uses-permission android:name="android.permission.READ_CONTACTS" />
- <uses-permission android:name="android.permission.WRITE_CONTACTS" />
- <uses-permission android:name="android.permission.READ_CALL_LOG" />
- <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
- <uses-permission android:name="android.permission.READ_PROFILE" />
- <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
- <uses-permission android:name="android.permission.GET_ACCOUNTS" />
- <uses-permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
- <uses-permission android:name="android.permission.NFC" />
- <uses-permission android:name="android.permission.READ_PHONE_STATE" />
- <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
- <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
- <uses-permission android:name="android.permission.WAKE_LOCK" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.WRITE_SETTINGS" />
- <uses-permission android:name="android.permission.USE_CREDENTIALS" />
- <uses-permission android:name="android.permission.VIBRATE" />
- <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
- <uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE" />
- <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
- <uses-permission android:name="com.android.voicemail.permission.WRITE_VOICEMAIL" />
- <uses-permission android:name="com.android.voicemail.permission.READ_VOICEMAIL" />
- <uses-permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK" />
- <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
- <uses-permission android:name="android.permission.BROADCAST_STICKY" />
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- <!-- This tells the activity manager to not delay any of our activity
- start requests, even if they happen immediately after the user
- presses home. -->
- <uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
-
- <application
- android:name="DialerApplication"
- android:label="@string/applicationLabel"
- android:icon="@mipmap/ic_launcher_phone"
- android:hardwareAccelerated="true"
- android:supportsRtl="true"
- android:backupAgent='com.android.dialer.DialerBackupAgent'
- android:usesCleartextTraffic="false">
-
- <meta-data android:name="com.google.android.backup.api_key"
- android:value="AEdPqrEAAAAIBXgtCEKQ6W0PXVnW-ZVia2KmlV2AxsTw3GjAeQ" />
-
- <!-- The entrance point for Phone UI.
- stateAlwaysHidden is set to suppress keyboard show up on
- dialpad screen. -->
- <activity android:name=".DialtactsActivity"
- android:label="@string/launcherActivityLabel"
- android:theme="@style/DialtactsActivityTheme"
- android:launchMode="singleTask"
- android:clearTaskOnLaunch="true"
- android:icon="@mipmap/ic_launcher_phone"
- android:windowSoftInputMode="stateAlwaysHidden|adjustNothing"
- android:resizeableActivity="true"
- android:directBootAware="true">
- <intent-filter>
- <action android:name="android.intent.action.DIAL" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
- <data android:mimeType="vnd.android.cursor.item/phone" />
- <data android:mimeType="vnd.android.cursor.item/person" />
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.DIAL" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
- <data android:scheme="voicemail" />
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.DIAL" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.BROWSABLE" />
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.VIEW" />
- <action android:name="android.intent.action.DIAL" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
- <data android:scheme="tel" />
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.VIEW" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
- <data android:mimeType="vnd.android.cursor.dir/calls" />
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.CALL_BUTTON" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
- </intent-filter>
- <!-- This was never intended to be public, but is here for backward
- compatibility. Use Intent.ACTION_DIAL instead. -->
- <intent-filter>
- <action android:name="com.android.phone.action.TOUCH_DIALER" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.TAB" />
- </intent-filter>
- <intent-filter android:label="@string/callHistoryIconLabel">
- <action android:name="com.android.phone.action.RECENT_CALLS" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.TAB" />
- </intent-filter>
- <meta-data
- android:name="com.android.keyguard.layout"
- android:resource="@layout/keyguard_preview" />
- </activity>
-
- <activity android:name="com.android.dialer.settings.DialerSettingsActivity"
- android:label="@string/dialer_settings_label"
- android:parentActivityName="com.android.dialer.DialtactsActivity"
- android:theme="@style/SettingsStyle"
- android:exported="false">
- </activity>
-
- <activity android:name="com.android.dialer.filterednumber.BlockedNumbersSettingsActivity"
- android:label="@string/manage_blocked_numbers_label"
- android:parentActivityName="com.android.dialer.settings.DialerSettingsActivity"
- android:theme="@style/ManageBlockedNumbersStyle"
- android:exported="false">
- </activity>
-
- <activity android:name="com.android.dialer.voicemail.VoicemailArchiveActivity"
- android:label="@string/voicemail_archive_activity_title"
- android:theme="@style/DialtactsThemeWithoutActionBarOverlay">
- </activity>
-
- <activity android:name="com.android.dialer.calllog.CallLogActivity"
- android:label="@string/call_log_activity_title"
- android:theme="@style/DialtactsThemeWithoutActionBarOverlay"
- android:icon="@mipmap/ic_launcher_phone">
- </activity>
-
- <activity android:name="com.android.dialer.CallDetailActivity"
- android:label="@string/callDetailTitle"
- android:theme="@style/CallDetailActivityTheme"
- android:parentActivityName="com.android.dialer.calllog.CallLogActivity"
- android:icon="@mipmap/ic_launcher_phone">
- <intent-filter>
- <action android:name="android.intent.action.VIEW"/>
- <category android:name="android.intent.category.DEFAULT"/>
- <data android:mimeType="vnd.android.cursor.item/calls"/>
- </intent-filter>
- </activity>
-
- <activity android:name="com.android.contacts.common.test.FragmentTestActivity">
- <intent-filter>
- <category android:name="android.intent.category.TEST"/>
- </intent-filter>
- </activity>
-
- <activity android:name="com.android.contacts.common.dialog.CallSubjectDialog"
- android:theme="@style/Theme.CallSubjectDialogTheme"
- android:windowSoftInputMode="stateVisible|adjustResize">
- <intent-filter>
- <action android:name="android.intent.action.VIEW"/>
- </intent-filter>
- </activity>
-
- <!-- Backwards compatibility: "Phone" from Gingerbread and earlier -->
- <activity-alias android:name="DialtactsActivity"
- android:targetActivity=".DialtactsActivity"
- android:exported="true"
- />
-
- <!-- Backwards compatibility: "Call log" from Gingerbread and earlier -->
- <activity-alias android:name="RecentCallsListActivity"
- android:targetActivity=".DialtactsActivity"
- android:exported="true"
- />
-
- <!-- Backwards compatibility: "Call log" from ICS -->
- <activity-alias android:name=".activities.CallLogActivity"
- android:targetActivity=".DialtactsActivity"
- android:exported="true"
- />
-
- <activity
- android:name="com.android.contacts.common.activity.RequestImportVCardPermissionsActivity"
- android:label="@string/launcherActivityLabel"
- android:theme="@style/BackgroundOnlyTheme"
- android:exported="false"/>
-
- <!-- vCard related -->
- <activity android:name="com.android.contacts.common.vcard.ImportVCardActivity"
- android:configChanges="orientation|screenSize|keyboardHidden"
- android:theme="@style/BackgroundOnlyTheme">
- </activity>
-
- <activity android:name="com.android.contacts.common.vcard.NfcImportVCardActivity"
- android:configChanges="orientation|screenSize|keyboardHidden"
- android:theme="@style/BackgroundOnlyTheme">
- </activity>
-
- <activity android:name="com.android.contacts.common.vcard.CancelActivity"
- android:theme="@style/BackgroundOnlyTheme"/>
-
- <activity android:name="com.android.contacts.common.vcard.SelectAccountActivity"
- android:theme="@style/BackgroundOnlyTheme"/>
-
- <activity android:name="com.android.contacts.common.vcard.ExportVCardActivity"
- android:theme="@style/BackgroundOnlyTheme"/>
-
- <activity android:name="com.android.contacts.common.vcard.ShareVCardActivity"
- android:theme="@style/BackgroundOnlyTheme" />
-
- <service
- android:name="com.android.contacts.common.vcard.VCardService"
- android:exported="false"/>
- <!-- end vCard related -->
-
- <receiver android:name=".calllog.CallLogReceiver">
- <intent-filter>
- <action android:name="android.intent.action.NEW_VOICEMAIL" />
- <data
- android:scheme="content"
- android:host="com.android.voicemail"
- android:mimeType="vnd.android.cursor.item/voicemail"
- />
- </intent-filter>
- <intent-filter android:priority="100">
- <action android:name="android.intent.action.BOOT_COMPLETED"/>
- </intent-filter>
- </receiver>
-
- <receiver android:name=".interactions.UndemoteOutgoingCallReceiver">
- <intent-filter>
- <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
- </intent-filter>
- </receiver>
-
- <service
- android:name=".calllog.CallLogNotificationsService"
- android:directBootAware="true"
- android:exported="false"
- />
-
- <receiver android:name=".calllog.MissedCallNotificationReceiver"
- android:directBootAware="true">
- <intent-filter>
- <action android:name="android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION" />
- </intent-filter>
- </receiver>
-
- <!-- Service to update a contact -->
- <service
- android:name=".contact.ContactUpdateService"
- android:exported="false" />
-
- <!-- Broadcast receiver that passively listens to location updates -->
- <receiver android:name="com.android.contacts.common.location.CountryDetector$LocationChangedReceiver"/>
-
- <!-- IntentService to update the user's current country -->
- <service android:name="com.android.contacts.common.location.UpdateCountryService"
- android:exported="false"/>
-
- <!-- Main in-call UI activity. This is never launched directly
- from outside the phone app; instead, it's either launched by
- the OutgoingCallBroadcaster (for outgoing calls), or as the
- fullScreenIntent of a notification (for incoming calls.) -->
- <activity android:name="com.android.incallui.InCallActivity"
- android:theme="@style/Theme.InCallScreen"
- android:label="@string/phoneAppLabel"
- android:excludeFromRecents="true"
- android:launchMode="singleInstance"
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboardHidden"
- android:exported="false"
- android:screenOrientation="nosensor"
- android:directBootAware="true"
- android:resizeableActivity="true">
- </activity>
-
- <service android:name="com.android.incallui.InCallServiceImpl"
- android:permission="android.permission.BIND_INCALL_SERVICE"
- android:directBootAware="true" >
- <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" />
- <meta-data android:name="android.telecom.IN_CALL_SERVICE_RINGING"
- android:value="false"/>
- <meta-data android:name="android.telecom.INCLUDE_EXTERNAL_CALLS"
- android:value="true"/>
- <intent-filter>
- <action android:name="android.telecom.InCallService"/>
- </intent-filter>
- </service>
-
- <!-- BroadcastReceiver for receiving Intents from Notification mechanism. -->
- <receiver android:name="com.android.incallui.NotificationBroadcastReceiver"
- android:directBootAware="true"
- android:exported="false" />
-
- <provider
- android:name=".database.FilteredNumberProvider"
- android:authorities="com.android.dialer.database.filterednumberprovider"
- android:exported="false"
- android:multiprocess="false"
- />
-
- <provider
- android:name="android.support.v4.content.FileProvider"
- android:authorities="@string/contacts_file_provider_authority"
- android:grantUriPermissions="true"
- android:exported="false">
- <meta-data
- android:name="android.support.FILE_PROVIDER_PATHS"
- android:resource="@xml/file_paths" />
- </provider>
+ coreApp="true"
+ package="com.android.dialer"
+ android:versionCode="90000"
+ android:versionName="9.0">
+
+ <uses-sdk
+ android:minSdkVersion="23"
+ android:targetSdkVersion="25"/>
+
+ <uses-permission android:name="android.permission.CALL_PHONE"/>
+ <uses-permission android:name="android.permission.READ_CONTACTS"/>
+ <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+ <uses-permission android:name="android.permission.READ_CALL_LOG"/>
+ <uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
+ <uses-permission android:name="android.permission.READ_PROFILE"/>
+ <uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
+ <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
+ <uses-permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
+ <uses-permission android:name="android.permission.NFC"/>
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
+ <uses-permission android:name="android.permission.USE_CREDENTIALS"/>
+ <uses-permission android:name="android.permission.VIBRATE"/>
+ <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
+ <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL"/>
+ <uses-permission android:name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
+ <uses-permission android:name="com.android.voicemail.permission.READ_VOICEMAIL"/>
+ <uses-permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"/>
+ <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+ <uses-permission android:name="android.permission.BROADCAST_STICKY"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.SEND_SMS"/>
+
+ <uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+ <!-- We use this to disable the status bar buttons of home, back and recent
+ during an incoming call. By doing so this allows us to not show the user
+ is viewing the activity in full screen alert, on a fresh system/factory
+ reset state of the app. -->
+ <uses-permission android:name="android.permission.STATUS_BAR"/>
+ <uses-permission android:name="android.permission.CAMERA"/>
+
+ <!-- This tells the activity manager to not delay any of our activity
+ start requests, even if they happen immediately after the user
+ presses home. -->
+ <uses-permission android:name="android.permission.STOP_APP_SWITCHES"/>
+
+ <!-- Permissions needed for badger count showing on launch icon. -->
+
+ <!--for Samsung-->
+ <uses-permission android:name="com.sec.android.provider.badge.permission.READ"/>
+ <uses-permission android:name="com.sec.android.provider.badge.permission.WRITE"/>
+
+ <!--for htc-->
+ <uses-permission android:name="com.htc.launcher.permission.READ_SETTINGS"/>
+ <uses-permission android:name="com.htc.launcher.permission.UPDATE_SHORTCUT"/>
+
+ <!--for sony-->
+ <uses-permission android:name="com.sonyericsson.home.permission.BROADCAST_BADGE"/>
+ <uses-permission android:name="com.sonymobile.home.permission.PROVIDER_INSERT_BADGE"/>
+
+ <!--for apex-->
+ <uses-permission android:name="com.anddoes.launcher.permission.UPDATE_COUNT"/>
+
+ <!--for solid-->
+ <uses-permission android:name="com.majeur.launcher.permission.UPDATE_BADGE"/>
+
+ <!--for huawei-->
+ <uses-permission android:name="com.huawei.android.launcher.permission.CHANGE_BADGE"/>
+ <uses-permission android:name="com.huawei.android.launcher.permission.READ_SETTINGS"/>
+ <uses-permission android:name="com.huawei.android.launcher.permission.WRITE_SETTINGS"/>
+
+ <!--for ZUK-->
+ <uses-permission android:name="android.permission.READ_APP_BADGE"/>
+
+ <!--for OPPO-->
+ <uses-permission android:name="com.oppo.launcher.permission.READ_SETTINGS"/>
+ <uses-permission android:name="com.oppo.launcher.permission.WRITE_SETTINGS"/>
+
+ <application
+ android:backupAgent='com.android.dialer.backup.DialerBackupAgent'
+ android:fullBackupOnly="true"
+ android:restoreAnyVersion="true"
+ android:hardwareAccelerated="true"
+ android:icon="@mipmap/ic_launcher_phone"
+ android:label="@string/applicationLabel"
+ android:name="com.android.dialer.app.DialerApplication"
+ android:supportsRtl="true"
+ android:usesCleartextTraffic="false">
+
+ </application>
- <provider
- android:name=".database.VoicemailArchiveProvider"
- android:authorities="com.android.dialer.database.voicemailarchiveprovider"
- android:exported="false"
- android:multiprocess="false"
- />
- </application>
</manifest>
diff --git a/CONTRIBUTING b/CONTRIBUTING
new file mode 100644
index 000000000..2827b7d3f
--- /dev/null
+++ b/CONTRIBUTING
@@ -0,0 +1,27 @@
+Want to contribute? Great! First, read this page (including the small print at the end).
+
+### Before you contribute
+Before we can use your code, you must sign the
+[Google Individual Contributor License Agreement]
+(https://cla.developers.google.com/about/google-individual)
+(CLA), which you can do online. The CLA is necessary mainly because you own the
+copyright to your changes, even after your contribution becomes part of our
+codebase, so we need your permission to use and distribute your code. We also
+need to be sure of various other things—for instance that you'll tell us if you
+know that your code infringes on other people's patents. You don't have to sign
+the CLA until after you've submitted your code for review and a member has
+approved it, but you must do it before we can put your code into our codebase.
+Before you start working on a larger contribution, you should get in touch with
+us first through the issue tracker with your idea so that we can help out and
+possibly guide you. Coordinating up front makes it much easier to avoid
+frustration later on.
+
+### Code reviews
+All submissions, including submissions by project members, require review. We
+use Github pull requests for this purpose.
+
+### The small print
+Contributions made by corporations are covered by a different agreement than
+the one above, the
+[Software Grant and Corporate Contributor License Agreement]
+(https://cla.developers.google.com/about/google-corporate).
diff --git a/InCallUI/AndroidManifest.xml b/InCallUI/AndroidManifest.xml
deleted file mode 100644
index 5c758edaa..000000000
--- a/InCallUI/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.incallui">
- <uses-sdk
- android:minSdkVersion="23"
- android:targetSdkVersion="23" />
-</manifest>
-
diff --git a/InCallUI/build.gradle b/InCallUI/build.gradle
deleted file mode 100644
index de4725199..000000000
--- a/InCallUI/build.gradle
+++ /dev/null
@@ -1,14 +0,0 @@
-apply plugin: 'com.android.library'
-
-android {
- sourceSets.main {
- manifest.srcFile 'AndroidManifest.xml'
- res.srcDirs = ['res']
- }
-}
-
-dependencies {
- compile 'com.android.support:support-v4:23.1.+'
- compile project(':phonecommon')
- compile project(':contactscommon')
-}
diff --git a/InCallUI/proguard.flags b/InCallUI/proguard.flags
deleted file mode 100644
index 4e8310ca9..000000000
--- a/InCallUI/proguard.flags
+++ /dev/null
@@ -1,14 +0,0 @@
--keep class com.android.incallui.widget.multiwaveview.* {
- *;
-}
-
-# Keep names that are used only by animation framework.
--keepclasseswithmembers class com.android.incallui.AnimationUtils$CrossFadeDrawable {
- *** setCrossFadeAlpha(...);
-}
-
-# Any class or method annotated with NeededForTesting or NeededForReflection.
--keepclassmembers class * {
-@com.android.contacts.common.test.NeededForTesting *;
-@com.android.incallui.NeededForReflection *;
-}
diff --git a/InCallUI/res/anim/activity_open_enter.xml b/InCallUI/res/anim/activity_open_enter.xml
deleted file mode 100644
index 303b9ddc0..000000000
--- a/InCallUI/res/anim/activity_open_enter.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="top">
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:interpolator="@anim/decelerate_cubic"
- android:fillEnabled="true"
- android:fillBefore="false" android:fillAfter="true"
- android:duration="300"/>
- <scale android:fromXScale=".8" android:toXScale="1.0"
- android:fromYScale=".8" android:toYScale="1.0"
- android:pivotX="50%p" android:pivotY="50%p"
- android:interpolator="@anim/decelerate_cubic"
- android:fillEnabled="true"
- android:fillBefore="false" android:fillAfter="true"
- android:duration="300"/>
-</set> \ No newline at end of file
diff --git a/InCallUI/res/anim/activity_open_exit.xml b/InCallUI/res/anim/activity_open_exit.xml
deleted file mode 100644
index afa7c5e72..000000000
--- a/InCallUI/res/anim/activity_open_exit.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:zAdjustment="normal">
- <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:fillEnabled="true" android:fillBefore="false" android:fillAfter="true"
- android:interpolator="@anim/decelerate_quint"
- android:duration="300"/>
-</set> \ No newline at end of file
diff --git a/InCallUI/res/anim/call_status_pulse.xml b/InCallUI/res/anim/call_status_pulse.xml
deleted file mode 100644
index abda25b73..000000000
--- a/InCallUI/res/anim/call_status_pulse.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-<alpha xmlns:android="http://schemas.android.com/apk/res/android"
- android:fromAlpha="0.5"
- android:toAlpha="1"
- android:duration="600"
- android:repeatCount="infinite"
- android:repeatMode="reverse" />
diff --git a/InCallUI/res/color/selectable_icon_tint.xml b/InCallUI/res/color/selectable_icon_tint.xml
deleted file mode 100644
index b8aad1303..000000000
--- a/InCallUI/res/color/selectable_icon_tint.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@color/button_disabled_color" android:state_enabled="false" />
- <item android:color="@color/incall_accent_color" android:state_selected="true" />
- <item android:color="@color/incall_accent_color" android:state_pressed="true" />
- <item android:color="@color/button_default_color" />
-</selector>
-
diff --git a/InCallUI/res/drawable-hdpi/fab_blue.png b/InCallUI/res/drawable-hdpi/fab_blue.png
deleted file mode 100644
index 8ff3d2918..000000000
--- a/InCallUI/res/drawable-hdpi/fab_blue.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/fab_ic_call.png b/InCallUI/res/drawable-hdpi/fab_ic_call.png
deleted file mode 100644
index 548a391a6..000000000
--- a/InCallUI/res/drawable-hdpi/fab_ic_call.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/fab_ic_end_call.png b/InCallUI/res/drawable-hdpi/fab_ic_end_call.png
deleted file mode 100644
index b7f54d3bb..000000000
--- a/InCallUI/res/drawable-hdpi/fab_ic_end_call.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/fab_ic_message.png b/InCallUI/res/drawable-hdpi/fab_ic_message.png
deleted file mode 100644
index a1cf2ad82..000000000
--- a/InCallUI/res/drawable-hdpi/fab_ic_message.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/fab_red.png b/InCallUI/res/drawable-hdpi/fab_red.png
deleted file mode 100644
index 497cc7916..000000000
--- a/InCallUI/res/drawable-hdpi/fab_red.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_business_white_24dp.png b/InCallUI/res/drawable-hdpi/ic_business_white_24dp.png
deleted file mode 100644
index d10ebb766..000000000
--- a/InCallUI/res/drawable-hdpi/ic_business_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_call_white_24dp.png b/InCallUI/res/drawable-hdpi/ic_call_white_24dp.png
deleted file mode 100644
index 1902e721b..000000000
--- a/InCallUI/res/drawable-hdpi/ic_call_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_lockscreen_glowdot.png b/InCallUI/res/drawable-hdpi/ic_lockscreen_glowdot.png
deleted file mode 100644
index 983c45e2c..000000000
--- a/InCallUI/res/drawable-hdpi/ic_lockscreen_glowdot.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_question_mark.png b/InCallUI/res/drawable-hdpi/ic_question_mark.png
deleted file mode 100644
index adab6c13f..000000000
--- a/InCallUI/res/drawable-hdpi/ic_question_mark.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_toolbar_add_call.png b/InCallUI/res/drawable-hdpi/ic_toolbar_add_call.png
deleted file mode 100644
index 06603f21c..000000000
--- a/InCallUI/res/drawable-hdpi/ic_toolbar_add_call.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_toolbar_arrow_whitespace.png b/InCallUI/res/drawable-hdpi/ic_toolbar_arrow_whitespace.png
deleted file mode 100644
index ea02daad2..000000000
--- a/InCallUI/res/drawable-hdpi/ic_toolbar_arrow_whitespace.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_toolbar_audio_bluetooth.png b/InCallUI/res/drawable-hdpi/ic_toolbar_audio_bluetooth.png
deleted file mode 100644
index 05e19bc25..000000000
--- a/InCallUI/res/drawable-hdpi/ic_toolbar_audio_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_toolbar_audio_headphones.png b/InCallUI/res/drawable-hdpi/ic_toolbar_audio_headphones.png
deleted file mode 100644
index 413fdff26..000000000
--- a/InCallUI/res/drawable-hdpi/ic_toolbar_audio_headphones.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_toolbar_audio_phone.png b/InCallUI/res/drawable-hdpi/ic_toolbar_audio_phone.png
deleted file mode 100644
index 90ee1fb5f..000000000
--- a/InCallUI/res/drawable-hdpi/ic_toolbar_audio_phone.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_toolbar_dialpad.png b/InCallUI/res/drawable-hdpi/ic_toolbar_dialpad.png
deleted file mode 100644
index 69ece11be..000000000
--- a/InCallUI/res/drawable-hdpi/ic_toolbar_dialpad.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_toolbar_hold.png b/InCallUI/res/drawable-hdpi/ic_toolbar_hold.png
deleted file mode 100644
index f32d6d552..000000000
--- a/InCallUI/res/drawable-hdpi/ic_toolbar_hold.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_toolbar_merge.png b/InCallUI/res/drawable-hdpi/ic_toolbar_merge.png
deleted file mode 100644
index 2871555e4..000000000
--- a/InCallUI/res/drawable-hdpi/ic_toolbar_merge.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_toolbar_mic_off.png b/InCallUI/res/drawable-hdpi/ic_toolbar_mic_off.png
deleted file mode 100644
index b142ca869..000000000
--- a/InCallUI/res/drawable-hdpi/ic_toolbar_mic_off.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_toolbar_speaker_on.png b/InCallUI/res/drawable-hdpi/ic_toolbar_speaker_on.png
deleted file mode 100644
index c934b1344..000000000
--- a/InCallUI/res/drawable-hdpi/ic_toolbar_speaker_on.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_toolbar_swap.png b/InCallUI/res/drawable-hdpi/ic_toolbar_swap.png
deleted file mode 100644
index e673f3251..000000000
--- a/InCallUI/res/drawable-hdpi/ic_toolbar_swap.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_toolbar_video.png b/InCallUI/res/drawable-hdpi/ic_toolbar_video.png
deleted file mode 100644
index cef47aaff..000000000
--- a/InCallUI/res/drawable-hdpi/ic_toolbar_video.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_toolbar_video_off.png b/InCallUI/res/drawable-hdpi/ic_toolbar_video_off.png
deleted file mode 100644
index 968ded7d8..000000000
--- a/InCallUI/res/drawable-hdpi/ic_toolbar_video_off.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_toolbar_video_switch.png b/InCallUI/res/drawable-hdpi/ic_toolbar_video_switch.png
deleted file mode 100644
index cdd623dc0..000000000
--- a/InCallUI/res/drawable-hdpi/ic_toolbar_video_switch.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-land/rounded_call_card_background.xml b/InCallUI/res/drawable-land/rounded_call_card_background.xml
deleted file mode 100644
index f41ecda79..000000000
--- a/InCallUI/res/drawable-land/rounded_call_card_background.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ 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
- -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@color/incall_call_banner_background_color" />
- <corners
- android:radius="4dp" />
-</shape> \ No newline at end of file
diff --git a/InCallUI/res/drawable-mdpi/fab_blue.png b/InCallUI/res/drawable-mdpi/fab_blue.png
deleted file mode 100644
index 2ca6b4bdf..000000000
--- a/InCallUI/res/drawable-mdpi/fab_blue.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/fab_ic_call.png b/InCallUI/res/drawable-mdpi/fab_ic_call.png
deleted file mode 100644
index ff7b345e1..000000000
--- a/InCallUI/res/drawable-mdpi/fab_ic_call.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/fab_ic_end_call.png b/InCallUI/res/drawable-mdpi/fab_ic_end_call.png
deleted file mode 100644
index 76ce3973d..000000000
--- a/InCallUI/res/drawable-mdpi/fab_ic_end_call.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/fab_ic_message.png b/InCallUI/res/drawable-mdpi/fab_ic_message.png
deleted file mode 100644
index 74876fe77..000000000
--- a/InCallUI/res/drawable-mdpi/fab_ic_message.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/fab_red.png b/InCallUI/res/drawable-mdpi/fab_red.png
deleted file mode 100644
index c9e76a057..000000000
--- a/InCallUI/res/drawable-mdpi/fab_red.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_business_white_24dp.png b/InCallUI/res/drawable-mdpi/ic_business_white_24dp.png
deleted file mode 100644
index 7b9227c06..000000000
--- a/InCallUI/res/drawable-mdpi/ic_business_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_call_white_24dp.png b/InCallUI/res/drawable-mdpi/ic_call_white_24dp.png
deleted file mode 100644
index d4e5f5d7d..000000000
--- a/InCallUI/res/drawable-mdpi/ic_call_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_lockscreen_glowdot.png b/InCallUI/res/drawable-mdpi/ic_lockscreen_glowdot.png
deleted file mode 100644
index 056c3f175..000000000
--- a/InCallUI/res/drawable-mdpi/ic_lockscreen_glowdot.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_question_mark.png b/InCallUI/res/drawable-mdpi/ic_question_mark.png
deleted file mode 100644
index cfe64f696..000000000
--- a/InCallUI/res/drawable-mdpi/ic_question_mark.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_toolbar_add_call.png b/InCallUI/res/drawable-mdpi/ic_toolbar_add_call.png
deleted file mode 100644
index 1ee2fb1f5..000000000
--- a/InCallUI/res/drawable-mdpi/ic_toolbar_add_call.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_toolbar_arrow_whitespace.png b/InCallUI/res/drawable-mdpi/ic_toolbar_arrow_whitespace.png
deleted file mode 100644
index c39990deb..000000000
--- a/InCallUI/res/drawable-mdpi/ic_toolbar_arrow_whitespace.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_toolbar_audio_bluetooth.png b/InCallUI/res/drawable-mdpi/ic_toolbar_audio_bluetooth.png
deleted file mode 100644
index a6634ed66..000000000
--- a/InCallUI/res/drawable-mdpi/ic_toolbar_audio_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_toolbar_audio_headphones.png b/InCallUI/res/drawable-mdpi/ic_toolbar_audio_headphones.png
deleted file mode 100644
index b387e850a..000000000
--- a/InCallUI/res/drawable-mdpi/ic_toolbar_audio_headphones.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_toolbar_audio_phone.png b/InCallUI/res/drawable-mdpi/ic_toolbar_audio_phone.png
deleted file mode 100644
index b4d887cf3..000000000
--- a/InCallUI/res/drawable-mdpi/ic_toolbar_audio_phone.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_toolbar_dialpad.png b/InCallUI/res/drawable-mdpi/ic_toolbar_dialpad.png
deleted file mode 100644
index 9baa21b95..000000000
--- a/InCallUI/res/drawable-mdpi/ic_toolbar_dialpad.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_toolbar_hold.png b/InCallUI/res/drawable-mdpi/ic_toolbar_hold.png
deleted file mode 100644
index c8372738b..000000000
--- a/InCallUI/res/drawable-mdpi/ic_toolbar_hold.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_toolbar_merge.png b/InCallUI/res/drawable-mdpi/ic_toolbar_merge.png
deleted file mode 100644
index 2fba86514..000000000
--- a/InCallUI/res/drawable-mdpi/ic_toolbar_merge.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_toolbar_mic_off.png b/InCallUI/res/drawable-mdpi/ic_toolbar_mic_off.png
deleted file mode 100644
index c6b02b82c..000000000
--- a/InCallUI/res/drawable-mdpi/ic_toolbar_mic_off.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_toolbar_speaker_on.png b/InCallUI/res/drawable-mdpi/ic_toolbar_speaker_on.png
deleted file mode 100644
index 008e245f8..000000000
--- a/InCallUI/res/drawable-mdpi/ic_toolbar_speaker_on.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_toolbar_swap.png b/InCallUI/res/drawable-mdpi/ic_toolbar_swap.png
deleted file mode 100644
index acc9850d5..000000000
--- a/InCallUI/res/drawable-mdpi/ic_toolbar_swap.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_toolbar_video.png b/InCallUI/res/drawable-mdpi/ic_toolbar_video.png
deleted file mode 100644
index 3f13f9c31..000000000
--- a/InCallUI/res/drawable-mdpi/ic_toolbar_video.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_toolbar_video_off.png b/InCallUI/res/drawable-mdpi/ic_toolbar_video_off.png
deleted file mode 100644
index 64a69f2a7..000000000
--- a/InCallUI/res/drawable-mdpi/ic_toolbar_video_off.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_toolbar_video_switch.png b/InCallUI/res/drawable-mdpi/ic_toolbar_video_switch.png
deleted file mode 100644
index 6d097c9e7..000000000
--- a/InCallUI/res/drawable-mdpi/ic_toolbar_video_switch.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/fab_blue.png b/InCallUI/res/drawable-xhdpi/fab_blue.png
deleted file mode 100644
index 300b07eb4..000000000
--- a/InCallUI/res/drawable-xhdpi/fab_blue.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/fab_ic_call.png b/InCallUI/res/drawable-xhdpi/fab_ic_call.png
deleted file mode 100644
index 2bff65e0a..000000000
--- a/InCallUI/res/drawable-xhdpi/fab_ic_call.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/fab_ic_end_call.png b/InCallUI/res/drawable-xhdpi/fab_ic_end_call.png
deleted file mode 100644
index 1c95e175a..000000000
--- a/InCallUI/res/drawable-xhdpi/fab_ic_end_call.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/fab_ic_message.png b/InCallUI/res/drawable-xhdpi/fab_ic_message.png
deleted file mode 100644
index 5e3334ae0..000000000
--- a/InCallUI/res/drawable-xhdpi/fab_ic_message.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/fab_red.png b/InCallUI/res/drawable-xhdpi/fab_red.png
deleted file mode 100644
index 373e49e8f..000000000
--- a/InCallUI/res/drawable-xhdpi/fab_red.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_business_white_24dp.png b/InCallUI/res/drawable-xhdpi/ic_business_white_24dp.png
deleted file mode 100644
index e5630455a..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_business_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_call_white_24dp.png b/InCallUI/res/drawable-xhdpi/ic_call_white_24dp.png
deleted file mode 100644
index cde9cea3a..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_call_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_lockscreen_glowdot.png b/InCallUI/res/drawable-xhdpi/ic_lockscreen_glowdot.png
deleted file mode 100644
index cbd039afd..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_lockscreen_glowdot.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_question_mark.png b/InCallUI/res/drawable-xhdpi/ic_question_mark.png
deleted file mode 100644
index 8da487088..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_question_mark.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_toolbar_add_call.png b/InCallUI/res/drawable-xhdpi/ic_toolbar_add_call.png
deleted file mode 100644
index b251d6bd8..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_toolbar_add_call.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_toolbar_arrow_whitespace.png b/InCallUI/res/drawable-xhdpi/ic_toolbar_arrow_whitespace.png
deleted file mode 100644
index cdaa79d37..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_toolbar_arrow_whitespace.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_toolbar_audio_bluetooth.png b/InCallUI/res/drawable-xhdpi/ic_toolbar_audio_bluetooth.png
deleted file mode 100644
index 88f6bb945..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_toolbar_audio_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_toolbar_audio_headphones.png b/InCallUI/res/drawable-xhdpi/ic_toolbar_audio_headphones.png
deleted file mode 100644
index 1acfcafbd..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_toolbar_audio_headphones.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_toolbar_audio_phone.png b/InCallUI/res/drawable-xhdpi/ic_toolbar_audio_phone.png
deleted file mode 100644
index 0ba8f1e3e..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_toolbar_audio_phone.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_toolbar_dialpad.png b/InCallUI/res/drawable-xhdpi/ic_toolbar_dialpad.png
deleted file mode 100644
index cf803d1c1..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_toolbar_dialpad.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_toolbar_hold.png b/InCallUI/res/drawable-xhdpi/ic_toolbar_hold.png
deleted file mode 100644
index 8fecf7514..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_toolbar_hold.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_toolbar_merge.png b/InCallUI/res/drawable-xhdpi/ic_toolbar_merge.png
deleted file mode 100644
index 777483eb0..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_toolbar_merge.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_toolbar_mic_off.png b/InCallUI/res/drawable-xhdpi/ic_toolbar_mic_off.png
deleted file mode 100644
index cf2041ad6..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_toolbar_mic_off.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_toolbar_speaker_on.png b/InCallUI/res/drawable-xhdpi/ic_toolbar_speaker_on.png
deleted file mode 100644
index 5b5831cc0..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_toolbar_speaker_on.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_toolbar_swap.png b/InCallUI/res/drawable-xhdpi/ic_toolbar_swap.png
deleted file mode 100644
index 38917cb88..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_toolbar_swap.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_toolbar_video.png b/InCallUI/res/drawable-xhdpi/ic_toolbar_video.png
deleted file mode 100644
index b20f50498..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_toolbar_video.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_toolbar_video_off.png b/InCallUI/res/drawable-xhdpi/ic_toolbar_video_off.png
deleted file mode 100644
index 1b269a6a7..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_toolbar_video_off.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_toolbar_video_switch.png b/InCallUI/res/drawable-xhdpi/ic_toolbar_video_switch.png
deleted file mode 100644
index fae6bfdb1..000000000
--- a/InCallUI/res/drawable-xhdpi/ic_toolbar_video_switch.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/fab_blue.png b/InCallUI/res/drawable-xxhdpi/fab_blue.png
deleted file mode 100644
index 76d68ac6a..000000000
--- a/InCallUI/res/drawable-xxhdpi/fab_blue.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/fab_ic_call.png b/InCallUI/res/drawable-xxhdpi/fab_ic_call.png
deleted file mode 100644
index a756b95ad..000000000
--- a/InCallUI/res/drawable-xxhdpi/fab_ic_call.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/fab_ic_end_call.png b/InCallUI/res/drawable-xxhdpi/fab_ic_end_call.png
deleted file mode 100644
index 37e826402..000000000
--- a/InCallUI/res/drawable-xxhdpi/fab_ic_end_call.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/fab_ic_message.png b/InCallUI/res/drawable-xxhdpi/fab_ic_message.png
deleted file mode 100644
index 66984b1e3..000000000
--- a/InCallUI/res/drawable-xxhdpi/fab_ic_message.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/fab_red.png b/InCallUI/res/drawable-xxhdpi/fab_red.png
deleted file mode 100644
index 92eb979d5..000000000
--- a/InCallUI/res/drawable-xxhdpi/fab_red.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_business_white_24dp.png b/InCallUI/res/drawable-xxhdpi/ic_business_white_24dp.png
deleted file mode 100644
index 7dfc8dc52..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_business_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_call_white_24dp.png b/InCallUI/res/drawable-xxhdpi/ic_call_white_24dp.png
deleted file mode 100644
index b761bc466..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_call_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_lockscreen_glowdot.png b/InCallUI/res/drawable-xxhdpi/ic_lockscreen_glowdot.png
deleted file mode 100644
index c0edd91c8..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_lockscreen_glowdot.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_question_mark.png b/InCallUI/res/drawable-xxhdpi/ic_question_mark.png
deleted file mode 100644
index b9b6b00e7..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_question_mark.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_toolbar_add_call.png b/InCallUI/res/drawable-xxhdpi/ic_toolbar_add_call.png
deleted file mode 100644
index 6e343c74e..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_toolbar_add_call.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_toolbar_arrow_whitespace.png b/InCallUI/res/drawable-xxhdpi/ic_toolbar_arrow_whitespace.png
deleted file mode 100644
index 737704018..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_toolbar_arrow_whitespace.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_toolbar_audio_bluetooth.png b/InCallUI/res/drawable-xxhdpi/ic_toolbar_audio_bluetooth.png
deleted file mode 100644
index b8a385d14..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_toolbar_audio_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_toolbar_audio_headphones.png b/InCallUI/res/drawable-xxhdpi/ic_toolbar_audio_headphones.png
deleted file mode 100644
index 62d0ae331..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_toolbar_audio_headphones.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_toolbar_audio_phone.png b/InCallUI/res/drawable-xxhdpi/ic_toolbar_audio_phone.png
deleted file mode 100644
index 0e88501d6..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_toolbar_audio_phone.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_toolbar_dialpad.png b/InCallUI/res/drawable-xxhdpi/ic_toolbar_dialpad.png
deleted file mode 100644
index a754f6875..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_toolbar_dialpad.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_toolbar_hold.png b/InCallUI/res/drawable-xxhdpi/ic_toolbar_hold.png
deleted file mode 100644
index f3757a8b5..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_toolbar_hold.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_toolbar_merge.png b/InCallUI/res/drawable-xxhdpi/ic_toolbar_merge.png
deleted file mode 100644
index 5d046008c..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_toolbar_merge.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_toolbar_mic_off.png b/InCallUI/res/drawable-xxhdpi/ic_toolbar_mic_off.png
deleted file mode 100644
index ae41d5c35..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_toolbar_mic_off.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_toolbar_speaker_on.png b/InCallUI/res/drawable-xxhdpi/ic_toolbar_speaker_on.png
deleted file mode 100644
index d1bbb0947..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_toolbar_speaker_on.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_toolbar_swap.png b/InCallUI/res/drawable-xxhdpi/ic_toolbar_swap.png
deleted file mode 100644
index ea9127ee2..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_toolbar_swap.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_toolbar_video.png b/InCallUI/res/drawable-xxhdpi/ic_toolbar_video.png
deleted file mode 100644
index 5c52dd6c6..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_toolbar_video.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_toolbar_video_off.png b/InCallUI/res/drawable-xxhdpi/ic_toolbar_video_off.png
deleted file mode 100644
index 898b7c04d..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_toolbar_video_off.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_toolbar_video_switch.png b/InCallUI/res/drawable-xxhdpi/ic_toolbar_video_switch.png
deleted file mode 100644
index 4380a47ca..000000000
--- a/InCallUI/res/drawable-xxhdpi/ic_toolbar_video_switch.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/fab_blue.png b/InCallUI/res/drawable-xxxhdpi/fab_blue.png
deleted file mode 100644
index 1dd8a9260..000000000
--- a/InCallUI/res/drawable-xxxhdpi/fab_blue.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/fab_ic_end_call.png b/InCallUI/res/drawable-xxxhdpi/fab_ic_end_call.png
deleted file mode 100644
index aabdadec2..000000000
--- a/InCallUI/res/drawable-xxxhdpi/fab_ic_end_call.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/fab_ic_message.png b/InCallUI/res/drawable-xxxhdpi/fab_ic_message.png
deleted file mode 100644
index c5a108aba..000000000
--- a/InCallUI/res/drawable-xxxhdpi/fab_ic_message.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/fab_red.png b/InCallUI/res/drawable-xxxhdpi/fab_red.png
deleted file mode 100644
index f1b36f70b..000000000
--- a/InCallUI/res/drawable-xxxhdpi/fab_red.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_business_white_24dp.png b/InCallUI/res/drawable-xxxhdpi/ic_business_white_24dp.png
deleted file mode 100644
index c9aea72ce..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_business_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_question_mark.png b/InCallUI/res/drawable-xxxhdpi/ic_question_mark.png
deleted file mode 100644
index 7ba34242c..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_question_mark.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_add_call.png b/InCallUI/res/drawable-xxxhdpi/ic_toolbar_add_call.png
deleted file mode 100644
index c97e4bb15..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_add_call.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_arrow_whitespace.png b/InCallUI/res/drawable-xxxhdpi/ic_toolbar_arrow_whitespace.png
deleted file mode 100644
index 1c11c5d0f..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_arrow_whitespace.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_audio_bluetooth.png b/InCallUI/res/drawable-xxxhdpi/ic_toolbar_audio_bluetooth.png
deleted file mode 100644
index f7fa12c8b..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_audio_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_audio_headphones.png b/InCallUI/res/drawable-xxxhdpi/ic_toolbar_audio_headphones.png
deleted file mode 100644
index 8199701ce..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_audio_headphones.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_audio_phone.png b/InCallUI/res/drawable-xxxhdpi/ic_toolbar_audio_phone.png
deleted file mode 100644
index ee14ea67a..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_audio_phone.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_dialpad.png b/InCallUI/res/drawable-xxxhdpi/ic_toolbar_dialpad.png
deleted file mode 100644
index e537112fb..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_dialpad.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_hold.png b/InCallUI/res/drawable-xxxhdpi/ic_toolbar_hold.png
deleted file mode 100644
index 883d0d609..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_hold.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_merge.png b/InCallUI/res/drawable-xxxhdpi/ic_toolbar_merge.png
deleted file mode 100644
index 4b6437507..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_merge.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_mic_off.png b/InCallUI/res/drawable-xxxhdpi/ic_toolbar_mic_off.png
deleted file mode 100644
index 2d8f279da..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_mic_off.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_speaker_on.png b/InCallUI/res/drawable-xxxhdpi/ic_toolbar_speaker_on.png
deleted file mode 100644
index 0560bb262..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_speaker_on.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_swap.png b/InCallUI/res/drawable-xxxhdpi/ic_toolbar_swap.png
deleted file mode 100644
index 6f03b3f66..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_swap.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_video.png b/InCallUI/res/drawable-xxxhdpi/ic_toolbar_video.png
deleted file mode 100644
index 0797fd019..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_video.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_video_off.png b/InCallUI/res/drawable-xxxhdpi/ic_toolbar_video_off.png
deleted file mode 100644
index 63f742bef..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_video_off.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_video_switch.png b/InCallUI/res/drawable-xxxhdpi/ic_toolbar_video_switch.png
deleted file mode 100644
index 77ff73cdb..000000000
--- a/InCallUI/res/drawable-xxxhdpi/ic_toolbar_video_switch.png
+++ /dev/null
Binary files differ
diff --git a/InCallUI/res/drawable/btn_add.xml b/InCallUI/res/drawable/btn_add.xml
deleted file mode 100644
index 7d5e90f71..000000000
--- a/InCallUI/res/drawable/btn_add.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item android:id="@+id/backgroundItem"
- android:drawable="@drawable/btn_background" />
-
- <item>
- <bitmap android:src="@drawable/ic_toolbar_add_call"
- android:gravity="center"
- android:tint="@color/selectable_icon_tint" />
- </item>
-
-</layer-list> \ No newline at end of file
diff --git a/InCallUI/res/drawable/btn_background.xml b/InCallUI/res/drawable/btn_background.xml
deleted file mode 100644
index 597885803..000000000
--- a/InCallUI/res/drawable/btn_background.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<!-- Background resource for "compound buttons" in the in-call UI.
- These buttons have two states (checked and unchecked), and
- show a blue bar along the bottom edge when checked. -->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/incall_accent_color">
- <item>
- <selector>
-
- <item android:state_focused="true"
- android:drawable="@drawable/btn_unselected_focused" />
-
- <item android:drawable="@drawable/btn_unselected" />
-
- </selector>
- </item>
-</ripple>
diff --git a/InCallUI/res/drawable/btn_change_to_video.xml b/InCallUI/res/drawable/btn_change_to_video.xml
deleted file mode 100644
index a26cee3e9..000000000
--- a/InCallUI/res/drawable/btn_change_to_video.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item android:id="@+id/backgroundItem"
- android:drawable="@drawable/btn_background" />
-
- <item>
- <bitmap android:src="@drawable/ic_toolbar_video"
- android:gravity="center"
- android:tint="@color/selectable_icon_tint"
- android:autoMirrored="true" />
- </item>
-
-</layer-list> \ No newline at end of file
diff --git a/InCallUI/res/drawable/btn_change_to_voice.xml b/InCallUI/res/drawable/btn_change_to_voice.xml
deleted file mode 100644
index 86a7f21d5..000000000
--- a/InCallUI/res/drawable/btn_change_to_voice.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item android:id="@+id/backgroundItem"
- android:drawable="@drawable/btn_background" />
-
- <item>
- <bitmap android:src="@drawable/ic_toolbar_audio_phone"
- android:gravity="center"
- android:tint="@color/selectable_icon_tint"
- android:autoMirrored="true" />
- </item>
-
-</layer-list> \ No newline at end of file
diff --git a/InCallUI/res/drawable/btn_compound_audio.xml b/InCallUI/res/drawable/btn_compound_audio.xml
deleted file mode 100644
index 25a64a6ab..000000000
--- a/InCallUI/res/drawable/btn_compound_audio.xml
+++ /dev/null
@@ -1,93 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- Layers used to render the in-call "Audio mode" compound button.
-
- This is a multi-mode button:
-
- - If no bluetooth headset is connected, it behaves like a simple
- "compound button" that switches the speaker on and off. (This is why
- the button itself is a ToggleButton instance.)
-
- - But if a bluetooth headset is connected, this becomes a simple
- action button (with no concept of a "checked" state) that brings
- up a popup menu offering you a 3-way choice between earpiece /
- speaker / bluetooth.
-
- See InCallTouchUi.updateAudioButton() for the corresponding code. -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <!-- The standard "compound button" background, used to distinguish
- between the "checked" and "unchecked" states when this button is
- simply an on/off toggle for the speaker.
- (In states where the audio button *not* a toggle, we explicitly
- hide this layer.) -->
- <item android:id="@+id/compoundBackgroundItem"
- android:drawable="@drawable/btn_compound_background" />
-
- <!-- The little triangle that indicates that this isn't a plain
- button, but will instead pop up a menu. This layer is *not*
- shown when the audio button is simply an on/off toggle. -->
- <!-- Use an explicit <bitmap> to avoid scaling the icon up to the full
- size of the button. -->
- <item android:id="@+id/moreIndicatorItem">
- <bitmap android:src="@drawable/ic_toolbar_arrow_whitespace"
- android:gravity="center"
- android:tint="@color/selectable_icon_tint" />
- </item>
-
- <!-- Finally, the button icon.
-
- When the audio button is simply an on/off toggle for the speaker,
- the icon is a "speakerphone" regardless of whether the speaker is
- active. (Instead, the "on/off" indication comes from the
- btn_compound_background selector.)
-
- But when the audio button is connected to the 3-way popup menu,
- we use the button's icon to indicate the current audio mode
- (i.e. one of { earpiece (or wired headset) , speaker , bluetooth }).
-
- Here we have separate layers for each possible foreground icon,
- and in InCallTouchUi.updateAudioButton() we hide them all
- *except* the one needed for the current state. -->
-
- <!-- These all use an explicit <bitmap> to avoid scaling the icon up
- to the full size of the button. -->
-
- <!-- Bluetooth is active -->
- <item android:id="@+id/bluetoothItem">
- <bitmap android:src="@drawable/ic_toolbar_audio_bluetooth"
- android:gravity="center"
- android:tint="@color/selectable_icon_tint" />
- </item>
-
-
- <!-- Handset earpiece is active -->
- <item android:id="@+id/handsetItem">
- <bitmap android:src="@drawable/ic_toolbar_audio_phone"
- android:gravity="center"
- android:tint="@color/selectable_icon_tint" />
- </item>
-
- <!-- Speakerphone icon showing 'speaker on' state -->
- <item android:id="@+id/speakerphoneItem">
- <bitmap android:src="@drawable/ic_toolbar_speaker_on"
- android:gravity="center"
- android:tint="@color/selectable_icon_tint" />
- </item>
-
-</layer-list>
diff --git a/InCallUI/res/drawable/btn_compound_background.xml b/InCallUI/res/drawable/btn_compound_background.xml
deleted file mode 100644
index 20e2a3056..000000000
--- a/InCallUI/res/drawable/btn_compound_background.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- Background resource for "compound buttons" in the in-call UI.
- These buttons have two states (selected and unselected). -->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/incall_accent_color">
- <item>
- <selector>
- <item android:state_selected="true" android:state_focused="true"
- android:drawable="@drawable/btn_selected_focused" />
-
- <item android:state_focused="true"
- android:drawable="@drawable/btn_unselected_focused" />
-
- <item android:state_selected="true"
- android:drawable="@drawable/btn_selected" />
-
- <item android:drawable="@drawable/btn_unselected" />
- </selector>
- </item>
-</ripple>
diff --git a/InCallUI/res/drawable/btn_compound_dialpad.xml b/InCallUI/res/drawable/btn_compound_dialpad.xml
deleted file mode 100644
index 1b78ead44..000000000
--- a/InCallUI/res/drawable/btn_compound_dialpad.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- Layers used to render the in-call "Dialpad" compound button. -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <!-- The standard "compound button" background. -->
- <item android:id="@+id/compoundBackgroundItem"
- android:drawable="@drawable/btn_compound_background" />
-
- <!-- ...and the actual icon on top. Use an explicit <bitmap> to avoid scaling
- the icon up to the full size of the button. -->
- <item>
- <bitmap android:src="@drawable/ic_toolbar_dialpad"
- android:gravity="center"
- android:tint="@color/selectable_icon_tint" />
- </item>
-
-</layer-list>
diff --git a/InCallUI/res/drawable/btn_compound_hold.xml b/InCallUI/res/drawable/btn_compound_hold.xml
deleted file mode 100644
index 7974efae5..000000000
--- a/InCallUI/res/drawable/btn_compound_hold.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- Layers used to render the in-call "Hold" compound button. -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <!-- The standard "compound button" background. -->
- <item android:id="@+id/compoundBackgroundItem"
- android:drawable="@drawable/btn_compound_background" />
-
- <!-- ...and the actual icon on top. Use an explicit <bitmap> to avoid scaling
- the icon up to the full size of the button. -->
- <item>
- <bitmap android:src="@drawable/ic_toolbar_hold"
- android:gravity="center"
- android:tint="@color/selectable_icon_tint" />
- </item>
-
-</layer-list>
diff --git a/InCallUI/res/drawable/btn_compound_mute.xml b/InCallUI/res/drawable/btn_compound_mute.xml
deleted file mode 100644
index 86708fb0c..000000000
--- a/InCallUI/res/drawable/btn_compound_mute.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- Layers used to render the in-call "Mute" compound button. -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <!-- The standard "compound button" background. -->
- <item android:id="@+id/compoundBackgroundItem"
- android:drawable="@drawable/btn_compound_background" />
-
- <!-- Use an explicit <bitmap> to avoid scaling the icon up to the full size of the button. -->
- <item>
- <bitmap android:src="@drawable/ic_toolbar_mic_off"
- android:gravity="center"
- android:tint="@color/selectable_icon_tint" />
- </item>
-
-</layer-list>
diff --git a/InCallUI/res/drawable/btn_compound_video_off.xml b/InCallUI/res/drawable/btn_compound_video_off.xml
deleted file mode 100644
index b942cd0c3..000000000
--- a/InCallUI/res/drawable/btn_compound_video_off.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<!-- Layers used to render the in-call "Mute" compound button. -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <!-- The standard "compound button" background. -->
- <item android:id="@+id/compoundBackgroundItem"
- android:drawable="@drawable/btn_compound_background" />
-
- <!-- Use an explicit <bitmap> to avoid scaling the icon up to the full size of the button. -->
- <item>
- <bitmap android:src="@drawable/ic_toolbar_video_off"
- android:gravity="center"
- android:tint="@color/selectable_icon_tint"
- android:autoMirrored="true" />
- </item>
-
-</layer-list>
diff --git a/InCallUI/res/drawable/btn_compound_video_switch.xml b/InCallUI/res/drawable/btn_compound_video_switch.xml
deleted file mode 100644
index f8111866e..000000000
--- a/InCallUI/res/drawable/btn_compound_video_switch.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<!-- Layers used to render the in-call "Mute" compound button. -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <!-- The standard "compound button" background. -->
- <item android:id="@+id/compoundBackgroundItem"
- android:drawable="@drawable/btn_compound_background" />
-
- <!-- Use an explicit <bitmap> to avoid scaling the icon up to the full size of the button. -->
- <item>
- <bitmap android:src="@drawable/ic_toolbar_video_switch"
- android:gravity="center"
- android:tint="@color/selectable_icon_tint"
- android:autoMirrored="true" />
- </item>
-
-</layer-list>
diff --git a/InCallUI/res/drawable/btn_merge.xml b/InCallUI/res/drawable/btn_merge.xml
deleted file mode 100644
index 2b4818a47..000000000
--- a/InCallUI/res/drawable/btn_merge.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item android:id="@+id/backgroundItem"
- android:drawable="@drawable/btn_background" />
-
- <item>
- <bitmap android:src="@drawable/ic_toolbar_merge"
- android:gravity="center"
- android:tint="@color/selectable_icon_tint" />
- </item>
-
-</layer-list> \ No newline at end of file
diff --git a/InCallUI/res/drawable/btn_overflow.xml b/InCallUI/res/drawable/btn_overflow.xml
deleted file mode 100644
index 2eb26cc14..000000000
--- a/InCallUI/res/drawable/btn_overflow.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item android:id="@+id/backgroundItem"
- android:drawable="@drawable/btn_background" />
-
- <item>
- <bitmap android:src="@drawable/ic_overflow_menu"
- android:gravity="center"
- android:tint="@color/selectable_icon_tint" />
- </item>
-
-</layer-list> \ No newline at end of file
diff --git a/InCallUI/res/drawable/btn_selected.xml b/InCallUI/res/drawable/btn_selected.xml
deleted file mode 100644
index 1446e4163..000000000
--- a/InCallUI/res/drawable/btn_selected.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
- <item>
- <shape android:shape="oval">
- <solid android:color="@color/button_selected_color" />
- </shape>
- </item>
-</layer-list> \ No newline at end of file
diff --git a/InCallUI/res/drawable/btn_selected_focused.xml b/InCallUI/res/drawable/btn_selected_focused.xml
deleted file mode 100644
index 2eda9bf8b..000000000
--- a/InCallUI/res/drawable/btn_selected_focused.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
- <item android:drawable="@drawable/btn_selected" />
-
- <item>
- <shape android:shape="oval" >
- <stroke
- android:width="4dp"
- android:color="@color/focus_color" />
- </shape>
- </item>
-</layer-list> \ No newline at end of file
diff --git a/InCallUI/res/drawable/btn_swap.xml b/InCallUI/res/drawable/btn_swap.xml
deleted file mode 100644
index 5d6c8ecaf..000000000
--- a/InCallUI/res/drawable/btn_swap.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item android:id="@+id/backgroundItem"
- android:drawable="@drawable/btn_background" />
-
- <item>
- <bitmap android:src="@drawable/ic_toolbar_swap"
- android:gravity="center"
- android:tint="@color/selectable_icon_tint" />
- </item>
-
-</layer-list> \ No newline at end of file
diff --git a/InCallUI/res/drawable/btn_unselected.xml b/InCallUI/res/drawable/btn_unselected.xml
deleted file mode 100644
index aed995cec..000000000
--- a/InCallUI/res/drawable/btn_unselected.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
- <item>
- <shape android:shape="oval">
- <solid android:color="@color/incall_background_color" />
- </shape>
- </item>
-</layer-list> \ No newline at end of file
diff --git a/InCallUI/res/drawable/btn_unselected_focused.xml b/InCallUI/res/drawable/btn_unselected_focused.xml
deleted file mode 100644
index 66075d427..000000000
--- a/InCallUI/res/drawable/btn_unselected_focused.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
- <item android:drawable="@drawable/btn_unselected" />
- <item>
- <shape android:shape="oval" >
- <stroke
- android:width="4dp"
- android:color="@color/focus_color" />
- </shape>
- </item>
-</layer-list> \ No newline at end of file
diff --git a/InCallUI/res/drawable/conference_ripple.xml b/InCallUI/res/drawable/conference_ripple.xml
deleted file mode 100644
index 4e4a21304..000000000
--- a/InCallUI/res/drawable/conference_ripple.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/ripple_light">
- <!-- Constrain the ripple to a rectangular area -->
- <item>
- <shape android:shape="rectangle">
- <solid android:color="@color/background_dialer_white" />
- </shape>
- </item>
-</ripple> \ No newline at end of file
diff --git a/InCallUI/res/drawable/end_call_background.xml b/InCallUI/res/drawable/end_call_background.xml
deleted file mode 100644
index c43deac4f..000000000
--- a/InCallUI/res/drawable/end_call_background.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- Background drawable used to render the "end call" button. -->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/end_call_touch_feedback_tint">
- <item android:id="@android:id/mask">
- <shape android:shape="oval">
- <solid android:color="@android:color/white" />
- </shape>
- </item>
-</ripple>
diff --git a/InCallUI/res/drawable/ic_incall_audio_handle.xml b/InCallUI/res/drawable/ic_incall_audio_handle.xml
deleted file mode 100644
index 2e71a5b70..000000000
--- a/InCallUI/res/drawable/ic_incall_audio_handle.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- Touch handle for the GlowPadView widget on the incoming call screen -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-
- <!-- Audio call icon on tinted floating action bar background. -->
- <item
- android:state_enabled="true"
- android:state_active="false"
- android:state_focused="false">
- <layer-list>
- <item>
- <bitmap
- android:src="@drawable/fab_blue"
- android:tint="@color/glowpad_incoming_widget_background_tint" />
- </item>
- <item>
- <bitmap
- android:gravity="center"
- android:src="@drawable/fab_ic_call"
- android:tint="@color/glowpad_incoming_widget_tint" />
- </item>
- </layer-list>
- </item>
-
-</selector>
diff --git a/InCallUI/res/drawable/ic_incall_video_handle.xml b/InCallUI/res/drawable/ic_incall_video_handle.xml
deleted file mode 100644
index a24e305c4..000000000
--- a/InCallUI/res/drawable/ic_incall_video_handle.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<!-- Touch handle for the GlowPadView widget on the incoming call screen -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-
- <!-- Video call icon on tinted floating action bar background. -->
- <item
- android:state_enabled="true"
- android:state_active="false"
- android:state_focused="false">
- <layer-list>
- <item>
- <bitmap
- android:src="@drawable/fab_blue"
- android:tint="@color/glowpad_incoming_widget_background_tint" />
- </item>
- <item>
- <bitmap
- android:gravity="center"
- android:src="@drawable/ic_videocam"
- android:tint="@color/glowpad_incoming_widget_tint" />
- </item>
- </layer-list>
- </item>
-
-</selector>
diff --git a/InCallUI/res/drawable/ic_lockscreen_answer.xml b/InCallUI/res/drawable/ic_lockscreen_answer.xml
deleted file mode 100644
index 3184111fb..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_answer.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<!-- Used with incoming call wigdet. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:state_enabled="true" android:state_active="false" android:state_focused="false"
- android:drawable="@drawable/ic_lockscreen_answer_normal_layer"/>
- <item
- android:state_enabled="true" android:state_active="true" android:state_focused="false"
- android:drawable="@drawable/ic_lockscreen_answer_activated_layer" />
- <item
- android:state_enabled="true" android:state_active="false" android:state_focused="true"
- android:drawable="@drawable/ic_lockscreen_answer_activated_layer" />
-</selector>
diff --git a/InCallUI/res/drawable/ic_lockscreen_answer_activated_layer.xml b/InCallUI/res/drawable/ic_lockscreen_answer_activated_layer.xml
deleted file mode 100644
index f22b87e34..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_answer_activated_layer.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/fab_green"/>
- <item>
- <bitmap
- android:gravity="center"
- android:src="@drawable/fab_ic_call"
- android:tint="@color/glowpad_widget_active_color"
- android:autoMirrored="true" />
- </item>
-</layer-list>
diff --git a/InCallUI/res/drawable/ic_lockscreen_answer_normal_layer.xml b/InCallUI/res/drawable/ic_lockscreen_answer_normal_layer.xml
deleted file mode 100644
index 31b884f99..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_answer_normal_layer.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- A fake circle to fix the size of this layer asset. -->
- <item>
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
- <solid android:color="#00000000"/>
- <size
- android:width="@dimen/incoming_call_widget_circle_size"
- android:height="@dimen/incoming_call_widget_circle_size" />
- </shape>
- </item>
- <item>
- <bitmap
- android:gravity="center"
- android:src="@drawable/fab_ic_call"
- android:tint="@color/glowpad_call_widget_normal_tint"
- android:autoMirrored="true" />
- </item>
-</layer-list>
diff --git a/InCallUI/res/drawable/ic_lockscreen_answer_video.xml b/InCallUI/res/drawable/ic_lockscreen_answer_video.xml
deleted file mode 100644
index 05577979a..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_answer_video.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-<!-- Used with incoming call wigdet. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:state_enabled="true" android:state_active="false" android:state_focused="false"
- android:drawable="@drawable/ic_lockscreen_answer_video_normal_layer"/>
- <item
- android:state_enabled="true" android:state_active="true" android:state_focused="false"
- android:drawable="@drawable/ic_lockscreen_answer_video_activated_layer" />
- <item
- android:state_enabled="true" android:state_active="false" android:state_focused="true"
- android:drawable="@drawable/ic_lockscreen_answer_video_activated_layer" />
-</selector>
diff --git a/InCallUI/res/drawable/ic_lockscreen_answer_video_activated_layer.xml b/InCallUI/res/drawable/ic_lockscreen_answer_video_activated_layer.xml
deleted file mode 100644
index 7895e1b6d..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_answer_video_activated_layer.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/fab_blue" />
- <item>
- <bitmap
- android:gravity="center"
- android:src="@drawable/ic_videocam"
- android:tint="@color/glowpad_widget_active_color"
- android:autoMirrored="true" />
- </item>
-</layer-list>
diff --git a/InCallUI/res/drawable/ic_lockscreen_answer_video_normal_layer.xml b/InCallUI/res/drawable/ic_lockscreen_answer_video_normal_layer.xml
deleted file mode 100644
index 793a36e10..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_answer_video_normal_layer.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- A fake circle to fix the size of this layer asset. -->
- <item>
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
- <solid android:color="#00000000"/>
- <size
- android:width="@dimen/incoming_call_widget_circle_size"
- android:height="@dimen/incoming_call_widget_circle_size" />
- </shape>
- </item>
- <item>
- <bitmap
- android:gravity="center"
- android:src="@drawable/ic_videocam"
- android:tint="@color/glowpad_call_widget_normal_tint"
- android:autoMirrored="true" />
- </item>
-</layer-list>
diff --git a/InCallUI/res/drawable/ic_lockscreen_decline.xml b/InCallUI/res/drawable/ic_lockscreen_decline.xml
deleted file mode 100644
index 6643816d9..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_decline.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<!-- Used with incoming call wigdet. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:state_enabled="true" android:state_active="false" android:state_focused="false"
- android:drawable="@drawable/ic_lockscreen_decline_normal_layer" />
- <item
- android:state_enabled="true" android:state_active="true" android:state_focused="false"
- android:drawable="@drawable/ic_lockscreen_decline_activated_layer" />
- <item
- android:state_enabled="true" android:state_active="false" android:state_focused="true"
- android:drawable="@drawable/ic_lockscreen_decline_activated_layer" />
-</selector>
diff --git a/InCallUI/res/drawable/ic_lockscreen_decline_activated_layer.xml b/InCallUI/res/drawable/ic_lockscreen_decline_activated_layer.xml
deleted file mode 100644
index 096c32b4a..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_decline_activated_layer.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/fab_red" />
- <item>
- <bitmap
- android:gravity="center"
- android:src="@drawable/fab_ic_end_call"
- android:tint="@color/glowpad_widget_active_color" />
- </item>
-</layer-list>
diff --git a/InCallUI/res/drawable/ic_lockscreen_decline_normal_layer.xml b/InCallUI/res/drawable/ic_lockscreen_decline_normal_layer.xml
deleted file mode 100644
index 4da5f8d66..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_decline_normal_layer.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- A fake circle to fix the size of this layer asset. -->
- <item>
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
- <solid android:color="#00000000"/>
- <size
- android:width="@dimen/incoming_call_widget_circle_size"
- android:height="@dimen/incoming_call_widget_circle_size" />
- </shape>
- </item>
- <item>
- <bitmap
- android:gravity="center"
- android:src="@drawable/fab_ic_end_call"
- android:tint="@color/glowpad_end_call_widget_normal_tint" />
- </item>
-</layer-list>
diff --git a/InCallUI/res/drawable/ic_lockscreen_decline_video.xml b/InCallUI/res/drawable/ic_lockscreen_decline_video.xml
deleted file mode 100644
index cedd49757..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_decline_video.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-<!-- Used with incoming call wigdet. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:state_enabled="true" android:state_active="false" android:state_focused="false"
- android:drawable="@drawable/ic_lockscreen_decline_video_normal_layer"/>
- <item
- android:state_enabled="true" android:state_active="true" android:state_focused="false"
- android:drawable="@drawable/ic_lockscreen_decline_video_activated_layer" />
- <item
- android:state_enabled="true" android:state_active="false" android:state_focused="true"
- android:drawable="@drawable/ic_lockscreen_decline_video_activated_layer" />
-</selector>
diff --git a/InCallUI/res/drawable/ic_lockscreen_decline_video_activated_layer.xml b/InCallUI/res/drawable/ic_lockscreen_decline_video_activated_layer.xml
deleted file mode 100644
index 0790aed19..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_decline_video_activated_layer.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/fab_red" />
- <item>
- <bitmap
- android:gravity="center"
- android:src="@drawable/ic_toolbar_video_off"
- android:tint="@color/glowpad_widget_active_color"
- android:autoMirrored="true" />
- </item>
-</layer-list>
diff --git a/InCallUI/res/drawable/ic_lockscreen_decline_video_normal_layer.xml b/InCallUI/res/drawable/ic_lockscreen_decline_video_normal_layer.xml
deleted file mode 100644
index e3b89b947..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_decline_video_normal_layer.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- A fake circle to fix the size of this layer asset. -->
- <item>
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
- <solid android:color="#00000000"/>
- <size
- android:width="@dimen/incoming_call_widget_circle_size"
- android:height="@dimen/incoming_call_widget_circle_size" />
- </shape>
- </item>
- <item>
- <bitmap
- android:gravity="center"
- android:src="@drawable/ic_toolbar_video_off"
- android:tint="@color/glowpad_end_call_widget_normal_tint"
- android:autoMirrored="true" />
- </item>
-</layer-list>
diff --git a/InCallUI/res/drawable/ic_lockscreen_outerring.xml b/InCallUI/res/drawable/ic_lockscreen_outerring.xml
deleted file mode 100644
index 489515fbc..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_outerring.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval">
- <size android:height="@dimen/glowpadview_outerring_diameter"
- android:width="@dimen/glowpadview_outerring_diameter" />
- <stroke android:color="@color/glowpad_outer_ring_color" android:width="1dp" />
-</shape>
diff --git a/InCallUI/res/drawable/ic_lockscreen_text.xml b/InCallUI/res/drawable/ic_lockscreen_text.xml
deleted file mode 100644
index f9caac818..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_text.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<!-- Used with incoming call wigdet. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:state_enabled="true" android:state_active="false" android:state_focused="false"
- android:drawable="@drawable/ic_lockscreen_text_normal_layer" />
- <item
- android:state_enabled="true" android:state_active="true" android:state_focused="false"
- android:drawable="@drawable/ic_lockscreen_text_activated_layer" />
- <item
- android:state_enabled="true" android:state_active="false" android:state_focused="true"
- android:drawable="@drawable/ic_lockscreen_text_activated_layer" />
-</selector>
diff --git a/InCallUI/res/drawable/ic_lockscreen_text_activated_layer.xml b/InCallUI/res/drawable/ic_lockscreen_text_activated_layer.xml
deleted file mode 100644
index a74e36b31..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_text_activated_layer.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item>
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
- <stroke android:color="@color/glowpad_text_widget_ring_color" android:width="1dp"/>
- <size
- android:width="@dimen/incoming_call_widget_circle_size"
- android:height="@dimen/incoming_call_widget_circle_size" />
- </shape>
- </item>
- <item>
- <bitmap
- android:gravity="center"
- android:src="@drawable/fab_ic_message"
- android:tint="@color/glowpad_widget_active_color"
- android:autoMirrored="true" />
- </item>
-</layer-list>
diff --git a/InCallUI/res/drawable/ic_lockscreen_text_normal_layer.xml b/InCallUI/res/drawable/ic_lockscreen_text_normal_layer.xml
deleted file mode 100644
index be32d0baa..000000000
--- a/InCallUI/res/drawable/ic_lockscreen_text_normal_layer.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- A fake circle to fix the size of this layer asset. -->
- <item>
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
- <solid android:color="#00000000"/>
- <size
- android:width="@dimen/incoming_call_widget_circle_size"
- android:height="@dimen/incoming_call_widget_circle_size" />
- </shape>
- </item>
- <item>
- <bitmap
- android:gravity="center"
- android:src="@drawable/fab_ic_message"
- android:tint="@color/glowpad_text_widget_normal_tint"
- android:autoMirrored="true" />
- </item>
-</layer-list>
diff --git a/InCallUI/res/drawable/incoming_sms_background.xml b/InCallUI/res/drawable/incoming_sms_background.xml
deleted file mode 100644
index 81ff21c61..000000000
--- a/InCallUI/res/drawable/incoming_sms_background.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <corners
- android:topLeftRadius="@dimen/person_contact_context_message_background_main_radius"
- android:topRightRadius="@dimen/person_contact_context_message_background_main_radius"
- android:bottomRightRadius="@dimen/person_contact_context_message_background_main_radius"
- android:bottomLeftRadius="@dimen/person_contact_context_message_background_accent_radius"/>
- <solid android:color="@color/person_contact_context_message_background_color" />
-</shape>
diff --git a/InCallUI/res/drawable/outgoing_sms_background.xml b/InCallUI/res/drawable/outgoing_sms_background.xml
deleted file mode 100644
index e4f868fea..000000000
--- a/InCallUI/res/drawable/outgoing_sms_background.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <corners
- android:topLeftRadius="@dimen/person_contact_context_message_background_main_radius"
- android:topRightRadius="@dimen/person_contact_context_message_background_main_radius"
- android:bottomLeftRadius="@dimen/person_contact_context_message_background_main_radius"
- android:bottomRightRadius="@dimen/person_contact_context_message_background_accent_radius"/>
- <solid android:color="@color/person_contact_context_message_background_color" />
-</shape>
diff --git a/InCallUI/res/drawable/spam_notification_icon.xml b/InCallUI/res/drawable/spam_notification_icon.xml
deleted file mode 100644
index c8bafe085..000000000
--- a/InCallUI/res/drawable/spam_notification_icon.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ 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
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item>
- <shape android:shape="oval">
- <solid android:color="@color/blocked_contact_background" />
- <size android:width="@dimen/notification_large_icon_width"
- android:height="@dimen/notification_large_icon_height" />
- </shape>
- </item>
-
- <item android:drawable="@drawable/ic_report_white_36dp" android:gravity="center" />
-
-</layer-list> \ No newline at end of file
diff --git a/InCallUI/res/drawable/subject_bubble.xml b/InCallUI/res/drawable/subject_bubble.xml
deleted file mode 100644
index adab67833..000000000
--- a/InCallUI/res/drawable/subject_bubble.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
- ~ 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
- -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="#ffffff" />
- <padding android:left="10dp" android:top="10dp" android:right="10dp" android:bottom="10dp" />
- <corners android:topLeftRadius="6dp" android:topRightRadius="6dp"
- android:bottomLeftRadius="0dp" android:bottomRightRadius="6dp"/>
-</shape> \ No newline at end of file
diff --git a/InCallUI/res/drawable/unknown_notification_icon.xml b/InCallUI/res/drawable/unknown_notification_icon.xml
deleted file mode 100644
index 85c50752c..000000000
--- a/InCallUI/res/drawable/unknown_notification_icon.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ 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
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item>
- <shape android:shape="oval">
- <solid android:color="@color/unknown_number_color" />
- <size android:width="@dimen/notification_large_icon_width"
- android:height="@dimen/notification_large_icon_height" />
- </shape>
- </item>
-
- <item android:drawable="@drawable/ic_question_mark" android:gravity="center" />
-
-</layer-list> \ No newline at end of file
diff --git a/InCallUI/res/layout-h400dp/call_card_fragment.xml b/InCallUI/res/layout-h400dp/call_card_fragment.xml
deleted file mode 100644
index 2ef6e52da..000000000
--- a/InCallUI/res/layout-h400dp/call_card_fragment.xml
+++ /dev/null
@@ -1,172 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ 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
- -->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <!-- Primary "call card" block, for the foreground call. -->
- <LinearLayout
- android:id="@+id/primary_call_info_container"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:orientation="vertical"
- android:elevation="@dimen/primary_call_elevation"
- android:background="@color/incall_call_banner_background_color"
- android:paddingTop="@dimen/call_banner_primary_call_container_top_padding"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:gravity="bottom">
-
- <include layout="@layout/primary_call_info"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:minHeight="@dimen/call_banner_height"
- android:animateLayoutChanges="true"
- android:gravity="center" />
-
- <fragment android:name="com.android.incallui.CallButtonFragment"
- android:id="@+id/callButtonFragment"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- </LinearLayout>
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="2">
-
- <FrameLayout
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:id="@+id/call_card_content">
-
- <!-- Contact photo for primary call info -->
- <ImageView android:id="@+id/photoLarge"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"
- android:importantForAccessibility="no"
- android:background="@color/incall_photo_background_color"
- android:src="@drawable/img_no_image_automirrored" />
-
- <!-- Call context -->
- <LinearLayout
- android:id="@+id/contact_context"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:orientation="vertical"
- android:background="@color/incall_background_color"
- android:visibility="gone">
- <TextView android:id="@+id/contactContextTitle"
- android:textSize="@dimen/contact_context_title_text_size"
- android:textColor="@color/contact_context_title_text_color"
- android:fontFamily="sans-serif-medium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="@dimen/contact_context_title_margin_bottom"/>
- <ListView android:id="@+id/contactContextInfo"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:divider="@null"
- android:dividerHeight="@dimen/contact_context_list_item_padding"/>
- </LinearLayout>
- </FrameLayout>
-
- <fragment android:name="com.android.incallui.VideoCallFragment"
- android:id="@+id/videoCallFragment"
- android:layout_alignParentTop="true"
- android:layout_gravity="top|center_horizontal"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <!-- Progress spinner, useful for indicating pending operations such as upgrade to video. -->
- <FrameLayout
- android:id="@+id/progressSpinner"
- android:background="#63000000"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_centerHorizontal="true"
- android:layout_centerVertical="true"
- android:visibility="gone">
-
- <ProgressBar
- android:id="@+id/progress_bar"
- style="@android:style/Widget.Material.ProgressBar"
- android:layout_gravity="center"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:indeterminate="true" />
-
- </FrameLayout>
-
-
- <include layout="@layout/manage_conference_call_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <!-- Placeholder for various fragments that are added dynamically underneath the caller info. -->
- <FrameLayout
- android:id="@+id/answer_and_dialpad_container"
- android:layout_gravity="bottom|center_horizontal"
- android:layout_alignParentBottom="true"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:elevation="@dimen/dialpad_elevation" />
-
- <FrameLayout
- android:id="@+id/floating_end_call_action_button_container"
- android:layout_width="@dimen/end_call_floating_action_button_diameter"
- android:layout_height="@dimen/end_call_floating_action_button_diameter"
- android:background="@drawable/fab_red"
- android:layout_alignParentBottom="true"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="@dimen/end_call_button_margin_bottom">
-
- <ImageButton android:id="@+id/floating_end_call_action_button"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/end_call_background"
- android:src="@drawable/fab_ic_end_call"
- android:scaleType="center"
- android:contentDescription="@string/onscreenEndCallText" />
-
- </FrameLayout>
-
- <TextView android:id="@+id/connectionServiceMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:visibility="gone"
- android:padding="@dimen/call_banner_side_padding"
- android:background="@android:color/white" />
-
- </RelativeLayout>
-
- </LinearLayout>
- <!-- Secondary "Call info" block, for the background ("on hold") call. -->
- <include layout="@layout/secondary_call_info" />
-</RelativeLayout> \ No newline at end of file
diff --git a/InCallUI/res/layout-h600dp/manage_conference_call_button.xml b/InCallUI/res/layout-h600dp/manage_conference_call_button.xml
deleted file mode 100644
index 9a83313ac..000000000
--- a/InCallUI/res/layout-h600dp/manage_conference_call_button.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-<!-- This button is used only on GSM and IMS devices, during a conference call. -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/manage_conference_call_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/incall_banner_secondary_background_color"
- android:focusable="true"
- android:contentDescription="@string/onscreenManageConferenceText">
-
- <Space android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@color/secondary_call_info_divider_highlight_color" />
-
- <!-- This LinearLayout nested immediately in a FrameLayout is necessary to apply both a
- background color and ripple to the button. -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/secondary_call_info_horizontal_padding"
- android:paddingEnd="@dimen/secondary_call_info_horizontal_padding"
- android:paddingTop="@dimen/secondary_call_info_vertical_padding"
- android:paddingBottom="@dimen/secondary_call_info_vertical_padding"
- android:background="?android:attr/selectableItemBackground">
-
- <ImageView android:id="@+id/manageConferenceButtonImage"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_group_white_24dp"
- android:tint="@color/incall_banner_secondary_text_color"
- android:paddingEnd="16dp"
- android:importantForAccessibility="no" />
-
- <TextView android:id="@+id/manageConferenceButtonLabel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:textColor="@color/incall_banner_secondary_text_color"
- android:textSize="@dimen/secondary_call_info_text_size"
- android:text="@string/onscreenManageConferenceText"
- android:importantForAccessibility="no" />
-
- </LinearLayout>
-
-</FrameLayout>
diff --git a/InCallUI/res/layout-w500dp-land/call_card_fragment.xml b/InCallUI/res/layout-w500dp-land/call_card_fragment.xml
deleted file mode 100644
index c71cf07a6..000000000
--- a/InCallUI/res/layout-w500dp-land/call_card_fragment.xml
+++ /dev/null
@@ -1,158 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal">
-
- <LinearLayout
- android:id="@+id/primary_call_info_container"
- android:layout_centerVertical="true"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:orientation="vertical"
- android:elevation="@dimen/primary_call_elevation"
- android:background="@drawable/rounded_call_card_background"
- android:paddingTop="@dimen/call_banner_primary_call_container_top_padding"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:alpha="0.9"
- android:layout_margin="10dp">
-
- <include layout="@layout/primary_call_info" />
-
- <fragment android:name="com.android.incallui.CallButtonFragment"
- android:id="@+id/callButtonFragment"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|center_horizontal"
- android:layout_marginBottom="@dimen/call_buttons_bottom_margin" />
-
- <!-- Secondary "Call info" block, for the background ("on hold") call. -->
- <include layout="@layout/secondary_call_info"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom" />
-
- </LinearLayout>
-
- <FrameLayout
- android:layout_height="match_parent"
- android:layout_width="0dp"
- android:layout_weight="1">
-
- <FrameLayout
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:id="@+id/call_card_content">
-
- <ImageView android:id="@+id/photoLarge"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"
- android:contentDescription="@string/contactPhoto"
- android:background="@color/incall_photo_background_color"
- android:src="@drawable/img_no_image_automirrored" />
-
- <!-- Call context -->
- <LinearLayout
- android:id="@+id/contact_context"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:orientation="vertical"
- android:background="@color/incall_background_color"
- android:visibility="gone">
- <TextView android:id="@+id/contactContextTitle"
- android:textSize="@dimen/contact_context_title_text_size"
- android:textColor="@color/contact_context_title_text_color"
- android:fontFamily="sans-serif-medium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="@dimen/contact_context_title_margin_bottom" />
- <ListView android:id="@+id/contactContextInfo"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:divider="@null"
- android:dividerHeight="@dimen/contact_context_list_item_padding" />
- </LinearLayout>
-
- </FrameLayout>
-
- <include layout="@layout/manage_conference_call_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignTop="@id/photoLarge" />
-
- <!-- Progress spinner, useful for indicating pending operations such as upgrade to video. -->
- <FrameLayout
- android:id="@+id/progressSpinner"
- android:background="#63000000"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_centerHorizontal="true"
- android:layout_centerVertical="true"
- android:visibility="gone" >
-
- <ProgressBar
- android:id="@+id/progress_bar"
- style="@android:style/Widget.Material.ProgressBar"
- android:layout_gravity="center"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:indeterminate="true" />
-
- </FrameLayout>
-
- <fragment android:name="com.android.incallui.VideoCallFragment"
- android:layout_alignParentStart="true"
- android:layout_gravity="start|center_vertical"
- android:id="@+id/videoCallFragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <!-- Placeholder for the dialpad which is replaced with the dialpad fragment when shown. -->
- <FrameLayout
- android:id="@+id/answer_and_dialpad_container"
- android:layout_gravity="bottom"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <FrameLayout
- android:id="@+id/floating_end_call_action_button_container"
- android:layout_width="@dimen/end_call_floating_action_button_diameter"
- android:layout_height="@dimen/end_call_floating_action_button_diameter"
- android:background="@drawable/fab_red"
- android:layout_gravity="bottom|center_horizontal"
- android:layout_marginBottom="@dimen/end_call_button_margin_bottom">
-
- <ImageButton android:id="@+id/floating_end_call_action_button"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/end_call_background"
- android:src="@drawable/fab_ic_end_call"
- android:scaleType="center"
- android:contentDescription="@string/onscreenEndCallText" />
-
- </FrameLayout>
-
- </FrameLayout>
-
-</LinearLayout>
diff --git a/InCallUI/res/layout-w600dp-land/manage_conference_call_button.xml b/InCallUI/res/layout-w600dp-land/manage_conference_call_button.xml
deleted file mode 100644
index 9a83313ac..000000000
--- a/InCallUI/res/layout-w600dp-land/manage_conference_call_button.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-<!-- This button is used only on GSM and IMS devices, during a conference call. -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/manage_conference_call_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/incall_banner_secondary_background_color"
- android:focusable="true"
- android:contentDescription="@string/onscreenManageConferenceText">
-
- <Space android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@color/secondary_call_info_divider_highlight_color" />
-
- <!-- This LinearLayout nested immediately in a FrameLayout is necessary to apply both a
- background color and ripple to the button. -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/secondary_call_info_horizontal_padding"
- android:paddingEnd="@dimen/secondary_call_info_horizontal_padding"
- android:paddingTop="@dimen/secondary_call_info_vertical_padding"
- android:paddingBottom="@dimen/secondary_call_info_vertical_padding"
- android:background="?android:attr/selectableItemBackground">
-
- <ImageView android:id="@+id/manageConferenceButtonImage"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_group_white_24dp"
- android:tint="@color/incall_banner_secondary_text_color"
- android:paddingEnd="16dp"
- android:importantForAccessibility="no" />
-
- <TextView android:id="@+id/manageConferenceButtonLabel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:textColor="@color/incall_banner_secondary_text_color"
- android:textSize="@dimen/secondary_call_info_text_size"
- android:text="@string/onscreenManageConferenceText"
- android:importantForAccessibility="no" />
-
- </LinearLayout>
-
-</FrameLayout>
diff --git a/InCallUI/res/layout/accessible_answer_fragment.xml b/InCallUI/res/layout/accessible_answer_fragment.xml
deleted file mode 100644
index 90fe57788..000000000
--- a/InCallUI/res/layout/accessible_answer_fragment.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-~ Copyright (C) 2013 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
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center_horizontal"
- android:background="@color/glowpad_background_color">
- <RelativeLayout
- android:id="@+id/accessible_answer_fragment_answer"
- android:orientation="vertical"
- android:layout_width="120dp"
- android:layout_height="120dp"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:clickable="true"
- android:layout_alignParentRight="true"
- android:layout_centerVertical="true"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp">
- <ImageView
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:src="@drawable/ic_lockscreen_answer_activated_layer"
- android:layout_centerInParent="true">
- </ImageView>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/description_target_answer"
- android:textSize="12sp"
- android:textColor="@color/accessible_answer_hint_text_color"
- android:layout_alignParentBottom="true"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="8dp"/>
- </RelativeLayout>
- <RelativeLayout
- android:id="@+id/accessible_answer_fragment_decline"
- android:orientation="vertical"
- android:layout_width="120dp"
- android:layout_height="120dp"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:clickable="true"
- android:layout_alignParentLeft="true"
- android:layout_centerVertical="true"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp">
- <ImageView
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:src="@drawable/ic_lockscreen_decline_activated_layer"
- android:layout_centerInParent="true">
- </ImageView>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/description_target_decline"
- android:textSize="12sp"
- android:textColor="@color/accessible_answer_hint_text_color"
- android:layout_alignParentBottom="true"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="8dp"/>
- </RelativeLayout>
- <LinearLayout
- android:id="@+id/accessible_answer_fragment_text"
- android:orientation="vertical"
- android:layout_width="92dp"
- android:layout_height="92dp"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:clickable="true"
- android:layout_alignParentEnd="false"
- android:layout_alignParentStart="false"
- android:layout_above="@+id/accessible_answer_fragment_decline"
- android:layout_alignWithParentIfMissing="false"
- android:layout_alignParentTop="false"
- android:layout_alignParentLeft="false"
- android:layout_alignParentBottom="false"
- android:layout_alignParentRight="false"
- android:layout_centerHorizontal="true"
- android:contentDescription="@string/description_target_send_sms"
- android:gravity="center">
- <ImageView
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:src="@drawable/ic_lockscreen_text">
- </ImageView>
- </LinearLayout>
-
-</RelativeLayout> \ No newline at end of file
diff --git a/InCallUI/res/layout/answer_fragment.xml b/InCallUI/res/layout/answer_fragment.xml
deleted file mode 100644
index ec6ef30ac..000000000
--- a/InCallUI/res/layout/answer_fragment.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<com.android.incallui.GlowPadWrapper
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:dc="http://schemas.android.com/apk/res-auto"
- android:id="@+id/glow_pad_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:focusable="true"
- android:layout_centerHorizontal="true"
- android:gravity="center"
- android:background="@color/glowpad_background_color"
- android:layout_marginBottom="@dimen/glowpadview_margin_bottom"
-
- dc:targetDrawables="@array/incoming_call_widget_audio_with_sms_targets"
- dc:targetDescriptions="@array/incoming_call_widget_audio_with_sms_target_descriptions"
- dc:directionDescriptions="@array/incoming_call_widget_audio_with_sms_direction_descriptions"
- dc:handleDrawable="@drawable/ic_incall_audio_handle"
- dc:outerRingDrawable="@drawable/ic_lockscreen_outerring"
- dc:outerRadius="@dimen/glowpadview_target_placement_radius"
- dc:innerRadius="@dimen/glowpadview_inner_radius"
- dc:snapMargin="@dimen/glowpadview_snap_margin"
- dc:feedbackCount="1"
- dc:vibrationDuration="20"
- dc:glowRadius="@dimen/glowpadview_glow_radius"
- dc:pointDrawable="@drawable/ic_lockscreen_glowdot"
- dc:allowScaling="true" />
diff --git a/InCallUI/res/layout/business_contact_context_list_header.xml b/InCallUI/res/layout/business_contact_context_list_header.xml
deleted file mode 100644
index 90521188e..000000000
--- a/InCallUI/res/layout/business_contact_context_list_header.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:paddingEnd="@dimen/business_contact_context_end_padding">
- <ImageView android:id="@+id/icon"
- android:layout_width="@dimen/contact_context_title_image_size"
- android:layout_height="@dimen/contact_context_title_image_size"
- android:layout_marginLeft="@dimen/contact_context_title_image_side_padding"
- android:layout_marginRight="@dimen/contact_context_title_image_side_padding"
- android:layout_gravity="center"
- android:scaleType="center"
- android:src="@drawable/ic_business_white_24dp"
- android:tint="@color/business_contact_context_text_color"
- android:alpha="0.8"
- android:importantForAccessibility="no"/>
- <TextView android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/business_contact_context_title"
- android:textSize="@dimen/business_contact_context_detail_font_size"
- android:textColor="@color/business_contact_context_text_color"
- android:fontFamily="sans-serif-medium"/>
- </LinearLayout> \ No newline at end of file
diff --git a/InCallUI/res/layout/business_context_info_list_item.xml b/InCallUI/res/layout/business_context_info_list_item.xml
deleted file mode 100644
index 616d219d9..000000000
--- a/InCallUI/res/layout/business_context_info_list_item.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:paddingEnd="@dimen/business_contact_context_end_padding">
- <ImageView android:id="@+id/icon"
- android:layout_width="@dimen/business_contact_context_image_size"
- android:layout_height="@dimen/business_contact_context_image_size"
- android:layout_marginLeft="@dimen/business_contact_context_image_padding"
- android:layout_marginRight="@dimen/business_contact_context_image_padding"
- android:layout_gravity="center"
- android:scaleType="centerCrop"
- android:tint="@color/business_contact_context_text_color"
- android:alpha="0.8"/>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:orientation="vertical">
- <TextView android:id="@+id/heading"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="@dimen/business_contact_context_heading_font_size"
- android:textColor="@color/business_contact_context_text_color"
- android:fontFamily="sans-serif-medium"/>
- <TextView android:id="@+id/detail"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="@dimen/business_contact_context_detail_font_size"
- android:textColor="@color/business_contact_context_text_color"
- android:fontFamily="sans-serif-medium"/>
- </LinearLayout>
- </LinearLayout> \ No newline at end of file
diff --git a/InCallUI/res/layout/call_button_fragment.xml b/InCallUI/res/layout/call_button_fragment.xml
deleted file mode 100644
index 802e3de62..000000000
--- a/InCallUI/res/layout/call_button_fragment.xml
+++ /dev/null
@@ -1,171 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<!-- In-call onscreen touch UI elements, used on some platforms.
-
- This layout is a fullscreen overlay, drawn on top of the
- non-touch-sensitive parts of the in-call UI (i.e. the call card).
-
- The top-level View here is a InCallTouchUi (FrameLayout) with 2 children:
- (1) inCallControls: the widgets visible while a regular call (or calls) is in progress
- (2) incomingCallWidget: the UI displayed while an incoming call is ringing
- In usual cases, one of these is visible at any given moment.
- One exception is when incomingCallWidget is fading-out. At that moment, we show
- inCallControls beneath incomingCallWidget for smoother transition.
- -->
-
-<!-- Main cluster of onscreen buttons on the lower part of the screen. -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/bottomButtons"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_margin="0dp"
- android:padding="0dp"
- android:background="@color/button_background_color"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:animateLayoutChanges="true" >
-
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="bottom|center_horizontal"
- android:baselineAligned="false">
-
- <!-- This row only ever shows either 4 or 5 buttons. This may depend on whether the device
- supports "Hold" (i.e. 4 buttons on CDMA devices, 5 buttons on GSM devices.) or whether
- it is in a video call.
-
- There are a couple of *pairs* of buttons that share a single "slot", namely Hold/Swap
- and Add/Merge. For these, the code in InCallTouchUi is responsible for making sure
- that at any point exactly one of the pair is VISIBLE and the other is GONE.
-
- If there are more than 5 buttons eligible to be shown, the presenter is responsible for
- collapsing those options into an overflow menu, which appears as one of the buttons
- in the row instead. -->
-
- <!-- FAR LEFT SLOT ===================================================================== -->
-
- <!-- "Audio mode". this is a multi-mode button that can behave either like a simple
- "compound button" with two states *or* like an action button that brings up a popup
- menu; see btn_compound_audio.xml and CallButtonFragment.updateAudioButtons(). -->
- <ToggleButton android:id="@+id/audioButton"
- style="@style/InCallCompoundButton"
- android:background="@drawable/btn_compound_audio"
- android:contentDescription="@string/audio_mode_speaker" />
-
- <!-- MIDDLE LEFT SLOT ================================================================== -->
-
- <!-- "Mute" -->
- <ToggleButton android:id="@+id/muteButton"
- style="@style/InCallCompoundButton"
- android:background="@drawable/btn_compound_mute"
- android:contentDescription="@string/onscreenMuteText_unselected" />
-
- <!-- CENTER SLOT ======================================================================= -->
-
- <!-- "Dialpad" -->
- <ToggleButton android:id="@+id/dialpadButton"
- style="@style/InCallCompoundButton"
- android:background="@drawable/btn_compound_dialpad"
- android:contentDescription="@string/onscreenShowDialpadText_unselected" />
-
- <!-- MIDDLE RIGHT SLOT ================================================================= -->
-
- <!-- This slot is either "Hold" or "Swap", depending on the state of the call. One or the
- other of these must always be set to GONE. -->
-
- <!-- "Hold" -->
- <ToggleButton android:id="@+id/holdButton"
- style="@style/InCallCompoundButton"
- android:background="@drawable/btn_compound_hold"
- android:contentDescription="@string/onscreenHoldText_unselected" />
-
- <!-- "Swap" (or "Manage calls" in some CDMA states) -->
- <ImageButton android:id="@+id/swapButton"
- style="@style/InCallButton"
- android:background="@drawable/btn_swap"
- android:contentDescription="@string/onscreenSwapCallsText"
- android:visibility="gone" />
-
- <!-- "Change to video call" -->
- <ImageButton android:id="@+id/changeToVideoButton"
- style="@style/InCallButton"
- android:background="@drawable/btn_change_to_video"
- android:contentDescription="@string/onscreenVideoCallText"
- android:visibility="gone" />
-
- <!-- "Switch camera" for video calls. -->
- <ToggleButton android:id="@+id/switchCameraButton"
- style="@style/InCallCompoundButton"
- android:background="@drawable/btn_compound_video_switch"
- android:contentDescription="@string/onscreenSwitchCameraText"
- android:visibility="gone" />
-
- <!-- FAR RIGHT SLOT ==================================================================== -->
-
- <!-- This slot is either "Add" or "Merge", depending on the state of the call. One or the
- other of these must always be set to GONE. -->
-
- <!-- "Turn off camera" for video calls. -->
- <ToggleButton android:id="@+id/pauseVideoButton"
- style="@style/InCallCompoundButton"
- android:background="@drawable/btn_compound_video_off"
- android:contentDescription="@string/onscreenTurnOffCameraText"
- android:visibility="gone" />
-
- <!-- "Change to audio call" for video calls. -->
- <ImageButton android:id="@+id/changeToVoiceButton"
- style="@style/InCallButton"
- android:background="@drawable/btn_change_to_voice"
- android:contentDescription="@string/onscreenChangeToVoiceText"
- android:visibility="gone" />
-
- <!-- "Add Call" -->
- <ImageButton android:id="@+id/addButton"
- style="@style/InCallButton"
- android:background="@drawable/btn_add"
- android:contentDescription="@string/onscreenAddCallText"
- android:visibility="gone" />
-
- <!-- "Merge calls". This button is used only on GSM devices, where we know that "Add" and
- "Merge" are never available at the same time. The "Merge" button for CDMA devices is
- "cdmaMergeButton" above. -->
- <ImageButton android:id="@+id/mergeButton"
- style="@style/InCallButton"
- android:background="@drawable/btn_merge"
- android:contentDescription="@string/onscreenMergeCallsText"
- android:visibility="gone" />
-
- <!-- "Overflow" -->
- <ImageButton android:id="@+id/overflowButton"
- style="@style/InCallButton"
- android:background="@drawable/btn_overflow"
- android:contentDescription="@string/onscreenOverflowText"
- android:visibility="gone" />
-
- <!-- "Manage conference button (Video Call) " -->
- <ImageButton android:id="@+id/manageVideoCallConferenceButton"
- style="@style/InCallButton"
- android:background="@drawable/ic_group_white_24dp"
- android:contentDescription="@string/onscreenManageConferenceText"
- android:visibility="gone" />
-
- </LinearLayout>
-
-</LinearLayout>
diff --git a/InCallUI/res/layout/call_card_fragment.xml b/InCallUI/res/layout/call_card_fragment.xml
deleted file mode 100644
index fabde378a..000000000
--- a/InCallUI/res/layout/call_card_fragment.xml
+++ /dev/null
@@ -1,158 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout
- android:id="@+id/primary_call_info_container"
- android:layout_centerVertical="true"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:elevation="@dimen/primary_call_elevation"
- android:background="@color/incall_call_banner_background_color"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:alpha="0.9">
-
- <include layout="@layout/primary_call_info"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/call_banner_vertical_margin"
- android:layout_marginBottom="@dimen/call_banner_vertical_margin"/>
-
- <fragment android:name="com.android.incallui.CallButtonFragment"
- android:id="@+id/callButtonFragment"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal" />
-
- </LinearLayout>
-
- <!-- Placeholder for the dialpad which is replaced with the dialpad fragment when shown. -->
- <FrameLayout
- android:id="@+id/answer_and_dialpad_container"
- android:layout_width="match_parent"
- android:elevation="@dimen/dialpad_elevation"
- android:layout_height="match_parent" />
-
- <!-- Secondary "Call info" block, for the background ("on hold") call. -->
- <include layout="@layout/secondary_call_info"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:elevation="4dp"
- android:layout_alignParentBottom="true" />
-
- <include layout="@layout/manage_conference_call_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:elevation="5dp"
- android:layout_alignParentBottom="true"/>
-
- <FrameLayout
- android:id="@+id/floating_end_call_action_button_container"
- android:layout_width="@dimen/end_call_floating_action_button_diameter"
- android:layout_height="@dimen/end_call_floating_action_button_diameter"
- android:background="@drawable/fab_red"
- android:layout_alignParentBottom="true"
- android:layout_centerHorizontal="true">
-
- <ImageButton android:id="@+id/floating_end_call_action_button"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/end_call_background"
- android:src="@drawable/fab_ic_end_call"
- android:scaleType="center"
- android:contentDescription="@string/onscreenEndCallText" />
-
- </FrameLayout>
-
- <!-- Progress spinner, useful for indicating pending operations such as upgrade to video. -->
- <FrameLayout
- android:id="@+id/progressSpinner"
- android:background="#63000000"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_centerHorizontal="true"
- android:layout_centerVertical="true"
- android:visibility="gone" >
-
- <ProgressBar
- android:id="@+id/progress_bar"
- style="@android:style/Widget.Material.ProgressBar"
- android:layout_gravity="center"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:indeterminate="true" />
-
- </FrameLayout>
-
- <fragment android:name="com.android.incallui.VideoCallFragment"
- android:layout_alignParentStart="true"
- android:layout_gravity="start|center_vertical"
- android:id="@+id/videoCallFragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <FrameLayout
- android:layout_height="match_parent"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:visibility="gone">
-
- <FrameLayout
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:id="@+id/call_card_content">
-
- <ImageView android:id="@+id/photoLarge"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"
- android:contentDescription="@string/contactPhoto"
- android:background="@color/incall_photo_background_color"
- android:src="@drawable/img_no_image_automirrored" />
-
- <!-- Call context -->
- <LinearLayout
- android:id="@+id/contact_context"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:orientation="vertical"
- android:background="@color/incall_background_color"
- android:visibility="gone">
- <TextView android:id="@+id/contactContextTitle"
- android:textSize="@dimen/contact_context_title_text_size"
- android:textColor="@color/glowpad_background_color"
- android:fontFamily="sans-serif-medium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="@dimen/contact_context_title_margin_bottom" />
- <ListView android:id="@+id/contactContextInfo"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:divider="@null"
- android:dividerHeight="@dimen/contact_context_list_item_padding" />
- </LinearLayout>
- </FrameLayout>
- </FrameLayout>
-
-</RelativeLayout>
diff --git a/InCallUI/res/layout/caller_in_conference.xml b/InCallUI/res/layout/caller_in_conference.xml
deleted file mode 100644
index ac78096f6..000000000
--- a/InCallUI/res/layout/caller_in_conference.xml
+++ /dev/null
@@ -1,116 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="64dp"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:paddingStart="16dp"
- android:paddingEnd="8dp">
-
- <!-- Caller information -->
- <LinearLayout
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- android:gravity="center_vertical">
-
- <ImageView android:id="@+id/callerPhoto"
- android:layout_width="@dimen/contact_browser_list_item_photo_size"
- android:layout_height="@dimen/contact_browser_list_item_photo_size" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:orientation="vertical"
- android:layout_marginStart="16dp"
- android:paddingBottom="2dp">
-
- <!-- Name or number of this caller -->
- <TextView android:id="@+id/conferenceCallerName"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:textSize="16sp"
- android:textColor="@color/conference_call_manager_caller_name_text_color"
- android:singleLine="true"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginEnd="2dp" />
-
- <!-- Number of this caller if name is supplied above -->
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- android:gravity="bottom">
-
- <!-- Number -->
- <TextView
- android:id="@+id/conferenceCallerNumber"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:ellipsize="marquee"
- android:textColor="@color/conference_call_manager_secondary_text_color"
- android:singleLine="true"
- android:layout_marginEnd="8dp" />
-
- <!-- Number type -->
- <TextView
- android:id="@+id/conferenceCallerNumberType"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="12sp"
- android:textColor="@color/conference_call_manager_secondary_text_color"
- android:ellipsize="marquee"
- android:singleLine="true"
- android:textAllCaps="true"
- android:gravity="start" />
-
- </LinearLayout> <!-- End of caller number -->
-
- </LinearLayout> <!-- End of caller information -->
-
- </LinearLayout>
-
- <!-- "Separate" (i.e. "go private") button for this caller -->
- <ImageView android:id="@+id/conferenceCallerSeparate"
- android:src="@drawable/ic_call_split_white_24dp"
- android:layout_width="@dimen/conference_call_manager_button_dimension"
- android:layout_height="@dimen/conference_call_manager_button_dimension"
- android:scaleType="center"
- android:contentDescription="@string/goPrivate"
- android:tint="@color/conference_call_manager_icon_color"
- android:background="@drawable/conference_ripple"
- android:clickable="true" />
-
- <!-- "Disconnect" button which terminates the connection with this caller. -->
- <ImageButton
- android:id="@+id/conferenceCallerDisconnect"
- android:layout_width="@dimen/conference_call_manager_button_dimension"
- android:layout_height="@dimen/conference_call_manager_button_dimension"
- android:layout_marginStart="8dp"
- android:src="@drawable/ic_call_end_white_24dp"
- android:scaleType="center"
- android:contentDescription="@string/onscreenEndCallText"
- android:tint="@color/conference_call_manager_icon_color"
- android:background="@drawable/conference_ripple"
- android:clickable="true" />
-
-</LinearLayout> <!-- End of single list element -->
diff --git a/InCallUI/res/layout/conference_manager_fragment.xml b/InCallUI/res/layout/conference_manager_fragment.xml
deleted file mode 100644
index 7350bee14..000000000
--- a/InCallUI/res/layout/conference_manager_fragment.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- The "Manage conference" UI. This panel is displayed (instead of
- the inCallPanel) when the user clicks the "Manage conference"
- button while on a conference call. -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/manageConferencePanel"
- android:background="@color/conference_call_manager_background_color"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingTop="@dimen/conference_call_manager_padding_top" >
- <!-- List of conference participants. -->
- <ListView
- android:id="@+id/participantList"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:listSelector="@null"
- android:background="@color/background_dialer_white"
- android:divider="@null"
- android:focusableInTouchMode="true"
- android:focusable="true"/>
-</FrameLayout>
diff --git a/InCallUI/res/layout/incall_dialpad_fragment.xml b/InCallUI/res/layout/incall_dialpad_fragment.xml
deleted file mode 100644
index b567dbbf2..000000000
--- a/InCallUI/res/layout/incall_dialpad_fragment.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2006 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.
--->
-
-<view class="com.android.incallui.DialpadFragment$DialpadSlidingLinearLayout"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/dtmf_twelve_key_dialer_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <include layout="@layout/dialpad_view"/>
-</view>
diff --git a/InCallUI/res/layout/incall_screen.xml b/InCallUI/res/layout/incall_screen.xml
deleted file mode 100644
index 3922ea073..000000000
--- a/InCallUI/res/layout/incall_screen.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<!-- In-call Phone UI; see InCallActivity.java. -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@android:color/black"
- android:id="@+id/main" >
-</FrameLayout>
diff --git a/InCallUI/res/layout/manage_conference_call_button.xml b/InCallUI/res/layout/manage_conference_call_button.xml
deleted file mode 100644
index 01ca1bdc3..000000000
--- a/InCallUI/res/layout/manage_conference_call_button.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-
-<!-- This button is used only on GSM and IMS devices, during a conference call. -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/manage_conference_call_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:background="@color/incall_banner_secondary_background_color"
- android:visibility="gone">
-
- <Space android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@color/secondary_call_info_divider_highlight_color" />
-
- <!-- This LinearLayout nested immediately in a FrameLayout is necessary to apply both a
- background color and ripple to the button. -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- android:paddingStart="@dimen/secondary_call_info_horizontal_padding"
- android:paddingEnd="@dimen/secondary_call_info_horizontal_padding"
- android:paddingTop="@dimen/secondary_call_info_vertical_padding"
- android:paddingBottom="@dimen/secondary_call_info_vertical_padding"
- android:background="?android:attr/selectableItemBackground">
-
- <!-- Call status of the background call, usually the string "On hold". -->
- <TextView android:id="@+id/conferenceLabel"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center_vertical"
- android:paddingEnd="18dp"
- android:text="@string/onscreenConferenceText"
- android:textColor="@color/incall_banner_secondary_text_color"
- android:textSize="@dimen/secondary_call_info_text_size"
- android:singleLine="true" />
-
- <ImageView android:id="@+id/manageConferenceImage"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_group_white_24dp"
- android:tint="@color/incall_banner_secondary_text_color"
- android:paddingEnd="16dp"/>
-
- <TextView android:id="@+id/manageConferenceLabel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="@color/incall_banner_secondary_text_color"
- android:textSize="@dimen/secondary_call_info_text_size"
- android:textAlignment="viewStart"
- android:text="@string/onscreenManageText"
- android:singleLine="true"/>
-
- </LinearLayout>
-
-</FrameLayout> \ No newline at end of file
diff --git a/InCallUI/res/layout/outgoing_call_animation.xml b/InCallUI/res/layout/outgoing_call_animation.xml
deleted file mode 100644
index 69ba3d3c6..000000000
--- a/InCallUI/res/layout/outgoing_call_animation.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-<View xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/outgoing_call_animation_circle"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@color/incall_background_color" /> \ No newline at end of file
diff --git a/InCallUI/res/layout/person_context_info_list_item.xml b/InCallUI/res/layout/person_context_info_list_item.xml
deleted file mode 100644
index 4f973d564..000000000
--- a/InCallUI/res/layout/person_context_info_list_item.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingRight="@dimen/person_contact_context_horizontal_padding"
- android:paddingLeft="@dimen/person_contact_context_horizontal_padding">
- <TextView android:id="@+id/message"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="@dimen/person_contact_context_message_horizontal_padding"
- android:paddingRight="@dimen/person_contact_context_message_horizontal_padding"
- android:paddingTop="@dimen/person_contact_context_message_vertical_padding"
- android:paddingBottom="@dimen/person_contact_context_message_vertical_padding"
- android:textSize="@dimen/person_contact_context_message_text_size"
- android:textColor="@color/person_contact_context_message_text_color"
- android:fontFamily="sans-serif-medium"
- android:background="@drawable/incoming_sms_background"/>
- <TextView android:id="@+id/detail"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="@dimen/person_contact_context_detail_padding_top"
- android:textSize="@dimen/person_contact_context_detail_text_size"
- android:textColor="@color/person_contact_context_detail_text_color"
- android:fontFamily="sans-serif-medium"
- android:layout_below="@id/message"/>
-</RelativeLayout> \ No newline at end of file
diff --git a/InCallUI/res/layout/primary_call_info.xml b/InCallUI/res/layout/primary_call_info.xml
deleted file mode 100644
index cae915224..000000000
--- a/InCallUI/res/layout/primary_call_info.xml
+++ /dev/null
@@ -1,231 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<!-- "Call Banner" for primary call, the foregound or ringing call. The "call banner" is a block
- of info about a single call, including the contact name, phone number, call time counter, and
- other status info. This info is shown as a "banner" overlaid across the top of contact photo.
- -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/primary_call_banner"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:paddingStart="@dimen/call_banner_side_padding"
- android:paddingEnd="@dimen/call_banner_side_padding"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:gravity="center">
-
- <LinearLayout android:id="@+id/callSubjectLayout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:clipChildren="false"
- android:clipToPadding="false">
-
- <TextView android:id="@+id/callSubject"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAlignment="viewStart"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="@color/incall_call_banner_background_color"
- android:textSize="@dimen/call_label_text_size"
- android:background="@drawable/subject_bubble"
- android:maxLines="2"
- android:ellipsize="end"
- android:singleLine="false"
- android:visibility="gone" />
- </LinearLayout>
-
- <LinearLayout android:id="@+id/callStateButton"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:clipChildren="false"
- android:clipToPadding="false">
-
- <ImageView android:id="@+id/workProfileIcon"
- android:src="@drawable/ic_work_profile"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:layout_marginEnd="8dp"
- android:baselineAlignBottom="true"
- android:tint="@color/incall_accent_color"
- android:scaleType="center"
- android:visibility="gone" />
-
- <!-- Subscription provider or WiFi calling icon displayed to the left of the label -->
- <ImageView android:id="@+id/callStateIcon"
- android:layout_width="24dp"
- android:layout_height="match_parent"
- android:layout_marginEnd="10dp"
- android:tint="@color/incall_accent_color"
- android:alpha="0.0"
- android:scaleType="fitCenter"
- android:visibility="gone" />
-
- <ImageView android:id="@+id/videoCallIcon"
- android:src="@drawable/ic_toolbar_video"
- android:layout_width="16dp"
- android:layout_height="match_parent"
- android:layout_marginEnd="16dp"
- android:baselineAlignBottom="true"
- android:tint="@color/incall_accent_color"
- android:scaleType="center"
- android:visibility="gone" />
-
- <com.android.phone.common.widget.ResizingTextTextView
- xmlns:ex="http://schemas.android.com/apk/res-auto"
- android:id="@+id/callStateLabel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAlignment="viewStart"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:textColor="@color/incall_accent_color"
- android:textSize="@dimen/call_status_text_size"
- android:alpha="0.7"
- android:singleLine="true"
- android:gravity="start"
- android:ellipsize="end"
- ex:resizing_text_min_size="@dimen/call_status_text_min_size" />
-
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <!-- Name (or the phone number, if we don't have a name to display). -->
- <com.android.phone.common.widget.ResizingTextTextView
- xmlns:ex="http://schemas.android.com/apk/res-auto"
- android:id="@+id/name"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginTop="-5dp"
- android:fontFamily="sans-serif-light"
- android:textAlignment="viewStart"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:textSize="@dimen/call_name_text_size"
- android:singleLine="true"
- ex:resizing_text_min_size="@dimen/call_name_text_min_size" />
-
- <!-- Contact photo for primary call info -->
- <ImageView android:id="@+id/photoSmall"
- android:layout_width="@dimen/contact_context_small_photo_size"
- android:layout_height="@dimen/contact_context_small_photo_size"
- android:layout_centerVertical="true"
- android:layout_alignParentEnd="true"
- android:scaleType="centerCrop"
- android:importantForAccessibility="no"
- android:src="@drawable/img_no_image_automirrored" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:clipChildren="false"
- android:clipToPadding="false">
-
- <ImageView android:id="@+id/hdAudioIcon"
- android:src="@drawable/ic_hd_24dp"
- android:layout_width="24dp"
- android:layout_height="match_parent"
- android:layout_marginEnd="8dp"
- android:tint="@color/incall_call_banner_subtext_color"
- android:scaleType="fitCenter"
- android:visibility="gone" />
-
- <ImageView android:id="@+id/forwardIcon"
- android:src="@drawable/ic_forward_white_24dp"
- android:layout_width="24dp"
- android:layout_height="match_parent"
- android:layout_marginEnd="8dp"
- android:tint="@color/incall_call_banner_subtext_color"
- android:scaleType="fitCenter"
- android:visibility="gone" />
-
- <ImageView android:id="@+id/spamIcon"
- android:src="@drawable/ic_report_24dp"
- android:layout_width="24dp"
- android:layout_height="match_parent"
- android:layout_marginEnd="8dp"
- android:scaleType="fitCenter"
- android:visibility="gone" />
-
- <!-- Label (like "Mobile" or "Work", if present) and phone number, side by side -->
- <LinearLayout android:id="@+id/labelAndNumber"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="horizontal">
-
- <TextView android:id="@+id/label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="@color/incall_call_banner_subtext_color"
- android:textSize="@dimen/call_label_text_size"
- android:singleLine="true"
- android:textDirection="ltr"
- android:visibility="gone" />
-
- <TextView android:id="@+id/phoneNumber"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginStart="6dp"
- android:textAlignment="viewStart"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="@color/incall_call_banner_subtext_color"
- android:textSize="@dimen/call_label_text_size"
- android:singleLine="false"
- android:visibility="gone" />
-
- </LinearLayout>
-
- <!-- Elapsed time indication for a call in progress. -->
- <TextView android:id="@+id/elapsedTime"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:textAlignment="viewEnd"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="@color/incall_call_banner_subtext_color"
- android:textSize="@dimen/call_label_text_size"
- android:singleLine="true"
- android:visibility="gone" />
-
- </LinearLayout>
-
- <!-- Call type indication: a special label and/or branding
- for certain kinds of calls (like "SIP call" for a SIP call.) -->
- <TextView android:id="@+id/callTypeLabel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="@color/incall_call_banner_text_color"
- android:maxLines="1"
- android:ellipsize="end"
- android:visibility="gone" />
-
-</LinearLayout> <!-- End of call_banner -->
diff --git a/InCallUI/res/layout/secondary_call_info.xml b/InCallUI/res/layout/secondary_call_info.xml
deleted file mode 100644
index e866795a6..000000000
--- a/InCallUI/res/layout/secondary_call_info.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-
-<!-- XML resource file for secondary call info, which will be used by CallCard. -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/secondary_call_info"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:background="@color/incall_banner_secondary_background_color"
- android:visibility="gone">
-
- <Space android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@color/secondary_call_info_divider_highlight_color" />
-
- <!-- This LinearLayout nested immediately in a FrameLayout is necessary to apply both a
- background color and ripple to the button. -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- android:paddingStart="@dimen/secondary_call_info_horizontal_padding"
- android:paddingEnd="@dimen/secondary_call_info_horizontal_padding"
- android:paddingTop="@dimen/secondary_call_info_vertical_padding"
- android:paddingBottom="@dimen/secondary_call_info_vertical_padding"
- android:background="?android:attr/selectableItemBackground">
-
- <ImageView android:id="@+id/secondaryCallVideoCallIcon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_toolbar_video"
- android:tint="@color/incall_banner_secondary_text_color"
- android:paddingEnd="16dp"/>
-
- <ImageView android:id="@+id/secondaryCallConferenceCallIcon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_group_white_24dp"
- android:tint="@color/incall_banner_secondary_text_color"
- android:paddingEnd="16dp"/>
-
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical">
-
- <!-- Name (or the phone number, if we don't have a name to display). -->
- <TextView android:id="@+id/secondaryCallName"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="@color/incall_banner_secondary_text_color"
- android:textSize="@dimen/secondary_call_info_text_size"
- android:textAlignment="viewStart"
- android:ellipsize="marquee"
- android:singleLine="true"/>
-
- <!-- Provider, e.g. AT&T, that a call is associated with -->
- <LinearLayout android:id="@+id/secondary_call_provider_info"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:visibility="gone" >
-
- <TextView android:id="@+id/secondaryCallProviderLabel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="@color/incall_banner_secondary_text_color"
- android:textSize="@dimen/secondary_call_info_text_size"
- android:textAlignment="viewStart"
- android:singleLine="true"/>
-
- </LinearLayout>
-
- </LinearLayout>
-
- <!-- Call status of the background call, usually the string "On hold". -->
- <TextView android:id="@+id/secondaryCallStatus"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingEnd="18dp"
- android:text="@string/onHold"
- android:textColor="@color/incall_banner_secondary_text_color"
- android:textSize="@dimen/secondary_call_info_text_size"
- android:singleLine="true" />
-
- </LinearLayout>
-
-</FrameLayout>
diff --git a/InCallUI/res/layout/video_call_fragment.xml b/InCallUI/res/layout/video_call_fragment.xml
deleted file mode 100644
index d5e11ef4a..000000000
--- a/InCallUI/res/layout/video_call_fragment.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <ViewStub
- android:id="@+id/videoCallViewsStub"
- android:inflatedId="@+id/videoCallViews"
- android:layout="@layout/video_call_views"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-</FrameLayout> \ No newline at end of file
diff --git a/InCallUI/res/layout/video_call_views.xml b/InCallUI/res/layout/video_call_views.xml
deleted file mode 100644
index d514f6df1..000000000
--- a/InCallUI/res/layout/video_call_views.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <TextureView
- android:id="@+id/incomingVideo"
- android:layout_gravity="center"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- <!-- The width and height are replaced at runtime based on the selected camera. -->
- <FrameLayout
- android:id="@+id/previewVideoContainer"
- android:layout_width="70dp"
- android:layout_height="0dp"
- android:layout_gravity="bottom|right"
- android:layout_margin="@dimen/video_preview_margin" >
-
- <!-- The video preview surface, where the user's outgoing video is shown. -->
- <TextureView
- android:id="@+id/previewVideo"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <!-- The user's profile photo, shown when the user's camera is shut off. -->
- <ImageView
- android:id="@+id/previewProfilePhoto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerInside"
- android:adjustViewBounds="false"
- android:contentDescription="@string/profile_photo_description"
- android:background="@android:color/black"
- android:visibility="gone" />
-
- <!-- The "camera off" icon, shown when the user's camera is shut off. -->
- <ImageView
- android:id="@+id/previewCameraOff"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|right"
- android:layout_marginEnd="10dp"
- android:layout_marginBottom="10dp"
- android:scaleType="centerCrop"
- android:contentDescription="@string/camera_off_description"
- android:src="@drawable/ic_toolbar_video_off"
- android:visibility="gone" />
- </FrameLayout>
-</FrameLayout>
diff --git a/InCallUI/res/menu/incall_audio_mode_menu.xml b/InCallUI/res/menu/incall_audio_mode_menu.xml
deleted file mode 100644
index 070c1813a..000000000
--- a/InCallUI/res/menu/incall_audio_mode_menu.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 Google Inc.
-
- 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.
--->
-
-<!-- "Audio mode" popup menu for the in-call UI. -->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- TODO: Need final icon assets. Also, PopupMenu currently ignores the
- android:icon attribute anyway(!) -->
- <item android:id="@+id/audio_mode_speaker"
- android:icon="@drawable/ic_toolbar_speaker_on"
- android:title="@string/audio_mode_speaker" />
-
- <!-- We display *either* "earpiece" or "wired headset", never both,
- depending on whether a wired headset is physically plugged in
- (see InCallTouchUi.showAudioModePopup().) -->
- <item android:id="@+id/audio_mode_earpiece"
- android:icon="@drawable/ic_toolbar_audio_phone"
- android:title="@string/audio_mode_earpiece" />
-
- <item android:id="@+id/audio_mode_wired_headset"
- android:icon="@drawable/ic_toolbar_audio_headphones"
- android:title="@string/audio_mode_wired_headset" />
-
- <item android:id="@+id/audio_mode_bluetooth"
- android:icon="@drawable/ic_toolbar_audio_bluetooth"
- android:title="@string/audio_mode_bluetooth" />
-</menu>
diff --git a/InCallUI/res/values-af/strings.xml b/InCallUI/res/values-af/strings.xml
deleted file mode 100644
index 181ffce66..000000000
--- a/InCallUI/res/values-af/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Foon"</string>
- <string name="onHold" msgid="527593602772521700">"Hou aan"</string>
- <string name="unknown" msgid="3646075119047488748">"Onbekend"</string>
- <string name="private_num" msgid="6081418498487514686">"Privaat nommer"</string>
- <string name="payphone" msgid="5743050584468748607">"Telefoonhokkie"</string>
- <string name="confCall" msgid="3181961445236675173">"Konferensie-oproep"</string>
- <string name="call_lost" msgid="8208184291640961172">"Oproep is ontkoppel"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Luidspreker"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Selfoonoorfoon"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Bedraade kopfoon"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Stuur die volgende luitone?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Stuur luitone\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Stuur"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Ja"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Nee"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Vervang die plekhouerkarakter met"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konferensie-oproep <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Stemboodskapnommer"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Bel"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Bel tans weer"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konferensie-oproep"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Inkomende oproep"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Inkomende werkoproep"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Oproep beëindig"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Hou aan"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Lui af"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"In oproep"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"My nommer is <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Koppel tans video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Video-oproep"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Versoek tans video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Kan nie video-oproep koppel nie"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Videoversoek is verwerp"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Jou terugbelnommer\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Jou noodterugbelnommer\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Bel"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Gemiste oproep"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Gemiste oproepe"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> gemiste oproepe"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Gemiste oproep vanaf <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Voortdurende oproep"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Voortdurende werkoproep"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Voortdurende Wi-Fi-oproep"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Voortdurende Wi-Fi-werkoproep"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Hou aan"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Inkomende oproep"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Inkomende werkoproep"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Inkomende Wi-Fi-oproep"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Inkomende Wi-Fi-werkoproep"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Inkomende video-oproep"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Inkomende verdagte strooipos-oproep"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Inkomende videoversoek"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Nuwe stemboodskap"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Nuwe stemboodskap (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Bel <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Stemboodskapnommer is onbekend"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Geen diens nie"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Gekose netwerk (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) is nie beskikbaar nie"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Antwoord"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Lui af"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Stem"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Aanvaar"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Maak toe"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Bel terug"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Boodskap"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Voortgesette oproep op \'n ander toestel"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Sit oproep deur"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Skakel vliegtuigmodus eers af om \'n oproep te maak."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Nie geregistreer op netwerk nie."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Sellulêre netwerk is nie beskikbaar nie."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Voer \'n geldige nommer in om \'n oproep te maak."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Kan nie bel nie."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Begin tans MMI-volgorde …"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Diens word nie gesteun nie."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Kan nie oproepe wissel nie."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Kan nie oproep skei nie."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Kan nie deurskakel nie."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Kan nie konferensie-oproep maak nie."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Kan nie oproep weier nie."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Kan nie oproep(e) vrystel nie."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP-oproep"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Noodoproep"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Skakel tans radio aan …"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Geen sein nie. Probeer tans weer …"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Kan nie bel nie. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> is nie \'n noodnommer nie."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Kan nie bel nie. Bel \'n noodnommer."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Gebruik sleutelbord om te bel"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Hou oproep"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Hervat oproep"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Beëindig oproep"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Wys belblad"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Versteek belblad"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Demp"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Ontdemp"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Voeg oproep by"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Smelt oproepe saam"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Ruil"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Bestuur oproepe"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Bestuur konferensie-oproep"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferensie-oproep"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Bestuur"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Oudio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Video-oproep"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Verander na stemoproep"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Wissel kamera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Skakel kamera aan"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Skakel kamera af"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Nog opsies"</string>
- <string name="player_started" msgid="3478865572468310331">"Speler het begin"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Speler het gestop"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kamera is nie gereed nie"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kamera is gereed"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Onbekende oproepsessiegebeurtenis"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Diens"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Opstelling"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Nie gestel nie&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Ander oproepinstellings"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Bel via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Inkomend via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"kontakfoto"</string>
- <string name="goPrivate" msgid="3554069451018659483">"gaan privaat"</string>
- <string name="selectContact" msgid="92191462970821951">"kies kontak"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Skryf jou eie …"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Kanselleer"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Stuur"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Antwoord"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Stuur SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Weier"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Antwoord as video-oproep"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Antwoord as oudio-oproep"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Aanvaar videoversoek"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Weier videoversoek"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Aanvaar videoversendversoek"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Weier videoversendversoek"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Aanvaar video-ontvangversoek"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Weier video-ontvangversoek"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Gly op vir <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Gly links vir <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Gly regs vir <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Gly af vir <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibreer"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibreer"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Klank"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Verstekklank (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Foonluitoon"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibreer wanneer dit lui"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Luitoon en vibreer"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Bestuur konferensie-oproep"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Noodnommer"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profielfoto"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera is af"</string>
- <string name="child_number" msgid="4469090994612105532">"via <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Nota is gestuur"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Onlangse boodskappe"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Besigheidinligting"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> myl ver"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km ver"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> – <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Maak môre om <xliff:g id="OPEN_TIME">%s</xliff:g> oop"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Maak vandag om <xliff:g id="OPEN_TIME">%s</xliff:g> oop"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Maak om <xliff:g id="CLOSE_TIME">%s</xliff:g> toe"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Het vandag om <xliff:g id="CLOSE_TIME">%s</xliff:g> toegemaak"</string>
- <string name="open_now" msgid="4615706338669555999">"Nou oop"</string>
- <string name="closed_now" msgid="2635314668145282080">"Nou gesluit"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Verdagte strooiposbeller"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Oproep het geëindig %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Dit is die eerste keer wat hierdie nommer jou bel."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Ons vermoed dat hierdie oproep strooipos was."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blokkeer/gee strooipos aan"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Voeg kontak by"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Nie strooipos nie"</string>
-</resources>
diff --git a/InCallUI/res/values-am/strings.xml b/InCallUI/res/values-am/strings.xml
deleted file mode 100644
index 99516cfec..000000000
--- a/InCallUI/res/values-am/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"ስልክ"</string>
- <string name="onHold" msgid="527593602772521700">"ያዝና ቆይ"</string>
- <string name="unknown" msgid="3646075119047488748">"ያልታወቀ"</string>
- <string name="private_num" msgid="6081418498487514686">"የግል ቁጥር"</string>
- <string name="payphone" msgid="5743050584468748607">"የሕዝብ ስልክ"</string>
- <string name="confCall" msgid="3181961445236675173">"የስብሰባ ጥሪ"</string>
- <string name="call_lost" msgid="8208184291640961172">"ጥሪው ተቋርጧል"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"ድምፅ ማጉያ"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"የስልክ ጆሮ ማዳመጫ"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"ባለ ገመድ የጆሮ ማዳመጫ"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"ብሉቱዝ"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"የሚከተሉትን የጥሪ ድምፆች ላክ?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"የጥሪ ድምፆች በመላክ ላይ \n"</string>
- <string name="send_button" msgid="4054398309483035794">"ላክ"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"አዎ"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"አይ"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"የልቅ ምልክት ተካ በ"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"የስብሰባ ጥሪ <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"የድምፅ መልእክት ቁጥር"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"በመደወል ላይ"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"ዳግም በመደወል ላይ"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"የስብሰባ ጥሪ"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"ገቢ ጥሪ"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"ገቢ የሥራ ጥሪ"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"ጥሪ አብቅቷል"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"ያዝና ቆይ"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"በመዝጋት ላይ"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"ጥሪ ላይ"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"ቁጥሬ <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g> ነው"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"ቪድዮ በማገናኘት ላይ"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"የቪዲዮ ጥሪ"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"ቪድዮ በመጠየቅ ላይ"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"የቪዲዮ ጥሪን ማገናኘት አይቻልም"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"የቪዲዮ ጥያቄ ውድቅ ተደርጓል"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"የእርስዎ የመልሶ መደወያ ቁጥር\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"የእርስዎ የድንገተኛ አደጋ መልሶ መደወያ ቁጥር\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"በመደወል ላይ"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"ያመለጠ ጥሪ"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"ያመለጡ ጥሪዎች"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ያመለጡ ጥሪዎች"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"ያልተመለሰ ጥሪ ከ<xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"በሂደት ላይ ያለ ጥሪ"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"በሂደት ላይ ያለ የሥራ ጥሪ"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"በሂደት ላይ ያለ የWi-Fi ጥሪ"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"በሂደት ላይ ያለ የWi-Fi የሥራ ጥሪ"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"ያዝና ቆይ"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"ገቢ ጥሪ"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"ገቢ የሥራ ጥሪ"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"ገቢ የWi-Fi ጥሪ"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"ገቢ የWi-Fi የሥራ ጥሪ"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"ገቢ የቪዲዮ ጥሪ"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"መጪ የተጠረጠረ የአይፈለጌ መልዕክት ጥሪ"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"ገቢ የቪዲዮ ጥያቄ"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"አዲስ የድምፅ መልእክት"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"አዲስ የድምፅ መልእክት (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"ደውል <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"የማይታወቅ የድምፅ መልእክት ቁጥር"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"ምንም አገልግሎት የለም"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"የተመረጠ አውታረመረብ (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) አይገኝም"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"መልስ"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"ዝጋ"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"ቪዲዮ"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"ድምፅ"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"ተቀበል"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"አስወግድ"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"መልሰህ ደውል"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"መልእክት"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"በሌላ መሳሪያ ጥሪ በመካሄድ ላይ ነው"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"ጥሪ አስተላልፍ"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"ለመደወል፣ መጀመሪያ የአውሮፕላኑን ሁኔታ ያጥፉ።"</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"በአውታረ መረቡ ላይ አልተመዘገበም።"</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"የተንቀሳቃሽ ስልክ አውታረ መረብ አይገኝም።"</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"አንድ ጥሪ ለማድረግ የሚሠራ ቁጥር ያስገቡ።"</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"መደወል አይቻልም።"</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"የMMI ቅደመ ተከተል በማስጀመር ላይ…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"አገልግሎት አይደገፍም።"</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"ጥሪዎችን መቀያየር አይቻልም።"</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"ጥሪን መለየት አይቻልም።"</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"ማስተላለፍ አይቻልም።"</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"የጉባዔ ጥሪ ማድረግ አይቻልም።"</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"ጥሪውን መዝጋት አይቻልም።"</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"ጥሪ(ዎች)ን መልቀቅ አይቻልም።"</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"የSIP ጥሪ"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"የአስቸኳይ ጊዜ ጥሪ"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"ሬዲዮ በማብራት ላይ…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"ምንም አገልግሎት የለም። ዳግም በመሞከር ላይ…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"መደወል አይቻልም። <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> የአስቸኳይ አደጋ ቁጥር አይደለም።"</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"መደወል አይቻልም። ወደ የአስቸኳይ አደጋ ቁጥር ይደውሉ።"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"ለመደወል የሰሌዳ ቁልፍ ተጠቀም"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"ጥሪ አቆይ"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"ጥሪ ቀጥል"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"ጥሪ ጨርስ"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"የመደወያ ሰሌዳ አሳይ"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"የመደወያ ሰሌዳ ደብቅ"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"ድምፅ-ከል አድርግ"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"ድምፅ አታጥፋ"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"ጥሪ ያክሉ"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"ጥሪዎችን አዋህድ"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"በውዝ"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"ጥሪዎችን አደራጅ"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"የስብሰባ ስልክ ጥሪ አደራጅ"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"የስብሰባ ጥሪ"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"ያስተዳድሩ"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"ኦዲዮ"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"የቪዲዮ ጥሪ"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"ወደ ድምፅ ጥሪ ይለውጡ"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"ካሜራ ቀይር"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"ካሜራ ያብሩ"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"ካሜራ ያጥፉ"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"ተጨማሪ አማራጮች"</string>
- <string name="player_started" msgid="3478865572468310331">"አጫዋች ጀምሯል"</string>
- <string name="player_stopped" msgid="1278611664986561535">"አጫዋች ቆሟል"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"ካሜራ ዝግጁ አይደለም"</string>
- <string name="camera_ready" msgid="2614541247814590887">"ካሜራ ዝግጁ ነው"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"ያልታወቀ የጥሪ ክፍለጊዜ ክስተት"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"አገልግሎት"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"አዋቅር"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;አልተዘጋጀም&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"ሌሎች የጥሪ ቅንብሮች"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"በ<xliff:g id="PROVIDER_NAME">%s</xliff:g> በኩል በመደወል ላይ"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"በ<xliff:g id="PROVIDER_NAME">%s</xliff:g> በኩል የመጣ"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"የዕውቂያ ፎቶ"</string>
- <string name="goPrivate" msgid="3554069451018659483">"ወደ ግላዊነት ሂድ"</string>
- <string name="selectContact" msgid="92191462970821951">"ዕውቂያ ይምረጡ"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"የእራስዎን ይጻፉ..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"ተወው"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"ላክ"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"መልስ"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"ኤስኤምኤስ ላክ"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"ውድቅ አድርግ"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"እንደ ቪድዮ ጥሪ ይመልሱ"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"እንደ ድምፅ ጥሪ ይመልሱ"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"የቪዲዮ ጥያቄ ተቀበል"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"የቪዲዮ ጥያቄ ውድቅ አድርግ"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"የቪዲዮ አስተላልፍ ጥያቄን ተቀበል"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"የቪዲዮ አስተላልፍ ጥያቄን ውድቅ አድርግ"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"የቪዲዮ ተቀበል ጥያቄን ተቀበል"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"የቪዲዮ ተቀበል ጥያቄን ውድቅ አድርግ"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"ለ<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ወደ ላይ ያንሸራትቱ።"</string>
- <string name="description_direction_left" msgid="6811598791620851239">"ለ<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ወደ ግራ ያንሸራትቱ።"</string>
- <string name="description_direction_right" msgid="5461971399586296023">"ለ<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ወደ ቀኝ ያንሸራትቱ።"</string>
- <string name="description_direction_down" msgid="3331715227997561639">"ለ<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ወደ ታች ያንሸራትቱ።"</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"ንዘር"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"ንዘር"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"ድምፅ"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"ነባሪ ድምፅ (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"የስልክ ጥሪ ቅላጼ"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"በሚደወልበት ጊዜ ንዘር"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"የጥሪ ቅላጼ እና ንዘረት"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"የስብሰባ ስልክ ጥሪ አደራጅ"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"የአደጋ ጊዜ ቁጥር"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"የመገለጫ ፎቶ"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"ካሜራ ጠፍቷል"</string>
- <string name="child_number" msgid="4469090994612105532">"በ<xliff:g id="CHILD_NUMBER">%s</xliff:g> በኩል"</string>
- <string name="note_sent" msgid="7623014827902758398">"ማስታወሻ ተልኳል"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"የቅርብ ጊዜ መልእክቶች"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"የንግድ መረጃ"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> ማይል ርቀት ላይ"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> ኪሜ ርቀት ላይ"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>፣ <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>፣ <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"ነገ <xliff:g id="OPEN_TIME">%s</xliff:g> ላይ ይከፈታል"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"ዛሬ <xliff:g id="OPEN_TIME">%s</xliff:g> ላይ ይከፈታል"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g> ላይ ይዘጋል"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"ዛሬ <xliff:g id="CLOSE_TIME">%s</xliff:g> ላይ ተዘግቷል"</string>
- <string name="open_now" msgid="4615706338669555999">"አሁን ክፍት ነው"</string>
- <string name="closed_now" msgid="2635314668145282080">"አሁን ዝግ ነው"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"የተጠረጠረ አይፈለጌ ጠሪ"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"ጥሪው አብቅቷል %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"ይህ ቁጥር ለእርስዎ ሲደውል ይህ የመጀመሪያው ነው።"</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"ይህ ቁጥር አይፈለጌ ላኪ ነው ብለን እንገምታለን።"</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"አይፈለጌ አግድ/ሪፖርት አድርግ"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"እውቂያ ያክሉ"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"አይፈለጌ አይደለም"</string>
-</resources>
diff --git a/InCallUI/res/values-ar/strings.xml b/InCallUI/res/values-ar/strings.xml
deleted file mode 100644
index e89f026b6..000000000
--- a/InCallUI/res/values-ar/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"الهاتف"</string>
- <string name="onHold" msgid="527593602772521700">"معلقة"</string>
- <string name="unknown" msgid="3646075119047488748">"غير معروف"</string>
- <string name="private_num" msgid="6081418498487514686">"رقم خاص"</string>
- <string name="payphone" msgid="5743050584468748607">"هاتف بالعملة"</string>
- <string name="confCall" msgid="3181961445236675173">"مكالمة جماعية"</string>
- <string name="call_lost" msgid="8208184291640961172">"تم قطع المكالمة"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"السماعة"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"سماعة الأذن للهاتف"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"سماعة رأس سلكية"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"بلوتوث"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"هل تريد إرسال النغمات التالية؟\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"إرسال النغمات\n"</string>
- <string name="send_button" msgid="4054398309483035794">"إرسال"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"نعم"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"لا"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"استبدال حرف البدل بـ"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"مكالمة جماعية <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"رقم البريد الصوتي"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"جارٍ الطلب"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"جارٍ إعادة الطلب"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"مكالمة جماعية"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"مكالمة واردة"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"مكالمة عمل واردة"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"تم إنهاء الاتصال"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"معلقة"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"جارٍ وقف المكالمة"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"بصدد مكالمة"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"رقمي <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"جارٍ الاتصال بالفيديو"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"مكالمة فيديو"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"جارٍ طلب الفيديو"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"يتعذر الاتصال بمكالمة فيديو"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"تم رفض طلب الفيديو"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"رقم معاودة الاتصال\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"رقم معاودة اتصال الطوارئ\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"جارٍ الطلب"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"مكالمة فائتة"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"المكالمات الفائتة"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> من المكالمات الفائتة"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"مكالمة فائتة من <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"مكالمة حالية"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"مكالمة عمل جارية"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"‏مكالمة جارية عبر Wi-Fi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"‏مكالمة عمل جارية عبر Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"معلقة"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"مكالمة واردة"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"مكالمة عمل واردة"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"‏مكالمة واردة عبر Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"‏مكالمة عمل واردة عبر اتصال Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"مكالمة فيديو واردة"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"مكالمة واردة يشتبه في كونها غير مرغوب فيها"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"طلب فيديو وارد"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"بريد صوتي جديد"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"بريد صوتي جديد (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"طلب <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"رقم البريد الصوتي غير معروف"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"لا تتوفر خدمة"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"الشبكة المحددة (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) غير متاحة"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"رد"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"قطع الاتصال"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"فيديو"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"صوت"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"قبول"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"تجاهل"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"معاودة الاتصال"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"رسالة"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"مكالمة جارية على جهاز آخر"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"تحويل الاتصال"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"لإجراء مكالمة، أوقف تشغيل وضع الطائرة أولاً."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"غير مسجل على الشبكة."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"شبكة الجوّال غير متاحة."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"لإجراء مكالمة، أدخل رقمًا صالحًا."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"يتعذر الاتصال."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"‏جارٍ بدء تسلسل MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"الخدمة ليست متوفرة."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"يتعذر تبديل المكالمات."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"يتعذر فصل المكالمة."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"يتعذر النقل."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"يتعذر إجراء مكالمة جماعية."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"يتعذر رفض المكالمة."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"يتعذر تحرير المكالمات."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"‏مكالمة SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"مكالمة طوارئ"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"جارٍ تشغيل اللاسلكي..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"لا تتوفر خدمة. جارٍ إعادة المحاولة…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"يتعذر الاتصال. لا يعد <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> رقم طوارئ."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"يتعذر الاتصال. يمكنك طلب رقم طوارئ"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"استخدام لوحة المفاتيح للطلب"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"تعليق المكالمة"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"استئناف المكالمة"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"إنهاء المكالمة"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"عرض لوحة الاتصال"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"إخفاء لوحة الاتصال"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"تجاهل"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"إلغاء التجاهل"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"إضافة مكالمة"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"دمج المكالمات"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"تبديل"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"إدارة المكالمات"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"إدارة مكالمة جماعية"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"مكالمة جماعية"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"إدارة"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"صوت"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"مكالمة فيديو"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"التغيير إلى مكالمة صوتية"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"تبديل الكاميرا"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"تشغيل الكاميرا"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"إيقاف الكاميرا"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"خيارات أخرى"</string>
- <string name="player_started" msgid="3478865572468310331">"تم بدء المشغّل"</string>
- <string name="player_stopped" msgid="1278611664986561535">"تم إيقاف المشغّل"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"الكاميرا غير جاهزة"</string>
- <string name="camera_ready" msgid="2614541247814590887">"الكاميرا جاهزة"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"حدث جلسة اتصال غير معروف"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"الخدمة"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"الإعداد"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"‏&lt;لم يتم التعيين&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"إعدادات الاتصال الأخرى"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"الاتصال عبر <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"واردة عبر <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"صورة جهة الاتصال"</string>
- <string name="goPrivate" msgid="3554069451018659483">"انتقال إلى مكالمة خاصة"</string>
- <string name="selectContact" msgid="92191462970821951">"تحديد جهة اتصال"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"اكتب ردك…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"إلغاء"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"إرسال"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"الرد"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"‏إرسال رسالة قصيرة SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"الرفض"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"الرد بمكالمة فيديو"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"الرد بمكالمة صوتية"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"قبول طلب الفيديو"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"رفض طلب الفيديو"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"قبول طلب بث الفيديو"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"رفض طلب بث الفيديو"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"قبول طلب استلام مكالمة الفيديو"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"رفض طلب استلام مكالمة الفيديو"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"تمرير لأعلى لـ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"تمرير لليسار لـ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"تمرير لليمين لـ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"تمرير لأسفل لـ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"اهتزاز"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"اهتزاز"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"الصوت"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"الصوت الافتراضي (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"نغمة رنين الهاتف"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"اهتزاز عند الرنين"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"نغمة الرنين والاهتزاز"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"إدارة مكالمة جماعية"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"رقم الطوارئ"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"صورة الملف الشخصي"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"تم إيقاف الكاميرا"</string>
- <string name="child_number" msgid="4469090994612105532">"عبر <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"تم إرسال الملاحظة"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"الرسائل الأخيرة"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"معلومات النشاط التجاري"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"على بُعد <xliff:g id="DISTANCE">%.1f</xliff:g> ميل"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"على بُعد <xliff:g id="DISTANCE">%.1f</xliff:g> كم"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>، <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>، <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"مفتوح غدًا في <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"مفتوح اليوم في <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"مغلق في <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"مغلق اليوم في <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"مفتوح الآن"</string>
- <string name="closed_now" msgid="2635314668145282080">"مغلق الآن"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"اشتباه في متصل غير مرغوب فيه"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"‏المكالمة انتهت %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"هذه هي المرة الأولى التي تتلقى فيها اتصالاً من هذا الرقم."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"لدينا شك أن هذه المكالمة واردة من متصل غير مرغوب فيه."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"حظر/إبلاغ عن رقم غير مرغوب فيه"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"إضافة جهة اتصال"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"ليس رقمًا غير مرغوب فيه"</string>
-</resources>
diff --git a/InCallUI/res/values-az/strings.xml b/InCallUI/res/values-az/strings.xml
deleted file mode 100644
index 4bb9652b9..000000000
--- a/InCallUI/res/values-az/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"Gözləmə mövqeyində"</string>
- <string name="unknown" msgid="3646075119047488748">"Naməlum"</string>
- <string name="private_num" msgid="6081418498487514686">"Şəxsi nömrə"</string>
- <string name="payphone" msgid="5743050584468748607">"Taksofon"</string>
- <string name="confCall" msgid="3181961445236675173">"Konfrans zəngi"</string>
- <string name="call_lost" msgid="8208184291640961172">"Zəng bitdi"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Dinamik"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Dəstək qulaqlığı"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Simli qulaqlıq"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Aşağıdakı tonlar göndərilsin?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Tonlar göndərilir\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Göndər"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Bəli"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Xeyr"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Joker simvolları əvəz edin"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konfrans zəngi <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Səsli poçt nömrəsi"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Nömrə yığılır"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Yenidən yığır"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konfrans zəngi"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Gələn zəng"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Daxil olan iş çağrısı"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Zəng sona çatdı"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Gözləmə mövqeyində"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Dəstək asılır"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Çağrıda"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Mənim nömrəm <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Video qoşulur"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Video zəng"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Video sorğusu göndərilir"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Video zəngə qoşulmaq mümkün deyil"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Video sorğusu rədd edildi"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Cavab zəngi nömrəniz\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Təcili cavab zəngi nömrəniz\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Nömrə yığılır"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Buraxılmış zəng"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Buraxılmış zənglər"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> buraxılmış zənglər"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> tərəfindən zəng buraxılıb"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Davam edən çağrı"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Davam edən iş çağrısı"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Davam edən Wi-Fi zəngi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Davam edən Wi-Fi iş çağrısı"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Gözləmə mövqeyində"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Gələn zəng"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Daxil olan iş çağrısı"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Gələn Wi-Fi zəngi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Daxil olan Wi-Fi iş çağrısı"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Gələn video zəng"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Şübhəli spam zəngi"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Gələn video çağrı"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Yeni səsli poçt"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Yeni səsli poçt (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Yığın <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Səsli poçt nömrəsi naməlumdur"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Xidmət yoxdur"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Seçilmiş (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) şəbəkə əlçatmazdır"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Cavab"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Dəstəyi qoyun"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Videolar"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Səs"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Qəbul edin"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Rədd edin"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Geriyə zəng"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Mesaj"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Digər cihazda davam etməkdə olan zəng"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Zəngi Transfer edin"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Zəng etmək üçün ilk olaraq Uçuş Rejimini söndürün."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Şəbəkədə qeydə alınmayıb."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobil şəbəkə əlçatan deyil"</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Zəngi yerləşdirmək üçün düzgün nömrə daxil edin."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Zəng etmək mümkün deyil."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI başlanma ardıcıllığı…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Xidmət dəstəklənmir."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Zəngləri keçirmək mümkün deyil."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Zəngi ayırmaq mümkün deyil."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Ötürmək mümkün deyil."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Konfrans keçirmək mümkün deyil."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Zəngi rədd etmək mümkün deyil."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Zəngləri buraxmaq mümkün deyil."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP çağrısıs"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Təcili zəng"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Radio yandırılır ..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Xidmət yoxdur. Yenidən cəhd edilir…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Zəng etmək mümkün deyil. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> fövqəladə nömrə deyil."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Zəng etmək mümkün deyil. Fövqəladə nömrəni yı"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Nömrə yığmaq üçün klaviaturadan istifadə ediin"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Zəngi gözlədin"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Zəngə davam edin"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Zəngi bitirin"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Yığım düymələrini göstərin"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Yığım düymələrini gizlədin"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Susdurun"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Susdurmayın"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Zəng əlavə edin"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Zəngləri birləşdirin"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Dəyişdirin"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Zəngləri idarə edin"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Konfrans çağrısını idarə edin"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konfrans zəngi"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"İdarə edin"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Video zəng"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Səsli çağrıya dəyişin"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Kameraya keçin"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Kameranı yandırın"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Kameranı söndürün"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Daha çox seçim"</string>
- <string name="player_started" msgid="3478865572468310331">"Pleyer Başladıldı"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Pleyer Dayandırıldı"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kamera hazır deyil"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kamera hazırdır"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Naməlum zəng sessiyası"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Xidmət"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Quraşdırma"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Təyin edilməyib&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Digər zəng parametrləri"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> vasitəsi ilə zəng edilir"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> vasitəsilə gələn"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"kontakt fotosu"</string>
- <string name="goPrivate" msgid="3554069451018659483">"şəxsi rejimə keçin"</string>
- <string name="selectContact" msgid="92191462970821951">"kontakt seçin"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Özünüzünkünü yazın"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Ləğv edin"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Göndər"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Cavab"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS göndərin"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"İmtina edin"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Video çağrı olaraq cavab verin"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Audio çağrı olaraq cavab verin"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Video sorğusunu qəbul edin"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Video sorğusunu ləğv edin"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Video ötürmə sorğusunu qəbul edin"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Video ötürmə sorğusunu ləğv edin"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Video qəbuletmə sorğusunu qəbul edin"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Video qəbuletmə sorğusunu ləğv edin"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> üçün yuxarı sürüşdürün."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> üçün sola sürüşdürün."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> üçün sağa sürüşdürün."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> üçün aşağı sürüşdürün."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrasiya"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrasiya"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Səs"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Defolt səs (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Telefon zəng səsi"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Zəng çalanda vibrasiya olsun"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Zəng səsi &amp; Vibrasiya"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Konfrans çağrısını idarə edin"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Təcili nömrə"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profil fotosu"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera deaktivdir"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> vasitəsilə"</string>
- <string name="note_sent" msgid="7623014827902758398">"Qeyd göndərildi"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Son mesajlar"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Biznes məlumatı"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> mil uzaqlıqda"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km uzaqlıqda"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Sabah saat <xliff:g id="OPEN_TIME">%s</xliff:g> açılır"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Bu gün saat <xliff:g id="OPEN_TIME">%s</xliff:g> açılır"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Saat <xliff:g id="CLOSE_TIME">%s</xliff:g> bağlanır"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Bu gün saat <xliff:g id="CLOSE_TIME">%s</xliff:g> bağlanıb"</string>
- <string name="open_now" msgid="4615706338669555999">"İndi açın"</string>
- <string name="closed_now" msgid="2635314668145282080">"İndi bağlandı"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Şübhəli spam çağrıcısı"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Zəng bitdi %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Bu nömrə ilk dəfədir Sizə zəng edir."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Bu zəngin spam olduğundan şübhələnirik."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Spamı blok edin/bildirin"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Kontakt əlavə edin"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Spam deyil"</string>
-</resources>
diff --git a/InCallUI/res/values-b+sr+Latn/strings.xml b/InCallUI/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index 9770f90a9..000000000
--- a/InCallUI/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"Na čekanju"</string>
- <string name="unknown" msgid="3646075119047488748">"Nepoznat"</string>
- <string name="private_num" msgid="6081418498487514686">"Privatan broj"</string>
- <string name="payphone" msgid="5743050584468748607">"Telefonska govornica"</string>
- <string name="confCall" msgid="3181961445236675173">"Konferencijski poziv"</string>
- <string name="call_lost" msgid="8208184291640961172">"Poziv je prekinut"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Zvučnik"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Slušalica telefona"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Žičane naglavne slušalice"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Želite li da pošaljete sledeće tonove?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Tonovi se šalju\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Pošalji"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Da"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Ne"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Zamenite džoker znak sa"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konferencijski poziv <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Broj govorne pošte"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Poziva se"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Ponovo se bira"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konferencijski poziv"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Dolazni poziv"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Dolazni poziv za Work"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Poziv je završen"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Na čekanju"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Veza se prekida"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Poziv je u toku"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Moj broj je <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Povezuje se video poziv"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Video poziv"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Zahteva se video poziv"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Povezivanje video poziva nije uspelo"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Zahtev za video poziv je odbijen"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Broj za povratni poziv\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Broj za hitan povratni poziv\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Poziva se"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Propušten poziv"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Propušteni pozivi"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"Broj propuštenih poziva: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Propušten poziv od: <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Tekući poziv"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Tekući poziv za Work"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Tekući Wi-Fi poziv"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Tekući poziv za Work preko Wi-Fi-ja"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Na čekanju"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Dolazni poziv"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Dolazni poziv za Work"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Dolazni Wi-Fi poziv"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Dolazni poziv za Work preko Wi-Fi-ja"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Dolazni video poziv"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Sumnja na nepoželjan dolazni poziv"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Zahtev za dolazni video poziv"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Nova poruka govorne pošte"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Nova poruka govorne pošte (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Pozovi <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Nepoznat broj govorne pošte"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Mobilna mreža nije dostupna"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Izabrana mreža (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) nije dostupna"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Odgovori"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Prekini vezu"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Glasovni"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Prihvatam"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Odbaci"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Uzvrati poziv"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Pošalji SMS"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Poziv je u toku na drugom uređaju"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Prebaci poziv"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Da biste uputili poziv, prvo isključite režim rada u avionu."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Nije registrovano na mreži."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobilna mreža nije dostupna."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Da biste uputili poziv, unesite važeći broj."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Poziv nije uspeo."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Pokreće se MMI sekvenca..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Usluga nije podržana."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Zamena poziva nije uspela."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Razdvajanje poziva nije uspelo."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Prebacivanje nije uspelo."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Konferencijski poziv nije uspeo."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Odbijanje poziva nije uspelo."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Uspostavljanje poziva nije uspelo."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP poziv"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Hitni poziv"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Uključuje se radio…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Mobilna mreža nije dostupna. Pokušavamo ponovo…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Poziv nije uspeo. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> nije broj za hitne slučajeve."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Poziv nije uspeo. Pozovite broj za hitne slučajeve."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Koristite tastaturu za pozivanje"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Stavi poziv na čekanje"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Nastavi poziv"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Završi poziv"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Prikaži numeričku tastaturu"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Sakrij numeričku tastaturu"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Isključi zvuk"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Uključi zvuk"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Dodaj poziv"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Objedini pozive"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Zameni"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Upravljaj pozivima"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Upravljaj konferencijskim pozivom"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferencijski poziv"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Upravljaj"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Video poziv"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Promeni u glasovni poziv"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Promeni kameru"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Uključi kameru"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Isključi kameru"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Još opcija"</string>
- <string name="player_started" msgid="3478865572468310331">"Plejer je pokrenut"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Plejer je zaustavljen"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kamera nije spremna"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kamera je spremna"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Nepoznat događaj sesije poziva"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Usluga"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Podešavanje"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Nije podešeno&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Druga podešavanja poziva"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Poziva se preko dobavljača <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Dolazni poziv preko <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"slika kontakta"</string>
- <string name="goPrivate" msgid="3554069451018659483">"idi na privatno"</string>
- <string name="selectContact" msgid="92191462970821951">"izaberite kontakt"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Napišite sami…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Otkaži"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Pošalji"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Odgovori"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Pošalji SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Odbij"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Odgovori video pozivom"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Odgovori audio-pozivom"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Prihvati zahtev za video"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Odbij zahtev za video"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Prihvati zahtev za odlazni video poziv"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Odbij zahtev za odlazni video poziv"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Prihvati zahtev za dolazni video poziv"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Odbij zahtev za dolazni video poziv"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Prevucite nagore za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Prevucite ulevo za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Prevucite udesno za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Prevucite nadole za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibracija"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibracija"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Zvuk"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Podrazumevani zvuk (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Melodija zvona telefona"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibriraj kada zvoni"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Melodija zvona i vibracija"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Upravljaj konferencijskim pozivom"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Broj za hitne slučajeve"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Slika profila"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera je isključena"</string>
- <string name="child_number" msgid="4469090994612105532">"na <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Beleška je poslata"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Nedavne poruke"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informacije o preduzeću"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"Udaljenost je <xliff:g id="DISTANCE">%.1f</xliff:g> mi"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"Udaljenost je <xliff:g id="DISTANCE">%.1f</xliff:g> km"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>–<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Otvara se sutra u <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Otvara se danas u <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Zatvara se u <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Zatvorilo se danas u <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Trenutno otvoreno"</string>
- <string name="closed_now" msgid="2635314668145282080">"Trenutno zatvoreno"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Nepoželjan pozivalac"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Poziv se završio u %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Ovo je bio prvi poziv sa ovog broja."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Sumnjamo da je ovaj poziv nepoželjan."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blokiraj/prijavi"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Dodaj kontakt"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Nije nepoželjan"</string>
-</resources>
diff --git a/InCallUI/res/values-be/strings.xml b/InCallUI/res/values-be/strings.xml
deleted file mode 100644
index 70145fb7c..000000000
--- a/InCallUI/res/values-be/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Тэлефон"</string>
- <string name="onHold" msgid="527593602772521700">"На ўтрыманні"</string>
- <string name="unknown" msgid="3646075119047488748">"Невядомы"</string>
- <string name="private_num" msgid="6081418498487514686">"Прыватны нумар"</string>
- <string name="payphone" msgid="5743050584468748607">"Таксафон"</string>
- <string name="confCall" msgid="3181961445236675173">"Канферэнц-выклік"</string>
- <string name="call_lost" msgid="8208184291640961172">"Выклік абарваўся"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Дынамік"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Дынамік тэлефона"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Правадная гарнітура"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Адправіць гэтыя тоны?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Адпраўка тонаў\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Адправiць"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Так"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Не"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Замяніце знак падстаноўкі на"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Канферэнц-выклік у <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Нумар галасавой пошты"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Набор нумара"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Паўторны набор"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Канферэнц-выклік"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Уваходны выклік"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Уваходны выклік па працы"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Выклік скончаны"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"На ўтрыманні"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Завяршэнне выкліку"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"У выкліку"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Мой нумар - <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Падлучэнне відэа"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Відэавыклік"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Запыт на відэа"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Немагчыма падлучыць відэавыклік"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Запыт на відэа адхілены"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Ваш нумар зваротнага выкліку\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Ваш нумар экстраннага зваротнага выкліку\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Набор нумара"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Прапушчаны выклік"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Прапушчаныя выклікі"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"Прапушчаных выклікаў: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Прапушчаны выклiк ад <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Бягучы выклік"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Бягучы выклік па працы"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Бягучы выклік праз Wi-Fi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Бягучы выклік па працы праз Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"На ўтрыманні"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Уваходны выклік"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Уваходны выклік па працы"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Уваходны выклік праз Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Уваходны выклік па працы праз Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Уваходны відэавыклік"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Уваходны выклiк ад абанента, якога падазраваюць у спаме"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Уваходны запыт на відэавыклік"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Новая галасавая пошта"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Новыя паведамленнi галасавой пошты (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Набраць <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Невядомы нумар галасавой пошты"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Не абслугоўваецца"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Выбраная сетка (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) недаступная"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Адказ"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Сконч. разм."</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Відэа"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Галасавы"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Прыняць"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Адхіліць"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Звар. выклік"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Паведамленне"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Бягучы выклік на іншай прыладзе"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Перадаць выклік"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Каб зрабіць выклік, спачатку выключыце рэжым палёту."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Не зарэгістраваны ў сетцы."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Мабільная сетка недаступная."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Каб зрабіць выклік, увядзіце сапраўдны нумар."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Выклік немагчымы."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Пачатак паслядоўнасці MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Служба не падтрымліваецца."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Немагчыма пераключыць выклікі."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Немагчыма аддзяліць выклік."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Немагчыма перадаць выклік."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Немагчыма зрабіць канферэнц-выклік."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Немагчыма адхіліць выклік."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Немагчыма скончыць выклік(і)."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP-выклік"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Экстранны выклік"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Уключэнне радыё…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Не абслугоўваецца. Паўтор спробы…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Выклік немагчымы. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> не з\'яўляецца нумарам экстраннай службы."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Выклік немагчымы. Набярыце нумар экстраннай службы."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Набраць нумар з клавіятуры"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Паставіць выклік на ўтрыманне"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Узнавіць выклік"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Завяршыць выклік"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Паказаць панэль набору"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Схаваць панэль набору"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Адключыць мікрафон"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Уключыць мікрафон"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Дадаць выклік"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Аб\'яднаць выклікі"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Пераключыць"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Кіраваць выклікамі"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Кіраванне канферэнц-выклікам"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Канферэнц-выклік"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Кіраванне"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Аўдыя"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Відэавыклік"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Змяніць на галасавы выклік"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Пераключыць камеру"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Уключыць камеру"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Адключыць камеру"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Дадатковыя параметры"</string>
- <string name="player_started" msgid="3478865572468310331">"Прайгравальнік запушчаны"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Прайгравальнік спынены"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Камера не гатовая"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Камера гатовая"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Невядомая падзея сеансу выкліку"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Сэрвіс"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Наладка"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Не зададзены&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Іншыя налады выклікаў"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Выклікі праз правайдара <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Уваходны выклік праз <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"фаіаграфія кантакту"</string>
- <string name="goPrivate" msgid="3554069451018659483">"перайсці да прыватнай гаворкі"</string>
- <string name="selectContact" msgid="92191462970821951">"выбраць кантакт"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Напiшыце сваё…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Скасаваць"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Адправiць"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Адказ"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Адправiць SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Адхіліць"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Адказаць відэавыклікам"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Адказаць аўдыявыклікам"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Прыняць запыт на відэа"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Адхіліць запыт на відэа"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Прыняць запыт на перадачу відэа"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Адхіліць запыт на перадачу відэа"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Прыняць запыт на атрыманне відэа"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Адхіліць запыт на атрыманне відэа"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Правядзіце пальцам уверх, каб <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Правядзіце пальцам улева, каб <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Правядзіце пальцам управа, каб <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Правядзіце пальцам уніз, каб <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Вібрацыя"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Вібрацыя"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Гук"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Стандартны гук (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Рынгтон тэлефона"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Вібрацыя падчас званка"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Рынгтон і вiбрацыя"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Кіраванне канферэнц-выклікам"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Нумар экстраннай службы"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Фота профілю"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Камера адключана"</string>
- <string name="child_number" msgid="4469090994612105532">"праз <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Нататка адпраўлена"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Апошнія паведамленні"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Бізнес-інфармацыя"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"Адлеглаць у мілях: <xliff:g id="DISTANCE">%.1f</xliff:g>"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"Адлегласць <xliff:g id="DISTANCE">%.1f</xliff:g> км"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Адкрываецца заўтра ў <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Адкрываецца сёння ў <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Закрываецца ў <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Закрыта сёння ў <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Адкрыць зараз"</string>
- <string name="closed_now" msgid="2635314668145282080">"Зараз закрыта"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Падазраваецца ў спаме"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Выклік завершаны %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Вы атрымліваеце выклік з гэтага нумара ўпершыню."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Існуе падазрэнне, што гэты выклік – спамерскі."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Забл./павед.пра спам"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Дадаць кантакт"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Не спам"</string>
-</resources>
diff --git a/InCallUI/res/values-bg/strings.xml b/InCallUI/res/values-bg/strings.xml
deleted file mode 100644
index adf504777..000000000
--- a/InCallUI/res/values-bg/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Телефон"</string>
- <string name="onHold" msgid="527593602772521700">"Задържано"</string>
- <string name="unknown" msgid="3646075119047488748">"Неизвестно лице"</string>
- <string name="private_num" msgid="6081418498487514686">"Частен номер"</string>
- <string name="payphone" msgid="5743050584468748607">"Обществен телефон"</string>
- <string name="confCall" msgid="3181961445236675173">"Конферентно обаждане"</string>
- <string name="call_lost" msgid="8208184291640961172">"Обаждането бе прекъснато"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Високоговорител"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Телефонна слушалка"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Слушалки с кабел"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Да се изпратят ли следните мелодии? \n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Мелодиите се изпращат\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Изпращане"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Да"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Не"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Замяна на заместващия символ със:"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Конферентно обаждане – <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Номер за гласова поща"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Набира се"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Набира се отново"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Конферентно обаждане"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Входящо обаждане"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Входящо служебно обаждане"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Обаждането завърши"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Задържано"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Разговорът се приключва"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Извършва се обаждане"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Номерът ми е <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Установява се видеовръзка"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Видеообаждане"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Заявява се видеовръзка"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Видеообаждането не може да се осъществи"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Заявката за видеовръзка е отхвърлена"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Номерът ви за обратно обаждане\n– <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Номерът ви за спешно обратно обаждане\n– <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Набиране"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Пропуснато обаждане"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Пропуснати обаждания"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> пропуснати обаждания"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Пропуснато обаждане от <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Текущо обаждане"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Текущо служебно обаждане"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Текущо обаждане през Wi-Fi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Текущо служебно обаждане през Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Задържано"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Входящо обаждане"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Входящо служебно обаждане"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Входящо обаждане през Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Входящо служебно обаждане през Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Входящо видеообаждане"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Входящо обаждане – възможен спам"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Заявка за входящо видеообаждане"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Нова гласова поща"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Нова гласова поща (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Набиране на <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Неизвестен номер за гласова поща"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Няма покритие"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Избраната мрежа (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) не е налице"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Приемане"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Затваряне"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Видеообажд."</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Гл. обаждане"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Приемане"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Отхвърляне"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Обр. обажд."</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Съобщение"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Текущо обаждане на друго устройство"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Прехвърляне на обаждането"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"За да осъществите обаждане, първо изключете самолетния режим."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Няма регистрация в мрежата."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Няма достъп до клетъчната мрежа."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"За да извършите обаждане, въведете валиден номер."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Не може да се извърши обаждане."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Стартира се последователността MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Услугата не се поддържа."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Обажданията не могат да се превключат."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Обаждането не може да се отдели."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Не може да се прехвърли."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Не може да се извърши конферентно обаждане."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Обаждането не може да се отхвърли."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Обаждането или съответно обажданията не могат да се освободят."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Обаждане чрез SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Спешно обаждане"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Радиомодулът се включва…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Няма услуга. Извършва се нов опит…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Не може да се извърши обаждане. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> не е номер за спешни случаи."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Не може да се извърши обаждане. Наберете номер за спешни случаи."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Използвайте клавиатурата за набиране"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Задържане на обаждането"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Възобновяване на обаждането"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Край на обаждането"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Показване на клавиатурата за набиране"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Скриване на клавиатурата за набиране"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Заглушаване"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Пускане"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Добавяне на обаждане"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Обединяване на обаждания"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Размяна"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Управление на обажданията"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Управление на конф. обаждане"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Конферентно обаждане"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Управление"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Аудио"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Видеообажд."</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Преминаване към гласово обаждане"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Превключване на камерата"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Включване на камерата"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Изключване на камерата"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Още опции"</string>
- <string name="player_started" msgid="3478865572468310331">"Плейърът е стартиран"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Плейърът е спрян"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Камерата не е в готовност"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Камерата е в готовност"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Неизвестно събитие в сесията на обаждане"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Услуга"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Настройване"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Не е зададено&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Други настройки за обаждане"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Обаждане чрез <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Входящо обаждане чрез <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"снимка на контакта"</string>
- <string name="goPrivate" msgid="3554069451018659483">"превключване към частно обаждане"</string>
- <string name="selectContact" msgid="92191462970821951">"избиране на контакта"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Напишете свой собствен..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Отказ"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Изпращане"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Приемане"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Изпращане на SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Отхвърляне"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Приемане като видеообаждане"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Приемане като аудиообаждане"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Приемане на заявката за видеовръзка"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Отхвърляне на заявката за видеовръзка"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Приемане на заявката за предаване на видео"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Отхвърляне на заявката за предаване на видео"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Приемане на заявката за получаване на видеообаждане"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Отхвърляне на заявката за получаване на видео"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Плъзнете нагоре за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Плъзнете наляво за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Плъзнете надясно за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Плъзнете надолу за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Вибриране"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Вибриране"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Звук"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Стандартен звук (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Мелодия на телефона"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Вибриране при звънене"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Мелодия и вибриране"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Управление на конферентното обаждане"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Спешен номер"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Снимка на потребителския профил"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Камерата е изключена"</string>
- <string name="child_number" msgid="4469090994612105532">"чрез <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Бележката е изпратена"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Скорошни съобщения"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Бизнес информация"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"На <xliff:g id="DISTANCE">%.1f</xliff:g> мили"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"На <xliff:g id="DISTANCE">%.1f</xliff:g> км"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> – <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>; <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Отваря утре в <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Отваря днес в <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Затваря в <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Затворено днес в <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"В момента работи"</string>
- <string name="closed_now" msgid="2635314668145282080">"В момента не работи"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Възможен спам"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Обаждането завърши %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"За първи път ви се обаждат от този номер."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Подозирахме, че това обаждане може да е от разпространител на спам."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Блокиране/спам"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Добавяне на контакт"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Не е спам"</string>
-</resources>
diff --git a/InCallUI/res/values-bn/strings.xml b/InCallUI/res/values-bn/strings.xml
deleted file mode 100644
index 2f561ce6e..000000000
--- a/InCallUI/res/values-bn/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"ফোন"</string>
- <string name="onHold" msgid="527593602772521700">"হোল্ডে রয়েছে"</string>
- <string name="unknown" msgid="3646075119047488748">"অজানা"</string>
- <string name="private_num" msgid="6081418498487514686">"ব্যক্তিগত নম্বর"</string>
- <string name="payphone" msgid="5743050584468748607">"অর্থের বিনিময়ে কল করার ফোন"</string>
- <string name="confCall" msgid="3181961445236675173">"কনফারেন্স কল"</string>
- <string name="call_lost" msgid="8208184291640961172">"কল সমাপ্ত হয়েছে"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"স্পিকার"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"হ্যান্ডসেট ইয়ারপিস"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"তারযুক্ত হেডসেট"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"ব্লুটুথ"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"নিম্নলিখিত টোনগুলি পাঠাবেন?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"টোনগুলি পাঠানো হচ্ছে\n"</string>
- <string name="send_button" msgid="4054398309483035794">"পাঠান"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"হ্যাঁ"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"না"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"ওয়াইল্ড অক্ষরগুলিকে এর মাধ্যমে প্রতিস্থাপিত করুন"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"কনফারেন্স কল <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"ভয়েসমেল নম্বর"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"ডায়াল করা হচ্ছে"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"আবার ডায়াল করা হচ্ছে"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"কনফারেন্স কল"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"আগত কল"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"আগত কাজের কল"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"কল সমাপ্ত হয়েছে"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"হোল্ডে রয়েছে"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"কল নামিয়ে রাখা হচ্ছে"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"কলের সময়ে"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"আমার নম্বর হল <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"ভিডিও সংযুক্ত করছে"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"ভিডিও কল"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"ভিডিওর অনুরোধ করছে"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"ভিডিও কলে সংযোগ করা যাচ্ছে না"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"ভিডিওর অনুরোধ প্রত্যাখ্যান করা হয়েছে"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"আপনার কলব্যাক নম্বর\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"আপনার জরুরী কলব্যাক নম্বর\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"ডায়াল করা হচ্ছে"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"মিসড কল"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"মিসড কলগুলি"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>টি মিসড কল"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> এর থেকে মিসড কল"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"চলমান কল"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"চলমান কাজের কল"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"চলমান ওয়াই-ফাই কল"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"চলমান ওয়াই-ফাই কাজের কল"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"হোল্ডে রয়েছে"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"আগত কল"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"আগত কাজের কল"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"আগত ওয়াই-ফাই কল"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"আগত ওয়াই-ফাই কাজের কল"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"আগত ভিডিও কল"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"আগত সন্দেহভাজন স্প্যাম কল"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"আগত ভিডিও অনুরোধ"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"নতুন ভয়েসমেল"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"নতুন ভয়েসমেল (<xliff:g id="COUNT">%d</xliff:g>টি)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> ডায়াল করুন"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"ভয়েসমেল নম্বর অজানা"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"কোনো পরিষেবা নেই"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"নির্বাচিত নেটওয়ার্ক (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) অনুপলব্ধ"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"উত্তর"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"কল নামিয়ে রাখুন"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"ভিডিও"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"ভয়েস"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"স্বীকার করুন"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"খারিজ করুন"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"ঘুরিয়ে কল করুন"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"বার্তা"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"অন্য ডিভাইসে চালু থাকা কল"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"কল স্থানান্তর করুন"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"একটি কল করতে, প্রথমে বিমানমোড বন্ধ করুন৷"</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"নেটওয়ার্কে নিবন্ধিত নয়৷"</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"সেলুলার নেটওয়ার্ক উপলব্ধ নয়।"</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"কোনো কল স্থাপন করতে, একটি বৈধ নম্বর লিখুন৷"</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"কল করা যাবে না৷"</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI ক্রম চালু হচ্ছে…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"পরিষেবা সমর্থিত নয়৷"</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"কলগুলি স্যুইচ করা যাবে না৷"</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"কল আলাদা করা যাবে না৷"</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"হস্তান্তর করা যাবে না৷"</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"কনফারেন্স করা যাবে না৷"</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"কল প্রত্যাখ্যান কলা যাবে না৷"</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"কল(গুলি) কাটা যাবে না৷"</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP কল"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"জরুরি কল"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"রেডিও চালু করা হচ্ছে…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"কোন পরিষেবা নেই৷ আবার চেষ্টা করা হচ্ছে..."</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"কল করা যাবে না৷ <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> কোনো জরুরী নম্বর নয়৷"</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"কল করা যাবে না৷ কোনো জরুরী নম্বর ডায়াল করুন৷"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"ডায়াল করতে কীবোর্ড ব্যবহার করুন"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"কল হোল্ডে রাখুন"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"কল আবার শুরু করুন"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"কল শেষ করুন"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"ডায়ালপ্যাড দেখান"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"ডায়ালপ্যাড লুকান"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"নিঃশব্দ করুন"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"সশব্দ করুন"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"কল যোগ করুন"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"কলগুলি মার্জ করুন"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"সোয়াপ করুন"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"কলগুলি পরিচালনা করুন"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"কনফারেন্স কল পরিচালনা করুন"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"কনফারেন্স কল"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"পরিচালনা করুন"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"অডিও"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"ভিডিও কল"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"ভয়েস কলে পরিবর্তন করুন"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"ক্যামেরা স্যুইচ করুন"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"ক্যামেরা চালু করুন"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"ক্যামেরা বন্ধ করুন"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"আরো বিকল্প"</string>
- <string name="player_started" msgid="3478865572468310331">"প্লেয়ার শুরু হয়েছে"</string>
- <string name="player_stopped" msgid="1278611664986561535">"প্লেয়ার বন্ধ হয়ে গেছে"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"ক্যামেরা রেডি নয়"</string>
- <string name="camera_ready" msgid="2614541247814590887">"ক্যামেরা রেডি"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"অজানা কল অধিবেশনের ইভেন্ট"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"পরিষেবা"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"সেটআপ"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;সেট করা নেই&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"অন্যান্য কল সেটিংস"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> এর মাধ্যমে কল করা হচ্ছে"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> এর মাধ্যমে ইনকামিং কল"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"পরিচিতির ফটো"</string>
- <string name="goPrivate" msgid="3554069451018659483">"ব্যক্তিগতভাবে কাজ করুন"</string>
- <string name="selectContact" msgid="92191462970821951">"পরিচিতি নির্বাচন করুন"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"আপনার নিজের পছন্দ মতো লিখুন…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"বাতিল করুন"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"পাঠান"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"উত্তর"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS পাঠান"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"অস্বীকার করুন"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"ভিডিও কল হিসেবে উত্তর দিন"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"অডিও কল হিসেবে উত্তর দিন"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"ভিডিওর অনুরোধ গ্রহণ করুন"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"ভিডিওর অনুরোধ প্রত্যাখ্যান করুন"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"ভিডিও প্রেরণ করার অনুরোধ স্বীকার করুন"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"ভিডিও প্রেরণ করার অনুরোধ প্রত্যাখ্যান করুন"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"ভিডিও গ্রহণ করার অনুরোধ স্বীকার করুন"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"ভিডিও গ্রহণ করার অনুরোধ প্রত্যাখ্যান করুন"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> এর জন্য উপরের দিকে স্লাইড করুন৷"</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> এর জন্য বাঁ দিকে স্লাইড করুন৷"</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> এর জন্য ডান দিকে স্লাইড করুন৷"</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> এর জন্য নীচের দিকে স্লাইড করুন৷"</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"কম্পন"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"কম্পন"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"শব্দ"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"ডিফল্ট শব্দ (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"ফোন রিংটোন"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"রিং হওয়ার সময় কম্পন হবে"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"রিংটোন ও কম্পন"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"কনফারেন্স কল পরিচালনা করুন"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"জরুরি নম্বর"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"প্রোফাইল ফটো"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"ক্যামেরা বন্ধ"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> এর মাধ্যমে"</string>
- <string name="note_sent" msgid="7623014827902758398">"নোট পাঠানো হয়েছে"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"সাম্প্রতিক বার্তাগুলি"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"ব্যবসার তথ্য"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> মাইল দূরে"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> কিলোমিটার দূরে"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"আগামীকাল <xliff:g id="OPEN_TIME">%s</xliff:g>\'টায় খুলবে"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"আজ <xliff:g id="OPEN_TIME">%s</xliff:g>\'টায় খুলবে"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g>\'টায় বন্ধ হয়"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"আজ <xliff:g id="CLOSE_TIME">%s</xliff:g>\'টায় বন্ধ হয়েছে"</string>
- <string name="open_now" msgid="4615706338669555999">"এখন খোলা রয়েছে"</string>
- <string name="closed_now" msgid="2635314668145282080">"এখন বন্ধ রয়েছে"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"সন্দেহভাজন স্প্যাম কলার"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"কল সমাপ্ত হয়েছে %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"এই প্রথমবার এই নম্বর থেকে আপনাকে কল করা হয়েছে৷"</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"এটি কোনো স্প্যামারের কল হতে পারে বলে আমাদের মনে হচ্ছে৷"</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"অবরুদ্ধ করুন/স্প্যাম হিসাবে অভিযোগ করুন"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"পরিচিতি যোগ করুন"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"স্প্যাম নয়"</string>
-</resources>
diff --git a/InCallUI/res/values-bs/strings.xml b/InCallUI/res/values-bs/strings.xml
deleted file mode 100644
index 9db727c98..000000000
--- a/InCallUI/res/values-bs/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"Na čekanju"</string>
- <string name="unknown" msgid="3646075119047488748">"Nepoznato"</string>
- <string name="private_num" msgid="6081418498487514686">"Privatni broj"</string>
- <string name="payphone" msgid="5743050584468748607">"Telefonska govornica"</string>
- <string name="confCall" msgid="3181961445236675173">"Konferencijski poziv"</string>
- <string name="call_lost" msgid="8208184291640961172">"Poziv je prekinut"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Zvučnik"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Slušalice telefona"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Žičane slušalice"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Poslati sljedeće tonove?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Slanje tonova\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Pošalji"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Da"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Ne"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Zamijeni zamjenski znak sa"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konferencijski poziv <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Broj govorne pošte"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Poziva se"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Ponovno pozivanje"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konferencijski poziv"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Dolazni poziv"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Dolazni poslovni poziv"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Poziv je završen"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Na čekanju"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Prekid veze"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Poziv u toku"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Moj broj je <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Uspostavljanje videopoziva"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videopoziv"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Zahtijevanje videopoziva"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Nije moguće uspostaviti videopoziv"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Zahtjev za videopoziv je odbijen"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Vaš broj za povratni poziv\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Vaš broj za hitni povratni poziv\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Poziva se"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Propušteni poziv"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Propušteni pozivi"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"Propušteni pozivi: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Propušteni poziv od kontakta <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Poziv u toku"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Poslovni poziv u toku"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Wi-Fi poziv u toku"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Wi-Fi poslovni poziv u toku"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Na čekanju"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Dolazni poziv"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Dolazni poslovni poziv"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Dolazni Wi-Fi poziv"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Dolazni Wi-Fi poslovni poziv"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Dolazni videopoziv"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Mogući neželjeni dolazni poziv"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Zahtjev za dolazni videopoziv"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Nova govorna pošta"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Nova govorna pošta (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Pozovi <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Nepoznat broj govorne pošte"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Nema mreže"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Odabrana mreža (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) je nedostupna"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Odgovori"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Prekini vezu"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Videopoziv"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Glasovni poziv"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Prihvati"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Odbaci"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Povr. poziv"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Poruka"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Poziv u toku na drugom uređaju"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Prenesi poziv"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Da uputite poziv, isključite Način rada u avionu."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Nije registrirano na mreži."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobilna mreža nije dostupna."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Da uputite poziv, upišite važeći broj."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Nije moguće pozvati."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Pokretanje MMI sekvence u toku…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Usluga nije podržana."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Nije moguće prebacivanje poziva."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Nije moguće odvojiti poziv."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Prijenos nije moguć."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Konferencijski poziv nije uspio."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Nije moguće odbiti poziv."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Nije moguće uputiti poziv(e)."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP poziv"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Hitni poziv"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Uključivanje radija u toku…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Nema mreže. Ponovni pokušaj u toku…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Nije moguće pozvati. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> nije broj za htine slučajeve."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Nije moguće pozvati. Pozovite broj za hitne slučajeve."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Koristi tastaturu za biranje"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Stavi poziv na čekanje"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Nastavi poziv"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Prekini poziv"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Prikaži telefonsku tipkovnicu"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Sakrij telefonsku tipkovnicu"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Isključi zvuk"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Uključi zvuk"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Dodaj poziv"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Spoji pozive"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Zamijeni"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Upravljaj pozivima"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Upravljaj konf. pozivom"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferencijski poziv"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Upravljaj"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Zvuk"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videopoziv"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Promijeni na glasovni poziv"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Promijeni kameru"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Uključi kameru"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Isključi kameru"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Više opcija"</string>
- <string name="player_started" msgid="3478865572468310331">"Plejer je pokrenut"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Plejer je zaustavljen"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kamera nije spremna"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kamera je spremna"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Nepoznati događaj sesije poziva"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Usluga"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Postavljanje"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Nije postavljeno&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Ostale postavke poziva"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Pozivanje putem <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Dolazni poziv putem <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"fotografija kontakta"</string>
- <string name="goPrivate" msgid="3554069451018659483">"idi na privatno"</string>
- <string name="selectContact" msgid="92191462970821951">"odaberi kontakt"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Napišite svoj..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Otkaži"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Pošalji"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Odgovori"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Pošalji SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Odbij"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Odgovori videopozivom"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Prihvati kao audiopoziv"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Prihvati zahtjev za videopoziv"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Odbij zahtjev za videopoziv"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Prihvati zahtjev za slanje videopoziva"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Odbij zahtjev za slanje videopoziva"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Prihvati zahtjev za primanje videopoziva"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Odbij zahtjev za primanje videopoziva"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Kliznite nagore za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Kliznite lijevo za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Kliznite nadesno za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Kliznite nadolje za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibracija"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibracija"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Zvuk"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Zadani zvuk (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Melodija zvona telefona"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibriraj kada zvoni"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Melodija zvona i vibracija"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Upravljaj konferencijskim pozivom"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Broj za hitne slučajeve"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Fotografija profila"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera je isključena"</string>
- <string name="child_number" msgid="4469090994612105532">"putem <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Bilješka je poslana"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Nedavne poruke"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informacije o preduzeću"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"Udaljenost u miljama: <xliff:g id="DISTANCE">%.1f</xliff:g>"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"Udaljenost u km: <xliff:g id="DISTANCE">%.1f</xliff:g>"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Otvara se sutra u <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Otvara se danas u <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Zatvara se u <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Zatvoreno danas u <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Otvori sad"</string>
- <string name="closed_now" msgid="2635314668145282080">"Zatvoreno sada"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Neželjeni pozivalac"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Poziv je završen %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Ovo je prvi poziv koji ste primili s ovog broja."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Sumnjamo da je ovaj poziv neželjen sadržaj."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blokiraj/prijavi"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Dodaj kontakt"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Nije neželjeno"</string>
-</resources>
diff --git a/InCallUI/res/values-ca/strings.xml b/InCallUI/res/values-ca/strings.xml
deleted file mode 100644
index 103f15b63..000000000
--- a/InCallUI/res/values-ca/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telèfon"</string>
- <string name="onHold" msgid="527593602772521700">"En espera"</string>
- <string name="unknown" msgid="3646075119047488748">"Desconegut"</string>
- <string name="private_num" msgid="6081418498487514686">"Número privat"</string>
- <string name="payphone" msgid="5743050584468748607">"Telèfon públic"</string>
- <string name="confCall" msgid="3181961445236675173">"Conferència"</string>
- <string name="call_lost" msgid="8208184291640961172">"La trucada s\'ha interromput"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Altaveu"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Auricular"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Auricular amb cable"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Vols enviar els tons següents?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"S\'estan enviant els tons\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Envia"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Sí"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"No"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Substitueix el caràcter comodí per"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Conferència, <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Número del missatge de veu"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"S\'està marcant"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"S\'està tornant a marcar"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Conferència"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Trucada entrant"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Trucada de feina entrant"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Ha finalitzat la trucada"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"En espera"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"S\'està penjant"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"En una trucada"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"El meu número és <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"S\'està connectant el vídeo"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videotrucada"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"S\'està sol·licitant el vídeo"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"No es pot connectar la videotrucada"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"S\'ha rebutjat la sol·licitud de vídeo"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Número de devolució de trucada\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Número de devolució de trucada d\'emergència\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"S\'està marcant"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Trucada perduda"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Trucades perdudes"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> trucades perdudes"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Trucada perduda de <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Trucada en curs"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Trucada de feina en curs"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Trucada per Wi-Fi en curs"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Trucada de feina per Wi-Fi en curs"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"En espera"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Trucada entrant"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Trucada de feina entrant"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Trucada per Wi-Fi entrant"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Trucada de feina per Wi-Fi entrant"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Videotrucada entrant"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Presumpta trucada brossa entrant"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Sol·licitud de vídeo entrant"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Missatge de veu nou"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Missatges de veu nous (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Marca <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Número del missatge de veu desconegut"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Sense servei"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"La xarxa seleccionada (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) no està disponible"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Respon"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Penja"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Vídeo"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Veu"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Accepta"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Ignora"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Torna trucada"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Missatge"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Trucada en curs en un altre dispositiu"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transfereix la trucada"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Per fer una trucada, primer desactiva el mode d\'avió."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"No s\'ha registrat a la xarxa."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"La xarxa mòbil no està disponible."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Introdueix un número vàlid per fer una trucada."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"No es pot trucar."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"S\'està iniciant la seqüència MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"El servei no és compatible."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"No es pot canviar de trucada."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"No es pot separar la trucada."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"No es poden transferir trucades."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"No es pot establir la conferència."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"No es pot rebutjar la trucada."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"No es poden alliberar trucades."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Trucada de SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Trucada d\'emergència"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"S\'està activant el senyal mòbil…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"No hi ha servei. S\'està tornant a provar…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"No es pot trucar. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> no és un número d\'emergència."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"No es pot trucar. Marca un número d\'emergència."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Utilitza el teclat per marcar"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Posa la trucada en espera"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Reprèn la trucada"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Finalitza la trucada"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Mostra el teclat"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Amaga el teclat"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Silencia"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Activa el so"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Afegeix una trucada"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Combina les trucades"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Canvia"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Gestiona les trucades"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Gestiona la conferència"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"conferència"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Gestiona"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Àudio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videotrucada"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Canvia a trucada de veu"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Canvia la càmera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Activa la càmera"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Desactiva la càmera"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Més opcions"</string>
- <string name="player_started" msgid="3478865572468310331">"S\'ha iniciat el reproductor"</string>
- <string name="player_stopped" msgid="1278611664986561535">"S\'ha aturat el reproductor"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"La càmera no està preparada"</string>
- <string name="camera_ready" msgid="2614541247814590887">"La càmera està preparada"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Esdeveniment de sessió de trucada desconeguda"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Servei"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Configura"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;No definit&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Altres opcions de configuració de les trucades"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"S\'està trucant amb <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Trucada entrant mitjançant <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"foto de contacte"</string>
- <string name="goPrivate" msgid="3554069451018659483">"conferència privada"</string>
- <string name="selectContact" msgid="92191462970821951">"selecciona el contacte"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Escriu la teva…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Cancel·la"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Envia"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Respon"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Envia SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Rebutja"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Respon amb una videotrucada"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Respon amb una trucada d\'àudio"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Accepta la sol·licitud de vídeo"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Rebutja la sol·licitud de vídeo"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Accepta la sol·licitud per transmetre vídeo"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Rebutja la sol·licitud per transmetre vídeo"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Accepta la sol·licitud per rebre vídeo"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Rebutja la sol·licitud per rebre vídeo"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Fes lliscar el dit cap amunt per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Fes lliscar el dit cap a l\'esquerra per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Fes lliscar el dit cap a la dreta per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Fes lliscar el dit cap avall per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibració"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibració"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"So"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"So predeterminat (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"So de trucada"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrar en sonar"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"So de trucada i vibració"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Gestiona la conferència"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Número d\'emergència"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Foto de perfil"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"La càmera està desactivada"</string>
- <string name="child_number" msgid="4469090994612105532">"mitjançant <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"S\'ha enviat la nota"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Missatges recents"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informació de l\'empresa"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"A <xliff:g id="DISTANCE">%.1f</xliff:g> mi de distància"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"A <xliff:g id="DISTANCE">%.1f</xliff:g> km de distància"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Obre demà a les <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Obre avui a les <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Tanca a les <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Avui ha tancat a les <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Obert ara"</string>
- <string name="closed_now" msgid="2635314668145282080">"Ara és tancat"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Possible trucada brossa"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"La trucada amb el número %1$s ha finalitzat"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"És la primera vegada que aquest número t\'ha trucat."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Sospitem que aquesta trucada prové d\'un emissor de contingut brossa."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Bloqueja/marca brossa"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Afegeix un contacte"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"No és brossa"</string>
-</resources>
diff --git a/InCallUI/res/values-cs/strings.xml b/InCallUI/res/values-cs/strings.xml
deleted file mode 100644
index f3e4a5ed3..000000000
--- a/InCallUI/res/values-cs/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"Přidržený hovor"</string>
- <string name="unknown" msgid="3646075119047488748">"Neznámý volající"</string>
- <string name="private_num" msgid="6081418498487514686">"Soukromé číslo"</string>
- <string name="payphone" msgid="5743050584468748607">"Telefonní automat"</string>
- <string name="confCall" msgid="3181961445236675173">"Konferenční hovor"</string>
- <string name="call_lost" msgid="8208184291640961172">"Volání zrušeno"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Reproduktor"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Sluchátko telefonu"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Kabelová náhlavní soupr."</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Odeslat následující tóny?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Odesílání tónů\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Odeslat"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Ano"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Ne"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Nahradit zástupné znaky jinými znaky"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konferenční hovor <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Číslo hlasové schránky"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Vytáčení"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Opakované vytáčení"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konferenční hovor"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Příchozí hovor"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Příchozí pracovní hovor"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Hovor ukončen"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Přidržený hovor"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Ukončování hovoru"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Probíhá hovor"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Moje číslo je <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Navazování spojení pro video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videohovor"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Požadování videa"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Videohovor nelze zahájit"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Žádost o video byla zamítnuta"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Vaše číslo pro zpětné volání\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Vaše číslo pro tísňové zpětné volání\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Vytáčení"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Zmeškaný hovor"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Zmeškané hovory"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"Zmeškané hovory: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Zmeškaný hovor od volajícího <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Probíhající hovor"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Probíhající pracovní hovor"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Probíhající hovor přes Wi-Fi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Probíhající pracovní hovor přes Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Přidržený hovor"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Příchozí hovor"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Příchozí pracovní hovor"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Příchozí hovor přes Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Příchozí pracovní hovor přes Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Příchozí videohovor"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"U příchozího hovoru máme podezření, že se jedná o spam"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Příchozí žádost o videohovor"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Nová hlasová zpráva"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Nové hlasové zprávy (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Volat hlasovou schránku <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Číslo hlasové schránky není známé"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Žádný signál"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Vybraná síť (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) není k dispozici"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Přijmout"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Zavěsit"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Videohovor"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Hlas. hovor"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Přijmout"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Odmítnout"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Zavolat zpět"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Posl. zprávu"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Probíhá hovor na jiném zařízení"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Převést hovor sem"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Chcete-li telefonovat, nejprve vypněte režim Letadlo."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Přihlášení k síti nebylo úspěšné."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobilní síť je nedostupná."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Chcete-li uskutečnit hovor, zadejte platné telefonní číslo."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Hovor nelze uskutečnit."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Spouštění sekvence MMI..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Služba není podporována."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Hovory nelze přepnout."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Hovor nelze rozdělit."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Hovor nelze předat."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Konferenční hovor nelze uskutečnit."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Hovor nelze odmítnout."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Hovor nelze ukončit."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Volání SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Tísňové volání"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Zapínání bezdrátového modulu..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Žádný signál. Probíhá další pokus…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Hovor nelze uskutečnit. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> není číslo tísňového volání."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Hovor nelze uskutečnit. Vytočte číslo tísňového volání."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Vytočte číslo pomocí klávesnice"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Podržet hovor"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Obnovit hovor"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Ukončit hovor"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Zobrazit číselník"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Skrýt číselník"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Vypnout zvuk"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Zapnout zvuk"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Přidat hovor"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Spojit hovory"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Zaměnit"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Spravovat hovory"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Spravovat konferenční hovor"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferenční hovor"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Spravovat"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Zvuk"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videohovor"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Změnit na hlasové volání"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Přepnout kameru"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Zapnout kameru"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Vypnout kameru"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Další možnosti"</string>
- <string name="player_started" msgid="3478865572468310331">"Přehrávač spuštěn"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Přehrávač zastaven"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Fotoaparát není připraven"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Fotoaparát je připraven"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Neznámá událost relace volání"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Služba"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Nastavení"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Nenastaveno&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Další nastavení hovorů"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Volání prostřednictvím poskytovatele <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Příchozí hovor přes poskytovatele <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"fotografie kontaktu"</string>
- <string name="goPrivate" msgid="3554069451018659483">"přepnout na soukromé"</string>
- <string name="selectContact" msgid="92191462970821951">"vybrat kontakt"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Napsat vlastní odpověď..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Zrušit"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Odeslat"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Přijmout"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Odeslat SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Odmítnout"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Přijmout jako videohovor"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Přijmout jako hlasový hovor"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Přijmout žádost o videhovor"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Odmítnout žádost o videohovor"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Přijmout žádost o odesílání videa"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Odmítnout žádost o odesílání videa"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Přijmout žádost o příjem videa"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Odmítnout žádost o příjem videa"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> – přejeďte prstem nahoru"</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> – přejeďte prstem doleva"</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> – přejeďte prstem doprava"</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> – přejeďte prstem dolů"</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrace"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrace"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Zvuk"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Výchozí zvuk (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Vyzváněcí tón telefonu"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrace při vyzvánění"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Vyzvánění a vibrace"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Správa konferenčního hovoru"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Číslo tísňové linky"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profilová fotka"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Fotoaparát je vypnutý"</string>
- <string name="child_number" msgid="4469090994612105532">"pomocí čísla <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Poznámka byla odeslána"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Nejnovější zprávy"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informace o firmě"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"Vzdálenost: <xliff:g id="DISTANCE">%.1f</xliff:g> mi"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"Vzdálenost: <xliff:g id="DISTANCE">%.1f</xliff:g> km"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>–<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Zítra otevírá v <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Dnes otevírá v <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Zavírá v <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Dnes zavřeno od <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Otevřeno"</string>
- <string name="closed_now" msgid="2635314668145282080">"Nyní zavřeno"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Podezření na spam"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Hovor skončil v %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Toto číslo vám volalo poprvé."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Máme podezření, že tento hovor byl spam."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blok./nahlásit spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Přidat kontakt"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Nešlo o spam"</string>
-</resources>
diff --git a/InCallUI/res/values-da/strings.xml b/InCallUI/res/values-da/strings.xml
deleted file mode 100644
index 82d773c1c..000000000
--- a/InCallUI/res/values-da/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"Afventer"</string>
- <string name="unknown" msgid="3646075119047488748">"Ukendt"</string>
- <string name="private_num" msgid="6081418498487514686">"Privat nummer"</string>
- <string name="payphone" msgid="5743050584468748607">"Mønttelefon"</string>
- <string name="confCall" msgid="3181961445236675173">"Telefonmøde"</string>
- <string name="call_lost" msgid="8208184291640961172">"Opkaldet blev afbrudt."</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Højttaler"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Ørestykke til håndsæt"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Headset med ledning"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Vil du sende følgende toner?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Sender toner\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Send"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Ja"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Nej"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Erstat jokertegnet med"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Telefonmøde <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Telefonsvarernummer"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Ringer op"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Ringer op igen"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Telefonmøde"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Indgående opkald"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Indgående arbejdsopkald"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Opkaldet er afsluttet"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Afventer"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Lægger på"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Opkald i gang"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Mit nummer er <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Opretter videoforbindelse"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videoopkald"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Anmoder om video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Kan ikke forbinde videoopkald"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Videoanmodningen blev afvist"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Dit tilbagekaldsnummer\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Dit tilbagekaldsnummer til nødopkald\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Ringer op"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Ubesvaret opkald"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Ubesvarede opkald"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ubesvarede opkald"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Ubesvaret opkald fra <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Igangværende opkald"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Igangværende opkald i forbindelse med arbejde"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Igangværende opkald via Wi-Fi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Igangværende Wi-Fi-opkald i forbindelse med arbejde"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Afventer"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Indgående opkald"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Indgående arbejdsopkald"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Indgående Wi-Fi-opkald"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Indgående Wi-Fi-opkald i forbindelse med arbejde"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Indgående videoopkald"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Indgående formodet spamopkald"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Indgående videoanmodning"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Ny telefonsvarerbesked"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Nye telefonsvarerbeskeder (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Ring til <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Telefonsvarernummeret er ukendt"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Ingen dækning"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Det valgte netværk (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) er ikke tilgængeligt"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Besvar"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Læg på"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Tale"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Acceptér"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Afvis"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Ring tilbage"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Besked"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Igangværende opkald på en anden enhed"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Overfør opkald"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Slå Flytilstand fra først for at foretage et opkald."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Ikke registreret på netværket."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobilnetværket er ikke tilgængeligt."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Indtast et gyldigt nummer for at foretage et opkald."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Der kan ikke ringes op."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Starter MMI-sekvens…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Tjenesten er ikke understøttet."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Der kan ikke skiftes opkald."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Opkaldet kan ikke adskilles."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Der kan ikke viderestilles."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Der kan ikke oprettes telefonmøde."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Opkaldet kan ikke afvises."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Et eller flere opkald kan ikke frigives."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP-opkald"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Nødopkald"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Tænder for radio…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Ingen tjeneste. Prøver igen…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Der kan ikke ringes op. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> er ikke et alarmnummer."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Der kan ikke ringes op. Ring til et alarmnummer."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Brug tastaturet til at ringe op"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Sæt opkald i venteposition"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Genoptag opkald"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Afslut opkald"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Vis numerisk tastatur"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Skjul numerisk tastatur"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Slå lyden fra"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Slå lyden til"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Tilføj opkald"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Slå opkald sammen"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Skift"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Administrer opkald"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Administrer telefonmøde"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Telefonmøde"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Administrer"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Lyd"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videoopkald"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Skift til taleopkald"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Skift kamera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Slå kameraet til"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Slå kameraet fra"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Flere valgmuligheder"</string>
- <string name="player_started" msgid="3478865572468310331">"Afspilleren er startet"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Afspilleren er stoppet"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kameraet er ikke klar"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kameraet er klar"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Ukendt opkaldsbegivenhed"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Tjeneste"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Konfiguration"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Ikke angivet&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Andre indstillinger for opkald"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Opkald via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Indgående opkald via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"billede af kontaktperson"</string>
- <string name="goPrivate" msgid="3554069451018659483">"gør privat"</string>
- <string name="selectContact" msgid="92191462970821951">"vælg kontaktperson"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Skriv dit eget svar…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Annuller"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Send"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Besvar"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Send sms"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Afvis"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Besvar som videoopkald"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Besvar som taleopkald"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Acceptér anmodning om video"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Afvis videoanmodning"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Acceptér anmodning om udgående video"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Afvis anmodning om udgående video"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Acceptér anmodning om indgående video"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Afvis anmodning om indgående video"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Skub op for at <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Skub til venstre for at <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Skub til højre for at <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Skub ned for at <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibration"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibration"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Lyd"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Standardlyd (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Ringetone ved opkald"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrer ved opringning"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Ringetone og vibration"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Administrer telefonmøde"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Alarmnummer"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profilbillede"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kameraet er slukket"</string>
- <string name="child_number" msgid="4469090994612105532">"via <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Noten er sendt"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Seneste beskeder"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Virksomhedsoplysninger"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> mil væk"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km væk"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>-<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Åbner i morgen kl. <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Åbner i dag kl. <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Lukker kl. <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Lukkede i dag kl. <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Åbent nu"</string>
- <string name="closed_now" msgid="2635314668145282080">"Lukket for i dag"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Formodet spammer"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Opkaldet blev afsluttet %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Dette er første gang, at dette nummer har ringet til dig."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Vi har mistanke om, at dette er et spamopkald."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Bloker/rap. spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Tilføj kontaktperson"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Ikke spam"</string>
-</resources>
diff --git a/InCallUI/res/values-de/strings.xml b/InCallUI/res/values-de/strings.xml
deleted file mode 100644
index b3ecffc4c..000000000
--- a/InCallUI/res/values-de/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"Gehaltener Anruf"</string>
- <string name="unknown" msgid="3646075119047488748">"Unbekannt"</string>
- <string name="private_num" msgid="6081418498487514686">"Private Nummer"</string>
- <string name="payphone" msgid="5743050584468748607">"Münztelefon"</string>
- <string name="confCall" msgid="3181961445236675173">"Telefonkonferenz"</string>
- <string name="call_lost" msgid="8208184291640961172">"Verbindung unterbrochen"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Lautsprecher"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Mobilgerät-Kopfhörer"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Kabelgebundenes Headset"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Folgende Töne senden?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Töne werden gesendet\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Senden"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Ja"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Nein"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Platzhalter ersetzen durch"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Telefonkonferenz <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Mailboxnummer"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Rufaufbau"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Wahlwiederholung"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Telefonkonferenz"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Eingehender Anruf"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Eingeh. geschäftl. Anruf"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Anruf beendet"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Gehaltener Anruf"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Auflegen"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Im Gespräch"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Meine Nummer lautet <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Videoverbindung wird hergestellt"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videoanruf"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Videoanfrage wird gesendet"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Videoanruf kann nicht verbunden werden"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Videoanfrage abgelehnt"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Deine Rückrufnummer lautet:\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Deine Notrufnummer lautet:\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Rufaufbau"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Verpasster Anruf"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Entgangene Anrufe"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> entgangene Anrufe"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Verpasster Anruf von <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Aktiver Anruf"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Aktiver geschäftlicher Anruf"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Aktiver WLAN-Anruf"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Aktiver geschäftlicher WLAN-Anruf"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Gehaltener Anruf"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Eingehender Anruf"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Eingehender geschäftlicher Anruf"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Eingehender WLAN-Anruf"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Eingehender geschäftlicher WLAN-Anruf"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Eingehender Videoanruf"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Verdacht auf eingehenden Spam-Anruf"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Eingehende Videoanfrage"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Neue Mailbox-Nachricht"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Neue Mailbox-Nachricht (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> wählen"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Mailboxnummer unbekannt"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Kein Service"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Ausgewähltes Netzwerk (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) nicht verfügbar"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Annehmen"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Beenden"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Videoanruf"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Sprachanruf"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Akzeptieren"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Ablehnen"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Zurückrufen"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Nachricht"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Aktiver Anruf auf anderem Gerät"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Anruf übertragen"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Deaktiviere zunächst den Flugmodus, um einen Anruf zu tätigen."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Nicht in Netzwerk registriert."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobilfunknetz nicht verfügbar."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Gib eine gültige Nummer ein, um einen Anruf zu tätigen."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Anruf nicht möglich."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI-Sequenz wird gestartet…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Dienst wird nicht unterstützt."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Anruf kann nicht gewechselt werden."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Anruf kann nicht getrennt werden."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Anruf kann nicht übergeben werden."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Konferenzschaltung nicht möglich."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Anruf kann nicht abgelehnt werden."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Anrufe können nicht freigegeben werden."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP-Anruf"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Notruf"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Mobilfunkverbindung wird aktiviert…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Kein Service. Neuer Versuch…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Anruf nicht möglich. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> ist keine Notrufnummer."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Anruf nicht möglich. Wähle eine Notrufnummer."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Zum Wählen Tastatur verwenden"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Anruf halten"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Anruf fortsetzen"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Anruf beenden"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Wähltasten einblenden"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Wähltasten ausblenden"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Stummschalten"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Stummschaltung aufheben"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Anruf hinzufügen"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Anrufe verbinden"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Wechseln"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Anrufe verwalten"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Telefonkonferenz verwalten"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Telefonkonferenz"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Verwalten"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videoanruf"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Zu Sprachanruf wechseln"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Kamera wechseln"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Kamera einschalten"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Kamera ausschalten"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Weitere Optionen"</string>
- <string name="player_started" msgid="3478865572468310331">"Videoübertragung gestartet"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Videoübertragung gestoppt"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kamera nicht bereit"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kamera bereit"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Unbekanntes Ereignis während eines Anrufs"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Dienst"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Einrichtung"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Nicht festgelegt&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Sonstige Anrufeinstellungen"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Anruf über <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Eingehender Anruf über <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"Kontaktbild"</string>
- <string name="goPrivate" msgid="3554069451018659483">"privat sprechen"</string>
- <string name="selectContact" msgid="92191462970821951">"Kontakt auswählen"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Eigene Antwort schreiben…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Abbrechen"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Senden"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Annehmen"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS senden"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Ablehnen"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Als Videoanruf annehmen"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Als normalen Anruf annehmen"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Videoanfrage akzeptieren"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Videoanfrage ablehnen"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Anfrage für ausgehenden Videoanruf akzeptieren"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Anfrage für ausgehenden Videoanruf ablehnen"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Anfrage für eingehenden Videoanruf akzeptieren"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Anfrage für eingehenden Videoanruf ablehnen"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Zum <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> nach oben schieben."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Zum <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> nach links schieben."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Zum <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> nach rechts schieben."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Zum <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> nach unten schieben."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrieren"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrieren"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Ton"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Standardklingelton (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Klingelton"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Beim Klingeln vibrieren"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Klingelton &amp; Vibration"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Telefonkonferenz verwalten"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Notrufnummer"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profilbild"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera aus"</string>
- <string name="child_number" msgid="4469090994612105532">"über <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Notiz gesendet"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Zuletzt eingegangene Nachrichten"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Geschäftsinformationen"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> Meilen entfernt"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> Kilometer entfernt"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> bis <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Öffnet morgen um <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Öffnet heute um <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Schließt um <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Hat heute um <xliff:g id="CLOSE_TIME">%s</xliff:g> geschlossen"</string>
- <string name="open_now" msgid="4615706338669555999">"Jetzt geöffnet"</string>
- <string name="closed_now" msgid="2635314668145282080">"Jetzt geschlossen"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Verdacht auf Spam"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Anruf beendet %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Du wurdest das erste Mal von dieser Nummer angerufen."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Dieser Anruf schien ein Spam-Anruf zu sein."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blockieren/Spam melden"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Kontakt hinzufügen"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Kein Spam"</string>
-</resources>
diff --git a/InCallUI/res/values-el/strings.xml b/InCallUI/res/values-el/strings.xml
deleted file mode 100644
index 993813290..000000000
--- a/InCallUI/res/values-el/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Τηλέφωνο"</string>
- <string name="onHold" msgid="527593602772521700">"Σε αναμονή"</string>
- <string name="unknown" msgid="3646075119047488748">"Άγνωστος"</string>
- <string name="private_num" msgid="6081418498487514686">"Απόκρυψη αριθμού"</string>
- <string name="payphone" msgid="5743050584468748607">"Τηλέφωνο με χρέωση"</string>
- <string name="confCall" msgid="3181961445236675173">"Κλήση συνδιάσκεψης"</string>
- <string name="call_lost" msgid="8208184291640961172">"Η κλήση απορρίφθηκε"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Ηχείο"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Ακουστικό"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Ενσύρματο ακουστικό"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Αποστολή των παρακάτω ήχων;\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Ήχοι αποστολής\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Αποστολή"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Ναι"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Όχι"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Αντικατάσταση του χαρακτήρα μπαλαντέρ με"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Κλήση συνδιάσκεψης <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Αριθμός αυτόματου τηλεφωνητή"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Κλήση"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Επανάκληση"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Κλήση συνδιάσκεψης"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Εισερχόμενη κλήση"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Εισερχόμ. κλήση εργασίας"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Η κλήση τερματίστηκε"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Σε αναμονή"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Κλείσιμο γραμμής"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Σε κλήση"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Ο αριθμός μου είναι <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Σύνδεση βίντεο"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Βιντεοκλήση"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Αίτημα βίντεο"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Δεν είναι δυνατή η σύνδεση βιντεοκλήσης"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Το αίτημα βίντεο απορρίφθηκε"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Αριθμός επανάκλησης\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Αριθμός επανάκλησης έκτακτης ανάγκης\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Κλήση"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Αναπάντητη κλήση"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Αναπάντητες κλήσεις"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> αναπάντητες κλήσεις"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Αναπάντητη κλήση από <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Κλήση σε εξέλιξη"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Κλήση εργασίας σε εξέλιξη"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Κλήση Wi-Fi σε εξέλιξη"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Κλήση εργασίας μέσω Wi-Fi σε εξέλιξη"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Σε αναμονή"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Εισερχόμενη κλήση"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Εισερχόμενη κλήση εργασίας"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Εισερχόμενη κλήση μέσω Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Εισερχόμενη κλήση εργασίας μέσω Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Εισερχόμενη βιντεοκλήση"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Πιθανώς ανεπιθύμητη εισερχόμενη κλήση"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Αίτημα εισερχόμενου βίντεο"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Νέο μήνυμα στον αυτόματο τηλεφωνητή"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Νέο μήνυμα στον αυτόματο τηλεφωνητή (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Καλέστε στο <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Άγνωστος αριθμός αυτόματου τηλεφωνητή"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Δίκτυο μη διαθέσιμο"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Το επιλεγμένο δίκτυο (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) δεν είναι διαθέσιμο"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Απάντηση"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Τερμ. κλήσης"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Βίντεο"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Φωνή"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Αποδοχή"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Παράβλεψη"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Επανάκληση"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Μήνυμα"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Κλήση σε εξέλιξη σε άλλη συσκευή"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Μεταφορά κλήσης"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Για να πραγματοποιήσετε μια κλήση, απενεργοποιήστε πρώτα τη λειτουργία πτήσης."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Δεν έχετε εγγραφεί στο δίκτυο."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Το δίκτυο κινητής τηλεφωνίας δεν είναι διαθέσιμο."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Για να πραγματοποιήσετε κλήση, εισαγάγετε έναν έγκυρο αριθμό."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Δεν είναι δυνατή η κλήση."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Έναρξη ακολουθίας MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Η υπηρεσία δεν υποστηρίζεται."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Δεν είναι δυνατή η εναλλαγή κλήσεων."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Δεν είναι δυνατός ο διαχωρισμός της κλήσης."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Δεν είναι δυνατή η μεταφορά."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Δεν είναι δυνατή η συνδιάσκεψη."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Δεν είναι δυνατή η απόρριψη της κλήσης."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Δεν είναι δυνατή η πραγματοποίηση κλήσεων."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Κλήση SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Κλήση έκτακτης ανάγκης"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Ενεργοποίηση πομπού…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Δεν υπάρχει υπηρεσία. Νέα προσπάθεια…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Δεν είναι δυνατή η κλήση. Το <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> δεν είναι αριθμός έκτακτης ανάγκης."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Δεν είναι δυνατή η κλήση. Πληκτρολογήστε έναν αριθμό έκτακτης ανάγκης."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Χρησιμοποιήστε το πληκτρολόγιο για να πραγματοποιήσετε μια κλήση"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Αναμονή κλήσης"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Συνέχιση κλήσης"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Τερματισμός κλήσης"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Εμφάνιση πληκτρολογίου κλήσης"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Απόκρυψη πληκτρολογίου κλήσης"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Σίγαση"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Κατάργηση σίγασης"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Προσθήκη κλήσης"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Συγχώνευση κλήσεων"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Ανταλλαγή"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Διαχείριση κλήσεων"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Διαχείριση κλήσης συνδιάσκεψης"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Κλήση διάσκεψης"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Διαχείριση"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Ήχος"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Βιντεοκλ."</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Αλλαγή σε φωνητική κλήση"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Αλλαγή κάμερας"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Ενεργοποίηση κάμερας"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Απενεργοποίηση κάμερας"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Περισσότερες επιλογές"</string>
- <string name="player_started" msgid="3478865572468310331">"Το πρόγραμμα αναπαραγωγής βίντεο ξεκίνησε"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Το πρόγραμμα αναπαραγωγής βίντεο διακόπηκε"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Η κάμερα δεν είναι έτοιμη"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Η κάμερα είναι έτοιμη"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Άγνωστο συμβάν περιόδου σύνδεσης κλήσης"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Υπηρεσία"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Ρύθμιση"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Δεν έχει οριστεί&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Άλλες ρυθμίσεις κλήσης"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Κλήση μέσω <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Εισερχόμενη κλήση μέσω <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"φωτογραφία επαφής"</string>
- <string name="goPrivate" msgid="3554069451018659483">"ιδιωτική χρήση"</string>
- <string name="selectContact" msgid="92191462970821951">"επιλογή επαφής"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Συντάξτε τη δική σας…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Ακύρωση"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Αποστολή"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Απάντηση"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Αποστολή SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Απόρριψη"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Απάντηση ως βιντεοκλήση"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Απάντηση ως κλήση ήχου"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Αποδοχή αιτήματος βίντεο"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Απόρριψη αιτήματος βίντεο"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Αποδοχή αιτήματος μετάδοσης βίντεο"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Απόρριψη αιτήματος μετάδοσης βίντεο"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Αποδοχή αιτήματος λήψης βίντεο"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Απόρριψη αιτήματος λήψης βίντεο"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Κύλιση προς τα επάνω για <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Κύλιση προς τα αριστερά για <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Κύλιση προς τα δεξιά για <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Κύλιση προς τα κάτω για <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Δόνηση"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Δόνηση"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Ήχος"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Προεπιλεγμένος ήχος (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Ήχος κλήσης τηλεφώνου"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Δόνηση κατά το κουδούνισμα"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Ήχος κλήσης και δόνηση"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Διαχείριση κλήσης συνδιάσκεψης"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Αριθμός έκτακτης ανάγκης"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Φωτογραφία προφίλ"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Απενεργοποίηση κάμερας"</string>
- <string name="child_number" msgid="4469090994612105532">"μέσω <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Η σημείωση εστάλη"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Πρόσφατα μηνύματα"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Πληροφορίες επιχείρησης"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> μίλια μακριά"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> χιλιόμετρα μακριά"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Ανοίγει αύριο στις <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Ανοίγει σήμερα στις <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Κλείνει στις <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Έκλεισε σήμερα στις <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Ανοιχτό τώρα"</string>
- <string name="closed_now" msgid="2635314668145282080">"Κλειστό τώρα"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Πιθανώς ανεπιθύμητος"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Η κλήση τερματίστηκε %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Αυτή είναι η πρώτη φορά που σας καλεί αυτός ο αριθμός."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Έχουμε υποψίες ότι αυτή κλήση είναι ανεπιθύμητη."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Αποκλ./αναφ. ανεπιθ."</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Προσθήκη επαφής"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Μη ανεπιθύμητος"</string>
-</resources>
diff --git a/InCallUI/res/values-en-rAU/strings.xml b/InCallUI/res/values-en-rAU/strings.xml
deleted file mode 100644
index 013aa9685..000000000
--- a/InCallUI/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Phone"</string>
- <string name="onHold" msgid="527593602772521700">"On hold"</string>
- <string name="unknown" msgid="3646075119047488748">"unknown"</string>
- <string name="private_num" msgid="6081418498487514686">"Private number"</string>
- <string name="payphone" msgid="5743050584468748607">"Payphone"</string>
- <string name="confCall" msgid="3181961445236675173">"Conference call"</string>
- <string name="call_lost" msgid="8208184291640961172">"Call cut off"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Speaker"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Handset earpiece"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Wired headset"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Send the following tones?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Sending tones\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Send"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"yes"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"no"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Replace wild character with"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Conference call <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Voicemail number"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Dialling"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Redialling"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Conference call"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Incoming call"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Incoming work call"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Call ended"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"On hold"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Hanging up"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"In call"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"My number is <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Connecting video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Video call"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Requesting video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Can\'t connect video call"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Video request rejected"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Your callback number\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Your emergency callback number\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Dialling"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Missed call"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Missed calls"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> missed calls"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Missed call from <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"On-going call"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Ongoing work call"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Ongoing Wi-Fi call"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Ongoing Wi-Fi work call"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"On hold"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Incoming call"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Incoming work call"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Incoming Wi-Fi call"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Incoming Wi-Fi work call"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Incoming video call"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Incoming suspected spam call"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Incoming video request"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"New voicemail"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"New voicemail (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Dial <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Voicemail number unknown"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"No service"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Selected network (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) unavailable"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Answer"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Hang up"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"In-stream video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Voice"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Accept"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Dismiss"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Call back"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Message"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Ongoing call on another device"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transfer call"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"To place a call, first turn off Aeroplane mode."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Not registered on network."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobile network not available."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"To place a call, enter a valid number."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Can\'t call."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Starting MMI sequence…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Service not supported."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Can\'t switch calls."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Can\'t separate call."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Can\'t transfer."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Can\'t conference."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Can\'t reject call."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Can\'t release call(s)."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP call"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Emergency call"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Turning on radio…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"No network. Trying again…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Can\'t call. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> is not an emergency number."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Can\'t call. Dial an emergency number."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Use keyboard to dial"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Hold Call"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Resume Call"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"End Call"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Show dial pad"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Hide dial pad"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Mute"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Unmute"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Add call"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Merge calls"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Swap"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Manage calls"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Manage conference call"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Conference call"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Manage"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Video call"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Change to voice call"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Switch camera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Turn on camera"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Turn off camera"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"More options"</string>
- <string name="player_started" msgid="3478865572468310331">"Player Started"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Player Stopped"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Camera not ready"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Camera ready"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Unknown call session event"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Service"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Set up"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Not set&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Other call settings"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Calling via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Incoming via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"contact photo"</string>
- <string name="goPrivate" msgid="3554069451018659483">"go private"</string>
- <string name="selectContact" msgid="92191462970821951">"select contact"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Write your own..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"cancel"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Send"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Answer"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Send SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Decline"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Answer as video call"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Answer as audio call"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Accept video request"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Decline video request"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Accept video transmit request"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Decline video transmit request"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Accept video receive request"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Decline video receive request"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Slide up for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Slide left for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Slide right for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Slide down for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrate"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrate"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Sound"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Default sound (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Phone ringtone"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrate when ringing"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Ringtone &amp; Vibrate"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Manage conference call"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Emergency number"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profile photo"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Camera off"</string>
- <string name="child_number" msgid="4469090994612105532">"via <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Note sent"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Recent messages"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Business info"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> mi away"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km away"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> – <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Opens tomorrow at <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Opens today at <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Closes at <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Closed today at <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Open now"</string>
- <string name="closed_now" msgid="2635314668145282080">"Closed now"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Suspected spam caller"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Call ended %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"This is the first time that this number has called you."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"We suspected that this call was from a spammer."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Block/report spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Add contact"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Not spam"</string>
-</resources>
diff --git a/InCallUI/res/values-en-rGB/strings.xml b/InCallUI/res/values-en-rGB/strings.xml
deleted file mode 100644
index 013aa9685..000000000
--- a/InCallUI/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Phone"</string>
- <string name="onHold" msgid="527593602772521700">"On hold"</string>
- <string name="unknown" msgid="3646075119047488748">"unknown"</string>
- <string name="private_num" msgid="6081418498487514686">"Private number"</string>
- <string name="payphone" msgid="5743050584468748607">"Payphone"</string>
- <string name="confCall" msgid="3181961445236675173">"Conference call"</string>
- <string name="call_lost" msgid="8208184291640961172">"Call cut off"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Speaker"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Handset earpiece"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Wired headset"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Send the following tones?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Sending tones\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Send"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"yes"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"no"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Replace wild character with"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Conference call <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Voicemail number"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Dialling"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Redialling"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Conference call"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Incoming call"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Incoming work call"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Call ended"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"On hold"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Hanging up"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"In call"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"My number is <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Connecting video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Video call"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Requesting video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Can\'t connect video call"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Video request rejected"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Your callback number\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Your emergency callback number\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Dialling"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Missed call"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Missed calls"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> missed calls"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Missed call from <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"On-going call"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Ongoing work call"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Ongoing Wi-Fi call"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Ongoing Wi-Fi work call"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"On hold"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Incoming call"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Incoming work call"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Incoming Wi-Fi call"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Incoming Wi-Fi work call"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Incoming video call"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Incoming suspected spam call"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Incoming video request"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"New voicemail"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"New voicemail (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Dial <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Voicemail number unknown"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"No service"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Selected network (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) unavailable"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Answer"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Hang up"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"In-stream video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Voice"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Accept"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Dismiss"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Call back"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Message"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Ongoing call on another device"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transfer call"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"To place a call, first turn off Aeroplane mode."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Not registered on network."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobile network not available."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"To place a call, enter a valid number."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Can\'t call."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Starting MMI sequence…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Service not supported."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Can\'t switch calls."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Can\'t separate call."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Can\'t transfer."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Can\'t conference."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Can\'t reject call."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Can\'t release call(s)."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP call"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Emergency call"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Turning on radio…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"No network. Trying again…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Can\'t call. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> is not an emergency number."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Can\'t call. Dial an emergency number."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Use keyboard to dial"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Hold Call"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Resume Call"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"End Call"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Show dial pad"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Hide dial pad"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Mute"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Unmute"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Add call"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Merge calls"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Swap"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Manage calls"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Manage conference call"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Conference call"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Manage"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Video call"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Change to voice call"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Switch camera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Turn on camera"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Turn off camera"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"More options"</string>
- <string name="player_started" msgid="3478865572468310331">"Player Started"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Player Stopped"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Camera not ready"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Camera ready"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Unknown call session event"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Service"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Set up"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Not set&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Other call settings"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Calling via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Incoming via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"contact photo"</string>
- <string name="goPrivate" msgid="3554069451018659483">"go private"</string>
- <string name="selectContact" msgid="92191462970821951">"select contact"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Write your own..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"cancel"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Send"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Answer"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Send SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Decline"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Answer as video call"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Answer as audio call"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Accept video request"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Decline video request"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Accept video transmit request"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Decline video transmit request"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Accept video receive request"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Decline video receive request"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Slide up for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Slide left for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Slide right for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Slide down for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrate"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrate"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Sound"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Default sound (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Phone ringtone"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrate when ringing"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Ringtone &amp; Vibrate"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Manage conference call"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Emergency number"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profile photo"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Camera off"</string>
- <string name="child_number" msgid="4469090994612105532">"via <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Note sent"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Recent messages"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Business info"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> mi away"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km away"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> – <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Opens tomorrow at <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Opens today at <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Closes at <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Closed today at <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Open now"</string>
- <string name="closed_now" msgid="2635314668145282080">"Closed now"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Suspected spam caller"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Call ended %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"This is the first time that this number has called you."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"We suspected that this call was from a spammer."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Block/report spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Add contact"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Not spam"</string>
-</resources>
diff --git a/InCallUI/res/values-en-rIN/strings.xml b/InCallUI/res/values-en-rIN/strings.xml
deleted file mode 100644
index 013aa9685..000000000
--- a/InCallUI/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Phone"</string>
- <string name="onHold" msgid="527593602772521700">"On hold"</string>
- <string name="unknown" msgid="3646075119047488748">"unknown"</string>
- <string name="private_num" msgid="6081418498487514686">"Private number"</string>
- <string name="payphone" msgid="5743050584468748607">"Payphone"</string>
- <string name="confCall" msgid="3181961445236675173">"Conference call"</string>
- <string name="call_lost" msgid="8208184291640961172">"Call cut off"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Speaker"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Handset earpiece"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Wired headset"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Send the following tones?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Sending tones\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Send"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"yes"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"no"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Replace wild character with"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Conference call <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Voicemail number"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Dialling"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Redialling"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Conference call"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Incoming call"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Incoming work call"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Call ended"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"On hold"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Hanging up"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"In call"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"My number is <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Connecting video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Video call"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Requesting video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Can\'t connect video call"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Video request rejected"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Your callback number\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Your emergency callback number\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Dialling"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Missed call"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Missed calls"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> missed calls"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Missed call from <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"On-going call"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Ongoing work call"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Ongoing Wi-Fi call"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Ongoing Wi-Fi work call"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"On hold"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Incoming call"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Incoming work call"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Incoming Wi-Fi call"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Incoming Wi-Fi work call"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Incoming video call"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Incoming suspected spam call"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Incoming video request"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"New voicemail"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"New voicemail (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Dial <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Voicemail number unknown"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"No service"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Selected network (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) unavailable"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Answer"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Hang up"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"In-stream video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Voice"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Accept"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Dismiss"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Call back"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Message"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Ongoing call on another device"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transfer call"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"To place a call, first turn off Aeroplane mode."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Not registered on network."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobile network not available."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"To place a call, enter a valid number."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Can\'t call."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Starting MMI sequence…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Service not supported."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Can\'t switch calls."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Can\'t separate call."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Can\'t transfer."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Can\'t conference."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Can\'t reject call."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Can\'t release call(s)."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP call"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Emergency call"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Turning on radio…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"No network. Trying again…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Can\'t call. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> is not an emergency number."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Can\'t call. Dial an emergency number."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Use keyboard to dial"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Hold Call"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Resume Call"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"End Call"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Show dial pad"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Hide dial pad"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Mute"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Unmute"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Add call"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Merge calls"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Swap"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Manage calls"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Manage conference call"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Conference call"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Manage"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Video call"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Change to voice call"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Switch camera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Turn on camera"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Turn off camera"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"More options"</string>
- <string name="player_started" msgid="3478865572468310331">"Player Started"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Player Stopped"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Camera not ready"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Camera ready"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Unknown call session event"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Service"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Set up"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Not set&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Other call settings"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Calling via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Incoming via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"contact photo"</string>
- <string name="goPrivate" msgid="3554069451018659483">"go private"</string>
- <string name="selectContact" msgid="92191462970821951">"select contact"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Write your own..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"cancel"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Send"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Answer"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Send SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Decline"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Answer as video call"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Answer as audio call"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Accept video request"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Decline video request"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Accept video transmit request"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Decline video transmit request"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Accept video receive request"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Decline video receive request"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Slide up for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Slide left for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Slide right for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Slide down for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrate"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrate"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Sound"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Default sound (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Phone ringtone"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrate when ringing"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Ringtone &amp; Vibrate"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Manage conference call"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Emergency number"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profile photo"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Camera off"</string>
- <string name="child_number" msgid="4469090994612105532">"via <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Note sent"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Recent messages"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Business info"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> mi away"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km away"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> – <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Opens tomorrow at <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Opens today at <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Closes at <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Closed today at <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Open now"</string>
- <string name="closed_now" msgid="2635314668145282080">"Closed now"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Suspected spam caller"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Call ended %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"This is the first time that this number has called you."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"We suspected that this call was from a spammer."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Block/report spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Add contact"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Not spam"</string>
-</resources>
diff --git a/InCallUI/res/values-es-rUS/strings.xml b/InCallUI/res/values-es-rUS/strings.xml
deleted file mode 100644
index 915c90779..000000000
--- a/InCallUI/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Teléfono"</string>
- <string name="onHold" msgid="527593602772521700">"En espera"</string>
- <string name="unknown" msgid="3646075119047488748">"Desconocido"</string>
- <string name="private_num" msgid="6081418498487514686">"Número privado"</string>
- <string name="payphone" msgid="5743050584468748607">"Teléfono pago"</string>
- <string name="confCall" msgid="3181961445236675173">"Llamada en conferencia"</string>
- <string name="call_lost" msgid="8208184291640961172">"Se interrumpió la llamada"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Altavoz"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Auricular del dispositivo"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Auriculares con cable"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"¿Deseas enviar los siguientes tonos?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Enviando tonos\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Enviar"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Sí"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"No"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Reemplazar el carácter comodín con"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Llamada en conferencia: <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Número de buzón de voz"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Marcando"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Volviendo a marcar"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Llamada en conferencia"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Llamada entrante"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Llamada entrante: trabajo"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Llamada finalizada"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"En espera"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Colgando"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"En llamada"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Mi número es <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Conectando video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videollamada"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Solicitando video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"No se puede conectar la videollamada"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Se rechazó la solicitud de videollamada"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Número de devolución de llamada\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Número de devolución de llamada de emergencia\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Marcando"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Llamada perdida"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Llamadas perdidas"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> llamadas perdidas"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Llamada perdida de <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Llamada en curso"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Llamada en curso: trabajo"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Llamada Wi-Fi en curso"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Llamada Wi-Fi en curso: trabajo"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"En espera"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Llamada entrante"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Llamada entrante: trabajo"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Llamada Wi-Fi entrante"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Llamada Wi-Fi entrante: trabajo"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Videollamada entrante"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Posible llamada entrante de spam"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Solicitud de videollamada entrante"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Nuevo mensaje de buzón de voz"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Buzón de voz nuevo (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Marcar <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Número de buzón de voz desconocido"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Sin servicio"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"La red seleccionada (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) no está disponible"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Responder"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Colgar"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Voz"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Aceptar"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Descartar"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Llamar"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Mensaje"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Llamada en curso en otro dispositivo"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transferir llamada"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Para realizar una llamada, primero debes desactivar el modo de avión."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"No está registrado en la red."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"La red móvil no está disponible."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Para realizar una llamada, ingresa un número válido."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"No se puede realizar la llamada."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Iniciando la secuencia de MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"El servicio no es compatible."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"No se pueden cambiar las llamadas."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"No se puede desviar la llamada."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"No se puede transferir."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"No se puede realizar la conferencia."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"No se puede rechazar la llamada."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"No se pueden liberar las llamadas."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Llamada SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Llamada de emergencia"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Encendiendo radio…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"No hay servicio. Vuelve a intentarlo…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"No se puede realizar la llamada. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> no es un número de emergencia."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"No se puede realizar la llamada. Marca un número de emergencia."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Usar teclado para marcar"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Retener llamada"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Reanudar llamada"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Finalizar llamada"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Mostrar teclado"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Ocultar teclado"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Silenciar"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Dejar de silenciar"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Agregar llamada"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Combinar llamadas"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Cambiar"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Administrar llamadas"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Administrar conferencia"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Llamada en conferencia"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Administrar"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Video"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Cambiar a llamada de voz"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Cambiar cámara"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Activar la cámara"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Desactivar la cámara"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Más opciones"</string>
- <string name="player_started" msgid="3478865572468310331">"Se inició el reproductor"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Se detuvo el reproductor"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"La cámara no está lista"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Cámara lista"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Evento de sesión de llamada desconocido"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Servicio"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Configuración"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Sin configurar&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Otras opciones de llamada"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Llamada por medio de <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Entrantes por medio de <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"foto de contacto"</string>
- <string name="goPrivate" msgid="3554069451018659483">"pasar a modo privado"</string>
- <string name="selectContact" msgid="92191462970821951">"seleccionar contacto"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Escribe tu propia respuesta…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Cancelar"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Enviar"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Responder"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Enviar SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Rechazar"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Responder como videollamada"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Responder como llamada de audio"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Aceptar solicitud de videollamada"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Rechazar solicitud de videollamada"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Aceptar solicitud de transmisión de videollamada"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Rechazar solicitud de transmisión de videollamada"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Aceptar solicitud de recepción de videollamada"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Rechazar solicitud de recepción de videollamada"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Desliza el dedo hacia arriba para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Desliza el dedo hacia la izquierda para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Desliza el dedo hacia la derecha para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Desliza el dedo hacia abajo para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrar"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrar"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Sonido"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Sonido predeterminado (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Tono del teléfono"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrar al sonar"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Tono y vibración"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Administrar llamada en conferencia"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Número de emergencia"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Foto de perfil"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Cámara desactivada"</string>
- <string name="child_number" msgid="4469090994612105532">"del <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Se envió la nota"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Mensajes recientes"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Información de la empresa"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"A <xliff:g id="DISTANCE">%.1f</xliff:g> mi"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"A <xliff:g id="DISTANCE">%.1f</xliff:g> km"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"De <xliff:g id="OPEN_TIME">%1$s</xliff:g> a <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g> y <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Abre mañana a la hora <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Abre hoy a la hora <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Cierra a la hora <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Cerró hoy a la hora <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Abierto ahora"</string>
- <string name="closed_now" msgid="2635314668145282080">"Cerrado ahora"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Posible spam"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Llamada finalizada %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Es la primera vez que te llaman desde este número."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Sospechamos que esta llamada era spam."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Bloquear/denunciar"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Agregar contacto"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"No es spam"</string>
-</resources>
diff --git a/InCallUI/res/values-es/strings.xml b/InCallUI/res/values-es/strings.xml
deleted file mode 100644
index ab570d593..000000000
--- a/InCallUI/res/values-es/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Teléfono"</string>
- <string name="onHold" msgid="527593602772521700">"En espera"</string>
- <string name="unknown" msgid="3646075119047488748">"Desconocida"</string>
- <string name="private_num" msgid="6081418498487514686">"Número privado"</string>
- <string name="payphone" msgid="5743050584468748607">"Teléfono público"</string>
- <string name="confCall" msgid="3181961445236675173">"Conferencia"</string>
- <string name="call_lost" msgid="8208184291640961172">"Llamada perdida"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Altavoz"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Auricular"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Auriculares con cable"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"¿Quieres enviar los siguientes tonos?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Enviando tonos\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Enviar"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Sí"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"No"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Sustituir el carácter comodín por"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Conferencia <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Número del mensaje de voz"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Llamando"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Llamando otra vez"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Conferencia"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Llamada entrante"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Llamada trabajo entrante"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Llamada finalizada"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"En espera"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Colgando"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Llamada entrante"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Mi número es el <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Conectando videollamada"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videollamada"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Solicitando videollamada"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"No se puede establecer la videollamada"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Solicitud de videollamada rechazada"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Tu número de devolución de llamada\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Tu número de devolución de llamada de emergencia\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Llamando"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Llamada perdida"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Llamadas perdidas"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> llamadas perdidas"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Llamada perdida de <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Llamada en curso"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Llamada de trabajo en curso"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Llamada Wi-Fi en curso"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Llamada Wi-Fi de trabajo en curso"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"En espera"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Llamada entrante"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Llamada de trabajo entrante"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Llamada Wi-Fi entrante"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Llamada Wi-Fi de trabajo entrante"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Videollamada entrante"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Llamada entrante sospechosa de spam"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Solicitud de videollamada entrante"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Nuevo mensaje de voz"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Nuevo mensaje de voz (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Marcar <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Número del mensaje de voz desconocido"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Sin servicio"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"La red seleccionada (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) no está disponible"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Responder"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Colgar"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Videollamada"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Voz"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Aceptar"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Rechazar"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Llamar"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Mensaje"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Llamada activa en otro dispositivo"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transferir llamada"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Para realizar una llamada, primero debes desactivar el modo avión."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"No estás registrado en la red."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"La red móvil no está disponible."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Para realizar una llamada, introduce un número válido."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"No se puede establecer la llamada."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Iniciando secuencia MMI..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Servicio no admitido."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"No se pueden intercambiar llamadas."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"No se pueden separar llamadas."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"No se puede transferir."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"No se puede establecer la conferencia."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"No se puede rechazar la llamada."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"No se pueden hacer llamadas."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Llamada SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Llamada de emergencia"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Activando señal móvil…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Sin servicio. Reintentado…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"No se puede establecer la llamada. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> no es un número de emergencia."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"No se puede establecer la llamada. Marca un número de emergencia."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Usa el teclado para marcar"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Retener llamada"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Seguir con la llamada"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Finalizar llamada"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Mostrar teclado"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Ocultar teclado"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Silenciar"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Activar sonido"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Añadir llamada"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Llamada a tres"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Cambiar"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Administrar llamadas"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Administrar conferencia"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Teleconferencia"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Gestionar"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videollamada"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Cambiar a llamada de voz"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Cambiar cámara"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Activar cámara"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Desactivar cámara"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Más opciones"</string>
- <string name="player_started" msgid="3478865572468310331">"Reproductor iniciado"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Reproductor detenido"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Cámara no preparada"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Cámara preparada"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Evento de sesión de llamada desconocido"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Servicio"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Configuración"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;No definido&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Otra configuración de llamada"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Llamada a través de <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Recibidas a través de <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"foto de contacto"</string>
- <string name="goPrivate" msgid="3554069451018659483">"llamada privada"</string>
- <string name="selectContact" msgid="92191462970821951">"seleccionar contacto"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Escribe tu propia respuesta..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Cancelar"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Enviar"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Responder"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Enviar SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Rechazar"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Responder como videollamada"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Responder como llamada de audio"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Aceptar solicitud de videollamada"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Rechazar solicitud de videollamada"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Aceptar solicitud de transmisión de videollamada"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Rechazar solicitud de transmisión de videollamada"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Aceptar solicitud de recepción de videollamada"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Rechazar solicitud de recepción de videollamada"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Desliza el dedo hacia arriba para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Desliza el dedo hacia la izquierda para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Desliza el dedo hacia la derecha para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Desliza el dedo hacia abajo para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrar"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrar"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Sonido"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Sonido predeterminado (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Tono de llamada del teléfono"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrar al sonar"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Tono de llamada y vibración"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Administrar videollamada"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Número de emergencia"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Foto de perfil"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Cámara apagada"</string>
- <string name="child_number" msgid="4469090994612105532">"a través de <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Nota enviada"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Mensajes recientes"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Información de la empresa"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"A <xliff:g id="DISTANCE">%.1f</xliff:g> mi"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"A <xliff:g id="DISTANCE">%.1f</xliff:g> km"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Abre mañana a las <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Abre hoy a las <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Cierra a las <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Cerrado hoy a las <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Abierto ahora"</string>
- <string name="closed_now" msgid="2635314668145282080">"Cerrado ahora"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Sospechoso de spam"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Llamada de %1$s terminada"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Es la primera vez que recibes una llamada de este número."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Sospechábamos que esta llamada era de spam."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Bloquear / Marcar como spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Añadir contacto"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"No es spam"</string>
-</resources>
diff --git a/InCallUI/res/values-et/strings.xml b/InCallUI/res/values-et/strings.xml
deleted file mode 100644
index 5d2a0d8b0..000000000
--- a/InCallUI/res/values-et/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"Ootel"</string>
- <string name="unknown" msgid="3646075119047488748">"Tundmatu"</string>
- <string name="private_num" msgid="6081418498487514686">"Eranumber"</string>
- <string name="payphone" msgid="5743050584468748607">"Telefoniautomaat"</string>
- <string name="confCall" msgid="3181961445236675173">"Konverentskõne"</string>
- <string name="call_lost" msgid="8208184291640961172">"Kõne katkes"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Kõlar"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Käsitelefoni kuular"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Juhtmega peakomplekt"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Kas saata järgmised toonid?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Toonide saatmine\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Saada"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Jah"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Ei"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Asenda metamärk üksusega"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konverentskõne <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Kõneposti number"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Valimine"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Uuesti valimine"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konverentskõne"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Sissetulev kõne"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Sissetulev töökõne"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Kõne lõpetati"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Ootel"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Lõpetamine"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Kõne on pooleli"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Minu number on <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Video ühendamine"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videokõne"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Video taotlemine"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Videokõnet ei õnnestu ühendada"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Videokõne taotlus lükati tagasi"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Teie tagasihelistamise number\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Teie hädaabikõne tagasihelistamise number\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Valimine"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Vastamata kõne"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Vastamata kõned"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> vastamata kõnet"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Vastamata kõne helistajalt <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Käimasolev kõne"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Käimasolev töökõne"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Käimasolev WiFi-kõne"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Käimasolev töökõne WiFi kaudu"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Ootel"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Sissetulev kõne"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Sissetulev töökõne"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Sissetulev WiFi-kõne"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Sissetulev töökõne WiFi kaudu"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Sissetulev videokõne"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Arvatav sissetulev rämpskõne"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Sissetulev videokõne taotlus"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Uus kõnepostisõnum"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Uus kõnepostisõnum (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Valige <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Kõnepostinumber on tundmatu"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Levi puudub"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Valitud võrk (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) pole saadaval"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Vasta"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Lõpeta kõne"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Videokõne"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Häälkõne"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Nõustu"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Loobu"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Helista tagasi"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Saada sõnum"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Pooleliolev kõne teise seadmes"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Kõne ülekandmine"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Helistamiseks lülitage esmalt lennukirežiim välja."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Ei ole võrgus registreeritud."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobiilsidevõrk pole saadaval."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Helistamiseks sisestage kehtiv number."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Ei saa helistada."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI-jada alustamine …"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Teenust ei toetata."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Kõnesid ei saa vahetada."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Kõnet ei saa eraldada."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Ei saa üle kanda."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Konverentskõnet ei saa pidada."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Kõnet ei saa tagasi lükata."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Kõnesid ei saa vabastada."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP-kõne"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Hädaabikõne"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Raadioside sisselülitamine …"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Levi puudub. Uuesti proovimine …"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Ei saa helistada. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> ei ole hädaabinumber."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Ei saa helistada. Valige hädaabinumber."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Kasutage valimiseks klaviatuuri"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Kõne ootele"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Jätka kõnet"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Lõpeta kõne"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Kuva valimisklahvistik"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Peida valimisklahvistik"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Vaigista"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Tühista vaigistus"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Lisa kõne"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Ühenda kõned"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Vaheta"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Halda kõnesid"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Halda konverentskõnet"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konverentskõne"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Halda"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Heli"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videokõne"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Mine üle häälkõnele"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Vaheta kaamerat"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Lülita kaamera sisse"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Lülita kaamera välja"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Rohkem valikuid"</string>
- <string name="player_started" msgid="3478865572468310331">"Pleier käivitati"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Pleier peatati"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kaamera pole valmis"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kaamera on valmis"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Tundmatu kõneseansisündmus"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Teenus"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Seadistamine"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Määramata&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Muud kõneseaded"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Kõne edastab <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Sissetulev kõne teenusepakkuja <xliff:g id="PROVIDER_NAME">%s</xliff:g> kaudu"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"kontakti foto"</string>
- <string name="goPrivate" msgid="3554069451018659483">"aktiveeri privaatne kõne"</string>
- <string name="selectContact" msgid="92191462970821951">"vali kontakt"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Kirjutage ise …"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Tühista"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Saada"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Vastamine"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Saada SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Tagasilükkamine"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Vastamine videokõnena"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Vastamine helikõnena"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Video taotluse aktsepteerimine"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Video taotluse tagasilükkamine"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Video edastamise taotluse aktsepteerimine"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Video edastamise taotluse tagasilükkamine"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Video vastuvõtmise taotluse aktsepteerimine"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Video vastuvõtmise taotluse tagasilükkamine"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Lohistage üles: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Lohistage vasakule: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Lohistage paremale: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Lohistage alla: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibreerimine"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibreerimine"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Heli"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Vaikeheli (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Telefonihelin"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibreerimine helina ajal"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Helin ja vibratsioon"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Konverentskõne haldamine"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Hädaabinumber"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profiilifoto"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kaamera on välja lülitatud"</string>
- <string name="child_number" msgid="4469090994612105532">"numbri <xliff:g id="CHILD_NUMBER">%s</xliff:g> kaudu"</string>
- <string name="note_sent" msgid="7623014827902758398">"Märkus on saadetud"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Hiljutised sõnumid"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Ettevõtte teave"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> miili kaugusel"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km kaugusel"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> kuni <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Avatakse homme kell <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Avatakse täna kell <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Suletakse kell <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Suleti täna kell <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Praegu avatud"</string>
- <string name="closed_now" msgid="2635314668145282080">"Praegu suletud"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Arvatav rämpskõne"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Kõne lõppes: %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Teile helistati sellelt numbrilt esimest korda."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Kahtlustasime, et see võis olla rämpskõne."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blokeeri / teavita rämpskõnest"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Lisa kontakt"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Pole rämpskõne"</string>
-</resources>
diff --git a/InCallUI/res/values-eu/strings.xml b/InCallUI/res/values-eu/strings.xml
deleted file mode 100644
index c300c47cd..000000000
--- a/InCallUI/res/values-eu/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefonoa"</string>
- <string name="onHold" msgid="527593602772521700">"Zain"</string>
- <string name="unknown" msgid="3646075119047488748">"Ezezaguna"</string>
- <string name="private_num" msgid="6081418498487514686">"Zenbaki pribatua"</string>
- <string name="payphone" msgid="5743050584468748607">"Telefono publikoa"</string>
- <string name="confCall" msgid="3181961445236675173">"Konferentzia-deia"</string>
- <string name="call_lost" msgid="8208184291640961172">"Eten egin da deia"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Bozgorailua"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Aurikularrak"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Kabledun entzungailua"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth konexioa"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Tonu hauek bidali nahi dituzu?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Tonuak bidaltzen\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Bidali"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Bai"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Ez"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Ordeztu komodina honekin:"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konferentzia-deiaren ordua: <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Erantzungailuaren zenbakia"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Deitzen"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Berriro markatzen"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konferentzia-deia"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Dei bat jaso duzu"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Laneko dei bat jaso duzu"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Amaitu da deia"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Zain"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Deia amaitzen"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Deia abian"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Nire zenbakia <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g> da"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Bideoa konektatzen"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Bideo-deia"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Bideo-deia eskatzen"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Ezin da konektatu bideo-deia"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Baztertu egin da bideo-deia egiteko eskaera"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Dei-erantzunetarako zenbakia:\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Larrialdi-dei bidez erantzuteko zenbakia:\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Deitzen"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Dei bat galdu duzu"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Dei batzuk galdu dituzu"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> dei galdu dituzu"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Deitzaile honen dei bat galdu duzu: <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Deia abian da"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Laneko dei bat abian da"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Wi-Fi bidezko deia abian da"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Wi-Fi bidezko laneko dei bat abian da"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Zain"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Dei bat jaso duzu"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Laneko dei bat jaso duzu"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Wi-Fi bidezko dei bat jaso duzu"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Wi-Fi bidezko laneko dei bat jaso duzu"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Bideo-dei bat jaso duzu"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Ustezko spam-deia jaso duzu"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Bideo-dei bat egiteko eskaera bat jaso duzu"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Ahots-mezu berria"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Ahots-mezu berriak (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Markatu <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Erantzungailuaren zenbakia ezezaguna da"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Ez dago zerbitzurik"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Hautatutako sarea (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) ez dago erabilgarri"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Erantzun"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Amaitu deia"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Bideo-deia"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Ahots-deia"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Onartu"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Baztertu"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Itzuli deia"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Bidali SMSa"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Dei bat abian da beste gailu batean"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transferitu deia"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Deitzeko, desaktibatu hegaldi modua."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Ez dago sarean erregistratuta."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Sare mugikorra ez dago erabilgarri."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Deitzeko, idatzi balio duen zenbaki bat."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Ezin da deitu."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI sekuentzia hasten…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Ez da onartzen zerbitzua."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Ezin aldatu beste dei batera."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Ezin da bereizi deia."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Ezin da transferitu."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Ezin da egin konferentzia-deia."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Ezin da baztertu deia."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Ezin dira amaitu deiak."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP deia"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Larrialdi-deia"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Irratia pizten…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Ez dago zerbitzurik. Berriro saiatzen…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Ezin da deitu. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> ez da larrialdietarako zenbakia."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Ezin da deitu. Markatu larrialdietarako zenbakia."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Erabili teklatua markatzeko"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Utzi deia zain"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Berrekin deiari"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Amaitu deia"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Erakutsi markagailua"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Ezkutatu markagailua"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Desaktibatu audioa"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Aktibatu audioa"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Gehitu deia"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Bateratu deiak"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Aldatu"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Kudeatu deiak"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Kudeatu konferentzia-deia"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferentzia-deia"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Kudeatu"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audioa"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Bideo-deia"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Aldatu ahots-deira"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Aldatu kamera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Aktibatu kamera"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Desaktibatu kamera"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Aukera gehiago"</string>
- <string name="player_started" msgid="3478865572468310331">"Abian da erreproduzigailua"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Gelditu da erreproduzigailua"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Ez dago prest kamera"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Prest dago kamera"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Dei-saioko gertaera ezezaguna"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Zerbitzua"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Konfigurazioa"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Ezarri gabe&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Deien beste ezarpen batzuk"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> bidez deitzen"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> bidez jasotzen"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"kontaktuaren argazkia"</string>
- <string name="goPrivate" msgid="3554069451018659483">"bihurtu pribatu"</string>
- <string name="selectContact" msgid="92191462970821951">"hautatu kontaktua"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Idatzi zeure erantzuna…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Utzi"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Bidali"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Erantzun"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Bidali SMS mezua"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Baztertu"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Erantzun bideo-dei moduan"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Erantzun audio-dei moduan"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Onartu bideo-deia egiteko eskaera"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Baztertu bideo-deia egiteko eskaera"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Onartu bideoa transmititzeko eskaera"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Baztertu bideoa transmititzeko eskaera"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Onartu bideo-deia jasotzeko eskaera"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Baztertu bideo-deia jasotzeko eskaera"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Lerratu gora hau egiteko: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Lerratu ezkerrera hau egiteko: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Lerratu eskuinera hau egiteko: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Lerratu behera hau egiteko: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Dardara"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Dardara"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Soinua"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Soinu lehenetsia (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Telefonoaren tonua"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Egin dar-dar tonuak jotzean"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Tonua eta dardara"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Kudeatu konferentzia-deia"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Larrialdietarako zenbakia"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profileko argazkia"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Desaktibatuta dago kamera"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> zenbakitik"</string>
- <string name="note_sent" msgid="7623014827902758398">"Bidali da oharra"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Azken mezuak"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Enpresaren informazioa"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"Hemendik <xliff:g id="DISTANCE">%.1f</xliff:g> miliara"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"Hemendik <xliff:g id="DISTANCE">%.1f</xliff:g> km-ra"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> – <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"<xliff:g id="OPEN_TIME">%s</xliff:g> da biharko irekitze-ordua"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"<xliff:g id="OPEN_TIME">%s</xliff:g> da gaurko irekitze-ordua"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g> da ixte-ordua"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"<xliff:g id="CLOSE_TIME">%s</xliff:g> da gaurko itxiera-ordua"</string>
- <string name="open_now" msgid="4615706338669555999">"Irekita dago"</string>
- <string name="closed_now" msgid="2635314668145282080">"Itxita dago"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Ustezko spam-deitzailea"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Deiaren amaiera: %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Zenbaki honek deitu dizun lehen aldia izan da."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Spam-igorle baten deia izan dela susmatu dugu."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Salatu spama dela"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Gehitu kontaktua"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Ez da spama"</string>
-</resources>
diff --git a/InCallUI/res/values-fa/strings.xml b/InCallUI/res/values-fa/strings.xml
deleted file mode 100644
index f96b8956a..000000000
--- a/InCallUI/res/values-fa/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"تلفن"</string>
- <string name="onHold" msgid="527593602772521700">"در انتظار"</string>
- <string name="unknown" msgid="3646075119047488748">"نامشخص"</string>
- <string name="private_num" msgid="6081418498487514686">"شماره خصوصی"</string>
- <string name="payphone" msgid="5743050584468748607">"تلفن عمومی"</string>
- <string name="confCall" msgid="3181961445236675173">"تماس کنفرانسی"</string>
- <string name="call_lost" msgid="8208184291640961172">"تماس قطع شد"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"بلندگو"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"گوشی"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"هدست سیمی"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"بلوتوث"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"شماره‌های بعدی ارسال شود؟\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"تون‌های ارسالی\n"</string>
- <string name="send_button" msgid="4054398309483035794">"ارسال"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"بله"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"نه"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"جایگزینی نویسه عمومی با"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"تماس کنفرانسی <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"شماره پست صوتی"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"شماره‌گیری"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"درحال شماره‌گیری مجدد"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"تماس کنفرانسی"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"تماس ورودی"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"تماس کاری ورودی"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"تماس پایان یافت"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"در انتظار"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"قطع تماس"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"درحال تماس"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"شماره من <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g> است"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"درحال برقراری تماس ویدئویی"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"تماس ویدئویی"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"درحال درخواست تماس ویدئویی"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"برقراری تماس ویدئویی ممکن نیست"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"درخواست تماس ویدئویی رد شد"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"شماره پاسخ تماس شما\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"شماره پاسخ تماس اضطراری شما\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"شماره‌گیری"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"تماس بی‌پاسخ"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"تماس بی‌پاسخ"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> تماس بی‌پاسخ"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"تماس بی‌پاسخ از <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"تماس درحال انجام"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"تماس کاری درحال انجام"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"‏تماس درحال انجام ازطریق Wi-Fi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"‏تماس کاری Wi-Fi درحال انجام"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"در انتظار"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"تماس ورودی"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"تماس کاری ورودی"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"‏تماس Wi-Fi ورودی"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"‏تماس کاری Wi-Fi ورودی"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"تماس ویدئویی ورودی"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"تماس هرزنامه احتمالی ورودی"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"درخواست تماس ویدئویی ورودی"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"پست صوتی جدید"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"پست صوتی جدید (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"شماره‌گیری <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"شماره پست صوتی ناشناس"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"بدون سرویس"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"شبکه انتخابی (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) قابل دسترس نیست"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"پاسخ"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"پایان تماس"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"ویدئو"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"صدا"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"پذیرفتن"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"نپذیرفتن"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"پاسخ تماس"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"پیام"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"تماس در حال انجام در دستگاهی دیگر"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"انتقال تماس"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"برای برقراری تماس، ابتدا حالت هواپیما را خاموش کنید."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"در شبکه ثبت نشده است."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"شبکه تلفن همراه در دسترس نیست."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"برای برقراری تماس، شماره معتبری وارد کنید."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"تماس ممکن نیست."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"‏شروع ترتیب MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"سرویس پشتیبانی نمی‌شود."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"جابه‌جایی بین تماس‌ها ممکن نیست."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"جدا کردن تماس ممکن نیست."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"انتقال ممکن نیست."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"تماس کنفرانسی ممکن نیست."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"رد کردن تماس ممکن نیست."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"آزاد کردن تماس(ها) ممکن نیست."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"‏تماس SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"تماس اضطراری"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"درحال روشن کردن رادیو…‏"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"سرویسی در دسترس نیست. درحال تلاش مجدد…‏"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"تماس ممکن نیست. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> شماره اضطراری نیست."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"تماس ممکن نیست. فقط شماره اضطراری."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"استفاده از صفحه‌کلید برای شماره‌گیری"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"در انتظار گذاشتن تماس"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"ازسرگیری تماس"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"پایان تماس"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"نمایش صفحه شماره‌گیری"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"پنهان کردن صفحه شماره‌گیری"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"بی‌صدا کردن"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"لغو نادیده گرفتن"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"افزودن تماس"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"ادغام تماس‌ها"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"تعویض"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"مدیریت تماس‌ها"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"مدیریت تماس کنفرانسی"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"تماس کنفرانسی"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"مدیریت"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"صوتی"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"تماس ویدئویی"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"تغییر به تماس صوتی"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"تعویض دوربین"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"روشن کردن دوربین"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"خاموش کردن دوربین"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"گزینه‌های بیشتر"</string>
- <string name="player_started" msgid="3478865572468310331">"پخش‌کننده راه‌اندازی شد"</string>
- <string name="player_stopped" msgid="1278611664986561535">"پخش‌کننده متوقف شد"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"دوربین آماده نیست"</string>
- <string name="camera_ready" msgid="2614541247814590887">"دوربین آماده است"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"رویداد جلسه تماس ناشناس"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"سرویس"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"راه‌اندازی"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"‏&lt;تنظیم نشده&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"سایر تنظیمات تماس"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"تماس با <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"تماس‌های ورودی ازطریق <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"عکس مخاطب"</string>
- <string name="goPrivate" msgid="3554069451018659483">"رفتن به حالت خصوصی"</string>
- <string name="selectContact" msgid="92191462970821951">"انتخاب مخاطب"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"پیام خودتان را بنویسید..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"لغو"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"ارسال"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"پاسخ"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"ارسال پیامک"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"رد کردن"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"پاسخ به‌صورت تماس ویدئویی"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"پاسخ به‌صورت تماس صوتی"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"پذیرفتن درخواست تماس ویدئویی"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"نپذیرفتن درخواست تماس ویدئویی"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"پذیرفتن درخواست مخابره ویدئویی"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"نپذیرفتن درخواست مخابره ویدئویی"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"پذیرفتن درخواست دریافت ویدئویی"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"نپذیرفتن درخواست دریافت ویدئویی"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"برای <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> به بالا بلغزانید."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"برای <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> به چپ بلغزانید."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"برای <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> به راست بلغزانید."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"برای <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> به پایین بلغزانید."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"لرزش"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"لرزش"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"صدا"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"صدای پیش‌فرض (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"آهنگ زنگ تلفن"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"لرزش هنگام زنگ زدن"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"آهنگ‌ زنگ و لرزش"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"مدیریت تماس کنفرانسی"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"شماره اضطراری"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"عکس نمایه"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"دوربین خاموش"</string>
- <string name="child_number" msgid="4469090994612105532">"ازطریق <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"یادداشت ارسال شد"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"پیام‌های جدید"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"اطلاعات کسب و کار"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> مایل فاصله"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> کیلومتر فاصله"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>،‏ <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> تا <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>،‏ <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"فردا ساعت <xliff:g id="OPEN_TIME">%s</xliff:g> باز می‌شود"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"امروز ساعت <xliff:g id="OPEN_TIME">%s</xliff:g> باز می‌شود"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"ساعت <xliff:g id="CLOSE_TIME">%s</xliff:g> بسته می‌شود"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"امروز ساعت <xliff:g id="CLOSE_TIME">%s</xliff:g> بسته شد"</string>
- <string name="open_now" msgid="4615706338669555999">"اکنون باز است"</string>
- <string name="closed_now" msgid="2635314668145282080">"اکنون بسته است"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"تماس‌گیرنده هرزنامه احتمالی"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"‏تماس به پایان رسید %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"این اولین بار است که این شماره با شما تماس گرفته است."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"ما به این تماس شک کردیم و احساس کردیم که ممکن است کلاهبردار باشد."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"مسدود کردن/گزارش دادن هرزنامه"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"افزودن مخاطب"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"هرزنامه نیست"</string>
-</resources>
diff --git a/InCallUI/res/values-fi/strings.xml b/InCallUI/res/values-fi/strings.xml
deleted file mode 100644
index 7553e7c25..000000000
--- a/InCallUI/res/values-fi/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Puhelin"</string>
- <string name="onHold" msgid="527593602772521700">"Pidossa"</string>
- <string name="unknown" msgid="3646075119047488748">"Tuntematon"</string>
- <string name="private_num" msgid="6081418498487514686">"Yksityinen numero"</string>
- <string name="payphone" msgid="5743050584468748607">"Maksupuhelin"</string>
- <string name="confCall" msgid="3181961445236675173">"Puhelinneuvottelu"</string>
- <string name="call_lost" msgid="8208184291640961172">"Puhelu katkaistiin."</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Kaiutin"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Puhelimen kaiutin"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Kuulokemikrofoni"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Lähetetäänkö seuraavat äänet?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Lähetetään ääniä\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Lähetä"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Kyllä"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Ei"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Muuta jokerimerkiksi"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Puhelinneuvottelu <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Puhelinvastaajan numero"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Soitetaan"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Soitetaan uudelleen"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Puhelinneuvottelu"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Saapuva puhelu"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Saapuva työpuhelu"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Puhelu päättyi"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Pidossa"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Katkaistaan"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Puhelu käynnissä"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Numeroni on <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Avataan videoyhteys"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videopuhelu"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Videota pyydetään"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Videopuhelua ei voi soittaa"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Videopyyntö hylättiin"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Takaisinsoittonumero:\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Hätäpuhelujen takaisinsoittonumero:\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Soitetaan"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Vastaamaton puhelu"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Vastaamattomia puheluita"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> vastaamatonta puhelua"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Vastaamaton puhelu: <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Käynnissä oleva puhelu"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Käynnissä oleva työpuhelu"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Käynnissä oleva Wi-Fi-puhelu"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Käynnissä oleva Wi-Fi-työpuhelu"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Pidossa"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Saapuva puhelu"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Saapuva työpuhelu"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Saapuva Wi-Fi-puhelu"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Saapuva Wi-Fi-työpuhelu"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Saapuva videopuhelu"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Tämä puhelu saattaa olla häirikköpuhelu."</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Saapuva videopyyntö"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Uusi vastaajaviesti"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Uusia vastaajaviestejä (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Soita: <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Puhelinvastaajan numero on tuntematon."</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Ei yhteyttä"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Valittu verkko (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) ei ole käytettävissä."</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Vastaa"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Katkaise"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Videopuhelu"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Äänipuhelu"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Hyväksy"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Hylkää"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Soita"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Viesti"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Puhelu on kesken toisella laitteella."</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Siirrä puhelu"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Poista lentokonetila käytöstä ennen puhelun soittamista."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Ei rekisteröity verkkoon"</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Matkapuhelinverkko ei ole käytettävissä."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Soita antamalla kelvollinen numero."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Puhelua ei voi soittaa."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Aloitetaan MMI-koodisekvenssiä…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Yhteyttä ei tueta."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Puhelua ei voi vaihtaa."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Puhelua ei voi erottaa."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Puhelua ei voi siirtää."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Puheluja ei voi yhdistää."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Puhelua ei voi hylätä."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Puheluja ei voi katkaista."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP-puhelu"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Hätäpuhelu"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Käynnistetään radiota…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Ei yhteyttä. Yritetään uudelleen…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Puhelua ei voi soittaa. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> ei ole hätänumero."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Puhelua ei voi soittaa. Valitse hätänumero."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Valitse numero näppäimistöllä."</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Aseta puhelu pitoon"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Jatka puhelua"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Lopeta puhelu"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Avaa näppäimistö"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Piilota näppäimistö"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Mykistä"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Poista mykistys"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Lisää puhelu"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Yhdistä puhelut"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Vaihda"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Hallinnoi puheluja"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Hallinnoi puhelinneuvottelua"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Puhelinneuvottelu"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Hallinnoi"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Ääni"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Video"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Muuta äänipuheluksi"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Vaihda kameraa"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Käynnistä kamera"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Sammuta kamera"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Lisäasetukset"</string>
- <string name="player_started" msgid="3478865572468310331">"Soitin käynnistettiin."</string>
- <string name="player_stopped" msgid="1278611664986561535">"Soitin pysäytettiin."</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kamera ei ole valmis."</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kamera on valmis."</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Tuntematon puheluistunnon tapahtuma"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Palveluntarjoaja"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Määritys"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Ei määritetty&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Muut puheluasetukset"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Käytetään operaattoria <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Saapuva puhelu (<xliff:g id="PROVIDER_NAME">%s</xliff:g>)"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"Yhteystiedon kuva"</string>
- <string name="goPrivate" msgid="3554069451018659483">"Muuta yksityiseksi."</string>
- <string name="selectContact" msgid="92191462970821951">"Valitse yhteystieto."</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Kirjoita oma…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Peruuta"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Lähetä"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Vastaa."</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Lähetä tekstiviesti."</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Hylkää."</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Vastaa ja aloita videopuhelu."</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Vastaa ja aloita äänipuhelu."</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Hyväksy videopyyntö."</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Hylkää videopyyntö."</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Hyväksy videon lähetyspyyntö."</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Hylkää videon lähetyspyyntö."</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Hyväksy videon vastaanottopyyntö."</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Hylkää videon vastaanottopyyntö."</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Valitse <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> liu\'uttamalla ylös."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Valitse <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> liu\'uttamalla vasemmalle."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Valitse <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> liu\'uttamalla oikealle."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Valitse <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> liu\'uttamalla alas."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Värinä"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Värinä"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Ääni"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Oletusääni (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Puhelimen soittoääni"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Käytä värinää, kun puhelin soi"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Soittoääni ja värinä"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Hallinnoi puhelinneuvottelua"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Hätänumero"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profiilikuva"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera on pois käytöstä."</string>
- <string name="child_number" msgid="4469090994612105532">"nron <xliff:g id="CHILD_NUMBER">%s</xliff:g> kautta"</string>
- <string name="note_sent" msgid="7623014827902758398">"Muistiinpano lähetettiin."</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Viimeisimmät viestit"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Yrityksen tiedot"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"Etäisyys: <xliff:g id="DISTANCE">%.1f</xliff:g> mailia"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"Etäisyys: <xliff:g id="DISTANCE">%.1f</xliff:g> kilometriä"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>–<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Avataan huomenna kello <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Avataan tänään kello <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Suljetaan tänään kello <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Suljettiin tänään kello <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Avoinna nyt"</string>
- <string name="closed_now" msgid="2635314668145282080">"Suljettu nyt"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Häirikkösoittaja"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Puhelu loppui %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Tämä oli ensimmäinen kerta, kun tästä numerosta soitettiin sinulle."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Epäilemme, että tämä puhelu tuli häirikkösoittajalta."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Estä / ilmoita"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Lisää yhteystieto"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Ei häirikkösoittaja"</string>
-</resources>
diff --git a/InCallUI/res/values-fr-rCA/strings.xml b/InCallUI/res/values-fr-rCA/strings.xml
deleted file mode 100644
index 2980646e8..000000000
--- a/InCallUI/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Téléphone"</string>
- <string name="onHold" msgid="527593602772521700">"En attente"</string>
- <string name="unknown" msgid="3646075119047488748">"Inconnue"</string>
- <string name="private_num" msgid="6081418498487514686">"Numéro privé"</string>
- <string name="payphone" msgid="5743050584468748607">"Cabine téléphonique"</string>
- <string name="confCall" msgid="3181961445236675173">"Conférence téléphonique"</string>
- <string name="call_lost" msgid="8208184291640961172">"L\'appel a été interrompu"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Haut-parleur"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Écouteur du combiné"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Écouteurs à fil"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Envoyer les tonalités suivantes?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Envoi des tonalités\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Envoyer"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Oui"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Non"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Remplacer le caractère générique par"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Conférence téléphonique <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Numéro de messagerie vocale"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Composition..."</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Recomposition en cours..."</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Conférence téléphonique"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Appel entrant"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Appel entrant - travail"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Appel terminé"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"En attente"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Fin de l\'appel"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"En cours d\'appel"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Mon numéro est le <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Connexion de la vidéo en cours…"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Appel vidéo"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Demande de vidéo en cours"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Impossible de connecter l\'appel vidéo"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Demande vidéo refusée"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Votre numéro de rappel :\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Votre numéro de rappel d\'urgence :\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Composition en cours..."</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Appel manqué"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Appels manqués"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> appels manqués"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Appel manqué de <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Appel en cours"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Appel en cours - travail"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Appel Wi-Fi en cours"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Appel Wi-Fi en cours - travail"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"En attente"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Appel entrant"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Appel entrant - travail"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Appel Wi-Fi entrant"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Appel Wi-Fi entrant - travail"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Appel vidéo entrant"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"L\'appel entrant est suspect"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Demande de vidéo reçue"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Nouveau message vocal"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Nouveaux messages vocaux (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Composer le <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Numéro de messagerie vocale inconnu"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Aucun service"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Réseau sélectionné (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) non disponible"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Répondre"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Raccrocher"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Vidéo"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Voix"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Accepter"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Fermer"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Rappeler"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Message"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Appel en cours sur un autre appareil"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transférer l\'appel"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Pour faire un appel, d\'abord désactiver le mode Avion."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Non enregistré sur le réseau."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Réseau cellulaire non disponible."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Pour faire un appel, entrez un numéro valide."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Impossible d\'appeler."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Lancement de la séquence IHM en cours…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Service non pris en charge."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Impossible de faire des appels."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Impossible de séparer les appels."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Impossible de transférer."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Impossible de créer la conférence."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Impossible de refuser l\'appel."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Impossible de libérer l\'appel ou les appels."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Appel SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Appel d\'urgence"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Activation du signal radio…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Aucun service. Nouvel essai en cours..."</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Appel impossible. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> n\'est pas un numéro d\'urgence."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Appel impossible. Composez un numéro d\'urgence."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Utilisez le clavier pour composer un numéro"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Mettre l\'appel en attente"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Reprendre l\'appel"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Mettre fin à l\'appel"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Afficher le clavier numérique"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Masquer le clavier numérique"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Désactiver le son"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Réactiver le son"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Ajouter un appel"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Fusionner les appels"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Permuter"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Gérer les appels"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Gérer la conférence"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Conférence téléphonique"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Gérer"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Appel vidéo"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Passer à un appel vocal"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Changer d\'appareil photo"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Activer la caméra"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Désactiver la caméra"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Plus d\'options"</string>
- <string name="player_started" msgid="3478865572468310331">"Le lecteur a démarré"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Le lecteur a arrêté"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"L\'appareil photo n\'est pas prêt"</string>
- <string name="camera_ready" msgid="2614541247814590887">"L\'appareil photo est prêt"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Événement inconnu de séance d\'appel"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Service"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Configuration"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Non défini&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Autres paramètres d\'appel"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Appel par <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Appel entrant par <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"photo du contact"</string>
- <string name="goPrivate" msgid="3554069451018659483">"mode privé"</string>
- <string name="selectContact" msgid="92191462970821951">"sélectionner un contact"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Réponse personnalisée..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Annuler"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Envoyer"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Répondre"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Envoyer un texto"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Refuser"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Répondre comme appel vidéo"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Répondre comme appel audio"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Accepter la demande vidéo"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Refuser la demande vidéo"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Accepter la demande de transmission vidéo"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Refuser la demande de transmission vidéo"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Accepter la demande de réception vidéo"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Refuser la demande de réception vidéo"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Faites glisser votre doigt vers le haut pour <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Faites glisser votre doigt vers la gauche pour <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Faites glisser votre doigt vers la droite pour <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Faire glisser le doigt vers le bas : <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibration"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibration"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Son"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Son par défaut (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Sonnerie du téléphone"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrer lorsque téléphone sonne"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Sonnerie et vibreur"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Gérer la conférence"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Numéro d\'urgence"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Photo de profil"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Appareil photo désactivé"</string>
- <string name="child_number" msgid="4469090994612105532">"au moyen du <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Note envoyée"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Messages récents"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Renseignements sur l\'entreprise"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"À <xliff:g id="DISTANCE">%.1f</xliff:g> mi"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"À <xliff:g id="DISTANCE">%.1f</xliff:g> km"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"De <xliff:g id="OPEN_TIME">%1$s</xliff:g> à <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Ouvre demain à <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Ouvre aujourd\'hui à <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Ferme à <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"A fermé aujourd\'hui à <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Ouvert"</string>
- <string name="closed_now" msgid="2635314668145282080">"Fermé"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Appel suspect"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Appel terminé %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"C\'est la première fois que ce numéro vous appelle."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Cet appel nous semblait suspect."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Sign. appel suspect"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Ajouter un contact"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"N\'est pas suspect"</string>
-</resources>
diff --git a/InCallUI/res/values-fr/strings.xml b/InCallUI/res/values-fr/strings.xml
deleted file mode 100644
index 521fedb64..000000000
--- a/InCallUI/res/values-fr/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Téléphone"</string>
- <string name="onHold" msgid="527593602772521700">"En attente"</string>
- <string name="unknown" msgid="3646075119047488748">"Inconnu"</string>
- <string name="private_num" msgid="6081418498487514686">"Numéro privé"</string>
- <string name="payphone" msgid="5743050584468748607">"Cabine téléphonique"</string>
- <string name="confCall" msgid="3181961445236675173">"Conférence téléphonique"</string>
- <string name="call_lost" msgid="8208184291640961172">"Appel interrompu"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Haut-parleur"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Écouteur du combiné"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Casque filaire"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Envoyer les tonalités suivantes ?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Envoi des tonalités…\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Envoyer"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Oui"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Non"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Remplacer le caractère générique par"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Conférence téléphonique à <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"N° de la messagerie vocale"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Appel…"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Rappel…"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Conférence téléphonique"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Appel entrant"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Appel profession. entrant"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Appel terminé"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"En attente"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Fin de l\'appel…"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Appel en cours"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Mon numéro est le <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Connexion de la vidéo…"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Appel vidéo"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Demande de vidéo…"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Impossible d\'établir la connexion de l\'appel vidéo."</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Demande d\'appel vidéo refusée"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Votre numéro de rappel\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Votre numéro de rappel d\'urgence\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Appel…"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Appel manqué"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Appels manqués"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> appels manqués"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Appel manqué de <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Appel en cours"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Appel professionnel en cours"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Appel Wi-Fi en cours"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Appel Wi-Fi professionnel en cours"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"En attente"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Appel entrant"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Appel professionnel entrant"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Appel Wi-Fi entrant"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Appel Wi-Fi professionnel entrant"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Appel vidéo entrant"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Appel entrant indésirable suspecté"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Demande de vidéo reçue"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Nouveau message vocal"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Nouveaux messages vocaux (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Composer le <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Numéro de messagerie vocale inconnu"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Aucun service"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Réseau sélectionné (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) non disponible"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Répondre"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Raccrocher"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Vidéo"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Appel vocal"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Accepter"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Fermer"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Rappeler"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Envoyer SMS"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Appel en cours sur un autre appareil"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transférer l\'appel"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Veuillez désactiver le mode Avion avant de passer un appel."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Non enregistré sur le réseau."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Réseau mobile indisponible."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Pour émettre un appel, veuillez saisir un numéro valide."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Impossible d\'émettre l\'appel."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Lancement de la séquence IHM…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Service non compatible."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Impossible de changer d\'appel."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Impossible d\'isoler l\'appel."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Transfert impossible."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Impossible de lancer une conférence téléphonique."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Impossible de refuser l\'appel."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Impossible de lancer les appels."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Appel SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Appel d\'urgence"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Activation du signal radio…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Aucun service disponible. Nouvelle tentative…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Impossible d\'émettre l\'appel. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> n\'est pas un numéro d\'urgence."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Impossible d\'émettre l\'appel. Veuillez composer un numéro d\'urgence."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Utilisez le clavier pour composer un numéro."</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Mettre l\'appel en attente"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Reprendre l\'appel"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Mettre fin à l\'appel"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Afficher le clavier"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Masquer le clavier"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Couper le son"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Réactiver le son"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Ajouter un appel"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Fusionner les appels"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Permuter"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Gérer les appels"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Gérer conférence téléphonique"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Conférence téléphonique"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Gérer"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Appel vidéo"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Passer à un appel vocal"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Changer de caméra"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Activer la caméra"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Désactiver la caméra"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Plus d\'options"</string>
- <string name="player_started" msgid="3478865572468310331">"Le lecteur a démarré."</string>
- <string name="player_stopped" msgid="1278611664986561535">"Le lecteur s\'est arrêté."</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"La caméra n\'est pas prête"</string>
- <string name="camera_ready" msgid="2614541247814590887">"La caméra est prête"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Événement de session d\'appel inconnu"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Service"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Configuration"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Non défini&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Autres paramètres d\'appel"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Appel via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Appel entrant via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"photo du contact"</string>
- <string name="goPrivate" msgid="3554069451018659483">"mode privé"</string>
- <string name="selectContact" msgid="92191462970821951">"sélectionner un contact"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Réponse personnalisée…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Annuler"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Envoyer"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Répondre"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Envoyer un SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Refuser"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Répondre via un appel vidéo"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Répondre via un appel audio"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Accepter la demande d\'appel vidéo"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Refuser la demande d\'appel vidéo"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Accepter la demande de transmission d\'appel vidéo"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Refuser la demande de transmission d\'appel vidéo"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Accepter la demande de réception d\'appel vidéo"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Refuser la demande de réception d\'appel vidéo"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Faites glisser vers le haut pour <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Faites glisser vers la gauche pour <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Faites glisser vers la droite pour <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Faites glisser vers le bas pour <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibreur"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibreur"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Sonnerie"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Sonnerie par défaut (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Sonnerie du téléphone"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibreur lorsque le tél. sonne"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Sonnerie et vibreur"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Gérer la conférence téléphonique"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Numéro d\'urgence"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Photo du profil"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Caméra désactivée"</string>
- <string name="child_number" msgid="4469090994612105532">"via le <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"La note a bien été envoyée."</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Messages récents"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informations sur l\'établissement"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"À <xliff:g id="DISTANCE">%.1f</xliff:g> mi"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"À <xliff:g id="DISTANCE">%.1f</xliff:g> km"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> – <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Ouvre demain à <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Ouvre aujourd\'hui à <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Ferme à <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Fermé aujourd\'hui à <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Ouvert"</string>
- <string name="closed_now" msgid="2635314668145282080">"Fermé"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Appel indésirable suspecté"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Appel terminé %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"C\'est la première fois que vous recevez un appel de ce numéro."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Nous suspectons cet appel de provenir d\'un spammeur."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Bloquer/Signaler spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Ajouter un contact"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Numéro fiable"</string>
-</resources>
diff --git a/InCallUI/res/values-gl/strings.xml b/InCallUI/res/values-gl/strings.xml
deleted file mode 100644
index 8968946e6..000000000
--- a/InCallUI/res/values-gl/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Teléfono"</string>
- <string name="onHold" msgid="527593602772521700">"En espera"</string>
- <string name="unknown" msgid="3646075119047488748">"Descoñecido"</string>
- <string name="private_num" msgid="6081418498487514686">"Número privado"</string>
- <string name="payphone" msgid="5743050584468748607">"Teléfono público"</string>
- <string name="confCall" msgid="3181961445236675173">"Conferencia telefónica"</string>
- <string name="call_lost" msgid="8208184291640961172">"Chamada interrompida"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Altofalante"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Auricular do teléfono"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Auriculares con cable"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Queres enviar os seguintes tons?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Enviando tons\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Enviar"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Si"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Non"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Substituír carácter comodín por"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Conferencia telefónica ás <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Número de correo de voz"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Marcando"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Marcando de novo"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Conferencia telefónica"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Chamada entrante"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Chamada traballo entrante"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Chamada finalizada"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"En espera"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Desconectando"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Chamada entrante"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"O meu número é o <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Conectando vídeo"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videochamada"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Solicitando vídeo"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Non se pode conectar a videochamada"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Rexeitouse a solicitude de vídeo"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"O teu número de devolución de chamada\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"O teu número de devolución de chamada de emerxencia\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Marcando"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Chamada perdida"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Chamadas perdidas"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> chamadas perdidas"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Chamada perdida de <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Chamada en curso"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Chamada de traballo saínte"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Chamada por wifi saínte"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Chamada por wifi de traballo saínte"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"En espera"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Chamada entrante"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Chamada de traballo entrante"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Chamada por wifi entrante"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Chamada wifi de traballo entrante"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Videochamada entrante"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Chamada entrante sospeitosa de spam"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Solicitude de vídeo entrante"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Correo de voz novo"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Correo de voz novo (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Marcar o <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Número de correo de voz descoñecido"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Sen servizo"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"A rede seleccionada (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) non está dispoñible"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Responder"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Colgar"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Vídeo"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Voz"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Aceptar"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Ignorar"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Dev. chamada"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Mensaxe"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Chamada en curso noutro dispositivo"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transferir chamada"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Para realizar unha chamada, primeiro desactiva o modo avión."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Sen rexistro na rede."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Rede móbil non dispoñible."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Para realizar unha chamada, introduce un número válido."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Non se pode realizar a chamada."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Iniciando secuencia MMI..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Servizo non compatible."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Non se poden cambiar as chamadas."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Non se pode separar a chamada."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Non se pode transferir."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Non se pode realizar a conferencia."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Non se pode rexeitar a chamada."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Non se poden desconectar as chamadas."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Chamada SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Chamada de emerxencia"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Activando radio..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Sen servizo. Tentándoo de novo…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Non se pode realizar a chamada. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> non é un número de emerxencia."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Non se pode realizar a chamada. Marca un número de emerxencia."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Utiliza o teclado para marcar"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Poñer a chamada en espera"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Retomar chamada"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Finalizar chamada"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Mostrar teclado de marcación"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Ocultar teclado de marcación"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Silenciar"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Activar o son"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Engadir chamada"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Combinar chamadas"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Cambiar"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Xestionar chamadas"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Xestionar confer. telefónica"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Conferencia telefónica"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Xestionar"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videocham."</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Cambiar para chamada de voz"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Cambiar cámara"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Acender cámara"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Apagar cámara"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Máis opcións"</string>
- <string name="player_started" msgid="3478865572468310331">"Iniciouse o reprodutor"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Detívose o reprodutor"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"A cámara non está preparada"</string>
- <string name="camera_ready" msgid="2614541247814590887">"A cámara está preparada"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Evento de sesión de chamada descoñecido"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Servizo"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Configuración"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Sen configurar&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Outras configuracións de chamada"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Chamando a través de <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Chamadas entrantes a través de <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"foto do contacto"</string>
- <string name="goPrivate" msgid="3554069451018659483">"activar o modo privado"</string>
- <string name="selectContact" msgid="92191462970821951">"seleccionar contacto"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Escribe a túa propia..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Cancelar"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Enviar"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Responder"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Enviar SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Rexeitar"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Responde como videochamada"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Responde como chamada de audio"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Acepta a solicitude de vídeo"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Rexeita a solicitude de vídeo"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Acepta a solicitude de transmisión de vídeo"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Rexeita a solicitude de transmisión de vídeo"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Acepta a solicitude de recepción de vídeo"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Rexeita a solicitude de recepción de vídeo"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Pasa o dedo cara a arriba para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Pasa o dedo cara a esquerda para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Pasa o dedo cara a dereita para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Pasa o dedo cara a abaixo para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrar"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrar"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Son"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Son predeterminado (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Ton de chamada do teléfono"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrar ao soar"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Ton de chamada e vibración"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Xestionar conferencia telefónica"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Número de emerxencia"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Foto do perfil"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"A cámara está desactivada"</string>
- <string name="child_number" msgid="4469090994612105532">"a través do <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Enviouse a nota"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Mensaxes recentes"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Información da empresa"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"A <xliff:g id="DISTANCE">%.1f</xliff:g> mi de distancia"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"A <xliff:g id="DISTANCE">%.1f</xliff:g> km de distancia"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Abre mañá ás <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Abre hoxe ás <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Pecha ás <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Pechou hoxe ás <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Aberto agora"</string>
- <string name="closed_now" msgid="2635314668145282080">"Pechado agora"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Chamada sospeitosa"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Finalizouse a chamada %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"É a primeira vez que te chama este número."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Sospeitamos que esta chamada era un xerador de spam."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Bloquear/marcar spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Engadir contacto"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Non é spam"</string>
-</resources>
diff --git a/InCallUI/res/values-gu/strings.xml b/InCallUI/res/values-gu/strings.xml
deleted file mode 100644
index 017ccb691..000000000
--- a/InCallUI/res/values-gu/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"ફોન"</string>
- <string name="onHold" msgid="527593602772521700">"હોલ્ડ પર"</string>
- <string name="unknown" msgid="3646075119047488748">"અજાણ્યો"</string>
- <string name="private_num" msgid="6081418498487514686">"ખાનગી નંબર"</string>
- <string name="payphone" msgid="5743050584468748607">"પેફોન"</string>
- <string name="confCall" msgid="3181961445236675173">"કોન્ફરન્સ કૉલ"</string>
- <string name="call_lost" msgid="8208184291640961172">"કૉલ કપાઇ ગયો"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"સ્પીકર"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"હેન્ડસેટ ઇયરપીસ"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"વાયર્ડ હેડસેટ"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"નીચે આપેલ ટોન્સ મોકલીએ?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"ટોન્સ મોકલી રહ્યાં છે\n"</string>
- <string name="send_button" msgid="4054398309483035794">"મોકલો"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"હા"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"નહીં"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"વાઇલ્ડ અક્ષરને આની સાથે બદલો"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"કોન્ફરન્સ કૉલ <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"વૉઇસમેઇલ નંબર"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"ડાયલ કરી રહ્યાં છે"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"ફરી ડાયલ કરી રહ્યાં છે"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"કોન્ફરન્સ કૉલ"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"ઇનકમિંગ કૉલ"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"ઇનકમિંગ કાર્ય કૉલ"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"કૉલ સમાપ્ત થયો"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"હોલ્ડ પર"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"સમાપ્ત કરી રહ્યાં છે"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"કૉલમાં"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"મારો નંબર <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g> છે"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"વિડિઓ કનેક્ટ કરી રહ્યાં છે"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"વિડિઓ કૉલ"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"વિડિઓની વિનંતી કરી રહ્યાં છે"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"વિડિઓ કૉલ કનેક્ટ કરી શકાતો નથી"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"વિડિઓ વિનંતી નકારી"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"તમારો કૉલબેક નંબર\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"તમારો કટોકટીનો કૉલબેક નંબર\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"ડાયલ કરી રહ્યાં છે"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"છૂટેલો કૉલ"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"છૂટેલા કૉલ્સ"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> છૂટેલા કૉલ"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> નો કૉલ ચૂકી ગયાં"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"ચાલી રહેલ કૉલ"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"ચાલી રહેલ કાર્ય કૉલ"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"ચાલી રહેલ Wi-Fi કૉલ"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"ચાલી રહેલ Wi-Fi કાર્ય કૉલ"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"હોલ્ડ પર"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"ઇનકમિંગ કૉલ"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"ઇનકમિંગ કાર્ય કૉલ"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"ઇનકમિંગ Wi-Fi કૉલ"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"ઇનકમિંગ Wi-Fi કાર્ય કૉલ"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"ઇનકમિંગ વિડિઓ કૉલ"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"ઇનકમિંગ શંકાસ્પદ સ્પામ કૉલ"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"ઇનકમિંગ વિડિઓ વિનંતી"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"નવો વૉઇસમેઇલ"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"નવો વૉઇસમેઇલ (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> ડાયલ કરો"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"વૉઇસમેઇલ નંબર અજાણ"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"કોઈ સેવા નથી"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"પસંદ કરેલ નેટવર્ક (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) અનુપલબ્ધ"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"જવાબ"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"સમાપ્ત કરો"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"વિડિઓ"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"વૉઇસ"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"સ્વીકારો"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"છોડી દો"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"કૉલ બૅક કરો"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"સંદેશ"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"અન્ય ઉપકરણ પર ચાલી રહેલ કૉલ"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"કૉલ સ્થાનાંતરિત કરો"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"કૉલ કરવા માટે, પહેલા એરપ્લેન મોડને બંધ કરો."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"નેટવર્ક પર નોંધણી કરાયેલ નથી."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"સેલ્યુલર નેટવર્ક ઉપલબ્ધ નથી."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"કૉલ કરવા માટે, માન્ય નંબર દાખલ કરો."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"કૉલ કરી શકાતો નથી."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI અનુક્રમ પ્રારંભ કરી રહ્યાં છે…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"સેવા સમર્થિત નથી."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"કૉલ્સ સ્વિચ કરી શકાતાં નથી."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"અલગ કૉલ કરી શકાતો નથી."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"ટ્રાંસ્ફર કરી શકાતો નથી."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"કોન્ફરન્સ કરી શકાતી નથી."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"કૉલ નકારી શકાતો નથી."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"કૉલ(કૉલ્સ) રિલીઝ કરી શકતાં નથી."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP કૉલ"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"કટોકટીનો કૉલ"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"રેડિઓ ચાલુ કરી રહ્યાં છે…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"કોઈ સેવા નથી. ફરી પ્રયાસ કરી રહ્યાં છે…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"કૉલ કરી શકાતો નથી. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> એ કટોકટીનો નંબર નથી."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"કૉલ કરી શકાતો નથી. કટોકટીનો નંબર ડાયલ કરો."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"ડાયલ કરવા માટે કીબોર્ડનો ઉપયોગ કરો"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"કૉલ હોલ્ડ કરો"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"કૉલ ફરી શરૂ કરો"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"કૉલ સમાપ્ત કરો"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"ડાયલપેડ બતાવો"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"ડાયલપેડ છુપાવો"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"મ્યૂટ કરો"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"અનમ્યૂટ કરો"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"કૉલ ઉમેરો"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"કૉલ્સ મર્જ કરો"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"સ્વેપ કરો"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"કૉલ્સ સંચાલિત કરો"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"કોન્ફરન્સ કૉલ સંચાલિત કરો"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"કોન્ફરન્સ કૉલ"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"સંચાલિત કરો"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"ઑડિઓ"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"વિડિઓ કૉલ"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"વૉઇસ કૉલ પર બદલો"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"કૅમેરા પર સ્વિચ કરો"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"કૅમેરો ચાલુ કરો"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"કૅમેરો બંધ કરો"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"વધુ વિકલ્પો"</string>
- <string name="player_started" msgid="3478865572468310331">"પ્લેયર પ્રારંભ કર્યું"</string>
- <string name="player_stopped" msgid="1278611664986561535">"પ્લેયર બંધ કર્યું"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"કૅમેરો તૈયાર નથી"</string>
- <string name="camera_ready" msgid="2614541247814590887">"કૅમેરો તૈયાર"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"અજાણી કૉલ સત્ર ઇવેન્ટ"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"સેવા"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"સેટઅપ"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;સેટ કરેલ નથી&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"અન્ય કૉલ સેટિંગ્સ"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> મારફતે કૉલ કરે છે"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> મારફતે ઇનકમિંગ"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"સંપર્ક ફોટો"</string>
- <string name="goPrivate" msgid="3554069451018659483">"ખાનગી થાઓ"</string>
- <string name="selectContact" msgid="92191462970821951">"સંપર્ક પસંદ કરો"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"તમારો પોતાનો લખો…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"રદ કરો"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"મોકલો"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"જવાબ"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS મોકલો"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"નકારો"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"વિડિઓ કૉલ તરીકે જવાબ આપો"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"ઑડિઓ કૉલ તરીકે જવાબ આપો"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"વિડિઓ વિનંતી સ્વીકારો"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"વિડિઓ વિનંતી નકારો"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"વિડિઓ ટ્રાંસ્મિટ વિનંતી સ્વીકારો"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"વિડિઓ ટ્રાંસ્મિટ વિનંતી નકારો"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"વિડિઓ પ્રાપ્તિ વિનંતી સ્વીકારો"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"વિડિઓ પ્રાપ્તિ વિનંતી નકારો"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> માટે ઉપર સ્લાઇડ કરો."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> માટે ડાબે સ્લાઇડ કરો."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> માટે જમણે સ્લાઇડ કરો."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> માટે નીચે સ્લાઇડ કરો."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"વાઇબ્રેટ"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"વાઇબ્રેટ"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"ધ્વનિ"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"ડિફોલ્ટ ધ્વનિ (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"ફોન રિંગટોન"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"જ્યારે રિંગ કરે ત્યારે વાઇબ્રેટ કરો"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"રિંગટોન અને વાઇબ્રેટ"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"કોન્ફરન્સ કૉલ સંચાલિત કરો"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"કટોકટીનો નંબર"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"પ્રોફાઇલ ફોટો"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"કૅમેરો બંધ"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> મારફતે"</string>
- <string name="note_sent" msgid="7623014827902758398">"નોંધ મોકલી"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"તાજેતરનાં સંદેશા"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"વ્યવસાયની માહિતી"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> માઇલ દૂર"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> કિમી દૂર"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"આવતીકાલે <xliff:g id="OPEN_TIME">%s</xliff:g> વાગ્યે ખુલશે"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"આજે <xliff:g id="OPEN_TIME">%s</xliff:g> વાગ્યે ખુલશે"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g> વાગ્યે બંધ થશે"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"આજે <xliff:g id="CLOSE_TIME">%s</xliff:g> વાગ્યે બંધ થયેલું"</string>
- <string name="open_now" msgid="4615706338669555999">"હમણાં ખુલ્લું"</string>
- <string name="closed_now" msgid="2635314668145282080">"હમણાં બંધ છે"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"શંકાસ્પદ સ્પામ કૉલર"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"%1$s નો કૉલ સમાપ્ત થયો"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"આ નંબરથી તમને પહેલી વાર કૉલ કરવામાં આવ્યો છે."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"આ કૉલ કોઈ સ્પામર હોવાની અમને શંકા છે."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"સ્પામની જાણ/અવરોધિત કરો"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"સંપર્ક ઉમેરો"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"સ્પામ નથી"</string>
-</resources>
diff --git a/InCallUI/res/values-h400dp/dimens.xml b/InCallUI/res/values-h400dp/dimens.xml
deleted file mode 100644
index dda755a3e..000000000
--- a/InCallUI/res/values-h400dp/dimens.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-<resources>
- <!-- Whether or not this layout displays a large photo. -->
- <bool name="has_large_photo">true</bool>
-
- <dimen name="call_banner_height">90dp</dimen>
-
- <dimen name="end_call_button_margin_bottom">15dp</dimen>
-
- <dimen name="floating_action_button_vertical_offset">-24dp</dimen>
-
- <dimen name="dialpad_elevation">2dp</dimen>
-
- <dimen name="video_preview_margin">20dp</dimen>
-</resources>
diff --git a/InCallUI/res/values-hi/strings.xml b/InCallUI/res/values-hi/strings.xml
deleted file mode 100644
index dee8f464d..000000000
--- a/InCallUI/res/values-hi/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"फ़ोन"</string>
- <string name="onHold" msgid="527593602772521700">"होल्ड पर"</string>
- <string name="unknown" msgid="3646075119047488748">"अज्ञात"</string>
- <string name="private_num" msgid="6081418498487514686">"निजी नंबर"</string>
- <string name="payphone" msgid="5743050584468748607">"पे-फ़ोन"</string>
- <string name="confCall" msgid="3181961445236675173">"कॉन्फ़्रेंस कॉल"</string>
- <string name="call_lost" msgid="8208184291640961172">"कॉल कट गया"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"स्पीकर"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"हैंडसेट इयरपीस"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"वायर वाला हैडसेट"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"ब्लूटूथ"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"निम्न टोन भेजें?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"भेजने वाली टोन\n"</string>
- <string name="send_button" msgid="4054398309483035794">"भेजें"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"हां"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"नहीं"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"वाइल्ड वर्ण को इससे बदलें:"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"कॉन्फ़्रेंस कॉल <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"वॉइसमेल नबंर"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"डायल किया जा रहा है"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"पुन: डायल हो रहा है"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"कॉन्फ़्रेंस कॉल"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"इनकमिंग कॉल"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"कार्यस्थल का इनकमिंग कॉल"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"कॉल समाप्त"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"होल्ड पर"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"कॉल समाप्त हो रहा है"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"कॉल में"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"मेरा नंबर <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g> है"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"वीडियो कनेक्ट हो रहा है"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"वीडियो कॉल"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"वीडियो का अनुरोध किया जा रहा है"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"वीडियो कॉल कनेक्ट नहीं किया जा सकता"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"वीडियो अनुरोध अस्वीकार किया गया"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"आपका कॉलबैक नंबर\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"आपका आपातकालीन कॉलबैक नंबर\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"डायल किया जा रहा है"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"छूटा कॉल"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"छूटे कॉल"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> छूटे कॉल"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> के छूटे कॉल"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"चल रहा कॉल"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"कार्यस्थल का जारी कॉल"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"चल रहा वाई-फ़ाई कॉल"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"कार्यस्थल का जारी वाई-फ़ाई कॉल"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"होल्ड पर"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"इनकमिंग कॉल"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"कार्यस्थल का इनकमिंग कॉल"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"इनकमिंग वाई-फ़ाई कॉल"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"कार्यस्थल का वाई-फ़ाई इनकमिंग कॉल"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"इनकमिंग वीडियो कॉल"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"संदिग्ध आवक स्पैम कॉल"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"इनकमिंग वीडियो अनुरोध"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"नया वॉइसमेल"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"नया वॉइसमेल (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> डायल करें"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"वॉइसमेल नंबर अज्ञात"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"कोई सेवा नहीं"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"चयनित नेटवर्क (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) अनुपलब्ध"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"उत्तर दें"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"समाप्त करें"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"वीडियो"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"ध्वनि"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"स्वीकार करें"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"ख़ारिज करें"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"कॉल बैक करें"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"संदेश"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"दूसरे डिवाइस पर चल रहा कॉल"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"कॉल स्थानान्तरित करें"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"कॉल करने के लिए, पहले हवाई जहाज़ मोड बंद करें."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"नेटवर्क पर पंजीकृत नहीं."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"सेल्युलर नेटवर्क उपलब्ध नहीं."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"कॉल करने के लिए, मान्य नंबर डालें."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"कॉल नहीं किया जा सकता."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI अनुक्रम प्रारंभ हो रहा है…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"सेवा समर्थित नहीं है."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"कॉल स्विच नहीं किए जा सकते."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"कॉल अलग नहीं किया जा सकता."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"स्थानान्तरित नहीं किया जा सकता."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"कॉन्फ़्रेंस नहीं की जा सकती."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"कॉल अस्वीकार नहीं किया जा सकता."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"कॉल रिलीज़ नहीं किया जा सकता (किए जा सकते)."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP कॉल"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"आपातकालीन कॉल"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"रेडियो चालू कर रहा है..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"कोई सेवा नहीं. पुन: प्रयास किया जा रहा है…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"कॉल नहीं किया जा सकता. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> एक आपातकालीन नंबर नहीं है."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"कॉल नहीं किया जा सकता. आपातकालीन नबर डायल करें."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"डायल करने के लिए कीबोर्ड का उपयोग करें"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"कॉल होल्ड करें"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"कॉल फिर से शुरू करें"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"कॉल समाप्त करें"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"डायलपैड दिखाएं"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"डायलपैड छिपाएं"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"म्यूट करें"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"अनम्यूट करें"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"कॉल जोड़ें"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"कॉल मर्ज करें"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"स्वैप करें"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"कॉल प्रबंधित करें"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"कॉन्फ़्रेंस कॉल प्रबंधित करें"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"कॉन्फ़्रेंस कॉल"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"प्रबंधित करें"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"ऑडियो"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"वीडियो कॉल"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"वॉइस कॉल में बदलें"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"कैमरा स्विच करें"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"कैमरा चालू करें"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"कैमरा बंद करें"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"अधिक विकल्प"</string>
- <string name="player_started" msgid="3478865572468310331">"प्लेयर प्रारंभ हो गया"</string>
- <string name="player_stopped" msgid="1278611664986561535">"प्लेयर रुक गया"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"कैमरा तैयार नहीं है"</string>
- <string name="camera_ready" msgid="2614541247814590887">"कैमरा तैयार है"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"अज्ञात कॉल सत्र इवेंट"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"सेवा"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"सेटअप"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;सेट नहीं है&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"अन्य कॉल सेटिंग"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> के माध्यम से कॉल किया जा रहा है"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> की ओर से इनकमिंग"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"संपर्क का फ़ोटो"</string>
- <string name="goPrivate" msgid="3554069451018659483">"निजी हो जाएं"</string>
- <string name="selectContact" msgid="92191462970821951">"संपर्क को चुनें"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"अपना स्वयं का लिखें..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"अभी नहीं"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"भेजें"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"उत्तर दें"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS भेजें"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"अस्वीकार करें"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"वीडियो कॉल के रूप में उत्तर दें"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"ऑडियो कॉल के रूप में उत्तर दें"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"वीडियो अनुरोध स्वीकार करें"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"वीडियो अनुरोध अस्वीकार करें"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"वीडियो प्रसारण अनुरोध स्वीकार करें"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"वीडियो प्रसारण अनुरोध अस्वीकार करें"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"वीडियो प्राप्ति अनुरोध स्वीकार करें"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"वीडियो प्राप्ति अनुरोध अस्वीकार करें"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> करने के लिए ऊपर स्लाइड करें."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> करने के लिए बाएं स्लाइड करें."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> करने के लिए दाएं स्लाइड करें."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> करने के लिए नीचे स्लाइड करें."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"कंपन"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"कंपन"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"ध्वनि"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"डिफ़ॉल्ट ध्वनि (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"फ़ोन रिंगटोन"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"रिंग आने पर कंपन करें"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"रिंगटोन और कंपन"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"कॉन्फ़्रेंस कॉल प्रबंधित करें"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"आपातकालीन नंबर"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"प्रोफ़ाइल फ़ोटो"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"कैमरा बंद है"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> के द्वारा"</string>
- <string name="note_sent" msgid="7623014827902758398">"नोट भेज दिया गया है"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"हाल ही के संदेश"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"व्यवसाय जानकारी"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> मील दूर"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> किमी दूर"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"कल <xliff:g id="OPEN_TIME">%s</xliff:g> बजे खुलेगा"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"आज <xliff:g id="OPEN_TIME">%s</xliff:g> बजे खुलता है"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g> बजे बंद होता है"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"आज <xliff:g id="CLOSE_TIME">%s</xliff:g> बजे बंद हो गया"</string>
- <string name="open_now" msgid="4615706338669555999">"अभी खुला है"</string>
- <string name="closed_now" msgid="2635314668145282080">"अभी बंद है"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"संदिग्ध स्पैम कॉलर"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"%1$s का कॉल समाप्त हो गया"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"इस नंबर से आपको पहली बार कॉल किया गया है."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"हमें आशंका है कि कॉल स्पैमकर्ता का हो सकता है."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"अवरुद्ध करें/स्पैम रिपोर्ट करें"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"संपर्क जोड़ें"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"स्पैम नहीं है"</string>
-</resources>
diff --git a/InCallUI/res/values-hr/strings.xml b/InCallUI/res/values-hr/strings.xml
deleted file mode 100644
index 9c11644c4..000000000
--- a/InCallUI/res/values-hr/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"Na čekanju"</string>
- <string name="unknown" msgid="3646075119047488748">"Nepoznato"</string>
- <string name="private_num" msgid="6081418498487514686">"Privatni broj"</string>
- <string name="payphone" msgid="5743050584468748607">"Javna telefonska govornica"</string>
- <string name="confCall" msgid="3181961445236675173">"Konferencijski poziv"</string>
- <string name="call_lost" msgid="8208184291640961172">"Poziv je prekinut"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Zvučnik"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Slušalice"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Žičane slušalice"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Poslati sljedeće tonove?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Slanje tonova\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Pošalji"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Da"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Ne"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Zamijeni zamjenski znak znakom"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konferencijski poziv <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Broj govorne pošte"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Biranje broja"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Ponovno biranje"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konferencijski poziv"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Dolazni poziv"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Dolazni poslovni poziv"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Poziv je završio"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Na čekanju"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Prekidanje veze"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Poziv u tijeku"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Moj je broj <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Uspostavljanje videopoziva"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videopoziv"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Zahtijevanje videopoziva"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Videopoziv nije uspostavljen"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Zahtjev za videopoziv odbijen"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Vaš broj za povratni poziv\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Vaš broj za povratni poziv za hitne službe\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Biranje broja"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Propušteni poziv"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Propušteni pozivi"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"Propušteni pozivi (<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>)"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Propušten poziv kontakta <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Poziv u tijeku"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Poslovni poziv u tijeku"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Wi-Fi poziv u tijeku"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Poslovni Wi-Fi poziv u tijeku"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Na čekanju"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Dolazni poziv"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Dolazni poslovni poziv"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Dolazni Wi-Fi poziv"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Dolazni poslovni Wi-Fi poziv"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Dolazni videopoziv"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Mogući neželjeni dolazni poziv"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Dolazni zahtjev za videopoziv"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Nova govorna pošta"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Nova govorna pošta (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Biraj <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Nepoznat broj govorne pošte"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Nema usluge"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Odabrana mreža (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) nije dostupna"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Odgovori"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Prekini vezu"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Videopoziv"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Glasovni poziv"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Prihvati"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Odbaci"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Uzvrati"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Poruka"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Poziv u tijeku na drugom uređaju"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Prijenos poziva"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Da biste uspostavili poziv, isključite način rada u zrakoplovu."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Nije registrirano na mreži."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobilna mreža nije dostupna."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Unesite važeći broj da biste uspostavili poziv."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Pozivanje nije moguće."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Pokretanje MMI sekvence…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Usluga nije podržana."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Prebacivanje poziva nije moguće."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Odvajanje poziva nije moguće."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Prijenos nije moguć."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Konferencijski poziv nije moguć."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Odbijanje poziva nije moguće."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Prekidanje poziva nije moguće."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP poziv"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Hitni poziv"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Uključivanje radija…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Nema usluge. Pokušavamo ponovo…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Pozivanje nije moguće. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> nije broj hitne službe."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Pozivanje nije moguće. Nazovite broj hitne službe."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Upotrijebite tipkovnicu"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Stavi poziv na čekanje"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Nastavi poziv"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Završi poziv"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Prikaži površinu za biranje brojeva"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Sakrij površinu za biranje brojeva"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Zanemari"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Prestani zanemarivati"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Dodaj poziv"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Spoji pozive"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Zamijeni"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Upravljaj pozivima"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Upravljaj konf. pozivom"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferencijski poziv"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Upravljanje"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videopoziv"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Prijeđi na glasovni poziv"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Promijeni kameru"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Uključivanje kamere"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Isključivanje kamere"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Više opcija"</string>
- <string name="player_started" msgid="3478865572468310331">"Player je pokrenut"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Player je prekinut"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Fotoaparat nije spreman"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Fotoaparat je spreman"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Nepoznati događaj sesije poziva"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Usluga"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Postavljanje"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Nije postavljeno&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Ostale postavke poziva"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Pozivanje putem operatera <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Dolazni pozivi putem usluge <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"fotografija kontakta"</string>
- <string name="goPrivate" msgid="3554069451018659483">"uputi na privatno"</string>
- <string name="selectContact" msgid="92191462970821951">"odabir kontakta"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Napišite odgovor..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Odustani"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Pošalji"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Odgovori"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Pošalji SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Odbij"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Prihvati kao videopoziv"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Prihvati kao audiopoziv"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Prihvati zahtjev za videopoziv"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Odbij zahtjev za videopoziv"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Prihvati zahtjev za slanje videopoziva"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Odbij zahtjev za slanje videopoziva"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Prihvati zahtjev za primanje videopoziva"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Odbij zahtjev za primanje videopoziva"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Kliznite prema gore za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Kliznite lijevo za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Kliznite desno za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Kliznite prema dolje za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibriranje"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibriranje"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Zvuk"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Zadani zvuk (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Melodija zvona telefona"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibracija tijekom zvonjenja"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Melodija zvona i vibracija"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Upravljaj konferencijskim pozivom"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Broj hitne službe"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Fotografija profila"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Fotoaparat je isključen"</string>
- <string name="child_number" msgid="4469090994612105532">"putem broja <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Bilješka je poslana"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Nedavne poruke"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informacije o tvrtki"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> mi udaljenosti"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km udaljenosti"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> – <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Otvara se sutra u <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Otvara se danas u <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Zatvara se u <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Zatvoreno danas u <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Trenutačno otvoreno"</string>
- <string name="closed_now" msgid="2635314668145282080">"Trenutačno zatvoreno"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Neželjeni pozivatelj"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Poziv je završio %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Prvi ste put primili poziv s tog broja."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Sumnjamo da vas zove pošiljatelj neželjenih sadržaja."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blokiranje/prijava neželjenog broja"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Dodavanje kontakta"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Nije neželjeni broj"</string>
-</resources>
diff --git a/InCallUI/res/values-hu/strings.xml b/InCallUI/res/values-hu/strings.xml
deleted file mode 100644
index 816942601..000000000
--- a/InCallUI/res/values-hu/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"Tartásban"</string>
- <string name="unknown" msgid="3646075119047488748">"Ismeretlen"</string>
- <string name="private_num" msgid="6081418498487514686">"Magántelefonszám"</string>
- <string name="payphone" msgid="5743050584468748607">"Nyilvános telefon"</string>
- <string name="confCall" msgid="3181961445236675173">"Konferenciahívás"</string>
- <string name="call_lost" msgid="8208184291640961172">"A hívás megszakadt"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Hangszóró"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Kézibeszélő fülhallgatója"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Vezetékes fülhallgató"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Elküldi a következő hangjelzéseket?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Hangjelzések küldése\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Küldés"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Igen"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Nem"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Helyettesítő karakter lecserélése a következőre:"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konferenciahívás – <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Hangposta száma"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Tárcsázás"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Újratárcsázás"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konferenciahívás"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Bejövő hívás"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Bejövő munkahelyi hívás"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"A hívás befejeződött"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Tartásban"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Megszakítás"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Hívásban"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"A számom: <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Videókapcsolat létrehozása"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videóhívás"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Videóhívást kér"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"A videóhívás létesítése sikertelen"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Videókérelem elutasítva"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Visszahívható telefonszáma:\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Vészhelyzet esetén visszahívható telefonszáma:\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Tárcsázás"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Nem fogadott hívás"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Nem fogadott hívások"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> nem fogadott hívás"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Nem fogadott hívás: <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Hívás folyamatban"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Folyamatban lévő munkahelyi hívás"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Folyamatban lévő Wi-Fi-hívás"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Folyamatban lévő munkahelyi hívás Wi-Fi-hálózaton keresztül"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Tartásban"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Bejövő hívás"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Bejövő munkahelyi hívás"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Bejövő Wi-Fi-hívás"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Bejövő munkahelyi hívás Wi-Fi-hálózaton keresztül"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Bejövő videóhívás"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Bejövő gyanús spamhívás"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Bejövő videókérés"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Új hangpostaüzenet"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Új hangpostaüzenet (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> tárcsázása"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"A hangposta száma ismeretlen"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Nincs szolgáltatás"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"A kiválasztott hálózat (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) nem áll rendelkezésre"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Fogadás"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Hívás bontása"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Videó"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Hang"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Elfogadás"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Elvetés"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Visszahívás"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Üzenet"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Folyamatban lévő hívás egy másik eszközön"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Hívásátirányítás"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Hívásindításhoz kapcsolja ki a Repülős üzemmódot."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Nincs regisztrálva a hálózaton."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"A mobilhálózat nem áll rendelkezésre."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Hívásindításhoz adjon meg egy érvényes számot."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"A hívás sikertelen."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI-sorozat indítása…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"A szolgáltatás nem támogatott."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"A hívások közötti váltás sikertelen."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"A híváselkülönítés sikertelen."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Az átirányítás sikertelen."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"A konferenciahívás sikertelen."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"A híváselutasítás sikertelen."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"A tartásban lévő hívás(ok) folytatása sikertelen."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP-hívás"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Segélyhívás"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Rádió bekapcsolása…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Nincs szolgáltatás. Újrapróbálkozás folyamatban…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"A hívás sikertelen. A(z) <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> szám nem segélyhívószám."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"A hívás sikertelen. Tárcsázzon segélyhívószámot."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"A tárcsázáshoz használja a billentyűzetet"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Hívás tartása"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Hívás folytatása"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Hívás befejezése"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Tárcsázó megjelenítése"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Tárcsázó elrejtése"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Némítás"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Némítás feloldása"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Hívás hozzáadása"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Hívások egyesítése"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Csere"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Hívások kezelése"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Konferenciahívás kezelése"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferenciahívás"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Kezelés"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Hang"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videóhívás"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Váltás hanghívásra"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Váltás a kamerák között"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Kamera bekapcsolása"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Kamera kikapcsolása"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"További lehetőségek"</string>
- <string name="player_started" msgid="3478865572468310331">"A lejátszó elindult"</string>
- <string name="player_stopped" msgid="1278611664986561535">"A lejátszó leállt"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"A kamera nem áll készen"</string>
- <string name="camera_ready" msgid="2614541247814590887">"A kamera készen áll"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Ismeretlen hívási esemény"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Szolgáltatás"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Beállítás"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Nincs megadva&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Egyéb hívásbeállítások"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Hívás a(z) <xliff:g id="PROVIDER_NAME">%s</xliff:g> szolgáltatón keresztül"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Bejövő hívás a következőn keresztül: <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"fotó a névjegyhez"</string>
- <string name="goPrivate" msgid="3554069451018659483">"magánbeszélgetés"</string>
- <string name="selectContact" msgid="92191462970821951">"névjegy kiválasztása"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Saját válasz írása…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Mégse"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Küldés"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Fogadás"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS küldése"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Elutasítás"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Fogadás videóhívásként"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Fogadás hanghívásként"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Videó kérésének elfogadása"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Videó kérésének elutasítása"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Videóküldési kérés elfogadása"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Videóküldési kérés elutasítása"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Videófogadási kérés elfogadása"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Videófogadási kérés elutasítása"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"A(z) <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> művelethez csúsztassa felfelé."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"A(z) <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> művelethez csúsztassa balra."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"A(z) <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> művelethez csúsztassa jobbra."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"A(z) <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> művelethez csúsztassa lefelé."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Rezgés"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Rezgés"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Hang"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Alapértelmezett hang (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Telefon csengőhangja"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Csörgéskor rezegjen"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Csengőhang és rezgés"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Konferenciahívás kezelése"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Segélyhívó szám"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profilfotó"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera ki"</string>
- <string name="child_number" msgid="4469090994612105532">"a következő számon keresztül: <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Üzenet elküldve"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Legutóbbi üzenetek"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Cég adatai"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> mérföldre"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> kilométerre"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="LOCALITY">%2$s</xliff:g>, <xliff:g id="STREET_ADDRESS">%1$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> – <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Holnap ekkor nyit: <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Ma ekkor nyit: <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Ekkor zár: <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Ma ekkor zárt: <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Jelenleg nyitva van"</string>
- <string name="closed_now" msgid="2635314668145282080">"Jelenleg zárva van"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Gyanús spamhívó"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Hívás befejezve: %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Ez az első alkalom, hogy erről a számról hívása érkezett."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Azt gyanítjuk, hogy ez egy spamhívás."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Letiltás/spam bejel."</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Névjegy hozzáadása"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Nem spam"</string>
-</resources>
diff --git a/InCallUI/res/values-hy/strings.xml b/InCallUI/res/values-hy/strings.xml
deleted file mode 100644
index 899262432..000000000
--- a/InCallUI/res/values-hy/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Հեռախոս"</string>
- <string name="onHold" msgid="527593602772521700">"Սպասում"</string>
- <string name="unknown" msgid="3646075119047488748">"Անհայտ"</string>
- <string name="private_num" msgid="6081418498487514686">"Գաղտնի համար"</string>
- <string name="payphone" msgid="5743050584468748607">"Հանրային հեռախոս"</string>
- <string name="confCall" msgid="3181961445236675173">"Կոնֆերանս զանգ"</string>
- <string name="call_lost" msgid="8208184291640961172">"Զանգն ընդհատվեց"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Բարձրախոս"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Հեռախոսի ականջակալ"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Լարային ականջակալ"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Ուղարկե՞լ հետևյալ ձայներանգները:\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Ձայներանգների ուղարկում\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Ուղարկել"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Այո"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Ոչ"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Փոխարինել կոպիտ գրանշանը"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Կոնֆերանս զանգ <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Ձայնային փոստի համարը"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Համարը հավաքվում է"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Վերահամարարկում"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Կոնֆերանս զանգ"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Մուտքային զանգ"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Մուտքային աշխատանքային զանգ"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Զանգն ավարտվեց"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Սպասում"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Անջատում"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Զանգը միացված է"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Իմ համարը՝ <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Տեսակապը միանում է"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Տեսազանգ"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Տեսակապի հայցում"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Հնարավոր չէ միացնել տեսազանգը"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Տեսազանգի հարցումը մերժվել է"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Հետադարձ զանգի համարը՝\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Արտակարգ իրավիճակների հետադարձ զանգի համարը՝\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Համարը հավաքվում է"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Բաց թողնված զանգ"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Բաց թողնված զանգեր"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> բաց թողնված զանգ"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Բաց թողնված զանգ <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>-ից"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Ընթացիկ զանգ"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Ընթացիկ աշխատանքային զանգ"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Ընթացիկ Wi-Fi զանգ"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Ընթացիկ աշխատանքային Wi-Fi զանգ"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Սպասում"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Մուտքային զանգ"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Մուտքային աշխատանքային զանգ"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Մուտքային Wi-Fi զանգ"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Մուտքային աշխատանքային Wi-Fi զանգ"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Մուտքային տեսազանգ"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Մուտքային զանգը հավանաբար լցոն է"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Մուտքային տեսազանգի հայցում"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Նոր ձայնային հաղորդագրություն"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Նոր ձայնային հաղորդագրություն (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Զանգել <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> համարին"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Ձայնային փոստի համարն անհայտ է"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Ծառայություն չկա"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Ընտրված ցանցը (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) անհասանելի է"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Պատասխանել"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Անջատել"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Տեսազանգ"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Ձայնային"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Ընդունել"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Մերժել"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Հետ զանգել"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Հաղորդագրություն"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Ընթացիկ զանգ այլ սարքում"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Փոխանցել զանգը"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Զանգ կատարելու համար նախ անջատեք Ինքնաթիռի ռեժիմը:"</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Ցանցում գրանցված չէ:"</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Բջջային ցանցն անհասանելի է:"</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Զանգ կատարելու համար մուտքագրեք ճիշտ համար:"</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Հնարավոր չէ զանգել:"</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Մեկնարկում է MMI հաջորդականությունը…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Ծառայությունը չի աջակցվում:"</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Հնարավոր չէ փոխարկել զանգերը:"</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Հնարավոր չէ առանձնացնել զանգը:"</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Հնարավոր չէ փոխանցել:"</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Հնարավոր չէ կոնֆերանս զանգ կատարել:"</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Հնարավոր չէ մերժել զանգը:"</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Հնարավոր չէ անջատել զանգ(եր)ը:"</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP զանգ"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Շտապ կանչ"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Ռադիոն միացվում է…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Ծառայությունը մատչելի չէ: Փորձը կրկնվում է…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Հնարավոր չէ զանգել: <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> համարը արտակարգ իրավիճակի համար չէ:"</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Հնարավոր չէ զանգել: Հավաքեք արտակարգ իրավիճակի որևէ համար:"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Օգտագործել ստեղնաշարը համար հավաքելու համար"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Հետաձգել զանգը"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Վերսկսել զանգը"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Ավարտել զանգը"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Ցուցադրել թվաշարը"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Թաքցնել թվաշարը"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Անջատել ձայնը"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Չանտեսել"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Ավելացնել զանգ"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Միացնել զանգերը"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Փոխանակել"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Կառավարել զանգերը"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Կառավարել կոնֆերանս զանգը"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Կոնֆերանս զանգ"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Կառավարել"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Ձայնային"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Տեսազանգ"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Փոխարկել ձայնային կանչի"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Փոխարկել խցիկը"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Միացնել տեսախցիկը"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Անջատել տեսախցիկը"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Այլ ընտրանքներ"</string>
- <string name="player_started" msgid="3478865572468310331">"Նվագարկիչը մեկնարկել է"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Նվագարկիչը դադարեցվել է"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Տեսախցիկը պատրաստ չէ"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Տեսախցիկը պատրաստ է"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Զանգի աշխատաշրջանի անհայտ իրադարձություն"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Ծառայություն"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Կարգավորում"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Կարգավորված չէ&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Զանգերի այլ կարգավորումներ"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Զանգում է <xliff:g id="PROVIDER_NAME">%s</xliff:g>-ի միջոցով"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Մուտքային զանգ <xliff:g id="PROVIDER_NAME">%s</xliff:g>-ի միջոցով"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"կոնտակտի լուսանկարը"</string>
- <string name="goPrivate" msgid="3554069451018659483">"անցնել անձնականի"</string>
- <string name="selectContact" msgid="92191462970821951">"ընտրել կոնտակտ"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Գրեք ձեր սեփականը…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Չեղարկել"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Ուղարկել"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Պատասխանել"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Ուղարկել SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Մերժել"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Պատասխանել տեսազանգով"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Պատասխանել ձայնային զանգով"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Ընդունել տեսազանգի հարցումը"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Մերժել տեսազանգի հարցումը"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Ընդունել տեսափոխանցման հարցումը"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Մերժել տեսափոխանցման հարցումը"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Ընդունել տեսազանգ ստանալու հարցումը"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Մերժել տեսազանգ ստանալու հարցումը"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Սահեցրեք վերև` <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> գործառույթի համար:"</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Սահեցրեք ձախ` <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> գործառույթի համար:"</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Սահեցրեք աջ` <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> գործառույթի համար:"</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Սահեցրեք ցած՝ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> գործառույթի համար:"</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Թրթռոց"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Թրթռոց"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Ձայն"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Կանխադրված ձայնը (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Հեռախոսի զանգերանգ"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Թրթռալ զանգի ժամանակ"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Ձայներանգ և թրթռոց"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Կառավարել կոնֆերանս զանգը"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Արտակարգ իրավիճակի համար"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Պրոֆիլի լուսանկար"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Տեսախցիկն անջատված"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g>-ի միջոցով"</string>
- <string name="note_sent" msgid="7623014827902758398">"Գրառումն ուղարկվեց"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Վերջին հաղորդագրությունները"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Բիզնես տեղեկատվություն"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> մղոն հեռու"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> կմ հեռու"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> – <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Բացվում է վաղը ժամը <xliff:g id="OPEN_TIME">%s</xliff:g>-ին"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Բացվում է այսօր ժամը <xliff:g id="OPEN_TIME">%s</xliff:g>-ին"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Փակվում է ժամը <xliff:g id="CLOSE_TIME">%s</xliff:g>-ին"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Փակվել է այսօր ժամը <xliff:g id="CLOSE_TIME">%s</xliff:g>-ին"</string>
- <string name="open_now" msgid="4615706338669555999">"Հիմա բաց է"</string>
- <string name="closed_now" msgid="2635314668145282080">"Հիմա փակ է"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Հավանաբար լցոն է"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Զանգն ավարտվեց %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Այս համարից առաջին անգամ եք զանգ ստանում:"</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Կասկածներ կային, որ այս զանգը լցոնողից է:"</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Արգելափակել/Նշել լցոն"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Ավելացնել կոնտակտ"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Լցոն չէ"</string>
-</resources>
diff --git a/InCallUI/res/values-in/strings.xml b/InCallUI/res/values-in/strings.xml
deleted file mode 100644
index a2cd31dc7..000000000
--- a/InCallUI/res/values-in/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telepon"</string>
- <string name="onHold" msgid="527593602772521700">"Ditahan"</string>
- <string name="unknown" msgid="3646075119047488748">"Tidak dikenal"</string>
- <string name="private_num" msgid="6081418498487514686">"Nomor pribadi"</string>
- <string name="payphone" msgid="5743050584468748607">"Telepon Umum"</string>
- <string name="confCall" msgid="3181961445236675173">"Telewicara"</string>
- <string name="call_lost" msgid="8208184291640961172">"Panggilan terputus"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Pengeras suara"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Earpiece handset"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Headset berkabel"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Kirim nada berikut?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Mengirim nada\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Kirim"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Ya"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Tidak"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Ganti karakter acak dengan"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Telewicara <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Nomor pesan suara"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Memanggil"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Memanggil ulang"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Telewicara"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Panggilan masuk"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Panggilan masuk di telepon kerja"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Panggilan diakhiri"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Ditahan"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Menutup panggilan"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Sedang dalam panggilan"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Nomor saya <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Menyambungkan video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Video call"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Meminta video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Tidak dapat menyambungkan video call"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Permintaan video ditolak"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Nomor panggilan balik Anda\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Nomor panggilan balik darurat Anda\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Memanggil"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Panggilan tak terjawab"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Panggilan tak terjawab"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> panggilan tak terjawab"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Panggilan tak terjawab dari <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Panggilan yang berlangsung"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Panggilan telepon kerja yang sedang berlangsung"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Panggilan Wi-Fi keluar"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Panggilan Wi-Fi kerja yang sedang berlangsung"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Ditahan"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Panggilan masuk"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Panggilan masuk di telepon kerja"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Panggilan Wi-Fi masuk"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Panggilan Wi-Fi masuk di telepon kerja"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Video call masuk"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Panggilan masuk yang diduga spam"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Permintaan video masuk"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Pesan suara baru"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Pesan suara baru (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Telepon <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Nomor pesan suara tidak dikenal"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Tidak ada layanan"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Jaringan yang dipilih (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) tidak tersedia"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Jawab"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Akhiri"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Suara"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Terima"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Tutup"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Telepon balik"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Pesan"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Panggilan yang berlangsung di perangkat lain"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transfer Panggilan"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Untuk melakukan panggilan, terlebih dahulu nonaktifkan mode Pesawat."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Tidak terdaftar pada jaringan."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Jaringan seluler tidak tersedia."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Untuk melakukan panggilan telepon, masukkan nomor yang valid."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Tidak dapat menelepon."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Memulai urutan MMI..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Layanan tidak didukung."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Tidak dapat beralih panggilan."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Tidak dapat memisahkan panggilan."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Tidak dapat mentransfer."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Tidak dapat melakukan telewicara."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Tidak dapat menolak panggilan."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Tidak dapat melepas panggilan."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Panggilan SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Panggilan darurat"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Menghidupkan radio..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Tidak ada layanan. Mencoba lagi…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Tidak dapat menelepon. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> bukan nomor darurat."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Tidak dapat menelepon. Panggil nomor darurat."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Gunakan keyboard untuk memanggil"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Tahan Panggilan"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Mulai Kembali Panggilan"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Akhiri Panggilan"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Tampilkan tombol nomor"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Sembunyikan tombol nomor"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Bisukan"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Suarakan"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Tambahkan panggilan"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Gabungkan panggilan"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Tukar"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Kelola panggilan"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Kelola telewicara"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferensi telepon"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Kelola"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Video call"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Ubah ke panggilan suara"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Beralih kamera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Nyalakan kamera"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Matikan kamera"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Opsi lainnya"</string>
- <string name="player_started" msgid="3478865572468310331">"Pemutar Dimulai"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Pemutar Dihentikan"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kamera tidak siap"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kamera siap"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Sesi panggilan tidak dikenal"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Layanan"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Siapkan"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Tidak disetel&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Setelan panggilan lainnya"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Memanggil via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Masuk melalui <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"foto kontak"</string>
- <string name="goPrivate" msgid="3554069451018659483">"aktifkan pribadi"</string>
- <string name="selectContact" msgid="92191462970821951">"pilih kontak"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Tulis respons Anda sendiri…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Batal"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Kirim"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Jawab"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Kirim SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Tolak"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Jawab sebagai video call"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Jawab sebagai panggilan audio"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Terima permintaan video"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Tolak permintaan video"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Terima permintaan transmisi video"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Tolak permintaan transmisi video"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Terima permintaan menerima video"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Tolak permintaan menerima video"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Geser ke atas untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Geser ke kiri untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Geser ke kanan untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Geser ke bawah untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Getar"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Getar"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Suara"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Suara default (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Nada dering ponsel"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Bergetar saat berdering"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Nada dering &amp; Getar"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Kelola telewicara"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Nomor darurat"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Foto profil"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera tidak aktif"</string>
- <string name="child_number" msgid="4469090994612105532">"melalui <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Catatan telah dikirim"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Pesan terbaru"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Info bisnis"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> mil"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Buka jam <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Hari ini buka jam <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Tutup pukul <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Hari ini tutup pukul <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Buka sekarang"</string>
- <string name="closed_now" msgid="2635314668145282080">"Sekarang tutup"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Diduga telepon spam"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Panggilan diakhiri %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Nomor ini baru pertama kali menghubungi Anda."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Kami menduga panggilan ini dari pelaku spam."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blokir/laporkan spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Tambahkan kontak"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Bukan spam"</string>
-</resources>
diff --git a/InCallUI/res/values-is/strings.xml b/InCallUI/res/values-is/strings.xml
deleted file mode 100644
index 480fd6eaa..000000000
--- a/InCallUI/res/values-is/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Sími"</string>
- <string name="onHold" msgid="527593602772521700">"Í bið"</string>
- <string name="unknown" msgid="3646075119047488748">"Óþekkt"</string>
- <string name="private_num" msgid="6081418498487514686">"Óþekkt númer"</string>
- <string name="payphone" msgid="5743050584468748607">"Símasjálfsali"</string>
- <string name="confCall" msgid="3181961445236675173">"Símafundur"</string>
- <string name="call_lost" msgid="8208184291640961172">"Símtali slitið"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Hátalari"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Símahátalari"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Höfuðtól með snúru"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Senda eftirfarandi tóna?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Sendir tóna\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Senda"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Já"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Nei"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Skipta algildisstaf út fyrir"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Símafundur <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Talhólfsnúmer"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Hringir"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Hringir aftur"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Símafundur"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Móttekið símtal"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Vinnusímtal berst"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Lagt á"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Í bið"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Leggur"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Í símtali"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Númerið mitt er <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Tengir myndskeið"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Myndsímtal"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Biður um myndskeið"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Ekki tókst að tengja myndsímtal"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Myndsímtalsbeiðni hafnað"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Svarhringingarnúmer þitt\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Svarhringingarnúmer þitt í neyðartilvikum\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Hringir"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Ósvarað símtal"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Ósvöruð símtöl"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ósvöruð símtöl"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Ósvarað símtal frá <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Samtal í gangi"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Vinnusímtal í gangi"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Wi-Fi símtal stendur yfir"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Vinnusímtal í gangi um Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Í bið"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Móttekið símtal"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Vinnusímtal berst"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Wi-Fi símtal berst"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Vinnusímtal berst um Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Myndsímtal berst"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Símtal sem berst er hugsanlega úr ruslnúmeri"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Myndbeiðni berst"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Ný skilaboð í talhólfinu"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Ný skilaboð í talhólfinu (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Hringja í <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Talhólfsnúmer ekki þekkt"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Ekkert símasamband"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Valið símkerfi (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) er ekki tiltækt"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Svara"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Leggja á"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Myndskeið"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Tal"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Samþykkja"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Hunsa"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Hringja til baka"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Skilaboð"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Símtal í gangi í öðru tæki"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Flytja símtal"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Til að hringja símtal þarftu fyrst að slökkva á flugstillingu."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Ekki skráð á símkerfi."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Farsímakerfi ekki til staðar."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Sláðu inn gilt númer til að hringja símtal."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Ekki hægt að hringja."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Ræsir MMI-runu…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Þjónustan er ekki studd."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Ekki hægt að skipta milli símtala."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Ekki hægt að aðskilja símtal."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Ekki hægt að flytja."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Ekki hægt að halda símafund."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Ekki hægt að hafna símtali."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Ekki hægt að leggja á."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP-símtal"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Neyðarsímtal"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Kveikir á loftneti…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Ekkert samband. Reynir aftur…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Getur ekki hringt. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> er ekki neyðarsímanúmer."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Ekki hægt að hringja. Hringdu í neyðarnúmer"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Notaðu lyklaborðið til að hringja"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Setja símtal í bið"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Halda símtali áfram"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Leggja á"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Sýna símatakkaborð"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Fela símatakkaborð"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Slökkva á hljóði"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Kveikja á hljóði"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Bæta við símtali"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Sameina símtöl"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Skipta milli"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Stjórna símtölum"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Stjórna símafundi"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Símafundur"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Stjórna"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Hljóð"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Myndsímtal"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Breyta í símtal"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Skipta um myndavél"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Kveikja á myndavél"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Slökkva á myndavél"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Fleiri valkostir"</string>
- <string name="player_started" msgid="3478865572468310331">"Spilari ræstur"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Spilari stöðvaður"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Myndavél ekki tilbúin"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Myndavél tilbúin"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Óþekkt atvik símtalslotu"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Þjónusta"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Uppsetning"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Ekki valið&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Aðrar símtalsstillingar"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Hringt í gegnum <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Berst í gegnum <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"mynd tengiliðar"</string>
- <string name="goPrivate" msgid="3554069451018659483">"tala í einrúmi"</string>
- <string name="selectContact" msgid="92191462970821951">"velja tengilið"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Skrifaðu eigið svar…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Hætta við"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Senda"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Svara"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Senda SMS-skilaboð"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Hafna"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Svara sem myndsímtali"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Svara sem símtali"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Samþykkja beiðni um myndsímtal"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Hafna beiðni um myndsímtal"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Samþykkja beiðni um sendingu myndsímtals"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Hafna beiðni um sendingu myndsímtals"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Samþykkja beiðni um móttöku myndsímtals"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Hafna beiðni um móttöku myndsímtals"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Strjúktu upp til að <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Strjúktu til vinstri til að <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Strjúktu til hægri til að <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Strjúktu niður til að <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Titra"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Titra"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Hljóð"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Sjálfgefið hljóð (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Hringitónn síma"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Titra við hringingu"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Hringitónn og titringur"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Stjórna símafundi"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Neyðarnúmer"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Prófílmynd"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Slökkt á myndavél"</string>
- <string name="child_number" msgid="4469090994612105532">"úr <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Glósa send"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Nýleg skilaboð"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Fyrirtækjaupplýsingar"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"í <xliff:g id="DISTANCE">%.1f</xliff:g> míl. fjarlægð"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"í <xliff:g id="DISTANCE">%.1f</xliff:g> km fjarlægð"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>–<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Opið á morgun frá kl. <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Opið í dag frá kl. <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Lokað kl. <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Var lokað í dag kl. <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Opið núna"</string>
- <string name="closed_now" msgid="2635314668145282080">"Lokað núna"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Grunur um svikasímtal"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Símtali lokið %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Þetta er í fyrsta sinn sem hringt er í þig úr þessu númeri."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Okkur grunaði að þetta símtal væri úr ruslnúmeri."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Bannlisti/tilkynna"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Bæta tengilið við"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Ekki ruslnúmer"</string>
-</resources>
diff --git a/InCallUI/res/values-it/strings.xml b/InCallUI/res/values-it/strings.xml
deleted file mode 100644
index c9b49501b..000000000
--- a/InCallUI/res/values-it/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefono"</string>
- <string name="onHold" msgid="527593602772521700">"In attesa"</string>
- <string name="unknown" msgid="3646075119047488748">"Sconosciuto"</string>
- <string name="private_num" msgid="6081418498487514686">"Numero privato"</string>
- <string name="payphone" msgid="5743050584468748607">"Telefono pubblico"</string>
- <string name="confCall" msgid="3181961445236675173">"Audioconferenza"</string>
- <string name="call_lost" msgid="8208184291640961172">"Chiamata persa"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Altoparlante"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Auricolare telefono"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Auricolare con cavo"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Inviare i numeri successivi?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Invio toni\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Invia"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Sì"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"No"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Sostituisci carattere jolly con"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Audioconferenza: <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Numero segreteria"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Chiamata in corso"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Ricomposizione"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Audioconferenza"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Chiamata in arrivo"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Chiamata lavoro in arrivo"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Chiamata terminata"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"In attesa"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"In fase di chiusura"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Chiamata in corso"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Il mio numero è: <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Collegamento video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videochiamata"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Richiesta video in corso"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Impossibile effettuare una videochiamata"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Richiesta video rifiutata"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Numero da richiamare:\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Numero da richiamare in caso di emergenza:\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Composizione in corso"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Chiamata persa"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Chiamate perse"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> chiamate perse"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Chiamata senza risposta da <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Chiamata in corso"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Chiamata di lavoro in corso"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Chiamata Wi-Fi in corso"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Chiamata di lavoro tramite Wi-Fi in corso"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"In attesa"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Chiamata in arrivo"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Chiamata di lavoro in arrivo"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Chiamata Wi-Fi in arrivo"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Chiamata di lavoro in arrivo tramite Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Videochiamata in arrivo"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Chiamata di presunto spam in arrivo"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Richiesta video in arrivo"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Nuovo messaggio vocale"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Nuovo messaggio vocale (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Componi <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Numero segreteria sconosciuto"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Nessun servizio"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Rete selezionata (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) non disponibile"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Rispondi"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Riaggancia"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Voce"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Accetta"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Ignora"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Richiama"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Messaggio"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Chiamata in corso su un altro dispositivo"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Trasferisci chiamata"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Per fare una chiamata, disattiva la modalità aereo."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Non registrato sulla rete."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Rete dati non disponibile."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Per fare una chiamata, inserisci un numero valido."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Impossibile chiamare."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Avvio sequenza MMI..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Servizio non supportato."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Impossibile cambiare chiamata."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Impossibile separare la chiamata."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Impossibile trasferire."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Impossibile fare una chiamata in conferenza."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Impossibile rifiutare la chiamata."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Impossibile riagganciare."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Chiamata SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Chiamata di emergenza"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Attivazione segnale cellulare..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Nessun servizio. Nuovo tentativo…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Impossibile chiamare. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> non è un numero di emergenza."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Impossibile chiamare. Componi un numero di emergenza."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Usa tastiera"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Metti in attesa la chiamata"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Riprendi chiamata"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Termina chiamata"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Mostra tastierino"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Nascondi tastierino"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Disattiva audio"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Riattiva audio"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Aggiungi chiamata"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Unisci chiamate"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Scambia"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Gestisci chiamate"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Gestisci audioconferenza"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Audioconferenza"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Gestisci"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videochiam"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Passa a chiamata vocale"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Cambia fotocamera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Attiva fotocamera"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Disattiva fotocamera"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Altre opzioni"</string>
- <string name="player_started" msgid="3478865572468310331">"Player avviato"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Player interrotto"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"La fotocamera non è pronta"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Fotocamera pronta"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Evento sessione chiamata sconosciuto"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Servizio"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Configura"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Non impostato&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Altre impostazioni di chiamata"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Chiamate tramite <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"In arrivo tramite <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"foto contatto"</string>
- <string name="goPrivate" msgid="3554069451018659483">"Privato"</string>
- <string name="selectContact" msgid="92191462970821951">"seleziona contatto"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Scrivi risposta personale..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Annulla"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Invia"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Rispondi"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Invia SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Rifiuta"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Rispondi con videochiamata"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Rispondi con chiamata audio"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Accetta richiesta video"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Rifiuta richiesta video"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Accetta richiesta di trasmissione video"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Rifiuta richiesta di trasmissione video"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Accetta richiesta di ricevimento video"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Rifiuta richiesta di ricevimento video"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Scorri verso l\'alto per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Scorri verso sinistra per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Scorri verso destra per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Scorri verso il basso per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrazione"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrazione"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Suono"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Suono predefinito (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Suoneria telefono"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrazione quando squilla"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Suoneria e vibrazione"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Gestisci audioconferenza"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Numero di emergenza"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Foto del profilo"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Fotocamera disattivata"</string>
- <string name="child_number" msgid="4469090994612105532">"tramite <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Nota inviata"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Messaggi recenti"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informazioni sull\'attività"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"Distante <xliff:g id="DISTANCE">%.1f</xliff:g> mi"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"Distante <xliff:g id="DISTANCE">%.1f</xliff:g> km"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Apre domani alle ore <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Apre oggi alle ore <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Chiude alle ore <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Ha chiuso oggi alle ore <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Aperto ora"</string>
- <string name="closed_now" msgid="2635314668145282080">"Ora chiuso"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Presunto spammer"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Chiamata terminata %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"È la prima volta che questo numero ti chiama."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Sospettavamo che questa chiamata provenisse da uno spammer."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blocca/Segnala spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Aggiungi contatto"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Non spam"</string>
-</resources>
diff --git a/InCallUI/res/values-iw/strings.xml b/InCallUI/res/values-iw/strings.xml
deleted file mode 100644
index c75c8da0d..000000000
--- a/InCallUI/res/values-iw/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"טלפון"</string>
- <string name="onHold" msgid="527593602772521700">"בהמתנה"</string>
- <string name="unknown" msgid="3646075119047488748">"לא ידוע"</string>
- <string name="private_num" msgid="6081418498487514686">"מספר פרטי"</string>
- <string name="payphone" msgid="5743050584468748607">"טלפון ציבורי"</string>
- <string name="confCall" msgid="3181961445236675173">"שיחת ועידה"</string>
- <string name="call_lost" msgid="8208184291640961172">"השיחה נותקה"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"רמקול"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"אוזנייה"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"אוזניות עם חיבור חוטי"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"האם לשלוח את הצלילים הבאים?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"שולח צלילים\n"</string>
- <string name="send_button" msgid="4054398309483035794">"שלח"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"כן"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"לא"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"החלף את התו הכללי ב"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"שיחת ועידה <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"המספר של הדואר הקולי"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"מחייג"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"מחייג שוב"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"שיחת ועידה"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"שיחה נכנסת"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"שיחת עבודה נכנסת"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"השיחה הסתיימה"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"בהמתנה"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"מנתק"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"בשיחה"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"המספר שלי הוא <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"מחבר וידאו"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"שיחת וידאו"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"מבקש וידאו"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"לא ניתן לחבר שיחת וידאו"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"בקשת וידאו נדחתה"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"המספר שלך להתקשרות חזרה\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"המספר שלך להתקשרות חזרה במצב חירום\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"מחייג"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"שיחה שלא נענתה"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"שיחות שלא נענו"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> שיחות שלא נענו"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"שיחה שלא נענתה מאת <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"שיחה פעילה"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"שיחת עבודה פעילה"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"‏שיחת Wi-Fi פעילה"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"‏שיחת עבודה פעילה ברשת WiFi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"בהמתנה"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"שיחה נכנסת"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"שיחת עבודה נכנסת"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"‏שיחת Wi-Fi נכנסת"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"‏שיחת עבודה נכנסת ברשת WiFi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"שיחת וידאו נכנסת"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"השיחה הנכנסת חשודה כספאם"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"בקשת וידאו נכנסת"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"דואר קולי חדש"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"דואר קולי חדש (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"‏חייג ‎<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>‎"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"המספר של הדואר הקולי אינו ידוע"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"אין שירות"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"הרשת שנבחרה (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) לא זמינה"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"ענה"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"נתק"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"וידאו"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"קול"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"אשר"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"בטל"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"התקשר חזרה"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"שלח הודעה"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"באחד מהמכשירים האחרים מתבצעת שיחה"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"העבר את השיחה"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"כדי להתקשר, כבה תחילה את מצב טיסה."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"לא רשום ברשת."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"רשת סלולרית אינה זמינה."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"כדי להתקשר, הזן מספר טלפון חוקי."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"לא ניתן להתקשר."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"‏מתחיל רצף MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"שירות לא נתמך."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"לא ניתן לעבור בין שיחות."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"לא ניתן להפריד שיחה."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"לא ניתן להעביר."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"לא ניתן לבצע שיחת ועידה."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"לא ניתן לדחות שיחה."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"לא ניתן להתקשר."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"‏שיחת SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"שיחת חירום"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"מפעיל את הרדיו…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"אין שירות. מנסה שוב..."</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"לא ניתן להתקשר. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> אינו מספר חירום."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"לא ניתן להתקשר. חייג למספר חירום."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"השתמש במקלדת כדי לחייג"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"החזק שיחה"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"המשך בשיחה"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"סיים שיחה"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"הצגת לוח החיוג"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"הסתרת לוח החיוג"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"השתקה"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"ביטול ההשתקה"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"הוסף שיחה"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"מזג שיחות"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"החלף"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"נהל שיחות"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"נהל שיחת ועידה"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"שיחת ועידה"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"ניהול"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"אודיו"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"שיחת וידאו"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"שנה לשיחה קולית"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"החלף מצלמה"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"הפעל את המצלמה"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"כבה את המצלמה"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"אפשרויות נוספות"</string>
- <string name="player_started" msgid="3478865572468310331">"הנגן הופעל"</string>
- <string name="player_stopped" msgid="1278611664986561535">"הנגן הפסיק"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"המצלמה לא מוכנה"</string>
- <string name="camera_ready" msgid="2614541247814590887">"המצלמה מוכנה"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"אירוע הפעלת שיחה לא ידוע"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"שירות"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"הגדרות"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;לא הוגדר&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"הגדרות אחרות של שיחה"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"שיחה באמצעות <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"שיחה נכנסת באמצעות <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"תמונה של איש קשר"</string>
- <string name="goPrivate" msgid="3554069451018659483">"עבור לשיחה פרטית"</string>
- <string name="selectContact" msgid="92191462970821951">"בחר איש קשר"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"כתוב תגובה משלך..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"בטל"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"שלח"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"ענה"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"‏שלח SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"דחה"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"ענה כשיחת וידאו"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"ענה כשיחת אודיו"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"קבל בקשת וידאו"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"דחה בקשת וידאו"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"אשר את הבקשה לשידור וידאו"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"דחה את הבקשה לשידור וידאו"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"אשר את הבקשה לקבלת וידאו"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"דחה את הבקשה לקבלת וידאו"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"הסט למעלה כדי <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"הסט שמאלה כדי <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"הסט ימינה כדי <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"הסט למטה כדי <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"רטט"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"רטט"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"צליל"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"צליל ברירת מחדל (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"רינגטון של טלפון"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"רטט בעת צלצול"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"רינגטון ורטט"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"נהל שיחת ועידה"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"מספר חירום"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"תמונת פרופיל"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"המצלמה כבויה"</string>
- <string name="child_number" msgid="4469090994612105532">"באמצעות <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"ההערה נשלחה"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"הודעות אחרונות"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"פרטי עסק"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"במרחק <xliff:g id="DISTANCE">%.1f</xliff:g> מייל"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"במרחק <xliff:g id="DISTANCE">%.1f</xliff:g> ק\"מ"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"ייפתח מחר ב-<xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"נפתח היום ב-<xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"נסגר ב-<xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"נסגר היום ב-<xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"פתוח עכשיו"</string>
- <string name="closed_now" msgid="2635314668145282080">"סגור עכשיו"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"חשד לשיחת ספאם"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"‏השיחה הסתיימה %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"זוהי הפעם הראשונה שמתקשרים אליך מהמספר הזה."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"אנו חושדים שהמספר הזה הוא של שולח ספאם."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"חסימה/דיווח על ספאם"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"הוספת איש קשר"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"לא ספאם"</string>
-</resources>
diff --git a/InCallUI/res/values-ja/strings.xml b/InCallUI/res/values-ja/strings.xml
deleted file mode 100644
index 551d896b5..000000000
--- a/InCallUI/res/values-ja/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"電話"</string>
- <string name="onHold" msgid="527593602772521700">"保留中"</string>
- <string name="unknown" msgid="3646075119047488748">"不明"</string>
- <string name="private_num" msgid="6081418498487514686">"非通知"</string>
- <string name="payphone" msgid="5743050584468748607">"公衆電話"</string>
- <string name="confCall" msgid="3181961445236675173">"グループ通話"</string>
- <string name="call_lost" msgid="8208184291640961172">"通話が遮断されました"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"スピーカー"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"モバイル端末のイヤホン"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"有線ヘッドセット"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"次の番号を送信しますか?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"番号を送信中\n"</string>
- <string name="send_button" msgid="4054398309483035794">"送信"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"はい"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"いいえ"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"ワイルド文字を置換:"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"グループ通話 <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"ボイスメールの番号"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"発信中"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"リダイヤル中"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"グループ通話"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"着信中"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"仕事の通話が着信中"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"通話終了"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"保留中"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"通話終了"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"通話中"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"この電話の番号: <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"ビデオハングアウトに接続中"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"ビデオハングアウト"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"ビデオハングアウトをリクエスト中"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"ビデオハングアウトの接続エラー"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"ビデオハングアウトのリクエスト不承認"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"コールバック先\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"緊急通報コールバック先\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"発信中"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"不在着信"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"不在着信"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"不在着信 <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> 件"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> さんからの不在着信"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"通話中"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"仕事の通話中"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Wi-Fi 通話中"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"仕事の Wi-Fi 通話中"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"保留中"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"着信中"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"仕事の通話が着信中"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Wi-Fi 通話が着信中"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"仕事の Wi-Fi 通話が着信中"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"ビデオハングアウトが着信中"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"迷惑電話の疑いがある通話を着信しています"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"ビデオハングアウト リクエストが着信中"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"新着のボイスメール"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"新着のボイスメール(<xliff:g id="COUNT">%d</xliff:g> 件)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> に発信"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"ボイスメールの番号が不明です"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"通信サービスはありません"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"選択したネットワーク(<xliff:g id="OPERATOR_NAME">%s</xliff:g>)が利用できません"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"電話に出る"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"通話終了"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"ビデオ"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"音声"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"受ける"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"拒否する"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"コールバック"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"メッセージ"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"別の端末で通話中"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"通話を転送"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"機内モードを OFF にしてから発信してください。"</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"ご加入の通信サービスがありません。"</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"モバイル ネットワークが利用できません。"</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"発信するには、有効な番号を入力してください。"</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"発信できません。"</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI シーケンスを開始しています..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"サービスはサポートされていません。"</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"通話を切り替えられません。"</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"通話を分割できません。"</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"転送できません。"</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"グループ通話できません。"</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"着信を拒否できません。"</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"通話を解放できません。"</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP 通話"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"緊急通報"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"無線通信を ON にしています..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"通信サービスはありません。もう一度お試しください…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"発信できません。<xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> は緊急通報番号ではありません。"</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"発信できません。緊急通報番号におかけください。"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"キーボードで番号を入力してください"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"通話を保留"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"通話を再開"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"通話を終了"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"ダイヤルパッドを表示"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"ダイヤルパッドを非表示"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"ミュート"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"ミュートを解除"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"通話を追加"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"グループ通話"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"切り替え"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"通話を管理"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"グループ通話オプション"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"グループ通話"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"管理"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"音声"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"ビデオ"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"音声通話に変更"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"カメラを切り替え"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"カメラを ON にする"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"カメラを OFF にする"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"その他のオプション"</string>
- <string name="player_started" msgid="3478865572468310331">"プレーヤーを開始しました"</string>
- <string name="player_stopped" msgid="1278611664986561535">"プレーヤーを停止しました"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"カメラが準備できていません"</string>
- <string name="camera_ready" msgid="2614541247814590887">"カメラが準備できました"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"不明な通話セッション イベントです"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"サービス"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"セットアップ"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;未設定&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"その他の通話設定"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> で発信中"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> で着信中"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"連絡先の写真"</string>
- <string name="goPrivate" msgid="3554069451018659483">"個別通話に切り替え"</string>
- <string name="selectContact" msgid="92191462970821951">"連絡先を選択"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"カスタム返信を作成..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"キャンセル"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"送信"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"電話に出る"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS を送信する"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"拒否"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"ビデオハングアウトで電話に出る"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"音声通話で電話に出る"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"ビデオハングアウト リクエストを承認する"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"ビデオハングアウト リクエストを拒否する"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"ビデオハングアウト送信リクエストを承認する"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"ビデオハングアウト送信リクエストを拒否する"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"ビデオハングアウト受信リクエストを承認する"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"ビデオハングアウト受信リクエストを拒否する"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"上にスライドして<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>を行います。"</string>
- <string name="description_direction_left" msgid="6811598791620851239">"左にスライドして<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>を行います。"</string>
- <string name="description_direction_right" msgid="5461971399586296023">"右にスライドして<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>を行います。"</string>
- <string name="description_direction_down" msgid="3331715227997561639">"下にスライドして<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>を行います。"</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"バイブレーション"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"バイブレーション"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"着信音"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"デフォルトの通知音(<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"着信音"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"着信時のバイブレーション"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"着信音とバイブレーション"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"グループ通話オプション"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"緊急通報番号"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"プロフィール写真"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"カメラ OFF"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> に着信"</string>
- <string name="note_sent" msgid="7623014827902758398">"メモを送信しました"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"最近のメッセージ"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"ビジネス情報"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> マイル内"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km 内"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>、<xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>~<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>、<xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"明日 <xliff:g id="OPEN_TIME">%s</xliff:g>に営業開始"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"本日 <xliff:g id="OPEN_TIME">%s</xliff:g>に営業開始"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g>に営業終了"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"本日 <xliff:g id="CLOSE_TIME">%s</xliff:g>に営業終了"</string>
- <string name="open_now" msgid="4615706338669555999">"現在営業中"</string>
- <string name="closed_now" msgid="2635314668145282080">"営業終了"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"迷惑電話の疑いあり"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"通話が終了しました %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"この番号からの通話を受信したのはこれが初めてです。"</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"この通話は迷惑電話の可能性があります。"</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"ブロック / 報告"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"連絡先に追加"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"迷惑電話ではない"</string>
-</resources>
diff --git a/InCallUI/res/values-ka/strings.xml b/InCallUI/res/values-ka/strings.xml
deleted file mode 100644
index b01006561..000000000
--- a/InCallUI/res/values-ka/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"ტელეფონი"</string>
- <string name="onHold" msgid="527593602772521700">"მოცდის რეჟიმში"</string>
- <string name="unknown" msgid="3646075119047488748">"უცნობი"</string>
- <string name="private_num" msgid="6081418498487514686">"დაფარული ნომერი"</string>
- <string name="payphone" msgid="5743050584468748607">"ტელეფონ-ავტომატი"</string>
- <string name="confCall" msgid="3181961445236675173">"საკონფერენციო ზარი"</string>
- <string name="call_lost" msgid="8208184291640961172">"ზარი შეწყდა"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"დინამიკი"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"ყურსაცვამის საყურისი"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"სადენიანი ყურსაცვამი"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"გსურთ შემდეგი ტონების გაგზავნა?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"ტონების გაგზავნა\n"</string>
- <string name="send_button" msgid="4054398309483035794">"გაგზავნა"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"დიახ"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"არა"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"ჩანაცვლების სიმბოლო ჩანაცვლდეს შემდეგით:"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"საკონფერენციო ზარი: <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"ხმოვანი ფოსტის ნომერი"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"მიმდინარეობს აკრეფა"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"იკრიფება ხელახლა"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"საკონფერენციო ზარი"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"შემომავალი ზარი"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"შემომავალი ზარი (სამსახური)"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"ზარი დასრულდა"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"მოცდის რეჟიმში"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"მიმდინარეობს გათიშვა"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"საუბრის რეჟიმში"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"ჩემი ნომერია <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"მიმდინარეობს ვიდეოს დაკავშირება"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"ვიდეო ზარი"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"მიმდინარეობს ვიდეოს მოთხოვნა"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"ვიდეო ზარის დაკავშირება ვერ მოხერხდა"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"ვიდეოს მოთხოვნა უარყოფილია"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"თქვენი ნომერი გადმორეკვისთვის\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"თქვენი ნომერი გადაუდებელი გადმორეკვისთვის\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"მიმდინარეობს აკრეფა"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"გამოტოვებული ზარი"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"გამოტოვებული ზარები"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> გამოტოვებული ზარი"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"გამოტოვებული ზარი <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>-ისგან"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"მიმდინარე ზარი"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"მიმდინარე ზარი (სამსახური)"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"მიმდინარე Wi-Fi ზარი"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"მიმდინარე Wi-Fi ზარი (სამსახური)"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"მოცდის რეჟიმში"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"შემომავალი ზარი"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"შემომავალი ზარი (სამსახური)"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"შემომავალი Wi-Fi ზარი"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"შემომავალი Wi-Fi ზარი (სამსახური)"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"შემომავალი ვიდეო ზარი"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"შემომავალი ზარი - სავარაუდოდ სპამი"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"შემომავალი ვიდეოს მოთხოვნა"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"ახალი ხმოვანი შეტყობინება"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"ახალი ხმოვანი შეტყობინება (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>-ზე დარეკვა"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"ხმოვანი ფოსტის ნომერი უცნობია"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"სერვისი არ არის"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"არჩეული ქსელი (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) მიუწვდომელია"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"პასუხი"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"გათიშვა"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"ვიდეო"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"ხმოვანი"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"მიღება"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"დახურვა"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"გადარეკვა"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"შეტყობინება"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"სხვა მოწყობილობაზე მიმდინარე ზარი"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"ზარის ტრანსფერი"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"ზარის განსახორციელებლად, ჯერ გამორთეთ თვითმფრინავის რეჟიმი."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"არ არის რეგისტრირებული ქსელში."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"ფიჭური ქსელი მიუწვდომელია."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"ზარის განსახორციელებლად, შეიყვანეთ სწორი ნომერი."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"დარეკვა ვერ ხერხდება."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI თანმიმდევრობის დაწყება…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"სერვისი არ არის მხარდაჭერილი."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"ზარების გადართვა ვერ ხერხდება."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"ზარის განცალკევება ვერ ხერხდება."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"გადამისამართება ვერ ხერხდება."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"საკონფერენციო ზარის განხორციელება ვერ ხერხდება."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"ზარის უარყოფა ვერ ხერხდება."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"ზარ(ებ)ის გათიშვა ვერ ხერხდება."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP ზარი"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"გადაუდებელი ზარი"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"მიმდინარეობს რადიოს ჩართვა…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"სერვისი არ არის. მიმდინარეობს ხელახლა ცდა…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"დარეკვა ვერ ხერხდება. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> არ არის გადაუდებელი დახმარების ნომერი."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"დარეკვა ვერ ხერხდება. აკრიფეთ გადაუდებელი დახმარების ნომერი."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"ნომრის ასაკრეფად გამოიყენეთ კლავიატურა"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"მოცდის რეჟიმზე გადაყვანა"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"ზარის განახლება"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"ზარის დასრულება"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"ციფერბლატის ჩვენება"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"ციფერბლატის დამალვა"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"დადუმება"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"დადუმების გაუქმება"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"ზარის დამატება"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"ზარების გაერთიანება"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"ჩანაცვლება"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"ზარების მართვა"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"საკონფერენციო ზარის მართვა"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"საკონფერენციო ზარი"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"მართვა"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"აუდიო"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"ვიდეო ზარი"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"ხმოვან ზარზე გადართვა"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"კამერის გადართვა"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"კამერის ჩართვა"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"კამერის გამორთვა"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"სხვა ვარიანტები"</string>
- <string name="player_started" msgid="3478865572468310331">"დამკვრელი ჩაირთო"</string>
- <string name="player_stopped" msgid="1278611664986561535">"დამკვრელი გამოირთო"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"კამერა არ არის მზად"</string>
- <string name="camera_ready" msgid="2614541247814590887">"კამერა მზადაა"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"ზარის სესიის უცნობი მოვლენა"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"სერვისი"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"დაყენება"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;არ არის დაყენებული&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"ზარის სხვა პარამეტრები"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"მიმდინარეობს დარეკვა <xliff:g id="PROVIDER_NAME">%s</xliff:g>-ის მეშვეობით"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"შემომავალი ზარი <xliff:g id="PROVIDER_NAME">%s</xliff:g>-დან"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"კონტაქტის ფოტო"</string>
- <string name="goPrivate" msgid="3554069451018659483">"პირადი რეჟიმი"</string>
- <string name="selectContact" msgid="92191462970821951">"კონტაქტის არჩევა"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"საკუთარის შექმნა..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"გაუქმება"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"გაგზავნა"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"პასუხი"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS-ის გაგზავნა"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"უარყოფა"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"პასუხი ვიდეო ზარის სახით"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"პასუხი ხმოვანი ზარის სახით"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"ვიდეოს მოთხოვნის მიღება"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"ვიდეოს მოთხოვნის უარყოფა"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"ვიდეოს გადაცემის მოთხოვნის მიღება"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"ვიდეოს გადაცემის მოთხოვნის უარყოფა"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"ვიდეოს მიღების მოთხოვნაზე დათანხმება"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"ვიდეოს მიღების მოთხოვნის უარყოფა"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"გაასრიალეთ ზემოთ, რათა შესრულდეს <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"გაასრიალეთ მარცხნივ, რათა შესრულდეს <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"გაასრიალეთ მარჯვნივ, რათა შესრულდეს <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"გაასრიალეთ ქვემოთ, რათა შესრულდეს <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"ვიბრაცია"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"ვიბრაცია"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"ხმა"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"ნაგულისხმები ხმა (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"ტელეფონის ზარი"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"ვიბრაცია დარეკვისას"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"ზარის მელოდია და ვიბრაცია"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"საკონფერენციო ზარის მართვა"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"გადაუდებელი დახმარების ნომერი"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"პროფილის ფოტო"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"კამერა გამორთულია"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g>-დან"</string>
- <string name="note_sent" msgid="7623014827902758398">"შენიშვნა გაიგზავნა"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"ბოლო შეტყობინებები"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"ბიზნეს-ინფორმაცია"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> მილში"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> კმ-ში"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> — <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"იხსნება ხვალ <xliff:g id="OPEN_TIME">%s</xliff:g>-ზე"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"იხსნება დღეს <xliff:g id="OPEN_TIME">%s</xliff:g>-ზე"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"იკეტება <xliff:g id="CLOSE_TIME">%s</xliff:g>-ზე"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"დაიკეტა დღეს <xliff:g id="CLOSE_TIME">%s</xliff:g>-ზე"</string>
- <string name="open_now" msgid="4615706338669555999">"ახლა ღიაა"</string>
- <string name="closed_now" msgid="2635314668145282080">"ახლა დაკეტილია"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"სავარაუდ.სპამ.აბონ."</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"ზარი დასრულდა %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"ამ ნომრიდან პირველად დაგირეკეს."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"ეჭვი გვაქვს, რომ ეს ზარი სპამია."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"დაბლოკ./სპამ.შეტყობ."</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"კონტაქტის დამატება"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"არ არის სპამი"</string>
-</resources>
diff --git a/InCallUI/res/values-kk/strings.xml b/InCallUI/res/values-kk/strings.xml
deleted file mode 100644
index 07ea6fb78..000000000
--- a/InCallUI/res/values-kk/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Телефон"</string>
- <string name="onHold" msgid="527593602772521700">"Күтуде"</string>
- <string name="unknown" msgid="3646075119047488748">"Белгісіз"</string>
- <string name="private_num" msgid="6081418498487514686">"Жеке нөмір"</string>
- <string name="payphone" msgid="5743050584468748607">"Автомат-телефон"</string>
- <string name="confCall" msgid="3181961445236675173">"Конференциялық қоңырау"</string>
- <string name="call_lost" msgid="8208184291640961172">"Қоңырау үзілді"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Динамик"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Телефон құлаққабы"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Сымды құлақаспап жинағы"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Келесі әуендер жіберілсін бе?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Жіберу әуендері\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Жіберу"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Иә"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Жоқ"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Қойылмалы таңбаны келесі таңбамен алмастыру"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"<xliff:g id="CONF_CALL_TIME">%s</xliff:g> конференциялық қоңырауы"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Дауыстық пошта нөмірі"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Терілуде"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Қайта терілуде"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Конференциялық қоңырау"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Кіріс қоңырау"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Кіріс жұмыс қоңырауы"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Қоңырау аяқталды"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Күтуде"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Қоңырау аяқталуда"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Қоңырауда"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Mенің нөмірім — <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Бейне қосылуда"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Бейне қоңырау"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Бейне сұралуда"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Бейне қоңырауға қосылу мүмкін емес"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Бейне сұрауы қабылданбады"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Кері қоңырау шалу нөміріңіз\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Төтенше кері қоңырау шалу нөміріңіз\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Терілуде"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Өткізіп алған қоңырау"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Өткізіп алған қоңыраулар"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> өткізіп алған қоңырау"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> қоңырауы өткізіп алынған"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Ағымдағы қоңырау"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Ағымдағы жұмыс қоңырауы"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Ағымдағы Wi-Fi қоңырауы"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Ағымдағы Wi-Fi жұмыс қоңырауы"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Күтуде"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Кіріс қоңырау"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Кіріс жұмыс қоңырауы"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Кіріс Wi-Fi қоңырауы"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Кіріс Wi-Fi жұмыс қоңырауы"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Кіріс бейне қоңырау"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Кіріс қоңырауы спам болуы мүмкін"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Кіріс бейне сұрау"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Жаңа дауыстық хабар"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Жаңа дауыстық хабар (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> нөмірін теру"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Дауыстық пошта нөмірі белгісіз"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Қызмет жоқ"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Таңдалған (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) желісі қол жетімді емес"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Жауап"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Қоңырауды аяқтау"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Бейне"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Дауыс"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Қабылдау"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Қабылдамау"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Кері қоңырау шалу"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Хабар"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Қоңырау басқа құрылғыдан шалынуда"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Қоңырауды басқа құрылғыға бағыттау"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Қоңырау шалу үшін алдымен ұшақ режимін өшіріңіз."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Желіде тіркелмеген."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Ұялы желі қол жетімді емес."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Қоңырау шалу үшін жарамды нөмірді енгізіңіз."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Қоңырау шалу мүмкін емес."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI қатарын бастау…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Қызметке қолдау көрсетілмейді."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Қоңырауларды ауыстыру мүмкін емес."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Қоңырауды бөлу мүмкін емес."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Тасымалдау мүмкін емес."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Конференция мүмкін емес."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Қоңырауды қабылдамау мүмкін емес."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Қоңырау(лар)ды босату мүмкін емес."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP қоңырауы"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Төтенше қоңырау"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Радио қосылуда…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Қызмет жоқ. Әрекет қайталануда…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Қоңырау шалу мүмкін емес. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> төтенше нөмір емес."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Қоңырау шалу мүмкін емес. Төтенше нөмірді теріңіз."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Теру үшін пернетақтаны пайдалану"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Қоңырауды ұстап тұру"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Қоңырауды жалғастыру"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Қоңырауды аяқтау"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Теру тақтасын көрсету"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Теру тақтасын жасыру"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Дыбысты өшіру"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Дыбысын қосу"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Қоңырау қосу"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Қоңырауларды біріктіру"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Алмастыру"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Қоңырауларды басқару"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Конференциялық қоңырауды басқару"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Конференциялық қоңырау"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Басқару"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Aудио"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Бейне қоңырау"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Дауыстық қоңырауға өзгерту"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Камераны ауыстыру"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Камераны қосу"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Камераны өшіру"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Қосымша опциялар"</string>
- <string name="player_started" msgid="3478865572468310331">"Ойнатқыш іске қосылды"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Ойнатқыш тоқтатылды"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Камера дайын емес"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Камера дайын"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Белгісіз қоңырау сеансы оқиғасы"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Қызмет"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Реттеу"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Орнатылмаған&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Басқа қоңырау параметрлері"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> арқылы қоңырау шалу"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> арқылы кіріс"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"контакт фотосуреті"</string>
- <string name="goPrivate" msgid="3554069451018659483">"жеке қоңырауға ауысу"</string>
- <string name="selectContact" msgid="92191462970821951">"контакт таңдау"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Өзіңіздікін жазыңыз..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Бас тарту"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Жіберу"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Жауап"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS жіберу"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Қабылдамау"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Бейне қоңырауға жауап беру"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Аудио қоңырауға жауап беру"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Бейне сұрауды қабылдау"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Бейне сұрауды қабылдамау"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Бейне тасымалдау сұрауын қабылдау"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Бейне тасымалдау сұрауын қабылдамау"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Бейне алу сұрауын қабылдау"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Бейне алу сұрауын қабылдамау"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> үшін жоғары сырғытыңыз."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> үшін сол жаққа сырғытыңыз."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> үшін оң жаққа сырғытыңыз."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> үшін төмен сырғытыңыз."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Діріл"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Діріл"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Дыбыс"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Әдепкі дыбыс (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Телефонның қоңырау әуені"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Шылдырлағанда дірілдеу"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Қоңырау әуені және діріл"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Конференциялық қоңырауды басқару"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Төтенше нөмір"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Профиль фотосуреті"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Камераны өшіру"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> арқылы"</string>
- <string name="note_sent" msgid="7623014827902758398">"Ескертпе жіберілді"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Жақындағы хабарлар"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Іскери ақпарат"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> миля қашықтықта"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> км қашықтықта"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Ертең <xliff:g id="OPEN_TIME">%s</xliff:g> уақытында ашылады"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Бүгін <xliff:g id="OPEN_TIME">%s</xliff:g> уақытында ашылады"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g> уақытында жабылады"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Бүгін <xliff:g id="CLOSE_TIME">%s</xliff:g> уақытында жабық"</string>
- <string name="open_now" msgid="4615706338669555999">"Қазір ашық"</string>
- <string name="closed_now" msgid="2635314668145282080">"Қазір жабық"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Спам қоңырау шалушы болуы мүмкін"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Қоңырау аяқталды (%1$s)"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Бұл нөмірдің сізге алғашқы қоңырау шалуы."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Бұл қоңырау спамер деп күдіктенеміз."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Бөгеу/спамға жіберу"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Контакт қосу"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Спам емес"</string>
-</resources>
diff --git a/InCallUI/res/values-km/strings.xml b/InCallUI/res/values-km/strings.xml
deleted file mode 100644
index 7a31d8933..000000000
--- a/InCallUI/res/values-km/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"ទូរស័ព្ទ"</string>
- <string name="onHold" msgid="527593602772521700">"រង់ចាំ"</string>
- <string name="unknown" msgid="3646075119047488748">"មិនស្គាល់"</string>
- <string name="private_num" msgid="6081418498487514686">"លេខ​ឯកជន"</string>
- <string name="payphone" msgid="5743050584468748607">"ទូរស័ព្ទសាធារណៈ"</string>
- <string name="confCall" msgid="3181961445236675173">"ការហៅជាក្រុម"</string>
- <string name="call_lost" msgid="8208184291640961172">"ការហៅទូរស័ព្ទបានដាក់ចុះ"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"ឧបករណ៍បំពងសម្លេង"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"អូប៉ាល័រសំឡេងទូរស័ព្ទ"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"កាសមានខ្សែ"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"ប៊្លូធូស"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"ផ្ញើសំឡេងដូចខាងក្រោមឬ?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"ផ្ញើ​សំឡេង \n"</string>
- <string name="send_button" msgid="4054398309483035794">"ផ្ញើ"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"បាទ/ចាស"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"ទេ"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"ជំនួស​តួ​អក្សរ​ជំនួស​ដោយ"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"ការហៅជាក្រុម <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"លេខ​សារ​ជា​សំឡេង"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"កំពុងហៅ"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"ការចុចហៅឡើងវិញ"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"ការហៅជាក្រុម"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"ការហៅចូល"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"កំពុងហៅចូលពីកន្លែងការងារ"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"បាន​បញ្ចប់​ការ​ហៅ"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"រង់ចាំ"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"បញ្ចប់​ការ​សន្ទនា"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"កំពុង​ហៅ"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"លេខ​របស់​ខ្ញុំ​គឺ <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"ភ្ជាប់​វីដេអូ"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"ហៅជាវីដេអូ"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"ស្នើ​វីដេអូ"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"មិនអាចភ្ជាប់ការហៅជាវីដេអូបានទេ"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"បានបដិសេធសំណើហៅជាវីដេអូ"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"លេខហៅទៅវិញរបស់អ្នក\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"លេខហៅទៅវិញពេលអាសន្នរបស់អ្នក\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"កំពុង​ហៅ"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"ខកខាន​ទទួល"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"ខកខាន​ទទួល"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"ខកខានទទួល <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ដង"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"ខកខាន​ទទួល​ពី <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"កំពុង​បន្ត​ការ​ហៅ"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"ការហៅពីកន្លែងការងារកំពុងដំណើរការ"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"ការហៅតាម Wi-Fi កំពុងបន្ត"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"ការហៅតាម Wi-Fi ពីកន្លែងការងារកំពុងដំណើរការ"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"រង់ចាំ"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"ការហៅចូល"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"កំពុងហៅចូលពីកន្លែងការងារ"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"មានការហៅចូលតាម Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"កំពុងហៅចូលពីកន្លែងការងារតាម Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"ការ​ហៅចូលជា​វីដេអូ​"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"ការ​ហៅ​បន្លំ​​ចូល​​​ដែល​សង្ស័យ"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"សំណើ​ការ​ហៅ​ជា​វីដេអូ​ចូល"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"សារ​ជា​សំឡេង​ថ្មី"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"សារ​ជា​សំឡេង​ថ្មី (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"ហៅ <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"លេខសារជាសំឡេងមិនស្គាល់"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"គ្មានសេវាទេ"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"បណ្ដាញ​ដែល​បាន​ជ្រើស ( <xliff:g id="OPERATOR_NAME">%s</xliff:g> ) មិន​អាច​ប្រើ​បាន​ទេ"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"ឆ្លើយតប"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"បញ្ចប់​ការ​សន្ទនា"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"វីដេអូ"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"សំឡេង"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"ព្រម​ទទួល"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"បដិសេធ"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"ហៅ​ទៅ​វិញ"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"សារ"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"ការ​ហៅ​កំពុង​ដំណើរការ​លើ​ឧបករណ៍​ផ្សេង"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"ផ្ទេរ​ការហៅ"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"ដើម្បីកំណត់ការហៅ សូមបិទរបៀបពេលជិះយន្តហោះជាមុនសិន"</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"មិនបានចុះឈ្មោះនៅលើបណ្ដាញទេ"</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"បណ្ដាញចល័តមិនអាចប្រើបានទេ"</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"ដើម្បីធ្វើការហៅ សូមបញ្ចូលលេខដែលត្រឹមត្រូវ"</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"មិនអាចហៅបានទេ"</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"កំពុងចាប់ផ្តើមលំដាប់ MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"សេវាកម្មមិនត្រូវបានគាំទ្រទេ"</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"មិនអាចប្តូរការហៅបានទេ"</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"មិនអាចបំបែកការហៅបានទេ"</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"មិនអាចផ្ទេរបានទេ"</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"មិនអាចធ្វើការហៅជាក្រុមបានទេ"</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"មិនអាចបដិសេធការហៅបានទេ"</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"មិនអាចធ្វើការហៅបានទេ"</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"ការ​ហៅ SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"ការ​ហៅ​ពេល​អាសន្ន"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"កំពុងបើកវិទ្យុ…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"គ្មានសេវាទេ សូមព្យាយាមម្តង…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"មិនអាចហៅបានទេ។ <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> មិនមែនជាលេខអាសន្នទេ"</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"មិនអាចហៅបានទេ សូមចុចហៅលេខអាសន្ន"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"ប្រើ​ក្ដារ​ចុច ​ដើម្បី​ចុច​លេខ"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"រង់ចាំការហៅ"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"បន្តការហៅ"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"បញ្ចប់ការហៅ"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"បង្ហាញ​បន្ទះ​លេខ"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"លាក់​បន្ទះ​លេខ"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"បិទ"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"បើក​សំឡេង"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"បន្ថែម​ការ​ហៅ"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"បញ្ចូល​ការ​ហៅ​ចូល​គ្នា"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"ប្ដូរ"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"គ្រប់គ្រង​ការ​ហៅ"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"គ្រប់គ្រងការហៅជាក្រុម"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"ការហៅជា​សន្និសិទ"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"គ្រប់គ្រង"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"សំឡេង"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"ហៅជាវីដេអូ"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"ប្ដូរ​ទៅ​ការ​ហៅ​ជា​សំឡេង"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"ប្ដូរកាមេរ៉ា"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"បើកកាមេរ៉ា"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"បិទកាមេរ៉ា"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"ជម្រើសច្រើនទៀត"</string>
- <string name="player_started" msgid="3478865572468310331">"អ្នកលេងបានចាប់ផ្តើម"</string>
- <string name="player_stopped" msgid="1278611664986561535">"អ្នកលេងបានឈប់"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"កាមេរ៉ាមិនទាន់ត្រៀមរួចរាល់ទេ"</string>
- <string name="camera_ready" msgid="2614541247814590887">"កាមេរ៉ាត្រៀមរួចរាល់ហើយ"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"ព្រឹត្តិការណ៍វេននៃការហៅមិនស្គាល់"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"សេវាកម្ម"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"ដំឡើង"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;មិន​បាន​កំណត់&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"​កំណត់​ការ​​ហៅ​ផ្សេងទៀត"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"ហៅតាមរយៈ <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"ចូល​តាមរយៈ <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"រូបថត​ទំនាក់ទំនង"</string>
- <string name="goPrivate" msgid="3554069451018659483">"ចូលជាលក្ខណៈឯកជន"</string>
- <string name="selectContact" msgid="92191462970821951">"ជ្រើសទំនាក់ទំនង"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"សរសេរដោយខ្លួនអ្នកផ្ទាល់..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"បោះបង់"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"ផ្ញើ"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"ឆ្លើយតប"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"ផ្ញើសារ SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"បដិសេធ"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"ឆ្លើយតប​ជា​ការ​ហៅ​ជា​​វីដេអូ"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"ឆ្លើយតប​ជា​ការ​ហៅ​ជា​សំឡេង"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"ទទួលយក​សំណើ​វីដេអូ"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"ទទួលយក​សំណើ​វីដេអូ"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"ទទួលយកសំណើបញ្ជូនជាវីដេអូ"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"បដិសេធសំណើបញ្ជូនជាវីដេអូ"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"ទទួលយកសំណើទទួលជាវីដេអូ"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"បដិសេធសំណើទទួលជាវីដេអូ"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"រុញឡើងលើដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
- <string name="description_direction_left" msgid="6811598791620851239">"រុញទៅឆ្វេងដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
- <string name="description_direction_right" msgid="5461971399586296023">"រុញទៅស្ដាំដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
- <string name="description_direction_down" msgid="3331715227997561639">"រុញចុះក្រោមដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"ញ័រ"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"ញ័រ"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"សំឡេង"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"សំឡេង​លំនាំដើម (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"សំឡេងរោទ៍ទូរស័ព្ទ"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"ញ័រពេលរោទ៍"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"សំឡេងរោទ៍ និងញ័រ"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"គ្រប់គ្រងការហៅជាក្រុម"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"លេខអាសន្ន"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"រូបថត​ប្រវត្តិរូប"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"បិទកាមេរ៉ា"</string>
- <string name="child_number" msgid="4469090994612105532">"តាមរយៈ <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"បានផ្ញើចំណាំ"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"សារថ្មីៗ"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"ព័ត៌មានធុរកិច្ច"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"ចម្ងាយ <xliff:g id="DISTANCE">%.1f</xliff:g> ម៉ាយល៍"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"ចម្ងាយ <xliff:g id="DISTANCE">%.1f</xliff:g> គម"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"បើកថ្ងៃស្អែកនៅម៉ោង <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"បើកថ្ងៃនេះនៅម៉ោង <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"បិទនៅម៉ោង <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"បានបិទថ្ងៃនេះនៅម៉ោង <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"បើកឥឡូវនេះ"</string>
- <string name="closed_now" msgid="2635314668145282080">"បិទឥឡូវនេះ"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"អ្នក​ហៅ​​បន្លំ​ដែល​សង្ស័យ"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"ការ​ហៅ​បាន​បញ្ចប់ %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"នេះ​គឺ​ជា​លើក​ដំបូង​ដែល​លេខ​នេះ​បាន​ហៅ​មក​អ្នក។"</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"យើង​បាន​សង្ស័យ​ថា​​ការ​ហៅ​នេះ​ជា​សារ​ឥត​បាន​ការ។"</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"រារាំង/រាយការណ៍សារឥតបានការ"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"បញ្ចូល​​ទំនាក់ទំនង"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"មិនមែន​សារ​ឥតបានការ"</string>
-</resources>
diff --git a/InCallUI/res/values-kn/strings.xml b/InCallUI/res/values-kn/strings.xml
deleted file mode 100644
index 441f6e189..000000000
--- a/InCallUI/res/values-kn/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"ಫೋನ್"</string>
- <string name="onHold" msgid="527593602772521700">"ತಡೆಹಿಡಿಯಲಾಗಿದೆ"</string>
- <string name="unknown" msgid="3646075119047488748">"ಅಪರಿಚಿತ"</string>
- <string name="private_num" msgid="6081418498487514686">"ಖಾಸಗಿ ಸಂಖ್ಯೆ"</string>
- <string name="payphone" msgid="5743050584468748607">"ಪೇಫೋನ್"</string>
- <string name="confCall" msgid="3181961445236675173">"ಕಾನ್ಫರೆನ್ಸ್ ಕರೆ"</string>
- <string name="call_lost" msgid="8208184291640961172">"ಕರೆಯನ್ನು ಬಿಡಲಾಗಿದೆ"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"ಸ್ಪೀಕರ್‌"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"ಹ್ಯಾಂಡ್‌ಸೆಟ್ ಇಯರ್‌ಪೀಸ್"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"ವೈರ್ಡ್ ಹೆಡ್‌ಸೆಟ್‌"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"ಬ್ಲೂಟೂತ್"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"ಕೆಳಗಿನ ಟೋನ್‌ಗಳನ್ನು ಕಳುಹಿಸುವುದೇ?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"ಟೋನ್‌ಗಳನ್ನು ಕಳುಹಿಸಲಾಗುತ್ತಿದೆ\n"</string>
- <string name="send_button" msgid="4054398309483035794">"ಕಳುಹಿಸು"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"ಹೌದು"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"ಇಲ್ಲ"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"ಇದರೊಂದಿಗೆ ವಿಶೇಷ ಅಕ್ಷರಗಳನ್ನು ಸ್ಥಳಾಂತರಿಸು"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"ಕಾನ್ಫರೆನ್ಸ್ ಕರೆ <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"ಧ್ವನಿಮೇಲ್‌ ಸಂಖ್ಯೆ"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"ಡಯಲ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"ಮರು ಡಯಲ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"ಕಾನ್ಫರೆನ್ಸ್ ಕರೆ"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"ಒಳಬರುವ ಕರೆ"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"ಒಳಬರುವ ಕೆಲಸದ ಕರೆ"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"ಕರೆ ಅಂತ್ಯಗೊಂಡಿದೆ"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"ತಡೆಹಿಡಿಯಲಾಗಿದೆ"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"ಹ್ಯಾಂಗ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"ಕರೆಯಲ್ಲಿ"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"ನನ್ನ ಸಂಖ್ಯೆ <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"ವೀಡಿಯೊ ಸಂಪರ್ಕಪಡಿಸಲಾಗುತ್ತಿದೆ"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"ವೀಡಿಯೊ ಕರೆ"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"ವೀಡಿಯೊ ವಿನಂತಿಸಲಾಗುತ್ತಿದೆ"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"ವೀಡಿಯೊ ಕರೆಯನ್ನು ಸಂಪರ್ಕಪಡಿಸಲಾಗುವುದಿಲ್ಲ"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"ವೀಡಿಯೊ ವಿನಂತಿಯನ್ನು ತಿರಸ್ಕರಿಸಲಾಗಿದೆ"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"ನಿಮ್ಮ ಮರಳಿಕರೆ ಮಾಡುವ ಸಂಖ್ಯೆ\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"ನಿಮ್ಮ ತುರ್ತು ಮರಳಿಕರೆ ಮಾಡುವ ಸಂಖ್ಯೆ\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"ಡಯಲ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"ಮಿಸ್ಡ್‌ ಕಾಲ್‌"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"ಮಿಸ್ಡ್ ಕಾಲ್‌ಗಳು"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ಮಿಸ್ಡ್ ಕಾಲ್‌ಗಳು"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> ಅವರಿಂದ ಮಿಸ್ಡ್ ಕಾಲ್"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಕರೆ"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಕೆಲಸದ ಕರೆ"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ ವೈ-ಫೈ ಕರೆ"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ ವೈ-ಫೈ ಕೆಲಸದ ಕರೆ"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"ತಡೆಹಿಡಿಯಲಾಗಿದೆ"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"ಒಳಬರುವ ಕರೆ"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"ಒಳಬರುವ ಕೆಲಸದ ಕರೆ"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"ಒಳಬರುವ ವೈ-ಫೈ ಕರೆ"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"ಒಳಬರುವ ವೈ-ಫೈ ಕೆಲಸದ ಕರೆ"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"ಒಳಬರುವ ವೀಡಿಯೊ ಕರೆ"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"ಒಳಬರುವ ಶಂಕಿತ ಸ್ಪ್ಯಾಮ್ ಕರೆ"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"ಒಳಬರುವ ವೀಡಿಯೊ ವಿನಂತಿ"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"ಹೊಸ ಧ್ವನಿಮೇಲ್‌"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"ಹೊಸ ಧ್ವನಿಮೇಲ್‌‌ (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> ಗೆ ಡಯಲ್‌‌ ಮಾಡು"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"ಅಪರಿಚಿತ ಧ್ವನಿಮೇಲ್‌ ಸಂಖ್ಯೆ"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"ಸೇವೆ ಇಲ್ಲ"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"ಆಯ್ಕೆಮಾಡಿದ (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) ನೆಟ್‌ವರ್ಕ್‌ ಲಭ್ಯವಿಲ್ಲ"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"ಉತ್ತರ"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"ಹ್ಯಾಂಗ್ ಅಪ್"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"ವೀಡಿಯೊ"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"ಧ್ವನಿ"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"ಸಮ್ಮತಿಸು"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"ವಜಾಗೊಳಿಸಿ"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"ಮರಳಿ ಕರೆ"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"ಸಂದೇಶ"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"ಮತ್ತೊಂದು ಸಾಧನದಲ್ಲಿ ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಕರೆ"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"ಕರೆ ವರ್ಗಾಯಿಸಿ"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"ಕರೆ ಮಾಡಲು, ಮೊದಲು ಏರ್‌ಪ್ಲೇನ್‌‌ ಮೋಡ್‌‌ ಆಫ್‌ ಮಾಡಿ."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"ನೆಟ್‌ವರ್ಕ್‌ನಲ್ಲಿ ಇನ್ನೂ ನೋಂದಣಿಯಾಗಿಲ್ಲ."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"ಸೆಲ್ಯುಲಾರ್ ನೆಟ್‌ವರ್ಕ್‌ ಲಭ್ಯವಿಲ್ಲ."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"ಕರೆಯನ್ನು ಮಾಡಲು, ಮಾನ್ಯವಾದ ಸಂಖ್ಯೆಯನ್ನು ನಮೂದಿಸಿ."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"ಕರೆ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI ಅನುಕ್ರಮ ಪ್ರಾರಂಭವಾಗುತ್ತಿದೆ…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"ಸೇವೆ ಬೆಂಬಲಿತವಾಗಿಲ್ಲ."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"ಕರೆಗಳನ್ನು ಬದಲಾಯಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"ಕರೆಯನ್ನು ಪ್ರತ್ಯೇಕಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"ವರ್ಗಾಯಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"ಕಾನ್ಫರೆನ್ಸ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"ಕರೆ ತಿರಸ್ಕರಿಸಲಾಗುವುದಿಲ್ಲ."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"ಕರೆ(ಗಳು) ಬಿಡುಗಡೆ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP ಕರೆ"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"ತುರ್ತು ಕರೆ"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"ರೇಡಿಯೋ ಆನ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"ಯಾವುದೇ ಸೇವೆ ಇಲ್ಲ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಲಾಗುತ್ತಿದೆ..."</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"ಕರೆ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> ತುರ್ತು ಸಂಖ್ಯೆಯಲ್ಲ."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"ಕರೆ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ತುರ್ತು ಸಂಖ್ಯೆಯನ್ನು ಡಯಲ್ ಮಾಡಿ."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"ಡಯಲ್‌ ಮಾಡಲು ಕೀಬೋರ್ಡ್‌ ಬಳಸಿ"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"ಕರೆಯನ್ನು ಹೋಲ್ಡ್‌‌ ಮಾಡು"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"ಕರೆಯನ್ನು ಮುಂದುವರಿಸಿ"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"ಕರೆ ಅಂತ್ಯಗೊಳಿಸಿ"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"ಡಯಲ್‌ಪ್ಯಾಡ್ ತೋರಿಸು"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"ಡಯಲ್‌ಪ್ಯಾಡ್ ಮರೆಮಾಡು"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"ಮ್ಯೂಟ್"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"ಅನ್‌ಮ್ಯೂಟ್"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"ಕರೆಯನ್ನು ಸೇರಿಸು"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"ಕರೆಗಳನ್ನು ವಿಲೀನಗೊಳಿಸು"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"ಸ್ವ್ಯಾಪ್‌ ಮಾಡು"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"ಕರೆಗಳನ್ನು ನಿರ್ವಹಿಸಿ"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"ಕಾನ್ಫರೆನ್ಸ್ ಕರೆಯನ್ನು ನಿರ್ವಹಿಸಿ"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"ಕಾನ್ಫರೆನ್ಸ್ ಕರೆ"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"ನಿರ್ವಹಿಸು"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"ಆಡಿಯೊ"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"ವೀಡಿಯೊ ಕರೆ"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"ಧ್ವನಿ ಕರೆಗೆ ಬದಲಾಯಿಸಿ"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"ಕ್ಯಾಮರಾ ಬದಲಿಸಿ"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"ಕ್ಯಾಮರಾ ಆನ್ ಮಾಡಿ"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"ಕ್ಯಾಮರಾ ಆಫ್ ಮಾಡಿ"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"ಇನ್ನಷ್ಟು ಆಯ್ಕೆಗಳು"</string>
- <string name="player_started" msgid="3478865572468310331">"ಪ್ಲೇಯರ್‌ ಪ್ರಾರಂಭವಾಗಿದೆ"</string>
- <string name="player_stopped" msgid="1278611664986561535">"ಪ್ಲೇಯರ್‌ ನಿಲ್ಲಿಸಲಾಗಿದೆ"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"ಕ್ಯಾಮರಾ ಸಿದ್ಧವಾಗಿಲ್ಲ"</string>
- <string name="camera_ready" msgid="2614541247814590887">"ಕ್ಯಾಮರಾ ಸಿದ್ಧವಾಗಿದೆ"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"ಅಪರಿಚಿತ ಕರೆಯ ಸೆಶನ್‌ ಈವೆಂಟ್‌"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"ಸೇವೆ"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"ಸೆಟಪ್"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;ಹೊಂದಿಸಿಲ್ಲ&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"ಇತರ ಕರೆ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> ಮೂಲಕ ಕರೆ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> ಮೂಲಕ ಒಳಬರುತ್ತಿರುವ ಕರೆ"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"ಸಂಪರ್ಕ ಫೋಟೋ"</string>
- <string name="goPrivate" msgid="3554069451018659483">"ಖಾಸಗಿಯಾಗಿ ಹೋಗಿ"</string>
- <string name="selectContact" msgid="92191462970821951">"ಸಂಪರ್ಕವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"ನಿಮ್ಮ ಸ್ವಂತದ್ದನ್ನು ಬರೆಯಿರಿ..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"ರದ್ದುಮಾಡಿ"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"ಕಳುಹಿಸು"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"ಉತ್ತರ"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS ಕಳುಹಿಸಿ"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"ನಿರಾಕರಿಸು"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"ವೀಡಿಯೊ ಕರೆ ರೂಪದಲ್ಲಿ ಉತ್ತರಿಸಿ"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"ಆಡಿಯೊ ಕರೆಯಂತೆ ಉತ್ತರಿಸಿ"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"ವೀಡಿಯೊ ವಿನಂತಿ ಒಪ್ಪಿಕೊಳ್ಳು"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"ವೀಡಿಯೊ ವಿನಂತಿ ತಿರಸ್ಕರಿಸು"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"ವೀಡಿಯೊ ಪ್ರಸಾರ ವಿನಂತಿ ಸಮ್ಮತಿಸಿ"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"ವೀಡಿಯೊ ಪ್ರಸಾರ ವಿನಂತಿ ತಿರಸ್ಕರಿಸಿ"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"ವೀಡಿಯೊ ಸ್ವೀಕರಿಸುವಿಕೆ ವಿನಂತಿ ಸಮ್ಮತಿಸಿ"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"ವೀಡಿಯೊ ಸ್ವೀಕರಿಸುವಿಕೆ ವಿನಂತಿ ತಿರಸ್ಕರಿಸಿ"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ಗೆ ಮೇಲಕ್ಕೆ ಸ್ಲೈಡ್ ಮಾಡಿ."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ಗೆ ಎಡಕ್ಕೆ ಸ್ಲೈಡ್ ಮಾಡಿ."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ಗೆ ಬಲಕ್ಕೆ ಸ್ಲೈಡ್ ಮಾಡಿ."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ಗೆ ಕೆಳಕ್ಕೆ ಸ್ಲೈಡ್ ಮಾಡಿ."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"ವೈಬ್ರೇಟ್‌"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"ವೈಬ್ರೇಟ್‌"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"ಶಬ್ದ"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"ಡಿಫಾಲ್ಟ್‌ ಧ್ವನಿ (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"ಫೋನ್ ರಿಂಗ್‌ಟೋನ್"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"ರಿಂಗ್ ಆಗುವಾಗ ವೈಬ್ರೇಟ್‌ ಆಗು"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"ರಿಂಗ್‌ಟೋನ್‌‌ ಮತ್ತು ವೈಬ್ರೇಟ್‌"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"ಕಾನ್ಫರೆನ್ಸ್ ಕರೆಯನ್ನು ನಿರ್ವಹಿಸಿ"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"ತುರ್ತು ಸಂಖ್ಯೆ"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"ಪ್ರೊಫೈಲ್ ಫೋಟೋ"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"ಕ್ಯಾಮರಾ ಆಫ್‌"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> ಮೂಲಕ"</string>
- <string name="note_sent" msgid="7623014827902758398">"ಟಿಪ್ಪಣಿ ಕಳುಹಿಸಲಾಗಿದೆ"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"ಇತ್ತೀಚಿನ ಸಂದೇಶಗಳು"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"ವ್ಯಾಪಾರ ಮಾಹಿತಿ"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> ಮೈಲು ದೂರ"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> ಕಿಮೀ ದೂರ"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"ನಾಳೆ <xliff:g id="OPEN_TIME">%s</xliff:g> ಗಂಟೆಗೆ ತೆರೆಯುತ್ತದೆ"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"ಇಂದು <xliff:g id="OPEN_TIME">%s</xliff:g> ಗಂಟೆಗೆ ತೆರೆಯುತ್ತದೆ"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g> ಗಂಟೆಗೆ ಮುಚ್ಚಲಾಗಿದೆ"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"ಇಂದು <xliff:g id="CLOSE_TIME">%s</xliff:g> ಗಂಟೆಗೆ ಮುಚ್ಚಲಾಗಿದೆ"</string>
- <string name="open_now" msgid="4615706338669555999">"ಇದೀಗ ತೆರೆಯಲಾಗಿದೆ"</string>
- <string name="closed_now" msgid="2635314668145282080">"ಇದೀಗ ಮುಚ್ಚಲಾಗಿದೆ"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"ಶಂಕಿತ ಸ್ಪ್ಯಾಮ್ ಕರೆದಾರರು"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"ಕರೆ ಮುಕ್ತಾಯಗೊಂಡಿದೆ %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"ಇದೇ ಮೊದಲ ಬಾರಿಗೆ ಈ ಸಂಖ್ಯೆಯಿಂದ ನಿಮಗೆ ಕರೆ ಮಾಡಲಾಗಿದೆ."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"ನಾವು ಈ ಕರೆಯನ್ನು ಸ್ಪ್ಯಾಮರ್‌ ಎಂದು ಶಂಕಿಸಿದ್ದೇವೆ."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"ಸ್ಪ್ಯಾಮ್ ನಿರ್ಬಂಧಿಸು/ವರದಿ ಮಾಡು"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"ಸಂಪರ್ಕ ಸೇರಿಸಿ"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"ಸ್ಪ್ಯಾಮ್‌ ಅಲ್ಲ"</string>
-</resources>
diff --git a/InCallUI/res/values-ko/strings.xml b/InCallUI/res/values-ko/strings.xml
deleted file mode 100644
index 973dc4637..000000000
--- a/InCallUI/res/values-ko/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"전화"</string>
- <string name="onHold" msgid="527593602772521700">"대기 중"</string>
- <string name="unknown" msgid="3646075119047488748">"알 수 없음"</string>
- <string name="private_num" msgid="6081418498487514686">"비공개 번호"</string>
- <string name="payphone" msgid="5743050584468748607">"공중전화"</string>
- <string name="confCall" msgid="3181961445236675173">"다자간 통화"</string>
- <string name="call_lost" msgid="8208184291640961172">"연락되지 않음"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"스피커"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"핸드셋 수화부"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"유선 헤드셋"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"블루투스"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"다음 톤을 보내시겠습니까?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"신호음 보내기\n"</string>
- <string name="send_button" msgid="4054398309483035794">"전송"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"예"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"아니요"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"와일드 문자를 다음으로 바꿈:"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"다자간 통화 <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"음성사서함 번호"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"전화 거는 중"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"재다이얼 중"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"다자간 통화"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"수신 전화"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"수신 업무 전화"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"통화 종료됨"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"대기 중"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"전화 끊는 중"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"통화 중"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"내 전화번호는 <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>입니다."</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"화상 통화 연결 중"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"화상 통화"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"화상 통화 요청 중"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"화상 통화를 연결할 수 없습니다."</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"화상 통화 요청이 거부되었습니다."</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"콜백 번호\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"긴급 콜백 번호\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"전화 거는 중"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"부재중 전화"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"부재중 전화"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"부재중 전화 <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>통"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g>의 부재중 전화"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"발신 전화"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"발신 업무 전화"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"발신 Wi-Fi 전화"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"발신 Wi-Fi 업무 전화"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"대기 중"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"수신 전화"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"수신 업무 전화"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Wi-Fi 수신 전화"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"수신 Wi-Fi 업무 전화"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"수신 화상 통화"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"의심스러운 스팸 발신자로부터 온 전화"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"수신 화상 통화 요청"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"새로운 음성사서함"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"새 음성사서함(<xliff:g id="COUNT">%d</xliff:g>개)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>(으)로 전화 걸기"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"알 수 없는 음성사서함 번호"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"서비스 불가"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"선택한 네트워크(<xliff:g id="OPERATOR_NAME">%s</xliff:g>)를 사용할 수 없음"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"전화 받기"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"전화 끊기"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"화상"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"음성"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"수락"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"해제"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"전화 걸기"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"메시지"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"다른 기기에서 진행 중인 통화"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"통화 전환"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"전화를 걸려면 먼저 비행기 모드를 해제하세요."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"네트워크에서 등록되지 않았습니다."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"사용 가능한 이동통신망이 없습니다."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"전화를 걸려면 올바른 번호를 입력하세요."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"전화를 걸 수 없습니다."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI 시퀀스 시작 중..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"서비스가 지원되지 않습니다."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"통화를 전환할 수 없습니다."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"통화를 분리할 수 없습니다."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"통화를 전환할 수 없습니다."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"다자간 통화를 이용할 수 없습니다."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"통화를 거부할 수 없습니다."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"통화를 끊을 수 없습니다."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP 통화"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"긴급 전화"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"무선을 켜는 중..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"서비스를 사용할 수 없습니다. 다시 시도 중..."</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"전화를 걸 수 없습니다. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g>은(는) 긴급 번호가 아닙니다."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"전화를 걸 수 없습니다. 긴급 번호를 사용하세요."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"키보드를 사용하여 전화 걸기"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"통화 대기"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"통화 재개"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"통화 종료"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"다이얼패드 표시"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"다이얼패드 숨기기"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"음소거"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"음소거 해제"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"통화 추가"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"통화 병합"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"전환"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"통화 관리"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"다자간 통화 관리"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"다자간 통화"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"관리"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"오디오"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"화상 통화"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"음성 통화로 변경"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"카메라 전환"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"카메라 켜기"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"카메라 끄기"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"옵션 더보기"</string>
- <string name="player_started" msgid="3478865572468310331">"플레이어가 시작되었습니다."</string>
- <string name="player_stopped" msgid="1278611664986561535">"플레이어가 중지되었습니다."</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"카메라가 준비되지 않았습니다."</string>
- <string name="camera_ready" msgid="2614541247814590887">"카메라가 준비되었습니다."</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"알 수 없는 통화 세션 이벤트"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"서비스"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"설정"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;설정 안됨&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"기타 통화 설정"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g>을(를) 통해 걸려온 전화"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g>을(를) 통해 걸려온 전화"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"연락처 사진"</string>
- <string name="goPrivate" msgid="3554069451018659483">"비공개로 실행"</string>
- <string name="selectContact" msgid="92191462970821951">"연락처 선택"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"나만의 응답 작성…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"취소"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"전송"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"전화 받기"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS 보내기"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"거부"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"화상 통화로 받기"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"음성 통화로 받기"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"화상 통화 요청 수락"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"화상 통화 요청 거부"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"화상 통화 전송 요청 허용"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"화상 통화 전송 요청 거부"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"화상 통화 수신 요청 허용"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"화상 통화 수신 요청 거부"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>하려면 위로 슬라이드합니다."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>하려면 왼쪽으로 슬라이드합니다."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>하려면 오른쪽으로 슬라이드합니다."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>하려면 아래로 슬라이드합니다."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"진동"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"진동"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"소리"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"기본 알림음(<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"전화 벨소리"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"전화 수신 시 진동"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"벨소리 및 진동"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"다자간 통화 관리"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"비상 전화번호"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"프로필 사진"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"카메라 꺼짐"</string>
- <string name="child_number" msgid="4469090994612105532">"수신 번호: <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"메모가 전송되었습니다."</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"최근 메시지"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"비즈니스 정보"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g>mi 거리"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g>km 거리"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>~<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"내일 <xliff:g id="OPEN_TIME">%s</xliff:g>에 영업 시작"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"오늘 <xliff:g id="OPEN_TIME">%s</xliff:g>에 영업 시작"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g>에 영업 종료"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"오늘 <xliff:g id="CLOSE_TIME">%s</xliff:g>에 영업 종료됨"</string>
- <string name="open_now" msgid="4615706338669555999">"영업 중"</string>
- <string name="closed_now" msgid="2635314668145282080">"영업 종료"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"의심스러운 스팸 발신자"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"%1$s번으로 끝나는 번호에서 걸려온 전화"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"이 번호에서 처음으로 걸려온 전화입니다."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"스팸 전화로 의심됩니다."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"스팸 차단/신고"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"연락처 추가"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"스팸 해제"</string>
-</resources>
diff --git a/InCallUI/res/values-ky/strings.xml b/InCallUI/res/values-ky/strings.xml
deleted file mode 100644
index bebdcfde8..000000000
--- a/InCallUI/res/values-ky/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Телефон"</string>
- <string name="onHold" msgid="527593602772521700">"Күтүлүүдө"</string>
- <string name="unknown" msgid="3646075119047488748">"Белгисиз"</string>
- <string name="private_num" msgid="6081418498487514686">"Купуя номер"</string>
- <string name="payphone" msgid="5743050584468748607">"Таксофон"</string>
- <string name="confCall" msgid="3181961445236675173">"Конференц-чалуу"</string>
- <string name="call_lost" msgid="8208184291640961172">"Чалуу үзүлдү"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Катуу сүйлөткүч"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Гарнитура"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Зымдуу гарнитура"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Төмөнкү номер жөнөтүлсүнбү?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Обондор жөнөтүлүүдө\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Жөнөтүү"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Ооба"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Жок"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Атайын белгини төмөнкүгө алмаштыруу"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Конференц-чалуу <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Үн почтасынын номери"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Терилүүдө"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Кайра терилүүдө"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Конференц-чалуу"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Кирүүчү чалуу"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Жумуш боюнча чалуу"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Чалуу аяктады"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Күтүлүүдө"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Чалуу аяктоодо"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Чалууда"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Менин номерим <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Видео туташтырылууда"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Видео чалуу"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Видео суралууда"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Видео чалууга туташуу мүмкүн болбой жатат"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Видео сурам четке кагылды"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Кайра чалына турган номер\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Өзгөчө кырдаалда кайра чалына турган номер\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Терилүүдө"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Кабыл алынбаган чалуу"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Кабыл алынбаган чалуулар"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> кабыл алынбаган чалуу"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> дегенден кабыл алынбаган чалуу"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Учурдагы чалуу"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Учурдагы чалуу (жумуш боюнча)"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Учурдагы Wi-Fi чалуу"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Учурдагы Wi-Fi чалуу (жумуш боюнча)"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Күтүлүүдө"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Кирүүчү чалуу"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Жумуш боюнча чалуу"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Кирүүчү Wi-Fi чалуу"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Жумуш боюнча келип жаткан Wi-Fi чалуу"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Кирүүчү видео чалуу"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Келип жаткан чалуу спам окшойт"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Кирүүчү видео сурамы"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Жаңы үн почтасы"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Жаңы үн почтасы (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> номерин терүү"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Үн почтасынын номери белгисиз"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Байланыш жок"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Тандалган тармак (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) жеткиликсиз"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Жооп берүү"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Чалууну бүтүрүү"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Видео"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Үн"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Кабыл алуу"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Этибарга албоо"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Кайра чалуу"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Билдирүү"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Башка түзмөктө сүйлөшүп жатасыз"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Чалууну бул түзмөккө өткөрүү"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Учак режимин өчүрүп туруп чалыңыз."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Тармакта катталган эмес."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Мобилдик тармак жеткиликтүү эмес."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Чалуу үчүн, жарактуу номер киргизиңиз."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Чалынбай жатат."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI кезеги башталууда…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Кызмат колдоого алынбайт."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Чалуулар которуштурулбай жатат."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Чалуу бөлүнбөй жатат."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Өткөрүлбөй жатат."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Конференц-чалуу түзүлбөй жатат."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Чалуу четке кагылбай жатат."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Чалуу (-лар) ажыратылбай жатат."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP чалуу"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Өзгөчө кырдаалда чалуу"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Радио күйгүзүлүүдө…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Кызмат жок. Кайра аракет кылууда…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Чалынбай жатат. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> өзгөчө кырдаал номери эмес."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Чалынбай жатат. Өзгөчө кырдаал номерин териңиз."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Баскычтоп менен териңиз"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Чалууну кармап туруу"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Чалууну улантуу"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Чалууну бүтүрүү"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Номер тергичти көрсөтүү"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Номер тергичти жашыруу"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Үнсүз"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Үндү чыгаруу"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Чалуу кошуу"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Чалууларды бириктирүү"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Алмаштыруу"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Чалууларды башкаруу"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Конференц-чалууну башкаруу"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Конференц чалуу"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Башкаруу"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Аудио"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Видео чалуу"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Үн чалууга өзгөртүү"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Камераны которуштуруу"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Камераны күйгүзүү"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Камераны өчүрүү"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Дагы параметрлер"</string>
- <string name="player_started" msgid="3478865572468310331">"Ойноткуч башталды"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Ойноткуч токтотулду"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Камера даяр эмес"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Камера даяр"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Чалуу сеансынын окуясы белгисиз"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Кызмат"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Орнотуу"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Коюлган эмес&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Башка чалуу жөндөөлөрү"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> аркылуу чалуу"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> аркылуу келүүдө"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"байланыштын сүрөтү"</string>
- <string name="goPrivate" msgid="3554069451018659483">"купуя режимине өтүү"</string>
- <string name="selectContact" msgid="92191462970821951">"байланыш тандоо"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Сиздин жообуңуз…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Жокко чыгаруу"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Жөнөтүү"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Жооп берүү"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS жөнөтүү"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Четке кагуу"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Видео чалуу түрүндө жооп берүү"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Аудио чалуу түрүндө жооп берүү"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Видео сурамын кабыл алуу"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Видео сурамын четке кагуу"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Видео өткөрүү сурамын кабыл алуу"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Видео өткөрүү сурамын четке кагуу"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Видео алуу сурамын кабыл алуу"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Видео алуу сурамын четке кагуу"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> үчүн жогору жылмыштырыңыз."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> үчүн солго жылмыштырыңыз."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> үчүн оңго жылмыштырыңыз."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> үчүн төмөн жылмыштырыңыз."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Дирилдөө"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Дирилдөө"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Үн"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Демейки үнү (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Телефондун рингтону"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Дирилдеп шыңгырасын"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Шыңгыр жана дирилдөө"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Конференц-чалууну башкаруу"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Өзгөчө кырдаал номери"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Профилдин сүрөтү"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Камера өчүк"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> аркылуу"</string>
- <string name="note_sent" msgid="7623014827902758398">"Билдирүү жөнөтүлдү"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Акыркы билдирүүлөр"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Компания тууралуу маалымат"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> миля алыста"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> км алыста"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> – <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Эртең саат <xliff:g id="OPEN_TIME">%s</xliff:g> ачылат"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Бүгүн саат <xliff:g id="OPEN_TIME">%s</xliff:g> ачылат"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Саат <xliff:g id="CLOSE_TIME">%s</xliff:g> жабылат"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Бүгүн саат <xliff:g id="CLOSE_TIME">%s</xliff:g> жабылды"</string>
- <string name="open_now" msgid="4615706338669555999">"Азыр ачык"</string>
- <string name="closed_now" msgid="2635314668145282080">"Эми жабылды"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Спам окшойт"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Чалуу %1$s бүттү"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Бул номер сизге биринчи жолу чалып жатат."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Бул чалуу спам окшойт."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Бөгөттөө/спам катары кабарлоо"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Байланыш кошуу"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Спам эмес"</string>
-</resources>
diff --git a/InCallUI/res/values-lo/strings.xml b/InCallUI/res/values-lo/strings.xml
deleted file mode 100644
index 3e7e81520..000000000
--- a/InCallUI/res/values-lo/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"ໂທລະສັບ"</string>
- <string name="onHold" msgid="527593602772521700">"ຖືສາຍລໍຖ້າ"</string>
- <string name="unknown" msgid="3646075119047488748">"ບໍ່ຮູ້ຈັກ"</string>
- <string name="private_num" msgid="6081418498487514686">"ເບີສ່ວນຕົວ"</string>
- <string name="payphone" msgid="5743050584468748607">"ຕູ້​ໂທ​ລະ​ສັບ​ສາ​ທາ​ລະ​ນະ"</string>
- <string name="confCall" msgid="3181961445236675173">"ການປະຊຸມທາງໂທລະສັບ"</string>
- <string name="call_lost" msgid="8208184291640961172">"ສາຍ​ຫຼຸດ​ແລ້ວ"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"ລຳໂພງ"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"ຊຸດຫູຟັງ"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"ຊຸດຫູຟັງແບບມີສາຍ"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"ສົ່ງໂທນສຽງຕໍ່ໄປນີ້ບໍ?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"ກຳລັງສົ່ງໂທນສຽງ\n"</string>
- <string name="send_button" msgid="4054398309483035794">"ສົ່ງ"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"ແມ່ນ"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"ບໍ່"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"ປ່ຽນແທນ \"ອັກຂະລະຕົວແທນ\" ດ້ວຍ"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"ການປະຊຸມທາງໂທລະສັບ <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"ເບີຂໍ້ຄວາມສຽງ"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"ກຳລັງໂທ"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"ກຳ​ລັງ​ໂທ​ຄືນ"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"ການປະຊຸມທາງໂທລະສັບ"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"​ສາຍ​ໂທ​ເຂົ້າ"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"ສາຍໂທເຂົ້າຈາກບ່ອນເຮັດວຽກ"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"ວາງສາຍແລ້ວ"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"ຖືສາຍລໍຖ້າ"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"ກຳລັງວາງສາຍ"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"ຢູ່ໃນສາຍ"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"ເບີໂທຂອງຂ້ອຍແມ່ນ <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"​ກຳ​ລັງ​ເຊື່ອມ​ຕໍ່​ວິ​ດີ​ໂອ"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"​ການໂທ​​ວິ​ດີ​ໂອ"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"​ກຳ​ລັງ​ຮ້ອງ​ຂໍການໂທ​ວິ​ດີ​ໂອ"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"ບໍ່​ສາ​ມາດ​ເຊື່ອມ​ຕໍ່​ການ​ໂທວິດີໂອ​ໄດ້"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"ປະ​ຕິ​ເສດ​ການຮ້ອງ​ຂໍການ​ໂທວິ​ດີ​ໂອ​ແລ້ວ"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"ເບີໂທກັບຂອງທ່ານ\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"ເບີ​ໂທ​ກັບ​ສຸກ​ເສີນ​ຂອງ​ທ່ານ\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"ກຳລັງໂທ"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"ສາຍບໍ່ໄດ້ຮັບ"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"ສາຍບໍ່ໄດ້ຮັບ"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ສາຍບໍ່ໄດ້ຮັບ"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"ສາຍບໍ່ໄດ້ຮັບຈາກ <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"ສາຍກຳລັງໂທ"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"ສາຍກຳລັງໂທຈາກບ່ອນເຮັດວຽກ"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"ສາຍກຳລັງໂທຜ່ານ Wi​-Fi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"ສາຍກຳລັງໂທຜ່ານ Wi-Fi ຈາກບ່ອນເຮັດວຽກ"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"ຖືສາຍລໍຖ້າ"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"​ສາຍ​ໂທ​ເຂົ້າ"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"ສາຍໂທເຂົ້າຈາກບ່ອນເຮັດວຽກ"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"ສາຍໂທເຂົ້າຜ່ານ Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"ສາຍໂທເຂົ້າຜ່ານ Wi-Fi ຈາກບ່ອນເຮັດວຽກ"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"ສາຍໂທ​ວິດີໂອ​ເຂົ້າ"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"ມີການໂທທີ່ຄາດວ່າເປັນສະແປມໂທເຂົ້າມາ"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"​ຄຳ​ຮ້ອງ​ຂໍ​ວິ​ດີ​ໂອທີ່​ເຂົ້າ​ມາ"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"ຂໍ້ຄວາມສຽງໃໝ່"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"ຂໍ້ຄວາມສຽງໃໝ່ (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"ໂທຫາ <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"ເບີຂໍ້ຄວາມສຽງບໍ່ຮູ້ຈັກ"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"ບໍ່ມີການບໍລິການ"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"ເຄືອຂ່າຍທີ່ເລືອກ (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) ບໍ່ສາມາດໃຊ້ໄດ້"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"ຮັບສາຍ"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"ວາງສາຍ"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"ວິດີໂອ"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"ສຽງ"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"ຍອມຮັບ"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"ປິດໄວ້"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"ໂທກັບ"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"ຂໍ້ຄວາມ"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"ສາຍທີ່ກຳລັງໂທອອກໃນອຸປະກອນອື່ນ"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"ໂອນສາຍ"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"ເພື່ອເຮັດການໂທ, ໃຫ້ປິດໂໝດເຮືອບິນກ່ອນ"</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"ບໍ່ໄດ້ລົງທະບຽນໃນເຄືອຂ່າຍ."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"ບໍ່​ມີ​ເຄືອ​ຂ່າຍ​ມື​ຖື​ທີ່​​ໃຊ້​ໄດ້."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"ເພື່ອເຮັດການ​ໂທ, ປ້ອນ​ເບີ​ໂທ​ທີ່​ໃຊ້​ໄດ້​."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"ບໍ່​ສາ​ມາດ​ໂທ​ໄດ້."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"ກຳລັງເລີ່ມຕົ້ນລຳດັບ MMI..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"ບໍ່ຮອງຮັບການ​ບໍ​ລິ​ການ."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"ບໍ່​ສາ​ມາດ​ສະ​ຫຼັບ​ສາ​ຍ​ໂທ​ໄດ້."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"ບໍ່​ສາ​ມາດ​ແຍກ​ສາຍ​ໂທ​ໄດ້."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"ບໍ່​ສາ​ມາດ​ໂອນສາຍ​ໄດ້."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"ບໍ່​ສາ​ມາດ​ປະ​ຊຸມ​ໄດ້."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"ບໍ່​ສາ​ມາດ​ປະ​ຕິ​ເສດ​ສາຍ​ໂທ​ໄດ້."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"ບໍ່​ສາ​ມາດ​ປ່ອຍ​ສາຍ​ໂທ​ໄດ້."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"ການໂທ SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"ການໂທສຸກເສີນ"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"ກຳລັງເປີດວິທະຍຸ"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"ບໍ່​ມີ​ການ​ບໍ​ລິ​ການ. ກຳ​ລັງ​ລອງ​ໃໝ່​ອີກ…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"ບໍ່ສາມາດໂທໄດ້. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> ບໍ່ແມ່ນເບີໂທສຸກເສີນ."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"ບໍ່​ສາ​ມາດ​ໂທ​ໄດ້. ກົດ​ເບີ​ໂທ​ສຸກ​ເສີນ."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"ໃຊ້ແປ້ນພິມເພື່ອກົດໂທ"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"ຖືສາຍ"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"​ສືບ​ຕໍ່​ສາຍ"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"ວາງສາຍ"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"ສະແດງປຸ່ມກົດ"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"ເຊື່ອງປຸ່ມກົດ"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"ປິດສຽງ"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"ເຊົາປິດສຽງ"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"ເພີ່ມການໂທ"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"ລວມສາຍ"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"ສະຫຼັບ"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"ຈັດການການໂທ"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"ຈັດ​ການ​ການ​ປະ​ຊຸມ​ທາງໂທລະສັບ"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"ການປະຊຸມທາງໂທລະສັບ"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"ຈັດການ"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"ສຽງ"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"​ການໂທ​​ວິ​ດີ​ໂອ"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"ປ່ຽນ​ເປັນ​ການ​ໂທ​ດ້ວຍ​ສຽງ"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"ສັບປ່ຽນກ້ອງ"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"ເປີດກ້ອງ"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"ປິດກ້ອງ"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"ຕົວເລືອກ​ເພີ່ມ​ເຕີມ"</string>
- <string name="player_started" msgid="3478865572468310331">"ເຄື່ອງ​ຫຼິ້ນ​ເລີ່ມ​ຕົ້ນ​ແລ້ວ"</string>
- <string name="player_stopped" msgid="1278611664986561535">"ເຄື່ອງ​ຫຼິ້ນ​ຢຸດ​ແລ້ວ"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"ກ້ອງ​ຖ່າຍ​ຮູບ​ບໍ່​ພ້ອມ"</string>
- <string name="camera_ready" msgid="2614541247814590887">"ກ້ອງ​ຖ່າຍ​ຮູບ​ພ້ອມ​ແລ້ວ"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"ເຫດ​ການ​ເຊ​ສ​ຊັນ​ການ​ໂທ​ບໍ່​ຮູ້​ຈັກ"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"ການບໍລິການ"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"ຕັ້ງຄ່າ"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;ບໍ່ໄດ້ຕັ້ງ&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"ການຕັ້ງຄ່າການໂທອື່ນ"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"ກຳລັງໂທຜ່ານ <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"ສາຍໂທເຂົ້າ​ຈາກ <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"ຮູບລາຍຊື່ຜູ້ຕິດຕໍ່"</string>
- <string name="goPrivate" msgid="3554069451018659483">"ໃຊ້ແບບສ່ວນຕົວ"</string>
- <string name="selectContact" msgid="92191462970821951">"ເລືອກລາຍຊື່ຜູ້ຕິດຕໍ່"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"ຂຽນ...ຂອງທ່ານເອງ"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"ຍົກເລີກ"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"ສົ່ງ"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"ຮັບສາຍ"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"ສົ່ງ SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"ປະຕິເສດ"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"ຮັບສາຍໂທວິດີໂອ"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"ຮັບສາຍໂທແບບສຽງ"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"ຍອມຮັບການຂໍວິດີໂອ"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"ປະຕິເສດການຂໍວິດີໂອ"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"ຍອມ​ຮັບ​ການ​ຂໍ​ສົ່ງ​ວິ​ດີ​ໂອ"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"ປະ​ຕິ​ເສດ​ການ​ຂໍ​ສົ່ງ​ວິ​ດີ​ໂອ"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"ຍອມ​ຮັບ​ການ​ຂໍ​ຮັບ​ວິ​ດີ​ໂອ"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"ປະ​ຕິ​ເສດ​ການ​ຂໍ​ຮັບ​ວິ​ດີ​ໂອ"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"ເລື່ອນຂຶ້ນເພື່ອ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"ເລື່ອນໄປຊ້າຍເພື່ອ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"ເລື່ອນໄປຂວາເພື່ອ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"ເລື່ອນລົງເພື່ອ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"ສັ່ນເຕືອນ"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"ສັ່ນເຕືອນ"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"ສຽງ"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"ສຽງເລີ່ມຕົ້ນ (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"ຣິງໂທນໂທລະສັບ"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"ສັ່ນເຕືອນເມື່ອດັງ"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"ຣິງໂທນ ແລະ ການສັ່ນເຕືອນ"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"ຈັດ​ການ​ການ​ປະ​ຊຸມ​ທາງໂທລະສັບ"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"ເບີໂທສຸກເສີນ"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"ຮູບໂປຣໄຟລ໌"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"ກ້ອງ​ຖ່າຍ​ຮູບ​ປິດຢູ່"</string>
- <string name="child_number" msgid="4469090994612105532">"ຜ່ານ <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"ສົ່ງ​ບັນ​ທຶກ​ແລ້ວ"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"ຂໍ້​ຄວາມ​ບໍ່​ດົນ​ມາ​ນີ້"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"ຂໍ້​ມູນ​ທຸ​ລະ​ກິດ"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"ຫ່າງອອກໄປ <xliff:g id="DISTANCE">%.1f</xliff:g> ໄມ​ລ໌​"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"ຫ່າງອອກໄປ <xliff:g id="DISTANCE">%.1f</xliff:g> ກມ"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"ເປີດມື້ອື່ນເວລາ <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"ເປີດມື້ນີ້ເວລາ <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"ປິດເວລາ <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"ປິດແລ້ວມື້ນີ້ເວລາ <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"ດຽວ​ນີ້​ເປີດ"</string>
- <string name="closed_now" msgid="2635314668145282080">"​ປິດ​ແລ້ວດຽວນີ້"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"ຄາດວ່າເປັນການໂທສະແປມ"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"ການໂທສິ້ນສຸດແລ້ວ %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"ນີ້ເປັນເທື່ອທຳອິດທີ່ເບີນີ້ໂທຫາທ່ານ."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"ພວກເຮົາສົງໄສວ່າເບີໂທນີ້ເປັນສະແປມ."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"ບລັອກ/ລາຍງານສະແປມ"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"ເພີ່ມລາຍຊື່ຜູ້ຕິດຕໍ່"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"ບໍ່ແມ່ນສະແປມ"</string>
-</resources>
diff --git a/InCallUI/res/values-lt/strings.xml b/InCallUI/res/values-lt/strings.xml
deleted file mode 100644
index 2d2a70146..000000000
--- a/InCallUI/res/values-lt/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefonas"</string>
- <string name="onHold" msgid="527593602772521700">"Sulaikyta"</string>
- <string name="unknown" msgid="3646075119047488748">"Nežinoma"</string>
- <string name="private_num" msgid="6081418498487514686">"Privatus numeris"</string>
- <string name="payphone" msgid="5743050584468748607">"Taksofonas"</string>
- <string name="confCall" msgid="3181961445236675173">"Konferencinis skambutis"</string>
- <string name="call_lost" msgid="8208184291640961172">"Skambutis atmestas"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Garsiakalbis"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Tel. su gars. prie ausies"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Laidinės ausinės"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Siųsti šiuo tonus?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Siunčiami tonai\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Siųsti"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Taip"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Ne"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Pakaitos simbolį pakeisti"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konferencinis skambutis <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Balso pašto numeris"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Renkamas numeris"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Numeris renkamas pakartotinai"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konferencinis skambutis"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Gaunamasis skambutis"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Gaunamasis darbo skambutis"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Skambutis baigtas"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Sulaikyta"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Baigiamas pokalbis"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Dalyvauju skambutyje"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Mano numeris: <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Prisijungiama prie vaizdo skambučio"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Vaizdo skambutis"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Pateikiama vaizdo skambučio užklausa"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Nepavyko prijungti vaizdo įrašo skambučio"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Vaizdo įrašo užklausa atmesta"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Atskambinimo numeris\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Atskambinimo numeris, kuriuos skambina pagalbos tarnyba\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Renkamas numeris"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Praleistas skambutis"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Praleisti skambučiai"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"Praleistų skambučių: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Praleistas skambutis nuo <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Vykstantis pokalbis"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Vykstantis darbo skambutis"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Vykstantis „Wi-Fi“ skambutis"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Vykstantis „Wi-Fi“ darbo skambutis"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Sulaikyta"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Gaunamasis skambutis"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Gaunamasis darbo skambutis"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Gaunamasis „Wi-Fi“ skambutis"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Gaunamasis „Wi-Fi“ darbo skambutis"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Gaunamas vaizdo skambutis"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Gaunamasis įtartinas šlamšto skambutis"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Gaunama vaizdo skambučio užklausa"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Naujas balso pašto pranešimas"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Naujas balso pašto pranešimas (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Rinkti <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Nežinomas balso pašto numeris"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Nėra paslaugos"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Pasirinktas tinklas (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) negalimas"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Atsiliepti"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Padėti ragelį"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Vaizdo įrašas"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Balsas"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Priimti"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Atsisakyti"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Perskambinti"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Siųsti pranešimą"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Kitame įrenginyje vykstantis skambutis"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Perkelti skambutį"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Jei norite skambinti, išjunkite lėktuvo režimą."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Neregistruota tinkle."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Korinis tinklas nepasiekiamas"</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Kad galėtumėte paskambinti, įveskite tinkamą numerį."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Nepavyko paskambinti."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Paleidžiama MMI seka..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Paslauga nepalaikoma."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Nepavyko perjungti skambučių."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Nepavyko atskirti skambučio."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Nepavyko peradresuoti."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Nepavyko sukurti konferencijos."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Nepavyko atmesti skambučio."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Nepavyko atjungti skamb."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP skambutis"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Skambutis pagalbos numeriu"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Įjungiamas radijas…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Nėra ryšio. Bandoma dar kartą…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Nepavyko paskambinti. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> nėra pagalbos numeris."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Nepavyko paskambinti. Surinkite pagalbos tarnybos numerį."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Naudokite klaviatūrą ir rinkite numerius"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Sulaikyti skambutį"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Tęsti skambutį"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Baigti skambutį"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Rodyti skambinimo skydelį"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Slėpti skambinimo skydelį"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Nutildyti"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Įjungti garsą"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Pridėti skambutį"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Sujungti skambučius"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Apkeisti"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Valdyti skambučius"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Tvarkyti konferencinį skambutį"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferencinis skambutis"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Tvarkyti"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Garsas"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Vaizdo skambutis"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Pakeisti į balso skambutį"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Perjungti fotoaparatą"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Įjungti fotoaparatą"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Išjungti fotoaparatą"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Daugiau parinkčių"</string>
- <string name="player_started" msgid="3478865572468310331">"Leistuvė paleista"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Leistuvė sustabdyta"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Fotoaparatas neparuoštas"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Fotoaparatas paruoštas"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Nežinomas skambučio sesijos įvykis"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Paslaugos teikėjas"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Sąranka"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Nenustatyta&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Kiti skambučio nustatymai"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Skambinama naudojantis „<xliff:g id="PROVIDER_NAME">%s</xliff:g>“ paslaugomis"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Gaunama per „<xliff:g id="PROVIDER_NAME">%s</xliff:g>“"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"kontakto nuotrauka"</string>
- <string name="goPrivate" msgid="3554069451018659483">"naudoti privatų režimą"</string>
- <string name="selectContact" msgid="92191462970821951">"pasirinkti kontaktą"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Sukurkite patys..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Atšaukti"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Siųsti"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Atsiliepti"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Siųsti SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Atmesti"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Atsiliepti kaip į vaizdo skambutį"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Atsiliepti kaip į garso skambutį"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Priimti vaizdo įrašo užkl"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Atmesti vaizdo įrašo užklausą"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Priimti vaizdo įrašo perdavimo užklausą"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Atmesti vaizdo įrašo perdavimo užklausą"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Priimti vaizdo įrašo gavimo užklausą"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Atmesti vaizdo įrašo gavimo užklausą"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Slyskite aukštyn link parinkties „<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>“."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Slyskite į kairę link parinkties „<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>“."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Slyskite į dešinę link parinkties „<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>“."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Slyskite žemyn link <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibruoti"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibruoti"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Garsas"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Numatytasis garsas (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Telefono skambėjimo tonas"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibruoti, kai skambina"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Skambėjimo tonas ir vibracija"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Tvarkyti konferencinį skambutį"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Pagalbos numeris"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profilio nuotrauka"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Fotoaparatas išjungtas"</string>
- <string name="child_number" msgid="4469090994612105532">"naudojant <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Užrašas išsiųstas"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Naujausi pranešimai"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Įmonės informacija"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"Už <xliff:g id="DISTANCE">%.1f</xliff:g> myl."</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"Už <xliff:g id="DISTANCE">%.1f</xliff:g> km"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>–<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Rytoj atidaroma <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Šiandien atidaroma <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Uždaroma <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Šiandien uždaryta <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Dabar atidaryta"</string>
- <string name="closed_now" msgid="2635314668145282080">"Dabar uždaryta"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Įt. skamb. dėl šl."</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Skambutis baigtas (%1$s)"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Tai pirmas kartas, kai jums buvo skambinama iš šio numerio."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Įtarėme, kad šis skambutis yra šlamštas."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Bl. / pran. apie šl."</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Pridėti kontaktą"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Ne šlamštas"</string>
-</resources>
diff --git a/InCallUI/res/values-lv/strings.xml b/InCallUI/res/values-lv/strings.xml
deleted file mode 100644
index 685ba8b0c..000000000
--- a/InCallUI/res/values-lv/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Tālrunis"</string>
- <string name="onHold" msgid="527593602772521700">"Aizturēts"</string>
- <string name="unknown" msgid="3646075119047488748">"Nezināms"</string>
- <string name="private_num" msgid="6081418498487514686">"Privāts numurs"</string>
- <string name="payphone" msgid="5743050584468748607">"Maksas tālrunis"</string>
- <string name="confCall" msgid="3181961445236675173">"Konferences zvans"</string>
- <string name="call_lost" msgid="8208184291640961172">"Zvans tika pārtraukts."</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Skaļrunis"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Auss skaļrunis"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Austiņas ar vadu"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Vai sūtīt tālāk norādītos signālus?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Sūtīšanas signāli\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Sūtīt"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Jā"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Nē"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Aizstāt aizstājējzīmi ar:"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konferences zvans: <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Balss pasta numurs"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Notiek numura sastādīšana"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Notiek atkārtota zvanīšana"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konferences zvans"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Ienākošs zvans"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Ienākošs darba zvans"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Zvans ir pabeigts"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Aizturēts"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Notiek klausules nolikšana"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Notiek zvans"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Mans tālruņa numurs: <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Notiek video savienojuma izveide"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videozvans"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Notiek video pieprasīšana"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Nevar veikt videozvanu"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Video pieprasījums noraidīts"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Jūsu atzvana numurs\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Jūsu ārkārtas atzvana numurs\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Notiek numura sastādīšana"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Neatbildēts zvans"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Neatbildēti zvani"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> neatbildēti zvani"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Neatbildēts zvans no: <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Notiekošs zvans"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Notiekošs darba zvans"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Notiekošs Wi-Fi zvans"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Notiekošs darba Wi-Fi zvans"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Aizturēts"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Ienākošs zvans"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Ienākošs darba zvans"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Ienākošs Wi-Fi zvans"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Ienākošs darba Wi-Fi zvans"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Ienākošs videozvans"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Ienākošs, iespējams, nevēlams zvans"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Ienākošs video pieprasījums"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Jauns balss pasta ziņojums"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Jauns balss pasts (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Sastādiet šādu numuru: <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Balss pasta numurs nav zināms."</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Nav pakalpojuma"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Atlasītais tīkls (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) nav pieejams."</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Atbildēt"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Beigt zvanu"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Balss"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Pieņemt"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Noraidīt"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Atzvanīt"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Sūtīt īsziņu"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Notiekošs zvans citā ierīcē"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Pāradresēt zvanu"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Lai veiktu zvanu, vispirms izslēdziet lidojuma režīmu."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Nav reģistrēts tīklā."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobilais tīkls nav pieejams."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Lai veiktu zvanu, ievadiet derīgu numuru."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Nevar veikt zvanu."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Notiek MMI secības startēšana…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Pakalpojums netiek atbalstīts."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Nevar pārslēgt zvanus."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Nevar nošķirt zvanu."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Nevar pārsūtīt."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Nevar veikt konferences zvanu."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Nevar noraidīt zvanu."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Nevar pārtraukt zvanu(-us)."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP zvans"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Ārkārtas izsaukums"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Notiek radio ieslēgšana…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Nav pakalpojuma. Notiek atkārtots mēģinājums…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Nevar veikt zvanu. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> nav ārkārtas numurs."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Nevar veikt zvanu. Zvaniet ārkārtas numuram."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Izmantojiet tastatūru, lai sastādītu numuru"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Aizturēt zvanu"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Atsākt zvanu"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Beigt zvanu"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Rādīt numura sastādīšanas tastatūru"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Slēpt numura sastādīšanas tastatūru"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Izslēgt skaņu"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Ieslēgt skaņu"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Pievienot zvanu"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Apvienot zvanus"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Mainīt"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Pārvaldīt zvanus"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Pārvaldīt konferences zvanu"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferences zvans"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Pārvaldīt"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videozvans"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Mainīt uz balss zvanu"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Pārslēgt kameru"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Ieslēgt kameru"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Izslēgt kameru"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Citas iespējas"</string>
- <string name="player_started" msgid="3478865572468310331">"Atskaņošana sākta"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Atskaņošana apturēta"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kamera nav gatava"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kamera ir gatava"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Nezināms zvana sesijas notikums"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Pakalpojums"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Iestatīšana"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Nav iestatīts&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Citi zvanu iestatījumi"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Zvans, ko nodrošina <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Ienākošie zvani, ko nodrošina <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"kontaktpersonas fotoattēls"</string>
- <string name="goPrivate" msgid="3554069451018659483">"pārslēgt uz privāto režīmu"</string>
- <string name="selectContact" msgid="92191462970821951">"atlasīt kontaktpersonu"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Rakstīt savu…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Atcelt"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Sūtīt"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Atbildēt"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Sūtīt īsziņu"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Noraidīt"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Atbildēt videozvanā"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Atbildēt audiozvanā"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Apstiprināt video pieprasījumu"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Noraidīt video pieprasījumu"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Apstiprināt video pārsūtīšanas pieprasījumu"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Noraidīt video pārsūtīšanas pieprasījumu"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Apstiprināt video saņemšanas pieprasījumu"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Noraidīt video saņemšanas pieprasījumu"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Velciet uz augšu, lai veiktu šādu darbību: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Velciet pa kreisi, lai veiktu šādu darbību: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Velciet pa labi, lai veiktu šādu darbību: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Velciet uz leju, lai veiktu šādu darbību: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrācija"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrācija"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Signāls"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Noklusējuma signāls (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Tālruņa zvana signāls"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrācija zvana laikā"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Zvana signāls un vibrācija"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Konferences zvana pārvaldība"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Ārkārtas numurs"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profila fotoattēls"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera ir izslēgta"</string>
- <string name="child_number" msgid="4469090994612105532">"no numura <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Piezīme nosūtīta"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Pēdējie ziņojumi"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informācija par uzņēmumu"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> jūdzes(-džu) attālumā"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km attālumā"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>–<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Tiks atvērts rīt plkst. <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Tiks atvērts šodien plkst. <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Tiks slēgts plkst. <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Tika slēgts šodien plkst. <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Atvērts"</string>
- <string name="closed_now" msgid="2635314668145282080">"Slēgts"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Nevēlams zvanītājs"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Zvans beidzās: %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Šis jums ir pirmais zvans no šī numura."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Iespējams, šis zvans bija no nevēlama zvanītāja."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Bloķēt numuru/ziņot"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Pievienot personu"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Nav nevēlams numurs"</string>
-</resources>
diff --git a/InCallUI/res/values-mk/strings.xml b/InCallUI/res/values-mk/strings.xml
deleted file mode 100644
index 3161c5475..000000000
--- a/InCallUI/res/values-mk/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Телефон"</string>
- <string name="onHold" msgid="527593602772521700">"На чекање"</string>
- <string name="unknown" msgid="3646075119047488748">"Непознат"</string>
- <string name="private_num" msgid="6081418498487514686">"Приватен број"</string>
- <string name="payphone" msgid="5743050584468748607">"Говорница"</string>
- <string name="confCall" msgid="3181961445236675173">"Конференциски повик"</string>
- <string name="call_lost" msgid="8208184291640961172">"Повикот е прекинат"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Звучник"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Слушалка"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Жичени слушалки"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Испратете ги следниве тонови?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Се испраќаат тонови\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Испрати"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Да"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Не"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Заменете го резервниот знак со"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Конференциски повик <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Број на говорна пошта"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Бирање"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Повторно бирање"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Конференциски повик"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Дојдовен повик"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Дојдовен работен повик"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Повикот заврши"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"На чекање"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Повикот се прекинува"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Повик во тек"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Мојот број е <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Се поврзува видео"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Видеоповик"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Се бара видео"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Не може да се поврзе видеоповик"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Барањето за видео е одбиено"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Вашиот број за повратен повик\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Вашиот број за итен повик\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Бирање"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Пропуштен повик"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Пропуштени повици"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> пропуштени повици"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Пропуштен повик од <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Тековен повик"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Тековен работен повик"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Појдовен повик преку Wi-Fi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Тековен работен повик преку Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"На чекање"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Дојдовен повик"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Дојдовен работен повик"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Дојдовен повик преку Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Дојдовен работен повик преку Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Дојдовен видеоповик"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Дојдовниот повик може да е спам"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Дојдовно барање за видео"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Нова говорна пошта"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Нова говорна пошта (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Бирај <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Непознат број на говорна пошта"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Нема услуга"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Избраната мрежа (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) е недостапна"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Одговори"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Спушти"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Видео"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Гласовен"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Прифати"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Отфрли"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Врати повик"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Порака"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Повик во тек на друг уред"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Префрлање повик"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"За да остварите повик, прво исклучете го авионскиот режим."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Не е регистриран на мрежа."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Не е достапна мобилна мрежа."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"За да остварите повик, внесете важечки број."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Не може да се повика."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Започнува ММИ низа..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Услугата не е поддржана."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Не може да се префрлат повици."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Не може да се оддели повик."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Не може да се пренесе."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Не може да се оствари конференциски повик."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Не може да се отфрли повик."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Не може да се оствари повик."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Повик преку SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Повик за итни случаи"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Се вклучува радиото..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Нема услуга. Се обидува повторно…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Не може да се повика. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> не е број за итни повици."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Не може да се повика. Бирајте го бројот за итни повици."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Користете ја тастатурата за бирање"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Стави на чекање"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Продолжи го повикот"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Заврши го повикот"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Прикажи копчиња за бирање"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Сокриј копчиња за бирање"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Исклучи звук"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Вклучи звук"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Додај повик"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Спои повици"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Замени"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Управувај со повици"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Управувај со конференциски повик"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Конференциски повик"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Управувај"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Аудио"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Видеоповик"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Промени во гласовен повик"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Промени ја камерата"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Вклучете ја камерата"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Исклучете ја камерата"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Повеќе опции"</string>
- <string name="player_started" msgid="3478865572468310331">"Плеерот се вклучи"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Плеерот запре"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Камерата не е подготвена"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Камерата е подготвена"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Непознат настан при сесија повици"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Услуга"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Поставување"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Нема поставка&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Други поставки за повик"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Повикување преку <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Дојдовни повици преку <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"фотографија на контакт"</string>
- <string name="goPrivate" msgid="3554069451018659483">"префли на приватно"</string>
- <string name="selectContact" msgid="92191462970821951">"избери контакт"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Напиши сопствена..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Откажи"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Испрати"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Одговори"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Испрати SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Одбиј"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Одговори со видеоповик"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Одговори со аудиоповик"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Прифати барање за видео"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Одбиј барање за видео"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Прифати барање за пренос на видео"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Одбиј барање за пренос на видео"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Прифати барање за прием на видео"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Одбиј барање за прием на видео"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Лизгај нагоре за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Лизгај налево за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Лизгај надесно за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Лизгај надолу за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Вибрации"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Вибрации"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Звук"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Стандарден звук (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Мелодија на телефонот"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Вибрации при ѕвонење"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Мелодија и вибрации"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Управувај со конференциски повик"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Број за итни случаи"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Фотографија на профилот"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Камерата е исклучена"</string>
- <string name="child_number" msgid="4469090994612105532">"преку <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Испратена е белешка"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Неодамнешни пораки"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Деловни информации"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"Оддалечено <xliff:g id="DISTANCE">%.1f</xliff:g> милји"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"Оддалечено <xliff:g id="DISTANCE">%.1f</xliff:g> км"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Отвора утре во <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Отвора денес во <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Затвора во <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Денес затвори во <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Сега е отворено"</string>
- <string name="closed_now" msgid="2635314668145282080">"Сега е затворено"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Повикот е можен спам"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Повикот заврши %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"За првпат добивте повик од бројов."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Постоеше сомнеж дека повиков е спам."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Блок./пријави спам"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Додајте го контактот"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Не е спам"</string>
-</resources>
diff --git a/InCallUI/res/values-ml/strings.xml b/InCallUI/res/values-ml/strings.xml
deleted file mode 100644
index 5c313ad31..000000000
--- a/InCallUI/res/values-ml/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"ഫോൺ"</string>
- <string name="onHold" msgid="527593602772521700">"ഹോൾഡിലാണ്"</string>
- <string name="unknown" msgid="3646075119047488748">"അജ്ഞാതം"</string>
- <string name="private_num" msgid="6081418498487514686">"സ്വകാര്യ നമ്പർ"</string>
- <string name="payphone" msgid="5743050584468748607">"പണം നൽകി ഉപയോഗിക്കുന്ന ഫോൺ"</string>
- <string name="confCall" msgid="3181961445236675173">"കോൺഫറൻസ് കോൾ"</string>
- <string name="call_lost" msgid="8208184291640961172">"കോൾ വിട്ടു"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"സ്പീക്കർ"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"ഹാൻഡ്‌സെറ്റ് ഇയർപീസ്"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"വയേർഡ് ഹെഡ്സെറ്റ്"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"ഇനിപ്പറയുന്ന ടോണുകൾ അയയ്‌ക്കണോ?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"ടോണുകൾ അയയ്‌ക്കുന്നു\n"</string>
- <string name="send_button" msgid="4054398309483035794">"അയയ്‌ക്കുക"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"ഉവ്വ്"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"ഇല്ല"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"വൈൽഡ് പ്രതീകം ഇതുപയോഗിച്ച് മാറ്റിസ്ഥാപിക്കുക"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"കോൺഫറൻസ് കോൾ <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"വോയ്‌സ്‌മെയിൽ നമ്പർ"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"ഡയൽ ചെയ്യുന്നു"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"വീണ്ടും ഡയൽചെയ്യുന്നു"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"കോൺഫറൻസ് കോൾ"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"ഇൻകമിംഗ് കോൾ"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"ഇൻകമിംഗ് ഔദ്യോഗിക കോൾ"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"കോൾ അവസാനിച്ചു"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"ഹോൾഡിലാണ്"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"ഹാംഗ് അപ്പ് ചെയ്യുന്നു"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"കോളിലാണ്"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"എന്റെ നമ്പർ <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g> ആണ്"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"വീഡിയോ കണക്‌റ്റുചെയ്യുന്നു"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"വീഡിയോ കോൾ"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"വീഡിയോ അഭ്യർത്ഥിക്കുന്നു"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"വീഡിയോ കോളുമായി കണക്‌റ്റുചെയ്യാനാവില്ല"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"വീഡിയോ അഭ്യർത്ഥന നിരസിച്ചു"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"നിങ്ങൾ തിരിച്ചുവിളിക്കേണ്ട നമ്പർ\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"അടിയന്തിരമായി നിങ്ങൾ തിരിച്ചുവിളിക്കേണ്ട നമ്പർ\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"ഡയൽ ചെയ്യുന്നു"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"മിസ്‌ഡ് കോൾ"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"മിസ്‌ഡ് കോളുകൾ"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> മിസ്‌ഡ് കോളുകൾ"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> എന്നതിൽ നിന്നുള്ള മിസ്‌ഡ് കോൾ"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"കോൾ സജീവമാണ്"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"ഓൺഗോയിംഗ് ഔദ്യോഗിക കോൾ"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"ഓൺഗോയിംഗ് വൈഫൈ കോൾ"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"ഓൺഗോയിംഗ് വൈഫൈ ഔദ്യോഗിക കോൾ"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"ഹോൾഡിലാണ്"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"ഇൻകമിംഗ് കോൾ"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"ഇൻകമിംഗ് ഔദ്യോഗിക കോൾ"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"ഇൻകമിംഗ് വൈഫൈ കോൾ"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"ഇൻകമിംഗ് വൈഫൈ ഔദ്യോഗിക കോൾ"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"ഇൻകമിംഗ് വീഡിയോ കോൾ"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"സംശയാസ്‌പദമായ ഇൻകമിംഗ് സ്‌പാം കോൾ"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"ഇൻകമിംഗ് വീഡിയോ അഭ്യർത്ഥന"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"പുതിയ വോയ്‌സ്‌മെയിൽ"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"പുതിയ വോയ്‌സ്‌മെയിൽ (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> ഡയൽ ചെയ്യുക"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"വോയ്‌സ്‌മെയിൽ നമ്പർ അജ്ഞാതമാണ്"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"സേവനമില്ല"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"തിരഞ്ഞെടുത്ത നെറ്റ്‌വർക്ക് (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) ലഭ്യമല്ല"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"മറുപടി"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"ഹാംഗ് അപ്പുചെയ്യുക"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"വീഡിയോ"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"വോയ്‌സ്"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"അംഗീകരിക്കുക"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"ഡിസ്മിസ്"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"തിരിച്ചുവിളിക്കുക"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"സന്ദേശം"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"മറ്റൊരു ഉപകരണത്തിൽ നടന്നുകൊണ്ടിരിക്കുന്ന കോൾ"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"കോൾ കൈമാറുക"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"ഒരു കോൾ ചെയ്യാൻ, ആദ്യം ഫ്ലൈറ്റ് മോഡ് ഓഫുചെയ്യുക."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"നെറ്റ്‌വർക്കിൽ രജിസ്റ്റർ ചെയ്‌തിട്ടില്ല."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"സെല്ലുലാർ നെറ്റ്‌വർക്ക് ലഭ്യമല്ല."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"ഒരു കോൾ ചെയ്യുന്നതിന്, സാധുതയുള്ള നമ്പർ നൽകുക."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"കോൾ ചെയ്യാനായില്ല."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI സീക്വൻസ് ആരംഭിക്കുന്നു…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"സേവനം പിന്തുണയ്‌ക്കുന്നില്ല."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"കോളുകൾ മാറാനാവില്ല."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"കോൾ വേർതിരിക്കാനാവില്ല."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"കൈമാറ്റം ചെയ്യാനാവില്ല."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"കോൺഫറൻസ് കോൾ ചെയ്യാനാവില്ല."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"കോൾ നിരസിക്കാനാവില്ല."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"കോൾ (കോളുകൾ) വിളിക്കാനാവില്ല."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP കോൾ"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"എമർജൻസി കോൾ"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"റേഡിയോ ഓൺ ചെയ്യുന്നു…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"സേവനമൊന്നുമില്ല. വീണ്ടും ശ്രമിക്കുന്നു…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"കോൾ ചെയ്യാനാവില്ല. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> എന്നത് ഒരു അടിയന്തിര നമ്പറല്ല."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"കോൾ ചെയ്യാനാവില്ല. ഒരു അടിയന്തിര കോൾ നമ്പർ ഡയൽചെയ്യുക."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"ഡയൽ ചെയ്യാൻ കീബോർഡ് ഉപയോഗിക്കുക"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"കോൾ ഹോൾഡുചെയ്യുക"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"കോൾ പുനരാരംഭിക്കുക"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"കോൾ അവസാനിപ്പിക്കുക"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"ഡയൽപാഡ് കാണിക്കുക"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"ഡയൽപാഡ് മറയ്‌ക്കുക"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"മ്യൂട്ടുചെയ്യുക"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"അൺമ്യൂട്ടുചെയ്യുക"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"കോൾ ചേർക്കുക"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"കോളുകൾ ലയിപ്പിക്കുക"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"സ്വാപ്പുചെയ്യുക"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"കോളുകൾ നിയന്ത്രിക്കുക"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"കോൺഫറൻസ് കോൾ നിയന്ത്രിക്കുക"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"കോൺഫറൻസ് കോൾ"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"മാനേജുചെയ്യുക"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"ഓഡിയോ"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"വീഡിയോ കോൾ"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"വോയ്‌സ്‌ കോളിലേക്ക് മാറ്റുക"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"ക്യാമറ സ്വിച്ചുചെയ്യുക"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"ക്യാമറ ഓണാക്കുക"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"ക്യാമറ ഓഫാക്കുക"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"കൂടുതൽ ഓ‌പ്‌ഷനുകൾ"</string>
- <string name="player_started" msgid="3478865572468310331">"പ്ലെയർ ആരംഭിച്ചു"</string>
- <string name="player_stopped" msgid="1278611664986561535">"പ്ലേയർ നിർത്തി"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"ക്യാമറ തയ്യാറായില്ല"</string>
- <string name="camera_ready" msgid="2614541247814590887">"ക്യാമറ തയ്യാറായി"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"അജ്ഞാത കോൾ സെഷൻ ഇവന്റ്"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"സേവനം"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"സജ്ജമാക്കുക"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;ക്രമീകരിച്ചിട്ടില്ല&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"മറ്റ് കോൾ ക്രമീകരണം"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> മുഖേന വിളിക്കുന്നു"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> മുഖേനയുള്ള ഇൻകമിംഗ്"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"കോൺടാക്റ്റ് ഫോട്ടോ"</string>
- <string name="goPrivate" msgid="3554069451018659483">"സ്വകാര്യം എന്നതിലേക്ക് പോകുക"</string>
- <string name="selectContact" msgid="92191462970821951">"കോൺടാക്റ്റ് തിരഞ്ഞെടുക്കുക"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"നിങ്ങളുടെ സ്വന്തം സന്ദേശമെഴുതുക..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"റദ്ദാക്കുക"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"അയയ്‌ക്കുക"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"മറുപടി"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS അയയ്ക്കുക"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"നിരസിക്കുക"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"വീഡിയോ കോളായി മറുപടി നൽകുക"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"ഓഡിയോ കോളായി മറുപടി നൽകുക"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"വീഡിയോ കോളിനുള്ള അഭ്യർത്ഥന അംഗീകരിക്കുക"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"വീഡിയോ കോൾ അഭ്യർത്ഥന നിരസിക്കുക"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"വീഡിയോ പ്രക്ഷേപണ അഭ്യർത്ഥന അംഗീകരിക്കുക"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"വീഡിയോ പ്രക്ഷേപണ അഭ്യർത്ഥന നിരസിക്കുക"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"വീഡിയോ കോൾ സ്വീകരിക്കാനുള്ള അഭ്യർത്ഥന അംഗീകരിക്കുക"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"വീഡിയോ കോൾ സ്വീകരിക്കാനുള്ള അഭ്യർത്ഥന നിരസിക്കുക"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> എന്നതിനായി മുകളിലേയ്‌ക്ക് സ്ലൈഡുചെയ്യുക."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> എന്നതിനായി ഇടത്തേയ്‌ക്ക് സ്ലൈഡുചെയ്യുക."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> എന്നതിനായി വലത്തേയ്‌ക്ക് സ്ലൈഡുചെയ്യുക."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> എന്നതിനായി താഴേക്ക് സ്ലൈഡുചെയ്യുക."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"വൈബ്രേറ്റുചെയ്യുക"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"വൈബ്രേറ്റുചെയ്യുക"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"ശബ്‌ദം"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"സ്ഥിര ശബ്‌ദം (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"ഫോൺ റിംഗ്ടോൺ"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"റിംഗുചെയ്യുമ്പോൾ വൈബ്രേറ്റുചെയ്യുക"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"റിംഗ്ടോണും വൈബ്രേറ്റുചെയ്യലും"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"കോൺഫറൻസ് കോൾ നിയന്ത്രിക്കുക"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"അടിയന്തര നമ്പർ"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"പ്രൊഫൈൽ ഫോട്ടോ"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"ക്യാമറ ഓഫാക്കുക"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> വഴി"</string>
- <string name="note_sent" msgid="7623014827902758398">"കുറിപ്പ് അയച്ചു"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"ഏറ്റവും പുതിയ സന്ദേശങ്ങൾ"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"ബിസിനസ്സ് വിവരം"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> മൈൽ അകലെ"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> കിലോമീറ്റർ അകലെ"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"നാളെ <xliff:g id="OPEN_TIME">%s</xliff:g>-ന് തുറക്കുന്നു"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"ഇന്ന് <xliff:g id="OPEN_TIME">%s</xliff:g>-ന് തുറക്കുന്നു"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g>-ന് അടയ്ക്കുന്നു"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"ഇന്ന് <xliff:g id="CLOSE_TIME">%s</xliff:g>-ന് അടച്ചു"</string>
- <string name="open_now" msgid="4615706338669555999">"ഇപ്പോൾ തുറന്നിരിക്കുന്നു"</string>
- <string name="closed_now" msgid="2635314668145282080">"ഇപ്പോൾ അടച്ചിരിക്കുന്നു"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"സംശയാസ്‌പദമായ സ്‌പാം കോളർ"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"കോൾ അവസാനിച്ചു, %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"ഈ നമ്പറിൽ നിന്ന് ആദ്യമായാണ് നിങ്ങൾക്ക് കോൾ വരുന്നത്."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"ഈ കോൾ ഒരു സ്‌പാമർ ആണെന്ന് ഞങ്ങൾക്ക് സംശയമുണ്ടായിരുന്നു."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"ബ്ലോക്കുചെയ്യുക/സ്പാമാണെന്ന് റിപ്പോർട്ടുചെയ്യുക"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"കോൺടാക്റ്റ് ചേർക്കുക"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"സ്പാം അല്ല"</string>
-</resources>
diff --git a/InCallUI/res/values-mn/strings.xml b/InCallUI/res/values-mn/strings.xml
deleted file mode 100644
index 972f914a4..000000000
--- a/InCallUI/res/values-mn/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Утас"</string>
- <string name="onHold" msgid="527593602772521700">"Хүлээлгэнд байгаа"</string>
- <string name="unknown" msgid="3646075119047488748">"Тодорхойгүй"</string>
- <string name="private_num" msgid="6081418498487514686">"Нууцалсан дугаар"</string>
- <string name="payphone" msgid="5743050584468748607">"Төлбөртэй утас"</string>
- <string name="confCall" msgid="3181961445236675173">"Хурлын дуудлага"</string>
- <string name="call_lost" msgid="8208184291640961172">"Дуудлага таслагдсан"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Чанга яригч"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Утасны чихэвч"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Утастай чихэвч"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Дараах аяыг илгээх үү?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Ая илгээж байна\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Илгээх"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Тийм"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Үгүй"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Тэмдэгтийг дараахаар солих"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Хурлын дуудлага <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Дуут шуудангийн дугаар"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Залгаж байна"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Дахин залгаж байна"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Хурлын дуудлага"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Орох дуудлага"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Орох ажлын дуудлага"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Дуудлага дууссан"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Хүлээлгэнд"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Тасалж байна"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Дуудлагатай"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Миний дугаар <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Видеог холбож байна"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Видео дуудлага"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Видео хүлээж байна"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Видео дуудлагад холбогдож чадсангүй"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Бичлэг хийх хүсэлтийг зөвшөөрсөнгүй"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Таны буцаан залгах дугаар\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Таны яаралтай хулээн авах дугаар\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Залгаж байна"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Аваагүй дуудлага"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Аваагүй дуудлага"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> аваагүй дуудлага"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g>-н аваагүй дуудлага"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Залгаж буй дуудлага"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Холбогдсон албаны дуудлага"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Холбогдсон Wi-Fi дуудлага"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Залгаж буй Wi-Fi албаны дуудлага"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Хүлээгдэж байна"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Орох дуудлага"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Орох ажлын дуудлага"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Орох Wi-Fi дуудлага"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Орох Wi-Fi албаны дуудлага"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Орох видео дуудлага"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Орж ирсэн сэжигтэй спам дуудлага"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Орох видео хүсэлт"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Шинэ дуут шуудан"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Шинэ дуут шуудан (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> руу залгах"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Дуут шуудангийн дугаар тодорхойгүй"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Үйлчилгээ байхгүй"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Сонгосон сүлжээг (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) ашиглах боломжгүй"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Хариулт"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Таслах"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Видео"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Дуу хоолой"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Зөвшөөрөх"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Алгасах"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Буцааж залгах"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Зурвас"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Өөр төхөөрөмж дээр хийгдэж буй дуудлага"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Дуудлага шилжүүлэх"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Залгахын тулд эхлээд Нислэгийн горимоос гарна уу."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Сүлжээнд бүртгэгдээгүй байна."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Үүрэн сүлжээ байхгүй."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Залгахын тулд хүчин төгөлдөр дугаар оруулна уу."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Залгах боломжгүй байна."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI дарааллыг эхлүүлж байна…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Дэмжигдээгүй үйлчилгээ байна."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Дуудлагыг солих боломжгүй байна."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Дуудлагыг салгаж чадахгүй байна."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Шилжүүлэх боломжгүй байна."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Хурлын дуудлага хийх боломжгүй байна."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Дуудлагыг цуцлах боломжгүй байна."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Дуудлага чөлөөлөх боломжгүй байна."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP дуудлага"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"яаралтай"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Радиог асааж байна..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Ажиллагаагүй байна. Дахин оролдоно уу..."</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Залгах боломжгүй. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> нь яаралтай дугаар биш байна."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Залгах боломжгүй. Яаралтай дугаар луу залгана уу."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Залгахдаа гар ашиглана уу"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Дуудлага хүлээлгэх"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Дуудлагыг үргэлжлүүлэх"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Дуудлагыг дуусгах"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Залгах товчлуурыг харуулах"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Залгах товчлуурыг нуух"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Дуу хаах"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Дууг нээх"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Дуудлага нэмэх"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Дуудлага нэгтгэх"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Солих"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Дуудлага удирдах"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Хурлын дуудлага удирдах"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Хурлын дуудлага"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Удирдах"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Аудио"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Видео дуудлага"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Дуут дуудлага руу өөрчлөх"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Камер солих"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Камераа асаана уу"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Камер унтраах"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Нэмэлт сонголт"</string>
- <string name="player_started" msgid="3478865572468310331">"Тоглуулагчийг эхлүүлсэн"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Тоглуулагчийг зогсоосон"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Камер бэлэн бус байна"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Камер бэлэн байна"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Үл мэдэгдэх дуудлагын үе"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Үйлчилгээ"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Тохируулга"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"Тохируулаагүй"</string>
- <string name="other_settings" msgid="6699076019841942826">"Бусад дуудлагын тохиргоо"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g>-р залгаж байна"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g>-р ирж байна"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"харилцагчийн зураг"</string>
- <string name="goPrivate" msgid="3554069451018659483">"хувийн яриа"</string>
- <string name="selectContact" msgid="92191462970821951">"харилцагч сонгох"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Өөрийн ...-г бичээрэй"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Цуцлах"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Илгээх"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Хариулт"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS илгээх"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Татгалзах"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Видео дуудлагаар хариулах"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Аудио дуудлагаар хариулах"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Видео хүсэлтийг хүлээн зөвшөөрөх"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Видео хүсэлтээс татгалзах"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Видео дамжуулах хүсэлтийг хүлээн зөвшөөрөх"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Видео дамжуулах хүсэлтээс татгалзах"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Видео хүлээж авах хүсэлтийг зөвшөөрөх"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Видео хүлээн авах хүсэлтээс татгалзах"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> хийх бол дээш гулсуулна уу."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>-г харахын тулд зүүн тийш гулсуулна уу."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> харахын тулд баруун тийш гулсуулна уу."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>-г харахын тулд доош гулсуулна уу."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Чичиргээ"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Чичиргээ"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Дуу"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Үндсэн дуу (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Утасны хонхны ая"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Хонх дуугарах үед чичрэх"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Хонхны ая, Чичиргээ"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Хурлын дуудлагыг удирдах"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Яаралтай дугаар"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Профайл зураг"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Камер унтраалттай байна"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g>-р"</string>
- <string name="note_sent" msgid="7623014827902758398">"Тэмдэглэлийг илгээсэн"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Саяхны зурвас"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Бизнес мэдээлэл"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> милийн зайтай"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> км-н зайтай"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Маргааш <xliff:g id="OPEN_TIME">%s</xliff:g>-с нээгдэнэ"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Өнөөдөр <xliff:g id="OPEN_TIME">%s</xliff:g>-с нээгдэнэ"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g>-с хаадаг"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Өнөөдөр <xliff:g id="CLOSE_TIME">%s</xliff:g>-с хаасан"</string>
- <string name="open_now" msgid="4615706338669555999">"Одоо нээлттэй"</string>
- <string name="closed_now" msgid="2635314668145282080">"Одоо хаалттай"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Сэжигтэй спам дуудлага хийгч"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Дуудлага дууссан %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Энэ дугаараас танд анх удаа дуудлага ирсэн."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Бид үүнийг спам дуудлага гэж үзсэн."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Спам гэж мэдээлэх/хориглох"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Харилцагч нэмэх"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Спам биш"</string>
-</resources>
diff --git a/InCallUI/res/values-mr/strings.xml b/InCallUI/res/values-mr/strings.xml
deleted file mode 100644
index 15b3dfc32..000000000
--- a/InCallUI/res/values-mr/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"फोन"</string>
- <string name="onHold" msgid="527593602772521700">"होल्ड वर"</string>
- <string name="unknown" msgid="3646075119047488748">"अज्ञात"</string>
- <string name="private_num" msgid="6081418498487514686">"खाजगी नंबर"</string>
- <string name="payphone" msgid="5743050584468748607">"सार्वजनिक फोन"</string>
- <string name="confCall" msgid="3181961445236675173">"परिषद कॉल"</string>
- <string name="call_lost" msgid="8208184291640961172">"कॉल सोडला"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"स्पीकर"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"हँडसेट इअरपीस"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"वायर्ड हेडसेट"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"ब्लूटुथ"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"खालील टोन पाठवायचे?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"टोन पाठवित आहे\n"</string>
- <string name="send_button" msgid="4054398309483035794">"पाठवा"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"होय"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"नाही"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"खराब वर्णास यासह पुनर्स्थित करा"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"परिषद कॉल <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"व्हॉइसमेल नंबर"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"डायल करीत आहे"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"रीडायल करत आहे"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"परिषद कॉल"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"येणारा कॉल"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"येणारा कार्य कॉल"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"कॉल संपला"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"होल्ड वर"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"हँग अप करणेे"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"कॉल मधील"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"माझा नंबर <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g> आहे"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"व्हिडिओ कनेक्ट करत आहे"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"व्हिडिओ कॉल"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"व्हिडिओ विनंती करत आहे"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"व्हिडिओ कॉल कनेक्ट करू शकत नाही"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"व्हिडिओ विनंती नाकारली"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"आपला कॉलबॅक नंबर\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"आपला आणीबाणी कॉलबॅक नंबर\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"डायल करीत आहे"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"सुटलेला कॉल"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"सुटलेले कॉल"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> सुटलेले कॉल"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> कडील सुटलेला कॉल"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"सुरू असलेला कॉल"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"सुरु असलेला कार्य कॉल"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"सुरु असलेला वाय-फाय कॉल"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"सुरु असलेला वाय-फाय कार्य कॉल"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"होल्ड वर"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"येणारा कॉल"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"येणारा कार्य कॉल"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"येणारा वाय-फाय कॉल"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"येणारा वाय-फाय कार्य कॉल"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"येणारा व्हिडिओ कॉल"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"येणारा संशयित स्पॅम कॉल"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"येणारी व्हिडिओ विनंती"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"नवीन व्हॉइसमेल"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"नवीन व्हॉइसमेल (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> डायल करा"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"व्हॉइसमेल नंबर अज्ञात"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"सेवा नाही"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"निवडलेले नेटवर्क (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) अनुपलब्ध"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"उत्तर"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"हँग अप"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"व्हिडिओ"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"व्हॉइस"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"स्वीकार करा"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"डिसमिस करा"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"पुन्हा कॉल करा"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"संदेश"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"दुसऱ्या डिव्हाइसवर सुरु असलेला कॉल"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"कॉल स्थानांतरित करा"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"कॉल करण्यासाठी, प्रथम विमान मोड बंद करा."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"नेटवर्कवर नोंदणीकृत नाही."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"मोबाईल नेटवर्क उपलब्ध नाही."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"कॉल करण्यासाठी, एक वैध नंबर प्रविष्ट करा."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"कॉल करू शकत नाही."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI क्रम प्रारंभ करीत आहे..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"सेवा समर्थित नाही."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"कॉल स्विच करू शकत नाही."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"कॉल विभक्त करू शकत नाही."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"हस्तांतर करू शकत नाही."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"परिषद घेऊ शकत नाही."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"कॉल नाकारू शकत नाही."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"कॉल रिलीज करू शकत नाही."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP कॉल"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"आणीबाणी कॉल"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"रेडिओ चालू करीत आहे..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"सेवा नाही. पुन्हा प्रयत्न करत आहे…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"कॉल करू शकत नाही. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> हा आणीबाणी नंबर नाही."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"कॉल करू शकत नाही. आणीबाणी नंबर डायल करा."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"डायल करण्यासाठी कीबोर्डचा वापर करा"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"कॉल होल्ड करा"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"कॉल पुनः सुरु करा"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"कॉल समाप्त करा"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"डायलपॅड दर्शवा"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"डायलपॅड लपवा"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"नि:शब्द करा"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"सशब्द करा"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"कॉल जोडा"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"कॉल विलीन करा"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"अदलाबदल करा"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"कॉल व्यवस्थापित करा"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"परिषद कॉल व्यवस्थापित करा"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"परिषद कॉल"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"व्यवस्थापित करा"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"ऑडिओ"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"व्हिडिओ कॉल"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"व्हॉइस कॉल वर बदला"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"कॅमेरा स्विच करा"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"कॅमेरा चालू करा"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"कॅमेरा बंद करा"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"अधिक पर्याय"</string>
- <string name="player_started" msgid="3478865572468310331">"प्लेअर सुरु झाले"</string>
- <string name="player_stopped" msgid="1278611664986561535">"प्लेअर थांबले"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"कॅमेरा तयार नाही"</string>
- <string name="camera_ready" msgid="2614541247814590887">"कॅमेरा तयार"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"अज्ञात कॉल सत्र इव्हेंट"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"सेवा"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"सेटअप"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;सेट नाही&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"इतर कॉल सेटिंग्ज"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> द्वारे कॉल करीत आहे"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> द्वारे येणारे"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"संपर्क फोटो"</string>
- <string name="goPrivate" msgid="3554069451018659483">"खाजगी व्हा"</string>
- <string name="selectContact" msgid="92191462970821951">"संपर्क निवडा"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"आपण स्वतः लिहा…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"रद्द करा"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"पाठवा"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"उत्तर"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS पाठवा"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"नकार द्या"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"व्हिडिओ कॉल म्हणून उत्तर द्या"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"ऑडिओ कॉल म्हणून उत्तर द्या"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"व्हिडिओ विनंती स्वीकारा"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"व्हिडिओ विनंतीस नकार द्या"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"व्हिडिओ प्रसारण विनंती स्वीकार करा"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"व्हिडिओ प्रसारण विनंतीस नकार द्या"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"व्हिडिओ प्राप्त करा विनंती स्वीकार करा"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"व्हिडिओ प्राप्त करा विनंतीस नकार द्या"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> साठी वर स्लाइड करा."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> साठी डावीकडे स्लाइड करा."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> साठी उजवीकडे स्लाइड करा."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> साठी खाली स्लाइड करा."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"कंपन करा"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"कंपन करा"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"ध्वनी"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"डीफॉल्ट आवाज (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"फोन रिंगटोन"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"रिंग करताना कंपन करा"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"रिंगटोन आणि कंपन"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"परिषद कॉल व्यवस्थापित करा"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"आणीबाणी नंबर"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"प्रोफाइल फोटो"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"कॅमेरा बंद"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> द्वारा"</string>
- <string name="note_sent" msgid="7623014827902758398">"टीप पाठविली"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"अलीकडील संदेश"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"व्यवसाय माहिती"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> मैल दूर"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> किमी दूर"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"उद्या <xliff:g id="OPEN_TIME">%s</xliff:g> वाजता उघडेल"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"आज <xliff:g id="OPEN_TIME">%s</xliff:g> उघडेल"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"आज <xliff:g id="CLOSE_TIME">%s</xliff:g> वाजता बंद होईल"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"आज <xliff:g id="CLOSE_TIME">%s</xliff:g> वाजता बंद केले"</string>
- <string name="open_now" msgid="4615706338669555999">"आता उघडा"</string>
- <string name="closed_now" msgid="2635314668145282080">"आता बंद केले आहे"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"संशयित स्पॅम कॉलर"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"कॉल समाप्त झाला %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"या नंबरने अापल्याला कॉल केल्याची ही पहिलीच वेळ आहे."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"अाम्हाला संशय अाहे की हा कॉल एक स्पॅमर असू शकतो."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"अवरोधित करा/स्पॅमचा अहवाल द्या"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"संपर्क जोडा"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"स्पॅम नाही"</string>
-</resources>
diff --git a/InCallUI/res/values-ms/strings.xml b/InCallUI/res/values-ms/strings.xml
deleted file mode 100644
index 4c7be3805..000000000
--- a/InCallUI/res/values-ms/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"Ditunda"</string>
- <string name="unknown" msgid="3646075119047488748">"Tidak diketahui"</string>
- <string name="private_num" msgid="6081418498487514686">"Nombor peribadi"</string>
- <string name="payphone" msgid="5743050584468748607">"Telefon Awam"</string>
- <string name="confCall" msgid="3181961445236675173">"Panggilan sidang"</string>
- <string name="call_lost" msgid="8208184291640961172">"Panggilan diputuskan"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Pembesar suara"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Alat dengar tel bimbit"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Set kepala berwayar"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Hantar nada berikut?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Menghantar nada\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Hantar"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Ya"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Tidak"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Gantikan aksara bebas dengan"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Panggilan sidang <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Nombor mel suara"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Mendail"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Mendail semula"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Panggilan sidang"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Panggilan masuk"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Pgln masuk tempat kerja"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Panggilan tamat"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Ditunda"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Menamatkan panggilan"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Dalam panggilan"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Nombor saya ialah <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Menyambungkan video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Panggilan video"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Meminta video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Tidak dapat menyambungkan panggilan video"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Permintaan video ditolak"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Nombor panggilan balik anda<xliff:g id="DARK_NUMBER">%1$s</xliff:g>\n"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Nombor panggilan balik kecemasan anda\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Mendail"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Panggilan terlepas"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Panggilan terlepas"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> panggilan terlepas"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Panggilan tidak dijawab daripada <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Panggilan sedang berlangsung"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Panggilan sedang berlangsung daripada tempat kerja"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Panggilan Wi-Fi sedang berlangsung"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Panggian Wi-Fi sedang berlangsung daripada tempat kerja"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Ditunda"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Panggilan masuk"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Panggilan masuk daripada tempat kerja"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Panggilan masuk melalui Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Panggilan masuk melalui Wi-Fi daripada tempat kerja"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Panggilan video masuk"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Disyaki panggilan spam masuk"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Permintaan video masuk"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Mel suara baharu"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Mel suara baharu (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Dail <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Nombor mel suara tidak dikenali"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Tiada perkhidmatan"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Rangkaian pilihan (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) tidak tersedia"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Jawab"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Letakkan gagang"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Suara"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Terima"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Ketepikan"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Panggil balik"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Mesej"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Panggilan sedang berlangsung pada peranti lain"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Pindahkan Panggilan"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Untuk membuat panggilan, matikan mod Pesawat terlebih dahulu."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Tidak didaftarkan pada rangkaian."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Rangkaian selular tidak tersedia."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Untuk membuat panggilan, masukkan nombor yang sah."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Tidak dapat memanggil."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Memulakan jujukan MMI..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Perkhidmatan tidak disokong."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Tidak dapat menukar panggilan."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Tidak dapat mengasingkan panggilan."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Tidak dapat memindahkan."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Tidak dapat membuat panggilan persidangan."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Tidak dapat menolak panggilan."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Tidak dapat melepaskan panggilan."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Panggilan SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Panggilan kecemasan"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Menghidupkan radio..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Tiada perkhidmatan. Mencuba lagi..."</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Tidak dapat memanggil. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> bukan nombor kecemasan."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Tidak dapat memanggil. Dail nombor kecemasan."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Gunakan papan kekunci untuk mendail"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Tahan Panggilan"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Sambung Semula Panggilan"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Tamatkan Panggilan"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Tunjukkan Pad Pendail"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Sembunyikan Pad Pendail"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Redam"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Nyahredam"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Tambah panggilan"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Gabung panggilan"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Silih"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Urus panggilan"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Urus panggilan sidang"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Panggilan sidang"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Urus"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Panggilan video"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Tukar ke panggilan suara"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Tukar kamera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Hidupkan kamera"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Matikan kamera"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Lagi pilihan"</string>
- <string name="player_started" msgid="3478865572468310331">"Pemain Dimulakan"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Pemain Dihentikan"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kamera tidak bersedia"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kamera bersedia"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Acara sesi panggilan tidak diketahui"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Perkhidmatan"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Persediaan"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Tidak ditetapkan&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Tetapan panggilan lain"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Memanggil melalui <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Panggilan masuk melalui <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"foto kenalan"</string>
- <string name="goPrivate" msgid="3554069451018659483">"jadi peribadi"</string>
- <string name="selectContact" msgid="92191462970821951">"pilih kenalan"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Tulis sendiri…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Batal"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Hantar"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Jawab"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Hantar SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Tolak"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Jawab sebagai panggilan video"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Jawab sebagai panggilan audio"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Terima permintaan video"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Tolak permintaan video"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Terima permintaan hantar video"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Tolak permintaan hantar video"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Terima permintaan terima video"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Tolak permintaan terima video"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Luncurkan ke atas untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Luncurkan ke kiri untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Luncurkan ke kanan untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Luncurkan ke bawah untuk <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Bergetar"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Bergetar"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Bunyi"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Bunyi lalai (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Nada dering telefon"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Bergetar apabila berdering"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Nada dering &amp; Bergetar"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Urus panggilan sidang"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Nombor kecemasan"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Foto profil"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera dimatikan"</string>
- <string name="child_number" msgid="4469090994612105532">"melalui <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Nota dihantar"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Mesej terbaharu"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Maklumat perniagaan"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> batu dari sini"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km dari sini"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Dibuka esok pada pukul <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Dibuka hari ini pada pukul <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Tutup pada pukul <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Ditutup hari ini pada pukul <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Dibuka sekarang"</string>
- <string name="closed_now" msgid="2635314668145282080">"Ditutup sekarang"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Disyaki pmggil spam"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Panggilan tamat %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Ini kali pertama nombor ini memanggil anda."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Kami mengesyaki panggilan ini adalah spam."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Sekat/laporkan spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Tambahkan kenalan"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Bukan spam"</string>
-</resources>
diff --git a/InCallUI/res/values-my/strings.xml b/InCallUI/res/values-my/strings.xml
deleted file mode 100644
index efe91436f..000000000
--- a/InCallUI/res/values-my/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"ဖုန်း"</string>
- <string name="onHold" msgid="527593602772521700">"ခဏ ကိုင်ထားစဉ်"</string>
- <string name="unknown" msgid="3646075119047488748">"အမျိုးအမည်မသိ"</string>
- <string name="private_num" msgid="6081418498487514686">"ကိုယ်ပိုင်ဖုန်းနံပါတ်"</string>
- <string name="payphone" msgid="5743050584468748607">"အများသုံးဖုန်း"</string>
- <string name="confCall" msgid="3181961445236675173">"အစည်းအဝေးခေါ်ဆိုမှု"</string>
- <string name="call_lost" msgid="8208184291640961172">"ဖုန်းလိုင်းကျသွားခဲ့သည်"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"စပီကာ"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"လက်ကိုင်တယ်လီဖုန်းနားခွက်"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"ကြိုးတပ် မိုက်ခွက်ပါနားကြပ်"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"ဘလူးတုသ်"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"အောက်ပါ တီးလုံးများကို ပို့မလား။\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"အသံများ ပို့နေသည်\n"</string>
- <string name="send_button" msgid="4054398309483035794">"ပို့ပါ"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Yes"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"No"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"အစားထိုး အထူးအက္ခရာတွင် အစားထိုးရန်"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"အစည်းအဝေးခေါ်ဆိုမှု <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"အသံစာနံပါတ်"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"ခေါ်ဆိုနေသည်"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"ပြန်ခေါ်နေသည်"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"အစည်းအဝေးခေါ်ဆိုမှု"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"အဝင် ခေါ်ဆိုမှု"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"အလုပ်ဆိုင်ရာ အဝင် ခေါ်ဆိုမှု"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"ဖုန်းခေါ်ဆိုမှု ပြီးဆုံးပါပြီ"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"ခဏ ကိုင်ထားစဉ်"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"ဖုန်းချနေပါသည်"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"ဖုန်းခေါ်ဆိုနေဆဲ"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"ကျွန်ုပ်၏ နံပါတ်မှာ <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g> ဖြစ်ပါသည်"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"ဗီဒီယို ချိတ်ဆက်နေသည်"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"ဗီဒီယို ခေါ်ဆိုမှု"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"ဗီဒီယိုခေါ်ဆိုနေသည်"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"ဗွီဒီယို ခေါ်ဆိုမှု ချိတ်ဆက်၍မရပါ။"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"ဗီဒီယို ခေါ်ဆိုမှုကို ပယ်ချလိုက်ပါပြီ"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"သင့်ကိုပြန်လည်ခေါ်ဆိုရန် နံပါတ်\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"သင့်ကိုအရေးပေါ် ပြန်လည်ခေါ်ဆိုရန် နံပါတ်\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"ဖုန်းခေါ်နေသည်"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"လွတ်သွားသော ခေါ်ဆိုမှု"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"လွတ်သွားသော ခေါ်ဆိုမှုများ"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"လွတ်သွားသော ခေါ်ဆိုမှု <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> မှလွတ်သွားသော ခေါ်ဆိုမှု"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"လက်ရှိခေါ်ဆိုမှု"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"လက်ရှိအလုပ်ခေါ်ဆိုမှု"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"လက်ရှိ Wi-Fi ခေါ်ဆိုမှု"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"လက်ရှိအလုပ် Wi-Fi ခေါ်ဆိုမှု"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"ခဏ ကိုင်ထားစဉ်"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"အဝင် ခေါ်ဆိုမှု"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"အလုပ်ဆိုင်ရာ အဝင်ခေါ်ဆိုမှု"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"အဝင် Wi-Fi ခေါ်ဆိုမှု"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"အလုပ်ဆိုင်ရာ အဝင် Wi-Fi ခေါ်ဆိုမှု"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"အဝင်ဗီဒီယိုခေါ်ဆိုမှု"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"ခေါ်နေသော မသင်္ကာဖွယ်ရာ စပမ်းခေါ်ဆိုမှု"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"ဗီဒီယိုအဝင် ခေါ်ဆိုမှု"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"အသံစာအသစ်"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"အသံစာ အသစ် (<xliff:g id="COUNT">%d</xliff:g>) ခု"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> ကိုခေါ်ပါ"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"အသံစာ၏နံပါတ်ကို မသိပါ"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"ဆက်သွယ်မှု ဧရိယာပြင်ပသို့ ရောက်ရှိနေသည်"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"ရွေးချယ်ထားသော ကွန်ရက် (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) မရရှိနိုင်ပါ"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"ဖြေကြားပါ"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"ဖုန်းချပါ"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"ဗီဒီယို"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"အသံ"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"လက်ခံပါ"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"ပယ်ပါ"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"ပြန်ခေါ်ပါ"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"မက်ဆေ့ဂျ်"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"အခြားကိရိယာတွင် လက်ရှိခေါ်ဆိုနေမှု"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"ခေါ်ဆိုမှုကို လွှဲပြောင်းပါ"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"ခေါ်ဆိုမှု ပြုလုပ်ရန်အတွက် လေယာဉ်ပျံမုဒ်ကို ဦးစွာပိတ်ပါ။"</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"ကွန်ယက်ပေါ်တွင် မှတ်ပုံတင်ထားခြင်း မရှိပါ။"</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"ဆဲလ်လူလာ ကွန်ရက် မရှိပါ။"</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"ခေါ်ဆိုမှု ပြုလုပ်ရန်အတွက် မှန်ကန်သည့်နံပါတ်တစ်ခုကို ထည့်ပါ။"</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"ခေါ်ဆို၍မရပါ။"</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI အစီအစဉ် စတင်နေသည်..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"ဝန်ဆောင်မှုအား ပံ့ပိုးမထားပါ။"</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"ခေါ်ဆိုမှုများကို လှည့်ပြောင်း၍မရပါ။"</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"ခေါ်ဆိုမှုကို ခွဲခြား၍မရပါ။"</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"မလွှဲပြောင်းနိုင်ပါ။"</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"အစည်းအဝေးခေါ်ဆိုမှု ပြုလုပ်၍မရပါ။"</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"ခေါ်ဆိုမှုကို ငြင်းဆို၍မရပါ။"</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"ခေါ်ဆိုမှု(များ) ကို လွှတ်၍မရပါ။"</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP ခေါ်ဆိုမှု"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"အရေးပေါ် ခေါ်ဆိုမှု"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"ရေဒီယို ဖွင့်နေသည်…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"ချိတ်ဆက်မှု ဧရိယာပြင်ပရောက်နေပါသည်။ ထပ်စမ်းကြည့်ပါ..."</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"ခေါ်ဆို၍မရနိုင်ပါ။ <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> သည်အရေးပေါ်နံပါတ်တစ်ခု မဟုတ်ပါ။"</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"ခေါ်ဆို၍မရနိုင်ပါ။ အရေးပေါ်နံပါတ်တစ်ခုကို ခေါ်ဆိုပါ။"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"ခေါ်ဆိုရန် ကီးဘုတ်ကိုအသုံးပြုပါ"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"ခေါ်ဆိုမှု ခေတ္တရပ်ထားပါ"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"ခေါ်ဆိုမှုကို ဆက်လုပ်ပါ"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"ခေါ်ဆိုမှု အပြီးသတ်ပါ"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"နံပါတ်အကွက် ပြပါ"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"နံပါတ်အကွက် ဝှက်ထားပါ"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"အသံပိတ်ပါ"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"အသံပြန်ဖွင့်ပါ"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"ခေါ်ဆိုမှုထည့်ပါ"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"ခေါ်ဆိုမှုများကို ပေါင်းစည်းပါ"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"ဖလှယ်ပါ"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"ခေါ်ဆိုမှုများကို စီမံခန့်ခွဲပါ"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"အစည်းအဝေးခေါ်ဆိုမှုကို စီမံခန့်ခွဲပါ"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"မျက်နှာစုံညီစည်းဝေး ဖုန်းခေါ်ဆိုမှု"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"စီမံခန့်ခွဲပါ"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"အသံ"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"ဗီဒီယို ခေါ်ဆိုမှု"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"အသံခေါ်ဆိုမှုသို့ ပြောင်းပါ"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"ကင်မရာပြောင်းပါ"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"ကင်မရာဖွင့်ပါ"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"ကင်မရာပိတ်ပါ"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"နောက်ထပ် ရွေးစရာများ"</string>
- <string name="player_started" msgid="3478865572468310331">"ပလေယာ စပါပြီ"</string>
- <string name="player_stopped" msgid="1278611664986561535">"ပလေယာ ရပ်တန့်သွားပါပြီ"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"ကင်မရာအဆင်သင့် မဖြစ်သေးပါ"</string>
- <string name="camera_ready" msgid="2614541247814590887">"ကင်မရာအဆင်သင့်ဖြစ်ပါပြီ"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"အမျိုးအမည်မသိ ခေါ်ဆိုမှုအချိန်ကာလ"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"ဝန်ဆောင်မှု"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"စနစ်ထည့်သွင်းမှုပြုလုပ်ပါ"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;မသတ်မှတ်ထားပါ&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"အခြားခေါ်ဆိုမှုဆက်တင်များ"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> မှတစ်ဆင့် ခေါ်ဆိုခြင်း"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> မှတစ်ဆင့်အဝင်ခေါ်ဆိုမှု"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"အဆက်အသွယ်ဓာတ်ပုံ"</string>
- <string name="goPrivate" msgid="3554069451018659483">"တသီးတသန့်ချိတ်ဆက်ရန်"</string>
- <string name="selectContact" msgid="92191462970821951">"လိပ်စာရွေးပါ"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"သင့်ကိုယ်ပိုင် စာသား ရေးပါ..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"မလုပ်တော့"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"ပို့ပါ"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"ဖြေကြားပါ"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS ပို့ပါ"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"ငြင်းပယ်ပါ"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"ဗီဒီယိုခေါ်ဆိုမှုအဖြစ် ဖြေကြားပါ"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"အသံခေါ်ဆိုမှုအဖြစ် ဖြေကြားပါ"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"ဗီဒီယိုခေါ်ဆိုမှုကို လက်ခံပါ"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"ဗီဒီယိုခေါ်ဆိုမှုကို ငြင်းပယ်ပါ"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"ဗီဒီယိုထုတ်လွှင့်ခြင်းတောင်းဆိုမှုကို လက်ခံပါ"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"ဗီဒီယိုထုတ်လွှင့်ခြင်းတောင်းဆိုမှုကို ငြင်းပယ်ပါ"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"ဗီဒီယိုလက်ခံရရှိမှုတောင်းဆိုချက်ကို လက်ခံပါ"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"ဗီဒီယိုလက်ခံရရှိကြောင်းတောင်းဆိုမှုကို ငြင်းပယ်ပါ"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> အတွက် အပေါ်ကို ပွတ်ဆွဲပါ"</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> အတွက် ဘယ်ဖက်ကို ပွတ်ဆွဲပါ"</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> အတွက် ညာဖက်ကို ပွတ်ဆွဲပါ"</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> အတွက် အောက်ကို ပွတ်ဆွဲပါ"</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"တုန်ခါပါ"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"တုန်ခါပါ"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"အသံ"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"မူရင်း အသံ (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"ဖုန်းမြည်သံ"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"ဖုန်းမြည်စဉ် တုန်ခါပါ"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"ဖုန်းမြည်သံ &amp; တုန်ခါသံ"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"အစည်းအဝေးခေါ်ဆိုမှုကို စီမံခန့်ခွဲပါ"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"အရေးပေါ်နံပါတ်"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"ပရိုဖိုင် ဓာတ်ပုံ"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"ကင်မရာ ပိတ်ပါ"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> မှတစ်ဆင့်"</string>
- <string name="note_sent" msgid="7623014827902758398">"မှတ်ချက်ကို ပို့လိုက်ပါပြီ"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"မကြာသေးမီက မက်ဆေ့ဂျ်များ"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"စီးပွားရေး အချက်အလက်"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> မိုင်အကွာ"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> ကီလိုမီတာအကွာ"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>၊ <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>၊ <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"မနက်ဖြန် <xliff:g id="OPEN_TIME">%s</xliff:g> ၌ဖွင့်မည်"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"ယနေ့ <xliff:g id="OPEN_TIME">%s</xliff:g> ၌ဖွင့်မည်"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g> ၌ပိတ်ပါမည်"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"ယနေ့ <xliff:g id="CLOSE_TIME">%s</xliff:g> ၌ပိတ်ခဲ့သည်"</string>
- <string name="open_now" msgid="4615706338669555999">"ယခုဖွင့်ပါ"</string>
- <string name="closed_now" msgid="2635314668145282080">"ယခုပိတ်ပါ"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"မသင်္ကာဖွယ်ရာ စပမ်းခေါ်ဆိုသူ"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"ဖုန်းခေါ်ဆိုမှု ပြီးဆုံးပါပြီ %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"ဤနံပါတ်သည် သင့်ထံ ပထမဆုံးခေါ်ဆိုသော နံပါတ်ဖြစ်သည်။"</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"ယခုဖုန်းခေါ်ဆိုမှုသည် စပမ်းပို့သူဆီမှ ဖြစ်နိုင်သည်ဟု ထင်ပါသည်။"</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"စပမ်းကို ပိတ်ဆို့ပါ/သတင်းပေးပို့ပါ"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"အဆက်အသွယ် ထည့်ပါ"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"စပမ်း မဟုတ်ပါ"</string>
-</resources>
diff --git a/InCallUI/res/values-nb/strings.xml b/InCallUI/res/values-nb/strings.xml
deleted file mode 100644
index d39e4d441..000000000
--- a/InCallUI/res/values-nb/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"På vent"</string>
- <string name="unknown" msgid="3646075119047488748">"Ukjent"</string>
- <string name="private_num" msgid="6081418498487514686">"Skjult nummer"</string>
- <string name="payphone" msgid="5743050584468748607">"Telefonkiosk"</string>
- <string name="confCall" msgid="3181961445236675173">"Konferansesamtale"</string>
- <string name="call_lost" msgid="8208184291640961172">"Anropet ble avbrutt"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Høyttaler"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Telefonhøyttaler"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Hodetelefoner med kabel"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Vil du sende disse lydene?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Sender lydene\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Send"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Ja"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Nei"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Erstatt jokertegn med"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konferansesamtale <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Nummeret til talepostkassen"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Ringer"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Ringer på nytt"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konferansesamtale"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Innkommende anrop"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Innkommende jobbanrop"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Anropet er avsluttet"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"På vent"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Legger på"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Anrop pågår"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Nummeret mitt er <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Kobler til video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videoanrop"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Ber om video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Kan ikke koble til videoanropet"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Videoforespørselen er avvist"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Tilbakeringingsnummeret ditt\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Tilbakeringingsnummeret ditt for nødstilfeller\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Ringer"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Tapt anrop"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Tapte anrop"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> tapte anrop"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Tapt anrop fra <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Pågående anrop"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Pågående jobbanrop"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Pågående Wi-Fi-anrop"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Pågående jobbanrop via Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"På vent"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Innkommende anrop"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Innkommende jobbanrop"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Innkommende Wi-Fi-anrop"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Innkommende jobbanrop via Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Innkommende videoanrop"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Innkommende anrop fra en mulig useriøs oppringer"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Innkommende videoforespørsel"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Ny talepost"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Ny talepost (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Ring <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Nummeret til talepostkassen er ukjent"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Ingen tjeneste"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Det valgte nettverket (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) er ikke tilgjengelig"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Svar"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Legg på"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Uten video"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Godta"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Avvis"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Ring tilbake"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Melding"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Samtale pågår på en annen enhet"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Overfør samtalen"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"For å ringe, slå av flymodus først."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Ikke registrert på nettverket."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobilnettverket er ikke tilgjengelig."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"For å ringe, skriv inn et gyldig nummer."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Kan ikke ringe."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Starter MMI-sekvens …"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Tjenesten støttes ikke."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Kan ikke bytte samtaler."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Kan ikke splitte opp anropet."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Kan ikke overføre."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Kan ikke opprette konferanse."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Kan ikke avvise anropet."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Kan ikke frigjøre samtale(r)."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP-anrop"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Nødanrop"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Slår på radioen …"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Ingen tjeneste. Prøver på nytt …"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Kan ikke ringe. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> er ikke et nødnummer."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Kan ikke ringe. Ring et nødnummer."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Bruk tastaturet for å ringe"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Sett anropet på vent"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Gjenoppta anropet"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Avslutt anropet"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Vis tastaturet"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Skjul tastaturet"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Slå av lyden"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Slå på lyden"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Legg til anrop"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Slå sammen anrop"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Bytt"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Administrer anrop"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Administrer konferansesamtale"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferansesamtale"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Administrer"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Lyd"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videoanrop"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Bytt til taleanrop"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Bytt kamera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Slå på kameraet"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Slå av kameraet"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Flere alternativer"</string>
- <string name="player_started" msgid="3478865572468310331">"Avspilleren har startet"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Avspilleren har stoppet"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kameraet er ikke klart"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kameraet er klart"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Ukjent anrop"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Tjeneste"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Konfigurering"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Ikke angitt&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Andre anropsinnstillinger"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Ringer via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Innkommende via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"kontaktbilde"</string>
- <string name="goPrivate" msgid="3554069451018659483">"aktivér privat samtale"</string>
- <string name="selectContact" msgid="92191462970821951">"velg kontakt"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Skriv ditt eget"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Avbryt"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Send"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Svar"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Send SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Avslå"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Svar med video"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Svar uten video"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Godta videoforespørselen"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Avslå videoforespørselen"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Godta forespørselen om å sende video"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Avslå forespørselen om å sende video"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Godta forespørselen om å motta video"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Avslå forespørselen om å motta video"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Dra opp for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Dra til venstre for å <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Dra til høyre for å <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Dra ned for å <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrering"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrering"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Lyd"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Standardlyd (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Telefonringelyd"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrer når telefonen ringer"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Ringelyd og vibrering"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Administrer konferansesamtale"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Nødnummer"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profilbilde"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kameraet er slått av"</string>
- <string name="child_number" msgid="4469090994612105532">"via <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Notatet er sendt"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Nylige meldinger"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informasjon om bedriften"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> mile unna"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km unna"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>–<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Åpner i morgen kl. <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Åpner i dag kl. <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Stenger kl. <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Stengte i dag kl. <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Åpen nå"</string>
- <string name="closed_now" msgid="2635314668145282080">"Stengt nå"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Mulig useriøst anrop"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Samtalen ble avsluttet %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Dette er første gang du blir oppringt fra dette nummeret."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Vi har mistanke om at dette anropet kommer fra en useriøs oppringer."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blokkér/rapportér"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Legg til som kontakt"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Ikke useriøs"</string>
-</resources>
diff --git a/InCallUI/res/values-ne/strings.xml b/InCallUI/res/values-ne/strings.xml
deleted file mode 100644
index 71c1ccae9..000000000
--- a/InCallUI/res/values-ne/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"फोन"</string>
- <string name="onHold" msgid="527593602772521700">"होल्डमा"</string>
- <string name="unknown" msgid="3646075119047488748">"अज्ञात"</string>
- <string name="private_num" msgid="6081418498487514686">"निजी नम्बर"</string>
- <string name="payphone" msgid="5743050584468748607">"पेफोन"</string>
- <string name="confCall" msgid="3181961445236675173">"सम्मेलन कल"</string>
- <string name="call_lost" msgid="8208184291640961172">"कल ड्रप भयो"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"स्पिकर"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"हेन्डसेट इयरपिस"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"तारसहितको हेडसेट"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"ब्लुटुथ"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"निम्न टोनहरू पठाउने हो?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"टोनहरू\n पठाउँदै"</string>
- <string name="send_button" msgid="4054398309483035794">"पठाउनुहोस्"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"हो"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"होइन"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"यसलाई वाइल्ड क्यारेक्टर राखेर बदल्नुहोस्"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"सम्मेलन कल <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"भ्वाइस मेल नम्बर"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"डायल गर्दै"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"पुन: डायल गर्दै"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"सम्मेलन कल"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"आगमन कल"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"कार्यालयबाट आएको कल"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"कल अन्त्य भयो"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"होल्डमा छ"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"फोन काट्दै"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"कलमा"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"मेरो नम्बर <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g> हो"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"भिडियो जडान गरिँदै"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"भिडियो कल"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"भिडियोका लागि अनुरोध गर्दै"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"भिडियो कलमा जडान गर्न सक्दैन"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"भिडियो अनुरोध अस्वीकार गरियो"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"तपाईंको कलब्याक नम्बर\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"तपाईंको आपतकालीन कलब्याक नम्बर\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"डायल गर्दै"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"छुटेको कल"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"छुटेका कलहरू"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> छुटेका कलहरू"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> बाट आएको छुटेको कल"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"चलिरहेको कल"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"चालु रहेको कार्यालयको कल"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"चालु रहेको WI-Fi कल"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Wi-Fi मार्फत चालु रहेको कार्यालयको कल"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"होल्डमा"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"आगमन कल"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"कार्यालयबाट आएको कल"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"आगमन Wi-Fi कल"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Wi-Fi मार्फत कार्यालयबाट आएको कल"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"आगमन भिडियो कल"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"शंकास्पद आगमन स्प्याम कल"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"आगमन भिडियो अनुरोध"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"नयाँ भ्वाइस मेल"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"नयाँ भ्वाइसमेल (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> मा डायल गर्नुहोस्"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"भ्वाइस मेल नम्बर अज्ञात छ"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"कुनै सेवा छैन"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"चयन गरिएको नेटवर्क (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) अनुपलब्ध छ"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"जवाफ दिनुहोस्"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"राख्नुहोस्"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"भिडियो"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"आवाज"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"स्वीकार गर्नुहोस्"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"खारेज गर्नुहोस्"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"कल फर्काउने"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"सन्देश"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"अर्को यन्त्रमा चलिरहेको कल"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"कल स्थानान्तरण गर्नुहोस्"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"कल गर्नका लागि, पहिले हवाइजहाज मोड बन्द गर्नुहोस्।"</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"नेटवर्कमा दर्ता भएको छैन।"</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"सेलुलर नेटवर्क उपलब्ध छैन।"</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"एक कल गर्नको लागि, मान्य नम्बर प्रविष्ट गर्नुहोस्।"</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"कल गर्न सकिंदैन।"</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI अनुक्रम सुरु गर्दै..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"सेवा समर्थित छैन।"</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"कल स्विच गर्न सक्दैन।"</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"कल अलग गर्न सक्दैन।"</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"ट्रान्सफर गर्न सक्दैन।"</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"सम्मेलन गर्न सक्दैन।"</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"कल अस्वीकार गर्न सक्दैन।"</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"कल (हरू) जारी गर्न सकिंदैन।"</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP कल"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"आपतकालीन कल"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"रेडियो खोल्दै..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"कुनै सेवा छैन। फेरि प्रयास गर्दै..."</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"कल गर्न सकिंदैन। <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> आपतकालीन नम्बर होइन।"</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"कल गर्न सकिंदैन। आपतकालीन नम्बर डायल गर्नुहोस्।"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"डायल गर्न किबोर्ड प्रयोग गर्नुहोस्"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"कललाई होल्ड गर्नुहोस्"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"कललाई पुन: निरन्तरता दिनुहोस्"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"कल अन्त्य गर्नुहोस्"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"डायलप्याड देखाउनुहोस्"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"डायलप्याड लुकाउनुहोस्"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"मौन"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"अनम्यूट गर्नुहोस्"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"कल थप्नुहोस्"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"कलहरू मर्ज गर्नुहोस्"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"स्वाप"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"कलहरूको प्रबन्ध मिलाउनुहोस्"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"सम्मेलन कलको प्रबन्ध मिलाउनहोस्"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"सम्मेलन कल"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"व्यवस्थापन गर्नुहोस्"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"अडियो"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"भिडियो कल"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"आवाज कलमा परिवर्तन गर्नुहोस्"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"क्यामेरा स्विच गर्नुहोस्"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"क्यामेरालाई सक्रिय गर्नुहोस्"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"क्यामेरालाई निष्क्रिय पार्नुहोस्"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"थप विकल्पहरू"</string>
- <string name="player_started" msgid="3478865572468310331">"प्लेयर सुरु भयो"</string>
- <string name="player_stopped" msgid="1278611664986561535">"प्लेयर रोकियो"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"क्यामेरा तयार छैन"</string>
- <string name="camera_ready" msgid="2614541247814590887">"क्यामेरा तयार छ"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"अज्ञात कल सत्र घटना"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"सेवा"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"सेटअप"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;सेट गरिएको छैन&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"अन्य कल सेटिङहरू"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> मार्फत कल गर्दै"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> मार्फत आगमन"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"सम्पर्क तस्बिर"</string>
- <string name="goPrivate" msgid="3554069451018659483">"निजी कलमा जानुहोस्"</string>
- <string name="selectContact" msgid="92191462970821951">"सम्पर्क चयन गर्नुहोस्"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"तपाईंको आफ्नै लेख्नुहोस्..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"रद्द गर्नुहोस्"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"पठाउनुहोस्"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"जवाफ दिनुहोस्"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS पठाउनुहोस्"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"अस्वीकार गर्नुहोस्"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"भिडियो कलको रूपमा जवाफ दिनुहोस्"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"अडियो कलको रूपमा जवाफ दिनुहोस्"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"भिडियो अनुरोध स्वीकार गर्नुहोस्"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"भिडियो अनुरोध अस्वीकार गर्नुहोस्"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"भिडियो प्रसारण गर्ने अनुरोध स्वीकार गर्नुहोस्"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"भिडियो प्रसारण गर्ने अनुरोध अस्वीकार गर्नुहोस्"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"भिडियो प्राप्त गर्ने अनुरोधलाई स्वीकार गर्नुहोस्"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"भिडियो प्राप्त गर्ने अनुरोध अस्वीकार गर्नुहोस्"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> को लागि माथि स्लाइड गर्नुहोस्।"</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> को लागि बायाँ स्लाइड गर्नुहोस्।"</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> को लागि दायाँ स्लाइड गर्नुहोस्।"</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> को लागि तल स्लाइड गर्नुहोस्।"</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"कम्पन हुने"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"कम्पन हुने"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"आवाज"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"पूर्वनिर्धारित ध्वनि (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"फोनको रिङटोन"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"घन्टी बज्दा कम्पन गराउनुहोस्"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"रिङटोन &amp; कम्पन"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"सम्मेलन कलको प्रबन्ध मिलाउनुहोस्"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"आपतकालीन नम्बर"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"प्रोफाइल तस्बिर"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"क्यामेरा बन्द छ"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> बाट"</string>
- <string name="note_sent" msgid="7623014827902758398">"नोट पठाइयो"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"भर्खरैका सन्देशहरू"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"व्यवसाय बारे जानकारी"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> माइल टाढा"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> किलोमिटर टाढा"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"भोलि <xliff:g id="OPEN_TIME">%s</xliff:g> मा खुल्छ"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"आज <xliff:g id="OPEN_TIME">%s</xliff:g> मा खुल्छ"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g> मा बन्द हुन्छ"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"आज <xliff:g id="CLOSE_TIME">%s</xliff:g> मा बन्द भयो"</string>
- <string name="open_now" msgid="4615706338669555999">"अहिले खुला छ"</string>
- <string name="closed_now" msgid="2635314668145282080">"अब बन्द भयो"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"शंकास्पद स्प्याम कलर"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"कल समाप्त भयो %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"यो नम्बरबाट तपाईँलाई फोन आएको यो पहिलो पटक हो।"</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"हामीले यो कल स्प्यामर हुन सक्ने आशङ्का गर्‍यौँ।"</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"स्प्याम रोक्नु्/रिपोर्ट गर्ने"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"सम्पर्क थप्नुहोस्"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"स्प्याम होइन"</string>
-</resources>
diff --git a/InCallUI/res/values-nl/strings.xml b/InCallUI/res/values-nl/strings.xml
deleted file mode 100644
index 9eaf556c0..000000000
--- a/InCallUI/res/values-nl/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefoon"</string>
- <string name="onHold" msgid="527593602772521700">"In de wacht"</string>
- <string name="unknown" msgid="3646075119047488748">"Onbekend"</string>
- <string name="private_num" msgid="6081418498487514686">"Privénummer"</string>
- <string name="payphone" msgid="5743050584468748607">"Betaaltelefoon"</string>
- <string name="confCall" msgid="3181961445236675173">"Telefonische vergadering"</string>
- <string name="call_lost" msgid="8208184291640961172">"Oproep beëindigd"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Luidspreker"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Oortelefoon van handset"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Bedrade headset"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"De volgende tonen verzenden?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Nummers verzenden\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Verzenden"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Ja"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Nee"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Jokerteken vervangen door"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Telefonische vergadering <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Voicemailnummer"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Kiezen"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Opnieuw bellen"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Telefonische vergadering"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Inkomende oproep"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Inkom. zakelijke oproep"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Oproep beëindigd"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"In de wacht"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Ophangen"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"In gesprek"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Mijn nummer is <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Verbinding maken met video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videogesprek"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Video aanvragen"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Kan geen videogesprek starten"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Videoverzoek geweigerd"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Je terugbelnummer\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Je terugbelnummer bij alarm\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Kiezen"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Gemiste oproep"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Gemiste oproepen"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> gemiste oproepen"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Gemiste oproep van <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Actieve oproep"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Actieve zakelijke oproep"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Actieve wifi-oproep"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Actieve zakelijke oproep via wifi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"In de wacht"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Inkomende oproep"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Inkomende zakelijke oproep"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Inkomende wifi-oproep"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Inkomende zakelijke oproep via wifi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Inkomend videogesprek"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Inkomende vermoedelijke spamoproep"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Inkomend videoverzoek"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Nieuwe voicemail"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Nieuwe voicemail (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> bellen"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Voicemailnummer onbekend"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Geen service"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Geselecteerd netwerk (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) niet beschikbaar"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Beantwoorden"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Ophangen"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Spraak"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Accepteren"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Sluiten"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Terugbellen"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Bericht"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Actief gesprek op een ander apparaat"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Gesprek doorschakelen"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Als je wilt bellen, moet je eerst de vliegtuigmodus uitschakelen."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Niet geregistreerd op netwerk."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobiel netwerk niet beschikbaar."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Als je wilt bellen, moet je een geldig nummer invoeren."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Kan niet bellen."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI-reeks starten..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Service wordt niet ondersteund."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Kan niet schakelen tussen oproepen."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Kan oproep niet scheiden."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Kan niet doorschakelen."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Telefonische vergadering niet mogelijk."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Kan oproep niet weigeren."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Kan oproep(en) niet vrijgeven."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP-oproep"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Noodoproep"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Radio inschakelen…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Geen bereik. Opnieuw proberen…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Kan niet bellen. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> is geen alarmnummer."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Kan niet bellen. Bel een alarmnummer."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Toetsen gebruiken om te bellen"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Oproep in de wacht zetten"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Oproep hervatten"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Oproep beëindigen"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Toetsenblok weergeven"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Toetsenblok verbergen"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Dempen"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Dempen opheffen"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Oproep toevoegen"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Samenvoegen"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Wisselen"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Oproepen beheren"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Telef. vergadering beheren"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Telefonische vergadering"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Beheren"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Vid.gespr."</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Wijzigen in spraakoproep"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Van camera wisselen"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Camera inschakelen"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Camera uitschakelen"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Meer opties"</string>
- <string name="player_started" msgid="3478865572468310331">"Speler gestart"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Speler gestopt"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Camera niet gereed"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Camera gereed"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Onbekende oproepsessiegebeurtenis"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Service"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Configuratie"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Niet ingesteld&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Andere instellingen voor bellen"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Bellen via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Inkomend via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"contactfoto"</string>
- <string name="goPrivate" msgid="3554069451018659483">"privé"</string>
- <string name="selectContact" msgid="92191462970821951">"contact selecteren"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Eigen reactie opstellen..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Annuleren"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Verzenden"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Beantwoorden"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Sms verzenden"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Weigeren"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Beantwoorden als videogesprek"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Beantwoorden als audiogesprek"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Videoverzoek accepteren"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Videoverzoek weigeren"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Verzoek voor video-overdracht accepteren"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Verzoek voor video-overdracht weigeren"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Verzoek voor video-ontvangst accepteren"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Verzoek voor video-ontvangst weigeren"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Veeg omhoog voor <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Veeg naar links voor <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Veeg naar rechts voor <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Veeg omlaag voor <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Trillen"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Trillen"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Geluid"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Standaardgeluid (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Beltoon telefoon"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Trillen bij bellen"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Beltoon en trillen"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Telefonische vergadering beheren"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Alarmnummer"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profielfoto"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Camera uit"</string>
- <string name="child_number" msgid="4469090994612105532">"via <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Notitie verzonden"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Recente berichten"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Bedrijfsinformatie"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> mijl hiervandaan"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km hiervandaan"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Gaat morgen open om <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Gaat vandaag open om <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Sluit om <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Vandaag gesloten vanaf <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Nu geopend"</string>
- <string name="closed_now" msgid="2635314668145282080">"Nu gesloten"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Vermoedelijke spambeller"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Oproep beëindigd %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Dit is de eerste keer dat je bent gebeld door dit nummer."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"We vermoedden dat deze oproep afkomstig was van een spammer."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blokk./spam melden"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Contact toevoegen"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Geen spam"</string>
-</resources>
diff --git a/InCallUI/res/values-pa/strings.xml b/InCallUI/res/values-pa/strings.xml
deleted file mode 100644
index 318fc4cdc..000000000
--- a/InCallUI/res/values-pa/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"ਫੋਨ"</string>
- <string name="onHold" msgid="527593602772521700">"ਰੋਕੀ ਗਈ"</string>
- <string name="unknown" msgid="3646075119047488748">"ਅਗਿਆਤ"</string>
- <string name="private_num" msgid="6081418498487514686">"ਨਿੱਜੀ ਨੰਬਰ"</string>
- <string name="payphone" msgid="5743050584468748607">"ਪੇ-ਫੋਨ"</string>
- <string name="confCall" msgid="3181961445236675173">"ਕਾਨਫਰੰਸ ਕਾਲ"</string>
- <string name="call_lost" msgid="8208184291640961172">"ਕਾਲ ਡ੍ਰੌਪ ਹੋਈ"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"ਸਪੀਕਰ"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"ਹੈੱਡਸੈੱਟ ਈਯਰਪੀਸ"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"ਵਾਇਰ ਵਾਲਾ ਹੈੱਡਸੈੱਟ"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"ਬਲੂਟੁੱਥ"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"ਕੀ ਅੱਗੇ ਦਿੱਤੀਆਂ ਗਈਆਂ ਧੁਨੀਆਂ ਭੇਜਣੀਆਂ ਹਨ?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"ਧੁਨੀਆਂ ਭੇਜੀਆਂ ਜਾ ਰਹੀਆਂ ਹਨ\n"</string>
- <string name="send_button" msgid="4054398309483035794">"ਭੇਜੋ"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"ਹਾਂ"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"ਨਹੀਂ"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"ਵਾਈਲਡ ਅੱਖਰ ਨੂੰ ਇਸ ਨਾਲ ਬਦਲੋ"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"ਕਾਨਫਰੰਸ ਕਾਲ <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"ਵੌਇਸਮੇਲ ਨੰਬਰ"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"ਡਾਇਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"ਦੁਬਾਰਾ ਡਾਇਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"ਕਾਨਫਰੰਸ ਕਾਲ"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"ਆ ਰਹੀ ਕਾਲ"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"ਕੰਮ ਸੰਬੰਧਿਤ ਆ ਰਹੀ ਕਾਲ"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"ਕਾਲ ਸਮਾਪਤ ਹੋਈ"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"ਰੋਕੀ ਗਈ"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"ਰੋਕੀ ਜਾ ਰਹੀ ਹੈ"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"ਚਾਲੂ ਕਾਲ"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"ਮੇਰਾ ਨੰਬਰ <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g> ਹੈ"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"ਵੀਡੀਓ ਨੂੰ ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"ਵੀਡੀਓ ਕਾਲ"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"ਵੀਡੀਓ ਲਈ ਬੇਨਤੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"ਵੀਡੀਓ ਕਾਲ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"ਵੀਡੀਓ ਬੇਨਤੀ ਅਸਵੀਕਾਰ ਕੀਤੀ ਗਈ"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"ਤੁਹਾਡਾ ਕਾਲਬੈਕ ਨੰਬਰ \n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"ਤੁਹਾਡਾ ਐਮਰਜੈਂਸੀ ਕਾਲਬੈਕ ਨੰਬਰ\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"ਡਾਇਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"ਖੁੰਝੀ ਹੋਈ ਕਾਲ"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"ਖੁੰਝੀਆਂ ਹੋਈਆਂ ਕਾਲਾਂ"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ਖੁੰਝੀਆਂ ਹੋਈਆਂ ਕਾਲਾਂ"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> ਤੋਂ ਖੁੰਝੀ ਹੋਈ ਕਾਲ"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"ਜਾਰੀ ਕਾਲ"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"ਕੰਮ ਸੰਬੰਧਿਤ ਜਾਰੀ ਕਾਲ"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"ਜਾਰੀ Wi-Fi ਕਾਲ"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"ਕੰਮ ਸੰਬੰਧਿਤ ਜਾਰੀ Wi-Fi ਕਾਲ"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"ਰੋਕੀ ਗਈ"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"ਆ ਰਹੀ ਕਾਲ"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"ਕੰਮ ਸੰਬੰਧਿਤ ਆ ਰਹੀ ਕਾਲ"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"ਆ ਰਹੀ Wi-Fi ਕਾਲ"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"ਕੰਮ ਸੰਬੰਧਿਤ ਆ ਰਹੀ Wi-Fi ਕਾਲ"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"ਆ ਰਹੀ ਵੀਡੀਓ ਕਾਲ"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"ਸ਼ੱਕੀ ਸਪੈਮ ਕਾਲ ਆ ਰਹੀ ਹੈ"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"ਆ ਰਹੀ ਵੀਡੀਓ ਬੇਨਤੀ"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"ਨਵੀਂ ਵੌਇਸਮੇਲ"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"ਨਵੀਂ ਵੌਇਸਮੇਲ (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> ਡਾਇਲ ਕਰੋ"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"ਵੌਇਸਮੇਲ ਨੰਬਰ ਅਗਿਆਤ"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"ਕੋਈ ਸੇਵਾ ਨਹੀਂ"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"ਚੁਣਿਆ ਗਿਆ ਨੈੱਟਵਰਕ (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"ਜਵਾਬ ਦਿਓ"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"ਰੋਕੋ"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"ਵੀਡੀਓ"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"ਵੌਇਸ"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"ਸਵੀਕਾਰ ਕਰੋ"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"ਰੱਦ ਕਰੋ"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"ਵਾਪਸ ਕਾਲ ਕਰੋ"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"ਸੁਨੇਹਾ"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"ਕਿਸੇ ਹੋਰ ਡੀਵਾਈਸ \'ਤੇ ਜਾਰੀ ਕਾਲ"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"ਕਾਲ ਟ੍ਰਾਂਸਫਰ ਕਰੋ"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"ਇੱਕ ਕਾਲ ਕਰਨ ਲਈ, ਪਹਿਲਾਂ ਜਹਾਜ਼ ਮੋਡ ਬੰਦ ਕਰੋ।"</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"ਨੈੱਟਵਰਕ \'ਤੇ ਰਜਿਸਟਰ ਨਹੀਂ।"</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"ਸੈਲਿਊਲਰ ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"ਇੱਕ ਕਾਲ ਕਰਨ ਲਈ, ਇੱਕ ਵੈਧ ਨੰਬਰ ਦਾਖਲ ਕਰੋ।"</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"ਕਾਲ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।"</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI ਕੜੀ ਨੂੰ ਸ਼ੁਰੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"ਸੇਵਾ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ।"</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"ਕਾਲਾਂ ਸਵਿੱਚ ਨਹੀਂ ਕੀਤੀਆਂ ਜਾ ਸਕਦੀਆਂ।"</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"ਵੱਖਰੇ ਤੌਰ \'ਤੇ ਕਾਲ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।"</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"ਕਾਲ ਟ੍ਰਾਂਸਫਰ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।"</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"ਕਾਨਫਰੰਸ ਕਾਲ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।"</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"ਕਾਲ ਰੱਦ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।"</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"ਕਾਲ(ਲਾਂ) ਰੀਲੀਜ਼ ਨਹੀਂ ਕੀਤੀਆਂ ਜਾ ਸਕਦੀਆਂ।"</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP ਕਾਲ"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"ਐਮਰਜੈਂਸੀ ਕਾਲ"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"ਰੇਡੀਓ ਚਾਲੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"ਕੋਈ ਸੇਵਾ ਨਹੀਂ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"ਕਾਲ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> ਇੱਕ ਐਮਰਜੈਂਸੀ ਨੰਬਰ ਨਹੀਂ ਹੈ।"</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"ਕਾਲ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਇੱਕ ਐਮਰਜੈਂਸੀ ਨੰਬਰ ਡਾਇਲ ਕਰੋ।"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"ਡਾਇਲ ਕਰਨ ਲਈ ਕੀ-ਬੋਰਡ ਵਰਤੋ"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"ਕਾਲ ਰੋਕੋ"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"ਕਾਲ ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"ਕਾਲ ਸਮਾਪਤ ਕਰੋ"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"ਡਾਇਲਪੈਡ ਵਿਖਾਓ"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"ਡਾਇਲਪੈਡ ਲੁਕਾਓ"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"ਮਿਊਟ ਕਰੋ"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"ਅਣਮਿਊਟ ਕਰੋ"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"ਕਾਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"ਕਾਲਾਂ ਸ਼ਾਮਲ ਕਰੋ"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"ਅਦਲਾ-ਬਦਲੀ ਕਰੋ"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"ਕਾਲਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"ਕਾਨਫਰੰਸ ਕਾਲ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"ਕਾਨਫਰੰਸ ਕਾਲ"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"ਔਡੀਓ"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"ਵੀਡੀਓ ਕਾਲ"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"ਵੌਇਸ ਕਾਲ ਵਿੱਚ ਬਦਲੋ"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"ਕੈਮਰੇ \'ਤੇ ਬਦਲੋ"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"ਕੈਮਰਾ ਚਾਲੂ ਕਰੋ"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"ਕੈਮਰਾ ਬੰਦ ਕਰੋ"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"ਹੋਰ ਚੋਣਾਂ"</string>
- <string name="player_started" msgid="3478865572468310331">"ਪਲੇਅਰ ਸ਼ੁਰੂ ਹੋ ਗਿਆ"</string>
- <string name="player_stopped" msgid="1278611664986561535">"ਪਲੇਅਰ ਰੁਕ ਗਿਆ"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"ਕੈਮਰਾ ਤਿਆਰ ਨਹੀਂ ਹੈ"</string>
- <string name="camera_ready" msgid="2614541247814590887">"ਕੈਮਰਾ ਤਿਆਰ ਹੈ"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"ਅਗਿਆਤ ਕਾਲ ਸੈਸ਼ਨ ਵਰਤਾਰਾ"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"ਸੇਵਾ"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"ਸਥਾਪਨਾ"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;ਸੈੱਟ ਨਹੀਂ&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"ਹੋਰ ਕਾਲ ਸੈਟਿੰਗਾਂ"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> ਰਾਹੀਂ ਕਾਲ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> ਰਾਹੀਂ ਆ ਰਹੀਆਂ ਕਾਲਾਂ"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"ਸੰਪਰਕ ਫ਼ੋਟੋ"</string>
- <string name="goPrivate" msgid="3554069451018659483">"ਨਿੱਜੀ ਵਿੱਚ ਬਦਲੋ"</string>
- <string name="selectContact" msgid="92191462970821951">"ਸੰਪਰਕ ਚੁਣੋ"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"ਆਪਣੇ ਆਪ ਲਿਖੋ..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"ਰੱਦ ਕਰੋ"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"ਭੇਜੋ"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"ਜਵਾਬ ਦਿਓ"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS ਭੇਜੋ"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"ਵੀਡੀਓ ਕਾਲ ਵਜੋਂ ਜਵਾਬ ਦਿਓ"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"ਔਡੀਓ ਕਾਲ ਵਜੋਂ ਜਵਾਬ ਦਿਓ"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"ਵੀਡੀਓ ਬੇਨਤੀ ਸਵੀਕਾਰ ਕਰੋ"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"ਵੀਡੀਓ ਬੇਨਤੀ ਨੂੰ ਅਸਵੀਕਾਰ ਕਰੋ"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"ਵੀਡੀਓ ਟ੍ਰਾਂਸਮਿਟ ਬੇਨਤੀ ਨੂੰ ਸਵੀਕਾਰ ਕਰੋ"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"ਵੀਡੀਓ ਟ੍ਰਾਂਸਮਿਟ ਬੇਨਤੀ ਨੂੰ ਅਸਵੀਕਾਰ ਕਰੋ"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"ਵੀਡੀਓ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਬੇਨਤੀ ਨੂੰ ਸਵੀਕਾਰ ਕਰੋ"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"ਵੀਡੀਓ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਬੇਨਤੀ ਨੂੰ ਅਸਵੀਕਾਰ ਕਰੋ"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ਲਈ ਉੱਤੇ ਸਲਾਈਡ ਕਰੋ।"</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ਲਈ ਖੱਬੇ ਪਾਸੇ ਸਲਾਈਡ ਕਰੋ।"</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ਲਈ ਸੱਜੇ ਪਾਸੇ ਸਲਾਈਡ ਕਰੋ।"</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ਲਈ ਹੇਠਾਂ ਸਲਾਈਡ ਕਰੋ।"</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"ਥਰਥਰਾਹਟ ਕਰੋ"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"ਥਰਥਰਾਹਟ ਕਰੋ"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"ਧੁਨੀ"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"ਪੂਰਵ-ਨਿਰਧਾਰਤ ਧੁਨੀ (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"ਫੋਨ ਰਿੰਗਟੋਨ"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"ਘੰਟੀ ਵੱਜਣ \'ਤੇ ਥਰਥਰਾਹਟ ਕਰੋ"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"ਰਿੰਗਟੋਨ ਅਤੇ ਥਰਥਰਾਹਟ"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"ਕਾਨਫਰੰਸ ਕਾਲ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"ਐਮਰਜੈਂਸੀ ਨੰਬਰ"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"ਪ੍ਰੋਫਾਈਲ ਫ਼ੋਟੋ"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"ਕੈਮਰਾ ਬੰਦ ਹੈ"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> ਰਾਹੀਂ"</string>
- <string name="note_sent" msgid="7623014827902758398">"ਨੋਟ-ਕਥਨ ਭੇਜਿਆ ਗਿਆ"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"ਹਾਲੀਆ ਸੁਨੇਹੇ"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"ਵਪਾਰ ਜਾਣਕਾਰੀ"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> ਮੀਲ ਦੂਰ"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> ਕਿ.ਮੀ. ਦੂਰ"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"ਕੱਲ੍ਹ <xliff:g id="OPEN_TIME">%s</xliff:g> ਵਜੇ ਖੁੱਲ੍ਹੇਗਾ"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"ਅੱਜ <xliff:g id="OPEN_TIME">%s</xliff:g> ਵਜੇ ਖੁੱਲ੍ਹੇਗਾ"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g> ਵਜੇ ਬੰਦ ਹੋਵੇਗਾ"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"ਅੱਜ <xliff:g id="CLOSE_TIME">%s</xliff:g> ਵਜੇ ਬੰਦ ਹੋਇਆ"</string>
- <string name="open_now" msgid="4615706338669555999">"ਹੁਣ ਖੁੱਲ੍ਹਾ ਹੈ"</string>
- <string name="closed_now" msgid="2635314668145282080">"ਹੁਣ ਬੰਦ ਹੈ"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"ਸ਼ੱਕੀ ਸਪੈਮ ਕਾਲਰ"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"%1$s ਦੀ ਕਾਲ ਸਮਾਪਤ ਹੋਈ"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"ਇਸ ਨੰਬਰ ਤੋਂ ਤੁਹਾਨੂੰ ਪਹਿਲੀ ਵਾਰ ਕਾਲ ਪ੍ਰਾਪਤ ਹੋਈ ਹੈ।"</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"ਸਾਨੂੰ ਇਹ ਕਾਲ ਇੱਕ ਸਪੈਮਰ ਜਾਪਦੀ ਸੀ।"</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"ਸਪੈਮ ਨੂੰ ਬਲੌਕ ਕਰੋ/ਰਿਪੋਰਟ ਕਰੋ"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"ਸੰਪਰਕ ਸ਼ਾਮਲ ਕਰੋ"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"ਸਪੈਮ ਨਹੀਂ"</string>
-</resources>
diff --git a/InCallUI/res/values-pl/strings.xml b/InCallUI/res/values-pl/strings.xml
deleted file mode 100644
index f9f78ec79..000000000
--- a/InCallUI/res/values-pl/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"Wstrzymane"</string>
- <string name="unknown" msgid="3646075119047488748">"Nieznany"</string>
- <string name="private_num" msgid="6081418498487514686">"Numer prywatny"</string>
- <string name="payphone" msgid="5743050584468748607">"Automat telefoniczny"</string>
- <string name="confCall" msgid="3181961445236675173">"Połączenie konferencyjne"</string>
- <string name="call_lost" msgid="8208184291640961172">"Połączenie przerwane"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Głośnik"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Słuchawka telefonu"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Przewodowy zestaw słuchawkowy"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Wysłać te tony?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Wysyłanie tonów\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Wyślij"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Tak"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Nie"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Zastąp symbol wieloznaczny"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Połączenie konferencyjne: <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Numer poczty głosowej"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Wybieranie"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Ponowne wybieranie numeru"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Połączenie konferencyjne"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Połączenie przychodzące"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Przychodzące połączenie służbowe"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Połączenie zakończone"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Wstrzymane"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Rozłączanie"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Trwa połączenie"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Mój numer to <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Rozpoczynanie rozmowy wideo"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Rozmowa wideo"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Wysyłanie żądania wideo"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Nie można nawiązać połączenia wideo"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Żądanie wideo zostało odrzucone"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Twój numer oddzwaniania\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Twój numer oddzwaniania dla połączeń alarmowych\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Wybieranie"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Nieodebrane połączenie"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Nieodebrane połączenia"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"Nieodebrane połączenia: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Nieodebrane połączenie od: <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Trwa połączenie"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Trwa połączenie służbowe"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Trwa połączenie przez Wi-Fi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Trwa połączenie służbowe przez Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Wstrzymane"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Połączenie przychodzące"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Przychodzące połączenie służbowe"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Przychodzące połączenie przez Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Przychodzące połączenie służbowe przez Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Przychodząca rozmowa wideo"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Przychodzące połączenie podejrzanie o spam"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Przychodzące żądanie wideo"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Nowa poczta głosowa"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Nowa poczta głosowa (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Wybierz numer <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Nieznany numer poczty głosowej"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Brak usługi"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Wybrana sieć (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) jest niedostępna"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Odbierz"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Rozłącz"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Wideo"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Głos"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Zaakceptuj"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Odrzuć"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Oddzwoń"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Wyślij SMS-a"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Trwająca rozmowa na innym urządzeniu"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Przełącz rozmowę"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Aby rozpocząć połączenie, wyłącz najpierw tryb samolotowy."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Nie zarejestrowano w sieci."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Sieć komórkowa jest niedostępna."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Aby zadzwonić, wybierz prawidłowy numer."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Nie można dzwonić."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Rozpoczynam sekwencję MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Usługa nie jest obsługiwana."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Nie można przełączyć połączeń."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Nie można rozdzielić połączenia."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Nie można przekazać."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Nie można nawiązać połączenia konferencyjnego."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Nie można odrzucić połączenia."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Nie można zwolnić połączeń."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Połączenie SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Połączenie alarmowe"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Włączam radio…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Brak sieci. Próbuję ponownie…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Nie można dzwonić. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> nie jest numerem alarmowym."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Nie można dzwonić. Wybierz numer alarmowy."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Aby zadzwonić, użyj klawiatury"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Wstrzymaj połączenie"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Wznów połączenie"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Zakończ połączenie"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Pokaż klawiaturę"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Ukryj klawiaturę"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Wycisz"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Wyłącz wyciszenie"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Dodaj połączenie"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Scal połączenia"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Przełącz"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Zarządzaj połączeniami"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Zarządzaj połączeniem konferencyjnym"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Połączenie konferencyjne"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Zarządzaj"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Dźwięk"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Rozmowa wideo"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Zmień na połączenie głosowe"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Przełącz kamerę"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Włącz kamerę"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Wyłącz kamerę"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Więcej opcji"</string>
- <string name="player_started" msgid="3478865572468310331">"Odtwarzacz włączony"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Odtwarzacz zatrzymany"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kamera niegotowa"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kamera gotowa"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Nieznane zdarzenie sesji połączenia"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Usługa"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Konfiguracja"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Nie ustawiono&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Inne ustawienia połączeń"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Nawiązywanie połączenia przez <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Przychodzące z sieci <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"zdjęcie kontaktu"</string>
- <string name="goPrivate" msgid="3554069451018659483">"przejdź do rozmowy prywatnej"</string>
- <string name="selectContact" msgid="92191462970821951">"wybierz kontakt"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Napisz własną..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Anuluj"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Wyślij"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Odbierz"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Wyślij SMS-a"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Odrzuć"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Odbierz jako rozmowę wideo"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Odbierz jako rozmowę audio"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Zaakceptuj żądanie wideo"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Odrzuć żądanie wideo"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Zaakceptuj wysyłanie obrazu wideo"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Odrzuć wysyłanie obrazu wideo"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Zaakceptuj odbieranie obrazu wideo"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Odrzuć odbieranie obrazu wideo"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Przesuń w górę: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Przesuń w lewo: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Przesuń w prawo: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Przesuń w dół: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Wibracje"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Wibracje"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Dźwięk"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Domyślny dźwięk (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Dzwonek telefonu"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Wibracje z dzwonkiem"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Dzwonek i wibracje"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Zarządzaj połączeniem konferencyjnym"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Numer alarmowy"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Zdjęcie profilowe"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera wyłączona"</string>
- <string name="child_number" msgid="4469090994612105532">"z <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Notatka wysłana"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Ostatnie wiadomości"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informacje o firmie"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> mil(e) stąd"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km stąd"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>-<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Otwarte jutro od <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Otwarte dzisiaj od <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Zamknięte od <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Zamknięte dzisiaj od <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Teraz otwarte"</string>
- <string name="closed_now" msgid="2635314668145282080">"Teraz zamknięte"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Podejrzenie spamu"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Połączenie zakończone (%1$s)"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"To pierwsze połączenie z tego numeru."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Podejrzewamy, że to połączenie to spam."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Zablokuj/zgłoś spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Dodaj kontakt"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"To nie spam"</string>
-</resources>
diff --git a/InCallUI/res/values-pt-rBR/strings.xml b/InCallUI/res/values-pt-rBR/strings.xml
deleted file mode 100644
index 5271f54cc..000000000
--- a/InCallUI/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefone"</string>
- <string name="onHold" msgid="527593602772521700">"Em espera"</string>
- <string name="unknown" msgid="3646075119047488748">"Desconhecido"</string>
- <string name="private_num" msgid="6081418498487514686">"Número privado"</string>
- <string name="payphone" msgid="5743050584468748607">"Chamada a cobrar"</string>
- <string name="confCall" msgid="3181961445236675173">"Teleconferência"</string>
- <string name="call_lost" msgid="8208184291640961172">"A chamada caiu."</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Alto-falante"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Minifone do aparelho"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Fone de ouvido com fio"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Enviar os seguintes tons?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Enviando tons\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Enviar"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Sim"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Não"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Substituir caractere curinga por"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Teleconferência <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Número do correio de voz"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Discando"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Rediscando"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Teleconferência"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Chamada recebida"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Chamada trabalho recebida"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Chamada encerrada"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Em espera"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Desligando"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Em chamada"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Meu número é <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Conectando vídeo"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videochamada"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Solicitando vídeo"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Não é possível conectar a videochamada"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Solicitação de vídeo rejeitada"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Seu número de retorno de chamada\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Seu número de retorno de chamada de emergência\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Discando"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Chamada perdida"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Chamadas perdidas"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> chamadas perdidas"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Chamada perdida de <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Chamanda em andamento"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Chamada de trabalho em andamento"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Chamada por Wi-Fi em andamento"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Chamada de trabalho por Wi-Fi em andamento"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Em espera"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Chamada recebida"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Chamada de trabalho recebida"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Chamada por Wi-Fi recebida"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Chamada de trabalho recebida por Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Videochamada recebida"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Chamada recebida suspeita (spam)"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Solicitação de vídeo recebida"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Novo correio de voz"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Novo correio de voz (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Discar <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Número correio de voz desconhecido"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Sem serviço"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"A rede selecionada (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) está indisponível"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Atender"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Desligar"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Vídeo"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Voz"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Aceitar"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Dispensar"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Retor. cham."</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Mensagem"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Chamada em andamento em outro dispositivo"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transferir chamada"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Para fazer uma chamada, primeiro desative o modo avião."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Não registrado na rede."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Rede celular não disponível."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Para realizar uma chamada, digite um número válido."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Não é possível realizar chamadas."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Iniciando sequência MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Serviço não compatível."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Não é possível alternar as chamadas."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Não é possível separar a chamada."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Não é possível transferir a chamada."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Não é possível fazer uma conferência."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Não é possível rejeitar a chamada."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Não é possível liberar chamadas."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Chamada SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Chamada de emergência"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Ativando o rádio…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Sem serviço. Tentando novamente..."</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Não é possível realizar chamadas. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> não é um número de emergência."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Não é possível realizar chamadas. Disque um número de emergência."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Use o teclado para discar"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Colocar chamada em espera"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Retomar chamada"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Encerrar chamada"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Mostrar teclado"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Ocultar teclado"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Desativar som"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Ativar som"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Adicionar chamada"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Juntar chamadas"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Trocar"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Gerenciar chamadas"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Gerenciar teleconferência"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Teleconferência"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Gerenciar"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Áudio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videocham."</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Alterar para chamada de voz"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Alternar câmera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Ativar câmera"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Desativar câmera"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Mais opções"</string>
- <string name="player_started" msgid="3478865572468310331">"Player iniciado"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Player interrompido"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"A câmera não está pronta"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Câmera pronta"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Evento de sessão de chamada desconhecido"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Serviço"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Configuração"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Não definido&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Outras configurações de chamada"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Chamando via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Chamada de <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"foto do contato"</string>
- <string name="goPrivate" msgid="3554069451018659483">"conversar em particular"</string>
- <string name="selectContact" msgid="92191462970821951">"selecionar contato"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Escreva sua resposta..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Cancelar"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Enviar"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Atender"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Enviar SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Recusar"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Atender como videochamada"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Atender como chamada de áudio"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Aceitar solicitação de vídeo"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Recusar solicitação de vídeo"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Aceitar solicitação de transmissão de vídeo"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Recusar solicitação de transmissão de vídeo"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Aceitar solicitação de recebimento de vídeo"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Recusar solicitação de recebimento de vídeo"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, deslize para cima."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, deslize para a esquerda."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, deslize para a direita."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, deslize para baixo."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrar"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrar"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Som"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Som padrão (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Toque do telefone"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrar ao tocar"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Toque e vibração"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Gerenciar teleconferência"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Número de emergência"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Foto do perfil"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Câmera desligada"</string>
- <string name="child_number" msgid="4469090994612105532">"via <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Nota enviada"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Mensagens recentes"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informações sobre a empresa"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> milhas de distância"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km de distância"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Abre amanhã às <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Abre hoje às <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Fecha às <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Fechou hoje às <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Aberto agora"</string>
- <string name="closed_now" msgid="2635314668145282080">"Fechado agora"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Autor suspeito (spam)"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Chamada encerra %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Esta é a primeira vez que este número ligou para você."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Suspeitamos que esta chamada seja de um criador de spams."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Bloq./denunciar spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Adicionar contato"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Não é spam"</string>
-</resources>
diff --git a/InCallUI/res/values-pt-rPT/strings.xml b/InCallUI/res/values-pt-rPT/strings.xml
deleted file mode 100644
index 2a04556fe..000000000
--- a/InCallUI/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefone"</string>
- <string name="onHold" msgid="527593602772521700">"Em espera"</string>
- <string name="unknown" msgid="3646075119047488748">"Desconhecido"</string>
- <string name="private_num" msgid="6081418498487514686">"Número privado"</string>
- <string name="payphone" msgid="5743050584468748607">"Telefone público"</string>
- <string name="confCall" msgid="3181961445236675173">"Conferência"</string>
- <string name="call_lost" msgid="8208184291640961172">"A chamada caiu"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Altifalante"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Auricular do telefone"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Auscultadores com fios"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Pretende enviar os seguintes tons?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"A enviar tons\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Enviar"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Sim"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Não"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Substituir o caráter universal por"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Conferência <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Número do correio de voz"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"A marcar"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"A remarcar"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Conferência"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Chamada recebida"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Chamada de trab. recebida"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Chamada terminada"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Em espera"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"A desligar"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Numa chamada"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"O meu número é <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"A ligar vídeo"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videochamada"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"A solicitar vídeo"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Não é possível ligar a videochamada"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Pedido de vídeo rejeitado"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"O seu número de retorno de chamadas\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"O seu número de retorno de chamadas de emergência\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"A marcar"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Chamada não atendida"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Chamadas não atendidas"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> chamadas não atendidas"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Chamada não atendida de <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Chamada em curso"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Chamada de trabalho em curso"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Chamada Wi-Fi em curso"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Chamada de trabalho via Wi-Fi em curso"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Em espera"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Chamada recebida"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Chamada de trab. recebida"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Chamada Wi-Fi recebida"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Chamada de trabalho recebida via Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Videochamada recebida"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"A receber chamada spam suspeita"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Pedido de vídeo recebido"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Nova mensagem de correio de voz"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Nova mensagem de correio de voz (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Marcar <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Número do correio de voz desconhecido"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Sem serviço"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Rede selecionada (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) indisponível"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Atender"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Desligar"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Vídeo"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Voz"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Aceitar"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Ignorar"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Ligar de volta"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Mensagem"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Chamada em curso noutro dispositivo"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transferir chamada"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Para efetuar uma chamada, desative primeiro o Modo de avião."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Sem registo na rede."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Rede móvel não disponível."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Para efetuar uma chamada, introduza um número válido."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Não é possível telefonar."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"A iniciar sequência de MMI..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Serviço não suportado."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Não é possível alternar entre chamadas."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Não é possível separar a chamada."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Não é possível transferir."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Não é possível efetuar uma conferência."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Não é possível rejeitar a chamada."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Não é possível libertar as chamadas."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Chamada SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Chamada de emergência"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"A ligar o rádio..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Sem serviço. A tentar novamente…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Não é possível telefonar. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> não é um número de emergência."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Não é possível telefonar. Marque um número de emergência."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Utilizar o teclado para marcar"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Colocar chamada em espera"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Retomar chamada"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Terminar chamada"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Mostrar o teclado"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Ocultar o teclado"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Desativar som"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Reativar o som"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Adicionar chamada"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Intercalar chamadas"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Trocar"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Gerir chamadas"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Gerir conferência"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Conferência"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Gerir"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Áudio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videochamada"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Mudar para chamada de voz"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Trocar câmara"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Ativar câmara"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Desativar câmara"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Mais opções"</string>
- <string name="player_started" msgid="3478865572468310331">"Leitor iniciado"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Leitor interrompido"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"A câmara não está pronta"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Câmara pronta"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Evento de sessão de chamada desconhecido"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Serviço"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Configuração"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Não definido&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Outras definições de chamadas"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"A telefonar através de <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Recebidas através de <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"foto do contacto"</string>
- <string name="goPrivate" msgid="3554069451018659483">"tornar privado"</string>
- <string name="selectContact" msgid="92191462970821951">"selecionar contacto"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Escreva a sua própria..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Cancelar"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Enviar"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Atender"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Enviar SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Recusar"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Atender como videochamada"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Atender como chamada de áudio"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Aceitar pedido de vídeo"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Recusar pedido de vídeo"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Aceitar pedido para transmitir vídeo"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Recusar pedido para transmitir vídeo"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Aceitar pedido para receber vídeo"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Recusar pedido para receber vídeo"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Deslize lentamente para cima para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Deslize lentamente para a esquerda para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Deslize lentamente para a direita para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Deslize lentamente para baixo para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrar"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrar"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Som"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Som predefinido (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Toque do telemóvel"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrar ao tocar"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Tocar e vibrar"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Gerir conferência"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Número de emergência"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Foto do perfil"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Câmara desligada"</string>
- <string name="child_number" msgid="4469090994612105532">"através de <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Nota enviada"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Mensagens recentes"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informações da empresa"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"A <xliff:g id="DISTANCE">%.1f</xliff:g> milhas de distância"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"A <xliff:g id="DISTANCE">%.1f</xliff:g> km de distância"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> – <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Abre amanhã às <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Abre hoje às <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Fecha às <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Fechou hoje às <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Aberto agora"</string>
- <string name="closed_now" msgid="2635314668145282080">"Fechado agora"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Chmd. spam suspeita"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Chamada terminada: %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"É a primeira vez que este número lhe liga."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Suspeitamos que a pessoa que fez esta chamada seja um spammer."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Bloq./denunciar spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Adicionar contacto"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Não é spam"</string>
-</resources>
diff --git a/InCallUI/res/values-pt/strings.xml b/InCallUI/res/values-pt/strings.xml
deleted file mode 100644
index 5271f54cc..000000000
--- a/InCallUI/res/values-pt/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefone"</string>
- <string name="onHold" msgid="527593602772521700">"Em espera"</string>
- <string name="unknown" msgid="3646075119047488748">"Desconhecido"</string>
- <string name="private_num" msgid="6081418498487514686">"Número privado"</string>
- <string name="payphone" msgid="5743050584468748607">"Chamada a cobrar"</string>
- <string name="confCall" msgid="3181961445236675173">"Teleconferência"</string>
- <string name="call_lost" msgid="8208184291640961172">"A chamada caiu."</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Alto-falante"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Minifone do aparelho"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Fone de ouvido com fio"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Enviar os seguintes tons?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Enviando tons\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Enviar"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Sim"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Não"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Substituir caractere curinga por"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Teleconferência <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Número do correio de voz"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Discando"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Rediscando"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Teleconferência"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Chamada recebida"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Chamada trabalho recebida"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Chamada encerrada"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Em espera"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Desligando"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Em chamada"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Meu número é <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Conectando vídeo"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videochamada"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Solicitando vídeo"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Não é possível conectar a videochamada"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Solicitação de vídeo rejeitada"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Seu número de retorno de chamada\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Seu número de retorno de chamada de emergência\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Discando"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Chamada perdida"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Chamadas perdidas"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> chamadas perdidas"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Chamada perdida de <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Chamanda em andamento"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Chamada de trabalho em andamento"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Chamada por Wi-Fi em andamento"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Chamada de trabalho por Wi-Fi em andamento"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Em espera"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Chamada recebida"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Chamada de trabalho recebida"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Chamada por Wi-Fi recebida"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Chamada de trabalho recebida por Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Videochamada recebida"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Chamada recebida suspeita (spam)"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Solicitação de vídeo recebida"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Novo correio de voz"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Novo correio de voz (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Discar <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Número correio de voz desconhecido"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Sem serviço"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"A rede selecionada (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) está indisponível"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Atender"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Desligar"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Vídeo"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Voz"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Aceitar"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Dispensar"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Retor. cham."</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Mensagem"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Chamada em andamento em outro dispositivo"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transferir chamada"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Para fazer uma chamada, primeiro desative o modo avião."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Não registrado na rede."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Rede celular não disponível."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Para realizar uma chamada, digite um número válido."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Não é possível realizar chamadas."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Iniciando sequência MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Serviço não compatível."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Não é possível alternar as chamadas."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Não é possível separar a chamada."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Não é possível transferir a chamada."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Não é possível fazer uma conferência."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Não é possível rejeitar a chamada."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Não é possível liberar chamadas."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Chamada SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Chamada de emergência"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Ativando o rádio…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Sem serviço. Tentando novamente..."</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Não é possível realizar chamadas. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> não é um número de emergência."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Não é possível realizar chamadas. Disque um número de emergência."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Use o teclado para discar"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Colocar chamada em espera"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Retomar chamada"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Encerrar chamada"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Mostrar teclado"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Ocultar teclado"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Desativar som"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Ativar som"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Adicionar chamada"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Juntar chamadas"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Trocar"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Gerenciar chamadas"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Gerenciar teleconferência"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Teleconferência"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Gerenciar"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Áudio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videocham."</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Alterar para chamada de voz"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Alternar câmera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Ativar câmera"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Desativar câmera"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Mais opções"</string>
- <string name="player_started" msgid="3478865572468310331">"Player iniciado"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Player interrompido"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"A câmera não está pronta"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Câmera pronta"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Evento de sessão de chamada desconhecido"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Serviço"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Configuração"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Não definido&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Outras configurações de chamada"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Chamando via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Chamada de <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"foto do contato"</string>
- <string name="goPrivate" msgid="3554069451018659483">"conversar em particular"</string>
- <string name="selectContact" msgid="92191462970821951">"selecionar contato"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Escreva sua resposta..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Cancelar"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Enviar"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Atender"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Enviar SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Recusar"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Atender como videochamada"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Atender como chamada de áudio"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Aceitar solicitação de vídeo"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Recusar solicitação de vídeo"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Aceitar solicitação de transmissão de vídeo"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Recusar solicitação de transmissão de vídeo"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Aceitar solicitação de recebimento de vídeo"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Recusar solicitação de recebimento de vídeo"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, deslize para cima."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, deslize para a esquerda."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, deslize para a direita."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, deslize para baixo."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrar"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrar"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Som"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Som padrão (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Toque do telefone"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrar ao tocar"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Toque e vibração"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Gerenciar teleconferência"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Número de emergência"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Foto do perfil"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Câmera desligada"</string>
- <string name="child_number" msgid="4469090994612105532">"via <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Nota enviada"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Mensagens recentes"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informações sobre a empresa"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> milhas de distância"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km de distância"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Abre amanhã às <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Abre hoje às <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Fecha às <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Fechou hoje às <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Aberto agora"</string>
- <string name="closed_now" msgid="2635314668145282080">"Fechado agora"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Autor suspeito (spam)"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Chamada encerra %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Esta é a primeira vez que este número ligou para você."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Suspeitamos que esta chamada seja de um criador de spams."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Bloq./denunciar spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Adicionar contato"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Não é spam"</string>
-</resources>
diff --git a/InCallUI/res/values-ro/strings.xml b/InCallUI/res/values-ro/strings.xml
deleted file mode 100644
index ca0036d0d..000000000
--- a/InCallUI/res/values-ro/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"În așteptare"</string>
- <string name="unknown" msgid="3646075119047488748">"Necunoscut"</string>
- <string name="private_num" msgid="6081418498487514686">"Număr privat"</string>
- <string name="payphone" msgid="5743050584468748607">"Telefon public"</string>
- <string name="confCall" msgid="3181961445236675173">"Conferință telefonică"</string>
- <string name="call_lost" msgid="8208184291640961172">"Apelul s-a încheiat"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Difuzor"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Casca receptorului"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Set căști-microfon cu fir"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Trimiteți următoarele tonuri?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Se trimit tonuri\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Trimiteți"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Da"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Nu"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Înlocuiți metacaracterul cu"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Conferință telefonică <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Numărul mesageriei vocale"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Se apelează"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Se reapelează"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Conferință telefonică"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Apel primit"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Apel de serviciu primit"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Apel încheiat"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"În așteptare"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Se încheie apelul"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Apel în desfășurare"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Numărul meu este <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Se conectează apelul video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Apel video"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Se solicită apel video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Nu se poate conecta apelul video"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Solicitarea pentru apel video a fost respinsă"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Numărul de apelare inversă\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Numărul de apelare inversă de urgență\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Se apelează"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Apel nepreluat"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Apeluri nepreluate"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> (de) apeluri nepreluate"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Apel nepreluat de la <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Apel în desfășurare"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Apel de serviciu în desfășurare"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Apel prin Wi-Fi în desfășurare"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Apel de serviciu prin Wi-Fi în desfășurare"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"În așteptare"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Apel primit"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Apel de serviciu primit"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Apel prin Wi-Fi primit"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Apel de serviciu prin Wi-Fi primit"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Apel video primit"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Un apel primit posibil spam"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Solicitare de trecere la apel video"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Mesaj vocal nou"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Mesaj vocal nou (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Apelați <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Numărul mesageriei vocale este necunoscut"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Fără semnal"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Rețeaua selectată (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) nu este disponibilă"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Răspundeți"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Încheiați apelul"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Apel video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Apel vocal"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Acceptați"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Refuzați"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Apelați înapoi"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Trimiteți mesaj"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Apel în curs pe alt dispozitiv"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transferați apelul"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Pentru a apela, mai întâi dezactivați modul Avion."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Neînregistrat în rețea."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Rețeaua mobilă nu este disponibilă."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Pentru a apela, introduceți un număr valid."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Nu se poate apela."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Se pornește secvența MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Serviciul nu este acceptat."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Apelurile nu pot fi comutate."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Apelul nu poate fi separat."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Nu se poate transfera."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Conferința telefonică nu poate fi inițiată."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Apelul nu poate fi respins."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Apelurile nu pot fi eliberate."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Apel SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Apel de urgență"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Se activează radio…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Fără semnal. Se încearcă din nou…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Nu se poate apela. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> nu este un număr de urgență."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Nu se poate apela. Formați un număr de urgență."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Folosiți tastatura pentru a apela"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Puneți apelul în așteptare"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Reluați apelul"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Încheiați apelul"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Afișează tastatura numerică"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Ascunde tastatura numerică"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Dezactivează sunetul"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Activează sunetul"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Adăugați un apel"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Îmbinați apelurile"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Schimbați"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Gestionați apelurile"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Gestionați conferința telefonică"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Conferință telefonică"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Gestionați"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Apel video"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Treceți la apel vocal"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Comutați camera foto"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Activați camera"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Dezactivați camera"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Mai multe opțiuni"</string>
- <string name="player_started" msgid="3478865572468310331">"Playerul a pornit"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Playerul s-a oprit"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Camera foto nu este pregătită"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Camera foto este pregătită"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Eveniment necunoscut privind o sesiune de apeluri"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Furnizor de servicii"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Configurați"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Nesetat&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Alte setări de apel"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Se apelează prin <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Primite prin <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"fotografia persoanei de contact"</string>
- <string name="goPrivate" msgid="3554069451018659483">"treceți în modul privat"</string>
- <string name="selectContact" msgid="92191462970821951">"selectați o persoană de contact"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Scrieți propriul răspuns…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Anulați"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Trimiteți"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Răspundeți"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Trimiteți SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Refuzați"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Răspundeți ca apel video"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Răspundeți ca apel audio"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Acceptați solicitarea de a trece la apel video"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Refuzați solicitarea de a trece la apel video"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Acceptați solicitarea de a transmite conținut video"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Refuzați solicitarea de a transmite conținut video"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Acceptați solicitarea de a primi conținut video"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Refuzați solicitarea de a primi conținut video"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Glisați în sus ca să <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Glisați spre stânga ca să <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Glisați spre dreapta ca să <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Glisați în jos ca să <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrații"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrații"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Sunet"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Sunet prestabilit (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Ton de sonerie pentru telefon"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrează când sună"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Ton de sonerie și vibrații"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Gestionați conferința telefonică"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Număr de urgență"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Fotografie de profil"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Camera foto este oprită"</string>
- <string name="child_number" msgid="4469090994612105532">"pe <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Nota a fost trimisă"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Mesaje recente"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informații despre companie"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> mi distanță"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km distanță"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> – <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Deschide mâine la <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Deschide astăzi la <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Închide la <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"A închis astăzi la <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Acum este deschis"</string>
- <string name="closed_now" msgid="2635314668145282080">"Acum este închis"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Posibil apelant spam"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Apelul s-a încheiat %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Aceasta este prima dată când ați primit apel de la acest număr."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Suspectăm că acesta este un apel spam."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blocați/raportați"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Adăugați persoana"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Nu este spam"</string>
-</resources>
diff --git a/InCallUI/res/values-ru/strings.xml b/InCallUI/res/values-ru/strings.xml
deleted file mode 100644
index 552ad4d02..000000000
--- a/InCallUI/res/values-ru/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Телефон"</string>
- <string name="onHold" msgid="527593602772521700">"На удержании"</string>
- <string name="unknown" msgid="3646075119047488748">"Неизвестный абонент"</string>
- <string name="private_num" msgid="6081418498487514686">"Скрытый номер"</string>
- <string name="payphone" msgid="5743050584468748607">"Телефон-автомат"</string>
- <string name="confCall" msgid="3181961445236675173">"Конференц-вызов"</string>
- <string name="call_lost" msgid="8208184291640961172">"Звонок сброшен"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Динамик"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Динамик гарнитуры"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Проводная гарнитура"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Отправить следующие тоны?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Отправка тональных сигналов\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Отправить"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Да"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Нет"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Заменить универсальный символ на"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Конференц-вызов: <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Номер голосовой почты"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Набор номера…"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Повторный вызов"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Конференц-вызов"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Входящий вызов"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Входящий вызов (работа)"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Вызов завершен"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"На удержании"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Завершение разговора"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Вызов"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Мой номер: <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Подключение видео"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Видеовызов"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Запрос видео"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Не удалось совершить видеовызов"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Видеовызов отклонен"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Номер обратного вызова:\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Номер обратного вызова для экстренных служб:\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Набор номера…"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Пропущенный вызов"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Пропущенные вызовы"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"Пропущенных вызовов: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Пропущенные вызовы от абонента <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Текущий вызов"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Текущий звонок (работа)"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Текущий Wi-Fi-звонок"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Текущий Wi-Fi-звонок (работа)"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"На удержании"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Входящий вызов"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Входящий вызов (работа)"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Входящий Wi-Fi-звонок"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Входящий Wi-Fi-звонок (работа)"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Входящий видеовызов"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Входящий вызов: подозрение на спам"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Входящий видеовызов"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Новое сообщение голосовой почты"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Новое сообщение голосовой почты (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Набрать номер <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Номер голосовой почты неизвестен"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Нет сигнала"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Выбранная сеть (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) недоступна."</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Ответить"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Завершить"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Видео"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Голос"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Разрешить"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Закрыть"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Перезвонить"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Написать SMS"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Вы участвуете в разговоре на другом устройстве"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Перевести на это устройство"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Отключите режим полета."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Нет регистрации в сети."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Мобильная сеть недоступна."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Недействительный номер."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Не удалось позвонить."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Запуск последовательности MMI..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Сервис не поддерживается."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Не удалось переключить вызов."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Не удалось разделить вызов."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Не удалось перенести."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Не удалось выполнить конференц-вызов."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Не удалось отклонить вызов."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Не удалось разъединить."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Вызов SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Экстренный вызов"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Включение радио…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Нет сигнала. Повторная попытка…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Не удалось позвонить. Номер <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> не принадлежит экстренным службам."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Не удалось позвонить. Наберите номер экстренных служб."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Используйте клавиатуру для набора номера"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Удерживать вызов"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Возобновить вызов"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Завершить вызов"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Показать панель набора номера"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Скрыть панель набора номера"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Выключить звук"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Включить звук"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Добавить вызов"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Объединить вызовы"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Перевести звонок"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Управление вызовами"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Настройка конференц-связи"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Конференц-вызов"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Управление"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Аудио"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Видеовызов"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Отключить видео"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Сменить камеру"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Включить камеру"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Выключить камеру"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Другие настройки"</string>
- <string name="player_started" msgid="3478865572468310331">"Видеоплеер включен"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Видеоплеер отключен"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Камера недоступна"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Камера доступна"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Неизвестное событие сеанса связи"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Служба"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Настройка"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Не задано&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Другие настройки вызовов"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Звонок через <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Входящий вызов (оператор: <xliff:g id="PROVIDER_NAME">%s</xliff:g>)"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"фотография контакта"</string>
- <string name="goPrivate" msgid="3554069451018659483">"приватная конференция"</string>
- <string name="selectContact" msgid="92191462970821951">"выбрать контакт"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Ваш ответ…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Отмена"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Отправить"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Ответить"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Отправить SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Отклонить"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Ответить с видео"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Ответить на голосовой вызов"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Ответить на видеовызов"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Отклонить видеовызов"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Разрешить передачу видео"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Отклонить передачу видео"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Принять видео"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Отклонить видео"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Проведите вверх, чтобы <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Проведите влево, чтобы <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Проведите вправо, чтобы <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Проведите вниз, чтобы <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Вибросигнал"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Вибросигнал"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Звук"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"По умолчанию (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Рингтон"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Вибросигнал и рингтон"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Мелодия звонка и вибросигнал"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Настройка конференц-связи"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Экстренная служба"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Фото профиля"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Камера отключена"</string>
- <string name="child_number" msgid="4469090994612105532">"через <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Сообщение отправлено"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Недавние сообщения"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Информация о компании"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> мил."</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> км"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>–<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Откроется завтра в <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Откроется сегодня в <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Работает до <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Сегодня не работает с <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Сейчас открыто"</string>
- <string name="closed_now" msgid="2635314668145282080">"Сейчас закрыто"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Подозрение на спам"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Вызов завершен (%1$s)"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Это первый вызов с этого номера."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Похоже, этот вызов – спам."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Заблокировать/в спам"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Добавить контакт"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Не спам"</string>
-</resources>
diff --git a/InCallUI/res/values-si/strings.xml b/InCallUI/res/values-si/strings.xml
deleted file mode 100644
index de0267a58..000000000
--- a/InCallUI/res/values-si/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"දුරකථනය"</string>
- <string name="onHold" msgid="527593602772521700">"රඳවා ගනිමින්"</string>
- <string name="unknown" msgid="3646075119047488748">"නොදනී"</string>
- <string name="private_num" msgid="6081418498487514686">"රහසිගත අංකය"</string>
- <string name="payphone" msgid="5743050584468748607">"පේෆෝනය"</string>
- <string name="confCall" msgid="3181961445236675173">"සම්මන්ත්‍රණ ඇමතුම"</string>
- <string name="call_lost" msgid="8208184291640961172">"ඇමතුම නැවතුණි"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"නාදකය"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"හෑන්ඩ්සෙටයේ සවන් කඬ"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"රැහැන් සහිත හෙඩ්සෙටය"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"බ්ලූටූත්"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"පහත නාද යවන්නද?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"නාද යවමින්\n"</string>
- <string name="send_button" msgid="4054398309483035794">"යවන්න"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"ඔව්"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"නැත"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"අනුලකුණ ප්‍රතිස්ථාපනය කරන්නේ"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"සම්මන්ත්‍රණ ඇමතුම <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"හඬ තැපැල් අංකය"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"ඩයල් කරමින්"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"නැවත ඩයල් කරමින්"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"සම්මන්ත්‍රණ ඇමතුම"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"එන ඇමතුම"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"එන කාර්යාල ඇමතුම"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"ඇමතුම අවසන් විය"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"රඳවා ගනිමින්"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"විසන්ධි කරමින්"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"ඇමතුමක"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"මගේ අංකය <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"වීඩියෝවකට සම්බන්ධ කරමින්"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"වීඩියෝ ඇමතුම"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"වීඩියෝවක් ඉල්ලමින්"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"වීඩියෝ ඇමතුම සම්බන්ධ කළ නොහැක"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"වීඩියෝ ඉල්ලීම ප්‍රතික්ෂේප කරන ලදී"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"ඔබේ පසුඇමතුම් අංකය\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"ඔබගේ හදිසි පසුඇමතුම් අංකය\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"ඩයල් කරමින්"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"මඟ හැරුණු ඇමතුම"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"මඟ හැරුණු ඇමතුම්"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"මඟ හැරුණු ඇමතුම් <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> වෙතින් මඟ හැරුණු ඇමතුම"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"කරගෙන යන ඇමතුම"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"කරගෙන යන කාර්යාල ඇමතුම"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"දැනට කරගෙන යන Wi-Fi ඇමතුම"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"කරගෙන යන Wi-Fi කාර්යාල ඇමතුම"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"රඳවා ගනිමින්"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"එන ඇමතුම"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"එන කාර්යාල ඇමතුම"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"එන Wi-Fi ඇමතුම"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"එන Wi-Fi කාර්යාල ඇමතුම"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"එන වීඩියෝ ඇමතුම"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"එන සැකසහිත අයාචිත තැපැල් ඇමතුම"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"එන වීඩියෝ ඉල්ලීම"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"නව හඬ තැපෑල"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"නව හඬ තැපැල් (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> ඩයල් කරන්න"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"හඬ තැපැල් අංකය නොදනී"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"සේවාව නැත"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"තෝරා ඇති ජාලය (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) නොමැත"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"පිළිතුරු දෙන්න"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"විසන්ධි කරන්න"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"වීඩියෝව"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"හඬ"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"පිළිගන්න"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"අස් කරන්න"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"පසුඇමතුම"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"පණිවිඩය"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"වෙනත් උපාංගයක සිදු වන ඇමතුම"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"ඇමතුම මාරු කරන්න"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"ඇමතුමක් ගැනීමට, මුලින්ම ගුවන් යානා ප්‍රකාරය ක්‍රියාවිරහිත කරන්න."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"ජාලය මත ලියාපදිංචි වී නැත."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"සෙලියුලර් ජාලය නොතිබේ."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"ඇමතුමක් ගැනීමට, වලංගු අංකයක් ඇතුළු කරන්න."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"ඇමතුම් ගැනීමට නොහැක."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI අනුපිළිවෙළ ආරම්භ කරමින්…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"සේවාවට සහාය දක්වන්නේ නැත."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"ඇමතුම් මාරු කිරීම කළ නොහැක."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"ඇමතුම වෙන් කිරීම කළ නොහැක."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"මාරු කිරීමට නොහැක."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"සම්මන්ත්‍රණය කළ නොහැක."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"ඇමතුම ප්‍රතික්ෂේප කළ නොහැක."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"ඇමතුම(ම්) මුදාහැරීම කළ නොහැක."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP ඇමතුම"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"හදිසි ඇමතුම"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"රේඩියෝව ක්‍රියාත්මක කරමින්…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"සේවාව නැත. නැවත උත්සාහ කරමින්…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"ඇමතීමට නොහැකිය. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> මෙය හදිසි ඇමතුම් අංකයක් නොවේ."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"ඇමතිය නොහැක. හදිසි අංකයක් අමතන්න."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"ඩයල් කිරීමට යතුරු පුවරුව භාවිත කරන්න"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"ඇමතුම රඳවා ගන්න"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"ඇමතුම නැවත පටන් ගන්න"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"ඇමතුම අවසන් කරන්න"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"ඇමතුම් පෑඩය පෙන්වන්න"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"ඇමතුම් පෑඩය සඟවන්න"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"නිහඬ කරන්න"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"නිහඬ කිරීම ඉවත් කරන්න"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"ඇමතුමක් එක් කරන්න"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"ඇමතුම් ඒකාබද්ධ කරන්න"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"මාරු කරන්න"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"ඇමතුම් කළමනාකරණය කරන්න"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"සම්මන්ත්‍රණ ඇමතුම කළමනාකරණය කරන්න"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"සම්මන්ත්‍රණ ඇමතුම"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"කළමනාකරණය කරන්න"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"ශ්‍රව්‍යය"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"වීඩියෝ ඇමතුම"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"හඬ ඇමතුමක් වෙත මාරු කරන්න"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"කැමරාව මාරු කරන්න"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"කැමරාව ක්‍රියාත්මක කරන්න"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"කැමරාව ක්‍රියා විරහිත කරන්න"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"තවත් විකල්ප"</string>
- <string name="player_started" msgid="3478865572468310331">"ධාවකය ආරම්භ කරන ලදි"</string>
- <string name="player_stopped" msgid="1278611664986561535">"ධාවකය නැවතුණි"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"කැමරාව සූදානම් නැහැ"</string>
- <string name="camera_ready" msgid="2614541247814590887">"කැමරාව සූදානම්"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"නොදන්නා ඇමතුම් සැසි සිදුවීම"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"සේවාව"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"පිහිටුවීම"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;පිහිටුවා නැත&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"වෙනත් ඇමතුම් සැකසීම්"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> හරහා අමතමින්"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> හරහා එන"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"සම්බන්ධතා ඡායාරූපය"</string>
- <string name="goPrivate" msgid="3554069451018659483">"රහසිගත වන්න"</string>
- <string name="selectContact" msgid="92191462970821951">"සම්බන්ධතාවය තෝරාගන්න"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"ඔබේම එකක් ලියන්න..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"අවලංගු කරන්න"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"යවන්න"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"පිළිතුරු දෙන්න"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS යවන්න"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"ප්‍රතික්ෂේප කිරීම"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"වීඩියෝ ඇමතුමට පිළිතුරු දෙන්න"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"ශ්‍රව්‍ය ඇමතුමට පිළිතුරු දෙන්න"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"වීඩියෝ ඉල්ලීම පිළිගන්න"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"වීඩියෝ ඉල්ලීම ප්‍රතික්ෂේප කරන්න"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"වීඩියෝ සම්ප්‍ර්ෂණ ඉල්ලීම පිළිගන්න"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"වීඩියෝ සම්ප්‍ර්ෂණ ඉල්ලීම ප්‍රතික්ෂේප කරන්න"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"වීඩියෝ ලැබීමේ ඉල්ලීම පිළිගන්නා ලදි"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"වීඩියෝ ලැබීමේ ඉල්ලීම ප්‍රතික්ෂේප කරන්න"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> සඳහා උඩට සර්පණය කරන්න."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> සඳහා වමට සර්පණය කරන්න."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> සඳහා දකුණට සර්පණය කරන්න."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> සඳහා පහළට සර්පණය කරන්න."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"කම්පනය"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"කම්පනය"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"හඬ"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"පෙරනිමි ශබ්දය (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"දුරකථන රිගින්ටෝනය"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"රිගින් වන විට කම්පන වන්න"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"රිගින් ටෝන් සහ කම්පනය කරන්න"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"සම්මන්ත්‍රණ ඇමතුම කළමනාකරණය කරන්න"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"හදිසි ඇමතුම් අංකය"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"පැතිකඩ ඡායාරූපය"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"කැමරාව ක්‍රියාවිරහිතයි"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> හරහා"</string>
- <string name="note_sent" msgid="7623014827902758398">"සටහන යවන ලදී"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"මෑත පණිවිඩ"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"ව්‍යාපාර තොරතුරු"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"සැතපුම් <xliff:g id="DISTANCE">%.1f</xliff:g>ක් ඈතින්"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"කි.මි. <xliff:g id="DISTANCE">%.1f</xliff:g>ක් ඈතින්"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"හෙට <xliff:g id="OPEN_TIME">%s</xliff:g>ට විවෘත කෙරේ"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"අද <xliff:g id="OPEN_TIME">%s</xliff:g>ට විවෘත කෙරේ"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g>ට වසයි"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"අද <xliff:g id="CLOSE_TIME">%s</xliff:g>ට වසන ලදී"</string>
- <string name="open_now" msgid="4615706338669555999">"දැන් විවෘතයි"</string>
- <string name="closed_now" msgid="2635314668145282080">"දැන් වසා ඇත"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"සැකසහිත අයාචිත තැපැල් අමතන්නා"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"ඇමතුම අවසන් විය %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"මෙය ඔබට මෙම අංකයෙන් ඇමතුමක් ලැබුණ පළමු අවස්ථාව වේ."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"මෙම ඇමතුම අයාචිත එවන්නෙකු අපි සැක කළෙමු."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"අවහිර ක./අයාචිත වා."</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"සම්බන්ධතාව එක් කරන්න"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"අයාචිත තැපෑලක් නොවේ"</string>
-</resources>
diff --git a/InCallUI/res/values-sk/strings.xml b/InCallUI/res/values-sk/strings.xml
deleted file mode 100644
index 07e8de671..000000000
--- a/InCallUI/res/values-sk/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefón"</string>
- <string name="onHold" msgid="527593602772521700">"Podržaný hovor"</string>
- <string name="unknown" msgid="3646075119047488748">"Neznáme"</string>
- <string name="private_num" msgid="6081418498487514686">"Súkromné číslo"</string>
- <string name="payphone" msgid="5743050584468748607">"Telefónny automat"</string>
- <string name="confCall" msgid="3181961445236675173">"Konferenčný hovor"</string>
- <string name="call_lost" msgid="8208184291640961172">"Hovor bol prerušený"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Reproduktor"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Slúchadlo"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Náhlavná súprava s káblom"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Odoslať nasledujúce tóny?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Odosielanie tónov\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Odoslať"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Áno"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Nie"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Nahradiť zástupný znak znakom"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konferenčný hovor <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Číslo hlasovej schránky"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Vytáča sa"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Znova sa vytáča"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konferenčný hovor"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Prichádzajúci hovor"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Prichádzajúci prac. hovor"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Hovor bol ukončený"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Podržaný hovor"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Ukončovanie hovoru"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Prebieha hovor"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Moje číslo je <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Pripája sa video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videohovor"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Žiada sa video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Videohovor nie je možné pripojiť"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Žiadosť o video bola odmietnutá"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Vaše číslo na spätné volanie\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Vaše číslo na spätné tiesňové volanie\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Vytáča sa"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Zmeškaný hovor"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Zmeškané hovory"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"Zmeškané hovory: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Zmeškaný hovor od volajúceho <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Prebiehajúci hovor"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Prebiehajúci pracovný hovor"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Odchádzajúci hovor cez Wi-Fi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Prebiehajúci pracovný hovor cez Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Podržaný hovor"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Prichádzajúci hovor"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Prichádzajúci pracovný hovor"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Prichádzajúci hovor cez Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Prichádzajúci pracovný hovor cez Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Prichádzajúci videohovor"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Prichádzajúci hovor, pri ktorom je podozrenie, že ide o spam"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Prichádzajúca žiadosť o video"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Nová hlasová správa"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Nová hlasová správa (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Zavolať hlasovú schránku <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Číslo hlasovej schránky je neznáme"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Žiadny signál"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Vybraná sieť (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) nie je k dispozícii"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Prijať"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Položiť"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Hlas"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Prijať"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Odmietnuť"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Zavolať späť"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Správa"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Prebiehajúci hovor v inom zariadení"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Prepojiť hovor"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Ak chcete volať, vypnite najprv režim v lietadle."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Prihlásenie do siete nebolo úspešné."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobilná sieť nie je k dispozícii."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Ak chcete volať, zadajte platné číslo."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Hovor sa nedá uskutočniť."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Prebieha spúšťanie sekvencie MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Služba nie je podporovaná."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Nedajú sa prepínať hovory."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Nedá sa rozdeliť hovor."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Nedá sa preniesť."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Konferenčný hovor sa nedá uskutočniť."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Nedá sa odmietnuť hovor."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Nedajú sa ukončiť hovory."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Hovor SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Tiesňové volanie"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Zapína sa rádio..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Žiadny signál. Prebieha ďalší pokus…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Hovor sa nedá uskutočniť. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> nie je číslo tiesňového volania."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Hovor nie je možné uskutočniť. Vytočte číslo tiesňového volania."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Číslo vytočíte pomocou klávesnice"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Podržať hovor"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Obnoviť hovor"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Ukončiť hovor"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Zobraziť číselnú klávesnicu"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Skryť číselnú klávesnicu"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Vypnúť zvuk"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Zapnúť zvuk"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Pridať hovor"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Zlúčiť hovory"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Zameniť"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Spravovať hovory"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Spravovať konferenčný hovor"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferenčný hovor"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Spravovať"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Zvuk"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videohovor"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Zmeniť na hlasový hovor"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Zapnúť kameru"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Zapnúť fotoaparát"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Vypnúť fotoaparát"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Ďalšie možnosti"</string>
- <string name="player_started" msgid="3478865572468310331">"Prehrávač bol spustený"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Prehrávač bol zastavený"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kamera nie je pripravená"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kamera je pripravená"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Neznáma udalosť relácie volania"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Služba"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Nastavenie"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Nenastavené&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Ďalšie nastavenia hovorov"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Voláte prostredníctvom poskytovateľa <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Prichádz. hovor prostred. poskytovateľa <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"fotka kontaktu"</string>
- <string name="goPrivate" msgid="3554069451018659483">"prepnúť na súkromné"</string>
- <string name="selectContact" msgid="92191462970821951">"vybrať kontakt"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Napísať vlastnú..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Zrušiť"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Odoslať"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Prijať"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Odoslať SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Odmietnuť"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Prijať ako videohovor"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Prijať ako zvukový hovor"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Prijať žiadosť o videohovor"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Odmietnuť žiadosť o videohovor"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Prijať žiadosť o prenos videa"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Odmietnuť žiadosť o prenos videa"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Povoliť žiadosť o prijatie videa"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Odmietnuť žiadosť o prijatie videa"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Prejdite prstom nahor: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Prejdite prstom doľava: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Prejdite prstom doprava: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Prejdite prstom nadol: <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrovanie"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrovanie"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Zvuk"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Predvolený zvuk (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Tón zvonenia telefónu"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibrovať pri zvonení"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Vyzváňací tón a vibrovanie"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Správa konferenčného hovoru"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Číslo tiesňového volania"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profilová fotka"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera je vypnutá"</string>
- <string name="child_number" msgid="4469090994612105532">"na čísle <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Poznámka bola odoslaná"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Nedávne správy"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informácie o firme"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"Vzdialené <xliff:g id="DISTANCE">%.1f</xliff:g> mi"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"Vzdialené <xliff:g id="DISTANCE">%.1f</xliff:g> km"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> – <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Zajtra sa otvára o <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Dnes sa otvára o <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Zatvára sa o <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Dnes bolo zatvorené o <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Otvorené"</string>
- <string name="closed_now" msgid="2635314668145282080">"Zatvorené"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Podozrenie na spam"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Hovor sa skončil %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Toto bol prvý hovor z tohto čísla."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Mali sme podozrenie, že tento hovor bol od šíriteľa spamu."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blok./nahlásiť spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Pridať kontakt"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Toto nie je spam"</string>
-</resources>
diff --git a/InCallUI/res/values-sl/strings.xml b/InCallUI/res/values-sl/strings.xml
deleted file mode 100644
index a2cf2102b..000000000
--- a/InCallUI/res/values-sl/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"Zadržan"</string>
- <string name="unknown" msgid="3646075119047488748">"Neznan"</string>
- <string name="private_num" msgid="6081418498487514686">"Zasebna številka"</string>
- <string name="payphone" msgid="5743050584468748607">"Telefonska govorilnica"</string>
- <string name="confCall" msgid="3181961445236675173">"Konferenčni klic"</string>
- <string name="call_lost" msgid="8208184291640961172">"Klic je bil prekinjen"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Zvočnik"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Slušalka"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Žične slušalke"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Ali želite poslati naslednje tone?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Pošiljanje tonov\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Pošlji"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Da"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Ne"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Zamenjaj nadomestni znak z"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konferenčni klic: <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Številka odzivnika"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Klicanje"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Vnovično klicanje"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konferenčni klic"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Dohodni klic"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Dohodni delovni klic"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Klic je končan"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Zadržan"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Prekinjanje"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Klic poteka"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Moja številka je <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Povezovanje videa"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videoklic"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Zahtevanje videa"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Videoklica ni mogoče vzpostaviti"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Zavrnjena zahteva za videoklic"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Vaša številka za povratni klic:\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Vaša številka za povratni klic v sili:\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Klicanje"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Neodgovorjeni klic"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Neodgovorjeni klici"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"Št. neodgovorjenih klicev: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Neodgovorjeni klic od: <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Aktivni klic"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Aktivni delovni klic"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Aktivni klic prek omrežja Wi-Fi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Aktivni delovni klic prek omrežja Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Zadržan"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Dohodni klic"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Dohodni delovni klic"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Dohodni klic Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Dohodni delovni klic prek omrežja Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Dohodni videoklic"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Domnevno neželeni dohodni klic"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Zahteva za dohodni video"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Novo sporočilo v odzivniku"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Novo sporočilo v odzivniku (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Klic: <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Neznana številka odzivnika"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Ni signala"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Izbrano omrežje (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) ni na voljo"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Odgovor"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Prekinitev"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Govor"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Sprejmem"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Opusti"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Povrat. klic"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"SMS"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Aktivni klic v drugi napravi"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Prenos klica"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Če želite poklicati, najprej izklopite način za letalo."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Ni registrirano v omrežju."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mobilno omrežje ni na voljo."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Če želite opraviti klic, vnesite veljavno številko."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Klicanje ni mogoče."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Začetek zaporedja MMI ..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Storitev ni podprta."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Preklop med klici ni mogoč."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Ločitev klica ni mogoča."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Prenos ni mogoč."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Konferenčni klic ni mogoč."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Zavrnitev klica ni mogoča."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Prekinitev klica ni mogoča."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Klic SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Klic v sili"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Vklop radia …"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Ni signala. Vnovičen poskus …"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Klicanje ni mogoče. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> ni številka za klic v sili."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Klicanje ni mogoče. Opravite klic v sili."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Za izbiranje številke uporabite tipkovnico"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Zadrži klic"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Nadaljuj klic"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Končaj klic"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Prikaži tipkovnico"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Skrij tipkovnico"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Izklopi zvok"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Vklopi zvok"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Dodaj klic"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Združi klice"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Zamenjaj"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Upravljaj klice"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Upravljaj konferenčne klice"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferenčni klic"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Upravljaj"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Zvok"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videoklic"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Preklopi na glasovni klic"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Preklopi med fotoaparati"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Vklopi kamero"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Izklopi kamero"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Več možnosti"</string>
- <string name="player_started" msgid="3478865572468310331">"Predvajanje začeto"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Predvajanje ustavljeno"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Fotoaparat ni pripravljen"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Fotoaparat je pripravljen"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Neznan dogodek seje klica"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Storitev"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Nastavitev"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Ni nastavljeno&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Druge klicne nastavitve"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Klicanje prek ponudnika <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Dohodni prek <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"fotografija stika"</string>
- <string name="goPrivate" msgid="3554069451018659483">"zasebno"</string>
- <string name="selectContact" msgid="92191462970821951">"izbira stika"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Napišite lasten odgovor …"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Prekliči"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Pošlji"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Odgovor"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Pošiljanje SMS-ja"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Zavrnitev"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Odgovor z video povezavo"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Odgovor z zvočno povezavo"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Sprejemanje zahteve za video"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Zavrnitev zahteve za video"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Sprejemanje zahteve za pošiljanje videa"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Zavrnitev zahteve za pošiljanje videa"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Sprejemanje zahteve za prejem videa"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Zavrnitev zahteve za prejem videa"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Povlecite navzgor za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Povlecite v levo za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Povlecite v desno za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Povlecite navzdol za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibriranje"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibriranje"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Zvok"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Privzeti zvok (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Ton zvonjenja telefona"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Vibriranje ob zvonjenju"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Zvonjenje in vibriranje"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Upravljanje konferenčnih klicev"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Številka za klic v sili"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Fotografija profila"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Fotoaparat je izklopljen"</string>
- <string name="child_number" msgid="4469090994612105532">"prek <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Opomba poslana"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Nedavna sporočila"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Podatki o podjetju"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> mi stran"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km stran"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>–<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Odpre se jutri ob <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Odpre se danes ob <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Zapre se ob <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Zaprto danes ob <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Trenutno odprto"</string>
- <string name="closed_now" msgid="2635314668145282080">"Trenutno zaprto"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Neželeni klicatelj"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Klic je bil končan %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"To je prvi klic, ki ste ga prejeli s te številke."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Predvidevali smo, da je to neželeni klic."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blok./prij. než. kl."</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Dodaj stik"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Ni neželeni klic"</string>
-</resources>
diff --git a/InCallUI/res/values-sq/strings.xml b/InCallUI/res/values-sq/strings.xml
deleted file mode 100644
index 43fd2263d..000000000
--- a/InCallUI/res/values-sq/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefoni"</string>
- <string name="onHold" msgid="527593602772521700">"Në pritje"</string>
- <string name="unknown" msgid="3646075119047488748">"I panjohur"</string>
- <string name="private_num" msgid="6081418498487514686">"Numër privat"</string>
- <string name="payphone" msgid="5743050584468748607">"Telefon me pagesë"</string>
- <string name="confCall" msgid="3181961445236675173">"Telefonatë konferencë"</string>
- <string name="call_lost" msgid="8208184291640961172">"Telefonata ra"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Altoparlant"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Kufje për vesh"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Kufje me tel"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Dërgo tonet e mëposhtme?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Po dërgon tone\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Dërgo"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Po"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Jo"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Zëvendëso karakterin variabël me"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Telefonatë konferencë <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Numri i postës zanore"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Po formon numrin"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Po riformon numrin"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Telefonatë konferencë"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Telefonatë hyrëse"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Telefonatë pune hyrëse"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Telefonata përfundoi"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Në pritje"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Mbyllja"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Në telefonatë"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Numri im është <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Po rilidh videon"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Telefonatë me video"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Po kërkon video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Nuk mund të lidhë telefonatën me video"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Kërkesa me video u refuzua"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Numri i kthimit të telefonatës\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Numri i kthimit të telefonatës së urgjencës\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Po formon numrin"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Telefonatë e humbur"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Telefonata të humbura"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> telefonata të humbura"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Telefonatë e humbur nga <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Telefonatë në vazhdim"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Telefonatë pune dalëse"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Telefonatë me Wi-Fi në vazhdim"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Telefonatë pune dalëse me Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Në pritje"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Telefonatë hyrëse"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Telefonatë pune hyrëse"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Telefonatë hyrëse me Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Telefonatë pune hyrëse me Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Telefonatë hyrëse me video"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Telefonatë e dyshuar si e padëshiruar"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Kërkesë për video hyrëse"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Postë e re zanore"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Postë e re zanore (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Formo numrin <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Numri i postës zanore është i panjohur"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Nuk ka shërbim"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Rrjeti i zgjedhur (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) i padisponueshëm"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Përgjigju"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Mbyll"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Zanore"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Prano"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Largoje"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Ri-telefono"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Mesazh"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Telefonatë në vazhdim në një pajisje tjetër"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Transfero telefonatën"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Për të kryer telefonatë, së pari çaktivizo modalitetin e aeroplanit."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"I paregjistruar në rrjet."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Rrjeti celular nuk mundësohet."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Për të kryer një telefonatë, fut një numër të vlefshëm."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Nuk mund të telefonojë."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Po fillon sekuencën MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Shërbimi nuk mbështetet."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Nuk mund të ndryshojë telefonatat."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Nuk mund të ndajë telefonatën."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Nuk mund të transferojë."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Nuk mund të kryejë telefonatë konference."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Nuk mund të refuzojë telefonatën."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Nuk mund të lëshojë telefonatën(at)."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Telefonatë SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Telefonata e urgjencës"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Po aktivizon radion…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Nuk ka shërbim. Po provon sërish…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Nuk mund të telefonohet. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> nuk është një numër urgjence."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Nuk mund të telefonohet. Formo një numër urgjence."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Përdor tastierën për të formuar numrin"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Vendose në pritje telefonatën"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Rifillo telefonatën"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Mbylle telefonatën"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Shfaq bllokun e formimit të numrit"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Fshih bllokun e formimit të numrit"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Çaktivizo audion"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Aktivizo audion"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Shto telefonatë"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Shkri telefonatat"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Shkëmbe"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Menaxho telefonatat"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Menaxho telefonatën konferencë"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Telefonatë konferencë"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Menaxho"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audioja"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Telefonatë me video"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Ndërro në telefonatë me video"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Ndërro kamerën"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Aktivizo kamerën"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Çaktivizo kamerën"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Opsione të tjera"</string>
- <string name="player_started" msgid="3478865572468310331">"Luajtësi filloi"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Luajtësi ndaloi"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kamera nuk është gati"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kamera është gati"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Ngjarje e panjohur në sesionin e telefonatës"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Shërbimi"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Konfigurimi"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;I pavendosur&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Cilësime të tjera të telefonatës"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Telefonatë nëpërmjet <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Hyrëse nëpërmjet <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"fotografia e kontaktit"</string>
- <string name="goPrivate" msgid="3554069451018659483">"bëje private"</string>
- <string name="selectContact" msgid="92191462970821951">"përzgjidh kontaktin"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Shkruaj përgjigjen tënde..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Anulo"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Dërgo"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Përgjigju"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Dërgo SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Refuzo"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Përgjigju si telefonatë me video"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Përgjigju si telefonatë me audio"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Prano kërkesën për video"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Refuzo kërkesën për video"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Prano kërkesën për transmetimin e videos"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Refuzo kërkesën për transmetimin e videos"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Prano kërkesën për marrjen e videos"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Refuzo kërkesën për marrjen e videos"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Rrëshqit lart për <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Rrëshqit majtas për <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Rrëshqit djathtas për <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Rrëshqit poshtë për <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Dridhja"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Dridhja"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Tingulli"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Tingulli i parazgjedhur (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Toni i ziles i telefonit"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Dridhje edhe kur bie zilja"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Me zile dhe me dridhje"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Menaxho telefonatën konferencë"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Numri i urgjencës"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Fotografia e profilit"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera joaktive"</string>
- <string name="child_number" msgid="4469090994612105532">"përmes <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Shënimi u dërgua"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Mesazhet e fundit"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Informacioni i biznesit"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> milje larg"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km larg"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Hapet nesër në <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Hapet sot në <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Mbyllet në <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Mbyllur sot në <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Tani është hapur"</string>
- <string name="closed_now" msgid="2635314668145282080">"Tani është mbyllur"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"I padëshirueshëm"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Telefonata përfundoi %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Kjo është hera e parë që ky numër ka telefonuar."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Ne dyshojmë që kjo telefonatë të jetë e padëshirueshme."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blloko/raporto të padëshiruar"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Shto një kontakt"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Jo i padëshiruar"</string>
-</resources>
diff --git a/InCallUI/res/values-sr/strings.xml b/InCallUI/res/values-sr/strings.xml
deleted file mode 100644
index 3a3820d8c..000000000
--- a/InCallUI/res/values-sr/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Телефон"</string>
- <string name="onHold" msgid="527593602772521700">"На чекању"</string>
- <string name="unknown" msgid="3646075119047488748">"Непознат"</string>
- <string name="private_num" msgid="6081418498487514686">"Приватан број"</string>
- <string name="payphone" msgid="5743050584468748607">"Телефонска говорница"</string>
- <string name="confCall" msgid="3181961445236675173">"Конференцијски позив"</string>
- <string name="call_lost" msgid="8208184291640961172">"Позив је прекинут"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Звучник"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Слушалица телефона"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Жичане наглавне слушалице"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Желите ли да пошаљете следеће тонове?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Тонови се шаљу\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Пошаљи"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Да"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Не"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Замените џокер знак са"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Конференцијски позив <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Број говорне поште"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Позива се"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Поново се бира"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Конференцијски позив"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Долазни позив"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Долазни позив за Work"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Позив је завршен"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"На чекању"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Веза се прекида"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Позив је у току"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Мој број је <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Повезује се видео позив"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Видео позив"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Захтева се видео позив"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Повезивање видео позива није успело"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Захтев за видео позив је одбијен"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Број за повратни позив\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Број за хитан повратни позив\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Позива се"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Пропуштен позив"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Пропуштени позиви"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"Број пропуштених позива: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Пропуштен позив од: <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Текући позив"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Текући позив за Work"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Текући Wi-Fi позив"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Текући позив за Work преко Wi-Fi-ја"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"На чекању"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Долазни позив"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Долазни позив за Work"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Долазни Wi-Fi позив"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Долазни позив за Work преко Wi-Fi-ја"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Долазни видео позив"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Сумња на непожељан долазни позив"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Захтев за долазни видео позив"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Нова порука говорне поште"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Нова порука говорне поште (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Позови <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Непознат број говорне поште"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Мобилна мрежа није доступна"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Изабрана мрежа (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) није доступна"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Одговори"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Прекини везу"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Видео"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Гласовни"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Прихватам"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Одбаци"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Узврати позив"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Пошаљи SMS"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Позив је у току на другом уређају"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Пребаци позив"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Да бисте упутили позив, прво искључите режим рада у авиону."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Није регистровано на мрежи."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Мобилна мрежа није доступна."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Да бисте упутили позив, унесите важећи број."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Позив није успео."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Покреће се MMI секвенца..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Услуга није подржана."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Замена позива није успела."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Раздвајање позива није успело."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Пребацивање није успело."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Конференцијски позив није успео."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Одбијање позива није успело."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Успостављање позива није успело."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP позив"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Хитни позив"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Укључује се радио…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Мобилна мрежа није доступна. Покушавамо поново…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Позив није успео. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> није број за хитне случајеве."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Позив није успео. Позовите број за хитне случајеве."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Користите тастатуру за позивање"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Стави позив на чекање"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Настави позив"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Заврши позив"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Прикажи нумеричку тастатуру"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Сакриј нумеричку тастатуру"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Искључи звук"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Укључи звук"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Додај позив"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Обједини позиве"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Замени"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Управљај позивима"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Управљај конференцијским позивом"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Конференцијски позив"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Управљај"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Аудио"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Видео позив"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Промени у гласовни позив"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Промени камеру"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Укључи камеру"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Искључи камеру"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Још опција"</string>
- <string name="player_started" msgid="3478865572468310331">"Плејер је покренут"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Плејер је заустављен"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Камера није спремна"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Камера је спремна"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Непознат догађај сесије позива"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Услуга"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Подешавање"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Није подешено&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Друга подешавања позива"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Позива се преко добављача <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Долазни позив преко <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"слика контакта"</string>
- <string name="goPrivate" msgid="3554069451018659483">"иди на приватно"</string>
- <string name="selectContact" msgid="92191462970821951">"изаберите контакт"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Напишите сами…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Откажи"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Пошаљи"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Одговори"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Пошаљи SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Одбиј"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Одговори видео позивом"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Одговори аудио-позивом"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Прихвати захтев за видео"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Одбиј захтев за видео"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Прихвати захтев за одлазни видео позив"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Одбиј захтев за одлазни видео позив"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Прихвати захтев за долазни видео позив"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Одбиј захтев за долазни видео позив"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Превуците нагоре за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Превуците улево за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Превуците удесно за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Превуците надоле за <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Вибрација"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Вибрација"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Звук"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Подразумевани звук (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Мелодија звона телефона"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Вибрирај када звони"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Мелодија звона и вибрација"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Управљај конференцијским позивом"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Број за хитне случајеве"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Слика профила"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Камера је искључена"</string>
- <string name="child_number" msgid="4469090994612105532">"на <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Белешка је послата"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Недавне поруке"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Информације о предузећу"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"Удаљеност је <xliff:g id="DISTANCE">%.1f</xliff:g> mi"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"Удаљеност је <xliff:g id="DISTANCE">%.1f</xliff:g> km"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>–<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Отвара се сутра у <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Отвара се данас у <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Затвара се у <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Затворило се данас у <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Тренутно отворено"</string>
- <string name="closed_now" msgid="2635314668145282080">"Тренутно затворено"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Непожељан позивалац"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Позив се завршио у %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Ово је био први позив са овог броја."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Сумњамо да је овај позив непожељан."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Блокирај/пријави"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Додај контакт"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Није непожељан"</string>
-</resources>
diff --git a/InCallUI/res/values-sv/strings.xml b/InCallUI/res/values-sv/strings.xml
deleted file mode 100644
index 980ecdb07..000000000
--- a/InCallUI/res/values-sv/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"Parkerat"</string>
- <string name="unknown" msgid="3646075119047488748">"Okänd"</string>
- <string name="private_num" msgid="6081418498487514686">"Privat nummer"</string>
- <string name="payphone" msgid="5743050584468748607">"Telefonautomat"</string>
- <string name="confCall" msgid="3181961445236675173">"Konferenssamtal"</string>
- <string name="call_lost" msgid="8208184291640961172">"Samtalet avbröts"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Högtalare"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Telefonlur"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Trådanslutet headset"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Ska följande toner skickas?\nBREAK"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Skickar signaler\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Skicka"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Ja"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Nej"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Ersätt jokertecknet med"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konferenssamtal <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Nummer till röstbrevlåda"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Ringer"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Ringer upp igen"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konferenssamtal"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Inkommande samtal"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Inkommande jobbsamtal"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Samtal avslutat"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Parkerat"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Lägger på"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"I samtal"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Mitt telefonnummer är <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Ansluter video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Videosamtal"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Begär video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Det gick inte att ansluta till videosamtalet"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Videobegäran avslogs"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Ditt nummer för återuppringning\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Ditt nummer för återuppringning vid nödsamtal\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Ringer"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Missat samtal"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Missade samtal"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> missade samtal"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Missat samtal från <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Pågående samtal"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Pågående jobbsamtal"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Pågående Wi-Fi-samtal"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Pågående jobbsamtal via Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Parkerat"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Inkommande samtal"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Inkommande jobbsamtal"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Inkommande Wi-Fi-samtal"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Inkommande jobbsamtal via Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Inkommande videosamtal"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Inkommande misstänkt spamsamtal"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Inkommande begäran om videosamtal"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Nytt röstmeddelande"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Nytt röstmeddelande (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Ring <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Nummer till röstbrevlåda okänt"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Ingen tjänst"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Det valda nätverket (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) är inte tillgängligt"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Svara"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Lägg på"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Röstsamtal"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Godkänn"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Ignorera"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Ring upp"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Meddelande"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Pågående samtal på en annan enhet"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Överför samtal"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Om du vill ringa måste du först inaktivera flygplansläge."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Inte registrerat på nätverk."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Det finns inget mobilnät tillgängligt."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Ange ett giltigt nummer om du vill ringa ett samtal."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Det gick inte att ringa."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Startar sekvens för MMI-kod …"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Tjänsten stöds inte."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Det gick inte att växla mellan samtal."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Det gick inte att koppla isär samtalen."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Det gick inte att överföra."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Det gick inte att starta ett konferenssamtal."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Det gick inte att avvisa samtalet."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Det gick inte att släppa samtal."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP-samtal"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Nödsamtal"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Sätter på radion …"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Ingen tjänst. Försöker igen …"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Det gick inte att ringa. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> är inget nödnummer."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Det gick inte att ringa. Slå ett nödnummer."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Använd tangentbordet om du vill ringa"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Parkera samtal"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Återuppta samtal"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Avsluta samtal"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Visa knappsats"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Dölj knappsats"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Ljud av"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Sluta ignorera"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Lägg till samtal"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Koppla ihop samtal"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Byt"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Hantera samtal"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Hantera konferenssamtal"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferenssamtal"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Hantera"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Ljud"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Videosamt."</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Byt till röstsamtal"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Byt kamera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Slå på kameran"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Stäng av kameran"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Fler alternativ"</string>
- <string name="player_started" msgid="3478865572468310331">"Videospelaren har startats"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Videospelaren har stoppats"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kameran är inte klar"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kameran är klar"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Okänd händelse vid samtalssession"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Tjänst"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Konfiguration"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Har inte angetts&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Övriga samtalsinställningar"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Ringer via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Inkommande via <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"kontaktbild"</string>
- <string name="goPrivate" msgid="3554069451018659483">"gör privat"</string>
- <string name="selectContact" msgid="92191462970821951">"välj kontakt"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Skriv ett eget svar …"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Avbryt"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Skicka"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Svara"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Skicka sms"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Avvisa"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Svara som videosamtal"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Svara som röstsamtal"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Godkänn videobegäran"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Avvisa videobegäran"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Godkänn begäran om att skicka video"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Avvisa begäran om att skicka video"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Godkänn begäran om att ta emot video"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Avvisa begäran om att ta emot video"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> genom att dra uppåt."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> genom att dra åt vänster."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> genom att dra åt höger."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> genom att dra nedåt."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Vibrera"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Vibrera"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Ljud"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Standardsignal (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Ringsignal"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Enheten vibrerar vid samtal"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Ringsignal och vibration"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Hantera konferenssamtal"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Nödsamtalsnummer"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profilbild"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera av"</string>
- <string name="child_number" msgid="4469090994612105532">"via <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Anteckningen har skickats"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Senaste meddelandena"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Företagsuppgifter"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> miles bort"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km bort"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>–<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Öppnar i morgon kl. <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Öppnar i dag kl. <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Stänger kl. <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Stängde i dag kl. <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Öppet"</string>
- <string name="closed_now" msgid="2635314668145282080">"Stängt"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Misstänkt spamsamtal"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Samtalet avslutat %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Det här är första gången det här numret ringde dig."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Vi misstänkte att det här samtalet var en spammare."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Blockera/rapp. spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Lägg till kontakt"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Inte spam"</string>
-</resources>
diff --git a/InCallUI/res/values-sw/strings.xml b/InCallUI/res/values-sw/strings.xml
deleted file mode 100644
index d60c4901a..000000000
--- a/InCallUI/res/values-sw/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Simu"</string>
- <string name="onHold" msgid="527593602772521700">"Inangoja"</string>
- <string name="unknown" msgid="3646075119047488748">"Isiyojulikana"</string>
- <string name="private_num" msgid="6081418498487514686">"Nambari ya faragha"</string>
- <string name="payphone" msgid="5743050584468748607">"Simu ya kulipia"</string>
- <string name="confCall" msgid="3181961445236675173">"Simu ya mkutano"</string>
- <string name="call_lost" msgid="8208184291640961172">"Simu imekatwa"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Spika"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Kipaza sauti cha kichwani"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Vifaa vya sauti visivyo na waya"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Ungependa kutuma milio ya sauti inayofuata? \n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Inatuma milio ya simu\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Tuma"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Ndiyo"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Hapana"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Badilisha herufi inayojitegemea kwa"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Simu ya mkutano <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Nambari ya ujumbe wa sauti"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Inapiga"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Inapiga simu tena"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Simu ya mkutano"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Unapigiwa simu"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Simu ya kazi inayoingia"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Simu imekamilika"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Inangoja"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Kukata simu"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Katika simu"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Nambari yangu ni <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Inaunganisha video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Hangout ya Video"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Inaomba video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Haiwezi kuunganisha Hangout ya video"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Ombi la video limekataliwa"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Nambari yako ya kupigiwa simu\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Nambari yako ya dharura ya kupigiwa simu\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Inapiga"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Simu ambayo hukujibu"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Simu ambazo hukujibu"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"Simu <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ambazo hukujibu"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Simu ambayo hukujibu kutoka <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Simu inayoendelea"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Simu ya kazi inayoendelea"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Simu ya Wi-Fi inayoendelea"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Simu ya Wi-Fi ya kazi inayoendelea"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Inangoja"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Unapigiwa simu"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Unapigiwa simu ya kazi"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Unapigiwa simu kupitia Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Unapigiwa simu ya kazini kupitia Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Hangout ya Video inayoingia"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Simu inayoingia inashukiwa kuwa taka"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Ombi linaloingia la video"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Ujumbe mpya wa sauti"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Ujumbe (<xliff:g id="COUNT">%d</xliff:g>) mpya wa sauti"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Piga <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Nambari ya ujumbe wa sauti haijulikani."</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Hakuna huduma"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Mtandao uliochaguliwa (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) haupatikani"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Jibu"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Kata simu"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Sauti"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Kubali"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Ondoa"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Mpigie"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Ujumbe"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Una Hangout inayoendelea kwenye kifaa kingine"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Hamisha Hangout"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Ili upige simu kwanza, zima Hali ya ndegeni."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Haijasajiliwa kwenye mtandao."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Mitandao ya simu za mkononi haipatikani."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Ili upige simu, weka nambari sahihi."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Haiwezi kupiga simu."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Inaanzisha msururu wa MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Huduma haitumiki."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Haiwezi kubadili simu."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Haiwezi kutenganisha simu."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Haiwezi kuhamisha."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Haiwezi kushiriki katika simu ya mkutano."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Haiwezi kukataa simu."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Haiwezi kutoa simu."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Simu ya SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Simu ya dharura"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Inawasha redio..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Hakuna huduma. Inajaribu tena..."</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Haiwezi kupiga simu. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> si nambari ya dharura."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Haiwezi kupiga simu. Piga simu nambari ya dharura."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Tumia kibodi kubonyeza"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Shikilia Simu"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Endelea na Simu"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Kata Simu"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Onyesha Vitufe vya Kupiga Simu"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Ficha Vitufe vya Kupiga Simu"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Zima Sauti"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Rejesha sauti"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Ongeza simu"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Unganisha simu"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Badili"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Dhibiti simu"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Dhibiti simu ya mkutano"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Mkutano kwenye simu"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Dhibiti"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Sauti"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Hangout ya Video"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Badilisha iwe simu ya sauti"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Badilisha kamera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Washa kamera"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Zima kamera"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Chaguo zaidi"</string>
- <string name="player_started" msgid="3478865572468310331">"Kichezaji Kimeanzishwa"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Kichezaji Kimekomeshwa"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kamera haiko tayari"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kamera iko tayari"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Tukio lisilojulikana la kipindi cha simu"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Huduma"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Weka mipangilio"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Haijawekwa&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Mipangilio mingine ya simu"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Kupiga simu kupitia <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Simu zinazoingia kupitia <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"picha ya anwani"</string>
- <string name="goPrivate" msgid="3554069451018659483">"tumia kwa faragha"</string>
- <string name="selectContact" msgid="92191462970821951">"chagua anwani"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Andika yako binafsi..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Ghairi"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Tuma"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Jibu"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Tuma SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Kataa"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Pokea kama Hangout ya Video"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Pokea kama simu ya sauti"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Kubali ombi la video"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Kataa ombi la video"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Kubali ombi la kutuma kupitia hangout ya video"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Kataa ombi la kutuma kupitia hangout ya video"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Kubali ombi la kupokea kupitia hangout ya video"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Kataa ombi la kupokea kupitia hangout ya video"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Telezesha kidole juu ili <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Telezesha kidole kushoto ili <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Telezesha kidole kulia ili <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Telezesha kidole chini ili <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Mtetemo"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Mtetemo"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Mlio"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Sauti chaguo-msingi (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Mlio wa simu"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Tetema wakati wa kuita"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Mlio wa simu na Mtetemo"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Dhibiti simu ya mkutano"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Nambari ya dharura"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Picha ya wasifu"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera imezimwa"</string>
- <string name="child_number" msgid="4469090994612105532">"kupitia <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Dokezo limetumwa"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Ujumbe wa hivi majuzi"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Maelezo ya biashara"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"Umbali wa maili <xliff:g id="DISTANCE">%.1f</xliff:g>"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"Umbali wa kilomita <xliff:g id="DISTANCE">%.1f</xliff:g>"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Itafunguliwa kesho saa <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Itafunguliwa leo saa <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Hufungwa saa <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Imefungwa leo saa <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Sasa imefunguliwa"</string>
- <string name="closed_now" msgid="2635314668145282080">"Sasa imefungwa"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Mpiga simu taka"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Simu imekatwa %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Hii ndiyo mara ya kwanza nambari hii imekupigia."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Tunashuku kwamba simu hii ni taka."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Zuia/ripoti taka"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Ongeza anwani"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Si barua taka"</string>
-</resources>
diff --git a/InCallUI/res/values-sw360dp/dimens.xml b/InCallUI/res/values-sw360dp/dimens.xml
deleted file mode 100644
index 9fbcd605b..000000000
--- a/InCallUI/res/values-sw360dp/dimens.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources>
- <!-- Dimensions for CallCard elements (the normal in-call UI) -->
-
- <!-- Text size for the name in the call info. -->
- <dimen name="call_status_text_size">22sp</dimen>
- <dimen name="call_status_text_min_size">18sp</dimen>
- <dimen name="call_name_text_size">45dp</dimen>
- <dimen name="call_name_text_min_size">34sp</dimen>
- <dimen name="call_label_text_size">18sp</dimen>
-
- <!-- The InCallUI dialpad will sometimes want digits sizes that are different from dialer. -->
- <dimen name="dialpad_key_number_margin_bottom">@dimen/dialpad_key_number_default_margin_bottom</dimen>
- <!-- Zero key should have less space between self and text because "+" is smaller -->
- <dimen name="dialpad_zero_key_number_margin_bottom">@dimen/dialpad_zero_key_number_default_margin_bottom</dimen>
- <dimen name="dialpad_digits_adjustable_text_size">@dimen/dialpad_digits_text_size</dimen>
- <dimen name="dialpad_digits_adjustable_height">@dimen/dialpad_digits_height</dimen>
- <dimen name="dialpad_key_numbers_size">@dimen/dialpad_key_numbers_default_size</dimen>
-</resources>
diff --git a/InCallUI/res/values-sw410dp/config.xml b/InCallUI/res/values-sw410dp/config.xml
deleted file mode 100644
index a57f86784..000000000
--- a/InCallUI/res/values-sw410dp/config.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
- ~ 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
- -->
-
-<resources>
- <!-- Determines the maximum number of buttons visible on the call card. Any buttons over this
- count are put into the overflow menu. -->
- <integer name="call_card_max_buttons">6</integer>
-</resources> \ No newline at end of file
diff --git a/InCallUI/res/values-ta/strings.xml b/InCallUI/res/values-ta/strings.xml
deleted file mode 100644
index 1ee57b4ba..000000000
--- a/InCallUI/res/values-ta/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"ஃபோன்"</string>
- <string name="onHold" msgid="527593602772521700">"ஹோல்டில் உள்ளது"</string>
- <string name="unknown" msgid="3646075119047488748">"தெரியாத எண்"</string>
- <string name="private_num" msgid="6081418498487514686">"தனிப்பட்ட எண்"</string>
- <string name="payphone" msgid="5743050584468748607">"கட்டணத் தொலைபேசி"</string>
- <string name="confCall" msgid="3181961445236675173">"குழு அழைப்பு"</string>
- <string name="call_lost" msgid="8208184291640961172">"அழைப்பு நிறுத்தப்பட்டது"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"ஸ்பீக்கர்"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"ஹேண்ட்செட் இயர்ஃபீஸ்"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"வயருள்ள ஹெட்செட்"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"புளூடூத்"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"பின்வரும் டோன்களை அனுப்பவா?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"டோன்களை அனுப்புகிறது\n"</string>
- <string name="send_button" msgid="4054398309483035794">"அனுப்பு"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"ஆம்"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"வேண்டாம்"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"சிறப்புக்குறியை இதன் மூலம் மாற்றியமை"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"குழு அழைப்பு: <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"குரலஞ்சல் எண்"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"அழைக்கிறது"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"மீண்டும் டயல் செய்கிறது"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"குழு அழைப்பு"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"உள்வரும் அழைப்பு"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"உள்வரும் அழைப்பு (பணி)"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"அழைப்பு முடிந்தது"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"ஹோல்டில் உள்ளது"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"துண்டிக்கிறது"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"அழைப்பில்"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"எனது எண்: <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"வீடியோவை இணைக்கிறது"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"வீடியோ அழைப்பு"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"வீடியோவைக் கோருகிறது"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"வீடியோ அழைப்பை இணைக்க முடியவில்லை"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"வீடியோ கோரிக்கை நிராகரிக்கப்பட்டது"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"உங்களைத் திரும்ப அழைப்பதற்கான எண்\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"அவசர அழைப்பு எண்\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"அழைக்கிறது"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"தவறிய அழைப்பு"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"தவறிய அழைப்புகள்"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> தவறிய அழைப்புகள்"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> இடமிருந்து தவறிய அழைப்பு"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"செயலில் இருக்கும் அழைப்பு"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"செயலில் இருக்கும் அழைப்பு (பணி)"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"செயலில் இருக்கும் வைஃபை அழைப்பு"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"செயலில் இருக்கும் வைஃபை அழைப்பு"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"ஹோல்டில் உள்ளது"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"உள்வரும் அழைப்பு"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"உள்வரும் அழைப்பு (பணி)"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"உள்வரும் வைஃபை அழைப்பு"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"உள்வரும் வைஃபை அழைப்பு (பணி)"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"உள்வரும் வீடியோ அழைப்பு"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"உள்வரும் சந்தேகத்திற்குரிய ஸ்பேம் அழைப்பு"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"உள்வரும் வீடியோ கோரிக்கை"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"புதிய குரலஞ்சல்"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"புதிய குரலஞ்சல் (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>ஐ அழை"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"குரலஞ்சல் எண் அறியப்படவில்லை"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"சேவை இல்லை"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"தேர்ந்தெடுத்த நெட்வொர்க் (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) கிடைக்கவில்லை"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"பதிலளி"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"துண்டி"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"வீடியோ"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"குரல்"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"ஏற்கிறேன்"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"நிராகரி"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"திரும்ப அழை"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"செய்தி அனுப்பு"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"மற்றொரு சாதனத்தில் செயலில் இருக்கும் அழைப்பு"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"அழைப்பை இடமாற்று"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"அழைப்பதற்கு, முதலில் விமானப் பயன்முறையை முடக்கவும்."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"நெட்வொர்க்கில் பதிவுசெய்யப்படவில்லை."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"செல்லுலார் நெட்வொர்க் கிடைக்கவில்லை."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"அழைக்க, சரியான எண்ணை உள்ளிடவும்."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"அழைக்க முடியாது."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI வரிசையைத் தொடங்குகிறது..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"சேவை ஆதரிக்கப்படவில்லை."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"அழைப்புகளில் மாற முடியாது."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"அழைப்பைப் பிரிக்க முடியாது."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"மாற்ற முடியாது."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"குழு அழைப்பு செய்ய முடியாது."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"அழைப்பை நிராகரிக்க முடியாது."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"அழைப்பை(அழைப்புகளை) விடுவிக்க முடியாது."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP அழைப்பு"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"அவசர அழைப்பு"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"ரேடியோவை இயக்குகிறது…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"சேவை இல்லை. மீண்டும் முயல்கிறது…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"<xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> என்பது அவசர அழைப்பு எண் இல்லை என்பதால் அழைக்க முடியாது."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"அழைக்க முடியாது. அவசர அழைப்பு எண்ணை அழைக்கவும்."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"டயல் செய்ய, விசைப்பலகையைப் பயன்படுத்தவும்"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"அழைப்பை ஹோல்டில் வை"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"அழைப்பை மீண்டும் தொடங்கு"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"அழைப்பை முடி"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"டயல்பேடைக் காட்டு"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"டயல்பேடை மறை"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"ஒலியடக்கு"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"ஒலி இயக்கு"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"அழைப்பைச் சேர்"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"அழைப்புகளை இணை"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"மாற்று"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"அழைப்புகளை நிர்வகி"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"குழு அழைப்பை நிர்வகி"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"குழு அழைப்பு"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"நிர்வகி"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"ஆடியோ"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"வீடியோ அழைப்பு"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"குரல் அழைப்பிற்கு மாறு"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"கேமராவை மாற்று"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"கேமராவை இயக்கு"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"கேமராவை முடக்கு"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"கூடுதல் விருப்பங்கள்"</string>
- <string name="player_started" msgid="3478865572468310331">"வீடியோ பிளேயர் துவங்கியது"</string>
- <string name="player_stopped" msgid="1278611664986561535">"வீடியோ பிளேயர் நிறுத்தப்பட்டது"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"கேமரா தயாராக இல்லை"</string>
- <string name="camera_ready" msgid="2614541247814590887">"கேமரா தயார்"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"தெரியாத அழைப்பு நேர நிகழ்வு"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"சேவை"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"அமை"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;அமைக்கப்படவில்லை&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"பிற அழைப்பு அமைப்புகள்"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> வழியாக அழைக்கிறது"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> மூலம் உள்வரும் அழைப்புகள்"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"தொடர்புப் படம்"</string>
- <string name="goPrivate" msgid="3554069451018659483">"தனிப்பட்டதிற்குச் செல்"</string>
- <string name="selectContact" msgid="92191462970821951">"தொடர்பைத் தேர்ந்தெடுக்கவும்"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"சொந்தமாக எழுதவும்..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"ரத்துசெய்"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"அனுப்பு"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"பதிலளி"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS அனுப்பு"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"நிராகரி"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"வீடியோ அழைப்பில் பதிலளி"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"ஆடியோ அழைப்பில் பதிலளி"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"வீடியோ கோரிக்கையை அனுமதி"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"வீடியோ கோரிக்கையை நிராகரி"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"வீடியோவைப் பரிமாற்றும் கோரிக்கையை அனுமதி"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"வீடியோவைப் பரிமாற்றும் கோரிக்கையை நிராகரி"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"வீடியோவைப் பெறும் கோரிக்கையை அனுமதி"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"வீடியோவைப் பெறும் கோரிக்கையை நிராகரி"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, மேலே ஸ்லைடு செய்க."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, இடப்புறம் ஸ்லைடு செய்க."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, வலப்புறம் ஸ்லைடு செய்க."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>, கீழே ஸ்லைடு செய்க."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"அதிர்வுறு"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"அதிர்வுறு"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"ஒலி"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"இயல்பு ஒலி (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"ஃபோனின் ரிங்டோன்"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"அழைக்கும் போது அதிர்வுறு"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"ரிங்டோன் &amp; அதிர்வு"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"குழு அழைப்பை நிர்வகி"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"அவசர அழைப்பு எண்"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"சுயவிவரப் படம்"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"கேமரா முடக்கப்பட்டது"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> வழியாக"</string>
- <string name="note_sent" msgid="7623014827902758398">"குறிப்பு அனுப்பப்பட்டது"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"சமீபத்திய செய்திகள்"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"வணிகத் தகவல்"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> மைல் தொலைவில்"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> கிமீ தொலைவில்"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"நாளை <xliff:g id="OPEN_TIME">%s</xliff:g>க்குத் திறக்கப்படும்"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"இன்று <xliff:g id="OPEN_TIME">%s</xliff:g>க்குத் திறக்கப்படும்"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g>க்கு மூடப்படும்"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"இன்று <xliff:g id="CLOSE_TIME">%s</xliff:g>க்கு மூடப்பட்டது"</string>
- <string name="open_now" msgid="4615706338669555999">"இப்போது திறக்கப்பட்டுள்ளது"</string>
- <string name="closed_now" msgid="2635314668145282080">"இப்போது மூடப்பட்டுள்ளது"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"சந்தேகத்திற்குரிய ஸ்பேம் அழைப்பாளர்"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"அழைப்பு துண்டிக்கப்பட்டது %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"இந்த எண்ணிலிருந்து உங்களுக்கு அழைப்பு வந்தது இதுவே முதல் முறை."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"இந்த அழைப்பு ஸ்பேமராக இருக்கக்கூடும் என சந்தேகிக்கிறோம்."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"தடு/ஸ்பேமென புகாரளி"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"தொடர்பைச் சேர்"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"ஸ்பேமில்லை"</string>
-</resources>
diff --git a/InCallUI/res/values-te/strings.xml b/InCallUI/res/values-te/strings.xml
deleted file mode 100644
index 936f1be7c..000000000
--- a/InCallUI/res/values-te/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"ఫోన్"</string>
- <string name="onHold" msgid="527593602772521700">"హోల్డ్‌లో ఉంది"</string>
- <string name="unknown" msgid="3646075119047488748">"తెలియదు"</string>
- <string name="private_num" msgid="6081418498487514686">"ప్రైవేట్ నంబర్"</string>
- <string name="payphone" msgid="5743050584468748607">"పే ఫోన్"</string>
- <string name="confCall" msgid="3181961445236675173">"కాన్ఫరెన్స్ కాల్"</string>
- <string name="call_lost" msgid="8208184291640961172">"కాల్ కట్ అయింది"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"స్పీకర్"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"హ్యాండ్‌సెట్ ఇయర్‌పీస్"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"వైర్ గల హెడ్‌సెట్"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"బ్లూటూత్"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"కింది టోన్‌లను పంపాలా?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"టోన్‌లు పంపుతోంది\n"</string>
- <string name="send_button" msgid="4054398309483035794">"పంపు"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"అవును"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"వద్దు"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"దీనితో వైల్డ అక్షరాన్ని భర్తీ చేయండి"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"కాన్ఫరెన్స్ కాల్ <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"వాయిస్ మెయిల్ నంబర్"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"డయల్ చేస్తోంది"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"మళ్లీ డయల్ చేస్తోంది"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"కాన్ఫరెన్స్ కాల్"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"ఇన్‌కమింగ్ కాల్"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"ఇన్‌కమింగ్ కార్యాలయ కాల్"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"కాల్ ముగిసింది"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"హోల్డ్‌లో ఉంది"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"ముగిస్తోంది"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"కాల్‌లో ఉంది"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"నా నంబర్ <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"వీడియోను కనెక్ట్ చేస్తోంది"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"వీడియో కాల్"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"వీడియో కోసం అభ్యర్థిస్తోంది"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"వీడియో కాల్‌ను కనెక్ట్ చేయలేరు"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"వీడియో అభ్యర్థన తిరస్కరించబడింది"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"మీ కాల్‌బ్యాక్ నంబర్\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"మీ అత్యవసర కాల్‌బ్యాక్ నంబర్\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"డయల్ చేస్తోంది"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"సమాధానం ఇవ్వని కాల్"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"సమాధానం ఇవ్వని కాల్‌లు"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> సమాధానం ఇవ్వని కాల్‌లు"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> నుండి సమాధానం ఇవ్వని కాల్"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"కాల్ కొనసాగుతోంది"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"కార్యాలయ కాల్ కొనసాగుతోంది"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Wi-Fi కాల్ కొనసాగుతోంది"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Wi-Fi కార్యాలయ కాల్ కొనసాగుతోంది"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"హోల్డ్‌లో ఉంది"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"ఇన్‌కమింగ్ కాల్"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"ఇన్‌కమింగ్ కార్యాలయ కాల్"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"ఇన్‌కమింగ్ Wi-Fi కాల్"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"ఇన్‌కమింగ్ Wi-Fi కార్యాలయ కాల్"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"ఇన్‌కమింగ్ వీడియో కాల్"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"అనుమానాస్పద స్పామ్ కాల్ వస్తోంది"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"ఇన్‌కమింగ్ వీడియో అభ్యర్థన"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"కొత్త వాయిస్ మెయిల్"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"కొత్త వాయిస్ మెయిల్ (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>కు డయల్ చేయండి"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"వాయిస్ మెయిల్ నంబర్ తెలియదు"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"సేవ లేదు"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"ఎంచుకున్న నెట్‌వర్క్ (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) అందుబాటులో లేదు"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"సమాధానం"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"కాల్ ముగించు"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"వీడియో"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"వాయిస్"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"ఆమోదిస్తున్నాను"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"తీసివేయి"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"తిరిగి కాల్ చేయి"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"సందేశం పంపు"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"మరో పరికరంలో కాల్ జరుగుతోంది"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"కాల్‌ను బదిలీ చేయి"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"కాల్ చేయడానికి, ముందుగా ఎయిర్‌ప్లైన్ మోడ్‌ను ఆపివేయండి."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"నెట్‌వర్క్‌లో నమోదు కాలేదు."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"సెల్యులార్ నెట్‌వర్క్ అందుబాటులో లేదు."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"కాల్ చేయడానికి, చెల్లుబాటు అయ్యే నంబర్‌ను నమోదు చేయండి."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"కాల్ చేయలేరు."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI శ్రేణిని ప్రారంభిస్తోంది…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"సేవకు మద్దతు లేదు."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"కాల్‌లను మార్చలేరు."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"కాల్‌ను వేరు చేయలేరు."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"బదిలీ చేయలేరు."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"కాన్ఫరెన్స్ కాల్ కుదరదు."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"కాల్‌ను తిరస్కరించలేరు."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"కాల్(ల)ను విడిచిపెట్టలేరు."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP కాల్"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"అత్యవసర కాల్"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"రేడియోను ఆన్ చేస్తోంది…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"సేవ లేదు. మళ్లీ ప్రయత్నిస్తోంది…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"కాల్ చేయలేరు. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> అత్యవసర నంబర్ కాదు."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"కాల్ చేయలేరు. అత్యవసర నంబర్‌కు డయల్ చేయండి."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"డయల్ చేయడానికి కీబోర్డ్‌ను ఉపయోగించండి"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"కాల్‌ను హోల్డ్‌లో ఉంచు"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"కాల్‌ను పునఃప్రారంభించు"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"కాల్‌ని ముగించు"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"డయల్‌ప్యాడ్‌ను చూపు"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"డయల్‌ప్యాడ్‌ను దాచు"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"మ్యూట్ చేయి"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"అన్‌మ్యూట్ చేయి"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"కాల్‌ను జోడించు"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"కాల్‌లను విలీనం చేయి"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"స్వాప్ చేయి"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"కాల్‌లను నిర్వహించు"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"కాన్ఫరెన్స్ కాల్‌ను నిర్వహించు"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"కాన్ఫరెన్స్ కాల్"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"నిర్వహించు"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"ఆడియో"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"వీడియో కాల్"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"వాయిస్ కాల్‌కి మార్చు"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"కెమెరాను మార్చు"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"కెమెరాను ఆన్ చేయి"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"కెమెరాను ఆఫ్ చేయి"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"మరిన్ని ఎంపికలు"</string>
- <string name="player_started" msgid="3478865572468310331">"ప్లేయర్ ప్రారంభమైంది"</string>
- <string name="player_stopped" msgid="1278611664986561535">"ప్లేయర్ ఆపివేయబడింది"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"కెమెరా సిద్ధంగా లేదు"</string>
- <string name="camera_ready" msgid="2614541247814590887">"కెమెరా సిద్ధంగా ఉంది"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"తెలియని కాల్ సెషన్ ఉదంతం"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"సేవ"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"సెటప్ చేయండి"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;సెట్ చేయలేదు&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"ఇతర కాల్ సెట్టింగ్‌లు"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> ద్వారా కాల్ చేయబడుతోంది"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> ద్వారా ఇన్‌కమింగ్"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"పరిచయం ఫోటో"</string>
- <string name="goPrivate" msgid="3554069451018659483">"ప్రైవేట్‌గా వెళ్లు"</string>
- <string name="selectContact" msgid="92191462970821951">"పరిచయాన్ని ఎంచుకోండి"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"మీ స్వంతంగా వ్రాయండి…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"రద్దు చేయి"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"పంపు"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"సమాధానం"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMSని పంపుతుంది"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"తిరస్కరిస్తుంది"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"వీడియో కాల్ రూపంలో సమాధానం"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"ఆడియో కాల్ రూపంలో సమాధానం"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"వీడియో అభ్యర్థనను ఆమోదిస్తుంది"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"వీడియో అభ్యర్థనను తిరస్కరిస్తుంది"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"వీడియో ప్రసరణ అభ్యర్థనను ఆమోదిస్తుంది"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"వీడియో ప్రసరణ అభ్యర్థనను తిరస్కరిస్తుంది"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"వీడియో స్వీకరణ అభ్యర్థనను ఆమోదిస్తుంది"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"వీడియో స్వీకరణ అభ్యర్థనను తిరస్కరిస్తుంది"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> కోసం పైకి స్లైడ్ చేయండి."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> కోసం ఎడమవైపుకు స్లైడ్ చేయండి."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> కోసం కుడివైపుకు స్లైడ్ చేయండి."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> కోసం కిందికి స్లైడ్ చేయండి."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"వైబ్రేట్"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"వైబ్రేట్"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"ధ్వని"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"డిఫాల్ట్ ధ్వని (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"ఫోన్ రింగ్‌టోన్"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"రింగ్ అయ్యేప్పుడు వైబ్రేషన్"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"రింగ్‌టోన్ &amp; వైబ్రేట్"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"కాన్ఫరెన్స్ కాల్‌ను నిర్వహించు"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"అత్యవసర నంబర్"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"ప్రొఫైల్ ఫోటో"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"కెమెరా ఆఫ్‌లో ఉంది"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> ద్వారా"</string>
- <string name="note_sent" msgid="7623014827902758398">"గమనిక పంపబడింది"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"ఇటీవలి సందేశాలు"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"వ్యాపార సంస్థ సమాచారం"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> మై దూరంలో ఉంది"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> కి.మీ దూరంలో ఉంది"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"రేపు <xliff:g id="OPEN_TIME">%s</xliff:g>కి తెరవబడుతుంది"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"ఈరోజు <xliff:g id="OPEN_TIME">%s</xliff:g>కి తెరవబడుతుంది"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g>కి మూసివేయబడుతుంది"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"ఈరోజు <xliff:g id="CLOSE_TIME">%s</xliff:g>కి మూసివేయబడి ఉంటుంది"</string>
- <string name="open_now" msgid="4615706338669555999">"ఇప్పుడు తెరిచి ఉంది"</string>
- <string name="closed_now" msgid="2635314668145282080">"ఇప్పుడు మూసివేయబడింది"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"అనుమానిత స్పామ్ కాలర్"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"కాల్ ముగిసింది %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"ఈ నంబర్ నుండి మీకు కాల్ రావడం ఇదే మొదటిసారి."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"ఈ కాల్ స్పామర్ కావచ్చని మేము అనుమానిస్తున్నాము."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"బ్లాక్/స్పామ్ నివే."</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"పరిచయాన్ని జోడించు"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"స్పామ్ కాదు"</string>
-</resources>
diff --git a/InCallUI/res/values-th/strings.xml b/InCallUI/res/values-th/strings.xml
deleted file mode 100644
index 69ae44d07..000000000
--- a/InCallUI/res/values-th/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"โทรศัพท์"</string>
- <string name="onHold" msgid="527593602772521700">"พักสาย"</string>
- <string name="unknown" msgid="3646075119047488748">"ไม่ทราบ"</string>
- <string name="private_num" msgid="6081418498487514686">"หมายเลขส่วนตัว"</string>
- <string name="payphone" msgid="5743050584468748607">"โทรศัพท์สาธารณะ"</string>
- <string name="confCall" msgid="3181961445236675173">"การประชุมสาย"</string>
- <string name="call_lost" msgid="8208184291640961172">"สายหลุด"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"ลำโพง"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"ชุดหูฟังโทรศัพท์"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"ชุดหูฟังแบบมีสาย"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"บลูทูธ"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"ส่งหมายเลขต่อไปไหม\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"กำลังส่งหมายเลข\n"</string>
- <string name="send_button" msgid="4054398309483035794">"ส่ง"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"ใช่"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"ไม่"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"แทนที่อักขระแทนด้วย"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"การประชุมสาย <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"หมายเลขข้อความเสียง"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"กำลังโทรออก"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"โทรใหม่"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"การประชุมสาย"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"สายเรียกเข้า"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"มีสายเรียกเข้าจากที่ทำงาน"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"วางสายแล้ว"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"พักสาย"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"กำลังวางสาย"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"กำลังใช้สาย"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"หมายเลขของฉันคือ <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"กำลังเชื่อมต่อวิดีโอ"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"แฮงเอาท์วิดีโอ"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"กำลังขอวิดีโอ"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"ไม่สามารถเชื่อมต่อแฮงเอาท์วิดีโอได้"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"คำขอวิดีโอถูกปฏิเสธ"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"หมายเลขโทรกลับของคุณ\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"หมายเลขโทรกลับฉุกเฉินของคุณ\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"กำลังโทรออก"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"สายที่ไม่ได้รับ"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"สายที่ไม่ได้รับ"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"ไม่ได้รับ <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> สาย"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"สายที่ไม่ได้รับจาก <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"โทรต่อเนื่อง"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"กำลังอยู่ในสายจากที่ทำงาน"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"กำลังโทรผ่าน Wi-Fi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"กำลังอยู่ในสายจากที่ทำงานผ่าน Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"พักสาย"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"สายเรียกเข้า"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"มีสายเรียกเข้าจากที่ทำงาน"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"สายโทรเข้าผ่าน Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"มีสายเรียกเข้าจากที่ทำงานผ่าน Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"แฮงเอาท์วิดีโอเรียกเข้า"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"สายเรียกเข้าที่สงสัยว่าเป็นสแปม"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"คำขอโทรเข้าเป็นวิดีโอ"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"ข้อความเสียงใหม่"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"ข้อความเสียงใหม่ (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"หมุนหมายเลข <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"ไม่ทราบหมายเลขข้อความเสียง"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"ไม่มีบริการ"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"เครือข่ายที่เลือกไว้ (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) ไม่พร้อมใช้งาน"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"รับสาย"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"วางสาย"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"วิดีโอ"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"เสียง"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"ยอมรับ"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"ปิด"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"โทรกลับ"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"ข้อความ"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"กำลังใช้สายบนอุปกรณ์อื่น"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"โอนสาย"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"หากต้องการโทรออก ให้ปิดโหมดบนเครื่องบินก่อน"</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"ยังไม่ได้ลงทะเบียนบนเครือข่าย"</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"เครือข่ายมือถือใช้งานไม่ได้"</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"หากต้องการโทรออก โปรดป้อนหมายเลขที่ถูกต้อง"</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"ไม่สามารถโทรได้"</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"กำลังเริ่มต้นลำดับ MMI..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"ไม่สนับสนุนบริการนี้"</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"ไม่สามารถสลับสายได้"</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"ไม่สามารถแยกสายได้"</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"ไม่สามารถโอนได้"</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"ไม่สามารถประชุมได้"</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"ไม่สามารถปฏิเสธสายได้"</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"ไม่สามารถเริ่มการโทรได้"</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"โทรแบบ SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"หมายเลขฉุกเฉิน"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"กำลังเปิดวิทยุ…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"ไม่มีบริการ โปรดลองอีกครั้ง…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"โทรออกไม่ได้ <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> ไม่ใช่หมายเลขฉุกเฉิน"</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"ไม่สามารถโทรออก โทรหมายเลขฉุกเฉิน"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"ใช้แป้นพิมพ์กดหมายเลขโทรศัพท์"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"พักสาย"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"โทรต่อ"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"วางสาย"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"แสดงแป้นหมายเลข"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"ซ่อนแป้นหมายเลข"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"ปิดเสียง"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"เปิดเสียง"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"เพิ่มการโทร"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"รวมสาย"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"สลับ"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"จัดการการโทร"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"จัดการการประชุมสาย"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"การประชุมสาย"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"จัดการ"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"เสียง"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"แฮงเอาท์วิดีโอ"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"เปลี่ยนเป็นการโทรด้วยเสียง"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"เปลี่ยนกล้อง"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"เปิดกล้อง"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"ปิดกล้อง"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"ตัวเลือกเพิ่มเติม"</string>
- <string name="player_started" msgid="3478865572468310331">"โปรแกรมเล่นเริ่มทำงานแล้ว"</string>
- <string name="player_stopped" msgid="1278611664986561535">"โปรแกรมเล่นหยุดแล้ว"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"กล้องไม่พร้อมทำงาน"</string>
- <string name="camera_ready" msgid="2614541247814590887">"กล้องพร้อมทำงาน"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"เหตุการณ์เซสชันการโทรที่ไม่รู้จัก"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"บริการ"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"ตั้งค่า"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;ไม่ได้ตั้งค่า&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"การตั้งค่าการโทรอื่นๆ"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"โทรผ่าน <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"สายเรียกเข้าผ่าน <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"ภาพของรายชื่อติดต่อ"</string>
- <string name="goPrivate" msgid="3554069451018659483">"เข้าสู่โหมดส่วนตัว"</string>
- <string name="selectContact" msgid="92191462970821951">"เลือกรายชื่อติดต่อ"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"เขียนคำตอบของคุณเอง..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"ยกเลิก"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"ส่ง"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"รับสาย"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"ส่ง SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"ปฏิเสธ"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"รับสายเป็นแฮงเอาท์วิดีโอ"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"รับสายเป็นการโทรด้วยเสียง"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"ยอมรับคำขอวิดีโอ"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"ปฏิเสธคำขอวิดีโอ"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"ยอมรับคำขอให้ส่งวิดีโอ"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"ปฏิเสธคำขอให้ส่งวิดีโอ"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"ยอมรับคำขอให้รับวิดีโอ"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"ปฏิเสธคำขอให้รับวิดีโอ"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"เลื่อนไปข้างบนเพื่อ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
- <string name="description_direction_left" msgid="6811598791620851239">"เลื่อนไปทางซ้ายเพื่อ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
- <string name="description_direction_right" msgid="5461971399586296023">"เลื่อนไปทางขวาเพื่อ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
- <string name="description_direction_down" msgid="3331715227997561639">"เลื่อนลงเพื่อ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"สั่น"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"สั่น"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"เสียง"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"เสียงเริ่มต้น (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"เสียงเรียกเข้าโทรศัพท์"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"สั่นเมื่อมีสายเข้า"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"เสียงเรียกเข้าและสั่น"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"จัดการการประชุมสาย"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"หมายเลขฉุกเฉิน"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"รูปโปรไฟล์"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"ปิดกล้อง"</string>
- <string name="child_number" msgid="4469090994612105532">"ผ่านหมายเลข <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"ส่งโน้ตแล้ว"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"ข้อความล่าสุด"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"ข้อมูลธุรกิจ"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"อยู่ห่างออกไป <xliff:g id="DISTANCE">%.1f</xliff:g> ไมล์"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"อยู่ห่างออกไป <xliff:g id="DISTANCE">%.1f</xliff:g> กม."</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"เปิดให้บริการพรุ่งนี้เวลา <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"เปิดให้บริการวันนี้เวลา <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"ปิดให้บริการเวลา <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"ปิดให้บริการแล้ววันนี้เวลา <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"ขณะนี้เปิดทำการ"</string>
- <string name="closed_now" msgid="2635314668145282080">"ขณะนี้ปิดทำการ"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"ผู้โทรที่สงสัยว่าเป็นสแปม"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"วางสายแล้ว %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"หมายเลขนี้โทรหาคุณเป็นครั้งแรก"</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"เราสงสัยว่าสายนี้จะเป็นนักส่งสแปม"</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"บล็อก/รายงานสแปม"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"เพิ่มผู้ติดต่อ"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"ไม่ใช่สแปม"</string>
-</resources>
diff --git a/InCallUI/res/values-tl/strings.xml b/InCallUI/res/values-tl/strings.xml
deleted file mode 100644
index b5658d705..000000000
--- a/InCallUI/res/values-tl/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telepono"</string>
- <string name="onHold" msgid="527593602772521700">"Naka-hold"</string>
- <string name="unknown" msgid="3646075119047488748">"Hindi alam"</string>
- <string name="private_num" msgid="6081418498487514686">"Pribadong numero"</string>
- <string name="payphone" msgid="5743050584468748607">"Payphone"</string>
- <string name="confCall" msgid="3181961445236675173">"Conference call"</string>
- <string name="call_lost" msgid="8208184291640961172">"Naputol ang tawag"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Speaker"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Handset earpiece"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Wired na headset"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Ipapadala ba ang mga sumusunod na tono?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Nagpapadala ng mga tono\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Ipadala"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Oo"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Hindi"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Palitan ang wild character ng"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Conference call <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Numero ng voicemail"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Dina-dial"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Muling dina-dial"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Conference call"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Papasok na tawag"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Papasok na tawag sa trabaho"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Ibinaba ang tawag"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Naka-hold"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Binababa"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Nasa tawag"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Ang aking numero ay <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Ikinokonekta ang video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Mag-video call"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Humihiling ng video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Hindi makakonekta sa video call"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Tinanggihan ang kahilingan sa video"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Ang iyong numero ng callback\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Ang iyong emergency na numero ng callback\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Dina-dial"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Hindi nasagot na tawag"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Mga hindi nasagot na tawag"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> (na) hindi nasagot na tawag"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Hindi nasagot ang tawag mula kay <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Kasalukuyang tawag"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Kasalukuyang tawag sa trabaho"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Kasalukuyang tawag sa Wi-Fi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Kasalukuyang tawag sa trabaho sa pamamagitan ng Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Naka-hold"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Papasok na tawag"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Papasok na tawag sa trabaho"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Papasok na tawag sa Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Papasok na tawag sa trabaho sa pamamagitan ng Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Papasok na video call"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Papasok na pinaghihinalaang spam na tawag"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Papasok na kahilingan ng video"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Bagong voicemail"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Bagong voicemail (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"I-dial ang <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Hindi kilala ang numero ng voicemail"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Walang serbisyo"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Hindi available ang piniling network (<xliff:g id="OPERATOR_NAME">%s</xliff:g>)"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Sagutin"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Ibaba"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Boses"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Tanggapin"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"I-dismiss"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Tawagan"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Mensahe"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Kasalukuyang tawag sa isa pang device"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Ilipat ang Tawag"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Upang tumawag, paki-off muna ang Airplane mode."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Hindi nakarehistro sa network."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Hindi available ang cellular network."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Upang tumawag, maglagay ng wastong numero."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Hindi makatawag."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Sinisimulan ang pagkakasunud-sunod ng MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Hindi sinusuportahan ang serbisyo."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Hindi mailipat ang mga tawag."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Hindi mapaghiwalay ang tawag."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Hindi mailipat."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Hindi makapag-conference."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Hindi matanggihan ang tawag."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Hindi mailabas ang (mga) tawag."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Tawag sa SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Emergency na tawag"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Ino-on ang radyo…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Walang serbisyo. Sinusubukang muli…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Hindi makatawag. Ang <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> ay hindi isang pang-emergency na numero."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Hindi makatawag. Mag-dial ng pang-emergency na numero."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Gamitin ang keyboard upang mag-dial"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"I-hold ang Tawag"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Ituloy ang Tawag"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Ibaba ang Tawag"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Ipakita ang Dialpad"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Itago ang Dialpad"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"I-mute"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Alisin sa pagkaka-mute"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Magdagdag ng tawag"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Pagsamahin ang mga tawag"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Pagpalitin"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Pamahalaan ang mga tawag"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Pamahalaan ang conference call"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Conference call"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Pamahalaan"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Mag-video call"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Gawing voice call"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Lumipat ng camera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"I-on ang camera"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"I-off ang camera"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Higit pang mga opsyon"</string>
- <string name="player_started" msgid="3478865572468310331">"Nagsimula na ang Player"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Huminto ang Player"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Hindi pa handa ang camera"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Handa na ang camera"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Hindi alam na kaganapan ng session ng tawag"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Serbisyo"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"I-setup"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Hindi nakatakda&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Iba pang mga setting ng tawag"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Tumatawag sa pamamagitan ng <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Papasok sa pamamagitan ng <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"larawan ng contact"</string>
- <string name="goPrivate" msgid="3554069451018659483">"maging pribado"</string>
- <string name="selectContact" msgid="92191462970821951">"pumili ng contact"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Sumulat ng sarili mong tugon…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Kanselahin"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Ipadala"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Sagutin"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Magpadala ng SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Tanggihan"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Sagutin bilang video call"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Sagutin bilang audio call"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Tanggapin ang kahilingan sa video"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Tanggihan ang kahilingan sa video"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Tanggapin ang kahilingan sa pagpapadala ng video"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Tanggihan ang kahilingan sa pagpapadala ng video"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Tanggapin ang kahilingan sa pagtanggap ng video"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Tanggihan ang kahilingan sa pagtanggap ng video"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Mag-slide pataas para sa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Mag-slide pakaliwa para sa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Mag-slide pakanan para sa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Mag-slide pababa para sa <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Mag-vibrate"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Mag-vibrate"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Tunog"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Default na tunog (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Ringtone ng telepono"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Mag-vibrate kapag nagri-ring"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Ringtone at Pag-vibrate"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Pamahalaan ang conference call"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Pang-emergency na numero"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Larawan sa profile"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Naka-off ang camera"</string>
- <string name="child_number" msgid="4469090994612105532">"sa pamamagitan ng <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Naipadala ang tala"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Mga kamakailang mensahe"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Impormasyon ng negosyo"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> (na) milya ang layo"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> (na) kilometro ang layo"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Magbubukas bukas nang <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Magbubukas ngayon nang <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Magsasara nang <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Sarado ngayon nang <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Bukas ngayon"</string>
- <string name="closed_now" msgid="2635314668145282080">"Sarado ngayon"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Hinihinalang spam na tumatawag"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Natapos ang tawag %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Ito ang unang beses na tinawagan ka ng numerong ito."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Pinaghihinalaan naming isang spammer ang tawag na ito."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"I-block/iulat na spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Magdagdag ng contact"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Hindi spam"</string>
-</resources>
diff --git a/InCallUI/res/values-tr/strings.xml b/InCallUI/res/values-tr/strings.xml
deleted file mode 100644
index 405bab8db..000000000
--- a/InCallUI/res/values-tr/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"Beklemede"</string>
- <string name="unknown" msgid="3646075119047488748">"Bilinmiyor"</string>
- <string name="private_num" msgid="6081418498487514686">"Gizli numara"</string>
- <string name="payphone" msgid="5743050584468748607">"Ankesörlü telefon"</string>
- <string name="confCall" msgid="3181961445236675173">"Konferans görüşmesi"</string>
- <string name="call_lost" msgid="8208184291640961172">"Çağrı kesildi"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Hoparlör"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Mobil cihaz kulaklığı"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Kablolu kulaklık"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Aşağıdaki zil sesleri gönderilsin mi?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Numara tonları gönderiliyor\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Gönder"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Evet"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Hayır"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Joker karakteri şununla değiştir:"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konferans görüşmesi <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Sesli mesaj numarası"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Numara çevriliyor"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Yeniden çevriliyor"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konferans görüşmesi"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Gelen çağrı"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"İşle ilgili gelen çağrı"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Çağrı sonlandırıldı"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Beklemede"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Sonlandırılıyor"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Görüşmede"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Numaram: <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Video bağlanıyor"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Video görüşmesi"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Video isteniyor"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Video görüşmesi bağlantısı yapılamıyor"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Video isteği reddedildi"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Geri aranacağınız numara\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Acil durumda geri aranacağınız numara\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Numara çevriliyor"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Cevapsız çağrı"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Cevapsız çağrılar"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> cevapsız çağrı"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Cevapsız çağrı: <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Devam eden çağrı"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"İşle ilgili devam eden çağrı"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Devam eden kablosuz çağrı"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"İşle ilgili devam eden kablosuz çağrı"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Beklemede"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Gelen çağrı"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"İşle ilgili gelen çağrı"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Gelen kablosuz çağrı"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"İşle ilgili gelen kablosuz çağrı"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Gelen video görüşmesi isteği"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Spam olabilecek gelen arama"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Gelen video isteği"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Yeni sesli mesaj"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Yeni sesli mesaj (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Çevir: <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Sesli mesaj numarası bilinmiyor"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Servis yok"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Seçili ağ (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) kullanılamıyor"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Yanıtla"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Kapat"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Ses"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Kabul et"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Yok say"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Geri ara"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"İleti"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Başka bir cihazda devam eden çağrı"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Çağrıyı Aktar"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Çağrı yapmak için öncelikle Uçak modunu kapatın."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Ağda kayıtlı değil."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Hücresel ağ kullanılamıyor."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Çağrı yapmak için geçerli bir numara girin."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Çağrı yapılamıyor."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI dizisi başlatılıyor…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Service desteklenmiyor"</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Çağrı geçişi yapılamıyor."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Çağrı ayrılamıyor."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Aktarılamıyor."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Konferans çağrısı yapılamıyor."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Çağrı reddedilemiyor."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Çağrılar bırakılamıyor."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP çağrısı"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Acil durum çağrısı"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Radyo açılıyor…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Servis yok. Tekrar deneniyor…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Çağrı yapılamıyor. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> bir acil durum numarası değil."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Çağrı yapılamıyor. Acil durum numarasını çevirin."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Çevirmek için klavyeyi kullan"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Çağrıyı Beklet"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Çağrıyı Devam Ettir"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Çağrıyı Sonlandır"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Tuş Takımını Göster"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Tuş Takımını Gizle"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Sesi kapat"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Sesi aç"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Çağrı ekle"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Çağrıları birleştir"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Değiştir"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Çağrıları yönet"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Konferans çağrısını yönet"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferans çağrısı"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Yönet"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Ses"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Vid. görşm"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Sesli çağrıya geç"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Kamerayı değiştir"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Kamerayı aç"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Kamerayı kapat"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Diğer seçenekler"</string>
- <string name="player_started" msgid="3478865572468310331">"Oynatıcı Başlatıldı"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Oynatıcı Durduruldu"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kamera hazır değil"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kamera hazır"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Bilinmeyen çağrı oturumu etkinliği"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Hizmet"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Kurulum"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Ayarlanmadı&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Diğer çağrı ayarları"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> üzerinden çağrı yapılıyor"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> adlı sağlayıcı üzerinden gelen çağrı"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"kişi fotoğrafı"</string>
- <string name="goPrivate" msgid="3554069451018659483">"özel görüşmeye geç"</string>
- <string name="selectContact" msgid="92191462970821951">"kişi seçin"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Kendi yanıtınızı oluşturun…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"İptal"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Gönder"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Yanıtla"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS gönder"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Reddet"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Video görüşmesi olarak yanıtla"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Sesli görüşme olarak yanıtla"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Video isteğini kabul et"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Video isteğini reddet"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Video aktarma isteğini kabul et"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Video aktarma isteğini reddet"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Video alma isteğini kabul et"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Video alma isteğini reddet"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> için yukarı kaydırın."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> için sola kaydırın."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> için sağa kaydırın."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> için aşağı kaydırın."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Titreşim"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Titreşim"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Ses"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Varsayılan ses (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Telefon zil sesi"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Çalarken titret"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Zil Sesi ve Titreşim"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Konferans çağrısını yönetin"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Acil durum numarası"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profil fotoğrafı"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera kapalı"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> üzerinden"</string>
- <string name="note_sent" msgid="7623014827902758398">"Not gönderildi"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Son iletiler"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"İşletme bilgileri"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> mil uzakta"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km uzakta"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Yarın açılış saati: <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Bugün açılış saati: <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Kapanış saati: <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Bugün kapanış saati: <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Şu an açık"</string>
- <string name="closed_now" msgid="2635314668145282080">"Şu an kapalı"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Spam olabilck arayan"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Arama sona erdi %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Bu, bu numaradan ilk aranışınız."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Bu aramanın spam olduğundan şüpheleniliyor."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Engelle/spam bildir"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Kişi ekle"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Spam değil"</string>
-</resources>
diff --git a/InCallUI/res/values-uk/strings.xml b/InCallUI/res/values-uk/strings.xml
deleted file mode 100644
index b323acd04..000000000
--- a/InCallUI/res/values-uk/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Номер телефону"</string>
- <string name="onHold" msgid="527593602772521700">"Очікування"</string>
- <string name="unknown" msgid="3646075119047488748">"Невідомо"</string>
- <string name="private_num" msgid="6081418498487514686">"Приватний номер"</string>
- <string name="payphone" msgid="5743050584468748607">"Таксофон"</string>
- <string name="confCall" msgid="3181961445236675173">"Конференц-зв’язок"</string>
- <string name="call_lost" msgid="8208184291640961172">"Виклик перервано"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Динамік"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Динамік гарнітури"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Дротова гарнітура"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Надіслати вказані нижче сигнали?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Надсилання сигналів\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Надіслати"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Так"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Ні"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Замінити довільний символ на"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Конференц-зв’язок <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Номер голосової пошти"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Набір номера"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Повторний набір"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Конференц-зв’язок"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Вхідний виклик"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Вхідний робочий виклик"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Виклик завершено"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Очікування"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Завершення виклику"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Триває виклик"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Мій номер: <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Відеодзвінок: з’єднання"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Відеодзвінок"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Надсилання запиту на відеодзвінок"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Не вдалося здійснити відеодзвінок"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Запрошення на відеодзвінок відхилено"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Номер для зв’язку:\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Екстрений номер:\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Набір номера"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Пропущений виклик"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Пропущені виклики"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"Пропущено викликів: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Пропущений виклик: <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Поточний виклик"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Поточний виклик на робочий телефон"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Поточний виклик через Wi-Fi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Поточний виклик на робочий телефон через Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Очікування"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Вхідний виклик"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Вхідний виклик на робочий телефон"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Вхідний виклик через Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Вхідний виклик на робочий телефон через Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Вхідний відеодзвінок"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Цей дзвінок може бути спамом"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Запит на вхідний відеодзвінок"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Нові голосові повідомлення"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Нові голосові повідомлення (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Набрати <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Невідомий номер голосової пошти"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Немає зв’язку"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Вибрана мережа (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) недоступна"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Відповісти"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Завершити"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Відео"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Гол. виклик"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Прийняти"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Відхилити"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Передзвонити"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Написати SMS"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Поточний виклик на іншому пристрої"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Передати виклик"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Щоб зателефонувати, вимкніть режим польоту."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Не зареєстровано в мережі."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Мобільна мережа недоступна."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Щоб зателефонувати, введіть дійсний номер."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Не вдається зателефонувати."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Запуск ряду MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Служба не підтримується."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Неможливо переключитися між викликами."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Неможливо розділити виклик."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Неможливо перенести."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Конференц-зв’язок недоступний."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Неможливо відхилити виклик."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Неможливо телефонувати."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Виклик через протокол SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Екстрений виклик"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Увімкнення радіо…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Немає зв’язку. Повторна спроба…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Не вдається зателефонувати. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> не є екстреним номером."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Не вдається зателефонувати. Наберіть екстрений номер."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Використовуйте для набору клавіатуру"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Призупинити виклик"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Відновити виклик"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Завершити виклик"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Показати цифрову клавіатуру"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Сховати цифрову клавіатуру"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Ігнорувати"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Не ігнорувати"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Додати виклик"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Об’єднати виклики"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Поміняти виклики"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Керувати викликами"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Керувати конференц-зв’язком"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Конференц-зв’язок"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Керувати"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Аудіо"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Відеодзвінок"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Перейти в режим голосового виклику"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Вибрати камеру"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Увімкнути камеру"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Вимкнути камеру"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Інші опції"</string>
- <string name="player_started" msgid="3478865572468310331">"Програвач запущено"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Програвач зупинено"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Камера неготова"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Камера готова"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Невідомий сеанс виклику"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Служба"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Налаштування"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Не налаштовано&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Інші налаштування виклику"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Виклик здійснюється через оператора <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Вхідні виклики через оператора <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"фото контакта"</string>
- <string name="goPrivate" msgid="3554069451018659483">"приватна розмова"</string>
- <string name="selectContact" msgid="92191462970821951">"вибрати контакт"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Напишіть власну відповідь…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Скасувати"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Надіслати"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Відповісти"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Надіслати SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Відхилити"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Відповісти в режимі відеодзвінка"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Відповісти в режимі аудіодзвінка"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Прийняти запит на відео"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Відхилити запит на відео"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Прийняти запит на передавання відео"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Відхилити запит на передавання відео"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Прийняти запит на отримання відео"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Відхилити запит на отримання відео"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Проведіть пальцем угору, щоб <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Проведіть пальцем ліворуч, щоб <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Проведіть пальцем праворуч, щоб <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Проведіть пальцем донизу, щоб <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Вібросигнал"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Вібросигнал"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Звук"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Звук за умовчанням (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Сигнал дзвінка телефона"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Вібрувати під час виклику"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Сигнал дзвінка та вібросигнал"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Керування конференц-зв’язком"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Екстрений номер"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Фотографія профілю"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Камеру вимкнено"</string>
- <string name="child_number" msgid="4469090994612105532">"на номер <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Нотатку надіслано"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Нещодавні повідомлення"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Інформація про компанію"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"За <xliff:g id="DISTANCE">%.1f</xliff:g> мил."</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"За <xliff:g id="DISTANCE">%.1f</xliff:g> км"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g>–<xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Відчиняється завтра о <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Відчиняється сьогодні о <xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Зачиняється о <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Зачинено сьогодні о <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Відчинено"</string>
- <string name="closed_now" msgid="2635314668145282080">"Зачинено"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Може бути спамом"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Дзвінок завершено %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"З цього номера вам телефонують уперше."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Ми підозрюємо, що телефонував спамер."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Заблокувати/це спам"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Додати контакт"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Не спам"</string>
-</resources>
diff --git a/InCallUI/res/values-ur/strings.xml b/InCallUI/res/values-ur/strings.xml
deleted file mode 100644
index 32e0d45aa..000000000
--- a/InCallUI/res/values-ur/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"فون"</string>
- <string name="onHold" msgid="527593602772521700">"ہولڈ پر ہے"</string>
- <string name="unknown" msgid="3646075119047488748">"نامعلوم"</string>
- <string name="private_num" msgid="6081418498487514686">"نجی نمبر"</string>
- <string name="payphone" msgid="5743050584468748607">"پے فون"</string>
- <string name="confCall" msgid="3181961445236675173">"کانفرنس کال"</string>
- <string name="call_lost" msgid="8208184291640961172">"کال ختم ہو گئی"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"اسپیکر"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"ہینڈسیٹ ایئرپیس"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"تار والا ہیڈسیٹ"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"بلوٹوتھ"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"درج ذیل ٹونز بھیجیں؟\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"ٹونز بھیج رہا ہے\n"</string>
- <string name="send_button" msgid="4054398309483035794">"بھیجیں"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"ہاں"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"نہیں"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"وائلڈ کریکٹر کو اس کے ساتھ بدلیں"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"کانفرنس کال <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"صوتی میل نمبر"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"ڈائل ہو رہا ہے"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"دوبارہ ڈائل ہو رہا ہے"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"کانفرنس کال"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"آنے والی کال"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"کام سے متعلق آنے والی کال"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"کال ختم ہوگئی"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"ہولڈ پر ہے"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"کال منقطع ہو رہی ہے"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"کال میں"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"میرا نمبر ہے <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"ویڈیو منسلک ہو رہی ہے"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"ویڈیو کال"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"ویڈیو کی درخواست کی جا رہی ہے"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"ویڈیو کال منسلک نہیں ہو سکتی"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"ویڈیو کی درخواست مسترد ہو گئی"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"‏آپ کا کال بیک نمبر‎\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"‏آپ کا ہنگامی کال بیک نمبر‎\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"ڈائل ہو رہا ہے"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"چھوٹی ہوئی کال"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"چھوٹی ہوئی کالیں"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> چھوٹی ہوئی کالیں"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> کی جانب سے چھوٹی ہوئی کال"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"جاری کال"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"کام سے متعلق جاری کال"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"‏Wi-Fi کال جاری ہے"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"‏کام سے متعلق جاری Wi-Fi کال"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"ہولڈ پر ہے"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"آنے والی کال"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"کام سے متعلق آنے والی کال"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"‏آنے والی Wi-Fi کال"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"‏کام سے متعلق آنے والی Wi-Fi کال"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"آنے والی ویڈیو کال"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"آنے والی مشتبہ سپام کال"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"آنے والی ویڈیو کی درخواست"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"نیا صوتی میل"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"نیا صوتی میل (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> ڈائل کریں"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"صوتی میل نمبر نامعلوم ہے"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"کوئی سروس نہیں ہے"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"منتخب کردہ نیٹ ورک (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) دستیاب نہیں ہے"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"جواب"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"کال منقطع کریں"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"ویڈیو"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"آواز"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"قبول کریں"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"برخاست کریں"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"واپس کال کریں"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"پیغام"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"ایک اور آلے پر جاری کال"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"کال منتقل کریں"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"کال کرنے کیلئے، پہلے ہوائی جہاز طرز کو آف کریں۔"</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"نیٹ ورک پر رجسٹرڈ نہیں ہے۔"</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"سیلولر نیٹ ورک دستیاب نہیں ہے۔"</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"کال کرنے کیلئے، ایک درست نمبر درج کریں۔"</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"کال نہیں ہو سکتی۔"</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"‏MMI ترتیب شروع ہو رہی ہے…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"سروس تعاون یافتہ نہیں ہے۔"</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"کالز سوئچ نہیں ہو سکتیں۔"</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"کال الگ نہیں ہو سکتی۔"</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"منتقل نہیں ہو سکتی۔"</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"کانفرنس نہیں ہو سکتی۔"</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"کال مسترد نہیں ہو سکتی۔"</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"کال(ز) ریلیز نہیں ہو سکتیں۔"</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"‏SIP کال"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"ہنگامی کال"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"ریڈیو آن ہو رہا ہے…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"کوئی سروس نہیں ہے۔ دوبارہ کوشش کی جا رہی ہے…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"کال نہیں کی جا سکتی۔ <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> ایک ہنگامی نمبر نہیں ہے۔"</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"کال نہیں کی جا سکتی۔ ایک ہنگامی نمبر ڈائل کریں۔"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"ڈائل کرنے کیلئے کی بورڈ استعمال کریں"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"کال کو ہولڈ کریں"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"کال کو دوبارہ شروع کریں"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"کال ختم کریں"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"ڈائل پیڈ دکھائیں"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"ڈائل پیڈ چھپائیں"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"خاموش کریں"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"آواز چالو کریں"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"کال شامل کریں"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"کالز کو ضم کریں"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"تبادلہ کریں"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"کالز کا نظم کریں"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"کانفرنس کال کا نظم کریں"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"کانفرنس کال"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"نظم کریں"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"آڈیو"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"ویڈیو کال"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"صوتی کال میں تبدیل کریں"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"کیمرا سوئچ کریں"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"کیمرا آن کریں"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"کیمرا آف کریں"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"مزید اختیارات"</string>
- <string name="player_started" msgid="3478865572468310331">"پلیئر شروع ہوگیا"</string>
- <string name="player_stopped" msgid="1278611664986561535">"پلیئر بند ہوگیا"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"کیمرا تیار نہیں ہے"</string>
- <string name="camera_ready" msgid="2614541247814590887">"کیمرا تیار ہے"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"نامعلوم کال سیشن ایونٹ"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"سروس"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"ترتیب دیں"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"‏‎&lt;سیٹ نہیں ہے&gt;‎"</string>
- <string name="other_settings" msgid="6699076019841942826">"کال کی دیگر ترتیبات"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"کالنگ بذریعہ <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> کے ذریعے آنے والی"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"رابطہ کی تصویر"</string>
- <string name="goPrivate" msgid="3554069451018659483">"نجی ہوجائیں"</string>
- <string name="selectContact" msgid="92191462970821951">"رابطہ منتخب کریں"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"اپنا ذاتی تحریر کریں…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"منسوخ کریں"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"بھیجیں"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"جواب دیں"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"‏SMS بھیجیں"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"مسترد کریں"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"ویڈیو کال کے بطور جواب دیں"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"آڈیو کال کے بطور جواب دیں"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"ویڈیو کی درخواست قبول کریں"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"ویڈیو کی درخواست مسترد کریں"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"ویڈیو منتقل کرنے کی درخواست قبول کریں"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"ویڈیو منتقل کرنے کی درخواست مسترد کریں"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"ویڈیو موصول کرنے کی درخواست قبول کریں"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"ویڈیو موصول کرنے کی درخواست مسترد کریں"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> کیلئے اوپر سلائیڈ کریں۔"</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> کیلئے بائیں سلائیڈ کریں۔"</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> کیلئے دائیں سلائیڈ کریں۔"</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> کیلئے نیچے سلائیڈ کریں۔"</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"ارتعاش"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"ارتعاش"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"آواز"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"ڈیفالٹ آواز (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"فون رِنگ ٹون"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"رِنگ کے وقت مرتعش کریں"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"رنگ ٹون اور ارتعاش"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"کانفرنس کال کا نظم کریں"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"ہنگامی نمبر"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"پروفائل کی تصویر"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"کیمرا آف ہے"</string>
- <string name="child_number" msgid="4469090994612105532">"بذریعہ <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"نوٹ بھیج دیا گیا"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"حالیہ پیغامات"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"کاروباری معلومات"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> میل دور"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> کلومیٹر دور"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>، <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>، <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"کل <xliff:g id="OPEN_TIME">%s</xliff:g> بجے کھلے گا"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"آج <xliff:g id="OPEN_TIME">%s</xliff:g> بجے کھلے گا"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g> بجے بند ہوگا"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"آج <xliff:g id="CLOSE_TIME">%s</xliff:g> بجے بند ہوا"</string>
- <string name="open_now" msgid="4615706338669555999">"ابھی کھلا ہے"</string>
- <string name="closed_now" msgid="2635314668145282080">"اب بند ہے"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"مشتبہ سپام کالر"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"‏کال ختم ہو گئی ‎%1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"اس نمبر سے پہلی بار آپ کو کال آئی ہے۔"</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"ہمیں شک ہے کہ یہ کال سپامر تھی۔"</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"مسدود کریں/سپام کی اطلاع دیں"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"رابطہ شامل کریں"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"سپام نہیں ہے"</string>
-</resources>
diff --git a/InCallUI/res/values-uz/strings.xml b/InCallUI/res/values-uz/strings.xml
deleted file mode 100644
index 15d534d25..000000000
--- a/InCallUI/res/values-uz/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Telefon"</string>
- <string name="onHold" msgid="527593602772521700">"Kutilmoqda"</string>
- <string name="unknown" msgid="3646075119047488748">"Noma’lum"</string>
- <string name="private_num" msgid="6081418498487514686">"Maxfiy raqam"</string>
- <string name="payphone" msgid="5743050584468748607">"Taksofon"</string>
- <string name="confCall" msgid="3181961445236675173">"Konferens-aloqa"</string>
- <string name="call_lost" msgid="8208184291640961172">"Chaqiruv uzilib qoldi"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Karnay"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Telefon quloqchini"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Simli garnitura"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Quyidagi tovush signallari yuborilsinmi?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Tovush signallari yuborilmoqda\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Yuborish"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Ha"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Yo‘q"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Universal belgini bunga almashtirish"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Konferens-aloqa: <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Ovozli pochta raqami"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Chaqiruv"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Qayta terilmoqda"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Konferens-aloqa"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Kiruvchi qo‘ng‘iroq"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Kiruvchi qo‘ng‘iroq (ish)"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Chaqiruv tugadi"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Kutilmoqda"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Suhbat tugatilmoqda"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Suhbat"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Mening raqamim – <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Videoga ulanmoqda"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Video qo‘ng‘iroq"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Video so‘ralmoqda"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Video qo‘ng‘iroqqa ulanib bo‘lmadi"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Video qo‘ng‘iroq so‘rovi rad etildi"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Teskari qo‘ng‘iroq raqamingiz\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Favqulodda holatlar uchun teskari qo‘ng‘iroq raqamingiz\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Chaqiruv"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Javobsiz qo‘ng‘iroq"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Javobsiz qo‘ng‘iroqlar"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ta javobsiz qo‘ng‘iroq"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> qo‘ng‘irog‘i javobsiz qoldirildi"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Joriy qo‘ng‘iroq"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Joriy qo‘ng‘iroq (ish)"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Joriy Wi-Fi qo‘ng‘iroq"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Joriy Wi-Fi qo‘ng‘iroq (ish)"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Kutilmoqda"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Kiruvchi qo‘ng‘iroq"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Kiruvchi qo‘ng‘iroq (ish)"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Kiruvchi Wi-Fi qo‘ng‘iroq"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Kiruvchi Wi-Fi qo‘ng‘iroq (ish)"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Kiruvchi video qo‘ng‘iroq"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Shubhali kiruvchi qo‘ng‘iroq"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Kiruvchi video qo‘ng‘iroq"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Yangi ovozli xabar"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Yangi ovozli xabar (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g> raqamini terish"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Ovozli pochta raqami noma’lum"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Xizmat mavjud emas"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Tanlangan tarmoq (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) mavjud emas"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Javob berish"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Tugatish"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video aloqa"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Ovozli aloqa"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Qabul qilish"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Rad etish"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Telefon qilish"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"SMS yuborish"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Boshqa qurilmada hozir qo‘ng‘iroq amalga oshirilmoqda."</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Qo‘ng‘iroqni o‘tkazish"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Parvoz rejimini o‘chirib qo‘ying."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Tarmoqda ro‘yxatdan o‘tmagan."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Uyali tarmoq mavjud emas."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Raqam noto‘g‘ri."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Qo‘ng‘iroq qilib bo‘lmadi."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"MMI tartibi ishga tushmoqda…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Xizmat qo‘llab-quvvatlanmaydi."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Qo‘ng‘iroqlarni almashtirib bo‘lmadi."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Qo‘ng‘iroqni ajratib bo‘lmadi."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"O‘tkazib bo‘lmadi."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Konferens-aloqa o‘rnatib bo‘lmadi."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Qo‘ng‘iroqni rad qilib bo‘lmadi."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Qo‘ng‘iroq(lar)ni chiqarib bo‘lmadi."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP qo‘ng‘iroq"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Favqulodda chaqiruv"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Radio yoqilmoqda…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Aloqa yo‘q. Qayta urinilmoqda…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Qo‘ng‘iroq qilib bo‘lmadi. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> favqulodda raqam emas."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Qo‘ng‘iroq qilib bo‘lmadi. Favqulodda raqamga tering."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Terish uchun klaviaturadan foydalaning"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Qo‘ng‘iroqni ushlab turish"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Qo‘ng‘iroqni davom ettirish"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Chaqiruvni tugatish"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Raqam terish panelini ochish"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Raqam terish panelini yopish"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Ovozni o‘chirish"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Ovozni yoqish"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Chaqiruv qo‘shish"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Qo‘ng‘iroqlarni birlashtirish"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Almashtirish"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Qo‘ng‘iroqlarni boshqarish"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Konferens-aloqani sozlash"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Konferens-aloqa"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Boshqarish"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Video qo‘ng‘iroq"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Ovozli qo‘ng‘iroqqa o‘zgartirish"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Kamerani almashtirish"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Kamerani yoqish"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Kamerani o‘chirish"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Boshqa sozlamalar"</string>
- <string name="player_started" msgid="3478865572468310331">"Pleyer ishga tushirildi"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Pleyer to‘xtatildi"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Kamera tayyor emas"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Kamera tayyor"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Aloqa seansining noma’lum hodisasi"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Xizmat"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Sozlash"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Ko‘rsatilmagan&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Boshqa qo‘ng‘iroq sozlamalari"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> orqali qo‘ng‘rioq qilinmoqda"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> orqali kiruvchi qo‘ng‘iroqlar"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"kontakt rasmi"</string>
- <string name="goPrivate" msgid="3554069451018659483">"alohida suhbatga o‘tish"</string>
- <string name="selectContact" msgid="92191462970821951">"kontaktni tanlash"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"O‘z javobingizni yozing…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Bekor qilish"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Yuborish"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Javob berish"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"SMS yuborish"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Rad etish"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Video qo‘ng‘iroqqa javob berish"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Ovozli qo‘ng‘iroqqa javob berish"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Video qo‘ng‘iroq so‘rovini qabul qilish"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Video qo‘ng‘iroq so‘rovini rad etish"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Video uzatishga ruxsat berish"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Video uzatishga ruxsat bermaslik"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Kiruvchi video qo‘ng‘iroqni qabul qilish"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Kiruvchi video qo‘ng‘iroqni rad etish"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> uchun tepaga suring."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> uchun chapga suring."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> uchun o‘ngga suring."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> uchun pastga suring."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Tebranish"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Tebranish"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Ovoz"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Standart ovoz (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Telefon ringtoni"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Jiringlash vaqtida tebranish"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Qo‘ng‘iroq ohangi va tebranish"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Konferens-aloqani sozlash"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Favqulodda qo‘ng‘iroq raqami"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Profil rasmi"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Kamera o‘chiq"</string>
- <string name="child_number" msgid="4469090994612105532">"<xliff:g id="CHILD_NUMBER">%s</xliff:g> orqali"</string>
- <string name="note_sent" msgid="7623014827902758398">"Xabar yuborildi"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"So‘nggi xabarlar"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Kompaniya haqida ma’lumot"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> mil masofada"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> km masofada"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> – <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Ertaga <xliff:g id="OPEN_TIME">%s</xliff:g> da ochiladi"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Bugun <xliff:g id="OPEN_TIME">%s</xliff:g> da ochiladi"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"<xliff:g id="CLOSE_TIME">%s</xliff:g> da yopiladi"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Bugun <xliff:g id="CLOSE_TIME">%s</xliff:g> da yopiladi"</string>
- <string name="open_now" msgid="4615706338669555999">"Ochiq"</string>
- <string name="closed_now" msgid="2635314668145282080">"Yopiq"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Shubhali abonent"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Qo‘ng‘iroq yakunlandi (%1$s)"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Sizga bu raqamdan birinchi marta qo‘ng‘iroq qilishdi."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Bu spam-qo‘ng‘iroqqa o‘xshayapti."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Bloklash/spamga"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Kontaktni qo‘shish"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Spam emas"</string>
-</resources>
diff --git a/InCallUI/res/values-vi/strings.xml b/InCallUI/res/values-vi/strings.xml
deleted file mode 100644
index 58a50c278..000000000
--- a/InCallUI/res/values-vi/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Điện thoại"</string>
- <string name="onHold" msgid="527593602772521700">"Đang chờ"</string>
- <string name="unknown" msgid="3646075119047488748">"Không xác định"</string>
- <string name="private_num" msgid="6081418498487514686">"Số cá nhân"</string>
- <string name="payphone" msgid="5743050584468748607">"Điện thoại trả tiền"</string>
- <string name="confCall" msgid="3181961445236675173">"Cuộc gọi nhiều bên"</string>
- <string name="call_lost" msgid="8208184291640961172">"Cuộc gọi bị gián đoạn"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Loa"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Tai nghe ĐTDĐ"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Tai nghe có dây"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Gửi các âm sau?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Đang gửi âm\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Gửi"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Có"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Không"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Thay thế ký tự tự do bằng"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Cuộc gọi nhiều bên <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Số thư thoại"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Đang gọi"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Đang quay số lại"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Cuộc gọi nhiều bên"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Cuộc gọi đến"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Cuộc gọi đến về công việc"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Cuộc gọi đã kết thúc"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Đang chờ"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Kết thúc cuộc gọi"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Trong cuộc gọi"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Số điện thoại của tôi là <xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Đang kết nối video"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Cuộc gọi điện video"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Đang yêu cầu video"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Không kết nối được cuộc gọi điện video"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Đã từ chối yêu cầu video"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Số gọi lại của bạn\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Số gọi lại khẩn cấp của bạn\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Đang gọi"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Cuộc gọi nhỡ"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Cuộc gọi nhỡ"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> cuộc gọi nhỡ"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Cuộc gọi nhỡ từ <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Cuộc gọi đang thực hiện"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Cuộc gọi đang diễn ra về công việc"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Cuộc gọi đang diễn ra qua Wi-Fi"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Cuộc gọi đang diễn ra qua Wi-Fi về công việc"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Đang chờ"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Cuộc gọi đến"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Cuộc gọi đến về công việc"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Cuộc gọi đến qua Wi-Fi"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Cuộc gọi đến qua Wi-Fi về công việc"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Cuộc gọi điện video đến"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Cuộc gọi spam đến bị nghi ngờ"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Yêu cầu video đến"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Thư thoại mới"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Thư thoại mới (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Quay số <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Số thư thoại không xác định"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Không có dịch vụ"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Mạng được chọn (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) không khả dụng"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Trả lời"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Gác máy"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Video"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Thoại"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Chấp nhận"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Loại bỏ"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Gọi lại"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Tin nhắn"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Cuộc gọi đang diễn ra trên một thiết bị khác"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Chuyển cuộc gọi"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Để thực hiện cuộc gọi, trước tiên, hãy tắt chế độ trên Máy bay."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Chưa được đăng ký trên mạng."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Không có mạng di động."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Để thực hiện cuộc gọi, hãy nhập một số hợp lệ."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Không thực hiện được cuộc gọi."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Đang khởi động chuỗi MMI…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Dịch vụ không được hỗ trợ."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Không chuyển đổi được cuộc gọi."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Không tách được cuộc gọi."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Không chuyển được cuộc gọi."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Không thực hiện được cuộc gọi nhiều bên."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Không từ chối được cuộc gọi."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Không thực hiện được cuộc gọi."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Cuộc gọi qua SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Cuộc gọi khẩn cấp"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Đang bật radio..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Không có dịch vụ nào. Đang thử lại…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Không thực hiện được cuộc gọi. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> không phải là số khẩn cấp."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Không thực hiện được cuộc gọi. Hãy quay số khẩn cấp."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Sử dụng bàn phím để quay số"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Giữ cuộc gọi"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Tiếp tục cuộc gọi"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Kết thúc cuộc gọi"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Hiển thị bàn phím số"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Ẩn bàn phím số"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Tắt tiếng"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Bật tiếng"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Thêm cuộc gọi"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Hợp nhất cuộc gọi"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Hoán đổi"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Quản lý cuộc gọi"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Quản lý cuộc gọi nhiều bên"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Cuộc gọi nhiều bên"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Quản lý"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Âm thanh"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Cuộc gọi điện video"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Thay đổi thành cuộc gọi thoại"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Chuyển máy ảnh"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Bật máy ảnh"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Tắt máy ảnh"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Tùy chọn khác"</string>
- <string name="player_started" msgid="3478865572468310331">"Đã khởi động trình phát"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Đã dừng trình phát"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Máy ảnh chưa sẵn sàng"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Máy ảnh đã sẵn sàng"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Sự kiện phiên cuộc gọi không xác định"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Dịch vụ"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Thiết lập"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Chưa được đặt&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Cài đặt cuộc gọi khác"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Gọi điện qua <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Cuộc gọi đến qua <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"ảnh liên hệ"</string>
- <string name="goPrivate" msgid="3554069451018659483">"chuyển thành riêng tư"</string>
- <string name="selectContact" msgid="92191462970821951">"chọn địa chỉ liên hệ"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Viết trả lời của riêng bạn..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Hủy"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Gửi"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Trả lời"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Gửi SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Từ chối"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Trả lời là cuộc gọi điện video"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Trả lời là cuộc gọi âm thanh"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Chấp nhận yêu cầu cuộc gọi video"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Từ chối yêu cầu cuộc gọi video"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Chấp nhận yêu cầu truyền video"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Từ chối yêu cầu truyền video"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Chấp nhận yêu cầu nhận video"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Từ chối yêu cầu nhận video"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Trượt lên để <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Trượt sang trái để <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Trượt sang phải để <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Trượt xuống để <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Rung"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Rung"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Âm thanh"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Âm thanh mặc định (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Nhạc chuông điện thoại"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Rung khi đổ chuông"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Nhạc chuông và rung"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Quản lý cuộc gọi nhiều bên"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Số khẩn cấp"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Ảnh hồ sơ"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Tắt máy ảnh"</string>
- <string name="child_number" msgid="4469090994612105532">"qua <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Đã gửi ghi chú"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Tin nhắn gần đây"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Thông tin doanh nghiệp"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"Cách <xliff:g id="DISTANCE">%.1f</xliff:g> dặm"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"Cách <xliff:g id="DISTANCE">%.1f</xliff:g> km"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Mở cửa lúc <xliff:g id="OPEN_TIME">%s</xliff:g> ngày mai"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Mở cửa lúc <xliff:g id="OPEN_TIME">%s</xliff:g> hôm nay"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Đóng cửa lúc <xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Đã đóng cửa lúc <xliff:g id="CLOSE_TIME">%s</xliff:g> hôm nay"</string>
- <string name="open_now" msgid="4615706338669555999">"Mở ngay bây giờ"</string>
- <string name="closed_now" msgid="2635314668145282080">"Hiện đã đóng cửa"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Người gọi spam bị nghi ngờ"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Đã kết thúc cuộc gọi %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Đây là lần đầu tiên số điện thoại này gọi điện cho bạn."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Chúng tôi đã nghi ngờ cuộc gọi này là người gửi spam."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Chặn/báo cáo spam"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Thêm liên hệ"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Không phải là spam"</string>
-</resources>
diff --git a/InCallUI/res/values-w500dp-land/colors.xml b/InCallUI/res/values-w500dp-land/colors.xml
deleted file mode 100644
index 77eea2e68..000000000
--- a/InCallUI/res/values-w500dp-land/colors.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-<resources>
- <!-- Background color for status bar. For portrait this will be ignored. -->
- <color name="statusbar_background_color">#000000</color>
-</resources>
diff --git a/InCallUI/res/values-w500dp-land/dimens.xml b/InCallUI/res/values-w500dp-land/dimens.xml
deleted file mode 100644
index 112ec5f09..000000000
--- a/InCallUI/res/values-w500dp-land/dimens.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<resources>
- <!-- Whether or not this layout displays a large photo. -->
- <bool name="has_large_photo">true</bool>
-
- <!-- Whether or not the landscape mode layout is currently being used -->
- <bool name="is_layout_landscape">true</bool>
-
- <!-- Height and width of the in-call buttons. -->
- <dimen name="in_call_button_dimension">40dp</dimen>
-
- <!-- Margin underneath the call buttons. This is used only in landscape mode and is sized
- so that the call buttons are center aligned with the end call button. -->
- <dimen name="call_buttons_bottom_margin">30dp</dimen>
-
- <dimen name="dialpad_elevation">2dp</dimen>
-
- <dimen name="video_preview_margin">20dp</dimen>
-</resources>
diff --git a/InCallUI/res/values-zh-rCN/strings.xml b/InCallUI/res/values-zh-rCN/strings.xml
deleted file mode 100644
index f9c43764d..000000000
--- a/InCallUI/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"电话"</string>
- <string name="onHold" msgid="527593602772521700">"保持"</string>
- <string name="unknown" msgid="3646075119047488748">"未知"</string>
- <string name="private_num" msgid="6081418498487514686">"私密号码"</string>
- <string name="payphone" msgid="5743050584468748607">"公用电话"</string>
- <string name="confCall" msgid="3181961445236675173">"电话会议"</string>
- <string name="call_lost" msgid="8208184291640961172">"通话中断"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"扬声器"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"手机听筒"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"有线耳机"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"蓝牙"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"发送以下信号音?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"正在发送信号音\n"</string>
- <string name="send_button" msgid="4054398309483035794">"发送"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"是"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"否"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"将通配符替换为"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"电话会议(<xliff:g id="CONF_CALL_TIME">%s</xliff:g>)"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"语音信箱号码"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"正在拨号"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"正在重拨"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"电话会议"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"来电"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"工作来电"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"通话已结束"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"保持"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"正在挂断"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"正在通话"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"我的电话号码:<xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"正在接通视频"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"视频通话"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"正在发出视频请求"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"无法接通视频通话"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"视频请求遭拒"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"您的回拨号码如下:\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"您的紧急回拨号码如下:\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"正在拨号"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"未接电话"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"未接电话"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> 个未接电话"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"来自<xliff:g id="MISSED_CALL_FROM">%s</xliff:g>的未接电话"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"通话进行中"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"工作通话进行中"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"WLAN 通话进行中"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"WLAN 工作通话进行中"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"保持"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"来电"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"工作来电"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"WLAN 来电"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"WLAN 工作来电"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"视频通话来电"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"有疑似骚扰来电"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"收到视频通话请求"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"新语音邮件"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"新语音邮件 (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"拨打 <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"语音信箱号码未知"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"没有服务"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"所选网络(<xliff:g id="OPERATOR_NAME">%s</xliff:g>)不可用"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"接听"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"挂断"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"视频"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"语音"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"接受"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"拒绝"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"回电"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"发短信"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"其他设备上有正在进行的通话"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"转接通话"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"要拨打电话,请先关闭飞行模式。"</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"尚未注册网络。"</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"无法连接到移动网络。"</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"要拨打电话,请输入有效的电话号码。"</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"无法拨打该电话。"</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"正在启动 MMI 序列…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"服务不受支持。"</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"无法切换通话。"</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"无法单独通话。"</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"无法转移呼叫。"</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"无法进行电话会议。"</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"无法拒接来电。"</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"无法挂断。"</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP 通话"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"紧急呼救"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"正在开启无线装置…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"找不到服务信号,正在重试…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"无法拨打该电话。<xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> 不是紧急呼救号码。"</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"无法拨打该电话。请拨打紧急呼救号码。"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"使用键盘拨号"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"保持通话"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"恢复通话"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"结束通话"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"显示拨号键盘"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"隐藏拨号键盘"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"静音"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"取消静音"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"添加通话"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"合并通话"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"切换"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"管理通话"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"管理电话会议"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"电话会议"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"管理"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"音频"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"视频通话"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"改为语音通话"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"切换摄像头"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"开启摄像头"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"关闭摄像头"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"更多选项"</string>
- <string name="player_started" msgid="3478865572468310331">"播放器已启动"</string>
- <string name="player_stopped" msgid="1278611664986561535">"播放器已停止"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"摄像头尚未准备就绪"</string>
- <string name="camera_ready" msgid="2614541247814590887">"摄像头已准备就绪"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"未知通话事件"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"服务"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"设置"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;未设置&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"其他通话设置"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"正在通过<xliff:g id="PROVIDER_NAME">%s</xliff:g>进行通话"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"有人通过<xliff:g id="PROVIDER_NAME">%s</xliff:g>来电"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"联系人照片"</string>
- <string name="goPrivate" msgid="3554069451018659483">"单独通话"</string>
- <string name="selectContact" msgid="92191462970821951">"选择联系人"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"自行撰写回复…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"取消"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"发送"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"接听"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"发送短信"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"拒绝"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"以视频通话的形式接听"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"以音频通话的形式接听"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"接受视频请求"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"拒绝视频请求"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"接受视频传输请求"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"拒绝视频传输请求"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"接受视频接收请求"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"拒绝视频接收请求"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"向上滑动即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
- <string name="description_direction_left" msgid="6811598791620851239">"向左滑动即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
- <string name="description_direction_right" msgid="5461971399586296023">"向右滑动即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
- <string name="description_direction_down" msgid="3331715227997561639">"向下滑动即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"振动"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"振动"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"提示音"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"默认提示音(<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"手机铃声"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"响铃时振动"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"铃声和振动"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"管理电话会议"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"紧急呼救号码"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"个人资料照片"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"摄像头已关闭"</string>
- <string name="child_number" msgid="4469090994612105532">"通过 <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"已发送备注"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"最近的信息"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"商家信息"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> 英里远"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> 公里远"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="LOCALITY">%2$s</xliff:g><xliff:g id="STREET_ADDRESS">%1$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>,<xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"将于明天<xliff:g id="OPEN_TIME">%s</xliff:g>开始营业"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"将于今天<xliff:g id="OPEN_TIME">%s</xliff:g>开始营业"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"将于<xliff:g id="CLOSE_TIME">%s</xliff:g>结束营业"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"已于今天<xliff:g id="CLOSE_TIME">%s</xliff:g>结束营业"</string>
- <string name="open_now" msgid="4615706338669555999">"营业中"</string>
- <string name="closed_now" msgid="2635314668145282080">"现已结束营业"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"疑似骚扰电话号码"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"通话已结束 %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"这是此号码的第一次来电。"</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"我们怀疑这是骚扰电话。"</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"屏蔽/举报骚扰电话号码"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"添加联系人"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"非骚扰电话号码"</string>
-</resources>
diff --git a/InCallUI/res/values-zh-rHK/strings.xml b/InCallUI/res/values-zh-rHK/strings.xml
deleted file mode 100644
index bf6f016cb..000000000
--- a/InCallUI/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"電話"</string>
- <string name="onHold" msgid="527593602772521700">"保留通話"</string>
- <string name="unknown" msgid="3646075119047488748">"不明"</string>
- <string name="private_num" msgid="6081418498487514686">"私人號碼"</string>
- <string name="payphone" msgid="5743050584468748607">"公共電話"</string>
- <string name="confCall" msgid="3181961445236675173">"會議通話"</string>
- <string name="call_lost" msgid="8208184291640961172">"通話已中斷"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"喇叭"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"免提聽筒"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"有線耳機"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"藍牙"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"要傳送以下訊號音嗎?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"正在傳送訊號音\n"</string>
- <string name="send_button" msgid="4054398309483035794">"傳送"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"是"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"否"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"將萬用字元改為"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"會議通話:<xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"留言號碼"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"正在撥號"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"正在重撥"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"會議通話"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"來電"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"工作來電"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"通話已結束"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"保留通話"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"正在掛斷電話"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"正在通話"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"我的電話號碼:<xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"正在建立視像通話連線"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"視像通話"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"正在提出視像通話要求"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"無法建立視像通話連線"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"視像通話要求被拒"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"您的回撥號碼:\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"您的緊急回撥號碼:\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"正在撥號"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"未接來電"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"未接來電"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> 個未接來電"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"來自 <xliff:g id="MISSED_CALL_FROM">%s</xliff:g> 的未接來電"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"通話中"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"正在進行工作通話"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"正在進行 Wi-Fi 通話"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"正在進行 Wi-Fi 工作通話"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"保留通話"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"來電"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"工作來電"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Wi-Fi 來電"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Wi-Fi 工作來電"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"視像通話來電"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"疑似收到垃圾來電"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"收到視像通話要求"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"新留言"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"新留言 (<xliff:g id="COUNT">%d</xliff:g> 個)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"撥打 <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"留言號碼不明"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"沒有服務"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"您選取的網絡 (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) 無法使用"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"接聽"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"掛斷電話"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"視像通話"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"語音通話"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"接受"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"拒絕"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"回電"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"短訊"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"其他裝置上有正在進行的通話"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"轉接來電"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"如要撥打電話,請先關閉飛行模式。"</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"未在網絡上註冊。"</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"無法連線至流動網絡。"</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"如要撥打電話,請輸入有效的號碼。"</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"無法通話。"</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"開始 MMI 序列…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"不支援此服務。"</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"無法切換通話。"</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"無法分開通話。"</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"無法轉接。"</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"無法進行會議通話。"</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"無法拒接來電。"</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"無法結束通話。"</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP 通話"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"緊急電話"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"正在開啟無線電…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"找不到服務,請再試一次…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"無法通話。<xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> 不是緊急電話號碼。"</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"無法通話。請撥打緊急電話號碼。"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"使用鍵盤撥號"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"保留通話"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"恢復通話"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"結束通話"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"顯示撥號鍵盤"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"隱藏撥號鍵盤"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"略過"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"取消靜音"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"新增通話"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"合併通話"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"切換"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"管理通話"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"管理會議通話"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"會議通話"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"管理"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"音訊"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"視像通話"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"變更為語音通話"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"切換鏡頭"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"開啟攝影機"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"關閉攝影機"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"更多選項"</string>
- <string name="player_started" msgid="3478865572468310331">"已啟動播放器"</string>
- <string name="player_stopped" msgid="1278611664986561535">"已停止播放器"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"相機未準備好"</string>
- <string name="camera_ready" msgid="2614541247814590887">"相機已準備就緒"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"不明的通話工作階段活動"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"服務"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"設定"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;未設定&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"其他通話設定"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"正在透過 <xliff:g id="PROVIDER_NAME">%s</xliff:g> 撥號"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"有人透過 <xliff:g id="PROVIDER_NAME">%s</xliff:g> 來電"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"聯絡人相片"</string>
- <string name="goPrivate" msgid="3554069451018659483">"私人通話"</string>
- <string name="selectContact" msgid="92191462970821951">"選取聯絡人"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"自行撰寫回覆..."</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"取消"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"傳送"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"接聽"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"傳送短訊"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"拒絕"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"接聽視像通話"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"接聽語音通話"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"接受視像通話要求"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"拒絕視像通話要求"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"接受視像通話轉駁要求"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"拒絕視像通話轉駁要求"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"接受視像接收要求"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"拒絕視像接收要求"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"向上滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
- <string name="description_direction_left" msgid="6811598791620851239">"向左滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
- <string name="description_direction_right" msgid="5461971399586296023">"向右滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
- <string name="description_direction_down" msgid="3331715227997561639">"向下滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"震動"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"震動"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"音效"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"預設音效 (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"手機鈴聲"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"響鈴時震動"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"鈴聲和震動"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"管理會議通話"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"緊急電話號碼"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"個人檔案相片"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"相機已關閉"</string>
- <string name="child_number" msgid="4469090994612105532">"透過 <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"已傳送筆記"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"最近的訊息"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"公司資料"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> 英里外"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> 公里外"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="LOCALITY">%2$s</xliff:g><xliff:g id="STREET_ADDRESS">%1$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>,<xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"將於明天<xliff:g id="OPEN_TIME">%s</xliff:g>開始營業"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"將於今天<xliff:g id="OPEN_TIME">%s</xliff:g>開始營業"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"將於<xliff:g id="CLOSE_TIME">%s</xliff:g>關門"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"已於今天<xliff:g id="CLOSE_TIME">%s</xliff:g>關門"</string>
- <string name="open_now" msgid="4615706338669555999">"營業中"</string>
- <string name="closed_now" msgid="2635314668145282080">"目前休息"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"疑似垃圾來電者"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"通話結束 %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"這是此號碼的第一次來電。"</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"我們懷疑此來電為垃圾來電。"</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"封鎖/舉報為垃圾來電"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"新增聯絡人"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"非垃圾來電"</string>
-</resources>
diff --git a/InCallUI/res/values-zh-rTW/strings.xml b/InCallUI/res/values-zh-rTW/strings.xml
deleted file mode 100644
index e316c7d40..000000000
--- a/InCallUI/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"電話"</string>
- <string name="onHold" msgid="527593602772521700">"保留"</string>
- <string name="unknown" msgid="3646075119047488748">"不明"</string>
- <string name="private_num" msgid="6081418498487514686">"私人號碼"</string>
- <string name="payphone" msgid="5743050584468748607">"公用電話"</string>
- <string name="confCall" msgid="3181961445236675173">"電話會議"</string>
- <string name="call_lost" msgid="8208184291640961172">"通話已中斷"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"喇叭"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"手機聽筒"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"有線耳機"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"藍牙"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"要傳送以下的信號音嗎?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"正在傳送信號音\n"</string>
- <string name="send_button" msgid="4054398309483035794">"傳送"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"是"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"否"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"將萬用字元替換為"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"電話會議 <xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"語音留言號碼"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"撥號中"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"重撥中"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"電話會議"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"來電"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"公司來電"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"通話已結束"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"保留"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"掛斷中"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"通話中"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"我的電話號碼:<xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"正在建立視訊通話連線"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"視訊通話"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"正在提出視訊通話要求"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"無法建立視訊通話連線"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"視訊通話要求遭拒"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"您的回撥號碼如下\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"您的緊急回撥號碼如下\n <xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"撥號中"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"未接來電"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"未接來電"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> 通未接來電"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"來自 <xliff:g id="MISSED_CALL_FROM">%s</xliff:g> 的未接來電"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"進行中的通話"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"進行中的公司通話"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"進行中的通話 (透過 Wi-Fi)"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"進行中的公司通話 (透過 Wi-Fi)"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"保留"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"來電"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"公司來電"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"來電 (透過 Wi-Fi)"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"公司來電 (透過 Wi-Fi)"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"視訊通話來電"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"可疑的騷擾/廣告來電"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"收到視訊通話要求"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"新的語音留言"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"新的語音留言 (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"撥打 <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"語音留言號碼不明"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"沒有服務"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"您所選取的網路 (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) 無法使用"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"接聽"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"掛斷"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"視訊通話"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"語音通話"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"接受"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"拒絕"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"回撥"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"傳送簡訊"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"其他裝置上有進行中的通話"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"轉接來電"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"撥號前,請先關閉飛航模式。"</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"尚未註冊網路。"</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"無法連線到行動網路。"</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"如要撥打電話,請輸入有效的號碼。"</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"無法通話。"</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"開始 MMI 序列…"</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"不支援的服務。"</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"無法切換通話。"</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"無法分割通話。"</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"無法轉接。"</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"無法進行電話會議。"</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"無法拒接來電。"</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"無法掛斷電話。"</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"SIP 通話"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"緊急電話"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"正在開啟無線通訊…"</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"找不到服務訊號,重試中…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"無法通話。<xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> 不是緊急電話號碼。"</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"無法通話。只能撥打緊急號碼。"</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"使用鍵盤撥號"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"保留通話"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"恢復通話"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"結束通話"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"顯示撥號鍵盤"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"隱藏撥號鍵盤"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"忽略"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"取消忽略"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"新增通話"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"合併通話"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"切換"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"管理通話"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"管理電話會議"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"電話會議"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"管理"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"音訊"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"視訊通話"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"變更為語音通話"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"切換鏡頭"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"開啟攝影機"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"關閉攝影機"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"更多選項"</string>
- <string name="player_started" msgid="3478865572468310331">"已啟動播放器"</string>
- <string name="player_stopped" msgid="1278611664986561535">"已停止播放器"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"相機尚未就緒"</string>
- <string name="camera_ready" msgid="2614541247814590887">"相機已準備就緒"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"不明的通話工作階段事件"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"服務"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"設定"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;未設定&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"其他通話設定"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"正在透過 <xliff:g id="PROVIDER_NAME">%s</xliff:g> 撥號"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"有人透過 <xliff:g id="PROVIDER_NAME">%s</xliff:g> 來電"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"聯絡人相片"</string>
- <string name="goPrivate" msgid="3554069451018659483">"私人通話"</string>
- <string name="selectContact" msgid="92191462970821951">"選取聯絡人"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"自行撰寫回應…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"取消"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"傳送"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"接聽"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"傳送簡訊"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"拒絕"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"接聽視訊通話"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"接聽語音通話"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"接受視訊通話要求"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"拒絕視訊通話要求"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"接受視訊傳送要求"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"拒絕視訊傳送要求"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"接受視訊接收要求"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"拒絕視訊接收要求"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"向上滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
- <string name="description_direction_left" msgid="6811598791620851239">"向左滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
- <string name="description_direction_right" msgid="5461971399586296023">"向右滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
- <string name="description_direction_down" msgid="3331715227997561639">"向下滑動即可<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>。"</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"震動"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"震動"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"音效"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"預設音效 (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"手機鈴聲"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"鈴響時震動"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"鈴聲與震動"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"管理電話會議"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"緊急電話號碼"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"個人資料相片"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"相機已停用"</string>
- <string name="child_number" msgid="4469090994612105532">"透過 <xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"備註已送出"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"最近的訊息"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"商家資訊"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> 英里遠"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> 公里遠"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="LOCALITY">%2$s</xliff:g>,<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>,<xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"將於明日<xliff:g id="OPEN_TIME">%s</xliff:g>開始營業"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"將於本日<xliff:g id="OPEN_TIME">%s</xliff:g>開始營業"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"將於<xliff:g id="CLOSE_TIME">%s</xliff:g>結束營業"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"已於本日<xliff:g id="CLOSE_TIME">%s</xliff:g>結束營業"</string>
- <string name="open_now" msgid="4615706338669555999">"營業中"</string>
- <string name="closed_now" msgid="2635314668145282080">"本日已結束營業"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"可疑的騷擾/廣告來電者"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"通話結束 %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"這組號碼首次致電給您。"</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"我們懷疑這通來電是騷擾/廣告電話。"</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"封鎖/回報為騷擾/廣告電話"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"新增聯絡人"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"非騷擾/廣告電話"</string>
-</resources>
diff --git a/InCallUI/res/values-zu/strings.xml b/InCallUI/res/values-zu/strings.xml
deleted file mode 100644
index 46bf5afbb..000000000
--- a/InCallUI/res/values-zu/strings.xml
+++ /dev/null
@@ -1,195 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="phoneAppLabel" product="default" msgid="4753450867264774000">"Ifoni"</string>
- <string name="onHold" msgid="527593602772521700">"Ibanjiwe"</string>
- <string name="unknown" msgid="3646075119047488748">"Akwaziwa"</string>
- <string name="private_num" msgid="6081418498487514686">"Inombolo eyimfihlo"</string>
- <string name="payphone" msgid="5743050584468748607">"Ucingo olufakwa imali"</string>
- <string name="confCall" msgid="3181961445236675173">"Ikholi yenkomfa"</string>
- <string name="call_lost" msgid="8208184291640961172">"Ikholi ivaliwe"</string>
- <string name="audio_mode_speaker" msgid="6160127758732918123">"Isipikha"</string>
- <string name="audio_mode_earpiece" msgid="3138677187223932893">"Isipikha sendlebe sama-ear phone"</string>
- <string name="audio_mode_wired_headset" msgid="583080366967943196">"Ama-earphone anezintambo"</string>
- <string name="audio_mode_bluetooth" msgid="3188504589946495676">"I-Bluetooth"</string>
- <string name="wait_prompt_str" msgid="3784275777844586675">"Thumela amathoni alandelayo?\n"</string>
- <string name="pause_prompt_str" msgid="4507496811727697620">"Ithumela amathoni\n"</string>
- <string name="send_button" msgid="4054398309483035794">"Thumela"</string>
- <string name="pause_prompt_yes" msgid="6738588490007499118">"Yebo"</string>
- <string name="pause_prompt_no" msgid="417286529736964178">"Cha"</string>
- <string name="wild_prompt_str" msgid="8178750766679617355">"Miselela uhlamvu lwasendle nge"</string>
- <string name="caller_manage_header" msgid="4036790479287738218">"Ikholi yenkomfa engu-<xliff:g id="CONF_CALL_TIME">%s</xliff:g>"</string>
- <string name="voicemail_settings_number_label" msgid="2951965862286532957">"Inombolo yevoyisimeyili"</string>
- <string name="card_title_dialing" msgid="5046026076417718916">"Iyadayela"</string>
- <string name="card_title_redialing" msgid="8072468059192027844">"Iphinda iyadayela"</string>
- <string name="card_title_conf_call" msgid="1747835072739982104">"Ikholi yenkomfa"</string>
- <string name="card_title_incoming_call" msgid="4138485434087223132">"Ikholi engenayo"</string>
- <string name="card_title_incoming_work_call" msgid="7000583925426981712">"Ikholi engenayo yomsebenzi"</string>
- <string name="card_title_call_ended" msgid="5249815286629136486">"Ikholi iqediwe"</string>
- <string name="card_title_on_hold" msgid="5633854828341577689">"Ibanjiwe"</string>
- <string name="card_title_hanging_up" msgid="3402022578391538671">"Iyavala"</string>
- <string name="card_title_in_call" msgid="5029165346952099302">"Ukukholi"</string>
- <string name="card_title_my_phone_number" msgid="3749572971322520177">"Inombolo yami ngu-<xliff:g id="MY_PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="card_title_video_call_connecting" msgid="9171270899902894036">"Ixhuma ividiyo"</string>
- <string name="card_title_video_call" msgid="6519406270853889302">"Ikholi yevidiyo"</string>
- <string name="card_title_video_call_requesting" msgid="1611293204379882739">"Icela ividiyo"</string>
- <string name="card_title_video_call_error" msgid="8488074823425848193">"Ayikwazi ukuxhuma ikholi yevidiyo"</string>
- <string name="card_title_video_call_rejected" msgid="2885215432045215465">"Isicelo sevidiyo sinqatshelwe"</string>
- <string name="card_title_callback_number" msgid="7646082782307705748">"Inombolo yakho yokuphinda ushaye\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="card_title_callback_number_emergency" msgid="8916355112472826080">"Inombolo yakho yokuphinda ushayelwe yesimo esiphuthumayo\n<xliff:g id="DARK_NUMBER">%1$s</xliff:g>"</string>
- <string name="notification_dialing" msgid="8080968169444117163">"Iyadayela"</string>
- <string name="notification_missedCallTitle" msgid="2774630248151712215">"Ikholi ephuthelwe"</string>
- <string name="notification_missedCallsTitle" msgid="263275811089605859">"Amakholi akuphuthele"</string>
- <string name="notification_missedCallsMsg" msgid="69408330370667429">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> amakholi akulahlekele"</string>
- <string name="notification_missedCallTicker" msgid="1599269453813734699">"Uphuthelwe ikholi kusukela ku-<xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
- <string name="notification_ongoing_call" msgid="8633734299234807397">"Ikholi eqhubekayo"</string>
- <string name="notification_ongoing_work_call" msgid="3465083293325006579">"Ikholi yomsebenzi eqhubekayo"</string>
- <string name="notification_ongoing_call_wifi" msgid="4140639349603930166">"Ikholi ye-Wi-Fi eqhubekayo"</string>
- <string name="notification_ongoing_work_call_wifi" msgid="8469582753279163976">"Ikholi yomsebenzi eqhubekayo ye-Wi-Fi"</string>
- <string name="notification_on_hold" msgid="3151343576023182586">"Ibanjiwe"</string>
- <string name="notification_incoming_call" msgid="5904745644632328863">"Ikholi engenayo"</string>
- <string name="notification_incoming_work_call" msgid="281305845895342925">"Ikholi engenayo yomsebenzi"</string>
- <string name="notification_incoming_call_wifi" msgid="8337740714221114955">"Ikholi ye-Wi-Fi engenayo"</string>
- <string name="notification_incoming_work_call_wifi" msgid="3248418394186803763">"Ikholi engenayo yomsebenzi ye-Wi-Fi"</string>
- <string name="notification_incoming_video_call" msgid="7814873581838165772">"Ikholi yevidiyo engenayo"</string>
- <string name="notification_incoming_spam_call" msgid="4671356711842697266">"Ikholi engenayo osolisayo kagaxekile"</string>
- <string name="notification_requesting_video_call" msgid="4844596091477863245">"Isicelo sevidiyo engenayo"</string>
- <string name="notification_voicemail_title" msgid="7595628197933709144">"Ivoyisimeyili entsha"</string>
- <string name="notification_voicemail_title_count" msgid="1241573926817248239">"Ivoyisimeyili entsha (<xliff:g id="COUNT">%d</xliff:g>)"</string>
- <string name="notification_voicemail_text_format" msgid="6496440879085042069">"Dayela u-<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
- <string name="notification_voicemail_no_vm_number" msgid="5433652017869242375">"Inombolo yevoyisimeyili ayaziwa"</string>
- <string name="notification_network_selection_title" msgid="6785177943238085441">"Ayikho isevisi"</string>
- <string name="notification_network_selection_text" msgid="9097902390701009591">"Inethiwekhi ekhethiwe (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) ayitholakali"</string>
- <string name="notification_action_answer" msgid="8418990052527593953">"Phendula"</string>
- <string name="notification_action_end_call" msgid="2152010639043225860">"Vala ikholi"</string>
- <string name="notification_action_answer_video" msgid="2400233093494856655">"Ividiyo"</string>
- <string name="notification_action_answer_voice" msgid="3206168292649273866">"Izwi"</string>
- <string name="notification_action_accept" msgid="8595047032790476122">"Yamukela"</string>
- <string name="notification_action_dismiss" msgid="1998811618480434300">"Cashisa"</string>
- <string name="notification_missedCall_call_back" msgid="7855043480614703539">"Phinda ushayele"</string>
- <string name="notification_missedCall_message" msgid="2407410183079324393">"Umlayezo"</string>
- <string name="notification_external_call" msgid="5611236780302924816">"Ikholi eqhubekayo kwenye idivayisi"</string>
- <string name="notification_transfer_call" msgid="687009078741947505">"Dlulisela ikholi"</string>
- <string name="incall_error_power_off" msgid="3626117639377110403">"Ukwenza ikholi, vala kuqala imodi Yendiza."</string>
- <string name="incall_error_emergency_only" msgid="8704761887752183855">"Ayibhalisiwe kwinethiwekhi."</string>
- <string name="incall_error_out_of_service" msgid="1830319376612608339">"Inethiwekhi yeselula ayitholakali."</string>
- <string name="incall_error_no_phone_number_supplied" msgid="3042963797202928322">"Ukuze wenze ikholi, faka inombolo evumelekile."</string>
- <string name="incall_error_call_failed" msgid="2213413937257570551">"Ayikwazi ukushaya."</string>
- <string name="incall_status_dialed_mmi" msgid="8864341962086874751">"Iqalisa ukulandelana kwe-MMI..."</string>
- <string name="incall_error_supp_service_unknown" msgid="3390926762577861268">"Isevisi ayisekelwe."</string>
- <string name="incall_error_supp_service_switch" msgid="4893764463854753730">"Ayikwazi ukushintsha amakholi."</string>
- <string name="incall_error_supp_service_separate" msgid="5469628699581380277">"Ayikwazi ukuhlukanisa ikholi."</string>
- <string name="incall_error_supp_service_transfer" msgid="3220469890457973326">"Ayikwazi ukudlulisela."</string>
- <string name="incall_error_supp_service_conference" msgid="3100373998543200356">"Ayikwazi ukwenza inkomfa."</string>
- <string name="incall_error_supp_service_reject" msgid="4543915892409365831">"Ayikwazi ukunqabela ikholi."</string>
- <string name="incall_error_supp_service_hangup" msgid="101167589969625637">"Ayikwazi ukukhipha amakholi."</string>
- <string name="incall_call_type_label_sip" msgid="1327822795765282192">"Ikholi ye-SIP"</string>
- <string name="emergency_enable_radio_dialog_title" msgid="7882321703828314787">"Ikholi ephuthumayo"</string>
- <string name="emergency_enable_radio_dialog_message" msgid="4382752053654184327">"Ivula irediyo..."</string>
- <string name="emergency_enable_radio_dialog_retry" msgid="1672288458940152814">"Ayikho isevisi. Iyazama futhi…"</string>
- <string name="dial_emergency_error" msgid="582305854626092376">"Ayikwazi ukushaya. U-<xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> akuyona inombolo yesimo esiphuthumayo."</string>
- <string name="dial_emergency_empty_error" msgid="199888628163390267">"Ayikwazi ukushaya. Shayela inombolo yesimo esiphuthumayo."</string>
- <string name="dialerKeyboardHintText" msgid="8533449463925408141">"Sebenzisa ikhibhodi ukudayela"</string>
- <string name="onscreenHoldText_unselected" msgid="4509232821220492533">"Bamba ikholi"</string>
- <string name="onscreenHoldText_selected" msgid="2988100347384733032">"Qalisa kabusha ikholi"</string>
- <string name="onscreenEndCallText" msgid="1416981593311001074">"Qeda ikholi"</string>
- <string name="onscreenShowDialpadText_unselected" msgid="8253784035944284938">"Bonisa iphedi yokudayela"</string>
- <string name="onscreenShowDialpadText_selected" msgid="7368390784890311449">"Fihla iphedi yokudayela"</string>
- <string name="onscreenMuteText_unselected" msgid="4130269060091842798">"Thulisa"</string>
- <string name="onscreenMuteText_selected" msgid="7074763815284369548">"Susa ukuthula"</string>
- <string name="onscreenAddCallText" msgid="5577548650466595598">"Engeza ikholi"</string>
- <string name="onscreenMergeCallsText" msgid="4946687067221459357">"Hlanganisa amakholi"</string>
- <string name="onscreenSwapCallsText" msgid="8272036175646846198">"Shintsha"</string>
- <string name="onscreenManageCallsText" msgid="5491297234697209677">"Phatha amakholi"</string>
- <string name="onscreenManageConferenceText" msgid="7043499154946980355">"Phatha ucingo lwengqungquthela"</string>
- <string name="onscreenConferenceText" msgid="171855677185793827">"Ikholi yenkomfa"</string>
- <string name="onscreenManageText" msgid="7625850560625708322">"Phatha"</string>
- <string name="onscreenAudioText" msgid="8963459818052898299">"Umsindo"</string>
- <string name="onscreenVideoCallText" msgid="1578940167445068369">"Ikholi yevidiyo"</string>
- <string name="onscreenChangeToVoiceText" msgid="6249580619992009182">"Shintshela kukholi yezwi"</string>
- <string name="onscreenSwitchCameraText" msgid="7141261218152736690">"Shintsha Ikhamera"</string>
- <string name="onscreenTurnOnCameraText" msgid="915019986687927588">"Vula ikhamera"</string>
- <string name="onscreenTurnOffCameraText" msgid="6225377831394679126">"Vala ikhamera"</string>
- <string name="onscreenOverflowText" msgid="7932741239724473887">"Izinketho eziningi"</string>
- <string name="player_started" msgid="3478865572468310331">"Umdlali uqalile"</string>
- <string name="player_stopped" msgid="1278611664986561535">"Umdlali umisiwe"</string>
- <string name="camera_not_ready" msgid="6614469280264241251">"Ikhamera ayilungile"</string>
- <string name="camera_ready" msgid="2614541247814590887">"Ikhamera ilungile"</string>
- <string name="unknown_call_session_event" msgid="2947023743819984299">"Umcimbi wesikhathi sekholi ongaziwa"</string>
- <string name="voicemail_provider" msgid="2878119321474918370">"Isevisi"</string>
- <string name="voicemail_settings" msgid="7548868784816068975">"Ukusetha"</string>
- <string name="voicemail_number_not_set" msgid="2690477999015436138">"&lt;Ayisethiwe&gt;"</string>
- <string name="other_settings" msgid="6699076019841942826">"Ezinye izilungiselelo zekholi"</string>
- <string name="calling_via_template" msgid="3539373093109976255">"Ishaya nge-<xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="incoming_via_template" msgid="6281138766370092800">"Ingena nge-<xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
- <string name="contactPhoto" msgid="6028825355597675193">"isithombe soxhumana naye"</string>
- <string name="goPrivate" msgid="3554069451018659483">"yenza kube imfihlo"</string>
- <string name="selectContact" msgid="92191462970821951">"khetha othintana naye"</string>
- <string name="respond_via_sms_custom_message" msgid="8210393177674619127">"Bhala okwakho…"</string>
- <string name="custom_message_cancel" msgid="5920059627508662163">"Khansela"</string>
- <string name="custom_message_send" msgid="3798076337006735995">"Thumela"</string>
- <string name="description_target_answer" msgid="1111945818996518320">"Phendula"</string>
- <string name="description_target_send_sms" msgid="3652217769615310018">"Thumela i-SMS"</string>
- <string name="description_target_decline" msgid="7108154434759234035">"Yenqaba"</string>
- <string name="description_target_answer_video_call" msgid="4655616461181308405">"Phendula njengekholi yevidiyo"</string>
- <string name="description_target_answer_audio_call" msgid="3234714934649708854">"Phendula njengekholi yomsindo"</string>
- <string name="description_target_accept_upgrade_to_video_request" msgid="384894008955682630">"Yamukela isicelo sevidiyo"</string>
- <string name="description_target_decline_upgrade_to_video_request" msgid="7342968876159189300">"Yenqaba isicelo sevidiyo"</string>
- <string name="description_target_accept_upgrade_to_video_transmit_request" msgid="4586773853073826378">"Yamukela isicelo sokudlulisa ividiyo"</string>
- <string name="description_target_decline_upgrade_to_video_transmit_request" msgid="1191166008711514234">"Yenqaba isicelo sokudlulisa ividiyo"</string>
- <string name="description_target_accept_upgrade_to_video_receive_request" msgid="2224978927364021080">"Yamukela isicelo sokwamukela ividiyo"</string>
- <string name="description_target_decline_upgrade_to_video_receive_request" msgid="3151115394424918077">"Yenqaba isicelo sokwamukela ividiyo"</string>
- <string name="description_direction_up" msgid="1735018141439291766">"Slayidela phezulu ku-<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_left" msgid="6811598791620851239">"Slayida ngakwesokunxele ku-<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_right" msgid="5461971399586296023">"Slayida ngakwesokudla ku-<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="description_direction_down" msgid="3331715227997561639">"Slayida ngezansi ku-<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="voicemail_notification_vibrate_when_title" msgid="4595145399183729630">"Dlidlizela"</string>
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="2390729279972461242">"Dlidlizela"</string>
- <string name="voicemail_notification_ringtone_title" msgid="1996920553949534944">"Umsindo"</string>
- <string name="default_notification_description" msgid="4950807644546509965">"Umsindo ozenzakalelayo (<xliff:g id="DEFAULT_SOUND_TITLE">%1$s</xliff:g>)"</string>
- <string name="ringtone_title" msgid="835582004693335905">"Ithoni yokukhala yefoni"</string>
- <string name="vibrate_on_ring_title" msgid="5019791043398986665">"Dlidlizisa uma ikhala"</string>
- <string name="preference_category_ringtone" msgid="6246687516643676729">"Ithoni yokukhala nokudlidliza"</string>
- <string name="manageConferenceLabel" msgid="7237614418556336108">"Phatha ucingo lwengqungquthela"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"Inombolo ephuthumayo"</string>
- <string name="profile_photo_description" msgid="7958198110870319358">"Isithombe sephrofayela"</string>
- <string name="camera_off_description" msgid="4220023868645225790">"Ikhamera ivaliwe"</string>
- <string name="child_number" msgid="4469090994612105532">"nge-<xliff:g id="CHILD_NUMBER">%s</xliff:g>"</string>
- <string name="note_sent" msgid="7623014827902758398">"Inothi lithunyelwe"</string>
- <string name="person_contact_context_title" msgid="8490058088809090979">"Imilayezo yakamuva"</string>
- <string name="business_contact_context_title" msgid="8448362898576496764">"Ulwazi lwebhizinisi"</string>
- <string name="distance_imperial_away" msgid="2083362798225798740">"<xliff:g id="DISTANCE">%.1f</xliff:g> amamitha kude"</string>
- <string name="distance_metric_away" msgid="9021396592464955256">"<xliff:g id="DISTANCE">%.1f</xliff:g> amakhilomitha kude"</string>
- <string name="display_address" msgid="444235484565491291">"<xliff:g id="STREET_ADDRESS">%1$s</xliff:g>, <xliff:g id="LOCALITY">%2$s</xliff:g>"</string>
- <string name="open_time_span" msgid="2762952234657271236">"<xliff:g id="OPEN_TIME">%1$s</xliff:g> - <xliff:g id="CLOSE_TIME">%2$s</xliff:g>"</string>
- <string name="opening_hours" msgid="7803506319518398380">"<xliff:g id="EARLIER_TIMES">%1$s</xliff:g>, <xliff:g id="LATER_TIME">%2$s</xliff:g>"</string>
- <string name="opens_tomorrow_at" msgid="3567511490448488788">"Kuvulwa kusasa ngo-<xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="opens_today_at" msgid="6281212768937222891">"Kuvulwa namuhla ngo-<xliff:g id="OPEN_TIME">%s</xliff:g>"</string>
- <string name="closes_today_at" msgid="4822369201263491509">"Kuvalwa ngo-<xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="closed_today_at" msgid="4060072663433467233">"Kuvalwe namuhla ngo-<xliff:g id="CLOSE_TIME">%s</xliff:g>"</string>
- <string name="open_now" msgid="4615706338669555999">"Kuvuliwe manje"</string>
- <string name="closed_now" msgid="2635314668145282080">"Kuvaliwe manje"</string>
- <string name="label_spam_caller" msgid="8270472297763063585">"Ofonayo osolisayo wogaxekile"</string>
- <string name="spam_notification_title" msgid="8681565585547329999">"Ikholi iphelile %1$s"</string>
- <string name="spam_notification_non_spam_call_text" msgid="1357729961551033350">"Lesi isikhathi sokuqala le nombolo ikushayela."</string>
- <string name="spam_notification_spam_call_text" msgid="5132722997094120379">"Sisolele le kholi ukuthi ugaxekile."</string>
- <string name="spam_notification_report_spam_action_text" msgid="2635952482608001896">"Vimba/bika ugaxekile"</string>
- <string name="spam_notification_add_contact_action_text" msgid="396934716185343647">"Engeza oxhumana naye"</string>
- <string name="spam_notification_not_spam_action_text" msgid="1694637126165301851">"Akusiko okugaxekile"</string>
-</resources>
diff --git a/InCallUI/res/values/animation_constants.xml b/InCallUI/res/values/animation_constants.xml
deleted file mode 100644
index 8df6a7281..000000000
--- a/InCallUI/res/values/animation_constants.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-<resources>
- <!-- Duration of the InCallUI reveal and shrink animations after a call is placed -->
- <integer name="reveal_animation_duration">333</integer>
- <integer name="shrink_animation_duration">333</integer>
- <integer name="video_animation_duration">257</integer>
-</resources>
diff --git a/InCallUI/res/values/array.xml b/InCallUI/res/values/array.xml
deleted file mode 100644
index 7877ec8f3..000000000
--- a/InCallUI/res/values/array.xml
+++ /dev/null
@@ -1,135 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<!-- Array resources for the Phone app. -->
-<resources>
- <!-- "Target" resources for the GlowPadView widget used for incoming calls;
- see InCallTouchUi.showIncomingCallWidget() and incall_touch_ui.xml. -->
-
- <!-- For audio calls, if respond via SMS is disabled:
- - Answer (drag right)
- - Decline (drag left) -->
- <array name="incoming_call_widget_audio_without_sms_targets">
- <item>@drawable/ic_lockscreen_answer</item>
- <item>@null</item>
- <item>@drawable/ic_lockscreen_decline</item>
- <item>@null</item>"
- </array>
- <array name="incoming_call_widget_audio_without_sms_target_descriptions">
- <item>@string/description_target_answer</item>
- <item>@null</item>
- <item>@string/description_target_decline</item>
- <item>@null</item>"
- </array>
- <array name="incoming_call_widget_audio_without_sms_direction_descriptions">
- <item>@string/description_direction_right</item>
- <item>@null</item>
- <item>@string/description_direction_left</item>
- <item>@null</item>
- </array>
-
- <!-- For audio calls, if respond via SMS is enabled:
- - Answer (drag right)
- - Respond via SMS (drag up)
- - Decline (drag left) -->
- <array name="incoming_call_widget_audio_with_sms_targets">
- <item>@drawable/ic_lockscreen_answer</item>
- <item>@drawable/ic_lockscreen_text</item>
- <item>@drawable/ic_lockscreen_decline</item>
- <item>@null</item>"
- </array>
- <array name="incoming_call_widget_audio_with_sms_target_descriptions">
- <item>@string/description_target_answer</item>
- <item>@string/description_target_send_sms</item>
- <item>@string/description_target_decline</item>
- <item>@null</item>"
- </array>
- <array name="incoming_call_widget_audio_with_sms_direction_descriptions">
- <item>@string/description_direction_right</item>
- <item>@string/description_direction_up</item>
- <item>@string/description_direction_left</item>
- <item>@null</item>
- </array>
-
- <!-- For video calls, if respond via SMS is disabled:
- - Answer as video call (drag right)
- - Decline (drag left)
- - Answer as audio call (drag down) -->
- <array name="incoming_call_widget_video_without_sms_targets">
- <item>@drawable/ic_lockscreen_answer</item>
- <item>@null</item>
- <item>@drawable/ic_lockscreen_decline</item>
- <item>@drawable/ic_lockscreen_answer_video</item>
- </array>
- <array name="incoming_call_widget_video_without_sms_target_descriptions">
- <item>@string/description_target_answer_video_call</item>
- <item>@null</item>
- <item>@string/description_target_decline</item>
- <item>@string/description_target_answer_audio_call</item>
- </array>
- <array name="incoming_call_widget_video_without_sms_direction_descriptions">
- <item>@string/description_direction_right</item>
- <item>@null</item>
- <item>@string/description_direction_left</item>
- <item>@string/description_direction_down</item>
- </array>
-
- <!-- For video calls, if respond via SMS is enabled:
- - Answer as video call (drag right)
- - Respond via SMS (drag up)
- - Decline (drag left)
- - Answer as audio call (drag down) -->
- <array name="incoming_call_widget_video_with_sms_targets">
- <item>@drawable/ic_lockscreen_answer_video</item>
- <item>@drawable/ic_lockscreen_text</item>
- <item>@drawable/ic_lockscreen_decline</item>
- <item>@drawable/ic_lockscreen_answer</item>
- </array>
- <array name="incoming_call_widget_video_with_sms_target_descriptions">
- <item>@string/description_target_answer_video_call</item>
- <item>@string/description_target_send_sms</item>
- <item>@string/description_target_decline</item>
- <item>@string/description_target_answer_audio_call</item>
- </array>
- <array name="incoming_call_widget_video_with_sms_direction_descriptions">
- <item>@string/description_direction_right</item>
- <item>@string/description_direction_up</item>
- <item>@string/description_direction_left</item>
- <item>@string/description_direction_down</item>
- </array>
-
- <!-- For accept/reject upgrade to video in active video call
- - Accept upgrade to video request (drag right)
- - Decline upgrade to video request (drag left)-->
- <array name="incoming_call_widget_video_request_targets">
- <item>@drawable/ic_lockscreen_answer_video</item>
- <item>@drawable/ic_lockscreen_decline_video</item>
- </array>
-
- <array name="incoming_call_widget_video_request_target_descriptions">
- <item>@string/description_target_accept_upgrade_to_video_request</item>
- <item>@null</item>
- <item>@string/description_target_decline_upgrade_to_video_request</item>
- <item>@null</item>"
- </array>
- <array name="incoming_call_widget_video_request_target_direction_descriptions">
- <item>@string/description_direction_right</item>
- <item>@null</item>
- <item>@string/description_direction_left</item>
- <item>@null</item>
- </array>
-</resources>
diff --git a/InCallUI/res/values/attrs.xml b/InCallUI/res/values/attrs.xml
deleted file mode 100644
index e135fb72d..000000000
--- a/InCallUI/res/values/attrs.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources>
- <declare-styleable name="GlowPadView">
- <attr name="android:gravity"/>
-
- <!-- Reference to an array resource that be shown as targets around a circle. -->
- <attr name="targetDrawables" format="reference"/>
-
- <!-- Reference to an array resource that be used as description for the targets around the circle. -->
- <attr name="targetDescriptions" format="reference"/>
-
- <!-- Reference to an array resource that be used to announce the directions with targets around the circle. -->
- <attr name="directionDescriptions" format="reference"/>
-
- <!-- Sets a drawable as the center. -->
- <attr name="handleDrawable" format="reference"/>
-
- <!-- Drawable to use for wave ripple animation. -->
- <attr name="outerRingDrawable" format="reference"/>
-
- <!-- Drawble used for drawing points -->
- <attr name="pointDrawable" format="reference"/>
-
- <!-- Inner radius of glow area. -->
- <attr name="innerRadius" format="dimension"/>
-
- <!-- Outer radius of glow area. Target icons will be drawn on this circle. -->
- <attr name="outerRadius" format="dimension"/>
-
- <!-- Size of target radius. Points within this distance of target center is a "hit". -->
- <!--
- <attr name="hitRadius" format="dimension"/>
- -->
-
- <!-- Radius of glow under finger. -->
- <attr name="glowRadius" format="dimension"/>
-
- <!-- Tactile feedback duration for actions. Set to '0' for no vibration. -->
- <attr name="vibrationDuration" format="integer"/>
-
- <!-- How close we need to be before snapping to a target. -->
- <attr name="snapMargin" format="dimension"/>
-
- <!-- Number of waves/chevrons to show in animation. -->
- <attr name="feedbackCount" format="integer"/>
-
- <!-- Used when the handle shouldn't wait to be hit before following the finger -->
- <attr name="alwaysTrackFinger" format="boolean"/>
-
- <!-- Determine whether the glow pad is allowed to scale to fit the bounds indicated
- by its parent. If this is set to false, no scaling will occur. If this is set to true
- scaling will occur to fit for any axis in which gravity is set to center. -->
- <attr name="allowScaling" format="boolean" />
- </declare-styleable>
-</resources>
diff --git a/InCallUI/res/values/colors.xml b/InCallUI/res/values/colors.xml
deleted file mode 100644
index 238d36033..000000000
--- a/InCallUI/res/values/colors.xml
+++ /dev/null
@@ -1,133 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources>
-
- <!-- In-call UI background color. -->
- <color name="incall_background_color">@color/dialer_theme_color</color>
- <color name="incall_accent_color">#ffffff</color>
-
- <!-- Background color of main banner. -->
- <color name="incall_call_banner_background_color">@color/incall_background_color</color>
- <color name="incall_call_banner_text_color">#ffffff</color>
- <!-- 100% opacity, white -->
- <color name="incall_call_banner_subtext_color">#ffffff</color>
- <color name="incall_banner_secondary_background_color">#f5f5f5</color>
- <color name="incall_banner_secondary_text_color">#333333</color>
-
- <color name="incall_action_bar_background_color">@color/incall_background_color</color>
- <color name="incall_action_bar_text_color">@color/incall_call_banner_text_color</color>
-
- <color name="incall_photo_background_color">#545454</color>
-
- <!-- Put on top of each photo, implying 80% darker than usual. -->
- <color name="on_hold_dim_effect">#cc000000</color>
-
- <color name="conference_call_manager_background_color">#f8f8f8</color>
- <color name="conference_call_manager_caller_name_text_color">#4d4d4d</color>
- <color name="conference_call_manager_icon_color">#999999</color>
- <!-- Used with some smaller texts in manage conference screen. -->
- <color name="conference_call_manager_secondary_text_color">#999999</color>
- <color name="secondary_call_info_divider_highlight_color">#ffffff</color>
-
- <color name="end_call_touch_feedback_tint">#dddddd</color>
-
- <!-- Color of dialpad digits -->
- <color name="dialpad_digits_text_color">#333</color>
-
- <color name="incall_dialpad_background">#ffffff</color>
- <color name="incall_dialpad_background_pressed">#ccaaaaaa</color>
-
- <color name="button_background_color">@color/incall_background_color</color>
- <color name="button_selected_color">@color/dialer_theme_color_dark</color>
- <!-- 70% opacity, white. -->
- <color name="button_default_color">#b3ffffff</color>
- <!-- 20% opacity, white. -->
- <color name="button_disabled_color">#33ffffff</color>
-
- <!-- Background color of action bars -->
- <color name="actionbar_background_color">@color/dialer_theme_color</color>
- <!-- Background color for status bar. For portrait this will be ignored. -->
- <color name="statusbar_background_color">@color/dialer_theme_color</color>
-
- <color name="translucent_shadow">#33999999</color>
-
- <!-- 70% opacity, black. -->
- <color name="glowpad_background_color">#b3000000</color>
- <!-- 15% opacity, white. -->
- <color name="glowpad_outer_ring_color">#26ffffff</color>
- <color name="glowpad_text_widget_ring_color">#ffffff</color>
- <color name="glowpad_widget_active_color">#ffffff</color>
- <color name="glowpad_text_widget_normal_tint">#cccccc</color>
- <color name="glowpad_call_widget_normal_tint">#00c853</color>
- <color name="glowpad_end_call_widget_normal_tint">#ff1744</color>
- <color name="glowpad_incoming_widget_tint">#a3a3a3</color>
- <color name="glowpad_incoming_widget_background_tint">#ffffff</color>
-
- <!-- 70% opacity, white. -->
- <color name="accessible_answer_hint_text_color">#B2FFFFFF</color>
-
- <!-- 20% opacity, theme color. -->
- <color name="incall_dialpad_touch_tint">#330288d1</color>
-
- <!-- Background colors for InCallUI. This set of colors is a subset of
- https://spec.googleplex.com/quantumpalette#extended which pass WCAG AA and all have a
- contrast ratio over 5:1.
-
- These colors are also used by InCallUIMaterialColorMapUtils to generate primary
- activity colors.
- -->
- <array name="background_colors">
- <item>#00796B</item>
- <item>#3367D6</item>
- <item>#303F9F</item>
- <item>#7B1FA2</item>
- <item>#C2185B</item>
- <item>#C53929</item>
- <item>#A52714</item>
- </array>
-
- <!-- Darker versions of background_colors, two shades darker. These colors are used for the
- status bar. -->
- <array name="background_colors_dark">
- <item>#00695C</item>
- <item>#2A56C6</item>
- <item>#283593</item>
- <item>#6A1B9A</item>
- <item>#AD1457</item>
- <item>#B93221</item>
- <item>#841F10</item>
- </array>
-
- <!-- Background color for spam. This color must match one of background_colors above. -->
- <color name="incall_call_spam_background_color">#A52714</color>
-
- <!-- Ripple color used over light backgrounds. -->
- <color name="ripple_light">#40000000</color>
-
- <color name="contact_context_title_text_color">@color/incall_call_banner_subtext_color</color>
- <color name="person_contact_context_message_text_color">@color/dialer_theme_color</color>
- <color name="person_contact_context_message_background_color">@color/incall_call_banner_subtext_color</color>
- <color name="person_contact_context_detail_text_color">@color/incall_call_banner_subtext_color</color>
- <color name="business_contact_context_text_color">@color/incall_call_banner_subtext_color</color>
-
- <!-- White background for dialer -->
- <color name="background_dialer_white">#ffffff</color>
-
- <!-- Background color for large notification icon in after call from unknown numbers -->
- <color name="unknown_number_color">#919191</color>
-</resources>
diff --git a/InCallUI/res/values/config.xml b/InCallUI/res/values/config.xml
deleted file mode 100644
index b81ba3ca0..000000000
--- a/InCallUI/res/values/config.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
- ~ 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
- -->
-<resources>
- <!-- Determines the maximum number of buttons visible on the call card. Any buttons over this
- count are put into the overflow menu. -->
- <integer name="call_card_max_buttons">5</integer>
-
- <!-- Determines video calls will automatically enter fullscreen mode after the start of the
- call. -->
- <bool name="video_call_auto_fullscreen">true</bool>
- <!-- The number of milliseconds after which a video call will automatically enter fullscreen
- mode (requires video_call_auto_fullscreen to be true). -->
- <integer name="video_call_auto_fullscreen_timeout">5000</integer>
-</resources> \ No newline at end of file
diff --git a/InCallUI/res/values/dimens.xml b/InCallUI/res/values/dimens.xml
deleted file mode 100644
index 59da7860a..000000000
--- a/InCallUI/res/values/dimens.xml
+++ /dev/null
@@ -1,150 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources>
- <!-- Whether or not this layout displays a large photo. -->
- <bool name="has_large_photo">false</bool>
-
- <!-- Whether or not the landscape mode layout is currently being used -->
- <bool name="is_layout_landscape">false</bool>
-
- <!-- Dimensions for CallCard elements (the normal in-call UI) -->
-
- <dimen name="call_banner_height">0dp</dimen>
- <dimen name="call_banner_vertical_margin">20dp</dimen>
-
- <dimen name="incall_action_bar_elevation">3dp</dimen>
-
- <!-- Margin between the bottom of the "call card" photo
- and the top of the in-call button cluster. -->
- <dimen name="in_call_touch_ui_upper_margin">2dp</dimen>
-
- <!-- Padding at the left and right edges of the "call banner". -->
- <dimen name="call_banner_side_padding">24dp</dimen>
- <!-- Padding at the top and bottom edges of the "call banner". -->
- <dimen name="call_banner_primary_call_container_top_padding">16dp</dimen>
- <dimen name="secondary_call_info_horizontal_padding">24dp</dimen>
- <dimen name="secondary_call_info_vertical_padding">32dp</dimen>
- <dimen name="secondary_call_info_text_size">16sp</dimen>
-
- <!-- Padding at the top and bottom edges of the "provider information" -->
- <dimen name="provider_info_top_bottom_padding">8dp</dimen>
-
- <!-- Text sizes for information in the call card.
- Note: These are the default sizes for small (<600dp height) devices: larger screen sizes
- apply the values in values-sw360dp/dimens.xml. -->
- <dimen name="call_status_text_size">16sp</dimen>
- <dimen name="call_status_text_min_size">12sp</dimen>
- <dimen name="call_name_text_size">34dp</dimen>
- <dimen name="call_name_text_min_size">28sp</dimen>
- <dimen name="call_label_text_size">16sp</dimen>
- <!-- Right padding for name and number fields in the call banner.
- This padding is used to ensure that ultra-long names or
- numbers won't overlap the elapsed time indication. -->
- <dimen name="call_banner_name_number_right_padding">50sp</dimen>
-
- <!-- Height and width of the in-call buttons. -->
- <dimen name="in_call_button_dimension">48dp</dimen>
-
- <dimen name="primary_call_elevation">0dp</dimen>
- <dimen name="dialpad_elevation">2dp</dimen>
-
- <!-- The InCallUI dialpad will sometimes want digits sizes that are different from dialer.
- Note: These are the default sizes for small (<600dp height) devices: larger screen sizes
- apply the values in values-h600dp/dimens.xml. -->
- <dimen name="dialpad_key_number_margin_bottom">1dp</dimen>
- <!-- Zero key should have less space between self and text because "+" is smaller -->
- <dimen name="dialpad_zero_key_number_margin_bottom">0dp</dimen>
- <dimen name="dialpad_digits_adjustable_text_size">20sp</dimen>
- <dimen name="dialpad_digits_adjustable_height">50dp</dimen>
- <dimen name="dialpad_key_numbers_size">36dp</dimen>
-
- <dimen name="floating_action_button_vertical_offset">-10dp</dimen>
-
- <dimen name="call_button_margin_vertical">8dp</dimen>
- <dimen name="call_button_margin_horizontal">6dp</dimen>
-
- <!-- Dimensions for OTA Call Card -->
- <dimen name="otaactivate_layout_marginTop">10dp</dimen>
- <dimen name="otalistenprogress_layout_marginTop">5dp</dimen>
- <dimen name="otasuccessfail_layout_marginTop">10dp</dimen>
-
- <!-- Dimension used to possibly down-scale high-res photo into what is suitable
- for notification's large icon. -->
- <dimen name="notification_icon_size">64dp</dimen>
-
- <!-- Circle size for incoming call widget's each item. -->
- <dimen name="incoming_call_widget_circle_size">56dp</dimen>
-
- <!-- Size of alarm alert outer ring. -->
- <dimen name="glowpadview_outerring_diameter">250dp</dimen>
- <!-- Default target placement radius for GlowPadView. Should be 1/2 of outerring diameter. -->
- <dimen name="glowpadview_target_placement_radius">125dp</dimen>
-
- <!-- Default glow radius for GlowPadView -->
- <dimen name="glowpadview_glow_radius">70dip</dimen>
-
- <!-- Default distance beyond which GlowPadView snaps to the matching target -->
- <dimen name="glowpadview_snap_margin">40dip</dimen>
-
- <!-- Default distance from each snap target that GlowPadView considers a "hit" -->
- <dimen name="glowpadview_inner_radius">15dip</dimen>
-
- <dimen name="glowpadview_margin_bottom">-48dip</dimen>
- <dimen name="glowpadview_margin_right">0dip</dimen>
- <!-- Height of translucent shadow effect -->
- <dimen name="translucent_shadow_height">2dp</dimen>
-
- <dimen name="end_call_button_margin_bottom">2dp</dimen>
-
- <dimen name="call_card_anim_translate_y_offset">50dp</dimen>
-
- <!-- The smaller dimension of the video preview. When in portrait orientation this is the
- width of the preview. When in landscape, this is the height. -->
- <dimen name="video_preview_small_dimension">90dp</dimen>
-
- <dimen name="video_preview_margin">0dp</dimen>
-
- <dimen name="end_call_floating_action_button_diameter">72dp</dimen>
- <dimen name="end_call_floating_action_button_small_diameter">56dp</dimen>
-
- <dimen name="conference_call_manager_padding_top">64dp</dimen>
- <dimen name="conference_call_manager_button_dimension">46dp</dimen>
-
- <dimen name="contact_context_title_text_size">14sp</dimen>
- <dimen name="contact_context_title_image_size">19dp</dimen>
- <dimen name="contact_context_title_image_side_padding">23dp</dimen>
- <dimen name="contact_context_title_margin_bottom">13dp</dimen>
- <dimen name="contact_context_list_item_padding">13dp</dimen>
-
- <dimen name="person_contact_context_horizontal_padding">30dp</dimen>
- <dimen name="person_contact_context_message_text_size">16sp</dimen>
- <dimen name="person_contact_context_message_vertical_padding">7dp</dimen>
- <dimen name="person_contact_context_message_horizontal_padding">12dp</dimen>
- <dimen name="person_contact_context_message_background_main_radius">15dp</dimen>
- <dimen name="person_contact_context_message_background_accent_radius">2dp</dimen>
- <dimen name="person_contact_context_detail_padding_top">7dp</dimen>
- <dimen name="person_contact_context_detail_text_size">14sp</dimen>
-
- <dimen name="business_contact_context_end_padding">10dp</dimen>
- <dimen name="business_contact_context_image_size">25dp</dimen>
- <dimen name="business_contact_context_image_padding">20dp</dimen>
- <dimen name="business_contact_context_heading_font_size">16sp</dimen>
- <dimen name="business_contact_context_detail_font_size">12sp</dimen>
-
- <dimen name="contact_context_small_photo_size">40dp</dimen>
-</resources>
diff --git a/InCallUI/res/values/ids.xml b/InCallUI/res/values/ids.xml
deleted file mode 100644
index accb8fb73..000000000
--- a/InCallUI/res/values/ids.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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.
--->
-
-<resources>
- <item type="id" name="fadeState" />
- <item type="id" name="view_tag_callcard_actual_height" />
-</resources>
diff --git a/InCallUI/res/values/strings.xml b/InCallUI/res/values/strings.xml
deleted file mode 100644
index 92de14042..000000000
--- a/InCallUI/res/values/strings.xml
+++ /dev/null
@@ -1,540 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Official label of the phone app, as seen in "Manage Applications"
- and other settings UIs. -->
- <string name="phoneAppLabel" product="default">Phone</string>
-
- <!-- Official label for the in-call UI. DO NOT TRANSLATE. -->
- <string name="inCallLabel" translate="false">InCallUI</string>
-
- <!-- Call status -->
-
- <!-- In-call screen: status label for a call that's on hold -->
- <string name="onHold">On hold</string>
-
- <!-- Incoming call screen, name of "unknown" caller -->
- <string name="unknown">Unknown</string>
- <!-- Incoming call screen, string when number hidden -->
- <string name="private_num">Private number</string>
- <!-- Incoming call screen, string when called from a pay phone -->
- <string name="payphone">Payphone</string>
-
- <!-- In-call screen: status label for a conference call -->
- <string name="confCall">Conference call</string>
- <!-- In-call screen: call lost dialog text -->
- <string name="call_lost">Call dropped</string>
-
- <!-- MMI dialog strings -->
- <!-- Dialog label when an MMI code starts running -->
-
- <!-- "Audio mode" popup menu: Item label to select the speakerphone [CHAR LIMIT=25] -->
- <string name="audio_mode_speaker">Speaker</string>
- <!-- "Audio mode" popup menu: Item label to select the handset earpiece [CHAR LIMIT=25] -->
- <string name="audio_mode_earpiece">Handset earpiece</string>
- <!-- "Audio mode" popup menu: Item label to select the wired headset [CHAR LIMIT=25] -->
- <string name="audio_mode_wired_headset">Wired headset</string>
- <!-- "Audio mode" popup menu: Item label to select the bluetooth headset [CHAR LIMIT=25] -->
- <string name="audio_mode_bluetooth">Bluetooth</string>
-
- <!-- post dial -->
- <!-- In-call screen: body text of the dialog that appears when we encounter
- the "wait" character in a phone number to be dialed; this dialog asks the
- user if it's OK to send the numbers following the "wait". -->
- <string name="wait_prompt_str">Send the following tones?\n</string>
- <!-- In-call screen: body text of the dialog that appears when we encounter
- the "PAUSE" character in a phone number to be dialed; this dialog gives
- informative message to the user to show the sending numbers following the "Pause". -->
- <string name="pause_prompt_str">Sending tones\n</string>
- <!-- In-call screen: button label on the "wait" prompt dialog -->
- <string name="send_button">Send</string>
- <!-- In-call screen: button label on the "wait" prompt dialog in CDMA Mode-->
- <string name="pause_prompt_yes">Yes</string>
- <!-- In-call screen: button label on the "wait" prompt dialog in CDMA Mode-->
- <string name="pause_prompt_no">No</string>
- <!-- In-call screen: on the "wild" character dialog, this is the label
- for a text widget that lets the user enter the digits that should
- replace the "wild" character. -->
- <string name="wild_prompt_str">Replace wild character with</string>
-
- <!-- In-call screen: status label for a conference call -->
- <string name="caller_manage_header">Conference call <xliff:g id="conf_call_time">%s</xliff:g></string>
-
- <!-- Used in FakePhoneActivity test code. DO NOT TRANSLATE. -->
- <string name="fake_phone_activity_phoneNumber_text" translatable="false">(650) 555-1234</string>
- <!-- Used in FakePhoneActivity test code. DO NOT TRANSLATE. -->
- <string name="fake_phone_activity_infoText_text" translatable="false">Incoming phone number</string>
- <!-- Used in FakePhoneActivity test code. DO NOT TRANSLATE. -->
- <string name="fake_phone_activity_placeCall_text" translatable="false">Fake Incoming Call</string>
-
- <!-- Call settings screen, Set voicemail dialog title -->
- <string name="voicemail_settings_number_label">Voicemail number</string>
-
- <!-- Card titles -->
- <!-- In-call screen: status label for a call in the "dialing" state [CHAR LIMIT=25] -->
- <string name="card_title_dialing">Dialing</string>
- <!-- In-call screen: status label for a re-dialing call [CHAR LIMIT=25] -->
- <string name="card_title_redialing">Redialing</string>
- <!-- In-call screen: status label for a conference call [CHAR LIMIT=25] -->
- <string name="card_title_conf_call">Conference call</string>
- <!-- In-call screen: status label for an incoming call [CHAR LIMIT=25] -->
- <string name="card_title_incoming_call">Incoming call</string>
- <!-- In-call screen: status label for an incoming work call [CHAR LIMIT=25] -->
- <string name="card_title_incoming_work_call">Incoming work call</string>
- <!-- In-call screen: status label displayed briefly after a call ends [CHAR LIMIT=25] -->
- <string name="card_title_call_ended">Call ended</string>
- <!-- In-call screen: status label for call that's on hold [CHAR LIMIT=25] -->
- <string name="card_title_on_hold">On hold</string>
- <!-- In-call screen: status label for a call that's in the process of hanging up [CHAR LIMIT=25] -->
- <string name="card_title_hanging_up">Hanging up</string>
- <!-- In-call screen: status label for a call that's in CDMA flash mode [CHAR LIMIT=25] -->
- <string name="card_title_in_call">In call</string>
- <!-- In-call screen: special status label that shows your own phone
- number during emergency callback mode (ECM) [CHAR LIMIT=30] -->
- <string name="card_title_my_phone_number">My number is <xliff:g id="my_phone_number">%s</xliff:g></string>
- <!-- In-call screen: status label when connecting video. -->
- <string name="card_title_video_call_connecting">Connecting video</string>
- <!-- In-call screen: status label when in a video call. -->
- <string name="card_title_video_call">Video call</string>
- <!-- In-call screen: status label when requesting video. -->
- <string name="card_title_video_call_requesting">Requesting video</string>
- <!-- In-call screen: status label when there is a problem connecting a video call. -->
- <string name="card_title_video_call_error">Can\'t connect video call</string>
- <!-- In-call screen: status label when the remote party rejects a video call request. -->
- <string name="card_title_video_call_rejected">Video request rejected</string>
-
- <!-- In-call screen: string shown to the user when their outgoing number is different than the
- number reported by TelephonyManager#getLine1Number() -->
- <string name="card_title_callback_number">Your callback number\n
- <xliff:g id="dark_number">%1$s</xliff:g>
- </string>
-
- <!-- In-call screen: string shown to the user when their outgoing number is different than the
- number reported by TelephonyManager#getLine1Number() and they're calling emergency
- services. -->
- <string name="card_title_callback_number_emergency">Your emergency callback number\n
- <xliff:g id="dark_number">%1$s</xliff:g>
- </string>
-
- <!-- Notification strings -->
- <!-- The "label" of the in-call Notification for a dialing call, used
- as the format string for a Chronometer widget. [CHAR LIMIT=60] -->
- <string name="notification_dialing">Dialing</string>
- <!-- Missed call notification label, used when there's exactly one missed call -->
- <string name="notification_missedCallTitle">Missed call</string>
- <!-- Missed call notification label, used when there are two or more missed calls -->
- <string name="notification_missedCallsTitle">Missed calls</string>
- <!-- Missed call notification message used when there are multiple missed calls -->
- <string name="notification_missedCallsMsg"><xliff:g id="num_missed_calls">%s</xliff:g> missed calls</string>
- <!-- Missed call notification message used for a single missed call, including
- the caller-id info from the missed call -->
- <string name="notification_missedCallTicker">Missed call from <xliff:g id="missed_call_from">%s</xliff:g></string>
- <!-- The "label" of the in-call Notification for an ongoing call. [CHAR LIMIT=60] -->
- <string name="notification_ongoing_call">Ongoing call</string>
- <!-- The "label" of the in-call Notification for an ongoing work call. [CHAR LIMIT=60] -->
- <string name="notification_ongoing_work_call">Ongoing work call</string>
- <!-- The "label" of the in-call Notification for an ongoing call, which is being made over
- Wi-Fi. [CHAR LIMIT=60] -->
- <string name="notification_ongoing_call_wifi">Ongoing Wi-Fi call</string>
- <!-- The "label" of the in-call Notification for an ongoing work call, which is being made
- over Wi-Fi. [CHAR LIMIT=60] -->
- <string name="notification_ongoing_work_call_wifi">Ongoing Wi-Fi work call</string>
- <!-- The "label" of the in-call Notification for a call that's on hold -->
- <string name="notification_on_hold">On hold</string>
- <!-- The "label" of the in-call Notification for an incoming ringing call. [CHAR LIMIT=60] -->
- <string name="notification_incoming_call">Incoming call</string>
- <!-- The "label" of the in-call Notification for an incoming ringing call. [CHAR LIMIT=60] -->
- <string name="notification_incoming_work_call">Incoming work call</string>
- <!-- The "label" of the in-call Notification for an incoming ringing call,
- which is being made over Wi-Fi. [CHAR LIMIT=60] -->
- <string name="notification_incoming_call_wifi">Incoming Wi-Fi call</string>
- <!-- The "label" of the in-call Notification for an incoming ringing work call,
- which is being made over Wi-Fi. [CHAR LIMIT=60] -->
- <string name="notification_incoming_work_call_wifi">Incoming Wi-Fi work call</string>
- <!-- The "label" of the in-call Notification for an incoming ringing video call. -->
- <string name="notification_incoming_video_call">Incoming video call</string>
- <!-- The "label" of the in-call Notification for an incoming ringing spam call. -->
- <string name="notification_incoming_spam_call">Incoming suspected spam call</string>
- <!-- The "label" of the in-call Notification for upgrading an existing call to a video call. -->
- <string name="notification_requesting_video_call">Incoming video request</string>
- <!-- Label for the "Voicemail" notification item, when expanded. -->
- <string name="notification_voicemail_title">New voicemail</string>
- <!-- Label for the expanded "Voicemail" notification item,
- including a count of messages. -->
- <string name="notification_voicemail_title_count">New voicemail (<xliff:g id="count">%d</xliff:g>)</string>
- <!-- Message displayed in the "Voicemail" notification item, allowing the user
- to dial the indicated number. -->
- <string name="notification_voicemail_text_format">Dial <xliff:g id="voicemail_number">%s</xliff:g></string>
- <!-- Message displayed in the "Voicemail" notification item,
- indicating that there's no voicemail number available -->
- <string name="notification_voicemail_no_vm_number">Voicemail number unknown</string>
- <!-- Label for the "No service" notification item, when expanded. -->
- <string name="notification_network_selection_title">No service</string>
- <!-- Label for the expanded "No service" notification item, including the
- operator name set by user -->
- <string name="notification_network_selection_text">Selected network (<xliff:g id="operator_name">%s</xliff:g>) unavailable</string>
- <!-- Label for the "Answer call" action. This is the displayed label for the action that answers
- an incoming call. [CHAR LIMIT=12] -->
- <string name="notification_action_answer">Answer</string>
- <!-- Label for "end call" Action.
- It is displayed in the "Ongoing call" notification, which is shown
- when the user is outside the in-call screen while the phone call is still
- active. [CHAR LIMIT=12] -->
- <string name="notification_action_end_call">Hang up</string>
- <!-- Label for "Video Call" notification action. This is a displayed on the notification for an
- incoming video call, and answers the call as a video call. [CHAR LIMIT=12] -->
- <string name="notification_action_answer_video">Video</string>
- <!-- Label for "Voice" notification action. This is a displayed on the notification for an
- incoming video call, and answers the call as an audio call. [CHAR LIMIT=12] -->
- <string name="notification_action_answer_voice">Voice</string>
- <!-- Label for "Accept" notification action. This is somewhat generic, and may refer to
- scenarios such as accepting an incoming call or accepting a video call request.
- [CHAR LIMIT=12] -->
- <string name="notification_action_accept">Accept</string>
- <!-- Label for "Dismiss" notification action. This is somewhat generic, and may refer to
- scenarios such as declining an incoming call or declining a video call request.
- [CHAR LIMIT=12] -->
- <string name="notification_action_dismiss">Dismiss</string>
-
- <!-- Message for "call back" Action, which is displayed in the missed call notification.
- The user will be able to call back to the person or the phone number.
- [CHAR LIMIT=12] -->
- <string name="notification_missedCall_call_back">Call back</string>
- <!-- Message for "reply via sms" action, which is displayed in the missed call notification.
- The user will be able to send text messages using the phone number.
- [CHAR LIMIT=12] -->
- <string name="notification_missedCall_message">Message</string>
- <!-- The "label" of the in-call Notification for an ongoing external call.
- External calls are a representation of a call which is in progress on the user's other
- device (e.g. another phone or a watch).
- [CHAR LIMIT=60] -->
- <string name="notification_external_call">Ongoing call on another device</string>
- <!-- Notification action displayed for external call notifications. External calls are a
- representation of a call which is in progress on the user's other device (e.g. another
- phone or a watch). The "transfer call" action initiates the process of transferring an
- external call to the current device.
- [CHAR LIMIT=30] -->
- <string name="notification_transfer_call">Transfer Call</string>
-
- <!-- In-call screen: call failure message displayed in an error dialog -->
- <string name="incall_error_power_off">To place a call, first turn off Airplane mode.</string>
- <!-- In-call screen: call failure message displayed in an error dialog.
- This string is currently unused (see comments in InCallActivity.java.) -->
- <string name="incall_error_emergency_only">Not registered on network.</string>
- <!-- In-call screen: call failure message displayed in an error dialog -->
- <string name="incall_error_out_of_service">Cellular network not available.</string>
- <!-- In-call screen: call failure message displayed in an error dialog -->
- <string name="incall_error_no_phone_number_supplied">To place a call, enter a valid number.</string>
- <!-- In-call screen: call failure message displayed in an error dialog -->
- <string name="incall_error_call_failed">Can\'t call.</string>
- <!-- In-call screen: status message displayed in a dialog when starting an MMI -->
- <string name="incall_status_dialed_mmi">Starting MMI sequence\u2026</string>
- <!-- In-call screen: message displayed in an error dialog -->
- <string name="incall_error_supp_service_unknown">Service not supported.</string>
- <!-- In-call screen: message displayed in an error dialog -->
- <string name="incall_error_supp_service_switch">Can\'t switch calls.</string>
- <!-- In-call screen: message displayed in an error dialog -->
- <string name="incall_error_supp_service_separate">Can\'t separate call.</string>
- <!-- In-call screen: message displayed in an error dialog -->
- <string name="incall_error_supp_service_transfer">Can\'t transfer.</string>
- <!-- In-call screen: message displayed in an error dialog -->
- <string name="incall_error_supp_service_conference">Can\'t conference.</string>
- <!-- In-call screen: message displayed in an error dialog -->
- <string name="incall_error_supp_service_reject">Can\'t reject call.</string>
- <!-- In-call screen: message displayed in an error dialog -->
- <string name="incall_error_supp_service_hangup">Can\'t release call(s).</string>
-
- <!-- In-call screen: "call type" indication for a SIP call [CHAR LIMIT=30] -->
- <string name="incall_call_type_label_sip">SIP call</string>
-
- <!-- Dialog title for the "radio enable" UI for emergency calls -->
- <string name="emergency_enable_radio_dialog_title">Emergency call</string>
- <!-- Status message for the "radio enable" UI for emergency calls -->
- <string name="emergency_enable_radio_dialog_message">Turning on radio\u2026</string>
- <!-- Status message for the "radio enable" UI for emergency calls -->
- <string name="emergency_enable_radio_dialog_retry">No service. Trying again\u2026</string>
-
- <!-- Dialer text on Emergency Dialer -->
- <!-- Emergency dialer: message displayed in an error dialog -->
- <string name="dial_emergency_error">Can\'t call. <xliff:g id="non_emergency_number">%s</xliff:g> is not an emergency number.</string>
- <!-- Emergency dialer: message displayed in an error dialog -->
- <string name="dial_emergency_empty_error">Can\'t call. Dial an emergency number.</string>
-
- <!-- Displayed in the text entry box in the dialer when in landscape mode to guide the user
- to dial using the physical keyboard -->
- <string name="dialerKeyboardHintText">Use keyboard to dial</string>
-
- <!-- Text for the onscreen "Hold" button when it is not selected. Pressing it will put
- the call on hold. -->
- <string name="onscreenHoldText_unselected">Hold Call</string>
- <!-- Text for the onscreen "Hold" button when it is selected. Pressing it will resume
- the call from a previously held state. -->
- <string name="onscreenHoldText_selected">Resume Call</string>
- <!-- Text for the onscreen "End call" button -->
- <string name="onscreenEndCallText">End Call</string>
- <!-- Text for the onscreen "Show Dialpad" button when it is not selected. Pressing it will
- show the dialpad. -->
- <string name="onscreenShowDialpadText_unselected">Show Dialpad</string>
- <!-- Text for the onscreen "Show Dialpad" button when it is selected. Pressing it will
- hide the dialpad. -->
- <string name="onscreenShowDialpadText_selected">Hide Dialpad</string>
- <!-- Text for the onscreen "Mute" button when it is not selected. Pressing it will mute
- the call. -->
- <string name="onscreenMuteText_unselected">Mute</string>
- <!-- Text for the onscreen "Mute" button when it is selected. Pressing it will unmute
- the call. -->
- <string name="onscreenMuteText_selected">Unmute</string>
- <!-- Text for the onscreen "Add call" button -->
- <string name="onscreenAddCallText">Add call</string>
- <!-- Text for the onscreen "Merge calls" button -->
- <string name="onscreenMergeCallsText">Merge calls</string>
- <!-- Text for the onscreen "Swap calls" button -->
- <string name="onscreenSwapCallsText">Swap</string>
- <!-- Text for the onscreen "Manage calls" button -->
- <string name="onscreenManageCallsText">Manage calls</string>
- <!-- Text for the onscreen "Manage conference" button [CHAR LIMIT=30] -->
- <string name="onscreenManageConferenceText">Manage conference call</string>
- <!-- Text for the first half of the onscreen "Manage conference" button [CHAR LIMIT=30] -->
- <string name="onscreenConferenceText">Conference call</string>
- <!-- Text for the second half of the onscreen "Manage conference" button [CHAR LIMIT=30] -->
- <string name="onscreenManageText">Manage</string>
- <!-- Text for the onscreen "Audio" button that lets you switch
- between speaker / bluetooth / earpiece [CHAR LIMIT=10] -->
- <string name="onscreenAudioText">Audio</string>
- <!-- Text for the onscreen "Video call" button used to change a voice call
- to a video call. [CHAR LIMIT=10] -->
- <string name="onscreenVideoCallText">Video call</string>
- <!-- Text for the onscreen "Change to voice" button. When clicked, this downgrades a video call
- to a voice call. -->
- <string name="onscreenChangeToVoiceText">Change to voice call</string>
- <!-- Text for the onscreen "Switch camera" button. When clicked, this switches the user's camera
- for video calling between the front-facing camera and the back-facing camera. -->
- <string name="onscreenSwitchCameraText">Switch camera</string>
- <!-- Text for the onscreen "turn on camera" button. -->
- <string name="onscreenTurnOnCameraText">Turn on camera</string>
- <!-- Text for the onscreen "turn off camera" button. -->
- <string name="onscreenTurnOffCameraText">Turn off camera</string>
- <!-- Text for the onscreen overflow button, to see additional actions which can be done. -->
- <string name="onscreenOverflowText">More options</string>
-
- <!-- Message indicating that Video Started flowing for IMS-VT calls -->
- <string name="player_started">Player Started</string>
- <!-- Message indicating that Video Stopped flowing for IMS-VT calls -->
- <string name="player_stopped">Player Stopped</string>
- <!-- Message indicating that camera failure has occurred for the selected camera and
- as result camera is not ready -->
- <string name="camera_not_ready">Camera not ready</string>
- <!-- Message indicating that camera is ready/available -->
- <string name="camera_ready">Camera ready</string>
- <!-- Message indicating unknown call session event -->
- <string name="unknown_call_session_event">"Unkown call session event"</string>
-
- <!-- For incoming calls, this is a string we can get from a CDMA network instead of
- the actual phone number, to indicate there's no number present. DO NOT TRANSLATE. -->
- <string-array name="absent_num" translatable="false">
- <item>ABSENT NUMBER</item>
- <item>ABSENTNUMBER</item>
- </string-array>
-
- <!-- Preference for Voicemail service provider under "Voicemail" settings.
- [CHAR LIMIT=40] -->
- <string name="voicemail_provider">Service</string>
-
- <!-- Preference for Voicemail setting of each provider.
- [CHAR LIMIT=40] -->
- <string name="voicemail_settings">Setup</string>
-
- <!-- String to display in voicemail number summary when no voicemail num is set -->
- <string name="voicemail_number_not_set">&lt;Not set&gt;</string>
-
- <!-- Title displayed above settings coming after voicemail in the call features screen -->
- <string name="other_settings">Other call settings</string>
-
- <!-- Title displayed in the overlay for outgoing calls which include the name of the provider.
- [CHAR LIMIT=40] -->
- <string name="calling_via_template">Calling via <xliff:g id="provider_name">%s</xliff:g></string>
- <!-- Title displayed in the overlay for incoming calls which include the name of the provider.
- [CHAR LIMIT=40] -->
- <string name="incoming_via_template">Incoming via <xliff:g id="provider_name">%s</xliff:g></string>
-
- <!-- Use this as a default to describe the contact photo; currently for screen readers through accessibility. -->
- <string name="contactPhoto">contact photo</string>
- <!-- Use this to describe the separate conference call button; currently for screen readers through accessibility. -->
- <string name="goPrivate">go private</string>
- <!-- Use this to describe the select contact button in EditPhoneNumberPreference; currently for screen readers through accessibility. -->
- <string name="selectContact">select contact</string>
-
- <!-- "Respond via SMS" option that lets you compose a custom response. [CHAR LIMIT=30] -->
- <string name="respond_via_sms_custom_message">Write your own...</string>
- <!-- "Custom Message" Cancel alert dialog button -->
- <string name="custom_message_cancel">Cancel</string>
- <!-- "Custom Message" Send alert dialog button -->
- <string name="custom_message_send">Send</string>
-
- <!-- Description of the answer target in the Slide unlock screen of Phone. [CHAR LIMIT=NONE] -->
- <string name="description_target_answer">Answer</string>
- <!-- Description of the send_sms target in the Slide unlock screen of Phone. [CHAR LIMIT=NONE] -->
- <string name="description_target_send_sms">Send SMS</string>
- <!-- Description of the decline target in the Slide unlock screen. [CHAR LIMIT=NONE] -->
- <string name="description_target_decline">Decline</string>
- <!-- Description of the target to answer a call as a video call in the Slide unlock screen.
- [CHAR LIMIT=NONE] -->
- <string name="description_target_answer_video_call">Answer as video call</string>
- <!-- Description of the target to answer a call as an audio call in the Slide unlock screen.
- [CHAR LIMIT=NONE] -->
- <string name="description_target_answer_audio_call">Answer as audio call</string>
- <!-- Description of the target to accept a request to upgrade from an audio call to a video call.
- [CHAR LIMIT=NONE] -->
- <string name="description_target_accept_upgrade_to_video_request">Accept video request</string>
- <!-- Description of the target to decline a request to upgrade from an audio call to a video call.
- [CHAR LIMIT=NONE] -->
- <string name="description_target_decline_upgrade_to_video_request">Decline video request</string>
- <!-- Description of the target to accept a request to upgrade from any call to a video transmit call.
- [CHAR LIMIT=NONE] -->
- <string name="description_target_accept_upgrade_to_video_transmit_request">Accept video transmit request</string>
- <!-- Description of the target to decline a request to upgrade from any call to a video transmit call.
- [CHAR LIMIT=NONE] -->
- <string name="description_target_decline_upgrade_to_video_transmit_request">Decline video transmit request</string>
- <!-- Description of the target to accept a request to upgrade from any call to a video receive call.
- [CHAR LIMIT=NONE] -->
- <string name="description_target_accept_upgrade_to_video_receive_request">Accept video receive request</string>
- <!-- Description of the target to decline a request to upgrade from any call to a video receive call.
- [CHAR LIMIT=NONE] -->
- <string name="description_target_decline_upgrade_to_video_receive_request">Decline video receive request</string>
-
- <!-- Description of the up direction in which one can to slide the handle in the phone answer screen. [CHAR LIMIT=NONE] -->
- <string name="description_direction_up">Slide up for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
- <!-- Description of the left direction in which one can to slide the handle in the phone answer screen. [CHAR LIMIT=NONE] -->
- <string name="description_direction_left">"Slide left for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
- <!-- Description of the right direction in which one can to slide the handle in the phone answer screen. [CHAR LIMIT=NONE] -->
- <string name="description_direction_right">Slide right for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
- <!-- Description of the down direction in which one can to slide the handle in the phone answer screen. [CHAR LIMIT=NONE] -->
- <string name="description_direction_down">Slide down for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
-
- <!-- Dialog title for the vibration settings for voicemail notifications [CHAR LIMIT=40] -->
- <string name="voicemail_notification_vibrate_when_title" msgid="8731372580674292759">Vibrate</string>
- <!-- Dialog title for the vibration settings for voice mail notifications [CHAR LIMIT=40]-->
- <string name="voicemail_notification_vibarte_when_dialog_title" msgid="8995274609647451109">Vibrate</string>
-
- <!-- Voicemail ringtone title. The user clicks on this preference to select
- which sound to play when a voicemail notification is received.
- [CHAR LIMIT=30] -->
- <string name="voicemail_notification_ringtone_title">Sound</string>
-
- <!-- The string used to describe a notification if it is the default one in the system. For
- example, if the user selects the default notification, it will appear as something like
- Default sound(Capella) in the notification summary.
- [CHAR LIMIT=40] -->
- <string name="default_notification_description">Default sound (<xliff:g id="default_sound_title">%1$s</xliff:g>)</string>
-
- <!-- The default value value for voicemail notification. -->
- <string name="voicemail_notification_vibrate_when_default" translatable="false">never</string>
-
- <!-- Actual values used in our code for voicemail notifications. DO NOT TRANSLATE -->
- <string-array name="voicemail_notification_vibrate_when_values" translatable="false">
- <item>always</item>
- <item>silent</item>
- <item>never</item>
- </string-array>
-
- <!-- Setting option name to pick ringtone (a list dialog comes up). [CHAR LIMIT=30] -->
- <string name="ringtone_title" msgid="5379026328015343686">Phone ringtone</string>
-
- <!-- Setting option name to enable or disable vibration when ringing
- the phone.
- [CHAR LIMIT=30] -->
- <string name="vibrate_on_ring_title">Vibrate when ringing</string>
-
- <!-- Title for the category "ringtone", which is shown above ringtone and vibration
- related settings.
- [CHAR LIMIT=30] -->
- <string name="preference_category_ringtone">Ringtone &amp; Vibrate</string>
-
- <!-- Label for "Manage conference call" panel [CHAR LIMIT=40] -->
- <string name="manageConferenceLabel">Manage conference call</string>
-
- <!-- This can be used in any application wanting to disable the text "Emergency number" -->
- <string name="emergency_call_dialog_number_for_display">Emergency number</string>
-
- <!-- Description of the profile photo shown when the device's camera is disabled udring a video
- call. [CHAR LIMIT=NONE] -->
- <string name="profile_photo_description">Profile photo</string>
-
- <!-- Description of the "camera off" icon displayed when the device's camera is disabled during
- a video call. [CHAR LIMIT=NONE] -->
- <string name="camera_off_description">Camera off</string>
-
- <!-- Used to inform the user that a call was received via a number other than the primary
- phone number associated with their device. [CHAR LIMIT=16] -->
- <string name="child_number">via <xliff:g id="child_number" example="650-555-1212">%s</xliff:g></string>
-
- <!-- Used to inform the user that the note associated with an outgoing call has been sent.
- [CHAR LIMIT=32] -->
- <string name="note_sent">Note sent</string>
-
- <!-- Title for the call context with a person-type contact. [CHAR LIMIT=40] -->
- <string name="person_contact_context_title">Recent messages</string>
-
- <!-- Title for the call context with a business-type contact. [CHAR LIMIT=40] -->
- <string name="business_contact_context_title">Business info</string>
-
- <!-- Distance strings for business caller ID context. -->
-
- <!-- Used to inform the user how far away a location is in miles. [CHAR LIMIT=NONE] -->
- <string name="distance_imperial_away"><xliff:g id="distance">%.1f</xliff:g> mi away</string>
- <!-- Used to inform the user how far away a location is in kilometers. [CHAR LIMIT=NONE] -->
- <string name="distance_metric_away"><xliff:g id="distance">%.1f</xliff:g> km away</string>
- <!-- A shortened way to display a business address. Formatted [street address], [city/locality]. -->
- <string name="display_address"><xliff:g id="street_address">%1$s</xliff:g>, <xliff:g id="locality">%2$s</xliff:g></string>
- <!-- Used to indicate hours of operation for a location as a time span. e.g. "11 am - 9 pm" [CHAR LIMIT=NONE] -->
- <string name="open_time_span"><xliff:g id="open_time">%1$s</xliff:g> - <xliff:g id="close_time">%2$s</xliff:g></string>
- <!-- Used to indicate a series of opening hours for a location.
- This first argument may be one or more time spans. e.g. "11 am - 9 pm, 9 pm - 11 pm"
- The second argument is an additional time span. e.g. "11 pm - 1 am"
- The string is used to build a list of opening hours.
- [CHAR LIMIT=NONE] -->
- <string name="opening_hours"><xliff:g id="earlier_times">%1$s</xliff:g>, <xliff:g id="later_time">%2$s</xliff:g></string>
- <!-- Used to express when a location will open the next day. [CHAR LIMIT=NONE] -->
- <string name="opens_tomorrow_at">Opens tomorrow at <xliff:g id="open_time">%s</xliff:g></string>
- <!-- Used to express the next time at which a location will be open today. [CHAR LIMIT=NONE] -->
- <string name="opens_today_at">Opens today at <xliff:g id="open_time">%s</xliff:g></string>
- <!-- Used to express the next time at which a location will close today. [CHAR LIMIT=NONE] -->
- <string name="closes_today_at">Closes at <xliff:g id="close_time">%s</xliff:g></string>
- <!-- Used to express the next time at which a location closed today if it is already closed. [CHAR LIMIT=NONE] -->
- <string name="closed_today_at">Closed today at <xliff:g id="close_time">%s</xliff:g></string>
- <!-- Displayed when a place is open. -->
- <string name="open_now">Open now</string>
- <!-- Displayed when a place is closed. -->
- <string name="closed_now">Closed now</string>
- <!-- Label for spam call in primary info. [CHAR LIMIT=20] -->
- <string name="label_spam_caller">Suspected spam caller</string>
-
- <!-- Title for the notification to the user after a call from an unknown number ends. [CHAR LIMIT=100] -->
- <string name="spam_notification_title">Call ended %1$s</string>
- <!-- Text displayed in the notification to the user after a non-spam call ends. [CHAR LIMIT=100] -->
- <string name="spam_notification_non_spam_call_text">This is the first time this number called you.</string>
- <!-- Text displayed in the notification to the user after a spam call ends. [CHAR LIMIT=100] -->
- <string name="spam_notification_spam_call_text">We suspected this call to be a spammer.</string>
- <!-- Text for the reporting spam action in the after call prompt. [CHAR LIMIT=20] -->
- <string name="spam_notification_report_spam_action_text">Block/report spam</string>
- <!-- Text for the adding to contacts action in the after call prompt. [CHAR LIMIT=20] -->
- <string name="spam_notification_add_contact_action_text">Add contact</string>
- <!-- Text for the reporting as not spam action in the after call prompt. [CHAR LIMIT=20] -->
- <string name="spam_notification_not_spam_action_text">Not spam</string>
-</resources>
diff --git a/InCallUI/res/values/styles.xml b/InCallUI/res/values/styles.xml
deleted file mode 100644
index 11d636261..000000000
--- a/InCallUI/res/values/styles.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2013 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
- -->
-
-<resources>
- <drawable name="grayBg">#FF333333</drawable>
-
- <style name="info_label">
- <item name="android:layout_height">wrap_content</item>
- <item name="android:layout_width">wrap_content</item>
- <item name="android:textAppearance">@style/TextAppearance.info_label</item>
- <item name="android:paddingEnd">4dip</item>
- </style>
-
- <style name="info_layout">
- <item name="android:orientation">vertical</item>
- <item name="android:paddingStart">10dip</item>
- <item name="android:paddingTop">10dip</item>
- <item name="android:paddingEnd">10dip</item>
- <item name="android:paddingBottom">10dip</item>
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">match_parent</item>
- </style>
-
- <style name="entry_layout">
- <item name="android:orientation">vertical</item>
- <item name="android:layout_width">wrap_content</item>
- <item name="android:layout_height">wrap_content</item>
- </style>
-
- <style name="TextAppearance" parent="android:TextAppearance">
- </style>
-
- <style name="TextAppearance.info_label">
- <item name="android:textSize">14sp</item>
- <item name="android:textStyle">bold</item>
- </style>
-
-
- <!-- Buttons in the main "button row" of the in-call onscreen touch UI. -->
- <style name="InCallButton">
- <item name="android:layout_width">@dimen/in_call_button_dimension</item>
- <item name="android:layout_height">@dimen/in_call_button_dimension</item>
- <item name="android:layout_marginBottom">@dimen/call_button_margin_vertical</item>
- <item name="android:layout_marginLeft">@dimen/call_button_margin_horizontal</item>
- <item name="android:layout_marginRight">@dimen/call_button_margin_horizontal</item>
- <item name="android:background">?android:attr/selectableItemBackground</item>
- </style>
-
- <!-- "Compound button" variation of InCallButton.
- These buttons have the concept of two states: checked and unchecked. This style is just
- like "InCallButton" except that we also clear out android:background, android:textOn,
- android:textOff, to avoid the default behavior of the ToggleButton class. -->
- <style name="InCallCompoundButton" parent="InCallButton">
- <item name="android:background">@null</item>
- <item name="android:textOn">@null</item>
- <item name="android:textOff">@null</item>
- </style>
-
- <!-- Theme for the InCallActivity activity. Should have a transparent background for the
- circular reveal animation for a new outgoing call to work correctly. We don't just use
- Theme.Black.NoTitleBar directly, since we want any popups or dialogs from the
- InCallActivity to have the correct Material style. -->
- <style name="Theme.InCallScreen" parent="@android:style/Theme.Material.Light">
- <item name="android:windowAnimationStyle">@null</item>
- <item name="android:windowIsTranslucent">true</item>
- <item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:windowContentOverlay">@null</item>
- <item name="dialpad_key_button_touch_tint">@color/incall_dialpad_touch_tint</item>
- <item name="android:textColorPrimary">@color/incall_call_banner_text_color</item>
- <item name="android:colorPrimaryDark">@color/dialer_theme_color_dark</item>
- <item name="android:popupMenuStyle">@style/InCallPopupMenuStyle</item>
- <item name="android:actionBarStyle">@style/InCallActionBarStyle</item>
- <item name="android:buttonStyleToggle">@style/InCallCompoundButton</item>
- <item name="android:alertDialogTheme">@android:style/Theme.Material.Light.Dialog.Alert</item>
- </style>
-
- <style name="InCallPopupMenuStyle" parent="@android:style/Theme.Material.Light">
- <item name="android:textColorPrimary">@color/popup_menu_color</item>
- </style>
-
- <style name="InCallActionBarStyle" parent="@android:style/Widget.Material.Light.ActionBar">
- <item name="android:background">@color/incall_action_bar_background_color</item>
- <item name="android:textColor">@color/incall_action_bar_text_color</item>
- </style>
-
-</resources>
diff --git a/InCallUI/src/com/android/incallui/AccelerometerListener.java b/InCallUI/src/com/android/incallui/AccelerometerListener.java
deleted file mode 100644
index b5ad29675..000000000
--- a/InCallUI/src/com/android/incallui/AccelerometerListener.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2009 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.incallui;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-
-/**
- * This class is used to listen to the accelerometer to monitor the
- * orientation of the phone. The client of this class is notified when
- * the orientation changes between horizontal and vertical.
- */
-public class AccelerometerListener {
- private static final String TAG = "AccelerometerListener";
- private static final boolean DEBUG = true;
- private static final boolean VDEBUG = false;
-
- private SensorManager mSensorManager;
- private Sensor mSensor;
-
- // mOrientation is the orientation value most recently reported to the client.
- private int mOrientation;
-
- // mPendingOrientation is the latest orientation computed based on the sensor value.
- // This is sent to the client after a rebounce delay, at which point it is copied to
- // mOrientation.
- private int mPendingOrientation;
-
- private OrientationListener mListener;
-
- // Device orientation
- public static final int ORIENTATION_UNKNOWN = 0;
- public static final int ORIENTATION_VERTICAL = 1;
- public static final int ORIENTATION_HORIZONTAL = 2;
-
- private static final int ORIENTATION_CHANGED = 1234;
-
- private static final int VERTICAL_DEBOUNCE = 100;
- private static final int HORIZONTAL_DEBOUNCE = 500;
- private static final double VERTICAL_ANGLE = 50.0;
-
- public interface OrientationListener {
- public void orientationChanged(int orientation);
- }
-
- public AccelerometerListener(Context context) {
- mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
- mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- }
-
- public void setListener(OrientationListener listener) {
- mListener = listener;
- }
-
- public void enable(boolean enable) {
- if (DEBUG) Log.d(TAG, "enable(" + enable + ")");
- synchronized (this) {
- if (enable) {
- mOrientation = ORIENTATION_UNKNOWN;
- mPendingOrientation = ORIENTATION_UNKNOWN;
- mSensorManager.registerListener(mSensorListener, mSensor,
- SensorManager.SENSOR_DELAY_NORMAL);
- } else {
- mSensorManager.unregisterListener(mSensorListener);
- mHandler.removeMessages(ORIENTATION_CHANGED);
- }
- }
- }
-
- private void setOrientation(int orientation) {
- synchronized (this) {
- if (mPendingOrientation == orientation) {
- // Pending orientation has not changed, so do nothing.
- return;
- }
-
- // Cancel any pending messages.
- // We will either start a new timer or cancel alltogether
- // if the orientation has not changed.
- mHandler.removeMessages(ORIENTATION_CHANGED);
-
- if (mOrientation != orientation) {
- // Set timer to send an event if the orientation has changed since its
- // previously reported value.
- mPendingOrientation = orientation;
- final Message m = mHandler.obtainMessage(ORIENTATION_CHANGED);
- // set delay to our debounce timeout
- int delay = (orientation == ORIENTATION_VERTICAL ? VERTICAL_DEBOUNCE
- : HORIZONTAL_DEBOUNCE);
- mHandler.sendMessageDelayed(m, delay);
- } else {
- // no message is pending
- mPendingOrientation = ORIENTATION_UNKNOWN;
- }
- }
- }
-
- private void onSensorEvent(double x, double y, double z) {
- if (VDEBUG) Log.d(TAG, "onSensorEvent(" + x + ", " + y + ", " + z + ")");
-
- // If some values are exactly zero, then likely the sensor is not powered up yet.
- // ignore these events to avoid false horizontal positives.
- if (x == 0.0 || y == 0.0 || z == 0.0) return;
-
- // magnitude of the acceleration vector projected onto XY plane
- final double xy = Math.hypot(x, y);
- // compute the vertical angle
- double angle = Math.atan2(xy, z);
- // convert to degrees
- angle = angle * 180.0 / Math.PI;
- final int orientation = (angle > VERTICAL_ANGLE ? ORIENTATION_VERTICAL : ORIENTATION_HORIZONTAL);
- if (VDEBUG) Log.d(TAG, "angle: " + angle + " orientation: " + orientation);
- setOrientation(orientation);
- }
-
- SensorEventListener mSensorListener = new SensorEventListener() {
- @Override
- public void onSensorChanged(SensorEvent event) {
- onSensorEvent(event.values[0], event.values[1], event.values[2]);
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // ignore
- }
- };
-
- Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case ORIENTATION_CHANGED:
- synchronized (this) {
- mOrientation = mPendingOrientation;
- if (DEBUG) {
- Log.d(TAG, "orientation: " +
- (mOrientation == ORIENTATION_HORIZONTAL ? "horizontal"
- : (mOrientation == ORIENTATION_VERTICAL ? "vertical"
- : "unknown")));
- }
- if (mListener != null) {
- mListener.orientationChanged(mOrientation);
- }
- }
- break;
- }
- }
- };
-}
diff --git a/InCallUI/src/com/android/incallui/AccessibleAnswerFragment.java b/InCallUI/src/com/android/incallui/AccessibleAnswerFragment.java
deleted file mode 100644
index 89c78ec61..000000000
--- a/InCallUI/src/com/android/incallui/AccessibleAnswerFragment.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.os.Bundle;
-import android.telecom.VideoProfile;
-import android.view.GestureDetector;
-import android.view.GestureDetector.SimpleOnGestureListener;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.dialer.R;
-
-/**
- * AnswerFragment to use when touch exploration is enabled in accessibility.
- */
-public class AccessibleAnswerFragment extends AnswerFragment {
-
- private static final String TAG = AccessibleAnswerFragment.class.getSimpleName();
- private static final int SWIPE_THRESHOLD = 100;
-
- private View mAnswer;
- private View mDecline;
- private View mText;
-
- private TouchListener mTouchListener;
- private GestureDetector mGestureDetector;
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- ViewGroup group = (ViewGroup) inflater.inflate(R.layout.accessible_answer_fragment,
- container, false);
-
- mTouchListener = new TouchListener();
- mGestureDetector = new GestureDetector(getContext(), new SimpleOnGestureListener() {
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
- float velocityY) {
- return AccessibleAnswerFragment.this.onFling(e1, e2, velocityX, velocityX);
- }
- });
-
- mAnswer = group.findViewById(R.id.accessible_answer_fragment_answer);
- mAnswer.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Log.d(TAG, "Answer Button Clicked");
- onAnswer(VideoProfile.STATE_AUDIO_ONLY, getContext());
- }
- });
- mDecline = group.findViewById(R.id.accessible_answer_fragment_decline);
- mDecline.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Log.d(TAG, "Decline Button Clicked");
- onDecline(getContext());
- }
- });
-
- mText = group.findViewById(R.id.accessible_answer_fragment_text);
- mText.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Log.d(TAG, "Text Button Clicked");
- onText();
- }
- });
- return group;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- // Intercept all touch events for full screen swiping gesture.
- InCallActivity activity = (InCallActivity) getActivity();
- activity.setDispatchTouchEventListener(mTouchListener);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- InCallActivity activity = (InCallActivity) getActivity();
- activity.setDispatchTouchEventListener(null);
- }
-
- private class TouchListener implements View.OnTouchListener {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- return mGestureDetector.onTouchEvent(event);
- }
- }
-
- private boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
- float velocityY) {
- if (hasPendingDialogs()) {
- return false;
- }
-
- float diffY = e2.getY() - e1.getY();
- float diffX = e2.getX() - e1.getX();
- if (Math.abs(diffX) > Math.abs(diffY)) {
- if (Math.abs(diffX) > SWIPE_THRESHOLD) {
- if (diffX > 0) {
- onSwipeRight();
- } else {
- onSwipeLeft();
- }
- }
- return true;
- } else if (Math.abs(diffY) > SWIPE_THRESHOLD) {
- if (diffY > 0) {
- onSwipeDown();
- } else {
- onSwipeUp();
- }
- return true;
- }
-
- return false;
- }
-
- private void onSwipeUp() {
- Log.d(TAG, "onSwipeUp");
- onText();
- }
-
- private void onSwipeDown() {
- Log.d(TAG, "onSwipeDown");
- }
-
- private void onSwipeLeft() {
- Log.d(TAG, "onSwipeLeft");
- onDecline(getContext());
- }
-
- private void onSwipeRight() {
- Log.d(TAG, "onSwipeRight");
- onAnswer(VideoProfile.STATE_AUDIO_ONLY, getContext());
- }
-}
diff --git a/InCallUI/src/com/android/incallui/AnswerFragment.java b/InCallUI/src/com/android/incallui/AnswerFragment.java
deleted file mode 100644
index 44ddfcd49..000000000
--- a/InCallUI/src/com/android/incallui/AnswerFragment.java
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ListView;
-
-import com.android.dialer.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-
-/**
- * Provides only common interface and functions. Should be derived to implement the actual UI.
- */
-public abstract class AnswerFragment extends BaseFragment<AnswerPresenter, AnswerPresenter.AnswerUi>
- implements AnswerPresenter.AnswerUi {
-
- public static final int TARGET_SET_FOR_AUDIO_WITHOUT_SMS = 0;
- public static final int TARGET_SET_FOR_AUDIO_WITH_SMS = 1;
- public static final int TARGET_SET_FOR_VIDEO_WITHOUT_SMS = 2;
- public static final int TARGET_SET_FOR_VIDEO_WITH_SMS = 3;
- public static final int TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST = 4;
-
- /**
- * This fragment implement no UI at all. Derived class should do it.
- */
- @Override
- public abstract View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState);
-
- /**
- * The popup showing the list of canned responses.
- *
- * This is an AlertDialog containing a ListView showing the possible choices. This may be null
- * if the InCallScreen hasn't ever called showRespondViaSmsPopup() yet, or if the popup was
- * visible once but then got dismissed.
- */
- private Dialog mCannedResponsePopup = null;
-
- /**
- * The popup showing a text field for users to type in their custom message.
- */
- private AlertDialog mCustomMessagePopup = null;
-
- private ArrayAdapter<String> mSmsResponsesAdapter;
-
- private final List<String> mSmsResponses = new ArrayList<>();
-
- @Override
- public AnswerPresenter createPresenter() {
- return InCallPresenter.getInstance().getAnswerPresenter();
- }
-
- @Override
- public AnswerPresenter.AnswerUi getUi() {
- return this;
- }
-
- @Override
- public void showMessageDialog() {
- final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-
- mSmsResponsesAdapter = new ArrayAdapter<>(builder.getContext(),
- android.R.layout.simple_list_item_1, android.R.id.text1, mSmsResponses);
-
- final ListView lv = new ListView(getActivity());
- lv.setAdapter(mSmsResponsesAdapter);
- lv.setOnItemClickListener(new RespondViaSmsItemClickListener());
-
- builder.setCancelable(true).setView(lv).setOnCancelListener(
- new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialogInterface) {
- onMessageDialogCancel();
- dismissCannedResponsePopup();
- getPresenter().onDismissDialog();
- }
- });
- mCannedResponsePopup = builder.create();
- mCannedResponsePopup.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
- mCannedResponsePopup.show();
- }
-
- private boolean isCannedResponsePopupShowing() {
- if (mCannedResponsePopup != null) {
- return mCannedResponsePopup.isShowing();
- }
- return false;
- }
-
- private boolean isCustomMessagePopupShowing() {
- if (mCustomMessagePopup != null) {
- return mCustomMessagePopup.isShowing();
- }
- return false;
- }
-
- /**
- * Dismiss the canned response list popup.
- *
- * This is safe to call even if the popup is already dismissed, and even if you never called
- * showRespondViaSmsPopup() in the first place.
- */
- protected void dismissCannedResponsePopup() {
- if (mCannedResponsePopup != null) {
- mCannedResponsePopup.dismiss(); // safe even if already dismissed
- mCannedResponsePopup = null;
- }
- }
-
- /**
- * Dismiss the custom compose message popup.
- */
- private void dismissCustomMessagePopup() {
- if (mCustomMessagePopup != null) {
- mCustomMessagePopup.dismiss();
- mCustomMessagePopup = null;
- }
- }
-
- public void dismissPendingDialogs() {
- if (isCannedResponsePopupShowing()) {
- dismissCannedResponsePopup();
- }
-
- if (isCustomMessagePopupShowing()) {
- dismissCustomMessagePopup();
- }
- }
-
- public boolean hasPendingDialogs() {
- return !(mCannedResponsePopup == null && mCustomMessagePopup == null);
- }
-
- /**
- * Shows the custom message entry dialog.
- */
- public void showCustomMessageDialog() {
- // Create an alert dialog containing an EditText
- final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- final EditText et = new EditText(builder.getContext());
- builder.setCancelable(true).setView(et)
- .setPositiveButton(R.string.custom_message_send,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- // The order is arranged in a way that the popup will be destroyed
- // when the InCallActivity is about to finish.
- final String textMessage = et.getText().toString().trim();
- dismissCustomMessagePopup();
- getPresenter().rejectCallWithMessage(textMessage);
- }
- })
- .setNegativeButton(R.string.custom_message_cancel,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dismissCustomMessagePopup();
- getPresenter().onDismissDialog();
- }
- })
- .setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialogInterface) {
- dismissCustomMessagePopup();
- getPresenter().onDismissDialog();
- }
- })
- .setTitle(R.string.respond_via_sms_custom_message);
- mCustomMessagePopup = builder.create();
-
- // Enable/disable the send button based on whether there is a message in the EditText
- et.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- final Button sendButton = mCustomMessagePopup.getButton(
- DialogInterface.BUTTON_POSITIVE);
- sendButton.setEnabled(s != null && s.toString().trim().length() != 0);
- }
- });
-
- // Keyboard up, show the dialog
- mCustomMessagePopup.getWindow().setSoftInputMode(
- WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
- mCustomMessagePopup.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
- mCustomMessagePopup.show();
-
- // Send button starts out disabled
- final Button sendButton = mCustomMessagePopup.getButton(DialogInterface.BUTTON_POSITIVE);
- sendButton.setEnabled(false);
- }
-
- @Override
- public void configureMessageDialog(List<String> textResponses) {
- mSmsResponses.clear();
- mSmsResponses.addAll(textResponses);
- mSmsResponses.add(getResources().getString(
- R.string.respond_via_sms_custom_message));
- if (mSmsResponsesAdapter != null) {
- mSmsResponsesAdapter.notifyDataSetChanged();
- }
- }
-
- @Override
- public Context getContext() {
- return getActivity();
- }
-
- public void onAnswer(int videoState, Context context) {
- Log.d(this, "onAnswer videoState=" + videoState + " context=" + context);
- getPresenter().onAnswer(videoState, context);
- }
-
- public void onDecline(Context context) {
- getPresenter().onDecline(context);
- }
-
- public void onDeclineUpgradeRequest(Context context) {
- InCallPresenter.getInstance().declineUpgradeRequest(context);
- }
-
- public void onText() {
- getPresenter().onText();
- }
-
- /**
- * OnItemClickListener for the "Respond via SMS" popup.
- */
- public class RespondViaSmsItemClickListener implements AdapterView.OnItemClickListener {
-
- /**
- * Handles the user selecting an item from the popup.
- */
- @Override
- public void onItemClick(AdapterView<?> parent, // The ListView
- View view, // The TextView that was clicked
- int position, long id) {
- Log.d(this, "RespondViaSmsItemClickListener.onItemClick(" + position + ")...");
- final String message = (String) parent.getItemAtPosition(position);
- Log.v(this, "- message: '" + message + "'");
- dismissCannedResponsePopup();
-
- // The "Custom" choice is a special case.
- // (For now, it's guaranteed to be the last item.)
- if (position == (parent.getCount() - 1)) {
- // Show the custom message dialog
- showCustomMessageDialog();
- } else {
- getPresenter().rejectCallWithMessage(message);
- }
- }
- }
-
- public void onShowAnswerUi(boolean shown) {
- // Do Nothing
- }
-
- public void showTargets(int targetSet) {
- // Do Nothing
- }
-
- public void showTargets(int targetSet, int videoState) {
- // Do Nothing
- }
-
- protected void onMessageDialogCancel() {
- // Do nothing.
- }
-}
diff --git a/InCallUI/src/com/android/incallui/AnswerPresenter.java b/InCallUI/src/com/android/incallui/AnswerPresenter.java
deleted file mode 100644
index 883b54fed..000000000
--- a/InCallUI/src/com/android/incallui/AnswerPresenter.java
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.content.Context;
-
-import com.android.dialer.compat.UserManagerCompat;
-import com.android.dialer.util.TelecomUtil;
-import com.android.incallui.InCallPresenter.InCallState;
-
-import java.util.List;
-
-/**
- * Presenter for the Incoming call widget. The {@link AnswerPresenter} handles the logic during
- * incoming calls. It is also in charge of responding to incoming calls, so there needs to be
- * an instance alive so that it can receive onIncomingCall callbacks.
- *
- * An instance of {@link AnswerPresenter} is created by InCallPresenter at startup, registers
- * for callbacks via InCallPresenter, and shows/hides the {@link AnswerFragment} via IncallActivity.
- *
- */
-public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi>
- implements CallList.CallUpdateListener, InCallPresenter.InCallUiListener,
- InCallPresenter.IncomingCallListener,
- CallList.Listener {
-
- private static final String TAG = AnswerPresenter.class.getSimpleName();
-
- private String mCallId;
- private Call mCall = null;
- private boolean mHasTextMessages = false;
-
- @Override
- public void onUiShowing(boolean showing) {
- if (showing) {
- CallList.getInstance().addListener(this);
- final CallList calls = CallList.getInstance();
- Call call;
- call = calls.getIncomingCall();
- if (call != null) {
- processIncomingCall(call);
- }
- call = calls.getVideoUpgradeRequestCall();
- Log.d(this, "getVideoUpgradeRequestCall call =" + call);
- if (call != null) {
- showAnswerUi(true);
- processVideoUpgradeRequestCall(call);
- }
- } else {
- CallList.getInstance().removeListener(this);
- // This is necessary because the activity can be destroyed while an incoming call exists.
- // This happens when back button is pressed while incoming call is still being shown.
- if (mCallId != null) {
- CallList.getInstance().removeCallUpdateListener(mCallId, this);
- }
- }
- }
-
- @Override
- public void onIncomingCall(InCallState oldState, InCallState newState, Call call) {
- Log.d(this, "onIncomingCall: " + this);
- Call modifyCall = CallList.getInstance().getVideoUpgradeRequestCall();
- if (modifyCall != null) {
- showAnswerUi(false);
- Log.d(this, "declining upgrade request id: ");
- CallList.getInstance().removeCallUpdateListener(mCallId, this);
- InCallPresenter.getInstance().declineUpgradeRequest();
- }
- if (!call.getId().equals(mCallId)) {
- // A new call is coming in.
- processIncomingCall(call);
- }
- }
-
- @Override
- public void onIncomingCall(Call call) {
- }
-
- @Override
- public void onCallListChange(CallList list) {
- }
-
- @Override
- public void onDisconnect(Call call) {
- // no-op
- }
-
- public void onSessionModificationStateChange(int sessionModificationState) {
- boolean isUpgradePending = sessionModificationState ==
- Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
-
- if (!isUpgradePending) {
- // Stop listening for updates.
- CallList.getInstance().removeCallUpdateListener(mCallId, this);
- showAnswerUi(false);
- }
- }
-
- @Override
- public void onLastForwardedNumberChange() {
- // no-op
- }
-
- @Override
- public void onChildNumberChange() {
- // no-op
- }
-
- private boolean isVideoUpgradePending(Call call) {
- return call.getSessionModificationState()
- == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
- }
-
- @Override
- public void onUpgradeToVideo(Call call) {
- Log.d(this, "onUpgradeToVideo: " + this + " call=" + call);
- showAnswerUi(true);
- boolean isUpgradePending = isVideoUpgradePending(call);
- InCallPresenter inCallPresenter = InCallPresenter.getInstance();
- if (isUpgradePending
- && inCallPresenter.getInCallState() == InCallPresenter.InCallState.INCOMING) {
- Log.d(this, "declining upgrade request");
- //If there is incoming call reject upgrade request
- inCallPresenter.declineUpgradeRequest(getUi().getContext());
- } else if (isUpgradePending) {
- Log.d(this, "process upgrade request as no MT call");
- processVideoUpgradeRequestCall(call);
- }
- }
-
- private void processIncomingCall(Call call) {
- mCallId = call.getId();
- mCall = call;
-
- // Listen for call updates for the current call.
- CallList.getInstance().addCallUpdateListener(mCallId, this);
-
- Log.d(TAG, "Showing incoming for call id: " + mCallId + " " + this);
- if (showAnswerUi(true)) {
- final List<String> textMsgs = CallList.getInstance().getTextResponses(call.getId());
- configureAnswerTargetsForSms(call, textMsgs);
- }
- }
-
- private boolean showAnswerUi(boolean show) {
- final InCallActivity activity = InCallPresenter.getInstance().getActivity();
- if (activity != null) {
- activity.showAnswerFragment(show);
- if (getUi() != null) {
- getUi().onShowAnswerUi(show);
- }
- return true;
- } else {
- return false;
- }
- }
-
- private void processVideoUpgradeRequestCall(Call call) {
- Log.d(this, " processVideoUpgradeRequestCall call=" + call);
- mCallId = call.getId();
- mCall = call;
-
- // Listen for call updates for the current call.
- CallList.getInstance().addCallUpdateListener(mCallId, this);
-
- final int currentVideoState = call.getVideoState();
- final int modifyToVideoState = call.getRequestedVideoState();
-
- if (currentVideoState == modifyToVideoState) {
- Log.w(this, "processVideoUpgradeRequestCall: Video states are same. Return.");
- return;
- }
-
- AnswerUi ui = getUi();
-
- if (ui == null) {
- Log.e(this, "Ui is null. Can't process upgrade request");
- return;
- }
- showAnswerUi(true);
- ui.showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST,
- modifyToVideoState);
- }
-
- private boolean isEnabled(int videoState, int mask) {
- return (videoState & mask) == mask;
- }
-
- @Override
- public void onCallChanged(Call call) {
- Log.d(this, "onCallStateChange() " + call + " " + this);
- if (call.getState() != Call.State.INCOMING) {
- boolean isUpgradePending = isVideoUpgradePending(call);
- if (!isUpgradePending) {
- // Stop listening for updates.
- CallList.getInstance().removeCallUpdateListener(mCallId, this);
- }
-
- final Call incall = CallList.getInstance().getIncomingCall();
- if (incall != null || isUpgradePending) {
- showAnswerUi(true);
- } else {
- showAnswerUi(false);
- }
-
- mHasTextMessages = false;
- } else if (!mHasTextMessages) {
- final List<String> textMsgs = CallList.getInstance().getTextResponses(call.getId());
- if (textMsgs != null) {
- configureAnswerTargetsForSms(call, textMsgs);
- }
- }
- }
-
- public void onAnswer(int videoState, Context context) {
- if (mCallId == null) {
- return;
- }
-
- if (mCall.getSessionModificationState()
- == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
- Log.d(this, "onAnswer (upgradeCall) mCallId=" + mCallId + " videoState=" + videoState);
- InCallPresenter.getInstance().acceptUpgradeRequest(videoState, context);
- } else {
- Log.d(this, "onAnswer (answerCall) mCallId=" + mCallId + " videoState=" + videoState);
- TelecomAdapter.getInstance().answerCall(mCall.getId(), videoState);
- }
- }
-
- /**
- * TODO: We are using reject and decline interchangeably. We should settle on
- * reject since it seems to be more prevalent.
- */
- public void onDecline(Context context) {
- Log.d(this, "onDecline " + mCallId);
- if (mCall.getSessionModificationState()
- == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
- InCallPresenter.getInstance().declineUpgradeRequest(context);
- } else {
- TelecomAdapter.getInstance().rejectCall(mCall.getId(), false, null);
- }
- }
-
- public void onText() {
- if (getUi() != null) {
- TelecomUtil.silenceRinger(getUi().getContext());
- getUi().showMessageDialog();
- }
- }
-
- public void rejectCallWithMessage(String message) {
- Log.d(this, "sendTextToDefaultActivity()...");
- TelecomAdapter.getInstance().rejectCall(mCall.getId(), true, message);
-
- onDismissDialog();
- }
-
- public void onDismissDialog() {
- InCallPresenter.getInstance().onDismissDialog();
- }
-
- private void configureAnswerTargetsForSms(Call call, List<String> textMsgs) {
- if (getUi() == null) {
- return;
- }
- mHasTextMessages = textMsgs != null;
- boolean withSms = UserManagerCompat.isUserUnlocked(getUi().getContext())
- && call.can(android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT)
- && mHasTextMessages;
-
- // Only present the user with the option to answer as a video call if the incoming call is
- // a bi-directional video call.
- if (VideoUtils.isBidirectionalVideoCall(call)) {
- if (withSms) {
- getUi().showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_WITH_SMS);
- getUi().configureMessageDialog(textMsgs);
- } else {
- getUi().showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_WITHOUT_SMS);
- }
- } else {
- if (withSms) {
- getUi().showTargets(AnswerFragment.TARGET_SET_FOR_AUDIO_WITH_SMS);
- getUi().configureMessageDialog(textMsgs);
- } else {
- getUi().showTargets(AnswerFragment.TARGET_SET_FOR_AUDIO_WITHOUT_SMS);
- }
- }
- }
-
- interface AnswerUi extends Ui {
- public void onShowAnswerUi(boolean shown);
- public void showTargets(int targetSet);
- public void showTargets(int targetSet, int videoState);
- public void showMessageDialog();
- public void configureMessageDialog(List<String> textResponses);
- public Context getContext();
- }
-}
diff --git a/InCallUI/src/com/android/incallui/AudioModeProvider.java b/InCallUI/src/com/android/incallui/AudioModeProvider.java
deleted file mode 100644
index ea56dd624..000000000
--- a/InCallUI/src/com/android/incallui/AudioModeProvider.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.telecom.CallAudioState;
-
-import com.google.common.collect.Lists;
-
-import java.util.List;
-
-/**
- * Proxy class for getting and setting the audio mode.
- */
-public class AudioModeProvider {
-
- static final int AUDIO_MODE_INVALID = 0;
-
- private static AudioModeProvider sAudioModeProvider = new AudioModeProvider();
- private int mAudioMode = CallAudioState.ROUTE_EARPIECE;
- private boolean mMuted = false;
- private int mSupportedModes = CallAudioState.ROUTE_EARPIECE
- | CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_WIRED_HEADSET
- | CallAudioState.ROUTE_SPEAKER;
- private final List<AudioModeListener> mListeners = Lists.newArrayList();
-
- public static AudioModeProvider getInstance() {
- return sAudioModeProvider;
- }
-
- public void onAudioStateChanged(boolean isMuted, int route, int supportedRouteMask) {
- onAudioModeChange(route, isMuted);
- onSupportedAudioModeChange(supportedRouteMask);
- }
-
- public void onAudioModeChange(int newMode, boolean muted) {
- if (mAudioMode != newMode) {
- mAudioMode = newMode;
- for (AudioModeListener l : mListeners) {
- l.onAudioMode(mAudioMode);
- }
- }
-
- if (mMuted != muted) {
- mMuted = muted;
- for (AudioModeListener l : mListeners) {
- l.onMute(mMuted);
- }
- }
- }
-
- public void onSupportedAudioModeChange(int newModeMask) {
- mSupportedModes = newModeMask;
-
- for (AudioModeListener l : mListeners) {
- l.onSupportedAudioMode(mSupportedModes);
- }
- }
-
- public void addListener(AudioModeListener listener) {
- if (!mListeners.contains(listener)) {
- mListeners.add(listener);
- listener.onSupportedAudioMode(mSupportedModes);
- listener.onAudioMode(mAudioMode);
- listener.onMute(mMuted);
- }
- }
-
- public void removeListener(AudioModeListener listener) {
- if (mListeners.contains(listener)) {
- mListeners.remove(listener);
- }
- }
-
- public int getSupportedModes() {
- return mSupportedModes;
- }
-
- public int getAudioMode() {
- return mAudioMode;
- }
-
- public boolean getMute() {
- return mMuted;
- }
-
- /* package */ interface AudioModeListener {
- void onAudioMode(int newMode);
- void onMute(boolean muted);
- void onSupportedAudioMode(int modeMask);
- }
-}
diff --git a/InCallUI/src/com/android/incallui/BaseFragment.java b/InCallUI/src/com/android/incallui/BaseFragment.java
deleted file mode 100644
index 58d991acd..000000000
--- a/InCallUI/src/com/android/incallui/BaseFragment.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.os.Bundle;
-
-/**
- * Parent for all fragments that use Presenters and Ui design.
- */
-public abstract class BaseFragment<T extends Presenter<U>, U extends Ui> extends Fragment {
-
- private static final String KEY_FRAGMENT_HIDDEN = "key_fragment_hidden";
-
- private T mPresenter;
-
- public abstract T createPresenter();
-
- public abstract U getUi();
-
- protected BaseFragment() {
- mPresenter = createPresenter();
- }
-
- /**
- * Presenter will be available after onActivityCreated().
- *
- * @return The presenter associated with this fragment.
- */
- public T getPresenter() {
- return mPresenter;
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- mPresenter.onUiReady(getUi());
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (savedInstanceState != null) {
- mPresenter.onRestoreInstanceState(savedInstanceState);
- if (savedInstanceState.getBoolean(KEY_FRAGMENT_HIDDEN)) {
- getFragmentManager().beginTransaction().hide(this).commit();
- }
- }
- }
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- mPresenter.onUiDestroy(getUi());
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mPresenter.onSaveInstanceState(outState);
- outState.putBoolean(KEY_FRAGMENT_HIDDEN, isHidden());
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- ((FragmentDisplayManager) activity).onFragmentAttached(this);
- }
-}
diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java
deleted file mode 100644
index 1ad37e01a..000000000
--- a/InCallUI/src/com/android/incallui/Call.java
+++ /dev/null
@@ -1,1023 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.content.Context;
-import android.hardware.camera2.CameraCharacteristics;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Trace;
-import android.support.annotation.IntDef;
-import android.telecom.Call.Details;
-import android.telecom.Connection;
-import android.telecom.DisconnectCause;
-import android.telecom.GatewayInfo;
-import android.telecom.InCallService.VideoCall;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telecom.TelecomManager;
-import android.telecom.VideoProfile;
-import android.text.TextUtils;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.compat.CallSdkCompat;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.SdkVersionOverride;
-import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.dialer.util.IntentUtil;
-import com.android.incallui.util.TelecomCallUtil;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-
-/**
- * Describes a single call and its state.
- */
-@NeededForTesting
-public class Call {
-
- /**
- * Specifies whether a number is in the call history or not.
- * {@link #CALL_HISTORY_STATUS_UNKNOWN} means there is no result.
- */
- @IntDef({CALL_HISTORY_STATUS_UNKNOWN, CALL_HISTORY_STATUS_PRESENT,
- CALL_HISTORY_STATUS_NOT_PRESENT})
- @Retention(RetentionPolicy.SOURCE)
- public @interface CallHistoryStatus {}
- public static final int CALL_HISTORY_STATUS_UNKNOWN = 0;
- public static final int CALL_HISTORY_STATUS_PRESENT = 1;
- public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2;
-
- /* Defines different states of this call */
- public static class State {
- public static final int INVALID = 0;
- public static final int NEW = 1; /* The call is new. */
- public static final int IDLE = 2; /* The call is idle. Nothing active */
- public static final int ACTIVE = 3; /* There is an active call */
- public static final int INCOMING = 4; /* A normal incoming phone call */
- public static final int CALL_WAITING = 5; /* Incoming call while another is active */
- public static final int DIALING = 6; /* An outgoing call during dial phase */
- public static final int REDIALING = 7; /* Subsequent dialing attempt after a failure */
- public static final int ONHOLD = 8; /* An active phone call placed on hold */
- public static final int DISCONNECTING = 9; /* A call is being ended. */
- public static final int DISCONNECTED = 10; /* State after a call disconnects */
- public static final int CONFERENCED = 11; /* Call part of a conference call */
- public static final int SELECT_PHONE_ACCOUNT = 12; /* Waiting for account selection */
- public static final int CONNECTING = 13; /* Waiting for Telecom broadcast to finish */
- public static final int BLOCKED = 14; /* The number was found on the block list */
-
-
- public static boolean isConnectingOrConnected(int state) {
- switch(state) {
- case ACTIVE:
- case INCOMING:
- case CALL_WAITING:
- case CONNECTING:
- case DIALING:
- case REDIALING:
- case ONHOLD:
- case CONFERENCED:
- return true;
- default:
- }
- return false;
- }
-
- public static boolean isDialing(int state) {
- return state == DIALING || state == REDIALING;
- }
-
- public static String toString(int state) {
- switch (state) {
- case INVALID:
- return "INVALID";
- case NEW:
- return "NEW";
- case IDLE:
- return "IDLE";
- case ACTIVE:
- return "ACTIVE";
- case INCOMING:
- return "INCOMING";
- case CALL_WAITING:
- return "CALL_WAITING";
- case DIALING:
- return "DIALING";
- case REDIALING:
- return "REDIALING";
- case ONHOLD:
- return "ONHOLD";
- case DISCONNECTING:
- return "DISCONNECTING";
- case DISCONNECTED:
- return "DISCONNECTED";
- case CONFERENCED:
- return "CONFERENCED";
- case SELECT_PHONE_ACCOUNT:
- return "SELECT_PHONE_ACCOUNT";
- case CONNECTING:
- return "CONNECTING";
- case BLOCKED:
- return "BLOCKED";
- default:
- return "UNKNOWN";
- }
- }
- }
-
- /**
- * Defines different states of session modify requests, which are used to upgrade to video, or
- * downgrade to audio.
- */
- public static class SessionModificationState {
- public static final int NO_REQUEST = 0;
- public static final int WAITING_FOR_RESPONSE = 1;
- public static final int REQUEST_FAILED = 2;
- public static final int RECEIVED_UPGRADE_TO_VIDEO_REQUEST = 3;
- public static final int UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT = 4;
- public static final int REQUEST_REJECTED = 5;
- }
-
- public static class VideoSettings {
- public static final int CAMERA_DIRECTION_UNKNOWN = -1;
- public static final int CAMERA_DIRECTION_FRONT_FACING =
- CameraCharacteristics.LENS_FACING_FRONT;
- public static final int CAMERA_DIRECTION_BACK_FACING =
- CameraCharacteristics.LENS_FACING_BACK;
-
- private int mCameraDirection = CAMERA_DIRECTION_UNKNOWN;
-
- /**
- * Sets the camera direction. if camera direction is set to CAMERA_DIRECTION_UNKNOWN,
- * the video state of the call should be used to infer the camera direction.
- *
- * @see {@link CameraCharacteristics#LENS_FACING_FRONT}
- * @see {@link CameraCharacteristics#LENS_FACING_BACK}
- */
- public void setCameraDir(int cameraDirection) {
- if (cameraDirection == CAMERA_DIRECTION_FRONT_FACING
- || cameraDirection == CAMERA_DIRECTION_BACK_FACING) {
- mCameraDirection = cameraDirection;
- } else {
- mCameraDirection = CAMERA_DIRECTION_UNKNOWN;
- }
- }
-
- /**
- * Gets the camera direction. if camera direction is set to CAMERA_DIRECTION_UNKNOWN,
- * the video state of the call should be used to infer the camera direction.
- *
- * @see {@link CameraCharacteristics#LENS_FACING_FRONT}
- * @see {@link CameraCharacteristics#LENS_FACING_BACK}
- */
- public int getCameraDir() {
- return mCameraDirection;
- }
-
- @Override
- public String toString() {
- return "(CameraDir:" + getCameraDir() + ")";
- }
- }
-
- /**
- * Tracks any state variables that is useful for logging. There is some amount of overlap with
- * existing call member variables, but this duplication helps to ensure that none of these
- * logging variables will interface with/and affect call logic.
- */
- public static class LogState {
-
- // Contact lookup type constants
- // Unknown lookup result (lookup not completed yet?)
- public static final int LOOKUP_UNKNOWN = 0;
- public static final int LOOKUP_NOT_FOUND = 1;
- public static final int LOOKUP_LOCAL_CONTACT = 2;
- public static final int LOOKUP_LOCAL_CACHE = 3;
- public static final int LOOKUP_REMOTE_CONTACT = 4;
- public static final int LOOKUP_EMERGENCY = 5;
- public static final int LOOKUP_VOICEMAIL = 6;
-
- // Call initiation type constants
- public static final int INITIATION_UNKNOWN = 0;
- public static final int INITIATION_INCOMING = 1;
- public static final int INITIATION_DIALPAD = 2;
- public static final int INITIATION_SPEED_DIAL = 3;
- public static final int INITIATION_REMOTE_DIRECTORY = 4;
- public static final int INITIATION_SMART_DIAL = 5;
- public static final int INITIATION_REGULAR_SEARCH = 6;
- public static final int INITIATION_CALL_LOG = 7;
- public static final int INITIATION_CALL_LOG_FILTER = 8;
- public static final int INITIATION_VOICEMAIL_LOG = 9;
- public static final int INITIATION_CALL_DETAILS = 10;
- public static final int INITIATION_QUICK_CONTACTS = 11;
- public static final int INITIATION_EXTERNAL = 12;
-
- public DisconnectCause disconnectCause;
- public boolean isIncoming = false;
- public int contactLookupResult = LOOKUP_UNKNOWN;
- public int callInitiationMethod = INITIATION_EXTERNAL;
- // If this was a conference call, the total number of calls involved in the conference.
- public int conferencedCalls = 0;
- public long duration = 0;
- public boolean isLogged = false;
-
- @Override
- public String toString() {
- return String.format(Locale.US, "["
- + "%s, " // DisconnectCause toString already describes the object type
- + "isIncoming: %s, "
- + "contactLookup: %s, "
- + "callInitiation: %s, "
- + "duration: %s"
- + "]",
- disconnectCause,
- isIncoming,
- lookupToString(contactLookupResult),
- initiationToString(callInitiationMethod),
- duration);
- }
-
- private static String lookupToString(int lookupType) {
- switch (lookupType) {
- case LOOKUP_LOCAL_CONTACT:
- return "Local";
- case LOOKUP_LOCAL_CACHE:
- return "Cache";
- case LOOKUP_REMOTE_CONTACT:
- return "Remote";
- case LOOKUP_EMERGENCY:
- return "Emergency";
- case LOOKUP_VOICEMAIL:
- return "Voicemail";
- default:
- return "Not found";
- }
- }
-
- private static String initiationToString(int initiationType) {
- switch (initiationType) {
- case INITIATION_INCOMING:
- return "Incoming";
- case INITIATION_DIALPAD:
- return "Dialpad";
- case INITIATION_SPEED_DIAL:
- return "Speed Dial";
- case INITIATION_REMOTE_DIRECTORY:
- return "Remote Directory";
- case INITIATION_SMART_DIAL:
- return "Smart Dial";
- case INITIATION_REGULAR_SEARCH:
- return "Regular Search";
- case INITIATION_CALL_LOG:
- return "Call Log";
- case INITIATION_CALL_LOG_FILTER:
- return "Call Log Filter";
- case INITIATION_VOICEMAIL_LOG:
- return "Voicemail Log";
- case INITIATION_CALL_DETAILS:
- return "Call Details";
- case INITIATION_QUICK_CONTACTS:
- return "Quick Contacts";
- default:
- return "Unknown";
- }
- }
- }
-
-
- private static final String ID_PREFIX = Call.class.getSimpleName() + "_";
- private static int sIdCounter = 0;
-
- private final android.telecom.Call.Callback mTelecomCallCallback =
- new android.telecom.Call.Callback() {
- @Override
- public void onStateChanged(android.telecom.Call call, int newState) {
- Log.d(this, "TelecomCallCallback onStateChanged call=" + call + " newState="
- + newState);
- update();
- }
-
- @Override
- public void onParentChanged(android.telecom.Call call,
- android.telecom.Call newParent) {
- Log.d(this, "TelecomCallCallback onParentChanged call=" + call + " newParent="
- + newParent);
- update();
- }
-
- @Override
- public void onChildrenChanged(android.telecom.Call call,
- List<android.telecom.Call> children) {
- update();
- }
-
- @Override
- public void onDetailsChanged(android.telecom.Call call,
- android.telecom.Call.Details details) {
- Log.d(this, "TelecomCallCallback onStateChanged call=" + call + " details="
- + details);
- update();
- }
-
- @Override
- public void onCannedTextResponsesLoaded(android.telecom.Call call,
- List<String> cannedTextResponses) {
- Log.d(this, "TelecomCallCallback onStateChanged call=" + call
- + " cannedTextResponses=" + cannedTextResponses);
- update();
- }
-
- @Override
- public void onPostDialWait(android.telecom.Call call,
- String remainingPostDialSequence) {
- Log.d(this, "TelecomCallCallback onStateChanged call=" + call
- + " remainingPostDialSequence=" + remainingPostDialSequence);
- update();
- }
-
- @Override
- public void onVideoCallChanged(android.telecom.Call call,
- VideoCall videoCall) {
- Log.d(this, "TelecomCallCallback onStateChanged call=" + call + " videoCall="
- + videoCall);
- update();
- }
-
- @Override
- public void onCallDestroyed(android.telecom.Call call) {
- Log.d(this, "TelecomCallCallback onStateChanged call=" + call);
- call.unregisterCallback(this);
- }
-
- @Override
- public void onConferenceableCallsChanged(android.telecom.Call call,
- List<android.telecom.Call> conferenceableCalls) {
- update();
- }
- };
-
- private final android.telecom.Call mTelecomCall;
- private final LatencyReport mLatencyReport;
- private boolean mIsEmergencyCall;
- private Uri mHandle;
- private final String mId;
- private int mState = State.INVALID;
- private DisconnectCause mDisconnectCause;
- private int mSessionModificationState;
- private final List<String> mChildCallIds = new ArrayList<>();
- private final VideoSettings mVideoSettings = new VideoSettings();
- private int mVideoState;
-
- /**
- * mRequestedVideoState is used to store requested upgrade / downgrade video state
- */
- private int mRequestedVideoState = VideoProfile.STATE_AUDIO_ONLY;
-
- private InCallVideoCallCallback mVideoCallCallback;
- private boolean mIsVideoCallCallbackRegistered;
- private String mChildNumber;
- private String mLastForwardedNumber;
- private String mCallSubject;
- private PhoneAccountHandle mPhoneAccountHandle;
- @CallHistoryStatus private int mCallHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
- private boolean mIsSpam;
-
- /**
- * Indicates whether the phone account associated with this call supports specifying a call
- * subject.
- */
- private boolean mIsCallSubjectSupported;
-
- private long mTimeAddedMs;
-
- private final LogState mLogState = new LogState();
-
- /**
- * Used only to create mock calls for testing
- */
- @NeededForTesting
- Call(int state) {
- mTelecomCall = null;
- mLatencyReport = new LatencyReport();
- mId = ID_PREFIX + Integer.toString(sIdCounter++);
- setState(state);
- }
-
- /**
- * Creates a new instance of a {@link Call}. Registers a callback for
- * {@link android.telecom.Call} events.
- */
- public Call(android.telecom.Call telecomCall, LatencyReport latencyReport) {
- this(telecomCall, latencyReport, true /* registerCallback */);
- }
-
- /**
- * Creates a new instance of a {@link Call}. Optionally registers a callback for
- * {@link android.telecom.Call} events.
- *
- * Intended for use when creating a {@link Call} instance for use with the
- * {@link ContactInfoCache}, where we do not want to register callbacks for the new call.
- */
- public Call(android.telecom.Call telecomCall, LatencyReport latencyReport,
- boolean registerCallback) {
- mTelecomCall = telecomCall;
- mLatencyReport = latencyReport;
- mId = ID_PREFIX + Integer.toString(sIdCounter++);
-
- updateFromTelecomCall(registerCallback);
-
- if (registerCallback) {
- mTelecomCall.registerCallback(mTelecomCallCallback);
- }
-
- mTimeAddedMs = System.currentTimeMillis();
- }
-
- public android.telecom.Call getTelecomCall() {
- return mTelecomCall;
- }
-
- /**
- * @return video settings of the call, null if the call is not a video call.
- * @see VideoProfile
- */
- public VideoSettings getVideoSettings() {
- return mVideoSettings;
- }
-
- private void update() {
- Trace.beginSection("Update");
- int oldState = getState();
- // We want to potentially register a video call callback here.
- updateFromTelecomCall(true /* registerCallback */);
- if (oldState != getState() && getState() == Call.State.DISCONNECTED) {
- CallList.getInstance().onDisconnect(this);
- } else {
- CallList.getInstance().onUpdate(this);
- }
- Trace.endSection();
- }
-
- private void updateFromTelecomCall(boolean registerCallback) {
- Log.d(this, "updateFromTelecomCall: " + mTelecomCall.toString());
- final int translatedState = translateState(mTelecomCall.getState());
- if (mState != State.BLOCKED) {
- setState(translatedState);
- setDisconnectCause(mTelecomCall.getDetails().getDisconnectCause());
- maybeCancelVideoUpgrade(mTelecomCall.getDetails().getVideoState());
- }
-
- if (registerCallback && mTelecomCall.getVideoCall() != null) {
- if (mVideoCallCallback == null) {
- mVideoCallCallback = new InCallVideoCallCallback(this);
- }
- mTelecomCall.getVideoCall().registerCallback(mVideoCallCallback);
- mIsVideoCallCallbackRegistered = true;
- }
-
- mChildCallIds.clear();
- final int numChildCalls = mTelecomCall.getChildren().size();
- for (int i = 0; i < numChildCalls; i++) {
- mChildCallIds.add(
- CallList.getInstance().getCallByTelecomCall(
- mTelecomCall.getChildren().get(i)).getId());
- }
-
- // The number of conferenced calls can change over the course of the call, so use the
- // maximum number of conferenced child calls as the metric for conference call usage.
- mLogState.conferencedCalls = Math.max(numChildCalls, mLogState.conferencedCalls);
-
- updateFromCallExtras(mTelecomCall.getDetails().getExtras());
-
- // If the handle of the call has changed, update state for the call determining if it is an
- // emergency call.
- Uri newHandle = mTelecomCall.getDetails().getHandle();
- if (!Objects.equals(mHandle, newHandle)) {
- mHandle = newHandle;
- updateEmergencyCallState();
- }
-
- // If the phone account handle of the call is set, cache capability bit indicating whether
- // the phone account supports call subjects.
- PhoneAccountHandle newPhoneAccountHandle = mTelecomCall.getDetails().getAccountHandle();
- if (!Objects.equals(mPhoneAccountHandle, newPhoneAccountHandle)) {
- mPhoneAccountHandle = newPhoneAccountHandle;
-
- if (mPhoneAccountHandle != null) {
- TelecomManager mgr = InCallPresenter.getInstance().getTelecomManager();
- PhoneAccount phoneAccount =
- TelecomManagerCompat.getPhoneAccount(mgr, mPhoneAccountHandle);
- if (phoneAccount != null) {
- mIsCallSubjectSupported = phoneAccount.hasCapabilities(
- PhoneAccount.CAPABILITY_CALL_SUBJECT);
- }
- }
- }
- }
-
- /**
- * Tests corruption of the {@code callExtras} bundle by calling {@link
- * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException}
- * will be thrown and caught by this function.
- *
- * @param callExtras the bundle to verify
- * @returns {@code true} if the bundle is corrupted, {@code false} otherwise.
- */
- protected boolean areCallExtrasCorrupted(Bundle callExtras) {
- /**
- * There's currently a bug in Telephony service (b/25613098) that could corrupt the
- * extras bundle, resulting in a IllegalArgumentException while validating data under
- * {@link Bundle#containsKey(String)}.
- */
- try {
- callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
- return false;
- } catch (IllegalArgumentException e) {
- Log.e(this, "CallExtras is corrupted, ignoring exception", e);
- return true;
- }
- }
-
- protected void updateFromCallExtras(Bundle callExtras) {
- if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
- /**
- * If the bundle is corrupted, abandon information update as a work around. These are
- * not critical for the dialer to function.
- */
- return;
- }
- // Check for a change in the child address and notify any listeners.
- if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
- String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
- if (!Objects.equals(childNumber, mChildNumber)) {
- mChildNumber = childNumber;
- CallList.getInstance().onChildNumberChange(this);
- }
- }
-
- // Last forwarded number comes in as an array of strings. We want to choose the
- // last item in the array. The forwarding numbers arrive independently of when the
- // call is originally set up, so we need to notify the the UI of the change.
- if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
- ArrayList<String> lastForwardedNumbers =
- callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
-
- if (lastForwardedNumbers != null) {
- String lastForwardedNumber = null;
- if (!lastForwardedNumbers.isEmpty()) {
- lastForwardedNumber = lastForwardedNumbers.get(
- lastForwardedNumbers.size() - 1);
- }
-
- if (!Objects.equals(lastForwardedNumber, mLastForwardedNumber)) {
- mLastForwardedNumber = lastForwardedNumber;
- CallList.getInstance().onLastForwardedNumberChange(this);
- }
- }
- }
-
- // Call subject is present in the extras at the start of call, so we do not need to
- // notify any other listeners of this.
- if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
- String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
- if (!Objects.equals(mCallSubject, callSubject)) {
- mCallSubject = callSubject;
- }
- }
- }
-
- /**
- * Determines if a received upgrade to video request should be cancelled. This can happen if
- * another InCall UI responds to the upgrade to video request.
- *
- * @param newVideoState The new video state.
- */
- private void maybeCancelVideoUpgrade(int newVideoState) {
- boolean isVideoStateChanged = mVideoState != newVideoState;
-
- if (mSessionModificationState == SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST
- && isVideoStateChanged) {
-
- Log.v(this, "maybeCancelVideoUpgrade : cancelling upgrade notification");
- setSessionModificationState(SessionModificationState.NO_REQUEST);
- }
- mVideoState = newVideoState;
- }
- private static int translateState(int state) {
- switch (state) {
- case android.telecom.Call.STATE_NEW:
- case android.telecom.Call.STATE_CONNECTING:
- return Call.State.CONNECTING;
- case android.telecom.Call.STATE_SELECT_PHONE_ACCOUNT:
- return Call.State.SELECT_PHONE_ACCOUNT;
- case android.telecom.Call.STATE_DIALING:
- return Call.State.DIALING;
- case android.telecom.Call.STATE_RINGING:
- return Call.State.INCOMING;
- case android.telecom.Call.STATE_ACTIVE:
- return Call.State.ACTIVE;
- case android.telecom.Call.STATE_HOLDING:
- return Call.State.ONHOLD;
- case android.telecom.Call.STATE_DISCONNECTED:
- return Call.State.DISCONNECTED;
- case android.telecom.Call.STATE_DISCONNECTING:
- return Call.State.DISCONNECTING;
- default:
- return Call.State.INVALID;
- }
- }
-
- public String getId() {
- return mId;
- }
-
- public long getTimeAddedMs() {
- return mTimeAddedMs;
- }
-
- public String getNumber() {
- return TelecomCallUtil.getNumber(mTelecomCall);
- }
-
- public void blockCall() {
- mTelecomCall.reject(false, null);
- setState(State.BLOCKED);
- }
-
- public Uri getHandle() {
- return mTelecomCall == null ? null : mTelecomCall.getDetails().getHandle();
- }
-
- public boolean isEmergencyCall() {
- return mIsEmergencyCall;
- }
-
- public int getState() {
- if (mTelecomCall != null && mTelecomCall.getParent() != null) {
- return State.CONFERENCED;
- } else {
- return mState;
- }
- }
-
- public void setState(int state) {
- mState = state;
- if (mState == State.INCOMING) {
- mLogState.isIncoming = true;
- } else if (mState == State.DISCONNECTED) {
- mLogState.duration = getConnectTimeMillis() == 0 ?
- 0: System.currentTimeMillis() - getConnectTimeMillis();
- }
- }
-
- public int getNumberPresentation() {
- return mTelecomCall == null ? null : mTelecomCall.getDetails().getHandlePresentation();
- }
-
- public int getCnapNamePresentation() {
- return mTelecomCall == null ? null
- : mTelecomCall.getDetails().getCallerDisplayNamePresentation();
- }
-
- public String getCnapName() {
- return mTelecomCall == null ? null
- : getTelecomCall().getDetails().getCallerDisplayName();
- }
-
- public Bundle getIntentExtras() {
- return mTelecomCall.getDetails().getIntentExtras();
- }
-
- public Bundle getExtras() {
- return mTelecomCall == null ? null : mTelecomCall.getDetails().getExtras();
- }
-
- /**
- * @return The child number for the call, or {@code null} if none specified.
- */
- public String getChildNumber() {
- return mChildNumber;
- }
-
- /**
- * @return The last forwarded number for the call, or {@code null} if none specified.
- */
- public String getLastForwardedNumber() {
- return mLastForwardedNumber;
- }
-
- /**
- * @return The call subject, or {@code null} if none specified.
- */
- public String getCallSubject() {
- return mCallSubject;
- }
-
- /**
- * @return {@code true} if the call's phone account supports call subjects, {@code false}
- * otherwise.
- */
- public boolean isCallSubjectSupported() {
- return mIsCallSubjectSupported;
- }
-
- /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
- public DisconnectCause getDisconnectCause() {
- if (mState == State.DISCONNECTED || mState == State.IDLE) {
- return mDisconnectCause;
- }
-
- return new DisconnectCause(DisconnectCause.UNKNOWN);
- }
-
- public void setDisconnectCause(DisconnectCause disconnectCause) {
- mDisconnectCause = disconnectCause;
- mLogState.disconnectCause = mDisconnectCause;
- }
-
- /** Returns the possible text message responses. */
- public List<String> getCannedSmsResponses() {
- return mTelecomCall.getCannedTextResponses();
- }
-
- /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
- public boolean can(int capabilities) {
- int supportedCapabilities = mTelecomCall.getDetails().getCallCapabilities();
-
- if ((capabilities & android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
- // We allow you to merge if the capabilities allow it or if it is a call with
- // conferenceable calls.
- if (mTelecomCall.getConferenceableCalls().isEmpty() &&
- ((android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE
- & supportedCapabilities) == 0)) {
- // Cannot merge calls if there are no calls to merge with.
- return false;
- }
- capabilities &= ~android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE;
- }
- return (capabilities == (capabilities & mTelecomCall.getDetails().getCallCapabilities()));
- }
-
- public boolean hasProperty(int property) {
- return mTelecomCall.getDetails().hasProperty(property);
- }
-
- /** Gets the time when the call first became active. */
- public long getConnectTimeMillis() {
- return mTelecomCall.getDetails().getConnectTimeMillis();
- }
-
- public boolean isConferenceCall() {
- return hasProperty(android.telecom.Call.Details.PROPERTY_CONFERENCE);
- }
-
- public GatewayInfo getGatewayInfo() {
- return mTelecomCall == null ? null : mTelecomCall.getDetails().getGatewayInfo();
- }
-
- public PhoneAccountHandle getAccountHandle() {
- return mTelecomCall == null ? null : mTelecomCall.getDetails().getAccountHandle();
- }
-
- /**
- * @return The {@link VideoCall} instance associated with the {@link android.telecom.Call}.
- * Will return {@code null} until {@link #updateFromTelecomCall()} has registered a valid
- * callback on the {@link VideoCall}.
- */
- public VideoCall getVideoCall() {
- return mTelecomCall == null || !mIsVideoCallCallbackRegistered ? null
- : mTelecomCall.getVideoCall();
- }
-
- public List<String> getChildCallIds() {
- return mChildCallIds;
- }
-
- public String getParentId() {
- android.telecom.Call parentCall = mTelecomCall.getParent();
- if (parentCall != null) {
- return CallList.getInstance().getCallByTelecomCall(parentCall).getId();
- }
- return null;
- }
-
- public int getVideoState() {
- return mTelecomCall.getDetails().getVideoState();
- }
-
- public boolean isVideoCall(Context context) {
- return CallUtil.isVideoEnabled(context) &&
- VideoUtils.isVideoCall(getVideoState());
- }
-
- /**
- * Handles incoming session modification requests. Stores the pending video request and sets
- * the session modification state to
- * {@link SessionModificationState#RECEIVED_UPGRADE_TO_VIDEO_REQUEST} so that we can keep track
- * of the fact the request was received. Only upgrade requests require user confirmation and
- * will be handled by this method. The remote user can turn off their own camera without
- * confirmation.
- *
- * @param videoState The requested video state.
- */
- public void setRequestedVideoState(int videoState) {
- Log.d(this, "setRequestedVideoState - video state= " + videoState);
- if (videoState == getVideoState()) {
- mSessionModificationState = Call.SessionModificationState.NO_REQUEST;
- Log.w(this,"setRequestedVideoState - Clearing session modification state");
- return;
- }
-
- mSessionModificationState = Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
- mRequestedVideoState = videoState;
- CallList.getInstance().onUpgradeToVideo(this);
-
- Log.d(this, "setRequestedVideoState - mSessionModificationState="
- + mSessionModificationState + " video state= " + videoState);
- update();
- }
-
- /**
- * Set the session modification state. Used to keep track of pending video session modification
- * operations and to inform listeners of these changes.
- *
- * @param state the new session modification state.
- */
- public void setSessionModificationState(int state) {
- boolean hasChanged = mSessionModificationState != state;
- mSessionModificationState = state;
- Log.d(this, "setSessionModificationState " + state + " mSessionModificationState="
- + mSessionModificationState);
- if (hasChanged) {
- CallList.getInstance().onSessionModificationStateChange(this, state);
- }
- }
-
- /**
- * Determines if the call handle is an emergency number or not and caches the result to avoid
- * repeated calls to isEmergencyNumber.
- */
- private void updateEmergencyCallState() {
- mIsEmergencyCall = TelecomCallUtil.isEmergencyCall(mTelecomCall);
- }
-
- /**
- * Gets the video state which was requested via a session modification request.
- *
- * @return The video state.
- */
- public int getRequestedVideoState() {
- return mRequestedVideoState;
- }
-
- public static boolean areSame(Call call1, Call call2) {
- if (call1 == null && call2 == null) {
- return true;
- } else if (call1 == null || call2 == null) {
- return false;
- }
-
- // otherwise compare call Ids
- return call1.getId().equals(call2.getId());
- }
-
- public static boolean areSameNumber(Call call1, Call call2) {
- if (call1 == null && call2 == null) {
- return true;
- } else if (call1 == null || call2 == null) {
- return false;
- }
-
- // otherwise compare call Numbers
- return TextUtils.equals(call1.getNumber(), call2.getNumber());
- }
-
- /**
- * Gets the current video session modification state.
- *
- * @return The session modification state.
- */
- public int getSessionModificationState() {
- return mSessionModificationState;
- }
-
- public LogState getLogState() {
- return mLogState;
- }
-
- /**
- * Determines if the call is an external call.
- *
- * An external call is one which does not exist locally for the
- * {@link android.telecom.ConnectionService} it is associated with.
- *
- * External calls are only supported in N and higher.
- *
- * @return {@code true} if the call is an external call, {@code false} otherwise.
- */
- public boolean isExternalCall() {
- return CompatUtils.isNCompatible() &&
- hasProperty(CallSdkCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
- }
-
- /**
- * Determines if the external call is pullable.
- *
- * An external call is one which does not exist locally for the
- * {@link android.telecom.ConnectionService} it is associated with. An external call may be
- * "pullable", which means that the user can request it be transferred to the current device.
- *
- * External calls are only supported in N and higher.
- *
- * @return {@code true} if the call is an external call, {@code false} otherwise.
- */
- public boolean isPullableExternalCall() {
- return CompatUtils.isNCompatible() &&
- (mTelecomCall.getDetails().getCallCapabilities()
- & CallSdkCompat.Details.CAPABILITY_CAN_PULL_CALL)
- == CallSdkCompat.Details.CAPABILITY_CAN_PULL_CALL;
- }
-
- /**
- * Logging utility methods
- */
- public void logCallInitiationType() {
- if (isExternalCall()) {
- return;
- }
-
- if (getState() == State.INCOMING) {
- getLogState().callInitiationMethod = LogState.INITIATION_INCOMING;
- } else if (getIntentExtras() != null) {
- getLogState().callInitiationMethod =
- getIntentExtras().getInt(IntentUtil.EXTRA_CALL_INITIATION_TYPE,
- LogState.INITIATION_EXTERNAL);
- }
- }
-
- @Override
- public String toString() {
- if (mTelecomCall == null) {
- // This should happen only in testing since otherwise we would never have a null
- // Telecom call.
- return String.valueOf(mId);
- }
-
- return String.format(Locale.US, "[%s, %s, %s, %s, children:%s, parent:%s, " +
- "conferenceable:%s, videoState:%s, mSessionModificationState:%d, VideoSettings:%s]",
- mId,
- State.toString(getState()),
- Details.capabilitiesToString(mTelecomCall.getDetails().getCallCapabilities()),
- Details.propertiesToString(mTelecomCall.getDetails().getCallProperties()),
- mChildCallIds,
- getParentId(),
- this.mTelecomCall.getConferenceableCalls(),
- VideoProfile.videoStateToString(mTelecomCall.getDetails().getVideoState()),
- mSessionModificationState,
- getVideoSettings());
- }
-
- public String toSimpleString() {
- return super.toString();
- }
-
- public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
- mCallHistoryStatus = callHistoryStatus;
- }
-
- @CallHistoryStatus
- public int getCallHistoryStatus() {
- return mCallHistoryStatus;
- }
-
- public void setSpam(boolean isSpam) {
- mIsSpam = isSpam;
- }
-
- public boolean isSpam() {
- return mIsSpam;
- }
-
- public LatencyReport getLatencyReport() {
- return mLatencyReport;
- }
-}
diff --git a/InCallUI/src/com/android/incallui/CallButtonFragment.java b/InCallUI/src/com/android/incallui/CallButtonFragment.java
deleted file mode 100644
index 6b633eaf3..000000000
--- a/InCallUI/src/com/android/incallui/CallButtonFragment.java
+++ /dev/null
@@ -1,819 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_ADD_CALL;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_AUDIO;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_COUNT;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_DIALPAD;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_DOWNGRADE_TO_AUDIO;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_HOLD;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_MANAGE_VIDEO_CONFERENCE;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_MERGE;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_MUTE;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_PAUSE_VIDEO;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_SWAP;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_SWITCH_CAMERA;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_UPGRADE_TO_VIDEO;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.RippleDrawable;
-import android.graphics.drawable.StateListDrawable;
-import android.os.Bundle;
-import android.telecom.CallAudioState;
-import android.util.SparseIntArray;
-import android.view.ContextThemeWrapper;
-import android.view.HapticFeedbackConstants;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CompoundButton;
-import android.widget.ImageButton;
-import android.widget.PopupMenu;
-import android.widget.PopupMenu.OnDismissListener;
-import android.widget.PopupMenu.OnMenuItemClickListener;
-
-import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
-import com.android.dialer.R;
-
-/**
- * Fragment for call control buttons
- */
-public class CallButtonFragment
- extends BaseFragment<CallButtonPresenter, CallButtonPresenter.CallButtonUi>
- implements CallButtonPresenter.CallButtonUi, OnMenuItemClickListener, OnDismissListener,
- View.OnClickListener {
-
- private int mButtonMaxVisible;
- // The button is currently visible in the UI
- private static final int BUTTON_VISIBLE = 1;
- // The button is hidden in the UI
- private static final int BUTTON_HIDDEN = 2;
- // The button has been collapsed into the overflow menu
- private static final int BUTTON_MENU = 3;
-
- public interface Buttons {
-
- public static final int BUTTON_AUDIO = 0;
- public static final int BUTTON_MUTE = 1;
- public static final int BUTTON_DIALPAD = 2;
- public static final int BUTTON_HOLD = 3;
- public static final int BUTTON_SWAP = 4;
- public static final int BUTTON_UPGRADE_TO_VIDEO = 5;
- public static final int BUTTON_SWITCH_CAMERA = 6;
- public static final int BUTTON_DOWNGRADE_TO_AUDIO = 7;
- public static final int BUTTON_ADD_CALL = 8;
- public static final int BUTTON_MERGE = 9;
- public static final int BUTTON_PAUSE_VIDEO = 10;
- public static final int BUTTON_MANAGE_VIDEO_CONFERENCE = 11;
- public static final int BUTTON_COUNT = 12;
- }
-
- private SparseIntArray mButtonVisibilityMap = new SparseIntArray(BUTTON_COUNT);
-
- private CompoundButton mAudioButton;
- private CompoundButton mMuteButton;
- private CompoundButton mShowDialpadButton;
- private CompoundButton mHoldButton;
- private ImageButton mSwapButton;
- private ImageButton mChangeToVideoButton;
- private ImageButton mChangeToVoiceButton;
- private CompoundButton mSwitchCameraButton;
- private ImageButton mAddCallButton;
- private ImageButton mMergeButton;
- private CompoundButton mPauseVideoButton;
- private ImageButton mOverflowButton;
- private ImageButton mManageVideoCallConferenceButton;
-
- private PopupMenu mAudioModePopup;
- private boolean mAudioModePopupVisible;
- private PopupMenu mOverflowPopup;
-
- private int mPrevAudioMode = 0;
-
- // Constants for Drawable.setAlpha()
- private static final int HIDDEN = 0;
- private static final int VISIBLE = 255;
-
- private boolean mIsEnabled;
- private MaterialPalette mCurrentThemeColors;
-
- @Override
- public CallButtonPresenter createPresenter() {
- // TODO: find a cleaner way to include audio mode provider than having a singleton instance.
- return new CallButtonPresenter();
- }
-
- @Override
- public CallButtonPresenter.CallButtonUi getUi() {
- return this;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- for (int i = 0; i < BUTTON_COUNT; i++) {
- mButtonVisibilityMap.put(i, BUTTON_HIDDEN);
- }
-
- mButtonMaxVisible = getResources().getInteger(R.integer.call_card_max_buttons);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- final View parent = inflater.inflate(R.layout.call_button_fragment, container, false);
-
- mAudioButton = (CompoundButton) parent.findViewById(R.id.audioButton);
- mAudioButton.setOnClickListener(this);
- mMuteButton = (CompoundButton) parent.findViewById(R.id.muteButton);
- mMuteButton.setOnClickListener(this);
- mShowDialpadButton = (CompoundButton) parent.findViewById(R.id.dialpadButton);
- mShowDialpadButton.setOnClickListener(this);
- mHoldButton = (CompoundButton) parent.findViewById(R.id.holdButton);
- mHoldButton.setOnClickListener(this);
- mSwapButton = (ImageButton) parent.findViewById(R.id.swapButton);
- mSwapButton.setOnClickListener(this);
- mChangeToVideoButton = (ImageButton) parent.findViewById(R.id.changeToVideoButton);
- mChangeToVideoButton.setOnClickListener(this);
- mChangeToVoiceButton = (ImageButton) parent.findViewById(R.id.changeToVoiceButton);
- mChangeToVoiceButton.setOnClickListener(this);
- mSwitchCameraButton = (CompoundButton) parent.findViewById(R.id.switchCameraButton);
- mSwitchCameraButton.setOnClickListener(this);
- mAddCallButton = (ImageButton) parent.findViewById(R.id.addButton);
- mAddCallButton.setOnClickListener(this);
- mMergeButton = (ImageButton) parent.findViewById(R.id.mergeButton);
- mMergeButton.setOnClickListener(this);
- mPauseVideoButton = (CompoundButton) parent.findViewById(R.id.pauseVideoButton);
- mPauseVideoButton.setOnClickListener(this);
- mOverflowButton = (ImageButton) parent.findViewById(R.id.overflowButton);
- mOverflowButton.setOnClickListener(this);
- mManageVideoCallConferenceButton = (ImageButton) parent.findViewById(
- R.id.manageVideoCallConferenceButton);
- mManageVideoCallConferenceButton.setOnClickListener(this);
- return parent;
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- // set the buttons
- updateAudioButtons();
- }
-
- @Override
- public void onResume() {
- if (getPresenter() != null) {
- getPresenter().refreshMuteState();
- }
- super.onResume();
-
- updateColors();
- }
-
- @Override
- public void onClick(View view) {
- int id = view.getId();
- Log.d(this, "onClick(View " + view + ", id " + id + ")...");
-
- if (id == R.id.audioButton) {
- onAudioButtonClicked();
- } else if (id == R.id.addButton) {
- getPresenter().addCallClicked();
- } else if (id == R.id.muteButton) {
- getPresenter().muteClicked(!mMuteButton.isSelected());
- } else if (id == R.id.mergeButton) {
- getPresenter().mergeClicked();
- mMergeButton.setEnabled(false);
- } else if (id == R.id.holdButton) {
- getPresenter().holdClicked(!mHoldButton.isSelected());
- } else if (id == R.id.swapButton) {
- getPresenter().swapClicked();
- } else if (id == R.id.dialpadButton) {
- getPresenter().showDialpadClicked(!mShowDialpadButton.isSelected());
- } else if (id == R.id.changeToVideoButton) {
- getPresenter().changeToVideoClicked();
- } else if (id == R.id.changeToVoiceButton) {
- getPresenter().changeToVoiceClicked();
- } else if (id == R.id.switchCameraButton) {
- getPresenter().switchCameraClicked(
- mSwitchCameraButton.isSelected() /* useFrontFacingCamera */);
- } else if (id == R.id.pauseVideoButton) {
- getPresenter().pauseVideoClicked(
- !mPauseVideoButton.isSelected() /* pause */);
- } else if (id == R.id.overflowButton) {
- if (mOverflowPopup != null) {
- mOverflowPopup.show();
- }
- } else if (id == R.id.manageVideoCallConferenceButton) {
- onManageVideoCallConferenceClicked();
- } else {
- Log.wtf(this, "onClick: unexpected");
- return;
- }
-
- view.performHapticFeedback(
- HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
- }
-
- public void updateColors() {
- MaterialPalette themeColors = InCallPresenter.getInstance().getThemeColors();
-
- if (mCurrentThemeColors != null && mCurrentThemeColors.equals(themeColors)) {
- return;
- }
-
- View[] compoundButtons = {
- mAudioButton,
- mMuteButton,
- mShowDialpadButton,
- mHoldButton,
- mSwitchCameraButton,
- mPauseVideoButton
- };
-
- for (View button : compoundButtons) {
- final LayerDrawable layers = (LayerDrawable) button.getBackground();
- final RippleDrawable btnCompoundDrawable = compoundBackgroundDrawable(themeColors);
- layers.setDrawableByLayerId(R.id.compoundBackgroundItem, btnCompoundDrawable);
- }
-
- ImageButton[] normalButtons = {
- mSwapButton,
- mChangeToVideoButton,
- mChangeToVoiceButton,
- mAddCallButton,
- mMergeButton,
- mOverflowButton
- };
-
- for (ImageButton button : normalButtons) {
- final LayerDrawable layers = (LayerDrawable) button.getBackground();
- final RippleDrawable btnDrawable = backgroundDrawable(themeColors);
- layers.setDrawableByLayerId(R.id.backgroundItem, btnDrawable);
- }
-
- mCurrentThemeColors = themeColors;
- }
-
- /**
- * Generate a RippleDrawable which will be the background for a compound button, i.e.
- * a button with pressed and unpressed states. The unpressed state will be the same color
- * as the rest of the call card, the pressed state will be the dark version of that color.
- */
- private RippleDrawable compoundBackgroundDrawable(MaterialPalette palette) {
- Resources res = getResources();
- ColorStateList rippleColor =
- ColorStateList.valueOf(res.getColor(R.color.incall_accent_color));
-
- StateListDrawable stateListDrawable = new StateListDrawable();
- addSelectedAndFocused(res, stateListDrawable);
- addFocused(res, stateListDrawable);
- addSelected(res, stateListDrawable, palette);
- addUnselected(res, stateListDrawable, palette);
-
- return new RippleDrawable(rippleColor, stateListDrawable, null);
- }
-
- /**
- * Generate a RippleDrawable which will be the background of a button to ensure it
- * is the same color as the rest of the call card.
- */
- private RippleDrawable backgroundDrawable(MaterialPalette palette) {
- Resources res = getResources();
- ColorStateList rippleColor =
- ColorStateList.valueOf(res.getColor(R.color.incall_accent_color));
-
- StateListDrawable stateListDrawable = new StateListDrawable();
- addFocused(res, stateListDrawable);
- addUnselected(res, stateListDrawable, palette);
-
- return new RippleDrawable(rippleColor, stateListDrawable, null);
- }
-
- // state_selected and state_focused
- private void addSelectedAndFocused(Resources res, StateListDrawable drawable) {
- int[] selectedAndFocused = {android.R.attr.state_selected, android.R.attr.state_focused};
- Drawable selectedAndFocusedDrawable = res.getDrawable(R.drawable.btn_selected_focused);
- drawable.addState(selectedAndFocused, selectedAndFocusedDrawable);
- }
-
- // state_focused
- private void addFocused(Resources res, StateListDrawable drawable) {
- int[] focused = {android.R.attr.state_focused};
- Drawable focusedDrawable = res.getDrawable(R.drawable.btn_unselected_focused);
- drawable.addState(focused, focusedDrawable);
- }
-
- // state_selected
- private void addSelected(Resources res, StateListDrawable drawable, MaterialPalette palette) {
- int[] selected = {android.R.attr.state_selected};
- LayerDrawable selectedDrawable = (LayerDrawable) res.getDrawable(R.drawable.btn_selected);
- ((GradientDrawable) selectedDrawable.getDrawable(0)).setColor(palette.mSecondaryColor);
- drawable.addState(selected, selectedDrawable);
- }
-
- // default
- private void addUnselected(Resources res, StateListDrawable drawable, MaterialPalette palette) {
- LayerDrawable unselectedDrawable =
- (LayerDrawable) res.getDrawable(R.drawable.btn_unselected);
- ((GradientDrawable) unselectedDrawable.getDrawable(0)).setColor(palette.mPrimaryColor);
- drawable.addState(new int[0], unselectedDrawable);
- }
-
- @Override
- public void setEnabled(boolean isEnabled) {
- mIsEnabled = isEnabled;
-
- mAudioButton.setEnabled(isEnabled);
- mMuteButton.setEnabled(isEnabled);
- mShowDialpadButton.setEnabled(isEnabled);
- mHoldButton.setEnabled(isEnabled);
- mSwapButton.setEnabled(isEnabled);
- mChangeToVideoButton.setEnabled(isEnabled);
- mChangeToVoiceButton.setEnabled(isEnabled);
- mSwitchCameraButton.setEnabled(isEnabled);
- mAddCallButton.setEnabled(isEnabled);
- mMergeButton.setEnabled(isEnabled);
- mPauseVideoButton.setEnabled(isEnabled);
- mOverflowButton.setEnabled(isEnabled);
- mManageVideoCallConferenceButton.setEnabled(isEnabled);
- }
-
- @Override
- public void showButton(int buttonId, boolean show) {
- mButtonVisibilityMap.put(buttonId, show ? BUTTON_VISIBLE : BUTTON_HIDDEN);
- }
-
- @Override
- public void enableButton(int buttonId, boolean enable) {
- final View button = getButtonById(buttonId);
- if (button != null) {
- button.setEnabled(enable);
- }
- }
-
- private View getButtonById(int id) {
- if (id == BUTTON_AUDIO) {
- return mAudioButton;
- } else if (id == BUTTON_MUTE) {
- return mMuteButton;
- } else if (id == BUTTON_DIALPAD) {
- return mShowDialpadButton;
- } else if (id == BUTTON_HOLD) {
- return mHoldButton;
- } else if (id == BUTTON_SWAP) {
- return mSwapButton;
- } else if (id == BUTTON_UPGRADE_TO_VIDEO) {
- return mChangeToVideoButton;
- } else if (id == BUTTON_DOWNGRADE_TO_AUDIO) {
- return mChangeToVoiceButton;
- } else if (id == BUTTON_SWITCH_CAMERA) {
- return mSwitchCameraButton;
- } else if (id == BUTTON_ADD_CALL) {
- return mAddCallButton;
- } else if (id == BUTTON_MERGE) {
- return mMergeButton;
- } else if (id == BUTTON_PAUSE_VIDEO) {
- return mPauseVideoButton;
- } else if (id == BUTTON_MANAGE_VIDEO_CONFERENCE) {
- return mManageVideoCallConferenceButton;
- } else {
- Log.w(this, "Invalid button id");
- return null;
- }
- }
-
- @Override
- public void setHold(boolean value) {
- if (mHoldButton.isSelected() != value) {
- mHoldButton.setSelected(value);
- mHoldButton.setContentDescription(getContext().getString(
- value ? R.string.onscreenHoldText_selected
- : R.string.onscreenHoldText_unselected));
- }
- }
-
- @Override
- public void setCameraSwitched(boolean isBackFacingCamera) {
- mSwitchCameraButton.setSelected(isBackFacingCamera);
- }
-
- @Override
- public void setVideoPaused(boolean isVideoPaused) {
- mPauseVideoButton.setSelected(isVideoPaused);
-
- if (isVideoPaused) {
- mPauseVideoButton.setContentDescription(getText(R.string.onscreenTurnOnCameraText));
- } else {
- mPauseVideoButton.setContentDescription(getText(R.string.onscreenTurnOffCameraText));
- }
- }
-
- @Override
- public void setMute(boolean value) {
- if (mMuteButton.isSelected() != value) {
- mMuteButton.setSelected(value);
- mMuteButton.setContentDescription(getContext().getString(
- value ? R.string.onscreenMuteText_selected
- : R.string.onscreenMuteText_unselected));
- }
- }
-
- private void addToOverflowMenu(int id, View button, PopupMenu menu) {
- button.setVisibility(View.GONE);
- menu.getMenu().add(Menu.NONE, id, Menu.NONE, button.getContentDescription());
- mButtonVisibilityMap.put(id, BUTTON_MENU);
- }
-
- private PopupMenu getPopupMenu() {
- return new PopupMenu(new ContextThemeWrapper(getActivity(), R.style.InCallPopupMenuStyle),
- mOverflowButton);
- }
-
- /**
- * Iterates through the list of buttons and toggles their visibility depending on the
- * setting configured by the CallButtonPresenter. If there are more visible buttons than
- * the allowed maximum, the excess buttons are collapsed into a single overflow menu.
- */
- @Override
- public void updateButtonStates() {
- View prevVisibleButton = null;
- int prevVisibleId = -1;
- PopupMenu menu = null;
- int visibleCount = 0;
- for (int i = 0; i < BUTTON_COUNT; i++) {
- final int visibility = mButtonVisibilityMap.get(i);
- final View button = getButtonById(i);
- if (visibility == BUTTON_VISIBLE) {
- visibleCount++;
- if (visibleCount <= mButtonMaxVisible) {
- button.setVisibility(View.VISIBLE);
- prevVisibleButton = button;
- prevVisibleId = i;
- } else {
- if (menu == null) {
- menu = getPopupMenu();
- }
- // Collapse the current button into the overflow menu. If is the first visible
- // button that exceeds the threshold, also collapse the previous visible button
- // so that the total number of visible buttons will never exceed the threshold.
- if (prevVisibleButton != null) {
- addToOverflowMenu(prevVisibleId, prevVisibleButton, menu);
- prevVisibleButton = null;
- prevVisibleId = -1;
- }
- addToOverflowMenu(i, button, menu);
- }
- } else if (visibility == BUTTON_HIDDEN) {
- button.setVisibility(View.GONE);
- }
- }
-
- mOverflowButton.setVisibility(menu != null ? View.VISIBLE : View.GONE);
- if (menu != null) {
- mOverflowPopup = menu;
- mOverflowPopup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- final int id = item.getItemId();
- getButtonById(id).performClick();
- return true;
- }
- });
- }
- }
-
- @Override
- public void setAudio(int mode) {
- updateAudioButtons();
- refreshAudioModePopup();
-
- if (mPrevAudioMode != mode) {
- updateAudioButtonContentDescription(mode);
- mPrevAudioMode = mode;
- }
- }
-
- @Override
- public void setSupportedAudio(int modeMask) {
- updateAudioButtons();
- refreshAudioModePopup();
- }
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- Log.d(this, "- onMenuItemClick: " + item);
- Log.d(this, " id: " + item.getItemId());
- Log.d(this, " title: '" + item.getTitle() + "'");
-
- int mode = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
- int resId = item.getItemId();
-
- if (resId == R.id.audio_mode_speaker) {
- mode = CallAudioState.ROUTE_SPEAKER;
- } else if (resId == R.id.audio_mode_earpiece || resId == R.id.audio_mode_wired_headset) {
- // InCallCallAudioState.ROUTE_EARPIECE means either the handset earpiece,
- // or the wired headset (if connected.)
- mode = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
- } else if (resId == R.id.audio_mode_bluetooth) {
- mode = CallAudioState.ROUTE_BLUETOOTH;
- } else {
- Log.e(this, "onMenuItemClick: unexpected View ID " + item.getItemId()
- + " (MenuItem = '" + item + "')");
- }
-
- getPresenter().setAudioMode(mode);
-
- return true;
- }
-
- // PopupMenu.OnDismissListener implementation; see showAudioModePopup().
- // This gets called when the PopupMenu gets dismissed for *any* reason, like
- // the user tapping outside its bounds, or pressing Back, or selecting one
- // of the menu items.
- @Override
- public void onDismiss(PopupMenu menu) {
- Log.d(this, "- onDismiss: " + menu);
- mAudioModePopupVisible = false;
- updateAudioButtons();
- }
-
- /**
- * Checks for supporting modes. If bluetooth is supported, it uses the audio
- * pop up menu. Otherwise, it toggles the speakerphone.
- */
- private void onAudioButtonClicked() {
- Log.d(this, "onAudioButtonClicked: " +
- CallAudioState.audioRouteToString(getPresenter().getSupportedAudio()));
-
- if (isSupported(CallAudioState.ROUTE_BLUETOOTH)) {
- showAudioModePopup();
- } else {
- getPresenter().toggleSpeakerphone();
- }
- }
-
- private void onManageVideoCallConferenceClicked() {
- Log.d(this, "onManageVideoCallConferenceClicked");
- InCallPresenter.getInstance().showConferenceCallManager(true);
- }
-
- /**
- * Refreshes the "Audio mode" popup if it's visible. This is useful
- * (for example) when a wired headset is plugged or unplugged,
- * since we need to switch back and forth between the "earpiece"
- * and "wired headset" items.
- *
- * This is safe to call even if the popup is already dismissed, or even if
- * you never called showAudioModePopup() in the first place.
- */
- public void refreshAudioModePopup() {
- if (mAudioModePopup != null && mAudioModePopupVisible) {
- // Dismiss the previous one
- mAudioModePopup.dismiss(); // safe even if already dismissed
- // And bring up a fresh PopupMenu
- showAudioModePopup();
- }
- }
-
- /**
- * Updates the audio button so that the appriopriate visual layers
- * are visible based on the supported audio formats.
- */
- private void updateAudioButtons() {
- final boolean bluetoothSupported = isSupported(CallAudioState.ROUTE_BLUETOOTH);
- final boolean speakerSupported = isSupported(CallAudioState.ROUTE_SPEAKER);
-
- boolean audioButtonEnabled = false;
- boolean audioButtonChecked = false;
- boolean showMoreIndicator = false;
-
- boolean showBluetoothIcon = false;
- boolean showSpeakerphoneIcon = false;
- boolean showHandsetIcon = false;
-
- boolean showToggleIndicator = false;
-
- if (bluetoothSupported) {
- Log.d(this, "updateAudioButtons - popup menu mode");
-
- audioButtonEnabled = true;
- audioButtonChecked = true;
- showMoreIndicator = true;
-
- // Update desired layers:
- if (isAudio(CallAudioState.ROUTE_BLUETOOTH)) {
- showBluetoothIcon = true;
- } else if (isAudio(CallAudioState.ROUTE_SPEAKER)) {
- showSpeakerphoneIcon = true;
- } else {
- showHandsetIcon = true;
- // TODO: if a wired headset is plugged in, that takes precedence
- // over the handset earpiece. If so, maybe we should show some
- // sort of "wired headset" icon here instead of the "handset
- // earpiece" icon. (Still need an asset for that, though.)
- }
-
- // The audio button is NOT a toggle in this state, so set selected to false.
- mAudioButton.setSelected(false);
- } else if (speakerSupported) {
- Log.d(this, "updateAudioButtons - speaker toggle mode");
-
- audioButtonEnabled = true;
-
- // The audio button *is* a toggle in this state, and indicated the
- // current state of the speakerphone.
- audioButtonChecked = isAudio(CallAudioState.ROUTE_SPEAKER);
- mAudioButton.setSelected(audioButtonChecked);
-
- // update desired layers:
- showToggleIndicator = true;
- showSpeakerphoneIcon = true;
- } else {
- Log.d(this, "updateAudioButtons - disabled...");
-
- // The audio button is a toggle in this state, but that's mostly
- // irrelevant since it's always disabled and unchecked.
- audioButtonEnabled = false;
- audioButtonChecked = false;
- mAudioButton.setSelected(false);
-
- // update desired layers:
- showToggleIndicator = true;
- showSpeakerphoneIcon = true;
- }
-
- // Finally, update it all!
-
- Log.v(this, "audioButtonEnabled: " + audioButtonEnabled);
- Log.v(this, "audioButtonChecked: " + audioButtonChecked);
- Log.v(this, "showMoreIndicator: " + showMoreIndicator);
- Log.v(this, "showBluetoothIcon: " + showBluetoothIcon);
- Log.v(this, "showSpeakerphoneIcon: " + showSpeakerphoneIcon);
- Log.v(this, "showHandsetIcon: " + showHandsetIcon);
-
- // Only enable the audio button if the fragment is enabled.
- mAudioButton.setEnabled(audioButtonEnabled && mIsEnabled);
- mAudioButton.setChecked(audioButtonChecked);
-
- final LayerDrawable layers = (LayerDrawable) mAudioButton.getBackground();
- Log.d(this, "'layers' drawable: " + layers);
-
- layers.findDrawableByLayerId(R.id.compoundBackgroundItem)
- .setAlpha(showToggleIndicator ? VISIBLE : HIDDEN);
-
- layers.findDrawableByLayerId(R.id.moreIndicatorItem)
- .setAlpha(showMoreIndicator ? VISIBLE : HIDDEN);
-
- layers.findDrawableByLayerId(R.id.bluetoothItem)
- .setAlpha(showBluetoothIcon ? VISIBLE : HIDDEN);
-
- layers.findDrawableByLayerId(R.id.handsetItem)
- .setAlpha(showHandsetIcon ? VISIBLE : HIDDEN);
-
- layers.findDrawableByLayerId(R.id.speakerphoneItem)
- .setAlpha(showSpeakerphoneIcon ? VISIBLE : HIDDEN);
-
- }
-
- /**
- * Update the content description of the audio button.
- */
- private void updateAudioButtonContentDescription(int mode) {
- int stringId = 0;
-
- // If bluetooth is not supported, the audio buttion will toggle, so use the label "speaker".
- // Otherwise, use the label of the currently selected audio mode.
- if (!isSupported(CallAudioState.ROUTE_BLUETOOTH)) {
- stringId = R.string.audio_mode_speaker;
- } else {
- switch (mode) {
- case CallAudioState.ROUTE_EARPIECE:
- stringId = R.string.audio_mode_earpiece;
- break;
- case CallAudioState.ROUTE_BLUETOOTH:
- stringId = R.string.audio_mode_bluetooth;
- break;
- case CallAudioState.ROUTE_WIRED_HEADSET:
- stringId = R.string.audio_mode_wired_headset;
- break;
- case CallAudioState.ROUTE_SPEAKER:
- stringId = R.string.audio_mode_speaker;
- break;
- }
- }
-
- if (stringId != 0) {
- mAudioButton.setContentDescription(getResources().getString(stringId));
- }
- }
-
- private void showAudioModePopup() {
- Log.d(this, "showAudioPopup()...");
-
- final ContextThemeWrapper contextWrapper = new ContextThemeWrapper(getActivity(),
- R.style.InCallPopupMenuStyle);
- mAudioModePopup = new PopupMenu(contextWrapper, mAudioButton /* anchorView */);
- mAudioModePopup.getMenuInflater().inflate(R.menu.incall_audio_mode_menu,
- mAudioModePopup.getMenu());
- mAudioModePopup.setOnMenuItemClickListener(this);
- mAudioModePopup.setOnDismissListener(this);
-
- final Menu menu = mAudioModePopup.getMenu();
-
- // TODO: Still need to have the "currently active" audio mode come
- // up pre-selected (or focused?) with a blue highlight. Still
- // need exact visual design, and possibly framework support for this.
- // See comments below for the exact logic.
-
- final MenuItem speakerItem = menu.findItem(R.id.audio_mode_speaker);
- speakerItem.setEnabled(isSupported(CallAudioState.ROUTE_SPEAKER));
- // TODO: Show speakerItem as initially "selected" if
- // speaker is on.
-
- // We display *either* "earpiece" or "wired headset", never both,
- // depending on whether a wired headset is physically plugged in.
- final MenuItem earpieceItem = menu.findItem(R.id.audio_mode_earpiece);
- final MenuItem wiredHeadsetItem = menu.findItem(R.id.audio_mode_wired_headset);
-
- final boolean usingHeadset = isSupported(CallAudioState.ROUTE_WIRED_HEADSET);
- earpieceItem.setVisible(!usingHeadset);
- earpieceItem.setEnabled(!usingHeadset);
- wiredHeadsetItem.setVisible(usingHeadset);
- wiredHeadsetItem.setEnabled(usingHeadset);
- // TODO: Show the above item (either earpieceItem or wiredHeadsetItem)
- // as initially "selected" if speakerOn and
- // bluetoothIndicatorOn are both false.
-
- final MenuItem bluetoothItem = menu.findItem(R.id.audio_mode_bluetooth);
- bluetoothItem.setEnabled(isSupported(CallAudioState.ROUTE_BLUETOOTH));
- // TODO: Show bluetoothItem as initially "selected" if
- // bluetoothIndicatorOn is true.
-
- mAudioModePopup.show();
-
- // Unfortunately we need to manually keep track of the popup menu's
- // visiblity, since PopupMenu doesn't have an isShowing() method like
- // Dialogs do.
- mAudioModePopupVisible = true;
- }
-
- private boolean isSupported(int mode) {
- return (mode == (getPresenter().getSupportedAudio() & mode));
- }
-
- private boolean isAudio(int mode) {
- return (mode == getPresenter().getAudioMode());
- }
-
- @Override
- public void displayDialpad(boolean value, boolean animate) {
- if (getActivity() != null && getActivity() instanceof InCallActivity) {
- boolean changed = ((InCallActivity) getActivity()).showDialpadFragment(value, animate);
- if (changed) {
- mShowDialpadButton.setSelected(value);
- mShowDialpadButton.setContentDescription(getContext().getString(
- value /* show */ ? R.string.onscreenShowDialpadText_unselected
- : R.string.onscreenShowDialpadText_selected));
- }
- }
- }
-
- @Override
- public boolean isDialpadVisible() {
- if (getActivity() != null && getActivity() instanceof InCallActivity) {
- return ((InCallActivity) getActivity()).isDialpadVisible();
- }
- return false;
- }
-
- @Override
- public Context getContext() {
- return getActivity();
- }
-}
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
deleted file mode 100644
index defafda99..000000000
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ /dev/null
@@ -1,486 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_ADD_CALL;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_AUDIO;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_DIALPAD;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_DOWNGRADE_TO_AUDIO;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_HOLD;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_MERGE;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_MUTE;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_PAUSE_VIDEO;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_SWAP;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_SWITCH_CAMERA;
-import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_UPGRADE_TO_VIDEO;
-
-import android.content.Context;
-import android.os.Build;
-import android.os.Bundle;
-import android.telecom.CallAudioState;
-import android.telecom.InCallService.VideoCall;
-import android.telecom.VideoProfile;
-
-import com.android.contacts.common.compat.CallSdkCompat;
-import com.android.contacts.common.compat.SdkVersionOverride;
-import com.android.dialer.compat.UserManagerCompat;
-import com.android.incallui.AudioModeProvider.AudioModeListener;
-import com.android.incallui.InCallCameraManager.Listener;
-import com.android.incallui.InCallPresenter.CanAddCallListener;
-import com.android.incallui.InCallPresenter.InCallDetailsListener;
-import com.android.incallui.InCallPresenter.InCallState;
-import com.android.incallui.InCallPresenter.InCallStateListener;
-import com.android.incallui.InCallPresenter.IncomingCallListener;
-
-/**
- * Logic for call buttons.
- */
-public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButtonUi>
- implements InCallStateListener, AudioModeListener, IncomingCallListener,
- InCallDetailsListener, CanAddCallListener, Listener {
-
- private static final String KEY_AUTOMATICALLY_MUTED = "incall_key_automatically_muted";
- private static final String KEY_PREVIOUS_MUTE_STATE = "incall_key_previous_mute_state";
-
- private Call mCall;
- private boolean mAutomaticallyMuted = false;
- private boolean mPreviousMuteState = false;
-
- public CallButtonPresenter() {
- }
-
- @Override
- public void onUiReady(CallButtonUi ui) {
- super.onUiReady(ui);
-
- AudioModeProvider.getInstance().addListener(this);
-
- // register for call state changes last
- final InCallPresenter inCallPresenter = InCallPresenter.getInstance();
- inCallPresenter.addListener(this);
- inCallPresenter.addIncomingCallListener(this);
- inCallPresenter.addDetailsListener(this);
- inCallPresenter.addCanAddCallListener(this);
- inCallPresenter.getInCallCameraManager().addCameraSelectionListener(this);
-
- // Update the buttons state immediately for the current call
- onStateChange(InCallState.NO_CALLS, inCallPresenter.getInCallState(),
- CallList.getInstance());
- }
-
- @Override
- public void onUiUnready(CallButtonUi ui) {
- super.onUiUnready(ui);
-
- InCallPresenter.getInstance().removeListener(this);
- AudioModeProvider.getInstance().removeListener(this);
- InCallPresenter.getInstance().removeIncomingCallListener(this);
- InCallPresenter.getInstance().removeDetailsListener(this);
- InCallPresenter.getInstance().getInCallCameraManager().removeCameraSelectionListener(this);
- InCallPresenter.getInstance().removeCanAddCallListener(this);
- }
-
- @Override
- public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
- CallButtonUi ui = getUi();
-
- if (newState == InCallState.OUTGOING) {
- mCall = callList.getOutgoingCall();
- } else if (newState == InCallState.INCALL) {
- mCall = callList.getActiveOrBackgroundCall();
-
- // When connected to voice mail, automatically shows the dialpad.
- // (On previous releases we showed it when in-call shows up, before waiting for
- // OUTGOING. We may want to do that once we start showing "Voice mail" label on
- // the dialpad too.)
- if (ui != null) {
- if (oldState == InCallState.OUTGOING && mCall != null) {
- if (CallerInfoUtils.isVoiceMailNumber(ui.getContext(), mCall)) {
- ui.displayDialpad(true /* show */, true /* animate */);
- }
- }
- }
- } else if (newState == InCallState.INCOMING) {
- if (ui != null) {
- ui.displayDialpad(false /* show */, true /* animate */);
- }
- mCall = callList.getIncomingCall();
- } else {
- mCall = null;
- }
- updateUi(newState, mCall);
- }
-
- /**
- * Updates the user interface in response to a change in the details of a call.
- * Currently handles changes to the call buttons in response to a change in the details for a
- * call. This is important to ensure changes to the active call are reflected in the available
- * buttons.
- *
- * @param call The active call.
- * @param details The call details.
- */
- @Override
- public void onDetailsChanged(Call call, android.telecom.Call.Details details) {
- // Only update if the changes are for the currently active call
- if (getUi() != null && call != null && call.equals(mCall)) {
- updateButtonsState(call);
- }
- }
-
- @Override
- public void onIncomingCall(InCallState oldState, InCallState newState, Call call) {
- onStateChange(oldState, newState, CallList.getInstance());
- }
-
- @Override
- public void onCanAddCallChanged(boolean canAddCall) {
- if (getUi() != null && mCall != null) {
- updateButtonsState(mCall);
- }
- }
-
- @Override
- public void onAudioMode(int mode) {
- if (getUi() != null) {
- getUi().setAudio(mode);
- }
- }
-
- @Override
- public void onSupportedAudioMode(int mask) {
- if (getUi() != null) {
- getUi().setSupportedAudio(mask);
- }
- }
-
- @Override
- public void onMute(boolean muted) {
- if (getUi() != null && !mAutomaticallyMuted) {
- getUi().setMute(muted);
- }
- }
-
- public int getAudioMode() {
- return AudioModeProvider.getInstance().getAudioMode();
- }
-
- public int getSupportedAudio() {
- return AudioModeProvider.getInstance().getSupportedModes();
- }
-
- public void setAudioMode(int mode) {
-
- // TODO: Set a intermediate state in this presenter until we get
- // an update for onAudioMode(). This will make UI response immediate
- // if it turns out to be slow
-
- Log.d(this, "Sending new Audio Mode: " + CallAudioState.audioRouteToString(mode));
- TelecomAdapter.getInstance().setAudioRoute(mode);
- }
-
- /**
- * Function assumes that bluetooth is not supported.
- */
- public void toggleSpeakerphone() {
- // this function should not be called if bluetooth is available
- if (0 != (CallAudioState.ROUTE_BLUETOOTH & getSupportedAudio())) {
-
- // It's clear the UI is wrong, so update the supported mode once again.
- Log.e(this, "toggling speakerphone not allowed when bluetooth supported.");
- getUi().setSupportedAudio(getSupportedAudio());
- return;
- }
-
- int newMode = CallAudioState.ROUTE_SPEAKER;
-
- // if speakerphone is already on, change to wired/earpiece
- if (getAudioMode() == CallAudioState.ROUTE_SPEAKER) {
- newMode = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
- }
-
- setAudioMode(newMode);
- }
-
- public void muteClicked(boolean checked) {
- Log.d(this, "turning on mute: " + checked);
- TelecomAdapter.getInstance().mute(checked);
- }
-
- public void holdClicked(boolean checked) {
- if (mCall == null) {
- return;
- }
- if (checked) {
- Log.i(this, "Putting the call on hold: " + mCall);
- TelecomAdapter.getInstance().holdCall(mCall.getId());
- } else {
- Log.i(this, "Removing the call from hold: " + mCall);
- TelecomAdapter.getInstance().unholdCall(mCall.getId());
- }
- }
-
- public void swapClicked() {
- if (mCall == null) {
- return;
- }
-
- Log.i(this, "Swapping the call: " + mCall);
- TelecomAdapter.getInstance().swap(mCall.getId());
- }
-
- public void mergeClicked() {
- TelecomAdapter.getInstance().merge(mCall.getId());
- }
-
- public void addCallClicked() {
- // Automatically mute the current call
- mAutomaticallyMuted = true;
- mPreviousMuteState = AudioModeProvider.getInstance().getMute();
- // Simulate a click on the mute button
- muteClicked(true);
- TelecomAdapter.getInstance().addCall();
- }
-
- public void changeToVoiceClicked() {
- VideoCall videoCall = mCall.getVideoCall();
- if (videoCall == null) {
- return;
- }
-
- VideoProfile videoProfile = new VideoProfile(VideoProfile.STATE_AUDIO_ONLY);
- videoCall.sendSessionModifyRequest(videoProfile);
- }
-
- public void showDialpadClicked(boolean checked) {
- Log.v(this, "Show dialpad " + String.valueOf(checked));
- getUi().displayDialpad(checked /* show */, true /* animate */);
- }
-
- public void changeToVideoClicked() {
- VideoCall videoCall = mCall.getVideoCall();
- if (videoCall == null) {
- return;
- }
- int currVideoState = mCall.getVideoState();
- int currUnpausedVideoState = VideoUtils.getUnPausedVideoState(currVideoState);
- currUnpausedVideoState |= VideoProfile.STATE_BIDIRECTIONAL;
-
- VideoProfile videoProfile = new VideoProfile(currUnpausedVideoState);
- videoCall.sendSessionModifyRequest(videoProfile);
- mCall.setSessionModificationState(Call.SessionModificationState.WAITING_FOR_RESPONSE);
- }
-
- /**
- * Switches the camera between the front-facing and back-facing camera.
- * @param useFrontFacingCamera True if we should switch to using the front-facing camera, or
- * false if we should switch to using the back-facing camera.
- */
- public void switchCameraClicked(boolean useFrontFacingCamera) {
- InCallCameraManager cameraManager = InCallPresenter.getInstance().getInCallCameraManager();
- cameraManager.setUseFrontFacingCamera(useFrontFacingCamera);
-
- VideoCall videoCall = mCall.getVideoCall();
- if (videoCall == null) {
- return;
- }
-
- String cameraId = cameraManager.getActiveCameraId();
- if (cameraId != null) {
- final int cameraDir = cameraManager.isUsingFrontFacingCamera()
- ? Call.VideoSettings.CAMERA_DIRECTION_FRONT_FACING
- : Call.VideoSettings.CAMERA_DIRECTION_BACK_FACING;
- mCall.getVideoSettings().setCameraDir(cameraDir);
- videoCall.setCamera(cameraId);
- videoCall.requestCameraCapabilities();
- }
- }
-
-
- /**
- * Stop or start client's video transmission.
- * @param pause True if pausing the local user's video, or false if starting the local user's
- * video.
- */
- public void pauseVideoClicked(boolean pause) {
- VideoCall videoCall = mCall.getVideoCall();
- if (videoCall == null) {
- return;
- }
-
- final int currUnpausedVideoState = VideoUtils.getUnPausedVideoState(mCall.getVideoState());
- if (pause) {
- videoCall.setCamera(null);
- VideoProfile videoProfile = new VideoProfile(currUnpausedVideoState
- & ~VideoProfile.STATE_TX_ENABLED);
- videoCall.sendSessionModifyRequest(videoProfile);
- } else {
- InCallCameraManager cameraManager = InCallPresenter.getInstance().
- getInCallCameraManager();
- videoCall.setCamera(cameraManager.getActiveCameraId());
- VideoProfile videoProfile = new VideoProfile(currUnpausedVideoState
- | VideoProfile.STATE_TX_ENABLED);
- videoCall.sendSessionModifyRequest(videoProfile);
- mCall.setSessionModificationState(Call.SessionModificationState.WAITING_FOR_RESPONSE);
- }
- getUi().setVideoPaused(pause);
- }
-
- private void updateUi(InCallState state, Call call) {
- Log.d(this, "Updating call UI for call: ", call);
-
- final CallButtonUi ui = getUi();
- if (ui == null) {
- return;
- }
-
- final boolean isEnabled =
- state.isConnectingOrConnected() &&!state.isIncoming() && call != null;
- ui.setEnabled(isEnabled);
-
- if (call == null) {
- return;
- }
-
- updateButtonsState(call);
- }
-
- /**
- * Updates the buttons applicable for the UI.
- *
- * @param call The active call.
- */
- private void updateButtonsState(Call call) {
- Log.v(this, "updateButtonsState");
- final CallButtonUi ui = getUi();
- final boolean isVideo = VideoUtils.isVideoCall(call);
-
- // Common functionality (audio, hold, etc).
- // Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available:
- // (1) If the device normally can hold, show HOLD in a disabled state.
- // (2) If the device doesn't have the concept of hold/swap, remove the button.
- final boolean showSwap = call.can(
- android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
- final boolean showHold = !showSwap
- && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD)
- && call.can(android.telecom.Call.Details.CAPABILITY_HOLD);
- final boolean isCallOnHold = call.getState() == Call.State.ONHOLD;
-
- final boolean showAddCall = TelecomAdapter.getInstance().canAddCall()
- && UserManagerCompat.isUserUnlocked(ui.getContext());
- final boolean showMerge = call.can(
- android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
- final boolean showUpgradeToVideo = !isVideo && hasVideoCallCapabilities(call);
- final boolean showDowngradeToAudio = isVideo && isDowngradeToAudioSupported(call);
- final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE);
-
- ui.showButton(BUTTON_AUDIO, true);
- ui.showButton(BUTTON_SWAP, showSwap);
- ui.showButton(BUTTON_HOLD, showHold);
- ui.setHold(isCallOnHold);
- ui.showButton(BUTTON_MUTE, showMute);
- ui.showButton(BUTTON_ADD_CALL, showAddCall);
- ui.showButton(BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo);
- ui.showButton(BUTTON_DOWNGRADE_TO_AUDIO, showDowngradeToAudio);
- ui.showButton(BUTTON_SWITCH_CAMERA, isVideo);
- ui.showButton(BUTTON_PAUSE_VIDEO, isVideo);
- if (isVideo) {
- getUi().setVideoPaused(!VideoUtils.isTransmissionEnabled(call));
- }
- ui.showButton(BUTTON_DIALPAD, true);
- ui.showButton(BUTTON_MERGE, showMerge);
-
- ui.updateButtonStates();
- }
-
- private boolean hasVideoCallCapabilities(Call call) {
- if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) {
- return call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX)
- && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX);
- }
- // In L, this single flag represents both video transmitting and receiving capabilities
- return call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX);
- }
-
- /**
- * Determines if downgrading from a video call to an audio-only call is supported. In order to
- * support downgrade to audio, the SDK version must be >= N and the call should NOT have the
- * {@link android.telecom.Call.Details#CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO}.
- * @param call The call.
- * @return {@code true} if downgrading to an audio-only call from a video call is supported.
- */
- private boolean isDowngradeToAudioSupported(Call call) {
- return !call.can(CallSdkCompat.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO);
- }
-
- public void refreshMuteState() {
- // Restore the previous mute state
- if (mAutomaticallyMuted &&
- AudioModeProvider.getInstance().getMute() != mPreviousMuteState) {
- if (getUi() == null) {
- return;
- }
- muteClicked(mPreviousMuteState);
- }
- mAutomaticallyMuted = false;
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean(KEY_AUTOMATICALLY_MUTED, mAutomaticallyMuted);
- outState.putBoolean(KEY_PREVIOUS_MUTE_STATE, mPreviousMuteState);
- }
-
- @Override
- public void onRestoreInstanceState(Bundle savedInstanceState) {
- mAutomaticallyMuted =
- savedInstanceState.getBoolean(KEY_AUTOMATICALLY_MUTED, mAutomaticallyMuted);
- mPreviousMuteState =
- savedInstanceState.getBoolean(KEY_PREVIOUS_MUTE_STATE, mPreviousMuteState);
- super.onRestoreInstanceState(savedInstanceState);
- }
-
- public interface CallButtonUi extends Ui {
- void showButton(int buttonId, boolean show);
- void enableButton(int buttonId, boolean enable);
- void setEnabled(boolean on);
- void setMute(boolean on);
- void setHold(boolean on);
- void setCameraSwitched(boolean isBackFacingCamera);
- void setVideoPaused(boolean isPaused);
- void setAudio(int mode);
- void setSupportedAudio(int mask);
- void displayDialpad(boolean on, boolean animate);
- boolean isDialpadVisible();
-
- /**
- * Once showButton() has been called on each of the individual buttons in the UI, call
- * this to configure the overflow menu appropriately.
- */
- void updateButtonStates();
- Context getContext();
- }
-
- @Override
- public void onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera) {
- if (getUi() == null) {
- return;
- }
- getUi().setCameraSwitched(!isUsingFrontFacingCamera);
- }
-}
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java
deleted file mode 100644
index c2022d18c..000000000
--- a/InCallUI/src/com/android/incallui/CallCardFragment.java
+++ /dev/null
@@ -1,1510 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.AnimationDrawable;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Trace;
-import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
-import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
-import android.telecom.DisconnectCause;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnLayoutChangeListener;
-import android.view.ViewGroup;
-import android.view.ViewPropertyAnimator;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
-import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
-import com.android.contacts.common.widget.FloatingActionButtonController;
-import com.android.dialer.R;
-import com.android.phone.common.animation.AnimUtils;
-
-import java.util.List;
-
-/**
- * Fragment for call card.
- */
-public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPresenter.CallCardUi>
- implements CallCardPresenter.CallCardUi {
- private static final String TAG = "CallCardFragment";
-
- /**
- * Internal class which represents the call state label which is to be applied.
- */
- private class CallStateLabel {
- private CharSequence mCallStateLabel;
- private boolean mIsAutoDismissing;
-
- public CallStateLabel(CharSequence callStateLabel, boolean isAutoDismissing) {
- mCallStateLabel = callStateLabel;
- mIsAutoDismissing = isAutoDismissing;
- }
-
- public CharSequence getCallStateLabel() {
- return mCallStateLabel;
- }
-
- /**
- * Determines if the call state label should auto-dismiss.
- *
- * @return {@code true} if the call state label should auto-dismiss.
- */
- public boolean isAutoDismissing() {
- return mIsAutoDismissing;
- }
- };
-
- private static final String IS_DIALPAD_SHOWING_KEY = "is_dialpad_showing";
-
- /**
- * The duration of time (in milliseconds) a call state label should remain visible before
- * resetting to its previous value.
- */
- private static final long CALL_STATE_LABEL_RESET_DELAY_MS = 3000;
- /**
- * Amount of time to wait before sending an announcement via the accessibility manager.
- * When the call state changes to an outgoing or incoming state for the first time, the
- * UI can often be changing due to call updates or contact lookup. This allows the UI
- * to settle to a stable state to ensure that the correct information is announced.
- */
- private static final long ACCESSIBILITY_ANNOUNCEMENT_DELAY_MS = 500;
-
- private AnimatorSet mAnimatorSet;
- private int mShrinkAnimationDuration;
- private int mFabNormalDiameter;
- private int mFabSmallDiameter;
- private boolean mIsLandscape;
- private boolean mHasLargePhoto;
- private boolean mIsDialpadShowing;
-
- // Primary caller info
- private TextView mPhoneNumber;
- private TextView mNumberLabel;
- private TextView mPrimaryName;
- private View mCallStateButton;
- private ImageView mCallStateIcon;
- private ImageView mCallStateVideoCallIcon;
- private TextView mCallStateLabel;
- private TextView mCallTypeLabel;
- private ImageView mHdAudioIcon;
- private ImageView mForwardIcon;
- private ImageView mSpamIcon;
- private View mCallNumberAndLabel;
- private TextView mElapsedTime;
- private Drawable mPrimaryPhotoDrawable;
- private TextView mCallSubject;
- private ImageView mWorkProfileIcon;
-
- // Container view that houses the entire primary call card, including the call buttons
- private View mPrimaryCallCardContainer;
- // Container view that houses the primary call information
- private ViewGroup mPrimaryCallInfo;
- private View mCallButtonsContainer;
- private ImageView mPhotoSmall;
-
- // Secondary caller info
- private View mSecondaryCallInfo;
- private TextView mSecondaryCallName;
- private View mSecondaryCallProviderInfo;
- private TextView mSecondaryCallProviderLabel;
- private View mSecondaryCallConferenceCallIcon;
- private View mSecondaryCallVideoCallIcon;
- private View mProgressSpinner;
-
- // Call card content
- private View mCallCardContent;
- private ImageView mPhotoLarge;
- private View mContactContext;
- private TextView mContactContextTitle;
- private ListView mContactContextListView;
- private LinearLayout mContactContextListHeaders;
-
- private View mManageConferenceCallButton;
-
- // Dark number info bar
- private TextView mInCallMessageLabel;
-
- private FloatingActionButtonController mFloatingActionButtonController;
- private View mFloatingActionButtonContainer;
- private ImageButton mFloatingActionButton;
- private int mFloatingActionButtonVerticalOffset;
-
- private float mTranslationOffset;
- private Animation mPulseAnimation;
-
- private int mVideoAnimationDuration;
- // Whether or not the call card is currently in the process of an animation
- private boolean mIsAnimating;
-
- private MaterialPalette mCurrentThemeColors;
-
- /**
- * Call state label to set when an auto-dismissing call state label is dismissed.
- */
- private CharSequence mPostResetCallStateLabel;
- private boolean mCallStateLabelResetPending = false;
- private Handler mHandler;
-
- /**
- * Determines if secondary call info is populated in the secondary call info UI.
- */
- private boolean mHasSecondaryCallInfo = false;
-
- @Override
- public CallCardPresenter.CallCardUi getUi() {
- return this;
- }
-
- @Override
- public CallCardPresenter createPresenter() {
- return new CallCardPresenter();
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mHandler = new Handler(Looper.getMainLooper());
- mShrinkAnimationDuration = getResources().getInteger(R.integer.shrink_animation_duration);
- mVideoAnimationDuration = getResources().getInteger(R.integer.video_animation_duration);
- mFloatingActionButtonVerticalOffset = getResources().getDimensionPixelOffset(
- R.dimen.floating_action_button_vertical_offset);
- mFabNormalDiameter = getResources().getDimensionPixelOffset(
- R.dimen.end_call_floating_action_button_diameter);
- mFabSmallDiameter = getResources().getDimensionPixelOffset(
- R.dimen.end_call_floating_action_button_small_diameter);
-
- if (savedInstanceState != null) {
- mIsDialpadShowing = savedInstanceState.getBoolean(IS_DIALPAD_SHOWING_KEY, false);
- }
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- final CallList calls = CallList.getInstance();
- final Call call = calls.getFirstCall();
- getPresenter().init(getActivity(), call);
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- outState.putBoolean(IS_DIALPAD_SHOWING_KEY, mIsDialpadShowing);
- super.onSaveInstanceState(outState);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- Trace.beginSection(TAG + " onCreate");
- mTranslationOffset =
- getResources().getDimensionPixelSize(R.dimen.call_card_anim_translate_y_offset);
- final View view = inflater.inflate(R.layout.call_card_fragment, container, false);
- Trace.endSection();
- return view;
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- mPulseAnimation =
- AnimationUtils.loadAnimation(view.getContext(), R.anim.call_status_pulse);
-
- mPhoneNumber = (TextView) view.findViewById(R.id.phoneNumber);
- mPrimaryName = (TextView) view.findViewById(R.id.name);
- mNumberLabel = (TextView) view.findViewById(R.id.label);
- mSecondaryCallInfo = view.findViewById(R.id.secondary_call_info);
- mSecondaryCallProviderInfo = view.findViewById(R.id.secondary_call_provider_info);
- mCallCardContent = view.findViewById(R.id.call_card_content);
- mPhotoLarge = (ImageView) view.findViewById(R.id.photoLarge);
- mPhotoLarge.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- getPresenter().onContactPhotoClick();
- }
- });
-
- mContactContext = view.findViewById(R.id.contact_context);
- mContactContextTitle = (TextView) view.findViewById(R.id.contactContextTitle);
- mContactContextListView = (ListView) view.findViewById(R.id.contactContextInfo);
- // This layout stores all the list header layouts so they can be easily removed.
- mContactContextListHeaders = new LinearLayout(getView().getContext());
- mContactContextListView.addHeaderView(mContactContextListHeaders);
-
- mCallStateIcon = (ImageView) view.findViewById(R.id.callStateIcon);
- mCallStateVideoCallIcon = (ImageView) view.findViewById(R.id.videoCallIcon);
- mWorkProfileIcon = (ImageView) view.findViewById(R.id.workProfileIcon);
- mCallStateLabel = (TextView) view.findViewById(R.id.callStateLabel);
- mHdAudioIcon = (ImageView) view.findViewById(R.id.hdAudioIcon);
- mForwardIcon = (ImageView) view.findViewById(R.id.forwardIcon);
- mSpamIcon = (ImageView) view.findViewById(R.id.spamIcon);
- mCallNumberAndLabel = view.findViewById(R.id.labelAndNumber);
- mCallTypeLabel = (TextView) view.findViewById(R.id.callTypeLabel);
- mElapsedTime = (TextView) view.findViewById(R.id.elapsedTime);
- mPrimaryCallCardContainer = view.findViewById(R.id.primary_call_info_container);
- mPrimaryCallInfo = (ViewGroup) view.findViewById(R.id.primary_call_banner);
- mCallButtonsContainer = view.findViewById(R.id.callButtonFragment);
- mPhotoSmall = (ImageView) view.findViewById(R.id.photoSmall);
- mPhotoSmall.setVisibility(View.GONE);
- mInCallMessageLabel = (TextView) view.findViewById(R.id.connectionServiceMessage);
- mProgressSpinner = view.findViewById(R.id.progressSpinner);
-
- mFloatingActionButtonContainer = view.findViewById(
- R.id.floating_end_call_action_button_container);
- mFloatingActionButton = (ImageButton) view.findViewById(
- R.id.floating_end_call_action_button);
- mFloatingActionButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- getPresenter().endCallClicked();
- }
- });
- mFloatingActionButtonController = new FloatingActionButtonController(getActivity(),
- mFloatingActionButtonContainer, mFloatingActionButton);
-
- mSecondaryCallInfo.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- getPresenter().secondaryInfoClicked();
- updateFabPositionForSecondaryCallInfo();
- }
- });
-
- mCallStateButton = view.findViewById(R.id.callStateButton);
- mCallStateButton.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- getPresenter().onCallStateButtonTouched();
- return false;
- }
- });
-
- mManageConferenceCallButton = view.findViewById(R.id.manage_conference_call_button);
- mManageConferenceCallButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- InCallActivity activity = (InCallActivity) getActivity();
- activity.showConferenceFragment(true);
- }
- });
-
- mPrimaryName.setElegantTextHeight(false);
- mCallStateLabel.setElegantTextHeight(false);
- mCallSubject = (TextView) view.findViewById(R.id.callSubject);
- }
-
- @Override
- public void setVisible(boolean on) {
- if (on) {
- getView().setVisibility(View.VISIBLE);
- } else {
- getView().setVisibility(View.INVISIBLE);
- }
- }
-
- /**
- * Hides or shows the progress spinner.
- *
- * @param visible {@code True} if the progress spinner should be visible.
- */
- @Override
- public void setProgressSpinnerVisible(boolean visible) {
- mProgressSpinner.setVisibility(visible ? View.VISIBLE : View.GONE);
- }
-
- @Override
- public void setContactContextTitle(View headerView) {
- mContactContextListHeaders.removeAllViews();
- mContactContextListHeaders.addView(headerView);
- }
-
- @Override
- public void setContactContextContent(ListAdapter listAdapter) {
- mContactContextListView.setAdapter(listAdapter);
- }
-
- @Override
- public void showContactContext(boolean show) {
- showImageView(mPhotoLarge, !show);
- showImageView(mPhotoSmall, show);
- mPrimaryCallCardContainer.setElevation(
- show ? 0 : getResources().getDimension(R.dimen.primary_call_elevation));
- mContactContext.setVisibility(show ? View.VISIBLE : View.GONE);
- }
-
- /**
- * Sets the visibility of the primary call card.
- * Ensures that when the primary call card is hidden, the video surface slides over to fill the
- * entire screen.
- *
- * @param visible {@code True} if the primary call card should be visible.
- */
- @Override
- public void setCallCardVisible(final boolean visible) {
- Log.v(this, "setCallCardVisible : isVisible = " + visible);
- // When animating the hide/show of the views in a landscape layout, we need to take into
- // account whether we are in a left-to-right locale or a right-to-left locale and adjust
- // the animations accordingly.
- final boolean isLayoutRtl = InCallPresenter.isRtl();
-
- // Retrieve here since at fragment creation time the incoming video view is not inflated.
- final View videoView = getView().findViewById(R.id.incomingVideo);
- if (videoView == null) {
- return;
- }
-
- // Determine how much space there is below or to the side of the call card.
- final float spaceBesideCallCard = getSpaceBesideCallCard();
-
- // We need to translate the video surface, but we need to know its position after the layout
- // has occurred so use a {@code ViewTreeObserver}.
- final ViewTreeObserver observer = getView().getViewTreeObserver();
- observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- // We don't want to continue getting called.
- getView().getViewTreeObserver().removeOnPreDrawListener(this);
-
- float videoViewTranslation = 0f;
-
- // Translate the call card to its pre-animation state.
- if (!mIsLandscape) {
- mPrimaryCallCardContainer.setTranslationY(visible ?
- -mPrimaryCallCardContainer.getHeight() : 0);
-
- ViewGroup.LayoutParams p = videoView.getLayoutParams();
- videoViewTranslation = p.height / 2 - spaceBesideCallCard / 2;
- }
-
- // Perform animation of video view.
- ViewPropertyAnimator videoViewAnimator = videoView.animate()
- .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
- .setDuration(mVideoAnimationDuration);
- if (mIsLandscape) {
- videoViewAnimator
- .translationX(visible ? videoViewTranslation : 0);
- } else {
- videoViewAnimator
- .translationY(visible ? videoViewTranslation : 0);
- }
- videoViewAnimator.start();
-
- // Animate the call card sliding.
- ViewPropertyAnimator callCardAnimator = mPrimaryCallCardContainer.animate()
- .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
- .setDuration(mVideoAnimationDuration)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- if (!visible) {
- mPrimaryCallCardContainer.setVisibility(View.GONE);
- }
- }
-
- @Override
- public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
- if (visible) {
- mPrimaryCallCardContainer.setVisibility(View.VISIBLE);
- }
- }
- });
-
- if (mIsLandscape) {
- float translationX = mPrimaryCallCardContainer.getWidth();
- translationX *= isLayoutRtl ? 1 : -1;
- callCardAnimator
- .translationX(visible ? 0 : translationX)
- .start();
- } else {
- callCardAnimator
- .translationY(visible ? 0 : -mPrimaryCallCardContainer.getHeight())
- .start();
- }
-
- return true;
- }
- });
- }
-
- /**
- * Determines the amount of space below the call card for portrait layouts), or beside the
- * call card for landscape layouts.
- *
- * @return The amount of space below or beside the call card.
- */
- public float getSpaceBesideCallCard() {
- if (mIsLandscape) {
- return getView().getWidth() - mPrimaryCallCardContainer.getWidth();
- } else {
- final int callCardHeight;
- // Retrieve the actual height of the call card, independent of whether or not the
- // outgoing call animation is in progress. The animation does not run in landscape mode
- // so this only needs to be done for portrait.
- if (mPrimaryCallCardContainer.getTag(R.id.view_tag_callcard_actual_height) != null) {
- callCardHeight = (int) mPrimaryCallCardContainer.getTag(
- R.id.view_tag_callcard_actual_height);
- } else {
- callCardHeight = mPrimaryCallCardContainer.getHeight();
- }
- return getView().getHeight() - callCardHeight;
- }
- }
-
- @Override
- public void setPrimaryName(String name, boolean nameIsNumber) {
- if (TextUtils.isEmpty(name)) {
- mPrimaryName.setText(null);
- } else {
- mPrimaryName.setText(nameIsNumber
- ? PhoneNumberUtilsCompat.createTtsSpannable(name)
- : name);
-
- // Set direction of the name field
- int nameDirection = View.TEXT_DIRECTION_INHERIT;
- if (nameIsNumber) {
- nameDirection = View.TEXT_DIRECTION_LTR;
- }
- mPrimaryName.setTextDirection(nameDirection);
- }
- }
-
- /**
- * Sets the primary image for the contact photo.
- *
- * @param image The drawable to set.
- * @param isVisible Whether the contact photo should be visible after being set.
- */
- @Override
- public void setPrimaryImage(Drawable image, boolean isVisible) {
- if (image != null) {
- setDrawableToImageViews(image);
- showImageView(mPhotoLarge, isVisible);
- }
- }
-
- @Override
- public void setPrimaryPhoneNumber(String number) {
- // Set the number
- if (TextUtils.isEmpty(number)) {
- mPhoneNumber.setText(null);
- mPhoneNumber.setVisibility(View.GONE);
- } else {
- mPhoneNumber.setText(PhoneNumberUtilsCompat.createTtsSpannable(number));
- mPhoneNumber.setVisibility(View.VISIBLE);
- mPhoneNumber.setTextDirection(View.TEXT_DIRECTION_LTR);
- }
- }
-
- @Override
- public void setPrimaryLabel(String label) {
- if (!TextUtils.isEmpty(label)) {
- mNumberLabel.setText(label);
- mNumberLabel.setVisibility(View.VISIBLE);
- } else {
- mNumberLabel.setVisibility(View.GONE);
- }
-
- }
-
- /**
- * Sets the primary caller information.
- *
- * @param number The caller phone number.
- * @param name The caller name.
- * @param nameIsNumber {@code true} if the name should be shown in place of the phone number.
- * @param label The label.
- * @param photo The contact photo drawable.
- * @param isSipCall {@code true} if this is a SIP call.
- * @param isContactPhotoShown {@code true} if the contact photo should be shown (it will be
- * updated even if it is not shown).
- * @param isWorkCall Whether the call is placed through a work phone account or caller is a work
- contact.
- */
- @Override
- public void setPrimary(String number, String name, boolean nameIsNumber, String label,
- Drawable photo, boolean isSipCall, boolean isContactPhotoShown, boolean isWorkCall) {
- Log.d(this, "Setting primary call");
- // set the name field.
- setPrimaryName(name, nameIsNumber);
-
- if (TextUtils.isEmpty(number) && TextUtils.isEmpty(label)) {
- mCallNumberAndLabel.setVisibility(View.GONE);
- mElapsedTime.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
- } else {
- mCallNumberAndLabel.setVisibility(View.VISIBLE);
- mElapsedTime.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_END);
- }
-
- setPrimaryPhoneNumber(number);
-
- // Set the label (Mobile, Work, etc)
- setPrimaryLabel(label);
-
- showInternetCallLabel(isSipCall);
-
- setDrawableToImageViews(photo);
- showImageView(mPhotoLarge, isContactPhotoShown);
- showImageView(mWorkProfileIcon, isWorkCall);
- }
-
- @Override
- public void setSecondary(boolean show, String name, boolean nameIsNumber, String label,
- String providerLabel, boolean isConference, boolean isVideoCall, boolean isFullscreen) {
-
- if (show) {
- mHasSecondaryCallInfo = true;
- boolean hasProvider = !TextUtils.isEmpty(providerLabel);
- initializeSecondaryCallInfo(hasProvider);
-
- // Do not show the secondary caller info in fullscreen mode, but ensure it is populated
- // in case fullscreen mode is exited in the future.
- setSecondaryInfoVisible(!isFullscreen);
-
- mSecondaryCallConferenceCallIcon.setVisibility(isConference ? View.VISIBLE : View.GONE);
- mSecondaryCallVideoCallIcon.setVisibility(isVideoCall ? View.VISIBLE : View.GONE);
-
- mSecondaryCallName.setText(nameIsNumber
- ? PhoneNumberUtilsCompat.createTtsSpannable(name)
- : name);
- if (hasProvider) {
- mSecondaryCallProviderLabel.setText(providerLabel);
- }
-
- int nameDirection = View.TEXT_DIRECTION_INHERIT;
- if (nameIsNumber) {
- nameDirection = View.TEXT_DIRECTION_LTR;
- }
- mSecondaryCallName.setTextDirection(nameDirection);
- } else {
- mHasSecondaryCallInfo = false;
- setSecondaryInfoVisible(false);
- }
- }
-
- /**
- * Sets the visibility of the secondary caller info box. Note, if the {@code visible} parameter
- * is passed in {@code true}, and there is no secondary caller info populated (as determined by
- * {@code mHasSecondaryCallInfo}, the secondary caller info box will not be shown.
- *
- * @param visible {@code true} if the secondary caller info should be shown, {@code false}
- * otherwise.
- */
- @Override
- public void setSecondaryInfoVisible(final boolean visible) {
- boolean wasVisible = mSecondaryCallInfo.isShown();
- final boolean isVisible = visible && mHasSecondaryCallInfo;
- Log.v(this, "setSecondaryInfoVisible: wasVisible = " + wasVisible + " isVisible = "
- + isVisible);
-
- // If visibility didn't change, nothing to do.
- if (wasVisible == isVisible) {
- return;
- }
-
- // If we are showing the secondary info, we need to show it before animating so that its
- // height will be determined on layout.
- if (isVisible) {
- mSecondaryCallInfo.setVisibility(View.VISIBLE);
- } else {
- mSecondaryCallInfo.setVisibility(View.GONE);
- }
-
- updateFabPositionForSecondaryCallInfo();
- // We need to translate the secondary caller info, but we need to know its position after
- // the layout has occurred so use a {@code ViewTreeObserver}.
- final ViewTreeObserver observer = getView().getViewTreeObserver();
-
- observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- // We don't want to continue getting called.
- getView().getViewTreeObserver().removeOnPreDrawListener(this);
-
- // Get the height of the secondary call info now, and then re-hide the view prior
- // to doing the actual animation.
- int secondaryHeight = mSecondaryCallInfo.getHeight();
- if (isVisible) {
- mSecondaryCallInfo.setVisibility(View.GONE);
- } else {
- mSecondaryCallInfo.setVisibility(View.VISIBLE);
- }
- Log.v(this, "setSecondaryInfoVisible: secondaryHeight = " + secondaryHeight);
-
- // Set the position of the secondary call info card to its starting location.
- mSecondaryCallInfo.setTranslationY(visible ? secondaryHeight : 0);
-
- // Animate the secondary card info slide up/down as it appears and disappears.
- ViewPropertyAnimator secondaryInfoAnimator = mSecondaryCallInfo.animate()
- .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
- .setDuration(mVideoAnimationDuration)
- .translationY(isVisible ? 0 : secondaryHeight)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!isVisible) {
- mSecondaryCallInfo.setVisibility(View.GONE);
- }
- }
-
- @Override
- public void onAnimationStart(Animator animation) {
- if (isVisible) {
- mSecondaryCallInfo.setVisibility(View.VISIBLE);
- }
- }
- });
- secondaryInfoAnimator.start();
-
- // Notify listeners of a change in the visibility of the secondary info. This is
- // important when in a video call so that the video call presenter can shift the
- // video preview up or down to accommodate the secondary caller info.
- InCallPresenter.getInstance().notifySecondaryCallerInfoVisibilityChanged(visible,
- secondaryHeight);
-
- return true;
- }
- });
- }
-
- @Override
- public void setCallState(
- int state,
- int videoState,
- int sessionModificationState,
- DisconnectCause disconnectCause,
- String connectionLabel,
- Drawable callStateIcon,
- String gatewayNumber,
- boolean isWifi,
- boolean isConference,
- boolean isWorkCall) {
- boolean isGatewayCall = !TextUtils.isEmpty(gatewayNumber);
- CallStateLabel callStateLabel = getCallStateLabelFromState(state, videoState,
- sessionModificationState, disconnectCause, connectionLabel, isGatewayCall, isWifi,
- isConference, isWorkCall);
-
- Log.v(this, "setCallState " + callStateLabel.getCallStateLabel());
- Log.v(this, "AutoDismiss " + callStateLabel.isAutoDismissing());
- Log.v(this, "DisconnectCause " + disconnectCause.toString());
- Log.v(this, "gateway " + connectionLabel + gatewayNumber);
-
- // Check for video state change and update the visibility of the contact photo. The contact
- // photo is hidden when the incoming video surface is shown.
- // The contact photo visibility can also change in setPrimary().
- boolean showContactPhoto = !VideoCallPresenter.showIncomingVideo(videoState, state);
- mPhotoLarge.setVisibility(showContactPhoto ? View.VISIBLE : View.GONE);
-
- // Check if the call subject is showing -- if it is, we want to bypass showing the call
- // state.
- boolean isSubjectShowing = mCallSubject.getVisibility() == View.VISIBLE;
-
- if (TextUtils.equals(callStateLabel.getCallStateLabel(), mCallStateLabel.getText()) &&
- !isSubjectShowing) {
- // Nothing to do if the labels are the same
- if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED) {
- mCallStateLabel.clearAnimation();
- mCallStateIcon.clearAnimation();
- }
- return;
- }
-
- if (isSubjectShowing) {
- changeCallStateLabel(null);
- callStateIcon = null;
- } else {
- // Update the call state label and icon.
- setCallStateLabel(callStateLabel);
- }
-
- if (!TextUtils.isEmpty(callStateLabel.getCallStateLabel())) {
- if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED) {
- mCallStateLabel.clearAnimation();
- } else {
- mCallStateLabel.startAnimation(mPulseAnimation);
- }
- } else {
- mCallStateLabel.clearAnimation();
- }
-
- if (callStateIcon != null) {
- mCallStateIcon.setVisibility(View.VISIBLE);
- // Invoke setAlpha(float) instead of setAlpha(int) to set the view's alpha. This is
- // needed because the pulse animation operates on the view alpha.
- mCallStateIcon.setAlpha(1.0f);
- mCallStateIcon.setImageDrawable(callStateIcon);
-
- if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED
- || TextUtils.isEmpty(callStateLabel.getCallStateLabel())) {
- mCallStateIcon.clearAnimation();
- } else {
- mCallStateIcon.startAnimation(mPulseAnimation);
- }
-
- if (callStateIcon instanceof AnimationDrawable) {
- ((AnimationDrawable) callStateIcon).start();
- }
- } else {
- mCallStateIcon.clearAnimation();
-
- // Invoke setAlpha(float) instead of setAlpha(int) to set the view's alpha. This is
- // needed because the pulse animation operates on the view alpha.
- mCallStateIcon.setAlpha(0.0f);
- mCallStateIcon.setVisibility(View.GONE);
- }
-
- if (VideoUtils.isVideoCall(videoState)
- || (state == Call.State.ACTIVE && sessionModificationState
- == Call.SessionModificationState.WAITING_FOR_RESPONSE)) {
- mCallStateVideoCallIcon.setVisibility(View.VISIBLE);
- } else {
- mCallStateVideoCallIcon.setVisibility(View.GONE);
- }
- }
-
- private void setCallStateLabel(CallStateLabel callStateLabel) {
- Log.v(this, "setCallStateLabel : label = " + callStateLabel.getCallStateLabel());
-
- if (callStateLabel.isAutoDismissing()) {
- mCallStateLabelResetPending = true;
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- Log.v(this, "restoringCallStateLabel : label = " +
- mPostResetCallStateLabel);
- changeCallStateLabel(mPostResetCallStateLabel);
- mCallStateLabelResetPending = false;
- }
- }, CALL_STATE_LABEL_RESET_DELAY_MS);
-
- changeCallStateLabel(callStateLabel.getCallStateLabel());
- } else {
- // Keep track of the current call state label; used when resetting auto dismissing
- // call state labels.
- mPostResetCallStateLabel = callStateLabel.getCallStateLabel();
-
- if (!mCallStateLabelResetPending) {
- changeCallStateLabel(callStateLabel.getCallStateLabel());
- }
- }
- }
-
- private void changeCallStateLabel(CharSequence callStateLabel) {
- Log.v(this, "changeCallStateLabel : label = " + callStateLabel);
- if (!TextUtils.isEmpty(callStateLabel)) {
- mCallStateLabel.setText(callStateLabel);
- mCallStateLabel.setAlpha(1);
- mCallStateLabel.setVisibility(View.VISIBLE);
- } else {
- Animation callStateLabelAnimation = mCallStateLabel.getAnimation();
- if (callStateLabelAnimation != null) {
- callStateLabelAnimation.cancel();
- }
- mCallStateLabel.setText(null);
- mCallStateLabel.setAlpha(0);
- mCallStateLabel.setVisibility(View.GONE);
- }
- }
-
- @Override
- public void setCallbackNumber(String callbackNumber, boolean isEmergencyCall) {
- if (mInCallMessageLabel == null) {
- return;
- }
-
- if (TextUtils.isEmpty(callbackNumber)) {
- mInCallMessageLabel.setVisibility(View.GONE);
- return;
- }
-
- // TODO: The new Locale-specific methods don't seem to be working. Revisit this.
- callbackNumber = PhoneNumberUtils.formatNumber(callbackNumber);
-
- int stringResourceId = isEmergencyCall ? R.string.card_title_callback_number_emergency
- : R.string.card_title_callback_number;
-
- String text = getString(stringResourceId, callbackNumber);
- mInCallMessageLabel.setText(text);
-
- mInCallMessageLabel.setVisibility(View.VISIBLE);
- }
-
- /**
- * Sets and shows the call subject if it is not empty. Hides the call subject otherwise.
- *
- * @param callSubject The call subject.
- */
- @Override
- public void setCallSubject(String callSubject) {
- boolean showSubject = !TextUtils.isEmpty(callSubject);
-
- mCallSubject.setVisibility(showSubject ? View.VISIBLE : View.GONE);
- if (showSubject) {
- mCallSubject.setText(callSubject);
- } else {
- mCallSubject.setText(null);
- }
- }
-
- public boolean isAnimating() {
- return mIsAnimating;
- }
-
- private void showInternetCallLabel(boolean show) {
- if (show) {
- final String label = getView().getContext().getString(
- R.string.incall_call_type_label_sip);
- mCallTypeLabel.setVisibility(View.VISIBLE);
- mCallTypeLabel.setText(label);
- } else {
- mCallTypeLabel.setVisibility(View.GONE);
- }
- }
-
- @Override
- public void setPrimaryCallElapsedTime(boolean show, long duration) {
- if (show) {
- if (mElapsedTime.getVisibility() != View.VISIBLE) {
- AnimUtils.fadeIn(mElapsedTime, AnimUtils.DEFAULT_DURATION);
- }
- String callTimeElapsed = DateUtils.formatElapsedTime(duration / 1000);
- mElapsedTime.setText(callTimeElapsed);
-
- String durationDescription =
- InCallDateUtils.formatDuration(duration);
- mElapsedTime.setContentDescription(
- !TextUtils.isEmpty(durationDescription) ? durationDescription : null);
- } else {
- // hide() animation has no effect if it is already hidden.
- AnimUtils.fadeOut(mElapsedTime, AnimUtils.DEFAULT_DURATION);
- }
- }
-
- /**
- * Set all the ImageViews to the same photo. Currently there are 2 photo views: the large one
- * (which fills about the bottom half of the screen) and the small one, which displays as a
- * circle next to the primary contact info. This method does not handle whether the ImageView
- * is shown or not.
- *
- * @param photo The photo to set for the image views.
- */
- private void setDrawableToImageViews(Drawable photo) {
- if (photo == null) {
- photo = ContactInfoCache.getInstance(getView().getContext())
- .getDefaultContactPhotoDrawable();
- }
-
- if (mPrimaryPhotoDrawable == photo){
- return;
- }
- mPrimaryPhotoDrawable = photo;
-
- mPhotoLarge.setImageDrawable(photo);
-
- // Modify the drawable to be round for the smaller ImageView.
- Bitmap bitmap = drawableToBitmap(photo);
- if (bitmap != null) {
- final RoundedBitmapDrawable drawable =
- RoundedBitmapDrawableFactory.create(getResources(), bitmap);
- drawable.setAntiAlias(true);
- drawable.setCornerRadius(bitmap.getHeight() / 2);
- photo = drawable;
- }
- mPhotoSmall.setImageDrawable(photo);
- }
-
- /**
- * Helper method for image view to handle animations.
- *
- * @param view The image view to show or hide.
- * @param isVisible {@code true} if we want to show the image, {@code false} to hide it.
- */
- private void showImageView(ImageView view, boolean isVisible) {
- if (view.getDrawable() == null) {
- if (isVisible) {
- AnimUtils.fadeIn(mElapsedTime, AnimUtils.DEFAULT_DURATION);
- }
- } else {
- // Cross fading is buggy and not noticeable due to the multiple calls to this method
- // that switch drawables in the middle of the cross-fade animations. Just show the
- // photo directly instead.
- view.setVisibility(isVisible ? View.VISIBLE : View.GONE);
- }
- }
-
- /**
- * Converts a drawable into a bitmap.
- *
- * @param drawable the drawable to be converted.
- */
- public static Bitmap drawableToBitmap(Drawable drawable) {
- Bitmap bitmap;
- if (drawable instanceof BitmapDrawable) {
- bitmap = ((BitmapDrawable) drawable).getBitmap();
- } else {
- if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
- // Needed for drawables that are just a colour.
- bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
- } else {
- bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
- drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
- }
-
- Log.i(TAG, "Created bitmap with width " + bitmap.getWidth() + ", height "
- + bitmap.getHeight());
-
- Canvas canvas = new Canvas(bitmap);
- drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- drawable.draw(canvas);
- }
- return bitmap;
- }
-
- /**
- * Gets the call state label based on the state of the call or cause of disconnect.
- *
- * Additional labels are applied as follows:
- * 1. All outgoing calls with display "Calling via [Provider]".
- * 2. Ongoing calls will display the name of the provider.
- * 3. Incoming calls will only display "Incoming via..." for accounts.
- * 4. Video calls, and session modification states (eg. requesting video).
- * 5. Incoming and active Wi-Fi calls will show label provided by hint.
- *
- * TODO: Move this to the CallCardPresenter.
- */
- private CallStateLabel getCallStateLabelFromState(int state, int videoState,
- int sessionModificationState, DisconnectCause disconnectCause, String label,
- boolean isGatewayCall, boolean isWifi, boolean isConference, boolean isWorkCall) {
- final Context context = getView().getContext();
- CharSequence callStateLabel = null; // Label to display as part of the call banner
-
- boolean hasSuggestedLabel = label != null;
- boolean isAccount = hasSuggestedLabel && !isGatewayCall;
- boolean isAutoDismissing = false;
-
- switch (state) {
- case Call.State.IDLE:
- // "Call state" is meaningless in this state.
- break;
- case Call.State.ACTIVE:
- // We normally don't show a "call state label" at all in this state
- // (but we can use the call state label to display the provider name).
- if ((isAccount || isWifi || isConference) && hasSuggestedLabel) {
- callStateLabel = label;
- } else if (sessionModificationState
- == Call.SessionModificationState.REQUEST_REJECTED) {
- callStateLabel = context.getString(R.string.card_title_video_call_rejected);
- isAutoDismissing = true;
- } else if (sessionModificationState
- == Call.SessionModificationState.REQUEST_FAILED) {
- callStateLabel = context.getString(R.string.card_title_video_call_error);
- isAutoDismissing = true;
- } else if (sessionModificationState
- == Call.SessionModificationState.WAITING_FOR_RESPONSE) {
- callStateLabel = context.getString(R.string.card_title_video_call_requesting);
- } else if (sessionModificationState
- == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
- callStateLabel = context.getString(R.string.card_title_video_call_requesting);
- } else if (VideoUtils.isVideoCall(videoState)) {
- callStateLabel = context.getString(R.string.card_title_video_call);
- }
- break;
- case Call.State.ONHOLD:
- callStateLabel = context.getString(R.string.card_title_on_hold);
- break;
- case Call.State.CONNECTING:
- case Call.State.DIALING:
- if (hasSuggestedLabel && !isWifi) {
- callStateLabel = context.getString(R.string.calling_via_template, label);
- } else {
- callStateLabel = context.getString(R.string.card_title_dialing);
- }
- break;
- case Call.State.REDIALING:
- callStateLabel = context.getString(R.string.card_title_redialing);
- break;
- case Call.State.INCOMING:
- case Call.State.CALL_WAITING:
- if (isWifi && hasSuggestedLabel) {
- callStateLabel = label;
- } else if (isAccount) {
- callStateLabel = context.getString(R.string.incoming_via_template, label);
- } else if (VideoUtils.isVideoCall(videoState)) {
- callStateLabel = context.getString(R.string.notification_incoming_video_call);
- } else {
- callStateLabel =
- context.getString(isWorkCall ? R.string.card_title_incoming_work_call
- : R.string.card_title_incoming_call);
- }
- break;
- case Call.State.DISCONNECTING:
- // While in the DISCONNECTING state we display a "Hanging up"
- // message in order to make the UI feel more responsive. (In
- // GSM it's normal to see a delay of a couple of seconds while
- // negotiating the disconnect with the network, so the "Hanging
- // up" state at least lets the user know that we're doing
- // something. This state is currently not used with CDMA.)
- callStateLabel = context.getString(R.string.card_title_hanging_up);
- break;
- case Call.State.DISCONNECTED:
- callStateLabel = disconnectCause.getLabel();
- if (TextUtils.isEmpty(callStateLabel)) {
- callStateLabel = context.getString(R.string.card_title_call_ended);
- }
- break;
- case Call.State.CONFERENCED:
- callStateLabel = context.getString(R.string.card_title_conf_call);
- break;
- default:
- Log.wtf(this, "updateCallStateWidgets: unexpected call: " + state);
- }
- return new CallStateLabel(callStateLabel, isAutoDismissing);
- }
-
- private void initializeSecondaryCallInfo(boolean hasProvider) {
- // mSecondaryCallName is initialized here (vs. onViewCreated) because it is inaccessible
- // until mSecondaryCallInfo is inflated in the call above.
- if (mSecondaryCallName == null) {
- mSecondaryCallName = (TextView) getView().findViewById(R.id.secondaryCallName);
- mSecondaryCallConferenceCallIcon =
- getView().findViewById(R.id.secondaryCallConferenceCallIcon);
- mSecondaryCallVideoCallIcon =
- getView().findViewById(R.id.secondaryCallVideoCallIcon);
- }
-
- if (mSecondaryCallProviderLabel == null && hasProvider) {
- mSecondaryCallProviderInfo.setVisibility(View.VISIBLE);
- mSecondaryCallProviderLabel = (TextView) getView()
- .findViewById(R.id.secondaryCallProviderLabel);
- }
- }
-
- public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- if (event.getEventType() == AccessibilityEvent.TYPE_ANNOUNCEMENT) {
- // Indicate this call is in active if no label is provided. The label is empty when
- // the call is in active, not in other status such as onhold or dialing etc.
- if (!mCallStateLabel.isShown() || TextUtils.isEmpty(mCallStateLabel.getText())) {
- event.getText().add(
- TextUtils.expandTemplate(
- getResources().getText(R.string.accessibility_call_is_active),
- mPrimaryName.getText()));
- } else {
- dispatchPopulateAccessibilityEvent(event, mCallStateLabel);
- dispatchPopulateAccessibilityEvent(event, mPrimaryName);
- dispatchPopulateAccessibilityEvent(event, mCallTypeLabel);
- dispatchPopulateAccessibilityEvent(event, mPhoneNumber);
- }
- return;
- }
- dispatchPopulateAccessibilityEvent(event, mCallStateLabel);
- dispatchPopulateAccessibilityEvent(event, mPrimaryName);
- dispatchPopulateAccessibilityEvent(event, mPhoneNumber);
- dispatchPopulateAccessibilityEvent(event, mCallTypeLabel);
- dispatchPopulateAccessibilityEvent(event, mSecondaryCallName);
- dispatchPopulateAccessibilityEvent(event, mSecondaryCallProviderLabel);
-
- return;
- }
-
- @Override
- public void sendAccessibilityAnnouncement() {
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (getView() != null && getView().getParent() != null &&
- isAccessibilityEnabled(getContext())) {
- AccessibilityEvent event = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_ANNOUNCEMENT);
- dispatchPopulateAccessibilityEvent(event);
- getView().getParent().requestSendAccessibilityEvent(getView(), event);
- }
- }
-
- private boolean isAccessibilityEnabled(Context context) {
- AccessibilityManager accessibilityManager =
- (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
- return accessibilityManager != null && accessibilityManager.isEnabled();
-
- }
- }, ACCESSIBILITY_ANNOUNCEMENT_DELAY_MS);
- }
-
- @Override
- public void setEndCallButtonEnabled(boolean enabled, boolean animate) {
- if (enabled != mFloatingActionButton.isEnabled()) {
- if (animate) {
- if (enabled) {
- mFloatingActionButtonController.scaleIn(AnimUtils.NO_DELAY);
- } else {
- mFloatingActionButtonController.scaleOut();
- }
- } else {
- if (enabled) {
- mFloatingActionButtonContainer.setScaleX(1);
- mFloatingActionButtonContainer.setScaleY(1);
- mFloatingActionButtonContainer.setVisibility(View.VISIBLE);
- } else {
- mFloatingActionButtonContainer.setVisibility(View.GONE);
- }
- }
- mFloatingActionButton.setEnabled(enabled);
- updateFabPosition();
- }
- }
-
- /**
- * Changes the visibility of the HD audio icon.
- *
- * @param visible {@code true} if the UI should show the HD audio icon.
- */
- @Override
- public void showHdAudioIndicator(boolean visible) {
- mHdAudioIcon.setVisibility(visible ? View.VISIBLE : View.GONE);
- }
-
- /**
- * Changes the visibility of the forward icon.
- *
- * @param visible {@code true} if the UI should show the forward icon.
- */
- @Override
- public void showForwardIndicator(boolean visible) {
- mForwardIcon.setVisibility(visible ? View.VISIBLE : View.GONE);
- }
-
- /**
- * Changes the visibility of the spam icon.
- *
- * @param visible {@code true} if the UI should show the spam icon.
- */
- @Override
- public void showSpamIndicator(boolean visible) {
- if (visible) {
- mSpamIcon.setVisibility(View.VISIBLE);
- mNumberLabel.setText(R.string.label_spam_caller);
- mPhoneNumber.setVisibility(View.GONE);
- }
- }
-
- /**
- * Changes the visibility of the "manage conference call" button.
- *
- * @param visible Whether to set the button to be visible or not.
- */
- @Override
- public void showManageConferenceCallButton(boolean visible) {
- mManageConferenceCallButton.setVisibility(visible ? View.VISIBLE : View.GONE);
- }
-
- /**
- * Determines the current visibility of the manage conference button.
- *
- * @return {@code true} if the button is visible.
- */
- @Override
- public boolean isManageConferenceVisible() {
- return mManageConferenceCallButton.getVisibility() == View.VISIBLE;
- }
-
- /**
- * Determines the current visibility of the call subject.
- *
- * @return {@code true} if the subject is visible.
- */
- @Override
- public boolean isCallSubjectVisible() {
- return mCallSubject.getVisibility() == View.VISIBLE;
- }
-
- /**
- * Get the overall InCallUI background colors and apply to call card.
- */
- public void updateColors() {
- MaterialPalette themeColors = InCallPresenter.getInstance().getThemeColors();
-
- if (mCurrentThemeColors != null && mCurrentThemeColors.equals(themeColors)) {
- return;
- }
-
- if (getResources().getBoolean(R.bool.is_layout_landscape)) {
- final GradientDrawable drawable =
- (GradientDrawable) mPrimaryCallCardContainer.getBackground();
- drawable.setColor(themeColors.mPrimaryColor);
- } else {
- mPrimaryCallCardContainer.setBackgroundColor(themeColors.mPrimaryColor);
- }
- mCallButtonsContainer.setBackgroundColor(themeColors.mPrimaryColor);
- mCallSubject.setTextColor(themeColors.mPrimaryColor);
- mContactContext.setBackgroundColor(themeColors.mPrimaryColor);
- //TODO: set color of message text in call context "recent messages" to be the theme color.
-
- mCurrentThemeColors = themeColors;
- }
-
- private void dispatchPopulateAccessibilityEvent(AccessibilityEvent event, View view) {
- if (view == null) return;
- final List<CharSequence> eventText = event.getText();
- int size = eventText.size();
- view.dispatchPopulateAccessibilityEvent(event);
- // if no text added write null to keep relative position
- if (size == eventText.size()) {
- eventText.add(null);
- }
- }
-
- @Override
- public void animateForNewOutgoingCall() {
- final ViewGroup parent = (ViewGroup) mPrimaryCallCardContainer.getParent();
-
- final ViewTreeObserver observer = getView().getViewTreeObserver();
-
- mIsAnimating = true;
-
- observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- final ViewTreeObserver observer = getView().getViewTreeObserver();
- if (!observer.isAlive()) {
- return;
- }
- observer.removeOnGlobalLayoutListener(this);
-
- final LayoutIgnoringListener listener = new LayoutIgnoringListener();
- mPrimaryCallCardContainer.addOnLayoutChangeListener(listener);
-
- // Prepare the state of views before the slide animation
- final int originalHeight = mPrimaryCallCardContainer.getHeight();
- mPrimaryCallCardContainer.setTag(R.id.view_tag_callcard_actual_height,
- originalHeight);
- mPrimaryCallCardContainer.setBottom(parent.getHeight());
-
- // Set up FAB.
- mFloatingActionButtonContainer.setVisibility(View.GONE);
- mFloatingActionButtonController.setScreenWidth(parent.getWidth());
-
- mCallButtonsContainer.setAlpha(0);
- mCallStateLabel.setAlpha(0);
- mPrimaryName.setAlpha(0);
- mCallTypeLabel.setAlpha(0);
- mCallNumberAndLabel.setAlpha(0);
-
- assignTranslateAnimation(mCallStateLabel, 1);
- assignTranslateAnimation(mCallStateIcon, 1);
- assignTranslateAnimation(mPrimaryName, 2);
- assignTranslateAnimation(mCallNumberAndLabel, 3);
- assignTranslateAnimation(mCallTypeLabel, 4);
- assignTranslateAnimation(mCallButtonsContainer, 5);
-
- final Animator animator = getShrinkAnimator(parent.getHeight(), originalHeight);
-
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mPrimaryCallCardContainer.setTag(R.id.view_tag_callcard_actual_height,
- null);
- setViewStatePostAnimation(listener);
- mIsAnimating = false;
- InCallPresenter.getInstance().onShrinkAnimationComplete();
- if (animator != null) {
- animator.removeListener(this);
- }
- }
- });
- animator.start();
- }
- });
- }
-
- @Override
- public void showNoteSentToast() {
- Toast.makeText(getContext(), R.string.note_sent, Toast.LENGTH_LONG).show();
- }
-
- public void onDialpadVisibilityChange(boolean isShown) {
- mIsDialpadShowing = isShown;
- updateFabPosition();
- }
-
- private void updateFabPosition() {
- int offsetY = 0;
- if (!mIsDialpadShowing) {
- offsetY = mFloatingActionButtonVerticalOffset;
- if (mSecondaryCallInfo.isShown() && mHasLargePhoto) {
- offsetY -= mSecondaryCallInfo.getHeight();
- }
- }
-
- mFloatingActionButtonController.align(
- FloatingActionButtonController.ALIGN_MIDDLE,
- 0 /* offsetX */,
- offsetY,
- true);
-
- mFloatingActionButtonController.resize(
- mIsDialpadShowing ? mFabSmallDiameter : mFabNormalDiameter, true);
- }
-
- @Override
- public Context getContext() {
- return getActivity();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- // If the previous launch animation is still running, cancel it so that we don't get
- // stuck in an intermediate animation state.
- if (mAnimatorSet != null && mAnimatorSet.isRunning()) {
- mAnimatorSet.cancel();
- }
-
- mIsLandscape = getResources().getBoolean(R.bool.is_layout_landscape);
- mHasLargePhoto = getResources().getBoolean(R.bool.has_large_photo);
-
- final ViewGroup parent = ((ViewGroup) mPrimaryCallCardContainer.getParent());
- final ViewTreeObserver observer = parent.getViewTreeObserver();
- parent.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- ViewTreeObserver viewTreeObserver = observer;
- if (!viewTreeObserver.isAlive()) {
- viewTreeObserver = parent.getViewTreeObserver();
- }
- viewTreeObserver.removeOnGlobalLayoutListener(this);
- mFloatingActionButtonController.setScreenWidth(parent.getWidth());
- updateFabPosition();
- }
- });
-
- updateColors();
- }
-
- /**
- * Adds a global layout listener to update the FAB's positioning on the next layout. This allows
- * us to position the FAB after the secondary call info's height has been calculated.
- */
- private void updateFabPositionForSecondaryCallInfo() {
- mSecondaryCallInfo.getViewTreeObserver().addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- final ViewTreeObserver observer = mSecondaryCallInfo.getViewTreeObserver();
- if (!observer.isAlive()) {
- return;
- }
- observer.removeOnGlobalLayoutListener(this);
-
- onDialpadVisibilityChange(mIsDialpadShowing);
- }
- });
- }
-
- /**
- * Animator that performs the upwards shrinking animation of the blue call card scrim.
- * At the start of the animation, each child view is moved downwards by a pre-specified amount
- * and then translated upwards together with the scrim.
- */
- private Animator getShrinkAnimator(int startHeight, int endHeight) {
- final ObjectAnimator shrinkAnimator =
- ObjectAnimator.ofInt(mPrimaryCallCardContainer, "bottom", startHeight, endHeight);
- shrinkAnimator.setDuration(mShrinkAnimationDuration);
- shrinkAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mFloatingActionButton.setEnabled(true);
- }
- });
- shrinkAnimator.setInterpolator(AnimUtils.EASE_IN);
- return shrinkAnimator;
- }
-
- private void assignTranslateAnimation(View view, int offset) {
- view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- view.buildLayer();
- view.setTranslationY(mTranslationOffset * offset);
- view.animate().translationY(0).alpha(1).withLayer()
- .setDuration(mShrinkAnimationDuration).setInterpolator(AnimUtils.EASE_IN);
- }
-
- private void setViewStatePostAnimation(View view) {
- view.setTranslationY(0);
- view.setAlpha(1);
- }
-
- private void setViewStatePostAnimation(OnLayoutChangeListener layoutChangeListener) {
- setViewStatePostAnimation(mCallButtonsContainer);
- setViewStatePostAnimation(mCallStateLabel);
- setViewStatePostAnimation(mPrimaryName);
- setViewStatePostAnimation(mCallTypeLabel);
- setViewStatePostAnimation(mCallNumberAndLabel);
- setViewStatePostAnimation(mCallStateIcon);
-
- mPrimaryCallCardContainer.removeOnLayoutChangeListener(layoutChangeListener);
-
- mFloatingActionButtonController.scaleIn(AnimUtils.NO_DELAY);
- }
-
- private final class LayoutIgnoringListener implements View.OnLayoutChangeListener {
- @Override
- public void onLayoutChange(View v,
- int left,
- int top,
- int right,
- int bottom,
- int oldLeft,
- int oldTop,
- int oldRight,
- int oldBottom) {
- v.setLeft(oldLeft);
- v.setRight(oldRight);
- v.setTop(oldTop);
- v.setBottom(oldBottom);
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
deleted file mode 100644
index 1ad0c11f1..000000000
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ /dev/null
@@ -1,1181 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import com.google.common.base.Preconditions;
-
-import android.Manifest;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.telecom.Call.Details;
-import android.telecom.DisconnectCause;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telecom.StatusHints;
-import android.telecom.TelecomManager;
-import android.telecom.VideoProfile;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.ListAdapter;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.R;
-import com.android.incallui.Call.State;
-import com.android.incallui.ContactInfoCache.ContactCacheEntry;
-import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback;
-import com.android.incallui.InCallPresenter.InCallDetailsListener;
-import com.android.incallui.InCallPresenter.InCallEventListener;
-import com.android.incallui.InCallPresenter.InCallState;
-import com.android.incallui.InCallPresenter.InCallStateListener;
-import com.android.incallui.InCallPresenter.IncomingCallListener;
-import com.android.incalluibind.ObjectFactory;
-
-import java.lang.ref.WeakReference;
-
-import static com.android.contacts.common.compat.CallSdkCompat.Details.PROPERTY_ENTERPRISE_CALL;
-/**
- * Presenter for the Call Card Fragment.
- * <p>
- * This class listens for changes to InCallState and passes it along to the fragment.
- */
-public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
- implements InCallStateListener, IncomingCallListener, InCallDetailsListener,
- InCallEventListener, CallList.CallUpdateListener, DistanceHelper.Listener {
-
- public interface EmergencyCallListener {
- public void onCallUpdated(BaseFragment fragment, boolean isEmergency);
- }
-
- private static final String TAG = CallCardPresenter.class.getSimpleName();
- private static final long CALL_TIME_UPDATE_INTERVAL_MS = 1000;
-
- private final EmergencyCallListener mEmergencyCallListener =
- ObjectFactory.newEmergencyCallListener();
- private DistanceHelper mDistanceHelper;
-
- private Call mPrimary;
- private Call mSecondary;
- private ContactCacheEntry mPrimaryContactInfo;
- private ContactCacheEntry mSecondaryContactInfo;
- private CallTimer mCallTimer;
- private Context mContext;
- @Nullable private ContactsPreferences mContactsPreferences;
- private boolean mSpinnerShowing = false;
- private boolean mHasShownToast = false;
- private InCallContactInteractions mInCallContactInteractions;
- private boolean mIsFullscreen = false;
-
- public static class ContactLookupCallback implements ContactInfoCacheCallback {
- private final WeakReference<CallCardPresenter> mCallCardPresenter;
- private final boolean mIsPrimary;
-
- public ContactLookupCallback(CallCardPresenter callCardPresenter, boolean isPrimary) {
- mCallCardPresenter = new WeakReference<CallCardPresenter>(callCardPresenter);
- mIsPrimary = isPrimary;
- }
-
- @Override
- public void onContactInfoComplete(String callId, ContactCacheEntry entry) {
- CallCardPresenter presenter = mCallCardPresenter.get();
- if (presenter != null) {
- presenter.onContactInfoComplete(callId, entry, mIsPrimary);
- }
- }
-
- @Override
- public void onImageLoadComplete(String callId, ContactCacheEntry entry) {
- CallCardPresenter presenter = mCallCardPresenter.get();
- if (presenter != null) {
- presenter.onImageLoadComplete(callId, entry);
- }
- }
-
- @Override
- public void onContactInteractionsInfoComplete(String callId, ContactCacheEntry entry) {
- CallCardPresenter presenter = mCallCardPresenter.get();
- if (presenter != null) {
- presenter.onContactInteractionsInfoComplete(callId, entry);
- }
- }
- }
-
- public CallCardPresenter() {
- // create the call timer
- mCallTimer = new CallTimer(new Runnable() {
- @Override
- public void run() {
- updateCallTime();
- }
- });
- }
-
- public void init(Context context, Call call) {
- mContext = Preconditions.checkNotNull(context);
- mDistanceHelper = ObjectFactory.newDistanceHelper(mContext, this);
- mContactsPreferences = ContactsPreferencesFactory.newContactsPreferences(mContext);
-
- // Call may be null if disconnect happened already.
- if (call != null) {
- mPrimary = call;
- if (shouldShowNoteSentToast(mPrimary)) {
- final CallCardUi ui = getUi();
- if (ui != null) {
- ui.showNoteSentToast();
- }
- }
- CallList.getInstance().addCallUpdateListener(call.getId(), this);
-
- // start processing lookups right away.
- if (!call.isConferenceCall()) {
- startContactInfoSearch(call, true, call.getState() == Call.State.INCOMING);
- } else {
- updateContactEntry(null, true);
- }
- }
-
- onStateChange(null, InCallPresenter.getInstance().getInCallState(), CallList.getInstance());
- }
-
- @Override
- public void onUiReady(CallCardUi ui) {
- super.onUiReady(ui);
-
- if (mContactsPreferences != null) {
- mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
- }
-
- // Contact search may have completed before ui is ready.
- if (mPrimaryContactInfo != null) {
- updatePrimaryDisplayInfo();
- }
-
- // Register for call state changes last
- InCallPresenter.getInstance().addListener(this);
- InCallPresenter.getInstance().addIncomingCallListener(this);
- InCallPresenter.getInstance().addDetailsListener(this);
- InCallPresenter.getInstance().addInCallEventListener(this);
- }
-
- @Override
- public void onUiUnready(CallCardUi ui) {
- super.onUiUnready(ui);
-
- // stop getting call state changes
- InCallPresenter.getInstance().removeListener(this);
- InCallPresenter.getInstance().removeIncomingCallListener(this);
- InCallPresenter.getInstance().removeDetailsListener(this);
- InCallPresenter.getInstance().removeInCallEventListener(this);
- if (mPrimary != null) {
- CallList.getInstance().removeCallUpdateListener(mPrimary.getId(), this);
- }
-
- if (mDistanceHelper != null) {
- mDistanceHelper.cleanUp();
- }
-
- mPrimary = null;
- mPrimaryContactInfo = null;
- mSecondaryContactInfo = null;
- }
-
- @Override
- public void onIncomingCall(InCallState oldState, InCallState newState, Call call) {
- // same logic should happen as with onStateChange()
- onStateChange(oldState, newState, CallList.getInstance());
- }
-
- @Override
- public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
- Log.d(this, "onStateChange() " + newState);
- final CallCardUi ui = getUi();
- if (ui == null) {
- return;
- }
-
- Call primary = null;
- Call secondary = null;
-
- if (newState == InCallState.INCOMING) {
- primary = callList.getIncomingCall();
- } else if (newState == InCallState.PENDING_OUTGOING || newState == InCallState.OUTGOING) {
- primary = callList.getOutgoingCall();
- if (primary == null) {
- primary = callList.getPendingOutgoingCall();
- }
-
- // getCallToDisplay doesn't go through outgoing or incoming calls. It will return the
- // highest priority call to display as the secondary call.
- secondary = getCallToDisplay(callList, null, true);
- } else if (newState == InCallState.INCALL) {
- primary = getCallToDisplay(callList, null, false);
- secondary = getCallToDisplay(callList, primary, true);
- }
-
- if (mInCallContactInteractions != null &&
- (oldState == InCallState.INCOMING || newState == InCallState.INCOMING)) {
- ui.showContactContext(newState != InCallState.INCOMING);
- }
-
- Log.d(this, "Primary call: " + primary);
- Log.d(this, "Secondary call: " + secondary);
-
- final boolean primaryChanged = !(Call.areSame(mPrimary, primary) &&
- Call.areSameNumber(mPrimary, primary));
- final boolean secondaryChanged = !(Call.areSame(mSecondary, secondary) &&
- Call.areSameNumber(mSecondary, secondary));
-
- mSecondary = secondary;
- Call previousPrimary = mPrimary;
- mPrimary = primary;
-
- if (primaryChanged && shouldShowNoteSentToast(primary)) {
- ui.showNoteSentToast();
- }
-
- // Refresh primary call information if either:
- // 1. Primary call changed.
- // 2. The call's ability to manage conference has changed.
- // 3. The call subject should be shown or hidden.
- if (shouldRefreshPrimaryInfo(primaryChanged, ui, shouldShowCallSubject(mPrimary))) {
- // primary call has changed
- if (previousPrimary != null) {
- //clear progess spinner (if any) related to previous primary call
- maybeShowProgressSpinner(previousPrimary.getState(),
- Call.SessionModificationState.NO_REQUEST);
- CallList.getInstance().removeCallUpdateListener(previousPrimary.getId(), this);
- }
- CallList.getInstance().addCallUpdateListener(mPrimary.getId(), this);
-
- mPrimaryContactInfo = ContactInfoCache.buildCacheEntryFromCall(mContext, mPrimary,
- mPrimary.getState() == Call.State.INCOMING);
- updatePrimaryDisplayInfo();
- maybeStartSearch(mPrimary, true);
- maybeClearSessionModificationState(mPrimary);
- }
-
- if (previousPrimary != null && mPrimary == null) {
- //clear progess spinner (if any) related to previous primary call
- maybeShowProgressSpinner(previousPrimary.getState(),
- Call.SessionModificationState.NO_REQUEST);
- CallList.getInstance().removeCallUpdateListener(previousPrimary.getId(), this);
- }
-
- if (mSecondary == null) {
- // Secondary call may have ended. Update the ui.
- mSecondaryContactInfo = null;
- updateSecondaryDisplayInfo();
- } else if (secondaryChanged) {
- // secondary call has changed
- mSecondaryContactInfo = ContactInfoCache.buildCacheEntryFromCall(mContext, mSecondary,
- mSecondary.getState() == Call.State.INCOMING);
- updateSecondaryDisplayInfo();
- maybeStartSearch(mSecondary, false);
- maybeClearSessionModificationState(mSecondary);
- }
-
- // Start/stop timers.
- if (isPrimaryCallActive()) {
- Log.d(this, "Starting the calltime timer");
- mCallTimer.start(CALL_TIME_UPDATE_INTERVAL_MS);
- } else {
- Log.d(this, "Canceling the calltime timer");
- mCallTimer.cancel();
- ui.setPrimaryCallElapsedTime(false, 0);
- }
-
- // Set the call state
- int callState = Call.State.IDLE;
- if (mPrimary != null) {
- callState = mPrimary.getState();
- updatePrimaryCallState();
- } else {
- getUi().setCallState(
- callState,
- VideoProfile.STATE_AUDIO_ONLY,
- Call.SessionModificationState.NO_REQUEST,
- new DisconnectCause(DisconnectCause.UNKNOWN),
- null,
- null,
- null,
- false /* isWifi */,
- false /* isConference */,
- false /* isWorkCall */);
- getUi().showHdAudioIndicator(false);
- }
-
- maybeShowManageConferenceCallButton();
-
- // Hide the end call button instantly if we're receiving an incoming call.
- getUi().setEndCallButtonEnabled(shouldShowEndCallButton(mPrimary, callState),
- callState != Call.State.INCOMING /* animate */);
-
- maybeSendAccessibilityEvent(oldState, newState, primaryChanged);
- }
-
- @Override
- public void onDetailsChanged(Call call, Details details) {
- updatePrimaryCallState();
-
- if (call.can(Details.CAPABILITY_MANAGE_CONFERENCE) !=
- details.can(Details.CAPABILITY_MANAGE_CONFERENCE)) {
- maybeShowManageConferenceCallButton();
- }
- }
-
- @Override
- public void onCallChanged(Call call) {
- // No-op; specific call updates handled elsewhere.
- }
-
- /**
- * Handles a change to the session modification state for a call. Triggers showing the progress
- * spinner, as well as updating the call state label.
- *
- * @param sessionModificationState The new session modification state.
- */
- @Override
- public void onSessionModificationStateChange(int sessionModificationState) {
- Log.d(this, "onSessionModificationStateChange : sessionModificationState = " +
- sessionModificationState);
-
- if (mPrimary == null) {
- return;
- }
- maybeShowProgressSpinner(mPrimary.getState(), sessionModificationState);
- getUi().setEndCallButtonEnabled(sessionModificationState !=
- Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST,
- true /* shouldAnimate */);
- updatePrimaryCallState();
- }
-
- /**
- * Handles a change to the last forwarding number by refreshing the primary call info.
- */
- @Override
- public void onLastForwardedNumberChange() {
- Log.v(this, "onLastForwardedNumberChange");
-
- if (mPrimary == null) {
- return;
- }
- updatePrimaryDisplayInfo();
- }
-
- /**
- * Handles a change to the child number by refreshing the primary call info.
- */
- @Override
- public void onChildNumberChange() {
- Log.v(this, "onChildNumberChange");
-
- if (mPrimary == null) {
- return;
- }
- updatePrimaryDisplayInfo();
- }
-
- private boolean shouldRefreshPrimaryInfo(boolean primaryChanged, CallCardUi ui,
- boolean shouldShowCallSubject) {
- if (mPrimary == null) {
- return false;
- }
- return primaryChanged ||
- ui.isManageConferenceVisible() != shouldShowManageConference() ||
- ui.isCallSubjectVisible() != shouldShowCallSubject;
- }
-
- private String getSubscriptionNumber() {
- // If it's an emergency call, and they're not populating the callback number,
- // then try to fall back to the phone sub info (to hopefully get the SIM's
- // number directly from the telephony layer).
- PhoneAccountHandle accountHandle = mPrimary.getAccountHandle();
- if (accountHandle != null) {
- TelecomManager mgr = InCallPresenter.getInstance().getTelecomManager();
- PhoneAccount account = TelecomManagerCompat.getPhoneAccount(mgr, accountHandle);
- if (account != null) {
- return getNumberFromHandle(account.getSubscriptionAddress());
- }
- }
- return null;
- }
-
- private void updatePrimaryCallState() {
- if (getUi() != null && mPrimary != null) {
- boolean isWorkCall = mPrimary.hasProperty(PROPERTY_ENTERPRISE_CALL)
- || (mPrimaryContactInfo == null ? false
- : mPrimaryContactInfo.userType == ContactsUtils.USER_TYPE_WORK);
- getUi().setCallState(
- mPrimary.getState(),
- mPrimary.getVideoState(),
- mPrimary.getSessionModificationState(),
- mPrimary.getDisconnectCause(),
- getConnectionLabel(),
- getCallStateIcon(),
- getGatewayNumber(),
- mPrimary.hasProperty(Details.PROPERTY_WIFI),
- mPrimary.isConferenceCall(),
- isWorkCall);
-
- maybeShowHdAudioIcon();
- setCallbackNumber();
- }
- }
-
- /**
- * Show the HD icon if the call is active and has {@link Details#PROPERTY_HIGH_DEF_AUDIO},
- * except if the call has a last forwarded number (we will show that icon instead).
- */
- private void maybeShowHdAudioIcon() {
- boolean showHdAudioIndicator =
- isPrimaryCallActive() && mPrimary.hasProperty(Details.PROPERTY_HIGH_DEF_AUDIO) &&
- TextUtils.isEmpty(mPrimary.getLastForwardedNumber());
- getUi().showHdAudioIndicator(showHdAudioIndicator);
- }
-
- private void maybeShowSpamIconAndLabel() {
- getUi().showSpamIndicator(mPrimary.isSpam());
- }
-
- /**
- * Only show the conference call button if we can manage the conference.
- */
- private void maybeShowManageConferenceCallButton() {
- getUi().showManageConferenceCallButton(shouldShowManageConference());
- }
-
- /**
- * Determines if a pending session modification exists for the current call. If so, the
- * progress spinner is shown, and the call state is updated.
- *
- * @param callState The call state.
- * @param sessionModificationState The session modification state.
- */
- private void maybeShowProgressSpinner(int callState, int sessionModificationState) {
- final boolean show = sessionModificationState ==
- Call.SessionModificationState.WAITING_FOR_RESPONSE
- && callState == Call.State.ACTIVE;
- if (show != mSpinnerShowing) {
- getUi().setProgressSpinnerVisible(show);
- mSpinnerShowing = show;
- }
- }
-
- /**
- * Determines if the manage conference button should be visible, based on the current primary
- * call.
- *
- * @return {@code True} if the manage conference button should be visible.
- */
- private boolean shouldShowManageConference() {
- if (mPrimary == null) {
- return false;
- }
-
- return mPrimary.can(android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE)
- && !mIsFullscreen;
- }
-
- private void setCallbackNumber() {
- String callbackNumber = null;
-
- // Show the emergency callback number if either:
- // 1. This is an emergency call.
- // 2. The phone is in Emergency Callback Mode, which means we should show the callback
- // number.
- boolean showCallbackNumber = mPrimary.hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
-
- if (mPrimary.isEmergencyCall() || showCallbackNumber) {
- callbackNumber = getSubscriptionNumber();
- } else {
- StatusHints statusHints = mPrimary.getTelecomCall().getDetails().getStatusHints();
- if (statusHints != null) {
- Bundle extras = statusHints.getExtras();
- if (extras != null) {
- callbackNumber = extras.getString(TelecomManager.EXTRA_CALL_BACK_NUMBER);
- }
- }
- }
-
- final String simNumber = TelecomManagerCompat.getLine1Number(
- InCallPresenter.getInstance().getTelecomManager(),
- InCallPresenter.getInstance().getTelephonyManager(),
- mPrimary.getAccountHandle());
- if (!showCallbackNumber && PhoneNumberUtils.compare(callbackNumber, simNumber)) {
- Log.d(this, "Numbers are the same (and callback number is not being forced to show);" +
- " not showing the callback number");
- callbackNumber = null;
- }
-
- getUi().setCallbackNumber(callbackNumber, mPrimary.isEmergencyCall() || showCallbackNumber);
- }
-
- public void updateCallTime() {
- final CallCardUi ui = getUi();
-
- if (ui == null) {
- mCallTimer.cancel();
- } else if (!isPrimaryCallActive()) {
- ui.setPrimaryCallElapsedTime(false, 0);
- mCallTimer.cancel();
- } else {
- final long callStart = mPrimary.getConnectTimeMillis();
- if (callStart > 0) {
- final long duration = System.currentTimeMillis() - callStart;
- ui.setPrimaryCallElapsedTime(true, duration);
- }
- }
- }
-
- public void onCallStateButtonTouched() {
- Intent broadcastIntent = ObjectFactory.getCallStateButtonBroadcastIntent(mContext);
- if (broadcastIntent != null) {
- Log.d(this, "Sending call state button broadcast: ", broadcastIntent);
- mContext.sendBroadcast(broadcastIntent, Manifest.permission.READ_PHONE_STATE);
- }
- }
-
- /**
- * Handles click on the contact photo by toggling fullscreen mode if the current call is a video
- * call.
- */
- public void onContactPhotoClick() {
- if (mPrimary != null && mPrimary.isVideoCall(mContext)) {
- InCallPresenter.getInstance().toggleFullscreenMode();
- }
- }
-
- private void maybeStartSearch(Call call, boolean isPrimary) {
- // no need to start search for conference calls which show generic info.
- if (call != null && !call.isConferenceCall()) {
- startContactInfoSearch(call, isPrimary, call.getState() == Call.State.INCOMING);
- }
- }
-
- private void maybeClearSessionModificationState(Call call) {
- if (call.getSessionModificationState() !=
- Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
- call.setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
- }
- }
-
- /**
- * Starts a query for more contact data for the save primary and secondary calls.
- */
- private void startContactInfoSearch(final Call call, final boolean isPrimary,
- boolean isIncoming) {
- final ContactInfoCache cache = ContactInfoCache.getInstance(mContext);
-
- cache.findInfo(call, isIncoming, new ContactLookupCallback(this, isPrimary));
- }
-
- private void onContactInfoComplete(String callId, ContactCacheEntry entry, boolean isPrimary) {
- final boolean entryMatchesExistingCall =
- (isPrimary && mPrimary != null && TextUtils.equals(callId, mPrimary.getId())) ||
- (!isPrimary && mSecondary != null && TextUtils.equals(callId, mSecondary.getId()));
- if (entryMatchesExistingCall) {
- updateContactEntry(entry, isPrimary);
- } else {
- Log.w(this, "Dropping stale contact lookup info for " + callId);
- }
-
- final Call call = CallList.getInstance().getCallById(callId);
- if (call != null) {
- call.getLogState().contactLookupResult = entry.contactLookupResult;
- }
- if (entry.contactUri != null) {
- CallerInfoUtils.sendViewNotification(mContext, entry.contactUri);
- }
- }
-
- private void onImageLoadComplete(String callId, ContactCacheEntry entry) {
- if (getUi() == null) {
- return;
- }
-
- if (entry.photo != null) {
- if (mPrimary != null && callId.equals(mPrimary.getId())) {
- boolean showContactPhoto = !VideoCallPresenter.showIncomingVideo(
- mPrimary.getVideoState(), mPrimary.getState());
- getUi().setPrimaryImage(entry.photo, showContactPhoto);
- }
- }
- }
-
- private void onContactInteractionsInfoComplete(String callId, ContactCacheEntry entry) {
- if (getUi() == null) {
- return;
- }
-
- if (mPrimary != null && callId.equals(mPrimary.getId())) {
- mPrimaryContactInfo.locationAddress = entry.locationAddress;
- updateContactInteractions();
- }
- }
-
- @Override
- public void onLocationReady() {
- // This will only update the contacts interactions data if the location returns after
- // the contact information is found.
- updateContactInteractions();
- }
-
- private void updateContactInteractions() {
- if (mPrimary != null && mPrimaryContactInfo != null
- && (mPrimaryContactInfo.locationAddress != null
- || mPrimaryContactInfo.openingHours != null)) {
- // TODO: This is hardcoded to "isBusiness" because functionality to differentiate
- // between business and personal has not yet been added.
- if (setInCallContactInteractionsType(true /* isBusiness */)) {
- getUi().setContactContextTitle(
- mInCallContactInteractions.getBusinessListHeaderView());
- }
-
- mInCallContactInteractions.setBusinessInfo(
- mPrimaryContactInfo.locationAddress,
- mDistanceHelper.calculateDistance(mPrimaryContactInfo.locationAddress),
- mPrimaryContactInfo.openingHours);
- getUi().setContactContextContent(mInCallContactInteractions.getListAdapter());
- getUi().showContactContext(mPrimary.getState() != State.INCOMING);
- } else {
- getUi().showContactContext(false);
- }
- }
-
- /**
- * Update the contact interactions type so that the correct UI is shown.
- *
- * @param isBusiness {@code true} if the interaction is a business interaction, {@code false} if
- * it is a personal contact.
- *
- * @return {@code true} if this is a new type of contact interaction (business or personal).
- * {@code false} if it hasn't changed.
- */
- private boolean setInCallContactInteractionsType(boolean isBusiness) {
- if (mInCallContactInteractions == null) {
- mInCallContactInteractions =
- new InCallContactInteractions(mContext, isBusiness);
- return true;
- }
-
- return mInCallContactInteractions.switchContactType(isBusiness);
- }
-
- private void updateContactEntry(ContactCacheEntry entry, boolean isPrimary) {
- if (isPrimary) {
- mPrimaryContactInfo = entry;
- updatePrimaryDisplayInfo();
- } else {
- mSecondaryContactInfo = entry;
- updateSecondaryDisplayInfo();
- }
- }
-
- /**
- * Get the highest priority call to display.
- * Goes through the calls and chooses which to return based on priority of which type of call
- * to display to the user. Callers can use the "ignore" feature to get the second best call
- * by passing a previously found primary call as ignore.
- *
- * @param ignore A call to ignore if found.
- */
- private Call getCallToDisplay(CallList callList, Call ignore, boolean skipDisconnected) {
- // Active calls come second. An active call always gets precedent.
- Call retval = callList.getActiveCall();
- if (retval != null && retval != ignore) {
- return retval;
- }
-
- // Sometimes there is intemediate state that two calls are in active even one is about
- // to be on hold.
- retval = callList.getSecondActiveCall();
- if (retval != null && retval != ignore) {
- return retval;
- }
-
- // Disconnected calls get primary position if there are no active calls
- // to let user know quickly what call has disconnected. Disconnected
- // calls are very short lived.
- if (!skipDisconnected) {
- retval = callList.getDisconnectingCall();
- if (retval != null && retval != ignore) {
- return retval;
- }
- retval = callList.getDisconnectedCall();
- if (retval != null && retval != ignore) {
- return retval;
- }
- }
-
- // Then we go to background call (calls on hold)
- retval = callList.getBackgroundCall();
- if (retval != null && retval != ignore) {
- return retval;
- }
-
- // Lastly, we go to a second background call.
- retval = callList.getSecondBackgroundCall();
-
- return retval;
- }
-
- private void updatePrimaryDisplayInfo() {
- final CallCardUi ui = getUi();
- if (ui == null) {
- // TODO: May also occur if search result comes back after ui is destroyed. Look into
- // removing that case completely.
- Log.d(TAG, "updatePrimaryDisplayInfo called but ui is null!");
- return;
- }
-
- if (mPrimary == null) {
- // Clear the primary display info.
- ui.setPrimary(null, null, false, null, null, false, false, false);
- return;
- }
-
- // Hide the contact photo if we are in a video call and the incoming video surface is
- // showing.
- boolean showContactPhoto = !VideoCallPresenter
- .showIncomingVideo(mPrimary.getVideoState(), mPrimary.getState());
-
- // Call placed through a work phone account.
- boolean hasWorkCallProperty = mPrimary.hasProperty(PROPERTY_ENTERPRISE_CALL);
-
- if (mPrimary.isConferenceCall()) {
- Log.d(TAG, "Update primary display info for conference call.");
-
- ui.setPrimary(
- null /* number */,
- getConferenceString(mPrimary),
- false /* nameIsNumber */,
- null /* label */,
- getConferencePhoto(mPrimary),
- false /* isSipCall */,
- showContactPhoto,
- hasWorkCallProperty);
- } else if (mPrimaryContactInfo != null) {
- Log.d(TAG, "Update primary display info for " + mPrimaryContactInfo);
-
- String name = getNameForCall(mPrimaryContactInfo);
- String number;
-
- boolean isChildNumberShown = !TextUtils.isEmpty(mPrimary.getChildNumber());
- boolean isForwardedNumberShown = !TextUtils.isEmpty(mPrimary.getLastForwardedNumber());
- boolean isCallSubjectShown = shouldShowCallSubject(mPrimary);
-
- if (isCallSubjectShown) {
- ui.setCallSubject(mPrimary.getCallSubject());
- } else {
- ui.setCallSubject(null);
- }
-
- if (isCallSubjectShown) {
- number = null;
- } else if (isChildNumberShown) {
- number = mContext.getString(R.string.child_number, mPrimary.getChildNumber());
- } else if (isForwardedNumberShown) {
- // Use last forwarded number instead of second line, if present.
- number = mPrimary.getLastForwardedNumber();
- } else {
- number = getNumberForCall(mPrimaryContactInfo);
- }
-
- ui.showForwardIndicator(isForwardedNumberShown);
- maybeShowHdAudioIcon();
-
- boolean nameIsNumber = name != null && name.equals(mPrimaryContactInfo.number);
- // Call with caller that is a work contact.
- boolean isWorkContact = (mPrimaryContactInfo.userType == ContactsUtils.USER_TYPE_WORK);
- ui.setPrimary(
- number,
- name,
- nameIsNumber,
- isChildNumberShown || isCallSubjectShown ? null : mPrimaryContactInfo.label,
- mPrimaryContactInfo.photo,
- mPrimaryContactInfo.isSipCall,
- showContactPhoto,
- hasWorkCallProperty || isWorkContact);
-
- updateContactInteractions();
- } else {
- // Clear the primary display info.
- ui.setPrimary(null, null, false, null, null, false, false, false);
- }
-
- if (mEmergencyCallListener != null) {
- boolean isEmergencyCall = mPrimary.isEmergencyCall();
- mEmergencyCallListener.onCallUpdated((BaseFragment) ui, isEmergencyCall);
- }
- maybeShowSpamIconAndLabel();
- }
-
- private void updateSecondaryDisplayInfo() {
- final CallCardUi ui = getUi();
- if (ui == null) {
- return;
- }
-
- if (mSecondary == null) {
- // Clear the secondary display info.
- ui.setSecondary(false, null, false, null, null, false /* isConference */,
- false /* isVideoCall */, mIsFullscreen);
- return;
- }
-
- if (mSecondary.isConferenceCall()) {
- ui.setSecondary(
- true /* show */,
- getConferenceString(mSecondary),
- false /* nameIsNumber */,
- null /* label */,
- getCallProviderLabel(mSecondary),
- true /* isConference */,
- mSecondary.isVideoCall(mContext),
- mIsFullscreen);
- } else if (mSecondaryContactInfo != null) {
- Log.d(TAG, "updateSecondaryDisplayInfo() " + mSecondaryContactInfo);
- String name = getNameForCall(mSecondaryContactInfo);
- boolean nameIsNumber = name != null && name.equals(mSecondaryContactInfo.number);
- ui.setSecondary(
- true /* show */,
- name,
- nameIsNumber,
- mSecondaryContactInfo.label,
- getCallProviderLabel(mSecondary),
- false /* isConference */,
- mSecondary.isVideoCall(mContext),
- mIsFullscreen);
- } else {
- // Clear the secondary display info.
- ui.setSecondary(false, null, false, null, null, false /* isConference */,
- false /* isVideoCall */, mIsFullscreen);
- }
- }
-
-
- /**
- * Gets the phone account to display for a call.
- */
- private PhoneAccount getAccountForCall(Call call) {
- PhoneAccountHandle accountHandle = call.getAccountHandle();
- if (accountHandle == null) {
- return null;
- }
- return TelecomManagerCompat.getPhoneAccount(
- InCallPresenter.getInstance().getTelecomManager(),
- accountHandle);
- }
-
- /**
- * Returns the gateway number for any existing outgoing call.
- */
- private String getGatewayNumber() {
- if (hasOutgoingGatewayCall()) {
- return getNumberFromHandle(mPrimary.getGatewayInfo().getGatewayAddress());
- }
- return null;
- }
-
- /**
- * Return the string label to represent the call provider
- */
- private String getCallProviderLabel(Call call) {
- PhoneAccount account = getAccountForCall(call);
- TelecomManager mgr = InCallPresenter.getInstance().getTelecomManager();
- if (account != null && !TextUtils.isEmpty(account.getLabel())
- && TelecomManagerCompat.getCallCapablePhoneAccounts(mgr).size() > 1) {
- return account.getLabel().toString();
- }
- return null;
- }
-
- /**
- * Returns the label (line of text above the number/name) for any given call.
- * For example, "calling via [Account/Google Voice]" for outgoing calls.
- */
- private String getConnectionLabel() {
- StatusHints statusHints = mPrimary.getTelecomCall().getDetails().getStatusHints();
- if (statusHints != null && !TextUtils.isEmpty(statusHints.getLabel())) {
- return statusHints.getLabel().toString();
- }
-
- if (hasOutgoingGatewayCall() && getUi() != null) {
- // Return the label for the gateway app on outgoing calls.
- final PackageManager pm = mContext.getPackageManager();
- try {
- ApplicationInfo info = pm.getApplicationInfo(
- mPrimary.getGatewayInfo().getGatewayProviderPackageName(), 0);
- return pm.getApplicationLabel(info).toString();
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(this, "Gateway Application Not Found.", e);
- return null;
- }
- }
- return getCallProviderLabel(mPrimary);
- }
-
- private Drawable getCallStateIcon() {
- // Return connection icon if one exists.
- StatusHints statusHints = mPrimary.getTelecomCall().getDetails().getStatusHints();
- if (statusHints != null && statusHints.getIcon() != null) {
- Drawable icon = statusHints.getIcon().loadDrawable(mContext);
- if (icon != null) {
- return icon;
- }
- }
-
- return null;
- }
-
- private boolean hasOutgoingGatewayCall() {
- // We only display the gateway information while STATE_DIALING so return false for any other
- // call state.
- // TODO: mPrimary can be null because this is called from updatePrimaryDisplayInfo which
- // is also called after a contact search completes (call is not present yet). Split the
- // UI update so it can receive independent updates.
- if (mPrimary == null) {
- return false;
- }
- return Call.State.isDialing(mPrimary.getState()) && mPrimary.getGatewayInfo() != null &&
- !mPrimary.getGatewayInfo().isEmpty();
- }
-
- /**
- * Gets the name to display for the call.
- */
- @NeededForTesting
- String getNameForCall(ContactCacheEntry contactInfo) {
- String preferredName = ContactDisplayUtils.getPreferredDisplayName(
- contactInfo.namePrimary,
- contactInfo.nameAlternative,
- mContactsPreferences);
- if (TextUtils.isEmpty(preferredName)) {
- return contactInfo.number;
- }
- return preferredName;
- }
-
- /**
- * Gets the number to display for a call.
- */
- @NeededForTesting
- String getNumberForCall(ContactCacheEntry contactInfo) {
- // If the name is empty, we use the number for the name...so don't show a second
- // number in the number field
- String preferredName = ContactDisplayUtils.getPreferredDisplayName(
- contactInfo.namePrimary,
- contactInfo.nameAlternative,
- mContactsPreferences);
- if (TextUtils.isEmpty(preferredName)) {
- return contactInfo.location;
- }
- return contactInfo.number;
- }
-
- public void secondaryInfoClicked() {
- if (mSecondary == null) {
- Log.w(this, "Secondary info clicked but no secondary call.");
- return;
- }
-
- Log.i(this, "Swapping call to foreground: " + mSecondary);
- TelecomAdapter.getInstance().unholdCall(mSecondary.getId());
- }
-
- public void endCallClicked() {
- if (mPrimary == null) {
- return;
- }
-
- Log.i(this, "Disconnecting call: " + mPrimary);
- final String callId = mPrimary.getId();
- mPrimary.setState(Call.State.DISCONNECTING);
- CallList.getInstance().onUpdate(mPrimary);
- TelecomAdapter.getInstance().disconnectCall(callId);
- }
-
- private String getNumberFromHandle(Uri handle) {
- return handle == null ? "" : handle.getSchemeSpecificPart();
- }
-
- /**
- * Handles a change to the fullscreen mode of the in-call UI.
- *
- * @param isFullscreenMode {@code True} if the in-call UI is entering full screen mode.
- */
- @Override
- public void onFullscreenModeChanged(boolean isFullscreenMode) {
- mIsFullscreen = isFullscreenMode;
- final CallCardUi ui = getUi();
- if (ui == null) {
- return;
- }
- ui.setCallCardVisible(!isFullscreenMode);
- ui.setSecondaryInfoVisible(!isFullscreenMode);
- maybeShowManageConferenceCallButton();
- }
-
- @Override
- public void onSecondaryCallerInfoVisibilityChanged(boolean isVisible, int height) {
- // No-op - the Call Card is the origin of this event.
- }
-
- private boolean isPrimaryCallActive() {
- return mPrimary != null && mPrimary.getState() == Call.State.ACTIVE;
- }
-
- private String getConferenceString(Call call) {
- boolean isGenericConference = call.hasProperty(Details.PROPERTY_GENERIC_CONFERENCE);
- Log.v(this, "getConferenceString: " + isGenericConference);
-
- final int resId = isGenericConference
- ? R.string.card_title_in_call : R.string.card_title_conf_call;
- return mContext.getResources().getString(resId);
- }
-
- private Drawable getConferencePhoto(Call call) {
- boolean isGenericConference = call.hasProperty(Details.PROPERTY_GENERIC_CONFERENCE);
- Log.v(this, "getConferencePhoto: " + isGenericConference);
-
- final int resId = isGenericConference
- ? R.drawable.img_phone : R.drawable.img_conference;
- Drawable photo = mContext.getResources().getDrawable(resId);
- photo.setAutoMirrored(true);
- return photo;
- }
-
- private boolean shouldShowEndCallButton(Call primary, int callState) {
- if (primary == null) {
- return false;
- }
- if ((!Call.State.isConnectingOrConnected(callState)
- && callState != Call.State.DISCONNECTING) || callState == Call.State.INCOMING) {
- return false;
- }
- if (mPrimary.getSessionModificationState()
- == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
- return false;
- }
- return true;
- }
-
- private void maybeSendAccessibilityEvent(InCallState oldState, InCallState newState,
- boolean primaryChanged) {
- if (mContext == null) {
- return;
- }
- final AccessibilityManager am = (AccessibilityManager) mContext.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
- if (!am.isEnabled()) {
- return;
- }
- // Announce the current call if it's new incoming/outgoing call or primary call is changed
- // due to switching calls between two ongoing calls (one is on hold).
- if ((oldState != InCallState.OUTGOING && newState == InCallState.OUTGOING)
- || (oldState != InCallState.INCOMING && newState == InCallState.INCOMING)
- || primaryChanged) {
- if (getUi() != null) {
- getUi().sendAccessibilityAnnouncement();
- }
- }
- }
-
- /**
- * Determines whether the call subject should be visible on the UI. For the call subject to be
- * visible, the call has to be in an incoming or waiting state, and the subject must not be
- * empty.
- *
- * @param call The call.
- * @return {@code true} if the subject should be shown, {@code false} otherwise.
- */
- private boolean shouldShowCallSubject(Call call) {
- if (call == null) {
- return false;
- }
-
- boolean isIncomingOrWaiting = mPrimary.getState() == Call.State.INCOMING ||
- mPrimary.getState() == Call.State.CALL_WAITING;
- return isIncomingOrWaiting && !TextUtils.isEmpty(call.getCallSubject()) &&
- call.getNumberPresentation() == TelecomManager.PRESENTATION_ALLOWED &&
- call.isCallSubjectSupported();
- }
-
- /**
- * Determines whether the "note sent" toast should be shown. It should be shown for a new
- * outgoing call with a subject.
- *
- * @param call The call
- * @return {@code true} if the toast should be shown, {@code false} otherwise.
- */
- private boolean shouldShowNoteSentToast(Call call) {
- return call != null && hasCallSubject(call) && (call.getState() == Call.State.DIALING
- || call.getState() == Call.State.CONNECTING);
- }
-
- private static boolean hasCallSubject(Call call) {
- return !TextUtils.isEmpty(call.getTelecomCall().getDetails().getIntentExtras()
- .getString(TelecomManager.EXTRA_CALL_SUBJECT));
- }
-
- public interface CallCardUi extends Ui {
- void setVisible(boolean on);
- void setContactContextTitle(View listHeaderView);
- void setContactContextContent(ListAdapter listAdapter);
- void showContactContext(boolean show);
- void setCallCardVisible(boolean visible);
- void setPrimary(String number, String name, boolean nameIsNumber, String label,
- Drawable photo, boolean isSipCall, boolean isContactPhotoShown, boolean isWorkCall);
- void setSecondary(boolean show, String name, boolean nameIsNumber, String label,
- String providerLabel, boolean isConference, boolean isVideoCall,
- boolean isFullscreen);
- void setSecondaryInfoVisible(boolean visible);
- void setCallState(int state, int videoState, int sessionModificationState,
- DisconnectCause disconnectCause, String connectionLabel,
- Drawable connectionIcon, String gatewayNumber, boolean isWifi,
- boolean isConference, boolean isWorkCall);
- void setPrimaryCallElapsedTime(boolean show, long duration);
- void setPrimaryName(String name, boolean nameIsNumber);
- void setPrimaryImage(Drawable image, boolean isVisible);
- void setPrimaryPhoneNumber(String phoneNumber);
- void setPrimaryLabel(String label);
- void setEndCallButtonEnabled(boolean enabled, boolean animate);
- void setCallbackNumber(String number, boolean isEmergencyCalls);
- void setCallSubject(String callSubject);
- void setProgressSpinnerVisible(boolean visible);
- void showHdAudioIndicator(boolean visible);
- void showForwardIndicator(boolean visible);
- void showSpamIndicator(boolean visible);
- void showManageConferenceCallButton(boolean visible);
- boolean isManageConferenceVisible();
- boolean isCallSubjectVisible();
- void animateForNewOutgoingCall();
- void sendAccessibilityAnnouncement();
- void showNoteSentToast();
- }
-}
diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java
deleted file mode 100644
index 48870f68a..000000000
--- a/InCallUI/src/com/android/incallui/CallList.java
+++ /dev/null
@@ -1,695 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.os.Handler;
-import android.os.Message;
-import android.os.Trace;
-import android.telecom.DisconnectCause;
-import android.telecom.PhoneAccount;
-
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.service.ExtendedCallInfoService;
-import com.android.incallui.util.TelecomCallUtil;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Maps;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Maintains the list of active calls and notifies interested classes of changes to the call list
- * as they are received from the telephony stack. Primary listener of changes to this class is
- * InCallPresenter.
- */
-public class CallList {
-
- private static final int DISCONNECTED_CALL_SHORT_TIMEOUT_MS = 200;
- private static final int DISCONNECTED_CALL_MEDIUM_TIMEOUT_MS = 2000;
- private static final int DISCONNECTED_CALL_LONG_TIMEOUT_MS = 5000;
-
- private static final int EVENT_DISCONNECTED_TIMEOUT = 1;
- private static final long BLOCK_QUERY_TIMEOUT_MS = 1000;
-
- private static CallList sInstance = new CallList();
-
- private final HashMap<String, Call> mCallById = new HashMap<>();
- private final HashMap<android.telecom.Call, Call> mCallByTelecomCall = new HashMap<>();
- private final HashMap<String, List<String>> mCallTextReponsesMap = Maps.newHashMap();
- /**
- * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
- * load factor before resizing, 1 means we only expect a single thread to
- * access the map so make only a single shard
- */
- private final Set<Listener> mListeners = Collections.newSetFromMap(
- new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
- private final HashMap<String, List<CallUpdateListener>> mCallUpdateListenerMap = Maps
- .newHashMap();
- private final Set<Call> mPendingDisconnectCalls = Collections.newSetFromMap(
- new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
- private ExtendedCallInfoService mExtendedCallInfoService;
-
- /**
- * Static singleton accessor method.
- */
- public static CallList getInstance() {
- return sInstance;
- }
-
- /**
- * USED ONLY FOR TESTING
- * Testing-only constructor. Instance should only be acquired through getInstance().
- */
- @NeededForTesting
- CallList() {
- }
-
- public void onCallAdded(final android.telecom.Call telecomCall, LatencyReport latencyReport) {
- Trace.beginSection("onCallAdded");
- final Call call = new Call(telecomCall, latencyReport);
- Log.d(this, "onCallAdded: callState=" + call.getState());
-
- if (call.getState() == Call.State.INCOMING ||
- call.getState() == Call.State.CALL_WAITING) {
- onIncoming(call, call.getCannedSmsResponses());
- if (mExtendedCallInfoService != null) {
- String number = TelecomCallUtil.getNumber(telecomCall);
- mExtendedCallInfoService.getExtendedCallInfo(number, null,
- new ExtendedCallInfoService.Listener() {
- @Override
- public void onComplete(boolean isSpam) {
- call.setSpam(isSpam);
- onUpdate(call);
- }
- });
- }
- } else {
- onUpdate(call);
- }
-
- call.logCallInitiationType();
- Trace.endSection();
- }
-
- public void onCallRemoved(android.telecom.Call telecomCall) {
- if (mCallByTelecomCall.containsKey(telecomCall)) {
- Call call = mCallByTelecomCall.get(telecomCall);
- Logger.logCall(call);
- if (updateCallInMap(call)) {
- Log.w(this, "Removing call not previously disconnected " + call.getId());
- }
- updateCallTextMap(call, null);
- }
- }
-
- /**
- * Called when a single call disconnects.
- */
- public void onDisconnect(Call call) {
- if (updateCallInMap(call)) {
- Log.i(this, "onDisconnect: " + call);
- // notify those listening for changes on this specific change
- notifyCallUpdateListeners(call);
- // notify those listening for all disconnects
- notifyListenersOfDisconnect(call);
- }
- }
-
- /**
- * Called when a single call has changed.
- */
- public void onIncoming(Call call, List<String> textMessages) {
- if (updateCallInMap(call)) {
- Log.i(this, "onIncoming - " + call);
- }
- updateCallTextMap(call, textMessages);
-
- for (Listener listener : mListeners) {
- listener.onIncomingCall(call);
- }
- }
-
- public void onUpgradeToVideo(Call call){
- Log.d(this, "onUpgradeToVideo call=" + call);
- for (Listener listener : mListeners) {
- listener.onUpgradeToVideo(call);
- }
- }
- /**
- * Called when a single call has changed.
- */
- public void onUpdate(Call call) {
- Trace.beginSection("onUpdate");
- onUpdateCall(call);
- notifyGenericListeners();
- Trace.endSection();
- }
-
- /**
- * Called when a single call has changed session modification state.
- *
- * @param call The call.
- * @param sessionModificationState The new session modification state.
- */
- public void onSessionModificationStateChange(Call call, int sessionModificationState) {
- final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId());
- if (listeners != null) {
- for (CallUpdateListener listener : listeners) {
- listener.onSessionModificationStateChange(sessionModificationState);
- }
- }
- }
-
- /**
- * Called when the last forwarded number changes for a call. With IMS, the last forwarded
- * number changes due to a supplemental service notification, so it is not pressent at the
- * start of the call.
- *
- * @param call The call.
- */
- public void onLastForwardedNumberChange(Call call) {
- final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId());
- if (listeners != null) {
- for (CallUpdateListener listener : listeners) {
- listener.onLastForwardedNumberChange();
- }
- }
- }
-
- /**
- * Called when the child number changes for a call. The child number can be received after a
- * call is initially set up, so we need to be able to inform listeners of the change.
- *
- * @param call The call.
- */
- public void onChildNumberChange(Call call) {
- final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId());
- if (listeners != null) {
- for (CallUpdateListener listener : listeners) {
- listener.onChildNumberChange();
- }
- }
- }
-
- public void notifyCallUpdateListeners(Call call) {
- final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId());
- if (listeners != null) {
- for (CallUpdateListener listener : listeners) {
- listener.onCallChanged(call);
- }
- }
- }
-
- /**
- * Add a call update listener for a call id.
- *
- * @param callId The call id to get updates for.
- * @param listener The listener to add.
- */
- public void addCallUpdateListener(String callId, CallUpdateListener listener) {
- List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(callId);
- if (listeners == null) {
- listeners = new CopyOnWriteArrayList<CallUpdateListener>();
- mCallUpdateListenerMap.put(callId, listeners);
- }
- listeners.add(listener);
- }
-
- /**
- * Remove a call update listener for a call id.
- *
- * @param callId The call id to remove the listener for.
- * @param listener The listener to remove.
- */
- public void removeCallUpdateListener(String callId, CallUpdateListener listener) {
- List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(callId);
- if (listeners != null) {
- listeners.remove(listener);
- }
- }
-
- public void addListener(Listener listener) {
- Preconditions.checkNotNull(listener);
-
- mListeners.add(listener);
-
- // Let the listener know about the active calls immediately.
- listener.onCallListChange(this);
- }
-
- public void removeListener(Listener listener) {
- if (listener != null) {
- mListeners.remove(listener);
- }
- }
-
- /**
- * TODO: Change so that this function is not needed. Instead of assuming there is an active
- * call, the code should rely on the status of a specific Call and allow the presenters to
- * update the Call object when the active call changes.
- */
- public Call getIncomingOrActive() {
- Call retval = getIncomingCall();
- if (retval == null) {
- retval = getActiveCall();
- }
- return retval;
- }
-
- public Call getOutgoingOrActive() {
- Call retval = getOutgoingCall();
- if (retval == null) {
- retval = getActiveCall();
- }
- return retval;
- }
-
- /**
- * A call that is waiting for {@link PhoneAccount} selection
- */
- public Call getWaitingForAccountCall() {
- return getFirstCallWithState(Call.State.SELECT_PHONE_ACCOUNT);
- }
-
- public Call getPendingOutgoingCall() {
- return getFirstCallWithState(Call.State.CONNECTING);
- }
-
- public Call getOutgoingCall() {
- Call call = getFirstCallWithState(Call.State.DIALING);
- if (call == null) {
- call = getFirstCallWithState(Call.State.REDIALING);
- }
- return call;
- }
-
- public Call getActiveCall() {
- return getFirstCallWithState(Call.State.ACTIVE);
- }
-
- public Call getSecondActiveCall() {
- return getCallWithState(Call.State.ACTIVE, 1);
- }
-
- public Call getBackgroundCall() {
- return getFirstCallWithState(Call.State.ONHOLD);
- }
-
- public Call getDisconnectedCall() {
- return getFirstCallWithState(Call.State.DISCONNECTED);
- }
-
- public Call getDisconnectingCall() {
- return getFirstCallWithState(Call.State.DISCONNECTING);
- }
-
- public Call getSecondBackgroundCall() {
- return getCallWithState(Call.State.ONHOLD, 1);
- }
-
- public Call getActiveOrBackgroundCall() {
- Call call = getActiveCall();
- if (call == null) {
- call = getBackgroundCall();
- }
- return call;
- }
-
- public Call getIncomingCall() {
- Call call = getFirstCallWithState(Call.State.INCOMING);
- if (call == null) {
- call = getFirstCallWithState(Call.State.CALL_WAITING);
- }
-
- return call;
- }
-
- public Call getFirstCall() {
- Call result = getIncomingCall();
- if (result == null) {
- result = getPendingOutgoingCall();
- }
- if (result == null) {
- result = getOutgoingCall();
- }
- if (result == null) {
- result = getFirstCallWithState(Call.State.ACTIVE);
- }
- if (result == null) {
- result = getDisconnectingCall();
- }
- if (result == null) {
- result = getDisconnectedCall();
- }
- return result;
- }
-
- public boolean hasLiveCall() {
- Call call = getFirstCall();
- if (call == null) {
- return false;
- }
- return call != getDisconnectingCall() && call != getDisconnectedCall();
- }
-
- /**
- * Returns the first call found in the call map with the specified call modification state.
- * @param state The session modification state to search for.
- * @return The first call with the specified state.
- */
- public Call getVideoUpgradeRequestCall() {
- for(Call call : mCallById.values()) {
- if (call.getSessionModificationState() ==
- Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
- return call;
- }
- }
- return null;
- }
-
- public Call getCallById(String callId) {
- return mCallById.get(callId);
- }
-
- public Call getCallByTelecomCall(android.telecom.Call telecomCall) {
- return mCallByTelecomCall.get(telecomCall);
- }
-
- public List<String> getTextResponses(String callId) {
- return mCallTextReponsesMap.get(callId);
- }
-
- /**
- * Returns first call found in the call map with the specified state.
- */
- public Call getFirstCallWithState(int state) {
- return getCallWithState(state, 0);
- }
-
- /**
- * Returns the [position]th call found in the call map with the specified state.
- * TODO: Improve this logic to sort by call time.
- */
- public Call getCallWithState(int state, int positionToFind) {
- Call retval = null;
- int position = 0;
- for (Call call : mCallById.values()) {
- if (call.getState() == state) {
- if (position >= positionToFind) {
- retval = call;
- break;
- } else {
- position++;
- }
- }
- }
-
- return retval;
- }
-
- /**
- * This is called when the service disconnects, either expectedly or unexpectedly.
- * For the expected case, it's because we have no calls left. For the unexpected case,
- * it is likely a crash of phone and we need to clean up our calls manually. Without phone,
- * there can be no active calls, so this is relatively safe thing to do.
- */
- public void clearOnDisconnect() {
- for (Call call : mCallById.values()) {
- final int state = call.getState();
- if (state != Call.State.IDLE &&
- state != Call.State.INVALID &&
- state != Call.State.DISCONNECTED) {
-
- call.setState(Call.State.DISCONNECTED);
- call.setDisconnectCause(new DisconnectCause(DisconnectCause.UNKNOWN));
- updateCallInMap(call);
- }
- }
- notifyGenericListeners();
- }
-
- /**
- * Called when the user has dismissed an error dialog. This indicates acknowledgement of
- * the disconnect cause, and that any pending disconnects should immediately occur.
- */
- public void onErrorDialogDismissed() {
- final Iterator<Call> iterator = mPendingDisconnectCalls.iterator();
- while (iterator.hasNext()) {
- Call call = iterator.next();
- iterator.remove();
- finishDisconnectedCall(call);
- }
- }
-
- /**
- * Processes an update for a single call.
- *
- * @param call The call to update.
- */
- private void onUpdateCall(Call call) {
- Log.d(this, "\t" + call);
- if (updateCallInMap(call)) {
- Log.i(this, "onUpdate - " + call);
- }
- updateCallTextMap(call, call.getCannedSmsResponses());
- notifyCallUpdateListeners(call);
- }
-
- /**
- * Sends a generic notification to all listeners that something has changed.
- * It is up to the listeners to call back to determine what changed.
- */
- private void notifyGenericListeners() {
- for (Listener listener : mListeners) {
- listener.onCallListChange(this);
- }
- }
-
- private void notifyListenersOfDisconnect(Call call) {
- for (Listener listener : mListeners) {
- listener.onDisconnect(call);
- }
- }
-
- /**
- * Updates the call entry in the local map.
- * @return false if no call previously existed and no call was added, otherwise true.
- */
- private boolean updateCallInMap(Call call) {
- Preconditions.checkNotNull(call);
-
- boolean updated = false;
-
- if (call.getState() == Call.State.DISCONNECTED) {
- // update existing (but do not add!!) disconnected calls
- if (mCallById.containsKey(call.getId())) {
- // For disconnected calls, we want to keep them alive for a few seconds so that the
- // UI has a chance to display anything it needs when a call is disconnected.
-
- // Set up a timer to destroy the call after X seconds.
- final Message msg = mHandler.obtainMessage(EVENT_DISCONNECTED_TIMEOUT, call);
- mHandler.sendMessageDelayed(msg, getDelayForDisconnect(call));
- mPendingDisconnectCalls.add(call);
-
- mCallById.put(call.getId(), call);
- mCallByTelecomCall.put(call.getTelecomCall(), call);
- updated = true;
- }
- } else if (!isCallDead(call)) {
- mCallById.put(call.getId(), call);
- mCallByTelecomCall.put(call.getTelecomCall(), call);
- updated = true;
- } else if (mCallById.containsKey(call.getId())) {
- mCallById.remove(call.getId());
- mCallByTelecomCall.remove(call.getTelecomCall());
- updated = true;
- }
-
- return updated;
- }
-
- private int getDelayForDisconnect(Call call) {
- Preconditions.checkState(call.getState() == Call.State.DISCONNECTED);
-
-
- final int cause = call.getDisconnectCause().getCode();
- final int delay;
- switch (cause) {
- case DisconnectCause.LOCAL:
- delay = DISCONNECTED_CALL_SHORT_TIMEOUT_MS;
- break;
- case DisconnectCause.REMOTE:
- case DisconnectCause.ERROR:
- delay = DISCONNECTED_CALL_MEDIUM_TIMEOUT_MS;
- break;
- case DisconnectCause.REJECTED:
- case DisconnectCause.MISSED:
- case DisconnectCause.CANCELED:
- // no delay for missed/rejected incoming calls and canceled outgoing calls.
- delay = 0;
- break;
- default:
- delay = DISCONNECTED_CALL_LONG_TIMEOUT_MS;
- break;
- }
-
- return delay;
- }
-
- private void updateCallTextMap(Call call, List<String> textResponses) {
- Preconditions.checkNotNull(call);
-
- if (!isCallDead(call)) {
- if (textResponses != null) {
- mCallTextReponsesMap.put(call.getId(), textResponses);
- }
- } else if (mCallById.containsKey(call.getId())) {
- mCallTextReponsesMap.remove(call.getId());
- }
- }
-
- private boolean isCallDead(Call call) {
- final int state = call.getState();
- return Call.State.IDLE == state || Call.State.INVALID == state;
- }
-
- /**
- * Sets up a call for deletion and notifies listeners of change.
- */
- private void finishDisconnectedCall(Call call) {
- if (mPendingDisconnectCalls.contains(call)) {
- mPendingDisconnectCalls.remove(call);
- }
- call.setState(Call.State.IDLE);
- updateCallInMap(call);
- notifyGenericListeners();
- }
-
- /**
- * Notifies all video calls of a change in device orientation.
- *
- * @param rotation The new rotation angle (in degrees).
- */
- public void notifyCallsOfDeviceRotation(int rotation) {
- for (Call call : mCallById.values()) {
- // First, ensure that the call videoState has video enabled (there is no need to set
- // device orientation on a voice call which has not yet been upgraded to video).
- // Second, ensure a VideoCall is set on the call so that the change can be sent to the
- // provider (a VideoCall can be present for a call that does not currently have video,
- // but can be upgraded to video).
-
- // NOTE: is it necessary to use this order because getVideoCall references the class
- // VideoProfile which is not available on APIs <23 (M).
- if (VideoUtils.isVideoCall(call) && call.getVideoCall() != null) {
- call.getVideoCall().setDeviceOrientation(rotation);
- }
- }
- }
-
- /**
- * Handles the timeout for destroying disconnected calls.
- */
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_DISCONNECTED_TIMEOUT:
- Log.d(this, "EVENT_DISCONNECTED_TIMEOUT ", msg.obj);
- finishDisconnectedCall((Call) msg.obj);
- break;
- default:
- Log.wtf(this, "Message not expected: " + msg.what);
- break;
- }
- }
- };
-
- public void setExtendedCallInfoService(ExtendedCallInfoService service) {
- mExtendedCallInfoService = service;
- }
-
- public void onInCallUiShown(boolean forFullScreenIntent) {
- for (Call call : mCallById.values()) {
- call.getLatencyReport().onInCallUiShown(forFullScreenIntent);
- }
- }
-
- /**
- * Listener interface for any class that wants to be notified of changes
- * to the call list.
- */
- public interface Listener {
- /**
- * Called when a new incoming call comes in.
- * This is the only method that gets called for incoming calls. Listeners
- * that want to perform an action on incoming call should respond in this method
- * because {@link #onCallListChange} does not automatically get called for
- * incoming calls.
- */
- public void onIncomingCall(Call call);
- /**
- * Called when a new modify call request comes in
- * This is the only method that gets called for modify requests.
- */
- public void onUpgradeToVideo(Call call);
- /**
- * Called anytime there are changes to the call list. The change can be switching call
- * states, updating information, etc. This method will NOT be called for new incoming
- * calls and for calls that switch to disconnected state. Listeners must add actions
- * to those method implementations if they want to deal with those actions.
- */
- public void onCallListChange(CallList callList);
-
- /**
- * Called when a call switches to the disconnected state. This is the only method
- * that will get called upon disconnection.
- */
- public void onDisconnect(Call call);
-
-
- }
-
- public interface CallUpdateListener {
- // TODO: refactor and limit arg to be call state. Caller info is not needed.
- public void onCallChanged(Call call);
-
- /**
- * Notifies of a change to the session modification state for a call.
- *
- * @param sessionModificationState The new session modification state.
- */
- public void onSessionModificationStateChange(int sessionModificationState);
-
- /**
- * Notifies of a change to the last forwarded number for a call.
- */
- public void onLastForwardedNumberChange();
-
- /**
- * Notifies of a change to the child number for a call.
- */
- public void onChildNumberChange();
- }
-}
diff --git a/InCallUI/src/com/android/incallui/CallTimer.java b/InCallUI/src/com/android/incallui/CallTimer.java
deleted file mode 100644
index d65e63373..000000000
--- a/InCallUI/src/com/android/incallui/CallTimer.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import com.google.common.base.Preconditions;
-
-import android.os.Handler;
-import android.os.SystemClock;
-
-/**
- * Helper class used to keep track of events requiring regular intervals.
- */
-public class CallTimer extends Handler {
- private Runnable mInternalCallback;
- private Runnable mCallback;
- private long mLastReportedTime;
- private long mInterval;
- private boolean mRunning;
-
- public CallTimer(Runnable callback) {
- Preconditions.checkNotNull(callback);
-
- mInterval = 0;
- mLastReportedTime = 0;
- mRunning = false;
- mCallback = callback;
- mInternalCallback = new CallTimerCallback();
- }
-
- public boolean start(long interval) {
- if (interval <= 0) {
- return false;
- }
-
- // cancel any previous timer
- cancel();
-
- mInterval = interval;
- mLastReportedTime = SystemClock.uptimeMillis();
-
- mRunning = true;
- periodicUpdateTimer();
-
- return true;
- }
-
- public void cancel() {
- removeCallbacks(mInternalCallback);
- mRunning = false;
- }
-
- private void periodicUpdateTimer() {
- if (!mRunning) {
- return;
- }
-
- final long now = SystemClock.uptimeMillis();
- long nextReport = mLastReportedTime + mInterval;
- while (now >= nextReport) {
- nextReport += mInterval;
- }
-
- postAtTime(mInternalCallback, nextReport);
- mLastReportedTime = nextReport;
-
- // Run the callback
- mCallback.run();
- }
-
- private class CallTimerCallback implements Runnable {
- @Override
- public void run() {
- periodicUpdateTimer();
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/CallerInfo.java b/InCallUI/src/com/android/incallui/CallerInfo.java
deleted file mode 100644
index f3d0e0763..000000000
--- a/InCallUI/src/com/android/incallui/CallerInfo.java
+++ /dev/null
@@ -1,585 +0,0 @@
-/*
- * Copyright (C) 2006 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.incallui;
-
-import com.android.dialer.util.PhoneLookupUtil;
-import com.google.common.primitives.Longs;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.PhoneLookup;
-import android.provider.ContactsContract.RawContacts;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.PhoneLookupSdkCompat;
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.ContactsUtils.UserType;
-import com.android.contacts.common.util.PhoneNumberHelper;
-import com.android.contacts.common.util.TelephonyManagerUtils;
-import com.android.dialer.R;
-import com.android.dialer.calllog.ContactInfoHelper;
-
-/**
- * Looks up caller information for the given phone number.
- */
-public class CallerInfo {
- private static final String TAG = "CallerInfo";
-
- // We should always use this projection starting from NYC onward.
- private static final String[] DEFAULT_PHONELOOKUP_PROJECTION = new String[] {
- PhoneLookupSdkCompat.CONTACT_ID,
- PhoneLookup.DISPLAY_NAME,
- PhoneLookup.LOOKUP_KEY,
- PhoneLookup.NUMBER,
- PhoneLookup.NORMALIZED_NUMBER,
- PhoneLookup.LABEL,
- PhoneLookup.TYPE,
- PhoneLookup.PHOTO_URI,
- PhoneLookup.CUSTOM_RINGTONE,
- PhoneLookup.SEND_TO_VOICEMAIL
- };
-
- // In pre-N, contact id is stored in {@link PhoneLookup._ID} in non-sip query.
- private static final String[] BACKWARD_COMPATIBLE_NON_SIP_DEFAULT_PHONELOOKUP_PROJECTION =
- new String[] {
- PhoneLookup._ID,
- PhoneLookup.DISPLAY_NAME,
- PhoneLookup.LOOKUP_KEY,
- PhoneLookup.NUMBER,
- PhoneLookup.NORMALIZED_NUMBER,
- PhoneLookup.LABEL,
- PhoneLookup.TYPE,
- PhoneLookup.PHOTO_URI,
- PhoneLookup.CUSTOM_RINGTONE,
- PhoneLookup.SEND_TO_VOICEMAIL
- };
-
- public static String[] getDefaultPhoneLookupProjection(Uri phoneLookupUri) {
- if (CompatUtils.isNCompatible()) {
- return DEFAULT_PHONELOOKUP_PROJECTION;
- }
- // Pre-N
- boolean isSip = phoneLookupUri.getBooleanQueryParameter(
- ContactsContract.PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, false);
- return (isSip) ? DEFAULT_PHONELOOKUP_PROJECTION
- : BACKWARD_COMPATIBLE_NON_SIP_DEFAULT_PHONELOOKUP_PROJECTION;
- }
-
- /**
- * Please note that, any one of these member variables can be null,
- * and any accesses to them should be prepared to handle such a case.
- *
- * Also, it is implied that phoneNumber is more often populated than
- * name is, (think of calls being dialed/received using numbers where
- * names are not known to the device), so phoneNumber should serve as
- * a dependable fallback when name is unavailable.
- *
- * One other detail here is that this CallerInfo object reflects
- * information found on a connection, it is an OUTPUT that serves
- * mainly to display information to the user. In no way is this object
- * used as input to make a connection, so we can choose to display
- * whatever human-readable text makes sense to the user for a
- * connection. This is especially relevant for the phone number field,
- * since it is the one field that is most likely exposed to the user.
- *
- * As an example:
- * 1. User dials "911"
- * 2. Device recognizes that this is an emergency number
- * 3. We use the "Emergency Number" string instead of "911" in the
- * phoneNumber field.
- *
- * What we're really doing here is treating phoneNumber as an essential
- * field here, NOT name. We're NOT always guaranteed to have a name
- * for a connection, but the number should be displayable.
- */
- public String name;
- public String nameAlternative;
- public String phoneNumber;
- public String normalizedNumber;
- public String forwardingNumber;
- public String geoDescription;
-
- public String cnapName;
- public int numberPresentation;
- public int namePresentation;
- public boolean contactExists;
-
- public String phoneLabel;
- /* Split up the phoneLabel into number type and label name */
- public int numberType;
- public String numberLabel;
-
- public int photoResource;
-
- // Contact ID, which will be 0 if a contact comes from the corp CP2.
- public long contactIdOrZero;
- public String lookupKeyOrNull;
- public boolean needUpdate;
- public Uri contactRefUri;
- public @UserType long userType;
-
- /**
- * Contact display photo URI. If a contact has no display photo but a thumbnail, it'll be
- * the thumbnail URI instead.
- */
- public Uri contactDisplayPhotoUri;
-
- // fields to hold individual contact preference data,
- // including the send to voicemail flag and the ringtone
- // uri reference.
- public Uri contactRingtoneUri;
- public boolean shouldSendToVoicemail;
-
- /**
- * Drawable representing the caller image. This is essentially
- * a cache for the image data tied into the connection /
- * callerinfo object.
- *
- * This might be a high resolution picture which is more suitable
- * for full-screen image view than for smaller icons used in some
- * kinds of notifications.
- *
- * The {@link #isCachedPhotoCurrent} flag indicates if the image
- * data needs to be reloaded.
- */
- public Drawable cachedPhoto;
- /**
- * Bitmap representing the caller image which has possibly lower
- * resolution than {@link #cachedPhoto} and thus more suitable for
- * icons (like notification icons).
- *
- * In usual cases this is just down-scaled image of {@link #cachedPhoto}.
- * If the down-scaling fails, this will just become null.
- *
- * The {@link #isCachedPhotoCurrent} flag indicates if the image
- * data needs to be reloaded.
- */
- public Bitmap cachedPhotoIcon;
- /**
- * Boolean which indicates if {@link #cachedPhoto} and
- * {@link #cachedPhotoIcon} is fresh enough. If it is false,
- * those images aren't pointing to valid objects.
- */
- public boolean isCachedPhotoCurrent;
-
- /**
- * String which holds the call subject sent as extra from the lower layers for this call. This
- * is used to display the no-caller ID reason for restricted/unknown number presentation.
- */
- public String callSubject;
-
- private boolean mIsEmergency;
- private boolean mIsVoiceMail;
-
- public CallerInfo() {
- // TODO: Move all the basic initialization here?
- mIsEmergency = false;
- mIsVoiceMail = false;
- userType = ContactsUtils.USER_TYPE_CURRENT;
- }
-
- /**
- * getCallerInfo given a Cursor.
- * @param context the context used to retrieve string constants
- * @param contactRef the URI to attach to this CallerInfo object
- * @param cursor the first object in the cursor is used to build the CallerInfo object.
- * @return the CallerInfo which contains the caller id for the given
- * number. The returned CallerInfo is null if no number is supplied.
- */
- public static CallerInfo getCallerInfo(Context context, Uri contactRef, Cursor cursor) {
- CallerInfo info = new CallerInfo();
- info.photoResource = 0;
- info.phoneLabel = null;
- info.numberType = 0;
- info.numberLabel = null;
- info.cachedPhoto = null;
- info.isCachedPhotoCurrent = false;
- info.contactExists = false;
- info.userType = ContactsUtils.USER_TYPE_CURRENT;
-
- Log.v(TAG, "getCallerInfo() based on cursor...");
-
- if (cursor != null) {
- if (cursor.moveToFirst()) {
- // TODO: photo_id is always available but not taken
- // care of here. Maybe we should store it in the
- // CallerInfo object as well.
-
- long contactId = 0L;
- int columnIndex;
-
- // Look for the name
- columnIndex = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME);
- if (columnIndex != -1) {
- info.name = cursor.getString(columnIndex);
- }
-
- // Look for the number
- columnIndex = cursor.getColumnIndex(PhoneLookup.NUMBER);
- if (columnIndex != -1) {
- info.phoneNumber = cursor.getString(columnIndex);
- }
-
- // Look for the normalized number
- columnIndex = cursor.getColumnIndex(PhoneLookup.NORMALIZED_NUMBER);
- if (columnIndex != -1) {
- info.normalizedNumber = cursor.getString(columnIndex);
- }
-
- // Look for the label/type combo
- columnIndex = cursor.getColumnIndex(PhoneLookup.LABEL);
- if (columnIndex != -1) {
- int typeColumnIndex = cursor.getColumnIndex(PhoneLookup.TYPE);
- if (typeColumnIndex != -1) {
- info.numberType = cursor.getInt(typeColumnIndex);
- info.numberLabel = cursor.getString(columnIndex);
- info.phoneLabel = Phone.getTypeLabel(context.getResources(),
- info.numberType, info.numberLabel)
- .toString();
- }
- }
-
- // Look for the person_id.
- columnIndex = getColumnIndexForPersonId(contactRef, cursor);
- if (columnIndex != -1) {
- contactId = cursor.getLong(columnIndex);
- // QuickContacts in M doesn't support enterprise contact id
- if (contactId != 0 && (ContactsUtils.FLAG_N_FEATURE
- || !Contacts.isEnterpriseContactId(contactId))) {
- info.contactIdOrZero = contactId;
- Log.v(TAG, "==> got info.contactIdOrZero: " + info.contactIdOrZero);
-
- // cache the lookup key for later use with person_id to create lookup URIs
- columnIndex = cursor.getColumnIndex(PhoneLookup.LOOKUP_KEY);
- if (columnIndex != -1) {
- info.lookupKeyOrNull = cursor.getString(columnIndex);
- }
- }
- } else {
- // No valid columnIndex, so we can't look up person_id.
- Log.v(TAG, "Couldn't find contactId column for " + contactRef);
- // Watch out: this means that anything that depends on
- // person_id will be broken (like contact photo lookups in
- // the in-call UI, for example.)
- }
-
- // Display photo URI.
- columnIndex = cursor.getColumnIndex(PhoneLookup.PHOTO_URI);
- if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
- info.contactDisplayPhotoUri = Uri.parse(cursor.getString(columnIndex));
- } else {
- info.contactDisplayPhotoUri = null;
- }
-
- // look for the custom ringtone, create from the string stored
- // in the database.
- columnIndex = cursor.getColumnIndex(PhoneLookup.CUSTOM_RINGTONE);
- if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
- if (TextUtils.isEmpty(cursor.getString(columnIndex))) {
- // make it consistent with frameworks/base/.../CallerInfo.java
- info.contactRingtoneUri = Uri.EMPTY;
- } else {
- info.contactRingtoneUri = Uri.parse(cursor.getString(columnIndex));
- }
- } else {
- info.contactRingtoneUri = null;
- }
-
- // look for the send to voicemail flag, set it to true only
- // under certain circumstances.
- columnIndex = cursor.getColumnIndex(PhoneLookup.SEND_TO_VOICEMAIL);
- info.shouldSendToVoicemail = (columnIndex != -1) &&
- ((cursor.getInt(columnIndex)) == 1);
- info.contactExists = true;
-
- // Determine userType by directoryId and contactId
- final String directory = contactRef == null ? null
- : contactRef.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
- final Long directoryId = directory == null ? null : Longs.tryParse(directory);
- info.userType = ContactsUtils.determineUserType(directoryId, contactId);
-
- info.nameAlternative = ContactInfoHelper.lookUpDisplayNameAlternative(
- context, info.lookupKeyOrNull, info.userType, directoryId);
- }
- cursor.close();
- }
-
- info.needUpdate = false;
- info.name = normalize(info.name);
- info.contactRefUri = contactRef;
-
- return info;
- }
-
- /**
- * getCallerInfo given a URI, look up in the call-log database
- * for the uri unique key.
- * @param context the context used to get the ContentResolver
- * @param contactRef the URI used to lookup caller id
- * @return the CallerInfo which contains the caller id for the given
- * number. The returned CallerInfo is null if no number is supplied.
- */
- private static CallerInfo getCallerInfo(Context context, Uri contactRef) {
-
- return getCallerInfo(context, contactRef,
- context.getContentResolver().query(contactRef, null, null, null, null));
- }
-
- /**
- * Performs another lookup if previous lookup fails and it's a SIP call
- * and the peer's username is all numeric. Look up the username as it
- * could be a PSTN number in the contact database.
- *
- * @param context the query context
- * @param number the original phone number, could be a SIP URI
- * @param previousResult the result of previous lookup
- * @return previousResult if it's not the case
- */
- static CallerInfo doSecondaryLookupIfNecessary(Context context,
- String number, CallerInfo previousResult) {
- if (!previousResult.contactExists
- && PhoneNumberHelper.isUriNumber(number)) {
- String username = PhoneNumberHelper.getUsernameFromUriNumber(number);
- if (PhoneNumberUtils.isGlobalPhoneNumber(username)) {
- previousResult = getCallerInfo(context,
- Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI,
- Uri.encode(username)));
- }
- }
- return previousResult;
- }
-
- // Accessors
-
- /**
- * @return true if the caller info is an emergency number.
- */
- public boolean isEmergencyNumber() {
- return mIsEmergency;
- }
-
- /**
- * @return true if the caller info is a voicemail number.
- */
- public boolean isVoiceMailNumber() {
- return mIsVoiceMail;
- }
-
- /**
- * Mark this CallerInfo as an emergency call.
- * @param context To lookup the localized 'Emergency Number' string.
- * @return this instance.
- */
- /* package */ CallerInfo markAsEmergency(Context context) {
- name = context.getString(R.string.emergency_call_dialog_number_for_display);
- phoneNumber = null;
-
- photoResource = R.drawable.img_phone;
- mIsEmergency = true;
- return this;
- }
-
-
- /**
- * Mark this CallerInfo as a voicemail call. The voicemail label
- * is obtained from the telephony manager. Caller must hold the
- * READ_PHONE_STATE permission otherwise the phoneNumber will be
- * set to null.
- * @return this instance.
- */
- /* package */ CallerInfo markAsVoiceMail(Context context) {
- mIsVoiceMail = true;
-
- try {
- // For voicemail calls, we display the voice mail tag
- // instead of the real phone number in the "number"
- // field.
- name = TelephonyManagerUtils.getVoiceMailAlphaTag(context);
- phoneNumber = null;
- } catch (SecurityException se) {
- // Should never happen: if this process does not have
- // permission to retrieve VM tag, it should not have
- // permission to retrieve VM number and would not call
- // this method.
- // Leave phoneNumber untouched.
- Log.e(TAG, "Cannot access VoiceMail.", se);
- }
- // TODO: There is no voicemail picture?
- // FIXME: FIND ANOTHER ICON
- // photoResource = android.R.drawable.badge_voicemail;
- return this;
- }
-
- private static String normalize(String s) {
- if (s == null || s.length() > 0) {
- return s;
- } else {
- return null;
- }
- }
-
- /**
- * Returns the column index to use to find the "person_id" field in
- * the specified cursor, based on the contact URI that was originally
- * queried.
- *
- * This is a helper function for the getCallerInfo() method that takes
- * a Cursor. Looking up the person_id is nontrivial (compared to all
- * the other CallerInfo fields) since the column we need to use
- * depends on what query we originally ran.
- *
- * Watch out: be sure to not do any database access in this method, since
- * it's run from the UI thread (see comments below for more info.)
- *
- * @return the columnIndex to use (with cursor.getLong()) to get the
- * person_id, or -1 if we couldn't figure out what colum to use.
- *
- * TODO: Add a unittest for this method. (This is a little tricky to
- * test, since we'll need a live contacts database to test against,
- * preloaded with at least some phone numbers and SIP addresses. And
- * we'll probably have to hardcode the column indexes we expect, so
- * the test might break whenever the contacts schema changes. But we
- * can at least make sure we handle all the URI patterns we claim to,
- * and that the mime types match what we expect...)
- */
- private static int getColumnIndexForPersonId(Uri contactRef, Cursor cursor) {
- // TODO: This is pretty ugly now, see bug 2269240 for
- // more details. The column to use depends upon the type of URL:
- // - content://com.android.contacts/data/phones ==> use the "contact_id" column
- // - content://com.android.contacts/phone_lookup ==> use the "_ID" column
- // - content://com.android.contacts/data ==> use the "contact_id" column
- // If it's none of the above, we leave columnIndex=-1 which means
- // that the person_id field will be left unset.
- //
- // The logic here *used* to be based on the mime type of contactRef
- // (for example Phone.CONTENT_ITEM_TYPE would tell us to use the
- // RawContacts.CONTACT_ID column). But looking up the mime type requires
- // a call to context.getContentResolver().getType(contactRef), which
- // isn't safe to do from the UI thread since it can cause an ANR if
- // the contacts provider is slow or blocked (like during a sync.)
- //
- // So instead, figure out the column to use for person_id by just
- // looking at the URI itself.
-
- Log.v(TAG, "- getColumnIndexForPersonId: contactRef URI = '"
- + contactRef + "'...");
- // Warning: Do not enable the following logging (due to ANR risk.)
- // if (VDBG) Rlog.v(TAG, "- MIME type: "
- // + context.getContentResolver().getType(contactRef));
-
- String url = contactRef.toString();
- String columnName = null;
- if (url.startsWith("content://com.android.contacts/data/phones")) {
- // Direct lookup in the Phone table.
- // MIME type: Phone.CONTENT_ITEM_TYPE (= "vnd.android.cursor.item/phone_v2")
- Log.v(TAG, "'data/phones' URI; using RawContacts.CONTACT_ID");
- columnName = RawContacts.CONTACT_ID;
- } else if (url.startsWith("content://com.android.contacts/data")) {
- // Direct lookup in the Data table.
- // MIME type: Data.CONTENT_TYPE (= "vnd.android.cursor.dir/data")
- Log.v(TAG, "'data' URI; using Data.CONTACT_ID");
- // (Note Data.CONTACT_ID and RawContacts.CONTACT_ID are equivalent.)
- columnName = Data.CONTACT_ID;
- } else if (url.startsWith("content://com.android.contacts/phone_lookup")) {
- // Lookup in the PhoneLookup table, which provides "fuzzy matching"
- // for phone numbers.
- // MIME type: PhoneLookup.CONTENT_TYPE (= "vnd.android.cursor.dir/phone_lookup")
- Log.v(TAG, "'phone_lookup' URI; using PhoneLookup._ID");
- columnName = PhoneLookupUtil.getContactIdColumnNameForUri(contactRef);
- } else {
- Log.v(TAG, "Unexpected prefix for contactRef '" + url + "'");
- }
- int columnIndex = (columnName != null) ? cursor.getColumnIndex(columnName) : -1;
- Log.v(TAG, "==> Using column '" + columnName
- + "' (columnIndex = " + columnIndex + ") for person_id lookup...");
- return columnIndex;
- }
-
- /**
- * Updates this CallerInfo's geoDescription field, based on the raw
- * phone number in the phoneNumber field.
- *
- * (Note that the various getCallerInfo() methods do *not* set the
- * geoDescription automatically; you need to call this method
- * explicitly to get it.)
- *
- * @param context the context used to look up the current locale / country
- * @param fallbackNumber if this CallerInfo's phoneNumber field is empty,
- * this specifies a fallback number to use instead.
- */
- public void updateGeoDescription(Context context, String fallbackNumber) {
- String number = TextUtils.isEmpty(phoneNumber) ? fallbackNumber : phoneNumber;
- geoDescription = com.android.dialer.util.PhoneNumberUtil.getGeoDescription(context, number);
- }
-
- /**
- * @return a string debug representation of this instance.
- */
- @Override
- public String toString() {
- // Warning: never check in this file with VERBOSE_DEBUG = true
- // because that will result in PII in the system log.
- final boolean VERBOSE_DEBUG = false;
-
- if (VERBOSE_DEBUG) {
- return new StringBuilder(384)
- .append(super.toString() + " { ")
- .append("\nname: " + name)
- .append("\nphoneNumber: " + phoneNumber)
- .append("\nnormalizedNumber: " + normalizedNumber)
- .append("\forwardingNumber: " + forwardingNumber)
- .append("\ngeoDescription: " + geoDescription)
- .append("\ncnapName: " + cnapName)
- .append("\nnumberPresentation: " + numberPresentation)
- .append("\nnamePresentation: " + namePresentation)
- .append("\ncontactExists: " + contactExists)
- .append("\nphoneLabel: " + phoneLabel)
- .append("\nnumberType: " + numberType)
- .append("\nnumberLabel: " + numberLabel)
- .append("\nphotoResource: " + photoResource)
- .append("\ncontactIdOrZero: " + contactIdOrZero)
- .append("\nneedUpdate: " + needUpdate)
- .append("\ncontactRefUri: " + contactRefUri)
- .append("\ncontactRingtoneUri: " + contactRingtoneUri)
- .append("\ncontactDisplayPhotoUri: " + contactDisplayPhotoUri)
- .append("\nshouldSendToVoicemail: " + shouldSendToVoicemail)
- .append("\ncachedPhoto: " + cachedPhoto)
- .append("\nisCachedPhotoCurrent: " + isCachedPhotoCurrent)
- .append("\nemergency: " + mIsEmergency)
- .append("\nvoicemail: " + mIsVoiceMail)
- .append("\nuserType: " + userType)
- .append(" }")
- .toString();
- } else {
- return new StringBuilder(128)
- .append(super.toString() + " { ")
- .append("name " + ((name == null) ? "null" : "non-null"))
- .append(", phoneNumber " + ((phoneNumber == null) ? "null" : "non-null"))
- .append(" }")
- .toString();
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/CallerInfoAsyncQuery.java b/InCallUI/src/com/android/incallui/CallerInfoAsyncQuery.java
deleted file mode 100644
index f7f0cbb5d..000000000
--- a/InCallUI/src/com/android/incallui/CallerInfoAsyncQuery.java
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * Copyright (C) 2006 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.incallui;
-
-import com.google.common.primitives.Longs;
-
-import android.Manifest;
-import android.content.AsyncQueryHandler;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.SQLException;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Directory;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.compat.DirectoryCompat;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.common.util.TelephonyManagerUtils;
-import com.android.dialer.R;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.service.CachedNumberLookupService;
-import com.android.dialer.service.CachedNumberLookupService.CachedContactInfo;
-import com.android.dialerbind.ObjectFactory;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Locale;
-
-/**
- * Helper class to make it easier to run asynchronous caller-id lookup queries.
- * @see CallerInfo
- *
- */
-public class CallerInfoAsyncQuery {
- private static final boolean DBG = false;
- private static final String LOG_TAG = "CallerInfoAsyncQuery";
-
- private static final int EVENT_NEW_QUERY = 1;
- private static final int EVENT_ADD_LISTENER = 2;
- private static final int EVENT_END_OF_QUEUE = 3;
- private static final int EVENT_EMERGENCY_NUMBER = 4;
- private static final int EVENT_VOICEMAIL_NUMBER = 5;
-
- private CallerInfoAsyncQueryHandler mHandler;
-
- // If the CallerInfo query finds no contacts, should we use the
- // PhoneNumberOfflineGeocoder to look up a "geo description"?
- // (TODO: This could become a flag in config.xml if it ever needs to be
- // configured on a per-product basis.)
- private static final boolean ENABLE_UNKNOWN_NUMBER_GEO_DESCRIPTION = true;
-
- /**
- * Interface for a CallerInfoAsyncQueryHandler result return.
- */
- public interface OnQueryCompleteListener {
- /**
- * Called when the query is complete.
- */
- public void onQueryComplete(int token, Object cookie, CallerInfo ci);
- }
-
-
- /**
- * Wrap the cookie from the WorkerArgs with additional information needed by our
- * classes.
- */
- private static final class CookieWrapper {
- public OnQueryCompleteListener listener;
- public Object cookie;
- public int event;
- public String number;
- }
-
- /**
- * Simple exception used to communicate problems with the query pool.
- */
- public static class QueryPoolException extends SQLException {
- public QueryPoolException(String error) {
- super(error);
- }
- }
-
- /**
- * Our own implementation of the AsyncQueryHandler.
- */
- private class CallerInfoAsyncQueryHandler extends AsyncQueryHandler {
-
- @Override
- public void startQuery(int token, Object cookie, Uri uri, String[] projection,
- String selection, String[] selectionArgs, String orderBy) {
- if (DBG) {
- // Show stack trace with the arguments.
- android.util.Log.d(LOG_TAG, "InCall: startQuery: url=" + uri +
- " projection=[" + Arrays.toString(projection) + "]" +
- " selection=" + selection + " " +
- " args=[" + Arrays.toString(selectionArgs) + "]",
- new RuntimeException("STACKTRACE"));
- }
- super.startQuery(token, cookie, uri, projection, selection, selectionArgs, orderBy);
- }
-
- /**
- * The information relevant to each CallerInfo query. Each query may have multiple
- * listeners, so each AsyncCursorInfo is associated with 2 or more CookieWrapper
- * objects in the queue (one with a new query event, and one with a end event, with
- * 0 or more additional listeners in between).
- */
- private Context mQueryContext;
- private Uri mQueryUri;
- private CallerInfo mCallerInfo;
-
- /**
- * Our own query worker thread.
- *
- * This thread handles the messages enqueued in the looper. The normal sequence
- * of events is that a new query shows up in the looper queue, followed by 0 or
- * more add listener requests, and then an end request. Of course, these requests
- * can be interlaced with requests from other tokens, but is irrelevant to this
- * handler since the handler has no state.
- *
- * Note that we depend on the queue to keep things in order; in other words, the
- * looper queue must be FIFO with respect to input from the synchronous startQuery
- * calls and output to this handleMessage call.
- *
- * This use of the queue is required because CallerInfo objects may be accessed
- * multiple times before the query is complete. All accesses (listeners) must be
- * queued up and informed in order when the query is complete.
- */
- protected class CallerInfoWorkerHandler extends WorkerHandler {
- public CallerInfoWorkerHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- WorkerArgs args = (WorkerArgs) msg.obj;
- CookieWrapper cw = (CookieWrapper) args.cookie;
-
- if (cw == null) {
- // Normally, this should never be the case for calls originating
- // from within this code.
- // However, if there is any code that this Handler calls (such as in
- // super.handleMessage) that DOES place unexpected messages on the
- // queue, then we need pass these messages on.
- Log.d(this, "Unexpected command (CookieWrapper is null): " + msg.what +
- " ignored by CallerInfoWorkerHandler, passing onto parent.");
-
- super.handleMessage(msg);
- } else {
- Log.d(this, "Processing event: " + cw.event + " token (arg1): " + msg.arg1 +
- " command: " + msg.what + " query URI: " +
- sanitizeUriToString(args.uri));
-
- switch (cw.event) {
- case EVENT_NEW_QUERY:
- //start the sql command.
- super.handleMessage(msg);
- break;
-
- // shortcuts to avoid query for recognized numbers.
- case EVENT_EMERGENCY_NUMBER:
- case EVENT_VOICEMAIL_NUMBER:
-
- case EVENT_ADD_LISTENER:
- case EVENT_END_OF_QUEUE:
- // query was already completed, so just send the reply.
- // passing the original token value back to the caller
- // on top of the event values in arg1.
- Message reply = args.handler.obtainMessage(msg.what);
- reply.obj = args;
- reply.arg1 = msg.arg1;
-
- reply.sendToTarget();
-
- break;
- default:
- }
- }
- }
- }
-
-
- /**
- * Asynchronous query handler class for the contact / callerinfo object.
- */
- private CallerInfoAsyncQueryHandler(Context context) {
- super(context.getContentResolver());
- }
-
- @Override
- protected Handler createHandler(Looper looper) {
- return new CallerInfoWorkerHandler(looper);
- }
-
- /**
- * Overrides onQueryComplete from AsyncQueryHandler.
- *
- * This method takes into account the state of this class; we construct the CallerInfo
- * object only once for each set of listeners. When the query thread has done its work
- * and calls this method, we inform the remaining listeners in the queue, until we're
- * out of listeners. Once we get the message indicating that we should expect no new
- * listeners for this CallerInfo object, we release the AsyncCursorInfo back into the
- * pool.
- */
- @Override
- protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- try {
- Log.d(this, "##### onQueryComplete() ##### query complete for token: " + token);
-
- //get the cookie and notify the listener.
- CookieWrapper cw = (CookieWrapper) cookie;
- if (cw == null) {
- // Normally, this should never be the case for calls originating
- // from within this code.
- // However, if there is any code that calls this method, we should
- // check the parameters to make sure they're viable.
- Log.d(this, "Cookie is null, ignoring onQueryComplete() request.");
- return;
- }
-
- if (cw.event == EVENT_END_OF_QUEUE) {
- release();
- return;
- }
-
- // check the token and if needed, create the callerinfo object.
- if (mCallerInfo == null) {
- if ((mQueryContext == null) || (mQueryUri == null)) {
- throw new QueryPoolException
- ("Bad context or query uri, or CallerInfoAsyncQuery already released.");
- }
-
- // adjust the callerInfo data as needed, and only if it was set from the
- // initial query request.
- // Change the callerInfo number ONLY if it is an emergency number or the
- // voicemail number, and adjust other data (including photoResource)
- // accordingly.
- if (cw.event == EVENT_EMERGENCY_NUMBER) {
- // Note we're setting the phone number here (refer to javadoc
- // comments at the top of CallerInfo class).
- mCallerInfo = new CallerInfo().markAsEmergency(mQueryContext);
- } else if (cw.event == EVENT_VOICEMAIL_NUMBER) {
- mCallerInfo = new CallerInfo().markAsVoiceMail(mQueryContext);
- } else {
- mCallerInfo = CallerInfo.getCallerInfo(mQueryContext, mQueryUri, cursor);
- Log.d(this, "==> Got mCallerInfo: " + mCallerInfo);
-
- CallerInfo newCallerInfo = CallerInfo.doSecondaryLookupIfNecessary(
- mQueryContext, cw.number, mCallerInfo);
- if (newCallerInfo != mCallerInfo) {
- mCallerInfo = newCallerInfo;
- Log.d(this, "#####async contact look up with numeric username"
- + mCallerInfo);
- }
-
- // Final step: look up the geocoded description.
- if (ENABLE_UNKNOWN_NUMBER_GEO_DESCRIPTION) {
- // Note we do this only if we *don't* have a valid name (i.e. if
- // no contacts matched the phone number of the incoming call),
- // since that's the only case where the incoming-call UI cares
- // about this field.
- //
- // (TODO: But if we ever want the UI to show the geoDescription
- // even when we *do* match a contact, we'll need to either call
- // updateGeoDescription() unconditionally here, or possibly add a
- // new parameter to CallerInfoAsyncQuery.startQuery() to force
- // the geoDescription field to be populated.)
-
- if (TextUtils.isEmpty(mCallerInfo.name)) {
- // Actually when no contacts match the incoming phone number,
- // the CallerInfo object is totally blank here (i.e. no name
- // *or* phoneNumber). So we need to pass in cw.number as
- // a fallback number.
- mCallerInfo.updateGeoDescription(mQueryContext, cw.number);
- }
- }
-
- // Use the number entered by the user for display.
- if (!TextUtils.isEmpty(cw.number)) {
- mCallerInfo.phoneNumber = PhoneNumberUtils.formatNumber(cw.number,
- mCallerInfo.normalizedNumber,
- TelephonyManagerUtils.getCurrentCountryIso(mQueryContext,
- Locale.getDefault()));
- }
- }
-
- Log.d(this, "constructing CallerInfo object for token: " + token);
-
- //notify that we can clean up the queue after this.
- CookieWrapper endMarker = new CookieWrapper();
- endMarker.event = EVENT_END_OF_QUEUE;
- startQuery(token, endMarker, null, null, null, null, null);
- }
-
- //notify the listener that the query is complete.
- if (cw.listener != null) {
- Log.d(this, "notifying listener: " + cw.listener.getClass().toString() +
- " for token: " + token + mCallerInfo);
- cw.listener.onQueryComplete(token, cw.cookie, mCallerInfo);
- }
- } finally {
- // The cursor may have been closed in CallerInfo.getCallerInfo()
- if (cursor != null && !cursor.isClosed()) {
- cursor.close();
- }
- }
- }
- }
-
- /**
- * Private constructor for factory methods.
- */
- private CallerInfoAsyncQuery() {
- }
-
- public static void startQuery(final int token, final Context context, final CallerInfo info,
- final OnQueryCompleteListener listener, final Object cookie) {
- Log.d(LOG_TAG, "##### CallerInfoAsyncQuery startContactProviderQuery()... #####");
- Log.d(LOG_TAG, "- number: " + info.phoneNumber);
- Log.d(LOG_TAG, "- cookie: " + cookie);
- if (!PermissionsUtil.hasPermission(context, Manifest.permission.READ_CONTACTS)) {
- Log.w(LOG_TAG, "Dialer doesn't have permission to read contacts.");
- listener.onQueryComplete(token, cookie, info);
- return;
- }
-
- OnQueryCompleteListener contactsProviderQueryCompleteListener =
- new OnQueryCompleteListener() {
- @Override
- public void onQueryComplete(int token, Object cookie, CallerInfo ci) {
- Log.d(LOG_TAG, "contactsProviderQueryCompleteListener done");
- // If there are no other directory queries, make sure that the listener is
- // notified of this result. see b/27621628
- if ((ci != null && ci.contactExists) ||
- !startOtherDirectoriesQuery(token, context, info, listener, cookie)) {
- if (listener != null && ci != null) {
- listener.onQueryComplete(token, cookie, ci);
- }
- }
- }
- };
- startDefaultDirectoryQuery(token, context, info, contactsProviderQueryCompleteListener,
- cookie);
- }
-
- // Private methods
- private static CallerInfoAsyncQuery startDefaultDirectoryQuery(int token, Context context,
- CallerInfo info, OnQueryCompleteListener listener, Object cookie) {
- // Construct the URI object and query params, and start the query.
- Uri uri = ContactInfoHelper.getContactInfoLookupUri(info.phoneNumber);
- return startQueryInternal(token, context, info, listener, cookie, uri);
- }
-
- /**
- * Factory method to start the query based on a CallerInfo object.
- *
- * Note: if the number contains an "@" character we treat it
- * as a SIP address, and look it up directly in the Data table
- * rather than using the PhoneLookup table.
- * TODO: But eventually we should expose two separate methods, one for
- * numbers and one for SIP addresses, and then have
- * PhoneUtils.startGetCallerInfo() decide which one to call based on
- * the phone type of the incoming connection.
- */
- private static CallerInfoAsyncQuery startQueryInternal(int token, Context context,
- CallerInfo info, OnQueryCompleteListener listener, Object cookie, Uri contactRef) {
- if (DBG) {
- Log.d(LOG_TAG, "==> contactRef: " + sanitizeUriToString(contactRef));
- }
-
- CallerInfoAsyncQuery c = new CallerInfoAsyncQuery();
- c.allocate(context, contactRef);
-
- //create cookieWrapper, start query
- CookieWrapper cw = new CookieWrapper();
- cw.listener = listener;
- cw.cookie = cookie;
- cw.number = info.phoneNumber;
-
- // check to see if these are recognized numbers, and use shortcuts if we can.
- if (PhoneNumberUtils.isLocalEmergencyNumber(context, info.phoneNumber)) {
- cw.event = EVENT_EMERGENCY_NUMBER;
- } else if (info.isVoiceMailNumber()) {
- cw.event = EVENT_VOICEMAIL_NUMBER;
- } else {
- cw.event = EVENT_NEW_QUERY;
- }
-
-
- String[] proejection = CallerInfo.getDefaultPhoneLookupProjection(contactRef);
- c.mHandler.startQuery(token,
- cw, // cookie
- contactRef, // uri
- proejection, // projection
- null, // selection
- null, // selectionArgs
- null); // orderBy
- return c;
- }
-
- // Return value indicates if listener was notified.
- private static boolean startOtherDirectoriesQuery(int token, Context context, CallerInfo info,
- OnQueryCompleteListener listener, Object cookie) {
- long[] directoryIds = getDirectoryIds(context);
- int size = directoryIds.length;
- if (size == 0) {
- return false;
- }
-
- DirectoryQueryCompleteListenerFactory listenerFactory =
- new DirectoryQueryCompleteListenerFactory(context, size, listener);
-
- // The current implementation of multiple async query runs in single handler thread
- // in AsyncQueryHandler.
- // intermediateListener.onQueryComplete is also called from the same caller thread.
- // TODO(b/26019872): use thread pool instead of single thread.
- for (int i = 0; i < size; i++) {
- long directoryId = directoryIds[i];
- Uri uri = ContactInfoHelper.getContactInfoLookupUri(info.phoneNumber, directoryId);
- if (DBG) {
- Log.d(LOG_TAG, "directoryId: " + directoryId + " uri: " + uri);
- }
- OnQueryCompleteListener intermediateListener =
- listenerFactory.newListener(directoryId);
- startQueryInternal(token, context, info, intermediateListener, cookie, uri);
- }
- return true;
- }
-
- /* Directory lookup related code - START */
- private static final String[] DIRECTORY_PROJECTION = new String[] {Directory._ID};
-
- private static long[] getDirectoryIds(Context context) {
- ArrayList<Long> results = new ArrayList<>();
-
- Uri uri = Directory.CONTENT_URI;
- if (ContactsUtils.FLAG_N_FEATURE) {
- uri = Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, "directories_enterprise");
- }
-
- ContentResolver cr = context.getContentResolver();
- Cursor cursor = cr.query(uri, DIRECTORY_PROJECTION, null, null, null);
- addDirectoryIdsFromCursor(cursor, results);
-
- return Longs.toArray(results);
- }
-
- private static void addDirectoryIdsFromCursor(Cursor cursor, ArrayList<Long> results) {
- if (cursor != null) {
- int idIndex = cursor.getColumnIndex(Directory._ID);
- while (cursor.moveToNext()) {
- long id = cursor.getLong(idIndex);
- if (DirectoryCompat.isRemoteDirectoryId(id)) {
- results.add(id);
- }
- }
- cursor.close();
- }
- }
-
- private static final class DirectoryQueryCompleteListenerFactory {
- // Make sure listener to be called once and only once
- private int mCount;
- private boolean mIsListenerCalled;
- private final OnQueryCompleteListener mListener;
- private final Context mContext;
- private final CachedNumberLookupService mCachedNumberLookupService =
- ObjectFactory.newCachedNumberLookupService();
-
- private class DirectoryQueryCompleteListener implements OnQueryCompleteListener {
- private final long mDirectoryId;
-
- DirectoryQueryCompleteListener(long directoryId) {
- mDirectoryId = directoryId;
- }
-
- @Override
- public void onQueryComplete(int token, Object cookie, CallerInfo ci) {
- onDirectoryQueryComplete(token, cookie, ci, mDirectoryId);
- }
- }
-
- DirectoryQueryCompleteListenerFactory(Context context, int size,
- OnQueryCompleteListener listener) {
- mCount = size;
- mListener = listener;
- mIsListenerCalled = false;
- mContext = context;
- }
-
- private void onDirectoryQueryComplete(int token, Object cookie, CallerInfo ci,
- long directoryId) {
- boolean shouldCallListener = false;
- synchronized (this) {
- mCount = mCount - 1;
- if (!mIsListenerCalled && (ci.contactExists || mCount == 0)) {
- mIsListenerCalled = true;
- shouldCallListener = true;
- }
- }
-
- // Don't call callback in synchronized block because mListener.onQueryComplete may
- // take long time to complete
- if (shouldCallListener && mListener != null) {
- addCallerInfoIntoCache(ci, directoryId);
- mListener.onQueryComplete(token, cookie, ci);
- }
- }
-
- private void addCallerInfoIntoCache(CallerInfo ci, long directoryId) {
- if (ci.contactExists && mCachedNumberLookupService != null) {
- // 1. Cache caller info
- CachedContactInfo cachedContactInfo = CallerInfoUtils
- .buildCachedContactInfo(mCachedNumberLookupService, ci);
- String directoryLabel = mContext.getString(R.string.directory_search_label);
- cachedContactInfo.setDirectorySource(directoryLabel, directoryId);
- mCachedNumberLookupService.addContact(mContext, cachedContactInfo);
-
- // 2. Cache photo
- if (ci.contactDisplayPhotoUri != null && ci.normalizedNumber != null) {
- try (InputStream in = mContext.getContentResolver()
- .openInputStream(ci.contactDisplayPhotoUri)) {
- if (in != null) {
- mCachedNumberLookupService.addPhoto(mContext, ci.normalizedNumber, in);
- }
- } catch (IOException e) {
- Log.e(LOG_TAG, "failed to fetch directory contact photo", e);
- }
-
- }
- }
- }
-
- public OnQueryCompleteListener newListener(long directoryId) {
- return new DirectoryQueryCompleteListener(directoryId);
- }
- }
- /* Directory lookup related code - END */
-
- /**
- * Method to create a new CallerInfoAsyncQueryHandler object, ensuring correct
- * state of context and uri.
- */
- private void allocate(Context context, Uri contactRef) {
- if ((context == null) || (contactRef == null)){
- throw new QueryPoolException("Bad context or query uri.");
- }
- mHandler = new CallerInfoAsyncQueryHandler(context);
- mHandler.mQueryContext = context;
- mHandler.mQueryUri = contactRef;
- }
-
- /**
- * Releases the relevant data.
- */
- private void release() {
- mHandler.mQueryContext = null;
- mHandler.mQueryUri = null;
- mHandler.mCallerInfo = null;
- mHandler = null;
- }
-
- private static String sanitizeUriToString(Uri uri) {
- if (uri != null) {
- String uriString = uri.toString();
- int indexOfLastSlash = uriString.lastIndexOf('/');
- if (indexOfLastSlash > 0) {
- return uriString.substring(0, indexOfLastSlash) + "/xxxxxxx";
- } else {
- return uriString;
- }
- } else {
- return "";
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/CallerInfoUtils.java b/InCallUI/src/com/android/incallui/CallerInfoUtils.java
deleted file mode 100644
index 289b652fc..000000000
--- a/InCallUI/src/com/android/incallui/CallerInfoUtils.java
+++ /dev/null
@@ -1,234 +0,0 @@
-package com.android.incallui;
-
-import android.content.Context;
-import android.content.Loader;
-import android.content.Loader.OnLoadCompleteListener;
-import android.net.Uri;
-import android.telecom.PhoneAccount;
-import android.telecom.TelecomManager;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.model.Contact;
-import com.android.contacts.common.model.ContactLoader;
-import com.android.dialer.R;
-import com.android.dialer.calllog.ContactInfo;
-import com.android.dialer.service.CachedNumberLookupService;
-import com.android.dialer.service.CachedNumberLookupService.CachedContactInfo;
-import com.android.dialer.util.TelecomUtil;
-
-import java.util.Arrays;
-
-/**
- * Utility methods for contact and caller info related functionality
- */
-public class CallerInfoUtils {
-
- private static final String TAG = CallerInfoUtils.class.getSimpleName();
-
- /** Define for not a special CNAP string */
- private static final int CNAP_SPECIAL_CASE_NO = -1;
-
- public CallerInfoUtils() {
- }
-
- private static final int QUERY_TOKEN = -1;
-
- /**
- * This is called to get caller info for a call. This will return a CallerInfo
- * object immediately based off information in the call, but
- * more information is returned to the OnQueryCompleteListener (which contains
- * information about the phone number label, user's name, etc).
- */
- public static CallerInfo getCallerInfoForCall(Context context, Call call,
- CallerInfoAsyncQuery.OnQueryCompleteListener listener) {
- CallerInfo info = buildCallerInfo(context, call);
-
- // TODO: Have phoneapp send a Uri when it knows the contact that triggered this call.
-
- if (info.numberPresentation == TelecomManager.PRESENTATION_ALLOWED) {
- // Start the query with the number provided from the call.
- Log.d(TAG, "==> Actually starting CallerInfoAsyncQuery.startQuery()...");
- CallerInfoAsyncQuery.startQuery(QUERY_TOKEN, context, info, listener, call);
- }
- return info;
- }
-
- public static CallerInfo buildCallerInfo(Context context, Call call) {
- CallerInfo info = new CallerInfo();
-
- // Store CNAP information retrieved from the Connection (we want to do this
- // here regardless of whether the number is empty or not).
- info.cnapName = call.getCnapName();
- info.name = info.cnapName;
- info.numberPresentation = call.getNumberPresentation();
- info.namePresentation = call.getCnapNamePresentation();
- info.callSubject = call.getCallSubject();
-
- String number = call.getNumber();
- if (!TextUtils.isEmpty(number)) {
- final String[] numbers = number.split("&");
- number = numbers[0];
- if (numbers.length > 1) {
- info.forwardingNumber = numbers[1];
- }
-
- number = modifyForSpecialCnapCases(context, info, number, info.numberPresentation);
- info.phoneNumber = number;
- }
-
- // Because the InCallUI is immediately launched before the call is connected, occasionally
- // a voicemail call will be passed to InCallUI as a "voicemail:" URI without a number.
- // This call should still be handled as a voicemail call.
- if ((call.getHandle() != null &&
- PhoneAccount.SCHEME_VOICEMAIL.equals(call.getHandle().getScheme())) ||
- isVoiceMailNumber(context, call)) {
- info.markAsVoiceMail(context);
- }
-
- ContactInfoCache.getInstance(context).maybeInsertCnapInformationIntoCache(context, call,
- info);
-
- return info;
- }
-
- /**
- * Creates a new {@link CachedContactInfo} from a {@link CallerInfo}
- *
- * @param lookupService the {@link CachedNumberLookupService} used to build a
- * new {@link CachedContactInfo}
- * @param {@link CallerInfo} object
- * @return a CachedContactInfo object created from this CallerInfo
- * @throws NullPointerException if lookupService or ci are null
- */
- public static CachedContactInfo buildCachedContactInfo(CachedNumberLookupService lookupService,
- CallerInfo ci) {
- ContactInfo info = new ContactInfo();
- info.name = ci.name;
- info.type = ci.numberType;
- info.label = ci.phoneLabel;
- info.number = ci.phoneNumber;
- info.normalizedNumber = ci.normalizedNumber;
- info.photoUri = ci.contactDisplayPhotoUri;
- info.userType = ci.userType;
-
- CachedContactInfo cacheInfo = lookupService.buildCachedContactInfo(info);
- cacheInfo.setLookupKey(ci.lookupKeyOrNull);
- return cacheInfo;
- }
-
- public static boolean isVoiceMailNumber(Context context, Call call) {
- return TelecomUtil.isVoicemailNumber(context,
- call.getTelecomCall().getDetails().getAccountHandle(),
- call.getNumber());
- }
-
- /**
- * Handles certain "corner cases" for CNAP. When we receive weird phone numbers
- * from the network to indicate different number presentations, convert them to
- * expected number and presentation values within the CallerInfo object.
- * @param number number we use to verify if we are in a corner case
- * @param presentation presentation value used to verify if we are in a corner case
- * @return the new String that should be used for the phone number
- */
- /* package */static String modifyForSpecialCnapCases(Context context, CallerInfo ci,
- String number, int presentation) {
- // Obviously we return number if ci == null, but still return number if
- // number == null, because in these cases the correct string will still be
- // displayed/logged after this function returns based on the presentation value.
- if (ci == null || number == null) return number;
-
- Log.d(TAG, "modifyForSpecialCnapCases: initially, number="
- + toLogSafePhoneNumber(number)
- + ", presentation=" + presentation + " ci " + ci);
-
- // "ABSENT NUMBER" is a possible value we could get from the network as the
- // phone number, so if this happens, change it to "Unknown" in the CallerInfo
- // and fix the presentation to be the same.
- final String[] absentNumberValues =
- context.getResources().getStringArray(R.array.absent_num);
- if (Arrays.asList(absentNumberValues).contains(number)
- && presentation == TelecomManager.PRESENTATION_ALLOWED) {
- number = context.getString(R.string.unknown);
- ci.numberPresentation = TelecomManager.PRESENTATION_UNKNOWN;
- }
-
- // Check for other special "corner cases" for CNAP and fix them similarly. Corner
- // cases only apply if we received an allowed presentation from the network, so check
- // if we think we have an allowed presentation, or if the CallerInfo presentation doesn't
- // match the presentation passed in for verification (meaning we changed it previously
- // because it's a corner case and we're being called from a different entry point).
- if (ci.numberPresentation == TelecomManager.PRESENTATION_ALLOWED
- || (ci.numberPresentation != presentation
- && presentation == TelecomManager.PRESENTATION_ALLOWED)) {
- // For all special strings, change number & numberPrentation.
- if (isCnapSpecialCaseRestricted(number)) {
- number = context.getString(R.string.private_num);
- ci.numberPresentation = TelecomManager.PRESENTATION_RESTRICTED;
- } else if (isCnapSpecialCaseUnknown(number)) {
- number = context.getString(R.string.unknown);
- ci.numberPresentation = TelecomManager.PRESENTATION_UNKNOWN;
- }
- Log.d(TAG, "SpecialCnap: number=" + toLogSafePhoneNumber(number)
- + "; presentation now=" + ci.numberPresentation);
- }
- Log.d(TAG, "modifyForSpecialCnapCases: returning number string="
- + toLogSafePhoneNumber(number));
- return number;
- }
-
- private static boolean isCnapSpecialCaseRestricted(String n) {
- return n.equals("PRIVATE") || n.equals("P") || n.equals("RES");
- }
-
- private static boolean isCnapSpecialCaseUnknown(String n) {
- return n.equals("UNAVAILABLE") || n.equals("UNKNOWN") || n.equals("UNA") || n.equals("U");
- }
-
- /* package */static String toLogSafePhoneNumber(String number) {
- // For unknown number, log empty string.
- if (number == null) {
- return "";
- }
-
- // Todo: Figure out an equivalent for VDBG
- if (false) {
- // When VDBG is true we emit PII.
- return number;
- }
-
- // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare
- // sanitized phone numbers.
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < number.length(); i++) {
- char c = number.charAt(i);
- if (c == '-' || c == '@' || c == '.' || c == '&') {
- builder.append(c);
- } else {
- builder.append('x');
- }
- }
- return builder.toString();
- }
-
- /**
- * Send a notification using a {@link ContactLoader} to inform the sync adapter that we are
- * viewing a particular contact, so that it can download the high-res photo.
- */
- public static void sendViewNotification(Context context, Uri contactUri) {
- final ContactLoader loader = new ContactLoader(context, contactUri,
- true /* postViewNotification */);
- loader.registerListener(0, new OnLoadCompleteListener<Contact>() {
- @Override
- public void onLoadComplete(
- Loader<Contact> loader, Contact contact) {
- try {
- loader.reset();
- } catch (RuntimeException e) {
- Log.e(TAG, "Error resetting loader", e);
- }
- }
- });
- loader.startLoading();
- }
-}
diff --git a/InCallUI/src/com/android/incallui/CircularRevealFragment.java b/InCallUI/src/com/android/incallui/CircularRevealFragment.java
deleted file mode 100644
index 01bd253ec..000000000
--- a/InCallUI/src/com/android/incallui/CircularRevealFragment.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2014 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.incallui;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.graphics.Outline;
-import android.graphics.Point;
-import android.os.Bundle;
-import android.view.Display;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewAnimationUtils;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnPreDrawListener;
-
-import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
-import com.android.dialer.R;
-
-public class CircularRevealFragment extends Fragment {
- static final String TAG = "CircularRevealFragment";
-
- private Point mTouchPoint;
- private OnCircularRevealCompleteListener mListener;
- private boolean mAnimationStarted;
-
- interface OnCircularRevealCompleteListener {
- public void onCircularRevealComplete(FragmentManager fm);
- }
-
- public static void startCircularReveal(FragmentManager fm, Point touchPoint,
- OnCircularRevealCompleteListener listener) {
- if (fm.findFragmentByTag(TAG) == null) {
- fm.beginTransaction().add(R.id.main,
- new CircularRevealFragment(touchPoint, listener), TAG)
- .commitAllowingStateLoss();
- } else {
- Log.w(TAG, "An instance of CircularRevealFragment already exists");
- }
- }
-
- public static void endCircularReveal(FragmentManager fm) {
- final Fragment fragment = fm.findFragmentByTag(TAG);
- if (fragment != null) {
- fm.beginTransaction().remove(fragment).commitAllowingStateLoss();
- }
- }
-
- /**
- * Empty constructor used only by the {@link FragmentManager}.
- */
- public CircularRevealFragment() {}
-
- public CircularRevealFragment(Point touchPoint, OnCircularRevealCompleteListener listener) {
- mTouchPoint = touchPoint;
- mListener = listener;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- if (!mAnimationStarted) {
- // Only run the animation once for each instance of the fragment
- startOutgoingAnimation(InCallPresenter.getInstance().getThemeColors());
- }
- mAnimationStarted = true;
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- return inflater.inflate(R.layout.outgoing_call_animation, container, false);
- }
-
- public void startOutgoingAnimation(MaterialPalette palette) {
- final Activity activity = getActivity();
- if (activity == null) {
- Log.w(this, "Asked to do outgoing call animation when not attached");
- return;
- }
-
- final View view = activity.getWindow().getDecorView();
-
- // The circle starts from an initial size of 0 so clip it such that it is invisible.
- // Otherwise the first frame is drawn with a fully opaque screen which causes jank. When
- // the animation later starts, this clip will be clobbered by the circular reveal clip.
- // See ViewAnimationUtils.createCircularReveal.
- view.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- // Using (0, 0, 0, 0) will not work since the outline will simply be treated as
- // an empty outline.
- outline.setOval(-1, -1, 0, 0);
- }
- });
- view.setClipToOutline(true);
-
- if (palette != null) {
- view.findViewById(R.id.outgoing_call_animation_circle).setBackgroundColor(
- palette.mPrimaryColor);
- activity.getWindow().setStatusBarColor(palette.mSecondaryColor);
- }
-
- view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- final ViewTreeObserver vto = view.getViewTreeObserver();
- if (vto.isAlive()) {
- vto.removeOnPreDrawListener(this);
- }
- final Animator animator = getRevealAnimator(mTouchPoint);
- if (animator != null) {
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- view.setClipToOutline(false);
- if (mListener != null) {
- mListener.onCircularRevealComplete(getFragmentManager());
- }
- }
- });
- animator.start();
- }
- return false;
- }
- });
- }
-
- private Animator getRevealAnimator(Point touchPoint) {
- final Activity activity = getActivity();
- if (activity == null) {
- return null;
- }
- final View view = activity.getWindow().getDecorView();
- final Display display = activity.getWindowManager().getDefaultDisplay();
- final Point size = new Point();
- display.getSize(size);
-
- int startX = size.x / 2;
- int startY = size.y / 2;
- if (touchPoint != null) {
- startX = touchPoint.x;
- startY = touchPoint.y;
- }
-
- final Animator valueAnimator = ViewAnimationUtils.createCircularReveal(view,
- startX, startY, 0, Math.max(size.x, size.y));
- valueAnimator.setDuration(getResources().getInteger(R.integer.reveal_animation_duration));
- return valueAnimator;
- }
-}
diff --git a/InCallUI/src/com/android/incallui/ConferenceManagerFragment.java b/InCallUI/src/com/android/incallui/ConferenceManagerFragment.java
deleted file mode 100644
index fe941c8c5..000000000
--- a/InCallUI/src/com/android/incallui/ConferenceManagerFragment.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.app.ActionBar;
-import android.content.Context;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ListView;
-
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.dialer.R;
-
-import java.util.List;
-
-/**
- * Fragment that allows the user to manage a conference call.
- */
-public class ConferenceManagerFragment
- extends BaseFragment<ConferenceManagerPresenter,
- ConferenceManagerPresenter.ConferenceManagerUi>
- implements ConferenceManagerPresenter.ConferenceManagerUi {
-
- private static final String KEY_IS_VISIBLE = "key_conference_is_visible";
-
- private ListView mConferenceParticipantList;
- private int mActionBarElevation;
- private ContactPhotoManager mContactPhotoManager;
- private LayoutInflater mInflater;
- private ConferenceParticipantListAdapter mConferenceParticipantListAdapter;
- private boolean mIsVisible;
- private boolean mIsRecreating;
-
- @Override
- public ConferenceManagerPresenter createPresenter() {
- return new ConferenceManagerPresenter();
- }
-
- @Override
- public ConferenceManagerPresenter.ConferenceManagerUi getUi() {
- return this;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (savedInstanceState != null) {
- mIsRecreating = true;
- mIsVisible = savedInstanceState.getBoolean(KEY_IS_VISIBLE);
- }
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- final View parent =
- inflater.inflate(R.layout.conference_manager_fragment, container, false);
-
- mConferenceParticipantList = (ListView) parent.findViewById(R.id.participantList);
- mContactPhotoManager =
- ContactPhotoManager.getInstance(getActivity().getApplicationContext());
- mActionBarElevation =
- (int) getResources().getDimension(R.dimen.incall_action_bar_elevation);
- mInflater = LayoutInflater.from(getActivity().getApplicationContext());
-
- return parent;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- if (mIsRecreating) {
- onVisibilityChanged(mIsVisible);
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- outState.putBoolean(KEY_IS_VISIBLE, mIsVisible);
- super.onSaveInstanceState(outState);
- }
-
- public void onVisibilityChanged(boolean isVisible) {
- mIsVisible = isVisible;
- ActionBar actionBar = getActivity().getActionBar();
- if (isVisible) {
- actionBar.setTitle(R.string.manageConferenceLabel);
- actionBar.setElevation(mActionBarElevation);
- actionBar.setHideOffset(0);
- actionBar.show();
-
- final CallList calls = CallList.getInstance();
- getPresenter().init(getActivity(), calls);
- // Request focus on the list of participants for accessibility purposes. This ensures
- // that once the list of participants is shown, the first participant is announced.
- mConferenceParticipantList.requestFocus();
- } else {
- actionBar.setElevation(0);
- actionBar.setHideOffset(actionBar.getHeight());
- }
- }
-
- @Override
- public boolean isFragmentVisible() {
- return isVisible();
- }
-
- @Override
- public void update(Context context, List<Call> participants, boolean parentCanSeparate) {
- if (mConferenceParticipantListAdapter == null) {
- mConferenceParticipantListAdapter = new ConferenceParticipantListAdapter(
- mConferenceParticipantList, context, mInflater, mContactPhotoManager);
-
- mConferenceParticipantList.setAdapter(mConferenceParticipantListAdapter);
- }
- mConferenceParticipantListAdapter.updateParticipants(participants, parentCanSeparate);
- }
-
- @Override
- public void refreshCall(Call call) {
- mConferenceParticipantListAdapter.refreshCall(call);
- }
-}
diff --git a/InCallUI/src/com/android/incallui/ConferenceManagerPresenter.java b/InCallUI/src/com/android/incallui/ConferenceManagerPresenter.java
deleted file mode 100644
index 6fb6e5dda..000000000
--- a/InCallUI/src/com/android/incallui/ConferenceManagerPresenter.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.content.Context;
-
-import com.android.incallui.InCallPresenter.InCallDetailsListener;
-import com.android.incallui.InCallPresenter.InCallState;
-import com.android.incallui.InCallPresenter.InCallStateListener;
-import com.android.incallui.InCallPresenter.IncomingCallListener;
-
-import com.google.common.base.Preconditions;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Logic for call buttons.
- */
-public class ConferenceManagerPresenter
- extends Presenter<ConferenceManagerPresenter.ConferenceManagerUi>
- implements InCallStateListener, InCallDetailsListener, IncomingCallListener {
-
- private Context mContext;
-
- @Override
- public void onUiReady(ConferenceManagerUi ui) {
- super.onUiReady(ui);
-
- // register for call state changes last
- InCallPresenter.getInstance().addListener(this);
- InCallPresenter.getInstance().addIncomingCallListener(this);
- }
-
- @Override
- public void onUiUnready(ConferenceManagerUi ui) {
- super.onUiUnready(ui);
-
- InCallPresenter.getInstance().removeListener(this);
- InCallPresenter.getInstance().removeIncomingCallListener(this);
- }
-
- @Override
- public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
- if (getUi().isFragmentVisible()) {
- Log.v(this, "onStateChange" + newState);
- if (newState == InCallState.INCALL) {
- final Call call = callList.getActiveOrBackgroundCall();
- if (call != null && call.isConferenceCall()) {
- Log.v(this, "Number of existing calls is " +
- String.valueOf(call.getChildCallIds().size()));
- update(callList);
- } else {
- InCallPresenter.getInstance().showConferenceCallManager(false);
- }
- } else {
- InCallPresenter.getInstance().showConferenceCallManager(false);
- }
- }
- }
-
- @Override
- public void onDetailsChanged(Call call, android.telecom.Call.Details details) {
- boolean canDisconnect = details.can(
- android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE);
- boolean canSeparate = details.can(
- android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE);
-
- if (call.can(android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE)
- != canDisconnect
- || call.can(android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE)
- != canSeparate) {
- getUi().refreshCall(call);
- }
-
- if (!details.can(
- android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE)) {
- InCallPresenter.getInstance().showConferenceCallManager(false);
- }
- }
-
- @Override
- public void onIncomingCall(InCallState oldState, InCallState newState, Call call) {
- // When incoming call exists, set conference ui invisible.
- if (getUi().isFragmentVisible()) {
- Log.d(this, "onIncomingCall()... Conference ui is showing, hide it.");
- InCallPresenter.getInstance().showConferenceCallManager(false);
- }
- }
-
- public void init(Context context, CallList callList) {
- mContext = Preconditions.checkNotNull(context);
- mContext = context;
- update(callList);
- }
-
- /**
- * Updates the conference participant adapter.
- *
- * @param callList The callList.
- */
- private void update(CallList callList) {
- // callList is non null, but getActiveOrBackgroundCall() may return null
- final Call currentCall = callList.getActiveOrBackgroundCall();
- if (currentCall == null) {
- return;
- }
-
- ArrayList<Call> calls = new ArrayList<>(currentCall.getChildCallIds().size());
- for (String callerId : currentCall.getChildCallIds()) {
- calls.add(callList.getCallById(callerId));
- }
-
- Log.d(this, "Number of calls is " + String.valueOf(calls.size()));
-
- // Users can split out a call from the conference call if either the active call or the
- // holding call is empty. If both are filled, users can not split out another call.
- final boolean hasActiveCall = (callList.getActiveCall() != null);
- final boolean hasHoldingCall = (callList.getBackgroundCall() != null);
- boolean canSeparate = !(hasActiveCall && hasHoldingCall);
-
- getUi().update(mContext, calls, canSeparate);
- }
-
- public interface ConferenceManagerUi extends Ui {
- boolean isFragmentVisible();
- void update(Context context, List<Call> participants, boolean parentCanSeparate);
- void refreshCall(Call call);
- }
-}
diff --git a/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java b/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java
deleted file mode 100644
index d68ae1f6f..000000000
--- a/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java
+++ /dev/null
@@ -1,533 +0,0 @@
-/*
- * Copyright (C) 2014 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.incallui;
-
-import com.google.common.base.MoreObjects;
-
-import android.content.Context;
-import android.net.Uri;
-import android.support.annotation.Nullable;
-import android.text.BidiFormatter;
-import android.text.TextDirectionHeuristics;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
-import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.R;
-import com.android.incallui.ContactInfoCache.ContactCacheEntry;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Adapter for a ListView containing conference call participant information.
- */
-public class ConferenceParticipantListAdapter extends BaseAdapter {
-
- /**
- * Internal class which represents a participant. Includes a reference to the {@link Call} and
- * the corresponding {@link ContactCacheEntry} for the participant.
- */
- private class ParticipantInfo {
- private Call mCall;
- private ContactCacheEntry mContactCacheEntry;
- private boolean mCacheLookupComplete = false;
-
- public ParticipantInfo(Call call, ContactCacheEntry contactCacheEntry) {
- mCall = call;
- mContactCacheEntry = contactCacheEntry;
- }
-
- public Call getCall() {
- return mCall;
- }
-
- public void setCall(Call call) {
- mCall = call;
- }
-
- public ContactCacheEntry getContactCacheEntry() {
- return mContactCacheEntry;
- }
-
- public void setContactCacheEntry(ContactCacheEntry entry) {
- mContactCacheEntry = entry;
- }
-
- public boolean isCacheLookupComplete() {
- return mCacheLookupComplete;
- }
-
- public void setCacheLookupComplete(boolean cacheLookupComplete) {
- mCacheLookupComplete = cacheLookupComplete;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof ParticipantInfo) {
- ParticipantInfo p = (ParticipantInfo) o;
- return
- Objects.equals(p.getCall().getId(), mCall.getId());
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return mCall.getId().hashCode();
- }
- }
-
- /**
- * Callback class used when making requests to the {@link ContactInfoCache} to resolve contact
- * info and contact photos for conference participants.
- */
- public static class ContactLookupCallback implements ContactInfoCache.ContactInfoCacheCallback {
- private final WeakReference<ConferenceParticipantListAdapter> mListAdapter;
-
- public ContactLookupCallback(ConferenceParticipantListAdapter listAdapter) {
- mListAdapter = new WeakReference<ConferenceParticipantListAdapter>(listAdapter);
- }
-
- /**
- * Called when contact info has been resolved.
- *
- * @param callId The call id.
- * @param entry The new contact information.
- */
- @Override
- public void onContactInfoComplete(String callId, ContactCacheEntry entry) {
- update(callId, entry);
- }
-
- /**
- * Called when contact photo has been loaded into the cache.
- *
- * @param callId The call id.
- * @param entry The new contact information.
- */
- @Override
- public void onImageLoadComplete(String callId, ContactCacheEntry entry) {
- update(callId, entry);
- }
-
- @Override
- public void onContactInteractionsInfoComplete(String callId, ContactCacheEntry entry) {}
-
- /**
- * Updates the contact information for a participant.
- *
- * @param callId The call id.
- * @param entry The new contact information.
- */
- private void update(String callId, ContactCacheEntry entry) {
- ConferenceParticipantListAdapter listAdapter = mListAdapter.get();
- if (listAdapter != null) {
- listAdapter.updateContactInfo(callId, entry);
- }
- }
- }
-
- /**
- * Listener used to handle tap of the "disconnect' button for a participant.
- */
- private View.OnClickListener mDisconnectListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- View parent = (View) v.getParent();
- String callId = (String) parent.getTag();
- TelecomAdapter.getInstance().disconnectCall(callId);
- }
- };
-
- /**
- * Listener used to handle tap of the "separate' button for a participant.
- */
- private View.OnClickListener mSeparateListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- View parent = (View) v.getParent();
- String callId = (String) parent.getTag();
- TelecomAdapter.getInstance().separateCall(callId);
- }
- };
-
- /**
- * The ListView containing the participant information.
- */
- private final ListView mListView;
-
- /**
- * The conference participants to show in the ListView.
- */
- private List<ParticipantInfo> mConferenceParticipants = new ArrayList<>();
-
- /**
- * Hashmap to make accessing participant info by call Id faster.
- */
- private final HashMap<String, ParticipantInfo> mParticipantsByCallId = new HashMap<>();
-
- /**
- * The context.
- */
- private final Context mContext;
-
- /**
- * ContactsPreferences used to lookup displayName preferences
- */
- @Nullable private final ContactsPreferences mContactsPreferences;
-
- /**
- * The layout inflater used to inflate new views.
- */
- private final LayoutInflater mLayoutInflater;
-
- /**
- * Contact photo manager to retrieve cached contact photo information.
- */
- private final ContactPhotoManager mContactPhotoManager;
-
- /**
- * {@code True} if the conference parent supports separating calls from the conference.
- */
- private boolean mParentCanSeparate;
-
- /**
- * Creates an instance of the ConferenceParticipantListAdapter.
- *
- * @param listView The listview.
- * @param context The context.
- * @param layoutInflater The layout inflater.
- * @param contactPhotoManager The contact photo manager, used to load contact photos.
- */
- public ConferenceParticipantListAdapter(ListView listView, Context context,
- LayoutInflater layoutInflater, ContactPhotoManager contactPhotoManager) {
-
- mListView = listView;
- mContext = context;
- mContactsPreferences = ContactsPreferencesFactory.newContactsPreferences(mContext);
- mLayoutInflater = layoutInflater;
- mContactPhotoManager = contactPhotoManager;
- }
-
- /**
- * Updates the adapter with the new conference participant information provided.
- *
- * @param conferenceParticipants The list of conference participants.
- * @param parentCanSeparate {@code True} if the parent supports separating calls from the
- * conference.
- */
- public void updateParticipants(List<Call> conferenceParticipants, boolean parentCanSeparate) {
- if (mContactsPreferences != null) {
- mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
- mContactsPreferences.refreshValue(ContactsPreferences.SORT_ORDER_KEY);
- }
- mParentCanSeparate = parentCanSeparate;
- updateParticipantInfo(conferenceParticipants);
- }
-
- /**
- * Determines the number of participants in the conference.
- *
- * @return The number of participants.
- */
- @Override
- public int getCount() {
- return mConferenceParticipants.size();
- }
-
- /**
- * Retrieves an item from the list of participants.
- *
- * @param position Position of the item whose data we want within the adapter's
- * data set.
- * @return The {@link ParticipantInfo}.
- */
- @Override
- public Object getItem(int position) {
- return mConferenceParticipants.get(position);
- }
-
- /**
- * Retreives the adapter-specific item id for an item at a specified position.
- *
- * @param position The position of the item within the adapter's data set whose row id we want.
- * @return The item id.
- */
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- /**
- * Refreshes call information for the call passed in.
- *
- * @param call The new call information.
- */
- public void refreshCall(Call call) {
- String callId = call.getId();
-
- if (mParticipantsByCallId.containsKey(callId)) {
- ParticipantInfo participantInfo = mParticipantsByCallId.get(callId);
- participantInfo.setCall(call);
- refreshView(callId);
- }
- }
-
- /**
- * Attempts to refresh the view for the specified call ID. This ensures the contact info and
- * photo loaded from cache are updated.
- *
- * @param callId The call id.
- */
- private void refreshView(String callId) {
- int first = mListView.getFirstVisiblePosition();
- int last = mListView.getLastVisiblePosition();
-
- for (int position = 0; position <= last - first; position++) {
- View view = mListView.getChildAt(position);
- String rowCallId = (String) view.getTag();
- if (rowCallId.equals(callId)) {
- getView(position+first, view, mListView);
- break;
- }
- }
- }
-
- /**
- * Creates or populates an existing conference participant row.
- *
- * @param position The position of the item within the adapter's data set of the item whose view
- * we want.
- * @param convertView The old view to reuse, if possible.
- * @param parent The parent that this view will eventually be attached to
- * @return The populated view.
- */
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // Make sure we have a valid convertView to start with
- final View result = convertView == null
- ? mLayoutInflater.inflate(R.layout.caller_in_conference, parent, false)
- : convertView;
-
- ParticipantInfo participantInfo = mConferenceParticipants.get(position);
- Call call = participantInfo.getCall();
- ContactCacheEntry contactCache = participantInfo.getContactCacheEntry();
-
- final ContactInfoCache cache = ContactInfoCache.getInstance(mContext);
-
- // If a cache lookup has not yet been performed to retrieve the contact information and
- // photo, do it now.
- if (!participantInfo.isCacheLookupComplete()) {
- cache.findInfo(participantInfo.getCall(),
- participantInfo.getCall().getState() == Call.State.INCOMING,
- new ContactLookupCallback(this));
- }
-
- boolean thisRowCanSeparate = mParentCanSeparate && call.getTelecomCall().getDetails().can(
- android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE);
- boolean thisRowCanDisconnect = call.getTelecomCall().getDetails().can(
- android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE);
-
- setCallerInfoForRow(result, contactCache.namePrimary,
- ContactDisplayUtils.getPreferredDisplayName(contactCache.namePrimary,
- contactCache.nameAlternative, mContactsPreferences),
- contactCache.number, contactCache.label,
- contactCache.lookupKey, contactCache.displayPhotoUri, thisRowCanSeparate,
- thisRowCanDisconnect);
-
- // Tag the row in the conference participant list with the call id to make it easier to
- // find calls when contact cache information is loaded.
- result.setTag(call.getId());
-
- return result;
- }
-
- /**
- * Replaces the contact info for a participant and triggers a refresh of the UI.
- *
- * @param callId The call id.
- * @param entry The new contact info.
- */
- /* package */ void updateContactInfo(String callId, ContactCacheEntry entry) {
- if (mParticipantsByCallId.containsKey(callId)) {
- ParticipantInfo participantInfo = mParticipantsByCallId.get(callId);
- participantInfo.setContactCacheEntry(entry);
- participantInfo.setCacheLookupComplete(true);
- refreshView(callId);
- }
- }
-
- /**
- * Sets the caller information for a row in the conference participant list.
- *
- * @param view The view to set the details on.
- * @param callerName The participant's name.
- * @param callerNumber The participant's phone number.
- * @param callerNumberType The participant's phone number typ.e
- * @param lookupKey The lookup key for the participant (for photo lookup).
- * @param photoUri The URI of the contact photo.
- * @param thisRowCanSeparate {@code True} if this participant can separate from the conference.
- * @param thisRowCanDisconnect {@code True} if this participant can be disconnected.
- */
- private final void setCallerInfoForRow(View view, String callerName, String preferredName,
- String callerNumber, String callerNumberType, String lookupKey, Uri photoUri,
- boolean thisRowCanSeparate, boolean thisRowCanDisconnect) {
-
- final ImageView photoView = (ImageView) view.findViewById(R.id.callerPhoto);
- final TextView nameTextView = (TextView) view.findViewById(R.id.conferenceCallerName);
- final TextView numberTextView = (TextView) view.findViewById(R.id.conferenceCallerNumber);
- final TextView numberTypeTextView = (TextView) view.findViewById(
- R.id.conferenceCallerNumberType);
- final View endButton = view.findViewById(R.id.conferenceCallerDisconnect);
- final View separateButton = view.findViewById(R.id.conferenceCallerSeparate);
-
- endButton.setVisibility(thisRowCanDisconnect ? View.VISIBLE : View.GONE);
- if (thisRowCanDisconnect) {
- endButton.setOnClickListener(mDisconnectListener);
- } else {
- endButton.setOnClickListener(null);
- }
-
- separateButton.setVisibility(thisRowCanSeparate ? View.VISIBLE : View.GONE);
- if (thisRowCanSeparate) {
- separateButton.setOnClickListener(mSeparateListener);
- } else {
- separateButton.setOnClickListener(null);
- }
-
- DefaultImageRequest imageRequest = (photoUri != null) ? null :
- new DefaultImageRequest(callerName, lookupKey, true /* isCircularPhoto */);
-
- mContactPhotoManager.loadDirectoryPhoto(photoView, photoUri, false, true, imageRequest);
-
- // set the caller name
- nameTextView.setText(preferredName);
-
- // set the caller number in subscript, or make the field disappear.
- if (TextUtils.isEmpty(callerNumber)) {
- numberTextView.setVisibility(View.GONE);
- numberTypeTextView.setVisibility(View.GONE);
- } else {
- numberTextView.setVisibility(View.VISIBLE);
- numberTextView.setText(PhoneNumberUtilsCompat.createTtsSpannable(
- BidiFormatter.getInstance().unicodeWrap(
- callerNumber, TextDirectionHeuristics.LTR)));
- numberTypeTextView.setVisibility(View.VISIBLE);
- numberTypeTextView.setText(callerNumberType);
- }
- }
-
- /**
- * Updates the participant info list which is bound to the ListView. Stores the call and
- * contact info for all entries. The list is sorted alphabetically by participant name.
- *
- * @param conferenceParticipants The calls which make up the conference participants.
- */
- private void updateParticipantInfo(List<Call> conferenceParticipants) {
- final ContactInfoCache cache = ContactInfoCache.getInstance(mContext);
- boolean newParticipantAdded = false;
- HashSet<String> newCallIds = new HashSet<>(conferenceParticipants.size());
-
- // Update or add conference participant info.
- for (Call call : conferenceParticipants) {
- String callId = call.getId();
- newCallIds.add(callId);
- ContactCacheEntry contactCache = cache.getInfo(callId);
- if (contactCache == null) {
- contactCache = ContactInfoCache.buildCacheEntryFromCall(mContext, call,
- call.getState() == Call.State.INCOMING);
- }
-
- if (mParticipantsByCallId.containsKey(callId)) {
- ParticipantInfo participantInfo = mParticipantsByCallId.get(callId);
- participantInfo.setCall(call);
- participantInfo.setContactCacheEntry(contactCache);
- } else {
- newParticipantAdded = true;
- ParticipantInfo participantInfo = new ParticipantInfo(call, contactCache);
- mConferenceParticipants.add(participantInfo);
- mParticipantsByCallId.put(call.getId(), participantInfo);
- }
- }
-
- // Remove any participants that no longer exist.
- Iterator<Map.Entry<String, ParticipantInfo>> it =
- mParticipantsByCallId.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<String, ParticipantInfo> entry = it.next();
- String existingCallId = entry.getKey();
- if (!newCallIds.contains(existingCallId)) {
- ParticipantInfo existingInfo = entry.getValue();
- mConferenceParticipants.remove(existingInfo);
- it.remove();
- }
- }
-
- if (newParticipantAdded) {
- // Sort the list of participants by contact name.
- sortParticipantList();
- }
- notifyDataSetChanged();
- }
-
- /**
- * Sorts the participant list by contact name.
- */
- private void sortParticipantList() {
- Collections.sort(mConferenceParticipants, new Comparator<ParticipantInfo>() {
- public int compare(ParticipantInfo p1, ParticipantInfo p2) {
- // Contact names might be null, so replace with empty string.
- ContactCacheEntry c1 = p1.getContactCacheEntry();
- String p1Name = MoreObjects.firstNonNull(
- ContactDisplayUtils.getPreferredSortName(
- c1.namePrimary,
- c1.nameAlternative,
- mContactsPreferences),
- "");
-
- ContactCacheEntry c2 = p2.getContactCacheEntry();
- String p2Name = MoreObjects.firstNonNull(
- ContactDisplayUtils.getPreferredSortName(
- c2.namePrimary,
- c2.nameAlternative,
- mContactsPreferences),
- "");
-
- return p1Name.compareToIgnoreCase(p2Name);
- }
- });
- }
-}
diff --git a/InCallUI/src/com/android/incallui/ContactInfoCache.java b/InCallUI/src/com/android/incallui/ContactInfoCache.java
deleted file mode 100644
index 9d6fc4627..000000000
--- a/InCallUI/src/com/android/incallui/ContactInfoCache.java
+++ /dev/null
@@ -1,699 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.location.Address;
-import android.media.RingtoneManager;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Looper;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.DisplayNameSources;
-import android.telecom.TelecomManager;
-import android.text.TextUtils;
-import android.util.Pair;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.util.PhoneNumberHelper;
-import com.android.dialer.R;
-import com.android.dialer.calllog.ContactInfo;
-import com.android.dialer.service.CachedNumberLookupService;
-import com.android.dialer.service.CachedNumberLookupService.CachedContactInfo;
-import com.android.dialer.util.MoreStrings;
-import com.android.incallui.Call.LogState;
-import com.android.incallui.service.PhoneNumberService;
-import com.android.incalluibind.ObjectFactory;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.Calendar;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Class responsible for querying Contact Information for Call objects. Can perform asynchronous
- * requests to the Contact Provider for information as well as respond synchronously for any data
- * that it currently has cached from previous queries. This class always gets called from the UI
- * thread so it does not need thread protection.
- */
-public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadCompleteListener {
-
- private static final String TAG = ContactInfoCache.class.getSimpleName();
- private static final int TOKEN_UPDATE_PHOTO_FOR_CALL_STATE = 0;
-
- private final Context mContext;
- private final PhoneNumberService mPhoneNumberService;
- private final CachedNumberLookupService mCachedNumberLookupService;
- private final HashMap<String, ContactCacheEntry> mInfoMap = Maps.newHashMap();
- private final HashMap<String, Set<ContactInfoCacheCallback>> mCallBacks = Maps.newHashMap();
-
- private static ContactInfoCache sCache = null;
-
- private Drawable mDefaultContactPhotoDrawable;
- private Drawable mConferencePhotoDrawable;
- private ContactUtils mContactUtils;
-
- public static synchronized ContactInfoCache getInstance(Context mContext) {
- if (sCache == null) {
- sCache = new ContactInfoCache(mContext.getApplicationContext());
- }
- return sCache;
- }
-
- private ContactInfoCache(Context context) {
- mContext = context;
- mPhoneNumberService = ObjectFactory.newPhoneNumberService(context);
- mCachedNumberLookupService =
- com.android.dialerbind.ObjectFactory.newCachedNumberLookupService();
- mContactUtils = ObjectFactory.getContactUtilsInstance(context);
-
- }
-
- public ContactCacheEntry getInfo(String callId) {
- return mInfoMap.get(callId);
- }
-
- public static ContactCacheEntry buildCacheEntryFromCall(Context context, Call call,
- boolean isIncoming) {
- final ContactCacheEntry entry = new ContactCacheEntry();
-
- // TODO: get rid of caller info.
- final CallerInfo info = CallerInfoUtils.buildCallerInfo(context, call);
- ContactInfoCache.populateCacheEntry(context, info, entry, call.getNumberPresentation(),
- isIncoming);
- return entry;
- }
-
- public void maybeInsertCnapInformationIntoCache(Context context, final Call call,
- final CallerInfo info) {
- if (mCachedNumberLookupService == null || TextUtils.isEmpty(info.cnapName)
- || mInfoMap.get(call.getId()) != null) {
- return;
- }
- final Context applicationContext = context.getApplicationContext();
- Log.i(TAG, "Found contact with CNAP name - inserting into cache");
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- ContactInfo contactInfo = new ContactInfo();
- CachedContactInfo cacheInfo = mCachedNumberLookupService.buildCachedContactInfo(
- contactInfo);
- cacheInfo.setSource(CachedContactInfo.SOURCE_TYPE_CNAP, "CNAP", 0);
- contactInfo.name = info.cnapName;
- contactInfo.number = call.getNumber();
- contactInfo.type = ContactsContract.CommonDataKinds.Phone.TYPE_MAIN;
- try {
- final JSONObject contactRows = new JSONObject().put(Phone.CONTENT_ITEM_TYPE,
- new JSONObject()
- .put(Phone.NUMBER, contactInfo.number)
- .put(Phone.TYPE, Phone.TYPE_MAIN));
- final String jsonString = new JSONObject()
- .put(Contacts.DISPLAY_NAME, contactInfo.name)
- .put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME)
- .put(Contacts.CONTENT_ITEM_TYPE, contactRows).toString();
- cacheInfo.setLookupKey(jsonString);
- } catch (JSONException e) {
- Log.w(TAG, "Creation of lookup key failed when caching CNAP information");
- }
- mCachedNumberLookupService.addContact(applicationContext, cacheInfo);
- return null;
- }
- }.execute();
- }
-
- private class FindInfoCallback implements CallerInfoAsyncQuery.OnQueryCompleteListener {
- private final boolean mIsIncoming;
-
- public FindInfoCallback(boolean isIncoming) {
- mIsIncoming = isIncoming;
- }
-
- @Override
- public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
- findInfoQueryComplete((Call) cookie, callerInfo, mIsIncoming, true);
- }
- }
-
- /**
- * Requests contact data for the Call object passed in.
- * Returns the data through callback. If callback is null, no response is made, however the
- * query is still performed and cached.
- *
- * @param callback The function to call back when the call is found. Can be null.
- */
- public void findInfo(final Call call, final boolean isIncoming,
- ContactInfoCacheCallback callback) {
- Preconditions.checkState(Looper.getMainLooper().getThread() == Thread.currentThread());
- Preconditions.checkNotNull(callback);
-
- final String callId = call.getId();
- final ContactCacheEntry cacheEntry = mInfoMap.get(callId);
- Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);
-
- // If we have a previously obtained intermediate result return that now
- if (cacheEntry != null) {
- Log.d(TAG, "Contact lookup. In memory cache hit; lookup "
- + (callBacks == null ? "complete" : "still running"));
- callback.onContactInfoComplete(callId, cacheEntry);
- // If no other callbacks are in flight, we're done.
- if (callBacks == null) {
- return;
- }
- }
-
- // If the entry already exists, add callback
- if (callBacks != null) {
- callBacks.add(callback);
- return;
- }
- Log.d(TAG, "Contact lookup. In memory cache miss; searching provider.");
- // New lookup
- callBacks = Sets.newHashSet();
- callBacks.add(callback);
- mCallBacks.put(callId, callBacks);
-
- /**
- * Performs a query for caller information.
- * Save any immediate data we get from the query. An asynchronous query may also be made
- * for any data that we do not already have. Some queries, such as those for voicemail and
- * emergency call information, will not perform an additional asynchronous query.
- */
- final CallerInfo callerInfo = CallerInfoUtils.getCallerInfoForCall(
- mContext, call, new FindInfoCallback(isIncoming));
-
- findInfoQueryComplete(call, callerInfo, isIncoming, false);
- }
-
- private void findInfoQueryComplete(Call call, CallerInfo callerInfo, boolean isIncoming,
- boolean didLocalLookup) {
- final String callId = call.getId();
- int presentationMode = call.getNumberPresentation();
- if (callerInfo.contactExists || callerInfo.isEmergencyNumber() ||
- callerInfo.isVoiceMailNumber()) {
- presentationMode = TelecomManager.PRESENTATION_ALLOWED;
- }
-
- ContactCacheEntry cacheEntry = mInfoMap.get(callId);
- // Ensure we always have a cacheEntry. Replace the existing entry if
- // it has no name or if we found a local contact.
- if (cacheEntry == null || TextUtils.isEmpty(cacheEntry.namePrimary) ||
- callerInfo.contactExists) {
- cacheEntry = buildEntry(mContext, callId, callerInfo, presentationMode, isIncoming);
- mInfoMap.put(callId, cacheEntry);
- }
-
- sendInfoNotifications(callId, cacheEntry);
-
- if (didLocalLookup) {
- // Before issuing a request for more data from other services, we only check that the
- // contact wasn't found in the local DB. We don't check the if the cache entry already
- // has a name because we allow overriding cnap data with data from other services.
- if (!callerInfo.contactExists && mPhoneNumberService != null) {
- Log.d(TAG, "Contact lookup. Local contacts miss, checking remote");
- final PhoneNumberServiceListener listener = new PhoneNumberServiceListener(callId);
- mPhoneNumberService.getPhoneNumberInfo(cacheEntry.number, listener, listener,
- isIncoming);
- } else if (cacheEntry.displayPhotoUri != null) {
- Log.d(TAG, "Contact lookup. Local contact found, starting image load");
- // Load the image with a callback to update the image state.
- // When the load is finished, onImageLoadComplete() will be called.
- cacheEntry.isLoadingPhoto = true;
- ContactsAsyncHelper.startObtainPhotoAsync(TOKEN_UPDATE_PHOTO_FOR_CALL_STATE,
- mContext, cacheEntry.displayPhotoUri, ContactInfoCache.this, callId);
- } else {
- if (callerInfo.contactExists) {
- Log.d(TAG, "Contact lookup done. Local contact found, no image.");
- } else {
- Log.d(TAG, "Contact lookup done. Local contact not found and"
- + " no remote lookup service available.");
- }
- clearCallbacks(callId);
- }
- }
- }
-
- class PhoneNumberServiceListener implements PhoneNumberService.NumberLookupListener,
- PhoneNumberService.ImageLookupListener, ContactUtils.Listener {
- private final String mCallId;
-
- PhoneNumberServiceListener(String callId) {
- mCallId = callId;
- }
-
- @Override
- public void onPhoneNumberInfoComplete(
- final PhoneNumberService.PhoneNumberInfo info) {
- // If we got a miss, this is the end of the lookup pipeline,
- // so clear the callbacks and return.
- if (info == null) {
- Log.d(TAG, "Contact lookup done. Remote contact not found.");
- clearCallbacks(mCallId);
- return;
- }
-
- ContactCacheEntry entry = new ContactCacheEntry();
- entry.namePrimary = info.getDisplayName();
- entry.number = info.getNumber();
- entry.contactLookupResult = info.getLookupSource();
- final int type = info.getPhoneType();
- final String label = info.getPhoneLabel();
- if (type == Phone.TYPE_CUSTOM) {
- entry.label = label;
- } else {
- final CharSequence typeStr = Phone.getTypeLabel(
- mContext.getResources(), type, label);
- entry.label = typeStr == null ? null : typeStr.toString();
- }
- final ContactCacheEntry oldEntry = mInfoMap.get(mCallId);
- if (oldEntry != null) {
- // Location is only obtained from local lookup so persist
- // the value for remote lookups. Once we have a name this
- // field is no longer used; it is persisted here in case
- // the UI is ever changed to use it.
- entry.location = oldEntry.location;
- // Contact specific ringtone is obtained from local lookup.
- entry.contactRingtoneUri = oldEntry.contactRingtoneUri;
- }
-
- // If no image and it's a business, switch to using the default business avatar.
- if (info.getImageUrl() == null && info.isBusiness()) {
- Log.d(TAG, "Business has no image. Using default.");
- entry.photo = mContext.getResources().getDrawable(R.drawable.img_business);
- }
-
- mInfoMap.put(mCallId, entry);
- sendInfoNotifications(mCallId, entry);
-
- if (mContactUtils != null) {
- // This method will callback "onContactInteractionsFound".
- entry.isLoadingContactInteractions =
- mContactUtils.retrieveContactInteractionsFromLookupKey(
- info.getLookupKey(), this);
- }
-
- entry.isLoadingPhoto = info.getImageUrl() != null;
-
- // If there is no image or contact interactions then we should not expect another
- // callback.
- if (!entry.isLoadingPhoto && !entry.isLoadingContactInteractions) {
- // We're done, so clear callbacks
- clearCallbacks(mCallId);
- }
- }
-
- @Override
- public void onImageFetchComplete(Bitmap bitmap) {
- onImageLoadComplete(TOKEN_UPDATE_PHOTO_FOR_CALL_STATE, null, bitmap, mCallId);
- }
-
- @Override
- public void onContactInteractionsFound(Address address,
- List<Pair<Calendar, Calendar>> openingHours) {
- final ContactCacheEntry entry = mInfoMap.get(mCallId);
- if (entry == null) {
- Log.e(this, "Contact context received for empty search entry.");
- clearCallbacks(mCallId);
- return;
- }
-
- entry.isLoadingContactInteractions = false;
-
- Log.v(ContactInfoCache.this, "Setting contact interactions for entry: ", entry);
-
- entry.locationAddress = address;
- entry.openingHours = openingHours;
- sendContactInteractionsNotifications(mCallId, entry);
-
- if (!entry.isLoadingPhoto) {
- clearCallbacks(mCallId);
- }
- }
- }
-
- /**
- * Implemented for ContactsAsyncHelper.OnImageLoadCompleteListener interface.
- * make sure that the call state is reflected after the image is loaded.
- */
- @Override
- public void onImageLoadComplete(int token, Drawable photo, Bitmap photoIcon, Object cookie) {
- Log.d(this, "Image load complete with context: ", mContext);
- // TODO: may be nice to update the image view again once the newer one
- // is available on contacts database.
-
- final String callId = (String) cookie;
- final ContactCacheEntry entry = mInfoMap.get(callId);
-
- if (entry == null) {
- Log.e(this, "Image Load received for empty search entry.");
- clearCallbacks(callId);
- return;
- }
-
- entry.isLoadingPhoto = false;
-
- Log.d(this, "setting photo for entry: ", entry);
-
- // Conference call icons are being handled in CallCardPresenter.
- if (photo != null) {
- Log.v(this, "direct drawable: ", photo);
- entry.photo = photo;
- } else if (photoIcon != null) {
- Log.v(this, "photo icon: ", photoIcon);
- entry.photo = new BitmapDrawable(mContext.getResources(), photoIcon);
- } else {
- Log.v(this, "unknown photo");
- entry.photo = null;
- }
-
- sendImageNotifications(callId, entry);
-
- if (!entry.isLoadingContactInteractions) {
- clearCallbacks(callId);
- }
- }
-
- /**
- * Blows away the stored cache values.
- */
- public void clearCache() {
- mInfoMap.clear();
- mCallBacks.clear();
- }
-
- private ContactCacheEntry buildEntry(Context context, String callId,
- CallerInfo info, int presentation, boolean isIncoming) {
- // The actual strings we're going to display onscreen:
- Drawable photo = null;
-
- final ContactCacheEntry cce = new ContactCacheEntry();
- populateCacheEntry(context, info, cce, presentation, isIncoming);
-
- // This will only be true for emergency numbers
- if (info.photoResource != 0) {
- photo = context.getResources().getDrawable(info.photoResource);
- } else if (info.isCachedPhotoCurrent) {
- if (info.cachedPhoto != null) {
- photo = info.cachedPhoto;
- } else {
- photo = getDefaultContactPhotoDrawable();
- }
- } else if (info.contactDisplayPhotoUri == null) {
- photo = getDefaultContactPhotoDrawable();
- } else {
- cce.displayPhotoUri = info.contactDisplayPhotoUri;
- }
-
- // Support any contact id in N because QuickContacts in N starts supporting enterprise
- // contact id
- if (info.lookupKeyOrNull != null
- && (ContactsUtils.FLAG_N_FEATURE || info.contactIdOrZero != 0)) {
- cce.lookupUri = Contacts.getLookupUri(info.contactIdOrZero, info.lookupKeyOrNull);
- } else {
- Log.v(TAG, "lookup key is null or contact ID is 0 on M. Don't create a lookup uri.");
- cce.lookupUri = null;
- }
-
- cce.photo = photo;
- cce.lookupKey = info.lookupKeyOrNull;
- cce.contactRingtoneUri = info.contactRingtoneUri;
- if (cce.contactRingtoneUri == null || cce.contactRingtoneUri == Uri.EMPTY) {
- cce.contactRingtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
- }
-
- return cce;
- }
-
- /**
- * Populate a cache entry from a call (which got converted into a caller info).
- */
- public static void populateCacheEntry(Context context, CallerInfo info, ContactCacheEntry cce,
- int presentation, boolean isIncoming) {
- Preconditions.checkNotNull(info);
- String displayName = null;
- String displayNumber = null;
- String displayLocation = null;
- String label = null;
- boolean isSipCall = false;
-
- // It appears that there is a small change in behaviour with the
- // PhoneUtils' startGetCallerInfo whereby if we query with an
- // empty number, we will get a valid CallerInfo object, but with
- // fields that are all null, and the isTemporary boolean input
- // parameter as true.
-
- // In the past, we would see a NULL callerinfo object, but this
- // ends up causing null pointer exceptions elsewhere down the
- // line in other cases, so we need to make this fix instead. It
- // appears that this was the ONLY call to PhoneUtils
- // .getCallerInfo() that relied on a NULL CallerInfo to indicate
- // an unknown contact.
-
- // Currently, infi.phoneNumber may actually be a SIP address, and
- // if so, it might sometimes include the "sip:" prefix. That
- // prefix isn't really useful to the user, though, so strip it off
- // if present. (For any other URI scheme, though, leave the
- // prefix alone.)
- // TODO: It would be cleaner for CallerInfo to explicitly support
- // SIP addresses instead of overloading the "phoneNumber" field.
- // Then we could remove this hack, and instead ask the CallerInfo
- // for a "user visible" form of the SIP address.
- String number = info.phoneNumber;
-
- if (!TextUtils.isEmpty(number)) {
- isSipCall = PhoneNumberHelper.isUriNumber(number);
- if (number.startsWith("sip:")) {
- number = number.substring(4);
- }
- }
-
- if (TextUtils.isEmpty(info.name)) {
- // No valid "name" in the CallerInfo, so fall back to
- // something else.
- // (Typically, we promote the phone number up to the "name" slot
- // onscreen, and possibly display a descriptive string in the
- // "number" slot.)
- if (TextUtils.isEmpty(number)) {
- // No name *or* number! Display a generic "unknown" string
- // (or potentially some other default based on the presentation.)
- displayName = getPresentationString(context, presentation, info.callSubject);
- Log.d(TAG, " ==> no name *or* number! displayName = " + displayName);
- } else if (presentation != TelecomManager.PRESENTATION_ALLOWED) {
- // This case should never happen since the network should never send a phone #
- // AND a restricted presentation. However we leave it here in case of weird
- // network behavior
- displayName = getPresentationString(context, presentation, info.callSubject);
- Log.d(TAG, " ==> presentation not allowed! displayName = " + displayName);
- } else if (!TextUtils.isEmpty(info.cnapName)) {
- // No name, but we do have a valid CNAP name, so use that.
- displayName = info.cnapName;
- info.name = info.cnapName;
- displayNumber = number;
- Log.d(TAG, " ==> cnapName available: displayName '" + displayName +
- "', displayNumber '" + displayNumber + "'");
- } else {
- // No name; all we have is a number. This is the typical
- // case when an incoming call doesn't match any contact,
- // or if you manually dial an outgoing number using the
- // dialpad.
- displayNumber = number;
-
- // Display a geographical description string if available
- // (but only for incoming calls.)
- if (isIncoming) {
- // TODO (CallerInfoAsyncQuery cleanup): Fix the CallerInfo
- // query to only do the geoDescription lookup in the first
- // place for incoming calls.
- displayLocation = info.geoDescription; // may be null
- Log.d(TAG, "Geodescrption: " + info.geoDescription);
- }
-
- Log.d(TAG, " ==> no name; falling back to number:"
- + " displayNumber '" + Log.pii(displayNumber)
- + "', displayLocation '" + displayLocation + "'");
- }
- } else {
- // We do have a valid "name" in the CallerInfo. Display that
- // in the "name" slot, and the phone number in the "number" slot.
- if (presentation != TelecomManager.PRESENTATION_ALLOWED) {
- // This case should never happen since the network should never send a name
- // AND a restricted presentation. However we leave it here in case of weird
- // network behavior
- displayName = getPresentationString(context, presentation, info.callSubject);
- Log.d(TAG, " ==> valid name, but presentation not allowed!" +
- " displayName = " + displayName);
- } else {
- // Causes cce.namePrimary to be set as info.name below. CallCardPresenter will
- // later determine whether to use the name or nameAlternative when presenting
- displayName = info.name;
- cce.nameAlternative = info.nameAlternative;
- displayNumber = number;
- label = info.phoneLabel;
- Log.d(TAG, " ==> name is present in CallerInfo: displayName '" + displayName
- + "', displayNumber '" + displayNumber + "'");
- }
- }
-
- cce.namePrimary = displayName;
- cce.number = displayNumber;
- cce.location = displayLocation;
- cce.label = label;
- cce.isSipCall = isSipCall;
- cce.userType = info.userType;
-
- if (info.contactExists) {
- cce.contactLookupResult = LogState.LOOKUP_LOCAL_CONTACT;
- }
- }
-
- /**
- * Sends the updated information to call the callbacks for the entry.
- */
- private void sendInfoNotifications(String callId, ContactCacheEntry entry) {
- final Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);
- if (callBacks != null) {
- for (ContactInfoCacheCallback callBack : callBacks) {
- callBack.onContactInfoComplete(callId, entry);
- }
- }
- }
-
- private void sendImageNotifications(String callId, ContactCacheEntry entry) {
- final Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);
- if (callBacks != null && entry.photo != null) {
- for (ContactInfoCacheCallback callBack : callBacks) {
- callBack.onImageLoadComplete(callId, entry);
- }
- }
- }
-
- private void sendContactInteractionsNotifications(String callId, ContactCacheEntry entry) {
- final Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);
- if (callBacks != null) {
- for (ContactInfoCacheCallback callBack : callBacks) {
- callBack.onContactInteractionsInfoComplete(callId, entry);
- }
- }
- }
-
- private void clearCallbacks(String callId) {
- mCallBacks.remove(callId);
- }
-
- /**
- * Gets name strings based on some special presentation modes and the associated custom label.
- */
- private static String getPresentationString(Context context, int presentation,
- String customLabel) {
- String name = context.getString(R.string.unknown);
- if (!TextUtils.isEmpty(customLabel) &&
- ((presentation == TelecomManager.PRESENTATION_UNKNOWN) ||
- (presentation == TelecomManager.PRESENTATION_RESTRICTED))) {
- name = customLabel;
- return name;
- } else {
- if (presentation == TelecomManager.PRESENTATION_RESTRICTED) {
- name = context.getString(R.string.private_num);
- } else if (presentation == TelecomManager.PRESENTATION_PAYPHONE) {
- name = context.getString(R.string.payphone);
- }
- }
- return name;
- }
-
- public Drawable getDefaultContactPhotoDrawable() {
- if (mDefaultContactPhotoDrawable == null) {
- mDefaultContactPhotoDrawable =
- mContext.getResources().getDrawable(R.drawable.img_no_image_automirrored);
- }
- return mDefaultContactPhotoDrawable;
- }
-
- public Drawable getConferenceDrawable() {
- if (mConferencePhotoDrawable == null) {
- mConferencePhotoDrawable =
- mContext.getResources().getDrawable(R.drawable.img_conference_automirrored);
- }
- return mConferencePhotoDrawable;
- }
-
- /**
- * Callback interface for the contact query.
- */
- public interface ContactInfoCacheCallback {
- public void onContactInfoComplete(String callId, ContactCacheEntry entry);
- public void onImageLoadComplete(String callId, ContactCacheEntry entry);
- public void onContactInteractionsInfoComplete(String callId, ContactCacheEntry entry);
- }
-
- public static class ContactCacheEntry {
- public String namePrimary;
- public String nameAlternative;
- public String number;
- public String location;
- public String label;
- public Drawable photo;
- public boolean isSipCall;
- // Note in cache entry whether this is a pending async loading action to know whether to
- // wait for its callback or not.
- public boolean isLoadingPhoto;
- public boolean isLoadingContactInteractions;
- /** This will be used for the "view" notification. */
- public Uri contactUri;
- /** Either a display photo or a thumbnail URI. */
- public Uri displayPhotoUri;
- public Uri lookupUri; // Sent to NotificationMananger
- public String lookupKey;
- public Address locationAddress;
- public List<Pair<Calendar, Calendar>> openingHours;
- public int contactLookupResult = LogState.LOOKUP_NOT_FOUND;
- public long userType = ContactsUtils.USER_TYPE_CURRENT;
- public Uri contactRingtoneUri;
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(this)
- .add("name", MoreStrings.toSafeString(namePrimary))
- .add("nameAlternative", MoreStrings.toSafeString(nameAlternative))
- .add("number", MoreStrings.toSafeString(number))
- .add("location", MoreStrings.toSafeString(location))
- .add("label", label)
- .add("photo", photo)
- .add("isSipCall", isSipCall)
- .add("contactUri", contactUri)
- .add("displayPhotoUri", displayPhotoUri)
- .add("locationAddress", locationAddress)
- .add("openingHours", openingHours)
- .add("contactLookupResult", contactLookupResult)
- .add("userType", userType)
- .add("contactRingtoneUri", contactRingtoneUri)
- .toString();
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/ContactUtils.java b/InCallUI/src/com/android/incallui/ContactUtils.java
deleted file mode 100644
index 0750af731..000000000
--- a/InCallUI/src/com/android/incallui/ContactUtils.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.incallui;
-
-import android.content.Context;
-import android.location.Address;
-import android.util.Pair;
-
-import com.android.incalluibind.ObjectFactory;
-
-import java.util.Calendar;
-import java.util.List;
-
-/**
- * Utility functions to help manipulate contact data.
- */
-public abstract class ContactUtils {
- protected Context mContext;
-
- public static ContactUtils getInstance(Context context) {
- return ObjectFactory.getContactUtilsInstance(context);
- }
-
- protected ContactUtils(Context context) {
- mContext = context;
- }
-
- public interface Listener {
- public void onContactInteractionsFound(Address address,
- List<Pair<Calendar, Calendar>> openingHours);
- }
-
- public abstract boolean retrieveContactInteractionsFromLookupKey(String lookupKey,
- Listener listener);
-}
diff --git a/InCallUI/src/com/android/incallui/ContactsAsyncHelper.java b/InCallUI/src/com/android/incallui/ContactsAsyncHelper.java
deleted file mode 100644
index d959fadd4..000000000
--- a/InCallUI/src/com/android/incallui/ContactsAsyncHelper.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2008 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.incallui;
-
-import android.app.Notification;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.ContactsContract.Contacts;
-
-import com.android.dialer.R;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Helper class for loading contacts photo asynchronously.
- */
-public class ContactsAsyncHelper {
-
- /**
- * Interface for a WorkerHandler result return.
- */
- public interface OnImageLoadCompleteListener {
- /**
- * Called when the image load is complete.
- *
- * @param token Integer passed in {@link ContactsAsyncHelper#startObtainPhotoAsync(int,
- * Context, Uri, OnImageLoadCompleteListener, Object)}.
- * @param photo Drawable object obtained by the async load.
- * @param photoIcon Bitmap object obtained by the async load.
- * @param cookie Object passed in {@link ContactsAsyncHelper#startObtainPhotoAsync(int,
- * Context, Uri, OnImageLoadCompleteListener, Object)}. Can be null iff. the original
- * cookie is null.
- */
- public void onImageLoadComplete(int token, Drawable photo, Bitmap photoIcon,
- Object cookie);
- }
-
- // constants
- private static final int EVENT_LOAD_IMAGE = 1;
-
- private final Handler mResultHandler = new Handler() {
- /** Called when loading is done. */
- @Override
- public void handleMessage(Message msg) {
- WorkerArgs args = (WorkerArgs) msg.obj;
- switch (msg.arg1) {
- case EVENT_LOAD_IMAGE:
- if (args.listener != null) {
- Log.d(this, "Notifying listener: " + args.listener.toString() +
- " image: " + args.displayPhotoUri + " completed");
- args.listener.onImageLoadComplete(msg.what, args.photo, args.photoIcon,
- args.cookie);
- }
- break;
- default:
- }
- }
- };
-
- /** Handler run on a worker thread to load photo asynchronously. */
- private static Handler sThreadHandler;
-
- /** For forcing the system to call its constructor */
- @SuppressWarnings("unused")
- private static ContactsAsyncHelper sInstance;
-
- static {
- sInstance = new ContactsAsyncHelper();
- }
-
- private static final class WorkerArgs {
- public Context context;
- public Uri displayPhotoUri;
- public Drawable photo;
- public Bitmap photoIcon;
- public Object cookie;
- public OnImageLoadCompleteListener listener;
- }
-
- /**
- * Thread worker class that handles the task of opening the stream and loading
- * the images.
- */
- private class WorkerHandler extends Handler {
- public WorkerHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- WorkerArgs args = (WorkerArgs) msg.obj;
-
- switch (msg.arg1) {
- case EVENT_LOAD_IMAGE:
- InputStream inputStream = null;
- try {
- try {
- inputStream = args.context.getContentResolver()
- .openInputStream(args.displayPhotoUri);
- } catch (Exception e) {
- Log.e(this, "Error opening photo input stream", e);
- }
-
- if (inputStream != null) {
- args.photo = Drawable.createFromStream(inputStream,
- args.displayPhotoUri.toString());
-
- // This assumes Drawable coming from contact database is usually
- // BitmapDrawable and thus we can have (down)scaled version of it.
- args.photoIcon = getPhotoIconWhenAppropriate(args.context, args.photo);
-
- Log.d(ContactsAsyncHelper.this, "Loading image: " + msg.arg1 +
- " token: " + msg.what + " image URI: " + args.displayPhotoUri);
- } else {
- args.photo = null;
- args.photoIcon = null;
- Log.d(ContactsAsyncHelper.this, "Problem with image: " + msg.arg1 +
- " token: " + msg.what + " image URI: " + args.displayPhotoUri +
- ", using default image.");
- }
- } finally {
- if (inputStream != null) {
- try {
- inputStream.close();
- } catch (IOException e) {
- Log.e(this, "Unable to close input stream.", e);
- }
- }
- }
- break;
- default:
- }
-
- // send the reply to the enclosing class.
- Message reply = ContactsAsyncHelper.this.mResultHandler.obtainMessage(msg.what);
- reply.arg1 = msg.arg1;
- reply.obj = msg.obj;
- reply.sendToTarget();
- }
-
- /**
- * Returns a Bitmap object suitable for {@link Notification}'s large icon. This might
- * return null when the given Drawable isn't BitmapDrawable, or if the system fails to
- * create a scaled Bitmap for the Drawable.
- */
- private Bitmap getPhotoIconWhenAppropriate(Context context, Drawable photo) {
- if (!(photo instanceof BitmapDrawable)) {
- return null;
- }
- int iconSize = context.getResources()
- .getDimensionPixelSize(R.dimen.notification_icon_size);
- Bitmap orgBitmap = ((BitmapDrawable) photo).getBitmap();
- int orgWidth = orgBitmap.getWidth();
- int orgHeight = orgBitmap.getHeight();
- int longerEdge = orgWidth > orgHeight ? orgWidth : orgHeight;
- // We want downscaled one only when the original icon is too big.
- if (longerEdge > iconSize) {
- float ratio = ((float) longerEdge) / iconSize;
- int newWidth = (int) (orgWidth / ratio);
- int newHeight = (int) (orgHeight / ratio);
- // If the longer edge is much longer than the shorter edge, the latter may
- // become 0 which will cause a crash.
- if (newWidth <= 0 || newHeight <= 0) {
- Log.w(this, "Photo icon's width or height become 0.");
- return null;
- }
-
- // It is sure ratio >= 1.0f in any case and thus the newly created Bitmap
- // should be smaller than the original.
- return Bitmap.createScaledBitmap(orgBitmap, newWidth, newHeight, true);
- } else {
- return orgBitmap;
- }
- }
- }
-
- /**
- * Private constructor for static class
- */
- private ContactsAsyncHelper() {
- HandlerThread thread = new HandlerThread("ContactsAsyncWorker");
- thread.start();
- sThreadHandler = new WorkerHandler(thread.getLooper());
- }
-
- /**
- * Starts an asynchronous image load. After finishing the load,
- * {@link OnImageLoadCompleteListener#onImageLoadComplete(int, Drawable, Bitmap, Object)}
- * will be called.
- *
- * @param token Arbitrary integer which will be returned as the first argument of
- * {@link OnImageLoadCompleteListener#onImageLoadComplete(int, Drawable, Bitmap, Object)}
- * @param context Context object used to do the time-consuming operation.
- * @param displayPhotoUri Uri to be used to fetch the photo
- * @param listener Callback object which will be used when the asynchronous load is done.
- * Can be null, which means only the asynchronous load is done while there's no way to
- * obtain the loaded photos.
- * @param cookie Arbitrary object the caller wants to remember, which will become the
- * fourth argument of {@link OnImageLoadCompleteListener#onImageLoadComplete(int, Drawable,
- * Bitmap, Object)}. Can be null, at which the callback will also has null for the argument.
- */
- public static final void startObtainPhotoAsync(int token, Context context, Uri displayPhotoUri,
- OnImageLoadCompleteListener listener, Object cookie) {
- // in case the source caller info is null, the URI will be null as well.
- // just update using the placeholder image in this case.
- if (displayPhotoUri == null) {
- Log.wtf("startObjectPhotoAsync", "Uri is missing");
- return;
- }
-
- // Added additional Cookie field in the callee to handle arguments
- // sent to the callback function.
-
- // setup arguments
- WorkerArgs args = new WorkerArgs();
- args.cookie = cookie;
- args.context = context;
- args.displayPhotoUri = displayPhotoUri;
- args.listener = listener;
-
- // setup message arguments
- Message msg = sThreadHandler.obtainMessage(token);
- msg.arg1 = EVENT_LOAD_IMAGE;
- msg.obj = args;
-
- Log.d("startObjectPhotoAsync", "Begin loading image: " + args.displayPhotoUri +
- ", displaying default image for now.");
-
- // notify the thread to begin working
- sThreadHandler.sendMessage(msg);
- }
-
-
-}
diff --git a/InCallUI/src/com/android/incallui/ContactsPreferencesFactory.java b/InCallUI/src/com/android/incallui/ContactsPreferencesFactory.java
deleted file mode 100644
index a9cc93bda..000000000
--- a/InCallUI/src/com/android/incallui/ContactsPreferencesFactory.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.incallui;
-
-import android.content.Context;
-import android.support.annotation.Nullable;
-import com.android.dialer.compat.UserManagerCompat;
-
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.contacts.common.testing.NeededForTesting;
-
-/**
- * Factory class for {@link ContactsPreferences}.
- */
-public class ContactsPreferencesFactory {
-
- private static boolean sUseTestInstance;
- private static ContactsPreferences sTestInstance;
-
- /**
- * Creates a new {@link ContactsPreferences} object if possible.
- *
- * @param context the context to use when creating the ContactsPreferences.
- * @return a new ContactsPreferences object or {@code null} if the user is locked.
- */
- @Nullable
- public static ContactsPreferences newContactsPreferences(Context context) {
- if (sUseTestInstance) {
- return sTestInstance;
- }
- if (UserManagerCompat.isUserUnlocked(context)) {
- return new ContactsPreferences(context);
- }
- return null;
- }
-
- /**
- * Sets the instance to be returned by all calls to {@link #newContactsPreferences(Context)}.
- *
- * @param testInstance the instance to return.
- */
- @NeededForTesting
- static void setTestInstance(ContactsPreferences testInstance) {
- sUseTestInstance = true;
- sTestInstance = testInstance;
- }
-}
diff --git a/InCallUI/src/com/android/incallui/DialpadFragment.java b/InCallUI/src/com/android/incallui/DialpadFragment.java
deleted file mode 100644
index ad288bdc6..000000000
--- a/InCallUI/src/com/android/incallui/DialpadFragment.java
+++ /dev/null
@@ -1,563 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.text.Editable;
-import android.text.method.DialerKeyListener;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
-import com.android.dialer.R;
-import com.android.phone.common.dialpad.DialpadKeyButton;
-import com.android.phone.common.dialpad.DialpadView;
-
-import java.util.HashMap;
-
-/**
- * Fragment for call control buttons
- */
-public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadPresenter.DialpadUi>
- implements DialpadPresenter.DialpadUi, View.OnTouchListener, View.OnKeyListener,
- View.OnHoverListener, View.OnClickListener {
-
- private static final int ACCESSIBILITY_DTMF_STOP_DELAY_MILLIS = 50;
-
- private final int[] mButtonIds = new int[] {R.id.zero, R.id.one, R.id.two, R.id.three,
- R.id.four, R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine, R.id.star,
- R.id.pound};
-
- /**
- * LinearLayout with getter and setter methods for the translationY property using floats,
- * for animation purposes.
- */
- public static class DialpadSlidingLinearLayout extends LinearLayout {
-
- public DialpadSlidingLinearLayout(Context context) {
- super(context);
- }
-
- public DialpadSlidingLinearLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public DialpadSlidingLinearLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public float getYFraction() {
- final int height = getHeight();
- if (height == 0) return 0;
- return getTranslationY() / height;
- }
-
- public void setYFraction(float yFraction) {
- setTranslationY(yFraction * getHeight());
- }
- }
-
- private EditText mDtmfDialerField;
-
- /** Hash Map to map a view id to a character*/
- private static final HashMap<Integer, Character> mDisplayMap =
- new HashMap<Integer, Character>();
-
- private static final Handler sHandler = new Handler(Looper.getMainLooper());
-
-
- /** Set up the static maps*/
- static {
- // Map the buttons to the display characters
- mDisplayMap.put(R.id.one, '1');
- mDisplayMap.put(R.id.two, '2');
- mDisplayMap.put(R.id.three, '3');
- mDisplayMap.put(R.id.four, '4');
- mDisplayMap.put(R.id.five, '5');
- mDisplayMap.put(R.id.six, '6');
- mDisplayMap.put(R.id.seven, '7');
- mDisplayMap.put(R.id.eight, '8');
- mDisplayMap.put(R.id.nine, '9');
- mDisplayMap.put(R.id.zero, '0');
- mDisplayMap.put(R.id.pound, '#');
- mDisplayMap.put(R.id.star, '*');
- }
-
- // KeyListener used with the "dialpad digits" EditText widget.
- private DTMFKeyListener mDialerKeyListener;
-
- private DialpadView mDialpadView;
-
- private int mCurrentTextColor;
-
- /**
- * Our own key listener, specialized for dealing with DTMF codes.
- * 1. Ignore the backspace since it is irrelevant.
- * 2. Allow ONLY valid DTMF characters to generate a tone and be
- * sent as a DTMF code.
- * 3. All other remaining characters are handled by the superclass.
- *
- * This code is purely here to handle events from the hardware keyboard
- * while the DTMF dialpad is up.
- */
- private class DTMFKeyListener extends DialerKeyListener {
-
- private DTMFKeyListener() {
- super();
- }
-
- /**
- * Overriden to return correct DTMF-dialable characters.
- */
- @Override
- protected char[] getAcceptedChars(){
- return DTMF_CHARACTERS;
- }
-
- /** special key listener ignores backspace. */
- @Override
- public boolean backspace(View view, Editable content, int keyCode,
- KeyEvent event) {
- return false;
- }
-
- /**
- * Return true if the keyCode is an accepted modifier key for the
- * dialer (ALT or SHIFT).
- */
- private boolean isAcceptableModifierKey(int keyCode) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_ALT_LEFT:
- case KeyEvent.KEYCODE_ALT_RIGHT:
- case KeyEvent.KEYCODE_SHIFT_LEFT:
- case KeyEvent.KEYCODE_SHIFT_RIGHT:
- return true;
- default:
- return false;
- }
- }
-
- /**
- * Overriden so that with each valid button press, we start sending
- * a dtmf code and play a local dtmf tone.
- */
- @Override
- public boolean onKeyDown(View view, Editable content,
- int keyCode, KeyEvent event) {
- // if (DBG) log("DTMFKeyListener.onKeyDown, keyCode " + keyCode + ", view " + view);
-
- // find the character
- char c = (char) lookup(event, content);
-
- // if not a long press, and parent onKeyDown accepts the input
- if (event.getRepeatCount() == 0 && super.onKeyDown(view, content, keyCode, event)) {
-
- boolean keyOK = ok(getAcceptedChars(), c);
-
- // if the character is a valid dtmf code, start playing the tone and send the
- // code.
- if (keyOK) {
- Log.d(this, "DTMFKeyListener reading '" + c + "' from input.");
- getPresenter().processDtmf(c);
- } else {
- Log.d(this, "DTMFKeyListener rejecting '" + c + "' from input.");
- }
- return true;
- }
- return false;
- }
-
- /**
- * Overriden so that with each valid button up, we stop sending
- * a dtmf code and the dtmf tone.
- */
- @Override
- public boolean onKeyUp(View view, Editable content,
- int keyCode, KeyEvent event) {
- // if (DBG) log("DTMFKeyListener.onKeyUp, keyCode " + keyCode + ", view " + view);
-
- super.onKeyUp(view, content, keyCode, event);
-
- // find the character
- char c = (char) lookup(event, content);
-
- boolean keyOK = ok(getAcceptedChars(), c);
-
- if (keyOK) {
- Log.d(this, "Stopping the tone for '" + c + "'");
- getPresenter().stopDtmf();
- return true;
- }
-
- return false;
- }
-
- /**
- * Handle individual keydown events when we DO NOT have an Editable handy.
- */
- public boolean onKeyDown(KeyEvent event) {
- char c = lookup(event);
- Log.d(this, "DTMFKeyListener.onKeyDown: event '" + c + "'");
-
- // if not a long press, and parent onKeyDown accepts the input
- if (event.getRepeatCount() == 0 && c != 0) {
- // if the character is a valid dtmf code, start playing the tone and send the
- // code.
- if (ok(getAcceptedChars(), c)) {
- Log.d(this, "DTMFKeyListener reading '" + c + "' from input.");
- getPresenter().processDtmf(c);
- return true;
- } else {
- Log.d(this, "DTMFKeyListener rejecting '" + c + "' from input.");
- }
- }
- return false;
- }
-
- /**
- * Handle individual keyup events.
- *
- * @param event is the event we are trying to stop. If this is null,
- * then we just force-stop the last tone without checking if the event
- * is an acceptable dialer event.
- */
- public boolean onKeyUp(KeyEvent event) {
- if (event == null) {
- //the below piece of code sends stopDTMF event unnecessarily even when a null event
- //is received, hence commenting it.
- /*if (DBG) log("Stopping the last played tone.");
- stopTone();*/
- return true;
- }
-
- char c = lookup(event);
- Log.d(this, "DTMFKeyListener.onKeyUp: event '" + c + "'");
-
- // TODO: stopTone does not take in character input, we may want to
- // consider checking for this ourselves.
- if (ok(getAcceptedChars(), c)) {
- Log.d(this, "Stopping the tone for '" + c + "'");
- getPresenter().stopDtmf();
- return true;
- }
-
- return false;
- }
-
- /**
- * Find the Dialer Key mapped to this event.
- *
- * @return The char value of the input event, otherwise
- * 0 if no matching character was found.
- */
- private char lookup(KeyEvent event) {
- // This code is similar to {@link DialerKeyListener#lookup(KeyEvent, Spannable) lookup}
- int meta = event.getMetaState();
- int number = event.getNumber();
-
- if (!((meta & (KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)) == 0) || (number == 0)) {
- int match = event.getMatch(getAcceptedChars(), meta);
- number = (match != 0) ? match : number;
- }
-
- return (char) number;
- }
-
- /**
- * Check to see if the keyEvent is dialable.
- */
- boolean isKeyEventAcceptable (KeyEvent event) {
- return (ok(getAcceptedChars(), lookup(event)));
- }
-
- /**
- * Overrides the characters used in {@link DialerKeyListener#CHARACTERS}
- * These are the valid dtmf characters.
- */
- public final char[] DTMF_CHARACTERS = new char[] {
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '#', '*'
- };
- }
-
- @Override
- public void onClick(View v) {
- final AccessibilityManager accessibilityManager = (AccessibilityManager)
- v.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- // When accessibility is on, simulate press and release to preserve the
- // semantic meaning of performClick(). Required for Braille support.
- if (accessibilityManager.isEnabled()) {
- final int id = v.getId();
- // Checking the press state prevents double activation.
- if (!v.isPressed() && mDisplayMap.containsKey(id)) {
- getPresenter().processDtmf(mDisplayMap.get(id));
- sHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- getPresenter().stopDtmf();
- }
- }, ACCESSIBILITY_DTMF_STOP_DELAY_MILLIS);
- }
- }
- if (v.getId() == R.id.dialpad_back) {
- getActivity().onBackPressed();
- }
- }
-
- @Override
- public boolean onHover(View v, MotionEvent event) {
- // When touch exploration is turned on, lifting a finger while inside
- // the button's hover target bounds should perform a click action.
- final AccessibilityManager accessibilityManager = (AccessibilityManager)
- v.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-
- if (accessibilityManager.isEnabled()
- && accessibilityManager.isTouchExplorationEnabled()) {
- final int left = v.getPaddingLeft();
- final int right = (v.getWidth() - v.getPaddingRight());
- final int top = v.getPaddingTop();
- final int bottom = (v.getHeight() - v.getPaddingBottom());
-
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_HOVER_ENTER:
- // Lift-to-type temporarily disables double-tap activation.
- v.setClickable(false);
- break;
- case MotionEvent.ACTION_HOVER_EXIT:
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- if ((x > left) && (x < right) && (y > top) && (y < bottom)) {
- v.performClick();
- }
- v.setClickable(true);
- break;
- }
- }
-
- return false;
- }
-
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- Log.d(this, "onKey: keyCode " + keyCode + ", view " + v);
-
- if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
- int viewId = v.getId();
- if (mDisplayMap.containsKey(viewId)) {
- switch (event.getAction()) {
- case KeyEvent.ACTION_DOWN:
- if (event.getRepeatCount() == 0) {
- getPresenter().processDtmf(mDisplayMap.get(viewId));
- }
- break;
- case KeyEvent.ACTION_UP:
- getPresenter().stopDtmf();
- break;
- }
- // do not return true [handled] here, since we want the
- // press / click animation to be handled by the framework.
- }
- }
- return false;
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- Log.d(this, "onTouch");
- int viewId = v.getId();
-
- // if the button is recognized
- if (mDisplayMap.containsKey(viewId)) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- // Append the character mapped to this button, to the display.
- // start the tone
- getPresenter().processDtmf(mDisplayMap.get(viewId));
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- // stop the tone on ANY other event, except for MOVE.
- getPresenter().stopDtmf();
- break;
- }
- // do not return true [handled] here, since we want the
- // press / click animation to be handled by the framework.
- }
- return false;
- }
-
- // TODO(klp) Adds hardware keyboard listener
-
- @Override
- public DialpadPresenter createPresenter() {
- return new DialpadPresenter();
- }
-
- @Override
- public DialpadPresenter.DialpadUi getUi() {
- return this;
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- final View parent = inflater.inflate(
- R.layout.incall_dialpad_fragment, container, false);
- mDialpadView = (DialpadView) parent.findViewById(R.id.dialpad_view);
- mDialpadView.setCanDigitsBeEdited(false);
- mDialpadView.setBackgroundResource(R.color.incall_dialpad_background);
- mDtmfDialerField = (EditText) parent.findViewById(R.id.digits);
- if (mDtmfDialerField != null) {
- mDialerKeyListener = new DTMFKeyListener();
- mDtmfDialerField.setKeyListener(mDialerKeyListener);
- // remove the long-press context menus that support
- // the edit (copy / paste / select) functions.
- mDtmfDialerField.setLongClickable(false);
- mDtmfDialerField.setElegantTextHeight(false);
- configureKeypadListeners();
- }
- View backButton = mDialpadView.findViewById(R.id.dialpad_back);
- backButton.setVisibility(View.VISIBLE);
- backButton.setOnClickListener(this);
-
- return parent;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- updateColors();
- }
-
- public void updateColors() {
- int textColor = InCallPresenter.getInstance().getThemeColors().mPrimaryColor;
-
- if (mCurrentTextColor == textColor) {
- return;
- }
-
- DialpadKeyButton dialpadKey;
- for (int i = 0; i < mButtonIds.length; i++) {
- dialpadKey = (DialpadKeyButton) mDialpadView.findViewById(mButtonIds[i]);
- ((TextView) dialpadKey.findViewById(R.id.dialpad_key_number)).setTextColor(textColor);
- }
-
- mCurrentTextColor = textColor;
- }
-
- @Override
- public void onDestroyView() {
- mDialerKeyListener = null;
- super.onDestroyView();
- }
-
- /**
- * Getter for Dialpad text.
- *
- * @return String containing current Dialpad EditText text.
- */
- public String getDtmfText() {
- return mDtmfDialerField.getText().toString();
- }
-
- /**
- * Sets the Dialpad text field with some text.
- *
- * @param text Text to set Dialpad EditText to.
- */
- public void setDtmfText(String text) {
- mDtmfDialerField.setText(PhoneNumberUtilsCompat.createTtsSpannable(text));
- }
-
- @Override
- public void setVisible(boolean on) {
- if (on) {
- getView().setVisibility(View.VISIBLE);
- } else {
- getView().setVisibility(View.INVISIBLE);
- }
- }
-
- /**
- * Starts the slide up animation for the Dialpad keys when the Dialpad is revealed.
- */
- public void animateShowDialpad() {
- final DialpadView dialpadView = (DialpadView) getView().findViewById(R.id.dialpad_view);
- dialpadView.animateShow();
- }
-
- @Override
- public void appendDigitsToField(char digit) {
- if (mDtmfDialerField != null) {
- // TODO: maybe *don't* manually append this digit if
- // mDialpadDigits is focused and this key came from the HW
- // keyboard, since in that case the EditText field will
- // get the key event directly and automatically appends
- // whetever the user types.
- // (Or, a cleaner fix would be to just make mDialpadDigits
- // *not* handle HW key presses. That seems to be more
- // complicated than just setting focusable="false" on it,
- // though.)
- mDtmfDialerField.getText().append(digit);
- }
- }
-
- /**
- * Called externally (from InCallScreen) to play a DTMF Tone.
- */
- /* package */ boolean onDialerKeyDown(KeyEvent event) {
- Log.d(this, "Notifying dtmf key down.");
- if (mDialerKeyListener != null) {
- return mDialerKeyListener.onKeyDown(event);
- } else {
- return false;
- }
- }
-
- /**
- * Called externally (from InCallScreen) to cancel the last DTMF Tone played.
- */
- public boolean onDialerKeyUp(KeyEvent event) {
- Log.d(this, "Notifying dtmf key up.");
- if (mDialerKeyListener != null) {
- return mDialerKeyListener.onKeyUp(event);
- } else {
- return false;
- }
- }
-
- private void configureKeypadListeners() {
- DialpadKeyButton dialpadKey;
- for (int i = 0; i < mButtonIds.length; i++) {
- dialpadKey = (DialpadKeyButton) mDialpadView.findViewById(mButtonIds[i]);
- dialpadKey.setOnTouchListener(this);
- dialpadKey.setOnKeyListener(this);
- dialpadKey.setOnHoverListener(this);
- dialpadKey.setOnClickListener(this);
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/DialpadPresenter.java b/InCallUI/src/com/android/incallui/DialpadPresenter.java
deleted file mode 100644
index 5e24bedef..000000000
--- a/InCallUI/src/com/android/incallui/DialpadPresenter.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.telephony.PhoneNumberUtils;
-
-/**
- * Logic for call buttons.
- */
-public class DialpadPresenter extends Presenter<DialpadPresenter.DialpadUi>
- implements InCallPresenter.InCallStateListener {
-
- private Call mCall;
-
- @Override
- public void onUiReady(DialpadUi ui) {
- super.onUiReady(ui);
- InCallPresenter.getInstance().addListener(this);
- mCall = CallList.getInstance().getOutgoingOrActive();
- }
-
- @Override
- public void onUiUnready(DialpadUi ui) {
- super.onUiUnready(ui);
- InCallPresenter.getInstance().removeListener(this);
- }
-
- @Override
- public void onStateChange(InCallPresenter.InCallState oldState,
- InCallPresenter.InCallState newState, CallList callList) {
- mCall = callList.getOutgoingOrActive();
- Log.d(this, "DialpadPresenter mCall = " + mCall);
- }
-
- /**
- * Processes the specified digit as a DTMF key, by playing the
- * appropriate DTMF tone, and appending the digit to the EditText
- * field that displays the DTMF digits sent so far.
- *
- */
- public final void processDtmf(char c) {
- Log.d(this, "Processing dtmf key " + c);
- // if it is a valid key, then update the display and send the dtmf tone.
- if (PhoneNumberUtils.is12Key(c) && mCall != null) {
- Log.d(this, "updating display and sending dtmf tone for '" + c + "'");
-
- // Append this key to the "digits" widget.
- getUi().appendDigitsToField(c);
- // Plays the tone through Telecom.
- TelecomAdapter.getInstance().playDtmfTone(mCall.getId(), c);
- } else {
- Log.d(this, "ignoring dtmf request for '" + c + "'");
- }
- }
-
- /**
- * Stops the local tone based on the phone type.
- */
- public void stopDtmf() {
- if (mCall != null) {
- Log.d(this, "stopping remote tone");
- TelecomAdapter.getInstance().stopDtmfTone(mCall.getId());
- }
- }
-
- public interface DialpadUi extends Ui {
- void setVisible(boolean on);
- void appendDigitsToField(char digit);
- }
-}
diff --git a/InCallUI/src/com/android/incallui/DistanceHelper.java b/InCallUI/src/com/android/incallui/DistanceHelper.java
deleted file mode 100644
index a4db5fed3..000000000
--- a/InCallUI/src/com/android/incallui/DistanceHelper.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.incallui;
-
-import android.location.Address;
-
-/**
- * Superclass for a helper class to get the current location and distance to other locations.
- */
-public abstract class DistanceHelper {
- public static final float DISTANCE_NOT_FOUND = -1;
- public static final float MILES_PER_METER = (float) 0.000621371192;
- public static final float KILOMETERS_PER_METER = (float) 0.001;
-
- public interface Listener {
- public void onLocationReady();
- }
-
- public void cleanUp() {}
-
- public float calculateDistance(Address address) {
- return DISTANCE_NOT_FOUND;
- }
-}
diff --git a/InCallUI/src/com/android/incallui/ExternalCallList.java b/InCallUI/src/com/android/incallui/ExternalCallList.java
deleted file mode 100644
index 06e0bb975..000000000
--- a/InCallUI/src/com/android/incallui/ExternalCallList.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.android.incallui;
-
-import com.google.common.base.Preconditions;
-
-import com.android.contacts.common.compat.CallSdkCompat;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.telecom.Call;
-import android.util.ArraySet;
-
-import java.util.Collections;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Tracks the external calls known to the InCall UI.
- *
- * External calls are those with {@link android.telecom.Call.Details#PROPERTY_IS_EXTERNAL_CALL}.
- */
-public class ExternalCallList {
-
- public interface ExternalCallListener {
- void onExternalCallAdded(Call call);
- void onExternalCallRemoved(Call call);
- void onExternalCallUpdated(Call call);
- }
-
- /**
- * Handles {@link android.telecom.Call.Callback} callbacks.
- */
- private final Call.Callback mTelecomCallCallback = new Call.Callback() {
- @Override
- public void onDetailsChanged(Call call, Call.Details details) {
- notifyExternalCallUpdated(call);
- }
- };
-
- private final Set<Call> mExternalCalls = new ArraySet<>();
- private final Set<ExternalCallListener> mExternalCallListeners = Collections.newSetFromMap(
- new ConcurrentHashMap<ExternalCallListener, Boolean>(8, 0.9f, 1));
-
- /**
- * Begins tracking an external call and notifies listeners of the new call.
- */
- public void onCallAdded(Call telecomCall) {
- Preconditions.checkArgument(telecomCall.getDetails()
- .hasProperty(CallSdkCompat.Details.PROPERTY_IS_EXTERNAL_CALL));
- mExternalCalls.add(telecomCall);
- telecomCall.registerCallback(mTelecomCallCallback, new Handler(Looper.getMainLooper()));
- notifyExternalCallAdded(telecomCall);
- }
-
- /**
- * Stops tracking an external call and notifies listeners of the removal of the call.
- */
- public void onCallRemoved(Call telecomCall) {
- Preconditions.checkArgument(mExternalCalls.contains(telecomCall));
- mExternalCalls.remove(telecomCall);
- telecomCall.unregisterCallback(mTelecomCallCallback);
- notifyExternalCallRemoved(telecomCall);
- }
-
- /**
- * Adds a new listener to external call events.
- */
- public void addExternalCallListener(ExternalCallListener listener) {
- mExternalCallListeners.add(Preconditions.checkNotNull(listener));
- }
-
- /**
- * Removes a listener to external call events.
- */
- public void removeExternalCallListener(ExternalCallListener listener) {
- Preconditions.checkArgument(mExternalCallListeners.contains(listener));
- mExternalCallListeners.remove(Preconditions.checkNotNull(listener));
- }
-
- /**
- * Notifies listeners of the addition of a new external call.
- */
- private void notifyExternalCallAdded(Call call) {
- for (ExternalCallListener listener : mExternalCallListeners) {
- listener.onExternalCallAdded(call);
- }
- }
-
- /**
- * Notifies listeners of the removal of an external call.
- */
- private void notifyExternalCallRemoved(Call call) {
- for (ExternalCallListener listener : mExternalCallListeners) {
- listener.onExternalCallRemoved(call);
- }
- }
-
- /**
- * Notifies listeners of changes to an external call.
- */
- private void notifyExternalCallUpdated(Call call) {
- for (ExternalCallListener listener : mExternalCallListeners) {
- listener.onExternalCallUpdated(call);
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/ExternalCallNotifier.java b/InCallUI/src/com/android/incallui/ExternalCallNotifier.java
deleted file mode 100644
index 639a46da0..000000000
--- a/InCallUI/src/com/android/incallui/ExternalCallNotifier.java
+++ /dev/null
@@ -1,406 +0,0 @@
-/*
- * 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.incallui;
-
-import com.google.common.base.Preconditions;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.compat.CallSdkCompat;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.contacts.common.util.BitmapUtil;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.R;
-import com.android.incallui.util.TelecomCallUtil;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
-import android.net.Uri;
-import android.support.annotation.Nullable;
-import android.telecom.Call;
-import android.telecom.PhoneAccount;
-import android.text.BidiFormatter;
-import android.text.TextDirectionHeuristics;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-
-import java.util.Map;
-
-/**
- * Handles the display of notifications for "external calls".
- *
- * External calls are a representation of a call which is in progress on the user's other device
- * (e.g. another phone, or a watch).
- */
-public class ExternalCallNotifier implements ExternalCallList.ExternalCallListener {
-
- /**
- * Tag used with the notification manager to uniquely identify external call notifications.
- */
- private static final String NOTIFICATION_TAG = "EXTERNAL_CALL";
-
- /**
- * Represents a call and associated cached notification data.
- */
- private static class NotificationInfo {
- private final Call mCall;
- private final int mNotificationId;
- @Nullable private String mContentTitle;
- @Nullable private Bitmap mLargeIcon;
- @Nullable private String mPersonReference;
-
- public NotificationInfo(Call call, int notificationId) {
- Preconditions.checkNotNull(call);
- mCall = call;
- mNotificationId = notificationId;
- }
-
- public Call getCall() {
- return mCall;
- }
-
- public int getNotificationId() {
- return mNotificationId;
- }
-
- public @Nullable String getContentTitle() {
- return mContentTitle;
- }
-
- public @Nullable Bitmap getLargeIcon() {
- return mLargeIcon;
- }
-
- public @Nullable String getPersonReference() {
- return mPersonReference;
- }
-
- public void setContentTitle(@Nullable String contentTitle) {
- mContentTitle = contentTitle;
- }
-
- public void setLargeIcon(@Nullable Bitmap largeIcon) {
- mLargeIcon = largeIcon;
- }
-
- public void setPersonReference(@Nullable String personReference) {
- mPersonReference = personReference;
- }
- }
-
- private final Context mContext;
- private final ContactInfoCache mContactInfoCache;
- private Map<Call, NotificationInfo> mNotifications = new ArrayMap<>();
- private int mNextUniqueNotificationId;
- private ContactsPreferences mContactsPreferences;
-
- /**
- * Initializes a new instance of the external call notifier.
- */
- public ExternalCallNotifier(Context context, ContactInfoCache contactInfoCache) {
- mContext = Preconditions.checkNotNull(context);
- mContactsPreferences = ContactsPreferencesFactory.newContactsPreferences(mContext);
- mContactInfoCache = Preconditions.checkNotNull(contactInfoCache);
- }
-
- /**
- * Handles the addition of a new external call by showing a new notification.
- * Triggered by {@link CallList#onCallAdded(android.telecom.Call)}.
- */
- @Override
- public void onExternalCallAdded(android.telecom.Call call) {
- Log.i(this, "onExternalCallAdded " + call);
- Preconditions.checkArgument(!mNotifications.containsKey(call));
- NotificationInfo info = new NotificationInfo(call, mNextUniqueNotificationId++);
- mNotifications.put(call, info);
-
- showNotifcation(info);
- }
-
- /**
- * Handles the removal of an external call by hiding its associated notification.
- * Triggered by {@link CallList#onCallRemoved(android.telecom.Call)}.
- */
- @Override
- public void onExternalCallRemoved(android.telecom.Call call) {
- Log.i(this, "onExternalCallRemoved " + call);
-
- dismissNotification(call);
- }
-
- /**
- * Handles updates to an external call.
- */
- @Override
- public void onExternalCallUpdated(Call call) {
- Preconditions.checkArgument(mNotifications.containsKey(call));
- postNotification(mNotifications.get(call));
- }
-
- /**
- * Initiates a call pull given a notification ID.
- *
- * @param notificationId The notification ID associated with the external call which is to be
- * pulled.
- */
- public void pullExternalCall(int notificationId) {
- for (NotificationInfo info : mNotifications.values()) {
- if (info.getNotificationId() == notificationId) {
- CallSdkCompat.pullExternalCall(info.getCall());
- return;
- }
- }
- }
-
- /**
- * Shows a notification for a new external call. Performs a contact cache lookup to find any
- * associated photo and information for the call.
- */
- private void showNotifcation(final NotificationInfo info) {
- // We make a call to the contact info cache to query for supplemental data to what the
- // call provides. This includes the contact name and photo.
- // This callback will always get called immediately and synchronously with whatever data
- // it has available, and may make a subsequent call later (same thread) if it had to
- // call into the contacts provider for more data.
- com.android.incallui.Call incallCall = new com.android.incallui.Call(info.getCall(),
- new LatencyReport(), false /* registerCallback */);
-
- mContactInfoCache.findInfo(incallCall, false /* isIncoming */,
- new ContactInfoCache.ContactInfoCacheCallback() {
- @Override
- public void onContactInfoComplete(String callId,
- ContactInfoCache.ContactCacheEntry entry) {
-
- // Ensure notification still exists as the external call could have been
- // removed during async contact info lookup.
- if (mNotifications.containsKey(info.getCall())) {
- saveContactInfo(info, entry);
- }
- }
-
- @Override
- public void onImageLoadComplete(String callId,
- ContactInfoCache.ContactCacheEntry entry) {
-
- // Ensure notification still exists as the external call could have been
- // removed during async contact info lookup.
- if (mNotifications.containsKey(info.getCall())) {
- savePhoto(info, entry);
- }
- }
-
- @Override
- public void onContactInteractionsInfoComplete(String callId,
- ContactInfoCache.ContactCacheEntry entry) {
- }
- });
- }
-
- /**
- * Dismisses a notification for an external call.
- */
- private void dismissNotification(Call call) {
- Preconditions.checkArgument(mNotifications.containsKey(call));
-
- NotificationManager notificationManager =
- (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancel(NOTIFICATION_TAG, mNotifications.get(call).getNotificationId());
-
- mNotifications.remove(call);
- }
-
- /**
- * Attempts to build a large icon to use for the notification based on the contact info and
- * post the updated notification to the notification manager.
- */
- private void savePhoto(NotificationInfo info, ContactInfoCache.ContactCacheEntry entry) {
- Bitmap largeIcon = getLargeIconToDisplay(mContext, entry, info.getCall());
- if (largeIcon != null) {
- largeIcon = getRoundedIcon(mContext, largeIcon);
- }
- info.setLargeIcon(largeIcon);
- postNotification(info);
- }
-
- /**
- * Builds and stores the contact information the notification will display and posts the updated
- * notification to the notification manager.
- */
- private void saveContactInfo(NotificationInfo info, ContactInfoCache.ContactCacheEntry entry) {
- info.setContentTitle(getContentTitle(mContext, mContactsPreferences,
- entry, info.getCall()));
- info.setPersonReference(getPersonReference(entry, info.getCall()));
- postNotification(info);
- }
-
- /**
- * Rebuild an existing or show a new notification given {@link NotificationInfo}.
- */
- private void postNotification(NotificationInfo info) {
- Log.i(this, "postNotification : " + info.getContentTitle());
- Notification.Builder builder = new Notification.Builder(mContext);
- // Set notification as ongoing since calls are long-running versus a point-in-time notice.
- builder.setOngoing(true);
- // Make the notification prioritized over the other normal notifications.
- builder.setPriority(Notification.PRIORITY_HIGH);
- // Set the content ("Ongoing call on another device")
- builder.setContentText(mContext.getString(R.string.notification_external_call));
- builder.setSmallIcon(R.drawable.ic_call_white_24dp);
- builder.setContentTitle(info.getContentTitle());
- builder.setLargeIcon(info.getLargeIcon());
- builder.setColor(mContext.getResources().getColor(R.color.dialer_theme_color));
- builder.addPerson(info.getPersonReference());
-
- // Where the external call supports being transferred to the local device, add an action
- // to the notification to initiate the call pull process.
- if ((info.getCall().getDetails().getCallCapabilities()
- & CallSdkCompat.Details.CAPABILITY_CAN_PULL_CALL)
- == CallSdkCompat.Details.CAPABILITY_CAN_PULL_CALL) {
-
- Intent intent = new Intent(
- NotificationBroadcastReceiver.ACTION_PULL_EXTERNAL_CALL, null, mContext,
- NotificationBroadcastReceiver.class);
- intent.putExtra(NotificationBroadcastReceiver.EXTRA_NOTIFICATION_ID,
- info.getNotificationId());
-
- builder.addAction(new Notification.Action.Builder(R.drawable.ic_call_white_24dp,
- mContext.getText(R.string.notification_transfer_call),
- PendingIntent.getBroadcast(mContext, 0, intent, 0)).build());
- }
-
- /**
- * This builder is used for the notification shown when the device is locked and the user
- * has set their notification settings to 'hide sensitive content'
- * {@see Notification.Builder#setPublicVersion}.
- */
- Notification.Builder publicBuilder = new Notification.Builder(mContext);
- publicBuilder.setSmallIcon(R.drawable.ic_call_white_24dp);
- publicBuilder.setColor(mContext.getResources().getColor(R.color.dialer_theme_color));
-
- builder.setPublicVersion(publicBuilder.build());
- Notification notification = builder.build();
-
- NotificationManager notificationManager =
- (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.notify(NOTIFICATION_TAG, info.getNotificationId(), notification);
- }
-
- /**
- * Finds a large icon to display in a notification for a call. For conference calls, a
- * conference call icon is used, otherwise if contact info is specified, the user's contact
- * photo or avatar is used.
- *
- * @param context The context.
- * @param contactInfo The contact cache info.
- * @param call The call.
- * @return The large icon to use for the notification.
- */
- private @Nullable Bitmap getLargeIconToDisplay(Context context,
- ContactInfoCache.ContactCacheEntry contactInfo, android.telecom.Call call) {
-
- Bitmap largeIcon = null;
- if (call.getDetails().hasProperty(android.telecom.Call.Details.PROPERTY_CONFERENCE) &&
- !call.getDetails()
- .hasProperty(android.telecom.Call.Details.PROPERTY_GENERIC_CONFERENCE)) {
-
- largeIcon = BitmapFactory.decodeResource(context.getResources(),
- R.drawable.img_conference);
- }
- if (contactInfo.photo != null && (contactInfo.photo instanceof BitmapDrawable)) {
- largeIcon = ((BitmapDrawable) contactInfo.photo).getBitmap();
- }
- return largeIcon;
- }
-
- /**
- * Given a bitmap, returns a rounded version of the icon suitable for display in a notification.
- *
- * @param context The context.
- * @param bitmap The bitmap to round.
- * @return The rounded bitmap.
- */
- private @Nullable Bitmap getRoundedIcon(Context context, @Nullable Bitmap bitmap) {
- if (bitmap == null) {
- return null;
- }
- final int height = (int) context.getResources().getDimension(
- android.R.dimen.notification_large_icon_height);
- final int width = (int) context.getResources().getDimension(
- android.R.dimen.notification_large_icon_width);
- return BitmapUtil.getRoundedBitmap(bitmap, width, height);
- }
-
- /**
- * Builds a notification content title for a call. If the call is a conference call, it is
- * identified as such. Otherwise an attempt is made to show an associated contact name or
- * phone number.
- *
- * @param context The context.
- * @param contactsPreferences Contacts preferences, used to determine the preferred formatting
- * for contact names.
- * @param contactInfo The contact info which was looked up in the contact cache.
- * @param call The call to generate a title for.
- * @return The content title.
- */
- private @Nullable String getContentTitle(Context context,
- @Nullable ContactsPreferences contactsPreferences,
- ContactInfoCache.ContactCacheEntry contactInfo, android.telecom.Call call) {
-
- if (call.getDetails().hasProperty(android.telecom.Call.Details.PROPERTY_CONFERENCE) &&
- !call.getDetails()
- .hasProperty(android.telecom.Call.Details.PROPERTY_GENERIC_CONFERENCE)) {
-
- return context.getResources().getString(R.string.card_title_conf_call);
- }
-
- String preferredName = ContactDisplayUtils.getPreferredDisplayName(contactInfo.namePrimary,
- contactInfo.nameAlternative, contactsPreferences);
- if (TextUtils.isEmpty(preferredName)) {
- return TextUtils.isEmpty(contactInfo.number) ? null : BidiFormatter.getInstance()
- .unicodeWrap(contactInfo.number, TextDirectionHeuristics.LTR);
- }
- return preferredName;
- }
-
- /**
- * Gets a "person reference" for a notification, used by the system to determine whether the
- * notification should be allowed past notification interruption filters.
- *
- * @param contactInfo The contact info from cache.
- * @param call The call.
- * @return the person reference.
- */
- private String getPersonReference(ContactInfoCache.ContactCacheEntry contactInfo,
- Call call) {
-
- String number = TelecomCallUtil.getNumber(call);
- // Query {@link Contacts#CONTENT_LOOKUP_URI} directly with work lookup key is not allowed.
- // So, do not pass {@link Contacts#CONTENT_LOOKUP_URI} to NotificationManager to avoid
- // NotificationManager using it.
- if (contactInfo.lookupUri != null && contactInfo.userType != ContactsUtils.USER_TYPE_WORK) {
- return contactInfo.lookupUri.toString();
- } else if (!TextUtils.isEmpty(number)) {
- return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null).toString();
- }
- return "";
- }
-}
diff --git a/InCallUI/src/com/android/incallui/FragmentDisplayManager.java b/InCallUI/src/com/android/incallui/FragmentDisplayManager.java
deleted file mode 100644
index 045d999a0..000000000
--- a/InCallUI/src/com/android/incallui/FragmentDisplayManager.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.incallui;
-
-import android.app.Fragment;
-
-interface FragmentDisplayManager {
- public void onFragmentAttached(Fragment fragment);
-}
diff --git a/InCallUI/src/com/android/incallui/GlowPadAnswerFragment.java b/InCallUI/src/com/android/incallui/GlowPadAnswerFragment.java
deleted file mode 100644
index 62a8e7829..000000000
--- a/InCallUI/src/com/android/incallui/GlowPadAnswerFragment.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.os.Bundle;
-import android.telecom.VideoProfile;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.dialer.R;
-
-public class GlowPadAnswerFragment extends AnswerFragment {
-
- private GlowPadWrapper mGlowpad;
-
- public GlowPadAnswerFragment() {
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- mGlowpad = (GlowPadWrapper) inflater.inflate(R.layout.answer_fragment,
- container, false);
-
- Log.d(this, "Creating view for answer fragment ", this);
- Log.d(this, "Created from activity", getActivity());
- mGlowpad.setAnswerFragment(this);
-
- return mGlowpad;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mGlowpad.requestFocus();
- }
-
- @Override
- public void onDestroyView() {
- Log.d(this, "onDestroyView");
- if (mGlowpad != null) {
- mGlowpad.stopPing();
- mGlowpad = null;
- }
- super.onDestroyView();
- }
-
- @Override
- public void onShowAnswerUi(boolean shown) {
- Log.d(this, "Show answer UI: " + shown);
- if (shown) {
- mGlowpad.startPing();
- } else {
- mGlowpad.stopPing();
- }
- }
-
- /**
- * Sets targets on the glowpad according to target set identified by the parameter.
- *
- * @param targetSet Integer identifying the set of targets to use.
- */
- public void showTargets(int targetSet) {
- showTargets(targetSet, VideoProfile.STATE_BIDIRECTIONAL);
- }
-
- /**
- * Sets targets on the glowpad according to target set identified by the parameter.
- *
- * @param targetSet Integer identifying the set of targets to use.
- */
- @Override
- public void showTargets(int targetSet, int videoState) {
- final int targetResourceId;
- final int targetDescriptionsResourceId;
- final int directionDescriptionsResourceId;
- final int handleDrawableResourceId;
- mGlowpad.setVideoState(videoState);
-
- switch (targetSet) {
- case TARGET_SET_FOR_AUDIO_WITH_SMS:
- targetResourceId = R.array.incoming_call_widget_audio_with_sms_targets;
- targetDescriptionsResourceId =
- R.array.incoming_call_widget_audio_with_sms_target_descriptions;
- directionDescriptionsResourceId =
- R.array.incoming_call_widget_audio_with_sms_direction_descriptions;
- handleDrawableResourceId = R.drawable.ic_incall_audio_handle;
- break;
- case TARGET_SET_FOR_VIDEO_WITHOUT_SMS:
- targetResourceId = R.array.incoming_call_widget_video_without_sms_targets;
- targetDescriptionsResourceId =
- R.array.incoming_call_widget_video_without_sms_target_descriptions;
- directionDescriptionsResourceId =
- R.array.incoming_call_widget_video_without_sms_direction_descriptions;
- handleDrawableResourceId = R.drawable.ic_incall_video_handle;
- break;
- case TARGET_SET_FOR_VIDEO_WITH_SMS:
- targetResourceId = R.array.incoming_call_widget_video_with_sms_targets;
- targetDescriptionsResourceId =
- R.array.incoming_call_widget_video_with_sms_target_descriptions;
- directionDescriptionsResourceId =
- R.array.incoming_call_widget_video_with_sms_direction_descriptions;
- handleDrawableResourceId = R.drawable.ic_incall_video_handle;
- break;
- case TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST:
- targetResourceId =
- R.array.incoming_call_widget_video_request_targets;
- targetDescriptionsResourceId =
- R.array.incoming_call_widget_video_request_target_descriptions;
- directionDescriptionsResourceId = R.array
- .incoming_call_widget_video_request_target_direction_descriptions;
- handleDrawableResourceId = R.drawable.ic_incall_video_handle;
- break;
- case TARGET_SET_FOR_AUDIO_WITHOUT_SMS:
- default:
- targetResourceId = R.array.incoming_call_widget_audio_without_sms_targets;
- targetDescriptionsResourceId =
- R.array.incoming_call_widget_audio_without_sms_target_descriptions;
- directionDescriptionsResourceId =
- R.array.incoming_call_widget_audio_without_sms_direction_descriptions;
- handleDrawableResourceId = R.drawable.ic_incall_audio_handle;
- break;
- }
-
- if (targetResourceId != mGlowpad.getTargetResourceId()) {
- mGlowpad.setTargetResources(targetResourceId);
- mGlowpad.setTargetDescriptionsResourceId(targetDescriptionsResourceId);
- mGlowpad.setDirectionDescriptionsResourceId(directionDescriptionsResourceId);
- mGlowpad.setHandleDrawable(handleDrawableResourceId);
- mGlowpad.reset(false);
- }
- }
-
- @Override
- protected void onMessageDialogCancel() {
- if (mGlowpad != null) {
- mGlowpad.startPing();
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/GlowPadWrapper.java b/InCallUI/src/com/android/incallui/GlowPadWrapper.java
deleted file mode 100644
index 342f6b4fd..000000000
--- a/InCallUI/src/com/android/incallui/GlowPadWrapper.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Message;
-import android.telecom.VideoProfile;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.dialer.R;
-import com.android.incallui.widget.multiwaveview.GlowPadView;
-
-/**
- *
- */
-public class GlowPadWrapper extends GlowPadView implements GlowPadView.OnTriggerListener {
-
- // Parameters for the GlowPadView "ping" animation; see triggerPing().
- private static final int PING_MESSAGE_WHAT = 101;
- private static final boolean ENABLE_PING_AUTO_REPEAT = true;
- private static final long PING_REPEAT_DELAY_MS = 1200;
-
- private final Handler mPingHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case PING_MESSAGE_WHAT:
- triggerPing();
- break;
- }
- }
- };
-
- private AnswerFragment mAnswerFragment;
- private boolean mPingEnabled = true;
- private boolean mTargetTriggered = false;
- private int mVideoState = VideoProfile.STATE_BIDIRECTIONAL;
-
- public GlowPadWrapper(Context context) {
- super(context);
- Log.d(this, "class created " + this + " ");
- }
-
- public GlowPadWrapper(Context context, AttributeSet attrs) {
- super(context, attrs);
- Log.d(this, "class created " + this);
- }
-
- @Override
- protected void onFinishInflate() {
- Log.d(this, "onFinishInflate()");
- super.onFinishInflate();
- setOnTriggerListener(this);
- }
-
- public void startPing() {
- Log.d(this, "startPing");
- mPingEnabled = true;
- triggerPing();
- }
-
- public void stopPing() {
- Log.d(this, "stopPing");
- mPingEnabled = false;
- mPingHandler.removeMessages(PING_MESSAGE_WHAT);
- }
-
- private void triggerPing() {
- Log.d(this, "triggerPing(): " + mPingEnabled + " " + this);
- if (mPingEnabled && !mPingHandler.hasMessages(PING_MESSAGE_WHAT)) {
- ping();
-
- if (ENABLE_PING_AUTO_REPEAT) {
- mPingHandler.sendEmptyMessageDelayed(PING_MESSAGE_WHAT, PING_REPEAT_DELAY_MS);
- }
- }
- }
-
- @Override
- public void onGrabbed(View v, int handle) {
- Log.d(this, "onGrabbed()");
- stopPing();
- }
-
- @Override
- public void onReleased(View v, int handle) {
- Log.d(this, "onReleased()");
- if (mTargetTriggered) {
- mTargetTriggered = false;
- } else {
- startPing();
- }
- }
-
- @Override
- public void onTrigger(View v, int target) {
- Log.d(this, "onTrigger() view=" + v + " target=" + target);
- final int resId = getResourceIdForTarget(target);
- if (resId == R.drawable.ic_lockscreen_answer) {
- mAnswerFragment.onAnswer(VideoProfile.STATE_AUDIO_ONLY, getContext());
- mTargetTriggered = true;
- } else if (resId == R.drawable.ic_lockscreen_decline) {
- mAnswerFragment.onDecline(getContext());
- mTargetTriggered = true;
- } else if (resId == R.drawable.ic_lockscreen_text) {
- mAnswerFragment.onText();
- mTargetTriggered = true;
- } else if (resId == R.drawable.ic_videocam || resId == R.drawable.ic_lockscreen_answer_video) {
- mAnswerFragment.onAnswer(mVideoState, getContext());
- mTargetTriggered = true;
- } else if (resId == R.drawable.ic_lockscreen_decline_video) {
- mAnswerFragment.onDeclineUpgradeRequest(getContext());
- mTargetTriggered = true;
- } else {
- // Code should never reach here.
- Log.e(this, "Trigger detected on unhandled resource. Skipping.");
- }
- }
-
- @Override
- public void onGrabbedStateChange(View v, int handle) {
-
- }
-
- @Override
- public void onFinishFinalAnimation() {
-
- }
-
- public void setAnswerFragment(AnswerFragment fragment) {
- mAnswerFragment = fragment;
- }
-
- /**
- * Sets the video state represented by the "video" icon on the glow pad.
- *
- * @param videoState The new video state.
- */
- public void setVideoState(int videoState) {
- mVideoState = videoState;
- }
-}
diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java
deleted file mode 100644
index eaaedff2c..000000000
--- a/InCallUI/src/com/android/incallui/InCallActivity.java
+++ /dev/null
@@ -1,980 +0,0 @@
-/*
- * Copyright (C) 2006 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.incallui;
-
-import android.app.ActionBar;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
-import android.content.DialogInterface.OnClickListener;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.graphics.Point;
-import android.hardware.SensorManager;
-import android.os.Bundle;
-import android.os.Trace;
-import android.telecom.DisconnectCause;
-import android.telecom.PhoneAccountHandle;
-import android.text.TextUtils;
-import android.view.KeyEvent;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.OrientationEventListener;
-import android.view.Surface;
-import android.view.View;
-import android.view.View.OnTouchListener;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-
-import com.android.contacts.common.activity.TransactionSafeActivity;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.interactions.TouchPointManager;
-import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
-import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment.SelectPhoneAccountListener;
-import com.android.dialer.R;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.ScreenEvent;
-import com.android.incallui.Call.State;
-import com.android.incallui.util.AccessibilityUtil;
-import com.android.phone.common.animation.AnimUtils;
-import com.android.phone.common.animation.AnimationListenerAdapter;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Main activity that the user interacts with while in a live call.
- */
-public class InCallActivity extends TransactionSafeActivity implements FragmentDisplayManager {
-
- public static final String TAG = InCallActivity.class.getSimpleName();
-
- public static final String SHOW_DIALPAD_EXTRA = "InCallActivity.show_dialpad";
- public static final String DIALPAD_TEXT_EXTRA = "InCallActivity.dialpad_text";
- public static final String NEW_OUTGOING_CALL_EXTRA = "InCallActivity.new_outgoing_call";
- public static final String FOR_FULL_SCREEN_INTENT = "InCallActivity.for_full_screen_intent";
-
- private static final String TAG_DIALPAD_FRAGMENT = "tag_dialpad_fragment";
- private static final String TAG_CONFERENCE_FRAGMENT = "tag_conference_manager_fragment";
- private static final String TAG_CALLCARD_FRAGMENT = "tag_callcard_fragment";
- private static final String TAG_ANSWER_FRAGMENT = "tag_answer_fragment";
- private static final String TAG_SELECT_ACCT_FRAGMENT = "tag_select_acct_fragment";
-
- private static final int DIALPAD_REQUEST_NONE = 1;
- private static final int DIALPAD_REQUEST_SHOW = 2;
- private static final int DIALPAD_REQUEST_HIDE = 3;
-
- /**
- * This is used to relaunch the activity if resizing beyond which it needs to load different
- * layout file.
- */
- private static final int SCREEN_HEIGHT_RESIZE_THRESHOLD = 500;
-
- private CallButtonFragment mCallButtonFragment;
- private CallCardFragment mCallCardFragment;
- private AnswerFragment mAnswerFragment;
- private DialpadFragment mDialpadFragment;
- private ConferenceManagerFragment mConferenceManagerFragment;
- private FragmentManager mChildFragmentManager;
-
- private AlertDialog mDialog;
- private InCallOrientationEventListener mInCallOrientationEventListener;
-
- /**
- * Used to indicate whether the dialpad should be hidden or shown {@link #onResume}.
- * {@code #DIALPAD_REQUEST_SHOW} indicates that the dialpad should be shown.
- * {@code #DIALPAD_REQUEST_HIDE} indicates that the dialpad should be hidden.
- * {@code #DIALPAD_REQUEST_NONE} indicates no change should be made to dialpad visibility.
- */
- private int mShowDialpadRequest = DIALPAD_REQUEST_NONE;
-
- /**
- * Use to determine if the dialpad should be animated on show.
- */
- private boolean mAnimateDialpadOnShow;
-
- /**
- * Use to determine the DTMF Text which should be pre-populated in the dialpad.
- */
- private String mDtmfText;
-
- /**
- * Use to pass parameters for showing the PostCharDialog to {@link #onResume}
- */
- private boolean mShowPostCharWaitDialogOnResume;
- private String mShowPostCharWaitDialogCallId;
- private String mShowPostCharWaitDialogChars;
-
- private boolean mIsLandscape;
- private Animation mSlideIn;
- private Animation mSlideOut;
- private boolean mDismissKeyguard = false;
-
- AnimationListenerAdapter mSlideOutListener = new AnimationListenerAdapter() {
- @Override
- public void onAnimationEnd(Animation animation) {
- showFragment(TAG_DIALPAD_FRAGMENT, false, true);
- }
- };
-
- private OnTouchListener mDispatchTouchEventListener;
-
- private SelectPhoneAccountListener mSelectAcctListener = new SelectPhoneAccountListener() {
- @Override
- public void onPhoneAccountSelected(PhoneAccountHandle selectedAccountHandle,
- boolean setDefault) {
- InCallPresenter.getInstance().handleAccountSelection(selectedAccountHandle,
- setDefault);
- }
-
- @Override
- public void onDialogDismissed() {
- InCallPresenter.getInstance().cancelAccountSelection();
- }
- };
-
- @Override
- protected void onCreate(Bundle icicle) {
- Log.d(this, "onCreate()... this = " + this);
-
- super.onCreate(icicle);
-
- // set this flag so this activity will stay in front of the keyguard
- // Have the WindowManager filter out touch events that are "too fat".
- int flags = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
- | WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
-
- getWindow().addFlags(flags);
-
- // Setup action bar for the conference call manager.
- requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
- ActionBar actionBar = getActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowTitleEnabled(true);
- actionBar.hide();
- }
-
- // TODO(klp): Do we need to add this back when prox sensor is not available?
- // lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
-
- setContentView(R.layout.incall_screen);
-
- internalResolveIntent(getIntent());
-
- mIsLandscape = getResources().getConfiguration().orientation ==
- Configuration.ORIENTATION_LANDSCAPE;
-
- final boolean isRtl = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) ==
- View.LAYOUT_DIRECTION_RTL;
-
- if (mIsLandscape) {
- mSlideIn = AnimationUtils.loadAnimation(this,
- isRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right);
- mSlideOut = AnimationUtils.loadAnimation(this,
- isRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right);
- } else {
- mSlideIn = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_in_bottom);
- mSlideOut = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_out_bottom);
- }
-
- mSlideIn.setInterpolator(AnimUtils.EASE_IN);
- mSlideOut.setInterpolator(AnimUtils.EASE_OUT);
-
- mSlideOut.setAnimationListener(mSlideOutListener);
-
- // If the dialpad fragment already exists, retrieve it. This is important when rotating as
- // we will not be able to hide or show the dialpad after the rotation otherwise.
- Fragment existingFragment =
- getFragmentManager().findFragmentByTag(DialpadFragment.class.getName());
- if (existingFragment != null) {
- mDialpadFragment = (DialpadFragment) existingFragment;
- }
-
- if (icicle != null) {
- // If the dialpad was shown before, set variables indicating it should be shown and
- // populated with the previous DTMF text. The dialpad is actually shown and populated
- // in onResume() to ensure the hosting CallCardFragment has been inflated and is ready
- // to receive it.
- if (icicle.containsKey(SHOW_DIALPAD_EXTRA)) {
- boolean showDialpad = icicle.getBoolean(SHOW_DIALPAD_EXTRA);
- mShowDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_HIDE;
- mAnimateDialpadOnShow = false;
- }
- mDtmfText = icicle.getString(DIALPAD_TEXT_EXTRA);
-
- SelectPhoneAccountDialogFragment dialogFragment = (SelectPhoneAccountDialogFragment)
- getFragmentManager().findFragmentByTag(TAG_SELECT_ACCT_FRAGMENT);
- if (dialogFragment != null) {
- dialogFragment.setListener(mSelectAcctListener);
- }
- }
- mInCallOrientationEventListener = new InCallOrientationEventListener(this);
-
- Log.d(this, "onCreate(): exit");
- }
-
- @Override
- protected void onSaveInstanceState(Bundle out) {
- // TODO: The dialpad fragment should handle this as part of its own state
- out.putBoolean(SHOW_DIALPAD_EXTRA,
- mCallButtonFragment != null && mCallButtonFragment.isDialpadVisible());
- if (mDialpadFragment != null) {
- out.putString(DIALPAD_TEXT_EXTRA, mDialpadFragment.getDtmfText());
- }
- super.onSaveInstanceState(out);
- }
-
- @Override
- protected void onStart() {
- Log.d(this, "onStart()...");
- super.onStart();
-
- // setting activity should be last thing in setup process
- InCallPresenter.getInstance().setActivity(this);
- enableInCallOrientationEventListener(getRequestedOrientation() ==
- InCallOrientationEventListener.FULL_SENSOR_SCREEN_ORIENTATION);
-
- InCallPresenter.getInstance().onActivityStarted();
- }
-
- @Override
- protected void onResume() {
- Log.i(this, "onResume()...");
- super.onResume();
-
- InCallPresenter.getInstance().setThemeColors();
- InCallPresenter.getInstance().onUiShowing(true);
-
- // Clear fullscreen state onResume; the stored value may not match reality.
- InCallPresenter.getInstance().clearFullscreen();
-
- // If there is a pending request to show or hide the dialpad, handle that now.
- if (mShowDialpadRequest != DIALPAD_REQUEST_NONE) {
- if (mShowDialpadRequest == DIALPAD_REQUEST_SHOW) {
- // Exit fullscreen so that the user has access to the dialpad hide/show button and
- // can hide the dialpad. Important when showing the dialpad from within dialer.
- InCallPresenter.getInstance().setFullScreen(false, true /* force */);
-
- mCallButtonFragment.displayDialpad(true /* show */,
- mAnimateDialpadOnShow /* animate */);
- mAnimateDialpadOnShow = false;
-
- if (mDialpadFragment != null) {
- mDialpadFragment.setDtmfText(mDtmfText);
- mDtmfText = null;
- }
- } else {
- Log.v(this, "onResume : force hide dialpad");
- if (mDialpadFragment != null) {
- mCallButtonFragment.displayDialpad(false /* show */, false /* animate */);
- }
- }
- mShowDialpadRequest = DIALPAD_REQUEST_NONE;
- }
-
- if (mShowPostCharWaitDialogOnResume) {
- showPostCharWaitDialog(mShowPostCharWaitDialogCallId, mShowPostCharWaitDialogChars);
- }
-
- CallList.getInstance().onInCallUiShown(
- getIntent().getBooleanExtra(FOR_FULL_SCREEN_INTENT, false));
- }
-
- // onPause is guaranteed to be called when the InCallActivity goes
- // in the background.
- @Override
- protected void onPause() {
- Log.d(this, "onPause()...");
- if (mDialpadFragment != null) {
- mDialpadFragment.onDialerKeyUp(null);
- }
-
- InCallPresenter.getInstance().onUiShowing(false);
- if (isFinishing()) {
- InCallPresenter.getInstance().unsetActivity(this);
- }
- super.onPause();
- }
-
- @Override
- protected void onStop() {
- Log.d(this, "onStop()...");
- enableInCallOrientationEventListener(false);
- InCallPresenter.getInstance().updateIsChangingConfigurations();
- InCallPresenter.getInstance().onActivityStopped();
- super.onStop();
- }
-
- @Override
- protected void onDestroy() {
- Log.d(this, "onDestroy()... this = " + this);
- InCallPresenter.getInstance().unsetActivity(this);
- InCallPresenter.getInstance().updateIsChangingConfigurations();
- super.onDestroy();
- }
-
- /**
- * When fragments have a parent fragment, onAttachFragment is not called on the parent
- * activity. To fix this, register our own callback instead that is always called for
- * all fragments.
- *
- * @see {@link BaseFragment#onAttach(Activity)}
- */
- @Override
- public void onFragmentAttached(Fragment fragment) {
- if (fragment instanceof DialpadFragment) {
- mDialpadFragment = (DialpadFragment) fragment;
- } else if (fragment instanceof AnswerFragment) {
- mAnswerFragment = (AnswerFragment) fragment;
- } else if (fragment instanceof CallCardFragment) {
- mCallCardFragment = (CallCardFragment) fragment;
- mChildFragmentManager = mCallCardFragment.getChildFragmentManager();
- } else if (fragment instanceof ConferenceManagerFragment) {
- mConferenceManagerFragment = (ConferenceManagerFragment) fragment;
- } else if (fragment instanceof CallButtonFragment) {
- mCallButtonFragment = (CallButtonFragment) fragment;
- }
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- Configuration oldConfig = getResources().getConfiguration();
- Log.v(this, String.format(
- "incallui config changed, screen size: w%ddp x h%ddp old:w%ddp x h%ddp",
- newConfig.screenWidthDp, newConfig.screenHeightDp,
- oldConfig.screenWidthDp, oldConfig.screenHeightDp));
- // Recreate this activity if height is changing beyond the threshold to load different
- // layout file.
- if (oldConfig.screenHeightDp < SCREEN_HEIGHT_RESIZE_THRESHOLD &&
- newConfig.screenHeightDp > SCREEN_HEIGHT_RESIZE_THRESHOLD ||
- oldConfig.screenHeightDp > SCREEN_HEIGHT_RESIZE_THRESHOLD &&
- newConfig.screenHeightDp < SCREEN_HEIGHT_RESIZE_THRESHOLD) {
- Log.i(this, String.format(
- "Recreate activity due to resize beyond threshold: %d dp",
- SCREEN_HEIGHT_RESIZE_THRESHOLD));
- recreate();
- }
- }
-
- /**
- * Returns true when the Activity is currently visible.
- */
- /* package */ boolean isVisible() {
- return isSafeToCommitTransactions();
- }
-
- private boolean hasPendingDialogs() {
- return mDialog != null || (mAnswerFragment != null && mAnswerFragment.hasPendingDialogs());
- }
-
- @Override
- public void finish() {
- Log.i(this, "finish(). Dialog showing: " + (mDialog != null));
-
- // skip finish if we are still showing a dialog.
- if (!hasPendingDialogs()) {
- super.finish();
- }
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- Log.d(this, "onNewIntent: intent = " + intent);
-
- // We're being re-launched with a new Intent. Since it's possible for a
- // single InCallActivity instance to persist indefinitely (even if we
- // finish() ourselves), this sequence can potentially happen any time
- // the InCallActivity needs to be displayed.
-
- // Stash away the new intent so that we can get it in the future
- // by calling getIntent(). (Otherwise getIntent() will return the
- // original Intent from when we first got created!)
- setIntent(intent);
-
- // Activities are always paused before receiving a new intent, so
- // we can count on our onResume() method being called next.
-
- // Just like in onCreate(), handle the intent.
- internalResolveIntent(intent);
- }
-
- @Override
- public void onBackPressed() {
- Log.i(this, "onBackPressed");
-
- // BACK is also used to exit out of any "special modes" of the
- // in-call UI:
- if (!isVisible()) {
- return;
- }
-
- if ((mConferenceManagerFragment == null || !mConferenceManagerFragment.isVisible())
- && (mCallCardFragment == null || !mCallCardFragment.isVisible())) {
- return;
- }
-
- if (mDialpadFragment != null && mDialpadFragment.isVisible()) {
- mCallButtonFragment.displayDialpad(false /* show */, true /* animate */);
- return;
- } else if (mConferenceManagerFragment != null && mConferenceManagerFragment.isVisible()) {
- showConferenceFragment(false);
- return;
- }
-
- // Always disable the Back key while an incoming call is ringing
- final Call call = CallList.getInstance().getIncomingCall();
- if (call != null) {
- Log.i(this, "Consume Back press for an incoming call");
- return;
- }
-
- // Nothing special to do. Fall back to the default behavior.
- super.onBackPressed();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- final int itemId = item.getItemId();
- if (itemId == android.R.id.home) {
- onBackPressed();
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- // push input to the dialer.
- if (mDialpadFragment != null && (mDialpadFragment.isVisible()) &&
- (mDialpadFragment.onDialerKeyUp(event))) {
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_CALL) {
- // Always consume CALL to be sure the PhoneWindow won't do anything with it
- return true;
- }
- return super.onKeyUp(keyCode, event);
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (mDispatchTouchEventListener != null) {
- boolean handled = mDispatchTouchEventListener.onTouch(null, ev);
- if (handled) {
- return true;
- }
- }
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_CALL:
- boolean handled = InCallPresenter.getInstance().handleCallKey();
- if (!handled) {
- Log.w(this, "InCallActivity should always handle KEYCODE_CALL in onKeyDown");
- }
- // Always consume CALL to be sure the PhoneWindow won't do anything with it
- return true;
-
- // Note there's no KeyEvent.KEYCODE_ENDCALL case here.
- // The standard system-wide handling of the ENDCALL key
- // (see PhoneWindowManager's handling of KEYCODE_ENDCALL)
- // already implements exactly what the UI spec wants,
- // namely (1) "hang up" if there's a current active call,
- // or (2) "don't answer" if there's a current ringing call.
-
- case KeyEvent.KEYCODE_CAMERA:
- // Disable the CAMERA button while in-call since it's too
- // easy to press accidentally.
- return true;
-
- case KeyEvent.KEYCODE_VOLUME_UP:
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- case KeyEvent.KEYCODE_VOLUME_MUTE:
- // Ringer silencing handled by PhoneWindowManager.
- break;
-
- case KeyEvent.KEYCODE_MUTE:
- // toggle mute
- TelecomAdapter.getInstance().mute(!AudioModeProvider.getInstance().getMute());
- return true;
-
- // Various testing/debugging features, enabled ONLY when VERBOSE == true.
- case KeyEvent.KEYCODE_SLASH:
- if (Log.VERBOSE) {
- Log.v(this, "----------- InCallActivity View dump --------------");
- // Dump starting from the top-level view of the entire activity:
- Window w = this.getWindow();
- View decorView = w.getDecorView();
- Log.d(this, "View dump:" + decorView);
- return true;
- }
- break;
- case KeyEvent.KEYCODE_EQUALS:
- // TODO: Dump phone state?
- break;
- }
-
- if (event.getRepeatCount() == 0 && handleDialerKeyDown(keyCode, event)) {
- return true;
- }
- return super.onKeyDown(keyCode, event);
- }
-
- private boolean handleDialerKeyDown(int keyCode, KeyEvent event) {
- Log.v(this, "handleDialerKeyDown: keyCode " + keyCode + ", event " + event + "...");
-
- // As soon as the user starts typing valid dialable keys on the
- // keyboard (presumably to type DTMF tones) we start passing the
- // key events to the DTMFDialer's onDialerKeyDown.
- if (mDialpadFragment != null && mDialpadFragment.isVisible()) {
- return mDialpadFragment.onDialerKeyDown(event);
- }
-
- return false;
- }
-
- public CallButtonFragment getCallButtonFragment() {
- return mCallButtonFragment;
- }
-
- public CallCardFragment getCallCardFragment() {
- return mCallCardFragment;
- }
-
- public AnswerFragment getAnswerFragment() {
- return mAnswerFragment;
- }
-
- private void internalResolveIntent(Intent intent) {
- final String action = intent.getAction();
- if (action.equals(Intent.ACTION_MAIN)) {
- // This action is the normal way to bring up the in-call UI.
- //
- // But we do check here for one extra that can come along with the
- // ACTION_MAIN intent:
-
- if (intent.hasExtra(SHOW_DIALPAD_EXTRA)) {
- // SHOW_DIALPAD_EXTRA can be used here to specify whether the DTMF
- // dialpad should be initially visible. If the extra isn't
- // present at all, we just leave the dialpad in its previous state.
-
- final boolean showDialpad = intent.getBooleanExtra(SHOW_DIALPAD_EXTRA, false);
- Log.d(this, "- internalResolveIntent: SHOW_DIALPAD_EXTRA: " + showDialpad);
-
- relaunchedFromDialer(showDialpad);
- }
-
- boolean newOutgoingCall = false;
- if (intent.getBooleanExtra(NEW_OUTGOING_CALL_EXTRA, false)) {
- intent.removeExtra(NEW_OUTGOING_CALL_EXTRA);
- Call call = CallList.getInstance().getOutgoingCall();
- if (call == null) {
- call = CallList.getInstance().getPendingOutgoingCall();
- }
-
- Bundle extras = null;
- if (call != null) {
- extras = call.getTelecomCall().getDetails().getIntentExtras();
- }
- if (extras == null) {
- // Initialize the extras bundle to avoid NPE
- extras = new Bundle();
- }
-
- Point touchPoint = null;
- if (TouchPointManager.getInstance().hasValidPoint()) {
- // Use the most immediate touch point in the InCallUi if available
- touchPoint = TouchPointManager.getInstance().getPoint();
- } else {
- // Otherwise retrieve the touch point from the call intent
- if (call != null) {
- touchPoint = (Point) extras.getParcelable(TouchPointManager.TOUCH_POINT);
- }
- }
-
- // Start animation for new outgoing call
- CircularRevealFragment.startCircularReveal(getFragmentManager(), touchPoint,
- InCallPresenter.getInstance());
-
- // InCallActivity is responsible for disconnecting a new outgoing call if there
- // is no way of making it (i.e. no valid call capable accounts).
- // If the version is not MSIM compatible, then ignore this code.
- if (CompatUtils.isMSIMCompatible()
- && InCallPresenter.isCallWithNoValidAccounts(call)) {
- TelecomAdapter.getInstance().disconnectCall(call.getId());
- }
-
- dismissKeyguard(true);
- newOutgoingCall = true;
- }
-
- Call pendingAccountSelectionCall = CallList.getInstance().getWaitingForAccountCall();
- if (pendingAccountSelectionCall != null) {
- showCallCardFragment(false);
- Bundle extras =
- pendingAccountSelectionCall.getTelecomCall().getDetails().getIntentExtras();
-
- final List<PhoneAccountHandle> phoneAccountHandles;
- if (extras != null) {
- phoneAccountHandles = extras.getParcelableArrayList(
- android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS);
- } else {
- phoneAccountHandles = new ArrayList<>();
- }
-
- DialogFragment dialogFragment = SelectPhoneAccountDialogFragment.newInstance(
- R.string.select_phone_account_for_calls, true, phoneAccountHandles,
- mSelectAcctListener);
- dialogFragment.show(getFragmentManager(), TAG_SELECT_ACCT_FRAGMENT);
- } else if (!newOutgoingCall) {
- showCallCardFragment(true);
- }
- return;
- }
- }
-
- /**
- * When relaunching from the dialer app, {@code showDialpad} indicates whether the dialpad
- * should be shown on launch.
- *
- * @param showDialpad {@code true} to indicate the dialpad should be shown on launch, and
- * {@code false} to indicate no change should be made to the
- * dialpad visibility.
- */
- private void relaunchedFromDialer(boolean showDialpad) {
- mShowDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_NONE;
- mAnimateDialpadOnShow = true;
-
- if (mShowDialpadRequest == DIALPAD_REQUEST_SHOW) {
- // If there's only one line in use, AND it's on hold, then we're sure the user
- // wants to use the dialpad toward the exact line, so un-hold the holding line.
- final Call call = CallList.getInstance().getActiveOrBackgroundCall();
- if (call != null && call.getState() == State.ONHOLD) {
- TelecomAdapter.getInstance().unholdCall(call.getId());
- }
- }
- }
-
- public void dismissKeyguard(boolean dismiss) {
- if (mDismissKeyguard == dismiss) {
- return;
- }
- mDismissKeyguard = dismiss;
- if (dismiss) {
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
- } else {
- getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
- }
- }
-
- private void showFragment(String tag, boolean show, boolean executeImmediately) {
- Trace.beginSection("showFragment - " + tag);
- final FragmentManager fm = getFragmentManagerForTag(tag);
-
- if (fm == null) {
- Log.w(TAG, "Fragment manager is null for : " + tag);
- return;
- }
-
- Fragment fragment = fm.findFragmentByTag(tag);
- if (!show && fragment == null) {
- // Nothing to show, so bail early.
- return;
- }
-
- final FragmentTransaction transaction = fm.beginTransaction();
- if (show) {
- if (fragment == null) {
- fragment = createNewFragmentForTag(tag);
- transaction.add(getContainerIdForFragment(tag), fragment, tag);
- } else {
- transaction.show(fragment);
- }
- Logger.logScreenView(getScreenTypeForTag(tag), this);
- } else {
- transaction.hide(fragment);
- }
-
- transaction.commitAllowingStateLoss();
- if (executeImmediately) {
- fm.executePendingTransactions();
- }
- Trace.endSection();
- }
-
- private Fragment createNewFragmentForTag(String tag) {
- if (TAG_DIALPAD_FRAGMENT.equals(tag)) {
- mDialpadFragment = new DialpadFragment();
- return mDialpadFragment;
- } else if (TAG_ANSWER_FRAGMENT.equals(tag)) {
- if (AccessibilityUtil.isTalkBackEnabled(this)) {
- mAnswerFragment = new AccessibleAnswerFragment();
- } else {
- mAnswerFragment = new GlowPadAnswerFragment();
- }
- return mAnswerFragment;
- } else if (TAG_CONFERENCE_FRAGMENT.equals(tag)) {
- mConferenceManagerFragment = new ConferenceManagerFragment();
- return mConferenceManagerFragment;
- } else if (TAG_CALLCARD_FRAGMENT.equals(tag)) {
- mCallCardFragment = new CallCardFragment();
- return mCallCardFragment;
- }
- throw new IllegalStateException("Unexpected fragment: " + tag);
- }
-
- private FragmentManager getFragmentManagerForTag(String tag) {
- if (TAG_DIALPAD_FRAGMENT.equals(tag)) {
- return mChildFragmentManager;
- } else if (TAG_ANSWER_FRAGMENT.equals(tag)) {
- return mChildFragmentManager;
- } else if (TAG_CONFERENCE_FRAGMENT.equals(tag)) {
- return getFragmentManager();
- } else if (TAG_CALLCARD_FRAGMENT.equals(tag)) {
- return getFragmentManager();
- }
- throw new IllegalStateException("Unexpected fragment: " + tag);
- }
-
- private int getScreenTypeForTag(String tag) {
- switch (tag) {
- case TAG_DIALPAD_FRAGMENT:
- return ScreenEvent.INCALL_DIALPAD;
- case TAG_CALLCARD_FRAGMENT:
- return ScreenEvent.INCALL;
- case TAG_CONFERENCE_FRAGMENT:
- return ScreenEvent.CONFERENCE_MANAGEMENT;
- case TAG_ANSWER_FRAGMENT:
- return ScreenEvent.INCOMING_CALL;
- default:
- return ScreenEvent.UNKNOWN;
- }
- }
-
- private int getContainerIdForFragment(String tag) {
- if (TAG_DIALPAD_FRAGMENT.equals(tag)) {
- return R.id.answer_and_dialpad_container;
- } else if (TAG_ANSWER_FRAGMENT.equals(tag)) {
- return R.id.answer_and_dialpad_container;
- } else if (TAG_CONFERENCE_FRAGMENT.equals(tag)) {
- return R.id.main;
- } else if (TAG_CALLCARD_FRAGMENT.equals(tag)) {
- return R.id.main;
- }
- throw new IllegalStateException("Unexpected fragment: " + tag);
- }
-
- /**
- * @return {@code true} while the visibility of the dialpad has actually changed.
- */
- public boolean showDialpadFragment(boolean show, boolean animate) {
- // If the dialpad is already visible, don't animate in. If it's gone, don't animate out.
- if ((show && isDialpadVisible()) || (!show && !isDialpadVisible())) {
- return false;
- }
- // We don't do a FragmentTransaction on the hide case because it will be dealt with when
- // the listener is fired after an animation finishes.
- if (!animate) {
- showFragment(TAG_DIALPAD_FRAGMENT, show, true);
- } else {
- if (show) {
- showFragment(TAG_DIALPAD_FRAGMENT, true, true);
- mDialpadFragment.animateShowDialpad();
- }
- mDialpadFragment.getView().startAnimation(show ? mSlideIn : mSlideOut);
- }
- // Note: onDialpadVisibilityChange is called here to ensure that the dialpad FAB
- // repositions itself.
- mCallCardFragment.onDialpadVisibilityChange(show);
-
- final ProximitySensor sensor = InCallPresenter.getInstance().getProximitySensor();
- if (sensor != null) {
- sensor.onDialpadVisible(show);
- }
- return true;
- }
-
- public boolean isDialpadVisible() {
- return mDialpadFragment != null && mDialpadFragment.isVisible();
- }
-
- public void showCallCardFragment(boolean show) {
- showFragment(TAG_CALLCARD_FRAGMENT, show, true);
- }
-
- /**
- * Hides or shows the conference manager fragment.
- *
- * @param show {@code true} if the conference manager should be shown, {@code false} if it
- * should be hidden.
- */
- public void showConferenceFragment(boolean show) {
- showFragment(TAG_CONFERENCE_FRAGMENT, show, true);
- mConferenceManagerFragment.onVisibilityChanged(show);
-
- // Need to hide the call card fragment to ensure that accessibility service does not try to
- // give focus to the call card when the conference manager is visible.
- mCallCardFragment.getView().setVisibility(show ? View.GONE : View.VISIBLE);
- }
-
- public void showAnswerFragment(boolean show) {
- // CallCardFragment is the parent fragment of AnswerFragment.
- // Must create the CallCardFragment first before creating
- // AnswerFragment if CallCardFragment is null.
- if (show && getCallCardFragment() == null) {
- showCallCardFragment(true);
- }
- showFragment(TAG_ANSWER_FRAGMENT, show, true);
- }
-
- public void showPostCharWaitDialog(String callId, String chars) {
- if (isVisible()) {
- final PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars);
- fragment.show(getFragmentManager(), "postCharWait");
-
- mShowPostCharWaitDialogOnResume = false;
- mShowPostCharWaitDialogCallId = null;
- mShowPostCharWaitDialogChars = null;
- } else {
- mShowPostCharWaitDialogOnResume = true;
- mShowPostCharWaitDialogCallId = callId;
- mShowPostCharWaitDialogChars = chars;
- }
- }
-
- @Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- if (mCallCardFragment != null) {
- mCallCardFragment.dispatchPopulateAccessibilityEvent(event);
- }
- return super.dispatchPopulateAccessibilityEvent(event);
- }
-
- public void maybeShowErrorDialogOnDisconnect(DisconnectCause disconnectCause) {
- Log.d(this, "maybeShowErrorDialogOnDisconnect");
-
- if (!isFinishing() && !TextUtils.isEmpty(disconnectCause.getDescription())
- && (disconnectCause.getCode() == DisconnectCause.ERROR ||
- disconnectCause.getCode() == DisconnectCause.RESTRICTED)) {
- showErrorDialog(disconnectCause.getDescription());
- }
- }
-
- public void dismissPendingDialogs() {
- if (mDialog != null) {
- mDialog.dismiss();
- mDialog = null;
- }
- if (mAnswerFragment != null) {
- mAnswerFragment.dismissPendingDialogs();
- }
-
- SelectPhoneAccountDialogFragment dialogFragment = (SelectPhoneAccountDialogFragment)
- getFragmentManager().findFragmentByTag(TAG_SELECT_ACCT_FRAGMENT);
- if (dialogFragment != null) {
- dialogFragment.dismiss();
- }
- }
-
- /**
- * Utility function to bring up a generic "error" dialog.
- */
- private void showErrorDialog(CharSequence msg) {
- Log.i(this, "Show Dialog: " + msg);
-
- dismissPendingDialogs();
-
- mDialog = new AlertDialog.Builder(this)
- .setMessage(msg)
- .setPositiveButton(android.R.string.ok, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- onDialogDismissed();
- }
- })
- .setOnCancelListener(new OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- onDialogDismissed();
- }
- })
- .create();
-
- mDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- mDialog.show();
- }
-
- private void onDialogDismissed() {
- mDialog = null;
- CallList.getInstance().onErrorDialogDismissed();
- InCallPresenter.getInstance().onDismissDialog();
- }
-
- public void setExcludeFromRecents(boolean exclude) {
- ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
- List<ActivityManager.AppTask> tasks = am.getAppTasks();
- int taskId = getTaskId();
- for (int i = 0; i < tasks.size(); i++) {
- ActivityManager.AppTask task = tasks.get(i);
- if (task.getTaskInfo().id == taskId) {
- try {
- task.setExcludeFromRecents(exclude);
- } catch (RuntimeException e) {
- Log.e(TAG, "RuntimeException when excluding task from recents.", e);
- }
- }
- }
- }
-
-
- public OnTouchListener getDispatchTouchEventListener() {
- return mDispatchTouchEventListener;
- }
-
- public void setDispatchTouchEventListener(OnTouchListener mDispatchTouchEventListener) {
- this.mDispatchTouchEventListener = mDispatchTouchEventListener;
- }
-
- /**
- * Enables the OrientationEventListener if enable flag is true. Disables it if enable is
- * false
- * @param enable true or false.
- */
- public void enableInCallOrientationEventListener(boolean enable) {
- if (enable) {
- mInCallOrientationEventListener.enable(enable);
- } else {
- mInCallOrientationEventListener.disable();
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/InCallAnimationUtils.java b/InCallUI/src/com/android/incallui/InCallAnimationUtils.java
deleted file mode 100644
index 44bb369e6..000000000
--- a/InCallUI/src/com/android/incallui/InCallAnimationUtils.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * 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.incallui;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.view.ViewPropertyAnimator;
-import android.widget.ImageView;
-
-/**
- * Utilities for Animation.
- */
-public class InCallAnimationUtils {
- private static final String LOG_TAG = InCallAnimationUtils.class.getSimpleName();
- /**
- * Turn on when you're interested in fading animation. Intentionally untied from other debug
- * settings.
- */
- private static final boolean FADE_DBG = false;
-
- /**
- * Duration for animations in msec, which can be used with
- * {@link ViewPropertyAnimator#setDuration(long)} for example.
- */
- public static final int ANIMATION_DURATION = 250;
-
- private InCallAnimationUtils() {
- }
-
- /**
- * Drawable achieving cross-fade, just like TransitionDrawable. We can have
- * call-backs via animator object (see also {@link CrossFadeDrawable#getAnimator()}).
- */
- private static class CrossFadeDrawable extends LayerDrawable {
- private final ObjectAnimator mAnimator;
-
- public CrossFadeDrawable(Drawable[] layers) {
- super(layers);
- mAnimator = ObjectAnimator.ofInt(this, "crossFadeAlpha", 0xff, 0);
- }
-
- private int mCrossFadeAlpha;
-
- /**
- * This will be used from ObjectAnimator.
- * Note: this method is protected by proguard.flags so that it won't be removed
- * automatically.
- */
- @SuppressWarnings("unused")
- public void setCrossFadeAlpha(int alpha) {
- mCrossFadeAlpha = alpha;
- invalidateSelf();
- }
-
- public ObjectAnimator getAnimator() {
- return mAnimator;
- }
-
- @Override
- public void draw(Canvas canvas) {
- Drawable first = getDrawable(0);
- Drawable second = getDrawable(1);
-
- if (mCrossFadeAlpha > 0) {
- first.setAlpha(mCrossFadeAlpha);
- first.draw(canvas);
- first.setAlpha(255);
- }
-
- if (mCrossFadeAlpha < 0xff) {
- second.setAlpha(0xff - mCrossFadeAlpha);
- second.draw(canvas);
- second.setAlpha(0xff);
- }
- }
- }
-
- private static CrossFadeDrawable newCrossFadeDrawable(Drawable first, Drawable second) {
- Drawable[] layers = new Drawable[2];
- layers[0] = first;
- layers[1] = second;
- return new CrossFadeDrawable(layers);
- }
-
- /**
- * Starts cross-fade animation using TransitionDrawable. Nothing will happen if "from" and "to"
- * are the same.
- */
- public static void startCrossFade(
- final ImageView imageView, final Drawable from, final Drawable to) {
- // We skip the cross-fade when those two Drawables are equal, or they are BitmapDrawables
- // pointing to the same Bitmap.
- final boolean drawableIsEqual = (from != null && to != null && from.equals(to));
- final boolean hasFromImage = ((from instanceof BitmapDrawable) &&
- ((BitmapDrawable) from).getBitmap() != null);
- final boolean hasToImage = ((to instanceof BitmapDrawable) &&
- ((BitmapDrawable) to).getBitmap() != null);
- final boolean areSameImage = drawableIsEqual || (hasFromImage && hasToImage &&
- ((BitmapDrawable) from).getBitmap().equals(((BitmapDrawable) to).getBitmap()));
-
- if (!areSameImage) {
- if (FADE_DBG) {
- log("Start cross-fade animation for " + imageView
- + "(" + Integer.toHexString(from.hashCode()) + " -> "
- + Integer.toHexString(to.hashCode()) + ")");
- }
-
- CrossFadeDrawable crossFadeDrawable = newCrossFadeDrawable(from, to);
- ObjectAnimator animator = crossFadeDrawable.getAnimator();
- imageView.setImageDrawable(crossFadeDrawable);
- animator.setDuration(ANIMATION_DURATION);
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- if (FADE_DBG) {
- log("cross-fade animation start ("
- + Integer.toHexString(from.hashCode()) + " -> "
- + Integer.toHexString(to.hashCode()) + ")");
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (FADE_DBG) {
- log("cross-fade animation ended ("
- + Integer.toHexString(from.hashCode()) + " -> "
- + Integer.toHexString(to.hashCode()) + ")");
- }
- animation.removeAllListeners();
- // Workaround for issue 6300562; this will force the drawable to the
- // resultant one regardless of animation glitch.
- imageView.setImageDrawable(to);
- }
- });
- animator.start();
-
- /* We could use TransitionDrawable here, but it may cause some weird animation in
- * some corner cases. See issue 6300562
- * TODO: decide which to be used in the long run. TransitionDrawable is old but system
- * one. Ours uses new animation framework and thus have callback (great for testing),
- * while no framework support for the exact class.
-
- Drawable[] layers = new Drawable[2];
- layers[0] = from;
- layers[1] = to;
- TransitionDrawable transitionDrawable = new TransitionDrawable(layers);
- imageView.setImageDrawable(transitionDrawable);
- transitionDrawable.startTransition(ANIMATION_DURATION); */
- imageView.setTag(to);
- } else if (!hasFromImage && hasToImage) {
- imageView.setImageDrawable(to);
- imageView.setTag(to);
- } else {
- if (FADE_DBG) {
- log("*Not* start cross-fade. " + imageView);
- }
- }
- }
-
- // Debugging / testing code
-
- private static void log(String msg) {
- Log.d(LOG_TAG, msg);
- }
-} \ No newline at end of file
diff --git a/InCallUI/src/com/android/incallui/InCallCameraManager.java b/InCallUI/src/com/android/incallui/InCallCameraManager.java
deleted file mode 100644
index 53000f1dd..000000000
--- a/InCallUI/src/com/android/incallui/InCallCameraManager.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2014 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.incallui;
-
-import android.content.Context;
-import android.graphics.SurfaceTexture;
-import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.params.StreamConfigurationMap;
-import android.util.Size;
-
-import java.lang.String;
-import java.util.Collections;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.Set;
-
-/**
- * Used to track which camera is used for outgoing video.
- */
-public class InCallCameraManager {
-
- public interface Listener {
- void onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera);
- }
-
- private final Set<Listener> mCameraSelectionListeners = Collections.
- newSetFromMap(new ConcurrentHashMap<Listener, Boolean>(8,0.9f,1));
-
- /**
- * The camera ID for the front facing camera.
- */
- private String mFrontFacingCameraId;
-
- /**
- * The camera ID for the rear facing camera.
- */
- private String mRearFacingCameraId;
-
- /**
- * The currently active camera.
- */
- private boolean mUseFrontFacingCamera;
-
- /**
- * Indicates whether the list of cameras has been initialized yet. Initialization is delayed
- * until a video call is present.
- */
- private boolean mIsInitialized = false;
-
- /**
- * The context.
- */
- private Context mContext;
-
- /**
- * Initializes the InCall CameraManager.
- *
- * @param context The current context.
- */
- public InCallCameraManager(Context context) {
- mUseFrontFacingCamera = true;
- mContext = context;
- }
-
- /**
- * Sets whether the front facing camera should be used or not.
- *
- * @param useFrontFacingCamera {@code True} if the front facing camera is to be used.
- */
- public void setUseFrontFacingCamera(boolean useFrontFacingCamera) {
- mUseFrontFacingCamera = useFrontFacingCamera;
- for (Listener listener : mCameraSelectionListeners) {
- listener.onActiveCameraSelectionChanged(mUseFrontFacingCamera);
- }
- }
-
- /**
- * Determines whether the front facing camera is currently in use.
- *
- * @return {@code True} if the front facing camera is in use.
- */
- public boolean isUsingFrontFacingCamera() {
- return mUseFrontFacingCamera;
- }
-
- /**
- * Determines the active camera ID.
- *
- * @return The active camera ID.
- */
- public String getActiveCameraId() {
- maybeInitializeCameraList(mContext);
-
- if (mUseFrontFacingCamera) {
- return mFrontFacingCameraId;
- } else {
- return mRearFacingCameraId;
- }
- }
-
- /**
- * Get the list of cameras available for use.
- *
- * @param context The context.
- */
- private void maybeInitializeCameraList(Context context) {
- if (mIsInitialized || context == null) {
- return;
- }
-
- Log.v(this, "initializeCameraList");
-
- CameraManager cameraManager = null;
- try {
- cameraManager = (CameraManager) context.getSystemService(
- Context.CAMERA_SERVICE);
- } catch (Exception e) {
- Log.e(this, "Could not get camera service.");
- return;
- }
-
- if (cameraManager == null) {
- return;
- }
-
- String[] cameraIds = {};
- try {
- cameraIds = cameraManager.getCameraIdList();
- } catch (CameraAccessException e) {
- Log.d(this, "Could not access camera: "+e);
- // Camera disabled by device policy.
- return;
- }
-
- for (int i = 0; i < cameraIds.length; i++) {
- CameraCharacteristics c = null;
- try {
- c = cameraManager.getCameraCharacteristics(cameraIds[i]);
- } catch (IllegalArgumentException e) {
- // Device Id is unknown.
- } catch (CameraAccessException e) {
- // Camera disabled by device policy.
- }
- if (c != null) {
- int facingCharacteristic = c.get(CameraCharacteristics.LENS_FACING);
- if (facingCharacteristic == CameraCharacteristics.LENS_FACING_FRONT) {
- mFrontFacingCameraId = cameraIds[i];
- } else if (facingCharacteristic == CameraCharacteristics.LENS_FACING_BACK) {
- mRearFacingCameraId = cameraIds[i];
- }
- }
- }
-
- mIsInitialized = true;
- Log.v(this, "initializeCameraList : done");
- }
-
- public void addCameraSelectionListener(Listener listener) {
- if (listener != null) {
- mCameraSelectionListeners.add(listener);
- }
- }
-
- public void removeCameraSelectionListener(Listener listener) {
- if (listener != null) {
- mCameraSelectionListeners.remove(listener);
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/InCallContactInteractions.java b/InCallUI/src/com/android/incallui/InCallContactInteractions.java
deleted file mode 100644
index 88070fe37..000000000
--- a/InCallUI/src/com/android/incallui/InCallContactInteractions.java
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * 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.incallui;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import android.content.Context;
-import android.location.Address;
-import android.text.TextUtils;
-import android.text.format.DateFormat;
-import android.util.Pair;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ImageView;
-import android.widget.ListAdapter;
-import android.widget.RelativeLayout;
-import android.widget.RelativeLayout.LayoutParams;
-import android.widget.TextView;
-
-import com.android.dialer.R;
-
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Wrapper class for objects that are used in generating the context about the contact in the InCall
- * screen.
- *
- * This handles generating the appropriate resource for the ListAdapter based on whether the contact
- * is a business contact or not and logic for the manipulation of data for the call context.
- */
-public class InCallContactInteractions {
- private static final String TAG = InCallContactInteractions.class.getSimpleName();
-
- private Context mContext;
- private InCallContactInteractionsListAdapter mListAdapter;
- private boolean mIsBusiness;
- private View mBusinessHeaderView;
- private LayoutInflater mInflater;
-
- public InCallContactInteractions(Context context, boolean isBusiness) {
- mContext = context;
- mInflater = (LayoutInflater)
- context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- switchContactType(isBusiness);
- }
-
- public InCallContactInteractionsListAdapter getListAdapter() {
- return mListAdapter;
- }
-
- /**
- * Switches the "isBusiness" value, if applicable. Recreates the list adapter with the resource
- * corresponding to the new isBusiness value if the "isBusiness" value is switched.
- *
- * @param isBusiness Whether or not the contact is a business.
- *
- * @return {@code true} if a new list adapter was created, {@code} otherwise.
- */
- public boolean switchContactType(boolean isBusiness) {
- if (mIsBusiness != isBusiness || mListAdapter == null) {
- mIsBusiness = isBusiness;
- mListAdapter = new InCallContactInteractionsListAdapter(mContext,
- mIsBusiness ? R.layout.business_context_info_list_item
- : R.layout.person_context_info_list_item);
- return true;
- }
- return false;
- }
-
- public View getBusinessListHeaderView() {
- if (mBusinessHeaderView == null) {
- mBusinessHeaderView = mInflater.inflate(
- R.layout.business_contact_context_list_header, null);
- }
- return mBusinessHeaderView;
- }
-
- public void setBusinessInfo(Address address, float distance,
- List<Pair<Calendar, Calendar>> openingHours) {
- mListAdapter.clear();
- List<ContactContextInfo> info = new ArrayList<ContactContextInfo>();
-
- // Hours of operation
- if (openingHours != null) {
- BusinessContextInfo hoursInfo = constructHoursInfo(openingHours);
- if (hoursInfo != null) {
- info.add(hoursInfo);
- }
- }
-
- // Location information
- if (address != null) {
- BusinessContextInfo locationInfo = constructLocationInfo(address, distance);
- info.add(locationInfo);
- }
-
- mListAdapter.addAll(info);
- }
-
- /**
- * Construct a BusinessContextInfo object containing hours of operation information.
- * The format is:
- * [Open now/Closed now]
- * [Hours]
- *
- * @param openingHours
- * @return BusinessContextInfo object with the schedule icon, the heading set to whether the
- * business is open or not and the details set to the hours of operation.
- */
- private BusinessContextInfo constructHoursInfo(List<Pair<Calendar, Calendar>> openingHours) {
- try {
- return constructHoursInfo(Calendar.getInstance(), openingHours);
- } catch (Exception e) {
- // Catch all exceptions here because we don't want any crashes if something goes wrong.
- Log.e(TAG, "Error constructing hours info: ", e);
- }
- return null;
- }
-
- /**
- * Pass in arbitrary current calendar time.
- */
- @VisibleForTesting
- BusinessContextInfo constructHoursInfo(Calendar currentTime,
- List<Pair<Calendar, Calendar>> openingHours) {
- if (currentTime == null || openingHours == null || openingHours.size() == 0) {
- return null;
- }
-
- BusinessContextInfo hoursInfo = new BusinessContextInfo();
- hoursInfo.iconId = R.drawable.ic_schedule_white_24dp;
-
- boolean isOpenNow = false;
- // This variable records which interval the current time is after. 0 denotes none of the
- // intervals, 1 after the first interval, etc. It is also the index of the interval the
- // current time is in (if open) or the next interval (if closed).
- int afterInterval = 0;
- // This variable counts the number of time intervals in today's opening hours.
- int todaysIntervalCount = 0;
-
- for (Pair<Calendar, Calendar> hours : openingHours) {
- if (hours.first.compareTo(currentTime) <= 0
- && currentTime.compareTo(hours.second) < 0) {
- // If the current time is on or after the opening time and strictly before the
- // closing time, then this business is open.
- isOpenNow = true;
- }
-
- if (currentTime.get(Calendar.DAY_OF_YEAR) == hours.first.get(Calendar.DAY_OF_YEAR)) {
- todaysIntervalCount += 1;
- }
-
- if (currentTime.compareTo(hours.second) > 0) {
- // This assumes that the list of intervals is sorted by time.
- afterInterval += 1;
- }
- }
-
- hoursInfo.heading = isOpenNow ? mContext.getString(R.string.open_now)
- : mContext.getString(R.string.closed_now);
-
- /*
- * The following logic determines what to display in various cases for hours of operation.
- *
- * - Display all intervals if open now and number of intervals is <=2.
- * - Display next closing time if open now and number of intervals is >2.
- * - Display next opening time if currently closed but opens later today.
- * - Display last time it closed today if closed now and tomorrow's hours are unknown.
- * - Display tomorrow's first open time if closed today and tomorrow's hours are known.
- *
- * NOTE: The logic below assumes that the intervals are sorted by ascending time. Possible
- * TODO to modify the logic above and ensure this is true.
- */
- if (isOpenNow) {
- if (todaysIntervalCount == 1) {
- hoursInfo.detail = getTimeSpanStringForHours(openingHours.get(0));
- } else if (todaysIntervalCount == 2) {
- hoursInfo.detail = mContext.getString(
- R.string.opening_hours,
- getTimeSpanStringForHours(openingHours.get(0)),
- getTimeSpanStringForHours(openingHours.get(1)));
- } else if (afterInterval < openingHours.size()) {
- // This check should not be necessary since if it is currently open, we should not
- // be after the last interval, but just in case, we don't want to crash.
- hoursInfo.detail = mContext.getString(
- R.string.closes_today_at,
- getFormattedTimeForCalendar(openingHours.get(afterInterval).second));
- }
- } else { // Currently closed
- final int lastIntervalToday = todaysIntervalCount - 1;
- if (todaysIntervalCount == 0) { // closed today
- hoursInfo.detail = mContext.getString(
- R.string.opens_tomorrow_at,
- getFormattedTimeForCalendar(openingHours.get(0).first));
- } else if (currentTime.after(openingHours.get(lastIntervalToday).second)) {
- // Passed hours for today
- if (todaysIntervalCount < openingHours.size()) {
- // If all of today's intervals are exhausted, assume the next are tomorrow's.
- hoursInfo.detail = mContext.getString(
- R.string.opens_tomorrow_at,
- getFormattedTimeForCalendar(
- openingHours.get(todaysIntervalCount).first));
- } else {
- // Grab the last time it was open today.
- hoursInfo.detail = mContext.getString(
- R.string.closed_today_at,
- getFormattedTimeForCalendar(
- openingHours.get(lastIntervalToday).second));
- }
- } else if (afterInterval < openingHours.size()) {
- // This check should not be necessary since if it is currently before the last
- // interval, afterInterval should be less than the count of intervals, but just in
- // case, we don't want to crash.
- hoursInfo.detail = mContext.getString(
- R.string.opens_today_at,
- getFormattedTimeForCalendar(openingHours.get(afterInterval).first));
- }
- }
-
- return hoursInfo;
- }
-
- String getFormattedTimeForCalendar(Calendar calendar) {
- return DateFormat.getTimeFormat(mContext).format(calendar.getTime());
- }
-
- String getTimeSpanStringForHours(Pair<Calendar, Calendar> hours) {
- return mContext.getString(R.string.open_time_span,
- getFormattedTimeForCalendar(hours.first),
- getFormattedTimeForCalendar(hours.second));
- }
-
- /**
- * Construct a BusinessContextInfo object with the location information of the business.
- * The format is:
- * [Straight line distance in miles or kilometers]
- * [Address without state/country/etc.]
- *
- * @param address An Address object containing address details of the business
- * @param distance The distance to the location in meters
- * @return A BusinessContextInfo object with the location icon, the heading as the distance to
- * the business and the details containing the address.
- */
- private BusinessContextInfo constructLocationInfo(Address address, float distance) {
- return constructLocationInfo(Locale.getDefault(), address, distance);
- }
-
- @VisibleForTesting
- BusinessContextInfo constructLocationInfo(Locale locale, Address address,
- float distance) {
- if (address == null) {
- return null;
- }
-
- BusinessContextInfo locationInfo = new BusinessContextInfo();
- locationInfo.iconId = R.drawable.ic_location_on_white_24dp;
- if (distance != DistanceHelper.DISTANCE_NOT_FOUND) {
- //TODO: add a setting to allow the user to select "KM" or "MI" as their distance units.
- if (Locale.US.equals(locale)) {
- locationInfo.heading = mContext.getString(R.string.distance_imperial_away,
- distance * DistanceHelper.MILES_PER_METER);
- } else {
- locationInfo.heading = mContext.getString(R.string.distance_metric_away,
- distance * DistanceHelper.KILOMETERS_PER_METER);
- }
- }
- if (address.getLocality() != null) {
- locationInfo.detail = mContext.getString(
- R.string.display_address,
- address.getAddressLine(0),
- address.getLocality());
- } else {
- locationInfo.detail = address.getAddressLine(0);
- }
- return locationInfo;
- }
-
- /**
- * Get the appropriate title for the context.
- * @return The "Business info" title for a business contact and the "Recent messages" title for
- * personal contacts.
- */
- public String getContactContextTitle() {
- return mIsBusiness
- ? mContext.getResources().getString(R.string.business_contact_context_title)
- : mContext.getResources().getString(R.string.person_contact_context_title);
- }
-
- public static abstract class ContactContextInfo {
- public abstract void bindView(View listItem);
- }
-
- public static class BusinessContextInfo extends ContactContextInfo {
- int iconId;
- String heading;
- String detail;
-
- @Override
- public void bindView(View listItem) {
- ImageView imageView = (ImageView) listItem.findViewById(R.id.icon);
- TextView headingTextView = (TextView) listItem.findViewById(R.id.heading);
- TextView detailTextView = (TextView) listItem.findViewById(R.id.detail);
-
- if (this.iconId == 0 || (this.heading == null && this.detail == null)) {
- return;
- }
-
- imageView.setImageDrawable(listItem.getContext().getDrawable(this.iconId));
-
- headingTextView.setText(this.heading);
- headingTextView.setVisibility(TextUtils.isEmpty(this.heading)
- ? View.GONE : View.VISIBLE);
-
- detailTextView.setText(this.detail);
- detailTextView.setVisibility(TextUtils.isEmpty(this.detail)
- ? View.GONE : View.VISIBLE);
-
- }
- }
-
- public static class PersonContextInfo extends ContactContextInfo {
- boolean isIncoming;
- String message;
- String detail;
-
- @Override
- public void bindView(View listItem) {
- TextView messageTextView = (TextView) listItem.findViewById(R.id.message);
- TextView detailTextView = (TextView) listItem.findViewById(R.id.detail);
-
- if (this.message == null || this.detail == null) {
- return;
- }
-
- messageTextView.setBackgroundResource(this.isIncoming ?
- R.drawable.incoming_sms_background : R.drawable.outgoing_sms_background);
- messageTextView.setText(this.message);
- LayoutParams messageLayoutParams = (LayoutParams) messageTextView.getLayoutParams();
- messageLayoutParams.addRule(this.isIncoming?
- RelativeLayout.ALIGN_PARENT_START : RelativeLayout.ALIGN_PARENT_END);
- messageTextView.setLayoutParams(messageLayoutParams);
-
- LayoutParams detailLayoutParams = (LayoutParams) detailTextView.getLayoutParams();
- detailLayoutParams.addRule(this.isIncoming ?
- RelativeLayout.ALIGN_PARENT_START : RelativeLayout.ALIGN_PARENT_END);
- detailTextView.setLayoutParams(detailLayoutParams);
- detailTextView.setText(this.detail);
- }
- }
-
- /**
- * A list adapter for call context information. We use the same adapter for both business and
- * contact context.
- */
- private class InCallContactInteractionsListAdapter extends ArrayAdapter<ContactContextInfo> {
- // The resource id of the list item layout.
- int mResId;
-
- public InCallContactInteractionsListAdapter(Context context, int resource) {
- super(context, resource);
- mResId = resource;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- View listItem = mInflater.inflate(mResId, null);
- ContactContextInfo item = getItem(position);
-
- if (item == null) {
- return listItem;
- }
-
- item.bindView(listItem);
- return listItem;
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/InCallDateUtils.java b/InCallUI/src/com/android/incallui/InCallDateUtils.java
deleted file mode 100644
index 3401692ea..000000000
--- a/InCallUI/src/com/android/incallui/InCallDateUtils.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.android.incallui;
-
-import android.icu.text.MeasureFormat;
-import android.icu.text.MeasureFormat.FormatWidth;
-import android.icu.util.Measure;
-import android.icu.util.MeasureUnit;
-
-import java.util.ArrayList;
-import java.util.Locale;
-
-/**
- * Methods to parse time and date information in the InCallUi
- */
-public class InCallDateUtils {
-
- /**
- * Return given duration in a human-friendly format. For example, "4 minutes 3 seconds" or
- * "3 hours 1 second". Returns the hours, minutes and seconds in that order if they exist.
- */
- public static String formatDuration(long millis) {
- int hours = 0;
- int minutes = 0;
- int seconds = 0;
- int elapsedSeconds = (int) (millis / 1000);
- if (elapsedSeconds >= 3600) {
- hours = elapsedSeconds / 3600;
- elapsedSeconds -= hours * 3600;
- }
- if (elapsedSeconds >= 60) {
- minutes = elapsedSeconds / 60;
- elapsedSeconds -= minutes * 60;
- }
- seconds = elapsedSeconds;
-
- final ArrayList<Measure> measures = new ArrayList<Measure>();
- if (hours > 0) {
- measures.add(new Measure(hours, MeasureUnit.HOUR));
- }
- if (minutes > 0) {
- measures.add(new Measure(minutes, MeasureUnit.MINUTE));
- }
- if (seconds > 0) {
- measures.add(new Measure(seconds, MeasureUnit.SECOND));
- }
-
- if (measures.isEmpty()) {
- return "";
- } else {
- return MeasureFormat.getInstance(Locale.getDefault(), FormatWidth.WIDE)
- .formatMeasures(measures.toArray(new Measure[measures.size()]));
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/InCallOrientationEventListener.java b/InCallUI/src/com/android/incallui/InCallOrientationEventListener.java
deleted file mode 100644
index 3cab6dc3b..000000000
--- a/InCallUI/src/com/android/incallui/InCallOrientationEventListener.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * 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.incallui;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.view.OrientationEventListener;
-import android.hardware.SensorManager;
-import android.view.Surface;
-import android.content.pm.ActivityInfo;
-
-/**
- * This class listens to Orientation events and overrides onOrientationChanged which gets
- * invoked when an orientation change occurs. When that happens, we notify InCallUI registrants
- * of the change.
- */
-public class InCallOrientationEventListener extends OrientationEventListener {
-
- /**
- * Screen orientation angles one of 0, 90, 180, 270, 360 in degrees.
- */
- public static int SCREEN_ORIENTATION_0 = 0;
- public static int SCREEN_ORIENTATION_90 = 90;
- public static int SCREEN_ORIENTATION_180 = 180;
- public static int SCREEN_ORIENTATION_270 = 270;
- public static int SCREEN_ORIENTATION_360 = 360;
-
- public static int FULL_SENSOR_SCREEN_ORIENTATION =
- ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR;
-
- public static int NO_SENSOR_SCREEN_ORIENTATION =
- ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
-
- /**
- * This is to identify dead zones where we won't notify others of orientation changed.
- * Say for e.g our threshold is x degrees. We will only notify UI when our current rotation is
- * within x degrees right or left of the screen orientation angles. If it's not within those
- * ranges, we return SCREEN_ORIENTATION_UNKNOWN and ignore it.
- */
- private static int SCREEN_ORIENTATION_UNKNOWN = -1;
-
- // Rotation threshold is 10 degrees. So if the rotation angle is within 10 degrees of any of
- // the above angles, we will notify orientation changed.
- private static int ROTATION_THRESHOLD = 10;
-
-
- /**
- * Cache the current rotation of the device.
- */
- private static int sCurrentOrientation = SCREEN_ORIENTATION_0;
- private boolean mEnabled = false;
-
- public InCallOrientationEventListener(Context context) {
- super(context);
- }
-
- /**
- * Handles changes in device orientation. Notifies InCallPresenter of orientation changes.
- *
- * Note that this API receives sensor rotation in degrees as a param and we convert that to
- * one of our screen orientation constants - (one of: {@link SCREEN_ORIENTATION_0},
- * {@link SCREEN_ORIENTATION_90}, {@link SCREEN_ORIENTATION_180},
- * {@link SCREEN_ORIENTATION_270}).
- *
- * @param rotation The new device sensor rotation in degrees
- */
- @Override
- public void onOrientationChanged(int rotation) {
- if (rotation == OrientationEventListener.ORIENTATION_UNKNOWN) {
- return;
- }
-
- final int orientation = toScreenOrientation(rotation);
-
- if (orientation != SCREEN_ORIENTATION_UNKNOWN && sCurrentOrientation != orientation) {
- sCurrentOrientation = orientation;
- InCallPresenter.getInstance().onDeviceOrientationChange(sCurrentOrientation);
- }
- }
-
- /**
- * Enables the OrientationEventListener and notifies listeners of current orientation if
- * notify flag is true
- * @param notify true or false. Notify device orientation changed if true.
- */
- public void enable(boolean notify) {
- if (mEnabled) {
- Log.v(this, "enable: Orientation listener is already enabled. Ignoring...");
- return;
- }
-
- super.enable();
- mEnabled = true;
- if (notify) {
- InCallPresenter.getInstance().onDeviceOrientationChange(sCurrentOrientation);
- }
- }
-
- /**
- * Enables the OrientationEventListener with notify flag defaulting to false.
- */
- public void enable() {
- enable(false);
- }
-
- /**
- * Disables the OrientationEventListener.
- */
- public void disable() {
- if (!mEnabled) {
- Log.v(this, "enable: Orientation listener is already disabled. Ignoring...");
- return;
- }
-
- mEnabled = false;
- super.disable();
- }
-
- /**
- * Returns true the OrientationEventListener is enabled, false otherwise.
- */
- public boolean isEnabled() {
- return mEnabled;
- }
-
- /**
- * Converts sensor rotation in degrees to screen orientation constants.
- * @param rotation sensor rotation angle in degrees
- * @return Screen orientation angle in degrees (0, 90, 180, 270). Returns -1 for degrees not
- * within threshold to identify zones where orientation change should not be trigerred.
- */
- private int toScreenOrientation(int rotation) {
- // Sensor orientation 90 is equivalent to screen orientation 270 and vice versa. This
- // function returns the screen orientation. So we convert sensor rotation 90 to 270 and
- // vice versa here.
- if (isInLeftRange(rotation, SCREEN_ORIENTATION_360, ROTATION_THRESHOLD) ||
- isInRightRange(rotation, SCREEN_ORIENTATION_0, ROTATION_THRESHOLD)) {
- return SCREEN_ORIENTATION_0;
- } else if (isWithinThreshold(rotation, SCREEN_ORIENTATION_90, ROTATION_THRESHOLD)) {
- return SCREEN_ORIENTATION_270;
- } else if (isWithinThreshold(rotation, SCREEN_ORIENTATION_180, ROTATION_THRESHOLD)) {
- return SCREEN_ORIENTATION_180;
- } else if (isWithinThreshold(rotation, SCREEN_ORIENTATION_270, ROTATION_THRESHOLD)) {
- return SCREEN_ORIENTATION_90;
- }
- return SCREEN_ORIENTATION_UNKNOWN;
- }
-
- private static boolean isWithinRange(int value, int begin, int end) {
- return value >= begin && value < end;
- }
-
- private static boolean isWithinThreshold(int value, int center, int threshold) {
- return isWithinRange(value, center - threshold, center + threshold);
- }
-
- private static boolean isInLeftRange(int value, int center, int threshold) {
- return isWithinRange(value, center - threshold, center);
- }
-
- private static boolean isInRightRange(int value, int center, int threshold) {
- return isWithinRange(value, center, center + threshold);
- }
-}
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
deleted file mode 100644
index 0103f61ed..000000000
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ /dev/null
@@ -1,1938 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import com.google.common.base.Preconditions;
-
-import android.app.ActivityManager.TaskDescription;
-import android.app.FragmentManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.graphics.Point;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.provider.CallLog;
-import android.telecom.DisconnectCause;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telecom.TelecomManager;
-import android.telecom.VideoProfile;
-import android.telephony.PhoneStateListener;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.compat.CallSdkCompat;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
-import com.android.contacts.common.interactions.TouchPointManager;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
-import com.android.dialer.R;
-import com.android.dialer.calllog.CallLogAsyncTaskUtil;
-import com.android.dialer.calllog.CallLogAsyncTaskUtil.OnCallLogQueryFinishedListener;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener;
-import com.android.dialer.filterednumber.FilteredNumbersUtil;
-import com.android.dialer.logging.InteractionEvent;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.util.TelecomUtil;
-import com.android.incallui.spam.SpamCallListListener;
-import com.android.incallui.util.TelecomCallUtil;
-import com.android.incalluibind.ObjectFactory;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Takes updates from the CallList and notifies the InCallActivity (UI)
- * of the changes.
- * Responsible for starting the activity for a new call and finishing the activity when all calls
- * are disconnected.
- * Creates and manages the in-call state and provides a listener pattern for the presenters
- * that want to listen in on the in-call state changes.
- * TODO: This class has become more of a state machine at this point. Consider renaming.
- */
-public class InCallPresenter implements CallList.Listener,
- CircularRevealFragment.OnCircularRevealCompleteListener,
- InCallVideoCallCallbackNotifier.SessionModificationListener {
-
- private static final String EXTRA_FIRST_TIME_SHOWN =
- "com.android.incallui.intent.extra.FIRST_TIME_SHOWN";
-
- private static final long BLOCK_QUERY_TIMEOUT_MS = 1000;
-
- private static final Bundle EMPTY_EXTRAS = new Bundle();
-
- private static InCallPresenter sInCallPresenter;
-
- /**
- * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
- * load factor before resizing, 1 means we only expect a single thread to
- * access the map so make only a single shard
- */
- private final Set<InCallStateListener> mListeners = Collections.newSetFromMap(
- new ConcurrentHashMap<InCallStateListener, Boolean>(8, 0.9f, 1));
- private final List<IncomingCallListener> mIncomingCallListeners = new CopyOnWriteArrayList<>();
- private final Set<InCallDetailsListener> mDetailsListeners = Collections.newSetFromMap(
- new ConcurrentHashMap<InCallDetailsListener, Boolean>(8, 0.9f, 1));
- private final Set<CanAddCallListener> mCanAddCallListeners = Collections.newSetFromMap(
- new ConcurrentHashMap<CanAddCallListener, Boolean>(8, 0.9f, 1));
- private final Set<InCallUiListener> mInCallUiListeners = Collections.newSetFromMap(
- new ConcurrentHashMap<InCallUiListener, Boolean>(8, 0.9f, 1));
- private final Set<InCallOrientationListener> mOrientationListeners = Collections.newSetFromMap(
- new ConcurrentHashMap<InCallOrientationListener, Boolean>(8, 0.9f, 1));
- private final Set<InCallEventListener> mInCallEventListeners = Collections.newSetFromMap(
- new ConcurrentHashMap<InCallEventListener, Boolean>(8, 0.9f, 1));
-
- private AudioModeProvider mAudioModeProvider;
- private StatusBarNotifier mStatusBarNotifier;
- private ExternalCallNotifier mExternalCallNotifier;
- private ContactInfoCache mContactInfoCache;
- private Context mContext;
- private CallList mCallList;
- private ExternalCallList mExternalCallList;
- private InCallActivity mInCallActivity;
- private InCallState mInCallState = InCallState.NO_CALLS;
- private ProximitySensor mProximitySensor;
- private boolean mServiceConnected = false;
- private boolean mAccountSelectionCancelled = false;
- private InCallCameraManager mInCallCameraManager = null;
- private AnswerPresenter mAnswerPresenter = new AnswerPresenter();
- private FilteredNumberAsyncQueryHandler mFilteredQueryHandler;
- private CallList.Listener mSpamCallListListener;
-
- /**
- * Whether or not we are currently bound and waiting for Telecom to send us a new call.
- */
- private boolean mBoundAndWaitingForOutgoingCall;
-
- /**
- * If there is no actual call currently in the call list, this will be used as a fallback
- * to determine the theme color for InCallUI.
- */
- private PhoneAccountHandle mPendingPhoneAccountHandle;
-
- /**
- * Determines if the InCall UI is in fullscreen mode or not.
- */
- private boolean mIsFullScreen = false;
-
- private final android.telecom.Call.Callback mCallCallback = new android.telecom.Call.Callback() {
- @Override
- public void onPostDialWait(android.telecom.Call telecomCall,
- String remainingPostDialSequence) {
- final Call call = mCallList.getCallByTelecomCall(telecomCall);
- if (call == null) {
- Log.w(this, "Call not found in call list: " + telecomCall);
- return;
- }
- onPostDialCharWait(call.getId(), remainingPostDialSequence);
- }
-
- @Override
- public void onDetailsChanged(android.telecom.Call telecomCall,
- android.telecom.Call.Details details) {
- final Call call = mCallList.getCallByTelecomCall(telecomCall);
- if (call == null) {
- Log.w(this, "Call not found in call list: " + telecomCall);
- return;
- }
- for (InCallDetailsListener listener : mDetailsListeners) {
- listener.onDetailsChanged(call, details);
- }
- }
-
- @Override
- public void onConferenceableCallsChanged(android.telecom.Call telecomCall,
- List<android.telecom.Call> conferenceableCalls) {
- Log.i(this, "onConferenceableCallsChanged: " + telecomCall);
- onDetailsChanged(telecomCall, telecomCall.getDetails());
- }
- };
-
- private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- public void onCallStateChanged(int state, String incomingNumber) {
- if (state == TelephonyManager.CALL_STATE_RINGING) {
- if (FilteredNumbersUtil.hasRecentEmergencyCall(mContext)) {
- return;
- }
- // Check if the number is blocked, to silence the ringer.
- String countryIso = GeoUtil.getCurrentCountryIso(mContext);
- mFilteredQueryHandler.isBlockedNumber(
- mOnCheckBlockedListener, incomingNumber, countryIso);
- }
- }
- };
-
- private final OnCheckBlockedListener mOnCheckBlockedListener = new OnCheckBlockedListener() {
- @Override
- public void onCheckComplete(final Integer id) {
- if (id != null) {
- // Silence the ringer now to prevent ringing and vibration before the call is
- // terminated when Telecom attempts to add it.
- TelecomUtil.silenceRinger(mContext);
- }
- }
- };
-
- /**
- * Observes the CallLog to delete the call log entry for the blocked call after it is added.
- * Times out if too much time has passed.
- */
- private class BlockedNumberContentObserver extends ContentObserver {
- private static final int TIMEOUT_MS = 5000;
-
- private Handler mHandler;
- private String mNumber;
- private long mTimeAddedMs;
-
- private Runnable mTimeoutRunnable = new Runnable() {
- @Override
- public void run() {
- unregister();
- }
- };
-
- public BlockedNumberContentObserver(Handler handler, String number, long timeAddedMs) {
- super(handler);
-
- mHandler = handler;
- mNumber = number;
- mTimeAddedMs = timeAddedMs;
- }
-
- @Override
- public void onChange(boolean selfChange) {
- CallLogAsyncTaskUtil.deleteBlockedCall(mContext, mNumber, mTimeAddedMs,
- new OnCallLogQueryFinishedListener() {
- @Override
- public void onQueryFinished(boolean hasEntry) {
- if (mContext != null && hasEntry) {
- unregister();
- }
- }
- });
- }
-
- public void register() {
- if (mContext != null) {
- mContext.getContentResolver().registerContentObserver(
- CallLog.CONTENT_URI, true, this);
- mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS);
- }
- }
-
- private void unregister() {
- if (mContext != null) {
- mHandler.removeCallbacks(mTimeoutRunnable);
- mContext.getContentResolver().unregisterContentObserver(this);
- }
- }
- };
-
- /**
- * Is true when the activity has been previously started. Some code needs to know not just if
- * the activity is currently up, but if it had been previously shown in foreground for this
- * in-call session (e.g., StatusBarNotifier). This gets reset when the session ends in the
- * tear-down method.
- */
- private boolean mIsActivityPreviouslyStarted = false;
-
- /**
- * Whether or not InCallService is bound to Telecom.
- */
- private boolean mServiceBound = false;
-
- /**
- * When configuration changes Android kills the current activity and starts a new one.
- * The flag is used to check if full clean up is necessary (activity is stopped and new
- * activity won't be started), or if a new activity will be started right after the current one
- * is destroyed, and therefore no need in release all resources.
- */
- private boolean mIsChangingConfigurations = false;
-
- /** Display colors for the UI. Consists of a primary color and secondary (darker) color */
- private MaterialPalette mThemeColors;
-
- private TelecomManager mTelecomManager;
- private TelephonyManager mTelephonyManager;
-
- public static synchronized InCallPresenter getInstance() {
- if (sInCallPresenter == null) {
- sInCallPresenter = new InCallPresenter();
- }
- return sInCallPresenter;
- }
-
- @NeededForTesting
- static synchronized void setInstance(InCallPresenter inCallPresenter) {
- sInCallPresenter = inCallPresenter;
- }
-
- public InCallState getInCallState() {
- return mInCallState;
- }
-
- public CallList getCallList() {
- return mCallList;
- }
-
- public void setUp(Context context,
- CallList callList,
- ExternalCallList externalCallList,
- AudioModeProvider audioModeProvider,
- StatusBarNotifier statusBarNotifier,
- ExternalCallNotifier externalCallNotifier,
- ContactInfoCache contactInfoCache,
- ProximitySensor proximitySensor) {
- if (mServiceConnected) {
- Log.i(this, "New service connection replacing existing one.");
- // retain the current resources, no need to create new ones.
- Preconditions.checkState(context == mContext);
- Preconditions.checkState(callList == mCallList);
- Preconditions.checkState(audioModeProvider == mAudioModeProvider);
- return;
- }
-
- Preconditions.checkNotNull(context);
- mContext = context;
-
- mContactInfoCache = contactInfoCache;
-
- mStatusBarNotifier = statusBarNotifier;
- mExternalCallNotifier = externalCallNotifier;
- addListener(mStatusBarNotifier);
-
- mAudioModeProvider = audioModeProvider;
-
- mProximitySensor = proximitySensor;
- addListener(mProximitySensor);
-
- addIncomingCallListener(mAnswerPresenter);
- addInCallUiListener(mAnswerPresenter);
-
- mCallList = callList;
- mExternalCallList = externalCallList;
- externalCallList.addExternalCallListener(mExternalCallNotifier);
-
- // This only gets called by the service so this is okay.
- mServiceConnected = true;
-
- // The final thing we do in this set up is add ourselves as a listener to CallList. This
- // will kick off an update and the whole process can start.
- mCallList.addListener(this);
-
- // Create spam call list listener and add it to the list of listeners
- mSpamCallListListener = new SpamCallListListener(context);
- mCallList.addListener(mSpamCallListListener);
-
- VideoPauseController.getInstance().setUp(this);
- InCallVideoCallCallbackNotifier.getInstance().addSessionModificationListener(this);
-
- mFilteredQueryHandler = new FilteredNumberAsyncQueryHandler(context.getContentResolver());
- mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
- mCallList.setExtendedCallInfoService(
- com.android.dialerbind.ObjectFactory.newExtendedCallInfoService(context));
-
- Log.d(this, "Finished InCallPresenter.setUp");
- }
-
- /**
- * Called when the telephony service has disconnected from us. This will happen when there are
- * no more active calls. However, we may still want to continue showing the UI for
- * certain cases like showing "Call Ended".
- * What we really want is to wait for the activity and the service to both disconnect before we
- * tear things down. This method sets a serviceConnected boolean and calls a secondary method
- * that performs the aforementioned logic.
- */
- public void tearDown() {
- Log.d(this, "tearDown");
- mCallList.clearOnDisconnect();
-
- mServiceConnected = false;
- attemptCleanup();
-
- mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
- VideoPauseController.getInstance().tearDown();
- InCallVideoCallCallbackNotifier.getInstance().removeSessionModificationListener(this);
- }
-
- private void attemptFinishActivity() {
- final boolean doFinish = (mInCallActivity != null && isActivityStarted());
- Log.i(this, "Hide in call UI: " + doFinish);
- if (doFinish) {
- mInCallActivity.setExcludeFromRecents(true);
- mInCallActivity.finish();
-
- if (mAccountSelectionCancelled) {
- // This finish is a result of account selection cancellation
- // do not include activity ending transition
- mInCallActivity.overridePendingTransition(0, 0);
- }
- }
- }
-
- /**
- * Called when the UI begins, and starts the callstate callbacks if necessary.
- */
- public void setActivity(InCallActivity inCallActivity) {
- if (inCallActivity == null) {
- throw new IllegalArgumentException("registerActivity cannot be called with null");
- }
- if (mInCallActivity != null && mInCallActivity != inCallActivity) {
- Log.w(this, "Setting a second activity before destroying the first.");
- }
- updateActivity(inCallActivity);
- }
-
- /**
- * Called when the UI ends. Attempts to tear down everything if necessary. See
- * {@link #tearDown()} for more insight on the tear-down process.
- */
- public void unsetActivity(InCallActivity inCallActivity) {
- if (inCallActivity == null) {
- throw new IllegalArgumentException("unregisterActivity cannot be called with null");
- }
- if (mInCallActivity == null) {
- Log.i(this, "No InCallActivity currently set, no need to unset.");
- return;
- }
- if (mInCallActivity != inCallActivity) {
- Log.w(this, "Second instance of InCallActivity is trying to unregister when another"
- + " instance is active. Ignoring.");
- return;
- }
- updateActivity(null);
- }
-
- /**
- * Updates the current instance of {@link InCallActivity} with the provided one. If a
- * {@code null} activity is provided, it means that the activity was finished and we should
- * attempt to cleanup.
- */
- private void updateActivity(InCallActivity inCallActivity) {
- boolean updateListeners = false;
- boolean doAttemptCleanup = false;
-
- if (inCallActivity != null) {
- if (mInCallActivity == null) {
- updateListeners = true;
- Log.i(this, "UI Initialized");
- } else {
- // since setActivity is called onStart(), it can be called multiple times.
- // This is fine and ignorable, but we do not want to update the world every time
- // this happens (like going to/from background) so we do not set updateListeners.
- }
-
- mInCallActivity = inCallActivity;
- mInCallActivity.setExcludeFromRecents(false);
-
- // By the time the UI finally comes up, the call may already be disconnected.
- // If that's the case, we may need to show an error dialog.
- if (mCallList != null && mCallList.getDisconnectedCall() != null) {
- maybeShowErrorDialogOnDisconnect(mCallList.getDisconnectedCall());
- }
-
- // When the UI comes up, we need to first check the in-call state.
- // If we are showing NO_CALLS, that means that a call probably connected and
- // then immediately disconnected before the UI was able to come up.
- // If we dont have any calls, start tearing down the UI instead.
- // NOTE: This code relies on {@link #mInCallActivity} being set so we run it after
- // it has been set.
- if (mInCallState == InCallState.NO_CALLS) {
- Log.i(this, "UI Initialized, but no calls left. shut down.");
- attemptFinishActivity();
- return;
- }
- } else {
- Log.i(this, "UI Destroyed");
- updateListeners = true;
- mInCallActivity = null;
-
- // We attempt cleanup for the destroy case but only after we recalculate the state
- // to see if we need to come back up or stay shut down. This is why we do the
- // cleanup after the call to onCallListChange() instead of directly here.
- doAttemptCleanup = true;
- }
-
- // Messages can come from the telephony layer while the activity is coming up
- // and while the activity is going down. So in both cases we need to recalculate what
- // state we should be in after they complete.
- // Examples: (1) A new incoming call could come in and then get disconnected before
- // the activity is created.
- // (2) All calls could disconnect and then get a new incoming call before the
- // activity is destroyed.
- //
- // b/1122139 - We previously had a check for mServiceConnected here as well, but there are
- // cases where we need to recalculate the current state even if the service in not
- // connected. In particular the case where startOrFinish() is called while the app is
- // already finish()ing. In that case, we skip updating the state with the knowledge that
- // we will check again once the activity has finished. That means we have to recalculate the
- // state here even if the service is disconnected since we may not have finished a state
- // transition while finish()ing.
- if (updateListeners) {
- onCallListChange(mCallList);
- }
-
- if (doAttemptCleanup) {
- attemptCleanup();
- }
- }
-
- private boolean mAwaitingCallListUpdate = false;
-
- public void onBringToForeground(boolean showDialpad) {
- Log.i(this, "Bringing UI to foreground.");
- bringToForeground(showDialpad);
- }
-
- public void onCallAdded(final android.telecom.Call call) {
- LatencyReport latencyReport = new LatencyReport(call);
- if (shouldAttemptBlocking(call)) {
- maybeBlockCall(call, latencyReport);
- } else {
- latencyReport.onCallBlockingDone();
- if (call.getDetails()
- .hasProperty(CallSdkCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) {
- mExternalCallList.onCallAdded(call);
- } else {
- mCallList.onCallAdded(call, latencyReport);
- }
- }
-
- // Since a call has been added we are no longer waiting for Telecom to send us a call.
- setBoundAndWaitingForOutgoingCall(false, null);
- call.registerCallback(mCallCallback);
- }
-
- private boolean shouldAttemptBlocking(android.telecom.Call call) {
- if (call.getState() != android.telecom.Call.STATE_RINGING) {
- return false;
- }
- if (TelecomCallUtil.isEmergencyCall(call)) {
- Log.i(this, "Not attempting to block incoming emergency call");
- return false;
- }
- if (FilteredNumbersUtil.hasRecentEmergencyCall(mContext)) {
- Log.i(this, "Not attempting to block incoming call due to recent emergency call");
- return false;
- }
- if (call.getDetails().hasProperty(CallSdkCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Checks whether a call should be blocked, and blocks it if so. Otherwise, it adds the call
- * to the CallList so it can proceed as normal. There is a timeout, so if the function for
- * checking whether a function is blocked does not return in a reasonable time, we proceed
- * with adding the call anyways.
- */
- private void maybeBlockCall(final android.telecom.Call call,
- final LatencyReport latencyReport) {
- final String countryIso = GeoUtil.getCurrentCountryIso(mContext);
- final String number = TelecomCallUtil.getNumber(call);
- final long timeAdded = System.currentTimeMillis();
-
- // Though AtomicBoolean's can be scary, don't fear, as in this case it is only used on the
- // main UI thread. It is needed so we can change its value within different scopes, since
- // that cannot be done with a final boolean.
- final AtomicBoolean hasTimedOut = new AtomicBoolean(false);
-
- final Handler handler = new Handler();
-
- // Proceed if the query is slow; the call may still be blocked after the query returns.
- final Runnable runnable = new Runnable() {
- public void run() {
- hasTimedOut.set(true);
- latencyReport.onCallBlockingDone();
- mCallList.onCallAdded(call, latencyReport);
- }
- };
- handler.postDelayed(runnable, BLOCK_QUERY_TIMEOUT_MS);
-
- OnCheckBlockedListener onCheckBlockedListener = new OnCheckBlockedListener() {
- @Override
- public void onCheckComplete(final Integer id) {
- if (!hasTimedOut.get()) {
- handler.removeCallbacks(runnable);
- }
- if (id == null) {
- if (!hasTimedOut.get()) {
- latencyReport.onCallBlockingDone();
- mCallList.onCallAdded(call, latencyReport);
- }
- } else {
- Log.i(this, "Rejecting incoming call from blocked number");
- call.reject(false, null);
- Logger.logInteraction(InteractionEvent.CALL_BLOCKED);
-
- mFilteredQueryHandler.incrementFilteredCount(id);
-
- // Register observer to update the call log.
- // BlockedNumberContentObserver will unregister after successful log or timeout.
- BlockedNumberContentObserver contentObserver =
- new BlockedNumberContentObserver(new Handler(), number, timeAdded);
- contentObserver.register();
- }
- }
- };
-
- final boolean success = mFilteredQueryHandler.isBlockedNumber(
- onCheckBlockedListener, number, countryIso);
- if (!success) {
- Log.d(this, "checkForBlockedCall: invalid number, skipping block checking");
- if (!hasTimedOut.get()) {
- handler.removeCallbacks(runnable);
-
- latencyReport.onCallBlockingDone();
- mCallList.onCallAdded(call, latencyReport);
- }
- }
- }
-
- public void onCallRemoved(android.telecom.Call call) {
- if (call.getDetails()
- .hasProperty(CallSdkCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) {
- mExternalCallList.onCallRemoved(call);
- } else {
- mCallList.onCallRemoved(call);
- call.unregisterCallback(mCallCallback);
- }
- }
-
- public void onCanAddCallChanged(boolean canAddCall) {
- for (CanAddCallListener listener : mCanAddCallListeners) {
- listener.onCanAddCallChanged(canAddCall);
- }
- }
-
- /**
- * Called when there is a change to the call list.
- * Sets the In-Call state for the entire in-call app based on the information it gets from
- * CallList. Dispatches the in-call state to all listeners. Can trigger the creation or
- * destruction of the UI based on the states that is calculates.
- */
- @Override
- public void onCallListChange(CallList callList) {
- if (mInCallActivity != null && mInCallActivity.getCallCardFragment() != null &&
- mInCallActivity.getCallCardFragment().isAnimating()) {
- mAwaitingCallListUpdate = true;
- return;
- }
- if (callList == null) {
- return;
- }
-
- mAwaitingCallListUpdate = false;
-
- InCallState newState = getPotentialStateFromCallList(callList);
- InCallState oldState = mInCallState;
- Log.d(this, "onCallListChange oldState= " + oldState + " newState=" + newState);
- newState = startOrFinishUi(newState);
- Log.d(this, "onCallListChange newState changed to " + newState);
-
- // Set the new state before announcing it to the world
- Log.i(this, "Phone switching state: " + oldState + " -> " + newState);
- mInCallState = newState;
-
- // notify listeners of new state
- for (InCallStateListener listener : mListeners) {
- Log.d(this, "Notify " + listener + " of state " + mInCallState.toString());
- listener.onStateChange(oldState, mInCallState, callList);
- }
-
- if (isActivityStarted()) {
- final boolean hasCall = callList.getActiveOrBackgroundCall() != null ||
- callList.getOutgoingCall() != null;
- mInCallActivity.dismissKeyguard(hasCall);
- }
- }
-
- /**
- * Called when there is a new incoming call.
- *
- * @param call
- */
- @Override
- public void onIncomingCall(Call call) {
- InCallState newState = startOrFinishUi(InCallState.INCOMING);
- InCallState oldState = mInCallState;
-
- Log.i(this, "Phone switching state: " + oldState + " -> " + newState);
- mInCallState = newState;
-
- for (IncomingCallListener listener : mIncomingCallListeners) {
- listener.onIncomingCall(oldState, mInCallState, call);
- }
- }
-
- @Override
- public void onUpgradeToVideo(Call call) {
- //NO-OP
- }
- /**
- * Called when a call becomes disconnected. Called everytime an existing call
- * changes from being connected (incoming/outgoing/active) to disconnected.
- */
- @Override
- public void onDisconnect(Call call) {
- maybeShowErrorDialogOnDisconnect(call);
-
- // We need to do the run the same code as onCallListChange.
- onCallListChange(mCallList);
-
- if (isActivityStarted()) {
- mInCallActivity.dismissKeyguard(false);
- }
-
- if (call.isEmergencyCall()) {
- FilteredNumbersUtil.recordLastEmergencyCallTime(mContext);
- }
- }
-
- @Override
- public void onUpgradeToVideoRequest(Call call, int videoState) {
- Log.d(this, "onUpgradeToVideoRequest call = " + call + " video state = " + videoState);
-
- if (call == null) {
- return;
- }
-
- call.setRequestedVideoState(videoState);
- }
-
- /**
- * Given the call list, return the state in which the in-call screen should be.
- */
- public InCallState getPotentialStateFromCallList(CallList callList) {
-
- InCallState newState = InCallState.NO_CALLS;
-
- if (callList == null) {
- return newState;
- }
- if (callList.getIncomingCall() != null) {
- newState = InCallState.INCOMING;
- } else if (callList.getWaitingForAccountCall() != null) {
- newState = InCallState.WAITING_FOR_ACCOUNT;
- } else if (callList.getPendingOutgoingCall() != null) {
- newState = InCallState.PENDING_OUTGOING;
- } else if (callList.getOutgoingCall() != null) {
- newState = InCallState.OUTGOING;
- } else if (callList.getActiveCall() != null ||
- callList.getBackgroundCall() != null ||
- callList.getDisconnectedCall() != null ||
- callList.getDisconnectingCall() != null) {
- newState = InCallState.INCALL;
- }
-
- if (newState == InCallState.NO_CALLS) {
- if (mBoundAndWaitingForOutgoingCall) {
- return InCallState.OUTGOING;
- }
- }
-
- return newState;
- }
-
- public boolean isBoundAndWaitingForOutgoingCall() {
- return mBoundAndWaitingForOutgoingCall;
- }
-
- public void setBoundAndWaitingForOutgoingCall(boolean isBound, PhoneAccountHandle handle) {
- // NOTE: It is possible for there to be a race and have handle become null before
- // the circular reveal starts. This should not cause any problems because CallCardFragment
- // should fallback to the actual call in the CallList at that point in time to determine
- // the theme color.
- Log.i(this, "setBoundAndWaitingForOutgoingCall: " + isBound);
- mBoundAndWaitingForOutgoingCall = isBound;
- mPendingPhoneAccountHandle = handle;
- if (isBound && mInCallState == InCallState.NO_CALLS) {
- mInCallState = InCallState.OUTGOING;
- }
- }
-
- @Override
- public void onCircularRevealComplete(FragmentManager fm) {
- if (mInCallActivity != null) {
- mInCallActivity.showCallCardFragment(true);
- mInCallActivity.getCallCardFragment().animateForNewOutgoingCall();
- CircularRevealFragment.endCircularReveal(mInCallActivity.getFragmentManager());
- }
- }
-
- public void onShrinkAnimationComplete() {
- if (mAwaitingCallListUpdate) {
- onCallListChange(mCallList);
- }
- }
-
- public void addIncomingCallListener(IncomingCallListener listener) {
- Preconditions.checkNotNull(listener);
- mIncomingCallListeners.add(listener);
- }
-
- public void removeIncomingCallListener(IncomingCallListener listener) {
- if (listener != null) {
- mIncomingCallListeners.remove(listener);
- }
- }
-
- public void addListener(InCallStateListener listener) {
- Preconditions.checkNotNull(listener);
- mListeners.add(listener);
- }
-
- public void removeListener(InCallStateListener listener) {
- if (listener != null) {
- mListeners.remove(listener);
- }
- }
-
- public void addDetailsListener(InCallDetailsListener listener) {
- Preconditions.checkNotNull(listener);
- mDetailsListeners.add(listener);
- }
-
- public void removeDetailsListener(InCallDetailsListener listener) {
- if (listener != null) {
- mDetailsListeners.remove(listener);
- }
- }
-
- public void addCanAddCallListener(CanAddCallListener listener) {
- Preconditions.checkNotNull(listener);
- mCanAddCallListeners.add(listener);
- }
-
- public void removeCanAddCallListener(CanAddCallListener listener) {
- if (listener != null) {
- mCanAddCallListeners.remove(listener);
- }
- }
-
- public void addOrientationListener(InCallOrientationListener listener) {
- Preconditions.checkNotNull(listener);
- mOrientationListeners.add(listener);
- }
-
- public void removeOrientationListener(InCallOrientationListener listener) {
- if (listener != null) {
- mOrientationListeners.remove(listener);
- }
- }
-
- public void addInCallEventListener(InCallEventListener listener) {
- Preconditions.checkNotNull(listener);
- mInCallEventListeners.add(listener);
- }
-
- public void removeInCallEventListener(InCallEventListener listener) {
- if (listener != null) {
- mInCallEventListeners.remove(listener);
- }
- }
-
- public ProximitySensor getProximitySensor() {
- return mProximitySensor;
- }
-
- public void handleAccountSelection(PhoneAccountHandle accountHandle, boolean setDefault) {
- if (mCallList != null) {
- Call call = mCallList.getWaitingForAccountCall();
- if (call != null) {
- String callId = call.getId();
- TelecomAdapter.getInstance().phoneAccountSelected(callId, accountHandle, setDefault);
- }
- }
- }
-
- public void cancelAccountSelection() {
- mAccountSelectionCancelled = true;
- if (mCallList != null) {
- Call call = mCallList.getWaitingForAccountCall();
- if (call != null) {
- String callId = call.getId();
- TelecomAdapter.getInstance().disconnectCall(callId);
- }
- }
- }
-
- /**
- * Hangs up any active or outgoing calls.
- */
- public void hangUpOngoingCall(Context context) {
- // By the time we receive this intent, we could be shut down and call list
- // could be null. Bail in those cases.
- if (mCallList == null) {
- if (mStatusBarNotifier == null) {
- // The In Call UI has crashed but the notification still stayed up. We should not
- // come to this stage.
- StatusBarNotifier.clearAllCallNotifications(context);
- }
- return;
- }
-
- Call call = mCallList.getOutgoingCall();
- if (call == null) {
- call = mCallList.getActiveOrBackgroundCall();
- }
-
- if (call != null) {
- TelecomAdapter.getInstance().disconnectCall(call.getId());
- call.setState(Call.State.DISCONNECTING);
- mCallList.onUpdate(call);
- }
- }
-
- /**
- * Answers any incoming call.
- */
- public void answerIncomingCall(Context context, int videoState) {
- // By the time we receive this intent, we could be shut down and call list
- // could be null. Bail in those cases.
- if (mCallList == null) {
- StatusBarNotifier.clearAllCallNotifications(context);
- return;
- }
-
- Call call = mCallList.getIncomingCall();
- if (call != null) {
- TelecomAdapter.getInstance().answerCall(call.getId(), videoState);
- showInCall(false, false/* newOutgoingCall */);
- }
- }
-
- /**
- * Declines any incoming call.
- */
- public void declineIncomingCall(Context context) {
- // By the time we receive this intent, we could be shut down and call list
- // could be null. Bail in those cases.
- if (mCallList == null) {
- StatusBarNotifier.clearAllCallNotifications(context);
- return;
- }
-
- Call call = mCallList.getIncomingCall();
- if (call != null) {
- TelecomAdapter.getInstance().rejectCall(call.getId(), false, null);
- }
- }
-
- public void acceptUpgradeRequest(int videoState, Context context) {
- Log.d(this, " acceptUpgradeRequest videoState " + videoState);
- // Bail if we have been shut down and the call list is null.
- if (mCallList == null) {
- StatusBarNotifier.clearAllCallNotifications(context);
- Log.e(this, " acceptUpgradeRequest mCallList is empty so returning");
- return;
- }
-
- Call call = mCallList.getVideoUpgradeRequestCall();
- if (call != null) {
- VideoProfile videoProfile = new VideoProfile(videoState);
- call.getVideoCall().sendSessionModifyResponse(videoProfile);
- call.setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
- }
- }
-
- public void declineUpgradeRequest(Context context) {
- Log.d(this, " declineUpgradeRequest");
- // Bail if we have been shut down and the call list is null.
- if (mCallList == null) {
- StatusBarNotifier.clearAllCallNotifications(context);
- Log.e(this, " declineUpgradeRequest mCallList is empty so returning");
- return;
- }
-
- Call call = mCallList.getVideoUpgradeRequestCall();
- if (call != null) {
- VideoProfile videoProfile =
- new VideoProfile(call.getVideoState());
- call.getVideoCall().sendSessionModifyResponse(videoProfile);
- call.setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
- }
- }
-
- /*package*/
- void declineUpgradeRequest() {
- // Pass mContext if InCallActivity is destroyed.
- // Ex: When user pressed back key while in active call and
- // then modify request is received followed by MT call.
- declineUpgradeRequest(mInCallActivity != null ? mInCallActivity : mContext);
- }
-
- /**
- * Returns true if the incall app is the foreground application.
- */
- public boolean isShowingInCallUi() {
- return (isActivityStarted() && mInCallActivity.isVisible());
- }
-
- /**
- * Returns true if the activity has been created and is running.
- * Returns true as long as activity is not destroyed or finishing. This ensures that we return
- * true even if the activity is paused (not in foreground).
- */
- public boolean isActivityStarted() {
- return (mInCallActivity != null &&
- !mInCallActivity.isDestroyed() &&
- !mInCallActivity.isFinishing());
- }
-
- public boolean isActivityPreviouslyStarted() {
- return mIsActivityPreviouslyStarted;
- }
-
- /**
- * Determines if the In-Call app is currently changing configuration.
- *
- * @return {@code true} if the In-Call app is changing configuration.
- */
- public boolean isChangingConfigurations() {
- return mIsChangingConfigurations;
- }
-
- /**
- * Tracks whether the In-Call app is currently in the process of changing configuration (i.e.
- * screen orientation).
- */
- /*package*/
- void updateIsChangingConfigurations() {
- mIsChangingConfigurations = false;
- if (mInCallActivity != null) {
- mIsChangingConfigurations = mInCallActivity.isChangingConfigurations();
- }
- Log.v(this, "updateIsChangingConfigurations = " + mIsChangingConfigurations);
- }
-
-
- /**
- * Called when the activity goes in/out of the foreground.
- */
- public void onUiShowing(boolean showing) {
- // We need to update the notification bar when we leave the UI because that
- // could trigger it to show again.
- if (mStatusBarNotifier != null) {
- mStatusBarNotifier.updateNotification(mInCallState, mCallList);
- }
-
- if (mProximitySensor != null) {
- mProximitySensor.onInCallShowing(showing);
- }
-
- Intent broadcastIntent = ObjectFactory.getUiReadyBroadcastIntent(mContext);
- if (broadcastIntent != null) {
- broadcastIntent.putExtra(EXTRA_FIRST_TIME_SHOWN, !mIsActivityPreviouslyStarted);
-
- if (showing) {
- Log.d(this, "Sending sticky broadcast: ", broadcastIntent);
- mContext.sendStickyBroadcast(broadcastIntent);
- } else {
- Log.d(this, "Removing sticky broadcast: ", broadcastIntent);
- mContext.removeStickyBroadcast(broadcastIntent);
- }
- }
-
- if (showing) {
- mIsActivityPreviouslyStarted = true;
- } else {
- updateIsChangingConfigurations();
- }
-
- for (InCallUiListener listener : mInCallUiListeners) {
- listener.onUiShowing(showing);
- }
- }
-
- public void addInCallUiListener(InCallUiListener listener) {
- mInCallUiListeners.add(listener);
- }
-
- public boolean removeInCallUiListener(InCallUiListener listener) {
- return mInCallUiListeners.remove(listener);
- }
-
- /*package*/
- void onActivityStarted() {
- Log.d(this, "onActivityStarted");
- notifyVideoPauseController(true);
- }
-
- /*package*/
- void onActivityStopped() {
- Log.d(this, "onActivityStopped");
- notifyVideoPauseController(false);
- }
-
- private void notifyVideoPauseController(boolean showing) {
- Log.d(this, "notifyVideoPauseController: mIsChangingConfigurations=" +
- mIsChangingConfigurations);
- if (!mIsChangingConfigurations) {
- VideoPauseController.getInstance().onUiShowing(showing);
- }
- }
-
- /**
- * Brings the app into the foreground if possible.
- */
- public void bringToForeground(boolean showDialpad) {
- // Before we bring the incall UI to the foreground, we check to see if:
- // 1. It is not currently in the foreground
- // 2. We are in a state where we want to show the incall ui (i.e. there are calls to
- // be displayed)
- // If the activity hadn't actually been started previously, yet there are still calls
- // present (e.g. a call was accepted by a bluetooth or wired headset), we want to
- // bring it up the UI regardless.
- if (!isShowingInCallUi() && mInCallState != InCallState.NO_CALLS) {
- showInCall(showDialpad, false /* newOutgoingCall */);
- }
- }
-
- public void onPostDialCharWait(String callId, String chars) {
- if (isActivityStarted()) {
- mInCallActivity.showPostCharWaitDialog(callId, chars);
- }
- }
-
- /**
- * Handles the green CALL key while in-call.
- * @return true if we consumed the event.
- */
- public boolean handleCallKey() {
- Log.v(this, "handleCallKey");
-
- // The green CALL button means either "Answer", "Unhold", or
- // "Swap calls", or can be a no-op, depending on the current state
- // of the Phone.
-
- /**
- * INCOMING CALL
- */
- final CallList calls = mCallList;
- final Call incomingCall = calls.getIncomingCall();
- Log.v(this, "incomingCall: " + incomingCall);
-
- // (1) Attempt to answer a call
- if (incomingCall != null) {
- TelecomAdapter.getInstance().answerCall(
- incomingCall.getId(), VideoProfile.STATE_AUDIO_ONLY);
- return true;
- }
-
- /**
- * STATE_ACTIVE CALL
- */
- final Call activeCall = calls.getActiveCall();
- if (activeCall != null) {
- // TODO: This logic is repeated from CallButtonPresenter.java. We should
- // consolidate this logic.
- final boolean canMerge = activeCall.can(
- android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
- final boolean canSwap = activeCall.can(
- android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
-
- Log.v(this, "activeCall: " + activeCall + ", canMerge: " + canMerge +
- ", canSwap: " + canSwap);
-
- // (2) Attempt actions on conference calls
- if (canMerge) {
- TelecomAdapter.getInstance().merge(activeCall.getId());
- return true;
- } else if (canSwap) {
- TelecomAdapter.getInstance().swap(activeCall.getId());
- return true;
- }
- }
-
- /**
- * BACKGROUND CALL
- */
- final Call heldCall = calls.getBackgroundCall();
- if (heldCall != null) {
- // We have a hold call so presumeable it will always support HOLD...but
- // there is no harm in double checking.
- final boolean canHold = heldCall.can(android.telecom.Call.Details.CAPABILITY_HOLD);
-
- Log.v(this, "heldCall: " + heldCall + ", canHold: " + canHold);
-
- // (4) unhold call
- if (heldCall.getState() == Call.State.ONHOLD && canHold) {
- TelecomAdapter.getInstance().unholdCall(heldCall.getId());
- return true;
- }
- }
-
- // Always consume hard keys
- return true;
- }
-
- /**
- * A dialog could have prevented in-call screen from being previously finished.
- * This function checks to see if there should be any UI left and if not attempts
- * to tear down the UI.
- */
- public void onDismissDialog() {
- Log.i(this, "Dialog dismissed");
- if (mInCallState == InCallState.NO_CALLS) {
- attemptFinishActivity();
- attemptCleanup();
- }
- }
-
- /**
- * Toggles whether the application is in fullscreen mode or not.
- *
- * @return {@code true} if in-call is now in fullscreen mode.
- */
- public boolean toggleFullscreenMode() {
- boolean isFullScreen = !mIsFullScreen;
- Log.v(this, "toggleFullscreenMode = " + isFullScreen);
- setFullScreen(isFullScreen);
- return mIsFullScreen;
- }
-
- /**
- * Clears the previous fullscreen state.
- */
- public void clearFullscreen() {
- mIsFullScreen = false;
- }
-
- /**
- * Changes the fullscreen mode of the in-call UI.
- *
- * @param isFullScreen {@code true} if in-call should be in fullscreen mode, {@code false}
- * otherwise.
- */
- public void setFullScreen(boolean isFullScreen) {
- setFullScreen(isFullScreen, false /* force */);
- }
-
- /**
- * Changes the fullscreen mode of the in-call UI.
- *
- * @param isFullScreen {@code true} if in-call should be in fullscreen mode, {@code false}
- * otherwise.
- * @param force {@code true} if fullscreen mode should be set regardless of its current state.
- */
- public void setFullScreen(boolean isFullScreen, boolean force) {
- Log.v(this, "setFullScreen = " + isFullScreen);
-
- // As a safeguard, ensure we cannot enter fullscreen if the dialpad is shown.
- if (isDialpadVisible()) {
- isFullScreen = false;
- Log.v(this, "setFullScreen overridden as dialpad is shown = " + isFullScreen);
- }
-
- if (mIsFullScreen == isFullScreen && !force) {
- Log.v(this, "setFullScreen ignored as already in that state.");
- return;
- }
- mIsFullScreen = isFullScreen;
- notifyFullscreenModeChange(mIsFullScreen);
- }
-
- /**
- * @return {@code true} if the in-call ui is currently in fullscreen mode, {@code false}
- * otherwise.
- */
- public boolean isFullscreen() {
- return mIsFullScreen;
- }
-
-
- /**
- * Called by the {@link VideoCallPresenter} to inform of a change in full screen video status.
- *
- * @param isFullscreenMode {@code True} if entering full screen mode.
- */
- public void notifyFullscreenModeChange(boolean isFullscreenMode) {
- for (InCallEventListener listener : mInCallEventListeners) {
- listener.onFullscreenModeChanged(isFullscreenMode);
- }
- }
-
- /**
- * Called by the {@link CallCardPresenter} to inform of a change in visibility of the secondary
- * caller info bar.
- *
- * @param isVisible {@code true} if the secondary caller info is visible, {@code false}
- * otherwise.
- * @param height the height of the secondary caller info bar.
- */
- public void notifySecondaryCallerInfoVisibilityChanged(boolean isVisible, int height) {
- for (InCallEventListener listener : mInCallEventListeners) {
- listener.onSecondaryCallerInfoVisibilityChanged(isVisible, height);
- }
- }
-
-
- /**
- * For some disconnected causes, we show a dialog. This calls into the activity to show
- * the dialog if appropriate for the call.
- */
- private void maybeShowErrorDialogOnDisconnect(Call call) {
- // For newly disconnected calls, we may want to show a dialog on specific error conditions
- if (isActivityStarted() && call.getState() == Call.State.DISCONNECTED) {
- if (call.getAccountHandle() == null && !call.isConferenceCall()) {
- setDisconnectCauseForMissingAccounts(call);
- }
- mInCallActivity.maybeShowErrorDialogOnDisconnect(call.getDisconnectCause());
- }
- }
-
- /**
- * When the state of in-call changes, this is the first method to get called. It determines if
- * the UI needs to be started or finished depending on the new state and does it.
- */
- private InCallState startOrFinishUi(InCallState newState) {
- Log.d(this, "startOrFinishUi: " + mInCallState + " -> " + newState);
-
- // TODO: Consider a proper state machine implementation
-
- // If the state isn't changing we have already done any starting/stopping of activities in
- // a previous pass...so lets cut out early
- if (newState == mInCallState) {
- return newState;
- }
-
- // A new Incoming call means that the user needs to be notified of the the call (since
- // it wasn't them who initiated it). We do this through full screen notifications and
- // happens indirectly through {@link StatusBarNotifier}.
- //
- // The process for incoming calls is as follows:
- //
- // 1) CallList - Announces existence of new INCOMING call
- // 2) InCallPresenter - Gets announcement and calculates that the new InCallState
- // - should be set to INCOMING.
- // 3) InCallPresenter - This method is called to see if we need to start or finish
- // the app given the new state.
- // 4) StatusBarNotifier - Listens to InCallState changes. InCallPresenter calls
- // StatusBarNotifier explicitly to issue a FullScreen Notification
- // that will either start the InCallActivity or show the user a
- // top-level notification dialog if the user is in an immersive app.
- // That notification can also start the InCallActivity.
- // 5) InCallActivity - Main activity starts up and at the end of its onCreate will
- // call InCallPresenter::setActivity() to let the presenter
- // know that start-up is complete.
- //
- // [ AND NOW YOU'RE IN THE CALL. voila! ]
- //
- // Our app is started using a fullScreen notification. We need to do this whenever
- // we get an incoming call. Depending on the current context of the device, either a
- // incoming call HUN or the actual InCallActivity will be shown.
- final boolean startIncomingCallSequence = (InCallState.INCOMING == newState);
-
- // A dialog to show on top of the InCallUI to select a PhoneAccount
- final boolean showAccountPicker = (InCallState.WAITING_FOR_ACCOUNT == newState);
-
- // A new outgoing call indicates that the user just now dialed a number and when that
- // happens we need to display the screen immediately or show an account picker dialog if
- // no default is set. However, if the main InCallUI is already visible, we do not want to
- // re-initiate the start-up animation, so we do not need to do anything here.
- //
- // It is also possible to go into an intermediate state where the call has been initiated
- // but Telecom has not yet returned with the details of the call (handle, gateway, etc.).
- // This pending outgoing state can also launch the call screen.
- //
- // This is different from the incoming call sequence because we do not need to shock the
- // user with a top-level notification. Just show the call UI normally.
- final boolean mainUiNotVisible = !isShowingInCallUi() || !getCallCardFragmentVisible();
- boolean showCallUi = InCallState.OUTGOING == newState && mainUiNotVisible;
-
- // Direct transition from PENDING_OUTGOING -> INCALL means that there was an error in the
- // outgoing call process, so the UI should be brought up to show an error dialog.
- showCallUi |= (InCallState.PENDING_OUTGOING == mInCallState
- && InCallState.INCALL == newState && !isShowingInCallUi());
-
- // Another exception - InCallActivity is in charge of disconnecting a call with no
- // valid accounts set. Bring the UI up if this is true for the current pending outgoing
- // call so that:
- // 1) The call can be disconnected correctly
- // 2) The UI comes up and correctly displays the error dialog.
- // TODO: Remove these special case conditions by making InCallPresenter a true state
- // machine. Telecom should also be the component responsible for disconnecting a call
- // with no valid accounts.
- showCallUi |= InCallState.PENDING_OUTGOING == newState && mainUiNotVisible
- && isCallWithNoValidAccounts(mCallList.getPendingOutgoingCall());
-
- // The only time that we have an instance of mInCallActivity and it isn't started is
- // when it is being destroyed. In that case, lets avoid bringing up another instance of
- // the activity. When it is finally destroyed, we double check if we should bring it back
- // up so we aren't going to lose anything by avoiding a second startup here.
- boolean activityIsFinishing = mInCallActivity != null && !isActivityStarted();
- if (activityIsFinishing) {
- Log.i(this, "Undo the state change: " + newState + " -> " + mInCallState);
- return mInCallState;
- }
-
- if (showCallUi || showAccountPicker) {
- Log.i(this, "Start in call UI");
- showInCall(false /* showDialpad */, !showAccountPicker /* newOutgoingCall */);
- } else if (startIncomingCallSequence) {
- Log.i(this, "Start Full Screen in call UI");
-
- // We're about the bring up the in-call UI for an incoming call. If we still have
- // dialogs up, we need to clear them out before showing incoming screen.
- if (isActivityStarted()) {
- mInCallActivity.dismissPendingDialogs();
- }
- if (!startUi(newState)) {
- // startUI refused to start the UI. This indicates that it needed to restart the
- // activity. When it finally restarts, it will call us back, so we do not actually
- // change the state yet (we return mInCallState instead of newState).
- return mInCallState;
- }
- } else if (newState == InCallState.NO_CALLS) {
- // The new state is the no calls state. Tear everything down.
- attemptFinishActivity();
- attemptCleanup();
- }
-
- return newState;
- }
-
- /**
- * Determines whether or not a call has no valid phone accounts that can be used to make the
- * call with. Emergency calls do not require a phone account.
- *
- * @param call to check accounts for.
- * @return {@code true} if the call has no call capable phone accounts set, {@code false} if
- * the call contains a phone account that could be used to initiate it with, or is an emergency
- * call.
- */
- public static boolean isCallWithNoValidAccounts(Call call) {
- if (call != null && !call.isEmergencyCall()) {
- Bundle extras = call.getIntentExtras();
-
- if (extras == null) {
- extras = EMPTY_EXTRAS;
- }
-
- final List<PhoneAccountHandle> phoneAccountHandles = extras
- .getParcelableArrayList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS);
-
- if ((call.getAccountHandle() == null &&
- (phoneAccountHandles == null || phoneAccountHandles.isEmpty()))) {
- Log.i(InCallPresenter.getInstance(), "No valid accounts for call " + call);
- return true;
- }
- }
- return false;
- }
-
- /**
- * Sets the DisconnectCause for a call that was disconnected because it was missing a
- * PhoneAccount or PhoneAccounts to select from.
- * @param call
- */
- private void setDisconnectCauseForMissingAccounts(Call call) {
- android.telecom.Call telecomCall = call.getTelecomCall();
-
- Bundle extras = telecomCall.getDetails().getIntentExtras();
- // Initialize the extras bundle to avoid NPE
- if (extras == null) {
- extras = new Bundle();
- }
-
- final List<PhoneAccountHandle> phoneAccountHandles = extras.getParcelableArrayList(
- android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS);
-
- if (phoneAccountHandles == null || phoneAccountHandles.isEmpty()) {
- String scheme = telecomCall.getDetails().getHandle().getScheme();
- final String errorMsg = PhoneAccount.SCHEME_TEL.equals(scheme) ?
- mContext.getString(R.string.callFailed_simError) :
- mContext.getString(R.string.incall_error_supp_service_unknown);
- DisconnectCause disconnectCause =
- new DisconnectCause(DisconnectCause.ERROR, null, errorMsg, errorMsg);
- call.setDisconnectCause(disconnectCause);
- }
- }
-
- private boolean startUi(InCallState inCallState) {
- boolean isCallWaiting = mCallList.getActiveCall() != null &&
- mCallList.getIncomingCall() != null;
-
- // If the screen is off, we need to make sure it gets turned on for incoming calls.
- // This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works
- // when the activity is first created. Therefore, to ensure the screen is turned on
- // for the call waiting case, we finish() the current activity and start a new one.
- // There should be no jank from this since the screen is already off and will remain so
- // until our new activity is up.
-
- if (isCallWaiting) {
- if (mProximitySensor.isScreenReallyOff() && isActivityStarted()) {
- Log.i(this, "Restarting InCallActivity to turn screen on for call waiting");
- mInCallActivity.finish();
- // When the activity actually finishes, we will start it again if there are
- // any active calls, so we do not need to start it explicitly here. Note, we
- // actually get called back on this function to restart it.
-
- // We return false to indicate that we did not actually start the UI.
- return false;
- } else {
- showInCall(false, false);
- }
- } else {
- mStatusBarNotifier.updateNotification(inCallState, mCallList);
- }
- return true;
- }
-
- /**
- * Checks to see if both the UI is gone and the service is disconnected. If so, tear it all
- * down.
- */
- private void attemptCleanup() {
- boolean shouldCleanup = (mInCallActivity == null && !mServiceConnected &&
- mInCallState == InCallState.NO_CALLS);
- Log.i(this, "attemptCleanup? " + shouldCleanup);
-
- if (shouldCleanup) {
- mIsActivityPreviouslyStarted = false;
- mIsChangingConfigurations = false;
-
- // blow away stale contact info so that we get fresh data on
- // the next set of calls
- if (mContactInfoCache != null) {
- mContactInfoCache.clearCache();
- }
- mContactInfoCache = null;
-
- if (mProximitySensor != null) {
- removeListener(mProximitySensor);
- mProximitySensor.tearDown();
- }
- mProximitySensor = null;
-
- mAudioModeProvider = null;
-
- if (mStatusBarNotifier != null) {
- removeListener(mStatusBarNotifier);
- }
- if (mExternalCallNotifier != null && mExternalCallList != null) {
- mExternalCallList.removeExternalCallListener(mExternalCallNotifier);
- }
- mStatusBarNotifier = null;
-
- if (mCallList != null) {
- mCallList.removeListener(this);
- mCallList.removeListener(mSpamCallListListener);
- }
- mCallList = null;
-
- mContext = null;
- mInCallActivity = null;
-
- mListeners.clear();
- mIncomingCallListeners.clear();
- mDetailsListeners.clear();
- mCanAddCallListeners.clear();
- mOrientationListeners.clear();
- mInCallEventListeners.clear();
-
- Log.d(this, "Finished InCallPresenter.CleanUp");
- }
- }
-
- public void showInCall(final boolean showDialpad, final boolean newOutgoingCall) {
- Log.i(this, "Showing InCallActivity");
- mContext.startActivity(getInCallIntent(showDialpad, newOutgoingCall));
- }
-
- public void onServiceBind() {
- mServiceBound = true;
- }
-
- public void onServiceUnbind() {
- InCallPresenter.getInstance().setBoundAndWaitingForOutgoingCall(false, null);
- mServiceBound = false;
- }
-
- public boolean isServiceBound() {
- return mServiceBound;
- }
-
- public void maybeStartRevealAnimation(Intent intent) {
- if (intent == null || mInCallActivity != null) {
- return;
- }
- final Bundle extras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
- if (extras == null) {
- // Incoming call, just show the in-call UI directly.
- return;
- }
-
- if (extras.containsKey(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS)) {
- // Account selection dialog will show up so don't show the animation.
- return;
- }
-
- final PhoneAccountHandle accountHandle =
- intent.getParcelableExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
- final Point touchPoint = extras.getParcelable(TouchPointManager.TOUCH_POINT);
-
- InCallPresenter.getInstance().setBoundAndWaitingForOutgoingCall(true, accountHandle);
-
- final Intent incallIntent = getInCallIntent(false, true);
- incallIntent.putExtra(TouchPointManager.TOUCH_POINT, touchPoint);
- mContext.startActivity(incallIntent);
- }
-
- public Intent getInCallIntent(boolean showDialpad, boolean newOutgoingCall) {
- final Intent intent = new Intent(Intent.ACTION_MAIN, null);
- intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
-
- intent.setClass(mContext, InCallActivity.class);
- if (showDialpad) {
- intent.putExtra(InCallActivity.SHOW_DIALPAD_EXTRA, true);
- }
- intent.putExtra(InCallActivity.NEW_OUTGOING_CALL_EXTRA, newOutgoingCall);
- return intent;
- }
-
- /**
- * Retrieves the current in-call camera manager instance, creating if necessary.
- *
- * @return The {@link InCallCameraManager}.
- */
- public InCallCameraManager getInCallCameraManager() {
- synchronized(this) {
- if (mInCallCameraManager == null) {
- mInCallCameraManager = new InCallCameraManager(mContext);
- }
-
- return mInCallCameraManager;
- }
- }
-
- /**
- * Notifies listeners of changes in orientation and notify calls of rotation angle change.
- *
- * @param orientation The screen orientation of the device (one of:
- * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_0},
- * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_90},
- * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_180},
- * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_270}).
- */
- public void onDeviceOrientationChange(int orientation) {
- Log.d(this, "onDeviceOrientationChange: orientation= " + orientation);
-
- if (mCallList != null) {
- mCallList.notifyCallsOfDeviceRotation(orientation);
- } else {
- Log.w(this, "onDeviceOrientationChange: CallList is null.");
- }
-
- // Notify listeners of device orientation changed.
- for (InCallOrientationListener listener : mOrientationListeners) {
- listener.onDeviceOrientationChanged(orientation);
- }
- }
-
- /**
- * Configures the in-call UI activity so it can change orientations or not. Enables the
- * orientation event listener if allowOrientationChange is true, disables it if false.
- *
- * @param allowOrientationChange {@code True} if the in-call UI can change between portrait
- * and landscape. {@Code False} if the in-call UI should be locked in portrait.
- */
- public void setInCallAllowsOrientationChange(boolean allowOrientationChange) {
- if (mInCallActivity == null) {
- Log.e(this, "InCallActivity is null. Can't set requested orientation.");
- return;
- }
-
- if (!allowOrientationChange) {
- mInCallActivity.setRequestedOrientation(
- InCallOrientationEventListener.NO_SENSOR_SCREEN_ORIENTATION);
- } else {
- // Using SCREEN_ORIENTATION_FULL_SENSOR allows for reverse-portrait orientation, where
- // SCREEN_ORIENTATION_SENSOR does not.
- mInCallActivity.setRequestedOrientation(
- InCallOrientationEventListener.FULL_SENSOR_SCREEN_ORIENTATION);
- }
- mInCallActivity.enableInCallOrientationEventListener(allowOrientationChange);
- }
-
- public void enableScreenTimeout(boolean enable) {
- Log.v(this, "enableScreenTimeout: value=" + enable);
- if (mInCallActivity == null) {
- Log.e(this, "enableScreenTimeout: InCallActivity is null.");
- return;
- }
-
- final Window window = mInCallActivity.getWindow();
- if (enable) {
- window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- } else {
- window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
- }
-
- /**
- * Returns the space available beside the call card.
- *
- * @return The space beside the call card.
- */
- public float getSpaceBesideCallCard() {
- if (mInCallActivity != null && mInCallActivity.getCallCardFragment() != null) {
- return mInCallActivity.getCallCardFragment().getSpaceBesideCallCard();
- }
- return 0;
- }
-
- /**
- * Returns whether the call card fragment is currently visible.
- *
- * @return True if the call card fragment is visible.
- */
- public boolean getCallCardFragmentVisible() {
- if (mInCallActivity != null && mInCallActivity.getCallCardFragment() != null) {
- return mInCallActivity.getCallCardFragment().isVisible();
- }
- return false;
- }
-
- /**
- * Hides or shows the conference manager fragment.
- *
- * @param show {@code true} if the conference manager should be shown, {@code false} if it
- * should be hidden.
- */
- public void showConferenceCallManager(boolean show) {
- if (mInCallActivity == null) {
- return;
- }
-
- mInCallActivity.showConferenceFragment(show);
- }
-
- /**
- * Determines if the dialpad is visible.
- *
- * @return {@code true} if the dialpad is visible, {@code false} otherwise.
- */
- public boolean isDialpadVisible() {
- if (mInCallActivity == null) {
- return false;
- }
- return mInCallActivity.isDialpadVisible();
- }
-
- /**
- * @return True if the application is currently running in a right-to-left locale.
- */
- public static boolean isRtl() {
- return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) ==
- View.LAYOUT_DIRECTION_RTL;
- }
-
- /**
- * Extract background color from call object. The theme colors will include a primary color
- * and a secondary color.
- */
- public void setThemeColors() {
- // This method will set the background to default if the color is PhoneAccount.NO_COLOR.
- mThemeColors = getColorsFromCall(mCallList.getFirstCall());
-
- if (mInCallActivity == null) {
- return;
- }
-
- final Resources resources = mInCallActivity.getResources();
- final int color;
- if (resources.getBoolean(R.bool.is_layout_landscape)) {
- // TODO use ResourcesCompat.getColor(Resources, int, Resources.Theme) when available
- // {@link Resources#getColor(int)} used for compatibility
- color = resources.getColor(R.color.statusbar_background_color);
- } else {
- color = mThemeColors.mSecondaryColor;
- }
-
- mInCallActivity.getWindow().setStatusBarColor(color);
- final TaskDescription td = new TaskDescription(
- resources.getString(R.string.notification_ongoing_call), null, color);
- mInCallActivity.setTaskDescription(td);
- }
-
- /**
- * @return A palette for colors to display in the UI.
- */
- public MaterialPalette getThemeColors() {
- return mThemeColors;
- }
-
- private MaterialPalette getColorsFromCall(Call call) {
- if (call == null) {
- return getColorsFromPhoneAccountHandle(mPendingPhoneAccountHandle);
- } else {
- if (call.isSpam()) {
- Resources resources = mContext.getResources();
- return new InCallUIMaterialColorMapUtils(
- resources).calculatePrimaryAndSecondaryColor(
- resources.getColor(R.color.incall_call_spam_background_color));
- } else {
- return getColorsFromPhoneAccountHandle(call.getAccountHandle());
- }
- }
- }
-
- private MaterialPalette getColorsFromPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
- int highlightColor = PhoneAccount.NO_HIGHLIGHT_COLOR;
- if (phoneAccountHandle != null) {
- final TelecomManager tm = getTelecomManager();
-
- if (tm != null) {
- final PhoneAccount account =
- TelecomManagerCompat.getPhoneAccount(tm, phoneAccountHandle);
- // For single-sim devices, there will be no selected highlight color, so the phone
- // account will default to NO_HIGHLIGHT_COLOR.
- if (account != null && CompatUtils.isLollipopMr1Compatible()) {
- highlightColor = account.getHighlightColor();
- }
- }
- }
- return new InCallUIMaterialColorMapUtils(
- mContext.getResources()).calculatePrimaryAndSecondaryColor(highlightColor);
- }
-
- /**
- * @return An instance of TelecomManager.
- */
- public TelecomManager getTelecomManager() {
- if (mTelecomManager == null) {
- mTelecomManager = (TelecomManager)
- mContext.getSystemService(Context.TELECOM_SERVICE);
- }
- return mTelecomManager;
- }
-
- /**
- * @return An instance of TelephonyManager
- */
- public TelephonyManager getTelephonyManager() {
- return mTelephonyManager;
- }
-
- InCallActivity getActivity() {
- return mInCallActivity;
- }
-
- AnswerPresenter getAnswerPresenter() {
- return mAnswerPresenter;
- }
-
- ExternalCallNotifier getExternalCallNotifier() {
- return mExternalCallNotifier;
- }
-
- /**
- * Private constructor. Must use getInstance() to get this singleton.
- */
- private InCallPresenter() {
- }
-
- /**
- * All the main states of InCallActivity.
- */
- public enum InCallState {
- // InCall Screen is off and there are no calls
- NO_CALLS,
-
- // Incoming-call screen is up
- INCOMING,
-
- // In-call experience is showing
- INCALL,
-
- // Waiting for user input before placing outgoing call
- WAITING_FOR_ACCOUNT,
-
- // UI is starting up but no call has been initiated yet.
- // The UI is waiting for Telecom to respond.
- PENDING_OUTGOING,
-
- // User is dialing out
- OUTGOING;
-
- public boolean isIncoming() {
- return (this == INCOMING);
- }
-
- public boolean isConnectingOrConnected() {
- return (this == INCOMING ||
- this == OUTGOING ||
- this == INCALL);
- }
- }
-
- /**
- * Interface implemented by classes that need to know about the InCall State.
- */
- public interface InCallStateListener {
- // TODO: Enhance state to contain the call objects instead of passing CallList
- public void onStateChange(InCallState oldState, InCallState newState, CallList callList);
- }
-
- public interface IncomingCallListener {
- public void onIncomingCall(InCallState oldState, InCallState newState, Call call);
- }
-
- public interface CanAddCallListener {
- public void onCanAddCallChanged(boolean canAddCall);
- }
-
- public interface InCallDetailsListener {
- public void onDetailsChanged(Call call, android.telecom.Call.Details details);
- }
-
- public interface InCallOrientationListener {
- public void onDeviceOrientationChanged(int orientation);
- }
-
- /**
- * Interface implemented by classes that need to know about events which occur within the
- * In-Call UI. Used as a means of communicating between fragments that make up the UI.
- */
- public interface InCallEventListener {
- public void onFullscreenModeChanged(boolean isFullscreenMode);
- public void onSecondaryCallerInfoVisibilityChanged(boolean isVisible, int height);
- }
-
- public interface InCallUiListener {
- void onUiShowing(boolean showing);
- }
-}
diff --git a/InCallUI/src/com/android/incallui/InCallServiceImpl.java b/InCallUI/src/com/android/incallui/InCallServiceImpl.java
deleted file mode 100644
index 1414bc51d..000000000
--- a/InCallUI/src/com/android/incallui/InCallServiceImpl.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2014 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.incallui;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-import android.telecom.Call;
-import android.telecom.CallAudioState;
-import android.telecom.InCallService;
-
-/**
- * Used to receive updates about calls from the Telecom component. This service is bound to
- * Telecom while there exist calls which potentially require UI. This includes ringing (incoming),
- * dialing (outgoing), and active calls. When the last call is disconnected, Telecom will unbind to
- * the service triggering InCallActivity (via CallList) to finish soon after.
- */
-public class InCallServiceImpl extends InCallService {
-
- @Override
- public void onCallAudioStateChanged(CallAudioState audioState) {
- AudioModeProvider.getInstance().onAudioStateChanged(audioState.isMuted(),
- audioState.getRoute(), audioState.getSupportedRouteMask());
- }
-
- @Override
- public void onBringToForeground(boolean showDialpad) {
- InCallPresenter.getInstance().onBringToForeground(showDialpad);
- }
-
- @Override
- public void onCallAdded(Call call) {
- InCallPresenter.getInstance().onCallAdded(call);
- }
-
- @Override
- public void onCallRemoved(Call call) {
- InCallPresenter.getInstance().onCallRemoved(call);
- }
-
- @Override
- public void onCanAddCallChanged(boolean canAddCall) {
- InCallPresenter.getInstance().onCanAddCallChanged(canAddCall);
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- final Context context = getApplicationContext();
- final ContactInfoCache contactInfoCache = ContactInfoCache.getInstance(context);
- InCallPresenter.getInstance().setUp(
- getApplicationContext(),
- CallList.getInstance(),
- new ExternalCallList(),
- AudioModeProvider.getInstance(),
- new StatusBarNotifier(context, contactInfoCache),
- new ExternalCallNotifier(context, contactInfoCache),
- contactInfoCache,
- new ProximitySensor(
- context,
- AudioModeProvider.getInstance(),
- new AccelerometerListener(context))
- );
- InCallPresenter.getInstance().onServiceBind();
- InCallPresenter.getInstance().maybeStartRevealAnimation(intent);
- TelecomAdapter.getInstance().setInCallService(this);
-
- return super.onBind(intent);
- }
-
- @Override
- public boolean onUnbind(Intent intent) {
- super.onUnbind(intent);
-
- InCallPresenter.getInstance().onServiceUnbind();
- tearDown();
-
- return false;
- }
-
- private void tearDown() {
- Log.v(this, "tearDown");
- // Tear down the InCall system
- TelecomAdapter.getInstance().clearInCallService();
- InCallPresenter.getInstance().tearDown();
- }
-}
diff --git a/InCallUI/src/com/android/incallui/InCallServiceListener.java b/InCallUI/src/com/android/incallui/InCallServiceListener.java
deleted file mode 100644
index 11a5b08ef..000000000
--- a/InCallUI/src/com/android/incallui/InCallServiceListener.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2014 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.incallui;
-
-import android.telecom.InCallService;
-
-/**
- * Interface implemented by In-Call components that maintain a reference to the Telecom API
- * {@code InCallService} object. Clarifies the expectations associated with the relevant method
- * calls.
- */
-public interface InCallServiceListener {
-
- /**
- * Called once at {@code InCallService} startup time with a valid instance. At
- * that time, there will be no existing {@code Call}s.
- *
- * @param inCallService The {@code InCallService} object.
- */
- void setInCallService(InCallService inCallService);
-
- /**
- * Called once at {@code InCallService} shutdown time. At that time, any {@code Call}s
- * will have transitioned through the disconnected state and will no longer exist.
- */
- void clearInCallService();
-}
diff --git a/InCallUI/src/com/android/incallui/InCallUIMaterialColorMapUtils.java b/InCallUI/src/com/android/incallui/InCallUIMaterialColorMapUtils.java
deleted file mode 100644
index 9c108b855..000000000
--- a/InCallUI/src/com/android/incallui/InCallUIMaterialColorMapUtils.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.android.incallui;
-
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.telecom.PhoneAccount;
-
-import com.android.contacts.common.util.MaterialColorMapUtils;
-import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
-import com.android.dialer.R;
-
-public class InCallUIMaterialColorMapUtils extends MaterialColorMapUtils {
- private final TypedArray sPrimaryColors;
- private final TypedArray sSecondaryColors;
- private final Resources mResources;
-
- public InCallUIMaterialColorMapUtils(Resources resources) {
- super(resources);
- sPrimaryColors = resources.obtainTypedArray(R.array.background_colors);
- sSecondaryColors = resources.obtainTypedArray(R.array.background_colors_dark);
- mResources = resources;
- }
-
- /**
- * Currently the InCallUI color will only vary by SIM color which is a list of colors
- * defined in the background_colors array, so first search the list for the matching color and
- * fall back to the closest matching color if an exact match does not exist.
- */
- @Override
- public MaterialPalette calculatePrimaryAndSecondaryColor(int color) {
- if (color == PhoneAccount.NO_HIGHLIGHT_COLOR) {
- return getDefaultPrimaryAndSecondaryColors(mResources);
- }
-
- for (int i = 0; i < sPrimaryColors.length(); i++) {
- if (sPrimaryColors.getColor(i, 0) == color) {
- return new MaterialPalette(
- sPrimaryColors.getColor(i, 0),
- sSecondaryColors.getColor(i, 0));
- }
- }
-
- // The color isn't in the list, so use the superclass to find an approximate color.
- return super.calculatePrimaryAndSecondaryColor(color);
- }
-
- /**
- * {@link Resources#getColor(int) used for compatibility
- */
- @SuppressWarnings("deprecation")
- public static MaterialPalette getDefaultPrimaryAndSecondaryColors(Resources resources) {
- final int primaryColor = resources.getColor(R.color.dialer_theme_color);
- final int secondaryColor = resources.getColor(R.color.dialer_theme_color_dark);
- return new MaterialPalette(primaryColor, secondaryColor);
- }
-}
diff --git a/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java b/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java
deleted file mode 100644
index 99e6d5129..000000000
--- a/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2014 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.incallui;
-
-import android.telecom.Connection;
-import android.telecom.Connection.VideoProvider;
-import android.telecom.InCallService.VideoCall;
-import android.telecom.VideoProfile;
-import android.telecom.VideoProfile.CameraCapabilities;
-
-/**
- * Implements the InCallUI VideoCall Callback.
- */
-public class InCallVideoCallCallback extends VideoCall.Callback {
-
- /**
- * The call associated with this {@link InCallVideoCallCallback}.
- */
- private Call mCall;
-
- /**
- * Creates an instance of the call video client, specifying the call it is related to.
- *
- * @param call The call.
- */
- public InCallVideoCallCallback(Call call) {
- mCall = call;
- }
-
- /**
- * Handles an incoming session modification request.
- *
- * @param videoProfile The requested video call profile.
- */
- @Override
- public void onSessionModifyRequestReceived(VideoProfile videoProfile) {
- Log.d(this, " onSessionModifyRequestReceived videoProfile=" + videoProfile);
- int previousVideoState = VideoUtils.getUnPausedVideoState(mCall.getVideoState());
- int newVideoState = VideoUtils.getUnPausedVideoState(videoProfile.getVideoState());
-
- boolean wasVideoCall = VideoUtils.isVideoCall(previousVideoState);
- boolean isVideoCall = VideoUtils.isVideoCall(newVideoState);
-
- // Check for upgrades to video.
- if (!wasVideoCall && isVideoCall && previousVideoState != newVideoState) {
- InCallVideoCallCallbackNotifier.getInstance().upgradeToVideoRequest(mCall,
- newVideoState);
- }
- }
-
- /**
- * Handles a session modification response.
- *
- * @param status Status of the session modify request. Valid values are
- * {@link Connection.VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
- * {@link Connection.VideoProvider#SESSION_MODIFY_REQUEST_FAIL},
- * {@link Connection.VideoProvider#SESSION_MODIFY_REQUEST_INVALID}
- * @param requestedProfile
- * @param responseProfile The actual profile changes made by the peer device.
- */
- @Override
- public void onSessionModifyResponseReceived(int status, VideoProfile requestedProfile,
- VideoProfile responseProfile) {
- Log.d(this, "onSessionModifyResponseReceived status=" + status + " requestedProfile="
- + requestedProfile + " responseProfile=" + responseProfile);
- if (status != VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) {
- // Report the reason the upgrade failed as the new session modification state.
- if (status == VideoProvider.SESSION_MODIFY_REQUEST_TIMED_OUT) {
- mCall.setSessionModificationState(
- Call.SessionModificationState.UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT);
- } else {
- if (status == VideoProvider.SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE) {
- mCall.setSessionModificationState(
- Call.SessionModificationState.REQUEST_REJECTED);
- } else {
- mCall.setSessionModificationState(
- Call.SessionModificationState.REQUEST_FAILED);
- }
- }
- }
-
- // Finally clear the outstanding request.
- mCall.setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
- }
-
- /**
- * Handles a call session event.
- *
- * @param event The event.
- */
- @Override
- public void onCallSessionEvent(int event) {
- InCallVideoCallCallbackNotifier.getInstance().callSessionEvent(event);
- }
-
- /**
- * Handles a change to the peer video dimensions.
- *
- * @param width The updated peer video width.
- * @param height The updated peer video height.
- */
- @Override
- public void onPeerDimensionsChanged(int width, int height) {
- InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(mCall, width, height);
- }
-
- /**
- * Handles a change to the video quality of the call.
- *
- * @param videoQuality The updated video call quality.
- */
- @Override
- public void onVideoQualityChanged(int videoQuality) {
- InCallVideoCallCallbackNotifier.getInstance().videoQualityChanged(mCall, videoQuality);
- }
-
- /**
- * Handles a change to the call data usage. No implementation as the in-call UI does not
- * display data usage.
- *
- * @param dataUsage The updated data usage.
- */
- @Override
- public void onCallDataUsageChanged(long dataUsage) {
- Log.d(this, "onCallDataUsageChanged: dataUsage = " + dataUsage);
- InCallVideoCallCallbackNotifier.getInstance().callDataUsageChanged(dataUsage);
- }
-
- /**
- * Handles changes to the camera capabilities. No implementation as the in-call UI does not
- * make use of camera capabilities.
- *
- * @param cameraCapabilities The changed camera capabilities.
- */
- @Override
- public void onCameraCapabilitiesChanged(CameraCapabilities cameraCapabilities) {
- if (cameraCapabilities != null) {
- InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(
- mCall, cameraCapabilities.getWidth(), cameraCapabilities.getHeight());
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/InCallVideoCallCallbackNotifier.java b/InCallUI/src/com/android/incallui/InCallVideoCallCallbackNotifier.java
deleted file mode 100644
index bb7529205..000000000
--- a/InCallUI/src/com/android/incallui/InCallVideoCallCallbackNotifier.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (C) 2014 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.incallui;
-
-import com.google.common.base.Preconditions;
-
-import java.util.Collections;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Class used by {@link InCallService.VideoCallCallback} to notify interested parties of incoming
- * events.
- */
-public class InCallVideoCallCallbackNotifier {
- /**
- * Singleton instance of this class.
- */
- private static InCallVideoCallCallbackNotifier sInstance =
- new InCallVideoCallCallbackNotifier();
-
- /**
- * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
- * load factor before resizing, 1 means we only expect a single thread to
- * access the map so make only a single shard
- */
- private final Set<SessionModificationListener> mSessionModificationListeners =
- Collections.newSetFromMap(new ConcurrentHashMap<SessionModificationListener, Boolean>
- (8, 0.9f, 1));
- private final Set<VideoEventListener> mVideoEventListeners = Collections.newSetFromMap(
- new ConcurrentHashMap<VideoEventListener, Boolean>(8, 0.9f, 1));
- private final Set<SurfaceChangeListener> mSurfaceChangeListeners = Collections.newSetFromMap(
- new ConcurrentHashMap<SurfaceChangeListener, Boolean>(8, 0.9f, 1));
-
- /**
- * Static singleton accessor method.
- */
- public static InCallVideoCallCallbackNotifier getInstance() {
- return sInstance;
- }
-
- /**
- * Private constructor. Instance should only be acquired through getInstance().
- */
- private InCallVideoCallCallbackNotifier() {
- }
-
- /**
- * Adds a new {@link SessionModificationListener}.
- *
- * @param listener The listener.
- */
- public void addSessionModificationListener(SessionModificationListener listener) {
- Preconditions.checkNotNull(listener);
- mSessionModificationListeners.add(listener);
- }
-
- /**
- * Remove a {@link SessionModificationListener}.
- *
- * @param listener The listener.
- */
- public void removeSessionModificationListener(SessionModificationListener listener) {
- if (listener != null) {
- mSessionModificationListeners.remove(listener);
- }
- }
-
- /**
- * Adds a new {@link VideoEventListener}.
- *
- * @param listener The listener.
- */
- public void addVideoEventListener(VideoEventListener listener) {
- Preconditions.checkNotNull(listener);
- mVideoEventListeners.add(listener);
- }
-
- /**
- * Remove a {@link VideoEventListener}.
- *
- * @param listener The listener.
- */
- public void removeVideoEventListener(VideoEventListener listener) {
- if (listener != null) {
- mVideoEventListeners.remove(listener);
- }
- }
-
- /**
- * Adds a new {@link SurfaceChangeListener}.
- *
- * @param listener The listener.
- */
- public void addSurfaceChangeListener(SurfaceChangeListener listener) {
- Preconditions.checkNotNull(listener);
- mSurfaceChangeListeners.add(listener);
- }
-
- /**
- * Remove a {@link SurfaceChangeListener}.
- *
- * @param listener The listener.
- */
- public void removeSurfaceChangeListener(SurfaceChangeListener listener) {
- if (listener != null) {
- mSurfaceChangeListeners.remove(listener);
- }
- }
-
- /**
- * Inform listeners of an upgrade to video request for a call.
- * @param call The call.
- * @param videoState The video state we want to upgrade to.
- */
- public void upgradeToVideoRequest(Call call, int videoState) {
- Log.d(this, "upgradeToVideoRequest call = " + call + " new video state = " + videoState);
- for (SessionModificationListener listener : mSessionModificationListeners) {
- listener.onUpgradeToVideoRequest(call, videoState);
- }
- }
-
- /**
- * Inform listeners of a call session event.
- *
- * @param event The call session event.
- */
- public void callSessionEvent(int event) {
- for (VideoEventListener listener : mVideoEventListeners) {
- listener.onCallSessionEvent(event);
- }
- }
-
- /**
- * Inform listeners of a downgrade to audio.
- *
- * @param call The call.
- * @param paused The paused state.
- */
- public void peerPausedStateChanged(Call call, boolean paused) {
- for (VideoEventListener listener : mVideoEventListeners) {
- listener.onPeerPauseStateChanged(call, paused);
- }
- }
-
- /**
- * Inform listeners of any change in the video quality of the call
- *
- * @param call The call.
- * @param videoQuality The updated video quality of the call.
- */
- public void videoQualityChanged(Call call, int videoQuality) {
- for (VideoEventListener listener : mVideoEventListeners) {
- listener.onVideoQualityChanged(call, videoQuality);
- }
- }
-
- /**
- * Inform listeners of a change to peer dimensions.
- *
- * @param call The call.
- * @param width New peer width.
- * @param height New peer height.
- */
- public void peerDimensionsChanged(Call call, int width, int height) {
- for (SurfaceChangeListener listener : mSurfaceChangeListeners) {
- listener.onUpdatePeerDimensions(call, width, height);
- }
- }
-
- /**
- * Inform listeners of a change to camera dimensions.
- *
- * @param call The call.
- * @param width The new camera video width.
- * @param height The new camera video height.
- */
- public void cameraDimensionsChanged(Call call, int width, int height) {
- for (SurfaceChangeListener listener : mSurfaceChangeListeners) {
- listener.onCameraDimensionsChange(call, width, height);
- }
- }
-
- /**
- * Inform listeners of a change to call data usage.
- *
- * @param dataUsage data usage value
- */
- public void callDataUsageChanged(long dataUsage) {
- for (VideoEventListener listener : mVideoEventListeners) {
- listener.onCallDataUsageChange(dataUsage);
- }
- }
-
- /**
- * Listener interface for any class that wants to be notified of upgrade to video request.
- */
- public interface SessionModificationListener {
- /**
- * Called when a peer request is received to upgrade an audio-only call to a video call.
- *
- * @param call The call the request was received for.
- * @param videoState The requested video state.
- */
- public void onUpgradeToVideoRequest(Call call, int videoState);
- }
-
- /**
- * Listener interface for any class that wants to be notified of video events, including pause
- * and un-pause of peer video, video quality changes.
- */
- public interface VideoEventListener {
- /**
- * Called when the peer pauses or un-pauses video transmission.
- *
- * @param call The call which paused or un-paused video transmission.
- * @param paused {@code True} when the video transmission is paused, {@code false}
- * otherwise.
- */
- public void onPeerPauseStateChanged(Call call, boolean paused);
-
- /**
- * Called when the video quality changes.
- *
- * @param call The call whose video quality changes.
- * @param videoCallQuality - values are QUALITY_HIGH, MEDIUM, LOW and UNKNOWN.
- */
- public void onVideoQualityChanged(Call call, int videoCallQuality);
-
- /*
- * Called when call data usage value is requested or when call data usage value is updated
- * because of a call state change
- *
- * @param dataUsage call data usage value
- */
- public void onCallDataUsageChange(long dataUsage);
-
- /**
- * Called when call session event is raised.
- *
- * @param event The call session event.
- */
- public void onCallSessionEvent(int event);
- }
-
- /**
- * Listener interface for any class that wants to be notified of changes to the video surfaces.
- */
- public interface SurfaceChangeListener {
- /**
- * Called when the peer video feed changes dimensions. This can occur when the peer rotates
- * their device, changing the aspect ratio of the video signal.
- *
- * @param call The call which experienced a peer video
- * @param width
- * @param height
- */
- public void onUpdatePeerDimensions(Call call, int width, int height);
-
- /**
- * Called when the local camera changes dimensions. This occurs when a change in camera
- * occurs.
- *
- * @param call The call which experienced the camera dimension change.
- * @param width The new camera video width.
- * @param height The new camera video height.
- */
- public void onCameraDimensionsChange(Call call, int width, int height);
- }
-}
diff --git a/InCallUI/src/com/android/incallui/LatencyReport.java b/InCallUI/src/com/android/incallui/LatencyReport.java
deleted file mode 100644
index 655372a8f..000000000
--- a/InCallUI/src/com/android/incallui/LatencyReport.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * 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.incallui;
-
-import android.os.Bundle;
-import android.os.SystemClock;
-
-import com.android.incalluibind.ObjectFactory;
-
-/**
- * Tracks latency information for a call.
- */
-public class LatencyReport {
- // The following are hidden constants from android.telecom.TelecomManager.
- private static final String EXTRA_CALL_CREATED_TIME_MILLIS =
- "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
- private static final String EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS =
- "android.telecom.extra.CALL_TELECOM_ROUTING_START_TIME_MILLIS";
- private static final String EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS =
- "android.telecom.extra.CALL_TELECOM_ROUTING_END_TIME_MILLIS";
-
- public static final long INVALID_TIME = -1;
-
- private final boolean mWasIncoming;
-
- // Time elapsed since boot when the call was created by the connection service.
- private final long mCreatedTimeMillis;
-
- // Time elapsed since boot when telecom began processing the call.
- private final long mTelecomRoutingStartTimeMillis;
-
- // Time elapsed since boot when telecom finished processing the call. This includes things like
- // looking up contact info and call blocking but before showing any UI.
- private final long mTelecomRoutingEndTimeMillis;
-
- // Time elapsed since boot when the call was added to the InCallUi.
- private final long mCallAddedTimeMillis;
-
- // Time elapsed since boot when the call was added and call blocking evaluation was completed.
- private long mCallBlockingTimeMillis = INVALID_TIME;
-
- // Time elapsed since boot when the call notification was shown.
- private long mCallNotificationTimeMillis = INVALID_TIME;
-
- // Time elapsed since boot when the InCallUI was shown.
- private long mInCallUiShownTimeMillis = INVALID_TIME;
-
- // Whether the call was shown to the user as a heads up notification instead of a full screen
- // UI.
- private boolean mDidDisplayHeadsUpNotification;
-
- public LatencyReport() {
- mWasIncoming = false;
- mCreatedTimeMillis = INVALID_TIME;
- mTelecomRoutingStartTimeMillis = INVALID_TIME;
- mTelecomRoutingEndTimeMillis = INVALID_TIME;
- mCallAddedTimeMillis = SystemClock.elapsedRealtime();
- }
-
- public LatencyReport(android.telecom.Call telecomCall) {
- mWasIncoming = telecomCall.getState() == android.telecom.Call.STATE_RINGING;
- Bundle extras = telecomCall.getDetails().getIntentExtras();
- if (extras == null) {
- mCreatedTimeMillis = INVALID_TIME;
- mTelecomRoutingStartTimeMillis = INVALID_TIME;
- mTelecomRoutingEndTimeMillis = INVALID_TIME;
- } else {
- mCreatedTimeMillis = extras.getLong(EXTRA_CALL_CREATED_TIME_MILLIS, INVALID_TIME);
- mTelecomRoutingStartTimeMillis = extras.getLong(
- EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS, INVALID_TIME);
- mTelecomRoutingEndTimeMillis = extras.getLong(
- EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS, INVALID_TIME);
- }
- mCallAddedTimeMillis = SystemClock.elapsedRealtime();
- }
-
- public boolean getWasIncoming() {
- return mWasIncoming;
- }
-
- public long getCreatedTimeMillis() {
- return mCreatedTimeMillis;
- }
-
- public long getTelecomRoutingStartTimeMillis() {
- return mTelecomRoutingStartTimeMillis;
- }
-
- public long getTelecomRoutingEndTimeMillis() {
- return mTelecomRoutingEndTimeMillis;
- }
-
- public long getCallAddedTimeMillis() {
- return mCallAddedTimeMillis;
- }
-
- public long getCallBlockingTimeMillis() {
- return mCallBlockingTimeMillis;
- }
-
- public void onCallBlockingDone() {
- if (mCallBlockingTimeMillis == INVALID_TIME) {
- mCallBlockingTimeMillis = SystemClock.elapsedRealtime();
- }
- }
-
- public long getCallNotificationTimeMillis() {
- return mCallNotificationTimeMillis;
- }
-
- public void onNotificationShown() {
- if (mCallNotificationTimeMillis == INVALID_TIME) {
- mCallNotificationTimeMillis = SystemClock.elapsedRealtime();
- }
- }
-
- public long getInCallUiShownTimeMillis() {
- return mInCallUiShownTimeMillis;
- }
-
- public void onInCallUiShown(boolean forFullScreenIntent) {
- if (mInCallUiShownTimeMillis == INVALID_TIME) {
- mInCallUiShownTimeMillis = SystemClock.elapsedRealtime();
- mDidDisplayHeadsUpNotification = mWasIncoming && !forFullScreenIntent;
- }
- }
-
- public boolean getDidDisplayHeadsUpNotification() {
- return mDidDisplayHeadsUpNotification;
- }
-}
diff --git a/InCallUI/src/com/android/incallui/Log.java b/InCallUI/src/com/android/incallui/Log.java
deleted file mode 100644
index 07a0e61ca..000000000
--- a/InCallUI/src/com/android/incallui/Log.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.net.Uri;
-import android.telecom.PhoneAccount;
-import android.telephony.PhoneNumberUtils;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-/**
- * Manages logging for the entire class.
- */
-public class Log {
-
- // Generic tag for all In Call logging
- public static final String TAG = "InCall";
-
- public static final boolean FORCE_DEBUG = false; /* STOPSHIP if true */
- public static final boolean DEBUG = FORCE_DEBUG ||
- android.util.Log.isLoggable(TAG, android.util.Log.DEBUG);
- public static final boolean VERBOSE = FORCE_DEBUG ||
- android.util.Log.isLoggable(TAG, android.util.Log.VERBOSE);
- public static final String TAG_DELIMETER = " - ";
-
- public static void d(String tag, String msg) {
- if (DEBUG) {
- android.util.Log.d(TAG, delimit(tag) + msg);
- }
- }
-
- public static void d(Object obj, String msg) {
- if (DEBUG) {
- android.util.Log.d(TAG, getPrefix(obj) + msg);
- }
- }
-
- public static void d(Object obj, String str1, Object str2) {
- if (DEBUG) {
- android.util.Log.d(TAG, getPrefix(obj) + str1 + str2);
- }
- }
-
- public static void v(Object obj, String msg) {
- if (VERBOSE) {
- android.util.Log.v(TAG, getPrefix(obj) + msg);
- }
- }
-
- public static void v(Object obj, String str1, Object str2) {
- if (VERBOSE) {
- android.util.Log.d(TAG, getPrefix(obj) + str1 + str2);
- }
- }
-
- public static void e(String tag, String msg, Exception e) {
- android.util.Log.e(TAG, delimit(tag) + msg, e);
- }
-
- public static void e(String tag, String msg) {
- android.util.Log.e(TAG, delimit(tag) + msg);
- }
-
- public static void e(Object obj, String msg, Exception e) {
- android.util.Log.e(TAG, getPrefix(obj) + msg, e);
- }
-
- public static void e(Object obj, String msg) {
- android.util.Log.e(TAG, getPrefix(obj) + msg);
- }
-
- public static void i(String tag, String msg) {
- android.util.Log.i(TAG, delimit(tag) + msg);
- }
-
- public static void i(Object obj, String msg) {
- android.util.Log.i(TAG, getPrefix(obj) + msg);
- }
-
- public static void w(Object obj, String msg) {
- android.util.Log.w(TAG, getPrefix(obj) + msg);
- }
-
- public static void wtf(Object obj, String msg) {
- android.util.Log.wtf(TAG, getPrefix(obj) + msg);
- }
-
- public static String piiHandle(Object pii) {
- if (pii == null || VERBOSE) {
- return String.valueOf(pii);
- }
-
- if (pii instanceof Uri) {
- Uri uri = (Uri) pii;
-
- // All Uri's which are not "tel" go through normal pii() method.
- if (!PhoneAccount.SCHEME_TEL.equals(uri.getScheme())) {
- return pii(pii);
- } else {
- pii = uri.getSchemeSpecificPart();
- }
- }
-
- String originalString = String.valueOf(pii);
- StringBuilder stringBuilder = new StringBuilder(originalString.length());
- for (char c : originalString.toCharArray()) {
- if (PhoneNumberUtils.isDialable(c)) {
- stringBuilder.append('*');
- } else {
- stringBuilder.append(c);
- }
- }
- return stringBuilder.toString();
- }
-
- /**
- * Redact personally identifiable information for production users.
- * If we are running in verbose mode, return the original string, otherwise
- * return a SHA-1 hash of the input string.
- */
- public static String pii(Object pii) {
- if (pii == null || VERBOSE) {
- return String.valueOf(pii);
- }
- return "[" + secureHash(String.valueOf(pii).getBytes()) + "]";
- }
-
- private static String secureHash(byte[] input) {
- MessageDigest messageDigest;
- try {
- messageDigest = MessageDigest.getInstance("SHA-1");
- } catch (NoSuchAlgorithmException e) {
- return null;
- }
- messageDigest.update(input);
- byte[] result = messageDigest.digest();
- return encodeHex(result);
- }
-
- private static String encodeHex(byte[] bytes) {
- StringBuffer hex = new StringBuffer(bytes.length * 2);
-
- for (int i = 0; i < bytes.length; i++) {
- int byteIntValue = bytes[i] & 0xff;
- if (byteIntValue < 0x10) {
- hex.append("0");
- }
- hex.append(Integer.toString(byteIntValue, 16));
- }
-
- return hex.toString();
- }
-
- private static String getPrefix(Object obj) {
- return (obj == null ? "" : (obj.getClass().getSimpleName() + TAG_DELIMETER));
- }
-
- private static String delimit(String tag) {
- return tag + TAG_DELIMETER;
- }
-}
diff --git a/InCallUI/src/com/android/incallui/NeededForReflection.java b/InCallUI/src/com/android/incallui/NeededForReflection.java
deleted file mode 100644
index 363a0a548..000000000
--- a/InCallUI/src/com/android/incallui/NeededForReflection.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2014 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.incallui;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Denotes that the class, constructor, method or field is used for reflection and therefore cannot
- * be removed by tools like ProGuard.
- */
-@Retention(RetentionPolicy.CLASS)
-@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
-public @interface NeededForReflection {}
diff --git a/InCallUI/src/com/android/incallui/NotificationBroadcastReceiver.java b/InCallUI/src/com/android/incallui/NotificationBroadcastReceiver.java
deleted file mode 100644
index 27f71159d..000000000
--- a/InCallUI/src/com/android/incallui/NotificationBroadcastReceiver.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.incallui;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.telecom.VideoProfile;
-
-/**
- * Accepts broadcast Intents which will be prepared by {@link StatusBarNotifier} and thus
- * sent from the notification manager.
- * This should be visible from outside, but shouldn't be exported.
- */
-public class NotificationBroadcastReceiver extends BroadcastReceiver {
-
- /**
- * Intent Action used for hanging up the current call from Notification bar. This will
- * choose first ringing call, first active call, or first background call (typically in
- * STATE_HOLDING state).
- */
- public static final String ACTION_DECLINE_INCOMING_CALL =
- "com.android.incallui.ACTION_DECLINE_INCOMING_CALL";
- public static final String ACTION_HANG_UP_ONGOING_CALL =
- "com.android.incallui.ACTION_HANG_UP_ONGOING_CALL";
- public static final String ACTION_ANSWER_VIDEO_INCOMING_CALL =
- "com.android.incallui.ACTION_ANSWER_VIDEO_INCOMING_CALL";
- public static final String ACTION_ANSWER_VOICE_INCOMING_CALL =
- "com.android.incallui.ACTION_ANSWER_VOICE_INCOMING_CALL";
- public static final String ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST =
- "com.android.incallui.ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST";
- public static final String ACTION_DECLINE_VIDEO_UPGRADE_REQUEST =
- "com.android.incallui.ACTION_DECLINE_VIDEO_UPGRADE_REQUEST";
- public static final String ACTION_PULL_EXTERNAL_CALL =
- "com.android.incallui.ACTION_PULL_EXTERNAL_CALL";
- public static final String EXTRA_NOTIFICATION_ID =
- "com.android.incallui.extra.EXTRA_NOTIFICATION_ID";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- Log.i(this, "Broadcast from Notification: " + action);
-
- // TODO: Commands of this nature should exist in the CallList.
- if (action.equals(ACTION_ANSWER_VIDEO_INCOMING_CALL)) {
- InCallPresenter.getInstance().answerIncomingCall(
- context, VideoProfile.STATE_BIDIRECTIONAL);
- } else if (action.equals(ACTION_ANSWER_VOICE_INCOMING_CALL)) {
- InCallPresenter.getInstance().answerIncomingCall(
- context, VideoProfile.STATE_AUDIO_ONLY);
- } else if (action.equals(ACTION_DECLINE_INCOMING_CALL)) {
- InCallPresenter.getInstance().declineIncomingCall(context);
- } else if (action.equals(ACTION_HANG_UP_ONGOING_CALL)) {
- InCallPresenter.getInstance().hangUpOngoingCall(context);
- } else if (action.equals(ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST)) {
- //TODO: Change calltype after adding support for TX and RX
- InCallPresenter.getInstance().acceptUpgradeRequest(
- VideoProfile.STATE_BIDIRECTIONAL, context);
- } else if (action.equals(ACTION_DECLINE_VIDEO_UPGRADE_REQUEST)) {
- InCallPresenter.getInstance().declineUpgradeRequest(context);
- } else if (action.equals(ACTION_PULL_EXTERNAL_CALL)) {
- int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
- InCallPresenter.getInstance().getExternalCallNotifier()
- .pullExternalCall(notificationId);
- }
- }
-
-}
diff --git a/InCallUI/src/com/android/incallui/PostCharDialogFragment.java b/InCallUI/src/com/android/incallui/PostCharDialogFragment.java
deleted file mode 100644
index 6f904ad9e..000000000
--- a/InCallUI/src/com/android/incallui/PostCharDialogFragment.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-import com.android.dialer.R;
-
-/**
- * Pop up an alert dialog with OK and Cancel buttons to allow user to Accept or Reject the WAIT
- * inserted as part of the Dial string.
- */
-public class PostCharDialogFragment extends DialogFragment {
-
- private static final String STATE_CALL_ID = "CALL_ID";
- private static final String STATE_POST_CHARS = "POST_CHARS";
-
- private String mCallId;
- private String mPostDialStr;
-
- public PostCharDialogFragment() {
- }
-
- public PostCharDialogFragment(String callId, String postDialStr) {
- mCallId = callId;
- mPostDialStr = postDialStr;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreateDialog(savedInstanceState);
-
- if (mPostDialStr == null && savedInstanceState != null) {
- mCallId = savedInstanceState.getString(STATE_CALL_ID);
- mPostDialStr = savedInstanceState.getString(STATE_POST_CHARS);
- }
-
- final StringBuilder buf = new StringBuilder();
- buf.append(getResources().getText(R.string.wait_prompt_str));
- buf.append(mPostDialStr);
-
- final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- builder.setMessage(buf.toString());
-
- builder.setPositiveButton(R.string.pause_prompt_yes, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int whichButton) {
- TelecomAdapter.getInstance().postDialContinue(mCallId, true);
- }
- });
- builder.setNegativeButton(R.string.pause_prompt_no, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int whichButton) {
- dialog.cancel();
- }
- });
-
- final AlertDialog dialog = builder.create();
- return dialog;
- }
-
- @Override
- public void onCancel(DialogInterface dialog) {
- super.onCancel(dialog);
-
- TelecomAdapter.getInstance().postDialContinue(mCallId, false);
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
-
- outState.putString(STATE_CALL_ID, mCallId);
- outState.putString(STATE_POST_CHARS, mPostDialStr);
- }
-}
diff --git a/InCallUI/src/com/android/incallui/Presenter.java b/InCallUI/src/com/android/incallui/Presenter.java
deleted file mode 100644
index 4e1fa978d..000000000
--- a/InCallUI/src/com/android/incallui/Presenter.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import android.os.Bundle;
-
-/**
- * Base class for Presenters.
- */
-public abstract class Presenter<U extends Ui> {
-
- private U mUi;
-
- /**
- * Called after the UI view has been created. That is when fragment.onViewCreated() is called.
- *
- * @param ui The Ui implementation that is now ready to be used.
- */
- public void onUiReady(U ui) {
- mUi = ui;
- }
-
- /**
- * Called when the UI view is destroyed in Fragment.onDestroyView().
- */
- public final void onUiDestroy(U ui) {
- onUiUnready(ui);
- mUi = null;
- }
-
- /**
- * To be overriden by Presenter implementations. Called when the fragment is being
- * destroyed but before ui is set to null.
- */
- public void onUiUnready(U ui) {
- }
-
- public void onSaveInstanceState(Bundle outState) {}
-
- public void onRestoreInstanceState(Bundle savedInstanceState) {}
-
- public U getUi() {
- return mUi;
- }
-}
diff --git a/InCallUI/src/com/android/incallui/ProximitySensor.java b/InCallUI/src/com/android/incallui/ProximitySensor.java
deleted file mode 100644
index 3c9fd9370..000000000
--- a/InCallUI/src/com/android/incallui/ProximitySensor.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import com.google.common.base.Objects;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
-import android.os.PowerManager;
-import android.telecom.CallAudioState;
-import android.view.Display;
-
-import com.android.incallui.AudioModeProvider.AudioModeListener;
-import com.android.incallui.InCallPresenter.InCallState;
-import com.android.incallui.InCallPresenter.InCallStateListener;
-
-/**
- * Class manages the proximity sensor for the in-call UI.
- * We enable the proximity sensor while the user in a phone call. The Proximity sensor turns off
- * the touchscreen and display when the user is close to the screen to prevent user's cheek from
- * causing touch events.
- * The class requires special knowledge of the activity and device state to know when the proximity
- * sensor should be enabled and disabled. Most of that state is fed into this class through
- * public methods.
- */
-public class ProximitySensor implements AccelerometerListener.OrientationListener,
- InCallStateListener, AudioModeListener {
- private static final String TAG = ProximitySensor.class.getSimpleName();
-
- private final PowerManager mPowerManager;
- private final PowerManager.WakeLock mProximityWakeLock;
- private final AudioModeProvider mAudioModeProvider;
- private final AccelerometerListener mAccelerometerListener;
- private final ProximityDisplayListener mDisplayListener;
- private int mOrientation = AccelerometerListener.ORIENTATION_UNKNOWN;
- private boolean mUiShowing = false;
- private boolean mIsPhoneOffhook = false;
- private boolean mDialpadVisible;
-
- // True if the keyboard is currently *not* hidden
- // Gets updated whenever there is a Configuration change
- private boolean mIsHardKeyboardOpen;
-
- public ProximitySensor(Context context, AudioModeProvider audioModeProvider,
- AccelerometerListener accelerometerListener) {
- mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- if (mPowerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
- mProximityWakeLock = mPowerManager.newWakeLock(
- PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
- } else {
- Log.w(TAG, "Device does not support proximity wake lock.");
- mProximityWakeLock = null;
- }
- mAccelerometerListener = accelerometerListener;
- mAccelerometerListener.setListener(this);
-
- mDisplayListener = new ProximityDisplayListener(
- (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE));
- mDisplayListener.register();
-
- mAudioModeProvider = audioModeProvider;
- mAudioModeProvider.addListener(this);
- }
-
- public void tearDown() {
- mAudioModeProvider.removeListener(this);
-
- mAccelerometerListener.enable(false);
- mDisplayListener.unregister();
-
- turnOffProximitySensor(true);
- }
-
- /**
- * Called to identify when the device is laid down flat.
- */
- @Override
- public void orientationChanged(int orientation) {
- mOrientation = orientation;
- updateProximitySensorMode();
- }
-
- /**
- * Called to keep track of the overall UI state.
- */
- @Override
- public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
- // We ignore incoming state because we do not want to enable proximity
- // sensor during incoming call screen. We check hasLiveCall() because a disconnected call
- // can also put the in-call screen in the INCALL state.
- boolean hasOngoingCall = InCallState.INCALL == newState && callList.hasLiveCall();
- boolean isOffhook = (InCallState.OUTGOING == newState) || hasOngoingCall;
-
- if (isOffhook != mIsPhoneOffhook) {
- mIsPhoneOffhook = isOffhook;
-
- mOrientation = AccelerometerListener.ORIENTATION_UNKNOWN;
- mAccelerometerListener.enable(mIsPhoneOffhook);
-
- updateProximitySensorMode();
- }
- }
-
- @Override
- public void onSupportedAudioMode(int modeMask) {
- }
-
- @Override
- public void onMute(boolean muted) {
- }
-
- /**
- * Called when the audio mode changes during a call.
- */
- @Override
- public void onAudioMode(int mode) {
- updateProximitySensorMode();
- }
-
- public void onDialpadVisible(boolean visible) {
- mDialpadVisible = visible;
- updateProximitySensorMode();
- }
-
- /**
- * Called by InCallActivity to listen for hard keyboard events.
- */
- public void onConfigurationChanged(Configuration newConfig) {
- mIsHardKeyboardOpen = newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO;
-
- // Update the Proximity sensor based on keyboard state
- updateProximitySensorMode();
- }
-
- /**
- * Used to save when the UI goes in and out of the foreground.
- */
- public void onInCallShowing(boolean showing) {
- if (showing) {
- mUiShowing = true;
-
- // We only consider the UI not showing for instances where another app took the foreground.
- // If we stopped showing because the screen is off, we still consider that showing.
- } else if (mPowerManager.isScreenOn()) {
- mUiShowing = false;
- }
- updateProximitySensorMode();
- }
-
- void onDisplayStateChanged(boolean isDisplayOn) {
- Log.i(this, "isDisplayOn: " + isDisplayOn);
- mAccelerometerListener.enable(isDisplayOn);
- }
-
- /**
- * TODO: There is no way to determine if a screen is off due to proximity or if it is
- * legitimately off, but if ever we can do that in the future, it would be useful here.
- * Until then, this function will simply return true of the screen is off.
- * TODO: Investigate whether this can be replaced with the ProximityDisplayListener.
- */
- public boolean isScreenReallyOff() {
- return !mPowerManager.isScreenOn();
- }
-
- private void turnOnProximitySensor() {
- if (mProximityWakeLock != null) {
- if (!mProximityWakeLock.isHeld()) {
- Log.i(this, "Acquiring proximity wake lock");
- mProximityWakeLock.acquire();
- } else {
- Log.i(this, "Proximity wake lock already acquired");
- }
- }
- }
-
- private void turnOffProximitySensor(boolean screenOnImmediately) {
- if (mProximityWakeLock != null) {
- if (mProximityWakeLock.isHeld()) {
- Log.i(this, "Releasing proximity wake lock");
- int flags =
- (screenOnImmediately ? 0 : PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY);
- mProximityWakeLock.release(flags);
- } else {
- Log.i(this, "Proximity wake lock already released");
- }
- }
- }
-
- /**
- * Updates the wake lock used to control proximity sensor behavior,
- * based on the current state of the phone.
- *
- * On devices that have a proximity sensor, to avoid false touches
- * during a call, we hold a PROXIMITY_SCREEN_OFF_WAKE_LOCK wake lock
- * whenever the phone is off hook. (When held, that wake lock causes
- * the screen to turn off automatically when the sensor detects an
- * object close to the screen.)
- *
- * This method is a no-op for devices that don't have a proximity
- * sensor.
- *
- * Proximity wake lock will *not* be held if any one of the
- * conditions is true while on a call:
- * 1) If the audio is routed via Bluetooth
- * 2) If a wired headset is connected
- * 3) if the speaker is ON
- * 4) If the slider is open(i.e. the hardkeyboard is *not* hidden)
- */
- private synchronized void updateProximitySensorMode() {
- final int audioMode = mAudioModeProvider.getAudioMode();
-
- // turn proximity sensor off and turn screen on immediately if
- // we are using a headset, the keyboard is open, or the device
- // is being held in a horizontal position.
- boolean screenOnImmediately = (CallAudioState.ROUTE_WIRED_HEADSET == audioMode
- || CallAudioState.ROUTE_SPEAKER == audioMode
- || CallAudioState.ROUTE_BLUETOOTH == audioMode
- || mIsHardKeyboardOpen);
-
- // We do not keep the screen off when the user is outside in-call screen and we are
- // horizontal, but we do not force it on when we become horizontal until the
- // proximity sensor goes negative.
- final boolean horizontal =
- (mOrientation == AccelerometerListener.ORIENTATION_HORIZONTAL);
- screenOnImmediately |= !mUiShowing && horizontal;
-
- // We do not keep the screen off when dialpad is visible, we are horizontal, and
- // the in-call screen is being shown.
- // At that moment we're pretty sure users want to use it, instead of letting the
- // proximity sensor turn off the screen by their hands.
- screenOnImmediately |= mDialpadVisible && horizontal;
-
- Log.v(this, "screenonImmediately: ", screenOnImmediately);
-
- Log.i(this, Objects.toStringHelper(this)
- .add("keybrd", mIsHardKeyboardOpen ? 1 : 0)
- .add("dpad", mDialpadVisible ? 1 : 0)
- .add("offhook", mIsPhoneOffhook ? 1 : 0)
- .add("hor", horizontal ? 1 : 0)
- .add("ui", mUiShowing ? 1 : 0)
- .add("aud", CallAudioState.audioRouteToString(audioMode))
- .toString());
-
- if (mIsPhoneOffhook && !screenOnImmediately) {
- Log.d(this, "Turning on proximity sensor");
- // Phone is in use! Arrange for the screen to turn off
- // automatically when the sensor detects a close object.
- turnOnProximitySensor();
- } else {
- Log.d(this, "Turning off proximity sensor");
- // Phone is either idle, or ringing. We don't want any special proximity sensor
- // behavior in either case.
- turnOffProximitySensor(screenOnImmediately);
- }
- }
-
- /**
- * Implementation of a {@link DisplayListener} that maintains a binary state:
- * Screen on vs screen off. Used by the proximity sensor manager to decide whether or not
- * it needs to listen to accelerometer events.
- */
- public class ProximityDisplayListener implements DisplayListener {
- private DisplayManager mDisplayManager;
- private boolean mIsDisplayOn = true;
-
- ProximityDisplayListener(DisplayManager displayManager) {
- mDisplayManager = displayManager;
- }
-
- void register() {
- mDisplayManager.registerDisplayListener(this, null);
- }
-
- void unregister() {
- mDisplayManager.unregisterDisplayListener(this);
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- }
-
- @Override
- public void onDisplayChanged(int displayId) {
- if (displayId == Display.DEFAULT_DISPLAY) {
- final Display display = mDisplayManager.getDisplay(displayId);
-
- final boolean isDisplayOn = display.getState() != Display.STATE_OFF;
- // For call purposes, we assume that as long as the screen is not truly off, it is
- // considered on, even if it is in an unknown or low power idle state.
- if (isDisplayOn != mIsDisplayOn) {
- mIsDisplayOn = isDisplayOn;
- onDisplayStateChanged(mIsDisplayOn);
- }
- }
- }
-
- @Override
- public void onDisplayAdded(int displayId) {
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
deleted file mode 100644
index cc87dd414..000000000
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ /dev/null
@@ -1,793 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui;
-
-import static com.android.contacts.common.compat.CallSdkCompat.Details.PROPERTY_ENTERPRISE_CALL;
-import static com.android.incallui.NotificationBroadcastReceiver.ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST;
-import static com.android.incallui.NotificationBroadcastReceiver.ACTION_ANSWER_VIDEO_INCOMING_CALL;
-import static com.android.incallui.NotificationBroadcastReceiver.ACTION_ANSWER_VOICE_INCOMING_CALL;
-import static com.android.incallui.NotificationBroadcastReceiver.ACTION_DECLINE_INCOMING_CALL;
-import static com.android.incallui.NotificationBroadcastReceiver.ACTION_DECLINE_VIDEO_UPGRADE_REQUEST;
-import static com.android.incallui.NotificationBroadcastReceiver.ACTION_HANG_UP_ONGOING_CALL;
-
-import com.google.common.base.Preconditions;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.media.AudioAttributes;
-import android.net.Uri;
-import android.provider.ContactsContract.Contacts;
-import android.support.annotation.Nullable;
-import android.telecom.Call.Details;
-import android.telecom.PhoneAccount;
-import android.telecom.TelecomManager;
-import android.text.BidiFormatter;
-import android.text.TextDirectionHeuristics;
-import android.text.TextUtils;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.ContactsUtils.UserType;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.contacts.common.util.BitmapUtil;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.R;
-import com.android.dialer.service.ExtendedCallInfoService;
-import com.android.incallui.ContactInfoCache.ContactCacheEntry;
-import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback;
-import com.android.incallui.InCallPresenter.InCallState;
-import com.android.incallui.async.PausableExecutorImpl;
-import com.android.incallui.ringtone.DialerRingtoneManager;
-import com.android.incallui.ringtone.InCallTonePlayer;
-import com.android.incallui.ringtone.ToneGeneratorFactory;
-
-import java.util.Objects;
-
-/**
- * This class adds Notifications to the status bar for the in-call experience.
- */
-public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
- CallList.CallUpdateListener {
-
- // Notification types
- // Indicates that no notification is currently showing.
- private static final int NOTIFICATION_NONE = 0;
- // Notification for an active call. This is non-interruptive, but cannot be dismissed.
- private static final int NOTIFICATION_IN_CALL = 1;
- // Notification for incoming calls. This is interruptive and will show up as a HUN.
- private static final int NOTIFICATION_INCOMING_CALL = 2;
-
- private static final int PENDING_INTENT_REQUEST_CODE_NON_FULL_SCREEN = 0;
- private static final int PENDING_INTENT_REQUEST_CODE_FULL_SCREEN = 1;
-
- private static final long[] VIBRATE_PATTERN = new long[] {0, 1000, 1000};
-
- private final Context mContext;
- @Nullable private ContactsPreferences mContactsPreferences;
- private final ContactInfoCache mContactInfoCache;
- private final NotificationManager mNotificationManager;
- private final DialerRingtoneManager mDialerRingtoneManager;
- private int mCurrentNotification = NOTIFICATION_NONE;
- private int mCallState = Call.State.INVALID;
- private int mSavedIcon = 0;
- private String mSavedContent = null;
- private Bitmap mSavedLargeIcon;
- private String mSavedContentTitle;
- private String mCallId = null;
- private InCallState mInCallState;
- private Uri mRingtone;
-
- public StatusBarNotifier(Context context, ContactInfoCache contactInfoCache) {
- Preconditions.checkNotNull(context);
- mContext = context;
- mContactsPreferences = ContactsPreferencesFactory.newContactsPreferences(mContext);
- mContactInfoCache = contactInfoCache;
- mNotificationManager =
- (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- mDialerRingtoneManager = new DialerRingtoneManager(
- new InCallTonePlayer(new ToneGeneratorFactory(), new PausableExecutorImpl()),
- CallList.getInstance());
- mCurrentNotification = NOTIFICATION_NONE;
- }
-
- /**
- * Creates notifications according to the state we receive from {@link InCallPresenter}.
- */
- @Override
- public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
- Log.d(this, "onStateChange");
- mInCallState = newState;
- updateNotification(newState, callList);
- }
-
- /**
- * Updates the phone app's status bar notification *and* launches the
- * incoming call UI in response to a new incoming call.
- *
- * If an incoming call is ringing (or call-waiting), the notification
- * will also include a "fullScreenIntent" that will cause the
- * InCallScreen to be launched, unless the current foreground activity
- * is marked as "immersive".
- *
- * (This is the mechanism that actually brings up the incoming call UI
- * when we receive a "new ringing connection" event from the telephony
- * layer.)
- *
- * Also note that this method is safe to call even if the phone isn't
- * actually ringing (or, more likely, if an incoming call *was*
- * ringing briefly but then disconnected). In that case, we'll simply
- * update or cancel the in-call notification based on the current
- * phone state.
- *
- * @see #updateInCallNotification(InCallState,CallList)
- */
- public void updateNotification(InCallState state, CallList callList) {
- updateInCallNotification(state, callList);
- }
-
- /**
- * Take down the in-call notification.
- * @see #updateInCallNotification(InCallState,CallList)
- */
- private void cancelNotification() {
- if (!TextUtils.isEmpty(mCallId)) {
- CallList.getInstance().removeCallUpdateListener(mCallId, this);
- mCallId = null;
- }
- if (mCurrentNotification != NOTIFICATION_NONE) {
- Log.d(this, "cancelInCall()...");
- mNotificationManager.cancel(mCurrentNotification);
- }
- mCurrentNotification = NOTIFICATION_NONE;
- }
-
- /**
- * Should only be called from a irrecoverable state where it is necessary to dismiss all
- * notifications.
- */
- static void clearAllCallNotifications(Context backupContext) {
- Log.i(StatusBarNotifier.class.getSimpleName(),
- "Something terrible happened. Clear all InCall notifications");
-
- NotificationManager notificationManager =
- (NotificationManager) backupContext.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancel(NOTIFICATION_IN_CALL);
- notificationManager.cancel(NOTIFICATION_INCOMING_CALL);
- }
-
- /**
- * Helper method for updateInCallNotification() and
- * updateNotification(): Update the phone app's
- * status bar notification based on the current telephony state, or
- * cancels the notification if the phone is totally idle.
- */
- private void updateInCallNotification(final InCallState state, CallList callList) {
- Log.d(this, "updateInCallNotification...");
-
- final Call call = getCallToShow(callList);
-
- if (call != null) {
- showNotification(call);
- } else {
- cancelNotification();
- }
- }
-
- private void showNotification(final Call call) {
- final boolean isIncoming = (call.getState() == Call.State.INCOMING ||
- call.getState() == Call.State.CALL_WAITING);
- if (!TextUtils.isEmpty(mCallId)) {
- CallList.getInstance().removeCallUpdateListener(mCallId, this);
- }
- mCallId = call.getId();
- CallList.getInstance().addCallUpdateListener(call.getId(), this);
-
- // we make a call to the contact info cache to query for supplemental data to what the
- // call provides. This includes the contact name and photo.
- // This callback will always get called immediately and synchronously with whatever data
- // it has available, and may make a subsequent call later (same thread) if it had to
- // call into the contacts provider for more data.
- mContactInfoCache.findInfo(call, isIncoming, new ContactInfoCacheCallback() {
- @Override
- public void onContactInfoComplete(String callId, ContactCacheEntry entry) {
- Call call = CallList.getInstance().getCallById(callId);
- if (call != null) {
- call.getLogState().contactLookupResult = entry.contactLookupResult;
- buildAndSendNotification(call, entry);
- }
- }
-
- @Override
- public void onImageLoadComplete(String callId, ContactCacheEntry entry) {
- Call call = CallList.getInstance().getCallById(callId);
- if (call != null) {
- buildAndSendNotification(call, entry);
- }
- }
-
- @Override
- public void onContactInteractionsInfoComplete(String callId, ContactCacheEntry entry) {}
- });
- }
-
- /**
- * Sets up the main Ui for the notification
- */
- private void buildAndSendNotification(Call originalCall, ContactCacheEntry contactInfo) {
- // This can get called to update an existing notification after contact information has come
- // back. However, it can happen much later. Before we continue, we need to make sure that
- // the call being passed in is still the one we want to show in the notification.
- final Call call = getCallToShow(CallList.getInstance());
- if (call == null || !call.getId().equals(originalCall.getId())) {
- return;
- }
-
- final int callState = call.getState();
- // Dont' show as spam if the number is in local contact.
- if (contactInfo.contactLookupResult == Call.LogState.LOOKUP_LOCAL_CONTACT) {
- call.setSpam(false);
- }
-
- // Check if data has changed; if nothing is different, don't issue another notification.
- final int iconResId = getIconToDisplay(call);
- Bitmap largeIcon = getLargeIconToDisplay(contactInfo, call);
- final String content =
- getContentString(call, contactInfo.userType);
- final String contentTitle = getContentTitle(contactInfo, call);
-
- final boolean isVideoUpgradeRequest = call.getSessionModificationState()
- == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
- final int notificationType;
- if (callState == Call.State.INCOMING || callState == Call.State.CALL_WAITING
- || isVideoUpgradeRequest) {
- notificationType = NOTIFICATION_INCOMING_CALL;
- } else {
- notificationType = NOTIFICATION_IN_CALL;
- }
-
- if (!checkForChangeAndSaveData(iconResId, content, largeIcon, contentTitle, callState,
- notificationType, contactInfo.contactRingtoneUri)) {
- return;
- }
-
- if (largeIcon != null) {
- largeIcon = getRoundedIcon(largeIcon);
- }
-
- /*
- * This builder is used for the notification shown when the device is locked and the user
- * has set their notification settings to 'hide sensitive content'
- * {@see Notification.Builder#setPublicVersion}.
- */
- Notification.Builder publicBuilder = new Notification.Builder(mContext);
- publicBuilder.setSmallIcon(iconResId)
- .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
- // Hide work call state for the lock screen notification
- .setContentTitle(getContentString(call, ContactsUtils.USER_TYPE_CURRENT));
- setNotificationWhen(call, callState, publicBuilder);
-
- /*
- * Builder for the notification shown when the device is unlocked or the user has set their
- * notification settings to 'show all notification content'.
- */
- final Notification.Builder builder = getNotificationBuilder();
- builder.setPublicVersion(publicBuilder.build());
-
- // Set up the main intent to send the user to the in-call screen
- builder.setContentIntent(createLaunchPendingIntent(false /* isFullScreen */));
-
- // Set the intent as a full screen intent as well if a call is incoming
- if (notificationType == NOTIFICATION_INCOMING_CALL
- && !InCallPresenter.getInstance().isShowingInCallUi()) {
- configureFullScreenIntent(
- builder, createLaunchPendingIntent(true /* isFullScreen */), call);
- // Set the notification category for incoming calls
- builder.setCategory(Notification.CATEGORY_CALL);
- }
-
- // Set the content
- builder.setContentText(content);
- builder.setSmallIcon(iconResId);
- builder.setContentTitle(contentTitle);
- builder.setLargeIcon(largeIcon);
- builder.setColor(mContext.getResources().getColor(R.color.dialer_theme_color));
-
- if (isVideoUpgradeRequest) {
- builder.setUsesChronometer(false);
- addDismissUpgradeRequestAction(builder);
- addAcceptUpgradeRequestAction(builder);
- } else {
- createIncomingCallNotification(call, callState, builder);
- }
-
- addPersonReference(builder, contactInfo, call);
-
- /*
- * Fire off the notification
- */
- Notification notification = builder.build();
-
- if (mDialerRingtoneManager.shouldPlayRingtone(callState, contactInfo.contactRingtoneUri)) {
- notification.flags |= Notification.FLAG_INSISTENT;
- notification.sound = contactInfo.contactRingtoneUri;
- AudioAttributes.Builder audioAttributes = new AudioAttributes.Builder();
- audioAttributes.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC);
- audioAttributes.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE);
- notification.audioAttributes = audioAttributes.build();
- if (mDialerRingtoneManager.shouldVibrate(mContext.getContentResolver())) {
- notification.vibrate = VIBRATE_PATTERN;
- }
- }
- if (mDialerRingtoneManager.shouldPlayCallWaitingTone(callState)) {
- Log.v(this, "Playing call waiting tone");
- mDialerRingtoneManager.playCallWaitingTone();
- }
- if (mCurrentNotification != notificationType && mCurrentNotification != NOTIFICATION_NONE) {
- Log.i(this, "Previous notification already showing - cancelling "
- + mCurrentNotification);
- mNotificationManager.cancel(mCurrentNotification);
- }
-
- Log.i(this, "Displaying notification for " + notificationType);
- mNotificationManager.notify(notificationType, notification);
- call.getLatencyReport().onNotificationShown();
- mCurrentNotification = notificationType;
- }
-
- private void createIncomingCallNotification(
- Call call, int state, Notification.Builder builder) {
- setNotificationWhen(call, state, builder);
-
- // Add hang up option for any active calls (active | onhold), outgoing calls (dialing).
- if (state == Call.State.ACTIVE ||
- state == Call.State.ONHOLD ||
- Call.State.isDialing(state)) {
- addHangupAction(builder);
- } else if (state == Call.State.INCOMING || state == Call.State.CALL_WAITING) {
- addDismissAction(builder);
- if (call.isVideoCall(mContext)) {
- addVoiceAction(builder);
- addVideoCallAction(builder);
- } else {
- addAnswerAction(builder);
- }
- }
- }
-
- /*
- * Sets the notification's when section as needed. For active calls, this is explicitly set as
- * the duration of the call. For all other states, the notification will automatically show the
- * time at which the notification was created.
- */
- private void setNotificationWhen(Call call, int state, Notification.Builder builder) {
- if (state == Call.State.ACTIVE) {
- builder.setUsesChronometer(true);
- builder.setWhen(call.getConnectTimeMillis());
- } else {
- builder.setUsesChronometer(false);
- }
- }
-
- /**
- * Checks the new notification data and compares it against any notification that we
- * are already displaying. If the data is exactly the same, we return false so that
- * we do not issue a new notification for the exact same data.
- */
- private boolean checkForChangeAndSaveData(int icon, String content, Bitmap largeIcon,
- String contentTitle, int state, int notificationType, Uri ringtone) {
-
- // The two are different:
- // if new title is not null, it should be different from saved version OR
- // if new title is null, the saved version should not be null
- final boolean contentTitleChanged =
- (contentTitle != null && !contentTitle.equals(mSavedContentTitle)) ||
- (contentTitle == null && mSavedContentTitle != null);
-
- // any change means we are definitely updating
- boolean retval = (mSavedIcon != icon) || !Objects.equals(mSavedContent, content)
- || (mCallState != state) || (mSavedLargeIcon != largeIcon)
- || contentTitleChanged || !Objects.equals(mRingtone, ringtone);
-
- // If we aren't showing a notification right now or the notification type is changing,
- // definitely do an update.
- if (mCurrentNotification != notificationType) {
- if (mCurrentNotification == NOTIFICATION_NONE) {
- Log.d(this, "Showing notification for first time.");
- }
- retval = true;
- }
-
- mSavedIcon = icon;
- mSavedContent = content;
- mCallState = state;
- mSavedLargeIcon = largeIcon;
- mSavedContentTitle = contentTitle;
- mRingtone = ringtone;
-
- if (retval) {
- Log.d(this, "Data changed. Showing notification");
- }
-
- return retval;
- }
-
- /**
- * Returns the main string to use in the notification.
- */
- @NeededForTesting
- String getContentTitle(ContactCacheEntry contactInfo, Call call) {
- if (call.isConferenceCall() && !call.hasProperty(Details.PROPERTY_GENERIC_CONFERENCE)) {
- return mContext.getResources().getString(R.string.card_title_conf_call);
- }
-
- String preferredName = ContactDisplayUtils.getPreferredDisplayName(contactInfo.namePrimary,
- contactInfo.nameAlternative, mContactsPreferences);
- if (TextUtils.isEmpty(preferredName)) {
- return TextUtils.isEmpty(contactInfo.number) ? null : BidiFormatter.getInstance()
- .unicodeWrap(contactInfo.number, TextDirectionHeuristics.LTR);
- }
- return preferredName;
- }
-
- private void addPersonReference(Notification.Builder builder, ContactCacheEntry contactInfo,
- Call call) {
- // Query {@link Contacts#CONTENT_LOOKUP_URI} directly with work lookup key is not allowed.
- // So, do not pass {@link Contacts#CONTENT_LOOKUP_URI} to NotificationManager to avoid
- // NotificationManager using it.
- if (contactInfo.lookupUri != null && contactInfo.userType != ContactsUtils.USER_TYPE_WORK) {
- builder.addPerson(contactInfo.lookupUri.toString());
- } else if (!TextUtils.isEmpty(call.getNumber())) {
- builder.addPerson(Uri.fromParts(PhoneAccount.SCHEME_TEL,
- call.getNumber(), null).toString());
- }
- }
-
- /**
- * Gets a large icon from the contact info object to display in the notification.
- */
- private Bitmap getLargeIconToDisplay(ContactCacheEntry contactInfo, Call call) {
- Bitmap largeIcon = null;
- if (call.isConferenceCall() && !call.hasProperty(Details.PROPERTY_GENERIC_CONFERENCE)) {
- largeIcon = BitmapFactory.decodeResource(mContext.getResources(),
- R.drawable.img_conference);
- }
- if (contactInfo.photo != null && (contactInfo.photo instanceof BitmapDrawable)) {
- largeIcon = ((BitmapDrawable) contactInfo.photo).getBitmap();
- }
- if (call.isSpam()) {
- Drawable drawable = mContext.getResources().getDrawable(R.drawable.blocked_contact);
- largeIcon = CallCardFragment.drawableToBitmap(drawable);
- }
- return largeIcon;
- }
-
- private Bitmap getRoundedIcon(Bitmap bitmap) {
- if (bitmap == null) {
- return null;
- }
- final int height = (int) mContext.getResources().getDimension(
- android.R.dimen.notification_large_icon_height);
- final int width = (int) mContext.getResources().getDimension(
- android.R.dimen.notification_large_icon_width);
- return BitmapUtil.getRoundedBitmap(bitmap, width, height);
- }
-
- /**
- * Returns the appropriate icon res Id to display based on the call for which
- * we want to display information.
- */
- private int getIconToDisplay(Call call) {
- // Even if both lines are in use, we only show a single item in
- // the expanded Notifications UI. It's labeled "Ongoing call"
- // (or "On hold" if there's only one call, and it's on hold.)
- // Also, we don't have room to display caller-id info from two
- // different calls. So if both lines are in use, display info
- // from the foreground call. And if there's a ringing call,
- // display that regardless of the state of the other calls.
- if (call.getState() == Call.State.ONHOLD) {
- return R.drawable.ic_phone_paused_white_24dp;
- } else if (call.getSessionModificationState()
- == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
- return R.drawable.ic_videocam;
- }
- return R.drawable.ic_call_white_24dp;
- }
-
- /**
- * Returns the message to use with the notification.
- */
- private String getContentString(Call call, @UserType long userType) {
- boolean isIncomingOrWaiting = call.getState() == Call.State.INCOMING ||
- call.getState() == Call.State.CALL_WAITING;
-
- if (isIncomingOrWaiting &&
- call.getNumberPresentation() == TelecomManager.PRESENTATION_ALLOWED) {
-
- if (!TextUtils.isEmpty(call.getChildNumber())) {
- return mContext.getString(R.string.child_number, call.getChildNumber());
- } else if (!TextUtils.isEmpty(call.getCallSubject()) && call.isCallSubjectSupported()) {
- return call.getCallSubject();
- }
- }
-
- int resId = R.string.notification_ongoing_call;
- if (call.hasProperty(Details.PROPERTY_WIFI)) {
- resId = R.string.notification_ongoing_call_wifi;
- }
-
- if (isIncomingOrWaiting) {
- if (call.hasProperty(Details.PROPERTY_WIFI)) {
- resId = R.string.notification_incoming_call_wifi;
- } else {
- if (call.isSpam()) {
- resId = R.string.notification_incoming_spam_call;
- } else {
- resId = R.string.notification_incoming_call;
- }
- }
- } else if (call.getState() == Call.State.ONHOLD) {
- resId = R.string.notification_on_hold;
- } else if (Call.State.isDialing(call.getState())) {
- resId = R.string.notification_dialing;
- } else if (call.getSessionModificationState()
- == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
- resId = R.string.notification_requesting_video_call;
- }
-
- // Is the call placed through work connection service.
- boolean isWorkCall = call.hasProperty(PROPERTY_ENTERPRISE_CALL);
- if(userType == ContactsUtils.USER_TYPE_WORK || isWorkCall) {
- resId = getWorkStringFromPersonalString(resId);
- }
-
- return mContext.getString(resId);
- }
-
- private static int getWorkStringFromPersonalString(int resId) {
- if (resId == R.string.notification_ongoing_call) {
- return R.string.notification_ongoing_work_call;
- } else if (resId == R.string.notification_ongoing_call_wifi) {
- return R.string.notification_ongoing_work_call_wifi;
- } else if (resId == R.string.notification_incoming_call_wifi) {
- return R.string.notification_incoming_work_call_wifi;
- } else if (resId == R.string.notification_incoming_call) {
- return R.string.notification_incoming_work_call;
- } else {
- return resId;
- }
- }
-
- /**
- * Gets the most relevant call to display in the notification.
- */
- private Call getCallToShow(CallList callList) {
- if (callList == null) {
- return null;
- }
- Call call = callList.getIncomingCall();
- if (call == null) {
- call = callList.getOutgoingCall();
- }
- if (call == null) {
- call = callList.getVideoUpgradeRequestCall();
- }
- if (call == null) {
- call = callList.getActiveOrBackgroundCall();
- }
- return call;
- }
-
- private void addAnswerAction(Notification.Builder builder) {
- Log.d(this, "Will show \"answer\" action in the incoming call Notification");
-
- PendingIntent answerVoicePendingIntent = createNotificationPendingIntent(
- mContext, ACTION_ANSWER_VOICE_INCOMING_CALL);
- builder.addAction(R.drawable.ic_call_white_24dp,
- mContext.getText(R.string.notification_action_answer),
- answerVoicePendingIntent);
- }
-
- private void addDismissAction(Notification.Builder builder) {
- Log.d(this, "Will show \"dismiss\" action in the incoming call Notification");
-
- PendingIntent declinePendingIntent =
- createNotificationPendingIntent(mContext, ACTION_DECLINE_INCOMING_CALL);
- builder.addAction(R.drawable.ic_close_dk,
- mContext.getText(R.string.notification_action_dismiss),
- declinePendingIntent);
- }
-
- private void addHangupAction(Notification.Builder builder) {
- Log.d(this, "Will show \"hang-up\" action in the ongoing active call Notification");
-
- PendingIntent hangupPendingIntent =
- createNotificationPendingIntent(mContext, ACTION_HANG_UP_ONGOING_CALL);
- builder.addAction(R.drawable.ic_call_end_white_24dp,
- mContext.getText(R.string.notification_action_end_call),
- hangupPendingIntent);
- }
-
- private void addVideoCallAction(Notification.Builder builder) {
- Log.i(this, "Will show \"video\" action in the incoming call Notification");
-
- PendingIntent answerVideoPendingIntent = createNotificationPendingIntent(
- mContext, ACTION_ANSWER_VIDEO_INCOMING_CALL);
- builder.addAction(R.drawable.ic_videocam,
- mContext.getText(R.string.notification_action_answer_video),
- answerVideoPendingIntent);
- }
-
- private void addVoiceAction(Notification.Builder builder) {
- Log.d(this, "Will show \"voice\" action in the incoming call Notification");
-
- PendingIntent answerVoicePendingIntent = createNotificationPendingIntent(
- mContext, ACTION_ANSWER_VOICE_INCOMING_CALL);
- builder.addAction(R.drawable.ic_call_white_24dp,
- mContext.getText(R.string.notification_action_answer_voice),
- answerVoicePendingIntent);
- }
-
- private void addAcceptUpgradeRequestAction(Notification.Builder builder) {
- Log.i(this, "Will show \"accept upgrade\" action in the incoming call Notification");
-
- PendingIntent acceptVideoPendingIntent = createNotificationPendingIntent(
- mContext, ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST);
- builder.addAction(0, mContext.getText(R.string.notification_action_accept),
- acceptVideoPendingIntent);
- }
-
- private void addDismissUpgradeRequestAction(Notification.Builder builder) {
- Log.i(this, "Will show \"dismiss upgrade\" action in the incoming call Notification");
-
- PendingIntent declineVideoPendingIntent = createNotificationPendingIntent(
- mContext, ACTION_DECLINE_VIDEO_UPGRADE_REQUEST);
- builder.addAction(0, mContext.getText(R.string.notification_action_dismiss),
- declineVideoPendingIntent);
- }
-
- /**
- * Adds fullscreen intent to the builder.
- */
- private void configureFullScreenIntent(Notification.Builder builder, PendingIntent intent,
- Call call) {
- // Ok, we actually want to launch the incoming call
- // UI at this point (in addition to simply posting a notification
- // to the status bar). Setting fullScreenIntent will cause
- // the InCallScreen to be launched immediately *unless* the
- // current foreground activity is marked as "immersive".
- Log.d(this, "- Setting fullScreenIntent: " + intent);
- builder.setFullScreenIntent(intent, true);
-
- // Ugly hack alert:
- //
- // The NotificationManager has the (undocumented) behavior
- // that it will *ignore* the fullScreenIntent field if you
- // post a new Notification that matches the ID of one that's
- // already active. Unfortunately this is exactly what happens
- // when you get an incoming call-waiting call: the
- // "ongoing call" notification is already visible, so the
- // InCallScreen won't get launched in this case!
- // (The result: if you bail out of the in-call UI while on a
- // call and then get a call-waiting call, the incoming call UI
- // won't come up automatically.)
- //
- // The workaround is to just notice this exact case (this is a
- // call-waiting call *and* the InCallScreen is not in the
- // foreground) and manually cancel the in-call notification
- // before (re)posting it.
- //
- // TODO: there should be a cleaner way of avoiding this
- // problem (see discussion in bug 3184149.)
-
- // If a call is onhold during an incoming call, the call actually comes in as
- // INCOMING. For that case *and* traditional call-waiting, we want to
- // cancel the notification.
- boolean isCallWaiting = (call.getState() == Call.State.CALL_WAITING ||
- (call.getState() == Call.State.INCOMING &&
- CallList.getInstance().getBackgroundCall() != null));
-
- if (isCallWaiting) {
- Log.i(this, "updateInCallNotification: call-waiting! force relaunch...");
- // Cancel the IN_CALL_NOTIFICATION immediately before
- // (re)posting it; this seems to force the
- // NotificationManager to launch the fullScreenIntent.
- mNotificationManager.cancel(NOTIFICATION_IN_CALL);
- }
- }
-
- private Notification.Builder getNotificationBuilder() {
- final Notification.Builder builder = new Notification.Builder(mContext);
- builder.setOngoing(true);
-
- // Make the notification prioritized over the other normal notifications.
- builder.setPriority(Notification.PRIORITY_HIGH);
-
- return builder;
- }
-
- private PendingIntent createLaunchPendingIntent(boolean isFullScreen) {
- Intent intent = InCallPresenter.getInstance().getInCallIntent(
- false /* showDialpad */, false /* newOutgoingCall */);
-
- int requestCode = PENDING_INTENT_REQUEST_CODE_NON_FULL_SCREEN;
- if (isFullScreen) {
- intent.putExtra(InCallActivity.FOR_FULL_SCREEN_INTENT, true);
- // Use a unique request code so that the pending intent isn't clobbered by the
- // non-full screen pending intent.
- requestCode = PENDING_INTENT_REQUEST_CODE_FULL_SCREEN;
- }
-
- // PendingIntent that can be used to launch the InCallActivity. The
- // system fires off this intent if the user pulls down the windowshade
- // and clicks the notification's expanded view. It's also used to
- // launch the InCallActivity immediately when when there's an incoming
- // call (see the "fullScreenIntent" field below).
- return PendingIntent.getActivity(mContext, requestCode, intent, 0);
- }
-
- /**
- * Returns PendingIntent for answering a phone call. This will typically be used from
- * Notification context.
- */
- private static PendingIntent createNotificationPendingIntent(Context context, String action) {
- final Intent intent = new Intent(action, null,
- context, NotificationBroadcastReceiver.class);
- return PendingIntent.getBroadcast(context, 0, intent, 0);
- }
-
- @Override
- public void onCallChanged(Call call) {
- if (CallList.getInstance().getIncomingCall() == null) {
- mDialerRingtoneManager.stopCallWaitingTone();
- }
- }
-
- /**
- * Responds to changes in the session modification state for the call by dismissing the
- * status bar notification as required.
- *
- * @param sessionModificationState The new session modification state.
- */
- @Override
- public void onSessionModificationStateChange(int sessionModificationState) {
- if (sessionModificationState == Call.SessionModificationState.NO_REQUEST) {
- if (mCallId != null) {
- CallList.getInstance().removeCallUpdateListener(mCallId, this);
- }
-
- updateNotification(mInCallState, CallList.getInstance());
- }
- }
-
- @Override
- public void onLastForwardedNumberChange() {
- // no-op
- }
-
- @Override
- public void onChildNumberChange() {
- // no-op
- }
-}
diff --git a/InCallUI/src/com/android/incallui/TelecomAdapter.java b/InCallUI/src/com/android/incallui/TelecomAdapter.java
deleted file mode 100644
index f172270dd..000000000
--- a/InCallUI/src/com/android/incallui/TelecomAdapter.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (C) 2014 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.incallui;
-
-import com.google.common.base.Preconditions;
-
-import android.content.ActivityNotFoundException;
-import android.content.Intent;
-import android.os.Looper;
-import android.telecom.InCallService;
-import android.telecom.PhoneAccountHandle;
-
-import java.util.List;
-
-final class TelecomAdapter implements InCallServiceListener {
- private static final String ADD_CALL_MODE_KEY = "add_call_mode";
-
- private static TelecomAdapter sInstance;
- private InCallService mInCallService;
-
- static TelecomAdapter getInstance() {
- Preconditions.checkState(Looper.getMainLooper().getThread() == Thread.currentThread());
- if (sInstance == null) {
- sInstance = new TelecomAdapter();
- }
- return sInstance;
- }
-
- private TelecomAdapter() {
- }
-
- @Override
- public void setInCallService(InCallService inCallService) {
- mInCallService = inCallService;
- }
-
- @Override
- public void clearInCallService() {
- mInCallService = null;
- }
-
- private android.telecom.Call getTelecomCallById(String callId) {
- Call call = CallList.getInstance().getCallById(callId);
- return call == null ? null : call.getTelecomCall();
- }
-
- void answerCall(String callId, int videoState) {
- android.telecom.Call call = getTelecomCallById(callId);
- if (call != null) {
- call.answer(videoState);
- } else {
- Log.e(this, "error answerCall, call not in call list: " + callId);
- }
- }
-
- void rejectCall(String callId, boolean rejectWithMessage, String message) {
- android.telecom.Call call = getTelecomCallById(callId);
- if (call != null) {
- call.reject(rejectWithMessage, message);
- } else {
- Log.e(this, "error rejectCall, call not in call list: " + callId);
- }
- }
-
- void disconnectCall(String callId) {
- android.telecom.Call call = getTelecomCallById(callId);
- if (call != null) {
- call.disconnect();
- } else {
- Log.e(this, "error disconnectCall, call not in call list " + callId);
- }
- }
-
- void holdCall(String callId) {
- android.telecom.Call call = getTelecomCallById(callId);
- if (call != null) {
- call.hold();
- } else {
- Log.e(this, "error holdCall, call not in call list " + callId);
- }
- }
-
- void unholdCall(String callId) {
- android.telecom.Call call = getTelecomCallById(callId);
- if (call != null) {
- call.unhold();
- } else {
- Log.e(this, "error unholdCall, call not in call list " + callId);
- }
- }
-
- void mute(boolean shouldMute) {
- if (mInCallService != null) {
- mInCallService.setMuted(shouldMute);
- } else {
- Log.e(this, "error mute, mInCallService is null");
- }
- }
-
- void setAudioRoute(int route) {
- if (mInCallService != null) {
- mInCallService.setAudioRoute(route);
- } else {
- Log.e(this, "error setAudioRoute, mInCallService is null");
- }
- }
-
- void separateCall(String callId) {
- android.telecom.Call call = getTelecomCallById(callId);
- if (call != null) {
- call.splitFromConference();
- } else {
- Log.e(this, "error separateCall, call not in call list " + callId);
- }
- }
-
- void merge(String callId) {
- android.telecom.Call call = getTelecomCallById(callId);
- if (call != null) {
- List<android.telecom.Call> conferenceable = call.getConferenceableCalls();
- if (!conferenceable.isEmpty()) {
- call.conference(conferenceable.get(0));
- } else {
- if (call.getDetails().can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE)) {
- call.mergeConference();
- }
- }
- } else {
- Log.e(this, "error merge, call not in call list " + callId);
- }
- }
-
- void swap(String callId) {
- android.telecom.Call call = getTelecomCallById(callId);
- if (call != null) {
- if (call.getDetails().can(android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE)) {
- call.swapConference();
- }
- } else {
- Log.e(this, "error swap, call not in call list " + callId);
- }
- }
-
- void addCall() {
- if (mInCallService != null) {
- Intent intent = new Intent(Intent.ACTION_DIAL);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- // when we request the dialer come up, we also want to inform
- // it that we're going through the "add call" option from the
- // InCallScreen / PhoneUtils.
- intent.putExtra(ADD_CALL_MODE_KEY, true);
- try {
- Log.d(this, "Sending the add Call intent");
- mInCallService.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- // This is rather rare but possible.
- // Note: this method is used even when the phone is encrypted. At that moment
- // the system may not find any Activity which can accept this Intent.
- Log.e(this, "Activity for adding calls isn't found.", e);
- }
- }
- }
-
- void playDtmfTone(String callId, char digit) {
- android.telecom.Call call = getTelecomCallById(callId);
- if (call != null) {
- call.playDtmfTone(digit);
- } else {
- Log.e(this, "error playDtmfTone, call not in call list " + callId);
- }
- }
-
- void stopDtmfTone(String callId) {
- android.telecom.Call call = getTelecomCallById(callId);
- if (call != null) {
- call.stopDtmfTone();
- } else {
- Log.e(this, "error stopDtmfTone, call not in call list " + callId);
- }
- }
-
- void postDialContinue(String callId, boolean proceed) {
- android.telecom.Call call = getTelecomCallById(callId);
- if (call != null) {
- call.postDialContinue(proceed);
- } else {
- Log.e(this, "error postDialContinue, call not in call list " + callId);
- }
- }
-
- void phoneAccountSelected(String callId, PhoneAccountHandle accountHandle, boolean setDefault) {
- if (accountHandle == null) {
- Log.e(this, "error phoneAccountSelected, accountHandle is null");
- // TODO: Do we really want to send null accountHandle?
- }
-
- android.telecom.Call call = getTelecomCallById(callId);
- if (call != null) {
- call.phoneAccountSelected(accountHandle, setDefault);
- } else {
- Log.e(this, "error phoneAccountSelected, call not in call list " + callId);
- }
- }
-
- boolean canAddCall() {
- if (mInCallService != null) {
- return mInCallService.canAddCall();
- }
- return false;
- }
-}
diff --git a/InCallUI/src/com/android/incallui/VideoCallFragment.java b/InCallUI/src/com/android/incallui/VideoCallFragment.java
deleted file mode 100644
index 6a46a423d..000000000
--- a/InCallUI/src/com/android/incallui/VideoCallFragment.java
+++ /dev/null
@@ -1,901 +0,0 @@
-/*
- * Copyright (C) 2014 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.incallui;
-
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.SurfaceTexture;
-import android.os.Bundle;
-import android.view.Display;
-import android.view.LayoutInflater;
-import android.view.Surface;
-import android.view.TextureView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.view.ViewTreeObserver;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import com.android.dialer.R;
-import com.android.phone.common.animation.AnimUtils;
-import com.google.common.base.Objects;
-
-/**
- * Fragment containing video calling surfaces.
- */
-public class VideoCallFragment extends BaseFragment<VideoCallPresenter,
- VideoCallPresenter.VideoCallUi> implements VideoCallPresenter.VideoCallUi {
- private static final String TAG = VideoCallFragment.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- /**
- * Used to indicate that the surface dimensions are not set.
- */
- private static final int DIMENSIONS_NOT_SET = -1;
-
- /**
- * Surface ID for the display surface.
- */
- public static final int SURFACE_DISPLAY = 1;
-
- /**
- * Surface ID for the preview surface.
- */
- public static final int SURFACE_PREVIEW = 2;
-
- /**
- * Used to indicate that the UI rotation is unknown.
- */
- public static final int ORIENTATION_UNKNOWN = -1;
-
- // Static storage used to retain the video surfaces across Activity restart.
- // TextureViews are not parcelable, so it is not possible to store them in the saved state.
- private static boolean sVideoSurfacesInUse = false;
- private static VideoCallSurface sPreviewSurface = null;
- private static VideoCallSurface sDisplaySurface = null;
- private static Point sDisplaySize = null;
-
- /**
- * {@link ViewStub} holding the video call surfaces. This is the parent for the
- * {@link VideoCallFragment}. Used to ensure that the video surfaces are only inflated when
- * required.
- */
- private ViewStub mVideoViewsStub;
-
- /**
- * Inflated view containing the video call surfaces represented by the {@link ViewStub}.
- */
- private View mVideoViews;
-
- /**
- * The {@link FrameLayout} containing the preview surface.
- */
- private View mPreviewVideoContainer;
-
- /**
- * Icon shown to indicate that the outgoing camera has been turned off.
- */
- private View mCameraOff;
-
- /**
- * {@link ImageView} containing the user's profile photo.
- */
- private ImageView mPreviewPhoto;
-
- /**
- * {@code True} when the layout of the activity has been completed.
- */
- private boolean mIsLayoutComplete = false;
-
- /**
- * {@code True} if in landscape mode.
- */
- private boolean mIsLandscape;
-
- private int mAnimationDuration;
-
- /**
- * Inner-class representing a {@link TextureView} and its associated {@link SurfaceTexture} and
- * {@link Surface}. Used to manage the lifecycle of these objects across device orientation
- * changes.
- */
- private static class VideoCallSurface implements TextureView.SurfaceTextureListener,
- View.OnClickListener, View.OnAttachStateChangeListener {
- private int mSurfaceId;
- private VideoCallPresenter mPresenter;
- private TextureView mTextureView;
- private SurfaceTexture mSavedSurfaceTexture;
- private Surface mSavedSurface;
- private boolean mIsDoneWithSurface;
- private int mWidth = DIMENSIONS_NOT_SET;
- private int mHeight = DIMENSIONS_NOT_SET;
-
- /**
- * Creates an instance of a {@link VideoCallSurface}.
- *
- * @param surfaceId The surface ID of the surface.
- * @param textureView The {@link TextureView} for the surface.
- */
- public VideoCallSurface(VideoCallPresenter presenter, int surfaceId,
- TextureView textureView) {
- this(presenter, surfaceId, textureView, DIMENSIONS_NOT_SET, DIMENSIONS_NOT_SET);
- }
-
- /**
- * Creates an instance of a {@link VideoCallSurface}.
- *
- * @param surfaceId The surface ID of the surface.
- * @param textureView The {@link TextureView} for the surface.
- * @param width The width of the surface.
- * @param height The height of the surface.
- */
- public VideoCallSurface(VideoCallPresenter presenter,int surfaceId, TextureView textureView,
- int width, int height) {
- Log.d(this, "VideoCallSurface: surfaceId=" + surfaceId +
- " width=" + width + " height=" + height);
- mPresenter = presenter;
- mWidth = width;
- mHeight = height;
- mSurfaceId = surfaceId;
-
- recreateView(textureView);
- }
-
- /**
- * Recreates a {@link VideoCallSurface} after a device orientation change. Re-applies the
- * saved {@link SurfaceTexture} to the
- *
- * @param view The {@link TextureView}.
- */
- public void recreateView(TextureView view) {
- if (DEBUG) {
- Log.i(TAG, "recreateView: " + view);
- }
-
- if (mTextureView == view) {
- return;
- }
-
- mTextureView = view;
- mTextureView.setSurfaceTextureListener(this);
- mTextureView.setOnClickListener(this);
-
- final boolean areSameSurfaces =
- Objects.equal(mSavedSurfaceTexture, mTextureView.getSurfaceTexture());
- Log.d(this, "recreateView: SavedSurfaceTexture=" + mSavedSurfaceTexture
- + " areSameSurfaces=" + areSameSurfaces);
- if (mSavedSurfaceTexture != null && !areSameSurfaces) {
- mTextureView.setSurfaceTexture(mSavedSurfaceTexture);
- if (createSurface(mWidth, mHeight)) {
- onSurfaceCreated();
- }
- }
- mIsDoneWithSurface = false;
- }
-
- public void resetPresenter(VideoCallPresenter presenter) {
- Log.d(this, "resetPresenter: CurrentPresenter=" + mPresenter + " NewPresenter="
- + presenter);
- mPresenter = presenter;
- }
-
- /**
- * Handles {@link SurfaceTexture} callback to indicate that a {@link SurfaceTexture} has
- * been successfully created.
- *
- * @param surfaceTexture The {@link SurfaceTexture} which has been created.
- * @param width The width of the {@link SurfaceTexture}.
- * @param height The height of the {@link SurfaceTexture}.
- */
- @Override
- public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width,
- int height) {
- boolean surfaceCreated;
- if (DEBUG) {
- Log.i(TAG, "onSurfaceTextureAvailable: " + surfaceTexture);
- }
- // Where there is no saved {@link SurfaceTexture} available, use the newly created one.
- // If a saved {@link SurfaceTexture} is available, we are re-creating after an
- // orientation change.
- Log.d(this, " onSurfaceTextureAvailable mSurfaceId=" + mSurfaceId + " surfaceTexture="
- + surfaceTexture + " width=" + width
- + " height=" + height + " mSavedSurfaceTexture=" + mSavedSurfaceTexture);
- Log.d(this, " onSurfaceTextureAvailable VideoCallPresenter=" + mPresenter);
- if (mSavedSurfaceTexture == null) {
- mSavedSurfaceTexture = surfaceTexture;
- surfaceCreated = createSurface(width, height);
- } else {
- // A saved SurfaceTexture was found.
- Log.d(this, " onSurfaceTextureAvailable: Replacing with cached surface...");
- mTextureView.setSurfaceTexture(mSavedSurfaceTexture);
- surfaceCreated = true;
- }
-
- // Inform presenter that the surface is available.
- if (surfaceCreated) {
- onSurfaceCreated();
- }
- }
-
- private void onSurfaceCreated() {
- if (mPresenter != null) {
- mPresenter.onSurfaceCreated(mSurfaceId);
- } else {
- Log.e(this, "onSurfaceTextureAvailable: Presenter is null");
- }
- }
-
- /**
- * Handles a change in the {@link SurfaceTexture}'s size.
- *
- * @param surfaceTexture The {@link SurfaceTexture}.
- * @param width The new width.
- * @param height The new height.
- */
- @Override
- public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width,
- int height) {
- // Not handled
- }
-
- /**
- * Handles {@link SurfaceTexture} destruct callback, indicating that it has been destroyed.
- *
- * @param surfaceTexture The {@link SurfaceTexture}.
- * @return {@code True} if the {@link TextureView} can release the {@link SurfaceTexture}.
- */
- @Override
- public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
- /**
- * Destroying the surface texture; inform the presenter so it can null the surfaces.
- */
- Log.d(this, " onSurfaceTextureDestroyed mSurfaceId=" + mSurfaceId + " surfaceTexture="
- + surfaceTexture + " SavedSurfaceTexture=" + mSavedSurfaceTexture
- + " SavedSurface=" + mSavedSurface);
- Log.d(this, " onSurfaceTextureDestroyed VideoCallPresenter=" + mPresenter);
-
- // Notify presenter if it is not null.
- onSurfaceDestroyed();
-
- if (mIsDoneWithSurface) {
- onSurfaceReleased();
- if (mSavedSurface != null) {
- mSavedSurface.release();
- mSavedSurface = null;
- }
- }
- return mIsDoneWithSurface;
- }
-
- private void onSurfaceDestroyed() {
- if (mPresenter != null) {
- mPresenter.onSurfaceDestroyed(mSurfaceId);
- } else {
- Log.e(this, "onSurfaceTextureDestroyed: Presenter is null.");
- }
- }
-
- /**
- * Handles {@link SurfaceTexture} update callback.
- * @param surface
- */
- @Override
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
- // Not Handled
- }
-
- @Override
- public void onViewAttachedToWindow(View v) {
- if (DEBUG) {
- Log.i(TAG, "OnViewAttachedToWindow");
- }
- if (mSavedSurfaceTexture != null) {
- mTextureView.setSurfaceTexture(mSavedSurfaceTexture);
- }
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {}
-
- /**
- * Retrieves the current {@link TextureView}.
- *
- * @return The {@link TextureView}.
- */
- public TextureView getTextureView() {
- return mTextureView;
- }
-
- /**
- * Called by the user presenter to indicate that the surface is no longer required due to a
- * change in video state. Releases and clears out the saved surface and surface textures.
- */
- public void setDoneWithSurface() {
- Log.d(this, "setDoneWithSurface: SavedSurface=" + mSavedSurface
- + " SavedSurfaceTexture=" + mSavedSurfaceTexture);
- mIsDoneWithSurface = true;
- if (mTextureView != null && mTextureView.isAvailable()) {
- return;
- }
-
- if (mSavedSurface != null) {
- onSurfaceReleased();
- mSavedSurface.release();
- mSavedSurface = null;
- }
- if (mSavedSurfaceTexture != null) {
- mSavedSurfaceTexture.release();
- mSavedSurfaceTexture = null;
- }
- }
-
- private void onSurfaceReleased() {
- if (mPresenter != null) {
- mPresenter.onSurfaceReleased(mSurfaceId);
- } else {
- Log.d(this, "setDoneWithSurface: Presenter is null.");
- }
- }
-
- /**
- * Retrieves the saved surface instance.
- *
- * @return The surface.
- */
- public Surface getSurface() {
- return mSavedSurface;
- }
-
- /**
- * Sets the dimensions of the surface.
- *
- * @param width The width of the surface, in pixels.
- * @param height The height of the surface, in pixels.
- */
- public void setSurfaceDimensions(int width, int height) {
- Log.d(this, "setSurfaceDimensions, width=" + width + " height=" + height);
- mWidth = width;
- mHeight = height;
-
- if (width != DIMENSIONS_NOT_SET && height != DIMENSIONS_NOT_SET
- && mSavedSurfaceTexture != null) {
- Log.d(this, "setSurfaceDimensions, mSavedSurfaceTexture is NOT equal to null.");
- mSavedSurfaceTexture.setDefaultBufferSize(width, height);
- }
- }
-
- /**
- * Creates the {@link Surface}, adjusting the {@link SurfaceTexture} buffer size.
- * @param width The width of the surface to create.
- * @param height The height of the surface to create.
- */
- private boolean createSurface(int width, int height) {
- Log.d(this, "createSurface mSavedSurfaceTexture=" + mSavedSurfaceTexture
- + " mSurfaceId =" + mSurfaceId + " mWidth " + width + " mHeight=" + height);
- if (width != DIMENSIONS_NOT_SET && height != DIMENSIONS_NOT_SET
- && mSavedSurfaceTexture != null) {
- mSavedSurfaceTexture.setDefaultBufferSize(width, height);
- mSavedSurface = new Surface(mSavedSurfaceTexture);
- return true;
- }
- return false;
- }
-
- /**
- * Handles a user clicking the surface, which is the trigger to toggle the full screen
- * Video UI.
- *
- * @param view The view receiving the click.
- */
- @Override
- public void onClick(View view) {
- if (mPresenter != null) {
- mPresenter.onSurfaceClick(mSurfaceId);
- } else {
- Log.e(this, "onClick: Presenter is null.");
- }
- }
-
- /**
- * Returns the dimensions of the surface.
- *
- * @return The dimensions of the surface.
- */
- public Point getSurfaceDimensions() {
- return new Point(mWidth, mHeight);
- }
- };
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mAnimationDuration = getResources().getInteger(R.integer.video_animation_duration);
- }
-
- /**
- * Handles creation of the activity and initialization of the presenter.
- *
- * @param savedInstanceState The saved instance state.
- */
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- mIsLandscape = getResources().getBoolean(R.bool.is_layout_landscape);
- Log.d(this, "onActivityCreated: IsLandscape=" + mIsLandscape);
- getPresenter().init(getActivity());
-
- super.onActivityCreated(savedInstanceState);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- super.onCreateView(inflater, container, savedInstanceState);
-
- final View view = inflater.inflate(R.layout.video_call_fragment, container, false);
-
- return view;
- }
-
- /**
- * Centers the display view vertically for portrait orientations. The view is centered within
- * the available space not occupied by the call card. This is a no-op for landscape mode.
- *
- * @param displayVideo The video view to center.
- */
- private void centerDisplayView(View displayVideo) {
- if (!mIsLandscape) {
- ViewGroup.LayoutParams p = displayVideo.getLayoutParams();
- int height = p.height;
-
- float spaceBesideCallCard = InCallPresenter.getInstance().getSpaceBesideCallCard();
- // If space beside call card is zeo, layout hasn't happened yet so there is no point
- // in attempting to center the view.
- if (Math.abs(spaceBesideCallCard - 0.0f) < 0.0001) {
- return;
- }
- float videoViewTranslation = height / 2 - spaceBesideCallCard / 2;
- displayVideo.setTranslationY(videoViewTranslation);
- }
- }
-
- /**
- * After creation of the fragment view, retrieves the required views.
- *
- * @param view The fragment view.
- * @param savedInstanceState The saved instance state.
- */
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- Log.d(this, "onViewCreated: VideoSurfacesInUse=" + sVideoSurfacesInUse);
-
- mVideoViewsStub = (ViewStub) view.findViewById(R.id.videoCallViewsStub);
- }
-
- @Override
- public void onStop() {
- super.onStop();
- Log.d(this, "onStop:");
- }
-
- @Override
- public void onPause() {
- super.onPause();
- Log.d(this, "onPause:");
- getPresenter().cancelAutoFullScreen();
- }
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- Log.d(this, "onDestroyView:");
- }
-
- /**
- * Creates the presenter for the {@link VideoCallFragment}.
- * @return The presenter instance.
- */
- @Override
- public VideoCallPresenter createPresenter() {
- Log.d(this, "createPresenter");
- VideoCallPresenter presenter = new VideoCallPresenter();
- onPresenterChanged(presenter);
- return presenter;
- }
-
- /**
- * @return The user interface for the presenter, which is this fragment.
- */
- @Override
- public VideoCallPresenter.VideoCallUi getUi() {
- return this;
- }
-
- /**
- * Inflate video surfaces.
- *
- * @param show {@code True} if the video surfaces should be shown.
- */
- private void inflateVideoUi(boolean show) {
- int visibility = show ? View.VISIBLE : View.GONE;
- getView().setVisibility(visibility);
-
- if (show) {
- inflateVideoCallViews();
- }
-
- if (mVideoViews != null) {
- mVideoViews.setVisibility(visibility);
- }
- }
-
- /**
- * Hides and shows the incoming video view and changes the outgoing video view's state based on
- * whether outgoing view is enabled or not.
- */
- @Override
- public void showVideoViews(boolean previewPaused, boolean showIncoming) {
- inflateVideoUi(true);
-
- View incomingVideoView = mVideoViews.findViewById(R.id.incomingVideo);
- if (incomingVideoView != null) {
- incomingVideoView.setVisibility(showIncoming ? View.VISIBLE : View.INVISIBLE);
- }
- if (mCameraOff != null) {
- mCameraOff.setVisibility(!previewPaused ? View.VISIBLE : View.INVISIBLE);
- }
- if (mPreviewPhoto != null) {
- mPreviewPhoto.setVisibility(!previewPaused ? View.VISIBLE : View.INVISIBLE);
- }
- }
-
- /**
- * Hide all video views.
- */
- @Override
- public void hideVideoUi() {
- inflateVideoUi(false);
- }
-
- /**
- * Cleans up the video telephony surfaces. Used when the presenter indicates a change to an
- * audio-only state. Since the surfaces are static, it is important to ensure they are cleaned
- * up promptly.
- */
- @Override
- public void cleanupSurfaces() {
- Log.d(this, "cleanupSurfaces");
- if (sDisplaySurface != null) {
- sDisplaySurface.setDoneWithSurface();
- sDisplaySurface = null;
- }
- if (sPreviewSurface != null) {
- sPreviewSurface.setDoneWithSurface();
- sPreviewSurface = null;
- }
- sVideoSurfacesInUse = false;
- }
-
- @Override
- public ImageView getPreviewPhotoView() {
- return mPreviewPhoto;
- }
-
- /**
- * Adjusts the location of the video preview view by the specified offset.
- *
- * @param shiftUp {@code true} if the preview should shift up, {@code false} if it should shift
- * down.
- * @param offset The offset.
- */
- @Override
- public void adjustPreviewLocation(boolean shiftUp, int offset) {
- if (sPreviewSurface == null || mPreviewVideoContainer == null) {
- return;
- }
-
- // Set the position of the secondary call info card to its starting location.
- mPreviewVideoContainer.setTranslationY(shiftUp ? 0 : -offset);
-
- // Animate the secondary card info slide up/down as it appears and disappears.
- mPreviewVideoContainer.animate()
- .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
- .setDuration(mAnimationDuration)
- .translationY(shiftUp ? -offset : 0)
- .start();
- }
-
- private void onPresenterChanged(VideoCallPresenter presenter) {
- Log.d(this, "onPresenterChanged: Presenter=" + presenter);
- if (sDisplaySurface != null) {
- sDisplaySurface.resetPresenter(presenter);;
- }
- if (sPreviewSurface != null) {
- sPreviewSurface.resetPresenter(presenter);
- }
- }
-
- /**
- * @return {@code True} if the display video surface has been created.
- */
- @Override
- public boolean isDisplayVideoSurfaceCreated() {
- boolean ret = sDisplaySurface != null && sDisplaySurface.getSurface() != null;
- Log.d(this, " isDisplayVideoSurfaceCreated returns " + ret);
- return ret;
- }
-
- /**
- * @return {@code True} if the preview video surface has been created.
- */
- @Override
- public boolean isPreviewVideoSurfaceCreated() {
- boolean ret = sPreviewSurface != null && sPreviewSurface.getSurface() != null;
- Log.d(this, " isPreviewVideoSurfaceCreated returns " + ret);
- return ret;
- }
-
- /**
- * {@link android.view.Surface} on which incoming video for a video call is displayed.
- * {@code Null} until the video views {@link android.view.ViewStub} is inflated.
- */
- @Override
- public Surface getDisplayVideoSurface() {
- return sDisplaySurface == null ? null : sDisplaySurface.getSurface();
- }
-
- /**
- * {@link android.view.Surface} on which a preview of the outgoing video for a video call is
- * displayed. {@code Null} until the video views {@link android.view.ViewStub} is inflated.
- */
- @Override
- public Surface getPreviewVideoSurface() {
- return sPreviewSurface == null ? null : sPreviewSurface.getSurface();
- }
-
- /**
- * Changes the dimensions of the preview surface. Called when the dimensions change due to a
- * device orientation change.
- *
- * @param width The new width.
- * @param height The new height.
- */
- @Override
- public void setPreviewSize(int width, int height) {
- Log.d(this, "setPreviewSize: width=" + width + " height=" + height);
- if (sPreviewSurface != null) {
- TextureView preview = sPreviewSurface.getTextureView();
-
- if (preview == null ) {
- return;
- }
-
- // Set the dimensions of both the video surface and the FrameLayout containing it.
- ViewGroup.LayoutParams params = preview.getLayoutParams();
- params.width = width;
- params.height = height;
- preview.setLayoutParams(params);
-
- if (mPreviewVideoContainer != null) {
- ViewGroup.LayoutParams containerParams = mPreviewVideoContainer.getLayoutParams();
- containerParams.width = width;
- containerParams.height = height;
- mPreviewVideoContainer.setLayoutParams(containerParams);
- }
-
- // The width and height are interchanged outside of this method based on the current
- // orientation, so we can transform using "width", which will be either the width or
- // the height.
- Matrix transform = new Matrix();
- transform.setScale(-1, 1, width/2, 0);
- preview.setTransform(transform);
- }
- }
-
- /**
- * Sets the rotation of the preview surface. Called when the dimensions change due to a
- * device orientation change.
- *
- * Please note that the screen orientation passed in is subtracted from 360 to get the actual
- * preview rotation values.
- *
- * @param rotation The screen orientation. One of -
- * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_0},
- * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_90},
- * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_180},
- * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_270}).
- */
- @Override
- public void setPreviewRotation(int orientation) {
- Log.d(this, "setPreviewRotation: orientation=" + orientation);
- if (sPreviewSurface != null) {
- TextureView preview = sPreviewSurface.getTextureView();
-
- if (preview == null ) {
- return;
- }
-
- preview.setRotation(orientation);
- }
- }
-
- @Override
- public void setPreviewSurfaceSize(int width, int height) {
- final boolean isPreviewSurfaceAvailable = sPreviewSurface != null;
- Log.d(this, "setPreviewSurfaceSize: width=" + width + " height=" + height +
- " isPreviewSurfaceAvailable=" + isPreviewSurfaceAvailable);
- if (isPreviewSurfaceAvailable) {
- sPreviewSurface.setSurfaceDimensions(width, height);
- }
- }
-
- /**
- * returns UI's current orientation.
- */
- @Override
- public int getCurrentRotation() {
- try {
- return getActivity().getWindowManager().getDefaultDisplay().getRotation();
- } catch (Exception e) {
- Log.e(this, "getCurrentRotation: Retrieving current rotation failed. Ex=" + e);
- }
- return ORIENTATION_UNKNOWN;
- }
-
- /**
- * Changes the dimensions of the display video surface. Called when the dimensions change due to
- * a peer resolution update
- *
- * @param width The new width.
- * @param height The new height.
- */
- @Override
- public void setDisplayVideoSize(int width, int height) {
- Log.v(this, "setDisplayVideoSize: width=" + width + " height=" + height);
- if (sDisplaySurface != null) {
- TextureView displayVideo = sDisplaySurface.getTextureView();
- if (displayVideo == null) {
- Log.e(this, "Display Video texture view is null. Bail out");
- return;
- }
- sDisplaySize = new Point(width, height);
- setSurfaceSizeAndTranslation(displayVideo, sDisplaySize);
- } else {
- Log.e(this, "Display Video Surface is null. Bail out");
- }
- }
-
- /**
- * Determines the size of the device screen.
- *
- * @return {@link Point} specifying the width and height of the screen.
- */
- @Override
- public Point getScreenSize() {
- // Get current screen size.
- Display display = getActivity().getWindowManager().getDefaultDisplay();
- Point size = new Point();
- display.getSize(size);
-
- return size;
- }
-
- /**
- * Determines the size of the preview surface.
- *
- * @return {@link Point} specifying the width and height of the preview surface.
- */
- @Override
- public Point getPreviewSize() {
- if (sPreviewSurface == null) {
- return null;
- }
- return sPreviewSurface.getSurfaceDimensions();
- }
-
- /**
- * Inflates the {@link ViewStub} containing the incoming and outgoing surfaces, if necessary,
- * and creates {@link VideoCallSurface} instances to track the surfaces.
- */
- private void inflateVideoCallViews() {
- Log.d(this, "inflateVideoCallViews");
- if (mVideoViews == null ) {
- mVideoViews = mVideoViewsStub.inflate();
- }
-
- if (mVideoViews != null) {
- mPreviewVideoContainer = mVideoViews.findViewById(R.id.previewVideoContainer);
- mCameraOff = mVideoViews.findViewById(R.id.previewCameraOff);
- mPreviewPhoto = (ImageView) mVideoViews.findViewById(R.id.previewProfilePhoto);
-
- TextureView displaySurface = (TextureView) mVideoViews.findViewById(R.id.incomingVideo);
-
- Log.d(this, "inflateVideoCallViews: sVideoSurfacesInUse=" + sVideoSurfacesInUse);
- //If peer adjusted screen size is not available, set screen size to default display size
- Point screenSize = sDisplaySize == null ? getScreenSize() : sDisplaySize;
- setSurfaceSizeAndTranslation(displaySurface, screenSize);
-
- if (!sVideoSurfacesInUse) {
- // Where the video surfaces are not already in use (first time creating them),
- // setup new VideoCallSurface instances to track them.
- Log.d(this, " inflateVideoCallViews screenSize" + screenSize);
-
- sDisplaySurface = new VideoCallSurface(getPresenter(), SURFACE_DISPLAY,
- (TextureView) mVideoViews.findViewById(R.id.incomingVideo), screenSize.x,
- screenSize.y);
- sPreviewSurface = new VideoCallSurface(getPresenter(), SURFACE_PREVIEW,
- (TextureView) mVideoViews.findViewById(R.id.previewVideo));
- sVideoSurfacesInUse = true;
- } else {
- // In this case, the video surfaces are already in use (we are recreating the
- // Fragment after a destroy/create cycle resulting from a rotation.
- sDisplaySurface.recreateView((TextureView) mVideoViews.findViewById(
- R.id.incomingVideo));
- sPreviewSurface.recreateView((TextureView) mVideoViews.findViewById(
- R.id.previewVideo));
- }
-
- // Attempt to center the incoming video view, if it is in the layout.
- final ViewTreeObserver observer = mVideoViews.getViewTreeObserver();
- observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- // Check if the layout includes the incoming video surface -- this will only be the
- // case for a video call.
- View displayVideo = mVideoViews.findViewById(R.id.incomingVideo);
- if (displayVideo != null) {
- centerDisplayView(displayVideo);
- }
- mIsLayoutComplete = true;
-
- // Remove the listener so we don't continually re-layout.
- ViewTreeObserver observer = mVideoViews.getViewTreeObserver();
- if (observer.isAlive()) {
- observer.removeOnGlobalLayoutListener(this);
- }
- }
- });
- }
- }
-
- /**
- * Resizes a surface so that it has the same size as the full screen and so that it is
- * centered vertically below the call card.
- *
- * @param textureView The {@link TextureView} to resize and position.
- * @param size The size of the screen.
- */
- private void setSurfaceSizeAndTranslation(TextureView textureView, Point size) {
- // Set the surface to have that size.
- ViewGroup.LayoutParams params = textureView.getLayoutParams();
- params.width = size.x;
- params.height = size.y;
- textureView.setLayoutParams(params);
- Log.d(this, "setSurfaceSizeAndTranslation: Size=" + size + "IsLayoutComplete=" +
- mIsLayoutComplete + "IsLandscape=" + mIsLandscape);
-
- // It is only possible to center the display view if layout of the views has completed.
- // It is only after layout is complete that the dimensions of the Call Card has been
- // established, which is a prerequisite to centering the view.
- // Incoming video calls will center the view
- if (mIsLayoutComplete) {
- centerDisplayView(textureView);
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/VideoCallPresenter.java b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
deleted file mode 100644
index 06e3e4440..000000000
--- a/InCallUI/src/com/android/incallui/VideoCallPresenter.java
+++ /dev/null
@@ -1,1306 +0,0 @@
-/*
- * Copyright (C) 2014 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.incallui;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.graphics.Point;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.ContactsContract;
-import android.telecom.Connection;
-import android.telecom.InCallService.VideoCall;
-import android.telecom.VideoProfile;
-import android.telecom.VideoProfile.CameraCapabilities;
-import android.view.Surface;
-import android.widget.ImageView;
-
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.dialer.R;
-import com.android.incallui.InCallPresenter.InCallDetailsListener;
-import com.android.incallui.InCallPresenter.InCallOrientationListener;
-import com.android.incallui.InCallPresenter.InCallStateListener;
-import com.android.incallui.InCallPresenter.IncomingCallListener;
-import com.android.incallui.InCallVideoCallCallbackNotifier.SurfaceChangeListener;
-import com.android.incallui.InCallVideoCallCallbackNotifier.VideoEventListener;
-
-import java.util.Objects;
-
-/**
- * Logic related to the {@link VideoCallFragment} and for managing changes to the video calling
- * surfaces based on other user interface events and incoming events from the
- * {@class VideoCallListener}.
- * <p>
- * When a call's video state changes to bi-directional video, the
- * {@link com.android.incallui.VideoCallPresenter} performs the following negotiation with the
- * telephony layer:
- * <ul>
- * <li>{@code VideoCallPresenter} creates and informs telephony of the display surface.</li>
- * <li>{@code VideoCallPresenter} creates the preview surface.</li>
- * <li>{@code VideoCallPresenter} informs telephony of the currently selected camera.</li>
- * <li>Telephony layer sends {@link CameraCapabilities}, including the
- * dimensions of the video for the current camera.</li>
- * <li>{@code VideoCallPresenter} adjusts size of the preview surface to match the aspect
- * ratio of the camera.</li>
- * <li>{@code VideoCallPresenter} informs telephony of the new preview surface.</li>
- * </ul>
- * <p>
- * When downgrading to an audio-only video state, the {@code VideoCallPresenter} nulls both
- * surfaces.
- */
-public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi> implements
- IncomingCallListener, InCallOrientationListener, InCallStateListener,
- InCallDetailsListener, SurfaceChangeListener, VideoEventListener,
- InCallPresenter.InCallEventListener {
- public static final String TAG = "VideoCallPresenter";
-
- public static final boolean DEBUG = false;
-
- /**
- * Runnable which is posted to schedule automatically entering fullscreen mode. Will not auto
- * enter fullscreen mode if the dialpad is visible (doing so would make it impossible to exit
- * the dialpad).
- */
- private Runnable mAutoFullscreenRunnable = new Runnable() {
- @Override
- public void run() {
- if (mAutoFullScreenPending && !InCallPresenter.getInstance().isDialpadVisible()
- && mIsVideoMode) {
-
- Log.v(this, "Automatically entering fullscreen mode.");
- InCallPresenter.getInstance().setFullScreen(true);
- mAutoFullScreenPending = false;
- } else {
- Log.v(this, "Skipping scheduled fullscreen mode.");
- }
- }
- };
-
- /**
- * Defines the state of the preview surface negotiation with the telephony layer.
- */
- private class PreviewSurfaceState {
- /**
- * The camera has not yet been set on the {@link VideoCall}; negotiation has not yet
- * started.
- */
- private static final int NONE = 0;
-
- /**
- * The camera has been set on the {@link VideoCall}, but camera capabilities have not yet
- * been received.
- */
- private static final int CAMERA_SET = 1;
-
- /**
- * The camera capabilties have been received from telephony, but the surface has not yet
- * been set on the {@link VideoCall}.
- */
- private static final int CAPABILITIES_RECEIVED = 2;
-
- /**
- * The surface has been set on the {@link VideoCall}.
- */
- private static final int SURFACE_SET = 3;
- }
-
- /**
- * The minimum width or height of the preview surface. Used when re-sizing the preview surface
- * to match the aspect ratio of the currently selected camera.
- */
- private float mMinimumVideoDimension;
-
- /**
- * The current context.
- */
- private Context mContext;
-
- /**
- * The call the video surfaces are currently related to
- */
- private Call mPrimaryCall;
-
- /**
- * The {@link VideoCall} used to inform the video telephony layer of changes to the video
- * surfaces.
- */
- private VideoCall mVideoCall;
-
- /**
- * Determines if the current UI state represents a video call.
- */
- private int mCurrentVideoState;
-
- /**
- * Call's current state
- */
- private int mCurrentCallState = Call.State.INVALID;
-
- /**
- * Determines the device orientation (portrait/lanscape).
- */
- private int mDeviceOrientation = InCallOrientationEventListener.SCREEN_ORIENTATION_0;
-
- /**
- * Tracks the state of the preview surface negotiation with the telephony layer.
- */
- private int mPreviewSurfaceState = PreviewSurfaceState.NONE;
-
- private static boolean mIsVideoMode = false;
-
- /**
- * Contact photo manager to retrieve cached contact photo information.
- */
- private ContactPhotoManager mContactPhotoManager = null;
-
- /**
- * The URI for the user's profile photo, or {@code null} if not specified.
- */
- private ContactInfoCache.ContactCacheEntry mProfileInfo = null;
-
- /**
- * UI thread handler used for delayed task execution.
- */
- private Handler mHandler;
-
- /**
- * Determines whether video calls should automatically enter full screen mode after
- * {@link #mAutoFullscreenTimeoutMillis} milliseconds.
- */
- private boolean mIsAutoFullscreenEnabled = false;
-
- /**
- * Determines the number of milliseconds after which a video call will automatically enter
- * fullscreen mode. Requires {@link #mIsAutoFullscreenEnabled} to be {@code true}.
- */
- private int mAutoFullscreenTimeoutMillis = 0;
-
- /**
- * Determines if the countdown is currently running to automatically enter full screen video
- * mode.
- */
- private boolean mAutoFullScreenPending = false;
-
- /**
- * Initializes the presenter.
- *
- * @param context The current context.
- */
- public void init(Context context) {
- mContext = context;
- mMinimumVideoDimension = mContext.getResources().getDimension(
- R.dimen.video_preview_small_dimension);
- mHandler = new Handler(Looper.getMainLooper());
- mIsAutoFullscreenEnabled = mContext.getResources()
- .getBoolean(R.bool.video_call_auto_fullscreen);
- mAutoFullscreenTimeoutMillis = mContext.getResources().getInteger(
- R.integer.video_call_auto_fullscreen_timeout);
- }
-
- /**
- * Called when the user interface is ready to be used.
- *
- * @param ui The Ui implementation that is now ready to be used.
- */
- @Override
- public void onUiReady(VideoCallUi ui) {
- super.onUiReady(ui);
- Log.d(this, "onUiReady:");
-
- // Do not register any listeners if video calling is not compatible to safeguard against
- // any accidental calls of video calling code.
- if (!CompatUtils.isVideoCompatible()) {
- return;
- }
-
- // Register for call state changes last
- InCallPresenter.getInstance().addListener(this);
- InCallPresenter.getInstance().addDetailsListener(this);
- InCallPresenter.getInstance().addIncomingCallListener(this);
- InCallPresenter.getInstance().addOrientationListener(this);
- // To get updates of video call details changes
- InCallPresenter.getInstance().addDetailsListener(this);
- InCallPresenter.getInstance().addInCallEventListener(this);
-
- // Register for surface and video events from {@link InCallVideoCallListener}s.
- InCallVideoCallCallbackNotifier.getInstance().addSurfaceChangeListener(this);
- InCallVideoCallCallbackNotifier.getInstance().addVideoEventListener(this);
- mCurrentVideoState = VideoProfile.STATE_AUDIO_ONLY;
- mCurrentCallState = Call.State.INVALID;
-
- final InCallPresenter.InCallState inCallState =
- InCallPresenter.getInstance().getInCallState();
- onStateChange(inCallState, inCallState, CallList.getInstance());
- }
-
- /**
- * Called when the user interface is no longer ready to be used.
- *
- * @param ui The Ui implementation that is no longer ready to be used.
- */
- @Override
- public void onUiUnready(VideoCallUi ui) {
- super.onUiUnready(ui);
- Log.d(this, "onUiUnready:");
-
- if (!CompatUtils.isVideoCompatible()) {
- return;
- }
-
- cancelAutoFullScreen();
-
- InCallPresenter.getInstance().removeListener(this);
- InCallPresenter.getInstance().removeDetailsListener(this);
- InCallPresenter.getInstance().removeIncomingCallListener(this);
- InCallPresenter.getInstance().removeOrientationListener(this);
- InCallPresenter.getInstance().removeInCallEventListener(this);
-
- InCallVideoCallCallbackNotifier.getInstance().removeSurfaceChangeListener(this);
- InCallVideoCallCallbackNotifier.getInstance().removeVideoEventListener(this);
- }
-
- /**
- * Handles the creation of a surface in the {@link VideoCallFragment}.
- *
- * @param surface The surface which was created.
- */
- public void onSurfaceCreated(int surface) {
- Log.d(this, "onSurfaceCreated surface=" + surface + " mVideoCall=" + mVideoCall);
- Log.d(this, "onSurfaceCreated PreviewSurfaceState=" + mPreviewSurfaceState);
- Log.d(this, "onSurfaceCreated presenter=" + this);
-
- final VideoCallUi ui = getUi();
- if (ui == null || mVideoCall == null) {
- Log.w(this, "onSurfaceCreated: Error bad state VideoCallUi=" + ui + " mVideoCall="
- + mVideoCall);
- return;
- }
-
- // If the preview surface has just been created and we have already received camera
- // capabilities, but not yet set the surface, we will set the surface now.
- if (surface == VideoCallFragment.SURFACE_PREVIEW ) {
- if (mPreviewSurfaceState == PreviewSurfaceState.CAPABILITIES_RECEIVED) {
- mPreviewSurfaceState = PreviewSurfaceState.SURFACE_SET;
- mVideoCall.setPreviewSurface(ui.getPreviewVideoSurface());
- } else if (mPreviewSurfaceState == PreviewSurfaceState.NONE && isCameraRequired()){
- enableCamera(mVideoCall, true);
- }
- } else if (surface == VideoCallFragment.SURFACE_DISPLAY) {
- mVideoCall.setDisplaySurface(ui.getDisplayVideoSurface());
- }
- }
-
- /**
- * Handles structural changes (format or size) to a surface.
- *
- * @param surface The surface which changed.
- * @param format The new PixelFormat of the surface.
- * @param width The new width of the surface.
- * @param height The new height of the surface.
- */
- public void onSurfaceChanged(int surface, int format, int width, int height) {
- //Do stuff
- }
-
- /**
- * Handles the destruction of a surface in the {@link VideoCallFragment}.
- * Note: The surface is being released, that is, it is no longer valid.
- *
- * @param surface The surface which was destroyed.
- */
- public void onSurfaceReleased(int surface) {
- Log.d(this, "onSurfaceReleased: mSurfaceId=" + surface);
- if ( mVideoCall == null) {
- Log.w(this, "onSurfaceReleased: VideoCall is null. mSurfaceId=" +
- surface);
- return;
- }
-
- if (surface == VideoCallFragment.SURFACE_DISPLAY) {
- mVideoCall.setDisplaySurface(null);
- } else if (surface == VideoCallFragment.SURFACE_PREVIEW) {
- mVideoCall.setPreviewSurface(null);
- enableCamera(mVideoCall, false);
- }
- }
-
- /**
- * Called by {@link VideoCallFragment} when the surface is detached from UI (TextureView).
- * Note: The surface will be cached by {@link VideoCallFragment}, so we don't immediately
- * null out incoming video surface.
- * @see VideoCallPresenter#onSurfaceReleased(int)
- *
- * @param surface The surface which was detached.
- */
- public void onSurfaceDestroyed(int surface) {
- Log.d(this, "onSurfaceDestroyed: mSurfaceId=" + surface);
- if (mVideoCall == null) {
- return;
- }
-
- final boolean isChangingConfigurations =
- InCallPresenter.getInstance().isChangingConfigurations();
- Log.d(this, "onSurfaceDestroyed: isChangingConfigurations=" + isChangingConfigurations);
-
- if (surface == VideoCallFragment.SURFACE_PREVIEW) {
- if (!isChangingConfigurations) {
- enableCamera(mVideoCall, false);
- } else {
- Log.w(this, "onSurfaceDestroyed: Activity is being destroyed due "
- + "to configuration changes. Not closing the camera.");
- }
- }
- }
-
- /**
- * Handles clicks on the video surfaces by toggling full screen state.
- * Informs the {@link InCallPresenter} of the change so that it can inform the
- * {@link CallCardPresenter} of the change.
- *
- * @param surfaceId The video surface receiving the click.
- */
- public void onSurfaceClick(int surfaceId) {
- boolean isFullscreen = InCallPresenter.getInstance().toggleFullscreenMode();
- Log.v(this, "toggleFullScreen = " + isFullscreen);
- }
-
- /**
- * Handles incoming calls.
- *
- * @param oldState The old in call state.
- * @param newState The new in call state.
- * @param call The call.
- */
- @Override
- public void onIncomingCall(InCallPresenter.InCallState oldState,
- InCallPresenter.InCallState newState, Call call) {
- // same logic should happen as with onStateChange()
- onStateChange(oldState, newState, CallList.getInstance());
- }
-
- /**
- * Handles state changes (including incoming calls)
- *
- * @param newState The in call state.
- * @param callList The call list.
- */
- @Override
- public void onStateChange(InCallPresenter.InCallState oldState,
- InCallPresenter.InCallState newState, CallList callList) {
- Log.d(this, "onStateChange oldState" + oldState + " newState=" + newState +
- " isVideoMode=" + isVideoMode());
-
- if (newState == InCallPresenter.InCallState.NO_CALLS) {
- if (isVideoMode()) {
- exitVideoMode();
- }
-
- cleanupSurfaces();
- }
-
- // Determine the primary active call).
- Call primary = null;
-
- // Determine the call which is the focus of the user's attention. In the case of an
- // incoming call waiting call, the primary call is still the active video call, however
- // the determination of whether we should be in fullscreen mode is based on the type of the
- // incoming call, not the active video call.
- Call currentCall = null;
-
- if (newState == InCallPresenter.InCallState.INCOMING) {
- // We don't want to replace active video call (primary call)
- // with a waiting call, since user may choose to ignore/decline the waiting call and
- // this should have no impact on current active video call, that is, we should not
- // change the camera or UI unless the waiting VT call becomes active.
- primary = callList.getActiveCall();
- currentCall = callList.getIncomingCall();
- if (!VideoUtils.isActiveVideoCall(primary)) {
- primary = callList.getIncomingCall();
- }
- } else if (newState == InCallPresenter.InCallState.OUTGOING) {
- currentCall = primary = callList.getOutgoingCall();
- } else if (newState == InCallPresenter.InCallState.PENDING_OUTGOING) {
- currentCall = primary = callList.getPendingOutgoingCall();
- } else if (newState == InCallPresenter.InCallState.INCALL) {
- currentCall = primary = callList.getActiveCall();
- }
-
- final boolean primaryChanged = !Objects.equals(mPrimaryCall, primary);
- Log.d(this, "onStateChange primaryChanged=" + primaryChanged);
- Log.d(this, "onStateChange primary= " + primary);
- Log.d(this, "onStateChange mPrimaryCall = " + mPrimaryCall);
- if (primaryChanged) {
- onPrimaryCallChanged(primary);
- } else if (mPrimaryCall != null) {
- updateVideoCall(primary);
- }
- updateCallCache(primary);
-
- // If the call context changed, potentially exit fullscreen or schedule auto enter of
- // fullscreen mode.
- // If the current call context is no longer a video call, exit fullscreen mode.
- maybeExitFullscreen(currentCall);
- // Schedule auto-enter of fullscreen mode if the current call context is a video call
- maybeAutoEnterFullscreen(currentCall);
- }
-
- /**
- * Handles a change to the fullscreen mode of the app.
- *
- * @param isFullscreenMode {@code true} if the app is now fullscreen, {@code false} otherwise.
- */
- @Override
- public void onFullscreenModeChanged(boolean isFullscreenMode) {
- cancelAutoFullScreen();
- }
-
- /**
- * Handles changes to the visibility of the secondary caller info bar.
- *
- * @param isVisible {@code true} if the secondary caller info is showing, {@code false}
- * otherwise.
- * @param height the height of the secondary caller info bar.
- */
- @Override
- public void onSecondaryCallerInfoVisibilityChanged(boolean isVisible, int height) {
- Log.d(this,
- "onSecondaryCallerInfoVisibilityChanged : isVisible = " + isVisible + " height = "
- + height);
- getUi().adjustPreviewLocation(isVisible /* shiftUp */, height);
- }
-
- private void checkForVideoStateChange(Call call) {
- final boolean isVideoCall = VideoUtils.isVideoCall(call);
- final boolean hasVideoStateChanged = mCurrentVideoState != call.getVideoState();
-
- Log.d(this, "checkForVideoStateChange: isVideoCall= " + isVideoCall
- + " hasVideoStateChanged=" + hasVideoStateChanged + " isVideoMode="
- + isVideoMode() + " previousVideoState: " +
- VideoProfile.videoStateToString(mCurrentVideoState) + " newVideoState: "
- + VideoProfile.videoStateToString(call.getVideoState()));
-
- if (!hasVideoStateChanged) {
- return;
- }
-
- updateCameraSelection(call);
-
- if (isVideoCall) {
- adjustVideoMode(call);
- } else if (isVideoMode()) {
- exitVideoMode();
- }
- }
-
- private void checkForCallStateChange(Call call) {
- final boolean isVideoCall = VideoUtils.isVideoCall(call);
- final boolean hasCallStateChanged = mCurrentCallState != call.getState();
-
- Log.d(this, "checkForCallStateChange: isVideoCall= " + isVideoCall
- + " hasCallStateChanged=" +
- hasCallStateChanged + " isVideoMode=" + isVideoMode());
-
- if (!hasCallStateChanged) {
- return;
- }
-
- if (isVideoCall) {
- final InCallCameraManager cameraManager = InCallPresenter.getInstance().
- getInCallCameraManager();
-
- String prevCameraId = cameraManager.getActiveCameraId();
- updateCameraSelection(call);
- String newCameraId = cameraManager.getActiveCameraId();
-
- if (!Objects.equals(prevCameraId, newCameraId) && VideoUtils.isActiveVideoCall(call)) {
- enableCamera(call.getVideoCall(), true);
- }
- }
-
- // Make sure we hide or show the video UI if needed.
- showVideoUi(call.getVideoState(), call.getState());
- }
-
- private void cleanupSurfaces() {
- final VideoCallUi ui = getUi();
- if (ui == null) {
- Log.w(this, "cleanupSurfaces");
- return;
- }
- ui.cleanupSurfaces();
- }
-
- private void onPrimaryCallChanged(Call newPrimaryCall) {
- final boolean isVideoCall = VideoUtils.isVideoCall(newPrimaryCall);
- final boolean isVideoMode = isVideoMode();
-
- Log.d(this, "onPrimaryCallChanged: isVideoCall=" + isVideoCall + " isVideoMode="
- + isVideoMode);
-
- if (!isVideoCall && isVideoMode) {
- // Terminate video mode if new primary call is not a video call
- // and we are currently in video mode.
- Log.d(this, "onPrimaryCallChanged: Exiting video mode...");
- exitVideoMode();
- } else if (isVideoCall) {
- Log.d(this, "onPrimaryCallChanged: Entering video mode...");
-
- updateCameraSelection(newPrimaryCall);
- adjustVideoMode(newPrimaryCall);
- }
- checkForOrientationAllowedChange(newPrimaryCall);
- }
-
- private boolean isVideoMode() {
- return mIsVideoMode;
- }
-
- private void updateCallCache(Call call) {
- if (call == null) {
- mCurrentVideoState = VideoProfile.STATE_AUDIO_ONLY;
- mCurrentCallState = Call.State.INVALID;
- mVideoCall = null;
- mPrimaryCall = null;
- } else {
- mCurrentVideoState = call.getVideoState();
- mVideoCall = call.getVideoCall();
- mCurrentCallState = call.getState();
- mPrimaryCall = call;
- }
- }
-
- /**
- * Handles changes to the details of the call. The {@link VideoCallPresenter} is interested in
- * changes to the video state.
- *
- * @param call The call for which the details changed.
- * @param details The new call details.
- */
- @Override
- public void onDetailsChanged(Call call, android.telecom.Call.Details details) {
- Log.d(this, " onDetailsChanged call=" + call + " details=" + details + " mPrimaryCall="
- + mPrimaryCall);
- if (call == null) {
- return;
- }
- // If the details change is not for the currently active call no update is required.
- if (!call.equals(mPrimaryCall)) {
- Log.d(this, " onDetailsChanged: Details not for current active call so returning. ");
- return;
- }
-
- updateVideoCall(call);
-
- updateCallCache(call);
- }
-
- private void updateVideoCall(Call call) {
- checkForVideoCallChange(call);
- checkForVideoStateChange(call);
- checkForCallStateChange(call);
- checkForOrientationAllowedChange(call);
- }
-
- private void checkForOrientationAllowedChange(Call call) {
- InCallPresenter.getInstance().setInCallAllowsOrientationChange(
- VideoUtils.isVideoCall(call));
- }
-
- /**
- * Checks for a change to the video call and changes it if required.
- */
- private void checkForVideoCallChange(Call call) {
- final VideoCall videoCall = call.getTelecomCall().getVideoCall();
- Log.d(this, "checkForVideoCallChange: videoCall=" + videoCall + " mVideoCall="
- + mVideoCall);
- if (!Objects.equals(videoCall, mVideoCall)) {
- changeVideoCall(call);
- }
- }
-
- /**
- * Handles a change to the video call. Sets the surfaces on the previous call to null and sets
- * the surfaces on the new video call accordingly.
- *
- * @param call The new video call.
- */
- private void changeVideoCall(Call call) {
- final VideoCall videoCall = call.getTelecomCall().getVideoCall();
- Log.d(this, "changeVideoCall to videoCall=" + videoCall + " mVideoCall=" + mVideoCall);
- // Null out the surfaces on the previous video call.
- if (mVideoCall != null) {
- // Log.d(this, "Null out the surfaces on the previous video call.");
- // mVideoCall.setDisplaySurface(null);
- // mVideoCall.setPreviewSurface(null);
- }
-
- final boolean hasChanged = mVideoCall == null && videoCall != null;
-
- mVideoCall = videoCall;
- if (mVideoCall == null || call == null) {
- Log.d(this, "Video call or primary call is null. Return");
- return;
- }
-
- if (VideoUtils.isVideoCall(call) && hasChanged) {
- adjustVideoMode(call);
- }
- }
-
- private static boolean isCameraRequired(int videoState) {
- return VideoProfile.isBidirectional(videoState)
- || VideoProfile.isTransmissionEnabled(videoState);
- }
-
- private boolean isCameraRequired() {
- return mPrimaryCall != null && isCameraRequired(mPrimaryCall.getVideoState());
- }
-
- /**
- * Adjusts the current video mode by setting up the preview and display surfaces as necessary.
- * Expected to be called whenever the video state associated with a call changes (e.g. a user
- * turns their camera on or off) to ensure the correct surfaces are shown/hidden.
- * TODO(vt): Need to adjust size and orientation of preview surface here.
- */
- private void adjustVideoMode(Call call) {
- VideoCall videoCall = call.getVideoCall();
- int newVideoState = call.getVideoState();
-
- Log.d(this, "adjustVideoMode videoCall= " + videoCall + " videoState: " + newVideoState);
- VideoCallUi ui = getUi();
- if (ui == null) {
- Log.e(this, "Error VideoCallUi is null so returning");
- return;
- }
-
- showVideoUi(newVideoState, call.getState());
-
- // Communicate the current camera to telephony and make a request for the camera
- // capabilities.
- if (videoCall != null) {
- if (ui.isDisplayVideoSurfaceCreated()) {
- Log.d(this, "Calling setDisplaySurface with " + ui.getDisplayVideoSurface());
- videoCall.setDisplaySurface(ui.getDisplayVideoSurface());
- }
-
- videoCall.setDeviceOrientation(mDeviceOrientation);
- enableCamera(videoCall, isCameraRequired(newVideoState));
- }
- int previousVideoState = mCurrentVideoState;
- mCurrentVideoState = newVideoState;
- mIsVideoMode = true;
-
- // adjustVideoMode may be called if we are already in a 1-way video state. In this case
- // we do not want to trigger auto-fullscreen mode.
- if (!VideoUtils.isVideoCall(previousVideoState) && VideoUtils.isVideoCall(newVideoState)) {
- maybeAutoEnterFullscreen(call);
- }
- }
-
- private void enableCamera(VideoCall videoCall, boolean isCameraRequired) {
- Log.d(this, "enableCamera: VideoCall=" + videoCall + " enabling=" + isCameraRequired);
- if (videoCall == null) {
- Log.w(this, "enableCamera: VideoCall is null.");
- return;
- }
-
- if (isCameraRequired) {
- InCallCameraManager cameraManager = InCallPresenter.getInstance().
- getInCallCameraManager();
- videoCall.setCamera(cameraManager.getActiveCameraId());
- mPreviewSurfaceState = PreviewSurfaceState.CAMERA_SET;
-
- videoCall.requestCameraCapabilities();
- } else {
- mPreviewSurfaceState = PreviewSurfaceState.NONE;
- videoCall.setCamera(null);
- }
- }
-
- /**
- * Exits video mode by hiding the video surfaces and making other adjustments (eg. audio).
- */
- private void exitVideoMode() {
- Log.d(this, "exitVideoMode");
-
- showVideoUi(VideoProfile.STATE_AUDIO_ONLY, Call.State.ACTIVE);
- enableCamera(mVideoCall, false);
- InCallPresenter.getInstance().setFullScreen(false);
-
- mIsVideoMode = false;
- }
-
- /**
- * Based on the current video state and call state, show or hide the incoming and
- * outgoing video surfaces. The outgoing video surface is shown any time video is transmitting.
- * The incoming video surface is shown whenever the video is un-paused and active.
- *
- * @param videoState The video state.
- * @param callState The call state.
- */
- private void showVideoUi(int videoState, int callState) {
- VideoCallUi ui = getUi();
- if (ui == null) {
- Log.e(this, "showVideoUi, VideoCallUi is null returning");
- return;
- }
- boolean showIncomingVideo = showIncomingVideo(videoState, callState);
- boolean showOutgoingVideo = showOutgoingVideo(videoState);
- Log.v(this, "showVideoUi : showIncoming = " + showIncomingVideo + " showOutgoing = "
- + showOutgoingVideo);
- if (showIncomingVideo || showOutgoingVideo) {
- ui.showVideoViews(showOutgoingVideo, showIncomingVideo);
-
- if (VideoProfile.isReceptionEnabled(videoState)) {
- loadProfilePhotoAsync();
- }
- } else {
- ui.hideVideoUi();
- }
-
- InCallPresenter.getInstance().enableScreenTimeout(
- VideoProfile.isAudioOnly(videoState));
- }
-
- /**
- * Determines if the incoming video surface should be shown based on the current videoState and
- * callState. The video surface is shown when incoming video is not paused, the call is active,
- * and video reception is enabled.
- *
- * @param videoState The current video state.
- * @param callState The current call state.
- * @return {@code true} if the incoming video surface should be shown, {@code false} otherwise.
- */
- public static boolean showIncomingVideo(int videoState, int callState) {
- if (!CompatUtils.isVideoCompatible()) {
- return false;
- }
-
- boolean isPaused = VideoProfile.isPaused(videoState);
- boolean isCallActive = callState == Call.State.ACTIVE;
-
- return !isPaused && isCallActive && VideoProfile.isReceptionEnabled(videoState);
- }
-
- /**
- * Determines if the outgoing video surface should be shown based on the current videoState.
- * The video surface is shown if video transmission is enabled.
- *
- * @param videoState The current video state.
- * @return {@code true} if the the outgoing video surface should be shown, {@code false}
- * otherwise.
- */
- public static boolean showOutgoingVideo(int videoState) {
- if (!CompatUtils.isVideoCompatible()) {
- return false;
- }
-
- return VideoProfile.isTransmissionEnabled(videoState);
- }
-
- /**
- * Handles peer video pause state changes.
- *
- * @param call The call which paused or un-pausedvideo transmission.
- * @param paused {@code True} when the video transmission is paused, {@code false} when video
- * transmission resumes.
- */
- @Override
- public void onPeerPauseStateChanged(Call call, boolean paused) {
- if (!call.equals(mPrimaryCall)) {
- return;
- }
-
- // TODO(vt): Show/hide the peer contact photo.
- }
-
- /**
- * Handles peer video dimension changes.
- *
- * @param call The call which experienced a peer video dimension change.
- * @param width The new peer video width .
- * @param height The new peer video height.
- */
- @Override
- public void onUpdatePeerDimensions(Call call, int width, int height) {
- Log.d(this, "onUpdatePeerDimensions: width= " + width + " height= " + height);
- VideoCallUi ui = getUi();
- if (ui == null) {
- Log.e(this, "VideoCallUi is null. Bail out");
- return;
- }
- if (!call.equals(mPrimaryCall)) {
- Log.e(this, "Current call is not equal to primary call. Bail out");
- return;
- }
-
- // Change size of display surface to match the peer aspect ratio
- if (width > 0 && height > 0) {
- setDisplayVideoSize(width, height);
- }
- }
-
- /**
- * Handles any video quality changes in the call.
- *
- * @param call The call which experienced a video quality change.
- * @param videoQuality The new video call quality.
- */
- @Override
- public void onVideoQualityChanged(Call call, int videoQuality) {
- // No-op
- }
-
- /**
- * Handles a change to the dimensions of the local camera. Receiving the camera capabilities
- * triggers the creation of the video
- *
- * @param call The call which experienced the camera dimension change.
- * @param width The new camera video width.
- * @param height The new camera video height.
- */
- @Override
- public void onCameraDimensionsChange(Call call, int width, int height) {
- Log.d(this, "onCameraDimensionsChange call=" + call + " width=" + width + " height="
- + height);
- VideoCallUi ui = getUi();
- if (ui == null) {
- Log.e(this, "onCameraDimensionsChange ui is null");
- return;
- }
-
- if (!call.equals(mPrimaryCall)) {
- Log.e(this, "Call is not primary call");
- return;
- }
-
- mPreviewSurfaceState = PreviewSurfaceState.CAPABILITIES_RECEIVED;
- changePreviewDimensions(width, height);
-
- // Check if the preview surface is ready yet; if it is, set it on the {@code VideoCall}.
- // If it not yet ready, it will be set when when creation completes.
- if (ui.isPreviewVideoSurfaceCreated()) {
- mPreviewSurfaceState = PreviewSurfaceState.SURFACE_SET;
- mVideoCall.setPreviewSurface(ui.getPreviewVideoSurface());
- }
- }
-
- /**
- * Changes the dimensions of the preview surface.
- *
- * @param width The new width.
- * @param height The new height.
- */
- private void changePreviewDimensions(int width, int height) {
- VideoCallUi ui = getUi();
- if (ui == null) {
- return;
- }
-
- // Resize the surface used to display the preview video
- ui.setPreviewSurfaceSize(width, height);
-
- // Configure the preview surface to the correct aspect ratio.
- float aspectRatio = 1.0f;
- if (width > 0 && height > 0) {
- aspectRatio = (float) width / (float) height;
- }
-
- // Resize the textureview housing the preview video and rotate it appropriately based on
- // the device orientation
- setPreviewSize(mDeviceOrientation, aspectRatio);
- }
-
- /**
- * Called when call session event is raised.
- *
- * @param event The call session event.
- */
- @Override
- public void onCallSessionEvent(int event) {
- StringBuilder sb = new StringBuilder();
- sb.append("onCallSessionEvent = ");
-
- switch (event) {
- case Connection.VideoProvider.SESSION_EVENT_RX_PAUSE:
- sb.append("rx_pause");
- break;
- case Connection.VideoProvider.SESSION_EVENT_RX_RESUME:
- sb.append("rx_resume");
- break;
- case Connection.VideoProvider.SESSION_EVENT_CAMERA_FAILURE:
- sb.append("camera_failure");
- break;
- case Connection.VideoProvider.SESSION_EVENT_CAMERA_READY:
- sb.append("camera_ready");
- break;
- default:
- sb.append("unknown event = ");
- sb.append(event);
- break;
- }
- Log.d(this, sb.toString());
- }
-
- /**
- * Handles a change to the call data usage
- *
- * @param dataUsage call data usage value
- */
- @Override
- public void onCallDataUsageChange(long dataUsage) {
- Log.d(this, "onCallDataUsageChange dataUsage=" + dataUsage);
- }
-
- /**
- * Handles changes to the device orientation.
- * @param orientation The screen orientation of the device (one of:
- * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_0},
- * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_90},
- * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_180},
- * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_270}).
- */
- @Override
- public void onDeviceOrientationChanged(int orientation) {
- mDeviceOrientation = orientation;
-
- VideoCallUi ui = getUi();
- if (ui == null) {
- Log.e(this, "onDeviceOrientationChanged: VideoCallUi is null");
- return;
- }
-
- Point previewDimensions = ui.getPreviewSize();
- if (previewDimensions == null) {
- return;
- }
- Log.d(this, "onDeviceOrientationChanged: orientation=" + orientation + " size: "
- + previewDimensions);
- changePreviewDimensions(previewDimensions.x, previewDimensions.y);
-
- ui.setPreviewRotation(mDeviceOrientation);
- }
-
- /**
- * Sets the preview surface size based on the current device orientation.
- * See: {@link InCallOrientationEventListener#SCREEN_ORIENTATION_0},
- * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_90},
- * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_180},
- * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_270}).
- *
- * @param orientation The device orientation
- * @param aspectRatio The aspect ratio of the camera (width / height).
- */
- private void setPreviewSize(int orientation, float aspectRatio) {
- VideoCallUi ui = getUi();
- if (ui == null) {
- return;
- }
-
- int height;
- int width;
-
- if (orientation == InCallOrientationEventListener.SCREEN_ORIENTATION_90 ||
- orientation == InCallOrientationEventListener.SCREEN_ORIENTATION_270) {
- width = (int) (mMinimumVideoDimension * aspectRatio);
- height = (int) mMinimumVideoDimension;
- } else {
- // Portrait or reverse portrait orientation.
- width = (int) mMinimumVideoDimension;
- height = (int) (mMinimumVideoDimension * aspectRatio);
- }
- ui.setPreviewSize(width, height);
- }
-
- /**
- * Sets the display video surface size based on peer width and height
- *
- * @param width peer width
- * @param height peer height
- */
- private void setDisplayVideoSize(int width, int height) {
- Log.v(this, "setDisplayVideoSize: Received peer width=" + width + " height=" + height);
- VideoCallUi ui = getUi();
- if (ui == null) {
- return;
- }
-
- // Get current display size
- Point size = ui.getScreenSize();
- Log.v(this, "setDisplayVideoSize: windowmgr width=" + size.x
- + " windowmgr height=" + size.y);
- if (size.y * width > size.x * height) {
- // current display height is too much. Correct it
- size.y = (int) (size.x * height / width);
- } else if (size.y * width < size.x * height) {
- // current display width is too much. Correct it
- size.x = (int) (size.y * width / height);
- }
- ui.setDisplayVideoSize(size.x, size.y);
- }
-
- /**
- * Exits fullscreen mode if the current call context has changed to a non-video call.
- *
- * @param call The call.
- */
- protected void maybeExitFullscreen(Call call) {
- if (call == null) {
- return;
- }
-
- if (!VideoUtils.isVideoCall(call) || call.getState() == Call.State.INCOMING) {
- InCallPresenter.getInstance().setFullScreen(false);
- }
- }
-
- /**
- * Schedules auto-entering of fullscreen mode.
- * Will not enter full screen mode if any of the following conditions are met:
- * 1. No call
- * 2. Call is not active
- * 3. Call is not video call
- * 4. Already in fullscreen mode
- * 5. The current video state is not bi-directional (if the remote party stops transmitting,
- * the user's contact photo would dominate in fullscreen mode).
- *
- * @param call The current call.
- */
- protected void maybeAutoEnterFullscreen(Call call) {
- if (!mIsAutoFullscreenEnabled) {
- return;
- }
-
- if (call == null || (
- call != null && (call.getState() != Call.State.ACTIVE ||
- !VideoUtils.isVideoCall(call)) ||
- InCallPresenter.getInstance().isFullscreen()) ||
- !VideoUtils.isBidirectionalVideoCall(call)) {
- // Ensure any previously scheduled attempt to enter fullscreen is cancelled.
- cancelAutoFullScreen();
- return;
- }
-
- if (mAutoFullScreenPending) {
- Log.v(this, "maybeAutoEnterFullscreen : already pending.");
- return;
- }
- Log.v(this, "maybeAutoEnterFullscreen : scheduled");
- mAutoFullScreenPending = true;
- mHandler.postDelayed(mAutoFullscreenRunnable, mAutoFullscreenTimeoutMillis);
- }
-
- /**
- * Cancels pending auto fullscreen mode.
- */
- public void cancelAutoFullScreen() {
- if (!mAutoFullScreenPending) {
- Log.v(this, "cancelAutoFullScreen : none pending.");
- return;
- }
- Log.v(this, "cancelAutoFullScreen : cancelling pending");
- mAutoFullScreenPending = false;
- }
-
- private static void updateCameraSelection(Call call) {
- Log.d(TAG, "updateCameraSelection: call=" + call);
- Log.d(TAG, "updateCameraSelection: call=" + toSimpleString(call));
-
- final Call activeCall = CallList.getInstance().getActiveCall();
- int cameraDir = Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN;
-
- // this function should never be called with null call object, however if it happens we
- // should handle it gracefully.
- if (call == null) {
- cameraDir = Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN;
- com.android.incallui.Log.e(TAG, "updateCameraSelection: Call object is null."
- + " Setting camera direction to default value (CAMERA_DIRECTION_UNKNOWN)");
- }
-
- // Clear camera direction if this is not a video call.
- else if (VideoUtils.isAudioCall(call)) {
- cameraDir = Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN;
- call.getVideoSettings().setCameraDir(cameraDir);
- }
-
- // If this is a waiting video call, default to active call's camera,
- // since we don't want to change the current camera for waiting call
- // without user's permission.
- else if (VideoUtils.isVideoCall(activeCall) && VideoUtils.isIncomingVideoCall(call)) {
- cameraDir = activeCall.getVideoSettings().getCameraDir();
- }
-
- // Infer the camera direction from the video state and store it,
- // if this is an outgoing video call.
- else if (VideoUtils.isOutgoingVideoCall(call) && !isCameraDirectionSet(call) ) {
- cameraDir = toCameraDirection(call.getVideoState());
- call.getVideoSettings().setCameraDir(cameraDir);
- }
-
- // Use the stored camera dir if this is an outgoing video call for which camera direction
- // is set.
- else if (VideoUtils.isOutgoingVideoCall(call)) {
- cameraDir = call.getVideoSettings().getCameraDir();
- }
-
- // Infer the camera direction from the video state and store it,
- // if this is an active video call and camera direction is not set.
- else if (VideoUtils.isActiveVideoCall(call) && !isCameraDirectionSet(call)) {
- cameraDir = toCameraDirection(call.getVideoState());
- call.getVideoSettings().setCameraDir(cameraDir);
- }
-
- // Use the stored camera dir if this is an active video call for which camera direction
- // is set.
- else if (VideoUtils.isActiveVideoCall(call)) {
- cameraDir = call.getVideoSettings().getCameraDir();
- }
-
- // For all other cases infer the camera direction but don't store it in the call object.
- else {
- cameraDir = toCameraDirection(call.getVideoState());
- }
-
- com.android.incallui.Log.d(TAG, "updateCameraSelection: Setting camera direction to " +
- cameraDir + " Call=" + call);
- final InCallCameraManager cameraManager = InCallPresenter.getInstance().
- getInCallCameraManager();
- cameraManager.setUseFrontFacingCamera(cameraDir ==
- Call.VideoSettings.CAMERA_DIRECTION_FRONT_FACING);
- }
-
- private static int toCameraDirection(int videoState) {
- return VideoProfile.isTransmissionEnabled(videoState) &&
- !VideoProfile.isBidirectional(videoState)
- ? Call.VideoSettings.CAMERA_DIRECTION_BACK_FACING
- : Call.VideoSettings.CAMERA_DIRECTION_FRONT_FACING;
- }
-
- private static boolean isCameraDirectionSet(Call call) {
- return VideoUtils.isVideoCall(call) && call.getVideoSettings().getCameraDir()
- != Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN;
- }
-
- private static String toSimpleString(Call call) {
- return call == null ? null : call.toSimpleString();
- }
-
- /**
- * Starts an asynchronous load of the user's profile photo.
- */
- public void loadProfilePhotoAsync() {
- final VideoCallUi ui = getUi();
- if (ui == null) {
- return;
- }
-
- final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
- /**
- * Performs asynchronous load of the user profile information.
- *
- * @param params The parameters of the task.
- *
- * @return {@code null}.
- */
- @Override
- protected Void doInBackground(Void... params) {
- if (mProfileInfo == null) {
- // Try and read the photo URI from the local profile.
- mProfileInfo = new ContactInfoCache.ContactCacheEntry();
- final Cursor cursor = mContext.getContentResolver().query(
- ContactsContract.Profile.CONTENT_URI, new String[]{
- ContactsContract.CommonDataKinds.Phone._ID,
- ContactsContract.CommonDataKinds.Phone.PHOTO_URI,
- ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY,
- ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
- ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_ALTERNATIVE
- }, null, null, null);
- if (cursor != null) {
- try {
- if (cursor.moveToFirst()) {
- mProfileInfo.lookupKey = cursor.getString(cursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY));
- String photoUri = cursor.getString(cursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.PHOTO_URI));
- mProfileInfo.displayPhotoUri = photoUri == null ? null
- : Uri.parse(photoUri);
- mProfileInfo.namePrimary = cursor.getString(cursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
- mProfileInfo.nameAlternative = cursor.getString(
- cursor.getColumnIndex(ContactsContract.CommonDataKinds
- .Phone.DISPLAY_NAME_ALTERNATIVE));
- }
- } finally {
- cursor.close();
- }
- }
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- // If user profile information was found, issue an async request to load the user's
- // profile photo.
- if (mProfileInfo != null) {
- if (mContactPhotoManager == null) {
- mContactPhotoManager = ContactPhotoManager.getInstance(mContext);
- }
- ContactPhotoManager.DefaultImageRequest imageRequest = (mProfileInfo != null)
- ? null :
- new ContactPhotoManager.DefaultImageRequest(mProfileInfo.namePrimary,
- mProfileInfo.lookupKey, false /* isCircularPhoto */);
-
- ImageView photoView = ui.getPreviewPhotoView();
- if (photoView == null) {
- return;
- }
- mContactPhotoManager.loadDirectoryPhoto(photoView,
- mProfileInfo.displayPhotoUri,
- false /* darkTheme */, false /* isCircular */, imageRequest);
- }
- }
- };
-
- task.execute();
- }
-
- /**
- * Defines the VideoCallUI interactions.
- */
- public interface VideoCallUi extends Ui {
- void showVideoViews(boolean showPreview, boolean showIncoming);
- void hideVideoUi();
- boolean isDisplayVideoSurfaceCreated();
- boolean isPreviewVideoSurfaceCreated();
- Surface getDisplayVideoSurface();
- Surface getPreviewVideoSurface();
- int getCurrentRotation();
- void setPreviewSize(int width, int height);
- void setPreviewSurfaceSize(int width, int height);
- void setDisplayVideoSize(int width, int height);
- Point getScreenSize();
- Point getPreviewSize();
- void cleanupSurfaces();
- ImageView getPreviewPhotoView();
- void adjustPreviewLocation(boolean shiftUp, int offset);
- void setPreviewRotation(int orientation);
- }
-}
diff --git a/InCallUI/src/com/android/incallui/VideoPauseController.java b/InCallUI/src/com/android/incallui/VideoPauseController.java
deleted file mode 100644
index fb873500e..000000000
--- a/InCallUI/src/com/android/incallui/VideoPauseController.java
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * 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.incallui;
-
-import com.android.incallui.Call.State;
-import com.android.incallui.InCallPresenter.InCallState;
-import com.android.incallui.InCallPresenter.InCallStateListener;
-import com.android.incallui.InCallPresenter.IncomingCallListener;
-import com.android.incallui.InCallVideoCallCallbackNotifier.SessionModificationListener;
-import com.google.common.base.Preconditions;
-
-import android.telecom.VideoProfile;
-
-/**
- * This class is responsible for generating video pause/resume requests when the InCall UI is sent
- * to the background and subsequently brought back to the foreground.
- */
-class VideoPauseController implements InCallStateListener, IncomingCallListener {
- private static final String TAG = "VideoPauseController";
-
- /**
- * Keeps track of the current active/foreground call.
- */
- private class CallContext {
- public CallContext(Call call) {
- Preconditions.checkNotNull(call);
- update(call);
- }
-
- public void update(Call call) {
- mCall = Preconditions.checkNotNull(call);
- mState = call.getState();
- mVideoState = call.getVideoState();
- }
-
- public int getState() {
- return mState;
- }
-
- public int getVideoState() {
- return mVideoState;
- }
-
- public String toString() {
- return String.format("CallContext {CallId=%s, State=%s, VideoState=%d}",
- mCall.getId(), mState, mVideoState);
- }
-
- public Call getCall() {
- return mCall;
- }
-
- private int mState = State.INVALID;
- private int mVideoState;
- private Call mCall;
- }
-
- private InCallPresenter mInCallPresenter;
- private static VideoPauseController sVideoPauseController;
-
- /**
- * The current call context, if applicable.
- */
- private CallContext mPrimaryCallContext = null;
-
- /**
- * Tracks whether the application is in the background. {@code True} if the application is in
- * the background, {@code false} otherwise.
- */
- private boolean mIsInBackground = false;
-
- /**
- * Singleton accessor for the {@link VideoPauseController}.
- * @return Singleton instance of the {@link VideoPauseController}.
- */
- /*package*/
- static synchronized VideoPauseController getInstance() {
- if (sVideoPauseController == null) {
- sVideoPauseController = new VideoPauseController();
- }
- return sVideoPauseController;
- }
-
- /**
- * Configures the {@link VideoPauseController} to listen to call events. Configured via the
- * {@link com.android.incallui.InCallPresenter}.
- *
- * @param inCallPresenter The {@link com.android.incallui.InCallPresenter}.
- */
- public void setUp(InCallPresenter inCallPresenter) {
- log("setUp");
- mInCallPresenter = Preconditions.checkNotNull(inCallPresenter);
- mInCallPresenter.addListener(this);
- mInCallPresenter.addIncomingCallListener(this);
- }
-
- /**
- * Cleans up the {@link VideoPauseController} by removing all listeners and clearing its
- * internal state. Called from {@link com.android.incallui.InCallPresenter}.
- */
- public void tearDown() {
- log("tearDown...");
- mInCallPresenter.removeListener(this);
- mInCallPresenter.removeIncomingCallListener(this);
- clear();
- }
-
- /**
- * Clears the internal state for the {@link VideoPauseController}.
- */
- private void clear() {
- mInCallPresenter = null;
- mPrimaryCallContext = null;
- mIsInBackground = false;
- }
-
- /**
- * Handles changes in the {@link InCallState}. Triggers pause and resumption of video for the
- * current foreground call.
- *
- * @param oldState The previous {@link InCallState}.
- * @param newState The current {@link InCallState}.
- * @param callList List of current call.
- */
- @Override
- public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
- log("onStateChange, OldState=" + oldState + " NewState=" + newState);
-
- Call call = null;
- if (newState == InCallState.INCOMING) {
- call = callList.getIncomingCall();
- } else if (newState == InCallState.WAITING_FOR_ACCOUNT) {
- call = callList.getWaitingForAccountCall();
- } else if (newState == InCallState.PENDING_OUTGOING) {
- call = callList.getPendingOutgoingCall();
- } else if (newState == InCallState.OUTGOING) {
- call = callList.getOutgoingCall();
- } else {
- call = callList.getActiveCall();
- }
-
- boolean hasPrimaryCallChanged = !areSame(call, mPrimaryCallContext);
- boolean canVideoPause = VideoUtils.canVideoPause(call);
- log("onStateChange, hasPrimaryCallChanged=" + hasPrimaryCallChanged);
- log("onStateChange, canVideoPause=" + canVideoPause);
- log("onStateChange, IsInBackground=" + mIsInBackground);
-
- if (hasPrimaryCallChanged) {
- onPrimaryCallChanged(call);
- return;
- }
-
- if (isDialing(mPrimaryCallContext) && canVideoPause && mIsInBackground) {
- // Bring UI to foreground if outgoing request becomes active while UI is in
- // background.
- bringToForeground();
- } else if (!isVideoCall(mPrimaryCallContext) && canVideoPause && mIsInBackground) {
- // Bring UI to foreground if VoLTE call becomes active while UI is in
- // background.
- bringToForeground();
- }
-
- updatePrimaryCallContext(call);
- }
-
- /**
- * Handles a change to the primary call.
- * <p>
- * Reject incoming or hangup dialing call: Where the previous call was an incoming call or a
- * call in dialing state, resume the new primary call.
- * Call swap: Where the new primary call is incoming, pause video on the previous primary call.
- *
- * @param call The new primary call.
- */
- private void onPrimaryCallChanged(Call call) {
- log("onPrimaryCallChanged: New call = " + call);
- log("onPrimaryCallChanged: Old call = " + mPrimaryCallContext);
- log("onPrimaryCallChanged, IsInBackground=" + mIsInBackground);
-
- Preconditions.checkState(!areSame(call, mPrimaryCallContext));
- final boolean canVideoPause = VideoUtils.canVideoPause(call);
-
- if ((isIncomingCall(mPrimaryCallContext) || isDialing(mPrimaryCallContext) ||
- (call != null && VideoProfile.isPaused(call.getVideoState())))
- && canVideoPause && !mIsInBackground) {
- // Send resume request for the active call, if user rejects incoming call, ends dialing
- // call, or the call was previously in a paused state and UI is in the foreground.
- sendRequest(call, true);
- } else if (isIncomingCall(call) && canVideoPause(mPrimaryCallContext)) {
- // Send pause request if there is an active video call, and we just received a new
- // incoming call.
- sendRequest(mPrimaryCallContext.getCall(), false);
- }
-
- updatePrimaryCallContext(call);
- }
-
- /**
- * Handles new incoming calls by triggering a change in the primary call.
- *
- * @param oldState the old {@link InCallState}.
- * @param newState the new {@link InCallState}.
- * @param call the incoming call.
- */
- @Override
- public void onIncomingCall(InCallState oldState, InCallState newState, Call call) {
- log("onIncomingCall, OldState=" + oldState + " NewState=" + newState + " Call=" + call);
-
- if (areSame(call, mPrimaryCallContext)) {
- return;
- }
-
- onPrimaryCallChanged(call);
- }
-
- /**
- * Caches a reference to the primary call and stores its previous state.
- *
- * @param call The new primary call.
- */
- private void updatePrimaryCallContext(Call call) {
- if (call == null) {
- mPrimaryCallContext = null;
- } else if (mPrimaryCallContext != null) {
- mPrimaryCallContext.update(call);
- } else {
- mPrimaryCallContext = new CallContext(call);
- }
- }
-
- /**
- * Called when UI goes in/out of the foreground.
- * @param showing true if UI is in the foreground, false otherwise.
- */
- public void onUiShowing(boolean showing) {
- // Only send pause/unpause requests if we are in the INCALL state.
- if (mInCallPresenter == null) {
- return;
- }
- final boolean isInCall = mInCallPresenter.getInCallState() == InCallState.INCALL;
- if (showing) {
- onResume(isInCall);
- } else {
- onPause(isInCall);
- }
- }
-
- /**
- * Called when UI is brought to the foreground. Sends a session modification request to resume
- * the outgoing video.
- * @param isInCall true if phone state is INCALL, false otherwise
- */
- private void onResume(boolean isInCall) {
- log("onResume");
-
- mIsInBackground = false;
- if (canVideoPause(mPrimaryCallContext) && isInCall) {
- sendRequest(mPrimaryCallContext.getCall(), true);
- } else {
- log("onResume. Ignoring...");
- }
- }
-
- /**
- * Called when UI is sent to the background. Sends a session modification request to pause the
- * outgoing video.
- * @param isInCall true if phone state is INCALL, false otherwise
- */
- private void onPause(boolean isInCall) {
- log("onPause");
-
- mIsInBackground = true;
- if (canVideoPause(mPrimaryCallContext) && isInCall) {
- sendRequest(mPrimaryCallContext.getCall(), false);
- } else {
- log("onPause, Ignoring...");
- }
- }
-
- private void bringToForeground() {
- if (mInCallPresenter != null) {
- log("Bringing UI to foreground");
- mInCallPresenter.bringToForeground(false);
- } else {
- loge("InCallPresenter is null. Cannot bring UI to foreground");
- }
- }
-
- /**
- * Sends Pause/Resume request.
- *
- * @param call Call to be paused/resumed.
- * @param resume If true resume request will be sent, otherwise pause request.
- */
- private void sendRequest(Call call, boolean resume) {
- // Check if this call supports pause/un-pause.
- if (!call.can(android.telecom.Call.Details.CAPABILITY_CAN_PAUSE_VIDEO)) {
- return;
- }
-
- if (resume) {
- log("sending resume request, call=" + call);
- call.getVideoCall()
- .sendSessionModifyRequest(VideoUtils.makeVideoUnPauseProfile(call));
- } else {
- log("sending pause request, call=" + call);
- call.getVideoCall().sendSessionModifyRequest(VideoUtils.makeVideoPauseProfile(call));
- }
- }
-
- /**
- * Determines if a given call is the same one stored in a {@link CallContext}.
- *
- * @param call The call.
- * @param callContext The call context.
- * @return {@code true} if the {@link Call} is the same as the one referenced in the
- * {@link CallContext}.
- */
- private static boolean areSame(Call call, CallContext callContext) {
- if (call == null && callContext == null) {
- return true;
- } else if (call == null || callContext == null) {
- return false;
- }
- return call.equals(callContext.getCall());
- }
-
- /**
- * Determines if a video call can be paused. Only a video call which is active can be paused.
- *
- * @param callContext The call context to check.
- * @return {@code true} if the call is an active video call.
- */
- private static boolean canVideoPause(CallContext callContext) {
- return isVideoCall(callContext) && callContext.getState() == Call.State.ACTIVE;
- }
-
- /**
- * Determines if a call referenced by a {@link CallContext} is a video call.
- *
- * @param callContext The call context.
- * @return {@code true} if the call is a video call, {@code false} otherwise.
- */
- private static boolean isVideoCall(CallContext callContext) {
- return callContext != null && VideoUtils.isVideoCall(callContext.getVideoState());
- }
-
- /**
- * Determines if call is in incoming/waiting state.
- *
- * @param call The call context.
- * @return {@code true} if the call is in incoming or waiting state, {@code false} otherwise.
- */
- private static boolean isIncomingCall(CallContext call) {
- return call != null && isIncomingCall(call.getCall());
- }
-
- /**
- * Determines if a call is in incoming/waiting state.
- *
- * @param call The call.
- * @return {@code true} if the call is in incoming or waiting state, {@code false} otherwise.
- */
- private static boolean isIncomingCall(Call call) {
- return call != null && (call.getState() == Call.State.CALL_WAITING
- || call.getState() == Call.State.INCOMING);
- }
-
- /**
- * Determines if a call is dialing.
- *
- * @param call The call context.
- * @return {@code true} if the call is dialing, {@code false} otherwise.
- */
- private static boolean isDialing(CallContext call) {
- return call != null && Call.State.isDialing(call.getState());
- }
-
- /**
- * Determines if a call is holding.
- *
- * @param call The call context.
- * @return {@code true} if the call is holding, {@code false} otherwise.
- */
- private static boolean isHolding(CallContext call) {
- return call != null && call.getState() == Call.State.ONHOLD;
- }
-
- /**
- * Logs a debug message.
- *
- * @param msg The message.
- */
- private void log(String msg) {
- Log.d(this, TAG + msg);
- }
-
- /**
- * Logs an error message.
- *
- * @param msg The message.
- */
- private void loge(String msg) {
- Log.e(this, TAG + msg);
- }
-}
diff --git a/InCallUI/src/com/android/incallui/VideoUtils.java b/InCallUI/src/com/android/incallui/VideoUtils.java
deleted file mode 100644
index a2eb8bcf2..000000000
--- a/InCallUI/src/com/android/incallui/VideoUtils.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.incallui;
-
-import android.telecom.VideoProfile;
-
-import com.android.contacts.common.compat.CompatUtils;
-
-import com.google.common.base.Preconditions;
-
-public class VideoUtils {
-
- public static boolean isVideoCall(Call call) {
- return call != null && isVideoCall(call.getVideoState());
- }
-
- public static boolean isVideoCall(int videoState) {
- if (!CompatUtils.isVideoCompatible()) {
- return false;
- }
-
- return VideoProfile.isTransmissionEnabled(videoState)
- || VideoProfile.isReceptionEnabled(videoState);
- }
-
- public static boolean isBidirectionalVideoCall(Call call) {
- if (!CompatUtils.isVideoCompatible()) {
- return false;
- }
-
- return VideoProfile.isBidirectional(call.getVideoState());
- }
-
- public static boolean isTransmissionEnabled(Call call) {
- if (!CompatUtils.isVideoCompatible()) {
- return false;
- }
-
- return VideoProfile.isTransmissionEnabled(call.getVideoState());
- }
-
- public static boolean isIncomingVideoCall(Call call) {
- if (!VideoUtils.isVideoCall(call)) {
- return false;
- }
- final int state = call.getState();
- return (state == Call.State.INCOMING) || (state == Call.State.CALL_WAITING);
- }
-
- public static boolean isActiveVideoCall(Call call) {
- return VideoUtils.isVideoCall(call) && call.getState() == Call.State.ACTIVE;
- }
-
- public static boolean isOutgoingVideoCall(Call call) {
- if (!VideoUtils.isVideoCall(call)) {
- return false;
- }
- final int state = call.getState();
- return Call.State.isDialing(state) || state == Call.State.CONNECTING
- || state == Call.State.SELECT_PHONE_ACCOUNT;
- }
-
- public static boolean isAudioCall(Call call) {
- if (!CompatUtils.isVideoCompatible()) {
- return true;
- }
-
- return call != null && VideoProfile.isAudioOnly(call.getVideoState());
- }
-
- // TODO (ims-vt) Check if special handling is needed for CONF calls.
- public static boolean canVideoPause(Call call) {
- return isVideoCall(call) && call.getState() == Call.State.ACTIVE;
- }
-
- public static VideoProfile makeVideoPauseProfile(Call call) {
- Preconditions.checkNotNull(call);
- Preconditions.checkState(!VideoProfile.isAudioOnly(call.getVideoState()));
- return new VideoProfile(getPausedVideoState(call.getVideoState()));
- }
-
- public static VideoProfile makeVideoUnPauseProfile(Call call) {
- Preconditions.checkNotNull(call);
- return new VideoProfile(getUnPausedVideoState(call.getVideoState()));
- }
-
- public static int getUnPausedVideoState(int videoState) {
- return videoState & (~VideoProfile.STATE_PAUSED);
- }
-
- public static int getPausedVideoState(int videoState) {
- return videoState | VideoProfile.STATE_PAUSED;
- }
-
-}
diff --git a/InCallUI/src/com/android/incallui/async/PausableExecutor.java b/InCallUI/src/com/android/incallui/async/PausableExecutor.java
deleted file mode 100644
index 1b8201a79..000000000
--- a/InCallUI/src/com/android/incallui/async/PausableExecutor.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.incallui.async;
-
-import com.android.contacts.common.testing.NeededForTesting;
-
-import java.util.concurrent.Executor;
-
-/**
- * Executor that can be used to easily synchronize testing and production code. Production code
- * should call {@link #milestone()} at points in the code where the state of the system is worthy of
- * testing. In a test scenario, this method will pause execution until the test acknowledges the
- * milestone through the use of {@link #ackMilestoneForTesting()}.
- */
-public interface PausableExecutor extends Executor {
-
- /**
- * Method called from asynchronous production code to inform this executor that it has
- * reached a point that puts the system into a state worth testing. TestableExecutors intended
- * for use in a testing environment should cause the calling thread to block. In the production
- * environment this should be a no-op.
- */
- void milestone();
-
- /**
- * Method called from the test code to inform this executor that the state of the production
- * system at the current milestone has been sufficiently tested. Every milestone must be
- * acknowledged.
- */
- @NeededForTesting
- void ackMilestoneForTesting();
-
- /**
- * Method called from the test code to inform this executor that the tests are finished with all
- * milestones. Future calls to {@link #milestone()} or {@link #awaitMilestoneForTesting()}
- * should return immediately.
- */
- @NeededForTesting
- void ackAllMilestonesForTesting();
-
- /**
- * Method called from the test code to block until a milestone has been reached in the
- * production code.
- */
- @NeededForTesting
- void awaitMilestoneForTesting() throws InterruptedException;
-}
diff --git a/InCallUI/src/com/android/incallui/async/PausableExecutorImpl.java b/InCallUI/src/com/android/incallui/async/PausableExecutorImpl.java
deleted file mode 100644
index 15900e57b..000000000
--- a/InCallUI/src/com/android/incallui/async/PausableExecutorImpl.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.incallui.async;
-
-import java.util.concurrent.Executors;
-
-/**
- * {@link PausableExecutor} intended for use in production environments.
- */
-public class PausableExecutorImpl implements PausableExecutor {
-
- @Override
- public void milestone() {}
-
- @Override
- public void ackMilestoneForTesting() {}
-
- @Override
- public void ackAllMilestonesForTesting() {}
-
- @Override
- public void awaitMilestoneForTesting() {}
-
- @Override
- public void execute(Runnable command) {
- Executors.newSingleThreadExecutor().execute(command);
- }
-}
diff --git a/InCallUI/src/com/android/incallui/ringtone/DialerRingtoneManager.java b/InCallUI/src/com/android/incallui/ringtone/DialerRingtoneManager.java
deleted file mode 100644
index 39844e5a2..000000000
--- a/InCallUI/src/com/android/incallui/ringtone/DialerRingtoneManager.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * 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.incallui.ringtone;
-
-import com.google.common.base.Preconditions;
-
-import android.content.ContentResolver;
-import android.net.Uri;
-import android.provider.Settings;
-import android.support.annotation.Nullable;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.incallui.Call;
-import com.android.incallui.Call.State;
-import com.android.incallui.CallList;
-
-/**
- * Class that determines when ringtones should be played and can play the call waiting tone when
- * necessary.
- */
-public class DialerRingtoneManager {
-
- /*
- * Flag used to determine if the Dialer is responsible for playing ringtones for incoming calls.
- * Once we're ready to enable Dialer Ringing, these flags should be removed.
- */
- private static final boolean IS_DIALER_RINGING_ENABLED = false;
- private Boolean mIsDialerRingingEnabledForTesting;
-
- private final InCallTonePlayer mInCallTonePlayer;
- private final CallList mCallList;
-
- /**
- * Creates the DialerRingtoneManager with the given {@link InCallTonePlayer}.
- *
- * @param inCallTonePlayer the tone player used to play in-call tones.
- * @param callList the CallList used to check for {@link State#CALL_WAITING}
- * @throws NullPointerException if inCallTonePlayer or callList are null
- */
- public DialerRingtoneManager(InCallTonePlayer inCallTonePlayer, CallList callList) {
- mInCallTonePlayer = Preconditions.checkNotNull(inCallTonePlayer);
- mCallList = Preconditions.checkNotNull(callList);
- }
-
- /**
- * Determines if a ringtone should be played for the given call state (see {@link State}) and
- * {@link Uri}.
- *
- * @param callState the call state for the call being checked.
- * @param ringtoneUri the ringtone to potentially play.
- * @return {@code true} if the ringtone should be played, {@code false} otherwise.
- */
- public boolean shouldPlayRingtone(int callState, @Nullable Uri ringtoneUri) {
- return isDialerRingingEnabled()
- && translateCallStateForCallWaiting(callState) == State.INCOMING
- && ringtoneUri != null;
- }
-
- /**
- * Determines if an incoming call should vibrate as well as ring.
- *
- * @param resolver {@link ContentResolver} used to look up the
- * {@link Settings.System#VIBRATE_WHEN_RINGING} setting.
- * @return {@code true} if the call should vibrate, {@code false} otherwise.
- */
- public boolean shouldVibrate(ContentResolver resolver) {
- return Settings.System.getInt(resolver, Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
- }
-
- /**
- * The incoming callState is never set as {@link State#CALL_WAITING} because
- * {@link Call#translateState(int)} doesn't account for that case, check for it here
- */
- private int translateCallStateForCallWaiting(int callState) {
- if (callState != State.INCOMING) {
- return callState;
- }
- return mCallList.getActiveCall() == null ? State.INCOMING : State.CALL_WAITING;
- }
-
- private boolean isDialerRingingEnabled() {
- if (mIsDialerRingingEnabledForTesting != null) {
- return mIsDialerRingingEnabledForTesting;
- }
- return CompatUtils.isNCompatible() && IS_DIALER_RINGING_ENABLED;
- }
-
- /**
- * Determines if a call waiting tone should be played for the the given call state
- * (see {@link State}).
- *
- * @param callState the call state for the call being checked.
- * @return {@code true} if the call waiting tone should be played, {@code false} otherwise.
- */
- public boolean shouldPlayCallWaitingTone(int callState) {
- return isDialerRingingEnabled()
- && translateCallStateForCallWaiting(callState) == State.CALL_WAITING
- && !mInCallTonePlayer.isPlayingTone();
- }
-
- /**
- * Plays the call waiting tone.
- */
- public void playCallWaitingTone() {
- if (!isDialerRingingEnabled()) {
- return;
- }
- mInCallTonePlayer.play(InCallTonePlayer.TONE_CALL_WAITING);
- }
-
- /**
- * Stops playing the call waiting tone.
- */
- public void stopCallWaitingTone() {
- if (!isDialerRingingEnabled()) {
- return;
- }
- mInCallTonePlayer.stop();
- }
-
- @NeededForTesting
- void setDialerRingingEnabledForTesting(boolean status) {
- mIsDialerRingingEnabledForTesting = status;
- }
-}
diff --git a/InCallUI/src/com/android/incallui/ringtone/InCallTonePlayer.java b/InCallUI/src/com/android/incallui/ringtone/InCallTonePlayer.java
deleted file mode 100644
index 3a8b03d91..000000000
--- a/InCallUI/src/com/android/incallui/ringtone/InCallTonePlayer.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * 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.incallui.ringtone;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-
-import android.media.AudioManager;
-import android.media.ToneGenerator;
-import android.support.annotation.Nullable;
-
-import com.android.incallui.Log;
-import com.android.incallui.async.PausableExecutor;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Class responsible for playing in-call related tones in a background thread. This class only
- * allows one tone to be played at a time.
- */
-public class InCallTonePlayer {
-
- public static final int TONE_CALL_WAITING = 4;
-
- public static final int VOLUME_RELATIVE_HIGH_PRIORITY = 80;
-
- private final ToneGeneratorFactory mToneGeneratorFactory;
- private final PausableExecutor mExecutor;
- private @Nullable CountDownLatch mNumPlayingTones;
-
- /**
- * Creates a new InCallTonePlayer.
- *
- * @param toneGeneratorFactory the {@link ToneGeneratorFactory} used to create
- * {@link ToneGenerator}s.
- * @param executor the {@link PausableExecutor} used to play tones in a background thread.
- * @throws NullPointerException if audioModeProvider, toneGeneratorFactory, or executor are
- * {@code null}.
- */
- public InCallTonePlayer(ToneGeneratorFactory toneGeneratorFactory, PausableExecutor executor) {
- mToneGeneratorFactory = Preconditions.checkNotNull(toneGeneratorFactory);
- mExecutor = Preconditions.checkNotNull(executor);
- }
-
- /**
- * @return {@code true} if a tone is currently playing, {@code false} otherwise.
- */
- public boolean isPlayingTone() {
- return mNumPlayingTones != null && mNumPlayingTones.getCount() > 0;
- }
-
- /**
- * Plays the given tone in a background thread.
- *
- * @param tone the tone to play.
- * @throws IllegalStateException if a tone is already playing.
- * @throws IllegalArgumentException if the tone is invalid.
- */
- public void play(int tone) {
- if (isPlayingTone()) {
- throw new IllegalStateException("Tone already playing");
- }
- final ToneGeneratorInfo info = getToneGeneratorInfo(tone);
- mNumPlayingTones = new CountDownLatch(1);
- mExecutor.execute(new Runnable() {
- @Override
- public void run() {
- playOnBackgroundThread(info);
- }
- });
- }
-
- private ToneGeneratorInfo getToneGeneratorInfo(int tone) {
- switch (tone) {
- case TONE_CALL_WAITING:
- /*
- * Call waiting tones play until they're stopped either by the user accepting or
- * declining the call so the tone length is set at what's effectively forever. The
- * tone is played at a high priority volume and through STREAM_VOICE_CALL since it's
- * call related and using that stream will route it through bluetooth devices
- * appropriately.
- */
- return new ToneGeneratorInfo(ToneGenerator.TONE_SUP_CALL_WAITING,
- VOLUME_RELATIVE_HIGH_PRIORITY,
- Integer.MAX_VALUE,
- AudioManager.STREAM_VOICE_CALL);
- default:
- throw new IllegalArgumentException("Bad tone: " + tone);
- }
- }
-
- private void playOnBackgroundThread(ToneGeneratorInfo info) {
- ToneGenerator toneGenerator = null;
- try {
- Log.v(this, "Starting tone " + info);
- toneGenerator = mToneGeneratorFactory.newInCallToneGenerator(info.stream, info.volume);
- toneGenerator.startTone(info.tone);
- /*
- * During tests, this will block until the tests call mExecutor.ackMilestone. This call
- * allows for synchronization to the point where the tone has started playing.
- */
- mExecutor.milestone();
- if (mNumPlayingTones != null) {
- mNumPlayingTones.await(info.toneLengthMillis, TimeUnit.MILLISECONDS);
- // Allows for synchronization to the point where the tone has completed playing.
- mExecutor.milestone();
- }
- } catch (InterruptedException e) {
- Log.w(this, "Interrupted while playing in-call tone.");
- } finally {
- if (toneGenerator != null) {
- toneGenerator.release();
- }
- if (mNumPlayingTones != null) {
- mNumPlayingTones.countDown();
- }
- // Allows for synchronization to the point where this background thread has cleaned up.
- mExecutor.milestone();
- }
- }
-
- /**
- * Stops playback of the current tone.
- */
- public void stop() {
- if (mNumPlayingTones != null) {
- mNumPlayingTones.countDown();
- }
- }
-
- private static class ToneGeneratorInfo {
- public final int tone;
- public final int volume;
- public final int toneLengthMillis;
- public final int stream;
-
- public ToneGeneratorInfo(int toneGeneratorType, int volume, int toneLengthMillis,
- int stream) {
- this.tone = toneGeneratorType;
- this.volume = volume;
- this.toneLengthMillis = toneLengthMillis;
- this.stream = stream;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(this)
- .add("tone", tone)
- .add("volume", volume)
- .add("toneLengthMillis", toneLengthMillis).toString();
- }
- }
-}
diff --git a/InCallUI/src/com/android/incallui/ringtone/ToneGeneratorFactory.java b/InCallUI/src/com/android/incallui/ringtone/ToneGeneratorFactory.java
deleted file mode 100644
index ac47c8a7d..000000000
--- a/InCallUI/src/com/android/incallui/ringtone/ToneGeneratorFactory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.incallui.ringtone;
-
-import android.media.ToneGenerator;
-
-/**
- * Factory used to create {@link ToneGenerator}s.
- */
-public class ToneGeneratorFactory {
-
- /**
- * Creates a new {@link ToneGenerator} to use while in a call.
- *
- * @param stream the stream through which to play tones.
- * @param volume the volume at which to play tones.
- * @return a new ToneGenerator.
- */
- public ToneGenerator newInCallToneGenerator(int stream, int volume) {
- return new ToneGenerator(stream, volume);
- }
-}
diff --git a/InCallUI/src/com/android/incallui/service/PhoneNumberService.java b/InCallUI/src/com/android/incallui/service/PhoneNumberService.java
deleted file mode 100644
index 70da4ef3a..000000000
--- a/InCallUI/src/com/android/incallui/service/PhoneNumberService.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui.service;
-
-import android.graphics.Bitmap;
-
-/**
- * Provides phone number lookup services.
- */
-public interface PhoneNumberService {
-
- /**
- * Get a phone number number asynchronously.
- *
- * @param phoneNumber The phone number to lookup.
- * @param listener The listener to notify when the phone number lookup is complete.
- * @param imageListener The listener to notify when the image lookup is complete.
- */
- public void getPhoneNumberInfo(String phoneNumber, NumberLookupListener listener,
- ImageLookupListener imageListener, boolean isIncoming);
-
- public interface NumberLookupListener {
-
- /**
- * Callback when a phone number has been looked up.
- *
- * @param info The looked up information. Or (@literal null} if there are no results.
- */
- public void onPhoneNumberInfoComplete(PhoneNumberInfo info);
- }
-
- public interface ImageLookupListener {
-
- /**
- * Callback when a image has been fetched.
- *
- * @param bitmap The fetched image.
- */
- public void onImageFetchComplete(Bitmap bitmap);
- }
-
- public interface PhoneNumberInfo {
- public String getDisplayName();
- public String getNumber();
- public int getPhoneType();
- public String getPhoneLabel();
- public String getNormalizedNumber();
- public String getImageUrl();
- public String getLookupKey();
- public boolean isBusiness();
- public int getLookupSource();
- }
-}
diff --git a/InCallUI/src/com/android/incallui/spam/SpamCallListListener.java b/InCallUI/src/com/android/incallui/spam/SpamCallListListener.java
deleted file mode 100644
index b97f4d099..000000000
--- a/InCallUI/src/com/android/incallui/spam/SpamCallListListener.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.incallui.spam;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import android.content.Context;
-import android.telecom.DisconnectCause;
-import android.text.TextUtils;
-
-import com.android.dialer.calllog.CallLogAsyncTaskUtil;
-import com.android.incallui.Call;
-import com.android.incallui.CallList;
-import com.android.incallui.Log;
-
-public class SpamCallListListener implements CallList.Listener {
- private static final String TAG = "SpamCallListListener";
-
- private final Context mContext;
-
- public SpamCallListListener(Context context) {
- mContext = context;
- }
-
- @Override
- public void onIncomingCall(final Call call) {
- String number = call.getNumber();
- if (TextUtils.isEmpty(number)) {
- return;
- }
- CallLogAsyncTaskUtil.getNumberInCallHistory(mContext, number,
- new CallLogAsyncTaskUtil.OnGetNumberInCallHistoryListener() {
- @Override
- public void onComplete(boolean inCallHistory) {
- call.setCallHistoryStatus(inCallHistory ?
- Call.CALL_HISTORY_STATUS_PRESENT
- : Call.CALL_HISTORY_STATUS_NOT_PRESENT);
- }
- });
- }
-
- @Override
- public void onUpgradeToVideo(Call call) {}
-
- @Override
- public void onCallListChange(CallList callList) {}
-
- @Override
- public void onDisconnect(Call call) {
- if (shouldShowAfterCallNotification(call)) {
- showNotification(call.getNumber());
- }
- }
-
- /**
- * Posts the intent for displaying the after call spam notification to the user.
- */
- @VisibleForTesting
- /* package */ void showNotification(String number) {
- //TODO(mhashmi): build and show notifications here
- }
-
- /**
- * Determines if the after call notification should be shown for the specified call.
- */
- private boolean shouldShowAfterCallNotification(Call call) {
- String number = call.getNumber();
- if (TextUtils.isEmpty(number)) {
- return false;
- }
-
- Call.LogState logState = call.getLogState();
- if (!logState.isIncoming) {
- return false;
- }
-
- if (logState.duration <= 0) {
- return false;
- }
-
- if (logState.contactLookupResult != Call.LogState.LOOKUP_NOT_FOUND
- && logState.contactLookupResult != Call.LogState.LOOKUP_UNKNOWN) {
- return false;
- }
-
- int callHistoryStatus = call.getCallHistoryStatus();
- if (callHistoryStatus == Call.CALL_HISTORY_STATUS_PRESENT) {
- return false;
- } else if (callHistoryStatus == Call.CALL_HISTORY_STATUS_UNKNOWN) {
- Log.i(TAG, "Call history status is unknown, returning false");
- return false;
- }
-
- // Check if call disconnected because of either user hanging up
- int disconnectCause = call.getDisconnectCause().getCode();
- if (disconnectCause != DisconnectCause.LOCAL && disconnectCause != DisconnectCause.REMOTE) {
- return false;
- }
-
- Log.i(TAG, "shouldShowAfterCallNotification, returning true");
- return true;
- }
-} \ No newline at end of file
diff --git a/InCallUI/src/com/android/incallui/util/AccessibilityUtil.java b/InCallUI/src/com/android/incallui/util/AccessibilityUtil.java
deleted file mode 100644
index 1fdd2bac6..000000000
--- a/InCallUI/src/com/android/incallui/util/AccessibilityUtil.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2013 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.incallui.util;
-
-import android.content.Context;
-import android.view.accessibility.AccessibilityManager;
-
-public class AccessibilityUtil {
- public static boolean isTalkBackEnabled(Context context) {
- AccessibilityManager accessibilityManager = (AccessibilityManager) context
- .getSystemService(Context.ACCESSIBILITY_SERVICE);
- return accessibilityManager != null
- && accessibilityManager.isEnabled()
- && accessibilityManager.isTouchExplorationEnabled();
- }
-}
diff --git a/InCallUI/src/com/android/incallui/util/TelecomCallUtil.java b/InCallUI/src/com/android/incallui/util/TelecomCallUtil.java
deleted file mode 100644
index 53ecc29e9..000000000
--- a/InCallUI/src/com/android/incallui/util/TelecomCallUtil.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.incallui.util;
-
-import android.net.Uri;
-import android.telecom.Call;
-import android.telephony.PhoneNumberUtils;
-
-/**
- * Class to provide a standard interface for obtaining information from the underlying
- * android.telecom.Call. Much of this should be obtained through the incall.Call, but
- * on occasion we need to interact with the telecom.Call directly (eg. call blocking,
- * before the incall.Call has been created).
- */
-public class TelecomCallUtil {
-
- // Whether the call handle is an emergency number.
- public static boolean isEmergencyCall(Call call) {
- Uri handle = call.getDetails().getHandle();
- return PhoneNumberUtils.isEmergencyNumber(
- handle == null ? "" : handle.getSchemeSpecificPart());
- }
-
- public static String getNumber(Call call) {
- if (call == null) {
- return null;
- }
- if (call.getDetails().getGatewayInfo() != null) {
- return call.getDetails().getGatewayInfo()
- .getOriginalAddress().getSchemeSpecificPart();
- }
- Uri handle = getHandle(call);
- return handle == null ? null : handle.getSchemeSpecificPart();
- }
-
- public static Uri getHandle(Call call) {
- return call == null ? null : call.getDetails().getHandle();
- }
-}
diff --git a/InCallUI/src/com/android/incallui/widget/multiwaveview/Ease.java b/InCallUI/src/com/android/incallui/widget/multiwaveview/Ease.java
deleted file mode 100644
index 5ef689771..000000000
--- a/InCallUI/src/com/android/incallui/widget/multiwaveview/Ease.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.incallui.widget.multiwaveview;
-
-import android.animation.TimeInterpolator;
-
-class Ease {
- private static final float DOMAIN = 1.0f;
- private static final float DURATION = 1.0f;
- private static final float START = 0.0f;
-
- static class Linear {
- public static final TimeInterpolator easeNone = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return input;
- }
- };
- }
-
- static class Cubic {
- public static final TimeInterpolator easeIn = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN*(input/=DURATION)*input*input + START;
- }
- };
- public static final TimeInterpolator easeOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN*((input=input/DURATION-1)*input*input + 1) + START;
- }
- };
- public static final TimeInterpolator easeInOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return ((input/=DURATION/2) < 1.0f) ?
- (DOMAIN/2*input*input*input + START)
- : (DOMAIN/2*((input-=2)*input*input + 2) + START);
- }
- };
- }
-
- static class Quad {
- public static final TimeInterpolator easeIn = new TimeInterpolator() {
- public float getInterpolation (float input) {
- return DOMAIN*(input/=DURATION)*input + START;
- }
- };
- public static final TimeInterpolator easeOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return -DOMAIN *(input/=DURATION)*(input-2) + START;
- }
- };
- public static final TimeInterpolator easeInOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return ((input/=DURATION/2) < 1) ?
- (DOMAIN/2*input*input + START)
- : (-DOMAIN/2 * ((--input)*(input-2) - 1) + START);
- }
- };
- }
-
- static class Quart {
- public static final TimeInterpolator easeIn = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN*(input/=DURATION)*input*input*input + START;
- }
- };
- public static final TimeInterpolator easeOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return -DOMAIN * ((input=input/DURATION-1)*input*input*input - 1) + START;
- }
- };
- public static final TimeInterpolator easeInOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return ((input/=DURATION/2) < 1) ?
- (DOMAIN/2*input*input*input*input + START)
- : (-DOMAIN/2 * ((input-=2)*input*input*input - 2) + START);
- }
- };
- }
-
- static class Quint {
- public static final TimeInterpolator easeIn = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN*(input/=DURATION)*input*input*input*input + START;
- }
- };
- public static final TimeInterpolator easeOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN*((input=input/DURATION-1)*input*input*input*input + 1) + START;
- }
- };
- public static final TimeInterpolator easeInOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return ((input/=DURATION/2) < 1) ?
- (DOMAIN/2*input*input*input*input*input + START)
- : (DOMAIN/2*((input-=2)*input*input*input*input + 2) + START);
- }
- };
- }
-
- static class Sine {
- public static final TimeInterpolator easeIn = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return -DOMAIN * (float) Math.cos(input/DURATION * (Math.PI/2)) + DOMAIN + START;
- }
- };
- public static final TimeInterpolator easeOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN * (float) Math.sin(input/DURATION * (Math.PI/2)) + START;
- }
- };
- public static final TimeInterpolator easeInOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return -DOMAIN/2 * ((float)Math.cos(Math.PI*input/DURATION) - 1.0f) + START;
- }
- };
- }
-
-}
diff --git a/InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java b/InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java
deleted file mode 100644
index efeb4b7e3..000000000
--- a/InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java
+++ /dev/null
@@ -1,1473 +0,0 @@
-/*
- * 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.incallui.widget.multiwaveview;
-
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Vibrator;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.widget.ExploreByTouchHelper;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.view.accessibility.AccessibilityNodeProvider;
-
-import com.android.dialer.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This is a copy of com.android.internal.widget.multiwaveview.GlowPadView with minor changes
- * to remove dependencies on private api's.
- *
- * Incoporated the scaling functionality.
- *
- * A re-usable widget containing a center, outer ring and wave animation.
- */
-public class GlowPadView extends View {
- private static final String TAG = "GlowPadView";
- private static final boolean DEBUG = false;
-
- // Wave state machine
- private static final int STATE_IDLE = 0;
- private static final int STATE_START = 1;
- private static final int STATE_FIRST_TOUCH = 2;
- private static final int STATE_TRACKING = 3;
- private static final int STATE_SNAP = 4;
- private static final int STATE_FINISH = 5;
-
- // Animation properties.
- private static final float SNAP_MARGIN_DEFAULT = 20.0f; // distance to ring before we snap to it
-
- public interface OnTriggerListener {
- int NO_HANDLE = 0;
- int CENTER_HANDLE = 1;
- public void onGrabbed(View v, int handle);
- public void onReleased(View v, int handle);
- public void onTrigger(View v, int target);
- public void onGrabbedStateChange(View v, int handle);
- public void onFinishFinalAnimation();
- }
-
- // Tuneable parameters for animation
- private static final int WAVE_ANIMATION_DURATION = 1350;
- private static final int RETURN_TO_HOME_DELAY = 1200;
- private static final int RETURN_TO_HOME_DURATION = 200;
- private static final int HIDE_ANIMATION_DELAY = 200;
- private static final int HIDE_ANIMATION_DURATION = 200;
- private static final int SHOW_ANIMATION_DURATION = 200;
- private static final int SHOW_ANIMATION_DELAY = 50;
- private static final int INITIAL_SHOW_HANDLE_DURATION = 200;
- private static final int REVEAL_GLOW_DELAY = 0;
- private static final int REVEAL_GLOW_DURATION = 0;
-
- private static final float TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.3f;
- private static final float TARGET_SCALE_EXPANDED = 1.0f;
- private static final float TARGET_SCALE_COLLAPSED = 0.8f;
- private static final float RING_SCALE_EXPANDED = 1.0f;
- private static final float RING_SCALE_COLLAPSED = 0.5f;
-
- private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>();
- private AnimationBundle mWaveAnimations = new AnimationBundle();
- private AnimationBundle mTargetAnimations = new AnimationBundle();
- private AnimationBundle mGlowAnimations = new AnimationBundle();
- private ArrayList<String> mTargetDescriptions;
- private ArrayList<String> mDirectionDescriptions;
- private OnTriggerListener mOnTriggerListener;
- private TargetDrawable mHandleDrawable;
- private TargetDrawable mOuterRing;
- private Vibrator mVibrator;
-
- private int mFeedbackCount = 3;
- private int mVibrationDuration = 0;
- private int mGrabbedState;
- private int mActiveTarget = -1;
- private float mGlowRadius;
- private float mWaveCenterX;
- private float mWaveCenterY;
- private int mMaxTargetHeight;
- private int mMaxTargetWidth;
- private float mRingScaleFactor = 1f;
- private boolean mAllowScaling;
-
- private float mOuterRadius = 0.0f;
- private float mSnapMargin = 0.0f;
- private boolean mDragging;
- private int mNewTargetResources;
-
- private AccessibilityNodeProvider mAccessibilityNodeProvider;
- private GlowpadExploreByTouchHelper mExploreByTouchHelper;
-
- private class AnimationBundle extends ArrayList<Tweener> {
- private static final long serialVersionUID = 0xA84D78726F127468L;
- private boolean mSuspended;
-
- public void start() {
- if (mSuspended) return; // ignore attempts to start animations
- final int count = size();
- for (int i = 0; i < count; i++) {
- Tweener anim = get(i);
- anim.animator.start();
- }
- }
-
- public void cancel() {
- final int count = size();
- for (int i = 0; i < count; i++) {
- Tweener anim = get(i);
- anim.animator.cancel();
- }
- clear();
- }
-
- public void stop() {
- final int count = size();
- for (int i = 0; i < count; i++) {
- Tweener anim = get(i);
- anim.animator.end();
- }
- clear();
- }
-
- public void setSuspended(boolean suspend) {
- mSuspended = suspend;
- }
- };
-
- private AnimatorListener mResetListener = new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
- dispatchOnFinishFinalAnimation();
- }
- };
-
- private AnimatorListener mResetListenerWithPing = new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- ping();
- switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
- dispatchOnFinishFinalAnimation();
- }
- };
-
- private AnimatorUpdateListener mUpdateListener = new AnimatorUpdateListener() {
- public void onAnimationUpdate(ValueAnimator animation) {
- invalidate();
- }
- };
-
- private boolean mAnimatingTargets;
- private AnimatorListener mTargetUpdateListener = new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- if (mNewTargetResources != 0) {
- internalSetTargetResources(mNewTargetResources);
- mNewTargetResources = 0;
- hideTargets(false, false);
- }
- mAnimatingTargets = false;
- }
- };
- private int mTargetResourceId;
- private int mTargetDescriptionsResourceId;
- private int mDirectionDescriptionsResourceId;
- private boolean mAlwaysTrackFinger;
- private int mHorizontalInset;
- private int mVerticalInset;
- private int mGravity = Gravity.TOP;
- private boolean mInitialLayout = true;
- private Tweener mBackgroundAnimator;
- private PointCloud mPointCloud;
- private float mInnerRadius;
- private int mPointerId;
-
- public GlowPadView(Context context) {
- this(context, null);
- }
-
- public GlowPadView(Context context, AttributeSet attrs) {
- super(context, attrs);
- Resources res = context.getResources();
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GlowPadView);
- mInnerRadius = a.getDimension(R.styleable.GlowPadView_innerRadius, mInnerRadius);
- mOuterRadius = a.getDimension(R.styleable.GlowPadView_outerRadius, mOuterRadius);
- mSnapMargin = a.getDimension(R.styleable.GlowPadView_snapMargin, mSnapMargin);
- mVibrationDuration = a.getInt(R.styleable.GlowPadView_vibrationDuration,
- mVibrationDuration);
- mFeedbackCount = a.getInt(R.styleable.GlowPadView_feedbackCount,
- mFeedbackCount);
- mAllowScaling = a.getBoolean(R.styleable.GlowPadView_allowScaling, false);
- TypedValue handle = a.peekValue(R.styleable.GlowPadView_handleDrawable);
- setHandleDrawable(handle != null ? handle.resourceId : R.drawable.ic_incall_audio_handle);
- mOuterRing = new TargetDrawable(res,
- getResourceId(a, R.styleable.GlowPadView_outerRingDrawable), 1);
-
- mAlwaysTrackFinger = a.getBoolean(R.styleable.GlowPadView_alwaysTrackFinger, false);
-
- int pointId = getResourceId(a, R.styleable.GlowPadView_pointDrawable);
- Drawable pointDrawable = pointId != 0 ? res.getDrawable(pointId) : null;
- mGlowRadius = a.getDimension(R.styleable.GlowPadView_glowRadius, 0.0f);
-
- TypedValue outValue = new TypedValue();
-
- // Read array of target drawables
- if (a.getValue(R.styleable.GlowPadView_targetDrawables, outValue)) {
- internalSetTargetResources(outValue.resourceId);
- }
- if (mTargetDrawables == null || mTargetDrawables.size() == 0) {
- throw new IllegalStateException("Must specify at least one target drawable");
- }
-
- // Read array of target descriptions
- if (a.getValue(R.styleable.GlowPadView_targetDescriptions, outValue)) {
- final int resourceId = outValue.resourceId;
- if (resourceId == 0) {
- throw new IllegalStateException("Must specify target descriptions");
- }
- setTargetDescriptionsResourceId(resourceId);
- }
-
- // Read array of direction descriptions
- if (a.getValue(R.styleable.GlowPadView_directionDescriptions, outValue)) {
- final int resourceId = outValue.resourceId;
- if (resourceId == 0) {
- throw new IllegalStateException("Must specify direction descriptions");
- }
- setDirectionDescriptionsResourceId(resourceId);
- }
-
- // Use gravity attribute from LinearLayout
- //a = context.obtainStyledAttributes(attrs, R.styleable.LinearLayout);
- mGravity = a.getInt(R.styleable.GlowPadView_android_gravity, Gravity.TOP);
- a.recycle();
-
-
- setVibrateEnabled(mVibrationDuration > 0);
-
- assignDefaultsIfNeeded();
-
- mPointCloud = new PointCloud(pointDrawable);
- mPointCloud.makePointCloud(mInnerRadius, mOuterRadius);
- mPointCloud.glowManager.setRadius(mGlowRadius);
-
- mExploreByTouchHelper = new GlowpadExploreByTouchHelper(this);
- ViewCompat.setAccessibilityDelegate(this, mExploreByTouchHelper);
- }
-
- private int getResourceId(TypedArray a, int id) {
- TypedValue tv = a.peekValue(id);
- return tv == null ? 0 : tv.resourceId;
- }
-
- private void dump() {
- Log.v(TAG, "Outer Radius = " + mOuterRadius);
- Log.v(TAG, "SnapMargin = " + mSnapMargin);
- Log.v(TAG, "FeedbackCount = " + mFeedbackCount);
- Log.v(TAG, "VibrationDuration = " + mVibrationDuration);
- Log.v(TAG, "GlowRadius = " + mGlowRadius);
- Log.v(TAG, "WaveCenterX = " + mWaveCenterX);
- Log.v(TAG, "WaveCenterY = " + mWaveCenterY);
- }
-
- public void suspendAnimations() {
- mWaveAnimations.setSuspended(true);
- mTargetAnimations.setSuspended(true);
- mGlowAnimations.setSuspended(true);
- }
-
- public void resumeAnimations() {
- mWaveAnimations.setSuspended(false);
- mTargetAnimations.setSuspended(false);
- mGlowAnimations.setSuspended(false);
- mWaveAnimations.start();
- mTargetAnimations.start();
- mGlowAnimations.start();
- }
-
- @Override
- protected int getSuggestedMinimumWidth() {
- // View should be large enough to contain the background + handle and
- // target drawable on either edge.
- return (int) (Math.max(mOuterRing.getWidth(), 2 * mOuterRadius) + mMaxTargetWidth);
- }
-
- @Override
- protected int getSuggestedMinimumHeight() {
- // View should be large enough to contain the unlock ring + target and
- // target drawable on either edge
- return (int) (Math.max(mOuterRing.getHeight(), 2 * mOuterRadius) + mMaxTargetHeight);
- }
-
- /**
- * This gets the suggested width accounting for the ring's scale factor.
- */
- protected int getScaledSuggestedMinimumWidth() {
- return (int) (mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius)
- + mMaxTargetWidth);
- }
-
- /**
- * This gets the suggested height accounting for the ring's scale factor.
- */
- protected int getScaledSuggestedMinimumHeight() {
- return (int) (mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius)
- + mMaxTargetHeight);
- }
-
- private int resolveMeasured(int measureSpec, int desired)
- {
- int result = 0;
- int specSize = MeasureSpec.getSize(measureSpec);
- switch (MeasureSpec.getMode(measureSpec)) {
- case MeasureSpec.UNSPECIFIED:
- result = desired;
- break;
- case MeasureSpec.AT_MOST:
- result = Math.min(specSize, desired);
- break;
- case MeasureSpec.EXACTLY:
- default:
- result = specSize;
- }
- return result;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int minimumWidth = getSuggestedMinimumWidth();
- final int minimumHeight = getSuggestedMinimumHeight();
- int computedWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
- int computedHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
-
- mRingScaleFactor = computeScaleFactor(minimumWidth, minimumHeight,
- computedWidth, computedHeight);
-
- int scaledWidth = getScaledSuggestedMinimumWidth();
- int scaledHeight = getScaledSuggestedMinimumHeight();
-
- computeInsets(computedWidth - scaledWidth, computedHeight - scaledHeight);
- setMeasuredDimension(computedWidth, computedHeight);
- }
-
- private void switchToState(int state, float x, float y) {
- switch (state) {
- case STATE_IDLE:
- deactivateTargets();
- hideGlow(0, 0, 0.0f, null);
- startBackgroundAnimation(0, 0.0f);
- mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
- mHandleDrawable.setAlpha(1.0f);
- break;
-
- case STATE_START:
- startBackgroundAnimation(0, 0.0f);
- break;
-
- case STATE_FIRST_TOUCH:
- mHandleDrawable.setAlpha(0.0f);
- deactivateTargets();
- showTargets(true);
- startBackgroundAnimation(INITIAL_SHOW_HANDLE_DURATION, 1.0f);
- setGrabbedState(OnTriggerListener.CENTER_HANDLE);
-
- final AccessibilityManager accessibilityManager =
- (AccessibilityManager) getContext().getSystemService(
- Context.ACCESSIBILITY_SERVICE);
- if (accessibilityManager.isEnabled()) {
- announceTargets();
- }
- break;
-
- case STATE_TRACKING:
- mHandleDrawable.setAlpha(0.0f);
- break;
-
- case STATE_SNAP:
- // TODO: Add transition states (see list_selector_background_transition.xml)
- mHandleDrawable.setAlpha(0.0f);
- showGlow(REVEAL_GLOW_DURATION , REVEAL_GLOW_DELAY, 0.0f, null);
- break;
-
- case STATE_FINISH:
- doFinish();
- break;
- }
- }
-
- private void showGlow(int duration, int delay, float finalAlpha,
- AnimatorListener finishListener) {
- mGlowAnimations.cancel();
- mGlowAnimations.add(Tweener.to(mPointCloud.glowManager, duration,
- "ease", Ease.Cubic.easeIn,
- "delay", delay,
- "alpha", finalAlpha,
- "onUpdate", mUpdateListener,
- "onComplete", finishListener));
- mGlowAnimations.start();
- }
-
- private void hideGlow(int duration, int delay, float finalAlpha,
- AnimatorListener finishListener) {
- mGlowAnimations.cancel();
- mGlowAnimations.add(Tweener.to(mPointCloud.glowManager, duration,
- "ease", Ease.Quart.easeOut,
- "delay", delay,
- "alpha", finalAlpha,
- "x", 0.0f,
- "y", 0.0f,
- "onUpdate", mUpdateListener,
- "onComplete", finishListener));
- mGlowAnimations.start();
- }
-
- private void deactivateTargets() {
- final int count = mTargetDrawables.size();
- for (int i = 0; i < count; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- target.setState(TargetDrawable.STATE_INACTIVE);
- }
- mActiveTarget = -1;
- }
-
- /**
- * Dispatches a trigger event to listener. Ignored if a listener is not set.
- * @param whichTarget the target that was triggered.
- */
- private void dispatchTriggerEvent(int whichTarget) {
- vibrate();
- if (mOnTriggerListener != null) {
- mOnTriggerListener.onTrigger(this, whichTarget);
- }
- }
-
- private void dispatchOnFinishFinalAnimation() {
- if (mOnTriggerListener != null) {
- mOnTriggerListener.onFinishFinalAnimation();
- }
- }
-
- private void doFinish() {
- final int activeTarget = mActiveTarget;
- final boolean targetHit = activeTarget != -1;
-
- if (targetHit) {
- if (DEBUG) Log.v(TAG, "Finish with target hit = " + targetHit);
-
- highlightSelected(activeTarget);
-
- // Inform listener of any active targets. Typically only one will be active.
- hideGlow(RETURN_TO_HOME_DURATION, RETURN_TO_HOME_DELAY, 0.0f, mResetListener);
- dispatchTriggerEvent(activeTarget);
- if (!mAlwaysTrackFinger) {
- // Force ring and targets to finish animation to final expanded state
- mTargetAnimations.stop();
- }
- } else {
- // Animate handle back to the center based on current state.
- hideGlow(HIDE_ANIMATION_DURATION, 0, 0.0f, mResetListenerWithPing);
- hideTargets(true, false);
- }
-
- setGrabbedState(OnTriggerListener.NO_HANDLE);
- }
-
- private void highlightSelected(int activeTarget) {
- // Highlight the given target and fade others
- mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE);
- hideUnselected(activeTarget);
- }
-
- private void hideUnselected(int active) {
- for (int i = 0; i < mTargetDrawables.size(); i++) {
- if (i != active) {
- mTargetDrawables.get(i).setAlpha(0.0f);
- }
- }
- }
-
- private void hideTargets(boolean animate, boolean expanded) {
- mTargetAnimations.cancel();
- // Note: these animations should complete at the same time so that we can swap out
- // the target assets asynchronously from the setTargetResources() call.
- mAnimatingTargets = animate;
- final int duration = animate ? HIDE_ANIMATION_DURATION : 0;
- final int delay = animate ? HIDE_ANIMATION_DELAY : 0;
-
- final float targetScale = expanded ?
- TARGET_SCALE_EXPANDED : TARGET_SCALE_COLLAPSED;
- final int length = mTargetDrawables.size();
- final TimeInterpolator interpolator = Ease.Cubic.easeOut;
- for (int i = 0; i < length; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- target.setState(TargetDrawable.STATE_INACTIVE);
- mTargetAnimations.add(Tweener.to(target, duration,
- "ease", interpolator,
- "alpha", 0.0f,
- "scaleX", targetScale,
- "scaleY", targetScale,
- "delay", delay,
- "onUpdate", mUpdateListener));
- }
-
- float ringScaleTarget = expanded ?
- RING_SCALE_EXPANDED : RING_SCALE_COLLAPSED;
- ringScaleTarget *= mRingScaleFactor;
- mTargetAnimations.add(Tweener.to(mOuterRing, duration,
- "ease", interpolator,
- "alpha", 0.0f,
- "scaleX", ringScaleTarget,
- "scaleY", ringScaleTarget,
- "delay", delay,
- "onUpdate", mUpdateListener,
- "onComplete", mTargetUpdateListener));
-
- mTargetAnimations.start();
- }
-
- private void showTargets(boolean animate) {
- mTargetAnimations.stop();
- mAnimatingTargets = animate;
- final int delay = animate ? SHOW_ANIMATION_DELAY : 0;
- final int duration = animate ? SHOW_ANIMATION_DURATION : 0;
- final int length = mTargetDrawables.size();
- for (int i = 0; i < length; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- target.setState(TargetDrawable.STATE_INACTIVE);
- mTargetAnimations.add(Tweener.to(target, duration,
- "ease", Ease.Cubic.easeOut,
- "alpha", 1.0f,
- "scaleX", 1.0f,
- "scaleY", 1.0f,
- "delay", delay,
- "onUpdate", mUpdateListener));
- }
- float ringScale = mRingScaleFactor * RING_SCALE_EXPANDED;
- mTargetAnimations.add(Tweener.to(mOuterRing, duration,
- "ease", Ease.Cubic.easeOut,
- "alpha", 1.0f,
- "scaleX", ringScale,
- "scaleY", ringScale,
- "delay", delay,
- "onUpdate", mUpdateListener,
- "onComplete", mTargetUpdateListener));
-
- mTargetAnimations.start();
- }
-
- private void vibrate() {
- if (mVibrator != null) {
- mVibrator.vibrate(mVibrationDuration);
- }
- }
-
- private ArrayList<TargetDrawable> loadDrawableArray(int resourceId) {
- Resources res = getContext().getResources();
- TypedArray array = res.obtainTypedArray(resourceId);
- final int count = array.length();
- ArrayList<TargetDrawable> drawables = new ArrayList<TargetDrawable>(count);
- for (int i = 0; i < count; i++) {
- TypedValue value = array.peekValue(i);
- TargetDrawable target = new TargetDrawable(res, value != null ? value.resourceId : 0, 3);
- drawables.add(target);
- }
- array.recycle();
- return drawables;
- }
-
- private void internalSetTargetResources(int resourceId) {
- final ArrayList<TargetDrawable> targets = loadDrawableArray(resourceId);
- mTargetDrawables = targets;
- mTargetResourceId = resourceId;
-
- int maxWidth = mHandleDrawable.getWidth();
- int maxHeight = mHandleDrawable.getHeight();
- final int count = targets.size();
- for (int i = 0; i < count; i++) {
- TargetDrawable target = targets.get(i);
- maxWidth = Math.max(maxWidth, target.getWidth());
- maxHeight = Math.max(maxHeight, target.getHeight());
- }
- if (mMaxTargetWidth != maxWidth || mMaxTargetHeight != maxHeight) {
- mMaxTargetWidth = maxWidth;
- mMaxTargetHeight = maxHeight;
- requestLayout(); // required to resize layout and call updateTargetPositions()
- } else {
- updateTargetPositions(mWaveCenterX, mWaveCenterY);
- updatePointCloudPosition(mWaveCenterX, mWaveCenterY);
- }
- }
- /**
- * Loads an array of drawables from the given resourceId.
- *
- * @param resourceId
- */
- public void setTargetResources(int resourceId) {
- if (mAnimatingTargets) {
- // postpone this change until we return to the initial state
- mNewTargetResources = resourceId;
- } else {
- internalSetTargetResources(resourceId);
- }
- }
-
- public int getTargetResourceId() {
- return mTargetResourceId;
- }
-
- /**
- * Sets the handle drawable to the drawable specified by the resource ID.
- * @param resourceId
- */
- public void setHandleDrawable(int resourceId) {
- if (mHandleDrawable != null) {
- mHandleDrawable.setDrawable(getResources(), resourceId);
- } else {
- mHandleDrawable = new TargetDrawable(getResources(), resourceId, 1);
- }
- mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
- }
-
- /**
- * Sets the resource id specifying the target descriptions for accessibility.
- *
- * @param resourceId The resource id.
- */
- public void setTargetDescriptionsResourceId(int resourceId) {
- mTargetDescriptionsResourceId = resourceId;
- if (mTargetDescriptions != null) {
- mTargetDescriptions.clear();
- }
- }
-
- /**
- * Gets the resource id specifying the target descriptions for accessibility.
- *
- * @return The resource id.
- */
- public int getTargetDescriptionsResourceId() {
- return mTargetDescriptionsResourceId;
- }
-
- /**
- * Sets the resource id specifying the target direction descriptions for accessibility.
- *
- * @param resourceId The resource id.
- */
- public void setDirectionDescriptionsResourceId(int resourceId) {
- mDirectionDescriptionsResourceId = resourceId;
- if (mDirectionDescriptions != null) {
- mDirectionDescriptions.clear();
- }
- }
-
- /**
- * Gets the resource id specifying the target direction descriptions.
- *
- * @return The resource id.
- */
- public int getDirectionDescriptionsResourceId() {
- return mDirectionDescriptionsResourceId;
- }
-
- /**
- * Enable or disable vibrate on touch.
- *
- * @param enabled
- */
- public void setVibrateEnabled(boolean enabled) {
- if (enabled && mVibrator == null) {
- mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
- } else {
- mVibrator = null;
- }
- }
-
- /**
- * Starts wave animation.
- *
- */
- public void ping() {
- if (mFeedbackCount > 0) {
- boolean doWaveAnimation = true;
- final AnimationBundle waveAnimations = mWaveAnimations;
-
- // Don't do a wave if there's already one in progress
- if (waveAnimations.size() > 0 && waveAnimations.get(0).animator.isRunning()) {
- long t = waveAnimations.get(0).animator.getCurrentPlayTime();
- if (t < WAVE_ANIMATION_DURATION/2) {
- doWaveAnimation = false;
- }
- }
-
- if (doWaveAnimation) {
- startWaveAnimation();
- }
- }
- }
-
- private void stopAndHideWaveAnimation() {
- mWaveAnimations.cancel();
- mPointCloud.waveManager.setAlpha(0.0f);
- }
-
- private void startWaveAnimation() {
- mWaveAnimations.cancel();
- mPointCloud.waveManager.setAlpha(1.0f);
- mPointCloud.waveManager.setRadius(mHandleDrawable.getWidth()/2.0f);
- mWaveAnimations.add(Tweener.to(mPointCloud.waveManager, WAVE_ANIMATION_DURATION,
- "ease", Ease.Quad.easeOut,
- "delay", 0,
- "radius", 2.0f * mOuterRadius,
- "onUpdate", mUpdateListener,
- "onComplete",
- new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- mPointCloud.waveManager.setRadius(0.0f);
- mPointCloud.waveManager.setAlpha(0.0f);
- }
- }));
- mWaveAnimations.start();
- }
-
- /**
- * Resets the widget to default state and cancels all animation. If animate is 'true', will
- * animate objects into place. Otherwise, objects will snap back to place.
- *
- * @param animate
- */
- public void reset(boolean animate) {
- mGlowAnimations.stop();
- mTargetAnimations.stop();
- startBackgroundAnimation(0, 0.0f);
- stopAndHideWaveAnimation();
- hideTargets(animate, false);
- hideGlow(0, 0, 0.0f, null);
- Tweener.reset();
- }
-
- private void startBackgroundAnimation(int duration, float alpha) {
- final Drawable background = getBackground();
- if (mAlwaysTrackFinger && background != null) {
- if (mBackgroundAnimator != null) {
- mBackgroundAnimator.animator.cancel();
- }
- mBackgroundAnimator = Tweener.to(background, duration,
- "ease", Ease.Cubic.easeIn,
- "alpha", (int)(255.0f * alpha),
- "delay", SHOW_ANIMATION_DELAY);
- mBackgroundAnimator.animator.start();
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- final int action = event.getActionMasked();
- boolean handled = false;
- switch (action) {
- case MotionEvent.ACTION_POINTER_DOWN:
- case MotionEvent.ACTION_DOWN:
- if (DEBUG) Log.v(TAG, "*** DOWN ***");
- handleDown(event);
- handleMove(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (DEBUG) Log.v(TAG, "*** MOVE ***");
- handleMove(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- case MotionEvent.ACTION_UP:
- if (DEBUG) Log.v(TAG, "*** UP ***");
- handleMove(event);
- handleUp(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_CANCEL:
- if (DEBUG) Log.v(TAG, "*** CANCEL ***");
- handleMove(event);
- handleCancel(event);
- handled = true;
- break;
- }
- invalidate();
- return handled ? true : super.onTouchEvent(event);
- }
-
- private void updateGlowPosition(float x, float y) {
- float dx = x - mOuterRing.getX();
- float dy = y - mOuterRing.getY();
- dx *= 1f / mRingScaleFactor;
- dy *= 1f / mRingScaleFactor;
- mPointCloud.glowManager.setX(mOuterRing.getX() + dx);
- mPointCloud.glowManager.setY(mOuterRing.getY() + dy);
- }
-
- private void handleDown(MotionEvent event) {
- int actionIndex = event.getActionIndex();
- float eventX = event.getX(actionIndex);
- float eventY = event.getY(actionIndex);
- switchToState(STATE_START, eventX, eventY);
- if (!trySwitchToFirstTouchState(eventX, eventY)) {
- mDragging = false;
- } else {
- mPointerId = event.getPointerId(actionIndex);
- updateGlowPosition(eventX, eventY);
- }
- }
-
- private void handleUp(MotionEvent event) {
- if (DEBUG && mDragging) Log.v(TAG, "** Handle RELEASE");
- int actionIndex = event.getActionIndex();
- if (event.getPointerId(actionIndex) == mPointerId) {
- switchToState(STATE_FINISH, event.getX(actionIndex), event.getY(actionIndex));
- }
- }
-
- private void handleCancel(MotionEvent event) {
- if (DEBUG && mDragging) Log.v(TAG, "** Handle CANCEL");
-
- // We should drop the active target here but it interferes with
- // moving off the screen in the direction of the navigation bar. At some point we may
- // want to revisit how we handle this. For now we'll allow a canceled event to
- // activate the current target.
-
- // mActiveTarget = -1; // Drop the active target if canceled.
-
- int actionIndex = event.findPointerIndex(mPointerId);
- actionIndex = actionIndex == -1 ? 0 : actionIndex;
- switchToState(STATE_FINISH, event.getX(actionIndex), event.getY(actionIndex));
- }
-
- private void handleMove(MotionEvent event) {
- int activeTarget = -1;
- final int historySize = event.getHistorySize();
- ArrayList<TargetDrawable> targets = mTargetDrawables;
- int ntargets = targets.size();
- float x = 0.0f;
- float y = 0.0f;
- int actionIndex = event.findPointerIndex(mPointerId);
-
- if (actionIndex == -1) {
- return; // no data for this pointer
- }
-
- for (int k = 0; k < historySize + 1; k++) {
- float eventX = k < historySize ? event.getHistoricalX(actionIndex, k)
- : event.getX(actionIndex);
- float eventY = k < historySize ? event.getHistoricalY(actionIndex, k)
- :event.getY(actionIndex);
- // tx and ty are relative to wave center
- float tx = eventX - mWaveCenterX;
- float ty = eventY - mWaveCenterY;
- float touchRadius = (float) Math.hypot(tx, ty);
- final float scale = touchRadius > mOuterRadius ? mOuterRadius / touchRadius : 1.0f;
- float limitX = tx * scale;
- float limitY = ty * scale;
- double angleRad = Math.atan2(-ty, tx);
-
- if (!mDragging) {
- trySwitchToFirstTouchState(eventX, eventY);
- }
-
- if (mDragging) {
- // For multiple targets, snap to the one that matches
- final float snapRadius = mRingScaleFactor * mOuterRadius - mSnapMargin;
- final float snapDistance2 = snapRadius * snapRadius;
- // Find first target in range
- for (int i = 0; i < ntargets; i++) {
- TargetDrawable target = targets.get(i);
-
- double targetMinRad = (i - 0.5) * 2 * Math.PI / ntargets;
- double targetMaxRad = (i + 0.5) * 2 * Math.PI / ntargets;
- if (target.isEnabled()) {
- boolean angleMatches =
- (angleRad > targetMinRad && angleRad <= targetMaxRad) ||
- (angleRad + 2 * Math.PI > targetMinRad &&
- angleRad + 2 * Math.PI <= targetMaxRad);
- if (angleMatches && (dist2(tx, ty) > snapDistance2)) {
- activeTarget = i;
- }
- }
- }
- }
- x = limitX;
- y = limitY;
- }
-
- if (!mDragging) {
- return;
- }
-
- if (activeTarget != -1) {
- switchToState(STATE_SNAP, x,y);
- updateGlowPosition(x, y);
- } else {
- switchToState(STATE_TRACKING, x, y);
- updateGlowPosition(x, y);
- }
-
- if (mActiveTarget != activeTarget) {
- // Defocus the old target
- if (mActiveTarget != -1) {
- TargetDrawable target = targets.get(mActiveTarget);
- target.setState(TargetDrawable.STATE_INACTIVE);
- }
- // Focus the new target
- if (activeTarget != -1) {
- TargetDrawable target = targets.get(activeTarget);
- target.setState(TargetDrawable.STATE_FOCUSED);
- final AccessibilityManager accessibilityManager =
- (AccessibilityManager) getContext().getSystemService(
- Context.ACCESSIBILITY_SERVICE);
- if (accessibilityManager.isEnabled()) {
- String targetContentDescription = getTargetDescription(activeTarget);
- announceForAccessibility(targetContentDescription);
- }
- }
- }
- mActiveTarget = activeTarget;
- }
-
- @Override
- public boolean onHoverEvent(MotionEvent event) {
- final AccessibilityManager accessibilityManager =
- (AccessibilityManager) getContext().getSystemService(
- Context.ACCESSIBILITY_SERVICE);
- if (accessibilityManager.isTouchExplorationEnabled()) {
- final int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_HOVER_ENTER:
- event.setAction(MotionEvent.ACTION_DOWN);
- break;
- case MotionEvent.ACTION_HOVER_MOVE:
- event.setAction(MotionEvent.ACTION_MOVE);
- break;
- case MotionEvent.ACTION_HOVER_EXIT:
- event.setAction(MotionEvent.ACTION_UP);
- break;
- }
- onTouchEvent(event);
- event.setAction(action);
- }
- super.onHoverEvent(event);
- return true;
- }
-
- /**
- * Sets the current grabbed state, and dispatches a grabbed state change
- * event to our listener.
- */
- private void setGrabbedState(int newState) {
- if (newState != mGrabbedState) {
- if (newState != OnTriggerListener.NO_HANDLE) {
- vibrate();
- }
- mGrabbedState = newState;
- if (mOnTriggerListener != null) {
- if (newState == OnTriggerListener.NO_HANDLE) {
- mOnTriggerListener.onReleased(this, OnTriggerListener.CENTER_HANDLE);
- } else {
- mOnTriggerListener.onGrabbed(this, OnTriggerListener.CENTER_HANDLE);
- }
- mOnTriggerListener.onGrabbedStateChange(this, newState);
- }
- }
- }
-
- private boolean trySwitchToFirstTouchState(float x, float y) {
- final float tx = x - mWaveCenterX;
- final float ty = y - mWaveCenterY;
- if (mAlwaysTrackFinger || dist2(tx,ty) <= getScaledGlowRadiusSquared()) {
- if (DEBUG) Log.v(TAG, "** Handle HIT");
- switchToState(STATE_FIRST_TOUCH, x, y);
- updateGlowPosition(tx, ty);
- mDragging = true;
- return true;
- }
- return false;
- }
-
- private void assignDefaultsIfNeeded() {
- if (mOuterRadius == 0.0f) {
- mOuterRadius = Math.max(mOuterRing.getWidth(), mOuterRing.getHeight())/2.0f;
- }
- if (mSnapMargin == 0.0f) {
- mSnapMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- SNAP_MARGIN_DEFAULT, getContext().getResources().getDisplayMetrics());
- }
- if (mInnerRadius == 0.0f) {
- mInnerRadius = mHandleDrawable.getWidth() / 10.0f;
- }
- }
-
- private void computeInsets(int dx, int dy) {
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
-
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
- mHorizontalInset = 0;
- break;
- case Gravity.RIGHT:
- mHorizontalInset = dx;
- break;
- case Gravity.CENTER_HORIZONTAL:
- default:
- mHorizontalInset = dx / 2;
- break;
- }
- switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
- case Gravity.TOP:
- mVerticalInset = 0;
- break;
- case Gravity.BOTTOM:
- mVerticalInset = dy;
- break;
- case Gravity.CENTER_VERTICAL:
- default:
- mVerticalInset = dy / 2;
- break;
- }
- }
-
- /**
- * Given the desired width and height of the ring and the allocated width and height, compute
- * how much we need to scale the ring.
- */
- private float computeScaleFactor(int desiredWidth, int desiredHeight,
- int actualWidth, int actualHeight) {
-
- // Return unity if scaling is not allowed.
- if (!mAllowScaling) return 1f;
-
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
-
- float scaleX = 1f;
- float scaleY = 1f;
-
- // We use the gravity as a cue for whether we want to scale on a particular axis.
- // We only scale to fit horizontally if we're not pinned to the left or right. Likewise,
- // we only scale to fit vertically if we're not pinned to the top or bottom. In these
- // cases, we want the ring to hang off the side or top/bottom, respectively.
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
- case Gravity.RIGHT:
- break;
- case Gravity.CENTER_HORIZONTAL:
- default:
- if (desiredWidth > actualWidth) {
- scaleX = (1f * actualWidth - mMaxTargetWidth) /
- (desiredWidth - mMaxTargetWidth);
- }
- break;
- }
- switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
- case Gravity.TOP:
- case Gravity.BOTTOM:
- break;
- case Gravity.CENTER_VERTICAL:
- default:
- if (desiredHeight > actualHeight) {
- scaleY = (1f * actualHeight - mMaxTargetHeight) /
- (desiredHeight - mMaxTargetHeight);
- }
- break;
- }
- return Math.min(scaleX, scaleY);
- }
-
- private float getRingWidth() {
- return mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius);
- }
-
- private float getRingHeight() {
- return mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius);
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- final int width = right - left;
- final int height = bottom - top;
-
- // Target placement width/height. This puts the targets on the greater of the ring
- // width or the specified outer radius.
- final float placementWidth = getRingWidth();
- final float placementHeight = getRingHeight();
- float newWaveCenterX = mHorizontalInset
- + (mMaxTargetWidth + placementWidth) / 2;
- float newWaveCenterY = mVerticalInset
- + (mMaxTargetHeight + placementHeight) / 2;
-
- if (mInitialLayout) {
- stopAndHideWaveAnimation();
- hideTargets(false, false);
- mInitialLayout = false;
- }
-
- mOuterRing.setPositionX(newWaveCenterX);
- mOuterRing.setPositionY(newWaveCenterY);
-
- mPointCloud.setScale(mRingScaleFactor);
-
- mHandleDrawable.setPositionX(newWaveCenterX);
- mHandleDrawable.setPositionY(newWaveCenterY);
-
- updateTargetPositions(newWaveCenterX, newWaveCenterY);
- updatePointCloudPosition(newWaveCenterX, newWaveCenterY);
- updateGlowPosition(newWaveCenterX, newWaveCenterY);
-
- mWaveCenterX = newWaveCenterX;
- mWaveCenterY = newWaveCenterY;
-
- if (DEBUG) dump();
- }
-
- private void updateTargetPositions(float centerX, float centerY) {
- // Reposition the target drawables if the view changed.
- ArrayList<TargetDrawable> targets = mTargetDrawables;
- final int size = targets.size();
- final float alpha = (float) (-2.0f * Math.PI / size);
- for (int i = 0; i < size; i++) {
- final TargetDrawable targetIcon = targets.get(i);
- final float angle = alpha * i;
- targetIcon.setPositionX(centerX);
- targetIcon.setPositionY(centerY);
- targetIcon.setX(getRingWidth() / 2 * (float) Math.cos(angle));
- targetIcon.setY(getRingHeight() / 2 * (float) Math.sin(angle));
- }
- }
-
- private void updatePointCloudPosition(float centerX, float centerY) {
- mPointCloud.setCenter(centerX, centerY);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- mPointCloud.draw(canvas);
- mOuterRing.draw(canvas);
- final int ntargets = mTargetDrawables.size();
- for (int i = 0; i < ntargets; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- if (target != null) {
- target.draw(canvas);
- }
- }
- mHandleDrawable.draw(canvas);
- }
-
- public void setOnTriggerListener(OnTriggerListener listener) {
- mOnTriggerListener = listener;
- }
-
- private float square(float d) {
- return d * d;
- }
-
- private float dist2(float dx, float dy) {
- return dx*dx + dy*dy;
- }
-
- private float getScaledGlowRadiusSquared() {
- final float scaledTapRadius;
- final AccessibilityManager accessibilityManager =
- (AccessibilityManager) getContext().getSystemService(
- Context.ACCESSIBILITY_SERVICE);
- if (accessibilityManager.isEnabled()) {
- scaledTapRadius = TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED * mGlowRadius;
- } else {
- scaledTapRadius = mGlowRadius;
- }
- return square(scaledTapRadius);
- }
-
- private void announceTargets() {
- StringBuilder utterance = new StringBuilder();
- final int targetCount = mTargetDrawables.size();
- for (int i = 0; i < targetCount; i++) {
- String targetDescription = getTargetDescription(i);
- String directionDescription = getDirectionDescription(i);
- if (!TextUtils.isEmpty(targetDescription)
- && !TextUtils.isEmpty(directionDescription)) {
- String text = String.format(directionDescription, targetDescription);
- utterance.append(text);
- }
- }
- if (utterance.length() > 0) {
- announceForAccessibility(utterance.toString());
- }
- }
-
- private String getTargetDescription(int index) {
- if (mTargetDescriptions == null || mTargetDescriptions.isEmpty()) {
- mTargetDescriptions = loadDescriptions(mTargetDescriptionsResourceId);
- if (mTargetDrawables.size() != mTargetDescriptions.size()) {
- Log.w(TAG, "The number of target drawables must be"
- + " equal to the number of target descriptions.");
- return null;
- }
- }
- return mTargetDescriptions.get(index);
- }
-
- private String getDirectionDescription(int index) {
- if (mDirectionDescriptions == null || mDirectionDescriptions.isEmpty()) {
- mDirectionDescriptions = loadDescriptions(mDirectionDescriptionsResourceId);
- if (mTargetDrawables.size() != mDirectionDescriptions.size()) {
- Log.w(TAG, "The number of target drawables must be"
- + " equal to the number of direction descriptions.");
- return null;
- }
- }
- return mDirectionDescriptions.get(index);
- }
-
- private ArrayList<String> loadDescriptions(int resourceId) {
- TypedArray array = getContext().getResources().obtainTypedArray(resourceId);
- final int count = array.length();
- ArrayList<String> targetContentDescriptions = new ArrayList<String>(count);
- for (int i = 0; i < count; i++) {
- String contentDescription = array.getString(i);
- targetContentDescriptions.add(contentDescription);
- }
- array.recycle();
- return targetContentDescriptions;
- }
-
- public int getResourceIdForTarget(int index) {
- final TargetDrawable drawable = mTargetDrawables.get(index);
- return drawable == null ? 0 : drawable.getResourceId();
- }
-
- public void setEnableTarget(int resourceId, boolean enabled) {
- for (int i = 0; i < mTargetDrawables.size(); i++) {
- final TargetDrawable target = mTargetDrawables.get(i);
- if (target.getResourceId() == resourceId) {
- target.setEnabled(enabled);
- break; // should never be more than one match
- }
- }
- }
-
- /**
- * Gets the position of a target in the array that matches the given resource.
- * @param resourceId
- * @return the index or -1 if not found
- */
- public int getTargetPosition(int resourceId) {
- for (int i = 0; i < mTargetDrawables.size(); i++) {
- final TargetDrawable target = mTargetDrawables.get(i);
- if (target.getResourceId() == resourceId) {
- return i; // should never be more than one match
- }
- }
- return -1;
- }
-
- private boolean replaceTargetDrawables(Resources res, int existingResourceId,
- int newResourceId) {
- if (existingResourceId == 0 || newResourceId == 0) {
- return false;
- }
-
- boolean result = false;
- final ArrayList<TargetDrawable> drawables = mTargetDrawables;
- final int size = drawables.size();
- for (int i = 0; i < size; i++) {
- final TargetDrawable target = drawables.get(i);
- if (target != null && target.getResourceId() == existingResourceId) {
- target.setDrawable(res, newResourceId);
- result = true;
- }
- }
-
- if (result) {
- requestLayout(); // in case any given drawable's size changes
- }
-
- return result;
- }
-
- /**
- * Searches the given package for a resource to use to replace the Drawable on the
- * target with the given resource id
- * @param component of the .apk that contains the resource
- * @param name of the metadata in the .apk
- * @param existingResId the resource id of the target to search for
- * @return true if found in the given package and replaced at least one target Drawables
- */
- public boolean replaceTargetDrawablesIfPresent(ComponentName component, String name,
- int existingResId) {
- if (existingResId == 0) return false;
-
- boolean replaced = false;
- if (component != null) {
- try {
- PackageManager packageManager = getContext().getPackageManager();
- // Look for the search icon specified in the activity meta-data
- Bundle metaData = packageManager.getActivityInfo(
- component, PackageManager.GET_META_DATA).metaData;
- if (metaData != null) {
- int iconResId = metaData.getInt(name);
- if (iconResId != 0) {
- Resources res = packageManager.getResourcesForActivity(component);
- replaced = replaceTargetDrawables(res, existingResId, iconResId);
- }
- }
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Failed to swap drawable; "
- + component.flattenToShortString() + " not found", e);
- } catch (Resources.NotFoundException nfe) {
- Log.w(TAG, "Failed to swap drawable from "
- + component.flattenToShortString(), nfe);
- }
- }
- if (!replaced) {
- // Restore the original drawable
- replaceTargetDrawables(getContext().getResources(), existingResId, existingResId);
- }
- return replaced;
- }
-
- public class GlowpadExploreByTouchHelper extends ExploreByTouchHelper {
-
- private Rect mBounds = new Rect();
-
- public GlowpadExploreByTouchHelper(View forView) {
- super(forView);
- }
-
- @Override
- protected int getVirtualViewAt(float x, float y) {
- if (mGrabbedState == OnTriggerListener.CENTER_HANDLE) {
- for (int i = 0; i < mTargetDrawables.size(); i++) {
- final TargetDrawable target = mTargetDrawables.get(i);
- if (target.isEnabled() && target.getBounds().contains((int) x, (int) y)) {
- return i;
- }
- }
- return INVALID_ID;
- } else {
- return HOST_ID;
- }
- }
-
- @Override
- protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
- if (mGrabbedState == OnTriggerListener.CENTER_HANDLE) {
- // Add virtual views backwards so that accessibility services like switch
- // access traverse them in the correct order
- for (int i = mTargetDrawables.size() - 1; i >= 0; i--) {
- if (mTargetDrawables.get(i).isEnabled()) {
- virtualViewIds.add(i);
- }
- }
- }
- }
-
- @Override
- protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
- if (virtualViewId >= 0 && virtualViewId < mTargetDescriptions.size()) {
- event.setContentDescription(mTargetDescriptions.get(virtualViewId));
- }
- }
-
- @Override
- public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
- if (host == GlowPadView.this && event.getEventType()
- == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
- event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
- }
- super.onInitializeAccessibilityEvent(host, event);
- }
-
- @Override
- public void onPopulateNodeForHost(AccessibilityNodeInfoCompat node) {
- if (mGrabbedState == OnTriggerListener.NO_HANDLE) {
- node.setClickable(true);
- node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
- }
- mBounds.set(0, 0, GlowPadView.this.getWidth(), GlowPadView.this.getHeight());
- node.setBoundsInParent(mBounds);
- }
-
- @Override
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
- if (mGrabbedState == OnTriggerListener.NO_HANDLE) {
- // Simulate handle being grabbed to expose targets.
- trySwitchToFirstTouchState(mWaveCenterX, mWaveCenterY);
- invalidateRoot();
- return true;
- }
- return super.performAccessibilityAction(host, action, args);
- }
-
- @Override
- protected void onPopulateNodeForVirtualView(int virtualViewId,
- AccessibilityNodeInfoCompat node) {
- if (virtualViewId < mTargetDrawables.size()) {
- final TargetDrawable target = mTargetDrawables.get(virtualViewId);
- node.setBoundsInParent(target.getBounds());
- node.setClickable(true);
- node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
- node.setContentDescription(getTargetDescription(virtualViewId));
- }
- }
-
- @Override
- protected boolean onPerformActionForVirtualView(int virtualViewId, int action,
- Bundle arguments) {
- if (action == AccessibilityNodeInfo.ACTION_CLICK) {
- if (virtualViewId >= 0 && virtualViewId < mTargetDrawables.size()) {
- dispatchTriggerEvent(virtualViewId);
- return true;
- }
- }
- return false;
- }
-
- }
-}
diff --git a/InCallUI/src/com/android/incallui/widget/multiwaveview/PointCloud.java b/InCallUI/src/com/android/incallui/widget/multiwaveview/PointCloud.java
deleted file mode 100644
index 07a2cb964..000000000
--- a/InCallUI/src/com/android/incallui/widget/multiwaveview/PointCloud.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * 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.incallui.widget.multiwaveview;
-
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-public class PointCloud {
- private static final float MIN_POINT_SIZE = 2.0f;
- private static final float MAX_POINT_SIZE = 4.0f;
- private static final int INNER_POINTS = 8;
- private static final String TAG = "PointCloud";
- private ArrayList<Point> mPointCloud = new ArrayList<Point>();
- private Drawable mDrawable;
- private float mCenterX;
- private float mCenterY;
- private Paint mPaint;
- private float mScale = 1.0f;
- private static final float PI = (float) Math.PI;
-
- // These allow us to have multiple concurrent animations.
- WaveManager waveManager = new WaveManager();
- GlowManager glowManager = new GlowManager();
- private float mOuterRadius;
-
- public class WaveManager {
- private float radius = 50;
- private float width = 200.0f; // TODO: Make configurable
- private float alpha = 0.0f;
- public void setRadius(float r) {
- radius = r;
- }
-
- public float getRadius() {
- return radius;
- }
-
- public void setAlpha(float a) {
- alpha = a;
- }
-
- public float getAlpha() {
- return alpha;
- }
- };
-
- public class GlowManager {
- private float x;
- private float y;
- private float radius = 0.0f;
- private float alpha = 0.0f;
-
- public void setX(float x1) {
- x = x1;
- }
-
- public float getX() {
- return x;
- }
-
- public void setY(float y1) {
- y = y1;
- }
-
- public float getY() {
- return y;
- }
-
- public void setAlpha(float a) {
- alpha = a;
- }
-
- public float getAlpha() {
- return alpha;
- }
-
- public void setRadius(float r) {
- radius = r;
- }
-
- public float getRadius() {
- return radius;
- }
- }
-
- class Point {
- float x;
- float y;
- float radius;
-
- public Point(float x2, float y2, float r) {
- x = (float) x2;
- y = (float) y2;
- radius = r;
- }
- }
-
- public PointCloud(Drawable drawable) {
- mPaint = new Paint();
- mPaint.setFilterBitmap(true);
- mPaint.setColor(Color.rgb(255, 255, 255)); // TODO: make configurable
- mPaint.setAntiAlias(true);
- mPaint.setDither(true);
-
- mDrawable = drawable;
- if (mDrawable != null) {
- drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
- }
- }
-
- public void setCenter(float x, float y) {
- mCenterX = x;
- mCenterY = y;
- }
-
- public void makePointCloud(float innerRadius, float outerRadius) {
- if (innerRadius == 0) {
- Log.w(TAG, "Must specify an inner radius");
- return;
- }
- mOuterRadius = outerRadius;
- mPointCloud.clear();
- final float pointAreaRadius = (outerRadius - innerRadius);
- final float ds = (2.0f * PI * innerRadius / INNER_POINTS);
- final int bands = (int) Math.round(pointAreaRadius / ds);
- final float dr = pointAreaRadius / bands;
- float r = innerRadius;
- for (int b = 0; b <= bands; b++, r += dr) {
- float circumference = 2.0f * PI * r;
- final int pointsInBand = (int) (circumference / ds);
- float eta = PI/2.0f;
- float dEta = 2.0f * PI / pointsInBand;
- for (int i = 0; i < pointsInBand; i++) {
- float x = r * (float) Math.cos(eta);
- float y = r * (float) Math.sin(eta);
- eta += dEta;
- mPointCloud.add(new Point(x, y, r));
- }
- }
- }
-
- public void setScale(float scale) {
- mScale = scale;
- }
-
- public float getScale() {
- return mScale;
- }
-
- private static float hypot(float x, float y) {
- return (float) Math.hypot(x, y);
- }
-
- private static float max(float a, float b) {
- return a > b ? a : b;
- }
-
- public int getAlphaForPoint(Point point) {
- // Contribution from positional glow
- float glowDistance = hypot(glowManager.x - point.x, glowManager.y - point.y);
- float glowAlpha = 0.0f;
-
- if (glowDistance < glowManager.radius) {
- double cos = Math.cos(Math.PI * 0.25d * glowDistance / glowManager.radius);
- glowAlpha = glowManager.alpha * max(0.0f, (float) Math.pow(cos, 10.0d));
- }
-
- // Compute contribution from Wave
- float radius = hypot(point.x, point.y);
- float distanceToWaveRing = (radius - waveManager.radius);
- float waveAlpha = 0.0f;
- if (distanceToWaveRing < waveManager.width * 0.5f && distanceToWaveRing < 0.0f) {
- double cos = Math.cos(Math.PI * 0.25d * distanceToWaveRing / waveManager.width);
- waveAlpha = waveManager.alpha * max(0.0f, (float) Math.pow(cos, 20.0d));
- }
-
- return (int) (max(glowAlpha, waveAlpha) * 255);
- }
-
- private float interp(float min, float max, float f) {
- return min + (max - min) * f;
- }
-
- public void draw(Canvas canvas) {
- ArrayList<Point> points = mPointCloud;
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.scale(mScale, mScale, mCenterX, mCenterY);
- for (int i = 0; i < points.size(); i++) {
- Point point = points.get(i);
- final float pointSize = interp(MAX_POINT_SIZE, MIN_POINT_SIZE,
- point.radius / mOuterRadius);
- final float px = point.x + mCenterX;
- final float py = point.y + mCenterY;
- int alpha = getAlphaForPoint(point);
-
- if (alpha == 0) continue;
-
- if (mDrawable != null) {
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
- final float cx = mDrawable.getIntrinsicWidth() * 0.5f;
- final float cy = mDrawable.getIntrinsicHeight() * 0.5f;
- final float s = pointSize / MAX_POINT_SIZE;
- canvas.scale(s, s, px, py);
- canvas.translate(px - cx, py - cy);
- mDrawable.setAlpha(alpha);
- mDrawable.draw(canvas);
- canvas.restore();
- } else {
- mPaint.setAlpha(alpha);
- canvas.drawCircle(px, py, pointSize, mPaint);
- }
- }
- canvas.restore();
- }
-
-}
diff --git a/InCallUI/src/com/android/incallui/widget/multiwaveview/TargetDrawable.java b/InCallUI/src/com/android/incallui/widget/multiwaveview/TargetDrawable.java
deleted file mode 100644
index adc5324eb..000000000
--- a/InCallUI/src/com/android/incallui/widget/multiwaveview/TargetDrawable.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * 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.incallui.widget.multiwaveview;
-
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
-import android.util.Log;
-
-public class TargetDrawable {
- private static final String TAG = "TargetDrawable";
- private static final boolean DEBUG = false;
-
- public static final int[] STATE_ACTIVE =
- { android.R.attr.state_enabled, android.R.attr.state_active };
- public static final int[] STATE_INACTIVE =
- { android.R.attr.state_enabled, -android.R.attr.state_active };
- public static final int[] STATE_FOCUSED =
- { android.R.attr.state_enabled, -android.R.attr.state_active,
- android.R.attr.state_focused };
-
- private float mTranslationX = 0.0f;
- private float mTranslationY = 0.0f;
- private float mPositionX = 0.0f;
- private float mPositionY = 0.0f;
- private float mScaleX = 1.0f;
- private float mScaleY = 1.0f;
- private float mAlpha = 1.0f;
- private Drawable mDrawable;
- private boolean mEnabled = true;
- private final int mResourceId;
- private int mNumDrawables = 1;
- private Rect mBounds;
-
- /**
- * This is changed from the framework version to pass in the number of drawables in the
- * container. The framework version relies on private api's to get the count from
- * StateListDrawable.
- *
- * @param res
- * @param resId
- * @param count The number of drawables in the resource.
- */
- public TargetDrawable(Resources res, int resId, int count) {
- mResourceId = resId;
- setDrawable(res, resId);
- mNumDrawables = count;
- }
-
- public void setDrawable(Resources res, int resId) {
- // Note we explicitly don't set mResourceId to resId since we allow the drawable to be
- // swapped at runtime and want to re-use the existing resource id for identification.
- Drawable drawable = resId == 0 ? null : res.getDrawable(resId);
- // Mutate the drawable so we can animate shared drawable properties.
- mDrawable = drawable != null ? drawable.mutate() : null;
- resizeDrawables();
- setState(STATE_INACTIVE);
- }
-
- public TargetDrawable(TargetDrawable other) {
- mResourceId = other.mResourceId;
- // Mutate the drawable so we can animate shared drawable properties.
- mDrawable = other.mDrawable != null ? other.mDrawable.mutate() : null;
- resizeDrawables();
- setState(STATE_INACTIVE);
- }
-
- public void setState(int [] state) {
- if (mDrawable instanceof StateListDrawable) {
- StateListDrawable d = (StateListDrawable) mDrawable;
- d.setState(state);
- }
- }
-
- /**
- * Returns true if the drawable is a StateListDrawable and is in the focused state.
- *
- * @return
- */
- public boolean isActive() {
- if (mDrawable instanceof StateListDrawable) {
- StateListDrawable d = (StateListDrawable) mDrawable;
- int[] states = d.getState();
- for (int i = 0; i < states.length; i++) {
- if (states[i] == android.R.attr.state_focused) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Returns true if this target is enabled. Typically an enabled target contains a valid
- * drawable in a valid state. Currently all targets with valid drawables are valid.
- *
- * @return
- */
- public boolean isEnabled() {
- return mDrawable != null && mEnabled;
- }
-
- /**
- * Makes drawables in a StateListDrawable all the same dimensions.
- * If not a StateListDrawable, then justs sets the bounds to the intrinsic size of the
- * drawable.
- */
- private void resizeDrawables() {
- if (mDrawable instanceof StateListDrawable) {
- StateListDrawable d = (StateListDrawable) mDrawable;
- int maxWidth = 0;
- int maxHeight = 0;
-
- for (int i = 0; i < mNumDrawables; i++) {
- d.selectDrawable(i);
- Drawable childDrawable = d.getCurrent();
- maxWidth = Math.max(maxWidth, childDrawable.getIntrinsicWidth());
- maxHeight = Math.max(maxHeight, childDrawable.getIntrinsicHeight());
- }
-
- if (DEBUG) Log.v(TAG, "union of childDrawable rects " + d + " to: "
- + maxWidth + "x" + maxHeight);
- d.setBounds(0, 0, maxWidth, maxHeight);
-
- for (int i = 0; i < mNumDrawables; i++) {
- d.selectDrawable(i);
- Drawable childDrawable = d.getCurrent();
- if (DEBUG) Log.v(TAG, "sizing drawable " + childDrawable + " to: "
- + maxWidth + "x" + maxHeight);
- childDrawable.setBounds(0, 0, maxWidth, maxHeight);
- }
- } else if (mDrawable != null) {
- mDrawable.setBounds(0, 0,
- mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight());
- }
- }
-
- public void setX(float x) {
- mTranslationX = x;
- }
-
- public void setY(float y) {
- mTranslationY = y;
- }
-
- public void setScaleX(float x) {
- mScaleX = x;
- }
-
- public void setScaleY(float y) {
- mScaleY = y;
- }
-
- public void setAlpha(float alpha) {
- mAlpha = alpha;
- }
-
- public float getX() {
- return mTranslationX;
- }
-
- public float getY() {
- return mTranslationY;
- }
-
- public float getScaleX() {
- return mScaleX;
- }
-
- public float getScaleY() {
- return mScaleY;
- }
-
- public float getAlpha() {
- return mAlpha;
- }
-
- public void setPositionX(float x) {
- mPositionX = x;
- }
-
- public void setPositionY(float y) {
- mPositionY = y;
- }
-
- public float getPositionX() {
- return mPositionX;
- }
-
- public float getPositionY() {
- return mPositionY;
- }
-
- public int getWidth() {
- return mDrawable != null ? mDrawable.getIntrinsicWidth() : 0;
- }
-
- public int getHeight() {
- return mDrawable != null ? mDrawable.getIntrinsicHeight() : 0;
- }
-
- public Rect getBounds() {
- if (mBounds == null) {
- mBounds = new Rect();
- }
- mBounds.set((int) (mTranslationX + mPositionX - getWidth() * 0.5),
- (int) (mTranslationY + mPositionY - getHeight() * 0.5),
- (int) (mTranslationX + mPositionX + getWidth() * 0.5),
- (int) (mTranslationY + mPositionY + getHeight() * 0.5));
- return mBounds;
- }
-
- public void draw(Canvas canvas) {
- if (mDrawable == null || !mEnabled) {
- return;
- }
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.scale(mScaleX, mScaleY, mPositionX, mPositionY);
- canvas.translate(mTranslationX + mPositionX, mTranslationY + mPositionY);
- canvas.translate(-0.5f * getWidth(), -0.5f * getHeight());
- mDrawable.setAlpha((int) Math.round(mAlpha * 255f));
- mDrawable.draw(canvas);
- canvas.restore();
- }
-
- public void setEnabled(boolean enabled) {
- mEnabled = enabled;
- }
-
- public int getResourceId() {
- return mResourceId;
- }
-}
diff --git a/InCallUI/src/com/android/incallui/widget/multiwaveview/Tweener.java b/InCallUI/src/com/android/incallui/widget/multiwaveview/Tweener.java
deleted file mode 100644
index 7222442fe..000000000
--- a/InCallUI/src/com/android/incallui/widget/multiwaveview/Tweener.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * 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.incallui.widget.multiwaveview;
-
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map.Entry;
-
-class Tweener {
- private static final String TAG = "Tweener";
- private static final boolean DEBUG = false;
-
- ObjectAnimator animator;
- private static HashMap<Object, Tweener> sTweens = new HashMap<Object, Tweener>();
-
- public Tweener(ObjectAnimator anim) {
- animator = anim;
- }
-
- private static void remove(Animator animator) {
- Iterator<Entry<Object, Tweener>> iter = sTweens.entrySet().iterator();
- while (iter.hasNext()) {
- Entry<Object, Tweener> entry = iter.next();
- if (entry.getValue().animator == animator) {
- if (DEBUG) Log.v(TAG, "Removing tweener " + sTweens.get(entry.getKey())
- + " sTweens.size() = " + sTweens.size());
- iter.remove();
- break; // an animator can only be attached to one object
- }
- }
- }
-
- public static Tweener to(Object object, long duration, Object... vars) {
- long delay = 0;
- AnimatorUpdateListener updateListener = null;
- AnimatorListener listener = null;
- TimeInterpolator interpolator = null;
-
- // Iterate through arguments and discover properties to animate
- ArrayList<PropertyValuesHolder> props = new ArrayList<PropertyValuesHolder>(vars.length/2);
- for (int i = 0; i < vars.length; i+=2) {
- if (!(vars[i] instanceof String)) {
- throw new IllegalArgumentException("Key must be a string: " + vars[i]);
- }
- String key = (String) vars[i];
- Object value = vars[i+1];
-
- if ("simultaneousTween".equals(key)) {
- // TODO
- } else if ("ease".equals(key)) {
- interpolator = (TimeInterpolator) value; // TODO: multiple interpolators?
- } else if ("onUpdate".equals(key) || "onUpdateListener".equals(key)) {
- updateListener = (AnimatorUpdateListener) value;
- } else if ("onComplete".equals(key) || "onCompleteListener".equals(key)) {
- listener = (AnimatorListener) value;
- } else if ("delay".equals(key)) {
- delay = ((Number) value).longValue();
- } else if ("syncWith".equals(key)) {
- // TODO
- } else if (value instanceof float[]) {
- props.add(PropertyValuesHolder.ofFloat(key,
- ((float[])value)[0], ((float[])value)[1]));
- } else if (value instanceof int[]) {
- props.add(PropertyValuesHolder.ofInt(key,
- ((int[])value)[0], ((int[])value)[1]));
- } else if (value instanceof Number) {
- float floatValue = ((Number)value).floatValue();
- props.add(PropertyValuesHolder.ofFloat(key, floatValue));
- } else {
- throw new IllegalArgumentException(
- "Bad argument for key \"" + key + "\" with value " + value.getClass());
- }
- }
-
- // Re-use existing tween, if present
- Tweener tween = sTweens.get(object);
- ObjectAnimator anim = null;
- if (tween == null) {
- anim = ObjectAnimator.ofPropertyValuesHolder(object,
- props.toArray(new PropertyValuesHolder[props.size()]));
- tween = new Tweener(anim);
- sTweens.put(object, tween);
- if (DEBUG) Log.v(TAG, "Added new Tweener " + tween);
- } else {
- anim = sTweens.get(object).animator;
- replace(props, object); // Cancel all animators for given object
- }
-
- if (interpolator != null) {
- anim.setInterpolator(interpolator);
- }
-
- // Update animation with properties discovered in loop above
- anim.setStartDelay(delay);
- anim.setDuration(duration);
- if (updateListener != null) {
- anim.removeAllUpdateListeners(); // There should be only one
- anim.addUpdateListener(updateListener);
- }
- if (listener != null) {
- anim.removeAllListeners(); // There should be only one.
- anim.addListener(listener);
- }
- anim.addListener(mCleanupListener);
-
- return tween;
- }
-
- Tweener from(Object object, long duration, Object... vars) {
- // TODO: for v of vars
- // toVars[v] = object[v]
- // object[v] = vars[v]
- return Tweener.to(object, duration, vars);
- }
-
- // Listener to watch for completed animations and remove them.
- private static AnimatorListener mCleanupListener = new AnimatorListenerAdapter() {
-
- @Override
- public void onAnimationEnd(Animator animation) {
- remove(animation);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- remove(animation);
- }
- };
-
- public static void reset() {
- if (DEBUG) {
- Log.v(TAG, "Reset()");
- if (sTweens.size() > 0) {
- Log.v(TAG, "Cleaning up " + sTweens.size() + " animations");
- }
- }
- sTweens.clear();
- }
-
- private static void replace(ArrayList<PropertyValuesHolder> props, Object... args) {
- for (final Object killobject : args) {
- Tweener tween = sTweens.get(killobject);
- if (tween != null) {
- tween.animator.cancel();
- if (props != null) {
- tween.animator.setValues(
- props.toArray(new PropertyValuesHolder[props.size()]));
- } else {
- sTweens.remove(tween);
- }
- }
- }
- }
-}
diff --git a/InCallUI/src/com/android/incalluibind/ObjectFactory.java b/InCallUI/src/com/android/incalluibind/ObjectFactory.java
deleted file mode 100644
index 7e9423acf..000000000
--- a/InCallUI/src/com/android/incalluibind/ObjectFactory.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2014 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.incalluibind;
-
-import android.content.Context;
-import android.content.Intent;
-
-import com.android.incallui.CallCardPresenter.EmergencyCallListener;
-import com.android.incallui.ContactUtils;
-import com.android.incallui.DistanceHelper;
-import com.android.incallui.service.PhoneNumberService;
-
-public class ObjectFactory {
-
- public static PhoneNumberService newPhoneNumberService(Context context) {
- // no phone number service.
- return null;
- }
-
- public static EmergencyCallListener newEmergencyCallListener() {
- return null;
- }
-
- /** @return An {@link Intent} to be broadcast when the InCallUI is visible. */
- public static Intent getUiReadyBroadcastIntent(Context context) {
- return null;
- }
-
- /**
- * @return An {@link Intent} to be broadcast when the call state button in the InCallUI is
- * touched while in a call.
- */
- public static Intent getCallStateButtonBroadcastIntent(Context context) {
- return null;
- }
-
- public static DistanceHelper newDistanceHelper(Context context,
- DistanceHelper.Listener listener) {
- return null;
- }
-
- public static ContactUtils getContactUtilsInstance(Context context) {
- return null;
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/CallCardPresenterTest.java b/InCallUI/tests/src/com/android/incallui/CallCardPresenterTest.java
deleted file mode 100644
index 79545ce4b..000000000
--- a/InCallUI/tests/src/com/android/incallui/CallCardPresenterTest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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.incallui;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.incallui.ContactInfoCache.ContactCacheEntry;
-
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@MediumTest
-public class CallCardPresenterTest extends AndroidTestCase {
-
- private static final String NAME_PRIMARY = "Full Name";
- private static final String NAME_ALTERNATIVE = "Name, Full";
- private static final String LOCATION = "US";
- private static final String NUMBER = "8006459001";
-
- @Mock private ContactsPreferences mContactsPreferences;
- private ContactCacheEntry mUnlockedContactInfo;
- private ContactCacheEntry mLockedContactInfo;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
-
- Mockito.when(mContactsPreferences.getDisplayOrder())
- .thenReturn(ContactsPreferences.DISPLAY_ORDER_PRIMARY);
-
- // Unlocked all contact info is available
- mUnlockedContactInfo = new ContactCacheEntry();
- mUnlockedContactInfo.namePrimary = NAME_PRIMARY;
- mUnlockedContactInfo.nameAlternative = NAME_ALTERNATIVE;
- mUnlockedContactInfo.location = LOCATION;
- mUnlockedContactInfo.number = NUMBER;
-
- // Locked only number and location are available
- mLockedContactInfo = new ContactCacheEntry();
- mLockedContactInfo .location = LOCATION;
- mLockedContactInfo .number = NUMBER;
- }
-
- @Override
- public void tearDown() throws Exception {
- super.tearDown();
- ContactsPreferencesFactory.setTestInstance(null);
- }
-
- public void testGetNameForCall_Unlocked() {
- ContactsPreferencesFactory.setTestInstance(mContactsPreferences);
- CallCardPresenter presenter = new CallCardPresenter();
- presenter.init(getContext(), null);
-
- assertEquals(NAME_PRIMARY, presenter.getNameForCall(mUnlockedContactInfo));
- }
-
- public void testGetNameForCall_Locked() {
- ContactsPreferencesFactory.setTestInstance(null);
- CallCardPresenter presenter = new CallCardPresenter();
- presenter.init(getContext(), null);
-
- assertEquals(NUMBER, presenter.getNameForCall(mLockedContactInfo));
- }
-
- public void testGetNameForCall_EmptyPreferredName() {
- ContactCacheEntry contactInfo = new ContactCacheEntry();
- contactInfo.number = NUMBER;
-
- ContactsPreferencesFactory.setTestInstance(null);
- CallCardPresenter presenter = new CallCardPresenter();
- presenter.init(getContext(), null);
-
- assertEquals(NUMBER, presenter.getNameForCall(contactInfo));
- }
-
- public void testGetNumberForCall_Unlocked() {
- ContactsPreferencesFactory.setTestInstance(mContactsPreferences);
- CallCardPresenter presenter = new CallCardPresenter();
- presenter.init(getContext(), null);
-
- assertEquals(NUMBER, presenter.getNumberForCall(mUnlockedContactInfo));
- }
-
- public void testGetNumberForCall_Locked() {
- ContactsPreferencesFactory.setTestInstance(null);
- CallCardPresenter presenter = new CallCardPresenter();
- presenter.init(getContext(), null);
-
- assertEquals(LOCATION, presenter.getNumberForCall(mLockedContactInfo));
- }
-
- public void testGetNumberForCall_EmptyPreferredName() {
- ContactCacheEntry contactInfo = new ContactCacheEntry();
- contactInfo.location = LOCATION;
-
- ContactsPreferencesFactory.setTestInstance(null);
- CallCardPresenter presenter = new CallCardPresenter();
- presenter.init(getContext(), null);
-
- assertEquals(LOCATION, presenter.getNumberForCall(contactInfo));
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/CallTest.java b/InCallUI/tests/src/com/android/incallui/CallTest.java
deleted file mode 100644
index 118ec38da..000000000
--- a/InCallUI/tests/src/com/android/incallui/CallTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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.incallui;
-
-import android.os.Bundle;
-import android.telecom.Connection;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-// @formatter:off
-/**
- * Run test with
- * adb shell am instrument -e class com.android.incallui.CallTest -w com.google.android.dialer.tests/android.test.InstrumentationTestRunner
- */
-// @formatter:on
-
-@SmallTest
-public class CallTest extends AndroidTestCase {
-
- private TestCall mCall;
-
- private final static String CHILD_NUMBER = "123";
- private final static ArrayList<String> LAST_FORWARDED_NUMBER_LIST =
- new ArrayList(Arrays.asList("456", "789"));
- private final static String LAST_FORWARDED_NUMBER = "789";
- private final static String CALL_SUBJECT = "foo";
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
-
- mCall = new TestCall();
- }
-
- @Override
- public void tearDown() throws Exception {
- super.tearDown();
- }
-
- public void testUpdateFromCallExtras() {
- mCall.updateFromCallExtras(getTestBundle());
- verifyTestBundleResult();
- }
-
- public void testUpdateFromCallExtras_corruptedBundle() {
- mCall.setBundleCorrupted(true);
- mCall.updateFromCallExtras(getTestBundle());
-
- assertEquals(mCall.getChildNumber(), null);
- assertEquals(mCall.getLastForwardedNumber(), null);
- assertEquals(mCall.getCallSubject(), null);
- }
-
- public void testUpdateFromCallExtras_corruptedBundleOverwrite() {
-
- mCall.updateFromCallExtras(getTestBundle());
- mCall.setBundleCorrupted(true);
- Bundle bundle = new Bundle();
- bundle.putString(Connection.EXTRA_CHILD_ADDRESS, "321");
- bundle.putStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER,
- new ArrayList(Arrays.asList("654", "987")));
- bundle.putString(Connection.EXTRA_CALL_SUBJECT, "bar");
- mCall.updateFromCallExtras(bundle);
- //corrupted bundle should not overwrite existing values.
- verifyTestBundleResult();
- }
-
- private Bundle getTestBundle() {
- Bundle bundle = new Bundle();
- bundle.putString(Connection.EXTRA_CHILD_ADDRESS, CHILD_NUMBER);
- bundle.putStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER,
- LAST_FORWARDED_NUMBER_LIST);
- bundle.putString(Connection.EXTRA_CALL_SUBJECT, CALL_SUBJECT);
- return bundle;
- }
-
- private void verifyTestBundleResult() {
- assertEquals(CHILD_NUMBER, mCall.getChildNumber());
- assertEquals(LAST_FORWARDED_NUMBER, mCall.getLastForwardedNumber());
- assertEquals(CALL_SUBJECT, mCall.getCallSubject());
- }
-
- private class TestCall extends Call {
-
- private boolean mBundleCorrupted = false;
-
- public TestCall() {
- super(Call.State.NEW);
- }
-
- @Override
- public void updateFromCallExtras(Bundle bundle) {
- super.updateFromCallExtras(bundle);
- }
-
- public void setBundleCorrupted(boolean value) {
- this.mBundleCorrupted = value;
- }
-
- @Override
- protected boolean areCallExtrasCorrupted(Bundle callExtras) {
- if (mBundleCorrupted) {
- return true;
- }
- return super.areCallExtrasCorrupted(callExtras);
- }
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/CallerInfoUtilsTest.java b/InCallUI/tests/src/com/android/incallui/CallerInfoUtilsTest.java
deleted file mode 100644
index de5a0239e..000000000
--- a/InCallUI/tests/src/com/android/incallui/CallerInfoUtilsTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.incallui;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-@SmallTest
-public class CallerInfoUtilsTest extends AndroidTestCase {
- public void testToLogSafeNumber_email() {
- assertEquals("xxx@xxx.xxx", CallerInfoUtils.toLogSafePhoneNumber("foo@foo.com"));
- }
-
- public void testToLogSafeNumber_phoneNumber() {
- assertEquals("xxx-xxx-xxxx", CallerInfoUtils.toLogSafePhoneNumber("123-456-6789"));
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/ContactsPreferencesFactoryTest.java b/InCallUI/tests/src/com/android/incallui/ContactsPreferencesFactoryTest.java
deleted file mode 100644
index bf915553b..000000000
--- a/InCallUI/tests/src/com/android/incallui/ContactsPreferencesFactoryTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.incallui;
-
-import com.android.dialer.compat.UserManagerCompat;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.contacts.common.preference.ContactsPreferences;
-
-import org.mockito.Mockito;
-
-@SmallTest
-public class ContactsPreferencesFactoryTest extends AndroidTestCase {
-
- public void testNewContactsPreferences_Unlocked() {
- if (!UserManagerCompat.isUserUnlocked(getContext())) {
- return;
- }
- assertNotNull(ContactsPreferencesFactory.newContactsPreferences(getContext()));
- }
-
- public void testNewContactsPreferences_Locked() {
- if (UserManagerCompat.isUserUnlocked(getContext())) {
- return;
- }
- assertNull(ContactsPreferencesFactory.newContactsPreferences(getContext()));
- }
-
- public void testNewContactsPreferences_TestInstance() {
- ContactsPreferences testInstance = Mockito.mock(ContactsPreferences.class);
- ContactsPreferencesFactory.setTestInstance(testInstance);
- // Assert that it returns the same object always
- assertSame(testInstance, ContactsPreferencesFactory.newContactsPreferences(getContext()));
- assertSame(testInstance, ContactsPreferencesFactory.newContactsPreferences(getContext()));
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/ExternalCallListTest.java b/InCallUI/tests/src/com/android/incallui/ExternalCallListTest.java
deleted file mode 100644
index 59434700c..000000000
--- a/InCallUI/tests/src/com/android/incallui/ExternalCallListTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * 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.incallui;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.net.Uri;
-import android.os.Bundle;
-import android.telecom.*;
-import android.telecom.Call;
-import android.test.AndroidTestCase;
-
-import com.android.contacts.common.compat.CallSdkCompat;
-
-import java.lang.reflect.Constructor;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-public class ExternalCallListTest extends AndroidTestCase {
-
- private static class Listener implements ExternalCallList.ExternalCallListener {
- private CountDownLatch mCallAddedLatch = new CountDownLatch(1);
- private CountDownLatch mCallRemovedLatch = new CountDownLatch(1);
- private CountDownLatch mCallUpdatedLatch = new CountDownLatch(1);
-
- @Override
- public void onExternalCallAdded(Call call) {
- mCallAddedLatch.countDown();
- }
-
- @Override
- public void onExternalCallRemoved(Call call) {
- mCallRemovedLatch.countDown();
- }
-
- @Override
- public void onExternalCallUpdated(Call call) {
- mCallUpdatedLatch.countDown();
- }
-
- public boolean awaitCallAdded() {
- try {
- return mCallAddedLatch.await(WAIT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- return false;
- }
- }
-
- public boolean awaitCallRemoved() {
- try {
- return mCallRemovedLatch.await(WAIT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- return false;
- }
- }
-
- public boolean awaitCallUpdated() {
- try {
- return mCallUpdatedLatch.await(WAIT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- return false;
- }
- }
- }
-
- private static final int WAIT_TIMEOUT_MILLIS = 5000;
-
- private ExternalCallList mExternalCallList = new ExternalCallList();
- private Listener mExternalCallListener = new Listener();
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mExternalCallList.addExternalCallListener(mExternalCallListener);
- }
-
- public void testAddCallSuccess() {
- TestTelecomCall call = getTestCall(CallSdkCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
- mExternalCallList.onCallAdded(call.getCall());
- assertTrue(mExternalCallListener.awaitCallAdded());
- }
-
- public void testAddCallFail() {
- TestTelecomCall call = getTestCall(0 /* no properties */);
- try {
- mExternalCallList.onCallAdded(call.getCall());
- fail();
- } catch (IllegalArgumentException e) {
- }
- }
-
- public void testUpdateCall() {
- TestTelecomCall call = getTestCall(CallSdkCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
- mExternalCallList.onCallAdded(call.getCall());
- assertTrue(mExternalCallListener.awaitCallAdded());
-
- call.forceDetailsUpdate();
- assertTrue(mExternalCallListener.awaitCallUpdated());
- }
-
- public void testRemoveCall() {
- TestTelecomCall call = getTestCall(CallSdkCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
- mExternalCallList.onCallAdded(call.getCall());
- assertTrue(mExternalCallListener.awaitCallAdded());
-
- mExternalCallList.onCallRemoved(call.getCall());
- assertTrue(mExternalCallListener.awaitCallRemoved());
- }
-
- private TestTelecomCall getTestCall(int properties) {
- TestTelecomCall testCall = TestTelecomCall.createInstance(
- "1",
- Uri.parse("tel:650-555-1212"), /* handle */
- TelecomManager.PRESENTATION_ALLOWED, /* handlePresentation */
- "Joe", /* callerDisplayName */
- TelecomManager.PRESENTATION_ALLOWED, /* callerDisplayNamePresentation */
- new PhoneAccountHandle(new ComponentName("test", "class"),
- "handle"), /* accountHandle */
- CallSdkCompat.Details.CAPABILITY_CAN_PULL_CALL, /* capabilities */
- properties, /* properties */
- null, /* disconnectCause */
- 0, /* connectTimeMillis */
- null, /* GatewayInfo */
- VideoProfile.STATE_AUDIO_ONLY, /* videoState */
- null, /* statusHints */
- null, /* extras */
- null /* intentExtras */);
- return testCall;
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/ExternalCallNotifierTest.java b/InCallUI/tests/src/com/android/incallui/ExternalCallNotifierTest.java
deleted file mode 100644
index 64ddd2ea5..000000000
--- a/InCallUI/tests/src/com/android/incallui/ExternalCallNotifierTest.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * 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.incallui;
-
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import com.android.contacts.common.preference.ContactsPreferences;
-
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.telecom.*;
-import android.telecom.Call;
-import android.telephony.TelephonyManager;
-import android.test.AndroidTestCase;
-import android.test.mock.MockContext;
-
-import com.android.contacts.common.compat.CallSdkCompat;
-
-/**
- * Unit tests for {@link ExternalCallNotifier}.
- */
-public class ExternalCallNotifierTest extends AndroidTestCase {
- private static final int TIMEOUT_MILLIS = 5000;
- private static final String NAME_PRIMARY = "Full Name";
- private static final String NAME_ALTERNATIVE = "Name, Full";
- private static final String LOCATION = "US";
- private static final String NUMBER = "6505551212";
-
- @Mock private ContactsPreferences mContactsPreferences;
- @Mock private NotificationManager mNotificationManager;
- @Mock private MockContext mMockContext;
- @Mock private Resources mResources;
- @Mock private StatusBarNotifier mStatusBarNotifier;
- @Mock private ContactInfoCache mContactInfoCache;
- @Mock private TelecomManager mTelecomManager;
- @Mock private TelephonyManager mTelephonyManager;
- @Mock private ProximitySensor mProximitySensor;
- @Mock private CallList mCallList;
- private InCallPresenter mInCallPresenter;
- private ExternalCallNotifier mExternalCallNotifier;
- private ContactInfoCache.ContactCacheEntry mContactInfo;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
-
- when(mContactsPreferences.getDisplayOrder())
- .thenReturn(ContactsPreferences.DISPLAY_ORDER_PRIMARY);
-
- // Setup the mock context to return mocks for some of the needed services; the notification
- // service is especially important as we want to be able to intercept calls into it and
- // validate the notifcations.
- when(mMockContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
- .thenReturn(mNotificationManager);
- when(mMockContext.getSystemService(eq(Context.TELECOM_SERVICE)))
- .thenReturn(mTelecomManager);
- when(mMockContext.getSystemService(eq(Context.TELEPHONY_SERVICE)))
- .thenReturn(mTelephonyManager);
-
- // These aspects of the context are used by the notification builder to build the actual
- // notification; we will rely on the actual implementations of these.
- when(mMockContext.getPackageManager()).thenReturn(mContext.getPackageManager());
- when(mMockContext.getResources()).thenReturn(mContext.getResources());
- when(mMockContext.getApplicationInfo()).thenReturn(mContext.getApplicationInfo());
- when(mMockContext.getContentResolver()).thenReturn(mContext.getContentResolver());
- when(mMockContext.getPackageName()).thenReturn(mContext.getPackageName());
-
- ContactsPreferencesFactory.setTestInstance(null);
- mExternalCallNotifier = new ExternalCallNotifier(mMockContext, mContactInfoCache);
-
- // We don't directly use the InCallPresenter in the test, or even in ExternalCallNotifier
- // itself. However, ExternalCallNotifier needs to make instances of
- // com.android.incallui.Call for the purpose of performing contact cache lookups. The
- // Call class depends on the static InCallPresenter for a number of things, so we need to
- // set it up here to prevent crashes.
- mInCallPresenter = InCallPresenter.getInstance();
- mInCallPresenter.setUp(mMockContext, mCallList, new ExternalCallList(),
- null, mStatusBarNotifier, mExternalCallNotifier, mContactInfoCache,
- mProximitySensor);
-
- // Unlocked all contact info is available
- mContactInfo = new ContactInfoCache.ContactCacheEntry();
- mContactInfo.namePrimary = NAME_PRIMARY;
- mContactInfo.nameAlternative = NAME_ALTERNATIVE;
- mContactInfo.location = LOCATION;
- mContactInfo.number = NUMBER;
-
- // Given the mock ContactInfoCache cache, we need to mock out what happens when the
- // ExternalCallNotifier calls into the contact info cache to do a lookup. We will always
- // return mock info stored in mContactInfo.
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- com.android.incallui.Call call = (com.android.incallui.Call) args[0];
- ContactInfoCache.ContactInfoCacheCallback callback
- = (ContactInfoCache.ContactInfoCacheCallback) args[2];
- callback.onContactInfoComplete(call.getId(), mContactInfo);
- return null;
- }
- }).when(mContactInfoCache).findInfo(any(com.android.incallui.Call.class), anyBoolean(),
- any(ContactInfoCache.ContactInfoCacheCallback.class));
- }
-
- @Override
- public void tearDown() throws Exception {
- super.tearDown();
- ContactsPreferencesFactory.setTestInstance(null);
- mInCallPresenter.tearDown();
- }
-
- public void testPostNonPullable() {
- TestTelecomCall call = getTestCall(false);
- mExternalCallNotifier.onExternalCallAdded(call.getCall());
- Notification notification = verifyNotificationPosted();
- assertNull(notification.actions);
- }
-
- public void testPostPullable() {
- TestTelecomCall call = getTestCall(true);
- mExternalCallNotifier.onExternalCallAdded(call.getCall());
- Notification notification = verifyNotificationPosted();
- assertEquals(1, notification.actions.length);
- }
-
- public void testNotificationDismissed() {
- TestTelecomCall call = getTestCall(false);
- mExternalCallNotifier.onExternalCallAdded(call.getCall());
- verifyNotificationPosted();
-
- mExternalCallNotifier.onExternalCallRemoved(call.getCall());
- verify(mNotificationManager, timeout(TIMEOUT_MILLIS)).cancel(eq("EXTERNAL_CALL"), eq(0));
- }
-
- public void testNotificationUpdated() {
- TestTelecomCall call = getTestCall(false);
- mExternalCallNotifier.onExternalCallAdded(call.getCall());
- verifyNotificationPosted();
-
- call.setCapabilities(CallSdkCompat.Details.CAPABILITY_CAN_PULL_CALL);
- mExternalCallNotifier.onExternalCallUpdated(call.getCall());
-
- ArgumentCaptor<Notification> notificationCaptor =
- ArgumentCaptor.forClass(Notification.class);
- verify(mNotificationManager, timeout(TIMEOUT_MILLIS).times(2))
- .notify(eq("EXTERNAL_CALL"), eq(0), notificationCaptor.capture());
- Notification notification1 = notificationCaptor.getAllValues().get(0);
- assertNull(notification1.actions);
- Notification notification2 = notificationCaptor.getAllValues().get(1);
- assertEquals(1, notification2.actions.length);
- }
-
- private Notification verifyNotificationPosted() {
- ArgumentCaptor<Notification> notificationCaptor =
- ArgumentCaptor.forClass(Notification.class);
- verify(mNotificationManager, timeout(TIMEOUT_MILLIS))
- .notify(eq("EXTERNAL_CALL"), eq(0), notificationCaptor.capture());
- return notificationCaptor.getValue();
- }
-
- private TestTelecomCall getTestCall(boolean canPull) {
- TestTelecomCall testCall = TestTelecomCall.createInstance(
- "1",
- Uri.parse("tel:650-555-1212"), /* handle */
- TelecomManager.PRESENTATION_ALLOWED, /* handlePresentation */
- "Joe", /* callerDisplayName */
- TelecomManager.PRESENTATION_ALLOWED, /* callerDisplayNamePresentation */
- new PhoneAccountHandle(new ComponentName("test", "class"),
- "handle"), /* accountHandle */
- canPull ? CallSdkCompat.Details.CAPABILITY_CAN_PULL_CALL : 0, /* capabilities */
- CallSdkCompat.Details.PROPERTY_IS_EXTERNAL_CALL, /* properties */
- null, /* disconnectCause */
- 0, /* connectTimeMillis */
- null, /* GatewayInfo */
- VideoProfile.STATE_AUDIO_ONLY, /* videoState */
- null, /* statusHints */
- null, /* extras */
- null /* intentExtras */);
- return testCall;
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/InCallContactInteractionsTest.java b/InCallUI/tests/src/com/android/incallui/InCallContactInteractionsTest.java
deleted file mode 100644
index 625cda448..000000000
--- a/InCallUI/tests/src/com/android/incallui/InCallContactInteractionsTest.java
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * 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.incallui;
-
-import android.location.Address;
-import android.test.AndroidTestCase;
-import android.util.Pair;
-
-import com.android.incallui.InCallContactInteractions.BusinessContextInfo;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Tests for InCallContactInteractions class methods for formatting info for display.
- *
- * NOTE: tests assume system settings are set to 12hr time format and US locale. This means that
- * the output of InCallContactInteractions methods are compared against strings in 12hr time format
- * and US locale address formatting unless otherwise specified.
- */
-public class InCallContactInteractionsTest extends AndroidTestCase {
- private InCallContactInteractions mInCallContactInteractions;
- private static final float TEST_DISTANCE = (float) 1234.56;
-
- @Override
- protected void setUp() {
- mInCallContactInteractions = new InCallContactInteractions(mContext, true /* isBusiness */);
- }
-
- public void testIsOpenNow_NowMatchesOpenTime() {
- assertEquals(mContext.getString(R.string.open_now),
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(8),
- Arrays.asList(
- Pair.create(
- getTestCalendarWithHour(8),
- getTestCalendarWithHour(20))))
- .heading);
- }
-
- public void testIsOpenNow_ClosingAfterMidnight() {
- assertEquals(mContext.getString(R.string.open_now),
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(10),
- Arrays.asList(
- Pair.create(
- getTestCalendarWithHour(8),
- getTestCalendarWithHourAndDaysFromToday(1, 1))))
- .heading);
- }
-
- public void testIsOpenNow_Open24Hours() {
- assertEquals(mContext.getString(R.string.open_now),
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(10),
- Arrays.asList(
- Pair.create(
- getTestCalendarWithHour(8),
- getTestCalendarWithHourAndDaysFromToday(8, 1))))
- .heading);
- }
-
- public void testIsOpenNow_AfterMiddayBreak() {
- assertEquals(mContext.getString(R.string.open_now),
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(13),
- Arrays.asList(
- Pair.create(
- getTestCalendarWithHour(8),
- getTestCalendarWithHour(10)),
- Pair.create(
- getTestCalendarWithHour(12),
- getTestCalendarWithHour(15))))
- .heading);
- }
-
- public void testIsClosedNow_DuringMiddayBreak() {
- assertEquals(mContext.getString(R.string.closed_now),
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(11),
- Arrays.asList(
- Pair.create(
- getTestCalendarWithHour(8),
- getTestCalendarWithHour(10)),
- Pair.create(
- getTestCalendarWithHour(12),
- getTestCalendarWithHour(15))))
- .heading);
- }
-
- public void testIsClosedNow_BeforeOpen() {
- assertEquals(mContext.getString(R.string.closed_now),
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(6),
- Arrays.asList(
- Pair.create(
- getTestCalendarWithHour(8),
- getTestCalendarWithHour(20))))
- .heading);
- }
-
- public void testIsClosedNow_NowMatchesClosedTime() {
- assertEquals(mContext.getString(R.string.closed_now),
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(20),
- Arrays.asList(
- Pair.create(
- getTestCalendarWithHour(8),
- getTestCalendarWithHour(20))))
- .heading);
- }
-
- public void testIsClosedNow_AfterClosed() {
- assertEquals(mContext.getString(R.string.closed_now),
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(21),
- Arrays.asList(
- Pair.create(
- getTestCalendarWithHour(8),
- getTestCalendarWithHour(20))))
- .heading);
- }
-
- public void testOpeningHours_SingleOpenRangeWhileOpen() {
- assertEquals("8:00 AM - 8:00 PM",
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(12),
- Arrays.asList(
- Pair.create(
- getTestCalendarWithHour(8),
- getTestCalendarWithHour(20))))
- .detail);
- }
-
- public void testOpeningHours_TwoOpenRangesWhileOpen() {
- assertEquals("8:00 AM - 10:00 AM, 12:00 PM - 3:00 PM",
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(12),
- Arrays.asList(
- Pair.create(
- getTestCalendarWithHour(8),
- getTestCalendarWithHour(10)),
- Pair.create(
- getTestCalendarWithHour(12),
- getTestCalendarWithHour(15))))
- .detail);
- }
-
- public void testOpeningHours_AfterClosedNoTomorrow() {
- assertEquals("Closed today at 8:00 PM",
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(21),
- Arrays.asList(
- Pair.create(
- getTestCalendarWithHour(8),
- getTestCalendarWithHour(20))))
- .detail);
- }
-
- public void testOpeningHours_NotOpenTodayOpenTomorrow() {
- assertEquals("Opens tomorrow at 8:00 AM",
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(21),
- Arrays.asList(
- Pair.create(
- getTestCalendarWithHourAndDaysFromToday(8, 1),
- getTestCalendarWithHourAndDaysFromToday(10, 1))))
- .detail);
- }
-
- public void testMultipleOpenRanges_BeforeOpen() {
- assertEquals("Opens today at 8:00 AM",
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(7),
- getMultipleOpeningHours())
- .detail);
- }
-
- public void testMultipleOpenRanges_DuringFirstRange() {
- assertEquals("Closes at 10:00 AM",
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(9),
- getMultipleOpeningHours())
- .detail);
- }
-
- public void testMultipleOpenRanges_BeforeMiddleRange() {
- assertEquals("Opens today at 12:00 PM",
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(11),
- getMultipleOpeningHours())
- .detail);
- }
-
- public void testMultipleOpeningHours_DuringLastRange() {
- assertEquals("Closes at 9:00 PM",
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(19),
- getMultipleOpeningHours())
- .detail);
- }
-
- public void testMultipleOpeningHours_AfterClose() {
- assertEquals("Opens tomorrow at 8:00 AM",
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(22),
- getMultipleOpeningHours())
- .detail);
- }
-
- public void testNotOpenTodayOrTomorrow() {
- assertEquals(null,
- mInCallContactInteractions.constructHoursInfo(
- getTestCalendarWithHour(21),
- new ArrayList<Pair<Calendar, Calendar>>()));
- }
-
- public void testLocationInfo_ForUS() {
- BusinessContextInfo info =
- mInCallContactInteractions.constructLocationInfo(
- Locale.US,
- getAddressForTest(),
- TEST_DISTANCE);
- assertEquals("0.8 mi away", info.heading);
- assertEquals("Test address, Test locality", info.detail);
- }
-
- public void testLocationInfo_ForNotUS() {
- BusinessContextInfo info =
- mInCallContactInteractions.constructLocationInfo(
- Locale.CANADA,
- getAddressForTest(),
- TEST_DISTANCE);
- assertEquals("1.2 km away", info.heading);
- assertEquals("Test address, Test locality", info.detail);
- }
-
- public void testLocationInfo_NoLocality() {
- Address address = getAddressForTest();
- address.setLocality(null);
- BusinessContextInfo info =
- mInCallContactInteractions.constructLocationInfo(
- Locale.CANADA,
- address,
- TEST_DISTANCE);
- assertEquals("1.2 km away", info.heading);
- assertEquals("Test address", info.detail);
- }
-
- public void testLocationInfo_NoAddress() {
- BusinessContextInfo info =
- mInCallContactInteractions.constructLocationInfo(
- Locale.CANADA,
- null,
- TEST_DISTANCE);
- assertEquals(null, info);
- }
-
- public void testLocationInfo_NoDistance() {
- BusinessContextInfo info =
- mInCallContactInteractions.constructLocationInfo(
- Locale.US,
- getAddressForTest(),
- DistanceHelper.DISTANCE_NOT_FOUND);
- assertEquals(null, info.heading);
- }
-
- private Address getAddressForTest() {
- Address address = new Address(Locale.US);
- address.setAddressLine(0, "Test address");
- address.setLocality("Test locality");
- return address;
- }
-
- private Calendar getTestCalendarWithHour(int hour) {
- Calendar calendar = Calendar.getInstance();
- calendar.set(Calendar.HOUR_OF_DAY, hour);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.MILLISECOND, 0);
- return calendar;
- }
-
- private Calendar getTestCalendarWithHourAndDaysFromToday(int hour, int daysFromToday) {
- Calendar calendar = getTestCalendarWithHour(hour);
- calendar.add(Calendar.DATE, daysFromToday);
- return calendar;
- }
-
- private List<Pair<Calendar, Calendar>> getMultipleOpeningHours() {
- return Arrays.asList(
- Pair.create(
- getTestCalendarWithHour(8),
- getTestCalendarWithHour(10)),
- Pair.create(
- getTestCalendarWithHour(12),
- getTestCalendarWithHour(15)),
- Pair.create(
- getTestCalendarWithHour(17),
- getTestCalendarWithHour(21)),
- Pair.create(
- getTestCalendarWithHourAndDaysFromToday(8, 1),
- getTestCalendarWithHourAndDaysFromToday(10, 1)),
- Pair.create(
- getTestCalendarWithHourAndDaysFromToday(12, 1),
- getTestCalendarWithHourAndDaysFromToday(8, 1)));
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/InCallPresenterTest.java b/InCallUI/tests/src/com/android/incallui/InCallPresenterTest.java
deleted file mode 100644
index f0f08ab68..000000000
--- a/InCallUI/tests/src/com/android/incallui/InCallPresenterTest.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * 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.incallui;
-
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.Intent;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.TelephonyManager;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import com.android.incallui.InCallPresenter.InCallState;
-
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@MediumTest
-public class InCallPresenterTest extends InstrumentationTestCase {
- private MockCallListWrapper mCallList;
- @Mock private InCallActivity mInCallActivity;
- @Mock private AudioModeProvider mAudioModeProvider;
- @Mock private StatusBarNotifier mStatusBarNotifier;
- @Mock private ExternalCallNotifier mExternalCallNotifier;
- @Mock private ContactInfoCache mContactInfoCache;
- @Mock private ProximitySensor mProximitySensor;
-
- InCallPresenter mInCallPresenter;
- @Mock private Context mContext;
- @Mock private TelephonyManager mTelephonyManager;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- System.setProperty("dexmaker.dexcache",
- getInstrumentation().getTargetContext().getCacheDir().getPath());
- MockitoAnnotations.initMocks(this);
- mCallList = new MockCallListWrapper();
-
- when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
-
- mInCallPresenter = InCallPresenter.getInstance();
- mInCallPresenter.setUp(mContext, mCallList.getCallList(), new ExternalCallList(),
- mAudioModeProvider, mStatusBarNotifier, mExternalCallNotifier, mContactInfoCache,
- mProximitySensor);
- }
-
- @Override
- protected void tearDown() throws Exception {
- // The tear down method needs to run in the main thread since there is an explicit check
- // inside TelecomAdapter.getInstance().
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mInCallPresenter.unsetActivity(mInCallActivity);
- mInCallPresenter.tearDown();
- InCallPresenter.setInstance(null);
- }
- });
- }
-
- public void testOnActivitySet_finishesActivityWhenNoCalls() {
- mInCallPresenter.setActivity(mInCallActivity);
-
- verify(mInCallActivity).finish();
- }
-
- public void testOnCallListChange_sendsNotificationWhenInCall() {
- mCallList.setHasCall(Call.State.INCOMING, true);
-
- mInCallPresenter.onCallListChange(mCallList.getCallList());
-
- verify(mStatusBarNotifier).updateNotification(InCallState.INCOMING,
- mCallList.getCallList());
- verifyInCallActivityNotStarted();
- }
-
- /**
- * This behavior is required to ensure that the screen is turned on again by the restarting
- * activity.
- */
- public void testOnCallListChange_handlesCallWaitingWhenScreenOffShouldRestartActivity() {
- mCallList.setHasCall(Call.State.ACTIVE, true);
-
- mInCallPresenter.onCallListChange(mCallList.getCallList());
- mInCallPresenter.setActivity(mInCallActivity);
-
- // Pretend that there is a call waiting and the screen is off
- when(mInCallActivity.isDestroyed()).thenReturn(false);
- when(mInCallActivity.isFinishing()).thenReturn(false);
- when(mProximitySensor.isScreenReallyOff()).thenReturn(true);
- mCallList.setHasCall(Call.State.INCOMING, true);
-
- mInCallPresenter.onCallListChange(mCallList.getCallList());
- verify(mInCallActivity).finish();
- }
-
- /**
- * Verifies that the PENDING_OUTGOING -> IN_CALL transition brings up InCallActivity so
- * that it can display an error dialog.
- */
- public void testOnCallListChange_pendingOutgoingToInCallTransitionShowsUiForErrorDialog() {
- mCallList.setHasCall(Call.State.CONNECTING, true);
-
- mInCallPresenter.onCallListChange(mCallList.getCallList());
-
- mCallList.setHasCall(Call.State.CONNECTING, false);
- mCallList.setHasCall(Call.State.ACTIVE, true);
-
- mInCallPresenter.onCallListChange(mCallList.getCallList());
-
- verify(mContext).startActivity(InCallPresenter.getInstance().getInCallIntent(false, false));
- verifyIncomingCallNotificationNotSent();
- }
-
- /**
- * Verifies that if there is a call in the SELECT_PHONE_ACCOUNT state, InCallActivity is displayed
- * to display the account picker.
- */
- public void testOnCallListChange_noAccountProvidedForCallShowsUiForAccountPicker() {
- mCallList.setHasCall(Call.State.SELECT_PHONE_ACCOUNT, true);
- mInCallPresenter.onCallListChange(mCallList.getCallList());
-
- verify(mContext).startActivity(InCallPresenter.getInstance().getInCallIntent(false, false));
- verifyIncomingCallNotificationNotSent();
- }
-
- /**
- * Verifies that for an expected call state change (e.g. NO_CALLS -> PENDING_OUTGOING),
- * InCallActivity is not displayed.
- */
- public void testOnCallListChange_noCallsToPendingOutgoingDoesNotShowUi() {
- mCallList.setHasCall(Call.State.CONNECTING, true);
- mInCallPresenter.onCallListChange(mCallList.getCallList());
-
- verifyInCallActivityNotStarted();
- verifyIncomingCallNotificationNotSent();
- }
-
- public void testOnCallListChange_LastCallDisconnectedNoCallsLeftFinishesUi() {
- mCallList.setHasCall(Call.State.DISCONNECTED, true);
- mInCallPresenter.onCallListChange(mCallList.getCallList());
-
- mInCallPresenter.setActivity(mInCallActivity);
-
- verify(mInCallActivity, never()).finish();
-
- // Last remaining disconnected call is removed from CallList, activity should shut down.
- mCallList.setHasCall(Call.State.DISCONNECTED, false);
- mInCallPresenter.onCallListChange(mCallList.getCallList());
- verify(mInCallActivity).finish();
- }
-
-
- //TODO
- public void testCircularReveal_startsCircularRevealForOutgoingCalls() {
-
- }
-
- public void testCircularReveal_waitTillCircularRevealSentBeforeShowingCallCard() {
- }
-
- public void testHangupOngoingCall_disconnectsCallCorrectly() {
- }
-
- public void testAnswerIncomingCall() {
- }
-
- public void testDeclineIncomingCall() {
- }
-
- private void verifyInCallActivityNotStarted() {
- verify(mContext, never()).startActivity(Mockito.any(Intent.class));
- }
-
- private void verifyIncomingCallNotificationNotSent() {
- verify(mStatusBarNotifier, never()).updateNotification(Mockito.any(InCallState.class),
- Mockito.any(CallList.class));
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/LatencyReportTest.java b/InCallUI/tests/src/com/android/incallui/LatencyReportTest.java
deleted file mode 100644
index 9d8a5131b..000000000
--- a/InCallUI/tests/src/com/android/incallui/LatencyReportTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.incallui;
-
-import static com.android.incallui.LatencyReport.INVALID_TIME;
-
-import android.os.Bundle;
-import android.telecom.Connection;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-public class LatencyReportTest extends AndroidTestCase {
- public void testEmptyInit() {
- LatencyReport report = new LatencyReport();
- assertEquals(INVALID_TIME, report.getCreatedTimeMillis());
- assertEquals(INVALID_TIME, report.getTelecomRoutingStartTimeMillis());
- assertEquals(INVALID_TIME, report.getTelecomRoutingEndTimeMillis());
- assertTrue(report.getCallAddedTimeMillis() > 0);
- }
-
- public void testCallBlocking() {
- LatencyReport report = new LatencyReport();
- assertEquals(INVALID_TIME, report.getCallBlockingTimeMillis());
- report.onCallBlockingDone();
- assertTrue(report.getCallBlockingTimeMillis() > 0);
- }
-
- public void testNotificationShown() {
- LatencyReport report = new LatencyReport();
- assertEquals(INVALID_TIME, report.getCallNotificationTimeMillis());
- report.onNotificationShown();
- assertTrue(report.getCallNotificationTimeMillis() > 0);
- }
-
- public void testInCallUiShown() {
- LatencyReport report = new LatencyReport();
- assertEquals(INVALID_TIME, report.getInCallUiShownTimeMillis());
- report.onInCallUiShown(false);
- assertTrue(report.getInCallUiShownTimeMillis() > 0);
- assertFalse(report.getDidDisplayHeadsUpNotification());
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/MockCallListWrapper.java b/InCallUI/tests/src/com/android/incallui/MockCallListWrapper.java
deleted file mode 100644
index 369c4303f..000000000
--- a/InCallUI/tests/src/com/android/incallui/MockCallListWrapper.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.incallui;
-
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
-import android.telecom.PhoneAccountHandle;
-
-import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.util.HashSet;
-
-/**
- * Provides an instance of a mock CallList, and provides utility methods to put the CallList into
- * various states (e.g. incoming call, active call, call waiting).
- */
-public class MockCallListWrapper {
- private CallList mCallList;
- private HashSet<Integer> mCallSet = new HashSet<>();
-
- public MockCallListWrapper() {
- mCallList = Mockito.mock(CallList.class);
- mCallList = spy(new CallList());
- when(mCallList.getFirstCallWithState(anyInt())).thenAnswer(new Answer<Call>() {
- @Override
- public Call answer(InvocationOnMock i) throws Throwable {
- Object[] args = i.getArguments();
- final int state = (int) args[0];
- if (mCallSet.contains(state)) {
- return getMockCall(state);
- } else {
- return null;
- }
- }
- });
- }
-
- public CallList getCallList() {
- return mCallList;
- }
-
- public void setHasCall(int state, boolean hasCall) {
- if (hasCall) {
- mCallSet.add(state);
- } else {
- mCallSet.remove(state);
- }
- }
-
- private static Call getMockCall(int state) {
- return getMockCall(state, state != Call.State.SELECT_PHONE_ACCOUNT);
- }
-
- private static Call getMockCall(int state, boolean hasAccountHandle) {
- final Call call = Mockito.mock(Call.class);
- when(call.getState()).thenReturn(Integer.valueOf(state));
- if (hasAccountHandle) {
- when(call.getAccountHandle()).thenReturn(new PhoneAccountHandle(null, null));
- }
- return call;
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/ProximitySensorTest.java b/InCallUI/tests/src/com/android/incallui/ProximitySensorTest.java
deleted file mode 100644
index 1c8f34721..000000000
--- a/InCallUI/tests/src/com/android/incallui/ProximitySensorTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.incallui;
-
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import com.android.incallui.InCallPresenter.InCallState;
-
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@MediumTest
-public class ProximitySensorTest extends InstrumentationTestCase {
- @Mock private AccelerometerListener mAccelerometerListener;
- private MockCallListWrapper mCallList;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- System.setProperty("dexmaker.dexcache",
- getInstrumentation().getTargetContext().getCacheDir().getPath());
- MockitoAnnotations.initMocks(this);
- mCallList = new MockCallListWrapper();
- }
-
- public void testAccelerometerBehaviorOnDisplayChange() {
- final ProximitySensor proximitySensor =
- new ProximitySensor(
- getInstrumentation().getContext(),
- new AudioModeProvider(),
- mAccelerometerListener);
- verify(mAccelerometerListener, never()).enable(anyBoolean());
- proximitySensor.onStateChange(null, InCallState.OUTGOING, mCallList.getCallList());
- verify(mAccelerometerListener).enable(true);
- verify(mAccelerometerListener, never()).enable(false);
-
- proximitySensor.onDisplayStateChanged(false);
- verify(mAccelerometerListener).enable(true);
- verify(mAccelerometerListener).enable(false);
-
- proximitySensor.onDisplayStateChanged(true);
- verify(mAccelerometerListener, times(2)).enable(true);
- verify(mAccelerometerListener).enable(false);
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/StatusBarNotifierTest.java b/InCallUI/tests/src/com/android/incallui/StatusBarNotifierTest.java
deleted file mode 100644
index 4c55ddcc0..000000000
--- a/InCallUI/tests/src/com/android/incallui/StatusBarNotifierTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.incallui;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.incallui.ContactInfoCache.ContactCacheEntry;
-
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@MediumTest
-public class StatusBarNotifierTest extends AndroidTestCase {
-
- private static final String NAME_PRIMARY = "Full Name";
- private static final String NAME_ALTERNATIVE = "Name, Full";
- private static final String LOCATION = "US";
- private static final String NUMBER = "8006459001";
-
- @Mock private Call mCall;
- @Mock private ContactsPreferences mContactsPreferences;
- private ContactCacheEntry mUnlockedContactInfo;
- private ContactCacheEntry mLockedContactInfo;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
-
- Mockito.when(mContactsPreferences.getDisplayOrder())
- .thenReturn(ContactsPreferences.DISPLAY_ORDER_PRIMARY);
-
- // Unlocked all contact info is available
- mUnlockedContactInfo = new ContactCacheEntry();
- mUnlockedContactInfo.namePrimary = NAME_PRIMARY;
- mUnlockedContactInfo.nameAlternative = NAME_ALTERNATIVE;
- mUnlockedContactInfo.location = LOCATION;
- mUnlockedContactInfo.number = NUMBER;
-
- // Locked only number and location are available
- mLockedContactInfo = new ContactCacheEntry();
- mLockedContactInfo .location = LOCATION;
- mLockedContactInfo .number = NUMBER;
- }
-
- @Override
- public void tearDown() throws Exception {
- super.tearDown();
- ContactsPreferencesFactory.setTestInstance(null);
- }
-
- public void testGetContentTitle_ConferenceCall() {
- ContactsPreferencesFactory.setTestInstance(null);
- StatusBarNotifier statusBarNotifier = new StatusBarNotifier(mContext, null);
-
- Mockito.when(mCall.isConferenceCall()).thenReturn(true);
- Mockito.when(mCall.hasProperty(Mockito.anyInt())).thenReturn(false);
-
- assertEquals(mContext.getResources().getString(R.string.card_title_conf_call),
- statusBarNotifier.getContentTitle(null, mCall));
- }
-
- public void testGetContentTitle_Unlocked() {
- ContactsPreferencesFactory.setTestInstance(mContactsPreferences);
- StatusBarNotifier statusBarNotifier = new StatusBarNotifier(mContext, null);
- assertEquals(NAME_PRIMARY, statusBarNotifier.getContentTitle(mUnlockedContactInfo, mCall));
- }
-
- public void testGetContentTitle_Locked() {
- ContactsPreferencesFactory.setTestInstance(null);
- StatusBarNotifier statusBarNotifier = new StatusBarNotifier(mContext, null);
- assertEquals(NUMBER, statusBarNotifier.getContentTitle(mLockedContactInfo, mCall));
- }
-
- public void testGetContentTitle_EmptyPreferredName() {
- ContactCacheEntry contactCacheEntry = new ContactCacheEntry();
- contactCacheEntry.number = NUMBER;
- StatusBarNotifier statusBarNotifier = new StatusBarNotifier(mContext, null);
- assertEquals(NUMBER, statusBarNotifier.getContentTitle(contactCacheEntry, mCall));
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/TestTelecomCall.java b/InCallUI/tests/src/com/android/incallui/TestTelecomCall.java
deleted file mode 100644
index 48ac6e18f..000000000
--- a/InCallUI/tests/src/com/android/incallui/TestTelecomCall.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * 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.incallui;
-
-import com.google.common.base.Preconditions;
-
-import android.content.Context;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.telecom.DisconnectCause;
-import android.telecom.GatewayInfo;
-import android.telecom.PhoneAccountHandle;
-import android.telecom.StatusHints;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-/**
- * Wrapper class which uses reflection to create instances of {@link android.telecom.Call} for use
- * with unit testing. Since {@link android.telecom.Call} is final, it cannot be mocked using
- * mockito, and since all setter methods are hidden, it is necessary to use reflection. In the
- * future, it would be desirable to replace this if a different mocking solution is used.
- */
-public class TestTelecomCall {
-
- private android.telecom.Call mCall;
-
- public static @Nullable TestTelecomCall createInstance(String callId,
- Uri handle,
- int handlePresentation,
- String callerDisplayName,
- int callerDisplayNamePresentation,
- PhoneAccountHandle accountHandle,
- int capabilities,
- int properties,
- DisconnectCause disconnectCause,
- long connectTimeMillis,
- GatewayInfo gatewayInfo,
- int videoState,
- StatusHints statusHints,
- Bundle extras,
- Bundle intentExtras) {
-
- try {
- // Phone and InCall adapter are @hide, so we cannot refer to them directly.
- Class<?> phoneClass = Class.forName("android.telecom.Phone");
- Class<?> incallAdapterClass = Class.forName("android.telecom.InCallAdapter");
- Class<?> callClass = android.telecom.Call.class;
- Constructor<?> cons = callClass
- .getDeclaredConstructor(phoneClass, String.class, incallAdapterClass);
- cons.setAccessible(true);
-
- android.telecom.Call call = (android.telecom.Call) cons.newInstance(null, callId, null);
-
- // Create an instance of the call details.
- Class<?> callDetailsClass = android.telecom.Call.Details.class;
- Constructor<?> detailsCons = callDetailsClass.getDeclaredConstructor(
- String.class, /* telecomCallId */
- Uri.class, /* handle */
- int.class, /* handlePresentation */
- String.class, /* callerDisplayName */
- int.class, /* callerDisplayNamePresentation */
- PhoneAccountHandle.class, /* accountHandle */
- int.class, /* capabilities */
- int.class, /* properties */
- DisconnectCause.class, /* disconnectCause */
- long.class, /* connectTimeMillis */
- GatewayInfo.class, /* gatewayInfo */
- int.class, /* videoState */
- StatusHints.class, /* statusHints */
- Bundle.class, /* extras */
- Bundle.class /* intentExtras */);
- detailsCons.setAccessible(true);
-
- android.telecom.Call.Details details = (android.telecom.Call.Details)
- detailsCons.newInstance(callId, handle, handlePresentation, callerDisplayName,
- callerDisplayNamePresentation, accountHandle, capabilities, properties,
- disconnectCause, connectTimeMillis, gatewayInfo, videoState,
- statusHints,
- extras, intentExtras);
-
- // Finally, set this as the details of the call.
- Field detailsField = call.getClass().getDeclaredField("mDetails");
- detailsField.setAccessible(true);
- detailsField.set(call, details);
-
- return new TestTelecomCall(call);
- } catch (NoSuchMethodException nsm) {
- return null;
- } catch (ClassNotFoundException cnf) {
- return null;
- } catch (IllegalAccessException e) {
- return null;
- } catch (InstantiationException e) {
- return null;
- } catch (InvocationTargetException e) {
- return null;
- } catch (NoSuchFieldException e) {
- return null;
- }
- }
-
- private TestTelecomCall(android.telecom.Call call) {
- mCall = call;
- }
-
- public android.telecom.Call getCall() {
- return mCall;
- }
-
- public void forceDetailsUpdate() {
- Preconditions.checkNotNull(mCall);
-
- try {
- Method method = mCall.getClass().getDeclaredMethod("fireDetailsChanged",
- android.telecom.Call.Details.class);
- method.setAccessible(true);
- method.invoke(mCall, mCall.getDetails());
- } catch (NoSuchMethodException e) {
- Log.e(this, "forceDetailsUpdate", e);
- } catch (InvocationTargetException e) {
- Log.e(this, "forceDetailsUpdate", e);
- } catch (IllegalAccessException e) {
- Log.e(this, "forceDetailsUpdate", e);
- }
- }
-
- public void setCapabilities(int capabilities) {
- Preconditions.checkNotNull(mCall);
- try {
- Field field = mCall.getDetails().getClass().getDeclaredField("mCallCapabilities");
- field.setAccessible(true);
- field.set(mCall.getDetails(), capabilities);
- } catch (IllegalAccessException e) {
- Log.e(this, "setProperties", e);
- } catch (NoSuchFieldException e) {
- Log.e(this, "setProperties", e);
- }
- }
-
- public void setCall(android.telecom.Call call) {
- mCall = call;
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/async/SingleProdThreadExecutor.java b/InCallUI/tests/src/com/android/incallui/async/SingleProdThreadExecutor.java
deleted file mode 100644
index 5717c9478..000000000
--- a/InCallUI/tests/src/com/android/incallui/async/SingleProdThreadExecutor.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.incallui.async;
-
-import java.util.concurrent.Executors;
-
-import javax.annotation.concurrent.ThreadSafe;
-
-/**
- * {@link PausableExecutor} for use in tests. It is intended to be used between one test thread
- * and one prod thread. See {@link com.android.incallui.ringtone.InCallTonePlayerTest} for example
- * usage.
- */
-@ThreadSafe
-public final class SingleProdThreadExecutor implements PausableExecutor {
-
- private int mMilestonesReached;
- private int mMilestonesAcked;
- private boolean mHasAckedAllMilestones;
-
- @Override
- public synchronized void milestone() {
- ++mMilestonesReached;
- notify();
- while (!mHasAckedAllMilestones && mMilestonesReached > mMilestonesAcked) {
- try {
- wait();
- } catch (InterruptedException e) {}
- }
- }
-
- @Override
- public synchronized void ackMilestoneForTesting() {
- ++mMilestonesAcked;
- notify();
- }
-
- @Override
- public synchronized void ackAllMilestonesForTesting() {
- mHasAckedAllMilestones = true;
- notify();
- }
-
- @Override
- public synchronized void awaitMilestoneForTesting() throws InterruptedException {
- while (!mHasAckedAllMilestones && mMilestonesReached <= mMilestonesAcked) {
- wait();
- }
- }
-
- @Override
- public synchronized void execute(Runnable command) {
- Executors.newSingleThreadExecutor().execute(command);
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/ringtone/DialerRingtoneManagerTest.java b/InCallUI/tests/src/com/android/incallui/ringtone/DialerRingtoneManagerTest.java
deleted file mode 100644
index 01db20272..000000000
--- a/InCallUI/tests/src/com/android/incallui/ringtone/DialerRingtoneManagerTest.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * 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.incallui.ringtone;
-
-import android.media.RingtoneManager;
-import android.net.Uri;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.incallui.Call;
-import com.android.incallui.Call.State;
-import com.android.incallui.CallList;
-
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-public class DialerRingtoneManagerTest extends AndroidTestCase {
-
- private static final Uri RINGTONE_URI = RingtoneManager
- .getDefaultUri(RingtoneManager.TYPE_RINGTONE);
-
- @Mock private InCallTonePlayer mInCallTonePlayer;
- @Mock private CallList mCallList;
- @Mock private Call mCall;
- private DialerRingtoneManager mRingtoneManagerEnabled;
- private DialerRingtoneManager mRingtoneManagerDisabled;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
- mRingtoneManagerEnabled = new DialerRingtoneManager(mInCallTonePlayer, mCallList);
- mRingtoneManagerEnabled.setDialerRingingEnabledForTesting(true);
- mRingtoneManagerDisabled = new DialerRingtoneManager(mInCallTonePlayer, mCallList);
- mRingtoneManagerDisabled.setDialerRingingEnabledForTesting(false);
- }
-
- public void testNullInCallTonePlayer() {
- try {
- new DialerRingtoneManager(null, mCallList);
- fail();
- } catch (NullPointerException e) {}
- }
-
- public void testNullCallList() {
- try {
- new DialerRingtoneManager(mInCallTonePlayer, null);
- fail();
- } catch (NullPointerException e) {}
- }
-
- public void testShouldPlayRingtone_M() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- assertFalse(mRingtoneManagerEnabled.shouldPlayRingtone(0, RINGTONE_URI));
- }
-
- public void testShouldPlayRingtone_N_NullUri() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- assertFalse(mRingtoneManagerEnabled.shouldPlayRingtone(State.INCOMING, null));
- }
-
- public void testShouldPlayRingtone_N_Disabled() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- assertFalse(mRingtoneManagerDisabled.shouldPlayRingtone(State.INCOMING, RINGTONE_URI));
- }
-
- public void testShouldPlayRingtone_N_NotIncoming() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- assertFalse(mRingtoneManagerEnabled.shouldPlayRingtone(State.ACTIVE, RINGTONE_URI));
- }
-
- // Specific case for call waiting since that needs its own sound
- public void testShouldPlayRingtone_N_CallWaitingByState() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- assertFalse(mRingtoneManagerEnabled.shouldPlayRingtone(State.CALL_WAITING, RINGTONE_URI));
- }
-
- public void testShouldPlayRingtone_N_CallWaitingByActiveCall() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- Mockito.when(mCallList.getActiveCall()).thenReturn(mCall);
- assertFalse(mRingtoneManagerEnabled.shouldPlayRingtone(State.INCOMING, RINGTONE_URI));
- }
-
- public void testShouldPlayRingtone_N() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- assertTrue(mRingtoneManagerEnabled.shouldPlayRingtone(State.INCOMING, RINGTONE_URI));
- }
-
- public void testShouldPlayCallWaitingTone_M() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- assertFalse(mRingtoneManagerEnabled.shouldPlayCallWaitingTone(0));
- }
-
- public void testShouldPlayCallWaitingTone_N_Disabled() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- assertFalse(mRingtoneManagerDisabled.shouldPlayCallWaitingTone(State.CALL_WAITING));
- }
-
- public void testShouldPlayCallWaitingTone_N_NotCallWaiting() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- assertFalse(mRingtoneManagerEnabled.shouldPlayCallWaitingTone(State.ACTIVE));
- }
-
- // Specific case for incoming since it plays its own sound
- public void testShouldPlayCallWaitingTone_N_Incoming() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- assertFalse(mRingtoneManagerEnabled.shouldPlayCallWaitingTone(State.INCOMING));
- }
-
- public void testShouldPlayCallWaitingTone_N_AlreadyPlaying() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- Mockito.when(mInCallTonePlayer.isPlayingTone()).thenReturn(true);
- assertFalse(mRingtoneManagerEnabled.shouldPlayCallWaitingTone(State.CALL_WAITING));
- }
-
- public void testShouldPlayCallWaitingTone_N_ByState() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- assertTrue(mRingtoneManagerEnabled.shouldPlayCallWaitingTone(State.CALL_WAITING));
- }
-
- public void testShouldPlayCallWaitingTone_N_ByActiveCall() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- Mockito.when(mCallList.getActiveCall()).thenReturn(mCall);
- assertTrue(mRingtoneManagerEnabled.shouldPlayCallWaitingTone(State.INCOMING));
- }
-
- public void testPlayCallWaitingTone_M() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- mRingtoneManagerEnabled.playCallWaitingTone();
- Mockito.verify(mInCallTonePlayer, Mockito.never()).play(Mockito.anyInt());
- }
-
- public void testPlayCallWaitingTone_N_NotEnabled() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- mRingtoneManagerDisabled.playCallWaitingTone();
- Mockito.verify(mInCallTonePlayer, Mockito.never()).play(Mockito.anyInt());
- }
-
- public void testPlayCallWaitingTone_N() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- mRingtoneManagerEnabled.playCallWaitingTone();
- Mockito.verify(mInCallTonePlayer).play(Mockito.anyInt());
- }
-
- public void testStopCallWaitingTone_M() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- mRingtoneManagerEnabled.stopCallWaitingTone();
- Mockito.verify(mInCallTonePlayer, Mockito.never()).stop();
- }
-
- public void testStopCallWaitingTone_N_NotEnabled() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- mRingtoneManagerDisabled.stopCallWaitingTone();
- Mockito.verify(mInCallTonePlayer, Mockito.never()).stop();
- }
-
- public void testStopCallWaitingTone_N() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- mRingtoneManagerEnabled.stopCallWaitingTone();
- Mockito.verify(mInCallTonePlayer).stop();
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/ringtone/InCallTonePlayerTest.java b/InCallUI/tests/src/com/android/incallui/ringtone/InCallTonePlayerTest.java
deleted file mode 100644
index bde5c50e4..000000000
--- a/InCallUI/tests/src/com/android/incallui/ringtone/InCallTonePlayerTest.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * 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.incallui.ringtone;
-
-import android.media.AudioManager;
-import android.media.ToneGenerator;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.incallui.async.PausableExecutor;
-import com.android.incallui.async.SingleProdThreadExecutor;
-
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-public class InCallTonePlayerTest extends AndroidTestCase {
-
- @Mock private ToneGeneratorFactory mToneGeneratorFactory;
- @Mock private ToneGenerator mToneGenerator;
- private InCallTonePlayer mInCallTonePlayer;
-
- /*
- * InCallTonePlayer milestones:
- * 1) After tone starts playing
- * 2) After tone finishes waiting (could have timed out)
- * 3) After cleaning up state to allow new tone to play
- */
- private PausableExecutor mExecutor;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
- Mockito.when(mToneGeneratorFactory.newInCallToneGenerator(Mockito.anyInt(),
- Mockito.anyInt())).thenReturn(mToneGenerator);
- mExecutor = new SingleProdThreadExecutor();
- mInCallTonePlayer = new InCallTonePlayer(mToneGeneratorFactory, mExecutor);
- }
-
- @Override
- public void tearDown() throws Exception {
- super.tearDown();
- // Stop any playing so the InCallTonePlayer isn't stuck waiting for the tone to complete
- mInCallTonePlayer.stop();
- // Ack all milestones to ensure that the prod thread doesn't block forever
- mExecutor.ackAllMilestonesForTesting();
- }
-
- public void testIsPlayingTone_False() {
- assertFalse(mInCallTonePlayer.isPlayingTone());
- }
-
- public void testIsPlayingTone_True() throws InterruptedException {
- mInCallTonePlayer.play(InCallTonePlayer.TONE_CALL_WAITING);
- mExecutor.awaitMilestoneForTesting();
-
- assertTrue(mInCallTonePlayer.isPlayingTone());
- }
-
- public void testPlay_InvalidTone() {
- try {
- mInCallTonePlayer.play(Integer.MIN_VALUE);
- fail();
- } catch (IllegalArgumentException e) {}
- }
-
- public void testPlay_CurrentlyPlaying() throws InterruptedException {
- mInCallTonePlayer.play(InCallTonePlayer.TONE_CALL_WAITING);
- mExecutor.awaitMilestoneForTesting();
- try {
- mInCallTonePlayer.play(InCallTonePlayer.TONE_CALL_WAITING);
- fail();
- } catch (IllegalStateException e) {}
- }
-
- public void testPlay_VoiceCallStream() throws InterruptedException {
- mInCallTonePlayer.play(InCallTonePlayer.TONE_CALL_WAITING);
- mExecutor.awaitMilestoneForTesting();
- Mockito.verify(mToneGeneratorFactory).newInCallToneGenerator(AudioManager.STREAM_VOICE_CALL,
- InCallTonePlayer.VOLUME_RELATIVE_HIGH_PRIORITY);
- }
-
- public void testPlay_Single() throws InterruptedException {
- mInCallTonePlayer.play(InCallTonePlayer.TONE_CALL_WAITING);
- mExecutor.awaitMilestoneForTesting();
- mExecutor.ackMilestoneForTesting();
- mInCallTonePlayer.stop();
- mExecutor.ackMilestoneForTesting();
- mExecutor.awaitMilestoneForTesting();
- mExecutor.ackMilestoneForTesting();
-
- Mockito.verify(mToneGenerator).startTone(ToneGenerator.TONE_SUP_CALL_WAITING);
- }
-
- public void testPlay_Consecutive() throws InterruptedException {
- mInCallTonePlayer.play(InCallTonePlayer.TONE_CALL_WAITING);
- mExecutor.awaitMilestoneForTesting();
- mExecutor.ackMilestoneForTesting();
- // Prevent waiting forever
- mInCallTonePlayer.stop();
- mExecutor.ackMilestoneForTesting();
- mExecutor.awaitMilestoneForTesting();
- mExecutor.ackMilestoneForTesting();
-
- mInCallTonePlayer.play(InCallTonePlayer.TONE_CALL_WAITING);
- mExecutor.awaitMilestoneForTesting();
- mExecutor.ackMilestoneForTesting();
- mInCallTonePlayer.stop();
- mExecutor.ackMilestoneForTesting();
- mExecutor.awaitMilestoneForTesting();
- mExecutor.ackMilestoneForTesting();
-
- Mockito.verify(mToneGenerator, Mockito.times(2))
- .startTone(ToneGenerator.TONE_SUP_CALL_WAITING);
- }
-
- public void testStop_NotPlaying() {
- // No crash
- mInCallTonePlayer.stop();
- }
-
- public void testStop() throws InterruptedException {
- mInCallTonePlayer.play(InCallTonePlayer.TONE_CALL_WAITING);
- mExecutor.awaitMilestoneForTesting();
-
- mInCallTonePlayer.stop();
- mExecutor.ackMilestoneForTesting();
- mExecutor.awaitMilestoneForTesting();
-
- assertFalse(mInCallTonePlayer.isPlayingTone());
- }
-}
diff --git a/InCallUI/tests/src/com/android/incallui/spam/SpamCallListListenerTest.java b/InCallUI/tests/src/com/android/incallui/spam/SpamCallListListenerTest.java
deleted file mode 100644
index fb0b460b6..000000000
--- a/InCallUI/tests/src/com/android/incallui/spam/SpamCallListListenerTest.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * 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.incallui.spam;
-
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.any;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.provider.CallLog;
-import android.telecom.DisconnectCause;
-import android.test.InstrumentationTestCase;
-
-import com.android.contacts.common.test.mocks.ContactsMockContext;
-import com.android.contacts.common.test.mocks.MockContentProvider;
-import com.android.dialer.calllog.CallLogAsyncTaskUtil;
-import com.android.dialer.util.AsyncTaskExecutors;
-import com.android.dialer.util.FakeAsyncTaskExecutor;
-import com.android.dialer.util.TelecomUtil;
-import com.android.incallui.Call;
-import com.android.incallui.Call.CallHistoryStatus;
-import com.android.incallui.Call.LogState;
-
-public class SpamCallListListenerTest extends InstrumentationTestCase {
- private static final String NUMBER = "+18005657862";
- private static final int DURATION = 100;
-
- private TestSpamCallListListener mListener;
- private FakeAsyncTaskExecutor mFakeAsyncTaskExecutor;
- private ContactsMockContext mContext;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mContext = new ContactsMockContext(getInstrumentation().getContext(), CallLog.AUTHORITY);
- mListener = new TestSpamCallListListener(mContext);
- mFakeAsyncTaskExecutor = new FakeAsyncTaskExecutor(getInstrumentation());
- AsyncTaskExecutors.setFactoryForTest(mFakeAsyncTaskExecutor.getFactory());
- }
-
- @Override
- public void tearDown() throws Exception {
- AsyncTaskExecutors.setFactoryForTest(null);
- CallLogAsyncTaskUtil.resetForTest();
- super.tearDown();
- }
-
- public void testOutgoingCall() {
- Call call = getMockCall(NUMBER, false, 0, Call.CALL_HISTORY_STATUS_NOT_PRESENT,
- LogState.LOOKUP_UNKNOWN, DisconnectCause.REMOTE);
- mListener.onDisconnect(call);
- assertFalse(mListener.mShowNotificationCalled);
- }
-
- public void testIncomingCall_UnknownNumber() {
- Call call = getMockCall(null, true, DURATION, Call.CALL_HISTORY_STATUS_NOT_PRESENT,
- LogState.LOOKUP_UNKNOWN, DisconnectCause.REMOTE);
- mListener.onDisconnect(call);
- assertFalse(mListener.mShowNotificationCalled);
- }
-
- public void testIncomingCall_Rejected() {
- Call call = getMockCall(NUMBER, true, 0, Call.CALL_HISTORY_STATUS_NOT_PRESENT,
- LogState.LOOKUP_UNKNOWN, DisconnectCause.REJECTED);
- mListener.onDisconnect(call);
- assertFalse(mListener.mShowNotificationCalled);
- }
- public void testIncomingCall_HangUpLocal() {
- Call call = getMockCall(NUMBER, true, DURATION, Call.CALL_HISTORY_STATUS_NOT_PRESENT,
- LogState.LOOKUP_UNKNOWN, DisconnectCause.LOCAL);
- mListener.onDisconnect(call);
- assertTrue(mListener.mShowNotificationCalled);
- }
-
- public void testIncomingCall_HangUpRemote() {
- Call call = getMockCall(NUMBER, true, DURATION, Call.CALL_HISTORY_STATUS_NOT_PRESENT,
- LogState.LOOKUP_UNKNOWN, DisconnectCause.REMOTE);
- mListener.onDisconnect(call);
- assertTrue(mListener.mShowNotificationCalled);
- }
-
- public void testIncomingCall_ValidNumber_NotInCallHistory_InContacts() {
- Call call = getMockCall(NUMBER, true, 0, Call.CALL_HISTORY_STATUS_NOT_PRESENT,
- LogState.LOOKUP_LOCAL_CONTACT, DisconnectCause.REJECTED);
- mListener.onDisconnect(call);
- assertFalse(mListener.mShowNotificationCalled);
- }
-
- public void testIncomingCall_ValidNumber_InCallHistory_InContacts() {
- Call call = getMockCall(NUMBER, true, 0, Call.CALL_HISTORY_STATUS_PRESENT,
- LogState.LOOKUP_LOCAL_CONTACT, DisconnectCause.REJECTED);
- mListener.onDisconnect(call);
- assertFalse(mListener.mShowNotificationCalled);
- }
-
- public void testIncomingCall_ValidNumber_InCallHistory_NotInContacts() {
- Call call = getMockCall(NUMBER, true, 0, Call.CALL_HISTORY_STATUS_PRESENT,
- LogState.LOOKUP_UNKNOWN, DisconnectCause.REJECTED);
- mListener.onDisconnect(call);
- assertFalse(mListener.mShowNotificationCalled);
- }
-
- public void testIncomingCall_ValidNumber_NotInCallHistory_NotInContacts() throws Throwable {
- Call call = getMockCall(NUMBER, true, DURATION, Call.CALL_HISTORY_STATUS_NOT_PRESENT,
- LogState.LOOKUP_UNKNOWN, DisconnectCause.LOCAL);
- mListener.onDisconnect(call);
- assertTrue(mListener.mShowNotificationCalled);
- }
-
- public void testIncomingCall_CheckCallHistory_NumberExists() throws Throwable {
- final Call call = getMockCall(NUMBER, true, DURATION, Call.CALL_HISTORY_STATUS_UNKNOWN,
- LogState.LOOKUP_UNKNOWN, DisconnectCause.LOCAL);
- expectCallLogQuery(NUMBER, true);
- incomingCall(call);
- verify(call).setCallHistoryStatus(eq(Call.CALL_HISTORY_STATUS_PRESENT));
- assertFalse(mListener.mShowNotificationCalled);
- }
-
- public void testIncomingCall_CheckCallHistory_NumberNotExists() throws Throwable {
- final Call call = getMockCall(NUMBER, true, DURATION, Call.CALL_HISTORY_STATUS_UNKNOWN,
- LogState.LOOKUP_UNKNOWN, DisconnectCause.LOCAL);
- expectCallLogQuery(NUMBER, false);
- incomingCall(call);
- verify(call).setCallHistoryStatus(eq(Call.CALL_HISTORY_STATUS_NOT_PRESENT));
- assertTrue(mListener.mShowNotificationCalled);
- }
-
- private void incomingCall(final Call call) throws Throwable {
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mListener.onIncomingCall(call);
- }
- });
- getInstrumentation().waitForIdleSync();
- mFakeAsyncTaskExecutor.runTask(CallLogAsyncTaskUtil.Tasks.GET_NUMBER_IN_CALL_HISTORY);
- mListener.onDisconnect(call);
- }
-
- private void expectCallLogQuery(String number, boolean inCallHistory) {
- MockContentProvider.Query query = mContext.getContactsProvider()
- .expectQuery(TelecomUtil.getCallLogUri(mContext))
- .withSelection(CallLog.Calls.NUMBER + " = ?", number)
- .withProjection(CallLog.Calls._ID)
- .withAnySortOrder();
- ContentValues values = new ContentValues();
- values.put(CallLog.Calls.NUMBER, number);
- if (inCallHistory) {
- query.returnRow(values);
- } else {
- query.returnEmptyCursor();
- }
- }
-
- private static Call getMockCall(String number,
- boolean isIncoming,
- int duration,
- @CallHistoryStatus int callHistoryStatus,
- int contactLookupResult,
- int disconnectCause) {
- Call call = mock(Call.class);
- LogState logState = new LogState();
- logState.isIncoming = isIncoming;
- logState.duration = duration;
- logState.contactLookupResult = contactLookupResult;
- when(call.getDisconnectCause()).thenReturn(new DisconnectCause(disconnectCause));
- when(call.getLogState()).thenReturn(logState);
- when(call.getNumber()).thenReturn(number);
- doCallRealMethod().when(call).setCallHistoryStatus(anyInt());
- when(call.getCallHistoryStatus()).thenCallRealMethod();
- call.setCallHistoryStatus(callHistoryStatus);
- return call;
- }
-
- private static class TestSpamCallListListener extends SpamCallListListener {
- private boolean mShowNotificationCalled;
-
- public TestSpamCallListListener(Context context) {
- super(context);
- }
-
- void showNotification(String number) {
- mShowNotificationCalled = true;
- }
- }
-} \ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..c5b1efa7a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/assets/product/res/drawable-hdpi/product_logo_avatar_anonymous_color_120.png b/assets/product/res/drawable-hdpi/product_logo_avatar_anonymous_color_120.png
new file mode 100644
index 000000000..70d3011dd
--- /dev/null
+++ b/assets/product/res/drawable-hdpi/product_logo_avatar_anonymous_color_120.png
Binary files differ
diff --git a/assets/product/res/drawable-hdpi/product_logo_avatar_anonymous_white_color_120.png b/assets/product/res/drawable-hdpi/product_logo_avatar_anonymous_white_color_120.png
new file mode 100644
index 000000000..4068d5aa7
--- /dev/null
+++ b/assets/product/res/drawable-hdpi/product_logo_avatar_anonymous_white_color_120.png
Binary files differ
diff --git a/assets/product/res/drawable-mdpi/product_logo_avatar_anonymous_color_120.png b/assets/product/res/drawable-mdpi/product_logo_avatar_anonymous_color_120.png
new file mode 100644
index 000000000..60d3c3a49
--- /dev/null
+++ b/assets/product/res/drawable-mdpi/product_logo_avatar_anonymous_color_120.png
Binary files differ
diff --git a/assets/product/res/drawable-mdpi/product_logo_avatar_anonymous_white_color_120.png b/assets/product/res/drawable-mdpi/product_logo_avatar_anonymous_white_color_120.png
new file mode 100644
index 000000000..0524cf053
--- /dev/null
+++ b/assets/product/res/drawable-mdpi/product_logo_avatar_anonymous_white_color_120.png
Binary files differ
diff --git a/assets/product/res/drawable-xhdpi/product_logo_avatar_anonymous_color_120.png b/assets/product/res/drawable-xhdpi/product_logo_avatar_anonymous_color_120.png
new file mode 100644
index 000000000..ec99ca6b8
--- /dev/null
+++ b/assets/product/res/drawable-xhdpi/product_logo_avatar_anonymous_color_120.png
Binary files differ
diff --git a/assets/product/res/drawable-xhdpi/product_logo_avatar_anonymous_white_color_120.png b/assets/product/res/drawable-xhdpi/product_logo_avatar_anonymous_white_color_120.png
new file mode 100644
index 000000000..ba27ee76b
--- /dev/null
+++ b/assets/product/res/drawable-xhdpi/product_logo_avatar_anonymous_white_color_120.png
Binary files differ
diff --git a/assets/product/res/drawable-xxhdpi/product_logo_avatar_anonymous_color_120.png b/assets/product/res/drawable-xxhdpi/product_logo_avatar_anonymous_color_120.png
new file mode 100644
index 000000000..2b009a3da
--- /dev/null
+++ b/assets/product/res/drawable-xxhdpi/product_logo_avatar_anonymous_color_120.png
Binary files differ
diff --git a/assets/product/res/drawable-xxhdpi/product_logo_avatar_anonymous_white_color_120.png b/assets/product/res/drawable-xxhdpi/product_logo_avatar_anonymous_white_color_120.png
new file mode 100644
index 000000000..2dc724899
--- /dev/null
+++ b/assets/product/res/drawable-xxhdpi/product_logo_avatar_anonymous_white_color_120.png
Binary files differ
diff --git a/assets/product/res/drawable-xxxhdpi/product_logo_avatar_anonymous_color_120.png b/assets/product/res/drawable-xxxhdpi/product_logo_avatar_anonymous_color_120.png
new file mode 100644
index 000000000..4b111b1ca
--- /dev/null
+++ b/assets/product/res/drawable-xxxhdpi/product_logo_avatar_anonymous_color_120.png
Binary files differ
diff --git a/assets/product/res/drawable-xxxhdpi/product_logo_avatar_anonymous_white_color_120.png b/assets/product/res/drawable-xxxhdpi/product_logo_avatar_anonymous_white_color_120.png
new file mode 100644
index 000000000..230be8ceb
--- /dev/null
+++ b/assets/product/res/drawable-xxxhdpi/product_logo_avatar_anonymous_white_color_120.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_arrow_back_white_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_arrow_back_white_24.png
new file mode 100644
index 000000000..cd1972677
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_arrow_back_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_arrow_drop_down_white_18.png b/assets/quantum/res/drawable-hdpi/quantum_ic_arrow_drop_down_white_18.png
new file mode 100644
index 000000000..41541bb0d
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_arrow_drop_down_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_bluetooth_audio_grey600_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_bluetooth_audio_grey600_24.png
new file mode 100644
index 000000000..ec2349ca8
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_bluetooth_audio_grey600_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_bluetooth_audio_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_bluetooth_audio_white_36.png
new file mode 100644
index 000000000..398f0a938
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_bluetooth_audio_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_call_end_white_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_call_end_white_24.png
new file mode 100644
index 000000000..625b827c4
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_call_end_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_call_end_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_call_end_white_36.png
new file mode 100644
index 000000000..51456d3d5
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_call_end_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_call_merge_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_call_merge_white_36.png
new file mode 100644
index 000000000..b7aba8072
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_call_merge_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_call_white_18.png b/assets/quantum/res/drawable-hdpi/quantum_ic_call_white_18.png
new file mode 100644
index 000000000..0bdc56be6
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_call_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_call_white_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_call_white_24.png
new file mode 100644
index 000000000..4dc506515
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_call_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_camera_alt_white_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_camera_alt_white_24.png
new file mode 100644
index 000000000..497c88ca8
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_camera_alt_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_camera_alt_white_48.png b/assets/quantum/res/drawable-hdpi/quantum_ic_camera_alt_white_48.png
new file mode 100644
index 000000000..c8e69dceb
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_camera_alt_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_camera_front_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_camera_front_white_36.png
new file mode 100644
index 000000000..f8394dc4f
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_camera_front_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_camera_rear_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_camera_rear_white_36.png
new file mode 100644
index 000000000..0354d41ca
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_camera_rear_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_check_black_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_check_black_24.png
new file mode 100644
index 000000000..e802d90ae
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_check_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_check_black_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_check_black_36.png
new file mode 100644
index 000000000..4992b76b0
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_check_black_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_check_white_48.png b/assets/quantum/res/drawable-hdpi/quantum_ic_check_white_48.png
new file mode 100644
index 000000000..2c2ad771f
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_check_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_close_white_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_close_white_24.png
new file mode 100644
index 000000000..ceb1a1eeb
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_close_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_close_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_close_white_36.png
new file mode 100644
index 000000000..86bd673af
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_close_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_dialpad_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_dialpad_white_36.png
new file mode 100644
index 000000000..82710e72a
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_dialpad_white_36.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_forward_white_24dp.png b/assets/quantum/res/drawable-hdpi/quantum_ic_forward_white_24.png
index a0711d377..a0711d377 100644
--- a/InCallUI/res/drawable-hdpi/ic_forward_white_24dp.png
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_forward_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_fullscreen_exit_white_48.png b/assets/quantum/res/drawable-hdpi/quantum_ic_fullscreen_exit_white_48.png
new file mode 100644
index 000000000..159bea7fd
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_fullscreen_exit_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_fullscreen_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_fullscreen_white_36.png
new file mode 100644
index 000000000..9e3b97cac
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_fullscreen_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_fullscreen_white_48.png b/assets/quantum/res/drawable-hdpi/quantum_ic_fullscreen_white_48.png
new file mode 100644
index 000000000..9b8131124
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_fullscreen_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_group_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_group_white_36.png
new file mode 100644
index 000000000..cbdef2f3e
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_group_white_36.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_hd_24dp.png b/assets/quantum/res/drawable-hdpi/quantum_ic_hd_white_24.png
index 35bf51a4f..35bf51a4f 100644
--- a/InCallUI/res/drawable-hdpi/ic_hd_24dp.png
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_hd_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_headset_grey600_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_headset_grey600_24.png
new file mode 100644
index 000000000..e859c2f31
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_headset_grey600_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_headset_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_headset_white_36.png
new file mode 100644
index 000000000..f77f24767
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_headset_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_message_white_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_message_white_24.png
new file mode 100644
index 000000000..57177b7c6
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_message_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_mic_off_black_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_mic_off_black_24.png
new file mode 100644
index 000000000..1755dbf3f
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_mic_off_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_mic_off_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_mic_off_white_36.png
new file mode 100644
index 000000000..203cb8a9f
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_mic_off_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_network_wifi_white_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_network_wifi_white_24.png
new file mode 100644
index 000000000..8df91f236
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_network_wifi_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_pause_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_pause_white_36.png
new file mode 100644
index 000000000..1d024393a
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_pause_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_person_add_white_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_person_add_white_24.png
new file mode 100644
index 000000000..10ae5a70c
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_person_add_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_photo_library_white_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_photo_library_white_24.png
new file mode 100644
index 000000000..c4a2229e9
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_photo_library_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_photo_white_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_photo_white_24.png
new file mode 100644
index 000000000..b414cf5b6
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_photo_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_photo_white_48.png b/assets/quantum/res/drawable-hdpi/quantum_ic_photo_white_48.png
new file mode 100644
index 000000000..f9f1defa6
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_photo_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_report_white_18.png b/assets/quantum/res/drawable-hdpi/quantum_ic_report_white_18.png
new file mode 100644
index 000000000..f0bb6f5be
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_report_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_send_black_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_send_black_24.png
new file mode 100644
index 000000000..f5518772a
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_send_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_send_white_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_send_white_24.png
new file mode 100644
index 000000000..5d4ad4b02
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_send_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_swap_calls_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_swap_calls_white_36.png
new file mode 100644
index 000000000..8c3a0edaa
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_swap_calls_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_switch_camera_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_switch_camera_white_36.png
new file mode 100644
index 000000000..21b4e4388
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_switch_camera_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_switch_video_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_switch_video_white_36.png
new file mode 100644
index 000000000..029b07736
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_switch_video_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_undo_white_48.png b/assets/quantum/res/drawable-hdpi/quantum_ic_undo_white_48.png
new file mode 100644
index 000000000..4366bb082
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_undo_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_videocam_off_white_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_videocam_off_white_24.png
new file mode 100644
index 000000000..aaf5ac208
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_videocam_off_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_videocam_off_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_videocam_off_white_36.png
new file mode 100644
index 000000000..f2e461a9f
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_videocam_off_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_videocam_white_18.png b/assets/quantum/res/drawable-hdpi/quantum_ic_videocam_white_18.png
new file mode 100644
index 000000000..abf478ada
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_videocam_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_videocam_white_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_videocam_white_24.png
new file mode 100644
index 000000000..d83e0d50c
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_videocam_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_videocam_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_videocam_white_36.png
new file mode 100644
index 000000000..49562a640
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_videocam_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_voicemail_white_18.png b/assets/quantum/res/drawable-hdpi/quantum_ic_voicemail_white_18.png
new file mode 100644
index 000000000..23e8997f2
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_voicemail_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_volume_up_grey600_24.png b/assets/quantum/res/drawable-hdpi/quantum_ic_volume_up_grey600_24.png
new file mode 100644
index 000000000..49eb8fcc3
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_volume_up_grey600_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-hdpi/quantum_ic_volume_up_white_36.png b/assets/quantum/res/drawable-hdpi/quantum_ic_volume_up_white_36.png
new file mode 100644
index 000000000..62d22bec8
--- /dev/null
+++ b/assets/quantum/res/drawable-hdpi/quantum_ic_volume_up_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_arrow_back_white_24.png b/assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_arrow_back_white_24.png
new file mode 100644
index 000000000..f51755762
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_arrow_back_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_send_black_24.png b/assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_send_black_24.png
new file mode 100644
index 000000000..c0ab46f5d
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_send_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_send_white_24.png b/assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_send_white_24.png
new file mode 100644
index 000000000..b8d4ce444
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_send_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_undo_white_48.png b/assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_undo_white_48.png
new file mode 100644
index 000000000..6c8174f3a
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-hdpi/quantum_ic_undo_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_arrow_back_white_24.png b/assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_arrow_back_white_24.png
new file mode 100644
index 000000000..22a1140ae
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_arrow_back_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_send_black_24.png b/assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_send_black_24.png
new file mode 100644
index 000000000..f2232c7db
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_send_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_send_white_24.png b/assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_send_white_24.png
new file mode 100644
index 000000000..7933f42f0
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_send_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_undo_white_48.png b/assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_undo_white_48.png
new file mode 100644
index 000000000..b47cef666
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-mdpi/quantum_ic_undo_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_arrow_back_white_24.png b/assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_arrow_back_white_24.png
new file mode 100644
index 000000000..d858f18e6
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_arrow_back_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_send_black_24.png b/assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_send_black_24.png
new file mode 100644
index 000000000..3a25fbb86
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_send_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_send_white_24.png b/assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_send_white_24.png
new file mode 100644
index 000000000..4735a7d71
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_send_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_undo_white_48.png b/assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_undo_white_48.png
new file mode 100644
index 000000000..6a984c4f1
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-xhdpi/quantum_ic_undo_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_arrow_back_white_24.png b/assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_arrow_back_white_24.png
new file mode 100644
index 000000000..614ad49a3
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_arrow_back_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_send_black_24.png b/assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_send_black_24.png
new file mode 100644
index 000000000..ba6dd1a96
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_send_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_send_white_24.png b/assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_send_white_24.png
new file mode 100644
index 000000000..4a9e2c24a
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_send_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_undo_white_48.png b/assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_undo_white_48.png
new file mode 100644
index 000000000..907911055
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-xxhdpi/quantum_ic_undo_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_arrow_back_white_24.png b/assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_arrow_back_white_24.png
new file mode 100644
index 000000000..d409b544b
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_arrow_back_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_send_black_24.png b/assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_send_black_24.png
new file mode 100644
index 000000000..92117e109
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_send_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_send_white_24.png b/assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_send_white_24.png
new file mode 100644
index 000000000..0167ac829
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_send_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_undo_white_48.png b/assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_undo_white_48.png
new file mode 100644
index 000000000..aa7a91943
--- /dev/null
+++ b/assets/quantum/res/drawable-ldrtl-xxxhdpi/quantum_ic_undo_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_arrow_back_white_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_arrow_back_white_24.png
new file mode 100644
index 000000000..4ef72eec9
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_arrow_back_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_arrow_drop_down_white_18.png b/assets/quantum/res/drawable-mdpi/quantum_ic_arrow_drop_down_white_18.png
new file mode 100644
index 000000000..7c1fc3d7c
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_arrow_drop_down_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_bluetooth_audio_grey600_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_bluetooth_audio_grey600_24.png
new file mode 100644
index 000000000..de635e034
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_bluetooth_audio_grey600_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_bluetooth_audio_white_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_bluetooth_audio_white_36.png
new file mode 100644
index 000000000..046372d0d
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_bluetooth_audio_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_call_end_white_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_call_end_white_24.png
new file mode 100644
index 000000000..378272ffc
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_call_end_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_call_end_white_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_call_end_white_36.png
new file mode 100644
index 000000000..625b827c4
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_call_end_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_call_merge_white_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_call_merge_white_36.png
new file mode 100644
index 000000000..a2eb54bab
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_call_merge_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_call_white_18.png b/assets/quantum/res/drawable-mdpi/quantum_ic_call_white_18.png
new file mode 100644
index 000000000..bd5748575
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_call_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_call_white_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_call_white_24.png
new file mode 100644
index 000000000..77f9de5e3
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_call_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_camera_alt_white_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_camera_alt_white_24.png
new file mode 100644
index 000000000..e83052200
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_camera_alt_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_camera_alt_white_48.png b/assets/quantum/res/drawable-mdpi/quantum_ic_camera_alt_white_48.png
new file mode 100644
index 000000000..be9fb226a
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_camera_alt_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_camera_front_white_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_camera_front_white_36.png
new file mode 100644
index 000000000..f36dcd496
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_camera_front_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_camera_rear_white_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_camera_rear_white_36.png
new file mode 100644
index 000000000..4a5494f28
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_camera_rear_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_check_black_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_check_black_24.png
new file mode 100644
index 000000000..1c14c9c44
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_check_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_check_black_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_check_black_36.png
new file mode 100644
index 000000000..e802d90ae
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_check_black_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_check_white_48.png b/assets/quantum/res/drawable-mdpi/quantum_ic_check_white_48.png
new file mode 100644
index 000000000..3b2b65d26
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_check_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_close_white_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_close_white_24.png
new file mode 100644
index 000000000..af7f8288d
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_close_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_close_white_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_close_white_36.png
new file mode 100644
index 000000000..ceb1a1eeb
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_close_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_dialpad_white_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_dialpad_white_36.png
new file mode 100644
index 000000000..9037f94e8
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_dialpad_white_36.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_forward_white_24dp.png b/assets/quantum/res/drawable-mdpi/quantum_ic_forward_white_24.png
index 65f73299f..65f73299f 100644
--- a/InCallUI/res/drawable-mdpi/ic_forward_white_24dp.png
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_forward_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_fullscreen_exit_white_48.png b/assets/quantum/res/drawable-mdpi/quantum_ic_fullscreen_exit_white_48.png
new file mode 100644
index 000000000..364bad0b8
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_fullscreen_exit_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_fullscreen_white_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_fullscreen_white_36.png
new file mode 100644
index 000000000..c150cb58d
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_fullscreen_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_fullscreen_white_48.png b/assets/quantum/res/drawable-mdpi/quantum_ic_fullscreen_white_48.png
new file mode 100644
index 000000000..4423c7ce9
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_fullscreen_white_48.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_people_24dp.png b/assets/quantum/res/drawable-mdpi/quantum_ic_group_white_36.png
index ff698afc0..ff698afc0 100644
--- a/res/drawable-hdpi/ic_people_24dp.png
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_group_white_36.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_hd_24dp.png b/assets/quantum/res/drawable-mdpi/quantum_ic_hd_white_24.png
index 30938fe4d..30938fe4d 100644
--- a/InCallUI/res/drawable-mdpi/ic_hd_24dp.png
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_hd_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_headset_grey600_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_headset_grey600_24.png
new file mode 100644
index 000000000..371efd382
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_headset_grey600_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_headset_white_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_headset_white_36.png
new file mode 100644
index 000000000..d25d3888e
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_headset_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_message_white_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_message_white_24.png
new file mode 100644
index 000000000..3072b7569
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_message_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_mic_off_black_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_mic_off_black_24.png
new file mode 100644
index 000000000..da605a5a1
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_mic_off_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_mic_off_white_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_mic_off_white_36.png
new file mode 100644
index 000000000..6fccf5d09
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_mic_off_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_network_wifi_white_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_network_wifi_white_24.png
new file mode 100644
index 000000000..1c3e8b987
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_network_wifi_white_24.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pause_24dp.png b/assets/quantum/res/drawable-mdpi/quantum_ic_pause_white_36.png
index 4d2ea05c4..4d2ea05c4 100644
--- a/res/drawable-hdpi/ic_pause_24dp.png
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_pause_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_person_add_white_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_person_add_white_24.png
new file mode 100644
index 000000000..38e0a2882
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_person_add_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_photo_library_white_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_photo_library_white_24.png
new file mode 100644
index 000000000..02ef4cdb0
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_photo_library_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_photo_white_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_photo_white_24.png
new file mode 100644
index 000000000..d474bd577
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_photo_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_photo_white_48.png b/assets/quantum/res/drawable-mdpi/quantum_ic_photo_white_48.png
new file mode 100644
index 000000000..2642b9e09
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_photo_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_report_white_18.png b/assets/quantum/res/drawable-mdpi/quantum_ic_report_white_18.png
new file mode 100644
index 000000000..63ef73683
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_report_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_send_black_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_send_black_24.png
new file mode 100644
index 000000000..8bff465eb
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_send_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_send_white_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_send_white_24.png
new file mode 100644
index 000000000..b58afb0b4
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_send_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_swap_calls_white_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_swap_calls_white_36.png
new file mode 100644
index 000000000..9491f2d1a
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_swap_calls_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_switch_camera_white_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_switch_camera_white_36.png
new file mode 100644
index 000000000..c318983c7
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_switch_camera_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_switch_video_white_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_switch_video_white_36.png
new file mode 100644
index 000000000..2bf8a169e
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_switch_video_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_undo_white_48.png b/assets/quantum/res/drawable-mdpi/quantum_ic_undo_white_48.png
new file mode 100644
index 000000000..b67f6a911
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_undo_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_videocam_off_white_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_videocam_off_white_24.png
new file mode 100644
index 000000000..d1cca6f0a
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_videocam_off_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_videocam_off_white_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_videocam_off_white_36.png
new file mode 100644
index 000000000..aaf5ac208
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_videocam_off_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_videocam_white_18.png b/assets/quantum/res/drawable-mdpi/quantum_ic_videocam_white_18.png
new file mode 100644
index 000000000..1dafd4927
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_videocam_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_videocam_white_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_videocam_white_24.png
new file mode 100644
index 000000000..d146209a5
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_videocam_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_videocam_white_36.png b/assets/quantum/res/drawable-mdpi/quantum_ic_videocam_white_36.png
new file mode 100644
index 000000000..d83e0d50c
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_videocam_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_voicemail_white_18.png b/assets/quantum/res/drawable-mdpi/quantum_ic_voicemail_white_18.png
new file mode 100644
index 000000000..fa4755769
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_voicemail_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-mdpi/quantum_ic_volume_up_grey600_24.png b/assets/quantum/res/drawable-mdpi/quantum_ic_volume_up_grey600_24.png
new file mode 100644
index 000000000..d6cea3667
--- /dev/null
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_volume_up_grey600_24.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_volume_up_24dp.png b/assets/quantum/res/drawable-mdpi/quantum_ic_volume_up_white_36.png
index 57d787163..57d787163 100644
--- a/res/drawable-hdpi/ic_volume_up_24dp.png
+++ b/assets/quantum/res/drawable-mdpi/quantum_ic_volume_up_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_arrow_back_white_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_arrow_back_white_24.png
new file mode 100644
index 000000000..832f5a361
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_arrow_back_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_arrow_drop_down_white_18.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_arrow_drop_down_white_18.png
new file mode 100644
index 000000000..4c6076df7
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_arrow_drop_down_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_bluetooth_audio_grey600_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_bluetooth_audio_grey600_24.png
new file mode 100644
index 000000000..eea1bbf04
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_bluetooth_audio_grey600_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_bluetooth_audio_white_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_bluetooth_audio_white_36.png
new file mode 100644
index 000000000..d5022d063
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_bluetooth_audio_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_call_end_white_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_call_end_white_24.png
new file mode 100644
index 000000000..a4fe6889d
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_call_end_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_call_end_white_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_call_end_white_36.png
new file mode 100644
index 000000000..e1831d7af
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_call_end_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_call_merge_white_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_call_merge_white_36.png
new file mode 100644
index 000000000..01daecf65
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_call_merge_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_call_white_18.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_call_white_18.png
new file mode 100644
index 000000000..4dc506515
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_call_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_call_white_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_call_white_24.png
new file mode 100644
index 000000000..ef45e933a
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_call_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_camera_alt_white_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_camera_alt_white_24.png
new file mode 100644
index 000000000..be9fb226a
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_camera_alt_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_camera_alt_white_48.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_camera_alt_white_48.png
new file mode 100644
index 000000000..777658e95
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_camera_alt_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_camera_front_white_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_camera_front_white_36.png
new file mode 100644
index 000000000..3eb24d1f2
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_camera_front_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_camera_rear_white_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_camera_rear_white_36.png
new file mode 100644
index 000000000..8392b2a88
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_camera_rear_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_check_black_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_check_black_24.png
new file mode 100644
index 000000000..64a4944f7
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_check_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_check_black_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_check_black_36.png
new file mode 100644
index 000000000..b26a2c05e
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_check_black_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_check_white_48.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_check_white_48.png
new file mode 100644
index 000000000..d670618c7
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_check_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_close_white_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_close_white_24.png
new file mode 100644
index 000000000..b7c7ffd0e
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_close_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_close_white_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_close_white_36.png
new file mode 100644
index 000000000..6b717e0dd
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_close_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_dialpad_white_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_dialpad_white_36.png
new file mode 100644
index 000000000..175000510
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_dialpad_white_36.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_forward_white_24dp.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_forward_white_24.png
index 7a5df52bf..7a5df52bf 100644
--- a/InCallUI/res/drawable-xhdpi/ic_forward_white_24dp.png
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_forward_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_fullscreen_exit_white_48.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_fullscreen_exit_white_48.png
new file mode 100644
index 000000000..ef360fe40
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_fullscreen_exit_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_fullscreen_white_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_fullscreen_white_36.png
new file mode 100644
index 000000000..9b8131124
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_fullscreen_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_fullscreen_white_48.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_fullscreen_white_48.png
new file mode 100644
index 000000000..c1dcfb290
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_fullscreen_white_48.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_people_24dp.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_group_white_36.png
index 6c68435fb..6c68435fb 100644
--- a/res/drawable-xxhdpi/ic_people_24dp.png
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_group_white_36.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_hd_24dp.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_hd_white_24.png
index 4c954d86f..4c954d86f 100644
--- a/InCallUI/res/drawable-xhdpi/ic_hd_24dp.png
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_hd_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_headset_grey600_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_headset_grey600_24.png
new file mode 100644
index 000000000..f7dbee156
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_headset_grey600_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_headset_white_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_headset_white_36.png
new file mode 100644
index 000000000..82db5427b
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_headset_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_message_white_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_message_white_24.png
new file mode 100644
index 000000000..763767b4f
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_message_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_mic_off_black_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_mic_off_black_24.png
new file mode 100644
index 000000000..fa741be1c
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_mic_off_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_mic_off_white_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_mic_off_white_36.png
new file mode 100644
index 000000000..7a15a9ea9
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_mic_off_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_network_wifi_white_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_network_wifi_white_24.png
new file mode 100644
index 000000000..ca927f3de
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_network_wifi_white_24.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pause_24dp.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_pause_white_36.png
index 7192ad487..7192ad487 100644
--- a/res/drawable-xxhdpi/ic_pause_24dp.png
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_pause_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_person_add_white_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_person_add_white_24.png
new file mode 100644
index 000000000..7e7c289d4
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_person_add_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_photo_library_white_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_photo_library_white_24.png
new file mode 100644
index 000000000..4bd2898a8
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_photo_library_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_photo_white_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_photo_white_24.png
new file mode 100644
index 000000000..2642b9e09
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_photo_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_photo_white_48.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_photo_white_48.png
new file mode 100644
index 000000000..2ffdb55f2
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_photo_white_48.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_report_white_36dp.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_report_white_18.png
index dc0c995c1..dc0c995c1 100644
--- a/InCallUI/res/drawable-mdpi/ic_report_white_36dp.png
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_report_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_send_black_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_send_black_24.png
new file mode 100644
index 000000000..fd36be8b9
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_send_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_send_white_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_send_white_24.png
new file mode 100644
index 000000000..ef59e7767
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_send_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_swap_calls_white_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_swap_calls_white_36.png
new file mode 100644
index 000000000..698cd5d75
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_swap_calls_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_switch_camera_white_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_switch_camera_white_36.png
new file mode 100644
index 000000000..bee95a1d4
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_switch_camera_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_switch_video_white_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_switch_video_white_36.png
new file mode 100644
index 000000000..1a7423f5f
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_switch_video_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_undo_white_48.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_undo_white_48.png
new file mode 100644
index 000000000..a5e719cdf
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_undo_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_off_white_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_off_white_24.png
new file mode 100644
index 000000000..5d540589b
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_off_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_off_white_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_off_white_36.png
new file mode 100644
index 000000000..69565f2c7
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_off_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_white_18.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_white_18.png
new file mode 100644
index 000000000..d83e0d50c
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_white_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_white_24.png
new file mode 100644
index 000000000..1b2583d34
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_white_36.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_white_36.png
new file mode 100644
index 000000000..44c28e2f2
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_videocam_white_36.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_voicemail_24dp.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_voicemail_white_18.png
index 03a62e15f..03a62e15f 100644
--- a/res/drawable-hdpi/ic_voicemail_24dp.png
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_voicemail_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xhdpi/quantum_ic_volume_up_grey600_24.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_volume_up_grey600_24.png
new file mode 100644
index 000000000..a45093ff7
--- /dev/null
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_volume_up_grey600_24.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_volume_up_24dp.png b/assets/quantum/res/drawable-xhdpi/quantum_ic_volume_up_white_36.png
index 2e751a40f..2e751a40f 100644
--- a/res/drawable-xxhdpi/ic_volume_up_24dp.png
+++ b/assets/quantum/res/drawable-xhdpi/quantum_ic_volume_up_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_arrow_back_white_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_arrow_back_white_24.png
new file mode 100644
index 000000000..32a6d91ce
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_arrow_back_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_arrow_drop_down_white_18.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_arrow_drop_down_white_18.png
new file mode 100644
index 000000000..2609ae134
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_arrow_drop_down_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_bluetooth_audio_grey600_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_bluetooth_audio_grey600_24.png
new file mode 100644
index 000000000..99f57c12a
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_bluetooth_audio_grey600_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_bluetooth_audio_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_bluetooth_audio_white_36.png
new file mode 100644
index 000000000..6842da6d0
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_bluetooth_audio_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_call_end_white_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_call_end_white_24.png
new file mode 100644
index 000000000..e1831d7af
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_call_end_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_call_end_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_call_end_white_36.png
new file mode 100644
index 000000000..13ffc2ad7
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_call_end_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_call_merge_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_call_merge_white_36.png
new file mode 100644
index 000000000..cefef6551
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_call_merge_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_call_white_18.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_call_white_18.png
new file mode 100644
index 000000000..6f4dcea1f
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_call_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_call_white_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_call_white_24.png
new file mode 100644
index 000000000..90ead2e45
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_call_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_alt_white_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_alt_white_24.png
new file mode 100644
index 000000000..c8e69dceb
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_alt_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_alt_white_48.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_alt_white_48.png
new file mode 100644
index 000000000..a4e7aea72
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_alt_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_front_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_front_white_36.png
new file mode 100644
index 000000000..115cdb72b
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_front_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_rear_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_rear_white_36.png
new file mode 100644
index 000000000..087508fdb
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_camera_rear_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_check_black_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_check_black_24.png
new file mode 100644
index 000000000..b26a2c05e
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_check_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_check_black_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_check_black_36.png
new file mode 100644
index 000000000..1e6101fd9
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_check_black_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_check_white_48.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_check_white_48.png
new file mode 100644
index 000000000..bfd7b82aa
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_check_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_close_white_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_close_white_24.png
new file mode 100644
index 000000000..6b717e0dd
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_close_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_close_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_close_white_36.png
new file mode 100644
index 000000000..3b28ea4b0
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_close_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_dialpad_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_dialpad_white_36.png
new file mode 100644
index 000000000..54ebbafae
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_dialpad_white_36.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_forward_white_24dp.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_forward_white_24.png
index 7bd5b1635..7bd5b1635 100644
--- a/InCallUI/res/drawable-xxhdpi/ic_forward_white_24dp.png
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_forward_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_fullscreen_exit_white_48.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_fullscreen_exit_white_48.png
new file mode 100644
index 000000000..b7f4133fd
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_fullscreen_exit_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_fullscreen_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_fullscreen_white_36.png
new file mode 100644
index 000000000..ca9135b49
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_fullscreen_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_fullscreen_white_48.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_fullscreen_white_48.png
new file mode 100644
index 000000000..a0a1b4d4f
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_fullscreen_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_group_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_group_white_36.png
new file mode 100644
index 000000000..57f87aec0
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_group_white_36.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_hd_24dp.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_hd_white_24.png
index dd08bbbec..dd08bbbec 100644
--- a/InCallUI/res/drawable-xxhdpi/ic_hd_24dp.png
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_hd_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_headset_grey600_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_headset_grey600_24.png
new file mode 100644
index 000000000..de1739bf4
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_headset_grey600_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_headset_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_headset_white_36.png
new file mode 100644
index 000000000..a0d8b14c0
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_headset_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_message_white_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_message_white_24.png
new file mode 100644
index 000000000..0a79824b8
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_message_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_mic_off_black_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_mic_off_black_24.png
new file mode 100644
index 000000000..084bf3c9f
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_mic_off_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_mic_off_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_mic_off_white_36.png
new file mode 100644
index 000000000..585d38326
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_mic_off_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_network_wifi_white_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_network_wifi_white_24.png
new file mode 100644
index 000000000..75469cd85
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_network_wifi_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_pause_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_pause_white_36.png
new file mode 100644
index 000000000..a03bad27e
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_pause_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_person_add_white_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_person_add_white_24.png
new file mode 100644
index 000000000..8f744f039
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_person_add_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_photo_library_white_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_photo_library_white_24.png
new file mode 100644
index 000000000..497479291
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_photo_library_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_photo_white_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_photo_white_24.png
new file mode 100644
index 000000000..f9f1defa6
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_photo_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_photo_white_48.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_photo_white_48.png
new file mode 100644
index 000000000..3fe5c5ceb
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_photo_white_48.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_report_white_36dp.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_report_white_18.png
index 919a872e0..919a872e0 100644
--- a/InCallUI/res/drawable-hdpi/ic_report_white_36dp.png
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_report_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_send_black_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_send_black_24.png
new file mode 100644
index 000000000..7caa9db4a
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_send_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_send_white_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_send_white_24.png
new file mode 100644
index 000000000..0c5256413
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_send_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_swap_calls_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_swap_calls_white_36.png
new file mode 100644
index 000000000..140da28a8
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_swap_calls_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_switch_camera_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_switch_camera_white_36.png
new file mode 100644
index 000000000..52b298e0d
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_switch_camera_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_switch_video_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_switch_video_white_36.png
new file mode 100644
index 000000000..0e0f3c26f
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_switch_video_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_undo_white_48.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_undo_white_48.png
new file mode 100644
index 000000000..8745f69ff
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_undo_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_off_white_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_off_white_24.png
new file mode 100644
index 000000000..69565f2c7
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_off_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_off_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_off_white_36.png
new file mode 100644
index 000000000..ff8483295
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_off_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_white_18.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_white_18.png
new file mode 100644
index 000000000..49562a640
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_white_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_white_24.png
new file mode 100644
index 000000000..44c28e2f2
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_white_36.png
new file mode 100644
index 000000000..839af26f8
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_videocam_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_voicemail_white_18.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_voicemail_white_18.png
new file mode 100644
index 000000000..d839f2167
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_voicemail_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_volume_up_grey600_24.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_volume_up_grey600_24.png
new file mode 100644
index 000000000..413b38652
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_volume_up_grey600_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxhdpi/quantum_ic_volume_up_white_36.png b/assets/quantum/res/drawable-xxhdpi/quantum_ic_volume_up_white_36.png
new file mode 100644
index 000000000..96c1f982f
--- /dev/null
+++ b/assets/quantum/res/drawable-xxhdpi/quantum_ic_volume_up_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_arrow_back_white_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_arrow_back_white_24.png
new file mode 100644
index 000000000..e27034d67
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_arrow_back_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_arrow_drop_down_white_18.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_arrow_drop_down_white_18.png
new file mode 100644
index 000000000..c19c19d2b
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_arrow_drop_down_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_bluetooth_audio_grey600_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_bluetooth_audio_grey600_24.png
new file mode 100644
index 000000000..1595be169
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_bluetooth_audio_grey600_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_bluetooth_audio_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_bluetooth_audio_white_36.png
new file mode 100644
index 000000000..3fe7c2350
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_bluetooth_audio_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_end_white_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_end_white_24.png
new file mode 100644
index 000000000..8801d0ded
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_end_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_end_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_end_white_36.png
new file mode 100644
index 000000000..c8099a1a1
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_end_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_merge_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_merge_white_36.png
new file mode 100644
index 000000000..9419ffbbc
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_merge_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_white_18.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_white_18.png
new file mode 100644
index 000000000..90ead2e45
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_white_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_white_24.png
new file mode 100644
index 000000000..b0e020573
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_call_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_alt_white_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_alt_white_24.png
new file mode 100644
index 000000000..777658e95
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_alt_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_alt_white_48.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_alt_white_48.png
new file mode 100644
index 000000000..f2fe54bd5
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_alt_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_front_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_front_white_36.png
new file mode 100644
index 000000000..9f43e2073
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_front_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_rear_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_rear_white_36.png
new file mode 100644
index 000000000..e3cac8e74
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_camera_rear_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_check_black_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_check_black_24.png
new file mode 100644
index 000000000..2f6d6386d
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_check_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_check_black_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_check_black_36.png
new file mode 100644
index 000000000..5697dba95
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_check_black_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_check_white_48.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_check_white_48.png
new file mode 100644
index 000000000..23a197082
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_check_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_close_white_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_close_white_24.png
new file mode 100644
index 000000000..396419219
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_close_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_close_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_close_white_36.png
new file mode 100644
index 000000000..4927bc242
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_close_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_dialpad_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_dialpad_white_36.png
new file mode 100644
index 000000000..a53aeb1d3
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_dialpad_white_36.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_forward_white_24dp.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_forward_white_24.png
index 428009cfe..428009cfe 100644
--- a/InCallUI/res/drawable-xxxhdpi/ic_forward_white_24dp.png
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_forward_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_fullscreen_exit_white_48.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_fullscreen_exit_white_48.png
new file mode 100644
index 000000000..b47b3f8bd
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_fullscreen_exit_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_fullscreen_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_fullscreen_white_36.png
new file mode 100644
index 000000000..a0a1b4d4f
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_fullscreen_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_fullscreen_white_48.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_fullscreen_white_48.png
new file mode 100644
index 000000000..ea9f18ae6
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_fullscreen_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_group_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_group_white_36.png
new file mode 100644
index 000000000..9ec120f5f
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_group_white_36.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_hd_24dp.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_hd_white_24.png
index 3f87b882e..3f87b882e 100644
--- a/InCallUI/res/drawable-xxxhdpi/ic_hd_24dp.png
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_hd_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_headset_grey600_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_headset_grey600_24.png
new file mode 100644
index 000000000..e968fa7d1
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_headset_grey600_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_headset_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_headset_white_36.png
new file mode 100644
index 000000000..89b991047
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_headset_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_message_white_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_message_white_24.png
new file mode 100644
index 000000000..fa7c17ac4
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_message_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_mic_off_black_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_mic_off_black_24.png
new file mode 100644
index 000000000..90d0606a4
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_mic_off_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_mic_off_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_mic_off_white_36.png
new file mode 100644
index 000000000..b0a10fbf6
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_mic_off_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_network_wifi_white_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_network_wifi_white_24.png
new file mode 100644
index 000000000..eb284e383
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_network_wifi_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_pause_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_pause_white_36.png
new file mode 100644
index 000000000..3ea7e03e5
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_pause_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_person_add_white_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_person_add_white_24.png
new file mode 100644
index 000000000..2fa2cca80
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_person_add_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_photo_library_white_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_photo_library_white_24.png
new file mode 100644
index 000000000..8627f4276
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_photo_library_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_photo_white_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_photo_white_24.png
new file mode 100644
index 000000000..2ffdb55f2
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_photo_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_photo_white_48.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_photo_white_48.png
new file mode 100644
index 000000000..7d5091ded
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_photo_white_48.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_report_white_36dp.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_report_white_18.png
index aed766804..aed766804 100644
--- a/InCallUI/res/drawable-xhdpi/ic_report_white_36dp.png
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_report_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_send_black_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_send_black_24.png
new file mode 100644
index 000000000..b1a0bce48
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_send_black_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_send_white_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_send_white_24.png
new file mode 100644
index 000000000..9dfa888c1
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_send_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_swap_calls_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_swap_calls_white_36.png
new file mode 100644
index 000000000..f8470b5dc
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_swap_calls_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_switch_camera_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_switch_camera_white_36.png
new file mode 100644
index 000000000..8cbb600a4
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_switch_camera_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_switch_video_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_switch_video_white_36.png
new file mode 100644
index 000000000..854e78301
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_switch_video_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_undo_white_48.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_undo_white_48.png
new file mode 100644
index 000000000..6d703c6ae
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_undo_white_48.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_off_white_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_off_white_24.png
new file mode 100644
index 000000000..bf37b57f9
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_off_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_off_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_off_white_36.png
new file mode 100644
index 000000000..7a915c30d
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_off_white_36.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_white_18.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_white_18.png
new file mode 100644
index 000000000..44c28e2f2
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_white_18.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_videocam_24dp.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_white_24.png
index ed20c0706..ed20c0706 100644
--- a/res/drawable-xxxhdpi/ic_videocam_24dp.png
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_white_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_white_36.png
new file mode 100644
index 000000000..eff5923da
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_videocam_white_36.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_voicemail_24dp.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_voicemail_white_18.png
index 28b8e936a..28b8e936a 100644
--- a/res/drawable-xxhdpi/ic_voicemail_24dp.png
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_voicemail_white_18.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_volume_up_grey600_24.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_volume_up_grey600_24.png
new file mode 100644
index 000000000..429dc02df
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_volume_up_grey600_24.png
Binary files differ
diff --git a/assets/quantum/res/drawable-xxxhdpi/quantum_ic_volume_up_white_36.png b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_volume_up_white_36.png
new file mode 100644
index 000000000..fd633b6cb
--- /dev/null
+++ b/assets/quantum/res/drawable-xxxhdpi/quantum_ic_volume_up_white_36.png
Binary files differ
diff --git a/build-app.gradle b/build-app.gradle
deleted file mode 100644
index 2ea437619..000000000
--- a/build-app.gradle
+++ /dev/null
@@ -1,39 +0,0 @@
-apply plugin: 'com.android.application'
-
-android {
- defaultConfig {
- minSdkVersion 23
- targetSdkVersion 23
- multiDexEnabled true
- }
-
- sourceSets.main {
- java.srcDirs = ['src', 'src-pre-N', 'InCallUI/src']
- manifest.srcFile 'AndroidManifest.xml'
- res.srcDirs = ['res']
- }
-
- sourceSets.androidTest {
- java.srcDirs = ['tests/src']
- res.srcDirs = ['test/res']
- }
-}
-
-dependencies {
- compile 'com.android.support:support-v4:23.1.+'
- compile 'com.android.support:support-v13:23.1.+'
- compile 'com.android.support:appcompat-v7:23.1.+'
- compile 'com.android.support:cardview-v7:23.1.+'
- compile 'com.android.support:design:23.1.+'
- compile 'com.android.support:recyclerview-v7:23.1.+'
-
- compile project(':android-common')
- compile project(':guava')
- compile project(':libphonenumber')
- compile project(':jsr305')
- compile project(':vcard')
-
- compile project(':contactscommon')
- compile project(':incallui')
- compile project(':phonecommon')
-}
diff --git a/build-library.gradle b/build-library.gradle
deleted file mode 100644
index a2394aac8..000000000
--- a/build-library.gradle
+++ /dev/null
@@ -1,39 +0,0 @@
-apply plugin: 'com.android.library'
-
-android {
- defaultConfig {
- minSdkVersion 23
- targetSdkVersion 23
- multiDexEnabled true
- }
-
- sourceSets.main {
- java.srcDirs = ['src', 'src-pre-N', 'InCallUI/src']
- manifest.srcFile 'AndroidManifest.xml'
- res.srcDirs = ['res']
- }
-
- sourceSets.androidTest {
- java.srcDirs = ['tests/src']
- res.srcDirs = ['test/res']
- }
-}
-
-dependencies {
- compile 'com.android.support:support-v4:23.1.+'
- compile 'com.android.support:support-v13:23.1.+'
- compile 'com.android.support:appcompat-v7:23.1.+'
- compile 'com.android.support:cardview-v7:23.1.+'
- compile 'com.android.support:design:23.1.+'
- compile 'com.android.support:recyclerview-v7:23.1.+'
-
- compile project(':android-common')
- compile project(':guava')
- compile project(':libphonenumber')
- compile project(':jsr305')
- compile project(':vcard')
-
- compile project(':contactscommon')
- compile project(':incallui')
- compile project(':phonecommon')
-}
diff --git a/java/com/android/contacts/common/AndroidManifest.xml b/java/com/android/contacts/common/AndroidManifest.xml
new file mode 100644
index 000000000..eae70cd30
--- /dev/null
+++ b/java/com/android/contacts/common/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.contacts.common">
+
+ <application>
+
+ <activity
+ android:name="com.android.contacts.common.dialog.CallSubjectDialog"
+ android:theme="@style/Theme.CallSubjectDialogTheme"
+ android:windowSoftInputMode="stateVisible|adjustResize">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW"/>
+ </intent-filter>
+ </activity>
+
+ <!-- Broadcast receiver that passively listens to location updates -->
+ <receiver android:name="com.android.contacts.common.location.CountryDetector$LocationChangedReceiver"/>
+
+ <!-- IntentService to update the user's current country -->
+ <service
+ android:exported="false"
+ android:name="com.android.contacts.common.location.UpdateCountryService"/>
+ </application>
+</manifest>
+
diff --git a/java/com/android/contacts/common/Bindings.java b/java/com/android/contacts/common/Bindings.java
new file mode 100644
index 000000000..29cf7950a
--- /dev/null
+++ b/java/com/android/contacts/common/Bindings.java
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+import android.content.Context;
+import com.android.contacts.common.bindings.ContactsCommonBindings;
+import com.android.contacts.common.bindings.ContactsCommonBindingsFactory;
+import com.android.contacts.common.bindings.ContactsCommonBindingsStub;
+import java.util.Objects;
+
+/** Accessor for the contacts common bindings. */
+public class Bindings {
+
+ private static ContactsCommonBindings instance;
+
+ private Bindings() {}
+
+ public static ContactsCommonBindings get(Context context) {
+ Objects.requireNonNull(context);
+ if (instance != null) {
+ return instance;
+ }
+
+ Context application = context.getApplicationContext();
+ if (application instanceof ContactsCommonBindingsFactory) {
+ instance = ((ContactsCommonBindingsFactory) application).newContactsCommonBindings();
+ }
+
+ if (instance == null) {
+ instance = new ContactsCommonBindingsStub();
+ }
+ return instance;
+ }
+
+ public static void setForTesting(ContactsCommonBindings testInstance) {
+ instance = testInstance;
+ }
+}
diff --git a/java/com/android/contacts/common/ClipboardUtils.java b/java/com/android/contacts/common/ClipboardUtils.java
new file mode 100644
index 000000000..9345b0f9c
--- /dev/null
+++ b/java/com/android/contacts/common/ClipboardUtils.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.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.text.TextUtils;
+import android.widget.Toast;
+
+public class ClipboardUtils {
+
+ private static final String TAG = "ClipboardUtils";
+
+ private ClipboardUtils() {}
+
+ /**
+ * Copy a text to clipboard.
+ *
+ * @param context Context
+ * @param label Label to show to the user describing this clip.
+ * @param text Text to copy.
+ * @param showToast If {@code true}, a toast is shown to the user.
+ */
+ public static void copyText(
+ Context context, CharSequence label, CharSequence text, boolean showToast) {
+ if (TextUtils.isEmpty(text)) {
+ return;
+ }
+
+ ClipboardManager clipboardManager =
+ (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clipData = ClipData.newPlainText(label == null ? "" : label, text);
+ clipboardManager.setPrimaryClip(clipData);
+
+ if (showToast) {
+ String toastText = context.getString(R.string.toast_text_copied);
+ Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show();
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/Collapser.java b/java/com/android/contacts/common/Collapser.java
new file mode 100644
index 000000000..0b5c48bf2
--- /dev/null
+++ b/java/com/android/contacts/common/Collapser.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2009 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.content.Context;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Class used for collapsing data items into groups of similar items. The data items that should be
+ * collapsible should implement the Collapsible interface. The class also contains a utility
+ * function that takes an ArrayList of items and returns a list of the same items collapsed into
+ * groups.
+ */
+public final class Collapser {
+
+ /*
+ * The Collapser uses an n^2 algorithm so we don't want it to run on
+ * lists beyond a certain size. This specifies the maximum size to collapse.
+ */
+ private static final int MAX_LISTSIZE_TO_COLLAPSE = 20;
+
+ /*
+ * This utility class cannot be instantiated.
+ */
+ private Collapser() {}
+
+ /**
+ * Collapses a list of Collapsible items into a list of collapsed items. Items are collapsed if
+ * {@link Collapsible#shouldCollapseWith(Object)} returns true, and are collapsed through the
+ * {@Link Collapsible#collapseWith(Object)} function implemented by the data item.
+ *
+ * @param list List of Objects of type <T extends Collapsible<T>> to be collapsed.
+ */
+ public static <T extends Collapsible<T>> void collapseList(List<T> list, Context context) {
+
+ int listSize = list.size();
+ // The algorithm below is n^2 so don't run on long lists
+ if (listSize > MAX_LISTSIZE_TO_COLLAPSE) {
+ return;
+ }
+
+ for (int i = 0; i < listSize; i++) {
+ T iItem = list.get(i);
+ if (iItem != null) {
+ for (int j = i + 1; j < listSize; j++) {
+ T jItem = list.get(j);
+ if (jItem != null) {
+ if (iItem.shouldCollapseWith(jItem, context)) {
+ iItem.collapseWith(jItem);
+ list.set(j, null);
+ } else if (jItem.shouldCollapseWith(iItem, context)) {
+ jItem.collapseWith(iItem);
+ list.set(i, null);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Remove the null items
+ Iterator<T> itr = list.iterator();
+ while (itr.hasNext()) {
+ if (itr.next() == null) {
+ itr.remove();
+ }
+ }
+ }
+
+ /*
+ * Interface implemented by data types that can be collapsed into groups of similar data. This
+ * can be used for example to collapse similar contact data items into a single item.
+ */
+ public interface Collapsible<T> {
+
+ void collapseWith(T t);
+
+ boolean shouldCollapseWith(T t, Context context);
+ }
+}
diff --git a/java/com/android/contacts/common/ContactPhotoManager.java b/java/com/android/contacts/common/ContactPhotoManager.java
new file mode 100644
index 000000000..834471047
--- /dev/null
+++ b/java/com/android/contacts/common/ContactPhotoManager.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2010 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.content.ComponentCallbacks2;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.ImageView;
+import com.android.contacts.common.lettertiles.LetterTileDrawable;
+import com.android.dialer.common.LogUtil;
+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_BUSINESS = LetterTileDrawable.TYPE_BUSINESS;
+ public static final int TYPE_VOICEMAIL = LetterTileDrawable.TYPE_VOICEMAIL;
+ public static final int TYPE_DEFAULT = LetterTileDrawable.TYPE_DEFAULT;
+ /** Scale and offset default constants used for default letter images */
+ public static final float SCALE_DEFAULT = 1.0f;
+
+ public static final float OFFSET_DEFAULT = 0.0f;
+ public static final boolean IS_CIRCULAR_DEFAULT = false;
+ // TODO: Use LogUtil.isVerboseEnabled for DEBUG branches instead of a lint check.
+ // LINT.DoNotSubmitIf(true)
+ static final boolean DEBUG = false;
+ // LINT.DoNotSubmitIf(true)
+ static final boolean DEBUG_SIZES = false;
+ /** Uri-related constants used for default letter images */
+ private static final String DISPLAY_NAME_PARAM_KEY = "display_name";
+
+ private static final String IDENTIFIER_PARAM_KEY = "identifier";
+ private static final String CONTACT_TYPE_PARAM_KEY = "contact_type";
+ private static final String SCALE_PARAM_KEY = "scale";
+ private static final String OFFSET_PARAM_KEY = "offset";
+ private static final String IS_CIRCULAR_PARAM_KEY = "is_circular";
+ private static final String DEFAULT_IMAGE_URI_SCHEME = "defaultimage";
+ private static final Uri DEFAULT_IMAGE_URI = Uri.parse(DEFAULT_IMAGE_URI_SCHEME + "://");
+ public static final DefaultImageProvider DEFAULT_AVATAR = new LetterTileDefaultImageProvider();
+ private static ContactPhotoManager sInstance;
+
+ /**
+ * Given a {@link DefaultImageRequest}, returns an Uri that can be used to request a letter tile
+ * avatar when passed to the {@link ContactPhotoManager}. The internal implementation of this uri
+ * is not guaranteed to remain the same across application versions, so the actual uri should
+ * never be persisted in long-term storage and reused.
+ *
+ * @param request A {@link DefaultImageRequest} object with the fields configured to return a
+ * @return A Uri that when later passed to the {@link ContactPhotoManager} via {@link
+ * #loadPhoto(ImageView, Uri, int, boolean, boolean, DefaultImageRequest)}, can be used to
+ * request a default contact image, drawn as a letter tile using the parameters as configured
+ * in the provided {@link DefaultImageRequest}
+ */
+ public static Uri getDefaultAvatarUriForContact(DefaultImageRequest request) {
+ final Builder builder = DEFAULT_IMAGE_URI.buildUpon();
+ if (request != null) {
+ if (!TextUtils.isEmpty(request.displayName)) {
+ builder.appendQueryParameter(DISPLAY_NAME_PARAM_KEY, request.displayName);
+ }
+ if (!TextUtils.isEmpty(request.identifier)) {
+ builder.appendQueryParameter(IDENTIFIER_PARAM_KEY, request.identifier);
+ }
+ if (request.contactType != TYPE_DEFAULT) {
+ builder.appendQueryParameter(CONTACT_TYPE_PARAM_KEY, String.valueOf(request.contactType));
+ }
+ if (request.scale != SCALE_DEFAULT) {
+ builder.appendQueryParameter(SCALE_PARAM_KEY, String.valueOf(request.scale));
+ }
+ if (request.offset != OFFSET_DEFAULT) {
+ builder.appendQueryParameter(OFFSET_PARAM_KEY, String.valueOf(request.offset));
+ }
+ if (request.isCircular != IS_CIRCULAR_DEFAULT) {
+ builder.appendQueryParameter(IS_CIRCULAR_PARAM_KEY, String.valueOf(request.isCircular));
+ }
+ }
+ return builder.build();
+ }
+
+ /**
+ * Adds a business contact type encoded fragment to the URL. Used to ensure photo URLS from Nearby
+ * Places can be identified as business photo URLs rather than URLs for personal contact photos.
+ *
+ * @param photoUrl The photo URL to modify.
+ * @return URL with the contact type parameter added and set to TYPE_BUSINESS.
+ */
+ public static String appendBusinessContactType(String photoUrl) {
+ Uri uri = Uri.parse(photoUrl);
+ Builder builder = uri.buildUpon();
+ builder.encodedFragment(String.valueOf(TYPE_BUSINESS));
+ return builder.build().toString();
+ }
+
+ /**
+ * Removes the contact type information stored in the photo URI encoded fragment.
+ *
+ * @param photoUri The photo URI to remove the contact type from.
+ * @return The photo URI with contact type removed.
+ */
+ public static Uri removeContactType(Uri photoUri) {
+ String encodedFragment = photoUri.getEncodedFragment();
+ if (!TextUtils.isEmpty(encodedFragment)) {
+ Builder builder = photoUri.buildUpon();
+ builder.encodedFragment(null);
+ return builder.build();
+ }
+ return photoUri;
+ }
+
+ /**
+ * Inspects a photo URI to determine if the photo URI represents a business.
+ *
+ * @param photoUri The URI to inspect.
+ * @return Whether the URI represents a business photo or not.
+ */
+ public static boolean isBusinessContactUri(Uri photoUri) {
+ if (photoUri == null) {
+ return false;
+ }
+
+ String encodedFragment = photoUri.getEncodedFragment();
+ return !TextUtils.isEmpty(encodedFragment)
+ && encodedFragment.equals(String.valueOf(TYPE_BUSINESS));
+ }
+
+ protected static DefaultImageRequest getDefaultImageRequestFromUri(Uri uri) {
+ final DefaultImageRequest request =
+ new DefaultImageRequest(
+ uri.getQueryParameter(DISPLAY_NAME_PARAM_KEY),
+ uri.getQueryParameter(IDENTIFIER_PARAM_KEY),
+ false);
+ try {
+ String contactType = uri.getQueryParameter(CONTACT_TYPE_PARAM_KEY);
+ if (!TextUtils.isEmpty(contactType)) {
+ request.contactType = Integer.valueOf(contactType);
+ }
+
+ String scale = uri.getQueryParameter(SCALE_PARAM_KEY);
+ if (!TextUtils.isEmpty(scale)) {
+ request.scale = Float.valueOf(scale);
+ }
+
+ String offset = uri.getQueryParameter(OFFSET_PARAM_KEY);
+ if (!TextUtils.isEmpty(offset)) {
+ request.offset = Float.valueOf(offset);
+ }
+
+ String isCircular = uri.getQueryParameter(IS_CIRCULAR_PARAM_KEY);
+ if (!TextUtils.isEmpty(isCircular)) {
+ request.isCircular = Boolean.valueOf(isCircular);
+ }
+ } catch (NumberFormatException e) {
+ LogUtil.w(
+ "ContactPhotoManager.getDefaultImageRequestFromUri",
+ "Invalid DefaultImageRequest image parameters provided, ignoring and using "
+ + "defaults.");
+ }
+
+ return request;
+ }
+
+ public static ContactPhotoManager getInstance(Context context) {
+ if (sInstance == null) {
+ Context applicationContext = context.getApplicationContext();
+ sInstance = createContactPhotoManager(applicationContext);
+ applicationContext.registerComponentCallbacks(sInstance);
+ if (PermissionsUtil.hasContactsPermissions(context)) {
+ sInstance.preloadPhotosInBackground();
+ }
+ }
+ return sInstance;
+ }
+
+ public static synchronized ContactPhotoManager createContactPhotoManager(Context context) {
+ return new ContactPhotoManagerImpl(context);
+ }
+
+ @VisibleForTesting
+ public static void injectContactPhotoManagerForTesting(ContactPhotoManager photoManager) {
+ sInstance = photoManager;
+ }
+
+ protected boolean isDefaultImageUri(Uri uri) {
+ return DEFAULT_IMAGE_URI_SCHEME.equals(uri.getScheme());
+ }
+
+ /**
+ * Load thumbnail image into the supplied image view. If the photo is already cached, it is
+ * displayed immediately. Otherwise a request is sent to load the photo from the database.
+ */
+ public abstract void loadThumbnail(
+ ImageView view,
+ long photoId,
+ boolean darkTheme,
+ boolean isCircular,
+ DefaultImageRequest defaultImageRequest,
+ DefaultImageProvider defaultProvider);
+
+ /**
+ * Calls {@link #loadThumbnail(ImageView, long, boolean, boolean, DefaultImageRequest,
+ * DefaultImageProvider)} using the {@link DefaultImageProvider} {@link #DEFAULT_AVATAR}.
+ */
+ public final void loadThumbnail(
+ ImageView view,
+ long photoId,
+ boolean darkTheme,
+ boolean isCircular,
+ DefaultImageRequest defaultImageRequest) {
+ loadThumbnail(view, photoId, darkTheme, isCircular, defaultImageRequest, DEFAULT_AVATAR);
+ }
+
+ /**
+ * Load photo into the supplied image view. If the photo is already cached, it is displayed
+ * immediately. Otherwise a request is sent to load the photo from the location specified by the
+ * URI.
+ *
+ * @param view The target view
+ * @param photoUri The uri of the photo to load
+ * @param requestedExtent Specifies an approximate Max(width, height) of the targetView. This is
+ * useful if the source image can be a lot bigger that the target, so that the decoding is
+ * done using efficient sampling. If requestedExtent is specified, no sampling of the image is
+ * performed
+ * @param darkTheme Whether the background is dark. This is used for default avatars
+ * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a default
+ * letter tile avatar should be drawn.
+ * @param defaultProvider The provider of default avatars (this is used if photoUri doesn't refer
+ * to an existing image)
+ */
+ public abstract void loadPhoto(
+ ImageView view,
+ Uri photoUri,
+ int requestedExtent,
+ boolean darkTheme,
+ boolean isCircular,
+ DefaultImageRequest defaultImageRequest,
+ DefaultImageProvider defaultProvider);
+
+ /**
+ * Calls {@link #loadPhoto(ImageView, Uri, int, boolean, boolean, DefaultImageRequest,
+ * DefaultImageProvider)} with {@link #DEFAULT_AVATAR} and {@code null} display names and lookup
+ * keys.
+ *
+ * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a default
+ * letter tile avatar should be drawn.
+ */
+ public final void loadPhoto(
+ ImageView view,
+ Uri photoUri,
+ int requestedExtent,
+ boolean darkTheme,
+ boolean isCircular,
+ DefaultImageRequest defaultImageRequest) {
+ loadPhoto(
+ view,
+ photoUri,
+ requestedExtent,
+ darkTheme,
+ isCircular,
+ defaultImageRequest,
+ DEFAULT_AVATAR);
+ }
+
+ /**
+ * Calls {@link #loadPhoto(ImageView, Uri, int, boolean, boolean, DefaultImageRequest,
+ * DefaultImageProvider)} with {@link #DEFAULT_AVATAR} and with the assumption, that the image is
+ * a thumbnail.
+ *
+ * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a default
+ * letter tile avatar should be drawn.
+ */
+ public final void loadDirectoryPhoto(
+ ImageView view,
+ Uri photoUri,
+ boolean darkTheme,
+ boolean isCircular,
+ DefaultImageRequest defaultImageRequest) {
+ loadPhoto(view, photoUri, -1, darkTheme, isCircular, defaultImageRequest, DEFAULT_AVATAR);
+ }
+
+ /**
+ * Remove photo from the supplied image view. This also cancels current pending load request
+ * inside this photo manager.
+ */
+ public abstract void removePhoto(ImageView view);
+
+ /** Cancels all pending requests to load photos asynchronously. */
+ public abstract void cancelPendingRequests(View fragmentRootView);
+
+ /** Temporarily stops loading photos from the database. */
+ public abstract void pause();
+
+ /** Resumes loading photos from the database. */
+ public abstract void resume();
+
+ /**
+ * Marks all cached photos for reloading. We can continue using cache but should also make sure
+ * the photos haven't changed in the background and notify the views if so.
+ */
+ public abstract void refreshCache();
+
+ /** Initiates a background process that over time will fill up cache with preload photos. */
+ public abstract void preloadPhotosInBackground();
+
+ // ComponentCallbacks2
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {}
+
+ // ComponentCallbacks2
+ @Override
+ public void onLowMemory() {}
+
+ // ComponentCallbacks2
+ @Override
+ public void onTrimMemory(int level) {}
+
+ /**
+ * Contains fields used to contain contact details and other user-defined settings that might be
+ * used by the ContactPhotoManager to generate a default contact image. This contact image takes
+ * the form of a letter or bitmap drawn on top of a colored tile.
+ */
+ public static class DefaultImageRequest {
+
+ /**
+ * Used to indicate that a drawable that represents a contact without any contact details should
+ * be returned.
+ */
+ public static final DefaultImageRequest EMPTY_DEFAULT_IMAGE_REQUEST = new DefaultImageRequest();
+ /**
+ * Used to indicate that a drawable that represents a business without a business photo should
+ * be returned.
+ */
+ public static final DefaultImageRequest EMPTY_DEFAULT_BUSINESS_IMAGE_REQUEST =
+ 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.
+ */
+ public static final DefaultImageRequest EMPTY_CIRCULAR_DEFAULT_IMAGE_REQUEST =
+ new DefaultImageRequest(null, null, true);
+ /**
+ * Used to indicate that a circular drawable that represents a business without a business photo
+ * should be returned.
+ */
+ public static final DefaultImageRequest EMPTY_CIRCULAR_BUSINESS_IMAGE_REQUEST =
+ new DefaultImageRequest(null, null, TYPE_BUSINESS, true);
+ /** The contact's display name. The display name is used to */
+ public String displayName;
+ /**
+ * A unique and deterministic string that can be used to identify this contact. This is usually
+ * the contact's lookup key, but other contact details can be used as well, especially for
+ * non-local or temporary contacts that might not have a lookup key. This is used to determine
+ * the color of the tile.
+ */
+ public String identifier;
+ /**
+ * 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. See: {@link #TYPE_PERSON} {@link #TYPE_BUSINESS} {@link #TYPE_PERSON} {@link
+ * #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.
+ */
+ public float scale = SCALE_DEFAULT;
+ /**
+ * The amount to vertically offset the letter or image to within the tile. The provided offset
+ * must be within the range of -0.5f to 0.5f. If set to -0.5f, the letter will be shifted
+ * upwards by 0.5 times the height of the canvas it is being drawn on, which means it will be
+ * drawn with the center of the letter starting at the top edge of the canvas. If set to 0.5f,
+ * the letter will be shifted downwards by 0.5 times the height of the canvas it is being drawn
+ * on, which means it will be drawn with the center of the letter starting at the bottom edge of
+ * the canvas. The default is 0.0f, which means the letter is drawn in the exact vertical center
+ * of the tile.
+ */
+ public float offset = OFFSET_DEFAULT;
+ /** Whether or not to draw the default image as a circle, instead of as a square/rectangle. */
+ public boolean isCircular = false;
+
+ public DefaultImageRequest() {}
+
+ public DefaultImageRequest(String displayName, String identifier, boolean isCircular) {
+ this(displayName, identifier, TYPE_DEFAULT, SCALE_DEFAULT, OFFSET_DEFAULT, isCircular);
+ }
+
+ public DefaultImageRequest(
+ String displayName, String identifier, int contactType, boolean isCircular) {
+ this(displayName, identifier, contactType, SCALE_DEFAULT, OFFSET_DEFAULT, isCircular);
+ }
+
+ public DefaultImageRequest(
+ String displayName,
+ String identifier,
+ int contactType,
+ float scale,
+ float offset,
+ boolean isCircular) {
+ this.displayName = displayName;
+ this.identifier = identifier;
+ this.contactType = contactType;
+ this.scale = scale;
+ this.offset = offset;
+ this.isCircular = isCircular;
+ }
+ }
+
+ public abstract static class DefaultImageProvider {
+
+ /**
+ * Applies the default avatar to the ImageView. Extent is an indicator for the size (width or
+ * height). If darkTheme is set, the avatar is one that looks better on dark background
+ *
+ * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a default
+ * letter tile avatar should be drawn.
+ */
+ public abstract void applyDefaultImage(
+ ImageView view, int extent, boolean darkTheme, DefaultImageRequest defaultImageRequest);
+ }
+
+ /**
+ * A default image provider that applies a letter tile consisting of a colored background and a
+ * letter in the foreground as the default image for a contact. The color of the background and
+ * the type of letter is decided based on the contact's details.
+ */
+ private static class LetterTileDefaultImageProvider extends DefaultImageProvider {
+
+ public static Drawable getDefaultImageForContact(
+ Resources resources, DefaultImageRequest defaultImageRequest) {
+ final LetterTileDrawable drawable = new LetterTileDrawable(resources);
+ final int tileShape =
+ defaultImageRequest.isCircular
+ ? LetterTileDrawable.SHAPE_CIRCLE
+ : LetterTileDrawable.SHAPE_RECTANGLE;
+ if (defaultImageRequest != null) {
+ // If the contact identifier is null or empty, fallback to the
+ // displayName. In that case, use {@code null} for the contact's
+ // display name so that a default bitmap will be used instead of a
+ // letter
+ if (TextUtils.isEmpty(defaultImageRequest.identifier)) {
+ drawable.setCanonicalDialerLetterTileDetails(
+ null, defaultImageRequest.displayName, tileShape, defaultImageRequest.contactType);
+ } else {
+ drawable.setCanonicalDialerLetterTileDetails(
+ defaultImageRequest.displayName,
+ defaultImageRequest.identifier,
+ tileShape,
+ defaultImageRequest.contactType);
+ }
+ drawable.setScale(defaultImageRequest.scale);
+ drawable.setOffset(defaultImageRequest.offset);
+ }
+ return drawable;
+ }
+
+ @Override
+ public void applyDefaultImage(
+ ImageView view, int extent, boolean darkTheme, DefaultImageRequest defaultImageRequest) {
+ final Drawable drawable = getDefaultImageForContact(view.getResources(), defaultImageRequest);
+ view.setImageDrawable(drawable);
+ }
+ }
+}
+
diff --git a/java/com/android/contacts/common/ContactPhotoManagerImpl.java b/java/com/android/contacts/common/ContactPhotoManagerImpl.java
new file mode 100644
index 000000000..2e6ff9fdc
--- /dev/null
+++ b/java/com/android/contacts/common/ContactPhotoManagerImpl.java
@@ -0,0 +1,1262 @@
+/*
+ * 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;
+
+import android.app.ActivityManager;
+import android.content.ComponentCallbacks2;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.TransitionDrawable;
+import android.media.ThumbnailUtils;
+import android.net.TrafficStats;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Handler.Callback;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Contacts.Photo;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
+import android.support.annotation.UiThread;
+import android.support.annotation.WorkerThread;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
+import android.text.TextUtils;
+import android.util.LruCache;
+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.util.PermissionsUtil;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+class ContactPhotoManagerImpl extends ContactPhotoManager implements Callback {
+
+ private static final String LOADER_THREAD_NAME = "ContactPhotoLoader";
+
+ private static final int FADE_TRANSITION_DURATION = 200;
+
+ /**
+ * Type of message sent by the UI thread to itself to indicate that some photos need to be loaded.
+ */
+ private static final int MESSAGE_REQUEST_LOADING = 1;
+
+ /** Type of message sent by the loader thread to indicate that some photos have been loaded. */
+ private static final int MESSAGE_PHOTOS_LOADED = 2;
+
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ private static final String[] COLUMNS = new String[] {Photo._ID, Photo.PHOTO};
+
+ /**
+ * Dummy object used to indicate that a bitmap for a given key could not be stored in the cache.
+ */
+ private static final BitmapHolder BITMAP_UNAVAILABLE;
+ /** Cache size for {@link #mBitmapHolderCache} for devices with "large" RAM. */
+ private static final int HOLDER_CACHE_SIZE = 2000000;
+ /** Cache size for {@link #mBitmapCache} for devices with "large" RAM. */
+ private static final int BITMAP_CACHE_SIZE = 36864 * 48; // 1728K
+ /** Height/width of a thumbnail image */
+ private static int mThumbnailSize;
+
+ static {
+ BITMAP_UNAVAILABLE = new BitmapHolder(new byte[0], 0);
+ BITMAP_UNAVAILABLE.bitmapRef = new SoftReference<Bitmap>(null);
+ }
+
+ private final Context mContext;
+ /**
+ * An LRU cache for bitmap holders. The cache contains bytes for photos just as they come from the
+ * database. Each holder has a soft reference to the actual bitmap.
+ */
+ private final LruCache<Object, BitmapHolder> mBitmapHolderCache;
+ /** Cache size threshold at which bitmaps will not be preloaded. */
+ private final int mBitmapHolderCacheRedZoneBytes;
+ /**
+ * Level 2 LRU cache for bitmaps. This is a smaller cache that holds the most recently used
+ * bitmaps to save time on decoding them from bytes (the bytes are stored in {@link
+ * #mBitmapHolderCache}.
+ */
+ private final LruCache<Object, Bitmap> mBitmapCache;
+ /**
+ * A map from ImageView to the corresponding photo ID or uri, encapsulated in a request. The
+ * request may swapped out before the photo loading request is started.
+ */
+ private final ConcurrentHashMap<ImageView, Request> mPendingRequests =
+ new ConcurrentHashMap<ImageView, Request>();
+ /** Handler for messages sent to the UI thread. */
+ private final Handler mMainThreadHandler = new Handler(this);
+ /** For debug: How many times we had to reload cached photo for a stale entry */
+ private final AtomicInteger mStaleCacheOverwrite = new AtomicInteger();
+ /** For debug: How many times we had to reload cached photo for a fresh entry. Should be 0. */
+ private final AtomicInteger mFreshCacheOverwrite = new AtomicInteger();
+ /** {@code true} if ALL entries in {@link #mBitmapHolderCache} are NOT fresh. */
+ private volatile boolean mBitmapHolderCacheAllUnfresh = true;
+ /** Thread responsible for loading photos from the database. Created upon the first request. */
+ private LoaderThread mLoaderThread;
+ /** A gate to make sure we only send one instance of MESSAGE_PHOTOS_NEEDED at a time. */
+ private boolean mLoadingRequested;
+ /** Flag indicating if the image loading is paused. */
+ private boolean mPaused;
+ /** The user agent string to use when loading URI based photos. */
+ private String mUserAgent;
+
+ public ContactPhotoManagerImpl(Context context) {
+ mContext = context;
+
+ final ActivityManager am =
+ ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE));
+
+ final float cacheSizeAdjustment = (am.isLowRamDevice()) ? 0.5f : 1.0f;
+
+ final int bitmapCacheSize = (int) (cacheSizeAdjustment * BITMAP_CACHE_SIZE);
+ mBitmapCache =
+ new LruCache<Object, Bitmap>(bitmapCacheSize) {
+ @Override
+ protected int sizeOf(Object key, Bitmap value) {
+ return value.getByteCount();
+ }
+
+ @Override
+ protected void entryRemoved(
+ boolean evicted, Object key, Bitmap oldValue, Bitmap newValue) {
+ if (DEBUG) {
+ dumpStats();
+ }
+ }
+ };
+ final int holderCacheSize = (int) (cacheSizeAdjustment * HOLDER_CACHE_SIZE);
+ mBitmapHolderCache =
+ new LruCache<Object, BitmapHolder>(holderCacheSize) {
+ @Override
+ protected int sizeOf(Object key, BitmapHolder value) {
+ return value.bytes != null ? value.bytes.length : 0;
+ }
+
+ @Override
+ protected void entryRemoved(
+ boolean evicted, Object key, BitmapHolder oldValue, BitmapHolder newValue) {
+ if (DEBUG) {
+ dumpStats();
+ }
+ }
+ };
+ mBitmapHolderCacheRedZoneBytes = (int) (holderCacheSize * 0.75);
+ LogUtil.i(
+ "ContactPhotoManagerImpl.ContactPhotoManagerImpl", "cache adj: " + cacheSizeAdjustment);
+ if (DEBUG) {
+ LogUtil.d(
+ "ContactPhotoManagerImpl.ContactPhotoManagerImpl",
+ "Cache size: " + btk(mBitmapHolderCache.maxSize()) + " + " + btk(mBitmapCache.maxSize()));
+ }
+
+ mThumbnailSize =
+ context.getResources().getDimensionPixelSize(R.dimen.contact_browser_list_item_photo_size);
+
+ // Get a user agent string to use for URI photo requests.
+ mUserAgent = Bindings.get(context).getUserAgent();
+ if (mUserAgent == null) {
+ mUserAgent = "";
+ }
+ }
+
+ /** Converts bytes to K bytes, rounding up. Used only for debug log. */
+ private static String btk(int bytes) {
+ return ((bytes + 1023) / 1024) + "K";
+ }
+
+ private static final int safeDiv(int dividend, int divisor) {
+ return (divisor == 0) ? 0 : (dividend / divisor);
+ }
+
+ private static boolean isChildView(View parent, View potentialChild) {
+ return potentialChild.getParent() != null
+ && (potentialChild.getParent() == parent
+ || (potentialChild.getParent() instanceof ViewGroup
+ && isChildView(parent, (ViewGroup) potentialChild.getParent())));
+ }
+
+ /**
+ * If necessary, decodes bytes stored in the holder to Bitmap. As long as the bitmap is held
+ * either by {@link #mBitmapCache} or by a soft reference in the holder, it will not be necessary
+ * to decode the bitmap.
+ */
+ private static void inflateBitmap(BitmapHolder holder, int requestedExtent) {
+ final int sampleSize =
+ BitmapUtil.findOptimalSampleSize(holder.originalSmallerExtent, requestedExtent);
+ byte[] bytes = holder.bytes;
+ if (bytes == null || bytes.length == 0) {
+ return;
+ }
+
+ if (sampleSize == holder.decodedSampleSize) {
+ // Check the soft reference. If will be retained if the bitmap is also
+ // in the LRU cache, so we don't need to check the LRU cache explicitly.
+ if (holder.bitmapRef != null) {
+ holder.bitmap = holder.bitmapRef.get();
+ if (holder.bitmap != null) {
+ return;
+ }
+ }
+ }
+
+ try {
+ Bitmap bitmap = BitmapUtil.decodeBitmapFromBytes(bytes, sampleSize);
+
+ // TODO: As a temporary workaround while framework support is being added to
+ // clip non-square bitmaps into a perfect circle, manually crop the bitmap into
+ // into a square if it will be displayed as a thumbnail so that it can be cropped
+ // into a circle.
+ final int height = bitmap.getHeight();
+ final int width = bitmap.getWidth();
+
+ // The smaller dimension of a scaled bitmap can range from anywhere from 0 to just
+ // below twice the length of a thumbnail image due to the way we calculate the optimal
+ // sample size.
+ if (height != width && Math.min(height, width) <= mThumbnailSize * 2) {
+ final int dimension = Math.min(height, width);
+ bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension);
+ }
+ // make bitmap mutable and draw size onto it
+ if (DEBUG_SIZES) {
+ Bitmap original = bitmap;
+ bitmap = bitmap.copy(bitmap.getConfig(), true);
+ original.recycle();
+ Canvas canvas = new Canvas(bitmap);
+ Paint paint = new Paint();
+ paint.setTextSize(16);
+ paint.setColor(Color.BLUE);
+ paint.setStyle(Style.FILL);
+ canvas.drawRect(0.0f, 0.0f, 50.0f, 20.0f, paint);
+ paint.setColor(Color.WHITE);
+ paint.setAntiAlias(true);
+ canvas.drawText(bitmap.getWidth() + "/" + sampleSize, 0, 15, paint);
+ }
+
+ holder.decodedSampleSize = sampleSize;
+ holder.bitmap = bitmap;
+ holder.bitmapRef = new SoftReference<Bitmap>(bitmap);
+ if (DEBUG) {
+ LogUtil.d(
+ "ContactPhotoManagerImpl.inflateBitmap",
+ "inflateBitmap "
+ + btk(bytes.length)
+ + " -> "
+ + bitmap.getWidth()
+ + "x"
+ + bitmap.getHeight()
+ + ", "
+ + btk(bitmap.getByteCount()));
+ }
+ } catch (OutOfMemoryError e) {
+ // Do nothing - the photo will appear to be missing
+ }
+ }
+
+ /** Dump cache stats on logcat. */
+ private void dumpStats() {
+ if (!DEBUG) {
+ return;
+ }
+ {
+ int numHolders = 0;
+ int rawBytes = 0;
+ int bitmapBytes = 0;
+ int numBitmaps = 0;
+ for (BitmapHolder h : mBitmapHolderCache.snapshot().values()) {
+ numHolders++;
+ if (h.bytes != null) {
+ rawBytes += h.bytes.length;
+ }
+ Bitmap b = h.bitmapRef != null ? h.bitmapRef.get() : null;
+ if (b != null) {
+ numBitmaps++;
+ bitmapBytes += b.getByteCount();
+ }
+ }
+ LogUtil.d(
+ "ContactPhotoManagerImpl.dumpStats",
+ "L1: "
+ + btk(rawBytes)
+ + " + "
+ + btk(bitmapBytes)
+ + " = "
+ + btk(rawBytes + bitmapBytes)
+ + ", "
+ + numHolders
+ + " holders, "
+ + numBitmaps
+ + " bitmaps, avg: "
+ + btk(safeDiv(rawBytes, numHolders))
+ + ","
+ + btk(safeDiv(bitmapBytes, numBitmaps)));
+ LogUtil.d(
+ "ContactPhotoManagerImpl.dumpStats",
+ "L1 Stats: "
+ + mBitmapHolderCache.toString()
+ + ", overwrite: fresh="
+ + mFreshCacheOverwrite.get()
+ + " stale="
+ + mStaleCacheOverwrite.get());
+ }
+
+ {
+ int numBitmaps = 0;
+ int bitmapBytes = 0;
+ for (Bitmap b : mBitmapCache.snapshot().values()) {
+ numBitmaps++;
+ bitmapBytes += b.getByteCount();
+ }
+ LogUtil.d(
+ "ContactPhotoManagerImpl.dumpStats",
+ "L2: "
+ + btk(bitmapBytes)
+ + ", "
+ + numBitmaps
+ + " bitmaps"
+ + ", avg: "
+ + btk(safeDiv(bitmapBytes, numBitmaps)));
+ // We don't get from L2 cache, so L2 stats is meaningless.
+ }
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ if (DEBUG) {
+ LogUtil.d("ContactPhotoManagerImpl.onTrimMemory", "onTrimMemory: " + level);
+ }
+ if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
+ // Clear the caches. Note all pending requests will be removed too.
+ clear();
+ }
+ }
+
+ @Override
+ public void preloadPhotosInBackground() {
+ ensureLoaderThread();
+ mLoaderThread.requestPreloading();
+ }
+
+ @Override
+ public void loadThumbnail(
+ ImageView view,
+ long photoId,
+ boolean darkTheme,
+ boolean isCircular,
+ DefaultImageRequest defaultImageRequest,
+ DefaultImageProvider defaultProvider) {
+ if (photoId == 0) {
+ // No photo is needed
+ defaultProvider.applyDefaultImage(view, -1, darkTheme, defaultImageRequest);
+ mPendingRequests.remove(view);
+ } else {
+ if (DEBUG) {
+ LogUtil.d("ContactPhotoManagerImpl.loadThumbnail", "loadPhoto request: " + photoId);
+ }
+ loadPhotoByIdOrUri(
+ view, Request.createFromThumbnailId(photoId, darkTheme, isCircular, defaultProvider));
+ }
+ }
+
+ @Override
+ public void loadPhoto(
+ ImageView view,
+ Uri photoUri,
+ int requestedExtent,
+ boolean darkTheme,
+ boolean isCircular,
+ DefaultImageRequest defaultImageRequest,
+ DefaultImageProvider defaultProvider) {
+ if (photoUri == null) {
+ // No photo is needed
+ defaultProvider.applyDefaultImage(view, requestedExtent, darkTheme, defaultImageRequest);
+ mPendingRequests.remove(view);
+ } else {
+ if (DEBUG) {
+ LogUtil.d("ContactPhotoManagerImpl.loadPhoto", "loadPhoto request: " + photoUri);
+ }
+ if (isDefaultImageUri(photoUri)) {
+ createAndApplyDefaultImageForUri(
+ view, photoUri, requestedExtent, darkTheme, isCircular, defaultProvider);
+ } else {
+ loadPhotoByIdOrUri(
+ view,
+ Request.createFromUri(
+ photoUri, requestedExtent, darkTheme, isCircular, defaultProvider));
+ }
+ }
+ }
+
+ private void createAndApplyDefaultImageForUri(
+ ImageView view,
+ Uri uri,
+ int requestedExtent,
+ boolean darkTheme,
+ boolean isCircular,
+ DefaultImageProvider defaultProvider) {
+ DefaultImageRequest request = getDefaultImageRequestFromUri(uri);
+ request.isCircular = isCircular;
+ defaultProvider.applyDefaultImage(view, requestedExtent, darkTheme, request);
+ }
+
+ private void loadPhotoByIdOrUri(ImageView view, Request request) {
+ boolean loaded = loadCachedPhoto(view, request, false);
+ if (loaded) {
+ mPendingRequests.remove(view);
+ } else {
+ mPendingRequests.put(view, request);
+ if (!mPaused) {
+ // Send a request to start loading photos
+ requestLoading();
+ }
+ }
+ }
+
+ @Override
+ public void removePhoto(ImageView view) {
+ view.setImageDrawable(null);
+ mPendingRequests.remove(view);
+ }
+
+ /**
+ * Cancels pending requests to load photos asynchronously for views inside {@param
+ * fragmentRootView}. If {@param fragmentRootView} is null, cancels all requests.
+ */
+ @Override
+ public void cancelPendingRequests(View fragmentRootView) {
+ if (fragmentRootView == null) {
+ mPendingRequests.clear();
+ return;
+ }
+ final Iterator<Entry<ImageView, Request>> iterator = mPendingRequests.entrySet().iterator();
+ while (iterator.hasNext()) {
+ final ImageView imageView = iterator.next().getKey();
+ // If an ImageView is orphaned (currently scrap) or a child of fragmentRootView, then
+ // we can safely remove its request.
+ if (imageView.getParent() == null || isChildView(fragmentRootView, imageView)) {
+ iterator.remove();
+ }
+ }
+ }
+
+ @Override
+ public void refreshCache() {
+ if (mBitmapHolderCacheAllUnfresh) {
+ if (DEBUG) {
+ LogUtil.d("ContactPhotoManagerImpl.refreshCache", "refreshCache -- no fresh entries.");
+ }
+ return;
+ }
+ if (DEBUG) {
+ LogUtil.d("ContactPhotoManagerImpl.refreshCache", "refreshCache");
+ }
+ mBitmapHolderCacheAllUnfresh = true;
+ for (BitmapHolder holder : mBitmapHolderCache.snapshot().values()) {
+ if (holder != BITMAP_UNAVAILABLE) {
+ holder.fresh = false;
+ }
+ }
+ }
+
+ /**
+ * Checks if the photo is present in cache. If so, sets the photo on the view.
+ *
+ * @return false if the photo needs to be (re)loaded from the provider.
+ */
+ @UiThread
+ private boolean loadCachedPhoto(ImageView view, Request request, boolean fadeIn) {
+ BitmapHolder holder = mBitmapHolderCache.get(request.getKey());
+ if (holder == null) {
+ // The bitmap has not been loaded ==> show default avatar
+ request.applyDefaultImage(view, request.mIsCircular);
+ return false;
+ }
+
+ if (holder.bytes == null) {
+ request.applyDefaultImage(view, request.mIsCircular);
+ return holder.fresh;
+ }
+
+ Bitmap cachedBitmap = holder.bitmapRef == null ? null : holder.bitmapRef.get();
+ if (cachedBitmap == null) {
+ request.applyDefaultImage(view, request.mIsCircular);
+ return false;
+ }
+
+ final Drawable previousDrawable = view.getDrawable();
+ if (fadeIn && previousDrawable != null) {
+ final Drawable[] layers = new Drawable[2];
+ // Prevent cascade of TransitionDrawables.
+ if (previousDrawable instanceof TransitionDrawable) {
+ final TransitionDrawable previousTransitionDrawable = (TransitionDrawable) previousDrawable;
+ layers[0] =
+ previousTransitionDrawable.getDrawable(
+ previousTransitionDrawable.getNumberOfLayers() - 1);
+ } else {
+ layers[0] = previousDrawable;
+ }
+ layers[1] = getDrawableForBitmap(mContext.getResources(), cachedBitmap, request);
+ TransitionDrawable drawable = new TransitionDrawable(layers);
+ view.setImageDrawable(drawable);
+ drawable.startTransition(FADE_TRANSITION_DURATION);
+ } else {
+ view.setImageDrawable(getDrawableForBitmap(mContext.getResources(), cachedBitmap, request));
+ }
+
+ // Put the bitmap in the LRU cache. But only do this for images that are small enough
+ // (we require that at least six of those can be cached at the same time)
+ if (cachedBitmap.getByteCount() < mBitmapCache.maxSize() / 6) {
+ mBitmapCache.put(request.getKey(), cachedBitmap);
+ }
+
+ // Soften the reference
+ holder.bitmap = null;
+
+ return holder.fresh;
+ }
+
+ /**
+ * Given a bitmap, returns a drawable that is configured to display the bitmap based on the
+ * specified request.
+ */
+ private Drawable getDrawableForBitmap(Resources resources, Bitmap bitmap, Request request) {
+ if (request.mIsCircular) {
+ final RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(resources, bitmap);
+ drawable.setAntiAlias(true);
+ drawable.setCornerRadius(bitmap.getHeight() / 2);
+ return drawable;
+ } else {
+ return new BitmapDrawable(resources, bitmap);
+ }
+ }
+
+ public void clear() {
+ if (DEBUG) {
+ LogUtil.d("ContactPhotoManagerImpl.clear", "clear");
+ }
+ mPendingRequests.clear();
+ mBitmapHolderCache.evictAll();
+ mBitmapCache.evictAll();
+ }
+
+ @Override
+ public void pause() {
+ mPaused = true;
+ }
+
+ @Override
+ public void resume() {
+ mPaused = false;
+ if (DEBUG) {
+ dumpStats();
+ }
+ if (!mPendingRequests.isEmpty()) {
+ requestLoading();
+ }
+ }
+
+ /**
+ * Sends a message to this thread itself to start loading images. If the current view contains
+ * multiple image views, all of those image views will get a chance to request their respective
+ * photos before any of those requests are executed. This allows us to load images in bulk.
+ */
+ private void requestLoading() {
+ if (!mLoadingRequested) {
+ mLoadingRequested = true;
+ mMainThreadHandler.sendEmptyMessage(MESSAGE_REQUEST_LOADING);
+ }
+ }
+
+ /** Processes requests on the main thread. */
+ @Override
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_REQUEST_LOADING:
+ {
+ mLoadingRequested = false;
+ if (!mPaused) {
+ ensureLoaderThread();
+ mLoaderThread.requestLoading();
+ }
+ return true;
+ }
+
+ case MESSAGE_PHOTOS_LOADED:
+ {
+ if (!mPaused) {
+ processLoadedImages();
+ }
+ if (DEBUG) {
+ dumpStats();
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void ensureLoaderThread() {
+ if (mLoaderThread == null) {
+ mLoaderThread = new LoaderThread(mContext.getContentResolver());
+ mLoaderThread.start();
+ }
+ }
+
+ /**
+ * Goes over pending loading requests and displays loaded photos. If some of the photos still
+ * haven't been loaded, sends another request for image loading.
+ */
+ private void processLoadedImages() {
+ final Iterator<Entry<ImageView, Request>> iterator = mPendingRequests.entrySet().iterator();
+ while (iterator.hasNext()) {
+ final Entry<ImageView, Request> entry = iterator.next();
+ // TODO: Temporarily disable contact photo fading in, until issues with
+ // RoundedBitmapDrawables overlapping the default image drawables are resolved.
+ final boolean loaded = loadCachedPhoto(entry.getKey(), entry.getValue(), false);
+ if (loaded) {
+ iterator.remove();
+ }
+ }
+
+ softenCache();
+
+ if (!mPendingRequests.isEmpty()) {
+ requestLoading();
+ }
+ }
+
+ /**
+ * Removes strong references to loaded bitmaps to allow them to be garbage collected if needed.
+ * Some of the bitmaps will still be retained by {@link #mBitmapCache}.
+ */
+ private void softenCache() {
+ for (BitmapHolder holder : mBitmapHolderCache.snapshot().values()) {
+ holder.bitmap = null;
+ }
+ }
+
+ /** Stores the supplied bitmap in cache. */
+ private void cacheBitmap(Object key, byte[] bytes, boolean preloading, int requestedExtent) {
+ if (DEBUG) {
+ BitmapHolder prev = mBitmapHolderCache.get(key);
+ if (prev != null && prev.bytes != null) {
+ LogUtil.d(
+ "ContactPhotoManagerImpl.cacheBitmap",
+ "overwriting cache: key=" + key + (prev.fresh ? " FRESH" : " stale"));
+ if (prev.fresh) {
+ mFreshCacheOverwrite.incrementAndGet();
+ } else {
+ mStaleCacheOverwrite.incrementAndGet();
+ }
+ }
+ LogUtil.d(
+ "ContactPhotoManagerImpl.cacheBitmap",
+ "caching data: key=" + key + ", " + (bytes == null ? "<null>" : btk(bytes.length)));
+ }
+ BitmapHolder holder =
+ new BitmapHolder(bytes, bytes == null ? -1 : BitmapUtil.getSmallerExtentFromBytes(bytes));
+
+ // Unless this image is being preloaded, decode it right away while
+ // we are still on the background thread.
+ if (!preloading) {
+ inflateBitmap(holder, requestedExtent);
+ }
+
+ if (bytes != null) {
+ mBitmapHolderCache.put(key, holder);
+ if (mBitmapHolderCache.get(key) != holder) {
+ LogUtil.w("ContactPhotoManagerImpl.cacheBitmap", "bitmap too big to fit in cache.");
+ mBitmapHolderCache.put(key, BITMAP_UNAVAILABLE);
+ }
+ } else {
+ mBitmapHolderCache.put(key, BITMAP_UNAVAILABLE);
+ }
+
+ mBitmapHolderCacheAllUnfresh = false;
+ }
+
+ /**
+ * Populates an array of photo IDs that need to be loaded. Also decodes bitmaps that we have
+ * already loaded
+ */
+ private void obtainPhotoIdsAndUrisToLoad(
+ Set<Long> photoIds, Set<String> photoIdsAsStrings, Set<Request> uris) {
+ photoIds.clear();
+ photoIdsAsStrings.clear();
+ uris.clear();
+
+ boolean jpegsDecoded = false;
+
+ /*
+ * Since the call is made from the loader thread, the map could be
+ * changing during the iteration. That's not really a problem:
+ * ConcurrentHashMap will allow those changes to happen without throwing
+ * exceptions. Since we may miss some requests in the situation of
+ * concurrent change, we will need to check the map again once loading
+ * is complete.
+ */
+ Iterator<Request> iterator = mPendingRequests.values().iterator();
+ while (iterator.hasNext()) {
+ Request request = iterator.next();
+ final BitmapHolder holder = mBitmapHolderCache.get(request.getKey());
+ if (holder == BITMAP_UNAVAILABLE) {
+ continue;
+ }
+ if (holder != null
+ && holder.bytes != null
+ && holder.fresh
+ && (holder.bitmapRef == null || holder.bitmapRef.get() == null)) {
+ // This was previously loaded but we don't currently have the inflated Bitmap
+ inflateBitmap(holder, request.getRequestedExtent());
+ jpegsDecoded = true;
+ } else {
+ if (holder == null || !holder.fresh) {
+ if (request.isUriRequest()) {
+ uris.add(request);
+ } else {
+ photoIds.add(request.getId());
+ photoIdsAsStrings.add(String.valueOf(request.mId));
+ }
+ }
+ }
+ }
+
+ if (jpegsDecoded) {
+ mMainThreadHandler.sendEmptyMessage(MESSAGE_PHOTOS_LOADED);
+ }
+ }
+
+ /** Maintains the state of a particular photo. */
+ private static class BitmapHolder {
+
+ final byte[] bytes;
+ final int originalSmallerExtent;
+
+ volatile boolean fresh;
+ Bitmap bitmap;
+ Reference<Bitmap> bitmapRef;
+ int decodedSampleSize;
+
+ public BitmapHolder(byte[] bytes, int originalSmallerExtent) {
+ this.bytes = bytes;
+ this.fresh = true;
+ this.originalSmallerExtent = originalSmallerExtent;
+ }
+ }
+
+ /**
+ * A holder for either a Uri or an id and a flag whether this was requested for the dark or light
+ * theme
+ */
+ private static final class Request {
+
+ private final long mId;
+ private final Uri mUri;
+ private final boolean mDarkTheme;
+ private final int mRequestedExtent;
+ private final DefaultImageProvider mDefaultProvider;
+ /** Whether or not the contact photo is to be displayed as a circle */
+ private final boolean mIsCircular;
+
+ private Request(
+ long id,
+ Uri uri,
+ int requestedExtent,
+ boolean darkTheme,
+ boolean isCircular,
+ DefaultImageProvider defaultProvider) {
+ mId = id;
+ mUri = uri;
+ mDarkTheme = darkTheme;
+ mIsCircular = isCircular;
+ mRequestedExtent = requestedExtent;
+ mDefaultProvider = defaultProvider;
+ }
+
+ public static Request createFromThumbnailId(
+ long id, boolean darkTheme, boolean isCircular, DefaultImageProvider defaultProvider) {
+ return new Request(id, null /* no URI */, -1, darkTheme, isCircular, defaultProvider);
+ }
+
+ public static Request createFromUri(
+ Uri uri,
+ int requestedExtent,
+ boolean darkTheme,
+ boolean isCircular,
+ DefaultImageProvider defaultProvider) {
+ return new Request(
+ 0 /* no ID */, uri, requestedExtent, darkTheme, isCircular, defaultProvider);
+ }
+
+ public boolean isUriRequest() {
+ return mUri != null;
+ }
+
+ public Uri getUri() {
+ return mUri;
+ }
+
+ public long getId() {
+ return mId;
+ }
+
+ public int getRequestedExtent() {
+ return mRequestedExtent;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (mId ^ (mId >>> 32));
+ result = prime * result + mRequestedExtent;
+ result = prime * result + ((mUri == null) ? 0 : mUri.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final Request that = (Request) obj;
+ if (mId != that.mId) {
+ return false;
+ }
+ if (mRequestedExtent != that.mRequestedExtent) {
+ return false;
+ }
+ if (!UriUtils.areEqual(mUri, that.mUri)) {
+ return false;
+ }
+ // Don't compare equality of mDarkTheme because it is only used in the default contact
+ // photo case. When the contact does have a photo, the contact photo is the same
+ // regardless of mDarkTheme, so we shouldn't need to put the photo request on the queue
+ // twice.
+ return true;
+ }
+
+ public Object getKey() {
+ return mUri == null ? mId : mUri;
+ }
+
+ /**
+ * Applies the default image to the current view. If the request is URI-based, looks for the
+ * contact type encoded fragment to determine if this is a request for a business photo, in
+ * which case we will load the default business photo.
+ *
+ * @param view The current image view to apply the image to.
+ * @param isCircular Whether the image is circular or not.
+ */
+ public void applyDefaultImage(ImageView view, boolean isCircular) {
+ final DefaultImageRequest request;
+
+ if (isCircular) {
+ request =
+ ContactPhotoManager.isBusinessContactUri(mUri)
+ ? DefaultImageRequest.EMPTY_CIRCULAR_BUSINESS_IMAGE_REQUEST
+ : DefaultImageRequest.EMPTY_CIRCULAR_DEFAULT_IMAGE_REQUEST;
+ } else {
+ request =
+ ContactPhotoManager.isBusinessContactUri(mUri)
+ ? DefaultImageRequest.EMPTY_DEFAULT_BUSINESS_IMAGE_REQUEST
+ : DefaultImageRequest.EMPTY_DEFAULT_IMAGE_REQUEST;
+ }
+ mDefaultProvider.applyDefaultImage(view, mRequestedExtent, mDarkTheme, request);
+ }
+ }
+
+ /** The thread that performs loading of photos from the database. */
+ private class LoaderThread extends HandlerThread implements Callback {
+
+ private static final int BUFFER_SIZE = 1024 * 16;
+ private static final int MESSAGE_PRELOAD_PHOTOS = 0;
+ private static final int MESSAGE_LOAD_PHOTOS = 1;
+
+ /** A pause between preload batches that yields to the UI thread. */
+ private static final int PHOTO_PRELOAD_DELAY = 1000;
+
+ /** Number of photos to preload per batch. */
+ private static final int PRELOAD_BATCH = 25;
+
+ /**
+ * Maximum number of photos to preload. If the cache size is 2Mb and the expected average size
+ * of a photo is 4kb, then this number should be 2Mb/4kb = 500.
+ */
+ private static final int MAX_PHOTOS_TO_PRELOAD = 100;
+
+ private static final int PRELOAD_STATUS_NOT_STARTED = 0;
+ private static final int PRELOAD_STATUS_IN_PROGRESS = 1;
+ private static final int PRELOAD_STATUS_DONE = 2;
+ private final ContentResolver mResolver;
+ private final StringBuilder mStringBuilder = new StringBuilder();
+ private final Set<Long> mPhotoIds = new HashSet<>();
+ private final Set<String> mPhotoIdsAsStrings = new HashSet<>();
+ private final Set<Request> mPhotoUris = new HashSet<>();
+ private final List<Long> mPreloadPhotoIds = new ArrayList<>();
+ private Handler mLoaderThreadHandler;
+ private byte[] mBuffer;
+ private int mPreloadStatus = PRELOAD_STATUS_NOT_STARTED;
+
+ public LoaderThread(ContentResolver resolver) {
+ super(LOADER_THREAD_NAME);
+ mResolver = resolver;
+ }
+
+ public void ensureHandler() {
+ if (mLoaderThreadHandler == null) {
+ mLoaderThreadHandler = new Handler(getLooper(), this);
+ }
+ }
+
+ /**
+ * Kicks off preloading of the next batch of photos on the background thread. Preloading will
+ * happen after a delay: we want to yield to the UI thread as much as possible.
+ *
+ * <p>If preloading is already complete, does nothing.
+ */
+ public void requestPreloading() {
+ if (mPreloadStatus == PRELOAD_STATUS_DONE) {
+ return;
+ }
+
+ ensureHandler();
+ if (mLoaderThreadHandler.hasMessages(MESSAGE_LOAD_PHOTOS)) {
+ return;
+ }
+
+ mLoaderThreadHandler.sendEmptyMessageDelayed(MESSAGE_PRELOAD_PHOTOS, PHOTO_PRELOAD_DELAY);
+ }
+
+ /**
+ * Sends a message to this thread to load requested photos. Cancels a preloading request, if
+ * any: we don't want preloading to impede loading of the photos we need to display now.
+ */
+ public void requestLoading() {
+ ensureHandler();
+ mLoaderThreadHandler.removeMessages(MESSAGE_PRELOAD_PHOTOS);
+ mLoaderThreadHandler.sendEmptyMessage(MESSAGE_LOAD_PHOTOS);
+ }
+
+ /**
+ * Receives the above message, loads photos and then sends a message to the main thread to
+ * process them.
+ */
+ @Override
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_PRELOAD_PHOTOS:
+ preloadPhotosInBackground();
+ break;
+ case MESSAGE_LOAD_PHOTOS:
+ loadPhotosInBackground();
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * The first time it is called, figures out which photos need to be preloaded. Each subsequent
+ * call preloads the next batch of photos and requests another cycle of preloading after a
+ * delay. The whole process ends when we either run out of photos to preload or fill up cache.
+ */
+ @WorkerThread
+ private void preloadPhotosInBackground() {
+ if (!PermissionsUtil.hasPermission(mContext, android.Manifest.permission.READ_CONTACTS)) {
+ return;
+ }
+
+ if (mPreloadStatus == PRELOAD_STATUS_DONE) {
+ return;
+ }
+
+ if (mPreloadStatus == PRELOAD_STATUS_NOT_STARTED) {
+ queryPhotosForPreload();
+ if (mPreloadPhotoIds.isEmpty()) {
+ mPreloadStatus = PRELOAD_STATUS_DONE;
+ } else {
+ mPreloadStatus = PRELOAD_STATUS_IN_PROGRESS;
+ }
+ requestPreloading();
+ return;
+ }
+
+ if (mBitmapHolderCache.size() > mBitmapHolderCacheRedZoneBytes) {
+ mPreloadStatus = PRELOAD_STATUS_DONE;
+ return;
+ }
+
+ mPhotoIds.clear();
+ mPhotoIdsAsStrings.clear();
+
+ int count = 0;
+ int preloadSize = mPreloadPhotoIds.size();
+ while (preloadSize > 0 && mPhotoIds.size() < PRELOAD_BATCH) {
+ preloadSize--;
+ count++;
+ Long photoId = mPreloadPhotoIds.get(preloadSize);
+ mPhotoIds.add(photoId);
+ mPhotoIdsAsStrings.add(photoId.toString());
+ mPreloadPhotoIds.remove(preloadSize);
+ }
+
+ loadThumbnails(true);
+
+ if (preloadSize == 0) {
+ mPreloadStatus = PRELOAD_STATUS_DONE;
+ }
+
+ LogUtil.v(
+ "ContactPhotoManagerImpl.preloadPhotosInBackground",
+ "preloaded " + count + " photos. cached bytes: " + mBitmapHolderCache.size());
+
+ requestPreloading();
+ }
+
+ @WorkerThread
+ private void queryPhotosForPreload() {
+ Cursor cursor = null;
+ try {
+ Uri uri =
+ Contacts.CONTENT_URI
+ .buildUpon()
+ .appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
+ .appendQueryParameter(
+ ContactsContract.LIMIT_PARAM_KEY, String.valueOf(MAX_PHOTOS_TO_PRELOAD))
+ .build();
+ cursor =
+ mResolver.query(
+ uri,
+ new String[] {Contacts.PHOTO_ID},
+ Contacts.PHOTO_ID + " NOT NULL AND " + Contacts.PHOTO_ID + "!=0",
+ null,
+ Contacts.STARRED + " DESC, " + Contacts.LAST_TIME_CONTACTED + " DESC");
+
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ // Insert them in reverse order, because we will be taking
+ // them from the end of the list for loading.
+ mPreloadPhotoIds.add(0, cursor.getLong(0));
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ @WorkerThread
+ private void loadPhotosInBackground() {
+ if (!PermissionsUtil.hasPermission(mContext, android.Manifest.permission.READ_CONTACTS)) {
+ return;
+ }
+ obtainPhotoIdsAndUrisToLoad(mPhotoIds, mPhotoIdsAsStrings, mPhotoUris);
+ loadThumbnails(false);
+ loadUriBasedPhotos();
+ requestPreloading();
+ }
+
+ /** Loads thumbnail photos with ids */
+ @WorkerThread
+ private void loadThumbnails(boolean preloading) {
+ if (mPhotoIds.isEmpty()) {
+ return;
+ }
+
+ // Remove loaded photos from the preload queue: we don't want
+ // the preloading process to load them again.
+ if (!preloading && mPreloadStatus == PRELOAD_STATUS_IN_PROGRESS) {
+ for (Long id : mPhotoIds) {
+ mPreloadPhotoIds.remove(id);
+ }
+ if (mPreloadPhotoIds.isEmpty()) {
+ mPreloadStatus = PRELOAD_STATUS_DONE;
+ }
+ }
+
+ mStringBuilder.setLength(0);
+ mStringBuilder.append(Photo._ID + " IN(");
+ for (int i = 0; i < mPhotoIds.size(); i++) {
+ if (i != 0) {
+ mStringBuilder.append(',');
+ }
+ mStringBuilder.append('?');
+ }
+ mStringBuilder.append(')');
+
+ Cursor cursor = null;
+ try {
+ if (DEBUG) {
+ LogUtil.d(
+ "ContactPhotoManagerImpl.loadThumbnails",
+ "loading " + TextUtils.join(",", mPhotoIdsAsStrings));
+ }
+ cursor =
+ mResolver.query(
+ Data.CONTENT_URI,
+ COLUMNS,
+ mStringBuilder.toString(),
+ mPhotoIdsAsStrings.toArray(EMPTY_STRING_ARRAY),
+ null);
+
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ Long id = cursor.getLong(0);
+ byte[] bytes = cursor.getBlob(1);
+ cacheBitmap(id, bytes, preloading, -1);
+ mPhotoIds.remove(id);
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ // Remaining photos were not found in the contacts database (but might be in profile).
+ for (Long id : mPhotoIds) {
+ if (ContactsContract.isProfileId(id)) {
+ Cursor profileCursor = null;
+ try {
+ profileCursor =
+ mResolver.query(
+ ContentUris.withAppendedId(Data.CONTENT_URI, id), COLUMNS, null, null, null);
+ if (profileCursor != null && profileCursor.moveToFirst()) {
+ cacheBitmap(profileCursor.getLong(0), profileCursor.getBlob(1), preloading, -1);
+ } else {
+ // Couldn't load a photo this way either.
+ cacheBitmap(id, null, preloading, -1);
+ }
+ } finally {
+ if (profileCursor != null) {
+ profileCursor.close();
+ }
+ }
+ } else {
+ // Not a profile photo and not found - mark the cache accordingly
+ cacheBitmap(id, null, preloading, -1);
+ }
+ }
+
+ mMainThreadHandler.sendEmptyMessage(MESSAGE_PHOTOS_LOADED);
+ }
+
+ /**
+ * Loads photos referenced with Uris. Those can be remote thumbnails (from directory searches),
+ * display photos etc
+ */
+ @WorkerThread
+ private void loadUriBasedPhotos() {
+ for (Request uriRequest : mPhotoUris) {
+ // Keep the original URI and use this to key into the cache. Failure to do so will
+ // result in an image being continually reloaded into cache if the original URI
+ // has a contact type encodedFragment (eg nearby places business photo URLs).
+ Uri originalUri = uriRequest.getUri();
+
+ // Strip off the "contact type" we added to the URI to ensure it was identifiable as
+ // a business photo -- there is no need to pass this on to the server.
+ Uri uri = ContactPhotoManager.removeContactType(originalUri);
+
+ if (mBuffer == null) {
+ mBuffer = new byte[BUFFER_SIZE];
+ }
+ try {
+ if (DEBUG) {
+ LogUtil.d("ContactPhotoManagerImpl.loadUriBasedPhotos", "loading " + uri);
+ }
+ final String scheme = uri.getScheme();
+ InputStream is = null;
+ if (scheme.equals("http") || scheme.equals("https")) {
+ TrafficStats.setThreadStatsTag(TrafficStatsTags.CONTACT_PHOTO_DOWNLOAD_TAG);
+ final HttpURLConnection connection =
+ (HttpURLConnection) new URL(uri.toString()).openConnection();
+
+ // Include the user agent if it is specified.
+ if (!TextUtils.isEmpty(mUserAgent)) {
+ connection.setRequestProperty("User-Agent", mUserAgent);
+ }
+ try {
+ is = connection.getInputStream();
+ } catch (IOException e) {
+ connection.disconnect();
+ is = null;
+ }
+ TrafficStats.clearThreadStatsTag();
+ } else {
+ is = mResolver.openInputStream(uri);
+ }
+ if (is != null) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ int size;
+ while ((size = is.read(mBuffer)) != -1) {
+ baos.write(mBuffer, 0, size);
+ }
+ } finally {
+ is.close();
+ }
+ cacheBitmap(originalUri, baos.toByteArray(), false, uriRequest.getRequestedExtent());
+ mMainThreadHandler.sendEmptyMessage(MESSAGE_PHOTOS_LOADED);
+ } else {
+ LogUtil.v("ContactPhotoManagerImpl.loadUriBasedPhotos", "cannot load photo " + uri);
+ cacheBitmap(originalUri, null, false, uriRequest.getRequestedExtent());
+ }
+ } catch (final Exception | OutOfMemoryError ex) {
+ LogUtil.v("ContactPhotoManagerImpl.loadUriBasedPhotos", "cannot load photo " + uri, ex);
+ cacheBitmap(originalUri, null, false, uriRequest.getRequestedExtent());
+ }
+ }
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/ContactPresenceIconUtil.java b/java/com/android/contacts/common/ContactPresenceIconUtil.java
new file mode 100644
index 000000000..eeaf652a8
--- /dev/null
+++ b/java/com/android/contacts/common/ContactPresenceIconUtil.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 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.content.Context;
+import android.graphics.drawable.Drawable;
+import android.provider.ContactsContract.StatusUpdates;
+
+/** Define the contact present show policy in Contacts */
+public class ContactPresenceIconUtil {
+
+ /**
+ * Get the presence icon resource according the status.
+ *
+ * @return null means don't show the status icon.
+ */
+ public static Drawable getPresenceIcon(Context context, int status) {
+ // We don't show the offline status in Contacts
+ switch (status) {
+ case StatusUpdates.AVAILABLE:
+ case StatusUpdates.IDLE:
+ case StatusUpdates.AWAY:
+ case StatusUpdates.DO_NOT_DISTURB:
+ case StatusUpdates.INVISIBLE:
+ return context.getResources().getDrawable(StatusUpdates.getPresenceIconResourceId(status));
+ case StatusUpdates.OFFLINE:
+ // The undefined status is treated as OFFLINE in getPresenceIconResourceId();
+ default:
+ return null;
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/ContactStatusUtil.java b/java/com/android/contacts/common/ContactStatusUtil.java
new file mode 100644
index 000000000..97d84c876
--- /dev/null
+++ b/java/com/android/contacts/common/ContactStatusUtil.java
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.content.res.Resources;
+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) {
+ case StatusUpdates.AVAILABLE:
+ return resources.getString(R.string.status_available);
+ case StatusUpdates.IDLE:
+ case StatusUpdates.AWAY:
+ return resources.getString(R.string.status_away);
+ case StatusUpdates.DO_NOT_DISTURB:
+ return resources.getString(R.string.status_busy);
+ case StatusUpdates.OFFLINE:
+ case StatusUpdates.INVISIBLE:
+ default:
+ return null;
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/ContactTileLoaderFactory.java b/java/com/android/contacts/common/ContactTileLoaderFactory.java
new file mode 100644
index 000000000..d71472ef8
--- /dev/null
+++ b/java/com/android/contacts/common/ContactTileLoaderFactory.java
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.support.annotation.VisibleForTesting;
+
+/**
+ * Used to create {@link CursorLoader} which finds contacts information from the strequents table.
+ *
+ * <p>Only returns contacts with phone numbers.
+ */
+public final class ContactTileLoaderFactory {
+
+ /**
+ * The _ID field returned for strequent items actually contains data._id instead of contacts._id
+ * because the query is performed on the data table. In order to obtain the contact id for
+ * strequent items, use Phone.contact_id instead.
+ */
+ @VisibleForTesting
+ public static final String[] COLUMNS_PHONE_ONLY =
+ new String[] {
+ Contacts._ID,
+ Contacts.DISPLAY_NAME_PRIMARY,
+ Contacts.STARRED,
+ Contacts.PHOTO_URI,
+ Contacts.LOOKUP_KEY,
+ Phone.NUMBER,
+ Phone.TYPE,
+ Phone.LABEL,
+ Phone.IS_SUPER_PRIMARY,
+ Contacts.PINNED,
+ Phone.CONTACT_ID,
+ Contacts.DISPLAY_NAME_ALTERNATIVE,
+ };
+
+ public static CursorLoader createStrequentPhoneOnlyLoader(Context context) {
+ Uri uri =
+ Contacts.CONTENT_STREQUENT_URI
+ .buildUpon()
+ .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true")
+ .build();
+
+ return new CursorLoader(context, uri, COLUMNS_PHONE_ONLY, null, null, null);
+ }
+}
diff --git a/java/com/android/contacts/common/ContactsUtils.java b/java/com/android/contacts/common/ContactsUtils.java
new file mode 100644
index 000000000..60af44b9a
--- /dev/null
+++ b/java/com/android/contacts/common/ContactsUtils.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2009 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.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<AccountWithDataSet> 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<Intent, Intent> 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.
+ *
+ * <p>3 types of query
+ *
+ * <p>1. 2 profile query: content://com.android.contacts/phone_lookup_enterprise/1234567890
+ * personal and work contact are mixed into one cursor. no directory id. contact_id indicates if
+ * it's work contact
+ *
+ * <p>2. work local query:
+ * content://com.android.contacts/phone_lookup_enterprise/1234567890?directory=1000000000 either
+ * directory_id or contact_id is enough to identify work contact
+ *
+ * <p>3. work remote query:
+ * content://com.android.contacts/phone_lookup_enterprise/1234567890?directory=1000000003
+ * contact_id is random. only directory_id is available
+ *
+ * <p>Summary: If directory_id is not null, always use directory_id to identify work contact.
+ * (which is the case here) Otherwise, use contact_id.
+ *
+ * @param directoryId directory id of ContactsProvider query
+ * @param contactId contact id
+ * @return UserType indicates the user type of the contact. A directory id or contact id larger
+ * than a thredshold indicates that the contact is stored in Work Profile, but not in current
+ * user. It's a contract by ContactsProvider and check by Contacts.isEnterpriseDirectoryId and
+ * Contacts.isEnterpriseContactId. Currently, only 2 kinds of users can be detected from the
+ * directoryId and contactId as ContactsProvider can only access current and work user's
+ * contacts
+ */
+ public static @UserType long determineUserType(Long directoryId, Long contactId) {
+ // First check directory id
+ if (directoryId != null) {
+ return DirectoryCompat.isEnterpriseDirectoryId(directoryId)
+ ? USER_TYPE_WORK
+ : USER_TYPE_CURRENT;
+ }
+ // Only check contact id if directory id is null
+ 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
+ * #USER_TYPE_CURRENT}. Please note that current user can be in work profile, where the dialer is
+ * running inside Work Profile.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({USER_TYPE_CURRENT, USER_TYPE_WORK})
+ public @interface UserType {}
+}
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/GroupMetaData.java b/java/com/android/contacts/common/GroupMetaData.java
new file mode 100644
index 000000000..b34f1d629
--- /dev/null
+++ b/java/com/android/contacts/common/GroupMetaData.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 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;
+
+/**
+ * Meta-data for a contact group. We load all groups associated with the contact's constituent
+ * accounts.
+ */
+public final class GroupMetaData {
+
+ private String mAccountName;
+ private String mAccountType;
+ private String mDataSet;
+ private long mGroupId;
+ private String mTitle;
+ private boolean mDefaultGroup;
+ private boolean mFavorites;
+
+ public GroupMetaData(
+ String accountName,
+ String accountType,
+ String dataSet,
+ long groupId,
+ String title,
+ boolean defaultGroup,
+ boolean favorites) {
+ this.mAccountName = accountName;
+ this.mAccountType = accountType;
+ this.mDataSet = dataSet;
+ this.mGroupId = groupId;
+ this.mTitle = title;
+ this.mDefaultGroup = defaultGroup;
+ this.mFavorites = favorites;
+ }
+
+ public String getAccountName() {
+ return mAccountName;
+ }
+
+ public String getAccountType() {
+ return mAccountType;
+ }
+
+ public String getDataSet() {
+ return mDataSet;
+ }
+
+ public long getGroupId() {
+ return mGroupId;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public boolean isDefaultGroup() {
+ return mDefaultGroup;
+ }
+
+ public boolean isFavorites() {
+ return mFavorites;
+ }
+}
diff --git a/java/com/android/contacts/common/MoreContactUtils.java b/java/com/android/contacts/common/MoreContactUtils.java
new file mode 100644
index 000000000..028f89971
--- /dev/null
+++ b/java/com/android/contacts/common/MoreContactUtils.java
@@ -0,0 +1,251 @@
+/*
+ * 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.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.net.Uri;
+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;
+
+/** Shared static contact utility methods. */
+public class MoreContactUtils {
+
+ private static final String WAIT_SYMBOL_AS_STRING = String.valueOf(PhoneNumberUtils.WAIT);
+
+ /**
+ * Returns true if two data with mimetypes which represent values in contact entries are
+ * considered equal for collapsing in the GUI. For caller-id, use {@link
+ * android.telephony.PhoneNumberUtils#compare(android.content.Context, String, String)} instead
+ */
+ public static boolean shouldCollapse(
+ CharSequence mimetype1, CharSequence data1, CharSequence mimetype2, CharSequence data2) {
+ // different mimetypes? don't collapse
+ if (!TextUtils.equals(mimetype1, mimetype2)) {
+ return false;
+ }
+
+ // exact same string? good, bail out early
+ if (TextUtils.equals(data1, data2)) {
+ return true;
+ }
+
+ // so if either is null, these two must be different
+ if (data1 == null || data2 == null) {
+ return false;
+ }
+
+ // if this is not about phone numbers, we know this is not a match (of course, some
+ // mimetypes could have more sophisticated matching is the future, e.g. addresses)
+ if (!TextUtils.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE, mimetype1)) {
+ return false;
+ }
+
+ return shouldCollapsePhoneNumbers(data1.toString(), data2.toString());
+ }
+
+ // TODO: Move this to PhoneDataItem.shouldCollapse override
+ private static boolean shouldCollapsePhoneNumbers(String number1, String number2) {
+ // Work around to address b/20724444. We want to distinguish between #555, *555 and 555.
+ // This makes no attempt to distinguish between 555 and 55*5, since 55*5 is an improbable
+ // number. PhoneNumberUtil already distinguishes between 555 and 55#5.
+ if (number1.contains("#") != number2.contains("#")
+ || number1.contains("*") != number2.contains("*")) {
+ return false;
+ }
+
+ // Now do the full phone number thing. split into parts, separated by waiting symbol
+ // and compare them individually
+ final String[] dataParts1 = number1.split(WAIT_SYMBOL_AS_STRING);
+ final String[] dataParts2 = number2.split(WAIT_SYMBOL_AS_STRING);
+ if (dataParts1.length != dataParts2.length) {
+ return false;
+ }
+ final PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+ for (int i = 0; i < dataParts1.length; i++) {
+ // Match phone numbers represented by keypad letters, in which case prefer the
+ // phone number with letters.
+ final String dataPart1 = PhoneNumberUtils.convertKeypadLettersToDigits(dataParts1[i]);
+ final String dataPart2 = dataParts2[i];
+
+ // substrings equal? shortcut, don't parse
+ if (TextUtils.equals(dataPart1, dataPart2)) {
+ continue;
+ }
+
+ // do a full parse of the numbers
+ final PhoneNumberUtil.MatchType result = util.isNumberMatch(dataPart1, dataPart2);
+ switch (result) {
+ case NOT_A_NUMBER:
+ // don't understand the numbers? let's play it safe
+ return false;
+ case NO_MATCH:
+ return false;
+ case EXACT_MATCH:
+ break;
+ case NSN_MATCH:
+ try {
+ // For NANP phone numbers, match when one has +1 and the other does not.
+ // In this case, prefer the +1 version.
+ if (util.parse(dataPart1, null).getCountryCode() == 1) {
+ // At this point, the numbers can be either case 1 or 2 below....
+ //
+ // case 1)
+ // +14155551212 <--- country code 1
+ // 14155551212 <--- 1 is trunk prefix, not country code
+ //
+ // and
+ //
+ // case 2)
+ // +14155551212
+ // 4155551212
+ //
+ // From b/7519057, case 2 needs to be equal. But also that bug, case 3
+ // below should not be equal.
+ //
+ // case 3)
+ // 14155551212
+ // 4155551212
+ //
+ // So in order to make sure transitive equality is valid, case 1 cannot
+ // be equal. Otherwise, transitive equality breaks and the following
+ // would all be collapsed:
+ // 4155551212 |
+ // 14155551212 |----> +14155551212
+ // +14155551212 |
+ //
+ // With transitive equality, the collapsed values should be:
+ // 4155551212 | 14155551212
+ // 14155551212 |----> +14155551212
+ // +14155551212 |
+
+ // Distinguish between case 1 and 2 by checking for trunk prefix '1'
+ // at the start of number 2.
+ if (dataPart2.trim().charAt(0) == '1') {
+ // case 1
+ return false;
+ }
+ break;
+ }
+ } catch (NumberParseException e) {
+ // This is the case where the first number does not have a country code.
+ // examples:
+ // (123) 456-7890 & 123-456-7890 (collapse)
+ // 0049 (8092) 1234 & +49/80921234 (unit test says do not collapse)
+
+ // Check the second number. If it also does not have a country code, then
+ // we should collapse. If it has a country code, then it's a different
+ // number and we should not collapse (this conclusion is based on an
+ // existing unit test).
+ try {
+ util.parse(dataPart2, null);
+ } catch (NumberParseException e2) {
+ // Number 2 also does not have a country. Collapse.
+ break;
+ }
+ }
+ return false;
+ case SHORT_NSN_MATCH:
+ return false;
+ default:
+ throw new IllegalStateException("Unknown result value from phone number " + "library");
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns the {@link android.graphics.Rect} with left, top, right, and bottom coordinates that
+ * are equivalent to the given {@link android.view.View}'s bounds. This is equivalent to how the
+ * target {@link android.graphics.Rect} is calculated in {@link
+ * android.provider.ContactsContract.QuickContact#showQuickContact}.
+ */
+ public static Rect getTargetRectFromView(View view) {
+ final int[] pos = new int[2];
+ view.getLocationOnScreen(pos);
+
+ final Rect rect = new Rect();
+ rect.left = pos[0];
+ rect.top = pos[1];
+ rect.right = pos[0] + view.getWidth();
+ rect.bottom = pos[1] + view.getHeight();
+ 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
+ * AccountType#getInviteContactActivityClassName()} or {@link
+ * AccountType#syncAdapterPackageName}).
+ */
+ public static Intent getInvitableIntent(AccountType accountType, Uri lookupUri) {
+ String syncAdapterPackageName = accountType.syncAdapterPackageName;
+ String className = accountType.getInviteContactActivityClassName();
+ if (TextUtils.isEmpty(syncAdapterPackageName) || TextUtils.isEmpty(className)) {
+ return null;
+ }
+ Intent intent = new Intent();
+ intent.setClassName(syncAdapterPackageName, className);
+
+ intent.setAction(ContactsContract.Intents.INVITE_CONTACT);
+
+ // Data is the lookup URI.
+ intent.setData(lookupUri);
+ return intent;
+ }
+}
diff --git a/java/com/android/contacts/common/bindings/ContactsCommonBindings.java b/java/com/android/contacts/common/bindings/ContactsCommonBindings.java
new file mode 100644
index 000000000..44be53b3f
--- /dev/null
+++ b/java/com/android/contacts/common/bindings/ContactsCommonBindings.java
@@ -0,0 +1,25 @@
+/*
+ * 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.bindings;
+
+import android.support.annotation.Nullable;
+
+/** Allows the container application to customize the contacts common library. */
+public interface ContactsCommonBindings {
+
+ /** Builds a user agent string for the current application. */
+ @Nullable
+ String getUserAgent();
+}
diff --git a/java/com/android/contacts/common/bindings/ContactsCommonBindingsFactory.java b/java/com/android/contacts/common/bindings/ContactsCommonBindingsFactory.java
new file mode 100644
index 000000000..8958ad997
--- /dev/null
+++ b/java/com/android/contacts/common/bindings/ContactsCommonBindingsFactory.java
@@ -0,0 +1,24 @@
+/*
+ * 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.bindings;
+
+/**
+ * This interface should be implementated by the Application subclass. It allows the contacts common
+ * module to get references to the ContactsCommonBindings.
+ */
+public interface ContactsCommonBindingsFactory {
+
+ ContactsCommonBindings newContactsCommonBindings();
+}
diff --git a/java/com/android/contacts/common/bindings/ContactsCommonBindingsStub.java b/java/com/android/contacts/common/bindings/ContactsCommonBindingsStub.java
new file mode 100644
index 000000000..f2e21b18e
--- /dev/null
+++ b/java/com/android/contacts/common/bindings/ContactsCommonBindingsStub.java
@@ -0,0 +1,27 @@
+/*
+ * 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.bindings;
+
+import android.support.annotation.Nullable;
+
+/** Default implementation for contacts common bindings. */
+public class ContactsCommonBindingsStub implements ContactsCommonBindings {
+
+ @Override
+ @Nullable
+ public String getUserAgent() {
+ return null;
+ }
+}
diff --git a/java/com/android/contacts/common/compat/CallCompat.java b/java/com/android/contacts/common/compat/CallCompat.java
new file mode 100644
index 000000000..641f7b1bd
--- /dev/null
+++ b/java/com/android/contacts/common/compat/CallCompat.java
@@ -0,0 +1,45 @@
+/*
+ * 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.compat;
+
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.NonNull;
+import android.telecom.Call;
+
+/** Compatibility utilities for android.telecom.Call */
+public class CallCompat {
+
+ public static boolean canPullExternalCall(@NonNull android.telecom.Call call) {
+ return VERSION.SDK_INT >= VERSION_CODES.N_MR1
+ && ((call.getDetails().getCallCapabilities() & Details.CAPABILITY_CAN_PULL_CALL)
+ == Details.CAPABILITY_CAN_PULL_CALL);
+ }
+
+ /** android.telecom.Call.Details */
+ public static class Details {
+
+ public static final int PROPERTY_IS_EXTERNAL_CALL = Call.Details.PROPERTY_IS_EXTERNAL_CALL;
+ public static final int PROPERTY_ENTERPRISE_CALL = Call.Details.PROPERTY_ENTERPRISE_CALL;
+ public static final int CAPABILITY_CAN_PULL_CALL = Call.Details.CAPABILITY_CAN_PULL_CALL;
+ public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO =
+ Call.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO;
+
+ public static final String EXTRA_ANSWERING_DROPS_FOREGROUND_CALL =
+ "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
+ }
+}
diff --git a/java/com/android/contacts/common/compat/CallableCompat.java b/java/com/android/contacts/common/compat/CallableCompat.java
new file mode 100644
index 000000000..5e86f518e
--- /dev/null
+++ b/java/com/android/contacts/common/compat/CallableCompat.java
@@ -0,0 +1,36 @@
+/*
+ * 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.compat;
+
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.ContactsContract.CommonDataKinds.Callable;
+
+public class CallableCompat {
+
+ // TODO: Use N APIs
+ private static final Uri ENTERPRISE_CONTENT_FILTER_URI =
+ Uri.withAppendedPath(Callable.CONTENT_URI, "filter_enterprise");
+
+ public static Uri getContentFilterUri() {
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ return ENTERPRISE_CONTENT_FILTER_URI;
+ }
+ return Callable.CONTENT_FILTER_URI;
+ }
+}
diff --git a/java/com/android/contacts/common/compat/ContactsCompat.java b/java/com/android/contacts/common/compat/ContactsCompat.java
new file mode 100644
index 000000000..39d0b55d3
--- /dev/null
+++ b/java/com/android/contacts/common/compat/ContactsCompat.java
@@ -0,0 +1,57 @@
+/*
+ * 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.compat;
+
+import android.net.Uri;
+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 {
+
+ // 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() {}
+
+ public static Uri getContentUri() {
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ return ENTERPRISE_CONTENT_FILTER_URI;
+ }
+ 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/DirectoryCompat.java b/java/com/android/contacts/common/compat/DirectoryCompat.java
new file mode 100644
index 000000000..85f4a4202
--- /dev/null
+++ b/java/com/android/contacts/common/compat/DirectoryCompat.java
@@ -0,0 +1,51 @@
+/*
+ * 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.compat;
+
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.ContactsContract.Directory;
+
+public class DirectoryCompat {
+
+ public static Uri getContentUri() {
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ return Directory.ENTERPRISE_CONTENT_URI;
+ }
+ return Directory.CONTENT_URI;
+ }
+
+ public static boolean isInvisibleDirectory(long directoryId) {
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ return (directoryId == Directory.LOCAL_INVISIBLE
+ || directoryId == Directory.ENTERPRISE_LOCAL_INVISIBLE);
+ }
+ return directoryId == Directory.LOCAL_INVISIBLE;
+ }
+
+ public static boolean isRemoteDirectoryId(long directoryId) {
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ return Directory.isRemoteDirectoryId(directoryId);
+ }
+ return !(directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE);
+ }
+
+ public static boolean isEnterpriseDirectoryId(long directoryId) {
+ return VERSION.SDK_INT >= VERSION_CODES.N && Directory.isEnterpriseDirectoryId(directoryId);
+ }
+}
diff --git a/java/com/android/contacts/common/compat/PhoneAccountCompat.java b/java/com/android/contacts/common/compat/PhoneAccountCompat.java
new file mode 100644
index 000000000..6a24ec033
--- /dev/null
+++ b/java/com/android/contacts/common/compat/PhoneAccountCompat.java
@@ -0,0 +1,104 @@
+/*
+ * 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.compat;
+
+import android.content.Context;
+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.
+ *
+ * @param phoneAccount the PhoneAccount from which to build the icon.
+ * @param context A {@code Context} to use for loading Drawables.
+ * @return An icon for this PhoneAccount, or null
+ */
+ @Nullable
+ public static Drawable createIconDrawable(
+ @Nullable PhoneAccount phoneAccount, @Nullable Context context) {
+ if (phoneAccount == null || context == null) {
+ return null;
+ }
+
+ 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 = 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/PhoneCompat.java b/java/com/android/contacts/common/compat/PhoneCompat.java
new file mode 100644
index 000000000..31db7b537
--- /dev/null
+++ b/java/com/android/contacts/common/compat/PhoneCompat.java
@@ -0,0 +1,36 @@
+/*
+ * 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.compat;
+
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+
+public class PhoneCompat {
+
+ // TODO: Use N APIs
+ private static final Uri ENTERPRISE_CONTENT_FILTER_URI =
+ Uri.withAppendedPath(Phone.CONTENT_URI, "filter_enterprise");
+
+ public static Uri getContentFilterUri() {
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ return ENTERPRISE_CONTENT_FILTER_URI;
+ }
+ return Phone.CONTENT_FILTER_URI;
+ }
+}
diff --git a/java/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java b/java/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java
new file mode 100644
index 000000000..960b340d8
--- /dev/null
+++ b/java/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java
@@ -0,0 +1,174 @@
+/*
+ * 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.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
+ * added in API level 23. In this way, we could enable the corresponding functionality for pre-M
+ * devices. We need maintain this class and keep it synced with PhoneNumberUtils. Another thing to
+ * keep in mind is that we use com.google.i18n rather than com.android.i18n in here, so we need make
+ * sure the application behavior is preserved.
+ */
+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) {
+ if (CompatUtils.isMarshmallowCompatible()) {
+ return PhoneNumberUtils.createTtsSpannable(phoneNumber);
+ } else {
+ return createTtsSpannableInternal(phoneNumber);
+ }
+ }
+
+ public static TtsSpan createTtsSpan(String 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
new file mode 100644
index 000000000..c8665af51
--- /dev/null
+++ b/java/com/android/contacts/common/compat/TelephonyManagerCompat.java
@@ -0,0 +1,213 @@
+/*
+ * 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.compat;
+
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.Nullable;
+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 {
+
+ // TODO: Use public API for these constants when available
+ public static final String EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE =
+ "android.telephony.event.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE";
+ public static final String EVENT_HANDOVER_TO_WIFI_FAILED =
+ "android.telephony.event.EVENT_HANDOVER_TO_WIFI_FAILED";
+ public static final String EVENT_CALL_REMOTELY_HELD = "android.telecom.event.CALL_REMOTELY_HELD";
+ public static final String EVENT_CALL_REMOTELY_UNHELD =
+ "android.telecom.event.CALL_REMOTELY_UNHELD";
+
+ public static final String TELEPHONY_MANAGER_CLASS = "android.telephony.TelephonyManager";
+
+ /**
+ * @param telephonyManager The telephony manager instance to use for method calls.
+ * @return true if the current device is "voice capable".
+ * <p>"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.
+ * <p>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)
+ *
+ * <p>Returns 1 if the method or telephonyManager is not available.
+ *
+ * @param telephonyManager The telephony manager instance to use for method calls.
+ */
+ public static int getPhoneCount(@Nullable TelephonyManager telephonyManager) {
+ if (telephonyManager == null) {
+ return 1;
+ }
+ 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.
+ *
+ * <p>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;
+ }
+
+ /**
+ * Whether the phone supports TTY mode.
+ *
+ * @param telephonyManager The telephony manager instance to use for method calls.
+ * @return {@code true} if the device supports TTY mode, and {@code false} otherwise.
+ */
+ public static boolean isTtyModeSupported(@Nullable TelephonyManager telephonyManager) {
+ if (telephonyManager == null) {
+ return false;
+ }
+ if (CompatUtils.isMarshmallowCompatible()
+ || CompatUtils.isMethodAvailable(TELEPHONY_MANAGER_CLASS, "isTtyModeSupported")) {
+ return telephonyManager.isTtyModeSupported();
+ }
+ return false;
+ }
+
+ /**
+ * Whether the phone supports hearing aid compatibility.
+ *
+ * @param telephonyManager The telephony manager instance to use for method calls.
+ * @return {@code true} if the device supports hearing aid compatibility, and {@code false}
+ * otherwise.
+ */
+ public static boolean isHearingAidCompatibilitySupported(
+ @Nullable TelephonyManager telephonyManager) {
+ if (telephonyManager == null) {
+ return false;
+ }
+ if (CompatUtils.isMarshmallowCompatible()
+ || CompatUtils.isMethodAvailable(
+ TELEPHONY_MANAGER_CLASS, "isHearingAidCompatibilitySupported")) {
+ return telephonyManager.isHearingAidCompatibilitySupported();
+ }
+ return false;
+ }
+
+ /**
+ * Returns the URI for the per-account voicemail ringtone set in Phone settings.
+ *
+ * @param telephonyManager The telephony manager instance to use for method calls.
+ * @param accountHandle The handle for the {@link android.telecom.PhoneAccount} for which to
+ * retrieve the voicemail ringtone.
+ * @return The URI for the ringtone to play when receiving a voicemail from a specific
+ * PhoneAccount.
+ */
+ @Nullable
+ public static Uri getVoicemailRingtoneUri(
+ TelephonyManager telephonyManager, PhoneAccountHandle accountHandle) {
+ if (VERSION.SDK_INT < VERSION_CODES.N) {
+ return null;
+ }
+ return telephonyManager.getVoicemailRingtoneUri(accountHandle);
+ }
+
+ /**
+ * Returns whether vibration is set for voicemail notification in Phone settings.
+ *
+ * @param telephonyManager The telephony manager instance to use for method calls.
+ * @param accountHandle The handle for the {@link android.telecom.PhoneAccount} for which to
+ * retrieve the voicemail vibration setting.
+ * @return {@code true} if the vibration is set for this PhoneAccount, {@code false} otherwise.
+ */
+ public static boolean isVoicemailVibrationEnabled(
+ TelephonyManager telephonyManager, PhoneAccountHandle accountHandle) {
+ return VERSION.SDK_INT < VERSION_CODES.N
+ || telephonyManager.isVoicemailVibrationEnabled(accountHandle);
+ }
+
+ /**
+ * This method uses a new system API to enable or disable visual voicemail. TODO: restrict
+ * to N MR1, not needed in future SDK.
+ */
+ public static void setVisualVoicemailEnabled(
+ TelephonyManager telephonyManager, PhoneAccountHandle handle, boolean enabled) {
+ if (VERSION.SDK_INT < VERSION_CODES.N_MR1) {
+ Assert.fail("setVisualVoicemailEnabled called on pre-NMR1");
+ }
+ try {
+ TelephonyManager.class
+ .getMethod("setVisualVoicemailEnabled", PhoneAccountHandle.class, boolean.class)
+ .invoke(telephonyManager, handle, enabled);
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ LogUtil.e("TelephonyManagerCompat.setVisualVoicemailEnabled", "failed", e);
+ }
+ }
+
+ /**
+ * This method uses a new system API to check if visual voicemail is enabled TODO: restrict
+ * to N MR1, not needed in future SDK.
+ */
+ public static boolean isVisualVoicemailEnabled(
+ TelephonyManager telephonyManager, PhoneAccountHandle handle) {
+ if (VERSION.SDK_INT < VERSION_CODES.N_MR1) {
+ Assert.fail("isVisualVoicemailEnabled called on pre-NMR1");
+ }
+ try {
+ return (boolean)
+ TelephonyManager.class
+ .getMethod("isVisualVoicemailEnabled", PhoneAccountHandle.class)
+ .invoke(telephonyManager, handle);
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ LogUtil.e("TelephonyManagerCompat.setVisualVoicemailEnabled", "failed", e);
+ }
+ return false;
+ }
+}
diff --git a/java/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java b/java/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java
new file mode 100644
index 000000000..5687f6fbf
--- /dev/null
+++ b/java/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java
@@ -0,0 +1,302 @@
+/*
+ * 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.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<PhoneAccountHandle> 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:
+ *
+ * <p>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.
+ *
+ * <p>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.
+ *
+ * @param telecomManager the {@link TelecomManager} to use to fetch the SIM call manager.
+ * @return The phone account handle of the current sim call manager.
+ */
+ @Nullable
+ public static PhoneAccountHandle getSimCallManager(TelecomManager telecomManager) {
+ if (telecomManager != null
+ && (CompatUtils.isMarshmallowCompatible()
+ || CompatUtils.isMethodAvailable(TELECOM_MANAGER_CLASS, "getSimCallManager"))) {
+ return telecomManager.getSimCallManager();
+ }
+ return null;
+ }
+}
diff --git a/java/com/android/contacts/common/database/ContactUpdateUtils.java b/java/com/android/contacts/common/database/ContactUpdateUtils.java
new file mode 100644
index 000000000..1a9febc07
--- /dev/null
+++ b/java/com/android/contacts/common/database/ContactUpdateUtils.java
@@ -0,0 +1,49 @@
+/*
+ * 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.database;
+
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract;
+import android.util.Log;
+
+/** Static methods to update contact information. */
+public class ContactUpdateUtils {
+
+ private static final String TAG = ContactUpdateUtils.class.getSimpleName();
+
+ public static void setSuperPrimary(Context context, long dataId) {
+ if (dataId == -1) {
+ Log.e(TAG, "Invalid arguments for setSuperPrimary request");
+ return;
+ }
+
+ // Update the primary values in the data record.
+ ContentValues values = new ContentValues(2);
+ values.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1);
+ values.put(ContactsContract.Data.IS_PRIMARY, 1);
+
+ context
+ .getContentResolver()
+ .update(
+ ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, dataId),
+ values,
+ null,
+ null);
+ }
+}
diff --git a/java/com/android/contacts/common/database/EmptyCursor.java b/java/com/android/contacts/common/database/EmptyCursor.java
new file mode 100644
index 000000000..c2b24cdf7
--- /dev/null
+++ b/java/com/android/contacts/common/database/EmptyCursor.java
@@ -0,0 +1,84 @@
+/*
+ * 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.database;
+
+import android.database.AbstractCursor;
+import android.database.CursorIndexOutOfBoundsException;
+
+/**
+ * A cursor that is empty.
+ *
+ * <p>If you want an empty cursor, this class is better than a MatrixCursor because it has less
+ * overhead.
+ */
+public final class EmptyCursor extends AbstractCursor {
+
+ private String[] mColumns;
+
+ public EmptyCursor(String[] columns) {
+ this.mColumns = columns;
+ }
+
+ @Override
+ public int getCount() {
+ return 0;
+ }
+
+ @Override
+ public String[] getColumnNames() {
+ return mColumns;
+ }
+
+ @Override
+ public String getString(int column) {
+ throw cursorException();
+ }
+
+ @Override
+ public short getShort(int column) {
+ throw cursorException();
+ }
+
+ @Override
+ public int getInt(int column) {
+ throw cursorException();
+ }
+
+ @Override
+ public long getLong(int column) {
+ throw cursorException();
+ }
+
+ @Override
+ public float getFloat(int column) {
+ throw cursorException();
+ }
+
+ @Override
+ public double getDouble(int column) {
+ throw cursorException();
+ }
+
+ @Override
+ public boolean isNull(int column) {
+ throw cursorException();
+ }
+
+ private CursorIndexOutOfBoundsException cursorException() {
+ return new CursorIndexOutOfBoundsException("Operation not permitted on an empty cursor.");
+ }
+}
diff --git a/java/com/android/contacts/common/database/NoNullCursorAsyncQueryHandler.java b/java/com/android/contacts/common/database/NoNullCursorAsyncQueryHandler.java
new file mode 100644
index 000000000..d5e61354a
--- /dev/null
+++ b/java/com/android/contacts/common/database/NoNullCursorAsyncQueryHandler.java
@@ -0,0 +1,73 @@
+/*
+ * 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.database;
+
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * An {@AsyncQueryHandler} that will never return a null cursor.
+ *
+ * <p>Instead, will return a {@link Cursor} with 0 records.
+ */
+public abstract class NoNullCursorAsyncQueryHandler extends AsyncQueryHandler {
+
+ public NoNullCursorAsyncQueryHandler(ContentResolver cr) {
+ super(cr);
+ }
+
+ @Override
+ public void startQuery(
+ int token,
+ Object cookie,
+ Uri uri,
+ String[] projection,
+ String selection,
+ String[] selectionArgs,
+ String orderBy) {
+ final CookieWithProjection projectionCookie = new CookieWithProjection(cookie, projection);
+ super.startQuery(token, projectionCookie, uri, projection, selection, selectionArgs, orderBy);
+ }
+
+ @Override
+ protected final void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ CookieWithProjection projectionCookie = (CookieWithProjection) cookie;
+
+ super.onQueryComplete(token, projectionCookie.originalCookie, cursor);
+
+ if (cursor == null) {
+ cursor = new EmptyCursor(projectionCookie.projection);
+ }
+ onNotNullableQueryComplete(token, projectionCookie.originalCookie, cursor);
+ }
+
+ protected abstract void onNotNullableQueryComplete(int token, Object cookie, Cursor cursor);
+
+ /** Class to add projection to an existing cookie. */
+ private static class CookieWithProjection {
+
+ public final Object originalCookie;
+ public final String[] projection;
+
+ public CookieWithProjection(Object cookie, String[] projection) {
+ this.originalCookie = cookie;
+ this.projection = projection;
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/dialog/CallSubjectDialog.java b/java/com/android/contacts/common/dialog/CallSubjectDialog.java
new file mode 100644
index 000000000..d2e3a2357
--- /dev/null
+++ b/java/com/android/contacts/common/dialog/CallSubjectDialog.java
@@ -0,0 +1,607 @@
+/*
+ * 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.dialog;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.ListView;
+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.compat.telecom.TelecomManagerCompat;
+import com.android.contacts.common.util.UriUtils;
+import com.android.dialer.animation.AnimUtils;
+import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.callintent.nano.CallInitiationType;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.util.ViewUtil;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implements a dialog which prompts for a call subject for an outgoing call. The dialog includes a
+ * pop up list of historical call subjects.
+ */
+public class CallSubjectDialog extends Activity {
+
+ public static final String PREF_KEY_SUBJECT_HISTORY_COUNT = "subject_history_count";
+ public static final String PREF_KEY_SUBJECT_HISTORY_ITEM = "subject_history_item";
+ /** Activity intent argument bundle keys: */
+ public static final String ARG_PHOTO_ID = "PHOTO_ID";
+
+ public static final String ARG_PHOTO_URI = "PHOTO_URI";
+ public static final String ARG_CONTACT_URI = "CONTACT_URI";
+ public static final String ARG_NAME_OR_NUMBER = "NAME_OR_NUMBER";
+ public static final String ARG_IS_BUSINESS = "IS_BUSINESS";
+ public static final String ARG_NUMBER = "NUMBER";
+ public static final String ARG_DISPLAY_NUMBER = "DISPLAY_NUMBER";
+ public static final String ARG_NUMBER_LABEL = "NUMBER_LABEL";
+ public static final String ARG_PHONE_ACCOUNT_HANDLE = "PHONE_ACCOUNT_HANDLE";
+ private static final int CALL_SUBJECT_LIMIT = 16;
+ private static final int CALL_SUBJECT_HISTORY_SIZE = 5;
+ private int mAnimationDuration;
+ private Charset mMessageEncoding;
+ private View mBackgroundView;
+ private View mDialogView;
+ private QuickContactBadge mContactPhoto;
+ private TextView mNameView;
+ private TextView mNumberView;
+ private EditText mCallSubjectView;
+ private TextView mCharacterLimitView;
+ private View mHistoryButton;
+ private View mSendAndCallButton;
+ private ListView mSubjectList;
+
+ private int mLimit = CALL_SUBJECT_LIMIT;
+ /** Handles changes to the text in the subject box. Ensures the character limit is updated. */
+ private final TextWatcher mTextWatcher =
+ new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // no-op
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ updateCharacterLimit();
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ // no-op
+ }
+ };
+
+ private int mPhotoSize;
+ private SharedPreferences mPrefs;
+ private List<String> mSubjectHistory;
+ /** Handles displaying the list of past call subjects. */
+ private final View.OnClickListener mHistoryOnClickListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ hideSoftKeyboard(CallSubjectDialog.this, mCallSubjectView);
+ showCallHistory(mSubjectList.getVisibility() == View.GONE);
+ }
+ };
+ /**
+ * Handles auto-hiding the call history when user clicks in the call subject field to give it
+ * focus.
+ */
+ private final View.OnClickListener mCallSubjectClickListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mSubjectList.getVisibility() == View.VISIBLE) {
+ showCallHistory(false);
+ }
+ }
+ };
+
+ private long mPhotoID;
+ private Uri mPhotoUri;
+ private Uri mContactUri;
+ private String mNameOrNumber;
+ private boolean mIsBusiness;
+ private String mNumber;
+ private String mDisplayNumber;
+ private String mNumberLabel;
+ private PhoneAccountHandle mPhoneAccountHandle;
+ /** Handles starting a call with a call subject specified. */
+ private final View.OnClickListener mSendAndCallOnClickListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String subject = mCallSubjectView.getText().toString();
+ Intent intent =
+ new CallIntentBuilder(mNumber, CallInitiationType.Type.CALL_SUBJECT_DIALOG)
+ .setPhoneAccountHandle(mPhoneAccountHandle)
+ .setCallSubject(subject)
+ .build();
+
+ TelecomManagerCompat.placeCall(
+ CallSubjectDialog.this,
+ (TelecomManager) getSystemService(Context.TELECOM_SERVICE),
+ intent);
+
+ mSubjectHistory.add(subject);
+ saveSubjectHistory(mSubjectHistory);
+ finish();
+ }
+ };
+ /** Click listener which handles user clicks outside of the dialog. */
+ private View.OnClickListener mBackgroundListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ };
+ /**
+ * Item click listener which handles user clicks on the items in the list view. Dismisses the
+ * activity, returning the subject to the caller and closing the activity with the {@link
+ * Activity#RESULT_OK} result code.
+ */
+ private AdapterView.OnItemClickListener mItemClickListener =
+ new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View view, int position, long arg3) {
+ mCallSubjectView.setText(mSubjectHistory.get(position));
+ showCallHistory(false);
+ }
+ };
+
+ /**
+ * Show the call subject dialog given a phone number to dial (e.g. from the dialpad).
+ *
+ * @param activity The activity.
+ * @param number The number to dial.
+ */
+ public static void start(Activity activity, String number) {
+ start(
+ activity,
+ -1 /* photoId */,
+ null /* photoUri */,
+ null /* contactUri */,
+ number /* nameOrNumber */,
+ false /* isBusiness */,
+ number /* number */,
+ null /* displayNumber */,
+ null /* numberLabel */,
+ null /* phoneAccountHandle */);
+ }
+
+ /**
+ * Creates a call subject dialog.
+ *
+ * @param activity The current activity.
+ * @param photoId The photo ID (used to populate contact photo).
+ * @param photoUri The photo Uri (used to populate contact photo).
+ * @param contactUri The Contact URI (used so quick contact can be invoked from contact photo).
+ * @param nameOrNumber The name or number of the callee.
+ * @param isBusiness {@code true} if a business is being called (used for contact photo).
+ * @param number The raw number to dial.
+ * @param displayNumber The number to dial, formatted for display.
+ * @param numberLabel The label for the number (if from a contact).
+ * @param phoneAccountHandle The phone account handle.
+ */
+ public static void start(
+ Activity activity,
+ long photoId,
+ Uri photoUri,
+ Uri contactUri,
+ String nameOrNumber,
+ boolean isBusiness,
+ String number,
+ String displayNumber,
+ String numberLabel,
+ PhoneAccountHandle phoneAccountHandle) {
+ Bundle arguments = new Bundle();
+ arguments.putLong(ARG_PHOTO_ID, photoId);
+ arguments.putParcelable(ARG_PHOTO_URI, photoUri);
+ arguments.putParcelable(ARG_CONTACT_URI, contactUri);
+ arguments.putString(ARG_NAME_OR_NUMBER, nameOrNumber);
+ arguments.putBoolean(ARG_IS_BUSINESS, isBusiness);
+ arguments.putString(ARG_NUMBER, number);
+ arguments.putString(ARG_DISPLAY_NUMBER, displayNumber);
+ arguments.putString(ARG_NUMBER_LABEL, numberLabel);
+ arguments.putParcelable(ARG_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+ start(activity, arguments);
+ }
+
+ /**
+ * Shows the call subject dialog given a Bundle containing all the arguments required to display
+ * the dialog (e.g. from Quick Contacts).
+ *
+ * @param activity The activity.
+ * @param arguments The arguments bundle.
+ */
+ public static void start(Activity activity, Bundle arguments) {
+ Intent intent = new Intent(activity, CallSubjectDialog.class);
+ intent.putExtras(arguments);
+ activity.startActivity(intent);
+ }
+
+ /**
+ * Loads the subject history from shared preferences.
+ *
+ * @param prefs Shared preferences.
+ * @return List of subject history strings.
+ */
+ public static List<String> loadSubjectHistory(SharedPreferences prefs) {
+ int historySize = prefs.getInt(PREF_KEY_SUBJECT_HISTORY_COUNT, 0);
+ List<String> subjects = new ArrayList(historySize);
+
+ for (int ix = 0; ix < historySize; ix++) {
+ String historyItem = prefs.getString(PREF_KEY_SUBJECT_HISTORY_ITEM + ix, null);
+ if (!TextUtils.isEmpty(historyItem)) {
+ subjects.add(historyItem);
+ }
+ }
+
+ return subjects;
+ }
+
+ /**
+ * Creates the dialog, inflating the layout and populating it with the name and phone number.
+ *
+ * @param savedInstanceState The last saved instance state of the Fragment, or null if this is a
+ * freshly created Fragment.
+ * @return Dialog instance.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mAnimationDuration = getResources().getInteger(R.integer.call_subject_animation_duration);
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ mPhotoSize =
+ getResources().getDimensionPixelSize(R.dimen.call_subject_dialog_contact_photo_size);
+ readArguments();
+ loadConfiguration();
+ mSubjectHistory = loadSubjectHistory(mPrefs);
+
+ setContentView(R.layout.dialog_call_subject);
+ getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ mBackgroundView = findViewById(R.id.call_subject_dialog);
+ mBackgroundView.setOnClickListener(mBackgroundListener);
+ mDialogView = findViewById(R.id.dialog_view);
+ mContactPhoto = (QuickContactBadge) findViewById(R.id.contact_photo);
+ mNameView = (TextView) findViewById(R.id.name);
+ mNumberView = (TextView) findViewById(R.id.number);
+ mCallSubjectView = (EditText) findViewById(R.id.call_subject);
+ mCallSubjectView.addTextChangedListener(mTextWatcher);
+ mCallSubjectView.setOnClickListener(mCallSubjectClickListener);
+ InputFilter[] filters = new InputFilter[1];
+ filters[0] = new InputFilter.LengthFilter(mLimit);
+ mCallSubjectView.setFilters(filters);
+ mCharacterLimitView = (TextView) findViewById(R.id.character_limit);
+ mHistoryButton = findViewById(R.id.history_button);
+ mHistoryButton.setOnClickListener(mHistoryOnClickListener);
+ mHistoryButton.setVisibility(mSubjectHistory.isEmpty() ? View.GONE : View.VISIBLE);
+ mSendAndCallButton = findViewById(R.id.send_and_call_button);
+ mSendAndCallButton.setOnClickListener(mSendAndCallOnClickListener);
+ mSubjectList = (ListView) findViewById(R.id.subject_list);
+ mSubjectList.setOnItemClickListener(mItemClickListener);
+ mSubjectList.setVisibility(View.GONE);
+
+ updateContactInfo();
+ updateCharacterLimit();
+ }
+
+ /** Populates the contact info fields based on the current contact information. */
+ private void updateContactInfo() {
+ if (mContactUri != null) {
+ setPhoto(mPhotoID, mPhotoUri, mContactUri, mNameOrNumber, mIsBusiness);
+ } else {
+ mContactPhoto.setVisibility(View.GONE);
+ }
+ mNameView.setText(mNameOrNumber);
+ if (!TextUtils.isEmpty(mNumberLabel) && !TextUtils.isEmpty(mDisplayNumber)) {
+ mNumberView.setVisibility(View.VISIBLE);
+ mNumberView.setText(
+ getString(R.string.call_subject_type_and_number, mNumberLabel, mDisplayNumber));
+ } else {
+ mNumberView.setVisibility(View.GONE);
+ mNumberView.setText(null);
+ }
+ }
+
+ /** Reads arguments from the fragment arguments and populates the necessary instance variables. */
+ private void readArguments() {
+ Bundle arguments = getIntent().getExtras();
+ if (arguments == null) {
+ LogUtil.e("CallSubjectDialog.readArguments", "arguments cannot be null");
+ return;
+ }
+ mPhotoID = arguments.getLong(ARG_PHOTO_ID);
+ mPhotoUri = arguments.getParcelable(ARG_PHOTO_URI);
+ mContactUri = arguments.getParcelable(ARG_CONTACT_URI);
+ mNameOrNumber = arguments.getString(ARG_NAME_OR_NUMBER);
+ mIsBusiness = arguments.getBoolean(ARG_IS_BUSINESS);
+ mNumber = arguments.getString(ARG_NUMBER);
+ mDisplayNumber = arguments.getString(ARG_DISPLAY_NUMBER);
+ mNumberLabel = arguments.getString(ARG_NUMBER_LABEL);
+ mPhoneAccountHandle = arguments.getParcelable(ARG_PHONE_ACCOUNT_HANDLE);
+ }
+
+ /**
+ * Updates the character limit display, coloring the text RED when the limit is reached or
+ * exceeded.
+ */
+ private void updateCharacterLimit() {
+ String subjectText = mCallSubjectView.getText().toString();
+ final int length;
+
+ // If a message encoding is specified, use that to count bytes in the message.
+ if (mMessageEncoding != null) {
+ length = subjectText.getBytes(mMessageEncoding).length;
+ } else {
+ // No message encoding specified, so just count characters entered.
+ length = subjectText.length();
+ }
+
+ mCharacterLimitView.setText(getString(R.string.call_subject_limit, length, mLimit));
+ if (length >= mLimit) {
+ mCharacterLimitView.setTextColor(
+ getResources().getColor(R.color.call_subject_limit_exceeded));
+ } else {
+ mCharacterLimitView.setTextColor(
+ getResources().getColor(R.color.dialer_secondary_text_color));
+ }
+ }
+
+ /** Sets the photo on the quick contact photo. */
+ private void setPhoto(
+ long photoId, Uri photoUri, Uri contactUri, String displayName, boolean isBusiness) {
+ mContactPhoto.assignContactUri(contactUri);
+ if (CompatUtils.isLollipopCompatible()) {
+ mContactPhoto.setOverlay(null);
+ }
+
+ int contactType;
+ if (isBusiness) {
+ contactType = ContactPhotoManager.TYPE_BUSINESS;
+ } else {
+ contactType = ContactPhotoManager.TYPE_DEFAULT;
+ }
+
+ String lookupKey = null;
+ if (contactUri != null) {
+ lookupKey = UriUtils.getLookupKeyFromUri(contactUri);
+ }
+
+ ContactPhotoManager.DefaultImageRequest request =
+ new ContactPhotoManager.DefaultImageRequest(
+ displayName, lookupKey, contactType, true /* isCircular */);
+
+ if (photoId == 0 && photoUri != null) {
+ ContactPhotoManager.getInstance(this)
+ .loadPhoto(
+ mContactPhoto,
+ photoUri,
+ mPhotoSize,
+ false /* darkTheme */,
+ true /* isCircular */,
+ request);
+ } else {
+ ContactPhotoManager.getInstance(this)
+ .loadThumbnail(
+ mContactPhoto, photoId, false /* darkTheme */, true /* isCircular */, request);
+ }
+ }
+
+ /**
+ * Saves the subject history list to shared prefs, removing older items so that there are only
+ * {@link #CALL_SUBJECT_HISTORY_SIZE} items at most.
+ *
+ * @param history The history.
+ */
+ private void saveSubjectHistory(List<String> history) {
+ // Remove oldest subject(s).
+ while (history.size() > CALL_SUBJECT_HISTORY_SIZE) {
+ history.remove(0);
+ }
+
+ SharedPreferences.Editor editor = mPrefs.edit();
+ int historyCount = 0;
+ for (String subject : history) {
+ if (!TextUtils.isEmpty(subject)) {
+ editor.putString(PREF_KEY_SUBJECT_HISTORY_ITEM + historyCount, subject);
+ historyCount++;
+ }
+ }
+ editor.putInt(PREF_KEY_SUBJECT_HISTORY_COUNT, historyCount);
+ editor.apply();
+ }
+
+ /** Hide software keyboard for the given {@link View}. */
+ public void hideSoftKeyboard(Context context, View view) {
+ InputMethodManager imm =
+ (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
+ }
+ }
+
+ /**
+ * Hides or shows the call history list.
+ *
+ * @param show {@code true} if the call history should be shown, {@code false} otherwise.
+ */
+ private void showCallHistory(final boolean show) {
+ // Bail early if the visibility has not changed.
+ if ((show && mSubjectList.getVisibility() == View.VISIBLE)
+ || (!show && mSubjectList.getVisibility() == View.GONE)) {
+ return;
+ }
+
+ final int dialogStartingBottom = mDialogView.getBottom();
+ if (show) {
+ // Showing the subject list; bind the list of history items to the list and show it.
+ ArrayAdapter<String> adapter =
+ new ArrayAdapter<String>(
+ CallSubjectDialog.this, R.layout.call_subject_history_list_item, mSubjectHistory);
+ mSubjectList.setAdapter(adapter);
+ mSubjectList.setVisibility(View.VISIBLE);
+ } else {
+ // Hiding the subject list.
+ mSubjectList.setVisibility(View.GONE);
+ }
+
+ // Use a ViewTreeObserver so that we can animate between the pre-layout and post-layout
+ // states.
+ ViewUtil.doOnPreDraw(
+ mBackgroundView,
+ true,
+ new Runnable() {
+ @Override
+ public void run() {
+ // Determine the amount the dialog has shifted due to the relayout.
+ int shiftAmount = dialogStartingBottom - mDialogView.getBottom();
+
+ // If the dialog needs to be shifted, do that now.
+ if (shiftAmount != 0) {
+ // Start animation in translated state and animate to translationY 0.
+ mDialogView.setTranslationY(shiftAmount);
+ mDialogView
+ .animate()
+ .translationY(0)
+ .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
+ .setDuration(mAnimationDuration)
+ .start();
+ }
+
+ if (show) {
+ // Show the subject list.
+ mSubjectList.setTranslationY(mSubjectList.getHeight());
+
+ mSubjectList
+ .animate()
+ .translationY(0)
+ .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
+ .setDuration(mAnimationDuration)
+ .setListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ mSubjectList.setVisibility(View.VISIBLE);
+ }
+ })
+ .start();
+ } else {
+ // Hide the subject list.
+ mSubjectList.setTranslationY(0);
+
+ mSubjectList
+ .animate()
+ .translationY(mSubjectList.getHeight())
+ .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
+ .setDuration(mAnimationDuration)
+ .setListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mSubjectList.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ }
+ })
+ .start();
+ }
+ }
+ });
+ }
+
+ /**
+ * Loads the message encoding and maximum message length from the phone account extras for the
+ * current phone account.
+ */
+ private void loadConfiguration() {
+ // Only attempt to load configuration from the phone account extras if the SDK is N or
+ // later. If we've got a prior SDK the default encoding and message length will suffice.
+ if (VERSION.SDK_INT < VERSION_CODES.N) {
+ return;
+ }
+
+ if (mPhoneAccountHandle == null) {
+ return;
+ }
+
+ TelecomManager telecomManager = (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
+ final PhoneAccount account = telecomManager.getPhoneAccount(mPhoneAccountHandle);
+
+ Bundle phoneAccountExtras = account.getExtras();
+ if (phoneAccountExtras == null) {
+ return;
+ }
+
+ // Get limit, if provided; otherwise default to existing value.
+ mLimit = phoneAccountExtras.getInt(PhoneAccount.EXTRA_CALL_SUBJECT_MAX_LENGTH, mLimit);
+
+ // Get charset; default to none (e.g. count characters 1:1).
+ String charsetName =
+ phoneAccountExtras.getString(PhoneAccount.EXTRA_CALL_SUBJECT_CHARACTER_ENCODING);
+
+ if (!TextUtils.isEmpty(charsetName)) {
+ try {
+ mMessageEncoding = Charset.forName(charsetName);
+ } catch (java.nio.charset.UnsupportedCharsetException uce) {
+ // Character set was invalid; log warning and fallback to none.
+ LogUtil.e("CallSubjectDialog.loadConfiguration", "invalid charset: " + charsetName);
+ mMessageEncoding = null;
+ }
+ } else {
+ // No character set specified, so count characters 1:1.
+ mMessageEncoding = null;
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/dialog/ClearFrequentsDialog.java b/java/com/android/contacts/common/dialog/ClearFrequentsDialog.java
new file mode 100644
index 000000000..e96496cda
--- /dev/null
+++ b/java/com/android/contacts/common/dialog/ClearFrequentsDialog.java
@@ -0,0 +1,88 @@
+/*
+ * 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.dialog;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.app.ProgressDialog;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import com.android.contacts.common.R;
+import com.android.dialer.util.PermissionsUtil;
+
+/** Dialog that clears the frequently contacted list after confirming with the user. */
+public class ClearFrequentsDialog extends DialogFragment {
+
+ /** Preferred way to show this dialog */
+ public static void show(FragmentManager fragmentManager) {
+ ClearFrequentsDialog dialog = new ClearFrequentsDialog();
+ dialog.show(fragmentManager, "clearFrequents");
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity().getApplicationContext();
+ final ContentResolver resolver = getActivity().getContentResolver();
+ final OnClickListener okListener =
+ new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (!PermissionsUtil.hasContactsPermissions(context)) {
+ return;
+ }
+
+ final ProgressDialog progressDialog =
+ ProgressDialog.show(
+ getContext(),
+ getString(R.string.clearFrequentsProgress_title),
+ null,
+ true,
+ true);
+
+ final AsyncTask<Void, Void, Void> task =
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ resolver.delete(
+ ContactsContract.DataUsageFeedback.DELETE_USAGE_URI, null, null);
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ progressDialog.dismiss();
+ }
+ };
+ task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+ };
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.clearFrequentsConfirmation_title)
+ .setMessage(R.string.clearFrequentsConfirmation)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, okListener)
+ .setCancelable(true)
+ .create();
+ }
+}
diff --git a/java/com/android/contacts/common/extensions/PhoneDirectoryExtender.java b/java/com/android/contacts/common/extensions/PhoneDirectoryExtender.java
new file mode 100644
index 000000000..2607ad19a
--- /dev/null
+++ b/java/com/android/contacts/common/extensions/PhoneDirectoryExtender.java
@@ -0,0 +1,28 @@
+/*
+ * 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.extensions;
+
+import android.content.Context;
+import com.android.contacts.common.list.DirectoryPartition;
+import java.util.List;
+
+/** An interface for adding extended phone directories. */
+public interface PhoneDirectoryExtender {
+ /**
+ * Return a list of extended directories to add. May return null if no directories are to be
+ * added.
+ */
+ List<DirectoryPartition> getExtendedDirectories(Context context);
+}
diff --git a/java/com/android/contacts/common/extensions/PhoneDirectoryExtenderAccessor.java b/java/com/android/contacts/common/extensions/PhoneDirectoryExtenderAccessor.java
new file mode 100644
index 000000000..84649f1ed
--- /dev/null
+++ b/java/com/android/contacts/common/extensions/PhoneDirectoryExtenderAccessor.java
@@ -0,0 +1,45 @@
+/*
+ * 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.extensions;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import com.android.dialer.common.Assert;
+
+/** Accessor for the phone directory extender singleton. */
+public final class PhoneDirectoryExtenderAccessor {
+
+ private static PhoneDirectoryExtender instance;
+
+ private PhoneDirectoryExtenderAccessor() {}
+
+ @NonNull
+ public static PhoneDirectoryExtender get(@NonNull Context context) {
+ Assert.isNotNull(context);
+ if (instance != null) {
+ return instance;
+ }
+
+ Context application = context.getApplicationContext();
+ if (application instanceof PhoneDirectoryExtenderFactory) {
+ instance = ((PhoneDirectoryExtenderFactory) application).newPhoneDirectoryExtender();
+ }
+
+ if (instance == null) {
+ instance = new PhoneDirectoryExtenderStub();
+ }
+ return instance;
+ }
+}
diff --git a/java/com/android/contacts/common/extensions/PhoneDirectoryExtenderFactory.java b/java/com/android/contacts/common/extensions/PhoneDirectoryExtenderFactory.java
new file mode 100644
index 000000000..9750ee300
--- /dev/null
+++ b/java/com/android/contacts/common/extensions/PhoneDirectoryExtenderFactory.java
@@ -0,0 +1,27 @@
+/*
+ * 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.extensions;
+
+import android.support.annotation.NonNull;
+
+/**
+ * This interface should be implemented by the Application subclass. It allows the contacts module
+ * to get references to the PhoneDirectoryExtender.
+ */
+public interface PhoneDirectoryExtenderFactory {
+
+ @NonNull
+ PhoneDirectoryExtender newPhoneDirectoryExtender();
+}
diff --git a/java/com/android/contacts/common/extensions/PhoneDirectoryExtenderStub.java b/java/com/android/contacts/common/extensions/PhoneDirectoryExtenderStub.java
new file mode 100644
index 000000000..95f971533
--- /dev/null
+++ b/java/com/android/contacts/common/extensions/PhoneDirectoryExtenderStub.java
@@ -0,0 +1,29 @@
+/*
+ * 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.extensions;
+
+import android.content.Context;
+import com.android.contacts.common.list.DirectoryPartition;
+import java.util.Collections;
+import java.util.List;
+
+/** No-op implementation for phone directory extender. */
+class PhoneDirectoryExtenderStub implements PhoneDirectoryExtender {
+
+ @Override
+ public List<DirectoryPartition> getExtendedDirectories(Context context) {
+ return Collections.emptyList();
+ }
+}
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.
+ *
+ * <p>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
new file mode 100644
index 000000000..30c03fdf3
--- /dev/null
+++ b/java/com/android/contacts/common/format/TextHighlighter.java
@@ -0,0 +1,93 @@
+/*
+ * 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.text.SpannableString;
+import android.text.style.CharacterStyle;
+import android.text.style.StyleSpan;
+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;
+
+ public TextHighlighter(int textStyle) {
+ mTextStyle = textStyle;
+ mTextStyleSpan = getStyleSpan();
+ }
+
+ /**
+ * Sets the text on the given text view, highlighting the word that matches the given prefix.
+ *
+ * @param view the view on which to set the text
+ * @param text the string to use as the text
+ * @param prefix the prefix to look for
+ */
+ public void setPrefixText(TextView view, String text, String prefix) {
+ view.setText(applyPrefixHighlight(text, prefix));
+ }
+
+ private CharacterStyle getStyleSpan() {
+ return new StyleSpan(mTextStyle);
+ }
+
+ /**
+ * Applies highlight span to the text.
+ *
+ * @param text Text sequence to be highlighted.
+ * @param start Start position of the highlight sequence.
+ * @param end End position of the highlight sequence.
+ */
+ public void applyMaskingHighlight(SpannableString text, int start, int end) {
+ /** Sets text color of the masked locations to be highlighted. */
+ text.setSpan(getStyleSpan(), start, end, 0);
+ }
+
+ /**
+ * Returns a CharSequence which highlights the given prefix if found in the given text.
+ *
+ * @param text the text to which to apply the highlight
+ * @param prefix the prefix to look for
+ */
+ public CharSequence applyPrefixHighlight(CharSequence text, String prefix) {
+ if (prefix == null) {
+ return text;
+ }
+
+ // Skip non-word characters at the beginning of prefix.
+ int prefixStart = 0;
+ while (prefixStart < prefix.length()
+ && !Character.isLetterOrDigit(prefix.charAt(prefixStart))) {
+ prefixStart++;
+ }
+ final String trimmedPrefix = prefix.substring(prefixStart);
+
+ int index = FormatUtils.indexOfWordPrefix(text, trimmedPrefix);
+ if (index != -1) {
+ final SpannableString result = new SpannableString(text);
+ result.setSpan(mTextStyleSpan, index, index + trimmedPrefix.length(), 0 /* flags */);
+ return result;
+ } else {
+ return text;
+ }
+ }
+}
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 <p></p> bits to it.
+ Assert.assertEquals("", actualHtmlText);
+ } else {
+ Assert.assertEquals("<p dir=ltr>" + expectedHtmlText + "</p>\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
new file mode 100644
index 000000000..7e1839c1e
--- /dev/null
+++ b/java/com/android/contacts/common/lettertiles/LetterTileDrawable.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2013 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.lettertiles;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import com.android.contacts.common.R;
+import com.android.dialer.common.Assert;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A drawable that encapsulates all the functionality needed to display a letter tile to represent a
+ * contact image.
+ */
+public class LetterTileDrawable extends Drawable {
+
+ /**
+ * ContactType indicates the avatar type of the contact. For a person or for the default when no
+ * name is provided, it is {@link #TYPE_DEFAULT}, otherwise, for a business it is {@link
+ * #TYPE_BUSINESS}, and voicemail contacts should use {@link #TYPE_VOICEMAIL}.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({TYPE_PERSON, TYPE_BUSINESS, TYPE_VOICEMAIL})
+ public @interface ContactType {}
+
+ /** Contact type constants */
+ public static final int TYPE_PERSON = 1;
+ public static final int TYPE_BUSINESS = 2;
+ public static final int TYPE_VOICEMAIL = 3;
+ @ContactType public static final int TYPE_DEFAULT = TYPE_PERSON;
+
+ /**
+ * Shape indicates the letter tile shape. It can be either a {@link #SHAPE_CIRCLE}, otherwise, it
+ * is a {@link #SHAPE_RECTANGLE}.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SHAPE_CIRCLE, SHAPE_RECTANGLE})
+ public @interface Shape {}
+
+ /** Shape constants */
+ public static final int SHAPE_CIRCLE = 1;
+
+ public static final int SHAPE_RECTANGLE = 2;
+
+ /** 54% opacity */
+ private static final int ALPHA = 138;
+
+ /** Reusable components to avoid new allocations */
+ private static final Paint sPaint = new Paint();
+
+ private static final Rect sRect = new Rect();
+ private static final char[] sFirstChar = new char[1];
+ /** Letter tile */
+ private static TypedArray sColors;
+
+ private static int sDefaultColor;
+ private static int sTileFontColor;
+ private static float sLetterToTileRatio;
+ private static Bitmap sDefaultPersonAvatar;
+ private static Bitmap sDefaultBusinessAvatar;
+ private static Bitmap sDefaultVoicemailAvatar;
+ private static final String TAG = LetterTileDrawable.class.getSimpleName();
+ private final Paint mPaint;
+ private int mContactType = TYPE_DEFAULT;
+ private float mScale = 1.0f;
+ private float mOffset = 0.0f;
+ private boolean mIsCircle = false;
+
+ private int mColor;
+ private Character mLetter = null;
+
+ private boolean mAvatarWasVoicemailOrBusiness = false;
+ private String mDisplayName;
+
+ public LetterTileDrawable(final Resources res) {
+ if (sColors == null) {
+ sColors = res.obtainTypedArray(R.array.letter_tile_colors);
+ sDefaultColor = res.getColor(R.color.letter_tile_default_color);
+ sTileFontColor = res.getColor(R.color.letter_tile_font_color);
+ sLetterToTileRatio = res.getFraction(R.dimen.letter_to_tile_ratio, 1, 1);
+ sDefaultPersonAvatar =
+ BitmapFactory.decodeResource(
+ res, R.drawable.product_logo_avatar_anonymous_white_color_120);
+ sDefaultBusinessAvatar =
+ BitmapFactory.decodeResource(res, R.drawable.ic_business_white_120dp);
+ sDefaultVoicemailAvatar = BitmapFactory.decodeResource(res, R.drawable.ic_voicemail_avatar);
+ sPaint.setTypeface(
+ Typeface.create(res.getString(R.string.letter_tile_letter_font_family), Typeface.NORMAL));
+ sPaint.setTextAlign(Align.CENTER);
+ sPaint.setAntiAlias(true);
+ }
+ mPaint = new Paint();
+ mPaint.setFilterBitmap(true);
+ mPaint.setDither(true);
+ mColor = sDefaultColor;
+ }
+
+ private static Bitmap getBitmapForContactType(int contactType) {
+ switch (contactType) {
+ case TYPE_BUSINESS:
+ return sDefaultBusinessAvatar;
+ case TYPE_VOICEMAIL:
+ return sDefaultVoicemailAvatar;
+ case TYPE_PERSON:
+ default:
+ return sDefaultPersonAvatar;
+ }
+ }
+
+ private static boolean isEnglishLetter(final char c) {
+ return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ final Rect bounds = getBounds();
+ if (!isVisible() || bounds.isEmpty()) {
+ return;
+ }
+ // Draw letter tile.
+ drawLetterTile(canvas);
+ }
+
+ /**
+ * Draw the bitmap onto the canvas at the current bounds taking into account the current scale.
+ */
+ private void drawBitmap(
+ final Bitmap bitmap, final int width, final int height, final Canvas canvas) {
+ // The bitmap should be drawn in the middle of the canvas without changing its width to
+ // height ratio.
+ final Rect destRect = copyBounds();
+
+ // Crop the destination bounds into a square, scaled and offset as appropriate
+ final int halfLength = (int) (mScale * Math.min(destRect.width(), destRect.height()) / 2);
+
+ destRect.set(
+ destRect.centerX() - halfLength,
+ (int) (destRect.centerY() - halfLength + mOffset * destRect.height()),
+ destRect.centerX() + halfLength,
+ (int) (destRect.centerY() + halfLength + mOffset * destRect.height()));
+
+ // Source rectangle remains the entire bounds of the source bitmap.
+ sRect.set(0, 0, width, height);
+
+ sPaint.setTextAlign(Align.CENTER);
+ sPaint.setAntiAlias(true);
+ sPaint.setAlpha(ALPHA);
+
+ canvas.drawBitmap(bitmap, sRect, destRect, sPaint);
+ }
+
+ private void drawLetterTile(final Canvas canvas) {
+ // Draw background color.
+ sPaint.setColor(mColor);
+
+ sPaint.setAlpha(mPaint.getAlpha());
+ final Rect bounds = getBounds();
+ final int minDimension = Math.min(bounds.width(), bounds.height());
+
+ if (mIsCircle) {
+ canvas.drawCircle(bounds.centerX(), bounds.centerY(), minDimension / 2, sPaint);
+ } else {
+ canvas.drawRect(bounds, sPaint);
+ }
+
+ // Draw letter/digit only if the first character is an english letter or there's a override
+
+ if (mLetter != null) {
+ // Draw letter or digit.
+ sFirstChar[0] = mLetter;
+
+ // Scale text by canvas bounds and user selected scaling factor
+ sPaint.setTextSize(mScale * sLetterToTileRatio * minDimension);
+ sPaint.getTextBounds(sFirstChar, 0, 1, sRect);
+ sPaint.setTypeface(Typeface.create("sans-serif", Typeface.NORMAL));
+ sPaint.setColor(sTileFontColor);
+ sPaint.setAlpha(ALPHA);
+
+ // Draw the letter in the canvas, vertically shifted up or down by the user-defined
+ // offset
+ canvas.drawText(
+ sFirstChar,
+ 0,
+ 1,
+ bounds.centerX(),
+ bounds.centerY() + mOffset * bounds.height() - sRect.exactCenterY(),
+ sPaint);
+ } else {
+ // Draw the default image if there is no letter/digit to be drawn
+ final Bitmap bitmap = getBitmapForContactType(mContactType);
+ drawBitmap(bitmap, bitmap.getWidth(), bitmap.getHeight(), canvas);
+ }
+ }
+
+ public int getColor() {
+ return mColor;
+ }
+
+ public LetterTileDrawable setColor(int color) {
+ mColor = color;
+ return this;
+ }
+
+ /** Returns a deterministic color based on the provided contact identifier string. */
+ private int pickColor(final String identifier) {
+ if (TextUtils.isEmpty(identifier) || mContactType == TYPE_VOICEMAIL) {
+ return sDefaultColor;
+ }
+ // String.hashCode() implementation is not supposed to change across java versions, so
+ // this should guarantee the same email address always maps to the same color.
+ // The email should already have been normalized by the ContactRequest.
+ final int color = Math.abs(identifier.hashCode()) % sColors.length();
+ return sColors.getColor(color, sDefaultColor);
+ }
+
+ @Override
+ public void setAlpha(final int alpha) {
+ mPaint.setAlpha(alpha);
+ }
+
+ @Override
+ public void setColorFilter(final ColorFilter cf) {
+ mPaint.setColorFilter(cf);
+ }
+
+ @Override
+ public int getOpacity() {
+ return android.graphics.PixelFormat.OPAQUE;
+ }
+
+ @Override
+ public void getOutline(Outline outline) {
+ if (mIsCircle) {
+ outline.setOval(getBounds());
+ } else {
+ outline.setRect(getBounds());
+ }
+
+ outline.setAlpha(1);
+ }
+
+ /**
+ * Scale the drawn letter tile to a ratio of its default size
+ *
+ * @param scale The ratio the letter tile should be scaled to as a percentage of its default size,
+ * from a scale of 0 to 2.0f. The default is 1.0f.
+ */
+ public LetterTileDrawable setScale(float scale) {
+ mScale = scale;
+ return this;
+ }
+
+ /**
+ * Assigns the vertical offset of the position of the letter tile to the ContactDrawable
+ *
+ * @param offset The provided offset must be within the range of -0.5f to 0.5f. If set to -0.5f,
+ * the letter will be shifted upwards by 0.5 times the height of the canvas it is being drawn
+ * on, which means it will be drawn with the center of the letter starting at the top edge of
+ * the canvas. If set to 0.5f, the letter will be shifted downwards by 0.5 times the height of
+ * the canvas it is being drawn on, which means it will be drawn with the center of the letter
+ * starting at the bottom edge of the canvas. The default is 0.0f.
+ */
+ public LetterTileDrawable setOffset(float offset) {
+ Assert.checkArgument(offset >= -0.5f && offset <= 0.5f);
+ mOffset = offset;
+ return this;
+ }
+
+ public LetterTileDrawable setLetter(Character letter) {
+ mLetter = letter;
+ return this;
+ }
+
+ public Character getLetter() {
+ return this.mLetter;
+ }
+
+ private LetterTileDrawable setLetterAndColorFromContactDetails(
+ final String displayName, final String identifier) {
+ if (displayName != null && displayName.length() > 0 && isEnglishLetter(displayName.charAt(0))) {
+ mLetter = Character.toUpperCase(displayName.charAt(0));
+ } else {
+ mLetter = null;
+ }
+ mColor = pickColor(identifier);
+ return this;
+ }
+
+ public LetterTileDrawable setContactType(@ContactType int contactType) {
+ mContactType = contactType;
+ return this;
+ }
+
+ @ContactType
+ public int getContactType() {
+ return this.mContactType;
+ }
+
+ public LetterTileDrawable setIsCircular(boolean isCircle) {
+ mIsCircle = isCircle;
+ return this;
+ }
+
+ /**
+ * Creates a canonical letter tile for use across dialer fragments.
+ *
+ * @param displayName The display name to produce the letter in the tile. Null values or numbers
+ * yield no letter.
+ * @param identifierForTileColor The string used to produce the tile color.
+ * @param shape The shape of the tile.
+ * @param contactType The type of contact, e.g. TYPE_VOICEMAIL.
+ * @return this
+ */
+ public LetterTileDrawable setCanonicalDialerLetterTileDetails(
+ @Nullable final String displayName,
+ @Nullable final String identifierForTileColor,
+ @Shape final int shape,
+ final int contactType) {
+ setContactType(contactType);
+ /**
+ * During hangup, we lose the call state for special types of contacts, like voicemail. To help
+ * callers avoid extraneous LetterTileDrawable allocations, we keep track of the special case
+ * until we encounter a new display name.
+ */
+ if (contactType == TYPE_VOICEMAIL || contactType == TYPE_BUSINESS) {
+ this.mAvatarWasVoicemailOrBusiness = true;
+ } else if (displayName != null && !displayName.equals(mDisplayName)) {
+ this.mAvatarWasVoicemailOrBusiness = false;
+ }
+ this.mDisplayName = displayName;
+ if (shape == SHAPE_CIRCLE) {
+ this.setIsCircular(true);
+ } else {
+ this.setIsCircular(false);
+ }
+
+ /**
+ * To preserve style, we don't use contactType to set the tile icon. In the future, when all
+ * callers surface this detail, we can use this to better style the tile icon.
+ */
+ if (mAvatarWasVoicemailOrBusiness) {
+ this.setLetterAndColorFromContactDetails(null, displayName);
+ return this;
+ } else {
+ if (identifierForTileColor != null) {
+ this.setLetterAndColorFromContactDetails(displayName, identifierForTileColor);
+ return this;
+ } else {
+ this.setLetterAndColorFromContactDetails(displayName, displayName);
+ return this;
+ }
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/list/AutoScrollListView.java b/java/com/android/contacts/common/list/AutoScrollListView.java
new file mode 100644
index 000000000..601abf528
--- /dev/null
+++ b/java/com/android/contacts/common/list/AutoScrollListView.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Context;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.widget.ListView;
+
+/**
+ * A ListView that can be asked to scroll (smoothly or otherwise) to a specific position. This class
+ * takes advantage of similar functionality that exists in {@link ListView} and enhances it.
+ */
+public class AutoScrollListView extends ListView {
+
+ /** Position the element at about 1/3 of the list height */
+ private static final float PREFERRED_SELECTION_OFFSET_FROM_TOP = 0.33f;
+
+ private int mRequestedScrollPosition = -1;
+ private boolean mSmoothScrollRequested;
+
+ public AutoScrollListView(Context context) {
+ super(context);
+ }
+
+ public AutoScrollListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AutoScrollListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * Brings the specified position to view by optionally performing a jump-scroll maneuver: first it
+ * jumps to some position near the one requested and then does a smooth scroll to the requested
+ * position. This creates an impression of full smooth scrolling without actually traversing the
+ * entire list. If smooth scrolling is not requested, instantly positions the requested item at a
+ * preferred offset.
+ */
+ public void requestPositionToScreen(int position, boolean smoothScroll) {
+ mRequestedScrollPosition = position;
+ mSmoothScrollRequested = smoothScroll;
+ requestLayout();
+ }
+
+ @Override
+ protected void layoutChildren() {
+ super.layoutChildren();
+ if (mRequestedScrollPosition == -1) {
+ return;
+ }
+
+ final int position = mRequestedScrollPosition;
+ mRequestedScrollPosition = -1;
+
+ int firstPosition = getFirstVisiblePosition() + 1;
+ int lastPosition = getLastVisiblePosition();
+ if (position >= firstPosition && position <= lastPosition) {
+ return; // Already on screen
+ }
+
+ final int offset = (int) (getHeight() * PREFERRED_SELECTION_OFFSET_FROM_TOP);
+ if (!mSmoothScrollRequested) {
+ setSelectionFromTop(position, offset);
+
+ // Since we have changed the scrolling position, we need to redo child layout
+ // Calling "requestLayout" in the middle of a layout pass has no effect,
+ // so we call layoutChildren explicitly
+ super.layoutChildren();
+
+ } else {
+ // We will first position the list a couple of screens before or after
+ // the new selection and then scroll smoothly to it.
+ int twoScreens = (lastPosition - firstPosition) * 2;
+ int preliminaryPosition;
+ if (position < firstPosition) {
+ preliminaryPosition = position + twoScreens;
+ if (preliminaryPosition >= getCount()) {
+ preliminaryPosition = getCount() - 1;
+ }
+ if (preliminaryPosition < firstPosition) {
+ setSelection(preliminaryPosition);
+ super.layoutChildren();
+ }
+ } else {
+ preliminaryPosition = position - twoScreens;
+ if (preliminaryPosition < 0) {
+ preliminaryPosition = 0;
+ }
+ if (preliminaryPosition > lastPosition) {
+ setSelection(preliminaryPosition);
+ super.layoutChildren();
+ }
+ }
+
+ smoothScrollToPositionFromTop(position, offset);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+
+ // Workaround for b/31160338 and b/32778636.
+ if (android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.N
+ || android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) {
+ layoutChildren();
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/list/ContactEntry.java b/java/com/android/contacts/common/list/ContactEntry.java
new file mode 100644
index 000000000..e33165e45
--- /dev/null
+++ b/java/com/android/contacts/common/list/ContactEntry.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013 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.list;
+
+import android.net.Uri;
+import android.provider.ContactsContract.PinnedPositions;
+import android.text.TextUtils;
+import com.android.contacts.common.preference.ContactsPreferences;
+
+/** Class to hold contact information */
+public class ContactEntry {
+
+ public static final ContactEntry BLANK_ENTRY = new ContactEntry();
+ private static final int UNSET_DISPLAY_ORDER_PREFERENCE = -1;
+ /** Primary name for a Contact */
+ public String namePrimary;
+ /** Alternative name for a Contact, e.g. last name first */
+ public String nameAlternative;
+ /**
+ * The user's preference on name display order, last name first or first time first. {@see
+ * ContactsPreferences}
+ */
+ public int nameDisplayOrder = UNSET_DISPLAY_ORDER_PREFERENCE;
+
+ public String phoneLabel;
+ public String phoneNumber;
+ public Uri photoUri;
+ public Uri lookupUri;
+ public String lookupKey;
+ public long id;
+ public int pinned = PinnedPositions.UNPINNED;
+ public boolean isFavorite = false;
+ public boolean isDefaultNumber = false;
+
+ public String getPreferredDisplayName() {
+ if (nameDisplayOrder == UNSET_DISPLAY_ORDER_PREFERENCE
+ || nameDisplayOrder == ContactsPreferences.DISPLAY_ORDER_PRIMARY
+ || TextUtils.isEmpty(nameAlternative)) {
+ return namePrimary;
+ }
+ return nameAlternative;
+ }
+}
diff --git a/java/com/android/contacts/common/list/ContactEntryListAdapter.java b/java/com/android/contacts/common/list/ContactEntryListAdapter.java
new file mode 100644
index 000000000..18bbae382
--- /dev/null
+++ b/java/com/android/contacts/common/list/ContactEntryListAdapter.java
@@ -0,0 +1,742 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+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;
+import android.widget.QuickContactBadge;
+import android.widget.SectionIndexer;
+import android.widget.TextView;
+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;
+import com.android.contacts.common.compat.DirectoryCompat;
+import com.android.contacts.common.util.SearchUtil;
+import com.android.dialer.compat.CompatUtils;
+import java.util.HashSet;
+
+/**
+ * Common base class for various contact-related lists, e.g. contact list, phone number list etc.
+ */
+public abstract class ContactEntryListAdapter extends IndexerListAdapter {
+
+ /**
+ * Indicates whether the {@link Directory#LOCAL_INVISIBLE} directory should be included in the
+ * search.
+ */
+ public static final boolean LOCAL_INVISIBLE_DIRECTORY_ENABLED = false;
+
+ private static final String TAG = "ContactEntryListAdapter";
+ private int mDisplayOrder;
+ private int mSortOrder;
+
+ private boolean mDisplayPhotos;
+ private boolean mCircularPhotos = true;
+ private boolean mQuickContactEnabled;
+ private boolean mAdjustSelectionBoundsEnabled;
+
+ /** The root view of the fragment that this adapter is associated with. */
+ private View mFragmentRootView;
+
+ private ContactPhotoManager mPhotoLoader;
+
+ private String mQueryString;
+ private String mUpperCaseQueryString;
+ private boolean mSearchMode;
+ private int mDirectorySearchMode;
+ private int mDirectoryResultLimit = Integer.MAX_VALUE;
+
+ private boolean mEmptyListEnabled = true;
+
+ private boolean mSelectionVisible;
+
+ private ContactListFilter mFilter;
+ private boolean mDarkTheme = false;
+
+ /** Resource used to provide header-text for default filter. */
+ private CharSequence mDefaultFilterHeaderText;
+
+ public ContactEntryListAdapter(Context context) {
+ super(context);
+ setDefaultFilterHeaderText(R.string.local_search_label);
+ addPartitions();
+ }
+
+ /**
+ * @param fragmentRootView Root view of the fragment. This is used to restrict the scope of image
+ * loading requests that get cancelled on cursor changes.
+ */
+ protected void setFragmentRootView(View fragmentRootView) {
+ mFragmentRootView = fragmentRootView;
+ }
+
+ protected void setDefaultFilterHeaderText(int resourceId) {
+ mDefaultFilterHeaderText = getContext().getResources().getText(resourceId);
+ }
+
+ @Override
+ protected ContactListItemView newView(
+ Context context, int partition, Cursor cursor, int position, ViewGroup parent) {
+ final ContactListItemView view = new ContactListItemView(context, null);
+ view.setIsSectionHeaderEnabled(isSectionHeaderDisplayEnabled());
+ view.setAdjustSelectionBoundsEnabled(isAdjustSelectionBoundsEnabled());
+ return view;
+ }
+
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ final ContactListItemView view = (ContactListItemView) itemView;
+ view.setIsSectionHeaderEnabled(isSectionHeaderDisplayEnabled());
+ bindWorkProfileIcon(view, partition);
+ }
+
+ @Override
+ protected View createPinnedSectionHeaderView(Context context, ViewGroup parent) {
+ return new ContactListPinnedHeaderView(context, null, parent);
+ }
+
+ @Override
+ protected void setPinnedSectionTitle(View pinnedHeaderView, String title) {
+ ((ContactListPinnedHeaderView) pinnedHeaderView).setSectionHeaderTitle(title);
+ }
+
+ protected void addPartitions() {
+ addPartition(createDefaultDirectoryPartition());
+ }
+
+ protected DirectoryPartition createDefaultDirectoryPartition() {
+ DirectoryPartition partition = new DirectoryPartition(true, true);
+ partition.setDirectoryId(Directory.DEFAULT);
+ partition.setDirectoryType(getContext().getString(R.string.contactsList));
+ partition.setPriorityDirectory(true);
+ partition.setPhotoSupported(true);
+ partition.setLabel(mDefaultFilterHeaderText.toString());
+ return partition;
+ }
+
+ /**
+ * Remove all directories after the default directory. This is typically used when contacts list
+ * screens are asked to exit the search mode and thus need to remove all remote directory results
+ * for the search.
+ *
+ * <p>This code assumes that the default directory and directories before that should not be
+ * deleted (e.g. Join screen has "suggested contacts" directory before the default director, and
+ * we should not remove the directory).
+ */
+ public void removeDirectoriesAfterDefault() {
+ final int partitionCount = getPartitionCount();
+ for (int i = partitionCount - 1; i >= 0; i--) {
+ final Partition partition = getPartition(i);
+ if ((partition instanceof DirectoryPartition)
+ && ((DirectoryPartition) partition).getDirectoryId() == Directory.DEFAULT) {
+ break;
+ } else {
+ removePartition(i);
+ }
+ }
+ }
+
+ protected int getPartitionByDirectoryId(long id) {
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ if (((DirectoryPartition) partition).getDirectoryId() == id) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ protected DirectoryPartition getDirectoryById(long id) {
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ final DirectoryPartition directoryPartition = (DirectoryPartition) partition;
+ if (directoryPartition.getDirectoryId() == id) {
+ return directoryPartition;
+ }
+ }
+ }
+ return null;
+ }
+
+ public abstract void configureLoader(CursorLoader loader, long directoryId);
+
+ /** Marks all partitions as "loading" */
+ public void onDataReload() {
+ boolean notify = false;
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ DirectoryPartition directoryPartition = (DirectoryPartition) partition;
+ if (!directoryPartition.isLoading()) {
+ notify = true;
+ }
+ directoryPartition.setStatus(DirectoryPartition.STATUS_NOT_LOADED);
+ }
+ }
+ if (notify) {
+ notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void clearPartitions() {
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ DirectoryPartition directoryPartition = (DirectoryPartition) partition;
+ directoryPartition.setStatus(DirectoryPartition.STATUS_NOT_LOADED);
+ }
+ }
+ super.clearPartitions();
+ }
+
+ public boolean isSearchMode() {
+ return mSearchMode;
+ }
+
+ public void setSearchMode(boolean flag) {
+ mSearchMode = flag;
+ }
+
+ public String getQueryString() {
+ return mQueryString;
+ }
+
+ public void setQueryString(String queryString) {
+ mQueryString = queryString;
+ if (TextUtils.isEmpty(queryString)) {
+ mUpperCaseQueryString = null;
+ } else {
+ mUpperCaseQueryString = SearchUtil.cleanStartAndEndOfSearchQuery(queryString.toUpperCase());
+ }
+ }
+
+ public String getUpperCaseQueryString() {
+ return mUpperCaseQueryString;
+ }
+
+ public int getDirectorySearchMode() {
+ return mDirectorySearchMode;
+ }
+
+ public void setDirectorySearchMode(int mode) {
+ mDirectorySearchMode = mode;
+ }
+
+ public int getDirectoryResultLimit() {
+ return mDirectoryResultLimit;
+ }
+
+ public void setDirectoryResultLimit(int limit) {
+ this.mDirectoryResultLimit = limit;
+ }
+
+ public int getDirectoryResultLimit(DirectoryPartition directoryPartition) {
+ final int limit = directoryPartition.getResultLimit();
+ return limit == DirectoryPartition.RESULT_LIMIT_DEFAULT ? mDirectoryResultLimit : limit;
+ }
+
+ public int getContactNameDisplayOrder() {
+ return mDisplayOrder;
+ }
+
+ public void setContactNameDisplayOrder(int displayOrder) {
+ mDisplayOrder = displayOrder;
+ }
+
+ public int getSortOrder() {
+ return mSortOrder;
+ }
+
+ public void setSortOrder(int sortOrder) {
+ mSortOrder = sortOrder;
+ }
+
+ protected ContactPhotoManager getPhotoLoader() {
+ return mPhotoLoader;
+ }
+
+ public void setPhotoLoader(ContactPhotoManager photoLoader) {
+ mPhotoLoader = photoLoader;
+ }
+
+ public boolean getDisplayPhotos() {
+ return mDisplayPhotos;
+ }
+
+ public void setDisplayPhotos(boolean displayPhotos) {
+ mDisplayPhotos = displayPhotos;
+ }
+
+ public boolean getCircularPhotos() {
+ return mCircularPhotos;
+ }
+
+ public boolean isSelectionVisible() {
+ return mSelectionVisible;
+ }
+
+ public void setSelectionVisible(boolean flag) {
+ this.mSelectionVisible = flag;
+ }
+
+ public boolean isQuickContactEnabled() {
+ return mQuickContactEnabled;
+ }
+
+ public void setQuickContactEnabled(boolean quickContactEnabled) {
+ mQuickContactEnabled = quickContactEnabled;
+ }
+
+ public boolean isAdjustSelectionBoundsEnabled() {
+ return mAdjustSelectionBoundsEnabled;
+ }
+
+ public void setAdjustSelectionBoundsEnabled(boolean enabled) {
+ mAdjustSelectionBoundsEnabled = enabled;
+ }
+
+ public void setProfileExists(boolean exists) {
+ // Stick the "ME" header for the profile
+ if (exists) {
+ setSectionHeader(R.string.user_profile_contacts_list_header, /* # of ME */ 1);
+ }
+ }
+
+ private void setSectionHeader(int resId, int numberOfItems) {
+ SectionIndexer indexer = getIndexer();
+ if (indexer != null) {
+ ((ContactsSectionIndexer) indexer)
+ .setProfileAndFavoritesHeader(getContext().getString(resId), numberOfItems);
+ }
+ }
+
+ public void setDarkTheme(boolean value) {
+ mDarkTheme = value;
+ }
+
+ /** Updates partitions according to the directory meta-data contained in the supplied cursor. */
+ public void changeDirectories(Cursor cursor) {
+ if (cursor.getCount() == 0) {
+ // Directory table must have at least local directory, without which this adapter will
+ // enter very weird state.
+ Log.e(
+ TAG,
+ "Directory search loader returned an empty cursor, which implies we have "
+ + "no directory entries.",
+ new RuntimeException());
+ return;
+ }
+ HashSet<Long> directoryIds = new HashSet<Long>();
+
+ int idColumnIndex = cursor.getColumnIndex(Directory._ID);
+ int directoryTypeColumnIndex = cursor.getColumnIndex(DirectoryListLoader.DIRECTORY_TYPE);
+ int displayNameColumnIndex = cursor.getColumnIndex(Directory.DISPLAY_NAME);
+ int photoSupportColumnIndex = cursor.getColumnIndex(Directory.PHOTO_SUPPORT);
+
+ // TODO preserve the order of partition to match those of the cursor
+ // Phase I: add new directories
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ long id = cursor.getLong(idColumnIndex);
+ directoryIds.add(id);
+ if (getPartitionByDirectoryId(id) == -1) {
+ DirectoryPartition partition = new DirectoryPartition(false, true);
+ partition.setDirectoryId(id);
+ if (DirectoryCompat.isRemoteDirectoryId(id)) {
+ if (DirectoryCompat.isEnterpriseDirectoryId(id)) {
+ partition.setLabel(mContext.getString(R.string.directory_search_label_work));
+ } else {
+ partition.setLabel(mContext.getString(R.string.directory_search_label));
+ }
+ } else {
+ if (DirectoryCompat.isEnterpriseDirectoryId(id)) {
+ partition.setLabel(mContext.getString(R.string.list_filter_phones_work));
+ } else {
+ partition.setLabel(mDefaultFilterHeaderText.toString());
+ }
+ }
+ partition.setDirectoryType(cursor.getString(directoryTypeColumnIndex));
+ partition.setDisplayName(cursor.getString(displayNameColumnIndex));
+ int photoSupport = cursor.getInt(photoSupportColumnIndex);
+ partition.setPhotoSupported(
+ photoSupport == Directory.PHOTO_SUPPORT_THUMBNAIL_ONLY
+ || photoSupport == Directory.PHOTO_SUPPORT_FULL);
+ addPartition(partition);
+ }
+ }
+
+ // Phase II: remove deleted directories
+ int count = getPartitionCount();
+ for (int i = count; --i >= 0; ) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ long id = ((DirectoryPartition) partition).getDirectoryId();
+ if (!directoryIds.contains(id)) {
+ removePartition(i);
+ }
+ }
+ }
+
+ invalidate();
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void changeCursor(int partitionIndex, Cursor cursor) {
+ if (partitionIndex >= getPartitionCount()) {
+ // There is no partition for this data
+ return;
+ }
+
+ Partition partition = getPartition(partitionIndex);
+ if (partition instanceof DirectoryPartition) {
+ ((DirectoryPartition) partition).setStatus(DirectoryPartition.STATUS_LOADED);
+ }
+
+ if (mDisplayPhotos && mPhotoLoader != null && isPhotoSupported(partitionIndex)) {
+ mPhotoLoader.refreshCache();
+ }
+
+ super.changeCursor(partitionIndex, cursor);
+
+ if (isSectionHeaderDisplayEnabled() && partitionIndex == getIndexedPartition()) {
+ updateIndexer(cursor);
+ }
+
+ // When the cursor changes, cancel any pending asynchronous photo loads.
+ mPhotoLoader.cancelPendingRequests(mFragmentRootView);
+ }
+
+ public void changeCursor(Cursor cursor) {
+ changeCursor(0, cursor);
+ }
+
+ /** Updates the indexer, which is used to produce section headers. */
+ private void updateIndexer(Cursor cursor) {
+ if (cursor == null || cursor.isClosed()) {
+ setIndexer(null);
+ return;
+ }
+
+ Bundle bundle = cursor.getExtras();
+ if (bundle.containsKey(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES)
+ && bundle.containsKey(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS)) {
+ String[] sections = bundle.getStringArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
+ int[] counts = bundle.getIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
+
+ if (getExtraStartingSection()) {
+ // Insert an additional unnamed section at the top of the list.
+ String[] allSections = new String[sections.length + 1];
+ int[] allCounts = new int[counts.length + 1];
+ for (int i = 0; i < sections.length; i++) {
+ allSections[i + 1] = sections[i];
+ allCounts[i + 1] = counts[i];
+ }
+ allCounts[0] = 1;
+ allSections[0] = "";
+ setIndexer(new ContactsSectionIndexer(allSections, allCounts));
+ } else {
+ setIndexer(new ContactsSectionIndexer(sections, counts));
+ }
+ } else {
+ setIndexer(null);
+ }
+ }
+
+ protected boolean getExtraStartingSection() {
+ return false;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ // We need a separate view type for each item type, plus another one for
+ // each type with header, plus one for "other".
+ return getItemViewTypeCount() * 2 + 1;
+ }
+
+ @Override
+ public int getItemViewType(int partitionIndex, int position) {
+ int type = super.getItemViewType(partitionIndex, position);
+ if (!isUserProfile(position)
+ && isSectionHeaderDisplayEnabled()
+ && partitionIndex == getIndexedPartition()) {
+ Placement placement = getItemPlacementInSection(position);
+ return placement.firstInSection ? type : getItemViewTypeCount() + type;
+ } else {
+ return type;
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ // TODO
+ // if (contactsListActivity.mProviderStatus != ProviderStatus.STATUS_NORMAL) {
+ // return true;
+ // }
+
+ if (!mEmptyListEnabled) {
+ return false;
+ } else if (isSearchMode()) {
+ return TextUtils.isEmpty(getQueryString());
+ } else {
+ return super.isEmpty();
+ }
+ }
+
+ public boolean isLoading() {
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition && ((DirectoryPartition) partition).isLoading()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Changes visibility parameters for the default directory partition. */
+ public void configureDefaultPartition(boolean showIfEmpty, boolean hasHeader) {
+ int defaultPartitionIndex = -1;
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition
+ && ((DirectoryPartition) partition).getDirectoryId() == Directory.DEFAULT) {
+ defaultPartitionIndex = i;
+ break;
+ }
+ }
+ if (defaultPartitionIndex != -1) {
+ setShowIfEmpty(defaultPartitionIndex, showIfEmpty);
+ setHasHeader(defaultPartitionIndex, hasHeader);
+ }
+ }
+
+ @Override
+ protected View newHeaderView(Context context, int partition, Cursor cursor, ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ View view = inflater.inflate(R.layout.directory_header, parent, false);
+ if (!getPinnedPartitionHeadersEnabled()) {
+ // If the headers are unpinned, there is no need for their background
+ // color to be non-transparent. Setting this transparent reduces maintenance for
+ // non-pinned headers. We don't need to bother synchronizing the activity's
+ // background color with the header background color.
+ view.setBackground(null);
+ }
+ return view;
+ }
+
+ protected void bindWorkProfileIcon(final ContactListItemView view, int partitionId) {
+ final Partition partition = getPartition(partitionId);
+ if (partition instanceof DirectoryPartition) {
+ final DirectoryPartition directoryPartition = (DirectoryPartition) partition;
+ final long directoryId = directoryPartition.getDirectoryId();
+ final long userType = ContactsUtils.determineUserType(directoryId, null);
+ view.setWorkProfileIconEnabled(userType == ContactsUtils.USER_TYPE_WORK);
+ }
+ }
+
+ @Override
+ protected void bindHeaderView(View view, int partitionIndex, Cursor cursor) {
+ Partition partition = getPartition(partitionIndex);
+ if (!(partition instanceof DirectoryPartition)) {
+ return;
+ }
+
+ DirectoryPartition directoryPartition = (DirectoryPartition) partition;
+ long directoryId = directoryPartition.getDirectoryId();
+ TextView labelTextView = (TextView) view.findViewById(R.id.label);
+ TextView displayNameTextView = (TextView) view.findViewById(R.id.display_name);
+ labelTextView.setText(directoryPartition.getLabel());
+ if (!DirectoryCompat.isRemoteDirectoryId(directoryId)) {
+ displayNameTextView.setText(null);
+ } else {
+ String directoryName = directoryPartition.getDisplayName();
+ String displayName =
+ !TextUtils.isEmpty(directoryName) ? directoryName : directoryPartition.getDirectoryType();
+ displayNameTextView.setText(displayName);
+ }
+
+ final Resources res = getContext().getResources();
+ final int headerPaddingTop =
+ partitionIndex == 1 && getPartition(0).isEmpty()
+ ? 0
+ : res.getDimensionPixelOffset(R.dimen.directory_header_extra_top_padding);
+ // There should be no extra padding at the top of the first directory header
+ view.setPaddingRelative(
+ view.getPaddingStart(), headerPaddingTop, view.getPaddingEnd(), view.getPaddingBottom());
+ }
+
+ /** Checks whether the contact entry at the given position represents the user's profile. */
+ protected boolean isUserProfile(int position) {
+ // The profile only ever appears in the first position if it is present. So if the position
+ // is anything beyond 0, it can't be the profile.
+ boolean isUserProfile = false;
+ if (position == 0) {
+ int partition = getPartitionForPosition(position);
+ if (partition >= 0) {
+ // Save the old cursor position - the call to getItem() may modify the cursor
+ // position.
+ int offset = getCursor(partition).getPosition();
+ Cursor cursor = (Cursor) getItem(position);
+ if (cursor != null) {
+ int profileColumnIndex = cursor.getColumnIndex(Contacts.IS_USER_PROFILE);
+ if (profileColumnIndex != -1) {
+ isUserProfile = cursor.getInt(profileColumnIndex) == 1;
+ }
+ // Restore the old cursor position.
+ cursor.moveToPosition(offset);
+ }
+ }
+ }
+ return isUserProfile;
+ }
+
+ public boolean isPhotoSupported(int partitionIndex) {
+ Partition partition = getPartition(partitionIndex);
+ if (partition instanceof DirectoryPartition) {
+ return ((DirectoryPartition) partition).isPhotoSupported();
+ }
+ return true;
+ }
+
+ /** Returns the currently selected filter. */
+ public ContactListFilter getFilter() {
+ return mFilter;
+ }
+
+ public void setFilter(ContactListFilter filter) {
+ mFilter = filter;
+ }
+
+ // TODO: move sharable logic (bindXX() methods) to here with extra arguments
+
+ /**
+ * Loads the photo for the quick contact view and assigns the contact uri.
+ *
+ * @param photoIdColumn Index of the photo id column
+ * @param photoUriColumn Index of the photo uri column. Optional: Can be -1
+ * @param contactIdColumn Index of the contact id column
+ * @param lookUpKeyColumn Index of the lookup key column
+ * @param displayNameColumn Index of the display name column
+ */
+ protected void bindQuickContact(
+ final ContactListItemView view,
+ int partitionIndex,
+ Cursor cursor,
+ int photoIdColumn,
+ int photoUriColumn,
+ int contactIdColumn,
+ int lookUpKeyColumn,
+ int displayNameColumn) {
+ long photoId = 0;
+ if (!cursor.isNull(photoIdColumn)) {
+ photoId = cursor.getLong(photoIdColumn);
+ }
+
+ QuickContactBadge quickContact = view.getQuickContact();
+ quickContact.assignContactUri(
+ getContactUri(partitionIndex, cursor, contactIdColumn, lookUpKeyColumn));
+ if (CompatUtils.hasPrioritizedMimeType()) {
+ // The Contacts app never uses the QuickContactBadge. Therefore, it is safe to assume
+ // that only Dialer will use this QuickContact badge. This means prioritizing the phone
+ // mimetype here is reasonable.
+ quickContact.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE);
+ }
+
+ if (photoId != 0 || photoUriColumn == -1) {
+ getPhotoLoader().loadThumbnail(quickContact, photoId, mDarkTheme, mCircularPhotos, null);
+ } else {
+ final String photoUriString = cursor.getString(photoUriColumn);
+ final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString);
+ DefaultImageRequest request = null;
+ if (photoUri == null) {
+ request = getDefaultImageRequestFromCursor(cursor, displayNameColumn, lookUpKeyColumn);
+ }
+ getPhotoLoader().loadPhoto(quickContact, photoUri, -1, mDarkTheme, mCircularPhotos, request);
+ }
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ // Whenever bindViewId() is called, the values passed into setId() are stable or
+ // stable-ish. For example, when one contact is modified we don't expect a second
+ // contact's Contact._ID values to change.
+ return true;
+ }
+
+ protected void bindViewId(final ContactListItemView view, Cursor cursor, int idColumn) {
+ // Set a semi-stable id, so that talkback won't get confused when the list gets
+ // refreshed. There is little harm in inserting the same ID twice.
+ long contactId = cursor.getLong(idColumn);
+ view.setId((int) (contactId % Integer.MAX_VALUE));
+ }
+
+ protected Uri getContactUri(
+ int partitionIndex, Cursor cursor, int contactIdColumn, int lookUpKeyColumn) {
+ long contactId = cursor.getLong(contactIdColumn);
+ String lookupKey = cursor.getString(lookUpKeyColumn);
+ long directoryId = ((DirectoryPartition) getPartition(partitionIndex)).getDirectoryId();
+ Uri uri = Contacts.getLookupUri(contactId, lookupKey);
+ if (uri != null && directoryId != Directory.DEFAULT) {
+ uri =
+ uri.buildUpon()
+ .appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId))
+ .build();
+ }
+ return uri;
+ }
+
+ /**
+ * Retrieves the lookup key and display name from a cursor, and returns a {@link
+ * DefaultImageRequest} containing these contact details
+ *
+ * @param cursor Contacts cursor positioned at the current row to retrieve contact details for
+ * @param displayNameColumn Column index of the display name
+ * @param lookupKeyColumn Column index of the lookup key
+ * @return {@link DefaultImageRequest} with the displayName and identifier fields set to the
+ * display name and lookup key of the contact.
+ */
+ public DefaultImageRequest getDefaultImageRequestFromCursor(
+ Cursor cursor, int displayNameColumn, int lookupKeyColumn) {
+ final String displayName = cursor.getString(displayNameColumn);
+ final String lookupKey = cursor.getString(lookupKeyColumn);
+ return new DefaultImageRequest(displayName, lookupKey, mCircularPhotos);
+ }
+}
diff --git a/java/com/android/contacts/common/list/ContactEntryListFragment.java b/java/com/android/contacts/common/list/ContactEntryListFragment.java
new file mode 100644
index 000000000..a8d9b55ba
--- /dev/null
+++ b/java/com/android/contacts/common/list/ContactEntryListFragment.java
@@ -0,0 +1,862 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcelable;
+import android.provider.ContactsContract.Directory;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemLongClickListener;
+import android.widget.ListView;
+import com.android.common.widget.CompositeCursorAdapter.Partition;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.preference.ContactsPreferences;
+import com.android.contacts.common.util.ContactListViewUtils;
+import java.util.Locale;
+
+/** Common base class for various contact-related list fragments. */
+public abstract class ContactEntryListFragment<T extends ContactEntryListAdapter> extends Fragment
+ implements OnItemClickListener,
+ OnScrollListener,
+ OnFocusChangeListener,
+ OnTouchListener,
+ OnItemLongClickListener,
+ LoaderCallbacks<Cursor> {
+ private static final String TAG = "ContactEntryListFragment";
+ private static final String KEY_LIST_STATE = "liststate";
+ private static final String KEY_SECTION_HEADER_DISPLAY_ENABLED = "sectionHeaderDisplayEnabled";
+ private static final String KEY_PHOTO_LOADER_ENABLED = "photoLoaderEnabled";
+ private static final String KEY_QUICK_CONTACT_ENABLED = "quickContactEnabled";
+ private static final String KEY_ADJUST_SELECTION_BOUNDS_ENABLED = "adjustSelectionBoundsEnabled";
+ private static final String KEY_INCLUDE_PROFILE = "includeProfile";
+ private static final String KEY_SEARCH_MODE = "searchMode";
+ private static final String KEY_VISIBLE_SCROLLBAR_ENABLED = "visibleScrollbarEnabled";
+ private static final String KEY_SCROLLBAR_POSITION = "scrollbarPosition";
+ private static final String KEY_QUERY_STRING = "queryString";
+ private static final String KEY_DIRECTORY_SEARCH_MODE = "directorySearchMode";
+ private static final String KEY_SELECTION_VISIBLE = "selectionVisible";
+ private static final String KEY_DARK_THEME = "darkTheme";
+ private static final String KEY_LEGACY_COMPATIBILITY = "legacyCompatibility";
+ private static final String KEY_DIRECTORY_RESULT_LIMIT = "directoryResultLimit";
+
+ private static final String DIRECTORY_ID_ARG_KEY = "directoryId";
+
+ private static final int DIRECTORY_LOADER_ID = -1;
+
+ private static final int DIRECTORY_SEARCH_DELAY_MILLIS = 300;
+ private static final int DIRECTORY_SEARCH_MESSAGE = 1;
+
+ private static final int DEFAULT_DIRECTORY_RESULT_LIMIT = 20;
+ private static final int STATUS_NOT_LOADED = 0;
+ private static final int STATUS_LOADING = 1;
+ private static final int STATUS_LOADED = 2;
+ protected boolean mUserProfileExists;
+ private boolean mSectionHeaderDisplayEnabled;
+ private boolean mPhotoLoaderEnabled;
+ private boolean mQuickContactEnabled = true;
+ private boolean mAdjustSelectionBoundsEnabled = true;
+ private boolean mIncludeProfile;
+ private boolean mSearchMode;
+ private boolean mVisibleScrollbarEnabled;
+ private boolean mShowEmptyListForEmptyQuery;
+ private int mVerticalScrollbarPosition = getDefaultVerticalScrollbarPosition();
+ private String mQueryString;
+ private int mDirectorySearchMode = DirectoryListLoader.SEARCH_MODE_NONE;
+ private boolean mSelectionVisible;
+ private boolean mLegacyCompatibility;
+ private boolean mEnabled = true;
+ private T mAdapter;
+ private View mView;
+ private ListView mListView;
+ /** Used to save the scrolling state of the list when the fragment is not recreated. */
+ private int mListViewTopIndex;
+
+ private int mListViewTopOffset;
+ /** Used for keeping track of the scroll state of the list. */
+ private Parcelable mListState;
+
+ private int mDisplayOrder;
+ private int mSortOrder;
+ private int mDirectoryResultLimit = DEFAULT_DIRECTORY_RESULT_LIMIT;
+ private ContactPhotoManager mPhotoManager;
+ private ContactsPreferences mContactsPrefs;
+ private boolean mForceLoad;
+ private boolean mDarkTheme;
+ private int mDirectoryListStatus = STATUS_NOT_LOADED;
+
+ /**
+ * Indicates whether we are doing the initial complete load of data (false) or a refresh caused by
+ * a change notification (true)
+ */
+ private boolean mLoadPriorityDirectoriesOnly;
+
+ private Context mContext;
+
+ private LoaderManager mLoaderManager;
+
+ private Handler mDelayedDirectorySearchHandler =
+ new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == DIRECTORY_SEARCH_MESSAGE) {
+ loadDirectoryPartition(msg.arg1, (DirectoryPartition) msg.obj);
+ }
+ }
+ };
+ private ContactsPreferences.ChangeListener mPreferencesChangeListener =
+ new ContactsPreferences.ChangeListener() {
+ @Override
+ public void onChange() {
+ loadPreferences();
+ reloadData();
+ }
+ };
+
+ protected abstract View inflateView(LayoutInflater inflater, ViewGroup container);
+
+ protected abstract T createListAdapter();
+
+ /**
+ * @param position Please note that the position is already adjusted for header views, so "0"
+ * means the first list item below header views.
+ */
+ protected abstract void onItemClick(int position, long id);
+
+ /**
+ * @param position Please note that the position is already adjusted for header views, so "0"
+ * means the first list item below header views.
+ */
+ protected boolean onItemLongClick(int position, long id) {
+ return false;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ setContext(activity);
+ setLoaderManager(super.getLoaderManager());
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ /** Sets a context for the fragment in the unit test environment. */
+ public void setContext(Context context) {
+ mContext = context;
+ configurePhotoLoader();
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (mEnabled != enabled) {
+ mEnabled = enabled;
+ if (mAdapter != null) {
+ if (mEnabled) {
+ reloadData();
+ } else {
+ mAdapter.clearPartitions();
+ }
+ }
+ }
+ }
+
+ @Override
+ public LoaderManager getLoaderManager() {
+ return mLoaderManager;
+ }
+
+ /** Overrides a loader manager for use in unit tests. */
+ public void setLoaderManager(LoaderManager loaderManager) {
+ mLoaderManager = loaderManager;
+ }
+
+ public T getAdapter() {
+ return mAdapter;
+ }
+
+ @Override
+ public View getView() {
+ return mView;
+ }
+
+ public ListView getListView() {
+ return mListView;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_SECTION_HEADER_DISPLAY_ENABLED, mSectionHeaderDisplayEnabled);
+ outState.putBoolean(KEY_PHOTO_LOADER_ENABLED, mPhotoLoaderEnabled);
+ outState.putBoolean(KEY_QUICK_CONTACT_ENABLED, mQuickContactEnabled);
+ outState.putBoolean(KEY_ADJUST_SELECTION_BOUNDS_ENABLED, mAdjustSelectionBoundsEnabled);
+ outState.putBoolean(KEY_INCLUDE_PROFILE, mIncludeProfile);
+ outState.putBoolean(KEY_SEARCH_MODE, mSearchMode);
+ outState.putBoolean(KEY_VISIBLE_SCROLLBAR_ENABLED, mVisibleScrollbarEnabled);
+ outState.putInt(KEY_SCROLLBAR_POSITION, mVerticalScrollbarPosition);
+ outState.putInt(KEY_DIRECTORY_SEARCH_MODE, mDirectorySearchMode);
+ outState.putBoolean(KEY_SELECTION_VISIBLE, mSelectionVisible);
+ outState.putBoolean(KEY_LEGACY_COMPATIBILITY, mLegacyCompatibility);
+ outState.putString(KEY_QUERY_STRING, mQueryString);
+ outState.putInt(KEY_DIRECTORY_RESULT_LIMIT, mDirectoryResultLimit);
+ outState.putBoolean(KEY_DARK_THEME, mDarkTheme);
+
+ if (mListView != null) {
+ outState.putParcelable(KEY_LIST_STATE, mListView.onSaveInstanceState());
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+ restoreSavedState(savedState);
+ mAdapter = createListAdapter();
+ mContactsPrefs = new ContactsPreferences(mContext);
+ }
+
+ public void restoreSavedState(Bundle savedState) {
+ if (savedState == null) {
+ return;
+ }
+
+ mSectionHeaderDisplayEnabled = savedState.getBoolean(KEY_SECTION_HEADER_DISPLAY_ENABLED);
+ mPhotoLoaderEnabled = savedState.getBoolean(KEY_PHOTO_LOADER_ENABLED);
+ mQuickContactEnabled = savedState.getBoolean(KEY_QUICK_CONTACT_ENABLED);
+ mAdjustSelectionBoundsEnabled = savedState.getBoolean(KEY_ADJUST_SELECTION_BOUNDS_ENABLED);
+ mIncludeProfile = savedState.getBoolean(KEY_INCLUDE_PROFILE);
+ mSearchMode = savedState.getBoolean(KEY_SEARCH_MODE);
+ mVisibleScrollbarEnabled = savedState.getBoolean(KEY_VISIBLE_SCROLLBAR_ENABLED);
+ mVerticalScrollbarPosition = savedState.getInt(KEY_SCROLLBAR_POSITION);
+ mDirectorySearchMode = savedState.getInt(KEY_DIRECTORY_SEARCH_MODE);
+ mSelectionVisible = savedState.getBoolean(KEY_SELECTION_VISIBLE);
+ mLegacyCompatibility = savedState.getBoolean(KEY_LEGACY_COMPATIBILITY);
+ mQueryString = savedState.getString(KEY_QUERY_STRING);
+ mDirectoryResultLimit = savedState.getInt(KEY_DIRECTORY_RESULT_LIMIT);
+ mDarkTheme = savedState.getBoolean(KEY_DARK_THEME);
+
+ // Retrieve list state. This will be applied in onLoadFinished
+ mListState = savedState.getParcelable(KEY_LIST_STATE);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ mContactsPrefs.registerChangeListener(mPreferencesChangeListener);
+
+ mForceLoad = loadPreferences();
+
+ mDirectoryListStatus = STATUS_NOT_LOADED;
+ mLoadPriorityDirectoriesOnly = true;
+
+ startLoading();
+ }
+
+ protected void startLoading() {
+ if (mAdapter == null) {
+ // The method was called before the fragment was started
+ return;
+ }
+
+ configureAdapter();
+ int partitionCount = mAdapter.getPartitionCount();
+ for (int i = 0; i < partitionCount; i++) {
+ Partition partition = mAdapter.getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ DirectoryPartition directoryPartition = (DirectoryPartition) partition;
+ if (directoryPartition.getStatus() == DirectoryPartition.STATUS_NOT_LOADED) {
+ if (directoryPartition.isPriorityDirectory() || !mLoadPriorityDirectoriesOnly) {
+ startLoadingDirectoryPartition(i);
+ }
+ }
+ } else {
+ getLoaderManager().initLoader(i, null, this);
+ }
+ }
+
+ // Next time this method is called, we should start loading non-priority directories
+ mLoadPriorityDirectoriesOnly = false;
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ if (id == DIRECTORY_LOADER_ID) {
+ DirectoryListLoader loader = new DirectoryListLoader(mContext);
+ loader.setDirectorySearchMode(mAdapter.getDirectorySearchMode());
+ loader.setLocalInvisibleDirectoryEnabled(
+ ContactEntryListAdapter.LOCAL_INVISIBLE_DIRECTORY_ENABLED);
+ return loader;
+ } else {
+ CursorLoader loader = createCursorLoader(mContext);
+ long directoryId =
+ args != null && args.containsKey(DIRECTORY_ID_ARG_KEY)
+ ? args.getLong(DIRECTORY_ID_ARG_KEY)
+ : Directory.DEFAULT;
+ mAdapter.configureLoader(loader, directoryId);
+ return loader;
+ }
+ }
+
+ public CursorLoader createCursorLoader(Context context) {
+ return new CursorLoader(context, null, null, null, null, null) {
+ @Override
+ protected Cursor onLoadInBackground() {
+ try {
+ return super.onLoadInBackground();
+ } catch (RuntimeException e) {
+ // We don't even know what the projection should be, so no point trying to
+ // return an empty MatrixCursor with the correct projection here.
+ Log.w(TAG, "RuntimeException while trying to query ContactsProvider.");
+ return null;
+ }
+ }
+ };
+ }
+
+ private void startLoadingDirectoryPartition(int partitionIndex) {
+ DirectoryPartition partition = (DirectoryPartition) mAdapter.getPartition(partitionIndex);
+ partition.setStatus(DirectoryPartition.STATUS_LOADING);
+ long directoryId = partition.getDirectoryId();
+ if (mForceLoad) {
+ if (directoryId == Directory.DEFAULT) {
+ loadDirectoryPartition(partitionIndex, partition);
+ } else {
+ loadDirectoryPartitionDelayed(partitionIndex, partition);
+ }
+ } else {
+ Bundle args = new Bundle();
+ args.putLong(DIRECTORY_ID_ARG_KEY, directoryId);
+ getLoaderManager().initLoader(partitionIndex, args, this);
+ }
+ }
+
+ /**
+ * Queues up a delayed request to search the specified directory. Since directory search will
+ * likely introduce a lot of network traffic, we want to wait for a pause in the user's typing
+ * before sending a directory request.
+ */
+ private void loadDirectoryPartitionDelayed(int partitionIndex, DirectoryPartition partition) {
+ mDelayedDirectorySearchHandler.removeMessages(DIRECTORY_SEARCH_MESSAGE, partition);
+ Message msg =
+ mDelayedDirectorySearchHandler.obtainMessage(
+ DIRECTORY_SEARCH_MESSAGE, partitionIndex, 0, partition);
+ mDelayedDirectorySearchHandler.sendMessageDelayed(msg, DIRECTORY_SEARCH_DELAY_MILLIS);
+ }
+
+ /** Loads the directory partition. */
+ protected void loadDirectoryPartition(int partitionIndex, DirectoryPartition partition) {
+ Bundle args = new Bundle();
+ args.putLong(DIRECTORY_ID_ARG_KEY, partition.getDirectoryId());
+ getLoaderManager().restartLoader(partitionIndex, args, this);
+ }
+
+ /** Cancels all queued directory loading requests. */
+ private void removePendingDirectorySearchRequests() {
+ mDelayedDirectorySearchHandler.removeMessages(DIRECTORY_SEARCH_MESSAGE);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ if (!mEnabled) {
+ return;
+ }
+
+ int loaderId = loader.getId();
+ if (loaderId == DIRECTORY_LOADER_ID) {
+ mDirectoryListStatus = STATUS_LOADED;
+ mAdapter.changeDirectories(data);
+ startLoading();
+ } else {
+ onPartitionLoaded(loaderId, data);
+ if (isSearchMode()) {
+ int directorySearchMode = getDirectorySearchMode();
+ if (directorySearchMode != DirectoryListLoader.SEARCH_MODE_NONE) {
+ if (mDirectoryListStatus == STATUS_NOT_LOADED) {
+ mDirectoryListStatus = STATUS_LOADING;
+ getLoaderManager().initLoader(DIRECTORY_LOADER_ID, null, this);
+ } else {
+ startLoading();
+ }
+ }
+ } else {
+ mDirectoryListStatus = STATUS_NOT_LOADED;
+ getLoaderManager().destroyLoader(DIRECTORY_LOADER_ID);
+ }
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {}
+
+ protected void onPartitionLoaded(int partitionIndex, Cursor data) {
+ if (partitionIndex >= mAdapter.getPartitionCount()) {
+ // When we get unsolicited data, ignore it. This could happen
+ // when we are switching from search mode to the default mode.
+ return;
+ }
+
+ mAdapter.changeCursor(partitionIndex, data);
+ setProfileHeader();
+
+ if (!isLoading()) {
+ completeRestoreInstanceState();
+ }
+ }
+
+ public boolean isLoading() {
+ if (mAdapter != null && mAdapter.isLoading()) {
+ return true;
+ }
+
+ return isLoadingDirectoryList();
+
+ }
+
+ public boolean isLoadingDirectoryList() {
+ return isSearchMode()
+ && getDirectorySearchMode() != DirectoryListLoader.SEARCH_MODE_NONE
+ && (mDirectoryListStatus == STATUS_NOT_LOADED || mDirectoryListStatus == STATUS_LOADING);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ mContactsPrefs.unregisterChangeListener();
+ mAdapter.clearPartitions();
+ }
+
+ protected void reloadData() {
+ removePendingDirectorySearchRequests();
+ mAdapter.onDataReload();
+ mLoadPriorityDirectoriesOnly = true;
+ mForceLoad = true;
+ startLoading();
+ }
+
+ /**
+ * Shows a view at the top of the list with a pseudo local profile prompting the user to add a
+ * local profile. Default implementation does nothing.
+ */
+ protected void setProfileHeader() {
+ mUserProfileExists = false;
+ }
+
+ /** Provides logic that dismisses this fragment. The default implementation does nothing. */
+ protected void finish() {}
+
+ public boolean isSectionHeaderDisplayEnabled() {
+ return mSectionHeaderDisplayEnabled;
+ }
+
+ public void setSectionHeaderDisplayEnabled(boolean flag) {
+ if (mSectionHeaderDisplayEnabled != flag) {
+ mSectionHeaderDisplayEnabled = flag;
+ if (mAdapter != null) {
+ mAdapter.setSectionHeaderDisplayEnabled(flag);
+ }
+ configureVerticalScrollbar();
+ }
+ }
+
+ public boolean isVisibleScrollbarEnabled() {
+ return mVisibleScrollbarEnabled;
+ }
+
+ public void setVisibleScrollbarEnabled(boolean flag) {
+ if (mVisibleScrollbarEnabled != flag) {
+ mVisibleScrollbarEnabled = flag;
+ configureVerticalScrollbar();
+ }
+ }
+
+ private void configureVerticalScrollbar() {
+ boolean hasScrollbar = isVisibleScrollbarEnabled() && isSectionHeaderDisplayEnabled();
+
+ if (mListView != null) {
+ mListView.setFastScrollEnabled(hasScrollbar);
+ mListView.setFastScrollAlwaysVisible(hasScrollbar);
+ mListView.setVerticalScrollbarPosition(mVerticalScrollbarPosition);
+ mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
+ }
+ }
+
+ public boolean isPhotoLoaderEnabled() {
+ return mPhotoLoaderEnabled;
+ }
+
+ public void setPhotoLoaderEnabled(boolean flag) {
+ mPhotoLoaderEnabled = flag;
+ configurePhotoLoader();
+ }
+
+ public void setQuickContactEnabled(boolean flag) {
+ this.mQuickContactEnabled = flag;
+ }
+
+ public void setAdjustSelectionBoundsEnabled(boolean flag) {
+ mAdjustSelectionBoundsEnabled = flag;
+ }
+
+ public final boolean isSearchMode() {
+ return mSearchMode;
+ }
+
+ /**
+ * Enter/exit search mode. This is method is tightly related to the current query, and should only
+ * be called by {@link #setQueryString}.
+ *
+ * <p>Also note this method doesn't call {@link #reloadData()}; {@link #setQueryString} does it.
+ */
+ protected void setSearchMode(boolean flag) {
+ if (mSearchMode != flag) {
+ mSearchMode = flag;
+ setSectionHeaderDisplayEnabled(!mSearchMode);
+
+ if (!flag) {
+ mDirectoryListStatus = STATUS_NOT_LOADED;
+ getLoaderManager().destroyLoader(DIRECTORY_LOADER_ID);
+ }
+
+ if (mAdapter != null) {
+ mAdapter.setSearchMode(flag);
+
+ mAdapter.clearPartitions();
+ if (!flag) {
+ // If we are switching from search to regular display, remove all directory
+ // partitions after default one, assuming they are remote directories which
+ // should be cleaned up on exiting the search mode.
+ mAdapter.removeDirectoriesAfterDefault();
+ }
+ mAdapter.configureDefaultPartition(false, flag);
+ }
+
+ if (mListView != null) {
+ mListView.setFastScrollEnabled(!flag);
+ }
+ }
+ }
+
+ public final String getQueryString() {
+ return mQueryString;
+ }
+
+ public void setQueryString(String queryString) {
+ if (!TextUtils.equals(mQueryString, queryString)) {
+ if (mShowEmptyListForEmptyQuery && mAdapter != null && mListView != null) {
+ if (TextUtils.isEmpty(mQueryString)) {
+ // Restore the adapter if the query used to be empty.
+ mListView.setAdapter(mAdapter);
+ } else if (TextUtils.isEmpty(queryString)) {
+ // Instantly clear the list view if the new query is empty.
+ mListView.setAdapter(null);
+ }
+ }
+
+ mQueryString = queryString;
+ setSearchMode(!TextUtils.isEmpty(mQueryString) || mShowEmptyListForEmptyQuery);
+
+ if (mAdapter != null) {
+ mAdapter.setQueryString(queryString);
+ reloadData();
+ }
+ }
+ }
+
+ public void setShowEmptyListForNullQuery(boolean show) {
+ mShowEmptyListForEmptyQuery = show;
+ }
+
+ public boolean getShowEmptyListForNullQuery() {
+ return mShowEmptyListForEmptyQuery;
+ }
+
+ public int getDirectoryLoaderId() {
+ return DIRECTORY_LOADER_ID;
+ }
+
+ public int getDirectorySearchMode() {
+ return mDirectorySearchMode;
+ }
+
+ public void setDirectorySearchMode(int mode) {
+ mDirectorySearchMode = mode;
+ }
+
+ protected int getContactNameDisplayOrder() {
+ return mDisplayOrder;
+ }
+
+ protected void setContactNameDisplayOrder(int displayOrder) {
+ mDisplayOrder = displayOrder;
+ if (mAdapter != null) {
+ mAdapter.setContactNameDisplayOrder(displayOrder);
+ }
+ }
+
+ public int getSortOrder() {
+ return mSortOrder;
+ }
+
+ public void setSortOrder(int sortOrder) {
+ mSortOrder = sortOrder;
+ if (mAdapter != null) {
+ mAdapter.setSortOrder(sortOrder);
+ }
+ }
+
+ public void setDirectoryResultLimit(int limit) {
+ mDirectoryResultLimit = limit;
+ }
+
+ protected boolean loadPreferences() {
+ boolean changed = false;
+ if (getContactNameDisplayOrder() != mContactsPrefs.getDisplayOrder()) {
+ setContactNameDisplayOrder(mContactsPrefs.getDisplayOrder());
+ changed = true;
+ }
+
+ if (getSortOrder() != mContactsPrefs.getSortOrder()) {
+ setSortOrder(mContactsPrefs.getSortOrder());
+ changed = true;
+ }
+
+ return changed;
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ onCreateView(inflater, container);
+
+ boolean searchMode = isSearchMode();
+ mAdapter.setSearchMode(searchMode);
+ mAdapter.configureDefaultPartition(false, searchMode);
+ mAdapter.setPhotoLoader(mPhotoManager);
+ mListView.setAdapter(mAdapter);
+
+ if (!isSearchMode()) {
+ mListView.setFocusableInTouchMode(true);
+ mListView.requestFocus();
+ }
+
+ return mView;
+ }
+
+ protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
+ mView = inflateView(inflater, container);
+
+ mListView = (ListView) mView.findViewById(android.R.id.list);
+ if (mListView == null) {
+ throw new RuntimeException(
+ "Your content must have a ListView whose id attribute is " + "'android.R.id.list'");
+ }
+
+ View emptyView = mView.findViewById(android.R.id.empty);
+ if (emptyView != null) {
+ mListView.setEmptyView(emptyView);
+ }
+
+ mListView.setOnItemClickListener(this);
+ mListView.setOnItemLongClickListener(this);
+ mListView.setOnFocusChangeListener(this);
+ mListView.setOnTouchListener(this);
+ mListView.setFastScrollEnabled(!isSearchMode());
+
+ // Tell list view to not show dividers. We'll do it ourself so that we can *not* show
+ // them when an A-Z headers is visible.
+ mListView.setDividerHeight(0);
+
+ // We manually save/restore the listview state
+ mListView.setSaveEnabled(false);
+
+ configureVerticalScrollbar();
+ configurePhotoLoader();
+
+ getAdapter().setFragmentRootView(getView());
+
+ ContactListViewUtils.applyCardPaddingToView(getResources(), mListView, mView);
+ }
+
+ @Override
+ public void onHiddenChanged(boolean hidden) {
+ super.onHiddenChanged(hidden);
+ if (getActivity() != null && getView() != null && !hidden) {
+ // If the padding was last applied when in a hidden state, it may have been applied
+ // incorrectly. Therefore we need to reapply it.
+ ContactListViewUtils.applyCardPaddingToView(getResources(), mListView, getView());
+ }
+ }
+
+ protected void configurePhotoLoader() {
+ if (isPhotoLoaderEnabled() && mContext != null) {
+ if (mPhotoManager == null) {
+ mPhotoManager = ContactPhotoManager.getInstance(mContext);
+ }
+ if (mListView != null) {
+ mListView.setOnScrollListener(this);
+ }
+ if (mAdapter != null) {
+ mAdapter.setPhotoLoader(mPhotoManager);
+ }
+ }
+ }
+
+ protected void configureAdapter() {
+ if (mAdapter == null) {
+ return;
+ }
+
+ mAdapter.setQuickContactEnabled(mQuickContactEnabled);
+ mAdapter.setAdjustSelectionBoundsEnabled(mAdjustSelectionBoundsEnabled);
+ mAdapter.setQueryString(mQueryString);
+ mAdapter.setDirectorySearchMode(mDirectorySearchMode);
+ mAdapter.setPinnedPartitionHeadersEnabled(false);
+ mAdapter.setContactNameDisplayOrder(mDisplayOrder);
+ mAdapter.setSortOrder(mSortOrder);
+ mAdapter.setSectionHeaderDisplayEnabled(mSectionHeaderDisplayEnabled);
+ mAdapter.setSelectionVisible(mSelectionVisible);
+ mAdapter.setDirectoryResultLimit(mDirectoryResultLimit);
+ mAdapter.setDarkTheme(mDarkTheme);
+ }
+
+ @Override
+ public void onScroll(
+ AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {}
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ if (scrollState == OnScrollListener.SCROLL_STATE_FLING) {
+ mPhotoManager.pause();
+ } else if (isPhotoLoaderEnabled()) {
+ mPhotoManager.resume();
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ hideSoftKeyboard();
+
+ int adjPosition = position - mListView.getHeaderViewsCount();
+ if (adjPosition >= 0) {
+ onItemClick(adjPosition, id);
+ }
+ }
+
+ @Override
+ public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
+ int adjPosition = position - mListView.getHeaderViewsCount();
+
+ if (adjPosition >= 0) {
+ return onItemLongClick(adjPosition, id);
+ }
+ return false;
+ }
+
+ private void hideSoftKeyboard() {
+ // Hide soft keyboard, if visible
+ InputMethodManager inputMethodManager =
+ (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMethodManager.hideSoftInputFromWindow(mListView.getWindowToken(), 0);
+ }
+
+ /** Dismisses the soft keyboard when the list takes focus. */
+ @Override
+ public void onFocusChange(View view, boolean hasFocus) {
+ if (view == mListView && hasFocus) {
+ hideSoftKeyboard();
+ }
+ }
+
+ /** Dismisses the soft keyboard when the list is touched. */
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ if (view == mListView) {
+ hideSoftKeyboard();
+ }
+ return false;
+ }
+
+ @Override
+ public void onPause() {
+ // Save the scrolling state of the list view
+ mListViewTopIndex = mListView.getFirstVisiblePosition();
+ View v = mListView.getChildAt(0);
+ mListViewTopOffset = (v == null) ? 0 : (v.getTop() - mListView.getPaddingTop());
+
+ super.onPause();
+ removePendingDirectorySearchRequests();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ // Restore the selection of the list view. See b/19982820.
+ // This has to be done manually because if the list view has its emptyView set,
+ // the scrolling state will be reset when clearPartitions() is called on the adapter.
+ mListView.setSelectionFromTop(mListViewTopIndex, mListViewTopOffset);
+ }
+
+ /** Restore the list state after the adapter is populated. */
+ protected void completeRestoreInstanceState() {
+ if (mListState != null) {
+ mListView.onRestoreInstanceState(mListState);
+ mListState = null;
+ }
+ }
+
+ public void setDarkTheme(boolean value) {
+ mDarkTheme = value;
+ if (mAdapter != null) {
+ mAdapter.setDarkTheme(value);
+ }
+ }
+
+ private int getDefaultVerticalScrollbarPosition() {
+ final Locale locale = Locale.getDefault();
+ final int layoutDirection = TextUtils.getLayoutDirectionFromLocale(locale);
+ switch (layoutDirection) {
+ case View.LAYOUT_DIRECTION_RTL:
+ return View.SCROLLBAR_POSITION_LEFT;
+ case View.LAYOUT_DIRECTION_LTR:
+ default:
+ return View.SCROLLBAR_POSITION_RIGHT;
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/list/ContactListAdapter.java b/java/com/android/contacts/common/list/ContactListAdapter.java
new file mode 100644
index 000000000..6cd311811
--- /dev/null
+++ b/java/com/android/contacts/common/list/ContactListAdapter.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.SearchSnippets;
+import android.view.ViewGroup;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.R;
+import com.android.contacts.common.preference.ContactsPreferences;
+
+/**
+ * A cursor adapter for the {@link ContactsContract.Contacts#CONTENT_TYPE} content type. Also
+ * includes support for including the {@link ContactsContract.Profile} record in the list.
+ */
+public abstract class ContactListAdapter extends ContactEntryListAdapter {
+
+ private CharSequence mUnknownNameText;
+
+ public ContactListAdapter(Context context) {
+ super(context);
+
+ mUnknownNameText = context.getText(R.string.missing_name);
+ }
+
+ protected static Uri buildSectionIndexerUri(Uri uri) {
+ return uri.buildUpon().appendQueryParameter(Contacts.EXTRA_ADDRESS_BOOK_INDEX, "true").build();
+ }
+
+ public Uri getContactUri(int partitionIndex, Cursor cursor) {
+ long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
+ String lookupKey = cursor.getString(ContactQuery.CONTACT_LOOKUP_KEY);
+ Uri uri = Contacts.getLookupUri(contactId, lookupKey);
+ long directoryId = ((DirectoryPartition) getPartition(partitionIndex)).getDirectoryId();
+ if (uri != null && directoryId != Directory.DEFAULT) {
+ uri =
+ uri.buildUpon()
+ .appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId))
+ .build();
+ }
+ return uri;
+ }
+
+ @Override
+ protected ContactListItemView newView(
+ Context context, int partition, Cursor cursor, int position, ViewGroup parent) {
+ ContactListItemView view = super.newView(context, partition, cursor, position, parent);
+ view.setUnknownNameText(mUnknownNameText);
+ view.setQuickContactEnabled(isQuickContactEnabled());
+ view.setAdjustSelectionBoundsEnabled(isAdjustSelectionBoundsEnabled());
+ view.setActivatedStateSupported(isSelectionVisible());
+ return view;
+ }
+
+ protected void bindSectionHeaderAndDivider(
+ ContactListItemView view, int position, Cursor cursor) {
+ view.setIsSectionHeaderEnabled(isSectionHeaderDisplayEnabled());
+ if (isSectionHeaderDisplayEnabled()) {
+ Placement placement = getItemPlacementInSection(position);
+ view.setSectionHeader(placement.sectionHeader);
+ } else {
+ view.setSectionHeader(null);
+ }
+ }
+
+ protected void bindPhoto(final ContactListItemView view, int partitionIndex, Cursor cursor) {
+ if (!isPhotoSupported(partitionIndex)) {
+ view.removePhotoView();
+ return;
+ }
+
+ // Set the photo, if available
+ long photoId = 0;
+ if (!cursor.isNull(ContactQuery.CONTACT_PHOTO_ID)) {
+ photoId = cursor.getLong(ContactQuery.CONTACT_PHOTO_ID);
+ }
+
+ if (photoId != 0) {
+ getPhotoLoader()
+ .loadThumbnail(view.getPhotoView(), photoId, false, getCircularPhotos(), null);
+ } else {
+ final String photoUriString = cursor.getString(ContactQuery.CONTACT_PHOTO_URI);
+ final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString);
+ DefaultImageRequest request = null;
+ if (photoUri == null) {
+ request =
+ getDefaultImageRequestFromCursor(
+ cursor, ContactQuery.CONTACT_DISPLAY_NAME, ContactQuery.CONTACT_LOOKUP_KEY);
+ }
+ getPhotoLoader()
+ .loadDirectoryPhoto(view.getPhotoView(), photoUri, false, getCircularPhotos(), request);
+ }
+ }
+
+ protected void bindNameAndViewId(final ContactListItemView view, Cursor cursor) {
+ view.showDisplayName(cursor, ContactQuery.CONTACT_DISPLAY_NAME);
+ // Note: we don't show phonetic any more (See issue 5265330)
+
+ bindViewId(view, cursor, ContactQuery.CONTACT_ID);
+ }
+
+ protected void bindPresenceAndStatusMessage(final ContactListItemView view, Cursor cursor) {
+ view.showPresenceAndStatusMessage(
+ cursor, ContactQuery.CONTACT_PRESENCE_STATUS, ContactQuery.CONTACT_CONTACT_STATUS);
+ }
+
+ protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
+ view.showSnippet(cursor, ContactQuery.CONTACT_SNIPPET);
+ }
+
+ @Override
+ public void changeCursor(int partitionIndex, Cursor cursor) {
+ super.changeCursor(partitionIndex, cursor);
+
+ if (cursor == null || !cursor.moveToFirst()) {
+ return;
+ }
+
+ // hasProfile tells whether the first row is a profile
+ final boolean hasProfile = cursor.getInt(ContactQuery.CONTACT_IS_USER_PROFILE) == 1;
+
+ // Add ME profile on top of favorites
+ cursor.moveToFirst();
+ setProfileExists(hasProfile);
+ }
+
+ /** @return Projection useful for children. */
+ protected final String[] getProjection(boolean forSearch) {
+ final int sortOrder = getContactNameDisplayOrder();
+ if (forSearch) {
+ if (sortOrder == ContactsPreferences.DISPLAY_ORDER_PRIMARY) {
+ return ContactQuery.FILTER_PROJECTION_PRIMARY;
+ } else {
+ return ContactQuery.FILTER_PROJECTION_ALTERNATIVE;
+ }
+ } else {
+ if (sortOrder == ContactsPreferences.DISPLAY_ORDER_PRIMARY) {
+ return ContactQuery.CONTACT_PROJECTION_PRIMARY;
+ } else {
+ return ContactQuery.CONTACT_PROJECTION_ALTERNATIVE;
+ }
+ }
+ }
+
+ protected static class ContactQuery {
+
+ public static final int CONTACT_ID = 0;
+ public static final int CONTACT_DISPLAY_NAME = 1;
+ public static final int CONTACT_PRESENCE_STATUS = 2;
+ public static final int CONTACT_CONTACT_STATUS = 3;
+ public static final int CONTACT_PHOTO_ID = 4;
+ public static final int CONTACT_PHOTO_URI = 5;
+ public static final int CONTACT_LOOKUP_KEY = 6;
+ public static final int CONTACT_IS_USER_PROFILE = 7;
+ public static final int CONTACT_PHONETIC_NAME = 8;
+ public static final int CONTACT_STARRED = 9;
+ public static final int CONTACT_SNIPPET = 10;
+ private static final String[] CONTACT_PROJECTION_PRIMARY =
+ new String[] {
+ Contacts._ID, // 0
+ Contacts.DISPLAY_NAME_PRIMARY, // 1
+ Contacts.CONTACT_PRESENCE, // 2
+ Contacts.CONTACT_STATUS, // 3
+ Contacts.PHOTO_ID, // 4
+ Contacts.PHOTO_THUMBNAIL_URI, // 5
+ Contacts.LOOKUP_KEY, // 6
+ Contacts.IS_USER_PROFILE, // 7
+ Contacts.PHONETIC_NAME, // 8
+ Contacts.STARRED, // 9
+ };
+ private static final String[] CONTACT_PROJECTION_ALTERNATIVE =
+ new String[] {
+ Contacts._ID, // 0
+ Contacts.DISPLAY_NAME_ALTERNATIVE, // 1
+ Contacts.CONTACT_PRESENCE, // 2
+ Contacts.CONTACT_STATUS, // 3
+ Contacts.PHOTO_ID, // 4
+ Contacts.PHOTO_THUMBNAIL_URI, // 5
+ Contacts.LOOKUP_KEY, // 6
+ Contacts.IS_USER_PROFILE, // 7
+ Contacts.PHONETIC_NAME, // 8
+ Contacts.STARRED, // 9
+ };
+ private static final String[] FILTER_PROJECTION_PRIMARY =
+ new String[] {
+ Contacts._ID, // 0
+ Contacts.DISPLAY_NAME_PRIMARY, // 1
+ Contacts.CONTACT_PRESENCE, // 2
+ Contacts.CONTACT_STATUS, // 3
+ Contacts.PHOTO_ID, // 4
+ Contacts.PHOTO_THUMBNAIL_URI, // 5
+ Contacts.LOOKUP_KEY, // 6
+ Contacts.IS_USER_PROFILE, // 7
+ Contacts.PHONETIC_NAME, // 8
+ Contacts.STARRED, // 9
+ SearchSnippets.SNIPPET, // 10
+ };
+ private static final String[] FILTER_PROJECTION_ALTERNATIVE =
+ new String[] {
+ Contacts._ID, // 0
+ Contacts.DISPLAY_NAME_ALTERNATIVE, // 1
+ Contacts.CONTACT_PRESENCE, // 2
+ Contacts.CONTACT_STATUS, // 3
+ Contacts.PHOTO_ID, // 4
+ Contacts.PHOTO_THUMBNAIL_URI, // 5
+ Contacts.LOOKUP_KEY, // 6
+ Contacts.IS_USER_PROFILE, // 7
+ Contacts.PHONETIC_NAME, // 8
+ Contacts.STARRED, // 9
+ SearchSnippets.SNIPPET, // 10
+ };
+ }
+}
diff --git a/java/com/android/contacts/common/list/ContactListFilter.java b/java/com/android/contacts/common/list/ContactListFilter.java
new file mode 100644
index 000000000..1a03bb64c
--- /dev/null
+++ b/java/com/android/contacts/common/list/ContactListFilter.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.SharedPreferences;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.ContactsContract.RawContacts;
+import android.text.TextUtils;
+
+/** Contact list filter parameters. */
+public final class ContactListFilter implements Comparable<ContactListFilter>, Parcelable {
+
+ public static final int FILTER_TYPE_DEFAULT = -1;
+ public static final int FILTER_TYPE_ALL_ACCOUNTS = -2;
+ public static final int FILTER_TYPE_CUSTOM = -3;
+ public static final int FILTER_TYPE_STARRED = -4;
+ public static final int FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY = -5;
+ public static final int FILTER_TYPE_SINGLE_CONTACT = -6;
+
+ public static final int FILTER_TYPE_ACCOUNT = 0;
+ public static final Parcelable.Creator<ContactListFilter> CREATOR =
+ new Parcelable.Creator<ContactListFilter>() {
+ @Override
+ public ContactListFilter createFromParcel(Parcel source) {
+ int filterType = source.readInt();
+ String accountName = source.readString();
+ String accountType = source.readString();
+ String dataSet = source.readString();
+ return new ContactListFilter(filterType, accountType, accountName, dataSet, null);
+ }
+
+ @Override
+ public ContactListFilter[] newArray(int size) {
+ return new ContactListFilter[size];
+ }
+ };
+ /**
+ * Obsolete filter which had been used in Honeycomb. This may be stored in {@link
+ * SharedPreferences}, but should be replaced with ALL filter when it is found.
+ *
+ * <p>TODO: "group" filter and relevant variables are all obsolete. Remove them.
+ */
+ private static final int FILTER_TYPE_GROUP = 1;
+
+ private static final String KEY_FILTER_TYPE = "filter.type";
+ private static final String KEY_ACCOUNT_NAME = "filter.accountName";
+ private static final String KEY_ACCOUNT_TYPE = "filter.accountType";
+ private static final String KEY_DATA_SET = "filter.dataSet";
+ public final int filterType;
+ public final String accountType;
+ public final String accountName;
+ public final String dataSet;
+ public final Drawable icon;
+ private String mId;
+
+ public ContactListFilter(
+ int filterType, String accountType, String accountName, String dataSet, Drawable icon) {
+ this.filterType = filterType;
+ this.accountType = accountType;
+ this.accountName = accountName;
+ this.dataSet = dataSet;
+ this.icon = icon;
+ }
+
+ public static ContactListFilter createFilterWithType(int filterType) {
+ return new ContactListFilter(filterType, null, null, null, null);
+ }
+
+ public static ContactListFilter createAccountFilter(
+ String accountType, String accountName, String dataSet, Drawable icon) {
+ return new ContactListFilter(
+ ContactListFilter.FILTER_TYPE_ACCOUNT, accountType, accountName, dataSet, icon);
+ }
+
+ /**
+ * Store the given {@link ContactListFilter} to preferences. If the requested filter is of type
+ * {@link #FILTER_TYPE_SINGLE_CONTACT} then do not save it to preferences because it is a
+ * temporary state.
+ */
+ public static void storeToPreferences(SharedPreferences prefs, ContactListFilter filter) {
+ if (filter != null && filter.filterType == FILTER_TYPE_SINGLE_CONTACT) {
+ return;
+ }
+ prefs
+ .edit()
+ .putInt(KEY_FILTER_TYPE, filter == null ? FILTER_TYPE_DEFAULT : filter.filterType)
+ .putString(KEY_ACCOUNT_NAME, filter == null ? null : filter.accountName)
+ .putString(KEY_ACCOUNT_TYPE, filter == null ? null : filter.accountType)
+ .putString(KEY_DATA_SET, filter == null ? null : filter.dataSet)
+ .apply();
+ }
+
+ /**
+ * Try to obtain ContactListFilter object saved in SharedPreference. If there's no info there,
+ * return ALL filter instead.
+ */
+ public static ContactListFilter restoreDefaultPreferences(SharedPreferences prefs) {
+ ContactListFilter filter = restoreFromPreferences(prefs);
+ if (filter == null) {
+ filter = ContactListFilter.createFilterWithType(FILTER_TYPE_ALL_ACCOUNTS);
+ }
+ // "Group" filter is obsolete and thus is not exposed anymore. The "single contact mode"
+ // should also not be stored in preferences anymore since it is a temporary state.
+ if (filter.filterType == FILTER_TYPE_GROUP || filter.filterType == FILTER_TYPE_SINGLE_CONTACT) {
+ filter = ContactListFilter.createFilterWithType(FILTER_TYPE_ALL_ACCOUNTS);
+ }
+ return filter;
+ }
+
+ private static ContactListFilter restoreFromPreferences(SharedPreferences prefs) {
+ int filterType = prefs.getInt(KEY_FILTER_TYPE, FILTER_TYPE_DEFAULT);
+ if (filterType == FILTER_TYPE_DEFAULT) {
+ return null;
+ }
+
+ String accountName = prefs.getString(KEY_ACCOUNT_NAME, null);
+ String accountType = prefs.getString(KEY_ACCOUNT_TYPE, null);
+ String dataSet = prefs.getString(KEY_DATA_SET, null);
+ return new ContactListFilter(filterType, accountType, accountName, dataSet, null);
+ }
+
+ public static final String filterTypeToString(int filterType) {
+ switch (filterType) {
+ case FILTER_TYPE_DEFAULT:
+ return "FILTER_TYPE_DEFAULT";
+ case FILTER_TYPE_ALL_ACCOUNTS:
+ return "FILTER_TYPE_ALL_ACCOUNTS";
+ case FILTER_TYPE_CUSTOM:
+ return "FILTER_TYPE_CUSTOM";
+ case FILTER_TYPE_STARRED:
+ return "FILTER_TYPE_STARRED";
+ case FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY:
+ return "FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY";
+ case FILTER_TYPE_SINGLE_CONTACT:
+ return "FILTER_TYPE_SINGLE_CONTACT";
+ case FILTER_TYPE_ACCOUNT:
+ return "FILTER_TYPE_ACCOUNT";
+ default:
+ return "(unknown)";
+ }
+ }
+
+ /** Returns true if this filter is based on data and may become invalid over time. */
+ public boolean isValidationRequired() {
+ return filterType == FILTER_TYPE_ACCOUNT;
+ }
+
+ @Override
+ public String toString() {
+ switch (filterType) {
+ case FILTER_TYPE_DEFAULT:
+ return "default";
+ case FILTER_TYPE_ALL_ACCOUNTS:
+ return "all_accounts";
+ case FILTER_TYPE_CUSTOM:
+ return "custom";
+ case FILTER_TYPE_STARRED:
+ return "starred";
+ case FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY:
+ return "with_phones";
+ case FILTER_TYPE_SINGLE_CONTACT:
+ return "single";
+ case FILTER_TYPE_ACCOUNT:
+ return "account: "
+ + accountType
+ + (dataSet != null ? "/" + dataSet : "")
+ + " "
+ + accountName;
+ }
+ return super.toString();
+ }
+
+ @Override
+ public int compareTo(ContactListFilter another) {
+ int res = accountName.compareTo(another.accountName);
+ if (res != 0) {
+ return res;
+ }
+
+ res = accountType.compareTo(another.accountType);
+ if (res != 0) {
+ return res;
+ }
+
+ return filterType - another.filterType;
+ }
+
+ @Override
+ public int hashCode() {
+ int code = filterType;
+ if (accountType != null) {
+ code = code * 31 + accountType.hashCode();
+ code = code * 31 + accountName.hashCode();
+ }
+ if (dataSet != null) {
+ code = code * 31 + dataSet.hashCode();
+ }
+ return code;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof ContactListFilter)) {
+ return false;
+ }
+
+ ContactListFilter otherFilter = (ContactListFilter) other;
+ return filterType == otherFilter.filterType
+ && TextUtils.equals(accountName, otherFilter.accountName)
+ && TextUtils.equals(accountType, otherFilter.accountType)
+ && TextUtils.equals(dataSet, otherFilter.dataSet);
+
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(filterType);
+ dest.writeString(accountName);
+ dest.writeString(accountType);
+ dest.writeString(dataSet);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Returns a string that can be used as a stable persistent identifier for this filter. */
+ public String getId() {
+ if (mId == null) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(filterType);
+ if (accountType != null) {
+ sb.append('-').append(accountType);
+ }
+ if (dataSet != null) {
+ sb.append('/').append(dataSet);
+ }
+ if (accountName != null) {
+ sb.append('-').append(accountName.replace('-', '_'));
+ }
+ mId = sb.toString();
+ }
+ return mId;
+ }
+
+ /**
+ * Adds the account query parameters to the given {@code uriBuilder}.
+ *
+ * @throws IllegalStateException if the filter type is not {@link #FILTER_TYPE_ACCOUNT}.
+ */
+ public Uri.Builder addAccountQueryParameterToUrl(Uri.Builder uriBuilder) {
+ if (filterType != FILTER_TYPE_ACCOUNT) {
+ throw new IllegalStateException("filterType must be FILTER_TYPE_ACCOUNT");
+ }
+ uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_NAME, accountName);
+ uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, accountType);
+ if (!TextUtils.isEmpty(dataSet)) {
+ uriBuilder.appendQueryParameter(RawContacts.DATA_SET, dataSet);
+ }
+ return uriBuilder;
+ }
+
+ public String toDebugString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("[filter type: " + filterType + " (" + filterTypeToString(filterType) + ")");
+ if (filterType == FILTER_TYPE_ACCOUNT) {
+ builder
+ .append(", accountType: " + accountType)
+ .append(", accountName: " + accountName)
+ .append(", dataSet: " + dataSet);
+ }
+ builder.append(", icon: " + icon + "]");
+ return builder.toString();
+ }
+}
diff --git a/java/com/android/contacts/common/list/ContactListFilterController.java b/java/com/android/contacts/common/list/ContactListFilterController.java
new file mode 100644
index 000000000..d2168f3f2
--- /dev/null
+++ b/java/com/android/contacts/common/list/ContactListFilterController.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Manages {@link ContactListFilter}. All methods must be called from UI thread. */
+public abstract class ContactListFilterController {
+
+ // singleton to cache the filter controller
+ private static ContactListFilterControllerImpl sFilterController = null;
+
+ public static ContactListFilterController getInstance(Context context) {
+ // We may need to synchronize this in the future if background task will call this.
+ if (sFilterController == null) {
+ sFilterController = new ContactListFilterControllerImpl(context);
+ }
+ return sFilterController;
+ }
+
+ public abstract void addListener(ContactListFilterListener listener);
+
+ public abstract void removeListener(ContactListFilterListener listener);
+
+ /** Return the currently-active filter. */
+ public abstract ContactListFilter getFilter();
+
+ /**
+ * @param filter the filter
+ * @param persistent True when the given filter should be saved soon. False when the filter should
+ * not be saved. The latter case may happen when some Intent requires a certain type of UI
+ * (e.g. single contact) temporarily.
+ */
+ public abstract void setContactListFilter(ContactListFilter filter, boolean persistent);
+
+ public abstract void selectCustomFilter();
+
+ /**
+ * Checks if the current filter is valid and reset the filter if not. It may happen when an
+ * account is removed while the filter points to the account with {@link
+ * ContactListFilter#FILTER_TYPE_ACCOUNT} type, for example. It may also happen if the current
+ * filter is {@link ContactListFilter#FILTER_TYPE_SINGLE_CONTACT}, in which case, we should switch
+ * to the last saved filter in {@link SharedPreferences}.
+ */
+ public abstract void checkFilterValidity(boolean notifyListeners);
+
+ public interface ContactListFilterListener {
+
+ void onContactListFilterChanged();
+ }
+}
+
+/**
+ * Stores the {@link ContactListFilter} selected by the user and saves it to {@link
+ * SharedPreferences} if necessary.
+ */
+class ContactListFilterControllerImpl extends ContactListFilterController {
+
+ private final Context mContext;
+ private final List<ContactListFilterListener> mListeners =
+ new ArrayList<ContactListFilterListener>();
+ private ContactListFilter mFilter;
+
+ public ContactListFilterControllerImpl(Context context) {
+ mContext = context;
+ mFilter = ContactListFilter.restoreDefaultPreferences(getSharedPreferences());
+ checkFilterValidity(true /* notify listeners */);
+ }
+
+ @Override
+ public void addListener(ContactListFilterListener listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public void removeListener(ContactListFilterListener listener) {
+ mListeners.remove(listener);
+ }
+
+ @Override
+ public ContactListFilter getFilter() {
+ return mFilter;
+ }
+
+ private SharedPreferences getSharedPreferences() {
+ return PreferenceManager.getDefaultSharedPreferences(mContext);
+ }
+
+ @Override
+ public void setContactListFilter(ContactListFilter filter, boolean persistent) {
+ setContactListFilter(filter, persistent, true);
+ }
+
+ private void setContactListFilter(
+ ContactListFilter filter, boolean persistent, boolean notifyListeners) {
+ if (!filter.equals(mFilter)) {
+ mFilter = filter;
+ if (persistent) {
+ ContactListFilter.storeToPreferences(getSharedPreferences(), mFilter);
+ }
+ if (notifyListeners && !mListeners.isEmpty()) {
+ notifyContactListFilterChanged();
+ }
+ }
+ }
+
+ @Override
+ public void selectCustomFilter() {
+ setContactListFilter(
+ ContactListFilter.createFilterWithType(ContactListFilter.FILTER_TYPE_CUSTOM), true);
+ }
+
+ private void notifyContactListFilterChanged() {
+ for (ContactListFilterListener listener : mListeners) {
+ listener.onContactListFilterChanged();
+ }
+ }
+
+ @Override
+ public void checkFilterValidity(boolean notifyListeners) {
+ if (mFilter == null) {
+ return;
+ }
+
+ switch (mFilter.filterType) {
+ case ContactListFilter.FILTER_TYPE_SINGLE_CONTACT:
+ setContactListFilter(
+ ContactListFilter.restoreDefaultPreferences(getSharedPreferences()),
+ false,
+ notifyListeners);
+ break;
+ case ContactListFilter.FILTER_TYPE_ACCOUNT:
+ if (!filterAccountExists()) {
+ // The current account filter points to invalid account. Use "all" filter
+ // instead.
+ setContactListFilter(
+ ContactListFilter.createFilterWithType(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS),
+ true,
+ notifyListeners);
+ }
+ }
+ }
+
+ /** @return true if the Account for the current filter exists. */
+ private boolean filterAccountExists() {
+ final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(mContext);
+ final AccountWithDataSet filterAccount =
+ new AccountWithDataSet(mFilter.accountName, mFilter.accountType, mFilter.dataSet);
+ return accountTypeManager.contains(filterAccount, false);
+ }
+}
diff --git a/java/com/android/contacts/common/list/ContactListItemView.java b/java/com/android/contacts/common/list/ContactListItemView.java
new file mode 100644
index 000000000..76842483a
--- /dev/null
+++ b/java/com/android/contacts/common/list/ContactListItemView.java
@@ -0,0 +1,1513 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.database.Cursor;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.SearchSnippets;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.drawable.DrawableCompat;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.TextUtils;
+import android.text.TextUtils.TruncateAt;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView.SelectionBoundsAdjuster;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+import android.widget.QuickContactBadge;
+import android.widget.TextView;
+import com.android.contacts.common.ContactPresenceIconUtil;
+import com.android.contacts.common.ContactStatusUtil;
+import com.android.contacts.common.R;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+import com.android.contacts.common.format.TextHighlighter;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.contacts.common.util.SearchUtil;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.util.ViewUtil;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A custom view for an item in the contact list. The view contains the contact's photo, a set of
+ * text views (for name, status, etc...) and icons for presence and call. The view uses no XML file
+ * for layout and all the measurements and layouts are done in the onMeasure and onLayout methods.
+ *
+ * <p>The layout puts the contact's photo on the right side of the view, the call icon (if present)
+ * to the left of the photo, the text lines are aligned to the left and the presence icon (if
+ * present) is set to the left of the status line.
+ *
+ * <p>The layout also supports a header (used as a header of a group of contacts) that is above the
+ * contact's data and a divider between contact view.
+ */
+public class ContactListItemView extends ViewGroup implements SelectionBoundsAdjuster {
+ private static final Pattern SPLIT_PATTERN =
+ Pattern.compile("([\\w-\\.]+)@((?:[\\w]+\\.)+)([a-zA-Z]{2,4})|[\\w]+");
+ static final char SNIPPET_START_MATCH = '[';
+ static final char SNIPPET_END_MATCH = ']';
+ /** A helper used to highlight a prefix in a text field. */
+ private final TextHighlighter mTextHighlighter;
+ // Style values for layout and appearance
+ // The initialized values are defaults if none is provided through xml.
+ private int mPreferredHeight = 0;
+ private int mGapBetweenImageAndText = 0;
+ private int mGapBetweenLabelAndData = 0;
+ private int mPresenceIconMargin = 4;
+ private int mPresenceIconSize = 16;
+ private int mTextIndent = 0;
+ private int mTextOffsetTop;
+ private int mNameTextViewTextSize;
+ private int mHeaderWidth;
+ private Drawable mActivatedBackgroundDrawable;
+ private int mVideoCallIconSize = 32;
+ private int mVideoCallIconMargin = 16;
+ // Set in onLayout. Represent left and right position of the View on the screen.
+ private int mLeftOffset;
+ private int mRightOffset;
+ /** Used with {@link #mLabelView}, specifying the width ratio between label and data. */
+ private int mLabelViewWidthWeight = 3;
+ /** Used with {@link #mDataView}, specifying the width ratio between label and data. */
+ private int mDataViewWidthWeight = 5;
+
+ private ArrayList<HighlightSequence> mNameHighlightSequence;
+ private ArrayList<HighlightSequence> mNumberHighlightSequence;
+ // Highlighting prefix for names.
+ private String mHighlightedPrefix;
+ /** Used to notify listeners when a video call icon is clicked. */
+ private PhoneNumberListAdapter.Listener mPhoneNumberListAdapterListener;
+ /** Indicates whether to show the "video call" icon, used to initiate a video call. */
+ private boolean mShowVideoCallIcon = false;
+ /** Indicates whether the view should leave room for the "video call" icon. */
+ private boolean mSupportVideoCallIcon = false;
+
+ private PhotoPosition mPhotoPosition = getDefaultPhotoPosition(false /* normal/non opposite */);
+ // Header layout data
+ private TextView mHeaderTextView;
+ private boolean mIsSectionHeaderEnabled;
+ // The views inside the contact view
+ private boolean mQuickContactEnabled = true;
+ private QuickContactBadge mQuickContact;
+ private ImageView mPhotoView;
+ private TextView mNameTextView;
+ private TextView mLabelView;
+ private TextView mDataView;
+ private TextView mSnippetView;
+ private TextView mStatusView;
+ private ImageView mPresenceIcon;
+ private ImageView mVideoCallIcon;
+ private ImageView mWorkProfileIcon;
+ private ColorStateList mSecondaryTextColor;
+ private int mDefaultPhotoViewSize = 0;
+ /**
+ * Can be effective even when {@link #mPhotoView} is null, as we want to have horizontal padding
+ * to align other data in this View.
+ */
+ private int mPhotoViewWidth;
+ /**
+ * Can be effective even when {@link #mPhotoView} is null, as we want to have vertical padding.
+ */
+ private int mPhotoViewHeight;
+ /**
+ * Only effective when {@link #mPhotoView} is null. When true all the Views on the right side of
+ * the photo should have horizontal padding on those left assuming there is a photo.
+ */
+ private boolean mKeepHorizontalPaddingForPhotoView;
+ /** Only effective when {@link #mPhotoView} is null. */
+ private boolean mKeepVerticalPaddingForPhotoView;
+ /**
+ * True when {@link #mPhotoViewWidth} and {@link #mPhotoViewHeight} are ready for being used.
+ * False indicates those values should be updated before being used in position calculation.
+ */
+ private boolean mPhotoViewWidthAndHeightAreReady = false;
+
+ private int mNameTextViewHeight;
+ private int mNameTextViewTextColor = Color.BLACK;
+ private int mPhoneticNameTextViewHeight;
+ private int mLabelViewHeight;
+ private int mDataViewHeight;
+ private int mSnippetTextViewHeight;
+ private int mStatusTextViewHeight;
+ private int mCheckBoxWidth;
+ // Holds Math.max(mLabelTextViewHeight, mDataViewHeight), assuming Label and Data share the
+ // same row.
+ private int mLabelAndDataViewMaxHeight;
+ private boolean mActivatedStateSupported;
+ private boolean mAdjustSelectionBoundsEnabled = true;
+ private Rect mBoundsWithoutHeader = new Rect();
+ private CharSequence mUnknownNameText;
+ private int mPosition;
+
+ public ContactListItemView(Context context) {
+ super(context);
+
+ mTextHighlighter = new TextHighlighter(Typeface.BOLD);
+ mNameHighlightSequence = new ArrayList<HighlightSequence>();
+ mNumberHighlightSequence = new ArrayList<HighlightSequence>();
+ }
+
+ public ContactListItemView(Context context, AttributeSet attrs, boolean supportVideoCallIcon) {
+ this(context, attrs);
+
+ mSupportVideoCallIcon = supportVideoCallIcon;
+ }
+
+ public ContactListItemView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray a;
+
+ if (R.styleable.ContactListItemView != null) {
+ // Read all style values
+ a = getContext().obtainStyledAttributes(attrs, R.styleable.ContactListItemView);
+ mPreferredHeight =
+ a.getDimensionPixelSize(
+ R.styleable.ContactListItemView_list_item_height, mPreferredHeight);
+ mActivatedBackgroundDrawable =
+ a.getDrawable(R.styleable.ContactListItemView_activated_background);
+
+ mGapBetweenImageAndText =
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_gap_between_image_and_text,
+ mGapBetweenImageAndText);
+ mGapBetweenLabelAndData =
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_gap_between_label_and_data,
+ mGapBetweenLabelAndData);
+ mPresenceIconMargin =
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_presence_icon_margin, mPresenceIconMargin);
+ mPresenceIconSize =
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_presence_icon_size, mPresenceIconSize);
+ mDefaultPhotoViewSize =
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_photo_size, mDefaultPhotoViewSize);
+ mTextIndent =
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_text_indent, mTextIndent);
+ mTextOffsetTop =
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_text_offset_top, mTextOffsetTop);
+ mDataViewWidthWeight =
+ a.getInteger(
+ R.styleable.ContactListItemView_list_item_data_width_weight, mDataViewWidthWeight);
+ mLabelViewWidthWeight =
+ a.getInteger(
+ R.styleable.ContactListItemView_list_item_label_width_weight, mLabelViewWidthWeight);
+ mNameTextViewTextColor =
+ a.getColor(
+ R.styleable.ContactListItemView_list_item_name_text_color, mNameTextViewTextColor);
+ mNameTextViewTextSize =
+ (int)
+ a.getDimension(
+ R.styleable.ContactListItemView_list_item_name_text_size,
+ (int) getResources().getDimension(R.dimen.contact_browser_list_item_text_size));
+ mVideoCallIconSize =
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_video_call_icon_size, mVideoCallIconSize);
+ mVideoCallIconMargin =
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_video_call_icon_margin,
+ mVideoCallIconMargin);
+
+ setPaddingRelative(
+ a.getDimensionPixelOffset(R.styleable.ContactListItemView_list_item_padding_left, 0),
+ a.getDimensionPixelOffset(R.styleable.ContactListItemView_list_item_padding_top, 0),
+ a.getDimensionPixelOffset(R.styleable.ContactListItemView_list_item_padding_right, 0),
+ a.getDimensionPixelOffset(R.styleable.ContactListItemView_list_item_padding_bottom, 0));
+
+ a.recycle();
+ }
+
+ mTextHighlighter = new TextHighlighter(Typeface.BOLD);
+
+ if (R.styleable.Theme != null) {
+ a = getContext().obtainStyledAttributes(R.styleable.Theme);
+ mSecondaryTextColor = a.getColorStateList(R.styleable.Theme_android_textColorSecondary);
+ a.recycle();
+ }
+
+ mHeaderWidth = getResources().getDimensionPixelSize(R.dimen.contact_list_section_header_width);
+
+ if (mActivatedBackgroundDrawable != null) {
+ mActivatedBackgroundDrawable.setCallback(this);
+ }
+
+ mNameHighlightSequence = new ArrayList<HighlightSequence>();
+ mNumberHighlightSequence = new ArrayList<HighlightSequence>();
+
+ setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
+ }
+
+ public static final 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);
+ }
+ }
+
+ /**
+ * Helper method for splitting a string into tokens. The lists passed in are populated with the
+ * tokens and offsets into the content of each token. The tokenization function parses e-mail
+ * addresses as a single token; otherwise it splits on any non-alphanumeric character.
+ *
+ * @param content Content to split.
+ * @return List of token strings.
+ */
+ private static List<String> split(String content) {
+ final Matcher matcher = SPLIT_PATTERN.matcher(content);
+ final ArrayList<String> tokens = new ArrayList<>();
+ while (matcher.find()) {
+ tokens.add(matcher.group());
+ }
+ return tokens;
+ }
+
+ public void setUnknownNameText(CharSequence unknownNameText) {
+ mUnknownNameText = unknownNameText;
+ }
+
+ public void setQuickContactEnabled(boolean flag) {
+ mQuickContactEnabled = flag;
+ }
+
+ /**
+ * Sets whether the video calling icon is shown. For the video calling icon to be shown, {@link
+ * #mSupportVideoCallIcon} must be {@code true}.
+ *
+ * @param showVideoCallIcon {@code true} if the video calling icon is shown, {@code false}
+ * otherwise.
+ * @param listener Listener to notify when the video calling icon is clicked.
+ * @param position The position in the adapater of the video calling icon.
+ */
+ public void setShowVideoCallIcon(
+ boolean showVideoCallIcon, PhoneNumberListAdapter.Listener listener, int position) {
+ mShowVideoCallIcon = showVideoCallIcon;
+ mPhoneNumberListAdapterListener = listener;
+ mPosition = position;
+
+ if (mShowVideoCallIcon) {
+ if (mVideoCallIcon == null) {
+ mVideoCallIcon = new ImageView(getContext());
+ addView(mVideoCallIcon);
+ }
+ mVideoCallIcon.setContentDescription(
+ getContext().getString(R.string.description_search_video_call));
+ mVideoCallIcon.setImageResource(R.drawable.ic_search_video_call);
+ mVideoCallIcon.setScaleType(ScaleType.CENTER);
+ mVideoCallIcon.setVisibility(View.VISIBLE);
+ mVideoCallIcon.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Inform the adapter that the video calling icon was clicked.
+ if (mPhoneNumberListAdapterListener != null) {
+ mPhoneNumberListAdapterListener.onVideoCallIconClicked(mPosition);
+ }
+ }
+ });
+ } else {
+ if (mVideoCallIcon != null) {
+ mVideoCallIcon.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ /**
+ * Sets whether the view supports a video calling icon. This is independent of whether the view is
+ * actually showing an icon. Support for the video calling icon ensures that the layout leaves
+ * space for the video icon, should it be shown.
+ *
+ * @param supportVideoCallIcon {@code true} if the video call icon is supported, {@code false}
+ * otherwise.
+ */
+ public void setSupportVideoCallIcon(boolean supportVideoCallIcon) {
+ mSupportVideoCallIcon = supportVideoCallIcon;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // We will match parent's width and wrap content vertically, but make sure
+ // height is no less than listPreferredItemHeight.
+ final int specWidth = resolveSize(0, widthMeasureSpec);
+ final int preferredHeight = mPreferredHeight;
+
+ mNameTextViewHeight = 0;
+ mPhoneticNameTextViewHeight = 0;
+ mLabelViewHeight = 0;
+ mDataViewHeight = 0;
+ mLabelAndDataViewMaxHeight = 0;
+ mSnippetTextViewHeight = 0;
+ mStatusTextViewHeight = 0;
+ mCheckBoxWidth = 0;
+
+ ensurePhotoViewSize();
+
+ // Width each TextView is able to use.
+ int effectiveWidth;
+ // All the other Views will honor the photo, so available width for them may be shrunk.
+ if (mPhotoViewWidth > 0 || mKeepHorizontalPaddingForPhotoView) {
+ effectiveWidth =
+ specWidth
+ - getPaddingLeft()
+ - getPaddingRight()
+ - (mPhotoViewWidth + mGapBetweenImageAndText);
+ } else {
+ effectiveWidth = specWidth - getPaddingLeft() - getPaddingRight();
+ }
+
+ if (mIsSectionHeaderEnabled) {
+ effectiveWidth -= mHeaderWidth + mGapBetweenImageAndText;
+ }
+
+ if (mSupportVideoCallIcon) {
+ effectiveWidth -= (mVideoCallIconSize + mVideoCallIconMargin);
+ }
+
+ // 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.
+
+ if (isVisible(mNameTextView)) {
+ // Calculate width for name text - this parallels similar measurement in onLayout.
+ int nameTextWidth = effectiveWidth;
+ if (mPhotoPosition != PhotoPosition.LEFT) {
+ nameTextWidth -= mTextIndent;
+ }
+ mNameTextView.measure(
+ MeasureSpec.makeMeasureSpec(nameTextWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mNameTextViewHeight = mNameTextView.getMeasuredHeight();
+ }
+
+ // If both data (phone number/email address) and label (type like "MOBILE") are quite long,
+ // we should ellipsize both using appropriate ratio.
+ final int dataWidth;
+ final int labelWidth;
+ if (isVisible(mDataView)) {
+ if (isVisible(mLabelView)) {
+ final int totalWidth = effectiveWidth - mGapBetweenLabelAndData;
+ dataWidth =
+ ((totalWidth * mDataViewWidthWeight) / (mDataViewWidthWeight + mLabelViewWidthWeight));
+ labelWidth =
+ ((totalWidth * mLabelViewWidthWeight) / (mDataViewWidthWeight + mLabelViewWidthWeight));
+ } else {
+ dataWidth = effectiveWidth;
+ labelWidth = 0;
+ }
+ } else {
+ dataWidth = 0;
+ if (isVisible(mLabelView)) {
+ labelWidth = effectiveWidth;
+ } else {
+ labelWidth = 0;
+ }
+ }
+
+ if (isVisible(mDataView)) {
+ mDataView.measure(
+ MeasureSpec.makeMeasureSpec(dataWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mDataViewHeight = mDataView.getMeasuredHeight();
+ }
+
+ if (isVisible(mLabelView)) {
+ mLabelView.measure(
+ MeasureSpec.makeMeasureSpec(labelWidth, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mLabelViewHeight = mLabelView.getMeasuredHeight();
+ }
+ mLabelAndDataViewMaxHeight = Math.max(mLabelViewHeight, mDataViewHeight);
+
+ if (isVisible(mSnippetView)) {
+ mSnippetView.measure(
+ MeasureSpec.makeMeasureSpec(effectiveWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mSnippetTextViewHeight = mSnippetView.getMeasuredHeight();
+ }
+
+ // Status view height is the biggest of the text view and the presence icon
+ if (isVisible(mPresenceIcon)) {
+ mPresenceIcon.measure(
+ MeasureSpec.makeMeasureSpec(mPresenceIconSize, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(mPresenceIconSize, MeasureSpec.EXACTLY));
+ mStatusTextViewHeight = mPresenceIcon.getMeasuredHeight();
+ }
+
+ if (mSupportVideoCallIcon && isVisible(mVideoCallIcon)) {
+ mVideoCallIcon.measure(
+ MeasureSpec.makeMeasureSpec(mVideoCallIconSize, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(mVideoCallIconSize, MeasureSpec.EXACTLY));
+ }
+
+ if (isVisible(mWorkProfileIcon)) {
+ mWorkProfileIcon.measure(
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mNameTextViewHeight = Math.max(mNameTextViewHeight, mWorkProfileIcon.getMeasuredHeight());
+ }
+
+ if (isVisible(mStatusView)) {
+ // Presence and status are in a same row, so status will be affected by icon size.
+ final int statusWidth;
+ if (isVisible(mPresenceIcon)) {
+ statusWidth = (effectiveWidth - mPresenceIcon.getMeasuredWidth() - mPresenceIconMargin);
+ } else {
+ statusWidth = effectiveWidth;
+ }
+ mStatusView.measure(
+ MeasureSpec.makeMeasureSpec(statusWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mStatusTextViewHeight = Math.max(mStatusTextViewHeight, mStatusView.getMeasuredHeight());
+ }
+
+ // Calculate height including padding.
+ int height =
+ (mNameTextViewHeight
+ + mPhoneticNameTextViewHeight
+ + mLabelAndDataViewMaxHeight
+ + mSnippetTextViewHeight
+ + mStatusTextViewHeight);
+
+ // Make sure the height is at least as high as the photo
+ height = Math.max(height, mPhotoViewHeight + getPaddingBottom() + getPaddingTop());
+
+ // Make sure height is at least the preferred height
+ height = Math.max(height, preferredHeight);
+
+ // Measure the header if it is visible.
+ if (mHeaderTextView != null && mHeaderTextView.getVisibility() == VISIBLE) {
+ mHeaderTextView.measure(
+ MeasureSpec.makeMeasureSpec(mHeaderWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ }
+
+ setMeasuredDimension(specWidth, height);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ final int height = bottom - top;
+ final int width = right - left;
+
+ // Determine the vertical bounds by laying out the header first.
+ int topBound = 0;
+ int bottomBound = height;
+ int leftBound = getPaddingLeft();
+ int rightBound = width - getPaddingRight();
+
+ final boolean isLayoutRtl = ViewUtil.isViewLayoutRtl(this);
+
+ // Put the section header on the left side of the contact view.
+ if (mIsSectionHeaderEnabled) {
+ // Align the text view all the way left, to be consistent with Contacts.
+ if (isLayoutRtl) {
+ rightBound = width;
+ } else {
+ leftBound = 0;
+ }
+ if (mHeaderTextView != null) {
+ int headerHeight = mHeaderTextView.getMeasuredHeight();
+ int headerTopBound = (bottomBound + topBound - headerHeight) / 2 + mTextOffsetTop;
+
+ mHeaderTextView.layout(
+ isLayoutRtl ? rightBound - mHeaderWidth : leftBound,
+ headerTopBound,
+ isLayoutRtl ? rightBound : leftBound + mHeaderWidth,
+ headerTopBound + headerHeight);
+ }
+ if (isLayoutRtl) {
+ rightBound -= mHeaderWidth;
+ } else {
+ leftBound += mHeaderWidth;
+ }
+ }
+
+ mBoundsWithoutHeader.set(left + leftBound, topBound, left + rightBound, bottomBound);
+ mLeftOffset = left + leftBound;
+ mRightOffset = left + rightBound;
+ if (mIsSectionHeaderEnabled) {
+ if (isLayoutRtl) {
+ rightBound -= mGapBetweenImageAndText;
+ } else {
+ leftBound += mGapBetweenImageAndText;
+ }
+ }
+
+ if (mActivatedStateSupported && isActivated()) {
+ mActivatedBackgroundDrawable.setBounds(mBoundsWithoutHeader);
+ }
+
+ final View photoView = mQuickContact != null ? mQuickContact : mPhotoView;
+ if (mPhotoPosition == PhotoPosition.LEFT) {
+ // Photo is the left most view. All the other Views should on the right of the photo.
+ if (photoView != null) {
+ // Center the photo vertically
+ final int photoTop = topBound + (bottomBound - topBound - mPhotoViewHeight) / 2;
+ photoView.layout(
+ leftBound, photoTop, leftBound + mPhotoViewWidth, photoTop + mPhotoViewHeight);
+ leftBound += mPhotoViewWidth + mGapBetweenImageAndText;
+ } else if (mKeepHorizontalPaddingForPhotoView) {
+ // Draw nothing but keep the padding.
+ leftBound += mPhotoViewWidth + mGapBetweenImageAndText;
+ }
+ } else {
+ // Photo is the right most view. Right bound should be adjusted that way.
+ if (photoView != null) {
+ // Center the photo vertically
+ final int photoTop = topBound + (bottomBound - topBound - mPhotoViewHeight) / 2;
+ photoView.layout(
+ rightBound - mPhotoViewWidth, photoTop, rightBound, photoTop + mPhotoViewHeight);
+ rightBound -= (mPhotoViewWidth + mGapBetweenImageAndText);
+ } else if (mKeepHorizontalPaddingForPhotoView) {
+ // Draw nothing but keep the padding.
+ rightBound -= (mPhotoViewWidth + mGapBetweenImageAndText);
+ }
+
+ // Add indent between left-most padding and texts.
+ leftBound += mTextIndent;
+ }
+
+ if (mSupportVideoCallIcon) {
+ // Place the video call button at the end of the list (e.g. take into account RTL mode).
+ if (isVisible(mVideoCallIcon)) {
+ // Center the video icon vertically
+ final int videoIconTop = topBound + (bottomBound - topBound - mVideoCallIconSize) / 2;
+
+ if (!isLayoutRtl) {
+ // When photo is on left, video icon is placed on the right edge.
+ mVideoCallIcon.layout(
+ rightBound - mVideoCallIconSize,
+ videoIconTop,
+ rightBound,
+ videoIconTop + mVideoCallIconSize);
+ } else {
+ // When photo is on right, video icon is placed on the left edge.
+ mVideoCallIcon.layout(
+ leftBound,
+ videoIconTop,
+ leftBound + mVideoCallIconSize,
+ videoIconTop + mVideoCallIconSize);
+ }
+ }
+
+ if (mPhotoPosition == PhotoPosition.LEFT) {
+ rightBound -= (mVideoCallIconSize + mVideoCallIconMargin);
+ } else {
+ leftBound += mVideoCallIconSize + mVideoCallIconMargin;
+ }
+ }
+
+ // Center text vertically, then apply the top offset.
+ final int totalTextHeight =
+ mNameTextViewHeight
+ + mPhoneticNameTextViewHeight
+ + mLabelAndDataViewMaxHeight
+ + mSnippetTextViewHeight
+ + mStatusTextViewHeight;
+ int textTopBound = (bottomBound + topBound - totalTextHeight) / 2 + mTextOffsetTop;
+
+ // Work Profile icon align top
+ int workProfileIconWidth = 0;
+ if (isVisible(mWorkProfileIcon)) {
+ workProfileIconWidth = mWorkProfileIcon.getMeasuredWidth();
+ final int distanceFromEnd = mCheckBoxWidth > 0 ? mCheckBoxWidth + mGapBetweenImageAndText : 0;
+ if (mPhotoPosition == PhotoPosition.LEFT) {
+ // When photo is on left, label is placed on the right edge of the list item.
+ mWorkProfileIcon.layout(
+ rightBound - workProfileIconWidth - distanceFromEnd,
+ textTopBound,
+ rightBound - distanceFromEnd,
+ textTopBound + mNameTextViewHeight);
+ } else {
+ // When photo is on right, label is placed on the left of data view.
+ mWorkProfileIcon.layout(
+ leftBound + distanceFromEnd,
+ textTopBound,
+ leftBound + workProfileIconWidth + distanceFromEnd,
+ textTopBound + mNameTextViewHeight);
+ }
+ }
+
+ // Layout all text view and presence icon
+ // Put name TextView first
+ if (isVisible(mNameTextView)) {
+ final int distanceFromEnd =
+ workProfileIconWidth
+ + (mCheckBoxWidth > 0 ? mCheckBoxWidth + mGapBetweenImageAndText : 0);
+ if (mPhotoPosition == PhotoPosition.LEFT) {
+ mNameTextView.layout(
+ leftBound,
+ textTopBound,
+ rightBound - distanceFromEnd,
+ textTopBound + mNameTextViewHeight);
+ } else {
+ mNameTextView.layout(
+ leftBound + distanceFromEnd,
+ textTopBound,
+ rightBound,
+ textTopBound + mNameTextViewHeight);
+ }
+ }
+
+ if (isVisible(mNameTextView) || isVisible(mWorkProfileIcon)) {
+ textTopBound += mNameTextViewHeight;
+ }
+
+ // Presence and status
+ if (isLayoutRtl) {
+ int statusRightBound = rightBound;
+ if (isVisible(mPresenceIcon)) {
+ int iconWidth = mPresenceIcon.getMeasuredWidth();
+ mPresenceIcon.layout(
+ rightBound - iconWidth, textTopBound, rightBound, textTopBound + mStatusTextViewHeight);
+ statusRightBound -= (iconWidth + mPresenceIconMargin);
+ }
+
+ if (isVisible(mStatusView)) {
+ mStatusView.layout(
+ leftBound, textTopBound, statusRightBound, textTopBound + mStatusTextViewHeight);
+ }
+ } else {
+ int statusLeftBound = leftBound;
+ if (isVisible(mPresenceIcon)) {
+ int iconWidth = mPresenceIcon.getMeasuredWidth();
+ mPresenceIcon.layout(
+ leftBound, textTopBound, leftBound + iconWidth, textTopBound + mStatusTextViewHeight);
+ statusLeftBound += (iconWidth + mPresenceIconMargin);
+ }
+
+ if (isVisible(mStatusView)) {
+ mStatusView.layout(
+ statusLeftBound, textTopBound, rightBound, textTopBound + mStatusTextViewHeight);
+ }
+ }
+
+ if (isVisible(mStatusView) || isVisible(mPresenceIcon)) {
+ textTopBound += mStatusTextViewHeight;
+ }
+
+ // Rest of text views
+ int dataLeftBound = leftBound;
+
+ // Label and Data align bottom.
+ if (isVisible(mLabelView)) {
+ if (!isLayoutRtl) {
+ mLabelView.layout(
+ dataLeftBound,
+ textTopBound + mLabelAndDataViewMaxHeight - mLabelViewHeight,
+ rightBound,
+ textTopBound + mLabelAndDataViewMaxHeight);
+ dataLeftBound += mLabelView.getMeasuredWidth() + mGapBetweenLabelAndData;
+ } else {
+ dataLeftBound = leftBound + mLabelView.getMeasuredWidth();
+ mLabelView.layout(
+ rightBound - mLabelView.getMeasuredWidth(),
+ textTopBound + mLabelAndDataViewMaxHeight - mLabelViewHeight,
+ rightBound,
+ textTopBound + mLabelAndDataViewMaxHeight);
+ rightBound -= (mLabelView.getMeasuredWidth() + mGapBetweenLabelAndData);
+ }
+ }
+
+ if (isVisible(mDataView)) {
+ if (!isLayoutRtl) {
+ mDataView.layout(
+ dataLeftBound,
+ textTopBound + mLabelAndDataViewMaxHeight - mDataViewHeight,
+ rightBound,
+ textTopBound + mLabelAndDataViewMaxHeight);
+ } else {
+ mDataView.layout(
+ rightBound - mDataView.getMeasuredWidth(),
+ textTopBound + mLabelAndDataViewMaxHeight - mDataViewHeight,
+ rightBound,
+ textTopBound + mLabelAndDataViewMaxHeight);
+ }
+ }
+ if (isVisible(mLabelView) || isVisible(mDataView)) {
+ textTopBound += mLabelAndDataViewMaxHeight;
+ }
+
+ if (isVisible(mSnippetView)) {
+ mSnippetView.layout(
+ leftBound, textTopBound, rightBound, textTopBound + mSnippetTextViewHeight);
+ }
+ }
+
+ @Override
+ public void adjustListItemSelectionBounds(Rect bounds) {
+ if (mAdjustSelectionBoundsEnabled) {
+ bounds.top += mBoundsWithoutHeader.top;
+ bounds.bottom = bounds.top + mBoundsWithoutHeader.height();
+ bounds.left = mBoundsWithoutHeader.left;
+ bounds.right = mBoundsWithoutHeader.right;
+ }
+ }
+
+ protected boolean isVisible(View view) {
+ return view != null && view.getVisibility() == View.VISIBLE;
+ }
+
+ /** Extracts width and height from the style */
+ private void ensurePhotoViewSize() {
+ if (!mPhotoViewWidthAndHeightAreReady) {
+ mPhotoViewWidth = mPhotoViewHeight = getDefaultPhotoViewSize();
+ if (!mQuickContactEnabled && mPhotoView == null) {
+ if (!mKeepHorizontalPaddingForPhotoView) {
+ mPhotoViewWidth = 0;
+ }
+ if (!mKeepVerticalPaddingForPhotoView) {
+ mPhotoViewHeight = 0;
+ }
+ }
+
+ mPhotoViewWidthAndHeightAreReady = true;
+ }
+ }
+
+ protected int getDefaultPhotoViewSize() {
+ return mDefaultPhotoViewSize;
+ }
+
+ /**
+ * Gets a LayoutParam that corresponds to the default photo size.
+ *
+ * @return A new LayoutParam.
+ */
+ private LayoutParams getDefaultPhotoLayoutParams() {
+ LayoutParams params = generateDefaultLayoutParams();
+ params.width = getDefaultPhotoViewSize();
+ params.height = params.width;
+ return params;
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+ if (mActivatedStateSupported) {
+ mActivatedBackgroundDrawable.setState(getDrawableState());
+ }
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return who == mActivatedBackgroundDrawable || super.verifyDrawable(who);
+ }
+
+ @Override
+ public void jumpDrawablesToCurrentState() {
+ super.jumpDrawablesToCurrentState();
+ if (mActivatedStateSupported) {
+ mActivatedBackgroundDrawable.jumpToCurrentState();
+ }
+ }
+
+ @Override
+ public void dispatchDraw(Canvas canvas) {
+ if (mActivatedStateSupported && isActivated()) {
+ mActivatedBackgroundDrawable.draw(canvas);
+ }
+
+ super.dispatchDraw(canvas);
+ }
+
+ /** Sets section header or makes it invisible if the title is null. */
+ public void setSectionHeader(String title) {
+ if (!TextUtils.isEmpty(title)) {
+ if (mHeaderTextView == null) {
+ mHeaderTextView = new TextView(getContext());
+ mHeaderTextView.setTextAppearance(getContext(), R.style.SectionHeaderStyle);
+ mHeaderTextView.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
+ addView(mHeaderTextView);
+ }
+ setMarqueeText(mHeaderTextView, title);
+ mHeaderTextView.setVisibility(View.VISIBLE);
+ mHeaderTextView.setAllCaps(true);
+ } else if (mHeaderTextView != null) {
+ mHeaderTextView.setVisibility(View.GONE);
+ }
+ }
+
+ public void setIsSectionHeaderEnabled(boolean isSectionHeaderEnabled) {
+ mIsSectionHeaderEnabled = isSectionHeaderEnabled;
+ }
+
+ /** Returns the quick contact badge, creating it if necessary. */
+ public QuickContactBadge getQuickContact() {
+ if (!mQuickContactEnabled) {
+ throw new IllegalStateException("QuickContact is disabled for this view");
+ }
+ if (mQuickContact == null) {
+ mQuickContact = new QuickContactBadge(getContext());
+ if (CompatUtils.isLollipopCompatible()) {
+ mQuickContact.setOverlay(null);
+ }
+ mQuickContact.setLayoutParams(getDefaultPhotoLayoutParams());
+ if (mNameTextView != null) {
+ mQuickContact.setContentDescription(
+ getContext()
+ .getString(R.string.description_quick_contact_for, mNameTextView.getText()));
+ }
+
+ addView(mQuickContact);
+ mPhotoViewWidthAndHeightAreReady = false;
+ }
+ return mQuickContact;
+ }
+
+ /** Returns the photo view, creating it if necessary. */
+ public ImageView getPhotoView() {
+ if (mPhotoView == null) {
+ mPhotoView = new ImageView(getContext());
+ mPhotoView.setLayoutParams(getDefaultPhotoLayoutParams());
+ // Quick contact style used above will set a background - remove it
+ mPhotoView.setBackground(null);
+ addView(mPhotoView);
+ mPhotoViewWidthAndHeightAreReady = false;
+ }
+ return mPhotoView;
+ }
+
+ /** Removes the photo view. */
+ public void removePhotoView() {
+ removePhotoView(false, true);
+ }
+
+ /**
+ * Removes the photo view.
+ *
+ * @param keepHorizontalPadding True means data on the right side will have padding on left,
+ * pretending there is still a photo view.
+ * @param keepVerticalPadding True means the View will have some height enough for accommodating a
+ * photo view.
+ */
+ public void removePhotoView(boolean keepHorizontalPadding, boolean keepVerticalPadding) {
+ mPhotoViewWidthAndHeightAreReady = false;
+ mKeepHorizontalPaddingForPhotoView = keepHorizontalPadding;
+ mKeepVerticalPaddingForPhotoView = keepVerticalPadding;
+ if (mPhotoView != null) {
+ removeView(mPhotoView);
+ mPhotoView = null;
+ }
+ if (mQuickContact != null) {
+ removeView(mQuickContact);
+ mQuickContact = null;
+ }
+ }
+
+ /**
+ * Sets a word prefix that will be highlighted if encountered in fields like name and search
+ * snippet. This will disable the mask highlighting for names.
+ *
+ * <p>NOTE: must be all upper-case
+ */
+ public void setHighlightedPrefix(String upperCasePrefix) {
+ mHighlightedPrefix = upperCasePrefix;
+ }
+
+ /** Clears previously set highlight sequences for the view. */
+ public void clearHighlightSequences() {
+ mNameHighlightSequence.clear();
+ mNumberHighlightSequence.clear();
+ mHighlightedPrefix = null;
+ }
+
+ /**
+ * Adds a highlight sequence to the name highlighter.
+ *
+ * @param start The start position of the highlight sequence.
+ * @param end The end position of the highlight sequence.
+ */
+ public void addNameHighlightSequence(int start, int end) {
+ mNameHighlightSequence.add(new HighlightSequence(start, end));
+ }
+
+ /**
+ * Adds a highlight sequence to the number highlighter.
+ *
+ * @param start The start position of the highlight sequence.
+ * @param end The end position of the highlight sequence.
+ */
+ public void addNumberHighlightSequence(int start, int end) {
+ mNumberHighlightSequence.add(new HighlightSequence(start, end));
+ }
+
+ /** Returns the text view for the contact name, creating it if necessary. */
+ public TextView getNameTextView() {
+ if (mNameTextView == null) {
+ mNameTextView = new TextView(getContext());
+ mNameTextView.setSingleLine(true);
+ mNameTextView.setEllipsize(getTextEllipsis());
+ mNameTextView.setTextColor(mNameTextViewTextColor);
+ mNameTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mNameTextViewTextSize);
+ // Manually call setActivated() since this view may be added after the first
+ // setActivated() call toward this whole item view.
+ mNameTextView.setActivated(isActivated());
+ mNameTextView.setGravity(Gravity.CENTER_VERTICAL);
+ mNameTextView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
+ mNameTextView.setId(R.id.cliv_name_textview);
+ if (CompatUtils.isLollipopCompatible()) {
+ mNameTextView.setElegantTextHeight(false);
+ }
+ addView(mNameTextView);
+ }
+ return mNameTextView;
+ }
+
+ /** Adds or updates a text view for the data label. */
+ public void setLabel(CharSequence text) {
+ if (TextUtils.isEmpty(text)) {
+ if (mLabelView != null) {
+ mLabelView.setVisibility(View.GONE);
+ }
+ } else {
+ getLabelView();
+ setMarqueeText(mLabelView, text);
+ mLabelView.setVisibility(VISIBLE);
+ }
+ }
+
+ /** Returns the text view for the data label, creating it if necessary. */
+ public TextView getLabelView() {
+ if (mLabelView == null) {
+ mLabelView = new TextView(getContext());
+ mLabelView.setLayoutParams(
+ new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+
+ mLabelView.setSingleLine(true);
+ mLabelView.setEllipsize(getTextEllipsis());
+ mLabelView.setTextAppearance(getContext(), R.style.TextAppearanceSmall);
+ if (mPhotoPosition == PhotoPosition.LEFT) {
+ mLabelView.setAllCaps(true);
+ } else {
+ mLabelView.setTypeface(mLabelView.getTypeface(), Typeface.BOLD);
+ }
+ mLabelView.setActivated(isActivated());
+ mLabelView.setId(R.id.cliv_label_textview);
+ addView(mLabelView);
+ }
+ return mLabelView;
+ }
+
+ /**
+ * Sets phone number for a list item. This takes care of number highlighting if the highlight mask
+ * exists.
+ */
+ public void setPhoneNumber(String text) {
+ if (text == null) {
+ if (mDataView != null) {
+ mDataView.setVisibility(View.GONE);
+ }
+ } else {
+ getDataView();
+
+ // TODO: Format number using PhoneNumberUtils.formatNumber before assigning it to
+ // mDataView. Make sure that determination of the highlight sequences are done only
+ // after number formatting.
+
+ // Sets phone number texts for display after highlighting it, if applicable.
+ // CharSequence textToSet = text;
+ final SpannableString textToSet = new SpannableString(text);
+
+ if (mNumberHighlightSequence.size() != 0) {
+ final HighlightSequence highlightSequence = mNumberHighlightSequence.get(0);
+ mTextHighlighter.applyMaskingHighlight(
+ textToSet, highlightSequence.start, highlightSequence.end);
+ }
+
+ setMarqueeText(mDataView, textToSet);
+ mDataView.setVisibility(VISIBLE);
+
+ // We have a phone number as "mDataView" so make it always LTR and VIEW_START
+ mDataView.setTextDirection(View.TEXT_DIRECTION_LTR);
+ mDataView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
+ }
+ }
+
+ private void setMarqueeText(TextView textView, CharSequence text) {
+ if (getTextEllipsis() == TruncateAt.MARQUEE) {
+ // To show MARQUEE correctly (with END effect during non-active state), we need
+ // to build Spanned with MARQUEE in addition to TextView's ellipsize setting.
+ final SpannableString spannable = new SpannableString(text);
+ spannable.setSpan(
+ TruncateAt.MARQUEE, 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ textView.setText(spannable);
+ } else {
+ textView.setText(text);
+ }
+ }
+
+ /** Returns the text view for the data text, creating it if necessary. */
+ public TextView getDataView() {
+ if (mDataView == null) {
+ mDataView = new TextView(getContext());
+ mDataView.setSingleLine(true);
+ mDataView.setEllipsize(getTextEllipsis());
+ mDataView.setTextAppearance(getContext(), R.style.TextAppearanceSmall);
+ mDataView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
+ mDataView.setActivated(isActivated());
+ mDataView.setId(R.id.cliv_data_view);
+ if (CompatUtils.isLollipopCompatible()) {
+ mDataView.setElegantTextHeight(false);
+ }
+ addView(mDataView);
+ }
+ return mDataView;
+ }
+
+ /** Adds or updates a text view for the search snippet. */
+ public void setSnippet(String text) {
+ if (TextUtils.isEmpty(text)) {
+ if (mSnippetView != null) {
+ mSnippetView.setVisibility(View.GONE);
+ }
+ } else {
+ mTextHighlighter.setPrefixText(getSnippetView(), text, mHighlightedPrefix);
+ mSnippetView.setVisibility(VISIBLE);
+ if (ContactDisplayUtils.isPossiblePhoneNumber(text)) {
+ // Give the text-to-speech engine a hint that it's a phone number
+ mSnippetView.setContentDescription(PhoneNumberUtilsCompat.createTtsSpannable(text));
+ } else {
+ mSnippetView.setContentDescription(null);
+ }
+ }
+ }
+
+ /** Returns the text view for the search snippet, creating it if necessary. */
+ public TextView getSnippetView() {
+ if (mSnippetView == null) {
+ mSnippetView = new TextView(getContext());
+ mSnippetView.setSingleLine(true);
+ mSnippetView.setEllipsize(getTextEllipsis());
+ mSnippetView.setTextAppearance(getContext(), android.R.style.TextAppearance_Small);
+ mSnippetView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
+ mSnippetView.setActivated(isActivated());
+ addView(mSnippetView);
+ }
+ return mSnippetView;
+ }
+
+ /** Returns the text view for the status, creating it if necessary. */
+ public TextView getStatusView() {
+ if (mStatusView == null) {
+ mStatusView = new TextView(getContext());
+ mStatusView.setSingleLine(true);
+ mStatusView.setEllipsize(getTextEllipsis());
+ mStatusView.setTextAppearance(getContext(), android.R.style.TextAppearance_Small);
+ mStatusView.setTextColor(mSecondaryTextColor);
+ mStatusView.setActivated(isActivated());
+ mStatusView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
+ addView(mStatusView);
+ }
+ return mStatusView;
+ }
+
+ /** Adds or updates a text view for the status. */
+ public void setStatus(CharSequence text) {
+ if (TextUtils.isEmpty(text)) {
+ if (mStatusView != null) {
+ mStatusView.setVisibility(View.GONE);
+ }
+ } else {
+ getStatusView();
+ setMarqueeText(mStatusView, text);
+ mStatusView.setVisibility(VISIBLE);
+ }
+ }
+
+ /** Adds or updates the presence icon view. */
+ public void setPresence(Drawable icon) {
+ if (icon != null) {
+ if (mPresenceIcon == null) {
+ mPresenceIcon = new ImageView(getContext());
+ addView(mPresenceIcon);
+ }
+ mPresenceIcon.setImageDrawable(icon);
+ mPresenceIcon.setScaleType(ScaleType.CENTER);
+ mPresenceIcon.setVisibility(View.VISIBLE);
+ } else {
+ if (mPresenceIcon != null) {
+ mPresenceIcon.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ /**
+ * Set to display work profile icon or not
+ *
+ * @param enabled set to display work profile icon or not
+ */
+ public void setWorkProfileIconEnabled(boolean enabled) {
+ if (mWorkProfileIcon != null) {
+ mWorkProfileIcon.setVisibility(enabled ? View.VISIBLE : View.GONE);
+ } else if (enabled) {
+ mWorkProfileIcon = new ImageView(getContext());
+ addView(mWorkProfileIcon);
+ mWorkProfileIcon.setImageResource(R.drawable.ic_work_profile);
+ mWorkProfileIcon.setScaleType(ScaleType.CENTER_INSIDE);
+ mWorkProfileIcon.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private TruncateAt getTextEllipsis() {
+ return TruncateAt.MARQUEE;
+ }
+
+ public void showDisplayName(Cursor cursor, int nameColumnIndex) {
+ CharSequence name = cursor.getString(nameColumnIndex);
+ setDisplayName(name);
+
+ // Since the quick contact content description is derived from the display name and there is
+ // no guarantee that when the quick contact is initialized the display name is already set,
+ // do it here too.
+ if (mQuickContact != null) {
+ mQuickContact.setContentDescription(
+ getContext().getString(R.string.description_quick_contact_for, mNameTextView.getText()));
+ }
+ }
+
+ public void setDisplayName(CharSequence name) {
+ if (!TextUtils.isEmpty(name)) {
+ // Chooses the available highlighting method for highlighting.
+ if (mHighlightedPrefix != null) {
+ name = mTextHighlighter.applyPrefixHighlight(name, mHighlightedPrefix);
+ } else if (mNameHighlightSequence.size() != 0) {
+ final SpannableString spannableName = new SpannableString(name);
+ for (HighlightSequence highlightSequence : mNameHighlightSequence) {
+ mTextHighlighter.applyMaskingHighlight(
+ spannableName, highlightSequence.start, highlightSequence.end);
+ }
+ name = spannableName;
+ }
+ } else {
+ name = mUnknownNameText;
+ }
+ setMarqueeText(getNameTextView(), name);
+
+ if (ContactDisplayUtils.isPossiblePhoneNumber(name)) {
+ // Give the text-to-speech engine a hint that it's a phone number
+ mNameTextView.setTextDirection(View.TEXT_DIRECTION_LTR);
+ mNameTextView.setContentDescription(
+ PhoneNumberUtilsCompat.createTtsSpannable(name.toString()));
+ } else {
+ // Remove span tags of highlighting for talkback to avoid reading highlighting and rest
+ // of the name into two separate parts.
+ mNameTextView.setContentDescription(name.toString());
+ }
+ }
+
+ public void hideDisplayName() {
+ if (mNameTextView != null) {
+ removeView(mNameTextView);
+ mNameTextView = null;
+ }
+ }
+
+ /** Sets the proper icon (star or presence or nothing) and/or status message. */
+ public void showPresenceAndStatusMessage(
+ Cursor cursor, int presenceColumnIndex, int contactStatusColumnIndex) {
+ Drawable icon = null;
+ int presence = 0;
+ if (!cursor.isNull(presenceColumnIndex)) {
+ presence = cursor.getInt(presenceColumnIndex);
+ icon = ContactPresenceIconUtil.getPresenceIcon(getContext(), presence);
+ }
+ setPresence(icon);
+
+ String statusMessage = null;
+ if (contactStatusColumnIndex != 0 && !cursor.isNull(contactStatusColumnIndex)) {
+ statusMessage = cursor.getString(contactStatusColumnIndex);
+ }
+ // If there is no status message from the contact, but there was a presence value, then use
+ // the default status message string
+ if (statusMessage == null && presence != 0) {
+ statusMessage = ContactStatusUtil.getStatusString(getContext(), presence);
+ }
+ setStatus(statusMessage);
+ }
+
+ /** Shows search snippet. */
+ public void showSnippet(Cursor cursor, int summarySnippetColumnIndex) {
+ if (cursor.getColumnCount() <= summarySnippetColumnIndex
+ || !SearchSnippets.SNIPPET.equals(cursor.getColumnName(summarySnippetColumnIndex))) {
+ setSnippet(null);
+ return;
+ }
+
+ String snippet = cursor.getString(summarySnippetColumnIndex);
+
+ // Do client side snippeting if provider didn't do it
+ final Bundle extras = cursor.getExtras();
+ if (extras.getBoolean(ContactsContract.DEFERRED_SNIPPETING)) {
+
+ final String query = extras.getString(ContactsContract.DEFERRED_SNIPPETING_QUERY);
+
+ String displayName = null;
+ int displayNameIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
+ if (displayNameIndex >= 0) {
+ displayName = cursor.getString(displayNameIndex);
+ }
+
+ snippet = updateSnippet(snippet, query, displayName);
+
+ } else {
+ if (snippet != null) {
+ int from = 0;
+ int to = snippet.length();
+ int start = snippet.indexOf(SNIPPET_START_MATCH);
+ if (start == -1) {
+ snippet = null;
+ } else {
+ int firstNl = snippet.lastIndexOf('\n', start);
+ if (firstNl != -1) {
+ from = firstNl + 1;
+ }
+ int end = snippet.lastIndexOf(SNIPPET_END_MATCH);
+ if (end != -1) {
+ int lastNl = snippet.indexOf('\n', end);
+ if (lastNl != -1) {
+ to = lastNl;
+ }
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = from; i < to; i++) {
+ char c = snippet.charAt(i);
+ if (c != SNIPPET_START_MATCH && c != SNIPPET_END_MATCH) {
+ sb.append(c);
+ }
+ }
+ snippet = sb.toString();
+ }
+ }
+ }
+
+ setSnippet(snippet);
+ }
+
+ /**
+ * Used for deferred snippets from the database. The contents come back as large strings which
+ * need to be extracted for display.
+ *
+ * @param snippet The snippet from the database.
+ * @param query The search query substring.
+ * @param displayName The contact display name.
+ * @return The proper snippet to display.
+ */
+ private String updateSnippet(String snippet, String query, String displayName) {
+
+ if (TextUtils.isEmpty(snippet) || TextUtils.isEmpty(query)) {
+ return null;
+ }
+ query = SearchUtil.cleanStartAndEndOfSearchQuery(query.toLowerCase());
+
+ // If the display name already contains the query term, return empty - snippets should
+ // not be needed in that case.
+ if (!TextUtils.isEmpty(displayName)) {
+ final String lowerDisplayName = displayName.toLowerCase();
+ final List<String> nameTokens = split(lowerDisplayName);
+ for (String nameToken : nameTokens) {
+ if (nameToken.startsWith(query)) {
+ return null;
+ }
+ }
+ }
+
+ // The snippet may contain multiple data lines.
+ // Show the first line that matches the query.
+ final SearchUtil.MatchedLine matched = SearchUtil.findMatchingLine(snippet, query);
+
+ if (matched != null && matched.line != null) {
+ // Tokenize for long strings since the match may be at the end of it.
+ // Skip this part for short strings since the whole string will be displayed.
+ // Most contact strings are short so the snippetize method will be called infrequently.
+ final int lengthThreshold =
+ getResources().getInteger(R.integer.snippet_length_before_tokenize);
+ if (matched.line.length() > lengthThreshold) {
+ return snippetize(matched.line, matched.startIndex, lengthThreshold);
+ } else {
+ return matched.line;
+ }
+ }
+
+ // No match found.
+ return null;
+ }
+
+ private String snippetize(String line, int matchIndex, int maxLength) {
+ // Show up to maxLength characters. But we only show full tokens so show the last full token
+ // up to maxLength characters. So as many starting tokens as possible before trying ending
+ // tokens.
+ int remainingLength = maxLength;
+ int tempRemainingLength = remainingLength;
+
+ // Start the end token after the matched query.
+ int index = matchIndex;
+ int endTokenIndex = index;
+
+ // Find the match token first.
+ while (index < line.length()) {
+ if (!Character.isLetterOrDigit(line.charAt(index))) {
+ endTokenIndex = index;
+ remainingLength = tempRemainingLength;
+ break;
+ }
+ tempRemainingLength--;
+ index++;
+ }
+
+ // Find as much content before the match.
+ index = matchIndex - 1;
+ tempRemainingLength = remainingLength;
+ int startTokenIndex = matchIndex;
+ while (index > -1 && tempRemainingLength > 0) {
+ if (!Character.isLetterOrDigit(line.charAt(index))) {
+ startTokenIndex = index;
+ remainingLength = tempRemainingLength;
+ }
+ tempRemainingLength--;
+ index--;
+ }
+
+ index = endTokenIndex;
+ tempRemainingLength = remainingLength;
+ // Find remaining content at after match.
+ while (index < line.length() && tempRemainingLength > 0) {
+ if (!Character.isLetterOrDigit(line.charAt(index))) {
+ endTokenIndex = index;
+ }
+ tempRemainingLength--;
+ index++;
+ }
+ // Append ellipse if there is content before or after.
+ final StringBuilder sb = new StringBuilder();
+ if (startTokenIndex > 0) {
+ sb.append("...");
+ }
+ sb.append(line.substring(startTokenIndex, endTokenIndex));
+ if (endTokenIndex < line.length()) {
+ sb.append("...");
+ }
+ return sb.toString();
+ }
+
+ public void setActivatedStateSupported(boolean flag) {
+ this.mActivatedStateSupported = flag;
+ }
+
+ public void setAdjustSelectionBoundsEnabled(boolean enabled) {
+ mAdjustSelectionBoundsEnabled = enabled;
+ }
+
+ @Override
+ public void requestLayout() {
+ // We will assume that once measured this will not need to resize
+ // itself, so there is no need to pass the layout request to the parent
+ // view (ListView).
+ forceLayout();
+ }
+
+ public void setPhotoPosition(PhotoPosition photoPosition) {
+ mPhotoPosition = photoPosition;
+ }
+
+ /**
+ * Set drawable resources directly for the drawable resource of the photo view.
+ *
+ * @param drawableId Id of drawable resource.
+ */
+ public void setDrawableResource(int drawableId) {
+ ImageView photo = getPhotoView();
+ photo.setScaleType(ImageView.ScaleType.CENTER);
+ 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
+ public boolean onTouchEvent(MotionEvent event) {
+ final float x = event.getX();
+ final float y = event.getY();
+ // If the touch event's coordinates are not within the view's header, then delegate
+ // to super.onTouchEvent so that regular view behavior is preserved. Otherwise, consume
+ // and ignore the touch event.
+ if (mBoundsWithoutHeader.contains((int) x, (int) y) || !pointIsInView(x, y)) {
+ return super.onTouchEvent(event);
+ } else {
+ return true;
+ }
+ }
+
+ private final boolean pointIsInView(float localX, float localY) {
+ return localX >= mLeftOffset
+ && localX < mRightOffset
+ && localY >= 0
+ && localY < (getBottom() - getTop());
+ }
+
+ /**
+ * Where to put contact photo. This affects the other Views' layout or look-and-feel.
+ *
+ * <p>TODO: replace enum with int constants
+ */
+ public enum PhotoPosition {
+ LEFT,
+ RIGHT
+ }
+
+ protected static class HighlightSequence {
+
+ private final int start;
+ private final int end;
+
+ HighlightSequence(int start, int end) {
+ this.start = start;
+ this.end = end;
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/list/ContactListPinnedHeaderView.java b/java/com/android/contacts/common/list/ContactListPinnedHeaderView.java
new file mode 100644
index 000000000..1f3e2bfe3
--- /dev/null
+++ b/java/com/android/contacts/common/list/ContactListPinnedHeaderView.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.TextView;
+import com.android.contacts.common.R;
+
+/** A custom view for the pinned section header shown at the top of the contact list. */
+public class ContactListPinnedHeaderView extends TextView {
+
+ public ContactListPinnedHeaderView(Context context, AttributeSet attrs, View parent) {
+ super(context, attrs);
+
+ if (R.styleable.ContactListItemView == null) {
+ return;
+ }
+ TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ContactListItemView);
+ int backgroundColor =
+ a.getColor(R.styleable.ContactListItemView_list_item_background_color, Color.WHITE);
+ int textOffsetTop =
+ a.getDimensionPixelSize(R.styleable.ContactListItemView_list_item_text_offset_top, 0);
+ int paddingStartOffset =
+ a.getDimensionPixelSize(R.styleable.ContactListItemView_list_item_padding_left, 0);
+ int textWidth = getResources().getDimensionPixelSize(R.dimen.contact_list_section_header_width);
+ int widthIncludingPadding = paddingStartOffset + textWidth;
+ a.recycle();
+
+ setBackgroundColor(backgroundColor);
+ setTextAppearance(getContext(), R.style.SectionHeaderStyle);
+ setLayoutParams(new LayoutParams(textWidth, LayoutParams.WRAP_CONTENT));
+ setLayoutDirection(parent.getLayoutDirection());
+ setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
+
+ // Apply text top offset. Multiply by two, because we are implementing this by padding for a
+ // vertically centered view, rather than adjusting the position directly via a layout.
+ setPaddingRelative(
+ 0, getPaddingTop() + (textOffsetTop * 2), getPaddingEnd(), getPaddingBottom());
+ }
+
+ /** Sets section header or makes it invisible if the title is null. */
+ public void setSectionHeaderTitle(String title) {
+ if (!TextUtils.isEmpty(title)) {
+ setText(title);
+ } else {
+ setVisibility(View.GONE);
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/list/ContactTileView.java b/java/com/android/contacts/common/list/ContactTileView.java
new file mode 100644
index 000000000..9273b0583
--- /dev/null
+++ b/java/com/android/contacts/common/list/ContactTileView.java
@@ -0,0 +1,171 @@
+/*
+ * 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.list;
+
+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;
+import android.widget.TextView;
+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;
+
+/** A ContactTile displays a contact's picture and name */
+public abstract class ContactTileView extends FrameLayout {
+
+ private static final String TAG = ContactTileView.class.getSimpleName();
+ protected Listener mListener;
+ private Uri mLookupUri;
+ private ImageView mPhoto;
+ private TextView mName;
+ private ContactPhotoManager mPhotoManager = null;
+
+ public ContactTileView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mName = (TextView) findViewById(R.id.contact_tile_name);
+ mPhoto = (ImageView) findViewById(R.id.contact_tile_image);
+
+ OnClickListener listener = createClickListener();
+ setOnClickListener(listener);
+ }
+
+ protected OnClickListener createClickListener() {
+ return new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mListener == null) {
+ return;
+ }
+ mListener.onContactSelected(
+ getLookupUri(), MoreContactUtils.getTargetRectFromView(ContactTileView.this));
+ }
+ };
+ }
+
+ public void setPhotoManager(ContactPhotoManager photoManager) {
+ mPhotoManager = photoManager;
+ }
+
+ /**
+ * Populates the data members to be displayed from the fields in {@link
+ * com.android.contacts.common.list.ContactEntry}
+ */
+ public void loadFromContact(ContactEntry entry) {
+
+ if (entry != null) {
+ mName.setText(getNameForView(entry));
+ mLookupUri = entry.lookupUri;
+
+ setVisibility(View.VISIBLE);
+
+ if (mPhotoManager != null) {
+ DefaultImageRequest request = getDefaultImageRequest(entry.namePrimary, entry.lookupKey);
+ configureViewForImage(entry.photoUri == null);
+ if (mPhoto != null) {
+ mPhotoManager.loadPhoto(
+ mPhoto,
+ entry.photoUri,
+ getApproximateImageSize(),
+ isDarkTheme(),
+ isContactPhotoCircular(),
+ request);
+
+
+ }
+ } else {
+ Log.w(TAG, "contactPhotoManager not set");
+ }
+ } else {
+ setVisibility(View.INVISIBLE);
+ }
+ }
+
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ public Uri getLookupUri() {
+ return mLookupUri;
+ }
+
+ /**
+ * Returns the string that should actually be displayed as the contact's name. Subclasses can
+ * override this to return formatted versions of the name - i.e. first name only.
+ */
+ protected String getNameForView(ContactEntry contactEntry) {
+ return contactEntry.namePrimary;
+ }
+
+ /**
+ * Implemented by subclasses to estimate the size of the picture. This can return -1 if only a
+ * thumbnail is shown anyway
+ */
+ protected abstract int getApproximateImageSize();
+
+ protected abstract boolean isDarkTheme();
+
+ /**
+ * Implemented by subclasses to reconfigure the view's layout and subviews, based on whether or
+ * not the contact has a user-defined photo.
+ *
+ * @param isDefaultImage True if the contact does not have a user-defined contact photo (which
+ * means a default contact image will be applied by the {@link ContactPhotoManager}
+ */
+ protected void configureViewForImage(boolean isDefaultImage) {
+ // No-op by default.
+ }
+
+ /**
+ * Implemented by subclasses to allow them to return a {@link DefaultImageRequest} with the
+ * various image parameters defined to match their own layouts.
+ *
+ * @param displayName The display name of the contact
+ * @param lookupKey The lookup key of the contact
+ * @return A {@link DefaultImageRequest} object with each field configured by the subclass as
+ * desired, or {@code null}.
+ */
+ protected DefaultImageRequest getDefaultImageRequest(String displayName, String lookupKey) {
+ return new DefaultImageRequest(displayName, lookupKey, isContactPhotoCircular());
+ }
+
+ /**
+ * Whether contact photo should be displayed as a circular image. Implemented by subclasses so
+ * they can change which drawables to fetch.
+ */
+ protected boolean isContactPhotoCircular() {
+ return true;
+ }
+
+ public interface Listener {
+
+ /** Notification that the contact was selected; no specific action is dictated. */
+ void onContactSelected(Uri contactLookupUri, Rect viewRect);
+
+ /** Notification that the specified number is to be called. */
+ void onCallNumberDirectly(String phoneNumber);
+ }
+}
diff --git a/java/com/android/contacts/common/list/ContactsSectionIndexer.java b/java/com/android/contacts/common/list/ContactsSectionIndexer.java
new file mode 100644
index 000000000..3f0f2b7ee
--- /dev/null
+++ b/java/com/android/contacts/common/list/ContactsSectionIndexer.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.text.TextUtils;
+import android.widget.SectionIndexer;
+import java.util.Arrays;
+
+/**
+ * A section indexer that is configured with precomputed section titles and their respective counts.
+ */
+public class ContactsSectionIndexer implements SectionIndexer {
+
+ private static final String BLANK_HEADER_STRING = " ";
+ private String[] mSections;
+ private int[] mPositions;
+ private int mCount;
+
+ /**
+ * Constructor.
+ *
+ * @param sections a non-null array
+ * @param counts a non-null array of the same size as <code>sections</code>
+ */
+ public ContactsSectionIndexer(String[] sections, int[] counts) {
+ if (sections == null || counts == null) {
+ throw new NullPointerException();
+ }
+
+ if (sections.length != counts.length) {
+ throw new IllegalArgumentException(
+ "The sections and counts arrays must have the same length");
+ }
+
+ // TODO process sections/counts based on current locale and/or specific section titles
+
+ this.mSections = sections;
+ mPositions = new int[counts.length];
+ int position = 0;
+ for (int i = 0; i < counts.length; i++) {
+ if (TextUtils.isEmpty(mSections[i])) {
+ mSections[i] = BLANK_HEADER_STRING;
+ } else if (!mSections[i].equals(BLANK_HEADER_STRING)) {
+ mSections[i] = mSections[i].trim();
+ }
+
+ mPositions[i] = position;
+ position += counts[i];
+ }
+ mCount = position;
+ }
+
+ public Object[] getSections() {
+ return mSections;
+ }
+
+ public int getPositionForSection(int section) {
+ if (section < 0 || section >= mSections.length) {
+ return -1;
+ }
+
+ return mPositions[section];
+ }
+
+ public int getSectionForPosition(int position) {
+ if (position < 0 || position >= mCount) {
+ return -1;
+ }
+
+ int index = Arrays.binarySearch(mPositions, position);
+
+ /*
+ * Consider this example: section positions are 0, 3, 5; the supplied
+ * position is 4. The section corresponding to position 4 starts at
+ * position 3, so the expected return value is 1. Binary search will not
+ * find 4 in the array and thus will return -insertPosition-1, i.e. -3.
+ * To get from that number to the expected value of 1 we need to negate
+ * and subtract 2.
+ */
+ return index >= 0 ? index : -index - 2;
+ }
+
+ public void setProfileAndFavoritesHeader(String header, int numberOfItemsToAdd) {
+ if (mSections != null) {
+ // Don't do anything if the header is already set properly.
+ if (mSections.length > 0 && header.equals(mSections[0])) {
+ return;
+ }
+
+ // Since the section indexer isn't aware of the profile at the top, we need to add a
+ // special section at the top for it and shift everything else down.
+ String[] tempSections = new String[mSections.length + 1];
+ int[] tempPositions = new int[mPositions.length + 1];
+ tempSections[0] = header;
+ tempPositions[0] = 0;
+ for (int i = 1; i <= mPositions.length; i++) {
+ tempSections[i] = mSections[i - 1];
+ tempPositions[i] = mPositions[i - 1] + numberOfItemsToAdd;
+ }
+ mSections = tempSections;
+ mPositions = tempPositions;
+ mCount = mCount + numberOfItemsToAdd;
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/list/DefaultContactListAdapter.java b/java/com/android/contacts/common/list/DefaultContactListAdapter.java
new file mode 100644
index 000000000..7bcae0e0e
--- /dev/null
+++ b/java/com/android/contacts/common/list/DefaultContactListAdapter.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.preference.PreferenceManager;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.SearchSnippets;
+import android.text.TextUtils;
+import android.view.View;
+import com.android.contacts.common.compat.ContactsCompat;
+import com.android.contacts.common.preference.ContactsPreferences;
+import java.util.ArrayList;
+import java.util.List;
+
+/** A cursor adapter for the {@link ContactsContract.Contacts#CONTENT_TYPE} content type. */
+public class DefaultContactListAdapter extends ContactListAdapter {
+
+ public DefaultContactListAdapter(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void configureLoader(CursorLoader loader, long directoryId) {
+ String sortOrder = null;
+ if (isSearchMode()) {
+ String query = getQueryString();
+ if (query == null) {
+ query = "";
+ }
+ query = query.trim();
+ if (TextUtils.isEmpty(query)) {
+ // Regardless of the directory, we don't want anything returned,
+ // so let's just send a "nothing" query to the local directory.
+ loader.setUri(Contacts.CONTENT_URI);
+ loader.setProjection(getProjection(false));
+ loader.setSelection("0");
+ } else {
+ final Builder builder = ContactsCompat.getContentUri().buildUpon();
+ appendSearchParameters(builder, query, directoryId);
+ loader.setUri(builder.build());
+ loader.setProjection(getProjection(true));
+ }
+ } else {
+ final ContactListFilter filter = getFilter();
+ configureUri(loader, directoryId, filter);
+ loader.setProjection(getProjection(false));
+ configureSelection(loader, directoryId, filter);
+ }
+
+ if (getSortOrder() == ContactsPreferences.SORT_ORDER_PRIMARY) {
+ if (sortOrder == null) {
+ sortOrder = Contacts.SORT_KEY_PRIMARY;
+ } else {
+ sortOrder += ", " + Contacts.SORT_KEY_PRIMARY;
+ }
+ } else {
+ if (sortOrder == null) {
+ sortOrder = Contacts.SORT_KEY_ALTERNATIVE;
+ } else {
+ sortOrder += ", " + Contacts.SORT_KEY_ALTERNATIVE;
+ }
+ }
+ loader.setSortOrder(sortOrder);
+ }
+
+ private void appendSearchParameters(Builder builder, String query, long directoryId) {
+ builder.appendPath(query); // Builder will encode the query
+ builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId));
+ if (directoryId != Directory.DEFAULT && directoryId != Directory.LOCAL_INVISIBLE) {
+ builder.appendQueryParameter(
+ ContactsContract.LIMIT_PARAM_KEY,
+ String.valueOf(getDirectoryResultLimit(getDirectoryById(directoryId))));
+ }
+ builder.appendQueryParameter(SearchSnippets.DEFERRED_SNIPPETING_KEY, "1");
+ }
+
+ protected void configureUri(CursorLoader loader, long directoryId, ContactListFilter filter) {
+ Uri uri = Contacts.CONTENT_URI;
+
+ if (directoryId == Directory.DEFAULT && isSectionHeaderDisplayEnabled()) {
+ uri = ContactListAdapter.buildSectionIndexerUri(uri);
+ }
+
+ // The "All accounts" filter is the same as the entire contents of Directory.DEFAULT
+ if (filter != null
+ && filter.filterType != ContactListFilter.FILTER_TYPE_CUSTOM
+ && filter.filterType != ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
+ final Uri.Builder builder = uri.buildUpon();
+ builder.appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT));
+ if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+ filter.addAccountQueryParameterToUrl(builder);
+ }
+ uri = builder.build();
+ }
+
+ loader.setUri(uri);
+ }
+
+ private void configureSelection(CursorLoader loader, long directoryId, ContactListFilter filter) {
+ if (filter == null) {
+ return;
+ }
+
+ if (directoryId != Directory.DEFAULT) {
+ return;
+ }
+
+ StringBuilder selection = new StringBuilder();
+ List<String> selectionArgs = new ArrayList<String>();
+
+ switch (filter.filterType) {
+ case ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS:
+ {
+ // We have already added directory=0 to the URI, which takes care of this
+ // filter
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_SINGLE_CONTACT:
+ {
+ // We have already added the lookup key to the URI, which takes care of this
+ // filter
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_STARRED:
+ {
+ selection.append(Contacts.STARRED + "!=0");
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY:
+ {
+ selection.append(Contacts.HAS_PHONE_NUMBER + "=1");
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_CUSTOM:
+ {
+ selection.append(Contacts.IN_VISIBLE_GROUP + "=1");
+ if (isCustomFilterForPhoneNumbersOnly()) {
+ selection.append(" AND " + Contacts.HAS_PHONE_NUMBER + "=1");
+ }
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_ACCOUNT:
+ {
+ // We use query parameters for account filter, so no selection to add here.
+ break;
+ }
+ }
+ loader.setSelection(selection.toString());
+ loader.setSelectionArgs(selectionArgs.toArray(new String[0]));
+ }
+
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ super.bindView(itemView, partition, cursor, position);
+ final ContactListItemView view = (ContactListItemView) itemView;
+
+ view.setHighlightedPrefix(isSearchMode() ? getUpperCaseQueryString() : null);
+
+ bindSectionHeaderAndDivider(view, position, cursor);
+
+ if (isQuickContactEnabled()) {
+ bindQuickContact(
+ view,
+ partition,
+ cursor,
+ ContactQuery.CONTACT_PHOTO_ID,
+ ContactQuery.CONTACT_PHOTO_URI,
+ ContactQuery.CONTACT_ID,
+ ContactQuery.CONTACT_LOOKUP_KEY,
+ ContactQuery.CONTACT_DISPLAY_NAME);
+ } else {
+ if (getDisplayPhotos()) {
+ bindPhoto(view, partition, cursor);
+ }
+ }
+
+ bindNameAndViewId(view, cursor);
+ bindPresenceAndStatusMessage(view, cursor);
+
+ if (isSearchMode()) {
+ bindSearchSnippet(view, cursor);
+ } else {
+ view.setSnippet(null);
+ }
+ }
+
+ private boolean isCustomFilterForPhoneNumbersOnly() {
+ // TODO: this flag should not be stored in shared prefs. It needs to be in the db.
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ return prefs.getBoolean(
+ ContactsPreferences.PREF_DISPLAY_ONLY_PHONES,
+ ContactsPreferences.PREF_DISPLAY_ONLY_PHONES_DEFAULT);
+ }
+}
diff --git a/java/com/android/contacts/common/list/DirectoryListLoader.java b/java/com/android/contacts/common/list/DirectoryListLoader.java
new file mode 100644
index 000000000..48b098c07
--- /dev/null
+++ b/java/com/android/contacts/common/list/DirectoryListLoader.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.ContactsContract.Directory;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.contacts.common.R;
+import com.android.contacts.common.compat.DirectoryCompat;
+
+/** A specialized loader for the list of directories, see {@link Directory}. */
+public class DirectoryListLoader extends AsyncTaskLoader<Cursor> {
+
+ public static final int SEARCH_MODE_NONE = 0;
+ public static final int SEARCH_MODE_DEFAULT = 1;
+ public static final int SEARCH_MODE_CONTACT_SHORTCUT = 2;
+ public static final int SEARCH_MODE_DATA_SHORTCUT = 3;
+ // This is a virtual column created for a MatrixCursor.
+ public static final String DIRECTORY_TYPE = "directoryType";
+ private static final String TAG = "ContactEntryListAdapter";
+ private static final String[] RESULT_PROJECTION = {
+ Directory._ID, DIRECTORY_TYPE, Directory.DISPLAY_NAME, Directory.PHOTO_SUPPORT,
+ };
+ private final ContentObserver mObserver =
+ new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ forceLoad();
+ }
+ };
+ private int mDirectorySearchMode;
+ private boolean mLocalInvisibleDirectoryEnabled;
+ private MatrixCursor mDefaultDirectoryList;
+
+ public DirectoryListLoader(Context context) {
+ super(context);
+ }
+
+ public void setDirectorySearchMode(int mode) {
+ mDirectorySearchMode = mode;
+ }
+
+ /**
+ * A flag that indicates whether the {@link Directory#LOCAL_INVISIBLE} directory should be
+ * included in the results.
+ */
+ public void setLocalInvisibleDirectoryEnabled(boolean flag) {
+ this.mLocalInvisibleDirectoryEnabled = flag;
+ }
+
+ @Override
+ protected void onStartLoading() {
+ getContext().getContentResolver().registerContentObserver(DirectoryQuery.URI, false, mObserver);
+ forceLoad();
+ }
+
+ @Override
+ protected void onStopLoading() {
+ getContext().getContentResolver().unregisterContentObserver(mObserver);
+ }
+
+ @Override
+ public Cursor loadInBackground() {
+ if (mDirectorySearchMode == SEARCH_MODE_NONE) {
+ return getDefaultDirectories();
+ }
+
+ MatrixCursor result = new MatrixCursor(RESULT_PROJECTION);
+ Context context = getContext();
+ PackageManager pm = context.getPackageManager();
+ String selection;
+ switch (mDirectorySearchMode) {
+ case SEARCH_MODE_DEFAULT:
+ selection = null;
+ break;
+
+ case SEARCH_MODE_CONTACT_SHORTCUT:
+ selection = Directory.SHORTCUT_SUPPORT + "=" + Directory.SHORTCUT_SUPPORT_FULL;
+ break;
+
+ case SEARCH_MODE_DATA_SHORTCUT:
+ selection =
+ Directory.SHORTCUT_SUPPORT
+ + " IN ("
+ + Directory.SHORTCUT_SUPPORT_FULL
+ + ", "
+ + Directory.SHORTCUT_SUPPORT_DATA_ITEMS_ONLY
+ + ")";
+ break;
+
+ default:
+ throw new RuntimeException("Unsupported directory search mode: " + mDirectorySearchMode);
+ }
+ Cursor cursor = null;
+ try {
+ cursor =
+ context
+ .getContentResolver()
+ .query(
+ DirectoryQuery.URI,
+ DirectoryQuery.PROJECTION,
+ selection,
+ null,
+ DirectoryQuery.ORDER_BY);
+
+ if (cursor == null) {
+ return result;
+ }
+
+ while (cursor.moveToNext()) {
+ long directoryId = cursor.getLong(DirectoryQuery.ID);
+ if (!mLocalInvisibleDirectoryEnabled && DirectoryCompat.isInvisibleDirectory(directoryId)) {
+ continue;
+ }
+ String directoryType = null;
+
+ String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
+ int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
+ if (!TextUtils.isEmpty(packageName) && typeResourceId != 0) {
+ try {
+ directoryType = pm.getResourcesForApplication(packageName).getString(typeResourceId);
+ } catch (Exception e) {
+ Log.e(TAG, "Cannot obtain directory type from package: " + packageName);
+ }
+ }
+ String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
+ int photoSupport = cursor.getInt(DirectoryQuery.PHOTO_SUPPORT);
+ result.addRow(new Object[] {directoryId, directoryType, displayName, photoSupport});
+ }
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Runtime Exception when querying directory");
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ return result;
+ }
+
+ private Cursor getDefaultDirectories() {
+ if (mDefaultDirectoryList == null) {
+ mDefaultDirectoryList = new MatrixCursor(RESULT_PROJECTION);
+ mDefaultDirectoryList.addRow(
+ new Object[] {Directory.DEFAULT, getContext().getString(R.string.contactsList), null});
+ mDefaultDirectoryList.addRow(
+ new Object[] {
+ Directory.LOCAL_INVISIBLE,
+ getContext().getString(R.string.local_invisible_directory),
+ null
+ });
+ }
+ return mDefaultDirectoryList;
+ }
+
+ @Override
+ protected void onReset() {
+ stopLoading();
+ }
+
+ private static final class DirectoryQuery {
+
+ public static final Uri URI = DirectoryCompat.getContentUri();
+ public static final String ORDER_BY = Directory._ID;
+
+ public static final String[] PROJECTION = {
+ Directory._ID,
+ Directory.PACKAGE_NAME,
+ Directory.TYPE_RESOURCE_ID,
+ Directory.DISPLAY_NAME,
+ Directory.PHOTO_SUPPORT,
+ };
+
+ public static final int ID = 0;
+ public static final int PACKAGE_NAME = 1;
+ public static final int TYPE_RESOURCE_ID = 2;
+ public static final int DISPLAY_NAME = 3;
+ public static final int PHOTO_SUPPORT = 4;
+ }
+}
diff --git a/java/com/android/contacts/common/list/DirectoryPartition.java b/java/com/android/contacts/common/list/DirectoryPartition.java
new file mode 100644
index 000000000..26b851041
--- /dev/null
+++ b/java/com/android/contacts/common/list/DirectoryPartition.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.provider.ContactsContract.Directory;
+import com.android.common.widget.CompositeCursorAdapter;
+
+/** Model object for a {@link Directory} row. */
+public final class DirectoryPartition extends CompositeCursorAdapter.Partition {
+
+ public static final int STATUS_NOT_LOADED = 0;
+ public static final int STATUS_LOADING = 1;
+ public static final int STATUS_LOADED = 2;
+
+ public static final int RESULT_LIMIT_DEFAULT = -1;
+
+ private long mDirectoryId;
+ private String mContentUri;
+ private String mDirectoryType;
+ private String mDisplayName;
+ private int mStatus;
+ private boolean mPriorityDirectory;
+ private boolean mPhotoSupported;
+ private int mResultLimit = RESULT_LIMIT_DEFAULT;
+ private boolean mDisplayNumber = true;
+
+ private String mLabel;
+
+ public DirectoryPartition(boolean showIfEmpty, boolean hasHeader) {
+ super(showIfEmpty, hasHeader);
+ }
+
+ /** Directory ID, see {@link Directory}. */
+ public long getDirectoryId() {
+ return mDirectoryId;
+ }
+
+ public void setDirectoryId(long directoryId) {
+ this.mDirectoryId = directoryId;
+ }
+
+ /**
+ * Directory type resolved from {@link Directory#PACKAGE_NAME} and {@link
+ * Directory#TYPE_RESOURCE_ID};
+ */
+ public String getDirectoryType() {
+ return mDirectoryType;
+ }
+
+ public void setDirectoryType(String directoryType) {
+ this.mDirectoryType = directoryType;
+ }
+
+ /** See {@link Directory#DISPLAY_NAME}. */
+ public String getDisplayName() {
+ return mDisplayName;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.mDisplayName = displayName;
+ }
+
+ public int getStatus() {
+ return mStatus;
+ }
+
+ public void setStatus(int status) {
+ mStatus = status;
+ }
+
+ public boolean isLoading() {
+ return mStatus == STATUS_NOT_LOADED || mStatus == STATUS_LOADING;
+ }
+
+ /** Returns true if this directory should be loaded before non-priority directories. */
+ public boolean isPriorityDirectory() {
+ return mPriorityDirectory;
+ }
+
+ public void setPriorityDirectory(boolean priorityDirectory) {
+ mPriorityDirectory = priorityDirectory;
+ }
+
+ /** Returns true if this directory supports photos. */
+ public boolean isPhotoSupported() {
+ return mPhotoSupported;
+ }
+
+ public void setPhotoSupported(boolean flag) {
+ this.mPhotoSupported = flag;
+ }
+
+ /**
+ * Max number of results for this directory. Defaults to {@link #RESULT_LIMIT_DEFAULT} which
+ * implies using the adapter's {@link
+ * com.android.contacts.common.list.ContactListAdapter#getDirectoryResultLimit()}
+ */
+ public int getResultLimit() {
+ return mResultLimit;
+ }
+
+ public void setResultLimit(int resultLimit) {
+ mResultLimit = resultLimit;
+ }
+
+ /**
+ * Used by extended directories to specify a custom content URI. Extended directories MUST have a
+ * content URI
+ */
+ public String getContentUri() {
+ return mContentUri;
+ }
+
+ public void setContentUri(String contentUri) {
+ mContentUri = contentUri;
+ }
+
+ /** A label to display in the header next to the display name. */
+ public String getLabel() {
+ return mLabel;
+ }
+
+ public void setLabel(String label) {
+ mLabel = label;
+ }
+
+ @Override
+ public String toString() {
+ return "DirectoryPartition{"
+ + "mDirectoryId="
+ + mDirectoryId
+ + ", mContentUri='"
+ + mContentUri
+ + '\''
+ + ", mDirectoryType='"
+ + mDirectoryType
+ + '\''
+ + ", mDisplayName='"
+ + mDisplayName
+ + '\''
+ + ", mStatus="
+ + mStatus
+ + ", mPriorityDirectory="
+ + mPriorityDirectory
+ + ", mPhotoSupported="
+ + mPhotoSupported
+ + ", mResultLimit="
+ + mResultLimit
+ + ", mLabel='"
+ + mLabel
+ + '\''
+ + '}';
+ }
+
+ /**
+ * Whether or not to display the phone number in app that have that option - Dialer. If false,
+ * Phone Label should be used instead of Phone Number.
+ */
+ public boolean isDisplayNumber() {
+ return mDisplayNumber;
+ }
+
+ public void setDisplayNumber(boolean displayNumber) {
+ mDisplayNumber = displayNumber;
+ }
+}
diff --git a/java/com/android/contacts/common/list/IndexerListAdapter.java b/java/com/android/contacts/common/list/IndexerListAdapter.java
new file mode 100644
index 000000000..2289f6e59
--- /dev/null
+++ b/java/com/android/contacts/common/list/IndexerListAdapter.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+
+/** A list adapter that supports section indexer and a pinned header. */
+public abstract class IndexerListAdapter extends PinnedHeaderListAdapter implements SectionIndexer {
+
+ protected Context mContext;
+ private SectionIndexer mIndexer;
+ private int mIndexedPartition = 0;
+ private boolean mSectionHeaderDisplayEnabled;
+ private View mHeader;
+ private Placement mPlacementCache = new Placement();
+
+ /** Constructor. */
+ public IndexerListAdapter(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ /**
+ * Creates a section header view that will be pinned at the top of the list as the user scrolls.
+ */
+ protected abstract View createPinnedSectionHeaderView(Context context, ViewGroup parent);
+
+ /** Sets the title in the pinned header as the user scrolls. */
+ protected abstract void setPinnedSectionTitle(View pinnedHeaderView, String title);
+
+ public boolean isSectionHeaderDisplayEnabled() {
+ return mSectionHeaderDisplayEnabled;
+ }
+
+ public void setSectionHeaderDisplayEnabled(boolean flag) {
+ this.mSectionHeaderDisplayEnabled = flag;
+ }
+
+ public int getIndexedPartition() {
+ return mIndexedPartition;
+ }
+
+ public void setIndexedPartition(int partition) {
+ this.mIndexedPartition = partition;
+ }
+
+ public SectionIndexer getIndexer() {
+ return mIndexer;
+ }
+
+ public void setIndexer(SectionIndexer indexer) {
+ mIndexer = indexer;
+ mPlacementCache.invalidate();
+ }
+
+ public Object[] getSections() {
+ if (mIndexer == null) {
+ return new String[] {" "};
+ } else {
+ return mIndexer.getSections();
+ }
+ }
+
+ /** @return relative position of the section in the indexed partition */
+ public int getPositionForSection(int sectionIndex) {
+ if (mIndexer == null) {
+ return -1;
+ }
+
+ return mIndexer.getPositionForSection(sectionIndex);
+ }
+
+ /** @param position relative position in the indexed partition */
+ public int getSectionForPosition(int position) {
+ if (mIndexer == null) {
+ return -1;
+ }
+
+ return mIndexer.getSectionForPosition(position);
+ }
+
+ @Override
+ public int getPinnedHeaderCount() {
+ if (isSectionHeaderDisplayEnabled()) {
+ return super.getPinnedHeaderCount() + 1;
+ } else {
+ return super.getPinnedHeaderCount();
+ }
+ }
+
+ @Override
+ public View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent) {
+ if (isSectionHeaderDisplayEnabled() && viewIndex == getPinnedHeaderCount() - 1) {
+ if (mHeader == null) {
+ mHeader = createPinnedSectionHeaderView(mContext, parent);
+ }
+ return mHeader;
+ } else {
+ return super.getPinnedHeaderView(viewIndex, convertView, parent);
+ }
+ }
+
+ @Override
+ public void configurePinnedHeaders(PinnedHeaderListView listView) {
+ super.configurePinnedHeaders(listView);
+
+ if (!isSectionHeaderDisplayEnabled()) {
+ return;
+ }
+
+ int index = getPinnedHeaderCount() - 1;
+ if (mIndexer == null || getCount() == 0) {
+ listView.setHeaderInvisible(index, false);
+ } else {
+ int listPosition = listView.getPositionAt(listView.getTotalTopPinnedHeaderHeight());
+ int position = listPosition - listView.getHeaderViewsCount();
+
+ int section = -1;
+ int partition = getPartitionForPosition(position);
+ if (partition == mIndexedPartition) {
+ int offset = getOffsetInPartition(position);
+ if (offset != -1) {
+ section = getSectionForPosition(offset);
+ }
+ }
+
+ if (section == -1) {
+ listView.setHeaderInvisible(index, false);
+ } else {
+ View topChild = listView.getChildAt(listPosition);
+ if (topChild != null) {
+ // Match the pinned header's height to the height of the list item.
+ mHeader.setMinimumHeight(topChild.getMeasuredHeight());
+ }
+ setPinnedSectionTitle(mHeader, (String) mIndexer.getSections()[section]);
+
+ // Compute the item position where the current partition begins
+ int partitionStart = getPositionForPartition(mIndexedPartition);
+ if (hasHeader(mIndexedPartition)) {
+ partitionStart++;
+ }
+
+ // Compute the item position where the next section begins
+ int nextSectionPosition = partitionStart + getPositionForSection(section + 1);
+ boolean isLastInSection = position == nextSectionPosition - 1;
+ listView.setFadingHeader(index, listPosition, isLastInSection);
+ }
+ }
+ }
+
+ /**
+ * Computes the item's placement within its section and populates the {@code placement} object
+ * accordingly. Please note that the returned object is volatile and should be copied if the
+ * result needs to be used later.
+ */
+ public Placement getItemPlacementInSection(int position) {
+ if (mPlacementCache.position == position) {
+ return mPlacementCache;
+ }
+
+ mPlacementCache.position = position;
+ if (isSectionHeaderDisplayEnabled()) {
+ int section = getSectionForPosition(position);
+ if (section != -1 && getPositionForSection(section) == position) {
+ mPlacementCache.firstInSection = true;
+ mPlacementCache.sectionHeader = (String) getSections()[section];
+ } else {
+ mPlacementCache.firstInSection = false;
+ mPlacementCache.sectionHeader = null;
+ }
+
+ mPlacementCache.lastInSection = (getPositionForSection(section + 1) - 1 == position);
+ } else {
+ mPlacementCache.firstInSection = false;
+ mPlacementCache.lastInSection = false;
+ mPlacementCache.sectionHeader = null;
+ }
+ return mPlacementCache;
+ }
+
+ /**
+ * An item view is displayed differently depending on whether it is placed at the beginning,
+ * middle or end of a section. It also needs to know the section header when it is at the
+ * beginning of a section. This object captures all this configuration.
+ */
+ public static final class Placement {
+
+ public boolean firstInSection;
+ public boolean lastInSection;
+ public String sectionHeader;
+ private int position = ListView.INVALID_POSITION;
+
+ public void invalidate() {
+ position = ListView.INVALID_POSITION;
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/list/OnPhoneNumberPickerActionListener.java b/java/com/android/contacts/common/list/OnPhoneNumberPickerActionListener.java
new file mode 100644
index 000000000..89bd889e6
--- /dev/null
+++ b/java/com/android/contacts/common/list/OnPhoneNumberPickerActionListener.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.app.ActionBar;
+import android.net.Uri;
+import com.android.dialer.callintent.nano.CallSpecificAppData;
+
+/** Action callbacks that can be sent by a phone number picker. */
+public interface OnPhoneNumberPickerActionListener {
+
+ int CALL_INITIATION_UNKNOWN = 0;
+
+ /** Returns the selected phone number uri to the requester. */
+ void onPickDataUri(Uri dataUri, boolean isVideoCall, CallSpecificAppData callSpecificAppData);
+
+ /**
+ * Returns the specified phone number to the requester. May call the specified phone number,
+ * either as an audio or video call.
+ */
+ void onPickPhoneNumber(
+ String phoneNumber, boolean isVideoCall, CallSpecificAppData callSpecificAppData);
+
+ /** Called when home menu in {@link ActionBar} is clicked by the user. */
+ void onHomeInActionBarSelected();
+}
diff --git a/java/com/android/contacts/common/list/PhoneNumberListAdapter.java b/java/com/android/contacts/common/list/PhoneNumberListAdapter.java
new file mode 100644
index 000000000..c7b24229f
--- /dev/null
+++ b/java/com/android/contacts/common/list/PhoneNumberListAdapter.java
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Callable;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Directory;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.ContactsUtils;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.R;
+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.preference.ContactsPreferences;
+import com.android.contacts.common.util.Constants;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.util.CallUtil;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A cursor adapter for the {@link Phone#CONTENT_ITEM_TYPE} and {@link
+ * SipAddress#CONTENT_ITEM_TYPE}.
+ *
+ * <p>By default this adapter just handles phone numbers. When {@link #setUseCallableUri(boolean)}
+ * is called with "true", this adapter starts handling SIP addresses too, by using {@link Callable}
+ * API instead of {@link Phone}.
+ */
+public class PhoneNumberListAdapter extends ContactEntryListAdapter {
+
+ private static final String TAG = PhoneNumberListAdapter.class.getSimpleName();
+ private static final String IGNORE_NUMBER_TOO_LONG_CLAUSE = "length(" + Phone.NUMBER + ") < 1000";
+ // A list of extended directories to add to the directories from the database
+ private final List<DirectoryPartition> mExtendedDirectories;
+ private final CharSequence mUnknownNameText;
+ // 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);
+ setDefaultFilterHeaderText(R.string.list_filter_phones);
+ mUnknownNameText = context.getText(android.R.string.unknownName);
+
+ mExtendedDirectories =
+ PhoneDirectoryExtenderAccessor.get(mContext).getExtendedDirectories(mContext);
+
+ int videoCapabilities = CallUtil.getVideoCallingAvailability(context);
+ mIsVideoEnabled = (videoCapabilities & CallUtil.VIDEO_CALLING_ENABLED) != 0;
+ mIsPresenceEnabled = (videoCapabilities & CallUtil.VIDEO_CALLING_PRESENCE) != 0;
+ }
+
+ @Override
+ public void configureLoader(CursorLoader loader, long directoryId) {
+ String query = getQueryString();
+ if (query == null) {
+ query = "";
+ }
+ if (isExtendedDirectory(directoryId)) {
+ final DirectoryPartition directory = getExtendedDirectoryFromId(directoryId);
+ final String contentUri = directory.getContentUri();
+ if (contentUri == null) {
+ throw new IllegalStateException("Extended directory must have a content URL: " + directory);
+ }
+ final Builder builder = Uri.parse(contentUri).buildUpon();
+ builder.appendPath(query);
+ builder.appendQueryParameter(
+ ContactsContract.LIMIT_PARAM_KEY, String.valueOf(getDirectoryResultLimit(directory)));
+ loader.setUri(builder.build());
+ loader.setProjection(PhoneQuery.PROJECTION_PRIMARY);
+ } else {
+ final boolean isRemoteDirectoryQuery = DirectoryCompat.isRemoteDirectoryId(directoryId);
+ final Builder builder;
+ if (isSearchMode()) {
+ final Uri baseUri;
+ if (isRemoteDirectoryQuery) {
+ baseUri = PhoneCompat.getContentFilterUri();
+ } else if (mUseCallableUri) {
+ baseUri = CallableCompat.getContentFilterUri();
+ } else {
+ baseUri = PhoneCompat.getContentFilterUri();
+ }
+ builder = baseUri.buildUpon();
+ builder.appendPath(query); // Builder will encode the query
+ builder.appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId));
+ if (isRemoteDirectoryQuery) {
+ builder.appendQueryParameter(
+ ContactsContract.LIMIT_PARAM_KEY,
+ String.valueOf(getDirectoryResultLimit(getDirectoryById(directoryId))));
+ }
+ } else {
+ Uri baseUri = mUseCallableUri ? Callable.CONTENT_URI : Phone.CONTENT_URI;
+ builder =
+ baseUri
+ .buildUpon()
+ .appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT));
+ if (isSectionHeaderDisplayEnabled()) {
+ builder.appendQueryParameter(Phone.EXTRA_ADDRESS_BOOK_INDEX, "true");
+ }
+ applyFilter(loader, builder, directoryId, getFilter());
+ }
+
+ // Ignore invalid phone numbers that are too long. These can potentially cause freezes
+ // in the UI and there is no reason to display them.
+ final String prevSelection = loader.getSelection();
+ final String newSelection;
+ if (!TextUtils.isEmpty(prevSelection)) {
+ newSelection = prevSelection + " AND " + IGNORE_NUMBER_TOO_LONG_CLAUSE;
+ } else {
+ newSelection = IGNORE_NUMBER_TOO_LONG_CLAUSE;
+ }
+ loader.setSelection(newSelection);
+
+ // Remove duplicates when it is possible.
+ builder.appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true");
+ loader.setUri(builder.build());
+
+ // TODO a projection that includes the search snippet
+ if (getContactNameDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_PRIMARY) {
+ loader.setProjection(PhoneQuery.PROJECTION_PRIMARY);
+ } else {
+ loader.setProjection(PhoneQuery.PROJECTION_ALTERNATIVE);
+ }
+
+ if (getSortOrder() == ContactsPreferences.SORT_ORDER_PRIMARY) {
+ loader.setSortOrder(Phone.SORT_KEY_PRIMARY);
+ } else {
+ loader.setSortOrder(Phone.SORT_KEY_ALTERNATIVE);
+ }
+ }
+ }
+
+ protected boolean isExtendedDirectory(long directoryId) {
+ return directoryId >= mFirstExtendedDirectoryId;
+ }
+
+ private DirectoryPartition getExtendedDirectoryFromId(long directoryId) {
+ final int directoryIndex = (int) (directoryId - mFirstExtendedDirectoryId);
+ return mExtendedDirectories.get(directoryIndex);
+ }
+
+ /**
+ * Configure {@code loader} and {@code uriBuilder} according to {@code directoryId} and {@code
+ * filter}.
+ */
+ private void applyFilter(
+ CursorLoader loader, Uri.Builder uriBuilder, long directoryId, ContactListFilter filter) {
+ if (filter == null || directoryId != Directory.DEFAULT) {
+ return;
+ }
+
+ final StringBuilder selection = new StringBuilder();
+ final List<String> selectionArgs = new ArrayList<String>();
+
+ switch (filter.filterType) {
+ case ContactListFilter.FILTER_TYPE_CUSTOM:
+ {
+ selection.append(Contacts.IN_VISIBLE_GROUP + "=1");
+ selection.append(" AND " + Contacts.HAS_PHONE_NUMBER + "=1");
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_ACCOUNT:
+ {
+ filter.addAccountQueryParameterToUrl(uriBuilder);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS:
+ case ContactListFilter.FILTER_TYPE_DEFAULT:
+ break; // No selection needed.
+ case ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY:
+ break; // This adapter is always "phone only", so no selection needed either.
+ default:
+ Log.w(
+ TAG,
+ "Unsupported filter type came "
+ + "(type: "
+ + filter.filterType
+ + ", toString: "
+ + filter
+ + ")"
+ + " showing all contacts.");
+ // No selection.
+ break;
+ }
+ loader.setSelection(selection.toString());
+ loader.setSelectionArgs(selectionArgs.toArray(new String[0]));
+ }
+
+ public String getPhoneNumber(int position) {
+ final Cursor item = (Cursor) getItem(position);
+ return item != null ? item.getString(PhoneQuery.PHONE_NUMBER) : null;
+ }
+
+ /**
+ * Retrieves the lookup key for the given cursor position.
+ *
+ * @param position The cursor position.
+ * @return The lookup key.
+ */
+ public String getLookupKey(int position) {
+ final Cursor item = (Cursor) getItem(position);
+ return item != null ? item.getString(PhoneQuery.LOOKUP_KEY) : null;
+ }
+
+ @Override
+ protected ContactListItemView newView(
+ Context context, int partition, Cursor cursor, int position, ViewGroup parent) {
+ ContactListItemView view = super.newView(context, partition, cursor, position, parent);
+ view.setUnknownNameText(mUnknownNameText);
+ view.setQuickContactEnabled(isQuickContactEnabled());
+ view.setPhotoPosition(mPhotoPosition);
+ return view;
+ }
+
+ protected void setHighlight(ContactListItemView view, Cursor cursor) {
+ view.setHighlightedPrefix(isSearchMode() ? getUpperCaseQueryString() : null);
+ }
+
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ super.bindView(itemView, partition, cursor, position);
+ ContactListItemView view = (ContactListItemView) itemView;
+
+ setHighlight(view, cursor);
+
+ // Look at elements before and after this position, checking if contact IDs are same.
+ // If they have one same contact ID, it means they can be grouped.
+ //
+ // In one group, only the first entry will show its photo and its name, and the other
+ // entries in the group show just their data (e.g. phone number, email address).
+ cursor.moveToPosition(position);
+ boolean isFirstEntry = true;
+ final long currentContactId = cursor.getLong(PhoneQuery.CONTACT_ID);
+ if (cursor.moveToPrevious() && !cursor.isBeforeFirst()) {
+ final long previousContactId = cursor.getLong(PhoneQuery.CONTACT_ID);
+ if (currentContactId == previousContactId) {
+ isFirstEntry = false;
+ }
+ }
+ cursor.moveToPosition(position);
+
+ bindViewId(view, cursor, PhoneQuery.PHONE_ID);
+
+ bindSectionHeaderAndDivider(view, position);
+ if (isFirstEntry) {
+ bindName(view, cursor);
+ if (isQuickContactEnabled()) {
+ bindQuickContact(
+ view,
+ partition,
+ cursor,
+ PhoneQuery.PHOTO_ID,
+ PhoneQuery.PHOTO_URI,
+ PhoneQuery.CONTACT_ID,
+ PhoneQuery.LOOKUP_KEY,
+ PhoneQuery.DISPLAY_NAME);
+ } else {
+ if (getDisplayPhotos()) {
+ bindPhoto(view, partition, cursor);
+ }
+ }
+ } else {
+ unbindName(view);
+
+ view.removePhotoView(true, false);
+ }
+
+ final DirectoryPartition directory = (DirectoryPartition) getPartition(partition);
+
+ // If the first partition does not have a header, then all subsequent partitions'
+ // getPositionForPartition returns an index off by 1.
+ int partitionOffset = 0;
+ if (partition > 0 && !getPartition(0).getHasHeader()) {
+ partitionOffset = 1;
+ }
+ position += getPositionForPartition(partition) + partitionOffset;
+
+ bindPhoneNumber(view, cursor, directory.isDisplayNumber(), position);
+ }
+
+ protected void bindPhoneNumber(
+ ContactListItemView view, Cursor cursor, boolean displayNumber, int position) {
+ CharSequence label = null;
+ if (displayNumber && !cursor.isNull(PhoneQuery.PHONE_TYPE)) {
+ final int type = cursor.getInt(PhoneQuery.PHONE_TYPE);
+ final String customLabel = cursor.getString(PhoneQuery.PHONE_LABEL);
+
+ // TODO cache
+ label = Phone.getTypeLabel(getContext().getResources(), type, customLabel);
+ }
+ view.setLabel(label);
+ final String text;
+ if (displayNumber) {
+ text = cursor.getString(PhoneQuery.PHONE_NUMBER);
+ } else {
+ // Display phone label. If that's null, display geocoded location for the number
+ final String phoneLabel = cursor.getString(PhoneQuery.PHONE_LABEL);
+ if (phoneLabel != null) {
+ text = phoneLabel;
+ } else {
+ final String phoneNumber = cursor.getString(PhoneQuery.PHONE_NUMBER);
+ text = GeoUtil.getGeocodedLocationFor(mContext, phoneNumber);
+ }
+ }
+ view.setPhoneNumber(text);
+
+ if (CompatUtils.isVideoCompatible()) {
+ // Determine if carrier presence indicates the number supports video calling.
+ int carrierPresence = cursor.getInt(PhoneQuery.CARRIER_PRESENCE);
+ boolean isPresent = (carrierPresence & Phone.CARRIER_PRESENCE_VT_CAPABLE) != 0;
+
+ boolean isVideoIconShown = mIsVideoEnabled && (!mIsPresenceEnabled || isPresent);
+ view.setShowVideoCallIcon(isVideoIconShown, mListener, position);
+ }
+ }
+
+ protected void bindSectionHeaderAndDivider(final ContactListItemView view, int position) {
+ if (isSectionHeaderDisplayEnabled()) {
+ Placement placement = getItemPlacementInSection(position);
+ view.setSectionHeader(placement.firstInSection ? placement.sectionHeader : null);
+ } else {
+ view.setSectionHeader(null);
+ }
+ }
+
+ protected void bindName(final ContactListItemView view, Cursor cursor) {
+ view.showDisplayName(cursor, PhoneQuery.DISPLAY_NAME);
+ // Note: we don't show phonetic names any more (see issue 5265330)
+ }
+
+ protected void unbindName(final ContactListItemView view) {
+ view.hideDisplayName();
+ }
+
+ @Override
+ protected void bindWorkProfileIcon(final ContactListItemView view, int partition) {
+ final DirectoryPartition directory = (DirectoryPartition) getPartition(partition);
+ final long directoryId = directory.getDirectoryId();
+ final long userType = ContactsUtils.determineUserType(directoryId, null);
+ // Work directory must not be a extended directory. An extended directory is custom
+ // directory in the app, but not a directory provided by framework. So it can't be
+ // USER_TYPE_WORK.
+ view.setWorkProfileIconEnabled(
+ !isExtendedDirectory(directoryId) && userType == ContactsUtils.USER_TYPE_WORK);
+ }
+
+ protected void bindPhoto(final ContactListItemView view, int partitionIndex, Cursor cursor) {
+ if (!isPhotoSupported(partitionIndex)) {
+ view.removePhotoView();
+ return;
+ }
+
+ long photoId = 0;
+ if (!cursor.isNull(PhoneQuery.PHOTO_ID)) {
+ photoId = cursor.getLong(PhoneQuery.PHOTO_ID);
+ }
+
+ if (photoId != 0) {
+ getPhotoLoader()
+ .loadThumbnail(view.getPhotoView(), photoId, false, getCircularPhotos(), null);
+ } else {
+ final String photoUriString = cursor.getString(PhoneQuery.PHOTO_URI);
+ final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString);
+
+ DefaultImageRequest request = null;
+ if (photoUri == null) {
+ final String displayName = cursor.getString(PhoneQuery.DISPLAY_NAME);
+ final String lookupKey = cursor.getString(PhoneQuery.LOOKUP_KEY);
+ request = new DefaultImageRequest(displayName, lookupKey, getCircularPhotos());
+ }
+ getPhotoLoader()
+ .loadDirectoryPhoto(view.getPhotoView(), photoUri, false, getCircularPhotos(), request);
+ }
+ }
+
+ public ContactListItemView.PhotoPosition getPhotoPosition() {
+ return mPhotoPosition;
+ }
+
+ public void setPhotoPosition(ContactListItemView.PhotoPosition photoPosition) {
+ mPhotoPosition = photoPosition;
+ }
+
+ public void setUseCallableUri(boolean useCallableUri) {
+ mUseCallableUri = useCallableUri;
+ }
+
+ /**
+ * Override base implementation to inject extended directories between local & remote directories.
+ * This is done in the following steps: 1. Call base implementation to add directories from the
+ * cursor. 2. Iterate all base directories and establish the following information: a. The highest
+ * directory id so that we can assign unused id's to the extended directories. b. The index of the
+ * last non-remote directory. This is where we will insert extended directories. 3. Iterate the
+ * extended directories and for each one, assign an ID and insert it in the proper location.
+ */
+ @Override
+ public void changeDirectories(Cursor cursor) {
+ super.changeDirectories(cursor);
+ if (getDirectorySearchMode() == DirectoryListLoader.SEARCH_MODE_NONE) {
+ return;
+ }
+ final int numExtendedDirectories = mExtendedDirectories.size();
+ if (getPartitionCount() == cursor.getCount() + numExtendedDirectories) {
+ // already added all directories;
+ return;
+ }
+ //
+ mFirstExtendedDirectoryId = Long.MAX_VALUE;
+ if (numExtendedDirectories > 0) {
+ // The Directory.LOCAL_INVISIBLE is not in the cursor but we can't reuse it's
+ // "special" ID.
+ long maxId = Directory.LOCAL_INVISIBLE;
+ int insertIndex = 0;
+ for (int i = 0, n = getPartitionCount(); i < n; i++) {
+ final DirectoryPartition partition = (DirectoryPartition) getPartition(i);
+ final long id = partition.getDirectoryId();
+ if (id > maxId) {
+ maxId = id;
+ }
+ if (!DirectoryCompat.isRemoteDirectoryId(id)) {
+ // assuming remote directories come after local, we will end up with the index
+ // where we should insert extended directories. This also works if there are no
+ // remote directories at all.
+ insertIndex = i + 1;
+ }
+ }
+ // Extended directories ID's cannot collide with base directories
+ mFirstExtendedDirectoryId = maxId + 1;
+ for (int i = 0; i < numExtendedDirectories; i++) {
+ final long id = mFirstExtendedDirectoryId + i;
+ final DirectoryPartition directory = mExtendedDirectories.get(i);
+ if (getPartitionByDirectoryId(id) == -1) {
+ addPartition(insertIndex, directory);
+ directory.setDirectoryId(id);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected Uri getContactUri(
+ int partitionIndex, Cursor cursor, int contactIdColumn, int lookUpKeyColumn) {
+ final DirectoryPartition directory = (DirectoryPartition) getPartition(partitionIndex);
+ final long directoryId = directory.getDirectoryId();
+ if (!isExtendedDirectory(directoryId)) {
+ return super.getContactUri(partitionIndex, cursor, contactIdColumn, lookUpKeyColumn);
+ }
+ return Contacts.CONTENT_LOOKUP_URI
+ .buildUpon()
+ .appendPath(Constants.LOOKUP_URI_ENCODED)
+ .appendQueryParameter(Directory.DISPLAY_NAME, directory.getLabel())
+ .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId))
+ .encodedFragment(cursor.getString(lookUpKeyColumn))
+ .build();
+ }
+
+ public Listener getListener() {
+ return mListener;
+ }
+
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ public interface Listener {
+
+ void onVideoCallIconClicked(int position);
+ }
+
+ public static class PhoneQuery {
+
+ /**
+ * Optional key used as part of a JSON lookup key to specify an analytics category associated
+ * with the row.
+ */
+ public static final String ANALYTICS_CATEGORY = "analytics_category";
+
+ /**
+ * Optional key used as part of a JSON lookup key to specify an analytics action associated with
+ * the row.
+ */
+ public static final String ANALYTICS_ACTION = "analytics_action";
+
+ /**
+ * Optional key used as part of a JSON lookup key to specify an analytics value associated with
+ * the row.
+ */
+ public static final String ANALYTICS_VALUE = "analytics_value";
+
+ public static final String[] PROJECTION_PRIMARY_INTERNAL =
+ new String[] {
+ Phone._ID, // 0
+ Phone.TYPE, // 1
+ Phone.LABEL, // 2
+ Phone.NUMBER, // 3
+ Phone.CONTACT_ID, // 4
+ Phone.LOOKUP_KEY, // 5
+ Phone.PHOTO_ID, // 6
+ Phone.DISPLAY_NAME_PRIMARY, // 7
+ Phone.PHOTO_THUMBNAIL_URI, // 8
+ };
+
+ public static final String[] PROJECTION_PRIMARY;
+ public static final String[] PROJECTION_ALTERNATIVE_INTERNAL =
+ new String[] {
+ Phone._ID, // 0
+ Phone.TYPE, // 1
+ Phone.LABEL, // 2
+ Phone.NUMBER, // 3
+ Phone.CONTACT_ID, // 4
+ Phone.LOOKUP_KEY, // 5
+ Phone.PHOTO_ID, // 6
+ Phone.DISPLAY_NAME_ALTERNATIVE, // 7
+ Phone.PHOTO_THUMBNAIL_URI, // 8
+ };
+ public static final String[] PROJECTION_ALTERNATIVE;
+ public static final int PHONE_ID = 0;
+ public static final int PHONE_TYPE = 1;
+ public static final int PHONE_LABEL = 2;
+ public static final int PHONE_NUMBER = 3;
+ public static final int CONTACT_ID = 4;
+ public static final int LOOKUP_KEY = 5;
+ public static final int PHOTO_ID = 6;
+ public static final int DISPLAY_NAME = 7;
+ public static final int PHOTO_URI = 8;
+ public static final int CARRIER_PRESENCE = 9;
+
+ static {
+ final List<String> projectionList =
+ new ArrayList<>(Arrays.asList(PROJECTION_PRIMARY_INTERNAL));
+ if (CompatUtils.isMarshmallowCompatible()) {
+ projectionList.add(Phone.CARRIER_PRESENCE); // 9
+ }
+ PROJECTION_PRIMARY = projectionList.toArray(new String[projectionList.size()]);
+ }
+
+ static {
+ final List<String> projectionList =
+ new ArrayList<>(Arrays.asList(PROJECTION_ALTERNATIVE_INTERNAL));
+ 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
new file mode 100644
index 000000000..4ae81529b
--- /dev/null
+++ b/java/com/android/contacts/common/list/PhoneNumberPickerFragment.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.contacts.common.R;
+import com.android.contacts.common.util.AccountFilterUtil;
+import com.android.dialer.callintent.nano.CallSpecificAppData;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.logging.Logger;
+import java.util.Set;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/** Fragment containing a phone number list for picking. */
+public class PhoneNumberPickerFragment extends ContactEntryListFragment<ContactEntryListAdapter>
+ implements PhoneNumberListAdapter.Listener {
+
+ private static final String KEY_FILTER = "filter";
+ private OnPhoneNumberPickerActionListener mListener;
+ private ContactListFilter mFilter;
+ private View mAccountFilterHeader;
+ /**
+ * Lives as ListView's header and is shown when {@link #mAccountFilterHeader} is set to View.GONE.
+ */
+ private View mPaddingView;
+ /** true if the loader has started at least once. */
+ private boolean mLoaderStarted;
+
+ private boolean mUseCallableUri;
+
+ private ContactListItemView.PhotoPosition mPhotoPosition =
+ ContactListItemView.getDefaultPhotoPosition(false /* normal/non opposite */);
+
+ private final Set<OnLoadFinishedListener> mLoadFinishedListeners =
+ new ArraySet<OnLoadFinishedListener>();
+
+ private CursorReranker mCursorReranker;
+
+ public PhoneNumberPickerFragment() {
+ setQuickContactEnabled(false);
+ setPhotoLoaderEnabled(true);
+ setSectionHeaderDisplayEnabled(true);
+ setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
+
+ // Show nothing instead of letting caller Activity show something.
+ setHasOptionsMenu(true);
+ }
+
+ /**
+ * Handles a click on the video call icon for a row in the list.
+ *
+ * @param position The position in the list where the click ocurred.
+ */
+ @Override
+ public void onVideoCallIconClicked(int position) {
+ callNumber(position, true /* isVideoCall */);
+ }
+
+ public void setDirectorySearchEnabled(boolean flag) {
+ setDirectorySearchMode(
+ flag ? DirectoryListLoader.SEARCH_MODE_DEFAULT : DirectoryListLoader.SEARCH_MODE_NONE);
+ }
+
+ public void setOnPhoneNumberPickerActionListener(OnPhoneNumberPickerActionListener listener) {
+ this.mListener = listener;
+ }
+
+ public OnPhoneNumberPickerActionListener getOnPhoneNumberPickerListener() {
+ return mListener;
+ }
+
+ @Override
+ protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
+ super.onCreateView(inflater, container);
+
+ View paddingView = inflater.inflate(R.layout.contact_detail_list_padding, null, false);
+ mPaddingView = paddingView.findViewById(R.id.contact_detail_list_padding);
+ getListView().addHeaderView(paddingView);
+
+ mAccountFilterHeader = getView().findViewById(R.id.account_filter_header_container);
+ updateFilterHeaderView();
+
+ setVisibleScrollbarEnabled(getVisibleScrollbarEnabled());
+ }
+
+ protected boolean getVisibleScrollbarEnabled() {
+ return true;
+ }
+
+ @Override
+ protected void setSearchMode(boolean flag) {
+ super.setSearchMode(flag);
+ updateFilterHeaderView();
+ }
+
+ private void updateFilterHeaderView() {
+ final ContactListFilter filter = getFilter();
+ if (mAccountFilterHeader == null || filter == null) {
+ return;
+ }
+ final boolean shouldShowHeader =
+ !isSearchMode()
+ && AccountFilterUtil.updateAccountFilterTitleForPhone(
+ mAccountFilterHeader, filter, false);
+ if (shouldShowHeader) {
+ mPaddingView.setVisibility(View.GONE);
+ mAccountFilterHeader.setVisibility(View.VISIBLE);
+ } else {
+ mPaddingView.setVisibility(View.VISIBLE);
+ mAccountFilterHeader.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void restoreSavedState(Bundle savedState) {
+ super.restoreSavedState(savedState);
+
+ if (savedState == null) {
+ return;
+ }
+
+ mFilter = savedState.getParcelable(KEY_FILTER);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(KEY_FILTER, mFilter);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final int itemId = item.getItemId();
+ if (itemId == android.R.id.home) { // See ActionBar#setDisplayHomeAsUpEnabled()
+ if (mListener != null) {
+ mListener.onHomeInActionBarSelected();
+ }
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onItemClick(int position, long id) {
+ callNumber(position, false /* isVideoCall */);
+ }
+
+ /**
+ * Initiates a call to the number at the specified position.
+ *
+ * @param position The position.
+ * @param isVideoCall {@code true} if the call should be initiated as a video call, {@code false}
+ * otherwise.
+ */
+ private void callNumber(int position, boolean isVideoCall) {
+ final String number = getPhoneNumber(position);
+ if (!TextUtils.isEmpty(number)) {
+ cacheContactInfo(position);
+ CallSpecificAppData callSpecificAppData = new CallSpecificAppData();
+ callSpecificAppData.callInitiationType = getCallInitiationType(true /* isRemoteDirectory */);
+ callSpecificAppData.positionOfSelectedSearchResult = position;
+ callSpecificAppData.charactersInSearchString =
+ getQueryString() == null ? 0 : getQueryString().length();
+ mListener.onPickPhoneNumber(number, isVideoCall, callSpecificAppData);
+ } else {
+ LogUtil.i(
+ "PhoneNumberPickerFragment.callNumber",
+ "item at %d was clicked before adapter is ready, ignoring",
+ position);
+ }
+
+ // Get the lookup key and track any analytics
+ final String lookupKey = getLookupKey(position);
+ if (!TextUtils.isEmpty(lookupKey)) {
+ maybeTrackAnalytics(lookupKey);
+ }
+ }
+
+ protected void cacheContactInfo(int position) {
+ // Not implemented. Hook for child classes
+ }
+
+ protected String getPhoneNumber(int position) {
+ final PhoneNumberListAdapter adapter = (PhoneNumberListAdapter) getAdapter();
+ return adapter.getPhoneNumber(position);
+ }
+
+ protected String getLookupKey(int position) {
+ final PhoneNumberListAdapter adapter = (PhoneNumberListAdapter) getAdapter();
+ return adapter.getLookupKey(position);
+ }
+
+ @Override
+ protected void startLoading() {
+ mLoaderStarted = true;
+ super.startLoading();
+ }
+
+ @Override
+ @MainThread
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ Assert.isMainThread();
+ // TODO: define and verify behavior for "Nearby places", corp directories,
+ // and dividers listed in UI between these categories
+ if (mCursorReranker != null
+ && data != null
+ && !data.isClosed()
+ && data.getCount() > 0
+ && loader.getId() != -1) { // skip invalid directory ID of -1
+ data = mCursorReranker.rerankCursor(data);
+ }
+ super.onLoadFinished(loader, data);
+
+ // disable scroll bar if there is no data
+ setVisibleScrollbarEnabled(data != null && !data.isClosed() && data.getCount() > 0);
+
+ if (data != null) {
+ notifyListeners();
+ }
+ }
+
+ /** Ranks cursor data rows and returns reference to new cursor object with reordered data. */
+ public interface CursorReranker {
+ @MainThread
+ Cursor rerankCursor(Cursor data);
+ }
+
+ @MainThread
+ public void setReranker(@Nullable CursorReranker reranker) {
+ Assert.isMainThread();
+ mCursorReranker = reranker;
+ }
+
+ /** Listener that is notified when cursor has finished loading data. */
+ public interface OnLoadFinishedListener {
+ void onLoadFinished();
+ }
+
+ @MainThread
+ public void addOnLoadFinishedListener(OnLoadFinishedListener listener) {
+ Assert.isMainThread();
+ mLoadFinishedListeners.add(listener);
+ }
+
+ @MainThread
+ public void removeOnLoadFinishedListener(OnLoadFinishedListener listener) {
+ Assert.isMainThread();
+ mLoadFinishedListeners.remove(listener);
+ }
+
+ @MainThread
+ protected void notifyListeners() {
+ Assert.isMainThread();
+ for (OnLoadFinishedListener listener : mLoadFinishedListeners) {
+ listener.onLoadFinished();
+ }
+ }
+
+ @MainThread
+ @Override
+ public void onDetach() {
+ Assert.isMainThread();
+ mLoadFinishedListeners.clear();
+ super.onDetach();
+ }
+
+ public void setUseCallableUri(boolean useCallableUri) {
+ mUseCallableUri = useCallableUri;
+ }
+
+ public boolean usesCallableUri() {
+ return mUseCallableUri;
+ }
+
+ @Override
+ protected ContactEntryListAdapter createListAdapter() {
+ PhoneNumberListAdapter adapter = new PhoneNumberListAdapter(getActivity());
+ adapter.setDisplayPhotos(true);
+ adapter.setUseCallableUri(mUseCallableUri);
+ return adapter;
+ }
+
+ @Override
+ protected void configureAdapter() {
+ super.configureAdapter();
+
+ final ContactEntryListAdapter adapter = getAdapter();
+ if (adapter == null) {
+ return;
+ }
+
+ if (!isSearchMode() && mFilter != null) {
+ adapter.setFilter(mFilter);
+ }
+
+ setPhotoPosition(adapter);
+ }
+
+ protected void setPhotoPosition(ContactEntryListAdapter adapter) {
+ ((PhoneNumberListAdapter) adapter).setPhotoPosition(mPhotoPosition);
+ }
+
+ @Override
+ protected View inflateView(LayoutInflater inflater, ViewGroup container) {
+ return inflater.inflate(R.layout.contact_list_content, null);
+ }
+
+ public ContactListFilter getFilter() {
+ return mFilter;
+ }
+
+ public void setFilter(ContactListFilter filter) {
+ if ((mFilter == null && filter == null) || (mFilter != null && mFilter.equals(filter))) {
+ return;
+ }
+
+ mFilter = filter;
+ if (mLoaderStarted) {
+ reloadData();
+ }
+ updateFilterHeaderView();
+ }
+
+ public void setPhotoPosition(ContactListItemView.PhotoPosition photoPosition) {
+ mPhotoPosition = photoPosition;
+
+ final PhoneNumberListAdapter adapter = (PhoneNumberListAdapter) getAdapter();
+ if (adapter != null) {
+ adapter.setPhotoPosition(photoPosition);
+ }
+ }
+
+ /**
+ * @param isRemoteDirectory {@code true} if the call was initiated using a contact/phone number
+ * not in the local contacts database
+ */
+ protected int getCallInitiationType(boolean isRemoteDirectory) {
+ return OnPhoneNumberPickerActionListener.CALL_INITIATION_UNKNOWN;
+ }
+
+ /**
+ * Where a lookup key contains analytic event information, logs the associated analytics event.
+ *
+ * @param lookupKey The lookup key JSON object.
+ */
+ private void maybeTrackAnalytics(String lookupKey) {
+ try {
+ JSONObject json = new JSONObject(lookupKey);
+
+ String analyticsCategory =
+ json.getString(PhoneNumberListAdapter.PhoneQuery.ANALYTICS_CATEGORY);
+ String analyticsAction = json.getString(PhoneNumberListAdapter.PhoneQuery.ANALYTICS_ACTION);
+ String analyticsValue = json.getString(PhoneNumberListAdapter.PhoneQuery.ANALYTICS_VALUE);
+
+ if (TextUtils.isEmpty(analyticsCategory)
+ || TextUtils.isEmpty(analyticsAction)
+ || TextUtils.isEmpty(analyticsValue)) {
+ return;
+ }
+
+ // Assume that the analytic value being tracked could be a float value, but just cast
+ // to a long so that the analytic server can handle it.
+ long value;
+ try {
+ float floatValue = Float.parseFloat(analyticsValue);
+ value = (long) floatValue;
+ } catch (NumberFormatException nfe) {
+ return;
+ }
+
+ Logger.get(getActivity())
+ .sendHitEventAnalytics(analyticsCategory, analyticsAction, "" /* label */, value);
+ } catch (JSONException e) {
+ // Not an error; just a lookup key that doesn't have the right information.
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/list/PinnedHeaderListAdapter.java b/java/com/android/contacts/common/list/PinnedHeaderListAdapter.java
new file mode 100644
index 000000000..0bdcef084
--- /dev/null
+++ b/java/com/android/contacts/common/list/PinnedHeaderListAdapter.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.common.widget.CompositeCursorAdapter;
+
+/** A subclass of {@link CompositeCursorAdapter} that manages pinned partition headers. */
+public abstract class PinnedHeaderListAdapter extends CompositeCursorAdapter
+ implements PinnedHeaderListView.PinnedHeaderAdapter {
+
+ public static final int PARTITION_HEADER_TYPE = 0;
+
+ private boolean mPinnedPartitionHeadersEnabled;
+ private boolean[] mHeaderVisibility;
+
+ public PinnedHeaderListAdapter(Context context) {
+ super(context);
+ }
+
+ public boolean getPinnedPartitionHeadersEnabled() {
+ return mPinnedPartitionHeadersEnabled;
+ }
+
+ public void setPinnedPartitionHeadersEnabled(boolean flag) {
+ this.mPinnedPartitionHeadersEnabled = flag;
+ }
+
+ @Override
+ public int getPinnedHeaderCount() {
+ if (mPinnedPartitionHeadersEnabled) {
+ return getPartitionCount();
+ } else {
+ return 0;
+ }
+ }
+
+ protected boolean isPinnedPartitionHeaderVisible(int partition) {
+ return getPinnedPartitionHeadersEnabled()
+ && hasHeader(partition)
+ && !isPartitionEmpty(partition);
+ }
+
+ /** The default implementation creates the same type of view as a normal partition header. */
+ @Override
+ public View getPinnedHeaderView(int partition, View convertView, ViewGroup parent) {
+ if (hasHeader(partition)) {
+ View view = null;
+ if (convertView != null) {
+ Integer headerType = (Integer) convertView.getTag();
+ if (headerType != null && headerType == PARTITION_HEADER_TYPE) {
+ view = convertView;
+ }
+ }
+ if (view == null) {
+ view = newHeaderView(getContext(), partition, null, parent);
+ view.setTag(PARTITION_HEADER_TYPE);
+ view.setFocusable(false);
+ view.setEnabled(false);
+ }
+ bindHeaderView(view, partition, getCursor(partition));
+ view.setLayoutDirection(parent.getLayoutDirection());
+ return view;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void configurePinnedHeaders(PinnedHeaderListView listView) {
+ if (!getPinnedPartitionHeadersEnabled()) {
+ return;
+ }
+
+ int size = getPartitionCount();
+
+ // Cache visibility bits, because we will need them several times later on
+ if (mHeaderVisibility == null || mHeaderVisibility.length != size) {
+ mHeaderVisibility = new boolean[size];
+ }
+ for (int i = 0; i < size; i++) {
+ boolean visible = isPinnedPartitionHeaderVisible(i);
+ mHeaderVisibility[i] = visible;
+ if (!visible) {
+ listView.setHeaderInvisible(i, true);
+ }
+ }
+
+ int headerViewsCount = listView.getHeaderViewsCount();
+
+ // Starting at the top, find and pin headers for partitions preceding the visible one(s)
+ int maxTopHeader = -1;
+ int topHeaderHeight = 0;
+ for (int i = 0; i < size; i++) {
+ if (mHeaderVisibility[i]) {
+ int position = listView.getPositionAt(topHeaderHeight) - headerViewsCount;
+ int partition = getPartitionForPosition(position);
+ if (i > partition) {
+ break;
+ }
+
+ listView.setHeaderPinnedAtTop(i, topHeaderHeight, false);
+ topHeaderHeight += listView.getPinnedHeaderHeight(i);
+ maxTopHeader = i;
+ }
+ }
+
+ // Starting at the bottom, find and pin headers for partitions following the visible one(s)
+ int maxBottomHeader = size;
+ int bottomHeaderHeight = 0;
+ int listHeight = listView.getHeight();
+ for (int i = size; --i > maxTopHeader; ) {
+ if (mHeaderVisibility[i]) {
+ int position = listView.getPositionAt(listHeight - bottomHeaderHeight) - headerViewsCount;
+ if (position < 0) {
+ break;
+ }
+
+ int partition = getPartitionForPosition(position - 1);
+ if (partition == -1 || i <= partition) {
+ break;
+ }
+
+ int height = listView.getPinnedHeaderHeight(i);
+ bottomHeaderHeight += height;
+
+ listView.setHeaderPinnedAtBottom(i, listHeight - bottomHeaderHeight, false);
+ maxBottomHeader = i;
+ }
+ }
+
+ // Headers in between the top-pinned and bottom-pinned should be hidden
+ for (int i = maxTopHeader + 1; i < maxBottomHeader; i++) {
+ if (mHeaderVisibility[i]) {
+ listView.setHeaderInvisible(i, isPartitionEmpty(i));
+ }
+ }
+ }
+
+ @Override
+ public int getScrollPositionForHeader(int viewIndex) {
+ return getPositionForPartition(viewIndex);
+ }
+}
diff --git a/java/com/android/contacts/common/list/PinnedHeaderListView.java b/java/com/android/contacts/common/list/PinnedHeaderListView.java
new file mode 100644
index 000000000..33c68b68c
--- /dev/null
+++ b/java/com/android/contacts/common/list/PinnedHeaderListView.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ListAdapter;
+import com.android.dialer.util.ViewUtil;
+
+/**
+ * A ListView that maintains a header pinned at the top of the list. The pinned header can be pushed
+ * up and dissolved as needed.
+ */
+public class PinnedHeaderListView extends AutoScrollListView
+ implements OnScrollListener, OnItemSelectedListener {
+
+ private static final int MAX_ALPHA = 255;
+ private static final int TOP = 0;
+ private static final int BOTTOM = 1;
+ private static final int FADING = 2;
+ private static final int DEFAULT_ANIMATION_DURATION = 20;
+ private static final int DEFAULT_SMOOTH_SCROLL_DURATION = 100;
+ private PinnedHeaderAdapter mAdapter;
+ private int mSize;
+ private PinnedHeader[] mHeaders;
+ private RectF mBounds = new RectF();
+ private OnScrollListener mOnScrollListener;
+ private OnItemSelectedListener mOnItemSelectedListener;
+ private int mScrollState;
+ private boolean mScrollToSectionOnHeaderTouch = false;
+ private boolean mHeaderTouched = false;
+ private int mAnimationDuration = DEFAULT_ANIMATION_DURATION;
+ private boolean mAnimating;
+ private long mAnimationTargetTime;
+ private int mHeaderPaddingStart;
+ private int mHeaderWidth;
+
+ public PinnedHeaderListView(Context context) {
+ this(context, null, android.R.attr.listViewStyle);
+ }
+
+ public PinnedHeaderListView(Context context, AttributeSet attrs) {
+ this(context, attrs, android.R.attr.listViewStyle);
+ }
+
+ public PinnedHeaderListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ super.setOnScrollListener(this);
+ super.setOnItemSelectedListener(this);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ mHeaderPaddingStart = getPaddingStart();
+ mHeaderWidth = r - l - mHeaderPaddingStart - getPaddingEnd();
+ }
+
+ @Override
+ public void setAdapter(ListAdapter adapter) {
+ mAdapter = (PinnedHeaderAdapter) adapter;
+ super.setAdapter(adapter);
+ }
+
+ @Override
+ public void setOnScrollListener(OnScrollListener onScrollListener) {
+ mOnScrollListener = onScrollListener;
+ super.setOnScrollListener(this);
+ }
+
+ @Override
+ public void setOnItemSelectedListener(OnItemSelectedListener listener) {
+ mOnItemSelectedListener = listener;
+ super.setOnItemSelectedListener(this);
+ }
+
+ public void setScrollToSectionOnHeaderTouch(boolean value) {
+ mScrollToSectionOnHeaderTouch = value;
+ }
+
+ @Override
+ public void onScroll(
+ AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+ if (mAdapter != null) {
+ int count = mAdapter.getPinnedHeaderCount();
+ if (count != mSize) {
+ mSize = count;
+ if (mHeaders == null) {
+ mHeaders = new PinnedHeader[mSize];
+ } else if (mHeaders.length < mSize) {
+ PinnedHeader[] headers = mHeaders;
+ mHeaders = new PinnedHeader[mSize];
+ System.arraycopy(headers, 0, mHeaders, 0, headers.length);
+ }
+ }
+
+ for (int i = 0; i < mSize; i++) {
+ if (mHeaders[i] == null) {
+ mHeaders[i] = new PinnedHeader();
+ }
+ mHeaders[i].view = mAdapter.getPinnedHeaderView(i, mHeaders[i].view, this);
+ }
+
+ mAnimationTargetTime = System.currentTimeMillis() + mAnimationDuration;
+ mAdapter.configurePinnedHeaders(this);
+ invalidateIfAnimating();
+ }
+ if (mOnScrollListener != null) {
+ mOnScrollListener.onScroll(this, firstVisibleItem, visibleItemCount, totalItemCount);
+ }
+ }
+
+ @Override
+ protected float getTopFadingEdgeStrength() {
+ // Disable vertical fading at the top when the pinned header is present
+ return mSize > 0 ? 0 : super.getTopFadingEdgeStrength();
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ mScrollState = scrollState;
+ if (mOnScrollListener != null) {
+ mOnScrollListener.onScrollStateChanged(this, scrollState);
+ }
+ }
+
+ /**
+ * Ensures that the selected item is positioned below the top-pinned headers and above the
+ * bottom-pinned ones.
+ */
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ int height = getHeight();
+
+ int windowTop = 0;
+ int windowBottom = height;
+
+ for (int i = 0; i < mSize; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible) {
+ if (header.state == TOP) {
+ windowTop = header.y + header.height;
+ } else if (header.state == BOTTOM) {
+ windowBottom = header.y;
+ break;
+ }
+ }
+ }
+
+ View selectedView = getSelectedView();
+ if (selectedView != null) {
+ if (selectedView.getTop() < windowTop) {
+ setSelectionFromTop(position, windowTop);
+ } else if (selectedView.getBottom() > windowBottom) {
+ setSelectionFromTop(position, windowBottom - selectedView.getHeight());
+ }
+ }
+
+ if (mOnItemSelectedListener != null) {
+ mOnItemSelectedListener.onItemSelected(parent, view, position, id);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ if (mOnItemSelectedListener != null) {
+ mOnItemSelectedListener.onNothingSelected(parent);
+ }
+ }
+
+ public int getPinnedHeaderHeight(int viewIndex) {
+ ensurePinnedHeaderLayout(viewIndex);
+ return mHeaders[viewIndex].view.getHeight();
+ }
+
+ /**
+ * Set header to be pinned at the top.
+ *
+ * @param viewIndex index of the header view
+ * @param y is position of the header in pixels.
+ * @param animate true if the transition to the new coordinate should be animated
+ */
+ public void setHeaderPinnedAtTop(int viewIndex, int y, boolean animate) {
+ ensurePinnedHeaderLayout(viewIndex);
+ PinnedHeader header = mHeaders[viewIndex];
+ header.visible = true;
+ header.y = y;
+ header.state = TOP;
+
+ // TODO perhaps we should animate at the top as well
+ header.animating = false;
+ }
+
+ /**
+ * Set header to be pinned at the bottom.
+ *
+ * @param viewIndex index of the header view
+ * @param y is position of the header in pixels.
+ * @param animate true if the transition to the new coordinate should be animated
+ */
+ public void setHeaderPinnedAtBottom(int viewIndex, int y, boolean animate) {
+ ensurePinnedHeaderLayout(viewIndex);
+ PinnedHeader header = mHeaders[viewIndex];
+ header.state = BOTTOM;
+ if (header.animating) {
+ header.targetTime = mAnimationTargetTime;
+ header.sourceY = header.y;
+ header.targetY = y;
+ } else if (animate && (header.y != y || !header.visible)) {
+ if (header.visible) {
+ header.sourceY = header.y;
+ } else {
+ header.visible = true;
+ header.sourceY = y + header.height;
+ }
+ header.animating = true;
+ header.targetVisible = true;
+ header.targetTime = mAnimationTargetTime;
+ header.targetY = y;
+ } else {
+ header.visible = true;
+ header.y = y;
+ }
+ }
+
+ /**
+ * Set header to be pinned at the top of the first visible item.
+ *
+ * @param viewIndex index of the header view
+ * @param position is position of the header in pixels.
+ */
+ public void setFadingHeader(int viewIndex, int position, boolean fade) {
+ ensurePinnedHeaderLayout(viewIndex);
+
+ View child = getChildAt(position - getFirstVisiblePosition());
+ if (child == null) {
+ return;
+ }
+
+ PinnedHeader header = mHeaders[viewIndex];
+ header.visible = true;
+ header.state = FADING;
+ header.alpha = MAX_ALPHA;
+ header.animating = false;
+
+ int top = getTotalTopPinnedHeaderHeight();
+ header.y = top;
+ if (fade) {
+ int bottom = child.getBottom() - top;
+ int headerHeight = header.height;
+ if (bottom < headerHeight) {
+ int portion = bottom - headerHeight;
+ header.alpha = MAX_ALPHA * (headerHeight + portion) / headerHeight;
+ header.y = top + portion;
+ }
+ }
+ }
+
+ /**
+ * Makes header invisible.
+ *
+ * @param viewIndex index of the header view
+ * @param animate true if the transition to the new coordinate should be animated
+ */
+ public void setHeaderInvisible(int viewIndex, boolean animate) {
+ PinnedHeader header = mHeaders[viewIndex];
+ if (header.visible && (animate || header.animating) && header.state == BOTTOM) {
+ header.sourceY = header.y;
+ if (!header.animating) {
+ header.visible = true;
+ header.targetY = getBottom() + header.height;
+ }
+ header.animating = true;
+ header.targetTime = mAnimationTargetTime;
+ header.targetVisible = false;
+ } else {
+ header.visible = false;
+ }
+ }
+
+ private void ensurePinnedHeaderLayout(int viewIndex) {
+ View view = mHeaders[viewIndex].view;
+ if (view.isLayoutRequested()) {
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ int widthSpec;
+ int heightSpec;
+
+ if (layoutParams != null && layoutParams.width > 0) {
+ widthSpec = View.MeasureSpec.makeMeasureSpec(layoutParams.width, View.MeasureSpec.EXACTLY);
+ } else {
+ widthSpec = View.MeasureSpec.makeMeasureSpec(mHeaderWidth, View.MeasureSpec.EXACTLY);
+ }
+
+ if (layoutParams != null && layoutParams.height > 0) {
+ heightSpec =
+ View.MeasureSpec.makeMeasureSpec(layoutParams.height, View.MeasureSpec.EXACTLY);
+ } else {
+ heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+ }
+ view.measure(widthSpec, heightSpec);
+ int height = view.getMeasuredHeight();
+ mHeaders[viewIndex].height = height;
+ view.layout(0, 0, view.getMeasuredWidth(), height);
+ }
+ }
+
+ /** Returns the sum of heights of headers pinned to the top. */
+ public int getTotalTopPinnedHeaderHeight() {
+ for (int i = mSize; --i >= 0; ) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible && header.state == TOP) {
+ return header.y + header.height;
+ }
+ }
+ return 0;
+ }
+
+ /** Returns the list item position at the specified y coordinate. */
+ public int getPositionAt(int y) {
+ do {
+ int position = pointToPosition(getPaddingLeft() + 1, y);
+ if (position != -1) {
+ return position;
+ }
+ // If position == -1, we must have hit a separator. Let's examine
+ // a nearby pixel
+ y--;
+ } while (y > 0);
+ return 0;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ mHeaderTouched = false;
+ if (super.onInterceptTouchEvent(ev)) {
+ return true;
+ }
+
+ if (mScrollState == SCROLL_STATE_IDLE) {
+ final int y = (int) ev.getY();
+ final int x = (int) ev.getX();
+ for (int i = mSize; --i >= 0; ) {
+ PinnedHeader header = mHeaders[i];
+ // For RTL layouts, this also takes into account that the scrollbar is on the left
+ // side.
+ final int padding = getPaddingLeft();
+ if (header.visible
+ && header.y <= y
+ && header.y + header.height > y
+ && x >= padding
+ && padding + header.view.getWidth() >= x) {
+ mHeaderTouched = true;
+ if (mScrollToSectionOnHeaderTouch && ev.getAction() == MotionEvent.ACTION_DOWN) {
+ return smoothScrollToPartition(i);
+ } else {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (mHeaderTouched) {
+ if (ev.getAction() == MotionEvent.ACTION_UP) {
+ mHeaderTouched = false;
+ }
+ return true;
+ }
+ return super.onTouchEvent(ev);
+ }
+
+ private boolean smoothScrollToPartition(int partition) {
+ if (mAdapter == null) {
+ return false;
+ }
+ final int position = mAdapter.getScrollPositionForHeader(partition);
+ if (position == -1) {
+ return false;
+ }
+
+ int offset = 0;
+ for (int i = 0; i < partition; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible) {
+ offset += header.height;
+ }
+ }
+ smoothScrollToPositionFromTop(
+ position + getHeaderViewsCount(), offset, DEFAULT_SMOOTH_SCROLL_DURATION);
+ return true;
+ }
+
+ private void invalidateIfAnimating() {
+ mAnimating = false;
+ for (int i = 0; i < mSize; i++) {
+ if (mHeaders[i].animating) {
+ mAnimating = true;
+ invalidate();
+ return;
+ }
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ long currentTime = mAnimating ? System.currentTimeMillis() : 0;
+
+ int top = 0;
+ int bottom = getBottom();
+ boolean hasVisibleHeaders = false;
+ for (int i = 0; i < mSize; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible) {
+ hasVisibleHeaders = true;
+ if (header.state == BOTTOM && header.y < bottom) {
+ bottom = header.y;
+ } else if (header.state == TOP || header.state == FADING) {
+ int newTop = header.y + header.height;
+ if (newTop > top) {
+ top = newTop;
+ }
+ }
+ }
+ }
+
+ if (hasVisibleHeaders) {
+ canvas.save();
+ }
+
+ super.dispatchDraw(canvas);
+
+ if (hasVisibleHeaders) {
+ canvas.restore();
+
+ // If the first item is visible and if it has a positive top that is greater than the
+ // first header's assigned y-value, use that for the first header's y value. This way,
+ // the header inherits any padding applied to the list view.
+ if (mSize > 0 && getFirstVisiblePosition() == 0) {
+ View firstChild = getChildAt(0);
+ PinnedHeader firstHeader = mHeaders[0];
+
+ if (firstHeader != null) {
+ int firstHeaderTop = firstChild != null ? firstChild.getTop() : 0;
+ firstHeader.y = Math.max(firstHeader.y, firstHeaderTop);
+ }
+ }
+
+ // First draw top headers, then the bottom ones to handle the Z axis correctly
+ for (int i = mSize; --i >= 0; ) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible && (header.state == TOP || header.state == FADING)) {
+ drawHeader(canvas, header, currentTime);
+ }
+ }
+
+ for (int i = 0; i < mSize; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible && header.state == BOTTOM) {
+ drawHeader(canvas, header, currentTime);
+ }
+ }
+ }
+
+ invalidateIfAnimating();
+ }
+
+ private void drawHeader(Canvas canvas, PinnedHeader header, long currentTime) {
+ if (header.animating) {
+ int timeLeft = (int) (header.targetTime - currentTime);
+ if (timeLeft <= 0) {
+ header.y = header.targetY;
+ header.visible = header.targetVisible;
+ header.animating = false;
+ } else {
+ header.y =
+ header.targetY + (header.sourceY - header.targetY) * timeLeft / mAnimationDuration;
+ }
+ }
+ if (header.visible) {
+ View view = header.view;
+ int saveCount = canvas.save();
+ int translateX =
+ ViewUtil.isViewLayoutRtl(this)
+ ? getWidth() - mHeaderPaddingStart - view.getWidth()
+ : mHeaderPaddingStart;
+ canvas.translate(translateX, header.y);
+ if (header.state == FADING) {
+ mBounds.set(0, 0, view.getWidth(), view.getHeight());
+ canvas.saveLayerAlpha(mBounds, header.alpha, Canvas.ALL_SAVE_FLAG);
+ }
+ view.draw(canvas);
+ canvas.restoreToCount(saveCount);
+ }
+ }
+
+ /** Adapter interface. The list adapter must implement this interface. */
+ public interface PinnedHeaderAdapter {
+
+ /** Returns the overall number of pinned headers, visible or not. */
+ int getPinnedHeaderCount();
+
+ /** Creates or updates the pinned header view. */
+ View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent);
+
+ /**
+ * Configures the pinned headers to match the visible list items. The adapter should call {@link
+ * PinnedHeaderListView#setHeaderPinnedAtTop}, {@link
+ * PinnedHeaderListView#setHeaderPinnedAtBottom}, {@link PinnedHeaderListView#setFadingHeader}
+ * or {@link PinnedHeaderListView#setHeaderInvisible}, for each header that needs to change its
+ * position or visibility.
+ */
+ void configurePinnedHeaders(PinnedHeaderListView listView);
+
+ /**
+ * Returns the list position to scroll to if the pinned header is touched. Return -1 if the list
+ * does not need to be scrolled.
+ */
+ int getScrollPositionForHeader(int viewIndex);
+ }
+
+ private static final class PinnedHeader {
+
+ View view;
+ boolean visible;
+ int y;
+ int height;
+ int alpha;
+ int state;
+
+ boolean animating;
+ boolean targetVisible;
+ int sourceY;
+ int targetY;
+ long targetTime;
+ }
+}
diff --git a/java/com/android/contacts/common/list/ViewPagerTabStrip.java b/java/com/android/contacts/common/list/ViewPagerTabStrip.java
new file mode 100644
index 000000000..969a6d342
--- /dev/null
+++ b/java/com/android/contacts/common/list/ViewPagerTabStrip.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 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.list;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+import com.android.contacts.common.R;
+
+public class ViewPagerTabStrip extends LinearLayout {
+
+ private final Paint mSelectedUnderlinePaint;
+ private int mSelectedUnderlineThickness;
+ private int mIndexForSelection;
+ private float mSelectionOffset;
+
+ public ViewPagerTabStrip(Context context) {
+ this(context, null);
+ }
+
+ public ViewPagerTabStrip(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ final Resources res = context.getResources();
+
+ mSelectedUnderlineThickness = res.getDimensionPixelSize(R.dimen.tab_selected_underline_height);
+ int underlineColor = res.getColor(R.color.tab_selected_underline_color);
+ int backgroundColor = res.getColor(R.color.contactscommon_actionbar_background_color);
+
+ mSelectedUnderlinePaint = new Paint();
+ mSelectedUnderlinePaint.setColor(underlineColor);
+
+ setBackgroundColor(backgroundColor);
+ setWillNotDraw(false);
+ }
+
+ /**
+ * Notifies this view that view pager has been scrolled. We save the tab index and selection
+ * offset for interpolating the position and width of selection underline.
+ */
+ void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ mIndexForSelection = position;
+ mSelectionOffset = positionOffset;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ int childCount = getChildCount();
+
+ // Thick colored underline below the current selection
+ if (childCount > 0) {
+ View selectedTitle = getChildAt(mIndexForSelection);
+
+ if (selectedTitle == null) {
+ // The view pager's tab count changed but we weren't notified yet. Ignore this draw
+ // pass, when we get a new selection we will update and draw the selection strip in
+ // the correct place.
+ return;
+ }
+ int selectedLeft = selectedTitle.getLeft();
+ int selectedRight = selectedTitle.getRight();
+ final boolean isRtl = isRtl();
+ final boolean hasNextTab =
+ isRtl ? mIndexForSelection > 0 : (mIndexForSelection < (getChildCount() - 1));
+ if ((mSelectionOffset > 0.0f) && hasNextTab) {
+ // Draw the selection partway between the tabs
+ View nextTitle = getChildAt(mIndexForSelection + (isRtl ? -1 : 1));
+ int nextLeft = nextTitle.getLeft();
+ int nextRight = nextTitle.getRight();
+
+ selectedLeft =
+ (int) (mSelectionOffset * nextLeft + (1.0f - mSelectionOffset) * selectedLeft);
+ selectedRight =
+ (int) (mSelectionOffset * nextRight + (1.0f - mSelectionOffset) * selectedRight);
+ }
+
+ int height = getHeight();
+ canvas.drawRect(
+ selectedLeft,
+ height - mSelectedUnderlineThickness,
+ selectedRight,
+ height,
+ mSelectedUnderlinePaint);
+ }
+ }
+
+ private boolean isRtl() {
+ return getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ }
+}
diff --git a/java/com/android/contacts/common/list/ViewPagerTabs.java b/java/com/android/contacts/common/list/ViewPagerTabs.java
new file mode 100644
index 000000000..34f623ef4
--- /dev/null
+++ b/java/com/android/contacts/common/list/ViewPagerTabs.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2014 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.list;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Outline;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.widget.FrameLayout;
+import android.widget.HorizontalScrollView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+import com.android.contacts.common.R;
+import com.android.dialer.compat.CompatUtils;
+
+/**
+ * Lightweight implementation of ViewPager tabs. This looks similar to traditional actionBar tabs,
+ * but allows for the view containing the tabs to be placed anywhere on screen. Text-related
+ * attributes can also be assigned in XML - these will get propogated to the child TextViews
+ * automatically.
+ */
+public class ViewPagerTabs extends HorizontalScrollView implements ViewPager.OnPageChangeListener {
+
+ private static final ViewOutlineProvider VIEW_BOUNDS_OUTLINE_PROVIDER;
+ private static final int TAB_SIDE_PADDING_IN_DPS = 10;
+ // TODO: This should use <declare-styleable> in the future
+ private static final int[] ATTRS =
+ new int[] {
+ android.R.attr.textSize,
+ android.R.attr.textStyle,
+ android.R.attr.textColor,
+ 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.
+ */
+ final int mTextStyle;
+
+ final ColorStateList mTextColor;
+ final int mTextSize;
+ final boolean mTextAllCaps;
+ ViewPager mPager;
+ int mPrevSelected = -1;
+ int mSidePadding;
+ private ViewPagerTabStrip mTabStrip;
+ private int[] mTabIcons;
+ // For displaying the unread count next to the tab icon.
+ private int[] mUnreadCounts;
+
+ public ViewPagerTabs(Context context) {
+ this(context, null);
+ }
+
+ public ViewPagerTabs(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ViewPagerTabs(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setFillViewport(true);
+
+ mSidePadding = (int) (getResources().getDisplayMetrics().density * TAB_SIDE_PADDING_IN_DPS);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);
+ mTextSize = a.getDimensionPixelSize(0, 0);
+ mTextStyle = a.getInt(1, 0);
+ mTextColor = a.getColorStateList(2);
+ mTextAllCaps = a.getBoolean(3, false);
+
+ mTabStrip = new ViewPagerTabStrip(context);
+ addView(
+ mTabStrip,
+ new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
+ a.recycle();
+
+ if (CompatUtils.isLollipopCompatible()) {
+ // enable shadow casting from view bounds
+ setOutlineProvider(VIEW_BOUNDS_OUTLINE_PROVIDER);
+ }
+ }
+
+ public void setViewPager(ViewPager viewPager) {
+ mPager = viewPager;
+ addTabs(mPager.getAdapter());
+ }
+
+ /**
+ * Set the tab icons and initialize an array for unread counts the same length as the icon array.
+ *
+ * @param tabIcons An array representing the tab icons in order.
+ */
+ public void configureTabIcons(int[] tabIcons) {
+ mTabIcons = tabIcons;
+ mUnreadCounts = new int[tabIcons.length];
+ }
+
+ public void setUnreadCount(int count, int position) {
+ if (mUnreadCounts == null || position >= mUnreadCounts.length) {
+ return;
+ }
+ mUnreadCounts[position] = count;
+ }
+
+ private void addTabs(PagerAdapter adapter) {
+ mTabStrip.removeAllViews();
+
+ final int count = adapter.getCount();
+ for (int i = 0; i < count; i++) {
+ addTab(adapter.getPageTitle(i), i);
+ }
+ }
+
+ private void addTab(CharSequence tabTitle, final int position) {
+ View tabView;
+ if (mTabIcons != null && position < mTabIcons.length) {
+ View layout = LayoutInflater.from(getContext()).inflate(R.layout.unread_count_tab, null);
+ View iconView = layout.findViewById(R.id.icon);
+ iconView.setBackgroundResource(mTabIcons[position]);
+ iconView.setContentDescription(tabTitle);
+ TextView textView = (TextView) layout.findViewById(R.id.count);
+ if (mUnreadCounts != null && mUnreadCounts[position] > 0) {
+ textView.setText(Integer.toString(mUnreadCounts[position]));
+ textView.setVisibility(View.VISIBLE);
+ iconView.setContentDescription(
+ getResources()
+ .getQuantityString(
+ R.plurals.tab_title_with_unread_items,
+ mUnreadCounts[position],
+ tabTitle.toString(),
+ mUnreadCounts[position]));
+ } else {
+ textView.setVisibility(View.INVISIBLE);
+ iconView.setContentDescription(getResources().getString(R.string.tab_title, tabTitle));
+ }
+ tabView = layout;
+ } else {
+ final TextView textView = new TextView(getContext());
+ textView.setText(tabTitle);
+ textView.setBackgroundResource(R.drawable.view_pager_tab_background);
+
+ // Assign various text appearance related attributes to child views.
+ if (mTextStyle > 0) {
+ textView.setTypeface(textView.getTypeface(), mTextStyle);
+ }
+ if (mTextSize > 0) {
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
+ }
+ if (mTextColor != null) {
+ textView.setTextColor(mTextColor);
+ }
+ textView.setAllCaps(mTextAllCaps);
+ textView.setGravity(Gravity.CENTER);
+
+ tabView = textView;
+ }
+
+ tabView.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mPager.setCurrentItem(getRtlPosition(position));
+ }
+ });
+
+ tabView.setOnLongClickListener(new OnTabLongClickListener(position));
+
+ tabView.setPadding(mSidePadding, 0, mSidePadding, 0);
+
+ mTabStrip.addView(
+ tabView,
+ position,
+ new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT, 1));
+
+ // Default to the first child being selected
+ if (position == 0) {
+ mPrevSelected = 0;
+ tabView.setSelected(true);
+ }
+ }
+
+ /**
+ * Remove a tab at a certain index.
+ *
+ * @param index The index of the tab view we wish to remove.
+ */
+ public void removeTab(int index) {
+ View view = mTabStrip.getChildAt(index);
+ if (view != null) {
+ mTabStrip.removeView(view);
+ }
+ }
+
+ /**
+ * Refresh a tab at a certain index by removing it and reconstructing it.
+ *
+ * @param index The index of the tab view we wish to update.
+ */
+ public void updateTab(int index) {
+ removeTab(index);
+
+ if (index < mPager.getAdapter().getCount()) {
+ addTab(mPager.getAdapter().getPageTitle(index), index);
+ }
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ position = getRtlPosition(position);
+ int tabStripChildCount = mTabStrip.getChildCount();
+ if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+ return;
+ }
+
+ mTabStrip.onPageScrolled(position, positionOffset, positionOffsetPixels);
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ position = getRtlPosition(position);
+ int tabStripChildCount = mTabStrip.getChildCount();
+ if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+ return;
+ }
+
+ if (mPrevSelected >= 0 && mPrevSelected < tabStripChildCount) {
+ mTabStrip.getChildAt(mPrevSelected).setSelected(false);
+ }
+ final View selectedChild = mTabStrip.getChildAt(position);
+ selectedChild.setSelected(true);
+
+ // Update scroll position
+ final int scrollPos = selectedChild.getLeft() - (getWidth() - selectedChild.getWidth()) / 2;
+ smoothScrollTo(scrollPos, 0);
+ mPrevSelected = position;
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {}
+
+ private int getRtlPosition(int position) {
+ if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ return mTabStrip.getChildCount() - 1 - position;
+ }
+ return position;
+ }
+
+ /** Simulates actionbar tab behavior by showing a toast with the tab title when long clicked. */
+ private class OnTabLongClickListener implements OnLongClickListener {
+
+ final int mPosition;
+
+ public OnTabLongClickListener(int position) {
+ mPosition = position;
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ final int[] screenPos = new int[2];
+ getLocationOnScreen(screenPos);
+
+ final Context context = getContext();
+ final int width = getWidth();
+ final int height = getHeight();
+ final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
+
+ Toast toast =
+ Toast.makeText(context, mPager.getAdapter().getPageTitle(mPosition), Toast.LENGTH_SHORT);
+
+ // Show the toast under the tab
+ toast.setGravity(
+ Gravity.TOP | Gravity.CENTER_HORIZONTAL,
+ (screenPos[0] + width / 2) - screenWidth / 2,
+ screenPos[1] + height);
+
+ toast.show();
+ return true;
+ }
+ }
+}
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:
+ *
+ * <ul>
+ * <li>Mobile network
+ * <li>Location manager
+ * <li>SIM's country
+ * <li>User's default locale
+ * </ul>
+ *
+ * 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<Address> 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/AccountTypeManager.java b/java/com/android/contacts/common/model/AccountTypeManager.java
new file mode 100644
index 000000000..f225ff6ac
--- /dev/null
+++ b/java/com/android/contacts/common/model/AccountTypeManager.java
@@ -0,0 +1,813 @@
+/*
+ * Copyright (C) 2009 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.model;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorDescription;
+import android.accounts.OnAccountsUpdateListener;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SyncAdapterType;
+import android.content.SyncStatusObserver;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.provider.ContactsContract;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.TimingLogger;
+import com.android.contacts.common.MoreContactUtils;
+import com.android.contacts.common.list.ContactListFilterController;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.AccountTypeWithDataSet;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.account.ExchangeAccountType;
+import com.android.contacts.common.model.account.ExternalAccountType;
+import com.android.contacts.common.model.account.FallbackAccountType;
+import com.android.contacts.common.model.account.GoogleAccountType;
+import com.android.contacts.common.model.account.SamsungAccountType;
+import com.android.contacts.common.model.dataitem.DataKind;
+import com.android.contacts.common.util.Constants;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Singleton holder for all parsed {@link AccountType} available on the system, typically filled
+ * through {@link PackageManager} queries.
+ */
+public abstract class AccountTypeManager {
+
+ static final String TAG = "AccountTypeManager";
+
+ private static final Object mInitializationLock = new Object();
+ private static AccountTypeManager mAccountTypeManager;
+
+ /**
+ * Requests the singleton instance of {@link AccountTypeManager} with data bound from the
+ * available authenticators. This method can safely be called from the UI thread.
+ */
+ public static AccountTypeManager getInstance(Context context) {
+ synchronized (mInitializationLock) {
+ if (mAccountTypeManager == null) {
+ context = context.getApplicationContext();
+ mAccountTypeManager = new AccountTypeManagerImpl(context);
+ }
+ }
+ return mAccountTypeManager;
+ }
+
+ /**
+ * Set the instance of account type manager. This is only for and should only be used by unit
+ * tests. While having this method is not ideal, it's simpler than the alternative of holding this
+ * as a service in the ContactsApplication context class.
+ *
+ * @param mockManager The mock AccountTypeManager.
+ */
+ public static void setInstanceForTest(AccountTypeManager mockManager) {
+ synchronized (mInitializationLock) {
+ mAccountTypeManager = mockManager;
+ }
+ }
+
+ /**
+ * Returns the list of all accounts (if contactWritableOnly is false) or just the list of contact
+ * writable accounts (if contactWritableOnly is true).
+ */
+ // TODO: Consider splitting this into getContactWritableAccounts() and getAllAccounts()
+ public abstract List<AccountWithDataSet> getAccounts(boolean contactWritableOnly);
+
+ /** Returns the list of accounts that are group writable. */
+ public abstract List<AccountWithDataSet> getGroupWritableAccounts();
+
+ public abstract AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet);
+
+ public final AccountType getAccountType(String accountType, String dataSet) {
+ return getAccountType(AccountTypeWithDataSet.get(accountType, dataSet));
+ }
+
+ public final AccountType getAccountTypeForAccount(AccountWithDataSet account) {
+ if (account != null) {
+ return getAccountType(account.getAccountTypeWithDataSet());
+ }
+ return getAccountType(null, null);
+ }
+
+ /**
+ * @return Unmodifiable map from {@link AccountTypeWithDataSet}s to {@link AccountType}s which
+ * support the "invite" feature and have one or more account.
+ * <p>This is a filtered down and more "usable" list compared to {@link
+ * #getAllInvitableAccountTypes}, where usable is defined as: (1) making sure that the app
+ * that contributed the account type is not disabled (in order to avoid presenting the user
+ * with an option that does nothing), and (2) that there is at least one raw contact with that
+ * account type in the database (assuming that the user probably doesn't use that account
+ * type).
+ * <p>Warning: Don't use on the UI thread because this can scan the database.
+ */
+ public abstract Map<AccountTypeWithDataSet, AccountType> getUsableInvitableAccountTypes();
+
+ /**
+ * Find the best {@link DataKind} matching the requested {@link AccountType#accountType}, {@link
+ * AccountType#dataSet}, and {@link DataKind#mimeType}. If no direct match found, we try searching
+ * {@link FallbackAccountType}.
+ */
+ public DataKind getKindOrFallback(AccountType type, String mimeType) {
+ return type == null ? null : type.getKindForMimetype(mimeType);
+ }
+
+ /**
+ * Returns all registered {@link AccountType}s, including extension ones.
+ *
+ * @param contactWritableOnly if true, it only returns ones that support writing contacts.
+ */
+ public abstract List<AccountType> getAccountTypes(boolean contactWritableOnly);
+
+ /**
+ * @param contactWritableOnly if true, it only returns ones that support writing contacts.
+ * @return true when this instance contains the given account.
+ */
+ public boolean contains(AccountWithDataSet account, boolean contactWritableOnly) {
+ for (AccountWithDataSet account_2 : getAccounts(false)) {
+ if (account.equals(account_2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+class AccountTypeManagerImpl extends AccountTypeManager
+ implements OnAccountsUpdateListener, SyncStatusObserver {
+
+ private static final Map<AccountTypeWithDataSet, AccountType>
+ EMPTY_UNMODIFIABLE_ACCOUNT_TYPE_MAP =
+ Collections.unmodifiableMap(new HashMap<AccountTypeWithDataSet, AccountType>());
+
+ /**
+ * A sample contact URI used to test whether any activities will respond to an invitable intent
+ * with the given URI as the intent data. This doesn't need to be specific to a real contact
+ * because an app that intercepts the intent should probably do so for all types of contact URIs.
+ */
+ private static final Uri SAMPLE_CONTACT_URI = ContactsContract.Contacts.getLookupUri(1, "xxx");
+
+ private static final int MESSAGE_LOAD_DATA = 0;
+ private static final int MESSAGE_PROCESS_BROADCAST_INTENT = 1;
+ private static final Comparator<AccountWithDataSet> ACCOUNT_COMPARATOR =
+ new Comparator<AccountWithDataSet>() {
+ @Override
+ public int compare(AccountWithDataSet a, AccountWithDataSet b) {
+ if (Objects.equals(a.name, b.name)
+ && Objects.equals(a.type, b.type)
+ && Objects.equals(a.dataSet, b.dataSet)) {
+ return 0;
+ } else if (b.name == null || b.type == null) {
+ return -1;
+ } else if (a.name == null || a.type == null) {
+ return 1;
+ } else {
+ int diff = a.name.compareTo(b.name);
+ if (diff != 0) {
+ return diff;
+ }
+ diff = a.type.compareTo(b.type);
+ if (diff != 0) {
+ return diff;
+ }
+
+ // Accounts without data sets get sorted before those that have them.
+ if (a.dataSet != null) {
+ return b.dataSet == null ? 1 : a.dataSet.compareTo(b.dataSet);
+ } else {
+ return -1;
+ }
+ }
+ }
+ };
+ private final InvitableAccountTypeCache mInvitableAccountTypeCache;
+ /**
+ * The boolean value is equal to true if the {@link InvitableAccountTypeCache} has been
+ * initialized. False otherwise.
+ */
+ private final AtomicBoolean mInvitablesCacheIsInitialized = new AtomicBoolean(false);
+ /**
+ * The boolean value is equal to true if the {@link FindInvitablesTask} is still executing. False
+ * otherwise.
+ */
+ private final AtomicBoolean mInvitablesTaskIsRunning = new AtomicBoolean(false);
+
+ private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+ private Context mContext;
+ private final Runnable mCheckFilterValidityRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ ContactListFilterController.getInstance(mContext).checkFilterValidity(true);
+ }
+ };
+ private AccountManager mAccountManager;
+ private AccountType mFallbackAccountType;
+ private List<AccountWithDataSet> mAccounts = new ArrayList<>();
+ private List<AccountWithDataSet> mContactWritableAccounts = new ArrayList<>();
+ private List<AccountWithDataSet> mGroupWritableAccounts = new ArrayList<>();
+ private Map<AccountTypeWithDataSet, AccountType> mAccountTypesWithDataSets = new ArrayMap<>();
+ private Map<AccountTypeWithDataSet, AccountType> mInvitableAccountTypes =
+ EMPTY_UNMODIFIABLE_ACCOUNT_TYPE_MAP;
+ private HandlerThread mListenerThread;
+ private Handler mListenerHandler;
+ private BroadcastReceiver mBroadcastReceiver =
+ new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Message msg = mListenerHandler.obtainMessage(MESSAGE_PROCESS_BROADCAST_INTENT, intent);
+ mListenerHandler.sendMessage(msg);
+ }
+ };
+ /* A latch that ensures that asynchronous initialization completes before data is used */
+ private volatile CountDownLatch mInitializationLatch = new CountDownLatch(1);
+
+ /** Internal constructor that only performs initial parsing. */
+ public AccountTypeManagerImpl(Context context) {
+ mContext = context;
+ mFallbackAccountType = new FallbackAccountType(context);
+
+ mAccountManager = AccountManager.get(mContext);
+
+ mListenerThread = new HandlerThread("AccountChangeListener");
+ mListenerThread.start();
+ mListenerHandler =
+ new Handler(mListenerThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_LOAD_DATA:
+ loadAccountsInBackground();
+ break;
+ case MESSAGE_PROCESS_BROADCAST_INTENT:
+ processBroadcastIntent((Intent) msg.obj);
+ break;
+ }
+ }
+ };
+
+ mInvitableAccountTypeCache = new InvitableAccountTypeCache();
+
+ // Request updates when packages or accounts change
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+ IntentFilter sdFilter = new IntentFilter();
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+
+ // Request updates when locale is changed so that the order of each field will
+ // be able to be changed on the locale change.
+ filter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+
+ mAccountManager.addOnAccountsUpdatedListener(this, mListenerHandler, false);
+
+ ContentResolver.addStatusChangeListener(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, this);
+
+ mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
+ }
+
+ /**
+ * Find a specific {@link AuthenticatorDescription} in the provided list that matches the given
+ * account type.
+ */
+ protected static AuthenticatorDescription findAuthenticator(
+ AuthenticatorDescription[] auths, String accountType) {
+ for (AuthenticatorDescription auth : auths) {
+ if (accountType.equals(auth.type)) {
+ return auth;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return all {@link AccountType}s with at least one account which supports "invite", i.e. its
+ * {@link AccountType#getInviteContactActivityClassName()} is not empty.
+ */
+ @VisibleForTesting
+ static Map<AccountTypeWithDataSet, AccountType> findAllInvitableAccountTypes(
+ Context context,
+ Collection<AccountWithDataSet> accounts,
+ Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet) {
+ Map<AccountTypeWithDataSet, AccountType> result = new ArrayMap<>();
+ for (AccountWithDataSet account : accounts) {
+ AccountTypeWithDataSet accountTypeWithDataSet = account.getAccountTypeWithDataSet();
+ AccountType type = accountTypesByTypeAndDataSet.get(accountTypeWithDataSet);
+ if (type == null) {
+ continue; // just in case
+ }
+ if (result.containsKey(accountTypeWithDataSet)) {
+ continue;
+ }
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(
+ TAG,
+ "Type "
+ + accountTypeWithDataSet
+ + " inviteClass="
+ + type.getInviteContactActivityClassName());
+ }
+ if (!TextUtils.isEmpty(type.getInviteContactActivityClassName())) {
+ result.put(accountTypeWithDataSet, type);
+ }
+ }
+ return Collections.unmodifiableMap(result);
+ }
+
+ @Override
+ public void onStatusChanged(int which) {
+ mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
+ }
+
+ public void processBroadcastIntent(Intent intent) {
+ mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
+ }
+
+ /* This notification will arrive on the background thread */
+ public void onAccountsUpdated(Account[] accounts) {
+ // Refresh to catch any changed accounts
+ loadAccountsInBackground();
+ }
+
+ /**
+ * Returns instantly if accounts and account types have already been loaded. Otherwise waits for
+ * the background thread to complete the loading.
+ */
+ void ensureAccountsLoaded() {
+ CountDownLatch latch = mInitializationLatch;
+ if (latch == null) {
+ return;
+ }
+ while (true) {
+ try {
+ latch.await();
+ return;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ /**
+ * Loads account list and corresponding account types (potentially with data sets). Always called
+ * on a background thread.
+ */
+ protected void loadAccountsInBackground() {
+ if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
+ Log.d(Constants.PERFORMANCE_TAG, "AccountTypeManager.loadAccountsInBackground start");
+ }
+ TimingLogger timings = new TimingLogger(TAG, "loadAccountsInBackground");
+ final long startTime = SystemClock.currentThreadTimeMillis();
+ final long startTimeWall = SystemClock.elapsedRealtime();
+
+ // Account types, keyed off the account type and data set concatenation.
+ final Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet = new ArrayMap<>();
+
+ // The same AccountTypes, but keyed off {@link RawContacts#ACCOUNT_TYPE}. Since there can
+ // be multiple account types (with different data sets) for the same type of account, each
+ // type string may have multiple AccountType entries.
+ final Map<String, List<AccountType>> accountTypesByType = new ArrayMap<>();
+
+ final List<AccountWithDataSet> allAccounts = new ArrayList<>();
+ final List<AccountWithDataSet> contactWritableAccounts = new ArrayList<>();
+ final List<AccountWithDataSet> groupWritableAccounts = new ArrayList<>();
+ final Set<String> extensionPackages = new HashSet<>();
+
+ final AccountManager am = mAccountManager;
+
+ final SyncAdapterType[] syncs = ContentResolver.getSyncAdapterTypes();
+ final AuthenticatorDescription[] auths = am.getAuthenticatorTypes();
+
+ // First process sync adapters to find any that provide contact data.
+ for (SyncAdapterType sync : syncs) {
+ if (!ContactsContract.AUTHORITY.equals(sync.authority)) {
+ // Skip sync adapters that don't provide contact data.
+ continue;
+ }
+
+ // Look for the formatting details provided by each sync
+ // adapter, using the authenticator to find general resources.
+ final String type = sync.accountType;
+ final AuthenticatorDescription auth = findAuthenticator(auths, type);
+ if (auth == null) {
+ Log.w(TAG, "No authenticator found for type=" + type + ", ignoring it.");
+ continue;
+ }
+
+ AccountType accountType;
+ if (GoogleAccountType.ACCOUNT_TYPE.equals(type)) {
+ accountType = new GoogleAccountType(mContext, auth.packageName);
+ } else if (ExchangeAccountType.isExchangeType(type)) {
+ accountType = new ExchangeAccountType(mContext, auth.packageName, type);
+ } else if (SamsungAccountType.isSamsungAccountType(mContext, type, auth.packageName)) {
+ accountType = new SamsungAccountType(mContext, auth.packageName, type);
+ } else {
+ Log.d(
+ TAG, "Registering external account type=" + type + ", packageName=" + auth.packageName);
+ accountType = new ExternalAccountType(mContext, auth.packageName, false);
+ }
+ if (!accountType.isInitialized()) {
+ if (accountType.isEmbedded()) {
+ throw new IllegalStateException(
+ "Problem initializing embedded type " + accountType.getClass().getCanonicalName());
+ } else {
+ // Skip external account types that couldn't be initialized.
+ continue;
+ }
+ }
+
+ accountType.accountType = auth.type;
+ accountType.titleRes = auth.labelId;
+ accountType.iconRes = auth.iconId;
+
+ addAccountType(accountType, accountTypesByTypeAndDataSet, accountTypesByType);
+
+ // Check to see if the account type knows of any other non-sync-adapter packages
+ // that may provide other data sets of contact data.
+ extensionPackages.addAll(accountType.getExtensionPackageNames());
+ }
+
+ // If any extension packages were specified, process them as well.
+ if (!extensionPackages.isEmpty()) {
+ Log.d(TAG, "Registering " + extensionPackages.size() + " extension packages");
+ for (String extensionPackage : extensionPackages) {
+ ExternalAccountType accountType = new ExternalAccountType(mContext, extensionPackage, true);
+ if (!accountType.isInitialized()) {
+ // Skip external account types that couldn't be initialized.
+ continue;
+ }
+ if (!accountType.hasContactsMetadata()) {
+ Log.w(
+ TAG,
+ "Skipping extension package "
+ + extensionPackage
+ + " because"
+ + " it doesn't have the CONTACTS_STRUCTURE metadata");
+ continue;
+ }
+ if (TextUtils.isEmpty(accountType.accountType)) {
+ Log.w(
+ TAG,
+ "Skipping extension package "
+ + extensionPackage
+ + " because"
+ + " the CONTACTS_STRUCTURE metadata doesn't have the accountType"
+ + " attribute");
+ continue;
+ }
+ Log.d(
+ TAG,
+ "Registering extension package account type="
+ + accountType.accountType
+ + ", dataSet="
+ + accountType.dataSet
+ + ", packageName="
+ + extensionPackage);
+
+ addAccountType(accountType, accountTypesByTypeAndDataSet, accountTypesByType);
+ }
+ }
+ timings.addSplit("Loaded account types");
+
+ // Map in accounts to associate the account names with each account type entry.
+ Account[] accounts = mAccountManager.getAccounts();
+ for (Account account : accounts) {
+ boolean syncable = ContentResolver.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
+
+ if (syncable) {
+ List<AccountType> accountTypes = accountTypesByType.get(account.type);
+ if (accountTypes != null) {
+ // Add an account-with-data-set entry for each account type that is
+ // authenticated by this account.
+ for (AccountType accountType : accountTypes) {
+ AccountWithDataSet accountWithDataSet =
+ new AccountWithDataSet(account.name, account.type, accountType.dataSet);
+ allAccounts.add(accountWithDataSet);
+ if (accountType.areContactsWritable()) {
+ contactWritableAccounts.add(accountWithDataSet);
+ }
+ if (accountType.isGroupMembershipEditable()) {
+ groupWritableAccounts.add(accountWithDataSet);
+ }
+ }
+ }
+ }
+ }
+
+ Collections.sort(allAccounts, ACCOUNT_COMPARATOR);
+ Collections.sort(contactWritableAccounts, ACCOUNT_COMPARATOR);
+ Collections.sort(groupWritableAccounts, ACCOUNT_COMPARATOR);
+
+ timings.addSplit("Loaded accounts");
+
+ synchronized (this) {
+ mAccountTypesWithDataSets = accountTypesByTypeAndDataSet;
+ mAccounts = allAccounts;
+ mContactWritableAccounts = contactWritableAccounts;
+ mGroupWritableAccounts = groupWritableAccounts;
+ mInvitableAccountTypes =
+ findAllInvitableAccountTypes(mContext, allAccounts, accountTypesByTypeAndDataSet);
+ }
+
+ timings.dumpToLog();
+ final long endTimeWall = SystemClock.elapsedRealtime();
+ final long endTime = SystemClock.currentThreadTimeMillis();
+
+ Log.i(
+ TAG,
+ "Loaded meta-data for "
+ + mAccountTypesWithDataSets.size()
+ + " account types, "
+ + mAccounts.size()
+ + " accounts in "
+ + (endTimeWall - startTimeWall)
+ + "ms(wall) "
+ + (endTime - startTime)
+ + "ms(cpu)");
+
+ if (mInitializationLatch != null) {
+ mInitializationLatch.countDown();
+ mInitializationLatch = null;
+ }
+ if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
+ Log.d(Constants.PERFORMANCE_TAG, "AccountTypeManager.loadAccountsInBackground finish");
+ }
+
+ // Check filter validity since filter may become obsolete after account update. It must be
+ // done from UI thread.
+ mMainThreadHandler.post(mCheckFilterValidityRunnable);
+ }
+
+ // Bookkeeping method for tracking the known account types in the given maps.
+ private void addAccountType(
+ AccountType accountType,
+ Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet,
+ Map<String, List<AccountType>> accountTypesByType) {
+ accountTypesByTypeAndDataSet.put(accountType.getAccountTypeAndDataSet(), accountType);
+ List<AccountType> accountsForType = accountTypesByType.get(accountType.accountType);
+ if (accountsForType == null) {
+ accountsForType = new ArrayList<>();
+ }
+ accountsForType.add(accountType);
+ accountTypesByType.put(accountType.accountType, accountsForType);
+ }
+
+ /** Return list of all known, contact writable {@link AccountWithDataSet}'s. */
+ @Override
+ public List<AccountWithDataSet> getAccounts(boolean contactWritableOnly) {
+ ensureAccountsLoaded();
+ return contactWritableOnly ? mContactWritableAccounts : mAccounts;
+ }
+
+ /** Return the list of all known, group writable {@link AccountWithDataSet}'s. */
+ public List<AccountWithDataSet> getGroupWritableAccounts() {
+ ensureAccountsLoaded();
+ return mGroupWritableAccounts;
+ }
+
+ /**
+ * Find the best {@link DataKind} matching the requested {@link AccountType#accountType}, {@link
+ * AccountType#dataSet}, and {@link DataKind#mimeType}. If no direct match found, we try searching
+ * {@link FallbackAccountType}.
+ */
+ @Override
+ public DataKind getKindOrFallback(AccountType type, String mimeType) {
+ ensureAccountsLoaded();
+ DataKind kind = null;
+
+ // Try finding account type and kind matching request
+ if (type != null) {
+ kind = type.getKindForMimetype(mimeType);
+ }
+
+ if (kind == null) {
+ // Nothing found, so try fallback as last resort
+ kind = mFallbackAccountType.getKindForMimetype(mimeType);
+ }
+
+ if (kind == null) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Unknown type=" + type + ", mime=" + mimeType);
+ }
+ }
+
+ return kind;
+ }
+
+ /** Return {@link AccountType} for the given account type and data set. */
+ @Override
+ public AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet) {
+ ensureAccountsLoaded();
+ synchronized (this) {
+ AccountType type = mAccountTypesWithDataSets.get(accountTypeWithDataSet);
+ return type != null ? type : mFallbackAccountType;
+ }
+ }
+
+ /**
+ * @return Unmodifiable map from {@link AccountTypeWithDataSet}s to {@link AccountType}s which
+ * support the "invite" feature and have one or more account. This is an unfiltered list. See
+ * {@link #getUsableInvitableAccountTypes()}.
+ */
+ private Map<AccountTypeWithDataSet, AccountType> getAllInvitableAccountTypes() {
+ ensureAccountsLoaded();
+ return mInvitableAccountTypes;
+ }
+
+ @Override
+ public Map<AccountTypeWithDataSet, AccountType> getUsableInvitableAccountTypes() {
+ ensureAccountsLoaded();
+ // Since this method is not thread-safe, it's possible for multiple threads to encounter
+ // the situation where (1) the cache has not been initialized yet or
+ // (2) an async task to refresh the account type list in the cache has already been
+ // started. Hence we use {@link AtomicBoolean}s and return cached values immediately
+ // while we compute the actual result in the background. We use this approach instead of
+ // using "synchronized" because computing the account type list involves a DB read, and
+ // can potentially cause a deadlock situation if this method is called from code which
+ // holds the DB lock. The trade-off of potentially having an incorrect list of invitable
+ // account types for a short period of time seems more manageable than enforcing the
+ // context in which this method is called.
+
+ // Computing the list of usable invitable account types is done on the fly as requested.
+ // If this method has never been called before, then block until the list has been computed.
+ if (!mInvitablesCacheIsInitialized.get()) {
+ mInvitableAccountTypeCache.setCachedValue(findUsableInvitableAccountTypes(mContext));
+ mInvitablesCacheIsInitialized.set(true);
+ } else {
+ // Otherwise, there is a value in the cache. If the value has expired and
+ // an async task has not already been started by another thread, then kick off a new
+ // async task to compute the list.
+ if (mInvitableAccountTypeCache.isExpired()
+ && mInvitablesTaskIsRunning.compareAndSet(false, true)) {
+ new FindInvitablesTask().execute();
+ }
+ }
+
+ return mInvitableAccountTypeCache.getCachedValue();
+ }
+
+ /**
+ * Return all usable {@link AccountType}s that support the "invite" feature from the list of all
+ * potential invitable account types (retrieved from {@link #getAllInvitableAccountTypes}). A
+ * usable invitable account type means: (1) there is at least 1 raw contact in the database with
+ * that account type, and (2) the app contributing the account type is not disabled.
+ *
+ * <p>Warning: Don't use on the UI thread because this can scan the database.
+ */
+ private Map<AccountTypeWithDataSet, AccountType> findUsableInvitableAccountTypes(
+ Context context) {
+ Map<AccountTypeWithDataSet, AccountType> allInvitables = getAllInvitableAccountTypes();
+ if (allInvitables.isEmpty()) {
+ return EMPTY_UNMODIFIABLE_ACCOUNT_TYPE_MAP;
+ }
+
+ final Map<AccountTypeWithDataSet, AccountType> result = new ArrayMap<>();
+ result.putAll(allInvitables);
+
+ final PackageManager packageManager = context.getPackageManager();
+ for (AccountTypeWithDataSet accountTypeWithDataSet : allInvitables.keySet()) {
+ AccountType accountType = allInvitables.get(accountTypeWithDataSet);
+
+ // Make sure that account types don't come from apps that are disabled.
+ Intent invitableIntent = MoreContactUtils.getInvitableIntent(accountType, SAMPLE_CONTACT_URI);
+ if (invitableIntent == null) {
+ result.remove(accountTypeWithDataSet);
+ continue;
+ }
+ ResolveInfo resolveInfo =
+ packageManager.resolveActivity(invitableIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ if (resolveInfo == null) {
+ // If we can't find an activity to start for this intent, then there's no point in
+ // showing this option to the user.
+ result.remove(accountTypeWithDataSet);
+ continue;
+ }
+
+ // Make sure that there is at least 1 raw contact with this account type. This check
+ // is non-trivial and should not be done on the UI thread.
+ if (!accountTypeWithDataSet.hasData(context)) {
+ result.remove(accountTypeWithDataSet);
+ }
+ }
+
+ return Collections.unmodifiableMap(result);
+ }
+
+ @Override
+ public List<AccountType> getAccountTypes(boolean contactWritableOnly) {
+ ensureAccountsLoaded();
+ final List<AccountType> accountTypes = new ArrayList<>();
+ synchronized (this) {
+ for (AccountType type : mAccountTypesWithDataSets.values()) {
+ if (!contactWritableOnly || type.areContactsWritable()) {
+ accountTypes.add(type);
+ }
+ }
+ }
+ return accountTypes;
+ }
+
+ /**
+ * This cache holds a list of invitable {@link AccountTypeWithDataSet}s, in the form of a {@link
+ * Map<AccountTypeWithDataSet, AccountType>}. Note that the cached value is valid only for {@link
+ * #TIME_TO_LIVE} milliseconds.
+ */
+ private static final class InvitableAccountTypeCache {
+
+ /**
+ * The cached {@link #mInvitableAccountTypes} list expires after this number of milliseconds has
+ * elapsed.
+ */
+ private static final long TIME_TO_LIVE = 60000;
+
+ private Map<AccountTypeWithDataSet, AccountType> mInvitableAccountTypes;
+
+ private long mTimeLastSet;
+
+ /**
+ * Returns true if the data in this cache is stale and needs to be refreshed. Returns false
+ * otherwise.
+ */
+ public boolean isExpired() {
+ return SystemClock.elapsedRealtime() - mTimeLastSet > TIME_TO_LIVE;
+ }
+
+ /**
+ * Returns the cached value. Note that the caller is responsible for checking {@link
+ * #isExpired()} to ensure that the value is not stale.
+ */
+ public Map<AccountTypeWithDataSet, AccountType> getCachedValue() {
+ return mInvitableAccountTypes;
+ }
+
+ public void setCachedValue(Map<AccountTypeWithDataSet, AccountType> map) {
+ mInvitableAccountTypes = map;
+ mTimeLastSet = SystemClock.elapsedRealtime();
+ }
+ }
+
+ /**
+ * Background task to find all usable {@link AccountType}s that support the "invite" feature from
+ * the list of all potential invitable account types. Once the work is completed, the list of
+ * account types is stored in the {@link AccountTypeManager}'s {@link InvitableAccountTypeCache}.
+ */
+ private class FindInvitablesTask
+ extends AsyncTask<Void, Void, Map<AccountTypeWithDataSet, AccountType>> {
+
+ @Override
+ protected Map<AccountTypeWithDataSet, AccountType> doInBackground(Void... params) {
+ return findUsableInvitableAccountTypes(mContext);
+ }
+
+ @Override
+ protected void onPostExecute(Map<AccountTypeWithDataSet, AccountType> accountTypes) {
+ mInvitableAccountTypeCache.setCachedValue(accountTypes);
+ mInvitablesTaskIsRunning.set(false);
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/model/BuilderWrapper.java b/java/com/android/contacts/common/model/BuilderWrapper.java
new file mode 100644
index 000000000..9c666e59c
--- /dev/null
+++ b/java/com/android/contacts/common/model/BuilderWrapper.java
@@ -0,0 +1,53 @@
+/*
+ * 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.model;
+
+import android.content.ContentProviderOperation.Builder;
+
+/**
+ * This class is created for the purpose of compatibility and make the type of
+ * ContentProviderOperation available on pre-M SDKs. Since ContentProviderOperation is usually
+ * created by Builder and we don’t have access to the type via Builder, so we need to create a
+ * wrapper class for Builder first and include type. Then we could use the builder and the type in
+ * this class to create a wrapper of ContentProviderOperation.
+ */
+public class BuilderWrapper {
+
+ private Builder mBuilder;
+ private int mType;
+
+ public BuilderWrapper(Builder builder, int type) {
+ mBuilder = builder;
+ mType = type;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public void setType(int mType) {
+ this.mType = mType;
+ }
+
+ public Builder getBuilder() {
+ return mBuilder;
+ }
+
+ public void setBuilder(Builder mBuilder) {
+ this.mBuilder = mBuilder;
+ }
+}
diff --git a/java/com/android/contacts/common/model/CPOWrapper.java b/java/com/android/contacts/common/model/CPOWrapper.java
new file mode 100644
index 000000000..4a67e6700
--- /dev/null
+++ b/java/com/android/contacts/common/model/CPOWrapper.java
@@ -0,0 +1,50 @@
+/*
+ * 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.model;
+
+import android.content.ContentProviderOperation;
+
+/**
+ * This class is created for the purpose of compatibility and make the type of
+ * ContentProviderOperation available on pre-M SDKs.
+ */
+public class CPOWrapper {
+
+ private ContentProviderOperation mOperation;
+ private int mType;
+
+ public CPOWrapper(ContentProviderOperation builder, int type) {
+ mOperation = builder;
+ mType = type;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public void setType(int type) {
+ this.mType = type;
+ }
+
+ public ContentProviderOperation getOperation() {
+ return mOperation;
+ }
+
+ public void setOperation(ContentProviderOperation operation) {
+ this.mOperation = operation;
+ }
+}
diff --git a/java/com/android/contacts/common/model/Contact.java b/java/com/android/contacts/common/model/Contact.java
new file mode 100644
index 000000000..ad0b66efe
--- /dev/null
+++ b/java/com/android/contacts/common/model/Contact.java
@@ -0,0 +1,384 @@
+/*
+ * 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.model;
+
+import android.content.ContentValues;
+import android.net.Uri;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.DisplayNameSources;
+import android.support.annotation.VisibleForTesting;
+import com.android.contacts.common.GroupMetaData;
+import com.android.contacts.common.model.account.AccountType;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+
+/**
+ * A Contact represents a single person or logical entity as perceived by the user. The information
+ * about a contact can come from multiple data sources, which are each represented by a RawContact
+ * object. Thus, a Contact is associated with a collection of RawContact objects.
+ *
+ * <p>The aggregation of raw contacts into a single contact is performed automatically, and it is
+ * also possible for users to manually split and join raw contacts into various contacts.
+ *
+ * <p>Only the {@link ContactLoader} class can create a Contact object with various flags to allow
+ * partial loading of contact data. Thus, an instance of this class should be treated as a read-only
+ * object.
+ */
+public class Contact {
+
+ private final Uri mRequestedUri;
+ private final Uri mLookupUri;
+ private final Uri mUri;
+ private final long mDirectoryId;
+ private final String mLookupKey;
+ private final long mId;
+ private final long mNameRawContactId;
+ private final int mDisplayNameSource;
+ private final long mPhotoId;
+ private final String mPhotoUri;
+ private final String mDisplayName;
+ private final String mAltDisplayName;
+ private final String mPhoneticName;
+ private final boolean mStarred;
+ private final Integer mPresence;
+ private final boolean mSendToVoicemail;
+ private final String mCustomRingtone;
+ private final boolean mIsUserProfile;
+ private final Contact.Status mStatus;
+ private final Exception mException;
+ private ImmutableList<RawContact> mRawContacts;
+ private ImmutableList<AccountType> mInvitableAccountTypes;
+ private String mDirectoryDisplayName;
+ private String mDirectoryType;
+ private String mDirectoryAccountType;
+ private String mDirectoryAccountName;
+ private int mDirectoryExportSupport;
+ private ImmutableList<GroupMetaData> mGroups;
+ private byte[] mPhotoBinaryData;
+ /**
+ * Small version of the contact photo loaded from a blob instead of from a file. If a large
+ * contact photo is not available yet, then this has the same value as mPhotoBinaryData.
+ */
+ private byte[] mThumbnailPhotoBinaryData;
+
+ /** Constructor for special results, namely "no contact found" and "error". */
+ private Contact(Uri requestedUri, Contact.Status status, Exception exception) {
+ if (status == Status.ERROR && exception == null) {
+ throw new IllegalArgumentException("ERROR result must have exception");
+ }
+ mStatus = status;
+ mException = exception;
+ mRequestedUri = requestedUri;
+ mLookupUri = null;
+ mUri = null;
+ mDirectoryId = -1;
+ mLookupKey = null;
+ mId = -1;
+ mRawContacts = null;
+ mNameRawContactId = -1;
+ mDisplayNameSource = DisplayNameSources.UNDEFINED;
+ mPhotoId = -1;
+ mPhotoUri = null;
+ mDisplayName = null;
+ mAltDisplayName = null;
+ mPhoneticName = null;
+ mStarred = false;
+ mPresence = null;
+ mInvitableAccountTypes = null;
+ mSendToVoicemail = false;
+ mCustomRingtone = null;
+ mIsUserProfile = false;
+ }
+
+ /** Constructor to call when contact was found */
+ public Contact(
+ Uri requestedUri,
+ Uri uri,
+ Uri lookupUri,
+ long directoryId,
+ String lookupKey,
+ long id,
+ long nameRawContactId,
+ int displayNameSource,
+ long photoId,
+ String photoUri,
+ String displayName,
+ String altDisplayName,
+ String phoneticName,
+ boolean starred,
+ Integer presence,
+ boolean sendToVoicemail,
+ String customRingtone,
+ boolean isUserProfile) {
+ mStatus = Status.LOADED;
+ mException = null;
+ mRequestedUri = requestedUri;
+ mLookupUri = lookupUri;
+ mUri = uri;
+ mDirectoryId = directoryId;
+ mLookupKey = lookupKey;
+ mId = id;
+ mRawContacts = null;
+ mNameRawContactId = nameRawContactId;
+ mDisplayNameSource = displayNameSource;
+ mPhotoId = photoId;
+ mPhotoUri = photoUri;
+ mDisplayName = displayName;
+ mAltDisplayName = altDisplayName;
+ mPhoneticName = phoneticName;
+ mStarred = starred;
+ mPresence = presence;
+ mInvitableAccountTypes = null;
+ mSendToVoicemail = sendToVoicemail;
+ mCustomRingtone = customRingtone;
+ mIsUserProfile = isUserProfile;
+ }
+
+ public Contact(Uri requestedUri, Contact from) {
+ mRequestedUri = requestedUri;
+
+ mStatus = from.mStatus;
+ mException = from.mException;
+ mLookupUri = from.mLookupUri;
+ mUri = from.mUri;
+ mDirectoryId = from.mDirectoryId;
+ mLookupKey = from.mLookupKey;
+ mId = from.mId;
+ mNameRawContactId = from.mNameRawContactId;
+ mDisplayNameSource = from.mDisplayNameSource;
+ mPhotoId = from.mPhotoId;
+ mPhotoUri = from.mPhotoUri;
+ mDisplayName = from.mDisplayName;
+ mAltDisplayName = from.mAltDisplayName;
+ mPhoneticName = from.mPhoneticName;
+ mStarred = from.mStarred;
+ mPresence = from.mPresence;
+ mRawContacts = from.mRawContacts;
+ mInvitableAccountTypes = from.mInvitableAccountTypes;
+
+ mDirectoryDisplayName = from.mDirectoryDisplayName;
+ mDirectoryType = from.mDirectoryType;
+ mDirectoryAccountType = from.mDirectoryAccountType;
+ mDirectoryAccountName = from.mDirectoryAccountName;
+ mDirectoryExportSupport = from.mDirectoryExportSupport;
+
+ mGroups = from.mGroups;
+
+ mPhotoBinaryData = from.mPhotoBinaryData;
+ mSendToVoicemail = from.mSendToVoicemail;
+ mCustomRingtone = from.mCustomRingtone;
+ mIsUserProfile = from.mIsUserProfile;
+ }
+
+ public static Contact forError(Uri requestedUri, Exception exception) {
+ return new Contact(requestedUri, Status.ERROR, exception);
+ }
+
+ public static Contact forNotFound(Uri requestedUri) {
+ return new Contact(requestedUri, Status.NOT_FOUND, null);
+ }
+
+ /** @param exportSupport See {@link Directory#EXPORT_SUPPORT}. */
+ public void setDirectoryMetaData(
+ String displayName,
+ String directoryType,
+ String accountType,
+ String accountName,
+ int exportSupport) {
+ mDirectoryDisplayName = displayName;
+ mDirectoryType = directoryType;
+ mDirectoryAccountType = accountType;
+ mDirectoryAccountName = accountName;
+ mDirectoryExportSupport = exportSupport;
+ }
+
+ /**
+ * Returns the URI for the contact that contains both the lookup key and the ID. This is the best
+ * URI to reference a contact. For directory contacts, this is the same a the URI as returned by
+ * {@link #getUri()}
+ */
+ public Uri getLookupUri() {
+ return mLookupUri;
+ }
+
+ public String getLookupKey() {
+ return mLookupKey;
+ }
+
+ /**
+ * Returns the contact Uri that was passed to the provider to make the query. This is the same as
+ * the requested Uri, unless the requested Uri doesn't specify a Contact: If it either references
+ * a Raw-Contact or a Person (a pre-Eclair style Uri), this Uri will always reference the full
+ * aggregate contact.
+ */
+ public Uri getUri() {
+ return mUri;
+ }
+
+ /** Returns the contact ID. */
+ @VisibleForTesting
+ public long getId() {
+ return mId;
+ }
+
+ /**
+ * @return true when an exception happened during loading, in which case {@link #getException}
+ * returns the actual exception object.
+ */
+ public boolean isError() {
+ return mStatus == Status.ERROR;
+ }
+
+ public Exception getException() {
+ return mException;
+ }
+
+ /** @return true if the specified contact is successfully loaded. */
+ public boolean isLoaded() {
+ return mStatus == Status.LOADED;
+ }
+
+ public long getNameRawContactId() {
+ return mNameRawContactId;
+ }
+
+ public int getDisplayNameSource() {
+ return mDisplayNameSource;
+ }
+
+ public long getPhotoId() {
+ return mPhotoId;
+ }
+
+ public String getPhotoUri() {
+ return mPhotoUri;
+ }
+
+ public String getDisplayName() {
+ return mDisplayName;
+ }
+
+ public boolean getStarred() {
+ return mStarred;
+ }
+
+ public Integer getPresence() {
+ return mPresence;
+ }
+
+ /**
+ * This can return non-null invitable account types only if the {@link ContactLoader} was
+ * configured to load invitable account types in its constructor.
+ */
+ public ImmutableList<AccountType> getInvitableAccountTypes() {
+ return mInvitableAccountTypes;
+ }
+
+ /* package */ void setInvitableAccountTypes(ImmutableList<AccountType> accountTypes) {
+ mInvitableAccountTypes = accountTypes;
+ }
+
+ public ImmutableList<RawContact> getRawContacts() {
+ return mRawContacts;
+ }
+
+ /* package */ void setRawContacts(ImmutableList<RawContact> rawContacts) {
+ mRawContacts = rawContacts;
+ }
+
+ public long getDirectoryId() {
+ return mDirectoryId;
+ }
+
+ public boolean isDirectoryEntry() {
+ return mDirectoryId != -1
+ && mDirectoryId != Directory.DEFAULT
+ && mDirectoryId != Directory.LOCAL_INVISIBLE;
+ }
+
+ /* package */ void setPhotoBinaryData(byte[] photoBinaryData) {
+ mPhotoBinaryData = photoBinaryData;
+ }
+
+ public byte[] getThumbnailPhotoBinaryData() {
+ return mThumbnailPhotoBinaryData;
+ }
+
+ /* package */ void setThumbnailPhotoBinaryData(byte[] photoBinaryData) {
+ mThumbnailPhotoBinaryData = photoBinaryData;
+ }
+
+ public ArrayList<ContentValues> getContentValues() {
+ if (mRawContacts.size() != 1) {
+ throw new IllegalStateException("Cannot extract content values from an aggregated contact");
+ }
+
+ RawContact rawContact = mRawContacts.get(0);
+ ArrayList<ContentValues> result = rawContact.getContentValues();
+
+ // If the photo was loaded using the URI, create an entry for the photo
+ // binary data.
+ if (mPhotoId == 0 && mPhotoBinaryData != null) {
+ ContentValues photo = new ContentValues();
+ photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
+ photo.put(Photo.PHOTO, mPhotoBinaryData);
+ result.add(photo);
+ }
+
+ return result;
+ }
+
+ /**
+ * This can return non-null group meta-data only if the {@link ContactLoader} was configured to
+ * load group metadata in its constructor.
+ */
+ public ImmutableList<GroupMetaData> getGroupMetaData() {
+ return mGroups;
+ }
+
+ /* package */ void setGroupMetaData(ImmutableList<GroupMetaData> groups) {
+ mGroups = groups;
+ }
+
+ public boolean isUserProfile() {
+ return mIsUserProfile;
+ }
+
+ @Override
+ public String toString() {
+ return "{requested="
+ + mRequestedUri
+ + ",lookupkey="
+ + mLookupKey
+ + ",uri="
+ + mUri
+ + ",status="
+ + mStatus
+ + "}";
+ }
+
+ private enum Status {
+ /** Contact is successfully loaded */
+ LOADED,
+ /** There was an error loading the contact */
+ ERROR,
+ /** Contact is not found */
+ NOT_FOUND,
+ }
+}
diff --git a/java/com/android/contacts/common/model/ContactLoader.java b/java/com/android/contacts/common/model/ContactLoader.java
new file mode 100644
index 000000000..eb16bffcd
--- /dev/null
+++ b/java/com/android/contacts/common/model/ContactLoader.java
@@ -0,0 +1,998 @@
+/*
+ * Copyright (C) 2010 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.model;
+
+import android.content.AsyncTaskLoader;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.Groups;
+import android.provider.ContactsContract.RawContacts;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.GroupMetaData;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.AccountTypeWithDataSet;
+import com.android.contacts.common.model.dataitem.DataItem;
+import com.android.contacts.common.model.dataitem.PhoneDataItem;
+import com.android.contacts.common.model.dataitem.PhotoDataItem;
+import com.android.contacts.common.util.Constants;
+import com.android.contacts.common.util.ContactLoaderUtils;
+import com.android.contacts.common.util.UriUtils;
+import com.android.dialer.compat.CompatUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/** Loads a single Contact and all it constituent RawContacts. */
+public class ContactLoader extends AsyncTaskLoader<Contact> {
+
+ private static final String TAG = ContactLoader.class.getSimpleName();
+
+ /** A short-lived cache that can be set by {@link #cacheResult()} */
+ private static Contact sCachedResult = null;
+
+ private final Uri mRequestedUri;
+ private final Set<Long> mNotifiedRawContactIds = Sets.newHashSet();
+ private Uri mLookupUri;
+ private boolean mLoadGroupMetaData;
+ private boolean mLoadInvitableAccountTypes;
+ private boolean mPostViewNotification;
+ private boolean mComputeFormattedPhoneNumber;
+ private Contact mContact;
+ private ForceLoadContentObserver mObserver;
+
+ public ContactLoader(Context context, Uri lookupUri, boolean postViewNotification) {
+ this(context, lookupUri, false, false, postViewNotification, false);
+ }
+
+ public ContactLoader(
+ Context context,
+ Uri lookupUri,
+ boolean loadGroupMetaData,
+ boolean loadInvitableAccountTypes,
+ boolean postViewNotification,
+ boolean computeFormattedPhoneNumber) {
+ super(context);
+ mLookupUri = lookupUri;
+ mRequestedUri = lookupUri;
+ mLoadGroupMetaData = loadGroupMetaData;
+ mLoadInvitableAccountTypes = loadInvitableAccountTypes;
+ mPostViewNotification = postViewNotification;
+ mComputeFormattedPhoneNumber = computeFormattedPhoneNumber;
+ }
+
+ /**
+ * Parses a {@link Contact} stored as a JSON string in a lookup URI.
+ *
+ * @param lookupUri The contact information to parse .
+ * @return The parsed {@code Contact} information.
+ */
+ public static Contact parseEncodedContactEntity(Uri lookupUri) {
+ try {
+ return loadEncodedContactEntity(lookupUri, lookupUri);
+ } catch (JSONException je) {
+ return null;
+ }
+ }
+
+ private static Contact loadEncodedContactEntity(Uri uri, Uri lookupUri) throws JSONException {
+ final String jsonString = uri.getEncodedFragment();
+ final JSONObject json = new JSONObject(jsonString);
+
+ final long directoryId =
+ Long.valueOf(uri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY));
+
+ final String displayName = json.optString(Contacts.DISPLAY_NAME);
+ final String altDisplayName = json.optString(Contacts.DISPLAY_NAME_ALTERNATIVE, displayName);
+ final int displayNameSource = json.getInt(Contacts.DISPLAY_NAME_SOURCE);
+ final String photoUri = json.optString(Contacts.PHOTO_URI, null);
+ final Contact contact =
+ new Contact(
+ uri,
+ uri,
+ lookupUri,
+ directoryId,
+ null /* lookupKey */,
+ -1 /* id */,
+ -1 /* nameRawContactId */,
+ displayNameSource,
+ 0 /* photoId */,
+ photoUri,
+ displayName,
+ altDisplayName,
+ null /* phoneticName */,
+ false /* starred */,
+ null /* presence */,
+ false /* sendToVoicemail */,
+ null /* customRingtone */,
+ false /* isUserProfile */);
+
+ final String accountName = json.optString(RawContacts.ACCOUNT_NAME, null);
+ final String directoryName = uri.getQueryParameter(Directory.DISPLAY_NAME);
+ if (accountName != null) {
+ final String accountType = json.getString(RawContacts.ACCOUNT_TYPE);
+ contact.setDirectoryMetaData(
+ directoryName,
+ null,
+ accountName,
+ accountType,
+ json.optInt(Directory.EXPORT_SUPPORT, Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY));
+ } else {
+ contact.setDirectoryMetaData(
+ directoryName,
+ null,
+ null,
+ null,
+ json.optInt(Directory.EXPORT_SUPPORT, Directory.EXPORT_SUPPORT_ANY_ACCOUNT));
+ }
+
+ final ContentValues values = new ContentValues();
+ values.put(Data._ID, -1);
+ values.put(Data.CONTACT_ID, -1);
+ final RawContact rawContact = new RawContact(values);
+
+ final JSONObject items = json.getJSONObject(Contacts.CONTENT_ITEM_TYPE);
+ final Iterator keys = items.keys();
+ while (keys.hasNext()) {
+ final String mimetype = (String) keys.next();
+
+ // Could be single object or array.
+ final JSONObject obj = items.optJSONObject(mimetype);
+ if (obj == null) {
+ final JSONArray array = items.getJSONArray(mimetype);
+ for (int i = 0; i < array.length(); i++) {
+ final JSONObject item = array.getJSONObject(i);
+ processOneRecord(rawContact, item, mimetype);
+ }
+ } else {
+ processOneRecord(rawContact, obj, mimetype);
+ }
+ }
+
+ contact.setRawContacts(new ImmutableList.Builder<RawContact>().add(rawContact).build());
+ return contact;
+ }
+
+ private static void processOneRecord(RawContact rawContact, JSONObject item, String mimetype)
+ throws JSONException {
+ final ContentValues itemValues = new ContentValues();
+ itemValues.put(Data.MIMETYPE, mimetype);
+ itemValues.put(Data._ID, -1);
+
+ final Iterator iterator = item.keys();
+ while (iterator.hasNext()) {
+ String name = (String) iterator.next();
+ final Object o = item.get(name);
+ if (o instanceof String) {
+ itemValues.put(name, (String) o);
+ } else if (o instanceof Integer) {
+ itemValues.put(name, (Integer) o);
+ }
+ }
+ rawContact.addDataItemValues(itemValues);
+ }
+
+ @Override
+ public Contact loadInBackground() {
+ Log.e(TAG, "loadInBackground=" + mLookupUri);
+ try {
+ final ContentResolver resolver = getContext().getContentResolver();
+ final Uri uriCurrentFormat = ContactLoaderUtils.ensureIsContactUri(resolver, mLookupUri);
+ final Contact cachedResult = sCachedResult;
+ sCachedResult = null;
+ // Is this the same Uri as what we had before already? In that case, reuse that result
+ final Contact result;
+ final boolean resultIsCached;
+ if (cachedResult != null && UriUtils.areEqual(cachedResult.getLookupUri(), mLookupUri)) {
+ // We are using a cached result from earlier. Below, we should make sure
+ // we are not doing any more network or disc accesses
+ result = new Contact(mRequestedUri, cachedResult);
+ resultIsCached = true;
+ } else {
+ if (uriCurrentFormat.getLastPathSegment().equals(Constants.LOOKUP_URI_ENCODED)) {
+ result = loadEncodedContactEntity(uriCurrentFormat, mLookupUri);
+ } else {
+ result = loadContactEntity(resolver, uriCurrentFormat);
+ }
+ resultIsCached = false;
+ }
+ if (result.isLoaded()) {
+ if (result.isDirectoryEntry()) {
+ if (!resultIsCached) {
+ loadDirectoryMetaData(result);
+ }
+ } else if (mLoadGroupMetaData) {
+ if (result.getGroupMetaData() == null) {
+ loadGroupMetaData(result);
+ }
+ }
+ if (mComputeFormattedPhoneNumber) {
+ computeFormattedPhoneNumbers(result);
+ }
+ if (!resultIsCached) {
+ loadPhotoBinaryData(result);
+ }
+
+ // Note ME profile should never have "Add connection"
+ if (mLoadInvitableAccountTypes && result.getInvitableAccountTypes() == null) {
+ loadInvitableAccountTypes(result);
+ }
+ }
+ return result;
+ } catch (Exception e) {
+ Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
+ return Contact.forError(mRequestedUri, e);
+ }
+ }
+
+ private Contact loadContactEntity(ContentResolver resolver, Uri contactUri) {
+ Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
+ Cursor cursor =
+ resolver.query(entityUri, ContactQuery.COLUMNS, null, null, Contacts.Entity.RAW_CONTACT_ID);
+ if (cursor == null) {
+ Log.e(TAG, "No cursor returned in loadContactEntity");
+ return Contact.forNotFound(mRequestedUri);
+ }
+
+ try {
+ if (!cursor.moveToFirst()) {
+ cursor.close();
+ return Contact.forNotFound(mRequestedUri);
+ }
+
+ // Create the loaded contact starting with the header data.
+ Contact contact = loadContactHeaderData(cursor, contactUri);
+
+ // Fill in the raw contacts, which is wrapped in an Entity and any
+ // status data. Initially, result has empty entities and statuses.
+ long currentRawContactId = -1;
+ RawContact rawContact = null;
+ ImmutableList.Builder<RawContact> rawContactsBuilder =
+ new ImmutableList.Builder<RawContact>();
+ do {
+ long rawContactId = cursor.getLong(ContactQuery.RAW_CONTACT_ID);
+ if (rawContactId != currentRawContactId) {
+ // First time to see this raw contact id, so create a new entity, and
+ // add it to the result's entities.
+ currentRawContactId = rawContactId;
+ rawContact = new RawContact(loadRawContactValues(cursor));
+ rawContactsBuilder.add(rawContact);
+ }
+ if (!cursor.isNull(ContactQuery.DATA_ID)) {
+ ContentValues data = loadDataValues(cursor);
+ rawContact.addDataItemValues(data);
+ }
+ } while (cursor.moveToNext());
+
+ contact.setRawContacts(rawContactsBuilder.build());
+
+ return contact;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Looks for the photo data item in entities. If found, a thumbnail will be stored. A larger photo
+ * will also be stored if available.
+ */
+ private void loadPhotoBinaryData(Contact contactData) {
+ loadThumbnailBinaryData(contactData);
+
+ // Try to load the large photo from a file using the photo URI.
+ String photoUri = contactData.getPhotoUri();
+ if (photoUri != null) {
+ try {
+ final InputStream inputStream;
+ final AssetFileDescriptor fd;
+ final Uri uri = Uri.parse(photoUri);
+ final String scheme = uri.getScheme();
+ if ("http".equals(scheme) || "https".equals(scheme)) {
+ // Support HTTP urls that might come from extended directories
+ inputStream = new URL(photoUri).openStream();
+ fd = null;
+ } else {
+ fd = getContext().getContentResolver().openAssetFileDescriptor(uri, "r");
+ inputStream = fd.createInputStream();
+ }
+ byte[] buffer = new byte[16 * 1024];
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ int size;
+ while ((size = inputStream.read(buffer)) != -1) {
+ baos.write(buffer, 0, size);
+ }
+ contactData.setPhotoBinaryData(baos.toByteArray());
+ } finally {
+ inputStream.close();
+ if (fd != null) {
+ fd.close();
+ }
+ }
+ return;
+ } catch (IOException ioe) {
+ // Just fall back to the case below.
+ }
+ }
+
+ // If we couldn't load from a file, fall back to the data blob.
+ contactData.setPhotoBinaryData(contactData.getThumbnailPhotoBinaryData());
+ }
+
+ private void loadThumbnailBinaryData(Contact contactData) {
+ final long photoId = contactData.getPhotoId();
+ if (photoId <= 0) {
+ // No photo ID
+ return;
+ }
+
+ for (RawContact rawContact : contactData.getRawContacts()) {
+ for (DataItem dataItem : rawContact.getDataItems()) {
+ if (dataItem.getId() == photoId) {
+ if (!(dataItem instanceof PhotoDataItem)) {
+ break;
+ }
+
+ final PhotoDataItem photo = (PhotoDataItem) dataItem;
+ contactData.setThumbnailPhotoBinaryData(photo.getPhoto());
+ break;
+ }
+ }
+ }
+ }
+
+ /** Sets the "invitable" account types to {@link Contact#mInvitableAccountTypes}. */
+ private void loadInvitableAccountTypes(Contact contactData) {
+ final ImmutableList.Builder<AccountType> resultListBuilder =
+ new ImmutableList.Builder<AccountType>();
+ if (!contactData.isUserProfile()) {
+ Map<AccountTypeWithDataSet, AccountType> invitables =
+ AccountTypeManager.getInstance(getContext()).getUsableInvitableAccountTypes();
+ if (!invitables.isEmpty()) {
+ final Map<AccountTypeWithDataSet, AccountType> resultMap = Maps.newHashMap(invitables);
+
+ // Remove the ones that already have a raw contact in the current contact
+ for (RawContact rawContact : contactData.getRawContacts()) {
+ final AccountTypeWithDataSet type =
+ AccountTypeWithDataSet.get(
+ rawContact.getAccountTypeString(), rawContact.getDataSet());
+ resultMap.remove(type);
+ }
+
+ resultListBuilder.addAll(resultMap.values());
+ }
+ }
+
+ // Set to mInvitableAccountTypes
+ contactData.setInvitableAccountTypes(resultListBuilder.build());
+ }
+
+ /** Extracts Contact level columns from the cursor. */
+ private Contact loadContactHeaderData(final Cursor cursor, Uri contactUri) {
+ final String directoryParameter =
+ contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
+ final long directoryId =
+ directoryParameter == null ? Directory.DEFAULT : Long.parseLong(directoryParameter);
+ final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
+ final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
+ final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
+ final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
+ final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
+ final String altDisplayName = cursor.getString(ContactQuery.ALT_DISPLAY_NAME);
+ final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
+ final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
+ final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
+ final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
+ final Integer presence =
+ cursor.isNull(ContactQuery.CONTACT_PRESENCE)
+ ? null
+ : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
+ final boolean sendToVoicemail = cursor.getInt(ContactQuery.SEND_TO_VOICEMAIL) == 1;
+ final String customRingtone = cursor.getString(ContactQuery.CUSTOM_RINGTONE);
+ final boolean isUserProfile = cursor.getInt(ContactQuery.IS_USER_PROFILE) == 1;
+
+ Uri lookupUri;
+ if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
+ lookupUri =
+ ContentUris.withAppendedId(
+ Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
+ } else {
+ lookupUri = contactUri;
+ }
+
+ return new Contact(
+ mRequestedUri,
+ contactUri,
+ lookupUri,
+ directoryId,
+ lookupKey,
+ contactId,
+ nameRawContactId,
+ displayNameSource,
+ photoId,
+ photoUri,
+ displayName,
+ altDisplayName,
+ phoneticName,
+ starred,
+ presence,
+ sendToVoicemail,
+ customRingtone,
+ isUserProfile);
+ }
+
+ /** Extracts RawContact level columns from the cursor. */
+ private ContentValues loadRawContactValues(Cursor cursor) {
+ ContentValues cv = new ContentValues();
+
+ cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
+
+ cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SET);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
+
+ return cv;
+ }
+
+ /** Extracts Data level columns from the cursor. */
+ private ContentValues loadDataValues(Cursor cursor) {
+ ContentValues cv = new ContentValues();
+
+ cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
+
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.TIMES_USED);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.LAST_TIME_USED);
+ if (CompatUtils.isMarshmallowCompatible()) {
+ cursorColumnToContentValues(cursor, cv, ContactQuery.CARRIER_PRESENCE);
+ }
+
+ return cv;
+ }
+
+ private void cursorColumnToContentValues(Cursor cursor, ContentValues values, int index) {
+ switch (cursor.getType(index)) {
+ case Cursor.FIELD_TYPE_NULL:
+ // don't put anything in the content values
+ break;
+ case Cursor.FIELD_TYPE_INTEGER:
+ values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
+ break;
+ case Cursor.FIELD_TYPE_STRING:
+ values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
+ break;
+ case Cursor.FIELD_TYPE_BLOB:
+ values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
+ break;
+ default:
+ throw new IllegalStateException("Invalid or unhandled data type");
+ }
+ }
+
+ private void loadDirectoryMetaData(Contact result) {
+ long directoryId = result.getDirectoryId();
+
+ Cursor cursor =
+ getContext()
+ .getContentResolver()
+ .query(
+ ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
+ DirectoryQuery.COLUMNS,
+ null,
+ null,
+ null);
+ if (cursor == null) {
+ return;
+ }
+ try {
+ if (cursor.moveToFirst()) {
+ final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
+ final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
+ final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
+ final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
+ final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
+ final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
+ String directoryType = null;
+ if (!TextUtils.isEmpty(packageName)) {
+ PackageManager pm = getContext().getPackageManager();
+ try {
+ Resources resources = pm.getResourcesForApplication(packageName);
+ directoryType = resources.getString(typeResourceId);
+ } catch (NameNotFoundException e) {
+ Log.w(
+ TAG, "Contact directory resource not found: " + packageName + "." + typeResourceId);
+ }
+ }
+
+ result.setDirectoryMetaData(
+ displayName, directoryType, accountType, accountName, exportSupport);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Loads groups meta-data for all groups associated with all constituent raw contacts' accounts.
+ */
+ private void loadGroupMetaData(Contact result) {
+ StringBuilder selection = new StringBuilder();
+ ArrayList<String> selectionArgs = new ArrayList<String>();
+ final HashSet<AccountKey> accountsSeen = new HashSet<>();
+ for (RawContact rawContact : result.getRawContacts()) {
+ final String accountName = rawContact.getAccountName();
+ final String accountType = rawContact.getAccountTypeString();
+ final String dataSet = rawContact.getDataSet();
+ final AccountKey accountKey = new AccountKey(accountName, accountType, dataSet);
+ if (accountName != null && accountType != null && !accountsSeen.contains(accountKey)) {
+ accountsSeen.add(accountKey);
+ if (selection.length() != 0) {
+ selection.append(" OR ");
+ }
+ selection.append("(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?");
+ selectionArgs.add(accountName);
+ selectionArgs.add(accountType);
+
+ if (dataSet != null) {
+ selection.append(" AND " + Groups.DATA_SET + "=?");
+ selectionArgs.add(dataSet);
+ } else {
+ selection.append(" AND " + Groups.DATA_SET + " IS NULL");
+ }
+ selection.append(")");
+ }
+ }
+ final ImmutableList.Builder<GroupMetaData> groupListBuilder =
+ new ImmutableList.Builder<GroupMetaData>();
+ final Cursor cursor =
+ getContext()
+ .getContentResolver()
+ .query(
+ Groups.CONTENT_URI,
+ GroupQuery.COLUMNS,
+ selection.toString(),
+ selectionArgs.toArray(new String[0]),
+ null);
+ if (cursor != null) {
+ try {
+ while (cursor.moveToNext()) {
+ final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
+ final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
+ final String dataSet = cursor.getString(GroupQuery.DATA_SET);
+ final long groupId = cursor.getLong(GroupQuery.ID);
+ final String title = cursor.getString(GroupQuery.TITLE);
+ final boolean defaultGroup =
+ !cursor.isNull(GroupQuery.AUTO_ADD) && cursor.getInt(GroupQuery.AUTO_ADD) != 0;
+ final boolean favorites =
+ !cursor.isNull(GroupQuery.FAVORITES) && cursor.getInt(GroupQuery.FAVORITES) != 0;
+
+ groupListBuilder.add(
+ new GroupMetaData(
+ accountName, accountType, dataSet, groupId, title, defaultGroup, favorites));
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ result.setGroupMetaData(groupListBuilder.build());
+ }
+
+ /**
+ * Iterates over all data items that represent phone numbers are tries to calculate a formatted
+ * number. This function can safely be called several times as no unformatted data is overwritten
+ */
+ private void computeFormattedPhoneNumbers(Contact contactData) {
+ final String countryIso = GeoUtil.getCurrentCountryIso(getContext());
+ final ImmutableList<RawContact> rawContacts = contactData.getRawContacts();
+ final int rawContactCount = rawContacts.size();
+ for (int rawContactIndex = 0; rawContactIndex < rawContactCount; rawContactIndex++) {
+ final RawContact rawContact = rawContacts.get(rawContactIndex);
+ final List<DataItem> dataItems = rawContact.getDataItems();
+ final int dataCount = dataItems.size();
+ for (int dataIndex = 0; dataIndex < dataCount; dataIndex++) {
+ final DataItem dataItem = dataItems.get(dataIndex);
+ if (dataItem instanceof PhoneDataItem) {
+ final PhoneDataItem phoneDataItem = (PhoneDataItem) dataItem;
+ phoneDataItem.computeFormattedPhoneNumber(countryIso);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void deliverResult(Contact result) {
+ unregisterObserver();
+
+ // The creator isn't interested in any further updates
+ if (isReset() || result == null) {
+ return;
+ }
+
+ mContact = result;
+
+ if (result.isLoaded()) {
+ mLookupUri = result.getLookupUri();
+
+ if (!result.isDirectoryEntry()) {
+ Log.i(TAG, "Registering content observer for " + mLookupUri);
+ if (mObserver == null) {
+ mObserver = new ForceLoadContentObserver();
+ }
+ getContext().getContentResolver().registerContentObserver(mLookupUri, true, mObserver);
+ }
+
+ if (mPostViewNotification) {
+ // inform the source of the data that this contact is being looked at
+ postViewNotificationToSyncAdapter();
+ }
+ }
+
+ super.deliverResult(mContact);
+ }
+
+ /**
+ * Posts a message to the contributing sync adapters that have opted-in, notifying them that the
+ * contact has just been loaded
+ */
+ private void postViewNotificationToSyncAdapter() {
+ Context context = getContext();
+ for (RawContact rawContact : mContact.getRawContacts()) {
+ final long rawContactId = rawContact.getId();
+ if (mNotifiedRawContactIds.contains(rawContactId)) {
+ continue; // Already notified for this raw contact.
+ }
+ mNotifiedRawContactIds.add(rawContactId);
+ final AccountType accountType = rawContact.getAccountType(context);
+ final String serviceName = accountType.getViewContactNotifyServiceClassName();
+ final String servicePackageName = accountType.getViewContactNotifyServicePackageName();
+ if (!TextUtils.isEmpty(serviceName) && !TextUtils.isEmpty(servicePackageName)) {
+ final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
+ final Intent intent = new Intent();
+ intent.setClassName(servicePackageName, serviceName);
+ intent.setAction(Intent.ACTION_VIEW);
+ intent.setDataAndType(uri, RawContacts.CONTENT_ITEM_TYPE);
+ try {
+ context.startService(intent);
+ } catch (Exception e) {
+ Log.e(TAG, "Error sending message to source-app", e);
+ }
+ }
+ }
+ }
+
+ private void unregisterObserver() {
+ if (mObserver != null) {
+ getContext().getContentResolver().unregisterContentObserver(mObserver);
+ mObserver = null;
+ }
+ }
+
+ public Uri getLookupUri() {
+ return mLookupUri;
+ }
+
+ public void setLookupUri(Uri lookupUri) {
+ mLookupUri = lookupUri;
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mContact != null) {
+ deliverResult(mContact);
+ }
+
+ if (takeContentChanged() || mContact == null) {
+ forceLoad();
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+ cancelLoad();
+ unregisterObserver();
+ mContact = null;
+ }
+
+ /**
+ * Projection used for the query that loads all data for the entire contact (except for social
+ * stream items).
+ */
+ private static class ContactQuery {
+
+ public static final int NAME_RAW_CONTACT_ID = 0;
+ public static final int DISPLAY_NAME_SOURCE = 1;
+ public static final int LOOKUP_KEY = 2;
+ public static final int DISPLAY_NAME = 3;
+ public static final int ALT_DISPLAY_NAME = 4;
+ public static final int PHONETIC_NAME = 5;
+ public static final int PHOTO_ID = 6;
+ public static final int STARRED = 7;
+ public static final int CONTACT_PRESENCE = 8;
+ public static final int CONTACT_STATUS = 9;
+ public static final int CONTACT_STATUS_TIMESTAMP = 10;
+ public static final int CONTACT_STATUS_RES_PACKAGE = 11;
+ public static final int CONTACT_STATUS_LABEL = 12;
+ public static final int CONTACT_ID = 13;
+ public static final int RAW_CONTACT_ID = 14;
+ public static final int ACCOUNT_NAME = 15;
+ public static final int ACCOUNT_TYPE = 16;
+ public static final int DATA_SET = 17;
+ public static final int DIRTY = 18;
+ public static final int VERSION = 19;
+ public static final int SOURCE_ID = 20;
+ public static final int SYNC1 = 21;
+ public static final int SYNC2 = 22;
+ public static final int SYNC3 = 23;
+ public static final int SYNC4 = 24;
+ public static final int DELETED = 25;
+ public static final int DATA_ID = 26;
+ public static final int DATA1 = 27;
+ public static final int DATA2 = 28;
+ public static final int DATA3 = 29;
+ public static final int DATA4 = 30;
+ public static final int DATA5 = 31;
+ public static final int DATA6 = 32;
+ public static final int DATA7 = 33;
+ public static final int DATA8 = 34;
+ public static final int DATA9 = 35;
+ public static final int DATA10 = 36;
+ public static final int DATA11 = 37;
+ public static final int DATA12 = 38;
+ public static final int DATA13 = 39;
+ public static final int DATA14 = 40;
+ public static final int DATA15 = 41;
+ public static final int DATA_SYNC1 = 42;
+ public static final int DATA_SYNC2 = 43;
+ public static final int DATA_SYNC3 = 44;
+ public static final int DATA_SYNC4 = 45;
+ public static final int DATA_VERSION = 46;
+ public static final int IS_PRIMARY = 47;
+ public static final int IS_SUPERPRIMARY = 48;
+ public static final int MIMETYPE = 49;
+ public static final int GROUP_SOURCE_ID = 50;
+ public static final int PRESENCE = 51;
+ public static final int CHAT_CAPABILITY = 52;
+ public static final int STATUS = 53;
+ public static final int STATUS_RES_PACKAGE = 54;
+ public static final int STATUS_ICON = 55;
+ public static final int STATUS_LABEL = 56;
+ public static final int STATUS_TIMESTAMP = 57;
+ public static final int PHOTO_URI = 58;
+ public static final int SEND_TO_VOICEMAIL = 59;
+ public static final int CUSTOM_RINGTONE = 60;
+ public static final int IS_USER_PROFILE = 61;
+ public static final int TIMES_USED = 62;
+ public static final int LAST_TIME_USED = 63;
+ public static final int CARRIER_PRESENCE = 64;
+ static final String[] COLUMNS_INTERNAL =
+ new String[] {
+ Contacts.NAME_RAW_CONTACT_ID,
+ Contacts.DISPLAY_NAME_SOURCE,
+ Contacts.LOOKUP_KEY,
+ Contacts.DISPLAY_NAME,
+ Contacts.DISPLAY_NAME_ALTERNATIVE,
+ Contacts.PHONETIC_NAME,
+ Contacts.PHOTO_ID,
+ Contacts.STARRED,
+ Contacts.CONTACT_PRESENCE,
+ Contacts.CONTACT_STATUS,
+ Contacts.CONTACT_STATUS_TIMESTAMP,
+ Contacts.CONTACT_STATUS_RES_PACKAGE,
+ Contacts.CONTACT_STATUS_LABEL,
+ Contacts.Entity.CONTACT_ID,
+ Contacts.Entity.RAW_CONTACT_ID,
+ RawContacts.ACCOUNT_NAME,
+ RawContacts.ACCOUNT_TYPE,
+ RawContacts.DATA_SET,
+ RawContacts.DIRTY,
+ RawContacts.VERSION,
+ RawContacts.SOURCE_ID,
+ RawContacts.SYNC1,
+ RawContacts.SYNC2,
+ RawContacts.SYNC3,
+ RawContacts.SYNC4,
+ RawContacts.DELETED,
+ Contacts.Entity.DATA_ID,
+ Data.DATA1,
+ Data.DATA2,
+ Data.DATA3,
+ Data.DATA4,
+ Data.DATA5,
+ Data.DATA6,
+ Data.DATA7,
+ Data.DATA8,
+ Data.DATA9,
+ Data.DATA10,
+ Data.DATA11,
+ Data.DATA12,
+ Data.DATA13,
+ Data.DATA14,
+ Data.DATA15,
+ Data.SYNC1,
+ Data.SYNC2,
+ Data.SYNC3,
+ Data.SYNC4,
+ Data.DATA_VERSION,
+ Data.IS_PRIMARY,
+ Data.IS_SUPER_PRIMARY,
+ Data.MIMETYPE,
+ GroupMembership.GROUP_SOURCE_ID,
+ Data.PRESENCE,
+ Data.CHAT_CAPABILITY,
+ Data.STATUS,
+ Data.STATUS_RES_PACKAGE,
+ Data.STATUS_ICON,
+ Data.STATUS_LABEL,
+ Data.STATUS_TIMESTAMP,
+ Contacts.PHOTO_URI,
+ Contacts.SEND_TO_VOICEMAIL,
+ Contacts.CUSTOM_RINGTONE,
+ Contacts.IS_USER_PROFILE,
+ Data.TIMES_USED,
+ Data.LAST_TIME_USED
+ };
+ static final String[] COLUMNS;
+
+ static {
+ List<String> projectionList = Lists.newArrayList(COLUMNS_INTERNAL);
+ if (CompatUtils.isMarshmallowCompatible()) {
+ projectionList.add(Data.CARRIER_PRESENCE);
+ }
+ COLUMNS = projectionList.toArray(new String[projectionList.size()]);
+ }
+ }
+
+ /** Projection used for the query that loads all data for the entire contact. */
+ private static class DirectoryQuery {
+
+ public static final int DISPLAY_NAME = 0;
+ public static final int PACKAGE_NAME = 1;
+ public static final int TYPE_RESOURCE_ID = 2;
+ public static final int ACCOUNT_TYPE = 3;
+ public static final int ACCOUNT_NAME = 4;
+ public static final int EXPORT_SUPPORT = 5;
+ static final String[] COLUMNS =
+ new String[] {
+ Directory.DISPLAY_NAME,
+ Directory.PACKAGE_NAME,
+ Directory.TYPE_RESOURCE_ID,
+ Directory.ACCOUNT_TYPE,
+ Directory.ACCOUNT_NAME,
+ Directory.EXPORT_SUPPORT,
+ };
+ }
+
+ private static class GroupQuery {
+
+ public static final int ACCOUNT_NAME = 0;
+ public static final int ACCOUNT_TYPE = 1;
+ public static final int DATA_SET = 2;
+ public static final int ID = 3;
+ public static final int TITLE = 4;
+ public static final int AUTO_ADD = 5;
+ public static final int FAVORITES = 6;
+ static final String[] COLUMNS =
+ new String[] {
+ Groups.ACCOUNT_NAME,
+ Groups.ACCOUNT_TYPE,
+ Groups.DATA_SET,
+ Groups._ID,
+ Groups.TITLE,
+ Groups.AUTO_ADD,
+ Groups.FAVORITES,
+ };
+ }
+
+ private static class AccountKey {
+
+ private final String mAccountName;
+ private final String mAccountType;
+ private final String mDataSet;
+
+ public AccountKey(String accountName, String accountType, String dataSet) {
+ mAccountName = accountName;
+ mAccountType = accountType;
+ mDataSet = dataSet;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAccountName, mAccountType, mDataSet);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof AccountKey)) {
+ return false;
+ }
+ final AccountKey other = (AccountKey) obj;
+ return Objects.equals(mAccountName, other.mAccountName)
+ && Objects.equals(mAccountType, other.mAccountType)
+ && Objects.equals(mDataSet, other.mDataSet);
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/model/RawContact.java b/java/com/android/contacts/common/model/RawContact.java
new file mode 100644
index 000000000..9efc8a878
--- /dev/null
+++ b/java/com/android/contacts/common/model/RawContact.java
@@ -0,0 +1,351 @@
+/*
+ * 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.model;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Entity;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.dataitem.DataItem;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * RawContact represents a single raw contact in the raw contacts database. It has specialized
+ * getters/setters for raw contact items, and also contains a collection of DataItem objects. A
+ * RawContact contains the information from a single account.
+ *
+ * <p>This allows RawContact objects to be thought of as a class with raw contact fields (like
+ * account type, name, data set, sync state, etc.) and a list of DataItem objects that represent
+ * contact information elements (like phone numbers, email, address, etc.).
+ */
+public final class RawContact implements Parcelable {
+
+ /** Create for building the parcelable. */
+ public static final Parcelable.Creator<RawContact> CREATOR =
+ new Parcelable.Creator<RawContact>() {
+
+ @Override
+ public RawContact createFromParcel(Parcel parcel) {
+ return new RawContact(parcel);
+ }
+
+ @Override
+ public RawContact[] newArray(int i) {
+ return new RawContact[i];
+ }
+ };
+
+ private final ContentValues mValues;
+ private final ArrayList<NamedDataItem> mDataItems;
+ private AccountTypeManager mAccountTypeManager;
+
+ /** A RawContact object can be created with or without a context. */
+ public RawContact() {
+ this(new ContentValues());
+ }
+
+ public RawContact(ContentValues values) {
+ mValues = values;
+ mDataItems = new ArrayList<NamedDataItem>();
+ }
+
+ /**
+ * Constructor for the parcelable.
+ *
+ * @param parcel The parcel to de-serialize from.
+ */
+ private RawContact(Parcel parcel) {
+ mValues = parcel.readParcelable(ContentValues.class.getClassLoader());
+ mDataItems = new ArrayList<>();
+ parcel.readTypedList(mDataItems, NamedDataItem.CREATOR);
+ }
+
+ public static RawContact createFrom(Entity entity) {
+ final ContentValues values = entity.getEntityValues();
+ final ArrayList<Entity.NamedContentValues> subValues = entity.getSubValues();
+
+ RawContact rawContact = new RawContact(values);
+ for (Entity.NamedContentValues subValue : subValues) {
+ rawContact.addNamedDataItemValues(subValue.uri, subValue.values);
+ }
+ return rawContact;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeParcelable(mValues, i);
+ parcel.writeTypedList(mDataItems);
+ }
+
+ public AccountTypeManager getAccountTypeManager(Context context) {
+ if (mAccountTypeManager == null) {
+ mAccountTypeManager = AccountTypeManager.getInstance(context);
+ }
+ return mAccountTypeManager;
+ }
+
+ public ContentValues getValues() {
+ return mValues;
+ }
+
+ /** Returns the id of the raw contact. */
+ public Long getId() {
+ return getValues().getAsLong(RawContacts._ID);
+ }
+
+ /** Returns the account name of the raw contact. */
+ public String getAccountName() {
+ return getValues().getAsString(RawContacts.ACCOUNT_NAME);
+ }
+
+ /** Returns the account type of the raw contact. */
+ public String getAccountTypeString() {
+ return getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ }
+
+ /** Returns the data set of the raw contact. */
+ public String getDataSet() {
+ return getValues().getAsString(RawContacts.DATA_SET);
+ }
+
+ public boolean isDirty() {
+ return getValues().getAsBoolean(RawContacts.DIRTY);
+ }
+
+ public String getSourceId() {
+ return getValues().getAsString(RawContacts.SOURCE_ID);
+ }
+
+ public String getSync1() {
+ return getValues().getAsString(RawContacts.SYNC1);
+ }
+
+ public String getSync2() {
+ return getValues().getAsString(RawContacts.SYNC2);
+ }
+
+ public String getSync3() {
+ return getValues().getAsString(RawContacts.SYNC3);
+ }
+
+ public String getSync4() {
+ return getValues().getAsString(RawContacts.SYNC4);
+ }
+
+ public boolean isDeleted() {
+ return getValues().getAsBoolean(RawContacts.DELETED);
+ }
+
+ public long getContactId() {
+ return getValues().getAsLong(Contacts.Entity.CONTACT_ID);
+ }
+
+ public boolean isStarred() {
+ return getValues().getAsBoolean(Contacts.STARRED);
+ }
+
+ public AccountType getAccountType(Context context) {
+ return getAccountTypeManager(context).getAccountType(getAccountTypeString(), getDataSet());
+ }
+
+ /**
+ * Sets the account name, account type, and data set strings. Valid combinations for account-name,
+ * account-type, data-set 1) null, null, null (local account) 2) non-null, non-null, null (valid
+ * account without data-set) 3) non-null, non-null, non-null (valid account with data-set)
+ */
+ private void setAccount(String accountName, String accountType, String dataSet) {
+ final ContentValues values = getValues();
+ if (accountName == null) {
+ if (accountType == null && dataSet == null) {
+ // This is a local account
+ values.putNull(RawContacts.ACCOUNT_NAME);
+ values.putNull(RawContacts.ACCOUNT_TYPE);
+ values.putNull(RawContacts.DATA_SET);
+ return;
+ }
+ } else {
+ if (accountType != null) {
+ // This is a valid account, either with or without a dataSet.
+ values.put(RawContacts.ACCOUNT_NAME, accountName);
+ values.put(RawContacts.ACCOUNT_TYPE, accountType);
+ if (dataSet == null) {
+ values.putNull(RawContacts.DATA_SET);
+ } else {
+ values.put(RawContacts.DATA_SET, dataSet);
+ }
+ return;
+ }
+ }
+ throw new IllegalArgumentException(
+ "Not a valid combination of account name, type, and data set.");
+ }
+
+ public void setAccount(AccountWithDataSet accountWithDataSet) {
+ if (accountWithDataSet != null) {
+ setAccount(accountWithDataSet.name, accountWithDataSet.type, accountWithDataSet.dataSet);
+ } else {
+ setAccount(null, null, null);
+ }
+ }
+
+ public void setAccountToLocal() {
+ setAccount(null, null, null);
+ }
+
+ /** Creates and inserts a DataItem object that wraps the content values, and returns it. */
+ public void addDataItemValues(ContentValues values) {
+ addNamedDataItemValues(Data.CONTENT_URI, values);
+ }
+
+ public NamedDataItem addNamedDataItemValues(Uri uri, ContentValues values) {
+ final NamedDataItem namedItem = new NamedDataItem(uri, values);
+ mDataItems.add(namedItem);
+ return namedItem;
+ }
+
+ public ArrayList<ContentValues> getContentValues() {
+ final ArrayList<ContentValues> list = new ArrayList<>(mDataItems.size());
+ for (NamedDataItem dataItem : mDataItems) {
+ if (Data.CONTENT_URI.equals(dataItem.mUri)) {
+ list.add(dataItem.mContentValues);
+ }
+ }
+ return list;
+ }
+
+ public List<DataItem> getDataItems() {
+ final ArrayList<DataItem> list = new ArrayList<>(mDataItems.size());
+ for (NamedDataItem dataItem : mDataItems) {
+ if (Data.CONTENT_URI.equals(dataItem.mUri)) {
+ list.add(DataItem.createFrom(dataItem.mContentValues));
+ }
+ }
+ return list;
+ }
+
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("RawContact: ").append(mValues);
+ for (RawContact.NamedDataItem namedDataItem : mDataItems) {
+ sb.append("\n ").append(namedDataItem.mUri);
+ sb.append("\n -> ").append(namedDataItem.mContentValues);
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mValues, mDataItems);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ RawContact other = (RawContact) obj;
+ return Objects.equals(mValues, other.mValues) && Objects.equals(mDataItems, other.mDataItems);
+ }
+
+ public static final class NamedDataItem implements Parcelable {
+
+ public static final Parcelable.Creator<NamedDataItem> CREATOR =
+ new Parcelable.Creator<NamedDataItem>() {
+
+ @Override
+ public NamedDataItem createFromParcel(Parcel parcel) {
+ return new NamedDataItem(parcel);
+ }
+
+ @Override
+ public NamedDataItem[] newArray(int i) {
+ return new NamedDataItem[i];
+ }
+ };
+ public final Uri mUri;
+ // This use to be a DataItem. DataItem creation is now delayed until the point of request
+ // since there is no benefit to storing them here due to the multiple inheritance.
+ // Eventually instanceof still has to be used anyways to determine which sub-class of
+ // DataItem it is. And having parent DataItem's here makes it very difficult to serialize or
+ // parcelable.
+ //
+ // Instead of having a common DataItem super class, we should refactor this to be a generic
+ // Object where the object is a concrete class that no longer relies on ContentValues.
+ // (this will also make the classes easier to use).
+ // Since instanceof is used later anyways, having a list of Objects won't hurt and is no
+ // worse than having a DataItem.
+ public final ContentValues mContentValues;
+
+ public NamedDataItem(Uri uri, ContentValues values) {
+ this.mUri = uri;
+ this.mContentValues = values;
+ }
+
+ public NamedDataItem(Parcel parcel) {
+ this.mUri = parcel.readParcelable(Uri.class.getClassLoader());
+ this.mContentValues = parcel.readParcelable(ContentValues.class.getClassLoader());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeParcelable(mUri, i);
+ parcel.writeParcelable(mContentValues, i);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUri, mContentValues);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final NamedDataItem other = (NamedDataItem) obj;
+ return Objects.equals(mUri, other.mUri)
+ && Objects.equals(mContentValues, other.mContentValues);
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/model/account/AccountType.java b/java/com/android/contacts/common/model/account/AccountType.java
new file mode 100644
index 000000000..1ae485a5f
--- /dev/null
+++ b/java/com/android/contacts/common/model/account/AccountType.java
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2009 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.model.account;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
+import android.support.annotation.VisibleForTesting;
+import android.util.ArrayMap;
+import android.view.inputmethod.EditorInfo;
+import android.widget.EditText;
+import com.android.contacts.common.R;
+import com.android.contacts.common.model.dataitem.DataKind;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Internal structure that represents constraints and styles for a specific data source, such as the
+ * various data types they support, including details on how those types should be rendered and
+ * edited.
+ *
+ * <p>In the future this may be inflated from XML defined by a data source.
+ */
+public abstract class AccountType {
+
+ private static final String TAG = "AccountType";
+ /** {@link Comparator} to sort by {@link DataKind#weight}. */
+ private static Comparator<DataKind> sWeightComparator =
+ new Comparator<DataKind>() {
+ @Override
+ public int compare(DataKind object1, DataKind object2) {
+ return object1.weight - object2.weight;
+ }
+ };
+ /** The {@link RawContacts#ACCOUNT_TYPE} these constraints apply to. */
+ public String accountType = null;
+ /** The {@link RawContacts#DATA_SET} these constraints apply to. */
+ public String dataSet = null;
+ /**
+ * Package that resources should be loaded from. Will be null for embedded types, in which case
+ * resources are stored in this package itself.
+ *
+ * <p>TODO Clean up {@link #resourcePackageName}, {@link #syncAdapterPackageName} and {@link
+ * #getViewContactNotifyServicePackageName()}.
+ *
+ * <p>There's the following invariants: - {@link #syncAdapterPackageName} is always set to the
+ * actual sync adapter package name. - {@link #resourcePackageName} too is set to the same value,
+ * unless {@link #isEmbedded()}, in which case it'll be null. There's an unfortunate exception of
+ * {@link FallbackAccountType}. Even though it {@link #isEmbedded()}, but we set non-null to
+ * {@link #resourcePackageName} for unit tests.
+ */
+ public String resourcePackageName;
+ /**
+ * The package name for the authenticator (for the embedded types, i.e. Google and Exchange) or
+ * the sync adapter (for external type, including extensions).
+ */
+ public String syncAdapterPackageName;
+
+ public int titleRes;
+ public int iconRes;
+ protected boolean mIsInitialized;
+ /** Set of {@link DataKind} supported by this source. */
+ private ArrayList<DataKind> mKinds = new ArrayList<>();
+ /** Lookup map of {@link #mKinds} on {@link DataKind#mimeType}. */
+ private Map<String, DataKind> mMimeKinds = new ArrayMap<>();
+
+ /**
+ * Return a string resource loaded from the given package (or the current package if {@code
+ * packageName} is null), unless {@code resId} is -1, in which case it returns {@code
+ * defaultValue}.
+ *
+ * <p>(The behavior is undefined if the resource or package doesn't exist.)
+ */
+ @VisibleForTesting
+ static CharSequence getResourceText(
+ Context context, String packageName, int resId, String defaultValue) {
+ if (resId != -1 && packageName != null) {
+ final PackageManager pm = context.getPackageManager();
+ return pm.getText(packageName, resId, null);
+ } else if (resId != -1) {
+ return context.getText(resId);
+ } else {
+ return defaultValue;
+ }
+ }
+
+ public static Drawable getDisplayIcon(
+ Context context, int titleRes, int iconRes, String syncAdapterPackageName) {
+ if (titleRes != -1 && syncAdapterPackageName != null) {
+ final PackageManager pm = context.getPackageManager();
+ return pm.getDrawable(syncAdapterPackageName, iconRes, null);
+ } else if (titleRes != -1) {
+ return context.getResources().getDrawable(iconRes);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Whether this account type was able to be fully initialized. This may be false if (for example)
+ * the package name associated with the account type could not be found.
+ */
+ public final boolean isInitialized() {
+ return mIsInitialized;
+ }
+
+ /**
+ * @return Whether this type is an "embedded" type. i.e. any of {@link FallbackAccountType},
+ * {@link GoogleAccountType} or {@link ExternalAccountType}.
+ * <p>If an embedded type cannot be initialized (i.e. if {@link #isInitialized()} returns
+ * {@code false}) it's considered critical, and the application will crash. On the other hand
+ * if it's not an embedded type, we just skip loading the type.
+ */
+ public boolean isEmbedded() {
+ return true;
+ }
+
+ public boolean isExtension() {
+ return false;
+ }
+
+ /**
+ * @return True if contacts can be created and edited using this app. If false, there could still
+ * be an external editor as provided by {@link #getEditContactActivityClassName()} or {@link
+ * #getCreateContactActivityClassName()}
+ */
+ public abstract boolean areContactsWritable();
+
+ /**
+ * Returns an optional custom edit activity.
+ *
+ * <p>Only makes sense for non-embedded account types. The activity class should reside in the
+ * sync adapter package as determined by {@link #syncAdapterPackageName}.
+ */
+ public String getEditContactActivityClassName() {
+ return null;
+ }
+
+ /**
+ * Returns an optional custom new contact activity.
+ *
+ * <p>Only makes sense for non-embedded account types. The activity class should reside in the
+ * sync adapter package as determined by {@link #syncAdapterPackageName}.
+ */
+ public String getCreateContactActivityClassName() {
+ return null;
+ }
+
+ /**
+ * Returns an optional custom invite contact activity.
+ *
+ * <p>Only makes sense for non-embedded account types. The activity class should reside in the
+ * sync adapter package as determined by {@link #syncAdapterPackageName}.
+ */
+ public String getInviteContactActivityClassName() {
+ return null;
+ }
+
+ /**
+ * Returns an optional service that can be launched whenever a contact is being looked at. This
+ * allows the sync adapter to provide more up-to-date information.
+ *
+ * <p>The service class should reside in the sync adapter package as determined by {@link
+ * #getViewContactNotifyServicePackageName()}.
+ */
+ public String getViewContactNotifyServiceClassName() {
+ return null;
+ }
+
+ /**
+ * TODO This is way too hacky should be removed.
+ *
+ * <p>This is introduced for {@link GoogleAccountType} where {@link #syncAdapterPackageName} is
+ * the authenticator package name but the notification service is in the sync adapter package. See
+ * {@link #resourcePackageName} -- we should clean up those.
+ */
+ public String getViewContactNotifyServicePackageName() {
+ return syncAdapterPackageName;
+ }
+
+ /** Returns an optional Activity string that can be used to view the group. */
+ public String getViewGroupActivity() {
+ return null;
+ }
+
+ public CharSequence getDisplayLabel(Context context) {
+ // Note this resource is defined in the sync adapter package, not resourcePackageName.
+ return getResourceText(context, syncAdapterPackageName, titleRes, accountType);
+ }
+
+ /** @return resource ID for the "invite contact" action label, or -1 if not defined. */
+ protected int getInviteContactActionResId() {
+ return -1;
+ }
+
+ /** @return resource ID for the "view group" label, or -1 if not defined. */
+ protected int getViewGroupLabelResId() {
+ return -1;
+ }
+
+ /** Returns {@link AccountTypeWithDataSet} for this type. */
+ public AccountTypeWithDataSet getAccountTypeAndDataSet() {
+ return AccountTypeWithDataSet.get(accountType, dataSet);
+ }
+
+ /**
+ * Returns a list of additional package names that should be inspected as additional external
+ * account types. This allows for a primary account type to indicate other packages that may not
+ * be sync adapters but which still provide contact data, perhaps under a separate data set within
+ * the account.
+ */
+ public List<String> getExtensionPackageNames() {
+ return new ArrayList<String>();
+ }
+
+ /**
+ * Returns an optional custom label for the "invite contact" action, which will be shown on the
+ * contact card. (If not defined, returns null.)
+ */
+ public CharSequence getInviteContactActionLabel(Context context) {
+ // Note this resource is defined in the sync adapter package, not resourcePackageName.
+ return getResourceText(context, syncAdapterPackageName, getInviteContactActionResId(), "");
+ }
+
+ /**
+ * Returns a label for the "view group" action. If not defined, this falls back to our own "View
+ * Updates" string
+ */
+ public CharSequence getViewGroupLabel(Context context) {
+ // Note this resource is defined in the sync adapter package, not resourcePackageName.
+ final CharSequence customTitle =
+ getResourceText(context, syncAdapterPackageName, getViewGroupLabelResId(), null);
+
+ return customTitle == null ? context.getText(R.string.view_updates_from_group) : customTitle;
+ }
+
+ public Drawable getDisplayIcon(Context context) {
+ return getDisplayIcon(context, titleRes, iconRes, syncAdapterPackageName);
+ }
+
+ /** Whether or not groups created under this account type have editable membership lists. */
+ public abstract boolean isGroupMembershipEditable();
+
+ /** Return list of {@link DataKind} supported, sorted by {@link DataKind#weight}. */
+ public ArrayList<DataKind> getSortedDataKinds() {
+ // TODO: optimize by marking if already sorted
+ Collections.sort(mKinds, sWeightComparator);
+ return mKinds;
+ }
+
+ /** Find the {@link DataKind} for a specific MIME-type, if it's handled by this data source. */
+ public DataKind getKindForMimetype(String mimeType) {
+ return this.mMimeKinds.get(mimeType);
+ }
+
+ /** Add given {@link DataKind} to list of those provided by this source. */
+ public DataKind addKind(DataKind kind) throws DefinitionException {
+ if (kind.mimeType == null) {
+ throw new DefinitionException("null is not a valid mime type");
+ }
+ if (mMimeKinds.get(kind.mimeType) != null) {
+ throw new DefinitionException("mime type '" + kind.mimeType + "' is already registered");
+ }
+
+ kind.resourcePackageName = this.resourcePackageName;
+ this.mKinds.add(kind);
+ this.mMimeKinds.put(kind.mimeType, kind);
+ return kind;
+ }
+
+ /**
+ * Generic method of inflating a given {@link ContentValues} into a user-readable {@link
+ * CharSequence}. For example, an inflater could combine the multiple columns of {@link
+ * StructuredPostal} together using a string resource before presenting to the user.
+ */
+ public interface StringInflater {
+
+ CharSequence inflateUsing(Context context, ContentValues values);
+ }
+
+ protected static class DefinitionException extends Exception {
+
+ public DefinitionException(String message) {
+ super(message);
+ }
+
+ public DefinitionException(String message, Exception inner) {
+ super(message, inner);
+ }
+ }
+
+ /**
+ * Description of a specific "type" or "label" of a {@link DataKind} row, such as {@link
+ * Phone#TYPE_WORK}. Includes constraints on total number of rows a {@link Contacts} may have of
+ * this type, and details on how user-defined labels are stored.
+ */
+ public static class EditType {
+
+ public int rawValue;
+ public int labelRes;
+ public boolean secondary;
+ /**
+ * The number of entries allowed for the type. -1 if not specified.
+ *
+ * @see DataKind#typeOverallMax
+ */
+ public int specificMax;
+
+ public String customColumn;
+
+ public EditType(int rawValue, int labelRes) {
+ this.rawValue = rawValue;
+ this.labelRes = labelRes;
+ this.specificMax = -1;
+ }
+
+ public EditType setSecondary(boolean secondary) {
+ this.secondary = secondary;
+ return this;
+ }
+
+ public EditType setSpecificMax(int specificMax) {
+ this.specificMax = specificMax;
+ return this;
+ }
+
+ public EditType setCustomColumn(String customColumn) {
+ this.customColumn = customColumn;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object instanceof EditType) {
+ final EditType other = (EditType) object;
+ return other.rawValue == rawValue;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return rawValue;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName()
+ + " rawValue="
+ + rawValue
+ + " labelRes="
+ + labelRes
+ + " secondary="
+ + secondary
+ + " specificMax="
+ + specificMax
+ + " customColumn="
+ + customColumn;
+ }
+ }
+
+ public static class EventEditType extends EditType {
+
+ private boolean mYearOptional;
+
+ public EventEditType(int rawValue, int labelRes) {
+ super(rawValue, labelRes);
+ }
+
+ public boolean isYearOptional() {
+ return mYearOptional;
+ }
+
+ public EventEditType setYearOptional(boolean yearOptional) {
+ mYearOptional = yearOptional;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " mYearOptional=" + mYearOptional;
+ }
+ }
+
+ /**
+ * Description of a user-editable field on a {@link DataKind} row, such as {@link Phone#NUMBER}.
+ * Includes flags to apply to an {@link EditText}, and the column where this field is stored.
+ */
+ public static final class EditField {
+
+ public String column;
+ public int titleRes;
+ public int inputType;
+ public int minLines;
+ public boolean optional;
+ public boolean shortForm;
+ public boolean longForm;
+
+ public EditField(String column, int titleRes) {
+ this.column = column;
+ this.titleRes = titleRes;
+ }
+
+ public EditField(String column, int titleRes, int inputType) {
+ this(column, titleRes);
+ this.inputType = inputType;
+ }
+
+ public EditField setOptional(boolean optional) {
+ this.optional = optional;
+ return this;
+ }
+
+ public EditField setShortForm(boolean shortForm) {
+ this.shortForm = shortForm;
+ return this;
+ }
+
+ public EditField setLongForm(boolean longForm) {
+ this.longForm = longForm;
+ return this;
+ }
+
+ public EditField setMinLines(int minLines) {
+ this.minLines = minLines;
+ return this;
+ }
+
+ public boolean isMultiLine() {
+ return (inputType & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) != 0;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName()
+ + ":"
+ + " column="
+ + column
+ + " titleRes="
+ + titleRes
+ + " inputType="
+ + inputType
+ + " minLines="
+ + minLines
+ + " optional="
+ + optional
+ + " shortForm="
+ + shortForm
+ + " longForm="
+ + longForm;
+ }
+ }
+
+ /**
+ * Compare two {@link AccountType} by their {@link AccountType#getDisplayLabel} with the current
+ * locale.
+ */
+ public static class DisplayLabelComparator implements Comparator<AccountType> {
+
+ private final Context mContext;
+ /** {@link Comparator} for the current locale. */
+ private final Collator mCollator = Collator.getInstance();
+
+ public DisplayLabelComparator(Context context) {
+ mContext = context;
+ }
+
+ private String getDisplayLabel(AccountType type) {
+ CharSequence label = type.getDisplayLabel(mContext);
+ return (label == null) ? "" : label.toString();
+ }
+
+ @Override
+ public int compare(AccountType lhs, AccountType rhs) {
+ return mCollator.compare(getDisplayLabel(lhs), getDisplayLabel(rhs));
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/model/account/AccountTypeWithDataSet.java b/java/com/android/contacts/common/model/account/AccountTypeWithDataSet.java
new file mode 100644
index 000000000..a32ebe139
--- /dev/null
+++ b/java/com/android/contacts/common/model/account/AccountTypeWithDataSet.java
@@ -0,0 +1,103 @@
+/*
+ * 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.model.account;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.BaseColumns;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.RawContacts;
+import android.text.TextUtils;
+import java.util.Objects;
+
+/** Encapsulates an "account type" string and a "data set" string. */
+public class AccountTypeWithDataSet {
+
+ private static final String[] ID_PROJECTION = new String[] {BaseColumns._ID};
+ private static final Uri RAW_CONTACTS_URI_LIMIT_1 =
+ RawContacts.CONTENT_URI
+ .buildUpon()
+ .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, "1")
+ .build();
+
+ /** account type. Can be null for fallback type. */
+ public final String accountType;
+
+ /** dataSet may be null, but never be "". */
+ public final String dataSet;
+
+ private AccountTypeWithDataSet(String accountType, String dataSet) {
+ this.accountType = TextUtils.isEmpty(accountType) ? null : accountType;
+ this.dataSet = TextUtils.isEmpty(dataSet) ? null : dataSet;
+ }
+
+ public static AccountTypeWithDataSet get(String accountType, String dataSet) {
+ return new AccountTypeWithDataSet(accountType, dataSet);
+ }
+
+ /**
+ * Return true if there are any contacts in the database with this account type and data set.
+ * Touches DB. Don't use in the UI thread.
+ */
+ public boolean hasData(Context context) {
+ final String BASE_SELECTION = RawContacts.ACCOUNT_TYPE + " = ?";
+ final String selection;
+ final String[] args;
+ if (TextUtils.isEmpty(dataSet)) {
+ selection = BASE_SELECTION + " AND " + RawContacts.DATA_SET + " IS NULL";
+ args = new String[] {accountType};
+ } else {
+ selection = BASE_SELECTION + " AND " + RawContacts.DATA_SET + " = ?";
+ args = new String[] {accountType, dataSet};
+ }
+
+ final Cursor c =
+ context
+ .getContentResolver()
+ .query(RAW_CONTACTS_URI_LIMIT_1, ID_PROJECTION, selection, args, null);
+ if (c == null) {
+ return false;
+ }
+ try {
+ return c.moveToFirst();
+ } finally {
+ c.close();
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof AccountTypeWithDataSet)) {
+ return false;
+ }
+
+ AccountTypeWithDataSet other = (AccountTypeWithDataSet) o;
+ return Objects.equals(accountType, other.accountType) && Objects.equals(dataSet, other.dataSet);
+ }
+
+ @Override
+ public int hashCode() {
+ return (accountType == null ? 0 : accountType.hashCode())
+ ^ (dataSet == null ? 0 : dataSet.hashCode());
+ }
+
+ @Override
+ public String toString() {
+ return "[" + accountType + "/" + dataSet + "]";
+ }
+}
diff --git a/java/com/android/contacts/common/model/account/AccountWithDataSet.java b/java/com/android/contacts/common/model/account/AccountWithDataSet.java
new file mode 100644
index 000000000..71faf509c
--- /dev/null
+++ b/java/com/android/contacts/common/model/account/AccountWithDataSet.java
@@ -0,0 +1,229 @@
+/*
+ * 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.model.account;
+
+import android.accounts.Account;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.BaseColumns;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.RawContacts;
+import android.text.TextUtils;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+/** Wrapper for an account that includes a data set (which may be null). */
+public class AccountWithDataSet implements Parcelable {
+
+ // For Parcelable
+ public static final Creator<AccountWithDataSet> CREATOR =
+ new Creator<AccountWithDataSet>() {
+ public AccountWithDataSet createFromParcel(Parcel source) {
+ return new AccountWithDataSet(source);
+ }
+
+ public AccountWithDataSet[] newArray(int size) {
+ return new AccountWithDataSet[size];
+ }
+ };
+ private static final String STRINGIFY_SEPARATOR = "\u0001";
+ private static final String ARRAY_STRINGIFY_SEPARATOR = "\u0002";
+ private static final Pattern STRINGIFY_SEPARATOR_PAT =
+ Pattern.compile(Pattern.quote(STRINGIFY_SEPARATOR));
+ private static final Pattern ARRAY_STRINGIFY_SEPARATOR_PAT =
+ Pattern.compile(Pattern.quote(ARRAY_STRINGIFY_SEPARATOR));
+ private static final String[] ID_PROJECTION = new String[] {BaseColumns._ID};
+ private static final Uri RAW_CONTACTS_URI_LIMIT_1 =
+ RawContacts.CONTENT_URI
+ .buildUpon()
+ .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, "1")
+ .build();
+ public final String name;
+ public final String type;
+ public final String dataSet;
+ private final AccountTypeWithDataSet mAccountTypeWithDataSet;
+
+ public AccountWithDataSet(String name, String type, String dataSet) {
+ this.name = emptyToNull(name);
+ this.type = emptyToNull(type);
+ this.dataSet = emptyToNull(dataSet);
+ mAccountTypeWithDataSet = AccountTypeWithDataSet.get(type, dataSet);
+ }
+
+ public AccountWithDataSet(Parcel in) {
+ this.name = in.readString();
+ this.type = in.readString();
+ this.dataSet = in.readString();
+ mAccountTypeWithDataSet = AccountTypeWithDataSet.get(type, dataSet);
+ }
+
+ private static String emptyToNull(String text) {
+ return TextUtils.isEmpty(text) ? null : text;
+ }
+
+ private static StringBuilder addStringified(StringBuilder sb, AccountWithDataSet account) {
+ if (!TextUtils.isEmpty(account.name)) {
+ sb.append(account.name);
+ }
+ sb.append(STRINGIFY_SEPARATOR);
+ if (!TextUtils.isEmpty(account.type)) {
+ sb.append(account.type);
+ }
+ sb.append(STRINGIFY_SEPARATOR);
+ if (!TextUtils.isEmpty(account.dataSet)) {
+ sb.append(account.dataSet);
+ }
+
+ return sb;
+ }
+
+ /**
+ * Unpack a string created by {@link #stringify}.
+ *
+ * @throws IllegalArgumentException if it's an invalid string.
+ */
+ public static AccountWithDataSet unstringify(String s) {
+ final String[] array = STRINGIFY_SEPARATOR_PAT.split(s, 3);
+ if (array.length < 3) {
+ throw new IllegalArgumentException("Invalid string " + s);
+ }
+ return new AccountWithDataSet(
+ array[0], array[1], TextUtils.isEmpty(array[2]) ? null : array[2]);
+ }
+
+ /** Pack a list of {@link AccountWithDataSet} into a string. */
+ public static String stringifyList(List<AccountWithDataSet> accounts) {
+ final StringBuilder sb = new StringBuilder();
+
+ for (AccountWithDataSet account : accounts) {
+ if (sb.length() > 0) {
+ sb.append(ARRAY_STRINGIFY_SEPARATOR);
+ }
+ addStringified(sb, account);
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Unpack a list of {@link AccountWithDataSet} into a string.
+ *
+ * @throws IllegalArgumentException if it's an invalid string.
+ */
+ public static List<AccountWithDataSet> unstringifyList(String s) {
+ final ArrayList<AccountWithDataSet> ret = new ArrayList<>();
+ if (TextUtils.isEmpty(s)) {
+ return ret;
+ }
+
+ final String[] array = ARRAY_STRINGIFY_SEPARATOR_PAT.split(s);
+
+ for (int i = 0; i < array.length; i++) {
+ ret.add(unstringify(array[i]));
+ }
+
+ return ret;
+ }
+
+ public boolean isLocalAccount() {
+ return name == null && type == null;
+ }
+
+ public Account getAccountOrNull() {
+ if (name != null && type != null) {
+ return new Account(name, type);
+ }
+ return null;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(name);
+ dest.writeString(type);
+ dest.writeString(dataSet);
+ }
+
+ public AccountTypeWithDataSet getAccountTypeWithDataSet() {
+ return mAccountTypeWithDataSet;
+ }
+
+ /**
+ * Return {@code true} if this account has any contacts in the database. Touches DB. Don't use in
+ * the UI thread.
+ */
+ public boolean hasData(Context context) {
+ final String BASE_SELECTION =
+ RawContacts.ACCOUNT_TYPE + " = ?" + " AND " + RawContacts.ACCOUNT_NAME + " = ?";
+ final String selection;
+ final String[] args;
+ if (TextUtils.isEmpty(dataSet)) {
+ selection = BASE_SELECTION + " AND " + RawContacts.DATA_SET + " IS NULL";
+ args = new String[] {type, name};
+ } else {
+ selection = BASE_SELECTION + " AND " + RawContacts.DATA_SET + " = ?";
+ args = new String[] {type, name, dataSet};
+ }
+
+ final Cursor c =
+ context
+ .getContentResolver()
+ .query(RAW_CONTACTS_URI_LIMIT_1, ID_PROJECTION, selection, args, null);
+ if (c == null) {
+ return false;
+ }
+ try {
+ return c.moveToFirst();
+ } finally {
+ c.close();
+ }
+ }
+
+ public boolean equals(Object obj) {
+ if (obj instanceof AccountWithDataSet) {
+ AccountWithDataSet other = (AccountWithDataSet) obj;
+ return Objects.equals(name, other.name)
+ && Objects.equals(type, other.type)
+ && Objects.equals(dataSet, other.dataSet);
+ }
+ return false;
+ }
+
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ result = 31 * result + (type != null ? type.hashCode() : 0);
+ result = 31 * result + (dataSet != null ? dataSet.hashCode() : 0);
+ return result;
+ }
+
+ public String toString() {
+ return "AccountWithDataSet {name=" + name + ", type=" + type + ", dataSet=" + dataSet + "}";
+ }
+
+ /** Pack the instance into a string. */
+ public String stringify() {
+ return addStringified(new StringBuilder(), this).toString();
+ }
+}
diff --git a/java/com/android/contacts/common/model/account/BaseAccountType.java b/java/com/android/contacts/common/model/account/BaseAccountType.java
new file mode 100644
index 000000000..21b555917
--- /dev/null
+++ b/java/com/android/contacts/common/model/account/BaseAccountType.java
@@ -0,0 +1,1890 @@
+/*
+ * Copyright (C) 2009 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.model.account;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.Relation;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.inputmethod.EditorInfo;
+import com.android.contacts.common.R;
+import com.android.contacts.common.model.dataitem.DataKind;
+import com.android.contacts.common.util.CommonDateUtils;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+public abstract class BaseAccountType extends AccountType {
+
+ public static final StringInflater ORGANIZATION_BODY_INFLATER =
+ new StringInflater() {
+ @Override
+ public CharSequence inflateUsing(Context context, ContentValues values) {
+ final CharSequence companyValue =
+ values.containsKey(Organization.COMPANY)
+ ? values.getAsString(Organization.COMPANY)
+ : null;
+ final CharSequence titleValue =
+ values.containsKey(Organization.TITLE)
+ ? values.getAsString(Organization.TITLE)
+ : null;
+
+ if (companyValue != null && titleValue != null) {
+ return companyValue + ": " + titleValue;
+ } else if (companyValue == null) {
+ return titleValue;
+ } else {
+ return companyValue;
+ }
+ }
+ };
+ protected static final int FLAGS_PHONE = EditorInfo.TYPE_CLASS_PHONE;
+ protected static final int FLAGS_EMAIL =
+ EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
+ protected static final int FLAGS_PERSON_NAME =
+ EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
+ | EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME;
+ protected static final int FLAGS_PHONETIC =
+ EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PHONETIC;
+ protected static final int FLAGS_GENERIC_NAME =
+ EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
+ protected static final int FLAGS_NOTE =
+ EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
+ | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
+ protected static final int FLAGS_EVENT = EditorInfo.TYPE_CLASS_TEXT;
+ protected static final int FLAGS_WEBSITE =
+ EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_URI;
+ protected static final int FLAGS_POSTAL =
+ EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
+ | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
+ | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
+ protected static final int FLAGS_SIP_ADDRESS =
+ EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; // since SIP addresses have the same
+ // basic format as email addresses
+ protected static final int FLAGS_RELATION =
+ EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
+ | EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME;
+
+ // Specify the maximum number of lines that can be used to display various field types. If no
+ // value is specified for a particular type, we use the default value from {@link DataKind}.
+ protected static final int MAX_LINES_FOR_POSTAL_ADDRESS = 10;
+ protected static final int MAX_LINES_FOR_GROUP = 10;
+ protected static final int MAX_LINES_FOR_NOTE = 100;
+ private static final String TAG = "BaseAccountType";
+
+ public BaseAccountType() {
+ this.accountType = null;
+ this.dataSet = null;
+ this.titleRes = R.string.account_phone;
+ this.iconRes = R.mipmap.ic_contacts_launcher;
+ }
+
+ protected static EditType buildPhoneType(int type) {
+ return new EditType(type, Phone.getTypeLabelResource(type));
+ }
+
+ protected static EditType buildEmailType(int type) {
+ return new EditType(type, Email.getTypeLabelResource(type));
+ }
+
+ protected static EditType buildPostalType(int type) {
+ return new EditType(type, StructuredPostal.getTypeLabelResource(type));
+ }
+
+ protected static EditType buildImType(int type) {
+ return new EditType(type, Im.getProtocolLabelResource(type));
+ }
+
+ protected static EditType buildEventType(int type, boolean yearOptional) {
+ return new EventEditType(type, Event.getTypeResource(type)).setYearOptional(yearOptional);
+ }
+
+ protected static EditType buildRelationType(int type) {
+ return new EditType(type, Relation.getTypeLabelResource(type));
+ }
+
+ // Utility methods to keep code shorter.
+ private static boolean getAttr(AttributeSet attrs, String attribute, boolean defaultValue) {
+ return attrs.getAttributeBooleanValue(null, attribute, defaultValue);
+ }
+
+ private static int getAttr(AttributeSet attrs, String attribute, int defaultValue) {
+ return attrs.getAttributeIntValue(null, attribute, defaultValue);
+ }
+
+ private static String getAttr(AttributeSet attrs, String attribute) {
+ return attrs.getAttributeValue(null, attribute);
+ }
+
+ protected DataKind addDataKindStructuredName(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(
+ StructuredName.CONTENT_ITEM_TYPE, R.string.nameLabelsGroup, Weight.NONE, true));
+ kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
+ kind.actionBody = new SimpleInflater(Nickname.NAME);
+ kind.typeOverallMax = 1;
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(
+ new EditField(StructuredName.DISPLAY_NAME, R.string.full_name, FLAGS_PERSON_NAME));
+ kind.fieldList.add(
+ new EditField(StructuredName.PREFIX, R.string.name_prefix, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kind.fieldList.add(
+ new EditField(StructuredName.FAMILY_NAME, R.string.name_family, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kind.fieldList.add(
+ new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kind.fieldList.add(
+ new EditField(StructuredName.GIVEN_NAME, R.string.name_given, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kind.fieldList.add(
+ new EditField(StructuredName.SUFFIX, R.string.name_suffix, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kind.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_FAMILY_NAME, R.string.name_phonetic_family, FLAGS_PHONETIC));
+ kind.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_MIDDLE_NAME, R.string.name_phonetic_middle, FLAGS_PHONETIC));
+ kind.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_GIVEN_NAME, R.string.name_phonetic_given, FLAGS_PHONETIC));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindDisplayName(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(
+ DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME,
+ R.string.nameLabelsGroup,
+ Weight.NONE,
+ true));
+ kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
+ kind.actionBody = new SimpleInflater(Nickname.NAME);
+ kind.typeOverallMax = 1;
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(
+ new EditField(StructuredName.DISPLAY_NAME, R.string.full_name, FLAGS_PERSON_NAME)
+ .setShortForm(true));
+
+ boolean displayOrderPrimary =
+ context.getResources().getBoolean(R.bool.config_editor_field_order_primary);
+
+ if (!displayOrderPrimary) {
+ kind.fieldList.add(
+ new EditField(StructuredName.PREFIX, R.string.name_prefix, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kind.fieldList.add(
+ new EditField(StructuredName.FAMILY_NAME, R.string.name_family, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kind.fieldList.add(
+ new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kind.fieldList.add(
+ new EditField(StructuredName.GIVEN_NAME, R.string.name_given, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kind.fieldList.add(
+ new EditField(StructuredName.SUFFIX, R.string.name_suffix, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ } else {
+ kind.fieldList.add(
+ new EditField(StructuredName.PREFIX, R.string.name_prefix, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kind.fieldList.add(
+ new EditField(StructuredName.GIVEN_NAME, R.string.name_given, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kind.fieldList.add(
+ new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kind.fieldList.add(
+ new EditField(StructuredName.FAMILY_NAME, R.string.name_family, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kind.fieldList.add(
+ new EditField(StructuredName.SUFFIX, R.string.name_suffix, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ }
+
+ return kind;
+ }
+
+ protected DataKind addDataKindPhoneticName(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(
+ DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME,
+ R.string.name_phonetic,
+ Weight.NONE,
+ true));
+ kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
+ kind.actionBody = new SimpleInflater(Nickname.NAME);
+ kind.typeOverallMax = 1;
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(
+ new EditField(DataKind.PSEUDO_COLUMN_PHONETIC_NAME, R.string.name_phonetic, FLAGS_PHONETIC)
+ .setShortForm(true));
+ kind.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_FAMILY_NAME, R.string.name_phonetic_family, FLAGS_PHONETIC)
+ .setLongForm(true));
+ kind.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_MIDDLE_NAME, R.string.name_phonetic_middle, FLAGS_PHONETIC)
+ .setLongForm(true));
+ kind.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_GIVEN_NAME, R.string.name_phonetic_given, FLAGS_PHONETIC)
+ .setLongForm(true));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindNickname(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(
+ Nickname.CONTENT_ITEM_TYPE, R.string.nicknameLabelsGroup, Weight.NICKNAME, true));
+ kind.typeOverallMax = 1;
+ kind.actionHeader = new SimpleInflater(R.string.nicknameLabelsGroup);
+ kind.actionBody = new SimpleInflater(Nickname.NAME);
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Nickname.TYPE, Nickname.TYPE_DEFAULT);
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(
+ new EditField(Nickname.NAME, R.string.nicknameLabelsGroup, FLAGS_PERSON_NAME));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindPhone(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(Phone.CONTENT_ITEM_TYPE, R.string.phoneLabelsGroup, Weight.PHONE, true));
+ kind.iconAltRes = R.drawable.ic_message_24dp;
+ kind.iconAltDescriptionRes = R.string.sms;
+ kind.actionHeader = new PhoneActionInflater();
+ kind.actionAltHeader = new PhoneActionAltInflater();
+ kind.actionBody = new SimpleInflater(Phone.NUMBER);
+ kind.typeColumn = Phone.TYPE;
+ kind.typeList = new ArrayList<>();
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_WORK));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_PAGER).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_OTHER));
+ kind.typeList.add(
+ buildPhoneType(Phone.TYPE_CUSTOM).setSecondary(true).setCustomColumn(Phone.LABEL));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_CALLBACK).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_CAR).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_COMPANY_MAIN).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_ISDN).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MAIN).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_OTHER_FAX).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_RADIO).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_TELEX).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_TTY_TDD).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_WORK_MOBILE).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_WORK_PAGER).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_ASSISTANT).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MMS).setSecondary(true));
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindEmail(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(Email.CONTENT_ITEM_TYPE, R.string.emailLabelsGroup, Weight.EMAIL, true));
+ kind.actionHeader = new EmailActionInflater();
+ kind.actionBody = new SimpleInflater(Email.DATA);
+ kind.typeColumn = Email.TYPE;
+ kind.typeList = new ArrayList<>();
+ kind.typeList.add(buildEmailType(Email.TYPE_HOME));
+ kind.typeList.add(buildEmailType(Email.TYPE_WORK));
+ kind.typeList.add(buildEmailType(Email.TYPE_OTHER));
+ kind.typeList.add(buildEmailType(Email.TYPE_MOBILE));
+ kind.typeList.add(
+ buildEmailType(Email.TYPE_CUSTOM).setSecondary(true).setCustomColumn(Email.LABEL));
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindStructuredPostal(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(
+ StructuredPostal.CONTENT_ITEM_TYPE,
+ R.string.postalLabelsGroup,
+ Weight.STRUCTURED_POSTAL,
+ true));
+ kind.actionHeader = new PostalActionInflater();
+ kind.actionBody = new SimpleInflater(StructuredPostal.FORMATTED_ADDRESS);
+ kind.typeColumn = StructuredPostal.TYPE;
+ kind.typeList = new ArrayList<>();
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_HOME));
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_WORK));
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_OTHER));
+ kind.typeList.add(
+ buildPostalType(StructuredPostal.TYPE_CUSTOM)
+ .setSecondary(true)
+ .setCustomColumn(StructuredPostal.LABEL));
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(
+ new EditField(StructuredPostal.FORMATTED_ADDRESS, R.string.postal_address, FLAGS_POSTAL));
+
+ kind.maxLinesForDisplay = MAX_LINES_FOR_POSTAL_ADDRESS;
+
+ return kind;
+ }
+
+ protected DataKind addDataKindIm(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(new DataKind(Im.CONTENT_ITEM_TYPE, R.string.imLabelsGroup, Weight.IM, true));
+ kind.actionHeader = new ImActionInflater();
+ kind.actionBody = new SimpleInflater(Im.DATA);
+
+ // NOTE: even though a traditional "type" exists, for editing
+ // purposes we're using the protocol to pick labels
+
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Im.TYPE, Im.TYPE_OTHER);
+
+ kind.typeColumn = Im.PROTOCOL;
+ kind.typeList = new ArrayList<>();
+ kind.typeList.add(buildImType(Im.PROTOCOL_AIM));
+ kind.typeList.add(buildImType(Im.PROTOCOL_MSN));
+ kind.typeList.add(buildImType(Im.PROTOCOL_YAHOO));
+ kind.typeList.add(buildImType(Im.PROTOCOL_SKYPE));
+ kind.typeList.add(buildImType(Im.PROTOCOL_QQ));
+ kind.typeList.add(buildImType(Im.PROTOCOL_GOOGLE_TALK));
+ kind.typeList.add(buildImType(Im.PROTOCOL_ICQ));
+ kind.typeList.add(buildImType(Im.PROTOCOL_JABBER));
+ kind.typeList.add(
+ buildImType(Im.PROTOCOL_CUSTOM).setSecondary(true).setCustomColumn(Im.CUSTOM_PROTOCOL));
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Im.DATA, R.string.imLabelsGroup, FLAGS_EMAIL));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindOrganization(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(
+ Organization.CONTENT_ITEM_TYPE,
+ R.string.organizationLabelsGroup,
+ Weight.ORGANIZATION,
+ true));
+ kind.actionHeader = new SimpleInflater(R.string.organizationLabelsGroup);
+ kind.actionBody = ORGANIZATION_BODY_INFLATER;
+ kind.typeOverallMax = 1;
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(
+ new EditField(Organization.COMPANY, R.string.ghostData_company, FLAGS_GENERIC_NAME));
+ kind.fieldList.add(
+ new EditField(Organization.TITLE, R.string.ghostData_title, FLAGS_GENERIC_NAME));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindPhoto(Context context) throws DefinitionException {
+ DataKind kind = addKind(new DataKind(Photo.CONTENT_ITEM_TYPE, -1, Weight.NONE, true));
+ kind.typeOverallMax = 1;
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Photo.PHOTO, -1, -1));
+ return kind;
+ }
+
+ protected DataKind addDataKindNote(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(new DataKind(Note.CONTENT_ITEM_TYPE, R.string.label_notes, Weight.NOTE, true));
+ kind.typeOverallMax = 1;
+ kind.actionHeader = new SimpleInflater(R.string.label_notes);
+ kind.actionBody = new SimpleInflater(Note.NOTE);
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Note.NOTE, R.string.label_notes, FLAGS_NOTE));
+
+ kind.maxLinesForDisplay = MAX_LINES_FOR_NOTE;
+
+ return kind;
+ }
+
+ protected DataKind addDataKindWebsite(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(
+ Website.CONTENT_ITEM_TYPE, R.string.websiteLabelsGroup, Weight.WEBSITE, true));
+ kind.actionHeader = new SimpleInflater(R.string.websiteLabelsGroup);
+ kind.actionBody = new SimpleInflater(Website.URL);
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Website.TYPE, Website.TYPE_OTHER);
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Website.URL, R.string.websiteLabelsGroup, FLAGS_WEBSITE));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindSipAddress(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(
+ SipAddress.CONTENT_ITEM_TYPE,
+ R.string.label_sip_address,
+ Weight.SIP_ADDRESS,
+ true));
+
+ kind.actionHeader = new SimpleInflater(R.string.label_sip_address);
+ kind.actionBody = new SimpleInflater(SipAddress.SIP_ADDRESS);
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(
+ new EditField(SipAddress.SIP_ADDRESS, R.string.label_sip_address, FLAGS_SIP_ADDRESS));
+ kind.typeOverallMax = 1;
+
+ return kind;
+ }
+
+ protected DataKind addDataKindGroupMembership(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(
+ GroupMembership.CONTENT_ITEM_TYPE,
+ R.string.groupsLabel,
+ Weight.GROUP_MEMBERSHIP,
+ true));
+
+ kind.typeOverallMax = 1;
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(GroupMembership.GROUP_ROW_ID, -1, -1));
+
+ kind.maxLinesForDisplay = MAX_LINES_FOR_GROUP;
+
+ return kind;
+ }
+
+ @Override
+ public boolean isGroupMembershipEditable() {
+ return false;
+ }
+
+ /** Parses the content of the EditSchema tag in contacts.xml. */
+ protected final void parseEditSchema(Context context, XmlPullParser parser, AttributeSet attrs)
+ throws XmlPullParserException, IOException, DefinitionException {
+
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ final int depth = parser.getDepth();
+ if (type != XmlPullParser.START_TAG || depth != outerDepth + 1) {
+ continue; // Not direct child tag
+ }
+
+ final String tag = parser.getName();
+
+ if (Tag.DATA_KIND.equals(tag)) {
+ for (DataKind kind : KindParser.INSTANCE.parseDataKindTag(context, parser, attrs)) {
+ addKind(kind);
+ }
+ } else {
+ Log.w(TAG, "Skipping unknown tag " + tag);
+ }
+ }
+ }
+
+ private interface Tag {
+
+ String DATA_KIND = "DataKind";
+ String TYPE = "Type";
+ }
+
+ private interface Attr {
+
+ String MAX_OCCURRENCE = "maxOccurs";
+ String DATE_WITH_TIME = "dateWithTime";
+ String YEAR_OPTIONAL = "yearOptional";
+ String KIND = "kind";
+ String TYPE = "type";
+ }
+
+ protected interface Weight {
+
+ int NONE = -1;
+ int PHONE = 10;
+ int EMAIL = 15;
+ int STRUCTURED_POSTAL = 25;
+ int NICKNAME = 111;
+ int EVENT = 120;
+ int ORGANIZATION = 125;
+ int NOTE = 130;
+ int IM = 140;
+ int SIP_ADDRESS = 145;
+ int GROUP_MEMBERSHIP = 150;
+ int WEBSITE = 160;
+ int RELATIONSHIP = 999;
+ }
+
+ /**
+ * Simple inflater that assumes a string resource has a "%s" that will be filled from the given
+ * column.
+ */
+ public static class SimpleInflater implements StringInflater {
+
+ private final int mStringRes;
+ private final String mColumnName;
+
+ public SimpleInflater(int stringRes) {
+ this(stringRes, null);
+ }
+
+ public SimpleInflater(String columnName) {
+ this(-1, columnName);
+ }
+
+ public SimpleInflater(int stringRes, String columnName) {
+ mStringRes = stringRes;
+ mColumnName = columnName;
+ }
+
+ @Override
+ public CharSequence inflateUsing(Context context, ContentValues values) {
+ final boolean validColumn = values.containsKey(mColumnName);
+ final boolean validString = mStringRes > 0;
+
+ final CharSequence stringValue = validString ? context.getText(mStringRes) : null;
+ final CharSequence columnValue = validColumn ? values.getAsString(mColumnName) : null;
+
+ if (validString && validColumn) {
+ return String.format(stringValue.toString(), columnValue);
+ } else if (validString) {
+ return stringValue;
+ } else if (validColumn) {
+ return columnValue;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName()
+ + " mStringRes="
+ + mStringRes
+ + " mColumnName"
+ + mColumnName;
+ }
+
+ public String getColumnNameForTest() {
+ return mColumnName;
+ }
+ }
+
+ public abstract static class CommonInflater implements StringInflater {
+
+ protected abstract int getTypeLabelResource(Integer type);
+
+ protected boolean isCustom(Integer type) {
+ return type == BaseTypes.TYPE_CUSTOM;
+ }
+
+ protected String getTypeColumn() {
+ return Phone.TYPE;
+ }
+
+ protected String getLabelColumn() {
+ return Phone.LABEL;
+ }
+
+ protected CharSequence getTypeLabel(Resources res, Integer type, CharSequence label) {
+ final int labelRes = getTypeLabelResource(type);
+ if (type == null) {
+ return res.getText(labelRes);
+ } else if (isCustom(type)) {
+ return res.getString(labelRes, label == null ? "" : label);
+ } else {
+ return res.getText(labelRes);
+ }
+ }
+
+ @Override
+ public CharSequence inflateUsing(Context context, ContentValues values) {
+ final Integer type = values.getAsInteger(getTypeColumn());
+ final String label = values.getAsString(getLabelColumn());
+ return getTypeLabel(context.getResources(), type, label);
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+ }
+
+ public static class PhoneActionInflater extends CommonInflater {
+
+ @Override
+ protected boolean isCustom(Integer type) {
+ return ContactDisplayUtils.isCustomPhoneType(type);
+ }
+
+ @Override
+ protected int getTypeLabelResource(Integer type) {
+ return ContactDisplayUtils.getPhoneLabelResourceId(type);
+ }
+ }
+
+ public static class PhoneActionAltInflater extends CommonInflater {
+
+ @Override
+ protected boolean isCustom(Integer type) {
+ return ContactDisplayUtils.isCustomPhoneType(type);
+ }
+
+ @Override
+ protected int getTypeLabelResource(Integer type) {
+ return ContactDisplayUtils.getSmsLabelResourceId(type);
+ }
+ }
+
+ public static class EmailActionInflater extends CommonInflater {
+
+ @Override
+ protected int getTypeLabelResource(Integer type) {
+ if (type == null) {
+ return R.string.email;
+ }
+ switch (type) {
+ case Email.TYPE_HOME:
+ return R.string.email_home;
+ case Email.TYPE_WORK:
+ return R.string.email_work;
+ case Email.TYPE_OTHER:
+ return R.string.email_other;
+ case Email.TYPE_MOBILE:
+ return R.string.email_mobile;
+ default:
+ return R.string.email_custom;
+ }
+ }
+ }
+
+ public static class EventActionInflater extends CommonInflater {
+
+ @Override
+ protected int getTypeLabelResource(Integer type) {
+ return Event.getTypeResource(type);
+ }
+ }
+
+ public static class RelationActionInflater extends CommonInflater {
+
+ @Override
+ protected int getTypeLabelResource(Integer type) {
+ return Relation.getTypeLabelResource(type == null ? Relation.TYPE_CUSTOM : type);
+ }
+ }
+
+ public static class PostalActionInflater extends CommonInflater {
+
+ @Override
+ protected int getTypeLabelResource(Integer type) {
+ if (type == null) {
+ return R.string.map_other;
+ }
+ switch (type) {
+ case StructuredPostal.TYPE_HOME:
+ return R.string.map_home;
+ case StructuredPostal.TYPE_WORK:
+ return R.string.map_work;
+ case StructuredPostal.TYPE_OTHER:
+ return R.string.map_other;
+ default:
+ return R.string.map_custom;
+ }
+ }
+ }
+
+ public static class ImActionInflater extends CommonInflater {
+
+ @Override
+ protected String getTypeColumn() {
+ return Im.PROTOCOL;
+ }
+
+ @Override
+ protected String getLabelColumn() {
+ return Im.CUSTOM_PROTOCOL;
+ }
+
+ @Override
+ protected int getTypeLabelResource(Integer type) {
+ if (type == null) {
+ return R.string.chat;
+ }
+ switch (type) {
+ case Im.PROTOCOL_AIM:
+ return R.string.chat_aim;
+ case Im.PROTOCOL_MSN:
+ return R.string.chat_msn;
+ case Im.PROTOCOL_YAHOO:
+ return R.string.chat_yahoo;
+ case Im.PROTOCOL_SKYPE:
+ return R.string.chat_skype;
+ case Im.PROTOCOL_QQ:
+ return R.string.chat_qq;
+ case Im.PROTOCOL_GOOGLE_TALK:
+ return R.string.chat_gtalk;
+ case Im.PROTOCOL_ICQ:
+ return R.string.chat_icq;
+ case Im.PROTOCOL_JABBER:
+ return R.string.chat_jabber;
+ case Im.PROTOCOL_NETMEETING:
+ return R.string.chat;
+ default:
+ return R.string.chat;
+ }
+ }
+ }
+
+ // TODO Extract it to its own class, and move all KindBuilders to it as well.
+ private static class KindParser {
+
+ public static final KindParser INSTANCE = new KindParser();
+
+ private final Map<String, KindBuilder> mBuilders = new ArrayMap<>();
+
+ private KindParser() {
+ addBuilder(new NameKindBuilder());
+ addBuilder(new NicknameKindBuilder());
+ addBuilder(new PhoneKindBuilder());
+ addBuilder(new EmailKindBuilder());
+ addBuilder(new StructuredPostalKindBuilder());
+ addBuilder(new ImKindBuilder());
+ addBuilder(new OrganizationKindBuilder());
+ addBuilder(new PhotoKindBuilder());
+ addBuilder(new NoteKindBuilder());
+ addBuilder(new WebsiteKindBuilder());
+ addBuilder(new SipAddressKindBuilder());
+ addBuilder(new GroupMembershipKindBuilder());
+ addBuilder(new EventKindBuilder());
+ addBuilder(new RelationshipKindBuilder());
+ }
+
+ private void addBuilder(KindBuilder builder) {
+ mBuilders.put(builder.getTagName(), builder);
+ }
+
+ /**
+ * Takes a {@link XmlPullParser} at the start of a DataKind tag, parses it and returns {@link
+ * DataKind}s. (Usually just one, but there are three for the "name" kind.)
+ *
+ * <p>This method returns a list, because we need to add 3 kinds for the name data kind.
+ * (structured, display and phonetic)
+ */
+ public List<DataKind> parseDataKindTag(
+ Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException {
+ final String kind = getAttr(attrs, Attr.KIND);
+ final KindBuilder builder = mBuilders.get(kind);
+ if (builder != null) {
+ return builder.parseDataKind(context, parser, attrs);
+ } else {
+ throw new DefinitionException("Undefined data kind '" + kind + "'");
+ }
+ }
+ }
+
+ private abstract static class KindBuilder {
+
+ public abstract String getTagName();
+
+ /** DataKind tag parser specific to each kind. Subclasses must implement it. */
+ public abstract List<DataKind> parseDataKind(
+ Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException;
+
+ /** Creates a new {@link DataKind}, and also parses the child Type tags in the DataKind tag. */
+ protected final DataKind newDataKind(
+ Context context,
+ XmlPullParser parser,
+ AttributeSet attrs,
+ boolean isPseudo,
+ String mimeType,
+ String typeColumn,
+ int titleRes,
+ int weight,
+ StringInflater actionHeader,
+ StringInflater actionBody)
+ throws DefinitionException, XmlPullParserException, IOException {
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Adding DataKind: " + mimeType);
+ }
+
+ final DataKind kind = new DataKind(mimeType, titleRes, weight, true);
+ kind.typeColumn = typeColumn;
+ kind.actionHeader = actionHeader;
+ kind.actionBody = actionBody;
+ kind.fieldList = new ArrayList<>();
+
+ // Get more information from the tag...
+ // A pseudo data kind doesn't have corresponding tag the XML, so we skip this.
+ if (!isPseudo) {
+ kind.typeOverallMax = getAttr(attrs, Attr.MAX_OCCURRENCE, -1);
+
+ // Process "Type" tags.
+ // If a kind has the type column, contacts.xml must have at least one type
+ // definition. Otherwise, it mustn't have a type definition.
+ if (kind.typeColumn != null) {
+ // Parse and add types.
+ kind.typeList = new ArrayList<>();
+ parseTypes(context, parser, attrs, kind, true);
+ if (kind.typeList.size() == 0) {
+ throw new DefinitionException("Kind " + kind.mimeType + " must have at least one type");
+ }
+ } else {
+ // Make sure it has no types.
+ parseTypes(context, parser, attrs, kind, false /* can't have types */);
+ }
+ }
+
+ return kind;
+ }
+
+ /**
+ * Parses Type elements in a DataKind element, and if {@code canHaveTypes} is true adds them to
+ * the given {@link DataKind}. Otherwise the {@link DataKind} can't have a type, so throws
+ * {@link DefinitionException}.
+ */
+ private void parseTypes(
+ Context context,
+ XmlPullParser parser,
+ AttributeSet attrs,
+ DataKind kind,
+ boolean canHaveTypes)
+ throws DefinitionException, XmlPullParserException, IOException {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ final int depth = parser.getDepth();
+ if (type != XmlPullParser.START_TAG || depth != outerDepth + 1) {
+ continue; // Not direct child tag
+ }
+
+ final String tag = parser.getName();
+ if (Tag.TYPE.equals(tag)) {
+ if (canHaveTypes) {
+ kind.typeList.add(parseTypeTag(parser, attrs, kind));
+ } else {
+ throw new DefinitionException("Kind " + kind.mimeType + " can't have types");
+ }
+ } else {
+ throw new DefinitionException("Unknown tag: " + tag);
+ }
+ }
+ }
+
+ /**
+ * Parses a single Type element and returns an {@link EditType} built from it. Uses {@link
+ * #buildEditTypeForTypeTag} defined in subclasses to actually build an {@link EditType}.
+ */
+ private EditType parseTypeTag(XmlPullParser parser, AttributeSet attrs, DataKind kind)
+ throws DefinitionException {
+
+ final String typeName = getAttr(attrs, Attr.TYPE);
+
+ final EditType et = buildEditTypeForTypeTag(attrs, typeName);
+ if (et == null) {
+ throw new DefinitionException(
+ "Undefined type '" + typeName + "' for data kind '" + kind.mimeType + "'");
+ }
+ et.specificMax = getAttr(attrs, Attr.MAX_OCCURRENCE, -1);
+
+ return et;
+ }
+
+ /**
+ * Returns an {@link EditType} for the given "type". Subclasses may optionally use the
+ * attributes in the tag to set optional values. (e.g. "yearOptional" for the event kind)
+ */
+ protected EditType buildEditTypeForTypeTag(AttributeSet attrs, String type) {
+ return null;
+ }
+
+ protected final void throwIfList(DataKind kind) throws DefinitionException {
+ if (kind.typeOverallMax != 1) {
+ throw new DefinitionException("Kind " + kind.mimeType + " must have 'overallMax=\"1\"'");
+ }
+ }
+ }
+
+ /** DataKind parser for Name. (structured, display, phonetic) */
+ private static class NameKindBuilder extends KindBuilder {
+
+ private static void checkAttributeTrue(boolean value, String attrName)
+ throws DefinitionException {
+ if (!value) {
+ throw new DefinitionException(attrName + " must be true");
+ }
+ }
+
+ @Override
+ public String getTagName() {
+ return "name";
+ }
+
+ @Override
+ public List<DataKind> parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException {
+
+ // Build 3 data kinds:
+ // - StructuredName.CONTENT_ITEM_TYPE
+ // - DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME
+ // - DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME
+
+ final boolean displayOrderPrimary =
+ context.getResources().getBoolean(R.bool.config_editor_field_order_primary);
+
+ final boolean supportsDisplayName = getAttr(attrs, "supportsDisplayName", false);
+ final boolean supportsPrefix = getAttr(attrs, "supportsPrefix", false);
+ final boolean supportsMiddleName = getAttr(attrs, "supportsMiddleName", false);
+ final boolean supportsSuffix = getAttr(attrs, "supportsSuffix", false);
+ final boolean supportsPhoneticFamilyName =
+ getAttr(attrs, "supportsPhoneticFamilyName", false);
+ final boolean supportsPhoneticMiddleName =
+ getAttr(attrs, "supportsPhoneticMiddleName", false);
+ final boolean supportsPhoneticGivenName = getAttr(attrs, "supportsPhoneticGivenName", false);
+
+ // For now, every things must be supported.
+ checkAttributeTrue(supportsDisplayName, "supportsDisplayName");
+ checkAttributeTrue(supportsPrefix, "supportsPrefix");
+ checkAttributeTrue(supportsMiddleName, "supportsMiddleName");
+ checkAttributeTrue(supportsSuffix, "supportsSuffix");
+ checkAttributeTrue(supportsPhoneticFamilyName, "supportsPhoneticFamilyName");
+ checkAttributeTrue(supportsPhoneticMiddleName, "supportsPhoneticMiddleName");
+ checkAttributeTrue(supportsPhoneticGivenName, "supportsPhoneticGivenName");
+
+ final List<DataKind> kinds = new ArrayList<>();
+
+ // Structured name
+ final DataKind ks =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ false,
+ StructuredName.CONTENT_ITEM_TYPE,
+ null,
+ R.string.nameLabelsGroup,
+ Weight.NONE,
+ new SimpleInflater(R.string.nameLabelsGroup),
+ new SimpleInflater(Nickname.NAME));
+
+ throwIfList(ks);
+ kinds.add(ks);
+
+ // Note about setLongForm/setShortForm below.
+ // We need to set this only when the type supports display name. (=supportsDisplayName)
+ // Otherwise (i.e. Exchange) we don't set these flags, but instead make some fields
+ // "optional".
+
+ ks.fieldList.add(
+ new EditField(StructuredName.DISPLAY_NAME, R.string.full_name, FLAGS_PERSON_NAME));
+ ks.fieldList.add(
+ new EditField(StructuredName.PREFIX, R.string.name_prefix, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ ks.fieldList.add(
+ new EditField(StructuredName.FAMILY_NAME, R.string.name_family, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ ks.fieldList.add(
+ new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ ks.fieldList.add(
+ new EditField(StructuredName.GIVEN_NAME, R.string.name_given, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ ks.fieldList.add(
+ new EditField(StructuredName.SUFFIX, R.string.name_suffix, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ ks.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_FAMILY_NAME, R.string.name_phonetic_family, FLAGS_PHONETIC));
+ ks.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_MIDDLE_NAME, R.string.name_phonetic_middle, FLAGS_PHONETIC));
+ ks.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_GIVEN_NAME, R.string.name_phonetic_given, FLAGS_PHONETIC));
+
+ // Display name
+ final DataKind kd =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ true,
+ DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME,
+ null,
+ R.string.nameLabelsGroup,
+ Weight.NONE,
+ new SimpleInflater(R.string.nameLabelsGroup),
+ new SimpleInflater(Nickname.NAME));
+ kd.typeOverallMax = 1;
+ kinds.add(kd);
+
+ kd.fieldList.add(
+ new EditField(StructuredName.DISPLAY_NAME, R.string.full_name, FLAGS_PERSON_NAME)
+ .setShortForm(true));
+
+ if (!displayOrderPrimary) {
+ kd.fieldList.add(
+ new EditField(StructuredName.PREFIX, R.string.name_prefix, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kd.fieldList.add(
+ new EditField(StructuredName.FAMILY_NAME, R.string.name_family, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kd.fieldList.add(
+ new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kd.fieldList.add(
+ new EditField(StructuredName.GIVEN_NAME, R.string.name_given, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kd.fieldList.add(
+ new EditField(StructuredName.SUFFIX, R.string.name_suffix, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ } else {
+ kd.fieldList.add(
+ new EditField(StructuredName.PREFIX, R.string.name_prefix, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kd.fieldList.add(
+ new EditField(StructuredName.GIVEN_NAME, R.string.name_given, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kd.fieldList.add(
+ new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kd.fieldList.add(
+ new EditField(StructuredName.FAMILY_NAME, R.string.name_family, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ kd.fieldList.add(
+ new EditField(StructuredName.SUFFIX, R.string.name_suffix, FLAGS_PERSON_NAME)
+ .setLongForm(true));
+ }
+
+ // Phonetic name
+ final DataKind kp =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ true,
+ DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME,
+ null,
+ R.string.name_phonetic,
+ Weight.NONE,
+ new SimpleInflater(R.string.nameLabelsGroup),
+ new SimpleInflater(Nickname.NAME));
+ kp.typeOverallMax = 1;
+ kinds.add(kp);
+
+ // We may want to change the order depending on displayOrderPrimary too.
+ kp.fieldList.add(
+ new EditField(
+ DataKind.PSEUDO_COLUMN_PHONETIC_NAME, R.string.name_phonetic, FLAGS_PHONETIC)
+ .setShortForm(true));
+ kp.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_FAMILY_NAME,
+ R.string.name_phonetic_family,
+ FLAGS_PHONETIC)
+ .setLongForm(true));
+ kp.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_MIDDLE_NAME,
+ R.string.name_phonetic_middle,
+ FLAGS_PHONETIC)
+ .setLongForm(true));
+ kp.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_GIVEN_NAME, R.string.name_phonetic_given, FLAGS_PHONETIC)
+ .setLongForm(true));
+ return kinds;
+ }
+ }
+
+ private static class NicknameKindBuilder extends KindBuilder {
+
+ @Override
+ public String getTagName() {
+ return "nickname";
+ }
+
+ @Override
+ public List<DataKind> parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException {
+ final DataKind kind =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ false,
+ Nickname.CONTENT_ITEM_TYPE,
+ null,
+ R.string.nicknameLabelsGroup,
+ Weight.NICKNAME,
+ new SimpleInflater(R.string.nicknameLabelsGroup),
+ new SimpleInflater(Nickname.NAME));
+
+ kind.fieldList.add(
+ new EditField(Nickname.NAME, R.string.nicknameLabelsGroup, FLAGS_PERSON_NAME));
+
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Nickname.TYPE, Nickname.TYPE_DEFAULT);
+
+ throwIfList(kind);
+ List<DataKind> result = new ArrayList<>();
+ result.add(kind);
+ return result;
+ }
+ }
+
+ private static class PhoneKindBuilder extends KindBuilder {
+
+ /** Just to avoid line-wrapping... */
+ protected static EditType build(int type, boolean secondary) {
+ return new EditType(type, Phone.getTypeLabelResource(type)).setSecondary(secondary);
+ }
+
+ @Override
+ public String getTagName() {
+ return "phone";
+ }
+
+ @Override
+ public List<DataKind> parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException {
+ final DataKind kind =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ false,
+ Phone.CONTENT_ITEM_TYPE,
+ Phone.TYPE,
+ R.string.phoneLabelsGroup,
+ Weight.PHONE,
+ new PhoneActionInflater(),
+ new SimpleInflater(Phone.NUMBER));
+
+ kind.iconAltRes = R.drawable.ic_message_24dp;
+ kind.iconAltDescriptionRes = R.string.sms;
+ kind.actionAltHeader = new PhoneActionAltInflater();
+
+ kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE));
+
+ List<DataKind> result = new ArrayList<>();
+ result.add(kind);
+ return result;
+ }
+
+ @Override
+ protected EditType buildEditTypeForTypeTag(AttributeSet attrs, String type) {
+ if ("home".equals(type)) {
+ return build(Phone.TYPE_HOME, false);
+ }
+ if ("mobile".equals(type)) {
+ return build(Phone.TYPE_MOBILE, false);
+ }
+ if ("work".equals(type)) {
+ return build(Phone.TYPE_WORK, false);
+ }
+ if ("fax_work".equals(type)) {
+ return build(Phone.TYPE_FAX_WORK, true);
+ }
+ if ("fax_home".equals(type)) {
+ return build(Phone.TYPE_FAX_HOME, true);
+ }
+ if ("pager".equals(type)) {
+ return build(Phone.TYPE_PAGER, true);
+ }
+ if ("other".equals(type)) {
+ return build(Phone.TYPE_OTHER, false);
+ }
+ if ("callback".equals(type)) {
+ return build(Phone.TYPE_CALLBACK, true);
+ }
+ if ("car".equals(type)) {
+ return build(Phone.TYPE_CAR, true);
+ }
+ if ("company_main".equals(type)) {
+ return build(Phone.TYPE_COMPANY_MAIN, true);
+ }
+ if ("isdn".equals(type)) {
+ return build(Phone.TYPE_ISDN, true);
+ }
+ if ("main".equals(type)) {
+ return build(Phone.TYPE_MAIN, true);
+ }
+ if ("other_fax".equals(type)) {
+ return build(Phone.TYPE_OTHER_FAX, true);
+ }
+ if ("radio".equals(type)) {
+ return build(Phone.TYPE_RADIO, true);
+ }
+ if ("telex".equals(type)) {
+ return build(Phone.TYPE_TELEX, true);
+ }
+ if ("tty_tdd".equals(type)) {
+ return build(Phone.TYPE_TTY_TDD, true);
+ }
+ if ("work_mobile".equals(type)) {
+ return build(Phone.TYPE_WORK_MOBILE, true);
+ }
+ if ("work_pager".equals(type)) {
+ return build(Phone.TYPE_WORK_PAGER, true);
+ }
+
+ // Note "assistant" used to be a custom column for the fallback type, but not anymore.
+ if ("assistant".equals(type)) {
+ return build(Phone.TYPE_ASSISTANT, true);
+ }
+ if ("mms".equals(type)) {
+ return build(Phone.TYPE_MMS, true);
+ }
+ if ("custom".equals(type)) {
+ return build(Phone.TYPE_CUSTOM, true).setCustomColumn(Phone.LABEL);
+ }
+ return null;
+ }
+ }
+
+ private static class EmailKindBuilder extends KindBuilder {
+
+ @Override
+ public String getTagName() {
+ return "email";
+ }
+
+ @Override
+ public List<DataKind> parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException {
+ final DataKind kind =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ false,
+ Email.CONTENT_ITEM_TYPE,
+ Email.TYPE,
+ R.string.emailLabelsGroup,
+ Weight.EMAIL,
+ new EmailActionInflater(),
+ new SimpleInflater(Email.DATA));
+ kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
+
+ List<DataKind> result = new ArrayList<>();
+ result.add(kind);
+ return result;
+ }
+
+ @Override
+ protected EditType buildEditTypeForTypeTag(AttributeSet attrs, String type) {
+ // EditType is mutable, so we need to create a new instance every time.
+ if ("home".equals(type)) {
+ return buildEmailType(Email.TYPE_HOME);
+ }
+ if ("work".equals(type)) {
+ return buildEmailType(Email.TYPE_WORK);
+ }
+ if ("other".equals(type)) {
+ return buildEmailType(Email.TYPE_OTHER);
+ }
+ if ("mobile".equals(type)) {
+ return buildEmailType(Email.TYPE_MOBILE);
+ }
+ if ("custom".equals(type)) {
+ return buildEmailType(Email.TYPE_CUSTOM).setSecondary(true).setCustomColumn(Email.LABEL);
+ }
+ return null;
+ }
+ }
+
+ private static class StructuredPostalKindBuilder extends KindBuilder {
+
+ @Override
+ public String getTagName() {
+ return "postal";
+ }
+
+ @Override
+ public List<DataKind> parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException {
+ final DataKind kind =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ false,
+ StructuredPostal.CONTENT_ITEM_TYPE,
+ StructuredPostal.TYPE,
+ R.string.postalLabelsGroup,
+ Weight.STRUCTURED_POSTAL,
+ new PostalActionInflater(),
+ new SimpleInflater(StructuredPostal.FORMATTED_ADDRESS));
+
+ if (getAttr(attrs, "needsStructured", false)) {
+ if (Locale.JAPANESE.getLanguage().equals(Locale.getDefault().getLanguage())) {
+ // Japanese order
+ kind.fieldList.add(
+ new EditField(StructuredPostal.COUNTRY, R.string.postal_country, FLAGS_POSTAL)
+ .setOptional(true));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.POSTCODE, R.string.postal_postcode, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.REGION, R.string.postal_region, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.CITY, R.string.postal_city, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.STREET, R.string.postal_street, FLAGS_POSTAL));
+ } else {
+ // Generic order
+ kind.fieldList.add(
+ new EditField(StructuredPostal.STREET, R.string.postal_street, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.CITY, R.string.postal_city, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.REGION, R.string.postal_region, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.POSTCODE, R.string.postal_postcode, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.COUNTRY, R.string.postal_country, FLAGS_POSTAL)
+ .setOptional(true));
+ }
+ } else {
+ kind.maxLinesForDisplay = MAX_LINES_FOR_POSTAL_ADDRESS;
+ kind.fieldList.add(
+ new EditField(
+ StructuredPostal.FORMATTED_ADDRESS, R.string.postal_address, FLAGS_POSTAL));
+ }
+
+ List<DataKind> result = new ArrayList<>();
+ result.add(kind);
+ return result;
+ }
+
+ @Override
+ protected EditType buildEditTypeForTypeTag(AttributeSet attrs, String type) {
+ // EditType is mutable, so we need to create a new instance every time.
+ if ("home".equals(type)) {
+ return buildPostalType(StructuredPostal.TYPE_HOME);
+ }
+ if ("work".equals(type)) {
+ return buildPostalType(StructuredPostal.TYPE_WORK);
+ }
+ if ("other".equals(type)) {
+ return buildPostalType(StructuredPostal.TYPE_OTHER);
+ }
+ if ("custom".equals(type)) {
+ return buildPostalType(StructuredPostal.TYPE_CUSTOM)
+ .setSecondary(true)
+ .setCustomColumn(Email.LABEL);
+ }
+ return null;
+ }
+ }
+
+ private static class ImKindBuilder extends KindBuilder {
+
+ @Override
+ public String getTagName() {
+ return "im";
+ }
+
+ @Override
+ public List<DataKind> parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException {
+
+ // IM is special:
+ // - It uses "protocol" as the custom label field
+ // - Its TYPE is fixed to TYPE_OTHER
+
+ final DataKind kind =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ false,
+ Im.CONTENT_ITEM_TYPE,
+ Im.PROTOCOL,
+ R.string.imLabelsGroup,
+ Weight.IM,
+ new ImActionInflater(),
+ new SimpleInflater(Im.DATA) // header / action
+ );
+ kind.fieldList.add(new EditField(Im.DATA, R.string.imLabelsGroup, FLAGS_EMAIL));
+
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Im.TYPE, Im.TYPE_OTHER);
+
+ List<DataKind> result = new ArrayList<>();
+ result.add(kind);
+ return result;
+ }
+
+ @Override
+ protected EditType buildEditTypeForTypeTag(AttributeSet attrs, String type) {
+ if ("aim".equals(type)) {
+ return buildImType(Im.PROTOCOL_AIM);
+ }
+ if ("msn".equals(type)) {
+ return buildImType(Im.PROTOCOL_MSN);
+ }
+ if ("yahoo".equals(type)) {
+ return buildImType(Im.PROTOCOL_YAHOO);
+ }
+ if ("skype".equals(type)) {
+ return buildImType(Im.PROTOCOL_SKYPE);
+ }
+ if ("qq".equals(type)) {
+ return buildImType(Im.PROTOCOL_QQ);
+ }
+ if ("google_talk".equals(type)) {
+ return buildImType(Im.PROTOCOL_GOOGLE_TALK);
+ }
+ if ("icq".equals(type)) {
+ return buildImType(Im.PROTOCOL_ICQ);
+ }
+ if ("jabber".equals(type)) {
+ return buildImType(Im.PROTOCOL_JABBER);
+ }
+ if ("custom".equals(type)) {
+ return buildImType(Im.PROTOCOL_CUSTOM)
+ .setSecondary(true)
+ .setCustomColumn(Im.CUSTOM_PROTOCOL);
+ }
+ return null;
+ }
+ }
+
+ private static class OrganizationKindBuilder extends KindBuilder {
+
+ @Override
+ public String getTagName() {
+ return "organization";
+ }
+
+ @Override
+ public List<DataKind> parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException {
+ final DataKind kind =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ false,
+ Organization.CONTENT_ITEM_TYPE,
+ null,
+ R.string.organizationLabelsGroup,
+ Weight.ORGANIZATION,
+ new SimpleInflater(R.string.organizationLabelsGroup),
+ ORGANIZATION_BODY_INFLATER);
+
+ kind.fieldList.add(
+ new EditField(Organization.COMPANY, R.string.ghostData_company, FLAGS_GENERIC_NAME));
+ kind.fieldList.add(
+ new EditField(Organization.TITLE, R.string.ghostData_title, FLAGS_GENERIC_NAME));
+
+ throwIfList(kind);
+
+ List<DataKind> result = new ArrayList<>();
+ result.add(kind);
+ return result;
+ }
+ }
+
+ private static class PhotoKindBuilder extends KindBuilder {
+
+ @Override
+ public String getTagName() {
+ return "photo";
+ }
+
+ @Override
+ public List<DataKind> parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException {
+ final DataKind kind =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ false,
+ Photo.CONTENT_ITEM_TYPE,
+ null /* no type */,
+ Weight.NONE,
+ -1,
+ null,
+ null // no header, no body
+ );
+
+ kind.fieldList.add(new EditField(Photo.PHOTO, -1, -1));
+
+ throwIfList(kind);
+
+ List<DataKind> result = new ArrayList<>();
+ result.add(kind);
+ return result;
+ }
+ }
+
+ private static class NoteKindBuilder extends KindBuilder {
+
+ @Override
+ public String getTagName() {
+ return "note";
+ }
+
+ @Override
+ public List<DataKind> parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException {
+ final DataKind kind =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ false,
+ Note.CONTENT_ITEM_TYPE,
+ null,
+ R.string.label_notes,
+ Weight.NOTE,
+ new SimpleInflater(R.string.label_notes),
+ new SimpleInflater(Note.NOTE));
+
+ kind.fieldList.add(new EditField(Note.NOTE, R.string.label_notes, FLAGS_NOTE));
+ kind.maxLinesForDisplay = MAX_LINES_FOR_NOTE;
+
+ throwIfList(kind);
+
+ List<DataKind> result = new ArrayList<>();
+ result.add(kind);
+ return result;
+ }
+ }
+
+ private static class WebsiteKindBuilder extends KindBuilder {
+
+ @Override
+ public String getTagName() {
+ return "website";
+ }
+
+ @Override
+ public List<DataKind> parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException {
+ final DataKind kind =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ false,
+ Website.CONTENT_ITEM_TYPE,
+ null,
+ R.string.websiteLabelsGroup,
+ Weight.WEBSITE,
+ new SimpleInflater(R.string.websiteLabelsGroup),
+ new SimpleInflater(Website.URL));
+
+ kind.fieldList.add(new EditField(Website.URL, R.string.websiteLabelsGroup, FLAGS_WEBSITE));
+
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Website.TYPE, Website.TYPE_OTHER);
+
+ List<DataKind> result = new ArrayList<>();
+ result.add(kind);
+ return result;
+ }
+ }
+
+ private static class SipAddressKindBuilder extends KindBuilder {
+
+ @Override
+ public String getTagName() {
+ return "sip_address";
+ }
+
+ @Override
+ public List<DataKind> parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException {
+ final DataKind kind =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ false,
+ SipAddress.CONTENT_ITEM_TYPE,
+ null,
+ R.string.label_sip_address,
+ Weight.SIP_ADDRESS,
+ new SimpleInflater(R.string.label_sip_address),
+ new SimpleInflater(SipAddress.SIP_ADDRESS));
+
+ kind.fieldList.add(
+ new EditField(SipAddress.SIP_ADDRESS, R.string.label_sip_address, FLAGS_SIP_ADDRESS));
+
+ throwIfList(kind);
+
+ List<DataKind> result = new ArrayList<>();
+ result.add(kind);
+ return result;
+ }
+ }
+
+ private static class GroupMembershipKindBuilder extends KindBuilder {
+
+ @Override
+ public String getTagName() {
+ return "group_membership";
+ }
+
+ @Override
+ public List<DataKind> parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException {
+ final DataKind kind =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ false,
+ GroupMembership.CONTENT_ITEM_TYPE,
+ null,
+ R.string.groupsLabel,
+ Weight.GROUP_MEMBERSHIP,
+ null,
+ null);
+
+ kind.fieldList.add(new EditField(GroupMembership.GROUP_ROW_ID, -1, -1));
+ kind.maxLinesForDisplay = MAX_LINES_FOR_GROUP;
+
+ throwIfList(kind);
+
+ List<DataKind> result = new ArrayList<>();
+ result.add(kind);
+ return result;
+ }
+ }
+
+ /**
+ * Event DataKind parser.
+ *
+ * <p>Event DataKind is used only for Google/Exchange types, so this parser is not used for now.
+ */
+ private static class EventKindBuilder extends KindBuilder {
+
+ @Override
+ public String getTagName() {
+ return "event";
+ }
+
+ @Override
+ public List<DataKind> parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException {
+ final DataKind kind =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ false,
+ Event.CONTENT_ITEM_TYPE,
+ Event.TYPE,
+ R.string.eventLabelsGroup,
+ Weight.EVENT,
+ new EventActionInflater(),
+ new SimpleInflater(Event.START_DATE));
+
+ kind.fieldList.add(new EditField(Event.DATA, R.string.eventLabelsGroup, FLAGS_EVENT));
+
+ if (getAttr(attrs, Attr.DATE_WITH_TIME, false)) {
+ kind.dateFormatWithoutYear = CommonDateUtils.NO_YEAR_DATE_AND_TIME_FORMAT;
+ kind.dateFormatWithYear = CommonDateUtils.DATE_AND_TIME_FORMAT;
+ } else {
+ kind.dateFormatWithoutYear = CommonDateUtils.NO_YEAR_DATE_FORMAT;
+ kind.dateFormatWithYear = CommonDateUtils.FULL_DATE_FORMAT;
+ }
+
+ List<DataKind> result = new ArrayList<>();
+ result.add(kind);
+ return result;
+ }
+
+ @Override
+ protected EditType buildEditTypeForTypeTag(AttributeSet attrs, String type) {
+ final boolean yo = getAttr(attrs, Attr.YEAR_OPTIONAL, false);
+
+ if ("birthday".equals(type)) {
+ return buildEventType(Event.TYPE_BIRTHDAY, yo).setSpecificMax(1);
+ }
+ if ("anniversary".equals(type)) {
+ return buildEventType(Event.TYPE_ANNIVERSARY, yo);
+ }
+ if ("other".equals(type)) {
+ return buildEventType(Event.TYPE_OTHER, yo);
+ }
+ if ("custom".equals(type)) {
+ return buildEventType(Event.TYPE_CUSTOM, yo)
+ .setSecondary(true)
+ .setCustomColumn(Event.LABEL);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Relationship DataKind parser.
+ *
+ * <p>Relationship DataKind is used only for Google/Exchange types, so this parser is not used for
+ * now.
+ */
+ private static class RelationshipKindBuilder extends KindBuilder {
+
+ @Override
+ public String getTagName() {
+ return "relationship";
+ }
+
+ @Override
+ public List<DataKind> parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)
+ throws DefinitionException, XmlPullParserException, IOException {
+ final DataKind kind =
+ newDataKind(
+ context,
+ parser,
+ attrs,
+ false,
+ Relation.CONTENT_ITEM_TYPE,
+ Relation.TYPE,
+ R.string.relationLabelsGroup,
+ Weight.RELATIONSHIP,
+ new RelationActionInflater(),
+ new SimpleInflater(Relation.NAME));
+
+ kind.fieldList.add(
+ new EditField(Relation.DATA, R.string.relationLabelsGroup, FLAGS_RELATION));
+
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Relation.TYPE, Relation.TYPE_SPOUSE);
+
+ List<DataKind> result = new ArrayList<>();
+ result.add(kind);
+ return result;
+ }
+
+ @Override
+ protected EditType buildEditTypeForTypeTag(AttributeSet attrs, String type) {
+ // EditType is mutable, so we need to create a new instance every time.
+ if ("assistant".equals(type)) {
+ return buildRelationType(Relation.TYPE_ASSISTANT);
+ }
+ if ("brother".equals(type)) {
+ return buildRelationType(Relation.TYPE_BROTHER);
+ }
+ if ("child".equals(type)) {
+ return buildRelationType(Relation.TYPE_CHILD);
+ }
+ if ("domestic_partner".equals(type)) {
+ return buildRelationType(Relation.TYPE_DOMESTIC_PARTNER);
+ }
+ if ("father".equals(type)) {
+ return buildRelationType(Relation.TYPE_FATHER);
+ }
+ if ("friend".equals(type)) {
+ return buildRelationType(Relation.TYPE_FRIEND);
+ }
+ if ("manager".equals(type)) {
+ return buildRelationType(Relation.TYPE_MANAGER);
+ }
+ if ("mother".equals(type)) {
+ return buildRelationType(Relation.TYPE_MOTHER);
+ }
+ if ("parent".equals(type)) {
+ return buildRelationType(Relation.TYPE_PARENT);
+ }
+ if ("partner".equals(type)) {
+ return buildRelationType(Relation.TYPE_PARTNER);
+ }
+ if ("referred_by".equals(type)) {
+ return buildRelationType(Relation.TYPE_REFERRED_BY);
+ }
+ if ("relative".equals(type)) {
+ return buildRelationType(Relation.TYPE_RELATIVE);
+ }
+ if ("sister".equals(type)) {
+ return buildRelationType(Relation.TYPE_SISTER);
+ }
+ if ("spouse".equals(type)) {
+ return buildRelationType(Relation.TYPE_SPOUSE);
+ }
+ if ("custom".equals(type)) {
+ return buildRelationType(Relation.TYPE_CUSTOM)
+ .setSecondary(true)
+ .setCustomColumn(Relation.LABEL);
+ }
+ return null;
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/model/account/ExchangeAccountType.java b/java/com/android/contacts/common/model/account/ExchangeAccountType.java
new file mode 100644
index 000000000..a27028e80
--- /dev/null
+++ b/java/com/android/contacts/common/model/account/ExchangeAccountType.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2009 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.model.account;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.util.Log;
+import com.android.contacts.common.R;
+import com.android.contacts.common.model.dataitem.DataKind;
+import com.android.contacts.common.util.CommonDateUtils;
+import java.util.ArrayList;
+import java.util.Locale;
+
+public class ExchangeAccountType extends BaseAccountType {
+
+ private static final String TAG = "ExchangeAccountType";
+
+ private static final String ACCOUNT_TYPE_AOSP = "com.android.exchange";
+ private static final String ACCOUNT_TYPE_GOOGLE_1 = "com.google.android.exchange";
+ private static final String ACCOUNT_TYPE_GOOGLE_2 = "com.google.android.gm.exchange";
+
+ public ExchangeAccountType(Context context, String authenticatorPackageName, String type) {
+ this.accountType = type;
+ this.resourcePackageName = null;
+ this.syncAdapterPackageName = authenticatorPackageName;
+
+ try {
+ addDataKindStructuredName(context);
+ addDataKindDisplayName(context);
+ addDataKindPhoneticName(context);
+ addDataKindNickname(context);
+ addDataKindPhone(context);
+ addDataKindEmail(context);
+ addDataKindStructuredPostal(context);
+ addDataKindIm(context);
+ addDataKindOrganization(context);
+ addDataKindPhoto(context);
+ addDataKindNote(context);
+ addDataKindEvent(context);
+ addDataKindWebsite(context);
+ addDataKindGroupMembership(context);
+
+ mIsInitialized = true;
+ } catch (DefinitionException e) {
+ Log.e(TAG, "Problem building account type", e);
+ }
+ }
+
+ public static boolean isExchangeType(String type) {
+ return ACCOUNT_TYPE_AOSP.equals(type)
+ || ACCOUNT_TYPE_GOOGLE_1.equals(type)
+ || ACCOUNT_TYPE_GOOGLE_2.equals(type);
+ }
+
+ @Override
+ protected DataKind addDataKindStructuredName(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(
+ StructuredName.CONTENT_ITEM_TYPE, R.string.nameLabelsGroup, Weight.NONE, true));
+ kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
+ kind.actionBody = new SimpleInflater(Nickname.NAME);
+
+ kind.typeOverallMax = 1;
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(
+ new EditField(StructuredName.PREFIX, R.string.name_prefix, FLAGS_PERSON_NAME)
+ .setOptional(true));
+ kind.fieldList.add(
+ new EditField(StructuredName.FAMILY_NAME, R.string.name_family, FLAGS_PERSON_NAME));
+ kind.fieldList.add(
+ new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle, FLAGS_PERSON_NAME));
+ kind.fieldList.add(
+ new EditField(StructuredName.GIVEN_NAME, R.string.name_given, FLAGS_PERSON_NAME));
+ kind.fieldList.add(
+ new EditField(StructuredName.SUFFIX, R.string.name_suffix, FLAGS_PERSON_NAME));
+
+ kind.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_FAMILY_NAME, R.string.name_phonetic_family, FLAGS_PHONETIC));
+ kind.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_GIVEN_NAME, R.string.name_phonetic_given, FLAGS_PHONETIC));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindDisplayName(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(
+ DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME,
+ R.string.nameLabelsGroup,
+ Weight.NONE,
+ true));
+
+ boolean displayOrderPrimary =
+ context.getResources().getBoolean(R.bool.config_editor_field_order_primary);
+ kind.typeOverallMax = 1;
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(
+ new EditField(StructuredName.PREFIX, R.string.name_prefix, FLAGS_PERSON_NAME)
+ .setOptional(true));
+ if (!displayOrderPrimary) {
+ kind.fieldList.add(
+ new EditField(StructuredName.FAMILY_NAME, R.string.name_family, FLAGS_PERSON_NAME));
+ kind.fieldList.add(
+ new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle, FLAGS_PERSON_NAME)
+ .setOptional(true));
+ kind.fieldList.add(
+ new EditField(StructuredName.GIVEN_NAME, R.string.name_given, FLAGS_PERSON_NAME));
+ } else {
+ kind.fieldList.add(
+ new EditField(StructuredName.GIVEN_NAME, R.string.name_given, FLAGS_PERSON_NAME));
+ kind.fieldList.add(
+ new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle, FLAGS_PERSON_NAME)
+ .setOptional(true));
+ kind.fieldList.add(
+ new EditField(StructuredName.FAMILY_NAME, R.string.name_family, FLAGS_PERSON_NAME));
+ }
+ kind.fieldList.add(
+ new EditField(StructuredName.SUFFIX, R.string.name_suffix, FLAGS_PERSON_NAME)
+ .setOptional(true));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindPhoneticName(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(
+ DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME,
+ R.string.name_phonetic,
+ Weight.NONE,
+ true));
+ kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
+ kind.actionBody = new SimpleInflater(Nickname.NAME);
+
+ kind.typeOverallMax = 1;
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_FAMILY_NAME, R.string.name_phonetic_family, FLAGS_PHONETIC));
+ kind.fieldList.add(
+ new EditField(
+ StructuredName.PHONETIC_GIVEN_NAME, R.string.name_phonetic_given, FLAGS_PHONETIC));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindNickname(Context context) throws DefinitionException {
+ final DataKind kind = super.addDataKindNickname(context);
+
+ kind.typeOverallMax = 1;
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(
+ new EditField(Nickname.NAME, R.string.nicknameLabelsGroup, FLAGS_PERSON_NAME));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindPhone(Context context) throws DefinitionException {
+ final DataKind kind = super.addDataKindPhone(context);
+
+ kind.typeColumn = Phone.TYPE;
+ kind.typeList = new ArrayList<>();
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE).setSpecificMax(1));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_HOME).setSpecificMax(2));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_WORK).setSpecificMax(2));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true).setSpecificMax(1));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true).setSpecificMax(1));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_PAGER).setSecondary(true).setSpecificMax(1));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_CAR).setSecondary(true).setSpecificMax(1));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_COMPANY_MAIN).setSecondary(true).setSpecificMax(1));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MMS).setSecondary(true).setSpecificMax(1));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_RADIO).setSecondary(true).setSpecificMax(1));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_ASSISTANT).setSecondary(true).setSpecificMax(1));
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindEmail(Context context) throws DefinitionException {
+ final DataKind kind = super.addDataKindEmail(context);
+
+ kind.typeOverallMax = 3;
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindStructuredPostal(Context context) throws DefinitionException {
+ final DataKind kind = super.addDataKindStructuredPostal(context);
+
+ final boolean useJapaneseOrder =
+ Locale.JAPANESE.getLanguage().equals(Locale.getDefault().getLanguage());
+ kind.typeColumn = StructuredPostal.TYPE;
+ kind.typeList = new ArrayList<>();
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_WORK).setSpecificMax(1));
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_HOME).setSpecificMax(1));
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_OTHER).setSpecificMax(1));
+
+ kind.fieldList = new ArrayList<>();
+ if (useJapaneseOrder) {
+ kind.fieldList.add(
+ new EditField(StructuredPostal.COUNTRY, R.string.postal_country, FLAGS_POSTAL)
+ .setOptional(true));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.POSTCODE, R.string.postal_postcode, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.REGION, R.string.postal_region, FLAGS_POSTAL));
+ kind.fieldList.add(new EditField(StructuredPostal.CITY, R.string.postal_city, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.STREET, R.string.postal_street, FLAGS_POSTAL));
+ } else {
+ kind.fieldList.add(
+ new EditField(StructuredPostal.STREET, R.string.postal_street, FLAGS_POSTAL));
+ kind.fieldList.add(new EditField(StructuredPostal.CITY, R.string.postal_city, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.REGION, R.string.postal_region, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.POSTCODE, R.string.postal_postcode, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.COUNTRY, R.string.postal_country, FLAGS_POSTAL)
+ .setOptional(true));
+ }
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindIm(Context context) throws DefinitionException {
+ final DataKind kind = super.addDataKindIm(context);
+
+ // Types are not supported for IM. There can be 3 IMs, but OWA only shows only the first
+ kind.typeOverallMax = 3;
+
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Im.TYPE, Im.TYPE_OTHER);
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Im.DATA, R.string.imLabelsGroup, FLAGS_EMAIL));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindOrganization(Context context) throws DefinitionException {
+ final DataKind kind = super.addDataKindOrganization(context);
+
+ kind.typeOverallMax = 1;
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(
+ new EditField(Organization.COMPANY, R.string.ghostData_company, FLAGS_GENERIC_NAME));
+ kind.fieldList.add(
+ new EditField(Organization.TITLE, R.string.ghostData_title, FLAGS_GENERIC_NAME));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindPhoto(Context context) throws DefinitionException {
+ final DataKind kind = super.addDataKindPhoto(context);
+
+ kind.typeOverallMax = 1;
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Photo.PHOTO, -1, -1));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindNote(Context context) throws DefinitionException {
+ final DataKind kind = super.addDataKindNote(context);
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Note.NOTE, R.string.label_notes, FLAGS_NOTE));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindEvent(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(Event.CONTENT_ITEM_TYPE, R.string.eventLabelsGroup, Weight.EVENT, true));
+ kind.actionHeader = new EventActionInflater();
+ kind.actionBody = new SimpleInflater(Event.START_DATE);
+
+ kind.typeOverallMax = 1;
+
+ kind.typeColumn = Event.TYPE;
+ kind.typeList = new ArrayList<>();
+ kind.typeList.add(buildEventType(Event.TYPE_BIRTHDAY, false).setSpecificMax(1));
+
+ kind.dateFormatWithYear = CommonDateUtils.DATE_AND_TIME_FORMAT;
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Event.DATA, R.string.eventLabelsGroup, FLAGS_EVENT));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindWebsite(Context context) throws DefinitionException {
+ final DataKind kind = super.addDataKindWebsite(context);
+
+ kind.typeOverallMax = 1;
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Website.URL, R.string.websiteLabelsGroup, FLAGS_WEBSITE));
+
+ return kind;
+ }
+
+ @Override
+ public boolean isGroupMembershipEditable() {
+ return true;
+ }
+
+ @Override
+ public boolean areContactsWritable() {
+ return true;
+ }
+}
diff --git a/java/com/android/contacts/common/model/account/ExternalAccountType.java b/java/com/android/contacts/common/model/account/ExternalAccountType.java
new file mode 100644
index 000000000..aca1f70d2
--- /dev/null
+++ b/java/com/android/contacts/common/model/account/ExternalAccountType.java
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2009 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.model.account;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+import com.android.contacts.common.R;
+import com.android.contacts.common.model.dataitem.DataKind;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/** A general contacts account type descriptor. */
+public class ExternalAccountType extends BaseAccountType {
+
+ private static final String TAG = "ExternalAccountType";
+
+ private static final String SYNC_META_DATA = "android.content.SyncAdapter";
+
+ /**
+ * The metadata name for so-called "contacts.xml".
+ *
+ * <p>On LMP and later, we also accept the "alternate" name. This is to allow sync adapters to
+ * have a contacts.xml without making it visible on older platforms. If you modify this also
+ * update the corresponding list in ContactsProvider/PhotoPriorityResolver
+ */
+ private static final String[] METADATA_CONTACTS_NAMES =
+ new String[] {
+ "android.provider.ALTERNATE_CONTACTS_STRUCTURE", "android.provider.CONTACTS_STRUCTURE"
+ };
+
+ private static final String TAG_CONTACTS_SOURCE_LEGACY = "ContactsSource";
+ private static final String TAG_CONTACTS_ACCOUNT_TYPE = "ContactsAccountType";
+ private static final String TAG_CONTACTS_DATA_KIND = "ContactsDataKind";
+ private static final String TAG_EDIT_SCHEMA = "EditSchema";
+
+ private static final String ATTR_EDIT_CONTACT_ACTIVITY = "editContactActivity";
+ private static final String ATTR_CREATE_CONTACT_ACTIVITY = "createContactActivity";
+ private static final String ATTR_INVITE_CONTACT_ACTIVITY = "inviteContactActivity";
+ private static final String ATTR_INVITE_CONTACT_ACTION_LABEL = "inviteContactActionLabel";
+ private static final String ATTR_VIEW_CONTACT_NOTIFY_SERVICE = "viewContactNotifyService";
+ private static final String ATTR_VIEW_GROUP_ACTIVITY = "viewGroupActivity";
+ private static final String ATTR_VIEW_GROUP_ACTION_LABEL = "viewGroupActionLabel";
+ private static final String ATTR_DATA_SET = "dataSet";
+ private static final String ATTR_EXTENSION_PACKAGE_NAMES = "extensionPackageNames";
+
+ // The following attributes should only be set in non-sync-adapter account types. They allow
+ // for the account type and resource IDs to be specified without an associated authenticator.
+ private static final String ATTR_ACCOUNT_TYPE = "accountType";
+ private static final String ATTR_ACCOUNT_LABEL = "accountTypeLabel";
+ private static final String ATTR_ACCOUNT_ICON = "accountTypeIcon";
+
+ private final boolean mIsExtension;
+
+ private String mEditContactActivityClassName;
+ private String mCreateContactActivityClassName;
+ private String mInviteContactActivity;
+ private String mInviteActionLabelAttribute;
+ private int mInviteActionLabelResId;
+ private String mViewContactNotifyService;
+ private String mViewGroupActivity;
+ private String mViewGroupLabelAttribute;
+ private int mViewGroupLabelResId;
+ private List<String> mExtensionPackageNames;
+ private String mAccountTypeLabelAttribute;
+ private String mAccountTypeIconAttribute;
+ private boolean mHasContactsMetadata;
+ private boolean mHasEditSchema;
+
+ public ExternalAccountType(Context context, String resPackageName, boolean isExtension) {
+ this(context, resPackageName, isExtension, null);
+ }
+
+ /**
+ * Constructor used for testing to initialize with any arbitrary XML.
+ *
+ * @param injectedMetadata If non-null, it'll be used to initialize the type. Only set by tests.
+ * If null, the metadata is loaded from the specified package.
+ */
+ ExternalAccountType(
+ Context context,
+ String packageName,
+ boolean isExtension,
+ XmlResourceParser injectedMetadata) {
+ this.mIsExtension = isExtension;
+ this.resourcePackageName = packageName;
+ this.syncAdapterPackageName = packageName;
+
+ final XmlResourceParser parser;
+ if (injectedMetadata == null) {
+ parser = loadContactsXml(context, packageName);
+ } else {
+ parser = injectedMetadata;
+ }
+ boolean needLineNumberInErrorLog = true;
+ try {
+ if (parser != null) {
+ inflate(context, parser);
+ }
+
+ // Done parsing; line number no longer needed in error log.
+ needLineNumberInErrorLog = false;
+ if (mHasEditSchema) {
+ checkKindExists(StructuredName.CONTENT_ITEM_TYPE);
+ checkKindExists(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
+ checkKindExists(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME);
+ checkKindExists(Photo.CONTENT_ITEM_TYPE);
+ } else {
+ // Bring in name and photo from fallback source, which are non-optional
+ addDataKindStructuredName(context);
+ addDataKindDisplayName(context);
+ addDataKindPhoneticName(context);
+ addDataKindPhoto(context);
+ }
+ } catch (DefinitionException e) {
+ final StringBuilder error = new StringBuilder();
+ error.append("Problem reading XML");
+ if (needLineNumberInErrorLog && (parser != null)) {
+ error.append(" in line ");
+ error.append(parser.getLineNumber());
+ }
+ error.append(" for external package ");
+ error.append(packageName);
+
+ Log.e(TAG, error.toString(), e);
+ return;
+ } finally {
+ if (parser != null) {
+ parser.close();
+ }
+ }
+
+ mExtensionPackageNames = new ArrayList<String>();
+ mInviteActionLabelResId =
+ resolveExternalResId(
+ context,
+ mInviteActionLabelAttribute,
+ syncAdapterPackageName,
+ ATTR_INVITE_CONTACT_ACTION_LABEL);
+ mViewGroupLabelResId =
+ resolveExternalResId(
+ context,
+ mViewGroupLabelAttribute,
+ syncAdapterPackageName,
+ ATTR_VIEW_GROUP_ACTION_LABEL);
+ titleRes =
+ resolveExternalResId(
+ context, mAccountTypeLabelAttribute, syncAdapterPackageName, ATTR_ACCOUNT_LABEL);
+ iconRes =
+ resolveExternalResId(
+ context, mAccountTypeIconAttribute, syncAdapterPackageName, ATTR_ACCOUNT_ICON);
+
+ // If we reach this point, the account type has been successfully initialized.
+ mIsInitialized = true;
+ }
+
+ /**
+ * Returns the CONTACTS_STRUCTURE metadata (aka "contacts.xml") in the given apk package.
+ *
+ * <p>This method looks through all services in the package that handle sync adapter intents for
+ * the first one that contains CONTACTS_STRUCTURE metadata. We have to look through all sync
+ * adapters in the package in case there are contacts and other sync adapters (eg, calendar) in
+ * the same package.
+ *
+ * <p>Returns {@code null} if the package has no CONTACTS_STRUCTURE metadata. In this case the
+ * account type *will* be initialized with minimal configuration.
+ */
+ public static XmlResourceParser loadContactsXml(Context context, String resPackageName) {
+ final PackageManager pm = context.getPackageManager();
+ final Intent intent = new Intent(SYNC_META_DATA).setPackage(resPackageName);
+ final List<ResolveInfo> intentServices =
+ pm.queryIntentServices(intent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+
+ if (intentServices != null) {
+ for (final ResolveInfo resolveInfo : intentServices) {
+ final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ if (serviceInfo == null) {
+ continue;
+ }
+ for (String metadataName : METADATA_CONTACTS_NAMES) {
+ final XmlResourceParser parser = serviceInfo.loadXmlMetaData(pm, metadataName);
+ if (parser != null) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(
+ TAG,
+ String.format(
+ "Metadata loaded from: %s, %s, %s",
+ serviceInfo.packageName, serviceInfo.name, metadataName));
+ }
+ return parser;
+ }
+ }
+ }
+ }
+
+ // Package was found, but that doesn't contain the CONTACTS_STRUCTURE metadata.
+ return null;
+ }
+
+ /** Returns {@code TRUE} if the package contains CONTACTS_STRUCTURE metadata. */
+ public static boolean hasContactsXml(Context context, String resPackageName) {
+ return loadContactsXml(context, resPackageName) != null;
+ }
+
+ /**
+ * Takes a string in the "@xxx/yyy" format and return the resource ID for the resource in the
+ * resource package.
+ *
+ * <p>If the argument is in the invalid format or isn't a resource name, it returns -1.
+ *
+ * @param context context
+ * @param resourceName Resource name in the "@xxx/yyy" format, e.g. "@string/invite_lavbel"
+ * @param packageName name of the package containing the resource.
+ * @param xmlAttributeName attribute name which the resource came from. Used for logging.
+ */
+ @VisibleForTesting
+ static int resolveExternalResId(
+ Context context, String resourceName, String packageName, String xmlAttributeName) {
+ if (TextUtils.isEmpty(resourceName)) {
+ return -1; // Empty text is okay.
+ }
+ if (resourceName.charAt(0) != '@') {
+ Log.e(TAG, xmlAttributeName + " must be a resource name beginnig with '@'");
+ return -1;
+ }
+ final String name = resourceName.substring(1);
+ final Resources res;
+ try {
+ res = context.getPackageManager().getResourcesForApplication(packageName);
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Unable to load package " + packageName);
+ return -1;
+ }
+ final int resId = res.getIdentifier(name, null, packageName);
+ if (resId == 0) {
+ Log.e(TAG, "Unable to load " + resourceName + " from package " + packageName);
+ return -1;
+ }
+ return resId;
+ }
+
+ private void checkKindExists(String mimeType) throws DefinitionException {
+ if (getKindForMimetype(mimeType) == null) {
+ throw new DefinitionException(mimeType + " must be supported");
+ }
+ }
+
+ @Override
+ public boolean isEmbedded() {
+ return false;
+ }
+
+ @Override
+ public boolean isExtension() {
+ return mIsExtension;
+ }
+
+ @Override
+ public boolean areContactsWritable() {
+ return mHasEditSchema;
+ }
+
+ /** Whether this account type has the android.provider.CONTACTS_STRUCTURE metadata xml. */
+ public boolean hasContactsMetadata() {
+ return mHasContactsMetadata;
+ }
+
+ @Override
+ public String getEditContactActivityClassName() {
+ return mEditContactActivityClassName;
+ }
+
+ @Override
+ public String getCreateContactActivityClassName() {
+ return mCreateContactActivityClassName;
+ }
+
+ @Override
+ public String getInviteContactActivityClassName() {
+ return mInviteContactActivity;
+ }
+
+ @Override
+ protected int getInviteContactActionResId() {
+ return mInviteActionLabelResId;
+ }
+
+ @Override
+ public String getViewContactNotifyServiceClassName() {
+ return mViewContactNotifyService;
+ }
+
+ @Override
+ public String getViewGroupActivity() {
+ return mViewGroupActivity;
+ }
+
+ @Override
+ protected int getViewGroupLabelResId() {
+ return mViewGroupLabelResId;
+ }
+
+ @Override
+ public List<String> getExtensionPackageNames() {
+ return mExtensionPackageNames;
+ }
+
+ /**
+ * Inflate this {@link AccountType} from the given parser. This may only load details matching the
+ * publicly-defined schema.
+ */
+ protected void inflate(Context context, XmlPullParser parser) throws DefinitionException {
+ final AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ try {
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ // Drain comments and whitespace
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ throw new IllegalStateException("No start tag found");
+ }
+
+ String rootTag = parser.getName();
+ if (!TAG_CONTACTS_ACCOUNT_TYPE.equals(rootTag)
+ && !TAG_CONTACTS_SOURCE_LEGACY.equals(rootTag)) {
+ throw new IllegalStateException(
+ "Top level element must be " + TAG_CONTACTS_ACCOUNT_TYPE + ", not " + rootTag);
+ }
+
+ mHasContactsMetadata = true;
+
+ int attributeCount = parser.getAttributeCount();
+ for (int i = 0; i < attributeCount; i++) {
+ String attr = parser.getAttributeName(i);
+ String value = parser.getAttributeValue(i);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, attr + "=" + value);
+ }
+ if (ATTR_EDIT_CONTACT_ACTIVITY.equals(attr)) {
+ mEditContactActivityClassName = value;
+ } else if (ATTR_CREATE_CONTACT_ACTIVITY.equals(attr)) {
+ mCreateContactActivityClassName = value;
+ } else if (ATTR_INVITE_CONTACT_ACTIVITY.equals(attr)) {
+ mInviteContactActivity = value;
+ } else if (ATTR_INVITE_CONTACT_ACTION_LABEL.equals(attr)) {
+ mInviteActionLabelAttribute = value;
+ } else if (ATTR_VIEW_CONTACT_NOTIFY_SERVICE.equals(attr)) {
+ mViewContactNotifyService = value;
+ } else if (ATTR_VIEW_GROUP_ACTIVITY.equals(attr)) {
+ mViewGroupActivity = value;
+ } else if (ATTR_VIEW_GROUP_ACTION_LABEL.equals(attr)) {
+ mViewGroupLabelAttribute = value;
+ } else if (ATTR_DATA_SET.equals(attr)) {
+ dataSet = value;
+ } else if (ATTR_EXTENSION_PACKAGE_NAMES.equals(attr)) {
+ mExtensionPackageNames.add(value);
+ } else if (ATTR_ACCOUNT_TYPE.equals(attr)) {
+ accountType = value;
+ } else if (ATTR_ACCOUNT_LABEL.equals(attr)) {
+ mAccountTypeLabelAttribute = value;
+ } else if (ATTR_ACCOUNT_ICON.equals(attr)) {
+ mAccountTypeIconAttribute = value;
+ } else {
+ Log.e(TAG, "Unsupported attribute " + attr);
+ }
+ }
+
+ // Parse all children kinds
+ final int startDepth = parser.getDepth();
+ while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > startDepth)
+ && type != XmlPullParser.END_DOCUMENT) {
+
+ if (type != XmlPullParser.START_TAG || parser.getDepth() != startDepth + 1) {
+ continue; // Not a direct child tag
+ }
+
+ String tag = parser.getName();
+ if (TAG_EDIT_SCHEMA.equals(tag)) {
+ mHasEditSchema = true;
+ parseEditSchema(context, parser, attrs);
+ } else if (TAG_CONTACTS_DATA_KIND.equals(tag)) {
+ final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ContactsDataKind);
+ final DataKind kind = new DataKind();
+
+ kind.mimeType = a.getString(R.styleable.ContactsDataKind_android_mimeType);
+ final String summaryColumn =
+ a.getString(R.styleable.ContactsDataKind_android_summaryColumn);
+ if (summaryColumn != null) {
+ // Inflate a specific column as summary when requested
+ kind.actionHeader = new SimpleInflater(summaryColumn);
+ }
+ final String detailColumn =
+ a.getString(R.styleable.ContactsDataKind_android_detailColumn);
+ if (detailColumn != null) {
+ // Inflate specific column as summary
+ kind.actionBody = new SimpleInflater(detailColumn);
+ }
+
+ a.recycle();
+
+ addKind(kind);
+ }
+ }
+ } catch (XmlPullParserException e) {
+ throw new DefinitionException("Problem reading XML", e);
+ } catch (IOException e) {
+ throw new DefinitionException("Problem reading XML", e);
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/model/account/FallbackAccountType.java b/java/com/android/contacts/common/model/account/FallbackAccountType.java
new file mode 100644
index 000000000..976a7b892
--- /dev/null
+++ b/java/com/android/contacts/common/model/account/FallbackAccountType.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2009 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.model.account;
+
+import android.content.Context;
+import android.util.Log;
+import com.android.contacts.common.R;
+import com.android.contacts.common.model.dataitem.DataKind;
+
+public class FallbackAccountType extends BaseAccountType {
+
+ private static final String TAG = "FallbackAccountType";
+
+ private FallbackAccountType(Context context, String resPackageName) {
+ this.accountType = null;
+ this.dataSet = null;
+ this.titleRes = R.string.account_phone;
+ this.iconRes = R.mipmap.ic_contacts_launcher;
+
+ // Note those are only set for unit tests.
+ this.resourcePackageName = resPackageName;
+ this.syncAdapterPackageName = resPackageName;
+
+ try {
+ addDataKindStructuredName(context);
+ addDataKindDisplayName(context);
+ addDataKindPhoneticName(context);
+ addDataKindNickname(context);
+ addDataKindPhone(context);
+ addDataKindEmail(context);
+ addDataKindStructuredPostal(context);
+ addDataKindIm(context);
+ addDataKindOrganization(context);
+ addDataKindPhoto(context);
+ addDataKindNote(context);
+ addDataKindWebsite(context);
+ addDataKindSipAddress(context);
+ addDataKindGroupMembership(context);
+
+ mIsInitialized = true;
+ } catch (DefinitionException e) {
+ Log.e(TAG, "Problem building account type", e);
+ }
+ }
+
+ public FallbackAccountType(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Used to compare with an {@link ExternalAccountType} built from a test contacts.xml. In order to
+ * build {@link DataKind}s with the same resource package name, {@code resPackageName} is
+ * injectable.
+ */
+ static AccountType createWithPackageNameForTest(Context context, String resPackageName) {
+ return new FallbackAccountType(context, resPackageName);
+ }
+
+ @Override
+ public boolean areContactsWritable() {
+ return true;
+ }
+}
diff --git a/java/com/android/contacts/common/model/account/GoogleAccountType.java b/java/com/android/contacts/common/model/account/GoogleAccountType.java
new file mode 100644
index 000000000..2f1fe0ed6
--- /dev/null
+++ b/java/com/android/contacts/common/model/account/GoogleAccountType.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2009 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.model.account;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Relation;
+import android.util.Log;
+import com.android.contacts.common.R;
+import com.android.contacts.common.model.dataitem.DataKind;
+import com.android.contacts.common.util.CommonDateUtils;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class GoogleAccountType extends BaseAccountType {
+
+ /**
+ * The package name that we should load contacts.xml from and rely on to handle G+ account
+ * actions. Even though this points to gms, in some cases gms will still hand off responsibility
+ * to the G+ app.
+ */
+ public static final String PLUS_EXTENSION_PACKAGE_NAME = "com.google.android.gms";
+
+ public static final String ACCOUNT_TYPE = "com.google";
+ private static final String TAG = "GoogleAccountType";
+ private static final List<String> mExtensionPackages =
+ new ArrayList<>(Collections.singletonList(PLUS_EXTENSION_PACKAGE_NAME));
+
+ public GoogleAccountType(Context context, String authenticatorPackageName) {
+ this.accountType = ACCOUNT_TYPE;
+ this.resourcePackageName = null;
+ this.syncAdapterPackageName = authenticatorPackageName;
+
+ try {
+ addDataKindStructuredName(context);
+ addDataKindDisplayName(context);
+ addDataKindPhoneticName(context);
+ addDataKindNickname(context);
+ addDataKindPhone(context);
+ addDataKindEmail(context);
+ addDataKindStructuredPostal(context);
+ addDataKindIm(context);
+ addDataKindOrganization(context);
+ addDataKindPhoto(context);
+ addDataKindNote(context);
+ addDataKindWebsite(context);
+ addDataKindSipAddress(context);
+ addDataKindGroupMembership(context);
+ addDataKindRelation(context);
+ addDataKindEvent(context);
+
+ mIsInitialized = true;
+ } catch (DefinitionException e) {
+ Log.e(TAG, "Problem building account type", e);
+ }
+ }
+
+ @Override
+ public List<String> getExtensionPackageNames() {
+ return mExtensionPackages;
+ }
+
+ @Override
+ protected DataKind addDataKindPhone(Context context) throws DefinitionException {
+ final DataKind kind = super.addDataKindPhone(context);
+
+ kind.typeColumn = Phone.TYPE;
+ kind.typeList = new ArrayList<>();
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_WORK));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MAIN));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_PAGER).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_OTHER));
+ kind.typeList.add(
+ buildPhoneType(Phone.TYPE_CUSTOM).setSecondary(true).setCustomColumn(Phone.LABEL));
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindEmail(Context context) throws DefinitionException {
+ final DataKind kind = super.addDataKindEmail(context);
+
+ kind.typeColumn = Email.TYPE;
+ kind.typeList = new ArrayList<>();
+ kind.typeList.add(buildEmailType(Email.TYPE_HOME));
+ kind.typeList.add(buildEmailType(Email.TYPE_WORK));
+ kind.typeList.add(buildEmailType(Email.TYPE_OTHER));
+ kind.typeList.add(
+ buildEmailType(Email.TYPE_CUSTOM).setSecondary(true).setCustomColumn(Email.LABEL));
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
+
+ return kind;
+ }
+
+ private DataKind addDataKindRelation(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(
+ Relation.CONTENT_ITEM_TYPE,
+ R.string.relationLabelsGroup,
+ Weight.RELATIONSHIP,
+ true));
+ kind.actionHeader = new RelationActionInflater();
+ kind.actionBody = new SimpleInflater(Relation.NAME);
+
+ kind.typeColumn = Relation.TYPE;
+ kind.typeList = new ArrayList<>();
+ kind.typeList.add(buildRelationType(Relation.TYPE_ASSISTANT));
+ kind.typeList.add(buildRelationType(Relation.TYPE_BROTHER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_CHILD));
+ kind.typeList.add(buildRelationType(Relation.TYPE_DOMESTIC_PARTNER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_FATHER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_FRIEND));
+ kind.typeList.add(buildRelationType(Relation.TYPE_MANAGER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_MOTHER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_PARENT));
+ kind.typeList.add(buildRelationType(Relation.TYPE_PARTNER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_REFERRED_BY));
+ kind.typeList.add(buildRelationType(Relation.TYPE_RELATIVE));
+ kind.typeList.add(buildRelationType(Relation.TYPE_SISTER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_SPOUSE));
+ kind.typeList.add(
+ buildRelationType(Relation.TYPE_CUSTOM).setSecondary(true).setCustomColumn(Relation.LABEL));
+
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Relation.TYPE, Relation.TYPE_SPOUSE);
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Relation.DATA, R.string.relationLabelsGroup, FLAGS_RELATION));
+
+ return kind;
+ }
+
+ private DataKind addDataKindEvent(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(
+ new DataKind(Event.CONTENT_ITEM_TYPE, R.string.eventLabelsGroup, Weight.EVENT, true));
+ kind.actionHeader = new EventActionInflater();
+ kind.actionBody = new SimpleInflater(Event.START_DATE);
+
+ kind.typeColumn = Event.TYPE;
+ kind.typeList = new ArrayList<>();
+ kind.dateFormatWithoutYear = CommonDateUtils.NO_YEAR_DATE_FORMAT;
+ kind.dateFormatWithYear = CommonDateUtils.FULL_DATE_FORMAT;
+ kind.typeList.add(buildEventType(Event.TYPE_BIRTHDAY, true).setSpecificMax(1));
+ kind.typeList.add(buildEventType(Event.TYPE_ANNIVERSARY, false));
+ kind.typeList.add(buildEventType(Event.TYPE_OTHER, false));
+ kind.typeList.add(
+ buildEventType(Event.TYPE_CUSTOM, false).setSecondary(true).setCustomColumn(Event.LABEL));
+
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Event.TYPE, Event.TYPE_BIRTHDAY);
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Event.DATA, R.string.eventLabelsGroup, FLAGS_EVENT));
+
+ return kind;
+ }
+
+ @Override
+ public boolean isGroupMembershipEditable() {
+ return true;
+ }
+
+ @Override
+ public boolean areContactsWritable() {
+ return true;
+ }
+
+ @Override
+ public String getViewContactNotifyServiceClassName() {
+ return "com.google.android.syncadapters.contacts." + "SyncHighResPhotoIntentService";
+ }
+
+ @Override
+ public String getViewContactNotifyServicePackageName() {
+ return "com.google.android.syncadapters.contacts";
+ }
+}
diff --git a/java/com/android/contacts/common/model/account/SamsungAccountType.java b/java/com/android/contacts/common/model/account/SamsungAccountType.java
new file mode 100644
index 000000000..45406bc2b
--- /dev/null
+++ b/java/com/android/contacts/common/model/account/SamsungAccountType.java
@@ -0,0 +1,235 @@
+/*
+ * 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.model.account;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Relation;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.util.Log;
+import com.android.contacts.common.R;
+import com.android.contacts.common.model.dataitem.DataKind;
+import com.android.contacts.common.util.CommonDateUtils;
+import java.util.ArrayList;
+import java.util.Locale;
+
+/**
+ * A writable account type that can be used to support samsung contacts. This may not perfectly
+ * match Samsung's latest intended account schema.
+ *
+ * <p>This is only used to partially support Samsung accounts. The DataKind labels & fields are
+ * setup to support the values used by Samsung. But, not everything in the Samsung account type is
+ * supported. The Samsung account type includes a "Message Type" mimetype that we have no intention
+ * of showing inside the Contact editor. Similarly, we don't handle the "Ringtone" mimetype here
+ * since managing ringtones is handled in a different flow.
+ */
+public class SamsungAccountType extends BaseAccountType {
+
+ private static final String TAG = "KnownExternalAccountType";
+ private static final String ACCOUNT_TYPE_SAMSUNG = "com.osp.app.signin";
+
+ public SamsungAccountType(Context context, String authenticatorPackageName, String type) {
+ this.accountType = type;
+ this.resourcePackageName = null;
+ this.syncAdapterPackageName = authenticatorPackageName;
+
+ try {
+ addDataKindStructuredName(context);
+ addDataKindDisplayName(context);
+ addDataKindPhoneticName(context);
+ addDataKindNickname(context);
+ addDataKindPhone(context);
+ addDataKindEmail(context);
+ addDataKindStructuredPostal(context);
+ addDataKindIm(context);
+ addDataKindOrganization(context);
+ addDataKindPhoto(context);
+ addDataKindNote(context);
+ addDataKindWebsite(context);
+ addDataKindGroupMembership(context);
+ addDataKindRelation(context);
+ addDataKindEvent(context);
+
+ mIsInitialized = true;
+ } catch (DefinitionException e) {
+ Log.e(TAG, "Problem building account type", e);
+ }
+ }
+
+ /**
+ * Returns {@code TRUE} if this is samsung's account type and Samsung hasn't bothered to define a
+ * contacts.xml to provide a more accurate definition than ours.
+ */
+ public static boolean isSamsungAccountType(Context context, String type, String packageName) {
+ return ACCOUNT_TYPE_SAMSUNG.equals(type)
+ && !ExternalAccountType.hasContactsXml(context, packageName);
+ }
+
+ @Override
+ protected DataKind addDataKindStructuredPostal(Context context) throws DefinitionException {
+ final DataKind kind = super.addDataKindStructuredPostal(context);
+
+ final boolean useJapaneseOrder =
+ Locale.JAPANESE.getLanguage().equals(Locale.getDefault().getLanguage());
+ kind.typeColumn = StructuredPostal.TYPE;
+ kind.typeList = new ArrayList<>();
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_WORK).setSpecificMax(1));
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_HOME).setSpecificMax(1));
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_OTHER).setSpecificMax(1));
+
+ kind.fieldList = new ArrayList<>();
+ if (useJapaneseOrder) {
+ kind.fieldList.add(
+ new EditField(StructuredPostal.COUNTRY, R.string.postal_country, FLAGS_POSTAL)
+ .setOptional(true));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.POSTCODE, R.string.postal_postcode, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.REGION, R.string.postal_region, FLAGS_POSTAL));
+ kind.fieldList.add(new EditField(StructuredPostal.CITY, R.string.postal_city, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.STREET, R.string.postal_street, FLAGS_POSTAL));
+ } else {
+ kind.fieldList.add(
+ new EditField(StructuredPostal.STREET, R.string.postal_street, FLAGS_POSTAL));
+ kind.fieldList.add(new EditField(StructuredPostal.CITY, R.string.postal_city, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.REGION, R.string.postal_region, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.POSTCODE, R.string.postal_postcode, FLAGS_POSTAL));
+ kind.fieldList.add(
+ new EditField(StructuredPostal.COUNTRY, R.string.postal_country, FLAGS_POSTAL)
+ .setOptional(true));
+ }
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindPhone(Context context) throws DefinitionException {
+ final DataKind kind = super.addDataKindPhone(context);
+
+ kind.typeColumn = Phone.TYPE;
+ kind.typeList = new ArrayList<>();
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_WORK));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MAIN));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_PAGER).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_RADIO).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_OTHER));
+ kind.typeList.add(
+ buildPhoneType(Phone.TYPE_CUSTOM).setSecondary(true).setCustomColumn(Phone.LABEL));
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindEmail(Context context) throws DefinitionException {
+ final DataKind kind = super.addDataKindEmail(context);
+
+ kind.typeColumn = Email.TYPE;
+ kind.typeList = new ArrayList<>();
+ kind.typeList.add(buildEmailType(Email.TYPE_HOME));
+ kind.typeList.add(buildEmailType(Email.TYPE_WORK));
+ kind.typeList.add(buildEmailType(Email.TYPE_OTHER));
+ kind.typeList.add(
+ buildEmailType(Email.TYPE_CUSTOM).setSecondary(true).setCustomColumn(Email.LABEL));
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
+
+ return kind;
+ }
+
+ private DataKind addDataKindRelation(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(new DataKind(Relation.CONTENT_ITEM_TYPE, R.string.relationLabelsGroup, 160, true));
+ kind.actionHeader = new RelationActionInflater();
+ kind.actionBody = new SimpleInflater(Relation.NAME);
+
+ kind.typeColumn = Relation.TYPE;
+ kind.typeList = new ArrayList<>();
+ kind.typeList.add(buildRelationType(Relation.TYPE_ASSISTANT));
+ kind.typeList.add(buildRelationType(Relation.TYPE_BROTHER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_CHILD));
+ kind.typeList.add(buildRelationType(Relation.TYPE_DOMESTIC_PARTNER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_FATHER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_FRIEND));
+ kind.typeList.add(buildRelationType(Relation.TYPE_MANAGER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_MOTHER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_PARENT));
+ kind.typeList.add(buildRelationType(Relation.TYPE_PARTNER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_REFERRED_BY));
+ kind.typeList.add(buildRelationType(Relation.TYPE_RELATIVE));
+ kind.typeList.add(buildRelationType(Relation.TYPE_SISTER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_SPOUSE));
+ kind.typeList.add(
+ buildRelationType(Relation.TYPE_CUSTOM).setSecondary(true).setCustomColumn(Relation.LABEL));
+
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Relation.TYPE, Relation.TYPE_SPOUSE);
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Relation.DATA, R.string.relationLabelsGroup, FLAGS_RELATION));
+
+ return kind;
+ }
+
+ private DataKind addDataKindEvent(Context context) throws DefinitionException {
+ DataKind kind =
+ addKind(new DataKind(Event.CONTENT_ITEM_TYPE, R.string.eventLabelsGroup, 150, true));
+ kind.actionHeader = new EventActionInflater();
+ kind.actionBody = new SimpleInflater(Event.START_DATE);
+
+ kind.typeColumn = Event.TYPE;
+ kind.typeList = new ArrayList<>();
+ kind.dateFormatWithoutYear = CommonDateUtils.NO_YEAR_DATE_FORMAT;
+ kind.dateFormatWithYear = CommonDateUtils.FULL_DATE_FORMAT;
+ kind.typeList.add(buildEventType(Event.TYPE_BIRTHDAY, true).setSpecificMax(1));
+ kind.typeList.add(buildEventType(Event.TYPE_ANNIVERSARY, false));
+ kind.typeList.add(buildEventType(Event.TYPE_OTHER, false));
+ kind.typeList.add(
+ buildEventType(Event.TYPE_CUSTOM, false).setSecondary(true).setCustomColumn(Event.LABEL));
+
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Event.TYPE, Event.TYPE_BIRTHDAY);
+
+ kind.fieldList = new ArrayList<>();
+ kind.fieldList.add(new EditField(Event.DATA, R.string.eventLabelsGroup, FLAGS_EVENT));
+
+ return kind;
+ }
+
+ @Override
+ public boolean isGroupMembershipEditable() {
+ return true;
+ }
+
+ @Override
+ public boolean areContactsWritable() {
+ return true;
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/DataItem.java b/java/com/android/contacts/common/model/dataitem/DataItem.java
new file mode 100644
index 000000000..dc746055b
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/DataItem.java
@@ -0,0 +1,258 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.CommonDataKinds.Identity;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.Relation;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.provider.ContactsContract.Contacts.Data;
+import android.provider.ContactsContract.Contacts.Entity;
+import com.android.contacts.common.Collapser;
+import com.android.contacts.common.MoreContactUtils;
+import com.android.contacts.common.model.account.AccountType.EditType;
+
+/** This is the base class for data items, which represents a row from the Data table. */
+public class DataItem implements Collapser.Collapsible<DataItem> {
+
+ private final ContentValues mContentValues;
+ protected DataKind mKind;
+
+ protected DataItem(ContentValues values) {
+ mContentValues = values;
+ }
+
+ /**
+ * Factory for creating subclasses of DataItem objects based on the mimetype in the content
+ * values. Raw contact is the raw contact that this data item is associated with.
+ */
+ public static DataItem createFrom(ContentValues values) {
+ final String mimeType = values.getAsString(Data.MIMETYPE);
+ if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return new GroupMembershipDataItem(values);
+ } else if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return new StructuredNameDataItem(values);
+ } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return new PhoneDataItem(values);
+ } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return new EmailDataItem(values);
+ } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return new StructuredPostalDataItem(values);
+ } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return new ImDataItem(values);
+ } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return new OrganizationDataItem(values);
+ } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return new NicknameDataItem(values);
+ } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return new NoteDataItem(values);
+ } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return new WebsiteDataItem(values);
+ } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return new SipAddressDataItem(values);
+ } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return new EventDataItem(values);
+ } else if (Relation.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return new RelationDataItem(values);
+ } else if (Identity.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return new IdentityDataItem(values);
+ } else if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return new PhotoDataItem(values);
+ }
+
+ // generic
+ return new DataItem(values);
+ }
+
+ public ContentValues getContentValues() {
+ return mContentValues;
+ }
+
+ public Long getRawContactId() {
+ return mContentValues.getAsLong(Data.RAW_CONTACT_ID);
+ }
+
+ public void setRawContactId(long rawContactId) {
+ mContentValues.put(Data.RAW_CONTACT_ID, rawContactId);
+ }
+
+ /** Returns the data id. */
+ public long getId() {
+ return mContentValues.getAsLong(Data._ID);
+ }
+
+ /** Returns the mimetype of the data. */
+ public String getMimeType() {
+ return mContentValues.getAsString(Data.MIMETYPE);
+ }
+
+ public void setMimeType(String mimeType) {
+ mContentValues.put(Data.MIMETYPE, mimeType);
+ }
+
+ public boolean isPrimary() {
+ Integer primary = mContentValues.getAsInteger(Data.IS_PRIMARY);
+ return primary != null && primary != 0;
+ }
+
+ public boolean isSuperPrimary() {
+ Integer superPrimary = mContentValues.getAsInteger(Data.IS_SUPER_PRIMARY);
+ return superPrimary != null && superPrimary != 0;
+ }
+
+ public boolean hasKindTypeColumn(DataKind kind) {
+ final String key = kind.typeColumn;
+ return key != null
+ && mContentValues.containsKey(key)
+ && mContentValues.getAsInteger(key) != null;
+ }
+
+ public int getKindTypeColumn(DataKind kind) {
+ final String key = kind.typeColumn;
+ return mContentValues.getAsInteger(key);
+ }
+
+ /**
+ * Indicates the carrier presence value for the current {@link DataItem}.
+ *
+ * @return {@link Data#CARRIER_PRESENCE_VT_CAPABLE} if the {@link DataItem} supports carrier video
+ * calling, {@code 0} otherwise.
+ */
+ public int getCarrierPresence() {
+ return mContentValues.getAsInteger(Data.CARRIER_PRESENCE);
+ }
+
+ /**
+ * This builds the data string depending on the type of data item by using the generic DataKind
+ * object underneath.
+ */
+ public String buildDataString(Context context, DataKind kind) {
+ if (kind.actionBody == null) {
+ return null;
+ }
+ CharSequence actionBody = kind.actionBody.inflateUsing(context, mContentValues);
+ return actionBody == null ? null : actionBody.toString();
+ }
+
+ /**
+ * This builds the data string(intended for display) depending on the type of data item. It
+ * returns the same value as {@link #buildDataString} by default, but certain data items can
+ * override it to provide their version of formatted data strings.
+ *
+ * @return Data string representing the data item, possibly formatted for display
+ */
+ public String buildDataStringForDisplay(Context context, DataKind kind) {
+ return buildDataString(context, kind);
+ }
+
+ public DataKind getDataKind() {
+ return mKind;
+ }
+
+ public void setDataKind(DataKind kind) {
+ mKind = kind;
+ }
+
+ public Integer getTimesUsed() {
+ return mContentValues.getAsInteger(Entity.TIMES_USED);
+ }
+
+ public Long getLastTimeUsed() {
+ return mContentValues.getAsLong(Entity.LAST_TIME_USED);
+ }
+
+ @Override
+ public void collapseWith(DataItem that) {
+ DataKind thisKind = getDataKind();
+ DataKind thatKind = that.getDataKind();
+ // If this does not have a type and that does, or if that's type is higher precedence,
+ // use that's type
+ if ((!hasKindTypeColumn(thisKind) && that.hasKindTypeColumn(thatKind))
+ || (that.hasKindTypeColumn(thatKind)
+ && getTypePrecedence(thisKind, getKindTypeColumn(thisKind))
+ > getTypePrecedence(thatKind, that.getKindTypeColumn(thatKind)))) {
+ mContentValues.put(thatKind.typeColumn, that.getKindTypeColumn(thatKind));
+ mKind = thatKind;
+ }
+
+ // Choose the max of the maxLines and maxLabelLines values.
+ mKind.maxLinesForDisplay = Math.max(thisKind.maxLinesForDisplay, thatKind.maxLinesForDisplay);
+
+ // If any of the collapsed entries are super primary make the whole thing super primary.
+ if (isSuperPrimary() || that.isSuperPrimary()) {
+ mContentValues.put(Data.IS_SUPER_PRIMARY, 1);
+ mContentValues.put(Data.IS_PRIMARY, 1);
+ }
+
+ // If any of the collapsed entries are primary make the whole thing primary.
+ if (isPrimary() || that.isPrimary()) {
+ mContentValues.put(Data.IS_PRIMARY, 1);
+ }
+
+ // Add up the times used
+ mContentValues.put(
+ Entity.TIMES_USED,
+ (getTimesUsed() == null ? 0 : getTimesUsed())
+ + (that.getTimesUsed() == null ? 0 : that.getTimesUsed()));
+
+ // Use the most recent time
+ mContentValues.put(
+ Entity.LAST_TIME_USED,
+ Math.max(
+ getLastTimeUsed() == null ? 0 : getLastTimeUsed(),
+ that.getLastTimeUsed() == null ? 0 : that.getLastTimeUsed()));
+ }
+
+ @Override
+ public boolean shouldCollapseWith(DataItem t, Context context) {
+ if (mKind == null || t.getDataKind() == null) {
+ return false;
+ }
+ return MoreContactUtils.shouldCollapse(
+ getMimeType(),
+ buildDataString(context, mKind),
+ t.getMimeType(),
+ t.buildDataString(context, t.getDataKind()));
+ }
+
+ /**
+ * Return the precedence for the the given {@link EditType#rawValue}, where lower numbers are
+ * higher precedence.
+ */
+ private static int getTypePrecedence(DataKind kind, int rawValue) {
+ for (int i = 0; i < kind.typeList.size(); i++) {
+ final EditType type = kind.typeList.get(i);
+ if (type.rawValue == rawValue) {
+ return i;
+ }
+ }
+ return Integer.MAX_VALUE;
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/DataKind.java b/java/com/android/contacts/common/model/dataitem/DataKind.java
new file mode 100644
index 000000000..3b470a2ae
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/DataKind.java
@@ -0,0 +1,132 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract.Data;
+import com.android.contacts.common.model.account.AccountType.EditField;
+import com.android.contacts.common.model.account.AccountType.EditType;
+import com.android.contacts.common.model.account.AccountType.StringInflater;
+import com.google.common.collect.Iterators;
+import java.text.SimpleDateFormat;
+import java.util.List;
+
+/**
+ * Description of a specific data type, usually marked by a unique {@link Data#MIMETYPE}. Includes
+ * details about how to view and edit {@link Data} rows of this kind, including the possible {@link
+ * EditType} labels and editable {@link EditField}.
+ */
+public final class DataKind {
+
+ public static final String PSEUDO_MIME_TYPE_DISPLAY_NAME = "#displayName";
+ public static final String PSEUDO_MIME_TYPE_PHONETIC_NAME = "#phoneticName";
+ public static final String PSEUDO_COLUMN_PHONETIC_NAME = "#phoneticName";
+
+ public String resourcePackageName;
+ public String mimeType;
+ public int titleRes;
+ public int iconAltRes;
+ public int iconAltDescriptionRes;
+ public int weight;
+ public boolean editable;
+
+ public StringInflater actionHeader;
+ public StringInflater actionAltHeader;
+ public StringInflater actionBody;
+
+ public String typeColumn;
+
+ /** Maximum number of values allowed in the list. -1 represents infinity. */
+ public int typeOverallMax;
+
+ public List<EditType> typeList;
+ public List<EditField> fieldList;
+
+ public ContentValues defaultValues;
+
+ /**
+ * If this is a date field, this specifies the format of the date when saving. The date includes
+ * year, month and day. If this is not a date field or the date field is not editable, this value
+ * should be ignored.
+ */
+ public SimpleDateFormat dateFormatWithoutYear;
+
+ /**
+ * If this is a date field, this specifies the format of the date when saving. The date includes
+ * month and day. If this is not a date field, the field is not editable or dates without year are
+ * not supported, this value should be ignored.
+ */
+ public SimpleDateFormat dateFormatWithYear;
+
+ /** The number of lines available for displaying this kind of data. Defaults to 1. */
+ public int maxLinesForDisplay;
+
+ public DataKind() {
+ maxLinesForDisplay = 1;
+ }
+
+ public DataKind(String mimeType, int titleRes, int weight, boolean editable) {
+ this.mimeType = mimeType;
+ this.titleRes = titleRes;
+ this.weight = weight;
+ this.editable = editable;
+ this.typeOverallMax = -1;
+ maxLinesForDisplay = 1;
+ }
+
+ public static String toString(SimpleDateFormat format) {
+ return format == null ? "(null)" : format.toPattern();
+ }
+
+ public static String toString(Iterable<?> list) {
+ if (list == null) {
+ return "(null)";
+ } else {
+ return Iterators.toString(list.iterator());
+ }
+ }
+
+ public String getKindString(Context context) {
+ return (titleRes == -1 || titleRes == 0) ? "" : context.getString(titleRes);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("DataKind:");
+ sb.append(" resPackageName=").append(resourcePackageName);
+ sb.append(" mimeType=").append(mimeType);
+ sb.append(" titleRes=").append(titleRes);
+ sb.append(" iconAltRes=").append(iconAltRes);
+ sb.append(" iconAltDescriptionRes=").append(iconAltDescriptionRes);
+ sb.append(" weight=").append(weight);
+ sb.append(" editable=").append(editable);
+ sb.append(" actionHeader=").append(actionHeader);
+ sb.append(" actionAltHeader=").append(actionAltHeader);
+ sb.append(" actionBody=").append(actionBody);
+ sb.append(" typeColumn=").append(typeColumn);
+ sb.append(" typeOverallMax=").append(typeOverallMax);
+ sb.append(" typeList=").append(toString(typeList));
+ sb.append(" fieldList=").append(toString(fieldList));
+ sb.append(" defaultValues=").append(defaultValues);
+ sb.append(" dateFormatWithoutYear=").append(toString(dateFormatWithoutYear));
+ sb.append(" dateFormatWithYear=").append(toString(dateFormatWithYear));
+
+ return sb.toString();
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/EmailDataItem.java b/java/com/android/contacts/common/model/dataitem/EmailDataItem.java
new file mode 100644
index 000000000..2fe297816
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/EmailDataItem.java
@@ -0,0 +1,47 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+
+/**
+ * Represents an email data item, wrapping the columns in {@link
+ * ContactsContract.CommonDataKinds.Email}.
+ */
+public class EmailDataItem extends DataItem {
+
+ /* package */ EmailDataItem(ContentValues values) {
+ super(values);
+ }
+
+ public String getAddress() {
+ return getContentValues().getAsString(Email.ADDRESS);
+ }
+
+ public String getDisplayName() {
+ return getContentValues().getAsString(Email.DISPLAY_NAME);
+ }
+
+ public String getData() {
+ return getContentValues().getAsString(Email.DATA);
+ }
+
+ public String getLabel() {
+ return getContentValues().getAsString(Email.LABEL);
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/EventDataItem.java b/java/com/android/contacts/common/model/dataitem/EventDataItem.java
new file mode 100644
index 000000000..15d9880b1
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/EventDataItem.java
@@ -0,0 +1,62 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.text.TextUtils;
+
+/**
+ * Represents an event data item, wrapping the columns in {@link
+ * ContactsContract.CommonDataKinds.Event}.
+ */
+public class EventDataItem extends DataItem {
+
+ /* package */ EventDataItem(ContentValues values) {
+ super(values);
+ }
+
+ public String getStartDate() {
+ return getContentValues().getAsString(Event.START_DATE);
+ }
+
+ public String getLabel() {
+ return getContentValues().getAsString(Event.LABEL);
+ }
+
+ @Override
+ public boolean shouldCollapseWith(DataItem t, Context context) {
+ if (!(t instanceof EventDataItem) || mKind == null || t.getDataKind() == null) {
+ return false;
+ }
+ final EventDataItem that = (EventDataItem) t;
+ // Events can be different (anniversary, birthday) but have the same start date
+ if (!TextUtils.equals(getStartDate(), that.getStartDate())) {
+ return false;
+ } else if (!hasKindTypeColumn(mKind) || !that.hasKindTypeColumn(that.getDataKind())) {
+ return hasKindTypeColumn(mKind) == that.hasKindTypeColumn(that.getDataKind());
+ } else if (getKindTypeColumn(mKind) != that.getKindTypeColumn(that.getDataKind())) {
+ return false;
+ } else if (getKindTypeColumn(mKind) == Event.TYPE_CUSTOM
+ && !TextUtils.equals(getLabel(), that.getLabel())) {
+ // Check if custom types are not the same
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/GroupMembershipDataItem.java b/java/com/android/contacts/common/model/dataitem/GroupMembershipDataItem.java
new file mode 100644
index 000000000..f921b3c9d
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/GroupMembershipDataItem.java
@@ -0,0 +1,40 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+
+/**
+ * Represents a group memebership data item, wrapping the columns in {@link
+ * ContactsContract.CommonDataKinds.GroupMembership}.
+ */
+public class GroupMembershipDataItem extends DataItem {
+
+ /* package */ GroupMembershipDataItem(ContentValues values) {
+ super(values);
+ }
+
+ public Long getGroupRowId() {
+ return getContentValues().getAsLong(GroupMembership.GROUP_ROW_ID);
+ }
+
+ public String getGroupSourceId() {
+ return getContentValues().getAsString(GroupMembership.GROUP_SOURCE_ID);
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/IdentityDataItem.java b/java/com/android/contacts/common/model/dataitem/IdentityDataItem.java
new file mode 100644
index 000000000..2badf92f7
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/IdentityDataItem.java
@@ -0,0 +1,39 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.provider.ContactsContract.CommonDataKinds.Identity;
+
+/**
+ * Represents an identity data item, wrapping the columns in {@link
+ * ContactsContract.CommonDataKinds.Identity}.
+ */
+public class IdentityDataItem extends DataItem {
+
+ /* package */ IdentityDataItem(ContentValues values) {
+ super(values);
+ }
+
+ public String getIdentity() {
+ return getContentValues().getAsString(Identity.IDENTITY);
+ }
+
+ public String getNamespace() {
+ return getContentValues().getAsString(Identity.NAMESPACE);
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/ImDataItem.java b/java/com/android/contacts/common/model/dataitem/ImDataItem.java
new file mode 100644
index 000000000..16b9fd094
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/ImDataItem.java
@@ -0,0 +1,109 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.text.TextUtils;
+
+/**
+ * Represents an IM data item, wrapping the columns in {@link ContactsContract.CommonDataKinds.Im}.
+ */
+public class ImDataItem extends DataItem {
+
+ private final boolean mCreatedFromEmail;
+
+ /* package */ ImDataItem(ContentValues values) {
+ super(values);
+ mCreatedFromEmail = false;
+ }
+
+ private ImDataItem(ContentValues values, boolean createdFromEmail) {
+ super(values);
+ mCreatedFromEmail = createdFromEmail;
+ }
+
+ public static ImDataItem createFromEmail(EmailDataItem item) {
+ final ImDataItem im = new ImDataItem(new ContentValues(item.getContentValues()), true);
+ im.setMimeType(Im.CONTENT_ITEM_TYPE);
+ return im;
+ }
+
+ public String getData() {
+ if (mCreatedFromEmail) {
+ return getContentValues().getAsString(Email.DATA);
+ } else {
+ return getContentValues().getAsString(Im.DATA);
+ }
+ }
+
+ public String getLabel() {
+ return getContentValues().getAsString(Im.LABEL);
+ }
+
+ /** Values are one of Im.PROTOCOL_ */
+ public Integer getProtocol() {
+ return getContentValues().getAsInteger(Im.PROTOCOL);
+ }
+
+ public boolean isProtocolValid() {
+ return getProtocol() != null;
+ }
+
+ public String getCustomProtocol() {
+ return getContentValues().getAsString(Im.CUSTOM_PROTOCOL);
+ }
+
+ public int getChatCapability() {
+ Integer result = getContentValues().getAsInteger(Im.CHAT_CAPABILITY);
+ return result == null ? 0 : result;
+ }
+
+ public boolean isCreatedFromEmail() {
+ return mCreatedFromEmail;
+ }
+
+ @Override
+ public boolean shouldCollapseWith(DataItem t, Context context) {
+ if (!(t instanceof ImDataItem) || mKind == null || t.getDataKind() == null) {
+ return false;
+ }
+ final ImDataItem that = (ImDataItem) t;
+ // IM can have the same data put different protocol. These should not collapse.
+ if (!getData().equals(that.getData())) {
+ return false;
+ } else if (!isProtocolValid() || !that.isProtocolValid()) {
+ // Deal with invalid protocol as if it was custom. If either has a non valid
+ // protocol, check to see if the other has a valid that is not custom
+ if (isProtocolValid()) {
+ return getProtocol() == Im.PROTOCOL_CUSTOM;
+ } else if (that.isProtocolValid()) {
+ return that.getProtocol() == Im.PROTOCOL_CUSTOM;
+ }
+ return true;
+ } else if (getProtocol() != that.getProtocol()) {
+ return false;
+ } else if (getProtocol() == Im.PROTOCOL_CUSTOM
+ && !TextUtils.equals(getCustomProtocol(), that.getCustomProtocol())) {
+ // Check if custom protocols are not the same
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/NicknameDataItem.java b/java/com/android/contacts/common/model/dataitem/NicknameDataItem.java
new file mode 100644
index 000000000..a448be786
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/NicknameDataItem.java
@@ -0,0 +1,39 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+
+/**
+ * Represents a nickname data item, wrapping the columns in {@link
+ * ContactsContract.CommonDataKinds.Nickname}.
+ */
+public class NicknameDataItem extends DataItem {
+
+ public NicknameDataItem(ContentValues values) {
+ super(values);
+ }
+
+ public String getName() {
+ return getContentValues().getAsString(Nickname.NAME);
+ }
+
+ public String getLabel() {
+ return getContentValues().getAsString(Nickname.LABEL);
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/NoteDataItem.java b/java/com/android/contacts/common/model/dataitem/NoteDataItem.java
new file mode 100644
index 000000000..b55ecc3e5
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/NoteDataItem.java
@@ -0,0 +1,35 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+
+/**
+ * Represents a note data item, wrapping the columns in {@link
+ * ContactsContract.CommonDataKinds.Note}.
+ */
+public class NoteDataItem extends DataItem {
+
+ /* package */ NoteDataItem(ContentValues values) {
+ super(values);
+ }
+
+ public String getNote() {
+ return getContentValues().getAsString(Note.NOTE);
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/OrganizationDataItem.java b/java/com/android/contacts/common/model/dataitem/OrganizationDataItem.java
new file mode 100644
index 000000000..b33124838
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/OrganizationDataItem.java
@@ -0,0 +1,64 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+
+/**
+ * Represents an organization data item, wrapping the columns in {@link
+ * ContactsContract.CommonDataKinds.Organization}.
+ */
+public class OrganizationDataItem extends DataItem {
+
+ /* package */ OrganizationDataItem(ContentValues values) {
+ super(values);
+ }
+
+ public String getCompany() {
+ return getContentValues().getAsString(Organization.COMPANY);
+ }
+
+ public String getLabel() {
+ return getContentValues().getAsString(Organization.LABEL);
+ }
+
+ public String getTitle() {
+ return getContentValues().getAsString(Organization.TITLE);
+ }
+
+ public String getDepartment() {
+ return getContentValues().getAsString(Organization.DEPARTMENT);
+ }
+
+ public String getJobDescription() {
+ return getContentValues().getAsString(Organization.JOB_DESCRIPTION);
+ }
+
+ public String getSymbol() {
+ return getContentValues().getAsString(Organization.SYMBOL);
+ }
+
+ public String getPhoneticName() {
+ return getContentValues().getAsString(Organization.PHONETIC_NAME);
+ }
+
+ public String getOfficeLocation() {
+ return getContentValues().getAsString(Organization.OFFICE_LOCATION);
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/PhoneDataItem.java b/java/com/android/contacts/common/model/dataitem/PhoneDataItem.java
new file mode 100644
index 000000000..e1f56456a
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/PhoneDataItem.java
@@ -0,0 +1,76 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+
+/**
+ * Represents a phone data item, wrapping the columns in {@link
+ * ContactsContract.CommonDataKinds.Phone}.
+ */
+public class PhoneDataItem extends DataItem {
+
+ public static final String KEY_FORMATTED_PHONE_NUMBER = "formattedPhoneNumber";
+
+ /* package */ PhoneDataItem(ContentValues values) {
+ super(values);
+ }
+
+ public String getNumber() {
+ return getContentValues().getAsString(Phone.NUMBER);
+ }
+
+ /** Returns the normalized phone number in E164 format. */
+ public String getNormalizedNumber() {
+ return getContentValues().getAsString(Phone.NORMALIZED_NUMBER);
+ }
+
+ public String getFormattedPhoneNumber() {
+ return getContentValues().getAsString(KEY_FORMATTED_PHONE_NUMBER);
+ }
+
+ public String getLabel() {
+ return getContentValues().getAsString(Phone.LABEL);
+ }
+
+ public void computeFormattedPhoneNumber(String defaultCountryIso) {
+ final String phoneNumber = getNumber();
+ if (phoneNumber != null) {
+ final String formattedPhoneNumber =
+ PhoneNumberUtilsCompat.formatNumber(
+ phoneNumber, getNormalizedNumber(), defaultCountryIso);
+ getContentValues().put(KEY_FORMATTED_PHONE_NUMBER, formattedPhoneNumber);
+ }
+ }
+
+ /**
+ * Returns the formatted phone number (if already computed using {@link
+ * #computeFormattedPhoneNumber}). Otherwise this method returns the unformatted phone number.
+ */
+ @Override
+ public String buildDataStringForDisplay(Context context, DataKind kind) {
+ final String formatted = getFormattedPhoneNumber();
+ if (formatted != null) {
+ return formatted;
+ } else {
+ return getNumber();
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/PhotoDataItem.java b/java/com/android/contacts/common/model/dataitem/PhotoDataItem.java
new file mode 100644
index 000000000..0bf7a318b
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/PhotoDataItem.java
@@ -0,0 +1,39 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts.Photo;
+
+/**
+ * Represents a photo data item, wrapping the columns in {@link ContactsContract.Contacts.Photo}.
+ */
+public class PhotoDataItem extends DataItem {
+
+ /* package */ PhotoDataItem(ContentValues values) {
+ super(values);
+ }
+
+ public Long getPhotoFileId() {
+ return getContentValues().getAsLong(Photo.PHOTO_FILE_ID);
+ }
+
+ public byte[] getPhoto() {
+ return getContentValues().getAsByteArray(Photo.PHOTO);
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/RelationDataItem.java b/java/com/android/contacts/common/model/dataitem/RelationDataItem.java
new file mode 100644
index 000000000..fdbcbb313
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/RelationDataItem.java
@@ -0,0 +1,62 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract.CommonDataKinds.Relation;
+import android.text.TextUtils;
+
+/**
+ * Represents a relation data item, wrapping the columns in {@link
+ * ContactsContract.CommonDataKinds.Relation}.
+ */
+public class RelationDataItem extends DataItem {
+
+ /* package */ RelationDataItem(ContentValues values) {
+ super(values);
+ }
+
+ public String getName() {
+ return getContentValues().getAsString(Relation.NAME);
+ }
+
+ public String getLabel() {
+ return getContentValues().getAsString(Relation.LABEL);
+ }
+
+ @Override
+ public boolean shouldCollapseWith(DataItem t, Context context) {
+ if (!(t instanceof RelationDataItem) || mKind == null || t.getDataKind() == null) {
+ return false;
+ }
+ final RelationDataItem that = (RelationDataItem) t;
+ // Relations can have different types (assistant, father) but have the same name
+ if (!TextUtils.equals(getName(), that.getName())) {
+ return false;
+ } else if (!hasKindTypeColumn(mKind) || !that.hasKindTypeColumn(that.getDataKind())) {
+ return hasKindTypeColumn(mKind) == that.hasKindTypeColumn(that.getDataKind());
+ } else if (getKindTypeColumn(mKind) != that.getKindTypeColumn(that.getDataKind())) {
+ return false;
+ } else if (getKindTypeColumn(mKind) == Relation.TYPE_CUSTOM
+ && !TextUtils.equals(getLabel(), that.getLabel())) {
+ // Check if custom types are not the same
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/SipAddressDataItem.java b/java/com/android/contacts/common/model/dataitem/SipAddressDataItem.java
new file mode 100644
index 000000000..0ca9eae6d
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/SipAddressDataItem.java
@@ -0,0 +1,40 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+
+/**
+ * Represents a sip address data item, wrapping the columns in {@link
+ * ContactsContract.CommonDataKinds.SipAddress}.
+ */
+public class SipAddressDataItem extends DataItem {
+
+ /* package */ SipAddressDataItem(ContentValues values) {
+ super(values);
+ }
+
+ public String getSipAddress() {
+ return getContentValues().getAsString(SipAddress.SIP_ADDRESS);
+ }
+
+ public String getLabel() {
+ return getContentValues().getAsString(SipAddress.LABEL);
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/StructuredNameDataItem.java b/java/com/android/contacts/common/model/dataitem/StructuredNameDataItem.java
new file mode 100644
index 000000000..22bf037f1
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/StructuredNameDataItem.java
@@ -0,0 +1,100 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.Contacts.Data;
+
+/**
+ * Represents a structured name data item, wrapping the columns in {@link
+ * ContactsContract.CommonDataKinds.StructuredName}.
+ */
+public class StructuredNameDataItem extends DataItem {
+
+ public StructuredNameDataItem() {
+ super(new ContentValues());
+ getContentValues().put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+ }
+
+ /* package */ StructuredNameDataItem(ContentValues values) {
+ super(values);
+ }
+
+ public String getDisplayName() {
+ return getContentValues().getAsString(StructuredName.DISPLAY_NAME);
+ }
+
+ public void setDisplayName(String name) {
+ getContentValues().put(StructuredName.DISPLAY_NAME, name);
+ }
+
+ public String getGivenName() {
+ return getContentValues().getAsString(StructuredName.GIVEN_NAME);
+ }
+
+ public String getFamilyName() {
+ return getContentValues().getAsString(StructuredName.FAMILY_NAME);
+ }
+
+ public String getPrefix() {
+ return getContentValues().getAsString(StructuredName.PREFIX);
+ }
+
+ public String getMiddleName() {
+ return getContentValues().getAsString(StructuredName.MIDDLE_NAME);
+ }
+
+ public String getSuffix() {
+ return getContentValues().getAsString(StructuredName.SUFFIX);
+ }
+
+ public String getPhoneticGivenName() {
+ return getContentValues().getAsString(StructuredName.PHONETIC_GIVEN_NAME);
+ }
+
+ public void setPhoneticGivenName(String name) {
+ getContentValues().put(StructuredName.PHONETIC_GIVEN_NAME, name);
+ }
+
+ public String getPhoneticMiddleName() {
+ return getContentValues().getAsString(StructuredName.PHONETIC_MIDDLE_NAME);
+ }
+
+ public void setPhoneticMiddleName(String name) {
+ getContentValues().put(StructuredName.PHONETIC_MIDDLE_NAME, name);
+ }
+
+ public String getPhoneticFamilyName() {
+ return getContentValues().getAsString(StructuredName.PHONETIC_FAMILY_NAME);
+ }
+
+ public void setPhoneticFamilyName(String name) {
+ getContentValues().put(StructuredName.PHONETIC_FAMILY_NAME, name);
+ }
+
+ public String getFullNameStyle() {
+ return getContentValues().getAsString(StructuredName.FULL_NAME_STYLE);
+ }
+
+ public boolean isSuperPrimary() {
+ final ContentValues contentValues = getContentValues();
+ return contentValues == null || !contentValues.containsKey(StructuredName.IS_SUPER_PRIMARY)
+ ? false
+ : contentValues.getAsBoolean(StructuredName.IS_SUPER_PRIMARY);
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/StructuredPostalDataItem.java b/java/com/android/contacts/common/model/dataitem/StructuredPostalDataItem.java
new file mode 100644
index 000000000..18aae282c
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/StructuredPostalDataItem.java
@@ -0,0 +1,68 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+
+/**
+ * Represents a structured postal data item, wrapping the columns in {@link
+ * ContactsContract.CommonDataKinds.StructuredPostal}.
+ */
+public class StructuredPostalDataItem extends DataItem {
+
+ /* package */ StructuredPostalDataItem(ContentValues values) {
+ super(values);
+ }
+
+ public String getFormattedAddress() {
+ return getContentValues().getAsString(StructuredPostal.FORMATTED_ADDRESS);
+ }
+
+ public String getLabel() {
+ return getContentValues().getAsString(StructuredPostal.LABEL);
+ }
+
+ public String getStreet() {
+ return getContentValues().getAsString(StructuredPostal.STREET);
+ }
+
+ public String getPOBox() {
+ return getContentValues().getAsString(StructuredPostal.POBOX);
+ }
+
+ public String getNeighborhood() {
+ return getContentValues().getAsString(StructuredPostal.NEIGHBORHOOD);
+ }
+
+ public String getCity() {
+ return getContentValues().getAsString(StructuredPostal.CITY);
+ }
+
+ public String getRegion() {
+ return getContentValues().getAsString(StructuredPostal.REGION);
+ }
+
+ public String getPostcode() {
+ return getContentValues().getAsString(StructuredPostal.POSTCODE);
+ }
+
+ public String getCountry() {
+ return getContentValues().getAsString(StructuredPostal.COUNTRY);
+ }
+}
diff --git a/java/com/android/contacts/common/model/dataitem/WebsiteDataItem.java b/java/com/android/contacts/common/model/dataitem/WebsiteDataItem.java
new file mode 100644
index 000000000..b8400ecd1
--- /dev/null
+++ b/java/com/android/contacts/common/model/dataitem/WebsiteDataItem.java
@@ -0,0 +1,39 @@
+/*
+ * 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.model.dataitem;
+
+import android.content.ContentValues;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+
+/**
+ * Represents a website data item, wrapping the columns in {@link
+ * ContactsContract.CommonDataKinds.Website}.
+ */
+public class WebsiteDataItem extends DataItem {
+
+ /* package */ WebsiteDataItem(ContentValues values) {
+ super(values);
+ }
+
+ public String getUrl() {
+ return getContentValues().getAsString(Website.URL);
+ }
+
+ public String getLabel() {
+ return getContentValues().getAsString(Website.LABEL);
+ }
+}
diff --git a/java/com/android/contacts/common/preference/ContactsPreferences.java b/java/com/android/contacts/common/preference/ContactsPreferences.java
new file mode 100644
index 000000000..7f0d99acd
--- /dev/null
+++ b/java/com/android/contacts/common/preference/ContactsPreferences.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2010 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.preference;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.Handler;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.text.TextUtils;
+import com.android.contacts.common.R;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+
+/** Manages user preferences for contacts. */
+public class ContactsPreferences implements OnSharedPreferenceChangeListener {
+
+ /** The value for the DISPLAY_ORDER key to show the given name first. */
+ public static final int DISPLAY_ORDER_PRIMARY = 1;
+
+ /** The value for the DISPLAY_ORDER key to show the family name first. */
+ public static final int DISPLAY_ORDER_ALTERNATIVE = 2;
+
+ public static final String DISPLAY_ORDER_KEY = "android.contacts.DISPLAY_ORDER";
+
+ /** The value for the SORT_ORDER key corresponding to sort by given name first. */
+ public static final int SORT_ORDER_PRIMARY = 1;
+
+ public static final String SORT_ORDER_KEY = "android.contacts.SORT_ORDER";
+
+ /** The value for the SORT_ORDER key corresponding to sort by family name first. */
+ public static final int SORT_ORDER_ALTERNATIVE = 2;
+
+ public static final String PREF_DISPLAY_ONLY_PHONES = "only_phones";
+
+ public static final boolean PREF_DISPLAY_ONLY_PHONES_DEFAULT = false;
+
+ /**
+ * Value to use when a preference is unassigned and needs to be read from the shared preferences
+ */
+ private static final int PREFERENCE_UNASSIGNED = -1;
+
+ private final Context mContext;
+ private final SharedPreferences mPreferences;
+ private int mSortOrder = PREFERENCE_UNASSIGNED;
+ private int mDisplayOrder = PREFERENCE_UNASSIGNED;
+ private String mDefaultAccount = null;
+ private ChangeListener mListener = null;
+ private Handler mHandler;
+ private String mDefaultAccountKey;
+ private String mDefaultAccountSavedKey;
+
+ public ContactsPreferences(Context context) {
+ mContext = context;
+ mHandler = new Handler();
+ mPreferences = mContext.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE);
+ mDefaultAccountKey =
+ mContext.getResources().getString(R.string.contact_editor_default_account_key);
+ mDefaultAccountSavedKey =
+ mContext.getResources().getString(R.string.contact_editor_anything_saved_key);
+ maybeMigrateSystemSettings();
+ }
+
+ public boolean isSortOrderUserChangeable() {
+ return mContext.getResources().getBoolean(R.bool.config_sort_order_user_changeable);
+ }
+
+ public int getDefaultSortOrder() {
+ if (mContext.getResources().getBoolean(R.bool.config_default_sort_order_primary)) {
+ return SORT_ORDER_PRIMARY;
+ } else {
+ return SORT_ORDER_ALTERNATIVE;
+ }
+ }
+
+ public int getSortOrder() {
+ if (!isSortOrderUserChangeable()) {
+ return getDefaultSortOrder();
+ }
+ if (mSortOrder == PREFERENCE_UNASSIGNED) {
+ mSortOrder = mPreferences.getInt(SORT_ORDER_KEY, getDefaultSortOrder());
+ }
+ return mSortOrder;
+ }
+
+ public void setSortOrder(int sortOrder) {
+ mSortOrder = sortOrder;
+ final Editor editor = mPreferences.edit();
+ editor.putInt(SORT_ORDER_KEY, sortOrder);
+ editor.commit();
+ }
+
+ public boolean isDisplayOrderUserChangeable() {
+ return mContext.getResources().getBoolean(R.bool.config_display_order_user_changeable);
+ }
+
+ public int getDefaultDisplayOrder() {
+ if (mContext.getResources().getBoolean(R.bool.config_default_display_order_primary)) {
+ return DISPLAY_ORDER_PRIMARY;
+ } else {
+ return DISPLAY_ORDER_ALTERNATIVE;
+ }
+ }
+
+ public int getDisplayOrder() {
+ if (!isDisplayOrderUserChangeable()) {
+ return getDefaultDisplayOrder();
+ }
+ if (mDisplayOrder == PREFERENCE_UNASSIGNED) {
+ mDisplayOrder = mPreferences.getInt(DISPLAY_ORDER_KEY, getDefaultDisplayOrder());
+ }
+ return mDisplayOrder;
+ }
+
+ public void setDisplayOrder(int displayOrder) {
+ mDisplayOrder = displayOrder;
+ final Editor editor = mPreferences.edit();
+ editor.putInt(DISPLAY_ORDER_KEY, displayOrder);
+ editor.commit();
+ }
+
+ public boolean isDefaultAccountUserChangeable() {
+ return mContext.getResources().getBoolean(R.bool.config_default_account_user_changeable);
+ }
+
+ public String getDefaultAccount() {
+ if (!isDefaultAccountUserChangeable()) {
+ return mDefaultAccount;
+ }
+ if (TextUtils.isEmpty(mDefaultAccount)) {
+ final String accountString = mPreferences.getString(mDefaultAccountKey, mDefaultAccount);
+ if (!TextUtils.isEmpty(accountString)) {
+ final AccountWithDataSet accountWithDataSet = AccountWithDataSet.unstringify(accountString);
+ mDefaultAccount = accountWithDataSet.name;
+ }
+ }
+ return mDefaultAccount;
+ }
+
+ public void setDefaultAccount(AccountWithDataSet accountWithDataSet) {
+ mDefaultAccount = accountWithDataSet == null ? null : accountWithDataSet.name;
+ final Editor editor = mPreferences.edit();
+ if (TextUtils.isEmpty(mDefaultAccount)) {
+ editor.remove(mDefaultAccountKey);
+ } else {
+ editor.putString(mDefaultAccountKey, accountWithDataSet.stringify());
+ }
+ editor.putBoolean(mDefaultAccountSavedKey, true);
+ editor.commit();
+ }
+
+ public void registerChangeListener(ChangeListener listener) {
+ if (mListener != null) {
+ unregisterChangeListener();
+ }
+
+ mListener = listener;
+
+ // Reset preferences to "unknown" because they may have changed while the
+ // listener was unregistered.
+ mDisplayOrder = PREFERENCE_UNASSIGNED;
+ mSortOrder = PREFERENCE_UNASSIGNED;
+ mDefaultAccount = null;
+
+ mPreferences.registerOnSharedPreferenceChangeListener(this);
+ }
+
+ public void unregisterChangeListener() {
+ if (mListener != null) {
+ mListener = null;
+ }
+
+ mPreferences.unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, final String key) {
+ // This notification is not sent on the Ui thread. Use the previously created Handler
+ // to switch to the Ui thread
+ mHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ refreshValue(key);
+ }
+ });
+ }
+
+ /**
+ * Forces the value for the given key to be looked up from shared preferences and notifies the
+ * registered {@link ChangeListener}
+ *
+ * @param key the {@link SharedPreferences} key to look up
+ */
+ public void refreshValue(String key) {
+ if (DISPLAY_ORDER_KEY.equals(key)) {
+ mDisplayOrder = PREFERENCE_UNASSIGNED;
+ mDisplayOrder = getDisplayOrder();
+ } else if (SORT_ORDER_KEY.equals(key)) {
+ mSortOrder = PREFERENCE_UNASSIGNED;
+ mSortOrder = getSortOrder();
+ } else if (mDefaultAccountKey.equals(key)) {
+ mDefaultAccount = null;
+ mDefaultAccount = getDefaultAccount();
+ }
+ if (mListener != null) {
+ mListener.onChange();
+ }
+ }
+
+ /**
+ * If there are currently no preferences (which means this is the first time we are run), For sort
+ * order and display order, check to see if there are any preferences stored in system settings
+ * (pre-L) which can be copied into our own SharedPreferences. For default account setting, check
+ * to see if there are any preferences stored in the previous SharedPreferences which can be
+ * copied into current SharedPreferences.
+ */
+ private void maybeMigrateSystemSettings() {
+ if (!mPreferences.contains(SORT_ORDER_KEY)) {
+ int sortOrder = getDefaultSortOrder();
+ try {
+ sortOrder = Settings.System.getInt(mContext.getContentResolver(), SORT_ORDER_KEY);
+ } catch (SettingNotFoundException e) {
+ }
+ setSortOrder(sortOrder);
+ }
+
+ if (!mPreferences.contains(DISPLAY_ORDER_KEY)) {
+ int displayOrder = getDefaultDisplayOrder();
+ try {
+ displayOrder = Settings.System.getInt(mContext.getContentResolver(), DISPLAY_ORDER_KEY);
+ } catch (SettingNotFoundException e) {
+ }
+ setDisplayOrder(displayOrder);
+ }
+
+ if (!mPreferences.contains(mDefaultAccountKey)) {
+ final SharedPreferences previousPrefs =
+ PreferenceManager.getDefaultSharedPreferences(mContext);
+ final String defaultAccount = previousPrefs.getString(mDefaultAccountKey, null);
+ if (!TextUtils.isEmpty(defaultAccount)) {
+ final AccountWithDataSet accountWithDataSet =
+ AccountWithDataSet.unstringify(defaultAccount);
+ setDefaultAccount(accountWithDataSet);
+ }
+ }
+ }
+
+ public interface ChangeListener {
+
+ void onChange();
+ }
+}
diff --git a/java/com/android/contacts/common/preference/DisplayOrderPreference.java b/java/com/android/contacts/common/preference/DisplayOrderPreference.java
new file mode 100644
index 000000000..8dda57f9f
--- /dev/null
+++ b/java/com/android/contacts/common/preference/DisplayOrderPreference.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 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.preference;
+
+import android.app.AlertDialog.Builder;
+import android.content.Context;
+import android.preference.ListPreference;
+import android.util.AttributeSet;
+import com.android.contacts.common.R;
+
+/** Custom preference: view-name-as (first name first or last name first). */
+public final class DisplayOrderPreference extends ListPreference {
+
+ private ContactsPreferences mPreferences;
+ private Context mContext;
+
+ public DisplayOrderPreference(Context context) {
+ super(context);
+ prepare();
+ }
+
+ public DisplayOrderPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ prepare();
+ }
+
+ private void prepare() {
+ mContext = getContext();
+ mPreferences = new ContactsPreferences(mContext);
+ setEntries(
+ new String[] {
+ mContext.getString(R.string.display_options_view_given_name_first),
+ mContext.getString(R.string.display_options_view_family_name_first),
+ });
+ setEntryValues(
+ new String[] {
+ String.valueOf(ContactsPreferences.DISPLAY_ORDER_PRIMARY),
+ String.valueOf(ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE),
+ });
+ setValue(String.valueOf(mPreferences.getDisplayOrder()));
+ }
+
+ @Override
+ protected boolean shouldPersist() {
+ return false; // This preference takes care of its own storage
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ switch (mPreferences.getDisplayOrder()) {
+ case ContactsPreferences.DISPLAY_ORDER_PRIMARY:
+ return mContext.getString(R.string.display_options_view_given_name_first);
+ case ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE:
+ return mContext.getString(R.string.display_options_view_family_name_first);
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean persistString(String value) {
+ int newValue = Integer.parseInt(value);
+ if (newValue != mPreferences.getDisplayOrder()) {
+ mPreferences.setDisplayOrder(newValue);
+ notifyChanged();
+ }
+ return true;
+ }
+
+ @Override
+ // UX recommendation is not to show cancel button on such lists.
+ protected void onPrepareDialogBuilder(Builder builder) {
+ super.onPrepareDialogBuilder(builder);
+ builder.setNegativeButton(null, null);
+ }
+}
diff --git a/java/com/android/contacts/common/preference/SortOrderPreference.java b/java/com/android/contacts/common/preference/SortOrderPreference.java
new file mode 100644
index 000000000..9b6f57860
--- /dev/null
+++ b/java/com/android/contacts/common/preference/SortOrderPreference.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 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.preference;
+
+import android.app.AlertDialog.Builder;
+import android.content.Context;
+import android.preference.ListPreference;
+import android.util.AttributeSet;
+import com.android.contacts.common.R;
+
+/** Custom preference: sort-by. */
+public final class SortOrderPreference extends ListPreference {
+
+ private ContactsPreferences mPreferences;
+ private Context mContext;
+
+ public SortOrderPreference(Context context) {
+ super(context);
+ prepare();
+ }
+
+ public SortOrderPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ prepare();
+ }
+
+ private void prepare() {
+ mContext = getContext();
+ mPreferences = new ContactsPreferences(mContext);
+ setEntries(
+ new String[] {
+ mContext.getString(R.string.display_options_sort_by_given_name),
+ mContext.getString(R.string.display_options_sort_by_family_name),
+ });
+ setEntryValues(
+ new String[] {
+ String.valueOf(ContactsPreferences.SORT_ORDER_PRIMARY),
+ String.valueOf(ContactsPreferences.SORT_ORDER_ALTERNATIVE),
+ });
+ setValue(String.valueOf(mPreferences.getSortOrder()));
+ }
+
+ @Override
+ protected boolean shouldPersist() {
+ return false; // This preference takes care of its own storage
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ switch (mPreferences.getSortOrder()) {
+ case ContactsPreferences.SORT_ORDER_PRIMARY:
+ return mContext.getString(R.string.display_options_sort_by_given_name);
+ case ContactsPreferences.SORT_ORDER_ALTERNATIVE:
+ return mContext.getString(R.string.display_options_sort_by_family_name);
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean persistString(String value) {
+ int newValue = Integer.parseInt(value);
+ if (newValue != mPreferences.getSortOrder()) {
+ mPreferences.setSortOrder(newValue);
+ notifyChanged();
+ }
+ return true;
+ }
+
+ @Override
+ // UX recommendation is not to show cancel button on such lists.
+ protected void onPrepareDialogBuilder(Builder builder) {
+ super.onPrepareDialogBuilder(builder);
+ builder.setNegativeButton(null, null);
+ }
+}
diff --git a/java/com/android/contacts/common/res/color/popup_menu_color.xml b/java/com/android/contacts/common/res/color/popup_menu_color.xml
new file mode 100644
index 000000000..c52bd5b50
--- /dev/null
+++ b/java/com/android/contacts/common/res/color/popup_menu_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:alpha="0.5" android:color="#ff000000" android:state_enabled="false"/>
+ <item android:color="#ff000000"/>
+</selector> \ No newline at end of file
diff --git a/res/color/tab_text_color.xml b/java/com/android/contacts/common/res/color/tab_text_color.xml
index 5ef1fe33b..71ef3e903 100644
--- a/res/color/tab_text_color.xml
+++ b/java/com/android/contacts/common/res/color/tab_text_color.xml
@@ -16,6 +16,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@color/actionbar_text_color" android:state_selected="true"/>
- <item android:color="@color/actionbar_unselected_text_color" />
+ <item android:color="@color/actionbar_text_color" android:state_selected="true"/>
+ <item android:color="@color/actionbar_unselected_text_color"/>
</selector> \ No newline at end of file
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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_ab_search.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_arrow_back_24dp.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_business_white_120dp.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_call_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_call_note_white_24dp.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_call_note_white_24dp.png
new file mode 100644
index 000000000..503e58e22
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_call_note_white_24dp.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_close_dk.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_create_24dp.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_group_white_24dp.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_history_white_drawable_24dp.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_info_outline_24dp.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_back.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_group_dk.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_group_lt.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_overflow_lt.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_person_dk.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_person_lt.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_remove_field_holo_light.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_dk.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_holo_light.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_lt.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_message_24dp.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_message_24dp.png
new file mode 100644
index 000000000..57177b7c6
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_message_24dp.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_person_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_person_add_24dp.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_person_add_24dp.png
new file mode 100644
index 000000000..10ae5a70c
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_person_add_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_phone_attach.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_phone_attach.png
new file mode 100644
index 000000000..84b1227bd
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_phone_attach.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_rx_videocam.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_scroll_handle.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_scroll_handle.png
new file mode 100644
index 000000000..3aa29b852
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_scroll_handle.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_tx_videocam.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_videocam.png
Binary files 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..c74bfab13
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/ic_voicemail_avatar.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/list_activated_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/list_background_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/list_focused_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/list_longpressed_holo_light.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/list_pressed_holo_light.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/list_section_divider_holo_custom.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-hdpi/list_title_holo.9.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_background_holo.9.png b/java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_background_holo.9.png
new file mode 100644
index 000000000..0d80482a9
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_background_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_focused_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_section_divider_holo_custom.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_title_holo.9.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_background_holo.9.png b/java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_background_holo.9.png
new file mode 100644
index 000000000..d86d61164
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_background_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_focused_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_section_divider_holo_custom.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_title_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-hdpi/list_activated_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-mdpi/list_activated_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-xhdpi/list_activated_holo.9.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_background_holo.9.png b/java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_background_holo.9.png
new file mode 100644
index 000000000..f709f2ce4
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_background_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_focused_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_section_divider_holo_custom.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_title_holo.9.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_ab_search.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_ab_search.png
new file mode 100644
index 000000000..2b23b1ec5
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_ab_search.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_arrow_back_24dp.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_arrow_back_24dp.png
new file mode 100644
index 000000000..1a21fb400
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_arrow_back_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_business_white_120dp.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_business_white_120dp.png
new file mode 100644
index 000000000..3dddca516
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_business_white_120dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_call_24dp.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_call_24dp.png
new file mode 100644
index 000000000..77f9de5e3
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_call_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_call_note_white_24dp.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_call_note_white_24dp.png
new file mode 100644
index 000000000..9d359db9f
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_call_note_white_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_close_dk.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_close_dk.png
new file mode 100644
index 000000000..590a728ad
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_close_dk.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_create_24dp.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_create_24dp.png
new file mode 100644
index 000000000..8a2df3992
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_create_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_group_white_24dp.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_group_white_24dp.png
new file mode 100644
index 000000000..ad268bf2f
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_group_white_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_history_white_drawable_24dp.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_history_white_drawable_24dp.png
new file mode 100644
index 000000000..b3000d31e
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_history_white_drawable_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_info_outline_24dp.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_info_outline_24dp.png
new file mode 100644
index 000000000..353e06495
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_info_outline_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_back.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_back.png
new file mode 100644
index 000000000..201ad40cb
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_back.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_group_dk.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_group_dk.png
new file mode 100644
index 000000000..9aac6d79b
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_group_dk.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_group_lt.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_group_lt.png
new file mode 100644
index 000000000..39c16ed2d
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_group_lt.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_overflow_lt.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_overflow_lt.png
new file mode 100644
index 000000000..841509682
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_overflow_lt.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_person_dk.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_person_dk.png
new file mode 100644
index 000000000..b8fc39aee
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_person_dk.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_person_lt.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_person_lt.png
new file mode 100644
index 000000000..736dfd6fa
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_person_lt.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_remove_field_holo_light.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_remove_field_holo_light.png
new file mode 100644
index 000000000..8c44e7015
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_remove_field_holo_light.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_star_dk.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_star_dk.png
new file mode 100644
index 000000000..c16c6c5de
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_star_dk.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_star_lt.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_star_lt.png
new file mode 100644
index 000000000..c67170e31
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_menu_star_lt.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_message_24dp.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_message_24dp.png
new file mode 100644
index 000000000..3072b7569
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_message_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_person_24dp.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_person_24dp.png
new file mode 100644
index 000000000..f0b1c725d
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_person_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_person_add_24dp.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_person_add_24dp.png
new file mode 100644
index 000000000..38e0a2882
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_person_add_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_phone_attach.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_phone_attach.png
new file mode 100644
index 000000000..fc4ddd32c
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_phone_attach.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_rx_videocam.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_rx_videocam.png
new file mode 100644
index 000000000..1b43a07d0
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_rx_videocam.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_scroll_handle.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_scroll_handle.png
new file mode 100644
index 000000000..af75db4b4
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_scroll_handle.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_tx_videocam.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_tx_videocam.png
new file mode 100644
index 000000000..64995d147
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_tx_videocam.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_videocam.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_videocam.png
new file mode 100644
index 000000000..dc9655b6d
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_videocam.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/ic_voicemail_avatar.png b/java/com/android/contacts/common/res/drawable-mdpi/ic_voicemail_avatar.png
new file mode 100644
index 000000000..a5a30213d
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/ic_voicemail_avatar.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/list_activated_holo.9.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/list_background_holo.9.png b/java/com/android/contacts/common/res/drawable-mdpi/list_background_holo.9.png
new file mode 100644
index 000000000..7d5d66de3
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/list_background_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/list_focused_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/list_longpressed_holo_light.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/list_pressed_holo_light.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/list_section_divider_holo_custom.9.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-mdpi/list_title_holo.9.png b/java/com/android/contacts/common/res/drawable-mdpi/list_title_holo.9.png
new file mode 100644
index 000000000..64bd6912c
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-mdpi/list_title_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-sw600dp-hdpi/list_activated_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-sw600dp-mdpi/list_activated_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-sw600dp-xhdpi/list_activated_holo.9.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_ab_search.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_ab_search.png
new file mode 100644
index 000000000..71f782701
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_ab_search.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_arrow_back_24dp.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_arrow_back_24dp.png
new file mode 100644
index 000000000..bb7327251
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_arrow_back_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_business_white_120dp.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_business_white_120dp.png
new file mode 100644
index 000000000..6256300b4
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_business_white_120dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_call_24dp.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_call_24dp.png
new file mode 100644
index 000000000..ef45e933a
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_call_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_call_note_white_24dp.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_call_note_white_24dp.png
new file mode 100644
index 000000000..40eed1d12
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_call_note_white_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_close_dk.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_close_dk.png
new file mode 100644
index 000000000..5769f1178
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_close_dk.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_create_24dp.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_create_24dp.png
new file mode 100644
index 000000000..48e75beee
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_create_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_group_white_24dp.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_group_white_24dp.png
new file mode 100644
index 000000000..09c0e3efd
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_group_white_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_history_white_drawable_24dp.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_history_white_drawable_24dp.png
new file mode 100644
index 000000000..e188d4a37
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_history_white_drawable_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_info_outline_24dp.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_info_outline_24dp.png
new file mode 100644
index 000000000..c571b2e3e
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_info_outline_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_back.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_back.png
new file mode 100644
index 000000000..d2f709942
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_back.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_group_dk.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_group_dk.png
new file mode 100644
index 000000000..ce5f704ec
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_group_dk.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_group_lt.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_group_lt.png
new file mode 100644
index 000000000..3d0580f93
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_group_lt.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_overflow_lt.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_overflow_lt.png
new file mode 100644
index 000000000..f91b71847
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_overflow_lt.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_person_dk.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_person_dk.png
new file mode 100644
index 000000000..2fbd458e9
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_person_dk.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_person_lt.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_person_lt.png
new file mode 100644
index 000000000..2cdb2d7a1
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_person_lt.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_remove_field_holo_light.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_remove_field_holo_light.png
new file mode 100644
index 000000000..65a6b7bbb
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_remove_field_holo_light.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_star_dk.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_star_dk.png
new file mode 100644
index 000000000..48483a0b6
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_star_dk.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_star_holo_light.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_star_holo_light.png
new file mode 100644
index 000000000..906791177
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_star_holo_light.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_star_lt.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_star_lt.png
new file mode 100644
index 000000000..e053c757a
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_menu_star_lt.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_message_24dp.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_message_24dp.png
new file mode 100644
index 000000000..763767b4f
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_message_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_person_24dp.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_person_24dp.png
new file mode 100644
index 000000000..aea15f0be
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_person_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_person_add_24dp.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_person_add_24dp.png
new file mode 100644
index 000000000..7e7c289d4
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_person_add_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_phone_attach.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_phone_attach.png
new file mode 100644
index 000000000..fdfafed9a
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_phone_attach.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_rx_videocam.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_rx_videocam.png
new file mode 100644
index 000000000..43319dc92
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_rx_videocam.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_scroll_handle.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_scroll_handle.png
new file mode 100644
index 000000000..2d43c4d5b
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_scroll_handle.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_tx_videocam.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_tx_videocam.png
new file mode 100644
index 000000000..d2671edf7
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_tx_videocam.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_videocam.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_videocam.png
new file mode 100644
index 000000000..c1783de67
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_videocam.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/ic_voicemail_avatar.png b/java/com/android/contacts/common/res/drawable-xhdpi/ic_voicemail_avatar.png
new file mode 100644
index 000000000..ca9d7d66b
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/ic_voicemail_avatar.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/list_activated_holo.9.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/list_background_holo.9.png b/java/com/android/contacts/common/res/drawable-xhdpi/list_background_holo.9.png
new file mode 100644
index 000000000..b65272542
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/list_background_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/list_focused_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/list_longpressed_holo_light.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/list_pressed_holo_light.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/list_section_divider_holo_custom.9.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/list_title_holo.9.png b/java/com/android/contacts/common/res/drawable-xhdpi/list_title_holo.9.png
new file mode 100644
index 000000000..f4f00ca0f
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xhdpi/list_title_holo.9.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_ab_search.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_ab_search.png
new file mode 100644
index 000000000..142c5457d
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_ab_search.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_arrow_back_24dp.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_arrow_back_24dp.png
new file mode 100644
index 000000000..72c51b0d5
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_arrow_back_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_business_white_120dp.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_business_white_120dp.png
new file mode 100644
index 000000000..8d67e448f
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_business_white_120dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_call_24dp.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_call_24dp.png
new file mode 100644
index 000000000..90ead2e45
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_call_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_call_note_white_24dp.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_call_note_white_24dp.png
new file mode 100644
index 000000000..2656cad18
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_call_note_white_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_close_dk.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_close_dk.png
new file mode 100644
index 000000000..670bf796c
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_close_dk.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_create_24dp.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_create_24dp.png
new file mode 100644
index 000000000..24142c729
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_create_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_group_white_24dp.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_group_white_24dp.png
new file mode 100644
index 000000000..03cad4c90
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_group_white_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_history_white_drawable_24dp.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_history_white_drawable_24dp.png
new file mode 100644
index 000000000..f44df1afd
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_history_white_drawable_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_info_outline_24dp.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_info_outline_24dp.png
new file mode 100644
index 000000000..c41a5fcff
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_info_outline_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_back.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_back.png
new file mode 100644
index 000000000..436a82da6
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_back.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_group_dk.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_group_dk.png
new file mode 100644
index 000000000..a70c60c03
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_group_dk.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_group_lt.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_group_lt.png
new file mode 100644
index 000000000..c64b9defe
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_group_lt.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_overflow_lt.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_overflow_lt.png
new file mode 100644
index 000000000..ff1759b8f
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_overflow_lt.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_person_dk.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_person_dk.png
new file mode 100644
index 000000000..878e694ad
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_person_dk.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_person_lt.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_person_lt.png
new file mode 100644
index 000000000..ed4138f15
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_person_lt.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_remove_field_holo_light.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_remove_field_holo_light.png
new file mode 100644
index 000000000..0fec2f2b5
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_remove_field_holo_light.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_star_dk.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_star_dk.png
new file mode 100644
index 000000000..fa682b11b
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_star_dk.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_star_holo_light.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_star_holo_light.png
new file mode 100644
index 000000000..6c45bc8e6
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_star_holo_light.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_star_lt.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_star_lt.png
new file mode 100644
index 000000000..955f7383b
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_menu_star_lt.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_message_24dp.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_message_24dp.png
new file mode 100644
index 000000000..0a79824b8
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_message_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_person_24dp.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_person_24dp.png
new file mode 100644
index 000000000..184f7418d
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_person_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_person_add_24dp.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_person_add_24dp.png
new file mode 100644
index 000000000..8f744f039
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_person_add_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_phone_attach.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_phone_attach.png
new file mode 100644
index 000000000..6a6cdeeaa
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_phone_attach.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_rx_videocam.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_rx_videocam.png
new file mode 100644
index 000000000..89d29b7f5
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_rx_videocam.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_scroll_handle.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_scroll_handle.png
new file mode 100644
index 000000000..55f1d1369
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_scroll_handle.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_tx_videocam.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_tx_videocam.png
new file mode 100644
index 000000000..8d897ba5a
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_tx_videocam.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_videocam.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_videocam.png
new file mode 100644
index 000000000..4ab5ad0ee
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_videocam.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/ic_voicemail_avatar.png b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_voicemail_avatar.png
new file mode 100644
index 000000000..d0979e9eb
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/ic_voicemail_avatar.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/list_activated_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/list_focused_holo.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/list_longpressed_holo_light.9.png
Binary files 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
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/list_pressed_holo_light.9.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/list_title_holo.9.png b/java/com/android/contacts/common/res/drawable-xxhdpi/list_title_holo.9.png
new file mode 100644
index 000000000..7ddf14a0d
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxhdpi/list_title_holo.9.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_ab_search.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_ab_search.png
new file mode 100644
index 000000000..2ffb2ecae
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_ab_search.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_arrow_back_24dp.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_arrow_back_24dp.png
new file mode 100644
index 000000000..ae01a04ae
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_arrow_back_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_business_white_120dp.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_business_white_120dp.png
new file mode 100644
index 000000000..1741675de
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_business_white_120dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_call_24dp.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_call_24dp.png
new file mode 100644
index 000000000..b0e020573
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_call_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_call_note_white_24dp.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_call_note_white_24dp.png
new file mode 100644
index 000000000..903c1623d
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_call_note_white_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_close_dk.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_close_dk.png
new file mode 100644
index 000000000..3a5540ff6
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_close_dk.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_create_24dp.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_create_24dp.png
new file mode 100644
index 000000000..d3ff0ecb6
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_create_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_history_white_drawable_24dp.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_history_white_drawable_24dp.png
new file mode 100644
index 000000000..5b96af5b7
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_history_white_drawable_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_info_outline_24dp.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_info_outline_24dp.png
new file mode 100644
index 000000000..3a82cab3b
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_info_outline_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_message_24dp.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_message_24dp.png
new file mode 100644
index 000000000..fa7c17ac4
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_message_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_person_24dp.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_person_24dp.png
new file mode 100644
index 000000000..33d40d8b6
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_person_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_person_add_24dp.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_person_add_24dp.png
new file mode 100644
index 000000000..2fa2cca80
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_person_add_24dp.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_phone_attach.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_phone_attach.png
new file mode 100644
index 000000000..b072ad11f
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_phone_attach.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_scroll_handle.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_scroll_handle.png
new file mode 100644
index 000000000..d90782a32
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_scroll_handle.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_videocam.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_videocam.png
new file mode 100644
index 000000000..0643ea55f
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_videocam.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_voicemail_avatar.png b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_voicemail_avatar.png
new file mode 100644
index 000000000..1d6e1aa0f
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable-xxxhdpi/ic_voicemail_avatar.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/drawable/dialog_background_material.xml b/java/com/android/contacts/common/res/drawable/dialog_background_material.xml
new file mode 100644
index 000000000..1b71cd63a
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/dialog_background_material.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:inset="16dp">
+ <shape android:shape="rectangle">
+ <corners android:radius="2dp"/>
+ <solid android:color="@color/call_subject_history_background"/>
+ </shape>
+</inset>
diff --git a/java/com/android/contacts/common/res/drawable/fastscroll_thumb.xml b/java/com/android/contacts/common/res/drawable/fastscroll_thumb.xml
new file mode 100644
index 000000000..67645ff91
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/fastscroll_thumb.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/ic_scroll_handle_pressed" android:state_pressed="true"/>
+ <item android:drawable="@drawable/ic_scroll_handle_default"/>
+</selector> \ No newline at end of file
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..56fab8f6f
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/ic_back_arrow.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 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
+ -->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:src="@drawable/ic_arrow_back_24dp"
+ android:tint="@color/actionbar_icon_color"/> \ No newline at end of file
diff --git a/java/com/android/contacts/common/res/drawable/ic_call.xml b/java/com/android/contacts/common/res/drawable/ic_call.xml
new file mode 100644
index 000000000..0fedd452f
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/ic_call.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:src="@drawable/ic_call_24dp"/>
diff --git a/java/com/android/contacts/common/res/drawable/ic_message_24dp.xml b/java/com/android/contacts/common/res/drawable/ic_message_24dp.xml
new file mode 100644
index 000000000..3c6c8b534
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/ic_message_24dp.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:src="@drawable/ic_message_24dp"/>
diff --git a/java/com/android/contacts/common/res/drawable/ic_more_vert.xml b/java/com/android/contacts/common/res/drawable/ic_more_vert.xml
new file mode 100644
index 000000000..fcc3d9e4f
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/ic_more_vert.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:width="24dp">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
+</vector>
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..0af90edb3
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/ic_person_add_tinted_24dp.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 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
+ -->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:src="@drawable/ic_person_add_24dp"
+ android:tint="@color/actionbar_icon_color"/>
diff --git a/java/com/android/contacts/common/res/drawable/ic_scroll_handle_default.xml b/java/com/android/contacts/common/res/drawable/ic_scroll_handle_default.xml
new file mode 100644
index 000000000..ac932f87c
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/ic_scroll_handle_default.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_scroll_handle"
+ android:tint="@color/dialer_secondary_text_color"/>
diff --git a/java/com/android/contacts/common/res/drawable/ic_scroll_handle_pressed.xml b/java/com/android/contacts/common/res/drawable/ic_scroll_handle_pressed.xml
new file mode 100644
index 000000000..4838de58a
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/ic_scroll_handle_pressed.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_scroll_handle"
+ android:tint="@color/dialtacts_theme_color"/> \ No newline at end of file
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..801806044
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/ic_search_add_contact.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:src="@drawable/ic_person_add_24dp"/>
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..f1b5cba43
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/ic_search_video_call.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:src="@drawable/ic_videocam"
+ android:tint="@color/search_video_call_icon_tint"/>
diff --git a/java/com/android/contacts/common/res/drawable/ic_tab_all.xml b/java/com/android/contacts/common/res/drawable/ic_tab_all.xml
new file mode 100644
index 000000000..9cc6fbc96
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/ic_tab_all.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/ic_menu_person_lt" android:state_selected="false"/>
+ <item android:drawable="@drawable/ic_menu_person_dk" android:state_selected="true"/>
+</selector>
+
diff --git a/java/com/android/contacts/common/res/drawable/ic_tab_groups.xml b/java/com/android/contacts/common/res/drawable/ic_tab_groups.xml
new file mode 100644
index 000000000..6b3e7a415
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/ic_tab_groups.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/ic_menu_group_lt" android:state_selected="false"/>
+ <item android:drawable="@drawable/ic_menu_group_dk" android:state_selected="true"/>
+</selector>
+
diff --git a/java/com/android/contacts/common/res/drawable/ic_tab_starred.xml b/java/com/android/contacts/common/res/drawable/ic_tab_starred.xml
new file mode 100644
index 000000000..a12e0993e
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/ic_tab_starred.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/ic_menu_star_lt" android:state_selected="false"/>
+ <item android:drawable="@drawable/ic_menu_star_dk" android:state_selected="true"/>
+</selector>
+
diff --git a/java/com/android/contacts/common/res/drawable/ic_work_profile.xml b/java/com/android/contacts/common/res/drawable/ic_work_profile.xml
new file mode 100644
index 000000000..fc21100c0
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/ic_work_profile.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="16dp"
+ android:viewportHeight="48"
+ android:viewportWidth="48"
+ android:width="16dp">
+
+
+ <path
+ android:fillColor="#757575"
+ android:pathData="M28 33h-8v-3H6v8c0 2.2 1.8 4 4 4h28c2.2 0 4-1.8
+4-4v-8H28v3zm12-21h-7V9l-3-3H18l-3 3.1V12H8c-2.2 0-4 1.8-4 4v8c0 2.2 1.8 4 4
+4h12v-3h8v3h12c2.2 0 4-1.8 4-4v-8c0-2.2-1.8-4-4-4zm-10 0H18V9h12v3z"/>
+ <path
+ android:pathData="M0 0h48v48H0z"/>
+</vector>
diff --git a/java/com/android/contacts/common/res/drawable/item_background_material_borderless_dark.xml b/java/com/android/contacts/common/res/drawable/item_background_material_borderless_dark.xml
new file mode 100644
index 000000000..94e309507
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/item_background_material_borderless_dark.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<!-- Based on the Theme.Material's default selectableItemBackgroundBorderless -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/dialer_ripple_material_dark"/> \ No newline at end of file
diff --git a/java/com/android/contacts/common/res/drawable/item_background_material_dark.xml b/java/com/android/contacts/common/res/drawable/item_background_material_dark.xml
new file mode 100644
index 000000000..91ab763a5
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/item_background_material_dark.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<!-- Based on the Theme.Material's default selectableItemBackground -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/dialer_ripple_material_dark">
+ <item android:id="@android:id/mask">
+ <color android:color="@android:color/white"/>
+ </item>
+</ripple> \ No newline at end of file
diff --git a/java/com/android/contacts/common/res/drawable/item_background_material_light.xml b/java/com/android/contacts/common/res/drawable/item_background_material_light.xml
new file mode 100644
index 000000000..d41accb02
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/item_background_material_light.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<!-- Based on the Theme.Material's default selectableItemBackground -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/dialer_ripple_material_light">
+ <item android:id="@android:id/mask">
+ <color android:color="@android:color/white"/>
+ </item>
+</ripple> \ No newline at end of file
diff --git a/java/com/android/contacts/common/res/drawable/list_item_activated_background.xml b/java/com/android/contacts/common/res/drawable/list_item_activated_background.xml
new file mode 100644
index 000000000..5b774fd20
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/list_item_activated_background.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/list_activated_holo" android:state_activated="true"/>
+ <item android:drawable="@drawable/list_background_holo"/>
+</selector>
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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<transition xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/list_pressed_holo_light"/>
+ <item android:drawable="@drawable/list_longpressed_holo_light"/>
+</transition>
diff --git a/java/com/android/contacts/common/res/drawable/searchedittext_custom_cursor.xml b/java/com/android/contacts/common/res/drawable/searchedittext_custom_cursor.xml
new file mode 100644
index 000000000..27614a1ac
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/searchedittext_custom_cursor.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2014 Google Inc. All Rights Reserved. -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <size android:width="2dp"/>
+ <solid android:color="@color/dialtacts_theme_color"/>
+</shape> \ No newline at end of file
diff --git a/java/com/android/contacts/common/res/drawable/unread_count_background.xml b/java/com/android/contacts/common/res/drawable/unread_count_background.xml
new file mode 100644
index 000000000..4fc6b9b60
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/unread_count_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/tab_unread_count_background_radius"/>
+ <solid android:color="@color/tab_unread_count_background_color"/>
+</shape>
diff --git a/java/com/android/contacts/common/res/drawable/view_pager_tab_background.xml b/java/com/android/contacts/common/res/drawable/view_pager_tab_background.xml
new file mode 100644
index 000000000..bef30a434
--- /dev/null
+++ b/java/com/android/contacts/common/res/drawable/view_pager_tab_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 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
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/tab_ripple_color">
+ <item android:id="@android:id/mask">
+ <color android:color="@android:color/white"/>
+ </item>
+</ripple> \ No newline at end of file
diff --git a/java/com/android/contacts/common/res/layout-ldrtl/unread_count_tab.xml b/java/com/android/contacts/common/res/layout-ldrtl/unread_count_tab.xml
new file mode 100644
index 000000000..2aa97722d
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout-ldrtl/unread_count_tab.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<!-- layoutDirection set to ltr as a workaround to a framework bug (b/22010411) causing view with
+ layout_centerInParent inside a RelativeLayout to expand to screen width when RTL is active -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/view_pager_tab_background"
+ android:layoutDirection="ltr">
+ <!-- The tab icon -->
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"/>
+ <TextView
+ android:id="@+id/count"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/tab_unread_count_background_size"
+ android:layout_marginTop="@dimen/tab_unread_count_margin_top"
+ android:layout_marginStart="@dimen/tab_unread_count_margin_left"
+ android:layout_toStartOf="@id/icon"
+ android:paddingLeft="@dimen/tab_unread_count_text_padding"
+ android:paddingRight="@dimen/tab_unread_count_text_padding"
+ android:background="@drawable/unread_count_background"
+ android:fontFamily="sans-serif-medium"
+ android:importantForAccessibility="no"
+ android:layoutDirection="locale"
+ android:minWidth="@dimen/tab_unread_count_background_size"
+ android:textAlignment="center"
+ android:textColor="@color/tab_accent_color"
+ android:textSize="@dimen/tab_unread_count_text_size"/>
+</RelativeLayout>
+
diff --git a/java/com/android/contacts/common/res/layout/account_filter_header.xml b/java/com/android/contacts/common/res/layout/account_filter_header.xml
new file mode 100644
index 000000000..a12ab08fd
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout/account_filter_header.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Layout showing the type of account filter
+ (e.g. All contacts filter, custom filter, etc.),
+ which is the header of all contact lists. -->
+
+<!-- Solely used to set a background color -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/background_primary">
+ <!-- Used to show the touch feedback -->
+ <FrameLayout
+ android:id="@+id/account_filter_header_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/contact_browser_list_header_left_margin"
+ android:layout_marginEnd="@dimen/contact_browser_list_header_right_margin"
+ android:paddingTop="@dimen/list_header_extra_top_padding"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone">
+ <!-- Shows the text and underlining -->
+ <TextView
+ android:id="@+id/account_filter_header"
+ style="@style/ContactListSeparatorTextViewStyle"
+ android:paddingStart="@dimen/contact_browser_list_item_text_indent"
+ android:paddingLeft="@dimen/contact_browser_list_item_text_indent"/>
+ </FrameLayout>
+</FrameLayout>
diff --git a/java/com/android/contacts/common/res/layout/account_selector_list_item.xml b/java/com/android/contacts/common/res/layout/account_selector_list_item.xml
new file mode 100644
index 000000000..587626e8d
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout/account_selector_list_item.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/detail_network_icon_size"
+ android:layout_height="@dimen/detail_network_icon_size"
+ android:layout_margin="16dip"
+ android:layout_gravity="center_vertical"/>
+
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="8dp"
+ android:layout_gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@android:id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dip"
+ android:layout_marginRight="8dip"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+ <TextView
+ android:id="@android:id/text2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dip"
+ android:layout_marginRight="8dip"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"/>
+ </LinearLayout>
+</LinearLayout>
diff --git a/java/com/android/contacts/common/res/layout/account_selector_list_item_condensed.xml b/java/com/android/contacts/common/res/layout/account_selector_list_item_condensed.xml
new file mode 100644
index 000000000..33821166e
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout/account_selector_list_item_condensed.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/detail_network_icon_size"
+ android:layout_height="@dimen/detail_network_icon_size"
+ android:layout_margin="24dip"
+ android:layout_gravity="center_vertical"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@android:id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dip"
+ android:layout_marginRight="8dip"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+ <TextView
+ android:id="@android:id/text2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dip"
+ android:layout_marginRight="8dip"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"/>
+ </LinearLayout>
+</LinearLayout>
diff --git a/java/com/android/contacts/common/res/layout/call_subject_history.xml b/java/com/android/contacts/common/res/layout/call_subject_history.xml
new file mode 100644
index 000000000..733f1d8b6
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout/call_subject_history.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/transparent">
+
+ <ListView
+ android:id="@+id/subject_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:background="@color/call_subject_history_background"
+ android:divider="@null"
+ android:elevation="8dp"/>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/java/com/android/contacts/common/res/layout/call_subject_history_list_item.xml b/java/com/android/contacts/common/res/layout/call_subject_history_list_item.xml
new file mode 100644
index 000000000..c378f24b2
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout/call_subject_history_list_item.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/call_subject_history_item_padding"
+ android:paddingBottom="@dimen/call_subject_history_item_padding"
+ android:paddingStart="@dimen/call_subject_dialog_margin"
+ android:paddingEnd="@dimen/call_subject_dialog_margin"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:textColor="@color/dialer_primary_text_color"
+ android:textSize="@dimen/call_subject_dialog_primary_text_size"/>
diff --git a/java/com/android/contacts/common/res/layout/contact_detail_list_padding.xml b/java/com/android/contacts/common/res/layout/contact_detail_list_padding.xml
new file mode 100644
index 000000000..02a5c809c
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout/contact_detail_list_padding.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- The actual padding is embedded in a FrameLayout since we cannot change the
+ visibility of a header view in a ListView without having a parent view. -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <View
+ android:id="@+id/contact_detail_list_padding"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/list_header_extra_top_padding"/>
+</FrameLayout>
diff --git a/java/com/android/contacts/common/res/layout/contact_list_card.xml b/java/com/android/contacts/common/res/layout/contact_list_card.xml
new file mode 100644
index 000000000..a04f4cad9
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout/contact_list_card.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list_card"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:visibility="invisible">
+ <View
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="@integer/contact_list_space_layout_weight"
+ android:background="@color/background_primary"/>
+ <View
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="@integer/contact_list_card_layout_weight"
+ android:background="@color/contact_all_list_background_color"
+ android:elevation="@dimen/contact_list_card_elevation"/>
+ <View
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="@integer/contact_list_space_layout_weight"
+ android:background="@color/background_primary"/>
+</LinearLayout>
diff --git a/java/com/android/contacts/common/res/layout/contact_list_content.xml b/java/com/android/contacts/common/res/layout/contact_list_content.xml
new file mode 100644
index 000000000..3ee27a0ad
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout/contact_list_content.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<!-- android:paddingTop is used instead of android:layout_marginTop. It looks
+ android:layout_marginTop is ignored when used with <fragment></fragment>, which
+ only happens in Tablet UI since we rely on ViewPager in Phone UI.
+ Instead, android:layout_marginTop inside <fragment /> is effective. -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pinned_header_list_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?attr/contact_browser_background"
+ android:orientation="vertical">
+
+ <!-- Shown only when an Account filter is set.
+ - paddingTop should be here to show "shade" effect correctly. -->
+ <include layout="@layout/account_filter_header"/>
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1">
+ <include layout="@layout/contact_list_card"/>
+ <view
+ android:id="@android:id/list"
+ class="com.android.contacts.common.list.PinnedHeaderListView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="?attr/contact_browser_list_padding_left"
+ android:layout_marginEnd="?attr/contact_browser_list_padding_right"
+ android:layout_marginLeft="?attr/contact_browser_list_padding_left"
+ android:layout_marginRight="?attr/contact_browser_list_padding_right"
+ android:paddingTop="?attr/list_item_padding_top"
+ android:clipToPadding="false"
+ android:fadingEdge="none"
+ android:fastScrollEnabled="true"/>
+ <ProgressBar
+ android:id="@+id/search_progress"
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:visibility="gone"/>
+ </FrameLayout>
+
+</LinearLayout>
diff --git a/java/com/android/contacts/common/res/layout/default_account_checkbox.xml b/java/com/android/contacts/common/res/layout/default_account_checkbox.xml
new file mode 100644
index 000000000..b7c0cf644
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout/default_account_checkbox.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/default_account_checkbox_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:orientation="vertical">
+ <CheckBox
+ android:id="@+id/default_account_checkbox_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="20dip"
+ android:layout_marginLeft="13dip"
+ android:paddingStart="15dip"
+ android:gravity="center"
+ android:text="@string/set_default_account"
+ android:textAlignment="viewStart"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@color/dialer_secondary_text_color"
+ />
+</LinearLayout>
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
new file mode 100644
index 000000000..709bb50cb
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout/dialog_call_subject.xml
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/call_subject_dialog"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/transparent"
+ android:orientation="vertical">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
+
+ <!-- The call subject dialog will be centered in the space above the subject list. -->
+ <LinearLayout
+ android:id="@+id/dialog_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:background="@drawable/dialog_background_material"
+ android:clickable="true"
+ android:elevation="16dp"
+ android:orientation="vertical"
+ android:theme="@android:style/Theme.Material.Light.Dialog">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/call_subject_dialog_margin"
+ android:layout_marginStart="@dimen/call_subject_dialog_margin"
+ android:layout_marginEnd="@dimen/call_subject_dialog_margin"
+ android:orientation="horizontal">
+
+ <QuickContactBadge
+ android:id="@+id/contact_photo"
+ android:layout_width="@dimen/call_subject_dialog_contact_photo_size"
+ android:layout_height="@dimen/call_subject_dialog_contact_photo_size"
+ android:layout_marginEnd="@dimen/call_subject_dialog_margin"
+ android:layout_gravity="top"
+ android:focusable="true"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textColor="@color/dialer_primary_text_color"
+ android:textSize="@dimen/call_subject_dialog_secondary_text_size"/>
+
+ <TextView
+ android:id="@+id/number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/call_subject_dialog_between_line_margin"
+ android:layout_gravity="center_vertical"
+ android:singleLine="true"
+ android:textColor="@color/dialer_secondary_text_color"
+ android:textSize="@dimen/call_subject_dialog_secondary_text_size"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <EditText
+ android:id="@+id/call_subject"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:layout_marginTop="@dimen/call_subject_dialog_edit_spacing"
+ android:layout_marginStart="@dimen/call_subject_dialog_margin"
+ android:layout_marginEnd="@dimen/call_subject_dialog_margin"
+ android:layout_gravity="top"
+ android:background="@null"
+ android:gravity="top"
+ android:hint="@string/call_subject_hint"
+ android:textColor="@color/dialer_secondary_text_color"
+ android:textSize="@dimen/call_subject_dialog_secondary_text_size"
+ />
+
+ <TextView
+ android:id="@+id/character_limit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/call_subject_dialog_margin"
+ android:layout_marginBottom="@dimen/call_subject_dialog_margin"
+ android:layout_marginStart="@dimen/call_subject_dialog_margin"
+ android:layout_marginEnd="@dimen/call_subject_dialog_margin"
+ android:singleLine="true"
+ android:textColor="@color/dialer_secondary_text_color"
+ android:textSize="@dimen/call_subject_dialog_secondary_text_size"/>
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dp"
+ android:background="@color/call_subject_divider"/>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/call_subject_dialog_margin"
+ android:layout_marginBottom="@dimen/call_subject_dialog_margin"
+ android:layout_marginStart="@dimen/call_subject_dialog_margin"
+ android:layout_marginEnd="@dimen/call_subject_dialog_margin">
+
+ <ImageView
+ android:id="@+id/history_button"
+ android:layout_width="25dp"
+ android:layout_height="25dp"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_history_white_drawable_24dp"
+ android:tint="@color/call_subject_history_icon"/>
+
+ <TextView
+ android:id="@+id/send_and_call_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:singleLine="true"
+ android:text="@string/send_and_call_button"
+ android:textColor="@color/call_subject_button"
+ android:textSize="@dimen/call_subject_dialog_secondary_text_size"/>
+
+ </RelativeLayout>
+ </LinearLayout>
+ </RelativeLayout>
+ <!-- The subject list is pinned to the bottom of the screen. -->
+ <ListView
+ android:id="@+id/subject_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/dialog_view"
+ android:background="@color/call_subject_history_background"
+ android:divider="@null"
+ android:elevation="8dp"/>
+
+</LinearLayout>
diff --git a/java/com/android/contacts/common/res/layout/directory_header.xml b/java/com/android/contacts/common/res/layout/directory_header.xml
new file mode 100644
index 000000000..b8f5163c0
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout/directory_header.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<!-- Layout used for list section separators. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/directory_header"
+ style="@style/DirectoryHeader"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/directory_header_extra_top_padding"
+ android:paddingBottom="@dimen/directory_header_extra_bottom_padding"
+ android:paddingStart="?attr/list_item_padding_left"
+ android:paddingEnd="?attr/list_item_padding_right"
+ android:paddingLeft="?attr/list_item_padding_left"
+ android:paddingRight="?attr/list_item_padding_right"
+ android:background="?attr/contact_browser_background"
+ android:minHeight="@dimen/list_section_divider_min_height">
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/DirectoryHeaderStyle"/>
+ <TextView
+ android:id="@+id/display_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/DirectoryHeaderStyle"/>
+ <TextView
+ android:id="@+id/count"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:paddingTop="1dip"
+ android:gravity="end"
+ android:singleLine="true"
+ android:textAppearance="@style/DirectoryHeaderStyle"/>
+</LinearLayout>
diff --git a/java/com/android/contacts/common/res/layout/list_separator.xml b/java/com/android/contacts/common/res/layout/list_separator.xml
new file mode 100644
index 000000000..ab60605c5
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout/list_separator.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/title"
+ android:textStyle="bold"
+ android:paddingTop="16dip"
+ android:paddingBottom="15dip"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip"
+ android:paddingLeft="16dip"
+ android:paddingRight="16dip"
+ android:textColor="@color/frequently_contacted_title_color"
+ android:textSize="@dimen/frequently_contacted_title_text_size"/>
diff --git a/java/com/android/contacts/common/res/layout/search_bar_expanded.xml b/java/com/android/contacts/common/res/layout/search_bar_expanded.xml
new file mode 100644
index 000000000..8a3bd6088
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout/search_bar_expanded.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/search_box_expanded"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ android:visibility="gone">
+
+ <ImageButton
+ android:id="@+id/search_back_button"
+ android:layout_width="@dimen/search_box_icon_size"
+ android:layout_height="@dimen/search_box_icon_size"
+ android:layout_margin="@dimen/search_box_navigation_icon_margin"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/action_menu_back_from_search"
+ android:src="@drawable/ic_back_arrow"
+ android:tint="@color/contactscommon_actionbar_background_color"/>
+
+ <EditText
+ android:id="@+id/search_view"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/search_box_icon_size"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/search_box_text_left_margin"
+ android:background="@null"
+ android:fontFamily="@string/search_font_family"
+ android:imeOptions="flagNoExtractUi"
+ android:inputType="textFilter"
+ android:singleLine="true"
+ android:textColor="@color/searchbox_text_color"
+ android:textColorHint="@color/searchbox_hint_text_color"
+ android:textCursorDrawable="@drawable/searchedittext_custom_cursor"
+ android:textSize="@dimen/search_text_size"/>
+
+ <ImageView
+ android:id="@+id/search_close_button"
+ android:layout_width="@dimen/search_box_close_icon_size"
+ android:layout_height="@dimen/search_box_close_icon_size"
+ android:padding="@dimen/search_box_close_icon_padding"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ android:clickable="true"
+ android:contentDescription="@string/description_clear_search"
+ android:src="@drawable/ic_close_dk"
+ android:tint="@color/searchbox_icon_tint"/>
+
+</LinearLayout>
diff --git a/java/com/android/contacts/common/res/layout/select_account_list_item.xml b/java/com/android/contacts/common/res/layout/select_account_list_item.xml
new file mode 100644
index 000000000..fbd31e573
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout/select_account_list_item.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<!-- Layout of a single item in the InCallUI Account Chooser Dialog. -->
+<com.android.contacts.common.widget.ActivityTouchLinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="8dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:scaleType="center"/>
+
+ <LinearLayout
+ android:id="@+id/text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginStart="8dp"
+ android:gravity="start|center_vertical"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:includeFontPadding="false"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/dialer_primary_text_color"/>
+ <TextView
+ android:id="@+id/number"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:includeFontPadding="false"
+ android:maxLines="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:visibility="gone"/>
+ </LinearLayout>
+
+</com.android.contacts.common.widget.ActivityTouchLinearLayout>
diff --git a/java/com/android/contacts/common/res/layout/unread_count_tab.xml b/java/com/android/contacts/common/res/layout/unread_count_tab.xml
new file mode 100644
index 000000000..83481ee2d
--- /dev/null
+++ b/java/com/android/contacts/common/res/layout/unread_count_tab.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/view_pager_tab_background">
+ <!-- The tab icon -->
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"/>
+ <TextView
+ android:id="@+id/count"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/tab_unread_count_background_size"
+ android:layout_marginTop="@dimen/tab_unread_count_margin_top"
+ android:layout_marginStart="@dimen/tab_unread_count_margin_left"
+ android:layout_toEndOf="@id/icon"
+ android:paddingLeft="@dimen/tab_unread_count_text_padding"
+ android:paddingRight="@dimen/tab_unread_count_text_padding"
+ android:background="@drawable/unread_count_background"
+ android:fontFamily="sans-serif-medium"
+ android:gravity="center"
+ android:importantForAccessibility="no"
+ android:minWidth="@dimen/tab_unread_count_background_size"
+ android:textAlignment="center"
+ android:textColor="@color/tab_accent_color"
+ android:textSize="@dimen/tab_unread_count_text_size"/>
+</RelativeLayout>
diff --git a/java/com/android/contacts/common/res/mipmap-hdpi/ic_contacts_launcher.png b/java/com/android/contacts/common/res/mipmap-hdpi/ic_contacts_launcher.png
new file mode 100644
index 000000000..64eff002f
--- /dev/null
+++ b/java/com/android/contacts/common/res/mipmap-hdpi/ic_contacts_launcher.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/mipmap-mdpi/ic_contacts_launcher.png b/java/com/android/contacts/common/res/mipmap-mdpi/ic_contacts_launcher.png
new file mode 100644
index 000000000..b4ee8215a
--- /dev/null
+++ b/java/com/android/contacts/common/res/mipmap-mdpi/ic_contacts_launcher.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/mipmap-xhdpi/ic_contacts_launcher.png b/java/com/android/contacts/common/res/mipmap-xhdpi/ic_contacts_launcher.png
new file mode 100644
index 000000000..6feeadfbe
--- /dev/null
+++ b/java/com/android/contacts/common/res/mipmap-xhdpi/ic_contacts_launcher.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/mipmap-xxhdpi/ic_contacts_launcher.png b/java/com/android/contacts/common/res/mipmap-xxhdpi/ic_contacts_launcher.png
new file mode 100644
index 000000000..01a3fde9d
--- /dev/null
+++ b/java/com/android/contacts/common/res/mipmap-xxhdpi/ic_contacts_launcher.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/mipmap-xxxhdpi/ic_contacts_launcher.png b/java/com/android/contacts/common/res/mipmap-xxxhdpi/ic_contacts_launcher.png
new file mode 100644
index 000000000..328e067ee
--- /dev/null
+++ b/java/com/android/contacts/common/res/mipmap-xxxhdpi/ic_contacts_launcher.png
Binary files differ
diff --git a/java/com/android/contacts/common/res/values-ja/donottranslate_config.xml b/java/com/android/contacts/common/res/values-ja/donottranslate_config.xml
new file mode 100644
index 000000000..e05c6d658
--- /dev/null
+++ b/java/com/android/contacts/common/res/values-ja/donottranslate_config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- If true, an option is shown in Display Options UI to choose a sort order -->
+ <bool name="config_sort_order_user_changeable">false</bool>
+
+ <!-- If true, the default sort order is primary (i.e. by given name) -->
+ <bool name="config_default_sort_order_primary">true</bool>
+
+ <!-- If true, an option is shown in Display Options UI to choose a name display order -->
+ <bool name="config_display_order_user_changeable">false</bool>
+
+ <!-- If true, the default sort order is primary (i.e. by given name) -->
+ <bool name="config_default_display_order_primary">true</bool>
+
+ <!-- If true, the order of name fields in the editor is primary (i.e. given name first) -->
+ <bool name="config_editor_field_order_primary">false</bool>
+
+ <!-- If true, phonetic name is included in the contact editor by default -->
+ <bool name="config_editor_include_phonetic_name">true</bool>
+</resources> \ No newline at end of file
diff --git a/java/com/android/contacts/common/res/values-ko/donottranslate_config.xml b/java/com/android/contacts/common/res/values-ko/donottranslate_config.xml
new file mode 100644
index 000000000..8def55498
--- /dev/null
+++ b/java/com/android/contacts/common/res/values-ko/donottranslate_config.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- If true, an option is shown in Display Options UI to choose a sort order -->
+ <bool name="config_sort_order_user_changeable">false</bool>
+
+ <!-- If true, the default sort order is primary (i.e. by given name) -->
+ <bool name="config_default_sort_order_primary">false</bool>
+
+ <!-- If true, an option is shown in Display Options UI to choose a name display order -->
+ <bool name="config_display_order_user_changeable">false</bool>
+
+ <!-- If true, the default sort order is primary (i.e. by given name) -->
+ <bool name="config_default_display_order_primary">false</bool>
+
+ <!-- If true, the order of name fields in the editor is primary (i.e. given name first) -->
+ <bool name="config_editor_field_order_primary">false</bool>
+</resources> \ No newline at end of file
diff --git a/java/com/android/contacts/common/res/values-land/integers.xml b/java/com/android/contacts/common/res/values-land/integers.xml
new file mode 100644
index 000000000..26bac6222
--- /dev/null
+++ b/java/com/android/contacts/common/res/values-land/integers.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<resources>
+ <integer name="contact_tile_column_count_in_favorites">3</integer>
+
+ <!-- The number of characters in the snippet before we need to tokenize and ellipse. -->
+ <integer name="snippet_length_before_tokenize">60</integer>
+</resources>
diff --git a/java/com/android/contacts/common/res/values-sw600dp-land/integers.xml b/java/com/android/contacts/common/res/values-sw600dp-land/integers.xml
new file mode 100644
index 000000000..be4eb0bb0
--- /dev/null
+++ b/java/com/android/contacts/common/res/values-sw600dp-land/integers.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<resources>
+ <integer name="contact_tile_column_count_in_favorites">3</integer>
+
+ <!-- The number of characters in the snippet before we need to tokenize and ellipse. -->
+ <integer name="snippet_length_before_tokenize">20</integer>
+</resources>
diff --git a/java/com/android/contacts/common/res/values-sw600dp/dimens.xml b/java/com/android/contacts/common/res/values-sw600dp/dimens.xml
new file mode 100644
index 000000000..cf67a1e72
--- /dev/null
+++ b/java/com/android/contacts/common/res/values-sw600dp/dimens.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <dimen name="detail_item_side_margin">0dip</dimen>
+
+ <dimen name="contact_browser_list_header_left_margin">@dimen/list_visible_scrollbar_padding
+ </dimen>
+ <dimen name="contact_browser_list_header_right_margin">24dip</dimen>
+ <dimen name="contact_browser_list_top_margin">16dip</dimen>
+
+ <!-- Right margin of the floating action button -->
+ <dimen name="floating_action_button_margin_right">32dp</dimen>
+ <!-- Bottom margin of the floating action button -->
+ <dimen name="floating_action_button_margin_bottom">32dp</dimen>
+</resources>
diff --git a/java/com/android/contacts/common/res/values-sw600dp/integers.xml b/java/com/android/contacts/common/res/values-sw600dp/integers.xml
new file mode 100644
index 000000000..31aeee995
--- /dev/null
+++ b/java/com/android/contacts/common/res/values-sw600dp/integers.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<resources>
+ <integer name="contact_tile_column_count_in_favorites">3</integer>
+
+ <!-- The number of characters in the snippet before we need to tokenize and ellipse. -->
+ <!-- Yikes, there is less space on a tablet! This makes the search experience rather
+ poor. Another reason to get rid of the exist tablet layout. -->
+ <integer name="snippet_length_before_tokenize">15</integer>
+</resources>
diff --git a/java/com/android/contacts/common/res/values-sw720dp-land/integers.xml b/java/com/android/contacts/common/res/values-sw720dp-land/integers.xml
new file mode 100644
index 000000000..577716d24
--- /dev/null
+++ b/java/com/android/contacts/common/res/values-sw720dp-land/integers.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<resources>
+ <integer name="contact_tile_column_count_in_favorites">4</integer>
+
+ <!-- The number of characters in the snippet before we need to tokenize and ellipse. -->
+ <integer name="snippet_length_before_tokenize">30</integer>
+</resources>
diff --git a/java/com/android/contacts/common/res/values-sw720dp/integers.xml b/java/com/android/contacts/common/res/values-sw720dp/integers.xml
new file mode 100644
index 000000000..05e309351
--- /dev/null
+++ b/java/com/android/contacts/common/res/values-sw720dp/integers.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<resources>
+ <integer name="contact_tile_column_count_in_favorites">2</integer>
+
+ <!-- The number of characters in the snippet before we need to tokenize and ellipse. -->
+ <integer name="snippet_length_before_tokenize">20</integer>
+</resources>
diff --git a/java/com/android/contacts/common/res/values-zh-rCN/donottranslate_config.xml b/java/com/android/contacts/common/res/values-zh-rCN/donottranslate_config.xml
new file mode 100644
index 000000000..08023992b
--- /dev/null
+++ b/java/com/android/contacts/common/res/values-zh-rCN/donottranslate_config.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- If true, an option is shown in Display Options UI to choose a sort order -->
+ <bool name="config_sort_order_user_changeable">false</bool>
+
+ <!-- If true, the default sort order is primary (i.e. by given name) -->
+ <bool name="config_default_sort_order_primary">true</bool>
+
+ <!-- If true, an option is shown in Display Options UI to choose a name display order -->
+ <bool name="config_display_order_user_changeable">false</bool>
+
+ <!-- If true, the default sort order is primary (i.e. by given name) -->
+ <bool name="config_default_display_order_primary">true</bool>
+
+ <!-- If true, the order of name fields in the editor is primary (i.e. given name first) -->
+ <bool name="config_editor_field_order_primary">false</bool>
+</resources> \ No newline at end of file
diff --git a/java/com/android/contacts/common/res/values-zh-rTW/donottranslate_config.xml b/java/com/android/contacts/common/res/values-zh-rTW/donottranslate_config.xml
new file mode 100644
index 000000000..08023992b
--- /dev/null
+++ b/java/com/android/contacts/common/res/values-zh-rTW/donottranslate_config.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- If true, an option is shown in Display Options UI to choose a sort order -->
+ <bool name="config_sort_order_user_changeable">false</bool>
+
+ <!-- If true, the default sort order is primary (i.e. by given name) -->
+ <bool name="config_default_sort_order_primary">true</bool>
+
+ <!-- If true, an option is shown in Display Options UI to choose a name display order -->
+ <bool name="config_display_order_user_changeable">false</bool>
+
+ <!-- If true, the default sort order is primary (i.e. by given name) -->
+ <bool name="config_default_display_order_primary">true</bool>
+
+ <!-- If true, the order of name fields in the editor is primary (i.e. given name first) -->
+ <bool name="config_editor_field_order_primary">false</bool>
+</resources> \ No newline at end of file
diff --git a/java/com/android/contacts/common/res/values/animation_constants.xml b/java/com/android/contacts/common/res/values/animation_constants.xml
new file mode 100644
index 000000000..9eec7d6c8
--- /dev/null
+++ b/java/com/android/contacts/common/res/values/animation_constants.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 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
+ -->
+<resources>
+ <integer name="floating_action_button_animation_duration">250</integer>
+</resources>
diff --git a/java/com/android/contacts/common/res/values/attrs.xml b/java/com/android/contacts/common/res/values/attrs.xml
new file mode 100644
index 000000000..44d04f025
--- /dev/null
+++ b/java/com/android/contacts/common/res/values/attrs.xml
@@ -0,0 +1,83 @@
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <declare-styleable name="Theme">
+ <attr name="android:textColorSecondary"/>
+ </declare-styleable>
+
+ <declare-styleable name="ContactsDataKind">
+ <!-- Mime-type handled by this mapping. -->
+ <attr name="android:mimeType"/>
+ <!-- Icon used to represent data of this kind. -->
+ <attr name="android:icon"/>
+ <!-- Column in data table that summarizes this data. -->
+ <attr name="android:summaryColumn"/>
+ <!-- Column in data table that contains details for this data. -->
+ <attr name="android:detailColumn"/>
+ <!-- Flag indicating that detail should be built from SocialProvider. -->
+ <attr name="android:detailSocialSummary"/>
+ <!-- Resource representing the term "All Contacts" (e.g. "All Friends" or
+ "All connections"). Optional (Default is "All Contacts"). -->
+ <attr name="android:allContactsName"/>
+ </declare-styleable>
+
+ <declare-styleable name="ContactListItemView">
+ <attr format="dimension" name="list_item_height"/>
+ <attr format="dimension" name="list_section_header_height"/>
+ <attr format="reference" name="activated_background"/>
+ <attr format="reference" name="section_header_background"/>
+ <attr format="dimension" name="list_item_padding_top"/>
+ <attr format="dimension" name="list_item_padding_right"/>
+ <attr format="dimension" name="list_item_padding_bottom"/>
+ <attr format="dimension" name="list_item_padding_left"/>
+ <attr format="dimension" name="list_item_gap_between_image_and_text"/>
+ <attr format="dimension" name="list_item_gap_between_label_and_data"/>
+ <attr format="dimension" name="list_item_presence_icon_margin"/>
+ <attr format="dimension" name="list_item_presence_icon_size"/>
+ <attr format="dimension" name="list_item_photo_size"/>
+ <attr format="dimension" name="list_item_profile_photo_size"/>
+ <attr format="color" name="list_item_prefix_highlight_color"/>
+ <attr format="color" name="list_item_background_color"/>
+ <attr format="dimension" name="list_item_header_text_indent"/>
+ <attr format="color" name="list_item_header_text_color"/>
+ <attr format="dimension" name="list_item_header_text_size"/>
+ <attr format="dimension" name="list_item_header_height"/>
+ <attr format="color" name="list_item_name_text_color"/>
+ <attr format="dimension" name="list_item_name_text_size"/>
+ <attr format="dimension" name="list_item_text_indent"/>
+ <attr format="dimension" name="list_item_text_offset_top"/>
+ <attr format="integer" name="list_item_data_width_weight"/>
+ <attr format="integer" name="list_item_label_width_weight"/>
+ <attr format="dimension" name="list_item_video_call_icon_size"/>
+ <attr format="dimension" name="list_item_video_call_icon_margin"/>
+ </declare-styleable>
+
+ <declare-styleable name="ContactBrowser">
+ <attr format="dimension" name="contact_browser_list_padding_left"/>
+ <attr format="dimension" name="contact_browser_list_padding_right"/>
+ <attr format="reference" name="contact_browser_background"/>
+ </declare-styleable>
+
+ <declare-styleable name="ProportionalLayout">
+ <attr format="string" name="direction"/>
+ <attr format="float" name="ratio"/>
+ </declare-styleable>
+
+ <declare-styleable name="Favorites">
+ <attr format="dimension" name="favorites_padding_bottom"/>
+ </declare-styleable>
+</resources>
diff --git a/java/com/android/contacts/common/res/values/colors.xml b/java/com/android/contacts/common/res/values/colors.xml
new file mode 100644
index 000000000..7524eff58
--- /dev/null
+++ b/java/com/android/contacts/common/res/values/colors.xml
@@ -0,0 +1,158 @@
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <!-- Background color corresponding to the holo list 9-patch. -->
+ <color name="holo_list_background_color">#eeeeee</color>
+
+ <color name="focus_color">#44ff0000</color>
+
+ <!-- Color of ripples used for views with dark backgrounds -->
+ <color name="dialer_ripple_material_dark">#a0ffffff</color>
+
+ <!-- Color of ripples used for views with light backgrounds -->
+ <color name="dialer_ripple_material_light">#30000000</color>
+
+ <!-- Divider color for header separator -->
+ <color name="primary_text_color">#363636</color>
+
+ <color name="secondary_text_color">@color/dialer_secondary_text_color</color>
+
+ <!-- Text color for section header. -->
+ <color name="section_header_text_color">#2A56C6</color>
+
+ <!-- Divider color for header separator -->
+ <color name="main_header_separator_color">#AAAAAA</color>
+
+ <!-- Divider color for header separator -->
+ <color name="secondary_header_separator_color">#D0D0D0</color>
+
+ <!-- Color of the theme of the People app -->
+ <color name="people_app_theme_color">#363636</color>
+
+ <!-- Color of image view placeholder. -->
+ <color name="image_placeholder">#DDDDDD</color>
+
+ <!-- Color of the semi-transparent shadow box on contact tiles -->
+ <color name="contact_tile_shadow_box_color">#7F000000</color>
+
+ <!-- Color of the status message for starred contacts in the People app -->
+ <color name="people_contact_tile_status_color">#CCCCCC</color>
+
+ <color name="shortcut_overlay_text_background">#7f000000</color>
+
+ <color name="textColorIconOverlay">#fff</color>
+ <color name="textColorIconOverlayShadow">#000</color>
+
+
+ <array name="letter_tile_colors">
+ <item>#DB4437</item>
+ <item>#E91E63</item>
+ <item>#9C27B0</item>
+ <item>#673AB7</item>
+ <item>#3F51B5</item>
+ <item>#4285F4</item>
+ <item>#039BE5</item>
+ <item>#0097A7</item>
+ <item>#009688</item>
+ <item>#0F9D58</item>
+ <item>#689F38</item>
+ <item>#EF6C00</item>
+ <item>#FF5722</item>
+ <item>#757575</item>
+ </array>
+
+ <!-- Darker versions of letter_tile_colors, two shades darker. These colors are used
+ for settings secondary activity colors. -->
+ <array name="letter_tile_colors_dark">
+ <item>#C53929</item>
+ <item>#C2185B</item>
+ <item>#7B1FA2</item>
+ <item>#512DA8</item>
+ <item>#303F9F</item>
+ <item>#3367D6</item>
+ <item>#0277BD</item>
+ <item>#006064</item>
+ <item>#00796B</item>
+ <item>#0B8043</item>
+ <item>#33691E</item>
+ <item>#E65100</item>
+ <item>#E64A19</item>
+ <item>#424242</item>
+ </array>
+
+ <!-- The default color used for tinting photos when no color can be extracted via Palette,
+ this is Blue Grey 500 -->
+ <color name="quickcontact_default_photo_tint_color">#607D8B</color>
+ <!-- The default secondary color when no color can be extracted via Palette,
+ this is Blue Grey 700 -->
+ <color name="quickcontact_default_photo_tint_color_dark">#455A64</color>
+
+
+ <color name="letter_tile_default_color">#cccccc</color>
+
+ <color name="letter_tile_font_color">#ffffff</color>
+
+ <color name="contactscommon_actionbar_background_color">@color/dialer_theme_color</color>
+ <!-- Color for icons in the actionbar -->
+ <color name="actionbar_icon_color">#ffffff</color>
+ <!-- Darker version of the actionbar color. Used for the status bar and navigation bar colors. -->
+ <color name="actionbar_background_color_dark">#008aa1</color>
+
+ <color name="tab_ripple_color">#ffffff</color>
+ <color name="tab_accent_color">@color/tab_ripple_color</color>
+ <color name="tab_selected_underline_color">#f50057</color>
+ <color name="tab_unread_count_background_color">#1C3AA9</color>
+
+ <!-- Color of the title to the Frequently Contacted section -->
+ <color name="frequently_contacted_title_color">@color/contactscommon_actionbar_background_color
+ </color>
+
+ <!-- Color of action bar text. Ensure this stays in sync with packages/Telephony
+ phone_settings_actionbar_text_color-->
+ <color name="actionbar_text_color">#ffffff</color>
+ <color name="actionbar_unselected_text_color">#a6ffffff</color>
+
+ <!-- Text color of the search box text as entered by user -->
+ <color name="searchbox_text_color">#000000</color>
+ <!-- Background color of the search box -->
+ <color name="searchbox_background_color">#ffffff</color>
+
+ <color name="searchbox_hint_text_color">#737373</color>
+ <color name="searchbox_icon_tint">@color/searchbox_hint_text_color</color>
+
+ <color name="search_shortcut_icon_color">@color/dialtacts_theme_color</color>
+
+ <!-- Color of the background of the contact detail and editor pages -->
+ <color name="background_primary">#f9f9f9</color>
+ <color name="contact_all_list_background_color">#FFFFFF</color>
+
+ <!-- Text color used for character counter when the max limit has been exceeded -->
+ <color name="call_subject_limit_exceeded">#d1041c</color>
+
+ <!-- Tint color for the call subject history icon. -->
+ <color name="call_subject_history_icon">#000000</color>
+
+ <!-- Divider line on the call subject dialog. -->
+ <color name="call_subject_divider">#d8d8d8</color>
+
+ <!-- Text color for the SEND & CALL button on the call subject dialog. -->
+ <color name="call_subject_button">#00c853</color>
+
+ <!-- Background color for the call subject history view. -->
+ <color name="call_subject_history_background">#ffffff</color>
+ <color name="search_video_call_icon_tint">@color/searchbox_hint_text_color</color>
+</resources>
diff --git a/java/com/android/contacts/common/res/values/dimens.xml b/java/com/android/contacts/common/res/values/dimens.xml
new file mode 100644
index 000000000..642eb31a4
--- /dev/null
+++ b/java/com/android/contacts/common/res/values/dimens.xml
@@ -0,0 +1,161 @@
+<!--
+ ~ 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
+ -->
+
+<resources>
+
+ <!-- Padding between the action bar's bottom edge and the first header
+ in contacts/group lists. -->
+ <dimen name="list_header_extra_top_padding">0dip</dimen>
+
+ <!-- Minimum height used with @drawable/list_section_divider_holo_custom.
+ Right now the drawable has implicit 32dip minimal height, which is confusing.
+ This value is for making the hidden configuration explicit in xml. -->
+ <dimen name="list_section_divider_min_height">32dip</dimen>
+
+ <dimen name="directory_header_extra_top_padding">18dp</dimen>
+ <dimen name="directory_header_extra_bottom_padding">8dp</dimen>
+
+ <!-- Horizontal padding in between contact tiles -->
+ <dimen name="contact_tile_divider_padding">23dip</dimen>
+ <!-- Horizontal whitespace (both padding and margin) before the first tile and after the last tile -->
+ <dimen name="contact_tile_start_end_whitespace">16dip</dimen>
+
+ <!-- Left and right padding for a contact detail item -->
+ <dimen name="detail_item_side_margin">16dip</dimen>
+
+ <!-- ContactTile Layouts -->
+ <!--
+ Use sp instead of dip so that the shadowbox heights can all scale uniformly
+ when the font size is scaled for accessibility purposes
+ -->
+ <dimen name="contact_tile_shadowbox_height">48sp</dimen>
+
+ <!-- Top padding of the ListView in the contact tile list -->
+ <dimen name="contact_tile_list_padding_top">0dip</dimen>
+
+ <!-- Padding to be used between a visible scrollbar and the contact list -->
+ <dimen name="list_visible_scrollbar_padding">32dip</dimen>
+
+ <dimen name="contact_browser_list_header_left_margin">16dip</dimen>
+ <dimen name="contact_browser_list_header_right_margin">@dimen/list_visible_scrollbar_padding
+ </dimen>
+ <dimen name="contact_browser_list_item_text_indent">8dip</dimen>
+ <!-- Width of a contact list item section header. -->
+ <dimen name="contact_list_section_header_width">56dp</dimen>
+
+ <!-- Size of the shortcut icon. 0dip means: use the system default -->
+ <dimen name="shortcut_icon_size">0dip</dimen>
+
+ <!-- Text size of shortcut icon overlay text -->
+ <dimen name="shortcut_overlay_text_size">12dp</dimen>
+
+ <!-- Extra vertical padding for darkened background behind shortcut icon overlay text -->
+ <dimen name="shortcut_overlay_text_background_padding">1dp</dimen>
+
+ <!-- Width of height of an icon from a third-party app in the networks section of the contact card. -->
+ <dimen name="detail_network_icon_size">32dip</dimen>
+
+ <!-- Empty message margins -->
+ <dimen name="empty_message_top_margin">48dip</dimen>
+
+ <!-- contact browser list margins -->
+ <dimen name="contact_browser_list_item_text_size">16sp</dimen>
+ <dimen name="contact_browser_list_item_photo_size">40dp</dimen>
+ <dimen name="contact_browser_list_item_gap_between_image_and_text">15dp</dimen>
+ <dimen name="contact_browser_list_top_margin">12dp</dimen>
+
+ <!-- Dimensions for "No contacts" string in PhoneFavoriteFragment for the All contacts
+ with phone numbers section
+ -->
+ <dimen name="contact_phone_list_empty_description_size">20sp</dimen>
+ <dimen name="contact_phone_list_empty_description_padding">10dip</dimen>
+
+ <!-- Dimensions for contact letter tiles -->
+ <dimen name="tile_letter_font_size">40dp</dimen>
+ <dimen name="tile_letter_font_size_small">20dp</dimen>
+ <dimen name="tile_divider_width">1dp</dimen>
+ <item name="letter_to_tile_ratio" type="dimen">67%</item>
+
+ <!-- Height of the floating action button -->
+ <dimen name="floating_action_button_height">56dp</dimen>
+ <!-- Width of the floating action button -->
+ <dimen name="floating_action_button_width">56dp</dimen>
+ <!-- Z translation of the floating action button -->
+ <dimen name="floating_action_button_translation_z">8dp</dimen>
+ <!-- Padding to be applied to the bottom of lists to make space for the floating action
+ button -->
+ <dimen name="floating_action_button_list_bottom_padding">88dp</dimen>
+ <!-- Right margin of the floating action button -->
+ <dimen name="floating_action_button_margin_right">16dp</dimen>
+ <!-- Bottom margin of the floating action button -->
+ <dimen name="floating_action_button_margin_bottom">16dp</dimen>
+
+ <!-- Height of the selection indicator of a tab. -->
+ <dimen name="tab_selected_underline_height">2dp</dimen>
+ <!-- Size of text in tabs. -->
+ <dimen name="tab_text_size">14sp</dimen>
+ <dimen name="tab_elevation">2dp</dimen>
+ <dimen name="tab_unread_count_background_size">16dp</dimen>
+ <dimen name="tab_unread_count_background_radius">2dp</dimen>
+ <dimen name="tab_unread_count_margin_left">0dp</dimen>
+ <dimen name="tab_unread_count_margin_top">2dp</dimen>
+ <dimen name="tab_unread_count_text_size">12sp</dimen>
+ <dimen name="tab_unread_count_text_padding">2dp</dimen>
+
+ <!-- Padding around the icon in the search box. -->
+ <dimen name="search_box_icon_margin">4dp</dimen>
+ <!-- Size of the icon (voice search, back arrow) in the search box. -->
+ <dimen name="search_box_icon_size">48dp</dimen>
+ <!-- Size of the close icon.-->
+ <dimen name="search_box_close_icon_size">56dp</dimen>
+ <!-- Padding around the close button. It's visible size without padding is 24dp. -->
+ <dimen name="search_box_close_icon_padding">16dp</dimen>
+ <!-- Padding around back arrow icon in the search box -->
+ <dimen name="search_box_navigation_icon_margin">14dp</dimen>
+ <!-- Left margin of the text field in the search box. -->
+ <dimen name="search_box_text_left_margin">15dp</dimen>
+ <!-- Search box text size -->
+ <dimen name="search_text_size">20sp</dimen>
+
+ <!-- Top margin for the Frequently Contacted section title -->
+ <dimen name="frequently_contacted_title_top_margin_when_first_row">16dp</dimen>
+ <!-- Top margin for the Frequently Contacted section title, when the title is the first
+ item in the list -->
+ <dimen name="frequently_contacted_title_top_margin">57dp</dimen>
+
+ <dimen name="frequently_contacted_title_text_size">24sp</dimen>
+
+ <!-- Size of icon for contacts number shortcuts -->
+ <dimen name="search_shortcut_radius">40dp</dimen>
+
+ <dimen name="contact_list_card_elevation">2dp</dimen>
+
+ <!-- Padding used around the periphery of the call subject dialog, as well as in between the
+ items. -->
+ <dimen name="call_subject_dialog_margin">20dp</dimen>
+ <!-- Padding used between lines of text in the call subject dialog. -->
+ <dimen name="call_subject_dialog_between_line_margin">8dp</dimen>
+ <!-- Size of the contact photo in the call subject dialog. -->
+ <dimen name="call_subject_dialog_contact_photo_size">50dp</dimen>
+ <!-- Margin above the edit text in the call subject dialog. -->
+ <dimen name="call_subject_dialog_edit_spacing">60dp</dimen>
+ <!-- Size of primary text in the call subject dialog. -->
+ <dimen name="call_subject_dialog_primary_text_size">16sp</dimen>
+ <!-- Size of secondary text in the call subject dialog. -->
+ <dimen name="call_subject_dialog_secondary_text_size">14sp</dimen>
+ <!-- Row padding for call subject history items. -->
+ <dimen name="call_subject_history_item_padding">15dp</dimen>
+</resources>
diff --git a/java/com/android/contacts/common/res/values/donottranslate_config.xml b/java/com/android/contacts/common/res/values/donottranslate_config.xml
new file mode 100644
index 000000000..324437928
--- /dev/null
+++ b/java/com/android/contacts/common/res/values/donottranslate_config.xml
@@ -0,0 +1,95 @@
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <!-- Flag indicating whether Contacts app is allowed to import contacts -->
+ <bool name="config_allow_import_from_vcf_file">true</bool>
+
+ <!-- If true, an option is shown in Display Options UI to choose a sort order -->
+ <bool name="config_sort_order_user_changeable">true</bool>
+
+ <!-- If true, the default sort order is primary (i.e. by given name) -->
+ <bool name="config_default_sort_order_primary">true</bool>
+
+ <!-- If true, an option is shown in Display Options UI to choose a name display order -->
+ <bool name="config_display_order_user_changeable">true</bool>
+
+ <!-- If true, the default sort order is primary (i.e. by given name) -->
+ <bool name="config_default_display_order_primary">true</bool>
+
+ <!-- If true, the order of name fields in the editor is primary (i.e. given name first) -->
+ <bool name="config_editor_field_order_primary">true</bool>
+
+ <!-- If true, an option is shown in Display Options UI to choose a default account -->
+ <bool name="config_default_account_user_changeable">true</bool>
+
+ <!-- Contacts preferences key for contact editor default account -->
+ <string name="contact_editor_default_account_key">ContactEditorUtils_default_account</string>
+
+ <!-- Contacts preferences key for contact editor anything saved -->
+ <string name="contact_editor_anything_saved_key">ContactEditorUtils_anything_saved</string>
+
+ <!-- The type of VCard for export. If you want to let the app emit vCard which is
+ specific to some vendor (like DoCoMo), specify this type (e.g. "docomo") -->
+ <string name="config_export_vcard_type" translatable="false">default</string>
+
+ <!-- The type of vcard for improt. If the vcard importer cannot guess the exact type
+ of a vCard type, the improter uses this type. -->
+ <string name="config_import_vcard_type" translatable="false">default</string>
+
+ <!-- Prefix of exported VCard file -->
+ <string name="config_export_file_prefix" translatable="false"></string>
+
+ <!-- Suffix of exported VCard file. Attached before an extension -->
+ <string name="config_export_file_suffix" translatable="false"></string>
+
+ <!-- Extension for exported VCard files -->
+ <string name="config_export_file_extension">vcf</string>
+
+ <!-- The filename that is suggested that users use when exporting vCards. Should include the .vcf extension. -->
+ <string name="exporting_vcard_filename" translatable="false">contacts.vcf</string>
+
+ <!-- Minimum number of exported VCard file index -->
+ <integer name="config_export_file_min_index">1</integer>
+
+ <!-- Maximum number of exported VCard file index -->
+ <integer name="config_export_file_max_index">99999</integer>
+
+ <!-- The list (separated by ',') of extensions should be checked in addition to
+ config_export_extension. e.g. If "aaa" is added to here and 00001.vcf and 00002.aaa
+ exist in a target directory, 00003.vcf becomes a next file name candidate.
+ Without this configuration, 00002.vcf becomes the candidate.-->
+ <string name="config_export_extensions_to_consider" translatable="false"></string>
+
+ <!-- If true, enable the "import contacts from SIM" feature if the device
+ has an appropriate SIM or ICC card.
+ Setting this flag to false in a resource overlay allows you to
+ entirely disable SIM import on a per-product basis. -->
+ <bool name="config_allow_sim_import">true</bool>
+
+ <!-- Flag indicating whether Contacts app is allowed to export contacts -->
+ <bool name="config_allow_export">true</bool>
+
+ <!-- Flag indicating whether Contacts app is allowed to share contacts with devices outside -->
+ <bool name="config_allow_share_contacts">true</bool>
+
+ <string name="pref_build_version_key">pref_build_version</string>
+ <string name="pref_open_source_licenses_key">pref_open_source_licenses</string>
+ <string name="pref_privacy_policy_key">pref_privacy_policy</string>
+ <string name="pref_terms_of_service_key">pref_terms_of_service</string>
+
+ <string name="star_sign">★</string>
+</resources>
diff --git a/java/com/android/contacts/common/res/values/ids.xml b/java/com/android/contacts/common/res/values/ids.xml
new file mode 100644
index 000000000..871f5a636
--- /dev/null
+++ b/java/com/android/contacts/common/res/values/ids.xml
@@ -0,0 +1,30 @@
+<!--
+ 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.
+ -->
+
+<resources>
+ <!-- For Debug Purpose -->
+ <item name="cliv_name_textview" type="id"/>
+ <item name="cliv_label_textview" type="id"/>
+ <item name="cliv_data_view" type="id"/>
+
+ <!-- For tag ids used by ContactPhotoManager to tag views with contact details -->
+ <item name="tag_display_name" type="id"/>
+ <item name="tag_identifier" type="id"/>
+ <item name="tag_contact_type" type="id"/>
+
+ <item name="contact_tile_image" type="id"/>
+ <item name="contact_tile_name" type="id"/>
+</resources>
diff --git a/java/com/android/contacts/common/res/values/integers.xml b/java/com/android/contacts/common/res/values/integers.xml
new file mode 100644
index 000000000..d38ad1da0
--- /dev/null
+++ b/java/com/android/contacts/common/res/values/integers.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<resources>
+
+ <!-- Determines the number of columns in a ContactTileRow in the favorites tab -->
+ <integer name="contact_tile_column_count_in_favorites">2</integer>
+ <integer name="contact_tile_column_count_in_favorites_new">3</integer>
+
+ <!-- The number of characters in the snippet before we need to tokenize and ellipse. -->
+ <integer name="snippet_length_before_tokenize">30</integer>
+
+ <!-- Layout weight of space elements in contact list view.
+ Default to 0 to indicate no padding-->
+ <integer name="contact_list_space_layout_weight">0</integer>
+ <!-- Layout weight of card in contact list view.
+ Default to 0 to indicate no padding -->
+ <integer name="contact_list_card_layout_weight">0</integer>
+
+ <!-- Duration of the animations on the call subject dialog. -->
+ <integer name="call_subject_animation_duration">250</integer>
+
+ <!-- A big number to make sure "About contacts" always showing at the bottom of Settings.-->
+ <integer name="about_contacts_order_number">100</integer>
+</resources>
diff --git a/java/com/android/contacts/common/res/values/strings.xml b/java/com/android/contacts/common/res/values/strings.xml
new file mode 100644
index 000000000..15e1f15d9
--- /dev/null
+++ b/java/com/android/contacts/common/res/values/strings.xml
@@ -0,0 +1,798 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Toast shown when text is copied to the clipboard [CHAR LIMIT=64] -->
+ <string name="toast_text_copied">Text copied</string>
+ <!-- Option displayed in context menu to copy long pressed item to clipboard [CHAR LIMIT=64] -->
+ <string name="copy_text">Copy to clipboard</string>
+
+ <!-- Action string for calling a custom phone number -->
+ <string name="call_custom">Call
+ <xliff:g id="custom">%s</xliff:g>
+ </string>
+ <!-- Action string for calling a home phone number -->
+ <string name="call_home">Call home</string>
+ <!-- Action string for calling a mobile phone number -->
+ <string name="call_mobile">Call mobile</string>
+ <!-- Action string for calling a work phone number -->
+ <string name="call_work">Call work</string>
+ <!-- Action string for calling a work fax phone number -->
+ <string name="call_fax_work">Call work fax</string>
+ <!-- Action string for calling a home fax phone number -->
+ <string name="call_fax_home">Call home fax</string>
+ <!-- Action string for calling a pager phone number -->
+ <string name="call_pager">Call pager</string>
+ <!-- Action string for calling an other phone number -->
+ <string name="call_other">Call</string>
+ <!-- Action string for calling a callback number -->
+ <string name="call_callback">Call callback</string>
+ <!-- Action string for calling a car phone number -->
+ <string name="call_car">Call car</string>
+ <!-- Action string for calling a company main phone number -->
+ <string name="call_company_main">Call company main</string>
+ <!-- Action string for calling a ISDN phone number -->
+ <string name="call_isdn">Call ISDN</string>
+ <!-- Action string for calling a main phone number -->
+ <string name="call_main">Call main</string>
+ <!-- Action string for calling an other fax phone number -->
+ <string name="call_other_fax">Call fax</string>
+ <!-- Action string for calling a radio phone number -->
+ <string name="call_radio">Call radio</string>
+ <!-- Action string for calling a Telex phone number -->
+ <string name="call_telex">Call telex</string>
+ <!-- Action string for calling a TTY/TDD phone number -->
+ <string name="call_tty_tdd">Call TTY/TDD</string>
+ <!-- Action string for calling a work mobile phone number -->
+ <string name="call_work_mobile">Call work mobile</string>
+ <!-- Action string for calling a work pager phone number -->
+ <string name="call_work_pager">Call work pager</string>
+ <!-- Action string for calling an assistant phone number -->
+ <string name="call_assistant">Call
+ <xliff:g id="assistant">%s</xliff:g>
+ </string>
+ <!-- Action string for calling a MMS phone number -->
+ <string name="call_mms">Call MMS</string>
+ <!-- Action string for calling a contact by shortcut -->
+ <string name="call_by_shortcut"><xliff:g id="contact_name">%s</xliff:g> (Call)</string>
+
+ <!-- Action string for sending an SMS to a custom phone number -->
+ <string name="sms_custom">Text
+ <xliff:g id="custom">%s</xliff:g>
+ </string>
+ <!-- Action string for sending an SMS to a home phone number -->
+ <string name="sms_home">Text home</string>
+ <!-- Action string for sending an SMS to a mobile phone number -->
+ <string name="sms_mobile">Text mobile</string>
+ <!-- Action string for sending an SMS to a work phone number -->
+ <string name="sms_work">Text work</string>
+ <!-- Action string for sending an SMS to a work fax phone number -->
+ <string name="sms_fax_work">Text work fax</string>
+ <!-- Action string for sending an SMS to a home fax phone number -->
+ <string name="sms_fax_home">Text home fax</string>
+ <!-- Action string for sending an SMS to a pager phone number -->
+ <string name="sms_pager">Text pager</string>
+ <!-- Action string for sending an SMS to an other phone number -->
+ <string name="sms_other">Text</string>
+ <!-- Action string for sending an SMS to a callback number -->
+ <string name="sms_callback">Text callback</string>
+ <!-- Action string for sending an SMS to a car phone number -->
+ <string name="sms_car">Text car</string>
+ <!-- Action string for sending an SMS to a company main phone number -->
+ <string name="sms_company_main">Text company main</string>
+ <!-- Action string for sending an SMS to a ISDN phone number -->
+ <string name="sms_isdn">Text ISDN</string>
+ <!-- Action string for sending an SMS to a main phone number -->
+ <string name="sms_main">Text main</string>
+ <!-- Action string for sending an SMS to an other fax phone number -->
+ <string name="sms_other_fax">Text fax</string>
+ <!-- Action string for sending an SMS to a radio phone number -->
+ <string name="sms_radio">Text radio</string>
+ <!-- Action string for sending an SMS to a Telex phone number -->
+ <string name="sms_telex">Text telex</string>
+ <!-- Action string for sending an SMS to a TTY/TDD phone number -->
+ <string name="sms_tty_tdd">Text TTY/TDD</string>
+ <!-- Action string for sending an SMS to a work mobile phone number -->
+ <string name="sms_work_mobile">Text work mobile</string>
+ <!-- Action string for sending an SMS to a work pager phone number -->
+ <string name="sms_work_pager">Text work pager</string>
+ <!-- Action string for sending an SMS to an assistant phone number -->
+ <string name="sms_assistant">Text
+ <xliff:g id="assistant">%s</xliff:g>
+ </string>
+ <!-- Action string for sending an SMS to a MMS phone number -->
+ <string name="sms_mms">Text MMS</string>
+ <!-- Action string for sending an SMS to a contact by shortcut -->
+ <string name="sms_by_shortcut"><xliff:g id="contact_name">%s</xliff:g> (Message)</string>
+
+ <!-- Title of the confirmation dialog for clearing frequents. [CHAR LIMIT=37] -->
+ <string name="clearFrequentsConfirmation_title">Clear frequently contacted?</string>
+
+ <!-- Confirmation dialog for clearing frequents. [CHAR LIMIT=NONE] -->
+ <string name="clearFrequentsConfirmation">You\'ll clear the frequently contacted list in the
+ Contacts and Phone apps, and force email apps to learn your addressing preferences from
+ scratch.
+ </string>
+
+ <!-- Title of the "Clearing frequently contacted" progress-dialog [CHAR LIMIT=35] -->
+ <string name="clearFrequentsProgress_title">Clearing frequently contacted\u2026</string>
+
+ <!-- Used to display as default status when the contact is available for chat [CHAR LIMIT=19] -->
+ <string name="status_available">Available</string>
+
+ <!-- Used to display as default status when the contact is away or idle for chat [CHAR LIMIT=19] -->
+ <string name="status_away">Away</string>
+
+ <!-- Used to display as default status when the contact is busy or Do not disturb for chat [CHAR LIMIT=19] -->
+ <string name="status_busy">Busy</string>
+
+ <!-- Directory partition name (also exists in contacts) -->
+ <string name="contactsList">Contacts</string>
+
+ <!-- The name of the invisible local contact directory -->
+ <string name="local_invisible_directory">Other</string>
+
+ <!-- The label in section header in the contact list for a contact directory [CHAR LIMIT=128] -->
+ <string name="directory_search_label">Directory</string>
+
+ <!-- The label in section header in the contact list for a work contact directory [CHAR LIMIT=128] -->
+ <string name="directory_search_label_work">Work directory</string>
+
+ <!-- The label in section header in the contact list for a local contacts [CHAR LIMIT=128] -->
+ <string name="local_search_label">All contacts</string>
+
+ <!-- String describing the text on the header of the profile contact in the contacts list
+ This may be programatically capitalized. [CHAR LIMIT=20] -->
+ <string msgid="9154761216179882405" name="user_profile_contacts_list_header">Me</string>
+
+ <!-- Title shown in the search result activity of contacts app while searching. [CHAR LIMIT=20]
+ (also in contacts) -->
+ <string name="search_results_searching">Searching\u2026</string>
+
+ <!-- Displayed at the top of search results indicating that more contacts were found than shown [CHAR LIMIT=64] -->
+ <string name="foundTooManyContacts">More than <xliff:g id="count">%d</xliff:g> found.</string>
+
+ <!-- Displayed at the top of the contacts showing the zero total number of contacts found when "Only contacts with phones" not selected. [CHAR LIMIT=30]
+ (also in contacts) -->
+ <string name="listFoundAllContactsZero">No contacts</string>
+
+ <!-- Displayed at the top of the contacts showing the total number of contacts found when typing search query -->
+ <plurals name="searchFoundContacts">
+ <item quantity="one">1 found</item>
+ <item quantity="other"><xliff:g id="count">%d</xliff:g> found</item>
+ </plurals>
+
+ <!-- String describing the text for photo of a contact in a contacts list.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_quick_contact_for">Quick contact for <xliff:g id="name">%1$s</xliff:g></string>
+
+ <!-- Shown as the display name for a person when the name is missing or unknown. [CHAR LIMIT=18]-->
+ <string name="missing_name">(No name)</string>
+
+ <!-- The text displayed on the divider for the Favorites tab in Phone app indicating that items below it are frequently called as opposed to starred contacts [CHAR LIMIT = 39] -->
+ <string name="favoritesFrequentCalled">Frequently called</string>
+
+ <!-- The text displayed on the divider for the Favorites tab in People app indicating that items below it are frequently contacted [CHAR LIMIT = 39] -->
+ <string name="favoritesFrequentContacted">Frequently contacted</string>
+
+ <!-- String describing a contact picture that introduces users to the contact detail screen.
+
+ Used by AccessibilityService to announce the purpose of the button.
+
+ [CHAR LIMIT=NONE]
+ -->
+ <string msgid="2795575601596468581" name="description_view_contact_detail">View contact</string>
+
+ <!-- Contact list filter selection indicating that the list shows all contacts with phone numbers [CHAR LIMIT=64] -->
+ <string name="list_filter_phones">All contacts with phone numbers</string>
+
+ <!-- Contact list filter selection indicating that the list shows all work contacts with phone numbers [CHAR LIMIT=64] -->
+ <string name="list_filter_phones_work">Work profile contacts</string>
+
+ <!-- Button to view the updates from the current group on the group detail page [CHAR LIMIT=25] -->
+ <string name="view_updates_from_group">View updates</string>
+
+ <!-- Title for data source when creating or editing a contact that doesn't
+ belong to a specific account. This contact will only exist on the device
+ and will not be synced. -->
+ <string name="account_phone">Device-only, unsynced</string>
+
+ <!-- Header that expands to list all name types when editing a structured name of a contact
+ [CHAR LIMIT=20] -->
+ <string name="nameLabelsGroup">Name</string>
+
+ <!-- Header that expands to list all nickname types when editing a nickname of a contact
+ [CHAR LIMIT=20] -->
+ <string name="nicknameLabelsGroup">Nickname</string>
+
+ <!-- Field title for the full name of a contact [CHAR LIMIT=64]-->
+ <string name="full_name">Name</string>
+ <!-- Field title for the given name of a contact -->
+ <string name="name_given">First name</string>
+ <!-- Field title for the family name of a contact -->
+ <string name="name_family">Last name</string>
+ <!-- Field title for the prefix name of a contact -->
+ <string name="name_prefix">Name prefix</string>
+ <!-- Field title for the middle name of a contact -->
+ <string name="name_middle">Middle name</string>
+ <!-- Field title for the suffix name of a contact -->
+ <string name="name_suffix">Name suffix</string>
+
+ <!-- Field title for the phonetic name of a contact [CHAR LIMIT=64]-->
+ <string name="name_phonetic">Phonetic name</string>
+
+ <!-- Field title for the phonetic given name of a contact -->
+ <string name="name_phonetic_given">Phonetic first name</string>
+ <!-- Field title for the phonetic middle name of a contact -->
+ <string name="name_phonetic_middle">Phonetic middle name</string>
+ <!-- Field title for the phonetic family name of a contact -->
+ <string name="name_phonetic_family">Phonetic last name</string>
+
+ <!-- Header that expands to list all of the types of phone numbers when editing or creating a
+ phone number for a contact [CHAR LIMIT=20] -->
+ <string name="phoneLabelsGroup">Phone</string>
+
+ <!-- Header that expands to list all of the types of email addresses when editing or creating
+ an email address for a contact [CHAR LIMIT=20] -->
+ <string name="emailLabelsGroup">Email</string>
+
+ <!-- Header that expands to list all of the types of postal addresses when editing or creating
+ an postal address for a contact [CHAR LIMIT=20] -->
+ <string name="postalLabelsGroup">Address</string>
+
+ <!-- Header that expands to list all of the types of IM account when editing or creating an IM
+ account for a contact [CHAR LIMIT=20] -->
+ <string name="imLabelsGroup">IM</string>
+
+ <!-- Header that expands to list all organization types when editing an organization of a
+ contact [CHAR LIMIT=20] -->
+ <string name="organizationLabelsGroup">Organization</string>
+
+ <!-- Header for the list of all relationships for a contact [CHAR LIMIT=20] -->
+ <string name="relationLabelsGroup">Relationship</string>
+
+ <!-- Header that expands to list all event types when editing an event of a contact
+ [CHAR LIMIT=20] -->
+ <string name="eventLabelsGroup">Special date</string>
+
+ <!-- Generic action string for text messaging a contact. Used by AccessibilityService to
+ announce the purpose of the view. [CHAR LIMIT=NONE] -->
+ <string name="sms">Text message</string>
+
+ <!-- Field title for the full postal address of a contact [CHAR LIMIT=64]-->
+ <string name="postal_address">Address</string>
+
+ <!-- Hint text for the organization name when editing -->
+ <string name="ghostData_company">Company</string>
+
+ <!-- Hint text for the organization title when editing -->
+ <string name="ghostData_title">Title</string>
+
+ <!-- The label describing the Notes field of a contact. This field allows free form text entry
+ about a contact -->
+ <string name="label_notes">Notes</string>
+
+ <!-- The label describing the SIP address field of a contact. [CHAR LIMIT=20] -->
+ <string name="label_sip_address">SIP</string>
+
+ <!-- Header that expands to list all website types when editing a website of a contact
+ [CHAR LIMIT=20] -->
+ <string name="websiteLabelsGroup">Website</string>
+
+ <!-- Header for the list of all groups for a contact [CHAR LIMIT=20] -->
+ <string name="groupsLabel">Groups</string>
+
+ <!-- Action string for sending an email to a home email address -->
+ <string name="email_home">Email home</string>
+ <!-- Action string for sending an email to a mobile email address -->
+ <string name="email_mobile">Email mobile</string>
+ <!-- Action string for sending an email to a work email address -->
+ <string name="email_work">Email work</string>
+ <!-- Action string for sending an email to an other email address -->
+ <string name="email_other">Email</string>
+ <!-- Action string for sending an email to a custom email address -->
+ <string name="email_custom">Email <xliff:g id="custom">%s</xliff:g></string>
+
+ <!-- Generic action string for sending an email -->
+ <string name="email">Email</string>
+
+ <!-- Field title for the street of a structured postal address of a contact -->
+ <string name="postal_street">Street</string>
+ <!-- Field title for the PO box of a structured postal address of a contact -->
+ <string name="postal_pobox">PO box</string>
+ <!-- Field title for the neighborhood of a structured postal address of a contact -->
+ <string name="postal_neighborhood">Neighborhood</string>
+ <!-- Field title for the city of a structured postal address of a contact -->
+ <string name="postal_city">City</string>
+ <!-- Field title for the region, or state, of a structured postal address of a contact -->
+ <string name="postal_region">State</string>
+ <!-- Field title for the postal code of a structured postal address of a contact -->
+ <string name="postal_postcode">ZIP code</string>
+ <!-- Field title for the country of a structured postal address of a contact -->
+ <string name="postal_country">Country</string>
+
+ <!-- Action string for viewing a home postal address -->
+ <string name="map_home">View home address</string>
+ <!-- Action string for viewing a work postal address -->
+ <string name="map_work">View work address</string>
+ <!-- Action string for viewing an other postal address -->
+ <string name="map_other">View address</string>
+ <!-- Action string for viewing a custom postal address -->
+ <string name="map_custom">View <xliff:g id="custom">%s</xliff:g> address</string>
+
+ <!-- Action string for starting an IM chat with the AIM protocol -->
+ <string name="chat_aim">Chat using AIM</string>
+ <!-- Action string for starting an IM chat with the MSN or Windows Live protocol -->
+ <string name="chat_msn">Chat using Windows Live</string>
+ <!-- Action string for starting an IM chat with the Yahoo protocol -->
+ <string name="chat_yahoo">Chat using Yahoo</string>
+ <!-- Action string for starting an IM chat with the Skype protocol -->
+ <string name="chat_skype">Chat using Skype</string>
+ <!-- Action string for starting an IM chat with the QQ protocol -->
+ <string name="chat_qq">Chat using QQ</string>
+ <!-- Action string for starting an IM chat with the Google Talk protocol -->
+ <string name="chat_gtalk">Chat using Google Talk</string>
+ <!-- Action string for starting an IM chat with the ICQ protocol -->
+ <string name="chat_icq">Chat using ICQ</string>
+ <!-- Action string for starting an IM chat with the Jabber protocol -->
+ <string name="chat_jabber">Chat using Jabber</string>
+
+ <!-- Generic action string for starting an IM chat -->
+ <string name="chat">Chat</string>
+
+ <!-- String describing the Contact Editor Minus button
+
+ Used by AccessibilityService to announce the purpose of the button.
+
+ [CHAR LIMIT=NONE]
+ -->
+ <string name="description_minus_button">delete</string>
+
+ <!-- Content description for the expand or collapse name fields button.
+ Clicking this button causes the name editor to toggle between showing
+ a single field where the entire name is edited at once, or multiple
+ fields corresponding to each part of the name (Name Prefix, First Name,
+ Middle Name, Last Name, Name Suffix).
+ [CHAR LIMIT=NONE] -->
+ <string name="expand_collapse_name_fields_description">Expand or collapse name fields</string>
+
+ <!-- Content description for the expand or collapse phonetic name fields button. [CHAR LIMIT=100] -->
+ <string name="expand_collapse_phonetic_name_fields_description">Expand or collapse phonetic
+ name fields</string>
+
+ <!-- Contact list filter label indicating that the list is showing all available accounts [CHAR LIMIT=64] -->
+ <string name="list_filter_all_accounts">All contacts</string>
+
+ <!-- Menu item to indicate you are done editing a contact and want to save the changes you've made -->
+ <string name="menu_done">Done</string>
+
+ <!-- Menu item to indicate you want to cancel the current editing process and NOT save the changes you've made [CHAR LIMIT=12] -->
+ <string name="menu_doNotSave">Cancel</string>
+
+ <!-- Displayed at the top of the contacts showing the account filter selected [CHAR LIMIT=64] -->
+ <string name="listAllContactsInAccount">Contacts in <xliff:g example="abc@gmail.com" id="name">%s</xliff:g></string>
+
+ <!-- Displayed at the top of the contacts showing single contact. [CHAR LIMIT=64] -->
+ <string name="listCustomView">Contacts in custom view</string>
+
+ <!-- Displayed at the top of the contacts showing single contact. [CHAR LIMIT=64] -->
+ <string name="listSingleContact">Single contact</string>
+
+ <!-- Message asking user to select an account to save contacts imported from vcard or SIM card [CHAR LIMIT=64] -->
+ <string name="dialog_new_contact_account">Save imported contacts to:</string>
+
+ <!-- Action string for selecting SIM for importing contacts -->
+ <string name="import_from_sim">Import from SIM card</string>
+
+ <!-- Action string for selecting a SIM subscription for importing contacts -->
+ <string name="import_from_sim_summary">Import from SIM <xliff:g id="sim_name">^1</xliff:g> - <xliff:g id="sim_number">^2</xliff:g></string>
+
+ <!-- Action string for selecting a SIM subscription for importing contacts, without a phone number -->
+ <string name="import_from_sim_summary_no_number">Import from SIM <xliff:g id="sim_name">%1$s</xliff:g></string>
+
+ <!-- Action string for selecting a .vcf file to import contacts from [CHAR LIMIT=30] -->
+ <string name="import_from_vcf_file" product="default">Import from .vcf file</string>
+
+ <!-- Message shown in a Dialog confirming a user's cancel request toward existing vCard import.
+ The argument is file name for the vCard import the user wants to cancel.
+ [CHAR LIMIT=128] -->
+ <string name="cancel_import_confirmation_message">Cancel import of <xliff:g example="import.vcf" id="filename">%s</xliff:g>?</string>
+
+ <!-- Message shown in a Dialog confirming a user's cancel request toward existing vCard export.
+ The argument is file name for the vCard export the user wants to cancel.
+ [CHAR LIMIT=128] -->
+ <string name="cancel_export_confirmation_message">Cancel export of <xliff:g example="export.vcf" id="filename">%s</xliff:g>?</string>
+
+ <!-- Title shown in a Dialog telling users cancel vCard import/export operation is failed. [CHAR LIMIT=40] -->
+ <string name="cancel_vcard_import_or_export_failed">Couldn\'t cancel vCard import/export</string>
+
+ <!-- The failed reason which should not be shown but it may in some buggy condition. [CHAR LIMIT=40] -->
+ <string name="fail_reason_unknown">Unknown error.</string>
+
+ <!-- The failed reason shown when vCard importer/exporter could not open the file
+ specified by a user. The file name should be in the message. [CHAR LIMIT=NONE] -->
+ <string name="fail_reason_could_not_open_file">Couldn\'t open \"<xliff:g id="file_name">%s</xliff:g>\": <xliff:g id="exact_reason">%s</xliff:g>.</string>
+
+ <!-- The failed reason shown when contacts exporter fails to be initialized.
+ Some exact reason must follow this. [CHAR LIMIT=NONE]-->
+ <string name="fail_reason_could_not_initialize_exporter">Couldn\'t start the exporter: \"<xliff:g id="exact_reason">%s</xliff:g>\".</string>
+
+ <!-- The failed reason shown when there's no contact which is allowed to be exported.
+ Note that user may have contacts data but all of them are probably not allowed to be
+ exported because of security/permission reasons. [CHAR LIMIT=NONE] -->
+ <string name="fail_reason_no_exportable_contact">There is no exportable contact.</string>
+
+ <!-- The user doesn't have all permissions required to use the current screen. So
+ close the current screen and show the user this message. -->
+ <string name="missing_required_permission">You have disabled a required permission.</string>
+
+ <!-- The failed reason shown when some error happend during contacts export.
+ Some exact reason must follow this. [CHAR LIMIT=NONE] -->
+ <string name="fail_reason_error_occurred_during_export">An error occurred during export: \"<xliff:g id="exact_reason">%s</xliff:g>\".</string>
+
+ <!-- The failed reason shown when the given file name is too long for the system.
+ The length limit of each file is different in each Android device, so we don't need to
+ mention it here. [CHAR LIMIT=NONE] -->
+ <string name="fail_reason_too_long_filename">Required filename is too long (\"<xliff:g id="filename">%s</xliff:g>\").</string>
+
+ <!-- The failed reason shown when Contacts app (especially vCard importer/exporter)
+ emitted some I/O error. Exact reason will be appended by the system. [CHAR LIMIT=NONE] -->
+ <string name="fail_reason_io_error">I/O error</string>
+
+ <!-- Failure reason show when Contacts app (especially vCard importer) encountered
+ low memory problem and could not proceed its import procedure. [CHAR LIMIT=NONE] -->
+ <string name="fail_reason_low_memory_during_import">Not enough memory. The file may be too large.</string>
+
+ <!-- The failed reason shown when vCard parser was not able to be parsed by the current vCard
+ implementation. This might happen even when the input vCard is completely valid, though
+ we believe it is rather rare in the actual world. [CHAR LIMIT=NONE] -->
+ <string name="fail_reason_vcard_parse_error">Couldn\'t parse vCard for an unexpected reason.</string>
+
+ <!-- The failed reason shown when vCard importer doesn't support the format.
+ This may be shown when the vCard is corrupted [CHAR LIMIT=40] -->
+ <string name="fail_reason_not_supported">The format isn\'t supported.</string>
+
+ <!-- Fail reason shown when vCard importer failed to look over meta information stored in vCard file(s). -->
+ <string name="fail_reason_failed_to_collect_vcard_meta_info">Couldn\'t collect meta information of given vCard file(s).</string>
+
+ <!-- The failed reason shown when the import of some of vCard files failed during multiple vCard
+ files import. It includes the case where all files were failed to be imported. -->
+ <string name="fail_reason_failed_to_read_files">One or more files couldn\'t be imported (%s).</string>
+
+ <!-- The title shown when exporting vCard is successfuly finished [CHAR LIMIT=40] -->
+ <string name="exporting_vcard_finished_title">Finished exporting <xliff:g example="export.vcf" id="filename">%s</xliff:g>.</string>
+
+ <!-- The title shown when exporting vCard has finished successfully but the destination filename could not be resolved. [CHAR LIMIT=NONE] -->
+ <string name="exporting_vcard_finished_title_fallback">Finished exporting contacts.</string>
+
+ <!-- The toast message shown when exporting vCard has finished and vCards are ready to be shared [CHAR LIMIT=150]-->
+ <string name="exporting_vcard_finished_toast">Finished exporting contacts, click the notification to share contacts.</string>
+
+ <!-- The message on notification shown when exporting vCard has finished and vCards are ready to be shared [CHAR LIMIT=60]-->
+ <string name="touch_to_share_contacts">Tap to share contacts.</string>
+
+ <!-- The title shown when exporting vCard is canceled (probably by a user)
+ The argument is file name the user canceled importing.
+ [CHAR LIMIT=40] -->
+ <string name="exporting_vcard_canceled_title">Exporting <xliff:g example="export.vcf" id="filename">%s</xliff:g> canceled.</string>
+
+ <!-- Dialog title shown when the application is exporting contact data outside. [CHAR LIMIT=NONE] -->
+ <string name="exporting_contact_list_title">Exporting contact data</string>
+
+ <!-- Message shown when the application is exporting contact data outside -->
+ <string name="exporting_contact_list_message">Contact data is being exported.</string>
+
+ <!-- The error reason the vCard composer "may" emit when database is corrupted or
+ something is going wrong. Usually users should not see this text. [CHAR LIMIT=NONE] -->
+ <string name="composer_failed_to_get_database_infomation">Couldn\'t get database information.</string>
+
+ <!-- This error message shown when the user actually have no contact
+ (e.g. just after data-wiping), or, data providers of the contact list prohibits their
+ contacts from being exported to outside world via vcard exporter, etc. [CHAR LIMIT=NONE] -->
+ <string name="composer_has_no_exportable_contact">There are no exportable contacts. If you do have contacts on your device, some data providers may not allow the contacts to be exported from the device.</string>
+
+ <!-- The error reason the vCard composer may emit when vCard composer is not initialized
+ even when needed.
+ Users should not usually see this error message. [CHAR LIMIT=NONE] -->
+ <string name="composer_not_initialized">The vCard composer didn\'t start properly.</string>
+
+ <!-- Dialog title shown when exporting Contact data failed. [CHAR LIMIT=20] -->
+ <string name="exporting_contact_failed_title">Couldn\'t export</string>
+
+ <!-- Dialog message shown when exporting Contact data failed. [CHAR LIMIT=NONE] -->
+ <string name="exporting_contact_failed_message">The contact data wasn\'t exported.\nReason: \"<xliff:g id="fail_reason">%s</xliff:g>\"</string>
+
+ <!-- Description shown when importing vCard data.
+ The argument is the name of a contact which is being read.
+ [CHAR LIMIT=20] -->
+ <string name="importing_vcard_description">Importing <xliff:g example="Joe Due" id="name">%s</xliff:g></string>
+
+ <!-- Dialog title shown when reading vCard data failed [CHAR LIMIT=40] -->
+ <string name="reading_vcard_failed_title">Couldn\'t read vCard data</string>
+
+ <!-- The title shown when reading vCard is canceled (probably by a user)
+ [CHAR LIMIT=40] -->
+ <string name="reading_vcard_canceled_title">Reading vCard data canceled</string>
+
+ <!-- The title shown when reading vCard finished
+ The argument is file name the user imported.
+ [CHAR LIMIT=40] -->
+ <string name="importing_vcard_finished_title">Finished importing vCard <xliff:g example="import.vcf" id="filename">%s</xliff:g></string>
+
+ <!-- The title shown when importing vCard is canceled (probably by a user)
+ The argument is file name the user canceled importing.
+ [CHAR LIMIT=40] -->
+ <string name="importing_vcard_canceled_title">Importing <xliff:g example="import.vcf" id="filename">%s</xliff:g> canceled</string>
+
+ <!-- The message shown when vCard import request is accepted. The system may start that work soon, or do it later
+ when there are already other import/export requests.
+ The argument is file name the user imported.
+ [CHAR LIMIT=40] -->
+ <string name="vcard_import_will_start_message"><xliff:g example="import.vcf" id="filename">%s</xliff:g> will be imported shortly.</string>
+ <!-- The message shown when vCard import request is accepted. The system may start that work soon, or do it later when there are already other import/export requests.
+ "The file" is what a user selected for importing.
+ [CHAR LIMIT=40] -->
+ <string name="vcard_import_will_start_message_with_default_name">The file will be imported shortly.</string>
+ <!-- The message shown when a given vCard import request is rejected by the system. [CHAR LIMIT=NONE] -->
+ <string name="vcard_import_request_rejected_message">vCard import request was rejected. Try again later.</string>
+ <!-- The message shown when vCard export request is accepted. The system may start that work soon, or do it later
+ when there are already other import/export requests.
+ The argument is file name the user exported.
+ [CHAR LIMIT=40] -->
+ <string name="vcard_export_will_start_message"><xliff:g example="import.vcf" id="filename">%s</xliff:g> will be exported shortly.</string>
+
+ <!-- The message shown when a vCard export request is accepted but the destination filename could not be resolved. [CHAR LIMIT=NONE] -->
+ <string name="vcard_export_will_start_message_fallback">The file will be exported shortly.</string>
+
+ <!-- The message shown when a vCard export request is accepted and contacts will be exported shortly. [CHAR LIMIT=70]-->
+ <string name="contacts_export_will_start_message">Contacts will be exported shortly.</string>
+
+ <!-- The message shown when a given vCard export request is rejected by the system. [CHAR LIMIT=NONE] -->
+ <string name="vcard_export_request_rejected_message">vCard export request was rejected. Try again later.</string>
+ <!-- Used when file name is unknown in vCard processing. It typically happens
+ when the file is given outside the Contacts app. [CHAR LIMIT=30] -->
+ <string name="vcard_unknown_filename">contact</string>
+
+ <!-- The message shown when vCard importer is caching files to be imported into local temporary
+ data storage. [CHAR LIMIT=NONE] -->
+ <string name="caching_vcard_message">Caching vCard(s) to local temporary storage. The actual import will start soon.</string>
+
+ <!-- Message used when vCard import has failed. [CHAR LIMIT=40] -->
+ <string name="vcard_import_failed">Couldn\'t import vCard.</string>
+
+ <!-- The "file name" displayed for vCards received directly via NFC [CHAR LIMIT=16] -->
+ <string name="nfc_vcard_file_name">Contact received over NFC</string>
+
+ <!-- Dialog title shown when a user confirms whether he/she export Contact data. [CHAR LIMIT=32] -->
+ <string name="confirm_export_title">Export contacts?</string>
+
+ <!-- The title shown when vCard importer is caching files to be imported into local temporary
+ data storage. [CHAR LIMIT=40] -->
+ <string name="caching_vcard_title">Caching</string>
+
+ <!-- The message shown while importing vCard(s).
+ First argument is current index of contacts to be imported.
+ Second argument is the total number of contacts.
+ Third argument is the name of a contact which is being read.
+ [CHAR LIMIT=20] -->
+ <string name="progress_notifier_message">Importing <xliff:g id="current_number">%s</xliff:g>/<xliff:g id="total_number">%s</xliff:g>: <xliff:g example="Joe Due" id="name">%s</xliff:g></string>
+
+ <!-- Action that exports all contacts to a user selected destination. [CHAR LIMIT=25] -->
+ <string name="export_to_vcf_file" product="default">Export to .vcf file</string>
+
+ <!-- Contact preferences related strings -->
+
+ <!-- Label of the "sort by" display option -->
+ <string name="display_options_sort_list_by">Sort by</string>
+
+ <!-- An allowable value for the "sort list by" contact display option -->
+ <string name="display_options_sort_by_given_name">First name</string>
+
+ <!-- An allowable value for the "sort list by" contact display option -->
+ <string name="display_options_sort_by_family_name">Last name</string>
+
+ <!-- Label of the "name format" display option [CHAR LIMIT=64]-->
+ <string name="display_options_view_names_as">Name format</string>
+
+ <!-- An allowable value for the "view names as" contact display option -->
+ <string name="display_options_view_given_name_first">First name first</string>
+
+ <!-- An allowable value for the "view names as" contact display option -->
+ <string name="display_options_view_family_name_first">Last name first</string>
+
+ <!--Label of the "default account" setting option to set default editor account. [CHAR LIMIT=80]-->
+ <string name="default_editor_account">Default account for new contacts</string>
+
+ <!--Label of the "Sync contact metadata" setting option to set sync account for Lychee. [CHAR LIMIT=80]-->
+ <string name="sync_contact_metadata_title">Sync contact metadata [DOGFOOD]</string>
+
+ <!--Label of the "Sync contact metadata" setting dialog to set sync account for Lychee. [CHAR LIMIT=80]-->
+ <string name="sync_contact_metadata_dialog_title">Sync contact metadata</string>
+
+ <!-- Label of the "About" setting -->
+ <string name="setting_about">About Contacts</string>
+
+ <!-- Title of the settings activity [CHAR LIMIT=64] -->
+ <string name="activity_title_settings">Settings</string>
+
+ <!-- Action that shares visible contacts -->
+ <string name="share_visible_contacts">Share visible contacts</string>
+
+ <!-- A framework exception (ie, transaction too large) can be thrown while attempting to share all visible contacts. If so, show this toast. -->
+ <string name="share_visible_contacts_failure">Failed to share visible contacts.</string>
+
+ <!-- Action that shares favorite contacts [CHAR LIMIT=40]-->
+ <string name="share_favorite_contacts">Share favorite contacts</string>
+
+ <!-- Action that shares contacts [CHAR LIMIT=30]-->
+ <string name="share_contacts">Share all contacts</string>
+
+ <!-- A framework exception can be thrown while attempting to share all contacts. If so, show this toast. [CHAR LIMIT=40]-->
+ <string name="share_contacts_failure">Failed to share contacts.</string>
+
+ <!-- Dialog title when selecting the bulk operation to perform from a list. [CHAR LIMIT=36] -->
+ <string name="dialog_import_export">Import/export contacts</string>
+
+ <!-- Dialog title when importing contacts from an external source. [CHAR LIMIT=36] -->
+ <string name="dialog_import">Import contacts</string>
+
+ <!-- Toast indicating that sharing a contact has failed. [CHAR LIMIT=NONE] -->
+ <string name="share_error">This contact can\'t be shared.</string>
+
+ <!-- Toast indicating that no visible contact to share [CHAR LIMIT=NONE] -->
+ <string name="no_contact_to_share">There are no contacts to share.</string>
+
+ <!-- Menu item to search contacts -->
+ <string name="menu_search">Search</string>
+
+ <!-- Query hint displayed inside the search field [CHAR LIMIT=64] -->
+ <string name="hint_findContacts">Find contacts</string>
+
+ <!-- The description text for the favorites tab.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+
+ [CHAR LIMIT=NONE] -->
+ <string name="contactsFavoritesLabel">Favorites</string>
+
+ <!-- Displayed at the top of the contacts showing the zero total number of contacts visible when "All contacts" is selected [CHAR LIMIT=64]-->
+ <string name="listTotalAllContactsZero">No contacts.</string>
+
+ <!-- Displayed at the top of the contacts showing the zero total number of contacts visible when "Custom" is selected [CHAR LIMIT=64]-->
+ <string name="listTotalAllContactsZeroCustom">No visible contacts.</string>
+
+ <!-- Displayed at the top of the contacts showing the zero total number of contacts visible when starred contact list is selected [CHAR LIMIT=64]-->
+ <string name="listTotalAllContactsZeroStarred">No favorites</string>
+
+ <!-- Displayed at the top of the contacts showing the zero total number of contacts visible when a group or account is selected [CHAR LIMIT=64]-->
+ <string name="listTotalAllContactsZeroGroup">No contacts in <xliff:g example="Friends" id="name">%s</xliff:g></string>
+
+ <!-- The menu item to clear frequents [CHAR LIMIT=30] -->
+ <string name="menu_clear_frequents">Clear frequents</string>
+
+ <!-- Menu item to select SIM card -->
+ <string name="menu_select_sim">Select SIM card</string>
+
+ <!-- The menu item to open the list of accounts. [CHAR LIMIT=60]-->
+ <string name="menu_accounts">Manage accounts</string>
+
+ <!-- The menu item to bulk import or bulk export contacts from SIM card or SD card. [CHAR LIMIT=30]-->
+ <string name="menu_import_export">Import/export</string>
+
+ <!-- The font-family to use for tab text. -->
+ <string name="tab_font_family" translatable="false">sans-serif</string>
+
+ <!-- Attribution of a contact status update, when the time of update is unknown -->
+ <string name="contact_status_update_attribution">via <xliff:g example="Google Talk" id="source">%1$s</xliff:g></string>
+
+ <!-- Attribution of a contact status update, when the time of update is known -->
+ <string name="contact_status_update_attribution_with_date"><xliff:g example="3 hours ago" id="date">%1$s</xliff:g> via <xliff:g example="Google Talk" id="source">%2$s</xliff:g></string>
+
+ <!-- Font family used when drawing letters for letter tile avatars. -->
+ <string name="letter_tile_letter_font_family" translatable="false">sans-serif-medium</string>
+
+ <!-- Content description for the fake action menu up button as used
+ inside search. [CHAR LIMIT=NONE] -->
+ <string name="action_menu_back_from_search">stop searching</string>
+
+ <!-- String describing the icon used to clear the search field -->
+ <string name="description_clear_search">Clear search</string>
+
+ <!-- The font-family to use for the text inside the searchbox. -->
+ <string name="search_font_family" translatable="false">sans-serif</string>
+
+ <!-- The title of the preference section that allows users to configure how they want their
+ contacts to be displayed. [CHAR LIMIT=128] -->
+ <string name="settings_contact_display_options_title">Contact display options</string>
+
+ <!-- Title for Select Account Dialog [CHAR LIMIT=30] -->
+ <string name="select_account_dialog_title">Account</string>
+
+ <!-- Label for the check box to toggle default sim card setting [CHAR LIMIT=35]-->
+ <string name="set_default_account">Always use this for calls</string>
+
+ <!-- Title for dialog to select Phone Account for outgoing call. [CHAR LIMIT=40] -->
+ <string name="select_phone_account_for_calls">Call with</string>
+
+ <!-- String used for actions in the dialer call log and the quick contact card to initiate
+ a call to an individual. The user is prompted to enter a note which is sent along with
+ the call (e.g. a call subject). [CHAR LIMIT=40] -->
+ <string name="call_with_a_note">Call with a note</string>
+
+ <!-- Hint text shown in the call subject dialog. [CHAR LIMIT=255] -->
+ <string name="call_subject_hint">Type a note to send with call ...</string>
+
+ <!-- Button used to start a new call with the user entered subject. [CHAR LIMIT=32] -->
+ <string name="send_and_call_button">SEND &amp; CALL</string>
+
+ <!-- String used to represent the total number of characters entered for a call subject,
+ compared to the character limit. Example: 2 / 64 -->
+ <string name="call_subject_limit"><xliff:g example="4" id="count">%1$s</xliff:g> / <xliff:g example="64" id="limit">%2$s</xliff:g></string>
+
+ <!-- String used to build a phone number bype and phone number string.
+ Example: Mobile • 650-555-1212 -->
+ <string name="call_subject_type_and_number"><xliff:g example="Mobile" id="type">%1$s</xliff:g> • <xliff:g example="(650) 555-1212" id="number">%2$s</xliff:g></string>
+
+ <!-- String format to describe a tab e.g.call history tab. -->
+ <string name="tab_title"><xliff:g id="title">%1$s</xliff:g> tab.</string>
+
+ <!-- String format to describe the number of unread items in a tab.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <plurals name="tab_title_with_unread_items">
+ <item quantity="one">
+ <xliff:g id="title">%1$s</xliff:g> tab. <xliff:g id="count">%2$d</xliff:g> unread item.
+ </item>
+ <item quantity="other">
+ <xliff:g id="title">%1$s</xliff:g> tab. <xliff:g id="count">%2$d</xliff:g> unread items.
+ </item>
+ </plurals>
+
+ <!-- Build version title in About preference. [CHAR LIMIT=40]-->
+ <string name="about_build_version">Build version</string>
+
+ <!-- Open source licenses title in About preference. [CHAR LIMIT=60] -->
+ <string name="about_open_source_licenses">Open source licenses</string>
+
+ <!-- Open source licenses summary in About preference. [CHAR LIMIT=NONE] -->
+ <string name="about_open_source_licenses_summary">License details for open source software</string>
+
+ <!-- Privacy policy title in About preference. [CHAR LIMIT=40]-->
+ <string name="about_privacy_policy">Privacy policy</string>
+
+ <!-- Terms of service title in about preference. [CHAR LIMIT=60]-->
+ <string name="about_terms_of_service">Terms of service</string>
+
+ <!-- Title for the activity that displays licenses for open source libraries. [CHAR LIMIT=100]-->
+ <string name="activity_title_licenses">Open source licenses</string>
+
+ <!-- Toast message showing when failed to open the url. [CHAR LIMIT=100]-->
+ <string name="url_open_error_toast">Failed to open the url.</string>
+
+ <!-- Description string for an action button to initiate a video call from search results.
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+
+ [CHAR LIMIT=NONE]-->
+ <string name="description_search_video_call">Place video call</string>
+</resources>
diff --git a/java/com/android/contacts/common/res/values/styles.xml b/java/com/android/contacts/common/res/values/styles.xml
new file mode 100644
index 000000000..07d4a0225
--- /dev/null
+++ b/java/com/android/contacts/common/res/values/styles.xml
@@ -0,0 +1,97 @@
+<!--
+ ~ 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
+ -->
+
+<resources>
+
+ <style name="DirectoryHeader">
+ <item name="android:background">@android:color/transparent</item>
+ </style>
+
+ <style name="SectionHeaderStyle" parent="@android:style/TextAppearance.Large">
+ <item name="android:textSize">16sp</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:textColor">@color/section_header_text_color</item>
+ <item name="android:textStyle">bold</item>
+ </style>
+
+ <style name="DirectoryHeaderStyle" parent="@android:style/TextAppearance.Small">
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">@color/dialer_secondary_text_color</item>
+ <item name="android:fontFamily">sans-serif-medium</item>
+ </style>
+
+ <!-- TextView style used for headers.
+
+This is similar to ?android:attr/listSeparatorTextView but uses different
+background and text color. See also android:style/Widget.Holo.TextView.ListSeparator
+(which is private, so we cannot specify it as a parent style). -->
+ <style name="ContactListSeparatorTextViewStyle">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <!-- See comments for @dimen/list_section_divider_min_height -->
+ <item name="android:minHeight">@dimen/list_section_divider_min_height</item>
+ <item name="android:background">@drawable/list_section_divider_holo_custom</item>
+ <item name="android:textAppearance">@style/DirectoryHeaderStyle</item>
+ <item name="android:gravity">center_vertical</item>
+ <item name="android:paddingLeft">8dip</item>
+ <item name="android:paddingStart">8dip</item>
+ <item name="android:paddingTop">4dip</item>
+ <item name="android:paddingBottom">4dip</item>
+ <item name="android:ellipsize">end</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:textAllCaps">true</item>
+ </style>
+
+ <style name="TextAppearanceMedium" parent="@android:style/TextAppearance.Medium">
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">#000000</item>
+ </style>
+
+ <style name="TextAppearanceSmall" parent="@android:style/TextAppearance.Small">
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">#737373</item>
+ </style>
+
+ <style name="ListViewStyle" parent="@android:style/Widget.Material.Light.ListView">
+ <item name="android:overScrollMode">always</item>
+ </style>
+
+ <style name="BackgroundOnlyTheme" parent="@android:style/Theme.Material.Light">
+ <item name="android:windowBackground">@null</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowAnimationStyle">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <!-- Activities that use this theme are background activities without obvious displays.
+ However, some also have dialogs. Therefore, it doesn't make sense to set this true.-->
+ <item name="android:windowNoDisplay">false</item>
+ <item name="android:windowIsFloating">true</item>
+ </style>
+
+ <style name="Theme.CallSubjectDialogTheme" parent="@android:style/Theme.Material.Light.Dialog">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">match_parent</item>
+
+ <!-- No backgrounds, titles or window float -->
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowFullscreen">false</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowDrawsSystemBarBackgrounds">false</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowElevation">0dp</item>
+ </style>
+</resources>
diff --git a/java/com/android/contacts/common/testing/InjectedServices.java b/java/com/android/contacts/common/testing/InjectedServices.java
new file mode 100644
index 000000000..5ab5e5feb
--- /dev/null
+++ b/java/com/android/contacts/common/testing/InjectedServices.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 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.testing;
+
+import android.content.ContentResolver;
+import android.content.SharedPreferences;
+import android.util.ArrayMap;
+import java.util.Map;
+
+/**
+ * A mechanism for providing alternative (mock) services to the application while running tests.
+ * Activities, Services and the Application should check with this class to see if a particular
+ * service has been overridden.
+ */
+public class InjectedServices {
+
+ private ContentResolver mContentResolver;
+ private SharedPreferences mSharedPreferences;
+ private Map<String, Object> mSystemServices;
+
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ public void setContentResolver(ContentResolver contentResolver) {
+ this.mContentResolver = contentResolver;
+ }
+
+ public SharedPreferences getSharedPreferences() {
+ return mSharedPreferences;
+ }
+
+ public void setSharedPreferences(SharedPreferences sharedPreferences) {
+ this.mSharedPreferences = sharedPreferences;
+ }
+
+ public void setSystemService(String name, Object service) {
+ if (mSystemServices == null) {
+ mSystemServices = new ArrayMap<>();
+ }
+
+ mSystemServices.put(name, service);
+ }
+
+ public Object getSystemService(String name) {
+ if (mSystemServices != null) {
+ return mSystemServices.get(name);
+ }
+ return null;
+ }
+}
diff --git a/java/com/android/contacts/common/util/AccountFilterUtil.java b/java/com/android/contacts/common/util/AccountFilterUtil.java
new file mode 100644
index 000000000..18743c65e
--- /dev/null
+++ b/java/com/android/contacts/common/util/AccountFilterUtil.java
@@ -0,0 +1,125 @@
+/*
+ * 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.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.
+ */
+ public static boolean updateAccountFilterTitleForPhone(
+ View filterContainer, ContactListFilter filter, boolean showTitleForAllAccounts) {
+ return updateAccountFilterTitle(filterContainer, filter, showTitleForAllAccounts, true);
+ }
+
+ private static boolean updateAccountFilterTitle(
+ View filterContainer,
+ ContactListFilter filter,
+ boolean showTitleForAllAccounts,
+ boolean forPhone) {
+ final Context context = filterContainer.getContext();
+ final TextView headerTextView =
+ (TextView) filterContainer.findViewById(R.id.account_filter_header);
+
+ boolean textWasSet = false;
+ if (filter != null) {
+ if (forPhone) {
+ if (filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
+ if (showTitleForAllAccounts) {
+ headerTextView.setText(R.string.list_filter_phones);
+ textWasSet = true;
+ }
+ } else if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+ headerTextView.setText(
+ context.getString(R.string.listAllContactsInAccount, filter.accountName));
+ textWasSet = true;
+ } 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) {
+ if (showTitleForAllAccounts) {
+ headerTextView.setText(R.string.list_filter_all_accounts);
+ textWasSet = true;
+ }
+ } else if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+ headerTextView.setText(
+ context.getString(R.string.listAllContactsInAccount, filter.accountName));
+ textWasSet = true;
+ } else if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
+ headerTextView.setText(R.string.listCustomView);
+ textWasSet = true;
+ } 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
new file mode 100644
index 000000000..20f916a3f
--- /dev/null
+++ b/java/com/android/contacts/common/util/BitmapUtil.java
@@ -0,0 +1,167 @@
+/*
+ * 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.util;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+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 {
+
+ private BitmapUtil() {}
+
+ /**
+ * Returns Width or Height of the picture, depending on which size is smaller. Doesn't actually
+ * decode the picture, so it is pretty efficient to run.
+ */
+ public static int getSmallerExtentFromBytes(byte[] bytes) {
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+
+ // don't actually decode the picture, just return its bounds
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
+
+ // test what the best sample size is
+ return Math.min(options.outWidth, options.outHeight);
+ }
+
+ /**
+ * Finds the optimal sampleSize for loading the picture
+ *
+ * @param originalSmallerExtent Width or height of the picture, whichever is smaller
+ * @param targetExtent Width or height of the target view, whichever is bigger.
+ * <p>If either one of the parameters is 0 or smaller, no sampling is applied
+ */
+ public static int findOptimalSampleSize(int originalSmallerExtent, int targetExtent) {
+ // If we don't know sizes, we can't do sampling.
+ if (targetExtent < 1) {
+ return 1;
+ }
+ if (originalSmallerExtent < 1) {
+ return 1;
+ }
+
+ // Test what the best sample size is. To do that, we find the sample size that gives us
+ // the best trade-off between resulting image size and memory requirement. We allow
+ // the down-sampled image to be 20% smaller than the target size. That way we can get around
+ // unfortunate cases where e.g. a 720 picture is requested for 362 and not down-sampled at
+ // all. Why 20%? Why not. Prove me wrong.
+ int extent = originalSmallerExtent;
+ int sampleSize = 1;
+ while ((extent >> 1) >= targetExtent * 0.8f) {
+ sampleSize <<= 1;
+ extent >>= 1;
+ }
+
+ return sampleSize;
+ }
+
+ /** Decodes the bitmap with the given sample size */
+ public static Bitmap decodeBitmapFromBytes(byte[] bytes, int sampleSize) {
+ final BitmapFactory.Options options;
+ if (sampleSize <= 1) {
+ options = null;
+ } else {
+ options = new BitmapFactory.Options();
+ options.inSampleSize = sampleSize;
+ }
+ 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.
+ *
+ * @param input {@link Bitmap} to scale and crop
+ * @param targetWidth desired output width
+ * @param targetHeight desired output height
+ * @return output bitmap scaled to the target width/height and cropped to an oval. The cropping
+ * algorithm will try to fit as much of the input into the output as possible, while
+ * preserving the target width/height ratio.
+ */
+ public static Bitmap getRoundedBitmap(Bitmap input, int targetWidth, int targetHeight) {
+ if (input == null) {
+ return null;
+ }
+ final Bitmap.Config inputConfig = input.getConfig();
+ final Bitmap result =
+ Bitmap.createBitmap(
+ targetWidth, targetHeight, inputConfig != null ? inputConfig : Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(result);
+ final Paint paint = new Paint();
+ canvas.drawARGB(0, 0, 0, 0);
+ paint.setAntiAlias(true);
+ final RectF dst = new RectF(0, 0, targetWidth, targetHeight);
+ canvas.drawOval(dst, paint);
+
+ // Specifies that only pixels present in the destination (i.e. the drawn oval) should
+ // be overwritten with pixels from the input bitmap.
+ paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+
+ final int inputWidth = input.getWidth();
+ final int inputHeight = input.getHeight();
+
+ // Choose the largest scale factor that will fit inside the dimensions of the
+ // input bitmap.
+ final float scaleBy =
+ Math.min((float) inputWidth / targetWidth, (float) inputHeight / targetHeight);
+
+ final int xCropAmountHalved = (int) (scaleBy * targetWidth / 2);
+ final int yCropAmountHalved = (int) (scaleBy * targetHeight / 2);
+
+ final Rect src =
+ new Rect(
+ inputWidth / 2 - xCropAmountHalved,
+ inputHeight / 2 - yCropAmountHalved,
+ inputWidth / 2 + xCropAmountHalved,
+ inputHeight / 2 + yCropAmountHalved);
+
+ canvas.drawBitmap(input, src, dst, paint);
+ return result;
+ }
+}
diff --git a/java/com/android/contacts/common/util/CommonDateUtils.java b/java/com/android/contacts/common/util/CommonDateUtils.java
new file mode 100644
index 000000000..312e691f8
--- /dev/null
+++ b/java/com/android/contacts/common/util/CommonDateUtils.java
@@ -0,0 +1,37 @@
+/*
+ * 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.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+/** Common date utilities. */
+public class CommonDateUtils {
+
+ // All the SimpleDateFormats in this class use the UTC timezone
+ public static final SimpleDateFormat NO_YEAR_DATE_FORMAT =
+ new SimpleDateFormat("--MM-dd", Locale.US);
+ public static final SimpleDateFormat FULL_DATE_FORMAT =
+ new SimpleDateFormat("yyyy-MM-dd", Locale.US);
+ public static final SimpleDateFormat DATE_AND_TIME_FORMAT =
+ new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
+ public static final SimpleDateFormat NO_YEAR_DATE_AND_TIME_FORMAT =
+ new SimpleDateFormat("--MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
+
+ /** Exchange requires 8:00 for birthdays */
+ public static final int DEFAULT_HOUR = 8;
+}
diff --git a/java/com/android/contacts/common/util/Constants.java b/java/com/android/contacts/common/util/Constants.java
new file mode 100644
index 000000000..172e8c348
--- /dev/null
+++ b/java/com/android/contacts/common/util/Constants.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2009 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 Constants {
+
+ /**
+ * Log tag for performance measurement. To enable: adb shell setprop log.tag.ContactsPerf VERBOSE
+ */
+ public static final String PERFORMANCE_TAG = "ContactsPerf";
+
+ // Used for lookup URI that contains an encoded JSON string.
+ public static final String LOOKUP_URI_ENCODED = "encoded";
+}
diff --git a/java/com/android/contacts/common/util/ContactDisplayUtils.java b/java/com/android/contacts/common/util/ContactDisplayUtils.java
new file mode 100644
index 000000000..1586784db
--- /dev/null
+++ b/java/com/android/contacts/common/util/ContactDisplayUtils.java
@@ -0,0 +1,307 @@
+/*
+ * 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.util;
+
+import static android.provider.ContactsContract.CommonDataKinds.Phone;
+
+import android.content.Context;
+import android.content.res.Resources;
+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 java.util.Objects;
+
+/** Methods for handling various contact data labels. */
+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.
+ *
+ * @param type Phone data type.
+ * @return {@literal true} if the type is custom. {@literal false} if not.
+ */
+ public static boolean isCustomPhoneType(Integer type) {
+ return type == Phone.TYPE_CUSTOM || type == Phone.TYPE_ASSISTANT;
+ }
+
+ /**
+ * Gets a display label for a given phone type.
+ *
+ * @param type The type of number.
+ * @param customLabel A custom label to use if the phone is determined to be of custom type
+ * determined by {@link #isCustomPhoneType(Integer))}
+ * @param interactionType whether this is a call or sms. Either {@link #INTERACTION_CALL} or
+ * {@link #INTERACTION_SMS}.
+ * @param context The application context.
+ * @return An appropriate string label
+ */
+ public static CharSequence getLabelForCallOrSms(
+ Integer type, CharSequence customLabel, int interactionType, @NonNull Context context) {
+ Objects.requireNonNull(context);
+
+ if (isCustomPhoneType(type)) {
+ return (customLabel == null) ? "" : customLabel;
+ } else {
+ int resId;
+ if (interactionType == INTERACTION_SMS) {
+ resId = getSmsLabelResourceId(type);
+ } else {
+ resId = getPhoneLabelResourceId(type);
+ if (interactionType != INTERACTION_CALL) {
+ Log.e(
+ TAG,
+ "Un-recognized interaction type: "
+ + interactionType
+ + ". Defaulting to ContactDisplayUtils.INTERACTION_CALL.");
+ }
+ }
+
+ return context.getResources().getText(resId);
+ }
+ }
+
+ /**
+ * Find a label for calling.
+ *
+ * @param type The type of number.
+ * @return An appropriate string label.
+ */
+ public static int getPhoneLabelResourceId(Integer type) {
+ if (type == null) {
+ return R.string.call_other;
+ }
+ switch (type) {
+ case Phone.TYPE_HOME:
+ return R.string.call_home;
+ case Phone.TYPE_MOBILE:
+ return R.string.call_mobile;
+ case Phone.TYPE_WORK:
+ return R.string.call_work;
+ case Phone.TYPE_FAX_WORK:
+ return R.string.call_fax_work;
+ case Phone.TYPE_FAX_HOME:
+ return R.string.call_fax_home;
+ case Phone.TYPE_PAGER:
+ return R.string.call_pager;
+ case Phone.TYPE_OTHER:
+ return R.string.call_other;
+ case Phone.TYPE_CALLBACK:
+ return R.string.call_callback;
+ case Phone.TYPE_CAR:
+ return R.string.call_car;
+ case Phone.TYPE_COMPANY_MAIN:
+ return R.string.call_company_main;
+ case Phone.TYPE_ISDN:
+ return R.string.call_isdn;
+ case Phone.TYPE_MAIN:
+ return R.string.call_main;
+ case Phone.TYPE_OTHER_FAX:
+ return R.string.call_other_fax;
+ case Phone.TYPE_RADIO:
+ return R.string.call_radio;
+ case Phone.TYPE_TELEX:
+ return R.string.call_telex;
+ case Phone.TYPE_TTY_TDD:
+ return R.string.call_tty_tdd;
+ case Phone.TYPE_WORK_MOBILE:
+ return R.string.call_work_mobile;
+ case Phone.TYPE_WORK_PAGER:
+ return R.string.call_work_pager;
+ case Phone.TYPE_ASSISTANT:
+ return R.string.call_assistant;
+ case Phone.TYPE_MMS:
+ return R.string.call_mms;
+ default:
+ return R.string.call_custom;
+ }
+ }
+
+ /**
+ * Find a label for sending an sms.
+ *
+ * @param type The type of number.
+ * @return An appropriate string label.
+ */
+ public static int getSmsLabelResourceId(Integer type) {
+ if (type == null) {
+ return R.string.sms_other;
+ }
+ switch (type) {
+ case Phone.TYPE_HOME:
+ return R.string.sms_home;
+ case Phone.TYPE_MOBILE:
+ return R.string.sms_mobile;
+ case Phone.TYPE_WORK:
+ return R.string.sms_work;
+ case Phone.TYPE_FAX_WORK:
+ return R.string.sms_fax_work;
+ case Phone.TYPE_FAX_HOME:
+ return R.string.sms_fax_home;
+ case Phone.TYPE_PAGER:
+ return R.string.sms_pager;
+ case Phone.TYPE_OTHER:
+ return R.string.sms_other;
+ case Phone.TYPE_CALLBACK:
+ return R.string.sms_callback;
+ case Phone.TYPE_CAR:
+ return R.string.sms_car;
+ case Phone.TYPE_COMPANY_MAIN:
+ return R.string.sms_company_main;
+ case Phone.TYPE_ISDN:
+ return R.string.sms_isdn;
+ case Phone.TYPE_MAIN:
+ return R.string.sms_main;
+ case Phone.TYPE_OTHER_FAX:
+ return R.string.sms_other_fax;
+ case Phone.TYPE_RADIO:
+ return R.string.sms_radio;
+ case Phone.TYPE_TELEX:
+ return R.string.sms_telex;
+ case Phone.TYPE_TTY_TDD:
+ return R.string.sms_tty_tdd;
+ case Phone.TYPE_WORK_MOBILE:
+ return R.string.sms_work_mobile;
+ case Phone.TYPE_WORK_PAGER:
+ return R.string.sms_work_pager;
+ case Phone.TYPE_ASSISTANT:
+ return R.string.sms_assistant;
+ case Phone.TYPE_MMS:
+ return R.string.sms_mms;
+ default:
+ return R.string.sms_custom;
+ }
+ }
+
+ /**
+ * Whether the given text could be a phone number.
+ *
+ * <p>Note this will miss many things that are legitimate phone numbers, for example, phone
+ * numbers with letters.
+ */
+ public static boolean isPossiblePhoneNumber(CharSequence text) {
+ return text != null && Patterns.PHONE.matcher(text.toString()).matches();
+ }
+
+ /**
+ * Returns a Spannable for the given message with a telephone {@link TtsSpan} set for the given
+ * phone number text wherever it is found within the message.
+ */
+ public static Spannable getTelephoneTtsSpannable(
+ @Nullable String message, @Nullable String phoneNumber) {
+ if (message == null) {
+ return null;
+ }
+ final Spannable spannable = new SpannableString(message);
+ int start = phoneNumber == null ? -1 : message.indexOf(phoneNumber);
+ while (start >= 0) {
+ final int end = start + phoneNumber.length();
+ final TtsSpan ttsSpan = PhoneNumberUtilsCompat.createTtsSpan(phoneNumber);
+ spannable.setSpan(
+ ttsSpan,
+ start,
+ end,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); // this is consistenly done in a misleading way..
+ start = message.indexOf(phoneNumber, end);
+ }
+ return spannable;
+ }
+
+ /**
+ * Retrieves a string from a string template that takes 1 phone number as argument, span the
+ * number with a telephone {@link TtsSpan}, and return the spanned string.
+ *
+ * @param resources to retrieve the string from
+ * @param stringId ID of the string
+ * @param number to pass in the template
+ * @return CharSequence with the phone number wrapped in a TtsSpan
+ */
+ public static CharSequence getTtsSpannedPhoneNumber(
+ Resources resources, int stringId, String number) {
+ String msg = resources.getString(stringId, number);
+ return ContactDisplayUtils.getTelephoneTtsSpannable(msg, number);
+ }
+
+ /**
+ * Returns either namePrimary or nameAlternative based on the {@link ContactsPreferences}.
+ * Defaults to the name that is non-null.
+ *
+ * @param namePrimary the primary name.
+ * @param nameAlternative the alternative name.
+ * @param contactsPreferences the ContactsPreferences used to determine the preferred display
+ * name.
+ * @return namePrimary or nameAlternative depending on the value of displayOrderPreference.
+ */
+ public static String getPreferredDisplayName(
+ String namePrimary,
+ String nameAlternative,
+ @Nullable ContactsPreferences contactsPreferences) {
+ if (contactsPreferences == null) {
+ return namePrimary != null ? namePrimary : nameAlternative;
+ }
+ if (contactsPreferences.getDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_PRIMARY) {
+ return namePrimary;
+ }
+
+ if (contactsPreferences.getDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE
+ && !TextUtils.isEmpty(nameAlternative)) {
+ return nameAlternative;
+ }
+
+ return namePrimary;
+ }
+
+ /**
+ * Returns either namePrimary or nameAlternative based on the {@link ContactsPreferences}.
+ * Defaults to the name that is non-null.
+ *
+ * @param namePrimary the primary name.
+ * @param nameAlternative the alternative name.
+ * @param contactsPreferences the ContactsPreferences used to determine the preferred sort order.
+ * @return namePrimary or nameAlternative depending on the value of displayOrderPreference.
+ */
+ public static String getPreferredSortName(
+ String namePrimary,
+ String nameAlternative,
+ @Nullable ContactsPreferences contactsPreferences) {
+ if (contactsPreferences == null) {
+ return namePrimary != null ? namePrimary : nameAlternative;
+ }
+
+ if (contactsPreferences.getSortOrder() == ContactsPreferences.SORT_ORDER_PRIMARY) {
+ return namePrimary;
+ }
+
+ if (contactsPreferences.getSortOrder() == ContactsPreferences.SORT_ORDER_ALTERNATIVE
+ && !TextUtils.isEmpty(nameAlternative)) {
+ return nameAlternative;
+ }
+
+ return namePrimary;
+ }
+}
diff --git a/java/com/android/contacts/common/util/ContactListViewUtils.java b/java/com/android/contacts/common/util/ContactListViewUtils.java
new file mode 100644
index 000000000..278c27d5c
--- /dev/null
+++ b/java/com/android/contacts/common/util/ContactListViewUtils.java
@@ -0,0 +1,89 @@
+/*
+ * 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.util;
+
+import android.content.res.Resources;
+import android.view.View;
+import android.widget.ListView;
+import com.android.contacts.common.R;
+import com.android.dialer.util.ViewUtil;
+
+/** Utilities for configuring ListViews with a card background. */
+public class ContactListViewUtils {
+
+ // These two constants will help add more padding for the text inside the card.
+ private static final double TEXT_LEFT_PADDING_TO_CARD_PADDING_RATIO = 1.1;
+
+ private static void addPaddingToView(
+ ListView listView, int parentWidth, int listSpaceWeight, int listViewWeight) {
+ if (listSpaceWeight > 0 && listViewWeight > 0) {
+ double paddingPercent =
+ (double) listSpaceWeight / (double) (listSpaceWeight * 2 + listViewWeight);
+ listView.setPadding(
+ (int) (parentWidth * paddingPercent * TEXT_LEFT_PADDING_TO_CARD_PADDING_RATIO),
+ listView.getPaddingTop(),
+ (int) (parentWidth * paddingPercent * TEXT_LEFT_PADDING_TO_CARD_PADDING_RATIO),
+ listView.getPaddingBottom());
+ // The EdgeEffect and ScrollBar need to span to the edge of the ListView's padding.
+ listView.setClipToPadding(false);
+ listView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
+ }
+ }
+
+ /**
+ * Add padding to {@param listView} if this configuration has set both space weight and view
+ * weight on the layout. Use this util method instead of defining the padding in the layout file
+ * so that the {@param listView}'s padding can be set proportional to the card padding.
+ *
+ * @param listView ListView that we add padding to
+ * @param rootLayout layout that contains ListView and R.id.list_card
+ */
+ public static void applyCardPaddingToView(
+ Resources resources, final ListView listView, final View rootLayout) {
+ // Set a padding on the list view so it appears in the center of the card
+ // in the layout if required.
+ final int listSpaceWeight = resources.getInteger(R.integer.contact_list_space_layout_weight);
+ final int listViewWeight = resources.getInteger(R.integer.contact_list_card_layout_weight);
+ if (listSpaceWeight > 0 && listViewWeight > 0) {
+ rootLayout.setBackgroundResource(0);
+ // Set the card view visible
+ View mCardView = rootLayout.findViewById(R.id.list_card);
+ if (mCardView == null) {
+ throw new RuntimeException(
+ "Your content must have a list card view who can be turned visible "
+ + "whenever it is necessary.");
+ }
+ mCardView.setVisibility(View.VISIBLE);
+
+ // Add extra padding to the list view to make them appear in the center of the card.
+ // In order to avoid jumping, we skip drawing the next frame of the ListView.
+ ViewUtil.doOnPreDraw(
+ listView,
+ false,
+ new Runnable() {
+ @Override
+ public void run() {
+ // Use the rootLayout.getWidth() instead of listView.getWidth() since
+ // we sometimes hide the listView until we finish loading data. This would
+ // result in incorrect padding.
+ ContactListViewUtils.addPaddingToView(
+ listView, rootLayout.getWidth(), listSpaceWeight, listViewWeight);
+ }
+ });
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/util/ContactLoaderUtils.java b/java/com/android/contacts/common/util/ContactLoaderUtils.java
new file mode 100644
index 000000000..e30971721
--- /dev/null
+++ b/java/com/android/contacts/common/util/ContactLoaderUtils.java
@@ -0,0 +1,78 @@
+/*
+ * 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.ContentResolver;
+import android.content.ContentUris;
+import android.net.Uri;
+import android.provider.Contacts;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.RawContacts;
+
+/** Utility methods for the {@link ContactLoader}. */
+public final class ContactLoaderUtils {
+
+ /** Static helper, not instantiable. */
+ private ContactLoaderUtils() {}
+
+ /**
+ * Transforms the given Uri and returns a Lookup-Uri that represents the contact. For legacy
+ * contacts, a raw-contact lookup is performed. An {@link IllegalArgumentException} can be thrown
+ * if the URI is null or the authority is not recognized.
+ *
+ * <p>Do not call from the UI thread.
+ */
+ @SuppressWarnings("deprecation")
+ public static Uri ensureIsContactUri(final ContentResolver resolver, final Uri uri)
+ throws IllegalArgumentException {
+ if (uri == null) {
+ throw new IllegalArgumentException("uri must not be null");
+ }
+
+ final String authority = uri.getAuthority();
+
+ // Current Style Uri?
+ if (ContactsContract.AUTHORITY.equals(authority)) {
+ final String type = resolver.getType(uri);
+ // Contact-Uri? Good, return it
+ if (ContactsContract.Contacts.CONTENT_ITEM_TYPE.equals(type)) {
+ return uri;
+ }
+
+ // RawContact-Uri? Transform it to ContactUri
+ if (RawContacts.CONTENT_ITEM_TYPE.equals(type)) {
+ final long rawContactId = ContentUris.parseId(uri);
+ return RawContacts.getContactLookupUri(
+ resolver, ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
+ }
+
+ // Anything else? We don't know what this is
+ throw new IllegalArgumentException("uri format is unknown");
+ }
+
+ // Legacy Style? Convert to RawContact
+ final String OBSOLETE_AUTHORITY = Contacts.AUTHORITY;
+ if (OBSOLETE_AUTHORITY.equals(authority)) {
+ // Legacy Format. Convert to RawContact-Uri and then lookup the contact
+ final long rawContactId = ContentUris.parseId(uri);
+ return RawContacts.getContactLookupUri(
+ resolver, ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
+ }
+
+ throw new IllegalArgumentException("uri authority is unknown");
+ }
+}
diff --git a/java/com/android/contacts/common/util/DateUtils.java b/java/com/android/contacts/common/util/DateUtils.java
new file mode 100644
index 000000000..1935d727a
--- /dev/null
+++ b/java/com/android/contacts/common/util/DateUtils.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2010 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.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.
+ *
+ * @param time Instance of time object to use for calculations.
+ * @param date1 First date to check.
+ * @param date2 Second date to check.
+ * @return The absolute difference in days between the two dates.
+ */
+ public static int getDayDifference(Time time, long date1, long date2) {
+ time.set(date1);
+ int startDay = Time.getJulianDay(date1, time.gmtoff);
+
+ time.set(date2);
+ int currentDay = Time.getJulianDay(date2, time.gmtoff);
+
+ return Math.abs(currentDay - startDay);
+ }
+}
diff --git a/java/com/android/contacts/common/util/FabUtil.java b/java/com/android/contacts/common/util/FabUtil.java
new file mode 100644
index 000000000..b1bb2e653
--- /dev/null
+++ b/java/com/android/contacts/common/util/FabUtil.java
@@ -0,0 +1,71 @@
+/*
+ * 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.util;
+
+import android.content.res.Resources;
+import android.graphics.Outline;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.widget.ListView;
+import com.android.contacts.common.R;
+import com.android.dialer.compat.CompatUtils;
+
+/** Provides static functions to work with views */
+public class FabUtil {
+
+ private static final ViewOutlineProvider OVAL_OUTLINE_PROVIDER =
+ new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setOval(0, 0, view.getWidth(), view.getHeight());
+ }
+ };
+
+ private FabUtil() {}
+
+ /**
+ * Configures the floating action button, clipping it to a circle and setting its translation z
+ *
+ * @param fabView the float action button's view
+ * @param res the resources file
+ */
+ public static void setupFloatingActionButton(View fabView, Resources res) {
+ if (CompatUtils.isLollipopCompatible()) {
+ fabView.setOutlineProvider(OVAL_OUTLINE_PROVIDER);
+ fabView.setTranslationZ(
+ res.getDimensionPixelSize(R.dimen.floating_action_button_translation_z));
+ }
+ }
+
+ /**
+ * Adds padding to the bottom of the given {@link ListView} so that the floating action button
+ * does not obscure any content.
+ *
+ * @param listView to add the padding to
+ * @param res valid resources object
+ */
+ public static void addBottomPaddingToListViewForFab(ListView listView, Resources res) {
+ final int fabPadding =
+ res.getDimensionPixelSize(R.dimen.floating_action_button_list_bottom_padding);
+ listView.setPaddingRelative(
+ listView.getPaddingStart(),
+ listView.getPaddingTop(),
+ listView.getPaddingEnd(),
+ listView.getPaddingBottom() + fabPadding);
+ listView.setClipToPadding(false);
+ }
+}
diff --git a/java/com/android/contacts/common/util/MaterialColorMapUtils.java b/java/com/android/contacts/common/util/MaterialColorMapUtils.java
new file mode 100644
index 000000000..a2d9847ec
--- /dev/null
+++ b/java/com/android/contacts/common/util/MaterialColorMapUtils.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2014 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.res.Resources;
+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 {
+
+ private final TypedArray sPrimaryColors;
+ private final TypedArray sSecondaryColors;
+
+ public MaterialColorMapUtils(Resources resources) {
+ sPrimaryColors =
+ resources.obtainTypedArray(com.android.contacts.common.R.array.letter_tile_colors);
+ sSecondaryColors =
+ 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.
+ *
+ * @return A value between 0.0f and 1.0f
+ */
+ public static float hue(int color) {
+ int r = (color >> 16) & 0xFF;
+ int g = (color >> 8) & 0xFF;
+ int b = color & 0xFF;
+
+ int V = Math.max(b, Math.max(r, g));
+ int temp = Math.min(b, Math.min(r, g));
+
+ float H;
+
+ if (V == temp) {
+ H = 0;
+ } else {
+ final float vtemp = V - temp;
+ final float cr = (V - r) / vtemp;
+ final float cg = (V - g) / vtemp;
+ final float cb = (V - b) / vtemp;
+
+ if (r == V) {
+ H = cb - cg;
+ } else if (g == V) {
+ H = 2 + cr - cb;
+ } else {
+ H = 4 + cg - cr;
+ }
+
+ H /= 6.f;
+ if (H < 0) {
+ H++;
+ }
+ }
+
+ return H;
+ }
+
+ /**
+ * Return primary and secondary colors from the Material color palette that are similar to {@param
+ * color}.
+ */
+ public MaterialPalette calculatePrimaryAndSecondaryColor(int color) {
+ Trace.beginSection("calculatePrimaryAndSecondaryColor");
+
+ final float colorHue = hue(color);
+ float minimumDistance = Float.MAX_VALUE;
+ int indexBestMatch = 0;
+ for (int i = 0; i < sPrimaryColors.length(); i++) {
+ final int primaryColor = sPrimaryColors.getColor(i, 0);
+ final float comparedHue = hue(primaryColor);
+ // No need to be perceptually accurate when calculating color distances since
+ // we are only mapping to 15 colors. Being slightly inaccurate isn't going to change
+ // the mapping very often.
+ final float distance = Math.abs(comparedHue - colorHue);
+ if (distance < minimumDistance) {
+ minimumDistance = distance;
+ indexBestMatch = i;
+ }
+ }
+
+ Trace.endSection();
+ return new MaterialPalette(
+ sPrimaryColors.getColor(indexBestMatch, 0), sSecondaryColors.getColor(indexBestMatch, 0));
+ }
+
+ public static class MaterialPalette implements Parcelable {
+
+ public static final Creator<MaterialPalette> CREATOR =
+ new Creator<MaterialPalette>() {
+ @Override
+ public MaterialPalette createFromParcel(Parcel in) {
+ return new MaterialPalette(in);
+ }
+
+ @Override
+ public MaterialPalette[] newArray(int size) {
+ return new MaterialPalette[size];
+ }
+ };
+ public final int mPrimaryColor;
+ public final int mSecondaryColor;
+
+ public MaterialPalette(int primaryColor, int secondaryColor) {
+ mPrimaryColor = primaryColor;
+ mSecondaryColor = secondaryColor;
+ }
+
+ private MaterialPalette(Parcel in) {
+ mPrimaryColor = in.readInt();
+ mSecondaryColor = in.readInt();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ MaterialPalette other = (MaterialPalette) obj;
+ if (mPrimaryColor != other.mPrimaryColor) {
+ return false;
+ }
+ if (mSecondaryColor != other.mSecondaryColor) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + mPrimaryColor;
+ result = prime * result + mSecondaryColor;
+ return result;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mPrimaryColor);
+ dest.writeInt(mSecondaryColor);
+ }
+ }
+}
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.
+ *
+ * <p>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<String, String> 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).
+ *
+ * <p>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<String, String> displayNameToStructuredName(
+ Context context, String displayName) {
+ Map<String, String> structuredName = new TreeMap<String, String>();
+ 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).
+ *
+ * <p>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<String, String> 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/SearchUtil.java b/java/com/android/contacts/common/util/SearchUtil.java
new file mode 100644
index 000000000..314d565b2
--- /dev/null
+++ b/java/com/android/contacts/common/util/SearchUtil.java
@@ -0,0 +1,198 @@
+/*
+ * 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.util;
+
+import android.support.annotation.VisibleForTesting;
+
+/** Methods related to search. */
+public class SearchUtil {
+
+ /**
+ * Given a string with lines delimited with '\n', finds the matching line to the given substring.
+ *
+ * @param contents The string to search.
+ * @param substring The substring to search for.
+ * @return A MatchedLine object containing the matching line and the startIndex of the substring
+ * match within that line.
+ */
+ public static MatchedLine findMatchingLine(String contents, String substring) {
+ final MatchedLine matched = new MatchedLine();
+
+ // Snippet may contain multiple lines separated by "\n".
+ // Locate the lines of the content that contain the substring.
+ final int index = SearchUtil.contains(contents, substring);
+ if (index != -1) {
+ // Match found. Find the corresponding line.
+ int start = index - 1;
+ while (start > -1) {
+ if (contents.charAt(start) == '\n') {
+ break;
+ }
+ start--;
+ }
+ int end = index + 1;
+ while (end < contents.length()) {
+ if (contents.charAt(end) == '\n') {
+ break;
+ }
+ end++;
+ }
+ matched.line = contents.substring(start + 1, end);
+ matched.startIndex = index - (start + 1);
+ }
+ return matched;
+ }
+
+ /**
+ * Similar to String.contains() with two main differences:
+ *
+ * <p>1) Only searches token prefixes. A token is defined as any combination of letters or
+ * numbers.
+ *
+ * <p>2) Returns the starting index where the substring is found.
+ *
+ * @param value The string to search.
+ * @param substring The substring to look for.
+ * @return The starting index where the substring is found. {@literal -1} if substring is not
+ * found in value.
+ */
+ @VisibleForTesting
+ static int contains(String value, String substring) {
+ if (value.length() < substring.length()) {
+ return -1;
+ }
+
+ // i18n support
+ // Generate the code points for the substring once.
+ // There will be a maximum of substring.length code points. But may be fewer.
+ // Since the array length is not an accurate size, we need to keep a separate variable.
+ final int[] substringCodePoints = new int[substring.length()];
+ int substringLength = 0; // may not equal substring.length()!!
+ for (int i = 0; i < substring.length(); ) {
+ final int codePoint = Character.codePointAt(substring, i);
+ substringCodePoints[substringLength] = codePoint;
+ substringLength++;
+ i += Character.charCount(codePoint);
+ }
+
+ for (int i = 0; i < value.length(); i = findNextTokenStart(value, i)) {
+ int numMatch = 0;
+ for (int j = i; j < value.length() && numMatch < substringLength; ++numMatch) {
+ int valueCp = Character.toLowerCase(value.codePointAt(j));
+ int substringCp = substringCodePoints[numMatch];
+ if (valueCp != substringCp) {
+ break;
+ }
+ j += Character.charCount(valueCp);
+ }
+ if (numMatch == substringLength) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Find the start of the next token. A token is composed of letters and numbers. Any other
+ * character are considered delimiters.
+ *
+ * @param line The string to search for the next token.
+ * @param startIndex The index to start searching. 0 based indexing.
+ * @return The index for the start of the next token. line.length() if next token not found.
+ */
+ @VisibleForTesting
+ static int findNextTokenStart(String line, int startIndex) {
+ int index = startIndex;
+
+ // If already in token, eat remainder of token.
+ while (index <= line.length()) {
+ if (index == line.length()) {
+ // No more tokens.
+ return index;
+ }
+ final int codePoint = line.codePointAt(index);
+ if (!Character.isLetterOrDigit(codePoint)) {
+ break;
+ }
+ index += Character.charCount(codePoint);
+ }
+
+ // Out of token, eat all consecutive delimiters.
+ while (index <= line.length()) {
+ if (index == line.length()) {
+ return index;
+ }
+ final int codePoint = line.codePointAt(index);
+ if (Character.isLetterOrDigit(codePoint)) {
+ break;
+ }
+ index += Character.charCount(codePoint);
+ }
+
+ return index;
+ }
+
+ /**
+ * Anything other than letter and numbers are considered delimiters. Remove start and end
+ * delimiters since they are not relevant to search.
+ *
+ * @param query The query string to clean.
+ * @return The cleaned query. Empty string if all characters are cleaned out.
+ */
+ public static String cleanStartAndEndOfSearchQuery(String query) {
+ int start = 0;
+ while (start < query.length()) {
+ int codePoint = query.codePointAt(start);
+ if (Character.isLetterOrDigit(codePoint)) {
+ break;
+ }
+ start += Character.charCount(codePoint);
+ }
+
+ if (start == query.length()) {
+ // All characters are delimiters.
+ return "";
+ }
+
+ int end = query.length() - 1;
+ while (end > -1) {
+ if (Character.isLowSurrogate(query.charAt(end))) {
+ // Assume valid i18n string. There should be a matching high surrogate before it.
+ end--;
+ }
+ int codePoint = query.codePointAt(end);
+ if (Character.isLetterOrDigit(codePoint)) {
+ break;
+ }
+ end--;
+ }
+
+ // end is a letter or digit.
+ return query.substring(start, end + 1);
+ }
+
+ public static class MatchedLine {
+
+ public int startIndex = -1;
+ public String line;
+
+ @Override
+ public String toString() {
+ return "MatchedLine{" + "line='" + line + '\'' + ", startIndex=" + startIndex + '}';
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/util/StopWatch.java b/java/com/android/contacts/common/util/StopWatch.java
new file mode 100644
index 000000000..b944b9867
--- /dev/null
+++ b/java/com/android/contacts/common/util/StopWatch.java
@@ -0,0 +1,100 @@
+/*
+ * 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.util;
+
+import android.util.Log;
+import java.util.ArrayList;
+
+/** A {@link StopWatch} records start, laps and stop, and print them to logcat. */
+public class StopWatch {
+
+ private final String mLabel;
+
+ private final ArrayList<Long> mTimes = new ArrayList<>();
+ private final ArrayList<String> mLapLabels = new ArrayList<>();
+
+ private StopWatch(String label) {
+ mLabel = label;
+ lap("");
+ }
+
+ /** Create a new instance and start it. */
+ public static StopWatch start(String label) {
+ 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());
+ mLapLabels.add(lapLabel);
+ }
+
+ /** Stop it and log the result, if the total time >= {@code timeThresholdToLog}. */
+ public void stopAndLog(String TAG, int timeThresholdToLog) {
+
+ lap("");
+
+ final long start = mTimes.get(0);
+ final long stop = mTimes.get(mTimes.size() - 1);
+
+ final long total = stop - start;
+ if (total < timeThresholdToLog) {
+ return;
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append(mLabel);
+ sb.append(",");
+ sb.append(total);
+ sb.append(": ");
+
+ long last = start;
+ for (int i = 1; i < mTimes.size(); i++) {
+ final long current = mTimes.get(i);
+ sb.append(mLapLabels.get(i));
+ sb.append(",");
+ sb.append((current - last));
+ sb.append(" ");
+ last = current;
+ }
+ 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
new file mode 100644
index 000000000..b664268ca
--- /dev/null
+++ b/java/com/android/contacts/common/util/TelephonyManagerUtils.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 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.Context;
+import android.telephony.TelephonyManager;
+
+/** This class provides several TelephonyManager util functions. */
+public class TelephonyManagerUtils {
+
+ /**
+ * Gets the voicemail tag from Telephony Manager.
+ *
+ * @param context Current application context
+ * @return Voicemail tag, the alphabetic identifier associated with the voice mail number.
+ */
+ public static String getVoiceMailAlphaTag(Context context) {
+ final TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ 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;
+}
diff --git a/java/com/android/contacts/common/util/UriUtils.java b/java/com/android/contacts/common/util/UriUtils.java
new file mode 100644
index 000000000..4690942ba
--- /dev/null
+++ b/java/com/android/contacts/common/util/UriUtils.java
@@ -0,0 +1,90 @@
+/*
+ * 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.net.Uri;
+import android.provider.ContactsContract;
+import java.util.List;
+
+/** Utility methods for dealing with URIs. */
+public class UriUtils {
+
+ /** Static helper, not instantiable. */
+ private UriUtils() {}
+
+ /** Checks whether two URI are equal, taking care of the case where either is null. */
+ public static boolean areEqual(Uri uri1, Uri uri2) {
+ if (uri1 == null && uri2 == null) {
+ return true;
+ }
+ if (uri1 == null || uri2 == null) {
+ return false;
+ }
+ return uri1.equals(uri2);
+ }
+
+ /** Parses a string into a URI and returns null if the given string is null. */
+ public static Uri parseUriOrNull(String uriString) {
+ if (uriString == null) {
+ return null;
+ }
+ return Uri.parse(uriString);
+ }
+
+ /** Converts a URI into a string, returns null if the given URI is null. */
+ public static String uriToString(Uri uri) {
+ return uri == null ? null : uri.toString();
+ }
+
+ public static boolean isEncodedContactUri(Uri uri) {
+ if (uri == null) {
+ return false;
+ }
+ final String lastPathSegment = uri.getLastPathSegment();
+ if (lastPathSegment == null) {
+ return false;
+ }
+ return lastPathSegment.equals(Constants.LOOKUP_URI_ENCODED);
+ }
+
+ /**
+ * @return {@code uri} as-is if the authority is of contacts provider. Otherwise or {@code uri} is
+ * null, return null otherwise
+ */
+ public static Uri nullForNonContactsUri(Uri uri) {
+ if (uri == null) {
+ return null;
+ }
+ return ContactsContract.AUTHORITY.equals(uri.getAuthority()) ? uri : null;
+ }
+
+ /** Parses the given URI to determine the original lookup key of the contact. */
+ public static String getLookupKeyFromUri(Uri lookupUri) {
+ // Would be nice to be able to persist the lookup key somehow to avoid having to parse
+ // the uri entirely just to retrieve the lookup key, but every uri is already parsed
+ // once anyway to check if it is an encoded JSON uri, so this has negligible effect
+ // on performance.
+ if (lookupUri != null && !UriUtils.isEncodedContactUri(lookupUri)) {
+ final List<String> segments = lookupUri.getPathSegments();
+ // This returns the third path segment of the uri, where the lookup key is located.
+ // See {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
+ return (segments.size() < 3) ? null : Uri.encode(segments.get(2));
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/java/com/android/contacts/common/widget/ActivityTouchLinearLayout.java b/java/com/android/contacts/common/widget/ActivityTouchLinearLayout.java
new file mode 100644
index 000000000..2988a5a58
--- /dev/null
+++ b/java/com/android/contacts/common/widget/ActivityTouchLinearLayout.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 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.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.LinearLayout;
+import com.android.dialer.util.TouchPointManager;
+
+/**
+ * Linear layout for an activity that listens to all touch events on the screen and saves the touch
+ * point. Typically touch events are handled by child views--this class intercepts those touch
+ * events before passing them on to the child.
+ */
+public class ActivityTouchLinearLayout extends LinearLayout {
+
+ public ActivityTouchLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ TouchPointManager.getInstance().setPoint((int) ev.getRawX(), (int) ev.getRawY());
+ }
+ return false;
+ }
+}
diff --git a/java/com/android/contacts/common/widget/FloatingActionButtonController.java b/java/com/android/contacts/common/widget/FloatingActionButtonController.java
new file mode 100644
index 000000000..f03129779
--- /dev/null
+++ b/java/com/android/contacts/common/widget/FloatingActionButtonController.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2014 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.widget;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.ImageButton;
+import com.android.contacts.common.R;
+import com.android.contacts.common.util.FabUtil;
+import com.android.dialer.animation.AnimUtils;
+
+/** Controls the movement and appearance of the FAB (Floating Action Button). */
+public class FloatingActionButtonController {
+
+ public static final int ALIGN_MIDDLE = 0;
+ public static final int ALIGN_QUARTER_END = 1;
+ public static final int ALIGN_END = 2;
+
+ private static final int FAB_SCALE_IN_DURATION = 266;
+ private static final int FAB_SCALE_IN_FADE_IN_DELAY = 100;
+ private static final int FAB_ICON_FADE_OUT_DURATION = 66;
+
+ private final int mAnimationDuration;
+ private final int mFloatingActionButtonWidth;
+ private final int mFloatingActionButtonMarginRight;
+ private final View mFloatingActionButtonContainer;
+ private final ImageButton mFloatingActionButton;
+ private final Interpolator mFabInterpolator;
+ private int mScreenWidth;
+
+ public FloatingActionButtonController(Activity activity, View container, ImageButton button) {
+ Resources resources = activity.getResources();
+ mFabInterpolator =
+ AnimationUtils.loadInterpolator(activity, android.R.interpolator.fast_out_slow_in);
+ mFloatingActionButtonWidth =
+ resources.getDimensionPixelSize(R.dimen.floating_action_button_width);
+ mFloatingActionButtonMarginRight =
+ resources.getDimensionPixelOffset(R.dimen.floating_action_button_margin_right);
+ mAnimationDuration = resources.getInteger(R.integer.floating_action_button_animation_duration);
+ mFloatingActionButtonContainer = container;
+ mFloatingActionButton = button;
+ FabUtil.setupFloatingActionButton(mFloatingActionButtonContainer, resources);
+ }
+
+ /**
+ * Passes the screen width into the class. Necessary for translation calculations. Should be
+ * called as soon as parent View width is available.
+ *
+ * @param screenWidth The width of the screen in pixels.
+ */
+ public void setScreenWidth(int screenWidth) {
+ mScreenWidth = screenWidth;
+ }
+
+ public boolean isVisible() {
+ return mFloatingActionButtonContainer.getVisibility() == View.VISIBLE;
+ }
+
+ /**
+ * Sets FAB as View.VISIBLE or View.GONE.
+ *
+ * @param visible Whether or not to make the container visible.
+ */
+ public void setVisible(boolean visible) {
+ mFloatingActionButtonContainer.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+
+ public void changeIcon(Drawable icon, String description) {
+ if (mFloatingActionButton.getDrawable() != icon
+ || !mFloatingActionButton.getContentDescription().equals(description)) {
+ mFloatingActionButton.setImageDrawable(icon);
+ mFloatingActionButton.setContentDescription(description);
+ }
+ }
+
+ /**
+ * Updates the FAB location (middle to right position) as the PageView scrolls.
+ *
+ * @param positionOffset A fraction used to calculate position of the FAB during page scroll.
+ */
+ public void onPageScrolled(float positionOffset) {
+ // As the page is scrolling, if we're on the first tab, update the FAB position so it
+ // moves along with it.
+ mFloatingActionButtonContainer.setTranslationX(
+ (int) (positionOffset * getTranslationXForAlignment(ALIGN_END)));
+ }
+
+ /**
+ * Aligns the FAB to the described location
+ *
+ * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT.
+ * @param animate Whether or not to animate the transition.
+ */
+ public void align(int align, boolean animate) {
+ align(align, 0 /*offsetX */, 0 /* offsetY */, animate);
+ }
+
+ /**
+ * Aligns the FAB to the described location plus specified additional offsets.
+ *
+ * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT.
+ * @param offsetX Additional offsetX to translate by.
+ * @param offsetY Additional offsetY to translate by.
+ * @param animate Whether or not to animate the transition.
+ */
+ public void align(int align, int offsetX, int offsetY, boolean animate) {
+ if (mScreenWidth == 0) {
+ return;
+ }
+
+ int translationX = getTranslationXForAlignment(align);
+
+ // Skip animation if container is not shown; animation causes container to show again.
+ if (animate && mFloatingActionButtonContainer.isShown()) {
+ mFloatingActionButtonContainer
+ .animate()
+ .translationX(translationX + offsetX)
+ .translationY(offsetY)
+ .setInterpolator(mFabInterpolator)
+ .setDuration(mAnimationDuration)
+ .start();
+ } else {
+ mFloatingActionButtonContainer.setTranslationX(translationX + offsetX);
+ mFloatingActionButtonContainer.setTranslationY(offsetY);
+ }
+ }
+
+ /**
+ * Resizes width and height of the floating action bar container.
+ *
+ * @param dimension The new dimensions for the width and height.
+ * @param animate Whether to animate this change.
+ */
+ public void resize(int dimension, boolean animate) {
+ if (animate) {
+ AnimUtils.changeDimensions(mFloatingActionButtonContainer, dimension, dimension);
+ } else {
+ mFloatingActionButtonContainer.getLayoutParams().width = dimension;
+ mFloatingActionButtonContainer.getLayoutParams().height = dimension;
+ mFloatingActionButtonContainer.requestLayout();
+ }
+ }
+
+ /**
+ * Scales the floating action button from no height and width to its actual dimensions. This is an
+ * animation for showing the floating action button.
+ *
+ * @param delayMs The delay for the effect, in milliseconds.
+ */
+ public void scaleIn(int delayMs) {
+ setVisible(true);
+ AnimUtils.scaleIn(mFloatingActionButtonContainer, FAB_SCALE_IN_DURATION, delayMs);
+ AnimUtils.fadeIn(
+ mFloatingActionButton, FAB_SCALE_IN_DURATION, delayMs + FAB_SCALE_IN_FADE_IN_DELAY, null);
+ }
+
+ /** Immediately remove the affects of the last call to {@link #scaleOut}. */
+ public void resetIn() {
+ mFloatingActionButton.setAlpha(1f);
+ mFloatingActionButton.setVisibility(View.VISIBLE);
+ mFloatingActionButtonContainer.setScaleX(1);
+ mFloatingActionButtonContainer.setScaleY(1);
+ }
+
+ /**
+ * Scales the floating action button from its actual dimensions to no height and width. This is an
+ * animation for hiding the floating action button.
+ */
+ public void scaleOut() {
+ AnimUtils.scaleOut(mFloatingActionButtonContainer, mAnimationDuration);
+ // Fade out the icon faster than the scale out animation, so that the icon scaling is less
+ // obvious. We don't want it to scale, but the resizing the container is not as performant.
+ AnimUtils.fadeOut(mFloatingActionButton, FAB_ICON_FADE_OUT_DURATION, null);
+ }
+
+ /**
+ * Calculates the X offset of the FAB to the given alignment, adjusted for whether or not the view
+ * is in RTL mode.
+ *
+ * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT.
+ * @return The translationX for the given alignment.
+ */
+ public int getTranslationXForAlignment(int align) {
+ int result = 0;
+ switch (align) {
+ case ALIGN_MIDDLE:
+ // Moves the FAB to exactly center screen.
+ return 0;
+ case ALIGN_QUARTER_END:
+ // Moves the FAB a quarter of the screen width.
+ result = mScreenWidth / 4;
+ break;
+ case ALIGN_END:
+ // Moves the FAB half the screen width. Same as aligning right with a marginRight.
+ result =
+ mScreenWidth / 2 - mFloatingActionButtonWidth / 2 - mFloatingActionButtonMarginRight;
+ break;
+ }
+ if (isLayoutRtl()) {
+ result *= -1;
+ }
+ return result;
+ }
+
+ private boolean isLayoutRtl() {
+ return mFloatingActionButtonContainer.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ }
+}
diff --git a/java/com/android/contacts/common/widget/LayoutSuppressingImageView.java b/java/com/android/contacts/common/widget/LayoutSuppressingImageView.java
new file mode 100644
index 000000000..d84d8f757
--- /dev/null
+++ b/java/com/android/contacts/common/widget/LayoutSuppressingImageView.java
@@ -0,0 +1,39 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+/**
+ * Custom {@link ImageView} that improves layouting performance.
+ *
+ * <p>This improves the performance by not passing requestLayout() to its parent, taking advantage
+ * of knowing that image size won't change once set.
+ */
+public class LayoutSuppressingImageView extends ImageView {
+
+ public LayoutSuppressingImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void requestLayout() {
+ forceLayout();
+ }
+}
diff --git a/java/com/android/contacts/common/widget/SelectPhoneAccountDialogFragment.java b/java/com/android/contacts/common/widget/SelectPhoneAccountDialogFragment.java
new file mode 100644
index 000000000..63f8ca580
--- /dev/null
+++ b/java/com/android/contacts/common/widget/SelectPhoneAccountDialogFragment.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2014 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.widget;
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+import com.android.contacts.common.R;
+import com.android.contacts.common.compat.PhoneAccountCompat;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Dialog that allows the user to select a phone accounts for a given action. Optionally provides
+ * the choice to set the phone account as default.
+ */
+public class SelectPhoneAccountDialogFragment extends DialogFragment {
+
+ private static final String ARG_TITLE_RES_ID = "title_res_id";
+ private static final String ARG_CAN_SET_DEFAULT = "can_set_default";
+ private static final String ARG_ACCOUNT_HANDLES = "account_handles";
+ private static final String ARG_IS_DEFAULT_CHECKED = "is_default_checked";
+ private static final String ARG_LISTENER = "listener";
+ private static final String ARG_CALL_ID = "call_id";
+
+ private int mTitleResId;
+ private boolean mCanSetDefault;
+ private List<PhoneAccountHandle> mAccountHandles;
+ private boolean mIsSelected;
+ private boolean mIsDefaultChecked;
+ private SelectPhoneAccountListener mListener;
+
+ public SelectPhoneAccountDialogFragment() {}
+
+ /**
+ * Create new fragment instance with default title and no option to set as default.
+ *
+ * @param accountHandles The {@code PhoneAccountHandle}s available to select from.
+ * @param listener The listener for the results of the account selection.
+ */
+ public static SelectPhoneAccountDialogFragment newInstance(
+ List<PhoneAccountHandle> accountHandles,
+ SelectPhoneAccountListener listener,
+ @Nullable String callId) {
+ return newInstance(
+ R.string.select_account_dialog_title, false, accountHandles, listener, callId);
+ }
+
+ /**
+ * Create new fragment instance. This method also allows specifying a custom title and "set
+ * default" checkbox.
+ *
+ * @param titleResId The resource ID for the string to use in the title of the dialog.
+ * @param canSetDefault {@code true} if the dialog should include an option to set the selection
+ * as the default. False otherwise.
+ * @param accountHandles The {@code PhoneAccountHandle}s available to select from.
+ * @param listener The listener for the results of the account selection.
+ */
+ public static SelectPhoneAccountDialogFragment newInstance(
+ int titleResId,
+ boolean canSetDefault,
+ List<PhoneAccountHandle> accountHandles,
+ SelectPhoneAccountListener listener,
+ @Nullable String callId) {
+ ArrayList<PhoneAccountHandle> accountHandlesCopy = new ArrayList<>();
+ if (accountHandles != null) {
+ accountHandlesCopy.addAll(accountHandles);
+ }
+ SelectPhoneAccountDialogFragment fragment = new SelectPhoneAccountDialogFragment();
+ final Bundle args = new Bundle();
+ args.putInt(ARG_TITLE_RES_ID, titleResId);
+ args.putBoolean(ARG_CAN_SET_DEFAULT, canSetDefault);
+ args.putParcelableArrayList(ARG_ACCOUNT_HANDLES, accountHandlesCopy);
+ args.putParcelable(ARG_LISTENER, listener);
+ args.putString(ARG_CALL_ID, callId);
+ fragment.setArguments(args);
+ fragment.setListener(listener);
+ return fragment;
+ }
+
+ public void setListener(SelectPhoneAccountListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(ARG_IS_DEFAULT_CHECKED, mIsDefaultChecked);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mTitleResId = getArguments().getInt(ARG_TITLE_RES_ID);
+ mCanSetDefault = getArguments().getBoolean(ARG_CAN_SET_DEFAULT);
+ mAccountHandles = getArguments().getParcelableArrayList(ARG_ACCOUNT_HANDLES);
+ mListener = getArguments().getParcelable(ARG_LISTENER);
+ if (savedInstanceState != null) {
+ mIsDefaultChecked = savedInstanceState.getBoolean(ARG_IS_DEFAULT_CHECKED);
+ }
+ mIsSelected = false;
+
+ final DialogInterface.OnClickListener selectionListener =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mIsSelected = true;
+ PhoneAccountHandle selectedAccountHandle = mAccountHandles.get(which);
+ Bundle result = new Bundle();
+ result.putParcelable(
+ SelectPhoneAccountListener.EXTRA_SELECTED_ACCOUNT_HANDLE, selectedAccountHandle);
+ result.putBoolean(SelectPhoneAccountListener.EXTRA_SET_DEFAULT, mIsDefaultChecked);
+ result.putString(SelectPhoneAccountListener.EXTRA_CALL_ID, getCallId());
+ if (mListener != null) {
+ mListener.onReceiveResult(SelectPhoneAccountListener.RESULT_SELECTED, result);
+ }
+ }
+ };
+
+ final CompoundButton.OnCheckedChangeListener checkListener =
+ new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton check, boolean isChecked) {
+ mIsDefaultChecked = isChecked;
+ }
+ };
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ ListAdapter selectAccountListAdapter =
+ new SelectAccountListAdapter(
+ builder.getContext(), R.layout.select_account_list_item, mAccountHandles);
+
+ AlertDialog dialog =
+ builder
+ .setTitle(mTitleResId)
+ .setAdapter(selectAccountListAdapter, selectionListener)
+ .create();
+
+ if (mCanSetDefault) {
+ // Generate custom checkbox view, lint suppressed since no appropriate parent (is dialog)
+ @SuppressLint("InflateParams")
+ LinearLayout checkboxLayout =
+ (LinearLayout)
+ LayoutInflater.from(builder.getContext())
+ .inflate(R.layout.default_account_checkbox, null);
+
+ CheckBox cb = (CheckBox) checkboxLayout.findViewById(R.id.default_account_checkbox_view);
+ cb.setOnCheckedChangeListener(checkListener);
+ cb.setChecked(mIsDefaultChecked);
+
+ dialog.getListView().addFooterView(checkboxLayout);
+ }
+
+ return dialog;
+ }
+
+ @Override
+ public void onStop() {
+ if (!mIsSelected && mListener != null) {
+ Bundle result = new Bundle();
+ result.putString(SelectPhoneAccountListener.EXTRA_CALL_ID, getCallId());
+ mListener.onReceiveResult(SelectPhoneAccountListener.RESULT_DISMISSED, result);
+ }
+ super.onStop();
+ }
+
+ @Nullable
+ private String getCallId() {
+ return getArguments().getString(ARG_CALL_ID);
+ }
+
+ public static class SelectPhoneAccountListener extends ResultReceiver {
+
+ static final int RESULT_SELECTED = 1;
+ static final int RESULT_DISMISSED = 2;
+
+ static final String EXTRA_SELECTED_ACCOUNT_HANDLE = "extra_selected_account_handle";
+ static final String EXTRA_SET_DEFAULT = "extra_set_default";
+ static final String EXTRA_CALL_ID = "extra_call_id";
+
+ public SelectPhoneAccountListener() {
+ super(new Handler());
+ }
+
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode == RESULT_SELECTED) {
+ onPhoneAccountSelected(
+ resultData.getParcelable(EXTRA_SELECTED_ACCOUNT_HANDLE),
+ resultData.getBoolean(EXTRA_SET_DEFAULT),
+ resultData.getString(EXTRA_CALL_ID));
+ } else if (resultCode == RESULT_DISMISSED) {
+ onDialogDismissed(resultData.getString(EXTRA_CALL_ID));
+ }
+ }
+
+ public void onPhoneAccountSelected(
+ PhoneAccountHandle selectedAccountHandle, boolean setDefault, @Nullable String callId) {}
+
+ public void onDialogDismissed(@Nullable String callId) {}
+ }
+
+ private static class SelectAccountListAdapter extends ArrayAdapter<PhoneAccountHandle> {
+
+ private int mResId;
+
+ public SelectAccountListAdapter(
+ Context context, int resource, List<PhoneAccountHandle> accountHandles) {
+ super(context, resource, accountHandles);
+ mResId = resource;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ LayoutInflater inflater =
+ (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ View rowView;
+ final ViewHolder holder;
+
+ if (convertView == null) {
+ // Cache views for faster scrolling
+ rowView = inflater.inflate(mResId, null);
+ holder = new ViewHolder();
+ holder.labelTextView = (TextView) rowView.findViewById(R.id.label);
+ holder.numberTextView = (TextView) rowView.findViewById(R.id.number);
+ holder.imageView = (ImageView) rowView.findViewById(R.id.icon);
+ rowView.setTag(holder);
+ } else {
+ rowView = convertView;
+ holder = (ViewHolder) rowView.getTag();
+ }
+
+ PhoneAccountHandle accountHandle = getItem(position);
+ PhoneAccount account =
+ getContext().getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
+ if (account == null) {
+ return rowView;
+ }
+ holder.labelTextView.setText(account.getLabel());
+ if (account.getAddress() == null
+ || TextUtils.isEmpty(account.getAddress().getSchemeSpecificPart())) {
+ holder.numberTextView.setVisibility(View.GONE);
+ } else {
+ holder.numberTextView.setVisibility(View.VISIBLE);
+ holder.numberTextView.setText(
+ PhoneNumberUtilsCompat.createTtsSpannable(
+ account.getAddress().getSchemeSpecificPart()));
+ }
+ holder.imageView.setImageDrawable(
+ PhoneAccountCompat.createIconDrawable(account, getContext()));
+ return rowView;
+ }
+
+ private static final class ViewHolder {
+
+ TextView labelTextView;
+ TextView numberTextView;
+ ImageView imageView;
+ }
+ }
+}
diff --git a/java/com/android/dialer/animation/AnimUtils.java b/java/com/android/dialer/animation/AnimUtils.java
new file mode 100644
index 000000000..9c9396e56
--- /dev/null
+++ b/java/com/android/dialer/animation/AnimUtils.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2014 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.dialer.animation;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+import android.view.animation.Interpolator;
+import com.android.dialer.compat.PathInterpolatorCompat;
+
+public class AnimUtils {
+
+ public static final int DEFAULT_DURATION = -1;
+ public static final int NO_DELAY = 0;
+
+ public static final Interpolator EASE_IN = PathInterpolatorCompat.create(0.0f, 0.0f, 0.2f, 1.0f);
+ public static final Interpolator EASE_OUT = PathInterpolatorCompat.create(0.4f, 0.0f, 1.0f, 1.0f);
+ public static final Interpolator EASE_OUT_EASE_IN =
+ PathInterpolatorCompat.create(0.4f, 0, 0.2f, 1);
+
+ public static void crossFadeViews(View fadeIn, View fadeOut, int duration) {
+ fadeIn(fadeIn, duration);
+ fadeOut(fadeOut, duration);
+ }
+
+ public static void fadeOut(View fadeOut, int duration) {
+ fadeOut(fadeOut, duration, null);
+ }
+
+ public static void fadeOut(final View fadeOut, int durationMs, final AnimationCallback callback) {
+ fadeOut.setAlpha(1);
+ final ViewPropertyAnimator animator = fadeOut.animate();
+ animator.cancel();
+ animator
+ .alpha(0)
+ .withLayer()
+ .setListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ fadeOut.setVisibility(View.GONE);
+ if (callback != null) {
+ callback.onAnimationEnd();
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ fadeOut.setVisibility(View.GONE);
+ fadeOut.setAlpha(0);
+ if (callback != null) {
+ callback.onAnimationCancel();
+ }
+ }
+ });
+ if (durationMs != DEFAULT_DURATION) {
+ animator.setDuration(durationMs);
+ }
+ animator.start();
+ }
+
+ public static void fadeIn(View fadeIn, int durationMs) {
+ fadeIn(fadeIn, durationMs, NO_DELAY, null);
+ }
+
+ public static void fadeIn(
+ final View fadeIn, int durationMs, int delay, final AnimationCallback callback) {
+ fadeIn.setAlpha(0);
+ final ViewPropertyAnimator animator = fadeIn.animate();
+ animator.cancel();
+
+ animator.setStartDelay(delay);
+ animator
+ .alpha(1)
+ .withLayer()
+ .setListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ fadeIn.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ fadeIn.setAlpha(1);
+ if (callback != null) {
+ callback.onAnimationCancel();
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (callback != null) {
+ callback.onAnimationEnd();
+ }
+ }
+ });
+ if (durationMs != DEFAULT_DURATION) {
+ animator.setDuration(durationMs);
+ }
+ animator.start();
+ }
+
+ /**
+ * Scales in the view from scale of 0 to actual dimensions.
+ *
+ * @param view The view to scale.
+ * @param durationMs The duration of the scaling in milliseconds.
+ * @param startDelayMs The delay to applying the scaling in milliseconds.
+ */
+ public static void scaleIn(final View view, int durationMs, int startDelayMs) {
+ AnimatorListenerAdapter listener =
+ (new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ view.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ view.setScaleX(1);
+ view.setScaleY(1);
+ }
+ });
+ scaleInternal(
+ view,
+ 0 /* startScaleValue */,
+ 1 /* endScaleValue */,
+ durationMs,
+ startDelayMs,
+ listener,
+ EASE_IN);
+ }
+
+ /**
+ * Scales out the view from actual dimensions to 0.
+ *
+ * @param view The view to scale.
+ * @param durationMs The duration of the scaling in milliseconds.
+ */
+ public static void scaleOut(final View view, int durationMs) {
+ AnimatorListenerAdapter listener =
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ view.setVisibility(View.GONE);
+ view.setScaleX(0);
+ view.setScaleY(0);
+ }
+ };
+
+ scaleInternal(
+ view,
+ 1 /* startScaleValue */,
+ 0 /* endScaleValue */,
+ durationMs,
+ NO_DELAY,
+ listener,
+ EASE_OUT);
+ }
+
+ private static void scaleInternal(
+ final View view,
+ int startScaleValue,
+ int endScaleValue,
+ int durationMs,
+ int startDelay,
+ AnimatorListenerAdapter listener,
+ Interpolator interpolator) {
+ view.setScaleX(startScaleValue);
+ view.setScaleY(startScaleValue);
+
+ final ViewPropertyAnimator animator = view.animate();
+ animator.cancel();
+
+ animator
+ .setInterpolator(interpolator)
+ .scaleX(endScaleValue)
+ .scaleY(endScaleValue)
+ .setListener(listener)
+ .withLayer();
+
+ if (durationMs != DEFAULT_DURATION) {
+ animator.setDuration(durationMs);
+ }
+ animator.setStartDelay(startDelay);
+
+ animator.start();
+ }
+
+ /**
+ * Animates a view to the new specified dimensions.
+ *
+ * @param view The view to change the dimensions of.
+ * @param newWidth The new width of the view.
+ * @param newHeight The new height of the view.
+ */
+ public static void changeDimensions(final View view, final int newWidth, final int newHeight) {
+ ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+
+ final int oldWidth = view.getWidth();
+ final int oldHeight = view.getHeight();
+ final int deltaWidth = newWidth - oldWidth;
+ final int deltaHeight = newHeight - oldHeight;
+
+ animator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ Float value = (Float) animator.getAnimatedValue();
+
+ view.getLayoutParams().width = (int) (value * deltaWidth + oldWidth);
+ view.getLayoutParams().height = (int) (value * deltaHeight + oldHeight);
+ view.requestLayout();
+ }
+ });
+ animator.start();
+ }
+
+ public static class AnimationCallback {
+
+ public void onAnimationEnd() {}
+
+ public void onAnimationCancel() {}
+ }
+}
diff --git a/java/com/android/dialer/animation/AnimationListenerAdapter.java b/java/com/android/dialer/animation/AnimationListenerAdapter.java
new file mode 100644
index 000000000..3f847f2b6
--- /dev/null
+++ b/java/com/android/dialer/animation/AnimationListenerAdapter.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 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.dialer.animation;
+
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+
+/**
+ * Provides empty implementations of the methods in {@link AnimationListener} for convenience
+ * reasons.
+ */
+public class AnimationListenerAdapter implements AnimationListener {
+
+ /** {@inheritDoc} */
+ @Override
+ public void onAnimationStart(Animation animation) {}
+
+ /** {@inheritDoc} */
+ @Override
+ public void onAnimationEnd(Animation animation) {}
+
+ /** {@inheritDoc} */
+ @Override
+ public void onAnimationRepeat(Animation animation) {}
+}
diff --git a/java/com/android/dialer/app/AndroidManifest.xml b/java/com/android/dialer/app/AndroidManifest.xml
new file mode 100644
index 000000000..80f294acc
--- /dev/null
+++ b/java/com/android/dialer/app/AndroidManifest.xml
@@ -0,0 +1,116 @@
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.dialer.app">
+
+ <uses-permission android:name="android.permission.CALL_PHONE"/>
+ <uses-permission android:name="android.permission.READ_CONTACTS"/>
+ <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+ <uses-permission android:name="android.permission.READ_CALL_LOG"/>
+ <uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
+ <uses-permission android:name="android.permission.READ_PROFILE"/>
+ <uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
+ <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
+ <uses-permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
+ <uses-permission android:name="android.permission.NFC"/>
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
+ <uses-permission android:name="android.permission.USE_CREDENTIALS"/>
+ <uses-permission android:name="android.permission.VIBRATE"/>
+ <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
+ <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL"/>
+ <uses-permission android:name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
+ <uses-permission android:name="com.android.voicemail.permission.READ_VOICEMAIL"/>
+ <uses-permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"/>
+ <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+ <uses-permission android:name="android.permission.BROADCAST_STICKY"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+
+ <!-- This tells the activity manager to not delay any of our activity
+ start requests, even if they happen immediately after the user
+ presses home. -->
+ <uses-permission android:name="android.permission.STOP_APP_SWITCHES"/>
+
+ <uses-sdk
+ android:minSdkVersion="23"
+ android:targetSdkVersion="25"/>
+
+ <application
+ android:backupAgent='com.android.dialer.backup.DialerBackupAgent'
+ android:fullBackupOnly="true"
+ android:restoreAnyVersion="true"
+ android:name="com.android.dialer.app.DialerApplication">
+
+ <activity
+ android:exported="false"
+ android:label="@string/manage_blocked_numbers_label"
+ android:name="com.android.dialer.app.filterednumber.BlockedNumbersSettingsActivity"
+ android:parentActivityName="com.android.dialer.app.settings.DialerSettingsActivity"
+ android:theme="@style/ManageBlockedNumbersStyle">
+ <intent-filter>
+ <action android:name="com.android.dialer.action.BLOCKED_NUMBERS_SETTINGS"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
+ <receiver android:name="com.android.dialer.app.calllog.CallLogReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.NEW_VOICEMAIL"/>
+ <data
+ android:host="com.android.voicemail"
+ android:mimeType="vnd.android.cursor.item/voicemail"
+ android:scheme="content"
+ />
+ </intent-filter>
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.BOOT_COMPLETED"/>
+ </intent-filter>
+ </receiver>
+
+ <service
+ android:directBootAware="true"
+ android:exported="false"
+ android:name="com.android.dialer.app.calllog.CallLogNotificationsService"
+ />
+
+ <receiver
+ android:directBootAware="true"
+ android:name="com.android.dialer.app.calllog.MissedCallNotificationReceiver">
+ <intent-filter>
+ <action android:name="android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION"/>
+ </intent-filter>
+ </receiver>
+
+ <provider
+ android:authorities="com.android.dialer.files"
+ android:exported="false"
+ android:grantUriPermissions="true"
+ android:name="android.support.v4.content.FileProvider">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/file_paths"/>
+ </provider>
+ </application>
+</manifest>
diff --git a/java/com/android/dialer/app/Bindings.java b/java/com/android/dialer/app/Bindings.java
new file mode 100644
index 000000000..2beb40184
--- /dev/null
+++ b/java/com/android/dialer/app/Bindings.java
@@ -0,0 +1,77 @@
+/*
+ * 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.dialer.app;
+
+import android.content.Context;
+import com.android.dialer.app.bindings.DialerBindings;
+import com.android.dialer.app.bindings.DialerBindingsFactory;
+import com.android.dialer.app.bindings.DialerBindingsStub;
+import com.android.dialer.app.legacybindings.DialerLegacyBindings;
+import com.android.dialer.app.legacybindings.DialerLegacyBindingsFactory;
+import com.android.dialer.app.legacybindings.DialerLegacyBindingsStub;
+import java.util.Objects;
+
+/** Accessor for the in call UI bindings. */
+public class Bindings {
+
+ private static DialerBindings instance;
+ private static DialerLegacyBindings legacyInstance;
+
+ private Bindings() {}
+
+ public static DialerBindings get(Context context) {
+ Objects.requireNonNull(context);
+ if (instance != null) {
+ return instance;
+ }
+
+ Context application = context.getApplicationContext();
+ if (application instanceof DialerBindingsFactory) {
+ instance = ((DialerBindingsFactory) application).newDialerBindings();
+ }
+
+ if (instance == null) {
+ instance = new DialerBindingsStub();
+ }
+ return instance;
+ }
+
+ public static DialerLegacyBindings getLegacy(Context context) {
+ Objects.requireNonNull(context);
+ if (legacyInstance != null) {
+ return legacyInstance;
+ }
+
+ Context application = context.getApplicationContext();
+ if (application instanceof DialerLegacyBindingsFactory) {
+ legacyInstance = ((DialerLegacyBindingsFactory) application).newDialerLegacyBindings();
+ }
+
+ if (legacyInstance == null) {
+ legacyInstance = new DialerLegacyBindingsStub();
+ }
+ return legacyInstance;
+ }
+
+ public static void setForTesting(DialerBindings testInstance) {
+ instance = testInstance;
+ }
+
+ public static void setLegacyBindingForTesting(DialerLegacyBindings testLegacyInstance) {
+ legacyInstance = testLegacyInstance;
+ }
+}
diff --git a/java/com/android/dialer/app/CallDetailActivity.java b/java/com/android/dialer/app/CallDetailActivity.java
new file mode 100644
index 000000000..cda2b2e2c
--- /dev/null
+++ b/java/com/android/dialer/app/CallDetailActivity.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2009 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.dialer.app;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.support.v7.app.AppCompatActivity;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.ListView;
+import android.widget.QuickContactBadge;
+import android.widget.TextView;
+import android.widget.Toast;
+import com.android.contacts.common.ClipboardUtils;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.preference.ContactsPreferences;
+import com.android.contacts.common.util.UriUtils;
+import com.android.dialer.app.calllog.CallDetailHistoryAdapter;
+import com.android.dialer.app.calllog.CallLogAsyncTaskUtil;
+import com.android.dialer.app.calllog.CallLogAsyncTaskUtil.CallLogAsyncTaskListener;
+import com.android.dialer.app.calllog.CallTypeHelper;
+import com.android.dialer.app.calllog.PhoneAccountUtils;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.callintent.nano.CallInitiationType;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.AsyncTaskExecutor;
+import com.android.dialer.common.AsyncTaskExecutors;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.logging.nano.ScreenEvent;
+import com.android.dialer.phonenumbercache.ContactInfoHelper;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.android.dialer.proguard.UsedByReflection;
+import com.android.dialer.spam.Spam;
+import com.android.dialer.telecom.TelecomUtil;
+import com.android.dialer.util.CallUtil;
+import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.TouchPointManager;
+
+/**
+ * Displays the details of a specific call log entry.
+ *
+ * <p>This activity can be either started with the URI of a single call log entry, or with the
+ * {@link #EXTRA_CALL_LOG_IDS} extra to specify a group of call log entries.
+ */
+@UsedByReflection(value = "AndroidManifest-app.xml")
+public class CallDetailActivity extends AppCompatActivity
+ implements MenuItem.OnMenuItemClickListener, View.OnClickListener {
+
+ /** A long array extra containing ids of call log entries to display. */
+ public static final String EXTRA_CALL_LOG_IDS = "EXTRA_CALL_LOG_IDS";
+ /** If we are started with a voicemail, we'll find the uri to play with this extra. */
+ public static final String EXTRA_VOICEMAIL_URI = "EXTRA_VOICEMAIL_URI";
+ /** If the activity was triggered from a notification. */
+ public static final String EXTRA_FROM_NOTIFICATION = "EXTRA_FROM_NOTIFICATION";
+
+ public static final String BLOCKED_OR_SPAM_QUERY_IDENTIFIER = "blockedOrSpamIdentifier";
+
+ private final AsyncTaskExecutor executor = AsyncTaskExecutors.createAsyncTaskExecutor();
+ protected String mNumber;
+ private Context mContext;
+ private ContactInfoHelper mContactInfoHelper;
+ private ContactsPreferences mContactsPreferences;
+ private CallTypeHelper mCallTypeHelper;
+ private ContactPhotoManager mContactPhotoManager;
+ private BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
+ private LayoutInflater mInflater;
+ private Resources mResources;
+ private PhoneCallDetails mDetails;
+ private Uri mVoicemailUri;
+ private String mPostDialDigits = "";
+ private ListView mHistoryList;
+ private QuickContactBadge mQuickContactBadge;
+ private TextView mCallerName;
+ private TextView mCallerNumber;
+ private TextView mAccountLabel;
+ private View mCallButton;
+ private View mEditBeforeCallActionItem;
+ private View mReportActionItem;
+ private View mCopyNumberActionItem;
+ private FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
+ private CallLogAsyncTaskListener mCallLogAsyncTaskListener =
+ new CallLogAsyncTaskListener() {
+ @Override
+ public void onDeleteCall() {
+ finish();
+ }
+
+ @Override
+ public void onDeleteVoicemail() {
+ finish();
+ }
+
+ @Override
+ public void onGetCallDetails(final PhoneCallDetails[] details) {
+ if (details == null) {
+ // Somewhere went wrong: we're going to bail out and show error to users.
+ Toast.makeText(mContext, R.string.toast_call_detail_error, Toast.LENGTH_SHORT).show();
+ finish();
+ return;
+ }
+
+ // All calls are from the same number and same contact, so pick the first detail.
+ mDetails = details[0];
+ mNumber = TextUtils.isEmpty(mDetails.number) ? null : mDetails.number.toString();
+
+ if (mNumber == null) {
+ updateDataAndRender(details);
+ return;
+ }
+
+ executor.submit(
+ BLOCKED_OR_SPAM_QUERY_IDENTIFIER,
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ mDetails.isBlocked =
+ mFilteredNumberAsyncQueryHandler.getBlockedIdSynchronousForCalllogOnly(
+ mNumber, mDetails.countryIso)
+ != null;
+ if (Spam.get(mContext).isSpamEnabled()) {
+ mDetails.isSpam =
+ hasIncomingCalls(details)
+ && Spam.get(mContext)
+ .checkSpamStatusSynchronous(mNumber, mDetails.countryIso);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ updateDataAndRender(details);
+ }
+ });
+ }
+
+ private void updateDataAndRender(PhoneCallDetails[] details) {
+ mPostDialDigits =
+ TextUtils.isEmpty(mDetails.postDialDigits) ? "" : mDetails.postDialDigits;
+
+ final CharSequence callLocationOrType = getNumberTypeOrLocation(mDetails);
+
+ final CharSequence displayNumber;
+ if (!TextUtils.isEmpty(mDetails.postDialDigits)) {
+ displayNumber = mDetails.number + mDetails.postDialDigits;
+ } else {
+ displayNumber = mDetails.displayNumber;
+ }
+
+ final String displayNumberStr =
+ mBidiFormatter.unicodeWrap(displayNumber.toString(), TextDirectionHeuristics.LTR);
+
+ mDetails.nameDisplayOrder = mContactsPreferences.getDisplayOrder();
+
+ if (!TextUtils.isEmpty(mDetails.getPreferredName())) {
+ mCallerName.setText(mDetails.getPreferredName());
+ mCallerNumber.setText(callLocationOrType + " " + displayNumberStr);
+ } else {
+ mCallerName.setText(displayNumberStr);
+ if (!TextUtils.isEmpty(callLocationOrType)) {
+ mCallerNumber.setText(callLocationOrType);
+ mCallerNumber.setVisibility(View.VISIBLE);
+ } else {
+ mCallerNumber.setVisibility(View.GONE);
+ }
+ }
+
+ CharSequence accountLabel =
+ PhoneAccountUtils.getAccountLabel(mContext, mDetails.accountHandle);
+ CharSequence accountContentDescription =
+ PhoneCallDetails.createAccountLabelDescription(
+ mResources, mDetails.viaNumber, accountLabel);
+ if (!TextUtils.isEmpty(mDetails.viaNumber)) {
+ if (!TextUtils.isEmpty(accountLabel)) {
+ accountLabel =
+ mResources.getString(
+ R.string.call_log_via_number_phone_account, accountLabel, mDetails.viaNumber);
+ } else {
+ accountLabel = mResources.getString(R.string.call_log_via_number, mDetails.viaNumber);
+ }
+ }
+ if (!TextUtils.isEmpty(accountLabel)) {
+ mAccountLabel.setText(accountLabel);
+ mAccountLabel.setContentDescription(accountContentDescription);
+ mAccountLabel.setVisibility(View.VISIBLE);
+ } else {
+ mAccountLabel.setVisibility(View.GONE);
+ }
+
+ final boolean canPlaceCallsTo =
+ PhoneNumberHelper.canPlaceCallsTo(mNumber, mDetails.numberPresentation);
+ mCallButton.setVisibility(canPlaceCallsTo ? View.VISIBLE : View.GONE);
+ mCopyNumberActionItem.setVisibility(canPlaceCallsTo ? View.VISIBLE : View.GONE);
+
+ final boolean isSipNumber = PhoneNumberHelper.isSipNumber(mNumber);
+ final boolean isVoicemailNumber =
+ PhoneNumberHelper.isVoicemailNumber(mContext, mDetails.accountHandle, mNumber);
+ final boolean showEditNumberBeforeCallAction =
+ canPlaceCallsTo && !isSipNumber && !isVoicemailNumber;
+ mEditBeforeCallActionItem.setVisibility(
+ showEditNumberBeforeCallAction ? View.VISIBLE : View.GONE);
+
+ final boolean showReportAction =
+ mContactInfoHelper.canReportAsInvalid(mDetails.sourceType, mDetails.objectId);
+ mReportActionItem.setVisibility(showReportAction ? View.VISIBLE : View.GONE);
+
+ invalidateOptionsMenu();
+
+ mHistoryList.setAdapter(
+ new CallDetailHistoryAdapter(mContext, mInflater, mCallTypeHelper, details));
+
+ updateContactPhoto(mDetails.isSpam);
+
+ findViewById(R.id.call_detail).setVisibility(View.VISIBLE);
+ }
+
+ /**
+ * Determines the location geocode text for a call, or the phone number type (if available).
+ *
+ * @param details The call details.
+ * @return The phone number type or location.
+ */
+ private CharSequence getNumberTypeOrLocation(PhoneCallDetails details) {
+ if (details.isSpam) {
+ return mResources.getString(R.string.spam_number_call_log_label);
+ } else if (details.isBlocked) {
+ return mResources.getString(R.string.blocked_number_call_log_label);
+ } else if (!TextUtils.isEmpty(details.namePrimary)) {
+ return Phone.getTypeLabel(mResources, details.numberType, details.numberLabel);
+ } else {
+ return details.geocode;
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ mContext = this;
+ mResources = getResources();
+ mContactInfoHelper = new ContactInfoHelper(this, GeoUtil.getCurrentCountryIso(this));
+ mContactsPreferences = new ContactsPreferences(mContext);
+ mCallTypeHelper = new CallTypeHelper(getResources());
+ mFilteredNumberAsyncQueryHandler = new FilteredNumberAsyncQueryHandler(mContext);
+
+ mVoicemailUri = getIntent().getParcelableExtra(EXTRA_VOICEMAIL_URI);
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ setContentView(R.layout.call_detail);
+ mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
+
+ mHistoryList = (ListView) findViewById(R.id.history);
+ mHistoryList.addHeaderView(mInflater.inflate(R.layout.call_detail_header, null));
+ mHistoryList.addFooterView(mInflater.inflate(R.layout.call_detail_footer, null), null, false);
+
+ mQuickContactBadge = (QuickContactBadge) findViewById(R.id.quick_contact_photo);
+ mQuickContactBadge.setOverlay(null);
+ if (CompatUtils.hasPrioritizedMimeType()) {
+ mQuickContactBadge.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE);
+ }
+ mCallerName = (TextView) findViewById(R.id.caller_name);
+ mCallerNumber = (TextView) findViewById(R.id.caller_number);
+ mAccountLabel = (TextView) findViewById(R.id.phone_account_label);
+ mContactPhotoManager = ContactPhotoManager.getInstance(this);
+
+ mCallButton = findViewById(R.id.call_back_button);
+ mCallButton.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (TextUtils.isEmpty(mNumber)) {
+ return;
+ }
+ DialerUtils.startActivityWithErrorToast(
+ CallDetailActivity.this,
+ new CallIntentBuilder(getDialableNumber(), CallInitiationType.Type.CALL_DETAILS)
+ .build());
+ }
+ });
+
+ mEditBeforeCallActionItem = findViewById(R.id.call_detail_action_edit_before_call);
+ mEditBeforeCallActionItem.setOnClickListener(this);
+ mReportActionItem = findViewById(R.id.call_detail_action_report);
+ mReportActionItem.setOnClickListener(this);
+
+ mCopyNumberActionItem = findViewById(R.id.call_detail_action_copy);
+ mCopyNumberActionItem.setOnClickListener(this);
+
+ if (getIntent().getBooleanExtra(EXTRA_FROM_NOTIFICATION, false)) {
+ closeSystemDialogs();
+ }
+
+ Logger.get(this).logScreenView(ScreenEvent.Type.CALL_DETAILS, this);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
+ getCallDetails();
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ TouchPointManager.getInstance().setPoint((int) ev.getRawX(), (int) ev.getRawY());
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
+ public void getCallDetails() {
+ CallLogAsyncTaskUtil.getCallDetails(this, mCallLogAsyncTaskListener, getCallLogEntryUris());
+ }
+
+ /**
+ * Returns the list of URIs to show.
+ *
+ * <p>There are two ways the URIs can be provided to the activity: as the data on the intent, or
+ * as a list of ids in the call log added as an extra on the URI.
+ *
+ * <p>If both are available, the data on the intent takes precedence.
+ */
+ private Uri[] getCallLogEntryUris() {
+ final Uri uri = getIntent().getData();
+ if (uri != null) {
+ // If there is a data on the intent, it takes precedence over the extra.
+ return new Uri[] {uri};
+ }
+ final long[] ids = getIntent().getLongArrayExtra(EXTRA_CALL_LOG_IDS);
+ final int numIds = ids == null ? 0 : ids.length;
+ final Uri[] uris = new Uri[numIds];
+ for (int index = 0; index < numIds; ++index) {
+ uris[index] =
+ ContentUris.withAppendedId(
+ TelecomUtil.getCallLogUri(CallDetailActivity.this), ids[index]);
+ }
+ return uris;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ final MenuItem deleteMenuItem =
+ menu.add(
+ Menu.NONE, R.id.call_detail_delete_menu_item, Menu.NONE, R.string.call_details_delete);
+ deleteMenuItem.setIcon(R.drawable.ic_delete_24dp);
+ deleteMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ deleteMenuItem.setOnMenuItemClickListener(this);
+
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ if (item.getItemId() == R.id.call_detail_delete_menu_item) {
+ Logger.get(mContext).logImpression(DialerImpression.Type.USER_DELETED_CALL_LOG_ITEM);
+ if (hasVoicemail()) {
+ CallLogAsyncTaskUtil.deleteVoicemail(this, mVoicemailUri, mCallLogAsyncTaskListener);
+ } else {
+ final StringBuilder callIds = new StringBuilder();
+ for (Uri callUri : getCallLogEntryUris()) {
+ if (callIds.length() != 0) {
+ callIds.append(",");
+ }
+ callIds.append(ContentUris.parseId(callUri));
+ }
+ CallLogAsyncTaskUtil.deleteCalls(this, callIds.toString(), mCallLogAsyncTaskListener);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void onClick(View view) {
+ int resId = view.getId();
+ if (resId == R.id.call_detail_action_copy) {
+ ClipboardUtils.copyText(mContext, null, mNumber, true);
+ } else if (resId == R.id.call_detail_action_edit_before_call) {
+ Intent dialIntent = new Intent(Intent.ACTION_DIAL, CallUtil.getCallUri(getDialableNumber()));
+ DialerUtils.startActivityWithErrorToast(mContext, dialIntent);
+ } else {
+ Assert.fail("Unexpected onClick event from " + view);
+ }
+ }
+
+ // Loads and displays the contact photo.
+ private void updateContactPhoto(boolean isSpam) {
+ if (mDetails == null) {
+ return;
+ }
+
+ mQuickContactBadge.assignContactUri(mDetails.contactUri);
+ final String displayName =
+ TextUtils.isEmpty(mDetails.namePrimary)
+ ? mDetails.displayNumber
+ : mDetails.namePrimary.toString();
+ mQuickContactBadge.setContentDescription(
+ mResources.getString(R.string.description_contact_details, displayName));
+
+ final boolean isVoicemailNumber =
+ PhoneNumberHelper.isVoicemailNumber(mContext, mDetails.accountHandle, mNumber);
+ if (isSpam) {
+ mQuickContactBadge.setImageDrawable(mContext.getDrawable(R.drawable.blocked_contact));
+ return;
+ }
+
+ final boolean isBusiness = mContactInfoHelper.isBusiness(mDetails.sourceType);
+ int contactType = ContactPhotoManager.TYPE_DEFAULT;
+ if (isVoicemailNumber) {
+ contactType = ContactPhotoManager.TYPE_VOICEMAIL;
+ } else if (isBusiness) {
+ contactType = ContactPhotoManager.TYPE_BUSINESS;
+ }
+
+ final String lookupKey =
+ mDetails.contactUri == null ? null : UriUtils.getLookupKeyFromUri(mDetails.contactUri);
+
+ final DefaultImageRequest request =
+ new DefaultImageRequest(displayName, lookupKey, contactType, true /* isCircular */);
+
+ mContactPhotoManager.loadDirectoryPhoto(
+ mQuickContactBadge,
+ mDetails.photoUri,
+ false /* darkTheme */,
+ true /* isCircular */,
+ request);
+ }
+
+ private void closeSystemDialogs() {
+ sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ }
+
+ private String getDialableNumber() {
+ return mNumber + mPostDialDigits;
+ }
+
+ public boolean hasVoicemail() {
+ return mVoicemailUri != null;
+ }
+
+ private static boolean hasIncomingCalls(PhoneCallDetails[] details) {
+ for (int i = 0; i < details.length; i++) {
+ if (details[i].hasIncomingCalls()) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/java/com/android/dialer/app/DialerApplication.java b/java/com/android/dialer/app/DialerApplication.java
new file mode 100644
index 000000000..3b979212b
--- /dev/null
+++ b/java/com/android/dialer/app/DialerApplication.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 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.dialer.app;
+
+import android.app.Application;
+import android.os.Trace;
+import android.preference.PreferenceManager;
+import com.android.dialer.blocking.BlockedNumbersAutoMigrator;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.enrichedcall.EnrichedCallManager;
+import com.android.dialer.inject.ApplicationModule;
+import com.android.dialer.inject.DaggerDialerAppComponent;
+import com.android.dialer.inject.DialerAppComponent;
+
+public class DialerApplication extends Application implements EnrichedCallManager.Factory {
+
+ private static final String TAG = "DialerApplication";
+
+ private volatile DialerAppComponent component;
+
+ @Override
+ public void onCreate() {
+ Trace.beginSection(TAG + " onCreate");
+ super.onCreate();
+ new BlockedNumbersAutoMigrator(
+ this,
+ PreferenceManager.getDefaultSharedPreferences(this),
+ new FilteredNumberAsyncQueryHandler(this))
+ .autoMigrate();
+ Trace.endSection();
+ }
+
+ @Override
+ public EnrichedCallManager getEnrichedCallManager() {
+ return component().enrichedCallManager();
+ }
+
+ protected DialerAppComponent buildApplicationComponent() {
+ return DaggerDialerAppComponent.builder()
+ .applicationModule(new ApplicationModule(this))
+ .build();
+ }
+
+ /**
+ * Returns the application component.
+ *
+ * <p>A single Component is created per application instance. Note that it won't be instantiated
+ * until it's first requested, but guarantees that only one will ever be created.
+ */
+ private final DialerAppComponent component() {
+ // Double-check idiom for lazy initialization
+ DialerAppComponent result = component;
+ if (result == null) {
+ synchronized (this) {
+ result = component;
+ if (result == null) {
+ component = result = buildApplicationComponent();
+ }
+ }
+ }
+ return result;
+ }
+}
diff --git a/java/com/android/dialer/app/DialtactsActivity.java b/java/com/android/dialer/app/DialtactsActivity.java
new file mode 100644
index 000000000..4c57cda70
--- /dev/null
+++ b/java/com/android/dialer/app/DialtactsActivity.java
@@ -0,0 +1,1484 @@
+/*
+ * Copyright (C) 2013 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.dialer.app;
+
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Trace;
+import android.provider.CallLog.Calls;
+import android.speech.RecognizerIntent;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
+import android.support.design.widget.CoordinatorLayout;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.ActionBar;
+import android.telecom.PhoneAccount;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.view.DragEvent;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnDragListener;
+import android.view.ViewTreeObserver;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.PopupMenu;
+import android.widget.TextView;
+import android.widget.Toast;
+import com.android.contacts.common.dialog.ClearFrequentsDialog;
+import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
+import com.android.contacts.common.list.PhoneNumberListAdapter;
+import com.android.contacts.common.list.PhoneNumberListAdapter.PhoneQuery;
+import com.android.contacts.common.list.PhoneNumberPickerFragment.CursorReranker;
+import com.android.contacts.common.list.PhoneNumberPickerFragment.OnLoadFinishedListener;
+import com.android.contacts.common.widget.FloatingActionButtonController;
+import com.android.dialer.animation.AnimUtils;
+import com.android.dialer.animation.AnimationListenerAdapter;
+import com.android.dialer.app.calllog.CallLogFragment;
+import com.android.dialer.app.calllog.CallLogNotificationsService;
+import com.android.dialer.app.calllog.ClearCallLogDialog;
+import com.android.dialer.app.dialpad.DialpadFragment;
+import com.android.dialer.app.list.DragDropController;
+import com.android.dialer.app.list.ListsFragment;
+import com.android.dialer.app.list.OnDragDropListener;
+import com.android.dialer.app.list.OnListFragmentScrolledListener;
+import com.android.dialer.app.list.PhoneFavoriteSquareTileView;
+import com.android.dialer.app.list.RegularSearchFragment;
+import com.android.dialer.app.list.SearchFragment;
+import com.android.dialer.app.list.SmartDialSearchFragment;
+import com.android.dialer.app.list.SpeedDialFragment;
+import com.android.dialer.app.settings.DialerSettingsActivity;
+import com.android.dialer.app.widget.ActionBarController;
+import com.android.dialer.app.widget.SearchEditTextLayout;
+import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.callintent.nano.CallSpecificAppData;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.database.Database;
+import com.android.dialer.database.DialerDatabaseHelper;
+import com.android.dialer.interactions.PhoneNumberInteraction;
+import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorCode;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.logging.nano.ScreenEvent;
+import com.android.dialer.p13n.inference.P13nRanking;
+import com.android.dialer.p13n.inference.protocol.P13nRanker;
+import com.android.dialer.p13n.inference.protocol.P13nRanker.P13nRefreshCompleteListener;
+import com.android.dialer.p13n.logging.P13nLogger;
+import com.android.dialer.p13n.logging.P13nLogging;
+import com.android.dialer.proguard.UsedByReflection;
+import com.android.dialer.smartdial.SmartDialNameMatcher;
+import com.android.dialer.smartdial.SmartDialPrefix;
+import com.android.dialer.telecom.TelecomUtil;
+import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.IntentUtil;
+import com.android.dialer.util.PermissionsUtil;
+import com.android.dialer.util.TouchPointManager;
+import com.android.dialer.util.TransactionSafeActivity;
+import com.android.dialer.util.ViewUtil;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+/** The dialer tab's title is 'phone', a more common name (see strings.xml). */
+@UsedByReflection(value = "AndroidManifest-app.xml")
+public class DialtactsActivity extends TransactionSafeActivity
+ implements View.OnClickListener,
+ DialpadFragment.OnDialpadQueryChangedListener,
+ OnListFragmentScrolledListener,
+ CallLogFragment.HostInterface,
+ DialpadFragment.HostInterface,
+ ListsFragment.HostInterface,
+ SpeedDialFragment.HostInterface,
+ SearchFragment.HostInterface,
+ OnDragDropListener,
+ OnPhoneNumberPickerActionListener,
+ PopupMenu.OnMenuItemClickListener,
+ ViewPager.OnPageChangeListener,
+ ActionBarController.ActivityUi,
+ PhoneNumberInteraction.InteractionErrorListener,
+ PhoneNumberInteraction.DisambigDialogDismissedListener,
+ ActivityCompat.OnRequestPermissionsResultCallback {
+
+ public static final boolean DEBUG = false;
+ @VisibleForTesting public static final String TAG_DIALPAD_FRAGMENT = "dialpad";
+ private static final String ACTION_SHOW_TAB = "ACTION_SHOW_TAB";
+ @VisibleForTesting public static final String EXTRA_SHOW_TAB = "EXTRA_SHOW_TAB";
+ public static final String EXTRA_CLEAR_NEW_VOICEMAILS = "EXTRA_CLEAR_NEW_VOICEMAILS";
+ private static final String TAG = "DialtactsActivity";
+ private static final String KEY_IN_REGULAR_SEARCH_UI = "in_regular_search_ui";
+ private static final String KEY_IN_DIALPAD_SEARCH_UI = "in_dialpad_search_ui";
+ private static final String KEY_SEARCH_QUERY = "search_query";
+ private static final String KEY_FIRST_LAUNCH = "first_launch";
+ private static final String KEY_WAS_CONFIGURATION_CHANGE = "was_configuration_change";
+ private static final String KEY_IS_DIALPAD_SHOWN = "is_dialpad_shown";
+ private static final String TAG_REGULAR_SEARCH_FRAGMENT = "search";
+ private static final String TAG_SMARTDIAL_SEARCH_FRAGMENT = "smartdial";
+ private static final String TAG_FAVORITES_FRAGMENT = "favorites";
+ /** Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}. */
+ private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
+
+ private static final int ACTIVITY_REQUEST_CODE_VOICE_SEARCH = 1;
+ public static final int ACTIVITY_REQUEST_CODE_CALL_COMPOSE = 2;
+
+ private static final int FAB_SCALE_IN_DELAY_MS = 300;
+ /** Fragment containing the dialpad that slides into view */
+ protected DialpadFragment mDialpadFragment;
+
+ private CoordinatorLayout mParentLayout;
+ /** Fragment for searching phone numbers using the alphanumeric keyboard. */
+ private RegularSearchFragment mRegularSearchFragment;
+
+ /** Fragment for searching phone numbers using the dialpad. */
+ private SmartDialSearchFragment mSmartDialSearchFragment;
+
+ /** Animation that slides in. */
+ private Animation mSlideIn;
+
+ /** Animation that slides out. */
+ private Animation mSlideOut;
+ /** Fragment containing the speed dial list, call history list, and all contacts list. */
+ private ListsFragment mListsFragment;
+ /**
+ * Tracks whether onSaveInstanceState has been called. If true, no fragment transactions can be
+ * commited.
+ */
+ private boolean mStateSaved;
+
+ private boolean mIsRestarting;
+ private boolean mInDialpadSearch;
+ private boolean mInRegularSearch;
+ private boolean mClearSearchOnPause;
+ private boolean mIsDialpadShown;
+ private boolean mShowDialpadOnResume;
+ /** Whether or not the device is in landscape orientation. */
+ private boolean mIsLandscape;
+ /** True if the dialpad is only temporarily showing due to being in call */
+ private boolean mInCallDialpadUp;
+ /** True when this activity has been launched for the first time. */
+ private boolean mFirstLaunch;
+ /**
+ * Search query to be applied to the SearchView in the ActionBar once onCreateOptionsMenu has been
+ * called.
+ */
+ private String mPendingSearchViewQuery;
+
+ private PopupMenu mOverflowMenu;
+ private EditText mSearchView;
+ private View mVoiceSearchButton;
+ private String mSearchQuery;
+ private String mDialpadQuery;
+ private DialerDatabaseHelper mDialerDatabaseHelper;
+ private DragDropController mDragDropController;
+ private ActionBarController mActionBarController;
+ private FloatingActionButtonController mFloatingActionButtonController;
+ private boolean mWasConfigurationChange;
+
+ private P13nLogger mP13nLogger;
+ private P13nRanker mP13nRanker;
+
+ AnimationListenerAdapter mSlideInListener =
+ new AnimationListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ maybeEnterSearchUi();
+ }
+ };
+ /** Listener for after slide out animation completes on dialer fragment. */
+ AnimationListenerAdapter mSlideOutListener =
+ new AnimationListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ commitDialpadFragmentHide();
+ }
+ };
+ /** Listener used to send search queries to the phone search fragment. */
+ private final TextWatcher mPhoneSearchQueryTextListener =
+ new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ final String newText = s.toString();
+ if (newText.equals(mSearchQuery)) {
+ // If the query hasn't changed (perhaps due to activity being destroyed
+ // and restored, or user launching the same DIAL intent twice), then there is
+ // no need to do anything here.
+ return;
+ }
+ if (DEBUG) {
+ LogUtil.v("DialtactsActivity.onTextChanged", "called with new query: " + newText);
+ LogUtil.v("DialtactsActivity.onTextChanged", "previous query: " + mSearchQuery);
+ }
+ mSearchQuery = newText;
+
+ // Show search fragment only when the query string is changed to non-empty text.
+ if (!TextUtils.isEmpty(newText)) {
+ // Call enterSearchUi only if we are switching search modes, or showing a search
+ // fragment for the first time.
+ final boolean sameSearchMode =
+ (mIsDialpadShown && mInDialpadSearch) || (!mIsDialpadShown && mInRegularSearch);
+ if (!sameSearchMode) {
+ enterSearchUi(mIsDialpadShown, mSearchQuery, true /* animate */);
+ }
+ }
+
+ if (mSmartDialSearchFragment != null && mSmartDialSearchFragment.isVisible()) {
+ mSmartDialSearchFragment.setQueryString(mSearchQuery);
+ } else if (mRegularSearchFragment != null && mRegularSearchFragment.isVisible()) {
+ mRegularSearchFragment.setQueryString(mSearchQuery);
+ }
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {}
+ };
+ /** Open the search UI when the user clicks on the search box. */
+ private final View.OnClickListener mSearchViewOnClickListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!isInSearchUi()) {
+ mActionBarController.onSearchBoxTapped();
+ enterSearchUi(
+ false /* smartDialSearch */, mSearchView.getText().toString(), true /* animate */);
+ }
+ }
+ };
+
+ private int mActionBarHeight;
+ private int mPreviouslySelectedTabIndex;
+ /** Handles the user closing the soft keyboard. */
+ private final View.OnKeyListener mSearchEditTextLayoutListener =
+ new View.OnKeyListener() {
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
+ if (TextUtils.isEmpty(mSearchView.getText().toString())) {
+ // If the search term is empty, close the search UI.
+ maybeExitSearchUi();
+ } else {
+ // If the search term is not empty, show the dialpad fab.
+ showFabInSearchUi();
+ }
+ }
+ return false;
+ }
+ };
+ /**
+ * The text returned from a voice search query. Set in {@link #onActivityResult} and used in
+ * {@link #onResume()} to populate the search box.
+ */
+ private String mVoiceSearchQuery;
+
+ /**
+ * @param tab the TAB_INDEX_* constant in {@link ListsFragment}
+ * @return A intent that will open the DialtactsActivity into the specified tab. The intent for
+ * each tab will be unique.
+ */
+ public static Intent getShowTabIntent(Context context, int tab) {
+ Intent intent = new Intent(context, DialtactsActivity.class);
+ intent.setAction(ACTION_SHOW_TAB);
+ intent.putExtra(DialtactsActivity.EXTRA_SHOW_TAB, tab);
+ intent.setData(
+ new Uri.Builder()
+ .scheme("intent")
+ .authority(context.getPackageName())
+ .appendPath(TAG)
+ .appendQueryParameter(DialtactsActivity.EXTRA_SHOW_TAB, String.valueOf(tab))
+ .build());
+
+ return intent;
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ TouchPointManager.getInstance().setPoint((int) ev.getRawX(), (int) ev.getRawY());
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Trace.beginSection(TAG + " onCreate");
+ super.onCreate(savedInstanceState);
+
+ mFirstLaunch = true;
+
+ final Resources resources = getResources();
+ mActionBarHeight = resources.getDimensionPixelSize(R.dimen.action_bar_height_large);
+
+ Trace.beginSection(TAG + " setContentView");
+ setContentView(R.layout.dialtacts_activity);
+ Trace.endSection();
+ getWindow().setBackgroundDrawable(null);
+
+ Trace.beginSection(TAG + " setup Views");
+ final ActionBar actionBar = getActionBarSafely();
+ actionBar.setCustomView(R.layout.search_edittext);
+ actionBar.setDisplayShowCustomEnabled(true);
+ actionBar.setBackgroundDrawable(null);
+
+ SearchEditTextLayout searchEditTextLayout =
+ (SearchEditTextLayout) actionBar.getCustomView().findViewById(R.id.search_view_container);
+ searchEditTextLayout.setPreImeKeyListener(mSearchEditTextLayoutListener);
+
+ mActionBarController = new ActionBarController(this, searchEditTextLayout);
+
+ mSearchView = (EditText) searchEditTextLayout.findViewById(R.id.search_view);
+ mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
+ mVoiceSearchButton = searchEditTextLayout.findViewById(R.id.voice_search_button);
+ searchEditTextLayout
+ .findViewById(R.id.search_magnifying_glass)
+ .setOnClickListener(mSearchViewOnClickListener);
+ searchEditTextLayout
+ .findViewById(R.id.search_box_start_search)
+ .setOnClickListener(mSearchViewOnClickListener);
+ searchEditTextLayout.setOnClickListener(mSearchViewOnClickListener);
+ searchEditTextLayout.setCallback(
+ new SearchEditTextLayout.Callback() {
+ @Override
+ public void onBackButtonClicked() {
+ onBackPressed();
+ }
+
+ @Override
+ public void onSearchViewClicked() {
+ // Hide FAB, as the keyboard is shown.
+ mFloatingActionButtonController.scaleOut();
+ }
+ });
+
+ mIsLandscape =
+ getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
+ mPreviouslySelectedTabIndex = ListsFragment.TAB_INDEX_SPEED_DIAL;
+ final View floatingActionButtonContainer = findViewById(R.id.floating_action_button_container);
+ ImageButton floatingActionButton = (ImageButton) findViewById(R.id.floating_action_button);
+ floatingActionButton.setOnClickListener(this);
+ mFloatingActionButtonController =
+ new FloatingActionButtonController(
+ this, floatingActionButtonContainer, floatingActionButton);
+
+ ImageButton optionsMenuButton =
+ (ImageButton) searchEditTextLayout.findViewById(R.id.dialtacts_options_menu_button);
+ optionsMenuButton.setOnClickListener(this);
+ mOverflowMenu = buildOptionsMenu(optionsMenuButton);
+ optionsMenuButton.setOnTouchListener(mOverflowMenu.getDragToOpenListener());
+
+ // Add the favorites fragment but only if savedInstanceState is null. Otherwise the
+ // fragment manager is responsible for recreating it.
+ if (savedInstanceState == null) {
+ getFragmentManager()
+ .beginTransaction()
+ .add(R.id.dialtacts_frame, new ListsFragment(), TAG_FAVORITES_FRAGMENT)
+ .commit();
+ } else {
+ mSearchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY);
+ mInRegularSearch = savedInstanceState.getBoolean(KEY_IN_REGULAR_SEARCH_UI);
+ mInDialpadSearch = savedInstanceState.getBoolean(KEY_IN_DIALPAD_SEARCH_UI);
+ mFirstLaunch = savedInstanceState.getBoolean(KEY_FIRST_LAUNCH);
+ mWasConfigurationChange = savedInstanceState.getBoolean(KEY_WAS_CONFIGURATION_CHANGE);
+ mShowDialpadOnResume = savedInstanceState.getBoolean(KEY_IS_DIALPAD_SHOWN);
+ mActionBarController.restoreInstanceState(savedInstanceState);
+ }
+
+ final boolean isLayoutRtl = ViewUtil.isRtl();
+ if (mIsLandscape) {
+ mSlideIn =
+ AnimationUtils.loadAnimation(
+ this, isLayoutRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right);
+ mSlideOut =
+ AnimationUtils.loadAnimation(
+ this, isLayoutRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right);
+ } else {
+ mSlideIn = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_in_bottom);
+ mSlideOut = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_out_bottom);
+ }
+
+ mSlideIn.setInterpolator(AnimUtils.EASE_IN);
+ mSlideOut.setInterpolator(AnimUtils.EASE_OUT);
+
+ mSlideIn.setAnimationListener(mSlideInListener);
+ mSlideOut.setAnimationListener(mSlideOutListener);
+
+ mParentLayout = (CoordinatorLayout) findViewById(R.id.dialtacts_mainlayout);
+ mParentLayout.setOnDragListener(new LayoutOnDragListener());
+ floatingActionButtonContainer
+ .getViewTreeObserver()
+ .addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ final ViewTreeObserver observer =
+ floatingActionButtonContainer.getViewTreeObserver();
+ if (!observer.isAlive()) {
+ return;
+ }
+ observer.removeOnGlobalLayoutListener(this);
+ int screenWidth = mParentLayout.getWidth();
+ mFloatingActionButtonController.setScreenWidth(screenWidth);
+ mFloatingActionButtonController.align(getFabAlignment(), false /* animate */);
+ }
+ });
+
+ Trace.endSection();
+
+ Trace.beginSection(TAG + " initialize smart dialing");
+ mDialerDatabaseHelper = Database.get(this).getDatabaseHelper(this);
+ SmartDialPrefix.initializeNanpSettings(this);
+ Trace.endSection();
+
+ mP13nLogger = P13nLogging.get(getApplicationContext());
+ mP13nRanker = P13nRanking.get(getApplicationContext());
+ Trace.endSection();
+ }
+
+ @NonNull
+ private ActionBar getActionBarSafely() {
+ return Assert.isNotNull(getSupportActionBar());
+ }
+
+ @Override
+ protected void onResume() {
+ Trace.beginSection(TAG + " onResume");
+ super.onResume();
+
+ mStateSaved = false;
+ if (mFirstLaunch) {
+ displayFragment(getIntent());
+ } else if (!phoneIsInUse() && mInCallDialpadUp) {
+ hideDialpadFragment(false, true);
+ mInCallDialpadUp = false;
+ } else if (mShowDialpadOnResume) {
+ showDialpadFragment(false);
+ mShowDialpadOnResume = false;
+ }
+
+ // If there was a voice query result returned in the {@link #onActivityResult} callback, it
+ // will have been stashed in mVoiceSearchQuery since the search results fragment cannot be
+ // shown until onResume has completed. Active the search UI and set the search term now.
+ if (!TextUtils.isEmpty(mVoiceSearchQuery)) {
+ mActionBarController.onSearchBoxTapped();
+ mSearchView.setText(mVoiceSearchQuery);
+ mVoiceSearchQuery = null;
+ }
+
+ mFirstLaunch = false;
+
+ if (mIsRestarting) {
+ // This is only called when the activity goes from resumed -> paused -> resumed, so it
+ // will not cause an extra view to be sent out on rotation
+ if (mIsDialpadShown) {
+ Logger.get(this).logScreenView(ScreenEvent.Type.DIALPAD, this);
+ }
+ mIsRestarting = false;
+ }
+
+ prepareVoiceSearchButton();
+ if (!mWasConfigurationChange) {
+ mDialerDatabaseHelper.startSmartDialUpdateThread();
+ }
+ mFloatingActionButtonController.align(getFabAlignment(), false /* animate */);
+
+ if (Calls.CONTENT_TYPE.equals(getIntent().getType())) {
+ // Externally specified extras take precedence to EXTRA_SHOW_TAB, which is only
+ // used internally.
+ final Bundle extras = getIntent().getExtras();
+ if (extras != null && extras.getInt(Calls.EXTRA_CALL_TYPE_FILTER) == Calls.VOICEMAIL_TYPE) {
+ mListsFragment.showTab(ListsFragment.TAB_INDEX_VOICEMAIL);
+ } else {
+ mListsFragment.showTab(ListsFragment.TAB_INDEX_HISTORY);
+ }
+ } else if (getIntent().hasExtra(EXTRA_SHOW_TAB)) {
+ int index = getIntent().getIntExtra(EXTRA_SHOW_TAB, ListsFragment.TAB_INDEX_SPEED_DIAL);
+ if (index < mListsFragment.getTabCount()) {
+ // Hide dialpad since this is an explicit intent to show a specific tab, which is coming
+ // from missed call or voicemail notification.
+ hideDialpadFragment(false, false);
+ exitSearchUi();
+ mListsFragment.showTab(index);
+ }
+ }
+
+ if (getIntent().getBooleanExtra(EXTRA_CLEAR_NEW_VOICEMAILS, false)) {
+ CallLogNotificationsService.markNewVoicemailsAsOld(this);
+ }
+
+ setSearchBoxHint();
+
+ mP13nLogger.reset();
+ mP13nRanker.refresh(
+ new P13nRefreshCompleteListener() {
+ @Override
+ public void onP13nRefreshComplete() {
+ // TODO: make zero-query search results visible
+ }
+ });
+ Trace.endSection();
+ }
+
+ @Override
+ protected void onRestart() {
+ super.onRestart();
+ mIsRestarting = true;
+ }
+
+ @Override
+ protected void onPause() {
+ if (mClearSearchOnPause) {
+ hideDialpadAndSearchUi();
+ mClearSearchOnPause = false;
+ }
+ if (mSlideOut.hasStarted() && !mSlideOut.hasEnded()) {
+ commitDialpadFragmentHide();
+ }
+ super.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putString(KEY_SEARCH_QUERY, mSearchQuery);
+ outState.putBoolean(KEY_IN_REGULAR_SEARCH_UI, mInRegularSearch);
+ outState.putBoolean(KEY_IN_DIALPAD_SEARCH_UI, mInDialpadSearch);
+ outState.putBoolean(KEY_FIRST_LAUNCH, mFirstLaunch);
+ outState.putBoolean(KEY_IS_DIALPAD_SHOWN, mIsDialpadShown);
+ outState.putBoolean(KEY_WAS_CONFIGURATION_CHANGE, isChangingConfigurations());
+ mActionBarController.saveInstanceState(outState);
+ mStateSaved = true;
+ }
+
+ @Override
+ public void onAttachFragment(final Fragment fragment) {
+ if (fragment instanceof DialpadFragment) {
+ mDialpadFragment = (DialpadFragment) fragment;
+ if (!mIsDialpadShown && !mShowDialpadOnResume) {
+ final FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ transaction.hide(mDialpadFragment);
+ transaction.commit();
+ }
+ } else if (fragment instanceof SmartDialSearchFragment) {
+ mSmartDialSearchFragment = (SmartDialSearchFragment) fragment;
+ mSmartDialSearchFragment.setOnPhoneNumberPickerActionListener(this);
+ if (!TextUtils.isEmpty(mDialpadQuery)) {
+ mSmartDialSearchFragment.setAddToContactNumber(mDialpadQuery);
+ }
+ } else if (fragment instanceof SearchFragment) {
+ mRegularSearchFragment = (RegularSearchFragment) fragment;
+ mRegularSearchFragment.setOnPhoneNumberPickerActionListener(this);
+ } else if (fragment instanceof ListsFragment) {
+ mListsFragment = (ListsFragment) fragment;
+ mListsFragment.addOnPageChangeListener(this);
+ }
+ if (fragment instanceof SearchFragment) {
+ final SearchFragment searchFragment = (SearchFragment) fragment;
+ searchFragment.setReranker(
+ new CursorReranker() {
+ @Override
+ @MainThread
+ public Cursor rerankCursor(Cursor data) {
+ Assert.isMainThread();
+ return mP13nRanker.rankCursor(data, PhoneQuery.PHONE_NUMBER);
+ }
+ });
+ searchFragment.addOnLoadFinishedListener(
+ new OnLoadFinishedListener() {
+ @Override
+ public void onLoadFinished() {
+ mP13nLogger.onSearchQuery(
+ searchFragment.getQueryString(),
+ (PhoneNumberListAdapter) searchFragment.getAdapter());
+ }
+ });
+ }
+ }
+
+ protected void handleMenuSettings() {
+ final Intent intent = new Intent(this, DialerSettingsActivity.class);
+ startActivity(intent);
+ }
+
+ @Override
+ public void onClick(View view) {
+ int resId = view.getId();
+ if (resId == R.id.floating_action_button) {
+ if (mListsFragment.getCurrentTabIndex() == ListsFragment.TAB_INDEX_ALL_CONTACTS
+ && !mInRegularSearch
+ && !mInDialpadSearch) {
+ DialerUtils.startActivityWithErrorToast(
+ this, IntentUtil.getNewContactIntent(), R.string.add_contact_not_available);
+ Logger.get(this).logImpression(DialerImpression.Type.NEW_CONTACT_FAB);
+ } else if (!mIsDialpadShown) {
+ mInCallDialpadUp = false;
+ showDialpadFragment(true);
+ }
+ } else if (resId == R.id.voice_search_button) {
+ try {
+ startActivityForResult(
+ new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH),
+ ACTIVITY_REQUEST_CODE_VOICE_SEARCH);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(
+ DialtactsActivity.this, R.string.voice_search_not_available, Toast.LENGTH_SHORT)
+ .show();
+ }
+ } else if (resId == R.id.dialtacts_options_menu_button) {
+ mOverflowMenu.show();
+ } else {
+ Assert.fail("Unexpected onClick event from " + view);
+ }
+ }
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ if (!isSafeToCommitTransactions()) {
+ return true;
+ }
+
+ int resId = item.getItemId();
+ if (item.getItemId() == R.id.menu_delete_all) {
+ ClearCallLogDialog.show(getFragmentManager());
+ return true;
+ } else if (resId == R.id.menu_clear_frequents) {
+ ClearFrequentsDialog.show(getFragmentManager());
+ Logger.get(this).logScreenView(ScreenEvent.Type.CLEAR_FREQUENTS, this);
+ return true;
+ } else if (resId == R.id.menu_call_settings) {
+ handleMenuSettings();
+ Logger.get(this).logScreenView(ScreenEvent.Type.SETTINGS, this);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == ACTIVITY_REQUEST_CODE_VOICE_SEARCH) {
+ if (resultCode == RESULT_OK) {
+ final ArrayList<String> matches =
+ data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
+ if (matches.size() > 0) {
+ mVoiceSearchQuery = matches.get(0);
+ } else {
+ LogUtil.i("DialtactsActivity.onActivityResult", "voice search - nothing heard");
+ }
+ } else {
+ LogUtil.e("DialtactsActivity.onActivityResult", "voice search failed: " + resultCode);
+ }
+ } else if (requestCode == ACTIVITY_REQUEST_CODE_CALL_COMPOSE) {
+ if (resultCode != RESULT_OK) {
+ LogUtil.i(
+ "DialtactsActivity.onActivityResult",
+ "returned from call composer, error occurred (resultCode=" + resultCode + ")");
+ String message =
+ getString(R.string.call_composer_connection_failed, getString(R.string.share_and_call));
+ Snackbar.make(mParentLayout, message, Snackbar.LENGTH_LONG).show();
+ } else {
+ LogUtil.i("DialtactsActivity.onActivityResult", "returned from call composer, no error");
+ }
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+ /**
+ * Update the number of unread voicemails (potentially other tabs) displayed next to the tab icon.
+ */
+ public void updateTabUnreadCounts() {
+ mListsFragment.updateTabUnreadCounts();
+ }
+
+ /**
+ * Initiates a fragment transaction to show the dialpad fragment. Animations and other visual
+ * updates are handled by a callback which is invoked after the dialpad fragment is shown.
+ *
+ * @see #onDialpadShown
+ */
+ private void showDialpadFragment(boolean animate) {
+ if (mIsDialpadShown || mStateSaved) {
+ return;
+ }
+ mIsDialpadShown = true;
+
+ mListsFragment.setUserVisibleHint(false);
+
+ final FragmentTransaction ft = getFragmentManager().beginTransaction();
+ if (mDialpadFragment == null) {
+ mDialpadFragment = new DialpadFragment();
+ ft.add(R.id.dialtacts_container, mDialpadFragment, TAG_DIALPAD_FRAGMENT);
+ } else {
+ ft.show(mDialpadFragment);
+ }
+
+ mDialpadFragment.setAnimate(animate);
+ Logger.get(this).logScreenView(ScreenEvent.Type.DIALPAD, this);
+ ft.commit();
+
+ if (animate) {
+ mFloatingActionButtonController.scaleOut();
+ } else {
+ mFloatingActionButtonController.setVisible(false);
+ maybeEnterSearchUi();
+ }
+ mActionBarController.onDialpadUp();
+
+ Assert.isNotNull(mListsFragment.getView()).animate().alpha(0).withLayer();
+
+ //adjust the title, so the user will know where we're at when the activity start/resumes.
+ setTitle(R.string.launcherDialpadActivityLabel);
+ }
+
+ /** Callback from child DialpadFragment when the dialpad is shown. */
+ public void onDialpadShown() {
+ Assert.isNotNull(mDialpadFragment);
+ if (mDialpadFragment.getAnimate()) {
+ Assert.isNotNull(mDialpadFragment.getView()).startAnimation(mSlideIn);
+ } else {
+ mDialpadFragment.setYFraction(0);
+ }
+
+ updateSearchFragmentPosition();
+ }
+
+ /**
+ * Initiates animations and other visual updates to hide the dialpad. The fragment is hidden in a
+ * callback after the hide animation ends.
+ *
+ * @see #commitDialpadFragmentHide
+ */
+ public void hideDialpadFragment(boolean animate, boolean clearDialpad) {
+ if (mDialpadFragment == null || mDialpadFragment.getView() == null) {
+ return;
+ }
+ if (clearDialpad) {
+ // Temporarily disable accessibility when we clear the dialpad, since it should be
+ // invisible and should not announce anything.
+ mDialpadFragment
+ .getDigitsWidget()
+ .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ mDialpadFragment.clearDialpad();
+ mDialpadFragment
+ .getDigitsWidget()
+ .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ }
+ if (!mIsDialpadShown) {
+ return;
+ }
+ mIsDialpadShown = false;
+ mDialpadFragment.setAnimate(animate);
+ mListsFragment.setUserVisibleHint(true);
+ mListsFragment.sendScreenViewForCurrentPosition();
+
+ updateSearchFragmentPosition();
+
+ mFloatingActionButtonController.align(getFabAlignment(), animate);
+ if (animate) {
+ mDialpadFragment.getView().startAnimation(mSlideOut);
+ } else {
+ commitDialpadFragmentHide();
+ }
+
+ mActionBarController.onDialpadDown();
+
+ if (isInSearchUi()) {
+ if (TextUtils.isEmpty(mSearchQuery)) {
+ exitSearchUi();
+ }
+ }
+ //reset the title to normal.
+ setTitle(R.string.launcherActivityLabel);
+ }
+
+ /** Finishes hiding the dialpad fragment after any animations are completed. */
+ private void commitDialpadFragmentHide() {
+ if (!mStateSaved && mDialpadFragment != null && !mDialpadFragment.isHidden()) {
+ final FragmentTransaction ft = getFragmentManager().beginTransaction();
+ ft.hide(mDialpadFragment);
+ ft.commit();
+ }
+ mFloatingActionButtonController.scaleIn(AnimUtils.NO_DELAY);
+ }
+
+ private void updateSearchFragmentPosition() {
+ SearchFragment fragment = null;
+ if (mSmartDialSearchFragment != null && mSmartDialSearchFragment.isVisible()) {
+ fragment = mSmartDialSearchFragment;
+ } else if (mRegularSearchFragment != null && mRegularSearchFragment.isVisible()) {
+ fragment = mRegularSearchFragment;
+ }
+ if (fragment != null && fragment.isVisible()) {
+ fragment.updatePosition(true /* animate */);
+ }
+ }
+
+ @Override
+ public boolean isInSearchUi() {
+ return mInDialpadSearch || mInRegularSearch;
+ }
+
+ @Override
+ public boolean hasSearchQuery() {
+ return !TextUtils.isEmpty(mSearchQuery);
+ }
+
+ @Override
+ public boolean shouldShowActionBar() {
+ return mListsFragment.shouldShowActionBar();
+ }
+
+ private void setNotInSearchUi() {
+ mInDialpadSearch = false;
+ mInRegularSearch = false;
+ }
+
+ private void hideDialpadAndSearchUi() {
+ if (mIsDialpadShown) {
+ hideDialpadFragment(false, true);
+ } else {
+ exitSearchUi();
+ }
+ }
+
+ private void prepareVoiceSearchButton() {
+ final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+ if (canIntentBeHandled(voiceIntent)) {
+ mVoiceSearchButton.setVisibility(View.VISIBLE);
+ mVoiceSearchButton.setOnClickListener(this);
+ } else {
+ mVoiceSearchButton.setVisibility(View.GONE);
+ }
+ }
+
+ public boolean isNearbyPlacesSearchEnabled() {
+ return false;
+ }
+
+ protected int getSearchBoxHint() {
+ return R.string.dialer_hint_find_contact;
+ }
+
+ /** Sets the hint text for the contacts search box */
+ private void setSearchBoxHint() {
+ SearchEditTextLayout searchEditTextLayout =
+ (SearchEditTextLayout)
+ getActionBarSafely().getCustomView().findViewById(R.id.search_view_container);
+ ((TextView) searchEditTextLayout.findViewById(R.id.search_box_start_search))
+ .setHint(getSearchBoxHint());
+ }
+
+ protected OptionsPopupMenu buildOptionsMenu(View invoker) {
+ final OptionsPopupMenu popupMenu = new OptionsPopupMenu(this, invoker);
+ popupMenu.inflate(R.menu.dialtacts_options);
+ popupMenu.setOnMenuItemClickListener(this);
+ return popupMenu;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ if (mPendingSearchViewQuery != null) {
+ mSearchView.setText(mPendingSearchViewQuery);
+ mPendingSearchViewQuery = null;
+ }
+ if (mActionBarController != null) {
+ mActionBarController.restoreActionBarOffset();
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the intent is due to hitting the green send key (hardware call button:
+ * KEYCODE_CALL) while in a call.
+ *
+ * @param intent the intent that launched this activity
+ * @return true if the intent is due to hitting the green send key while in a call
+ */
+ private boolean isSendKeyWhileInCall(Intent intent) {
+ // If there is a call in progress and the user launched the dialer by hitting the call
+ // button, go straight to the in-call screen.
+ final boolean callKey = Intent.ACTION_CALL_BUTTON.equals(intent.getAction());
+
+ // When KEYCODE_CALL event is handled it dispatches an intent with the ACTION_CALL_BUTTON.
+ // Besides of checking the intent action, we must check if the phone is really during a
+ // call in order to decide whether to ignore the event or continue to display the activity.
+ if (callKey && phoneIsInUse()) {
+ TelecomUtil.showInCallScreen(this, false);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Sets the current tab based on the intent's request type
+ *
+ * @param intent Intent that contains information about which tab should be selected
+ */
+ private void displayFragment(Intent intent) {
+ // If we got here by hitting send and we're in call forward along to the in-call activity
+ if (isSendKeyWhileInCall(intent)) {
+ finish();
+ return;
+ }
+
+ final boolean showDialpadChooser =
+ !ACTION_SHOW_TAB.equals(intent.getAction())
+ && phoneIsInUse()
+ && !DialpadFragment.isAddCallMode(intent);
+ if (showDialpadChooser || (intent.getData() != null && isDialIntent(intent))) {
+ showDialpadFragment(false);
+ mDialpadFragment.setStartedFromNewIntent(true);
+ if (showDialpadChooser && !mDialpadFragment.isVisible()) {
+ mInCallDialpadUp = true;
+ }
+ }
+ }
+
+ @Override
+ public void onNewIntent(Intent newIntent) {
+ setIntent(newIntent);
+
+ mStateSaved = false;
+ displayFragment(newIntent);
+
+ invalidateOptionsMenu();
+ }
+
+ /** Returns true if the given intent contains a phone number to populate the dialer with */
+ private boolean isDialIntent(Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) {
+ return true;
+ }
+ if (Intent.ACTION_VIEW.equals(action)) {
+ final Uri data = intent.getData();
+ if (data != null && PhoneAccount.SCHEME_TEL.equals(data.getScheme())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Shows the search fragment */
+ private void enterSearchUi(boolean smartDialSearch, String query, boolean animate) {
+ if (mStateSaved || getFragmentManager().isDestroyed()) {
+ // Weird race condition where fragment is doing work after the activity is destroyed
+ // due to talkback being on (b/10209937). Just return since we can't do any
+ // constructive here.
+ return;
+ }
+
+ if (DEBUG) {
+ LogUtil.v("DialtactsActivity.enterSearchUi", "smart dial " + smartDialSearch);
+ }
+
+ final FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ if (mInDialpadSearch && mSmartDialSearchFragment != null) {
+ transaction.remove(mSmartDialSearchFragment);
+ } else if (mInRegularSearch && mRegularSearchFragment != null) {
+ transaction.remove(mRegularSearchFragment);
+ }
+
+ final String tag;
+ if (smartDialSearch) {
+ tag = TAG_SMARTDIAL_SEARCH_FRAGMENT;
+ } else {
+ tag = TAG_REGULAR_SEARCH_FRAGMENT;
+ }
+ mInDialpadSearch = smartDialSearch;
+ mInRegularSearch = !smartDialSearch;
+
+ mFloatingActionButtonController.scaleOut();
+
+ SearchFragment fragment = (SearchFragment) getFragmentManager().findFragmentByTag(tag);
+ if (animate) {
+ transaction.setCustomAnimations(android.R.animator.fade_in, 0);
+ } else {
+ transaction.setTransition(FragmentTransaction.TRANSIT_NONE);
+ }
+ if (fragment == null) {
+ if (smartDialSearch) {
+ fragment = new SmartDialSearchFragment();
+ } else {
+ fragment = Bindings.getLegacy(this).newRegularSearchFragment();
+ fragment.setOnTouchListener(
+ new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ // Show the FAB when the user touches the lists fragment and the soft
+ // keyboard is hidden.
+ hideDialpadFragment(true, false);
+ showFabInSearchUi();
+ v.performClick();
+ return false;
+ }
+ });
+ }
+ transaction.add(R.id.dialtacts_frame, fragment, tag);
+ } else {
+ transaction.show(fragment);
+ }
+ // DialtactsActivity will provide the options menu
+ fragment.setHasOptionsMenu(false);
+ fragment.setShowEmptyListForNullQuery(true);
+ if (!smartDialSearch) {
+ fragment.setQueryString(query);
+ }
+ transaction.commit();
+
+ if (animate) {
+ Assert.isNotNull(mListsFragment.getView()).animate().alpha(0).withLayer();
+ }
+ mListsFragment.setUserVisibleHint(false);
+
+ if (smartDialSearch) {
+ Logger.get(this).logScreenView(ScreenEvent.Type.SMART_DIAL_SEARCH, this);
+ } else {
+ Logger.get(this).logScreenView(ScreenEvent.Type.REGULAR_SEARCH, this);
+ }
+ }
+
+ /** Hides the search fragment */
+ private void exitSearchUi() {
+ // See related bug in enterSearchUI();
+ if (getFragmentManager().isDestroyed() || mStateSaved) {
+ return;
+ }
+
+ mSearchView.setText(null);
+
+ if (mDialpadFragment != null) {
+ mDialpadFragment.clearDialpad();
+ }
+
+ setNotInSearchUi();
+
+ // Restore the FAB for the lists fragment.
+ if (getFabAlignment() != FloatingActionButtonController.ALIGN_END) {
+ mFloatingActionButtonController.setVisible(false);
+ }
+ mFloatingActionButtonController.scaleIn(FAB_SCALE_IN_DELAY_MS);
+ onPageScrolled(mListsFragment.getCurrentTabIndex(), 0 /* offset */, 0 /* pixelOffset */);
+ onPageSelected(mListsFragment.getCurrentTabIndex());
+
+ final FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ if (mSmartDialSearchFragment != null) {
+ transaction.remove(mSmartDialSearchFragment);
+ }
+ if (mRegularSearchFragment != null) {
+ transaction.remove(mRegularSearchFragment);
+ }
+ transaction.commit();
+
+ Assert.isNotNull(mListsFragment.getView()).animate().alpha(1).withLayer();
+
+ if (mDialpadFragment == null || !mDialpadFragment.isVisible()) {
+ // If the dialpad fragment wasn't previously visible, then send a screen view because
+ // we are exiting regular search. Otherwise, the screen view will be sent by
+ // {@link #hideDialpadFragment}.
+ mListsFragment.sendScreenViewForCurrentPosition();
+ mListsFragment.setUserVisibleHint(true);
+ }
+
+ mActionBarController.onSearchUiExited();
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mStateSaved) {
+ return;
+ }
+ if (mIsDialpadShown) {
+ if (TextUtils.isEmpty(mSearchQuery)
+ || (mSmartDialSearchFragment != null
+ && mSmartDialSearchFragment.isVisible()
+ && mSmartDialSearchFragment.getAdapter().getCount() == 0)) {
+ exitSearchUi();
+ }
+ hideDialpadFragment(true, false);
+ } else if (isInSearchUi()) {
+ exitSearchUi();
+ DialerUtils.hideInputMethod(mParentLayout);
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ private void maybeEnterSearchUi() {
+ if (!isInSearchUi()) {
+ enterSearchUi(true /* isSmartDial */, mSearchQuery, false);
+ }
+ }
+
+ /** @return True if the search UI was exited, false otherwise */
+ private boolean maybeExitSearchUi() {
+ if (isInSearchUi() && TextUtils.isEmpty(mSearchQuery)) {
+ exitSearchUi();
+ DialerUtils.hideInputMethod(mParentLayout);
+ return true;
+ }
+ return false;
+ }
+
+ private void showFabInSearchUi() {
+ mFloatingActionButtonController.changeIcon(
+ getResources().getDrawable(R.drawable.fab_ic_dial, null),
+ getResources().getString(R.string.action_menu_dialpad_button));
+ mFloatingActionButtonController.align(getFabAlignment(), false /* animate */);
+ mFloatingActionButtonController.scaleIn(FAB_SCALE_IN_DELAY_MS);
+ }
+
+ @Override
+ public void onDialpadQueryChanged(String query) {
+ mDialpadQuery = query;
+ if (mSmartDialSearchFragment != null) {
+ mSmartDialSearchFragment.setAddToContactNumber(query);
+ }
+ final String normalizedQuery =
+ SmartDialNameMatcher.normalizeNumber(query, SmartDialNameMatcher.LATIN_SMART_DIAL_MAP);
+
+ if (!TextUtils.equals(mSearchView.getText(), normalizedQuery)) {
+ if (DEBUG) {
+ LogUtil.v("DialtactsActivity.onDialpadQueryChanged", "new query: " + query);
+ }
+ if (mDialpadFragment == null || !mDialpadFragment.isVisible()) {
+ // This callback can happen if the dialpad fragment is recreated because of
+ // activity destruction. In that case, don't update the search view because
+ // that would bring the user back to the search fragment regardless of the
+ // previous state of the application. Instead, just return here and let the
+ // fragment manager correctly figure out whatever fragment was last displayed.
+ if (!TextUtils.isEmpty(normalizedQuery)) {
+ mPendingSearchViewQuery = normalizedQuery;
+ }
+ return;
+ }
+ mSearchView.setText(normalizedQuery);
+ }
+
+ try {
+ if (mDialpadFragment != null && mDialpadFragment.isVisible()) {
+ mDialpadFragment.process_quote_emergency_unquote(normalizedQuery);
+ }
+ } catch (Exception ignored) {
+ // Skip any exceptions for this piece of code
+ }
+ }
+
+ @Override
+ public boolean onDialpadSpacerTouchWithEmptyQuery() {
+ if (mInDialpadSearch
+ && mSmartDialSearchFragment != null
+ && !mSmartDialSearchFragment.isShowingPermissionRequest()) {
+ hideDialpadFragment(true /* animate */, true /* clearDialpad */);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onListFragmentScrollStateChange(int scrollState) {
+ if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
+ hideDialpadFragment(true, false);
+ DialerUtils.hideInputMethod(mParentLayout);
+ }
+ }
+
+ @Override
+ public void onListFragmentScroll(int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+ // TODO: No-op for now. This should eventually show/hide the actionBar based on
+ // interactions with the ListsFragments.
+ }
+
+ private boolean phoneIsInUse() {
+ return TelecomUtil.isInCall(this);
+ }
+
+ private boolean canIntentBeHandled(Intent intent) {
+ final PackageManager packageManager = getPackageManager();
+ final List<ResolveInfo> resolveInfo =
+ packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ return resolveInfo != null && resolveInfo.size() > 0;
+ }
+
+ /** Called when the user has long-pressed a contact tile to start a drag operation. */
+ @Override
+ public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {
+ mListsFragment.showRemoveView(true);
+ }
+
+ @Override
+ public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {}
+
+ /** Called when the user has released a contact tile after long-pressing it. */
+ @Override
+ public void onDragFinished(int x, int y) {
+ mListsFragment.showRemoveView(false);
+ }
+
+ @Override
+ public void onDroppedOnRemove() {}
+
+ /**
+ * Allows the SpeedDialFragment to attach the drag controller to mRemoveViewContainer once it has
+ * been attached to the activity.
+ */
+ @Override
+ public void setDragDropController(DragDropController dragController) {
+ mDragDropController = dragController;
+ mListsFragment.getRemoveView().setDragDropController(dragController);
+ }
+
+ /** Implemented to satisfy {@link SpeedDialFragment.HostInterface} */
+ @Override
+ public void showAllContactsTab() {
+ if (mListsFragment != null) {
+ mListsFragment.showTab(ListsFragment.TAB_INDEX_ALL_CONTACTS);
+ }
+ }
+
+ /** Implemented to satisfy {@link CallLogFragment.HostInterface} */
+ @Override
+ public void showDialpad() {
+ showDialpadFragment(true);
+ }
+
+ @Override
+ public void enableFloatingButton(boolean enabled) {
+ LogUtil.d("DialtactsActivity.enableFloatingButton", "enable: %b", enabled);
+ // Floating button shouldn't be enabled when dialpad is shown.
+ if (!isDialpadShown() || !enabled) {
+ mFloatingActionButtonController.setVisible(enabled);
+ }
+ }
+
+ @Override
+ public void onPickDataUri(
+ Uri dataUri, boolean isVideoCall, CallSpecificAppData callSpecificAppData) {
+ mClearSearchOnPause = true;
+ PhoneNumberInteraction.startInteractionForPhoneCall(
+ DialtactsActivity.this, dataUri, isVideoCall, callSpecificAppData);
+ }
+
+ @Override
+ public void onPickPhoneNumber(
+ String phoneNumber, boolean isVideoCall, CallSpecificAppData callSpecificAppData) {
+ if (phoneNumber == null) {
+ // Invalid phone number, but let the call go through so that InCallUI can show
+ // an error message.
+ phoneNumber = "";
+ }
+
+ Intent intent =
+ new CallIntentBuilder(phoneNumber, callSpecificAppData).setIsVideoCall(isVideoCall).build();
+
+ DialerUtils.startActivityWithErrorToast(this, intent);
+ mClearSearchOnPause = true;
+ }
+
+ @Override
+ public void onHomeInActionBarSelected() {
+ exitSearchUi();
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ int tabIndex = mListsFragment.getCurrentTabIndex();
+
+ // Scroll the button from center to end when moving from the Speed Dial to Call History tab.
+ // In RTL, scroll when the current tab is Call History instead, since the order of the tabs
+ // is reversed and the ViewPager returns the left tab position during scroll.
+ boolean isRtl = ViewUtil.isRtl();
+ if (!isRtl && tabIndex == ListsFragment.TAB_INDEX_SPEED_DIAL && !mIsLandscape) {
+ mFloatingActionButtonController.onPageScrolled(positionOffset);
+ } else if (isRtl && tabIndex == ListsFragment.TAB_INDEX_HISTORY && !mIsLandscape) {
+ mFloatingActionButtonController.onPageScrolled(1 - positionOffset);
+ } else if (tabIndex != ListsFragment.TAB_INDEX_SPEED_DIAL) {
+ mFloatingActionButtonController.onPageScrolled(1);
+ }
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ updateMissedCalls();
+ int tabIndex = mListsFragment.getCurrentTabIndex();
+ mPreviouslySelectedTabIndex = tabIndex;
+ mFloatingActionButtonController.setVisible(true);
+ if (tabIndex == ListsFragment.TAB_INDEX_ALL_CONTACTS
+ && !mInRegularSearch
+ && !mInDialpadSearch) {
+ mFloatingActionButtonController.changeIcon(
+ getResources().getDrawable(R.drawable.ic_person_add_24dp, null),
+ getResources().getString(R.string.search_shortcut_create_new_contact));
+ } else {
+ mFloatingActionButtonController.changeIcon(
+ getResources().getDrawable(R.drawable.fab_ic_dial, null),
+ getResources().getString(R.string.action_menu_dialpad_button));
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {}
+
+ @Override
+ public boolean isActionBarShowing() {
+ return mActionBarController.isActionBarShowing();
+ }
+
+ @Override
+ public ActionBarController getActionBarController() {
+ return mActionBarController;
+ }
+
+ @Override
+ public boolean isDialpadShown() {
+ return mIsDialpadShown;
+ }
+
+ @Override
+ public int getDialpadHeight() {
+ if (mDialpadFragment != null) {
+ return mDialpadFragment.getDialpadHeight();
+ }
+ return 0;
+ }
+
+ @Override
+ public int getActionBarHideOffset() {
+ return getActionBarSafely().getHideOffset();
+ }
+
+ @Override
+ public void setActionBarHideOffset(int offset) {
+ getActionBarSafely().setHideOffset(offset);
+ }
+
+ @Override
+ public int getActionBarHeight() {
+ return mActionBarHeight;
+ }
+
+ private int getFabAlignment() {
+ if (!mIsLandscape
+ && !isInSearchUi()
+ && mListsFragment.getCurrentTabIndex() == ListsFragment.TAB_INDEX_SPEED_DIAL) {
+ return FloatingActionButtonController.ALIGN_MIDDLE;
+ }
+ return FloatingActionButtonController.ALIGN_END;
+ }
+
+ private void updateMissedCalls() {
+ if (mPreviouslySelectedTabIndex == ListsFragment.TAB_INDEX_HISTORY) {
+ mListsFragment.markMissedCallsAsReadAndRemoveNotifications();
+ }
+ }
+
+ @Override
+ public void onDisambigDialogDismissed() {
+ // Don't do anything; the app will remain open with favorites tiles displayed.
+ }
+
+ @Override
+ public void interactionError(@InteractionErrorCode int interactionErrorCode) {
+ switch (interactionErrorCode) {
+ case InteractionErrorCode.USER_LEAVING_ACTIVITY:
+ // This is expected to happen if the user exits the activity before the interaction occurs.
+ return;
+ case InteractionErrorCode.CONTACT_NOT_FOUND:
+ case InteractionErrorCode.CONTACT_HAS_NO_NUMBER:
+ case InteractionErrorCode.OTHER_ERROR:
+ default:
+ // All other error codes are unexpected. For example, it should be impossible to start an
+ // interaction with an invalid contact from the Dialtacts activity.
+ Assert.fail("PhoneNumberInteraction error: " + interactionErrorCode);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, String[] permissions, int[] grantResults) {
+ // This should never happen; it should be impossible to start an interaction without the
+ // contacts permission from the Dialtacts activity.
+ Assert.fail(
+ String.format(
+ Locale.US,
+ "Permissions requested unexpectedly: %d/%s/%s",
+ requestCode,
+ Arrays.toString(permissions),
+ Arrays.toString(grantResults)));
+ }
+
+ protected class OptionsPopupMenu extends PopupMenu {
+
+ public OptionsPopupMenu(Context context, View anchor) {
+ super(context, anchor, Gravity.END);
+ }
+
+ @Override
+ public void show() {
+ final boolean hasContactsPermission =
+ PermissionsUtil.hasContactsPermissions(DialtactsActivity.this);
+ final Menu menu = getMenu();
+ final MenuItem clearFrequents = menu.findItem(R.id.menu_clear_frequents);
+ clearFrequents.setVisible(
+ mListsFragment != null
+ && mListsFragment.getSpeedDialFragment() != null
+ && mListsFragment.getSpeedDialFragment().hasFrequents()
+ && hasContactsPermission);
+
+ menu.findItem(R.id.menu_delete_all)
+ .setVisible(PermissionsUtil.hasPhonePermissions(DialtactsActivity.this));
+ super.show();
+ }
+ }
+
+ /**
+ * Listener that listens to drag events and sends their x and y coordinates to a {@link
+ * DragDropController}.
+ */
+ private class LayoutOnDragListener implements OnDragListener {
+
+ @Override
+ public boolean onDrag(View v, DragEvent event) {
+ if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) {
+ mDragDropController.handleDragHovered(v, (int) event.getX(), (int) event.getY());
+ }
+ return true;
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/FloatingActionButtonBehavior.java b/java/com/android/dialer/app/FloatingActionButtonBehavior.java
new file mode 100644
index 000000000..d4a79ca19
--- /dev/null
+++ b/java/com/android/dialer/app/FloatingActionButtonBehavior.java
@@ -0,0 +1,50 @@
+/*
+ * 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.dialer.app;
+
+import android.content.Context;
+import android.support.design.widget.CoordinatorLayout;
+import android.support.design.widget.Snackbar.SnackbarLayout;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import com.android.dialer.proguard.UsedByReflection;
+
+/**
+ * Implements custom behavior for the movement of the FAB in response to the Snackbar. Because we
+ * are not using the design framework FloatingActionButton widget, we need to manually implement the
+ * Material Design behavior of having the FAB translate upward and downward with the appearance and
+ * disappearance of a Snackbar.
+ */
+@UsedByReflection(value = "dialtacts_activity.xml")
+public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior<FrameLayout> {
+
+ @UsedByReflection(value = "dialtacts_activity.xml")
+ public FloatingActionButtonBehavior(Context context, AttributeSet attrs) {}
+
+ @Override
+ public boolean layoutDependsOn(CoordinatorLayout parent, FrameLayout child, View dependency) {
+ return dependency instanceof SnackbarLayout;
+ }
+
+ @Override
+ public boolean onDependentViewChanged(
+ CoordinatorLayout parent, FrameLayout child, View dependency) {
+ float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
+ child.setTranslationY(translationY);
+ return true;
+ }
+}
diff --git a/java/com/android/dialer/app/PhoneCallDetails.java b/java/com/android/dialer/app/PhoneCallDetails.java
new file mode 100644
index 000000000..436f68eec
--- /dev/null
+++ b/java/com/android/dialer/app/PhoneCallDetails.java
@@ -0,0 +1,207 @@
+/*
+ * 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.dialer.app;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import android.text.TextUtils;
+import com.android.contacts.common.ContactsUtils.UserType;
+import com.android.contacts.common.preference.ContactsPreferences;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.dialer.app.calllog.PhoneNumberDisplayUtil;
+import com.android.dialer.phonenumbercache.ContactInfo;
+
+/** The details of a phone call to be shown in the UI. */
+public class PhoneCallDetails {
+
+ // The number of the other party involved in the call.
+ public CharSequence number;
+ // Post-dial digits associated with the outgoing call.
+ public String postDialDigits;
+ // The secondary line number the call was received via.
+ public String viaNumber;
+ // The number presenting rules set by the network, e.g., {@link Calls#PRESENTATION_ALLOWED}
+ public int numberPresentation;
+ // The country corresponding with the phone number.
+ public String countryIso;
+ // The geocoded location for the phone number.
+ public String geocode;
+
+ /**
+ * The type of calls, as defined in the call log table, e.g., {@link Calls#INCOMING_TYPE}.
+ *
+ * <p>There might be multiple types if this represents a set of entries grouped together.
+ */
+ public int[] callTypes;
+
+ // The date of the call, in milliseconds since the epoch.
+ public long date;
+ // The duration of the call in milliseconds, or 0 for missed calls.
+ public long duration;
+ // The name of the contact, or the empty string.
+ public CharSequence namePrimary;
+ // The alternative name of the contact, e.g. last name first, or the empty string
+ public CharSequence nameAlternative;
+ /**
+ * The user's preference on name display order, last name first or first time first. {@see
+ * ContactsPreferences}
+ */
+ public int nameDisplayOrder;
+ // The type of phone, e.g., {@link Phone#TYPE_HOME}, 0 if not available.
+ public int numberType;
+ // The custom label associated with the phone number in the contact, or the empty string.
+ public CharSequence numberLabel;
+ // The URI of the contact associated with this phone call.
+ public Uri contactUri;
+
+ /**
+ * The photo URI of the picture of the contact that is associated with this phone call or null if
+ * there is none.
+ *
+ * <p>This is meant to store the high-res photo only.
+ */
+ public Uri photoUri;
+
+ // The source type of the contact associated with this call.
+ public int sourceType;
+
+ // The object id type of the contact associated with this call.
+ public String objectId;
+
+ // The unique identifier for the account associated with the call.
+ public PhoneAccountHandle accountHandle;
+
+ // Features applicable to this call.
+ public int features;
+
+ // Total data usage for this call.
+ public Long dataUsage;
+
+ // Voicemail transcription
+ public String transcription;
+
+ // The display string for the number.
+ public String displayNumber;
+
+ // Whether the contact number is a voicemail number.
+ public boolean isVoicemail;
+
+ /** The {@link UserType} of the contact */
+ public @UserType long contactUserType;
+
+ /**
+ * If this is a voicemail, whether the message is read. For other types of calls, this defaults to
+ * {@code true}.
+ */
+ public boolean isRead = true;
+
+ // If this call is a spam number.
+ public boolean isSpam = false;
+
+ // If this call is a blocked number.
+ public boolean isBlocked = false;
+
+ // Call location and date text.
+ public CharSequence callLocationAndDate;
+
+ // Call description.
+ public CharSequence callDescription;
+ public String accountComponentName;
+ public String accountId;
+ public ContactInfo cachedContactInfo;
+ public int voicemailId;
+ public int previousGroup;
+
+ /**
+ * Constructor with required fields for the details of a call with a number associated with a
+ * contact.
+ */
+ public PhoneCallDetails(
+ CharSequence number, int numberPresentation, CharSequence postDialDigits) {
+ this.number = number;
+ this.numberPresentation = numberPresentation;
+ this.postDialDigits = postDialDigits.toString();
+ }
+ /**
+ * Construct the "on {accountLabel} via {viaNumber}" accessibility description for the account
+ * list item, depending on the existence of the accountLabel and viaNumber.
+ *
+ * @param viaNumber The number that this call is being placed via.
+ * @param accountLabel The {@link PhoneAccount} label that this call is being placed with.
+ * @return The description of the account that this call has been placed on.
+ */
+ public static CharSequence createAccountLabelDescription(
+ Resources resources, @Nullable String viaNumber, @Nullable CharSequence accountLabel) {
+
+ if ((!TextUtils.isEmpty(viaNumber)) && !TextUtils.isEmpty(accountLabel)) {
+ String msg =
+ resources.getString(
+ R.string.description_via_number_phone_account, accountLabel, viaNumber);
+ CharSequence accountNumberLabel =
+ ContactDisplayUtils.getTelephoneTtsSpannable(msg, viaNumber);
+ return (accountNumberLabel == null) ? msg : accountNumberLabel;
+ } else if (!TextUtils.isEmpty(viaNumber)) {
+ CharSequence viaNumberLabel =
+ ContactDisplayUtils.getTtsSpannedPhoneNumber(
+ resources, R.string.description_via_number, viaNumber);
+ return (viaNumberLabel == null) ? viaNumber : viaNumberLabel;
+ } else if (!TextUtils.isEmpty(accountLabel)) {
+ return TextUtils.expandTemplate(
+ resources.getString(R.string.description_phone_account), accountLabel);
+ }
+ return "";
+ }
+
+ /**
+ * Returns the preferred name for the call details as specified by the {@link #nameDisplayOrder}
+ *
+ * @return the preferred name
+ */
+ public CharSequence getPreferredName() {
+ if (nameDisplayOrder == ContactsPreferences.DISPLAY_ORDER_PRIMARY
+ || TextUtils.isEmpty(nameAlternative)) {
+ return namePrimary;
+ }
+ return nameAlternative;
+ }
+
+ public void updateDisplayNumber(
+ Context context, CharSequence formattedNumber, boolean isVoicemail) {
+ displayNumber =
+ PhoneNumberDisplayUtil.getDisplayNumber(
+ context, number, numberPresentation, formattedNumber, postDialDigits, isVoicemail)
+ .toString();
+ }
+
+ public boolean hasIncomingCalls() {
+ for (int i = 0; i < callTypes.length; i++) {
+ if (callTypes[i] == CallLog.Calls.INCOMING_TYPE
+ || callTypes[i] == CallLog.Calls.MISSED_TYPE
+ || callTypes[i] == CallLog.Calls.VOICEMAIL_TYPE
+ || callTypes[i] == CallLog.Calls.REJECTED_TYPE
+ || callTypes[i] == CallLog.Calls.BLOCKED_TYPE) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/java/com/android/dialer/app/SpecialCharSequenceMgr.java b/java/com/android/dialer/app/SpecialCharSequenceMgr.java
new file mode 100644
index 000000000..2ae19704a
--- /dev/null
+++ b/java/com/android/dialer/app/SpecialCharSequenceMgr.java
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2006 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.dialer.app;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.DialogFragment;
+import android.app.KeyguardManager;
+import android.app.ProgressDialog;
+import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Looper;
+import android.provider.Settings;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.Toast;
+import com.android.common.io.MoreCloseables;
+import com.android.contacts.common.compat.TelephonyManagerCompat;
+import com.android.contacts.common.database.NoNullCursorAsyncQueryHandler;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
+import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment.SelectPhoneAccountListener;
+import com.android.dialer.app.calllog.PhoneAccountUtils;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.telecom.TelecomUtil;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class to listen for some magic character sequences that are handled specially by the
+ * dialer.
+ *
+ * <p>Note the Phone app also handles these sequences too (in a couple of relatively obscure places
+ * in the UI), so there's a separate version of this class under apps/Phone.
+ *
+ * <p>TODO: there's lots of duplicated code between this class and the corresponding class under
+ * apps/Phone. Let's figure out a way to unify these two classes (in the framework? in a common
+ * shared library?)
+ */
+public class SpecialCharSequenceMgr {
+
+ private static final String TAG = "SpecialCharSequenceMgr";
+
+ private static final String TAG_SELECT_ACCT_FRAGMENT = "tag_select_acct_fragment";
+
+ private static final String SECRET_CODE_ACTION = "android.provider.Telephony.SECRET_CODE";
+ private static final String MMI_IMEI_DISPLAY = "*#06#";
+ private static final String MMI_REGULATORY_INFO_DISPLAY = "*#07#";
+ /** ***** This code is used to handle SIM Contact queries ***** */
+ private static final String ADN_PHONE_NUMBER_COLUMN_NAME = "number";
+
+ private static final String ADN_NAME_COLUMN_NAME = "name";
+ private static final int ADN_QUERY_TOKEN = -1;
+ /**
+ * Remembers the previous {@link QueryHandler} and cancel the operation when needed, to prevent
+ * possible crash.
+ *
+ * <p>QueryHandler may call {@link ProgressDialog#dismiss()} when the screen is already gone,
+ * which will cause the app crash. This variable enables the class to prevent the crash on {@link
+ * #cleanup()}.
+ *
+ * <p>TODO: Remove this and replace it (and {@link #cleanup()}) with better implementation. One
+ * complication is that we have SpecialCharSequenceMgr in Phone package too, which has *slightly*
+ * different implementation. Note that Phone package doesn't have this problem, so the class on
+ * Phone side doesn't have this functionality. Fundamental fix would be to have one shared
+ * implementation and resolve this corner case more gracefully.
+ */
+ private static QueryHandler sPreviousAdnQueryHandler;
+
+ /** This class is never instantiated. */
+ private SpecialCharSequenceMgr() {}
+
+ public static boolean handleChars(Context context, String input, EditText textField) {
+ //get rid of the separators so that the string gets parsed correctly
+ String dialString = PhoneNumberUtils.stripSeparators(input);
+
+ return handleDeviceIdDisplay(context, dialString)
+ || handleRegulatoryInfoDisplay(context, dialString)
+ || handlePinEntry(context, dialString)
+ || handleAdnEntry(context, dialString, textField)
+ || handleSecretCode(context, dialString);
+
+ }
+
+ /**
+ * Cleanup everything around this class. Must be run inside the main thread.
+ *
+ * <p>This should be called when the screen becomes background.
+ */
+ public static void cleanup() {
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ Log.wtf(TAG, "cleanup() is called outside the main thread");
+ return;
+ }
+
+ if (sPreviousAdnQueryHandler != null) {
+ sPreviousAdnQueryHandler.cancel();
+ sPreviousAdnQueryHandler = null;
+ }
+ }
+
+ /**
+ * Handles secret codes to launch arbitrary activities in the form of *#*#<code>#*#*. If a secret
+ * code is encountered an Intent is started with the android_secret_code://<code> URI.
+ *
+ * @param context the context to use
+ * @param input the text to check for a secret code in
+ * @return true if a secret code was encountered
+ */
+ static boolean handleSecretCode(Context context, String input) {
+ // Secret codes are in the form *#*#<code>#*#*
+ int len = input.length();
+ if (len > 8 && input.startsWith("*#*#") && input.endsWith("#*#*")) {
+ final Intent intent =
+ new Intent(
+ SECRET_CODE_ACTION,
+ Uri.parse("android_secret_code://" + input.substring(4, len - 4)));
+ context.sendBroadcast(intent);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Handle ADN requests by filling in the SIM contact number into the requested EditText.
+ *
+ * <p>This code works alongside the Asynchronous query handler {@link QueryHandler} and query
+ * cancel handler implemented in {@link SimContactQueryCookie}.
+ */
+ static boolean handleAdnEntry(Context context, String input, EditText textField) {
+ /* ADN entries are of the form "N(N)(N)#" */
+ TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ if (telephonyManager == null
+ || telephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_GSM) {
+ return false;
+ }
+
+ // if the phone is keyguard-restricted, then just ignore this
+ // input. We want to make sure that sim card contacts are NOT
+ // exposed unless the phone is unlocked, and this code can be
+ // accessed from the emergency dialer.
+ KeyguardManager keyguardManager =
+ (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+ if (keyguardManager.inKeyguardRestrictedInputMode()) {
+ return false;
+ }
+
+ int len = input.length();
+ if ((len > 1) && (len < 5) && (input.endsWith("#"))) {
+ try {
+ // get the ordinal number of the sim contact
+ final int index = Integer.parseInt(input.substring(0, len - 1));
+
+ // The original code that navigated to a SIM Contacts list view did not
+ // highlight the requested contact correctly, a requirement for PTCRB
+ // certification. This behaviour is consistent with the UI paradigm
+ // for touch-enabled lists, so it does not make sense to try to work
+ // around it. Instead we fill in the the requested phone number into
+ // the dialer text field.
+
+ // create the async query handler
+ final QueryHandler handler = new QueryHandler(context.getContentResolver());
+
+ // create the cookie object
+ final SimContactQueryCookie sc =
+ new SimContactQueryCookie(index - 1, handler, ADN_QUERY_TOKEN);
+
+ // setup the cookie fields
+ sc.contactNum = index - 1;
+ sc.setTextField(textField);
+
+ // create the progress dialog
+ sc.progressDialog = new ProgressDialog(context);
+ sc.progressDialog.setTitle(R.string.simContacts_title);
+ sc.progressDialog.setMessage(context.getText(R.string.simContacts_emptyLoading));
+ sc.progressDialog.setIndeterminate(true);
+ sc.progressDialog.setCancelable(true);
+ sc.progressDialog.setOnCancelListener(sc);
+ sc.progressDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+
+ List<PhoneAccountHandle> subscriptionAccountHandles =
+ PhoneAccountUtils.getSubscriptionPhoneAccounts(context);
+ Context applicationContext = context.getApplicationContext();
+ boolean hasUserSelectedDefault =
+ subscriptionAccountHandles.contains(
+ TelecomUtil.getDefaultOutgoingPhoneAccount(
+ applicationContext, PhoneAccount.SCHEME_TEL));
+
+ if (subscriptionAccountHandles.size() <= 1 || hasUserSelectedDefault) {
+ Uri uri = TelecomUtil.getAdnUriForPhoneAccount(applicationContext, null);
+ handleAdnQuery(handler, sc, uri);
+ } else {
+ SelectPhoneAccountListener callback =
+ new HandleAdnEntryAccountSelectedCallback(applicationContext, handler, sc);
+
+ DialogFragment dialogFragment =
+ SelectPhoneAccountDialogFragment.newInstance(
+ subscriptionAccountHandles, callback, null);
+ dialogFragment.show(((Activity) context).getFragmentManager(), TAG_SELECT_ACCT_FRAGMENT);
+ }
+
+ return true;
+ } catch (NumberFormatException ex) {
+ // Ignore
+ }
+ }
+ return false;
+ }
+
+ private static void handleAdnQuery(QueryHandler handler, SimContactQueryCookie cookie, Uri uri) {
+ if (handler == null || cookie == null || uri == null) {
+ Log.w(TAG, "queryAdn parameters incorrect");
+ return;
+ }
+
+ // display the progress dialog
+ cookie.progressDialog.show();
+
+ // run the query.
+ handler.startQuery(
+ ADN_QUERY_TOKEN,
+ cookie,
+ uri,
+ new String[] {ADN_PHONE_NUMBER_COLUMN_NAME},
+ null,
+ null,
+ null);
+
+ if (sPreviousAdnQueryHandler != null) {
+ // It is harmless to call cancel() even after the handler's gone.
+ sPreviousAdnQueryHandler.cancel();
+ }
+ sPreviousAdnQueryHandler = handler;
+ }
+
+ static boolean handlePinEntry(final Context context, final String input) {
+ if ((input.startsWith("**04") || input.startsWith("**05")) && input.endsWith("#")) {
+ List<PhoneAccountHandle> subscriptionAccountHandles =
+ PhoneAccountUtils.getSubscriptionPhoneAccounts(context);
+ boolean hasUserSelectedDefault =
+ subscriptionAccountHandles.contains(
+ TelecomUtil.getDefaultOutgoingPhoneAccount(context, PhoneAccount.SCHEME_TEL));
+
+ if (subscriptionAccountHandles.size() <= 1 || hasUserSelectedDefault) {
+ // Don't bring up the dialog for single-SIM or if the default outgoing account is
+ // a subscription account.
+ return TelecomUtil.handleMmi(context, input, null);
+ } else {
+ SelectPhoneAccountListener listener = new HandleMmiAccountSelectedCallback(context, input);
+
+ DialogFragment dialogFragment =
+ SelectPhoneAccountDialogFragment.newInstance(
+ subscriptionAccountHandles, listener, null);
+ dialogFragment.show(((Activity) context).getFragmentManager(), TAG_SELECT_ACCT_FRAGMENT);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ // TODO: Use TelephonyCapabilities.getDeviceIdLabel() to get the device id label instead of a
+ // hard-coded string.
+ static boolean handleDeviceIdDisplay(Context context, String input) {
+ TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+
+ if (telephonyManager != null && input.equals(MMI_IMEI_DISPLAY)) {
+ int labelResId =
+ (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM)
+ ? R.string.imei
+ : R.string.meid;
+
+ List<String> deviceIds = new ArrayList<String>();
+ if (TelephonyManagerCompat.getPhoneCount(telephonyManager) > 1
+ && CompatUtils.isMethodAvailable(
+ TelephonyManagerCompat.TELEPHONY_MANAGER_CLASS, "getDeviceId", Integer.TYPE)) {
+ for (int slot = 0; slot < telephonyManager.getPhoneCount(); slot++) {
+ String deviceId = telephonyManager.getDeviceId(slot);
+ if (!TextUtils.isEmpty(deviceId)) {
+ deviceIds.add(deviceId);
+ }
+ }
+ } else {
+ deviceIds.add(telephonyManager.getDeviceId());
+ }
+
+ new AlertDialog.Builder(context)
+ .setTitle(labelResId)
+ .setItems(deviceIds.toArray(new String[deviceIds.size()]), null)
+ .setPositiveButton(android.R.string.ok, null)
+ .setCancelable(false)
+ .show();
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean handleRegulatoryInfoDisplay(Context context, String input) {
+ if (input.equals(MMI_REGULATORY_INFO_DISPLAY)) {
+ Log.d(TAG, "handleRegulatoryInfoDisplay() sending intent to settings app");
+ Intent showRegInfoIntent = new Intent(Settings.ACTION_SHOW_REGULATORY_INFO);
+ try {
+ context.startActivity(showRegInfoIntent);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "startActivity() failed: " + e);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public static class HandleAdnEntryAccountSelectedCallback extends SelectPhoneAccountListener {
+
+ private final Context mContext;
+ private final QueryHandler mQueryHandler;
+ private final SimContactQueryCookie mCookie;
+
+ public HandleAdnEntryAccountSelectedCallback(
+ Context context, QueryHandler queryHandler, SimContactQueryCookie cookie) {
+ mContext = context;
+ mQueryHandler = queryHandler;
+ mCookie = cookie;
+ }
+
+ @Override
+ public void onPhoneAccountSelected(
+ PhoneAccountHandle selectedAccountHandle, boolean setDefault, @Nullable String callId) {
+ Uri uri = TelecomUtil.getAdnUriForPhoneAccount(mContext, selectedAccountHandle);
+ handleAdnQuery(mQueryHandler, mCookie, uri);
+ // TODO: Show error dialog if result isn't valid.
+ }
+ }
+
+ public static class HandleMmiAccountSelectedCallback extends SelectPhoneAccountListener {
+
+ private final Context mContext;
+ private final String mInput;
+
+ public HandleMmiAccountSelectedCallback(Context context, String input) {
+ mContext = context.getApplicationContext();
+ mInput = input;
+ }
+
+ @Override
+ public void onPhoneAccountSelected(
+ PhoneAccountHandle selectedAccountHandle, boolean setDefault, @Nullable String callId) {
+ TelecomUtil.handleMmi(mContext, mInput, selectedAccountHandle);
+ }
+ }
+
+ /**
+ * Cookie object that contains everything we need to communicate to the handler's onQuery
+ * Complete, as well as what we need in order to cancel the query (if requested).
+ *
+ * <p>Note, access to the textField field is going to be synchronized, because the user can
+ * request a cancel at any time through the UI.
+ */
+ private static class SimContactQueryCookie implements DialogInterface.OnCancelListener {
+
+ public ProgressDialog progressDialog;
+ public int contactNum;
+
+ // Used to identify the query request.
+ private int mToken;
+ private QueryHandler mHandler;
+
+ // The text field we're going to update
+ private EditText textField;
+
+ public SimContactQueryCookie(int number, QueryHandler handler, int token) {
+ contactNum = number;
+ mHandler = handler;
+ mToken = token;
+ }
+
+ /** Synchronized getter for the EditText. */
+ public synchronized EditText getTextField() {
+ return textField;
+ }
+
+ /** Synchronized setter for the EditText. */
+ public synchronized void setTextField(EditText text) {
+ textField = text;
+ }
+
+ /**
+ * Cancel the ADN query by stopping the operation and signaling the cookie that a cancel request
+ * is made.
+ */
+ @Override
+ public synchronized void onCancel(DialogInterface dialog) {
+ // close the progress dialog
+ if (progressDialog != null) {
+ progressDialog.dismiss();
+ }
+
+ // setting the textfield to null ensures that the UI does NOT get
+ // updated.
+ textField = null;
+
+ // Cancel the operation if possible.
+ mHandler.cancelOperation(mToken);
+ }
+ }
+
+ /**
+ * Asynchronous query handler that services requests to look up ADNs
+ *
+ * <p>Queries originate from {@link #handleAdnEntry}.
+ */
+ private static class QueryHandler extends NoNullCursorAsyncQueryHandler {
+
+ private boolean mCanceled;
+
+ public QueryHandler(ContentResolver cr) {
+ super(cr);
+ }
+
+ /** Override basic onQueryComplete to fill in the textfield when we're handed the ADN cursor. */
+ @Override
+ protected void onNotNullableQueryComplete(int token, Object cookie, Cursor c) {
+ try {
+ sPreviousAdnQueryHandler = null;
+ if (mCanceled) {
+ return;
+ }
+
+ SimContactQueryCookie sc = (SimContactQueryCookie) cookie;
+
+ // close the progress dialog.
+ sc.progressDialog.dismiss();
+
+ // get the EditText to update or see if the request was cancelled.
+ EditText text = sc.getTextField();
+
+ // if the TextView is valid, and the cursor is valid and positionable on the
+ // Nth number, then we update the text field and display a toast indicating the
+ // caller name.
+ if ((c != null) && (text != null) && (c.moveToPosition(sc.contactNum))) {
+ String name = c.getString(c.getColumnIndexOrThrow(ADN_NAME_COLUMN_NAME));
+ String number = c.getString(c.getColumnIndexOrThrow(ADN_PHONE_NUMBER_COLUMN_NAME));
+
+ // fill the text in.
+ text.getText().replace(0, 0, number);
+
+ // display the name as a toast
+ Context context = sc.progressDialog.getContext();
+ CharSequence msg =
+ ContactDisplayUtils.getTtsSpannedPhoneNumber(
+ context.getResources(), R.string.menu_callNumber, name);
+ Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
+ }
+ } finally {
+ MoreCloseables.closeQuietly(c);
+ }
+ }
+
+ public void cancel() {
+ mCanceled = true;
+ // Ask AsyncQueryHandler to cancel the whole request. This will fail when the query is
+ // already started.
+ cancelOperation(ADN_QUERY_TOKEN);
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/alert/AlertManager.java b/java/com/android/dialer/app/alert/AlertManager.java
new file mode 100644
index 000000000..ec6180262
--- /dev/null
+++ b/java/com/android/dialer/app/alert/AlertManager.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dialer.app.alert;
+
+import android.view.View;
+
+/** Manages "alerts" to gain the user's attention. */
+public interface AlertManager {
+
+ /** Inflates <code>layoutId</code> into a view that is ready to be inserted as an alert. */
+ View inflate(int layoutId);
+
+ void add(View view);
+
+ void clear();
+}
diff --git a/java/com/android/dialer/app/bindings/DialerBindings.java b/java/com/android/dialer/app/bindings/DialerBindings.java
new file mode 100644
index 000000000..e1f517860
--- /dev/null
+++ b/java/com/android/dialer/app/bindings/DialerBindings.java
@@ -0,0 +1,25 @@
+/*
+ * 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.dialer.app.bindings;
+
+import com.android.dialer.common.ConfigProvider;
+
+/** This interface allows the container application to customize the dialer. */
+public interface DialerBindings {
+
+ ConfigProvider getConfigProvider();
+}
diff --git a/java/com/android/dialer/app/bindings/DialerBindingsFactory.java b/java/com/android/dialer/app/bindings/DialerBindingsFactory.java
new file mode 100644
index 000000000..9f209f99e
--- /dev/null
+++ b/java/com/android/dialer/app/bindings/DialerBindingsFactory.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dialer.app.bindings;
+
+/**
+ * This interface should be implementated by the Application subclass. It allows the dialer module
+ * to get references to the DialerBindings.
+ */
+public interface DialerBindingsFactory {
+
+ DialerBindings newDialerBindings();
+}
diff --git a/java/com/android/dialer/app/bindings/DialerBindingsStub.java b/java/com/android/dialer/app/bindings/DialerBindingsStub.java
new file mode 100644
index 000000000..f56743fa5
--- /dev/null
+++ b/java/com/android/dialer/app/bindings/DialerBindingsStub.java
@@ -0,0 +1,48 @@
+/*
+ * 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.dialer.app.bindings;
+
+import com.android.dialer.common.ConfigProvider;
+
+/** Default implementation for dialer bindings. */
+public class DialerBindingsStub implements DialerBindings {
+ private ConfigProvider configProvider;
+
+ @Override
+ public ConfigProvider getConfigProvider() {
+ if (configProvider == null) {
+ configProvider =
+ new ConfigProvider() {
+ @Override
+ public String getString(String key, String defaultValue) {
+ return defaultValue;
+ }
+
+ @Override
+ public long getLong(String key, long defaultValue) {
+ return defaultValue;
+ }
+
+ @Override
+ public boolean getBoolean(String key, boolean defaultValue) {
+ return defaultValue;
+ }
+ };
+ }
+ return configProvider;
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/BlockReportSpamListener.java b/java/com/android/dialer/app/calllog/BlockReportSpamListener.java
new file mode 100644
index 000000000..66f40bcd7
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/BlockReportSpamListener.java
@@ -0,0 +1,212 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.app.FragmentManager;
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.support.v7.widget.RecyclerView;
+import com.android.dialer.blocking.BlockReportSpamDialogs;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.logging.nano.ReportingLocation;
+import com.android.dialer.spam.Spam;
+
+/** Listener to show dialogs for block and report spam actions. */
+public class BlockReportSpamListener implements CallLogListItemViewHolder.OnClickListener {
+
+ private final Context mContext;
+ private final FragmentManager mFragmentManager;
+ private final RecyclerView.Adapter mAdapter;
+ private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
+
+ public BlockReportSpamListener(
+ Context context,
+ FragmentManager fragmentManager,
+ RecyclerView.Adapter adapter,
+ FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler) {
+ mContext = context;
+ mFragmentManager = fragmentManager;
+ mAdapter = adapter;
+ mFilteredNumberAsyncQueryHandler = filteredNumberAsyncQueryHandler;
+ }
+
+ @Override
+ public void onBlockReportSpam(
+ String displayNumber,
+ final String number,
+ final String countryIso,
+ final int callType,
+ final int contactSourceType) {
+ BlockReportSpamDialogs.BlockReportSpamDialogFragment.newInstance(
+ displayNumber,
+ Spam.get(mContext).isDialogReportSpamCheckedByDefault(),
+ new BlockReportSpamDialogs.OnSpamDialogClickListener() {
+ @Override
+ public void onClick(boolean isSpamChecked) {
+ LogUtil.i("BlockReportSpamListener.onBlockReportSpam", "onClick");
+ if (isSpamChecked && Spam.get(mContext).isSpamEnabled()) {
+ Logger.get(mContext)
+ .logImpression(
+ DialerImpression.Type
+ .REPORT_CALL_AS_SPAM_VIA_CALL_LOG_BLOCK_REPORT_SPAM_SENT_VIA_BLOCK_NUMBER_DIALOG);
+ Spam.get(mContext)
+ .reportSpamFromCallHistory(
+ number,
+ countryIso,
+ callType,
+ ReportingLocation.Type.CALL_LOG_HISTORY,
+ contactSourceType);
+ }
+ mFilteredNumberAsyncQueryHandler.blockNumber(
+ new FilteredNumberAsyncQueryHandler.OnBlockNumberListener() {
+ @Override
+ public void onBlockComplete(Uri uri) {
+ Logger.get(mContext)
+ .logImpression(DialerImpression.Type.USER_ACTION_BLOCKED_NUMBER);
+ mAdapter.notifyDataSetChanged();
+ }
+ },
+ number,
+ countryIso);
+ }
+ },
+ null)
+ .show(mFragmentManager, BlockReportSpamDialogs.BLOCK_REPORT_SPAM_DIALOG_TAG);
+ }
+
+ @Override
+ public void onBlock(
+ String displayNumber,
+ final String number,
+ final String countryIso,
+ final int callType,
+ final int contactSourceType) {
+ BlockReportSpamDialogs.BlockDialogFragment.newInstance(
+ displayNumber,
+ Spam.get(mContext).isSpamEnabled(),
+ new BlockReportSpamDialogs.OnConfirmListener() {
+ @Override
+ public void onClick() {
+ LogUtil.i("BlockReportSpamListener.onBlock", "onClick");
+ if (Spam.get(mContext).isSpamEnabled()) {
+ Logger.get(mContext)
+ .logImpression(
+ DialerImpression.Type
+ .DIALOG_ACTION_CONFIRM_NUMBER_SPAM_INDIRECTLY_VIA_BLOCK_NUMBER);
+ Spam.get(mContext)
+ .reportSpamFromCallHistory(
+ number,
+ countryIso,
+ callType,
+ ReportingLocation.Type.CALL_LOG_HISTORY,
+ contactSourceType);
+ }
+ mFilteredNumberAsyncQueryHandler.blockNumber(
+ new FilteredNumberAsyncQueryHandler.OnBlockNumberListener() {
+ @Override
+ public void onBlockComplete(Uri uri) {
+ Logger.get(mContext)
+ .logImpression(DialerImpression.Type.USER_ACTION_BLOCKED_NUMBER);
+ mAdapter.notifyDataSetChanged();
+ }
+ },
+ number,
+ countryIso);
+ }
+ },
+ null)
+ .show(mFragmentManager, BlockReportSpamDialogs.BLOCK_DIALOG_TAG);
+ }
+
+ @Override
+ public void onUnblock(
+ String displayNumber,
+ final String number,
+ final String countryIso,
+ final int callType,
+ final int contactSourceType,
+ final boolean isSpam,
+ final Integer blockId) {
+ BlockReportSpamDialogs.UnblockDialogFragment.newInstance(
+ displayNumber,
+ isSpam,
+ new BlockReportSpamDialogs.OnConfirmListener() {
+ @Override
+ public void onClick() {
+ LogUtil.i("BlockReportSpamListener.onUnblock", "onClick");
+ if (isSpam && Spam.get(mContext).isSpamEnabled()) {
+ Logger.get(mContext)
+ .logImpression(DialerImpression.Type.REPORT_AS_NOT_SPAM_VIA_UNBLOCK_NUMBER);
+ Spam.get(mContext)
+ .reportNotSpamFromCallHistory(
+ number,
+ countryIso,
+ callType,
+ ReportingLocation.Type.CALL_LOG_HISTORY,
+ contactSourceType);
+ }
+ mFilteredNumberAsyncQueryHandler.unblock(
+ new FilteredNumberAsyncQueryHandler.OnUnblockNumberListener() {
+ @Override
+ public void onUnblockComplete(int rows, ContentValues values) {
+ Logger.get(mContext)
+ .logImpression(DialerImpression.Type.USER_ACTION_UNBLOCKED_NUMBER);
+ mAdapter.notifyDataSetChanged();
+ }
+ },
+ blockId);
+ }
+ },
+ null)
+ .show(mFragmentManager, BlockReportSpamDialogs.UNBLOCK_DIALOG_TAG);
+ }
+
+ @Override
+ public void onReportNotSpam(
+ String displayNumber,
+ final String number,
+ final String countryIso,
+ final int callType,
+ final int contactSourceType) {
+ BlockReportSpamDialogs.ReportNotSpamDialogFragment.newInstance(
+ displayNumber,
+ new BlockReportSpamDialogs.OnConfirmListener() {
+ @Override
+ public void onClick() {
+ LogUtil.i("BlockReportSpamListener.onReportNotSpam", "onClick");
+ if (Spam.get(mContext).isSpamEnabled()) {
+ Logger.get(mContext)
+ .logImpression(DialerImpression.Type.DIALOG_ACTION_CONFIRM_NUMBER_NOT_SPAM);
+ Spam.get(mContext)
+ .reportNotSpamFromCallHistory(
+ number,
+ countryIso,
+ callType,
+ ReportingLocation.Type.CALL_LOG_HISTORY,
+ contactSourceType);
+ }
+ mAdapter.notifyDataSetChanged();
+ }
+ },
+ null)
+ .show(mFragmentManager, BlockReportSpamDialogs.NOT_SPAM_DIALOG_TAG);
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallDetailHistoryAdapter.java b/java/com/android/dialer/app/calllog/CallDetailHistoryAdapter.java
new file mode 100644
index 000000000..ab6ef7362
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallDetailHistoryAdapter.java
@@ -0,0 +1,214 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.content.Context;
+import android.icu.lang.UCharacter;
+import android.icu.text.BreakIterator;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.CallLog.Calls;
+import android.text.format.DateUtils;
+import android.text.format.Formatter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+import com.android.dialer.app.PhoneCallDetails;
+import com.android.dialer.app.R;
+import com.android.dialer.util.CallUtil;
+import com.android.dialer.util.DialerUtils;
+import java.util.ArrayList;
+import java.util.Locale;
+
+/** Adapter for a ListView containing history items from the details of a call. */
+public class CallDetailHistoryAdapter extends BaseAdapter {
+
+ /** Each history item shows the detail of a call. */
+ private static final int VIEW_TYPE_HISTORY_ITEM = 1;
+
+ private final Context mContext;
+ private final LayoutInflater mLayoutInflater;
+ private final CallTypeHelper mCallTypeHelper;
+ private final PhoneCallDetails[] mPhoneCallDetails;
+
+ /** List of items to be concatenated together for duration strings. */
+ private ArrayList<CharSequence> mDurationItems = new ArrayList<>();
+
+ public CallDetailHistoryAdapter(
+ Context context,
+ LayoutInflater layoutInflater,
+ CallTypeHelper callTypeHelper,
+ PhoneCallDetails[] phoneCallDetails) {
+ mContext = context;
+ mLayoutInflater = layoutInflater;
+ mCallTypeHelper = callTypeHelper;
+ mPhoneCallDetails = phoneCallDetails;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ // None of history will be clickable.
+ return false;
+ }
+
+ @Override
+ public int getCount() {
+ return mPhoneCallDetails.length;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mPhoneCallDetails[position];
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return VIEW_TYPE_HISTORY_ITEM;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // Make sure we have a valid convertView to start with
+ final View result =
+ convertView == null
+ ? mLayoutInflater.inflate(R.layout.call_detail_history_item, parent, false)
+ : convertView;
+
+ PhoneCallDetails details = mPhoneCallDetails[position];
+ CallTypeIconsView callTypeIconView =
+ (CallTypeIconsView) result.findViewById(R.id.call_type_icon);
+ TextView callTypeTextView = (TextView) result.findViewById(R.id.call_type_text);
+ TextView dateView = (TextView) result.findViewById(R.id.date);
+ TextView durationView = (TextView) result.findViewById(R.id.duration);
+
+ int callType = details.callTypes[0];
+ boolean isVideoCall =
+ (details.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO
+ && CallUtil.isVideoEnabled(mContext);
+ boolean isPulledCall =
+ (details.features & Calls.FEATURES_PULLED_EXTERNALLY) == Calls.FEATURES_PULLED_EXTERNALLY;
+
+ callTypeIconView.clear();
+ callTypeIconView.add(callType);
+ callTypeIconView.setShowVideo(isVideoCall);
+ callTypeTextView.setText(mCallTypeHelper.getCallTypeText(callType, isVideoCall, isPulledCall));
+ // Set the date.
+ dateView.setText(formatDate(details.date));
+ // Set the duration
+ if (Calls.VOICEMAIL_TYPE == callType || CallTypeHelper.isMissedCallType(callType)) {
+ durationView.setVisibility(View.GONE);
+ } else {
+ durationView.setVisibility(View.VISIBLE);
+ durationView.setText(formatDurationAndDataUsage(details.duration, details.dataUsage));
+ }
+
+ return result;
+ }
+
+ /**
+ * Formats the provided date into a value suitable for display in the current locale.
+ *
+ * <p>For example, returns a string like "Wednesday, May 25, 2016, 8:02PM" or "Chorshanba, 2016
+ * may 25,20:02".
+ *
+ * <p>For pre-N devices, the returned value may not start with a capital if the local convention
+ * is to not capitalize day names. On N+ devices, the returned value is always capitalized.
+ */
+ private CharSequence formatDate(long callDateMillis) {
+ CharSequence dateValue =
+ DateUtils.formatDateRange(
+ mContext,
+ callDateMillis /* startDate */,
+ callDateMillis /* endDate */,
+ DateUtils.FORMAT_SHOW_TIME
+ | DateUtils.FORMAT_SHOW_DATE
+ | DateUtils.FORMAT_SHOW_WEEKDAY
+ | DateUtils.FORMAT_SHOW_YEAR);
+
+ // We want the beginning of the date string to be capitalized, even if the word at the beginning
+ // of the string is not usually capitalized. For example, "Wednesdsay" in Uzbek is "chorshanba”
+ // (not capitalized). To handle this issue we apply title casing to the start of the sentence so
+ // that "chorshanba, 2016 may 25,20:02" becomes "Chorshanba, 2016 may 25,20:02".
+ //
+ // The ICU library was not available in Android until N, so we can only do this in N+ devices.
+ // Pre-N devices will still see incorrect capitalization in some languages.
+ if (VERSION.SDK_INT < VERSION_CODES.N) {
+ return dateValue;
+ }
+
+ // Using the ICU library is safer than just applying toUpperCase() on the first letter of the
+ // word because in some languages, there can be multiple starting characters which should be
+ // upper-cased together. For example in Dutch "ij" is a digraph in which both letters should be
+ // capitalized together.
+
+ // TITLECASE_NO_LOWERCASE is necessary so that things that are already capitalized like the
+ // month ("May") are not lower-cased as part of the conversion.
+ return UCharacter.toTitleCase(
+ Locale.getDefault(),
+ dateValue.toString(),
+ BreakIterator.getSentenceInstance(),
+ UCharacter.TITLECASE_NO_LOWERCASE);
+ }
+
+ private CharSequence formatDuration(long elapsedSeconds) {
+ long minutes = 0;
+ long seconds = 0;
+
+ if (elapsedSeconds >= 60) {
+ minutes = elapsedSeconds / 60;
+ elapsedSeconds -= minutes * 60;
+ seconds = elapsedSeconds;
+ return mContext.getString(R.string.callDetailsDurationFormat, minutes, seconds);
+ } else {
+ seconds = elapsedSeconds;
+ return mContext.getString(R.string.callDetailsShortDurationFormat, seconds);
+ }
+ }
+
+ /**
+ * Formats a string containing the call duration and the data usage (if specified).
+ *
+ * @param elapsedSeconds Total elapsed seconds.
+ * @param dataUsage Data usage in bytes, or null if not specified.
+ * @return String containing call duration and data usage.
+ */
+ private CharSequence formatDurationAndDataUsage(long elapsedSeconds, Long dataUsage) {
+ CharSequence duration = formatDuration(elapsedSeconds);
+
+ if (dataUsage != null) {
+ mDurationItems.clear();
+ mDurationItems.add(duration);
+ mDurationItems.add(Formatter.formatShortFileSize(mContext, dataUsage));
+
+ return DialerUtils.join(mDurationItems);
+ } else {
+ return duration;
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallLogAdapter.java b/java/com/android/dialer/app/calllog/CallLogAdapter.java
new file mode 100644
index 000000000..ea09a8c0a
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallLogAdapter.java
@@ -0,0 +1,915 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.Trace;
+import android.provider.CallLog;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.annotation.WorkerThread;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.contacts.common.ContactsUtils;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+import com.android.contacts.common.preference.ContactsPreferences;
+import com.android.dialer.app.Bindings;
+import com.android.dialer.app.DialtactsActivity;
+import com.android.dialer.app.PhoneCallDetails;
+import com.android.dialer.app.R;
+import com.android.dialer.app.calllog.CallLogGroupBuilder.GroupCreator;
+import com.android.dialer.app.calllog.calllogcache.CallLogCache;
+import com.android.dialer.app.contactinfo.ContactInfoCache;
+import com.android.dialer.app.voicemail.VoicemailPlaybackPresenter;
+import com.android.dialer.app.voicemail.VoicemailPlaybackPresenter.OnVoicemailDeletedListener;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.AsyncTaskExecutor;
+import com.android.dialer.common.AsyncTaskExecutors;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.enrichedcall.EnrichedCallCapabilities;
+import com.android.dialer.enrichedcall.EnrichedCallManager;
+import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.phonenumbercache.CallLogQuery;
+import com.android.dialer.phonenumbercache.ContactInfo;
+import com.android.dialer.phonenumbercache.ContactInfoHelper;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.android.dialer.spam.Spam;
+import com.android.dialer.util.PermissionsUtil;
+import java.util.Map;
+import java.util.Set;
+
+/** Adapter class to fill in data for the Call Log. */
+public class CallLogAdapter extends GroupingListAdapter
+ implements GroupCreator, OnVoicemailDeletedListener, CapabilitiesListener {
+
+ // Types of activities the call log adapter is used for
+ public static final int ACTIVITY_TYPE_CALL_LOG = 1;
+ public static final int ACTIVITY_TYPE_DIALTACTS = 2;
+ private static final int NO_EXPANDED_LIST_ITEM = -1;
+ public static final int ALERT_POSITION = 0;
+ private static final int VIEW_TYPE_ALERT = 1;
+ private static final int VIEW_TYPE_CALLLOG = 2;
+
+ private static final String KEY_EXPANDED_POSITION = "expanded_position";
+ private static final String KEY_EXPANDED_ROW_ID = "expanded_row_id";
+
+ public static final String LOAD_DATA_TASK_IDENTIFIER = "load_data";
+
+ protected final Activity mActivity;
+ protected final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
+ /** Cache for repeated requests to Telecom/Telephony. */
+ protected final CallLogCache mCallLogCache;
+
+ private final CallFetcher mCallFetcher;
+ private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
+ private final int mActivityType;
+
+ /** Instance of helper class for managing views. */
+ private final CallLogListItemHelper mCallLogListItemHelper;
+ /** Helper to group call log entries. */
+ private final CallLogGroupBuilder mCallLogGroupBuilder;
+
+ private final AsyncTaskExecutor mAsyncTaskExecutor = AsyncTaskExecutors.createAsyncTaskExecutor();
+ private ContactInfoCache mContactInfoCache;
+ // Tracks the position of the currently expanded list item.
+ private int mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
+ // Tracks the rowId of the currently expanded list item, so the position can be updated if there
+ // are any changes to the call log entries, such as additions or removals.
+ private long mCurrentlyExpandedRowId = NO_EXPANDED_LIST_ITEM;
+
+ private final CallLogAlertManager mCallLogAlertManager;
+ /** The OnClickListener used to expand or collapse the action buttons of a call log entry. */
+ private final View.OnClickListener mExpandCollapseListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder) v.getTag();
+ if (viewHolder == null) {
+ return;
+ }
+
+ if (mVoicemailPlaybackPresenter != null) {
+ // Always reset the voicemail playback state on expand or collapse.
+ mVoicemailPlaybackPresenter.resetAll();
+ }
+
+ if (viewHolder.rowId == mCurrentlyExpandedRowId) {
+ // Hide actions, if the clicked item is the expanded item.
+ viewHolder.showActions(false);
+
+ mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
+ mCurrentlyExpandedRowId = NO_EXPANDED_LIST_ITEM;
+ } else {
+ if (viewHolder.callType == CallLog.Calls.MISSED_TYPE) {
+ CallLogAsyncTaskUtil.markCallAsRead(mActivity, viewHolder.callIds);
+ if (mActivityType == ACTIVITY_TYPE_DIALTACTS) {
+ ((DialtactsActivity) v.getContext()).updateTabUnreadCounts();
+ }
+ }
+ expandViewHolderActions(viewHolder);
+ }
+ }
+ };
+
+ /**
+ * A list of {@link CallLogQuery#ID} that will be hidden. The hide might be temporary so instead
+ * if removing an item, it will be shown as an invisible view. This simplifies the calculation of
+ * item position.
+ */
+ @NonNull private Set<Long> mHiddenRowIds = new ArraySet<>();
+ /**
+ * Holds a list of URIs that are pending deletion or undo. If the activity ends before the undo
+ * timeout, all of the pending URIs will be deleted.
+ *
+ * <p>TODO: move this and OnVoicemailDeletedListener to somewhere like {@link
+ * VisualVoicemailCallLogFragment}. The CallLogAdapter does not need to know about what to do with
+ * hidden item or what to hide.
+ */
+ @NonNull private final Set<Uri> mHiddenItemUris = new ArraySet<>();
+
+ private CallLogListItemViewHolder.OnClickListener mBlockReportSpamListener;
+ /**
+ * Map, keyed by call Id, used to track the day group for a call. As call log entries are put into
+ * the primary call groups in {@link com.android.dialer.app.calllog.CallLogGroupBuilder}, they are
+ * also assigned a secondary "day group". This map tracks the day group assigned to all calls in
+ * the call log. This information is used to trigger the display of a day group header above the
+ * call log entry at the start of a day group. Note: Multiple calls are grouped into a single
+ * primary "call group" in the call log, and the cursor used to bind rows includes all of these
+ * calls. When determining if a day group change has occurred it is necessary to look at the last
+ * entry in the call log to determine its day group. This map provides a means of determining the
+ * previous day group without having to reverse the cursor to the start of the previous day call
+ * log entry.
+ */
+ private Map<Long, Integer> mDayGroups = new ArrayMap<>();
+
+ private boolean mLoading = true;
+ private ContactsPreferences mContactsPreferences;
+
+ private boolean mIsSpamEnabled;
+
+ @NonNull private final EnrichedCallManager mEnrichedCallManager;
+
+ public CallLogAdapter(
+ Activity activity,
+ ViewGroup alertContainer,
+ CallFetcher callFetcher,
+ CallLogCache callLogCache,
+ ContactInfoCache contactInfoCache,
+ VoicemailPlaybackPresenter voicemailPlaybackPresenter,
+ int activityType) {
+ super();
+
+ mActivity = activity;
+ mCallFetcher = callFetcher;
+ mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
+ if (mVoicemailPlaybackPresenter != null) {
+ mVoicemailPlaybackPresenter.setOnVoicemailDeletedListener(this);
+ }
+
+ mActivityType = activityType;
+
+ mContactInfoCache = contactInfoCache;
+
+ if (!PermissionsUtil.hasContactsPermissions(activity)) {
+ mContactInfoCache.disableRequestProcessing();
+ }
+
+ Resources resources = mActivity.getResources();
+
+ mCallLogCache = callLogCache;
+
+ PhoneCallDetailsHelper phoneCallDetailsHelper =
+ new PhoneCallDetailsHelper(mActivity, resources, mCallLogCache);
+ mCallLogListItemHelper =
+ new CallLogListItemHelper(phoneCallDetailsHelper, resources, mCallLogCache);
+ mCallLogGroupBuilder = new CallLogGroupBuilder(this);
+ mFilteredNumberAsyncQueryHandler = new FilteredNumberAsyncQueryHandler(mActivity);
+
+ mContactsPreferences = new ContactsPreferences(mActivity);
+
+ mBlockReportSpamListener =
+ new BlockReportSpamListener(
+ mActivity,
+ ((Activity) mActivity).getFragmentManager(),
+ this,
+ mFilteredNumberAsyncQueryHandler);
+ setHasStableIds(true);
+
+ mCallLogAlertManager =
+ new CallLogAlertManager(this, LayoutInflater.from(mActivity), alertContainer);
+ mEnrichedCallManager = EnrichedCallManager.Accessor.getInstance(activity.getApplication());
+ }
+
+ private void expandViewHolderActions(CallLogListItemViewHolder viewHolder) {
+ if (!TextUtils.isEmpty(viewHolder.voicemailUri)) {
+ Logger.get(mActivity).logImpression(DialerImpression.Type.VOICEMAIL_EXPAND_ENTRY);
+ }
+
+ int lastExpandedPosition = mCurrentlyExpandedPosition;
+ // Show the actions for the clicked list item.
+ viewHolder.showActions(true);
+ mCurrentlyExpandedPosition = viewHolder.getAdapterPosition();
+ mCurrentlyExpandedRowId = viewHolder.rowId;
+
+ // If another item is expanded, notify it that it has changed. Its actions will be
+ // hidden when it is re-binded because we change mCurrentlyExpandedRowId above.
+ if (lastExpandedPosition != RecyclerView.NO_POSITION) {
+ notifyItemChanged(lastExpandedPosition);
+ }
+ }
+
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putInt(KEY_EXPANDED_POSITION, mCurrentlyExpandedPosition);
+ outState.putLong(KEY_EXPANDED_ROW_ID, mCurrentlyExpandedRowId);
+ }
+
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ if (savedInstanceState != null) {
+ mCurrentlyExpandedPosition =
+ savedInstanceState.getInt(KEY_EXPANDED_POSITION, RecyclerView.NO_POSITION);
+ mCurrentlyExpandedRowId =
+ savedInstanceState.getLong(KEY_EXPANDED_ROW_ID, NO_EXPANDED_LIST_ITEM);
+ }
+ }
+
+ /** Requery on background thread when {@link Cursor} changes. */
+ @Override
+ protected void onContentChanged() {
+ mCallFetcher.fetchCalls();
+ }
+
+ public void setLoading(boolean loading) {
+ mLoading = loading;
+ }
+
+ public boolean isEmpty() {
+ if (mLoading) {
+ // We don't want the empty state to show when loading.
+ return false;
+ } else {
+ return getItemCount() == 0;
+ }
+ }
+
+ public void clearFilteredNumbersCache() {
+ mFilteredNumberAsyncQueryHandler.clearCache();
+ }
+
+ public void onResume() {
+ if (PermissionsUtil.hasPermission(mActivity, android.Manifest.permission.READ_CONTACTS)) {
+ mContactInfoCache.start();
+ }
+ mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
+ mIsSpamEnabled = Spam.get(mActivity).isSpamEnabled();
+ mEnrichedCallManager.registerCapabilitiesListener(this);
+ notifyDataSetChanged();
+ }
+
+ public void onPause() {
+ pauseCache();
+ for (Uri uri : mHiddenItemUris) {
+ CallLogAsyncTaskUtil.deleteVoicemail(mActivity, uri, null);
+ }
+ mEnrichedCallManager.unregisterCapabilitiesListener(this);
+ }
+
+ public void onStop() {
+ mEnrichedCallManager.clearCachedData();
+ }
+
+ public CallLogAlertManager getAlertManager() {
+ return mCallLogAlertManager;
+ }
+
+ @VisibleForTesting
+ /* package */ void pauseCache() {
+ mContactInfoCache.stop();
+ mCallLogCache.reset();
+ }
+
+ @Override
+ protected void addGroups(Cursor cursor) {
+ mCallLogGroupBuilder.addGroups(cursor);
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ if (viewType == VIEW_TYPE_ALERT) {
+ return mCallLogAlertManager.createViewHolder(parent);
+ }
+ return createCallLogEntryViewHolder(parent);
+ }
+
+ /**
+ * Creates a new call log entry {@link ViewHolder}.
+ *
+ * @param parent the parent view.
+ * @return The {@link ViewHolder}.
+ */
+ private ViewHolder createCallLogEntryViewHolder(ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(mActivity);
+ View view = inflater.inflate(R.layout.call_log_list_item, parent, false);
+ CallLogListItemViewHolder viewHolder =
+ CallLogListItemViewHolder.create(
+ view,
+ mActivity,
+ mBlockReportSpamListener,
+ mExpandCollapseListener,
+ mCallLogCache,
+ mCallLogListItemHelper,
+ mVoicemailPlaybackPresenter);
+
+ viewHolder.callLogEntryView.setTag(viewHolder);
+
+ viewHolder.primaryActionView.setTag(viewHolder);
+
+ return viewHolder;
+ }
+
+ /**
+ * Binds the views in the entry to the data in the call log. TODO: This gets called 20-30 times
+ * when Dialer starts up for a single call log entry and should not. It invokes cross-process
+ * methods and the repeat execution can get costly.
+ *
+ * @param viewHolder The view corresponding to this entry.
+ * @param position The position of the entry.
+ */
+ @Override
+ public void onBindViewHolder(ViewHolder viewHolder, int position) {
+ Trace.beginSection("onBindViewHolder: " + position);
+ switch (getItemViewType(position)) {
+ case VIEW_TYPE_ALERT:
+ //Do nothing
+ break;
+ default:
+ bindCallLogListViewHolder(viewHolder, position);
+ break;
+ }
+ Trace.endSection();
+ }
+
+ @Override
+ public void onViewRecycled(ViewHolder viewHolder) {
+ if (viewHolder.getItemViewType() == VIEW_TYPE_CALLLOG) {
+ CallLogListItemViewHolder views = (CallLogListItemViewHolder) viewHolder;
+ if (views.asyncTask != null) {
+ views.asyncTask.cancel(true);
+ }
+ }
+ }
+
+ @Override
+ public void onViewAttachedToWindow(ViewHolder viewHolder) {
+ if (viewHolder.getItemViewType() == VIEW_TYPE_CALLLOG) {
+ ((CallLogListItemViewHolder) viewHolder).isAttachedToWindow = true;
+ }
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(ViewHolder viewHolder) {
+ if (viewHolder.getItemViewType() == VIEW_TYPE_CALLLOG) {
+ ((CallLogListItemViewHolder) viewHolder).isAttachedToWindow = false;
+ }
+ }
+
+ /**
+ * Binds the view holder for the call log list item view.
+ *
+ * @param viewHolder The call log list item view holder.
+ * @param position The position of the list item.
+ */
+ private void bindCallLogListViewHolder(final ViewHolder viewHolder, final int position) {
+ Cursor c = (Cursor) getItem(position);
+ if (c == null) {
+ return;
+ }
+ CallLogListItemViewHolder views = (CallLogListItemViewHolder) viewHolder;
+ views.isLoaded = false;
+ PhoneCallDetails details = createPhoneCallDetails(c, getGroupSize(position), views);
+ if (mHiddenRowIds.contains(c.getLong(CallLogQuery.ID))) {
+ views.callLogEntryView.setVisibility(View.GONE);
+ views.dayGroupHeader.setVisibility(View.GONE);
+ return;
+ } else {
+ views.callLogEntryView.setVisibility(View.VISIBLE);
+ // dayGroupHeader will be restored after loadAndRender() if it is needed.
+ }
+ if (mCurrentlyExpandedRowId == views.rowId) {
+ views.inflateActionViewStub();
+ }
+ loadAndRender(views, views.rowId, details);
+ }
+
+ private void loadAndRender(
+ final CallLogListItemViewHolder views, final long rowId, final PhoneCallDetails details) {
+ // Reset block and spam information since this view could be reused which may contain
+ // outdated data.
+ views.isSpam = false;
+ views.blockId = null;
+ views.isSpamFeatureEnabled = false;
+ views.isCallComposerCapable =
+ isCallComposerCapable(PhoneNumberUtils.formatNumberToE164(views.number, views.countryIso));
+ final AsyncTask<Void, Void, Boolean> loadDataTask =
+ new AsyncTask<Void, Void, Boolean>() {
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ views.blockId =
+ mFilteredNumberAsyncQueryHandler.getBlockedIdSynchronousForCalllogOnly(
+ views.number, views.countryIso);
+ details.isBlocked = views.blockId != null;
+ if (isCancelled()) {
+ return false;
+ }
+ if (mIsSpamEnabled) {
+ views.isSpamFeatureEnabled = true;
+ // Only display the call as a spam call if there are incoming calls in the list.
+ // Call log cards with only outgoing calls should never be displayed as spam.
+ views.isSpam =
+ details.hasIncomingCalls()
+ && Spam.get(mActivity)
+ .checkSpamStatusSynchronous(views.number, views.countryIso);
+ details.isSpam = views.isSpam;
+ if (isCancelled()) {
+ return false;
+ }
+ return loadData(views, rowId, details);
+ } else {
+ return loadData(views, rowId, details);
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Boolean success) {
+ views.isLoaded = true;
+ if (success) {
+ int currentGroup = getDayGroupForCall(views.rowId);
+ if (currentGroup != details.previousGroup) {
+ views.dayGroupHeaderVisibility = View.VISIBLE;
+ views.dayGroupHeaderText = getGroupDescription(currentGroup);
+ } else {
+ views.dayGroupHeaderVisibility = View.GONE;
+ }
+ render(views, details, rowId);
+ }
+ }
+ };
+
+ views.asyncTask = loadDataTask;
+ mAsyncTaskExecutor.submit(LOAD_DATA_TASK_IDENTIFIER, loadDataTask);
+ }
+
+ @MainThread
+ private boolean isCallComposerCapable(@Nullable String e164Number) {
+ if (e164Number == null) {
+ return false;
+ }
+
+ EnrichedCallCapabilities capabilities = mEnrichedCallManager.getCapabilities(e164Number);
+ if (capabilities == null) {
+ mEnrichedCallManager.requestCapabilities(e164Number);
+ return false;
+ }
+ return capabilities.supportsCallComposer();
+ }
+
+ /**
+ * Initialize PhoneCallDetails by reading all data from cursor. This method must be run on main
+ * thread since cursor is not thread safe.
+ */
+ @MainThread
+ private PhoneCallDetails createPhoneCallDetails(
+ Cursor cursor, int count, final CallLogListItemViewHolder views) {
+ Assert.isMainThread();
+ final String number = cursor.getString(CallLogQuery.NUMBER);
+ final String postDialDigits =
+ (VERSION.SDK_INT >= VERSION_CODES.N) ? cursor.getString(CallLogQuery.POST_DIAL_DIGITS) : "";
+ final String viaNumber =
+ (VERSION.SDK_INT >= VERSION_CODES.N) ? cursor.getString(CallLogQuery.VIA_NUMBER) : "";
+ final int numberPresentation = cursor.getInt(CallLogQuery.NUMBER_PRESENTATION);
+ final ContactInfo cachedContactInfo = ContactInfoHelper.getContactInfo(cursor);
+ final PhoneCallDetails details =
+ new PhoneCallDetails(number, numberPresentation, postDialDigits);
+ details.viaNumber = viaNumber;
+ details.countryIso = cursor.getString(CallLogQuery.COUNTRY_ISO);
+ details.date = cursor.getLong(CallLogQuery.DATE);
+ details.duration = cursor.getLong(CallLogQuery.DURATION);
+ details.features = getCallFeatures(cursor, count);
+ details.geocode = cursor.getString(CallLogQuery.GEOCODED_LOCATION);
+ details.transcription = cursor.getString(CallLogQuery.TRANSCRIPTION);
+ details.callTypes = getCallTypes(cursor, count);
+
+ details.accountComponentName = cursor.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME);
+ details.accountId = cursor.getString(CallLogQuery.ACCOUNT_ID);
+ details.cachedContactInfo = cachedContactInfo;
+
+ if (!cursor.isNull(CallLogQuery.DATA_USAGE)) {
+ details.dataUsage = cursor.getLong(CallLogQuery.DATA_USAGE);
+ }
+
+ views.rowId = cursor.getLong(CallLogQuery.ID);
+ // Stash away the Ids of the calls so that we can support deleting a row in the call log.
+ views.callIds = getCallIds(cursor, count);
+ details.previousGroup = getPreviousDayGroup(cursor);
+
+ // Store values used when the actions ViewStub is inflated on expansion.
+ views.number = number;
+ views.countryIso = details.countryIso;
+ views.postDialDigits = details.postDialDigits;
+ views.numberPresentation = numberPresentation;
+
+ if (details.callTypes[0] == CallLog.Calls.VOICEMAIL_TYPE
+ || details.callTypes[0] == CallLog.Calls.MISSED_TYPE) {
+ details.isRead = cursor.getInt(CallLogQuery.IS_READ) == 1;
+ }
+ views.callType = cursor.getInt(CallLogQuery.CALL_TYPE);
+ views.voicemailUri = cursor.getString(CallLogQuery.VOICEMAIL_URI);
+
+ return details;
+ }
+
+ /**
+ * Load data for call log. Any expensive operation should be put here to avoid blocking main
+ * thread. Do NOT put any cursor operation here since it's not thread safe.
+ */
+ @WorkerThread
+ private boolean loadData(CallLogListItemViewHolder views, long rowId, PhoneCallDetails details) {
+ Assert.isWorkerThread();
+ if (rowId != views.rowId) {
+ LogUtil.i(
+ "CallLogAdapter.loadData",
+ "rowId of viewHolder changed after load task is issued, aborting load");
+ return false;
+ }
+
+ final PhoneAccountHandle accountHandle =
+ PhoneAccountUtils.getAccount(details.accountComponentName, details.accountId);
+
+ final boolean isVoicemailNumber =
+ mCallLogCache.isVoicemailNumber(accountHandle, details.number);
+
+ // Note: Binding of the action buttons is done as required in configureActionViews when the
+ // user expands the actions ViewStub.
+
+ ContactInfo info = ContactInfo.EMPTY;
+ if (PhoneNumberHelper.canPlaceCallsTo(details.number, details.numberPresentation)
+ && !isVoicemailNumber) {
+ // Lookup contacts with this number
+ // Only do remote lookup in first 5 rows.
+ info =
+ mContactInfoCache.getValue(
+ details.number + details.postDialDigits,
+ details.countryIso,
+ details.cachedContactInfo,
+ rowId
+ < Bindings.get(mActivity)
+ .getConfigProvider()
+ .getLong("number_of_call_to_do_remote_lookup", 5L));
+ }
+ CharSequence formattedNumber =
+ info.formattedNumber == null
+ ? null
+ : PhoneNumberUtilsCompat.createTtsSpannable(info.formattedNumber);
+ details.updateDisplayNumber(mActivity, formattedNumber, isVoicemailNumber);
+
+ views.displayNumber = details.displayNumber;
+ views.accountHandle = accountHandle;
+ details.accountHandle = accountHandle;
+
+ if (!TextUtils.isEmpty(info.name) || !TextUtils.isEmpty(info.nameAlternative)) {
+ details.contactUri = info.lookupUri;
+ details.namePrimary = info.name;
+ details.nameAlternative = info.nameAlternative;
+ details.nameDisplayOrder = mContactsPreferences.getDisplayOrder();
+ details.numberType = info.type;
+ details.numberLabel = info.label;
+ details.photoUri = info.photoUri;
+ details.sourceType = info.sourceType;
+ details.objectId = info.objectId;
+ details.contactUserType = info.userType;
+ }
+
+ views.info = info;
+ views.numberType =
+ (String)
+ Phone.getTypeLabel(mActivity.getResources(), details.numberType, details.numberLabel);
+
+ mCallLogListItemHelper.updatePhoneCallDetails(details);
+ return true;
+ }
+
+ /**
+ * Render item view given position. This is running on UI thread so DO NOT put any expensive
+ * operation into it.
+ */
+ @MainThread
+ private void render(CallLogListItemViewHolder views, PhoneCallDetails details, long rowId) {
+ Assert.isMainThread();
+ if (rowId != views.rowId) {
+ LogUtil.i(
+ "CallLogAdapter.render",
+ "rowId of viewHolder changed after load task is issued, aborting render");
+ return;
+ }
+
+ // Default case: an item in the call log.
+ views.primaryActionView.setVisibility(View.VISIBLE);
+ views.workIconView.setVisibility(
+ details.contactUserType == ContactsUtils.USER_TYPE_WORK ? View.VISIBLE : View.GONE);
+
+ mCallLogListItemHelper.setPhoneCallDetails(views, details);
+ if (mCurrentlyExpandedRowId == views.rowId) {
+ // In case ViewHolders were added/removed, update the expanded position if the rowIds
+ // match so that we can restore the correct expanded state on rebind.
+ mCurrentlyExpandedPosition = views.getAdapterPosition();
+ views.showActions(true);
+ } else {
+ views.showActions(false);
+ }
+ views.dayGroupHeader.setVisibility(views.dayGroupHeaderVisibility);
+ views.dayGroupHeader.setText(views.dayGroupHeaderText);
+ }
+
+ @Override
+ public int getItemCount() {
+ return super.getItemCount() + (mCallLogAlertManager.isEmpty() ? 0 : 1);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (position == ALERT_POSITION && !mCallLogAlertManager.isEmpty()) {
+ return VIEW_TYPE_ALERT;
+ }
+ return VIEW_TYPE_CALLLOG;
+ }
+
+ /**
+ * Retrieves an item at the specified position, taking into account the presence of a promo card.
+ *
+ * @param position The position to retrieve.
+ * @return The item at that position.
+ */
+ @Override
+ public Object getItem(int position) {
+ return super.getItem(position - (mCallLogAlertManager.isEmpty() ? 0 : 1));
+ }
+
+ @Override
+ public long getItemId(int position) {
+ Cursor cursor = (Cursor) getItem(position);
+ if (cursor != null) {
+ return cursor.getLong(CallLogQuery.ID);
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public int getGroupSize(int position) {
+ return super.getGroupSize(position - (mCallLogAlertManager.isEmpty() ? 0 : 1));
+ }
+
+ protected boolean isCallLogActivity() {
+ return mActivityType == ACTIVITY_TYPE_CALL_LOG;
+ }
+
+ /**
+ * In order to implement the "undo" function, when a voicemail is "deleted" i.e. when the user
+ * clicks the delete button, the deleted item is temporarily hidden from the list. If a user
+ * clicks delete on a second item before the first item's undo option has expired, the first item
+ * is immediately deleted so that only one item can be "undoed" at a time.
+ */
+ @Override
+ public void onVoicemailDeleted(CallLogListItemViewHolder viewHolder, Uri uri) {
+ mHiddenRowIds.add(viewHolder.rowId);
+ // Save the new hidden item uri in case the activity is suspend before the undo has timed out.
+ mHiddenItemUris.add(uri);
+
+ collapseExpandedCard();
+ notifyItemChanged(viewHolder.getAdapterPosition());
+ // The next item might have to update its day group label
+ notifyItemChanged(viewHolder.getAdapterPosition() + 1);
+ }
+
+ private void collapseExpandedCard() {
+ mCurrentlyExpandedRowId = NO_EXPANDED_LIST_ITEM;
+ mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
+ }
+
+ /** When the list is changing all stored position is no longer valid. */
+ public void invalidatePositions() {
+ mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
+ }
+
+ /** When the user clicks "undo", the hidden item is unhidden. */
+ @Override
+ public void onVoicemailDeleteUndo(long rowId, int adapterPosition, Uri uri) {
+ mHiddenItemUris.remove(uri);
+ mHiddenRowIds.remove(rowId);
+ notifyItemChanged(adapterPosition);
+ // The next item might have to update its day group label
+ notifyItemChanged(adapterPosition + 1);
+ }
+
+ /** This callback signifies that a database deletion has completed. */
+ @Override
+ public void onVoicemailDeletedInDatabase(long rowId, Uri uri) {
+ mHiddenItemUris.remove(uri);
+ }
+
+ /**
+ * Retrieves the day group of the previous call in the call log. Used to determine if the day
+ * group has changed and to trigger display of the day group text.
+ *
+ * @param cursor The call log cursor.
+ * @return The previous day group, or DAY_GROUP_NONE if this is the first call.
+ */
+ private int getPreviousDayGroup(Cursor cursor) {
+ // We want to restore the position in the cursor at the end.
+ int startingPosition = cursor.getPosition();
+ moveToPreviousNonHiddenRow(cursor);
+ if (cursor.isBeforeFirst()) {
+ cursor.moveToPosition(startingPosition);
+ return CallLogGroupBuilder.DAY_GROUP_NONE;
+ }
+ int result = getDayGroupForCall(cursor.getLong(CallLogQuery.ID));
+ cursor.moveToPosition(startingPosition);
+ return result;
+ }
+
+ private void moveToPreviousNonHiddenRow(Cursor cursor) {
+ while (cursor.moveToPrevious() && mHiddenRowIds.contains(cursor.getLong(CallLogQuery.ID))) {}
+ }
+
+ /**
+ * Given a call Id, look up the day group that the call belongs to. The day group data is
+ * populated in {@link com.android.dialer.app.calllog.CallLogGroupBuilder}.
+ *
+ * @param callId The call to retrieve the day group for.
+ * @return The day group for the call.
+ */
+ @MainThread
+ private int getDayGroupForCall(long callId) {
+ Integer result = mDayGroups.get(callId);
+ if (result != null) {
+ return result;
+ }
+ return CallLogGroupBuilder.DAY_GROUP_NONE;
+ }
+
+ /**
+ * Returns the call types for the given number of items in the cursor.
+ *
+ * <p>It uses the next {@code count} rows in the cursor to extract the types.
+ *
+ * <p>It position in the cursor is unchanged by this function.
+ */
+ private static int[] getCallTypes(Cursor cursor, int count) {
+ int position = cursor.getPosition();
+ int[] callTypes = new int[count];
+ for (int index = 0; index < count; ++index) {
+ callTypes[index] = cursor.getInt(CallLogQuery.CALL_TYPE);
+ cursor.moveToNext();
+ }
+ cursor.moveToPosition(position);
+ return callTypes;
+ }
+
+ /**
+ * Determine the features which were enabled for any of the calls that make up a call log entry.
+ *
+ * @param cursor The cursor.
+ * @param count The number of calls for the current call log entry.
+ * @return The features.
+ */
+ private int getCallFeatures(Cursor cursor, int count) {
+ int features = 0;
+ int position = cursor.getPosition();
+ for (int index = 0; index < count; ++index) {
+ features |= cursor.getInt(CallLogQuery.FEATURES);
+ cursor.moveToNext();
+ }
+ cursor.moveToPosition(position);
+ return features;
+ }
+
+ /**
+ * Sets whether processing of requests for contact details should be enabled.
+ *
+ * <p>This method should be called in tests to disable such processing of requests when not
+ * needed.
+ */
+ @VisibleForTesting
+ void disableRequestProcessingForTest() {
+ // TODO: Remove this and test the cache directly.
+ mContactInfoCache.disableRequestProcessing();
+ }
+
+ @VisibleForTesting
+ void injectContactInfoForTest(String number, String countryIso, ContactInfo contactInfo) {
+ // TODO: Remove this and test the cache directly.
+ mContactInfoCache.injectContactInfoForTest(number, countryIso, contactInfo);
+ }
+
+ /**
+ * Stores the day group associated with a call in the call log.
+ *
+ * @param rowId The row Id of the current call.
+ * @param dayGroup The day group the call belongs in.
+ */
+ @Override
+ @MainThread
+ public void setDayGroup(long rowId, int dayGroup) {
+ if (!mDayGroups.containsKey(rowId)) {
+ mDayGroups.put(rowId, dayGroup);
+ }
+ }
+
+ /** Clears the day group associations on re-bind of the call log. */
+ @Override
+ @MainThread
+ public void clearDayGroups() {
+ mDayGroups.clear();
+ }
+
+ /**
+ * Retrieves the call Ids represented by the current call log row.
+ *
+ * @param cursor Call log cursor to retrieve call Ids from.
+ * @param groupSize Number of calls associated with the current call log row.
+ * @return Array of call Ids.
+ */
+ private long[] getCallIds(final Cursor cursor, final int groupSize) {
+ // We want to restore the position in the cursor at the end.
+ int startingPosition = cursor.getPosition();
+ long[] ids = new long[groupSize];
+ // Copy the ids of the rows in the group.
+ for (int index = 0; index < groupSize; ++index) {
+ ids[index] = cursor.getLong(CallLogQuery.ID);
+ cursor.moveToNext();
+ }
+ cursor.moveToPosition(startingPosition);
+ return ids;
+ }
+
+ /**
+ * Determines the description for a day group.
+ *
+ * @param group The day group to retrieve the description for.
+ * @return The day group description.
+ */
+ private CharSequence getGroupDescription(int group) {
+ if (group == CallLogGroupBuilder.DAY_GROUP_TODAY) {
+ return mActivity.getResources().getString(R.string.call_log_header_today);
+ } else if (group == CallLogGroupBuilder.DAY_GROUP_YESTERDAY) {
+ return mActivity.getResources().getString(R.string.call_log_header_yesterday);
+ } else {
+ return mActivity.getResources().getString(R.string.call_log_header_other);
+ }
+ }
+
+ @Override
+ public void onCapabilitiesUpdated() {
+ notifyDataSetChanged();
+ }
+
+ /** Interface used to initiate a refresh of the content. */
+ public interface CallFetcher {
+
+ void fetchCalls();
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallLogAlertManager.java b/java/com/android/dialer/app/calllog/CallLogAlertManager.java
new file mode 100644
index 000000000..40b30f001
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallLogAlertManager.java
@@ -0,0 +1,90 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.dialer.app.R;
+import com.android.dialer.app.alert.AlertManager;
+import com.android.dialer.common.Assert;
+
+/** Manages "alerts" to be shown at the top of an call log to gain the user's attention. */
+public class CallLogAlertManager implements AlertManager {
+
+ private final CallLogAdapter adapter;
+ private final View view;
+ private final LayoutInflater inflater;
+ private final ViewGroup parent;
+ private final ViewGroup container;
+
+ public CallLogAlertManager(CallLogAdapter adapter, LayoutInflater inflater, ViewGroup parent) {
+ this.adapter = adapter;
+ this.inflater = inflater;
+ this.parent = parent;
+ view = inflater.inflate(R.layout.call_log_alert_item, parent, false);
+ container = (ViewGroup) view.findViewById(R.id.container);
+ }
+
+ @Override
+ public View inflate(int layoutId) {
+ return inflater.inflate(layoutId, container, false);
+ }
+
+ public RecyclerView.ViewHolder createViewHolder(ViewGroup parent) {
+ Assert.checkArgument(
+ parent == this.parent,
+ "createViewHolder should be called with the same parent in constructor");
+ return new AlertViewHolder(view);
+ }
+
+ public boolean isEmpty() {
+ return container.getChildCount() == 0;
+ }
+
+ public boolean contains(View view) {
+ return container.indexOfChild(view) != -1;
+ }
+
+ @Override
+ public void clear() {
+ container.removeAllViews();
+ adapter.notifyItemRemoved(CallLogAdapter.ALERT_POSITION);
+ }
+
+ @Override
+ public void add(View view) {
+ if (contains(view)) {
+ return;
+ }
+ container.addView(view);
+ if (container.getChildCount() == 1) {
+ // Was empty before
+ adapter.notifyItemInserted(CallLogAdapter.ALERT_POSITION);
+ }
+ }
+
+ /**
+ * Does nothing. The view this ViewHolder show is directly managed by {@link CallLogAlertManager}
+ */
+ private static class AlertViewHolder extends RecyclerView.ViewHolder {
+ private AlertViewHolder(View view) {
+ super(view);
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallLogAsync.java b/java/com/android/dialer/app/calllog/CallLogAsync.java
new file mode 100644
index 000000000..f62deca89
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallLogAsync.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2010 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.dialer.app.calllog;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.provider.CallLog.Calls;
+import com.android.dialer.common.Assert;
+
+/**
+ * Class to access the call log asynchronously to avoid carrying out database operations on the UI
+ * thread, using an {@link AsyncTask}.
+ *
+ * <pre class="prettyprint"> Typical usage: ==============
+ *
+ * // From an activity... String mLastNumber = "";
+ *
+ * CallLogAsync log = new CallLogAsync();
+ *
+ * CallLogAsync.GetLastOutgoingCallArgs lastCallArgs = new CallLogAsync.GetLastOutgoingCallArgs(
+ * this, new CallLogAsync.OnLastOutgoingCallComplete() { public void lastOutgoingCall(String number)
+ * { mLastNumber = number; } }); log.getLastOutgoingCall(lastCallArgs); </pre>
+ */
+public class CallLogAsync {
+
+ /** CallLog.getLastOutgoingCall(...) */
+ public AsyncTask getLastOutgoingCall(GetLastOutgoingCallArgs args) {
+ Assert.isMainThread();
+ return new GetLastOutgoingCallTask(args.callback).execute(args);
+ }
+
+ /** Interface to retrieve the last dialed number asynchronously. */
+ public interface OnLastOutgoingCallComplete {
+
+ /** @param number The last dialed number or an empty string if none exists yet. */
+ void lastOutgoingCall(String number);
+ }
+
+ /** Parameter object to hold the args to get the last outgoing call from the call log DB. */
+ public static class GetLastOutgoingCallArgs {
+
+ public final Context context;
+ public final OnLastOutgoingCallComplete callback;
+
+ public GetLastOutgoingCallArgs(Context context, OnLastOutgoingCallComplete callback) {
+ this.context = context;
+ this.callback = callback;
+ }
+ }
+
+ /** AsyncTask to get the last outgoing call from the DB. */
+ private class GetLastOutgoingCallTask extends AsyncTask<GetLastOutgoingCallArgs, Void, String> {
+
+ private final OnLastOutgoingCallComplete mCallback;
+
+ public GetLastOutgoingCallTask(OnLastOutgoingCallComplete callback) {
+ mCallback = callback;
+ }
+
+ // Happens on a background thread. We cannot run the callback
+ // here because only the UI thread can modify the view
+ // hierarchy (e.g enable/disable the dial button). The
+ // callback is ran rom the post execute method.
+ @Override
+ protected String doInBackground(GetLastOutgoingCallArgs... list) {
+ String number = "";
+ for (GetLastOutgoingCallArgs args : list) {
+ // May block. Select only the last one.
+ number = Calls.getLastOutgoingCall(args.context);
+ }
+ return number; // passed to the onPostExecute method.
+ }
+
+ // Happens on the UI thread, it is safe to run the callback
+ // that may do some work on the views.
+ @Override
+ protected void onPostExecute(String number) {
+ Assert.isMainThread();
+ mCallback.lastOutgoingCall(number);
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallLogAsyncTaskUtil.java b/java/com/android/dialer/app/calllog/CallLogAsyncTaskUtil.java
new file mode 100644
index 000000000..b4e6fc5ad
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallLogAsyncTaskUtil.java
@@ -0,0 +1,376 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.Manifest.permission;
+import android.annotation.TargetApi;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.CallLog;
+import android.provider.VoicemailContract.Voicemails;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.content.ContextCompat;
+import android.telecom.PhoneAccountHandle;
+import android.text.TextUtils;
+import com.android.contacts.common.GeoUtil;
+import com.android.dialer.app.PhoneCallDetails;
+import com.android.dialer.common.AsyncTaskExecutor;
+import com.android.dialer.common.AsyncTaskExecutors;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.phonenumbercache.ContactInfo;
+import com.android.dialer.phonenumbercache.ContactInfoHelper;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.android.dialer.telecom.TelecomUtil;
+import com.android.dialer.util.PermissionsUtil;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+@TargetApi(VERSION_CODES.M)
+public class CallLogAsyncTaskUtil {
+
+ private static final String TAG = "CallLogAsyncTaskUtil";
+ private static AsyncTaskExecutor sAsyncTaskExecutor;
+
+ private static void initTaskExecutor() {
+ sAsyncTaskExecutor = AsyncTaskExecutors.createThreadPoolExecutor();
+ }
+
+ public static void getCallDetails(
+ @NonNull final Context context,
+ @Nullable final CallLogAsyncTaskListener callLogAsyncTaskListener,
+ @NonNull final Uri... callUris) {
+ if (sAsyncTaskExecutor == null) {
+ initTaskExecutor();
+ }
+
+ sAsyncTaskExecutor.submit(
+ Tasks.GET_CALL_DETAILS,
+ new AsyncTask<Void, Void, PhoneCallDetails[]>() {
+ @Override
+ public PhoneCallDetails[] doInBackground(Void... params) {
+ if (ContextCompat.checkSelfPermission(context, permission.READ_CALL_LOG)
+ != PackageManager.PERMISSION_GRANTED) {
+ LogUtil.w("CallLogAsyncTaskUtil.getCallDetails", "missing READ_CALL_LOG permission");
+ return null;
+ }
+ // TODO: All calls correspond to the same person, so make a single lookup.
+ final int numCalls = callUris.length;
+ PhoneCallDetails[] details = new PhoneCallDetails[numCalls];
+ try {
+ for (int index = 0; index < numCalls; ++index) {
+ details[index] = getPhoneCallDetailsForUri(context, callUris[index]);
+ }
+ return details;
+ } catch (IllegalArgumentException e) {
+ // Something went wrong reading in our primary data.
+ LogUtil.e(
+ "CallLogAsyncTaskUtil.getCallDetails", "invalid URI starting call details", e);
+ return null;
+ }
+ }
+
+ @Override
+ public void onPostExecute(PhoneCallDetails[] phoneCallDetails) {
+ if (callLogAsyncTaskListener != null) {
+ callLogAsyncTaskListener.onGetCallDetails(phoneCallDetails);
+ }
+ }
+ });
+ }
+
+ /** Return the phone call details for a given call log URI. */
+ private static PhoneCallDetails getPhoneCallDetailsForUri(
+ @NonNull Context context, @NonNull Uri callUri) {
+ Cursor cursor =
+ context
+ .getContentResolver()
+ .query(callUri, CallDetailQuery.CALL_LOG_PROJECTION, null, null, null);
+
+ try {
+ if (cursor == null || !cursor.moveToFirst()) {
+ throw new IllegalArgumentException("Cannot find content: " + callUri);
+ }
+
+ // Read call log.
+ final String countryIso = cursor.getString(CallDetailQuery.COUNTRY_ISO_COLUMN_INDEX);
+ final String number = cursor.getString(CallDetailQuery.NUMBER_COLUMN_INDEX);
+ final String postDialDigits =
+ (VERSION.SDK_INT >= VERSION_CODES.N)
+ ? cursor.getString(CallDetailQuery.POST_DIAL_DIGITS)
+ : "";
+ final String viaNumber =
+ (VERSION.SDK_INT >= VERSION_CODES.N) ? cursor.getString(CallDetailQuery.VIA_NUMBER) : "";
+ final int numberPresentation =
+ cursor.getInt(CallDetailQuery.NUMBER_PRESENTATION_COLUMN_INDEX);
+
+ final PhoneAccountHandle accountHandle =
+ PhoneAccountUtils.getAccount(
+ cursor.getString(CallDetailQuery.ACCOUNT_COMPONENT_NAME),
+ cursor.getString(CallDetailQuery.ACCOUNT_ID));
+
+ // If this is not a regular number, there is no point in looking it up in the contacts.
+ ContactInfoHelper contactInfoHelper =
+ new ContactInfoHelper(context, GeoUtil.getCurrentCountryIso(context));
+ boolean isVoicemail = PhoneNumberHelper.isVoicemailNumber(context, accountHandle, number);
+ boolean shouldLookupNumber =
+ PhoneNumberHelper.canPlaceCallsTo(number, numberPresentation) && !isVoicemail;
+ ContactInfo info = ContactInfo.EMPTY;
+
+ if (shouldLookupNumber) {
+ ContactInfo lookupInfo = contactInfoHelper.lookupNumber(number, countryIso);
+ info = lookupInfo != null ? lookupInfo : ContactInfo.EMPTY;
+ }
+
+ PhoneCallDetails details = new PhoneCallDetails(number, numberPresentation, postDialDigits);
+ details.updateDisplayNumber(context, info.formattedNumber, isVoicemail);
+
+ details.viaNumber = viaNumber;
+ details.accountHandle = accountHandle;
+ details.contactUri = info.lookupUri;
+ details.namePrimary = info.name;
+ details.nameAlternative = info.nameAlternative;
+ details.numberType = info.type;
+ details.numberLabel = info.label;
+ details.photoUri = info.photoUri;
+ details.sourceType = info.sourceType;
+ details.objectId = info.objectId;
+
+ details.callTypes = new int[] {cursor.getInt(CallDetailQuery.CALL_TYPE_COLUMN_INDEX)};
+ details.date = cursor.getLong(CallDetailQuery.DATE_COLUMN_INDEX);
+ details.duration = cursor.getLong(CallDetailQuery.DURATION_COLUMN_INDEX);
+ details.features = cursor.getInt(CallDetailQuery.FEATURES);
+ details.geocode = cursor.getString(CallDetailQuery.GEOCODED_LOCATION_COLUMN_INDEX);
+ details.transcription = cursor.getString(CallDetailQuery.TRANSCRIPTION_COLUMN_INDEX);
+
+ details.countryIso =
+ !TextUtils.isEmpty(countryIso) ? countryIso : GeoUtil.getCurrentCountryIso(context);
+
+ if (!cursor.isNull(CallDetailQuery.DATA_USAGE)) {
+ details.dataUsage = cursor.getLong(CallDetailQuery.DATA_USAGE);
+ }
+
+ return details;
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ /**
+ * Delete specified calls from the call log.
+ *
+ * @param context The context.
+ * @param callIds String of the callIds to delete from the call log, delimited by commas (",").
+ * @param callLogAsyncTaskListener The listener to invoke after the entries have been deleted.
+ */
+ public static void deleteCalls(
+ @NonNull final Context context,
+ final String callIds,
+ @Nullable final CallLogAsyncTaskListener callLogAsyncTaskListener) {
+ if (sAsyncTaskExecutor == null) {
+ initTaskExecutor();
+ }
+
+ sAsyncTaskExecutor.submit(
+ Tasks.DELETE_CALL,
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ public Void doInBackground(Void... params) {
+ context
+ .getContentResolver()
+ .delete(
+ TelecomUtil.getCallLogUri(context),
+ CallLog.Calls._ID + " IN (" + callIds + ")",
+ null);
+ return null;
+ }
+
+ @Override
+ public void onPostExecute(Void result) {
+ if (callLogAsyncTaskListener != null) {
+ callLogAsyncTaskListener.onDeleteCall();
+ }
+ }
+ });
+ }
+
+ public static void markVoicemailAsRead(
+ @NonNull final Context context, @NonNull final Uri voicemailUri) {
+ if (sAsyncTaskExecutor == null) {
+ initTaskExecutor();
+ }
+
+ sAsyncTaskExecutor.submit(
+ Tasks.MARK_VOICEMAIL_READ,
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ public Void doInBackground(Void... params) {
+ ContentValues values = new ContentValues();
+ values.put(Voicemails.IS_READ, true);
+ context
+ .getContentResolver()
+ .update(voicemailUri, values, Voicemails.IS_READ + " = 0", null);
+
+ Intent intent = new Intent(context, CallLogNotificationsService.class);
+ intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_VOICEMAILS_AS_OLD);
+ context.startService(intent);
+ return null;
+ }
+ });
+ }
+
+ public static void deleteVoicemail(
+ @NonNull final Context context,
+ final Uri voicemailUri,
+ @Nullable final CallLogAsyncTaskListener callLogAsyncTaskListener) {
+ if (sAsyncTaskExecutor == null) {
+ initTaskExecutor();
+ }
+
+ sAsyncTaskExecutor.submit(
+ Tasks.DELETE_VOICEMAIL,
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ public Void doInBackground(Void... params) {
+ context.getContentResolver().delete(voicemailUri, null, null);
+ return null;
+ }
+
+ @Override
+ public void onPostExecute(Void result) {
+ if (callLogAsyncTaskListener != null) {
+ callLogAsyncTaskListener.onDeleteVoicemail();
+ }
+ }
+ });
+ }
+
+ public static void markCallAsRead(@NonNull final Context context, @NonNull final long[] callIds) {
+ if (!PermissionsUtil.hasPhonePermissions(context)) {
+ return;
+ }
+ if (sAsyncTaskExecutor == null) {
+ initTaskExecutor();
+ }
+
+ sAsyncTaskExecutor.submit(
+ Tasks.MARK_CALL_READ,
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ public Void doInBackground(Void... params) {
+
+ StringBuilder where = new StringBuilder();
+ where.append(CallLog.Calls.TYPE).append(" = ").append(CallLog.Calls.MISSED_TYPE);
+ where.append(" AND ");
+
+ Long[] callIdLongs = new Long[callIds.length];
+ for (int i = 0; i < callIds.length; i++) {
+ callIdLongs[i] = callIds[i];
+ }
+ where
+ .append(CallLog.Calls._ID)
+ .append(" IN (" + TextUtils.join(",", callIdLongs) + ")");
+
+ ContentValues values = new ContentValues(1);
+ values.put(CallLog.Calls.IS_READ, "1");
+ context
+ .getContentResolver()
+ .update(CallLog.Calls.CONTENT_URI, values, where.toString(), null);
+ return null;
+ }
+ });
+ }
+
+ @VisibleForTesting
+ public static void resetForTest() {
+ sAsyncTaskExecutor = null;
+ }
+
+ /** The enumeration of {@link AsyncTask} objects used in this class. */
+ public enum Tasks {
+ DELETE_VOICEMAIL,
+ DELETE_CALL,
+ MARK_VOICEMAIL_READ,
+ MARK_CALL_READ,
+ GET_CALL_DETAILS,
+ UPDATE_DURATION,
+ }
+
+ public interface CallLogAsyncTaskListener {
+
+ void onDeleteCall();
+
+ void onDeleteVoicemail();
+
+ void onGetCallDetails(PhoneCallDetails[] details);
+ }
+
+ private static final class CallDetailQuery {
+
+ public static final String[] CALL_LOG_PROJECTION;
+ static final int DATE_COLUMN_INDEX = 0;
+ static final int DURATION_COLUMN_INDEX = 1;
+ static final int NUMBER_COLUMN_INDEX = 2;
+ static final int CALL_TYPE_COLUMN_INDEX = 3;
+ static final int COUNTRY_ISO_COLUMN_INDEX = 4;
+ static final int GEOCODED_LOCATION_COLUMN_INDEX = 5;
+ static final int NUMBER_PRESENTATION_COLUMN_INDEX = 6;
+ static final int ACCOUNT_COMPONENT_NAME = 7;
+ static final int ACCOUNT_ID = 8;
+ static final int FEATURES = 9;
+ static final int DATA_USAGE = 10;
+ static final int TRANSCRIPTION_COLUMN_INDEX = 11;
+ static final int POST_DIAL_DIGITS = 12;
+ static final int VIA_NUMBER = 13;
+ private static final String[] CALL_LOG_PROJECTION_INTERNAL =
+ new String[] {
+ CallLog.Calls.DATE,
+ CallLog.Calls.DURATION,
+ CallLog.Calls.NUMBER,
+ CallLog.Calls.TYPE,
+ CallLog.Calls.COUNTRY_ISO,
+ CallLog.Calls.GEOCODED_LOCATION,
+ CallLog.Calls.NUMBER_PRESENTATION,
+ CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME,
+ CallLog.Calls.PHONE_ACCOUNT_ID,
+ CallLog.Calls.FEATURES,
+ CallLog.Calls.DATA_USAGE,
+ CallLog.Calls.TRANSCRIPTION
+ };
+
+ static {
+ ArrayList<String> projectionList = new ArrayList<>();
+ projectionList.addAll(Arrays.asList(CALL_LOG_PROJECTION_INTERNAL));
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ projectionList.add(CallLog.Calls.POST_DIAL_DIGITS);
+ projectionList.add(CallLog.Calls.VIA_NUMBER);
+ }
+ projectionList.trimToSize();
+ CALL_LOG_PROJECTION = projectionList.toArray(new String[projectionList.size()]);
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallLogFragment.java b/java/com/android/dialer/app/calllog/CallLogFragment.java
new file mode 100644
index 000000000..1ae68cd65
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallLogFragment.java
@@ -0,0 +1,528 @@
+/*
+ * 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.dialer.app.calllog;
+
+import static android.Manifest.permission.READ_CALL_LOG;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.KeyguardManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.provider.ContactsContract;
+import android.support.annotation.CallSuper;
+import android.support.annotation.Nullable;
+import android.support.v13.app.FragmentCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.contacts.common.GeoUtil;
+import com.android.dialer.app.Bindings;
+import com.android.dialer.app.R;
+import com.android.dialer.app.calllog.calllogcache.CallLogCache;
+import com.android.dialer.app.contactinfo.ContactInfoCache;
+import com.android.dialer.app.contactinfo.ContactInfoCache.OnContactInfoChangedListener;
+import com.android.dialer.app.contactinfo.ExpirableCacheHeadlessFragment;
+import com.android.dialer.app.list.ListsFragment;
+import com.android.dialer.app.list.ListsFragment.ListsPage;
+import com.android.dialer.app.voicemail.VoicemailPlaybackPresenter;
+import com.android.dialer.app.widget.EmptyContentView;
+import com.android.dialer.app.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.database.CallLogQueryHandler;
+import com.android.dialer.phonenumbercache.ContactInfoHelper;
+import com.android.dialer.util.PermissionsUtil;
+
+/**
+ * Displays a list of call log entries. To filter for a particular kind of call (all, missed or
+ * voicemails), specify it in the constructor.
+ */
+public class CallLogFragment extends Fragment
+ implements ListsPage,
+ CallLogQueryHandler.Listener,
+ CallLogAdapter.CallFetcher,
+ OnEmptyViewActionButtonClickedListener,
+ FragmentCompat.OnRequestPermissionsResultCallback,
+ CallLogModalAlertManager.Listener {
+ private static final String KEY_FILTER_TYPE = "filter_type";
+ private static final String KEY_HAS_READ_CALL_LOG_PERMISSION = "has_read_call_log_permission";
+ private static final String KEY_REFRESH_DATA_REQUIRED = "refresh_data_required";
+
+ private static final int READ_CALL_LOG_PERMISSION_REQUEST_CODE = 1;
+
+ private static final int EVENT_UPDATE_DISPLAY = 1;
+
+ private static final long MILLIS_IN_MINUTE = 60 * 1000;
+ private final Handler mHandler = new Handler();
+ // See issue 6363009
+ private final ContentObserver mCallLogObserver = new CustomContentObserver();
+ private final ContentObserver mContactsObserver = new CustomContentObserver();
+ private RecyclerView mRecyclerView;
+ private LinearLayoutManager mLayoutManager;
+ private CallLogAdapter mAdapter;
+ private CallLogQueryHandler mCallLogQueryHandler;
+ private boolean mScrollToTop;
+ private EmptyContentView mEmptyListView;
+ private KeyguardManager mKeyguardManager;
+ private ContactInfoCache mContactInfoCache;
+ private final OnContactInfoChangedListener mOnContactInfoChangedListener =
+ new OnContactInfoChangedListener() {
+ @Override
+ public void onContactInfoChanged() {
+ if (mAdapter != null) {
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+ };
+ private boolean mRefreshDataRequired;
+ private boolean mHasReadCallLogPermission;
+ // Exactly same variable is in Fragment as a package private.
+ private boolean mMenuVisible = true;
+ // Default to all calls.
+ protected int mCallTypeFilter = CallLogQueryHandler.CALL_TYPE_ALL;
+
+ private final Handler mDisplayUpdateHandler =
+ new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_UPDATE_DISPLAY:
+ refreshData();
+ rescheduleDisplayUpdate();
+ break;
+ }
+ }
+ };
+ protected CallLogModalAlertManager mModalAlertManager;
+ private ViewGroup mModalAlertView;
+
+ @Override
+ public void onCreate(Bundle state) {
+ LogUtil.d("CallLogFragment.onCreate", toString());
+ super.onCreate(state);
+ mRefreshDataRequired = true;
+ if (state != null) {
+ mCallTypeFilter = state.getInt(KEY_FILTER_TYPE, mCallTypeFilter);
+ mHasReadCallLogPermission = state.getBoolean(KEY_HAS_READ_CALL_LOG_PERMISSION, false);
+ mRefreshDataRequired = state.getBoolean(KEY_REFRESH_DATA_REQUIRED, mRefreshDataRequired);
+ }
+
+ final Activity activity = getActivity();
+ final ContentResolver resolver = activity.getContentResolver();
+ mCallLogQueryHandler = new CallLogQueryHandler(activity, resolver, this);
+ mKeyguardManager = (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
+ resolver.registerContentObserver(CallLog.CONTENT_URI, true, mCallLogObserver);
+ resolver.registerContentObserver(
+ ContactsContract.Contacts.CONTENT_URI, true, mContactsObserver);
+ setHasOptionsMenu(true);
+ }
+
+ /** Called by the CallLogQueryHandler when the list of calls has been fetched or updated. */
+ @Override
+ public boolean onCallsFetched(Cursor cursor) {
+ if (getActivity() == null || getActivity().isFinishing()) {
+ // Return false; we did not take ownership of the cursor
+ return false;
+ }
+ mAdapter.invalidatePositions();
+ mAdapter.setLoading(false);
+ mAdapter.changeCursor(cursor);
+ // This will update the state of the "Clear call log" menu item.
+ getActivity().invalidateOptionsMenu();
+
+ if (cursor != null && cursor.getCount() > 0) {
+ mRecyclerView.setPaddingRelative(
+ mRecyclerView.getPaddingStart(),
+ 0,
+ mRecyclerView.getPaddingEnd(),
+ getResources().getDimensionPixelSize(R.dimen.floating_action_button_list_bottom_padding));
+ mEmptyListView.setVisibility(View.GONE);
+ } else {
+ mRecyclerView.setPaddingRelative(
+ mRecyclerView.getPaddingStart(), 0, mRecyclerView.getPaddingEnd(), 0);
+ mEmptyListView.setVisibility(View.VISIBLE);
+ }
+ if (mScrollToTop) {
+ // The smooth-scroll animation happens over a fixed time period.
+ // As a result, if it scrolls through a large portion of the list,
+ // each frame will jump so far from the previous one that the user
+ // will not experience the illusion of downward motion. Instead,
+ // if we're not already near the top of the list, we instantly jump
+ // near the top, and animate from there.
+ if (mLayoutManager.findFirstVisibleItemPosition() > 5) {
+ // TODO: Jump to near the top, then begin smooth scroll.
+ mRecyclerView.smoothScrollToPosition(0);
+ }
+ // Workaround for framework issue: the smooth-scroll doesn't
+ // occur if setSelection() is called immediately before.
+ mHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (getActivity() == null || getActivity().isFinishing()) {
+ return;
+ }
+ mRecyclerView.smoothScrollToPosition(0);
+ }
+ });
+
+ mScrollToTop = false;
+ }
+ return true;
+ }
+
+ @Override
+ public void onVoicemailStatusFetched(Cursor statusCursor) {}
+
+ @Override
+ public void onVoicemailUnreadCountFetched(Cursor cursor) {}
+
+ @Override
+ public void onMissedCallsUnreadCountFetched(Cursor cursor) {}
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+ View view = inflater.inflate(R.layout.call_log_fragment, container, false);
+ setupView(view);
+ return view;
+ }
+
+ protected void setupView(View view) {
+ mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
+ mRecyclerView.setHasFixedSize(true);
+ mLayoutManager = new LinearLayoutManager(getActivity());
+ mRecyclerView.setLayoutManager(mLayoutManager);
+ mEmptyListView = (EmptyContentView) view.findViewById(R.id.empty_list_view);
+ mEmptyListView.setImage(R.drawable.empty_call_log);
+ mEmptyListView.setActionClickedListener(this);
+ mModalAlertView = (ViewGroup) view.findViewById(R.id.modal_message_container);
+ mModalAlertManager =
+ new CallLogModalAlertManager(LayoutInflater.from(getContext()), mModalAlertView, this);
+ }
+
+ protected void setupData() {
+ int activityType = CallLogAdapter.ACTIVITY_TYPE_DIALTACTS;
+ String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
+
+ mContactInfoCache =
+ new ContactInfoCache(
+ ExpirableCacheHeadlessFragment.attach((AppCompatActivity) getActivity())
+ .getRetainedCache(),
+ new ContactInfoHelper(getActivity(), currentCountryIso),
+ mOnContactInfoChangedListener);
+ mAdapter =
+ Bindings.getLegacy(getActivity())
+ .newCallLogAdapter(
+ getActivity(),
+ mRecyclerView,
+ this,
+ CallLogCache.getCallLogCache(getActivity()),
+ mContactInfoCache,
+ getVoicemailPlaybackPresenter(),
+ activityType);
+ mRecyclerView.setAdapter(mAdapter);
+ fetchCalls();
+ }
+
+ @Nullable
+ protected VoicemailPlaybackPresenter getVoicemailPlaybackPresenter() {
+ return null;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ setupData();
+ mAdapter.onRestoreInstanceState(savedInstanceState);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ updateEmptyMessage(mCallTypeFilter);
+ }
+
+ @Override
+ public void onResume() {
+ LogUtil.d("CallLogFragment.onResume", toString());
+ super.onResume();
+ final boolean hasReadCallLogPermission =
+ PermissionsUtil.hasPermission(getActivity(), READ_CALL_LOG);
+ if (!mHasReadCallLogPermission && hasReadCallLogPermission) {
+ // We didn't have the permission before, and now we do. Force a refresh of the call log.
+ // Note that this code path always happens on a fresh start, but mRefreshDataRequired
+ // is already true in that case anyway.
+ mRefreshDataRequired = true;
+ updateEmptyMessage(mCallTypeFilter);
+ }
+
+ mHasReadCallLogPermission = hasReadCallLogPermission;
+
+ /*
+ * Always clear the filtered numbers cache since users could have blocked/unblocked numbers
+ * from the settings page
+ */
+ mAdapter.clearFilteredNumbersCache();
+ refreshData();
+ mAdapter.onResume();
+
+ rescheduleDisplayUpdate();
+ }
+
+ @Override
+ public void onPause() {
+ LogUtil.d("CallLogFragment.onPause", toString());
+ cancelDisplayUpdate();
+ mAdapter.onPause();
+ super.onPause();
+ }
+
+ @Override
+ public void onStop() {
+ updateOnTransition();
+
+ super.onStop();
+ mAdapter.onStop();
+ }
+
+ @Override
+ public void onDestroy() {
+ LogUtil.d("CallLogFragment.onDestroy", toString());
+ mAdapter.changeCursor(null);
+
+ getActivity().getContentResolver().unregisterContentObserver(mCallLogObserver);
+ getActivity().getContentResolver().unregisterContentObserver(mContactsObserver);
+ super.onDestroy();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(KEY_FILTER_TYPE, mCallTypeFilter);
+ outState.putBoolean(KEY_HAS_READ_CALL_LOG_PERMISSION, mHasReadCallLogPermission);
+ outState.putBoolean(KEY_REFRESH_DATA_REQUIRED, mRefreshDataRequired);
+
+ mContactInfoCache.stop();
+
+ mAdapter.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void fetchCalls() {
+ mCallLogQueryHandler.fetchCalls(mCallTypeFilter);
+ ((ListsFragment) getParentFragment()).updateTabUnreadCounts();
+ }
+
+ private void updateEmptyMessage(int filterType) {
+ final Context context = getActivity();
+ if (context == null) {
+ return;
+ }
+
+ if (!PermissionsUtil.hasPermission(context, READ_CALL_LOG)) {
+ mEmptyListView.setDescription(R.string.permission_no_calllog);
+ mEmptyListView.setActionLabel(R.string.permission_single_turn_on);
+ return;
+ }
+
+ final int messageId;
+ switch (filterType) {
+ case Calls.MISSED_TYPE:
+ messageId = R.string.call_log_missed_empty;
+ break;
+ case Calls.VOICEMAIL_TYPE:
+ messageId = R.string.call_log_voicemail_empty;
+ break;
+ case CallLogQueryHandler.CALL_TYPE_ALL:
+ messageId = R.string.call_log_all_empty;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unexpected filter type in CallLogFragment: " + filterType);
+ }
+ mEmptyListView.setDescription(messageId);
+ if (filterType == CallLogQueryHandler.CALL_TYPE_ALL) {
+ mEmptyListView.setActionLabel(R.string.call_log_all_empty_action);
+ }
+ }
+
+ public CallLogAdapter getAdapter() {
+ return mAdapter;
+ }
+
+ @Override
+ public void setMenuVisibility(boolean menuVisible) {
+ super.setMenuVisibility(menuVisible);
+ if (mMenuVisible != menuVisible) {
+ mMenuVisible = menuVisible;
+ if (!menuVisible) {
+ updateOnTransition();
+ } else if (isResumed()) {
+ refreshData();
+ }
+ }
+ }
+
+ /** Requests updates to the data to be shown. */
+ private void refreshData() {
+ // Prevent unnecessary refresh.
+ if (mRefreshDataRequired) {
+ // Mark all entries in the contact info cache as out of date, so they will be looked up
+ // again once being shown.
+ mContactInfoCache.invalidate();
+ mAdapter.setLoading(true);
+
+ fetchCalls();
+ mCallLogQueryHandler.fetchVoicemailStatus();
+ mCallLogQueryHandler.fetchMissedCallsUnreadCount();
+ updateOnTransition();
+ mRefreshDataRequired = false;
+ } else {
+ // Refresh the display of the existing data to update the timestamp text descriptions.
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+
+ /**
+ * Updates the voicemail notification state.
+ *
+ * <p>TODO: Move to CallLogActivity
+ */
+ private void updateOnTransition() {
+ // We don't want to update any call data when keyguard is on because the user has likely not
+ // seen the new calls yet.
+ // This might be called before onCreate() and thus we need to check null explicitly.
+ if (mKeyguardManager != null
+ && !mKeyguardManager.inKeyguardRestrictedInputMode()
+ && mCallTypeFilter == Calls.VOICEMAIL_TYPE) {
+ CallLogNotificationsHelper.updateVoicemailNotifications(getActivity());
+ }
+ }
+
+ @Override
+ public void onEmptyViewActionButtonClicked() {
+ final Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ if (!PermissionsUtil.hasPermission(activity, READ_CALL_LOG)) {
+ FragmentCompat.requestPermissions(
+ this, new String[] {READ_CALL_LOG}, READ_CALL_LOG_PERMISSION_REQUEST_CODE);
+ } else {
+ ((HostInterface) activity).showDialpad();
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, String[] permissions, int[] grantResults) {
+ if (requestCode == READ_CALL_LOG_PERMISSION_REQUEST_CODE) {
+ if (grantResults.length >= 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
+ // Force a refresh of the data since we were missing the permission before this.
+ mRefreshDataRequired = true;
+ }
+ }
+ }
+
+ /** Schedules an update to the relative call times (X mins ago). */
+ private void rescheduleDisplayUpdate() {
+ if (!mDisplayUpdateHandler.hasMessages(EVENT_UPDATE_DISPLAY)) {
+ long time = System.currentTimeMillis();
+ // This value allows us to change the display relatively close to when the time changes
+ // from one minute to the next.
+ long millisUtilNextMinute = MILLIS_IN_MINUTE - (time % MILLIS_IN_MINUTE);
+ mDisplayUpdateHandler.sendEmptyMessageDelayed(EVENT_UPDATE_DISPLAY, millisUtilNextMinute);
+ }
+ }
+
+ /** Cancels any pending update requests to update the relative call times (X mins ago). */
+ private void cancelDisplayUpdate() {
+ mDisplayUpdateHandler.removeMessages(EVENT_UPDATE_DISPLAY);
+ }
+
+ @Override
+ @CallSuper
+ public void onPageResume(@Nullable Activity activity) {
+ LogUtil.d("CallLogFragment.onPageResume", "frag: %s", this);
+ if (activity != null) {
+ ((HostInterface) activity)
+ .enableFloatingButton(mModalAlertManager == null || mModalAlertManager.isEmpty());
+ }
+ }
+
+ @Override
+ @CallSuper
+ public void onPagePause(@Nullable Activity activity) {
+ LogUtil.d("CallLogFragment.onPagePause", "frag: %s", this);
+ }
+
+ @Override
+ public void onShowModalAlert(boolean show) {
+ LogUtil.d(
+ "CallLogFragment.onShowModalAlert",
+ "show: %b, fragment: %s, isVisible: %b",
+ show,
+ this,
+ getUserVisibleHint());
+ getAdapter().notifyDataSetChanged();
+ HostInterface hostInterface = (HostInterface) getActivity();
+ if (show) {
+ mRecyclerView.setVisibility(View.GONE);
+ mModalAlertView.setVisibility(View.VISIBLE);
+ if (hostInterface != null && getUserVisibleHint()) {
+ hostInterface.enableFloatingButton(false);
+ }
+ } else {
+ mRecyclerView.setVisibility(View.VISIBLE);
+ mModalAlertView.setVisibility(View.GONE);
+ if (hostInterface != null && getUserVisibleHint()) {
+ hostInterface.enableFloatingButton(true);
+ }
+ }
+ }
+
+ public interface HostInterface {
+
+ void showDialpad();
+
+ void enableFloatingButton(boolean enabled);
+ }
+
+ protected class CustomContentObserver extends ContentObserver {
+
+ public CustomContentObserver() {
+ super(mHandler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mRefreshDataRequired = true;
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallLogGroupBuilder.java b/java/com/android/dialer/app/calllog/CallLogGroupBuilder.java
new file mode 100644
index 000000000..45ff3783d
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallLogGroupBuilder.java
@@ -0,0 +1,274 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.database.Cursor;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.text.format.Time;
+import com.android.contacts.common.util.DateUtils;
+import com.android.dialer.compat.AppCompatConstants;
+import com.android.dialer.phonenumbercache.CallLogQuery;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import java.util.Objects;
+
+/**
+ * Groups together calls in the call log. The primary grouping attempts to group together calls to
+ * and from the same number into a single row on the call log. A secondary grouping assigns calls,
+ * grouped via the primary grouping, to "day groups". The day groups provide a means of identifying
+ * the calls which occurred "Today", "Yesterday", "Last week", or "Other".
+ *
+ * <p>This class is meant to be used in conjunction with {@link GroupingListAdapter}.
+ */
+public class CallLogGroupBuilder {
+
+ /**
+ * Day grouping for call log entries used to represent no associated day group. Used primarily
+ * when retrieving the previous day group, but there is no previous day group (i.e. we are at the
+ * start of the list).
+ */
+ public static final int DAY_GROUP_NONE = -1;
+ /** Day grouping for calls which occurred today. */
+ public static final int DAY_GROUP_TODAY = 0;
+ /** Day grouping for calls which occurred yesterday. */
+ public static final int DAY_GROUP_YESTERDAY = 1;
+ /** Day grouping for calls which occurred before last week. */
+ public static final int DAY_GROUP_OTHER = 2;
+ /** Instance of the time object used for time calculations. */
+ private static final Time TIME = new Time();
+ /** The object on which the groups are created. */
+ private final GroupCreator mGroupCreator;
+
+ public CallLogGroupBuilder(GroupCreator groupCreator) {
+ mGroupCreator = groupCreator;
+ }
+
+ /**
+ * Finds all groups of adjacent entries in the call log which should be grouped together and calls
+ * {@link GroupCreator#addGroup(int, int)} on {@link #mGroupCreator} for each of them.
+ *
+ * <p>For entries that are not grouped with others, we do not need to create a group of size one.
+ *
+ * <p>It assumes that the cursor will not change during its execution.
+ *
+ * @see GroupingListAdapter#addGroups(Cursor)
+ */
+ public void addGroups(Cursor cursor) {
+ final int count = cursor.getCount();
+ if (count == 0) {
+ return;
+ }
+
+ // Clear any previous day grouping information.
+ mGroupCreator.clearDayGroups();
+
+ // Get current system time, used for calculating which day group calls belong to.
+ long currentTime = System.currentTimeMillis();
+ cursor.moveToFirst();
+
+ // Determine the day group for the first call in the cursor.
+ final long firstDate = cursor.getLong(CallLogQuery.DATE);
+ final long firstRowId = cursor.getLong(CallLogQuery.ID);
+ int groupDayGroup = getDayGroup(firstDate, currentTime);
+ mGroupCreator.setDayGroup(firstRowId, groupDayGroup);
+
+ // Instantiate the group values to those of the first call in the cursor.
+ String groupNumber = cursor.getString(CallLogQuery.NUMBER);
+ String groupPostDialDigits =
+ (VERSION.SDK_INT >= VERSION_CODES.N) ? cursor.getString(CallLogQuery.POST_DIAL_DIGITS) : "";
+ String groupViaNumbers =
+ (VERSION.SDK_INT >= VERSION_CODES.N) ? cursor.getString(CallLogQuery.VIA_NUMBER) : "";
+ int groupCallType = cursor.getInt(CallLogQuery.CALL_TYPE);
+ String groupAccountComponentName = cursor.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME);
+ String groupAccountId = cursor.getString(CallLogQuery.ACCOUNT_ID);
+ int groupSize = 1;
+
+ String number;
+ String numberPostDialDigits;
+ String numberViaNumbers;
+ int callType;
+ String accountComponentName;
+ String accountId;
+
+ while (cursor.moveToNext()) {
+ // Obtain the values for the current call to group.
+ number = cursor.getString(CallLogQuery.NUMBER);
+ numberPostDialDigits =
+ (VERSION.SDK_INT >= VERSION_CODES.N)
+ ? cursor.getString(CallLogQuery.POST_DIAL_DIGITS)
+ : "";
+ numberViaNumbers =
+ (VERSION.SDK_INT >= VERSION_CODES.N) ? cursor.getString(CallLogQuery.VIA_NUMBER) : "";
+ callType = cursor.getInt(CallLogQuery.CALL_TYPE);
+ accountComponentName = cursor.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME);
+ accountId = cursor.getString(CallLogQuery.ACCOUNT_ID);
+
+ final boolean isSameNumber = equalNumbers(groupNumber, number);
+ final boolean isSamePostDialDigits = groupPostDialDigits.equals(numberPostDialDigits);
+ final boolean isSameViaNumbers = groupViaNumbers.equals(numberViaNumbers);
+ final boolean isSameAccount =
+ isSameAccount(groupAccountComponentName, accountComponentName, groupAccountId, accountId);
+
+ // Group with the same number and account. Never group voicemails. Only group blocked
+ // calls with other blocked calls.
+ if (isSameNumber
+ && isSameAccount
+ && isSamePostDialDigits
+ && isSameViaNumbers
+ && areBothNotVoicemail(callType, groupCallType)
+ && (areBothNotBlocked(callType, groupCallType)
+ || areBothBlocked(callType, groupCallType))) {
+ // Increment the size of the group to include the current call, but do not create
+ // the group until finding a call that does not match.
+ groupSize++;
+ } else {
+ // The call group has changed. Determine the day group for the new call group.
+ final long date = cursor.getLong(CallLogQuery.DATE);
+ groupDayGroup = getDayGroup(date, currentTime);
+
+ // Create a group for the previous group of calls, which does not include the
+ // current call.
+ mGroupCreator.addGroup(cursor.getPosition() - groupSize, groupSize);
+
+ // Start a new group; it will include at least the current call.
+ groupSize = 1;
+
+ // Update the group values to those of the current call.
+ groupNumber = number;
+ groupPostDialDigits = numberPostDialDigits;
+ groupViaNumbers = numberViaNumbers;
+ groupCallType = callType;
+ groupAccountComponentName = accountComponentName;
+ groupAccountId = accountId;
+ }
+
+ // Save the day group associated with the current call.
+ final long currentCallId = cursor.getLong(CallLogQuery.ID);
+ mGroupCreator.setDayGroup(currentCallId, groupDayGroup);
+ }
+
+ // Create a group for the last set of calls.
+ mGroupCreator.addGroup(count - groupSize, groupSize);
+ }
+
+ @VisibleForTesting
+ boolean equalNumbers(@Nullable String number1, @Nullable String number2) {
+ if (PhoneNumberHelper.isUriNumber(number1) || PhoneNumberHelper.isUriNumber(number2)) {
+ return compareSipAddresses(number1, number2);
+ } else {
+ return PhoneNumberUtils.compare(number1, number2);
+ }
+ }
+
+ private boolean isSameAccount(String name1, String name2, String id1, String id2) {
+ return TextUtils.equals(name1, name2) && TextUtils.equals(id1, id2);
+ }
+
+ @VisibleForTesting
+ boolean compareSipAddresses(@Nullable String number1, @Nullable String number2) {
+ if (number1 == null || number2 == null) {
+ return Objects.equals(number1, number2);
+ }
+
+ int index1 = number1.indexOf('@');
+ final String userinfo1;
+ final String rest1;
+ if (index1 != -1) {
+ userinfo1 = number1.substring(0, index1);
+ rest1 = number1.substring(index1);
+ } else {
+ userinfo1 = number1;
+ rest1 = "";
+ }
+
+ int index2 = number2.indexOf('@');
+ final String userinfo2;
+ final String rest2;
+ if (index2 != -1) {
+ userinfo2 = number2.substring(0, index2);
+ rest2 = number2.substring(index2);
+ } else {
+ userinfo2 = number2;
+ rest2 = "";
+ }
+
+ return userinfo1.equals(userinfo2) && rest1.equalsIgnoreCase(rest2);
+ }
+
+ /**
+ * Given a call date and the current date, determine which date group the call belongs in.
+ *
+ * @param date The call date.
+ * @param now The current date.
+ * @return The date group the call belongs in.
+ */
+ private int getDayGroup(long date, long now) {
+ int days = DateUtils.getDayDifference(TIME, date, now);
+
+ if (days == 0) {
+ return DAY_GROUP_TODAY;
+ } else if (days == 1) {
+ return DAY_GROUP_YESTERDAY;
+ } else {
+ return DAY_GROUP_OTHER;
+ }
+ }
+
+ private boolean areBothNotVoicemail(int callType, int groupCallType) {
+ return callType != AppCompatConstants.CALLS_VOICEMAIL_TYPE
+ && groupCallType != AppCompatConstants.CALLS_VOICEMAIL_TYPE;
+ }
+
+ private boolean areBothNotBlocked(int callType, int groupCallType) {
+ return callType != AppCompatConstants.CALLS_BLOCKED_TYPE
+ && groupCallType != AppCompatConstants.CALLS_BLOCKED_TYPE;
+ }
+
+ private boolean areBothBlocked(int callType, int groupCallType) {
+ return callType == AppCompatConstants.CALLS_BLOCKED_TYPE
+ && groupCallType == AppCompatConstants.CALLS_BLOCKED_TYPE;
+ }
+
+ public interface GroupCreator {
+
+ /**
+ * Defines the interface for adding a group to the call log. The primary group for a call log
+ * groups the calls together based on the number which was dialed.
+ *
+ * @param cursorPosition The starting position of the group in the cursor.
+ * @param size The size of the group.
+ */
+ void addGroup(int cursorPosition, int size);
+
+ /**
+ * Defines the interface for tracking the day group each call belongs to. Calls in a call group
+ * are assigned the same day group as the first call in the group. The day group assigns calls
+ * to the buckets: Today, Yesterday, Last week, and Other
+ *
+ * @param rowId The row Id of the current call.
+ * @param dayGroup The day group the call belongs in.
+ */
+ void setDayGroup(long rowId, int dayGroup);
+
+ /** Defines the interface for clearing the day groupings information on rebind/regroup. */
+ void clearDayGroups();
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallLogListItemHelper.java b/java/com/android/dialer/app/calllog/CallLogListItemHelper.java
new file mode 100644
index 000000000..ea2119c83
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallLogListItemHelper.java
@@ -0,0 +1,277 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.content.res.Resources;
+import android.provider.CallLog.Calls;
+import android.support.annotation.WorkerThread;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.dialer.app.PhoneCallDetails;
+import com.android.dialer.app.R;
+import com.android.dialer.app.calllog.calllogcache.CallLogCache;
+import com.android.dialer.common.Assert;
+import com.android.dialer.compat.AppCompatConstants;
+
+/** Helper class to fill in the views of a call log entry. */
+/* package */ class CallLogListItemHelper {
+
+ private static final String TAG = "CallLogListItemHelper";
+
+ /** Helper for populating the details of a phone call. */
+ private final PhoneCallDetailsHelper mPhoneCallDetailsHelper;
+ /** Resources to look up strings. */
+ private final Resources mResources;
+
+ private final CallLogCache mCallLogCache;
+
+ /**
+ * Creates a new helper instance.
+ *
+ * @param phoneCallDetailsHelper used to set the details of a phone call
+ * @param resources The object from which resources can be retrieved
+ * @param callLogCache A cache for values retrieved from telecom/telephony
+ */
+ public CallLogListItemHelper(
+ PhoneCallDetailsHelper phoneCallDetailsHelper,
+ Resources resources,
+ CallLogCache callLogCache) {
+ mPhoneCallDetailsHelper = phoneCallDetailsHelper;
+ mResources = resources;
+ mCallLogCache = callLogCache;
+ }
+
+ /**
+ * Update phone call details. This is called before any drawing to avoid expensive operation on UI
+ * thread.
+ *
+ * @param details
+ */
+ @WorkerThread
+ public void updatePhoneCallDetails(PhoneCallDetails details) {
+ Assert.isWorkerThread();
+ details.callLocationAndDate = mPhoneCallDetailsHelper.getCallLocationAndDate(details);
+ details.callDescription = getCallDescription(details);
+ }
+
+ /**
+ * Sets the name, label, and number for a contact.
+ *
+ * @param views the views to populate
+ * @param details the details of a phone call needed to fill in the data
+ */
+ public void setPhoneCallDetails(CallLogListItemViewHolder views, PhoneCallDetails details) {
+ mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details);
+
+ // Set the accessibility text for the contact badge
+ views.quickContactView.setContentDescription(getContactBadgeDescription(details));
+
+ // Set the primary action accessibility description
+ views.primaryActionView.setContentDescription(details.callDescription);
+
+ // Cache name or number of caller. Used when setting the content descriptions of buttons
+ // when the actions ViewStub is inflated.
+ views.nameOrNumber = getNameOrNumber(details);
+
+ // The call type or Location associated with the call. Use when setting text for a
+ // voicemail log's call button
+ views.callTypeOrLocation = mPhoneCallDetailsHelper.getCallTypeOrLocation(details);
+
+ // Cache country iso. Used for number filtering.
+ views.countryIso = details.countryIso;
+
+ views.updatePhoto();
+ }
+
+ /**
+ * Sets the accessibility descriptions for the action buttons in the action button ViewStub.
+ *
+ * @param views The views associated with the current call log entry.
+ */
+ public void setActionContentDescriptions(CallLogListItemViewHolder views) {
+ if (views.nameOrNumber == null) {
+ Log.e(TAG, "setActionContentDescriptions; name or number is null.");
+ }
+
+ // Calling expandTemplate with a null parameter will cause a NullPointerException.
+ // Although we don't expect a null name or number, it is best to protect against it.
+ CharSequence nameOrNumber = views.nameOrNumber == null ? "" : views.nameOrNumber;
+
+ views.videoCallButtonView.setContentDescription(
+ TextUtils.expandTemplate(
+ mResources.getString(R.string.description_video_call_action), nameOrNumber));
+
+ views.createNewContactButtonView.setContentDescription(
+ TextUtils.expandTemplate(
+ mResources.getString(R.string.description_create_new_contact_action), nameOrNumber));
+
+ views.addToExistingContactButtonView.setContentDescription(
+ TextUtils.expandTemplate(
+ mResources.getString(R.string.description_add_to_existing_contact_action),
+ nameOrNumber));
+
+ views.detailsButtonView.setContentDescription(
+ TextUtils.expandTemplate(
+ mResources.getString(R.string.description_details_action), nameOrNumber));
+ }
+
+ /**
+ * Returns the accessibility description for the contact badge for a call log entry.
+ *
+ * @param details Details of call.
+ * @return Accessibility description.
+ */
+ private CharSequence getContactBadgeDescription(PhoneCallDetails details) {
+ if (details.isSpam) {
+ return mResources.getString(
+ R.string.description_spam_contact_details, getNameOrNumber(details));
+ }
+ return mResources.getString(R.string.description_contact_details, getNameOrNumber(details));
+ }
+
+ /**
+ * Returns the accessibility description of the "return call/call" action for a call log entry.
+ * Accessibility text is a combination of: {Voicemail Prefix}. {Number of Calls}. {Caller
+ * information} {Phone Account}. If most recent call is a voicemail, {Voicemail Prefix} is "New
+ * Voicemail.", otherwise "".
+ *
+ * <p>If more than one call for the caller, {Number of Calls} is: "{number of calls} calls.",
+ * otherwise "".
+ *
+ * <p>The {Caller Information} references the most recent call associated with the caller. For
+ * incoming calls: If missed call: Missed call from {Name/Number} {Call Type} {Call Time}. If
+ * answered call: Answered call from {Name/Number} {Call Type} {Call Time}.
+ *
+ * <p>For outgoing calls: If outgoing: Call to {Name/Number] {Call Type} {Call Time}.
+ *
+ * <p>Where: {Name/Number} is the name or number of the caller (as shown in call log). {Call type}
+ * is the contact phone number type (eg mobile) or location. {Call Time} is the time since the
+ * last call for the contact occurred.
+ *
+ * <p>The {Phone Account} refers to the account/SIM through which the call was placed or received
+ * in multi-SIM devices.
+ *
+ * <p>Examples: 3 calls. New Voicemail. Missed call from Joe Smith mobile 2 hours ago on SIM 1.
+ *
+ * <p>2 calls. Answered call from John Doe mobile 1 hour ago.
+ *
+ * @param context The application context.
+ * @param details Details of call.
+ * @return Return call action description.
+ */
+ public CharSequence getCallDescription(PhoneCallDetails details) {
+ // Get the name or number of the caller.
+ final CharSequence nameOrNumber = getNameOrNumber(details);
+
+ // Get the call type or location of the caller; null if not applicable
+ final CharSequence typeOrLocation = mPhoneCallDetailsHelper.getCallTypeOrLocation(details);
+
+ // Get the time/date of the call
+ final CharSequence timeOfCall = mPhoneCallDetailsHelper.getCallDate(details);
+
+ SpannableStringBuilder callDescription = new SpannableStringBuilder();
+
+ // Add number of calls if more than one.
+ if (details.callTypes.length > 1) {
+ callDescription.append(
+ mResources.getString(R.string.description_num_calls, details.callTypes.length));
+ }
+
+ // If call had video capabilities, add the "Video Call" string.
+ if ((details.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO) {
+ callDescription.append(mResources.getString(R.string.description_video_call));
+ }
+
+ String accountLabel = mCallLogCache.getAccountLabel(details.accountHandle);
+ CharSequence onAccountLabel =
+ PhoneCallDetails.createAccountLabelDescription(mResources, details.viaNumber, accountLabel);
+
+ int stringID = getCallDescriptionStringID(details.callTypes, details.isRead);
+ callDescription.append(
+ TextUtils.expandTemplate(
+ mResources.getString(stringID),
+ nameOrNumber,
+ typeOrLocation == null ? "" : typeOrLocation,
+ timeOfCall,
+ onAccountLabel));
+
+ return callDescription;
+ }
+
+ /**
+ * Determine the appropriate string ID to describe a call for accessibility purposes.
+ *
+ * @param callTypes The type of call corresponding to this entry or multiple if this entry
+ * represents multiple calls grouped together.
+ * @param isRead If the entry is a voicemail, {@code true} if the voicemail is read.
+ * @return String resource ID to use.
+ */
+ public int getCallDescriptionStringID(int[] callTypes, boolean isRead) {
+ int lastCallType = getLastCallType(callTypes);
+ int stringID;
+
+ if (lastCallType == AppCompatConstants.CALLS_MISSED_TYPE) {
+ //Message: Missed call from <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>,
+ //<PhoneAccount>.
+ stringID = R.string.description_incoming_missed_call;
+ } else if (lastCallType == AppCompatConstants.CALLS_INCOMING_TYPE) {
+ //Message: Answered call from <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>,
+ //<PhoneAccount>.
+ stringID = R.string.description_incoming_answered_call;
+ } else if (lastCallType == AppCompatConstants.CALLS_VOICEMAIL_TYPE) {
+ //Message: (Unread) [V/v]oicemail from <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>,
+ //<PhoneAccount>.
+ stringID =
+ isRead ? R.string.description_read_voicemail : R.string.description_unread_voicemail;
+ } else {
+ //Message: Call to <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>, <PhoneAccount>.
+ stringID = R.string.description_outgoing_call;
+ }
+ return stringID;
+ }
+
+ /**
+ * Determine the call type for the most recent call.
+ *
+ * @param callTypes Call types to check.
+ * @return Call type.
+ */
+ private int getLastCallType(int[] callTypes) {
+ if (callTypes.length > 0) {
+ return callTypes[0];
+ } else {
+ return Calls.MISSED_TYPE;
+ }
+ }
+
+ /**
+ * Return the name or number of the caller specified by the details.
+ *
+ * @param details Call details
+ * @return the name (if known) of the caller, otherwise the formatted number.
+ */
+ private CharSequence getNameOrNumber(PhoneCallDetails details) {
+ final CharSequence recipient;
+ if (!TextUtils.isEmpty(details.getPreferredName())) {
+ recipient = details.getPreferredName();
+ } else {
+ recipient = details.displayNumber + details.postDialDigits;
+ }
+ return recipient;
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
new file mode 100644
index 000000000..6abd36078
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
@@ -0,0 +1,966 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.support.v7.widget.CardView;
+import android.support.v7.widget.RecyclerView;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.PhoneNumberUtils;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
+import android.text.TextUtils;
+import android.view.ContextMenu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewStub;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.QuickContactBadge;
+import android.widget.TextView;
+import com.android.contacts.common.ClipboardUtils;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+import com.android.contacts.common.dialog.CallSubjectDialog;
+import com.android.contacts.common.util.UriUtils;
+import com.android.dialer.app.DialtactsActivity;
+import com.android.dialer.app.R;
+import com.android.dialer.app.calllog.calllogcache.CallLogCache;
+import com.android.dialer.app.voicemail.VoicemailPlaybackLayout;
+import com.android.dialer.app.voicemail.VoicemailPlaybackPresenter;
+import com.android.dialer.blocking.BlockedNumbersMigrator;
+import com.android.dialer.blocking.FilteredNumberCompat;
+import com.android.dialer.blocking.FilteredNumbersUtil;
+import com.android.dialer.callcomposer.CallComposerActivity;
+import com.android.dialer.callcomposer.nano.CallComposerContact;
+import com.android.dialer.common.ConfigProviderBindings;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.logging.nano.ScreenEvent;
+import com.android.dialer.phonenumbercache.ContactInfo;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.android.dialer.util.CallUtil;
+import com.android.dialer.util.DialerUtils;
+
+/**
+ * This is an object containing references to views contained by the call log list item. This
+ * improves performance by reducing the frequency with which we need to find views by IDs.
+ *
+ * <p>This object also contains UI logic pertaining to the view, to isolate it from the
+ * CallLogAdapter.
+ */
+public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener,
+ MenuItem.OnMenuItemClickListener,
+ View.OnCreateContextMenuListener {
+ private static final String CONFIG_SHARE_VOICEMAIL_ALLOWED = "share_voicemail_allowed";
+
+ /** The root view of the call log list item */
+ public final View rootView;
+ /** The quick contact badge for the contact. */
+ public final QuickContactBadge quickContactView;
+ /** The primary action view of the entry. */
+ public final View primaryActionView;
+ /** The details of the phone call. */
+ public final PhoneCallDetailsViews phoneCallDetailsViews;
+ /** The text of the header for a day grouping. */
+ public final TextView dayGroupHeader;
+ /** The view containing the details for the call log row, including the action buttons. */
+ public final CardView callLogEntryView;
+ /** The actionable view which places a call to the number corresponding to the call log row. */
+ public final ImageView primaryActionButtonView;
+
+ private final Context mContext;
+ private final CallLogCache mCallLogCache;
+ private final CallLogListItemHelper mCallLogListItemHelper;
+ private final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
+ private final OnClickListener mBlockReportListener;
+ private final int mPhotoSize;
+ /** Whether the data fields are populated by the worker thread, ready to be shown. */
+ public boolean isLoaded;
+ /** The view containing call log item actions. Null until the ViewStub is inflated. */
+ public View actionsView;
+ /** The button views below are assigned only when the action section is expanded. */
+ public VoicemailPlaybackLayout voicemailPlaybackView;
+
+ public View callButtonView;
+ public View videoCallButtonView;
+ public View createNewContactButtonView;
+ public View addToExistingContactButtonView;
+ public View sendMessageView;
+ public View blockReportView;
+ public View blockView;
+ public View unblockView;
+ public View reportNotSpamView;
+ public View detailsButtonView;
+ public View callWithNoteButtonView;
+ public View callComposeButtonView;
+ public View sendVoicemailButtonView;
+ public ImageView workIconView;
+ /**
+ * The row Id for the first call associated with the call log entry. Used as a key for the map
+ * used to track which call log entries have the action button section expanded.
+ */
+ public long rowId;
+ /**
+ * The call Ids for the calls represented by the current call log entry. Used when the user
+ * deletes a call log entry.
+ */
+ public long[] callIds;
+ /**
+ * The callable phone number for the current call log entry. Cached here as the call back intent
+ * is set only when the actions ViewStub is inflated.
+ */
+ public String number;
+ /** The post-dial numbers that are dialed following the phone number. */
+ public String postDialDigits;
+ /** The formatted phone number to display. */
+ public String displayNumber;
+ /**
+ * The phone number presentation for the current call log entry. Cached here as the call back
+ * intent is set only when the actions ViewStub is inflated.
+ */
+ public int numberPresentation;
+ /** The type of the phone number (e.g. main, work, etc). */
+ public String numberType;
+ /**
+ * The country iso for the call. Cached here as the call back intent is set only when the actions
+ * ViewStub is inflated.
+ */
+ public String countryIso;
+ /**
+ * The type of call for the current call log entry. Cached here as the call back intent is set
+ * only when the actions ViewStub is inflated.
+ */
+ public int callType;
+ /**
+ * ID for blocked numbers database. Set when context menu is created, if the number is blocked.
+ */
+ public Integer blockId;
+ /**
+ * The account for the current call log entry. Cached here as the call back intent is set only
+ * when the actions ViewStub is inflated.
+ */
+ public PhoneAccountHandle accountHandle;
+ /**
+ * If the call has an associated voicemail message, the URI of the voicemail message for playback.
+ * Cached here as the voicemail intent is only set when the actions ViewStub is inflated.
+ */
+ public String voicemailUri;
+ /**
+ * The name or number associated with the call. Cached here for use when setting content
+ * descriptions on buttons in the actions ViewStub when it is inflated.
+ */
+ public CharSequence nameOrNumber;
+ /**
+ * The call type or Location associated with the call. Cached here for use when setting text for a
+ * voicemail log's call button
+ */
+ public CharSequence callTypeOrLocation;
+ /** Whether this row is for a business or not. */
+ public boolean isBusiness;
+ /** The contact info for the contact displayed in this list item. */
+ public volatile ContactInfo info;
+ /** Whether spam feature is enabled, which affects UI. */
+ public boolean isSpamFeatureEnabled;
+ /** Whether the current log entry is a spam number or not. */
+ public boolean isSpam;
+
+ public boolean isCallComposerCapable;
+
+ private View.OnClickListener mExpandCollapseListener;
+ private boolean mVoicemailPrimaryActionButtonClicked;
+
+ public int dayGroupHeaderVisibility;
+ public CharSequence dayGroupHeaderText;
+ public boolean isAttachedToWindow;
+
+ public AsyncTask<Void, Void, ?> asyncTask;
+
+ private CallLogListItemViewHolder(
+ Context context,
+ OnClickListener blockReportListener,
+ View.OnClickListener expandCollapseListener,
+ CallLogCache callLogCache,
+ CallLogListItemHelper callLogListItemHelper,
+ VoicemailPlaybackPresenter voicemailPlaybackPresenter,
+ View rootView,
+ QuickContactBadge quickContactView,
+ View primaryActionView,
+ PhoneCallDetailsViews phoneCallDetailsViews,
+ CardView callLogEntryView,
+ TextView dayGroupHeader,
+ ImageView primaryActionButtonView) {
+ super(rootView);
+
+ mContext = context;
+ mExpandCollapseListener = expandCollapseListener;
+ mCallLogCache = callLogCache;
+ mCallLogListItemHelper = callLogListItemHelper;
+ mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
+ mBlockReportListener = blockReportListener;
+
+ this.rootView = rootView;
+ this.quickContactView = quickContactView;
+ this.primaryActionView = primaryActionView;
+ this.phoneCallDetailsViews = phoneCallDetailsViews;
+ this.callLogEntryView = callLogEntryView;
+ this.dayGroupHeader = dayGroupHeader;
+ this.primaryActionButtonView = primaryActionButtonView;
+ this.workIconView = (ImageView) rootView.findViewById(R.id.work_profile_icon);
+ mPhotoSize = mContext.getResources().getDimensionPixelSize(R.dimen.contact_photo_size);
+
+ // Set text height to false on the TextViews so they don't have extra padding.
+ phoneCallDetailsViews.nameView.setElegantTextHeight(false);
+ phoneCallDetailsViews.callLocationAndDate.setElegantTextHeight(false);
+
+ quickContactView.setOverlay(null);
+ if (CompatUtils.hasPrioritizedMimeType()) {
+ quickContactView.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE);
+ }
+ primaryActionButtonView.setOnClickListener(this);
+ primaryActionView.setOnClickListener(mExpandCollapseListener);
+ primaryActionView.setOnCreateContextMenuListener(this);
+ }
+
+ public static CallLogListItemViewHolder create(
+ View view,
+ Context context,
+ OnClickListener blockReportListener,
+ View.OnClickListener expandCollapseListener,
+ CallLogCache callLogCache,
+ CallLogListItemHelper callLogListItemHelper,
+ VoicemailPlaybackPresenter voicemailPlaybackPresenter) {
+
+ return new CallLogListItemViewHolder(
+ context,
+ blockReportListener,
+ expandCollapseListener,
+ callLogCache,
+ callLogListItemHelper,
+ voicemailPlaybackPresenter,
+ view,
+ (QuickContactBadge) view.findViewById(R.id.quick_contact_photo),
+ view.findViewById(R.id.primary_action_view),
+ PhoneCallDetailsViews.fromView(view),
+ (CardView) view.findViewById(R.id.call_log_row),
+ (TextView) view.findViewById(R.id.call_log_day_group_label),
+ (ImageView) view.findViewById(R.id.primary_action_button));
+ }
+
+ public static CallLogListItemViewHolder createForTest(Context context) {
+ Resources resources = context.getResources();
+ CallLogCache callLogCache = CallLogCache.getCallLogCache(context);
+ PhoneCallDetailsHelper phoneCallDetailsHelper =
+ new PhoneCallDetailsHelper(context, resources, callLogCache);
+
+ CallLogListItemViewHolder viewHolder =
+ new CallLogListItemViewHolder(
+ context,
+ null,
+ null /* expandCollapseListener */,
+ callLogCache,
+ new CallLogListItemHelper(phoneCallDetailsHelper, resources, callLogCache),
+ null /* voicemailPlaybackPresenter */,
+ new View(context),
+ new QuickContactBadge(context),
+ new View(context),
+ PhoneCallDetailsViews.createForTest(context),
+ new CardView(context),
+ new TextView(context),
+ new ImageView(context));
+ viewHolder.detailsButtonView = new TextView(context);
+ viewHolder.actionsView = new View(context);
+ viewHolder.voicemailPlaybackView = new VoicemailPlaybackLayout(context);
+ viewHolder.workIconView = new ImageButton(context);
+ return viewHolder;
+ }
+
+ @Override
+ public void onCreateContextMenu(
+ final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ if (TextUtils.isEmpty(number)) {
+ return;
+ }
+
+ if (callType == CallLog.Calls.VOICEMAIL_TYPE) {
+ menu.setHeaderTitle(mContext.getResources().getText(R.string.voicemail));
+ } else {
+ menu.setHeaderTitle(
+ PhoneNumberUtilsCompat.createTtsSpannable(
+ BidiFormatter.getInstance().unicodeWrap(number, TextDirectionHeuristics.LTR)));
+ }
+
+ menu.add(
+ ContextMenu.NONE,
+ R.id.context_menu_copy_to_clipboard,
+ ContextMenu.NONE,
+ R.string.action_copy_number_text)
+ .setOnMenuItemClickListener(this);
+
+ // The edit number before call does not show up if any of the conditions apply:
+ // 1) Number cannot be called
+ // 2) Number is the voicemail number
+ // 3) Number is a SIP address
+
+ if (PhoneNumberHelper.canPlaceCallsTo(number, numberPresentation)
+ && !mCallLogCache.isVoicemailNumber(accountHandle, number)
+ && !PhoneNumberHelper.isSipNumber(number)) {
+ menu.add(
+ ContextMenu.NONE,
+ R.id.context_menu_edit_before_call,
+ ContextMenu.NONE,
+ R.string.action_edit_number_before_call)
+ .setOnMenuItemClickListener(this);
+ }
+
+ if (callType == CallLog.Calls.VOICEMAIL_TYPE
+ && phoneCallDetailsViews.voicemailTranscriptionView.length() > 0) {
+ menu.add(
+ ContextMenu.NONE,
+ R.id.context_menu_copy_transcript_to_clipboard,
+ ContextMenu.NONE,
+ R.string.copy_transcript_text)
+ .setOnMenuItemClickListener(this);
+ }
+
+ String e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso);
+ boolean isVoicemailNumber = mCallLogCache.isVoicemailNumber(accountHandle, number);
+ if (!isVoicemailNumber
+ && FilteredNumbersUtil.canBlockNumber(mContext, e164Number, number)
+ && FilteredNumberCompat.canAttemptBlockOperations(mContext)) {
+ boolean isBlocked = blockId != null;
+ if (isBlocked) {
+ menu.add(
+ ContextMenu.NONE,
+ R.id.context_menu_unblock,
+ ContextMenu.NONE,
+ R.string.call_log_action_unblock_number)
+ .setOnMenuItemClickListener(this);
+ } else {
+ if (isSpamFeatureEnabled) {
+ if (isSpam) {
+ menu.add(
+ ContextMenu.NONE,
+ R.id.context_menu_report_not_spam,
+ ContextMenu.NONE,
+ R.string.call_log_action_remove_spam)
+ .setOnMenuItemClickListener(this);
+ menu.add(
+ ContextMenu.NONE,
+ R.id.context_menu_block,
+ ContextMenu.NONE,
+ R.string.call_log_action_block_number)
+ .setOnMenuItemClickListener(this);
+ } else {
+ menu.add(
+ ContextMenu.NONE,
+ R.id.context_menu_block_report_spam,
+ ContextMenu.NONE,
+ R.string.call_log_action_block_report_number)
+ .setOnMenuItemClickListener(this);
+ }
+ } else {
+ menu.add(
+ ContextMenu.NONE,
+ R.id.context_menu_block,
+ ContextMenu.NONE,
+ R.string.call_log_action_block_number)
+ .setOnMenuItemClickListener(this);
+ }
+ }
+ }
+
+ Logger.get(mContext).logScreenView(ScreenEvent.Type.CALL_LOG_CONTEXT_MENU, (Activity) mContext);
+ }
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ int resId = item.getItemId();
+ if (resId == R.id.context_menu_copy_to_clipboard) {
+ ClipboardUtils.copyText(mContext, null, number, true);
+ return true;
+ } else if (resId == R.id.context_menu_copy_transcript_to_clipboard) {
+ ClipboardUtils.copyText(
+ mContext, null, phoneCallDetailsViews.voicemailTranscriptionView.getText(), true);
+ return true;
+ } else if (resId == R.id.context_menu_edit_before_call) {
+ final Intent intent = new Intent(Intent.ACTION_DIAL, CallUtil.getCallUri(number));
+ intent.setClass(mContext, DialtactsActivity.class);
+ DialerUtils.startActivityWithErrorToast(mContext, intent);
+ return true;
+ } else if (resId == R.id.context_menu_block_report_spam) {
+ Logger.get(mContext)
+ .logImpression(DialerImpression.Type.CALL_LOG_CONTEXT_MENU_BLOCK_REPORT_SPAM);
+ maybeShowBlockNumberMigrationDialog(
+ new BlockedNumbersMigrator.Listener() {
+ @Override
+ public void onComplete() {
+ mBlockReportListener.onBlockReportSpam(
+ displayNumber, number, countryIso, callType, info.sourceType);
+ }
+ });
+ } else if (resId == R.id.context_menu_block) {
+ Logger.get(mContext).logImpression(DialerImpression.Type.CALL_LOG_CONTEXT_MENU_BLOCK_NUMBER);
+ maybeShowBlockNumberMigrationDialog(
+ new BlockedNumbersMigrator.Listener() {
+ @Override
+ public void onComplete() {
+ mBlockReportListener.onBlock(
+ displayNumber, number, countryIso, callType, info.sourceType);
+ }
+ });
+ } else if (resId == R.id.context_menu_unblock) {
+ Logger.get(mContext)
+ .logImpression(DialerImpression.Type.CALL_LOG_CONTEXT_MENU_UNBLOCK_NUMBER);
+ mBlockReportListener.onUnblock(
+ displayNumber, number, countryIso, callType, info.sourceType, isSpam, blockId);
+ } else if (resId == R.id.context_menu_report_not_spam) {
+ Logger.get(mContext)
+ .logImpression(DialerImpression.Type.CALL_LOG_CONTEXT_MENU_REPORT_AS_NOT_SPAM);
+ mBlockReportListener.onReportNotSpam(
+ displayNumber, number, countryIso, callType, info.sourceType);
+ }
+ return false;
+ }
+
+ /**
+ * Configures the action buttons in the expandable actions ViewStub. The ViewStub is not inflated
+ * during initial binding, so click handlers, tags and accessibility text must be set here, if
+ * necessary.
+ */
+ public void inflateActionViewStub() {
+ ViewStub stub = (ViewStub) rootView.findViewById(R.id.call_log_entry_actions_stub);
+ if (stub != null) {
+ actionsView = stub.inflate();
+
+ voicemailPlaybackView =
+ (VoicemailPlaybackLayout) actionsView.findViewById(R.id.voicemail_playback_layout);
+ voicemailPlaybackView.setViewHolder(this);
+
+ callButtonView = actionsView.findViewById(R.id.call_action);
+ callButtonView.setOnClickListener(this);
+
+ videoCallButtonView = actionsView.findViewById(R.id.video_call_action);
+ videoCallButtonView.setOnClickListener(this);
+
+ createNewContactButtonView = actionsView.findViewById(R.id.create_new_contact_action);
+ createNewContactButtonView.setOnClickListener(this);
+
+ addToExistingContactButtonView =
+ actionsView.findViewById(R.id.add_to_existing_contact_action);
+ addToExistingContactButtonView.setOnClickListener(this);
+
+ sendMessageView = actionsView.findViewById(R.id.send_message_action);
+ sendMessageView.setOnClickListener(this);
+
+ blockReportView = actionsView.findViewById(R.id.block_report_action);
+ blockReportView.setOnClickListener(this);
+
+ blockView = actionsView.findViewById(R.id.block_action);
+ blockView.setOnClickListener(this);
+
+ unblockView = actionsView.findViewById(R.id.unblock_action);
+ unblockView.setOnClickListener(this);
+
+ reportNotSpamView = actionsView.findViewById(R.id.report_not_spam_action);
+ reportNotSpamView.setOnClickListener(this);
+
+ detailsButtonView = actionsView.findViewById(R.id.details_action);
+ detailsButtonView.setOnClickListener(this);
+
+ callWithNoteButtonView = actionsView.findViewById(R.id.call_with_note_action);
+ callWithNoteButtonView.setOnClickListener(this);
+
+ callComposeButtonView = actionsView.findViewById(R.id.call_compose_action);
+ callComposeButtonView.setOnClickListener(this);
+
+ sendVoicemailButtonView = actionsView.findViewById(R.id.share_voicemail);
+ sendVoicemailButtonView.setOnClickListener(this);
+ }
+ }
+
+ private void updatePrimaryActionButton(boolean isExpanded) {
+
+ if (nameOrNumber == null) {
+ LogUtil.e("CallLogListItemViewHolder.updatePrimaryActionButton", "name or number is null");
+ }
+
+ // Calling expandTemplate with a null parameter will cause a NullPointerException.
+ CharSequence validNameOrNumber = nameOrNumber == null ? "" : nameOrNumber;
+
+ if (!TextUtils.isEmpty(voicemailUri)) {
+ // Treat as voicemail list item; show play button if not expanded.
+ if (!isExpanded) {
+ primaryActionButtonView.setImageResource(R.drawable.ic_play_arrow_24dp);
+ primaryActionButtonView.setContentDescription(
+ TextUtils.expandTemplate(
+ mContext.getString(R.string.description_voicemail_action), validNameOrNumber));
+ primaryActionButtonView.setVisibility(View.VISIBLE);
+ } else {
+ primaryActionButtonView.setVisibility(View.GONE);
+ }
+ } else {
+ // Treat as normal list item; show call button, if possible.
+ if (PhoneNumberHelper.canPlaceCallsTo(number, numberPresentation)) {
+ boolean isVoicemailNumber = mCallLogCache.isVoicemailNumber(accountHandle, number);
+ if (isVoicemailNumber) {
+ // Call to generic voicemail number, in case there are multiple accounts.
+ primaryActionButtonView.setTag(IntentProvider.getReturnVoicemailCallIntentProvider());
+ } else {
+ primaryActionButtonView.setTag(
+ IntentProvider.getReturnCallIntentProvider(number + postDialDigits));
+ }
+
+ primaryActionButtonView.setContentDescription(
+ TextUtils.expandTemplate(
+ mContext.getString(R.string.description_call_action), validNameOrNumber));
+ primaryActionButtonView.setImageResource(R.drawable.ic_call_24dp);
+ primaryActionButtonView.setVisibility(View.VISIBLE);
+ } else {
+ primaryActionButtonView.setTag(null);
+ primaryActionButtonView.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ private static boolean isShareVoicemailAllowed(Context context) {
+ return ConfigProviderBindings.get(context).getBoolean(CONFIG_SHARE_VOICEMAIL_ALLOWED, true);
+ }
+
+ /**
+ * Binds text titles, click handlers and intents to the voicemail, details and callback action
+ * buttons.
+ */
+ private void bindActionButtons() {
+ boolean canPlaceCallToNumber = PhoneNumberHelper.canPlaceCallsTo(number, numberPresentation);
+
+ if (isFullyUndialableVoicemail()) {
+ // Sometimes the voicemail server will report the message is from some non phone number
+ // source. If the number does not contains any dialable digit treat it as it is from a unknown
+ // number, remove all action buttons but still show the voicemail playback layout.
+ callButtonView.setVisibility(View.GONE);
+ videoCallButtonView.setVisibility(View.GONE);
+ detailsButtonView.setVisibility(View.GONE);
+ createNewContactButtonView.setVisibility(View.GONE);
+ addToExistingContactButtonView.setVisibility(View.GONE);
+ sendMessageView.setVisibility(View.GONE);
+ callWithNoteButtonView.setVisibility(View.GONE);
+ callComposeButtonView.setVisibility(View.GONE);
+ blockReportView.setVisibility(View.GONE);
+ blockView.setVisibility(View.GONE);
+ unblockView.setVisibility(View.GONE);
+ reportNotSpamView.setVisibility(View.GONE);
+
+ if (isShareVoicemailAllowed(mContext)) {
+ sendVoicemailButtonView.setVisibility(View.VISIBLE);
+ }
+ voicemailPlaybackView.setVisibility(View.VISIBLE);
+ Uri uri = Uri.parse(voicemailUri);
+ mVoicemailPlaybackPresenter.setPlaybackView(
+ voicemailPlaybackView, rowId, uri, mVoicemailPrimaryActionButtonClicked);
+ mVoicemailPrimaryActionButtonClicked = false;
+ CallLogAsyncTaskUtil.markVoicemailAsRead(mContext, uri);
+ return;
+ }
+
+ if (!TextUtils.isEmpty(voicemailUri) && canPlaceCallToNumber) {
+ callButtonView.setTag(IntentProvider.getReturnCallIntentProvider(number));
+ ((TextView) callButtonView.findViewById(R.id.call_action_text))
+ .setText(
+ TextUtils.expandTemplate(
+ mContext.getString(R.string.call_log_action_call),
+ nameOrNumber == null ? "" : nameOrNumber));
+ TextView callTypeOrLocationView =
+ ((TextView) callButtonView.findViewById(R.id.call_type_or_location_text));
+ if (callType == Calls.VOICEMAIL_TYPE && !TextUtils.isEmpty(callTypeOrLocation)) {
+ callTypeOrLocationView.setText(callTypeOrLocation);
+ callTypeOrLocationView.setVisibility(View.VISIBLE);
+ } else {
+ callTypeOrLocationView.setVisibility(View.GONE);
+ }
+ callButtonView.setVisibility(View.VISIBLE);
+ } else {
+ callButtonView.setVisibility(View.GONE);
+ }
+
+ if (shouldShowVideoCallActionButton(canPlaceCallToNumber)) {
+ videoCallButtonView.setTag(IntentProvider.getReturnVideoCallIntentProvider(number));
+ videoCallButtonView.setVisibility(View.VISIBLE);
+ } else {
+ videoCallButtonView.setVisibility(View.GONE);
+ }
+
+ // For voicemail calls, show the voicemail playback layout; hide otherwise.
+ if (callType == Calls.VOICEMAIL_TYPE
+ && mVoicemailPlaybackPresenter != null
+ && !TextUtils.isEmpty(voicemailUri)) {
+ voicemailPlaybackView.setVisibility(View.VISIBLE);
+ if (isShareVoicemailAllowed(mContext)) {
+ Logger.get(mContext).logImpression(DialerImpression.Type.VVM_SHARE_VISIBLE);
+ sendVoicemailButtonView.setVisibility(View.VISIBLE);
+ }
+
+ Uri uri = Uri.parse(voicemailUri);
+ mVoicemailPlaybackPresenter.setPlaybackView(
+ voicemailPlaybackView, rowId, uri, mVoicemailPrimaryActionButtonClicked);
+ mVoicemailPrimaryActionButtonClicked = false;
+ CallLogAsyncTaskUtil.markVoicemailAsRead(mContext, uri);
+ } else {
+ voicemailPlaybackView.setVisibility(View.GONE);
+ sendVoicemailButtonView.setVisibility(View.GONE);
+ }
+
+ if (callType == Calls.VOICEMAIL_TYPE) {
+ detailsButtonView.setVisibility(View.GONE);
+ } else {
+ detailsButtonView.setVisibility(View.VISIBLE);
+ detailsButtonView.setTag(IntentProvider.getCallDetailIntentProvider(rowId, callIds, null));
+ }
+
+ boolean isBlockedOrSpam = blockId != null || (isSpamFeatureEnabled && isSpam);
+
+ if (!isBlockedOrSpam && info != null && UriUtils.isEncodedContactUri(info.lookupUri)) {
+ createNewContactButtonView.setTag(
+ IntentProvider.getAddContactIntentProvider(
+ info.lookupUri, info.name, info.number, info.type, true /* isNewContact */));
+ createNewContactButtonView.setVisibility(View.VISIBLE);
+
+ addToExistingContactButtonView.setTag(
+ IntentProvider.getAddContactIntentProvider(
+ info.lookupUri, info.name, info.number, info.type, false /* isNewContact */));
+ addToExistingContactButtonView.setVisibility(View.VISIBLE);
+ } else {
+ createNewContactButtonView.setVisibility(View.GONE);
+ addToExistingContactButtonView.setVisibility(View.GONE);
+ }
+
+ if (canPlaceCallToNumber && !isBlockedOrSpam) {
+ sendMessageView.setTag(IntentProvider.getSendSmsIntentProvider(number));
+ sendMessageView.setVisibility(View.VISIBLE);
+ } else {
+ sendMessageView.setVisibility(View.GONE);
+ }
+
+ mCallLogListItemHelper.setActionContentDescriptions(this);
+
+ boolean supportsCallSubject = mCallLogCache.doesAccountSupportCallSubject(accountHandle);
+ boolean isVoicemailNumber = mCallLogCache.isVoicemailNumber(accountHandle, number);
+ callWithNoteButtonView.setVisibility(
+ supportsCallSubject && !isVoicemailNumber && info != null ? View.VISIBLE : View.GONE);
+
+ callComposeButtonView.setVisibility(isCallComposerCapable ? View.VISIBLE : View.GONE);
+
+ updateBlockReportActions(isVoicemailNumber);
+ }
+
+ private boolean isFullyUndialableVoicemail() {
+ if (callType == Calls.VOICEMAIL_TYPE) {
+ if (!hasDialableChar(number)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasDialableChar(CharSequence number) {
+ if (TextUtils.isEmpty(number)) {
+ return false;
+ }
+ for (char c : number.toString().toCharArray()) {
+ if (PhoneNumberUtils.isDialable(c)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean shouldShowVideoCallActionButton(boolean canPlaceCallToNumber) {
+ return canPlaceCallToNumber && (hasPlacedVideoCall() || canSupportVideoCall());
+ }
+
+ private boolean hasPlacedVideoCall() {
+ return phoneCallDetailsViews.callTypeIcons.isVideoShown();
+ }
+
+ private boolean canSupportVideoCall() {
+ return mCallLogCache.canRelyOnVideoPresence()
+ && info != null
+ && (info.carrierPresence & Phone.CARRIER_PRESENCE_VT_CAPABLE) != 0;
+ }
+
+ /**
+ * Show or hide the action views, such as voicemail, details, and add contact.
+ *
+ * <p>If the action views have never been shown yet for this view, inflate the view stub.
+ */
+ public void showActions(boolean show) {
+ showOrHideVoicemailTranscriptionView(show);
+
+ if (show) {
+ if (!isLoaded) {
+ // b/31268128 for some unidentified reason showActions() can be called before the item is
+ // loaded, causing NPE on uninitialized fields. Just log and return here, showActions() will
+ // be called again once the item is loaded.
+ LogUtil.e(
+ "CallLogListItemViewHolder.showActions",
+ "called before item is loaded",
+ new Exception());
+ return;
+ }
+
+ // Inflate the view stub if necessary, and wire up the event handlers.
+ inflateActionViewStub();
+ bindActionButtons();
+ actionsView.setVisibility(View.VISIBLE);
+ actionsView.setAlpha(1.0f);
+ } else {
+ // When recycling a view, it is possible the actionsView ViewStub was previously
+ // inflated so we should hide it in this case.
+ if (actionsView != null) {
+ actionsView.setVisibility(View.GONE);
+ }
+ }
+
+ updatePrimaryActionButton(show);
+ }
+
+ public void showOrHideVoicemailTranscriptionView(boolean isExpanded) {
+ if (callType != Calls.VOICEMAIL_TYPE) {
+ return;
+ }
+
+ final TextView view = phoneCallDetailsViews.voicemailTranscriptionView;
+ if (!isExpanded || TextUtils.isEmpty(view.getText())) {
+ view.setVisibility(View.GONE);
+ return;
+ }
+ view.setVisibility(View.VISIBLE);
+ }
+
+ public void updatePhoto() {
+ quickContactView.assignContactUri(info.lookupUri);
+
+ if (isSpamFeatureEnabled && isSpam) {
+ quickContactView.setImageDrawable(mContext.getDrawable(R.drawable.blocked_contact));
+ return;
+ }
+ final boolean isVoicemail = mCallLogCache.isVoicemailNumber(accountHandle, number);
+ int contactType = ContactPhotoManager.TYPE_DEFAULT;
+ if (isVoicemail) {
+ contactType = ContactPhotoManager.TYPE_VOICEMAIL;
+ } else if (isBusiness) {
+ contactType = ContactPhotoManager.TYPE_BUSINESS;
+ }
+
+ final String lookupKey =
+ info.lookupUri != null ? UriUtils.getLookupKeyFromUri(info.lookupUri) : null;
+ final String displayName = TextUtils.isEmpty(info.name) ? displayNumber : info.name;
+ final DefaultImageRequest request =
+ new DefaultImageRequest(displayName, lookupKey, contactType, true /* isCircular */);
+
+ if (info.photoId == 0 && info.photoUri != null) {
+ ContactPhotoManager.getInstance(mContext)
+ .loadPhoto(
+ quickContactView,
+ info.photoUri,
+ mPhotoSize,
+ false /* darkTheme */,
+ true /* isCircular */,
+ request);
+ } else {
+ ContactPhotoManager.getInstance(mContext)
+ .loadThumbnail(
+ quickContactView,
+ info.photoId,
+ false /* darkTheme */,
+ true /* isCircular */,
+ request);
+ }
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view.getId() == R.id.primary_action_button && !TextUtils.isEmpty(voicemailUri)) {
+ Logger.get(mContext).logImpression(DialerImpression.Type.VOICEMAIL_PLAY_AUDIO_DIRECTLY);
+ mVoicemailPrimaryActionButtonClicked = true;
+ mExpandCollapseListener.onClick(primaryActionView);
+ } else if (view.getId() == R.id.call_with_note_action) {
+ CallSubjectDialog.start(
+ (Activity) mContext,
+ info.photoId,
+ info.photoUri,
+ info.lookupUri,
+ (String) nameOrNumber /* top line of contact view in call subject dialog */,
+ isBusiness,
+ number,
+ TextUtils.isEmpty(info.name) ? null : displayNumber, /* second line of contact
+ view in dialog. */
+ numberType, /* phone number type (e.g. mobile) in second line of contact view */
+ accountHandle);
+ } else if (view.getId() == R.id.block_report_action) {
+ Logger.get(mContext).logImpression(DialerImpression.Type.CALL_LOG_BLOCK_REPORT_SPAM);
+ maybeShowBlockNumberMigrationDialog(
+ new BlockedNumbersMigrator.Listener() {
+ @Override
+ public void onComplete() {
+ mBlockReportListener.onBlockReportSpam(
+ displayNumber, number, countryIso, callType, info.sourceType);
+ }
+ });
+ } else if (view.getId() == R.id.block_action) {
+ Logger.get(mContext).logImpression(DialerImpression.Type.CALL_LOG_BLOCK_NUMBER);
+ maybeShowBlockNumberMigrationDialog(
+ new BlockedNumbersMigrator.Listener() {
+ @Override
+ public void onComplete() {
+ mBlockReportListener.onBlock(
+ displayNumber, number, countryIso, callType, info.sourceType);
+ }
+ });
+ } else if (view.getId() == R.id.unblock_action) {
+ Logger.get(mContext).logImpression(DialerImpression.Type.CALL_LOG_UNBLOCK_NUMBER);
+ mBlockReportListener.onUnblock(
+ displayNumber, number, countryIso, callType, info.sourceType, isSpam, blockId);
+ } else if (view.getId() == R.id.report_not_spam_action) {
+ Logger.get(mContext).logImpression(DialerImpression.Type.CALL_LOG_REPORT_AS_NOT_SPAM);
+ mBlockReportListener.onReportNotSpam(
+ displayNumber, number, countryIso, callType, info.sourceType);
+ } else if (view.getId() == R.id.call_compose_action) {
+ LogUtil.i("CallLogListItemViewHolder.onClick", "share and call pressed");
+ Logger.get(mContext).logImpression(DialerImpression.Type.CALL_LOG_SHARE_AND_CALL);
+ CallComposerContact contact = new CallComposerContact();
+ contact.photoId = info.photoId;
+ contact.photoUri = info.photoUri == null ? null : info.photoUri.toString();
+ contact.contactUri = info.lookupUri == null ? null : info.lookupUri.toString();
+ contact.nameOrNumber = (String) nameOrNumber;
+ contact.isBusiness = isBusiness;
+ contact.number = number;
+ /* second line of contact view. */
+ contact.displayNumber = TextUtils.isEmpty(info.name) ? null : displayNumber;
+ /* phone number type (e.g. mobile) in second line of contact view */
+ contact.numberLabel = numberType;
+ Activity activity = (Activity) mContext;
+ activity.startActivityForResult(
+ CallComposerActivity.newIntent(activity, contact),
+ DialtactsActivity.ACTIVITY_REQUEST_CODE_CALL_COMPOSE);
+ } else if (view.getId() == R.id.share_voicemail) {
+ Logger.get(mContext).logImpression(DialerImpression.Type.VVM_SHARE_PRESSED);
+ mVoicemailPlaybackPresenter.shareVoicemail();
+ } else {
+ logCallLogAction(view.getId());
+ final IntentProvider intentProvider = (IntentProvider) view.getTag();
+ if (intentProvider != null) {
+ final Intent intent = intentProvider.getIntent(mContext);
+ // See IntentProvider.getCallDetailIntentProvider() for why this may be null.
+ if (intent != null) {
+ DialerUtils.startActivityWithErrorToast(mContext, intent);
+ }
+ }
+ }
+ }
+
+ private void logCallLogAction(int id) {
+ if (id == R.id.send_message_action) {
+ Logger.get(mContext).logImpression(DialerImpression.Type.CALL_LOG_SEND_MESSAGE);
+ } else if (id == R.id.add_to_existing_contact_action) {
+ Logger.get(mContext).logImpression(DialerImpression.Type.CALL_LOG_ADD_TO_CONTACT);
+ } else if (id == R.id.create_new_contact_action) {
+ Logger.get(mContext).logImpression(DialerImpression.Type.CALL_LOG_CREATE_NEW_CONTACT);
+ }
+ }
+
+ private void maybeShowBlockNumberMigrationDialog(BlockedNumbersMigrator.Listener listener) {
+ if (!FilteredNumberCompat.maybeShowBlockNumberMigrationDialog(
+ mContext, ((Activity) mContext).getFragmentManager(), listener)) {
+ listener.onComplete();
+ }
+ }
+
+ private void updateBlockReportActions(boolean isVoicemailNumber) {
+ // Set block/spam actions.
+ blockReportView.setVisibility(View.GONE);
+ blockView.setVisibility(View.GONE);
+ unblockView.setVisibility(View.GONE);
+ reportNotSpamView.setVisibility(View.GONE);
+ String e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso);
+ if (isVoicemailNumber
+ || !FilteredNumbersUtil.canBlockNumber(mContext, e164Number, number)
+ || !FilteredNumberCompat.canAttemptBlockOperations(mContext)) {
+ return;
+ }
+ boolean isBlocked = blockId != null;
+ if (isBlocked) {
+ unblockView.setVisibility(View.VISIBLE);
+ } else {
+ if (isSpamFeatureEnabled) {
+ if (isSpam) {
+ blockView.setVisibility(View.VISIBLE);
+ reportNotSpamView.setVisibility(View.VISIBLE);
+ } else {
+ blockReportView.setVisibility(View.VISIBLE);
+ }
+ } else {
+ blockView.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ public interface OnClickListener {
+
+ void onBlockReportSpam(
+ String displayNumber,
+ String number,
+ String countryIso,
+ int callType,
+ int contactSourceType);
+
+ void onBlock(
+ String displayNumber,
+ String number,
+ String countryIso,
+ int callType,
+ int contactSourceType);
+
+ void onUnblock(
+ String displayNumber,
+ String number,
+ String countryIso,
+ int callType,
+ int contactSourceType,
+ boolean isSpam,
+ Integer blockId);
+
+ void onReportNotSpam(
+ String displayNumber,
+ String number,
+ String countryIso,
+ int callType,
+ int contactSourceType);
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallLogModalAlertManager.java b/java/com/android/dialer/app/calllog/CallLogModalAlertManager.java
new file mode 100644
index 000000000..9de260a0a
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallLogModalAlertManager.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 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.dialer.app.calllog;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.dialer.app.R;
+import com.android.dialer.app.alert.AlertManager;
+
+/**
+ * Alert manager controls modal view to show message in call log. When modal view is shown, regular
+ * call log will be hidden.
+ */
+public class CallLogModalAlertManager implements AlertManager {
+
+ interface Listener {
+ void onShowModalAlert(boolean show);
+ }
+
+ private final Listener listener;
+ private final ViewGroup parent;
+ private final ViewGroup container;
+ private final LayoutInflater inflater;
+
+ public CallLogModalAlertManager(LayoutInflater inflater, ViewGroup parent, Listener listener) {
+ this.inflater = inflater;
+ this.parent = parent;
+ this.listener = listener;
+ container = (ViewGroup) parent.findViewById(R.id.modal_message_container);
+ }
+
+ @Override
+ public View inflate(int layoutId) {
+ return inflater.inflate(layoutId, parent, false);
+ }
+
+ @Override
+ public void add(View view) {
+ if (contains(view)) {
+ return;
+ }
+ container.addView(view);
+ listener.onShowModalAlert(true);
+ }
+
+ @Override
+ public void clear() {
+ container.removeAllViews();
+ listener.onShowModalAlert(false);
+ }
+
+ public boolean isEmpty() {
+ return container.getChildCount() == 0;
+ }
+
+ public boolean contains(View view) {
+ return container.indexOfChild(view) != -1;
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallLogNotificationsHelper.java b/java/com/android/dialer/app/calllog/CallLogNotificationsHelper.java
new file mode 100644
index 000000000..8f664d1a4
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallLogNotificationsHelper.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2013 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.dialer.app.calllog;
+
+import android.Manifest;
+import android.annotation.TargetApi;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.provider.CallLog.Calls;
+import android.support.annotation.Nullable;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.contacts.common.GeoUtil;
+import com.android.dialer.app.R;
+import com.android.dialer.phonenumbercache.ContactInfo;
+import com.android.dialer.phonenumbercache.ContactInfoHelper;
+import com.android.dialer.telecom.TelecomUtil;
+import com.android.dialer.util.PermissionsUtil;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Helper class operating on call log notifications. */
+public class CallLogNotificationsHelper {
+
+ private static final String TAG = "CallLogNotifHelper";
+ private static CallLogNotificationsHelper sInstance;
+ private final Context mContext;
+ private final NewCallsQuery mNewCallsQuery;
+ private final ContactInfoHelper mContactInfoHelper;
+ private final String mCurrentCountryIso;
+
+ CallLogNotificationsHelper(
+ Context context,
+ NewCallsQuery newCallsQuery,
+ ContactInfoHelper contactInfoHelper,
+ String countryIso) {
+ mContext = context;
+ mNewCallsQuery = newCallsQuery;
+ mContactInfoHelper = contactInfoHelper;
+ mCurrentCountryIso = countryIso;
+ }
+
+ /** Returns the singleton instance of the {@link CallLogNotificationsHelper}. */
+ public static CallLogNotificationsHelper getInstance(Context context) {
+ if (sInstance == null) {
+ ContentResolver contentResolver = context.getContentResolver();
+ String countryIso = GeoUtil.getCurrentCountryIso(context);
+ sInstance =
+ new CallLogNotificationsHelper(
+ context,
+ createNewCallsQuery(context, contentResolver),
+ new ContactInfoHelper(context, countryIso),
+ countryIso);
+ }
+ return sInstance;
+ }
+
+ /** Removes the missed call notifications. */
+ public static void removeMissedCallNotifications(Context context) {
+ TelecomUtil.cancelMissedCallsNotification(context);
+ }
+
+ /** Update the voice mail notifications. */
+ public static void updateVoicemailNotifications(Context context) {
+ CallLogNotificationsService.updateVoicemailNotifications(context, null);
+ }
+
+ /** Create a new instance of {@link NewCallsQuery}. */
+ public static NewCallsQuery createNewCallsQuery(
+ Context context, ContentResolver contentResolver) {
+
+ return new DefaultNewCallsQuery(context.getApplicationContext(), contentResolver);
+ }
+
+ /**
+ * Get all voicemails with the "new" flag set to 1.
+ *
+ * @return A list of NewCall objects where each object represents a new voicemail.
+ */
+ @Nullable
+ public List<NewCall> getNewVoicemails() {
+ return mNewCallsQuery.query(Calls.VOICEMAIL_TYPE);
+ }
+
+ /**
+ * Get all missed calls with the "new" flag set to 1.
+ *
+ * @return A list of NewCall objects where each object represents a new missed call.
+ */
+ @Nullable
+ public List<NewCall> getNewMissedCalls() {
+ return mNewCallsQuery.query(Calls.MISSED_TYPE);
+ }
+
+ /**
+ * Given a number and number information (presentation and country ISO), get the best name for
+ * display. If the name is empty but we have a special presentation, display that. Otherwise
+ * attempt to look it up in the database or the cache. If that fails, fall back to displaying the
+ * number.
+ */
+ public String getName(
+ @Nullable String number, int numberPresentation, @Nullable String countryIso) {
+ return getContactInfo(number, numberPresentation, countryIso).name;
+ }
+
+ /**
+ * Given a number and number information (presentation and country ISO), get {@link ContactInfo}.
+ * If the name is empty but we have a special presentation, display that. Otherwise attempt to
+ * look it up in the cache. If that fails, fall back to displaying the number.
+ */
+ public ContactInfo getContactInfo(
+ @Nullable String number, int numberPresentation, @Nullable String countryIso) {
+ if (countryIso == null) {
+ countryIso = mCurrentCountryIso;
+ }
+
+ number = (number == null) ? "" : number;
+ ContactInfo contactInfo = new ContactInfo();
+ contactInfo.number = number;
+ contactInfo.formattedNumber = PhoneNumberUtils.formatNumber(number, countryIso);
+ // contactInfo.normalizedNumber is not PhoneNumberUtils.normalizeNumber. Read ContactInfo.
+ contactInfo.normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
+
+ // 1. Special number representation.
+ contactInfo.name =
+ PhoneNumberDisplayUtil.getDisplayName(mContext, number, numberPresentation, false)
+ .toString();
+ if (!TextUtils.isEmpty(contactInfo.name)) {
+ return contactInfo;
+ }
+
+ // 2. Look it up in the cache.
+ ContactInfo cachedContactInfo = mContactInfoHelper.lookupNumber(number, countryIso);
+
+ if (cachedContactInfo != null && !TextUtils.isEmpty(cachedContactInfo.name)) {
+ return cachedContactInfo;
+ }
+
+ if (!TextUtils.isEmpty(contactInfo.formattedNumber)) {
+ // 3. If we cannot lookup the contact, use the formatted number instead.
+ contactInfo.name = contactInfo.formattedNumber;
+ } else if (!TextUtils.isEmpty(number)) {
+ // 4. If number can't be formatted, use number.
+ contactInfo.name = number;
+ } else {
+ // 5. Otherwise, it's unknown number.
+ contactInfo.name = mContext.getResources().getString(R.string.unknown);
+ }
+ return contactInfo;
+ }
+
+ /** Allows determining the new calls for which a notification should be generated. */
+ public interface NewCallsQuery {
+
+ /** Returns the new calls of a certain type for which a notification should be generated. */
+ @Nullable
+ List<NewCall> query(int type);
+ }
+
+ /** Information about a new voicemail. */
+ public static final class NewCall {
+
+ public final Uri callsUri;
+ public final Uri voicemailUri;
+ public final String number;
+ public final int numberPresentation;
+ public final String accountComponentName;
+ public final String accountId;
+ public final String transcription;
+ public final String countryIso;
+ public final long dateMs;
+
+ public NewCall(
+ Uri callsUri,
+ Uri voicemailUri,
+ String number,
+ int numberPresentation,
+ String accountComponentName,
+ String accountId,
+ String transcription,
+ String countryIso,
+ long dateMs) {
+ this.callsUri = callsUri;
+ this.voicemailUri = voicemailUri;
+ this.number = number;
+ this.numberPresentation = numberPresentation;
+ this.accountComponentName = accountComponentName;
+ this.accountId = accountId;
+ this.transcription = transcription;
+ this.countryIso = countryIso;
+ this.dateMs = dateMs;
+ }
+ }
+
+ /**
+ * Default implementation of {@link NewCallsQuery} that looks up the list of new calls to notify
+ * about in the call log.
+ */
+ private static final class DefaultNewCallsQuery implements NewCallsQuery {
+
+ private static final String[] PROJECTION = {
+ Calls._ID,
+ Calls.NUMBER,
+ Calls.VOICEMAIL_URI,
+ Calls.NUMBER_PRESENTATION,
+ Calls.PHONE_ACCOUNT_COMPONENT_NAME,
+ Calls.PHONE_ACCOUNT_ID,
+ Calls.TRANSCRIPTION,
+ Calls.COUNTRY_ISO,
+ Calls.DATE
+ };
+ private static final int ID_COLUMN_INDEX = 0;
+ private static final int NUMBER_COLUMN_INDEX = 1;
+ private static final int VOICEMAIL_URI_COLUMN_INDEX = 2;
+ private static final int NUMBER_PRESENTATION_COLUMN_INDEX = 3;
+ private static final int PHONE_ACCOUNT_COMPONENT_NAME_COLUMN_INDEX = 4;
+ private static final int PHONE_ACCOUNT_ID_COLUMN_INDEX = 5;
+ private static final int TRANSCRIPTION_COLUMN_INDEX = 6;
+ private static final int COUNTRY_ISO_COLUMN_INDEX = 7;
+ private static final int DATE_COLUMN_INDEX = 8;
+
+ private final ContentResolver mContentResolver;
+ private final Context mContext;
+
+ private DefaultNewCallsQuery(Context context, ContentResolver contentResolver) {
+ mContext = context;
+ mContentResolver = contentResolver;
+ }
+
+ @Override
+ @Nullable
+ @TargetApi(VERSION_CODES.M)
+ public List<NewCall> query(int type) {
+ if (!PermissionsUtil.hasPermission(mContext, Manifest.permission.READ_CALL_LOG)) {
+ Log.w(TAG, "No READ_CALL_LOG permission, returning null for calls lookup.");
+ return null;
+ }
+ final String selection = String.format("%s = 1 AND %s = ?", Calls.NEW, Calls.TYPE);
+ final String[] selectionArgs = new String[] {Integer.toString(type)};
+ try (Cursor cursor =
+ mContentResolver.query(
+ Calls.CONTENT_URI_WITH_VOICEMAIL,
+ PROJECTION,
+ selection,
+ selectionArgs,
+ Calls.DEFAULT_SORT_ORDER)) {
+ if (cursor == null) {
+ return null;
+ }
+ List<NewCall> newCalls = new ArrayList<>();
+ while (cursor.moveToNext()) {
+ newCalls.add(createNewCallsFromCursor(cursor));
+ }
+ return newCalls;
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Exception when querying Contacts Provider for calls lookup");
+ return null;
+ }
+ }
+
+ /** Returns an instance of {@link NewCall} created by using the values of the cursor. */
+ private NewCall createNewCallsFromCursor(Cursor cursor) {
+ String voicemailUriString = cursor.getString(VOICEMAIL_URI_COLUMN_INDEX);
+ Uri callsUri =
+ ContentUris.withAppendedId(
+ Calls.CONTENT_URI_WITH_VOICEMAIL, cursor.getLong(ID_COLUMN_INDEX));
+ Uri voicemailUri = voicemailUriString == null ? null : Uri.parse(voicemailUriString);
+ return new NewCall(
+ callsUri,
+ voicemailUri,
+ cursor.getString(NUMBER_COLUMN_INDEX),
+ cursor.getInt(NUMBER_PRESENTATION_COLUMN_INDEX),
+ cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME_COLUMN_INDEX),
+ cursor.getString(PHONE_ACCOUNT_ID_COLUMN_INDEX),
+ cursor.getString(TRANSCRIPTION_COLUMN_INDEX),
+ cursor.getString(COUNTRY_ISO_COLUMN_INDEX),
+ cursor.getLong(DATE_COLUMN_INDEX));
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallLogNotificationsService.java b/java/com/android/dialer/app/calllog/CallLogNotificationsService.java
new file mode 100644
index 000000000..820528126
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallLogNotificationsService.java
@@ -0,0 +1,203 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.telecom.TelecomUtil;
+import com.android.dialer.util.PermissionsUtil;
+import me.leolin.shortcutbadger.ShortcutBadger;
+
+/**
+ * Provides operations for managing call-related notifications.
+ *
+ * <p>It handles the following actions:
+ *
+ * <ul>
+ * <li>Updating voicemail notifications
+ * <li>Marking new voicemails as old
+ * <li>Updating missed call notifications
+ * <li>Marking new missed calls as old
+ * <li>Calling back from a missed call
+ * <li>Sending an SMS from a missed call
+ * </ul>
+ */
+public class CallLogNotificationsService extends IntentService {
+
+ /** Action to mark all the new voicemails as old. */
+ public static final String ACTION_MARK_NEW_VOICEMAILS_AS_OLD =
+ "com.android.dialer.calllog.ACTION_MARK_NEW_VOICEMAILS_AS_OLD";
+ /**
+ * Action to update voicemail notifications.
+ *
+ * <p>May include an optional extra {@link #EXTRA_NEW_VOICEMAIL_URI}.
+ */
+ public static final String ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS =
+ "com.android.dialer.calllog.UPDATE_VOICEMAIL_NOTIFICATIONS";
+ /**
+ * Extra to included with {@link #ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS} to identify the new
+ * voicemail that triggered an update.
+ *
+ * <p>It must be a {@link Uri}.
+ */
+ public static final String EXTRA_NEW_VOICEMAIL_URI = "NEW_VOICEMAIL_URI";
+ /**
+ * Action to update the missed call notifications.
+ *
+ * <p>Includes optional extras {@link #EXTRA_MISSED_CALL_NUMBER} and {@link
+ * #EXTRA_MISSED_CALL_COUNT}.
+ */
+ public static final String ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS =
+ "com.android.dialer.calllog.UPDATE_MISSED_CALL_NOTIFICATIONS";
+ /** Action to mark all the new missed calls as old. */
+ public static final String ACTION_MARK_NEW_MISSED_CALLS_AS_OLD =
+ "com.android.dialer.calllog.ACTION_MARK_NEW_MISSED_CALLS_AS_OLD";
+ /** Action to call back a missed call. */
+ public static final String ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION =
+ "com.android.dialer.calllog.CALL_BACK_FROM_MISSED_CALL_NOTIFICATION";
+
+ public static final String ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION =
+ "com.android.dialer.calllog.SEND_SMS_FROM_MISSED_CALL_NOTIFICATION";
+ /**
+ * Extra to be included with {@link #ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS}, {@link
+ * #ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION} and {@link
+ * #ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION} to identify the number to display, call or
+ * text back.
+ *
+ * <p>It must be a {@link String}.
+ */
+ public static final String EXTRA_MISSED_CALL_NUMBER = "MISSED_CALL_NUMBER";
+ /**
+ * Extra to be included with {@link #ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS} to represent the
+ * number of missed calls.
+ *
+ * <p>It must be a {@link Integer}
+ */
+ public static final String EXTRA_MISSED_CALL_COUNT = "MISSED_CALL_COUNT";
+
+ public static final int UNKNOWN_MISSED_CALL_COUNT = -1;
+ private VoicemailQueryHandler mVoicemailQueryHandler;
+
+ public CallLogNotificationsService() {
+ super("CallLogNotificationsService");
+ }
+
+ /**
+ * Updates notifications for any new voicemails.
+ *
+ * @param context a valid context.
+ * @param voicemailUri The uri pointing to the voicemail to update the notification for. If {@code
+ * null}, then notifications for all new voicemails will be updated.
+ */
+ public static void updateVoicemailNotifications(Context context, Uri voicemailUri) {
+ if (!TelecomUtil.isDefaultDialer(context)) {
+ LogUtil.i(
+ "CallLogNotificationsService.updateVoicemailNotifications",
+ "not default dialer, ignoring voicemail notifications");
+ return;
+ }
+ if (TelecomUtil.hasReadWriteVoicemailPermissions(context)) {
+ Intent serviceIntent = new Intent(context, CallLogNotificationsService.class);
+ serviceIntent.setAction(CallLogNotificationsService.ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS);
+ // If voicemailUri is null, then notifications for all voicemails will be updated.
+ if (voicemailUri != null) {
+ serviceIntent.putExtra(CallLogNotificationsService.EXTRA_NEW_VOICEMAIL_URI, voicemailUri);
+ }
+ context.startService(serviceIntent);
+ }
+ }
+
+ /**
+ * Updates notifications for any new missed calls.
+ *
+ * @param context A valid context.
+ * @param count The number of new missed calls.
+ * @param number The phone number of the newest missed call.
+ */
+ public static void updateMissedCallNotifications(Context context, int count, String number) {
+ Intent serviceIntent = new Intent(context, CallLogNotificationsService.class);
+ serviceIntent.setAction(CallLogNotificationsService.ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS);
+ serviceIntent.putExtra(EXTRA_MISSED_CALL_COUNT, count);
+ serviceIntent.putExtra(EXTRA_MISSED_CALL_NUMBER, number);
+ context.startService(serviceIntent);
+ }
+
+ public static void markNewVoicemailsAsOld(Context context) {
+ Intent serviceIntent = new Intent(context, CallLogNotificationsService.class);
+ serviceIntent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_VOICEMAILS_AS_OLD);
+ context.startService(serviceIntent);
+ }
+
+ public static boolean updateBadgeCount(Context context, int count) {
+ boolean success = ShortcutBadger.applyCount(context, count);
+ LogUtil.i(
+ "CallLogNotificationsService.updateBadgeCount",
+ "update badge count: %d success: %b",
+ count,
+ success);
+ return success;
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ if (intent == null) {
+ LogUtil.d("CallLogNotificationsService.onHandleIntent", "could not handle null intent");
+ return;
+ }
+
+ if (!PermissionsUtil.hasPermission(this, android.Manifest.permission.READ_CALL_LOG)) {
+ return;
+ }
+
+ String action = intent.getAction();
+ switch (action) {
+ case ACTION_MARK_NEW_VOICEMAILS_AS_OLD:
+ if (mVoicemailQueryHandler == null) {
+ mVoicemailQueryHandler = new VoicemailQueryHandler(this, getContentResolver());
+ }
+ mVoicemailQueryHandler.markNewVoicemailsAsOld();
+ break;
+ case ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS:
+ Uri voicemailUri = intent.getParcelableExtra(EXTRA_NEW_VOICEMAIL_URI);
+ DefaultVoicemailNotifier.getInstance(this).updateNotification(voicemailUri);
+ break;
+ case ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS:
+ int count = intent.getIntExtra(EXTRA_MISSED_CALL_COUNT, UNKNOWN_MISSED_CALL_COUNT);
+ String number = intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER);
+ MissedCallNotifier.getInstance(this).updateMissedCallNotification(count, number);
+ updateBadgeCount(this, count);
+ break;
+ case ACTION_MARK_NEW_MISSED_CALLS_AS_OLD:
+ CallLogNotificationsHelper.removeMissedCallNotifications(this);
+ break;
+ case ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION:
+ MissedCallNotifier.getInstance(this)
+ .callBackFromMissedCall(intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER));
+ break;
+ case ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION:
+ MissedCallNotifier.getInstance(this)
+ .sendSmsFromMissedCall(intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER));
+ break;
+ default:
+ LogUtil.d("CallLogNotificationsService.onHandleIntent", "could not handle: " + intent);
+ break;
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallLogReceiver.java b/java/com/android/dialer/app/calllog/CallLogReceiver.java
new file mode 100644
index 000000000..a781b0887
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallLogReceiver.java
@@ -0,0 +1,77 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.provider.VoicemailContract;
+import com.android.dialer.app.voicemail.error.VoicemailStatusCorruptionHandler;
+import com.android.dialer.app.voicemail.error.VoicemailStatusCorruptionHandler.Source;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.database.CallLogQueryHandler;
+
+/**
+ * Receiver for call log events.
+ *
+ * <p>It is currently used to handle {@link VoicemailContract#ACTION_NEW_VOICEMAIL} and {@link
+ * Intent#ACTION_BOOT_COMPLETED}.
+ */
+public class CallLogReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (VoicemailContract.ACTION_NEW_VOICEMAIL.equals(intent.getAction())) {
+ checkVoicemailStatus(context);
+ CallLogNotificationsService.updateVoicemailNotifications(context, intent.getData());
+ } else if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
+ CallLogNotificationsService.updateVoicemailNotifications(context, null);
+ } else {
+ LogUtil.w("CallLogReceiver.onReceive", "could not handle: " + intent);
+ }
+ }
+
+ private static void checkVoicemailStatus(Context context) {
+ new CallLogQueryHandler(
+ context,
+ context.getContentResolver(),
+ new CallLogQueryHandler.Listener() {
+ @Override
+ public void onVoicemailStatusFetched(Cursor statusCursor) {
+ VoicemailStatusCorruptionHandler.maybeFixVoicemailStatus(
+ context, statusCursor, Source.Notification);
+ }
+
+ @Override
+ public void onVoicemailUnreadCountFetched(Cursor cursor) {
+ // Do nothing
+ }
+
+ @Override
+ public void onMissedCallsUnreadCountFetched(Cursor cursor) {
+ // Do nothing
+ }
+
+ @Override
+ public boolean onCallsFetched(Cursor combinedCursor) {
+ return false;
+ }
+ })
+ .fetchVoicemailStatus();
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallTypeHelper.java b/java/com/android/dialer/app/calllog/CallTypeHelper.java
new file mode 100644
index 000000000..f3c27a1ac
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallTypeHelper.java
@@ -0,0 +1,136 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.content.res.Resources;
+import com.android.dialer.app.R;
+import com.android.dialer.compat.AppCompatConstants;
+
+/** Helper class to perform operations related to call types. */
+public class CallTypeHelper {
+
+ /** Name used to identify incoming calls. */
+ private final CharSequence mIncomingName;
+ /** Name used to identify incoming calls which were transferred to another device. */
+ private final CharSequence mIncomingPulledName;
+ /** Name used to identify outgoing calls. */
+ private final CharSequence mOutgoingName;
+ /** Name used to identify outgoing calls which were transferred to another device. */
+ private final CharSequence mOutgoingPulledName;
+ /** Name used to identify missed calls. */
+ private final CharSequence mMissedName;
+ /** Name used to identify incoming video calls. */
+ private final CharSequence mIncomingVideoName;
+ /** Name used to identify incoming video calls which were transferred to another device. */
+ private final CharSequence mIncomingVideoPulledName;
+ /** Name used to identify outgoing video calls. */
+ private final CharSequence mOutgoingVideoName;
+ /** Name used to identify outgoing video calls which were transferred to another device. */
+ private final CharSequence mOutgoingVideoPulledName;
+ /** Name used to identify missed video calls. */
+ private final CharSequence mMissedVideoName;
+ /** Name used to identify voicemail calls. */
+ private final CharSequence mVoicemailName;
+ /** Name used to identify rejected calls. */
+ private final CharSequence mRejectedName;
+ /** Name used to identify blocked calls. */
+ private final CharSequence mBlockedName;
+ /** Name used to identify calls which were answered on another device. */
+ private final CharSequence mAnsweredElsewhereName;
+
+ public CallTypeHelper(Resources resources) {
+ // Cache these values so that we do not need to look them up each time.
+ mIncomingName = resources.getString(R.string.type_incoming);
+ mIncomingPulledName = resources.getString(R.string.type_incoming_pulled);
+ mOutgoingName = resources.getString(R.string.type_outgoing);
+ mOutgoingPulledName = resources.getString(R.string.type_outgoing_pulled);
+ mMissedName = resources.getString(R.string.type_missed);
+ mIncomingVideoName = resources.getString(R.string.type_incoming_video);
+ mIncomingVideoPulledName = resources.getString(R.string.type_incoming_video_pulled);
+ mOutgoingVideoName = resources.getString(R.string.type_outgoing_video);
+ mOutgoingVideoPulledName = resources.getString(R.string.type_outgoing_video_pulled);
+ mMissedVideoName = resources.getString(R.string.type_missed_video);
+ mVoicemailName = resources.getString(R.string.type_voicemail);
+ mRejectedName = resources.getString(R.string.type_rejected);
+ mBlockedName = resources.getString(R.string.type_blocked);
+ mAnsweredElsewhereName = resources.getString(R.string.type_answered_elsewhere);
+ }
+
+ public static boolean isMissedCallType(int callType) {
+ return (callType != AppCompatConstants.CALLS_INCOMING_TYPE
+ && callType != AppCompatConstants.CALLS_OUTGOING_TYPE
+ && callType != AppCompatConstants.CALLS_VOICEMAIL_TYPE
+ && callType != AppCompatConstants.CALLS_ANSWERED_EXTERNALLY_TYPE);
+ }
+
+ /** Returns the text used to represent the given call type. */
+ public CharSequence getCallTypeText(int callType, boolean isVideoCall, boolean isPulledCall) {
+ switch (callType) {
+ case AppCompatConstants.CALLS_INCOMING_TYPE:
+ if (isVideoCall) {
+ if (isPulledCall) {
+ return mIncomingVideoPulledName;
+ } else {
+ return mIncomingVideoName;
+ }
+ } else {
+ if (isPulledCall) {
+ return mIncomingPulledName;
+ } else {
+ return mIncomingName;
+ }
+ }
+
+ case AppCompatConstants.CALLS_OUTGOING_TYPE:
+ if (isVideoCall) {
+ if (isPulledCall) {
+ return mOutgoingVideoPulledName;
+ } else {
+ return mOutgoingVideoName;
+ }
+ } else {
+ if (isPulledCall) {
+ return mOutgoingPulledName;
+ } else {
+ return mOutgoingName;
+ }
+ }
+
+ case AppCompatConstants.CALLS_MISSED_TYPE:
+ if (isVideoCall) {
+ return mMissedVideoName;
+ } else {
+ return mMissedName;
+ }
+
+ case AppCompatConstants.CALLS_VOICEMAIL_TYPE:
+ return mVoicemailName;
+
+ case AppCompatConstants.CALLS_REJECTED_TYPE:
+ return mRejectedName;
+
+ case AppCompatConstants.CALLS_BLOCKED_TYPE:
+ return mBlockedName;
+
+ case AppCompatConstants.CALLS_ANSWERED_EXTERNALLY_TYPE:
+ return mAnsweredElsewhereName;
+
+ default:
+ return mMissedName;
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallTypeIconsView.java b/java/com/android/dialer/app/calllog/CallTypeIconsView.java
new file mode 100644
index 000000000..cd5c5460c
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallTypeIconsView.java
@@ -0,0 +1,221 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import com.android.contacts.common.util.BitmapUtil;
+import com.android.dialer.app.R;
+import com.android.dialer.compat.AppCompatConstants;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * View that draws one or more symbols for different types of calls (missed calls, outgoing etc).
+ * The symbols are set up horizontally. As this view doesn't create subviews, it is better suited
+ * for ListView-recycling that a regular LinearLayout using ImageViews.
+ */
+public class CallTypeIconsView extends View {
+
+ private static Resources sResources;
+ private List<Integer> mCallTypes = new ArrayList<>(3);
+ private boolean mShowVideo = false;
+ private int mWidth;
+ private int mHeight;
+
+ public CallTypeIconsView(Context context) {
+ this(context, null);
+ }
+
+ public CallTypeIconsView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ if (sResources == null) {
+ sResources = new Resources(context);
+ }
+ }
+
+ public void clear() {
+ mCallTypes.clear();
+ mWidth = 0;
+ mHeight = 0;
+ invalidate();
+ }
+
+ public void add(int callType) {
+ mCallTypes.add(callType);
+
+ final Drawable drawable = getCallTypeDrawable(callType);
+ mWidth += drawable.getIntrinsicWidth() + sResources.iconMargin;
+ mHeight = Math.max(mHeight, drawable.getIntrinsicHeight());
+ invalidate();
+ }
+
+ /**
+ * Determines whether the video call icon will be shown.
+ *
+ * @param showVideo True where the video icon should be shown.
+ */
+ public void setShowVideo(boolean showVideo) {
+ mShowVideo = showVideo;
+ if (showVideo) {
+ mWidth += sResources.videoCall.getIntrinsicWidth();
+ mHeight = Math.max(mHeight, sResources.videoCall.getIntrinsicHeight());
+ invalidate();
+ }
+ }
+
+ /**
+ * Determines if the video icon should be shown.
+ *
+ * @return True if the video icon should be shown.
+ */
+ public boolean isVideoShown() {
+ return mShowVideo;
+ }
+
+ public int getCount() {
+ return mCallTypes.size();
+ }
+
+ public int getCallType(int index) {
+ return mCallTypes.get(index);
+ }
+
+ private Drawable getCallTypeDrawable(int callType) {
+ switch (callType) {
+ case AppCompatConstants.CALLS_INCOMING_TYPE:
+ case AppCompatConstants.CALLS_ANSWERED_EXTERNALLY_TYPE:
+ return sResources.incoming;
+ case AppCompatConstants.CALLS_OUTGOING_TYPE:
+ return sResources.outgoing;
+ case AppCompatConstants.CALLS_MISSED_TYPE:
+ return sResources.missed;
+ case AppCompatConstants.CALLS_VOICEMAIL_TYPE:
+ return sResources.voicemail;
+ case AppCompatConstants.CALLS_BLOCKED_TYPE:
+ return sResources.blocked;
+ default:
+ // It is possible for users to end up with calls with unknown call types in their
+ // call history, possibly due to 3rd party call log implementations (e.g. to
+ // distinguish between rejected and missed calls). Instead of crashing, just
+ // assume that all unknown call types are missed calls.
+ return sResources.missed;
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(mWidth, mHeight);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ int left = 0;
+ for (Integer callType : mCallTypes) {
+ final Drawable drawable = getCallTypeDrawable(callType);
+ final int right = left + drawable.getIntrinsicWidth();
+ drawable.setBounds(left, 0, right, drawable.getIntrinsicHeight());
+ drawable.draw(canvas);
+ left = right + sResources.iconMargin;
+ }
+
+ // If showing the video call icon, draw it scaled appropriately.
+ if (mShowVideo) {
+ final Drawable drawable = sResources.videoCall;
+ final int right = left + sResources.videoCall.getIntrinsicWidth();
+ drawable.setBounds(left, 0, right, sResources.videoCall.getIntrinsicHeight());
+ drawable.draw(canvas);
+ }
+ }
+
+ private static class Resources {
+
+ // Drawable representing an incoming answered call.
+ public final Drawable incoming;
+
+ // Drawable respresenting an outgoing call.
+ public final Drawable outgoing;
+
+ // Drawable representing an incoming missed call.
+ public final Drawable missed;
+
+ // Drawable representing a voicemail.
+ public final Drawable voicemail;
+
+ // Drawable representing a blocked call.
+ public final Drawable blocked;
+
+ // Drawable repesenting a video call.
+ public final Drawable videoCall;
+
+ /** The margin to use for icons. */
+ public final int iconMargin;
+
+ /**
+ * Configures the call icon drawables. A single white call arrow which points down and left is
+ * used as a basis for all of the call arrow icons, applying rotation and colors as needed.
+ *
+ * @param context The current context.
+ */
+ public Resources(Context context) {
+ final android.content.res.Resources r = context.getResources();
+
+ incoming = r.getDrawable(R.drawable.ic_call_arrow);
+ incoming.setColorFilter(r.getColor(R.color.answered_call), PorterDuff.Mode.MULTIPLY);
+
+ // Create a rotated instance of the call arrow for outgoing calls.
+ outgoing = BitmapUtil.getRotatedDrawable(r, R.drawable.ic_call_arrow, 180f);
+ outgoing.setColorFilter(r.getColor(R.color.answered_call), PorterDuff.Mode.MULTIPLY);
+
+ // Need to make a copy of the arrow drawable, otherwise the same instance colored
+ // above will be recolored here.
+ missed = r.getDrawable(R.drawable.ic_call_arrow).mutate();
+ missed.setColorFilter(r.getColor(R.color.missed_call), PorterDuff.Mode.MULTIPLY);
+
+ voicemail = r.getDrawable(R.drawable.quantum_ic_voicemail_white_18);
+ voicemail.setColorFilter(
+ r.getColor(R.color.dialer_secondary_text_color), PorterDuff.Mode.MULTIPLY);
+
+ blocked = getScaledBitmap(context, R.drawable.ic_block_24dp);
+ blocked.setColorFilter(r.getColor(R.color.blocked_call), PorterDuff.Mode.MULTIPLY);
+
+ videoCall = getScaledBitmap(context, R.drawable.quantum_ic_videocam_white_24);
+ videoCall.setColorFilter(
+ r.getColor(R.color.dialer_secondary_text_color), PorterDuff.Mode.MULTIPLY);
+
+ iconMargin = r.getDimensionPixelSize(R.dimen.call_log_icon_margin);
+ }
+
+ // Gets the icon, scaled to the height of the call type icons. This helps display all the
+ // icons to be the same height, while preserving their width aspect ratio.
+ private Drawable getScaledBitmap(Context context, int resourceId) {
+ Bitmap icon = BitmapFactory.decodeResource(context.getResources(), resourceId);
+ int scaledHeight = context.getResources().getDimensionPixelSize(R.dimen.call_type_icon_size);
+ int scaledWidth =
+ (int) ((float) icon.getWidth() * ((float) scaledHeight / (float) icon.getHeight()));
+ Bitmap scaledIcon = Bitmap.createScaledBitmap(icon, scaledWidth, scaledHeight, false);
+ return new BitmapDrawable(context.getResources(), scaledIcon);
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/ClearCallLogDialog.java b/java/com/android/dialer/app/calllog/ClearCallLogDialog.java
new file mode 100644
index 000000000..0c9bd4b35
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/ClearCallLogDialog.java
@@ -0,0 +1,98 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.app.ProgressDialog;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.CallLog.Calls;
+import com.android.dialer.app.R;
+import com.android.dialer.phonenumbercache.CachedNumberLookupService;
+import com.android.dialer.phonenumbercache.PhoneNumberCache;
+
+/** Dialog that clears the call log after confirming with the user */
+public class ClearCallLogDialog extends DialogFragment {
+
+ /** Preferred way to show this dialog */
+ public static void show(FragmentManager fragmentManager) {
+ ClearCallLogDialog dialog = new ClearCallLogDialog();
+ dialog.show(fragmentManager, "deleteCallLog");
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final ContentResolver resolver = getActivity().getContentResolver();
+ final Context context = getActivity().getApplicationContext();
+ final OnClickListener okListener =
+ new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final ProgressDialog progressDialog =
+ ProgressDialog.show(
+ getActivity(), getString(R.string.clearCallLogProgress_title), "", true, false);
+ progressDialog.setOwnerActivity(getActivity());
+ final AsyncTask<Void, Void, Void> task =
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ resolver.delete(Calls.CONTENT_URI, null, null);
+ CachedNumberLookupService cachedNumberLookupService =
+ PhoneNumberCache.get(context).getCachedNumberLookupService();
+ if (cachedNumberLookupService != null) {
+ cachedNumberLookupService.clearAllCacheEntries(context);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ final Activity activity = progressDialog.getOwnerActivity();
+
+ if (activity == null || activity.isDestroyed() || activity.isFinishing()) {
+ return;
+ }
+
+ if (progressDialog != null && progressDialog.isShowing()) {
+ progressDialog.dismiss();
+ }
+ }
+ };
+ // TODO: Once we have the API, we should configure this ProgressDialog
+ // to only show up after a certain time (e.g. 150ms)
+ progressDialog.show();
+ task.execute();
+ }
+ };
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.clearCallLogConfirmation_title)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setMessage(R.string.clearCallLogConfirmation)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, okListener)
+ .setCancelable(true)
+ .create();
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/DefaultVoicemailNotifier.java b/java/com/android/dialer/app/calllog/DefaultVoicemailNotifier.java
new file mode 100644
index 000000000..651a0ccb8
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/DefaultVoicemailNotifier.java
@@ -0,0 +1,273 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.Nullable;
+import android.support.v4.util.Pair;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import com.android.contacts.common.compat.TelephonyManagerCompat;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.dialer.app.DialtactsActivity;
+import com.android.dialer.app.R;
+import com.android.dialer.app.calllog.CallLogNotificationsHelper.NewCall;
+import com.android.dialer.app.list.ListsFragment;
+import com.android.dialer.blocking.FilteredNumbersUtil;
+import com.android.dialer.telecom.TelecomUtil;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/** Shows a voicemail notification in the status bar. */
+public class DefaultVoicemailNotifier {
+
+ public static final String TAG = "VoicemailNotifier";
+
+ /** The tag used to identify notifications from this class. */
+ private static final String NOTIFICATION_TAG = "DefaultVoicemailNotifier";
+ /** The identifier of the notification of new voicemails. */
+ private static final int NOTIFICATION_ID = 1;
+
+ /** The singleton instance of {@link DefaultVoicemailNotifier}. */
+ private static DefaultVoicemailNotifier sInstance;
+
+ private final Context mContext;
+
+ private DefaultVoicemailNotifier(Context context) {
+ mContext = context;
+ }
+
+ /** Returns the singleton instance of the {@link DefaultVoicemailNotifier}. */
+ public static DefaultVoicemailNotifier getInstance(Context context) {
+ if (sInstance == null) {
+ ContentResolver contentResolver = context.getContentResolver();
+ sInstance = new DefaultVoicemailNotifier(context);
+ }
+ return sInstance;
+ }
+
+ /**
+ * Updates the notification and notifies of the call with the given URI.
+ *
+ * <p>Clears the notification if there are no new voicemails, and notifies if the given URI
+ * corresponds to a new voicemail.
+ *
+ * <p>It is not safe to call this method from the main thread.
+ */
+ public void updateNotification(Uri newCallUri) {
+ // Lookup the list of new voicemails to include in the notification.
+ // TODO: Move this into a service, to avoid holding the receiver up.
+ final List<NewCall> newCalls =
+ CallLogNotificationsHelper.getInstance(mContext).getNewVoicemails();
+
+ if (newCalls == null) {
+ // Query failed, just return.
+ return;
+ }
+
+ if (newCalls.isEmpty()) {
+ // No voicemails to notify about: clear the notification.
+ getNotificationManager().cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
+ return;
+ }
+
+ Resources resources = mContext.getResources();
+
+ // This represents a list of names to include in the notification.
+ String callers = null;
+
+ // Maps each number into a name: if a number is in the map, it has already left a more
+ // recent voicemail.
+ final Map<String, String> names = new ArrayMap<>();
+
+ // Determine the call corresponding to the new voicemail we have to notify about.
+ NewCall callToNotify = null;
+
+ // Iterate over the new voicemails to determine all the information above.
+ Iterator<NewCall> itr = newCalls.iterator();
+ while (itr.hasNext()) {
+ NewCall newCall = itr.next();
+
+ // Skip notifying for numbers which are blocked.
+ if (FilteredNumbersUtil.shouldBlockVoicemail(
+ mContext, newCall.number, newCall.countryIso, newCall.dateMs)) {
+ itr.remove();
+
+ // Delete the voicemail.
+ mContext.getContentResolver().delete(newCall.voicemailUri, null, null);
+ continue;
+ }
+
+ // Check if we already know the name associated with this number.
+ String name = names.get(newCall.number);
+ if (name == null) {
+ name =
+ CallLogNotificationsHelper.getInstance(mContext)
+ .getName(newCall.number, newCall.numberPresentation, newCall.countryIso);
+ names.put(newCall.number, name);
+ // This is a new caller. Add it to the back of the list of callers.
+ if (TextUtils.isEmpty(callers)) {
+ callers = name;
+ } else {
+ callers =
+ resources.getString(R.string.notification_voicemail_callers_list, callers, name);
+ }
+ }
+ // Check if this is the new call we need to notify about.
+ if (newCallUri != null
+ && newCall.voicemailUri != null
+ && ContentUris.parseId(newCallUri) == ContentUris.parseId(newCall.voicemailUri)) {
+ callToNotify = newCall;
+ }
+ }
+
+ // All the potential new voicemails have been removed, e.g. if they were spam.
+ if (newCalls.isEmpty()) {
+ return;
+ }
+
+ // If there is only one voicemail, set its transcription as the "long text".
+ String transcription = null;
+ if (newCalls.size() == 1) {
+ transcription = newCalls.get(0).transcription;
+ }
+
+ if (newCallUri != null && callToNotify == null) {
+ Log.e(TAG, "The new call could not be found in the call log: " + newCallUri);
+ }
+
+ // Determine the title of the notification and the icon for it.
+ final String title =
+ resources.getQuantityString(
+ R.plurals.notification_voicemail_title, newCalls.size(), newCalls.size());
+ // TODO: Use the photo of contact if all calls are from the same person.
+ final int icon = android.R.drawable.stat_notify_voicemail;
+
+ Pair<Uri, Integer> info = getNotificationInfo(callToNotify);
+
+ Notification.Builder notificationBuilder =
+ new Notification.Builder(mContext)
+ .setSmallIcon(icon)
+ .setContentTitle(title)
+ .setContentText(callers)
+ .setColor(resources.getColor(R.color.dialer_theme_color))
+ .setSound(info.first)
+ .setDefaults(info.second)
+ .setDeleteIntent(createMarkNewVoicemailsAsOldIntent())
+ .setAutoCancel(true);
+
+ if (!TextUtils.isEmpty(transcription)) {
+ notificationBuilder.setStyle(new Notification.BigTextStyle().bigText(transcription));
+ }
+
+ // Determine the intent to fire when the notification is clicked on.
+ final Intent contentIntent;
+ // Open the call log.
+ contentIntent = DialtactsActivity.getShowTabIntent(mContext, ListsFragment.TAB_INDEX_VOICEMAIL);
+ contentIntent.putExtra(DialtactsActivity.EXTRA_CLEAR_NEW_VOICEMAILS, true);
+ notificationBuilder.setContentIntent(
+ PendingIntent.getActivity(mContext, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+
+ // The text to show in the ticker, describing the new event.
+ if (callToNotify != null) {
+ CharSequence msg =
+ ContactDisplayUtils.getTtsSpannedPhoneNumber(
+ resources,
+ R.string.notification_new_voicemail_ticker,
+ names.get(callToNotify.number));
+ notificationBuilder.setTicker(msg);
+ }
+ Log.i(TAG, "Creating voicemail notification");
+ getNotificationManager().notify(NOTIFICATION_TAG, NOTIFICATION_ID, notificationBuilder.build());
+ }
+
+ /**
+ * Determines which ringtone Uri and Notification defaults to use when updating the notification
+ * for the given call.
+ */
+ private Pair<Uri, Integer> getNotificationInfo(@Nullable NewCall callToNotify) {
+ Log.v(TAG, "getNotificationInfo");
+ if (callToNotify == null) {
+ Log.i(TAG, "callToNotify == null");
+ return new Pair<>(null, 0);
+ }
+ PhoneAccountHandle accountHandle;
+ if (callToNotify.accountComponentName == null || callToNotify.accountId == null) {
+ Log.v(TAG, "accountComponentName == null || callToNotify.accountId == null");
+ accountHandle = TelecomUtil.getDefaultOutgoingPhoneAccount(mContext, PhoneAccount.SCHEME_TEL);
+ if (accountHandle == null) {
+ Log.i(TAG, "No default phone account found, using default notification ringtone");
+ return new Pair<>(null, Notification.DEFAULT_ALL);
+ }
+
+ } else {
+ accountHandle =
+ new PhoneAccountHandle(
+ ComponentName.unflattenFromString(callToNotify.accountComponentName),
+ callToNotify.accountId);
+ }
+ if (accountHandle.getComponentName() != null) {
+ Log.v(TAG, "PhoneAccountHandle.ComponentInfo:" + accountHandle.getComponentName());
+ } else {
+ Log.i(TAG, "PhoneAccountHandle.ComponentInfo: null");
+ }
+ return new Pair<>(
+ TelephonyManagerCompat.getVoicemailRingtoneUri(getTelephonyManager(), accountHandle),
+ getNotificationDefaults(accountHandle));
+ }
+
+ private int getNotificationDefaults(PhoneAccountHandle accountHandle) {
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ return TelephonyManagerCompat.isVoicemailVibrationEnabled(
+ getTelephonyManager(), accountHandle)
+ ? Notification.DEFAULT_VIBRATE
+ : 0;
+ }
+ return Notification.DEFAULT_ALL;
+ }
+
+ /** Creates a pending intent that marks all new voicemails as old. */
+ private PendingIntent createMarkNewVoicemailsAsOldIntent() {
+ Intent intent = new Intent(mContext, CallLogNotificationsService.class);
+ intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_VOICEMAILS_AS_OLD);
+ return PendingIntent.getService(mContext, 0, intent, 0);
+ }
+
+ private NotificationManager getNotificationManager() {
+ return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ private TelephonyManager getTelephonyManager() {
+ return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/GroupingListAdapter.java b/java/com/android/dialer/app/calllog/GroupingListAdapter.java
new file mode 100644
index 000000000..d1157206f
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/GroupingListAdapter.java
@@ -0,0 +1,153 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.os.Handler;
+import android.support.v7.widget.RecyclerView;
+import android.util.SparseIntArray;
+
+/**
+ * Maintains a list that groups items into groups of consecutive elements which are disjoint, that
+ * is, an item can only belong to one group. This is leveraged for grouping calls in the call log
+ * received from or made to the same phone number.
+ *
+ * <p>There are two integers stored as metadata for every list item in the adapter.
+ */
+abstract class GroupingListAdapter extends RecyclerView.Adapter {
+
+ protected ContentObserver mChangeObserver =
+ new ContentObserver(new Handler()) {
+ @Override
+ public boolean deliverSelfNotifications() {
+ return true;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ onContentChanged();
+ }
+ };
+ protected DataSetObserver mDataSetObserver =
+ new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ notifyDataSetChanged();
+ }
+ };
+ private Cursor mCursor;
+ /**
+ * SparseIntArray, which maps the cursor position of the first element of a group to the size of
+ * the group. The index of a key in this map corresponds to the list position of that group.
+ */
+ private SparseIntArray mGroupMetadata;
+
+ private int mItemCount;
+
+ public GroupingListAdapter() {
+ reset();
+ }
+
+ /**
+ * Finds all groups of adjacent items in the cursor and calls {@link #addGroup} for each of them.
+ */
+ protected abstract void addGroups(Cursor cursor);
+
+ protected abstract void onContentChanged();
+
+ public void changeCursor(Cursor cursor) {
+ if (cursor == mCursor) {
+ return;
+ }
+
+ if (mCursor != null) {
+ mCursor.unregisterContentObserver(mChangeObserver);
+ mCursor.unregisterDataSetObserver(mDataSetObserver);
+ mCursor.close();
+ }
+
+ // Reset whenever the cursor is changed.
+ reset();
+ mCursor = cursor;
+
+ if (cursor != null) {
+ addGroups(mCursor);
+
+ // Calculate the item count by subtracting group child counts from the cursor count.
+ mItemCount = mGroupMetadata.size();
+
+ cursor.registerContentObserver(mChangeObserver);
+ cursor.registerDataSetObserver(mDataSetObserver);
+ notifyDataSetChanged();
+ }
+ }
+
+ /**
+ * Records information about grouping in the list. Should be called by the overridden {@link
+ * #addGroups} method.
+ */
+ public void addGroup(int cursorPosition, int groupSize) {
+ int lastIndex = mGroupMetadata.size() - 1;
+ if (lastIndex < 0 || cursorPosition <= mGroupMetadata.keyAt(lastIndex)) {
+ mGroupMetadata.put(cursorPosition, groupSize);
+ } else {
+ // Optimization to avoid binary search if adding groups in ascending cursor position.
+ mGroupMetadata.append(cursorPosition, groupSize);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return mItemCount;
+ }
+
+ /**
+ * Given the position of a list item, returns the size of the group of items corresponding to that
+ * position.
+ */
+ public int getGroupSize(int listPosition) {
+ if (listPosition < 0 || listPosition >= mGroupMetadata.size()) {
+ return 0;
+ }
+
+ return mGroupMetadata.valueAt(listPosition);
+ }
+
+ /**
+ * Given the position of a list item, returns the the first item in the group of items
+ * corresponding to that position.
+ */
+ public Object getItem(int listPosition) {
+ if (mCursor == null || listPosition < 0 || listPosition >= mGroupMetadata.size()) {
+ return null;
+ }
+
+ int cursorPosition = mGroupMetadata.keyAt(listPosition);
+ if (mCursor.moveToPosition(cursorPosition)) {
+ return mCursor;
+ } else {
+ return null;
+ }
+ }
+
+ private void reset() {
+ mItemCount = 0;
+ mGroupMetadata = new SparseIntArray();
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/IntentProvider.java b/java/com/android/dialer/app/calllog/IntentProvider.java
new file mode 100644
index 000000000..879ac353d
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/IntentProvider.java
@@ -0,0 +1,198 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.telecom.PhoneAccountHandle;
+import com.android.contacts.common.model.Contact;
+import com.android.contacts.common.model.ContactLoader;
+import com.android.dialer.app.CallDetailActivity;
+import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.callintent.nano.CallInitiationType;
+import com.android.dialer.telecom.TelecomUtil;
+import com.android.dialer.util.CallUtil;
+import com.android.dialer.util.IntentUtil;
+import java.util.ArrayList;
+
+/**
+ * Used to create an intent to attach to an action in the call log.
+ *
+ * <p>The intent is constructed lazily with the given information.
+ */
+public abstract class IntentProvider {
+
+ private static final String TAG = IntentProvider.class.getSimpleName();
+
+ public static IntentProvider getReturnCallIntentProvider(final String number) {
+ return getReturnCallIntentProvider(number, null);
+ }
+
+ public static IntentProvider getReturnCallIntentProvider(
+ final String number, final PhoneAccountHandle accountHandle) {
+ return new IntentProvider() {
+ @Override
+ public Intent getIntent(Context context) {
+ return new CallIntentBuilder(number, CallInitiationType.Type.CALL_LOG)
+ .setPhoneAccountHandle(accountHandle)
+ .build();
+ }
+ };
+ }
+
+ public static IntentProvider getReturnVideoCallIntentProvider(final String number) {
+ return getReturnVideoCallIntentProvider(number, null);
+ }
+
+ public static IntentProvider getReturnVideoCallIntentProvider(
+ final String number, final PhoneAccountHandle accountHandle) {
+ return new IntentProvider() {
+ @Override
+ public Intent getIntent(Context context) {
+ return new CallIntentBuilder(number, CallInitiationType.Type.CALL_LOG)
+ .setPhoneAccountHandle(accountHandle)
+ .setIsVideoCall(true)
+ .build();
+ }
+ };
+ }
+
+ public static IntentProvider getReturnVoicemailCallIntentProvider() {
+ return new IntentProvider() {
+ @Override
+ public Intent getIntent(Context context) {
+ return new CallIntentBuilder(CallUtil.getVoicemailUri(), CallInitiationType.Type.CALL_LOG)
+ .build();
+ }
+ };
+ }
+
+ public static IntentProvider getSendSmsIntentProvider(final String number) {
+ return new IntentProvider() {
+ @Override
+ public Intent getIntent(Context context) {
+ return IntentUtil.getSendSmsIntent(number);
+ }
+ };
+ }
+
+ /**
+ * Retrieves the call details intent provider for an entry in the call log.
+ *
+ * @param id The call ID of the first call in the call group.
+ * @param extraIds The call ID of the other calls grouped together with the call.
+ * @param voicemailUri If call log entry is for a voicemail, the voicemail URI.
+ * @return The call details intent provider.
+ */
+ public static IntentProvider getCallDetailIntentProvider(
+ final long id, final long[] extraIds, final String voicemailUri) {
+ return new IntentProvider() {
+ @Override
+ public Intent getIntent(Context context) {
+ Intent intent = new Intent(context, CallDetailActivity.class);
+ // Check if the first item is a voicemail.
+ if (voicemailUri != null) {
+ intent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_URI, Uri.parse(voicemailUri));
+ }
+
+ if (extraIds != null && extraIds.length > 0) {
+ intent.putExtra(CallDetailActivity.EXTRA_CALL_LOG_IDS, extraIds);
+ } else {
+ // If there is a single item, use the direct URI for it.
+ intent.setData(ContentUris.withAppendedId(TelecomUtil.getCallLogUri(context), id));
+ }
+ return intent;
+ }
+ };
+ }
+
+ /** Retrieves an add contact intent for the given contact and phone call details. */
+ public static IntentProvider getAddContactIntentProvider(
+ final Uri lookupUri,
+ final CharSequence name,
+ final CharSequence number,
+ final int numberType,
+ final boolean isNewContact) {
+ return new IntentProvider() {
+ @Override
+ public Intent getIntent(Context context) {
+ Contact contactToSave = null;
+
+ if (lookupUri != null) {
+ contactToSave = ContactLoader.parseEncodedContactEntity(lookupUri);
+ }
+
+ if (contactToSave != null) {
+ // Populate the intent with contact information stored in the lookup URI.
+ // Note: This code mirrors code in Contacts/QuickContactsActivity.
+ final Intent intent;
+ if (isNewContact) {
+ intent = IntentUtil.getNewContactIntent();
+ } else {
+ intent = IntentUtil.getAddToExistingContactIntent();
+ }
+
+ ArrayList<ContentValues> values = contactToSave.getContentValues();
+ // Only pre-fill the name field if the provided display name is an nickname
+ // or better (e.g. structured name, nickname)
+ if (contactToSave.getDisplayNameSource()
+ >= ContactsContract.DisplayNameSources.NICKNAME) {
+ intent.putExtra(ContactsContract.Intents.Insert.NAME, contactToSave.getDisplayName());
+ } else if (contactToSave.getDisplayNameSource()
+ == ContactsContract.DisplayNameSources.ORGANIZATION) {
+ // This is probably an organization. Instead of copying the organization
+ // name into a name entry, copy it into the organization entry. This
+ // way we will still consider the contact an organization.
+ final ContentValues organization = new ContentValues();
+ organization.put(
+ ContactsContract.CommonDataKinds.Organization.COMPANY,
+ contactToSave.getDisplayName());
+ organization.put(
+ ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
+ values.add(organization);
+ }
+
+ // Last time used and times used are aggregated values from the usage stat
+ // table. They need to be removed from data values so the SQL table can insert
+ // properly
+ for (ContentValues value : values) {
+ value.remove(ContactsContract.Data.LAST_TIME_USED);
+ value.remove(ContactsContract.Data.TIMES_USED);
+ }
+
+ intent.putExtra(ContactsContract.Intents.Insert.DATA, values);
+
+ return intent;
+ } else {
+ // If no lookup uri is provided, rely on the available phone number and name.
+ if (isNewContact) {
+ return IntentUtil.getNewContactIntent(name, number, numberType);
+ } else {
+ return IntentUtil.getAddToExistingContactIntent(name, number, numberType);
+ }
+ }
+ }
+ };
+ }
+
+ public abstract Intent getIntent(Context context);
+}
diff --git a/java/com/android/dialer/app/calllog/MissedCallNotificationReceiver.java b/java/com/android/dialer/app/calllog/MissedCallNotificationReceiver.java
new file mode 100644
index 000000000..3a202034e
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/MissedCallNotificationReceiver.java
@@ -0,0 +1,50 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Receives broadcasts that should trigger a refresh of the missed call notification. This includes
+ * both an explicit broadcast from Telecom and a reboot.
+ */
+public class MissedCallNotificationReceiver extends BroadcastReceiver {
+
+ //TODO: Use compat class for these methods.
+ public static final String ACTION_SHOW_MISSED_CALLS_NOTIFICATION =
+ "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
+
+ public static final String EXTRA_NOTIFICATION_COUNT = "android.telecom.extra.NOTIFICATION_COUNT";
+
+ public static final String EXTRA_NOTIFICATION_PHONE_NUMBER =
+ "android.telecom.extra.NOTIFICATION_PHONE_NUMBER";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (!ACTION_SHOW_MISSED_CALLS_NOTIFICATION.equals(action)) {
+ return;
+ }
+
+ int count =
+ intent.getIntExtra(
+ EXTRA_NOTIFICATION_COUNT, CallLogNotificationsService.UNKNOWN_MISSED_CALL_COUNT);
+ String number = intent.getStringExtra(EXTRA_NOTIFICATION_PHONE_NUMBER);
+ CallLogNotificationsService.updateMissedCallNotifications(context, count, number);
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/MissedCallNotifier.java b/java/com/android/dialer/app/calllog/MissedCallNotifier.java
new file mode 100644
index 000000000..2fa3dae65
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/MissedCallNotifier.java
@@ -0,0 +1,330 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.provider.CallLog.Calls;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.os.UserManagerCompat;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
+import android.text.TextUtils;
+import com.android.contacts.common.ContactsUtils;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+import com.android.dialer.app.DialtactsActivity;
+import com.android.dialer.app.R;
+import com.android.dialer.app.calllog.CallLogNotificationsHelper.NewCall;
+import com.android.dialer.app.contactinfo.ContactPhotoLoader;
+import com.android.dialer.app.list.ListsFragment;
+import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.callintent.nano.CallInitiationType;
+import com.android.dialer.common.ConfigProviderBindings;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.phonenumbercache.ContactInfo;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.IntentUtil;
+import java.util.List;
+
+/** Creates a notification for calls that the user missed (neither answered nor rejected). */
+public class MissedCallNotifier {
+
+ /** The tag used to identify notifications from this class. */
+ private static final String NOTIFICATION_TAG = "MissedCallNotifier";
+ /** The identifier of the notification of new missed calls. */
+ private static final int NOTIFICATION_ID = 1;
+
+ private static MissedCallNotifier sInstance;
+ private Context mContext;
+ private CallLogNotificationsHelper mCalllogNotificationsHelper;
+
+ @VisibleForTesting
+ MissedCallNotifier(Context context, CallLogNotificationsHelper callLogNotificationsHelper) {
+ mContext = context;
+ mCalllogNotificationsHelper = callLogNotificationsHelper;
+ }
+
+ /** Returns the singleton instance of the {@link MissedCallNotifier}. */
+ public static MissedCallNotifier getInstance(Context context) {
+ if (sInstance == null) {
+ CallLogNotificationsHelper callLogNotificationsHelper =
+ CallLogNotificationsHelper.getInstance(context);
+ sInstance = new MissedCallNotifier(context, callLogNotificationsHelper);
+ }
+ return sInstance;
+ }
+
+ /**
+ * Creates a missed call notification with a post call message if there are no existing missed
+ * calls.
+ */
+ public void createPostCallMessageNotification(String number, String message) {
+ int count = CallLogNotificationsService.UNKNOWN_MISSED_CALL_COUNT;
+ if (ConfigProviderBindings.get(mContext).getBoolean("enable_call_compose", false)) {
+ updateMissedCallNotification(count, number, message);
+ } else {
+ updateMissedCallNotification(count, number, null);
+ }
+ }
+
+ /** Creates a missed call notification. */
+ public void updateMissedCallNotification(int count, String number) {
+ updateMissedCallNotification(count, number, null);
+ }
+
+ private void updateMissedCallNotification(
+ int count, String number, @Nullable String postCallMessage) {
+ final int titleResId;
+ CharSequence expandedText; // The text in the notification's line 1 and 2.
+
+ final List<NewCall> newCalls = mCalllogNotificationsHelper.getNewMissedCalls();
+
+ if (count == CallLogNotificationsService.UNKNOWN_MISSED_CALL_COUNT) {
+ if (newCalls == null) {
+ // If the intent did not contain a count, and we are unable to get a count from the
+ // call log, then no notification can be shown.
+ return;
+ }
+ count = newCalls.size();
+ }
+
+ if (count == 0) {
+ // No voicemails to notify about: clear the notification.
+ clearMissedCalls();
+ return;
+ }
+
+ // The call log has been updated, use that information preferentially.
+ boolean useCallLog = newCalls != null && newCalls.size() == count;
+ NewCall newestCall = useCallLog ? newCalls.get(0) : null;
+ long timeMs = useCallLog ? newestCall.dateMs : System.currentTimeMillis();
+ String missedNumber = useCallLog ? newestCall.number : number;
+
+ Notification.Builder builder = new Notification.Builder(mContext);
+ // Display the first line of the notification:
+ // 1 missed call: <caller name || handle>
+ // More than 1 missed call: <number of calls> + "missed calls"
+ if (count == 1) {
+ //TODO: look up caller ID that is not in contacts.
+ ContactInfo contactInfo =
+ mCalllogNotificationsHelper.getContactInfo(
+ missedNumber,
+ useCallLog ? newestCall.numberPresentation : Calls.PRESENTATION_ALLOWED,
+ useCallLog ? newestCall.countryIso : null);
+
+ titleResId =
+ contactInfo.userType == ContactsUtils.USER_TYPE_WORK
+ ? R.string.notification_missedWorkCallTitle
+ : R.string.notification_missedCallTitle;
+ if (TextUtils.equals(contactInfo.name, contactInfo.formattedNumber)
+ || TextUtils.equals(contactInfo.name, contactInfo.number)) {
+ expandedText =
+ PhoneNumberUtilsCompat.createTtsSpannable(
+ BidiFormatter.getInstance()
+ .unicodeWrap(contactInfo.name, TextDirectionHeuristics.LTR));
+ } else {
+ expandedText = contactInfo.name;
+ }
+
+ if (!TextUtils.isEmpty(postCallMessage)) {
+ // Ex. "John Doe: Hey dude"
+ expandedText =
+ mContext.getString(
+ R.string.post_call_notification_message, expandedText, postCallMessage);
+ }
+ ContactPhotoLoader loader = new ContactPhotoLoader(mContext, contactInfo);
+ Bitmap photoIcon = loader.loadPhotoIcon();
+ if (photoIcon != null) {
+ builder.setLargeIcon(photoIcon);
+ }
+ } else {
+ titleResId = R.string.notification_missedCallsTitle;
+ expandedText = mContext.getString(R.string.notification_missedCallsMsg, count);
+ }
+
+ // Create a public viewable version of the notification, suitable for display when sensitive
+ // notification content is hidden.
+ Notification.Builder publicBuilder = new Notification.Builder(mContext);
+ publicBuilder
+ .setSmallIcon(android.R.drawable.stat_notify_missed_call)
+ .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
+ // Show "Phone" for notification title.
+ .setContentTitle(mContext.getText(R.string.userCallActivityLabel))
+ // Notification details shows that there are missed call(s), but does not reveal
+ // the missed caller information.
+ .setContentText(mContext.getText(titleResId))
+ .setContentIntent(createCallLogPendingIntent())
+ .setAutoCancel(true)
+ .setWhen(timeMs)
+ .setShowWhen(true)
+ .setDeleteIntent(createClearMissedCallsPendingIntent());
+
+ // Create the notification suitable for display when sensitive information is showing.
+ builder
+ .setSmallIcon(android.R.drawable.stat_notify_missed_call)
+ .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
+ .setContentTitle(mContext.getText(titleResId))
+ .setContentText(expandedText)
+ .setContentIntent(createCallLogPendingIntent())
+ .setAutoCancel(true)
+ .setWhen(timeMs)
+ .setShowWhen(true)
+ .setDefaults(Notification.DEFAULT_VIBRATE)
+ .setDeleteIntent(createClearMissedCallsPendingIntent())
+ // Include a public version of the notification to be shown when the missed call
+ // notification is shown on the user's lock screen and they have chosen to hide
+ // sensitive notification information.
+ .setPublicVersion(publicBuilder.build());
+
+ // Add additional actions when there is only 1 missed call and the user isn't locked
+ if (UserManagerCompat.isUserUnlocked(mContext) && count == 1) {
+ if (!TextUtils.isEmpty(missedNumber)
+ && !TextUtils.equals(missedNumber, mContext.getString(R.string.handle_restricted))) {
+ builder.addAction(
+ R.drawable.ic_phone_24dp,
+ mContext.getString(R.string.notification_missedCall_call_back),
+ createCallBackPendingIntent(missedNumber));
+
+ if (!PhoneNumberHelper.isUriNumber(missedNumber)) {
+ builder.addAction(
+ R.drawable.ic_message_24dp,
+ mContext.getString(R.string.notification_missedCall_message),
+ createSendSmsFromNotificationPendingIntent(missedNumber));
+ }
+ }
+ }
+
+ Notification notification = builder.build();
+ configureLedOnNotification(notification);
+
+ LogUtil.i("MissedCallNotifier.updateMissedCallNotification", "adding missed call notification");
+ getNotificationMgr().notify(NOTIFICATION_TAG, NOTIFICATION_ID, notification);
+ }
+
+ private void clearMissedCalls() {
+ AsyncTask.execute(
+ new Runnable() {
+ @Override
+ public void run() {
+ // Call log is only accessible when unlocked. If that's the case, clear the list of
+ // new missed calls from the call log.
+ if (UserManagerCompat.isUserUnlocked(mContext)) {
+ ContentValues values = new ContentValues();
+ values.put(Calls.NEW, 0);
+ values.put(Calls.IS_READ, 1);
+ StringBuilder where = new StringBuilder();
+ where.append(Calls.NEW);
+ where.append(" = 1 AND ");
+ where.append(Calls.TYPE);
+ where.append(" = ?");
+ try {
+ mContext
+ .getContentResolver()
+ .update(
+ Calls.CONTENT_URI,
+ values,
+ where.toString(),
+ new String[] {Integer.toString(Calls.MISSED_TYPE)});
+ } catch (IllegalArgumentException e) {
+ LogUtil.e(
+ "MissedCallNotifier.clearMissedCalls",
+ "contacts provider update command failed",
+ e);
+ }
+ }
+ getNotificationMgr().cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
+ }
+ });
+ }
+
+ /** Trigger an intent to make a call from a missed call number. */
+ public void callBackFromMissedCall(String number) {
+ closeSystemDialogs(mContext);
+ CallLogNotificationsHelper.removeMissedCallNotifications(mContext);
+ DialerUtils.startActivityWithErrorToast(
+ mContext,
+ new CallIntentBuilder(number, CallInitiationType.Type.MISSED_CALL_NOTIFICATION)
+ .build()
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ }
+
+ /** Trigger an intent to send an sms from a missed call number. */
+ public void sendSmsFromMissedCall(String number) {
+ closeSystemDialogs(mContext);
+ CallLogNotificationsHelper.removeMissedCallNotifications(mContext);
+ DialerUtils.startActivityWithErrorToast(
+ mContext, IntentUtil.getSendSmsIntent(number).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ }
+
+ /**
+ * Creates a new pending intent that sends the user to the call log.
+ *
+ * @return The pending intent.
+ */
+ private PendingIntent createCallLogPendingIntent() {
+ Intent contentIntent =
+ DialtactsActivity.getShowTabIntent(mContext, ListsFragment.TAB_INDEX_HISTORY);
+ return PendingIntent.getActivity(mContext, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ /** Creates a pending intent that marks all new missed calls as old. */
+ private PendingIntent createClearMissedCallsPendingIntent() {
+ Intent intent = new Intent(mContext, CallLogNotificationsService.class);
+ intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_MISSED_CALLS_AS_OLD);
+ return PendingIntent.getService(mContext, 0, intent, 0);
+ }
+
+ private PendingIntent createCallBackPendingIntent(String number) {
+ Intent intent = new Intent(mContext, CallLogNotificationsService.class);
+ intent.setAction(CallLogNotificationsService.ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION);
+ intent.putExtra(CallLogNotificationsService.EXTRA_MISSED_CALL_NUMBER, number);
+ // Use FLAG_UPDATE_CURRENT to make sure any previous pending intent is updated with the new
+ // extra.
+ return PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ private PendingIntent createSendSmsFromNotificationPendingIntent(String number) {
+ Intent intent = new Intent(mContext, CallLogNotificationsService.class);
+ intent.setAction(CallLogNotificationsService.ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION);
+ intent.putExtra(CallLogNotificationsService.EXTRA_MISSED_CALL_NUMBER, number);
+ // Use FLAG_UPDATE_CURRENT to make sure any previous pending intent is updated with the new
+ // extra.
+ return PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ /** Configures a notification to emit the blinky notification light. */
+ private void configureLedOnNotification(Notification notification) {
+ notification.flags |= Notification.FLAG_SHOW_LIGHTS;
+ notification.defaults |= Notification.DEFAULT_LIGHTS;
+ }
+
+ /** Closes open system dialogs and the notification shade. */
+ private void closeSystemDialogs(Context context) {
+ context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ }
+
+ private NotificationManager getNotificationMgr() {
+ return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/PhoneAccountUtils.java b/java/com/android/dialer/app/calllog/PhoneAccountUtils.java
new file mode 100644
index 000000000..c6d94d341
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/PhoneAccountUtils.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2013 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.dialer.app.calllog;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.text.TextUtils;
+import com.android.dialer.telecom.TelecomUtil;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Methods to help extract {@code PhoneAccount} information from database and Telecomm sources. */
+public class PhoneAccountUtils {
+
+ /** Return a list of phone accounts that are subscription/SIM accounts. */
+ public static List<PhoneAccountHandle> getSubscriptionPhoneAccounts(Context context) {
+ List<PhoneAccountHandle> subscriptionAccountHandles = new ArrayList<PhoneAccountHandle>();
+ final List<PhoneAccountHandle> accountHandles =
+ TelecomUtil.getCallCapablePhoneAccounts(context);
+ for (PhoneAccountHandle accountHandle : accountHandles) {
+ PhoneAccount account = TelecomUtil.getPhoneAccount(context, accountHandle);
+ if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+ subscriptionAccountHandles.add(accountHandle);
+ }
+ }
+ return subscriptionAccountHandles;
+ }
+
+ /** Compose PhoneAccount object from component name and account id. */
+ @Nullable
+ public static PhoneAccountHandle getAccount(
+ @Nullable String componentString, @Nullable String accountId) {
+ if (TextUtils.isEmpty(componentString) || TextUtils.isEmpty(accountId)) {
+ return null;
+ }
+ final ComponentName componentName = ComponentName.unflattenFromString(componentString);
+ if (componentName == null) {
+ return null;
+ }
+ return new PhoneAccountHandle(componentName, accountId);
+ }
+
+ /** Extract account label from PhoneAccount object. */
+ @Nullable
+ public static String getAccountLabel(
+ Context context, @Nullable PhoneAccountHandle accountHandle) {
+ PhoneAccount account = getAccountOrNull(context, accountHandle);
+ if (account != null && account.getLabel() != null) {
+ return account.getLabel().toString();
+ }
+ return null;
+ }
+
+ /** Extract account color from PhoneAccount object. */
+ public static int getAccountColor(Context context, @Nullable PhoneAccountHandle accountHandle) {
+ final PhoneAccount account = TelecomUtil.getPhoneAccount(context, accountHandle);
+
+ // For single-sim devices the PhoneAccount will be NO_HIGHLIGHT_COLOR by default, so it is
+ // safe to always use the account highlight color.
+ return account == null ? PhoneAccount.NO_HIGHLIGHT_COLOR : account.getHighlightColor();
+ }
+
+ /**
+ * Determine whether a phone account supports call subjects.
+ *
+ * @return {@code true} if call subjects are supported, {@code false} otherwise.
+ */
+ public static boolean getAccountSupportsCallSubject(
+ Context context, @Nullable PhoneAccountHandle accountHandle) {
+ final PhoneAccount account = TelecomUtil.getPhoneAccount(context, accountHandle);
+
+ return account != null && account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
+ }
+
+ /**
+ * Retrieve the account metadata, but if the account does not exist or the device has only a
+ * single registered and enabled account, return null.
+ */
+ @Nullable
+ private static PhoneAccount getAccountOrNull(
+ Context context, @Nullable PhoneAccountHandle accountHandle) {
+ if (TelecomUtil.getCallCapablePhoneAccounts(context).size() <= 1) {
+ return null;
+ }
+ return TelecomUtil.getPhoneAccount(context, accountHandle);
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java b/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java
new file mode 100644
index 000000000..b18270bb3
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java
@@ -0,0 +1,352 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.provider.CallLog.Calls;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.support.v4.content.ContextCompat;
+import android.telecom.PhoneAccount;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.view.View;
+import android.widget.TextView;
+import com.android.dialer.app.PhoneCallDetails;
+import com.android.dialer.app.R;
+import com.android.dialer.app.calllog.calllogcache.CallLogCache;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.android.dialer.util.DialerUtils;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.concurrent.TimeUnit;
+
+/** Helper class to fill in the views in {@link PhoneCallDetailsViews}. */
+public class PhoneCallDetailsHelper {
+
+ /** The maximum number of icons will be shown to represent the call types in a group. */
+ private static final int MAX_CALL_TYPE_ICONS = 3;
+
+ private final Context mContext;
+ private final Resources mResources;
+ private final CallLogCache mCallLogCache;
+ /** Calendar used to construct dates */
+ private final Calendar mCalendar;
+ /** The injected current time in milliseconds since the epoch. Used only by tests. */
+ private Long mCurrentTimeMillisForTest;
+
+ private CharSequence mPhoneTypeLabelForTest;
+ /** List of items to be concatenated together for accessibility descriptions */
+ private ArrayList<CharSequence> mDescriptionItems = new ArrayList<>();
+
+ /**
+ * Creates a new instance of the helper.
+ *
+ * <p>Generally you should have a single instance of this helper in any context.
+ *
+ * @param resources used to look up strings
+ */
+ public PhoneCallDetailsHelper(Context context, Resources resources, CallLogCache callLogCache) {
+ mContext = context;
+ mResources = resources;
+ mCallLogCache = callLogCache;
+ mCalendar = Calendar.getInstance();
+ }
+
+ /** Fills the call details views with content. */
+ public void setPhoneCallDetails(PhoneCallDetailsViews views, PhoneCallDetails details) {
+ // Display up to a given number of icons.
+ views.callTypeIcons.clear();
+ int count = details.callTypes.length;
+ boolean isVoicemail = false;
+ for (int index = 0; index < count && index < MAX_CALL_TYPE_ICONS; ++index) {
+ views.callTypeIcons.add(details.callTypes[index]);
+ if (index == 0) {
+ isVoicemail = details.callTypes[index] == Calls.VOICEMAIL_TYPE;
+ }
+ }
+
+ // Show the video icon if the call had video enabled.
+ views.callTypeIcons.setShowVideo(
+ (details.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO);
+ views.callTypeIcons.requestLayout();
+ views.callTypeIcons.setVisibility(View.VISIBLE);
+
+ // Show the total call count only if there are more than the maximum number of icons.
+ final Integer callCount;
+ if (count > MAX_CALL_TYPE_ICONS) {
+ callCount = count;
+ } else {
+ callCount = null;
+ }
+
+ // Set the call count, location, date and if voicemail, set the duration.
+ setDetailText(views, callCount, details);
+
+ // Set the account label if it exists.
+ String accountLabel = mCallLogCache.getAccountLabel(details.accountHandle);
+ if (!TextUtils.isEmpty(details.viaNumber)) {
+ if (!TextUtils.isEmpty(accountLabel)) {
+ accountLabel =
+ mResources.getString(
+ R.string.call_log_via_number_phone_account, accountLabel, details.viaNumber);
+ } else {
+ accountLabel = mResources.getString(R.string.call_log_via_number, details.viaNumber);
+ }
+ }
+ if (!TextUtils.isEmpty(accountLabel)) {
+ views.callAccountLabel.setVisibility(View.VISIBLE);
+ views.callAccountLabel.setText(accountLabel);
+ int color = mCallLogCache.getAccountColor(details.accountHandle);
+ if (color == PhoneAccount.NO_HIGHLIGHT_COLOR) {
+ int defaultColor = R.color.dialer_secondary_text_color;
+ views.callAccountLabel.setTextColor(mContext.getResources().getColor(defaultColor));
+ } else {
+ views.callAccountLabel.setTextColor(color);
+ }
+ } else {
+ views.callAccountLabel.setVisibility(View.GONE);
+ }
+
+ final CharSequence nameText;
+ final CharSequence displayNumber = details.displayNumber;
+ if (TextUtils.isEmpty(details.getPreferredName())) {
+ nameText = displayNumber;
+ // We have a real phone number as "nameView" so make it always LTR
+ views.nameView.setTextDirection(View.TEXT_DIRECTION_LTR);
+ } else {
+ nameText = details.getPreferredName();
+ }
+
+ views.nameView.setText(nameText);
+
+ if (isVoicemail) {
+ views.voicemailTranscriptionView.setText(
+ TextUtils.isEmpty(details.transcription) ? null : details.transcription);
+ }
+
+ // Bold if not read
+ Typeface typeface = details.isRead ? Typeface.SANS_SERIF : Typeface.DEFAULT_BOLD;
+ views.nameView.setTypeface(typeface);
+ views.voicemailTranscriptionView.setTypeface(typeface);
+ views.callLocationAndDate.setTypeface(typeface);
+ views.callLocationAndDate.setTextColor(
+ ContextCompat.getColor(
+ mContext,
+ details.isRead ? R.color.call_log_detail_color : R.color.call_log_unread_text_color));
+ }
+
+ /**
+ * Builds a string containing the call location and date. For voicemail logs only the call date is
+ * returned because location information is displayed in the call action button
+ *
+ * @param details The call details.
+ * @return The call location and date string.
+ */
+ public CharSequence getCallLocationAndDate(PhoneCallDetails details) {
+ mDescriptionItems.clear();
+
+ if (details.callTypes[0] != Calls.VOICEMAIL_TYPE) {
+ // Get type of call (ie mobile, home, etc) if known, or the caller's location.
+ CharSequence callTypeOrLocation = getCallTypeOrLocation(details);
+
+ // Only add the call type or location if its not empty. It will be empty for unknown
+ // callers.
+ if (!TextUtils.isEmpty(callTypeOrLocation)) {
+ mDescriptionItems.add(callTypeOrLocation);
+ }
+ }
+
+ // The date of this call
+ mDescriptionItems.add(getCallDate(details));
+
+ // Create a comma separated list from the call type or location, and call date.
+ return DialerUtils.join(mDescriptionItems);
+ }
+
+ /**
+ * For a call, if there is an associated contact for the caller, return the known call type (e.g.
+ * mobile, home, work). If there is no associated contact, attempt to use the caller's location if
+ * known.
+ *
+ * @param details Call details to use.
+ * @return Type of call (mobile/home) if known, or the location of the caller (if known).
+ */
+ public CharSequence getCallTypeOrLocation(PhoneCallDetails details) {
+ if (details.isSpam) {
+ return mResources.getString(R.string.spam_number_call_log_label);
+ } else if (details.isBlocked) {
+ return mResources.getString(R.string.blocked_number_call_log_label);
+ }
+
+ CharSequence numberFormattedLabel = null;
+ // Only show a label if the number is shown and it is not a SIP address.
+ if (!TextUtils.isEmpty(details.number)
+ && !PhoneNumberHelper.isUriNumber(details.number.toString())
+ && !mCallLogCache.isVoicemailNumber(details.accountHandle, details.number)) {
+
+ if (TextUtils.isEmpty(details.namePrimary) && !TextUtils.isEmpty(details.geocode)) {
+ numberFormattedLabel = details.geocode;
+ } else if (!(details.numberType == Phone.TYPE_CUSTOM
+ && TextUtils.isEmpty(details.numberLabel))) {
+ // Get type label only if it will not be "Custom" because of an empty number label.
+ numberFormattedLabel =
+ mPhoneTypeLabelForTest != null
+ ? mPhoneTypeLabelForTest
+ : Phone.getTypeLabel(mResources, details.numberType, details.numberLabel);
+ }
+ }
+
+ if (!TextUtils.isEmpty(details.namePrimary) && TextUtils.isEmpty(numberFormattedLabel)) {
+ numberFormattedLabel = details.displayNumber;
+ }
+ return numberFormattedLabel;
+ }
+
+ public void setPhoneTypeLabelForTest(CharSequence phoneTypeLabel) {
+ this.mPhoneTypeLabelForTest = phoneTypeLabel;
+ }
+
+ /**
+ * Get the call date/time of the call. For the call log this is relative to the current time. e.g.
+ * 3 minutes ago. For voicemail, see {@link #getGranularDateTime(PhoneCallDetails)}
+ *
+ * @param details Call details to use.
+ * @return String representing when the call occurred.
+ */
+ public CharSequence getCallDate(PhoneCallDetails details) {
+ if (details.callTypes[0] == Calls.VOICEMAIL_TYPE) {
+ return getGranularDateTime(details);
+ }
+
+ return DateUtils.getRelativeTimeSpanString(
+ details.date,
+ getCurrentTimeMillis(),
+ DateUtils.MINUTE_IN_MILLIS,
+ DateUtils.FORMAT_ABBREV_RELATIVE);
+ }
+
+ /**
+ * Get the granular version of the call date/time of the call. The result is always in the form
+ * 'DATE at TIME'. The date value changes based on when the call was created.
+ *
+ * <p>If created today, DATE is 'Today' If created this year, DATE is 'MMM dd' Otherwise, DATE is
+ * 'MMM dd, yyyy'
+ *
+ * <p>TIME is the localized time format, e.g. 'hh:mm a' or 'HH:mm'
+ *
+ * @param details Call details to use
+ * @return String representing when the call occurred
+ */
+ public CharSequence getGranularDateTime(PhoneCallDetails details) {
+ return mResources.getString(
+ R.string.voicemailCallLogDateTimeFormat,
+ getGranularDate(details.date),
+ DateUtils.formatDateTime(mContext, details.date, DateUtils.FORMAT_SHOW_TIME));
+ }
+
+ /**
+ * Get the granular version of the call date. See {@link #getGranularDateTime(PhoneCallDetails)}
+ */
+ private String getGranularDate(long date) {
+ if (DateUtils.isToday(date)) {
+ return mResources.getString(R.string.voicemailCallLogToday);
+ }
+ return DateUtils.formatDateTime(
+ mContext,
+ date,
+ DateUtils.FORMAT_SHOW_DATE
+ | DateUtils.FORMAT_ABBREV_MONTH
+ | (shouldShowYear(date) ? DateUtils.FORMAT_SHOW_YEAR : DateUtils.FORMAT_NO_YEAR));
+ }
+
+ /**
+ * Determines whether the year should be shown for the given date
+ *
+ * @return {@code true} if date is within the current year, {@code false} otherwise
+ */
+ private boolean shouldShowYear(long date) {
+ mCalendar.setTimeInMillis(getCurrentTimeMillis());
+ int currentYear = mCalendar.get(Calendar.YEAR);
+ mCalendar.setTimeInMillis(date);
+ return currentYear != mCalendar.get(Calendar.YEAR);
+ }
+
+ /** Sets the text of the header view for the details page of a phone call. */
+ public void setCallDetailsHeader(TextView nameView, PhoneCallDetails details) {
+ final CharSequence nameText;
+ if (!TextUtils.isEmpty(details.namePrimary)) {
+ nameText = details.namePrimary;
+ } else if (!TextUtils.isEmpty(details.displayNumber)) {
+ nameText = details.displayNumber;
+ } else {
+ nameText = mResources.getString(R.string.unknown);
+ }
+
+ nameView.setText(nameText);
+ }
+
+ public void setCurrentTimeForTest(long currentTimeMillis) {
+ mCurrentTimeMillisForTest = currentTimeMillis;
+ }
+
+ /**
+ * Returns the current time in milliseconds since the epoch.
+ *
+ * <p>It can be injected in tests using {@link #setCurrentTimeForTest(long)}.
+ */
+ private long getCurrentTimeMillis() {
+ if (mCurrentTimeMillisForTest == null) {
+ return System.currentTimeMillis();
+ } else {
+ return mCurrentTimeMillisForTest;
+ }
+ }
+
+ /** Sets the call count, date, and if it is a voicemail, sets the duration. */
+ private void setDetailText(
+ PhoneCallDetailsViews views, Integer callCount, PhoneCallDetails details) {
+ // Combine the count (if present) and the date.
+ CharSequence dateText = details.callLocationAndDate;
+ final CharSequence text;
+ if (callCount != null) {
+ text = mResources.getString(R.string.call_log_item_count_and_date, callCount, dateText);
+ } else {
+ text = dateText;
+ }
+
+ if (details.callTypes[0] == Calls.VOICEMAIL_TYPE && details.duration > 0) {
+ views.callLocationAndDate.setText(
+ mResources.getString(
+ R.string.voicemailCallLogDateTimeFormatWithDuration,
+ text,
+ getVoicemailDuration(details)));
+ } else {
+ views.callLocationAndDate.setText(text);
+ }
+ }
+
+ private String getVoicemailDuration(PhoneCallDetails details) {
+ long minutes = TimeUnit.SECONDS.toMinutes(details.duration);
+ long seconds = details.duration - TimeUnit.MINUTES.toSeconds(minutes);
+ if (minutes > 99) {
+ minutes = 99;
+ }
+ return mResources.getString(R.string.voicemailDurationFormat, minutes, seconds);
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java b/java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java
new file mode 100644
index 000000000..476996826
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java
@@ -0,0 +1,75 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.TextView;
+import com.android.dialer.app.R;
+
+/** Encapsulates the views that are used to display the details of a phone call in the call log. */
+public final class PhoneCallDetailsViews {
+
+ public final TextView nameView;
+ public final View callTypeView;
+ public final CallTypeIconsView callTypeIcons;
+ public final TextView callLocationAndDate;
+ public final TextView voicemailTranscriptionView;
+ public final TextView callAccountLabel;
+
+ private PhoneCallDetailsViews(
+ TextView nameView,
+ View callTypeView,
+ CallTypeIconsView callTypeIcons,
+ TextView callLocationAndDate,
+ TextView voicemailTranscriptionView,
+ TextView callAccountLabel) {
+ this.nameView = nameView;
+ this.callTypeView = callTypeView;
+ this.callTypeIcons = callTypeIcons;
+ this.callLocationAndDate = callLocationAndDate;
+ this.voicemailTranscriptionView = voicemailTranscriptionView;
+ this.callAccountLabel = callAccountLabel;
+ }
+
+ /**
+ * Create a new instance by extracting the elements from the given view.
+ *
+ * <p>The view should contain three text views with identifiers {@code R.id.name}, {@code
+ * R.id.date}, and {@code R.id.number}, and a linear layout with identifier {@code
+ * R.id.call_types}.
+ */
+ public static PhoneCallDetailsViews fromView(View view) {
+ return new PhoneCallDetailsViews(
+ (TextView) view.findViewById(R.id.name),
+ view.findViewById(R.id.call_type),
+ (CallTypeIconsView) view.findViewById(R.id.call_type_icons),
+ (TextView) view.findViewById(R.id.call_location_and_date),
+ (TextView) view.findViewById(R.id.voicemail_transcription),
+ (TextView) view.findViewById(R.id.call_account_label));
+ }
+
+ public static PhoneCallDetailsViews createForTest(Context context) {
+ return new PhoneCallDetailsViews(
+ new TextView(context),
+ new View(context),
+ new CallTypeIconsView(context),
+ new TextView(context),
+ new TextView(context),
+ new TextView(context));
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/PhoneNumberDisplayUtil.java b/java/com/android/dialer/app/calllog/PhoneNumberDisplayUtil.java
new file mode 100644
index 000000000..410d4cc37
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/PhoneNumberDisplayUtil.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.dialer.app.calllog;
+
+import android.content.Context;
+import android.provider.CallLog.Calls;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
+import android.text.TextUtils;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+import com.android.dialer.app.R;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+
+/** Helper for formatting and managing the display of phone numbers. */
+public class PhoneNumberDisplayUtil {
+
+ /** Returns the string to display for the given phone number if there is no matching contact. */
+ /* package */
+ static CharSequence getDisplayName(
+ Context context, CharSequence number, int presentation, boolean isVoicemail) {
+ if (presentation == Calls.PRESENTATION_UNKNOWN) {
+ return context.getResources().getString(R.string.unknown);
+ }
+ if (presentation == Calls.PRESENTATION_RESTRICTED) {
+ return PhoneNumberHelper.getDisplayNameForRestrictedNumber(context);
+ }
+ if (presentation == Calls.PRESENTATION_PAYPHONE) {
+ return context.getResources().getString(R.string.payphone);
+ }
+ if (isVoicemail) {
+ return context.getResources().getString(R.string.voicemail);
+ }
+ if (PhoneNumberHelper.isLegacyUnknownNumbers(number)) {
+ return context.getResources().getString(R.string.unknown);
+ }
+ return "";
+ }
+
+ /**
+ * Returns the string to display for the given phone number.
+ *
+ * @param number the number to display
+ * @param formattedNumber the formatted number if available, may be null
+ */
+ public static CharSequence getDisplayNumber(
+ Context context,
+ CharSequence number,
+ int presentation,
+ CharSequence formattedNumber,
+ CharSequence postDialDigits,
+ boolean isVoicemail) {
+ final CharSequence displayName = getDisplayName(context, number, presentation, isVoicemail);
+ if (!TextUtils.isEmpty(displayName)) {
+ return getTtsSpannableLtrNumber(displayName);
+ }
+
+ if (!TextUtils.isEmpty(formattedNumber)) {
+ return getTtsSpannableLtrNumber(formattedNumber);
+ } else if (!TextUtils.isEmpty(number)) {
+ return getTtsSpannableLtrNumber(number.toString() + postDialDigits);
+ } else {
+ return context.getResources().getString(R.string.unknown);
+ }
+ }
+
+ /** Returns number annotated as phone number in LTR direction. */
+ public static CharSequence getTtsSpannableLtrNumber(CharSequence number) {
+ return PhoneNumberUtilsCompat.createTtsSpannable(
+ BidiFormatter.getInstance().unicodeWrap(number.toString(), TextDirectionHeuristics.LTR));
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java b/java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java
new file mode 100644
index 000000000..e539ceef6
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java
@@ -0,0 +1,132 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.app.Activity;
+import android.database.ContentObserver;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.provider.CallLog;
+import android.provider.VoicemailContract;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.dialer.app.R;
+import com.android.dialer.app.list.ListsFragment;
+import com.android.dialer.app.voicemail.VoicemailAudioManager;
+import com.android.dialer.app.voicemail.VoicemailErrorManager;
+import com.android.dialer.app.voicemail.VoicemailPlaybackPresenter;
+import com.android.dialer.common.LogUtil;
+
+public class VisualVoicemailCallLogFragment extends CallLogFragment {
+
+ private final ContentObserver mVoicemailStatusObserver = new CustomContentObserver();
+ private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
+
+ private VoicemailErrorManager mVoicemailAlertManager;
+
+ @Override
+ public void onCreate(Bundle state) {
+ super.onCreate(state);
+ mCallTypeFilter = CallLog.Calls.VOICEMAIL_TYPE;
+ mVoicemailPlaybackPresenter = VoicemailPlaybackPresenter.getInstance(getActivity(), state);
+ getActivity()
+ .getContentResolver()
+ .registerContentObserver(
+ VoicemailContract.Status.CONTENT_URI, true, mVoicemailStatusObserver);
+ }
+
+ @Override
+ protected VoicemailPlaybackPresenter getVoicemailPlaybackPresenter() {
+ return mVoicemailPlaybackPresenter;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ mVoicemailAlertManager =
+ new VoicemailErrorManager(getContext(), getAdapter().getAlertManager(), mModalAlertManager);
+ getActivity()
+ .getContentResolver()
+ .registerContentObserver(
+ VoicemailContract.Status.CONTENT_URI,
+ true,
+ mVoicemailAlertManager.getContentObserver());
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+ View view = inflater.inflate(R.layout.call_log_fragment, container, false);
+ setupView(view);
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mVoicemailPlaybackPresenter.onResume();
+ mVoicemailAlertManager.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ mVoicemailPlaybackPresenter.onPause();
+ mVoicemailAlertManager.onPause();
+ super.onPause();
+ }
+
+ @Override
+ public void onDestroy() {
+ getActivity()
+ .getContentResolver()
+ .unregisterContentObserver(mVoicemailAlertManager.getContentObserver());
+ mVoicemailPlaybackPresenter.onDestroy();
+ getActivity().getContentResolver().unregisterContentObserver(mVoicemailStatusObserver);
+ super.onDestroy();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mVoicemailPlaybackPresenter.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void fetchCalls() {
+ super.fetchCalls();
+ ((ListsFragment) getParentFragment()).updateTabUnreadCounts();
+ }
+
+ @Override
+ public void onPageResume(@Nullable Activity activity) {
+ LogUtil.d("VisualVoicemailCallLogFragment.onPageResume", null);
+ super.onPageResume(activity);
+ if (activity != null) {
+ activity.setVolumeControlStream(VoicemailAudioManager.PLAYBACK_STREAM);
+ }
+ }
+
+ @Override
+ public void onPagePause(@Nullable Activity activity) {
+ LogUtil.d("VisualVoicemailCallLogFragment.onPagePause", null);
+ super.onPagePause(activity);
+ if (activity != null) {
+ activity.setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/VoicemailQueryHandler.java b/java/com/android/dialer/app/calllog/VoicemailQueryHandler.java
new file mode 100644
index 000000000..d6d8354ec
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/VoicemailQueryHandler.java
@@ -0,0 +1,74 @@
+/*
+ * 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.dialer.app.calllog;
+
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.CallLog.Calls;
+import android.util.Log;
+
+/** Handles asynchronous queries to the call log for voicemail. */
+public class VoicemailQueryHandler extends AsyncQueryHandler {
+
+ private static final String TAG = "VoicemailQueryHandler";
+
+ /** The token for the query to mark all new voicemails as old. */
+ private static final int UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN = 50;
+
+ private Context mContext;
+
+ public VoicemailQueryHandler(Context context, ContentResolver contentResolver) {
+ super(contentResolver);
+ mContext = context;
+ }
+
+ /** Updates all new voicemails to mark them as old. */
+ public void markNewVoicemailsAsOld() {
+ // Mark all "new" voicemails as not new anymore.
+ StringBuilder where = new StringBuilder();
+ where.append(Calls.NEW);
+ where.append(" = 1 AND ");
+ where.append(Calls.TYPE);
+ where.append(" = ?");
+
+ ContentValues values = new ContentValues(1);
+ values.put(Calls.NEW, "0");
+
+ startUpdate(
+ UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN,
+ null,
+ Calls.CONTENT_URI_WITH_VOICEMAIL,
+ values,
+ where.toString(),
+ new String[] {Integer.toString(Calls.VOICEMAIL_TYPE)});
+ }
+
+ @Override
+ protected void onUpdateComplete(int token, Object cookie, int result) {
+ if (token == UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN) {
+ if (mContext != null) {
+ Intent serviceIntent = new Intent(mContext, CallLogNotificationsService.class);
+ serviceIntent.setAction(CallLogNotificationsService.ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS);
+ mContext.startService(serviceIntent);
+ } else {
+ Log.w(TAG, "Unknown update completed: ignoring: " + token);
+ }
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/calllogcache/CallLogCache.java b/java/com/android/dialer/app/calllog/calllogcache/CallLogCache.java
new file mode 100644
index 000000000..7645a333e
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/calllogcache/CallLogCache.java
@@ -0,0 +1,105 @@
+/*
+ * 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.dialer.app.calllog.calllogcache;
+
+import android.content.Context;
+import android.telecom.PhoneAccountHandle;
+import com.android.dialer.app.calllog.CallLogAdapter;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.util.CallUtil;
+
+/**
+ * This is the base class for the CallLogCaches.
+ *
+ * <p>Keeps a cache of recently made queries to the Telecom/Telephony processes. The aim of this
+ * cache is to reduce the number of cross-process requests to TelecomManager, which can negatively
+ * affect performance.
+ *
+ * <p>This is designed with the specific use case of the {@link CallLogAdapter} in mind.
+ */
+public abstract class CallLogCache {
+ // TODO: Dialer should be fixed so as not to check isVoicemail() so often but at the time of
+ // this writing, that was a much larger undertaking than creating this cache.
+
+ protected final Context mContext;
+
+ private boolean mHasCheckedForVideoAvailability;
+ private int mVideoAvailability;
+
+ public CallLogCache(Context context) {
+ mContext = context;
+ }
+
+ /** Return the most compatible version of the TelecomCallLogCache. */
+ public static CallLogCache getCallLogCache(Context context) {
+ if (CompatUtils.isClassAvailable("android.telecom.PhoneAccountHandle")) {
+ return new CallLogCacheLollipopMr1(context);
+ }
+ return new CallLogCacheLollipop(context);
+ }
+
+ public void reset() {
+ mHasCheckedForVideoAvailability = false;
+ mVideoAvailability = 0;
+ }
+
+ /**
+ * Returns true if the given number is the number of the configured voicemail. To be able to
+ * mock-out this, it is not a static method.
+ */
+ public abstract boolean isVoicemailNumber(PhoneAccountHandle accountHandle, CharSequence number);
+
+ /**
+ * Returns {@code true} when the current sim supports video calls, regardless of the value in a
+ * contact's {@link android.provider.ContactsContract.CommonDataKinds.Phone#CARRIER_PRESENCE}
+ * column.
+ */
+ public boolean isVideoEnabled() {
+ if (!mHasCheckedForVideoAvailability) {
+ mVideoAvailability = CallUtil.getVideoCallingAvailability(mContext);
+ mHasCheckedForVideoAvailability = true;
+ }
+ return (mVideoAvailability & CallUtil.VIDEO_CALLING_ENABLED) != 0;
+ }
+
+ /**
+ * Returns {@code true} when the current sim supports checking video calling capabilities via the
+ * {@link android.provider.ContactsContract.CommonDataKinds.Phone#CARRIER_PRESENCE} column.
+ */
+ public boolean canRelyOnVideoPresence() {
+ if (!mHasCheckedForVideoAvailability) {
+ mVideoAvailability = CallUtil.getVideoCallingAvailability(mContext);
+ mHasCheckedForVideoAvailability = true;
+ }
+ return (mVideoAvailability & CallUtil.VIDEO_CALLING_PRESENCE) != 0;
+ }
+
+ /** Extract account label from PhoneAccount object. */
+ public abstract String getAccountLabel(PhoneAccountHandle accountHandle);
+
+ /** Extract account color from PhoneAccount object. */
+ public abstract int getAccountColor(PhoneAccountHandle accountHandle);
+
+ /**
+ * Determines if the PhoneAccount supports specifying a call subject (i.e. calling with a note)
+ * for outgoing calls.
+ *
+ * @param accountHandle The PhoneAccount handle.
+ * @return {@code true} if calling with a note is supported, {@code false} otherwise.
+ */
+ public abstract boolean doesAccountSupportCallSubject(PhoneAccountHandle accountHandle);
+}
diff --git a/java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipop.java b/java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipop.java
new file mode 100644
index 000000000..78aaa4193
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipop.java
@@ -0,0 +1,74 @@
+/*
+ * 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.dialer.app.calllog.calllogcache;
+
+import android.content.Context;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+
+/**
+ * This is a compatibility class for the CallLogCache for versions of dialer before Lollipop Mr1
+ * (the introduction of phone accounts).
+ *
+ * <p>This class should not be initialized directly and instead be acquired from {@link
+ * CallLogCache#getCallLogCache}.
+ */
+class CallLogCacheLollipop extends CallLogCache {
+
+ private String mVoicemailNumber;
+
+ /* package */ CallLogCacheLollipop(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean isVoicemailNumber(PhoneAccountHandle accountHandle, CharSequence number) {
+ if (TextUtils.isEmpty(number)) {
+ return false;
+ }
+
+ String numberString = number.toString();
+
+ if (!TextUtils.isEmpty(mVoicemailNumber)) {
+ return PhoneNumberUtils.compare(numberString, mVoicemailNumber);
+ }
+
+ if (PhoneNumberUtils.isVoiceMailNumber(numberString)) {
+ mVoicemailNumber = numberString;
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public String getAccountLabel(PhoneAccountHandle accountHandle) {
+ return null;
+ }
+
+ @Override
+ public int getAccountColor(PhoneAccountHandle accountHandle) {
+ return PhoneAccount.NO_HIGHLIGHT_COLOR;
+ }
+
+ @Override
+ public boolean doesAccountSupportCallSubject(PhoneAccountHandle accountHandle) {
+ return false;
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipopMr1.java b/java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipopMr1.java
new file mode 100644
index 000000000..c342b7e3b
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipopMr1.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2013 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.dialer.app.calllog.calllogcache;
+
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.telecom.PhoneAccountHandle;
+import android.text.TextUtils;
+import android.util.Pair;
+import com.android.dialer.app.calllog.PhoneAccountUtils;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This is the CallLogCache for versions of dialer Lollipop Mr1 and above with support for multi-SIM
+ * devices.
+ *
+ * <p>This class should not be initialized directly and instead be acquired from {@link
+ * CallLogCache#getCallLogCache}.
+ */
+class CallLogCacheLollipopMr1 extends CallLogCache {
+
+ /*
+ * Maps from a phone-account/number pair to a boolean because multiple numbers could return true
+ * for the voicemail number if those numbers are not pre-normalized. Access must be synchronzied
+ * as it's used in the background thread in CallLogAdapter. {@see CallLogAdapter#loadData}
+ */
+ @VisibleForTesting
+ final Map<Pair<PhoneAccountHandle, CharSequence>, Boolean> mVoicemailQueryCache =
+ new ConcurrentHashMap<>();
+
+ private final Map<PhoneAccountHandle, String> mPhoneAccountLabelCache = new HashMap<>();
+ private final Map<PhoneAccountHandle, Integer> mPhoneAccountColorCache = new HashMap<>();
+ private final Map<PhoneAccountHandle, Boolean> mPhoneAccountCallWithNoteCache = new HashMap<>();
+
+ /* package */ CallLogCacheLollipopMr1(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void reset() {
+ mVoicemailQueryCache.clear();
+ mPhoneAccountLabelCache.clear();
+ mPhoneAccountColorCache.clear();
+ mPhoneAccountCallWithNoteCache.clear();
+
+ super.reset();
+ }
+
+ @Override
+ public boolean isVoicemailNumber(PhoneAccountHandle accountHandle, CharSequence number) {
+ if (TextUtils.isEmpty(number)) {
+ return false;
+ }
+
+ Pair<PhoneAccountHandle, CharSequence> key = new Pair<>(accountHandle, number);
+ Boolean value = mVoicemailQueryCache.get(key);
+ if (value != null) {
+ return value;
+ }
+ boolean isVoicemail =
+ PhoneNumberHelper.isVoicemailNumber(mContext, accountHandle, number.toString());
+ mVoicemailQueryCache.put(key, isVoicemail);
+ return isVoicemail;
+ }
+
+ @Override
+ public String getAccountLabel(PhoneAccountHandle accountHandle) {
+ if (mPhoneAccountLabelCache.containsKey(accountHandle)) {
+ return mPhoneAccountLabelCache.get(accountHandle);
+ } else {
+ String label = PhoneAccountUtils.getAccountLabel(mContext, accountHandle);
+ mPhoneAccountLabelCache.put(accountHandle, label);
+ return label;
+ }
+ }
+
+ @Override
+ public int getAccountColor(PhoneAccountHandle accountHandle) {
+ if (mPhoneAccountColorCache.containsKey(accountHandle)) {
+ return mPhoneAccountColorCache.get(accountHandle);
+ } else {
+ Integer color = PhoneAccountUtils.getAccountColor(mContext, accountHandle);
+ mPhoneAccountColorCache.put(accountHandle, color);
+ return color;
+ }
+ }
+
+ @Override
+ public boolean doesAccountSupportCallSubject(PhoneAccountHandle accountHandle) {
+ if (mPhoneAccountCallWithNoteCache.containsKey(accountHandle)) {
+ return mPhoneAccountCallWithNoteCache.get(accountHandle);
+ } else {
+ Boolean supportsCallWithNote =
+ PhoneAccountUtils.getAccountSupportsCallSubject(mContext, accountHandle);
+ mPhoneAccountCallWithNoteCache.put(accountHandle, supportsCallWithNote);
+ return supportsCallWithNote;
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/contactinfo/ContactInfoCache.java b/java/com/android/dialer/app/contactinfo/ContactInfoCache.java
new file mode 100644
index 000000000..4135cb7b8
--- /dev/null
+++ b/java/com/android/dialer/app/contactinfo/ContactInfoCache.java
@@ -0,0 +1,357 @@
+/*
+ * 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.dialer.app.contactinfo;
+
+import android.os.Handler;
+import android.os.Message;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import com.android.dialer.phonenumbercache.ContactInfo;
+import com.android.dialer.phonenumbercache.ContactInfoHelper;
+import com.android.dialer.util.ExpirableCache;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.PriorityBlockingQueue;
+
+/**
+ * This is a cache of contact details for the phone numbers in the c all log. The key is the phone
+ * number with the country in which teh call was placed or received. The content of the cache is
+ * expired (but not purged) whenever the application comes to the foreground.
+ *
+ * <p>This cache queues request for information and queries for information on a background thread,
+ * so {@code start()} and {@code stop()} must be called to initiate or halt that thread's exeuction
+ * as needed.
+ *
+ * <p>TODO: Explore whether there is a pattern to remove external dependencies for starting and
+ * stopping the query thread.
+ */
+public class ContactInfoCache {
+
+ private static final int REDRAW = 1;
+ private static final int START_THREAD = 2;
+ private static final int START_PROCESSING_REQUESTS_DELAY_MS = 1000;
+
+ private final ExpirableCache<NumberWithCountryIso, ContactInfo> mCache;
+ private final ContactInfoHelper mContactInfoHelper;
+ private final OnContactInfoChangedListener mOnContactInfoChangedListener;
+ private final BlockingQueue<ContactInfoRequest> mUpdateRequests;
+ private final Handler mHandler;
+ private QueryThread mContactInfoQueryThread;
+ private volatile boolean mRequestProcessingDisabled = false;
+
+ private static class InnerHandler extends Handler {
+
+ private final WeakReference<ContactInfoCache> contactInfoCacheWeakReference;
+
+ public InnerHandler(WeakReference<ContactInfoCache> contactInfoCacheWeakReference) {
+ this.contactInfoCacheWeakReference = contactInfoCacheWeakReference;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ ContactInfoCache reference = contactInfoCacheWeakReference.get();
+ if (reference == null) {
+ return;
+ }
+ switch (msg.what) {
+ case REDRAW:
+ reference.mOnContactInfoChangedListener.onContactInfoChanged();
+ break;
+ case START_THREAD:
+ reference.startRequestProcessing();
+ }
+ }
+ }
+
+ public ContactInfoCache(
+ @NonNull ExpirableCache<NumberWithCountryIso, ContactInfo> internalCache,
+ @NonNull ContactInfoHelper contactInfoHelper,
+ @NonNull OnContactInfoChangedListener listener) {
+ mCache = internalCache;
+ mContactInfoHelper = contactInfoHelper;
+ mOnContactInfoChangedListener = listener;
+ mUpdateRequests = new PriorityBlockingQueue<>();
+ mHandler = new InnerHandler(new WeakReference<>(this));
+ }
+
+ public ContactInfo getValue(
+ String number,
+ String countryIso,
+ ContactInfo callLogContactInfo,
+ boolean remoteLookupIfNotFoundLocally) {
+ NumberWithCountryIso numberCountryIso = new NumberWithCountryIso(number, countryIso);
+ ExpirableCache.CachedValue<ContactInfo> cachedInfo = mCache.getCachedValue(numberCountryIso);
+ ContactInfo info = cachedInfo == null ? null : cachedInfo.getValue();
+ if (cachedInfo == null) {
+ mCache.put(numberCountryIso, ContactInfo.EMPTY);
+ // Use the cached contact info from the call log.
+ info = callLogContactInfo;
+ // The db request should happen on a non-UI thread.
+ // Request the contact details immediately since they are currently missing.
+ int requestType =
+ remoteLookupIfNotFoundLocally
+ ? ContactInfoRequest.TYPE_LOCAL_AND_REMOTE
+ : ContactInfoRequest.TYPE_LOCAL;
+ enqueueRequest(number, countryIso, callLogContactInfo, /* immediate */ true, requestType);
+ // We will format the phone number when we make the background request.
+ } else {
+ if (cachedInfo.isExpired()) {
+ // The contact info is no longer up to date, we should request it. However, we
+ // do not need to request them immediately.
+ enqueueRequest(
+ number,
+ countryIso,
+ callLogContactInfo, /* immediate */
+ false,
+ ContactInfoRequest.TYPE_LOCAL);
+ } else if (!callLogInfoMatches(callLogContactInfo, info)) {
+ // The call log information does not match the one we have, look it up again.
+ // We could simply update the call log directly, but that needs to be done in a
+ // background thread, so it is easier to simply request a new lookup, which will, as
+ // a side-effect, update the call log.
+ enqueueRequest(
+ number,
+ countryIso,
+ callLogContactInfo, /* immediate */
+ false,
+ ContactInfoRequest.TYPE_LOCAL);
+ }
+
+ if (info == ContactInfo.EMPTY) {
+ // Use the cached contact info from the call log.
+ info = callLogContactInfo;
+ }
+ }
+ return info;
+ }
+
+ /**
+ * Queries the appropriate content provider for the contact associated with the number.
+ *
+ * <p>Upon completion it also updates the cache in the call log, if it is different from {@code
+ * callLogInfo}.
+ *
+ * <p>The number might be either a SIP address or a phone number.
+ *
+ * <p>It returns true if it updated the content of the cache and we should therefore tell the view
+ * to update its content.
+ */
+ private boolean queryContactInfo(ContactInfoRequest request) {
+ ContactInfo info;
+ if (request.isLocalRequest()) {
+ info = mContactInfoHelper.lookupNumber(request.number, request.countryIso);
+ if (request.type == ContactInfoRequest.TYPE_LOCAL_AND_REMOTE) {
+ if (!mContactInfoHelper.hasName(info)) {
+ enqueueRequest(
+ request.number,
+ request.countryIso,
+ request.callLogInfo,
+ true,
+ ContactInfoRequest.TYPE_REMOTE);
+ return false;
+ }
+ }
+ } else {
+ info = mContactInfoHelper.lookupNumberInRemoteDirectory(request.number, request.countryIso);
+ }
+
+ if (info == null) {
+ // The lookup failed, just return without requesting to update the view.
+ return false;
+ }
+
+ // Check the existing entry in the cache: only if it has changed we should update the
+ // view.
+ NumberWithCountryIso numberCountryIso =
+ new NumberWithCountryIso(request.number, request.countryIso);
+ ContactInfo existingInfo = mCache.getPossiblyExpired(numberCountryIso);
+
+ final boolean isRemoteSource = info.sourceType != 0;
+
+ // Don't force redraw if existing info in the cache is equal to {@link ContactInfo#EMPTY}
+ // to avoid updating the data set for every new row that is scrolled into view.
+
+ // Exception: Photo uris for contacts from remote sources are not cached in the call log
+ // cache, so we have to force a redraw for these contacts regardless.
+ boolean updated =
+ (existingInfo != ContactInfo.EMPTY || isRemoteSource) && !info.equals(existingInfo);
+
+ // Store the data in the cache so that the UI thread can use to display it. Store it
+ // even if it has not changed so that it is marked as not expired.
+ mCache.put(numberCountryIso, info);
+
+ // Update the call log even if the cache it is up-to-date: it is possible that the cache
+ // contains the value from a different call log entry.
+ mContactInfoHelper.updateCallLogContactInfo(
+ request.number, request.countryIso, info, request.callLogInfo);
+ if (!request.isLocalRequest()) {
+ mContactInfoHelper.updateCachedNumberLookupService(info);
+ }
+ return updated;
+ }
+
+ /**
+ * After a delay, start the thread to begin processing requests. We perform lookups on a
+ * background thread, but this must be called to indicate the thread should be running.
+ */
+ public void start() {
+ // Schedule a thread-creation message if the thread hasn't been created yet, as an
+ // optimization to queue fewer messages.
+ if (mContactInfoQueryThread == null) {
+ // TODO: Check whether this delay before starting to process is necessary.
+ mHandler.sendEmptyMessageDelayed(START_THREAD, START_PROCESSING_REQUESTS_DELAY_MS);
+ }
+ }
+
+ /**
+ * Stops the thread and clears the queue of messages to process. This cleans up the thread for
+ * lookups so that it is not perpetually running.
+ */
+ public void stop() {
+ stopRequestProcessing();
+ }
+
+ /**
+ * Starts a background thread to process contact-lookup requests, unless one has already been
+ * started.
+ */
+ private synchronized void startRequestProcessing() {
+ // For unit-testing.
+ if (mRequestProcessingDisabled) {
+ return;
+ }
+
+ // If a thread is already started, don't start another.
+ if (mContactInfoQueryThread != null) {
+ return;
+ }
+
+ mContactInfoQueryThread = new QueryThread();
+ mContactInfoQueryThread.setPriority(Thread.MIN_PRIORITY);
+ mContactInfoQueryThread.start();
+ }
+
+ public void invalidate() {
+ mCache.expireAll();
+ stopRequestProcessing();
+ }
+
+ /**
+ * Stops the background thread that processes updates and cancels any pending requests to start
+ * it.
+ */
+ private synchronized void stopRequestProcessing() {
+ // Remove any pending requests to start the processing thread.
+ mHandler.removeMessages(START_THREAD);
+ if (mContactInfoQueryThread != null) {
+ // Stop the thread; we are finished with it.
+ mContactInfoQueryThread.stopProcessing();
+ mContactInfoQueryThread.interrupt();
+ mContactInfoQueryThread = null;
+ }
+ }
+
+ /**
+ * Enqueues a request to look up the contact details for the given phone number.
+ *
+ * <p>It also provides the current contact info stored in the call log for this number.
+ *
+ * <p>If the {@code immediate} parameter is true, it will start immediately the thread that looks
+ * up the contact information (if it has not been already started). Otherwise, it will be started
+ * with a delay. See {@link #START_PROCESSING_REQUESTS_DELAY_MS}.
+ */
+ private void enqueueRequest(
+ String number,
+ String countryIso,
+ ContactInfo callLogInfo,
+ boolean immediate,
+ @ContactInfoRequest.TYPE int type) {
+ ContactInfoRequest request = new ContactInfoRequest(number, countryIso, callLogInfo, type);
+ if (!mUpdateRequests.contains(request)) {
+ mUpdateRequests.offer(request);
+ }
+
+ if (immediate) {
+ startRequestProcessing();
+ }
+ }
+
+ /** Checks whether the contact info from the call log matches the one from the contacts db. */
+ private boolean callLogInfoMatches(ContactInfo callLogInfo, ContactInfo info) {
+ // The call log only contains a subset of the fields in the contacts db. Only check those.
+ return TextUtils.equals(callLogInfo.name, info.name)
+ && callLogInfo.type == info.type
+ && TextUtils.equals(callLogInfo.label, info.label);
+ }
+
+ /** Sets whether processing of requests for contact details should be enabled. */
+ public void disableRequestProcessing() {
+ mRequestProcessingDisabled = true;
+ }
+
+ @VisibleForTesting
+ public void injectContactInfoForTest(String number, String countryIso, ContactInfo contactInfo) {
+ NumberWithCountryIso numberCountryIso = new NumberWithCountryIso(number, countryIso);
+ mCache.put(numberCountryIso, contactInfo);
+ }
+
+ public interface OnContactInfoChangedListener {
+
+ void onContactInfoChanged();
+ }
+
+ /*
+ * Handles requests for contact name and number type.
+ */
+ private class QueryThread extends Thread {
+
+ private volatile boolean mDone = false;
+
+ public QueryThread() {
+ super("ContactInfoCache.QueryThread");
+ }
+
+ public void stopProcessing() {
+ mDone = true;
+ }
+
+ @Override
+ public void run() {
+ boolean shouldRedraw = false;
+ while (true) {
+ // Check if thread is finished, and if so return immediately.
+ if (mDone) {
+ return;
+ }
+
+ try {
+ ContactInfoRequest request = mUpdateRequests.take();
+ shouldRedraw |= queryContactInfo(request);
+ if (shouldRedraw
+ && (mUpdateRequests.isEmpty()
+ || request.isLocalRequest() && !mUpdateRequests.peek().isLocalRequest())) {
+ shouldRedraw = false;
+ mHandler.sendEmptyMessage(REDRAW);
+ }
+ } catch (InterruptedException e) {
+ // Ignore and attempt to continue processing requests
+ }
+ }
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/contactinfo/ContactInfoRequest.java b/java/com/android/dialer/app/contactinfo/ContactInfoRequest.java
new file mode 100644
index 000000000..5c2eb1dbb
--- /dev/null
+++ b/java/com/android/dialer/app/contactinfo/ContactInfoRequest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.dialer.app.contactinfo;
+
+import android.support.annotation.IntDef;
+import android.text.TextUtils;
+import com.android.dialer.phonenumbercache.ContactInfo;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicLong;
+
+/** A request for contact details for the given number, used by the ContactInfoCache. */
+public final class ContactInfoRequest implements Comparable<ContactInfoRequest> {
+
+ private static final AtomicLong NEXT_SEQUENCE_NUMBER = new AtomicLong(0);
+
+ private final long sequenceNumber;
+
+ /** The number to look-up. */
+ public final String number;
+ /** The country in which a call to or from this number was placed or received. */
+ public final String countryIso;
+ /** The cached contact information stored in the call log. */
+ public final ContactInfo callLogInfo;
+
+ /** Is the request a remote lookup. Remote requests are treated as lower priority. */
+ @TYPE public final int type;
+
+ /** Specifies the type of the request is. */
+ @IntDef(
+ value = {
+ TYPE_LOCAL,
+ TYPE_LOCAL_AND_REMOTE,
+ TYPE_REMOTE,
+ }
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TYPE {}
+
+ public static final int TYPE_LOCAL = 0;
+ /** If cannot find the contact locally, do remote lookup later. */
+ public static final int TYPE_LOCAL_AND_REMOTE = 1;
+
+ public static final int TYPE_REMOTE = 2;
+
+ public ContactInfoRequest(
+ String number, String countryIso, ContactInfo callLogInfo, @TYPE int type) {
+ this.sequenceNumber = NEXT_SEQUENCE_NUMBER.getAndIncrement();
+ this.number = number;
+ this.countryIso = countryIso;
+ this.callLogInfo = callLogInfo;
+ this.type = type;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof ContactInfoRequest)) {
+ return false;
+ }
+
+ ContactInfoRequest other = (ContactInfoRequest) obj;
+
+ if (!TextUtils.equals(number, other.number)) {
+ return false;
+ }
+ if (!TextUtils.equals(countryIso, other.countryIso)) {
+ return false;
+ }
+ if (!Objects.equals(callLogInfo, other.callLogInfo)) {
+ return false;
+ }
+
+ if (type != other.type) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean isLocalRequest() {
+ return type == TYPE_LOCAL || type == TYPE_LOCAL_AND_REMOTE;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(sequenceNumber, number, countryIso, callLogInfo, type);
+ }
+
+ @Override
+ public int compareTo(ContactInfoRequest other) {
+ // Local query always comes first.
+ if (isLocalRequest() && !other.isLocalRequest()) {
+ return -1;
+ }
+ if (!isLocalRequest() && other.isLocalRequest()) {
+ return 1;
+ }
+ // First come first served.
+ return sequenceNumber < other.sequenceNumber ? -1 : 1;
+ }
+}
diff --git a/java/com/android/dialer/app/contactinfo/ContactPhotoLoader.java b/java/com/android/dialer/app/contactinfo/ContactPhotoLoader.java
new file mode 100644
index 000000000..a8c718502
--- /dev/null
+++ b/java/com/android/dialer/app/contactinfo/ContactPhotoLoader.java
@@ -0,0 +1,129 @@
+/*
+ * 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.dialer.app.contactinfo;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.lettertiles.LetterTileDrawable;
+import com.android.dialer.app.R;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.phonenumbercache.ContactInfo;
+import com.android.dialer.phonenumbercache.ContactInfoHelper;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Objects;
+
+/**
+ * Class to create the appropriate contact icon from a ContactInfo. This class is for synchronous,
+ * blocking calls to generate bitmaps, while ContactCommons.ContactPhotoManager is to cache, manage
+ * and update a ImageView asynchronously.
+ */
+public class ContactPhotoLoader {
+
+ private final Context mContext;
+ private final ContactInfo mContactInfo;
+
+ public ContactPhotoLoader(Context context, ContactInfo contactInfo) {
+ mContext = Objects.requireNonNull(context);
+ mContactInfo = Objects.requireNonNull(contactInfo);
+ }
+
+ private static Bitmap drawableToBitmap(Drawable drawable, int width, int height) {
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+ return bitmap;
+ }
+
+ /** Create a contact photo icon bitmap appropriate for the ContactInfo. */
+ public Bitmap loadPhotoIcon() {
+ Assert.isWorkerThread();
+ int photoSize = mContext.getResources().getDimensionPixelSize(R.dimen.contact_photo_size);
+ return drawableToBitmap(getIcon(), photoSize, photoSize);
+ }
+
+ @VisibleForTesting
+ Drawable getIcon() {
+ Drawable drawable = createPhotoIconDrawable();
+ if (drawable == null) {
+ drawable = createLetterTileDrawable();
+ }
+ return drawable;
+ }
+
+ /**
+ * @return a {@link Drawable} of circular photo icon if the photo can be loaded, {@code null}
+ * otherwise.
+ */
+ @Nullable
+ private Drawable createPhotoIconDrawable() {
+ if (mContactInfo.photoUri == null) {
+ return null;
+ }
+ try {
+ InputStream input = mContext.getContentResolver().openInputStream(mContactInfo.photoUri);
+ if (input == null) {
+ LogUtil.w(
+ "ContactPhotoLoader.createPhotoIconDrawable",
+ "createPhotoIconDrawable: InputStream is null");
+ return null;
+ }
+ Bitmap bitmap = BitmapFactory.decodeStream(input);
+ input.close();
+
+ if (bitmap == null) {
+ LogUtil.w(
+ "ContactPhotoLoader.createPhotoIconDrawable",
+ "createPhotoIconDrawable: Bitmap is null");
+ return null;
+ }
+ final RoundedBitmapDrawable drawable =
+ RoundedBitmapDrawableFactory.create(mContext.getResources(), bitmap);
+ drawable.setAntiAlias(true);
+ drawable.setCornerRadius(bitmap.getHeight() / 2);
+ return drawable;
+ } catch (IOException e) {
+ LogUtil.e("ContactPhotoLoader.createPhotoIconDrawable", e.toString());
+ return null;
+ }
+ }
+
+ /** @return a {@link LetterTileDrawable} based on the ContactInfo. */
+ private Drawable createLetterTileDrawable() {
+ ContactInfoHelper helper =
+ new ContactInfoHelper(mContext, GeoUtil.getCurrentCountryIso(mContext));
+ LetterTileDrawable drawable = new LetterTileDrawable(mContext.getResources());
+ drawable.setCanonicalDialerLetterTileDetails(
+ mContactInfo.name,
+ mContactInfo.lookupKey,
+ LetterTileDrawable.SHAPE_CIRCLE,
+ helper.isBusiness(mContactInfo.sourceType)
+ ? LetterTileDrawable.TYPE_BUSINESS
+ : LetterTileDrawable.TYPE_DEFAULT);
+ return drawable;
+ }
+}
diff --git a/java/com/android/dialer/app/contactinfo/ExpirableCacheHeadlessFragment.java b/java/com/android/dialer/app/contactinfo/ExpirableCacheHeadlessFragment.java
new file mode 100644
index 000000000..aed51b507
--- /dev/null
+++ b/java/com/android/dialer/app/contactinfo/ExpirableCacheHeadlessFragment.java
@@ -0,0 +1,67 @@
+/*
+ * 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.dialer.app.contactinfo;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+import com.android.dialer.phonenumbercache.ContactInfo;
+import com.android.dialer.util.ExpirableCache;
+
+/**
+ * Fragment without any UI whose purpose is to retain an instance of {@link ExpirableCache} across
+ * configuration change through the use of {@link #setRetainInstance(boolean)}. This is done as
+ * opposed to implementing {@link android.os.Parcelable} as it is a less widespread change.
+ */
+public class ExpirableCacheHeadlessFragment extends Fragment {
+
+ private static final String FRAGMENT_TAG = "ExpirableCacheHeadlessFragment";
+ private static final int CONTACT_INFO_CACHE_SIZE = 100;
+
+ private ExpirableCache<NumberWithCountryIso, ContactInfo> retainedCache;
+
+ @NonNull
+ public static ExpirableCacheHeadlessFragment attach(@NonNull AppCompatActivity parentActivity) {
+ return attach(parentActivity.getSupportFragmentManager());
+ }
+
+ @NonNull
+ private static ExpirableCacheHeadlessFragment attach(FragmentManager fragmentManager) {
+ ExpirableCacheHeadlessFragment fragment =
+ (ExpirableCacheHeadlessFragment) fragmentManager.findFragmentByTag(FRAGMENT_TAG);
+ if (fragment == null) {
+ fragment = new ExpirableCacheHeadlessFragment();
+ // Allowing state loss since in rare cases this is called after activity's state is saved and
+ // it's fine if the cache is lost.
+ fragmentManager.beginTransaction().add(fragment, FRAGMENT_TAG).commitNowAllowingStateLoss();
+ }
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ retainedCache = ExpirableCache.create(CONTACT_INFO_CACHE_SIZE);
+ setRetainInstance(true);
+ }
+
+ public ExpirableCache<NumberWithCountryIso, ContactInfo> getRetainedCache() {
+ return retainedCache;
+ }
+}
diff --git a/java/com/android/dialer/app/contactinfo/NumberWithCountryIso.java b/java/com/android/dialer/app/contactinfo/NumberWithCountryIso.java
new file mode 100644
index 000000000..a005c447d
--- /dev/null
+++ b/java/com/android/dialer/app/contactinfo/NumberWithCountryIso.java
@@ -0,0 +1,57 @@
+/*
+ * 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.dialer.app.contactinfo;
+
+import android.text.TextUtils;
+
+/**
+ * Stores a phone number of a call with the country code where it originally occurred. This object
+ * is used as a key in the {@code ContactInfoCache}.
+ *
+ * <p>The country does not necessarily specify the country of the phone number itself, but rather it
+ * is the country in which the user was in when the call was placed or received.
+ */
+public final class NumberWithCountryIso {
+
+ public final String number;
+ public final String countryIso;
+
+ public NumberWithCountryIso(String number, String countryIso) {
+ this.number = number;
+ this.countryIso = countryIso;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+ if (!(o instanceof NumberWithCountryIso)) {
+ return false;
+ }
+ NumberWithCountryIso other = (NumberWithCountryIso) o;
+ return TextUtils.equals(number, other.number) && TextUtils.equals(countryIso, other.countryIso);
+ }
+
+ @Override
+ public int hashCode() {
+ int numberHashCode = number == null ? 0 : number.hashCode();
+ int countryHashCode = countryIso == null ? 0 : countryIso.hashCode();
+
+ return numberHashCode ^ countryHashCode;
+ }
+}
diff --git a/java/com/android/dialer/app/dialpad/DialpadFragment.java b/java/com/android/dialer/app/dialpad/DialpadFragment.java
new file mode 100644
index 000000000..18bb250ce
--- /dev/null
+++ b/java/com/android/dialer/app/dialpad/DialpadFragment.java
@@ -0,0 +1,1689 @@
+/*
+ * 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.dialer.app.dialpad;
+
+import android.Manifest.permission;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.AudioManager;
+import android.media.ToneGenerator;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Trace;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.provider.Contacts.PhonesColumns;
+import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.content.ContextCompat;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.PhoneNumberFormattingTextWatcher;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.view.HapticFeedbackConstants;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.PopupMenu;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.dialog.CallSubjectDialog;
+import com.android.contacts.common.util.StopWatch;
+import com.android.contacts.common.widget.FloatingActionButtonController;
+import com.android.dialer.animation.AnimUtils;
+import com.android.dialer.app.DialtactsActivity;
+import com.android.dialer.app.R;
+import com.android.dialer.app.SpecialCharSequenceMgr;
+import com.android.dialer.app.calllog.CallLogAsync;
+import com.android.dialer.app.calllog.PhoneAccountUtils;
+import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.callintent.nano.CallInitiationType;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.dialpadview.DialpadKeyButton;
+import com.android.dialer.dialpadview.DialpadView;
+import com.android.dialer.proguard.UsedByReflection;
+import com.android.dialer.telecom.TelecomUtil;
+import com.android.dialer.util.CallUtil;
+import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.PermissionsUtil;
+import java.util.HashSet;
+import java.util.List;
+
+/** Fragment that displays a twelve-key phone dialpad. */
+public class DialpadFragment extends Fragment
+ implements View.OnClickListener,
+ View.OnLongClickListener,
+ View.OnKeyListener,
+ AdapterView.OnItemClickListener,
+ TextWatcher,
+ PopupMenu.OnMenuItemClickListener,
+ DialpadKeyButton.OnPressedListener {
+
+ private static final String TAG = "DialpadFragment";
+ private static final boolean DEBUG = DialtactsActivity.DEBUG;
+ private static final String EMPTY_NUMBER = "";
+ private static final char PAUSE = ',';
+ private static final char WAIT = ';';
+ /** The length of DTMF tones in milliseconds */
+ private static final int TONE_LENGTH_MS = 150;
+
+ private static final int TONE_LENGTH_INFINITE = -1;
+ /** The DTMF tone volume relative to other sounds in the stream */
+ private static final int TONE_RELATIVE_VOLUME = 80;
+ /** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */
+ private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_DTMF;
+ /** Identifier for the "Add Call" intent extra. */
+ private static final String ADD_CALL_MODE_KEY = "add_call_mode";
+ /**
+ * Identifier for intent extra for sending an empty Flash message for CDMA networks. This message
+ * is used by the network to simulate a press/depress of the "hookswitch" of a landline phone. Aka
+ * "empty flash".
+ *
+ * <p>TODO: Using an intent extra to tell the phone to send this flash is a temporary measure. To
+ * be replaced with an Telephony/TelecomManager call in the future. TODO: Keep in sync with the
+ * string defined in OutgoingCallBroadcaster.java in Phone app until this is replaced with the
+ * Telephony/Telecom API.
+ */
+ private static final String EXTRA_SEND_EMPTY_FLASH = "com.android.phone.extra.SEND_EMPTY_FLASH";
+
+ private static final String PREF_DIGITS_FILLED_BY_INTENT = "pref_digits_filled_by_intent";
+ private final Object mToneGeneratorLock = new Object();
+ /** Set of dialpad keys that are currently being pressed */
+ private final HashSet<View> mPressedDialpadKeys = new HashSet<View>(12);
+ // Last number dialed, retrieved asynchronously from the call DB
+ // in onCreate. This number is displayed when the user hits the
+ // send key and cleared in onPause.
+ private final CallLogAsync mCallLog = new CallLogAsync();
+ private OnDialpadQueryChangedListener mDialpadQueryListener;
+ private DialpadView mDialpadView;
+ private EditText mDigits;
+ private int mDialpadSlideInDuration;
+ /** Remembers if we need to clear digits field when the screen is completely gone. */
+ private boolean mClearDigitsOnStop;
+
+ private View mOverflowMenuButton;
+ private PopupMenu mOverflowPopupMenu;
+ private View mDelete;
+ private ToneGenerator mToneGenerator;
+ private View mSpacer;
+ private FloatingActionButtonController mFloatingActionButtonController;
+ private ListView mDialpadChooser;
+ private DialpadChooserAdapter mDialpadChooserAdapter;
+ /** Regular expression prohibiting manual phone call. Can be empty, which means "no rule". */
+ private String mProhibitedPhoneNumberRegexp;
+
+ private PseudoEmergencyAnimator mPseudoEmergencyAnimator;
+ private String mLastNumberDialed = EMPTY_NUMBER;
+
+ // determines if we want to playback local DTMF tones.
+ private boolean mDTMFToneEnabled;
+ private String mCurrentCountryIso;
+ private CallStateReceiver mCallStateReceiver;
+ private boolean mWasEmptyBeforeTextChange;
+ /**
+ * This field is set to true while processing an incoming DIAL intent, in order to make sure that
+ * SpecialCharSequenceMgr actions can be triggered by user input but *not* by a tel: URI passed by
+ * some other app. It will be set to false when all digits are cleared.
+ */
+ private boolean mDigitsFilledByIntent;
+
+ private boolean mStartedFromNewIntent = false;
+ private boolean mFirstLaunch = false;
+ private boolean mAnimate = false;
+
+ /**
+ * Determines whether an add call operation is requested.
+ *
+ * @param intent The intent.
+ * @return {@literal true} if add call operation was requested. {@literal false} otherwise.
+ */
+ public static boolean isAddCallMode(Intent intent) {
+ if (intent == null) {
+ return false;
+ }
+ final String action = intent.getAction();
+ if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
+ // see if we are "adding a call" from the InCallScreen; false by default.
+ return intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Format the provided string of digits into one that represents a properly formatted phone
+ * number.
+ *
+ * @param dialString String of characters to format
+ * @param normalizedNumber the E164 format number whose country code is used if the given
+ * phoneNumber doesn't have the country code.
+ * @param countryIso The country code representing the format to use if the provided normalized
+ * number is null or invalid.
+ * @return the provided string of digits as a formatted phone number, retaining any post-dial
+ * portion of the string.
+ */
+ @VisibleForTesting
+ static String getFormattedDigits(String dialString, String normalizedNumber, String countryIso) {
+ String number = PhoneNumberUtils.extractNetworkPortion(dialString);
+ // Also retrieve the post dial portion of the provided data, so that the entire dial
+ // string can be reconstituted later.
+ final String postDial = PhoneNumberUtils.extractPostDialPortion(dialString);
+
+ if (TextUtils.isEmpty(number)) {
+ return postDial;
+ }
+
+ number = PhoneNumberUtils.formatNumber(number, normalizedNumber, countryIso);
+
+ if (TextUtils.isEmpty(postDial)) {
+ return number;
+ }
+
+ return number.concat(postDial);
+ }
+
+ /**
+ * Returns true of the newDigit parameter can be added at the current selection point, otherwise
+ * returns false. Only prevents input of WAIT and PAUSE digits at an unsupported position. Fails
+ * early if start == -1 or start is larger than end.
+ */
+ @VisibleForTesting
+ /* package */ static boolean canAddDigit(CharSequence digits, int start, int end, char newDigit) {
+ if (newDigit != WAIT && newDigit != PAUSE) {
+ throw new IllegalArgumentException(
+ "Should not be called for anything other than PAUSE & WAIT");
+ }
+
+ // False if no selection, or selection is reversed (end < start)
+ if (start == -1 || end < start) {
+ return false;
+ }
+
+ // unsupported selection-out-of-bounds state
+ if (start > digits.length() || end > digits.length()) {
+ return false;
+ }
+
+ // Special digit cannot be the first digit
+ if (start == 0) {
+ return false;
+ }
+
+ if (newDigit == WAIT) {
+ // preceding char is ';' (WAIT)
+ if (digits.charAt(start - 1) == WAIT) {
+ return false;
+ }
+
+ // next char is ';' (WAIT)
+ if ((digits.length() > end) && (digits.charAt(end) == WAIT)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private TelephonyManager getTelephonyManager() {
+ return (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ @Override
+ public Context getContext() {
+ return getActivity();
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ mWasEmptyBeforeTextChange = TextUtils.isEmpty(s);
+ }
+
+ @Override
+ public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
+ if (mWasEmptyBeforeTextChange != TextUtils.isEmpty(input)) {
+ final Activity activity = getActivity();
+ if (activity != null) {
+ activity.invalidateOptionsMenu();
+ updateMenuOverflowButton(mWasEmptyBeforeTextChange);
+ }
+ }
+
+ // DTMF Tones do not need to be played here any longer -
+ // the DTMF dialer handles that functionality now.
+ }
+
+ @Override
+ public void afterTextChanged(Editable input) {
+ // When DTMF dialpad buttons are being pressed, we delay SpecialCharSequenceMgr sequence,
+ // since some of SpecialCharSequenceMgr's behavior is too abrupt for the "touch-down"
+ // behavior.
+ if (!mDigitsFilledByIntent
+ && SpecialCharSequenceMgr.handleChars(getActivity(), input.toString(), mDigits)) {
+ // A special sequence was entered, clear the digits
+ mDigits.getText().clear();
+ }
+
+ if (isDigitsEmpty()) {
+ mDigitsFilledByIntent = false;
+ mDigits.setCursorVisible(false);
+ }
+
+ if (mDialpadQueryListener != null) {
+ mDialpadQueryListener.onDialpadQueryChanged(mDigits.getText().toString());
+ }
+
+ updateDeleteButtonEnabledState();
+ }
+
+ @Override
+ public void onCreate(Bundle state) {
+ Trace.beginSection(TAG + " onCreate");
+ super.onCreate(state);
+
+ mFirstLaunch = state == null;
+
+ mCurrentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
+
+ mProhibitedPhoneNumberRegexp =
+ getResources().getString(R.string.config_prohibited_phone_number_regexp);
+
+ if (state != null) {
+ mDigitsFilledByIntent = state.getBoolean(PREF_DIGITS_FILLED_BY_INTENT);
+ }
+
+ mDialpadSlideInDuration = getResources().getInteger(R.integer.dialpad_slide_in_duration);
+
+ if (mCallStateReceiver == null) {
+ IntentFilter callStateIntentFilter =
+ new IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
+ mCallStateReceiver = new CallStateReceiver();
+ getActivity().registerReceiver(mCallStateReceiver, callStateIntentFilter);
+ }
+ Trace.endSection();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+ Trace.beginSection(TAG + " onCreateView");
+ Trace.beginSection(TAG + " inflate view");
+ final View fragmentView = inflater.inflate(R.layout.dialpad_fragment, container, false);
+ Trace.endSection();
+ Trace.beginSection(TAG + " buildLayer");
+ fragmentView.buildLayer();
+ Trace.endSection();
+
+ Trace.beginSection(TAG + " setup views");
+
+ mDialpadView = (DialpadView) fragmentView.findViewById(R.id.dialpad_view);
+ mDialpadView.setCanDigitsBeEdited(true);
+ mDigits = mDialpadView.getDigits();
+ mDigits.setKeyListener(UnicodeDialerKeyListener.INSTANCE);
+ mDigits.setOnClickListener(this);
+ mDigits.setOnKeyListener(this);
+ mDigits.setOnLongClickListener(this);
+ mDigits.addTextChangedListener(this);
+ mDigits.setElegantTextHeight(false);
+
+ PhoneNumberFormattingTextWatcher watcher =
+ new PhoneNumberFormattingTextWatcher(GeoUtil.getCurrentCountryIso(getActivity()));
+ mDigits.addTextChangedListener(watcher);
+
+ // Check for the presence of the keypad
+ View oneButton = fragmentView.findViewById(R.id.one);
+ if (oneButton != null) {
+ configureKeypadListeners(fragmentView);
+ }
+
+ mDelete = mDialpadView.getDeleteButton();
+
+ if (mDelete != null) {
+ mDelete.setOnClickListener(this);
+ mDelete.setOnLongClickListener(this);
+ }
+
+ mSpacer = fragmentView.findViewById(R.id.spacer);
+ mSpacer.setOnTouchListener(
+ new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (isDigitsEmpty()) {
+ if (getActivity() != null) {
+ return ((HostInterface) getActivity()).onDialpadSpacerTouchWithEmptyQuery();
+ }
+ return true;
+ }
+ return false;
+ }
+ });
+
+ mDigits.setCursorVisible(false);
+
+ // Set up the "dialpad chooser" UI; see showDialpadChooser().
+ mDialpadChooser = (ListView) fragmentView.findViewById(R.id.dialpadChooser);
+ mDialpadChooser.setOnItemClickListener(this);
+
+ final View floatingActionButtonContainer =
+ fragmentView.findViewById(R.id.dialpad_floating_action_button_container);
+ final ImageButton floatingActionButton =
+ (ImageButton) fragmentView.findViewById(R.id.dialpad_floating_action_button);
+ floatingActionButton.setOnClickListener(this);
+ mFloatingActionButtonController =
+ new FloatingActionButtonController(
+ getActivity(), floatingActionButtonContainer, floatingActionButton);
+ Trace.endSection();
+ Trace.endSection();
+ return fragmentView;
+ }
+
+ private boolean isLayoutReady() {
+ return mDigits != null;
+ }
+
+ @VisibleForTesting
+ public EditText getDigitsWidget() {
+ return mDigits;
+ }
+
+ /** @return true when {@link #mDigits} is actually filled by the Intent. */
+ private boolean fillDigitsIfNecessary(Intent intent) {
+ // Only fills digits from an intent if it is a new intent.
+ // Otherwise falls back to the previously used number.
+ if (!mFirstLaunch && !mStartedFromNewIntent) {
+ return false;
+ }
+
+ final String action = intent.getAction();
+ if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
+ Uri uri = intent.getData();
+ if (uri != null) {
+ if (PhoneAccount.SCHEME_TEL.equals(uri.getScheme())) {
+ // Put the requested number into the input area
+ String data = uri.getSchemeSpecificPart();
+ // Remember it is filled via Intent.
+ mDigitsFilledByIntent = true;
+ final String converted =
+ PhoneNumberUtils.convertKeypadLettersToDigits(
+ PhoneNumberUtils.replaceUnicodeDigits(data));
+ setFormattedDigits(converted, null);
+ return true;
+ } else {
+ if (!PermissionsUtil.hasContactsPermissions(getActivity())) {
+ return false;
+ }
+ String type = intent.getType();
+ if (People.CONTENT_ITEM_TYPE.equals(type) || Phones.CONTENT_ITEM_TYPE.equals(type)) {
+ // Query the phone number
+ Cursor c =
+ getActivity()
+ .getContentResolver()
+ .query(
+ intent.getData(),
+ new String[] {PhonesColumns.NUMBER, PhonesColumns.NUMBER_KEY},
+ null,
+ null,
+ null);
+ if (c != null) {
+ try {
+ if (c.moveToFirst()) {
+ // Remember it is filled via Intent.
+ mDigitsFilledByIntent = true;
+ // Put the number into the input area
+ setFormattedDigits(c.getString(0), c.getString(1));
+ return true;
+ }
+ } finally {
+ c.close();
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks the given Intent and changes dialpad's UI state. For example, if the Intent requires the
+ * screen to enter "Add Call" mode, this method will show correct UI for the mode.
+ */
+ private void configureScreenFromIntent(Activity parent) {
+ // If we were not invoked with a DIAL intent,
+ if (!(parent instanceof DialtactsActivity)) {
+ setStartedFromNewIntent(false);
+ return;
+ }
+ // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
+ // digits in the dialer field.
+ Intent intent = parent.getIntent();
+
+ if (!isLayoutReady()) {
+ // This happens typically when parent's Activity#onNewIntent() is called while
+ // Fragment#onCreateView() isn't called yet, and thus we cannot configure Views at
+ // this point. onViewCreate() should call this method after preparing layouts, so
+ // just ignore this call now.
+ LogUtil.i(
+ "DialpadFragment.configureScreenFromIntent",
+ "Screen configuration is requested before onCreateView() is called. Ignored");
+ return;
+ }
+
+ boolean needToShowDialpadChooser = false;
+
+ // Be sure *not* to show the dialpad chooser if this is an
+ // explicit "Add call" action, though.
+ final boolean isAddCallMode = isAddCallMode(intent);
+ if (!isAddCallMode) {
+
+ // Don't show the chooser when called via onNewIntent() and phone number is present.
+ // i.e. User clicks a telephone link from gmail for example.
+ // In this case, we want to show the dialpad with the phone number.
+ final boolean digitsFilled = fillDigitsIfNecessary(intent);
+ if (!(mStartedFromNewIntent && digitsFilled)) {
+
+ final String action = intent.getAction();
+ if (Intent.ACTION_DIAL.equals(action)
+ || Intent.ACTION_VIEW.equals(action)
+ || Intent.ACTION_MAIN.equals(action)) {
+ // If there's already an active call, bring up an intermediate UI to
+ // make the user confirm what they really want to do.
+ if (isPhoneInUse()) {
+ needToShowDialpadChooser = true;
+ }
+ }
+ }
+ }
+ showDialpadChooser(needToShowDialpadChooser);
+ setStartedFromNewIntent(false);
+ }
+
+ public void setStartedFromNewIntent(boolean value) {
+ mStartedFromNewIntent = value;
+ }
+
+ public void clearCallRateInformation() {
+ setCallRateInformation(null, null);
+ }
+
+ public void setCallRateInformation(String countryName, String displayRate) {
+ mDialpadView.setCallRateInformation(countryName, displayRate);
+ }
+
+ /** Sets formatted digits to digits field. */
+ private void setFormattedDigits(String data, String normalizedNumber) {
+ final String formatted = getFormattedDigits(data, normalizedNumber, mCurrentCountryIso);
+ if (!TextUtils.isEmpty(formatted)) {
+ Editable digits = mDigits.getText();
+ digits.replace(0, digits.length(), formatted);
+ // for some reason this isn't getting called in the digits.replace call above..
+ // but in any case, this will make sure the background drawable looks right
+ afterTextChanged(digits);
+ }
+ }
+
+ private void configureKeypadListeners(View fragmentView) {
+ final int[] buttonIds =
+ new int[] {
+ R.id.one,
+ R.id.two,
+ R.id.three,
+ R.id.four,
+ R.id.five,
+ R.id.six,
+ R.id.seven,
+ R.id.eight,
+ R.id.nine,
+ R.id.star,
+ R.id.zero,
+ R.id.pound
+ };
+
+ DialpadKeyButton dialpadKey;
+
+ for (int i = 0; i < buttonIds.length; i++) {
+ dialpadKey = (DialpadKeyButton) fragmentView.findViewById(buttonIds[i]);
+ dialpadKey.setOnPressedListener(this);
+ }
+
+ // Long-pressing one button will initiate Voicemail.
+ final DialpadKeyButton one = (DialpadKeyButton) fragmentView.findViewById(R.id.one);
+ one.setOnLongClickListener(this);
+
+ // Long-pressing zero button will enter '+' instead.
+ final DialpadKeyButton zero = (DialpadKeyButton) fragmentView.findViewById(R.id.zero);
+ zero.setOnLongClickListener(this);
+ }
+
+ @Override
+ public void onStart() {
+ Trace.beginSection(TAG + " onStart");
+ super.onStart();
+ // if the mToneGenerator creation fails, just continue without it. It is
+ // a local audio signal, and is not as important as the dtmf tone itself.
+ final long start = System.currentTimeMillis();
+ synchronized (mToneGeneratorLock) {
+ if (mToneGenerator == null) {
+ try {
+ mToneGenerator = new ToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME);
+ } catch (RuntimeException e) {
+ LogUtil.e(
+ "DialpadFragment.onStart",
+ "Exception caught while creating local tone generator: " + e);
+ mToneGenerator = null;
+ }
+ }
+ }
+ final long total = System.currentTimeMillis() - start;
+ if (total > 50) {
+ LogUtil.i("DialpadFragment.onStart", "Time for ToneGenerator creation: " + total);
+ }
+ Trace.endSection();
+ }
+
+ @Override
+ public void onResume() {
+ Trace.beginSection(TAG + " onResume");
+ super.onResume();
+
+ final DialtactsActivity activity = (DialtactsActivity) getActivity();
+ mDialpadQueryListener = activity;
+
+ final StopWatch stopWatch = StopWatch.start("Dialpad.onResume");
+
+ // Query the last dialed number. Do it first because hitting
+ // the DB is 'slow'. This call is asynchronous.
+ queryLastOutgoingCall();
+
+ stopWatch.lap("qloc");
+
+ final ContentResolver contentResolver = activity.getContentResolver();
+
+ // retrieve the DTMF tone play back setting.
+ mDTMFToneEnabled =
+ Settings.System.getInt(contentResolver, Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
+
+ stopWatch.lap("dtwd");
+
+ stopWatch.lap("hptc");
+
+ mPressedDialpadKeys.clear();
+
+ configureScreenFromIntent(getActivity());
+
+ stopWatch.lap("fdin");
+
+ if (!isPhoneInUse()) {
+ // A sanity-check: the "dialpad chooser" UI should not be visible if the phone is idle.
+ showDialpadChooser(false);
+ }
+
+ stopWatch.lap("hnt");
+
+ updateDeleteButtonEnabledState();
+
+ stopWatch.lap("bes");
+
+ stopWatch.stopAndLog(TAG, 50);
+
+ // Populate the overflow menu in onResume instead of onCreate, so that if the SMS activity
+ // is disabled while Dialer is paused, the "Send a text message" option can be correctly
+ // removed when resumed.
+ mOverflowMenuButton = mDialpadView.getOverflowMenuButton();
+ mOverflowPopupMenu = buildOptionsMenu(mOverflowMenuButton);
+ mOverflowMenuButton.setOnTouchListener(mOverflowPopupMenu.getDragToOpenListener());
+ mOverflowMenuButton.setOnClickListener(this);
+ mOverflowMenuButton.setVisibility(isDigitsEmpty() ? View.INVISIBLE : View.VISIBLE);
+
+ if (mFirstLaunch) {
+ // The onHiddenChanged callback does not get called the first time the fragment is
+ // attached, so call it ourselves here.
+ onHiddenChanged(false);
+ }
+
+ mFirstLaunch = false;
+ Trace.endSection();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ // Make sure we don't leave this activity with a tone still playing.
+ stopTone();
+ mPressedDialpadKeys.clear();
+
+ // TODO: I wonder if we should not check if the AsyncTask that
+ // lookup the last dialed number has completed.
+ mLastNumberDialed = EMPTY_NUMBER; // Since we are going to query again, free stale number.
+
+ SpecialCharSequenceMgr.cleanup();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+
+ synchronized (mToneGeneratorLock) {
+ if (mToneGenerator != null) {
+ mToneGenerator.release();
+ mToneGenerator = null;
+ }
+ }
+
+ if (mClearDigitsOnStop) {
+ mClearDigitsOnStop = false;
+ clearDialpad();
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(PREF_DIGITS_FILLED_BY_INTENT, mDigitsFilledByIntent);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mPseudoEmergencyAnimator != null) {
+ mPseudoEmergencyAnimator.destroy();
+ mPseudoEmergencyAnimator = null;
+ }
+ getActivity().unregisterReceiver(mCallStateReceiver);
+ }
+
+ private void keyPressed(int keyCode) {
+ if (getView() == null || getView().getTranslationY() != 0) {
+ return;
+ }
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_1:
+ playTone(ToneGenerator.TONE_DTMF_1, TONE_LENGTH_INFINITE);
+ break;
+ case KeyEvent.KEYCODE_2:
+ playTone(ToneGenerator.TONE_DTMF_2, TONE_LENGTH_INFINITE);
+ break;
+ case KeyEvent.KEYCODE_3:
+ playTone(ToneGenerator.TONE_DTMF_3, TONE_LENGTH_INFINITE);
+ break;
+ case KeyEvent.KEYCODE_4:
+ playTone(ToneGenerator.TONE_DTMF_4, TONE_LENGTH_INFINITE);
+ break;
+ case KeyEvent.KEYCODE_5:
+ playTone(ToneGenerator.TONE_DTMF_5, TONE_LENGTH_INFINITE);
+ break;
+ case KeyEvent.KEYCODE_6:
+ playTone(ToneGenerator.TONE_DTMF_6, TONE_LENGTH_INFINITE);
+ break;
+ case KeyEvent.KEYCODE_7:
+ playTone(ToneGenerator.TONE_DTMF_7, TONE_LENGTH_INFINITE);
+ break;
+ case KeyEvent.KEYCODE_8:
+ playTone(ToneGenerator.TONE_DTMF_8, TONE_LENGTH_INFINITE);
+ break;
+ case KeyEvent.KEYCODE_9:
+ playTone(ToneGenerator.TONE_DTMF_9, TONE_LENGTH_INFINITE);
+ break;
+ case KeyEvent.KEYCODE_0:
+ playTone(ToneGenerator.TONE_DTMF_0, TONE_LENGTH_INFINITE);
+ break;
+ case KeyEvent.KEYCODE_POUND:
+ playTone(ToneGenerator.TONE_DTMF_P, TONE_LENGTH_INFINITE);
+ break;
+ case KeyEvent.KEYCODE_STAR:
+ playTone(ToneGenerator.TONE_DTMF_S, TONE_LENGTH_INFINITE);
+ break;
+ default:
+ break;
+ }
+
+ getView().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+ KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
+ mDigits.onKeyDown(keyCode, event);
+
+ // If the cursor is at the end of the text we hide it.
+ final int length = mDigits.length();
+ if (length == mDigits.getSelectionStart() && length == mDigits.getSelectionEnd()) {
+ mDigits.setCursorVisible(false);
+ }
+ }
+
+ @Override
+ public boolean onKey(View view, int keyCode, KeyEvent event) {
+ if (view.getId() == R.id.digits) {
+ if (keyCode == KeyEvent.KEYCODE_ENTER) {
+ handleDialButtonPressed();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * When a key is pressed, we start playing DTMF tone, do vibration, and enter the digit
+ * immediately. When a key is released, we stop the tone. Note that the "key press" event will be
+ * delivered by the system with certain amount of delay, it won't be synced with user's actual
+ * "touch-down" behavior.
+ */
+ @Override
+ public void onPressed(View view, boolean pressed) {
+ if (DEBUG) {
+ LogUtil.d("DialpadFragment.onPressed", "view: " + view + ", pressed: " + pressed);
+ }
+ if (pressed) {
+ int resId = view.getId();
+ if (resId == R.id.one) {
+ keyPressed(KeyEvent.KEYCODE_1);
+ } else if (resId == R.id.two) {
+ keyPressed(KeyEvent.KEYCODE_2);
+ } else if (resId == R.id.three) {
+ keyPressed(KeyEvent.KEYCODE_3);
+ } else if (resId == R.id.four) {
+ keyPressed(KeyEvent.KEYCODE_4);
+ } else if (resId == R.id.five) {
+ keyPressed(KeyEvent.KEYCODE_5);
+ } else if (resId == R.id.six) {
+ keyPressed(KeyEvent.KEYCODE_6);
+ } else if (resId == R.id.seven) {
+ keyPressed(KeyEvent.KEYCODE_7);
+ } else if (resId == R.id.eight) {
+ keyPressed(KeyEvent.KEYCODE_8);
+ } else if (resId == R.id.nine) {
+ keyPressed(KeyEvent.KEYCODE_9);
+ } else if (resId == R.id.zero) {
+ keyPressed(KeyEvent.KEYCODE_0);
+ } else if (resId == R.id.pound) {
+ keyPressed(KeyEvent.KEYCODE_POUND);
+ } else if (resId == R.id.star) {
+ keyPressed(KeyEvent.KEYCODE_STAR);
+ } else {
+ LogUtil.e(
+ "DialpadFragment.onPressed", "Unexpected onTouch(ACTION_DOWN) event from: " + view);
+ }
+ mPressedDialpadKeys.add(view);
+ } else {
+ mPressedDialpadKeys.remove(view);
+ if (mPressedDialpadKeys.isEmpty()) {
+ stopTone();
+ }
+ }
+ }
+
+ /**
+ * Called by the containing Activity to tell this Fragment to build an overflow options menu for
+ * display by the container when appropriate.
+ *
+ * @param invoker the View that invoked the options menu, to act as an anchor location.
+ */
+ private PopupMenu buildOptionsMenu(View invoker) {
+ final PopupMenu popupMenu =
+ new PopupMenu(getActivity(), invoker) {
+ @Override
+ public void show() {
+ final Menu menu = getMenu();
+
+ boolean enable = !isDigitsEmpty();
+ for (int i = 0; i < menu.size(); i++) {
+ MenuItem item = menu.getItem(i);
+ item.setEnabled(enable);
+ if (item.getItemId() == R.id.menu_call_with_note) {
+ item.setVisible(CallUtil.isCallWithSubjectSupported(getContext()));
+ }
+ }
+ super.show();
+ }
+ };
+ popupMenu.inflate(R.menu.dialpad_options);
+ popupMenu.setOnMenuItemClickListener(this);
+ return popupMenu;
+ }
+
+ @Override
+ public void onClick(View view) {
+ int resId = view.getId();
+ if (resId == R.id.dialpad_floating_action_button) {
+ view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+ handleDialButtonPressed();
+ } else if (resId == R.id.deleteButton) {
+ keyPressed(KeyEvent.KEYCODE_DEL);
+ } else if (resId == R.id.digits) {
+ if (!isDigitsEmpty()) {
+ mDigits.setCursorVisible(true);
+ }
+ } else if (resId == R.id.dialpad_overflow) {
+ mOverflowPopupMenu.show();
+ } else {
+ LogUtil.w("DialpadFragment.onClick", "Unexpected event from: " + view);
+ return;
+ }
+ }
+
+ @Override
+ public boolean onLongClick(View view) {
+ final Editable digits = mDigits.getText();
+ final int id = view.getId();
+ if (id == R.id.deleteButton) {
+ digits.clear();
+ return true;
+ } else if (id == R.id.one) {
+ if (isDigitsEmpty() || TextUtils.equals(mDigits.getText(), "1")) {
+ // We'll try to initiate voicemail and thus we want to remove irrelevant string.
+ removePreviousDigitIfPossible('1');
+
+ List<PhoneAccountHandle> subscriptionAccountHandles =
+ PhoneAccountUtils.getSubscriptionPhoneAccounts(getActivity());
+ boolean hasUserSelectedDefault =
+ subscriptionAccountHandles.contains(
+ TelecomUtil.getDefaultOutgoingPhoneAccount(
+ getActivity(), PhoneAccount.SCHEME_VOICEMAIL));
+ boolean needsAccountDisambiguation =
+ subscriptionAccountHandles.size() > 1 && !hasUserSelectedDefault;
+
+ if (needsAccountDisambiguation || isVoicemailAvailable()) {
+ // On a multi-SIM phone, if the user has not selected a default
+ // subscription, initiate a call to voicemail so they can select an account
+ // from the "Call with" dialog.
+ callVoicemail();
+ } else if (getActivity() != null) {
+ // Voicemail is unavailable maybe because Airplane mode is turned on.
+ // Check the current status and show the most appropriate error message.
+ final boolean isAirplaneModeOn =
+ Settings.System.getInt(
+ getActivity().getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0)
+ != 0;
+ if (isAirplaneModeOn) {
+ DialogFragment dialogFragment =
+ ErrorDialogFragment.newInstance(R.string.dialog_voicemail_airplane_mode_message);
+ dialogFragment.show(getFragmentManager(), "voicemail_request_during_airplane_mode");
+ } else {
+ DialogFragment dialogFragment =
+ ErrorDialogFragment.newInstance(R.string.dialog_voicemail_not_ready_message);
+ dialogFragment.show(getFragmentManager(), "voicemail_not_ready");
+ }
+ }
+ return true;
+ }
+ return false;
+ } else if (id == R.id.zero) {
+ if (mPressedDialpadKeys.contains(view)) {
+ // If the zero key is currently pressed, then the long press occurred by touch
+ // (and not via other means like certain accessibility input methods).
+ // Remove the '0' that was input when the key was first pressed.
+ removePreviousDigitIfPossible('0');
+ }
+ keyPressed(KeyEvent.KEYCODE_PLUS);
+ stopTone();
+ mPressedDialpadKeys.remove(view);
+ return true;
+ } else if (id == R.id.digits) {
+ mDigits.setCursorVisible(true);
+ return false;
+ }
+ return false;
+ }
+
+ /**
+ * Remove the digit just before the current position of the cursor, iff the following conditions
+ * are true: 1) The cursor is not positioned at index 0. 2) The digit before the current cursor
+ * position matches the current digit.
+ *
+ * @param digit to remove from the digits view.
+ */
+ private void removePreviousDigitIfPossible(char digit) {
+ final int currentPosition = mDigits.getSelectionStart();
+ if (currentPosition > 0 && digit == mDigits.getText().charAt(currentPosition - 1)) {
+ mDigits.setSelection(currentPosition);
+ mDigits.getText().delete(currentPosition - 1, currentPosition);
+ }
+ }
+
+ public void callVoicemail() {
+ DialerUtils.startActivityWithErrorToast(
+ getActivity(),
+ new CallIntentBuilder(CallUtil.getVoicemailUri(), CallInitiationType.Type.DIALPAD).build());
+ hideAndClearDialpad(false);
+ }
+
+ private void hideAndClearDialpad(boolean animate) {
+ ((DialtactsActivity) getActivity()).hideDialpadFragment(animate, true);
+ }
+
+ /**
+ * In most cases, when the dial button is pressed, there is a number in digits area. Pack it in
+ * the intent, start the outgoing call broadcast as a separate task and finish this activity.
+ *
+ * <p>When there is no digit and the phone is CDMA and off hook, we're sending a blank flash for
+ * CDMA. CDMA networks use Flash messages when special processing needs to be done, mainly for
+ * 3-way or call waiting scenarios. Presumably, here we're in a special 3-way scenario where the
+ * network needs a blank flash before being able to add the new participant. (This is not the case
+ * with all 3-way calls, just certain CDMA infrastructures.)
+ *
+ * <p>Otherwise, there is no digit, display the last dialed number. Don't finish since the user
+ * may want to edit it. The user needs to press the dial button again, to dial it (general case
+ * described above).
+ */
+ private void handleDialButtonPressed() {
+ if (isDigitsEmpty()) { // No number entered.
+ handleDialButtonClickWithEmptyDigits();
+ } else {
+ final String number = mDigits.getText().toString();
+
+ // "persist.radio.otaspdial" is a temporary hack needed for one carrier's automated
+ // test equipment.
+ // TODO: clean it up.
+ if (number != null
+ && !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
+ && number.matches(mProhibitedPhoneNumberRegexp)) {
+ LogUtil.i(
+ "DialpadFragment.handleDialButtonPressed",
+ "The phone number is prohibited explicitly by a rule.");
+ if (getActivity() != null) {
+ DialogFragment dialogFragment =
+ ErrorDialogFragment.newInstance(R.string.dialog_phone_call_prohibited_message);
+ dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
+ }
+
+ // Clear the digits just in case.
+ clearDialpad();
+ } else {
+ final Intent intent =
+ new CallIntentBuilder(number, CallInitiationType.Type.DIALPAD).build();
+ DialerUtils.startActivityWithErrorToast(getActivity(), intent);
+ hideAndClearDialpad(false);
+ }
+ }
+ }
+
+ public void clearDialpad() {
+ if (mDigits != null) {
+ mDigits.getText().clear();
+ }
+ }
+
+ private void handleDialButtonClickWithEmptyDigits() {
+ if (phoneIsCdma() && isPhoneInUse()) {
+ // TODO: Move this logic into services/Telephony
+ //
+ // This is really CDMA specific. On GSM is it possible
+ // to be off hook and wanted to add a 3rd party using
+ // the redial feature.
+ startActivity(newFlashIntent());
+ } else {
+ if (!TextUtils.isEmpty(mLastNumberDialed)) {
+ // Recall the last number dialed.
+ mDigits.setText(mLastNumberDialed);
+
+ // ...and move the cursor to the end of the digits string,
+ // so you'll be able to delete digits using the Delete
+ // button (just as if you had typed the number manually.)
+ //
+ // Note we use mDigits.getText().length() here, not
+ // mLastNumberDialed.length(), since the EditText widget now
+ // contains a *formatted* version of mLastNumberDialed (due to
+ // mTextWatcher) and its length may have changed.
+ mDigits.setSelection(mDigits.getText().length());
+ } else {
+ // There's no "last number dialed" or the
+ // background query is still running. There's
+ // nothing useful for the Dial button to do in
+ // this case. Note: with a soft dial button, this
+ // can never happens since the dial button is
+ // disabled under these conditons.
+ playTone(ToneGenerator.TONE_PROP_NACK);
+ }
+ }
+ }
+
+ /** Plays the specified tone for TONE_LENGTH_MS milliseconds. */
+ private void playTone(int tone) {
+ playTone(tone, TONE_LENGTH_MS);
+ }
+
+ /**
+ * Play the specified tone for the specified milliseconds
+ *
+ * <p>The tone is played locally, using the audio stream for phone calls. Tones are played only if
+ * the "Audible touch tones" user preference is checked, and are NOT played if the device is in
+ * silent mode.
+ *
+ * <p>The tone length can be -1, meaning "keep playing the tone." If the caller does so, it should
+ * call stopTone() afterward.
+ *
+ * @param tone a tone code from {@link ToneGenerator}
+ * @param durationMs tone length.
+ */
+ private void playTone(int tone, int durationMs) {
+ // if local tone playback is disabled, just return.
+ if (!mDTMFToneEnabled) {
+ return;
+ }
+
+ // Also do nothing if the phone is in silent mode.
+ // We need to re-check the ringer mode for *every* playTone()
+ // call, rather than keeping a local flag that's updated in
+ // onResume(), since it's possible to toggle silent mode without
+ // leaving the current activity (via the ENDCALL-longpress menu.)
+ AudioManager audioManager =
+ (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int ringerMode = audioManager.getRingerMode();
+ if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
+ || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
+ return;
+ }
+
+ synchronized (mToneGeneratorLock) {
+ if (mToneGenerator == null) {
+ LogUtil.w("DialpadFragment.playTone", "mToneGenerator == null, tone: " + tone);
+ return;
+ }
+
+ // Start the new tone (will stop any playing tone)
+ mToneGenerator.startTone(tone, durationMs);
+ }
+ }
+
+ /** Stop the tone if it is played. */
+ private void stopTone() {
+ // if local tone playback is disabled, just return.
+ if (!mDTMFToneEnabled) {
+ return;
+ }
+ synchronized (mToneGeneratorLock) {
+ if (mToneGenerator == null) {
+ LogUtil.w("DialpadFragment.stopTone", "mToneGenerator == null");
+ return;
+ }
+ mToneGenerator.stopTone();
+ }
+ }
+
+ /**
+ * Brings up the "dialpad chooser" UI in place of the usual Dialer elements (the textfield/button
+ * and the dialpad underneath).
+ *
+ * <p>We show this UI if the user brings up the Dialer while a call is already in progress, since
+ * there's a good chance we got here accidentally (and the user really wanted the in-call dialpad
+ * instead). So in this situation we display an intermediate UI that lets the user explicitly
+ * choose between the in-call dialpad ("Use touch tone keypad") and the regular Dialer ("Add
+ * call"). (Or, the option "Return to call in progress" just goes back to the in-call UI with no
+ * dialpad at all.)
+ *
+ * @param enabled If true, show the "dialpad chooser" instead of the regular Dialer UI
+ */
+ private void showDialpadChooser(boolean enabled) {
+ if (getActivity() == null) {
+ return;
+ }
+ // Check if onCreateView() is already called by checking one of View objects.
+ if (!isLayoutReady()) {
+ return;
+ }
+
+ if (enabled) {
+ LogUtil.i("DialpadFragment.showDialpadChooser", "Showing dialpad chooser!");
+ if (mDialpadView != null) {
+ mDialpadView.setVisibility(View.GONE);
+ }
+
+ mFloatingActionButtonController.setVisible(false);
+ mDialpadChooser.setVisibility(View.VISIBLE);
+
+ // Instantiate the DialpadChooserAdapter and hook it up to the
+ // ListView. We do this only once.
+ if (mDialpadChooserAdapter == null) {
+ mDialpadChooserAdapter = new DialpadChooserAdapter(getActivity());
+ }
+ mDialpadChooser.setAdapter(mDialpadChooserAdapter);
+ } else {
+ LogUtil.i("DialpadFragment.showDialpadChooser", "Displaying normal Dialer UI.");
+ if (mDialpadView != null) {
+ mDialpadView.setVisibility(View.VISIBLE);
+ } else {
+ mDigits.setVisibility(View.VISIBLE);
+ }
+
+ // mFloatingActionButtonController must also be 'scaled in', in order to be visible after
+ // 'scaleOut()' hidden method.
+ if (!mFloatingActionButtonController.isVisible()) {
+ // Just call 'scaleIn()' method if the mFloatingActionButtonController was not already
+ // previously visible.
+ mFloatingActionButtonController.scaleIn(0);
+ mFloatingActionButtonController.setVisible(true);
+ }
+ mDialpadChooser.setVisibility(View.GONE);
+ }
+ }
+
+ /** @return true if we're currently showing the "dialpad chooser" UI. */
+ private boolean isDialpadChooserVisible() {
+ return mDialpadChooser.getVisibility() == View.VISIBLE;
+ }
+
+ /** Handle clicks from the dialpad chooser. */
+ @Override
+ public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+ DialpadChooserAdapter.ChoiceItem item =
+ (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
+ int itemId = item.id;
+ if (itemId == DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD) {
+ // Fire off an intent to go back to the in-call UI
+ // with the dialpad visible.
+ returnToInCallScreen(true);
+ } else if (itemId == DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL) {
+ // Fire off an intent to go back to the in-call UI
+ // (with the dialpad hidden).
+ returnToInCallScreen(false);
+ } else if (itemId == DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL) {
+ // Ok, guess the user really did want to be here (in the
+ // regular Dialer) after all. Bring back the normal Dialer UI.
+ showDialpadChooser(false);
+ } else {
+ LogUtil.w("DialpadFragment.onItemClick", "Unexpected itemId: " + itemId);
+ }
+ }
+
+ /**
+ * Returns to the in-call UI (where there's presumably a call in progress) in response to the user
+ * selecting "use touch tone keypad" or "return to call" from the dialpad chooser.
+ */
+ private void returnToInCallScreen(boolean showDialpad) {
+ TelecomUtil.showInCallScreen(getActivity(), showDialpad);
+
+ // Finally, finish() ourselves so that we don't stay on the
+ // activity stack.
+ // Note that we do this whether or not the showCallScreenWithDialpad()
+ // call above had any effect or not! (That call is a no-op if the
+ // phone is idle, which can happen if the current call ends while
+ // the dialpad chooser is up. In this case we can't show the
+ // InCallScreen, and there's no point staying here in the Dialer,
+ // so we just take the user back where he came from...)
+ getActivity().finish();
+ }
+
+ /**
+ * @return true if the phone is "in use", meaning that at least one line is active (ie. off hook
+ * or ringing or dialing, or on hold).
+ */
+ private boolean isPhoneInUse() {
+ final Context context = getActivity();
+ if (context != null) {
+ return TelecomUtil.isInCall(context);
+ }
+ return false;
+ }
+
+ /** @return true if the phone is a CDMA phone type */
+ private boolean phoneIsCdma() {
+ return getTelephonyManager().getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA;
+ }
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ int resId = item.getItemId();
+ if (resId == R.id.menu_2s_pause) {
+ updateDialString(PAUSE);
+ return true;
+ } else if (resId == R.id.menu_add_wait) {
+ updateDialString(WAIT);
+ return true;
+ } else if (resId == R.id.menu_call_with_note) {
+ CallSubjectDialog.start(getActivity(), mDigits.getText().toString());
+ hideAndClearDialpad(false);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Updates the dial string (mDigits) after inserting a Pause character (,) or Wait character (;).
+ */
+ private void updateDialString(char newDigit) {
+ if (newDigit != WAIT && newDigit != PAUSE) {
+ throw new IllegalArgumentException("Not expected for anything other than PAUSE & WAIT");
+ }
+
+ int selectionStart;
+ int selectionEnd;
+
+ // SpannableStringBuilder editable_text = new SpannableStringBuilder(mDigits.getText());
+ int anchor = mDigits.getSelectionStart();
+ int point = mDigits.getSelectionEnd();
+
+ selectionStart = Math.min(anchor, point);
+ selectionEnd = Math.max(anchor, point);
+
+ if (selectionStart == -1) {
+ selectionStart = selectionEnd = mDigits.length();
+ }
+
+ Editable digits = mDigits.getText();
+
+ if (canAddDigit(digits, selectionStart, selectionEnd, newDigit)) {
+ digits.replace(selectionStart, selectionEnd, Character.toString(newDigit));
+
+ if (selectionStart != selectionEnd) {
+ // Unselect: back to a regular cursor, just pass the character inserted.
+ mDigits.setSelection(selectionStart + 1);
+ }
+ }
+ }
+
+ /** Update the enabledness of the "Dial" and "Backspace" buttons if applicable. */
+ private void updateDeleteButtonEnabledState() {
+ if (getActivity() == null) {
+ return;
+ }
+ final boolean digitsNotEmpty = !isDigitsEmpty();
+ mDelete.setEnabled(digitsNotEmpty);
+ }
+
+ /**
+ * Handle transitions for the menu button depending on the state of the digits edit text.
+ * Transition out when going from digits to no digits and transition in when the first digit is
+ * pressed.
+ *
+ * @param transitionIn True if transitioning in, False if transitioning out
+ */
+ private void updateMenuOverflowButton(boolean transitionIn) {
+ mOverflowMenuButton = mDialpadView.getOverflowMenuButton();
+ if (transitionIn) {
+ AnimUtils.fadeIn(mOverflowMenuButton, AnimUtils.DEFAULT_DURATION);
+ } else {
+ AnimUtils.fadeOut(mOverflowMenuButton, AnimUtils.DEFAULT_DURATION);
+ }
+ }
+
+ /**
+ * Check if voicemail is enabled/accessible.
+ *
+ * @return true if voicemail is enabled and accessible. Note that this can be false "temporarily"
+ * after the app boot.
+ */
+ private boolean isVoicemailAvailable() {
+ try {
+ PhoneAccountHandle defaultUserSelectedAccount =
+ TelecomUtil.getDefaultOutgoingPhoneAccount(getActivity(), PhoneAccount.SCHEME_VOICEMAIL);
+ if (defaultUserSelectedAccount == null) {
+ // In a single-SIM phone, there is no default outgoing phone account selected by
+ // the user, so just call TelephonyManager#getVoicemailNumber directly.
+ return !TextUtils.isEmpty(getTelephonyManager().getVoiceMailNumber());
+ } else {
+ return !TextUtils.isEmpty(
+ TelecomUtil.getVoicemailNumber(getActivity(), defaultUserSelectedAccount));
+ }
+ } catch (SecurityException se) {
+ // Possibly no READ_PHONE_STATE privilege.
+ LogUtil.w(
+ "DialpadFragment.isVoicemailAvailable",
+ "SecurityException is thrown. Maybe privilege isn't sufficient.");
+ }
+ return false;
+ }
+
+ /** @return true if the widget with the phone number digits is empty. */
+ private boolean isDigitsEmpty() {
+ return mDigits.length() == 0;
+ }
+
+ /**
+ * Starts the asyn query to get the last dialed/outgoing number. When the background query
+ * finishes, mLastNumberDialed is set to the last dialed number or an empty string if none exists
+ * yet.
+ */
+ private void queryLastOutgoingCall() {
+ mLastNumberDialed = EMPTY_NUMBER;
+ if (ContextCompat.checkSelfPermission(getActivity(), permission.READ_CALL_LOG)
+ != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ CallLogAsync.GetLastOutgoingCallArgs lastCallArgs =
+ new CallLogAsync.GetLastOutgoingCallArgs(
+ getActivity(),
+ new CallLogAsync.OnLastOutgoingCallComplete() {
+ @Override
+ public void lastOutgoingCall(String number) {
+ // TODO: Filter out emergency numbers if
+ // the carrier does not want redial for
+ // these.
+ // If the fragment has already been detached since the last time
+ // we called queryLastOutgoingCall in onResume there is no point
+ // doing anything here.
+ if (getActivity() == null) {
+ return;
+ }
+ mLastNumberDialed = number;
+ updateDeleteButtonEnabledState();
+ }
+ });
+ mCallLog.getLastOutgoingCall(lastCallArgs);
+ }
+
+ private Intent newFlashIntent() {
+ Intent intent = new CallIntentBuilder(EMPTY_NUMBER, CallInitiationType.Type.DIALPAD).build();
+ intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
+ return intent;
+ }
+
+ @Override
+ public void onHiddenChanged(boolean hidden) {
+ super.onHiddenChanged(hidden);
+ final DialtactsActivity activity = (DialtactsActivity) getActivity();
+ final DialpadView dialpadView = (DialpadView) getView().findViewById(R.id.dialpad_view);
+ if (activity == null) {
+ return;
+ }
+ if (!hidden && !isDialpadChooserVisible()) {
+ if (mAnimate) {
+ dialpadView.animateShow();
+ }
+ mFloatingActionButtonController.setVisible(false);
+ mFloatingActionButtonController.scaleIn(mAnimate ? mDialpadSlideInDuration : 0);
+ activity.onDialpadShown();
+ mDigits.requestFocus();
+ }
+ if (hidden) {
+ if (mAnimate) {
+ mFloatingActionButtonController.scaleOut();
+ } else {
+ mFloatingActionButtonController.setVisible(false);
+ }
+ }
+ }
+
+ public boolean getAnimate() {
+ return mAnimate;
+ }
+
+ public void setAnimate(boolean value) {
+ mAnimate = value;
+ }
+
+ public void setYFraction(float yFraction) {
+ ((DialpadSlidingRelativeLayout) getView()).setYFraction(yFraction);
+ }
+
+ public int getDialpadHeight() {
+ if (mDialpadView == null) {
+ return 0;
+ }
+ return mDialpadView.getHeight();
+ }
+
+ public void process_quote_emergency_unquote(String query) {
+ if (PseudoEmergencyAnimator.PSEUDO_EMERGENCY_NUMBER.equals(query)) {
+ if (mPseudoEmergencyAnimator == null) {
+ mPseudoEmergencyAnimator =
+ new PseudoEmergencyAnimator(
+ new PseudoEmergencyAnimator.ViewProvider() {
+ @Override
+ public View getView() {
+ return DialpadFragment.this.getView();
+ }
+ });
+ }
+ mPseudoEmergencyAnimator.start();
+ } else {
+ if (mPseudoEmergencyAnimator != null) {
+ mPseudoEmergencyAnimator.end();
+ }
+ }
+ }
+
+ public interface OnDialpadQueryChangedListener {
+
+ void onDialpadQueryChanged(String query);
+ }
+
+ public interface HostInterface {
+
+ /**
+ * Notifies the parent activity that the space above the dialpad has been tapped with no query
+ * in the dialpad present. In most situations this will cause the dialpad to be dismissed,
+ * unless there happens to be content showing.
+ */
+ boolean onDialpadSpacerTouchWithEmptyQuery();
+ }
+
+ /**
+ * LinearLayout with getter and setter methods for the translationY property using floats, for
+ * animation purposes.
+ */
+ public static class DialpadSlidingRelativeLayout extends RelativeLayout {
+
+ public DialpadSlidingRelativeLayout(Context context) {
+ super(context);
+ }
+
+ public DialpadSlidingRelativeLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public DialpadSlidingRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @UsedByReflection(value = "dialpad_fragment.xml")
+ public float getYFraction() {
+ final int height = getHeight();
+ if (height == 0) {
+ return 0;
+ }
+ return getTranslationY() / height;
+ }
+
+ @UsedByReflection(value = "dialpad_fragment.xml")
+ public void setYFraction(float yFraction) {
+ setTranslationY(yFraction * getHeight());
+ }
+ }
+
+ public static class ErrorDialogFragment extends DialogFragment {
+
+ private static final String ARG_TITLE_RES_ID = "argTitleResId";
+ private static final String ARG_MESSAGE_RES_ID = "argMessageResId";
+ private int mTitleResId;
+ private int mMessageResId;
+
+ public static ErrorDialogFragment newInstance(int messageResId) {
+ return newInstance(0, messageResId);
+ }
+
+ public static ErrorDialogFragment newInstance(int titleResId, int messageResId) {
+ final ErrorDialogFragment fragment = new ErrorDialogFragment();
+ final Bundle args = new Bundle();
+ args.putInt(ARG_TITLE_RES_ID, titleResId);
+ args.putInt(ARG_MESSAGE_RES_ID, messageResId);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mTitleResId = getArguments().getInt(ARG_TITLE_RES_ID);
+ mMessageResId = getArguments().getInt(ARG_MESSAGE_RES_ID);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ if (mTitleResId != 0) {
+ builder.setTitle(mTitleResId);
+ }
+ if (mMessageResId != 0) {
+ builder.setMessage(mMessageResId);
+ }
+ builder.setPositiveButton(
+ android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ }
+ });
+ return builder.create();
+ }
+ }
+
+ /**
+ * Simple list adapter, binding to an icon + text label for each item in the "dialpad chooser"
+ * list.
+ */
+ private static class DialpadChooserAdapter extends BaseAdapter {
+
+ // IDs for the possible "choices":
+ static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
+ static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
+ static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
+ private static final int NUM_ITEMS = 3;
+ private LayoutInflater mInflater;
+ private ChoiceItem[] mChoiceItems = new ChoiceItem[NUM_ITEMS];
+
+ public DialpadChooserAdapter(Context context) {
+ // Cache the LayoutInflate to avoid asking for a new one each time.
+ mInflater = LayoutInflater.from(context);
+
+ // Initialize the possible choices.
+ // TODO: could this be specified entirely in XML?
+
+ // - "Use touch tone keypad"
+ mChoiceItems[0] =
+ new ChoiceItem(
+ context.getString(R.string.dialer_useDtmfDialpad),
+ BitmapFactory.decodeResource(
+ context.getResources(), R.drawable.ic_dialer_fork_tt_keypad),
+ DIALPAD_CHOICE_USE_DTMF_DIALPAD);
+
+ // - "Return to call in progress"
+ mChoiceItems[1] =
+ new ChoiceItem(
+ context.getString(R.string.dialer_returnToInCallScreen),
+ BitmapFactory.decodeResource(
+ context.getResources(), R.drawable.ic_dialer_fork_current_call),
+ DIALPAD_CHOICE_RETURN_TO_CALL);
+
+ // - "Add call"
+ mChoiceItems[2] =
+ new ChoiceItem(
+ context.getString(R.string.dialer_addAnotherCall),
+ BitmapFactory.decodeResource(
+ context.getResources(), R.drawable.ic_dialer_fork_add_call),
+ DIALPAD_CHOICE_ADD_NEW_CALL);
+ }
+
+ @Override
+ public int getCount() {
+ return NUM_ITEMS;
+ }
+
+ /** Return the ChoiceItem for a given position. */
+ @Override
+ public Object getItem(int position) {
+ return mChoiceItems[position];
+ }
+
+ /** Return a unique ID for each possible choice. */
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ /** Make a view for each row. */
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // When convertView is non-null, we can reuse it (there's no need
+ // to reinflate it.)
+ if (convertView == null) {
+ convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
+ }
+
+ TextView text = (TextView) convertView.findViewById(R.id.text);
+ text.setText(mChoiceItems[position].text);
+
+ ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
+ icon.setImageBitmap(mChoiceItems[position].icon);
+
+ return convertView;
+ }
+
+ // Simple struct for a single "choice" item.
+ static class ChoiceItem {
+
+ String text;
+ Bitmap icon;
+ int id;
+
+ public ChoiceItem(String s, Bitmap b, int i) {
+ text = s;
+ icon = b;
+ id = i;
+ }
+ }
+ }
+
+ private class CallStateReceiver extends BroadcastReceiver {
+
+ /**
+ * Receive call state changes so that we can take down the "dialpad chooser" if the phone
+ * becomes idle while the chooser UI is visible.
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
+ if ((TextUtils.equals(state, TelephonyManager.EXTRA_STATE_IDLE)
+ || TextUtils.equals(state, TelephonyManager.EXTRA_STATE_OFFHOOK))
+ && isDialpadChooserVisible()) {
+ // Note there's a race condition in the UI here: the
+ // dialpad chooser could conceivably disappear (on its
+ // own) at the exact moment the user was trying to select
+ // one of the choices, which would be confusing. (But at
+ // least that's better than leaving the dialpad chooser
+ // onscreen, but useless...)
+ showDialpadChooser(false);
+ }
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/dialpad/PseudoEmergencyAnimator.java b/java/com/android/dialer/app/dialpad/PseudoEmergencyAnimator.java
new file mode 100644
index 000000000..2ffacb6d8
--- /dev/null
+++ b/java/com/android/dialer/app/dialpad/PseudoEmergencyAnimator.java
@@ -0,0 +1,161 @@
+/*
+ * 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.dialer.app.dialpad;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.ArgbEvaluator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.LightingColorFilter;
+import android.os.Handler;
+import android.os.Vibrator;
+import android.view.View;
+import com.android.dialer.app.R;
+
+/** Animates the dial button on "emergency" phone numbers. */
+public class PseudoEmergencyAnimator {
+
+ public static final String PSEUDO_EMERGENCY_NUMBER = "01189998819991197253";
+ private static final int VIBRATE_LENGTH_MILLIS = 200;
+ private static final int ITERATION_LENGTH_MILLIS = 1000;
+ private static final int ANIMATION_ITERATION_COUNT = 6;
+ private ViewProvider mViewProvider;
+ private ValueAnimator mPseudoEmergencyColorAnimator;
+
+ PseudoEmergencyAnimator(ViewProvider viewProvider) {
+ mViewProvider = viewProvider;
+ }
+
+ public void destroy() {
+ end();
+ mViewProvider = null;
+ }
+
+ public void start() {
+ if (mPseudoEmergencyColorAnimator == null) {
+ Integer colorFrom = Color.BLUE;
+ Integer colorTo = Color.RED;
+ mPseudoEmergencyColorAnimator =
+ ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
+
+ mPseudoEmergencyColorAnimator.addUpdateListener(
+ new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ try {
+ int color = (int) animator.getAnimatedValue();
+ ColorFilter colorFilter = new LightingColorFilter(Color.BLACK, color);
+
+ View floatingActionButtonContainer =
+ getView().findViewById(R.id.dialpad_floating_action_button_container);
+ if (floatingActionButtonContainer != null) {
+ floatingActionButtonContainer.getBackground().setColorFilter(colorFilter);
+ }
+ } catch (Exception e) {
+ animator.cancel();
+ }
+ }
+ });
+
+ mPseudoEmergencyColorAnimator.addListener(
+ new AnimatorListener() {
+ @Override
+ public void onAnimationCancel(Animator animation) {}
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ try {
+ vibrate(VIBRATE_LENGTH_MILLIS);
+ } catch (Exception e) {
+ animation.cancel();
+ }
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {}
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ try {
+ View floatingActionButtonContainer =
+ getView().findViewById(R.id.dialpad_floating_action_button_container);
+ if (floatingActionButtonContainer != null) {
+ floatingActionButtonContainer.getBackground().clearColorFilter();
+ }
+
+ new Handler()
+ .postDelayed(
+ new Runnable() {
+ @Override
+ public void run() {
+ try {
+ vibrate(VIBRATE_LENGTH_MILLIS);
+ } catch (Exception e) {
+ // ignored
+ }
+ }
+ },
+ ITERATION_LENGTH_MILLIS);
+ } catch (Exception e) {
+ animation.cancel();
+ }
+ }
+ });
+
+ mPseudoEmergencyColorAnimator.setDuration(VIBRATE_LENGTH_MILLIS);
+ mPseudoEmergencyColorAnimator.setRepeatMode(ValueAnimator.REVERSE);
+ mPseudoEmergencyColorAnimator.setRepeatCount(ANIMATION_ITERATION_COUNT);
+ }
+ if (!mPseudoEmergencyColorAnimator.isStarted()) {
+ mPseudoEmergencyColorAnimator.start();
+ }
+ }
+
+ public void end() {
+ if (mPseudoEmergencyColorAnimator != null && mPseudoEmergencyColorAnimator.isStarted()) {
+ mPseudoEmergencyColorAnimator.end();
+ }
+ }
+
+ private View getView() {
+ return mViewProvider == null ? null : mViewProvider.getView();
+ }
+
+ private Context getContext() {
+ View view = getView();
+ return view != null ? view.getContext() : null;
+ }
+
+ private void vibrate(long milliseconds) {
+ Context context = getContext();
+ if (context != null) {
+ Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+ if (vibrator != null) {
+ vibrator.vibrate(milliseconds);
+ }
+ }
+ }
+
+ public interface ViewProvider {
+
+ View getView();
+ }
+}
diff --git a/java/com/android/dialer/app/dialpad/SmartDialCursorLoader.java b/java/com/android/dialer/app/dialpad/SmartDialCursorLoader.java
new file mode 100644
index 000000000..f3a93f916
--- /dev/null
+++ b/java/com/android/dialer/app/dialpad/SmartDialCursorLoader.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2013 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.dialer.app.dialpad;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.util.Log;
+import com.android.contacts.common.list.PhoneNumberListAdapter.PhoneQuery;
+import com.android.dialer.database.Database;
+import com.android.dialer.database.DialerDatabaseHelper;
+import com.android.dialer.database.DialerDatabaseHelper.ContactNumber;
+import com.android.dialer.smartdial.SmartDialNameMatcher;
+import com.android.dialer.smartdial.SmartDialPrefix;
+import com.android.dialer.util.PermissionsUtil;
+import java.util.ArrayList;
+
+/** Implements a Loader<Cursor> class to asynchronously load SmartDial search results. */
+public class SmartDialCursorLoader extends AsyncTaskLoader<Cursor> {
+
+ private static final String TAG = "SmartDialCursorLoader";
+ private static final boolean DEBUG = false;
+
+ private final Context mContext;
+
+ private Cursor mCursor;
+
+ private String mQuery;
+ private SmartDialNameMatcher mNameMatcher;
+
+ private ForceLoadContentObserver mObserver;
+
+ private boolean mShowEmptyListForNullQuery = true;
+
+ public SmartDialCursorLoader(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ /**
+ * Configures the query string to be used to find SmartDial matches.
+ *
+ * @param query The query string user typed.
+ */
+ public void configureQuery(String query) {
+ if (DEBUG) {
+ Log.v(TAG, "Configure new query to be " + query);
+ }
+ mQuery = SmartDialNameMatcher.normalizeNumber(query, SmartDialPrefix.getMap());
+
+ /** Constructs a name matcher object for matching names. */
+ mNameMatcher = new SmartDialNameMatcher(mQuery, SmartDialPrefix.getMap());
+ mNameMatcher.setShouldMatchEmptyQuery(!mShowEmptyListForNullQuery);
+ }
+
+ /**
+ * Queries the SmartDial database and loads results in background.
+ *
+ * @return Cursor of contacts that matches the SmartDial query.
+ */
+ @Override
+ public Cursor loadInBackground() {
+ if (DEBUG) {
+ Log.v(TAG, "Load in background " + mQuery);
+ }
+
+ if (!PermissionsUtil.hasContactsPermissions(mContext)) {
+ return new MatrixCursor(PhoneQuery.PROJECTION_PRIMARY);
+ }
+
+ /** Loads results from the database helper. */
+ final DialerDatabaseHelper dialerDatabaseHelper =
+ Database.get(mContext).getDatabaseHelper(mContext);
+ final ArrayList<ContactNumber> allMatches =
+ dialerDatabaseHelper.getLooseMatches(mQuery, mNameMatcher);
+
+ if (DEBUG) {
+ Log.v(TAG, "Loaded matches " + String.valueOf(allMatches.size()));
+ }
+
+ /** Constructs a cursor for the returned array of results. */
+ final MatrixCursor cursor = new MatrixCursor(PhoneQuery.PROJECTION_PRIMARY);
+ Object[] row = new Object[PhoneQuery.PROJECTION_PRIMARY.length];
+ for (ContactNumber contact : allMatches) {
+ row[PhoneQuery.PHONE_ID] = contact.dataId;
+ row[PhoneQuery.PHONE_NUMBER] = contact.phoneNumber;
+ row[PhoneQuery.CONTACT_ID] = contact.id;
+ row[PhoneQuery.LOOKUP_KEY] = contact.lookupKey;
+ row[PhoneQuery.PHOTO_ID] = contact.photoId;
+ row[PhoneQuery.DISPLAY_NAME] = contact.displayName;
+ row[PhoneQuery.CARRIER_PRESENCE] = contact.carrierPresence;
+ cursor.addRow(row);
+ }
+ return cursor;
+ }
+
+ @Override
+ public void deliverResult(Cursor cursor) {
+ if (isReset()) {
+ /** The Loader has been reset; ignore the result and invalidate the data. */
+ releaseResources(cursor);
+ return;
+ }
+
+ /** Hold a reference to the old data so it doesn't get garbage collected. */
+ Cursor oldCursor = mCursor;
+ mCursor = cursor;
+
+ if (mObserver == null) {
+ mObserver = new ForceLoadContentObserver();
+ mContext
+ .getContentResolver()
+ .registerContentObserver(DialerDatabaseHelper.SMART_DIAL_UPDATED_URI, true, mObserver);
+ }
+
+ if (isStarted()) {
+ /** If the Loader is in a started state, deliver the results to the client. */
+ super.deliverResult(cursor);
+ }
+
+ /** Invalidate the old data as we don't need it any more. */
+ if (oldCursor != null && oldCursor != cursor) {
+ releaseResources(oldCursor);
+ }
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mCursor != null) {
+ /** Deliver any previously loaded data immediately. */
+ deliverResult(mCursor);
+ }
+ if (mCursor == null) {
+ /** Force loads every time as our results change with queries. */
+ forceLoad();
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ /** The Loader is in a stopped state, so we should attempt to cancel the current load. */
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ /** Ensure the loader has been stopped. */
+ onStopLoading();
+
+ if (mObserver != null) {
+ mContext.getContentResolver().unregisterContentObserver(mObserver);
+ mObserver = null;
+ }
+
+ /** Release all previously saved query results. */
+ if (mCursor != null) {
+ releaseResources(mCursor);
+ mCursor = null;
+ }
+ }
+
+ @Override
+ public void onCanceled(Cursor cursor) {
+ super.onCanceled(cursor);
+
+ if (mObserver != null) {
+ mContext.getContentResolver().unregisterContentObserver(mObserver);
+ mObserver = null;
+ }
+
+ /** The load has been canceled, so we should release the resources associated with 'data'. */
+ releaseResources(cursor);
+ }
+
+ private void releaseResources(Cursor cursor) {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ public void setShowEmptyListForNullQuery(boolean show) {
+ mShowEmptyListForNullQuery = show;
+ if (mNameMatcher != null) {
+ mNameMatcher.setShouldMatchEmptyQuery(!show);
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/dialpad/UnicodeDialerKeyListener.java b/java/com/android/dialer/app/dialpad/UnicodeDialerKeyListener.java
new file mode 100644
index 000000000..051daf46e
--- /dev/null
+++ b/java/com/android/dialer/app/dialpad/UnicodeDialerKeyListener.java
@@ -0,0 +1,56 @@
+/*
+ * 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.dialer.app.dialpad;
+
+import android.telephony.PhoneNumberUtils;
+import android.text.Spanned;
+import android.text.method.DialerKeyListener;
+
+/**
+ * {@link DialerKeyListener} with Unicode support. Converts any Unicode(e.g. Arabic) characters that
+ * represent digits into digits before filtering the results so that we can support pasted digits
+ * from Unicode languages.
+ */
+public class UnicodeDialerKeyListener extends DialerKeyListener {
+
+ public static final UnicodeDialerKeyListener INSTANCE = new UnicodeDialerKeyListener();
+
+ @Override
+ public CharSequence filter(
+ CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
+ final String converted =
+ PhoneNumberUtils.convertKeypadLettersToDigits(
+ PhoneNumberUtils.replaceUnicodeDigits(source.toString()));
+ // PhoneNumberUtils.replaceUnicodeDigits performs a character for character replacement,
+ // so we can assume that start and end positions should remain unchanged.
+ CharSequence result = super.filter(converted, start, end, dest, dstart, dend);
+ if (result == null) {
+ if (source.equals(converted)) {
+ // There was no conversion or filtering performed. Just return null according to
+ // the behavior of DialerKeyListener.
+ return null;
+ } else {
+ // filter returns null if the charsequence is to be returned unchanged/unfiltered.
+ // But in this case we do want to return a modified character string (even if
+ // none of the characters in the modified string are filtered). So if
+ // result == null we return the unfiltered but converted numeric string instead.
+ return converted.subSequence(start, end);
+ }
+ }
+ return result;
+ }
+}
diff --git a/java/com/android/dialer/app/filterednumber/BlockedNumbersAdapter.java b/java/com/android/dialer/app/filterednumber/BlockedNumbersAdapter.java
new file mode 100644
index 000000000..b9381331c
--- /dev/null
+++ b/java/com/android/dialer/app/filterednumber/BlockedNumbersAdapter.java
@@ -0,0 +1,97 @@
+/*
+ * 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.dialer.app.filterednumber;
+
+import android.app.FragmentManager;
+import android.content.Context;
+import android.database.Cursor;
+import android.telephony.PhoneNumberUtils;
+import android.view.View;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.GeoUtil;
+import com.android.dialer.app.R;
+import com.android.dialer.blocking.BlockNumberDialogFragment;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.InteractionEvent;
+import com.android.dialer.phonenumbercache.ContactInfoHelper;
+
+public class BlockedNumbersAdapter extends NumbersAdapter {
+
+ private BlockedNumbersAdapter(
+ Context context,
+ FragmentManager fragmentManager,
+ ContactInfoHelper contactInfoHelper,
+ ContactPhotoManager contactPhotoManager) {
+ super(context, fragmentManager, contactInfoHelper, contactPhotoManager);
+ }
+
+ public static BlockedNumbersAdapter newBlockedNumbersAdapter(
+ Context context, FragmentManager fragmentManager) {
+ return new BlockedNumbersAdapter(
+ context,
+ fragmentManager,
+ new ContactInfoHelper(context, GeoUtil.getCurrentCountryIso(context)),
+ ContactPhotoManager.getInstance(context));
+ }
+
+ @Override
+ public void bindView(View view, final Context context, Cursor cursor) {
+ super.bindView(view, context, cursor);
+ final Integer id = cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns._ID));
+ final String countryIso =
+ cursor.getString(cursor.getColumnIndex(FilteredNumberColumns.COUNTRY_ISO));
+ final String number = cursor.getString(cursor.getColumnIndex(FilteredNumberColumns.NUMBER));
+ final String normalizedNumber =
+ cursor.getString(cursor.getColumnIndex(FilteredNumberColumns.NORMALIZED_NUMBER));
+
+ final View deleteButton = view.findViewById(R.id.delete_button);
+ deleteButton.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ BlockNumberDialogFragment.show(
+ id,
+ number,
+ countryIso,
+ PhoneNumberUtils.formatNumber(number, countryIso),
+ R.id.blocked_numbers_activity_container,
+ getFragmentManager(),
+ new BlockNumberDialogFragment.Callback() {
+ @Override
+ public void onFilterNumberSuccess() {}
+
+ @Override
+ public void onUnfilterNumberSuccess() {
+ Logger.get(context)
+ .logInteraction(InteractionEvent.Type.UNBLOCK_NUMBER_MANAGEMENT_SCREEN);
+ }
+
+ @Override
+ public void onChangeFilteredNumberUndo() {}
+ });
+ }
+ });
+
+ updateView(view, number, countryIso);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ // Always return false, so that the header with blocking-related options always shows.
+ return false;
+ }
+}
diff --git a/java/com/android/dialer/app/filterednumber/BlockedNumbersFragment.java b/java/com/android/dialer/app/filterednumber/BlockedNumbersFragment.java
new file mode 100644
index 000000000..f53a45840
--- /dev/null
+++ b/java/com/android/dialer/app/filterednumber/BlockedNumbersFragment.java
@@ -0,0 +1,271 @@
+/*
+ * 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.dialer.app.filterednumber;
+
+import android.app.ListFragment;
+import android.app.LoaderManager;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.Loader;
+import android.database.Cursor;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.android.contacts.common.lettertiles.LetterTileDrawable;
+import com.android.dialer.app.R;
+import com.android.dialer.blocking.BlockedNumbersMigrator;
+import com.android.dialer.blocking.BlockedNumbersMigrator.Listener;
+import com.android.dialer.blocking.FilteredNumberCompat;
+import com.android.dialer.blocking.FilteredNumbersUtil;
+import com.android.dialer.blocking.FilteredNumbersUtil.CheckForSendToVoicemailContactListener;
+import com.android.dialer.blocking.FilteredNumbersUtil.ImportSendToVoicemailContactsListener;
+import com.android.dialer.database.FilteredNumberContract;
+import com.android.dialer.voicemailstatus.VisualVoicemailEnabledChecker;
+
+public class BlockedNumbersFragment extends ListFragment
+ implements LoaderManager.LoaderCallbacks<Cursor>,
+ View.OnClickListener,
+ VisualVoicemailEnabledChecker.Callback {
+
+ private static final char ADD_BLOCKED_NUMBER_ICON_LETTER = '+';
+ protected View migratePromoView;
+ private BlockedNumbersMigrator blockedNumbersMigratorForTest;
+ private TextView blockedNumbersText;
+ private TextView footerText;
+ private BlockedNumbersAdapter mAdapter;
+ private VisualVoicemailEnabledChecker mVoicemailEnabledChecker;
+ private View mImportSettings;
+ private View mBlockedNumbersDisabledForEmergency;
+ private View mBlockedNumberListDivider;
+
+ @Override
+ public Context getContext() {
+ return getActivity();
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ LayoutInflater inflater =
+ (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ getListView().addHeaderView(inflater.inflate(R.layout.blocked_number_header, null));
+ getListView().addFooterView(inflater.inflate(R.layout.blocked_number_footer, null));
+ //replace the icon for add number with LetterTileDrawable(), so it will have identical style
+ ImageView addNumberIcon = (ImageView) getActivity().findViewById(R.id.add_number_icon);
+ LetterTileDrawable drawable = new LetterTileDrawable(getResources());
+ drawable.setLetter(ADD_BLOCKED_NUMBER_ICON_LETTER);
+ drawable.setColor(
+ ActivityCompat.getColor(getActivity(), R.color.add_blocked_number_icon_color));
+ drawable.setIsCircular(true);
+ addNumberIcon.setImageDrawable(drawable);
+
+ if (mAdapter == null) {
+ mAdapter =
+ BlockedNumbersAdapter.newBlockedNumbersAdapter(
+ getContext(), getActivity().getFragmentManager());
+ }
+ setListAdapter(mAdapter);
+
+ blockedNumbersText = (TextView) getListView().findViewById(R.id.blocked_number_text_view);
+ migratePromoView = getListView().findViewById(R.id.migrate_promo);
+ getListView().findViewById(R.id.migrate_promo_allow_button).setOnClickListener(this);
+ mImportSettings = getListView().findViewById(R.id.import_settings);
+ mBlockedNumbersDisabledForEmergency =
+ getListView().findViewById(R.id.blocked_numbers_disabled_for_emergency);
+ mBlockedNumberListDivider = getActivity().findViewById(R.id.blocked_number_list_divider);
+ getListView().findViewById(R.id.import_button).setOnClickListener(this);
+ getListView().findViewById(R.id.view_numbers_button).setOnClickListener(this);
+ getListView().findViewById(R.id.add_number_linear_layout).setOnClickListener(this);
+
+ footerText = (TextView) getActivity().findViewById(R.id.blocked_number_footer_textview);
+ mVoicemailEnabledChecker = new VisualVoicemailEnabledChecker(getContext(), this);
+ mVoicemailEnabledChecker.asyncUpdate();
+ updateActiveVoicemailProvider();
+ }
+
+ @Override
+ public void onDestroy() {
+ setListAdapter(null);
+ super.onDestroy();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
+ ColorDrawable backgroundDrawable =
+ new ColorDrawable(ActivityCompat.getColor(getActivity(), R.color.dialer_theme_color));
+ actionBar.setBackgroundDrawable(backgroundDrawable);
+ actionBar.setDisplayShowCustomEnabled(false);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ actionBar.setDisplayShowTitleEnabled(true);
+ actionBar.setTitle(R.string.manage_blocked_numbers_label);
+
+ // If the device can use the framework blocking solution, users should not be able to add
+ // new blocked numbers from the Blocked Management UI. They will be shown a promo card
+ // asking them to migrate to new blocking instead.
+ if (FilteredNumberCompat.canUseNewFiltering()) {
+ migratePromoView.setVisibility(View.VISIBLE);
+ blockedNumbersText.setVisibility(View.GONE);
+ getListView().findViewById(R.id.add_number_linear_layout).setVisibility(View.GONE);
+ getListView().findViewById(R.id.add_number_linear_layout).setOnClickListener(null);
+ mBlockedNumberListDivider.setVisibility(View.GONE);
+ mImportSettings.setVisibility(View.GONE);
+ getListView().findViewById(R.id.import_button).setOnClickListener(null);
+ getListView().findViewById(R.id.view_numbers_button).setOnClickListener(null);
+ mBlockedNumbersDisabledForEmergency.setVisibility(View.GONE);
+ footerText.setVisibility(View.GONE);
+ } else {
+ FilteredNumbersUtil.checkForSendToVoicemailContact(
+ getActivity(),
+ new CheckForSendToVoicemailContactListener() {
+ @Override
+ public void onComplete(boolean hasSendToVoicemailContact) {
+ final int visibility = hasSendToVoicemailContact ? View.VISIBLE : View.GONE;
+ mImportSettings.setVisibility(visibility);
+ }
+ });
+ }
+
+ // All views except migrate and the block list are hidden when new filtering is available
+ if (!FilteredNumberCompat.canUseNewFiltering()
+ && FilteredNumbersUtil.hasRecentEmergencyCall(getContext())) {
+ mBlockedNumbersDisabledForEmergency.setVisibility(View.VISIBLE);
+ } else {
+ mBlockedNumbersDisabledForEmergency.setVisibility(View.GONE);
+ }
+
+ mVoicemailEnabledChecker.asyncUpdate();
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.blocked_number_fragment, container, false);
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ final String[] projection = {
+ FilteredNumberContract.FilteredNumberColumns._ID,
+ FilteredNumberContract.FilteredNumberColumns.COUNTRY_ISO,
+ FilteredNumberContract.FilteredNumberColumns.NUMBER,
+ FilteredNumberContract.FilteredNumberColumns.NORMALIZED_NUMBER
+ };
+ final String selection =
+ FilteredNumberContract.FilteredNumberColumns.TYPE
+ + "="
+ + FilteredNumberContract.FilteredNumberTypes.BLOCKED_NUMBER;
+ return new CursorLoader(
+ getContext(),
+ FilteredNumberContract.FilteredNumber.CONTENT_URI,
+ projection,
+ selection,
+ null,
+ null);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ mAdapter.swapCursor(data);
+ if (FilteredNumberCompat.canUseNewFiltering() || data.getCount() == 0) {
+ mBlockedNumberListDivider.setVisibility(View.INVISIBLE);
+ } else {
+ mBlockedNumberListDivider.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mAdapter.swapCursor(null);
+ }
+
+ @Override
+ public void onClick(final View view) {
+ final BlockedNumbersSettingsActivity activity = (BlockedNumbersSettingsActivity) getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ int resId = view.getId();
+ if (resId == R.id.add_number_linear_layout) {
+ activity.showSearchUi();
+ } else if (resId == R.id.view_numbers_button) {
+ activity.showNumbersToImportPreviewUi();
+ } else if (resId == R.id.import_button) {
+ FilteredNumbersUtil.importSendToVoicemailContacts(
+ activity,
+ new ImportSendToVoicemailContactsListener() {
+ @Override
+ public void onImportComplete() {
+ mImportSettings.setVisibility(View.GONE);
+ }
+ });
+ } else if (resId == R.id.migrate_promo_allow_button) {
+ view.setEnabled(false);
+ (blockedNumbersMigratorForTest != null
+ ? blockedNumbersMigratorForTest
+ : new BlockedNumbersMigrator(getContext()))
+ .migrate(
+ new Listener() {
+ @Override
+ public void onComplete() {
+ getContext()
+ .startActivity(
+ FilteredNumberCompat.createManageBlockedNumbersIntent(getContext()));
+ // Remove this activity from the backstack
+ activity.finish();
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onVisualVoicemailEnabledStatusChanged(boolean newStatus) {
+ updateActiveVoicemailProvider();
+ }
+
+ private void updateActiveVoicemailProvider() {
+ if (getActivity() == null || getActivity().isFinishing()) {
+ return;
+ }
+ if (mVoicemailEnabledChecker.isVisualVoicemailEnabled()) {
+ footerText.setText(R.string.block_number_footer_message_vvm);
+ } else {
+ footerText.setText(R.string.block_number_footer_message_no_vvm);
+ }
+ }
+
+ void setBlockedNumbersMigratorForTest(BlockedNumbersMigrator blockedNumbersMigrator) {
+ blockedNumbersMigratorForTest = blockedNumbersMigrator;
+ }
+}
diff --git a/java/com/android/dialer/app/filterednumber/BlockedNumbersSettingsActivity.java b/java/com/android/dialer/app/filterednumber/BlockedNumbersSettingsActivity.java
new file mode 100644
index 000000000..eef920710
--- /dev/null
+++ b/java/com/android/dialer/app/filterednumber/BlockedNumbersSettingsActivity.java
@@ -0,0 +1,146 @@
+/*
+ * 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.dialer.app.filterednumber;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.MenuItem;
+import com.android.dialer.app.R;
+import com.android.dialer.app.list.BlockedListSearchFragment;
+import com.android.dialer.app.list.SearchFragment;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.ScreenEvent;
+
+public class BlockedNumbersSettingsActivity extends AppCompatActivity
+ implements SearchFragment.HostInterface {
+
+ private static final String TAG_BLOCKED_MANAGEMENT_FRAGMENT = "blocked_management";
+ private static final String TAG_BLOCKED_SEARCH_FRAGMENT = "blocked_search";
+ private static final String TAG_VIEW_NUMBERS_TO_IMPORT_FRAGMENT = "view_numbers_to_import";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.blocked_numbers_activity);
+
+ // If savedInstanceState != null, the Activity will automatically restore the last fragment.
+ if (savedInstanceState == null) {
+ showManagementUi();
+ }
+ }
+
+ /** Shows fragment with the list of currently blocked numbers and settings related to blocking. */
+ public void showManagementUi() {
+ BlockedNumbersFragment fragment =
+ (BlockedNumbersFragment)
+ getFragmentManager().findFragmentByTag(TAG_BLOCKED_MANAGEMENT_FRAGMENT);
+ if (fragment == null) {
+ fragment = new BlockedNumbersFragment();
+ }
+
+ getFragmentManager()
+ .beginTransaction()
+ .replace(R.id.blocked_numbers_activity_container, fragment, TAG_BLOCKED_MANAGEMENT_FRAGMENT)
+ .commit();
+
+ Logger.get(this).logScreenView(ScreenEvent.Type.BLOCKED_NUMBER_MANAGEMENT, this);
+ }
+
+ /** Shows fragment with search UI for browsing/finding numbers to block. */
+ public void showSearchUi() {
+ BlockedListSearchFragment fragment =
+ (BlockedListSearchFragment)
+ getFragmentManager().findFragmentByTag(TAG_BLOCKED_SEARCH_FRAGMENT);
+ if (fragment == null) {
+ fragment = new BlockedListSearchFragment();
+ fragment.setHasOptionsMenu(false);
+ fragment.setShowEmptyListForNullQuery(true);
+ fragment.setDirectorySearchEnabled(false);
+ }
+
+ getFragmentManager()
+ .beginTransaction()
+ .replace(R.id.blocked_numbers_activity_container, fragment, TAG_BLOCKED_SEARCH_FRAGMENT)
+ .addToBackStack(null)
+ .commit();
+
+ Logger.get(this).logScreenView(ScreenEvent.Type.BLOCKED_NUMBER_ADD_NUMBER, this);
+ }
+
+ /**
+ * Shows fragment with UI to preview the numbers of contacts currently marked as send-to-voicemail
+ * in Contacts. These numbers can be imported into Dialer's blocked number list.
+ */
+ public void showNumbersToImportPreviewUi() {
+ ViewNumbersToImportFragment fragment =
+ (ViewNumbersToImportFragment)
+ getFragmentManager().findFragmentByTag(TAG_VIEW_NUMBERS_TO_IMPORT_FRAGMENT);
+ if (fragment == null) {
+ fragment = new ViewNumbersToImportFragment();
+ }
+
+ getFragmentManager()
+ .beginTransaction()
+ .replace(
+ R.id.blocked_numbers_activity_container, fragment, TAG_VIEW_NUMBERS_TO_IMPORT_FRAGMENT)
+ .addToBackStack(null)
+ .commit();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onBackPressed() {
+ // TODO: Achieve back navigation without overriding onBackPressed.
+ if (getFragmentManager().getBackStackEntryCount() > 0) {
+ getFragmentManager().popBackStack();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public boolean isActionBarShowing() {
+ return false;
+ }
+
+ @Override
+ public boolean isDialpadShown() {
+ return false;
+ }
+
+ @Override
+ public int getDialpadHeight() {
+ return 0;
+ }
+
+ @Override
+ public int getActionBarHideOffset() {
+ return 0;
+ }
+
+ @Override
+ public int getActionBarHeight() {
+ return 0;
+ }
+}
diff --git a/java/com/android/dialer/app/filterednumber/NumbersAdapter.java b/java/com/android/dialer/app/filterednumber/NumbersAdapter.java
new file mode 100644
index 000000000..f71517a44
--- /dev/null
+++ b/java/com/android/dialer/app/filterednumber/NumbersAdapter.java
@@ -0,0 +1,138 @@
+/*
+ * 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.dialer.app.filterednumber;
+
+import android.app.FragmentManager;
+import android.content.Context;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.QuickContactBadge;
+import android.widget.SimpleCursorAdapter;
+import android.widget.TextView;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.util.UriUtils;
+import com.android.dialer.app.R;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.phonenumbercache.ContactInfo;
+import com.android.dialer.phonenumbercache.ContactInfoHelper;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+
+public class NumbersAdapter extends SimpleCursorAdapter {
+
+ private Context mContext;
+ private FragmentManager mFragmentManager;
+ private ContactInfoHelper mContactInfoHelper;
+ private BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
+ private ContactPhotoManager mContactPhotoManager;
+
+ public NumbersAdapter(
+ Context context,
+ FragmentManager fragmentManager,
+ ContactInfoHelper contactInfoHelper,
+ ContactPhotoManager contactPhotoManager) {
+ super(context, R.layout.blocked_number_item, null, new String[] {}, new int[] {}, 0);
+ mContext = context;
+ mFragmentManager = fragmentManager;
+ mContactInfoHelper = contactInfoHelper;
+ mContactPhotoManager = contactPhotoManager;
+ }
+
+ public void updateView(View view, String number, String countryIso) {
+ final TextView callerName = (TextView) view.findViewById(R.id.caller_name);
+ final TextView callerNumber = (TextView) view.findViewById(R.id.caller_number);
+ final QuickContactBadge quickContactBadge =
+ (QuickContactBadge) view.findViewById(R.id.quick_contact_photo);
+ quickContactBadge.setOverlay(null);
+ if (CompatUtils.hasPrioritizedMimeType()) {
+ quickContactBadge.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE);
+ }
+
+ ContactInfo info = mContactInfoHelper.lookupNumber(number, countryIso);
+ if (info == null) {
+ info = new ContactInfo();
+ info.number = number;
+ }
+ final CharSequence locationOrType = getNumberTypeOrLocation(info);
+ final String displayNumber = getDisplayNumber(info);
+ final String displayNumberStr =
+ mBidiFormatter.unicodeWrap(displayNumber, TextDirectionHeuristics.LTR);
+
+ String nameForDefaultImage;
+ if (!TextUtils.isEmpty(info.name)) {
+ nameForDefaultImage = info.name;
+ callerName.setText(info.name);
+ callerNumber.setText(locationOrType + " " + displayNumberStr);
+ } else {
+ nameForDefaultImage = displayNumber;
+ callerName.setText(displayNumberStr);
+ if (!TextUtils.isEmpty(locationOrType)) {
+ callerNumber.setText(locationOrType);
+ callerNumber.setVisibility(View.VISIBLE);
+ } else {
+ callerNumber.setVisibility(View.GONE);
+ }
+ }
+ loadContactPhoto(info, nameForDefaultImage, quickContactBadge);
+ }
+
+ private void loadContactPhoto(ContactInfo info, String displayName, QuickContactBadge badge) {
+ final String lookupKey =
+ info.lookupUri == null ? null : UriUtils.getLookupKeyFromUri(info.lookupUri);
+ final int contactType =
+ mContactInfoHelper.isBusiness(info.sourceType)
+ ? ContactPhotoManager.TYPE_BUSINESS
+ : ContactPhotoManager.TYPE_DEFAULT;
+ final DefaultImageRequest request =
+ new DefaultImageRequest(displayName, lookupKey, contactType, true /* isCircular */);
+ badge.assignContactUri(info.lookupUri);
+ badge.setContentDescription(
+ mContext.getResources().getString(R.string.description_contact_details, displayName));
+ mContactPhotoManager.loadDirectoryPhoto(
+ badge, info.photoUri, false /* darkTheme */, true /* isCircular */, request);
+ }
+
+ private String getDisplayNumber(ContactInfo info) {
+ if (!TextUtils.isEmpty(info.formattedNumber)) {
+ return info.formattedNumber;
+ } else if (!TextUtils.isEmpty(info.number)) {
+ return info.number;
+ } else {
+ return "";
+ }
+ }
+
+ private CharSequence getNumberTypeOrLocation(ContactInfo info) {
+ if (!TextUtils.isEmpty(info.name)) {
+ return ContactsContract.CommonDataKinds.Phone.getTypeLabel(
+ mContext.getResources(), info.type, info.label);
+ } else {
+ return PhoneNumberHelper.getGeoDescription(mContext, info.number);
+ }
+ }
+
+ protected Context getContext() {
+ return mContext;
+ }
+
+ protected FragmentManager getFragmentManager() {
+ return mFragmentManager;
+ }
+}
diff --git a/java/com/android/dialer/app/filterednumber/ViewNumbersToImportAdapter.java b/java/com/android/dialer/app/filterednumber/ViewNumbersToImportAdapter.java
new file mode 100644
index 000000000..5228a1d79
--- /dev/null
+++ b/java/com/android/dialer/app/filterednumber/ViewNumbersToImportAdapter.java
@@ -0,0 +1,56 @@
+/*
+ * 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.dialer.app.filterednumber;
+
+import android.app.FragmentManager;
+import android.content.Context;
+import android.database.Cursor;
+import android.view.View;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.GeoUtil;
+import com.android.dialer.app.R;
+import com.android.dialer.blocking.FilteredNumbersUtil;
+import com.android.dialer.phonenumbercache.ContactInfoHelper;
+
+public class ViewNumbersToImportAdapter extends NumbersAdapter {
+
+ private ViewNumbersToImportAdapter(
+ Context context,
+ FragmentManager fragmentManager,
+ ContactInfoHelper contactInfoHelper,
+ ContactPhotoManager contactPhotoManager) {
+ super(context, fragmentManager, contactInfoHelper, contactPhotoManager);
+ }
+
+ public static ViewNumbersToImportAdapter newViewNumbersToImportAdapter(
+ Context context, FragmentManager fragmentManager) {
+ return new ViewNumbersToImportAdapter(
+ context,
+ fragmentManager,
+ new ContactInfoHelper(context, GeoUtil.getCurrentCountryIso(context)),
+ ContactPhotoManager.getInstance(context));
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ super.bindView(view, context, cursor);
+
+ final String number = cursor.getString(FilteredNumbersUtil.PhoneQuery.NUMBER_COLUMN_INDEX);
+
+ view.findViewById(R.id.delete_button).setVisibility(View.GONE);
+ updateView(view, number, null /* countryIso */);
+ }
+}
diff --git a/java/com/android/dialer/app/filterednumber/ViewNumbersToImportFragment.java b/java/com/android/dialer/app/filterednumber/ViewNumbersToImportFragment.java
new file mode 100644
index 000000000..d45f61ed7
--- /dev/null
+++ b/java/com/android/dialer/app/filterednumber/ViewNumbersToImportFragment.java
@@ -0,0 +1,130 @@
+/*
+ * 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.dialer.app.filterednumber;
+
+import android.app.ListFragment;
+import android.app.LoaderManager;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.dialer.app.R;
+import com.android.dialer.blocking.FilteredNumbersUtil;
+import com.android.dialer.blocking.FilteredNumbersUtil.ImportSendToVoicemailContactsListener;
+
+public class ViewNumbersToImportFragment extends ListFragment
+ implements LoaderManager.LoaderCallbacks<Cursor>, View.OnClickListener {
+
+ private ViewNumbersToImportAdapter mAdapter;
+
+ @Override
+ public Context getContext() {
+ return getActivity();
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ if (mAdapter == null) {
+ mAdapter =
+ ViewNumbersToImportAdapter.newViewNumbersToImportAdapter(
+ getContext(), getActivity().getFragmentManager());
+ }
+ setListAdapter(mAdapter);
+ }
+
+ @Override
+ public void onDestroy() {
+ setListAdapter(null);
+ super.onDestroy();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
+ actionBar.setTitle(R.string.import_send_to_voicemail_numbers_label);
+ actionBar.setDisplayShowCustomEnabled(false);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ actionBar.setDisplayShowTitleEnabled(true);
+
+ getActivity().findViewById(R.id.cancel_button).setOnClickListener(this);
+ getActivity().findViewById(R.id.import_button).setOnClickListener(this);
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.view_numbers_to_import_fragment, container, false);
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ final CursorLoader cursorLoader =
+ new CursorLoader(
+ getContext(),
+ Phone.CONTENT_URI,
+ FilteredNumbersUtil.PhoneQuery.PROJECTION,
+ FilteredNumbersUtil.PhoneQuery.SELECT_SEND_TO_VOICEMAIL_TRUE,
+ null,
+ null);
+ return cursorLoader;
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ mAdapter.swapCursor(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mAdapter.swapCursor(null);
+ }
+
+ @Override
+ public void onClick(final View view) {
+ if (view.getId() == R.id.import_button) {
+ FilteredNumbersUtil.importSendToVoicemailContacts(
+ getContext(),
+ new ImportSendToVoicemailContactsListener() {
+ @Override
+ public void onImportComplete() {
+ if (getActivity() != null) {
+ getActivity().onBackPressed();
+ }
+ }
+ });
+ } else if (view.getId() == R.id.cancel_button) {
+ getActivity().onBackPressed();
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/legacybindings/DialerLegacyBindings.java b/java/com/android/dialer/app/legacybindings/DialerLegacyBindings.java
new file mode 100644
index 000000000..2125a1524
--- /dev/null
+++ b/java/com/android/dialer/app/legacybindings/DialerLegacyBindings.java
@@ -0,0 +1,47 @@
+/*
+ * 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.dialer.app.legacybindings;
+
+import android.app.Activity;
+import android.view.ViewGroup;
+import com.android.dialer.app.calllog.CallLogAdapter;
+import com.android.dialer.app.calllog.calllogcache.CallLogCache;
+import com.android.dialer.app.contactinfo.ContactInfoCache;
+import com.android.dialer.app.list.RegularSearchFragment;
+import com.android.dialer.app.voicemail.VoicemailPlaybackPresenter;
+
+/**
+ * These are old bindings between Dialer and the container application. All new bindings should be
+ * added to the bindings module and not here.
+ */
+public interface DialerLegacyBindings {
+
+ /**
+ * activityType must be one of following constants: CallLogAdapter.ACTIVITY_TYPE_CALL_LOG, or
+ * CallLogAdapter.ACTIVITY_TYPE_DIALTACTS.
+ */
+ CallLogAdapter newCallLogAdapter(
+ Activity activity,
+ ViewGroup alertContainer,
+ CallLogAdapter.CallFetcher callFetcher,
+ CallLogCache callLogCache,
+ ContactInfoCache contactInfoCache,
+ VoicemailPlaybackPresenter voicemailPlaybackPresenter,
+ int activityType);
+
+ RegularSearchFragment newRegularSearchFragment();
+}
diff --git a/java/com/android/dialer/app/legacybindings/DialerLegacyBindingsFactory.java b/java/com/android/dialer/app/legacybindings/DialerLegacyBindingsFactory.java
new file mode 100644
index 000000000..70d379c9f
--- /dev/null
+++ b/java/com/android/dialer/app/legacybindings/DialerLegacyBindingsFactory.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dialer.app.legacybindings;
+
+/**
+ * This interface should be implementated by the Application subclass. It allows the dialer module
+ * to get references to the DialerLegacyBindings.
+ */
+public interface DialerLegacyBindingsFactory {
+
+ DialerLegacyBindings newDialerLegacyBindings();
+}
diff --git a/java/com/android/dialer/app/legacybindings/DialerLegacyBindingsStub.java b/java/com/android/dialer/app/legacybindings/DialerLegacyBindingsStub.java
new file mode 100644
index 000000000..f01df78f8
--- /dev/null
+++ b/java/com/android/dialer/app/legacybindings/DialerLegacyBindingsStub.java
@@ -0,0 +1,53 @@
+/*
+ * 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.dialer.app.legacybindings;
+
+import android.app.Activity;
+import android.view.ViewGroup;
+import com.android.dialer.app.calllog.CallLogAdapter;
+import com.android.dialer.app.calllog.calllogcache.CallLogCache;
+import com.android.dialer.app.contactinfo.ContactInfoCache;
+import com.android.dialer.app.list.RegularSearchFragment;
+import com.android.dialer.app.voicemail.VoicemailPlaybackPresenter;
+
+/** Default implementation for dialer legacy bindings. */
+public class DialerLegacyBindingsStub implements DialerLegacyBindings {
+
+ @Override
+ public CallLogAdapter newCallLogAdapter(
+ Activity activity,
+ ViewGroup alertContainer,
+ CallLogAdapter.CallFetcher callFetcher,
+ CallLogCache callLogCache,
+ ContactInfoCache contactInfoCache,
+ VoicemailPlaybackPresenter voicemailPlaybackPresenter,
+ int activityType) {
+ return new CallLogAdapter(
+ activity,
+ alertContainer,
+ callFetcher,
+ callLogCache,
+ contactInfoCache,
+ voicemailPlaybackPresenter,
+ activityType);
+ }
+
+ @Override
+ public RegularSearchFragment newRegularSearchFragment() {
+ return new RegularSearchFragment();
+ }
+}
diff --git a/java/com/android/dialer/app/list/AllContactsFragment.java b/java/com/android/dialer/app/list/AllContactsFragment.java
new file mode 100644
index 000000000..093e8f384
--- /dev/null
+++ b/java/com/android/dialer/app/list/AllContactsFragment.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2013 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.dialer.app.list;
+
+import static android.Manifest.permission.READ_CONTACTS;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.Loader;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.QuickContact;
+import android.support.annotation.Nullable;
+import android.support.v13.app.FragmentCompat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import com.android.contacts.common.list.ContactEntryListAdapter;
+import com.android.contacts.common.list.ContactEntryListFragment;
+import com.android.contacts.common.list.ContactListFilter;
+import com.android.contacts.common.list.DefaultContactListAdapter;
+import com.android.contacts.common.util.FabUtil;
+import com.android.dialer.app.R;
+import com.android.dialer.app.list.ListsFragment.ListsPage;
+import com.android.dialer.app.widget.EmptyContentView;
+import com.android.dialer.app.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.IntentUtil;
+import com.android.dialer.util.PermissionsUtil;
+
+/** Fragments to show all contacts with phone numbers. */
+public class AllContactsFragment extends ContactEntryListFragment<ContactEntryListAdapter>
+ implements ListsPage,
+ OnEmptyViewActionButtonClickedListener,
+ FragmentCompat.OnRequestPermissionsResultCallback {
+
+ private static final int READ_CONTACTS_PERMISSION_REQUEST_CODE = 1;
+
+ private EmptyContentView mEmptyListView;
+
+ /**
+ * Listen to broadcast events about permissions in order to be notified if the READ_CONTACTS
+ * permission is granted via the UI in another fragment.
+ */
+ private BroadcastReceiver mReadContactsPermissionGrantedReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ reloadData();
+ }
+ };
+
+ public AllContactsFragment() {
+ setQuickContactEnabled(false);
+ setAdjustSelectionBoundsEnabled(true);
+ setPhotoLoaderEnabled(true);
+ setSectionHeaderDisplayEnabled(true);
+ setDarkTheme(false);
+ setVisibleScrollbarEnabled(true);
+ }
+
+ @Override
+ public void onViewCreated(View view, android.os.Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ mEmptyListView = (EmptyContentView) view.findViewById(R.id.empty_list_view);
+ mEmptyListView.setImage(R.drawable.empty_contacts);
+ mEmptyListView.setDescription(R.string.all_contacts_empty);
+ mEmptyListView.setActionClickedListener(this);
+ getListView().setEmptyView(mEmptyListView);
+ mEmptyListView.setVisibility(View.GONE);
+
+ FabUtil.addBottomPaddingToListViewForFab(getListView(), getResources());
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ PermissionsUtil.registerPermissionReceiver(
+ getActivity(), mReadContactsPermissionGrantedReceiver, READ_CONTACTS);
+ }
+
+ @Override
+ public void onStop() {
+ PermissionsUtil.unregisterPermissionReceiver(
+ getActivity(), mReadContactsPermissionGrantedReceiver);
+ super.onStop();
+ }
+
+ @Override
+ protected void startLoading() {
+ if (PermissionsUtil.hasPermission(getActivity(), READ_CONTACTS)) {
+ super.startLoading();
+ mEmptyListView.setDescription(R.string.all_contacts_empty);
+ mEmptyListView.setActionLabel(R.string.all_contacts_empty_add_contact_action);
+ } else {
+ mEmptyListView.setDescription(R.string.permission_no_contacts);
+ mEmptyListView.setActionLabel(R.string.permission_single_turn_on);
+ mEmptyListView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ super.onLoadFinished(loader, data);
+
+ if (data == null || data.getCount() == 0) {
+ mEmptyListView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ protected ContactEntryListAdapter createListAdapter() {
+ final DefaultContactListAdapter adapter =
+ new DefaultContactListAdapter(getActivity()) {
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ super.bindView(itemView, partition, cursor, position);
+ itemView.setTag(this.getContactUri(partition, cursor));
+ }
+ };
+ adapter.setDisplayPhotos(true);
+ adapter.setFilter(
+ ContactListFilter.createFilterWithType(ContactListFilter.FILTER_TYPE_DEFAULT));
+ adapter.setSectionHeaderDisplayEnabled(isSectionHeaderDisplayEnabled());
+ return adapter;
+ }
+
+ @Override
+ protected View inflateView(LayoutInflater inflater, ViewGroup container) {
+ return inflater.inflate(R.layout.all_contacts_fragment, null);
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ final Uri uri = (Uri) view.getTag();
+ if (uri != null) {
+ if (CompatUtils.hasPrioritizedMimeType()) {
+ QuickContact.showQuickContact(getContext(), view, uri, null, Phone.CONTENT_ITEM_TYPE);
+ } else {
+ QuickContact.showQuickContact(getActivity(), view, uri, QuickContact.MODE_LARGE, null);
+ }
+ }
+ }
+
+ @Override
+ protected void onItemClick(int position, long id) {
+ // Do nothing. Implemented to satisfy ContactEntryListFragment.
+ }
+
+ @Override
+ public void onEmptyViewActionButtonClicked() {
+ final Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ if (!PermissionsUtil.hasPermission(activity, READ_CONTACTS)) {
+ FragmentCompat.requestPermissions(
+ this, new String[] {READ_CONTACTS}, READ_CONTACTS_PERMISSION_REQUEST_CODE);
+ } else {
+ // Add new contact
+ DialerUtils.startActivityWithErrorToast(
+ activity, IntentUtil.getNewContactIntent(), R.string.add_contact_not_available);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, String[] permissions, int[] grantResults) {
+ if (requestCode == READ_CONTACTS_PERMISSION_REQUEST_CODE) {
+ if (grantResults.length >= 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
+ // Force a refresh of the data since we were missing the permission before this.
+ reloadData();
+ }
+ }
+ }
+
+ @Override
+ public void onPageResume(@Nullable Activity activity) {
+ LogUtil.i("AllContactsFragment.onPageResume", null);
+ }
+
+ @Override
+ public void onPagePause(@Nullable Activity activity) {
+ LogUtil.i("AllContactsFragment.onPagePause", null);
+ }
+}
diff --git a/java/com/android/dialer/app/list/BlockedListSearchAdapter.java b/java/com/android/dialer/app/list/BlockedListSearchAdapter.java
new file mode 100644
index 000000000..a90ce7a0d
--- /dev/null
+++ b/java/com/android/dialer/app/list/BlockedListSearchAdapter.java
@@ -0,0 +1,84 @@
+/*
+ * 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.dialer.app.list;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.view.View;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.list.ContactListItemView;
+import com.android.dialer.app.R;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
+
+/** List adapter to display search results for adding a blocked number. */
+public class BlockedListSearchAdapter extends RegularSearchListAdapter {
+
+ private Resources mResources;
+ private FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
+
+ public BlockedListSearchAdapter(Context context) {
+ super(context);
+ mResources = context.getResources();
+ disableAllShortcuts();
+ setShortcutEnabled(SHORTCUT_BLOCK_NUMBER, true);
+
+ mFilteredNumberAsyncQueryHandler = new FilteredNumberAsyncQueryHandler(context);
+ }
+
+ @Override
+ protected boolean isChanged(boolean showNumberShortcuts) {
+ return setShortcutEnabled(SHORTCUT_BLOCK_NUMBER, showNumberShortcuts || mIsQuerySipAddress);
+ }
+
+ public void setViewBlocked(ContactListItemView view, Integer id) {
+ view.setTag(R.id.block_id, id);
+ final int textColor = mResources.getColor(R.color.blocked_number_block_color);
+ view.getDataView().setTextColor(textColor);
+ view.getLabelView().setTextColor(textColor);
+ //TODO: Add icon
+ }
+
+ public void setViewUnblocked(ContactListItemView view) {
+ view.setTag(R.id.block_id, null);
+ final int textColor = mResources.getColor(R.color.dialer_secondary_text_color);
+ view.getDataView().setTextColor(textColor);
+ view.getLabelView().setTextColor(textColor);
+ //TODO: Remove icon
+ }
+
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ super.bindView(itemView, partition, cursor, position);
+
+ final ContactListItemView view = (ContactListItemView) itemView;
+ // Reset view state to unblocked.
+ setViewUnblocked(view);
+
+ final String number = getPhoneNumber(position);
+ final String countryIso = GeoUtil.getCurrentCountryIso(mContext);
+ final FilteredNumberAsyncQueryHandler.OnCheckBlockedListener onCheckListener =
+ new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
+ @Override
+ public void onCheckComplete(Integer id) {
+ if (id != null && id != FilteredNumberAsyncQueryHandler.INVALID_ID) {
+ setViewBlocked(view, id);
+ }
+ }
+ };
+ mFilteredNumberAsyncQueryHandler.isBlockedNumber(onCheckListener, number, countryIso);
+ }
+}
diff --git a/java/com/android/dialer/app/list/BlockedListSearchFragment.java b/java/com/android/dialer/app/list/BlockedListSearchFragment.java
new file mode 100644
index 000000000..2129981c0
--- /dev/null
+++ b/java/com/android/dialer/app/list/BlockedListSearchFragment.java
@@ -0,0 +1,245 @@
+/*
+ * 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.dialer.app.list;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.telephony.PhoneNumberUtils;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.EditText;
+import android.widget.Toast;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.list.ContactEntryListAdapter;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.dialer.app.R;
+import com.android.dialer.app.widget.SearchEditTextLayout;
+import com.android.dialer.blocking.BlockNumberDialogFragment;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.InteractionEvent;
+
+public class BlockedListSearchFragment extends RegularSearchFragment
+ implements BlockNumberDialogFragment.Callback {
+
+ private static final String TAG = BlockedListSearchFragment.class.getSimpleName();
+
+ private final TextWatcher mPhoneSearchQueryTextListener =
+ new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ setQueryString(s.toString());
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {}
+ };
+ private final SearchEditTextLayout.Callback mSearchLayoutCallback =
+ new SearchEditTextLayout.Callback() {
+ @Override
+ public void onBackButtonClicked() {
+ getActivity().onBackPressed();
+ }
+
+ @Override
+ public void onSearchViewClicked() {}
+ };
+ private FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
+ private EditText mSearchView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setShowEmptyListForNullQuery(true);
+ /*
+ * Pass in the empty string here so ContactEntryListFragment#setQueryString interprets it as
+ * an empty search query, rather than as an uninitalized value. In the latter case, the
+ * adapter returned by #createListAdapter is used, which populates the view with contacts.
+ * Passing in the empty string forces ContactEntryListFragment to interpret it as an empty
+ * query, which results in showing an empty view
+ */
+ setQueryString(getQueryString() == null ? "" : getQueryString());
+ mFilteredNumberAsyncQueryHandler = new FilteredNumberAsyncQueryHandler(getContext());
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
+ actionBar.setCustomView(R.layout.search_edittext);
+ actionBar.setDisplayShowCustomEnabled(true);
+ actionBar.setDisplayHomeAsUpEnabled(false);
+ actionBar.setDisplayShowHomeEnabled(false);
+
+ final SearchEditTextLayout searchEditTextLayout =
+ (SearchEditTextLayout) actionBar.getCustomView().findViewById(R.id.search_view_container);
+ searchEditTextLayout.expand(false, true);
+ searchEditTextLayout.setCallback(mSearchLayoutCallback);
+ searchEditTextLayout.setBackgroundDrawable(null);
+
+ mSearchView = (EditText) searchEditTextLayout.findViewById(R.id.search_view);
+ mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
+ mSearchView.setHint(R.string.block_number_search_hint);
+
+ searchEditTextLayout
+ .findViewById(R.id.search_box_expanded)
+ .setBackgroundColor(getContext().getResources().getColor(android.R.color.white));
+
+ if (!TextUtils.isEmpty(getQueryString())) {
+ mSearchView.setText(getQueryString());
+ }
+
+ // TODO: Don't set custom text size; use default search text size.
+ mSearchView.setTextSize(
+ TypedValue.COMPLEX_UNIT_PX,
+ getResources().getDimension(R.dimen.blocked_number_search_text_size));
+ }
+
+ @Override
+ protected ContactEntryListAdapter createListAdapter() {
+ BlockedListSearchAdapter adapter = new BlockedListSearchAdapter(getActivity());
+ adapter.setDisplayPhotos(true);
+ // Don't show SIP addresses.
+ adapter.setUseCallableUri(false);
+ // Keep in sync with the queryString set in #onCreate
+ adapter.setQueryString(getQueryString() == null ? "" : getQueryString());
+ return adapter;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ super.onItemClick(parent, view, position, id);
+ final int adapterPosition = position - getListView().getHeaderViewsCount();
+ final BlockedListSearchAdapter adapter = (BlockedListSearchAdapter) getAdapter();
+ final int shortcutType = adapter.getShortcutTypeFromPosition(adapterPosition);
+ final Integer blockId = (Integer) view.getTag(R.id.block_id);
+ final String number;
+ switch (shortcutType) {
+ case DialerPhoneNumberListAdapter.SHORTCUT_INVALID:
+ // Handles click on a search result, either contact or nearby places result.
+ number = adapter.getPhoneNumber(adapterPosition);
+ blockContactNumber(number, blockId);
+ break;
+ case DialerPhoneNumberListAdapter.SHORTCUT_BLOCK_NUMBER:
+ // Handles click on 'Block number' shortcut to add the user query as a number.
+ number = adapter.getQueryString();
+ blockNumber(number);
+ break;
+ default:
+ Log.w(TAG, "Ignoring unsupported shortcut type: " + shortcutType);
+ break;
+ }
+ }
+
+ @Override
+ protected void onItemClick(int position, long id) {
+ // Prevent SearchFragment.onItemClicked from being called.
+ }
+
+ private void blockNumber(final String number) {
+ final String countryIso = GeoUtil.getCurrentCountryIso(getContext());
+ final OnCheckBlockedListener onCheckListener =
+ new OnCheckBlockedListener() {
+ @Override
+ public void onCheckComplete(Integer id) {
+ if (id == null) {
+ BlockNumberDialogFragment.show(
+ id,
+ number,
+ countryIso,
+ PhoneNumberUtils.formatNumber(number, countryIso),
+ R.id.blocked_numbers_activity_container,
+ getFragmentManager(),
+ BlockedListSearchFragment.this);
+ } else if (id == FilteredNumberAsyncQueryHandler.INVALID_ID) {
+ Toast.makeText(
+ getContext(),
+ ContactDisplayUtils.getTtsSpannedPhoneNumber(
+ getResources(), R.string.invalidNumber, number),
+ Toast.LENGTH_SHORT)
+ .show();
+ } else {
+ Toast.makeText(
+ getContext(),
+ ContactDisplayUtils.getTtsSpannedPhoneNumber(
+ getResources(), R.string.alreadyBlocked, number),
+ Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
+ };
+ mFilteredNumberAsyncQueryHandler.isBlockedNumber(onCheckListener, number, countryIso);
+ }
+
+ @Override
+ public void onFilterNumberSuccess() {
+ Logger.get(getContext()).logInteraction(InteractionEvent.Type.BLOCK_NUMBER_MANAGEMENT_SCREEN);
+ goBack();
+ }
+
+ @Override
+ public void onUnfilterNumberSuccess() {
+ Log.wtf(TAG, "Unblocked a number from the BlockedListSearchFragment");
+ goBack();
+ }
+
+ private void goBack() {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+ activity.onBackPressed();
+ }
+
+ @Override
+ public void onChangeFilteredNumberUndo() {
+ getAdapter().notifyDataSetChanged();
+ }
+
+ private void blockContactNumber(final String number, final Integer blockId) {
+ if (blockId != null) {
+ Toast.makeText(
+ getContext(),
+ ContactDisplayUtils.getTtsSpannedPhoneNumber(
+ getResources(), R.string.alreadyBlocked, number),
+ Toast.LENGTH_SHORT)
+ .show();
+ return;
+ }
+
+ BlockNumberDialogFragment.show(
+ blockId,
+ number,
+ GeoUtil.getCurrentCountryIso(getContext()),
+ number,
+ R.id.blocked_numbers_activity_container,
+ getFragmentManager(),
+ this);
+ }
+}
diff --git a/java/com/android/dialer/app/list/ContentChangedFilter.java b/java/com/android/dialer/app/list/ContentChangedFilter.java
new file mode 100644
index 000000000..663846da5
--- /dev/null
+++ b/java/com/android/dialer/app/list/ContentChangedFilter.java
@@ -0,0 +1,56 @@
+/*
+ * 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.dialer.app.list;
+
+import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * AccessibilityDelegate that will filter out TYPE_WINDOW_CONTENT_CHANGED Used to suppress "Showing
+ * items x of y" from firing of ListView whenever it's content changes. AccessibilityEvent can only
+ * be rejected at a view's parent once it is generated, use addToParent() to add this delegate to
+ * the parent.
+ */
+public class ContentChangedFilter extends AccessibilityDelegate {
+
+ //the view we don't want TYPE_WINDOW_CONTENT_CHANGED to fire.
+ private View mView;
+
+ private ContentChangedFilter(View view) {
+ super();
+ mView = view;
+ }
+
+ /** Add this delegate to the parent of @param view to filter out TYPE_WINDOW_CONTENT_CHANGED */
+ public static void addToParent(View view) {
+ View parent = (View) view.getParent();
+ parent.setAccessibilityDelegate(new ContentChangedFilter(view));
+ }
+
+ @Override
+ public boolean onRequestSendAccessibilityEvent(
+ ViewGroup host, View child, AccessibilityEvent event) {
+ if (child == mView) {
+ if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
+ return false;
+ }
+ }
+ return super.onRequestSendAccessibilityEvent(host, child, event);
+ }
+}
diff --git a/java/com/android/dialer/app/list/DialerPhoneNumberListAdapter.java b/java/com/android/dialer/app/list/DialerPhoneNumberListAdapter.java
new file mode 100644
index 000000000..7e2525f24
--- /dev/null
+++ b/java/com/android/dialer/app/list/DialerPhoneNumberListAdapter.java
@@ -0,0 +1,228 @@
+/*
+ * 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.dialer.app.list;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.telephony.PhoneNumberUtils;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.list.ContactListItemView;
+import com.android.contacts.common.list.PhoneNumberListAdapter;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.dialer.app.R;
+import com.android.dialer.util.CallUtil;
+
+/**
+ * {@link PhoneNumberListAdapter} with the following added shortcuts, that are displayed as list
+ * items: 1) Directly calling the phone number query 2) Adding the phone number query to a contact
+ *
+ * <p>These shortcuts can be enabled or disabled to toggle whether or not they show up in the list.
+ */
+public class DialerPhoneNumberListAdapter extends PhoneNumberListAdapter {
+
+ public static final int SHORTCUT_INVALID = -1;
+ public static final int SHORTCUT_DIRECT_CALL = 0;
+ public static final int SHORTCUT_CREATE_NEW_CONTACT = 1;
+ public static final int SHORTCUT_ADD_TO_EXISTING_CONTACT = 2;
+ public static final int SHORTCUT_SEND_SMS_MESSAGE = 3;
+ public static final int SHORTCUT_MAKE_VIDEO_CALL = 4;
+ public static final int SHORTCUT_BLOCK_NUMBER = 5;
+ public static final int SHORTCUT_COUNT = 6;
+ private final boolean[] mShortcutEnabled = new boolean[SHORTCUT_COUNT];
+ private final BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
+ private String mFormattedQueryString;
+ private String mCountryIso;
+ private boolean mVideoCallingEnabled = false;
+
+ public DialerPhoneNumberListAdapter(Context context) {
+ super(context);
+
+ mCountryIso = GeoUtil.getCurrentCountryIso(context);
+ mVideoCallingEnabled = CallUtil.isVideoEnabled(context);
+ }
+
+ @Override
+ public int getCount() {
+ return super.getCount() + getShortcutCount();
+ }
+
+ /** @return The number of enabled shortcuts. Ranges from 0 to a maximum of SHORTCUT_COUNT */
+ public int getShortcutCount() {
+ int count = 0;
+ for (int i = 0; i < mShortcutEnabled.length; i++) {
+ if (mShortcutEnabled[i]) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public void disableAllShortcuts() {
+ for (int i = 0; i < mShortcutEnabled.length; i++) {
+ mShortcutEnabled[i] = false;
+ }
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ final int shortcut = getShortcutTypeFromPosition(position);
+ if (shortcut >= 0) {
+ // shortcutPos should always range from 1 to SHORTCUT_COUNT
+ return super.getViewTypeCount() + shortcut;
+ } else {
+ return super.getItemViewType(position);
+ }
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ // Number of item view types in the super implementation + 2 for the 2 new shortcuts
+ return super.getViewTypeCount() + SHORTCUT_COUNT;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final int shortcutType = getShortcutTypeFromPosition(position);
+ if (shortcutType >= 0) {
+ if (convertView != null) {
+ assignShortcutToView((ContactListItemView) convertView, shortcutType);
+ return convertView;
+ } else {
+ final ContactListItemView v =
+ new ContactListItemView(getContext(), null, mVideoCallingEnabled);
+ assignShortcutToView(v, shortcutType);
+ return v;
+ }
+ } else {
+ return super.getView(position, convertView, parent);
+ }
+ }
+
+ @Override
+ protected ContactListItemView newView(
+ Context context, int partition, Cursor cursor, int position, ViewGroup parent) {
+ final ContactListItemView view = super.newView(context, partition, cursor, position, parent);
+
+ view.setSupportVideoCallIcon(mVideoCallingEnabled);
+ return view;
+ }
+
+ /**
+ * @param position The position of the item
+ * @return The enabled shortcut type matching the given position if the item is a shortcut, -1
+ * otherwise
+ */
+ public int getShortcutTypeFromPosition(int position) {
+ int shortcutCount = position - super.getCount();
+ if (shortcutCount >= 0) {
+ // Iterate through the array of shortcuts, looking only for shortcuts where
+ // mShortcutEnabled[i] is true
+ for (int i = 0; shortcutCount >= 0 && i < mShortcutEnabled.length; i++) {
+ if (mShortcutEnabled[i]) {
+ shortcutCount--;
+ if (shortcutCount < 0) {
+ return i;
+ }
+ }
+ }
+ throw new IllegalArgumentException(
+ "Invalid position - greater than cursor count " + " but not a shortcut.");
+ }
+ return SHORTCUT_INVALID;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return getShortcutCount() == 0 && super.isEmpty();
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ final int shortcutType = getShortcutTypeFromPosition(position);
+ if (shortcutType >= 0) {
+ return true;
+ } else {
+ return super.isEnabled(position);
+ }
+ }
+
+ private void assignShortcutToView(ContactListItemView v, int shortcutType) {
+ final CharSequence text;
+ final int drawableId;
+ final Resources resources = getContext().getResources();
+ final String number = getFormattedQueryString();
+ switch (shortcutType) {
+ case SHORTCUT_DIRECT_CALL:
+ text =
+ ContactDisplayUtils.getTtsSpannedPhoneNumber(
+ resources,
+ R.string.search_shortcut_call_number,
+ mBidiFormatter.unicodeWrap(number, TextDirectionHeuristics.LTR));
+ drawableId = R.drawable.ic_search_phone;
+ break;
+ case SHORTCUT_CREATE_NEW_CONTACT:
+ text = resources.getString(R.string.search_shortcut_create_new_contact);
+ drawableId = R.drawable.ic_search_add_contact;
+ break;
+ case SHORTCUT_ADD_TO_EXISTING_CONTACT:
+ text = resources.getString(R.string.search_shortcut_add_to_contact);
+ drawableId = R.drawable.ic_person_24dp;
+ break;
+ case SHORTCUT_SEND_SMS_MESSAGE:
+ text = resources.getString(R.string.search_shortcut_send_sms_message);
+ drawableId = R.drawable.ic_message_24dp;
+ break;
+ case SHORTCUT_MAKE_VIDEO_CALL:
+ text = resources.getString(R.string.search_shortcut_make_video_call);
+ drawableId = R.drawable.ic_videocam;
+ break;
+ case SHORTCUT_BLOCK_NUMBER:
+ text = resources.getString(R.string.search_shortcut_block_number);
+ drawableId = R.drawable.ic_not_interested_googblue_24dp;
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid shortcut type");
+ }
+ v.setDrawableResource(drawableId);
+ v.setDisplayName(text);
+ v.setPhotoPosition(super.getPhotoPosition());
+ v.setAdjustSelectionBoundsEnabled(false);
+ }
+
+ /** @return True if the shortcut state (disabled vs enabled) was changed by this operation */
+ public boolean setShortcutEnabled(int shortcutType, boolean visible) {
+ final boolean changed = mShortcutEnabled[shortcutType] != visible;
+ mShortcutEnabled[shortcutType] = visible;
+ return changed;
+ }
+
+ public String getFormattedQueryString() {
+ return mFormattedQueryString;
+ }
+
+ @Override
+ public void setQueryString(String queryString) {
+ mFormattedQueryString =
+ PhoneNumberUtils.formatNumber(PhoneNumberUtils.normalizeNumber(queryString), mCountryIso);
+ super.setQueryString(queryString);
+ }
+}
diff --git a/java/com/android/dialer/app/list/DragDropController.java b/java/com/android/dialer/app/list/DragDropController.java
new file mode 100644
index 000000000..c22dd1318
--- /dev/null
+++ b/java/com/android/dialer/app/list/DragDropController.java
@@ -0,0 +1,106 @@
+/*
+ * 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.dialer.app.list;
+
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.view.View;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class that handles and combines drag events generated from multiple views, and then fires off
+ * events to any OnDragDropListeners that have registered for callbacks.
+ */
+public class DragDropController {
+
+ private final List<OnDragDropListener> mOnDragDropListeners = new ArrayList<OnDragDropListener>();
+ private final DragItemContainer mDragItemContainer;
+ private final int[] mLocationOnScreen = new int[2];
+
+ public DragDropController(DragItemContainer dragItemContainer) {
+ mDragItemContainer = dragItemContainer;
+ }
+
+ /** @return True if the drag is started, false if the drag is cancelled for some reason. */
+ boolean handleDragStarted(View v, int x, int y) {
+ int screenX = x;
+ int screenY = y;
+ // The coordinates in dragEvent of DragEvent.ACTION_DRAG_STARTED before NYC is window-related.
+ // This is fixed in NYC.
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ v.getLocationOnScreen(mLocationOnScreen);
+ screenX = x + mLocationOnScreen[0];
+ screenY = y + mLocationOnScreen[1];
+ }
+ final PhoneFavoriteSquareTileView tileView =
+ mDragItemContainer.getViewForLocation(screenX, screenY);
+ if (tileView == null) {
+ return false;
+ }
+ for (int i = 0; i < mOnDragDropListeners.size(); i++) {
+ mOnDragDropListeners.get(i).onDragStarted(screenX, screenY, tileView);
+ }
+
+ return true;
+ }
+
+ public void handleDragHovered(View v, int x, int y) {
+ v.getLocationOnScreen(mLocationOnScreen);
+ final int screenX = x + mLocationOnScreen[0];
+ final int screenY = y + mLocationOnScreen[1];
+ final PhoneFavoriteSquareTileView view =
+ mDragItemContainer.getViewForLocation(screenX, screenY);
+ for (int i = 0; i < mOnDragDropListeners.size(); i++) {
+ mOnDragDropListeners.get(i).onDragHovered(screenX, screenY, view);
+ }
+ }
+
+ public void handleDragFinished(int x, int y, boolean isRemoveView) {
+ if (isRemoveView) {
+ for (int i = 0; i < mOnDragDropListeners.size(); i++) {
+ mOnDragDropListeners.get(i).onDroppedOnRemove();
+ }
+ }
+
+ for (int i = 0; i < mOnDragDropListeners.size(); i++) {
+ mOnDragDropListeners.get(i).onDragFinished(x, y);
+ }
+ }
+
+ public void addOnDragDropListener(OnDragDropListener listener) {
+ if (!mOnDragDropListeners.contains(listener)) {
+ mOnDragDropListeners.add(listener);
+ }
+ }
+
+ public void removeOnDragDropListener(OnDragDropListener listener) {
+ if (mOnDragDropListeners.contains(listener)) {
+ mOnDragDropListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Callback interface used to retrieve views based on the current touch coordinates of the drag
+ * event. The {@link DragItemContainer} houses the draggable views that this {@link
+ * DragDropController} controls.
+ */
+ public interface DragItemContainer {
+
+ PhoneFavoriteSquareTileView getViewForLocation(int x, int y);
+ }
+}
diff --git a/java/com/android/dialer/app/list/ListsFragment.java b/java/com/android/dialer/app/list/ListsFragment.java
new file mode 100644
index 000000000..725ad3001
--- /dev/null
+++ b/java/com/android/dialer/app/list/ListsFragment.java
@@ -0,0 +1,587 @@
+/*
+ * Copyright (C) 2013 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.dialer.app.list;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.SharedPreferences;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Trace;
+import android.preference.PreferenceManager;
+import android.provider.VoicemailContract;
+import android.support.annotation.Nullable;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.contacts.common.list.ViewPagerTabs;
+import com.android.dialer.app.R;
+import com.android.dialer.app.calllog.CallLogFragment;
+import com.android.dialer.app.calllog.CallLogNotificationsHelper;
+import com.android.dialer.app.calllog.VisualVoicemailCallLogFragment;
+import com.android.dialer.app.voicemail.error.VoicemailStatusCorruptionHandler;
+import com.android.dialer.app.voicemail.error.VoicemailStatusCorruptionHandler.Source;
+import com.android.dialer.app.widget.ActionBarController;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.database.CallLogQueryHandler;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.logging.nano.ScreenEvent;
+import com.android.dialer.util.ViewUtil;
+import com.android.dialer.voicemailstatus.VisualVoicemailEnabledChecker;
+import com.android.dialer.voicemailstatus.VoicemailStatusHelper;
+import com.android.dialer.voicemailstatus.VoicemailStatusHelperImpl;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fragment that is used as the main screen of the Dialer.
+ *
+ * <p>Contains a ViewPager that contains various contact lists like the Speed Dial list and the All
+ * Contacts list. This will also eventually contain the logic that allows sliding the ViewPager
+ * containing the lists up above the search bar and pin it against the top of the screen.
+ */
+public class ListsFragment extends Fragment
+ implements ViewPager.OnPageChangeListener, CallLogQueryHandler.Listener {
+
+ /** Every fragment in the list show implement this interface. */
+ public interface ListsPage {
+
+ /**
+ * Called when the page is resumed, including selecting the page or activity resume. Note: This
+ * is called before the page fragment is attached to a activity.
+ *
+ * @param activity the activity hosting the ListFragment
+ */
+ void onPageResume(@Nullable Activity activity);
+
+ /**
+ * Called when the page is paused, including selecting another page or activity pause. Note:
+ * This is called after the page fragment is detached from a activity.
+ *
+ * @param activity the activity hosting the ListFragment
+ */
+ void onPagePause(@Nullable Activity activity);
+ }
+
+ public static final int TAB_INDEX_SPEED_DIAL = 0;
+ public static final int TAB_INDEX_HISTORY = 1;
+ public static final int TAB_INDEX_ALL_CONTACTS = 2;
+ public static final int TAB_INDEX_VOICEMAIL = 3;
+ public static final int TAB_COUNT_DEFAULT = 3;
+ public static final int TAB_COUNT_WITH_VOICEMAIL = 4;
+ private static final String TAG = "ListsFragment";
+ private ActionBar mActionBar;
+ private ViewPager mViewPager;
+ private ViewPagerTabs mViewPagerTabs;
+ private ViewPagerAdapter mViewPagerAdapter;
+ private RemoveView mRemoveView;
+ private View mRemoveViewContent;
+ private SpeedDialFragment mSpeedDialFragment;
+ private CallLogFragment mHistoryFragment;
+ private AllContactsFragment mAllContactsFragment;
+ private CallLogFragment mVoicemailFragment;
+ private ListsPage mCurrentPage;
+ private SharedPreferences mPrefs;
+ private boolean mHasActiveVoicemailProvider;
+ private boolean mHasFetchedVoicemailStatus;
+ private boolean mShowVoicemailTabAfterVoicemailStatusIsFetched;
+ private VoicemailStatusHelper mVoicemailStatusHelper;
+ private ArrayList<OnPageChangeListener> mOnPageChangeListeners =
+ new ArrayList<OnPageChangeListener>();
+ private String[] mTabTitles;
+ private int[] mTabIcons;
+ /** The position of the currently selected tab. */
+ private int mTabIndex = TAB_INDEX_SPEED_DIAL;
+
+ private CallLogQueryHandler mCallLogQueryHandler;
+
+ private final ContentObserver mVoicemailStatusObserver =
+ new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ mCallLogQueryHandler.fetchVoicemailStatus();
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ LogUtil.d("ListsFragment.onCreate", null);
+ Trace.beginSection(TAG + " onCreate");
+ super.onCreate(savedInstanceState);
+
+ mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
+ mHasFetchedVoicemailStatus = false;
+
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
+ mHasActiveVoicemailProvider =
+ mPrefs.getBoolean(
+ VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, false);
+
+ Trace.endSection();
+ }
+
+ @Override
+ public void onResume() {
+ LogUtil.d("ListsFragment.onResume", null);
+ Trace.beginSection(TAG + " onResume");
+ super.onResume();
+
+ mActionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
+ if (getUserVisibleHint()) {
+ sendScreenViewForCurrentPosition();
+ }
+
+ // Fetch voicemail status to determine if we should show the voicemail tab.
+ mCallLogQueryHandler =
+ new CallLogQueryHandler(getActivity(), getActivity().getContentResolver(), this);
+ mCallLogQueryHandler.fetchVoicemailStatus();
+ mCallLogQueryHandler.fetchMissedCallsUnreadCount();
+ Trace.endSection();
+ mCurrentPage = getListsPage(mViewPager.getCurrentItem());
+ if (mCurrentPage != null) {
+ mCurrentPage.onPageResume(getActivity());
+ }
+ }
+
+ @Override
+ public void onPause() {
+ LogUtil.d("ListsFragment.onPause", null);
+ if (mCurrentPage != null) {
+ mCurrentPage.onPagePause(getActivity());
+ }
+ super.onPause();
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mViewPager.removeOnPageChangeListener(this);
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ LogUtil.d("ListsFragment.onCreateView", null);
+ Trace.beginSection(TAG + " onCreateView");
+ Trace.beginSection(TAG + " inflate view");
+ final View parentView = inflater.inflate(R.layout.lists_fragment, container, false);
+ Trace.endSection();
+ Trace.beginSection(TAG + " setup views");
+ mViewPager = (ViewPager) parentView.findViewById(R.id.lists_pager);
+ mViewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());
+ mViewPager.setAdapter(mViewPagerAdapter);
+ mViewPager.setOffscreenPageLimit(TAB_COUNT_WITH_VOICEMAIL - 1);
+ mViewPager.addOnPageChangeListener(this);
+ showTab(TAB_INDEX_SPEED_DIAL);
+
+ mTabTitles = new String[TAB_COUNT_WITH_VOICEMAIL];
+ mTabTitles[TAB_INDEX_SPEED_DIAL] = getResources().getString(R.string.tab_speed_dial);
+ mTabTitles[TAB_INDEX_HISTORY] = getResources().getString(R.string.tab_history);
+ mTabTitles[TAB_INDEX_ALL_CONTACTS] = getResources().getString(R.string.tab_all_contacts);
+ mTabTitles[TAB_INDEX_VOICEMAIL] = getResources().getString(R.string.tab_voicemail);
+
+ mTabIcons = new int[TAB_COUNT_WITH_VOICEMAIL];
+ mTabIcons[TAB_INDEX_SPEED_DIAL] = R.drawable.ic_grade_24dp;
+ mTabIcons[TAB_INDEX_HISTORY] = R.drawable.ic_schedule_24dp;
+ mTabIcons[TAB_INDEX_ALL_CONTACTS] = R.drawable.ic_people_24dp;
+ mTabIcons[TAB_INDEX_VOICEMAIL] = R.drawable.ic_voicemail_24dp;
+
+ mViewPagerTabs = (ViewPagerTabs) parentView.findViewById(R.id.lists_pager_header);
+ mViewPagerTabs.configureTabIcons(mTabIcons);
+ mViewPagerTabs.setViewPager(mViewPager);
+ addOnPageChangeListener(mViewPagerTabs);
+
+ mRemoveView = (RemoveView) parentView.findViewById(R.id.remove_view);
+ mRemoveViewContent = parentView.findViewById(R.id.remove_view_content);
+
+ getActivity()
+ .getContentResolver()
+ .registerContentObserver(
+ VoicemailContract.Status.CONTENT_URI, true, mVoicemailStatusObserver);
+
+ Trace.endSection();
+ Trace.endSection();
+ return parentView;
+ }
+
+ @Override
+ public void onDestroy() {
+ getActivity().getContentResolver().unregisterContentObserver(mVoicemailStatusObserver);
+ super.onDestroy();
+ }
+
+ public void addOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
+ if (!mOnPageChangeListeners.contains(onPageChangeListener)) {
+ mOnPageChangeListeners.add(onPageChangeListener);
+ }
+ }
+
+ /**
+ * Shows the tab with the specified index. If the voicemail tab index is specified, but the
+ * voicemail status hasn't been fetched, it will try to show the tab after the voicemail status
+ * has been fetched.
+ */
+ public void showTab(int index) {
+ if (index == TAB_INDEX_VOICEMAIL) {
+ if (mHasActiveVoicemailProvider) {
+ Logger.get(getContext()).logImpression(DialerImpression.Type.VVM_TAB_VISIBLE);
+ mViewPager.setCurrentItem(getRtlPosition(TAB_INDEX_VOICEMAIL));
+ } else if (!mHasFetchedVoicemailStatus) {
+ // Try to show the voicemail tab after the voicemail status returns.
+ mShowVoicemailTabAfterVoicemailStatusIsFetched = true;
+ }
+ } else if (index < getTabCount()) {
+ mViewPager.setCurrentItem(getRtlPosition(index));
+ }
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ mTabIndex = getRtlPosition(position);
+
+ final int count = mOnPageChangeListeners.size();
+ for (int i = 0; i < count; i++) {
+ mOnPageChangeListeners.get(i).onPageScrolled(position, positionOffset, positionOffsetPixels);
+ }
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ LogUtil.i("ListsFragment.onPageSelected", "position: %d", position);
+ mTabIndex = getRtlPosition(position);
+
+ // Show the tab which has been selected instead.
+ mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
+
+ final int count = mOnPageChangeListeners.size();
+ for (int i = 0; i < count; i++) {
+ mOnPageChangeListeners.get(i).onPageSelected(position);
+ }
+ sendScreenViewForCurrentPosition();
+
+ if (mCurrentPage != null) {
+ mCurrentPage.onPagePause(getActivity());
+ }
+ mCurrentPage = getListsPage(position);
+ if (mCurrentPage != null) {
+ mCurrentPage.onPageResume(getActivity());
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ final int count = mOnPageChangeListeners.size();
+ for (int i = 0; i < count; i++) {
+ mOnPageChangeListeners.get(i).onPageScrollStateChanged(state);
+ }
+ }
+
+ @Override
+ public void onVoicemailStatusFetched(Cursor statusCursor) {
+ mHasFetchedVoicemailStatus = true;
+
+ if (getActivity() == null || getActivity().isFinishing()) {
+ return;
+ }
+
+ VoicemailStatusCorruptionHandler.maybeFixVoicemailStatus(
+ getContext(), statusCursor, Source.Activity);
+
+ // Update mHasActiveVoicemailProvider, which controls the number of tabs displayed.
+ boolean hasActiveVoicemailProvider =
+ mVoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor) > 0;
+ if (hasActiveVoicemailProvider != mHasActiveVoicemailProvider) {
+ mHasActiveVoicemailProvider = hasActiveVoicemailProvider;
+ mViewPagerAdapter.notifyDataSetChanged();
+
+ if (hasActiveVoicemailProvider) {
+ mViewPagerTabs.updateTab(TAB_INDEX_VOICEMAIL);
+ } else {
+ mViewPagerTabs.removeTab(TAB_INDEX_VOICEMAIL);
+ removeVoicemailFragment();
+ }
+
+ mPrefs
+ .edit()
+ .putBoolean(
+ VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER,
+ hasActiveVoicemailProvider)
+ .commit();
+ }
+
+ if (hasActiveVoicemailProvider) {
+ mCallLogQueryHandler.fetchVoicemailUnreadCount();
+ }
+
+ if (mHasActiveVoicemailProvider && mShowVoicemailTabAfterVoicemailStatusIsFetched) {
+ mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
+ showTab(TAB_INDEX_VOICEMAIL);
+ }
+ }
+
+ @Override
+ public void onVoicemailUnreadCountFetched(Cursor cursor) {
+ if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
+ return;
+ }
+
+ int count = 0;
+ try {
+ count = cursor.getCount();
+ } finally {
+ cursor.close();
+ }
+
+ mViewPagerTabs.setUnreadCount(count, TAB_INDEX_VOICEMAIL);
+ mViewPagerTabs.updateTab(TAB_INDEX_VOICEMAIL);
+ }
+
+ @Override
+ public void onMissedCallsUnreadCountFetched(Cursor cursor) {
+ if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
+ return;
+ }
+
+ int count = 0;
+ try {
+ count = cursor.getCount();
+ } finally {
+ cursor.close();
+ }
+
+ mViewPagerTabs.setUnreadCount(count, TAB_INDEX_HISTORY);
+ mViewPagerTabs.updateTab(TAB_INDEX_HISTORY);
+ }
+
+ @Override
+ public boolean onCallsFetched(Cursor statusCursor) {
+ // Return false; did not take ownership of cursor
+ return false;
+ }
+
+ public int getCurrentTabIndex() {
+ return mTabIndex;
+ }
+
+ /**
+ * External method to update unread count because the unread count changes when the user expands a
+ * voicemail in the call log or when the user expands an unread call in the call history tab.
+ */
+ public void updateTabUnreadCounts() {
+ if (mCallLogQueryHandler != null) {
+ mCallLogQueryHandler.fetchMissedCallsUnreadCount();
+ if (mHasActiveVoicemailProvider) {
+ mCallLogQueryHandler.fetchVoicemailUnreadCount();
+ }
+ }
+ }
+
+ /** External method to mark all missed calls as read. */
+ public void markMissedCallsAsReadAndRemoveNotifications() {
+ if (mCallLogQueryHandler != null) {
+ mCallLogQueryHandler.markMissedCallsAsRead();
+ CallLogNotificationsHelper.removeMissedCallNotifications(getActivity());
+ }
+ }
+
+ public void showRemoveView(boolean show) {
+ mRemoveViewContent.setVisibility(show ? View.VISIBLE : View.GONE);
+ mRemoveView.setAlpha(show ? 0 : 1);
+ mRemoveView.animate().alpha(show ? 1 : 0).start();
+ }
+
+ public boolean shouldShowActionBar() {
+ // TODO: Update this based on scroll state.
+ return mActionBar != null;
+ }
+
+ public SpeedDialFragment getSpeedDialFragment() {
+ return mSpeedDialFragment;
+ }
+
+ public RemoveView getRemoveView() {
+ return mRemoveView;
+ }
+
+ public int getTabCount() {
+ return mViewPagerAdapter.getCount();
+ }
+
+ private int getRtlPosition(int position) {
+ if (ViewUtil.isRtl()) {
+ return mViewPagerAdapter.getCount() - 1 - position;
+ }
+ return position;
+ }
+
+ public void sendScreenViewForCurrentPosition() {
+ if (!isResumed()) {
+ return;
+ }
+
+ int screenType;
+ switch (getCurrentTabIndex()) {
+ case TAB_INDEX_SPEED_DIAL:
+ screenType = ScreenEvent.Type.SPEED_DIAL;
+ break;
+ case TAB_INDEX_HISTORY:
+ screenType = ScreenEvent.Type.CALL_LOG;
+ break;
+ case TAB_INDEX_ALL_CONTACTS:
+ screenType = ScreenEvent.Type.ALL_CONTACTS;
+ break;
+ case TAB_INDEX_VOICEMAIL:
+ screenType = ScreenEvent.Type.VOICEMAIL_LOG;
+ break;
+ default:
+ return;
+ }
+ Logger.get(getActivity()).logScreenView(screenType, getActivity());
+ }
+
+ private void removeVoicemailFragment() {
+ if (mVoicemailFragment != null) {
+ getChildFragmentManager()
+ .beginTransaction()
+ .remove(mVoicemailFragment)
+ .commitAllowingStateLoss();
+ mVoicemailFragment = null;
+ }
+ }
+
+ private ListsPage getListsPage(int position) {
+ switch (getRtlPosition(position)) {
+ case TAB_INDEX_SPEED_DIAL:
+ return mSpeedDialFragment;
+ case TAB_INDEX_HISTORY:
+ return mHistoryFragment;
+ case TAB_INDEX_ALL_CONTACTS:
+ return mAllContactsFragment;
+ case TAB_INDEX_VOICEMAIL:
+ return mVoicemailFragment;
+ }
+ throw new IllegalStateException("No fragment at position " + position);
+ }
+
+ public interface HostInterface {
+
+ ActionBarController getActionBarController();
+ }
+
+ public class ViewPagerAdapter extends FragmentPagerAdapter {
+
+ private final List<Fragment> mFragments = new ArrayList<>();
+
+ public ViewPagerAdapter(FragmentManager fm) {
+ super(fm);
+ for (int i = 0; i < TAB_COUNT_WITH_VOICEMAIL; i++) {
+ mFragments.add(null);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return getRtlPosition(position);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ LogUtil.d("ViewPagerAdapter.getItem", "position: %d", position);
+ switch (getRtlPosition(position)) {
+ case TAB_INDEX_SPEED_DIAL:
+ if (mSpeedDialFragment == null) {
+ mSpeedDialFragment = new SpeedDialFragment();
+ }
+ return mSpeedDialFragment;
+ case TAB_INDEX_HISTORY:
+ if (mHistoryFragment == null) {
+ mHistoryFragment = new CallLogFragment();
+ }
+ return mHistoryFragment;
+ case TAB_INDEX_ALL_CONTACTS:
+ if (mAllContactsFragment == null) {
+ mAllContactsFragment = new AllContactsFragment();
+ }
+ return mAllContactsFragment;
+ case TAB_INDEX_VOICEMAIL:
+ if (mVoicemailFragment == null) {
+ mVoicemailFragment = new VisualVoicemailCallLogFragment();
+ LogUtil.v(
+ "ViewPagerAdapter.getItem",
+ "new VisualVoicemailCallLogFragment: %s",
+ mVoicemailFragment);
+ }
+ return mVoicemailFragment;
+ }
+ throw new IllegalStateException("No fragment at position " + position);
+ }
+
+ @Override
+ public Fragment instantiateItem(ViewGroup container, int position) {
+ LogUtil.d("ViewPagerAdapter.instantiateItem", "position: %d", position);
+ // On rotation the FragmentManager handles rotation. Therefore getItem() isn't called.
+ // Copy the fragments that the FragmentManager finds so that we can store them in
+ // instance variables for later.
+ final Fragment fragment = (Fragment) super.instantiateItem(container, position);
+ if (fragment instanceof SpeedDialFragment) {
+ mSpeedDialFragment = (SpeedDialFragment) fragment;
+ } else if (fragment instanceof CallLogFragment && position == TAB_INDEX_HISTORY) {
+ mHistoryFragment = (CallLogFragment) fragment;
+ } else if (fragment instanceof AllContactsFragment) {
+ mAllContactsFragment = (AllContactsFragment) fragment;
+ } else if (fragment instanceof CallLogFragment && position == TAB_INDEX_VOICEMAIL) {
+ mVoicemailFragment = (CallLogFragment) fragment;
+ LogUtil.v("ViewPagerAdapter.instantiateItem", mVoicemailFragment.toString());
+ }
+ mFragments.set(position, fragment);
+ return fragment;
+ }
+
+ /**
+ * When {@link android.support.v4.view.PagerAdapter#notifyDataSetChanged} is called, this method
+ * is called on all pages to determine whether they need to be recreated. When the voicemail tab
+ * is removed, the view needs to be recreated by returning POSITION_NONE. If
+ * notifyDataSetChanged is called for some other reason, the voicemail tab is recreated only if
+ * it is active. All other tabs do not need to be recreated and POSITION_UNCHANGED is returned.
+ */
+ @Override
+ public int getItemPosition(Object object) {
+ return !mHasActiveVoicemailProvider && mFragments.indexOf(object) == TAB_INDEX_VOICEMAIL
+ ? POSITION_NONE
+ : POSITION_UNCHANGED;
+ }
+
+ @Override
+ public int getCount() {
+ return mHasActiveVoicemailProvider ? TAB_COUNT_WITH_VOICEMAIL : TAB_COUNT_DEFAULT;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return mTabTitles[position];
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/list/OnDragDropListener.java b/java/com/android/dialer/app/list/OnDragDropListener.java
new file mode 100644
index 000000000..b71c7fef6
--- /dev/null
+++ b/java/com/android/dialer/app/list/OnDragDropListener.java
@@ -0,0 +1,58 @@
+/*
+ * 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.dialer.app.list;
+
+/**
+ * Classes that want to receive callbacks in response to drag events should implement this
+ * interface.
+ */
+public interface OnDragDropListener {
+
+ /**
+ * Called when a drag is started.
+ *
+ * @param x X-coordinate of the drag event
+ * @param y Y-coordinate of the drag event
+ * @param view The contact tile which the drag was started on
+ */
+ void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view);
+
+ /**
+ * Called when a drag is in progress and the user moves the dragged contact to a location.
+ *
+ * @param x X-coordinate of the drag event
+ * @param y Y-coordinate of the drag event
+ * @param view Contact tile in the ListView which is currently being displaced by the dragged
+ * contact
+ */
+ void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view);
+
+ /**
+ * Called when a drag is completed (whether by dropping it somewhere or simply by dragging the
+ * contact off the screen)
+ *
+ * @param x X-coordinate of the drag event
+ * @param y Y-coordinate of the drag event
+ */
+ void onDragFinished(int x, int y);
+
+ /**
+ * Called when a contact has been dropped on the remove view, indicating that the user wants to
+ * remove this contact.
+ */
+ void onDroppedOnRemove();
+}
diff --git a/src/com/android/dialer/list/OnListFragmentScrolledListener.java b/java/com/android/dialer/app/list/OnListFragmentScrolledListener.java
index 5ed3a6434..a76f3b527 100644
--- a/src/com/android/dialer/list/OnListFragmentScrolledListener.java
+++ b/java/com/android/dialer/app/list/OnListFragmentScrolledListener.java
@@ -14,13 +14,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.dialer.list;
+package com.android.dialer.app.list;
/*
* Interface to provide callback to activity when a child fragment is scrolled
*/
public interface OnListFragmentScrolledListener {
- public void onListFragmentScrollStateChange(int scrollState);
- public void onListFragmentScroll(int firstVisibleItem, int visibleItemCount,
- int totalItemCount);
+
+ void onListFragmentScrollStateChange(int scrollState);
+
+ void onListFragmentScroll(int firstVisibleItem, int visibleItemCount, int totalItemCount);
}
diff --git a/java/com/android/dialer/app/list/PhoneFavoriteListView.java b/java/com/android/dialer/app/list/PhoneFavoriteListView.java
new file mode 100644
index 000000000..9516f0611
--- /dev/null
+++ b/java/com/android/dialer/app/list/PhoneFavoriteListView.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ * Licensed to 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.dialer.app.list;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.DragEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.widget.GridView;
+import android.widget.ImageView;
+import com.android.dialer.app.R;
+import com.android.dialer.app.list.DragDropController.DragItemContainer;
+
+/** Viewgroup that presents the user's speed dial contacts in a grid. */
+public class PhoneFavoriteListView extends GridView
+ implements OnDragDropListener, DragItemContainer {
+
+ public static final String LOG_TAG = PhoneFavoriteListView.class.getSimpleName();
+ final int[] mLocationOnScreen = new int[2];
+ private final long SCROLL_HANDLER_DELAY_MILLIS = 5;
+ private final int DRAG_SCROLL_PX_UNIT = 25;
+ private final float DRAG_SHADOW_ALPHA = 0.7f;
+ /**
+ * {@link #mTopScrollBound} and {@link mBottomScrollBound} will be offseted to the top / bottom by
+ * {@link #getHeight} * {@link #BOUND_GAP_RATIO} pixels.
+ */
+ private final float BOUND_GAP_RATIO = 0.2f;
+
+ private float mTouchSlop;
+ private int mTopScrollBound;
+ private int mBottomScrollBound;
+ private int mLastDragY;
+ private Handler mScrollHandler;
+ private final Runnable mDragScroller =
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mLastDragY <= mTopScrollBound) {
+ smoothScrollBy(-DRAG_SCROLL_PX_UNIT, (int) SCROLL_HANDLER_DELAY_MILLIS);
+ } else if (mLastDragY >= mBottomScrollBound) {
+ smoothScrollBy(DRAG_SCROLL_PX_UNIT, (int) SCROLL_HANDLER_DELAY_MILLIS);
+ }
+ mScrollHandler.postDelayed(this, SCROLL_HANDLER_DELAY_MILLIS);
+ }
+ };
+ private boolean mIsDragScrollerRunning = false;
+ private int mTouchDownForDragStartX;
+ private int mTouchDownForDragStartY;
+ private Bitmap mDragShadowBitmap;
+ private ImageView mDragShadowOverlay;
+ private final AnimatorListenerAdapter mDragShadowOverAnimatorListener =
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mDragShadowBitmap != null) {
+ mDragShadowBitmap.recycle();
+ mDragShadowBitmap = null;
+ }
+ mDragShadowOverlay.setVisibility(GONE);
+ mDragShadowOverlay.setImageBitmap(null);
+ }
+ };
+ private View mDragShadowParent;
+ private int mAnimationDuration;
+ // X and Y offsets inside the item from where the user grabbed to the
+ // child's left coordinate. This is used to aid in the drawing of the drag shadow.
+ private int mTouchOffsetToChildLeft;
+ private int mTouchOffsetToChildTop;
+ private int mDragShadowLeft;
+ private int mDragShadowTop;
+ private DragDropController mDragDropController = new DragDropController(this);
+
+ public PhoneFavoriteListView(Context context) {
+ this(context, null);
+ }
+
+ public PhoneFavoriteListView(Context context, AttributeSet attrs) {
+ this(context, attrs, -1);
+ }
+
+ public PhoneFavoriteListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mAnimationDuration = context.getResources().getInteger(R.integer.fade_duration);
+ mTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
+ mDragDropController.addOnDragDropListener(this);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
+ }
+
+ /**
+ * TODO: This is all swipe to remove code (nothing to do with drag to remove). This should be
+ * cleaned up and removed once drag to remove becomes the only way to remove contacts.
+ */
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mTouchDownForDragStartX = (int) ev.getX();
+ mTouchDownForDragStartY = (int) ev.getY();
+ }
+
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onDragEvent(DragEvent event) {
+ final int action = event.getAction();
+ final int eX = (int) event.getX();
+ final int eY = (int) event.getY();
+ switch (action) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ {
+ if (!PhoneFavoriteTileView.DRAG_PHONE_FAVORITE_TILE.equals(event.getLocalState())) {
+ // Ignore any drag events that were not propagated by long pressing
+ // on a {@link PhoneFavoriteTileView}
+ return false;
+ }
+ if (!mDragDropController.handleDragStarted(this, eX, eY)) {
+ return false;
+ }
+ break;
+ }
+ case DragEvent.ACTION_DRAG_LOCATION:
+ mLastDragY = eY;
+ mDragDropController.handleDragHovered(this, eX, eY);
+ // Kick off {@link #mScrollHandler} if it's not started yet.
+ if (!mIsDragScrollerRunning
+ &&
+ // And if the distance traveled while dragging exceeds the touch slop
+ (Math.abs(mLastDragY - mTouchDownForDragStartY) >= 4 * mTouchSlop)) {
+ mIsDragScrollerRunning = true;
+ ensureScrollHandler();
+ mScrollHandler.postDelayed(mDragScroller, SCROLL_HANDLER_DELAY_MILLIS);
+ }
+ break;
+ case DragEvent.ACTION_DRAG_ENTERED:
+ final int boundGap = (int) (getHeight() * BOUND_GAP_RATIO);
+ mTopScrollBound = (getTop() + boundGap);
+ mBottomScrollBound = (getBottom() - boundGap);
+ break;
+ case DragEvent.ACTION_DRAG_EXITED:
+ case DragEvent.ACTION_DRAG_ENDED:
+ case DragEvent.ACTION_DROP:
+ ensureScrollHandler();
+ mScrollHandler.removeCallbacks(mDragScroller);
+ mIsDragScrollerRunning = false;
+ // Either a successful drop or it's ended with out drop.
+ if (action == DragEvent.ACTION_DROP || action == DragEvent.ACTION_DRAG_ENDED) {
+ mDragDropController.handleDragFinished(eX, eY, false);
+ }
+ break;
+ default:
+ break;
+ }
+ // This ListView will consume the drag events on behalf of its children.
+ return true;
+ }
+
+ public void setDragShadowOverlay(ImageView overlay) {
+ mDragShadowOverlay = overlay;
+ mDragShadowParent = (View) mDragShadowOverlay.getParent();
+ }
+
+ /** Find the view under the pointer. */
+ private View getViewAtPosition(int x, int y) {
+ final int count = getChildCount();
+ View child;
+ for (int childIdx = 0; childIdx < count; childIdx++) {
+ child = getChildAt(childIdx);
+ if (y >= child.getTop()
+ && y <= child.getBottom()
+ && x >= child.getLeft()
+ && x <= child.getRight()) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ private void ensureScrollHandler() {
+ if (mScrollHandler == null) {
+ mScrollHandler = getHandler();
+ }
+ }
+
+ public DragDropController getDragDropController() {
+ return mDragDropController;
+ }
+
+ @Override
+ public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView tileView) {
+ if (mDragShadowOverlay == null) {
+ return;
+ }
+
+ mDragShadowOverlay.clearAnimation();
+ mDragShadowBitmap = createDraggedChildBitmap(tileView);
+ if (mDragShadowBitmap == null) {
+ return;
+ }
+
+ tileView.getLocationOnScreen(mLocationOnScreen);
+ mDragShadowLeft = mLocationOnScreen[0];
+ mDragShadowTop = mLocationOnScreen[1];
+
+ // x and y are the coordinates of the on-screen touch event. Using these
+ // and the on-screen location of the tileView, calculate the difference between
+ // the position of the user's finger and the position of the tileView. These will
+ // be used to offset the location of the drag shadow so that it appears that the
+ // tileView is positioned directly under the user's finger.
+ mTouchOffsetToChildLeft = x - mDragShadowLeft;
+ mTouchOffsetToChildTop = y - mDragShadowTop;
+
+ mDragShadowParent.getLocationOnScreen(mLocationOnScreen);
+ mDragShadowLeft -= mLocationOnScreen[0];
+ mDragShadowTop -= mLocationOnScreen[1];
+
+ mDragShadowOverlay.setImageBitmap(mDragShadowBitmap);
+ mDragShadowOverlay.setVisibility(VISIBLE);
+ mDragShadowOverlay.setAlpha(DRAG_SHADOW_ALPHA);
+
+ mDragShadowOverlay.setX(mDragShadowLeft);
+ mDragShadowOverlay.setY(mDragShadowTop);
+ }
+
+ @Override
+ public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView tileView) {
+ // Update the drag shadow location.
+ mDragShadowParent.getLocationOnScreen(mLocationOnScreen);
+ mDragShadowLeft = x - mTouchOffsetToChildLeft - mLocationOnScreen[0];
+ mDragShadowTop = y - mTouchOffsetToChildTop - mLocationOnScreen[1];
+ // Draw the drag shadow at its last known location if the drag shadow exists.
+ if (mDragShadowOverlay != null) {
+ mDragShadowOverlay.setX(mDragShadowLeft);
+ mDragShadowOverlay.setY(mDragShadowTop);
+ }
+ }
+
+ @Override
+ public void onDragFinished(int x, int y) {
+ if (mDragShadowOverlay != null) {
+ mDragShadowOverlay.clearAnimation();
+ mDragShadowOverlay
+ .animate()
+ .alpha(0.0f)
+ .setDuration(mAnimationDuration)
+ .setListener(mDragShadowOverAnimatorListener)
+ .start();
+ }
+ }
+
+ @Override
+ public void onDroppedOnRemove() {}
+
+ private Bitmap createDraggedChildBitmap(View view) {
+ view.setDrawingCacheEnabled(true);
+ final Bitmap cache = view.getDrawingCache();
+
+ Bitmap bitmap = null;
+ if (cache != null) {
+ try {
+ bitmap = cache.copy(Bitmap.Config.ARGB_8888, false);
+ } catch (final OutOfMemoryError e) {
+ Log.w(LOG_TAG, "Failed to copy bitmap from Drawing cache", e);
+ bitmap = null;
+ }
+ }
+
+ view.destroyDrawingCache();
+ view.setDrawingCacheEnabled(false);
+
+ return bitmap;
+ }
+
+ @Override
+ public PhoneFavoriteSquareTileView getViewForLocation(int x, int y) {
+ getLocationOnScreen(mLocationOnScreen);
+ // Calculate the X and Y coordinates of the drag event relative to the view
+ final int viewX = x - mLocationOnScreen[0];
+ final int viewY = y - mLocationOnScreen[1];
+ final View child = getViewAtPosition(viewX, viewY);
+
+ if (!(child instanceof PhoneFavoriteSquareTileView)) {
+ return null;
+ }
+
+ return (PhoneFavoriteSquareTileView) child;
+ }
+}
diff --git a/java/com/android/dialer/app/list/PhoneFavoriteSquareTileView.java b/java/com/android/dialer/app/list/PhoneFavoriteSquareTileView.java
new file mode 100644
index 000000000..5a18d039b
--- /dev/null
+++ b/java/com/android/dialer/app/list/PhoneFavoriteSquareTileView.java
@@ -0,0 +1,119 @@
+/*
+
+* 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.dialer.app.list;
+
+import android.content.Context;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.QuickContact;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.TextView;
+import com.android.contacts.common.list.ContactEntry;
+import com.android.dialer.app.R;
+import com.android.dialer.compat.CompatUtils;
+
+/** Displays the contact's picture overlaid with their name and number type in a tile. */
+public class PhoneFavoriteSquareTileView extends PhoneFavoriteTileView {
+
+ private static final String TAG = PhoneFavoriteSquareTileView.class.getSimpleName();
+
+ private final float mHeightToWidthRatio;
+
+ private ImageButton mSecondaryButton;
+
+ private ContactEntry mContactEntry;
+
+ public PhoneFavoriteSquareTileView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mHeightToWidthRatio =
+ getResources().getFraction(R.dimen.contact_tile_height_to_width_ratio, 1, 1);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ final TextView nameView = (TextView) findViewById(R.id.contact_tile_name);
+ nameView.setElegantTextHeight(false);
+ final TextView phoneTypeView = (TextView) findViewById(R.id.contact_tile_phone_type);
+ phoneTypeView.setElegantTextHeight(false);
+ mSecondaryButton = (ImageButton) findViewById(R.id.contact_tile_secondary_button);
+ }
+
+ @Override
+ protected int getApproximateImageSize() {
+ // The picture is the full size of the tile (minus some padding, but we can be generous)
+ return getWidth();
+ }
+
+ private void launchQuickContact() {
+ if (CompatUtils.hasPrioritizedMimeType()) {
+ QuickContact.showQuickContact(
+ getContext(),
+ PhoneFavoriteSquareTileView.this,
+ getLookupUri(),
+ null,
+ Phone.CONTENT_ITEM_TYPE);
+ } else {
+ QuickContact.showQuickContact(
+ getContext(),
+ PhoneFavoriteSquareTileView.this,
+ getLookupUri(),
+ QuickContact.MODE_LARGE,
+ null);
+ }
+ }
+
+ @Override
+ public void loadFromContact(ContactEntry entry) {
+ super.loadFromContact(entry);
+ if (entry != null) {
+ mSecondaryButton.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ launchQuickContact();
+ }
+ });
+ }
+ mContactEntry = entry;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int width = MeasureSpec.getSize(widthMeasureSpec);
+ final int height = (int) (mHeightToWidthRatio * width);
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ getChildAt(i)
+ .measure(
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ }
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected String getNameForView(ContactEntry contactEntry) {
+ return contactEntry.getPreferredDisplayName();
+ }
+
+ public ContactEntry getContactEntry() {
+ return mContactEntry;
+ }
+}
diff --git a/java/com/android/dialer/app/list/PhoneFavoriteTileView.java b/java/com/android/dialer/app/list/PhoneFavoriteTileView.java
new file mode 100644
index 000000000..db89cf3dc
--- /dev/null
+++ b/java/com/android/dialer/app/list/PhoneFavoriteTileView.java
@@ -0,0 +1,155 @@
+/*
+
+* 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.dialer.app.list;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.MoreContactUtils;
+import com.android.contacts.common.list.ContactEntry;
+import com.android.contacts.common.list.ContactTileView;
+import com.android.dialer.app.R;
+
+/**
+ * A light version of the {@link com.android.contacts.common.list.ContactTileView} that is used in
+ * Dialtacts for frequently called contacts. Slightly different behavior from superclass when you
+ * tap it, you want to call the frequently-called number for the contact, even if that is not the
+ * default number for that contact. This abstract class is the super class to both the row and tile
+ * view.
+ */
+public abstract class PhoneFavoriteTileView extends ContactTileView {
+
+ // Constant to pass to the drag event so that the drag action only happens when a phone favorite
+ // tile is long pressed.
+ static final String DRAG_PHONE_FAVORITE_TILE = "PHONE_FAVORITE_TILE";
+ private static final String TAG = PhoneFavoriteTileView.class.getSimpleName();
+ private static final boolean DEBUG = false;
+ // These parameters instruct the photo manager to display the default image/letter at 70% of
+ // its normal size, and vertically offset upwards 12% towards the top of the letter tile, to
+ // make room for the contact name and number label at the bottom of the image.
+ private static final float DEFAULT_IMAGE_LETTER_OFFSET = -0.12f;
+ private static final float DEFAULT_IMAGE_LETTER_SCALE = 0.70f;
+ // Dummy clip data object that is attached to drag shadows so that text views
+ // don't crash with an NPE if the drag shadow is released in their bounds
+ private static final ClipData EMPTY_CLIP_DATA = ClipData.newPlainText("", "");
+ /** View that contains the transparent shadow that is overlaid on top of the contact image. */
+ private View mShadowOverlay;
+ /** Users' most frequent phone number. */
+ private String mPhoneNumberString;
+
+ public PhoneFavoriteTileView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mShadowOverlay = findViewById(R.id.shadow_overlay);
+
+ setOnLongClickListener(
+ new OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ final PhoneFavoriteTileView view = (PhoneFavoriteTileView) v;
+ // NOTE The drag shadow is handled in the ListView.
+ view.startDrag(
+ EMPTY_CLIP_DATA, new View.DragShadowBuilder(), DRAG_PHONE_FAVORITE_TILE, 0);
+ return true;
+ }
+ });
+ }
+
+ @Override
+ public void loadFromContact(ContactEntry entry) {
+ super.loadFromContact(entry);
+ // Set phone number to null in case we're reusing the view.
+ mPhoneNumberString = null;
+ if (entry != null) {
+ // Grab the phone-number to call directly. See {@link onClick()}.
+ mPhoneNumberString = entry.phoneNumber;
+
+ // If this is a blank entry, don't show anything.
+ // TODO krelease: Just hide the view for now. For this to truly look like an empty row
+ // the entire ContactTileRow needs to be hidden.
+ if (entry == ContactEntry.BLANK_ENTRY) {
+ setVisibility(View.INVISIBLE);
+ } else {
+ final ImageView starIcon = (ImageView) findViewById(R.id.contact_star_icon);
+ starIcon.setVisibility(entry.isFavorite ? View.VISIBLE : View.GONE);
+ setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ @Override
+ protected boolean isDarkTheme() {
+ return false;
+ }
+
+ @Override
+ protected OnClickListener createClickListener() {
+ return new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mListener == null) {
+ return;
+ }
+ if (TextUtils.isEmpty(mPhoneNumberString)) {
+ // Copy "superclass" implementation
+ mListener.onContactSelected(
+ getLookupUri(), MoreContactUtils.getTargetRectFromView(PhoneFavoriteTileView.this));
+ } else {
+ // When you tap a frequently-called contact, you want to
+ // call them at the number that you usually talk to them
+ // at (i.e. the one displayed in the UI), regardless of
+ // whether that's their default number.
+ mListener.onCallNumberDirectly(mPhoneNumberString);
+ }
+ }
+ };
+ }
+
+ @Override
+ protected DefaultImageRequest getDefaultImageRequest(String displayName, String lookupKey) {
+ return new DefaultImageRequest(
+ displayName,
+ lookupKey,
+ ContactPhotoManager.TYPE_DEFAULT,
+ DEFAULT_IMAGE_LETTER_SCALE,
+ DEFAULT_IMAGE_LETTER_OFFSET,
+ false);
+ }
+
+ @Override
+ protected void configureViewForImage(boolean isDefaultImage) {
+ // Hide the shadow overlay if the image is a default image (i.e. colored letter tile)
+ if (mShadowOverlay != null) {
+ mShadowOverlay.setVisibility(isDefaultImage ? View.GONE : View.VISIBLE);
+ }
+ }
+
+ @Override
+ protected boolean isContactPhotoCircular() {
+ // Unlike Contacts' tiles, the Dialer's favorites tiles are square.
+ return false;
+ }
+}
diff --git a/java/com/android/dialer/app/list/PhoneFavoritesTileAdapter.java b/java/com/android/dialer/app/list/PhoneFavoritesTileAdapter.java
new file mode 100644
index 000000000..c692ecac7
--- /dev/null
+++ b/java/com/android/dialer/app/list/PhoneFavoritesTileAdapter.java
@@ -0,0 +1,627 @@
+/*
+ * Copyright (C) 2013 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.dialer.app.list;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.PinnedPositions;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactTileLoaderFactory;
+import com.android.contacts.common.list.ContactEntry;
+import com.android.contacts.common.list.ContactTileView;
+import com.android.contacts.common.preference.ContactsPreferences;
+import com.android.dialer.app.R;
+import com.android.dialer.shortcuts.ShortcutRefresher;
+import com.google.common.collect.ComparisonChain;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.PriorityQueue;
+
+/** Also allows for a configurable number of columns as well as a maximum row of tiled contacts. */
+public class PhoneFavoritesTileAdapter extends BaseAdapter implements OnDragDropListener {
+
+ // Pinned positions start from 1, so there are a total of 20 maximum pinned contacts
+ private static final int PIN_LIMIT = 21;
+ private static final String TAG = PhoneFavoritesTileAdapter.class.getSimpleName();
+ private static final boolean DEBUG = false;
+ /**
+ * The soft limit on how many contact tiles to show. NOTE This soft limit would not restrict the
+ * number of starred contacts to show, rather 1. If the count of starred contacts is less than
+ * this limit, show 20 tiles total. 2. If the count of starred contacts is more than or equal to
+ * this limit, show all starred tiles and no frequents.
+ */
+ private static final int TILES_SOFT_LIMIT = 20;
+ /** Contact data stored in cache. This is used to populate the associated view. */
+ private ArrayList<ContactEntry> mContactEntries = null;
+
+ private int mNumFrequents;
+ private int mNumStarred;
+
+ private ContactTileView.Listener mListener;
+ private OnDataSetChangedForAnimationListener mDataSetChangedListener;
+ private Context mContext;
+ private Resources mResources;
+ private ContactsPreferences mContactsPreferences;
+ private final Comparator<ContactEntry> mContactEntryComparator =
+ new Comparator<ContactEntry>() {
+ @Override
+ public int compare(ContactEntry lhs, ContactEntry rhs) {
+ return ComparisonChain.start()
+ .compare(lhs.pinned, rhs.pinned)
+ .compare(getPreferredSortName(lhs), getPreferredSortName(rhs))
+ .result();
+ }
+
+ private String getPreferredSortName(ContactEntry contactEntry) {
+ if (mContactsPreferences.getSortOrder() == ContactsPreferences.SORT_ORDER_PRIMARY
+ || TextUtils.isEmpty(contactEntry.nameAlternative)) {
+ return contactEntry.namePrimary;
+ }
+ return contactEntry.nameAlternative;
+ }
+ };
+ /** Back up of the temporarily removed Contact during dragging. */
+ private ContactEntry mDraggedEntry = null;
+ /** Position of the temporarily removed contact in the cache. */
+ private int mDraggedEntryIndex = -1;
+ /** New position of the temporarily removed contact in the cache. */
+ private int mDropEntryIndex = -1;
+ /** New position of the temporarily entered contact in the cache. */
+ private int mDragEnteredEntryIndex = -1;
+
+ private boolean mAwaitingRemove = false;
+ private boolean mDelayCursorUpdates = false;
+ private ContactPhotoManager mPhotoManager;
+
+ /** Indicates whether a drag is in process. */
+ private boolean mInDragging = false;
+
+ public PhoneFavoritesTileAdapter(
+ Context context,
+ ContactTileView.Listener listener,
+ OnDataSetChangedForAnimationListener dataSetChangedListener) {
+ mDataSetChangedListener = dataSetChangedListener;
+ mListener = listener;
+ mContext = context;
+ mResources = context.getResources();
+ mContactsPreferences = new ContactsPreferences(mContext);
+ mNumFrequents = 0;
+ mContactEntries = new ArrayList<>();
+ }
+
+ void setPhotoLoader(ContactPhotoManager photoLoader) {
+ mPhotoManager = photoLoader;
+ }
+
+ /**
+ * Indicates whether a drag is in process.
+ *
+ * @param inDragging Boolean variable indicating whether there is a drag in process.
+ */
+ private void setInDragging(boolean inDragging) {
+ mDelayCursorUpdates = inDragging;
+ mInDragging = inDragging;
+ }
+
+ void refreshContactsPreferences() {
+ mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
+ mContactsPreferences.refreshValue(ContactsPreferences.SORT_ORDER_KEY);
+ }
+
+ /**
+ * Gets the number of frequents from the passed in cursor.
+ *
+ * <p>This methods is needed so the GroupMemberTileAdapter can override this.
+ *
+ * @param cursor The cursor to get number of frequents from.
+ */
+ private void saveNumFrequentsFromCursor(Cursor cursor) {
+ mNumFrequents = cursor.getCount() - mNumStarred;
+ }
+
+ /**
+ * Creates {@link ContactTileView}s for each item in {@link Cursor}.
+ *
+ * <p>Else use {@link ContactTileLoaderFactory}
+ */
+ void setContactCursor(Cursor cursor) {
+ if (!mDelayCursorUpdates && cursor != null && !cursor.isClosed()) {
+ mNumStarred = getNumStarredContacts(cursor);
+ if (mAwaitingRemove) {
+ mDataSetChangedListener.cacheOffsetsForDatasetChange();
+ }
+
+ saveNumFrequentsFromCursor(cursor);
+ saveCursorToCache(cursor);
+ // cause a refresh of any views that rely on this data
+ notifyDataSetChanged();
+ // about to start redraw
+ mDataSetChangedListener.onDataSetChangedForAnimation();
+ }
+ }
+
+ /**
+ * Saves the cursor data to the cache, to speed up UI changes.
+ *
+ * @param cursor Returned cursor from {@link ContactTileLoaderFactory} with data to populate the
+ * view.
+ */
+ private void saveCursorToCache(Cursor cursor) {
+ mContactEntries.clear();
+
+ if (cursor == null) {
+ return;
+ }
+
+ final LongSparseArray<Object> duplicates = new LongSparseArray<>(cursor.getCount());
+
+ // Track the length of {@link #mContactEntries} and compare to {@link #TILES_SOFT_LIMIT}.
+ int counter = 0;
+
+ // The cursor should not be closed since this is invoked from a CursorLoader.
+ if (cursor.moveToFirst()) {
+ int starredColumn = cursor.getColumnIndexOrThrow(Contacts.STARRED);
+ int contactIdColumn = cursor.getColumnIndexOrThrow(Phone.CONTACT_ID);
+ int photoUriColumn = cursor.getColumnIndexOrThrow(Contacts.PHOTO_URI);
+ int lookupKeyColumn = cursor.getColumnIndexOrThrow(Contacts.LOOKUP_KEY);
+ int pinnedColumn = cursor.getColumnIndexOrThrow(Contacts.PINNED);
+ int nameColumn = cursor.getColumnIndexOrThrow(Contacts.DISPLAY_NAME_PRIMARY);
+ int nameAlternativeColumn = cursor.getColumnIndexOrThrow(Contacts.DISPLAY_NAME_ALTERNATIVE);
+ int isDefaultNumberColumn = cursor.getColumnIndexOrThrow(Phone.IS_SUPER_PRIMARY);
+ int phoneTypeColumn = cursor.getColumnIndexOrThrow(Phone.TYPE);
+ int phoneLabelColumn = cursor.getColumnIndexOrThrow(Phone.LABEL);
+ int phoneNumberColumn = cursor.getColumnIndexOrThrow(Phone.NUMBER);
+ do {
+ final int starred = cursor.getInt(starredColumn);
+ final long id;
+
+ // We display a maximum of TILES_SOFT_LIMIT contacts, or the total number of starred
+ // whichever is greater.
+ if (starred < 1 && counter >= TILES_SOFT_LIMIT) {
+ break;
+ } else {
+ id = cursor.getLong(contactIdColumn);
+ }
+
+ final ContactEntry existing = (ContactEntry) duplicates.get(id);
+ if (existing != null) {
+ // Check if the existing number is a default number. If not, clear the phone number
+ // and label fields so that the disambiguation dialog will show up.
+ if (!existing.isDefaultNumber) {
+ existing.phoneLabel = null;
+ existing.phoneNumber = null;
+ }
+ continue;
+ }
+
+ final String photoUri = cursor.getString(photoUriColumn);
+ final String lookupKey = cursor.getString(lookupKeyColumn);
+ final int pinned = cursor.getInt(pinnedColumn);
+ final String name = cursor.getString(nameColumn);
+ final String nameAlternative = cursor.getString(nameAlternativeColumn);
+ final boolean isStarred = cursor.getInt(starredColumn) > 0;
+ final boolean isDefaultNumber = cursor.getInt(isDefaultNumberColumn) > 0;
+
+ final ContactEntry contact = new ContactEntry();
+
+ contact.id = id;
+ contact.namePrimary =
+ (!TextUtils.isEmpty(name)) ? name : mResources.getString(R.string.missing_name);
+ contact.nameAlternative =
+ (!TextUtils.isEmpty(nameAlternative))
+ ? nameAlternative
+ : mResources.getString(R.string.missing_name);
+ contact.nameDisplayOrder = mContactsPreferences.getDisplayOrder();
+ contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
+ contact.lookupKey = lookupKey;
+ contact.lookupUri =
+ ContentUris.withAppendedId(
+ Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
+ contact.isFavorite = isStarred;
+ contact.isDefaultNumber = isDefaultNumber;
+
+ // Set phone number and label
+ final int phoneNumberType = cursor.getInt(phoneTypeColumn);
+ final String phoneNumberCustomLabel = cursor.getString(phoneLabelColumn);
+ contact.phoneLabel =
+ (String) Phone.getTypeLabel(mResources, phoneNumberType, phoneNumberCustomLabel);
+ contact.phoneNumber = cursor.getString(phoneNumberColumn);
+
+ contact.pinned = pinned;
+ mContactEntries.add(contact);
+
+ duplicates.put(id, contact);
+
+ counter++;
+ } while (cursor.moveToNext());
+ }
+
+ mAwaitingRemove = false;
+
+ arrangeContactsByPinnedPosition(mContactEntries);
+
+ ShortcutRefresher.refresh(mContext, mContactEntries);
+ notifyDataSetChanged();
+ }
+
+ /** Iterates over the {@link Cursor} Returns position of the first NON Starred Contact */
+ private int getNumStarredContacts(Cursor cursor) {
+ if (cursor == null) {
+ return 0;
+ }
+
+ if (cursor.moveToFirst()) {
+ int starredColumn = cursor.getColumnIndex(Contacts.STARRED);
+ do {
+ if (cursor.getInt(starredColumn) == 0) {
+ return cursor.getPosition();
+ }
+ } while (cursor.moveToNext());
+ }
+ // There are not NON Starred contacts in cursor
+ // Set divider position to end
+ return cursor.getCount();
+ }
+
+ /** Returns the number of frequents that will be displayed in the list. */
+ int getNumFrequents() {
+ return mNumFrequents;
+ }
+
+ @Override
+ public int getCount() {
+ if (mContactEntries == null) {
+ return 0;
+ }
+
+ return mContactEntries.size();
+ }
+
+ /**
+ * Returns an ArrayList of the {@link ContactEntry}s that are to appear on the row for the given
+ * position.
+ */
+ @Override
+ public ContactEntry getItem(int position) {
+ return mContactEntries.get(position);
+ }
+
+ /**
+ * For the top row of tiled contacts, the item id is the position of the row of contacts. For
+ * frequent contacts, the item id is the maximum number of rows of tiled contacts + the actual
+ * contact id. Since contact ids are always greater than 0, this guarantees that all items within
+ * this adapter will always have unique ids.
+ */
+ @Override
+ public long getItemId(int position) {
+ return getItem(position).id;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return getCount() > 0;
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ if (DEBUG) {
+ Log.v(TAG, "notifyDataSetChanged");
+ }
+ super.notifyDataSetChanged();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (DEBUG) {
+ Log.v(TAG, "get view for " + String.valueOf(position));
+ }
+
+ PhoneFavoriteTileView tileView = null;
+
+ if (convertView instanceof PhoneFavoriteTileView) {
+ tileView = (PhoneFavoriteTileView) convertView;
+ }
+
+ if (tileView == null) {
+ tileView =
+ (PhoneFavoriteTileView) View.inflate(mContext, R.layout.phone_favorite_tile_view, null);
+ }
+ tileView.setPhotoManager(mPhotoManager);
+ tileView.setListener(mListener);
+ tileView.loadFromContact(getItem(position));
+ return tileView;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return ViewTypes.COUNT;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return ViewTypes.TILE;
+ }
+
+ /**
+ * Temporarily removes a contact from the list for UI refresh. Stores data for this contact in the
+ * back-up variable.
+ *
+ * @param index Position of the contact to be removed.
+ */
+ private void popContactEntry(int index) {
+ if (isIndexInBound(index)) {
+ mDraggedEntry = mContactEntries.get(index);
+ mDraggedEntryIndex = index;
+ mDragEnteredEntryIndex = index;
+ markDropArea(mDragEnteredEntryIndex);
+ }
+ }
+
+ /**
+ * @param itemIndex Position of the contact in {@link #mContactEntries}.
+ * @return True if the given index is valid for {@link #mContactEntries}.
+ */
+ boolean isIndexInBound(int itemIndex) {
+ return itemIndex >= 0 && itemIndex < mContactEntries.size();
+ }
+
+ /**
+ * Mark the tile as drop area by given the item index in {@link #mContactEntries}.
+ *
+ * @param itemIndex Position of the contact in {@link #mContactEntries}.
+ */
+ private void markDropArea(int itemIndex) {
+ if (mDraggedEntry != null
+ && isIndexInBound(mDragEnteredEntryIndex)
+ && isIndexInBound(itemIndex)) {
+ mDataSetChangedListener.cacheOffsetsForDatasetChange();
+ // Remove the old placeholder item and place the new placeholder item.
+ mContactEntries.remove(mDragEnteredEntryIndex);
+ mDragEnteredEntryIndex = itemIndex;
+ mContactEntries.add(mDragEnteredEntryIndex, ContactEntry.BLANK_ENTRY);
+ ContactEntry.BLANK_ENTRY.id = mDraggedEntry.id;
+ mDataSetChangedListener.onDataSetChangedForAnimation();
+ notifyDataSetChanged();
+ }
+ }
+
+ /** Drops the temporarily removed contact to the desired location in the list. */
+ private void handleDrop() {
+ boolean changed = false;
+ if (mDraggedEntry != null) {
+ if (isIndexInBound(mDragEnteredEntryIndex) && mDragEnteredEntryIndex != mDraggedEntryIndex) {
+ // Don't add the ContactEntry here (to prevent a double animation from occuring).
+ // When we receive a new cursor the list of contact entries will automatically be
+ // populated with the dragged ContactEntry at the correct spot.
+ mDropEntryIndex = mDragEnteredEntryIndex;
+ mContactEntries.set(mDropEntryIndex, mDraggedEntry);
+ mDataSetChangedListener.cacheOffsetsForDatasetChange();
+ changed = true;
+ } else if (isIndexInBound(mDraggedEntryIndex)) {
+ // If {@link #mDragEnteredEntryIndex} is invalid,
+ // falls back to the original position of the contact.
+ mContactEntries.remove(mDragEnteredEntryIndex);
+ mContactEntries.add(mDraggedEntryIndex, mDraggedEntry);
+ mDropEntryIndex = mDraggedEntryIndex;
+ notifyDataSetChanged();
+ }
+
+ if (changed && mDropEntryIndex < PIN_LIMIT) {
+ final ArrayList<ContentProviderOperation> operations =
+ getReflowedPinningOperations(mContactEntries, mDraggedEntryIndex, mDropEntryIndex);
+ if (!operations.isEmpty()) {
+ // update the database here with the new pinned positions
+ try {
+ mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations);
+ } catch (RemoteException | OperationApplicationException e) {
+ Log.e(TAG, "Exception thrown when pinning contacts", e);
+ }
+ }
+ }
+ mDraggedEntry = null;
+ }
+ }
+
+ /**
+ * Used when a contact is removed from speeddial. This will both unstar and set pinned position of
+ * the contact to PinnedPosition.DEMOTED so that it doesn't show up anymore in the favorites list.
+ */
+ private void unstarAndUnpinContact(Uri contactUri) {
+ final ContentValues values = new ContentValues(2);
+ values.put(Contacts.STARRED, false);
+ values.put(Contacts.PINNED, PinnedPositions.DEMOTED);
+ mContext.getContentResolver().update(contactUri, values, null, null);
+ }
+
+ /**
+ * Given a list of contacts that each have pinned positions, rearrange the list (destructive) such
+ * that all pinned contacts are in their defined pinned positions, and unpinned contacts take the
+ * spaces between those pinned contacts. Demoted contacts should not appear in the resulting list.
+ *
+ * <p>This method also updates the pinned positions of pinned contacts so that they are all unique
+ * positive integers within range from 0 to toArrange.size() - 1. This is because when the contact
+ * entries are read from the database, it is possible for them to have overlapping pin positions
+ * due to sync or modifications by third party apps.
+ */
+ @VisibleForTesting
+ private void arrangeContactsByPinnedPosition(ArrayList<ContactEntry> toArrange) {
+ final PriorityQueue<ContactEntry> pinnedQueue =
+ new PriorityQueue<>(PIN_LIMIT, mContactEntryComparator);
+
+ final List<ContactEntry> unpinnedContacts = new LinkedList<>();
+
+ final int length = toArrange.size();
+ for (int i = 0; i < length; i++) {
+ final ContactEntry contact = toArrange.get(i);
+ // Decide whether the contact is hidden(demoted), pinned, or unpinned
+ if (contact.pinned > PIN_LIMIT || contact.pinned == PinnedPositions.UNPINNED) {
+ unpinnedContacts.add(contact);
+ } else if (contact.pinned > PinnedPositions.DEMOTED) {
+ // Demoted or contacts with negative pinned positions are ignored.
+ // Pinned contacts go into a priority queue where they are ranked by pinned
+ // position. This is required because the contacts provider does not return
+ // contacts ordered by pinned position.
+ pinnedQueue.add(contact);
+ }
+ }
+
+ final int maxToPin = Math.min(PIN_LIMIT, pinnedQueue.size() + unpinnedContacts.size());
+
+ toArrange.clear();
+ for (int i = 1; i < maxToPin + 1; i++) {
+ if (!pinnedQueue.isEmpty() && pinnedQueue.peek().pinned <= i) {
+ final ContactEntry toPin = pinnedQueue.poll();
+ toPin.pinned = i;
+ toArrange.add(toPin);
+ } else if (!unpinnedContacts.isEmpty()) {
+ toArrange.add(unpinnedContacts.remove(0));
+ }
+ }
+
+ // If there are still contacts in pinnedContacts at this point, it means that the pinned
+ // positions of these pinned contacts exceed the actual number of contacts in the list.
+ // For example, the user had 10 frequents, starred and pinned one of them at the last spot,
+ // and then cleared frequents. Contacts in this situation should become unpinned.
+ while (!pinnedQueue.isEmpty()) {
+ final ContactEntry entry = pinnedQueue.poll();
+ entry.pinned = PinnedPositions.UNPINNED;
+ toArrange.add(entry);
+ }
+
+ // Any remaining unpinned contacts that weren't in the gaps between the pinned contacts
+ // now just get appended to the end of the list.
+ toArrange.addAll(unpinnedContacts);
+ }
+
+ /**
+ * Given an existing list of contact entries and a single entry that is to be pinned at a
+ * particular position, return a list of {@link ContentProviderOperation}s that contains new
+ * pinned positions for all contacts that are forced to be pinned at new positions, trying as much
+ * as possible to keep pinned contacts at their original location.
+ *
+ * <p>At this point in time the pinned position of each contact in the list has already been
+ * updated by {@link #arrangeContactsByPinnedPosition}, so we can assume that all pinned
+ * positions(within {@link #PIN_LIMIT} are unique positive integers.
+ */
+ @VisibleForTesting
+ private ArrayList<ContentProviderOperation> getReflowedPinningOperations(
+ ArrayList<ContactEntry> list, int oldPos, int newPinPos) {
+ final ArrayList<ContentProviderOperation> positions = new ArrayList<>();
+ final int lowerBound = Math.min(oldPos, newPinPos);
+ final int upperBound = Math.max(oldPos, newPinPos);
+ for (int i = lowerBound; i <= upperBound; i++) {
+ final ContactEntry entry = list.get(i);
+
+ // Pinned positions in the database start from 1 instead of being zero-indexed like
+ // arrays, so offset by 1.
+ final int databasePinnedPosition = i + 1;
+ if (entry.pinned == databasePinnedPosition) {
+ continue;
+ }
+
+ final Uri uri = Uri.withAppendedPath(Contacts.CONTENT_URI, String.valueOf(entry.id));
+ final ContentValues values = new ContentValues();
+ values.put(Contacts.PINNED, databasePinnedPosition);
+ positions.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
+ }
+ return positions;
+ }
+
+ @Override
+ public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {
+ setInDragging(true);
+ final int itemIndex = mContactEntries.indexOf(view.getContactEntry());
+ popContactEntry(itemIndex);
+ }
+
+ @Override
+ public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {
+ if (view == null) {
+ // The user is hovering over a view that is not a contact tile, no need to do
+ // anything here.
+ return;
+ }
+ final int itemIndex = mContactEntries.indexOf(view.getContactEntry());
+ if (mInDragging
+ && mDragEnteredEntryIndex != itemIndex
+ && isIndexInBound(itemIndex)
+ && itemIndex < PIN_LIMIT
+ && itemIndex >= 0) {
+ markDropArea(itemIndex);
+ }
+ }
+
+ @Override
+ public void onDragFinished(int x, int y) {
+ setInDragging(false);
+ // A contact has been dragged to the RemoveView in order to be unstarred, so simply wait
+ // for the new contact cursor which will cause the UI to be refreshed without the unstarred
+ // contact.
+ if (!mAwaitingRemove) {
+ handleDrop();
+ }
+ }
+
+ @Override
+ public void onDroppedOnRemove() {
+ if (mDraggedEntry != null) {
+ unstarAndUnpinContact(mDraggedEntry.lookupUri);
+ mAwaitingRemove = true;
+ }
+ }
+
+ interface OnDataSetChangedForAnimationListener {
+
+ void onDataSetChangedForAnimation(long... idsInPlace);
+
+ void cacheOffsetsForDatasetChange();
+ }
+
+ private static class ViewTypes {
+
+ static final int TILE = 0;
+ static final int COUNT = 1;
+ }
+}
diff --git a/java/com/android/dialer/app/list/RegularSearchFragment.java b/java/com/android/dialer/app/list/RegularSearchFragment.java
new file mode 100644
index 000000000..26959539b
--- /dev/null
+++ b/java/com/android/dialer/app/list/RegularSearchFragment.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2013 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.dialer.app.list;
+
+import static android.Manifest.permission.READ_CONTACTS;
+
+import android.app.Activity;
+import android.content.pm.PackageManager;
+import android.support.v13.app.FragmentCompat;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import com.android.contacts.common.list.ContactEntryListAdapter;
+import com.android.contacts.common.list.PinnedHeaderListView;
+import com.android.dialer.app.R;
+import com.android.dialer.app.widget.EmptyContentView;
+import com.android.dialer.app.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener;
+import com.android.dialer.callintent.nano.CallInitiationType;
+import com.android.dialer.phonenumbercache.CachedNumberLookupService;
+import com.android.dialer.phonenumbercache.PhoneNumberCache;
+import com.android.dialer.util.PermissionsUtil;
+
+public class RegularSearchFragment extends SearchFragment
+ implements OnEmptyViewActionButtonClickedListener,
+ FragmentCompat.OnRequestPermissionsResultCallback {
+
+ public static final int PERMISSION_REQUEST_CODE = 1;
+
+ private static final int SEARCH_DIRECTORY_RESULT_LIMIT = 5;
+ protected String mPermissionToRequest;
+
+ public RegularSearchFragment() {
+ configureDirectorySearch();
+ }
+
+ public void configureDirectorySearch() {
+ setDirectorySearchEnabled(true);
+ setDirectoryResultLimit(SEARCH_DIRECTORY_RESULT_LIMIT);
+ }
+
+ @Override
+ protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
+ super.onCreateView(inflater, container);
+ ((PinnedHeaderListView) getListView()).setScrollToSectionOnHeaderTouch(true);
+ }
+
+ @Override
+ protected ContactEntryListAdapter createListAdapter() {
+ RegularSearchListAdapter adapter = new RegularSearchListAdapter(getActivity());
+ adapter.setDisplayPhotos(true);
+ adapter.setUseCallableUri(usesCallableUri());
+ adapter.setListener(this);
+ return adapter;
+ }
+
+ @Override
+ protected void cacheContactInfo(int position) {
+ CachedNumberLookupService cachedNumberLookupService =
+ PhoneNumberCache.get(getContext()).getCachedNumberLookupService();
+ if (cachedNumberLookupService != null) {
+ final RegularSearchListAdapter adapter = (RegularSearchListAdapter) getAdapter();
+ cachedNumberLookupService.addContact(
+ getContext(), adapter.getContactInfo(cachedNumberLookupService, position));
+ }
+ }
+
+ @Override
+ protected void setupEmptyView() {
+ if (mEmptyView != null && getActivity() != null) {
+ final int imageResource;
+ final int actionLabelResource;
+ final int descriptionResource;
+ final OnEmptyViewActionButtonClickedListener listener;
+ if (!PermissionsUtil.hasPermission(getActivity(), READ_CONTACTS)) {
+ imageResource = R.drawable.empty_contacts;
+ actionLabelResource = R.string.permission_single_turn_on;
+ descriptionResource = R.string.permission_no_search;
+ listener = this;
+ mPermissionToRequest = READ_CONTACTS;
+ } else {
+ imageResource = EmptyContentView.NO_IMAGE;
+ actionLabelResource = EmptyContentView.NO_LABEL;
+ descriptionResource = EmptyContentView.NO_LABEL;
+ listener = null;
+ mPermissionToRequest = null;
+ }
+
+ mEmptyView.setImage(imageResource);
+ mEmptyView.setActionLabel(actionLabelResource);
+ mEmptyView.setDescription(descriptionResource);
+ if (listener != null) {
+ mEmptyView.setActionClickedListener(listener);
+ }
+ }
+ }
+
+ @Override
+ public void onEmptyViewActionButtonClicked() {
+ final Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ if (READ_CONTACTS.equals(mPermissionToRequest)) {
+ FragmentCompat.requestPermissions(
+ this, new String[] {mPermissionToRequest}, PERMISSION_REQUEST_CODE);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, String[] permissions, int[] grantResults) {
+ if (requestCode == PERMISSION_REQUEST_CODE) {
+ setupEmptyView();
+ if (grantResults != null
+ && grantResults.length == 1
+ && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
+ PermissionsUtil.notifyPermissionGranted(getActivity(), permissions[0]);
+ }
+ }
+ }
+
+ @Override
+ protected int getCallInitiationType(boolean isRemoteDirectory) {
+ return isRemoteDirectory
+ ? CallInitiationType.Type.REMOTE_DIRECTORY
+ : CallInitiationType.Type.REGULAR_SEARCH;
+ }
+
+ public interface CapabilityChecker {
+
+ boolean isNearbyPlacesSearchEnabled();
+ }
+}
diff --git a/java/com/android/dialer/app/list/RegularSearchListAdapter.java b/java/com/android/dialer/app/list/RegularSearchListAdapter.java
new file mode 100644
index 000000000..94544d2db
--- /dev/null
+++ b/java/com/android/dialer/app/list/RegularSearchListAdapter.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2013 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.dialer.app.list;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.text.TextUtils;
+import com.android.contacts.common.ContactsUtils;
+import com.android.contacts.common.compat.DirectoryCompat;
+import com.android.contacts.common.list.DirectoryPartition;
+import com.android.dialer.phonenumbercache.CachedNumberLookupService;
+import com.android.dialer.phonenumbercache.CachedNumberLookupService.CachedContactInfo;
+import com.android.dialer.phonenumbercache.ContactInfo;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.android.dialer.util.CallUtil;
+
+/** List adapter to display regular search results. */
+public class RegularSearchListAdapter extends DialerPhoneNumberListAdapter {
+
+ protected boolean mIsQuerySipAddress;
+
+ public RegularSearchListAdapter(Context context) {
+ super(context);
+ setShortcutEnabled(SHORTCUT_CREATE_NEW_CONTACT, false);
+ setShortcutEnabled(SHORTCUT_ADD_TO_EXISTING_CONTACT, false);
+ }
+
+ public CachedContactInfo getContactInfo(CachedNumberLookupService lookupService, int position) {
+ ContactInfo info = new ContactInfo();
+ CachedContactInfo cacheInfo = lookupService.buildCachedContactInfo(info);
+ final Cursor item = (Cursor) getItem(position);
+ if (item != null) {
+ final DirectoryPartition partition =
+ (DirectoryPartition) getPartition(getPartitionForPosition(position));
+ final long directoryId = partition.getDirectoryId();
+ final boolean isExtendedDirectory = isExtendedDirectory(directoryId);
+
+ info.name = item.getString(PhoneQuery.DISPLAY_NAME);
+ info.type = item.getInt(PhoneQuery.PHONE_TYPE);
+ info.label = item.getString(PhoneQuery.PHONE_LABEL);
+ info.number = item.getString(PhoneQuery.PHONE_NUMBER);
+ final String photoUriStr = item.getString(PhoneQuery.PHOTO_URI);
+ info.photoUri = photoUriStr == null ? null : Uri.parse(photoUriStr);
+ /*
+ * An extended directory is custom directory in the app, but not a directory provided by
+ * framework. So it can't be USER_TYPE_WORK.
+ *
+ * When a search result is selected, RegularSearchFragment calls getContactInfo and
+ * cache the resulting @{link ContactInfo} into local db. Set usertype to USER_TYPE_WORK
+ * only if it's NOT extended directory id and is enterprise directory.
+ */
+ info.userType =
+ !isExtendedDirectory && DirectoryCompat.isEnterpriseDirectoryId(directoryId)
+ ? ContactsUtils.USER_TYPE_WORK
+ : ContactsUtils.USER_TYPE_CURRENT;
+
+ cacheInfo.setLookupKey(item.getString(PhoneQuery.LOOKUP_KEY));
+
+ final String sourceName = partition.getLabel();
+ if (isExtendedDirectory) {
+ cacheInfo.setExtendedSource(sourceName, directoryId);
+ } else {
+ cacheInfo.setDirectorySource(sourceName, directoryId);
+ }
+ }
+ return cacheInfo;
+ }
+
+ @Override
+ public String getFormattedQueryString() {
+ if (mIsQuerySipAddress) {
+ // Return unnormalized SIP address
+ return getQueryString();
+ }
+ return super.getFormattedQueryString();
+ }
+
+ @Override
+ public void setQueryString(String queryString) {
+ // Don't show actions if the query string contains a letter.
+ final boolean showNumberShortcuts =
+ !TextUtils.isEmpty(getFormattedQueryString()) && hasDigitsInQueryString();
+ mIsQuerySipAddress = PhoneNumberHelper.isUriNumber(queryString);
+
+ if (isChanged(showNumberShortcuts)) {
+ notifyDataSetChanged();
+ }
+ super.setQueryString(queryString);
+ }
+
+ protected boolean isChanged(boolean showNumberShortcuts) {
+ boolean changed = false;
+ changed |= setShortcutEnabled(SHORTCUT_DIRECT_CALL, showNumberShortcuts || mIsQuerySipAddress);
+ changed |= setShortcutEnabled(SHORTCUT_SEND_SMS_MESSAGE, showNumberShortcuts);
+ changed |=
+ setShortcutEnabled(
+ SHORTCUT_MAKE_VIDEO_CALL, showNumberShortcuts && CallUtil.isVideoEnabled(getContext()));
+ return changed;
+ }
+
+ /** Whether there is at least one digit in the query string. */
+ private boolean hasDigitsInQueryString() {
+ String queryString = getQueryString();
+ int length = queryString.length();
+ for (int i = 0; i < length; i++) {
+ if (Character.isDigit(queryString.charAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/java/com/android/dialer/app/list/RemoveView.java b/java/com/android/dialer/app/list/RemoveView.java
new file mode 100644
index 000000000..3b917db43
--- /dev/null
+++ b/java/com/android/dialer/app/list/RemoveView.java
@@ -0,0 +1,105 @@
+/*
+ * 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.dialer.app.list;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.DragEvent;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.android.dialer.app.R;
+
+public class RemoveView extends FrameLayout {
+
+ DragDropController mDragDropController;
+ TextView mRemoveText;
+ ImageView mRemoveIcon;
+ int mUnhighlightedColor;
+ int mHighlightedColor;
+ Drawable mRemoveDrawable;
+
+ public RemoveView(Context context) {
+ super(context);
+ }
+
+ public RemoveView(Context context, AttributeSet attrs) {
+ this(context, attrs, -1);
+ }
+
+ public RemoveView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mRemoveText = (TextView) findViewById(R.id.remove_view_text);
+ mRemoveIcon = (ImageView) findViewById(R.id.remove_view_icon);
+ final Resources r = getResources();
+ mUnhighlightedColor = r.getColor(R.color.remove_text_color);
+ mHighlightedColor = r.getColor(R.color.remove_highlighted_text_color);
+ mRemoveDrawable = r.getDrawable(R.drawable.ic_remove);
+ }
+
+ public void setDragDropController(DragDropController controller) {
+ mDragDropController = controller;
+ }
+
+ @Override
+ public boolean onDragEvent(DragEvent event) {
+ final int action = event.getAction();
+ switch (action) {
+ case DragEvent.ACTION_DRAG_ENTERED:
+ // TODO: This is temporary solution and should be removed once accessibility for
+ // drag and drop is supported by framework(b/26871588).
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT);
+ setAppearanceHighlighted();
+ break;
+ case DragEvent.ACTION_DRAG_EXITED:
+ setAppearanceNormal();
+ break;
+ case DragEvent.ACTION_DRAG_LOCATION:
+ if (mDragDropController != null) {
+ mDragDropController.handleDragHovered(this, (int) event.getX(), (int) event.getY());
+ }
+ break;
+ case DragEvent.ACTION_DROP:
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT);
+ if (mDragDropController != null) {
+ mDragDropController.handleDragFinished((int) event.getX(), (int) event.getY(), true);
+ }
+ setAppearanceNormal();
+ break;
+ }
+ return true;
+ }
+
+ private void setAppearanceNormal() {
+ mRemoveText.setTextColor(mUnhighlightedColor);
+ mRemoveIcon.setColorFilter(mUnhighlightedColor);
+ invalidate();
+ }
+
+ private void setAppearanceHighlighted() {
+ mRemoveText.setTextColor(mHighlightedColor);
+ mRemoveIcon.setColorFilter(mHighlightedColor);
+ invalidate();
+ }
+}
diff --git a/java/com/android/dialer/app/list/SearchFragment.java b/java/com/android/dialer/app/list/SearchFragment.java
new file mode 100644
index 000000000..4a7d48ae4
--- /dev/null
+++ b/java/com/android/dialer/app/list/SearchFragment.java
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2013 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.dialer.app.list;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Interpolator;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.Space;
+import com.android.contacts.common.list.ContactEntryListAdapter;
+import com.android.contacts.common.list.ContactListItemView;
+import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
+import com.android.contacts.common.list.PhoneNumberPickerFragment;
+import com.android.contacts.common.util.FabUtil;
+import com.android.dialer.animation.AnimUtils;
+import com.android.dialer.app.R;
+import com.android.dialer.app.dialpad.DialpadFragment.ErrorDialogFragment;
+import com.android.dialer.app.widget.DialpadSearchEmptyContentView;
+import com.android.dialer.app.widget.EmptyContentView;
+import com.android.dialer.callintent.nano.CallSpecificAppData;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.IntentUtil;
+import com.android.dialer.util.PermissionsUtil;
+
+public class SearchFragment extends PhoneNumberPickerFragment {
+
+ protected EmptyContentView mEmptyView;
+ private OnListFragmentScrolledListener mActivityScrollListener;
+ private View.OnTouchListener mActivityOnTouchListener;
+ /*
+ * Stores the untouched user-entered string that is used to populate the add to contacts
+ * intent.
+ */
+ private String mAddToContactNumber;
+ private int mActionBarHeight;
+ private int mShadowHeight;
+ private int mPaddingTop;
+ private int mShowDialpadDuration;
+ private int mHideDialpadDuration;
+ /**
+ * Used to resize the list view containing search results so that it fits the available space
+ * above the dialpad. Does not have a user-visible effect in regular touch usage (since the
+ * dialpad hides that portion of the ListView anyway), but improves usability in accessibility
+ * mode.
+ */
+ private Space mSpacer;
+
+ private HostInterface mActivity;
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ setQuickContactEnabled(true);
+ setAdjustSelectionBoundsEnabled(false);
+ setDarkTheme(false);
+ setPhotoPosition(ContactListItemView.getDefaultPhotoPosition(false /* opposite */));
+ setUseCallableUri(true);
+
+ try {
+ mActivityScrollListener = (OnListFragmentScrolledListener) activity;
+ } catch (ClassCastException e) {
+ LogUtil.v(
+ "SearchFragment.onAttach",
+ activity.toString()
+ + " doesn't implement OnListFragmentScrolledListener. "
+ + "Ignoring.");
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (isSearchMode()) {
+ getAdapter().setHasHeader(0, false);
+ }
+
+ mActivity = (HostInterface) getActivity();
+
+ final Resources res = getResources();
+ mActionBarHeight = mActivity.getActionBarHeight();
+ mShadowHeight = res.getDrawable(R.drawable.search_shadow).getIntrinsicHeight();
+ mPaddingTop = res.getDimensionPixelSize(R.dimen.search_list_padding_top);
+ mShowDialpadDuration = res.getInteger(R.integer.dialpad_slide_in_duration);
+ mHideDialpadDuration = res.getInteger(R.integer.dialpad_slide_out_duration);
+
+ final ListView listView = getListView();
+
+ if (mEmptyView == null) {
+ if (this instanceof SmartDialSearchFragment) {
+ mEmptyView = new DialpadSearchEmptyContentView(getActivity());
+ } else {
+ mEmptyView = new EmptyContentView(getActivity());
+ }
+ ((ViewGroup) getListView().getParent()).addView(mEmptyView);
+ getListView().setEmptyView(mEmptyView);
+ setupEmptyView();
+ }
+
+ listView.setBackgroundColor(res.getColor(R.color.background_dialer_results));
+ listView.setClipToPadding(false);
+ setVisibleScrollbarEnabled(false);
+
+ //Turn of accessibility live region as the list constantly update itself and spam messages.
+ listView.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_NONE);
+ ContentChangedFilter.addToParent(listView);
+
+ listView.setOnScrollListener(
+ new OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ if (mActivityScrollListener != null) {
+ mActivityScrollListener.onListFragmentScrollStateChange(scrollState);
+ }
+ }
+
+ @Override
+ public void onScroll(
+ AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {}
+ });
+ if (mActivityOnTouchListener != null) {
+ listView.setOnTouchListener(mActivityOnTouchListener);
+ }
+
+ updatePosition(false /* animate */);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ FabUtil.addBottomPaddingToListViewForFab(getListView(), getResources());
+ }
+
+ @Override
+ public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
+ Animator animator = null;
+ if (nextAnim != 0) {
+ animator = AnimatorInflater.loadAnimator(getActivity(), nextAnim);
+ }
+ if (animator != null) {
+ final View view = getView();
+ final int oldLayerType = view.getLayerType();
+ animator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setLayerType(oldLayerType, null);
+ }
+ });
+ }
+ return animator;
+ }
+
+ @Override
+ protected void setSearchMode(boolean flag) {
+ super.setSearchMode(flag);
+ // This hides the "All contacts with phone numbers" header in the search fragment
+ final ContactEntryListAdapter adapter = getAdapter();
+ if (adapter != null) {
+ adapter.setHasHeader(0, false);
+ }
+ }
+
+ public void setAddToContactNumber(String addToContactNumber) {
+ mAddToContactNumber = addToContactNumber;
+ }
+
+ /**
+ * Return true if phone number is prohibited by a value -
+ * (R.string.config_prohibited_phone_number_regexp) in the config files. False otherwise.
+ */
+ public boolean checkForProhibitedPhoneNumber(String number) {
+ // Regular expression prohibiting manual phone call. Can be empty i.e. "no rule".
+ String prohibitedPhoneNumberRegexp =
+ getResources().getString(R.string.config_prohibited_phone_number_regexp);
+
+ // "persist.radio.otaspdial" is a temporary hack needed for one carrier's automated
+ // test equipment.
+ if (number != null
+ && !TextUtils.isEmpty(prohibitedPhoneNumberRegexp)
+ && number.matches(prohibitedPhoneNumberRegexp)) {
+ LogUtil.i(
+ "SearchFragment.checkForProhibitedPhoneNumber",
+ "the phone number is prohibited explicitly by a rule");
+ if (getActivity() != null) {
+ DialogFragment dialogFragment =
+ ErrorDialogFragment.newInstance(R.string.dialog_phone_call_prohibited_message);
+ dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected ContactEntryListAdapter createListAdapter() {
+ DialerPhoneNumberListAdapter adapter = new DialerPhoneNumberListAdapter(getActivity());
+ adapter.setDisplayPhotos(true);
+ adapter.setUseCallableUri(super.usesCallableUri());
+ adapter.setListener(this);
+ return adapter;
+ }
+
+ @Override
+ protected void onItemClick(int position, long id) {
+ final DialerPhoneNumberListAdapter adapter = (DialerPhoneNumberListAdapter) getAdapter();
+ final int shortcutType = adapter.getShortcutTypeFromPosition(position);
+ final OnPhoneNumberPickerActionListener listener;
+ final Intent intent;
+ final String number;
+
+ LogUtil.i("SearchFragment.onItemClick", "shortcutType: " + shortcutType);
+
+ switch (shortcutType) {
+ case DialerPhoneNumberListAdapter.SHORTCUT_INVALID:
+ super.onItemClick(position, id);
+ break;
+ case DialerPhoneNumberListAdapter.SHORTCUT_DIRECT_CALL:
+ number = adapter.getQueryString();
+ listener = getOnPhoneNumberPickerListener();
+ if (listener != null && !checkForProhibitedPhoneNumber(number)) {
+ CallSpecificAppData callSpecificAppData = new CallSpecificAppData();
+ callSpecificAppData.callInitiationType =
+ getCallInitiationType(false /* isRemoteDirectory */);
+ callSpecificAppData.positionOfSelectedSearchResult = position;
+ callSpecificAppData.charactersInSearchString =
+ getQueryString() == null ? 0 : getQueryString().length();
+ listener.onPickPhoneNumber(number, false /* isVideoCall */, callSpecificAppData);
+ }
+ break;
+ case DialerPhoneNumberListAdapter.SHORTCUT_CREATE_NEW_CONTACT:
+ number =
+ TextUtils.isEmpty(mAddToContactNumber)
+ ? adapter.getFormattedQueryString()
+ : mAddToContactNumber;
+ intent = IntentUtil.getNewContactIntent(number);
+ DialerUtils.startActivityWithErrorToast(getActivity(), intent);
+ break;
+ case DialerPhoneNumberListAdapter.SHORTCUT_ADD_TO_EXISTING_CONTACT:
+ number =
+ TextUtils.isEmpty(mAddToContactNumber)
+ ? adapter.getFormattedQueryString()
+ : mAddToContactNumber;
+ intent = IntentUtil.getAddToExistingContactIntent(number);
+ DialerUtils.startActivityWithErrorToast(
+ getActivity(), intent, R.string.add_contact_not_available);
+ break;
+ case DialerPhoneNumberListAdapter.SHORTCUT_SEND_SMS_MESSAGE:
+ number = adapter.getFormattedQueryString();
+ intent = IntentUtil.getSendSmsIntent(number);
+ DialerUtils.startActivityWithErrorToast(getActivity(), intent);
+ break;
+ case DialerPhoneNumberListAdapter.SHORTCUT_MAKE_VIDEO_CALL:
+ number =
+ TextUtils.isEmpty(mAddToContactNumber) ? adapter.getQueryString() : mAddToContactNumber;
+ listener = getOnPhoneNumberPickerListener();
+ if (listener != null && !checkForProhibitedPhoneNumber(number)) {
+ CallSpecificAppData callSpecificAppData = new CallSpecificAppData();
+ callSpecificAppData.callInitiationType =
+ getCallInitiationType(false /* isRemoteDirectory */);
+ callSpecificAppData.positionOfSelectedSearchResult = position;
+ callSpecificAppData.charactersInSearchString =
+ getQueryString() == null ? 0 : getQueryString().length();
+ listener.onPickPhoneNumber(number, true /* isVideoCall */, callSpecificAppData);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Updates the position and padding of the search fragment, depending on whether the dialpad is
+ * shown. This can be optionally animated.
+ */
+ public void updatePosition(boolean animate) {
+ if (mActivity == null) {
+ // Activity will be set in onStart, and this method will be called again
+ return;
+ }
+
+ // Use negative shadow height instead of 0 to account for the 9-patch's shadow.
+ int startTranslationValue =
+ mActivity.isDialpadShown() ? mActionBarHeight - mShadowHeight : -mShadowHeight;
+ int endTranslationValue = 0;
+ // Prevents ListView from being translated down after a rotation when the ActionBar is up.
+ if (animate || mActivity.isActionBarShowing()) {
+ endTranslationValue = mActivity.isDialpadShown() ? 0 : mActionBarHeight - mShadowHeight;
+ }
+ if (animate) {
+ // If the dialpad will be shown, then this animation involves sliding the list up.
+ final boolean slideUp = mActivity.isDialpadShown();
+
+ Interpolator interpolator = slideUp ? AnimUtils.EASE_IN : AnimUtils.EASE_OUT;
+ int duration = slideUp ? mShowDialpadDuration : mHideDialpadDuration;
+ getView().setTranslationY(startTranslationValue);
+ getView()
+ .animate()
+ .translationY(endTranslationValue)
+ .setInterpolator(interpolator)
+ .setDuration(duration)
+ .setListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ if (!slideUp) {
+ resizeListView();
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (slideUp) {
+ resizeListView();
+ }
+ }
+ });
+
+ } else {
+ getView().setTranslationY(endTranslationValue);
+ resizeListView();
+ }
+
+ // There is padding which should only be applied when the dialpad is not shown.
+ int paddingTop = mActivity.isDialpadShown() ? 0 : mPaddingTop;
+ final ListView listView = getListView();
+ listView.setPaddingRelative(
+ listView.getPaddingStart(),
+ paddingTop,
+ listView.getPaddingEnd(),
+ listView.getPaddingBottom());
+ }
+
+ public void resizeListView() {
+ if (mSpacer == null) {
+ return;
+ }
+ int spacerHeight = mActivity.isDialpadShown() ? mActivity.getDialpadHeight() : 0;
+ if (spacerHeight != mSpacer.getHeight()) {
+ final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpacer.getLayoutParams();
+ lp.height = spacerHeight;
+ mSpacer.setLayoutParams(lp);
+ }
+ }
+
+ @Override
+ protected void startLoading() {
+ if (getActivity() == null) {
+ return;
+ }
+
+ if (PermissionsUtil.hasContactsPermissions(getActivity())) {
+ super.startLoading();
+ } else if (TextUtils.isEmpty(getQueryString())) {
+ // Clear out any existing call shortcuts.
+ final DialerPhoneNumberListAdapter adapter = (DialerPhoneNumberListAdapter) getAdapter();
+ adapter.disableAllShortcuts();
+ } else {
+ // The contact list is not going to change (we have no results since permissions are
+ // denied), but the shortcuts might because of the different query, so update the
+ // list.
+ getAdapter().notifyDataSetChanged();
+ }
+
+ setupEmptyView();
+ }
+
+ public void setOnTouchListener(View.OnTouchListener onTouchListener) {
+ mActivityOnTouchListener = onTouchListener;
+ }
+
+ @Override
+ protected View inflateView(LayoutInflater inflater, ViewGroup container) {
+ final LinearLayout parent = (LinearLayout) super.inflateView(inflater, container);
+ final int orientation = getResources().getConfiguration().orientation;
+ if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ mSpacer = new Space(getActivity());
+ parent.addView(
+ mSpacer, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0));
+ }
+ return parent;
+ }
+
+ protected void setupEmptyView() {}
+
+ public interface HostInterface {
+
+ boolean isActionBarShowing();
+
+ boolean isDialpadShown();
+
+ int getDialpadHeight();
+
+ int getActionBarHideOffset();
+
+ int getActionBarHeight();
+ }
+}
diff --git a/java/com/android/dialer/app/list/SmartDialNumberListAdapter.java b/java/com/android/dialer/app/list/SmartDialNumberListAdapter.java
new file mode 100644
index 000000000..566a15d53
--- /dev/null
+++ b/java/com/android/dialer/app/list/SmartDialNumberListAdapter.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 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.dialer.app.list;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.support.annotation.NonNull;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.contacts.common.list.ContactListItemView;
+import com.android.dialer.app.dialpad.SmartDialCursorLoader;
+import com.android.dialer.smartdial.SmartDialMatchPosition;
+import com.android.dialer.smartdial.SmartDialNameMatcher;
+import com.android.dialer.smartdial.SmartDialPrefix;
+import com.android.dialer.util.CallUtil;
+import java.util.ArrayList;
+
+/** List adapter to display the SmartDial search results. */
+public class SmartDialNumberListAdapter extends DialerPhoneNumberListAdapter {
+
+ private static final String TAG = SmartDialNumberListAdapter.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ @NonNull private final SmartDialNameMatcher mNameMatcher;
+
+ public SmartDialNumberListAdapter(Context context) {
+ super(context);
+ mNameMatcher = new SmartDialNameMatcher("", SmartDialPrefix.getMap());
+ setShortcutEnabled(SmartDialNumberListAdapter.SHORTCUT_DIRECT_CALL, false);
+
+ if (DEBUG) {
+ Log.v(TAG, "Constructing List Adapter");
+ }
+ }
+
+ /** Sets query for the SmartDialCursorLoader. */
+ public void configureLoader(SmartDialCursorLoader loader) {
+ if (DEBUG) {
+ Log.v(TAG, "Configure Loader with query" + getQueryString());
+ }
+
+ if (getQueryString() == null) {
+ loader.configureQuery("");
+ mNameMatcher.setQuery("");
+ } else {
+ loader.configureQuery(getQueryString());
+ mNameMatcher.setQuery(PhoneNumberUtils.normalizeNumber(getQueryString()));
+ }
+ }
+
+ /**
+ * Sets highlight options for a List item in the SmartDial search results.
+ *
+ * @param view ContactListItemView where the result will be displayed.
+ * @param cursor Object containing information of the associated List item.
+ */
+ @Override
+ protected void setHighlight(ContactListItemView view, Cursor cursor) {
+ view.clearHighlightSequences();
+
+ if (mNameMatcher.matches(cursor.getString(PhoneQuery.DISPLAY_NAME))) {
+ final ArrayList<SmartDialMatchPosition> nameMatches = mNameMatcher.getMatchPositions();
+ for (SmartDialMatchPosition match : nameMatches) {
+ view.addNameHighlightSequence(match.start, match.end);
+ if (DEBUG) {
+ Log.v(
+ TAG,
+ cursor.getString(PhoneQuery.DISPLAY_NAME)
+ + " "
+ + mNameMatcher.getQuery()
+ + " "
+ + String.valueOf(match.start));
+ }
+ }
+ }
+
+ final SmartDialMatchPosition numberMatch =
+ mNameMatcher.matchesNumber(cursor.getString(PhoneQuery.PHONE_NUMBER));
+ if (numberMatch != null) {
+ view.addNumberHighlightSequence(numberMatch.start, numberMatch.end);
+ }
+ }
+
+ @Override
+ public void setQueryString(String queryString) {
+ final boolean showNumberShortcuts = !TextUtils.isEmpty(getFormattedQueryString());
+ boolean changed = false;
+ changed |= setShortcutEnabled(SHORTCUT_CREATE_NEW_CONTACT, showNumberShortcuts);
+ changed |= setShortcutEnabled(SHORTCUT_ADD_TO_EXISTING_CONTACT, showNumberShortcuts);
+ changed |= setShortcutEnabled(SHORTCUT_SEND_SMS_MESSAGE, showNumberShortcuts);
+ changed |=
+ setShortcutEnabled(
+ SHORTCUT_MAKE_VIDEO_CALL, showNumberShortcuts && CallUtil.isVideoEnabled(getContext()));
+ if (changed) {
+ notifyDataSetChanged();
+ }
+ super.setQueryString(queryString);
+ }
+
+ public void setShowEmptyListForNullQuery(boolean show) {
+ mNameMatcher.setShouldMatchEmptyQuery(!show);
+ }
+}
diff --git a/java/com/android/dialer/app/list/SmartDialSearchFragment.java b/java/com/android/dialer/app/list/SmartDialSearchFragment.java
new file mode 100644
index 000000000..c783d3ac3
--- /dev/null
+++ b/java/com/android/dialer/app/list/SmartDialSearchFragment.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2013 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.dialer.app.list;
+
+import static android.Manifest.permission.CALL_PHONE;
+
+import android.app.Activity;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.support.v13.app.FragmentCompat;
+import com.android.contacts.common.list.ContactEntryListAdapter;
+import com.android.dialer.app.R;
+import com.android.dialer.app.dialpad.SmartDialCursorLoader;
+import com.android.dialer.app.widget.EmptyContentView;
+import com.android.dialer.callintent.nano.CallInitiationType;
+import com.android.dialer.util.PermissionsUtil;
+
+/** Implements a fragment to load and display SmartDial search results. */
+public class SmartDialSearchFragment extends SearchFragment
+ implements EmptyContentView.OnEmptyViewActionButtonClickedListener,
+ FragmentCompat.OnRequestPermissionsResultCallback {
+
+ private static final String TAG = SmartDialSearchFragment.class.getSimpleName();
+
+ private static final int CALL_PHONE_PERMISSION_REQUEST_CODE = 1;
+
+ /** Creates a SmartDialListAdapter to display and operate on search results. */
+ @Override
+ protected ContactEntryListAdapter createListAdapter() {
+ SmartDialNumberListAdapter adapter = new SmartDialNumberListAdapter(getActivity());
+ adapter.setUseCallableUri(super.usesCallableUri());
+ adapter.setQuickContactEnabled(true);
+ adapter.setShowEmptyListForNullQuery(getShowEmptyListForNullQuery());
+ // Set adapter's query string to restore previous instance state.
+ adapter.setQueryString(getQueryString());
+ adapter.setListener(this);
+ return adapter;
+ }
+
+ /** Creates a SmartDialCursorLoader object to load query results. */
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ // Smart dialing does not support Directory Load, falls back to normal search instead.
+ if (id == getDirectoryLoaderId()) {
+ return super.onCreateLoader(id, args);
+ } else {
+ final SmartDialNumberListAdapter adapter = (SmartDialNumberListAdapter) getAdapter();
+ SmartDialCursorLoader loader = new SmartDialCursorLoader(super.getContext());
+ loader.setShowEmptyListForNullQuery(getShowEmptyListForNullQuery());
+ adapter.configureLoader(loader);
+ return loader;
+ }
+ }
+
+ @Override
+ protected void setupEmptyView() {
+ if (mEmptyView != null && getActivity() != null) {
+ if (!PermissionsUtil.hasPermission(getActivity(), CALL_PHONE)) {
+ mEmptyView.setImage(R.drawable.empty_contacts);
+ mEmptyView.setActionLabel(R.string.permission_single_turn_on);
+ mEmptyView.setDescription(R.string.permission_place_call);
+ mEmptyView.setActionClickedListener(this);
+ } else {
+ mEmptyView.setImage(EmptyContentView.NO_IMAGE);
+ mEmptyView.setActionLabel(EmptyContentView.NO_LABEL);
+ mEmptyView.setDescription(EmptyContentView.NO_LABEL);
+ }
+ }
+ }
+
+ @Override
+ public void onEmptyViewActionButtonClicked() {
+ final Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ FragmentCompat.requestPermissions(
+ this, new String[] {CALL_PHONE}, CALL_PHONE_PERMISSION_REQUEST_CODE);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, String[] permissions, int[] grantResults) {
+ if (requestCode == CALL_PHONE_PERMISSION_REQUEST_CODE) {
+ setupEmptyView();
+ }
+ }
+
+ @Override
+ protected int getCallInitiationType(boolean isRemoteDirectory) {
+ return CallInitiationType.Type.SMART_DIAL;
+ }
+
+ public boolean isShowingPermissionRequest() {
+ return mEmptyView != null && mEmptyView.isShowingContent();
+ }
+
+ @Override
+ public void setShowEmptyListForNullQuery(boolean show) {
+ if (getAdapter() != null) {
+ ((SmartDialNumberListAdapter) getAdapter()).setShowEmptyListForNullQuery(show);
+ }
+ super.setShowEmptyListForNullQuery(show);
+ }
+}
diff --git a/java/com/android/dialer/app/list/SpeedDialFragment.java b/java/com/android/dialer/app/list/SpeedDialFragment.java
new file mode 100644
index 000000000..8e0f89028
--- /dev/null
+++ b/java/com/android/dialer/app/list/SpeedDialFragment.java
@@ -0,0 +1,512 @@
+/*
+ * Copyright (C) 2013 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.dialer.app.list;
+
+import static android.Manifest.permission.READ_CONTACTS;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.content.CursorLoader;
+import android.content.Loader;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Trace;
+import android.support.annotation.Nullable;
+import android.support.v13.app.FragmentCompat;
+import android.support.v4.util.LongSparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+import android.view.animation.LayoutAnimationController;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.FrameLayout;
+import android.widget.FrameLayout.LayoutParams;
+import android.widget.ImageView;
+import android.widget.ListView;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactTileLoaderFactory;
+import com.android.contacts.common.list.ContactTileView;
+import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
+import com.android.dialer.app.R;
+import com.android.dialer.app.list.ListsFragment.ListsPage;
+import com.android.dialer.app.widget.EmptyContentView;
+import com.android.dialer.callintent.nano.CallInitiationType;
+import com.android.dialer.callintent.nano.CallSpecificAppData;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.util.PermissionsUtil;
+import com.android.dialer.util.ViewUtil;
+import java.util.ArrayList;
+
+/** This fragment displays the user's favorite/frequent contacts in a grid. */
+public class SpeedDialFragment extends Fragment
+ implements ListsPage,
+ OnItemClickListener,
+ PhoneFavoritesTileAdapter.OnDataSetChangedForAnimationListener,
+ EmptyContentView.OnEmptyViewActionButtonClickedListener,
+ FragmentCompat.OnRequestPermissionsResultCallback {
+
+ private static final int READ_CONTACTS_PERMISSION_REQUEST_CODE = 1;
+
+ /**
+ * By default, the animation code assumes that all items in a list view are of the same height
+ * when animating new list items into view (e.g. from the bottom of the screen into view). This
+ * can cause incorrect translation offsets when a item that is larger or smaller than other list
+ * item is removed from the list. This key is used to provide the actual height of the removed
+ * object so that the actual translation appears correct to the user.
+ */
+ private static final long KEY_REMOVED_ITEM_HEIGHT = Long.MAX_VALUE;
+
+ private static final String TAG = "SpeedDialFragment";
+ private static final boolean DEBUG = false;
+ /** Used with LoaderManager. */
+ private static final int LOADER_ID_CONTACT_TILE = 1;
+
+ private final LongSparseArray<Integer> mItemIdTopMap = new LongSparseArray<>();
+ private final LongSparseArray<Integer> mItemIdLeftMap = new LongSparseArray<>();
+ private final ContactTileView.Listener mContactTileAdapterListener =
+ new ContactTileAdapterListener();
+ private final LoaderManager.LoaderCallbacks<Cursor> mContactTileLoaderListener =
+ new ContactTileLoaderListener();
+ private final ScrollListener mScrollListener = new ScrollListener();
+ private int mAnimationDuration;
+ private OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener;
+ private OnListFragmentScrolledListener mActivityScrollListener;
+ private PhoneFavoritesTileAdapter mContactTileAdapter;
+ private View mParentView;
+ private PhoneFavoriteListView mListView;
+ private View mContactTileFrame;
+ /** Layout used when there are no favorites. */
+ private EmptyContentView mEmptyView;
+
+ @Override
+ public void onCreate(Bundle savedState) {
+ if (DEBUG) {
+ LogUtil.d("SpeedDialFragment.onCreate", null);
+ }
+ Trace.beginSection(TAG + " onCreate");
+ super.onCreate(savedState);
+
+ // Construct two base adapters which will become part of PhoneFavoriteMergedAdapter.
+ // We don't construct the resultant adapter at this moment since it requires LayoutInflater
+ // that will be available on onCreateView().
+ mContactTileAdapter =
+ new PhoneFavoritesTileAdapter(getActivity(), mContactTileAdapterListener, this);
+ mContactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(getActivity()));
+ mAnimationDuration = getResources().getInteger(R.integer.fade_duration);
+ Trace.endSection();
+ }
+
+ @Override
+ public void onResume() {
+ Trace.beginSection(TAG + " onResume");
+ super.onResume();
+ if (mContactTileAdapter != null) {
+ mContactTileAdapter.refreshContactsPreferences();
+ }
+ if (PermissionsUtil.hasContactsPermissions(getActivity())) {
+ if (getLoaderManager().getLoader(LOADER_ID_CONTACT_TILE) == null) {
+ getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null, mContactTileLoaderListener);
+
+ } else {
+ getLoaderManager().getLoader(LOADER_ID_CONTACT_TILE).forceLoad();
+ }
+
+ mEmptyView.setDescription(R.string.speed_dial_empty);
+ mEmptyView.setActionLabel(R.string.speed_dial_empty_add_favorite_action);
+ } else {
+ mEmptyView.setDescription(R.string.permission_no_speeddial);
+ mEmptyView.setActionLabel(R.string.permission_single_turn_on);
+ }
+ Trace.endSection();
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ Trace.beginSection(TAG + " onCreateView");
+ mParentView = inflater.inflate(R.layout.speed_dial_fragment, container, false);
+
+ mListView = (PhoneFavoriteListView) mParentView.findViewById(R.id.contact_tile_list);
+ mListView.setOnItemClickListener(this);
+ mListView.setVerticalScrollBarEnabled(false);
+ mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
+ mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
+ mListView.getDragDropController().addOnDragDropListener(mContactTileAdapter);
+
+ final ImageView dragShadowOverlay =
+ (ImageView) getActivity().findViewById(R.id.contact_tile_drag_shadow_overlay);
+ mListView.setDragShadowOverlay(dragShadowOverlay);
+
+ mEmptyView = (EmptyContentView) mParentView.findViewById(R.id.empty_list_view);
+ mEmptyView.setImage(R.drawable.empty_speed_dial);
+ mEmptyView.setActionClickedListener(this);
+
+ mContactTileFrame = mParentView.findViewById(R.id.contact_tile_frame);
+
+ final LayoutAnimationController controller =
+ new LayoutAnimationController(
+ AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in));
+ controller.setDelay(0);
+ mListView.setLayoutAnimation(controller);
+ mListView.setAdapter(mContactTileAdapter);
+
+ mListView.setOnScrollListener(mScrollListener);
+ mListView.setFastScrollEnabled(false);
+ mListView.setFastScrollAlwaysVisible(false);
+
+ //prevent content changes of the list from firing accessibility events.
+ mListView.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_NONE);
+ ContentChangedFilter.addToParent(mListView);
+
+ Trace.endSection();
+ return mParentView;
+ }
+
+ public boolean hasFrequents() {
+ if (mContactTileAdapter == null) {
+ return false;
+ }
+ return mContactTileAdapter.getNumFrequents() > 0;
+ }
+
+ /* package */ void setEmptyViewVisibility(final boolean visible) {
+ final int previousVisibility = mEmptyView.getVisibility();
+ final int emptyViewVisibility = visible ? View.VISIBLE : View.GONE;
+ final int listViewVisibility = visible ? View.GONE : View.VISIBLE;
+
+ if (previousVisibility != emptyViewVisibility) {
+ final FrameLayout.LayoutParams params = (LayoutParams) mContactTileFrame.getLayoutParams();
+ params.height = visible ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT;
+ mContactTileFrame.setLayoutParams(params);
+ mEmptyView.setVisibility(emptyViewVisibility);
+ mListView.setVisibility(listViewVisibility);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ final Activity activity = getActivity();
+
+ try {
+ mActivityScrollListener = (OnListFragmentScrolledListener) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(
+ activity.toString() + " must implement OnListFragmentScrolledListener");
+ }
+
+ try {
+ OnDragDropListener listener = (OnDragDropListener) activity;
+ mListView.getDragDropController().addOnDragDropListener(listener);
+ ((HostInterface) activity).setDragDropController(mListView.getDragDropController());
+ } catch (ClassCastException e) {
+ throw new ClassCastException(
+ activity.toString() + " must implement OnDragDropListener and HostInterface");
+ }
+
+ try {
+ mPhoneNumberPickerActionListener = (OnPhoneNumberPickerActionListener) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(
+ activity.toString() + " must implement PhoneFavoritesFragment.listener");
+ }
+
+ // Use initLoader() instead of restartLoader() to refraining unnecessary reload.
+ // This method call implicitly assures ContactTileLoaderListener's onLoadFinished() will
+ // be called, on which we'll check if "all" contacts should be reloaded again or not.
+ if (PermissionsUtil.hasContactsPermissions(activity)) {
+ getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null, mContactTileLoaderListener);
+ } else {
+ setEmptyViewVisibility(true);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This is only effective for elements provided by {@link #mContactTileAdapter}. {@link
+ * #mContactTileAdapter} has its own logic for click events.
+ */
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ final int contactTileAdapterCount = mContactTileAdapter.getCount();
+ if (position <= contactTileAdapterCount) {
+ LogUtil.e(
+ "SpeedDialFragment.onItemClick",
+ "event for unexpected position. The position "
+ + position
+ + " is before \"all\" section. Ignored.");
+ }
+ }
+
+ /**
+ * Cache the current view offsets into memory. Once a relayout of views in the ListView has
+ * happened due to a dataset change, the cached offsets are used to create animations that slide
+ * views from their previous positions to their new ones, to give the appearance that the views
+ * are sliding into their new positions.
+ */
+ private void saveOffsets(int removedItemHeight) {
+ final int firstVisiblePosition = mListView.getFirstVisiblePosition();
+ if (DEBUG) {
+ LogUtil.d("SpeedDialFragment.saveOffsets", "Child count : " + mListView.getChildCount());
+ }
+ for (int i = 0; i < mListView.getChildCount(); i++) {
+ final View child = mListView.getChildAt(i);
+ final int position = firstVisiblePosition + i;
+ // Since we are getting the position from mListView and then querying
+ // mContactTileAdapter, its very possible that things are out of sync
+ // and we might index out of bounds. Let's make sure that this doesn't happen.
+ if (!mContactTileAdapter.isIndexInBound(position)) {
+ continue;
+ }
+ final long itemId = mContactTileAdapter.getItemId(position);
+ if (DEBUG) {
+ LogUtil.d(
+ "SpeedDialFragment.saveOffsets",
+ "Saving itemId: " + itemId + " for listview child " + i + " Top: " + child.getTop());
+ }
+ mItemIdTopMap.put(itemId, child.getTop());
+ mItemIdLeftMap.put(itemId, child.getLeft());
+ }
+ mItemIdTopMap.put(KEY_REMOVED_ITEM_HEIGHT, removedItemHeight);
+ }
+
+ /*
+ * Performs animations for the gridView
+ */
+ private void animateGridView(final long... idsInPlace) {
+ if (mItemIdTopMap.size() == 0) {
+ // Don't do animations if the database is being queried for the first time and
+ // the previous item offsets have not been cached, or the user hasn't done anything
+ // (dragging, swiping etc) that requires an animation.
+ return;
+ }
+
+ ViewUtil.doOnPreDraw(
+ mListView,
+ true,
+ new Runnable() {
+ @Override
+ public void run() {
+
+ final int firstVisiblePosition = mListView.getFirstVisiblePosition();
+ final AnimatorSet animSet = new AnimatorSet();
+ final ArrayList<Animator> animators = new ArrayList<Animator>();
+ for (int i = 0; i < mListView.getChildCount(); i++) {
+ final View child = mListView.getChildAt(i);
+ int position = firstVisiblePosition + i;
+
+ // Since we are getting the position from mListView and then querying
+ // mContactTileAdapter, its very possible that things are out of sync
+ // and we might index out of bounds. Let's make sure that this doesn't happen.
+ if (!mContactTileAdapter.isIndexInBound(position)) {
+ continue;
+ }
+
+ final long itemId = mContactTileAdapter.getItemId(position);
+
+ if (containsId(idsInPlace, itemId)) {
+ animators.add(ObjectAnimator.ofFloat(child, "alpha", 0.0f, 1.0f));
+ break;
+ } else {
+ Integer startTop = mItemIdTopMap.get(itemId);
+ Integer startLeft = mItemIdLeftMap.get(itemId);
+ final int top = child.getTop();
+ final int left = child.getLeft();
+ int deltaX = 0;
+ int deltaY = 0;
+
+ if (startLeft != null) {
+ if (startLeft != left) {
+ deltaX = startLeft - left;
+ animators.add(ObjectAnimator.ofFloat(child, "translationX", deltaX, 0.0f));
+ }
+ }
+
+ if (startTop != null) {
+ if (startTop != top) {
+ deltaY = startTop - top;
+ animators.add(ObjectAnimator.ofFloat(child, "translationY", deltaY, 0.0f));
+ }
+ }
+
+ if (DEBUG) {
+ LogUtil.d(
+ "SpeedDialFragment.onPreDraw",
+ "Found itemId: "
+ + itemId
+ + " for listview child "
+ + i
+ + " Top: "
+ + top
+ + " Delta: "
+ + deltaY);
+ }
+ }
+ }
+
+ if (animators.size() > 0) {
+ animSet.setDuration(mAnimationDuration).playTogether(animators);
+ animSet.start();
+ }
+
+ mItemIdTopMap.clear();
+ mItemIdLeftMap.clear();
+ }
+ });
+ }
+
+ private boolean containsId(long[] ids, long target) {
+ // Linear search on array is fine because this is typically only 0-1 elements long
+ for (int i = 0; i < ids.length; i++) {
+ if (ids[i] == target) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onDataSetChangedForAnimation(long... idsInPlace) {
+ animateGridView(idsInPlace);
+ }
+
+ @Override
+ public void cacheOffsetsForDatasetChange() {
+ saveOffsets(0);
+ }
+
+ @Override
+ public void onEmptyViewActionButtonClicked() {
+ final Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ if (!PermissionsUtil.hasPermission(activity, READ_CONTACTS)) {
+ FragmentCompat.requestPermissions(
+ this, new String[] {READ_CONTACTS}, READ_CONTACTS_PERMISSION_REQUEST_CODE);
+ } else {
+ // Switch tabs
+ ((HostInterface) activity).showAllContactsTab();
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, String[] permissions, int[] grantResults) {
+ if (requestCode == READ_CONTACTS_PERMISSION_REQUEST_CODE) {
+ if (grantResults.length == 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
+ PermissionsUtil.notifyPermissionGranted(getActivity(), READ_CONTACTS);
+ }
+ }
+ }
+
+ @Override
+ public void onPageResume(@Nullable Activity activity) {
+ LogUtil.i("SpeedDialFragment.onPageResume", null);
+ }
+
+ @Override
+ public void onPagePause(@Nullable Activity activity) {
+ LogUtil.i("SpeedDialFragment.onPagePause", null);
+ }
+
+ public interface HostInterface {
+
+ void setDragDropController(DragDropController controller);
+
+ void showAllContactsTab();
+ }
+
+ private class ContactTileLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
+
+ @Override
+ public CursorLoader onCreateLoader(int id, Bundle args) {
+ if (DEBUG) {
+ LogUtil.d("ContactTileLoaderListener.onCreateLoader", null);
+ }
+ return ContactTileLoaderFactory.createStrequentPhoneOnlyLoader(getActivity());
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ if (DEBUG) {
+ LogUtil.d("ContactTileLoaderListener.onLoadFinished", null);
+ }
+ mContactTileAdapter.setContactCursor(data);
+ setEmptyViewVisibility(mContactTileAdapter.getCount() == 0);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ if (DEBUG) {
+ LogUtil.d("ContactTileLoaderListener.onLoaderReset", null);
+ }
+ }
+ }
+
+ private class ContactTileAdapterListener implements ContactTileView.Listener {
+
+ @Override
+ public void onContactSelected(Uri contactUri, Rect targetRect) {
+ if (mPhoneNumberPickerActionListener != null) {
+ CallSpecificAppData callSpecificAppData = new CallSpecificAppData();
+ callSpecificAppData.callInitiationType = CallInitiationType.Type.SPEED_DIAL;
+ mPhoneNumberPickerActionListener.onPickDataUri(
+ contactUri, false /* isVideoCall */, callSpecificAppData);
+ }
+ }
+
+ @Override
+ public void onCallNumberDirectly(String phoneNumber) {
+ if (mPhoneNumberPickerActionListener != null) {
+ CallSpecificAppData callSpecificAppData = new CallSpecificAppData();
+ callSpecificAppData.callInitiationType = CallInitiationType.Type.SPEED_DIAL;
+ mPhoneNumberPickerActionListener.onPickPhoneNumber(
+ phoneNumber, false /* isVideoCall */, callSpecificAppData);
+ }
+ }
+ }
+
+ private class ScrollListener implements ListView.OnScrollListener {
+
+ @Override
+ public void onScroll(
+ AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+ if (mActivityScrollListener != null) {
+ mActivityScrollListener.onListFragmentScroll(
+ firstVisibleItem, visibleItemCount, totalItemCount);
+ }
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ mActivityScrollListener.onListFragmentScrollStateChange(scrollState);
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/manifests/activities/AndroidManifest.xml b/java/com/android/dialer/app/manifests/activities/AndroidManifest.xml
new file mode 100644
index 000000000..247b34f4c
--- /dev/null
+++ b/java/com/android/dialer/app/manifests/activities/AndroidManifest.xml
@@ -0,0 +1,129 @@
+<!-- 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.
+-->
+
+<!-- This manifest file contains activites that are subclasses by
+ Google Dialer. TODO: Need to stop subclassing activities and move this
+ back into the main manifest file. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.dialer.app">
+
+ <application>
+
+ <activity
+ android:exported="false"
+ android:label="@string/dialer_settings_label"
+ android:name="com.android.dialer.app.settings.DialerSettingsActivity"
+ android:parentActivityName="com.android.dialer.app.DialtactsActivity"
+ android:theme="@style/SettingsStyle">
+ </activity>
+
+ <activity
+ android:label="@string/callDetailTitle"
+ android:name="com.android.dialer.app.CallDetailActivity"
+ android:parentActivityName="com.android.dialer.calllog.CallLogActivity"
+ android:theme="@style/CallDetailActivityTheme">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeType="vnd.android.cursor.item/calls"/>
+ </intent-filter>
+ </activity>
+
+ <!-- The entrance point for Phone UI.
+ stateAlwaysHidden is set to suppress keyboard show up on
+ dialpad screen. -->
+ <activity
+ android:clearTaskOnLaunch="true"
+ android:directBootAware="true"
+ android:label="@string/launcherActivityLabel"
+ android:launchMode="singleTask"
+ android:name="com.android.dialer.app.DialtactsActivity"
+ android:resizeableActivity="true"
+ android:theme="@style/DialtactsActivityTheme"
+ android:windowSoftInputMode="stateAlwaysHidden|adjustNothing">
+ <intent-filter>
+ <action android:name="android.intent.action.DIAL"/>
+
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.BROWSABLE"/>
+
+ <data android:mimeType="vnd.android.cursor.item/phone"/>
+ <data android:mimeType="vnd.android.cursor.item/person"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.DIAL"/>
+
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.BROWSABLE"/>
+
+ <data android:scheme="voicemail"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.DIAL"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.BROWSABLE"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW"/>
+ <action android:name="android.intent.action.DIAL"/>
+
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.BROWSABLE"/>
+
+ <data android:scheme="tel"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW"/>
+
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.BROWSABLE"/>
+
+ <data android:mimeType="vnd.android.cursor.dir/calls"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.CALL_BUTTON"/>
+
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.BROWSABLE"/>
+ </intent-filter>
+ <!-- This was never intended to be public, but is here for backward
+ compatibility. Use Intent.ACTION_DIAL instead. -->
+ <intent-filter>
+ <action android:name="com.android.phone.action.TOUCH_DIALER"/>
+
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.TAB"/>
+ </intent-filter>
+ <intent-filter android:label="@string/callHistoryIconLabel">
+ <action android:name="com.android.phone.action.RECENT_CALLS"/>
+
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.TAB"/>
+ </intent-filter>
+
+ <meta-data
+ android:name="com.android.keyguard.layout"
+ android:resource="@layout/keyguard_preview"/>
+ </activity>
+
+ </application>
+
+</manifest>
diff --git a/res/color/settings_text_color_primary.xml b/java/com/android/dialer/app/res/color/settings_text_color_primary.xml
index 862d8a2c3..ba259088a 100644
--- a/res/color/settings_text_color_primary.xml
+++ b/java/com/android/dialer/app/res/color/settings_text_color_primary.xml
@@ -18,6 +18,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:color="@color/setting_disabled_color" />
- <item android:color="@color/setting_primary_color" />
+ <item android:color="@color/setting_disabled_color" android:state_enabled="false"/>
+ <item android:color="@color/setting_primary_color"/>
</selector>
diff --git a/res/color/settings_text_color_secondary.xml b/java/com/android/dialer/app/res/color/settings_text_color_secondary.xml
index 0b00e4688..2f7899272 100644
--- a/res/color/settings_text_color_secondary.xml
+++ b/java/com/android/dialer/app/res/color/settings_text_color_secondary.xml
@@ -18,6 +18,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:color="@color/setting_disabled_color" />
- <item android:color="@color/setting_secondary_color" />
+ <item android:color="@color/setting_disabled_color" android:state_enabled="false"/>
+ <item android:color="@color/setting_secondary_color"/>
</selector>
diff --git a/res/drawable-hdpi/empty_call_log.png b/java/com/android/dialer/app/res/drawable-hdpi/empty_call_log.png
index d6f6daaab..d6f6daaab 100644
--- a/res/drawable-hdpi/empty_call_log.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/empty_call_log.png
Binary files differ
diff --git a/res/drawable-hdpi/empty_contacts.png b/java/com/android/dialer/app/res/drawable-hdpi/empty_contacts.png
index d3c0378f5..d3c0378f5 100644
--- a/res/drawable-hdpi/empty_contacts.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/empty_contacts.png
Binary files differ
diff --git a/res/drawable-hdpi/empty_speed_dial.png b/java/com/android/dialer/app/res/drawable-hdpi/empty_speed_dial.png
index 3e9232fc9..3e9232fc9 100644
--- a/res/drawable-hdpi/empty_speed_dial.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/empty_speed_dial.png
Binary files differ
diff --git a/res/drawable-hdpi/fab_ic_dial.png b/java/com/android/dialer/app/res/drawable-hdpi/fab_ic_dial.png
index 3cad4c660..3cad4c660 100644
--- a/res/drawable-hdpi/fab_ic_dial.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/fab_ic_dial.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_archive_white_24dp.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_archive_white_24dp.png
index bb72e890f..bb72e890f 100644
--- a/res/drawable-hdpi/ic_archive_white_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_archive_white_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_call_arrow.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_call_arrow.png
index 14a33e39f..14a33e39f 100644
--- a/res/drawable-hdpi/ic_call_arrow.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_call_arrow.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_content_copy_24dp.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_content_copy_24dp.png
index 70eb07378..70eb07378 100644
--- a/res/drawable-hdpi/ic_content_copy_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_content_copy_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_delete_24dp.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_delete_24dp.png
index 9fb43b066..9fb43b066 100644
--- a/res/drawable-hdpi/ic_delete_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_delete_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_dialer_fork_add_call.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_dialer_fork_add_call.png
index 4e0d5649e..4e0d5649e 100755..100644
--- a/res/drawable-hdpi/ic_dialer_fork_add_call.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_dialer_fork_add_call.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_dialer_fork_current_call.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_dialer_fork_current_call.png
index 2cf41d598..2cf41d598 100755..100644
--- a/res/drawable-hdpi/ic_dialer_fork_current_call.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_dialer_fork_current_call.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_dialer_fork_tt_keypad.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_dialer_fork_tt_keypad.png
index 043685fd9..043685fd9 100755..100644
--- a/res/drawable-hdpi/ic_dialer_fork_tt_keypad.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_dialer_fork_tt_keypad.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_grade_24dp.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_grade_24dp.png
index 86eecdd4a..86eecdd4a 100644
--- a/res/drawable-hdpi/ic_grade_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_grade_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_handle.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_handle.png
index 34310aa49..34310aa49 100644
--- a/res/drawable-hdpi/ic_handle.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_handle.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_history_lt.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_menu_history_lt.png
index a36323ca9..a36323ca9 100644
--- a/res/drawable-hdpi/ic_menu_history_lt.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_menu_history_lt.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_mic_grey600.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_mic_grey600.png
index 4b67cf71a..4b67cf71a 100644
--- a/res/drawable-hdpi/ic_mic_grey600.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_mic_grey600.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_more_vert_24dp.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_more_vert_24dp.png
index 67f07e473..67f07e473 100644
--- a/res/drawable-hdpi/ic_more_vert_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_more_vert_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_not_interested_googblue_24dp.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_not_interested_googblue_24dp.png
index 26a26f911..26a26f911 100644
--- a/res/drawable-hdpi/ic_not_interested_googblue_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_not_interested_googblue_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-hdpi/ic_not_spam.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_not_spam.png
new file mode 100644
index 000000000..bf413f912
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_not_spam.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-hdpi/ic_pause_24dp.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_pause_24dp.png
new file mode 100644
index 000000000..4d2ea05c4
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_pause_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-hdpi/ic_people_24dp.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_people_24dp.png
new file mode 100644
index 000000000..ff698afc0
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_people_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_phone_24dp.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_phone_24dp.png
index b27dfba06..b27dfba06 100644
--- a/res/drawable-hdpi/ic_phone_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_phone_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_play_arrow_24dp.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_play_arrow_24dp.png
index 57c9fa546..57c9fa546 100644
--- a/res/drawable-hdpi/ic_play_arrow_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_play_arrow_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_remove.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_remove.png
index 1ee6adf8d..1ee6adf8d 100644
--- a/res/drawable-hdpi/ic_remove.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_remove.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_results_phone.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_results_phone.png
index 3a1a7a790..3a1a7a790 100644
--- a/res/drawable-hdpi/ic_results_phone.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_results_phone.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_schedule_24dp.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_schedule_24dp.png
index f3581d104..f3581d104 100644
--- a/res/drawable-hdpi/ic_schedule_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_schedule_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_share_white_24dp.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_share_white_24dp.png
index b09a6926d..b09a6926d 100644
--- a/res/drawable-hdpi/ic_share_white_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_star.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_star.png
index 62e1f8a6d..62e1f8a6d 100644
--- a/res/drawable-hdpi/ic_star.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_star.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_unblock.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_unblock.png
index 03643b20d..03643b20d 100644
--- a/res/drawable-hdpi/ic_unblock.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_unblock.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_vm_sound_off_dis.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_vm_sound_off_dis.png
index 47e32492c..47e32492c 100644
--- a/res/drawable-hdpi/ic_vm_sound_off_dis.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_vm_sound_off_dis.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_vm_sound_off_dk.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_vm_sound_off_dk.png
index 2bfe0c0cf..2bfe0c0cf 100644
--- a/res/drawable-hdpi/ic_vm_sound_off_dk.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_vm_sound_off_dk.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_vm_sound_on_dis.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_vm_sound_on_dis.png
index 90b5238f3..90b5238f3 100644
--- a/res/drawable-hdpi/ic_vm_sound_on_dis.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_vm_sound_on_dis.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_vm_sound_on_dk.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_vm_sound_on_dk.png
index 7556637fc..7556637fc 100644
--- a/res/drawable-hdpi/ic_vm_sound_on_dk.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_vm_sound_on_dk.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-hdpi/ic_voicemail_24dp.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_voicemail_24dp.png
new file mode 100644
index 000000000..03a62e15f
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_voicemail_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_volume_down_24dp.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_volume_down_24dp.png
index e22e92c85..e22e92c85 100644
--- a/res/drawable-hdpi/ic_volume_down_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_volume_down_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-hdpi/ic_volume_up_24dp.png b/java/com/android/dialer/app/res/drawable-hdpi/ic_volume_up_24dp.png
new file mode 100644
index 000000000..57d787163
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable-hdpi/ic_volume_up_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/search_shadow.9.png b/java/com/android/dialer/app/res/drawable-hdpi/search_shadow.9.png
index 3dc1c17f6..3dc1c17f6 100644
--- a/res/drawable-hdpi/search_shadow.9.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/search_shadow.9.png
Binary files differ
diff --git a/res/drawable-hdpi/shadow_contact_photo.png b/java/com/android/dialer/app/res/drawable-hdpi/shadow_contact_photo.png
index 44b06f261..44b06f261 100644
--- a/res/drawable-hdpi/shadow_contact_photo.png
+++ b/java/com/android/dialer/app/res/drawable-hdpi/shadow_contact_photo.png
Binary files differ
diff --git a/res/drawable-mdpi/empty_call_log.png b/java/com/android/dialer/app/res/drawable-mdpi/empty_call_log.png
index 3cd59b35b..3cd59b35b 100644
--- a/res/drawable-mdpi/empty_call_log.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/empty_call_log.png
Binary files differ
diff --git a/res/drawable-mdpi/empty_contacts.png b/java/com/android/dialer/app/res/drawable-mdpi/empty_contacts.png
index 2ce7eae37..2ce7eae37 100644
--- a/res/drawable-mdpi/empty_contacts.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/empty_contacts.png
Binary files differ
diff --git a/res/drawable-mdpi/empty_speed_dial.png b/java/com/android/dialer/app/res/drawable-mdpi/empty_speed_dial.png
index 98152e0d3..98152e0d3 100644
--- a/res/drawable-mdpi/empty_speed_dial.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/empty_speed_dial.png
Binary files differ
diff --git a/res/drawable-mdpi/fab_ic_dial.png b/java/com/android/dialer/app/res/drawable-mdpi/fab_ic_dial.png
index 4c854e1a1..4c854e1a1 100644
--- a/res/drawable-mdpi/fab_ic_dial.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/fab_ic_dial.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_archive_white_24dp.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_archive_white_24dp.png
index f6aa3f966..f6aa3f966 100644
--- a/res/drawable-mdpi/ic_archive_white_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_archive_white_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_call_arrow.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_call_arrow.png
index 169cf2934..169cf2934 100644
--- a/res/drawable-mdpi/ic_call_arrow.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_call_arrow.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_content_copy_24dp.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_content_copy_24dp.png
index 80c069557..80c069557 100644
--- a/res/drawable-mdpi/ic_content_copy_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_content_copy_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_delete_24dp.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_delete_24dp.png
index c903fd1dd..c903fd1dd 100644
--- a/res/drawable-mdpi/ic_delete_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_delete_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_dialer_fork_add_call.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_dialer_fork_add_call.png
index 56ac2a33a..56ac2a33a 100644
--- a/res/drawable-mdpi/ic_dialer_fork_add_call.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_dialer_fork_add_call.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_dialer_fork_current_call.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_dialer_fork_current_call.png
index 16a44a078..16a44a078 100644
--- a/res/drawable-mdpi/ic_dialer_fork_current_call.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_dialer_fork_current_call.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_dialer_fork_tt_keypad.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_dialer_fork_tt_keypad.png
index 66df69eac..66df69eac 100644
--- a/res/drawable-mdpi/ic_dialer_fork_tt_keypad.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_dialer_fork_tt_keypad.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_grade_24dp.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_grade_24dp.png
index d2cbe4c92..d2cbe4c92 100644
--- a/res/drawable-mdpi/ic_grade_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_grade_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_handle.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_handle.png
index 81a67ba6f..81a67ba6f 100644
--- a/res/drawable-mdpi/ic_handle.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_handle.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_history_lt.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_menu_history_lt.png
index 3597a5e82..3597a5e82 100644
--- a/res/drawable-mdpi/ic_menu_history_lt.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_menu_history_lt.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_mic_grey600.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_mic_grey600.png
index 2310c734a..2310c734a 100644
--- a/res/drawable-mdpi/ic_mic_grey600.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_mic_grey600.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_more_vert_24dp.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_more_vert_24dp.png
index 017e45ede..017e45ede 100644
--- a/res/drawable-mdpi/ic_more_vert_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_more_vert_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_not_interested_googblue_24dp.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_not_interested_googblue_24dp.png
index d7d5c588f..d7d5c588f 100644
--- a/res/drawable-mdpi/ic_not_interested_googblue_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_not_interested_googblue_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-mdpi/ic_not_spam.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_not_spam.png
new file mode 100644
index 000000000..b1f1c7efe
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_not_spam.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pause_24dp.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_pause_24dp.png
index 2272d478c..2272d478c 100644
--- a/res/drawable-mdpi/ic_pause_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_pause_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_people_24dp.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_people_24dp.png
index 270e4de2e..270e4de2e 100644
--- a/res/drawable-mdpi/ic_people_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_people_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_phone_24dp.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_phone_24dp.png
index c1766b854..c1766b854 100644
--- a/res/drawable-mdpi/ic_phone_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_phone_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_play_arrow_24dp.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_play_arrow_24dp.png
index c61e948bb..c61e948bb 100644
--- a/res/drawable-mdpi/ic_play_arrow_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_play_arrow_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_remove.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_remove.png
index 2c134ea10..2c134ea10 100644
--- a/res/drawable-mdpi/ic_remove.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_remove.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_results_phone.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_results_phone.png
index 74ccf14b8..74ccf14b8 100644
--- a/res/drawable-mdpi/ic_results_phone.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_results_phone.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_schedule_24dp.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_schedule_24dp.png
index 501ee842e..501ee842e 100644
--- a/res/drawable-mdpi/ic_schedule_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_schedule_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_share_white_24dp.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_share_white_24dp.png
index e944fd70c..e944fd70c 100644
--- a/res/drawable-mdpi/ic_share_white_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_star.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_star.png
index d2af0ba20..d2af0ba20 100644
--- a/res/drawable-mdpi/ic_star.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_star.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_unblock.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_unblock.png
index d80fb2f5c..d80fb2f5c 100644
--- a/res/drawable-mdpi/ic_unblock.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_unblock.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_vm_sound_off_dis.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_vm_sound_off_dis.png
index 4c671ecb4..4c671ecb4 100644
--- a/res/drawable-mdpi/ic_vm_sound_off_dis.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_vm_sound_off_dis.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_vm_sound_off_dk.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_vm_sound_off_dk.png
index 41044b456..41044b456 100644
--- a/res/drawable-mdpi/ic_vm_sound_off_dk.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_vm_sound_off_dk.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_vm_sound_on_dis.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_vm_sound_on_dis.png
index c6040c09e..c6040c09e 100644
--- a/res/drawable-mdpi/ic_vm_sound_on_dis.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_vm_sound_on_dis.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_vm_sound_on_dk.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_vm_sound_on_dk.png
index ac6a69c14..ac6a69c14 100644
--- a/res/drawable-mdpi/ic_vm_sound_on_dk.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_vm_sound_on_dk.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_voicemail_24dp.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_voicemail_24dp.png
index e5aa7db05..e5aa7db05 100644
--- a/res/drawable-mdpi/ic_voicemail_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_voicemail_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_volume_down_24dp.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_volume_down_24dp.png
index 10992ed70..10992ed70 100644
--- a/res/drawable-mdpi/ic_volume_down_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_volume_down_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_volume_up_24dp.png b/java/com/android/dialer/app/res/drawable-mdpi/ic_volume_up_24dp.png
index 7cfd4c7b8..7cfd4c7b8 100644
--- a/res/drawable-mdpi/ic_volume_up_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/ic_volume_up_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/search_shadow.9.png b/java/com/android/dialer/app/res/drawable-mdpi/search_shadow.9.png
index 0c33905cd..0c33905cd 100644
--- a/res/drawable-mdpi/search_shadow.9.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/search_shadow.9.png
Binary files differ
diff --git a/res/drawable-mdpi/shadow_contact_photo.png b/java/com/android/dialer/app/res/drawable-mdpi/shadow_contact_photo.png
index 8665d8303..8665d8303 100644
--- a/res/drawable-mdpi/shadow_contact_photo.png
+++ b/java/com/android/dialer/app/res/drawable-mdpi/shadow_contact_photo.png
Binary files differ
diff --git a/res/drawable-xhdpi/empty_call_log.png b/java/com/android/dialer/app/res/drawable-xhdpi/empty_call_log.png
index 14ec04ba1..14ec04ba1 100644
--- a/res/drawable-xhdpi/empty_call_log.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/empty_call_log.png
Binary files differ
diff --git a/res/drawable-xhdpi/empty_contacts.png b/java/com/android/dialer/app/res/drawable-xhdpi/empty_contacts.png
index 65b1de333..65b1de333 100644
--- a/res/drawable-xhdpi/empty_contacts.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/empty_contacts.png
Binary files differ
diff --git a/res/drawable-xhdpi/empty_speed_dial.png b/java/com/android/dialer/app/res/drawable-xhdpi/empty_speed_dial.png
index a3a76751b..a3a76751b 100644
--- a/res/drawable-xhdpi/empty_speed_dial.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/empty_speed_dial.png
Binary files differ
diff --git a/res/drawable-xhdpi/fab_ic_dial.png b/java/com/android/dialer/app/res/drawable-xhdpi/fab_ic_dial.png
index 398a03cee..398a03cee 100644
--- a/res/drawable-xhdpi/fab_ic_dial.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/fab_ic_dial.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_archive_white_24dp.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_archive_white_24dp.png
index 3513bd9fe..3513bd9fe 100644
--- a/res/drawable-xhdpi/ic_archive_white_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_archive_white_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_call_arrow.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_call_arrow.png
index 6f1366018..6f1366018 100644
--- a/res/drawable-xhdpi/ic_call_arrow.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_call_arrow.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_content_copy_24dp.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_content_copy_24dp.png
index 537fd4e8b..537fd4e8b 100644
--- a/res/drawable-xhdpi/ic_content_copy_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_content_copy_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_delete_24dp.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_delete_24dp.png
index be1ee4d07..be1ee4d07 100644
--- a/res/drawable-xhdpi/ic_delete_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_delete_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_dialer_fork_add_call.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_dialer_fork_add_call.png
index aff140fcd..aff140fcd 100644
--- a/res/drawable-xhdpi/ic_dialer_fork_add_call.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_dialer_fork_add_call.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_dialer_fork_current_call.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_dialer_fork_current_call.png
index 8975727e0..8975727e0 100644
--- a/res/drawable-xhdpi/ic_dialer_fork_current_call.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_dialer_fork_current_call.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_dialer_fork_tt_keypad.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_dialer_fork_tt_keypad.png
index 4d48ea9ea..4d48ea9ea 100644
--- a/res/drawable-xhdpi/ic_dialer_fork_tt_keypad.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_dialer_fork_tt_keypad.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_grade_24dp.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_grade_24dp.png
index d65f39d7c..d65f39d7c 100644
--- a/res/drawable-xhdpi/ic_grade_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_grade_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_handle.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_handle.png
index 0ad839286..0ad839286 100644
--- a/res/drawable-xhdpi/ic_handle.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_handle.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_history_lt.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_menu_history_lt.png
index 6b411cbc3..6b411cbc3 100644
--- a/res/drawable-xhdpi/ic_menu_history_lt.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_menu_history_lt.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_mic_grey600.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_mic_grey600.png
index a9a83b329..a9a83b329 100644
--- a/res/drawable-xhdpi/ic_mic_grey600.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_mic_grey600.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_more_vert_24dp.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_more_vert_24dp.png
index efab8a74f..efab8a74f 100644
--- a/res/drawable-xhdpi/ic_more_vert_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_more_vert_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_not_interested_googblue_24dp.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_not_interested_googblue_24dp.png
index 3e6ec071b..3e6ec071b 100644
--- a/res/drawable-xhdpi/ic_not_interested_googblue_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_not_interested_googblue_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-xhdpi/ic_not_spam.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_not_spam.png
new file mode 100644
index 000000000..138f27cdb
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_not_spam.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pause_24dp.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_pause_24dp.png
index f49aed757..f49aed757 100644
--- a/res/drawable-xhdpi/ic_pause_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_pause_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_people_24dp.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_people_24dp.png
index 323981ccf..323981ccf 100644
--- a/res/drawable-xhdpi/ic_people_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_people_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_phone_24dp.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_phone_24dp.png
index 83167f4cd..83167f4cd 100644
--- a/res/drawable-xhdpi/ic_phone_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_phone_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_play_arrow_24dp.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_play_arrow_24dp.png
index a3c80e73d..a3c80e73d 100644
--- a/res/drawable-xhdpi/ic_play_arrow_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_play_arrow_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_remove.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_remove.png
index be81592ef..be81592ef 100644
--- a/res/drawable-xhdpi/ic_remove.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_remove.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_results_phone.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_results_phone.png
index 0e24fa45c..0e24fa45c 100644
--- a/res/drawable-xhdpi/ic_results_phone.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_results_phone.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_schedule_24dp.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_schedule_24dp.png
index 2e27936a4..2e27936a4 100644
--- a/res/drawable-xhdpi/ic_schedule_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_schedule_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_share_white_24dp.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_share_white_24dp.png
index 22a8783e7..22a8783e7 100644
--- a/res/drawable-xhdpi/ic_share_white_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_star.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_star.png
index 2071f42f2..2071f42f2 100644
--- a/res/drawable-xhdpi/ic_star.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_star.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_unblock.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_unblock.png
index f7dfa21ac..f7dfa21ac 100644
--- a/res/drawable-xhdpi/ic_unblock.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_unblock.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_vm_sound_off_dis.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_vm_sound_off_dis.png
index 36b5e2030..36b5e2030 100644
--- a/res/drawable-xhdpi/ic_vm_sound_off_dis.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_vm_sound_off_dis.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_vm_sound_off_dk.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_vm_sound_off_dk.png
index 99d7fd51a..99d7fd51a 100644
--- a/res/drawable-xhdpi/ic_vm_sound_off_dk.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_vm_sound_off_dk.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_vm_sound_on_dis.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_vm_sound_on_dis.png
index 468023d8a..468023d8a 100644
--- a/res/drawable-xhdpi/ic_vm_sound_on_dis.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_vm_sound_on_dis.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_vm_sound_on_dk.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_vm_sound_on_dk.png
index 970329493..970329493 100644
--- a/res/drawable-xhdpi/ic_vm_sound_on_dk.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_vm_sound_on_dk.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_voicemail_24dp.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_voicemail_24dp.png
index 59126d706..59126d706 100644
--- a/res/drawable-xhdpi/ic_voicemail_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_voicemail_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_volume_down_24dp.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_volume_down_24dp.png
index 2621bc15d..2621bc15d 100644
--- a/res/drawable-xhdpi/ic_volume_down_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_volume_down_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_volume_up_24dp.png b/java/com/android/dialer/app/res/drawable-xhdpi/ic_volume_up_24dp.png
index 2ed00343b..2ed00343b 100644
--- a/res/drawable-xhdpi/ic_volume_up_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/ic_volume_up_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/search_shadow.9.png b/java/com/android/dialer/app/res/drawable-xhdpi/search_shadow.9.png
index 5667ab368..5667ab368 100644
--- a/res/drawable-xhdpi/search_shadow.9.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/search_shadow.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/shadow_contact_photo.png b/java/com/android/dialer/app/res/drawable-xhdpi/shadow_contact_photo.png
index 8359a50e9..8359a50e9 100644
--- a/res/drawable-xhdpi/shadow_contact_photo.png
+++ b/java/com/android/dialer/app/res/drawable-xhdpi/shadow_contact_photo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/empty_call_log.png b/java/com/android/dialer/app/res/drawable-xxhdpi/empty_call_log.png
index 501d7f1e2..501d7f1e2 100644
--- a/res/drawable-xxhdpi/empty_call_log.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/empty_call_log.png
Binary files differ
diff --git a/res/drawable-xxhdpi/empty_contacts.png b/java/com/android/dialer/app/res/drawable-xxhdpi/empty_contacts.png
index 407d78c9c..407d78c9c 100644
--- a/res/drawable-xxhdpi/empty_contacts.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/empty_contacts.png
Binary files differ
diff --git a/res/drawable-xxhdpi/empty_speed_dial.png b/java/com/android/dialer/app/res/drawable-xxhdpi/empty_speed_dial.png
index fb2ea5f15..fb2ea5f15 100644
--- a/res/drawable-xxhdpi/empty_speed_dial.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/empty_speed_dial.png
Binary files differ
diff --git a/res/drawable-xxhdpi/fab_ic_dial.png b/java/com/android/dialer/app/res/drawable-xxhdpi/fab_ic_dial.png
index 5f1cd45fb..5f1cd45fb 100644
--- a/res/drawable-xxhdpi/fab_ic_dial.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/fab_ic_dial.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_archive_white_24dp.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_archive_white_24dp.png
index 00e04e42b..00e04e42b 100644
--- a/res/drawable-xxhdpi/ic_archive_white_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_archive_white_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_call_arrow.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_call_arrow.png
index 0364ee015..0364ee015 100644
--- a/res/drawable-xxhdpi/ic_call_arrow.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_call_arrow.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_content_copy_24dp.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_content_copy_24dp.png
index 9dff893e7..9dff893e7 100644
--- a/res/drawable-xxhdpi/ic_content_copy_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_content_copy_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_delete_24dp.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_delete_24dp.png
index eb637920d..eb637920d 100644
--- a/res/drawable-xxhdpi/ic_delete_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_delete_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_dialer_fork_add_call.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_dialer_fork_add_call.png
index 1657da4e2..1657da4e2 100644
--- a/res/drawable-xxhdpi/ic_dialer_fork_add_call.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_dialer_fork_add_call.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_dialer_fork_current_call.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_dialer_fork_current_call.png
index f25cce695..f25cce695 100644
--- a/res/drawable-xxhdpi/ic_dialer_fork_current_call.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_dialer_fork_current_call.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_dialer_fork_tt_keypad.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_dialer_fork_tt_keypad.png
index 7ac4d8b58..7ac4d8b58 100644
--- a/res/drawable-xxhdpi/ic_dialer_fork_tt_keypad.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_dialer_fork_tt_keypad.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_grade_24dp.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_grade_24dp.png
index aa5879215..aa5879215 100644
--- a/res/drawable-xxhdpi/ic_grade_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_grade_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_handle.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_handle.png
index d07a1d057..d07a1d057 100644
--- a/res/drawable-xxhdpi/ic_handle.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_handle.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_history_lt.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_menu_history_lt.png
index 779bc0620..779bc0620 100644
--- a/res/drawable-xxhdpi/ic_menu_history_lt.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_menu_history_lt.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_mic_grey600.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_mic_grey600.png
index 07128dd82..07128dd82 100644
--- a/res/drawable-xxhdpi/ic_mic_grey600.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_mic_grey600.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_more_vert_24dp.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_more_vert_24dp.png
index d32281307..d32281307 100644
--- a/res/drawable-xxhdpi/ic_more_vert_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_more_vert_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_not_interested_googblue_24dp.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_not_interested_googblue_24dp.png
index 7c256b5d7..7c256b5d7 100644
--- a/res/drawable-xxhdpi/ic_not_interested_googblue_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_not_interested_googblue_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-xxhdpi/ic_not_spam.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_not_spam.png
new file mode 100644
index 000000000..f699959cb
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_not_spam.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-xxhdpi/ic_pause_24dp.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_pause_24dp.png
new file mode 100644
index 000000000..7192ad487
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_pause_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-xxhdpi/ic_people_24dp.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_people_24dp.png
new file mode 100644
index 000000000..6c68435fb
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_people_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_phone_24dp.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_phone_24dp.png
index 8fff728bb..8fff728bb 100644
--- a/res/drawable-xxhdpi/ic_phone_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_phone_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_play_arrow_24dp.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_play_arrow_24dp.png
index 547ef30aa..547ef30aa 100644
--- a/res/drawable-xxhdpi/ic_play_arrow_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_play_arrow_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_remove.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_remove.png
index 2722f23aa..2722f23aa 100644
--- a/res/drawable-xxhdpi/ic_remove.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_remove.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_results_phone.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_results_phone.png
index 9594619cb..9594619cb 100644
--- a/res/drawable-xxhdpi/ic_results_phone.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_results_phone.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_schedule_24dp.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_schedule_24dp.png
index bfc72736a..bfc72736a 100644
--- a/res/drawable-xxhdpi/ic_schedule_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_schedule_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_share_white_24dp.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_share_white_24dp.png
index a35b3cd14..a35b3cd14 100644
--- a/res/drawable-xxhdpi/ic_share_white_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_star.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_star.png
index f3c830435..f3c830435 100644
--- a/res/drawable-xxhdpi/ic_star.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_star.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_unblock.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_unblock.png
index 828a4879f..828a4879f 100644
--- a/res/drawable-xxhdpi/ic_unblock.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_unblock.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_vm_sound_off_dis.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_vm_sound_off_dis.png
index bab4a4311..bab4a4311 100644
--- a/res/drawable-xxhdpi/ic_vm_sound_off_dis.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_vm_sound_off_dis.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_vm_sound_off_dk.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_vm_sound_off_dk.png
index 1c13101a8..1c13101a8 100644
--- a/res/drawable-xxhdpi/ic_vm_sound_off_dk.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_vm_sound_off_dk.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_vm_sound_on_dis.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_vm_sound_on_dis.png
index ed3a17329..ed3a17329 100644
--- a/res/drawable-xxhdpi/ic_vm_sound_on_dis.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_vm_sound_on_dis.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_vm_sound_on_dk.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_vm_sound_on_dk.png
index c04b8d117..c04b8d117 100644
--- a/res/drawable-xxhdpi/ic_vm_sound_on_dk.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_vm_sound_on_dk.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-xxhdpi/ic_voicemail_24dp.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_voicemail_24dp.png
new file mode 100644
index 000000000..28b8e936a
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_voicemail_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_volume_down_24dp.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_volume_down_24dp.png
index 5eb8b671f..5eb8b671f 100644
--- a/res/drawable-xxhdpi/ic_volume_down_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_volume_down_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-xxhdpi/ic_volume_up_24dp.png b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_volume_up_24dp.png
new file mode 100644
index 000000000..2e751a40f
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/ic_volume_up_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/search_shadow.9.png b/java/com/android/dialer/app/res/drawable-xxhdpi/search_shadow.9.png
index ff55620d0..ff55620d0 100644
--- a/res/drawable-xxhdpi/search_shadow.9.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/search_shadow.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/shadow_contact_photo.png b/java/com/android/dialer/app/res/drawable-xxhdpi/shadow_contact_photo.png
index bfeb0ff53..bfeb0ff53 100644
--- a/res/drawable-xxhdpi/shadow_contact_photo.png
+++ b/java/com/android/dialer/app/res/drawable-xxhdpi/shadow_contact_photo.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/empty_call_log.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/empty_call_log.png
index fbac1e40f..fbac1e40f 100644
--- a/res/drawable-xxxhdpi/empty_call_log.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/empty_call_log.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/empty_contacts.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/empty_contacts.png
index 5893965e9..5893965e9 100644
--- a/res/drawable-xxxhdpi/empty_contacts.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/empty_contacts.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/fab_ic_dial.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/fab_ic_dial.png
index 9361aa864..9361aa864 100644
--- a/res/drawable-xxxhdpi/fab_ic_dial.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/fab_ic_dial.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_archive_white_24dp.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_archive_white_24dp.png
index 34cd3fd80..34cd3fd80 100644
--- a/res/drawable-xxxhdpi/ic_archive_white_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_archive_white_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_call_arrow.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_call_arrow.png
index 8243c2536..8243c2536 100644
--- a/res/drawable-xxxhdpi/ic_call_arrow.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_call_arrow.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_content_copy_24dp.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_content_copy_24dp.png
index 4ddee9ef0..4ddee9ef0 100644
--- a/res/drawable-xxxhdpi/ic_content_copy_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_content_copy_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_delete_24dp.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_delete_24dp.png
index 2f250f64a..2f250f64a 100644
--- a/res/drawable-xxxhdpi/ic_delete_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_delete_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_grade_24dp.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_grade_24dp.png
index 7f38d0963..7f38d0963 100644
--- a/res/drawable-xxxhdpi/ic_grade_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_grade_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_handle.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_handle.png
index 72641c7ab..72641c7ab 100644
--- a/res/drawable-xxxhdpi/ic_handle.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_handle.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_mic_grey600.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_mic_grey600.png
index b7403ff22..b7403ff22 100644
--- a/res/drawable-xxxhdpi/ic_mic_grey600.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_mic_grey600.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_more_vert_24dp.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_more_vert_24dp.png
index 2f2cb3d00..2f2cb3d00 100644
--- a/res/drawable-xxxhdpi/ic_more_vert_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_more_vert_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_not_interested_googblue_24dp.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_not_interested_googblue_24dp.png
index 6591ed485..6591ed485 100644
--- a/res/drawable-xxxhdpi/ic_not_interested_googblue_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_not_interested_googblue_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_not_spam.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_not_spam.png
new file mode 100644
index 000000000..2a18de24e
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_not_spam.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_pause_24dp.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_pause_24dp.png
index 660ac6585..660ac6585 100644
--- a/res/drawable-xxxhdpi/ic_pause_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_pause_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_people_24dp.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_people_24dp.png
index 5676f7041..5676f7041 100644
--- a/res/drawable-xxxhdpi/ic_people_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_people_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_phone_24dp.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_phone_24dp.png
index 30d141db5..30d141db5 100644
--- a/res/drawable-xxxhdpi/ic_phone_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_phone_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_play_arrow_24dp.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_play_arrow_24dp.png
index be5c062b5..be5c062b5 100644
--- a/res/drawable-xxxhdpi/ic_play_arrow_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_play_arrow_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_results_phone.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_results_phone.png
index 395652cdf..395652cdf 100644
--- a/res/drawable-xxxhdpi/ic_results_phone.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_results_phone.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_schedule_24dp.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_schedule_24dp.png
index b94f4dfa1..b94f4dfa1 100644
--- a/res/drawable-xxxhdpi/ic_schedule_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_schedule_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_share_white_24dp.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_share_white_24dp.png
index e351c7beb..e351c7beb 100644
--- a/res/drawable-xxxhdpi/ic_share_white_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_unblock.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_unblock.png
index 99a1842a2..99a1842a2 100644
--- a/res/drawable-xxxhdpi/ic_unblock.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_unblock.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_voicemail_24dp.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_voicemail_24dp.png
index 820ff5066..820ff5066 100644
--- a/res/drawable-xxxhdpi/ic_voicemail_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_voicemail_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_volume_down_24dp.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_volume_down_24dp.png
index 4ab55abbd..4ab55abbd 100644
--- a/res/drawable-xxxhdpi/ic_volume_down_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_volume_down_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_volume_up_24dp.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_volume_up_24dp.png
index 82972b4e5..82972b4e5 100644
--- a/res/drawable-xxxhdpi/ic_volume_up_24dp.png
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_volume_up_24dp.png
Binary files differ
diff --git a/res/drawable/background_dial_holo_dark.xml b/java/com/android/dialer/app/res/drawable/background_dial_holo_dark.xml
index e06507f27..35afbe025 100644
--- a/res/drawable/background_dial_holo_dark.xml
+++ b/java/com/android/dialer/app/res/drawable/background_dial_holo_dark.xml
@@ -15,8 +15,8 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <gradient
- android:startColor="#ff020709"
- android:endColor="#ff0a242d"
- android:angle="270" />
+ <gradient
+ android:angle="270"
+ android:endColor="#ff0a242d"
+ android:startColor="#ff020709"/>
</shape>
diff --git a/res/drawable/floating_action_button.xml b/java/com/android/dialer/app/res/drawable/floating_action_button.xml
index d550190a8..0b9af5229 100644
--- a/res/drawable/floating_action_button.xml
+++ b/java/com/android/dialer/app/res/drawable/floating_action_button.xml
@@ -16,10 +16,10 @@
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/floating_action_button_touch_tint">
- <item android:id="@android:id/mask">
- <shape android:shape="oval">
- <solid android:color="@android:color/white" />
- </shape>
- </item>
+ android:color="@color/floating_action_button_touch_tint">
+ <item android:id="@android:id/mask">
+ <shape android:shape="oval">
+ <solid android:color="@android:color/white"/>
+ </shape>
+ </item>
</ripple> \ No newline at end of file
diff --git a/res/drawable/ic_call_detail_content_copy.xml b/java/com/android/dialer/app/res/drawable/ic_call_detail_content_copy.xml
index dd604dff7..87e0fbc6f 100644
--- a/res/drawable/ic_call_detail_content_copy.xml
+++ b/java/com/android/dialer/app/res/drawable/ic_call_detail_content_copy.xml
@@ -16,5 +16,5 @@
-->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_content_copy_24dp"
- android:tint="@color/call_detail_footer_icon_tint" />
+ android:src="@drawable/ic_content_copy_24dp"
+ android:tint="@color/call_detail_footer_icon_tint"/>
diff --git a/res/drawable/ic_call_detail_edit.xml b/java/com/android/dialer/app/res/drawable/ic_call_detail_edit.xml
index e5ad3e59e..e6d5c4776 100644
--- a/res/drawable/ic_call_detail_edit.xml
+++ b/java/com/android/dialer/app/res/drawable/ic_call_detail_edit.xml
@@ -16,5 +16,5 @@
-->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_create_24dp"
- android:tint="@color/call_detail_footer_icon_tint" />
+ android:src="@drawable/ic_create_24dp"
+ android:tint="@color/call_detail_footer_icon_tint"/>
diff --git a/res/drawable/ic_call_detail_report.xml b/java/com/android/dialer/app/res/drawable/ic_call_detail_report.xml
index 201ac4cb6..e90e83e8b 100644
--- a/res/drawable/ic_call_detail_report.xml
+++ b/java/com/android/dialer/app/res/drawable/ic_call_detail_report.xml
@@ -16,5 +16,5 @@
-->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_report_24dp"
- android:tint="@color/call_detail_footer_icon_tint" />
+ android:src="@drawable/ic_report_24dp"
+ android:tint="@color/call_detail_footer_icon_tint"/>
diff --git a/res/drawable/ic_call_detail_unblock.xml b/java/com/android/dialer/app/res/drawable/ic_call_detail_unblock.xml
index ba5378b10..3b614cf0d 100644
--- a/res/drawable/ic_call_detail_unblock.xml
+++ b/java/com/android/dialer/app/res/drawable/ic_call_detail_unblock.xml
@@ -16,5 +16,5 @@
-->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_unblock"
- android:tint="@color/call_detail_footer_icon_tint" />
+ android:src="@drawable/ic_unblock"
+ android:tint="@color/call_detail_footer_icon_tint"/>
diff --git a/java/com/android/dialer/app/res/drawable/ic_pause.xml b/java/com/android/dialer/app/res/drawable/ic_pause.xml
new file mode 100644
index 000000000..5bea58192
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable/ic_pause.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_enabled="false">
+ <bitmap
+ android:src="@drawable/ic_pause_24dp"
+ android:tint="@color/voicemail_icon_disabled_tint"/>
+ </item>
+
+ <item>
+ <bitmap
+ android:src="@drawable/ic_pause_24dp"
+ android:tint="@color/voicemail_playpause_icon_tint"/>
+ </item>
+
+</selector>
diff --git a/java/com/android/dialer/app/res/drawable/ic_play_arrow.xml b/java/com/android/dialer/app/res/drawable/ic_play_arrow.xml
new file mode 100644
index 000000000..d7d935016
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable/ic_play_arrow.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true">
+
+ <item android:state_enabled="false">
+ <bitmap
+ android:src="@drawable/ic_play_arrow_24dp"
+ android:tint="@color/voicemail_icon_disabled_tint"/>
+ </item>
+
+ <item>
+ <bitmap
+ android:src="@drawable/ic_play_arrow_24dp"
+ android:tint="@color/voicemail_playpause_icon_tint"/>
+ </item>
+
+</selector>
diff --git a/res/drawable/ic_search_phone.xml b/java/com/android/dialer/app/res/drawable/ic_search_phone.xml
index ac9053273..5d449ee56 100644
--- a/res/drawable/ic_search_phone.xml
+++ b/java/com/android/dialer/app/res/drawable/ic_search_phone.xml
@@ -16,5 +16,5 @@
-->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_results_phone"
- android:tint="@color/search_shortcut_icon_color" />
+ android:src="@drawable/ic_results_phone"
+ android:tint="@color/search_shortcut_icon_color"/>
diff --git a/res/drawable/ic_speakerphone_off.xml b/java/com/android/dialer/app/res/drawable/ic_speakerphone_off.xml
index 3dfedeafb..f07d0a889 100644
--- a/res/drawable/ic_speakerphone_off.xml
+++ b/java/com/android/dialer/app/res/drawable/ic_speakerphone_off.xml
@@ -15,6 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:drawable="@drawable/ic_vm_sound_off_dis" />
- <item android:drawable="@drawable/ic_vm_sound_off_dk" />
+ <item android:drawable="@drawable/ic_vm_sound_off_dis" android:state_enabled="false"/>
+ <item android:drawable="@drawable/ic_vm_sound_off_dk"/>
</selector>
diff --git a/res/drawable/ic_speakerphone_on.xml b/java/com/android/dialer/app/res/drawable/ic_speakerphone_on.xml
index ae7bb32df..456a0483e 100644
--- a/res/drawable/ic_speakerphone_on.xml
+++ b/java/com/android/dialer/app/res/drawable/ic_speakerphone_on.xml
@@ -15,6 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:drawable="@drawable/ic_vm_sound_on_dis" />
- <item android:drawable="@drawable/ic_vm_sound_on_dk" />
+ <item android:drawable="@drawable/ic_vm_sound_on_dis" android:state_enabled="false"/>
+ <item android:drawable="@drawable/ic_vm_sound_on_dk"/>
</selector>
diff --git a/res/drawable/ic_voicemail_seek_handle.xml b/java/com/android/dialer/app/res/drawable/ic_voicemail_seek_handle.xml
index d3fc95a65..84cda0310 100644
--- a/res/drawable/ic_voicemail_seek_handle.xml
+++ b/java/com/android/dialer/app/res/drawable/ic_voicemail_seek_handle.xml
@@ -15,6 +15,6 @@
~ limitations under the License
-->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_handle"
- android:tint="@color/actionbar_background_color" >
+ android:src="@drawable/ic_handle"
+ android:tint="@color/actionbar_background_color">
</bitmap> \ No newline at end of file
diff --git a/res/drawable/ic_voicemail_seek_handle_disabled.xml b/java/com/android/dialer/app/res/drawable/ic_voicemail_seek_handle_disabled.xml
index 2be52ade6..5e974c45a 100644
--- a/res/drawable/ic_voicemail_seek_handle_disabled.xml
+++ b/java/com/android/dialer/app/res/drawable/ic_voicemail_seek_handle_disabled.xml
@@ -15,6 +15,6 @@
~ limitations under the License
-->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_handle"
- android:tint="@color/voicemail_icon_disabled_tint" >
+ android:src="@drawable/ic_handle"
+ android:tint="@color/voicemail_icon_disabled_tint">
</bitmap> \ No newline at end of file
diff --git a/res/drawable/oval_ripple.xml b/java/com/android/dialer/app/res/drawable/oval_ripple.xml
index 0022d2671..abb002588 100644
--- a/res/drawable/oval_ripple.xml
+++ b/java/com/android/dialer/app/res/drawable/oval_ripple.xml
@@ -17,10 +17,10 @@
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?android:attr/colorControlHighlight" >
- <item>
- <shape android:shape="oval">
- <solid android:color="#fff" />
- </shape>
- </item>
+ android:color="?android:attr/colorControlHighlight">
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="#fff"/>
+ </shape>
+ </item>
</ripple>
diff --git a/res/drawable/overflow_menu.xml b/java/com/android/dialer/app/res/drawable/overflow_menu.xml
index 0467d6bf1..81be5dcd5 100644
--- a/res/drawable/overflow_menu.xml
+++ b/java/com/android/dialer/app/res/drawable/overflow_menu.xml
@@ -15,6 +15,6 @@
~ limitations under the License
-->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_overflow_menu"
- android:autoMirrored="true"
- android:tint="@color/actionbar_icon_color" />
+ android:autoMirrored="true"
+ android:src="@drawable/ic_overflow_menu"
+ android:tint="@color/actionbar_icon_color"/>
diff --git a/res/drawable/rounded_corner.xml b/java/com/android/dialer/app/res/drawable/rounded_corner.xml
index fb8f4f56d..97b58b6b1 100644
--- a/res/drawable/rounded_corner.xml
+++ b/java/com/android/dialer/app/res/drawable/rounded_corner.xml
@@ -16,7 +16,7 @@
~ limitations under the License
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle" >
- <solid android:color="@color/searchbox_background_color" />
- <corners android:radius="2dp" />
+ android:shape="rectangle">
+ <solid android:color="@color/searchbox_background_color"/>
+ <corners android:radius="2dp"/>
</shape>
diff --git a/java/com/android/dialer/app/res/drawable/seekbar_drawable.xml b/java/com/android/dialer/app/res/drawable/seekbar_drawable.xml
new file mode 100644
index 000000000..e47a6406c
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable/seekbar_drawable.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="true">
+ <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background">
+ <shape android:shape="line">
+ <stroke
+ android:color="@color/voicemail_playback_seek_bar_yet_to_play"
+ android:width="2dip"
+ />
+ </shape>
+ </item>
+ <!-- I am not defining a secondary progress colour - we don't use it. -->
+ <item android:id="@android:id/progress">
+ <clip>
+ <shape android:shape="line">
+ <stroke
+ android:color="@color/voicemail_playback_seek_bar_already_played"
+ android:width="2dip"
+ />
+ </shape>
+ </clip>
+ </item>
+ </layer-list>
+ </item>
+ <item>
+ <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background">
+ <shape android:shape="line">
+ <stroke
+ android:color="@color/voicemail_playback_seek_bar_yet_to_play"
+ android:width="2dip"
+ />
+ </shape>
+ </item>
+ <!-- I am not defining a secondary progress colour - we don't use it. -->
+ <item android:id="@android:id/progress">
+ <clip>
+ <shape android:shape="line">
+ <stroke
+ android:color="@color/voicemail_playback_seek_bar_yet_to_play"
+ android:width="2dip"
+ />
+ </shape>
+ </clip>
+ </item>
+ </layer-list>
+ </item>
+</selector>
diff --git a/res/drawable/selectable_primary_flat_button.xml b/java/com/android/dialer/app/res/drawable/selectable_primary_flat_button.xml
index c6eb7a26a..47d1152db 100644
--- a/res/drawable/selectable_primary_flat_button.xml
+++ b/java/com/android/dialer/app/res/drawable/selectable_primary_flat_button.xml
@@ -18,10 +18,14 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false">
- <shape><solid android:color="@color/material_grey_300" /></shape>
- </item>
+ <item android:state_enabled="false">
+ <shape>
+ <solid android:color="@color/material_grey_300"/>
+ </shape>
+ </item>
<item>
- <shape><solid android:color="@color/dialer_theme_color" /></shape>
+ <shape>
+ <solid android:color="@color/dialer_theme_color"/>
+ </shape>
</item>
</selector> \ No newline at end of file
diff --git a/res/drawable/shadow_fade_left.xml b/java/com/android/dialer/app/res/drawable/shadow_fade_left.xml
index cb87cf536..6271a8f86 100644
--- a/res/drawable/shadow_fade_left.xml
+++ b/java/com/android/dialer/app/res/drawable/shadow_fade_left.xml
@@ -15,10 +15,10 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle" >
- <gradient
- android:type="linear"
- android:startColor="@null"
- android:endColor="#1a000000"
- android:angle="0"/>
+ android:shape="rectangle">
+ <gradient
+ android:angle="0"
+ android:endColor="#1a000000"
+ android:startColor="@null"
+ android:type="linear"/>
</shape>
diff --git a/res/drawable/shadow_fade_up.xml b/java/com/android/dialer/app/res/drawable/shadow_fade_up.xml
index e961c860a..86d37a9bc 100644
--- a/res/drawable/shadow_fade_up.xml
+++ b/java/com/android/dialer/app/res/drawable/shadow_fade_up.xml
@@ -15,10 +15,10 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle" >
- <gradient
- android:type="linear"
- android:startColor="#1a000000"
- android:endColor="@null"
- android:angle="90"/>
+ android:shape="rectangle">
+ <gradient
+ android:angle="90"
+ android:endColor="@null"
+ android:startColor="#1a000000"
+ android:type="linear"/>
</shape> \ No newline at end of file
diff --git a/java/com/android/dialer/app/res/layout-land/dialpad_fragment.xml b/java/com/android/dialer/app/res/layout-land/dialpad_fragment.xml
new file mode 100644
index 000000000..8d8236a43
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout-land/dialpad_fragment.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.dialer.app.dialpad.DialpadFragment$DialpadSlidingRelativeLayout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <!-- spacer view -->
+ <View
+ android:id="@+id/spacer"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="4"
+ android:background="#00000000"/>
+
+ <!-- Dialpad shadow -->
+ <View
+ android:layout_width="@dimen/shadow_length"
+ android:layout_height="match_parent"
+ android:background="@drawable/shadow_fade_left"/>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="6">
+
+ <include
+ layout="@layout/dialpad_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <!-- "Dialpad chooser" UI, shown only when the user brings up the
+ Dialer while a call is already in progress.
+ When this UI is visible, the other Dialer elements
+ (the textfield/button and the dialpad) are hidden. -->
+
+ <ListView
+ android:id="@+id/dialpadChooser"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/background_dialer_light"
+ android:visibility="gone"/>
+
+ <!-- Margin bottom and alignParentBottom don't work well together, so use a Space instead. -->
+ <Space
+ android:id="@+id/dialpad_floating_action_button_margin_bottom"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/floating_action_button_margin_bottom"
+ android:layout_alignParentBottom="true"/>
+
+ <FrameLayout
+ android:id="@+id/dialpad_floating_action_button_container"
+ android:layout_width="@dimen/floating_action_button_width"
+ android:layout_height="@dimen/floating_action_button_height"
+ android:layout_above="@id/dialpad_floating_action_button_margin_bottom"
+ android:layout_centerHorizontal="true"
+ android:background="@drawable/fab_green">
+
+ <ImageButton
+ android:id="@+id/dialpad_floating_action_button"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/floating_action_button"
+ android:contentDescription="@string/description_dial_button"
+ android:src="@drawable/fab_ic_call"/>
+
+ </FrameLayout>
+
+ </RelativeLayout>
+
+ </LinearLayout>
+</view>
diff --git a/java/com/android/dialer/app/res/layout-land/empty_content_view_dialpad_search.xml b/java/com/android/dialer/app/res/layout-land/empty_content_view_dialpad_search.xml
new file mode 100644
index 000000000..5f8068067
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout-land/empty_content_view_dialpad_search.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="4"
+ android:orientation="vertical">
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+ <ImageView
+ android:id="@+id/emptyListViewImage"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:importantForAccessibility="no"/>
+
+ <TextView
+ android:id="@+id/emptyListViewMessage"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:gravity="center_horizontal|top"
+ android:textColor="@color/empty_list_text_color"
+ android:textSize="@dimen/empty_list_message_text_size"/>
+
+ <TextView
+ android:id="@+id/emptyListViewAction"
+ style="@style/TextActionStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:clickable="true"
+ android:gravity="center_horizontal"/>
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+ </LinearLayout>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="6"/>
+
+</merge>
diff --git a/java/com/android/dialer/app/res/layout/account_filter_header_for_phone_favorite.xml b/java/com/android/dialer/app/res/layout/account_filter_header_for_phone_favorite.xml
new file mode 100644
index 000000000..c6e186257
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/account_filter_header_for_phone_favorite.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Layout showing the type of account filter for phone favorite screen
+ (or, new phone "all" screen).
+ This is very similar to account_filter_header.xml but different in its
+ top padding. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/account_filter_header_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/contact_browser_list_header_left_margin"
+ android:layout_marginEnd="@dimen/contact_browser_list_header_right_margin"
+ android:paddingTop="8dip"
+ android:background="?android:attr/selectableItemBackground"
+ android:orientation="vertical"
+ android:visibility="gone">
+ <TextView
+ android:id="@+id/account_filter_header"
+ style="@style/ContactListSeparatorTextViewStyle"
+ android:paddingStart="@dimen/contact_browser_list_item_text_indent"/>
+ <TextView
+ android:id="@+id/contact_list_all_empty"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/contact_phone_list_empty_description_padding"
+ android:paddingBottom="@dimen/contact_phone_list_empty_description_padding"
+ android:paddingStart="8dip"
+ android:text="@string/listFoundAllContactsZero"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="@dimen/contact_phone_list_empty_description_size"
+ android:visibility="gone"/>
+</LinearLayout>
diff --git a/res/layout/all_contacts_activity.xml b/java/com/android/dialer/app/res/layout/all_contacts_activity.xml
index 50cba1eca..72f0a147f 100644
--- a/res/layout/all_contacts_activity.xml
+++ b/java/com/android/dialer/app/res/layout/all_contacts_activity.xml
@@ -15,11 +15,12 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/all_contacts_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <fragment
+ android:id="@+id/all_contacts_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:id="@+id/all_contacts_frame">
- <fragment android:name="com.android.dialer.list.AllContactsFragment"
- android:id="@+id/all_contacts_fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:name="com.android.dialer.app.list.AllContactsFragment"/>
</FrameLayout>
diff --git a/java/com/android/dialer/app/res/layout/all_contacts_fragment.xml b/java/com/android/dialer/app/res/layout/all_contacts_fragment.xml
new file mode 100644
index 000000000..f59847825
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/all_contacts_fragment.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pinned_header_list_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <!-- Shown only when an Account filter is set.
+ - paddingTop should be here to show "shade" effect correctly. -->
+ <!-- TODO: Remove the filter header. -->
+ <include layout="@layout/account_filter_header"/>
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1">
+ <view
+ android:id="@android:id/list"
+ style="@style/DialtactsTheme"
+ class="com.android.contacts.common.list.PinnedHeaderListView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="?attr/contact_browser_list_padding_left"
+ android:layout_marginEnd="?attr/contact_browser_list_padding_right"
+ android:paddingTop="18dp"
+ android:fadingEdge="none"
+ android:fastScrollEnabled="true"
+ android:nestedScrollingEnabled="true"/>
+
+ <com.android.dialer.app.widget.EmptyContentView
+ android:id="@+id/empty_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:visibility="gone"/>
+
+ </FrameLayout>
+</LinearLayout>
diff --git a/java/com/android/dialer/app/res/layout/blocked_number_footer.xml b/java/com/android/dialer/app/res/layout/blocked_number_footer.xml
new file mode 100644
index 000000000..9e05cfbf4
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/blocked_number_footer.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusable="false"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="@dimen/blocked_number_container_padding"
+ android:background="@android:color/white"
+ android:focusable="true"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/blocked_number_footer_textview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/block_number_footer_message_vvm"
+ android:textColor="@color/blocked_number_secondary_text_color"
+ android:textSize="@dimen/blocked_number_settings_description_text_size"/>
+ </LinearLayout>
+</LinearLayout>
diff --git a/java/com/android/dialer/app/res/layout/blocked_number_fragment.xml b/java/com/android/dialer/app/res/layout/blocked_number_fragment.xml
new file mode 100644
index 000000000..745b913cc
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/blocked_number_fragment.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/blocked_number_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/blocked_number_background"
+ android:orientation="vertical">
+
+ <ListView
+ android:id="@id/android:list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:divider="@null"
+ android:headerDividersEnabled="false"/>
+
+</LinearLayout>
diff --git a/java/com/android/dialer/app/res/layout/blocked_number_header.xml b/java/com/android/dialer/app/res/layout/blocked_number_header.xml
new file mode 100644
index 000000000..e34510b73
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/blocked_number_header.xml
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:card_view="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusable="false"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/blocked_numbers_disabled_for_emergency"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="27dp"
+ android:paddingBottom="29dp"
+ android:paddingStart="@dimen/blocked_number_container_padding"
+ android:paddingEnd="44dp"
+ android:background="@color/blocked_number_disabled_emergency_background_color"
+ android:focusable="true"
+ android:orientation="vertical"
+ android:visibility="gone">
+
+ <TextView
+ style="@style/BlockedNumbersDescriptionTextStyle"
+ android:textStyle="bold"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/blocked_numbers_disabled_emergency_header_label"/>
+
+ <TextView
+ style="@style/BlockedNumbersDescriptionTextStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/blocked_numbers_disabled_emergency_desc"/>
+
+ </LinearLayout>
+
+ <android.support.v7.widget.CardView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ card_view:cardCornerRadius="0dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/white"
+ android:focusable="true"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/blocked_number_text_view"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:paddingStart="@dimen/blocked_number_container_padding"
+ android:gravity="center_vertical"
+ android:text="@string/block_list"
+ android:textColor="@color/blocked_number_header_color"/>
+
+ <RelativeLayout
+ android:id="@+id/import_settings"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ tools:visibility="visible">
+
+ <TextView
+ android:id="@+id/import_description"
+ style="@style/BlockedNumbersDescriptionTextStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="11dp"
+ android:paddingBottom="27dp"
+ android:paddingStart="@dimen/blocked_number_container_padding"
+ android:paddingEnd="@dimen/blocked_number_container_padding"
+ android:text="@string/blocked_call_settings_import_description"
+ android:textColor="@color/secondary_text_color"
+ android:textSize="@dimen/blocked_number_settings_description_text_size"/>
+
+ <Button
+ android:id="@+id/import_button"
+ style="@style/DialerFlatButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/blocked_number_container_padding"
+ android:layout_alignParentEnd="true"
+ android:layout_below="@id/import_description"
+ android:text="@string/blocked_call_settings_import_button"/>
+
+ <Button
+ android:id="@+id/view_numbers_button"
+ style="@style/DialerFlatButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
+ android:layout_below="@id/import_description"
+ android:layout_toStartOf="@id/import_button"
+ android:text="@string/blocked_call_settings_view_numbers_button"/>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginTop="8dp"
+ android:layout_below="@id/import_button"
+ android:background="@color/divider_line_color"/>
+
+ </RelativeLayout>
+
+ <LinearLayout
+ android:id="@+id/migrate_promo"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone"
+ tools:visibility="visible">
+
+ <TextView
+ android:id="@+id/migrate_promo_header"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:textStyle="bold"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:paddingStart="@dimen/blocked_number_container_padding"
+ android:paddingEnd="@dimen/blocked_number_container_padding"
+ android:gravity="center_vertical"
+ android:text="@string/migrate_blocked_numbers_dialog_title"
+ android:textColor="@color/blocked_number_header_color"/>
+
+ <TextView
+ android:id="@+id/migrate_promo_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/blocked_number_container_padding"
+ android:layout_marginStart="@dimen/blocked_number_container_padding"
+ android:layout_marginEnd="@dimen/blocked_number_container_padding"
+ android:text="@string/migrate_blocked_numbers_dialog_message"
+ android:textColor="@color/secondary_text_color"/>
+
+ <Button
+ android:id="@+id/migrate_promo_allow_button"
+ style="@style/DialerPrimaryFlatButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/blocked_number_container_padding"
+ android:layout_marginStart="@dimen/blocked_number_container_padding"
+ android:layout_marginEnd="@dimen/blocked_number_container_padding"
+ android:layout_gravity="end"
+ android:text="@string/migrate_blocked_numbers_dialog_allow_button"/>
+
+ <View
+ style="@style/FullWidthDivider"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/add_number_linear_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/blocked_number_add_top_margin"
+ android:paddingBottom="@dimen/blocked_number_add_bottom_margin"
+ android:paddingStart="@dimen/blocked_number_horizontal_margin"
+ android:background="?android:attr/selectableItemBackground"
+ android:baselineAligned="false"
+ android:clickable="true"
+ android:contentDescription="@string/addBlockedNumber"
+ android:focusable="true"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/add_number_icon"
+ android:layout_width="@dimen/contact_photo_size"
+ android:layout_height="@dimen/contact_photo_size"
+ android:importantForAccessibility="no"/>
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginStart="@dimen/blocked_number_horizontal_margin"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/add_number_textview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:includeFontPadding="false"
+ android:text="@string/addBlockedNumber"
+ android:textColor="@color/blocked_number_primary_text_color"
+ android:textSize="@dimen/blocked_number_primary_text_size"/>
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <View
+ android:id="@+id/blocked_number_list_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginStart="72dp"
+ android:background="@color/divider_line_color"/>
+
+ </LinearLayout>
+
+ </android.support.v7.widget.CardView>
+
+</LinearLayout>
diff --git a/java/com/android/dialer/app/res/layout/blocked_number_item.xml b/java/com/android/dialer/app/res/layout/blocked_number_item.xml
new file mode 100644
index 000000000..92ebdc35d
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/blocked_number_item.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/caller_information"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/blocked_number_horizontal_margin"
+ android:background="@android:color/white"
+ android:baselineAligned="false"
+ android:focusable="true"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <QuickContactBadge
+ android:id="@+id/quick_contact_photo"
+ android:layout_width="@dimen/contact_photo_size"
+ android:layout_height="@dimen/contact_photo_size"
+ android:layout_marginTop="@dimen/blocked_number_top_margin"
+ android:layout_marginBottom="@dimen/blocked_number_bottom_margin"
+ android:focusable="true"/>
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:layout_marginStart="@dimen/blocked_number_horizontal_margin"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/caller_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:includeFontPadding="false"
+ android:singleLine="true"
+ android:textColor="@color/blocked_number_primary_text_color"
+ android:textSize="@dimen/blocked_number_primary_text_size"/>
+
+ <TextView
+ android:id="@+id/caller_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textColor="@color/blocked_number_secondary_text_color"
+ android:textSize="@dimen/blocked_number_settings_description_text_size"/>
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/delete_button"
+ android:layout_width="@dimen/blocked_number_delete_icon_size"
+ android:layout_height="@dimen/blocked_number_delete_icon_size"
+ android:layout_marginEnd="24dp"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/description_blocked_number_list_delete"
+ android:scaleType="center"
+ android:src="@drawable/ic_remove"
+ android:tint="@color/blocked_number_icon_tint"/>
+
+</LinearLayout>
diff --git a/res/layout/blocked_numbers_activity.xml b/java/com/android/dialer/app/res/layout/blocked_numbers_activity.xml
index d28eaf879..0c4874c0f 100644
--- a/res/layout/blocked_numbers_activity.xml
+++ b/java/com/android/dialer/app/res/layout/blocked_numbers_activity.xml
@@ -15,8 +15,8 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/blocked_numbers_activity_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginTop="@dimen/action_bar_height">
+ android:id="@+id/blocked_numbers_activity_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="@dimen/action_bar_height">
</FrameLayout>
diff --git a/java/com/android/dialer/app/res/layout/call_detail.xml b/java/com/android/dialer/app/res/layout/call_detail.xml
new file mode 100644
index 000000000..58a7bf0dc
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/call_detail.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/call_detail"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/background_dialer_call_log">
+
+ <!--
+ The list view is under everything.
+ It contains a first header element which is hidden under the controls UI.
+ When scrolling, the controls move up until the name bar hits the top.
+ -->
+ <ListView
+ android:id="@+id/history"
+ android:layout_width="match_parent"
+ android:layout_height="fill_parent"/>
+
+</FrameLayout>
diff --git a/java/com/android/dialer/app/res/layout/call_detail_footer.xml b/java/com/android/dialer/app/res/layout/call_detail_footer.xml
new file mode 100644
index 000000000..57713448e
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/call_detail_footer.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/divider_line_thickness"
+ android:background="@color/call_log_action_divider"/>
+
+ <TextView
+ android:id="@+id/call_detail_action_copy"
+ style="@style/CallDetailActionItemStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:drawableStart="@drawable/ic_call_detail_content_copy"
+ android:text="@string/action_copy_number_text"/>
+
+ <TextView
+ android:id="@+id/call_detail_action_edit_before_call"
+ style="@style/CallDetailActionItemStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:drawableStart="@drawable/ic_call_detail_edit"
+ android:text="@string/action_edit_number_before_call"
+ android:visibility="gone"/>
+
+ <TextView
+ android:id="@+id/call_detail_action_report"
+ style="@style/CallDetailActionItemStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:drawableStart="@drawable/ic_call_detail_report"
+ android:text="@string/action_report_number"
+ android:visibility="gone"/>
+
+</LinearLayout>
diff --git a/java/com/android/dialer/app/res/layout/call_detail_header.xml b/java/com/android/dialer/app/res/layout/call_detail_header.xml
new file mode 100644
index 000000000..fd85f0af1
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/call_detail_header.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/caller_information"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/call_detail_top_margin"
+ android:paddingBottom="@dimen/call_detail_bottom_margin"
+ android:paddingStart="@dimen/call_detail_horizontal_margin"
+ android:background="@color/background_dialer_white"
+ android:baselineAligned="false"
+ android:elevation="@dimen/call_detail_elevation"
+ android:focusable="true"
+ android:orientation="horizontal">
+
+ <QuickContactBadge
+ android:id="@+id/quick_contact_photo"
+ android:layout_width="@dimen/contact_photo_size"
+ android:layout_height="@dimen/contact_photo_size"
+ android:layout_marginTop="3dp"
+ android:layout_alignParentStart="true"
+ android:layout_gravity="top"
+ android:focusable="true"/>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginStart="@dimen/call_detail_horizontal_margin"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/caller_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"
+ android:layout_marginBottom="3dp"
+ android:includeFontPadding="false"
+ android:singleLine="true"
+ android:textColor="?android:textColorPrimary"
+ android:textSize="@dimen/call_log_primary_text_size"/>
+
+ <TextView
+ android:id="@+id/caller_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="1dp"
+ android:singleLine="true"
+ android:textColor="?android:textColorSecondary"
+ android:textSize="@dimen/call_log_detail_text_size"/>
+
+ <TextView
+ android:id="@+id/phone_account_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textColor="?android:textColorSecondary"
+ android:textSize="@dimen/call_log_detail_text_size"
+ android:visibility="gone"/>
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/call_back_button"
+ android:layout_width="@dimen/call_log_list_item_primary_action_dimen"
+ android:layout_height="@dimen/call_log_list_item_primary_action_dimen"
+ android:layout_marginEnd="4dp"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/description_call_log_call_action"
+ android:scaleType="center"
+ android:src="@drawable/ic_call_24dp"
+ android:tint="@color/call_log_list_item_primary_action_icon_tint"
+ android:visibility="gone"/>
+
+</LinearLayout>
diff --git a/java/com/android/dialer/app/res/layout/call_detail_history_item.xml b/java/com/android/dialer/app/res/layout/call_detail_history_item.xml
new file mode 100644
index 000000000..5958ee81c
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/call_detail_history_item.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/call_log_inner_margin"
+ android:paddingBottom="@dimen/call_log_inner_margin"
+ android:paddingStart="@dimen/call_detail_horizontal_margin"
+ android:paddingEnd="@dimen/call_log_outer_margin"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <view
+ android:id="@+id/call_type_icon"
+ class="com.android.dialer.app.calllog.CallTypeIconsView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
+ <TextView
+ android:id="@+id/call_type_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/call_log_icon_margin"
+ android:textColor="?android:textColorPrimary"
+ android:textSize="@dimen/call_log_primary_text_size"/>
+ </LinearLayout>
+ <TextView
+ android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="?android:textColorSecondary"
+ android:textSize="@dimen/call_log_detail_text_size"/>
+ <TextView
+ android:id="@+id/duration"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="?android:textColorSecondary"
+ android:textSize="@dimen/call_log_detail_text_size"/>
+</LinearLayout>
diff --git a/java/com/android/dialer/app/res/layout/call_log_alert_item.xml b/java/com/android/dialer/app/res/layout/call_log_alert_item.xml
new file mode 100644
index 000000000..1e487c288
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/call_log_alert_item.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+</LinearLayout> \ No newline at end of file
diff --git a/java/com/android/dialer/app/res/layout/call_log_fragment.xml b/java/com/android/dialer/app/res/layout/call_log_fragment.xml
new file mode 100644
index 000000000..64f7c10e6
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/call_log_fragment.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Layout parameters are set programmatically. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/background_dialer_call_log"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/modal_message_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"/>
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/recycler_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="@dimen/floating_action_button_list_bottom_padding"
+ android:paddingStart="@dimen/call_log_horizontal_margin"
+ android:paddingEnd="@dimen/call_log_horizontal_margin"
+ android:background="@color/background_dialer_call_log"
+ android:clipToPadding="false"/>
+
+ <com.android.dialer.app.widget.EmptyContentView
+ android:id="@+id/empty_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:layout_gravity="center"
+ android:gravity="center_vertical"/>
+
+</LinearLayout>
diff --git a/java/com/android/dialer/app/res/layout/call_log_list_item.xml b/java/com/android/dialer/app/res/layout/call_log_list_item.xml
new file mode 100644
index 000000000..d54415369
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/call_log_list_item.xml
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/call_log_list_item"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <!-- Day group heading. Used to show a "today", "yesterday", "last week" or "other" heading
+ above a group of call log entries. -->
+ <TextView
+ android:id="@+id/call_log_day_group_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:layout_marginStart="@dimen/call_log_start_margin"
+ android:layout_marginEnd="@dimen/call_log_outer_margin"
+ android:fontFamily="sans-serif-medium"
+ android:textColor="@color/call_log_day_group_heading_color"
+ android:textSize="@dimen/call_log_day_group_heading_size"
+ android:paddingTop="@dimen/call_log_day_group_padding_top"
+ android:paddingBottom="@dimen/call_log_day_group_padding_bottom"/>
+
+ <android.support.v7.widget.CardView
+ android:id="@+id/call_log_row"
+ style="@style/CallLogCardStyle">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <!-- Primary area containing the contact badge and caller information -->
+ <LinearLayout
+ android:id="@+id/primary_action_view"
+ android:background="?android:attr/selectableItemBackground"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/call_log_start_margin"
+ android:paddingEnd="@dimen/call_log_outer_margin"
+ android:paddingTop="@dimen/call_log_vertical_padding"
+ android:paddingBottom="@dimen/call_log_vertical_padding"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:focusable="true"
+ android:nextFocusRight="@+id/call_back_action"
+ android:nextFocusLeft="@+id/quick_contact_photo">
+
+ <QuickContactBadge
+ android:id="@+id/quick_contact_photo"
+ android:layout_width="@dimen/contact_photo_size"
+ android:layout_height="@dimen/contact_photo_size"
+ android:paddingTop="2dp"
+ android:nextFocusRight="@id/primary_action_view"
+ android:layout_gravity="top"
+ android:focusable="true"/>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center_vertical"
+ android:layout_marginStart="@dimen/call_log_list_item_info_margin_start">
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/call_log_name_margin_bottom"
+ android:layout_marginEnd="@dimen/call_log_icon_margin"
+ android:textColor="@color/call_log_primary_color"
+ android:textSize="@dimen/call_log_primary_text_size"
+ android:singleLine="true"/>
+
+ <LinearLayout
+ android:id="@+id/call_type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <view
+ class="com.android.dialer.app.calllog.CallTypeIconsView"
+ android:id="@+id/call_type_icons"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/call_log_icon_margin"
+ android:layout_gravity="center_vertical"/>
+
+ <ImageView
+ android:id="@+id/work_profile_icon"
+ android:src="@drawable/ic_work_profile"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/call_log_icon_margin"
+ android:scaleType="center"
+ android:visibility="gone"/>
+
+ <TextView
+ android:id="@+id/call_location_and_date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/call_log_icon_margin"
+ android:layout_gravity="center_vertical"
+ android:textColor="@color/call_log_detail_color"
+ android:textSize="@dimen/call_log_detail_text_size"
+ android:singleLine="true"/>
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/call_account_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/call_log_call_account_margin_bottom"
+ android:layout_marginEnd="@dimen/call_log_icon_margin"
+ android:textColor="?android:textColorSecondary"
+ android:textSize="@dimen/call_log_detail_text_size"
+ android:visibility="gone"
+ android:singleLine="true"/>
+
+ <TextView
+ android:id="@+id/voicemail_transcription"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/call_log_icon_margin"
+ android:textColor="@color/call_log_voicemail_transcript_color"
+ android:textSize="@dimen/call_log_voicemail_transcription_text_size"
+ android:ellipsize="marquee"
+ android:autoLink="all"
+ android:visibility="gone"
+ android:singleLine="false"
+ android:maxLines="10"/>
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/primary_action_button"
+ android:layout_width="@dimen/call_log_list_item_primary_action_dimen"
+ android:layout_height="@dimen/call_log_list_item_primary_action_dimen"
+ android:layout_gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:scaleType="center"
+ android:tint="@color/call_log_list_item_primary_action_icon_tint"
+ android:visibility="gone"/>
+
+ </LinearLayout>
+
+ <!-- Viewstub with additional expandable actions for a call log entry -->
+ <ViewStub
+ android:id="@+id/call_log_entry_actions_stub"
+ android:inflatedId="@+id/call_log_entry_actions"
+ android:layout="@layout/call_log_list_item_actions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"/>
+
+ </LinearLayout>
+
+ </android.support.v7.widget.CardView>
+
+</LinearLayout>
diff --git a/java/com/android/dialer/app/res/layout/call_log_list_item_actions.xml b/java/com/android/dialer/app/res/layout/call_log_list_item_actions.xml
new file mode 100644
index 000000000..5b857afa0
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/call_log_list_item_actions.xml
@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 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
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/call_log_action_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:importantForAccessibility="1"
+ android:orientation="vertical"
+ android:visibility="visible">
+
+ <com.android.dialer.app.voicemail.VoicemailPlaybackLayout
+ android:id="@+id/voicemail_playback_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="@color/call_log_action_divider"/>
+
+ <LinearLayout
+ android:id="@+id/call_action"
+ style="@style/CallLogActionStyle"
+ android:paddingTop="@dimen/call_log_actions_top_padding">
+
+ <ImageView
+ style="@style/CallLogActionIconStyle"
+ android:src="@drawable/ic_call_24dp"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/call_action_text"
+ style="@style/CallLogActionTextStyle"
+ android:text="@string/description_call_log_call_action"/>
+
+ <TextView
+ android:id="@+id/call_type_or_location_text"
+ style="@style/CallLogActionSupportTextStyle"/>
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/video_call_action"
+ style="@style/CallLogActionStyle">
+
+ <ImageView
+ style="@style/CallLogActionIconStyle"
+ android:src="@drawable/quantum_ic_videocam_white_24"/>
+
+ <TextView
+ style="@style/CallLogActionTextStyle"
+ android:text="@string/call_log_action_video_call"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/create_new_contact_action"
+ style="@style/CallLogActionStyle">
+
+ <ImageView
+ style="@style/CallLogActionIconStyle"
+ android:src="@drawable/ic_person_add_24dp"/>
+
+ <TextView
+ style="@style/CallLogActionTextStyle"
+ android:text="@string/search_shortcut_create_new_contact"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/add_to_existing_contact_action"
+ style="@style/CallLogActionStyle">
+
+ <ImageView
+ style="@style/CallLogActionIconStyle"
+ android:src="@drawable/ic_person_24dp"/>
+
+ <TextView
+ style="@style/CallLogActionTextStyle"
+ android:text="@string/search_shortcut_add_to_contact"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/send_message_action"
+ style="@style/CallLogActionStyle">
+
+ <ImageView
+ style="@style/CallLogActionIconStyle"
+ android:src="@drawable/ic_message_24dp"/>
+
+ <TextView
+ style="@style/CallLogActionTextStyle"
+ android:text="@string/call_log_action_send_message"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/call_with_note_action"
+ style="@style/CallLogActionStyle">
+
+ <ImageView
+ style="@style/CallLogActionIconStyle"
+ android:src="@drawable/ic_call_note_white_24dp"/>
+
+ <TextView
+ style="@style/CallLogActionTextStyle"
+ android:text="@string/call_with_a_note"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/call_compose_action"
+ style="@style/CallLogActionStyle">
+
+ <ImageView
+ style="@style/CallLogActionIconStyle"
+ android:src="@drawable/ic_phone_attach"/>
+
+ <TextView
+ style="@style/CallLogActionTextStyle"
+ android:text="@string/share_and_call"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/report_not_spam_action"
+ style="@style/CallLogActionStyle"
+ android:visibility="gone">
+
+ <ImageView
+ style="@style/CallLogActionIconStyle"
+ android:src="@drawable/ic_not_spam"/>
+
+ <TextView
+ style="@style/CallLogActionTextStyle"
+ android:text="@string/call_log_action_remove_spam"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/block_report_action"
+ style="@style/CallLogActionStyle"
+ android:visibility="gone">
+
+ <ImageView
+ style="@style/CallLogActionIconStyle"
+ android:src="@drawable/ic_block_24dp"/>
+
+ <TextView
+ style="@style/CallLogActionTextStyle"
+ android:text="@string/call_log_action_block_report_number"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/block_action"
+ style="@style/CallLogActionStyle"
+ android:visibility="gone">
+
+ <ImageView
+ style="@style/CallLogActionIconStyle"
+ android:src="@drawable/ic_block_24dp"/>
+
+ <TextView
+ style="@style/CallLogActionTextStyle"
+ android:text="@string/call_log_action_block_number"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/unblock_action"
+ style="@style/CallLogActionStyle"
+ android:visibility="gone">
+
+ <ImageView
+ style="@style/CallLogActionIconStyle"
+ android:src="@drawable/ic_unblock"/>
+
+ <TextView
+ style="@style/CallLogActionTextStyle"
+ android:text="@string/call_log_action_unblock_number"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/details_action"
+ style="@style/CallLogActionStyle">
+
+ <ImageView
+ style="@style/CallLogActionIconStyle"
+ android:src="@drawable/ic_info_outline_24dp"/>
+
+ <TextView
+ style="@style/CallLogActionTextStyle"
+ android:text="@string/call_log_action_details"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/share_voicemail"
+ android:visibility="gone"
+ style="@style/CallLogActionStyle">
+
+ <ImageView
+ style="@style/CallLogActionIconStyle"
+ android:src="@drawable/quantum_ic_send_black_24"/>
+
+ <TextView
+ style="@style/CallLogActionTextStyle"
+ android:text="@string/call_log_action_share_voicemail"/>
+
+ </LinearLayout>
+</LinearLayout>
diff --git a/java/com/android/dialer/app/res/layout/dialpad_chooser_list_item.xml b/java/com/android/dialer/app/res/layout/dialpad_chooser_list_item.xml
new file mode 100644
index 000000000..e00529614
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/dialpad_chooser_list_item.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<!-- Layout of a single item in the Dialer's "Dialpad chooser" UI. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:scaleType="center"/>
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/dialpad_primary_text_color"/>
+
+</LinearLayout>
diff --git a/java/com/android/dialer/app/res/layout/dialpad_fragment.xml b/java/com/android/dialer/app/res/layout/dialpad_fragment.xml
new file mode 100644
index 000000000..2cf198fcb
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/dialpad_fragment.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.dialer.app.dialpad.DialpadFragment$DialpadSlidingRelativeLayout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <!-- spacer view -->
+ <View
+ android:id="@+id/spacer"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:background="#00000000"/>
+ <!-- Dialpad shadow -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/shadow_length"
+ android:background="@drawable/shadow_fade_up"/>
+ <include layout="@layout/dialpad_view"/>
+ <!-- "Dialpad chooser" UI, shown only when the user brings up the
+ Dialer while a call is already in progress.
+ When this UI is visible, the other Dialer elements
+ (the textfield/button and the dialpad) are hidden. -->
+ <ListView
+ android:id="@+id/dialpadChooser"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/background_dialer_light"
+ android:visibility="gone"/>
+
+ </LinearLayout>
+
+ <!-- Margin bottom and alignParentBottom don't work well together, so use a Space instead. -->
+ <Space
+ android:id="@+id/dialpad_floating_action_button_margin_bottom"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/floating_action_button_margin_bottom"
+ android:layout_alignParentBottom="true"/>
+
+ <FrameLayout
+ android:id="@+id/dialpad_floating_action_button_container"
+ android:layout_width="@dimen/floating_action_button_width"
+ android:layout_height="@dimen/floating_action_button_height"
+ android:layout_above="@id/dialpad_floating_action_button_margin_bottom"
+ android:layout_centerHorizontal="true"
+ android:background="@drawable/fab_green">
+
+ <ImageButton
+ android:id="@+id/dialpad_floating_action_button"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/floating_action_button"
+ android:contentDescription="@string/description_dial_button"
+ android:src="@drawable/fab_ic_call"/>
+
+ </FrameLayout>
+
+</view>
diff --git a/java/com/android/dialer/app/res/layout/dialtacts_activity.xml b/java/com/android/dialer/app/res/layout/dialtacts_activity.xml
new file mode 100644
index 000000000..042b4a5e8
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/dialtacts_activity.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<android.support.design.widget.CoordinatorLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/dialtacts_mainlayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/background_dialer_light"
+ android:clipChildren="false"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/dialtacts_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false">
+ <!-- The main contacts grid -->
+ <FrameLayout
+ android:id="@+id/dialtacts_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"/>
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/floating_action_button_container"
+ android:layout_width="@dimen/floating_action_button_width"
+ android:layout_height="@dimen/floating_action_button_height"
+ android:layout_marginBottom="@dimen/floating_action_button_margin_bottom"
+ android:layout_gravity="center_horizontal|bottom"
+ android:background="@drawable/dialer_fab"
+ app:layout_behavior="com.android.dialer.app.FloatingActionButtonBehavior">
+
+ <ImageButton
+ android:id="@+id/floating_action_button"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/floating_action_button"
+ android:contentDescription="@string/action_menu_dialpad_button"
+ android:src="@drawable/fab_ic_dial"/>
+
+ </FrameLayout>
+
+ <!-- Host container for the contact tile drag shadow -->
+ <FrameLayout
+ android:id="@+id/activity_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ImageView
+ android:id="@+id/contact_tile_drag_shadow_overlay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:importantForAccessibility="no"
+ android:visibility="gone"/>
+ </FrameLayout>
+
+</android.support.design.widget.CoordinatorLayout>
diff --git a/java/com/android/dialer/app/res/layout/empty_content_view.xml b/java/com/android/dialer/app/res/layout/empty_content_view.xml
new file mode 100644
index 000000000..96a6a0262
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/empty_content_view.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <ImageView
+ android:id="@+id/emptyListViewImage"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"/>
+
+ <TextView
+ android:id="@+id/emptyListViewMessage"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:gravity="center_horizontal|top"
+ android:textColor="@color/empty_list_text_color"
+ android:textSize="@dimen/empty_list_message_text_size"/>
+
+ <TextView
+ android:id="@+id/emptyListViewAction"
+ style="@style/TextActionStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:clickable="true"
+ android:gravity="center_horizontal"/>
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="40dp"/>
+
+</merge>
diff --git a/java/com/android/dialer/app/res/layout/empty_content_view_dialpad_search.xml b/java/com/android/dialer/app/res/layout/empty_content_view_dialpad_search.xml
new file mode 100644
index 000000000..e245aaca0
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/empty_content_view_dialpad_search.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <ImageView
+ android:id="@+id/emptyListViewImage"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal" />
+
+ <TextView
+ android:id="@+id/emptyListViewMessage"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal|top"
+ android:textSize="@dimen/empty_list_message_text_size"
+ android:textColor="@color/empty_list_text_color"
+ android:paddingRight="16dp"
+ android:paddingLeft="16dp"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"/>
+
+ <TextView
+ android:id="@+id/emptyListViewAction"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:layout_gravity="center_horizontal"
+ android:paddingRight="16dp"
+ android:paddingLeft="16dp"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:clickable="true"
+ style="@style/TextActionStyle" />
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="40dp" />
+
+</merge> \ No newline at end of file
diff --git a/java/com/android/dialer/app/res/layout/keyguard_preview.xml b/java/com/android/dialer/app/res/layout/keyguard_preview.xml
new file mode 100644
index 000000000..41fe89165
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/keyguard_preview.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 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
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="25dp"
+ android:background="@color/dialer_theme_color_dark"/>
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:background="#ffffff"/>
+</LinearLayout>
diff --git a/java/com/android/dialer/app/res/layout/lists_fragment.xml b/java/com/android/dialer/app/res/layout/lists_fragment.xml
new file mode 100644
index 000000000..442b428f2
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/lists_fragment.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/lists_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:animateLayoutChanges="true">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <!-- TODO: Apply background color to ActionBar instead of a FrameLayout. For now, this is
+ the easiest way to preserve correct pane scrolling and searchbar collapse/expand
+ behaviors. -->
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/action_bar_height_large"
+ android:background="@color/actionbar_background_color"
+ android:elevation="@dimen/tab_elevation"/>
+
+ <com.android.contacts.common.list.ViewPagerTabs
+ android:id="@+id/lists_pager_header"
+ style="@style/DialtactsActionBarTabTextStyle"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/tab_height"
+ android:layout_gravity="top"
+ android:elevation="@dimen/tab_elevation"
+ android:orientation="horizontal"
+ android:textAllCaps="true"/>
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/lists_pager"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ </LinearLayout>
+
+ <!-- Sets android:importantForAccessibility="no" to avoid being announced when navigating with
+ talkback enabled. It will still be announced when user drag or drop contact onto it.
+ This is required since drag and drop event is only sent to views are visible when drag
+ starts. -->
+ <com.android.dialer.app.list.RemoveView
+ android:id="@+id/remove_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/tab_height"
+ android:layout_marginTop="@dimen/action_bar_height_large"
+ android:contentDescription="@string/remove_contact"
+ android:importantForAccessibility="no">
+
+ <LinearLayout
+ android:id="@+id/remove_view_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/actionbar_background_color"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:visibility="gone">
+
+ <ImageView
+ android:id="@+id/remove_view_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:src="@drawable/ic_remove"
+ android:tint="@color/remove_text_color"/>
+
+ <TextView
+ android:id="@+id/remove_view_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/remove_contact"
+ android:textColor="@color/remove_text_color"
+ android:textSize="@dimen/remove_text_size"/>
+
+ </LinearLayout>
+
+ </com.android.dialer.app.list.RemoveView>
+
+</FrameLayout>
diff --git a/java/com/android/dialer/app/res/layout/phone_favorite_tile_view.xml b/java/com/android/dialer/app/res/layout/phone_favorite_tile_view.xml
new file mode 100644
index 000000000..92b2e8e53
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/phone_favorite_tile_view.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<view
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/contact_tile"
+ class="com.android.dialer.app.list.PhoneFavoriteSquareTileView"
+ android:paddingBottom="@dimen/contact_tile_divider_width"
+ android:paddingEnd="@dimen/contact_tile_divider_width">
+
+ <RelativeLayout
+ android:id="@+id/contact_favorite_card"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:nextFocusRight="@+id/contact_tile_secondary_button">
+
+ <com.android.contacts.common.widget.LayoutSuppressingImageView
+ android:id="@+id/contact_tile_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerCrop"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="6"/>
+ <View
+ android:id="@+id/shadow_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="4"
+ android:background="@drawable/shadow_contact_photo"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:paddingBottom="@dimen/contact_tile_text_bottom_padding"
+ android:paddingStart="@dimen/contact_tile_text_side_padding"
+ android:paddingEnd="@dimen/contact_tile_text_side_padding"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+ <TextView
+ android:id="@+id/contact_tile_name"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:fadingEdgeLength="3dip"
+ android:fontFamily="sans-serif-medium"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textColor="@color/contact_tile_name_color"
+ android:textSize="15sp"/>
+ <ImageView
+ android:id="@+id/contact_star_icon"
+ android:layout_width="@dimen/favorites_star_icon_size"
+ android:layout_height="@dimen/favorites_star_icon_size"
+ android:layout_marginStart="3dp"
+ android:src="@drawable/ic_star"
+ android:visibility="gone"/>
+ </LinearLayout>
+ <TextView
+ android:id="@+id/contact_tile_phone_type"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:fadingEdgeLength="3dip"
+ android:fontFamily="sans-serif"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textColor="@color/contact_tile_name_color"
+ android:textSize="11sp"/>
+ </LinearLayout>
+
+ <View
+ android:id="@+id/contact_tile_push_state"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/item_background_material_dark"
+ android:importantForAccessibility="no"/>
+
+ <ImageButton
+ android:id="@id/contact_tile_secondary_button"
+ android:layout_width="@dimen/contact_tile_info_button_height_and_width"
+ android:layout_height="@dimen/contact_tile_info_button_height_and_width"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:paddingTop="8dp"
+ android:paddingBottom="4dp"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
+ android:paddingLeft="4dp"
+ android:paddingRight="9dp"
+ android:background="@drawable/item_background_material_dark"
+ android:contentDescription="@string/description_view_contact_detail"
+ android:scaleType="center"
+ android:src="@drawable/ic_more_vert_24dp"/>
+
+ </RelativeLayout>
+</view>
diff --git a/java/com/android/dialer/app/res/layout/search_edittext.xml b/java/com/android/dialer/app/res/layout/search_edittext.xml
new file mode 100644
index 000000000..1b4f9c4a4
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/search_edittext.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/search_view_container"
+ class="com.android.dialer.app.widget.SearchEditTextLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="@dimen/search_top_margin"
+ android:layout_marginBottom="@dimen/search_bottom_margin"
+ android:layout_marginLeft="@dimen/search_margin_horizontal"
+ android:layout_marginRight="@dimen/search_margin_horizontal"
+ android:background="@drawable/rounded_corner"
+ android:elevation="@dimen/search_box_elevation"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/search_box_collapsed"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingStart="@dimen/search_box_left_padding"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/search_magnifying_glass"
+ android:layout_width="@dimen/search_box_icon_size"
+ android:layout_height="@dimen/search_box_icon_size"
+ android:padding="@dimen/search_box_search_icon_padding"
+ android:importantForAccessibility="no"
+ android:scaleType="center"
+ android:src="@drawable/ic_ab_search"
+ android:tint="@color/searchbox_icon_tint"/>
+
+ <TextView
+ android:id="@+id/search_box_start_search"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/search_box_collapsed_text_margin_left"
+ android:fontFamily="@string/search_font_family"
+ android:gravity="center_vertical"
+ android:hint="@string/dialer_hint_find_contact"
+ android:textColorHint="@color/searchbox_hint_text_color"
+ android:textSize="@dimen/search_collapsed_text_size"/>
+
+ <ImageView
+ android:id="@+id/voice_search_button"
+ android:layout_width="@dimen/search_box_icon_size"
+ android:layout_height="match_parent"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:clickable="true"
+ android:contentDescription="@string/description_start_voice_search"
+ android:scaleType="center"
+ android:src="@drawable/ic_mic_grey600"
+ android:tint="@color/searchbox_icon_tint"/>
+
+ <ImageButton
+ android:id="@+id/dialtacts_options_menu_button"
+ android:layout_width="@dimen/search_box_icon_size"
+ android:layout_height="match_parent"
+ android:paddingEnd="@dimen/search_box_right_padding"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/action_menu_overflow_description"
+ android:scaleType="center"
+ android:src="@drawable/ic_overflow_menu"
+ android:tint="@color/searchbox_icon_tint"/>
+
+ </LinearLayout>
+
+ <include layout="@layout/search_bar_expanded"/>
+
+</view>
diff --git a/java/com/android/dialer/app/res/layout/speed_dial_fragment.xml b/java/com/android/dialer/app/res/layout/speed_dial_fragment.xml
new file mode 100644
index 000000000..c778c6bc4
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/speed_dial_fragment.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false">
+
+ <FrameLayout
+ android:id="@+id/contact_tile_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:paddingStart="@dimen/favorites_row_start_padding"
+ android:paddingEnd="@dimen/favorites_row_end_padding">
+ <com.android.dialer.app.list.PhoneFavoriteListView
+ android:id="@+id/contact_tile_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="@dimen/favorites_row_top_padding"
+ android:paddingBottom="@dimen/floating_action_button_list_bottom_padding"
+ android:clipToPadding="false"
+ android:divider="@null"
+ android:fadingEdge="none"
+ android:nestedScrollingEnabled="true"
+ android:numColumns="@integer/contact_tile_column_count_in_favorites"/>
+ </FrameLayout>
+
+ <com.android.dialer.app.widget.EmptyContentView
+ android:id="@+id/empty_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:visibility="gone"/>
+
+</FrameLayout>
diff --git a/java/com/android/dialer/app/res/layout/view_numbers_to_import_fragment.xml b/java/com/android/dialer/app/res/layout/view_numbers_to_import_fragment.xml
new file mode 100644
index 000000000..be691748a
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/view_numbers_to_import_fragment.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/blocked_number_background"
+ android:orientation="vertical">
+
+ <ListView
+ android:id="@id/android:list"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:divider="@null"
+ android:headerDividersEnabled="false"/>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:background="@android:color/white">
+
+ <Button
+ android:id="@+id/import_button"
+ style="@style/DialerFlatButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/blocked_number_container_padding"
+ android:layout_alignParentEnd="true"
+ android:text="@string/blocked_call_settings_import_button"/>
+
+ <Button
+ android:id="@+id/cancel_button"
+ style="@style/DialerFlatButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/import_description"
+ android:layout_toLeftOf="@id/import_button"
+ android:text="@android:string/cancel"/>
+
+ </RelativeLayout>
+
+</LinearLayout>
diff --git a/java/com/android/dialer/app/res/layout/voicemail_playback_layout.xml b/java/com/android/dialer/app/res/layout/voicemail_playback_layout.xml
new file mode 100644
index 000000000..7fff9d204
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/voicemail_playback_layout.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="64dp"
+ android:layout_marginEnd="24dp"
+ android:background="@color/background_dialer_call_log_list_item"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/playback_state_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textSize="14sp"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/voicemail_playback_top_padding"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/playback_position_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:importantForAccessibility="no"
+ android:textSize="14sp"/>
+
+ <SeekBar
+ android:id="@+id/playback_seek"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:contentDescription="@string/description_playback_seek"
+ android:max="0"
+ android:progress="0"
+ android:progressDrawable="@drawable/seekbar_drawable"
+ android:thumb="@drawable/ic_voicemail_seek_handle"/>
+
+ <TextView
+ android:id="@+id/total_duration_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:importantForAccessibility="no"
+ android:textSize="14sp"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="horizontal">
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+
+ <ImageButton
+ android:id="@+id/playback_speakerphone"
+ style="@style/VoicemailPlaybackLayoutButtonStyle"
+ android:contentDescription="@string/description_playback_speakerphone"
+ android:src="@drawable/ic_volume_down_24dp"
+ android:tint="@color/voicemail_icon_tint"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+
+ <ImageButton
+ android:id="@+id/playback_start_stop"
+ style="@style/VoicemailPlaybackLayoutButtonStyle"
+ android:contentDescription="@string/voicemail_play_start_pause"
+ android:src="@drawable/ic_play_arrow"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+
+ <ImageButton
+ android:id="@+id/delete_voicemail"
+ style="@style/VoicemailPlaybackLayoutButtonStyle"
+ android:contentDescription="@string/call_log_trash_voicemail"
+ android:src="@drawable/ic_delete_24dp"
+ android:tint="@color/voicemail_icon_tint"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/java/com/android/dialer/app/res/menu/dialpad_options.xml b/java/com/android/dialer/app/res/menu/dialpad_options.xml
new file mode 100644
index 000000000..2921ea3bb
--- /dev/null
+++ b/java/com/android/dialer/app/res/menu/dialpad_options.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item
+ android:id="@+id/menu_2s_pause"
+ android:showAsAction="withText"
+ android:title="@string/add_2sec_pause"/>
+ <item
+ android:id="@+id/menu_add_wait"
+ android:showAsAction="withText"
+ android:title="@string/add_wait"/>
+ <item
+ android:id="@+id/menu_call_with_note"
+ android:showAsAction="withText"
+ android:title="@string/call_with_a_note"/>
+</menu>
diff --git a/java/com/android/dialer/app/res/menu/dialtacts_options.xml b/java/com/android/dialer/app/res/menu/dialtacts_options.xml
new file mode 100644
index 000000000..434aa81d9
--- /dev/null
+++ b/java/com/android/dialer/app/res/menu/dialtacts_options.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item
+ android:id="@+id/menu_delete_all"
+ android:title="@string/call_log_delete_all"/>
+ <item
+ android:id="@+id/menu_clear_frequents"
+ android:title="@string/menu_clear_frequents"/>
+ <item
+ android:id="@+id/menu_call_settings"
+ android:title="@string/dialer_settings_label"/>
+
+</menu>
diff --git a/res/mipmap-hdpi/ic_launcher_phone.png b/java/com/android/dialer/app/res/mipmap-hdpi/ic_launcher_phone.png
index 15c41423b..15c41423b 100644
--- a/res/mipmap-hdpi/ic_launcher_phone.png
+++ b/java/com/android/dialer/app/res/mipmap-hdpi/ic_launcher_phone.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_phone.png b/java/com/android/dialer/app/res/mipmap-mdpi/ic_launcher_phone.png
index 3088f7502..3088f7502 100644
--- a/res/mipmap-mdpi/ic_launcher_phone.png
+++ b/java/com/android/dialer/app/res/mipmap-mdpi/ic_launcher_phone.png
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher_phone.png b/java/com/android/dialer/app/res/mipmap-xhdpi/ic_launcher_phone.png
index e87de01fb..e87de01fb 100644
--- a/res/mipmap-xhdpi/ic_launcher_phone.png
+++ b/java/com/android/dialer/app/res/mipmap-xhdpi/ic_launcher_phone.png
Binary files differ
diff --git a/res/mipmap-xxhdpi/ic_launcher_phone.png b/java/com/android/dialer/app/res/mipmap-xxhdpi/ic_launcher_phone.png
index b866b79a7..b866b79a7 100644
--- a/res/mipmap-xxhdpi/ic_launcher_phone.png
+++ b/java/com/android/dialer/app/res/mipmap-xxhdpi/ic_launcher_phone.png
Binary files differ
diff --git a/res/mipmap-xxxhdpi/ic_launcher_phone.png b/java/com/android/dialer/app/res/mipmap-xxxhdpi/ic_launcher_phone.png
index 26f51f153..26f51f153 100644
--- a/res/mipmap-xxxhdpi/ic_launcher_phone.png
+++ b/java/com/android/dialer/app/res/mipmap-xxxhdpi/ic_launcher_phone.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/values/animation_constants.xml b/java/com/android/dialer/app/res/values/animation_constants.xml
new file mode 100644
index 000000000..91230cd54
--- /dev/null
+++ b/java/com/android/dialer/app/res/values/animation_constants.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources>
+ <integer name="fade_duration">300</integer>
+
+ <!-- Swipe constants -->
+ <integer name="swipe_escape_velocity">100</integer>
+ <integer name="escape_animation_duration">200</integer>
+ <integer name="max_escape_animation_duration">400</integer>
+ <integer name="max_dismiss_velocity">2000</integer>
+ <integer name="snap_animation_duration">350</integer>
+ <integer name="swipe_scroll_slop">2</integer>
+ <dimen name="min_swipe">0dip</dimen>
+ <dimen name="min_vert">10dip</dimen>
+ <dimen name="min_lock">20dip</dimen>
+</resources>
diff --git a/java/com/android/dialer/app/res/values/attrs.xml b/java/com/android/dialer/app/res/values/attrs.xml
new file mode 100644
index 000000000..b346390f7
--- /dev/null
+++ b/java/com/android/dialer/app/res/values/attrs.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources>
+
+ <declare-styleable name="SearchEditTextLayout"/>
+
+</resources>
diff --git a/java/com/android/dialer/app/res/values/colors.xml b/java/com/android/dialer/app/res/values/colors.xml
new file mode 100644
index 000000000..b88e55276
--- /dev/null
+++ b/java/com/android/dialer/app/res/values/colors.xml
@@ -0,0 +1,115 @@
+<!--
+ ~ 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
+-->
+
+<resources>
+ <color name="dialer_red_highlight_color">#ff1744</color>
+ <color name="dialer_green_highlight_color">#00c853</color>
+
+ <color name="dialer_button_text_color">#fff</color>
+ <color name="dialer_flat_button_text_color">@color/dialer_theme_color</color>
+
+ <!-- Color for the setting text. -->
+ <color name="setting_primary_color">@color/dialer_primary_text_color</color>
+ <!-- Color for the setting description text. -->
+ <color name="setting_secondary_color">@color/dialer_secondary_text_color</color>
+ <color name="setting_disabled_color">#aaaaaa</color>
+ <color name="setting_background_color">#ffffff</color>
+ <color name="setting_button_color">#eee</color>
+
+ <!-- 54% black -->
+ <color name="call_log_icon_tint">#8a000000</color>
+ <!-- 87% black -->
+ <color name="call_log_primary_color">#de000000</color>
+ <!-- 54% black -->
+ <color name="call_log_detail_color">#8a000000</color>
+ <!-- 87% black -->
+ <color name="call_log_voicemail_transcript_color">#de000000</color>
+ <!-- 70% black -->
+ <color name="call_log_action_color">#b3000000</color>
+ <!-- 54% black -->
+ <color name="call_log_day_group_heading_color">#8a000000</color>
+ <!-- 87% black-->
+ <color name="call_log_unread_text_color">#de000000</color>
+ <color name="call_log_list_item_primary_action_icon_tint">@color/call_log_icon_tint</color>
+
+ <color name="voicemail_icon_tint">@color/call_log_icon_tint</color>
+ <color name="voicemail_icon_disabled_tint">#80000000</color>
+ <color name="voicemail_playpause_icon_tint">@color/voicemail_icon_tint</color>
+ <!-- Colour of voicemail progress bar to the right of position indicator. -->
+ <color name="voicemail_playback_seek_bar_yet_to_play">#cecece</color>
+ <!-- Colour of voicemail progress bar to the left of position indicator. -->
+ <color name="voicemail_playback_seek_bar_already_played">@color/dialer_theme_color</color>
+
+ <!-- Background color of new dialer activity -->
+ <color name="background_dialer_light">#fafafa</color>
+ <!-- Background color for search results and call details -->
+ <color name="background_dialer_results">#f9f9f9</color>
+ <color name="background_dialer_call_log">@color/background_dialer_light</color>
+
+ <!-- Color of the 1dp divider that separates favorites -->
+ <color name="favorite_contacts_separator_color">#d0d0d0</color>
+
+ <!-- Color of the contact name in favorite tiles -->
+ <color name="contact_tile_name_color">#ffffff</color>
+
+ <color name="contact_list_name_text_color">@color/dialer_primary_text_color</color>
+
+ <!-- Undo dialogue color -->
+ <color name="undo_dialogue_text_color">#4d4d4d</color>
+
+ <color name="empty_list_text_color">#b2b2b2</color>
+
+ <color name="remove_text_color">#ffffff</color>
+
+ <!-- Text color for the "Remove" text when a contact is dragged on top of the remove view -->
+ <color name="remove_highlighted_text_color">#FF3F3B</color>
+
+ <!-- Color of the bottom border below the contacts grid on the main dialer screen. -->
+ <color name="contacts_grid_bottom_border_color">#16000000</color>
+
+ <!-- Color of actions in expanded call log entries. This text color represents actions such
+ as call back, play voicemail, etc. -->
+ <color name="call_log_action_text">@color/dialer_theme_color</color>
+
+ <!-- Color for missed call icons. -->
+ <color name="missed_call">#ff2e58</color>
+ <!-- Color for answered or outgoing call icons. -->
+ <color name="answered_call">@color/dialer_green_highlight_color</color>
+ <!-- Color for blocked call icons. -->
+ <color name="blocked_call">@color/dialer_secondary_text_color</color>
+
+ <color name="dialer_dialpad_touch_tint">@color/dialer_theme_color_20pct</color>
+
+ <color name="floating_action_button_touch_tint">#80ffffff</color>
+
+ <color name="call_log_action_divider">#eeeeee</color>
+ <color name="divider_line_color">#D8D8D8</color>
+
+ <!-- Colors for blocked numbers list -->
+ <color name="blocked_number_primary_text_color">@color/dialer_primary_text_color</color>
+ <color name="blocked_number_secondary_text_color">@color/dialer_secondary_text_color</color>
+ <color name="blocked_number_icon_tint">#616161</color>
+ <color name="blocked_number_background">#FFFFFF</color>
+ <color name="blocked_number_block_color">#F44336</color>
+ <color name="blocked_number_header_color">@color/dialer_theme_color</color>
+ <color name="blocked_number_disabled_emergency_header_color">#616161</color>
+ <color name="blocked_number_disabled_emergency_background_color">#E0E0E0</color>
+ <color name="add_blocked_number_icon_color">#bdbdbd</color>
+ <!-- Grey 700 -->
+ <color name="call_detail_footer_text_color">#616161</color>
+ <color name="call_detail_footer_icon_tint">@color/call_detail_footer_text_color</color>
+
+</resources>
diff --git a/java/com/android/dialer/app/res/values/dimens.xml b/java/com/android/dialer/app/res/values/dimens.xml
new file mode 100644
index 000000000..f3fd63350
--- /dev/null
+++ b/java/com/android/dialer/app/res/values/dimens.xml
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+-->
+<resources>
+ <dimen name="button_horizontal_padding">16dp</dimen>
+ <dimen name="divider_line_thickness">1dp</dimen>
+
+ <!--
+ Drag to remove view (in dp because it is used in conjunction with a statically
+ sized icon
+ -->
+ <dimen name="remove_text_size">16dp</dimen>
+
+ <!-- Call Log -->
+ <dimen name="call_log_horizontal_margin">8dp</dimen>
+ <dimen name="call_log_call_action_size">32dp</dimen>
+ <dimen name="call_log_call_action_width">54dp</dimen>
+ <dimen name="call_log_icon_margin">4dp</dimen>
+ <dimen name="call_log_inner_margin">13dp</dimen>
+ <dimen name="call_log_outer_margin">8dp</dimen>
+ <dimen name="call_log_start_margin">8dp</dimen>
+ <dimen name="call_log_indent_margin">24dp</dimen>
+ <dimen name="call_log_name_margin_bottom">2dp</dimen>
+ <dimen name="call_log_call_account_margin_bottom">2dp</dimen>
+ <dimen name="call_log_vertical_padding">8dp</dimen>
+ <dimen name="call_log_list_item_height">56dp</dimen>
+ <dimen name="call_log_list_item_info_margin_start">16dp</dimen>
+ <dimen name="show_call_history_list_item_height">72dp</dimen>
+
+ <!-- Size of contact photos in the call log and call details. -->
+ <dimen name="contact_photo_size">48dp</dimen>
+ <dimen name="call_detail_button_spacing">2dip</dimen>
+ <dimen name="call_detail_horizontal_margin">20dp</dimen>
+ <dimen name="call_detail_top_margin">16dp</dimen>
+ <dimen name="call_detail_bottom_margin">16dp</dimen>
+ <dimen name="call_detail_header_top_margin">20dp</dimen>
+ <dimen name="call_detail_header_bottom_margin">9dp</dimen>
+ <dimen name="call_detail_elevation">0.5dp</dimen>
+ <dimen name="call_detail_action_item_padding_horizontal">28dp</dimen>
+ <dimen name="call_detail_action_item_padding_vertical">16dp</dimen>
+ <dimen name="call_detail_action_item_drawable_padding">28dp</dimen>
+ <dimen name="call_detail_action_item_text_size">16sp</dimen>
+ <dimen name="transcription_top_margin">18dp</dimen>
+ <dimen name="transcription_bottom_margin">18dp</dimen>
+
+ <!-- Size of call provider icon width and height -->
+ <dimen name="call_provider_small_icon_size">12dp</dimen>
+
+ <!-- Match call_button_height to Phone's dimens/in_call_end_button_height -->
+ <dimen name="call_button_height">74dp</dimen>
+
+ <!-- Dimensions for speed dial tiles -->
+ <dimen name="contact_tile_divider_width">1dp</dimen>
+ <dimen name="contact_tile_info_button_height_and_width">36dp</dimen>
+ <item name="contact_tile_height_to_width_ratio" type="dimen">76%</item>
+ <dimen name="contact_tile_text_side_padding">12dp</dimen>
+ <dimen name="contact_tile_text_bottom_padding">9dp</dimen>
+ <dimen name="favorites_row_top_padding">2dp</dimen>
+ <dimen name="favorites_row_bottom_padding">0dp</dimen>
+ <dimen name="favorites_row_start_padding">1dp</dimen>
+
+ <!-- Padding from the last contact tile will provide the end padding. -->
+ <dimen name="favorites_row_end_padding">0dp</dimen>
+ <dimen name="favorites_row_undo_text_side_padding">32dp</dimen>
+
+ <!-- Size of the star icon on the favorites tile. -->
+ <dimen name="favorites_star_icon_size">12dp</dimen>
+
+ <!-- Padding for the tooltip -->
+ <dimen name="dismiss_button_padding_start">20dip</dimen>
+ <dimen name="dismiss_button_padding_end">28dip</dimen>
+
+ <!-- Margin to the left and right of the search box. -->
+ <dimen name="search_margin_horizontal">8dp</dimen>
+ <!-- Margin above the search box. -->
+ <dimen name="search_top_margin">8dp</dimen>
+ <!-- Margin below the search box. -->
+ <dimen name="search_bottom_margin">8dp</dimen>
+ <dimen name="search_collapsed_text_size">14sp</dimen>
+ <!-- Search box interior padding - left -->
+ <dimen name="search_box_left_padding">8dp</dimen>
+ <!-- Search box interior padding - right -->
+ <dimen name="search_box_right_padding">8dp</dimen>
+ <dimen name="search_box_search_icon_padding">2dp</dimen>
+ <dimen name="search_box_collapsed_text_margin_left">22dp</dimen>
+ <dimen name="search_list_padding_top">16dp</dimen>
+ <dimen name="search_box_elevation">3dp</dimen>
+
+ <!-- Padding for icons to increase their touch target. Icons are typically 24 dps in size
+ so this extra padding makes the entire touch target 40dp -->
+ <dimen name="icon_padding">8dp</dimen>
+
+ <!-- Length of dialpad's shadows in dialer. -->
+ <dimen name="shadow_length">10dp</dimen>
+
+ <dimen name="empty_list_message_top_padding">20dp</dimen>
+ <dimen name="empty_list_message_text_size">16sp</dimen>
+
+ <!-- Dimensions for individual preference cards -->
+ <dimen name="preference_padding_top">16dp</dimen>
+ <dimen name="preference_padding_bottom">16dp</dimen>
+ <dimen name="preference_side_margin">16dp</dimen>
+ <dimen name="preference_summary_line_spacing_extra">4dp</dimen>
+
+ <dimen name="call_log_list_item_primary_action_dimen">48dp</dimen>
+
+ <!-- Dimensions for promo cards -->
+ <dimen name="promo_card_icon_size">24dp</dimen>
+ <dimen name="promo_card_start_padding">16dp</dimen>
+ <dimen name="promo_card_top_padding">21dp</dimen>
+ <dimen name="promo_card_main_padding">24dp</dimen>
+ <dimen name="promo_card_title_padding">12dp</dimen>
+ <dimen name="promo_card_action_vertical_padding">4dp</dimen>
+ <dimen name="promo_card_action_end_padding">4dp</dimen>
+ <dimen name="promo_card_action_between_padding">11dp</dimen>
+ <dimen name="promo_card_line_spacing">4dp</dimen>
+
+ <dimen name="voicemail_playback_top_padding">12dp</dimen>
+
+ <!-- Size of entries in blocked numbers list -->
+ <dimen name="blocked_number_container_padding">16dp</dimen>
+ <dimen name="blocked_number_horizontal_margin">16dp</dimen>
+ <dimen name="blocked_number_top_margin">16dp</dimen>
+ <dimen name="blocked_number_bottom_margin">16dp</dimen>
+ <dimen name="blocked_number_add_top_margin">8dp</dimen>
+ <dimen name="blocked_number_add_bottom_margin">8dp</dimen>
+ <dimen name="blocked_number_primary_text_size">16sp</dimen>
+ <dimen name="blocked_number_secondary_text_size">12sp</dimen>
+ <dimen name="blocked_number_delete_icon_size">32dp</dimen>
+ <dimen name="blocked_number_search_text_size">14sp</dimen>
+ <dimen name="blocked_number_settings_description_text_size">14sp</dimen>
+ <dimen name="blocked_number_header_height">48dp</dimen>
+
+ <dimen name="call_type_icon_size">12dp</dimen>
+</resources>
diff --git a/java/com/android/dialer/app/res/values/donottranslate_config.xml b/java/com/android/dialer/app/res/values/donottranslate_config.xml
new file mode 100644
index 000000000..e7a8e6fc3
--- /dev/null
+++ b/java/com/android/dialer/app/res/values/donottranslate_config.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+
+ <!-- If true, enable vibration (haptic feedback) for dialer key presses.
+ The pattern is set on a per-platform basis using config_virtualKeyVibePattern.
+ TODO: If enough users are annoyed by this, we might eventually
+ need to make it a user preference rather than a per-platform
+ resource. -->
+ <bool name="config_enable_dialer_key_vibration">true</bool>
+
+ <!-- If true, show an onscreen "Dial" button in the dialer.
+ In practice this is used on all platforms even the ones with hard SEND/END
+ keys, but for maximum flexibility it's controlled by a flag here
+ (which can be overridden on a per-product basis.) -->
+ <bool name="config_show_onscreen_dial_button">true</bool>
+
+ <!-- Regular expression for prohibiting certain phone numbers in dialpad.
+ Ignored if empty. -->
+ <string name="config_prohibited_phone_number_regexp"></string>
+
+</resources>
diff --git a/java/com/android/dialer/app/res/values/ids.xml b/java/com/android/dialer/app/res/values/ids.xml
new file mode 100644
index 000000000..8566f26b6
--- /dev/null
+++ b/java/com/android/dialer/app/res/values/ids.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <item name="call_detail_delete_menu_item" type="id"/>
+ <item name="context_menu_copy_to_clipboard" type="id"/>
+ <item name="context_menu_copy_transcript_to_clipboard" type="id"/>
+ <item name="context_menu_edit_before_call" type="id"/>
+ <item name="context_menu_block_report_spam" type="id"/>
+ <item name="context_menu_block" type="id"/>
+ <item name="context_menu_unblock" type="id"/>
+ <item name="context_menu_report_not_spam" type="id"/>
+ <item name="settings_header_sounds_and_vibration" type="id"/>
+ <item name="block_id" type="id"/>
+</resources>
diff --git a/java/com/android/dialer/app/res/values/strings.xml b/java/com/android/dialer/app/res/values/strings.xml
new file mode 100644
index 000000000..689ee1ba8
--- /dev/null
+++ b/java/com/android/dialer/app/res/values/strings.xml
@@ -0,0 +1,960 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Application name used in Settings/Apps. Default label for activities
+ that don't specify a label. -->
+ <string name="applicationLabel">Phone</string>
+
+ <!-- Title for the activity that dials the phone, when launched directly into the dialpad -->
+ <string name="launcherDialpadActivityLabel">Phone Keypad</string>
+ <!-- The description text for the dialer tab.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+
+ [CHAR LIMIT=NONE] -->
+ <string name="dialerIconLabel">Phone</string>
+
+ <!-- The description text for the call log tab.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+
+ [CHAR LIMIT=NONE] -->
+ <string name="callHistoryIconLabel">Call history</string>
+
+ <!-- Text for a menu item to report a call as having been incorrectly identified. [CHAR LIMIT=48] -->
+ <string name="action_report_number">Report inaccurate number</string>
+
+ <!-- Option displayed in context menu to copy long pressed phone number. [CHAR LIMIT=48] -->
+ <string name="action_copy_number_text">Copy number</string>
+
+ <!-- Option displayed in context menu to copy long pressed voicemail transcription. [CHAR LIMIT=48] -->
+ <string name="copy_transcript_text">Copy transcription</string>
+
+ <!-- Label for action to block a number. [CHAR LIMIT=48] -->
+ <string name="action_block_number">Block number</string>
+
+ <!-- Label for action to unblock a number [CHAR LIMIT=48]-->
+ <string name="action_unblock_number">Unblock number</string>
+
+ <!-- Menu item in call details used to remove a call or voicemail from the call log. -->
+ <string name="call_details_delete">Delete</string>
+
+ <!-- Label for action to edit a number before calling it. [CHAR LIMIT=48] -->
+ <string name="action_edit_number_before_call">Edit number before call</string>
+
+ <!-- Menu item used to remove all calls from the call log -->
+ <string name="call_log_delete_all">Clear call history</string>
+
+ <!-- Menu item used to delete a voicemail. [CHAR LIMIT=30] -->
+ <string name="call_log_trash_voicemail">Delete voicemail</string>
+
+ <!-- Text for snackbar to undo a voicemail delete. [CHAR LIMIT=30] -->
+ <string name="snackbar_voicemail_deleted">Voicemail deleted</string>
+
+ <!-- Text for undo button in snackbar for voicemail deletion. [CHAR LIMIT=10] -->
+ <string name="snackbar_voicemail_deleted_undo">UNDO</string>
+
+ <!-- Title of the confirmation dialog for clearing the call log. [CHAR LIMIT=37] -->
+ <string name="clearCallLogConfirmation_title">Clear call history?</string>
+
+ <!-- Confirmation dialog for clearing the call log. [CHAR LIMIT=NONE] -->
+ <string name="clearCallLogConfirmation">This will delete all calls from your history</string>
+
+ <!-- Title of the "Clearing call log" progress-dialog [CHAR LIMIT=35] -->
+ <string name="clearCallLogProgress_title">Clearing call history\u2026</string>
+
+ <!-- Title used for the activity for placing a call. This name appears
+ in activity disambig dialogs -->
+ <string name="userCallActivityLabel" product="default">Phone</string>
+
+ <!-- Notification strings -->
+ <!-- Missed call notification label, used when there's exactly one missed call -->
+ <string name="notification_missedCallTitle">Missed call</string>
+ <!-- Missed call notification label, used when there's exactly one missed call from work contact -->
+ <string name="notification_missedWorkCallTitle">Missed work call</string>
+ <!-- Missed call notification label, used when there are two or more missed calls -->
+ <string name="notification_missedCallsTitle">Missed calls</string>
+ <!-- Missed call notification message used when there are multiple missed calls -->
+ <string name="notification_missedCallsMsg"><xliff:g id="num_missed_calls">%s</xliff:g> missed calls</string>
+ <!-- Message for "call back" Action, which is displayed in the missed call notificaiton.
+ The user will be able to call back to the person or the phone number.
+ [CHAR LIMIT=18] -->
+ <string name="notification_missedCall_call_back">Call back</string>
+ <!-- Message for "reply via sms" action, which is displayed in the missed call notification.
+ The user will be able to send text messages using the phone number.
+ [CHAR LIMIT=18] -->
+ <string name="notification_missedCall_message">Message</string>
+ <!-- Hardcoded number used for restricted incoming phone numbers. -->
+ <string name="handle_restricted" translatable="false">RESTRICTED</string>
+ <!-- Format for a post call message. (ex. John Doe: Give me a call when you're free.) -->
+ <string name="post_call_notification_message"><xliff:g id="name">%1$s</xliff:g>: <xliff:g id="message">%2$s</xliff:g></string>
+
+ <!-- Title of the notification of new voicemails. [CHAR LIMIT=30] -->
+ <plurals name="notification_voicemail_title">
+ <item quantity="one">Voicemail</item>
+ <item quantity="other">
+ <xliff:g id="count">%1$d</xliff:g>
+ Voicemails
+ </item>
+ </plurals>
+
+ <!-- Used in the notification of a new voicemail for the action to play the voicemail. -->
+ <string name="notification_action_voicemail_play">Play</string>
+
+ <!-- Used to build a list of names or phone numbers, to indicate the callers who left
+ voicemails.
+ The first argument may be one or more callers, the most recent ones.
+ The second argument is an additional callers.
+ This string is used to build a list of callers.
+
+ [CHAR LIMIT=10]
+ -->
+ <string name="notification_voicemail_callers_list"><xliff:g id="newer_callers">%1$s</xliff:g>,
+ <xliff:g id="older_caller">%2$s</xliff:g>
+ </string>
+
+ <!-- Text used in the ticker to notify the user of the latest voicemail. [CHAR LIMIT=30] -->
+ <string name="notification_new_voicemail_ticker">New voicemail from
+ <xliff:g id="caller">%1$s</xliff:g>
+ </string>
+
+ <!-- Message to show when there is an error playing back the voicemail. [CHAR LIMIT=40] -->
+ <string name="voicemail_playback_error">Couldn\'t play voicemail</string>
+
+ <!-- Message to display whilst we are waiting for the content to be fetched. [CHAR LIMIT=40] -->
+ <string name="voicemail_fetching_content">Loading voicemail\u2026</string>
+
+ <!-- Message to display whilst we are waiting for the content to be archived. [CHAR LIMIT=40] -->
+ <string name="voicemail_archiving_content">Archiving voicemail\u2026</string>
+
+ <!-- Message to display if we fail to get content within a suitable time period. [CHAR LIMIT=40] -->
+ <string name="voicemail_fetching_timout">Couldn\'t load voicemail</string>
+
+ <!-- The header to show that call log is only showing voicemail calls. [CHAR LIMIT=40] -->
+ <string name="call_log_voicemail_header">Calls with voicemail only</string>
+
+ <!-- The header to show that call log is only showing incoming calls. [CHAR LIMIT=40] -->
+ <string name="call_log_incoming_header">Incoming calls only</string>
+
+ <!-- The header to show that call log is only showing outgoing calls. [CHAR LIMIT=40] -->
+ <string name="call_log_outgoing_header">Outgoing calls only</string>
+
+ <!-- The header to show that call log is only showing missed calls. [CHAR LIMIT=40] -->
+ <string name="call_log_missed_header">Missed calls only</string>
+
+ <!-- The counter for calls in a group and the date of the latest call as shown in the call log [CHAR LIMIT=15] -->
+ <string name="call_log_item_count_and_date">(<xliff:g id="count">%1$d</xliff:g>)
+ <xliff:g id="date">%2$s</xliff:g>
+ </string>
+
+ <!-- String describing the Search ImageButton
+
+ Used by AccessibilityService to announce the purpose of the button.
+ [CHAR LIMIT=NONE]
+ -->
+ <string name="description_search_button">search</string>
+
+ <!-- String describing the Dial ImageButton
+
+ Used by AccessibilityService to announce the purpose of the button.
+ -->
+ <string name="description_dial_button">dial</string>
+
+ <!-- String describing the digits text box containing the number to dial.
+
+ Used by AccessibilityService to announce the purpose of the view.
+ -->
+ <string name="description_digits_edittext">number to dial</string>
+
+ <!-- String describing the button in the voicemail playback to start/stop playback.
+
+ Used by AccessibilityService to announce the purpose of the view.
+ -->
+ <string name="description_playback_start_stop">Play or stop playback</string>
+
+ <!-- String describing the button in the voicemail playback to switch on/off speakerphone.
+
+ Used by AccessibilityService to announce the purpose of the view.
+ -->
+ <string name="description_playback_speakerphone">Switch on or off speakerphone</string>
+
+ <!-- String describing the seekbar in the voicemail playback to seek playback position.
+
+ Used by AccessibilityService to announce the purpose of the view.
+ -->
+ <string name="description_playback_seek">Seek playback position</string>
+
+ <!-- String describing the button in the voicemail playback to decrease playback rate.
+
+ Used by AccessibilityService to announce the purpose of the view.
+ -->
+ <string name="description_rate_decrease">Decrease playback rate</string>
+
+ <!-- String describing the button in the voicemail playback to increase playback rate.
+
+ Used by AccessibilityService to announce the purpose of the view.
+ -->
+ <string name="description_rate_increase">Increase playback rate</string>
+
+ <!-- Content description for the fake action menu button that brings up the call history
+ activity -->
+ <string name="action_menu_call_history_description">Call history</string>
+
+ <!-- Content description for the fake action menu overflow button.
+ This should be same as the description for the real action menu
+ overflow button available in ActionBar.
+ [CHAR LIMIT=NONE] -->
+ <string msgid="2295659037509008453" name="action_menu_overflow_description">More options</string>
+
+ <!-- Content description for the button that displays the dialpad
+ [CHAR LIMIT=NONE] -->
+ <string name="action_menu_dialpad_button">key pad</string>
+
+ <!-- Menu item used to show only outgoing in the call log. [CHAR LIMIT=30] -->
+ <string name="menu_show_outgoing_only">Show outgoing only</string>
+
+ <!-- Menu item used to show only incoming in the call log. [CHAR LIMIT=30] -->
+ <string name="menu_show_incoming_only">Show incoming only</string>
+
+ <!-- Menu item used to show only missed in the call log. [CHAR LIMIT=30] -->
+ <string name="menu_show_missed_only">Show missed only</string>
+
+ <!-- Menu item used to show only voicemails in the call log. [CHAR LIMIT=30] -->
+ <string name="menu_show_voicemails_only">Show voicemails only</string>
+
+ <!-- Menu item used to show all calls in the call log. [CHAR LIMIT=30] -->
+ <string name="menu_show_all_calls">Show all calls</string>
+
+ <!-- Menu items for dialpad options as part of Pause and Wait ftr [CHAR LIMIT=30] -->
+ <string name="add_2sec_pause">Add 2-sec pause</string>
+ <string name="add_wait">Add wait</string>
+
+ <!-- Label for the dialer app setting page [CHAR LIMIT=30]-->
+ <string name="dialer_settings_label">Settings</string>
+
+ <!-- Menu item to display all contacts [CHAR LIMIT=30] -->
+ <string name="menu_allContacts">All contacts</string>
+
+ <!-- Title bar for call detail screen -->
+ <string name="callDetailTitle">Call details</string>
+
+ <!-- Toast for call detail screen when couldn't read the requested details -->
+ <string name="toast_call_detail_error">Details not available</string>
+
+ <!-- Item label: jump to the in-call DTMF dialpad.
+ (Part of a list of options shown in the dialer when another call
+ is already in progress.) -->
+ <string name="dialer_useDtmfDialpad">Use touch tone keypad</string>
+
+ <!-- Item label: jump to the in-call UI.
+ (Part of a list of options shown in the dialer when another call
+ is already in progress.) -->
+ <string name="dialer_returnToInCallScreen">Return to call in progress</string>
+
+ <!-- Item label: use the Dialer's keypad to add another call.
+ (Part of a list of options shown in the dialer when another call
+ is already in progress.) -->
+ <string name="dialer_addAnotherCall">Add call</string>
+
+ <!-- Title for incoming call type. [CHAR LIMIT=40] -->
+ <string name="type_incoming">Incoming call</string>
+
+ <!-- Title for incoming call which was transferred to another device. [CHAR LIMIT=60] -->
+ <string name="type_incoming_pulled">Incoming call transferred to another device</string>
+
+ <!-- Title for outgoing call type. [CHAR LIMIT=40] -->
+ <string name="type_outgoing">Outgoing call</string>
+
+ <!-- Title for outgoing call which was transferred to another device. [CHAR LIMIT=60] -->
+ <string name="type_outgoing_pulled">Outgoing call transferred to another device</string>
+
+ <!-- Title for missed call type. [CHAR LIMIT=40] -->
+ <string name="type_missed">Missed call</string>
+
+ <!-- Title for incoming video call in call details screen [CHAR LIMIT=60] -->
+ <string name="type_incoming_video">Incoming video call</string>
+
+ <!-- Title for incoming video call in call details screen which was transferred to another device.
+ [CHAR LIMIT=60] -->
+ <string name="type_incoming_video_pulled">Incoming video call transferred to another device</string>
+
+ <!-- Title for outgoing video call in call details screen [CHAR LIMIT=60] -->
+ <string name="type_outgoing_video">Outgoing video call</string>
+
+ <!-- Title for outgoing video call in call details screen which was transferred to another device.
+ [CHAR LIMIT=60] -->
+ <string name="type_outgoing_video_pulled">Outgoing video call transferred to another device</string>
+
+ <!-- Title for missed video call in call details screen [CHAR LIMIT=60] -->
+ <string name="type_missed_video">Missed video call</string>
+
+ <!-- Title for voicemail details screen -->
+ <string name="type_voicemail">Voicemail</string>
+
+ <!-- Title for rejected call type. [CHAR LIMIT=40] -->
+ <string name="type_rejected">Declined call</string>
+
+ <!-- Title for blocked call type. [CHAR LIMIT=40] -->
+ <string name="type_blocked">Blocked call</string>
+
+ <!-- Title for "answered elsewhere" call type. This will happen if a call was ringing
+ simultaneously on multiple devices, and the user answered it on a device other than the
+ current device. [CHAR LIMIT=60] -->
+ <string name="type_answered_elsewhere">Call answered on another device</string>
+
+ <!-- Description for incoming calls going to voice mail vs. not -->
+ <string name="actionIncomingCall">Incoming calls</string>
+
+ <!-- String describing the icon in the call log used to play a voicemail.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_call_log_play_button">Play voicemail</string>
+
+ <!-- String describing the button to view the contact for the current number.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_view_contact">View contact <xliff:g id="name">%1$s</xliff:g></string>
+
+ <!-- String describing the button to call a number or contact.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_call">Call <xliff:g id="name">%1$s</xliff:g></string>
+
+ <!-- String describing the button to access the contact details for a name or number.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_contact_details">Contact details for <xliff:g id="nameOrNumber">%1$s</xliff:g></string>
+
+ <!-- String describing the button to access the contact details for a name or number when the
+ when the number is a suspected spam.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_spam_contact_details">Contact details for suspected spam caller <xliff:g id="nameOrNumber">%1$s</xliff:g></string>
+
+ <!-- String indicating the number of calls to/from a caller in the call log.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_num_calls"><xliff:g id="numberOfCalls">%1$s</xliff:g> calls.</string>
+
+ <!-- String indicating a call log entry had video capabilities.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ [CHAR LIMIT=NONE]
+ -->
+ <string name="description_video_call">Video call.</string>
+
+ <!-- String describing the button to SMS a number or contact.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ [CHAR LIMIT=NONE]
+ -->
+ <string name="description_send_text_message">Send SMS to <xliff:g id="name">%1$s</xliff:g></string>
+
+ <!-- String describing the icon in the call log used to represent an unheard voicemail left to
+ the user.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ [CHAR LIMIT=NONE]
+ -->
+ <string name="description_call_log_unheard_voicemail">Unheard voicemail</string>
+
+ <!-- String describing the icon used to start a voice search -->
+ <string name="description_start_voice_search">Start voice search</string>
+
+ <!-- Menu item used to call a contact, containing the number of the contact to call -->
+ <string name="menu_callNumber">Call <xliff:g id="number">%s</xliff:g></string>
+
+ <!-- String used for displaying calls to the voicemail number in the call log -->
+ <string name="voicemail">Voicemail</string>
+
+ <!-- A nicely formatted call duration displayed when viewing call details for duration less than 1 minute. For example "28 sec" -->
+ <string name="callDetailsShortDurationFormat"><xliff:g example="28" id="seconds">%s</xliff:g> sec</string>
+
+ <!-- A nicely formatted call duration displayed when viewing call details. For example "42 min 28 sec" -->
+ <string name="callDetailsDurationFormat"><xliff:g example="42" id="minutes">%s</xliff:g> min <xliff:g example="28" id="seconds">%s</xliff:g> sec</string>
+
+ <!-- The string 'Today'. This value is used in the voicemailCallLogDateTimeFormat rather than an
+ explicit date string, e.g. Jul 25, 2014, in the event that a voicemail was created on the
+ current day -->
+ <string name="voicemailCallLogToday">@string/call_log_header_today</string>
+
+ <!-- A format string used for displaying the date and time for a voicemail call log. For example: Jul 25, 2014 at 2:49 PM
+ The date will be replaced by 'Today' for voicemails created on the current day. For example: Today at 2:49 PM -->
+ <string name="voicemailCallLogDateTimeFormat"><xliff:g example="Jul 25, 2014" id="date">%1$s</xliff:g> at <xliff:g example="2:49 PM" id="time">%2$s</xliff:g></string>
+
+ <!-- Format for duration of voicemails which are displayed when viewing voicemail logs. For example "01:22" -->
+ <string name="voicemailDurationFormat"><xliff:g example="10" id="minutes">%1$02d</xliff:g>:<xliff:g example="20" id="seconds">%2$02d</xliff:g></string>
+
+ <!-- A format string used for displaying the date, time and duration for a voicemail call log. For example: Jul 25, 2014 at 2:49 PM • 00:34 -->
+ <string name="voicemailCallLogDateTimeFormatWithDuration"><xliff:g example="Jul 25, 2014 at 2:49PM" id="dateAndTime">%1$s</xliff:g> \u2022 <xliff:g example="01:22" id="duration">%2$s</xliff:g></string>
+
+ <!-- Dialog message which is shown when the user tries to make a phone call
+ to prohibited phone numbers [CHAR LIMIT=NONE] -->
+ <string msgid="4313552620858880999" name="dialog_phone_call_prohibited_message">Can\'t call this number</string>
+
+ <!-- Dialog message which is shown when the user tries to check voicemail
+ while the system isn't ready for the access. [CHAR LIMIT=NONE] -->
+ <string name="dialog_voicemail_not_ready_message">To set up voicemail, go to Menu &gt; Settings.</string>
+
+ <!-- Dialog message which is shown when the user tries to check voicemail
+ while the system is in airplane mode. The user cannot access to
+ voicemail service in Airplane mode. [CHAR LIMI=NONE] -->
+ <string name="dialog_voicemail_airplane_mode_message">To call voicemail, first turn off Airplane mode.</string>
+
+ <!-- Message that appears in the favorites tab of the Phone app when the contact list has not fully loaded yet (below the favorite and frequent contacts) [CHAR LIMIT=20] -->
+ <string name="contact_list_loading">Loading\u2026</string>
+
+ <!-- The title of a dialog that displays the IMEI of the phone -->
+ <string name="imei">IMEI</string>
+
+ <!-- The title of a dialog that displays the MEID of the CDMA phone -->
+ <string name="meid">MEID</string>
+
+ <!-- Dialog text displayed when loading a phone number from the SIM card for speed dial -->
+ <string name="simContacts_emptyLoading">Loading from SIM card\u2026</string>
+
+ <!-- Dialog title displayed when loading a phone number from the SIM card for speed dial -->
+ <string name="simContacts_title">SIM card contacts</string>
+
+ <!-- Message displayed when there is no application available to handle the add contact menu option. [CHAR LIMIT=NONE] -->
+ <string name="add_contact_not_available">No contacts app available</string>
+
+ <!-- Message displayed when there is no application available to handle voice search. [CHAR LIMIT=NONE] -->
+ <string name="voice_search_not_available">Voice search not available</string>
+
+ <!-- Message displayed when the Phone application has been disabled and a phone call cannot
+ be made. [CHAR LIMIT=NONE] -->
+ <string name="call_not_available">Cannot make a phone call because the Phone application has been disabled.</string>
+
+ <!-- Hint displayed in dialer search box when there is no query that is currently typed.
+ [CHAR LIMIT=30] -->
+ <string name="dialer_hint_find_contact">Search contacts</string>
+
+ <!-- Hint displayed in add blocked number search box when there is no query typed.
+ [CHAR LIMIT=45] -->
+ <string name="block_number_search_hint">Add number or search contacts</string>
+
+ <!-- String resource for the font-family to use for the call log activity's title -->
+ <string name="call_log_activity_title_font_family" translatable="false">sans-serif-light</string>
+
+ <!-- String resource for the font-family to use for the full call history footer -->
+ <string name="view_full_call_history_font_family" translatable="false">sans-serif</string>
+
+ <!-- Text displayed when the call log is empty. -->
+ <string name="call_log_all_empty">Your call history is empty</string>
+
+ <!-- Label of the button displayed when the call history is empty. Allows the user to make a call. -->
+ <string name="call_log_all_empty_action">Make a call</string>
+
+ <!-- Text displayed when the list of missed calls is empty -->
+ <string name="call_log_missed_empty">You have no missed calls.</string>
+
+ <!-- Text displayed when the list of voicemails is empty -->
+ <string name="call_log_voicemail_empty">Your voicemail inbox is empty.</string>
+
+ <!-- Menu option to show favorite contacts only -->
+ <string name="show_favorites_only">Show favorites only</string>
+
+ <!-- Title of activity that displays a list of all calls -->
+ <string name="call_log_activity_title">Call History</string>
+
+ <!-- Title for the call log tab containing the list of all voicemails and calls
+ [CHAR LIMIT=30] -->
+ <string name="call_log_all_title">All</string>
+
+ <!-- Title for the call log tab containing the list of all missed calls only
+ [CHAR LIMIT=30] -->
+ <string name="call_log_missed_title">Missed</string>
+
+ <!-- Title for the call log tab containing the list of all voicemail calls only
+ [CHAR LIMIT=30] -->
+ <string name="call_log_voicemail_title">Voicemail</string>
+
+ <!-- Accessibility text for the tab showing recent and favorite contacts who can be called.
+ [CHAR LIMIT=40] -->
+ <string name="tab_speed_dial">Speed dial</string>
+
+ <!-- Accessibility text for the tab showing the call history. [CHAR LIMIT=40] -->
+ <string name="tab_history">Call History</string>
+
+ <!-- Accessibility text for the tab showing the user's contacts. [CHAR LIMIT=40] -->
+ <string name="tab_all_contacts">Contacts</string>
+
+ <!-- Accessibility text for the tab showing the user's voicemails. [CHAR LIMIT=40] -->
+ <string name="tab_voicemail">Voicemail</string>
+
+ <!-- Text displayed when user swipes out a favorite contact -->
+ <string name="favorite_hidden">Removed from favorites</string>
+ <!-- Text displayed for the undo button to undo removing a favorite contact -->
+ <string name="favorite_hidden_undo">Undo</string>
+
+ <!-- Shortcut item used to call a number directly from search -->
+ <string name="search_shortcut_call_number">Call
+ <xliff:g id="number">%s</xliff:g>
+ </string>
+
+ <!-- Shortcut item used to add a number directly to a new contact from search.
+ [CHAR LIMIT=25] -->
+ <string name="search_shortcut_create_new_contact">Create new contact</string>
+
+ <!-- Shortcut item used to add a number to an existing contact directly from search.
+ [CHAR LIMIT=25] -->
+ <string name="search_shortcut_add_to_contact">Add to a contact</string>
+
+ <!-- Shortcut item used to send a text message directly from search. [CHAR LIMIT=25] -->
+ <string name="search_shortcut_send_sms_message">Send SMS</string>
+
+ <!-- Shortcut item used to make a video call directly from search. [CHAR LIMIT=25] -->
+ <string name="search_shortcut_make_video_call">Make video call</string>
+
+ <!-- Shortcut item used to block a number directly from search. [CHAR LIMIT=25] -->
+ <string name="search_shortcut_block_number">Block number</string>
+
+ <!-- Number of missed calls shown on call card [CHAR LIMIT=40] -->
+ <string name="num_missed_calls"><xliff:g id="number">%s</xliff:g> new missed calls</string>
+
+ <!-- Shown when there are no speed dial favorites. -->
+ <string name="speed_dial_empty">No one is on your speed dial yet</string>
+
+ <!-- Shown as an action when there are no speed dial favorites -->
+ <string name="speed_dial_empty_add_favorite_action">Add a favorite</string>
+
+ <!-- Shown when there are no contacts in the all contacts list. -->
+ <string name="all_contacts_empty">You don\'t have any contacts yet</string>
+
+ <!-- Shown as an action when the all contacts list is empty -->
+ <string name="all_contacts_empty_add_contact_action">Add a contact</string>
+
+ <!-- Shows up as a tooltip to provide a hint to the user that the profile pic in a contact
+ card can be tapped to bring up a list of all numbers, or long pressed to start reordering
+ [CHAR LIMIT=NONE]
+ -->
+ <string name="contact_tooltip">Touch image to see all numbers or touch &amp; hold to reorder</string>
+
+ <!-- Remove button that shows up when contact is long-pressed. [CHAR LIMIT=NONE] -->
+ <string name="remove_contact">Remove</string>
+
+ <!-- Button text for the "video call" displayed underneath an entry in the call log.
+ Tapping causes a video call to be placed to the caller represented by the call log entry.
+ [CHAR LIMIT=30] -->
+ <string name="call_log_action_video_call">Video call</string>
+
+ <!-- Button text for a button displayed underneath an entry in the call log, which opens up a
+ messaging app to send a SMS to the number represented by the call log entry.
+ [CHAR LIMIT=30] -->
+ <string name="call_log_action_send_message">Send a message</string>
+
+ <!-- Button text for a button displayed underneath an entry in the call log, which opens up the
+ call compose UI for the number represented by the call log entry.
+ [CHAR LIMIT=30] -->
+ <string name="share_and_call">Share and call</string>
+
+ <!-- Button text for the button displayed underneath an entry in the call log.
+ Tapping navigates the user to the call details screen where the user can view details for
+ the call log entry. [CHAR LIMIT=30] -->
+ <string name="call_log_action_details">Call details</string>
+
+ <!-- Button text for the button displayed underneath an entry in the call log.
+ Tapping opens dialog to share voicemail archive with other apps. [CHAR LIMIT=30] -->
+ <string name="call_log_action_share_voicemail">Send to &#8230;</string>
+
+ <!-- Button text for the button displayed underneath an entry in the call log, which when
+ tapped triggers a return call to the named user. [CHAR LIMIT=30] -->
+ <string name="call_log_action_call">
+ Call <xliff:g example="John Smith" id="nameOrNumber">^1</xliff:g>
+ </string>
+
+ <!-- String describing an incoming missed call entry in the call log.
+ Note: AccessibilityServices uses this attribute to announce what the view represents.
+ [CHAR LIMIT=NONE] -->
+ <string name="description_incoming_missed_call">Missed call from <xliff:g example="John Smith" id="nameOrNumber">^1</xliff:g>, <xliff:g example="Mobile" id="typeOrLocation">^2</xliff:g>, <xliff:g example="2 min ago" id="timeOfCall">^3</xliff:g>, <xliff:g example="on SIM 1" id="phoneAccount">^4</xliff:g>.</string>
+
+ <!-- String describing an incoming answered call entry in the call log.
+ Note: AccessibilityServices uses this attribute to announce what the view represents.
+ [CHAR LIMIT=NONE] -->
+ <string name="description_incoming_answered_call">Answered call from <xliff:g example="John Smith" id="nameOrNumber">^1</xliff:g>, <xliff:g example="Mobile" id="typeOrLocation">^2</xliff:g>, <xliff:g example="2 min ago" id="timeOfCall">^3</xliff:g>, <xliff:g example="on SIM 1" id="phoneAccount">^4</xliff:g>.</string>
+
+ <!-- String describing an "unread" voicemail entry in the voicemails tab.
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ [CHAR LIMIT=NONE] -->
+ <string name="description_unread_voicemail">Unread voicemail from <xliff:g example="John Smith" id="nameOrNumber">^1</xliff:g>, <xliff:g example="Mobile" id="typeOrLocation">^2</xliff:g>, <xliff:g example="2 min ago" id="timeOfCall">^3</xliff:g>, <xliff:g example="on SIM 1" id="phoneAccount">^4</xliff:g>.</string>
+
+ <!-- String describing a "read" voicemail entry in the voicemails tab.
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ [CHAR LIMIT=NONE] -->
+ <string name="description_read_voicemail">Voicemail from <xliff:g example="John Smith" id="nameOrNumber">^1</xliff:g>, <xliff:g example="Mobile" id="typeOrLocation">^2</xliff:g>, <xliff:g example="2 min ago" id="timeOfCall">^3</xliff:g>, <xliff:g example="on SIM 1" id="phoneAccount">^4</xliff:g>.</string>
+
+ <!-- String describing an outgoing call entry in the call log.
+ Note: AccessibilityServices uses this attribute to announce what the view represents.
+ [CHAR LIMIT=NONE] -->
+ <string name="description_outgoing_call">Call to <xliff:g example="John Smith" id="nameOrNumber">^1</xliff:g>, <xliff:g example="Mobile" id="typeOrLocation">^2</xliff:g>, <xliff:g example="2 min ago" id="timeOfCall">^3</xliff:g>, <xliff:g example="on SIM 1" id="phoneAccount">^4</xliff:g>.</string>
+
+ <!-- String describing the phone account the call was made on or to. This string will be used
+ in description_incoming_missed_call, description_incoming_answered_call, and
+ description_outgoing_call.
+ Note: AccessibilityServices uses this attribute to announce what the view represents.
+ [CHAR LIMIT=NONE] -->
+ <string name="description_phone_account">on <xliff:g example="SIM 1" id="phoneAccount">^1</xliff:g></string>
+
+ <!-- String describing the secondary line number the call was received via.
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ [CHAR LIMIT=NONE]-->
+ <string name="description_via_number">via <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g></string>
+
+ <!-- TextView text item showing the secondary line number the call was received via.
+ [CHAR LIMIT=NONE]-->
+ <string name="call_log_via_number">via <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g></string>
+
+ <!-- String describing the PhoneAccount and via number that a call was received on, if both are
+ visible.
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ [CHAR LIMIT=NONE]-->
+ <string name="description_via_number_phone_account">on <xliff:g example="SIM 1" id="phoneAccount">%1$s</xliff:g>, via <xliff:g example="(555) 555-5555" id="number">%2$s</xliff:g></string>
+
+ <!-- The order of the PhoneAccount and via number that a call was received on,
+ if both are visible.
+ [CHAR LIMIT=NONE]-->
+ <string name="call_log_via_number_phone_account"><xliff:g example="SIM 1" id="phoneAccount">%1$s</xliff:g> via <xliff:g example="(555) 555-5555" id="number">%2$s</xliff:g></string>
+
+ <!-- String describing the phone icon on a call log list item. When tapped, it will place a
+ call to the number represented by that call log entry. [CHAR LIMIT=NONE]-->
+ <string name="description_call_log_call_action">Call</string>
+
+ <!-- String describing the "call" action for an entry in the call log. The call back
+ action triggers a return call to the named user.
+ Note: AccessibilityServices uses this attribute to announce the purpose of the button.
+ [CHAR LIMIT=NONE] -->
+ <string name="description_call_action">
+ Call <xliff:g example="John Smith" id="nameOrNumber">^1</xliff:g>
+ </string>
+
+ <!-- String describing the "video call" action for an entry in the call log. The video call
+ action triggers a return video call to the named person/number.
+ Note: AccessibilityServices uses this attribute to announce the purpose of the button.
+ [CHAR LIMIT=NONE] -->
+ <string name="description_video_call_action">
+ Video call <xliff:g example="John Smith" id="nameOrNumber">^1</xliff:g>.
+ </string>
+
+ <!-- String describing the "listen" action for an entry in the call log. The listen
+ action is shown for call log entries representing a voicemail message and this button
+ triggers playing back the voicemail.
+ Note: AccessibilityServices uses this attribute to announce the purpose of the button.
+ [CHAR LIMIT=NONE] -->
+ <string name="description_voicemail_action">
+ Listen to voicemail from <xliff:g example="John Smith" id="nameOrNumber">^1</xliff:g>
+ </string>
+
+ <!-- String describing the "play voicemail" action for an entry in the call log.
+ Note: AccessibilityServices uses this attribute to announce the purpose of the button.
+ [CHAR LIMIT=NONE] -->
+ <string name="description_voicemail_play">
+ Play voicemail from <xliff:g example="John Smith" id="nameOrNumber">^1</xliff:g>
+ </string>
+
+ <!-- String describing the "pause voicemail" action for an entry in the call log.
+ Note: AccessibilityServices uses this attribute to announce the purpose of the button.
+ [CHAR LIMIT=NONE] -->
+ <string name="description_voicemail_pause">
+ Pause voicemail from <xliff:g example="John Smith" id="nameOrNumber">^1</xliff:g>
+ </string>
+
+
+ <!-- String describing the "delete voicemail" action for an entry in the call log.
+ Note: AccessibilityServices uses this attribute to announce the purpose of the button.
+ [CHAR LIMIT=NONE] -->
+ <string name="description_voicemail_delete">
+ Delete voicemail from <xliff:g example="John Smith" id="nameOrNumber">^1</xliff:g>
+ </string>
+
+ <!-- String describing the number of new voicemails, displayed as a number badge on a tab.
+ Note: AccessibilityServices uses this attribute to announce the purpose of the button.
+ [CHAR LIMIT=NONE] -->
+ <plurals name="description_voicemail_unread">
+ <item quantity="one"><xliff:g id="count">%d</xliff:g> new voicemail</item>
+ <item quantity="other"><xliff:g id="count">%d</xliff:g> new voicemails</item>
+ </plurals>
+
+ <!-- Description for the "create new contact" action for an entry in the call log. This action
+ opens a screen for creating a new contact for this name or number. [CHAR LIMIT=NONE] -->
+ <string name="description_create_new_contact_action">
+ Create contact for <xliff:g example="John Smith" id="nameOrNumber">^1</xliff:g>
+ </string>
+
+ <!-- Description for the "add to existing contact" action for an entry in the call log. This
+ action opens a screen for adding this name or number to an existing contact.
+ [CHAR LIMIT=NONE] -->
+ <string name="description_add_to_existing_contact_action">
+ Add <xliff:g example="John Smith" id="nameOrNumber">^1</xliff:g> to existing contact
+ </string>
+
+ <!-- String describing the "details" action for an entry in the call log. The details action
+ displays the call details screen for an entry in the call log. This shows the calls to
+ and from the specified number associated with the call log entry.
+ [CHAR LIMIT=NONE] -->
+ <string name="description_details_action">
+ Call details for <xliff:g example="John Smith" id="nameOrNumber">^1</xliff:g>
+ </string>
+
+ <!-- Toast message which appears when a call log entry is deleted.
+ [CHAR LIMIT=NONE] -->
+ <string name="toast_entry_removed">Deleted from call history</string>
+
+ <!-- String used as a header in the call log above calls which occurred today.
+ [CHAR LIMIT=65] -->
+ <string name="call_log_header_today">Today</string>
+
+ <!-- String used as a header in the call log above calls which occurred yesterday.
+ [CHAR LIMIT=65] -->
+ <string name="call_log_header_yesterday">Yesterday</string>
+
+ <!-- String used as a header in the call log above calls which occurred two days or more ago.
+ [CHAR LIMIT=65] -->
+ <string name="call_log_header_other">Older</string>
+
+ <!-- String a header on the call details screen. Appears above the list calls to or from a
+ particular number.
+ [CHAR LIMIT=65] -->
+ <string name="call_detail_list_header">Calls list</string>
+
+ <!-- String describing the "speaker on" button on the playback control used to listen to a
+ voicemail message. When speaker is on, playback of the voicemail will occur through the
+ phone speaker.
+ Note: AccessibilityServices uses this attribute to announce the purpose of the button.
+ [CHAR LIMIT=NONE] -->
+ <string name="voicemail_speaker_on">Turn speaker on.</string>
+
+ <!-- String describing the "speaker off" button on the playback control used to listen to a
+ voicemail message. When speaker is off, playback of the voicemail will occur through the
+ phone earpiece.
+ Note: AccessibilityServices uses this attribute to announce the purpose of the button.
+ [CHAR LIMIT=NONE] -->
+ <string name="voicemail_speaker_off">Turn speaker off.</string>
+
+ <!-- String describing the "play faster" button in the playback control used to listen to a
+ voicemail message. Speeds up playback of the voicemail message.
+ Note: AccessibilityServices uses this attribute to announce the purpose of the button.
+ [CHAR LIMIT=NONE] -->
+ <string name="voicemail_play_faster">Play faster.</string>
+
+ <!-- String describing the "play slower" button in the playback control used to listen to a
+ voicemail message. Slows down playback of the voicemail message.
+ Note: AccessibilityServices uses this attribute to announce the purpose of the button.
+ [CHAR LIMIT=NONE] -->
+ <string name="voicemail_play_slower">Play slower.</string>
+
+ <!-- String describing the "play/pause" button in the playback control used to listen to a
+ voicemail message. Starts playback or pauses ongoing playback.
+ Note: AccessibilityServices uses this attribute to announce the purpose of the button.
+ [CHAR LIMIT=NONE] -->
+ <string name="voicemail_play_start_pause">Start or pause playback.</string>
+
+ <!-- Dialer settings related strings-->
+
+ <!-- Title for "Display options" category, which controls how contacts are shown.
+ [CHAR LIMIT=40] -->
+ <string name="display_options_title">Display options</string>
+
+ <!-- Title for the "Sounds and vibration" settings control settings related to ringtones,
+ dialpad tones, and vibration for incoming calls. [CHAR LIMIT=40] -->
+ <string name="sounds_and_vibration_title">Sounds and vibration</string>
+
+ <!-- Title for "Accessibility" category, which controls settings such as TTY mode and hearing
+ aid compatability. [CHAR LIMIT=40] -->
+ <string name="accessibility_settings_title">Accessibility</string>
+
+ <!-- Setting option name to pick ringtone (a list dialog comes up). [CHAR LIMIT=30] -->
+ <string name="ringtone_title">Phone ringtone</string>
+
+ <!-- Setting option name to enable or disable vibration when ringing the phone.
+ [CHAR LIMIT=30] -->
+ <string name="vibrate_on_ring_title">"Also vibrate for calls</string>
+
+ <!-- Setting option name to enable or disable DTMF tone sound [CHAR LIMIT=30] -->
+ <string name="dtmf_tone_enable_title">Keypad tones</string>
+ <!-- Label for setting to adjust the length of DTMF tone sounds. [CHAR LIMIT=40] -->
+ <string name="dtmf_tone_length_title">Keypad tone length</string>
+ <!-- Options displayed for the length of DTMF tone sounds. [CHAR LIMIT=40] -->
+ <string-array name="dtmf_tone_length_entries">
+ <item>Normal</item>
+ <item>Long</item>
+ </string-array>
+ <string-array name="dtmf_tone_length_entry_values" translatable="false">
+ <item>0</item>
+ <item>1</item>
+ </string-array>
+
+ <!-- Title of settings screen for managing the "Respond via SMS" feature. [CHAR LIMIT=30] -->
+ <string name="respond_via_sms_setting_title">Quick responses</string>
+
+ <!-- Label for the call settings section [CHAR LIMIT=30] -->
+ <string name="call_settings_label">Calls</string>
+
+ <!-- Label for the blocked numbers settings section [CHAR LIMIT=30] -->
+ <string name="manage_blocked_numbers_label">Call blocking</string>
+
+ <!-- Label for a section describing that call blocking is temporarily disabled because an
+ emergency call was made. [CHAR LIMIT=50] -->
+ <string name="blocked_numbers_disabled_emergency_header_label">
+ Call blocking temporarily off
+ </string>
+
+ <!-- Description that call blocking is temporarily disabled because the user called an
+ emergency number, and explains that call blocking will be re-enabled after a buffer
+ period has passed. [CHAR LIMIT=NONE] -->
+ <string name="blocked_numbers_disabled_emergency_desc">
+ Call blocking has been disabled because you contacted emergency services from this phone
+ within the last 48 hours. It will be automatically reenabled once the 48 hour period
+ expires.
+ </string>
+
+ <!-- Label for fragment to import numbers from contacts marked as send to voicemail.
+ [CHAR_LIMIT=30] -->
+ <string name="import_send_to_voicemail_numbers_label">Import numbers</string>
+
+ <!-- Text informing the user they have previously marked contacts to be sent to voicemail.
+ This will be followed by two buttons, 1) to view who is marked to be sent to voicemail
+ and 2) importing these settings to Dialer's block list. [CHAR LIMIT=NONE] -->
+ <string name="blocked_call_settings_import_description">
+ You previously marked some callers to be automatically sent to voicemail via other apps.
+ </string>
+
+ <!-- Label for button to view numbers of contacts previous marked to be sent to voicemail.
+ [CHAR_LIMIT=20] -->
+ <string name="blocked_call_settings_view_numbers_button">View Numbers</string>
+
+ <!-- Label for button to import settings for sending contacts to voicemail into Dialer's block
+ list. [CHAR_LIMIT=20] -->
+ <string name="blocked_call_settings_import_button">Import</string>
+
+ <!-- String describing the delete icon on a blocked number list item.
+ When tapped, it will show a dialog confirming the unblocking of the number.
+ [CHAR LIMIT=NONE]-->
+ <string name="description_blocked_number_list_delete">Unblock number</string>
+
+ <!-- Button to bring up UI to add a number to the blocked call list. [CHAR LIMIT=40] -->
+ <string name="addBlockedNumber">Add number</string>
+
+ <!-- Footer message of number blocking screen with visual voicemail active.
+ [CHAR LIMIT=NONE] -->
+ <string name="block_number_footer_message_vvm">
+ Calls from these numbers will be blocked and voicemails will be automatically deleted.
+ </string>
+
+ <!-- Footer message of number blocking screen with no visual voicemail.
+ [CHAR LIMIT=NONE] -->
+ <string name="block_number_footer_message_no_vvm">
+ Calls from these numbers will be blocked, but they may still be able to leave you voicemails.
+ </string>
+
+ <!-- Heading for the block list in the "Spam and blocked cal)ls" settings. [CHAR LIMIT=64] -->
+ <string name="block_list">Blocked numbers</string>
+
+ <!-- Error message shown when user tries to add a number to the block list that was already
+ blocked. [CHAR LIMIT=64] -->
+ <string name="alreadyBlocked"><xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g>
+ is already blocked.</string>
+
+ <!-- Label for the phone account settings [CHAR LIMIT=30] -->
+ <string name="phone_account_settings_label">Calling accounts</string>
+
+ <!-- Internal key for ringtone preference. -->
+ <string name="ringtone_preference_key" translatable="false">button_ringtone_key</string>
+ <!-- Internal key for vibrate when ringing preference. -->
+ <string name="vibrate_on_preference_key" translatable="false">button_vibrate_on_ring</string>
+ <!-- Internal key for vibrate when ringing preference. -->
+ <string name="play_dtmf_preference_key" translatable="false">button_play_dtmf_tone</string>
+ <!-- Internal key for DTMF tone length preference. -->
+ <string name="dtmf_tone_length_preference_key" translatable="false">button_dtmf_settings</string>
+
+ <!-- The label of the button used to turn on a single permission [CHAR LIMIT=30]-->
+ <string name="permission_single_turn_on">Turn on</string>
+
+ <!-- The label of the button used to turn on multiple permissions [CHAR LIMIT=30]-->
+ <string name="permission_multiple_turn_on">Set permissions</string>
+
+ <!-- Shown as a prompt to turn on the contacts permission to enable speed dial [CHAR LIMIT=NONE]-->
+ <string name="permission_no_speeddial">To enable speed dial, turn on the Contacts permission.</string>
+
+ <!-- Shown as a prompt to turn on the phone permission to enable the call log [CHAR LIMIT=NONE]-->
+ <string name="permission_no_calllog">To see your call log, turn on the Phone permission.</string>
+
+ <!-- Shown as a prompt to turn on the contacts permission to show all contacts [CHAR LIMIT=NONE]-->
+ <string name="permission_no_contacts">To see your contacts, turn on the Contacts permission.</string>
+
+ <!-- Shown as a prompt to turn on the phone permission to show voicemails [CHAR LIMIT=NONE]-->
+ <string name="permission_no_voicemail">To access your voicemail, turn on the Phone permission.</string>
+
+ <!-- Shown as a prompt to turn on contacts permissions to allow contact search [CHAR LIMIT=NONE]-->
+ <string name="permission_no_search">To search your contacts, turn on the Contacts permissions.</string>
+
+ <!-- Shown as a prompt to turn on the phone permission to allow a call to be placed [CHAR LIMIT=NONE]-->
+ <string name="permission_place_call">To place a call, turn on the Phone permission.</string>
+
+ <!-- Shown as a message that notifies the user that the Phone app cannot write to system settings, which is why the system settings app is being launched directly instead. [CHAR LIMIT=NONE]-->
+ <string name="toast_cannot_write_system_settings">Phone app does not have permission to write to system settings.</string>
+
+ <!-- Label under the name of a blocked number in the call log. [CHAR LIMIT=15] -->
+ <string name="blocked_number_call_log_label">Blocked</string>
+
+ <!-- Button text for a button displayed underneath an entry in the call log, which marks the
+ phone number represented by the call log entry as a Spam number.
+ [CHAR LIMIT=30] -->
+ <string name="call_log_action_block_report_number">Block/report spam</string>
+
+ <!-- Button text for a button displayed underneath an entry in the call log, which marks the
+ phone number represented by the call log entry as a Spam number.
+ [CHAR LIMIT=30] -->
+ <string name="call_log_action_block_number">Block number</string>
+
+ <!-- Button text for a button displayed underneath an entry in the call log, which removes the
+ phone number represented by the call log entry from the Spam numbers list.
+ [CHAR LIMIT=30] -->
+ <string name="call_log_action_remove_spam">Not spam</string>
+
+ <!-- Button text for a button displayed underneath an entry in the call log, which removes the
+ phone number represented by the call log entry from the blacklisted numbers.
+ [CHAR LIMIT=30] -->
+ <string name="call_log_action_unblock_number">Unblock number</string>
+
+ <!-- Label under the name of a spam number in the call log. [CHAR LIMIT=15] -->
+ <string name="spam_number_call_log_label">Spam</string>
+
+ <!-- Shown as a message that notifies the user enriched calling isn't working -->
+ <string name="call_composer_connection_failed"><xliff:g id="feature">%1$s</xliff:g> unavailable right now</string>
+
+</resources>
diff --git a/java/com/android/dialer/app/res/values/styles.xml b/java/com/android/dialer/app/res/values/styles.xml
new file mode 100644
index 000000000..ac4422ba2
--- /dev/null
+++ b/java/com/android/dialer/app/res/values/styles.xml
@@ -0,0 +1,279 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources>
+
+ <style name="DialtactsTheme" parent="DialerThemeBase">
+
+ <!-- Style for the overflow button in the actionbar. -->
+ <item name="android:actionOverflowButtonStyle">@style/DialtactsActionBarOverflow</item>
+ <item name="actionOverflowButtonStyle">@style/DialtactsActionBarOverflow</item>
+
+ <!-- Styles that require AppCompat compatibility, remember to update both sets -->
+ <item name="android:windowActionBarOverlay">true</item>
+ <item name="windowActionBarOverlay">true</item>
+ <item name="android:windowActionModeOverlay">true</item>
+ <item name="windowActionModeOverlay">true</item>
+ <item name="android:actionBarStyle">@style/DialtactsActionBarStyle</item>
+ <item name="actionBarStyle">@style/DialtactsActionBarStyle</item>
+
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:overlapAnchor">true</item>
+ <item name="android:homeAsUpIndicator">@drawable/ic_back_arrow</item>
+
+ <item name="android:listViewStyle">@style/ListViewStyle</item>
+ <item name="activated_background">@drawable/list_item_activated_background</item>
+ <item name="section_header_background">@drawable/list_title_holo</item>
+ <item name="list_section_header_height">32dip</item>
+ <item name="list_item_padding_top">7dp</item>
+ <item name="list_item_padding_right">24dp</item>
+ <item name="list_item_padding_bottom">7dp</item>
+ <item name="list_item_padding_left">16dp</item>
+ <item name="list_item_gap_between_image_and_text">
+ @dimen/contact_browser_list_item_gap_between_image_and_text
+ </item>
+ <item name="list_item_gap_between_label_and_data">8dip</item>
+ <item name="list_item_presence_icon_margin">4dip</item>
+ <item name="list_item_presence_icon_size">16dip</item>
+ <item name="list_item_photo_size">@dimen/contact_browser_list_item_photo_size</item>
+ <item name="list_item_profile_photo_size">70dip</item>
+ <item name="list_item_prefix_highlight_color">@color/people_app_theme_color</item>
+ <item name="list_item_background_color">@color/background_dialer_light</item>
+ <item name="list_item_header_text_indent">8dip</item>
+ <item name="list_item_header_text_color">@color/dialer_secondary_text_color</item>
+ <item name="list_item_header_text_size">14sp</item>
+ <item name="list_item_header_height">30dip</item>
+ <item name="list_item_data_width_weight">5</item>
+ <item name="list_item_label_width_weight">3</item>
+ <item name="contact_browser_list_padding_left">0dp</item>
+ <item name="contact_browser_list_padding_right">0dp</item>
+ <item name="contact_browser_background">@color/background_dialer_results</item>
+ <item name="list_item_name_text_color">@color/contact_list_name_text_color</item>
+ <item name="list_item_name_text_size">16sp</item>
+ <item name="list_item_text_indent">@dimen/contact_browser_list_item_text_indent</item>
+ <item name="list_item_text_offset_top">-2dp</item>
+ <!-- Favorites -->
+ <item name="favorites_padding_bottom">?android:attr/actionBarSize</item>
+ <item name="dialpad_key_button_touch_tint">@color/dialer_dialpad_touch_tint</item>
+ <item name="android:textAppearanceButton">@style/DialerButtonTextStyle</item>
+
+ <!-- Video call icon -->
+ <item name="list_item_video_call_icon_size">32dip</item>
+ <item name="list_item_video_call_icon_margin">8dip</item>
+
+ <item name="dialpad_style">@style/Dialpad.Light</item>
+ </style>
+
+ <!-- Action bar overflow menu icon. -->
+ <style name="DialtactsActionBarOverflow"
+ parent="@android:style/Widget.Material.Light.ActionButton.Overflow">
+ <item name="android:src">@drawable/ic_overflow_menu</item>
+ </style>
+
+ <!-- Action bar overflow menu icon. White with no shadow. -->
+ <style name="DialtactsActionBarOverflowWhite"
+ parent="@android:style/Widget.Material.Light.ActionButton.Overflow">
+ <item name="android:src">@drawable/overflow_menu</item>
+ </style>
+
+ <style name="DialpadTheme" parent="DialtactsTheme">
+ <item name="android:textColorPrimary">#FFFFFF</item>
+ </style>
+
+ <style name="DialtactsThemeWithoutActionBarOverlay" parent="DialtactsTheme">
+ <!-- Styles that require AppCompat compatibility, remember to update both sets -->
+ <item name="android:windowActionBarOverlay">false</item>
+ <item name="windowActionBarOverlay">false</item>
+ <item name="android:actionOverflowButtonStyle">@style/DialtactsActionBarOverflowWhite</item>
+ <item name="actionOverflowButtonStyle">@style/DialtactsActionBarOverflowWhite</item>
+ </style>
+
+ <!-- Hide the actionbar title during the activity preview -->
+ <style name="DialtactsActivityTheme" parent="DialtactsTheme">
+ <!-- Styles that require AppCompat compatibility, remember to update both sets -->
+ <item name="android:actionBarStyle">@style/DialtactsActionBarWithoutTitleStyle</item>
+ <item name="actionBarStyle">@style/DialtactsActionBarWithoutTitleStyle</item>
+
+ <item name="android:fastScrollThumbDrawable">@drawable/fastscroll_thumb</item>
+ <item name="android:fastScrollTrackDrawable">@null</item>
+ </style>
+
+ <style name="CallDetailActivityTheme" parent="DialtactsThemeWithoutActionBarOverlay">
+ <item name="android:windowBackground">@color/background_dialer_results</item>
+ <item name="android:actionOverflowButtonStyle">@style/DialtactsActionBarOverflowWhite</item>
+ </style>
+
+ <style name="CallDetailActionItemStyle">
+ <item name="android:foreground">?android:attr/selectableItemBackground</item>
+ <item name="android:clickable">true</item>
+ <item name="android:drawablePadding">@dimen/call_detail_action_item_drawable_padding</item>
+ <item name="android:gravity">center_vertical</item>
+ <item name="android:paddingStart">@dimen/call_detail_action_item_padding_horizontal</item>
+ <item name="android:paddingEnd">@dimen/call_detail_action_item_padding_horizontal</item>
+ <item name="android:paddingTop">@dimen/call_detail_action_item_padding_vertical</item>
+ <item name="android:paddingBottom">@dimen/call_detail_action_item_padding_vertical</item>
+ <item name="android:textColor">@color/call_detail_footer_text_color</item>
+ <item name="android:textSize">@dimen/call_detail_action_item_text_size</item>
+ </style>
+
+ <style name="DialtactsActionBarStyle" parent="DialerActionBarBaseStyle">
+ <!-- Styles that require AppCompat compatibility, remember to update both sets -->
+ <item name="android:background">@color/actionbar_background_color</item>
+ <item name="background">@color/actionbar_background_color</item>
+ <item name="android:titleTextStyle">@style/DialtactsActionBarTitleText</item>
+ <item name="titleTextStyle">@style/DialtactsActionBarTitleText</item>
+ <item name="android:elevation">@dimen/action_bar_elevation</item>
+ <item name="elevation">@dimen/action_bar_elevation</item>
+ <!-- Empty icon -->
+ <item name="android:icon">@android:color/transparent</item>
+ <item name="icon">@android:color/transparent</item>
+ <!-- Shift the title text to the right -->
+ <item name="android:contentInsetStart">@dimen/actionbar_contentInsetStart</item>
+ <item name="contentInsetStart">@dimen/actionbar_contentInsetStart</item>
+ </style>
+
+ <style name="DialtactsActionBarWithoutTitleStyle" parent="DialtactsActionBarStyle">
+ <!-- Styles that require AppCompat compatibility, remember to update both sets -->
+ <item name="android:displayOptions"></item>
+ <item name="displayOptions"></item>
+ <item name="android:height">@dimen/action_bar_height_large</item>
+ <item name="height">@dimen/action_bar_height_large</item>
+ <!-- Override ActionBar title offset to keep search box aligned left -->
+ <item name="android:contentInsetStart">0dp</item>
+ <item name="contentInsetStart">0dp</item>
+ <item name="android:contentInsetEnd">0dp</item>
+ <item name="contentInsetEnd">0dp</item>
+ </style>
+
+ <!-- Text in the action bar at the top of the screen -->
+ <style name="DialtactsActionBarTitleText"
+ parent="@android:style/TextAppearance.Material.Widget.ActionBar.Title">
+ <item name="android:textColor">@color/actionbar_text_color</item>
+ </style>
+
+ <!-- Text style for tabs. -->
+ <style name="DialtactsActionBarTabTextStyle"
+ parent="android:style/Widget.Material.Light.ActionBar.TabText">
+ <item name="android:textColor">@color/tab_text_color</item>
+ <item name="android:textSize">@dimen/tab_text_size</item>
+ <item name="android:fontFamily">"sans-serif-medium"</item>
+ </style>
+
+ <style name="CallLogActionStyle">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">@dimen/call_log_action_height</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
+ <item name="android:orientation">horizontal</item>
+ <item name="android:gravity">center_vertical</item>
+ </style>
+
+ <style name="CallLogActionTextStyle">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:paddingStart">@dimen/call_log_action_horizontal_padding</item>
+ <item name="android:paddingEnd">@dimen/call_log_action_horizontal_padding</item>
+ <item name="android:textColor">@color/call_log_action_color</item>
+ <item name="android:textSize">@dimen/call_log_primary_text_size</item>
+ <item name="android:fontFamily">"sans-serif"</item>
+ <item name="android:focusable">true</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:importantForAccessibility">no</item>
+ </style>
+
+ <style name="CallLogActionSupportTextStyle" parent="@style/CallLogActionTextStyle">
+ <item name="android:textSize">@dimen/call_log_detail_text_size</item>
+ <item name="android:textColor">@color/call_log_detail_color</item>
+ </style>
+
+ <style name="CallLogActionIconStyle">
+ <item name="android:layout_width">@dimen/call_log_action_icon_dimen</item>
+ <item name="android:layout_height">@dimen/call_log_action_icon_dimen</item>
+ <item name="android:layout_marginStart">@dimen/call_log_action_icon_margin_start</item>
+ <item name="android:tint">?android:textColorSecondary</item>
+ <item name="android:importantForAccessibility">no</item>
+ </style>
+
+ <style name="DismissButtonStyle">
+ <item name="android:paddingLeft">@dimen/dismiss_button_padding_start</item>
+ <item name="android:paddingRight">@dimen/dismiss_button_padding_end</item>
+ </style>
+
+ <!-- Style applied to the "Settings" screen. Keep in sync with SettingsLight in Telephony. -->
+ <style name="SettingsStyle" parent="DialtactsThemeWithoutActionBarOverlay">
+ <!-- Setting text. -->
+ <item name="android:textColorPrimary">@color/settings_text_color_primary</item>
+ <!-- Setting description. -->
+ <item name="android:textColorSecondary">@color/settings_text_color_secondary</item>
+ <item name="android:windowBackground">@color/setting_background_color</item>
+ <item name="android:colorAccent">@color/dialtacts_theme_color</item>
+ <item name="android:textColorLink">@color/dialtacts_theme_color</item>
+ </style>
+
+ <style name="ManageBlockedNumbersStyle" parent="SettingsStyle">
+ <!-- Styles that require AppCompat compatibility, remember to update both sets -->
+ <item name="android:windowActionBarOverlay">true</item>
+ <item name="windowActionBarOverlay">true</item>
+ <item name="android:actionBarStyle">@style/ManageBlockedNumbersActionBarStyle</item>
+ <item name="actionBarStyle">@style/ManageBlockedNumbersActionBarStyle</item>
+ <item name="android:fastScrollTrackDrawable">@null</item>
+ </style>
+
+ <style name="ManageBlockedNumbersActionBarStyle" parent="DialtactsActionBarWithoutTitleStyle">
+ <!-- Styles that require AppCompat compatibility, remember to update both sets -->
+ <item name="android:height">@dimen/action_bar_height</item>
+ <item name="height">@dimen/action_bar_height</item>
+ </style>
+
+ <style name="VoicemailPlaybackLayoutTextStyle">
+ <item name="android:textSize">14sp</item>
+ </style>
+
+ <style name="VoicemailPlaybackLayoutButtonStyle">
+ <item name="android:layout_width">56dp</item>
+ <item name="android:layout_height">56dp</item>
+ <item name="android:background">@drawable/oval_ripple</item>
+ <item name="android:padding">8dp</item>
+ </style>
+
+ <style name="DialerFlatButtonStyle" parent="@android:style/Widget.Material.Button">
+ <item name="android:background">?android:attr/selectableItemBackground</item>
+ <item name="android:paddingEnd">@dimen/button_horizontal_padding</item>
+ <item name="android:paddingStart">@dimen/button_horizontal_padding</item>
+ <item name="android:textColor">@color/dialer_flat_button_text_color</item>
+ </style>
+
+ <!-- Style for the 'primary' button in a view. Unlike the DialerFlatButtonStyle, this button -->
+ <!-- is not colored white, to draw more attention to it. -->
+ <style name="DialerPrimaryFlatButtonStyle" parent="@android:style/Widget.Material.Button">
+ <item name="android:background">@drawable/selectable_primary_flat_button</item>
+ <item name="android:paddingEnd">@dimen/button_horizontal_padding</item>
+ <item name="android:paddingStart">@dimen/button_horizontal_padding</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="BlockedNumbersDescriptionTextStyle">
+ <item name="android:lineSpacingMultiplier">1.43</item>
+ <item name="android:paddingTop">8dp</item>
+ <item name="android:paddingBottom">8dp</item>
+ <item name="android:textSize">@dimen/blocked_number_settings_description_text_size</item>
+ </style>
+
+ <style name="FullWidthDivider">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">1dp</item>
+ <item name="android:background">?android:attr/listDivider</item>
+ </style>
+</resources>
diff --git a/java/com/android/dialer/app/res/xml/display_options_settings.xml b/java/com/android/dialer/app/res/xml/display_options_settings.xml
new file mode 100644
index 000000000..0b4e11d47
--- /dev/null
+++ b/java/com/android/dialer/app/res/xml/display_options_settings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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
+ -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <com.android.contacts.common.preference.SortOrderPreference
+ android:dialogTitle="@string/display_options_sort_list_by"
+ android:key="sortOrder"
+ android:title="@string/display_options_sort_list_by"/>
+
+ <com.android.contacts.common.preference.DisplayOrderPreference
+ android:dialogTitle="@string/display_options_view_names_as"
+ android:key="displayOrder"
+ android:title="@string/display_options_view_names_as"/>
+
+</PreferenceScreen>
diff --git a/java/com/android/dialer/app/res/xml/file_paths.xml b/java/com/android/dialer/app/res/xml/file_paths.xml
new file mode 100644
index 000000000..41522e4c8
--- /dev/null
+++ b/java/com/android/dialer/app/res/xml/file_paths.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<paths>
+ <!-- Offer access to files under Context.getCacheDir() -->
+ <cache-path name="my_cache"/>
+ <!-- Offer access to voicemail folder under Context.getFilesDir() -->
+ <files-path
+ name="voicemails"
+ path="voicemails/"/>
+</paths>
diff --git a/java/com/android/dialer/app/res/xml/searchable.xml b/java/com/android/dialer/app/res/xml/searchable.xml
new file mode 100644
index 000000000..0ea168589
--- /dev/null
+++ b/java/com/android/dialer/app/res/xml/searchable.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ android:hint="@string/dialer_hint_find_contact"
+ android:imeOptions="actionSearch"
+ android:inputType="textNoSuggestions"
+ android:label="@string/applicationLabel"
+ android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"
+ /> \ No newline at end of file
diff --git a/java/com/android/dialer/app/res/xml/sound_settings.xml b/java/com/android/dialer/app/res/xml/sound_settings.xml
new file mode 100644
index 000000000..796ed2ec1
--- /dev/null
+++ b/java/com/android/dialer/app/res/xml/sound_settings.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2014 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
+ -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <com.android.dialer.app.settings.DefaultRingtonePreference
+ android:dialogTitle="@string/ringtone_title"
+ android:key="@string/ringtone_preference_key"
+ android:persistent="false"
+ android:ringtoneType="ringtone"
+ android:title="@string/ringtone_title"/>
+
+ <CheckBoxPreference
+ android:defaultValue="false"
+ android:key="@string/vibrate_on_preference_key"
+ android:persistent="false"
+ android:title="@string/vibrate_on_ring_title"/>
+
+ <CheckBoxPreference
+ android:defaultValue="true"
+ android:key="@string/play_dtmf_preference_key"
+ android:persistent="false"
+ android:title="@string/dtmf_tone_enable_title"/>
+
+ <ListPreference
+ android:entries="@array/dtmf_tone_length_entries"
+ android:entryValues="@array/dtmf_tone_length_entry_values"
+ android:key="@string/dtmf_tone_length_preference_key"
+ android:title="@string/dtmf_tone_length_title"/>
+
+</PreferenceScreen>
diff --git a/java/com/android/dialer/app/settings/AppCompatPreferenceActivity.java b/java/com/android/dialer/app/settings/AppCompatPreferenceActivity.java
new file mode 100644
index 000000000..2c464386b
--- /dev/null
+++ b/java/com/android/dialer/app/settings/AppCompatPreferenceActivity.java
@@ -0,0 +1,155 @@
+/*
+ * 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.dialer.app.settings;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatDelegate;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
+ * to be used with AppCompat.
+ */
+public class AppCompatPreferenceActivity extends PreferenceActivity {
+
+ private AppCompatDelegate mDelegate;
+
+ private boolean mIsSafeToCommitTransactions;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ getDelegate().installViewFactory();
+ getDelegate().onCreate(savedInstanceState);
+ super.onCreate(savedInstanceState);
+ mIsSafeToCommitTransactions = true;
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ getDelegate().onPostCreate(savedInstanceState);
+ }
+
+ public ActionBar getSupportActionBar() {
+ return getDelegate().getSupportActionBar();
+ }
+
+ public void setSupportActionBar(Toolbar toolbar) {
+ getDelegate().setSupportActionBar(toolbar);
+ }
+
+ @Override
+ public MenuInflater getMenuInflater() {
+ return getDelegate().getMenuInflater();
+ }
+
+ @Override
+ public void setContentView(int layoutResID) {
+ getDelegate().setContentView(layoutResID);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ getDelegate().setContentView(view);
+ }
+
+ @Override
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().setContentView(view, params);
+ }
+
+ @Override
+ public void addContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().addContentView(view, params);
+ }
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ getDelegate().onPostResume();
+ }
+
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ super.onTitleChanged(title, color);
+ getDelegate().setTitle(title);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ getDelegate().onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ getDelegate().onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ getDelegate().onDestroy();
+ }
+
+ @Override
+ public void invalidateOptionsMenu() {
+ getDelegate().invalidateOptionsMenu();
+ }
+
+ private AppCompatDelegate getDelegate() {
+ if (mDelegate == null) {
+ mDelegate = AppCompatDelegate.create(this, null);
+ }
+ return mDelegate;
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mIsSafeToCommitTransactions = true;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mIsSafeToCommitTransactions = true;
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mIsSafeToCommitTransactions = false;
+ }
+
+ /**
+ * Returns true if it is safe to commit {@link FragmentTransaction}s at this time, based on
+ * whether {@link Activity#onSaveInstanceState} has been called or not.
+ *
+ * <p>Make sure that the current activity calls into {@link super.onSaveInstanceState(Bundle
+ * outState)} (if that method is overridden), so the flag is properly set.
+ */
+ public boolean isSafeToCommitTransactions() {
+ return mIsSafeToCommitTransactions;
+ }
+}
diff --git a/java/com/android/dialer/app/settings/DefaultRingtonePreference.java b/java/com/android/dialer/app/settings/DefaultRingtonePreference.java
new file mode 100644
index 000000000..579584e0f
--- /dev/null
+++ b/java/com/android/dialer/app/settings/DefaultRingtonePreference.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 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.dialer.app.settings;
+
+import android.content.Context;
+import android.content.Intent;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.preference.RingtonePreference;
+import android.provider.Settings;
+import android.util.AttributeSet;
+import android.widget.Toast;
+import com.android.dialer.app.R;
+
+/** RingtonePreference which doesn't show default ringtone setting. */
+public class DefaultRingtonePreference extends RingtonePreference {
+
+ public DefaultRingtonePreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onPrepareRingtonePickerIntent(Intent ringtonePickerIntent) {
+ super.onPrepareRingtonePickerIntent(ringtonePickerIntent);
+
+ /*
+ * Since this preference is for choosing the default ringtone, it
+ * doesn't make sense to show a 'Default' item.
+ */
+ ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
+ }
+
+ @Override
+ protected void onSaveRingtone(Uri ringtoneUri) {
+ if (!Settings.System.canWrite(getContext())) {
+ Toast.makeText(
+ getContext(),
+ getContext().getResources().getString(R.string.toast_cannot_write_system_settings),
+ Toast.LENGTH_SHORT)
+ .show();
+ return;
+ }
+ RingtoneManager.setActualDefaultRingtoneUri(getContext(), getRingtoneType(), ringtoneUri);
+ }
+
+ @Override
+ protected Uri onRestoreRingtone() {
+ return RingtoneManager.getActualDefaultRingtoneUri(getContext(), getRingtoneType());
+ }
+}
diff --git a/java/com/android/dialer/app/settings/DialerSettingsActivity.java b/java/com/android/dialer/app/settings/DialerSettingsActivity.java
new file mode 100644
index 000000000..b04674013
--- /dev/null
+++ b/java/com/android/dialer/app/settings/DialerSettingsActivity.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2013 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.dialer.app.settings;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+import android.view.MenuItem;
+import android.widget.Toast;
+import com.android.contacts.common.compat.TelephonyManagerCompat;
+import com.android.dialer.app.R;
+import com.android.dialer.blocking.FilteredNumberCompat;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.proguard.UsedByReflection;
+import java.util.List;
+
+@UsedByReflection(value = "AndroidManifest-app.xml")
+public class DialerSettingsActivity extends AppCompatPreferenceActivity {
+
+ protected SharedPreferences mPreferences;
+ private boolean migrationStatusOnBuildHeaders;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ /*
+ * The blockedCallsHeader need to be recreated if the migration status changed because
+ * the intent needs to be updated.
+ */
+ if (migrationStatusOnBuildHeaders != FilteredNumberCompat.hasMigratedToNewBlocking(this)) {
+ invalidateHeaders();
+ }
+ }
+
+ @Override
+ public void onBuildHeaders(List<Header> target) {
+ if (showDisplayOptions()) {
+ Header displayOptionsHeader = new Header();
+ displayOptionsHeader.titleRes = R.string.display_options_title;
+ displayOptionsHeader.fragment = DisplayOptionsSettingsFragment.class.getName();
+ target.add(displayOptionsHeader);
+ }
+
+ Header soundSettingsHeader = new Header();
+ soundSettingsHeader.titleRes = R.string.sounds_and_vibration_title;
+ soundSettingsHeader.fragment = SoundSettingsFragment.class.getName();
+ soundSettingsHeader.id = R.id.settings_header_sounds_and_vibration;
+ target.add(soundSettingsHeader);
+
+ if (CompatUtils.isMarshmallowCompatible()) {
+ Header quickResponseSettingsHeader = new Header();
+ Intent quickResponseSettingsIntent =
+ new Intent(TelecomManager.ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS);
+ quickResponseSettingsHeader.titleRes = R.string.respond_via_sms_setting_title;
+ quickResponseSettingsHeader.intent = quickResponseSettingsIntent;
+ target.add(quickResponseSettingsHeader);
+ }
+
+ TelephonyManager telephonyManager =
+ (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+
+ // "Call Settings" (full settings) is shown if the current user is primary user and there
+ // is only one SIM. Before N, "Calling accounts" setting is shown if the current user is
+ // primary user and there are multiple SIMs. In N+, "Calling accounts" is shown whenever
+ // "Call Settings" is not shown.
+ boolean isPrimaryUser = isPrimaryUser();
+ if (isPrimaryUser && TelephonyManagerCompat.getPhoneCount(telephonyManager) <= 1) {
+ Header callSettingsHeader = new Header();
+ Intent callSettingsIntent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS);
+ callSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+ callSettingsHeader.titleRes = R.string.call_settings_label;
+ callSettingsHeader.intent = callSettingsIntent;
+ target.add(callSettingsHeader);
+ } else if ((VERSION.SDK_INT >= VERSION_CODES.N) || isPrimaryUser) {
+ Header phoneAccountSettingsHeader = new Header();
+ Intent phoneAccountSettingsIntent = new Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS);
+ phoneAccountSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+ phoneAccountSettingsHeader.titleRes = R.string.phone_account_settings_label;
+ phoneAccountSettingsHeader.intent = phoneAccountSettingsIntent;
+ target.add(phoneAccountSettingsHeader);
+ }
+ if (FilteredNumberCompat.canCurrentUserOpenBlockSettings(this)) {
+ Header blockedCallsHeader = new Header();
+ blockedCallsHeader.titleRes = R.string.manage_blocked_numbers_label;
+ blockedCallsHeader.intent = FilteredNumberCompat.createManageBlockedNumbersIntent(this);
+ target.add(blockedCallsHeader);
+ migrationStatusOnBuildHeaders = FilteredNumberCompat.hasMigratedToNewBlocking(this);
+ }
+ if (isPrimaryUser
+ && (TelephonyManagerCompat.isTtyModeSupported(telephonyManager)
+ || TelephonyManagerCompat.isHearingAidCompatibilitySupported(telephonyManager))) {
+ Header accessibilitySettingsHeader = new Header();
+ Intent accessibilitySettingsIntent =
+ new Intent(TelecomManager.ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS);
+ accessibilitySettingsHeader.titleRes = R.string.accessibility_settings_title;
+ accessibilitySettingsHeader.intent = accessibilitySettingsIntent;
+ target.add(accessibilitySettingsHeader);
+ }
+ }
+
+ /**
+ * Returns {@code true} or {@code false} based on whether the display options setting should be
+ * shown. For languages such as Chinese, Japanese, or Korean, display options aren't useful since
+ * contacts are sorted and displayed family name first by default.
+ *
+ * @return {@code true} if the display options should be shown, {@code false} otherwise.
+ */
+ private boolean showDisplayOptions() {
+ return getResources().getBoolean(R.bool.config_display_order_user_changeable)
+ && getResources().getBoolean(R.bool.config_sort_order_user_changeable);
+ }
+
+ @Override
+ public void onHeaderClick(Header header, int position) {
+ if (header.id == R.id.settings_header_sounds_and_vibration) {
+ // If we don't have the permission to write to system settings, go to system sound
+ // settings instead. Otherwise, perform the super implementation (which launches our
+ // own preference fragment.
+ if (!Settings.System.canWrite(this)) {
+ Toast.makeText(
+ this,
+ getResources().getString(R.string.toast_cannot_write_system_settings),
+ Toast.LENGTH_SHORT)
+ .show();
+ startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS));
+ return;
+ }
+ }
+ super.onHeaderClick(header, position);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (!isSafeToCommitTransactions()) {
+ return;
+ }
+ super.onBackPressed();
+ }
+
+ @Override
+ protected boolean isValidFragment(String fragmentName) {
+ return true;
+ }
+
+ /** @return Whether the current user is the primary user. */
+ private boolean isPrimaryUser() {
+ return getSystemService(UserManager.class).isSystemUser();
+ }
+}
diff --git a/src/com/android/dialer/settings/DisplayOptionsSettingsFragment.java b/java/com/android/dialer/app/settings/DisplayOptionsSettingsFragment.java
index 4b2c8f6db..bf1637f27 100644
--- a/src/com/android/dialer/settings/DisplayOptionsSettingsFragment.java
+++ b/java/com/android/dialer/app/settings/DisplayOptionsSettingsFragment.java
@@ -14,18 +14,17 @@
* limitations under the License
*/
-package com.android.dialer.settings;
+package com.android.dialer.app.settings;
import android.os.Bundle;
import android.preference.PreferenceFragment;
-
-import com.android.dialer.R;
+import com.android.dialer.app.R;
public class DisplayOptionsSettingsFragment extends PreferenceFragment {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- addPreferencesFromResource(R.xml.display_options_settings);
- }
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.display_options_settings);
+ }
}
diff --git a/java/com/android/dialer/app/settings/SoundSettingsFragment.java b/java/com/android/dialer/app/settings/SoundSettingsFragment.java
new file mode 100644
index 000000000..83ce45398
--- /dev/null
+++ b/java/com/android/dialer/app/settings/SoundSettingsFragment.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2014 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.dialer.app.settings;
+
+import android.content.Context;
+import android.media.RingtoneManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Vibrator;
+import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.widget.Toast;
+import com.android.dialer.app.R;
+import com.android.dialer.compat.SdkVersionOverride;
+import com.android.dialer.util.SettingsUtil;
+
+public class SoundSettingsFragment extends PreferenceFragment
+ implements Preference.OnPreferenceChangeListener {
+
+ private static final int NO_DTMF_TONE = 0;
+ private static final int PLAY_DTMF_TONE = 1;
+
+ private static final int NO_VIBRATION_FOR_CALLS = 0;
+ private static final int DO_VIBRATION_FOR_CALLS = 1;
+
+ private static final int DTMF_TONE_TYPE_NORMAL = 0;
+
+ private static final int MSG_UPDATE_RINGTONE_SUMMARY = 1;
+
+ private Preference mRingtonePreference;
+ private final Handler mRingtoneLookupComplete =
+ new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_RINGTONE_SUMMARY:
+ mRingtonePreference.setSummary((CharSequence) msg.obj);
+ break;
+ }
+ }
+ };
+ private final Runnable mRingtoneLookupRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ updateRingtonePreferenceSummary();
+ }
+ };
+ private CheckBoxPreference mVibrateWhenRinging;
+ private CheckBoxPreference mPlayDtmfTone;
+ private ListPreference mDtmfToneLength;
+
+ @Override
+ public Context getContext() {
+ return getActivity();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ addPreferencesFromResource(R.xml.sound_settings);
+
+ Context context = getActivity();
+
+ mRingtonePreference = findPreference(context.getString(R.string.ringtone_preference_key));
+ mVibrateWhenRinging =
+ (CheckBoxPreference) findPreference(context.getString(R.string.vibrate_on_preference_key));
+ mPlayDtmfTone =
+ (CheckBoxPreference) findPreference(context.getString(R.string.play_dtmf_preference_key));
+ mDtmfToneLength =
+ (ListPreference)
+ findPreference(context.getString(R.string.dtmf_tone_length_preference_key));
+
+ if (hasVibrator()) {
+ mVibrateWhenRinging.setOnPreferenceChangeListener(this);
+ } else {
+ getPreferenceScreen().removePreference(mVibrateWhenRinging);
+ mVibrateWhenRinging = null;
+ }
+
+ mPlayDtmfTone.setOnPreferenceChangeListener(this);
+ mPlayDtmfTone.setChecked(shouldPlayDtmfTone());
+
+ TelephonyManager telephonyManager =
+ (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
+ if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M
+ && telephonyManager.canChangeDtmfToneLength()
+ && (telephonyManager.isWorldPhone() || !shouldHideCarrierSettings())) {
+ mDtmfToneLength.setOnPreferenceChangeListener(this);
+ mDtmfToneLength.setValueIndex(
+ Settings.System.getInt(
+ context.getContentResolver(),
+ Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
+ DTMF_TONE_TYPE_NORMAL));
+ } else {
+ getPreferenceScreen().removePreference(mDtmfToneLength);
+ mDtmfToneLength = null;
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (!Settings.System.canWrite(getContext())) {
+ // If the user launches this setting fragment, then toggles the WRITE_SYSTEM_SETTINGS
+ // AppOp, then close the fragment since there is nothing useful to do.
+ getActivity().onBackPressed();
+ return;
+ }
+
+ if (mVibrateWhenRinging != null) {
+ mVibrateWhenRinging.setChecked(shouldVibrateWhenRinging());
+ }
+
+ // Lookup the ringtone name asynchronously.
+ new Thread(mRingtoneLookupRunnable).start();
+ }
+
+ /**
+ * Supports onPreferenceChangeListener to look for preference changes.
+ *
+ * @param preference The preference to be changed
+ * @param objValue The value of the selection, NOT its localized display value.
+ */
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object objValue) {
+ if (!Settings.System.canWrite(getContext())) {
+ // A user shouldn't be able to get here, but this protects against monkey crashes.
+ Toast.makeText(
+ getContext(),
+ getResources().getString(R.string.toast_cannot_write_system_settings),
+ Toast.LENGTH_SHORT)
+ .show();
+ return true;
+ }
+ if (preference == mVibrateWhenRinging) {
+ boolean doVibrate = (Boolean) objValue;
+ Settings.System.putInt(
+ getActivity().getContentResolver(),
+ Settings.System.VIBRATE_WHEN_RINGING,
+ doVibrate ? DO_VIBRATION_FOR_CALLS : NO_VIBRATION_FOR_CALLS);
+ } else if (preference == mDtmfToneLength) {
+ int index = mDtmfToneLength.findIndexOfValue((String) objValue);
+ Settings.System.putInt(
+ getActivity().getContentResolver(), Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, index);
+ }
+ return true;
+ }
+
+ /** Click listener for toggle events. */
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+ if (!Settings.System.canWrite(getContext())) {
+ Toast.makeText(
+ getContext(),
+ getResources().getString(R.string.toast_cannot_write_system_settings),
+ Toast.LENGTH_SHORT)
+ .show();
+ return true;
+ }
+ if (preference == mPlayDtmfTone) {
+ Settings.System.putInt(
+ getActivity().getContentResolver(),
+ Settings.System.DTMF_TONE_WHEN_DIALING,
+ mPlayDtmfTone.isChecked() ? PLAY_DTMF_TONE : NO_DTMF_TONE);
+ }
+ return true;
+ }
+
+ /** Updates the summary text on the ringtone preference with the name of the ringtone. */
+ private void updateRingtonePreferenceSummary() {
+ SettingsUtil.updateRingtoneName(
+ getActivity(),
+ mRingtoneLookupComplete,
+ RingtoneManager.TYPE_RINGTONE,
+ mRingtonePreference.getKey(),
+ MSG_UPDATE_RINGTONE_SUMMARY);
+ }
+
+ /**
+ * Obtain the value for "vibrate when ringing" setting. The default value is false.
+ *
+ * <p>Watch out: if the setting is missing in the device, this will try obtaining the old "vibrate
+ * on ring" setting from AudioManager, and save the previous setting to the new one.
+ */
+ private boolean shouldVibrateWhenRinging() {
+ int vibrateWhenRingingSetting =
+ Settings.System.getInt(
+ getActivity().getContentResolver(),
+ Settings.System.VIBRATE_WHEN_RINGING,
+ NO_VIBRATION_FOR_CALLS);
+ return hasVibrator() && (vibrateWhenRingingSetting == DO_VIBRATION_FOR_CALLS);
+ }
+
+ /** Obtains the value for dialpad/DTMF tones. The default value is true. */
+ private boolean shouldPlayDtmfTone() {
+ int dtmfToneSetting =
+ Settings.System.getInt(
+ getActivity().getContentResolver(),
+ Settings.System.DTMF_TONE_WHEN_DIALING,
+ PLAY_DTMF_TONE);
+ return dtmfToneSetting == PLAY_DTMF_TONE;
+ }
+
+ /** Whether the device hardware has a vibrator. */
+ private boolean hasVibrator() {
+ Vibrator vibrator = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
+ return vibrator != null && vibrator.hasVibrator();
+ }
+
+ private boolean shouldHideCarrierSettings() {
+ CarrierConfigManager configManager =
+ (CarrierConfigManager) getActivity().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ return configManager
+ .getConfig()
+ .getBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL);
+ }
+}
diff --git a/java/com/android/dialer/app/voicemail/VoicemailAudioManager.java b/java/com/android/dialer/app/voicemail/VoicemailAudioManager.java
new file mode 100644
index 000000000..8d70cdbe7
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/VoicemailAudioManager.java
@@ -0,0 +1,252 @@
+/*
+ * 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.dialer.app.voicemail;
+
+import android.content.Context;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.AudioManager.OnAudioFocusChangeListener;
+import android.telecom.CallAudioState;
+import com.android.dialer.common.LogUtil;
+import java.util.concurrent.RejectedExecutionException;
+
+/** This class manages all audio changes for voicemail playback. */
+public final class VoicemailAudioManager
+ implements OnAudioFocusChangeListener, WiredHeadsetManager.Listener {
+
+ private static final String TAG = "VoicemailAudioManager";
+
+ public static final int PLAYBACK_STREAM = AudioManager.STREAM_VOICE_CALL;
+
+ private AudioManager mAudioManager;
+ private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
+ private WiredHeadsetManager mWiredHeadsetManager;
+ private boolean mWasSpeakerOn;
+ private CallAudioState mCallAudioState;
+ private boolean mBluetoothScoEnabled;
+
+ public VoicemailAudioManager(
+ Context context, VoicemailPlaybackPresenter voicemailPlaybackPresenter) {
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
+ mWiredHeadsetManager = new WiredHeadsetManager(context);
+ mWiredHeadsetManager.setListener(this);
+
+ mCallAudioState = getInitialAudioState();
+ LogUtil.i(
+ "VoicemailAudioManager.VoicemailAudioManager", "Initial audioState = " + mCallAudioState);
+ }
+
+ public void requestAudioFocus() {
+ int result =
+ mAudioManager.requestAudioFocus(
+ this, PLAYBACK_STREAM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+ if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+ throw new RejectedExecutionException("Could not capture audio focus.");
+ }
+ updateBluetoothScoState(true);
+ }
+
+ public void abandonAudioFocus() {
+ updateBluetoothScoState(false);
+ mAudioManager.abandonAudioFocus(this);
+ }
+
+ @Override
+ public void onAudioFocusChange(int focusChange) {
+ LogUtil.d("VoicemailAudioManager.onAudioFocusChange", "focusChange=" + focusChange);
+ mVoicemailPlaybackPresenter.onAudioFocusChange(focusChange == AudioManager.AUDIOFOCUS_GAIN);
+ }
+
+ @Override
+ public void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn) {
+ LogUtil.i(
+ "VoicemailAudioManager.onWiredHeadsetPluggedInChanged",
+ "wired headset was plugged in changed: " + oldIsPluggedIn + " -> " + newIsPluggedIn);
+
+ if (oldIsPluggedIn == newIsPluggedIn) {
+ return;
+ }
+
+ int newRoute = mCallAudioState.getRoute(); // start out with existing route
+ if (newIsPluggedIn) {
+ newRoute = CallAudioState.ROUTE_WIRED_HEADSET;
+ } else {
+ if (mWasSpeakerOn) {
+ newRoute = CallAudioState.ROUTE_SPEAKER;
+ } else {
+ newRoute = CallAudioState.ROUTE_EARPIECE;
+ }
+ }
+
+ mVoicemailPlaybackPresenter.setSpeakerphoneOn(newRoute == CallAudioState.ROUTE_SPEAKER);
+
+ // We need to call this every time even if we do not change the route because the supported
+ // routes changed either to include or not include WIRED_HEADSET.
+ setSystemAudioState(
+ new CallAudioState(false /* muted */, newRoute, calculateSupportedRoutes()));
+ }
+
+ public void setSpeakerphoneOn(boolean on) {
+ setAudioRoute(on ? CallAudioState.ROUTE_SPEAKER : CallAudioState.ROUTE_WIRED_OR_EARPIECE);
+ }
+
+ public boolean isWiredHeadsetPluggedIn() {
+ return mWiredHeadsetManager.isPluggedIn();
+ }
+
+ public void registerReceivers() {
+ // Receivers is plural because we expect to add bluetooth support.
+ mWiredHeadsetManager.registerReceiver();
+ }
+
+ public void unregisterReceivers() {
+ mWiredHeadsetManager.unregisterReceiver();
+ }
+
+ /**
+ * Bluetooth SCO (Synchronous Connection-Oriented) is the "phone" bluetooth audio. The system will
+ * route to the bluetooth headset automatically if A2DP ("media") is available, but if the headset
+ * only supports SCO then dialer must route it manually.
+ */
+ private void updateBluetoothScoState(boolean hasAudioFocus) {
+ if (hasAudioFocus) {
+ if (hasMediaAudioCapability()) {
+ mBluetoothScoEnabled = false;
+ } else {
+ mBluetoothScoEnabled = true;
+ LogUtil.i(
+ "VoicemailAudioManager.updateBluetoothScoState",
+ "bluetooth device doesn't support media, using SCO instead");
+ }
+ } else {
+ mBluetoothScoEnabled = false;
+ }
+ applyBluetoothScoState();
+ }
+
+ private void applyBluetoothScoState() {
+ if (mBluetoothScoEnabled) {
+ mAudioManager.startBluetoothSco();
+ // The doc for startBluetoothSco() states it could take seconds to establish the SCO
+ // connection, so we should probably resume the playback after we've acquired SCO.
+ // In practice the delay is unnoticeable so this is ignored for simplicity.
+ mAudioManager.setBluetoothScoOn(true);
+ } else {
+ mAudioManager.setBluetoothScoOn(false);
+ mAudioManager.stopBluetoothSco();
+ }
+ }
+
+ private boolean hasMediaAudioCapability() {
+ for (AudioDeviceInfo info : mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) {
+ if (info.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Change the audio route, for example from earpiece to speakerphone.
+ *
+ * @param route The new audio route to use. See {@link CallAudioState}.
+ */
+ void setAudioRoute(int route) {
+ LogUtil.v(
+ "VoicemailAudioManager.setAudioRoute",
+ "route: " + CallAudioState.audioRouteToString(route));
+
+ // Change ROUTE_WIRED_OR_EARPIECE to a single entry.
+ int newRoute = selectWiredOrEarpiece(route, mCallAudioState.getSupportedRouteMask());
+
+ // If route is unsupported, do nothing.
+ if ((mCallAudioState.getSupportedRouteMask() | newRoute) == 0) {
+ LogUtil.w(
+ "VoicemailAudioManager.setAudioRoute",
+ "Asking to set to a route that is unsupported: " + newRoute);
+ return;
+ }
+
+ // Remember the new speaker state so it can be restored when the user plugs and unplugs
+ // a headset.
+ mWasSpeakerOn = newRoute == CallAudioState.ROUTE_SPEAKER;
+ setSystemAudioState(
+ new CallAudioState(false /* muted */, newRoute, mCallAudioState.getSupportedRouteMask()));
+ }
+
+ private CallAudioState getInitialAudioState() {
+ int supportedRouteMask = calculateSupportedRoutes();
+ int route = selectWiredOrEarpiece(CallAudioState.ROUTE_WIRED_OR_EARPIECE, supportedRouteMask);
+ return new CallAudioState(false /* muted */, route, supportedRouteMask);
+ }
+
+ private int calculateSupportedRoutes() {
+ int routeMask = CallAudioState.ROUTE_SPEAKER;
+ if (mWiredHeadsetManager.isPluggedIn()) {
+ routeMask |= CallAudioState.ROUTE_WIRED_HEADSET;
+ } else {
+ routeMask |= CallAudioState.ROUTE_EARPIECE;
+ }
+ return routeMask;
+ }
+
+ private int selectWiredOrEarpiece(int route, int supportedRouteMask) {
+ // Since they are mutually exclusive and one is ALWAYS valid, we allow a special input of
+ // ROUTE_WIRED_OR_EARPIECE so that callers don't have to make a call to check which is
+ // supported before calling setAudioRoute.
+ if (route == CallAudioState.ROUTE_WIRED_OR_EARPIECE) {
+ route = CallAudioState.ROUTE_WIRED_OR_EARPIECE & supportedRouteMask;
+ if (route == 0) {
+ LogUtil.e(
+ "VoicemailAudioManager.selectWiredOrEarpiece",
+ "One of wired headset or earpiece should always be valid.");
+ // assume earpiece in this case.
+ route = CallAudioState.ROUTE_EARPIECE;
+ }
+ }
+ return route;
+ }
+
+ private void setSystemAudioState(CallAudioState callAudioState) {
+ CallAudioState oldAudioState = mCallAudioState;
+ mCallAudioState = callAudioState;
+
+ LogUtil.i(
+ "VoicemailAudioManager.setSystemAudioState",
+ "changing from " + oldAudioState + " to " + mCallAudioState);
+
+ // Audio route.
+ if (mCallAudioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
+ turnOnSpeaker(true);
+ } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_EARPIECE
+ || mCallAudioState.getRoute() == CallAudioState.ROUTE_WIRED_HEADSET) {
+ // Just handle turning off the speaker, the system will handle switching between wired
+ // headset and earpiece.
+ turnOnSpeaker(false);
+ // BluetoothSco is not handled by the system so it has to be reset.
+ applyBluetoothScoState();
+ }
+ }
+
+ private void turnOnSpeaker(boolean on) {
+ if (mAudioManager.isSpeakerphoneOn() != on) {
+ LogUtil.i("VoicemailAudioManager.turnOnSpeaker", "turning speaker phone on: " + on);
+ mAudioManager.setSpeakerphoneOn(on);
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/voicemail/VoicemailErrorManager.java b/java/com/android/dialer/app/voicemail/VoicemailErrorManager.java
new file mode 100644
index 000000000..939007adf
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/VoicemailErrorManager.java
@@ -0,0 +1,129 @@
+/*
+ * 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.dialer.app.voicemail;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.os.Handler;
+import com.android.dialer.app.calllog.CallLogAlertManager;
+import com.android.dialer.app.calllog.CallLogModalAlertManager;
+import com.android.dialer.app.voicemail.error.VoicemailErrorAlert;
+import com.android.dialer.app.voicemail.error.VoicemailErrorMessageCreator;
+import com.android.dialer.app.voicemail.error.VoicemailStatus;
+import com.android.dialer.app.voicemail.error.VoicemailStatusReader;
+import com.android.dialer.database.CallLogQueryHandler;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fetches voicemail status and generate {@link VoicemailStatus} for {@link VoicemailErrorAlert} to
+ * show.
+ */
+public class VoicemailErrorManager implements CallLogQueryHandler.Listener, VoicemailStatusReader {
+
+ private final Context context;
+ private final CallLogQueryHandler callLogQueryHandler;
+ private final VoicemailErrorAlert alertItem;
+
+ private final ContentObserver statusObserver =
+ new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ maybeFetchStatus();
+ }
+ };
+
+ private boolean isForeground;
+ private boolean statusInvalidated;
+
+ public VoicemailErrorManager(
+ Context context,
+ CallLogAlertManager alertManager,
+ CallLogModalAlertManager modalAlertManager) {
+ this.context = context;
+ alertItem =
+ new VoicemailErrorAlert(
+ context, alertManager, modalAlertManager, new VoicemailErrorMessageCreator());
+ callLogQueryHandler = new CallLogQueryHandler(context, context.getContentResolver(), this);
+ maybeFetchStatus();
+ }
+
+ public ContentObserver getContentObserver() {
+ return statusObserver;
+ }
+
+ @Override
+ public void onVoicemailStatusFetched(Cursor statusCursor) {
+ List<VoicemailStatus> statuses = new ArrayList<>();
+ while (statusCursor.moveToNext()) {
+ VoicemailStatus status = new VoicemailStatus(context, statusCursor);
+ if (status.isActive()) {
+ statuses.add(status);
+ }
+ }
+ alertItem.updateStatus(statuses, this);
+ // TODO: b/30668323 support error from multiple sources.
+ return;
+ }
+
+ @Override
+ public void onVoicemailUnreadCountFetched(Cursor cursor) {
+ // Do nothing
+ }
+
+ @Override
+ public void onMissedCallsUnreadCountFetched(Cursor cursor) {
+ // Do nothing
+ }
+
+ @Override
+ public boolean onCallsFetched(Cursor combinedCursor) {
+ // Do nothing
+ return false;
+ }
+
+ public void onResume() {
+ isForeground = true;
+ if (statusInvalidated) {
+ maybeFetchStatus();
+ }
+ }
+
+ public void onPause() {
+ isForeground = false;
+ statusInvalidated = false;
+ }
+
+ @Override
+ public void refresh() {
+ maybeFetchStatus();
+ }
+
+ /**
+ * Fetch the status when the dialer is in foreground, or queue a fetch when the dialer resumes.
+ */
+ private void maybeFetchStatus() {
+ if (!isForeground) {
+ // Dialer is in the background, UI should not be updated. Reload the status when it resumes.
+ statusInvalidated = true;
+ return;
+ }
+ callLogQueryHandler.fetchVoicemailStatus();
+ }
+}
diff --git a/java/com/android/dialer/app/voicemail/VoicemailPlaybackLayout.java b/java/com/android/dialer/app/voicemail/VoicemailPlaybackLayout.java
new file mode 100644
index 000000000..fc6a37608
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/VoicemailPlaybackLayout.java
@@ -0,0 +1,449 @@
+/*
+ * 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.dialer.app.voicemail;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Handler;
+import android.support.annotation.VisibleForTesting;
+import android.support.design.widget.Snackbar;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+import com.android.dialer.app.PhoneCallDetails;
+import com.android.dialer.app.R;
+import com.android.dialer.app.calllog.CallLogAsyncTaskUtil;
+import com.android.dialer.app.calllog.CallLogListItemViewHolder;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import java.util.Objects;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.concurrent.GuardedBy;
+import javax.annotation.concurrent.NotThreadSafe;
+import javax.annotation.concurrent.ThreadSafe;
+
+/**
+ * Displays and plays a single voicemail. See {@link VoicemailPlaybackPresenter} for details on the
+ * voicemail playback implementation.
+ *
+ * <p>This class is not thread-safe, it is thread-confined. All calls to all public methods on this
+ * class are expected to come from the main ui thread.
+ */
+@NotThreadSafe
+public class VoicemailPlaybackLayout extends LinearLayout
+ implements VoicemailPlaybackPresenter.PlaybackView,
+ CallLogAsyncTaskUtil.CallLogAsyncTaskListener {
+
+ private static final String TAG = VoicemailPlaybackLayout.class.getSimpleName();
+ private static final int VOICEMAIL_DELETE_DELAY_MS = 3000;
+
+ private Context mContext;
+ private CallLogListItemViewHolder mViewHolder;
+ private VoicemailPlaybackPresenter mPresenter;
+ /** Click listener to toggle speakerphone. */
+ private final View.OnClickListener mSpeakerphoneListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mPresenter != null) {
+ mPresenter.toggleSpeakerphone();
+ }
+ }
+ };
+
+ private Uri mVoicemailUri;
+ private final View.OnClickListener mDeleteButtonListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Logger.get(mContext).logImpression(DialerImpression.Type.VOICEMAIL_DELETE_ENTRY);
+ if (mPresenter == null) {
+ return;
+ }
+
+ // When the undo button is pressed, the viewHolder we have is no longer valid because when
+ // we hide the view it is binded to something else, and the layout is not updated for
+ // hidden items. copy the adapter position so we can update the view upon undo.
+ // TODO: refactor this so the view holder will always be valid.
+ final int adapterPosition = mViewHolder.getAdapterPosition();
+
+ mPresenter.pausePlayback();
+ mPresenter.onVoicemailDeleted(mViewHolder);
+
+ final Uri deleteUri = mVoicemailUri;
+ final Runnable deleteCallback =
+ new Runnable() {
+ @Override
+ public void run() {
+ if (Objects.equals(deleteUri, mVoicemailUri)) {
+ CallLogAsyncTaskUtil.deleteVoicemail(
+ mContext, deleteUri, VoicemailPlaybackLayout.this);
+ }
+ }
+ };
+
+ final Handler handler = new Handler();
+ // Add a little buffer time in case the user clicked "undo" at the end of the delay
+ // window.
+ handler.postDelayed(deleteCallback, VOICEMAIL_DELETE_DELAY_MS + 50);
+
+ Snackbar.make(
+ VoicemailPlaybackLayout.this,
+ R.string.snackbar_voicemail_deleted,
+ Snackbar.LENGTH_LONG)
+ .setDuration(VOICEMAIL_DELETE_DELAY_MS)
+ .setAction(
+ R.string.snackbar_voicemail_deleted_undo,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mPresenter.onVoicemailDeleteUndo(adapterPosition);
+ handler.removeCallbacks(deleteCallback);
+ }
+ })
+ .setActionTextColor(
+ mContext.getResources().getColor(R.color.dialer_snackbar_action_text_color))
+ .show();
+ }
+ };
+ private boolean mIsPlaying = false;
+ /** Click listener to play or pause voicemail playback. */
+ private final View.OnClickListener mStartStopButtonListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (mPresenter == null) {
+ return;
+ }
+
+ if (mIsPlaying) {
+ mPresenter.pausePlayback();
+ } else {
+ Logger.get(mContext)
+ .logImpression(DialerImpression.Type.VOICEMAIL_PLAY_AUDIO_AFTER_EXPANDING_ENTRY);
+ mPresenter.resumePlayback();
+ }
+ }
+ };
+
+ private SeekBar mPlaybackSeek;
+ private ImageButton mStartStopButton;
+ private ImageButton mPlaybackSpeakerphone;
+ private ImageButton mDeleteButton;
+ private TextView mStateText;
+ private TextView mPositionText;
+ private TextView mTotalDurationText;
+ /** Handle state changes when the user manipulates the seek bar. */
+ private final OnSeekBarChangeListener mSeekBarChangeListener =
+ new OnSeekBarChangeListener() {
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ if (mPresenter != null) {
+ mPresenter.pausePlaybackForSeeking();
+ }
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ if (mPresenter != null) {
+ mPresenter.resumePlaybackAfterSeeking(seekBar.getProgress());
+ }
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ setClipPosition(progress, seekBar.getMax());
+ // Update the seek position if user manually changed it. This makes sure position gets
+ // updated when user use volume button to seek playback in talkback mode.
+ if (fromUser) {
+ mPresenter.seek(progress);
+ }
+ }
+ };
+
+ private PositionUpdater mPositionUpdater;
+ private Drawable mVoicemailSeekHandleEnabled;
+ private Drawable mVoicemailSeekHandleDisabled;
+
+ public VoicemailPlaybackLayout(Context context) {
+ this(context, null);
+ }
+
+ public VoicemailPlaybackLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ LayoutInflater inflater =
+ (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.voicemail_playback_layout, this);
+ }
+
+ public void setViewHolder(CallLogListItemViewHolder mViewHolder) {
+ this.mViewHolder = mViewHolder;
+ }
+
+ @Override
+ public void setPresenter(VoicemailPlaybackPresenter presenter, Uri voicemailUri) {
+ mPresenter = presenter;
+ mVoicemailUri = voicemailUri;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mPlaybackSeek = (SeekBar) findViewById(R.id.playback_seek);
+ mStartStopButton = (ImageButton) findViewById(R.id.playback_start_stop);
+ mPlaybackSpeakerphone = (ImageButton) findViewById(R.id.playback_speakerphone);
+ mDeleteButton = (ImageButton) findViewById(R.id.delete_voicemail);
+
+ mStateText = (TextView) findViewById(R.id.playback_state_text);
+ mStateText.setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE);
+ mPositionText = (TextView) findViewById(R.id.playback_position_text);
+ mTotalDurationText = (TextView) findViewById(R.id.total_duration_text);
+
+ mPlaybackSeek.setOnSeekBarChangeListener(mSeekBarChangeListener);
+ mStartStopButton.setOnClickListener(mStartStopButtonListener);
+ mPlaybackSpeakerphone.setOnClickListener(mSpeakerphoneListener);
+ mDeleteButton.setOnClickListener(mDeleteButtonListener);
+
+ mPositionText.setText(formatAsMinutesAndSeconds(0));
+ mTotalDurationText.setText(formatAsMinutesAndSeconds(0));
+
+ mVoicemailSeekHandleEnabled =
+ getResources().getDrawable(R.drawable.ic_voicemail_seek_handle, mContext.getTheme());
+ mVoicemailSeekHandleDisabled =
+ getResources()
+ .getDrawable(R.drawable.ic_voicemail_seek_handle_disabled, mContext.getTheme());
+ }
+
+ @Override
+ public void onPlaybackStarted(int duration, ScheduledExecutorService executorService) {
+ mIsPlaying = true;
+
+ mStartStopButton.setImageResource(R.drawable.ic_pause);
+
+ if (mPositionUpdater != null) {
+ mPositionUpdater.stopUpdating();
+ mPositionUpdater = null;
+ }
+ mPositionUpdater = new PositionUpdater(duration, executorService);
+ mPositionUpdater.startUpdating();
+ }
+
+ @Override
+ public void onPlaybackStopped() {
+ mIsPlaying = false;
+
+ mStartStopButton.setImageResource(R.drawable.ic_play_arrow);
+
+ if (mPositionUpdater != null) {
+ mPositionUpdater.stopUpdating();
+ mPositionUpdater = null;
+ }
+ }
+
+ @Override
+ public void onPlaybackError() {
+ if (mPositionUpdater != null) {
+ mPositionUpdater.stopUpdating();
+ }
+
+ disableUiElements();
+ mStateText.setText(getString(R.string.voicemail_playback_error));
+ }
+
+ @Override
+ public void onSpeakerphoneOn(boolean on) {
+ if (on) {
+ mPlaybackSpeakerphone.setImageResource(R.drawable.ic_volume_up_24dp);
+ // Speaker is now on, tapping button will turn it off.
+ mPlaybackSpeakerphone.setContentDescription(
+ mContext.getString(R.string.voicemail_speaker_off));
+ } else {
+ mPlaybackSpeakerphone.setImageResource(R.drawable.ic_volume_down_24dp);
+ // Speaker is now off, tapping button will turn it on.
+ mPlaybackSpeakerphone.setContentDescription(
+ mContext.getString(R.string.voicemail_speaker_on));
+ }
+ }
+
+ @Override
+ public void setClipPosition(int positionMs, int durationMs) {
+ int seekBarPositionMs = Math.max(0, positionMs);
+ int seekBarMax = Math.max(seekBarPositionMs, durationMs);
+ if (mPlaybackSeek.getMax() != seekBarMax) {
+ mPlaybackSeek.setMax(seekBarMax);
+ }
+
+ mPlaybackSeek.setProgress(seekBarPositionMs);
+
+ mPositionText.setText(formatAsMinutesAndSeconds(seekBarPositionMs));
+ mTotalDurationText.setText(formatAsMinutesAndSeconds(durationMs));
+ }
+
+ @Override
+ public void setSuccess() {
+ mStateText.setText(null);
+ }
+
+ @Override
+ public void setIsFetchingContent() {
+ disableUiElements();
+ mStateText.setText(getString(R.string.voicemail_fetching_content));
+ }
+
+ @Override
+ public void setFetchContentTimeout() {
+ mStartStopButton.setEnabled(true);
+ mStateText.setText(getString(R.string.voicemail_fetching_timout));
+ }
+
+ @Override
+ public int getDesiredClipPosition() {
+ return mPlaybackSeek.getProgress();
+ }
+
+ @Override
+ public void disableUiElements() {
+ mStartStopButton.setEnabled(false);
+ resetSeekBar();
+ }
+
+ @Override
+ public void enableUiElements() {
+ mDeleteButton.setEnabled(true);
+ mStartStopButton.setEnabled(true);
+ mPlaybackSeek.setEnabled(true);
+ mPlaybackSeek.setThumb(mVoicemailSeekHandleEnabled);
+ }
+
+ @Override
+ public void resetSeekBar() {
+ mPlaybackSeek.setProgress(0);
+ mPlaybackSeek.setEnabled(false);
+ mPlaybackSeek.setThumb(mVoicemailSeekHandleDisabled);
+ }
+
+ @Override
+ public void onDeleteCall() {}
+
+ @Override
+ public void onDeleteVoicemail() {
+ mPresenter.onVoicemailDeletedInDatabase();
+ }
+
+ @Override
+ public void onGetCallDetails(PhoneCallDetails[] details) {}
+
+ private String getString(int resId) {
+ return mContext.getString(resId);
+ }
+
+ /**
+ * Formats a number of milliseconds as something that looks like {@code 00:05}.
+ *
+ * <p>We always use four digits, two for minutes two for seconds. In the very unlikely event that
+ * the voicemail duration exceeds 99 minutes, the display is capped at 99 minutes.
+ */
+ private String formatAsMinutesAndSeconds(int millis) {
+ int seconds = millis / 1000;
+ int minutes = seconds / 60;
+ seconds -= minutes * 60;
+ if (minutes > 99) {
+ minutes = 99;
+ }
+ return String.format("%02d:%02d", minutes, seconds);
+ }
+
+ @VisibleForTesting
+ public String getStateText() {
+ return mStateText.getText().toString();
+ }
+
+ /** Controls the animation of the playback slider. */
+ @ThreadSafe
+ private final class PositionUpdater implements Runnable {
+
+ /** Update rate for the slider, 30fps. */
+ private static final int SLIDER_UPDATE_PERIOD_MILLIS = 1000 / 30;
+
+ private final ScheduledExecutorService mExecutorService;
+ private final Object mLock = new Object();
+ private int mDurationMs;
+
+ @GuardedBy("mLock")
+ private ScheduledFuture<?> mScheduledFuture;
+
+ private Runnable mUpdateClipPositionRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ int currentPositionMs = 0;
+ synchronized (mLock) {
+ if (mScheduledFuture == null || mPresenter == null) {
+ // This task has been canceled. Just stop now.
+ return;
+ }
+ currentPositionMs = mPresenter.getMediaPlayerPosition();
+ }
+ setClipPosition(currentPositionMs, mDurationMs);
+ }
+ };
+
+ public PositionUpdater(int durationMs, ScheduledExecutorService executorService) {
+ mDurationMs = durationMs;
+ mExecutorService = executorService;
+ }
+
+ @Override
+ public void run() {
+ post(mUpdateClipPositionRunnable);
+ }
+
+ public void startUpdating() {
+ synchronized (mLock) {
+ cancelPendingRunnables();
+ mScheduledFuture =
+ mExecutorService.scheduleAtFixedRate(
+ this, 0, SLIDER_UPDATE_PERIOD_MILLIS, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ public void stopUpdating() {
+ synchronized (mLock) {
+ cancelPendingRunnables();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void cancelPendingRunnables() {
+ if (mScheduledFuture != null) {
+ mScheduledFuture.cancel(true);
+ mScheduledFuture = null;
+ }
+ removeCallbacks(mUpdateClipPositionRunnable);
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/voicemail/VoicemailPlaybackPresenter.java b/java/com/android/dialer/app/voicemail/VoicemailPlaybackPresenter.java
new file mode 100644
index 000000000..657022291
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/VoicemailPlaybackPresenter.java
@@ -0,0 +1,1050 @@
+/*
+ * 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.dialer.app.voicemail;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.provider.CallLog;
+import android.provider.VoicemailContract;
+import android.provider.VoicemailContract.Voicemails;
+import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.content.FileProvider;
+import android.text.TextUtils;
+import android.view.WindowManager.LayoutParams;
+import android.webkit.MimeTypeMap;
+import com.android.common.io.MoreCloseables;
+import com.android.dialer.app.R;
+import com.android.dialer.app.calllog.CallLogListItemViewHolder;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.AsyncTaskExecutor;
+import com.android.dialer.common.AsyncTaskExecutors;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.constants.Constants;
+import com.android.dialer.phonenumbercache.CallLogQuery;
+import com.google.common.io.ByteStreams;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.annotation.concurrent.NotThreadSafe;
+import javax.annotation.concurrent.ThreadSafe;
+
+/**
+ * Contains the controlling logic for a voicemail playback in the call log. It is closely coupled to
+ * assumptions about the behaviors and lifecycle of the call log, in particular in the {@link
+ * CallLogFragment} and {@link CallLogAdapter}.
+ *
+ * <p>This controls a single {@link com.android.dialer.voicemail.VoicemailPlaybackLayout}. A single
+ * instance can be reused for different such layouts, using {@link #setPlaybackView}. This is to
+ * facilitate reuse across different voicemail call log entries.
+ *
+ * <p>This class is not thread safe. The thread policy for this class is thread-confinement, all
+ * calls into this class from outside must be done from the main UI thread.
+ */
+@NotThreadSafe
+@VisibleForTesting
+@TargetApi(VERSION_CODES.M)
+public class VoicemailPlaybackPresenter
+ implements MediaPlayer.OnPreparedListener,
+ MediaPlayer.OnCompletionListener,
+ MediaPlayer.OnErrorListener {
+
+ public static final int PLAYBACK_REQUEST = 0;
+ private static final int NUMBER_OF_THREADS_IN_POOL = 2;
+ // Time to wait for content to be fetched before timing out.
+ private static final long FETCH_CONTENT_TIMEOUT_MS = 20000;
+ private static final String VOICEMAIL_URI_KEY =
+ VoicemailPlaybackPresenter.class.getName() + ".VOICEMAIL_URI";
+ private static final String IS_PREPARED_KEY =
+ VoicemailPlaybackPresenter.class.getName() + ".IS_PREPARED";
+ // If present in the saved instance bundle, we should not resume playback on create.
+ private static final String IS_PLAYING_STATE_KEY =
+ VoicemailPlaybackPresenter.class.getName() + ".IS_PLAYING_STATE_KEY";
+ // If present in the saved instance bundle, indicates where to set the playback slider.
+ private static final String CLIP_POSITION_KEY =
+ VoicemailPlaybackPresenter.class.getName() + ".CLIP_POSITION_KEY";
+ private static final String IS_SPEAKERPHONE_ON_KEY =
+ VoicemailPlaybackPresenter.class.getName() + ".IS_SPEAKER_PHONE_ON";
+ private static final String VOICEMAIL_SHARE_FILE_NAME_DATE_FORMAT = "MM-dd-yy_hhmmaa";
+ private static VoicemailPlaybackPresenter sInstance;
+ private static ScheduledExecutorService mScheduledExecutorService;
+ /**
+ * The most recently cached duration. We cache this since we don't want to keep requesting it from
+ * the player, as this can easily lead to throwing {@link IllegalStateException} (any time the
+ * player is released, it's illegal to ask for the duration).
+ */
+ private final AtomicInteger mDuration = new AtomicInteger(0);
+
+ protected Context mContext;
+ private long mRowId;
+ protected Uri mVoicemailUri;
+ protected MediaPlayer mMediaPlayer;
+ // Used to run async tasks that need to interact with the UI.
+ protected AsyncTaskExecutor mAsyncTaskExecutor;
+ private Activity mActivity;
+ private PlaybackView mView;
+ private int mPosition;
+ private boolean mIsPlaying;
+ // MediaPlayer crashes on some method calls if not prepared but does not have a method which
+ // exposes its prepared state. Store this locally, so we can check and prevent crashes.
+ private boolean mIsPrepared;
+ private boolean mIsSpeakerphoneOn;
+
+ private boolean mShouldResumePlaybackAfterSeeking;
+ /**
+ * Used to handle the result of a successful or time-out fetch result.
+ *
+ * <p>This variable is thread-contained, accessed only on the ui thread.
+ */
+ private FetchResultHandler mFetchResultHandler;
+
+ private PowerManager.WakeLock mProximityWakeLock;
+ private VoicemailAudioManager mVoicemailAudioManager;
+ private OnVoicemailDeletedListener mOnVoicemailDeletedListener;
+
+ /** Initialize variables which are activity-independent and state-independent. */
+ protected VoicemailPlaybackPresenter(Activity activity) {
+ Context context = activity.getApplicationContext();
+ mAsyncTaskExecutor = AsyncTaskExecutors.createAsyncTaskExecutor();
+ mVoicemailAudioManager = new VoicemailAudioManager(context, this);
+ PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ if (powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
+ mProximityWakeLock =
+ powerManager.newWakeLock(
+ PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "VoicemailPlaybackPresenter");
+ }
+ }
+
+ /**
+ * Obtain singleton instance of this class. Use a single instance to provide a consistent listener
+ * to the AudioManager when requesting and abandoning audio focus.
+ *
+ * <p>Otherwise, after rotation the previous listener will still be active but a new listener will
+ * be provided to calls to the AudioManager, which is bad. For example, abandoning audio focus
+ * with the new listeners results in an AUDIO_FOCUS_GAIN callback to the previous listener, which
+ * is the opposite of the intended behavior.
+ */
+ @MainThread
+ public static VoicemailPlaybackPresenter getInstance(
+ Activity activity, Bundle savedInstanceState) {
+ if (sInstance == null) {
+ sInstance = new VoicemailPlaybackPresenter(activity);
+ }
+
+ sInstance.init(activity, savedInstanceState);
+ return sInstance;
+ }
+
+ private static synchronized ScheduledExecutorService getScheduledExecutorServiceInstance() {
+ if (mScheduledExecutorService == null) {
+ mScheduledExecutorService = Executors.newScheduledThreadPool(NUMBER_OF_THREADS_IN_POOL);
+ }
+ return mScheduledExecutorService;
+ }
+
+ /** Update variables which are activity-dependent or state-dependent. */
+ @MainThread
+ protected void init(Activity activity, Bundle savedInstanceState) {
+ Assert.isMainThread();
+ mActivity = activity;
+ mContext = activity;
+
+ if (savedInstanceState != null) {
+ // Restores playback state when activity is recreated, such as after rotation.
+ mVoicemailUri = savedInstanceState.getParcelable(VOICEMAIL_URI_KEY);
+ mIsPrepared = savedInstanceState.getBoolean(IS_PREPARED_KEY);
+ mPosition = savedInstanceState.getInt(CLIP_POSITION_KEY, 0);
+ mIsPlaying = savedInstanceState.getBoolean(IS_PLAYING_STATE_KEY, false);
+ mIsSpeakerphoneOn = savedInstanceState.getBoolean(IS_SPEAKERPHONE_ON_KEY, false);
+ }
+
+ if (mMediaPlayer == null) {
+ mIsPrepared = false;
+ mIsPlaying = false;
+ }
+
+ if (mActivity != null) {
+ if (isPlaying()) {
+ mActivity.getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
+ } else {
+ mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+ }
+ }
+
+ /** Must be invoked when the parent Activity is saving it state. */
+ public void onSaveInstanceState(Bundle outState) {
+ if (mView != null) {
+ outState.putParcelable(VOICEMAIL_URI_KEY, mVoicemailUri);
+ outState.putBoolean(IS_PREPARED_KEY, mIsPrepared);
+ outState.putInt(CLIP_POSITION_KEY, mView.getDesiredClipPosition());
+ outState.putBoolean(IS_PLAYING_STATE_KEY, mIsPlaying);
+ outState.putBoolean(IS_SPEAKERPHONE_ON_KEY, mIsSpeakerphoneOn);
+ }
+ }
+
+ /** Specify the view which this presenter controls and the voicemail to prepare to play. */
+ public void setPlaybackView(
+ PlaybackView view, long rowId, Uri voicemailUri, final boolean startPlayingImmediately) {
+ mRowId = rowId;
+ mView = view;
+ mView.setPresenter(this, voicemailUri);
+ mView.onSpeakerphoneOn(mIsSpeakerphoneOn);
+
+ // Handles cases where the same entry is binded again when scrolling in list, or where
+ // the MediaPlayer was retained after an orientation change.
+ if (mMediaPlayer != null && mIsPrepared && voicemailUri.equals(mVoicemailUri)) {
+ // If the voicemail card was rebinded, we need to set the position to the appropriate
+ // point. Since we retain the media player, we can just set it to the position of the
+ // media player.
+ mPosition = mMediaPlayer.getCurrentPosition();
+ onPrepared(mMediaPlayer);
+ } else {
+ if (!voicemailUri.equals(mVoicemailUri)) {
+ mVoicemailUri = voicemailUri;
+ mPosition = 0;
+ }
+ /*
+ * Check to see if the content field in the DB is set. If set, we proceed to
+ * prepareContent() method. We get the duration of the voicemail from the query and set
+ * it if the content is not available.
+ */
+ checkForContent(
+ new OnContentCheckedListener() {
+ @Override
+ public void onContentChecked(boolean hasContent) {
+ if (hasContent) {
+ prepareContent();
+ } else {
+ if (startPlayingImmediately) {
+ requestContent(PLAYBACK_REQUEST);
+ }
+ if (mView != null) {
+ mView.resetSeekBar();
+ mView.setClipPosition(0, mDuration.get());
+ }
+ }
+ }
+ });
+
+ if (startPlayingImmediately) {
+ // Since setPlaybackView can get called during the view binding process, we don't
+ // want to reset mIsPlaying to false if the user is currently playing the
+ // voicemail and the view is rebound.
+ mIsPlaying = startPlayingImmediately;
+ }
+ }
+ }
+
+ /** Reset the presenter for playback back to its original state. */
+ public void resetAll() {
+ pausePresenter(true);
+
+ mView = null;
+ mVoicemailUri = null;
+ }
+
+ /**
+ * When navigating away from voicemail playback, we need to release the media player, pause the UI
+ * and save the position.
+ *
+ * @param reset {@code true} if we want to reset the position of the playback, {@code false} if we
+ * want to retain the current position (in case we return to the voicemail).
+ */
+ public void pausePresenter(boolean reset) {
+ pausePlayback();
+ if (mMediaPlayer != null) {
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ }
+
+ disableProximitySensor(false /* waitForFarState */);
+
+ mIsPrepared = false;
+ mIsPlaying = false;
+
+ if (reset) {
+ // We want to reset the position whether or not the view is valid.
+ mPosition = 0;
+ }
+
+ if (mView != null) {
+ mView.onPlaybackStopped();
+ if (reset) {
+ mView.setClipPosition(0, mDuration.get());
+ } else {
+ mPosition = mView.getDesiredClipPosition();
+ }
+ }
+ }
+
+ /** Must be invoked when the parent activity is resumed. */
+ public void onResume() {
+ mVoicemailAudioManager.registerReceivers();
+ }
+
+ /** Must be invoked when the parent activity is paused. */
+ public void onPause() {
+ mVoicemailAudioManager.unregisterReceivers();
+
+ if (mActivity != null && mIsPrepared && mActivity.isChangingConfigurations()) {
+ // If an configuration change triggers the pause, retain the MediaPlayer.
+ LogUtil.d("VoicemailPlaybackPresenter.onPause", "configuration changed.");
+ return;
+ }
+
+ // Release the media player, otherwise there may be failures.
+ pausePresenter(false);
+ }
+
+ /** Must be invoked when the parent activity is destroyed. */
+ public void onDestroy() {
+ // Clear references to avoid leaks from the singleton instance.
+ mActivity = null;
+ mContext = null;
+
+ if (mScheduledExecutorService != null) {
+ mScheduledExecutorService.shutdown();
+ mScheduledExecutorService = null;
+ }
+
+ if (mFetchResultHandler != null) {
+ mFetchResultHandler.destroy();
+ mFetchResultHandler = null;
+ }
+ }
+
+ /** Checks to see if we have content available for this voicemail. */
+ protected void checkForContent(final OnContentCheckedListener callback) {
+ mAsyncTaskExecutor.submit(
+ Tasks.CHECK_FOR_CONTENT,
+ new AsyncTask<Void, Void, Boolean>() {
+ @Override
+ public Boolean doInBackground(Void... params) {
+ return queryHasContent(mVoicemailUri);
+ }
+
+ @Override
+ public void onPostExecute(Boolean hasContent) {
+ callback.onContentChecked(hasContent);
+ }
+ });
+ }
+
+ private boolean queryHasContent(Uri voicemailUri) {
+ if (voicemailUri == null || mContext == null) {
+ return false;
+ }
+
+ ContentResolver contentResolver = mContext.getContentResolver();
+ Cursor cursor = contentResolver.query(voicemailUri, null, null, null, null);
+ try {
+ if (cursor != null && cursor.moveToNext()) {
+ int duration = cursor.getInt(cursor.getColumnIndex(VoicemailContract.Voicemails.DURATION));
+ // Convert database duration (seconds) into mDuration (milliseconds)
+ mDuration.set(duration > 0 ? duration * 1000 : 0);
+ return cursor.getInt(cursor.getColumnIndex(VoicemailContract.Voicemails.HAS_CONTENT)) == 1;
+ }
+ } finally {
+ MoreCloseables.closeQuietly(cursor);
+ }
+ return false;
+ }
+
+ /**
+ * Makes a broadcast request to ask that a voicemail source fetch this content.
+ *
+ * <p>This method <b>must be called on the ui thread</b>.
+ *
+ * <p>This method will be called when we realise that we don't have content for this voicemail. It
+ * will trigger a broadcast to request that the content be downloaded. It will add a listener to
+ * the content resolver so that it will be notified when the has_content field changes. It will
+ * also set a timer. If the has_content field changes to true within the allowed time, we will
+ * proceed to {@link #prepareContent()}. If the has_content field does not become true within the
+ * allowed time, we will update the ui to reflect the fact that content was not available.
+ *
+ * @return whether issued request to fetch content
+ */
+ protected boolean requestContent(int code) {
+ if (mContext == null || mVoicemailUri == null) {
+ return false;
+ }
+
+ FetchResultHandler tempFetchResultHandler =
+ new FetchResultHandler(new Handler(), mVoicemailUri, code);
+
+ switch (code) {
+ default:
+ if (mFetchResultHandler != null) {
+ mFetchResultHandler.destroy();
+ }
+ mView.setIsFetchingContent();
+ mFetchResultHandler = tempFetchResultHandler;
+ break;
+ }
+
+ mAsyncTaskExecutor.submit(
+ Tasks.SEND_FETCH_REQUEST,
+ new AsyncTask<Void, Void, Void>() {
+
+ @Override
+ protected Void doInBackground(Void... voids) {
+ try (Cursor cursor =
+ mContext
+ .getContentResolver()
+ .query(
+ mVoicemailUri,
+ new String[] {Voicemails.SOURCE_PACKAGE},
+ null,
+ null,
+ null)) {
+ String sourcePackage;
+ if (!hasContent(cursor)) {
+ LogUtil.e(
+ "VoicemailPlaybackPresenter.requestContent",
+ "mVoicemailUri does not return a SOURCE_PACKAGE");
+ sourcePackage = null;
+ } else {
+ sourcePackage = cursor.getString(0);
+ }
+ // Send voicemail fetch request.
+ Intent intent = new Intent(VoicemailContract.ACTION_FETCH_VOICEMAIL, mVoicemailUri);
+ intent.setPackage(sourcePackage);
+ LogUtil.i(
+ "VoicemailPlaybackPresenter.requestContent",
+ "Sending ACTION_FETCH_VOICEMAIL to " + sourcePackage);
+ mContext.sendBroadcast(intent);
+ }
+ return null;
+ }
+ });
+ return true;
+ }
+
+ /**
+ * Prepares the voicemail content for playback.
+ *
+ * <p>This method will be called once we know that our voicemail has content (according to the
+ * content provider). this method asynchronously tries to prepare the data source through the
+ * media player. If preparation is successful, the media player will {@link #onPrepared()}, and it
+ * will call {@link #onError()} otherwise.
+ */
+ protected void prepareContent() {
+ if (mView == null) {
+ return;
+ }
+ LogUtil.d("VoicemailPlaybackPresenter.prepareContent", null);
+
+ // Release the previous media player, otherwise there may be failures.
+ if (mMediaPlayer != null) {
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ }
+
+ mView.disableUiElements();
+ mIsPrepared = false;
+
+ try {
+ mMediaPlayer = new MediaPlayer();
+ mMediaPlayer.setOnPreparedListener(this);
+ mMediaPlayer.setOnErrorListener(this);
+ mMediaPlayer.setOnCompletionListener(this);
+
+ mMediaPlayer.reset();
+ mMediaPlayer.setDataSource(mContext, mVoicemailUri);
+ mMediaPlayer.setAudioStreamType(VoicemailAudioManager.PLAYBACK_STREAM);
+ mMediaPlayer.prepareAsync();
+ } catch (IOException e) {
+ handleError(e);
+ }
+ }
+
+ /**
+ * Once the media player is prepared, enables the UI and adopts the appropriate playback state.
+ */
+ @Override
+ public void onPrepared(MediaPlayer mp) {
+ if (mView == null || mContext == null) {
+ return;
+ }
+ LogUtil.d("VoicemailPlaybackPresenter.onPrepared", null);
+ mIsPrepared = true;
+
+ mDuration.set(mMediaPlayer.getDuration());
+
+ LogUtil.d("VoicemailPlaybackPresenter.onPrepared", "mPosition=" + mPosition);
+ mView.setClipPosition(mPosition, mDuration.get());
+ mView.enableUiElements();
+ mView.setSuccess();
+ mMediaPlayer.seekTo(mPosition);
+
+ if (mIsPlaying) {
+ resumePlayback();
+ } else {
+ pausePlayback();
+ }
+ }
+
+ /**
+ * Invoked if preparing the media player fails, for example, if file is missing or the voicemail
+ * is an unknown file format that can't be played.
+ */
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ handleError(new IllegalStateException("MediaPlayer error listener invoked: " + extra));
+ return true;
+ }
+
+ protected void handleError(Exception e) {
+ LogUtil.e("VoicemailPlaybackPresenter.handlerError", "could not play voicemail", e);
+
+ if (mIsPrepared) {
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ mIsPrepared = false;
+ }
+
+ if (mView != null) {
+ mView.onPlaybackError();
+ }
+
+ mPosition = 0;
+ mIsPlaying = false;
+ }
+
+ /** After done playing the voicemail clip, reset the clip position to the start. */
+ @Override
+ public void onCompletion(MediaPlayer mediaPlayer) {
+ pausePlayback();
+
+ // Reset the seekbar position to the beginning.
+ mPosition = 0;
+ if (mView != null) {
+ mediaPlayer.seekTo(0);
+ mView.setClipPosition(0, mDuration.get());
+ }
+ }
+
+ /**
+ * Only play voicemail when audio focus is granted. When it is lost (usually by another
+ * application requesting focus), pause playback. Audio focus gain/lost only triggers the focus is
+ * requested. Audio focus is requested when the user pressed play and abandoned when the user
+ * pressed pause or the audio has finished. Losing focus should not abandon focus as the voicemail
+ * should resume once the focus is returned.
+ *
+ * @param gainedFocus {@code true} if the audio focus was gained, {@code} false otherwise.
+ */
+ public void onAudioFocusChange(boolean gainedFocus) {
+ if (mIsPlaying == gainedFocus) {
+ // Nothing new here, just exit.
+ return;
+ }
+
+ if (gainedFocus) {
+ resumePlayback();
+ } else {
+ pausePlayback(true);
+ }
+ }
+
+ /**
+ * Resumes voicemail playback at the clip position stored by the presenter. Null-op if already
+ * playing.
+ */
+ public void resumePlayback() {
+ if (mView == null) {
+ return;
+ }
+
+ if (!mIsPrepared) {
+ /*
+ * Check content before requesting content to avoid duplicated requests. It is possible
+ * that the UI doesn't know content has arrived if the fetch took too long causing a
+ * timeout, but succeeded.
+ */
+ checkForContent(
+ new OnContentCheckedListener() {
+ @Override
+ public void onContentChecked(boolean hasContent) {
+ if (!hasContent) {
+ // No local content, download from server. Queue playing if the request was
+ // issued,
+ mIsPlaying = requestContent(PLAYBACK_REQUEST);
+ } else {
+ // Queue playing once the media play loaded the content.
+ mIsPlaying = true;
+ prepareContent();
+ }
+ }
+ });
+ return;
+ }
+
+ mIsPlaying = true;
+
+ mActivity.getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+ if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
+ // Clamp the start position between 0 and the duration.
+ mPosition = Math.max(0, Math.min(mPosition, mDuration.get()));
+
+ mMediaPlayer.seekTo(mPosition);
+
+ try {
+ // Grab audio focus.
+ // Can throw RejectedExecutionException.
+ mVoicemailAudioManager.requestAudioFocus();
+ mMediaPlayer.start();
+ setSpeakerphoneOn(mIsSpeakerphoneOn);
+ mVoicemailAudioManager.setSpeakerphoneOn(mIsSpeakerphoneOn);
+ } catch (RejectedExecutionException e) {
+ handleError(e);
+ }
+ }
+
+ LogUtil.d("VoicemailPlaybackPresenter.resumePlayback", "resumed playback at %d.", mPosition);
+ mView.onPlaybackStarted(mDuration.get(), getScheduledExecutorServiceInstance());
+ }
+
+ /** Pauses voicemail playback at the current position. Null-op if already paused. */
+ public void pausePlayback() {
+ pausePlayback(false);
+ }
+
+ private void pausePlayback(boolean keepFocus) {
+ if (!mIsPrepared) {
+ return;
+ }
+
+ mIsPlaying = false;
+
+ if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
+ mMediaPlayer.pause();
+ }
+
+ mPosition = mMediaPlayer == null ? 0 : mMediaPlayer.getCurrentPosition();
+
+ LogUtil.d("VoicemailPlaybackPresenter.pausePlayback", "paused playback at %d.", mPosition);
+
+ if (mView != null) {
+ mView.onPlaybackStopped();
+ }
+
+ if (!keepFocus) {
+ mVoicemailAudioManager.abandonAudioFocus();
+ }
+ if (mActivity != null) {
+ mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+ disableProximitySensor(true /* waitForFarState */);
+ }
+
+ /**
+ * Pauses playback when the user starts seeking the position, and notes whether the voicemail is
+ * playing to know whether to resume playback once the user selects a new position.
+ */
+ public void pausePlaybackForSeeking() {
+ if (mMediaPlayer != null) {
+ mShouldResumePlaybackAfterSeeking = mMediaPlayer.isPlaying();
+ }
+ pausePlayback(true);
+ }
+
+ public void resumePlaybackAfterSeeking(int desiredPosition) {
+ mPosition = desiredPosition;
+ if (mShouldResumePlaybackAfterSeeking) {
+ mShouldResumePlaybackAfterSeeking = false;
+ resumePlayback();
+ }
+ }
+
+ /**
+ * Seek to position. This is called when user manually seek the playback. It could be either by
+ * touch or volume button while in talkback mode.
+ */
+ public void seek(int position) {
+ mPosition = position;
+ mMediaPlayer.seekTo(mPosition);
+ }
+
+ private void enableProximitySensor() {
+ if (mProximityWakeLock == null
+ || mIsSpeakerphoneOn
+ || !mIsPrepared
+ || mMediaPlayer == null
+ || !mMediaPlayer.isPlaying()) {
+ return;
+ }
+
+ if (!mProximityWakeLock.isHeld()) {
+ LogUtil.i(
+ "VoicemailPlaybackPresenter.enableProximitySensor", "acquiring proximity wake lock");
+ mProximityWakeLock.acquire();
+ } else {
+ LogUtil.i(
+ "VoicemailPlaybackPresenter.enableProximitySensor",
+ "proximity wake lock already acquired");
+ }
+ }
+
+ private void disableProximitySensor(boolean waitForFarState) {
+ if (mProximityWakeLock == null) {
+ return;
+ }
+ if (mProximityWakeLock.isHeld()) {
+ LogUtil.i(
+ "VoicemailPlaybackPresenter.disableProximitySensor", "releasing proximity wake lock");
+ int flags = waitForFarState ? PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY : 0;
+ mProximityWakeLock.release(flags);
+ } else {
+ LogUtil.i(
+ "VoicemailPlaybackPresenter.disableProximitySensor",
+ "proximity wake lock already released");
+ }
+ }
+
+ /** This is for use by UI interactions only. It simplifies UI logic. */
+ public void toggleSpeakerphone() {
+ mVoicemailAudioManager.setSpeakerphoneOn(!mIsSpeakerphoneOn);
+ setSpeakerphoneOn(!mIsSpeakerphoneOn);
+ }
+
+ public void setOnVoicemailDeletedListener(OnVoicemailDeletedListener listener) {
+ mOnVoicemailDeletedListener = listener;
+ }
+
+ public int getMediaPlayerPosition() {
+ return mIsPrepared && mMediaPlayer != null ? mMediaPlayer.getCurrentPosition() : 0;
+ }
+
+ void onVoicemailDeleted(CallLogListItemViewHolder viewHolder) {
+ if (mOnVoicemailDeletedListener != null) {
+ mOnVoicemailDeletedListener.onVoicemailDeleted(viewHolder, mVoicemailUri);
+ }
+ }
+
+ void onVoicemailDeleteUndo(int adapterPosition) {
+ if (mOnVoicemailDeletedListener != null) {
+ mOnVoicemailDeletedListener.onVoicemailDeleteUndo(mRowId, adapterPosition, mVoicemailUri);
+ }
+ }
+
+ void onVoicemailDeletedInDatabase() {
+ if (mOnVoicemailDeletedListener != null) {
+ mOnVoicemailDeletedListener.onVoicemailDeletedInDatabase(mRowId, mVoicemailUri);
+ }
+ }
+
+ @VisibleForTesting
+ public boolean isPlaying() {
+ return mIsPlaying;
+ }
+
+ @VisibleForTesting
+ public boolean isSpeakerphoneOn() {
+ return mIsSpeakerphoneOn;
+ }
+
+ /**
+ * This method only handles app-level changes to the speakerphone. Audio layer changes should be
+ * handled separately. This is so that the VoicemailAudioManager can trigger changes to the
+ * presenter without the presenter triggering the audio manager and duplicating actions.
+ */
+ public void setSpeakerphoneOn(boolean on) {
+ if (mView == null) {
+ return;
+ }
+
+ mView.onSpeakerphoneOn(on);
+
+ mIsSpeakerphoneOn = on;
+
+ // This should run even if speakerphone is not being toggled because we may be switching
+ // from earpiece to headphone and vise versa. Also upon initial setup the default audio
+ // source is the earpiece, so we want to trigger the proximity sensor.
+ if (mIsPlaying) {
+ if (on || mVoicemailAudioManager.isWiredHeadsetPluggedIn()) {
+ disableProximitySensor(false /* waitForFarState */);
+ } else {
+ enableProximitySensor();
+ }
+ }
+ }
+
+ @VisibleForTesting
+ public void clearInstance() {
+ sInstance = null;
+ }
+
+ /**
+ * Share voicemail to be opened by user selected apps. This method will collect information, copy
+ * voicemail to a temporary file in background and launch a chooser intent to share it.
+ */
+ @TargetApi(VERSION_CODES.M)
+ public void shareVoicemail() {
+ mAsyncTaskExecutor.submit(
+ Tasks.SHARE_VOICEMAIL,
+ new AsyncTask<Void, Void, Uri>() {
+ @Nullable
+ @Override
+ protected Uri doInBackground(Void... params) {
+ ContentResolver contentResolver = mContext.getContentResolver();
+ try (Cursor callLogInfo = getCallLogInfoCursor(contentResolver, mVoicemailUri);
+ Cursor contentInfo = getContentInfoCursor(contentResolver, mVoicemailUri)) {
+
+ if (hasContent(callLogInfo) && hasContent(contentInfo)) {
+ String cachedName = callLogInfo.getString(CallLogQuery.CACHED_NAME);
+ String number =
+ contentInfo.getString(
+ contentInfo.getColumnIndex(VoicemailContract.Voicemails.NUMBER));
+ long date =
+ contentInfo.getLong(
+ contentInfo.getColumnIndex(VoicemailContract.Voicemails.DATE));
+ String mimeType =
+ contentInfo.getString(
+ contentInfo.getColumnIndex(VoicemailContract.Voicemails.MIME_TYPE));
+
+ // Copy voicemail content to a new file.
+ // Please see reference in third_party/java_src/android_app/dialer/java/com/android/
+ // dialer/app/res/xml/file_paths.xml for correct cache directory name.
+ File parentDir = new File(mContext.getCacheDir(), "my_cache");
+ if (!parentDir.exists()) {
+ parentDir.mkdirs();
+ }
+ File temporaryVoicemailFile =
+ new File(parentDir, getFileName(cachedName, number, mimeType, date));
+
+ try (InputStream inputStream = contentResolver.openInputStream(mVoicemailUri);
+ OutputStream outputStream =
+ contentResolver.openOutputStream(Uri.fromFile(temporaryVoicemailFile))) {
+ if (inputStream != null && outputStream != null) {
+ ByteStreams.copy(inputStream, outputStream);
+ return FileProvider.getUriForFile(
+ mContext,
+ Constants.get().getFileProviderAuthority(),
+ temporaryVoicemailFile);
+ }
+ } catch (IOException e) {
+ LogUtil.e(
+ "VoicemailAsyncTaskUtil.shareVoicemail",
+ "failed to copy voicemail content to new file: ",
+ e);
+ }
+ return null;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Uri uri) {
+ if (uri == null) {
+ LogUtil.e("VoicemailAsyncTaskUtil.shareVoicemail", "failed to get voicemail");
+ } else {
+ mContext.startActivity(
+ Intent.createChooser(
+ getShareIntent(mContext, uri),
+ mContext.getResources().getText(R.string.call_log_action_share_voicemail)));
+ }
+ }
+ });
+ }
+
+ private static String getFileName(String cachedName, String number, String mimeType, long date) {
+ String callerName = TextUtils.isEmpty(cachedName) ? number : cachedName;
+ SimpleDateFormat simpleDateFormat =
+ new SimpleDateFormat(VOICEMAIL_SHARE_FILE_NAME_DATE_FORMAT, Locale.getDefault());
+
+ String fileExtension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
+
+ return callerName
+ + "_"
+ + simpleDateFormat.format(new Date(date))
+ + (TextUtils.isEmpty(fileExtension) ? "" : "." + fileExtension);
+ }
+
+ private static Intent getShareIntent(Context context, Uri voicemailFileUri) {
+ Intent shareIntent = new Intent();
+ shareIntent.setAction(Intent.ACTION_SEND);
+ shareIntent.putExtra(Intent.EXTRA_STREAM, voicemailFileUri);
+ shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ shareIntent.setType(context.getContentResolver().getType(voicemailFileUri));
+ return shareIntent;
+ }
+
+ private static boolean hasContent(@Nullable Cursor cursor) {
+ return cursor != null && cursor.moveToFirst();
+ }
+
+ @Nullable
+ private static Cursor getCallLogInfoCursor(ContentResolver contentResolver, Uri voicemailUri) {
+ return contentResolver.query(
+ ContentUris.withAppendedId(
+ CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, ContentUris.parseId(voicemailUri)),
+ CallLogQuery.getProjection(),
+ null,
+ null,
+ null);
+ }
+
+ @Nullable
+ private static Cursor getContentInfoCursor(ContentResolver contentResolver, Uri voicemailUri) {
+ return contentResolver.query(
+ voicemailUri,
+ new String[] {
+ VoicemailContract.Voicemails._ID,
+ VoicemailContract.Voicemails.NUMBER,
+ VoicemailContract.Voicemails.DATE,
+ VoicemailContract.Voicemails.MIME_TYPE,
+ },
+ null,
+ null,
+ null);
+ }
+
+ /** The enumeration of {@link AsyncTask} objects we use in this class. */
+ public enum Tasks {
+ CHECK_FOR_CONTENT,
+ CHECK_CONTENT_AFTER_CHANGE,
+ SHARE_VOICEMAIL,
+ SEND_FETCH_REQUEST
+ }
+
+ /** Contract describing the behaviour we need from the ui we are controlling. */
+ public interface PlaybackView {
+
+ int getDesiredClipPosition();
+
+ void disableUiElements();
+
+ void enableUiElements();
+
+ void onPlaybackError();
+
+ void onPlaybackStarted(int duration, ScheduledExecutorService executorService);
+
+ void onPlaybackStopped();
+
+ void onSpeakerphoneOn(boolean on);
+
+ void setClipPosition(int clipPositionInMillis, int clipLengthInMillis);
+
+ void setSuccess();
+
+ void setFetchContentTimeout();
+
+ void setIsFetchingContent();
+
+ void setPresenter(VoicemailPlaybackPresenter presenter, Uri voicemailUri);
+
+ void resetSeekBar();
+ }
+
+ public interface OnVoicemailDeletedListener {
+
+ void onVoicemailDeleted(CallLogListItemViewHolder viewHolder, Uri uri);
+
+ void onVoicemailDeleteUndo(long rowId, int adaptorPosition, Uri uri);
+
+ void onVoicemailDeletedInDatabase(long rowId, Uri uri);
+ }
+
+ protected interface OnContentCheckedListener {
+
+ void onContentChecked(boolean hasContent);
+ }
+
+ @ThreadSafe
+ private class FetchResultHandler extends ContentObserver implements Runnable {
+
+ private final Handler mFetchResultHandler;
+ private final Uri mVoicemailUri;
+ private AtomicBoolean mIsWaitingForResult = new AtomicBoolean(true);
+
+ public FetchResultHandler(Handler handler, Uri uri, int code) {
+ super(handler);
+ mFetchResultHandler = handler;
+ mVoicemailUri = uri;
+ if (mContext != null) {
+ mContext.getContentResolver().registerContentObserver(mVoicemailUri, false, this);
+ mFetchResultHandler.postDelayed(this, FETCH_CONTENT_TIMEOUT_MS);
+ }
+ }
+
+ /** Stop waiting for content and notify UI if {@link FETCH_CONTENT_TIMEOUT_MS} has elapsed. */
+ @Override
+ public void run() {
+ if (mIsWaitingForResult.getAndSet(false) && mContext != null) {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ if (mView != null) {
+ mView.setFetchContentTimeout();
+ }
+ }
+ }
+
+ public void destroy() {
+ if (mIsWaitingForResult.getAndSet(false) && mContext != null) {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ mFetchResultHandler.removeCallbacks(this);
+ }
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mAsyncTaskExecutor.submit(
+ Tasks.CHECK_CONTENT_AFTER_CHANGE,
+ new AsyncTask<Void, Void, Boolean>() {
+
+ @Override
+ public Boolean doInBackground(Void... params) {
+ return queryHasContent(mVoicemailUri);
+ }
+
+ @Override
+ public void onPostExecute(Boolean hasContent) {
+ if (hasContent && mContext != null && mIsWaitingForResult.getAndSet(false)) {
+ mContext.getContentResolver().unregisterContentObserver(FetchResultHandler.this);
+ prepareContent();
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/voicemail/WiredHeadsetManager.java b/java/com/android/dialer/app/voicemail/WiredHeadsetManager.java
new file mode 100644
index 000000000..24d4c6ff7
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/WiredHeadsetManager.java
@@ -0,0 +1,88 @@
+/*
+ * 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.dialer.app.voicemail;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.util.Log;
+
+/** Listens for and caches headset state. */
+class WiredHeadsetManager {
+
+ private static final String TAG = WiredHeadsetManager.class.getSimpleName();
+ private final WiredHeadsetBroadcastReceiver mReceiver;
+ private boolean mIsPluggedIn;
+ private Listener mListener;
+ private Context mContext;
+
+ WiredHeadsetManager(Context context) {
+ mContext = context;
+ mReceiver = new WiredHeadsetBroadcastReceiver();
+
+ AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mIsPluggedIn = audioManager.isWiredHeadsetOn();
+ }
+
+ void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ boolean isPluggedIn() {
+ return mIsPluggedIn;
+ }
+
+ void registerReceiver() {
+ // Register for misc other intent broadcasts.
+ IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
+ mContext.registerReceiver(mReceiver, intentFilter);
+ }
+
+ void unregisterReceiver() {
+ mContext.unregisterReceiver(mReceiver);
+ }
+
+ private void onHeadsetPluggedInChanged(boolean isPluggedIn) {
+ if (mIsPluggedIn != isPluggedIn) {
+ Log.v(TAG, "onHeadsetPluggedInChanged, mIsPluggedIn: " + mIsPluggedIn + " -> " + isPluggedIn);
+ boolean oldIsPluggedIn = mIsPluggedIn;
+ mIsPluggedIn = isPluggedIn;
+ if (mListener != null) {
+ mListener.onWiredHeadsetPluggedInChanged(oldIsPluggedIn, mIsPluggedIn);
+ }
+ }
+ }
+
+ interface Listener {
+
+ void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn);
+ }
+
+ /** Receiver for wired headset plugged and unplugged events. */
+ private class WiredHeadsetBroadcastReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (AudioManager.ACTION_HEADSET_PLUG.equals(intent.getAction())) {
+ boolean isPluggedIn = intent.getIntExtra("state", 0) == 1;
+ Log.v(TAG, "ACTION_HEADSET_PLUG event, plugged in: " + isPluggedIn);
+ onHeadsetPluggedInChanged(isPluggedIn);
+ }
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/voicemail/error/AndroidManifest.xml b/java/com/android/dialer/app/voicemail/error/AndroidManifest.xml
new file mode 100644
index 000000000..65d043034
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/error/AndroidManifest.xml
@@ -0,0 +1,5 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.dialer.app.voicemail.error">
+
+ <uses-permission android:name="android.permission.CALL_PHONE"/>
+</manifest>
diff --git a/java/com/android/dialer/app/voicemail/error/OmtpVoicemailMessageCreator.java b/java/com/android/dialer/app/voicemail/error/OmtpVoicemailMessageCreator.java
new file mode 100644
index 000000000..e36406d17
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/error/OmtpVoicemailMessageCreator.java
@@ -0,0 +1,177 @@
+/*
+ * 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.dialer.app.voicemail.error;
+
+import android.content.Context;
+import android.provider.VoicemailContract.Status;
+import android.support.annotation.Nullable;
+import com.android.dialer.app.voicemail.error.VoicemailErrorMessage.Action;
+import com.android.dialer.common.LogUtil;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Create error message from {@link VoicemailStatus} for OMTP visual voicemail. This is also the
+ * default behavior if other message creator does not handle the status.
+ */
+public class OmtpVoicemailMessageCreator {
+
+ private static final float QUOTA_NEAR_FULL_THRESHOLD = 0.9f;
+ private static final float QUOTA_FULL_THRESHOLD = 0.99f;
+
+ @Nullable
+ public static VoicemailErrorMessage create(Context context, VoicemailStatus status) {
+ if (Status.CONFIGURATION_STATE_OK == status.configurationState
+ && Status.DATA_CHANNEL_STATE_OK == status.dataChannelState
+ && Status.NOTIFICATION_CHANNEL_STATE_OK == status.notificationChannelState) {
+
+ return checkQuota(context, status);
+ }
+ // Initial state when the source is activating. Other error might be written into data and
+ // notification channel during activation.
+ if (Status.CONFIGURATION_STATE_CONFIGURING == status.configurationState
+ && Status.DATA_CHANNEL_STATE_OK == status.dataChannelState
+ && Status.NOTIFICATION_CHANNEL_STATE_OK == status.notificationChannelState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.voicemail_error_activating_title),
+ context.getString(R.string.voicemail_error_activating_message),
+ VoicemailErrorMessage.createCallVoicemailAction(context));
+ }
+
+ if (Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION == status.notificationChannelState) {
+ return createNoSignalMessage(context, status);
+ }
+
+ if (Status.CONFIGURATION_STATE_FAILED == status.configurationState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.voicemail_error_activation_failed_title),
+ context.getString(R.string.voicemail_error_activation_failed_message),
+ VoicemailErrorMessage.createCallVoicemailAction(context),
+ VoicemailErrorMessage.createRetryAction(context, status));
+ }
+
+ if (Status.DATA_CHANNEL_STATE_NO_CONNECTION == status.dataChannelState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.voicemail_error_no_data_title),
+ context.getString(R.string.voicemail_error_no_data_message),
+ VoicemailErrorMessage.createCallVoicemailAction(context),
+ VoicemailErrorMessage.createRetryAction(context, status));
+ }
+
+ if (Status.DATA_CHANNEL_STATE_NO_CONNECTION_CELLULAR_REQUIRED == status.dataChannelState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.voicemail_error_no_data_title),
+ context.getString(R.string.voicemail_error_no_data_cellular_required_message),
+ VoicemailErrorMessage.createCallVoicemailAction(context),
+ VoicemailErrorMessage.createRetryAction(context, status));
+ }
+
+ if (Status.DATA_CHANNEL_STATE_BAD_CONFIGURATION == status.dataChannelState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.voicemail_error_bad_config_title),
+ context.getString(R.string.voicemail_error_bad_config_message),
+ VoicemailErrorMessage.createCallVoicemailAction(context),
+ VoicemailErrorMessage.createRetryAction(context, status));
+ }
+
+ if (Status.DATA_CHANNEL_STATE_COMMUNICATION_ERROR == status.dataChannelState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.voicemail_error_communication_title),
+ context.getString(R.string.voicemail_error_communication_message),
+ VoicemailErrorMessage.createCallVoicemailAction(context),
+ VoicemailErrorMessage.createRetryAction(context, status));
+ }
+
+ if (Status.DATA_CHANNEL_STATE_SERVER_ERROR == status.dataChannelState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.voicemail_error_server_title),
+ context.getString(R.string.voicemail_error_server_message),
+ VoicemailErrorMessage.createCallVoicemailAction(context),
+ VoicemailErrorMessage.createRetryAction(context, status));
+ }
+
+ if (Status.DATA_CHANNEL_STATE_SERVER_CONNECTION_ERROR == status.dataChannelState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.voicemail_error_server_connection_title),
+ context.getString(R.string.voicemail_error_server_connection_message),
+ VoicemailErrorMessage.createCallVoicemailAction(context),
+ VoicemailErrorMessage.createRetryAction(context, status));
+ }
+
+ // This should be an assertion error, but there's a bug in NYC-DR (b/31069259) that will
+ // sometimes give status mixed from multiple SIMs. There's no meaningful message to be displayed
+ // from it, so just suppress the message.
+ LogUtil.e("OmtpVoicemailMessageCreator.create", "Unhandled status: " + status);
+ return null;
+ }
+
+ @Nullable
+ private static VoicemailErrorMessage checkQuota(Context context, VoicemailStatus status) {
+ if (status.quotaOccupied != Status.QUOTA_UNAVAILABLE
+ && status.quotaTotal != Status.QUOTA_UNAVAILABLE) {
+ if ((float) status.quotaOccupied / (float) status.quotaTotal >= QUOTA_FULL_THRESHOLD) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.voicemail_error_inbox_full_title),
+ context.getString(R.string.voicemail_error_inbox_full_message));
+ }
+
+ if ((float) status.quotaOccupied / (float) status.quotaTotal >= QUOTA_NEAR_FULL_THRESHOLD) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.voicemail_error_inbox_near_full_title),
+ context.getString(R.string.voicemail_error_inbox_near_full_message));
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private static VoicemailErrorMessage createNoSignalMessage(
+ Context context, VoicemailStatus status) {
+ CharSequence title;
+ CharSequence description;
+ List<Action> actions = new ArrayList<>();
+ if (Status.CONFIGURATION_STATE_OK == status.configurationState) {
+ if (Status.DATA_CHANNEL_STATE_NO_CONNECTION_CELLULAR_REQUIRED == status.dataChannelState) {
+ title = context.getString(R.string.voicemail_error_no_signal_title);
+ description =
+ context.getString(R.string.voicemail_error_no_signal_cellular_required_message);
+ } else {
+ title = context.getString(R.string.voicemail_error_no_signal_title);
+ if (status.isAirplaneMode) {
+ description = context.getString(R.string.voicemail_error_no_signal_airplane_mode_message);
+ } else {
+ description = context.getString(R.string.voicemail_error_no_signal_message);
+ }
+ actions.add(VoicemailErrorMessage.createSyncAction(context, status));
+ }
+ } else {
+ title = context.getString(R.string.voicemail_error_not_activate_no_signal_title);
+ if (status.isAirplaneMode) {
+ description =
+ context.getString(
+ R.string.voicemail_error_not_activate_no_signal_airplane_mode_message);
+ } else {
+ description = context.getString(R.string.voicemail_error_not_activate_no_signal_message);
+ actions.add(VoicemailErrorMessage.createRetryAction(context, status));
+ }
+ }
+ if (status.isAirplaneMode) {
+ actions.add(VoicemailErrorMessage.createChangeAirplaneModeAction(context));
+ }
+ return new VoicemailErrorMessage(title, description, actions);
+ }
+}
diff --git a/java/com/android/dialer/app/voicemail/error/VoicemailErrorAlert.java b/java/com/android/dialer/app/voicemail/error/VoicemailErrorAlert.java
new file mode 100644
index 000000000..d34a0f3c7
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/error/VoicemailErrorAlert.java
@@ -0,0 +1,165 @@
+/*
+ * 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.dialer.app.voicemail.error;
+
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.view.View;
+import android.widget.TextView;
+import com.android.dialer.app.alert.AlertManager;
+import com.android.dialer.app.voicemail.error.VoicemailErrorMessage.Action;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import java.util.List;
+
+/**
+ * UI for the voicemail error message, which will be inserted to the top of the voicemail tab if any
+ * occurred.
+ */
+public class VoicemailErrorAlert {
+
+ private final Context context;
+ private final AlertManager alertManager;
+ private final VoicemailErrorMessageCreator messageCreator;
+
+ private final View view;
+ private final TextView header;
+ private final TextView details;
+ private final TextView primaryAction;
+ private final TextView secondaryAction;
+ private final TextView primaryActionRaised;
+ private final TextView secondaryActionRaised;
+ private final AlertManager modalAlertManager;
+ private View modalView;
+
+ public VoicemailErrorAlert(
+ Context context,
+ AlertManager alertManager,
+ AlertManager modalAlertManager,
+ VoicemailErrorMessageCreator messageCreator) {
+ this.context = context;
+ this.alertManager = alertManager;
+ this.modalAlertManager = modalAlertManager;
+ this.messageCreator = messageCreator;
+
+ view = alertManager.inflate(R.layout.voicemai_error_message_fragment);
+ header = (TextView) view.findViewById(R.id.error_card_header);
+ details = (TextView) view.findViewById(R.id.error_card_details);
+ primaryAction = (TextView) view.findViewById(R.id.primary_action);
+ secondaryAction = (TextView) view.findViewById(R.id.secondary_action);
+ primaryActionRaised = (TextView) view.findViewById(R.id.primary_action_raised);
+ secondaryActionRaised = (TextView) view.findViewById(R.id.secondary_action_raised);
+ }
+
+ public void updateStatus(List<VoicemailStatus> statuses, VoicemailStatusReader statusReader) {
+ LogUtil.i("VoicemailErrorAlert.updateStatus", "%d status", statuses.size());
+ VoicemailErrorMessage message = null;
+ view.setVisibility(View.VISIBLE);
+ for (VoicemailStatus status : statuses) {
+ message = messageCreator.create(context, status, statusReader);
+ if (message != null) {
+ break;
+ }
+ }
+
+ alertManager.clear();
+ modalAlertManager.clear();
+ if (message != null) {
+ LogUtil.i(
+ "VoicemailErrorAlert.updateStatus",
+ "isModal: %b, %s",
+ message.isModal(),
+ message.getTitle());
+ if (message.isModal()) {
+ if (message instanceof VoicemailTosMessage) {
+ modalView = getTosView(modalAlertManager, (VoicemailTosMessage) message);
+ } else {
+ throw new IllegalArgumentException("Modal message type is undefined!");
+ }
+ modalAlertManager.add(modalView);
+ } else {
+ loadMessage(message);
+ alertManager.add(view);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ public View getView() {
+ return view;
+ }
+
+ @VisibleForTesting
+ public View getModalView() {
+ return modalView;
+ }
+
+ void loadMessage(VoicemailErrorMessage message) {
+ header.setText(message.getTitle());
+ details.setText(message.getDescription());
+ bindActions(message);
+ }
+
+ private View getTosView(AlertManager alertManager, VoicemailTosMessage message) {
+ View view = alertManager.inflate(R.layout.voicemail_tos_fragment);
+ TextView tosTitle = (TextView) view.findViewById(R.id.tos_message_title);
+ tosTitle.setText(message.getTitle());
+ TextView tosDetails = (TextView) view.findViewById(R.id.tos_message_details);
+ tosDetails.setText(message.getDescription());
+
+ Assert.checkArgument(message.getActions().size() == 2);
+ Action primaryAction = message.getActions().get(0);
+ TextView primaryButton = (TextView) view.findViewById(R.id.voicemail_tos_button_decline);
+ primaryButton.setText(primaryAction.getText());
+ primaryButton.setOnClickListener(primaryAction.getListener());
+ Action secondaryAction = message.getActions().get(1);
+ TextView secondaryButton = (TextView) view.findViewById(R.id.voicemail_tos_button_accept);
+ secondaryButton.setText(secondaryAction.getText());
+ secondaryButton.setOnClickListener(secondaryAction.getListener());
+ return view;
+ }
+
+ /**
+ * Attach actions to buttons until all buttons are assigned. If there are not enough actions the
+ * rest of the buttons will be removed. If there are more actions then buttons the extra actions
+ * will be dropped. {@link VoicemailErrorMessage#getActions()} will specify what actions should be
+ * shown and in what order.
+ */
+ private void bindActions(VoicemailErrorMessage message) {
+ TextView[] buttons = new TextView[] {primaryAction, secondaryAction};
+ TextView[] raisedButtons = new TextView[] {primaryActionRaised, secondaryActionRaised};
+ for (int i = 0; i < buttons.length; i++) {
+ if (message.getActions() != null && i < message.getActions().size()) {
+ VoicemailErrorMessage.Action action = message.getActions().get(i);
+ TextView button;
+ if (action.isRaised()) {
+ button = raisedButtons[i];
+ buttons[i].setVisibility(View.GONE);
+ } else {
+ button = buttons[i];
+ raisedButtons[i].setVisibility(View.GONE);
+ }
+ button.setText(action.getText());
+ button.setOnClickListener(action.getListener());
+ button.setVisibility(View.VISIBLE);
+ } else {
+ buttons[i].setVisibility(View.GONE);
+ raisedButtons[i].setVisibility(View.GONE);
+ }
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessage.java b/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessage.java
new file mode 100644
index 000000000..61572008b
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessage.java
@@ -0,0 +1,178 @@
+/*
+ * 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.dialer.app.voicemail.error;
+
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+import android.provider.VoicemailContract;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.telephony.TelephonyManager;
+import android.view.View;
+import android.view.View.OnClickListener;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.util.CallUtil;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represents an error determined from the current {@link
+ * android.provider.VoicemailContract.Status}. The message will contain a title, a description, and
+ * a list of actions that can be performed.
+ */
+public class VoicemailErrorMessage {
+
+ private final CharSequence title;
+ private final CharSequence description;
+ private final List<Action> actions;
+
+ private boolean modal;
+
+ /** Something the user can click on to resolve an error, such as retrying or calling voicemail */
+ public static class Action {
+
+ private final CharSequence text;
+ private final View.OnClickListener listener;
+ private final boolean raised;
+
+ public Action(CharSequence text, View.OnClickListener listener) {
+ this(text, listener, false);
+ }
+
+ public Action(CharSequence text, View.OnClickListener listener, boolean raised) {
+ this.text = text;
+ this.listener = listener;
+ this.raised = raised;
+ }
+
+ public CharSequence getText() {
+ return text;
+ }
+
+ public View.OnClickListener getListener() {
+ return listener;
+ }
+
+ public boolean isRaised() {
+ return raised;
+ }
+ }
+
+ public CharSequence getTitle() {
+ return title;
+ }
+
+ public CharSequence getDescription() {
+ return description;
+ }
+
+ @Nullable
+ public List<Action> getActions() {
+ return actions;
+ }
+
+ public boolean isModal() {
+ return modal;
+ }
+
+ public VoicemailErrorMessage setModal(boolean value) {
+ modal = value;
+ return this;
+ }
+
+ public VoicemailErrorMessage(CharSequence title, CharSequence description, Action... actions) {
+ this(title, description, Arrays.asList(actions));
+ }
+
+ public VoicemailErrorMessage(
+ CharSequence title, CharSequence description, @Nullable List<Action> actions) {
+ this.title = title;
+ this.description = description;
+ this.actions = actions;
+ }
+
+ @NonNull
+ public static Action createChangeAirplaneModeAction(final Context context) {
+ return new Action(
+ context.getString(R.string.voicemail_action_turn_off_airplane_mode),
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS);
+ context.startActivity(intent);
+ }
+ });
+ }
+
+ @NonNull
+ public static Action createSetPinAction(final Context context) {
+ return new Action(
+ context.getString(R.string.voicemail_action_set_pin),
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Logger.get(context)
+ .logImpression(DialerImpression.Type.VOICEMAIL_ALERT_SET_PIN_CLICKED);
+ Intent intent = new Intent(TelephonyManager.ACTION_CONFIGURE_VOICEMAIL);
+ context.startActivity(intent);
+ }
+ });
+ }
+
+ @NonNull
+ public static Action createCallVoicemailAction(final Context context) {
+ return new Action(
+ context.getString(R.string.voicemail_action_call_voicemail),
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_CALL, CallUtil.getVoicemailUri());
+ context.startActivity(intent);
+ }
+ });
+ }
+
+ @NonNull
+ public static Action createSyncAction(final Context context, final VoicemailStatus status) {
+ return new Action(
+ context.getString(R.string.voicemail_action_sync),
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(VoicemailContract.ACTION_SYNC_VOICEMAIL);
+ intent.setPackage(status.sourcePackage);
+ context.sendBroadcast(intent);
+ }
+ });
+ }
+
+ @NonNull
+ public static Action createRetryAction(final Context context, final VoicemailStatus status) {
+ return new Action(
+ context.getString(R.string.voicemail_action_retry),
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(VoicemailContract.ACTION_SYNC_VOICEMAIL);
+ intent.setPackage(status.sourcePackage);
+ context.sendBroadcast(intent);
+ }
+ });
+ }
+}
diff --git a/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessageCreator.java b/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessageCreator.java
new file mode 100644
index 000000000..5ebef801d
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessageCreator.java
@@ -0,0 +1,45 @@
+/*
+ * 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.dialer.app.voicemail.error;
+
+import android.content.Context;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.Nullable;
+
+/**
+ * Given a VoicemailStatus, {@link VoicemailErrorMessageCreator#create(Context, VoicemailStatus)}
+ * will return a {@link VoicemailErrorMessage} representing the message to be shown to the user, or
+ * <code>null</code> if no message should be shown.
+ */
+public class VoicemailErrorMessageCreator {
+
+ @Nullable
+ public VoicemailErrorMessage create(
+ Context context, VoicemailStatus status, VoicemailStatusReader statusReader) {
+ // Never return error message before NMR1. Voicemail status is not supported on those.
+ if (VERSION.SDK_INT < VERSION_CODES.N_MR1) {
+ return null;
+ }
+ switch (status.type) {
+ case Vvm3VoicemailMessageCreator.VVM_TYPE_VVM3:
+ return Vvm3VoicemailMessageCreator.create(context, status, statusReader);
+ default:
+ return OmtpVoicemailMessageCreator.create(context, status);
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/voicemail/error/VoicemailStatus.java b/java/com/android/dialer/app/voicemail/error/VoicemailStatus.java
new file mode 100644
index 000000000..a09941de2
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/error/VoicemailStatus.java
@@ -0,0 +1,260 @@
+/*
+ * 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.dialer.app.voicemail.error;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.provider.VoicemailContract.Status;
+import android.support.annotation.Nullable;
+import android.telephony.TelephonyManager;
+import com.android.dialer.database.VoicemailStatusQuery;
+
+/** Structured data from {@link android.provider.VoicemailContract.Status} */
+public class VoicemailStatus {
+
+ public final String sourcePackage;
+ public final String type;
+
+ public final String phoneAccountComponentName;
+ public final String phoneAccountId;
+
+ @Nullable public final Uri settingsUri;
+ @Nullable public final Uri voicemailAccessUri;
+
+ public final int configurationState;
+ public final int dataChannelState;
+ public final int notificationChannelState;
+
+ public final int quotaOccupied;
+ public final int quotaTotal;
+
+ // System status
+
+ public final boolean isAirplaneMode;
+
+ /** Wraps the row currently pointed by <code>statusCursor</code> */
+ public VoicemailStatus(Context context, Cursor statusCursor) {
+ sourcePackage = getString(statusCursor, VoicemailStatusQuery.SOURCE_PACKAGE_INDEX, "");
+
+ settingsUri = getUri(statusCursor, VoicemailStatusQuery.SETTINGS_URI_INDEX);
+ voicemailAccessUri = getUri(statusCursor, VoicemailStatusQuery.VOICEMAIL_ACCESS_URI_INDEX);
+
+ configurationState =
+ getInt(
+ statusCursor,
+ VoicemailStatusQuery.CONFIGURATION_STATE_INDEX,
+ Status.CONFIGURATION_STATE_NOT_CONFIGURED);
+ dataChannelState =
+ getInt(
+ statusCursor,
+ VoicemailStatusQuery.DATA_CHANNEL_STATE_INDEX,
+ Status.DATA_CHANNEL_STATE_NO_CONNECTION);
+ notificationChannelState =
+ getInt(
+ statusCursor,
+ VoicemailStatusQuery.NOTIFICATION_CHANNEL_STATE_INDEX,
+ Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
+
+ isAirplaneMode =
+ Settings.System.getInt(context.getContentResolver(), Global.AIRPLANE_MODE_ON, 0) != 0;
+
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ quotaOccupied =
+ getInt(statusCursor, VoicemailStatusQuery.QUOTA_OCCUPIED_INDEX, Status.QUOTA_UNAVAILABLE);
+ quotaTotal =
+ getInt(statusCursor, VoicemailStatusQuery.QUOTA_TOTAL_INDEX, Status.QUOTA_UNAVAILABLE);
+ } else {
+ quotaOccupied = Status.QUOTA_UNAVAILABLE;
+ quotaTotal = Status.QUOTA_UNAVAILABLE;
+ }
+
+ if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
+ type =
+ getString(
+ statusCursor, VoicemailStatusQuery.SOURCE_TYPE_INDEX, TelephonyManager.VVM_TYPE_OMTP);
+ phoneAccountComponentName =
+ getString(statusCursor, VoicemailStatusQuery.PHONE_ACCOUNT_COMPONENT_NAME, "");
+ phoneAccountId = getString(statusCursor, VoicemailStatusQuery.PHONE_ACCOUNT_ID, "");
+ } else {
+ type = TelephonyManager.VVM_TYPE_OMTP;
+ phoneAccountComponentName = "";
+ phoneAccountId = "";
+ }
+ }
+
+ private VoicemailStatus(Builder builder) {
+ sourcePackage = builder.sourcePackage;
+ phoneAccountComponentName = builder.phoneAccountComponentName;
+ phoneAccountId = builder.phoneAccountId;
+ type = builder.type;
+ settingsUri = builder.settingsUri;
+ voicemailAccessUri = builder.voicemailAccessUri;
+ configurationState = builder.configurationState;
+ dataChannelState = builder.dataChannelState;
+ notificationChannelState = builder.notificationChannelState;
+ quotaOccupied = builder.quotaOccupied;
+ quotaTotal = builder.quotaTotal;
+ isAirplaneMode = builder.isAirplaneMode;
+ }
+
+ static class Builder {
+
+ private String sourcePackage = "";
+ private String type = TelephonyManager.VVM_TYPE_OMTP;
+ private String phoneAccountComponentName = "";
+ private String phoneAccountId = "";
+
+ @Nullable private Uri settingsUri;
+ @Nullable private Uri voicemailAccessUri;
+
+ private int configurationState = Status.CONFIGURATION_STATE_NOT_CONFIGURED;
+ private int dataChannelState = Status.DATA_CHANNEL_STATE_NO_CONNECTION;
+ private int notificationChannelState = Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION;
+
+ private int quotaOccupied = Status.QUOTA_UNAVAILABLE;
+ private int quotaTotal = Status.QUOTA_UNAVAILABLE;
+
+ private boolean isAirplaneMode;
+
+ public VoicemailStatus build() {
+ return new VoicemailStatus(this);
+ }
+
+ public Builder setSourcePackage(String sourcePackage) {
+ this.sourcePackage = sourcePackage;
+ return this;
+ }
+
+ public Builder setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ public Builder setPhoneAccountComponentName(String name) {
+ this.phoneAccountComponentName = name;
+ return this;
+ }
+
+ public Builder setPhoneAccountId(String id) {
+ this.phoneAccountId = id;
+ return this;
+ }
+
+ public Builder setSettingsUri(Uri settingsUri) {
+ this.settingsUri = settingsUri;
+ return this;
+ }
+
+ public Builder setVoicemailAccessUri(Uri voicemailAccessUri) {
+ this.voicemailAccessUri = voicemailAccessUri;
+ return this;
+ }
+
+ public Builder setConfigurationState(int configurationState) {
+ this.configurationState = configurationState;
+ return this;
+ }
+
+ public Builder setDataChannelState(int dataChannelState) {
+ this.dataChannelState = dataChannelState;
+ return this;
+ }
+
+ public Builder setNotificationChannelState(int notificationChannelState) {
+ this.notificationChannelState = notificationChannelState;
+ return this;
+ }
+
+ public Builder setQuotaOccupied(int quotaOccupied) {
+ this.quotaOccupied = quotaOccupied;
+ return this;
+ }
+
+ public Builder setQuotaTotal(int quotaTotal) {
+ this.quotaTotal = quotaTotal;
+ return this;
+ }
+
+ public Builder setAirplaneMode(boolean isAirplaneMode) {
+ this.isAirplaneMode = isAirplaneMode;
+ return this;
+ }
+ }
+
+ public boolean isActive() {
+ switch (configurationState) {
+ case Status.CONFIGURATION_STATE_NOT_CONFIGURED:
+ case Status.CONFIGURATION_STATE_DISABLED:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "VoicemailStatus["
+ + "sourcePackage: "
+ + sourcePackage
+ + ", type:"
+ + type
+ + ", settingsUri: "
+ + settingsUri
+ + ", voicemailAccessUri: "
+ + voicemailAccessUri
+ + ", configurationState: "
+ + configurationState
+ + ", dataChannelState: "
+ + dataChannelState
+ + ", notificationChannelState: "
+ + notificationChannelState
+ + ", quotaOccupied: "
+ + quotaOccupied
+ + ", quotaTotal: "
+ + quotaTotal
+ + ", isAirplaneMode: "
+ + isAirplaneMode
+ + "]";
+ }
+
+ @Nullable
+ private static Uri getUri(Cursor cursor, int index) {
+ if (cursor.getString(index) != null) {
+ return Uri.parse(cursor.getString(index));
+ }
+ return null;
+ }
+
+ private static int getInt(Cursor cursor, int index, int defaultValue) {
+ if (cursor.isNull(index)) {
+ return defaultValue;
+ }
+ return cursor.getInt(index);
+ }
+
+ private static String getString(Cursor cursor, int index, String defaultValue) {
+ if (cursor.isNull(index)) {
+ return defaultValue;
+ }
+ return cursor.getString(index);
+ }
+}
diff --git a/java/com/android/dialer/app/voicemail/error/VoicemailStatusCorruptionHandler.java b/java/com/android/dialer/app/voicemail/error/VoicemailStatusCorruptionHandler.java
new file mode 100644
index 000000000..6f411217c
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/error/VoicemailStatusCorruptionHandler.java
@@ -0,0 +1,114 @@
+/*
+ * 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.dialer.app.voicemail.error;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.VoicemailContract.Status;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.TelephonyManager;
+import com.android.contacts.common.compat.TelephonyManagerCompat;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.ConfigProviderBindings;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+
+/**
+ * This class will detect the corruption in the voicemail status and log it so we can track how many
+ * users are affected.
+ */
+public class VoicemailStatusCorruptionHandler {
+
+ /** Where the check is made so logging can be done. */
+ public enum Source {
+ Activity,
+ Notification
+ }
+
+ private static final String CONFIG_VVM_STATUS_FIX_DISABLED = "vvm_status_fix_disabled";
+
+ public static void maybeFixVoicemailStatus(Context context, Cursor statusCursor, Source source) {
+
+ if (ConfigProviderBindings.get(context).getBoolean(CONFIG_VVM_STATUS_FIX_DISABLED, false)) {
+ return;
+ }
+
+ if (VERSION.SDK_INT != VERSION_CODES.N_MR1) {
+ // This issue is specific to N MR1, it is fixed in future SDK.
+ return;
+ }
+
+ if (statusCursor.getCount() == 0) {
+ return;
+ }
+
+ statusCursor.moveToFirst();
+ VoicemailStatus status = new VoicemailStatus(context, statusCursor);
+ PhoneAccountHandle phoneAccountHandle =
+ new PhoneAccountHandle(
+ ComponentName.unflattenFromString(status.phoneAccountComponentName),
+ status.phoneAccountId);
+
+ TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+
+ boolean visualVoicemailEnabled =
+ TelephonyManagerCompat.isVisualVoicemailEnabled(telephonyManager, phoneAccountHandle);
+ LogUtil.i(
+ "VoicemailStatusCorruptionHandler.maybeFixVoicemailStatus",
+ "Source="
+ + source
+ + ", CONFIGURATION_STAIE="
+ + status.configurationState
+ + ", visualVoicemailEnabled="
+ + visualVoicemailEnabled);
+
+ // If visual voicemail is enabled, the CONFIGURATION_STATE should be either OK, PIN_NOT_SET,
+ // or other failure code. CONFIGURATION_STATE_NOT_CONFIGURED means that the client has been
+ // shut down improperly (b/32371710). The client should be reset or the VVM tab will be
+ // missing.
+ if (Status.CONFIGURATION_STATE_NOT_CONFIGURED == status.configurationState
+ && visualVoicemailEnabled) {
+ LogUtil.e(
+ "VoicemailStatusCorruptionHandler.maybeFixVoicemailStatus",
+ "VVM3 voicemail status corrupted");
+
+ switch (source) {
+ case Activity:
+ Logger.get(context)
+ .logImpression(
+ DialerImpression.Type
+ .VOICEMAIL_CONFIGURATION_STATE_CORRUPTION_DETECTED_FROM_ACTIVITY);
+ break;
+ case Notification:
+ Logger.get(context)
+ .logImpression(
+ DialerImpression.Type
+ .VOICEMAIL_CONFIGURATION_STATE_CORRUPTION_DETECTED_FROM_NOTIFICATION);
+ break;
+ default:
+ Assert.fail("this should never happen");
+ break;
+ }
+ // At this point we could attempt to work around the issue by disabling and re-enabling
+ // voicemail. Unfortunately this work around is buggy so we'll do nothing for now.
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/voicemail/error/VoicemailStatusReader.java b/java/com/android/dialer/app/voicemail/error/VoicemailStatusReader.java
new file mode 100644
index 000000000..fd9e7ef25
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/error/VoicemailStatusReader.java
@@ -0,0 +1,25 @@
+/*
+ * 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.dialer.app.voicemail.error;
+
+/**
+ * A source that is generating the voicemail status to show error messages, used by {@link
+ * VoicemailErrorMessageCreator} to inform the source that the status should be updated
+ */
+public interface VoicemailStatusReader {
+ void refresh();
+}
diff --git a/java/com/android/dialer/app/voicemail/error/VoicemailTosMessage.java b/java/com/android/dialer/app/voicemail/error/VoicemailTosMessage.java
new file mode 100644
index 000000000..86b124419
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/error/VoicemailTosMessage.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 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.dialer.app.voicemail.error;
+
+/** Voicemail TOS message. */
+public class VoicemailTosMessage extends VoicemailErrorMessage {
+
+ public VoicemailTosMessage(CharSequence title, CharSequence description, Action... actions) {
+ super(title, description, actions);
+ }
+}
diff --git a/java/com/android/dialer/app/voicemail/error/Vvm3VoicemailMessageCreator.java b/java/com/android/dialer/app/voicemail/error/Vvm3VoicemailMessageCreator.java
new file mode 100644
index 000000000..6e9405cbf
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/error/Vvm3VoicemailMessageCreator.java
@@ -0,0 +1,428 @@
+/*
+ * 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.dialer.app.voicemail.error;
+
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.preference.PreferenceManager;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.VisibleForTesting;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.TelephonyManager;
+import android.view.View;
+import android.view.View.OnClickListener;
+import com.android.contacts.common.compat.TelephonyManagerCompat;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.dialer.app.voicemail.error.VoicemailErrorMessage.Action;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import java.util.Locale;
+
+/**
+ * Create error message from {@link VoicemailStatus} for VVM3 visual voicemail. VVM3 is used only by
+ * Verizon Wireless.
+ */
+@RequiresApi(VERSION_CODES.N_MR1)
+public class Vvm3VoicemailMessageCreator {
+
+ public static final String VVM_TYPE_VVM3 = "vvm_type_vvm3";
+
+ // Copied from com.android.phone.vvm.omtp.protocol.Vvm3EventHandler
+ // TODO(b/28380841): unbundle VVM client so we can access these values directly
+ public static final int VMS_DNS_FAILURE = -9001;
+ public static final int VMG_DNS_FAILURE = -9002;
+ public static final int SPG_DNS_FAILURE = -9003;
+ public static final int VMS_NO_CELLULAR = -9004;
+ public static final int VMG_NO_CELLULAR = -9005;
+ public static final int SPG_NO_CELLULAR = -9006;
+ public static final int VMS_TIMEOUT = -9007;
+ public static final int VMG_TIMEOUT = -9008;
+ public static final int STATUS_SMS_TIMEOUT = -9009;
+
+ public static final int SUBSCRIBER_BLOCKED = -9990;
+ public static final int UNKNOWN_USER = -9991;
+ public static final int UNKNOWN_DEVICE = -9992;
+ public static final int INVALID_PASSWORD = -9993;
+ public static final int MAILBOX_NOT_INITIALIZED = -9994;
+ public static final int SERVICE_NOT_PROVISIONED = -9995;
+ public static final int SERVICE_NOT_ACTIVATED = -9996;
+ public static final int USER_BLOCKED = -9998;
+ public static final int IMAP_GETQUOTA_ERROR = -9997;
+ public static final int IMAP_SELECT_ERROR = -9989;
+ public static final int IMAP_ERROR = -9999;
+
+ public static final int VMG_INTERNAL_ERROR = -101;
+ public static final int VMG_DB_ERROR = -102;
+ public static final int VMG_COMMUNICATION_ERROR = -103;
+ public static final int SPG_URL_NOT_FOUND = -301;
+
+ // Non VVM3 codes:
+ public static final int VMG_UNKNOWN_ERROR = -1;
+ public static final int PIN_NOT_SET = -100;
+ public static final int SUBSCRIBER_UNKNOWN = -99;
+
+ private static final String ISO639_SPANISH = "es";
+ @VisibleForTesting static final String VVM3_TOS_ACCEPTANCE_FLAG_KEY = "vvm3_tos_acceptance_flag";
+
+ @Nullable
+ public static VoicemailErrorMessage create(
+ final Context context,
+ final VoicemailStatus status,
+ final VoicemailStatusReader statusReader) {
+ VoicemailErrorMessage tosMessage = maybeShowTosMessage(context, status, statusReader);
+ if (tosMessage != null) {
+ return tosMessage;
+ }
+
+ if (VMS_DNS_FAILURE == status.dataChannelState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_vms_dns_failure_title),
+ getCustomerSupportString(context, R.string.vvm3_error_vms_dns_failure_message),
+ VoicemailErrorMessage.createRetryAction(context, status),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (VMG_DNS_FAILURE == status.configurationState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_vmg_dns_failure_title),
+ getCustomerSupportString(context, R.string.vvm3_error_vmg_dns_failure_message),
+ VoicemailErrorMessage.createRetryAction(context, status),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (SPG_DNS_FAILURE == status.configurationState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_spg_dns_failure_title),
+ getCustomerSupportString(context, R.string.vvm3_error_spg_dns_failure_message),
+ VoicemailErrorMessage.createRetryAction(context, status),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (VMS_NO_CELLULAR == status.dataChannelState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_vms_no_cellular_title),
+ getCustomerSupportString(context, R.string.vvm3_error_vms_no_cellular_message),
+ VoicemailErrorMessage.createRetryAction(context, status),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (VMG_NO_CELLULAR == status.configurationState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_vmg_no_cellular_title),
+ getCustomerSupportString(context, R.string.vvm3_error_vmg_no_cellular_message),
+ VoicemailErrorMessage.createRetryAction(context, status),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (SPG_NO_CELLULAR == status.configurationState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_spg_no_cellular_title),
+ getCustomerSupportString(context, R.string.vvm3_error_spg_no_cellular_message),
+ VoicemailErrorMessage.createRetryAction(context, status),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (VMS_TIMEOUT == status.dataChannelState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_vms_timeout_title),
+ getCustomerSupportString(context, R.string.vvm3_error_vms_timeout_message),
+ VoicemailErrorMessage.createRetryAction(context, status),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (VMG_TIMEOUT == status.configurationState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_vmg_timeout_title),
+ getCustomerSupportString(context, R.string.vvm3_error_vmg_timeout_message),
+ VoicemailErrorMessage.createRetryAction(context, status),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (STATUS_SMS_TIMEOUT == status.notificationChannelState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_status_sms_timeout_title),
+ getCustomerSupportString(context, R.string.vvm3_error_status_sms_timeout_message),
+ VoicemailErrorMessage.createRetryAction(context, status),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (SUBSCRIBER_BLOCKED == status.configurationState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_subscriber_blocked_title),
+ getCustomerSupportString(context, R.string.vvm3_error_subscriber_blocked_message),
+ VoicemailErrorMessage.createRetryAction(context, status),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (UNKNOWN_USER == status.configurationState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_unknown_user_title),
+ getCustomerSupportString(context, R.string.vvm3_error_unknown_user_message),
+ VoicemailErrorMessage.createCallVoicemailAction(context),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (UNKNOWN_DEVICE == status.configurationState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_unknown_device_title),
+ getCustomerSupportString(context, R.string.vvm3_error_unknown_device_message),
+ VoicemailErrorMessage.createCallVoicemailAction(context),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (INVALID_PASSWORD == status.configurationState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_invalid_password_title),
+ getCustomerSupportString(context, R.string.vvm3_error_invalid_password_message),
+ VoicemailErrorMessage.createCallVoicemailAction(context),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (MAILBOX_NOT_INITIALIZED == status.configurationState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_mailbox_not_initialized_title),
+ getCustomerSupportString(context, R.string.vvm3_error_mailbox_not_initialized_message),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (SERVICE_NOT_PROVISIONED == status.configurationState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_service_not_provisioned_title),
+ getCustomerSupportString(context, R.string.vvm3_error_service_not_provisioned_message),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (SERVICE_NOT_ACTIVATED == status.configurationState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_service_not_activated_title),
+ getCustomerSupportString(context, R.string.vvm3_error_service_not_activated_message),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (USER_BLOCKED == status.configurationState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_user_blocked_title),
+ getCustomerSupportString(context, R.string.vvm3_error_user_blocked_message),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (SUBSCRIBER_UNKNOWN == status.configurationState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_subscriber_unknown_title),
+ getCustomerSupportString(context, R.string.vvm3_error_subscriber_unknown_message),
+ VoicemailErrorMessage.createCallVoicemailAction(context),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (IMAP_GETQUOTA_ERROR == status.dataChannelState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_imap_getquota_error_title),
+ getCustomerSupportString(context, R.string.vvm3_error_imap_getquota_error_message),
+ VoicemailErrorMessage.createCallVoicemailAction(context),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (IMAP_SELECT_ERROR == status.dataChannelState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_imap_select_error_title),
+ getCustomerSupportString(context, R.string.vvm3_error_imap_select_error_message),
+ VoicemailErrorMessage.createCallVoicemailAction(context),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (IMAP_ERROR == status.dataChannelState) {
+ return new VoicemailErrorMessage(
+ context.getString(R.string.vvm3_error_imap_error_title),
+ getCustomerSupportString(context, R.string.vvm3_error_imap_error_message),
+ VoicemailErrorMessage.createCallVoicemailAction(context),
+ createCallCustomerSupportAction(context));
+ }
+
+ if (PIN_NOT_SET == status.configurationState) {
+ Logger.get(context).logImpression(DialerImpression.Type.VOICEMAIL_ALERT_SET_PIN_SHOWN);
+ return new VoicemailErrorMessage(
+ context.getString(R.string.voicemail_error_pin_not_set_title),
+ getCustomerSupportString(context, R.string.voicemail_error_pin_not_set_message),
+ VoicemailErrorMessage.createSetPinAction(context));
+ }
+
+ return OmtpVoicemailMessageCreator.create(context, status);
+ }
+
+ @NonNull
+ private static CharSequence getCustomerSupportString(Context context, int id) {
+ // TODO: get number based on the country the user is currently in.
+ return ContactDisplayUtils.getTtsSpannedPhoneNumber(
+ context.getResources(),
+ id,
+ context.getString(R.string.verizon_domestic_customer_support_display_number));
+ }
+
+ @NonNull
+ private static Action createCallCustomerSupportAction(final Context context) {
+ return new Action(
+ context.getString(R.string.voicemail_action_call_customer_support),
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent =
+ new Intent(
+ Intent.ACTION_CALL,
+ Uri.parse(
+ "tel:"
+ + context.getString(
+ R.string.verizon_domestic_customer_support_number)));
+ context.startActivity(intent);
+ }
+ });
+ }
+
+ @Nullable
+ private static VoicemailErrorMessage maybeShowTosMessage(
+ final Context context,
+ final VoicemailStatus status,
+ final VoicemailStatusReader statusReader) {
+ final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ if (preferences.getBoolean(VVM3_TOS_ACCEPTANCE_FLAG_KEY, false)) {
+ return null;
+ }
+ Logger.get(context).logImpression(DialerImpression.Type.VOICEMAIL_VVM3_TOS_SHOWN);
+
+ CharSequence termsAndConditions;
+ CharSequence acceptText;
+ CharSequence declineText;
+ // TODO(b/29082671): use LocaleList
+ if (Locale.getDefault().getLanguage().equals(new Locale(ISO639_SPANISH).getLanguage())) {
+ // Spanish
+ termsAndConditions = context.getString(R.string.verizon_terms_and_conditions_1_1_spanish);
+ acceptText = context.getString(R.string.verizon_terms_and_conditions_accept_spanish);
+ declineText = context.getString(R.string.verizon_terms_and_conditions_decline_spanish);
+ } else {
+ termsAndConditions = context.getString(R.string.verizon_terms_and_conditions_1_1_english);
+ acceptText = context.getString(R.string.verizon_terms_and_conditions_accept_english);
+ declineText = context.getString(R.string.verizon_terms_and_conditions_decline_english);
+ }
+
+ return new VoicemailTosMessage(
+ context.getString(R.string.verizon_terms_and_conditions_title),
+ context.getString(R.string.verizon_terms_and_conditions_message, termsAndConditions),
+ new Action(
+ declineText,
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ LogUtil.i("Vvm3VoicemailMessageCreator.maybeShowTosMessage", "decline clicked");
+ PhoneAccountHandle handle =
+ new PhoneAccountHandle(
+ ComponentName.unflattenFromString(status.phoneAccountComponentName),
+ status.phoneAccountId);
+ Logger.get(context)
+ .logImpression(DialerImpression.Type.VOICEMAIL_VVM3_TOS_DECLINE_CLICKED);
+ showDeclineTosDialog(context, handle, status);
+ }
+ }),
+ new Action(
+ acceptText,
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ LogUtil.i("Vvm3VoicemailMessageCreator.maybeShowTosMessage", "accept clicked");
+ preferences.edit().putBoolean(VVM3_TOS_ACCEPTANCE_FLAG_KEY, true).apply();
+ Logger.get(context)
+ .logImpression(DialerImpression.Type.VOICEMAIL_VVM3_TOS_ACCEPTED);
+ statusReader.refresh();
+ }
+ },
+ true /* raised */))
+ .setModal(true);
+ }
+
+ private static void showDeclineTosDialog(
+ final Context context, final PhoneAccountHandle handle, VoicemailStatus status) {
+ if (PIN_NOT_SET == status.configurationState) {
+ LogUtil.i(
+ "Vvm3VoicemailMessageCreator.showDeclineTosDialog",
+ "PIN_NOT_SET, showing set PIN dialog");
+ showSetPinBeforeDeclineDialog(context);
+ return;
+ }
+ LogUtil.i(
+ "Vvm3VoicemailMessageCreator.showDeclineTosDialog",
+ "showing decline ToS dialog, status=" + status);
+ final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setMessage(R.string.verizon_terms_and_conditions_decline_dialog_message);
+ builder.setPositiveButton(
+ R.string.verizon_terms_and_conditions_decline_dialog_downgrade,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Logger.get(context).logImpression(DialerImpression.Type.VOICEMAIL_VVM3_TOS_DECLINED);
+ TelephonyManagerCompat.setVisualVoicemailEnabled(telephonyManager, handle, false);
+ }
+ });
+
+ builder.setNegativeButton(
+ android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+
+ builder.setCancelable(true);
+ builder.show();
+ }
+
+ private static void showSetPinBeforeDeclineDialog(final Context context) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setMessage(R.string.verizon_terms_and_conditions_decline_set_pin_dialog_message);
+ builder.setPositiveButton(
+ R.string.verizon_terms_and_conditions_decline_set_pin_dialog_set_pin,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Logger.get(context)
+ .logImpression(DialerImpression.Type.VOICEMAIL_VVM3_TOS_DECLINE_CHANGE_PIN_SHOWN);
+ Intent intent = new Intent(TelephonyManager.ACTION_CONFIGURE_VOICEMAIL);
+ context.startActivity(intent);
+ }
+ });
+
+ builder.setNegativeButton(
+ android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+
+ builder.setCancelable(true);
+ builder.show();
+ }
+}
diff --git a/java/com/android/dialer/app/voicemail/error/res/layout/voicemai_error_message_fragment.xml b/java/com/android/dialer/app/voicemail/error/res/layout/voicemai_error_message_fragment.xml
new file mode 100644
index 000000000..0dfb1c2fd
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/error/res/layout/voicemai_error_message_fragment.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<android.support.v7.widget.CardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/error_card"
+ style="@style/CallLogCardStyle"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/error_card_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/alert_main_padding"
+ android:layout_marginStart="@dimen/alert_main_padding"
+ android:layout_marginEnd="@dimen/alert_main_padding"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/error_card_header"
+ android:textStyle="bold"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/alert_title_padding"
+ android:layout_gravity="center_vertical"
+ android:singleLine="false"
+ android:textColor="@color/primary_text_color"
+ android:textSize="@dimen/call_log_primary_text_size"/>
+
+ <TextView
+ android:id="@+id/error_card_details"
+ android:autoLink="web"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:lineSpacingExtra="@dimen/alert_line_spacing"
+ android:singleLine="false"
+ android:textColor="@color/secondary_text_color"
+ android:textSize="@dimen/call_log_detail_text_size"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/error_actions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="20dp"
+ android:paddingTop="@dimen/alert_action_vertical_padding"
+ android:paddingBottom="@dimen/alert_action_vertical_padding"
+ android:paddingStart="@dimen/alert_action_horizontal_padding"
+ android:paddingEnd="@dimen/alert_action_horizontal_padding"
+ android:gravity="start"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/primary_action_raised"
+ style="@style/RaisedErrorActionStyle"
+ android:nextFocusLeft="@+id/promo_card"
+ android:nextFocusRight="@+id/primary_action"
+ android:clickable="true"
+ />
+
+ <TextView
+ android:id="@+id/primary_action"
+ style="@style/ErrorActionStyle"
+ android:background="?android:attr/selectableItemBackground"
+ android:nextFocusLeft="@+id/promo_card"
+ android:nextFocusRight="@+id/secondary_action"
+ android:clickable="true"
+ />
+
+ <TextView
+ android:id="@+id/secondary_action"
+ style="@style/ErrorActionStyle"
+ android:paddingEnd="@dimen/alert_action_between_padding"
+ android:background="?android:attr/selectableItemBackground"
+ android:nextFocusLeft="@+id/primary_action"
+ android:nextFocusRight="@+id/promo_card"
+ android:clickable="true"/>
+
+ <android.support.v4.widget.Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+
+ <TextView
+ android:id="@+id/secondary_action_raised"
+ style="@style/RaisedErrorActionStyle"
+ android:paddingEnd="@dimen/alert_action_between_padding"
+ android:layout_marginEnd="8dp"
+ android:nextFocusLeft="@+id/primary_action"
+ android:nextFocusRight="@+id/promo_card"
+ android:clickable="true"/>
+
+ </LinearLayout>
+ </LinearLayout>
+</android.support.v7.widget.CardView>
diff --git a/java/com/android/dialer/app/voicemail/error/res/layout/voicemail_tos_fragment.xml b/java/com/android/dialer/app/voicemail/error/res/layout/voicemail_tos_fragment.xml
new file mode 100644
index 000000000..2b9d17328
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/error/res/layout/voicemail_tos_fragment.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ScrollView
+ android:id="@+id/voicemail_tos_message"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/tos_message_title"
+ android:textStyle="bold"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="24dp"
+ android:paddingBottom="12dp"
+ android:text="@string/verizon_terms_and_conditions_title"
+ android:textColor="@color/primary_text_color"
+ android:textSize="@dimen/call_log_primary_text_size"/>
+ <TextView
+ android:id="@+id/tos_message_details"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="16dp"
+ android:autoLink="web"
+ android:text="@string/verizon_terms_and_conditions_1.1_english"
+ android:textColor="@color/secondary_text_color"
+ android:textSize="@dimen/call_log_detail_text_size"/>
+ </LinearLayout>
+ </ScrollView>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="#D2D2D2"/>
+
+ <LinearLayout
+ android:id="@+id/voicemail_tos_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:orientation="horizontal">
+ <TextView
+ android:id="@+id/voicemail_tos_button_decline"
+ style="@style/ErrorActionStyle"
+ android:background="?android:attr/selectableItemBackground"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/verizon_terms_and_conditions_decline_english"/>
+ <android.support.v4.widget.Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+ <TextView
+ android:id="@+id/voicemail_tos_button_accept"
+ style="@style/RaisedErrorActionStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/verizon_terms_and_conditions_accept_english"/>
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/java/com/android/dialer/app/voicemail/error/res/values/dimens.xml b/java/com/android/dialer/app/voicemail/error/res/values/dimens.xml
new file mode 100644
index 000000000..20dd40a8f
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/error/res/values/dimens.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="alert_icon_size">24dp</dimen>
+ <dimen name="alert_start_padding">16dp</dimen>
+ <dimen name="alert_top_padding">21dp</dimen>
+ <dimen name="alert_main_padding">24dp</dimen>
+ <dimen name="alert_title_padding">12dp</dimen>
+ <dimen name="alert_action_vertical_padding">4dp</dimen>
+ <dimen name="alert_action_horizontal_padding">4dp</dimen>
+ <dimen name="alert_action_between_padding">11dp</dimen>
+ <dimen name="alert_line_spacing">4dp</dimen>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/app/voicemail/error/res/values/strings.xml b/java/com/android/dialer/app/voicemail/error/res/values/strings.xml
new file mode 100644
index 000000000..1d39b9dcb
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/error/res/values/strings.xml
@@ -0,0 +1,176 @@
+<!--
+ ~ 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
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="voicemail_error_turn_off_airplane_mode_title">Turn off airplane mode</string>
+
+ <string name="voicemail_error_activating_title">Activating visual voicemail</string>
+ <string name="voicemail_error_activating_message">You might not receive voicemail notifications until visual voicemail is fully activated. Call voicemail to retrieve new messages until voicemail is fully activated.</string>
+
+ <string name="voicemail_error_not_activate_no_signal_title">Can\'t activate visual voicemail</string>
+ <string name="voicemail_error_not_activate_no_signal_message">Make sure your phone has cellular connection and try again.</string>
+ <string name="voicemail_error_not_activate_no_signal_airplane_mode_message">Turn off airplane mode and try again.</string>
+
+ <string name="voicemail_error_no_signal_title">No connection</string>
+ <string name="voicemail_error_no_signal_message">You won\'t be notified for new voicemails. If you\'re on Wi-Fi, you can check for voicemail by syncing now.</string>
+ <string name="voicemail_error_no_signal_airplane_mode_message">You won\'t be notified for new voicemails. Turn off airplane mode to sync your voicemail.</string>
+ <string name="voicemail_error_no_signal_cellular_required_message">Your phone needs a cellular data connection to check voicemail.</string>
+
+ <string name="voicemail_error_activation_failed_title">Can\'t activate visual voicemail</string>
+ <string name="voicemail_error_activation_failed_message">You can still call to check voicemail.</string>
+
+ <string name="voicemail_error_no_data_title">Can\'t update visual voicemail</string>
+ <string name="voicemail_error_no_data_message">Try again when your Wi-Fi or cellular connection is better. You can still call to check voicemail.</string>
+ <string name="voicemail_error_no_data_cellular_required_message">Try again when your cellular data connection is better. You can still call to check voicemail.</string>
+
+ <string name="voicemail_error_bad_config_title">Can\'t update visual voicemail</string>
+ <string name="voicemail_error_bad_config_message">You can still call to check voicemail.</string>
+
+ <string name="voicemail_error_communication_title">Can\'t update visual voicemail</string>
+ <string name="voicemail_error_communication_message">You can still call to check voicemail.</string>
+
+ <string name="voicemail_error_server_connection_title">Can\'t update visual voicemail</string>
+ <string name="voicemail_error_server_connection_message">You can still call to check voicemail.</string>
+
+ <string name="voicemail_error_server_title">Can\'t update visual voicemail</string>
+ <string name="voicemail_error_server_message">You can still call to check voicemail.</string>
+
+ <string name="voicemail_error_inbox_near_full_title">Inbox almost full</string>
+ <string name="voicemail_error_inbox_near_full_message">You won\'t be able to receive new voicemail if your inbox is full.</string>
+
+ <string name="voicemail_error_inbox_full_title">Can\'t receive new voicemails</string>
+ <string name="voicemail_error_inbox_full_message">Your inbox is full. Try deleting some messages to receive new voicemail.</string>
+
+
+ <string name="voicemail_error_pin_not_set_title">Set your voicemail PIN</string>
+ <string name="voicemail_error_pin_not_set_message">You\'ll need a voicemail PIN anytime you call to access your voicemail.</string>
+
+ <string name="voicemail_error_unknown_title">Unknown error</string>
+
+ <string name="voicemail_action_turn_off_airplane_mode">Airplane Mode Settings</string>
+ <string name="voicemail_action_set_pin">Set PIN</string>
+ <string name="voicemail_action_retry">Try Again</string>
+ <string name="voicemail_action_sync">Sync</string>
+ <string name="voicemail_action_call_voicemail">Call Voicemail</string>
+ <string name="voicemail_action_call_customer_support">Call Customer Support</string>
+
+ <string name="vvm3_error_vms_dns_failure_title">Something Went Wrong</string>
+ <string name="vvm3_error_vms_dns_failure_message">Sorry, we ran into a problem. Please try again later. If there is still a problem, please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9001.</string>
+
+ <string name="vvm3_error_vmg_dns_failure_title">Something Went Wrong</string>
+ <string name="vvm3_error_vmg_dns_failure_message">Sorry, we ran into a problem. Please try again later. If there is still a problem, please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9002.</string>
+
+ <string name="vvm3_error_spg_dns_failure_title">Something Went Wrong</string>
+ <string name="vvm3_error_spg_dns_failure_message">Sorry, we ran into a problem. Please try again later. If there is still a problem, please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9003.</string>
+
+ <string name="vvm3_error_vms_no_cellular_title">Can\'t Connect to Your Voice Mailbox</string>
+ <string name="vvm3_error_vms_no_cellular_message">Sorry, we\'re having trouble connecting to your voice mailbox. If you\'re in an area with poor signal strength, wait until you have a strong signal and try again. If there is still a problem, please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9004.</string>
+
+ <string name="vvm3_error_vmg_no_cellular_title">Can\'t Connect to Your Voice Mailbox</string>
+ <string name="vvm3_error_vmg_no_cellular_message">Sorry, we\'re having trouble connecting to your voice mailbox. If you\'re in an area with poor signal strength, wait until you have a strong signal and try again. If there is still a problem, please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9005.</string>
+
+ <string name="vvm3_error_spg_no_cellular_title">Can\'t Connect to Your Voice Mailbox</string>
+ <string name="vvm3_error_spg_no_cellular_message">Sorry, we\'re having trouble connecting to your voice mailbox. If you\'re in an area with poor signal strength, wait until you have a strong signal and try again. If there is still a problem, please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9006.</string>
+
+ <string name="vvm3_error_vms_timeout_title">Something Went Wrong</string>
+ <string name="vvm3_error_vms_timeout_message">Sorry, we ran into a problem. Please try again later. If there is still a problem, please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9007.</string>
+
+ <string name="vvm3_error_vmg_timeout_title">Something Went Wrong</string>
+ <string name="vvm3_error_vmg_timeout_message">Sorry, we ran into a problem. Please try again later. If there is still a problem, please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9008.</string>
+
+ <string name="vvm3_error_status_sms_timeout_title">Something Went Wrong</string>
+ <string name="vvm3_error_status_sms_timeout_message">Sorry, we\'re having trouble setting up your service. Please try again later. If there is still a problem, please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9009.</string>
+
+ <string name="vvm3_error_subscriber_blocked_title">Can\'t Connect to Your Voice Mailbox</string>
+ <string name="vvm3_error_subscriber_blocked_message">Sorry, we\'re not able to connect to your voice mailbox at this time. Please try again later. If there is still a problem, please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9990."</string>
+
+ <string name="vvm3_error_unknown_user_title">Set Up Voice Mail</string>
+ <string name="vvm3_error_unknown_user_message">Voicemail is not set up on your account. Please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9991.</string>
+
+ <string name="vvm3_error_unknown_device_title">Voice Mail</string>
+ <string name="vvm3_error_unknown_device_message">Visual Voicemail cannot be used on this device. Please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9992.</string>
+
+ <string name="vvm3_error_invalid_password_title">Something Went Wrong</string>
+ <string name="vvm3_error_invalid_password_message">Please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9993.</string>
+
+ <string name="vvm3_error_mailbox_not_initialized_title">Visual Voice Mail</string>
+ <string name="vvm3_error_mailbox_not_initialized_message">To complete Visual Voicemail setup, please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9994.</string>
+
+ <string name="vvm3_error_service_not_provisioned_title">Visual Voice Mail</string>
+ <string name="vvm3_error_service_not_provisioned_message">To complete Visual Voicemail setup, please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9995.</string>
+
+ <string name="vvm3_error_service_not_activated_title">Visual Voice Mail</string>
+ <string name="vvm3_error_service_not_activated_message">To activate Visual Voice Mail, please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9996.</string>
+
+ <string name="vvm3_error_user_blocked_title">Something Went Wrong</string>
+ <string name="vvm3_error_user_blocked_message">To complete Visual Voicemail setup, please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9998.</string>
+
+ <string name="vvm3_error_subscriber_unknown_title">Visual Voicemail is Disabled</string>
+ <string name="vvm3_error_subscriber_unknown_message">Please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> to activate visual voicemail.</string>
+
+ <string name="vvm3_error_imap_getquota_error_title">Something Went Wrong</string>
+ <string name="vvm3_error_imap_getquota_error_message">Please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9997.</string>
+
+ <string name="vvm3_error_imap_select_error_title">Something Went Wrong</string>
+ <string name="vvm3_error_imap_select_error_message">Please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9989.</string>
+
+ <string name="vvm3_error_imap_error_title">Something Went Wrong</string>
+ <string name="vvm3_error_imap_error_message">Please contact Customer Service at <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> and tell them the error code is 9999.</string>
+
+ <string translatable="false" name="verizon_domestic_customer_support_number">+18009220204</string>
+ <string translatable="false" name="verizon_domestic_customer_support_display_number">(800) 922–0204</string>
+
+ <string name="verizon_terms_and_conditions_title">Visual Voicemail Terms and Conditions</string>
+ <string name="verizon_terms_and_conditions_message">You must accept Verizon Wireless\'s terms and conditions to use visual voicemail:\n\n%s</string>
+
+ <string translatable="false" name="verizon_terms_and_conditions_1.1_english">
+Visual Voice Mail (VVM) is a service that provides access to voice mail messages directly on the device, without the need to call *86. This service requires traditional Voice Mail but does not support all traditional Voice Mail features, which you can access by dialing *86 from your handset. Use of this feature will be billed on a per-megabyte basis, or according to any data package you have. Mobile to mobile minutes do not apply. Standard rates apply to any calls, emails or messages initiated from Visual Voice Mail.\n
+\n
+You may disable VVM in settings. This will revert you to basic voice mail. In some cases you may need to call customer care to cancel and if you cancel Visual Voice Mail you may lose all stored voice mails and information.\n
+\n
+For the Premium Visual Voice Mail service, some voice messages may not be completely transcribed; incomplete messages will end with [...]. Only the first 45 seconds of each voice message will be transcribed, so for longer messages, you will need to listen to the voice message itself. Any profane or offensive language also will not be transcribed and will appear as [...] in the transcription.\n
+\n
+Speech recordings may be collected and stored for a period of 30 days, solely for the purpose of testing and improving transcription technology and performance, subject to the Verizon Wireless Privacy Policy, which can be found at http://www.verizon.com/about/privacy/policy/\n
+\n
+You understand that by selecting ACCEPT, your messages will be stored and anyone in possession of this device will have access to your voice mail. You further understand that your voice mail messages may be stored in electronic format on this device. To limit unauthorized access to your voice mail, you should consider locking your phone when not in use. Not available in all areas or over Wi-Fi.\n
+\n
+If you do not accept all of these terms and conditions, do not use Visual Voice Mail. </string>
+
+ <string translatable="false" name="verizon_terms_and_conditions_1.1_spanish">
+El buzón de voz visual (VVM) es un servicio que permite acceder a los mensajes del buzón de voz directamente en el dispositivo, sin necesidad de llamar al *86. Este servicio requiere el buzón de voz tradicional, pero no admite todas las funciones del buzón de voz tradicional, a las que se puede acceder marcando *86 en el teléfono. El uso de esta función se factura por megabyte o conforme a cualquier paquete de datos que tenga. No se aplican los minutos de un dispositivo móvil a otro. Se aplican tarifas estándar a todos los correos electrónicos, las llamadas o los mensajes originados en el buzón de voz visual.\n
+\n
+Puede inhabilitar el VVM en la configuración. Esto le permite volver al buzón de voz básico. En algunos casos, es posible que deba llamar al servicio de atención al cliente para cancelar el buzón de voz visual. Si lo cancela, puede perder la información y los mensajes de voz almacenados.\n
+\n
+En el caso del servicio de buzón de voz visual premium, es posible que algunos mensajes no se transcriban totalmente; los mensajes incompletos finalizan con "[…]". Solo se transcriben los primeros 45 segundos de cada mensaje de voz, por lo que debe escuchar los mensajes de voz más largos. Tampoco se transcribe ninguna palabra ofensiva o profana; aparece como "[…]" en la transcripción.\n
+\n
+Es posible que reunamos y almacenemos grabaciones de voz durante 30 días, con el único fin de probar y mejorar el rendimiento y la tecnología de la transcripción, sujeto a la Política de privacidad de Verizon Wireless, disponible en http://www.verizon.com/about/privacy/policy/.\n
+\n
+Entiende que, al seleccionar ACEPTAR, sus mensajes se almacenarán, y cualquier persona que disponga de este dispositivo tendrá acceso al buzón de voz. Entiende, además, que los mensajes de voz pueden almacenarse en formato electrónico en este dispositivo. Para limitar el acceso no autorizado al buzón de voz, debe considerar el bloqueo del teléfono cuando no está en uso. No está disponible en todas las áreas ni mediante Wi-Fi.\n
+\n
+Si no acepta todos estos términos y condiciones, no use el buzón de voz visual.
+ </string>
+
+ <string translatable="false" name="verizon_terms_and_conditions_accept_english">Accept</string>
+ <string translatable="false" name="verizon_terms_and_conditions_accept_spanish">Aceptar</string>
+ <string translatable="false" name="verizon_terms_and_conditions_decline_english">Decline</string>
+ <string translatable="false" name="verizon_terms_and_conditions_decline_spanish">Rechazar</string>
+
+ <string name="verizon_terms_and_conditions_decline_dialog_message">Visual voicemail will be disabled if the terms and conditions are declined.</string>
+ <string name="verizon_terms_and_conditions_decline_dialog_downgrade">Disable visual voicemail</string>
+
+ <string name="verizon_terms_and_conditions_decline_set_pin_dialog_message">Voicemail will only be accessible by calling *86. Set a new voicemail PIN to proceed.</string>
+ <string name="verizon_terms_and_conditions_decline_set_pin_dialog_set_pin">Set PIN</string>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/app/voicemail/error/res/values/styles.xml b/java/com/android/dialer/app/voicemail/error/res/values/styles.xml
new file mode 100644
index 000000000..c4a8542f1
--- /dev/null
+++ b/java/com/android/dialer/app/voicemail/error/res/values/styles.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <style name="ErrorActionStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">48dp</item>
+ <item name="android:gravity">end|center_vertical</item>
+ <item name="android:paddingStart">8dp</item>
+ <item name="android:paddingEnd">8dp</item>
+ <item name="android:layout_marginStart">8dp</item>
+ <item name="android:layout_marginEnd">8dp</item>
+ <item name="android:textColor">@color/dialtacts_theme_color</item>
+ <item name="android:fontFamily">"sans-serif-medium"</item>
+ <item name="android:focusable">true</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+
+ <style name="RaisedErrorActionStyle" parent="Widget.AppCompat.Button.Colored">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:colorButtonNormal">@color/dialer_theme_color</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:layout_height">@dimen/call_log_action_height</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/app/widget/ActionBarController.java b/java/com/android/dialer/app/widget/ActionBarController.java
new file mode 100644
index 000000000..7fe056c51
--- /dev/null
+++ b/java/com/android/dialer/app/widget/ActionBarController.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2013 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.dialer.app.widget;
+
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.os.Bundle;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+import com.android.dialer.animation.AnimUtils.AnimationCallback;
+import com.android.dialer.app.DialtactsActivity;
+
+/**
+ * Controls the various animated properties of the actionBar: showing/hiding, fading/revealing, and
+ * collapsing/expanding, and assigns suitable properties to the actionBar based on the current state
+ * of the UI.
+ */
+public class ActionBarController {
+
+ public static final boolean DEBUG = DialtactsActivity.DEBUG;
+ public static final String TAG = "ActionBarController";
+ private static final String KEY_IS_SLID_UP = "key_actionbar_is_slid_up";
+ private static final String KEY_IS_FADED_OUT = "key_actionbar_is_faded_out";
+ private static final String KEY_IS_EXPANDED = "key_actionbar_is_expanded";
+
+ private ActivityUi mActivityUi;
+ private SearchEditTextLayout mSearchBox;
+
+ private boolean mIsActionBarSlidUp;
+
+ private final AnimationCallback mFadeOutCallback =
+ new AnimationCallback() {
+ @Override
+ public void onAnimationEnd() {
+ slideActionBar(true /* slideUp */, false /* animate */);
+ }
+
+ @Override
+ public void onAnimationCancel() {
+ slideActionBar(true /* slideUp */, false /* animate */);
+ }
+ };
+
+ public ActionBarController(ActivityUi activityUi, SearchEditTextLayout searchBox) {
+ mActivityUi = activityUi;
+ mSearchBox = searchBox;
+ }
+
+ /** @return Whether or not the action bar is currently showing (both slid down and visible) */
+ public boolean isActionBarShowing() {
+ return !mIsActionBarSlidUp && !mSearchBox.isFadedOut();
+ }
+
+ /** Called when the user has tapped on the collapsed search box, to start a new search query. */
+ public void onSearchBoxTapped() {
+ if (DEBUG) {
+ Log.d(TAG, "OnSearchBoxTapped: isInSearchUi " + mActivityUi.isInSearchUi());
+ }
+ if (!mActivityUi.isInSearchUi()) {
+ mSearchBox.expand(true /* animate */, true /* requestFocus */);
+ }
+ }
+
+ /** Called when search UI has been exited for some reason. */
+ public void onSearchUiExited() {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "OnSearchUIExited: isExpanded "
+ + mSearchBox.isExpanded()
+ + " isFadedOut: "
+ + mSearchBox.isFadedOut()
+ + " shouldShowActionBar: "
+ + mActivityUi.shouldShowActionBar());
+ }
+ if (mSearchBox.isExpanded()) {
+ mSearchBox.collapse(true /* animate */);
+ }
+ if (mSearchBox.isFadedOut()) {
+ mSearchBox.fadeIn();
+ }
+
+ if (mActivityUi.shouldShowActionBar()) {
+ slideActionBar(false /* slideUp */, false /* animate */);
+ } else {
+ slideActionBar(true /* slideUp */, false /* animate */);
+ }
+ }
+
+ /**
+ * Called to indicate that the user is trying to hide the dialpad. Should be called before any
+ * state changes have actually occurred.
+ */
+ public void onDialpadDown() {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "OnDialpadDown: isInSearchUi "
+ + mActivityUi.isInSearchUi()
+ + " hasSearchQuery: "
+ + mActivityUi.hasSearchQuery()
+ + " isFadedOut: "
+ + mSearchBox.isFadedOut()
+ + " isExpanded: "
+ + mSearchBox.isExpanded());
+ }
+ if (mActivityUi.isInSearchUi()) {
+ if (mActivityUi.hasSearchQuery()) {
+ if (mSearchBox.isFadedOut()) {
+ mSearchBox.setVisible(true);
+ }
+ if (!mSearchBox.isExpanded()) {
+ mSearchBox.expand(false /* animate */, false /* requestFocus */);
+ }
+ slideActionBar(false /* slideUp */, true /* animate */);
+ } else {
+ mSearchBox.fadeIn();
+ }
+ }
+ }
+
+ /**
+ * Called to indicate that the user is trying to show the dialpad. Should be called before any
+ * state changes have actually occurred.
+ */
+ public void onDialpadUp() {
+ if (DEBUG) {
+ Log.d(TAG, "OnDialpadUp: isInSearchUi " + mActivityUi.isInSearchUi());
+ }
+ if (mActivityUi.isInSearchUi()) {
+ slideActionBar(true /* slideUp */, true /* animate */);
+ } else {
+ // From the lists fragment
+ mSearchBox.fadeOut(mFadeOutCallback);
+ }
+ }
+
+ public void slideActionBar(boolean slideUp, boolean animate) {
+ if (DEBUG) {
+ Log.d(TAG, "Sliding actionBar - up: " + slideUp + " animate: " + animate);
+ }
+ if (animate) {
+ ValueAnimator animator = slideUp ? ValueAnimator.ofFloat(0, 1) : ValueAnimator.ofFloat(1, 0);
+ animator.addUpdateListener(
+ new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ final float value = (float) animation.getAnimatedValue();
+ setHideOffset((int) (mActivityUi.getActionBarHeight() * value));
+ }
+ });
+ animator.start();
+ } else {
+ setHideOffset(slideUp ? mActivityUi.getActionBarHeight() : 0);
+ }
+ mIsActionBarSlidUp = slideUp;
+ }
+
+ public void setAlpha(float alphaValue) {
+ mSearchBox.animate().alpha(alphaValue).start();
+ }
+
+ /** @return The offset the action bar is being translated upwards by */
+ public int getHideOffset() {
+ return mActivityUi.getActionBarHideOffset();
+ }
+
+ public void setHideOffset(int offset) {
+ mIsActionBarSlidUp = offset >= mActivityUi.getActionBarHeight();
+ mActivityUi.setActionBarHideOffset(offset);
+ }
+
+ public int getActionBarHeight() {
+ return mActivityUi.getActionBarHeight();
+ }
+
+ /** Saves the current state of the action bar into a provided {@link Bundle} */
+ public void saveInstanceState(Bundle outState) {
+ outState.putBoolean(KEY_IS_SLID_UP, mIsActionBarSlidUp);
+ outState.putBoolean(KEY_IS_FADED_OUT, mSearchBox.isFadedOut());
+ outState.putBoolean(KEY_IS_EXPANDED, mSearchBox.isExpanded());
+ }
+
+ /** Restores the action bar state from a provided {@link Bundle}. */
+ public void restoreInstanceState(Bundle inState) {
+ mIsActionBarSlidUp = inState.getBoolean(KEY_IS_SLID_UP);
+
+ final boolean isSearchBoxFadedOut = inState.getBoolean(KEY_IS_FADED_OUT);
+ if (isSearchBoxFadedOut) {
+ if (!mSearchBox.isFadedOut()) {
+ mSearchBox.setVisible(false);
+ }
+ } else if (mSearchBox.isFadedOut()) {
+ mSearchBox.setVisible(true);
+ }
+
+ final boolean isSearchBoxExpanded = inState.getBoolean(KEY_IS_EXPANDED);
+ if (isSearchBoxExpanded) {
+ if (!mSearchBox.isExpanded()) {
+ mSearchBox.expand(false, false);
+ }
+ } else if (mSearchBox.isExpanded()) {
+ mSearchBox.collapse(false);
+ }
+ }
+
+ /**
+ * This should be called after onCreateOptionsMenu has been called, when the actionbar has been
+ * laid out and actually has a height.
+ */
+ public void restoreActionBarOffset() {
+ slideActionBar(mIsActionBarSlidUp /* slideUp */, false /* animate */);
+ }
+
+ @VisibleForTesting
+ public boolean getIsActionBarSlidUp() {
+ return mIsActionBarSlidUp;
+ }
+
+ public interface ActivityUi {
+
+ boolean isInSearchUi();
+
+ boolean hasSearchQuery();
+
+ boolean shouldShowActionBar();
+
+ int getActionBarHeight();
+
+ int getActionBarHideOffset();
+
+ void setActionBarHideOffset(int offset);
+ }
+}
diff --git a/java/com/android/dialer/app/widget/DialpadSearchEmptyContentView.java b/java/com/android/dialer/app/widget/DialpadSearchEmptyContentView.java
new file mode 100644
index 000000000..85fd5ec6a
--- /dev/null
+++ b/java/com/android/dialer/app/widget/DialpadSearchEmptyContentView.java
@@ -0,0 +1,43 @@
+/*
+ * 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.dialer.app.widget;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.widget.LinearLayout;
+import com.android.dialer.app.R;
+import com.android.dialer.util.OrientationUtil;
+
+/** Empty content view to be shown when dialpad is visible. */
+public class DialpadSearchEmptyContentView extends EmptyContentView {
+
+ public DialpadSearchEmptyContentView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void inflateLayout() {
+ int orientation =
+ OrientationUtil.isLandscape(getContext()) ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL;
+
+ setOrientation(orientation);
+
+ final LayoutInflater inflater =
+ (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.empty_content_view_dialpad_search, this);
+ }
+}
diff --git a/java/com/android/dialer/app/widget/EmptyContentView.java b/java/com/android/dialer/app/widget/EmptyContentView.java
new file mode 100644
index 000000000..cfc8665a2
--- /dev/null
+++ b/java/com/android/dialer/app/widget/EmptyContentView.java
@@ -0,0 +1,121 @@
+/*
+ * 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.dialer.app.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.android.dialer.app.R;
+
+public class EmptyContentView extends LinearLayout implements View.OnClickListener {
+
+ /** Listener to call when action button is clicked. */
+ public interface OnEmptyViewActionButtonClickedListener {
+ void onEmptyViewActionButtonClicked();
+ }
+
+ public static final int NO_LABEL = 0;
+ public static final int NO_IMAGE = 0;
+
+ private ImageView mImageView;
+ private TextView mDescriptionView;
+ private TextView mActionView;
+ private OnEmptyViewActionButtonClickedListener mOnActionButtonClickedListener;
+
+ public EmptyContentView(Context context) {
+ this(context, null);
+ }
+
+ public EmptyContentView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public EmptyContentView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public EmptyContentView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ inflateLayout();
+
+ // Don't let touches fall through the empty view.
+ setClickable(true);
+ mImageView = (ImageView) findViewById(R.id.emptyListViewImage);
+ mDescriptionView = (TextView) findViewById(R.id.emptyListViewMessage);
+ mActionView = (TextView) findViewById(R.id.emptyListViewAction);
+ mActionView.setOnClickListener(this);
+ }
+
+ public void setDescription(int resourceId) {
+ if (resourceId == NO_LABEL) {
+ mDescriptionView.setText(null);
+ mDescriptionView.setVisibility(View.GONE);
+ } else {
+ mDescriptionView.setText(resourceId);
+ mDescriptionView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ public void setImage(int resourceId) {
+ if (resourceId == NO_LABEL) {
+ mImageView.setImageDrawable(null);
+ mImageView.setVisibility(View.GONE);
+ } else {
+ mImageView.setImageResource(resourceId);
+ mImageView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ public void setActionLabel(int resourceId) {
+ if (resourceId == NO_LABEL) {
+ mActionView.setText(null);
+ mActionView.setVisibility(View.GONE);
+ } else {
+ mActionView.setText(resourceId);
+ mActionView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ public boolean isShowingContent() {
+ return mImageView.getVisibility() == View.VISIBLE
+ || mDescriptionView.getVisibility() == View.VISIBLE
+ || mActionView.getVisibility() == View.VISIBLE;
+ }
+
+ public void setActionClickedListener(OnEmptyViewActionButtonClickedListener listener) {
+ mOnActionButtonClickedListener = listener;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (mOnActionButtonClickedListener != null) {
+ mOnActionButtonClickedListener.onEmptyViewActionButtonClicked();
+ }
+ }
+
+ protected void inflateLayout() {
+ setOrientation(LinearLayout.VERTICAL);
+ final LayoutInflater inflater =
+ (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.empty_content_view, this);
+ }
+
+}
diff --git a/java/com/android/dialer/app/widget/SearchEditTextLayout.java b/java/com/android/dialer/app/widget/SearchEditTextLayout.java
new file mode 100644
index 000000000..be850f9a0
--- /dev/null
+++ b/java/com/android/dialer/app/widget/SearchEditTextLayout.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2014 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.dialer.app.widget;
+
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import com.android.dialer.animation.AnimUtils;
+import com.android.dialer.app.R;
+import com.android.dialer.util.DialerUtils;
+
+public class SearchEditTextLayout extends FrameLayout {
+
+ private static final float EXPAND_MARGIN_FRACTION_START = 0.8f;
+ private static final int ANIMATION_DURATION = 200;
+ /* Subclass-visible for testing */
+ protected boolean mIsExpanded = false;
+ protected boolean mIsFadedOut = false;
+ private OnKeyListener mPreImeKeyListener;
+ private int mTopMargin;
+ private int mBottomMargin;
+ private int mLeftMargin;
+ private int mRightMargin;
+ private float mCollapsedElevation;
+ private View mCollapsed;
+ private View mExpanded;
+ private EditText mSearchView;
+ private View mSearchIcon;
+ private View mCollapsedSearchBox;
+ private View mVoiceSearchButtonView;
+ private View mOverflowButtonView;
+ private View mBackButtonView;
+ private View mExpandedSearchBox;
+ private View mClearButtonView;
+
+ private ValueAnimator mAnimator;
+
+ private Callback mCallback;
+
+ public SearchEditTextLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setPreImeKeyListener(OnKeyListener listener) {
+ mPreImeKeyListener = listener;
+ }
+
+ public void setCallback(Callback listener) {
+ mCallback = listener;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
+ mTopMargin = params.topMargin;
+ mBottomMargin = params.bottomMargin;
+ mLeftMargin = params.leftMargin;
+ mRightMargin = params.rightMargin;
+
+ mCollapsedElevation = getElevation();
+
+ mCollapsed = findViewById(R.id.search_box_collapsed);
+ mExpanded = findViewById(R.id.search_box_expanded);
+ mSearchView = (EditText) mExpanded.findViewById(R.id.search_view);
+
+ mSearchIcon = findViewById(R.id.search_magnifying_glass);
+ mCollapsedSearchBox = findViewById(R.id.search_box_start_search);
+ mVoiceSearchButtonView = findViewById(R.id.voice_search_button);
+ mOverflowButtonView = findViewById(R.id.dialtacts_options_menu_button);
+ mBackButtonView = findViewById(R.id.search_back_button);
+ mExpandedSearchBox = findViewById(R.id.search_box_expanded);
+ mClearButtonView = findViewById(R.id.search_close_button);
+
+ // Convert a long click into a click to expand the search box, and then long click on the
+ // search view. This accelerates the long-press scenario for copy/paste.
+ mCollapsedSearchBox.setOnLongClickListener(
+ new OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View view) {
+ mCollapsedSearchBox.performClick();
+ mSearchView.performLongClick();
+ return false;
+ }
+ });
+
+ mSearchView.setOnFocusChangeListener(
+ new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus) {
+ DialerUtils.showInputMethod(v);
+ } else {
+ DialerUtils.hideInputMethod(v);
+ }
+ }
+ });
+
+ mSearchView.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mCallback != null) {
+ mCallback.onSearchViewClicked();
+ }
+ }
+ });
+
+ mSearchView.addTextChangedListener(
+ new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mClearButtonView.setVisibility(TextUtils.isEmpty(s) ? View.GONE : View.VISIBLE);
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {}
+ });
+
+ findViewById(R.id.search_close_button)
+ .setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mSearchView.setText(null);
+ }
+ });
+
+ findViewById(R.id.search_back_button)
+ .setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mCallback != null) {
+ mCallback.onBackButtonClicked();
+ }
+ }
+ });
+
+ super.onFinishInflate();
+ }
+
+ @Override
+ public boolean dispatchKeyEventPreIme(KeyEvent event) {
+ if (mPreImeKeyListener != null) {
+ if (mPreImeKeyListener.onKey(this, event.getKeyCode(), event)) {
+ return true;
+ }
+ }
+ return super.dispatchKeyEventPreIme(event);
+ }
+
+ public void fadeOut() {
+ fadeOut(null);
+ }
+
+ public void fadeOut(AnimUtils.AnimationCallback callback) {
+ AnimUtils.fadeOut(this, ANIMATION_DURATION, callback);
+ mIsFadedOut = true;
+ }
+
+ public void fadeIn() {
+ AnimUtils.fadeIn(this, ANIMATION_DURATION);
+ mIsFadedOut = false;
+ }
+
+ public void setVisible(boolean visible) {
+ if (visible) {
+ setAlpha(1);
+ setVisibility(View.VISIBLE);
+ mIsFadedOut = false;
+ } else {
+ setAlpha(0);
+ setVisibility(View.GONE);
+ mIsFadedOut = true;
+ }
+ }
+
+ public void expand(boolean animate, boolean requestFocus) {
+ updateVisibility(true /* isExpand */);
+
+ if (animate) {
+ AnimUtils.crossFadeViews(mExpanded, mCollapsed, ANIMATION_DURATION);
+ mAnimator = ValueAnimator.ofFloat(EXPAND_MARGIN_FRACTION_START, 0f);
+ setMargins(EXPAND_MARGIN_FRACTION_START);
+ prepareAnimator(true);
+ } else {
+ mExpanded.setVisibility(View.VISIBLE);
+ mExpanded.setAlpha(1);
+ setMargins(0f);
+ mCollapsed.setVisibility(View.GONE);
+ }
+
+ // Set 9-patch background. This owns the padding, so we need to restore the original values.
+ int paddingTop = this.getPaddingTop();
+ int paddingStart = this.getPaddingStart();
+ int paddingBottom = this.getPaddingBottom();
+ int paddingEnd = this.getPaddingEnd();
+ setBackgroundResource(R.drawable.search_shadow);
+ setElevation(0);
+ setPaddingRelative(paddingStart, paddingTop, paddingEnd, paddingBottom);
+
+ if (requestFocus) {
+ mSearchView.requestFocus();
+ }
+ mIsExpanded = true;
+ }
+
+ public void collapse(boolean animate) {
+ updateVisibility(false /* isExpand */);
+
+ if (animate) {
+ AnimUtils.crossFadeViews(mCollapsed, mExpanded, ANIMATION_DURATION);
+ mAnimator = ValueAnimator.ofFloat(0f, 1f);
+ prepareAnimator(false);
+ } else {
+ mCollapsed.setVisibility(View.VISIBLE);
+ mCollapsed.setAlpha(1);
+ setMargins(1f);
+ mExpanded.setVisibility(View.GONE);
+ }
+
+ mIsExpanded = false;
+ setElevation(mCollapsedElevation);
+ setBackgroundResource(R.drawable.rounded_corner);
+ }
+
+ /**
+ * Updates the visibility of views depending on whether we will show the expanded or collapsed
+ * search view. This helps prevent some jank with the crossfading if we are animating.
+ *
+ * @param isExpand Whether we are about to show the expanded search box.
+ */
+ private void updateVisibility(boolean isExpand) {
+ int collapsedViewVisibility = isExpand ? View.GONE : View.VISIBLE;
+ int expandedViewVisibility = isExpand ? View.VISIBLE : View.GONE;
+
+ mSearchIcon.setVisibility(collapsedViewVisibility);
+ mCollapsedSearchBox.setVisibility(collapsedViewVisibility);
+ mVoiceSearchButtonView.setVisibility(collapsedViewVisibility);
+ mOverflowButtonView.setVisibility(collapsedViewVisibility);
+ mBackButtonView.setVisibility(expandedViewVisibility);
+ // TODO: Prevents keyboard from jumping up in landscape mode after exiting the
+ // SearchFragment when the query string is empty. More elegant fix?
+ //mExpandedSearchBox.setVisibility(expandedViewVisibility);
+ if (TextUtils.isEmpty(mSearchView.getText())) {
+ mClearButtonView.setVisibility(View.GONE);
+ } else {
+ mClearButtonView.setVisibility(expandedViewVisibility);
+ }
+ }
+
+ private void prepareAnimator(final boolean expand) {
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+
+ mAnimator.addUpdateListener(
+ new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ final Float fraction = (Float) animation.getAnimatedValue();
+ setMargins(fraction);
+ }
+ });
+
+ mAnimator.setDuration(ANIMATION_DURATION);
+ mAnimator.start();
+ }
+
+ public boolean isExpanded() {
+ return mIsExpanded;
+ }
+
+ public boolean isFadedOut() {
+ return mIsFadedOut;
+ }
+
+ /**
+ * Assigns margins to the search box as a fraction of its maximum margin size
+ *
+ * @param fraction How large the margins should be as a fraction of their full size
+ */
+ private void setMargins(float fraction) {
+ MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
+ params.topMargin = (int) (mTopMargin * fraction);
+ params.bottomMargin = (int) (mBottomMargin * fraction);
+ params.leftMargin = (int) (mLeftMargin * fraction);
+ params.rightMargin = (int) (mRightMargin * fraction);
+ requestLayout();
+ }
+
+ /** Listener for the back button next to the search view being pressed */
+ public interface Callback {
+
+ void onBackButtonClicked();
+
+ void onSearchViewClicked();
+ }
+}
diff --git a/java/com/android/dialer/backup/AndroidManifest.xml b/java/com/android/dialer/backup/AndroidManifest.xml
new file mode 100644
index 000000000..cfdb3d93d
--- /dev/null
+++ b/java/com/android/dialer/backup/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ 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
+ -->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.dialer.backup">
+
+ <application
+ android:backupAgent="com.android.dialer.backup.DialerBackupAgent"
+ android:fullBackupOnly="true"
+ android:restoreAnyVersion="true"
+ android:name="com.android.dialer.app.DialerApplication"
+ />
+
+</manifest> \ No newline at end of file
diff --git a/java/com/android/dialer/backup/DialerBackupAgent.java b/java/com/android/dialer/backup/DialerBackupAgent.java
new file mode 100644
index 000000000..391a93f29
--- /dev/null
+++ b/java/com/android/dialer/backup/DialerBackupAgent.java
@@ -0,0 +1,276 @@
+/*
+ * 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.dialer.backup;
+
+import android.annotation.TargetApi;
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.FullBackupDataOutput;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.os.ParcelFileDescriptor;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.provider.VoicemailContract;
+import android.provider.VoicemailContract.Voicemails;
+import android.util.Pair;
+import com.android.dialer.backup.nano.VoicemailInfo;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.ConfigProviderBindings;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.telecom.TelecomUtil;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Locale;
+
+/**
+ * The Dialer backup agent to backup voicemails, and files under files, shared prefs and databases
+ */
+public class DialerBackupAgent extends BackupAgent {
+ // File names suffix for backup/restore.
+ private static final String VOICEMAIL_BACKUP_FILE_SUFFIX = "_voicemail_backup.proto";
+ // File name formats for backup. It looks like 000000_voicemail_backup.proto, 0000001...
+ private static final String VOICEMAIL_BACKUP_FILE_FORMAT = "%06d" + VOICEMAIL_BACKUP_FILE_SUFFIX;
+ // Order by Date entries from database. We start backup from the newest.
+ private static final String ORDER_BY_DATE = "date DESC";
+ // Voicemail Uri Column
+ public static final String VOICEMAIL_URI = "voicemail_uri";
+ // Voicemail packages to backup
+ public static final String VOICEMAIL_SOURCE_PACKAGE = "com.android.phone";
+
+ private long voicemailsBackedupSoFar = 0;
+ private long sizeOfVoicemailsBackedupSoFar = 0;
+ private boolean maxVoicemailBackupReached = false;
+
+ /**
+ * onBackup is used for Key/Value backup. Since we are using Dolly/Android Auto backup, we do not
+ * need to implement this method and Dolly should not be calling this. Instead Dolly will be
+ * calling onFullBackup.
+ */
+ @Override
+ public void onBackup(
+ ParcelFileDescriptor parcelFileDescriptor,
+ BackupDataOutput backupDataOutput,
+ ParcelFileDescriptor parcelFileDescriptor1)
+ throws IOException {
+ Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_BACKUP);
+ Assert.fail("Android Backup should not call DialerBackupAgent.onBackup");
+ }
+
+ /**
+ * onRestore is used for Key/Value restore. Since we are using Dolly/Android Auto backup/restore,
+ * we do not need to implement this method as Dolly should not be calling this method. Instead
+ * onFileRestore will be called by Dolly.
+ */
+ @Override
+ public void onRestore(
+ BackupDataInput backupDataInput, int i, ParcelFileDescriptor parcelFileDescriptor)
+ throws IOException {
+ Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_RESTORE);
+ Assert.fail("Android Backup should not call DialerBackupAgent.onRestore");
+ }
+
+ @TargetApi(VERSION_CODES.M)
+ @Override
+ public void onFullBackup(FullBackupDataOutput data) throws IOException {
+ Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_FULL_BACKUP);
+ LogUtil.i("DialerBackupAgent.onFullBackup", "performing dialer backup");
+ boolean autoBackupEnabled =
+ ConfigProviderBindings.get(this).getBoolean("enable_autobackup", true);
+ boolean vmBackupEnabled =
+ ConfigProviderBindings.get(this).getBoolean("enable_vm_backup", false);
+
+ if (autoBackupEnabled) {
+ if (!maxVoicemailBackupReached && vmBackupEnabled) {
+ voicemailsBackedupSoFar = 0;
+ sizeOfVoicemailsBackedupSoFar = 0;
+
+ LogUtil.i("DialerBackupAgent.onFullBackup", "autoBackup is enabled");
+ ContentResolver contentResolver = getContentResolver();
+ int limit = 1000;
+
+ Uri uri =
+ TelecomUtil.getCallLogUri(this)
+ .buildUpon()
+ .appendQueryParameter(Calls.LIMIT_PARAM_KEY, Integer.toString(limit))
+ .build();
+
+ LogUtil.i("DialerBackupAgent.onFullBackup", "backing up from: " + uri);
+
+ try (Cursor cursor =
+ contentResolver.query(
+ uri,
+ null,
+ String.format(
+ "(%s = ? AND deleted = 0 AND %s = ?)", Calls.TYPE, Voicemails.SOURCE_PACKAGE),
+ new String[] {
+ Integer.toString(CallLog.Calls.VOICEMAIL_TYPE), VOICEMAIL_SOURCE_PACKAGE
+ },
+ ORDER_BY_DATE,
+ null)) {
+
+ if (cursor == null) {
+ LogUtil.i("DialerBackupAgent.onFullBackup", "cursor was null");
+ return;
+ }
+
+ LogUtil.i("DialerBackupAgent.onFullBackup", "cursor count: " + cursor.getCount());
+ if (cursor.moveToFirst()) {
+ int fileNum = 0;
+ do {
+ backupRow(
+ data, cursor, String.format(Locale.US, VOICEMAIL_BACKUP_FILE_FORMAT, fileNum++));
+ } while (cursor.moveToNext() && !maxVoicemailBackupReached);
+ } else {
+ LogUtil.i("DialerBackupAgent.onFullBackup", "cursor.moveToFirst failed");
+ }
+ }
+ }
+ LogUtil.i(
+ "DialerBackupAgent.onFullBackup",
+ "vm files backed up: %d, vm size backed up:%d, "
+ + "max vm backup reached:%b, vm backup enabled:%b",
+ voicemailsBackedupSoFar,
+ sizeOfVoicemailsBackedupSoFar,
+ maxVoicemailBackupReached,
+ vmBackupEnabled);
+ super.onFullBackup(data);
+ Logger.get(this).logImpression(DialerImpression.Type.BACKUP_FULL_BACKED_UP);
+ } else {
+ Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_BACKUP_DISABLED);
+ LogUtil.i("DialerBackupAgent.onFullBackup", "autoBackup is disabled");
+ }
+ }
+
+ private void backupRow(FullBackupDataOutput data, Cursor cursor, String fileName)
+ throws IOException {
+
+ VoicemailInfo cursorRowInProto =
+ DialerBackupUtils.convertVoicemailCursorRowToProto(cursor, getContentResolver());
+
+ File file = new File(getFilesDir(), fileName);
+ DialerBackupUtils.writeProtoToFile(file, cursorRowInProto);
+
+ if (sizeOfVoicemailsBackedupSoFar + file.length()
+ > DialerBackupUtils.maxVoicemailSizeToBackup) {
+ Logger.get(this).logImpression(DialerImpression.Type.BACKUP_MAX_VM_BACKUP_REACHED);
+ maxVoicemailBackupReached = true;
+ file.delete();
+ return;
+ }
+
+ backupFile(file, data);
+ }
+
+ // TODO: Write to FullBackupDataOutput directly (b/33849960)
+ private void backupFile(File file, FullBackupDataOutput data) throws IOException {
+ try {
+ super.fullBackupFile(file, data);
+ sizeOfVoicemailsBackedupSoFar = sizeOfVoicemailsBackedupSoFar + file.length();
+ voicemailsBackedupSoFar++;
+ Logger.get(this).logImpression(DialerImpression.Type.BACKUP_VOICEMAIL_BACKED_UP);
+ LogUtil.i("DialerBackupAgent.backupFile", "file backed up:" + file.getAbsolutePath());
+ } finally {
+ file.delete();
+ }
+ }
+
+ // Being tracked in b/33839952
+ @Override
+ public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
+ Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_QUOTA_EXCEEDED);
+ LogUtil.i("DialerBackupAgent.onQuotaExceeded", "does nothing");
+ }
+
+ @TargetApi(VERSION_CODES.M)
+ @Override
+ public void onRestoreFile(
+ ParcelFileDescriptor data, long size, File destination, int type, long mode, long mtime)
+ throws IOException {
+ LogUtil.i("DialerBackupAgent.onRestoreFile", "size:" + size + " destination: " + destination);
+
+ String fileName = destination.getName();
+ LogUtil.i("DialerBackupAgent.onRestoreFile", "file name: " + fileName);
+
+ if (ConfigProviderBindings.get(this).getBoolean("enable_autobackup", true)) {
+ if (fileName.endsWith(VOICEMAIL_BACKUP_FILE_SUFFIX)
+ && ConfigProviderBindings.get(this).getBoolean("enable_vm_restore", true)) {
+ if (DialerBackupUtils.canRestoreVoicemails(getContentResolver(), this)) {
+ try {
+ super.onRestoreFile(data, size, destination, type, mode, mtime);
+ restoreVoicemail(destination);
+ destination.delete();
+ } catch (IOException e) {
+ Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_RESTORE_IO_EXCEPTION);
+ LogUtil.e(
+ "DialerBackupAgent.onRestoreFile",
+ "could not restore voicemail - IOException: ",
+ e);
+ }
+ } else {
+ LogUtil.i(
+ "DialerBackupAgent.onRestoreFile", "build does not support restoring voicemails");
+ }
+
+ } else {
+ super.onRestoreFile(data, size, destination, type, mode, mtime);
+ LogUtil.i("DialerBackupAgent.onRestoreFile", "restored: " + fileName);
+ Logger.get(this).logImpression(DialerImpression.Type.BACKUP_RESTORED_FILE);
+ }
+ } else {
+ Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_RESTORE_DISABLED);
+ LogUtil.i("DialerBackupAgent.onRestoreFile", "autoBackup is disabled");
+ }
+ }
+
+ @Override
+ public void onRestoreFinished() {
+ Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_RESTORE_FINISHED);
+ LogUtil.i("DialerBackupAgent.onRestoreFinished", "do nothing");
+ }
+
+ @TargetApi(VERSION_CODES.M)
+ private void restoreVoicemail(File file) throws IOException {
+ Pair<ContentValues, byte[]> pair =
+ DialerBackupUtils.convertVoicemailProtoFileToContentValueAndAudioBytes(
+ file, getApplicationContext());
+
+ if (pair == null) {
+ LogUtil.i("DialerBackupAgent.restoreVoicemail", "not restoring VM due to duplicate");
+ Logger.get(this)
+ .logImpression(DialerImpression.Type.BACKUP_ON_RESTORE_VM_DUPLICATE_NOT_RESTORING);
+ return;
+ }
+
+ // TODO: Uniquely identify backup agent as the creator of this voicemail b/34084298
+ try (OutputStream restoreStream =
+ getContentResolver()
+ .openOutputStream(
+ getContentResolver()
+ .insert(VoicemailContract.Voicemails.CONTENT_URI, pair.first))) {
+ DialerBackupUtils.copyAudioBytesToContentUri(pair.second, restoreStream);
+ Logger.get(this).logImpression(DialerImpression.Type.BACKUP_RESTORED_VOICEMAIL);
+ }
+ }
+}
diff --git a/java/com/android/dialer/backup/DialerBackupUtils.java b/java/com/android/dialer/backup/DialerBackupUtils.java
new file mode 100644
index 000000000..ff0cb4f7c
--- /dev/null
+++ b/java/com/android/dialer/backup/DialerBackupUtils.java
@@ -0,0 +1,320 @@
+/*
+ * 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.dialer.backup;
+
+import android.annotation.TargetApi;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.provider.VoicemailContract;
+import android.provider.VoicemailContract.Voicemails;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Pair;
+import com.android.dialer.backup.nano.VoicemailInfo;
+import com.android.dialer.common.ConfigProviderBindings;
+import com.android.dialer.common.LogUtil;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Files;
+import com.google.protobuf.nano.MessageNano;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/** Helper functions for DialerBackupAgent */
+public class DialerBackupUtils {
+ // Backup voicemails up to 20MB
+ static long maxVoicemailSizeToBackup = 20000000L;
+ static final String RESTORED_COLUMN = "restored";
+
+ private DialerBackupUtils() {}
+
+ public static void copyAudioBytesToContentUri(
+ @NonNull byte[] audioBytesArray, @NonNull OutputStream restoreStream) throws IOException {
+ LogUtil.i("DialerBackupUtils.copyStream", "audioByteArray length: " + audioBytesArray.length);
+
+ ByteArrayInputStream decodedStream = new ByteArrayInputStream(audioBytesArray);
+ LogUtil.i(
+ "DialerBackupUtils.copyStream", "decodedStream.available: " + decodedStream.available());
+
+ ByteStreams.copy(decodedStream, restoreStream);
+ }
+
+ public static @Nullable byte[] audioStreamToByteArray(@NonNull InputStream stream)
+ throws IOException {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ if (stream.available() > 0) {
+ ByteStreams.copy(stream, buffer);
+ } else {
+ LogUtil.i("DialerBackupUtils.audioStreamToByteArray", "no audio stream to backup");
+ }
+ return buffer.toByteArray();
+ }
+
+ public static void writeProtoToFile(@NonNull File file, @NonNull VoicemailInfo voicemailInfo)
+ throws IOException {
+ LogUtil.i(
+ "DialerBackupUtils.writeProtoToFile",
+ "backup " + voicemailInfo + " to " + file.getAbsolutePath());
+
+ byte[] bytes = MessageNano.toByteArray(voicemailInfo);
+ Files.write(bytes, file);
+ }
+
+ /** Only restore voicemails that have the restored column in calllog (NMR2+ builds) */
+ @TargetApi(VERSION_CODES.M)
+ public static boolean canRestoreVoicemails(ContentResolver contentResolver, Context context) {
+ try (Cursor cursor = contentResolver.query(Voicemails.CONTENT_URI, null, null, null, null)) {
+ // Restored column only exists in NMR2 and above builds.
+ if (cursor.getColumnIndex(RESTORED_COLUMN) != -1) {
+ LogUtil.i("DialerBackupUtils.canRestoreVoicemails", "Build supports restore");
+ return true;
+ } else {
+ LogUtil.i("DialerBackupUtils.canRestoreVoicemails", "Build does not support restore");
+ return false;
+ }
+ }
+ }
+
+ public static VoicemailInfo protoFileToVoicemailInfo(@NonNull File file) throws IOException {
+ byte[] byteArray = Files.toByteArray(file);
+ return VoicemailInfo.parseFrom(byteArray);
+ }
+
+ @TargetApi(VERSION_CODES.M)
+ public static VoicemailInfo convertVoicemailCursorRowToProto(
+ @NonNull Cursor cursor, @NonNull ContentResolver contentResolver) throws IOException {
+
+ VoicemailInfo voicemailInfo = new VoicemailInfo();
+
+ for (int i = 0; i < cursor.getColumnCount(); ++i) {
+ String name = cursor.getColumnName(i);
+ String value = cursor.getString(i);
+
+ LogUtil.i(
+ "DialerBackupUtils.convertVoicemailCursorRowToProto",
+ "column index: %d, column name: %s, column value: %s",
+ i,
+ name,
+ value);
+
+ switch (name) {
+ case Voicemails.DATE:
+ voicemailInfo.date = value;
+ break;
+ case Voicemails.DELETED:
+ voicemailInfo.deleted = value;
+ break;
+ case Voicemails.DIRTY:
+ voicemailInfo.dirty = value;
+ break;
+ case Voicemails.DIR_TYPE:
+ voicemailInfo.dirType = value;
+ break;
+ case Voicemails.DURATION:
+ voicemailInfo.duration = value;
+ break;
+ case Voicemails.HAS_CONTENT:
+ voicemailInfo.hasContent = value;
+ break;
+ case Voicemails.IS_READ:
+ voicemailInfo.isRead = value;
+ break;
+ case Voicemails.ITEM_TYPE:
+ voicemailInfo.itemType = value;
+ break;
+ case Voicemails.LAST_MODIFIED:
+ voicemailInfo.lastModified = value;
+ break;
+ case Voicemails.MIME_TYPE:
+ voicemailInfo.mimeType = value;
+ break;
+ case Voicemails.NUMBER:
+ voicemailInfo.number = value;
+ break;
+ case Voicemails.PHONE_ACCOUNT_COMPONENT_NAME:
+ voicemailInfo.phoneAccountComponentName = value;
+ break;
+ case Voicemails.PHONE_ACCOUNT_ID:
+ voicemailInfo.phoneAccountId = value;
+ break;
+ case Voicemails.SOURCE_DATA:
+ voicemailInfo.sourceData = value;
+ break;
+ case Voicemails.SOURCE_PACKAGE:
+ voicemailInfo.sourcePackage = value;
+ break;
+ case Voicemails.TRANSCRIPTION:
+ voicemailInfo.transcription = value;
+ break;
+ case DialerBackupAgent.VOICEMAIL_URI:
+ try (InputStream audioStream = contentResolver.openInputStream(Uri.parse(value))) {
+ voicemailInfo.encodedVoicemailKey = audioStreamToByteArray(audioStream);
+ }
+ break;
+ default:
+ LogUtil.i(
+ "DialerBackupUtils.convertVoicemailCursorRowToProto",
+ "Not backing up column: %s, with value: %s",
+ name,
+ value);
+ break;
+ }
+ }
+ return voicemailInfo;
+ }
+
+ public static Pair<ContentValues, byte[]> convertVoicemailProtoFileToContentValueAndAudioBytes(
+ @NonNull File file, Context context) throws IOException {
+
+ VoicemailInfo voicemailInfo = DialerBackupUtils.protoFileToVoicemailInfo(file);
+ LogUtil.i(
+ "DialerBackupUtils.convertVoicemailProtoFileToContentValueAndEncodedAudio",
+ "file name: "
+ + file.getName()
+ + " voicemailInfo size: "
+ + voicemailInfo.getSerializedSize());
+
+ if (isDuplicate(context, voicemailInfo)) {
+ LogUtil.i(
+ "DialerBackupUtils.convertVoicemailProtoFileToContentValueAndEncodedAudio",
+ "voicemail already exists");
+ return null;
+ } else {
+ ContentValues contentValues = new ContentValues();
+
+ if (!voicemailInfo.date.isEmpty()) {
+ contentValues.put(Voicemails.DATE, voicemailInfo.date);
+ }
+ if (!voicemailInfo.deleted.isEmpty()) {
+ contentValues.put(Voicemails.DELETED, voicemailInfo.deleted);
+ }
+ if (!voicemailInfo.dirty.isEmpty()) {
+ contentValues.put(Voicemails.DIRTY, voicemailInfo.dirty);
+ }
+ if (!voicemailInfo.duration.isEmpty()) {
+ contentValues.put(Voicemails.DURATION, voicemailInfo.duration);
+ }
+ if (!voicemailInfo.isRead.isEmpty()) {
+ contentValues.put(Voicemails.IS_READ, voicemailInfo.isRead);
+ }
+ if (!voicemailInfo.lastModified.isEmpty()) {
+ contentValues.put(Voicemails.LAST_MODIFIED, voicemailInfo.lastModified);
+ }
+ if (!voicemailInfo.mimeType.isEmpty()) {
+ contentValues.put(Voicemails.MIME_TYPE, voicemailInfo.mimeType);
+ }
+ if (!voicemailInfo.number.isEmpty()) {
+ contentValues.put(Voicemails.NUMBER, voicemailInfo.number);
+ }
+ if (!voicemailInfo.phoneAccountComponentName.isEmpty()) {
+ contentValues.put(
+ Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, voicemailInfo.phoneAccountComponentName);
+ }
+ if (!voicemailInfo.phoneAccountId.isEmpty()) {
+ contentValues.put(Voicemails.PHONE_ACCOUNT_ID, voicemailInfo.phoneAccountId);
+ }
+ if (!voicemailInfo.sourceData.isEmpty()) {
+ contentValues.put(Voicemails.SOURCE_DATA, voicemailInfo.sourceData);
+ }
+ if (!voicemailInfo.sourcePackage.isEmpty()) {
+ contentValues.put(Voicemails.SOURCE_PACKAGE, voicemailInfo.sourcePackage);
+ }
+ if (!voicemailInfo.transcription.isEmpty()) {
+ contentValues.put(Voicemails.TRANSCRIPTION, voicemailInfo.transcription);
+ }
+ contentValues.put(VoicemailContract.Voicemails.HAS_CONTENT, 1);
+ contentValues.put(RESTORED_COLUMN, "1");
+ contentValues.put(Voicemails.SOURCE_PACKAGE, getSourcePackage(context, voicemailInfo));
+
+ LogUtil.i(
+ "DialerBackupUtils.convertVoicemailProtoFileToContentValueAndEncodedAudio",
+ "cv: " + contentValues);
+
+ return Pair.create(contentValues, voicemailInfo.encodedVoicemailKey);
+ }
+ }
+
+ /**
+ * We should be using the system package name as the source package if there is no endless VM/VM
+ * archive present on the device. This is to separate pre-O (no endless VM) and O+ (endless VM)
+ * devices. This ensures that the source of truth for VMs is the VM server when endless VM is not
+ * enabled, and when endless VM/archived VMs is present, the source of truth for VMs is the device
+ * itself.
+ */
+ private static String getSourcePackage(Context context, VoicemailInfo voicemailInfo) {
+ if (ConfigProviderBindings.get(context)
+ .getBoolean("voicemail_restore_force_system_source_package", false)) {
+ LogUtil.i("DialerBackupUtils.getSourcePackage", "forcing system source package");
+ return "com.android.phone";
+ }
+ if (ConfigProviderBindings.get(context)
+ .getBoolean("voicemail_restore_check_archive_for_source_package", true)) {
+ if ("1".equals(voicemailInfo.archived)) {
+ LogUtil.i(
+ "DialerBackupUtils.getSourcePackage",
+ "voicemail was archived, using app source package");
+ // Using our app's source package will prevent the archived voicemail from being deleted by
+ // the system when it syncs with the voicemail server. In most cases the user will not see
+ // duplicate voicemails because this voicemail was archived and likely deleted from the
+ // voicemail server.
+ return context.getPackageName();
+ } else {
+ // Use the system source package. This means that if the voicemail is not present on the
+ // voicemail server then the system will delete it when it syncs.
+ LogUtil.i(
+ "DialerBackupUtils.getSourcePackage",
+ "voicemail was not archived, using system source package");
+ return "com.android.phone";
+ }
+ }
+ // Use our app's source package. This means that if the system syncs voicemail from the server
+ // the user could potentially get duplicate voicemails.
+ LogUtil.i("DialerBackupUtils.getSourcePackage", "defaulting to using app source package");
+ return context.getPackageName();
+ }
+
+ @TargetApi(VERSION_CODES.M)
+ private static boolean isDuplicate(Context context, VoicemailInfo voicemailInfo) {
+ // This checks for VM that might already exist, and doesn't restore them
+ try (Cursor cursor =
+ context
+ .getContentResolver()
+ .query(
+ VoicemailContract.Voicemails.CONTENT_URI,
+ null,
+ String.format(
+ "(%s = ? AND %s = ? AND %s = ?)",
+ Voicemails.NUMBER, Voicemails.DATE, Voicemails.DURATION),
+ new String[] {voicemailInfo.number, voicemailInfo.date, voicemailInfo.duration},
+ null,
+ null)) {
+ if (cursor.moveToFirst()
+ && ConfigProviderBindings.get(context)
+ .getBoolean("enable_vm_restore_no_duplicate", true)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/java/com/android/dialer/backup/proto/VoicemailInfo.java b/java/com/android/dialer/backup/proto/VoicemailInfo.java
new file mode 100644
index 000000000..9ff8423f3
--- /dev/null
+++ b/java/com/android/dialer/backup/proto/VoicemailInfo.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+
+package com.android.dialer.backup.nano;
+
+@SuppressWarnings("hiding")
+public final class VoicemailInfo extends
+ com.google.protobuf.nano.ExtendableMessageNano<VoicemailInfo> {
+
+ private static volatile VoicemailInfo[] _emptyArray;
+ public static VoicemailInfo[] emptyArray() {
+ // Lazily initializes the empty array
+ if (_emptyArray == null) {
+ synchronized (
+ com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
+ if (_emptyArray == null) {
+ _emptyArray = new VoicemailInfo[0];
+ }
+ }
+ }
+ return _emptyArray;
+ }
+
+ // optional string date = 1;
+ public java.lang.String date;
+
+ // optional string deleted = 2;
+ public java.lang.String deleted;
+
+ // optional string dirty = 3;
+ public java.lang.String dirty;
+
+ // optional string dir_type = 4;
+ public java.lang.String dirType;
+
+ // optional string duration = 5;
+ public java.lang.String duration;
+
+ // optional string has_content = 6;
+ public java.lang.String hasContent;
+
+ // optional string is_read = 7;
+ public java.lang.String isRead;
+
+ // optional string item_type = 8;
+ public java.lang.String itemType;
+
+ // optional string last_modified = 9;
+ public java.lang.String lastModified;
+
+ // optional string mime_type = 10;
+ public java.lang.String mimeType;
+
+ // optional string number = 11;
+ public java.lang.String number;
+
+ // optional string phone_account_component_name = 12;
+ public java.lang.String phoneAccountComponentName;
+
+ // optional string phone_account_id = 13;
+ public java.lang.String phoneAccountId;
+
+ // optional string source_data = 14;
+ public java.lang.String sourceData;
+
+ // optional string source_package = 15;
+ public java.lang.String sourcePackage;
+
+ // optional string transcription = 16;
+ public java.lang.String transcription;
+
+ // optional string voicemail_uri = 17;
+ public java.lang.String voicemailUri;
+
+ // optional bytes encoded_voicemail_key = 18;
+ public byte[] encodedVoicemailKey;
+
+ // optional string archived = 19;
+ public java.lang.String archived;
+
+ // @@protoc_insertion_point(class_scope:com.android.dialer.backup.VoicemailInfo)
+
+ public VoicemailInfo() {
+ clear();
+ }
+
+ public VoicemailInfo clear() {
+ date = "";
+ deleted = "";
+ dirty = "";
+ dirType = "";
+ duration = "";
+ hasContent = "";
+ isRead = "";
+ itemType = "";
+ lastModified = "";
+ mimeType = "";
+ number = "";
+ phoneAccountComponentName = "";
+ phoneAccountId = "";
+ sourceData = "";
+ sourcePackage = "";
+ transcription = "";
+ voicemailUri = "";
+ encodedVoicemailKey = com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES;
+ archived = "";
+ unknownFieldData = null;
+ cachedSize = -1;
+ return this;
+ }
+
+ @Override
+ public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)
+ throws java.io.IOException {
+ if (this.date != null && !this.date.equals("")) {
+ output.writeString(1, this.date);
+ }
+ if (this.deleted != null && !this.deleted.equals("")) {
+ output.writeString(2, this.deleted);
+ }
+ if (this.dirty != null && !this.dirty.equals("")) {
+ output.writeString(3, this.dirty);
+ }
+ if (this.dirType != null && !this.dirType.equals("")) {
+ output.writeString(4, this.dirType);
+ }
+ if (this.duration != null && !this.duration.equals("")) {
+ output.writeString(5, this.duration);
+ }
+ if (this.hasContent != null && !this.hasContent.equals("")) {
+ output.writeString(6, this.hasContent);
+ }
+ if (this.isRead != null && !this.isRead.equals("")) {
+ output.writeString(7, this.isRead);
+ }
+ if (this.itemType != null && !this.itemType.equals("")) {
+ output.writeString(8, this.itemType);
+ }
+ if (this.lastModified != null && !this.lastModified.equals("")) {
+ output.writeString(9, this.lastModified);
+ }
+ if (this.mimeType != null && !this.mimeType.equals("")) {
+ output.writeString(10, this.mimeType);
+ }
+ if (this.number != null && !this.number.equals("")) {
+ output.writeString(11, this.number);
+ }
+ if (this.phoneAccountComponentName != null && !this.phoneAccountComponentName.equals("")) {
+ output.writeString(12, this.phoneAccountComponentName);
+ }
+ if (this.phoneAccountId != null && !this.phoneAccountId.equals("")) {
+ output.writeString(13, this.phoneAccountId);
+ }
+ if (this.sourceData != null && !this.sourceData.equals("")) {
+ output.writeString(14, this.sourceData);
+ }
+ if (this.sourcePackage != null && !this.sourcePackage.equals("")) {
+ output.writeString(15, this.sourcePackage);
+ }
+ if (this.transcription != null && !this.transcription.equals("")) {
+ output.writeString(16, this.transcription);
+ }
+ if (this.voicemailUri != null && !this.voicemailUri.equals("")) {
+ output.writeString(17, this.voicemailUri);
+ }
+ if (!java.util.Arrays.equals(this.encodedVoicemailKey, com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES)) {
+ output.writeBytes(18, this.encodedVoicemailKey);
+ }
+ if (this.archived != null && !this.archived.equals("")) {
+ output.writeString(19, this.archived);
+ }
+ super.writeTo(output);
+ }
+
+ @Override
+ protected int computeSerializedSize() {
+ int size = super.computeSerializedSize();
+ if (this.date != null && !this.date.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(1, this.date);
+ }
+ if (this.deleted != null && !this.deleted.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(2, this.deleted);
+ }
+ if (this.dirty != null && !this.dirty.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(3, this.dirty);
+ }
+ if (this.dirType != null && !this.dirType.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(4, this.dirType);
+ }
+ if (this.duration != null && !this.duration.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(5, this.duration);
+ }
+ if (this.hasContent != null && !this.hasContent.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(6, this.hasContent);
+ }
+ if (this.isRead != null && !this.isRead.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(7, this.isRead);
+ }
+ if (this.itemType != null && !this.itemType.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(8, this.itemType);
+ }
+ if (this.lastModified != null && !this.lastModified.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(9, this.lastModified);
+ }
+ if (this.mimeType != null && !this.mimeType.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(10, this.mimeType);
+ }
+ if (this.number != null && !this.number.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(11, this.number);
+ }
+ if (this.phoneAccountComponentName != null && !this.phoneAccountComponentName.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(12, this.phoneAccountComponentName);
+ }
+ if (this.phoneAccountId != null && !this.phoneAccountId.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(13, this.phoneAccountId);
+ }
+ if (this.sourceData != null && !this.sourceData.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(14, this.sourceData);
+ }
+ if (this.sourcePackage != null && !this.sourcePackage.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(15, this.sourcePackage);
+ }
+ if (this.transcription != null && !this.transcription.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(16, this.transcription);
+ }
+ if (this.voicemailUri != null && !this.voicemailUri.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(17, this.voicemailUri);
+ }
+ if (!java.util.Arrays.equals(this.encodedVoicemailKey, com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES)) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeBytesSize(18, this.encodedVoicemailKey);
+ }
+ if (this.archived != null && !this.archived.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano
+ .computeStringSize(19, this.archived);
+ }
+ return size;
+ }
+
+ @Override
+ public VoicemailInfo mergeFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ while (true) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ return this;
+ default: {
+ if (!super.storeUnknownField(input, tag)) {
+ return this;
+ }
+ break;
+ }
+ case 10: {
+ this.date = input.readString();
+ break;
+ }
+ case 18: {
+ this.deleted = input.readString();
+ break;
+ }
+ case 26: {
+ this.dirty = input.readString();
+ break;
+ }
+ case 34: {
+ this.dirType = input.readString();
+ break;
+ }
+ case 42: {
+ this.duration = input.readString();
+ break;
+ }
+ case 50: {
+ this.hasContent = input.readString();
+ break;
+ }
+ case 58: {
+ this.isRead = input.readString();
+ break;
+ }
+ case 66: {
+ this.itemType = input.readString();
+ break;
+ }
+ case 74: {
+ this.lastModified = input.readString();
+ break;
+ }
+ case 82: {
+ this.mimeType = input.readString();
+ break;
+ }
+ case 90: {
+ this.number = input.readString();
+ break;
+ }
+ case 98: {
+ this.phoneAccountComponentName = input.readString();
+ break;
+ }
+ case 106: {
+ this.phoneAccountId = input.readString();
+ break;
+ }
+ case 114: {
+ this.sourceData = input.readString();
+ break;
+ }
+ case 122: {
+ this.sourcePackage = input.readString();
+ break;
+ }
+ case 130: {
+ this.transcription = input.readString();
+ break;
+ }
+ case 138: {
+ this.voicemailUri = input.readString();
+ break;
+ }
+ case 146: {
+ this.encodedVoicemailKey = input.readBytes();
+ break;
+ }
+ case 154: {
+ this.archived = input.readString();
+ break;
+ }
+ }
+ }
+ }
+
+ public static VoicemailInfo parseFrom(byte[] data)
+ throws com.google.protobuf.nano.InvalidProtocolBufferNanoException {
+ return com.google.protobuf.nano.MessageNano.mergeFrom(new VoicemailInfo(), data);
+ }
+
+ public static VoicemailInfo parseFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ return new VoicemailInfo().mergeFrom(input);
+ }
+}
diff --git a/java/com/android/dialer/blocking/AndroidManifest.xml b/java/com/android/dialer/blocking/AndroidManifest.xml
new file mode 100644
index 000000000..08d243988
--- /dev/null
+++ b/java/com/android/dialer/blocking/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.dialer.blocking">
+
+ <application android:theme="@style/Theme.AppCompat">
+
+ <provider
+ android:authorities="com.android.dialer.blocking.filterednumberprovider"
+ android:exported="false"
+ android:multiprocess="false"
+ android:name="com.android.dialer.blocking.FilteredNumberProvider"/>
+
+ </application>
+</manifest>
diff --git a/java/com/android/dialer/blocking/BlockNumberDialogFragment.java b/java/com/android/dialer/blocking/BlockNumberDialogFragment.java
new file mode 100644
index 000000000..c405b2fe7
--- /dev/null
+++ b/java/com/android/dialer/blocking/BlockNumberDialogFragment.java
@@ -0,0 +1,328 @@
+/*
+ * 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.dialer.blocking;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.design.widget.Snackbar;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.Toast;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler.OnBlockNumberListener;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler.OnUnblockNumberListener;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.InteractionEvent;
+import com.android.dialer.voicemailstatus.VisualVoicemailEnabledChecker;
+
+/**
+ * Fragment for confirming and enacting blocking/unblocking a number. Also invokes snackbar
+ * providing undo functionality.
+ */
+public class BlockNumberDialogFragment extends DialogFragment {
+
+ private static final String BLOCK_DIALOG_FRAGMENT = "BlockNumberDialog";
+ private static final String ARG_BLOCK_ID = "argBlockId";
+ private static final String ARG_NUMBER = "argNumber";
+ private static final String ARG_COUNTRY_ISO = "argCountryIso";
+ private static final String ARG_DISPLAY_NUMBER = "argDisplayNumber";
+ private static final String ARG_PARENT_VIEW_ID = "parentViewId";
+ private String mNumber;
+ private String mDisplayNumber;
+ private String mCountryIso;
+ private FilteredNumberAsyncQueryHandler mHandler;
+ private View mParentView;
+ private VisualVoicemailEnabledChecker mVoicemailEnabledChecker;
+ private Callback mCallback;
+
+ public static BlockNumberDialogFragment show(
+ Integer blockId,
+ String number,
+ String countryIso,
+ String displayNumber,
+ Integer parentViewId,
+ FragmentManager fragmentManager,
+ Callback callback) {
+ final BlockNumberDialogFragment newFragment =
+ BlockNumberDialogFragment.newInstance(
+ blockId, number, countryIso, displayNumber, parentViewId);
+
+ newFragment.setCallback(callback);
+ newFragment.show(fragmentManager, BlockNumberDialogFragment.BLOCK_DIALOG_FRAGMENT);
+ return newFragment;
+ }
+
+ private static BlockNumberDialogFragment newInstance(
+ Integer blockId,
+ String number,
+ String countryIso,
+ String displayNumber,
+ Integer parentViewId) {
+ final BlockNumberDialogFragment fragment = new BlockNumberDialogFragment();
+ final Bundle args = new Bundle();
+ if (blockId != null) {
+ args.putInt(ARG_BLOCK_ID, blockId.intValue());
+ }
+ if (parentViewId != null) {
+ args.putInt(ARG_PARENT_VIEW_ID, parentViewId.intValue());
+ }
+ args.putString(ARG_NUMBER, number);
+ args.putString(ARG_COUNTRY_ISO, countryIso);
+ args.putString(ARG_DISPLAY_NUMBER, displayNumber);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ public void setFilteredNumberAsyncQueryHandlerForTesting(
+ FilteredNumberAsyncQueryHandler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public Context getContext() {
+ return getActivity();
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+ final boolean isBlocked = getArguments().containsKey(ARG_BLOCK_ID);
+
+ mNumber = getArguments().getString(ARG_NUMBER);
+ mDisplayNumber = getArguments().getString(ARG_DISPLAY_NUMBER);
+ mCountryIso = getArguments().getString(ARG_COUNTRY_ISO);
+
+ if (TextUtils.isEmpty(mDisplayNumber)) {
+ mDisplayNumber = mNumber;
+ }
+
+ mHandler = new FilteredNumberAsyncQueryHandler(getContext());
+ mVoicemailEnabledChecker = new VisualVoicemailEnabledChecker(getActivity(), null);
+ // Choose not to update VoicemailEnabledChecker, as checks should already been done in
+ // all current use cases.
+ mParentView = getActivity().findViewById(getArguments().getInt(ARG_PARENT_VIEW_ID));
+
+ CharSequence title;
+ String okText;
+ String message;
+ if (isBlocked) {
+ title = null;
+ okText = getString(R.string.unblock_number_ok);
+ message =
+ ContactDisplayUtils.getTtsSpannedPhoneNumber(
+ getResources(), R.string.unblock_number_confirmation_title, mDisplayNumber)
+ .toString();
+ } else {
+ title =
+ ContactDisplayUtils.getTtsSpannedPhoneNumber(
+ getResources(), R.string.block_number_confirmation_title, mDisplayNumber);
+ okText = getString(R.string.block_number_ok);
+ if (FilteredNumberCompat.useNewFiltering(getContext())) {
+ message = getString(R.string.block_number_confirmation_message_new_filtering);
+ } else if (mVoicemailEnabledChecker.isVisualVoicemailEnabled()) {
+ message = getString(R.string.block_number_confirmation_message_vvm);
+ } else {
+ message = getString(R.string.block_number_confirmation_message_no_vvm);
+ }
+ }
+
+ AlertDialog.Builder builder =
+ new AlertDialog.Builder(getActivity())
+ .setTitle(title)
+ .setMessage(message)
+ .setPositiveButton(
+ okText,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ if (isBlocked) {
+ unblockNumber();
+ } else {
+ blockNumber();
+ }
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null);
+ return builder.create();
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ String e164Number = PhoneNumberUtils.formatNumberToE164(mNumber, mCountryIso);
+ if (!FilteredNumbersUtil.canBlockNumber(getContext(), e164Number, mNumber)) {
+ dismiss();
+ Toast.makeText(
+ getContext(),
+ ContactDisplayUtils.getTtsSpannedPhoneNumber(
+ getResources(), R.string.invalidNumber, mDisplayNumber),
+ Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ // Dismiss on rotation.
+ dismiss();
+ mCallback = null;
+
+ super.onPause();
+ }
+
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ private CharSequence getBlockedMessage() {
+ return ContactDisplayUtils.getTtsSpannedPhoneNumber(
+ getResources(), R.string.snackbar_number_blocked, mDisplayNumber);
+ }
+
+ private CharSequence getUnblockedMessage() {
+ return ContactDisplayUtils.getTtsSpannedPhoneNumber(
+ getResources(), R.string.snackbar_number_unblocked, mDisplayNumber);
+ }
+
+ private int getActionTextColor() {
+ return getContext().getResources().getColor(R.color.dialer_snackbar_action_text_color);
+ }
+
+ private void blockNumber() {
+ final CharSequence message = getBlockedMessage();
+ final CharSequence undoMessage = getUnblockedMessage();
+ final Callback callback = mCallback;
+ final int actionTextColor = getActionTextColor();
+ final Context applicationContext = getContext().getApplicationContext();
+
+ final OnUnblockNumberListener onUndoListener =
+ new OnUnblockNumberListener() {
+ @Override
+ public void onUnblockComplete(int rows, ContentValues values) {
+ Snackbar.make(mParentView, undoMessage, Snackbar.LENGTH_LONG).show();
+ if (callback != null) {
+ callback.onChangeFilteredNumberUndo();
+ }
+ }
+ };
+
+ final OnBlockNumberListener onBlockNumberListener =
+ new OnBlockNumberListener() {
+ @Override
+ public void onBlockComplete(final Uri uri) {
+ final View.OnClickListener undoListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ // Delete the newly created row on 'undo'.
+ Logger.get(applicationContext)
+ .logInteraction(InteractionEvent.Type.UNDO_BLOCK_NUMBER);
+ mHandler.unblock(onUndoListener, uri);
+ }
+ };
+
+ Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG)
+ .setAction(R.string.block_number_undo, undoListener)
+ .setActionTextColor(actionTextColor)
+ .show();
+
+ if (callback != null) {
+ callback.onFilterNumberSuccess();
+ }
+
+ if (FilteredNumbersUtil.hasRecentEmergencyCall(applicationContext)) {
+ FilteredNumbersUtil.maybeNotifyCallBlockingDisabled(applicationContext);
+ }
+ }
+ };
+
+ mHandler.blockNumber(onBlockNumberListener, mNumber, mCountryIso);
+ }
+
+ private void unblockNumber() {
+ final CharSequence message = getUnblockedMessage();
+ final CharSequence undoMessage = getBlockedMessage();
+ final Callback callback = mCallback;
+ final int actionTextColor = getActionTextColor();
+ final Context applicationContext = getContext().getApplicationContext();
+
+ final OnBlockNumberListener onUndoListener =
+ new OnBlockNumberListener() {
+ @Override
+ public void onBlockComplete(final Uri uri) {
+ Snackbar.make(mParentView, undoMessage, Snackbar.LENGTH_LONG).show();
+ if (callback != null) {
+ callback.onChangeFilteredNumberUndo();
+ }
+ }
+ };
+
+ mHandler.unblock(
+ new OnUnblockNumberListener() {
+ @Override
+ public void onUnblockComplete(int rows, final ContentValues values) {
+ final View.OnClickListener undoListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ // Re-insert the row on 'undo', with a new ID.
+ Logger.get(applicationContext)
+ .logInteraction(InteractionEvent.Type.UNDO_UNBLOCK_NUMBER);
+ mHandler.blockNumber(onUndoListener, values);
+ }
+ };
+
+ Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG)
+ .setAction(R.string.block_number_undo, undoListener)
+ .setActionTextColor(actionTextColor)
+ .show();
+
+ if (callback != null) {
+ callback.onUnfilterNumberSuccess();
+ }
+ }
+ },
+ getArguments().getInt(ARG_BLOCK_ID));
+ }
+
+ /**
+ * Use a callback interface to update UI after success/undo. Favor this approach over other more
+ * standard paradigms because of the variety of scenarios in which the DialogFragment can be
+ * invoked (by an Activity, by a fragment, by an adapter, by an adapter list item). Because of
+ * this, we do NOT support retaining state on rotation, and will dismiss the dialog upon rotation
+ * instead.
+ */
+ public interface Callback {
+
+ /** Called when a number is successfully added to the set of filtered numbers */
+ void onFilterNumberSuccess();
+
+ /** Called when a number is successfully removed from the set of filtered numbers */
+ void onUnfilterNumberSuccess();
+
+ /** Called when the action of filtering or unfiltering a number is undone */
+ void onChangeFilteredNumberUndo();
+ }
+}
diff --git a/java/com/android/dialer/blocking/BlockReportSpamDialogs.java b/java/com/android/dialer/blocking/BlockReportSpamDialogs.java
new file mode 100644
index 000000000..b5f81fcc5
--- /dev/null
+++ b/java/com/android/dialer/blocking/BlockReportSpamDialogs.java
@@ -0,0 +1,305 @@
+/*
+ * 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.dialer.blocking;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+
+/** Helper class for creating block/report dialog fragments. */
+public class BlockReportSpamDialogs {
+
+ public static final String BLOCK_REPORT_SPAM_DIALOG_TAG = "BlockReportSpamDialog";
+ public static final String BLOCK_DIALOG_TAG = "BlockDialog";
+ public static final String UNBLOCK_DIALOG_TAG = "UnblockDialog";
+ public static final String NOT_SPAM_DIALOG_TAG = "NotSpamDialog";
+
+ /** Creates a dialog with the default cancel button listener (dismisses dialog). */
+ private static AlertDialog.Builder createDialogBuilder(
+ Activity activity, final DialogFragment fragment) {
+ return new AlertDialog.Builder(activity, R.style.AlertDialogTheme)
+ .setCancelable(true)
+ .setNegativeButton(
+ android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ fragment.dismiss();
+ }
+ });
+ }
+
+ /**
+ * Creates a generic click listener which dismisses the fragment and then calls the actual
+ * listener.
+ */
+ private static DialogInterface.OnClickListener createGenericOnClickListener(
+ final DialogFragment fragment, final OnConfirmListener listener) {
+ return new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ fragment.dismiss();
+ listener.onClick();
+ }
+ };
+ }
+
+ private static String getBlockMessage(Context context) {
+ String message;
+ if (FilteredNumberCompat.useNewFiltering(context)) {
+ message = context.getString(R.string.block_number_confirmation_message_new_filtering);
+ } else {
+ message = context.getString(R.string.block_report_number_alert_details);
+ }
+ return message;
+ }
+
+ /**
+ * Listener passed to block/report spam dialog for positive click in {@link
+ * BlockReportSpamDialogFragment}.
+ */
+ public interface OnSpamDialogClickListener {
+
+ /**
+ * Called when user clicks on positive button in block/report spam dialog.
+ *
+ * @param isSpamChecked Whether the spam checkbox is checked.
+ */
+ void onClick(boolean isSpamChecked);
+ }
+
+ /** Listener passed to all dialogs except the block/report spam dialog for positive click. */
+ public interface OnConfirmListener {
+
+ /** Called when user clicks on positive button in the dialog. */
+ void onClick();
+ }
+
+ /** Contains the common attributes between all block/unblock/report dialog fragments. */
+ private static class CommonDialogsFragment extends DialogFragment {
+
+ /** The number to display in the dialog title. */
+ protected String mDisplayNumber;
+
+ /** Called when dialog positive button is pressed. */
+ protected OnConfirmListener mPositiveListener;
+
+ /** Called when dialog is dismissed. */
+ @Nullable protected DialogInterface.OnDismissListener mDismissListener;
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ if (mDismissListener != null) {
+ mDismissListener.onDismiss(dialog);
+ }
+ super.onDismiss(dialog);
+ }
+
+ @Override
+ public void onPause() {
+ // The dialog is dismissed onPause, i.e. rotation.
+ dismiss();
+ mDismissListener = null;
+ mPositiveListener = null;
+ mDisplayNumber = null;
+ super.onPause();
+ }
+ }
+
+ /** Dialog for block/report spam with the mark as spam checkbox. */
+ public static class BlockReportSpamDialogFragment extends CommonDialogsFragment {
+
+ /** Called when dialog positive button is pressed. */
+ private OnSpamDialogClickListener mPositiveListener;
+
+ /** Whether the mark as spam checkbox is checked before displaying the dialog. */
+ private boolean mSpamChecked;
+
+ public static DialogFragment newInstance(
+ String displayNumber,
+ boolean spamChecked,
+ OnSpamDialogClickListener positiveListener,
+ @Nullable DialogInterface.OnDismissListener dismissListener) {
+ BlockReportSpamDialogFragment fragment = new BlockReportSpamDialogFragment();
+ fragment.mSpamChecked = spamChecked;
+ fragment.mDisplayNumber = displayNumber;
+ fragment.mPositiveListener = positiveListener;
+ fragment.mDismissListener = dismissListener;
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+ View dialogView = View.inflate(getActivity(), R.layout.block_report_spam_dialog, null);
+ final CheckBox isSpamCheckbox =
+ (CheckBox) dialogView.findViewById(R.id.report_number_as_spam_action);
+ // Listen for changes on the checkbox and update if orientation changes
+ isSpamCheckbox.setChecked(mSpamChecked);
+ isSpamCheckbox.setOnCheckedChangeListener(
+ new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ mSpamChecked = isChecked;
+ }
+ });
+
+ TextView details = (TextView) dialogView.findViewById(R.id.block_details);
+ details.setText(getBlockMessage(getContext()));
+
+ AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
+ Dialog dialog =
+ alertDialogBuilder
+ .setView(dialogView)
+ .setTitle(getString(R.string.block_report_number_alert_title, mDisplayNumber))
+ .setPositiveButton(
+ R.string.block_number_ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ mPositiveListener.onClick(isSpamCheckbox.isChecked());
+ }
+ })
+ .create();
+ dialog.setCanceledOnTouchOutside(true);
+ return dialog;
+ }
+ }
+
+ /** Dialog for blocking a number. */
+ public static class BlockDialogFragment extends CommonDialogsFragment {
+
+ private boolean isSpamEnabled;
+
+ public static DialogFragment newInstance(
+ String displayNumber,
+ boolean isSpamEnabled,
+ OnConfirmListener positiveListener,
+ @Nullable DialogInterface.OnDismissListener dismissListener) {
+ BlockDialogFragment fragment = new BlockDialogFragment();
+ fragment.mDisplayNumber = displayNumber;
+ fragment.mPositiveListener = positiveListener;
+ fragment.mDismissListener = dismissListener;
+ fragment.isSpamEnabled = isSpamEnabled;
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+ // Return the newly created dialog
+ AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
+ Dialog dialog =
+ alertDialogBuilder
+ .setTitle(getString(R.string.block_number_confirmation_title, mDisplayNumber))
+ .setMessage(
+ isSpamEnabled
+ ? getString(
+ R.string.block_number_alert_details, getBlockMessage(getContext()))
+ : getString(R.string.block_report_number_alert_details))
+ .setPositiveButton(
+ R.string.block_number_ok, createGenericOnClickListener(this, mPositiveListener))
+ .create();
+ dialog.setCanceledOnTouchOutside(true);
+ return dialog;
+ }
+ }
+
+ /** Dialog for unblocking a number. */
+ public static class UnblockDialogFragment extends CommonDialogsFragment {
+
+ /** Whether or not the number is spam. */
+ private boolean mIsSpam;
+
+ public static DialogFragment newInstance(
+ String displayNumber,
+ boolean isSpam,
+ OnConfirmListener positiveListener,
+ @Nullable DialogInterface.OnDismissListener dismissListener) {
+ UnblockDialogFragment fragment = new UnblockDialogFragment();
+ fragment.mDisplayNumber = displayNumber;
+ fragment.mIsSpam = isSpam;
+ fragment.mPositiveListener = positiveListener;
+ fragment.mDismissListener = dismissListener;
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+ // Return the newly created dialog
+ AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
+ if (mIsSpam) {
+ alertDialogBuilder
+ .setMessage(R.string.unblock_number_alert_details)
+ .setTitle(getString(R.string.unblock_report_number_alert_title, mDisplayNumber));
+ } else {
+ alertDialogBuilder.setMessage(
+ getString(R.string.unblock_report_number_alert_title, mDisplayNumber));
+ }
+ Dialog dialog =
+ alertDialogBuilder
+ .setPositiveButton(
+ R.string.unblock_number_ok, createGenericOnClickListener(this, mPositiveListener))
+ .create();
+ dialog.setCanceledOnTouchOutside(true);
+ return dialog;
+ }
+ }
+
+ /** Dialog for reporting a number as not spam. */
+ public static class ReportNotSpamDialogFragment extends CommonDialogsFragment {
+
+ public static DialogFragment newInstance(
+ String displayNumber,
+ OnConfirmListener positiveListener,
+ @Nullable DialogInterface.OnDismissListener dismissListener) {
+ ReportNotSpamDialogFragment fragment = new ReportNotSpamDialogFragment();
+ fragment.mDisplayNumber = displayNumber;
+ fragment.mPositiveListener = positiveListener;
+ fragment.mDismissListener = dismissListener;
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+ // Return the newly created dialog
+ AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
+ Dialog dialog =
+ alertDialogBuilder
+ .setTitle(R.string.report_not_spam_alert_title)
+ .setMessage(getString(R.string.report_not_spam_alert_details, mDisplayNumber))
+ .setPositiveButton(
+ R.string.report_not_spam_alert_button,
+ createGenericOnClickListener(this, mPositiveListener))
+ .create();
+ dialog.setCanceledOnTouchOutside(true);
+ return dialog;
+ }
+ }
+}
diff --git a/java/com/android/dialer/blocking/BlockedNumbersAutoMigrator.java b/java/com/android/dialer/blocking/BlockedNumbersAutoMigrator.java
new file mode 100644
index 000000000..1773e9b84
--- /dev/null
+++ b/java/com/android/dialer/blocking/BlockedNumbersAutoMigrator.java
@@ -0,0 +1,110 @@
+/*
+ * 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.dialer.blocking;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.support.annotation.NonNull;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler.OnHasBlockedNumbersListener;
+import com.android.dialer.common.LogUtil;
+import java.util.Objects;
+
+/**
+ * Class responsible for checking if the user can be auto-migrated to {@link
+ * android.provider.BlockedNumberContract} blocking. In order for this to happen, the user cannot
+ * have any numbers that are blocked in the Dialer solution.
+ */
+public class BlockedNumbersAutoMigrator {
+
+ static final String HAS_CHECKED_AUTO_MIGRATE_KEY = "checkedAutoMigrate";
+
+ @NonNull private final Context context;
+ @NonNull private final SharedPreferences sharedPreferences;
+ @NonNull private final FilteredNumberAsyncQueryHandler queryHandler;
+
+ /**
+ * Constructs the BlockedNumbersAutoMigrator with the given {@link SharedPreferences} and {@link
+ * FilteredNumberAsyncQueryHandler}.
+ *
+ * @param sharedPreferences The SharedPreferences used to persist information.
+ * @param queryHandler The FilteredNumberAsyncQueryHandler used to determine if there are blocked
+ * numbers.
+ * @throws NullPointerException if sharedPreferences or queryHandler are null.
+ */
+ public BlockedNumbersAutoMigrator(
+ @NonNull Context context,
+ @NonNull SharedPreferences sharedPreferences,
+ @NonNull FilteredNumberAsyncQueryHandler queryHandler) {
+ this.context = Objects.requireNonNull(context);
+ this.sharedPreferences = Objects.requireNonNull(sharedPreferences);
+ this.queryHandler = Objects.requireNonNull(queryHandler);
+ }
+
+ /**
+ * Attempts to perform the auto-migration. Auto-migration will only be attempted once and can be
+ * performed only when the user has no blocked numbers. As a result of this method, the user will
+ * be migrated to the framework blocking solution, as determined by {@link
+ * FilteredNumberCompat#hasMigratedToNewBlocking()}.
+ */
+ public void autoMigrate() {
+ if (!shouldAttemptAutoMigrate()) {
+ return;
+ }
+
+ LogUtil.i("BlockedNumbersAutoMigrator", "attempting to auto-migrate.");
+ queryHandler.hasBlockedNumbers(
+ new OnHasBlockedNumbersListener() {
+ @Override
+ public void onHasBlockedNumbers(boolean hasBlockedNumbers) {
+ if (hasBlockedNumbers) {
+ LogUtil.i("BlockedNumbersAutoMigrator", "not auto-migrating: blocked numbers exist.");
+ return;
+ }
+ LogUtil.i("BlockedNumbersAutoMigrator", "auto-migrating: no blocked numbers.");
+ FilteredNumberCompat.setHasMigratedToNewBlocking(context, true);
+ }
+ });
+ }
+
+ private boolean shouldAttemptAutoMigrate() {
+ if (sharedPreferences.contains(HAS_CHECKED_AUTO_MIGRATE_KEY)) {
+ LogUtil.v("BlockedNumbersAutoMigrator", "not attempting auto-migrate: already checked once.");
+ return false;
+ }
+
+ if (!FilteredNumberCompat.canAttemptBlockOperations(context)) {
+ // This may be the case where the user is on the lock screen, so we shouldn't record that the
+ // migration status was checked.
+ LogUtil.i(
+ "BlockedNumbersAutoMigrator", "not attempting auto-migrate: current user can't block");
+ return false;
+ }
+ LogUtil.i("BlockedNumbersAutoMigrator", "updating state as already checked for auto-migrate.");
+ sharedPreferences.edit().putBoolean(HAS_CHECKED_AUTO_MIGRATE_KEY, true).apply();
+
+ if (!FilteredNumberCompat.canUseNewFiltering()) {
+ LogUtil.i("BlockedNumbersAutoMigrator", "not attempting auto-migrate: not available.");
+ return false;
+ }
+
+ if (FilteredNumberCompat.hasMigratedToNewBlocking(context)) {
+ LogUtil.i("BlockedNumbersAutoMigrator", "not attempting auto-migrate: already migrated.");
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/java/com/android/dialer/blocking/BlockedNumbersMigrator.java b/java/com/android/dialer/blocking/BlockedNumbersMigrator.java
new file mode 100644
index 000000000..88f474a84
--- /dev/null
+++ b/java/com/android/dialer/blocking/BlockedNumbersMigrator.java
@@ -0,0 +1,159 @@
+/*
+ * 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.dialer.blocking;
+
+import android.annotation.TargetApi;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.AsyncTask;
+import android.os.Build.VERSION_CODES;
+import android.provider.BlockedNumberContract.BlockedNumbers;
+import android.support.annotation.RequiresApi;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.database.FilteredNumberContract;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
+import java.util.Objects;
+
+/**
+ * Class which should be used to migrate numbers from {@link FilteredNumberContract} blocking to
+ * {@link android.provider.BlockedNumberContract} blocking.
+ */
+@TargetApi(VERSION_CODES.M)
+public class BlockedNumbersMigrator {
+
+ private final Context context;
+
+ /**
+ * Creates a new BlockedNumbersMigrate, using the given {@link ContentResolver} to perform queries
+ * against the blocked numbers tables.
+ */
+ public BlockedNumbersMigrator(Context context) {
+ this.context = Objects.requireNonNull(context);
+ }
+
+ @RequiresApi(VERSION_CODES.N)
+ @TargetApi(VERSION_CODES.N)
+ private static boolean migrateToNewBlockingInBackground(ContentResolver resolver) {
+ try (Cursor cursor =
+ resolver.query(
+ FilteredNumber.CONTENT_URI,
+ new String[] {FilteredNumberColumns.NUMBER},
+ null,
+ null,
+ null)) {
+ if (cursor == null) {
+ LogUtil.i(
+ "BlockedNumbersMigrator.migrateToNewBlockingInBackground", "migrate - cursor was null");
+ return false;
+ }
+
+ LogUtil.i(
+ "BlockedNumbersMigrator.migrateToNewBlockingInBackground",
+ "migrate - attempting to migrate " + cursor.getCount() + "numbers");
+
+ int numMigrated = 0;
+ while (cursor.moveToNext()) {
+ String originalNumber =
+ cursor.getString(cursor.getColumnIndex(FilteredNumberColumns.NUMBER));
+ if (isNumberInNewBlocking(resolver, originalNumber)) {
+ LogUtil.i(
+ "BlockedNumbersMigrator.migrateToNewBlockingInBackground",
+ "migrate - number was already blocked in new blocking");
+ continue;
+ }
+ ContentValues values = new ContentValues();
+ values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, originalNumber);
+ resolver.insert(BlockedNumbers.CONTENT_URI, values);
+ ++numMigrated;
+ }
+ LogUtil.i(
+ "BlockedNumbersMigrator.migrateToNewBlockingInBackground",
+ "migrate - migration complete. " + numMigrated + " numbers migrated.");
+ return true;
+ }
+ }
+
+ @RequiresApi(VERSION_CODES.N)
+ @TargetApi(VERSION_CODES.N)
+ private static boolean isNumberInNewBlocking(ContentResolver resolver, String originalNumber) {
+ try (Cursor cursor =
+ resolver.query(
+ BlockedNumbers.CONTENT_URI,
+ new String[] {BlockedNumbers.COLUMN_ID},
+ BlockedNumbers.COLUMN_ORIGINAL_NUMBER + " = ?",
+ new String[] {originalNumber},
+ null)) {
+ return cursor != null && cursor.getCount() != 0;
+ }
+ }
+
+ /**
+ * Copies all of the numbers in the {@link FilteredNumberContract} block list to the {@link
+ * android.provider.BlockedNumberContract} block list.
+ *
+ * @param listener {@link Listener} called once the migration is complete.
+ * @return {@code true} if the migrate can be attempted, {@code false} otherwise.
+ * @throws NullPointerException if listener is null
+ */
+ public boolean migrate(final Listener listener) {
+ LogUtil.i("BlockedNumbersMigrator.migrate", "migrate - start");
+ if (!FilteredNumberCompat.canUseNewFiltering()) {
+ LogUtil.i("BlockedNumbersMigrator.migrate", "migrate - can't use new filtering");
+ return false;
+ }
+ Objects.requireNonNull(listener);
+ new MigratorTask(listener).execute();
+ return true;
+ }
+
+ /**
+ * Listener for the operation to migrate from {@link FilteredNumberContract} blocking to {@link
+ * android.provider.BlockedNumberContract} blocking.
+ */
+ public interface Listener {
+
+ /** Called when the migration operation is finished. */
+ void onComplete();
+ }
+
+ @TargetApi(VERSION_CODES.N)
+ private class MigratorTask extends AsyncTask<Void, Void, Boolean> {
+
+ private final Listener listener;
+
+ public MigratorTask(Listener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ LogUtil.i("BlockedNumbersMigrator.doInBackground", "migrate - start background migration");
+ return migrateToNewBlockingInBackground(context.getContentResolver());
+ }
+
+ @Override
+ protected void onPostExecute(Boolean isSuccessful) {
+ LogUtil.i("BlockedNumbersMigrator.onPostExecute", "migrate - marking migration complete");
+ FilteredNumberCompat.setHasMigratedToNewBlocking(context, isSuccessful);
+ LogUtil.i("BlockedNumbersMigrator.onPostExecute", "migrate - calling listener");
+ listener.onComplete();
+ }
+ }
+}
diff --git a/java/com/android/dialer/blocking/FilteredNumberAsyncQueryHandler.java b/java/com/android/dialer/blocking/FilteredNumberAsyncQueryHandler.java
new file mode 100644
index 000000000..852e7a0ed
--- /dev/null
+++ b/java/com/android/dialer/blocking/FilteredNumberAsyncQueryHandler.java
@@ -0,0 +1,428 @@
+/*
+ * 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.dialer.blocking;
+
+import android.annotation.TargetApi;
+import android.content.AsyncQueryHandler;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabaseCorruptException;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.os.UserManagerCompat;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumberTypes;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class FilteredNumberAsyncQueryHandler extends AsyncQueryHandler {
+
+ public static final int INVALID_ID = -1;
+ // Id used to replace null for blocked id since ConcurrentHashMap doesn't allow null key/value.
+ @VisibleForTesting static final int BLOCKED_NUMBER_CACHE_NULL_ID = -1;
+
+ @VisibleForTesting
+ static final Map<String, Integer> blockedNumberCache = new ConcurrentHashMap<>();
+
+ private static final int NO_TOKEN = 0;
+ private final Context context;
+
+ public FilteredNumberAsyncQueryHandler(Context context) {
+ super(context.getContentResolver());
+ this.context = context;
+ }
+
+ @Override
+ protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ if (cookie != null) {
+ ((Listener) cookie).onQueryComplete(token, cookie, cursor);
+ }
+ }
+
+ @Override
+ protected void onInsertComplete(int token, Object cookie, Uri uri) {
+ if (cookie != null) {
+ ((Listener) cookie).onInsertComplete(token, cookie, uri);
+ }
+ }
+
+ @Override
+ protected void onUpdateComplete(int token, Object cookie, int result) {
+ if (cookie != null) {
+ ((Listener) cookie).onUpdateComplete(token, cookie, result);
+ }
+ }
+
+ @Override
+ protected void onDeleteComplete(int token, Object cookie, int result) {
+ if (cookie != null) {
+ ((Listener) cookie).onDeleteComplete(token, cookie, result);
+ }
+ }
+
+ public void hasBlockedNumbers(final OnHasBlockedNumbersListener listener) {
+ if (!FilteredNumberCompat.canAttemptBlockOperations(context)) {
+ listener.onHasBlockedNumbers(false);
+ return;
+ }
+ startQuery(
+ NO_TOKEN,
+ new Listener() {
+ @Override
+ protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ listener.onHasBlockedNumbers(cursor != null && cursor.getCount() > 0);
+ }
+ },
+ FilteredNumberCompat.getContentUri(context, null),
+ new String[] {FilteredNumberCompat.getIdColumnName(context)},
+ FilteredNumberCompat.useNewFiltering(context)
+ ? null
+ : FilteredNumberColumns.TYPE + "=" + FilteredNumberTypes.BLOCKED_NUMBER,
+ null,
+ null);
+ }
+
+ /**
+ * Checks if the given number is blocked, calling the given {@link OnCheckBlockedListener} with
+ * the id for the blocked number, {@link #INVALID_ID}, or {@code null} based on the result of the
+ * check.
+ */
+ public void isBlockedNumber(
+ final OnCheckBlockedListener listener, @Nullable final String number, String countryIso) {
+ if (number == null) {
+ listener.onCheckComplete(INVALID_ID);
+ return;
+ }
+ if (!FilteredNumberCompat.canAttemptBlockOperations(context)) {
+ listener.onCheckComplete(null);
+ return;
+ }
+ Integer cachedId = blockedNumberCache.get(number);
+ if (cachedId != null) {
+ if (listener == null) {
+ return;
+ }
+ if (cachedId == BLOCKED_NUMBER_CACHE_NULL_ID) {
+ cachedId = null;
+ }
+ listener.onCheckComplete(cachedId);
+ return;
+ }
+
+ String e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso);
+ String formattedNumber = FilteredNumbersUtil.getBlockableNumber(context, e164Number, number);
+ if (TextUtils.isEmpty(formattedNumber)) {
+ listener.onCheckComplete(INVALID_ID);
+ blockedNumberCache.put(number, INVALID_ID);
+ return;
+ }
+
+ if (!UserManagerCompat.isUserUnlocked(context)) {
+ LogUtil.i(
+ "FilteredNumberAsyncQueryHandler.isBlockedNumber",
+ "Device locked in FBE mode, cannot access blocked number database");
+ listener.onCheckComplete(INVALID_ID);
+ return;
+ }
+
+ startQuery(
+ NO_TOKEN,
+ new Listener() {
+ @Override
+ protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ /*
+ * In the frameworking blocking, numbers can be blocked in both e164 format
+ * and not, resulting in multiple rows being returned for this query. For
+ * example, both '16502530000' and '6502530000' can exist at the same time
+ * and will be returned by this query.
+ */
+ if (cursor == null || cursor.getCount() == 0) {
+ blockedNumberCache.put(number, BLOCKED_NUMBER_CACHE_NULL_ID);
+ listener.onCheckComplete(null);
+ return;
+ }
+ cursor.moveToFirst();
+ // New filtering doesn't have a concept of type
+ if (!FilteredNumberCompat.useNewFiltering(context)
+ && cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns.TYPE))
+ != FilteredNumberTypes.BLOCKED_NUMBER) {
+ blockedNumberCache.put(number, BLOCKED_NUMBER_CACHE_NULL_ID);
+ listener.onCheckComplete(null);
+ return;
+ }
+ Integer blockedId = cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns._ID));
+ blockedNumberCache.put(number, blockedId);
+ listener.onCheckComplete(blockedId);
+ }
+ },
+ FilteredNumberCompat.getContentUri(context, null),
+ FilteredNumberCompat.filter(
+ new String[] {
+ FilteredNumberCompat.getIdColumnName(context),
+ FilteredNumberCompat.getTypeColumnName(context)
+ }),
+ getIsBlockedNumberSelection(e164Number != null) + " = ?",
+ new String[] {formattedNumber},
+ null);
+ }
+
+ /**
+ * Synchronously check if this number has been blocked.
+ *
+ * @return blocked id.
+ */
+ @TargetApi(VERSION_CODES.M)
+ @Nullable
+ public Integer getBlockedIdSynchronousForCalllogOnly(@Nullable String number, String countryIso) {
+ Assert.isWorkerThread();
+ if (number == null) {
+ return null;
+ }
+ if (!FilteredNumberCompat.canAttemptBlockOperations(context)) {
+ return null;
+ }
+ Integer cachedId = blockedNumberCache.get(number);
+ if (cachedId != null) {
+ if (cachedId == BLOCKED_NUMBER_CACHE_NULL_ID) {
+ cachedId = null;
+ }
+ return cachedId;
+ }
+
+ String e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso);
+ String formattedNumber = FilteredNumbersUtil.getBlockableNumber(context, e164Number, number);
+ if (TextUtils.isEmpty(formattedNumber)) {
+ return null;
+ }
+
+ try (Cursor cursor =
+ context
+ .getContentResolver()
+ .query(
+ FilteredNumberCompat.getContentUri(context, null),
+ FilteredNumberCompat.filter(
+ new String[] {
+ FilteredNumberCompat.getIdColumnName(context),
+ FilteredNumberCompat.getTypeColumnName(context)
+ }),
+ getIsBlockedNumberSelection(e164Number != null) + " = ?",
+ new String[] {formattedNumber},
+ null)) {
+ /*
+ * In the frameworking blocking, numbers can be blocked in both e164 format
+ * and not, resulting in multiple rows being returned for this query. For
+ * example, both '16502530000' and '6502530000' can exist at the same time
+ * and will be returned by this query.
+ */
+ if (cursor == null || cursor.getCount() == 0) {
+ blockedNumberCache.put(number, BLOCKED_NUMBER_CACHE_NULL_ID);
+ return null;
+ }
+ cursor.moveToFirst();
+ int blockedId = cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns._ID));
+ blockedNumberCache.put(number, blockedId);
+ return blockedId;
+ } catch (SecurityException e) {
+ LogUtil.e("FilteredNumberAsyncQueryHandler.getBlockedIdSynchronousForCalllogOnly", null, e);
+ return null;
+ }
+ }
+
+ @VisibleForTesting
+ public void clearCache() {
+ blockedNumberCache.clear();
+ }
+
+ /*
+ * TODO: b/27779827, non-e164 numbers can be blocked in the new form of blocking. As a
+ * temporary workaround, determine which column of the database to query based on whether the
+ * number is e164 or not.
+ */
+ private String getIsBlockedNumberSelection(boolean isE164Number) {
+ if (FilteredNumberCompat.useNewFiltering(context) && !isE164Number) {
+ return FilteredNumberCompat.getOriginalNumberColumnName(context);
+ }
+ return FilteredNumberCompat.getE164NumberColumnName(context);
+ }
+
+ public void blockNumber(
+ final OnBlockNumberListener listener, String number, @Nullable String countryIso) {
+ blockNumber(listener, null, number, countryIso);
+ }
+
+ /** Add a number manually blocked by the user. */
+ public void blockNumber(
+ final OnBlockNumberListener listener,
+ @Nullable String normalizedNumber,
+ String number,
+ @Nullable String countryIso) {
+ blockNumber(
+ listener,
+ FilteredNumberCompat.newBlockNumberContentValues(
+ context, number, normalizedNumber, countryIso));
+ }
+
+ /**
+ * Block a number with specified ContentValues. Can be manually added or a restored row from
+ * performing the 'undo' action after unblocking.
+ */
+ public void blockNumber(final OnBlockNumberListener listener, ContentValues values) {
+ blockedNumberCache.clear();
+ if (!FilteredNumberCompat.canAttemptBlockOperations(context)) {
+ listener.onBlockComplete(null);
+ return;
+ }
+ startInsert(
+ NO_TOKEN,
+ new Listener() {
+ @Override
+ public void onInsertComplete(int token, Object cookie, Uri uri) {
+ if (listener != null) {
+ listener.onBlockComplete(uri);
+ }
+ }
+ },
+ FilteredNumberCompat.getContentUri(context, null),
+ values);
+ }
+
+ /**
+ * Unblocks the number with the given id.
+ *
+ * @param listener (optional) The {@link OnUnblockNumberListener} called after the number is
+ * unblocked.
+ * @param id The id of the number to unblock.
+ */
+ public void unblock(@Nullable final OnUnblockNumberListener listener, Integer id) {
+ if (id == null) {
+ throw new IllegalArgumentException("Null id passed into unblock");
+ }
+ unblock(listener, FilteredNumberCompat.getContentUri(context, id));
+ }
+
+ /**
+ * Removes row from database.
+ *
+ * @param listener (optional) The {@link OnUnblockNumberListener} called after the number is
+ * unblocked.
+ * @param uri The uri of row to remove, from {@link FilteredNumberAsyncQueryHandler#blockNumber}.
+ */
+ public void unblock(@Nullable final OnUnblockNumberListener listener, final Uri uri) {
+ blockedNumberCache.clear();
+ if (!FilteredNumberCompat.canAttemptBlockOperations(context)) {
+ if (listener != null) {
+ listener.onUnblockComplete(0, null);
+ }
+ return;
+ }
+ startQuery(
+ NO_TOKEN,
+ new Listener() {
+ @Override
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ int rowsReturned = cursor == null ? 0 : cursor.getCount();
+ if (rowsReturned != 1) {
+ throw new SQLiteDatabaseCorruptException(
+ "Returned " + rowsReturned + " rows for uri " + uri + "where 1 expected.");
+ }
+ cursor.moveToFirst();
+ final ContentValues values = new ContentValues();
+ DatabaseUtils.cursorRowToContentValues(cursor, values);
+ values.remove(FilteredNumberCompat.getIdColumnName(context));
+
+ startDelete(
+ NO_TOKEN,
+ new Listener() {
+ @Override
+ public void onDeleteComplete(int token, Object cookie, int result) {
+ if (listener != null) {
+ listener.onUnblockComplete(result, values);
+ }
+ }
+ },
+ uri,
+ null,
+ null);
+ }
+ },
+ uri,
+ null,
+ null,
+ null,
+ null);
+ }
+
+ public interface OnCheckBlockedListener {
+
+ /**
+ * Invoked after querying if a number is blocked.
+ *
+ * @param id The ID of the row if blocked, null otherwise.
+ */
+ void onCheckComplete(Integer id);
+ }
+
+ public interface OnBlockNumberListener {
+
+ /**
+ * Invoked after inserting a blocked number.
+ *
+ * @param uri The uri of the newly created row.
+ */
+ void onBlockComplete(Uri uri);
+ }
+
+ public interface OnUnblockNumberListener {
+
+ /**
+ * Invoked after removing a blocked number
+ *
+ * @param rows The number of rows affected (expected value 1).
+ * @param values The deleted data (used for restoration).
+ */
+ void onUnblockComplete(int rows, ContentValues values);
+ }
+
+ public interface OnHasBlockedNumbersListener {
+
+ /**
+ * @param hasBlockedNumbers {@code true} if any blocked numbers are stored. {@code false}
+ * otherwise.
+ */
+ void onHasBlockedNumbers(boolean hasBlockedNumbers);
+ }
+
+ /** Methods for FilteredNumberAsyncQueryHandler result returns. */
+ private abstract static class Listener {
+
+ protected void onQueryComplete(int token, Object cookie, Cursor cursor) {}
+
+ protected void onInsertComplete(int token, Object cookie, Uri uri) {}
+
+ protected void onUpdateComplete(int token, Object cookie, int result) {}
+
+ protected void onDeleteComplete(int token, Object cookie, int result) {}
+ }
+}
diff --git a/java/com/android/dialer/blocking/FilteredNumberCompat.java b/java/com/android/dialer/blocking/FilteredNumberCompat.java
new file mode 100644
index 000000000..0ee85d897
--- /dev/null
+++ b/java/com/android/dialer/blocking/FilteredNumberCompat.java
@@ -0,0 +1,320 @@
+/*
+ * 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.dialer.blocking;
+
+import android.annotation.TargetApi;
+import android.app.FragmentManager;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.os.UserManager;
+import android.preference.PreferenceManager;
+import android.provider.BlockedNumberContract;
+import android.provider.BlockedNumberContract.BlockedNumbers;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.telecom.TelecomManager;
+import android.telephony.PhoneNumberUtils;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumberSources;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumberTypes;
+import com.android.dialer.telecom.TelecomUtil;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Compatibility class to encapsulate logic to switch between call blocking using {@link
+ * com.android.dialer.database.FilteredNumberContract} and using {@link
+ * android.provider.BlockedNumberContract}. This class should be used rather than explicitly
+ * referencing columns from either contract class in situations where both blocking solutions may be
+ * used.
+ */
+public class FilteredNumberCompat {
+
+ private static Boolean canAttemptBlockOperationsForTest;
+
+ @VisibleForTesting
+ public static final String HAS_MIGRATED_TO_NEW_BLOCKING_KEY = "migratedToNewBlocking";
+
+ /** @return The column name for ID in the filtered number database. */
+ public static String getIdColumnName(Context context) {
+ return useNewFiltering(context) ? BlockedNumbers.COLUMN_ID : FilteredNumberColumns._ID;
+ }
+
+ /**
+ * @return The column name for type in the filtered number database. Will be {@code null} for the
+ * framework blocking implementation.
+ */
+ @Nullable
+ public static String getTypeColumnName(Context context) {
+ return useNewFiltering(context) ? null : FilteredNumberColumns.TYPE;
+ }
+
+ /**
+ * @return The column name for source in the filtered number database. Will be {@code null} for
+ * the framework blocking implementation
+ */
+ @Nullable
+ public static String getSourceColumnName(Context context) {
+ return useNewFiltering(context) ? null : FilteredNumberColumns.SOURCE;
+ }
+
+ /** @return The column name for the original number in the filtered number database. */
+ public static String getOriginalNumberColumnName(Context context) {
+ return useNewFiltering(context)
+ ? BlockedNumbers.COLUMN_ORIGINAL_NUMBER
+ : FilteredNumberColumns.NUMBER;
+ }
+
+ /**
+ * @return The column name for country iso in the filtered number database. Will be {@code null}
+ * the framework blocking implementation
+ */
+ @Nullable
+ public static String getCountryIsoColumnName(Context context) {
+ return useNewFiltering(context) ? null : FilteredNumberColumns.COUNTRY_ISO;
+ }
+
+ /** @return The column name for the e164 formatted number in the filtered number database. */
+ public static String getE164NumberColumnName(Context context) {
+ return useNewFiltering(context)
+ ? BlockedNumbers.COLUMN_E164_NUMBER
+ : FilteredNumberColumns.NORMALIZED_NUMBER;
+ }
+
+ /**
+ * @return {@code true} if the current SDK version supports using new filtering, {@code false}
+ * otherwise.
+ */
+ public static boolean canUseNewFiltering() {
+ return VERSION.SDK_INT >= VERSION_CODES.N;
+ }
+
+ /**
+ * @return {@code true} if the new filtering should be used, i.e. it's enabled and any necessary
+ * migration has been performed, {@code false} otherwise.
+ */
+ public static boolean useNewFiltering(Context context) {
+ return canUseNewFiltering() && hasMigratedToNewBlocking(context);
+ }
+
+ /**
+ * @return {@code true} if the user has migrated to use {@link
+ * android.provider.BlockedNumberContract} blocking, {@code false} otherwise.
+ */
+ public static boolean hasMigratedToNewBlocking(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getBoolean(HAS_MIGRATED_TO_NEW_BLOCKING_KEY, false);
+ }
+
+ /**
+ * Called to inform this class whether the user has fully migrated to use {@link
+ * android.provider.BlockedNumberContract} blocking or not.
+ *
+ * @param hasMigrated {@code true} if the user has migrated, {@code false} otherwise.
+ */
+ public static void setHasMigratedToNewBlocking(Context context, boolean hasMigrated) {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putBoolean(HAS_MIGRATED_TO_NEW_BLOCKING_KEY, hasMigrated)
+ .apply();
+ }
+
+ /**
+ * Gets the content {@link Uri} for number filtering.
+ *
+ * @param id The optional id to append with the base content uri.
+ * @return The Uri for number filtering.
+ */
+ public static Uri getContentUri(Context context, @Nullable Integer id) {
+ if (id == null) {
+ return getBaseUri(context);
+ }
+ return ContentUris.withAppendedId(getBaseUri(context), id);
+ }
+
+ private static Uri getBaseUri(Context context) {
+ // Explicit version check to aid static analysis
+ return useNewFiltering(context) && VERSION.SDK_INT >= VERSION_CODES.N
+ ? BlockedNumbers.CONTENT_URI
+ : FilteredNumber.CONTENT_URI;
+ }
+
+ /**
+ * Removes any null column names from the given projection array. This method is intended to be
+ * used to strip out any column names that aren't available in every version of number blocking.
+ * Example: {@literal getContext().getContentResolver().query( someUri, // Filtering ensures that
+ * no non-existant columns are queried FilteredNumberCompat.filter(new String[]
+ * {FilteredNumberCompat.getIdColumnName(), FilteredNumberCompat.getTypeColumnName()},
+ * FilteredNumberCompat.getE164NumberColumnName() + " = ?", new String[] {e164Number}); }
+ *
+ * @param projection The projection array.
+ * @return The filtered projection array.
+ */
+ @Nullable
+ public static String[] filter(@Nullable String[] projection) {
+ if (projection == null) {
+ return null;
+ }
+ List<String> filtered = new ArrayList<>();
+ for (String column : projection) {
+ if (column != null) {
+ filtered.add(column);
+ }
+ }
+ return filtered.toArray(new String[filtered.size()]);
+ }
+
+ /**
+ * Creates a new {@link ContentValues} suitable for inserting in the filtered number table.
+ *
+ * @param number The unformatted number to insert.
+ * @param e164Number (optional) The number to insert formatted to E164 standard.
+ * @param countryIso (optional) The country iso to use to format the number.
+ * @return The ContentValues to insert.
+ * @throws NullPointerException If number is null.
+ */
+ public static ContentValues newBlockNumberContentValues(
+ Context context, String number, @Nullable String e164Number, @Nullable String countryIso) {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(getOriginalNumberColumnName(context), Objects.requireNonNull(number));
+ if (!useNewFiltering(context)) {
+ if (e164Number == null) {
+ e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso);
+ }
+ contentValues.put(getE164NumberColumnName(context), e164Number);
+ contentValues.put(getCountryIsoColumnName(context), countryIso);
+ contentValues.put(getTypeColumnName(context), FilteredNumberTypes.BLOCKED_NUMBER);
+ contentValues.put(getSourceColumnName(context), FilteredNumberSources.USER);
+ }
+ return contentValues;
+ }
+
+ /**
+ * Shows block number migration dialog if necessary.
+ *
+ * @param fragmentManager The {@link FragmentManager} used to show fragments.
+ * @param listener The {@link BlockedNumbersMigrator.Listener} to call when migration is complete.
+ * @return boolean True if migration dialog is shown.
+ */
+ public static boolean maybeShowBlockNumberMigrationDialog(
+ Context context, FragmentManager fragmentManager, BlockedNumbersMigrator.Listener listener) {
+ if (shouldShowMigrationDialog(context)) {
+ LogUtil.i(
+ "FilteredNumberCompat.maybeShowBlockNumberMigrationDialog",
+ "maybeShowBlockNumberMigrationDialog - showing migration dialog");
+ MigrateBlockedNumbersDialogFragment.newInstance(new BlockedNumbersMigrator(context), listener)
+ .show(fragmentManager, "MigrateBlockedNumbers");
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean shouldShowMigrationDialog(Context context) {
+ return canUseNewFiltering() && !hasMigratedToNewBlocking(context);
+ }
+
+ /**
+ * Creates the {@link Intent} which opens the blocked numbers management interface.
+ *
+ * @param context The {@link Context}.
+ * @return The intent.
+ */
+ public static Intent createManageBlockedNumbersIntent(Context context) {
+ // Explicit version check to aid static analysis
+ if (canUseNewFiltering()
+ && hasMigratedToNewBlocking(context)
+ && VERSION.SDK_INT >= VERSION_CODES.N) {
+ return context.getSystemService(TelecomManager.class).createManageBlockedNumbersIntent();
+ }
+ Intent intent = new Intent("com.android.dialer.action.BLOCKED_NUMBERS_SETTINGS");
+ intent.setPackage(context.getPackageName());
+ return intent;
+ }
+
+ /**
+ * Method used to determine if block operations are possible.
+ *
+ * @param context The {@link Context}.
+ * @return {@code true} if the app and user can block numbers, {@code false} otherwise.
+ */
+ public static boolean canAttemptBlockOperations(Context context) {
+ if (canAttemptBlockOperationsForTest != null) {
+ return canAttemptBlockOperationsForTest;
+ }
+
+ if (VERSION.SDK_INT < VERSION_CODES.N) {
+ // Dialer blocking, must be primary user
+ return context.getSystemService(UserManager.class).isSystemUser();
+ }
+
+ // Great Wall blocking, must be primary user and the default or system dialer
+ // TODO: check that we're the system Dialer
+ return TelecomUtil.isDefaultDialer(context)
+ && safeBlockedNumbersContractCanCurrentUserBlockNumbers(context);
+ }
+
+ static void setCanAttemptBlockOperationsForTest(boolean canAttempt) {
+ canAttemptBlockOperationsForTest = canAttempt;
+ }
+
+ /**
+ * Used to determine if the call blocking settings can be opened.
+ *
+ * @param context The {@link Context}.
+ * @return {@code true} if the current user can open the call blocking settings, {@code false}
+ * otherwise.
+ */
+ public static boolean canCurrentUserOpenBlockSettings(Context context) {
+ if (VERSION.SDK_INT < VERSION_CODES.N) {
+ // Dialer blocking, must be primary user
+ return context.getSystemService(UserManager.class).isSystemUser();
+ }
+ // BlockedNumberContract blocking, verify through Contract API
+ return TelecomUtil.isDefaultDialer(context)
+ && safeBlockedNumbersContractCanCurrentUserBlockNumbers(context);
+ }
+
+ /**
+ * Calls {@link BlockedNumberContract#canCurrentUserBlockNumbers(Context)} in such a way that it
+ * never throws an exception. While on the CryptKeeper screen, the BlockedNumberContract isn't
+ * available, using this method ensures that the Dialer doesn't crash when on that screen.
+ *
+ * @param context The {@link Context}.
+ * @return the result of BlockedNumberContract#canCurrentUserBlockNumbers, or {@code false} if an
+ * exception was thrown.
+ */
+ @TargetApi(VERSION_CODES.N)
+ private static boolean safeBlockedNumbersContractCanCurrentUserBlockNumbers(Context context) {
+ try {
+ return BlockedNumberContract.canCurrentUserBlockNumbers(context);
+ } catch (Exception e) {
+ LogUtil.e(
+ "FilteredNumberCompat.safeBlockedNumbersContractCanCurrentUserBlockNumbers",
+ "Exception while querying BlockedNumberContract",
+ e);
+ return false;
+ }
+ }
+}
diff --git a/java/com/android/dialer/blocking/FilteredNumberProvider.java b/java/com/android/dialer/blocking/FilteredNumberProvider.java
new file mode 100644
index 000000000..5d369038c
--- /dev/null
+++ b/java/com/android/dialer/blocking/FilteredNumberProvider.java
@@ -0,0 +1,176 @@
+/*
+ * 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.dialer.blocking;
+
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.contacts.common.GeoUtil;
+import com.android.dialer.database.Database;
+import com.android.dialer.database.DialerDatabaseHelper;
+import com.android.dialer.database.FilteredNumberContract;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
+
+/** Filtered number content provider. */
+public class FilteredNumberProvider extends ContentProvider {
+
+ private static final int FILTERED_NUMBERS_TABLE = 1;
+ private static final int FILTERED_NUMBERS_TABLE_ID = 2;
+ private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+ private static final String TAG = FilteredNumberProvider.class.getSimpleName();
+ private DialerDatabaseHelper mDialerDatabaseHelper;
+
+ @Override
+ public boolean onCreate() {
+ mDialerDatabaseHelper = Database.get(getContext()).getDatabaseHelper(getContext());
+ if (mDialerDatabaseHelper == null) {
+ return false;
+ }
+ sUriMatcher.addURI(
+ FilteredNumberContract.AUTHORITY,
+ FilteredNumberContract.FilteredNumber.FILTERED_NUMBERS_TABLE,
+ FILTERED_NUMBERS_TABLE);
+ sUriMatcher.addURI(
+ FilteredNumberContract.AUTHORITY,
+ FilteredNumberContract.FilteredNumber.FILTERED_NUMBERS_TABLE + "/#",
+ FILTERED_NUMBERS_TABLE_ID);
+ return true;
+ }
+
+ @Override
+ public Cursor query(
+ Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ final SQLiteDatabase db = mDialerDatabaseHelper.getReadableDatabase();
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(DialerDatabaseHelper.Tables.FILTERED_NUMBER_TABLE);
+ final int match = sUriMatcher.match(uri);
+ switch (match) {
+ case FILTERED_NUMBERS_TABLE:
+ break;
+ case FILTERED_NUMBERS_TABLE_ID:
+ qb.appendWhere(FilteredNumberColumns._ID + "=" + ContentUris.parseId(uri));
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown uri: " + uri);
+ }
+ final Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, null);
+ if (c != null) {
+ c.setNotificationUri(
+ getContext().getContentResolver(), FilteredNumberContract.FilteredNumber.CONTENT_URI);
+ } else {
+ Log.d(TAG, "CURSOR WAS NULL");
+ }
+ return c;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return FilteredNumberContract.FilteredNumber.CONTENT_ITEM_TYPE;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ SQLiteDatabase db = mDialerDatabaseHelper.getWritableDatabase();
+ setDefaultValues(values);
+ long id = db.insert(DialerDatabaseHelper.Tables.FILTERED_NUMBER_TABLE, null, values);
+ if (id < 0) {
+ return null;
+ }
+ notifyChange(uri);
+ return ContentUris.withAppendedId(uri, id);
+ }
+
+ @VisibleForTesting
+ protected long getCurrentTimeMs() {
+ return System.currentTimeMillis();
+ }
+
+ private void setDefaultValues(ContentValues values) {
+ if (values.getAsString(FilteredNumberColumns.COUNTRY_ISO) == null) {
+ values.put(FilteredNumberColumns.COUNTRY_ISO, GeoUtil.getCurrentCountryIso(getContext()));
+ }
+ if (values.getAsInteger(FilteredNumberColumns.TIMES_FILTERED) == null) {
+ values.put(FilteredNumberContract.FilteredNumberColumns.TIMES_FILTERED, 0);
+ }
+ if (values.getAsLong(FilteredNumberColumns.CREATION_TIME) == null) {
+ values.put(FilteredNumberColumns.CREATION_TIME, getCurrentTimeMs());
+ }
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ SQLiteDatabase db = mDialerDatabaseHelper.getWritableDatabase();
+ final int match = sUriMatcher.match(uri);
+ switch (match) {
+ case FILTERED_NUMBERS_TABLE:
+ break;
+ case FILTERED_NUMBERS_TABLE_ID:
+ selection = getSelectionWithId(selection, ContentUris.parseId(uri));
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown uri: " + uri);
+ }
+ int rows =
+ db.delete(DialerDatabaseHelper.Tables.FILTERED_NUMBER_TABLE, selection, selectionArgs);
+ if (rows > 0) {
+ notifyChange(uri);
+ }
+ return rows;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ SQLiteDatabase db = mDialerDatabaseHelper.getWritableDatabase();
+ final int match = sUriMatcher.match(uri);
+ switch (match) {
+ case FILTERED_NUMBERS_TABLE:
+ break;
+ case FILTERED_NUMBERS_TABLE_ID:
+ selection = getSelectionWithId(selection, ContentUris.parseId(uri));
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown uri: " + uri);
+ }
+ int rows =
+ db.update(
+ DialerDatabaseHelper.Tables.FILTERED_NUMBER_TABLE, values, selection, selectionArgs);
+ if (rows > 0) {
+ notifyChange(uri);
+ }
+ return rows;
+ }
+
+ private String getSelectionWithId(String selection, long id) {
+ if (TextUtils.isEmpty(selection)) {
+ return FilteredNumberContract.FilteredNumberColumns._ID + "=" + id;
+ } else {
+ return selection + "AND " + FilteredNumberContract.FilteredNumberColumns._ID + "=" + id;
+ }
+ }
+
+ private void notifyChange(Uri uri) {
+ getContext().getContentResolver().notifyChange(uri, null);
+ }
+}
diff --git a/java/com/android/dialer/blocking/FilteredNumbersUtil.java b/java/com/android/dialer/blocking/FilteredNumbersUtil.java
new file mode 100644
index 000000000..61ecf1886
--- /dev/null
+++ b/java/com/android/dialer/blocking/FilteredNumbersUtil.java
@@ -0,0 +1,380 @@
+/*
+ * 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.dialer.blocking;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.AsyncTask;
+import android.preference.PreferenceManager;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.provider.Settings;
+import android.support.annotation.Nullable;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.widget.Toast;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler.OnHasBlockedNumbersListener;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.InteractionEvent;
+import com.android.dialer.util.PermissionsUtil;
+import java.util.concurrent.TimeUnit;
+
+/** Utility to help with tasks related to filtered numbers. */
+public class FilteredNumbersUtil {
+
+ public static final String CALL_BLOCKING_NOTIFICATION_TAG = "call_blocking";
+ public static final int CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_NOTIFICATION_ID = 10;
+ // Pref key for storing the time of end of the last emergency call in milliseconds after epoch.
+ protected static final String LAST_EMERGENCY_CALL_MS_PREF_KEY = "last_emergency_call_ms";
+ // Pref key for storing whether a notification has been dispatched to notify the user that call
+ // blocking has been disabled because of a recent emergency call.
+ protected static final String NOTIFIED_CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_PREF_KEY =
+ "notified_call_blocking_disabled_by_emergency_call";
+ // Disable incoming call blocking if there was a call within the past 2 days.
+ private static final long RECENT_EMERGENCY_CALL_THRESHOLD_MS = 1000 * 60 * 60 * 24 * 2;
+
+ /**
+ * Used for testing to specify the custom threshold value, in milliseconds for whether an
+ * emergency call is "recent". The default value will be used if this custom threshold is less
+ * than zero. For example, to set this threshold to 60 seconds:
+ *
+ * <p>adb shell settings put system dialer_emergency_call_threshold_ms 60000
+ */
+ private static final String RECENT_EMERGENCY_CALL_THRESHOLD_SETTINGS_KEY =
+ "dialer_emergency_call_threshold_ms";
+
+ /** Checks if there exists a contact with {@code Contacts.SEND_TO_VOICEMAIL} set to true. */
+ public static void checkForSendToVoicemailContact(
+ final Context context, final CheckForSendToVoicemailContactListener listener) {
+ final AsyncTask task =
+ new AsyncTask<Object, Void, Boolean>() {
+ @Override
+ public Boolean doInBackground(Object... params) {
+ if (context == null || !PermissionsUtil.hasContactsPermissions(context)) {
+ return false;
+ }
+
+ final Cursor cursor =
+ context
+ .getContentResolver()
+ .query(
+ Contacts.CONTENT_URI,
+ ContactsQuery.PROJECTION,
+ ContactsQuery.SELECT_SEND_TO_VOICEMAIL_TRUE,
+ null,
+ null);
+
+ boolean hasSendToVoicemailContacts = false;
+ if (cursor != null) {
+ try {
+ hasSendToVoicemailContacts = cursor.getCount() > 0;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ return hasSendToVoicemailContacts;
+ }
+
+ @Override
+ public void onPostExecute(Boolean hasSendToVoicemailContact) {
+ if (listener != null) {
+ listener.onComplete(hasSendToVoicemailContact);
+ }
+ }
+ };
+ task.execute();
+ }
+
+ /**
+ * Blocks all the phone numbers of any contacts marked as SEND_TO_VOICEMAIL, then clears the
+ * SEND_TO_VOICEMAIL flag on those contacts.
+ */
+ public static void importSendToVoicemailContacts(
+ final Context context, final ImportSendToVoicemailContactsListener listener) {
+ Logger.get(context).logInteraction(InteractionEvent.Type.IMPORT_SEND_TO_VOICEMAIL);
+ final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler =
+ new FilteredNumberAsyncQueryHandler(context);
+
+ final AsyncTask<Object, Void, Boolean> task =
+ new AsyncTask<Object, Void, Boolean>() {
+ @Override
+ public Boolean doInBackground(Object... params) {
+ if (context == null) {
+ return false;
+ }
+
+ // Get the phone number of contacts marked as SEND_TO_VOICEMAIL.
+ final Cursor phoneCursor =
+ context
+ .getContentResolver()
+ .query(
+ Phone.CONTENT_URI,
+ PhoneQuery.PROJECTION,
+ PhoneQuery.SELECT_SEND_TO_VOICEMAIL_TRUE,
+ null,
+ null);
+
+ if (phoneCursor == null) {
+ return false;
+ }
+
+ try {
+ while (phoneCursor.moveToNext()) {
+ final String normalizedNumber =
+ phoneCursor.getString(PhoneQuery.NORMALIZED_NUMBER_COLUMN_INDEX);
+ final String number = phoneCursor.getString(PhoneQuery.NUMBER_COLUMN_INDEX);
+ if (normalizedNumber != null) {
+ // Block the phone number of the contact.
+ mFilteredNumberAsyncQueryHandler.blockNumber(
+ null, normalizedNumber, number, null);
+ }
+ }
+ } finally {
+ phoneCursor.close();
+ }
+
+ // Clear SEND_TO_VOICEMAIL on all contacts. The setting has been imported to Dialer.
+ ContentValues newValues = new ContentValues();
+ newValues.put(Contacts.SEND_TO_VOICEMAIL, 0);
+ context
+ .getContentResolver()
+ .update(
+ Contacts.CONTENT_URI,
+ newValues,
+ ContactsQuery.SELECT_SEND_TO_VOICEMAIL_TRUE,
+ null);
+
+ return true;
+ }
+
+ @Override
+ public void onPostExecute(Boolean success) {
+ if (success) {
+ if (listener != null) {
+ listener.onImportComplete();
+ }
+ } else if (context != null) {
+ String toastStr = context.getString(R.string.send_to_voicemail_import_failed);
+ Toast.makeText(context, toastStr, Toast.LENGTH_SHORT).show();
+ }
+ }
+ };
+ task.execute();
+ }
+
+ /**
+ * WARNING: This method should NOT be executed on the UI thread. Use {@code
+ * FilteredNumberAsyncQueryHandler} to asynchronously check if a number is blocked.
+ */
+ public static boolean shouldBlockVoicemail(
+ Context context, String number, String countryIso, long voicemailDateMs) {
+ final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
+ if (TextUtils.isEmpty(normalizedNumber)) {
+ return false;
+ }
+
+ if (hasRecentEmergencyCall(context)) {
+ return false;
+ }
+
+ final Cursor cursor =
+ context
+ .getContentResolver()
+ .query(
+ FilteredNumber.CONTENT_URI,
+ new String[] {FilteredNumberColumns.CREATION_TIME},
+ FilteredNumberColumns.NORMALIZED_NUMBER + "=?",
+ new String[] {normalizedNumber},
+ null);
+ if (cursor == null) {
+ return false;
+ }
+ try {
+ /*
+ * Block if number is found and it was added before this voicemail was received.
+ * The VVM's date is reported with precision to the minute, even though its
+ * magnitude is in milliseconds, so we perform the comparison in minutes.
+ */
+ return cursor.moveToFirst()
+ && TimeUnit.MINUTES.convert(voicemailDateMs, TimeUnit.MILLISECONDS)
+ >= TimeUnit.MINUTES.convert(cursor.getLong(0), TimeUnit.MILLISECONDS);
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public static long getLastEmergencyCallTimeMillis(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getLong(LAST_EMERGENCY_CALL_MS_PREF_KEY, 0);
+ }
+
+ public static boolean hasRecentEmergencyCall(Context context) {
+ if (context == null) {
+ return false;
+ }
+
+ Long lastEmergencyCallTime = getLastEmergencyCallTimeMillis(context);
+ if (lastEmergencyCallTime == 0) {
+ return false;
+ }
+
+ return (System.currentTimeMillis() - lastEmergencyCallTime)
+ < getRecentEmergencyCallThresholdMs(context);
+ }
+
+ public static void recordLastEmergencyCallTime(Context context) {
+ if (context == null) {
+ return;
+ }
+
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putLong(LAST_EMERGENCY_CALL_MS_PREF_KEY, System.currentTimeMillis())
+ .putBoolean(NOTIFIED_CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_PREF_KEY, false)
+ .apply();
+
+ maybeNotifyCallBlockingDisabled(context);
+ }
+
+ public static void maybeNotifyCallBlockingDisabled(final Context context) {
+ // The Dialer is not responsible for this notification after migrating
+ if (FilteredNumberCompat.useNewFiltering(context)) {
+ return;
+ }
+ // Skip if the user has already received a notification for the most recent emergency call.
+ if (PreferenceManager.getDefaultSharedPreferences(context)
+ .getBoolean(NOTIFIED_CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_PREF_KEY, false)) {
+ return;
+ }
+
+ // If the user has blocked numbers, notify that call blocking is temporarily disabled.
+ FilteredNumberAsyncQueryHandler queryHandler = new FilteredNumberAsyncQueryHandler(context);
+ queryHandler.hasBlockedNumbers(
+ new OnHasBlockedNumbersListener() {
+ @Override
+ public void onHasBlockedNumbers(boolean hasBlockedNumbers) {
+ if (context == null || !hasBlockedNumbers) {
+ return;
+ }
+
+ NotificationManager notificationManager =
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ Notification.Builder builder =
+ new Notification.Builder(context)
+ .setSmallIcon(R.drawable.ic_block_24dp)
+ .setContentTitle(
+ context.getString(R.string.call_blocking_disabled_notification_title))
+ .setContentText(
+ context.getString(R.string.call_blocking_disabled_notification_text))
+ .setAutoCancel(true);
+
+ builder.setContentIntent(
+ PendingIntent.getActivity(
+ context,
+ 0,
+ FilteredNumberCompat.createManageBlockedNumbersIntent(context),
+ PendingIntent.FLAG_UPDATE_CURRENT));
+
+ notificationManager.notify(
+ CALL_BLOCKING_NOTIFICATION_TAG,
+ CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_NOTIFICATION_ID,
+ builder.build());
+
+ // Record that the user has been notified for this emergency call.
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putBoolean(NOTIFIED_CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_PREF_KEY, true)
+ .apply();
+ }
+ });
+ }
+
+ /**
+ * @param e164Number The e164 formatted version of the number, or {@code null} if such a format
+ * doesn't exist.
+ * @param number The number to attempt blocking.
+ * @return {@code true} if the number can be blocked, {@code false} otherwise.
+ */
+ public static boolean canBlockNumber(Context context, String e164Number, String number) {
+ String blockableNumber = getBlockableNumber(context, e164Number, number);
+ return !TextUtils.isEmpty(blockableNumber)
+ && !PhoneNumberUtils.isEmergencyNumber(blockableNumber);
+ }
+
+ /**
+ * @param e164Number The e164 formatted version of the number, or {@code null} if such a format
+ * doesn't exist..
+ * @param number The number to attempt blocking.
+ * @return The version of the given number that can be blocked with the current blocking solution.
+ */
+ @Nullable
+ public static String getBlockableNumber(
+ Context context, @Nullable String e164Number, String number) {
+ if (!FilteredNumberCompat.useNewFiltering(context)) {
+ return e164Number;
+ }
+ return TextUtils.isEmpty(e164Number) ? number : e164Number;
+ }
+
+ private static long getRecentEmergencyCallThresholdMs(Context context) {
+ if (LogUtil.isVerboseEnabled()) {
+ long thresholdMs =
+ Settings.System.getLong(
+ context.getContentResolver(), RECENT_EMERGENCY_CALL_THRESHOLD_SETTINGS_KEY, 0);
+ return thresholdMs > 0 ? thresholdMs : RECENT_EMERGENCY_CALL_THRESHOLD_MS;
+ } else {
+ return RECENT_EMERGENCY_CALL_THRESHOLD_MS;
+ }
+ }
+
+ public interface CheckForSendToVoicemailContactListener {
+
+ void onComplete(boolean hasSendToVoicemailContact);
+ }
+
+ public interface ImportSendToVoicemailContactsListener {
+
+ void onImportComplete();
+ }
+
+ private static class ContactsQuery {
+
+ static final String[] PROJECTION = {Contacts._ID};
+
+ static final String SELECT_SEND_TO_VOICEMAIL_TRUE = Contacts.SEND_TO_VOICEMAIL + "=1";
+
+ static final int ID_COLUMN_INDEX = 0;
+ }
+
+ public static class PhoneQuery {
+
+ public static final String[] PROJECTION = {Contacts._ID, Phone.NORMALIZED_NUMBER, Phone.NUMBER};
+
+ public static final int ID_COLUMN_INDEX = 0;
+ public static final int NORMALIZED_NUMBER_COLUMN_INDEX = 1;
+ public static final int NUMBER_COLUMN_INDEX = 2;
+
+ public static final String SELECT_SEND_TO_VOICEMAIL_TRUE = Contacts.SEND_TO_VOICEMAIL + "=1";
+ }
+}
diff --git a/java/com/android/dialer/blocking/MigrateBlockedNumbersDialogFragment.java b/java/com/android/dialer/blocking/MigrateBlockedNumbersDialogFragment.java
new file mode 100644
index 000000000..76e50b38e
--- /dev/null
+++ b/java/com/android/dialer/blocking/MigrateBlockedNumbersDialogFragment.java
@@ -0,0 +1,113 @@
+/*
+ * 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.dialer.blocking;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnShowListener;
+import android.os.Bundle;
+import android.view.View;
+import com.android.dialer.blocking.BlockedNumbersMigrator.Listener;
+import java.util.Objects;
+
+/**
+ * Dialog fragment shown to users when they need to migrate to use {@link
+ * android.provider.BlockedNumberContract} for blocking.
+ */
+public class MigrateBlockedNumbersDialogFragment extends DialogFragment {
+
+ private BlockedNumbersMigrator mBlockedNumbersMigrator;
+ private BlockedNumbersMigrator.Listener mMigrationListener;
+
+ /**
+ * Creates a new MigrateBlockedNumbersDialogFragment.
+ *
+ * @param blockedNumbersMigrator The {@link BlockedNumbersMigrator} which will be used to migrate
+ * the numbers.
+ * @param migrationListener The {@link BlockedNumbersMigrator.Listener} to call when the migration
+ * is complete.
+ * @return The new MigrateBlockedNumbersDialogFragment.
+ * @throws NullPointerException if blockedNumbersMigrator or migrationListener are {@code null}.
+ */
+ public static DialogFragment newInstance(
+ BlockedNumbersMigrator blockedNumbersMigrator,
+ BlockedNumbersMigrator.Listener migrationListener) {
+ MigrateBlockedNumbersDialogFragment fragment = new MigrateBlockedNumbersDialogFragment();
+ fragment.mBlockedNumbersMigrator = Objects.requireNonNull(blockedNumbersMigrator);
+ fragment.mMigrationListener = Objects.requireNonNull(migrationListener);
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+ AlertDialog dialog =
+ new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.migrate_blocked_numbers_dialog_title)
+ .setMessage(R.string.migrate_blocked_numbers_dialog_message)
+ .setPositiveButton(R.string.migrate_blocked_numbers_dialog_allow_button, null)
+ .setNegativeButton(R.string.migrate_blocked_numbers_dialog_cancel_button, null)
+ .create();
+ // The Dialog's buttons aren't available until show is called, so an OnShowListener
+ // is used to set the positive button callback.
+ dialog.setOnShowListener(
+ new OnShowListener() {
+ @Override
+ public void onShow(DialogInterface dialog) {
+ final AlertDialog alertDialog = (AlertDialog) dialog;
+ alertDialog
+ .getButton(AlertDialog.BUTTON_POSITIVE)
+ .setOnClickListener(newPositiveButtonOnClickListener(alertDialog));
+ }
+ });
+ return dialog;
+ }
+
+ /*
+ * Creates a new View.OnClickListener to be used as the positive button in this dialog. The
+ * OnClickListener will grey out the dialog's positive and negative buttons while the migration
+ * is underway, and close the dialog once the migrate is complete.
+ */
+ private View.OnClickListener newPositiveButtonOnClickListener(final AlertDialog alertDialog) {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
+ alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false);
+ mBlockedNumbersMigrator.migrate(
+ new Listener() {
+ @Override
+ public void onComplete() {
+ alertDialog.dismiss();
+ mMigrationListener.onComplete();
+ }
+ });
+ }
+ };
+ }
+
+ @Override
+ public void onPause() {
+ // The dialog is dismissed and state is cleaned up onPause, i.e. rotation.
+ dismiss();
+ mBlockedNumbersMigrator = null;
+ mMigrationListener = null;
+ super.onPause();
+ }
+}
diff --git a/res/drawable-hdpi/ic_block_24dp.png b/java/com/android/dialer/blocking/res/drawable-hdpi/ic_block_24dp.png
index 2ccc89d24..2ccc89d24 100644
--- a/res/drawable-hdpi/ic_block_24dp.png
+++ b/java/com/android/dialer/blocking/res/drawable-hdpi/ic_block_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_report_24dp.png b/java/com/android/dialer/blocking/res/drawable-hdpi/ic_report_24dp.png
index dc0c995c1..dc0c995c1 100644
--- a/res/drawable-hdpi/ic_report_24dp.png
+++ b/java/com/android/dialer/blocking/res/drawable-hdpi/ic_report_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/blocking/res/drawable-hdpi/ic_report_white_36dp.png b/java/com/android/dialer/blocking/res/drawable-hdpi/ic_report_white_36dp.png
new file mode 100644
index 000000000..919a872e0
--- /dev/null
+++ b/java/com/android/dialer/blocking/res/drawable-hdpi/ic_report_white_36dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_block_24dp.png b/java/com/android/dialer/blocking/res/drawable-mdpi/ic_block_24dp.png
index ec1b33f0e..ec1b33f0e 100644
--- a/res/drawable-mdpi/ic_block_24dp.png
+++ b/java/com/android/dialer/blocking/res/drawable-mdpi/ic_block_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_report_24dp.png b/java/com/android/dialer/blocking/res/drawable-mdpi/ic_report_24dp.png
index 70b82d6c1..70b82d6c1 100644
--- a/res/drawable-mdpi/ic_report_24dp.png
+++ b/java/com/android/dialer/blocking/res/drawable-mdpi/ic_report_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/blocking/res/drawable-mdpi/ic_report_white_36dp.png b/java/com/android/dialer/blocking/res/drawable-mdpi/ic_report_white_36dp.png
new file mode 100644
index 000000000..dc0c995c1
--- /dev/null
+++ b/java/com/android/dialer/blocking/res/drawable-mdpi/ic_report_white_36dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_block_24dp.png b/java/com/android/dialer/blocking/res/drawable-xhdpi/ic_block_24dp.png
index 7aba97b65..7aba97b65 100644
--- a/res/drawable-xhdpi/ic_block_24dp.png
+++ b/java/com/android/dialer/blocking/res/drawable-xhdpi/ic_block_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_report_24dp.png b/java/com/android/dialer/blocking/res/drawable-xhdpi/ic_report_24dp.png
index 18e7764ab..18e7764ab 100644
--- a/res/drawable-xhdpi/ic_report_24dp.png
+++ b/java/com/android/dialer/blocking/res/drawable-xhdpi/ic_report_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_report_24dp.png b/java/com/android/dialer/blocking/res/drawable-xhdpi/ic_report_white_36dp.png
index aed766804..aed766804 100644
--- a/res/drawable-xxhdpi/ic_report_24dp.png
+++ b/java/com/android/dialer/blocking/res/drawable-xhdpi/ic_report_white_36dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_block_24dp.png b/java/com/android/dialer/blocking/res/drawable-xxhdpi/ic_block_24dp.png
index fddfa54b8..fddfa54b8 100644
--- a/res/drawable-xxhdpi/ic_block_24dp.png
+++ b/java/com/android/dialer/blocking/res/drawable-xxhdpi/ic_block_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/blocking/res/drawable-xxhdpi/ic_report_24dp.png b/java/com/android/dialer/blocking/res/drawable-xxhdpi/ic_report_24dp.png
new file mode 100644
index 000000000..aed766804
--- /dev/null
+++ b/java/com/android/dialer/blocking/res/drawable-xxhdpi/ic_report_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_report_white_36dp.png b/java/com/android/dialer/blocking/res/drawable-xxhdpi/ic_report_white_36dp.png
index f7cfacbd4..f7cfacbd4 100644
--- a/InCallUI/res/drawable-xxhdpi/ic_report_white_36dp.png
+++ b/java/com/android/dialer/blocking/res/drawable-xxhdpi/ic_report_white_36dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_block_24dp.png b/java/com/android/dialer/blocking/res/drawable-xxxhdpi/ic_block_24dp.png
index 0378d1bed..0378d1bed 100644
--- a/res/drawable-xxxhdpi/ic_block_24dp.png
+++ b/java/com/android/dialer/blocking/res/drawable-xxxhdpi/ic_block_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_report_24dp.png b/java/com/android/dialer/blocking/res/drawable-xxxhdpi/ic_report_24dp.png
index 855e59015..855e59015 100644
--- a/res/drawable-xxxhdpi/ic_report_24dp.png
+++ b/java/com/android/dialer/blocking/res/drawable-xxxhdpi/ic_report_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_report_white_36dp.png b/java/com/android/dialer/blocking/res/drawable-xxxhdpi/ic_report_white_36dp.png
index 7ef0d7afc..7ef0d7afc 100644
--- a/InCallUI/res/drawable-xxxhdpi/ic_report_white_36dp.png
+++ b/java/com/android/dialer/blocking/res/drawable-xxxhdpi/ic_report_white_36dp.png
Binary files differ
diff --git a/java/com/android/dialer/blocking/res/drawable/blocked_contact.xml b/java/com/android/dialer/blocking/res/drawable/blocked_contact.xml
new file mode 100644
index 000000000..09d7989e8
--- /dev/null
+++ b/java/com/android/dialer/blocking/res/drawable/blocked_contact.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="@color/blocked_contact_background"/>
+ <size
+ android:height="24dp"
+ android:width="24dp"/>
+ </shape>
+ </item>
+
+ <item
+ android:drawable="@drawable/ic_report_24dp"
+ android:gravity="center"
+ android:height="18dp"
+ android:width="18dp"/>
+
+</layer-list>
diff --git a/java/com/android/dialer/blocking/res/layout/block_report_spam_dialog.xml b/java/com/android/dialer/blocking/res/layout/block_report_spam_dialog.xml
new file mode 100644
index 000000000..82e8d80b3
--- /dev/null
+++ b/java/com/android/dialer/blocking/res/layout/block_report_spam_dialog.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="25dp"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/block_details"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="10dp"
+ android:text="@string/block_report_number_alert_details"
+ android:textColor="@color/block_report_spam_primary_text_color"
+ android:textSize="@dimen/blocked_report_spam_primary_text_size"/>
+
+ <CheckBox
+ android:id="@+id/report_number_as_spam_action"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/checkbox_report_as_spam_action"
+ android:textSize="@dimen/blocked_report_spam_primary_text_size"/>
+</LinearLayout>
diff --git a/java/com/android/dialer/blocking/res/values/colors.xml b/java/com/android/dialer/blocking/res/values/colors.xml
new file mode 100644
index 000000000..d1a567d9e
--- /dev/null
+++ b/java/com/android/dialer/blocking/res/values/colors.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ 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
+-->
+<resources>
+
+ <!-- 87% black -->
+ <color name="block_report_spam_primary_text_color">#de000000</color>
+
+ <!-- Note, this is also used by InCallUi. -->
+ <color name="blocked_contact_background">#A52714</color>
+
+</resources>
diff --git a/java/com/android/dialer/blocking/res/values/dimens.xml b/java/com/android/dialer/blocking/res/values/dimens.xml
new file mode 100644
index 000000000..cd7cfe2fd
--- /dev/null
+++ b/java/com/android/dialer/blocking/res/values/dimens.xml
@@ -0,0 +1,18 @@
+<!--
+ ~ 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
+-->
+<resources>
+ <dimen name="blocked_report_spam_primary_text_size">16sp</dimen>
+</resources>
diff --git a/java/com/android/dialer/blocking/res/values/strings.xml b/java/com/android/dialer/blocking/res/values/strings.xml
new file mode 100644
index 000000000..8abff4561
--- /dev/null
+++ b/java/com/android/dialer/blocking/res/values/strings.xml
@@ -0,0 +1,122 @@
+<!--
+ ~ 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
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Title for dialog which opens when the user needs to migrate to the framework blocking implementation [CHAR LIMIT=30]-->
+ <string name="migrate_blocked_numbers_dialog_title">New, simplified blocking</string>
+
+ <!-- Body text for dialog which opens when the user needs to migrate to the framework blocking implementation [CHAR LIMIT=NONE]-->
+ <string name="migrate_blocked_numbers_dialog_message">To better protect you, Phone needs to change how blocking works. Your blocked numbers will now stop both calls and texts and may be shared with other apps.</string>
+
+ <!-- Positive confirmation button for the dialog which opens when the user needs to migrate to the framework blocking implementation [CHAR LIMIT=NONE]-->
+ <string name="migrate_blocked_numbers_dialog_allow_button">Allow</string>
+
+ <!-- Do not translate -->
+ <string name="migrate_blocked_numbers_dialog_cancel_button">@android:string/cancel</string>
+
+ <!-- Confirmation dialog title for blocking a number. [CHAR LIMIT=NONE] -->
+ <string name="block_number_confirmation_title">Block
+ <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g>?</string>
+
+ <!-- Confirmation dialog message for blocking a number with visual voicemail active.
+ [CHAR LIMIT=NONE] -->
+ <string name="block_number_confirmation_message_vvm">
+ Calls from this number will be blocked and voicemails will be automatically deleted.
+ </string>
+
+ <!-- Confirmation dialog message for blocking a number with no visual voicemail.
+ [CHAR LIMIT=NONE] -->
+ <string name="block_number_confirmation_message_no_vvm">
+ Calls from this number will be blocked, but the caller may still be able to leave you voicemails.
+ </string>
+
+ <!-- Confirmation dialog message for blocking a number with new filtering enabled.
+ [CHAR LIMIT=NONE] -->
+ <string name="block_number_confirmation_message_new_filtering">
+ You will no longer receive calls or texts from this number.
+ </string>
+
+ <!-- Block number alert dialog button [CHAR LIMIT=32] -->
+ <string name="block_number_ok">BLOCK</string>
+
+ <!-- Confirmation dialog for unblocking a number. [CHAR LIMIT=NONE] -->
+ <string name="unblock_number_confirmation_title">Unblock
+ <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g>?</string>
+
+ <!-- Unblock number alert dialog button [CHAR LIMIT=32] -->
+ <string name="unblock_number_ok">UNBLOCK</string>
+
+ <!-- Error message shown when user tries to add invalid number to the block list.
+ [CHAR LIMIT=64] -->
+ <string name="invalidNumber"><xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g>
+ is invalid.</string>
+
+ <!-- Text for snackbar to undo blocking a number. [CHAR LIMIT=64] -->
+ <string name="snackbar_number_blocked">
+ <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g> blocked</string>
+
+ <!-- Text for snackbar to undo unblocking a number. [CHAR LIMIT=64] -->
+ <string name="snackbar_number_unblocked">
+ <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g>
+ unblocked</string>
+
+ <!-- Text for undo button in snackbar for blocking/unblocking number. [CHAR LIMIT=10] -->
+ <string name="block_number_undo">UNDO</string>
+
+ <!-- Error toast message for when send to voicemail import fails. [CHAR LIMIT=40] -->
+ <string name="send_to_voicemail_import_failed">Import failed</string>
+
+ <!-- Title of notification telling the user that call blocking has been temporarily disabled.
+ [CHAR LIMIT=56] -->
+ <string name="call_blocking_disabled_notification_title">
+ Call blocking disabled for 48 hours
+ </string>
+
+ <!-- Text for notification which provides the reason that call blocking has been temporarily
+ disabled. Namely, we disable call blocking after an emergency call in case of return
+ phone calls made by emergency services. [CHAR LIMIT=64] -->
+ <string name="call_blocking_disabled_notification_text">
+ Disabled because an emergency call was made.
+ </string>
+
+ <!-- Title of alert dialog after clicking on Block/report as spam. [CHAR LIMIT=100] -->
+ <string name="block_report_number_alert_title">Block <xliff:g id="number">%1$s</xliff:g>?</string>
+
+ <!-- Text in alert dialog after clicking on Block/report as spam. [CHAR LIMIT=100] -->
+ <string name="block_report_number_alert_details">You will no longer receive calls from this number.</string>
+
+ <!-- Text in alert dialog after clicking on Block. [CHAR LIMIT=100] -->
+ <string name="block_number_alert_details"><xliff:g id="text">%1$s</xliff:g> This call will be reported as spam.</string>
+
+ <!-- Text in alert dialog after clicking on Unblock. [CHAR LIMIT=100] -->
+ <string name="unblock_number_alert_details">This number will be unblocked and reported as not spam. Future calls won\'t be identified as spam.</string>
+
+ <!-- Title of alert dialog after clicking on Unblock. [CHAR LIMIT=100] -->
+ <string name="unblock_report_number_alert_title">Unblock <xliff:g id="number">%1$s</xliff:g>?</string>
+
+ <!-- Report not spam number alert dialog button [CHAR LIMIT=32] -->
+ <string name="report_not_spam_alert_button">Report</string>
+
+ <!-- Title of alert dialog after clicking on Report as not spam. [CHAR LIMIT=100] -->
+ <string name="report_not_spam_alert_title">Report a mistake?</string>
+
+ <!-- Text in alert dialog after clicking on Report as not spam. [CHAR LIMIT=100] -->
+ <string name="report_not_spam_alert_details">Future calls from <xliff:g id="number">%1$s</xliff:g> will no longer be identified as spam.</string>
+
+ <!-- Label for checkbox in the Alert dialog to allow the user to report the number as spam as well. [CHAR LIMIT=30] -->
+ <string name="checkbox_report_as_spam_action">Report call as spam</string>
+
+</resources>
diff --git a/java/com/android/dialer/buildtype/BuildType.java b/java/com/android/dialer/buildtype/BuildType.java
new file mode 100644
index 000000000..c0a54a519
--- /dev/null
+++ b/java/com/android/dialer/buildtype/BuildType.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 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.dialer.buildtype;
+
+import android.support.annotation.IntDef;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Utility to find out which build type the app is running as. */
+public class BuildType {
+
+ /** The type of build. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ BUGFOOD, FISHFOOD, DOGFOOD, RELEASE,
+ })
+ public @interface Type {}
+
+ public static final int BUGFOOD = 1;
+ public static final int FISHFOOD = 2;
+ public static final int DOGFOOD = 3;
+ public static final int RELEASE = 4;
+
+ private static int cachedBuildType;
+ private static boolean didInitializeBuildType;
+
+ @Type
+ public static synchronized int get() {
+ if (!didInitializeBuildType) {
+ didInitializeBuildType = true;
+ try {
+ Class<?> clazz = Class.forName(BuildTypeAccessor.class.getName() + "Impl");
+ BuildTypeAccessor accessorImpl = (BuildTypeAccessor) clazz.getConstructor().newInstance();
+ cachedBuildType = accessorImpl.getBuildType();
+ } catch (ReflectiveOperationException e) {
+ LogUtil.e("BuildType.get", "error creating BuildTypeAccessorImpl", e);
+ Assert.fail(
+ "Unable to get build type. To fix this error include one of the build type "
+ + "modules (bugfood, etc...) in your target.");
+ }
+ }
+ return cachedBuildType;
+ }
+
+ private BuildType() {}
+}
diff --git a/java/com/android/dialer/buildtype/BuildTypeAccessor.java b/java/com/android/dialer/buildtype/BuildTypeAccessor.java
new file mode 100644
index 000000000..940cf817c
--- /dev/null
+++ b/java/com/android/dialer/buildtype/BuildTypeAccessor.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 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.dialer.buildtype;
+
+import com.android.dialer.proguard.UsedByReflection;
+
+/**
+ * Gets the build type. The functionality depends on a an implementation being present in the app
+ * that has the same package and the class name ending in "Impl". For example,
+ * com.android.dialer.buildtype.BuildTypeAccessorImpl. This class is found by the module using
+ * reflection.
+ */
+@UsedByReflection(value = "BuildType.java")
+/* package */ interface BuildTypeAccessor {
+ @BuildType.Type
+ int getBuildType();
+}
diff --git a/java/com/android/dialer/buildtype/dogfood/BuildTypeAccessorImpl.java b/java/com/android/dialer/buildtype/dogfood/BuildTypeAccessorImpl.java
new file mode 100644
index 000000000..e1f2cdc79
--- /dev/null
+++ b/java/com/android/dialer/buildtype/dogfood/BuildTypeAccessorImpl.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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.dialer.buildtype;
+
+import com.android.dialer.proguard.UsedByReflection;
+
+/** Gets the build type. */
+@UsedByReflection(value = "BuildType.java")
+public class BuildTypeAccessorImpl implements BuildTypeAccessor {
+
+ @Override
+ @BuildType.Type
+ public int getBuildType() {
+ return BuildType.DOGFOOD;
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/AndroidManifest.xml b/java/com/android/dialer/callcomposer/AndroidManifest.xml
new file mode 100644
index 000000000..c99f22b90
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ 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
+ -->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.dialer.callcomposer">
+
+ <application>
+ <activity
+ android:name="com.android.dialer.callcomposer.CallComposerActivity"
+ android:exported="false"
+ android:theme="@style/Theme.AppCompat.CallComposer"
+ android:windowSoftInputMode="adjustResize"
+ android:screenOrientation="portrait"/>
+ </application>
+</manifest>
diff --git a/java/com/android/dialer/callcomposer/CallComposerActivity.java b/java/com/android/dialer/callcomposer/CallComposerActivity.java
new file mode 100644
index 000000000..eef3d210d
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/CallComposerActivity.java
@@ -0,0 +1,728 @@
+/*
+ * 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.dialer.callcomposer;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorSet;
+import android.animation.ArgbEvaluator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.content.FileProvider;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.support.v7.app.AppCompatActivity;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLayoutChangeListener;
+import android.view.ViewAnimationUtils;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.QuickContactBadge;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import android.widget.Toolbar;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.util.UriUtils;
+import com.android.dialer.callcomposer.CallComposerFragment.CallComposerListener;
+import com.android.dialer.callcomposer.nano.CallComposerContact;
+import com.android.dialer.callcomposer.util.CopyAndResizeImageTask;
+import com.android.dialer.callcomposer.util.CopyAndResizeImageTask.Callback;
+import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.callintent.nano.CallInitiationType;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.UiUtil;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.constants.Constants;
+import com.android.dialer.enrichedcall.EnrichedCallManager;
+import com.android.dialer.enrichedcall.EnrichedCallManager.State;
+import com.android.dialer.enrichedcall.Session;
+import com.android.dialer.enrichedcall.extensions.StateExtension;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.multimedia.MultimediaData;
+import com.android.dialer.protos.ProtoParsers;
+import com.android.dialer.telecom.TelecomUtil;
+import com.android.dialer.util.ViewUtil;
+import java.io.File;
+
+/**
+ * Implements an activity which prompts for a call with additional media for an outgoing call. The
+ * activity includes a pop up with:
+ *
+ * <ul>
+ * <li>Contact galleryIcon
+ * <li>Name
+ * <li>Number
+ * <li>Media options to attach a gallery image, camera image or a message
+ * </ul>
+ */
+public class CallComposerActivity extends AppCompatActivity
+ implements OnClickListener,
+ OnPageChangeListener,
+ CallComposerListener,
+ OnLayoutChangeListener,
+ AnimatorListener,
+ EnrichedCallManager.StateChangedListener {
+
+ private static final int VIEW_PAGER_ANIMATION_DURATION_MILLIS = 300;
+ private static final int ENTRANCE_ANIMATION_DURATION_MILLIS = 500;
+
+ private static final String ARG_CALL_COMPOSER_CONTACT = "CALL_COMPOSER_CONTACT";
+
+ private static final String ENTRANCE_ANIMATION_KEY = "entrance_animation_key";
+ private static final String CURRENT_INDEX_KEY = "current_index_key";
+ private static final String VIEW_PAGER_STATE_KEY = "view_pager_state_key";
+ private static final String LOCATIONS_KEY = "locations_key";
+ private static final String SESSION_ID_KEY = "session_id_key";
+
+ private CallComposerContact contact;
+ private Long sessionId = Session.NO_SESSION_ID;
+
+ private TextView nameView;
+ private TextView numberView;
+ private QuickContactBadge contactPhoto;
+ private RelativeLayout contactContainer;
+ private Toolbar toolbar;
+ private View sendAndCall;
+
+ private ImageView cameraIcon;
+ private ImageView galleryIcon;
+ private ImageView messageIcon;
+ private ViewPager pager;
+ private CallComposerPagerAdapter adapter;
+
+ private FrameLayout background;
+ private LinearLayout windowContainer;
+
+ private FastOutSlowInInterpolator interpolator;
+ private boolean shouldAnimateEntrance = true;
+ private boolean inFullscreenMode;
+ private boolean isSendAndCallHidingOrHidden = true;
+ private boolean isAnimatingContactBar;
+ private boolean layoutChanged;
+ private int currentIndex;
+ private int[] locations;
+ private int currentLocation;
+
+ @NonNull private EnrichedCallManager enrichedCallManager;
+
+ public static Intent newIntent(Context context, CallComposerContact contact) {
+ Intent intent = new Intent(context, CallComposerActivity.class);
+ ProtoParsers.put(intent, ARG_CALL_COMPOSER_CONTACT, contact);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.call_composer_activity);
+
+ nameView = (TextView) findViewById(R.id.contact_name);
+ numberView = (TextView) findViewById(R.id.phone_number);
+ contactPhoto = (QuickContactBadge) findViewById(R.id.contact_photo);
+ cameraIcon = (ImageView) findViewById(R.id.call_composer_camera);
+ galleryIcon = (ImageView) findViewById(R.id.call_composer_photo);
+ messageIcon = (ImageView) findViewById(R.id.call_composer_message);
+ contactContainer = (RelativeLayout) findViewById(R.id.contact_bar);
+ pager = (ViewPager) findViewById(R.id.call_composer_view_pager);
+ background = (FrameLayout) findViewById(R.id.background);
+ windowContainer = (LinearLayout) findViewById(R.id.call_composer_container);
+ toolbar = (Toolbar) findViewById(R.id.toolbar);
+ sendAndCall = findViewById(R.id.send_and_call_button);
+
+ interpolator = new FastOutSlowInInterpolator();
+ adapter =
+ new CallComposerPagerAdapter(
+ getSupportFragmentManager(),
+ getResources().getInteger(R.integer.call_composer_message_limit));
+ pager.setAdapter(adapter);
+ pager.addOnPageChangeListener(this);
+
+ setActionBar(toolbar);
+ toolbar.setNavigationIcon(R.drawable.quantum_ic_arrow_back_white_24);
+ toolbar.setNavigationOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ });
+
+ background.addOnLayoutChangeListener(this);
+ cameraIcon.setOnClickListener(this);
+ galleryIcon.setOnClickListener(this);
+ messageIcon.setOnClickListener(this);
+ sendAndCall.setOnClickListener(this);
+
+ onHandleIntent(getIntent());
+
+ enrichedCallManager = EnrichedCallManager.Accessor.getInstance(getApplication());
+ if (savedInstanceState != null) {
+ shouldAnimateEntrance = savedInstanceState.getBoolean(ENTRANCE_ANIMATION_KEY);
+ locations = savedInstanceState.getIntArray(LOCATIONS_KEY);
+ pager.onRestoreInstanceState(savedInstanceState.getParcelable(VIEW_PAGER_STATE_KEY));
+ currentIndex = savedInstanceState.getInt(CURRENT_INDEX_KEY);
+ sessionId = savedInstanceState.getLong(SESSION_ID_KEY);
+ onPageSelected(currentIndex);
+ } else {
+ locations = new int[adapter.getCount()];
+ for (int i = 0; i < locations.length; i++) {
+ locations[i] = CallComposerFragment.CONTENT_TOP_UNSET;
+ }
+ sessionId = enrichedCallManager.startCallComposerSession(contact.number);
+ }
+
+ // Since we can't animate the views until they are ready to be drawn, we use this listener to
+ // track that and animate the call compose UI as soon as it's ready.
+ ViewUtil.doOnPreDraw(
+ windowContainer,
+ false,
+ new Runnable() {
+ @Override
+ public void run() {
+ runEntranceAnimation();
+ }
+ });
+
+ setMediaIconSelected(0);
+
+ // This activity is started using startActivityForResult. By default, mark this as succeeded
+ // and flip this to RESULT_CANCELED if something goes wrong.
+ setResult(RESULT_OK);
+
+ if (sessionId == Session.NO_SESSION_ID) {
+ LogUtil.w("CallComposerActivity.onCreate", "failed to create call composer session");
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ enrichedCallManager.registerStateChangedListener(this);
+ refreshUiForCallComposerState();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ enrichedCallManager.unregisterStateChangedListener(this);
+ }
+
+ @Override
+ public void onEnrichedCallStateChanged() {
+ refreshUiForCallComposerState();
+ }
+
+ private void refreshUiForCallComposerState() {
+ Session session = enrichedCallManager.getSession(sessionId);
+ if (session == null) {
+ return;
+ }
+
+ @State int state = session.getState();
+ LogUtil.i(
+ "CallComposerActivity.refreshUiForCallComposerState",
+ "state: %s",
+ StateExtension.toString(state));
+
+ if (state == EnrichedCallManager.STATE_START_FAILED
+ || state == EnrichedCallManager.STATE_CLOSED) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ onHandleIntent(intent);
+ }
+
+ @Override
+ public void onClick(View view) {
+ LogUtil.enterBlock("CallComposerActivity.onClick");
+ if (view == cameraIcon) {
+ pager.setCurrentItem(CallComposerPagerAdapter.INDEX_CAMERA, true /* animate */);
+ } else if (view == galleryIcon) {
+ pager.setCurrentItem(CallComposerPagerAdapter.INDEX_GALLERY, true /* animate */);
+ } else if (view == messageIcon) {
+ pager.setCurrentItem(CallComposerPagerAdapter.INDEX_MESSAGE, true /* animate */);
+ } else if (view == sendAndCall) {
+ if (!sessionReady()) {
+ LogUtil.i(
+ "CallComposerActivity.onClick", "sendAndCall pressed, but the session isn't ready");
+ Logger.get(this)
+ .logImpression(
+ DialerImpression.Type
+ .CALL_COMPOSER_ACTIVITY_SEND_AND_CALL_PRESSED_WHEN_SESSION_NOT_READY);
+ return;
+ }
+ sendAndCall.setEnabled(false);
+ CallComposerFragment fragment =
+ (CallComposerFragment) adapter.instantiateItem(pager, currentIndex);
+ MultimediaData.Builder builder = MultimediaData.builder();
+
+ if (fragment instanceof MessageComposerFragment) {
+ MessageComposerFragment messageComposerFragment = (MessageComposerFragment) fragment;
+ builder.setSubject(messageComposerFragment.getMessage());
+ placeRCSCall(builder);
+ }
+ if (fragment instanceof GalleryComposerFragment) {
+ GalleryComposerFragment galleryComposerFragment = (GalleryComposerFragment) fragment;
+ // If the current data is not a copy, make one.
+ if (!galleryComposerFragment.selectedDataIsCopy()) {
+ new CopyAndResizeImageTask(
+ CallComposerActivity.this,
+ galleryComposerFragment.getGalleryData().getFileUri(),
+ new Callback() {
+ @Override
+ public void onCopySuccessful(File file, String mimeType) {
+ Uri shareableUri =
+ FileProvider.getUriForFile(
+ CallComposerActivity.this,
+ Constants.get().getFileProviderAuthority(),
+ file);
+
+ builder.setImage(grantUriPermission(shareableUri), mimeType);
+ placeRCSCall(builder);
+ }
+
+ @Override
+ public void onCopyFailed(Throwable throwable) {
+ // TODO(b/33753902)
+ LogUtil.e("CallComposerActivity.onCopyFailed", "copy Failed", throwable);
+ }
+ })
+ .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ } else {
+ Uri shareableUri =
+ FileProvider.getUriForFile(
+ this,
+ Constants.get().getFileProviderAuthority(),
+ new File(galleryComposerFragment.getGalleryData().getFilePath()));
+
+ builder.setImage(
+ grantUriPermission(shareableUri),
+ galleryComposerFragment.getGalleryData().getMimeType());
+
+ placeRCSCall(builder);
+ }
+ }
+ if (fragment instanceof CameraComposerFragment) {
+ CameraComposerFragment cameraComposerFragment = (CameraComposerFragment) fragment;
+ cameraComposerFragment.getCameraUriWhenReady(
+ uri -> {
+ builder.setImage(grantUriPermission(uri), cameraComposerFragment.getMimeType());
+ placeRCSCall(builder);
+ });
+ }
+ } else {
+ Assert.fail();
+ }
+ }
+
+ private boolean sessionReady() {
+ Session session = enrichedCallManager.getSession(sessionId);
+ if (session == null) {
+ return false;
+ }
+
+ return session.getState() == EnrichedCallManager.STATE_STARTED;
+ }
+
+ private void placeRCSCall(MultimediaData.Builder builder) {
+ LogUtil.i("CallComposerActivity.placeRCSCall", "placing enriched call");
+ Logger.get(this).logImpression(DialerImpression.Type.CALL_COMPOSER_ACTIVITY_PLACE_RCS_CALL);
+ enrichedCallManager.sendCallComposerData(sessionId, builder.build());
+ TelecomUtil.placeCall(
+ this, new CallIntentBuilder(contact.number, CallInitiationType.Type.CALL_COMPOSER).build());
+ finish();
+ }
+
+ /** Give permission to Messenger to view our image for RCS purposes. */
+ private Uri grantUriPermission(Uri uri) {
+ // TODO: Move this to the enriched call manager.
+ grantUriPermission(
+ "com.google.android.apps.messaging", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ return uri;
+ }
+
+ /** Animates {@code contactContainer} to align with content inside viewpager. */
+ @Override
+ public void onPageSelected(int position) {
+ if (currentIndex == CallComposerPagerAdapter.INDEX_MESSAGE) {
+ UiUtil.hideKeyboardFrom(this, windowContainer);
+ } else if (position == CallComposerPagerAdapter.INDEX_MESSAGE && inFullscreenMode) {
+ UiUtil.openKeyboardFrom(this, windowContainer);
+ }
+ currentIndex = position;
+ CallComposerFragment fragment = (CallComposerFragment) adapter.instantiateItem(pager, position);
+ locations[currentIndex] = fragment.getContentTopPx();
+ animateContactContainer(locations[currentIndex]);
+ animateSendAndCall(fragment.shouldHide());
+ setMediaIconSelected(position);
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ CallComposerFragment fragment = (CallComposerFragment) adapter.instantiateItem(pager, position);
+ animateContactContainer(fragment.getContentTopPx());
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {}
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(VIEW_PAGER_STATE_KEY, pager.onSaveInstanceState());
+ outState.putBoolean(ENTRANCE_ANIMATION_KEY, shouldAnimateEntrance);
+ outState.putInt(CURRENT_INDEX_KEY, currentIndex);
+ outState.putIntArray(LOCATIONS_KEY, locations);
+ outState.putLong(SESSION_ID_KEY, sessionId);
+ }
+
+ @Override
+ public void onBackPressed() {
+ runExitAnimation();
+ }
+
+ @Override
+ public void composeCall(CallComposerFragment fragment) {
+ // Since our ViewPager restores state to our fragments, it's possible that they could call
+ // #composeCall, so we have to check if the calling fragment is the current fragment.
+ if (adapter.instantiateItem(pager, currentIndex) != fragment) {
+ return;
+ }
+ animateSendAndCall(fragment.shouldHide());
+ }
+
+ // To detect when the keyboard changes.
+ @Override
+ public void onLayoutChange(
+ View view,
+ int left,
+ int top,
+ int right,
+ int bottom,
+ int oldLeft,
+ int oldTop,
+ int oldRight,
+ int oldBottom) {
+ // To prevent infinite layout change loops
+ if (layoutChanged) {
+ layoutChanged = false;
+ return;
+ }
+
+ layoutChanged = true;
+ if (pager.getTop() < 0 || inFullscreenMode) {
+ ViewGroup.LayoutParams layoutParams = pager.getLayoutParams();
+ layoutParams.height = background.getHeight() - toolbar.getHeight() - messageIcon.getHeight();
+ pager.setLayoutParams(layoutParams);
+ }
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ isAnimatingContactBar = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ isAnimatingContactBar = false;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {}
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {}
+
+ /**
+ * Reads arguments from the fragment arguments and populates the necessary instance variables.
+ * Copied from {@link com.android.contacts.common.dialog.CallSubjectDialog}.
+ */
+ private void onHandleIntent(Intent intent) {
+ Bundle arguments = intent.getExtras();
+ if (arguments == null) {
+ throw new RuntimeException("CallComposerActivity.onHandleIntent, Arguments cannot be null.");
+ }
+ contact =
+ ProtoParsers.getFromInstanceState(
+ arguments, ARG_CALL_COMPOSER_CONTACT, new CallComposerContact());
+ updateContactInfo();
+ }
+
+ /**
+ * Populates the contact info fields based on the current contact information. Copied from {@link
+ * com.android.contacts.common.dialog.CallSubjectDialog}.
+ */
+ private void updateContactInfo() {
+ if (contact.contactUri != null) {
+ setPhoto(
+ contact.photoId,
+ Uri.parse(contact.photoUri),
+ Uri.parse(contact.contactUri),
+ contact.nameOrNumber,
+ contact.isBusiness);
+ } else {
+ contactPhoto.setVisibility(View.GONE);
+ }
+ nameView.setText(contact.nameOrNumber);
+ getActionBar().setTitle(contact.nameOrNumber);
+ if (!TextUtils.isEmpty(contact.numberLabel) && !TextUtils.isEmpty(contact.displayNumber)) {
+ numberView.setVisibility(View.VISIBLE);
+ String secondaryInfo =
+ getString(
+ com.android.contacts.common.R.string.call_subject_type_and_number,
+ contact.numberLabel,
+ contact.displayNumber);
+ numberView.setText(secondaryInfo);
+ toolbar.setSubtitle(secondaryInfo);
+ } else {
+ numberView.setVisibility(View.GONE);
+ numberView.setText(null);
+ }
+ }
+
+ /**
+ * Sets the photo on the quick contact galleryIcon. Copied from {@link
+ * com.android.contacts.common.dialog.CallSubjectDialog}.
+ */
+ private void setPhoto(
+ long photoId, Uri photoUri, Uri contactUri, String displayName, boolean isBusiness) {
+ contactPhoto.assignContactUri(contactUri);
+ if (CompatUtils.isLollipopCompatible()) {
+ contactPhoto.setOverlay(null);
+ }
+
+ int contactType;
+ if (isBusiness) {
+ contactType = ContactPhotoManager.TYPE_BUSINESS;
+ } else {
+ contactType = ContactPhotoManager.TYPE_DEFAULT;
+ }
+
+ String lookupKey = null;
+ if (contactUri != null) {
+ lookupKey = UriUtils.getLookupKeyFromUri(contactUri);
+ }
+
+ ContactPhotoManager.DefaultImageRequest request =
+ new ContactPhotoManager.DefaultImageRequest(
+ displayName, lookupKey, contactType, true /* isCircular */);
+
+ if (photoId == 0 && photoUri != null) {
+ contactPhoto.setImageDrawable(
+ getDrawable(R.drawable.product_logo_avatar_anonymous_color_120));
+ } else {
+ ContactPhotoManager.getInstance(this)
+ .loadThumbnail(
+ contactPhoto, photoId, false /* darkTheme */, true /* isCircular */, request);
+ }
+ }
+
+ private void animateContactContainer(int toY) {
+ if (toY == CallComposerFragment.CONTENT_TOP_UNSET
+ || toY == currentLocation
+ || (toY != locations[currentIndex]
+ && locations[currentIndex] != CallComposerFragment.CONTENT_TOP_UNSET)
+ || isAnimatingContactBar
+ || inFullscreenMode) {
+ return;
+ }
+ currentLocation = toY;
+ contactContainer
+ .animate()
+ .translationY(toY)
+ .setInterpolator(interpolator)
+ .setDuration(VIEW_PAGER_ANIMATION_DURATION_MILLIS)
+ .setListener(this)
+ .start();
+ }
+
+ /** Animates compose UI into view */
+ private void runEntranceAnimation() {
+ if (!shouldAnimateEntrance) {
+ return;
+ }
+ shouldAnimateEntrance = false;
+
+ int colorFrom = ContextCompat.getColor(this, android.R.color.transparent);
+ int colorTo = ContextCompat.getColor(this, R.color.call_composer_background_color);
+ ValueAnimator backgroundAnimation =
+ ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
+ backgroundAnimation.setInterpolator(interpolator);
+ backgroundAnimation.setDuration(ENTRANCE_ANIMATION_DURATION_MILLIS); // milliseconds
+ backgroundAnimation.addUpdateListener(
+ new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ background.setBackgroundColor((int) animator.getAnimatedValue());
+ }
+ });
+
+ ValueAnimator contentAnimation = ValueAnimator.ofFloat(windowContainer.getHeight(), 0);
+ contentAnimation.setInterpolator(interpolator);
+ contentAnimation.setDuration(ENTRANCE_ANIMATION_DURATION_MILLIS);
+ contentAnimation.addUpdateListener(
+ new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ windowContainer.setY((Float) animation.getAnimatedValue());
+ }
+ });
+
+ AnimatorSet set = new AnimatorSet();
+ set.play(contentAnimation).with(backgroundAnimation);
+ set.start();
+ }
+
+ /** Animates compose UI out of view and ends the activity. */
+ private void runExitAnimation() {
+ int colorTo = ContextCompat.getColor(this, android.R.color.transparent);
+ int colorFrom = ContextCompat.getColor(this, R.color.call_composer_background_color);
+ ValueAnimator backgroundAnimation =
+ ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
+ backgroundAnimation.setInterpolator(interpolator);
+ backgroundAnimation.setDuration(ENTRANCE_ANIMATION_DURATION_MILLIS); // milliseconds
+ backgroundAnimation.addUpdateListener(
+ new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ background.setBackgroundColor((int) animator.getAnimatedValue());
+ }
+ });
+
+ ValueAnimator contentAnimation = ValueAnimator.ofFloat(0, windowContainer.getHeight());
+ contentAnimation.setInterpolator(interpolator);
+ contentAnimation.setDuration(ENTRANCE_ANIMATION_DURATION_MILLIS);
+ contentAnimation.addUpdateListener(
+ new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ windowContainer.setY((Float) animation.getAnimatedValue());
+ if (animation.getAnimatedFraction() > .75) {
+ finish();
+ }
+ }
+ });
+ AnimatorSet set = new AnimatorSet();
+ set.play(contentAnimation).with(backgroundAnimation);
+ set.start();
+ }
+
+ @Override
+ public void showFullscreen(boolean show) {
+ if (inFullscreenMode == show) {
+ return;
+ }
+ inFullscreenMode = show;
+ toolbar.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
+ contactContainer.setVisibility(show ? View.GONE : View.VISIBLE);
+ ViewGroup.LayoutParams layoutParams = pager.getLayoutParams();
+ if (show) {
+ layoutParams.height = background.getHeight() - toolbar.getHeight() - messageIcon.getHeight();
+ } else {
+ layoutParams.height =
+ getResources().getDimensionPixelSize(R.dimen.call_composer_view_pager_height);
+ }
+ pager.setLayoutParams(layoutParams);
+ }
+
+ @Override
+ public boolean isFullscreen() {
+ return inFullscreenMode;
+ }
+
+ private void animateSendAndCall(final boolean shouldHide) {
+ // createCircularReveal doesn't respect animations being disabled, handle it here.
+ if (ViewUtil.areAnimationsDisabled(this)) {
+ isSendAndCallHidingOrHidden = shouldHide;
+ sendAndCall.setVisibility(shouldHide ? View.INVISIBLE : View.VISIBLE);
+ return;
+ }
+
+ // If the animation is changing directions, start it again. Else do nothing.
+ if (isSendAndCallHidingOrHidden != shouldHide) {
+ int centerX = sendAndCall.getWidth() / 2;
+ int centerY = sendAndCall.getHeight() / 2;
+ int startRadius = shouldHide ? centerX : 0;
+ int endRadius = shouldHide ? 0 : centerX;
+
+ // When the device rotates and state is restored, the send and call button may not be attached
+ // yet and this causes a crash when we attempt to to reveal it. To prevent this, we wait until
+ // {@code sendAndCall} is ready, then animate and reveal it.
+ ViewUtil.doOnPreDraw(
+ sendAndCall,
+ true,
+ new Runnable() {
+ @Override
+ public void run() {
+ Animator animator =
+ ViewAnimationUtils.createCircularReveal(
+ sendAndCall, centerX, centerY, startRadius, endRadius);
+ animator.addListener(
+ new AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ isSendAndCallHidingOrHidden = shouldHide;
+ sendAndCall.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (isSendAndCallHidingOrHidden) {
+ sendAndCall.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {}
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {}
+ });
+ animator.start();
+ }
+ });
+ }
+ }
+
+ private void setMediaIconSelected(int position) {
+ float alpha = 0.54f;
+ cameraIcon.setAlpha(position == CallComposerPagerAdapter.INDEX_CAMERA ? 1 : alpha);
+ galleryIcon.setAlpha(position == CallComposerPagerAdapter.INDEX_GALLERY ? 1 : alpha);
+ messageIcon.setAlpha(position == CallComposerPagerAdapter.INDEX_MESSAGE ? 1 : alpha);
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/CallComposerFragment.java b/java/com/android/dialer/callcomposer/CallComposerFragment.java
new file mode 100644
index 000000000..d6f944955
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/CallComposerFragment.java
@@ -0,0 +1,125 @@
+/*
+ * 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.dialer.callcomposer;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.FragmentUtils;
+import com.android.dialer.common.LogUtil;
+
+/** Base fragment with fields and methods needed for all fragments in the call compose UI. */
+public abstract class CallComposerFragment extends Fragment {
+
+ protected static final int CAMERA_PERMISSION = 1;
+ protected static final int STORAGE_PERMISSION = 2;
+
+ private static final String LOCATION_KEY = "location_key";
+ public static final int CONTENT_TOP_UNSET = Integer.MAX_VALUE;
+
+ private View topView;
+ private int contentTopPx = CONTENT_TOP_UNSET;
+ private CallComposerListener testListener;
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view = super.onCreateView(layoutInflater, viewGroup, bundle);
+ Assert.isNotNull(topView);
+ return view;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ if (!(context instanceof CallComposerListener) && testListener == null) {
+ LogUtil.e(
+ "CallComposerFragment.onAttach",
+ "Container activity must implement CallComposerListener.");
+ Assert.fail();
+ }
+ }
+
+ /** Call this method to declare which view is located at the top of the fragment's layout. */
+ public void setTopView(View view) {
+ topView = view;
+ // For each fragment that extends CallComposerFragment, the heights may vary and since
+ // ViewPagers cannot have their height set to wrap_content, we have to adjust the top of our
+ // container to match the top of the fragment. This listener populates {@code contentTopPx} as
+ // it's available.
+ topView
+ .getViewTreeObserver()
+ .addOnGlobalLayoutListener(
+ new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ topView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ contentTopPx = topView.getTop();
+ }
+ });
+ }
+
+ public int getContentTopPx() {
+ return contentTopPx;
+ }
+
+ public void setParentForTesting(CallComposerListener listener) {
+ testListener = listener;
+ }
+
+ public CallComposerListener getListener() {
+ if (testListener != null) {
+ return testListener;
+ }
+ return FragmentUtils.getParentUnsafe(this, CallComposerListener.class);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(LOCATION_KEY, contentTopPx);
+ }
+
+ @Override
+ public void onViewStateRestored(Bundle savedInstanceState) {
+ super.onViewStateRestored(savedInstanceState);
+ if (savedInstanceState != null) {
+ contentTopPx = savedInstanceState.getInt(LOCATION_KEY);
+ }
+ }
+
+ public abstract boolean shouldHide();
+
+ /** Interface used to listen to CallComposeFragments */
+ public interface CallComposerListener {
+ /** Let the listener know when a call is ready to be composed. */
+ void composeCall(CallComposerFragment fragment);
+
+ /** Let the listener know when the layout has changed to full screen */
+ void showFullscreen(boolean show);
+
+ /** True is the listener is in fullscreen. */
+ boolean isFullscreen();
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/CallComposerPagerAdapter.java b/java/com/android/dialer/callcomposer/CallComposerPagerAdapter.java
new file mode 100644
index 000000000..4d4058a0a
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/CallComposerPagerAdapter.java
@@ -0,0 +1,57 @@
+/*
+ * 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.dialer.callcomposer;
+
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import com.android.dialer.common.Assert;
+
+/** ViewPager adapter for call compose UI. */
+public class CallComposerPagerAdapter extends FragmentStatePagerAdapter {
+
+ public static final int INDEX_CAMERA = 0;
+ public static final int INDEX_GALLERY = 1;
+ public static final int INDEX_MESSAGE = 2;
+
+ private final int messageComposerCharLimit;
+
+ public CallComposerPagerAdapter(FragmentManager fragmentManager, int messageComposerCharLimit) {
+ super(fragmentManager);
+ this.messageComposerCharLimit = messageComposerCharLimit;
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ switch (position) {
+ case INDEX_MESSAGE:
+ return MessageComposerFragment.newInstance(messageComposerCharLimit);
+ case INDEX_GALLERY:
+ return GalleryComposerFragment.newInstance();
+ case INDEX_CAMERA:
+ return new CameraComposerFragment();
+ default:
+ Assert.fail();
+ return null;
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return 3;
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/CameraComposerFragment.java b/java/com/android/dialer/callcomposer/CameraComposerFragment.java
new file mode 100644
index 000000000..f2d0a94a7
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/CameraComposerFragment.java
@@ -0,0 +1,378 @@
+/*
+ * 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.dialer.callcomposer;
+
+import android.Manifest;
+import android.Manifest.permission;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Animatable;
+import android.hardware.Camera.CameraInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.content.ContextCompat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+import com.android.dialer.callcomposer.camera.CameraManager;
+import com.android.dialer.callcomposer.camera.CameraManager.CameraManagerListener;
+import com.android.dialer.callcomposer.camera.CameraManager.MediaCallback;
+import com.android.dialer.callcomposer.camera.CameraPreview.CameraPreviewHost;
+import com.android.dialer.callcomposer.camera.camerafocus.RenderOverlay;
+import com.android.dialer.callcomposer.cameraui.CameraMediaChooserView;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.util.PermissionsUtil;
+
+/** Fragment used to compose call with image from the user's camera. */
+public class CameraComposerFragment extends CallComposerFragment
+ implements CameraManagerListener, OnClickListener, CameraManager.MediaCallback {
+
+ private View permissionView;
+ private ImageButton exitFullscreen;
+ private ImageButton fullscreen;
+ private ImageButton swapCamera;
+ private ImageButton capture;
+ private ImageButton cancel;
+ private CameraMediaChooserView cameraView;
+ private RenderOverlay focus;
+ private View shutter;
+ private View allowPermission;
+ private CameraPreviewHost preview;
+ private ProgressBar loading;
+
+ private Uri cameraUri;
+ private boolean processingUri;
+ private String[] permissions = new String[] {Manifest.permission.CAMERA};
+ private CameraUriCallback uriCallback;
+
+ public static CameraComposerFragment newInstance() {
+ return new CameraComposerFragment();
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle bundle) {
+ View root = inflater.inflate(R.layout.fragment_camera_composer, container, false);
+ permissionView = root.findViewById(R.id.permission_view);
+ loading = (ProgressBar) root.findViewById(R.id.loading);
+ cameraView = (CameraMediaChooserView) root.findViewById(R.id.camera_view);
+ shutter = cameraView.findViewById(R.id.camera_shutter_visual);
+ exitFullscreen = (ImageButton) cameraView.findViewById(R.id.camera_exit_fullscreen);
+ fullscreen = (ImageButton) cameraView.findViewById(R.id.camera_fullscreen);
+ swapCamera = (ImageButton) cameraView.findViewById(R.id.swap_camera_button);
+ capture = (ImageButton) cameraView.findViewById(R.id.camera_capture_button);
+ cancel = (ImageButton) cameraView.findViewById(R.id.camera_cancel_button);
+ focus = (RenderOverlay) cameraView.findViewById(R.id.focus_visual);
+ preview = (CameraPreviewHost) cameraView.findViewById(R.id.camera_preview);
+
+ exitFullscreen.setOnClickListener(this);
+ fullscreen.setOnClickListener(this);
+ swapCamera.setOnClickListener(this);
+ capture.setOnClickListener(this);
+ cancel.setOnClickListener(this);
+
+ if (!PermissionsUtil.hasPermission(getContext(), permission.CAMERA)) {
+ LogUtil.i("CameraComposerFragment.onCreateView", "Permission view shown.");
+ Logger.get(getContext()).logImpression(DialerImpression.Type.CAMERA_PERMISSION_DISPLAYED);
+ ImageView permissionImage = (ImageView) permissionView.findViewById(R.id.permission_icon);
+ TextView permissionText = (TextView) permissionView.findViewById(R.id.permission_text);
+ allowPermission = permissionView.findViewById(R.id.allow);
+
+ allowPermission.setOnClickListener(this);
+ permissionText.setText(R.string.camera_permission_text);
+ permissionImage.setImageResource(R.drawable.quantum_ic_camera_alt_white_48);
+ permissionImage.setColorFilter(
+ ContextCompat.getColor(getContext(), R.color.dialer_theme_color));
+ permissionView.setVisibility(View.VISIBLE);
+ } else {
+ setupCamera();
+ }
+
+ setTopView(cameraView);
+ return root;
+ }
+
+ private void setupCamera() {
+ CameraManager.get().setListener(this);
+ preview.setShown();
+ CameraManager.get().setRenderOverlay(focus);
+ CameraManager.get().selectCamera(CameraInfo.CAMERA_FACING_BACK);
+ setCameraUri(null);
+ }
+
+ @Override
+ public void onCameraError(int errorCode, Exception exception) {
+ LogUtil.e("CameraComposerFragment.onCameraError", "errorCode: ", errorCode, exception);
+ }
+
+ @Override
+ public void onCameraChanged() {
+ updateViewState();
+ }
+
+ @Override
+ public boolean shouldHide() {
+ return !processingUri && cameraUri == null;
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view == capture) {
+ float heightPercent = 1;
+ if (!getListener().isFullscreen()) {
+ heightPercent = Math.min((float) cameraView.getHeight() / preview.getView().getHeight(), 1);
+ }
+
+ showShutterEffect(shutter);
+ processingUri = true;
+ setCameraUri(null);
+ focus.getPieRenderer().clear();
+ CameraManager.get().takePicture(heightPercent, this);
+ } else if (view == swapCamera) {
+ ((Animatable) swapCamera.getDrawable()).start();
+ CameraManager.get().swapCamera();
+ } else if (view == cancel) {
+ processingUri = false;
+ setCameraUri(null);
+ } else if (view == exitFullscreen) {
+ getListener().showFullscreen(false);
+ fullscreen.setVisibility(View.VISIBLE);
+ exitFullscreen.setVisibility(View.GONE);
+ } else if (view == fullscreen) {
+ getListener().showFullscreen(true);
+ fullscreen.setVisibility(View.GONE);
+ exitFullscreen.setVisibility(View.VISIBLE);
+ } else if (view == allowPermission) {
+ // Checks to see if the user has permanently denied this permission. If this is the first
+ // time seeing this permission or they only pressed deny previously, they will see the
+ // permission request. If they permanently denied the permission, they will be sent to Dialer
+ // settings in order enable the permission.
+ if (PermissionsUtil.isFirstRequest(getContext(), permissions[0])
+ || shouldShowRequestPermissionRationale(permissions[0])) {
+ Logger.get(getContext()).logImpression(DialerImpression.Type.CAMERA_PERMISSION_REQUESTED);
+ LogUtil.i("CameraComposerFragment.onClick", "Camera permission requested.");
+ requestPermissions(permissions, CAMERA_PERMISSION);
+ } else {
+ Logger.get(getContext()).logImpression(DialerImpression.Type.CAMERA_PERMISSION_SETTINGS);
+ LogUtil.i("CameraComposerFragment.onClick", "Settings opened to enable permission.");
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setData(Uri.parse("package:" + getContext().getPackageName()));
+ startActivity(intent);
+ }
+ }
+ }
+
+ /**
+ * Called by {@link com.android.dialer.callcomposer.camera.ImagePersistTask} when the image is
+ * finished being cropped and stored on the device.
+ */
+ @Override
+ public void onMediaReady(Uri uri, String contentType, int width, int height) {
+ if (processingUri) {
+ processingUri = false;
+ setCameraUri(uri);
+ // If the user needed the URI before it was ready, uriCallback will be set and we should
+ // send the URI to them ASAP.
+ if (uriCallback != null) {
+ uriCallback.uriReady(uri);
+ uriCallback = null;
+ }
+ } else {
+ updateViewState();
+ }
+ }
+
+ /**
+ * Called by {@link com.android.dialer.callcomposer.camera.ImagePersistTask} when the image failed
+ * to crop or be stored on the device.
+ */
+ @Override
+ public void onMediaFailed(Exception exception) {
+ LogUtil.e("CallComposerFragment.onMediaFailed", null, exception);
+ Toast.makeText(getContext(), R.string.camera_media_failure, Toast.LENGTH_LONG).show();
+ setCameraUri(null);
+ processingUri = false;
+ if (uriCallback != null) {
+ loading.setVisibility(View.GONE);
+ uriCallback = null;
+ }
+ }
+
+ /**
+ * Usually called by {@link CameraManager} if the user does something to interrupt the picture
+ * while it's being taken (like switching the camera).
+ */
+ @Override
+ public void onMediaInfo(int what) {
+ if (what == MediaCallback.MEDIA_NO_DATA) {
+ Toast.makeText(getContext(), R.string.camera_media_failure, Toast.LENGTH_LONG).show();
+ }
+ setCameraUri(null);
+ processingUri = false;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ CameraManager.get().setListener(null);
+ }
+
+ private void showShutterEffect(final View shutterVisual) {
+ float maxAlpha = .7f;
+ int animationDurationMillis = 100;
+
+ AnimationSet animation = new AnimationSet(false /* shareInterpolator */);
+ Animation alphaInAnimation = new AlphaAnimation(0.0f, maxAlpha);
+ alphaInAnimation.setDuration(animationDurationMillis);
+ animation.addAnimation(alphaInAnimation);
+
+ Animation alphaOutAnimation = new AlphaAnimation(maxAlpha, 0.0f);
+ alphaOutAnimation.setStartOffset(animationDurationMillis);
+ alphaOutAnimation.setDuration(animationDurationMillis);
+ animation.addAnimation(alphaOutAnimation);
+
+ animation.setAnimationListener(
+ new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ shutterVisual.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ shutterVisual.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {}
+ });
+ shutterVisual.startAnimation(animation);
+ }
+
+ @NonNull
+ public String getMimeType() {
+ return "image/jpeg";
+ }
+
+ private void setCameraUri(Uri uri) {
+ cameraUri = uri;
+ // It's possible that if the user takes a picture and press back very quickly, the activity will
+ // no longer be alive and when the image cropping process completes, so we need to check that
+ // activity is still alive before trying to invoke it.
+ if (getListener() != null) {
+ updateViewState();
+ getListener().composeCall(this);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (PermissionsUtil.hasCameraPermissions(getContext())) {
+ permissionView.setVisibility(View.GONE);
+ setupCamera();
+ }
+ }
+
+ /** Updates the state of the buttons and overlays based on the current state of the view */
+ private void updateViewState() {
+ Assert.isNotNull(cameraView);
+ Assert.isNotNull(getContext());
+
+ boolean isCameraAvailable = CameraManager.get().isCameraAvailable();
+ boolean uriReadyOrProcessing = cameraUri != null || processingUri;
+
+ if (cameraUri == null && isCameraAvailable) {
+ CameraManager.get().resetPreview();
+ cancel.setVisibility(View.GONE);
+ }
+
+ if (!CameraManager.get().hasFrontAndBackCamera()) {
+ swapCamera.setVisibility(View.GONE);
+ } else {
+ swapCamera.setVisibility(uriReadyOrProcessing ? View.GONE : View.VISIBLE);
+ }
+
+ capture.setVisibility(uriReadyOrProcessing ? View.GONE : View.VISIBLE);
+ cancel.setVisibility(uriReadyOrProcessing ? View.VISIBLE : View.GONE);
+
+ if (uriReadyOrProcessing) {
+ fullscreen.setVisibility(View.GONE);
+ exitFullscreen.setVisibility(View.GONE);
+ } else if (getListener().isFullscreen()) {
+ exitFullscreen.setVisibility(View.VISIBLE);
+ fullscreen.setVisibility(View.GONE);
+ } else {
+ exitFullscreen.setVisibility(View.GONE);
+ fullscreen.setVisibility(View.VISIBLE);
+ }
+
+ swapCamera.setEnabled(isCameraAvailable);
+ capture.setEnabled(isCameraAvailable);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ if (permissions.length > 0 && permissions[0].equals(this.permissions[0])) {
+ PermissionsUtil.permissionRequested(getContext(), permissions[0]);
+ }
+ if (requestCode == CAMERA_PERMISSION
+ && grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ Logger.get(getContext()).logImpression(DialerImpression.Type.CAMERA_PERMISSION_GRANTED);
+ LogUtil.i("CameraComposerFragment.onRequestPermissionsResult", "Permission granted.");
+ permissionView.setVisibility(View.GONE);
+ setupCamera();
+ } else if (requestCode == CAMERA_PERMISSION) {
+ Logger.get(getContext()).logImpression(DialerImpression.Type.CAMERA_PERMISSION_DENIED);
+ LogUtil.i("CameraComposerFragment.onRequestPermissionsResult", "Permission denied.");
+ }
+ }
+
+ public void getCameraUriWhenReady(CameraUriCallback callback) {
+ if (processingUri) {
+ loading.setVisibility(View.VISIBLE);
+ uriCallback = callback;
+ } else {
+ callback.uriReady(cameraUri);
+ }
+ }
+
+ /** Callback to let the caller know when the URI is ready. */
+ public interface CameraUriCallback {
+ void uriReady(Uri uri);
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/GalleryComposerFragment.java b/java/com/android/dialer/callcomposer/GalleryComposerFragment.java
new file mode 100644
index 000000000..623127945
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/GalleryComposerFragment.java
@@ -0,0 +1,256 @@
+/*
+ * 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.dialer.callcomposer;
+
+import static android.app.Activity.RESULT_OK;
+
+import android.Manifest.permission;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.LoaderManager.LoaderCallbacks;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.GridView;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.android.dialer.callcomposer.util.CopyAndResizeImageTask;
+import com.android.dialer.callcomposer.util.CopyAndResizeImageTask.Callback;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.util.PermissionsUtil;
+import java.io.File;
+
+/** Fragment used to compose call with image from the user's gallery. */
+public class GalleryComposerFragment extends CallComposerFragment
+ implements LoaderCallbacks<Cursor>, OnClickListener {
+
+ private static final int RESULT_LOAD_IMAGE = 1;
+ private static final int RESULT_OPEN_SETTINGS = 2;
+
+ private GalleryGridAdapter adapter;
+ private GridView galleryGridView;
+ private View permissionView;
+ private View allowPermission;
+
+ private String[] permissions = new String[] {permission.READ_EXTERNAL_STORAGE};
+ private CursorLoader cursorLoader;
+ private GalleryGridItemData selectedData = null;
+ private boolean selectedDataIsCopy;
+
+ public static GalleryComposerFragment newInstance() {
+ return new GalleryComposerFragment();
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle bundle) {
+ View view = inflater.inflate(R.layout.fragment_gallery_composer, container, false);
+ galleryGridView = (GridView) view.findViewById(R.id.gallery_grid_view);
+ permissionView = view.findViewById(R.id.permission_view);
+
+ if (!PermissionsUtil.hasPermission(getContext(), permission.READ_EXTERNAL_STORAGE)) {
+ Logger.get(getContext()).logImpression(DialerImpression.Type.STORAGE_PERMISSION_DISPLAYED);
+ LogUtil.i("GalleryComposerFragment.onCreateView", "Permission view shown.");
+ ImageView permissionImage = (ImageView) permissionView.findViewById(R.id.permission_icon);
+ TextView permissionText = (TextView) permissionView.findViewById(R.id.permission_text);
+ allowPermission = permissionView.findViewById(R.id.allow);
+
+ allowPermission.setOnClickListener(this);
+ permissionText.setText(R.string.gallery_permission_text);
+ permissionImage.setImageResource(R.drawable.quantum_ic_photo_white_48);
+ permissionImage.setColorFilter(
+ ContextCompat.getColor(getContext(), R.color.dialer_theme_color));
+ permissionView.setVisibility(View.VISIBLE);
+ } else {
+ setupGallery();
+ }
+
+ setTopView(galleryGridView);
+ return view;
+ }
+
+ private void setupGallery() {
+ adapter = new GalleryGridAdapter(getContext(), null, this);
+ galleryGridView.setAdapter(adapter);
+ getLoaderManager().initLoader(0 /* id */, null /* args */, this /* loaderCallbacks */);
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ return cursorLoader = new GalleryCursorLoader(getContext());
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
+ adapter.swapCursor(cursor);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ adapter.swapCursor(null);
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view == allowPermission) {
+ // Checks to see if the user has permanently denied this permission. If this is their first
+ // time seeing this permission or they've only pressed deny previously, they will see the
+ // permission request. If they've permanently denied the permission, they will be sent to
+ // Dialer settings in order to enable the permission.
+ if (PermissionsUtil.isFirstRequest(getContext(), permissions[0])
+ || shouldShowRequestPermissionRationale(permissions[0])) {
+ LogUtil.i("GalleryComposerFragment.onClick", "Storage permission requested.");
+ Logger.get(getContext()).logImpression(DialerImpression.Type.STORAGE_PERMISSION_REQUESTED);
+ requestPermissions(permissions, STORAGE_PERMISSION);
+ } else {
+ LogUtil.i("GalleryComposerFragment.onClick", "Settings opened to enable permission.");
+ Logger.get(getContext()).logImpression(DialerImpression.Type.STORAGE_PERMISSION_SETTINGS);
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ intent.setData(Uri.parse("package:" + getContext().getPackageName()));
+ startActivityForResult(intent, RESULT_OPEN_SETTINGS);
+ }
+ return;
+ } else {
+ GalleryGridItemView itemView = ((GalleryGridItemView) view);
+ if (itemView.isGallery()) {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("image/*");
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, GalleryCursorLoader.ACCEPTABLE_IMAGE_TYPES);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ startActivityForResult(intent, RESULT_LOAD_IMAGE);
+ } else if (itemView.getData().equals(selectedData)) {
+ setSelected(null, false);
+ } else {
+ setSelected(new GalleryGridItemData(itemView.getData()), false);
+ }
+ }
+ }
+
+ @Nullable
+ public GalleryGridItemData getGalleryData() {
+ return selectedData;
+ }
+
+ public GridView getGalleryGridView() {
+ return galleryGridView;
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && data != null) {
+ prepareDataForAttachment(data);
+ } else if (requestCode == RESULT_OPEN_SETTINGS
+ && PermissionsUtil.hasPermission(getContext(), permission.READ_EXTERNAL_STORAGE)) {
+ permissionView.setVisibility(View.GONE);
+ setupGallery();
+ }
+ }
+
+ private void setSelected(GalleryGridItemData data, boolean isCopy) {
+ selectedData = data;
+ selectedDataIsCopy = isCopy;
+ adapter.setSelected(selectedData);
+ getListener().composeCall(this);
+ }
+
+ @Override
+ public boolean shouldHide() {
+ return selectedData == null
+ || selectedData.getFilePath() == null
+ || selectedData.getMimeType() == null;
+ }
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ if (permissions.length > 0 && permissions[0].equals(this.permissions[0])) {
+ PermissionsUtil.permissionRequested(getContext(), permissions[0]);
+ }
+ if (requestCode == STORAGE_PERMISSION
+ && grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ Logger.get(getContext()).logImpression(DialerImpression.Type.STORAGE_PERMISSION_GRANTED);
+ LogUtil.i("GalleryComposerFragment.onRequestPermissionsResult", "Permission granted.");
+ permissionView.setVisibility(View.GONE);
+ setupGallery();
+ } else if (requestCode == STORAGE_PERMISSION) {
+ Logger.get(getContext()).logImpression(DialerImpression.Type.STORAGE_PERMISSION_DENIED);
+ LogUtil.i("GalleryComposerFragment.onRequestPermissionsResult", "Permission denied.");
+ }
+ }
+
+ public CursorLoader getCursorLoader() {
+ return cursorLoader;
+ }
+
+ public boolean selectedDataIsCopy() {
+ return selectedDataIsCopy;
+ }
+
+ private void prepareDataForAttachment(Intent data) {
+ // We're using the builtin photo picker which supplies the return url as it's "data".
+ String url = data.getDataString();
+ if (url == null) {
+ final Bundle extras = data.getExtras();
+ if (extras != null) {
+ final Uri uri = extras.getParcelable(Intent.EXTRA_STREAM);
+ if (uri != null) {
+ url = uri.toString();
+ }
+ }
+ }
+
+ // This should never happen, but just in case..
+ // Guard against null uri cases for when the activity returns a null/invalid intent.
+ if (url != null) {
+ new CopyAndResizeImageTask(
+ getContext(),
+ Uri.parse(url),
+ new Callback() {
+ @Override
+ public void onCopySuccessful(File file, String mimeType) {
+ setSelected(adapter.insertEntry(file.getAbsolutePath(), mimeType), true);
+ }
+
+ @Override
+ public void onCopyFailed(Throwable throwable) {
+ // TODO(b/33753902)
+ LogUtil.e(
+ "GalleryComposerFragment.onFailure", "Data preparation failed", throwable);
+ }
+ })
+ .execute();
+ } else {
+ // TODO(b/33753902)
+ }
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/GalleryCursorLoader.java b/java/com/android/dialer/callcomposer/GalleryCursorLoader.java
new file mode 100644
index 000000000..f9990e167
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/GalleryCursorLoader.java
@@ -0,0 +1,54 @@
+/*
+ * 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.dialer.callcomposer;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.net.Uri;
+import android.provider.MediaStore.Files;
+import android.provider.MediaStore.Files.FileColumns;
+import android.provider.MediaStore.Images.Media;
+import android.support.v4.content.CursorLoader;
+
+/** A BoundCursorLoader that reads local media on the device. */
+public class GalleryCursorLoader extends CursorLoader {
+ public static final String MEDIA_SCANNER_VOLUME_EXTERNAL = "external";
+ public static final String[] ACCEPTABLE_IMAGE_TYPES =
+ new String[] {"image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp"};
+
+ private static final Uri STORAGE_URI = Files.getContentUri(MEDIA_SCANNER_VOLUME_EXTERNAL);
+ private static final String SORT_ORDER = Media.DATE_MODIFIED + " DESC";
+ private static final String IMAGE_SELECTION = createSelection();
+
+ public GalleryCursorLoader(Context context) {
+ super(
+ context,
+ STORAGE_URI,
+ GalleryGridItemData.IMAGE_PROJECTION,
+ IMAGE_SELECTION,
+ null,
+ SORT_ORDER);
+ }
+
+ @SuppressLint("DefaultLocale")
+ private static String createSelection() {
+ return String.format(
+ "mime_type IN ('image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp')"
+ + " AND media_type in (%d)",
+ FileColumns.MEDIA_TYPE_IMAGE);
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/GalleryGridAdapter.java b/java/com/android/dialer/callcomposer/GalleryGridAdapter.java
new file mode 100644
index 000000000..0a7fd769b
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/GalleryGridAdapter.java
@@ -0,0 +1,118 @@
+/*
+ * 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.dialer.callcomposer;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MergeCursor;
+import android.support.annotation.NonNull;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.CursorAdapter;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Bridges between the image cursor loaded by GalleryBoundCursorLoader and the GalleryGridView. */
+public class GalleryGridAdapter extends CursorAdapter {
+
+ @NonNull private final OnClickListener onClickListener;
+ @NonNull private final List<GalleryGridItemView> views = new ArrayList<>();
+ @NonNull private final Context context;
+
+ private GalleryGridItemData selectedData;
+
+ public GalleryGridAdapter(
+ @NonNull Context context, Cursor cursor, @NonNull OnClickListener onClickListener) {
+ super(context, cursor, 0);
+ this.onClickListener = Assert.isNotNull(onClickListener);
+ this.context = Assert.isNotNull(context);
+ }
+
+ @Override
+ public int getCount() {
+ // Add one for the header.
+ return super.getCount() + 1;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // At position 0, we want to insert a header. If position == 0, we don't need the cursor.
+ // If position != 0, then we need to move the cursor to position - 1 to account for the offset
+ // of the header.
+ if (position != 0 && !getCursor().moveToPosition(position - 1)) {
+ Assert.fail("couldn't move cursor to position " + (position - 1));
+ }
+ View view;
+ if (convertView == null) {
+ view = newView(context, getCursor(), parent);
+ } else {
+ view = convertView;
+ }
+ bindView(view, context, getCursor(), position);
+ return view;
+ }
+
+ private void bindView(View view, Context context, Cursor cursor, int position) {
+ if (position == 0) {
+ GalleryGridItemView gridView = (GalleryGridItemView) view;
+ gridView.showGallery(true);
+ } else {
+ bindView(view, context, cursor);
+ }
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ GalleryGridItemView gridView = (GalleryGridItemView) view;
+ gridView.bind(cursor);
+ gridView.setSelected(gridView.getData().equals(selectedData));
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ GalleryGridItemView view =
+ (GalleryGridItemView)
+ LayoutInflater.from(context).inflate(R.layout.gallery_grid_item_view, parent, false);
+ view.setOnClickListener(onClickListener);
+ views.add(view);
+ return view;
+ }
+
+ public void setSelected(GalleryGridItemData selectedData) {
+ this.selectedData = selectedData;
+ for (GalleryGridItemView view : views) {
+ view.setSelected(view.getData().equals(selectedData));
+ }
+ }
+
+ public GalleryGridItemData insertEntry(String filePath, String mimeType) {
+ LogUtil.i("GalleryGridAdapter.insertRow", mimeType + " " + filePath);
+
+ MatrixCursor extraRow = new MatrixCursor(GalleryGridItemData.IMAGE_PROJECTION);
+ extraRow.addRow(new Object[] {0L, filePath, mimeType, ""});
+ extraRow.moveToFirst();
+ Cursor extendedCursor = new MergeCursor(new Cursor[] {extraRow, getCursor()});
+ swapCursor(extendedCursor);
+
+ return new GalleryGridItemData(extraRow);
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/GalleryGridItemData.java b/java/com/android/dialer/callcomposer/GalleryGridItemData.java
new file mode 100644
index 000000000..402c6ce6d
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/GalleryGridItemData.java
@@ -0,0 +1,91 @@
+/*
+ * 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.dialer.callcomposer;
+
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.MediaStore.Images.Media;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import com.android.dialer.common.Assert;
+import java.io.File;
+import java.util.Objects;
+
+/** Provides data for GalleryGridItemView */
+public final class GalleryGridItemData {
+ public static final String[] IMAGE_PROJECTION =
+ new String[] {Media._ID, Media.DATA, Media.MIME_TYPE, Media.DATE_MODIFIED};
+
+ private static final int INDEX_DATA_PATH = 1;
+ private static final int INDEX_MIME_TYPE = 2;
+ private static final int INDEX_DATE_MODIFIED = 3;
+
+ private String filePath;
+ private String mimeType;
+ private long dateModifiedSeconds;
+
+ public GalleryGridItemData() {}
+
+ public GalleryGridItemData(GalleryGridItemData copyData) {
+ filePath = Assert.isNotNull(copyData.getFilePath());
+ mimeType = Assert.isNotNull(copyData.getMimeType());
+ dateModifiedSeconds = Assert.isNotNull(copyData.getDateModifiedSeconds());
+ }
+
+ public GalleryGridItemData(Cursor cursor) {
+ bind(cursor);
+ }
+
+ public void bind(Cursor cursor) {
+ mimeType = Assert.isNotNull(cursor.getString(INDEX_MIME_TYPE));
+ String dateModified = Assert.isNotNull(cursor.getString(INDEX_DATE_MODIFIED));
+ dateModifiedSeconds = !TextUtils.isEmpty(dateModified) ? Long.parseLong(dateModified) : -1;
+ filePath = Assert.isNotNull(cursor.getString(INDEX_DATA_PATH));
+ }
+
+ @Nullable
+ public String getFilePath() {
+ return filePath;
+ }
+
+ @Nullable
+ public Uri getFileUri() {
+ return TextUtils.isEmpty(filePath) ? null : Uri.fromFile(new File(filePath));
+ }
+
+ /** @return The date in seconds. This can be negative if we could not retrieve date info */
+ public long getDateModifiedSeconds() {
+ return dateModifiedSeconds;
+ }
+
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof GalleryGridItemData
+ && Objects.equals(mimeType, ((GalleryGridItemData) obj).mimeType)
+ && Objects.equals(filePath, ((GalleryGridItemData) obj).filePath)
+ && ((GalleryGridItemData) obj).dateModifiedSeconds == dateModifiedSeconds;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(filePath, mimeType, dateModifiedSeconds);
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/GalleryGridItemView.java b/java/com/android/dialer/callcomposer/GalleryGridItemView.java
new file mode 100644
index 000000000..d70fd57c1
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/GalleryGridItemView.java
@@ -0,0 +1,126 @@
+/*
+ * 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.dialer.callcomposer;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy;
+import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
+import com.bumptech.glide.request.RequestOptions;
+import java.util.concurrent.TimeUnit;
+
+/** Shows an item in the gallery picker grid view. Hosts an FileImageView with a checkbox. */
+public class GalleryGridItemView extends FrameLayout {
+
+ private final GalleryGridItemData data = new GalleryGridItemData();
+
+ private ImageView image;
+ private View checkbox;
+ private View gallery;
+ private String currentFilePath;
+ private boolean isGallery;
+
+ public GalleryGridItemView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ image = (ImageView) findViewById(R.id.image);
+ checkbox = findViewById(R.id.checkbox);
+ gallery = findViewById(R.id.gallery);
+
+ image.setClipToOutline(true);
+ checkbox.setClipToOutline(true);
+ gallery.setClipToOutline(true);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // The grid view auto-fit the columns, so we want to let the height match the width
+ // to make the image square.
+ super.onMeasure(widthMeasureSpec, widthMeasureSpec);
+ }
+
+ public GalleryGridItemData getData() {
+ return data;
+ }
+
+ @Override
+ public void setSelected(boolean selected) {
+ if (selected) {
+ checkbox.setVisibility(VISIBLE);
+ int paddingPx = getResources().getDimensionPixelSize(R.dimen.gallery_item_selected_padding);
+ setPadding(paddingPx, paddingPx, paddingPx, paddingPx);
+ } else {
+ checkbox.setVisibility(GONE);
+ int paddingPx = getResources().getDimensionPixelOffset(R.dimen.gallery_item_padding);
+ setPadding(paddingPx, paddingPx, paddingPx, paddingPx);
+ }
+ }
+
+ public boolean isGallery() {
+ return isGallery;
+ }
+
+ public void showGallery(boolean show) {
+ isGallery = show;
+ gallery.setVisibility(show ? VISIBLE : INVISIBLE);
+ }
+
+ public void bind(Cursor cursor) {
+ data.bind(cursor);
+ showGallery(false);
+ updateImageView();
+ }
+
+ private void updateImageView() {
+ image.setScaleType(ScaleType.CENTER_CROP);
+
+ if (currentFilePath == null || !currentFilePath.equals(data.getFilePath())) {
+ currentFilePath = data.getFilePath();
+
+ // Downloads/loads an image from the given URI so that the image's largest dimension is
+ // between 1/2 the given dimensions and the given dimensions, with no restrictions on the
+ // image's smallest dimension. We skip the memory cache, but glide still applies it's disk
+ // cache to optimize loads.
+ Glide.with(getContext())
+ .load(data.getFileUri())
+ .apply(RequestOptions.downsampleOf(DownsampleStrategy.AT_MOST).skipMemoryCache(true))
+ .transition(DrawableTransitionOptions.withCrossFade())
+ .into(image);
+ }
+ long dateModifiedSeconds = data.getDateModifiedSeconds();
+ if (dateModifiedSeconds > 0) {
+ image.setContentDescription(
+ getResources()
+ .getString(
+ R.string.gallery_item_description,
+ TimeUnit.SECONDS.toMillis(dateModifiedSeconds)));
+ } else {
+ image.setContentDescription(
+ getResources().getString(R.string.gallery_item_description_no_date));
+ }
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/MessageComposerFragment.java b/java/com/android/dialer/callcomposer/MessageComposerFragment.java
new file mode 100644
index 000000000..521b71402
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/MessageComposerFragment.java
@@ -0,0 +1,143 @@
+/*
+ * 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.dialer.callcomposer;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.TextView;
+
+/** Fragment used to compose call with message fragment. */
+public class MessageComposerFragment extends CallComposerFragment
+ implements OnClickListener, TextWatcher, OnTouchListener, OnLongClickListener {
+ private static final String CHAR_LIMIT_KEY = "char_limit";
+
+ public static final int NO_CHAR_LIMIT = -1;
+
+ private EditText customMessage;
+ private boolean isLongClick = false;
+ private int charLimit;
+
+ public static MessageComposerFragment newInstance(int charLimit) {
+ MessageComposerFragment fragment = new MessageComposerFragment();
+ Bundle args = new Bundle();
+ args.putInt(CHAR_LIMIT_KEY, charLimit);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Nullable
+ public String getMessage() {
+ return customMessage == null ? null : customMessage.getText().toString();
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ charLimit = getArguments().getInt(CHAR_LIMIT_KEY, NO_CHAR_LIMIT);
+
+ View view = inflater.inflate(R.layout.fragment_message_composer, container, false);
+ TextView urgent = (TextView) view.findViewById(R.id.message_urgent);
+ customMessage = (EditText) view.findViewById(R.id.custom_message);
+
+ urgent.setOnClickListener(this);
+ customMessage.setOnTouchListener(this);
+ customMessage.setOnLongClickListener(this);
+ customMessage.addTextChangedListener(this);
+ if (charLimit != NO_CHAR_LIMIT) {
+ TextView remainingChar = (TextView) view.findViewById(R.id.remaining_characters);
+ remainingChar.setText("" + charLimit);
+ customMessage.setFilters(new InputFilter[] {new InputFilter.LengthFilter(charLimit)});
+ customMessage.addTextChangedListener(
+ new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ remainingChar.setText("" + (charLimit - editable.length()));
+ }
+ });
+ }
+ view.findViewById(R.id.message_chat).setOnClickListener(this);
+ view.findViewById(R.id.message_question).setOnClickListener(this);
+
+ setTopView(urgent);
+ return view;
+ }
+
+ @Override
+ public void onClick(View view) {
+ customMessage.setText(((TextView) view).getText());
+ customMessage.setSelection(customMessage.getText().length());
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {}
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ getListener().composeCall(this);
+ }
+
+ /**
+ * EditTexts take two clicks to dispatch an onClick() event, so instead we add an onTouchListener
+ * to listen for them. The caveat to this is that it also requires listening for onLongClicks to
+ * distinguish whether a MotionEvent came from a click or a long click.
+ */
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ if (isLongClick) {
+ isLongClick = false;
+ } else {
+ getListener().showFullscreen(true);
+ }
+ }
+ view.performClick();
+ return false;
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ isLongClick = true;
+ return false;
+ }
+
+ @Override
+ public boolean shouldHide() {
+ return TextUtils.isEmpty(getMessage());
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/AndroidManifest.xml b/java/com/android/dialer/callcomposer/camera/AndroidManifest.xml
new file mode 100644
index 000000000..82f141284
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<!--
+ ~ 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
+ -->
+<manifest package="com.android.dialer.callcomposer.camera"/> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/camera/CameraManager.java b/java/com/android/dialer/callcomposer/camera/CameraManager.java
new file mode 100644
index 000000000..87cd16a99
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/CameraManager.java
@@ -0,0 +1,822 @@
+/*
+ * 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.dialer.callcomposer.camera;
+
+import android.content.Context;
+import android.hardware.Camera;
+import android.hardware.Camera.CameraInfo;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Looper;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.view.MotionEvent;
+import android.view.OrientationEventListener;
+import android.view.Surface;
+import android.view.View;
+import android.view.WindowManager;
+import com.android.dialer.callcomposer.camera.camerafocus.FocusOverlayManager;
+import com.android.dialer.callcomposer.camera.camerafocus.RenderOverlay;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Class which manages interactions with the camera, but does not do any UI. This class is designed
+ * to be a singleton to ensure there is one component managing the camera and releasing the native
+ * resources. In order to acquire a camera, a caller must:
+ *
+ * <ul>
+ * <li>Call selectCamera to select front or back camera
+ * <li>Call setSurface to control where the preview is shown
+ * <li>Call openCamera to request the camera start preview
+ * </ul>
+ *
+ * Callers should call onPause and onResume to ensure that the camera is release while the activity
+ * is not active. This class is not thread safe. It should only be called from one thread (the UI
+ * thread or test thread)
+ */
+public class CameraManager implements FocusOverlayManager.Listener {
+ /** Callbacks for the camera manager listener */
+ public interface CameraManagerListener {
+ void onCameraError(int errorCode, Exception e);
+
+ void onCameraChanged();
+ }
+
+ /** Callback when taking image or video */
+ public interface MediaCallback {
+ int MEDIA_CAMERA_CHANGED = 1;
+ int MEDIA_NO_DATA = 2;
+
+ void onMediaReady(Uri uriToMedia, String contentType, int width, int height);
+
+ void onMediaFailed(Exception exception);
+
+ void onMediaInfo(int what);
+ }
+
+ // Error codes
+ private static final int ERROR_OPENING_CAMERA = 1;
+ private static final int ERROR_SHOWING_PREVIEW = 2;
+ private static final int ERROR_HARDWARE_ACCELERATION_DISABLED = 3;
+ private static final int ERROR_TAKING_PICTURE = 4;
+
+ private static final int NO_CAMERA_SELECTED = -1;
+
+ private static final Camera.ShutterCallback DUMMY_SHUTTER_CALLBACK =
+ new Camera.ShutterCallback() {
+ @Override
+ public void onShutter() {
+ // Do nothing
+ }
+ };
+
+ private static CameraManager sInstance;
+
+ /** The CameraInfo for the currently selected camera */
+ private final CameraInfo mCameraInfo;
+
+ /** The index of the selected camera or NO_CAMERA_SELECTED if a camera hasn't been selected yet */
+ private int mCameraIndex;
+
+ /** True if the device has front and back cameras */
+ private final boolean mHasFrontAndBackCamera;
+
+ /** True if the camera should be open (may not yet be actually open) */
+ private boolean mOpenRequested;
+
+ /** The preview view to show the preview on */
+ private CameraPreview mCameraPreview;
+
+ /** The helper classs to handle orientation changes */
+ private OrientationHandler mOrientationHandler;
+
+ /** Tracks whether the preview has hardware acceleration */
+ private boolean mIsHardwareAccelerationSupported;
+
+ /**
+ * The task for opening the camera, so it doesn't block the UI thread Using AsyncTask rather than
+ * SafeAsyncTask because the tasks need to be serialized, but don't need to be on the UI thread
+ * TODO: If we have other AyncTasks (not SafeAsyncTasks) this may contend and we may need
+ * to create a dedicated thread, or synchronize the threads in the thread pool
+ */
+ private AsyncTask<Integer, Void, Camera> mOpenCameraTask;
+
+ /**
+ * The camera index that is queued to be opened, but not completed yet, or NO_CAMERA_SELECTED if
+ * no open task is pending
+ */
+ private int mPendingOpenCameraIndex = NO_CAMERA_SELECTED;
+
+ /** The instance of the currently opened camera */
+ private Camera mCamera;
+
+ /** The rotation of the screen relative to the camera's natural orientation */
+ private int mRotation;
+
+ /** The callback to notify when errors or other events occur */
+ private CameraManagerListener mListener;
+
+ /** True if the camera is currently in the process of taking an image */
+ private boolean mTakingPicture;
+
+ /** Manages auto focus visual and behavior */
+ private final FocusOverlayManager mFocusOverlayManager;
+
+ private CameraManager() {
+ mCameraInfo = new CameraInfo();
+ mCameraIndex = NO_CAMERA_SELECTED;
+
+ // Check to see if a front and back camera exist
+ boolean hasFrontCamera = false;
+ boolean hasBackCamera = false;
+ final CameraInfo cameraInfo = new CameraInfo();
+ final int cameraCount = Camera.getNumberOfCameras();
+ try {
+ for (int i = 0; i < cameraCount; i++) {
+ Camera.getCameraInfo(i, cameraInfo);
+ if (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) {
+ hasFrontCamera = true;
+ } else if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
+ hasBackCamera = true;
+ }
+ if (hasFrontCamera && hasBackCamera) {
+ break;
+ }
+ }
+ } catch (final RuntimeException e) {
+ LogUtil.e("CameraManager.CameraManager", "Unable to load camera info", e);
+ }
+ mHasFrontAndBackCamera = hasFrontCamera && hasBackCamera;
+ mFocusOverlayManager = new FocusOverlayManager(this, Looper.getMainLooper());
+
+ // Assume the best until we are proven otherwise
+ mIsHardwareAccelerationSupported = true;
+ }
+
+ /** Gets the singleton instance */
+ public static CameraManager get() {
+ if (sInstance == null) {
+ sInstance = new CameraManager();
+ }
+ return sInstance;
+ }
+
+ /**
+ * Sets the surface to use to display the preview This must only be called AFTER the CameraPreview
+ * has a texture ready
+ *
+ * @param preview The preview surface view
+ */
+ void setSurface(final CameraPreview preview) {
+ if (preview == mCameraPreview) {
+ return;
+ }
+
+ if (preview != null) {
+ Assert.checkArgument(preview.isValid());
+ preview.setOnTouchListener(
+ new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(final View view, final MotionEvent motionEvent) {
+ if ((motionEvent.getActionMasked() & MotionEvent.ACTION_UP)
+ == MotionEvent.ACTION_UP) {
+ mFocusOverlayManager.setPreviewSize(view.getWidth(), view.getHeight());
+ mFocusOverlayManager.onSingleTapUp(
+ (int) motionEvent.getX() + view.getLeft(),
+ (int) motionEvent.getY() + view.getTop());
+ }
+ view.performClick();
+ return true;
+ }
+ });
+ }
+ mCameraPreview = preview;
+ tryShowPreview();
+ }
+
+ public void setRenderOverlay(final RenderOverlay renderOverlay) {
+ mFocusOverlayManager.setFocusRenderer(
+ renderOverlay != null ? renderOverlay.getPieRenderer() : null);
+ }
+
+ /** Convenience function to swap between front and back facing cameras */
+ public void swapCamera() {
+ Assert.checkState(mCameraIndex >= 0);
+ selectCamera(
+ mCameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT
+ ? CameraInfo.CAMERA_FACING_BACK
+ : CameraInfo.CAMERA_FACING_FRONT);
+ }
+
+ /**
+ * Selects the first camera facing the desired direction, or the first camera if there is no
+ * camera in the desired direction
+ *
+ * @param desiredFacing One of the CameraInfo.CAMERA_FACING_* constants
+ * @return True if a camera was selected, or false if selecting a camera failed
+ */
+ public boolean selectCamera(final int desiredFacing) {
+ try {
+ // We already selected a camera facing that direction
+ if (mCameraIndex >= 0 && mCameraInfo.facing == desiredFacing) {
+ return true;
+ }
+
+ final int cameraCount = Camera.getNumberOfCameras();
+ Assert.checkState(cameraCount > 0);
+
+ mCameraIndex = NO_CAMERA_SELECTED;
+ setCamera(null);
+ final CameraInfo cameraInfo = new CameraInfo();
+ for (int i = 0; i < cameraCount; i++) {
+ Camera.getCameraInfo(i, cameraInfo);
+ if (cameraInfo.facing == desiredFacing) {
+ mCameraIndex = i;
+ Camera.getCameraInfo(i, mCameraInfo);
+ break;
+ }
+ }
+
+ // There's no camera in the desired facing direction, just select the first camera
+ // regardless of direction
+ if (mCameraIndex < 0) {
+ mCameraIndex = 0;
+ Camera.getCameraInfo(0, mCameraInfo);
+ }
+
+ if (mOpenRequested) {
+ // The camera is open, so reopen with the newly selected camera
+ openCamera();
+ }
+ return true;
+ } catch (final RuntimeException e) {
+ LogUtil.e("CameraManager.selectCamera", "RuntimeException in CameraManager.selectCamera", e);
+ if (mListener != null) {
+ mListener.onCameraError(ERROR_OPENING_CAMERA, e);
+ }
+ return false;
+ }
+ }
+
+ public int getCameraIndex() {
+ return mCameraIndex;
+ }
+
+ public void selectCameraByIndex(final int cameraIndex) {
+ if (mCameraIndex == cameraIndex) {
+ return;
+ }
+
+ try {
+ mCameraIndex = cameraIndex;
+ Camera.getCameraInfo(mCameraIndex, mCameraInfo);
+ if (mOpenRequested) {
+ openCamera();
+ }
+ } catch (final RuntimeException e) {
+ LogUtil.e(
+ "CameraManager.selectCameraByIndex",
+ "RuntimeException in CameraManager.selectCameraByIndex",
+ e);
+ if (mListener != null) {
+ mListener.onCameraError(ERROR_OPENING_CAMERA, e);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ public CameraInfo getCameraInfo() {
+ if (mCameraIndex == NO_CAMERA_SELECTED) {
+ return null;
+ }
+ return mCameraInfo;
+ }
+
+ /** @return True if the device has both a front and back camera */
+ public boolean hasFrontAndBackCamera() {
+ return mHasFrontAndBackCamera;
+ }
+
+ /** Opens the camera on a separate thread and initiates the preview if one is available */
+ void openCamera() {
+ if (mCameraIndex == NO_CAMERA_SELECTED) {
+ // Ensure a selected camera if none is currently selected. This may happen if the
+ // camera chooser is not the default media chooser.
+ selectCamera(CameraInfo.CAMERA_FACING_BACK);
+ }
+ mOpenRequested = true;
+ // We're already opening the camera or already have the camera handle, nothing more to do
+ if (mPendingOpenCameraIndex == mCameraIndex || mCamera != null) {
+ return;
+ }
+
+ // True if the task to open the camera has to be delayed until the current one completes
+ boolean delayTask = false;
+
+ // Cancel any previous open camera tasks
+ if (mOpenCameraTask != null) {
+ mPendingOpenCameraIndex = NO_CAMERA_SELECTED;
+ delayTask = true;
+ }
+
+ mPendingOpenCameraIndex = mCameraIndex;
+ mOpenCameraTask =
+ new AsyncTask<Integer, Void, Camera>() {
+ private Exception mException;
+
+ @Override
+ protected Camera doInBackground(final Integer... params) {
+ try {
+ final int cameraIndex = params[0];
+ LogUtil.v("CameraManager.doInBackground", "Opening camera " + mCameraIndex);
+ return Camera.open(cameraIndex);
+ } catch (final Exception e) {
+ LogUtil.e("CameraManager.doInBackground", "Exception while opening camera", e);
+ mException = e;
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(final Camera camera) {
+ // If we completed, but no longer want this camera, then release the camera
+ if (mOpenCameraTask != this || !mOpenRequested) {
+ releaseCamera(camera);
+ cleanup();
+ return;
+ }
+
+ cleanup();
+
+ LogUtil.v(
+ "CameraManager.onPostExecute",
+ "Opened camera " + mCameraIndex + " " + (camera != null));
+ setCamera(camera);
+ if (camera == null) {
+ if (mListener != null) {
+ mListener.onCameraError(ERROR_OPENING_CAMERA, mException);
+ }
+ LogUtil.e("CameraManager.onPostExecute", "Error opening camera");
+ }
+ }
+
+ @Override
+ protected void onCancelled() {
+ super.onCancelled();
+ cleanup();
+ }
+
+ private void cleanup() {
+ mPendingOpenCameraIndex = NO_CAMERA_SELECTED;
+ if (mOpenCameraTask != null && mOpenCameraTask.getStatus() == Status.PENDING) {
+ // If there's another task waiting on this one to complete, start it now
+ mOpenCameraTask.execute(mCameraIndex);
+ } else {
+ mOpenCameraTask = null;
+ }
+ }
+ };
+ LogUtil.v("CameraManager.openCamera", "Start opening camera " + mCameraIndex);
+ if (!delayTask) {
+ mOpenCameraTask.execute(mCameraIndex);
+ }
+ }
+
+ /** Closes the camera releasing the resources it uses */
+ void closeCamera() {
+ mOpenRequested = false;
+ setCamera(null);
+ }
+
+ /**
+ * Sets the listener which will be notified of errors or other events in the camera
+ *
+ * @param listener The listener to notify
+ */
+ public void setListener(final CameraManagerListener listener) {
+ Assert.isMainThread();
+ mListener = listener;
+ if (!mIsHardwareAccelerationSupported && mListener != null) {
+ mListener.onCameraError(ERROR_HARDWARE_ACCELERATION_DISABLED, null);
+ }
+ }
+
+ public void takePicture(final float heightPercent, @NonNull final MediaCallback callback) {
+ Assert.checkState(!mTakingPicture);
+ Assert.isNotNull(callback);
+ mCameraPreview.setFocusable(false);
+ mFocusOverlayManager.cancelAutoFocus();
+ if (mCamera == null) {
+ // The caller should have checked isCameraAvailable first, but just in case, protect
+ // against a null camera by notifying the callback that taking the picture didn't work
+ callback.onMediaFailed(null);
+ return;
+ }
+ final Camera.PictureCallback jpegCallback =
+ new Camera.PictureCallback() {
+ @Override
+ public void onPictureTaken(final byte[] bytes, final Camera camera) {
+ mTakingPicture = false;
+ if (mCamera != camera) {
+ // This may happen if the camera was changed between front/back while the
+ // picture is being taken.
+ callback.onMediaInfo(MediaCallback.MEDIA_CAMERA_CHANGED);
+ return;
+ }
+
+ if (bytes == null) {
+ callback.onMediaInfo(MediaCallback.MEDIA_NO_DATA);
+ return;
+ }
+
+ final Camera.Size size = camera.getParameters().getPictureSize();
+ int width;
+ int height;
+ if (mRotation == 90 || mRotation == 270) {
+ // Is rotated, so swapping dimensions is desired
+ //noinspection SuspiciousNameCombination
+ width = size.height;
+ //noinspection SuspiciousNameCombination
+ height = size.width;
+ } else {
+ width = size.width;
+ height = size.height;
+ }
+ LogUtil.i(
+ "CameraManager.onPictureTaken", "taken picture size: " + bytes.length + " bytes");
+ new ImagePersistTask(
+ width, height, heightPercent, bytes, mCameraPreview.getContext(), callback)
+ .execute();
+ }
+ };
+
+ mTakingPicture = true;
+ try {
+ mCamera.takePicture(
+ // A shutter callback is required to enable shutter sound
+ DUMMY_SHUTTER_CALLBACK, null /* raw */, null /* postView */, jpegCallback);
+ } catch (final RuntimeException e) {
+ LogUtil.e("CameraManager.takePicture", "RuntimeException in CameraManager.takePicture", e);
+ mTakingPicture = false;
+ if (mListener != null) {
+ mListener.onCameraError(ERROR_TAKING_PICTURE, e);
+ }
+ }
+ }
+
+ /**
+ * Asynchronously releases a camera
+ *
+ * @param camera The camera to release
+ */
+ private void releaseCamera(final Camera camera) {
+ if (camera == null) {
+ return;
+ }
+
+ mFocusOverlayManager.onCameraReleased();
+
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(final Void... params) {
+ LogUtil.v("CameraManager.doInBackground", "Releasing camera " + mCameraIndex);
+ camera.release();
+ return null;
+ }
+ }.execute();
+ }
+
+ /** Updates the orientation of the camera to match the orientation of the device */
+ private void updateCameraOrientation() {
+ if (mCamera == null || mCameraPreview == null || mTakingPicture) {
+ return;
+ }
+
+ final WindowManager windowManager =
+ (WindowManager) mCameraPreview.getContext().getSystemService(Context.WINDOW_SERVICE);
+
+ int degrees = 0;
+ switch (windowManager.getDefaultDisplay().getRotation()) {
+ case Surface.ROTATION_0:
+ degrees = 0;
+ break;
+ case Surface.ROTATION_90:
+ degrees = 90;
+ break;
+ case Surface.ROTATION_180:
+ degrees = 180;
+ break;
+ case Surface.ROTATION_270:
+ degrees = 270;
+ break;
+ }
+
+ // The display orientation of the camera (this controls the preview image).
+ int orientation;
+
+ // The clockwise rotation angle relative to the orientation of the camera. This affects
+ // pictures returned by the camera in Camera.PictureCallback.
+ int rotation;
+ if (mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+ orientation = (mCameraInfo.orientation + degrees) % 360;
+ rotation = orientation;
+ // compensate the mirror but only for orientation
+ orientation = (360 - orientation) % 360;
+ } else { // back-facing
+ orientation = (mCameraInfo.orientation - degrees + 360) % 360;
+ rotation = orientation;
+ }
+ mRotation = rotation;
+ try {
+ mCamera.setDisplayOrientation(orientation);
+ final Camera.Parameters params = mCamera.getParameters();
+ params.setRotation(rotation);
+ mCamera.setParameters(params);
+ } catch (final RuntimeException e) {
+ LogUtil.e(
+ "CameraManager.updateCameraOrientation",
+ "RuntimeException in CameraManager.updateCameraOrientation",
+ e);
+ if (mListener != null) {
+ mListener.onCameraError(ERROR_OPENING_CAMERA, e);
+ }
+ }
+ }
+
+ /** Sets the current camera, releasing any previously opened camera */
+ private void setCamera(final Camera camera) {
+ if (mCamera == camera) {
+ return;
+ }
+
+ releaseCamera(mCamera);
+ mCamera = camera;
+ tryShowPreview();
+ if (mListener != null) {
+ mListener.onCameraChanged();
+ }
+ }
+
+ /** Shows the preview if the camera is open and the preview is loaded */
+ private void tryShowPreview() {
+ if (mCameraPreview == null || mCamera == null) {
+ if (mOrientationHandler != null) {
+ mOrientationHandler.disable();
+ mOrientationHandler = null;
+ }
+ // releaseMediaRecorder(true /* cleanupFile */);
+ mFocusOverlayManager.onPreviewStopped();
+ return;
+ }
+ try {
+ mCamera.stopPreview();
+ updateCameraOrientation();
+
+ final Camera.Parameters params = mCamera.getParameters();
+ final Camera.Size pictureSize = chooseBestPictureSize();
+ final Camera.Size previewSize = chooseBestPreviewSize(pictureSize);
+ params.setPreviewSize(previewSize.width, previewSize.height);
+ params.setPictureSize(pictureSize.width, pictureSize.height);
+ logCameraSize("Setting preview size: ", previewSize);
+ logCameraSize("Setting picture size: ", pictureSize);
+ mCameraPreview.setSize(previewSize, mCameraInfo.orientation);
+ for (final String focusMode : params.getSupportedFocusModes()) {
+ if (TextUtils.equals(focusMode, Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
+ // Use continuous focus if available
+ params.setFocusMode(focusMode);
+ break;
+ }
+ }
+
+ mCamera.setParameters(params);
+ mCameraPreview.startPreview(mCamera);
+ mCamera.startPreview();
+ mCamera.setAutoFocusMoveCallback(
+ new Camera.AutoFocusMoveCallback() {
+ @Override
+ public void onAutoFocusMoving(final boolean start, final Camera camera) {
+ mFocusOverlayManager.onAutoFocusMoving(start);
+ }
+ });
+ mFocusOverlayManager.setParameters(mCamera.getParameters());
+ mFocusOverlayManager.setMirror(mCameraInfo.facing == CameraInfo.CAMERA_FACING_BACK);
+ mFocusOverlayManager.onPreviewStarted();
+ if (mOrientationHandler == null) {
+ mOrientationHandler = new OrientationHandler(mCameraPreview.getContext());
+ mOrientationHandler.enable();
+ }
+ } catch (final IOException e) {
+ LogUtil.e("CameraManager.tryShowPreview", "IOException in CameraManager.tryShowPreview", e);
+ if (mListener != null) {
+ mListener.onCameraError(ERROR_SHOWING_PREVIEW, e);
+ }
+ } catch (final RuntimeException e) {
+ LogUtil.e(
+ "CameraManager.tryShowPreview", "RuntimeException in CameraManager.tryShowPreview", e);
+ if (mListener != null) {
+ mListener.onCameraError(ERROR_SHOWING_PREVIEW, e);
+ }
+ }
+ }
+
+ public boolean isCameraAvailable() {
+ return mCamera != null && !mTakingPicture && mIsHardwareAccelerationSupported;
+ }
+
+ /**
+ * Choose the best picture size by trying to find a size close to the MmsConfig's max size, which
+ * is closest to the screen aspect ratio. In case of RCS conversation returns default size.
+ */
+ private Camera.Size chooseBestPictureSize() {
+ return mCamera.getParameters().getPictureSize();
+ }
+
+ /**
+ * Chose the best preview size based on the picture size. Try to find a size with the same aspect
+ * ratio and size as the picture if possible
+ */
+ private Camera.Size chooseBestPreviewSize(final Camera.Size pictureSize) {
+ final List<Camera.Size> sizes =
+ new ArrayList<Camera.Size>(mCamera.getParameters().getSupportedPreviewSizes());
+ final float aspectRatio = pictureSize.width / (float) pictureSize.height;
+ final int capturePixels = pictureSize.width * pictureSize.height;
+
+ // Sort the sizes so the best size is first
+ Collections.sort(
+ sizes,
+ new SizeComparator(Integer.MAX_VALUE, Integer.MAX_VALUE, aspectRatio, capturePixels));
+
+ return sizes.get(0);
+ }
+
+ private class OrientationHandler extends OrientationEventListener {
+ OrientationHandler(final Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onOrientationChanged(final int orientation) {
+ updateCameraOrientation();
+ }
+ }
+
+ private static class SizeComparator implements Comparator<Camera.Size> {
+ private static final int PREFER_LEFT = -1;
+ private static final int PREFER_RIGHT = 1;
+
+ // The max width/height for the preferred size. Integer.MAX_VALUE if no size limit
+ private final int mMaxWidth;
+ private final int mMaxHeight;
+
+ // The desired aspect ratio
+ private final float mTargetAspectRatio;
+
+ // The desired size (width x height) to try to match
+ private final int mTargetPixels;
+
+ public SizeComparator(
+ final int maxWidth,
+ final int maxHeight,
+ final float targetAspectRatio,
+ final int targetPixels) {
+ mMaxWidth = maxWidth;
+ mMaxHeight = maxHeight;
+ mTargetAspectRatio = targetAspectRatio;
+ mTargetPixels = targetPixels;
+ }
+
+ /**
+ * Returns a negative value if left is a better choice than right, or a positive value if right
+ * is a better choice is better than left. 0 if they are equal
+ */
+ @Override
+ public int compare(final Camera.Size left, final Camera.Size right) {
+ // If one size is less than the max size prefer it over the other
+ if ((left.width <= mMaxWidth && left.height <= mMaxHeight)
+ != (right.width <= mMaxWidth && right.height <= mMaxHeight)) {
+ return left.width <= mMaxWidth ? PREFER_LEFT : PREFER_RIGHT;
+ }
+
+ // If one is closer to the target aspect ratio, prefer it.
+ final float leftAspectRatio = left.width / (float) left.height;
+ final float rightAspectRatio = right.width / (float) right.height;
+ final float leftAspectRatioDiff = Math.abs(leftAspectRatio - mTargetAspectRatio);
+ final float rightAspectRatioDiff = Math.abs(rightAspectRatio - mTargetAspectRatio);
+ if (leftAspectRatioDiff != rightAspectRatioDiff) {
+ return (leftAspectRatioDiff - rightAspectRatioDiff) < 0 ? PREFER_LEFT : PREFER_RIGHT;
+ }
+
+ // At this point they have the same aspect ratio diff and are either both bigger
+ // than the max size or both smaller than the max size, so prefer the one closest
+ // to target size
+ final int leftDiff = Math.abs((left.width * left.height) - mTargetPixels);
+ final int rightDiff = Math.abs((right.width * right.height) - mTargetPixels);
+ return leftDiff - rightDiff;
+ }
+ }
+
+ @Override // From FocusOverlayManager.Listener
+ public void autoFocus() {
+ if (mCamera == null) {
+ return;
+ }
+
+ try {
+ mCamera.autoFocus(
+ new Camera.AutoFocusCallback() {
+ @Override
+ public void onAutoFocus(final boolean success, final Camera camera) {
+ mFocusOverlayManager.onAutoFocus(success, false /* shutterDown */);
+ }
+ });
+ } catch (final RuntimeException e) {
+ LogUtil.e("CameraManager.autoFocus", "RuntimeException in CameraManager.autoFocus", e);
+ // If autofocus fails, the camera should have called the callback with success=false,
+ // but some throw an exception here
+ mFocusOverlayManager.onAutoFocus(false /*success*/, false /*shutterDown*/);
+ }
+ }
+
+ @Override // From FocusOverlayManager.Listener
+ public void cancelAutoFocus() {
+ if (mCamera == null) {
+ return;
+ }
+ try {
+ mCamera.cancelAutoFocus();
+ } catch (final RuntimeException e) {
+ // Ignore
+ LogUtil.e(
+ "CameraManager.cancelAutoFocus", "RuntimeException in CameraManager.cancelAutoFocus", e);
+ }
+ }
+
+ @Override // From FocusOverlayManager.Listener
+ public boolean capture() {
+ return false;
+ }
+
+ @Override // From FocusOverlayManager.Listener
+ public void setFocusParameters() {
+ if (mCamera == null) {
+ return;
+ }
+ try {
+ final Camera.Parameters parameters = mCamera.getParameters();
+ parameters.setFocusMode(mFocusOverlayManager.getFocusMode());
+ if (parameters.getMaxNumFocusAreas() > 0) {
+ // Don't set focus areas (even to null) if focus areas aren't supported, camera may
+ // crash
+ parameters.setFocusAreas(mFocusOverlayManager.getFocusAreas());
+ }
+ parameters.setMeteringAreas(mFocusOverlayManager.getMeteringAreas());
+ mCamera.setParameters(parameters);
+ } catch (final RuntimeException e) {
+ // This occurs when the device is out of space or when the camera is locked
+ LogUtil.e(
+ "CameraManager.setFocusParameters",
+ "RuntimeException in CameraManager setFocusParameters");
+ }
+ }
+
+ public void resetPreview() {
+ mCamera.startPreview();
+ if (mCameraPreview != null) {
+ mCameraPreview.setFocusable(true);
+ }
+ }
+
+ private void logCameraSize(final String prefix, final Camera.Size size) {
+ // Log the camera size and aspect ratio for help when examining bug reports for camera
+ // failures
+ LogUtil.i(
+ "CameraManager.logCameraSize",
+ prefix + size.width + "x" + size.height + " (" + (size.width / (float) size.height) + ")");
+ }
+
+ @VisibleForTesting
+ public void resetCameraManager() {
+ sInstance = null;
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/CameraPreview.java b/java/com/android/dialer/callcomposer/camera/CameraPreview.java
new file mode 100644
index 000000000..6581ad67b
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/CameraPreview.java
@@ -0,0 +1,177 @@
+/*
+ * 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.dialer.callcomposer.camera;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.hardware.Camera;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.View.OnTouchListener;
+import com.android.dialer.common.Assert;
+import com.android.dialer.util.PermissionsUtil;
+import java.io.IOException;
+
+/**
+ * Contains shared code for SoftwareCameraPreview and HardwareCameraPreview, cannot use inheritance
+ * because those classes must inherit from separate Views, so those classes delegate calls to this
+ * helper class. Specifics for each implementation are in CameraPreviewHost
+ */
+public class CameraPreview {
+ /** Implemented by the camera for rendering. */
+ public interface CameraPreviewHost {
+ View getView();
+
+ boolean isValid();
+
+ void startPreview(final Camera camera) throws IOException;
+
+ void onCameraPermissionGranted();
+
+ void setShown();
+ }
+
+ private int mCameraWidth = -1;
+ private int mCameraHeight = -1;
+ private boolean mTabHasBeenShown = false;
+ private OnTouchListener mListener;
+
+ private final CameraPreviewHost mHost;
+
+ public CameraPreview(final CameraPreviewHost host) {
+ Assert.isNotNull(host);
+ Assert.isNotNull(host.getView());
+ mHost = host;
+ }
+
+ // This is set when the tab is actually selected.
+ public void setShown() {
+ mTabHasBeenShown = true;
+ maybeOpenCamera();
+ }
+
+ // Opening camera is very expensive. Most of the ANR reports seem to be related to the camera.
+ // So we delay until the camera is actually needed. See b/23287938
+ private void maybeOpenCamera() {
+ boolean visible = mHost.getView().getVisibility() == View.VISIBLE;
+ if (mTabHasBeenShown && visible && PermissionsUtil.hasCameraPermissions(getContext())) {
+ CameraManager.get().openCamera();
+ }
+ }
+
+ public void setSize(final Camera.Size size, final int orientation) {
+ switch (orientation) {
+ case 0:
+ case 180:
+ mCameraWidth = size.width;
+ mCameraHeight = size.height;
+ break;
+ case 90:
+ case 270:
+ default:
+ mCameraWidth = size.height;
+ mCameraHeight = size.width;
+ }
+ mHost.getView().requestLayout();
+ }
+
+ public int getWidthMeasureSpec(final int widthMeasureSpec, final int heightMeasureSpec) {
+ if (mCameraHeight >= 0) {
+ final int width = View.MeasureSpec.getSize(widthMeasureSpec);
+ return MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
+ } else {
+ return widthMeasureSpec;
+ }
+ }
+
+ public int getHeightMeasureSpec(final int widthMeasureSpec, final int heightMeasureSpec) {
+ if (mCameraHeight >= 0) {
+ final int orientation = getContext().getResources().getConfiguration().orientation;
+ final int width = View.MeasureSpec.getSize(widthMeasureSpec);
+ final float aspectRatio = (float) mCameraWidth / (float) mCameraHeight;
+ int height;
+ if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ height = (int) (width * aspectRatio);
+ } else {
+ height = (int) (width / aspectRatio);
+ }
+ return View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
+ } else {
+ return heightMeasureSpec;
+ }
+ }
+
+ // onVisibilityChanged is set to Visible when the tab is _created_,
+ // which may be when the user is viewing a different tab.
+ public void onVisibilityChanged(final int visibility) {
+ if (PermissionsUtil.hasCameraPermissions(getContext())) {
+ if (visibility == View.VISIBLE) {
+ maybeOpenCamera();
+ } else {
+ CameraManager.get().closeCamera();
+ }
+ }
+ }
+
+ public Context getContext() {
+ return mHost.getView().getContext();
+ }
+
+ public void setOnTouchListener(final View.OnTouchListener listener) {
+ mListener = listener;
+ mHost.getView().setOnTouchListener(listener);
+ }
+
+ public void setFocusable(boolean focusable) {
+ mHost.getView().setOnTouchListener(focusable ? mListener : null);
+ }
+
+ public int getHeight() {
+ return mHost.getView().getHeight();
+ }
+
+ public void onAttachedToWindow() {
+ maybeOpenCamera();
+ }
+
+ public void onDetachedFromWindow() {
+ CameraManager.get().closeCamera();
+ }
+
+ public void onRestoreInstanceState() {
+ maybeOpenCamera();
+ }
+
+ public void onCameraPermissionGranted() {
+ maybeOpenCamera();
+ }
+
+ /** @return True if the view is valid and prepared for the camera to start showing the preview */
+ public boolean isValid() {
+ return mHost.isValid();
+ }
+
+ /**
+ * Starts the camera preview on the current surface. Abstracts out the differences in API from the
+ * CameraManager
+ *
+ * @throws IOException Which is caught by the CameraManager to display an error
+ */
+ public void startPreview(final Camera camera) throws IOException {
+ mHost.startPreview(camera);
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/HardwareCameraPreview.java b/java/com/android/dialer/callcomposer/camera/HardwareCameraPreview.java
new file mode 100644
index 000000000..c0d61f3f8
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/HardwareCameraPreview.java
@@ -0,0 +1,125 @@
+/*
+ * 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.dialer.callcomposer.camera;
+
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.view.TextureView;
+import android.view.View;
+import java.io.IOException;
+
+/**
+ * A hardware accelerated preview texture for the camera. This is the preferred CameraPreview
+ * because it animates smoother. When hardware acceleration isn't available, SoftwareCameraPreview
+ * is used.
+ *
+ * <p>There is a significant amount of duplication between HardwareCameraPreview and
+ * SoftwareCameraPreview which we can't easily share due to a lack of multiple inheritance, The
+ * implementations of the shared methods are delegated to CameraPreview
+ */
+public class HardwareCameraPreview extends TextureView implements CameraPreview.CameraPreviewHost {
+ private CameraPreview mPreview;
+
+ public HardwareCameraPreview(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ mPreview = new CameraPreview(this);
+ setSurfaceTextureListener(
+ new SurfaceTextureListener() {
+ @Override
+ public void onSurfaceTextureAvailable(
+ final SurfaceTexture surfaceTexture, final int i, final int i2) {
+ CameraManager.get().setSurface(mPreview);
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(
+ final SurfaceTexture surfaceTexture, final int i, final int i2) {
+ CameraManager.get().setSurface(mPreview);
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(final SurfaceTexture surfaceTexture) {
+ CameraManager.get().setSurface(null);
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(final SurfaceTexture surfaceTexture) {
+ CameraManager.get().setSurface(mPreview);
+ }
+ });
+ }
+
+ @Override
+ public void setShown() {
+ mPreview.setShown();
+ }
+
+ @Override
+ protected void onVisibilityChanged(final View changedView, final int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ mPreview.onVisibilityChanged(visibility);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mPreview.onDetachedFromWindow();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mPreview.onAttachedToWindow();
+ }
+
+ @Override
+ protected void onRestoreInstanceState(final Parcelable state) {
+ super.onRestoreInstanceState(state);
+ mPreview.onRestoreInstanceState();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ widthMeasureSpec = mPreview.getWidthMeasureSpec(widthMeasureSpec, heightMeasureSpec);
+ heightMeasureSpec = mPreview.getHeightMeasureSpec(widthMeasureSpec, heightMeasureSpec);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public View getView() {
+ return this;
+ }
+
+ @Override
+ public boolean isValid() {
+ return getSurfaceTexture() != null;
+ }
+
+ @Override
+ public void startPreview(final Camera camera) throws IOException {
+ camera.setPreviewTexture(getSurfaceTexture());
+ }
+
+ @Override
+ public void onCameraPermissionGranted() {
+ mPreview.onCameraPermissionGranted();
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/ImagePersistTask.java b/java/com/android/dialer/callcomposer/camera/ImagePersistTask.java
new file mode 100644
index 000000000..150009495
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/ImagePersistTask.java
@@ -0,0 +1,143 @@
+/*
+ * 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.dialer.callcomposer.camera;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.support.v4.content.FileProvider;
+import com.android.dialer.callcomposer.camera.exif.ExifInterface;
+import com.android.dialer.callcomposer.camera.exif.ExifTag;
+import com.android.dialer.callcomposer.util.CopyAndResizeImageTask;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.FallibleAsyncTask;
+import com.android.dialer.constants.Constants;
+import com.android.dialer.util.DialerUtils;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** Persisting image routine. */
+@TargetApi(VERSION_CODES.M)
+public class ImagePersistTask extends FallibleAsyncTask<Void, Void, Uri> {
+ private int mWidth;
+ private int mHeight;
+ private final float mHeightPercent;
+ private final byte[] mBytes;
+ private final Context mContext;
+ private final CameraManager.MediaCallback mCallback;
+
+ ImagePersistTask(
+ final int width,
+ final int height,
+ final float heightPercent,
+ final byte[] bytes,
+ final Context context,
+ final CameraManager.MediaCallback callback) {
+ Assert.checkArgument(heightPercent >= 0 && heightPercent <= 1);
+ Assert.isNotNull(bytes);
+ Assert.isNotNull(context);
+ Assert.isNotNull(callback);
+ mWidth = width;
+ mHeight = height;
+ mHeightPercent = heightPercent;
+ mBytes = bytes;
+ mContext = context;
+ mCallback = callback;
+ }
+
+ @Override
+ protected Uri doInBackgroundFallible(final Void... params) throws Exception {
+ File outputFile = DialerUtils.createShareableFile(mContext);
+
+ try (OutputStream outputStream = new FileOutputStream(outputFile)) {
+ if (mHeightPercent != 1.0f) {
+ writeClippedBitmap(outputStream);
+ } else {
+ outputStream.write(mBytes, 0, mBytes.length);
+ }
+ }
+
+ return FileProvider.getUriForFile(
+ mContext, Constants.get().getFileProviderAuthority(), outputFile);
+ }
+
+ @Override
+ protected void onPostExecute(FallibleTaskResult<Uri> result) {
+ if (result.isFailure()) {
+ mCallback.onMediaFailed(new Exception("Persisting image failed", result.getThrowable()));
+ } else {
+ mCallback.onMediaReady(result.getResult(), "image/jpeg", mWidth, mHeight);
+ }
+ }
+
+ private void writeClippedBitmap(OutputStream outputStream) throws IOException {
+ int orientation = android.media.ExifInterface.ORIENTATION_UNDEFINED;
+ final ExifInterface exifInterface = new ExifInterface();
+ try {
+ exifInterface.readExif(mBytes);
+ final Integer orientationValue = exifInterface.getTagIntValue(ExifInterface.TAG_ORIENTATION);
+ if (orientationValue != null) {
+ orientation = orientationValue.intValue();
+ }
+ } catch (final IOException e) {
+ // Couldn't get exif tags, not the end of the world
+ }
+ Bitmap bitmap = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length);
+ final int clippedWidth;
+ final int clippedHeight;
+ if (ExifInterface.getOrientationParams(orientation).invertDimensions) {
+ Assert.checkState(mWidth == bitmap.getHeight());
+ Assert.checkState(mHeight == bitmap.getWidth());
+ clippedWidth = (int) (mHeight * mHeightPercent);
+ clippedHeight = mWidth;
+ } else {
+ Assert.checkState(mWidth == bitmap.getWidth());
+ Assert.checkState(mHeight == bitmap.getHeight());
+ clippedWidth = mWidth;
+ clippedHeight = (int) (mHeight * mHeightPercent);
+ }
+ final int offsetTop = (bitmap.getHeight() - clippedHeight) / 2;
+ final int offsetLeft = (bitmap.getWidth() - clippedWidth) / 2;
+ mWidth = clippedWidth;
+ mHeight = clippedHeight;
+ Bitmap clippedBitmap =
+ Bitmap.createBitmap(clippedWidth, clippedHeight, Bitmap.Config.ARGB_8888);
+ clippedBitmap.setDensity(bitmap.getDensity());
+ final Canvas clippedBitmapCanvas = new Canvas(clippedBitmap);
+ final Matrix matrix = new Matrix();
+ matrix.postTranslate(-offsetLeft, -offsetTop);
+ clippedBitmapCanvas.drawBitmap(bitmap, matrix, null /* paint */);
+ clippedBitmapCanvas.save();
+ clippedBitmap = CopyAndResizeImageTask.resizeForEnrichedCalling(clippedBitmap);
+ // EXIF data can take a big chunk of the file size and is often cleared by the
+ // carrier, only store orientation since that's critical
+ final ExifTag orientationTag = exifInterface.getTag(ExifInterface.TAG_ORIENTATION);
+ exifInterface.clearExif();
+ exifInterface.setTag(orientationTag);
+ exifInterface.writeExif(clippedBitmap, outputStream);
+
+ clippedBitmap.recycle();
+ bitmap.recycle();
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/SoftwareCameraPreview.java b/java/com/android/dialer/callcomposer/camera/SoftwareCameraPreview.java
new file mode 100644
index 000000000..fe2c600df
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/SoftwareCameraPreview.java
@@ -0,0 +1,120 @@
+/*
+ * 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.dialer.callcomposer.camera;
+
+import android.content.Context;
+import android.hardware.Camera;
+import android.os.Parcelable;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import java.io.IOException;
+
+/**
+ * A software rendered preview surface for the camera. This renders slower and causes more jank, so
+ * HardwareCameraPreview is preferred if possible.
+ *
+ * <p>There is a significant amount of duplication between HardwareCameraPreview and
+ * SoftwareCameraPreview which we can't easily share due to a lack of multiple inheritance, The
+ * implementations of the shared methods are delegated to CameraPreview
+ */
+public class SoftwareCameraPreview extends SurfaceView implements CameraPreview.CameraPreviewHost {
+ private final CameraPreview mPreview;
+
+ public SoftwareCameraPreview(final Context context) {
+ super(context);
+ mPreview = new CameraPreview(this);
+ getHolder()
+ .addCallback(
+ new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(final SurfaceHolder surfaceHolder) {
+ CameraManager.get().setSurface(mPreview);
+ }
+
+ @Override
+ public void surfaceChanged(
+ final SurfaceHolder surfaceHolder,
+ final int format,
+ final int width,
+ final int height) {
+ CameraManager.get().setSurface(mPreview);
+ }
+
+ @Override
+ public void surfaceDestroyed(final SurfaceHolder surfaceHolder) {
+ CameraManager.get().setSurface(null);
+ }
+ });
+ }
+
+ @Override
+ public void setShown() {
+ mPreview.setShown();
+ }
+
+ @Override
+ protected void onVisibilityChanged(final View changedView, final int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ mPreview.onVisibilityChanged(visibility);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mPreview.onDetachedFromWindow();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mPreview.onAttachedToWindow();
+ }
+
+ @Override
+ protected void onRestoreInstanceState(final Parcelable state) {
+ super.onRestoreInstanceState(state);
+ mPreview.onRestoreInstanceState();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ widthMeasureSpec = mPreview.getWidthMeasureSpec(widthMeasureSpec, heightMeasureSpec);
+ heightMeasureSpec = mPreview.getHeightMeasureSpec(widthMeasureSpec, heightMeasureSpec);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public View getView() {
+ return this;
+ }
+
+ @Override
+ public boolean isValid() {
+ return getHolder() != null;
+ }
+
+ @Override
+ public void startPreview(final Camera camera) throws IOException {
+ camera.setPreviewDisplay(getHolder());
+ }
+
+ @Override
+ public void onCameraPermissionGranted() {
+ mPreview.onCameraPermissionGranted();
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/camerafocus/AndroidManifest.xml b/java/com/android/dialer/callcomposer/camera/camerafocus/AndroidManifest.xml
new file mode 100644
index 000000000..77ef22295
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/camerafocus/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<!--
+ ~ 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
+ -->
+<manifest package="com.android.dialer.callcomposer.camera.camerafocus"/> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/camera/camerafocus/FocusIndicator.java b/java/com/android/dialer/callcomposer/camera/camerafocus/FocusIndicator.java
new file mode 100644
index 000000000..234cf5388
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/camerafocus/FocusIndicator.java
@@ -0,0 +1,28 @@
+/*
+ * 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.dialer.callcomposer.camera.camerafocus;
+
+/** Methods needed by the CameraPreview in order communicate camera events. */
+public interface FocusIndicator {
+ void showStart();
+
+ void showSuccess(boolean timeout);
+
+ void showFail(boolean timeout);
+
+ void clear();
+}
diff --git a/java/com/android/dialer/callcomposer/camera/camerafocus/FocusOverlayManager.java b/java/com/android/dialer/callcomposer/camera/camerafocus/FocusOverlayManager.java
new file mode 100644
index 000000000..1c5ac380c
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/camerafocus/FocusOverlayManager.java
@@ -0,0 +1,482 @@
+/*
+ * 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.dialer.callcomposer.camera.camerafocus;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.hardware.Camera.Area;
+import android.hardware.Camera.Parameters;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class that handles everything about focus in still picture mode. This also handles the metering
+ * area because it is the same as focus area.
+ *
+ * <p>The test cases: (1) The camera has continuous autofocus. Move the camera. Take a picture when
+ * CAF is not in progress. (2) The camera has continuous autofocus. Move the camera. Take a picture
+ * when CAF is in progress. (3) The camera has face detection. Point the camera at some faces. Hold
+ * the shutter. Release to take a picture. (4) The camera has face detection. Point the camera at
+ * some faces. Single tap the shutter to take a picture. (5) The camera has autofocus. Single tap
+ * the shutter to take a picture. (6) The camera has autofocus. Hold the shutter. Release to take a
+ * picture. (7) The camera has no autofocus. Single tap the shutter and take a picture. (8) The
+ * camera has autofocus and supports focus area. Touch the screen to trigger autofocus. Take a
+ * picture. (9) The camera has autofocus and supports focus area. Touch the screen to trigger
+ * autofocus. Wait until it times out. (10) The camera has no autofocus and supports metering area.
+ * Touch the screen to change metering area.
+ */
+public class FocusOverlayManager {
+ private static final String TRUE = "true";
+ private static final String AUTO_EXPOSURE_LOCK_SUPPORTED = "auto-exposure-lock-supported";
+ private static final String AUTO_WHITE_BALANCE_LOCK_SUPPORTED =
+ "auto-whitebalance-lock-supported";
+
+ private static final int RESET_TOUCH_FOCUS = 0;
+ private static final int RESET_TOUCH_FOCUS_DELAY = 3000;
+
+ private int mState = STATE_IDLE;
+ private static final int STATE_IDLE = 0; // Focus is not active.
+ private static final int STATE_FOCUSING = 1; // Focus is in progress.
+ // Focus is in progress and the camera should take a picture after focus finishes.
+ private static final int STATE_FOCUSING_SNAP_ON_FINISH = 2;
+ private static final int STATE_SUCCESS = 3; // Focus finishes and succeeds.
+ private static final int STATE_FAIL = 4; // Focus finishes and fails.
+
+ private boolean mInitialized;
+ private boolean mFocusAreaSupported;
+ private boolean mMeteringAreaSupported;
+ private boolean mLockAeAwbNeeded;
+ private boolean mAeAwbLock;
+ private Matrix mMatrix;
+
+ private PieRenderer mPieRenderer;
+
+ private int mPreviewWidth; // The width of the preview frame layout.
+ private int mPreviewHeight; // The height of the preview frame layout.
+ private boolean mMirror; // true if the camera is front-facing.
+ private List<Area> mFocusArea; // focus area in driver format
+ private List<Area> mMeteringArea; // metering area in driver format
+ private String mFocusMode;
+ private Parameters mParameters;
+ private Handler mHandler;
+ private Listener mListener;
+
+ /** Listener used for the focus indicator to communicate back to the camera. */
+ public interface Listener {
+ void autoFocus();
+
+ void cancelAutoFocus();
+
+ boolean capture();
+
+ void setFocusParameters();
+ }
+
+ private class MainHandler extends Handler {
+ public MainHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case RESET_TOUCH_FOCUS:
+ {
+ cancelAutoFocus();
+ break;
+ }
+ }
+ }
+ }
+
+ public FocusOverlayManager(Listener listener, Looper looper) {
+ mHandler = new MainHandler(looper);
+ mMatrix = new Matrix();
+ mListener = listener;
+ }
+
+ public void setFocusRenderer(PieRenderer renderer) {
+ mPieRenderer = renderer;
+ mInitialized = (mMatrix != null);
+ }
+
+ public void setParameters(Parameters parameters) {
+ // parameters can only be null when onConfigurationChanged is called
+ // before camera is open. We will just return in this case, because
+ // parameters will be set again later with the right parameters after
+ // camera is open.
+ if (parameters == null) {
+ return;
+ }
+ mParameters = parameters;
+ mFocusAreaSupported = isFocusAreaSupported(parameters);
+ mMeteringAreaSupported = isMeteringAreaSupported(parameters);
+ mLockAeAwbNeeded =
+ (isAutoExposureLockSupported(mParameters) || isAutoWhiteBalanceLockSupported(mParameters));
+ }
+
+ public void setPreviewSize(int previewWidth, int previewHeight) {
+ if (mPreviewWidth != previewWidth || mPreviewHeight != previewHeight) {
+ mPreviewWidth = previewWidth;
+ mPreviewHeight = previewHeight;
+ setMatrix();
+ }
+ }
+
+ public void setMirror(boolean mirror) {
+ mMirror = mirror;
+ setMatrix();
+ }
+
+ private void setMatrix() {
+ if (mPreviewWidth != 0 && mPreviewHeight != 0) {
+ Matrix matrix = new Matrix();
+ prepareMatrix(matrix, mMirror, mPreviewWidth, mPreviewHeight);
+ // In face detection, the matrix converts the driver coordinates to UI
+ // coordinates. In tap focus, the inverted matrix converts the UI
+ // coordinates to driver coordinates.
+ matrix.invert(mMatrix);
+ mInitialized = (mPieRenderer != null);
+ }
+ }
+
+ private void lockAeAwbIfNeeded() {
+ if (mLockAeAwbNeeded && !mAeAwbLock) {
+ mAeAwbLock = true;
+ mListener.setFocusParameters();
+ }
+ }
+
+ public void onAutoFocus(boolean focused, boolean shutterButtonPressed) {
+ if (mState == STATE_FOCUSING_SNAP_ON_FINISH) {
+ // Take the picture no matter focus succeeds or fails. No need
+ // to play the AF sound if we're about to play the shutter
+ // sound.
+ if (focused) {
+ mState = STATE_SUCCESS;
+ } else {
+ mState = STATE_FAIL;
+ }
+ updateFocusUI();
+ capture();
+ } else if (mState == STATE_FOCUSING) {
+ // This happens when (1) user is half-pressing the focus key or
+ // (2) touch focus is triggered. Play the focus tone. Do not
+ // take the picture now.
+ if (focused) {
+ mState = STATE_SUCCESS;
+ } else {
+ mState = STATE_FAIL;
+ }
+ updateFocusUI();
+ // If this is triggered by touch focus, cancel focus after a
+ // while.
+ if (mFocusArea != null) {
+ mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY);
+ }
+ if (shutterButtonPressed) {
+ // Lock AE & AWB so users can half-press shutter and recompose.
+ lockAeAwbIfNeeded();
+ }
+ } else if (mState == STATE_IDLE) {
+ // User has released the focus key before focus completes.
+ // Do nothing.
+ }
+ }
+
+ public void onAutoFocusMoving(boolean moving) {
+ if (!mInitialized) {
+ return;
+ }
+
+ // Ignore if we have requested autofocus. This method only handles
+ // continuous autofocus.
+ if (mState != STATE_IDLE) {
+ return;
+ }
+
+ if (moving) {
+ mPieRenderer.showStart();
+ } else {
+ mPieRenderer.showSuccess(true);
+ }
+ }
+
+ private void initializeFocusAreas(
+ int focusWidth, int focusHeight, int x, int y, int previewWidth, int previewHeight) {
+ if (mFocusArea == null) {
+ mFocusArea = new ArrayList<>();
+ mFocusArea.add(new Area(new Rect(), 1));
+ }
+
+ // Convert the coordinates to driver format.
+ calculateTapArea(
+ focusWidth, focusHeight, 1f, x, y, previewWidth, previewHeight, mFocusArea.get(0).rect);
+ }
+
+ private void initializeMeteringAreas(
+ int focusWidth, int focusHeight, int x, int y, int previewWidth, int previewHeight) {
+ if (mMeteringArea == null) {
+ mMeteringArea = new ArrayList<>();
+ mMeteringArea.add(new Area(new Rect(), 1));
+ }
+
+ // Convert the coordinates to driver format.
+ // AE area is bigger because exposure is sensitive and
+ // easy to over- or underexposure if area is too small.
+ calculateTapArea(
+ focusWidth,
+ focusHeight,
+ 1.5f,
+ x,
+ y,
+ previewWidth,
+ previewHeight,
+ mMeteringArea.get(0).rect);
+ }
+
+ public void onSingleTapUp(int x, int y) {
+ if (!mInitialized || mState == STATE_FOCUSING_SNAP_ON_FINISH) {
+ return;
+ }
+
+ // Let users be able to cancel previous touch focus.
+ if ((mFocusArea != null)
+ && (mState == STATE_FOCUSING || mState == STATE_SUCCESS || mState == STATE_FAIL)) {
+ cancelAutoFocus();
+ }
+ // Initialize variables.
+ int focusWidth = mPieRenderer.getSize();
+ int focusHeight = mPieRenderer.getSize();
+ if (focusWidth == 0 || mPieRenderer.getWidth() == 0 || mPieRenderer.getHeight() == 0) {
+ return;
+ }
+ int previewWidth = mPreviewWidth;
+ int previewHeight = mPreviewHeight;
+ // Initialize mFocusArea.
+ if (mFocusAreaSupported) {
+ initializeFocusAreas(focusWidth, focusHeight, x, y, previewWidth, previewHeight);
+ }
+ // Initialize mMeteringArea.
+ if (mMeteringAreaSupported) {
+ initializeMeteringAreas(focusWidth, focusHeight, x, y, previewWidth, previewHeight);
+ }
+
+ // Use margin to set the focus indicator to the touched area.
+ mPieRenderer.setFocus(x, y);
+
+ // Set the focus area and metering area.
+ mListener.setFocusParameters();
+ if (mFocusAreaSupported) {
+ autoFocus();
+ } else { // Just show the indicator in all other cases.
+ updateFocusUI();
+ // Reset the metering area in 3 seconds.
+ mHandler.removeMessages(RESET_TOUCH_FOCUS);
+ mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY);
+ }
+ }
+
+ public void onPreviewStarted() {
+ mState = STATE_IDLE;
+ }
+
+ public void onPreviewStopped() {
+ // If auto focus was in progress, it would have been stopped.
+ mState = STATE_IDLE;
+ resetTouchFocus();
+ updateFocusUI();
+ }
+
+ public void onCameraReleased() {
+ onPreviewStopped();
+ }
+
+ private void autoFocus() {
+ LogUtil.v("FocusOverlayManager.autoFocus", "Start autofocus.");
+ mListener.autoFocus();
+ mState = STATE_FOCUSING;
+ updateFocusUI();
+ mHandler.removeMessages(RESET_TOUCH_FOCUS);
+ }
+
+ public void cancelAutoFocus() {
+ LogUtil.v("FocusOverlayManager.cancelAutoFocus", "Cancel autofocus.");
+
+ // Reset the tap area before calling mListener.cancelAutofocus.
+ // Otherwise, focus mode stays at auto and the tap area passed to the
+ // driver is not reset.
+ resetTouchFocus();
+ mListener.cancelAutoFocus();
+ mState = STATE_IDLE;
+ updateFocusUI();
+ mHandler.removeMessages(RESET_TOUCH_FOCUS);
+ }
+
+ private void capture() {
+ if (mListener.capture()) {
+ mState = STATE_IDLE;
+ mHandler.removeMessages(RESET_TOUCH_FOCUS);
+ }
+ }
+
+ public String getFocusMode() {
+ List<String> supportedFocusModes = mParameters.getSupportedFocusModes();
+
+ if (mFocusAreaSupported && mFocusArea != null) {
+ // Always use autofocus in tap-to-focus.
+ mFocusMode = Parameters.FOCUS_MODE_AUTO;
+ } else {
+ mFocusMode = Parameters.FOCUS_MODE_CONTINUOUS_PICTURE;
+ }
+
+ if (!isSupported(mFocusMode, supportedFocusModes)) {
+ // For some reasons, the driver does not support the current
+ // focus mode. Fall back to auto.
+ if (isSupported(Parameters.FOCUS_MODE_AUTO, mParameters.getSupportedFocusModes())) {
+ mFocusMode = Parameters.FOCUS_MODE_AUTO;
+ } else {
+ mFocusMode = mParameters.getFocusMode();
+ }
+ }
+ return mFocusMode;
+ }
+
+ public List<Area> getFocusAreas() {
+ return mFocusArea;
+ }
+
+ public List<Area> getMeteringAreas() {
+ return mMeteringArea;
+ }
+
+ private void updateFocusUI() {
+ if (!mInitialized) {
+ return;
+ }
+ FocusIndicator focusIndicator = mPieRenderer;
+
+ if (mState == STATE_IDLE) {
+ if (mFocusArea == null) {
+ focusIndicator.clear();
+ } else {
+ // Users touch on the preview and the indicator represents the
+ // metering area. Either focus area is not supported or
+ // autoFocus call is not required.
+ focusIndicator.showStart();
+ }
+ } else if (mState == STATE_FOCUSING || mState == STATE_FOCUSING_SNAP_ON_FINISH) {
+ focusIndicator.showStart();
+ } else {
+ if (Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)) {
+ // TODO: check HAL behavior and decide if this can be removed.
+ focusIndicator.showSuccess(false);
+ } else if (mState == STATE_SUCCESS) {
+ focusIndicator.showSuccess(false);
+ } else if (mState == STATE_FAIL) {
+ focusIndicator.showFail(false);
+ }
+ }
+ }
+
+ private void resetTouchFocus() {
+ if (!mInitialized) {
+ return;
+ }
+
+ // Put focus indicator to the center. clear reset position
+ mPieRenderer.clear();
+
+ mFocusArea = null;
+ mMeteringArea = null;
+ }
+
+ private void calculateTapArea(
+ int focusWidth,
+ int focusHeight,
+ float areaMultiple,
+ int x,
+ int y,
+ int previewWidth,
+ int previewHeight,
+ Rect rect) {
+ int areaWidth = (int) (focusWidth * areaMultiple);
+ int areaHeight = (int) (focusHeight * areaMultiple);
+ final int maxW = previewWidth - areaWidth;
+ int left = maxW > 0 ? clamp(x - areaWidth / 2, 0, maxW) : 0;
+ final int maxH = previewHeight - areaHeight;
+ int top = maxH > 0 ? clamp(y - areaHeight / 2, 0, maxH) : 0;
+
+ RectF rectF = new RectF(left, top, left + areaWidth, top + areaHeight);
+ mMatrix.mapRect(rectF);
+ rectFToRect(rectF, rect);
+ }
+
+ private int clamp(int x, int min, int max) {
+ Assert.checkArgument(max >= min);
+ if (x > max) {
+ return max;
+ }
+ if (x < min) {
+ return min;
+ }
+ return x;
+ }
+
+ private boolean isAutoExposureLockSupported(Parameters params) {
+ return TRUE.equals(params.get(AUTO_EXPOSURE_LOCK_SUPPORTED));
+ }
+
+ private boolean isAutoWhiteBalanceLockSupported(Parameters params) {
+ return TRUE.equals(params.get(AUTO_WHITE_BALANCE_LOCK_SUPPORTED));
+ }
+
+ private boolean isSupported(String value, List<String> supported) {
+ return supported != null && supported.indexOf(value) >= 0;
+ }
+
+ private boolean isMeteringAreaSupported(Parameters params) {
+ return params.getMaxNumMeteringAreas() > 0;
+ }
+
+ private boolean isFocusAreaSupported(Parameters params) {
+ return (params.getMaxNumFocusAreas() > 0
+ && isSupported(Parameters.FOCUS_MODE_AUTO, params.getSupportedFocusModes()));
+ }
+
+ private void prepareMatrix(Matrix matrix, boolean mirror, int viewWidth, int viewHeight) {
+ // Need mirror for front camera.
+ matrix.setScale(mirror ? -1 : 1, 1);
+ // Camera driver coordinates range from (-1000, -1000) to (1000, 1000).
+ // UI coordinates range from (0, 0) to (width, height).
+ matrix.postScale(viewWidth / 2000f, viewHeight / 2000f);
+ matrix.postTranslate(viewWidth / 2f, viewHeight / 2f);
+ }
+
+ private void rectFToRect(RectF rectF, Rect rect) {
+ rect.left = Math.round(rectF.left);
+ rect.top = Math.round(rectF.top);
+ rect.right = Math.round(rectF.right);
+ rect.bottom = Math.round(rectF.bottom);
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/camerafocus/OverlayRenderer.java b/java/com/android/dialer/callcomposer/camera/camerafocus/OverlayRenderer.java
new file mode 100644
index 000000000..4a3b522bb
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/camerafocus/OverlayRenderer.java
@@ -0,0 +1,97 @@
+/*
+ * 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.dialer.callcomposer.camera.camerafocus;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.view.MotionEvent;
+
+/** Abstract class that all Camera overlays should implement. */
+public abstract class OverlayRenderer implements RenderOverlay.Renderer {
+
+ protected RenderOverlay mOverlay;
+
+ private int mLeft;
+ private int mTop;
+ private int mRight;
+ private int mBottom;
+ private boolean mVisible;
+
+ public void setVisible(boolean vis) {
+ mVisible = vis;
+ update();
+ }
+
+ public boolean isVisible() {
+ return mVisible;
+ }
+
+ // default does not handle touch
+ @Override
+ public boolean handlesTouch() {
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent evt) {
+ return false;
+ }
+
+ public abstract void onDraw(Canvas canvas);
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mVisible) {
+ onDraw(canvas);
+ }
+ }
+
+ @Override
+ public void setOverlay(RenderOverlay overlay) {
+ mOverlay = overlay;
+ }
+
+ @Override
+ public void layout(int left, int top, int right, int bottom) {
+ mLeft = left;
+ mRight = right;
+ mTop = top;
+ mBottom = bottom;
+ }
+
+ protected Context getContext() {
+ if (mOverlay != null) {
+ return mOverlay.getContext();
+ } else {
+ return null;
+ }
+ }
+
+ public int getWidth() {
+ return mRight - mLeft;
+ }
+
+ public int getHeight() {
+ return mBottom - mTop;
+ }
+
+ protected void update() {
+ if (mOverlay != null) {
+ mOverlay.update();
+ }
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/camerafocus/PieItem.java b/java/com/android/dialer/callcomposer/camera/camerafocus/PieItem.java
new file mode 100644
index 000000000..86f69c0ae
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/camerafocus/PieItem.java
@@ -0,0 +1,179 @@
+/*
+ * 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.dialer.callcomposer.camera.camerafocus;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.drawable.Drawable;
+import java.util.List;
+
+/** Pie menu item. */
+public class PieItem {
+
+ /** Listener to detect pie item clicks. */
+ public interface OnClickListener {
+ void onClick(PieItem item);
+ }
+
+ private Drawable mDrawable;
+ private int level;
+ private float mCenter;
+ private float start;
+ private float sweep;
+ private float animate;
+ private int inner;
+ private int outer;
+ private boolean mSelected;
+ private boolean mEnabled;
+ private List<PieItem> mItems;
+ private Path mPath;
+ private OnClickListener mOnClickListener;
+ private float mAlpha;
+
+ // Gray out the view when disabled
+ private static final float ENABLED_ALPHA = 1;
+ private static final float DISABLED_ALPHA = (float) 0.3;
+
+ public PieItem(Drawable drawable, int level) {
+ mDrawable = drawable;
+ this.level = level;
+ setAlpha(1f);
+ mEnabled = true;
+ setAnimationAngle(getAnimationAngle());
+ start = -1;
+ mCenter = -1;
+ }
+
+ public boolean hasItems() {
+ return mItems != null;
+ }
+
+ public List<PieItem> getItems() {
+ return mItems;
+ }
+
+ public void setPath(Path p) {
+ mPath = p;
+ }
+
+ public Path getPath() {
+ return mPath;
+ }
+
+ public void setAlpha(float alpha) {
+ mAlpha = alpha;
+ mDrawable.setAlpha((int) (255 * alpha));
+ }
+
+ public void setAnimationAngle(float a) {
+ animate = a;
+ }
+
+ private float getAnimationAngle() {
+ return animate;
+ }
+
+ public void setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ if (mEnabled) {
+ setAlpha(ENABLED_ALPHA);
+ } else {
+ setAlpha(DISABLED_ALPHA);
+ }
+ }
+
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ public void setSelected(boolean s) {
+ mSelected = s;
+ }
+
+ public boolean isSelected() {
+ return mSelected;
+ }
+
+ public int getLevel() {
+ return level;
+ }
+
+ public void setGeometry(float st, float sw, int inside, int outside) {
+ start = st;
+ sweep = sw;
+ inner = inside;
+ outer = outside;
+ }
+
+ public float getCenter() {
+ return mCenter;
+ }
+
+ public float getStart() {
+ return start;
+ }
+
+ public float getStartAngle() {
+ return start + animate;
+ }
+
+ public float getSweep() {
+ return sweep;
+ }
+
+ public int getInnerRadius() {
+ return inner;
+ }
+
+ public int getOuterRadius() {
+ return outer;
+ }
+
+ public void setOnClickListener(OnClickListener listener) {
+ mOnClickListener = listener;
+ }
+
+ public void performClick() {
+ if (mOnClickListener != null) {
+ mOnClickListener.onClick(this);
+ }
+ }
+
+ public int getIntrinsicWidth() {
+ return mDrawable.getIntrinsicWidth();
+ }
+
+ public int getIntrinsicHeight() {
+ return mDrawable.getIntrinsicHeight();
+ }
+
+ public void setBounds(int left, int top, int right, int bottom) {
+ mDrawable.setBounds(left, top, right, bottom);
+ }
+
+ public void draw(Canvas canvas) {
+ mDrawable.draw(canvas);
+ }
+
+ public void setImageResource(Context context, int resId) {
+ Drawable d = context.getResources().getDrawable(resId).mutate();
+ d.setBounds(mDrawable.getBounds());
+ mDrawable = d;
+ setAlpha(mAlpha);
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/camerafocus/PieRenderer.java b/java/com/android/dialer/callcomposer/camera/camerafocus/PieRenderer.java
new file mode 100644
index 000000000..59b57b002
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/camerafocus/PieRenderer.java
@@ -0,0 +1,816 @@
+/*
+ * 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.dialer.callcomposer.camera.camerafocus;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import android.os.Handler;
+import android.os.Message;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.Transformation;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Used to draw and render the pie item focus indicator. */
+public class PieRenderer extends OverlayRenderer implements FocusIndicator {
+ // Sometimes continuous autofocus starts and stops several times quickly.
+ // These states are used to make sure the animation is run for at least some
+ // time.
+ private volatile int mState;
+ private ScaleAnimation mAnimation = new ScaleAnimation();
+ private static final int STATE_IDLE = 0;
+ private static final int STATE_FOCUSING = 1;
+ private static final int STATE_FINISHING = 2;
+ private static final int STATE_PIE = 8;
+
+ private Runnable mDisappear = new Disappear();
+ private Animation.AnimationListener mEndAction = new EndAction();
+ private static final int SCALING_UP_TIME = 600;
+ private static final int SCALING_DOWN_TIME = 100;
+ private static final int DISAPPEAR_TIMEOUT = 200;
+ private static final int DIAL_HORIZONTAL = 157;
+
+ private static final long PIE_FADE_IN_DURATION = 200;
+ private static final long PIE_XFADE_DURATION = 200;
+ private static final long PIE_SELECT_FADE_DURATION = 300;
+
+ private static final int MSG_OPEN = 0;
+ private static final int MSG_CLOSE = 1;
+ private static final float PIE_SWEEP = (float) (Math.PI * 2 / 3);
+ // geometry
+ private Point mCenter;
+ private int mRadius;
+ private int mRadiusInc;
+
+ // the detection if touch is inside a slice is offset
+ // inbounds by this amount to allow the selection to show before the
+ // finger covers it
+ private int mTouchOffset;
+
+ private List<PieItem> mItems;
+
+ private PieItem mOpenItem;
+
+ private Paint mSelectedPaint;
+ private Paint mSubPaint;
+
+ // touch handling
+ private PieItem mCurrentItem;
+
+ private Paint mFocusPaint;
+ private int mSuccessColor;
+ private int mFailColor;
+ private int mCircleSize;
+ private int mFocusX;
+ private int mFocusY;
+ private int mCenterX;
+ private int mCenterY;
+
+ private int mDialAngle;
+ private RectF mCircle;
+ private RectF mDial;
+ private Point mPoint1;
+ private Point mPoint2;
+ private int mStartAnimationAngle;
+ private boolean mFocused;
+ private int mInnerOffset;
+ private int mOuterStroke;
+ private int mInnerStroke;
+ private boolean mTapMode;
+ private boolean mBlockFocus;
+ private int mTouchSlopSquared;
+ private Point mDown;
+ private boolean mOpening;
+ private LinearAnimation mXFade;
+ private LinearAnimation mFadeIn;
+ private volatile boolean mFocusCancelled;
+
+ private Handler mHandler =
+ new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_OPEN:
+ if (mListener != null) {
+ mListener.onPieOpened(mCenter.x, mCenter.y);
+ }
+ break;
+ case MSG_CLOSE:
+ if (mListener != null) {
+ mListener.onPieClosed();
+ }
+ break;
+ }
+ }
+ };
+
+ private PieListener mListener;
+
+ /** Listener for the pie item to communicate back to the renderer. */
+ public interface PieListener {
+ void onPieOpened(int centerX, int centerY);
+
+ void onPieClosed();
+ }
+
+ public void setPieListener(PieListener pl) {
+ mListener = pl;
+ }
+
+ public PieRenderer(Context context) {
+ init(context);
+ }
+
+ private void init(Context ctx) {
+ setVisible(false);
+ mItems = new ArrayList<PieItem>();
+ Resources res = ctx.getResources();
+ mRadius = res.getDimensionPixelSize(R.dimen.pie_radius_start);
+ mCircleSize = mRadius - res.getDimensionPixelSize(R.dimen.focus_radius_offset);
+ mRadiusInc = res.getDimensionPixelSize(R.dimen.pie_radius_increment);
+ mTouchOffset = res.getDimensionPixelSize(R.dimen.pie_touch_offset);
+ mCenter = new Point(0, 0);
+ mSelectedPaint = new Paint();
+ mSelectedPaint.setColor(Color.argb(255, 51, 181, 229));
+ mSelectedPaint.setAntiAlias(true);
+ mSubPaint = new Paint();
+ mSubPaint.setAntiAlias(true);
+ mSubPaint.setColor(Color.argb(200, 250, 230, 128));
+ mFocusPaint = new Paint();
+ mFocusPaint.setAntiAlias(true);
+ mFocusPaint.setColor(Color.WHITE);
+ mFocusPaint.setStyle(Paint.Style.STROKE);
+ mSuccessColor = Color.GREEN;
+ mFailColor = Color.RED;
+ mCircle = new RectF();
+ mDial = new RectF();
+ mPoint1 = new Point();
+ mPoint2 = new Point();
+ mInnerOffset = res.getDimensionPixelSize(R.dimen.focus_inner_offset);
+ mOuterStroke = res.getDimensionPixelSize(R.dimen.focus_outer_stroke);
+ mInnerStroke = res.getDimensionPixelSize(R.dimen.focus_inner_stroke);
+ mState = STATE_IDLE;
+ mBlockFocus = false;
+ mTouchSlopSquared = ViewConfiguration.get(ctx).getScaledTouchSlop();
+ mTouchSlopSquared = mTouchSlopSquared * mTouchSlopSquared;
+ mDown = new Point();
+ }
+
+ public boolean showsItems() {
+ return mTapMode;
+ }
+
+ public void addItem(PieItem item) {
+ // add the item to the pie itself
+ mItems.add(item);
+ }
+
+ public void removeItem(PieItem item) {
+ mItems.remove(item);
+ }
+
+ public void clearItems() {
+ mItems.clear();
+ }
+
+ public void showInCenter() {
+ if ((mState == STATE_PIE) && isVisible()) {
+ mTapMode = false;
+ show(false);
+ } else {
+ if (mState != STATE_IDLE) {
+ cancelFocus();
+ }
+ mState = STATE_PIE;
+ setCenter(mCenterX, mCenterY);
+ mTapMode = true;
+ show(true);
+ }
+ }
+
+ public void hide() {
+ show(false);
+ }
+
+ /**
+ * guaranteed has center set
+ *
+ * @param show
+ */
+ private void show(boolean show) {
+ if (show) {
+ mState = STATE_PIE;
+ // ensure clean state
+ mCurrentItem = null;
+ mOpenItem = null;
+ for (PieItem item : mItems) {
+ item.setSelected(false);
+ }
+ layoutPie();
+ fadeIn();
+ } else {
+ mState = STATE_IDLE;
+ mTapMode = false;
+ if (mXFade != null) {
+ mXFade.cancel();
+ }
+ }
+ setVisible(show);
+ mHandler.sendEmptyMessage(show ? MSG_OPEN : MSG_CLOSE);
+ }
+
+ private void fadeIn() {
+ mFadeIn = new LinearAnimation(0, 1);
+ mFadeIn.setDuration(PIE_FADE_IN_DURATION);
+ mFadeIn.setAnimationListener(
+ new AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {}
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ mFadeIn = null;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {}
+ });
+ mFadeIn.startNow();
+ mOverlay.startAnimation(mFadeIn);
+ }
+
+ public void setCenter(int x, int y) {
+ mCenter.x = x;
+ mCenter.y = y;
+ // when using the pie menu, align the focus ring
+ alignFocus(x, y);
+ }
+
+ private void layoutPie() {
+ int rgap = 2;
+ int inner = mRadius + rgap;
+ int outer = mRadius + mRadiusInc - rgap;
+ int gap = 1;
+ layoutItems(mItems, (float) (Math.PI / 2), inner, outer, gap);
+ }
+
+ private void layoutItems(List<PieItem> items, float centerAngle, int inner, int outer, int gap) {
+ float emptyangle = PIE_SWEEP / 16;
+ float sweep = (PIE_SWEEP - 2 * emptyangle) / items.size();
+ float angle = centerAngle - PIE_SWEEP / 2 + emptyangle + sweep / 2;
+ // check if we have custom geometry
+ // first item we find triggers custom sweep for all
+ // this allows us to re-use the path
+ for (PieItem item : items) {
+ if (item.getCenter() >= 0) {
+ sweep = item.getSweep();
+ break;
+ }
+ }
+ Path path = makeSlice(getDegrees(0) - gap, getDegrees(sweep) + gap, outer, inner, mCenter);
+ for (PieItem item : items) {
+ // shared between items
+ item.setPath(path);
+ if (item.getCenter() >= 0) {
+ angle = item.getCenter();
+ }
+ int w = item.getIntrinsicWidth();
+ int h = item.getIntrinsicHeight();
+ // move views to outer border
+ int r = inner + (outer - inner) * 2 / 3;
+ int x = (int) (r * Math.cos(angle));
+ int y = mCenter.y - (int) (r * Math.sin(angle)) - h / 2;
+ x = mCenter.x + x - w / 2;
+ item.setBounds(x, y, x + w, y + h);
+ float itemstart = angle - sweep / 2;
+ item.setGeometry(itemstart, sweep, inner, outer);
+ if (item.hasItems()) {
+ layoutItems(item.getItems(), angle, inner, outer + mRadiusInc / 2, gap);
+ }
+ angle += sweep;
+ }
+ }
+
+ private Path makeSlice(float start, float end, int outer, int inner, Point center) {
+ RectF bb = new RectF(center.x - outer, center.y - outer, center.x + outer, center.y + outer);
+ RectF bbi = new RectF(center.x - inner, center.y - inner, center.x + inner, center.y + inner);
+ Path path = new Path();
+ path.arcTo(bb, start, end - start, true);
+ path.arcTo(bbi, end, start - end);
+ path.close();
+ return path;
+ }
+
+ /**
+ * converts a
+ *
+ * @param angle from 0..PI to Android degrees (clockwise starting at 3 o'clock)
+ * @return skia angle
+ */
+ private float getDegrees(double angle) {
+ return (float) (360 - 180 * angle / Math.PI);
+ }
+
+ private void startFadeOut() {
+ mOverlay
+ .animate()
+ .alpha(0)
+ .setListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ deselect();
+ show(false);
+ mOverlay.setAlpha(1);
+ super.onAnimationEnd(animation);
+ }
+ })
+ .setDuration(PIE_SELECT_FADE_DURATION);
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ float alpha = 1;
+ if (mXFade != null) {
+ alpha = mXFade.getValue();
+ } else if (mFadeIn != null) {
+ alpha = mFadeIn.getValue();
+ }
+ int state = canvas.save();
+ if (mFadeIn != null) {
+ float sf = 0.9f + alpha * 0.1f;
+ canvas.scale(sf, sf, mCenter.x, mCenter.y);
+ }
+ drawFocus(canvas);
+ if (mState == STATE_FINISHING) {
+ canvas.restoreToCount(state);
+ return;
+ }
+ if ((mOpenItem == null) || (mXFade != null)) {
+ // draw base menu
+ for (PieItem item : mItems) {
+ drawItem(canvas, item, alpha);
+ }
+ }
+ if (mOpenItem != null) {
+ for (PieItem inner : mOpenItem.getItems()) {
+ drawItem(canvas, inner, (mXFade != null) ? (1 - 0.5f * alpha) : 1);
+ }
+ }
+ canvas.restoreToCount(state);
+ }
+
+ private void drawItem(Canvas canvas, PieItem item, float alpha) {
+ if (mState == STATE_PIE) {
+ if (item.getPath() != null) {
+ if (item.isSelected()) {
+ Paint p = mSelectedPaint;
+ int state = canvas.save();
+ float r = getDegrees(item.getStartAngle());
+ canvas.rotate(r, mCenter.x, mCenter.y);
+ canvas.drawPath(item.getPath(), p);
+ canvas.restoreToCount(state);
+ }
+ alpha = alpha * (item.isEnabled() ? 1 : 0.3f);
+ // draw the item view
+ item.setAlpha(alpha);
+ item.draw(canvas);
+ }
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent evt) {
+ float x = evt.getX();
+ float y = evt.getY();
+ int action = evt.getActionMasked();
+ PointF polar = getPolar(x, y, !(mTapMode));
+ if (MotionEvent.ACTION_DOWN == action) {
+ mDown.x = (int) evt.getX();
+ mDown.y = (int) evt.getY();
+ mOpening = false;
+ if (mTapMode) {
+ PieItem item = findItem(polar);
+ if ((item != null) && (mCurrentItem != item)) {
+ mState = STATE_PIE;
+ onEnter(item);
+ }
+ } else {
+ setCenter((int) x, (int) y);
+ show(true);
+ }
+ return true;
+ } else if (MotionEvent.ACTION_UP == action) {
+ if (isVisible()) {
+ PieItem item = mCurrentItem;
+ if (mTapMode) {
+ item = findItem(polar);
+ if (item != null && mOpening) {
+ mOpening = false;
+ return true;
+ }
+ }
+ if (item == null) {
+ mTapMode = false;
+ show(false);
+ } else if (!mOpening && !item.hasItems()) {
+ item.performClick();
+ startFadeOut();
+ mTapMode = false;
+ }
+ return true;
+ }
+ } else if (MotionEvent.ACTION_CANCEL == action) {
+ if (isVisible() || mTapMode) {
+ show(false);
+ }
+ deselect();
+ return false;
+ } else if (MotionEvent.ACTION_MOVE == action) {
+ if (polar.y < mRadius) {
+ if (mOpenItem != null) {
+ mOpenItem = null;
+ } else {
+ deselect();
+ }
+ return false;
+ }
+ PieItem item = findItem(polar);
+ boolean moved = hasMoved(evt);
+ if ((item != null) && (mCurrentItem != item) && (!mOpening || moved)) {
+ // only select if we didn't just open or have moved past slop
+ mOpening = false;
+ if (moved) {
+ // switch back to swipe mode
+ mTapMode = false;
+ }
+ onEnter(item);
+ }
+ }
+ return false;
+ }
+
+ private boolean hasMoved(MotionEvent e) {
+ return mTouchSlopSquared
+ < (e.getX() - mDown.x) * (e.getX() - mDown.x) + (e.getY() - mDown.y) * (e.getY() - mDown.y);
+ }
+
+ /**
+ * enter a slice for a view updates model only
+ *
+ * @param item
+ */
+ private void onEnter(PieItem item) {
+ if (mCurrentItem != null) {
+ mCurrentItem.setSelected(false);
+ }
+ if (item != null && item.isEnabled()) {
+ item.setSelected(true);
+ mCurrentItem = item;
+ if ((mCurrentItem != mOpenItem) && mCurrentItem.hasItems()) {
+ openCurrentItem();
+ }
+ } else {
+ mCurrentItem = null;
+ }
+ }
+
+ private void deselect() {
+ if (mCurrentItem != null) {
+ mCurrentItem.setSelected(false);
+ }
+ if (mOpenItem != null) {
+ mOpenItem = null;
+ }
+ mCurrentItem = null;
+ }
+
+ private void openCurrentItem() {
+ if ((mCurrentItem != null) && mCurrentItem.hasItems()) {
+ mCurrentItem.setSelected(false);
+ mOpenItem = mCurrentItem;
+ mOpening = true;
+ mXFade = new LinearAnimation(1, 0);
+ mXFade.setDuration(PIE_XFADE_DURATION);
+ mXFade.setAnimationListener(
+ new AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {}
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ mXFade = null;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {}
+ });
+ mXFade.startNow();
+ mOverlay.startAnimation(mXFade);
+ }
+ }
+
+ private PointF getPolar(float x, float y, boolean useOffset) {
+ PointF res = new PointF();
+ // get angle and radius from x/y
+ res.x = (float) Math.PI / 2;
+ x = x - mCenter.x;
+ y = mCenter.y - y;
+ res.y = (float) Math.sqrt(x * x + y * y);
+ if (x != 0) {
+ res.x = (float) Math.atan2(y, x);
+ if (res.x < 0) {
+ res.x = (float) (2 * Math.PI + res.x);
+ }
+ }
+ res.y = res.y + (useOffset ? mTouchOffset : 0);
+ return res;
+ }
+
+ /**
+ * @param polar x: angle, y: dist
+ * @return the item at angle/dist or null
+ */
+ private PieItem findItem(PointF polar) {
+ // find the matching item:
+ List<PieItem> items = (mOpenItem != null) ? mOpenItem.getItems() : mItems;
+ for (PieItem item : items) {
+ if (inside(polar, item)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ private boolean inside(PointF polar, PieItem item) {
+ return (item.getInnerRadius() < polar.y)
+ && (item.getStartAngle() < polar.x)
+ && (item.getStartAngle() + item.getSweep() > polar.x)
+ && (!mTapMode || (item.getOuterRadius() > polar.y));
+ }
+
+ @Override
+ public boolean handlesTouch() {
+ return true;
+ }
+
+ // focus specific code
+
+ public void setBlockFocus(boolean blocked) {
+ mBlockFocus = blocked;
+ if (blocked) {
+ clear();
+ }
+ }
+
+ public void setFocus(int x, int y) {
+ mFocusX = x;
+ mFocusY = y;
+ setCircle(mFocusX, mFocusY);
+ }
+
+ public void alignFocus(int x, int y) {
+ mOverlay.removeCallbacks(mDisappear);
+ mAnimation.cancel();
+ mAnimation.reset();
+ mFocusX = x;
+ mFocusY = y;
+ mDialAngle = DIAL_HORIZONTAL;
+ setCircle(x, y);
+ mFocused = false;
+ }
+
+ public int getSize() {
+ return 2 * mCircleSize;
+ }
+
+ private int getRandomRange() {
+ return (int) (-60 + 120 * Math.random());
+ }
+
+ @Override
+ public void layout(int l, int t, int r, int b) {
+ super.layout(l, t, r, b);
+ mCenterX = (r - l) / 2;
+ mCenterY = (b - t) / 2;
+ mFocusX = mCenterX;
+ mFocusY = mCenterY;
+ setCircle(mFocusX, mFocusY);
+ if (isVisible() && mState == STATE_PIE) {
+ setCenter(mCenterX, mCenterY);
+ layoutPie();
+ }
+ }
+
+ private void setCircle(int cx, int cy) {
+ mCircle.set(cx - mCircleSize, cy - mCircleSize, cx + mCircleSize, cy + mCircleSize);
+ mDial.set(
+ cx - mCircleSize + mInnerOffset,
+ cy - mCircleSize + mInnerOffset,
+ cx + mCircleSize - mInnerOffset,
+ cy + mCircleSize - mInnerOffset);
+ }
+
+ public void drawFocus(Canvas canvas) {
+ if (mBlockFocus) {
+ return;
+ }
+ mFocusPaint.setStrokeWidth(mOuterStroke);
+ canvas.drawCircle((float) mFocusX, (float) mFocusY, (float) mCircleSize, mFocusPaint);
+ if (mState == STATE_PIE) {
+ return;
+ }
+ int color = mFocusPaint.getColor();
+ if (mState == STATE_FINISHING) {
+ mFocusPaint.setColor(mFocused ? mSuccessColor : mFailColor);
+ }
+ mFocusPaint.setStrokeWidth(mInnerStroke);
+ drawLine(canvas, mDialAngle, mFocusPaint);
+ drawLine(canvas, mDialAngle + 45, mFocusPaint);
+ drawLine(canvas, mDialAngle + 180, mFocusPaint);
+ drawLine(canvas, mDialAngle + 225, mFocusPaint);
+ canvas.save();
+ // rotate the arc instead of its offset to better use framework's shape caching
+ canvas.rotate(mDialAngle, mFocusX, mFocusY);
+ canvas.drawArc(mDial, 0, 45, false, mFocusPaint);
+ canvas.drawArc(mDial, 180, 45, false, mFocusPaint);
+ canvas.restore();
+ mFocusPaint.setColor(color);
+ }
+
+ private void drawLine(Canvas canvas, int angle, Paint p) {
+ convertCart(angle, mCircleSize - mInnerOffset, mPoint1);
+ convertCart(angle, mCircleSize - mInnerOffset + mInnerOffset / 3, mPoint2);
+ canvas.drawLine(
+ mPoint1.x + mFocusX, mPoint1.y + mFocusY, mPoint2.x + mFocusX, mPoint2.y + mFocusY, p);
+ }
+
+ private static void convertCart(int angle, int radius, Point out) {
+ double a = 2 * Math.PI * (angle % 360) / 360;
+ out.x = (int) (radius * Math.cos(a) + 0.5);
+ out.y = (int) (radius * Math.sin(a) + 0.5);
+ }
+
+ @Override
+ public void showStart() {
+ if (mState == STATE_PIE) {
+ return;
+ }
+ cancelFocus();
+ mStartAnimationAngle = 67;
+ int range = getRandomRange();
+ startAnimation(SCALING_UP_TIME, false, mStartAnimationAngle, mStartAnimationAngle + range);
+ mState = STATE_FOCUSING;
+ }
+
+ @Override
+ public void showSuccess(boolean timeout) {
+ if (mState == STATE_FOCUSING) {
+ startAnimation(SCALING_DOWN_TIME, timeout, mStartAnimationAngle);
+ mState = STATE_FINISHING;
+ mFocused = true;
+ }
+ }
+
+ @Override
+ public void showFail(boolean timeout) {
+ if (mState == STATE_FOCUSING) {
+ startAnimation(SCALING_DOWN_TIME, timeout, mStartAnimationAngle);
+ mState = STATE_FINISHING;
+ mFocused = false;
+ }
+ }
+
+ private void cancelFocus() {
+ mFocusCancelled = true;
+ mOverlay.removeCallbacks(mDisappear);
+ if (mAnimation != null) {
+ mAnimation.cancel();
+ }
+ mFocusCancelled = false;
+ mFocused = false;
+ mState = STATE_IDLE;
+ }
+
+ @Override
+ public void clear() {
+ if (mState == STATE_PIE) {
+ return;
+ }
+ cancelFocus();
+ mOverlay.post(mDisappear);
+ }
+
+ private void startAnimation(long duration, boolean timeout, float toScale) {
+ startAnimation(duration, timeout, mDialAngle, toScale);
+ }
+
+ private void startAnimation(long duration, boolean timeout, float fromScale, float toScale) {
+ setVisible(true);
+ mAnimation.reset();
+ mAnimation.setDuration(duration);
+ mAnimation.setScale(fromScale, toScale);
+ mAnimation.setAnimationListener(timeout ? mEndAction : null);
+ mOverlay.startAnimation(mAnimation);
+ update();
+ }
+
+ private class EndAction implements Animation.AnimationListener {
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ // Keep the focus indicator for some time.
+ if (!mFocusCancelled) {
+ mOverlay.postDelayed(mDisappear, DISAPPEAR_TIMEOUT);
+ }
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {}
+
+ @Override
+ public void onAnimationStart(Animation animation) {}
+ }
+
+ private class Disappear implements Runnable {
+ @Override
+ public void run() {
+ if (mState == STATE_PIE) {
+ return;
+ }
+ setVisible(false);
+ mFocusX = mCenterX;
+ mFocusY = mCenterY;
+ mState = STATE_IDLE;
+ setCircle(mFocusX, mFocusY);
+ mFocused = false;
+ }
+ }
+
+ private class ScaleAnimation extends Animation {
+ private float mFrom = 1f;
+ private float mTo = 1f;
+
+ public ScaleAnimation() {
+ setFillAfter(true);
+ }
+
+ public void setScale(float from, float to) {
+ mFrom = from;
+ mTo = to;
+ }
+
+ @Override
+ protected void applyTransformation(float interpolatedTime, Transformation t) {
+ mDialAngle = (int) (mFrom + (mTo - mFrom) * interpolatedTime);
+ }
+ }
+
+ private static class LinearAnimation extends Animation {
+ private float mFrom;
+ private float mTo;
+ private float mValue;
+
+ public LinearAnimation(float from, float to) {
+ setFillAfter(true);
+ setInterpolator(new LinearInterpolator());
+ mFrom = from;
+ mTo = to;
+ }
+
+ public float getValue() {
+ return mValue;
+ }
+
+ @Override
+ protected void applyTransformation(float interpolatedTime, Transformation t) {
+ mValue = (mFrom + (mTo - mFrom) * interpolatedTime);
+ }
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/camerafocus/RenderOverlay.java b/java/com/android/dialer/callcomposer/camera/camerafocus/RenderOverlay.java
new file mode 100644
index 000000000..c38ae6c81
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/camerafocus/RenderOverlay.java
@@ -0,0 +1,153 @@
+/*
+ * 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.dialer.callcomposer.camera.camerafocus;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Focusing overlay. */
+public class RenderOverlay extends FrameLayout {
+
+ /** Render interface. */
+ interface Renderer {
+ boolean handlesTouch();
+
+ boolean onTouchEvent(MotionEvent evt);
+
+ void setOverlay(RenderOverlay overlay);
+
+ void layout(int left, int top, int right, int bottom);
+
+ void draw(Canvas canvas);
+ }
+
+ private RenderView mRenderView;
+ private List<Renderer> mClients;
+
+ // reverse list of touch clients
+ private List<Renderer> mTouchClients;
+ private int[] mPosition = new int[2];
+
+ public RenderOverlay(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mRenderView = new RenderView(context);
+ addView(mRenderView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ mClients = new ArrayList<>(10);
+ mTouchClients = new ArrayList<>(10);
+ setWillNotDraw(false);
+
+ addRenderer(new PieRenderer(context));
+ }
+
+ public PieRenderer getPieRenderer() {
+ for (Renderer renderer : mClients) {
+ if (renderer instanceof PieRenderer) {
+ return (PieRenderer) renderer;
+ }
+ }
+ return null;
+ }
+
+ public void addRenderer(Renderer renderer) {
+ mClients.add(renderer);
+ renderer.setOverlay(this);
+ if (renderer.handlesTouch()) {
+ mTouchClients.add(0, renderer);
+ }
+ renderer.layout(getLeft(), getTop(), getRight(), getBottom());
+ }
+
+ public void addRenderer(int pos, Renderer renderer) {
+ mClients.add(pos, renderer);
+ renderer.setOverlay(this);
+ renderer.layout(getLeft(), getTop(), getRight(), getBottom());
+ }
+
+ public void remove(Renderer renderer) {
+ mClients.remove(renderer);
+ renderer.setOverlay(null);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent m) {
+ return false;
+ }
+
+ private void adjustPosition() {
+ getLocationInWindow(mPosition);
+ }
+
+ public void update() {
+ mRenderView.invalidate();
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ private class RenderView extends View {
+
+ public RenderView(Context context) {
+ super(context);
+ setWillNotDraw(false);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent evt) {
+ if (mTouchClients != null) {
+ boolean res = false;
+ for (Renderer client : mTouchClients) {
+ res |= client.onTouchEvent(evt);
+ }
+ return res;
+ }
+ return false;
+ }
+
+ @Override
+ public void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ adjustPosition();
+ super.onLayout(changed, left, top, right, bottom);
+ if (mClients == null) {
+ return;
+ }
+ for (Renderer renderer : mClients) {
+ renderer.layout(left, top, right, bottom);
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+ if (mClients == null) {
+ return;
+ }
+ boolean redraw = false;
+ for (Renderer renderer : mClients) {
+ renderer.draw(canvas);
+ redraw = redraw || ((OverlayRenderer) renderer).isVisible();
+ }
+ if (redraw) {
+ invalidate();
+ }
+ }
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/camerafocus/res/values/dimens.xml b/java/com/android/dialer/callcomposer/camera/camerafocus/res/values/dimens.xml
new file mode 100644
index 000000000..fba631b0e
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/camerafocus/res/values/dimens.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources>
+ <!-- Camera focus indicator values -->
+ <dimen name="pie_radius_start">40dp</dimen>
+ <dimen name="pie_radius_increment">30dp</dimen>
+ <dimen name="pie_touch_offset">20dp</dimen>
+ <dimen name="focus_radius_offset">8dp</dimen>
+ <dimen name="focus_inner_offset">12dp</dimen>
+ <dimen name="focus_outer_stroke">3dp</dimen>
+ <dimen name="focus_inner_stroke">2dp</dimen>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/camera/exif/CountedDataInputStream.java b/java/com/android/dialer/callcomposer/camera/exif/CountedDataInputStream.java
new file mode 100644
index 000000000..e2c8185da
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/exif/CountedDataInputStream.java
@@ -0,0 +1,129 @@
+/*
+ * 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.dialer.callcomposer.camera.exif;
+
+import com.android.dialer.common.Assert;
+import java.io.EOFException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+
+class CountedDataInputStream extends FilterInputStream {
+
+ private int mCount = 0;
+
+ // allocate a byte buffer for a long value;
+ private final byte[] mByteArray = new byte[8];
+ private final ByteBuffer mByteBuffer = ByteBuffer.wrap(mByteArray);
+
+ CountedDataInputStream(InputStream in) {
+ super(in);
+ }
+
+ int getReadByteCount() {
+ return mCount;
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ int r = in.read(b);
+ mCount += (r >= 0) ? r : 0;
+ return r;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int r = in.read(b, off, len);
+ mCount += (r >= 0) ? r : 0;
+ return r;
+ }
+
+ @Override
+ public int read() throws IOException {
+ int r = in.read();
+ mCount += (r >= 0) ? 1 : 0;
+ return r;
+ }
+
+ @Override
+ public long skip(long length) throws IOException {
+ long skip = in.skip(length);
+ mCount += skip;
+ return skip;
+ }
+
+ private void skipOrThrow(long length) throws IOException {
+ if (skip(length) != length) {
+ throw new EOFException();
+ }
+ }
+
+ void skipTo(long target) throws IOException {
+ long cur = mCount;
+ long diff = target - cur;
+ Assert.checkArgument(diff >= 0);
+ skipOrThrow(diff);
+ }
+
+ private void readOrThrow(byte[] b, int off, int len) throws IOException {
+ int r = read(b, off, len);
+ if (r != len) {
+ throw new EOFException();
+ }
+ }
+
+ private void readOrThrow(byte[] b) throws IOException {
+ readOrThrow(b, 0, b.length);
+ }
+
+ void setByteOrder(ByteOrder order) {
+ mByteBuffer.order(order);
+ }
+
+ ByteOrder getByteOrder() {
+ return mByteBuffer.order();
+ }
+
+ short readShort() throws IOException {
+ readOrThrow(mByteArray, 0, 2);
+ mByteBuffer.rewind();
+ return mByteBuffer.getShort();
+ }
+
+ int readUnsignedShort() throws IOException {
+ return readShort() & 0xffff;
+ }
+
+ int readInt() throws IOException {
+ readOrThrow(mByteArray, 0, 4);
+ mByteBuffer.rewind();
+ return mByteBuffer.getInt();
+ }
+
+ long readUnsignedInt() throws IOException {
+ return readInt() & 0xffffffffL;
+ }
+
+ String readString(int n, Charset charset) throws IOException {
+ byte[] buf = new byte[n];
+ readOrThrow(buf);
+ return new String(buf, charset);
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/exif/ExifData.java b/java/com/android/dialer/callcomposer/camera/exif/ExifData.java
new file mode 100644
index 000000000..27936ae2f
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/exif/ExifData.java
@@ -0,0 +1,89 @@
+/*
+ * 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.dialer.callcomposer.camera.exif;
+
+/**
+ * This class stores the EXIF header in IFDs according to the JPEG specification. It is the result
+ * produced by {@link ExifReader}.
+ *
+ * @see ExifReader
+ * @see IfdData
+ */
+public class ExifData {
+
+ private final IfdData[] mIfdDatas = new IfdData[IfdId.TYPE_IFD_COUNT];
+
+ /**
+ * Adds IFD data. If IFD data of the same type already exists, it will be replaced by the new
+ * data.
+ */
+ void addIfdData(IfdData data) {
+ mIfdDatas[data.getId()] = data;
+ }
+
+ /** Returns the {@link IfdData} object corresponding to a given IFD if it exists or null. */
+ IfdData getIfdData(int ifdId) {
+ if (ExifTag.isValidIfd(ifdId)) {
+ return mIfdDatas[ifdId];
+ }
+ return null;
+ }
+
+ /**
+ * Returns the tag with a given TID in the given IFD if the tag exists. Otherwise returns null.
+ */
+ protected ExifTag getTag(short tag, int ifd) {
+ IfdData ifdData = mIfdDatas[ifd];
+ return (ifdData == null) ? null : ifdData.getTag(tag);
+ }
+
+ /**
+ * Adds the given ExifTag to its default IFD and returns an existing ExifTag with the same TID or
+ * null if none exist.
+ */
+ ExifTag addTag(ExifTag tag) {
+ if (tag != null) {
+ int ifd = tag.getIfd();
+ return addTag(tag, ifd);
+ }
+ return null;
+ }
+
+ /**
+ * Adds the given ExifTag to the given IFD and returns an existing ExifTag with the same TID or
+ * null if none exist.
+ */
+ private ExifTag addTag(ExifTag tag, int ifdId) {
+ if (tag != null && ExifTag.isValidIfd(ifdId)) {
+ IfdData ifdData = getOrCreateIfdData(ifdId);
+ return ifdData.setTag(tag);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the {@link IfdData} object corresponding to a given IFD or generates one if none exist.
+ */
+ private IfdData getOrCreateIfdData(int ifdId) {
+ IfdData ifdData = mIfdDatas[ifdId];
+ if (ifdData == null) {
+ ifdData = new IfdData(ifdId);
+ mIfdDatas[ifdId] = ifdData;
+ }
+ return ifdData;
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/exif/ExifInterface.java b/java/com/android/dialer/callcomposer/camera/exif/ExifInterface.java
new file mode 100644
index 000000000..92dee1c94
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/exif/ExifInterface.java
@@ -0,0 +1,374 @@
+/*
+ * 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.dialer.callcomposer.camera.exif;
+
+import android.annotation.SuppressLint;
+import android.graphics.Bitmap;
+import android.util.SparseIntArray;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.HashSet;
+import java.util.TimeZone;
+
+/**
+ * This class provides methods and constants for reading and writing jpeg file metadata. It contains
+ * a collection of ExifTags, and a collection of definitions for creating valid ExifTags. The
+ * collection of ExifTags can be updated by: reading new ones from a file, deleting or adding
+ * existing ones, or building new ExifTags from a tag definition. These ExifTags can be written to a
+ * valid jpeg image as exif metadata.
+ *
+ * <p>Each ExifTag has a tag ID (TID) and is stored in a specific image file directory (IFD) as
+ * specified by the exif standard. A tag definition can be looked up with a constant that is a
+ * combination of TID and IFD. This definition has information about the type, number of components,
+ * and valid IFDs for a tag.
+ *
+ * @see ExifTag
+ */
+public class ExifInterface {
+ private static final int IFD_NULL = -1;
+ static final int DEFINITION_NULL = 0;
+
+ /** Tag constants for Jeita EXIF 2.2 */
+ // IFD 0
+ public static final int TAG_ORIENTATION = defineTag(IfdId.TYPE_IFD_0, (short) 0x0112);
+
+ static final int TAG_EXIF_IFD = defineTag(IfdId.TYPE_IFD_0, (short) 0x8769);
+ static final int TAG_GPS_IFD = defineTag(IfdId.TYPE_IFD_0, (short) 0x8825);
+ static final int TAG_STRIP_OFFSETS = defineTag(IfdId.TYPE_IFD_0, (short) 0x0111);
+ static final int TAG_STRIP_BYTE_COUNTS = defineTag(IfdId.TYPE_IFD_0, (short) 0x0117);
+ // IFD 1
+ static final int TAG_JPEG_INTERCHANGE_FORMAT = defineTag(IfdId.TYPE_IFD_1, (short) 0x0201);
+ static final int TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = defineTag(IfdId.TYPE_IFD_1, (short) 0x0202);
+ // IFD Exif Tags
+ static final int TAG_INTEROPERABILITY_IFD = defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA005);
+
+ /** Tags that contain offset markers. These are included in the banned defines. */
+ private static HashSet<Short> sOffsetTags = new HashSet<>();
+
+ static {
+ sOffsetTags.add(getTrueTagKey(TAG_GPS_IFD));
+ sOffsetTags.add(getTrueTagKey(TAG_EXIF_IFD));
+ sOffsetTags.add(getTrueTagKey(TAG_JPEG_INTERCHANGE_FORMAT));
+ sOffsetTags.add(getTrueTagKey(TAG_INTEROPERABILITY_IFD));
+ sOffsetTags.add(getTrueTagKey(TAG_STRIP_OFFSETS));
+ }
+
+ private static final String NULL_ARGUMENT_STRING = "Argument is null";
+
+ private static final String GPS_DATE_FORMAT_STR = "yyyy:MM:dd";
+
+ private ExifData mData = new ExifData();
+
+ @SuppressLint("SimpleDateFormat")
+ public ExifInterface() {
+ DateFormat mGPSDateStampFormat = new SimpleDateFormat(GPS_DATE_FORMAT_STR);
+ mGPSDateStampFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ /**
+ * Reads the exif tags from a byte array, clearing this ExifInterface object's existing exif tags.
+ *
+ * @param jpeg a byte array containing a jpeg compressed image.
+ * @throws java.io.IOException
+ */
+ public void readExif(byte[] jpeg) throws IOException {
+ readExif(new ByteArrayInputStream(jpeg));
+ }
+
+ /**
+ * Reads the exif tags from an InputStream, clearing this ExifInterface object's existing exif
+ * tags.
+ *
+ * @param inStream an InputStream containing a jpeg compressed image.
+ * @throws java.io.IOException
+ */
+ private void readExif(InputStream inStream) throws IOException {
+ if (inStream == null) {
+ throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
+ }
+ ExifData d;
+ try {
+ d = new ExifReader(this).read(inStream);
+ } catch (ExifInvalidFormatException e) {
+ throw new IOException("Invalid exif format : " + e);
+ }
+ mData = d;
+ }
+
+ /** Returns the TID for a tag constant. */
+ static short getTrueTagKey(int tag) {
+ // Truncate
+ return (short) tag;
+ }
+
+ /** Returns the constant representing a tag with a given TID and default IFD. */
+ private static int defineTag(int ifdId, short tagId) {
+ return (tagId & 0x0000ffff) | (ifdId << 16);
+ }
+
+ static boolean isIfdAllowed(int info, int ifd) {
+ int[] ifds = IfdData.getIfds();
+ int ifdFlags = getAllowedIfdFlagsFromInfo(info);
+ for (int i = 0; i < ifds.length; i++) {
+ if (ifd == ifds[i] && ((ifdFlags >> i) & 1) == 1) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static int getAllowedIfdFlagsFromInfo(int info) {
+ return info >>> 24;
+ }
+
+ /**
+ * Returns true if tag TID is one of the following: {@code TAG_EXIF_IFD}, {@code TAG_GPS_IFD},
+ * {@code TAG_JPEG_INTERCHANGE_FORMAT}, {@code TAG_STRIP_OFFSETS}, {@code
+ * TAG_INTEROPERABILITY_IFD}
+ *
+ * <p>Note: defining tags with these TID's is disallowed.
+ *
+ * @param tag a tag's TID (can be obtained from a defined tag constant with {@link
+ * #getTrueTagKey}).
+ * @return true if the TID is that of an offset tag.
+ */
+ static boolean isOffsetTag(short tag) {
+ return sOffsetTags.contains(tag);
+ }
+
+ private SparseIntArray mTagInfo = null;
+
+ SparseIntArray getTagInfo() {
+ if (mTagInfo == null) {
+ mTagInfo = new SparseIntArray();
+ initTagInfo();
+ }
+ return mTagInfo;
+ }
+
+ private void initTagInfo() {
+ /**
+ * We put tag information in a 4-bytes integer. The first byte a bitmask representing the
+ * allowed IFDs of the tag, the second byte is the data type, and the last two byte are a short
+ * value indicating the default component count of this tag.
+ */
+ // IFD0 tags
+ int[] ifdAllowedIfds = {IfdId.TYPE_IFD_0, IfdId.TYPE_IFD_1};
+ int ifdFlags = getFlagsFromAllowedIfds(ifdAllowedIfds) << 24;
+ mTagInfo.put(ExifInterface.TAG_STRIP_OFFSETS, ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16);
+ mTagInfo.put(ExifInterface.TAG_EXIF_IFD, ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
+ mTagInfo.put(ExifInterface.TAG_GPS_IFD, ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
+ mTagInfo.put(ExifInterface.TAG_ORIENTATION, ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
+ mTagInfo.put(ExifInterface.TAG_STRIP_BYTE_COUNTS, ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16);
+ // IFD1 tags
+ int[] ifd1AllowedIfds = {IfdId.TYPE_IFD_1};
+ int ifdFlags1 = getFlagsFromAllowedIfds(ifd1AllowedIfds) << 24;
+ mTagInfo.put(
+ ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT,
+ ifdFlags1 | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
+ mTagInfo.put(
+ ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
+ ifdFlags1 | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
+ // Exif tags
+ int[] exifAllowedIfds = {IfdId.TYPE_IFD_EXIF};
+ int exifFlags = getFlagsFromAllowedIfds(exifAllowedIfds) << 24;
+ mTagInfo.put(
+ ExifInterface.TAG_INTEROPERABILITY_IFD, exifFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
+ }
+
+ private static int getFlagsFromAllowedIfds(int[] allowedIfds) {
+ if (allowedIfds == null || allowedIfds.length == 0) {
+ return 0;
+ }
+ int flags = 0;
+ int[] ifds = IfdData.getIfds();
+ for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
+ for (int j : allowedIfds) {
+ if (ifds[i] == j) {
+ flags |= 1 << i;
+ break;
+ }
+ }
+ }
+ return flags;
+ }
+
+ private Integer getTagIntValue(int tagId, int ifdId) {
+ int[] l = getTagIntValues(tagId, ifdId);
+ if (l == null || l.length <= 0) {
+ return null;
+ }
+ return l[0];
+ }
+
+ private int[] getTagIntValues(int tagId, int ifdId) {
+ ExifTag t = getTag(tagId, ifdId);
+ if (t == null) {
+ return null;
+ }
+ return t.getValueAsInts();
+ }
+
+ /** Gets an ExifTag for an IFD other than the tag's default. */
+ public ExifTag getTag(int tagId, int ifdId) {
+ if (!ExifTag.isValidIfd(ifdId)) {
+ return null;
+ }
+ return mData.getTag(getTrueTagKey(tagId), ifdId);
+ }
+
+ public Integer getTagIntValue(int tagId) {
+ int ifdId = getDefinedTagDefaultIfd(tagId);
+ return getTagIntValue(tagId, ifdId);
+ }
+
+ /**
+ * Gets the default IFD for a tag.
+ *
+ * @param tagId a defined tag constant, e.g. {@link #TAG_EXIF_IFD}.
+ * @return the default IFD for a tag definition or {@link #IFD_NULL} if no definition exists.
+ */
+ private int getDefinedTagDefaultIfd(int tagId) {
+ int info = getTagInfo().get(tagId);
+ if (info == DEFINITION_NULL) {
+ return IFD_NULL;
+ }
+ return getTrueIfd(tagId);
+ }
+
+ /** Returns the default IFD for a tag constant. */
+ private static int getTrueIfd(int tag) {
+ return tag >>> 16;
+ }
+
+ /**
+ * Constants for {@code TAG_ORIENTATION}. They can be interpreted as follows:
+ *
+ * <ul>
+ * <li>TOP_LEFT is the normal orientation.
+ * <li>TOP_RIGHT is a left-right mirror.
+ * <li>BOTTOM_LEFT is a 180 degree rotation.
+ * <li>BOTTOM_RIGHT is a top-bottom mirror.
+ * <li>LEFT_TOP is mirrored about the top-left<->bottom-right axis.
+ * <li>RIGHT_TOP is a 90 degree clockwise rotation.
+ * <li>LEFT_BOTTOM is mirrored about the top-right<->bottom-left axis.
+ * <li>RIGHT_BOTTOM is a 270 degree clockwise rotation.
+ * </ul>
+ */
+ interface Orientation {
+ short TOP_LEFT = 1;
+ short TOP_RIGHT = 2;
+ short BOTTOM_LEFT = 3;
+ short BOTTOM_RIGHT = 4;
+ short LEFT_TOP = 5;
+ short RIGHT_TOP = 6;
+ short LEFT_BOTTOM = 7;
+ short RIGHT_BOTTOM = 8;
+ }
+
+ /** Wrapper class to define some orientation parameters. */
+ public static class OrientationParams {
+ int rotation = 0;
+ int scaleX = 1;
+ int scaleY = 1;
+ public boolean invertDimensions = false;
+ }
+
+ public static OrientationParams getOrientationParams(int orientation) {
+ OrientationParams params = new OrientationParams();
+ switch (orientation) {
+ case Orientation.TOP_RIGHT: // Flip horizontal
+ params.scaleX = -1;
+ break;
+ case Orientation.BOTTOM_RIGHT: // Flip vertical
+ params.scaleY = -1;
+ break;
+ case Orientation.BOTTOM_LEFT: // Rotate 180
+ params.rotation = 180;
+ break;
+ case Orientation.RIGHT_BOTTOM: // Rotate 270
+ params.rotation = 270;
+ params.invertDimensions = true;
+ break;
+ case Orientation.RIGHT_TOP: // Rotate 90
+ params.rotation = 90;
+ params.invertDimensions = true;
+ break;
+ case Orientation.LEFT_TOP: // Transpose
+ params.rotation = 90;
+ params.scaleX = -1;
+ params.invertDimensions = true;
+ break;
+ case Orientation.LEFT_BOTTOM: // Transverse
+ params.rotation = 270;
+ params.scaleX = -1;
+ params.invertDimensions = true;
+ break;
+ }
+ return params;
+ }
+
+ /** Clears this ExifInterface object's existing exif tags. */
+ public void clearExif() {
+ mData = new ExifData();
+ }
+
+ /**
+ * Puts an ExifTag into this ExifInterface object's tags, removing a previous ExifTag with the
+ * same TID and IFD. The IFD it is put into will be the one the tag was created with in {@link
+ * #buildTag}.
+ *
+ * @param tag an ExifTag to put into this ExifInterface's tags.
+ * @return the previous ExifTag with the same TID and IFD or null if none exists.
+ */
+ public ExifTag setTag(ExifTag tag) {
+ return mData.addTag(tag);
+ }
+
+ /**
+ * Returns the ExifTag in that tag's default IFD for a defined tag constant or null if none
+ * exists.
+ *
+ * @param tagId a defined tag constant, e.g. {@link #TAG_EXIF_IFD}.
+ * @return an {@link ExifTag} or null if none exists.
+ */
+ public ExifTag getTag(int tagId) {
+ int ifdId = getDefinedTagDefaultIfd(tagId);
+ return getTag(tagId, ifdId);
+ }
+
+ /**
+ * Writes the tags from this ExifInterface object into a jpeg compressed bitmap, removing prior
+ * exif tags.
+ *
+ * @param bmap a bitmap to compress and write exif into.
+ * @param exifOutStream the OutputStream to which the jpeg image with added exif tags will be
+ * written.
+ * @throws java.io.IOException
+ */
+ public void writeExif(Bitmap bmap, OutputStream exifOutStream) throws IOException {
+ if (bmap == null || exifOutStream == null) {
+ throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
+ }
+ bmap.compress(Bitmap.CompressFormat.JPEG, 90, exifOutStream);
+ exifOutStream.flush();
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/exif/ExifInvalidFormatException.java b/java/com/android/dialer/callcomposer/camera/exif/ExifInvalidFormatException.java
new file mode 100644
index 000000000..92449d74f
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/exif/ExifInvalidFormatException.java
@@ -0,0 +1,24 @@
+/*
+ * 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.dialer.callcomposer.camera.exif;
+
+/** Exception for invalid exif formats. */
+public class ExifInvalidFormatException extends Exception {
+ ExifInvalidFormatException(String meg) {
+ super(meg);
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/exif/ExifParser.java b/java/com/android/dialer/callcomposer/camera/exif/ExifParser.java
new file mode 100644
index 000000000..23d748c17
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/exif/ExifParser.java
@@ -0,0 +1,846 @@
+/*
+ * 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.dialer.callcomposer.camera.exif;
+
+import android.annotation.SuppressLint;
+import com.android.dialer.common.LogUtil;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+/**
+ * This class provides a low-level EXIF parsing API. Given a JPEG format InputStream, the caller can
+ * request which IFD's to read via {@link #parse(java.io.InputStream, int)} with given options.
+ *
+ * <p>Below is an example of getting EXIF data from IFD 0 and EXIF IFD using the parser.
+ *
+ * <pre>
+ * void parse() {
+ * ExifParser parser = ExifParser.parse(mImageInputStream,
+ * ExifParser.OPTION_IFD_0 | ExifParser.OPTIONS_IFD_EXIF);
+ * int event = parser.next();
+ * while (event != ExifParser.EVENT_END) {
+ * switch (event) {
+ * case ExifParser.EVENT_START_OF_IFD:
+ * break;
+ * case ExifParser.EVENT_NEW_TAG:
+ * ExifTag tag = parser.getTag();
+ * if (!tag.hasValue()) {
+ * parser.registerForTagValue(tag);
+ * } else {
+ * processTag(tag);
+ * }
+ * break;
+ * case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
+ * tag = parser.getTag();
+ * if (tag.getDataType() != ExifTag.TYPE_UNDEFINED) {
+ * processTag(tag);
+ * }
+ * break;
+ * }
+ * event = parser.next();
+ * }
+ * }
+ *
+ * void processTag(ExifTag tag) {
+ * // process the tag as you like.
+ * }
+ * </pre>
+ */
+public class ExifParser {
+ private static final boolean LOGV = false;
+ /**
+ * When the parser reaches a new IFD area. Call {@link #getCurrentIfd()} to know which IFD we are
+ * in.
+ */
+ static final int EVENT_START_OF_IFD = 0;
+ /** When the parser reaches a new tag. Call {@link #getTag()}to get the corresponding tag. */
+ static final int EVENT_NEW_TAG = 1;
+ /**
+ * When the parser reaches the value area of tag that is registered by {@link
+ * #registerForTagValue(ExifTag)} previously. Call {@link #getTag()} to get the corresponding tag.
+ */
+ static final int EVENT_VALUE_OF_REGISTERED_TAG = 2;
+
+ /** When the parser reaches the compressed image area. */
+ static final int EVENT_COMPRESSED_IMAGE = 3;
+ /**
+ * When the parser reaches the uncompressed image strip. Call {@link #getStripIndex()} to get the
+ * index of the strip.
+ *
+ * @see #getStripIndex()
+ */
+ static final int EVENT_UNCOMPRESSED_STRIP = 4;
+ /** When there is nothing more to parse. */
+ static final int EVENT_END = 5;
+
+ /** Option bit to request to parse IFD0. */
+ private static final int OPTION_IFD_0 = 1;
+ /** Option bit to request to parse IFD1. */
+ private static final int OPTION_IFD_1 = 1 << 1;
+ /** Option bit to request to parse Exif-IFD. */
+ private static final int OPTION_IFD_EXIF = 1 << 2;
+ /** Option bit to request to parse GPS-IFD. */
+ private static final int OPTION_IFD_GPS = 1 << 3;
+ /** Option bit to request to parse Interoperability-IFD. */
+ private static final int OPTION_IFD_INTEROPERABILITY = 1 << 4;
+ /** Option bit to request to parse thumbnail. */
+ private static final int OPTION_THUMBNAIL = 1 << 5;
+
+ private static final int EXIF_HEADER = 0x45786966; // EXIF header "Exif"
+ private static final short EXIF_HEADER_TAIL = (short) 0x0000; // EXIF header in APP1
+
+ // TIFF header
+ private static final short LITTLE_ENDIAN_TAG = (short) 0x4949; // "II"
+ private static final short BIG_ENDIAN_TAG = (short) 0x4d4d; // "MM"
+ private static final short TIFF_HEADER_TAIL = 0x002A;
+
+ private static final int TAG_SIZE = 12;
+ private static final int OFFSET_SIZE = 2;
+
+ private static final Charset US_ASCII = Charset.forName("US-ASCII");
+
+ private static final int DEFAULT_IFD0_OFFSET = 8;
+
+ private final CountedDataInputStream mTiffStream;
+ private final int mOptions;
+ private int mIfdStartOffset = 0;
+ private int mNumOfTagInIfd = 0;
+ private int mIfdType;
+ private ExifTag mTag;
+ private ImageEvent mImageEvent;
+ private ExifTag mStripSizeTag;
+ private ExifTag mJpegSizeTag;
+ private boolean mNeedToParseOffsetsInCurrentIfd;
+ private boolean mContainExifData = false;
+ private int mApp1End;
+ private byte[] mDataAboveIfd0;
+ private int mIfd0Position;
+ private final ExifInterface mInterface;
+
+ private static final short TAG_EXIF_IFD = ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD);
+ private static final short TAG_GPS_IFD = ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD);
+ private static final short TAG_INTEROPERABILITY_IFD =
+ ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD);
+ private static final short TAG_JPEG_INTERCHANGE_FORMAT =
+ ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
+ private static final short TAG_JPEG_INTERCHANGE_FORMAT_LENGTH =
+ ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
+ private static final short TAG_STRIP_OFFSETS =
+ ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS);
+ private static final short TAG_STRIP_BYTE_COUNTS =
+ ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS);
+
+ private final TreeMap<Integer, Object> mCorrespondingEvent = new TreeMap<>();
+
+ private boolean isIfdRequested(int ifdType) {
+ switch (ifdType) {
+ case IfdId.TYPE_IFD_0:
+ return (mOptions & OPTION_IFD_0) != 0;
+ case IfdId.TYPE_IFD_1:
+ return (mOptions & OPTION_IFD_1) != 0;
+ case IfdId.TYPE_IFD_EXIF:
+ return (mOptions & OPTION_IFD_EXIF) != 0;
+ case IfdId.TYPE_IFD_GPS:
+ return (mOptions & OPTION_IFD_GPS) != 0;
+ case IfdId.TYPE_IFD_INTEROPERABILITY:
+ return (mOptions & OPTION_IFD_INTEROPERABILITY) != 0;
+ }
+ return false;
+ }
+
+ private boolean isThumbnailRequested() {
+ return (mOptions & OPTION_THUMBNAIL) != 0;
+ }
+
+ private ExifParser(InputStream inputStream, int options, ExifInterface iRef)
+ throws IOException, ExifInvalidFormatException {
+ if (inputStream == null) {
+ throw new IOException("Null argument inputStream to ExifParser");
+ }
+ if (LOGV) {
+ LogUtil.v("ExifParser.ExifParser", "Reading exif...");
+ }
+ mInterface = iRef;
+ mContainExifData = seekTiffData(inputStream);
+ mTiffStream = new CountedDataInputStream(inputStream);
+ mOptions = options;
+ if (!mContainExifData) {
+ return;
+ }
+
+ parseTiffHeader();
+ long offset = mTiffStream.readUnsignedInt();
+ if (offset > Integer.MAX_VALUE) {
+ throw new ExifInvalidFormatException("Invalid offset " + offset);
+ }
+ mIfd0Position = (int) offset;
+ mIfdType = IfdId.TYPE_IFD_0;
+ if (isIfdRequested(IfdId.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) {
+ registerIfd(IfdId.TYPE_IFD_0, offset);
+ if (offset != DEFAULT_IFD0_OFFSET) {
+ mDataAboveIfd0 = new byte[(int) offset - DEFAULT_IFD0_OFFSET];
+ read(mDataAboveIfd0);
+ }
+ }
+ }
+
+ /**
+ * Parses the the given InputStream with the given options
+ *
+ * @exception java.io.IOException
+ * @exception ExifInvalidFormatException
+ */
+ protected static ExifParser parse(InputStream inputStream, int options, ExifInterface iRef)
+ throws IOException, ExifInvalidFormatException {
+ return new ExifParser(inputStream, options, iRef);
+ }
+
+ /**
+ * Parses the the given InputStream with default options; that is, every IFD and thumbnaill will
+ * be parsed.
+ *
+ * @exception java.io.IOException
+ * @exception ExifInvalidFormatException
+ * @see #parse(java.io.InputStream, int, ExifInterface)
+ */
+ protected static ExifParser parse(InputStream inputStream, ExifInterface iRef)
+ throws IOException, ExifInvalidFormatException {
+ return new ExifParser(
+ inputStream,
+ OPTION_IFD_0
+ | OPTION_IFD_1
+ | OPTION_IFD_EXIF
+ | OPTION_IFD_GPS
+ | OPTION_IFD_INTEROPERABILITY
+ | OPTION_THUMBNAIL,
+ iRef);
+ }
+
+ /**
+ * Moves the parser forward and returns the next parsing event
+ *
+ * @exception java.io.IOException
+ * @exception ExifInvalidFormatException
+ * @see #EVENT_START_OF_IFD
+ * @see #EVENT_NEW_TAG
+ * @see #EVENT_VALUE_OF_REGISTERED_TAG
+ * @see #EVENT_COMPRESSED_IMAGE
+ * @see #EVENT_UNCOMPRESSED_STRIP
+ * @see #EVENT_END
+ */
+ protected int next() throws IOException, ExifInvalidFormatException {
+ if (!mContainExifData) {
+ return EVENT_END;
+ }
+ int offset = mTiffStream.getReadByteCount();
+ int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
+ if (offset < endOfTags) {
+ mTag = readTag();
+ if (mTag == null) {
+ return next();
+ }
+ if (mNeedToParseOffsetsInCurrentIfd) {
+ checkOffsetOrImageTag(mTag);
+ }
+ return EVENT_NEW_TAG;
+ } else if (offset == endOfTags) {
+ // There is a link to ifd1 at the end of ifd0
+ if (mIfdType == IfdId.TYPE_IFD_0) {
+ long ifdOffset = readUnsignedLong();
+ if (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested()) {
+ if (ifdOffset != 0) {
+ registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
+ }
+ }
+ } else {
+ int offsetSize = 4;
+ // Some camera models use invalid length of the offset
+ if (mCorrespondingEvent.size() > 0) {
+ offsetSize = mCorrespondingEvent.firstEntry().getKey() - mTiffStream.getReadByteCount();
+ }
+ if (offsetSize < 4) {
+ LogUtil.i("ExifParser.next", "Invalid size of link to next IFD: " + offsetSize);
+ } else {
+ long ifdOffset = readUnsignedLong();
+ if (ifdOffset != 0) {
+ LogUtil.i("ExifParser.next", "Invalid link to next IFD: " + ifdOffset);
+ }
+ }
+ }
+ }
+ while (mCorrespondingEvent.size() != 0) {
+ Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
+ Object event = entry.getValue();
+ try {
+ skipTo(entry.getKey());
+ } catch (IOException e) {
+ LogUtil.i(
+ "ExifParser.next",
+ "Failed to skip to data at: "
+ + entry.getKey()
+ + " for "
+ + event.getClass().getName()
+ + ", the file may be broken.");
+ continue;
+ }
+ if (event instanceof IfdEvent) {
+ mIfdType = ((IfdEvent) event).ifd;
+ mNumOfTagInIfd = mTiffStream.readUnsignedShort();
+ mIfdStartOffset = entry.getKey();
+
+ if (mNumOfTagInIfd * TAG_SIZE + mIfdStartOffset + OFFSET_SIZE > mApp1End) {
+ LogUtil.i("ExifParser.next", "Invalid size of IFD " + mIfdType);
+ return EVENT_END;
+ }
+
+ mNeedToParseOffsetsInCurrentIfd = needToParseOffsetsInCurrentIfd();
+ if (((IfdEvent) event).isRequested) {
+ return EVENT_START_OF_IFD;
+ } else {
+ skipRemainingTagsInCurrentIfd();
+ }
+ } else if (event instanceof ImageEvent) {
+ mImageEvent = (ImageEvent) event;
+ return mImageEvent.type;
+ } else {
+ ExifTagEvent tagEvent = (ExifTagEvent) event;
+ mTag = tagEvent.tag;
+ if (mTag.getDataType() != ExifTag.TYPE_UNDEFINED) {
+ readFullTagValue(mTag);
+ checkOffsetOrImageTag(mTag);
+ }
+ if (tagEvent.isRequested) {
+ return EVENT_VALUE_OF_REGISTERED_TAG;
+ }
+ }
+ }
+ return EVENT_END;
+ }
+
+ /**
+ * Skips the tags area of current IFD, if the parser is not in the tag area, nothing will happen.
+ *
+ * @throws java.io.IOException
+ * @throws ExifInvalidFormatException
+ */
+ private void skipRemainingTagsInCurrentIfd() throws IOException, ExifInvalidFormatException {
+ int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
+ int offset = mTiffStream.getReadByteCount();
+ if (offset > endOfTags) {
+ return;
+ }
+ if (mNeedToParseOffsetsInCurrentIfd) {
+ while (offset < endOfTags) {
+ mTag = readTag();
+ offset += TAG_SIZE;
+ if (mTag == null) {
+ continue;
+ }
+ checkOffsetOrImageTag(mTag);
+ }
+ } else {
+ skipTo(endOfTags);
+ }
+ long ifdOffset = readUnsignedLong();
+ // For ifd0, there is a link to ifd1 in the end of all tags
+ if (mIfdType == IfdId.TYPE_IFD_0
+ && (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested())) {
+ if (ifdOffset > 0) {
+ registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
+ }
+ }
+ }
+
+ private boolean needToParseOffsetsInCurrentIfd() {
+ switch (mIfdType) {
+ case IfdId.TYPE_IFD_0:
+ return isIfdRequested(IfdId.TYPE_IFD_EXIF)
+ || isIfdRequested(IfdId.TYPE_IFD_GPS)
+ || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)
+ || isIfdRequested(IfdId.TYPE_IFD_1);
+ case IfdId.TYPE_IFD_1:
+ return isThumbnailRequested();
+ case IfdId.TYPE_IFD_EXIF:
+ // The offset to interoperability IFD is located in Exif IFD
+ return isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY);
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * If {@link #next()} return {@link #EVENT_NEW_TAG} or {@link #EVENT_VALUE_OF_REGISTERED_TAG},
+ * call this function to get the corresponding tag.
+ *
+ * <p>For {@link #EVENT_NEW_TAG}, the tag may not contain the value if the size of the value is
+ * greater than 4 bytes. One should call {@link ExifTag#hasValue()} to check if the tag contains
+ * value. If there is no value,call {@link #registerForTagValue(ExifTag)} to have the parser emit
+ * {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area pointed by the offset.
+ *
+ * <p>When {@link #EVENT_VALUE_OF_REGISTERED_TAG} is emitted, the value of the tag will have
+ * already been read except for tags of undefined type. For tags of undefined type, call one of
+ * the read methods to get the value.
+ *
+ * @see #registerForTagValue(ExifTag)
+ * @see #read(byte[])
+ * @see #read(byte[], int, int)
+ * @see #readLong()
+ * @see #readRational()
+ * @see #readString(int)
+ * @see #readString(int, java.nio.charset.Charset)
+ */
+ protected ExifTag getTag() {
+ return mTag;
+ }
+
+ /**
+ * Gets the ID of current IFD.
+ *
+ * @see IfdId#TYPE_IFD_0
+ * @see IfdId#TYPE_IFD_1
+ * @see IfdId#TYPE_IFD_GPS
+ * @see IfdId#TYPE_IFD_INTEROPERABILITY
+ * @see IfdId#TYPE_IFD_EXIF
+ */
+ int getCurrentIfd() {
+ return mIfdType;
+ }
+
+ /**
+ * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to get the index of this
+ * strip.
+ */
+ int getStripIndex() {
+ return mImageEvent.stripIndex;
+ }
+
+ /** When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to get the strip size. */
+ int getStripSize() {
+ if (mStripSizeTag == null) {
+ return 0;
+ }
+ return (int) mStripSizeTag.getValueAt(0);
+ }
+
+ /**
+ * When receiving {@link #EVENT_COMPRESSED_IMAGE}, call this function to get the image data size.
+ */
+ int getCompressedImageSize() {
+ if (mJpegSizeTag == null) {
+ return 0;
+ }
+ return (int) mJpegSizeTag.getValueAt(0);
+ }
+
+ private void skipTo(int offset) throws IOException {
+ mTiffStream.skipTo(offset);
+ while (!mCorrespondingEvent.isEmpty() && mCorrespondingEvent.firstKey() < offset) {
+ mCorrespondingEvent.pollFirstEntry();
+ }
+ }
+
+ /**
+ * When getting {@link #EVENT_NEW_TAG} in the tag area of IFD, the tag may not contain the value
+ * if the size of the value is greater than 4 bytes. When the value is not available here, call
+ * this method so that the parser will emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches
+ * the area where the value is located.
+ *
+ * @see #EVENT_VALUE_OF_REGISTERED_TAG
+ */
+ void registerForTagValue(ExifTag tag) {
+ if (tag.getOffset() >= mTiffStream.getReadByteCount()) {
+ mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, true));
+ }
+ }
+
+ private void registerIfd(int ifdType, long offset) {
+ // Cast unsigned int to int since the offset is always smaller
+ // than the size of APP1 (65536)
+ mCorrespondingEvent.put((int) offset, new IfdEvent(ifdType, isIfdRequested(ifdType)));
+ }
+
+ private void registerCompressedImage(long offset) {
+ mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_COMPRESSED_IMAGE));
+ }
+
+ private void registerUncompressedStrip(int stripIndex, long offset) {
+ mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_UNCOMPRESSED_STRIP, stripIndex));
+ }
+
+ @SuppressLint("DefaultLocale")
+ private ExifTag readTag() throws IOException, ExifInvalidFormatException {
+ short tagId = mTiffStream.readShort();
+ short dataFormat = mTiffStream.readShort();
+ long numOfComp = mTiffStream.readUnsignedInt();
+ if (numOfComp > Integer.MAX_VALUE) {
+ throw new ExifInvalidFormatException("Number of component is larger then Integer.MAX_VALUE");
+ }
+ // Some invalid image file contains invalid data type. Ignore those tags
+ if (!ExifTag.isValidType(dataFormat)) {
+ LogUtil.i("ExifParser.readTag", "Tag %04x: Invalid data type %d", tagId, dataFormat);
+ mTiffStream.skip(4);
+ return null;
+ }
+ // TODO: handle numOfComp overflow
+ ExifTag tag =
+ new ExifTag(
+ tagId,
+ dataFormat,
+ (int) numOfComp,
+ mIfdType,
+ ((int) numOfComp) != ExifTag.SIZE_UNDEFINED);
+ int dataSize = tag.getDataSize();
+ if (dataSize > 4) {
+ long offset = mTiffStream.readUnsignedInt();
+ if (offset > Integer.MAX_VALUE) {
+ throw new ExifInvalidFormatException("offset is larger then Integer.MAX_VALUE");
+ }
+ // Some invalid images put some undefined data before IFD0.
+ // Read the data here.
+ if ((offset < mIfd0Position) && (dataFormat == ExifTag.TYPE_UNDEFINED)) {
+ byte[] buf = new byte[(int) numOfComp];
+ System.arraycopy(
+ mDataAboveIfd0, (int) offset - DEFAULT_IFD0_OFFSET, buf, 0, (int) numOfComp);
+ tag.setValue(buf);
+ } else {
+ tag.setOffset((int) offset);
+ }
+ } else {
+ boolean defCount = tag.hasDefinedCount();
+ // Set defined count to 0 so we can add \0 to non-terminated strings
+ tag.setHasDefinedCount(false);
+ // Read value
+ readFullTagValue(tag);
+ tag.setHasDefinedCount(defCount);
+ mTiffStream.skip(4 - dataSize);
+ // Set the offset to the position of value.
+ tag.setOffset(mTiffStream.getReadByteCount() - 4);
+ }
+ return tag;
+ }
+
+ /**
+ * Check the if the tag is one of the offset tag that points to the IFD or image the caller is
+ * interested in, register the IFD or image.
+ */
+ private void checkOffsetOrImageTag(ExifTag tag) {
+ // Some invalid formattd image contains tag with 0 size.
+ if (tag.getComponentCount() == 0) {
+ return;
+ }
+ short tid = tag.getTagId();
+ int ifd = tag.getIfd();
+ if (tid == TAG_EXIF_IFD && checkAllowed(ifd, ExifInterface.TAG_EXIF_IFD)) {
+ if (isIfdRequested(IfdId.TYPE_IFD_EXIF) || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
+ registerIfd(IfdId.TYPE_IFD_EXIF, tag.getValueAt(0));
+ }
+ } else if (tid == TAG_GPS_IFD && checkAllowed(ifd, ExifInterface.TAG_GPS_IFD)) {
+ if (isIfdRequested(IfdId.TYPE_IFD_GPS)) {
+ registerIfd(IfdId.TYPE_IFD_GPS, tag.getValueAt(0));
+ }
+ } else if (tid == TAG_INTEROPERABILITY_IFD
+ && checkAllowed(ifd, ExifInterface.TAG_INTEROPERABILITY_IFD)) {
+ if (isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
+ registerIfd(IfdId.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0));
+ }
+ } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT
+ && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)) {
+ if (isThumbnailRequested()) {
+ registerCompressedImage(tag.getValueAt(0));
+ }
+ } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT_LENGTH
+ && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)) {
+ if (isThumbnailRequested()) {
+ mJpegSizeTag = tag;
+ }
+ } else if (tid == TAG_STRIP_OFFSETS && checkAllowed(ifd, ExifInterface.TAG_STRIP_OFFSETS)) {
+ if (isThumbnailRequested()) {
+ if (tag.hasValue()) {
+ for (int i = 0; i < tag.getComponentCount(); i++) {
+ if (tag.getDataType() == ExifTag.TYPE_UNSIGNED_SHORT) {
+ registerUncompressedStrip(i, tag.getValueAt(i));
+ } else {
+ registerUncompressedStrip(i, tag.getValueAt(i));
+ }
+ }
+ } else {
+ mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, false));
+ }
+ }
+ } else if (tid == TAG_STRIP_BYTE_COUNTS
+ && checkAllowed(ifd, ExifInterface.TAG_STRIP_BYTE_COUNTS)
+ && isThumbnailRequested()
+ && tag.hasValue()) {
+ mStripSizeTag = tag;
+ }
+ }
+
+ private boolean checkAllowed(int ifd, int tagId) {
+ int info = mInterface.getTagInfo().get(tagId);
+ return info != ExifInterface.DEFINITION_NULL && ExifInterface.isIfdAllowed(info, ifd);
+ }
+
+ void readFullTagValue(ExifTag tag) throws IOException {
+ // Some invalid images contains tags with wrong size, check it here
+ short type = tag.getDataType();
+ if (type == ExifTag.TYPE_ASCII
+ || type == ExifTag.TYPE_UNDEFINED
+ || type == ExifTag.TYPE_UNSIGNED_BYTE) {
+ int size = tag.getComponentCount();
+ if (mCorrespondingEvent.size() > 0) {
+ if (mCorrespondingEvent.firstEntry().getKey() < mTiffStream.getReadByteCount() + size) {
+ Object event = mCorrespondingEvent.firstEntry().getValue();
+ if (event instanceof ImageEvent) {
+ // Tag value overlaps thumbnail, ignore thumbnail.
+ LogUtil.i(
+ "ExifParser.readFullTagValue",
+ "Thumbnail overlaps value for tag: \n" + tag.toString());
+ Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
+ LogUtil.i("ExifParser.readFullTagValue", "Invalid thumbnail offset: " + entry.getKey());
+ } else {
+ // Tag value overlaps another shorten count
+ if (event instanceof IfdEvent) {
+ LogUtil.i(
+ "ExifParser.readFullTagValue",
+ "Ifd " + ((IfdEvent) event).ifd + " overlaps value for tag: \n" + tag.toString());
+ } else if (event instanceof ExifTagEvent) {
+ LogUtil.i(
+ "ExifParser.readFullTagValue",
+ "Tag value for tag: \n"
+ + ((ExifTagEvent) event).tag.toString()
+ + " overlaps value for tag: \n"
+ + tag.toString());
+ }
+ size = mCorrespondingEvent.firstEntry().getKey() - mTiffStream.getReadByteCount();
+ LogUtil.i(
+ "ExifParser.readFullTagValue",
+ "Invalid size of tag: \n" + tag.toString() + " setting count to: " + size);
+ tag.forceSetComponentCount(size);
+ }
+ }
+ }
+ }
+ switch (tag.getDataType()) {
+ case ExifTag.TYPE_UNSIGNED_BYTE:
+ case ExifTag.TYPE_UNDEFINED:
+ {
+ byte[] buf = new byte[tag.getComponentCount()];
+ read(buf);
+ tag.setValue(buf);
+ }
+ break;
+ case ExifTag.TYPE_ASCII:
+ tag.setValue(readString(tag.getComponentCount()));
+ break;
+ case ExifTag.TYPE_UNSIGNED_LONG:
+ {
+ long[] value = new long[tag.getComponentCount()];
+ for (int i = 0, n = value.length; i < n; i++) {
+ value[i] = readUnsignedLong();
+ }
+ tag.setValue(value);
+ }
+ break;
+ case ExifTag.TYPE_UNSIGNED_RATIONAL:
+ {
+ Rational[] value = new Rational[tag.getComponentCount()];
+ for (int i = 0, n = value.length; i < n; i++) {
+ value[i] = readUnsignedRational();
+ }
+ tag.setValue(value);
+ }
+ break;
+ case ExifTag.TYPE_UNSIGNED_SHORT:
+ {
+ int[] value = new int[tag.getComponentCount()];
+ for (int i = 0, n = value.length; i < n; i++) {
+ value[i] = readUnsignedShort();
+ }
+ tag.setValue(value);
+ }
+ break;
+ case ExifTag.TYPE_LONG:
+ {
+ int[] value = new int[tag.getComponentCount()];
+ for (int i = 0, n = value.length; i < n; i++) {
+ value[i] = readLong();
+ }
+ tag.setValue(value);
+ }
+ break;
+ case ExifTag.TYPE_RATIONAL:
+ {
+ Rational[] value = new Rational[tag.getComponentCount()];
+ for (int i = 0, n = value.length; i < n; i++) {
+ value[i] = readRational();
+ }
+ tag.setValue(value);
+ }
+ break;
+ }
+ if (LOGV) {
+ LogUtil.v("ExifParser.readFullTagValue", "\n" + tag.toString());
+ }
+ }
+
+ private void parseTiffHeader() throws IOException, ExifInvalidFormatException {
+ short byteOrder = mTiffStream.readShort();
+ if (LITTLE_ENDIAN_TAG == byteOrder) {
+ mTiffStream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+ } else if (BIG_ENDIAN_TAG == byteOrder) {
+ mTiffStream.setByteOrder(ByteOrder.BIG_ENDIAN);
+ } else {
+ throw new ExifInvalidFormatException("Invalid TIFF header");
+ }
+
+ if (mTiffStream.readShort() != TIFF_HEADER_TAIL) {
+ throw new ExifInvalidFormatException("Invalid TIFF header");
+ }
+ }
+
+ private boolean seekTiffData(InputStream inputStream)
+ throws IOException, ExifInvalidFormatException {
+ CountedDataInputStream dataStream = new CountedDataInputStream(inputStream);
+ if (dataStream.readShort() != JpegHeader.SOI) {
+ throw new ExifInvalidFormatException("Invalid JPEG format");
+ }
+
+ short marker = dataStream.readShort();
+ while (marker != JpegHeader.EOI && !JpegHeader.isSofMarker(marker)) {
+ int length = dataStream.readUnsignedShort();
+ // Some invalid formatted image contains multiple APP1,
+ // try to find the one with Exif data.
+ if (marker == JpegHeader.APP1) {
+ int header;
+ short headerTail;
+ if (length >= 8) {
+ header = dataStream.readInt();
+ headerTail = dataStream.readShort();
+ length -= 6;
+ if (header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) {
+ mApp1End = length;
+ return true;
+ }
+ }
+ }
+ if (length < 2 || (length - 2) != dataStream.skip(length - 2)) {
+ LogUtil.i("ExifParser.seekTiffData", "Invalid JPEG format.");
+ return false;
+ }
+ marker = dataStream.readShort();
+ }
+ return false;
+ }
+
+ /** Reads bytes from the InputStream. */
+ protected int read(byte[] buffer, int offset, int length) throws IOException {
+ return mTiffStream.read(buffer, offset, length);
+ }
+
+ /** Equivalent to read(buffer, 0, buffer.length). */
+ protected int read(byte[] buffer) throws IOException {
+ return mTiffStream.read(buffer);
+ }
+
+ /**
+ * Reads a String from the InputStream with US-ASCII charset. The parser will read n bytes and
+ * convert it to ascii string. This is used for reading values of type {@link ExifTag#TYPE_ASCII}.
+ */
+ private String readString(int n) throws IOException {
+ return readString(n, US_ASCII);
+ }
+
+ /**
+ * Reads a String from the InputStream with the given charset. The parser will read n bytes and
+ * convert it to string. This is used for reading values of type {@link ExifTag#TYPE_ASCII}.
+ */
+ private String readString(int n, Charset charset) throws IOException {
+ if (n > 0) {
+ return mTiffStream.readString(n, charset);
+ } else {
+ return "";
+ }
+ }
+
+ /** Reads value of type {@link ExifTag#TYPE_UNSIGNED_SHORT} from the InputStream. */
+ private int readUnsignedShort() throws IOException {
+ return mTiffStream.readShort() & 0xffff;
+ }
+
+ /** Reads value of type {@link ExifTag#TYPE_UNSIGNED_LONG} from the InputStream. */
+ private long readUnsignedLong() throws IOException {
+ return readLong() & 0xffffffffL;
+ }
+
+ /** Reads value of type {@link ExifTag#TYPE_UNSIGNED_RATIONAL} from the InputStream. */
+ private Rational readUnsignedRational() throws IOException {
+ long nomi = readUnsignedLong();
+ long denomi = readUnsignedLong();
+ return new Rational(nomi, denomi);
+ }
+
+ /** Reads value of type {@link ExifTag#TYPE_LONG} from the InputStream. */
+ private int readLong() throws IOException {
+ return mTiffStream.readInt();
+ }
+
+ /** Reads value of type {@link ExifTag#TYPE_RATIONAL} from the InputStream. */
+ private Rational readRational() throws IOException {
+ int nomi = readLong();
+ int denomi = readLong();
+ return new Rational(nomi, denomi);
+ }
+
+ private static class ImageEvent {
+ int stripIndex;
+ int type;
+
+ ImageEvent(int type) {
+ this.stripIndex = 0;
+ this.type = type;
+ }
+
+ ImageEvent(int type, int stripIndex) {
+ this.type = type;
+ this.stripIndex = stripIndex;
+ }
+ }
+
+ private static class IfdEvent {
+ int ifd;
+ boolean isRequested;
+
+ IfdEvent(int ifd, boolean isInterestedIfd) {
+ this.ifd = ifd;
+ this.isRequested = isInterestedIfd;
+ }
+ }
+
+ private static class ExifTagEvent {
+ ExifTag tag;
+ boolean isRequested;
+
+ ExifTagEvent(ExifTag tag, boolean isRequireByUser) {
+ this.tag = tag;
+ this.isRequested = isRequireByUser;
+ }
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/exif/ExifReader.java b/java/com/android/dialer/callcomposer/camera/exif/ExifReader.java
new file mode 100644
index 000000000..89d212661
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/exif/ExifReader.java
@@ -0,0 +1,81 @@
+/*
+ * 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.dialer.callcomposer.camera.exif;
+
+import com.android.dialer.common.LogUtil;
+import java.io.IOException;
+import java.io.InputStream;
+
+/** This class reads the EXIF header of a JPEG file and stores it in {@link ExifData}. */
+class ExifReader {
+
+ private final ExifInterface mInterface;
+
+ ExifReader(ExifInterface iRef) {
+ mInterface = iRef;
+ }
+
+ /**
+ * Parses the inputStream and and returns the EXIF data in an {@link ExifData}.
+ *
+ * @throws ExifInvalidFormatException
+ * @throws java.io.IOException
+ */
+ protected ExifData read(InputStream inputStream) throws ExifInvalidFormatException, IOException {
+ ExifParser parser = ExifParser.parse(inputStream, mInterface);
+ ExifData exifData = new ExifData();
+ ExifTag tag;
+
+ int event = parser.next();
+ while (event != ExifParser.EVENT_END) {
+ switch (event) {
+ case ExifParser.EVENT_START_OF_IFD:
+ exifData.addIfdData(new IfdData(parser.getCurrentIfd()));
+ break;
+ case ExifParser.EVENT_NEW_TAG:
+ tag = parser.getTag();
+ if (!tag.hasValue()) {
+ parser.registerForTagValue(tag);
+ } else {
+ exifData.getIfdData(tag.getIfd()).setTag(tag);
+ }
+ break;
+ case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
+ tag = parser.getTag();
+ if (tag.getDataType() == ExifTag.TYPE_UNDEFINED) {
+ parser.readFullTagValue(tag);
+ }
+ exifData.getIfdData(tag.getIfd()).setTag(tag);
+ break;
+ case ExifParser.EVENT_COMPRESSED_IMAGE:
+ byte[] buf = new byte[parser.getCompressedImageSize()];
+ if (buf.length != parser.read(buf)) {
+ LogUtil.i("ExifReader.read", "Failed to read the compressed thumbnail");
+ }
+ break;
+ case ExifParser.EVENT_UNCOMPRESSED_STRIP:
+ buf = new byte[parser.getStripSize()];
+ if (buf.length != parser.read(buf)) {
+ LogUtil.i("ExifReader.read", "Failed to read the strip bytes");
+ }
+ break;
+ }
+ event = parser.next();
+ }
+ return exifData;
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/exif/ExifTag.java b/java/com/android/dialer/callcomposer/camera/exif/ExifTag.java
new file mode 100644
index 000000000..a254ae93b
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/exif/ExifTag.java
@@ -0,0 +1,619 @@
+/*
+ * 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.dialer.callcomposer.camera.exif;
+
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * This class stores information of an EXIF tag. For more information about defined EXIF tags,
+ * please read the Jeita EXIF 2.2 standard. Tags should be instantiated using {@link
+ * ExifInterface#buildTag}.
+ *
+ * @see ExifInterface
+ */
+public class ExifTag {
+ /** The BYTE type in the EXIF standard. An 8-bit unsigned integer. */
+ static final short TYPE_UNSIGNED_BYTE = 1;
+ /**
+ * The ASCII type in the EXIF standard. An 8-bit byte containing one 7-bit ASCII code. The final
+ * byte is terminated with NULL.
+ */
+ static final short TYPE_ASCII = 2;
+ /** The SHORT type in the EXIF standard. A 16-bit (2-byte) unsigned integer */
+ static final short TYPE_UNSIGNED_SHORT = 3;
+ /** The LONG type in the EXIF standard. A 32-bit (4-byte) unsigned integer */
+ static final short TYPE_UNSIGNED_LONG = 4;
+ /**
+ * The RATIONAL type of EXIF standard. It consists of two LONGs. The first one is the numerator
+ * and the second one expresses the denominator.
+ */
+ static final short TYPE_UNSIGNED_RATIONAL = 5;
+ /**
+ * The UNDEFINED type in the EXIF standard. An 8-bit byte that can take any value depending on the
+ * field definition.
+ */
+ static final short TYPE_UNDEFINED = 7;
+ /**
+ * The SLONG type in the EXIF standard. A 32-bit (4-byte) signed integer (2's complement
+ * notation).
+ */
+ static final short TYPE_LONG = 9;
+ /**
+ * The SRATIONAL type of EXIF standard. It consists of two SLONGs. The first one is the numerator
+ * and the second one is the denominator.
+ */
+ static final short TYPE_RATIONAL = 10;
+
+ private static final Charset US_ASCII = Charset.forName("US-ASCII");
+ private static final int[] TYPE_TO_SIZE_MAP = new int[11];
+ private static final int UNSIGNED_SHORT_MAX = 65535;
+ private static final long UNSIGNED_LONG_MAX = 4294967295L;
+ private static final long LONG_MAX = Integer.MAX_VALUE;
+ private static final long LONG_MIN = Integer.MIN_VALUE;
+
+ static {
+ TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_BYTE] = 1;
+ TYPE_TO_SIZE_MAP[TYPE_ASCII] = 1;
+ TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_SHORT] = 2;
+ TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_LONG] = 4;
+ TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_RATIONAL] = 8;
+ TYPE_TO_SIZE_MAP[TYPE_UNDEFINED] = 1;
+ TYPE_TO_SIZE_MAP[TYPE_LONG] = 4;
+ TYPE_TO_SIZE_MAP[TYPE_RATIONAL] = 8;
+ }
+
+ static final int SIZE_UNDEFINED = 0;
+
+ // Exif TagId
+ private final short mTagId;
+ // Exif Tag Type
+ private final short mDataType;
+ // If tag has defined count
+ private boolean mHasDefinedDefaultComponentCount;
+ // Actual data count in tag (should be number of elements in value array)
+ private int mComponentCountActual;
+ // The ifd that this tag should be put in
+ private int mIfd;
+ // The value (array of elements of type Tag Type)
+ private Object mValue;
+ // Value offset in exif header.
+ private int mOffset;
+
+ /** Returns true if the given IFD is a valid IFD. */
+ static boolean isValidIfd(int ifdId) {
+ return ifdId == IfdId.TYPE_IFD_0
+ || ifdId == IfdId.TYPE_IFD_1
+ || ifdId == IfdId.TYPE_IFD_EXIF
+ || ifdId == IfdId.TYPE_IFD_INTEROPERABILITY
+ || ifdId == IfdId.TYPE_IFD_GPS;
+ }
+
+ /** Returns true if a given type is a valid tag type. */
+ static boolean isValidType(short type) {
+ return type == TYPE_UNSIGNED_BYTE
+ || type == TYPE_ASCII
+ || type == TYPE_UNSIGNED_SHORT
+ || type == TYPE_UNSIGNED_LONG
+ || type == TYPE_UNSIGNED_RATIONAL
+ || type == TYPE_UNDEFINED
+ || type == TYPE_LONG
+ || type == TYPE_RATIONAL;
+ }
+
+ // Use builtTag in ExifInterface instead of constructor.
+ ExifTag(short tagId, short type, int componentCount, int ifd, boolean hasDefinedComponentCount) {
+ mTagId = tagId;
+ mDataType = type;
+ mComponentCountActual = componentCount;
+ mHasDefinedDefaultComponentCount = hasDefinedComponentCount;
+ mIfd = ifd;
+ mValue = null;
+ }
+
+ /**
+ * Gets the element size of the given data type in bytes.
+ *
+ * @see #TYPE_ASCII
+ * @see #TYPE_LONG
+ * @see #TYPE_RATIONAL
+ * @see #TYPE_UNDEFINED
+ * @see #TYPE_UNSIGNED_BYTE
+ * @see #TYPE_UNSIGNED_LONG
+ * @see #TYPE_UNSIGNED_RATIONAL
+ * @see #TYPE_UNSIGNED_SHORT
+ */
+ private static int getElementSize(short type) {
+ return TYPE_TO_SIZE_MAP[type];
+ }
+
+ /**
+ * Returns the ID of the IFD this tag belongs to.
+ *
+ * @see IfdId#TYPE_IFD_0
+ * @see IfdId#TYPE_IFD_1
+ * @see IfdId#TYPE_IFD_EXIF
+ * @see IfdId#TYPE_IFD_GPS
+ * @see IfdId#TYPE_IFD_INTEROPERABILITY
+ */
+ int getIfd() {
+ return mIfd;
+ }
+
+ void setIfd(int ifdId) {
+ mIfd = ifdId;
+ }
+
+ /** Gets the TID of this tag. */
+ short getTagId() {
+ return mTagId;
+ }
+
+ /**
+ * Gets the data type of this tag
+ *
+ * @see #TYPE_ASCII
+ * @see #TYPE_LONG
+ * @see #TYPE_RATIONAL
+ * @see #TYPE_UNDEFINED
+ * @see #TYPE_UNSIGNED_BYTE
+ * @see #TYPE_UNSIGNED_LONG
+ * @see #TYPE_UNSIGNED_RATIONAL
+ * @see #TYPE_UNSIGNED_SHORT
+ */
+ short getDataType() {
+ return mDataType;
+ }
+
+ /** Gets the total data size in bytes of the value of this tag. */
+ int getDataSize() {
+ return getComponentCount() * getElementSize(getDataType());
+ }
+
+ /** Gets the component count of this tag. */
+
+ // TODO: fix integer overflows with this
+ int getComponentCount() {
+ return mComponentCountActual;
+ }
+
+ /**
+ * Sets the component count of this tag. Call this function before setValue() if the length of
+ * value does not match the component count.
+ */
+ void forceSetComponentCount(int count) {
+ mComponentCountActual = count;
+ }
+
+ /**
+ * Returns true if this ExifTag contains value; otherwise, this tag will contain an offset value
+ * that is determined when the tag is written.
+ */
+ boolean hasValue() {
+ return mValue != null;
+ }
+
+ /**
+ * Sets integer values into this tag. This method should be used for tags of type {@link
+ * #TYPE_UNSIGNED_SHORT}. This method will fail if:
+ *
+ * <ul>
+ * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_SHORT}, {@link
+ * #TYPE_UNSIGNED_LONG}, or {@link #TYPE_LONG}.
+ * <li>The value overflows.
+ * <li>The value.length does NOT match the component count in the definition for this tag.
+ * </ul>
+ */
+ boolean setValue(int[] value) {
+ if (checkBadComponentCount(value.length)) {
+ return false;
+ }
+ if (mDataType != TYPE_UNSIGNED_SHORT
+ && mDataType != TYPE_LONG
+ && mDataType != TYPE_UNSIGNED_LONG) {
+ return false;
+ }
+ if (mDataType == TYPE_UNSIGNED_SHORT && checkOverflowForUnsignedShort(value)) {
+ return false;
+ } else if (mDataType == TYPE_UNSIGNED_LONG && checkOverflowForUnsignedLong(value)) {
+ return false;
+ }
+
+ long[] data = new long[value.length];
+ for (int i = 0; i < value.length; i++) {
+ data[i] = value[i];
+ }
+ mValue = data;
+ mComponentCountActual = value.length;
+ return true;
+ }
+
+ /**
+ * Sets long values into this tag. This method should be used for tags of type {@link
+ * #TYPE_UNSIGNED_LONG}. This method will fail if:
+ *
+ * <ul>
+ * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_LONG}.
+ * <li>The value overflows.
+ * <li>The value.length does NOT match the component count in the definition for this tag.
+ * </ul>
+ */
+ boolean setValue(long[] value) {
+ if (checkBadComponentCount(value.length) || mDataType != TYPE_UNSIGNED_LONG) {
+ return false;
+ }
+ if (checkOverflowForUnsignedLong(value)) {
+ return false;
+ }
+ mValue = value;
+ mComponentCountActual = value.length;
+ return true;
+ }
+
+ /**
+ * Sets a string value into this tag. This method should be used for tags of type {@link
+ * #TYPE_ASCII}. The string is converted to an ASCII string. Characters that cannot be converted
+ * are replaced with '?'. The length of the string must be equal to either (component count -1) or
+ * (component count). The final byte will be set to the string null terminator '\0', overwriting
+ * the last character in the string if the value.length is equal to the component count. This
+ * method will fail if:
+ *
+ * <ul>
+ * <li>The data type is not {@link #TYPE_ASCII} or {@link #TYPE_UNDEFINED}.
+ * <li>The length of the string is not equal to (component count -1) or (component count) in the
+ * definition for this tag.
+ * </ul>
+ */
+ boolean setValue(String value) {
+ if (mDataType != TYPE_ASCII && mDataType != TYPE_UNDEFINED) {
+ return false;
+ }
+
+ byte[] buf = value.getBytes(US_ASCII);
+ byte[] finalBuf = buf;
+ if (buf.length > 0) {
+ finalBuf =
+ (buf[buf.length - 1] == 0 || mDataType == TYPE_UNDEFINED)
+ ? buf
+ : Arrays.copyOf(buf, buf.length + 1);
+ } else if (mDataType == TYPE_ASCII && mComponentCountActual == 1) {
+ finalBuf = new byte[] {0};
+ }
+ int count = finalBuf.length;
+ if (checkBadComponentCount(count)) {
+ return false;
+ }
+ mComponentCountActual = count;
+ mValue = finalBuf;
+ return true;
+ }
+
+ /**
+ * Sets Rational values into this tag. This method should be used for tags of type {@link
+ * #TYPE_UNSIGNED_RATIONAL}, or {@link #TYPE_RATIONAL}. This method will fail if:
+ *
+ * <ul>
+ * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_RATIONAL} or {@link
+ * #TYPE_RATIONAL}.
+ * <li>The value overflows.
+ * <li>The value.length does NOT match the component count in the definition for this tag.
+ * </ul>
+ *
+ * @see Rational
+ */
+ boolean setValue(Rational[] value) {
+ if (checkBadComponentCount(value.length)) {
+ return false;
+ }
+ if (mDataType != TYPE_UNSIGNED_RATIONAL && mDataType != TYPE_RATIONAL) {
+ return false;
+ }
+ if (mDataType == TYPE_UNSIGNED_RATIONAL && checkOverflowForUnsignedRational(value)) {
+ return false;
+ } else if (mDataType == TYPE_RATIONAL && checkOverflowForRational(value)) {
+ return false;
+ }
+
+ mValue = value;
+ mComponentCountActual = value.length;
+ return true;
+ }
+
+ /**
+ * Sets byte values into this tag. This method should be used for tags of type {@link
+ * #TYPE_UNSIGNED_BYTE} or {@link #TYPE_UNDEFINED}. This method will fail if:
+ *
+ * <ul>
+ * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_BYTE} or {@link
+ * #TYPE_UNDEFINED} .
+ * <li>The length does NOT match the component count in the definition for this tag.
+ * </ul>
+ */
+ private boolean setValue(byte[] value, int offset, int length) {
+ if (checkBadComponentCount(length)) {
+ return false;
+ }
+ if (mDataType != TYPE_UNSIGNED_BYTE && mDataType != TYPE_UNDEFINED) {
+ return false;
+ }
+ mValue = new byte[length];
+ System.arraycopy(value, offset, mValue, 0, length);
+ mComponentCountActual = length;
+ return true;
+ }
+
+ /** Equivalent to setValue(value, 0, value.length). */
+ boolean setValue(byte[] value) {
+ return setValue(value, 0, value.length);
+ }
+
+ /**
+ * Gets the value as an array of ints. This method should be used for tags of type {@link
+ * #TYPE_UNSIGNED_SHORT}, {@link #TYPE_UNSIGNED_LONG}.
+ *
+ * @return the value as as an array of ints, or null if the tag's value does not exist or cannot
+ * be converted to an array of ints.
+ */
+ int[] getValueAsInts() {
+ if (mValue == null) {
+ return null;
+ } else if (mValue instanceof long[]) {
+ long[] val = (long[]) mValue;
+ int[] arr = new int[val.length];
+ for (int i = 0; i < val.length; i++) {
+ arr[i] = (int) val[i]; // Truncates
+ }
+ return arr;
+ }
+ return null;
+ }
+
+ /** Gets the tag's value or null if none exists. */
+ public Object getValue() {
+ return mValue;
+ }
+
+ /** Gets a string representation of the value. */
+ private String forceGetValueAsString() {
+ if (mValue == null) {
+ return "";
+ } else if (mValue instanceof byte[]) {
+ if (mDataType == TYPE_ASCII) {
+ return new String((byte[]) mValue, US_ASCII);
+ } else {
+ return Arrays.toString((byte[]) mValue);
+ }
+ } else if (mValue instanceof long[]) {
+ if (((long[]) mValue).length == 1) {
+ return String.valueOf(((long[]) mValue)[0]);
+ } else {
+ return Arrays.toString((long[]) mValue);
+ }
+ } else if (mValue instanceof Object[]) {
+ if (((Object[]) mValue).length == 1) {
+ Object val = ((Object[]) mValue)[0];
+ if (val == null) {
+ return "";
+ } else {
+ return val.toString();
+ }
+ } else {
+ return Arrays.toString((Object[]) mValue);
+ }
+ } else {
+ return mValue.toString();
+ }
+ }
+
+ /**
+ * Gets the value for type {@link #TYPE_ASCII}, {@link #TYPE_LONG}, {@link #TYPE_UNDEFINED},
+ * {@link #TYPE_UNSIGNED_BYTE}, {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_UNSIGNED_SHORT}.
+ *
+ * @exception IllegalArgumentException if the data type is {@link #TYPE_RATIONAL} or {@link
+ * #TYPE_UNSIGNED_RATIONAL}.
+ */
+ long getValueAt(int index) {
+ if (mValue instanceof long[]) {
+ return ((long[]) mValue)[index];
+ } else if (mValue instanceof byte[]) {
+ return ((byte[]) mValue)[index];
+ }
+ throw new IllegalArgumentException(
+ "Cannot get integer value from " + convertTypeToString(mDataType));
+ }
+
+ /**
+ * Gets the {@link #TYPE_ASCII} data.
+ *
+ * @exception IllegalArgumentException If the type is NOT {@link #TYPE_ASCII}.
+ */
+ protected String getString() {
+ if (mDataType != TYPE_ASCII) {
+ throw new IllegalArgumentException(
+ "Cannot get ASCII value from " + convertTypeToString(mDataType));
+ }
+ return new String((byte[]) mValue, US_ASCII);
+ }
+
+ /**
+ * Gets the offset of this tag. This is only valid if this data size > 4 and contains an offset to
+ * the location of the actual value.
+ */
+ protected int getOffset() {
+ return mOffset;
+ }
+
+ /** Sets the offset of this tag. */
+ protected void setOffset(int offset) {
+ mOffset = offset;
+ }
+
+ void setHasDefinedCount(boolean d) {
+ mHasDefinedDefaultComponentCount = d;
+ }
+
+ boolean hasDefinedCount() {
+ return mHasDefinedDefaultComponentCount;
+ }
+
+ private boolean checkBadComponentCount(int count) {
+ return mHasDefinedDefaultComponentCount && (mComponentCountActual != count);
+ }
+
+ private static String convertTypeToString(short type) {
+ switch (type) {
+ case TYPE_UNSIGNED_BYTE:
+ return "UNSIGNED_BYTE";
+ case TYPE_ASCII:
+ return "ASCII";
+ case TYPE_UNSIGNED_SHORT:
+ return "UNSIGNED_SHORT";
+ case TYPE_UNSIGNED_LONG:
+ return "UNSIGNED_LONG";
+ case TYPE_UNSIGNED_RATIONAL:
+ return "UNSIGNED_RATIONAL";
+ case TYPE_UNDEFINED:
+ return "UNDEFINED";
+ case TYPE_LONG:
+ return "LONG";
+ case TYPE_RATIONAL:
+ return "RATIONAL";
+ default:
+ return "";
+ }
+ }
+
+ private boolean checkOverflowForUnsignedShort(int[] value) {
+ for (int v : value) {
+ if (v > UNSIGNED_SHORT_MAX || v < 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean checkOverflowForUnsignedLong(long[] value) {
+ for (long v : value) {
+ if (v < 0 || v > UNSIGNED_LONG_MAX) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean checkOverflowForUnsignedLong(int[] value) {
+ for (int v : value) {
+ if (v < 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean checkOverflowForUnsignedRational(Rational[] value) {
+ for (Rational v : value) {
+ if (v.getNumerator() < 0
+ || v.getDenominator() < 0
+ || v.getNumerator() > UNSIGNED_LONG_MAX
+ || v.getDenominator() > UNSIGNED_LONG_MAX) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean checkOverflowForRational(Rational[] value) {
+ for (Rational v : value) {
+ if (v.getNumerator() < LONG_MIN
+ || v.getDenominator() < LONG_MIN
+ || v.getNumerator() > LONG_MAX
+ || v.getDenominator() > LONG_MAX) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (obj instanceof ExifTag) {
+ ExifTag tag = (ExifTag) obj;
+ if (tag.mTagId != this.mTagId
+ || tag.mComponentCountActual != this.mComponentCountActual
+ || tag.mDataType != this.mDataType) {
+ return false;
+ }
+ if (mValue != null) {
+ if (tag.mValue == null) {
+ return false;
+ } else if (mValue instanceof long[]) {
+ if (!(tag.mValue instanceof long[])) {
+ return false;
+ }
+ return Arrays.equals((long[]) mValue, (long[]) tag.mValue);
+ } else if (mValue instanceof Rational[]) {
+ if (!(tag.mValue instanceof Rational[])) {
+ return false;
+ }
+ return Arrays.equals((Rational[]) mValue, (Rational[]) tag.mValue);
+ } else if (mValue instanceof byte[]) {
+ if (!(tag.mValue instanceof byte[])) {
+ return false;
+ }
+ return Arrays.equals((byte[]) mValue, (byte[]) tag.mValue);
+ } else {
+ return mValue.equals(tag.mValue);
+ }
+ } else {
+ return tag.mValue == null;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mTagId,
+ mDataType,
+ mHasDefinedDefaultComponentCount,
+ mComponentCountActual,
+ mIfd,
+ mValue,
+ mOffset);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("tag id: %04X\n", mTagId)
+ + "ifd id: "
+ + mIfd
+ + "\ntype: "
+ + convertTypeToString(mDataType)
+ + "\ncount: "
+ + mComponentCountActual
+ + "\noffset: "
+ + mOffset
+ + "\nvalue: "
+ + forceGetValueAsString()
+ + "\n";
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/exif/IfdData.java b/java/com/android/dialer/callcomposer/camera/exif/IfdData.java
new file mode 100644
index 000000000..b808defc6
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/exif/IfdData.java
@@ -0,0 +1,126 @@
+/*
+ * 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.dialer.callcomposer.camera.exif;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * This class stores all the tags in an IFD.
+ *
+ * @see ExifData
+ * @see ExifTag
+ */
+class IfdData {
+
+ private final int mIfdId;
+ private final Map<Short, ExifTag> mExifTags = new HashMap<>();
+ private static final int[] sIfds = {
+ IfdId.TYPE_IFD_0,
+ IfdId.TYPE_IFD_1,
+ IfdId.TYPE_IFD_EXIF,
+ IfdId.TYPE_IFD_INTEROPERABILITY,
+ IfdId.TYPE_IFD_GPS
+ };
+ /**
+ * Creates an IfdData with given IFD ID.
+ *
+ * @see IfdId#TYPE_IFD_0
+ * @see IfdId#TYPE_IFD_1
+ * @see IfdId#TYPE_IFD_EXIF
+ * @see IfdId#TYPE_IFD_GPS
+ * @see IfdId#TYPE_IFD_INTEROPERABILITY
+ */
+ IfdData(int ifdId) {
+ mIfdId = ifdId;
+ }
+
+ static int[] getIfds() {
+ return sIfds;
+ }
+
+ /** Get a array the contains all {@link ExifTag} in this IFD. */
+ private ExifTag[] getAllTags() {
+ return mExifTags.values().toArray(new ExifTag[mExifTags.size()]);
+ }
+
+ /**
+ * Gets the ID of this IFD.
+ *
+ * @see IfdId#TYPE_IFD_0
+ * @see IfdId#TYPE_IFD_1
+ * @see IfdId#TYPE_IFD_EXIF
+ * @see IfdId#TYPE_IFD_GPS
+ * @see IfdId#TYPE_IFD_INTEROPERABILITY
+ */
+ protected int getId() {
+ return mIfdId;
+ }
+
+ /** Gets the {@link ExifTag} with given tag id. Return null if there is no such tag. */
+ protected ExifTag getTag(short tagId) {
+ return mExifTags.get(tagId);
+ }
+
+ /** Adds or replaces a {@link ExifTag}. */
+ protected ExifTag setTag(ExifTag tag) {
+ tag.setIfd(mIfdId);
+ return mExifTags.put(tag.getTagId(), tag);
+ }
+
+ /** Gets the tags count in the IFD. */
+ private int getTagCount() {
+ return mExifTags.size();
+ }
+
+ /**
+ * Returns true if all tags in this two IFDs are equal. Note that tags of IFDs offset or thumbnail
+ * offset will be ignored.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (obj instanceof IfdData) {
+ IfdData data = (IfdData) obj;
+ if (data.getId() == mIfdId && data.getTagCount() == getTagCount()) {
+ ExifTag[] tags = data.getAllTags();
+ for (ExifTag tag : tags) {
+ if (ExifInterface.isOffsetTag(tag.getTagId())) {
+ continue;
+ }
+ ExifTag tag2 = mExifTags.get(tag.getTagId());
+ if (!tag.equals(tag2)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mIfdId, mExifTags);
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/exif/IfdId.java b/java/com/android/dialer/callcomposer/camera/exif/IfdId.java
new file mode 100644
index 000000000..c61545752
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/exif/IfdId.java
@@ -0,0 +1,28 @@
+/*
+ * 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.dialer.callcomposer.camera.exif;
+
+/** The constants of the IFD ID defined in EXIF spec. */
+public interface IfdId {
+ int TYPE_IFD_0 = 0;
+ int TYPE_IFD_1 = 1;
+ int TYPE_IFD_EXIF = 2;
+ int TYPE_IFD_INTEROPERABILITY = 3;
+ int TYPE_IFD_GPS = 4;
+ /* This is used in ExifData to allocate enough IfdData */
+ int TYPE_IFD_COUNT = 5;
+}
diff --git a/java/com/android/dialer/callcomposer/camera/exif/JpegHeader.java b/java/com/android/dialer/callcomposer/camera/exif/JpegHeader.java
new file mode 100644
index 000000000..3d98fcc0e
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/exif/JpegHeader.java
@@ -0,0 +1,38 @@
+/*
+ * 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.dialer.callcomposer.camera.exif;
+
+class JpegHeader {
+ static final short SOI = (short) 0xFFD8;
+ static final short APP1 = (short) 0xFFE1;
+ static final short EOI = (short) 0xFFD9;
+
+ /**
+ * SOF (start of frame). All value between SOF0 and SOF15 is SOF marker except for DHT, JPG, and
+ * DAC marker.
+ */
+ private static final short SOF0 = (short) 0xFFC0;
+
+ private static final short SOF15 = (short) 0xFFCF;
+ private static final short DHT = (short) 0xFFC4;
+ private static final short JPG = (short) 0xFFC8;
+ private static final short DAC = (short) 0xFFCC;
+
+ static boolean isSofMarker(short marker) {
+ return marker >= SOF0 && marker <= SOF15 && marker != DHT && marker != JPG && marker != DAC;
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/camera/exif/Rational.java b/java/com/android/dialer/callcomposer/camera/exif/Rational.java
new file mode 100644
index 000000000..9afca8449
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/camera/exif/Rational.java
@@ -0,0 +1,70 @@
+/*
+ * 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.dialer.callcomposer.camera.exif;
+
+import java.util.Objects;
+
+/**
+ * The rational data type of EXIF tag. Contains a pair of longs representing the numerator and
+ * denominator of a Rational number.
+ */
+public class Rational {
+
+ private final long mNumerator;
+ private final long mDenominator;
+
+ /** Create a Rational with a given numerator and denominator. */
+ Rational(long nominator, long denominator) {
+ mNumerator = nominator;
+ mDenominator = denominator;
+ }
+
+ /** Gets the numerator of the rational. */
+ long getNumerator() {
+ return mNumerator;
+ }
+
+ /** Gets the denominator of the rational */
+ long getDenominator() {
+ return mDenominator;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof Rational) {
+ Rational data = (Rational) obj;
+ return mNumerator == data.mNumerator && mDenominator == data.mDenominator;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mNumerator, mDenominator);
+ }
+
+ @Override
+ public String toString() {
+ return mNumerator + "/" + mDenominator;
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/cameraui/AndroidManifest.xml b/java/com/android/dialer/callcomposer/cameraui/AndroidManifest.xml
new file mode 100644
index 000000000..12694ee5f
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/cameraui/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<!--
+ ~ 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
+ -->
+<manifest package="com.android.dialer.callcomposer.cameraui"/> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/cameraui/CameraMediaChooserView.java b/java/com/android/dialer/callcomposer/cameraui/CameraMediaChooserView.java
new file mode 100644
index 000000000..85c64e477
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/cameraui/CameraMediaChooserView.java
@@ -0,0 +1,107 @@
+/*
+ * 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.dialer.callcomposer.cameraui;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.hardware.Camera;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import com.android.dialer.callcomposer.camera.CameraManager;
+import com.android.dialer.callcomposer.camera.HardwareCameraPreview;
+import com.android.dialer.callcomposer.camera.SoftwareCameraPreview;
+import com.android.dialer.common.LogUtil;
+
+/** Used to display the view of the camera. */
+public class CameraMediaChooserView extends FrameLayout {
+ private static final String STATE_CAMERA_INDEX = "camera_index";
+ private static final String STATE_SUPER = "super";
+
+ // True if we have at least queued an update to the view tree to support software rendering
+ // fallback
+ private boolean mIsSoftwareFallbackActive;
+
+ public CameraMediaChooserView(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ final Bundle bundle = new Bundle();
+ bundle.putParcelable(STATE_SUPER, super.onSaveInstanceState());
+ final int cameraIndex = CameraManager.get().getCameraIndex();
+ LogUtil.i("CameraMediaChooserView.onSaveInstanceState", "saving camera index:" + cameraIndex);
+ bundle.putInt(STATE_CAMERA_INDEX, cameraIndex);
+ return bundle;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(final Parcelable state) {
+ if (!(state instanceof Bundle)) {
+ return;
+ }
+
+ final Bundle bundle = (Bundle) state;
+ final int cameraIndex = bundle.getInt(STATE_CAMERA_INDEX);
+ super.onRestoreInstanceState(bundle.getParcelable(STATE_SUPER));
+
+ LogUtil.i(
+ "CameraMediaChooserView.onRestoreInstanceState", "restoring camera index:" + cameraIndex);
+ if (cameraIndex != -1) {
+ CameraManager.get().selectCameraByIndex(cameraIndex);
+ } else {
+ resetState();
+ }
+ }
+
+ public void resetState() {
+ CameraManager.get().selectCamera(Camera.CameraInfo.CAMERA_FACING_BACK);
+ }
+
+ @Override
+ protected void onDraw(final Canvas canvas) {
+ super.onDraw(canvas);
+ // If the canvas isn't hardware accelerated, we have to replace the HardwareCameraPreview
+ // with a SoftwareCameraPreview which supports software rendering
+ if (!canvas.isHardwareAccelerated() && !mIsSoftwareFallbackActive) {
+ mIsSoftwareFallbackActive = true;
+ // Post modifying the tree since we can't modify the view tree during a draw pass
+ post(
+ new Runnable() {
+ @Override
+ public void run() {
+ final HardwareCameraPreview cameraPreview =
+ (HardwareCameraPreview) findViewById(R.id.camera_preview);
+ if (cameraPreview == null) {
+ return;
+ }
+ final ViewGroup parent = ((ViewGroup) cameraPreview.getParent());
+ final int index = parent.indexOfChild(cameraPreview);
+ final SoftwareCameraPreview softwareCameraPreview =
+ new SoftwareCameraPreview(getContext());
+ // Be sure to remove the hardware view before adding the software view to
+ // prevent having 2 camera previews active at the same time
+ parent.removeView(cameraPreview);
+ parent.addView(softwareCameraPreview, index);
+ }
+ });
+ }
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/cameraui/res/drawable-hdpi/ic_capture.png b/java/com/android/dialer/callcomposer/cameraui/res/drawable-hdpi/ic_capture.png
new file mode 100644
index 000000000..b974c9f70
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/cameraui/res/drawable-hdpi/ic_capture.png
Binary files differ
diff --git a/java/com/android/dialer/callcomposer/cameraui/res/drawable-mdpi/ic_capture.png b/java/com/android/dialer/callcomposer/cameraui/res/drawable-mdpi/ic_capture.png
new file mode 100644
index 000000000..98427587b
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/cameraui/res/drawable-mdpi/ic_capture.png
Binary files differ
diff --git a/java/com/android/dialer/callcomposer/cameraui/res/drawable-xhdpi/ic_capture.png b/java/com/android/dialer/callcomposer/cameraui/res/drawable-xhdpi/ic_capture.png
new file mode 100644
index 000000000..4ec9f75e8
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/cameraui/res/drawable-xhdpi/ic_capture.png
Binary files differ
diff --git a/java/com/android/dialer/callcomposer/cameraui/res/drawable-xxhdpi/ic_capture.png b/java/com/android/dialer/callcomposer/cameraui/res/drawable-xxhdpi/ic_capture.png
new file mode 100644
index 000000000..e2345dc86
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/cameraui/res/drawable-xxhdpi/ic_capture.png
Binary files differ
diff --git a/java/com/android/dialer/callcomposer/cameraui/res/drawable-xxxhdpi/ic_capture.png b/java/com/android/dialer/callcomposer/cameraui/res/drawable-xxxhdpi/ic_capture.png
new file mode 100644
index 000000000..3bab00984
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/cameraui/res/drawable-xxxhdpi/ic_capture.png
Binary files differ
diff --git a/java/com/android/dialer/callcomposer/cameraui/res/drawable/transparent_button_background.xml b/java/com/android/dialer/callcomposer/cameraui/res/drawable/transparent_button_background.xml
new file mode 100644
index 000000000..fda52c99c
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/cameraui/res/drawable/transparent_button_background.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:drawable="@color/background_item_grey_pressed"
+ android:state_pressed="true"/>
+ <item
+ android:drawable="@color/background_item_grey_pressed"
+ android:state_activated="true"/>
+ <item
+ android:drawable="@android:color/transparent"/>
+</selector> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/cameraui/res/layout/camera_view.xml b/java/com/android/dialer/callcomposer/cameraui/res/layout/camera_view.xml
new file mode 100644
index 000000000..75401b14b
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/cameraui/res/layout/camera_view.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<com.android.dialer.callcomposer.cameraui.CameraMediaChooserView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/camera_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/black">
+
+ <FrameLayout
+ android:id="@+id/mediapicker_enabled"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+
+ <!-- Default to using the hardware rendered camera preview, we will fall back to
+ SoftwareCameraPreview in CameraMediaChooserView if needed -->
+ <com.android.dialer.callcomposer.camera.HardwareCameraPreview
+ android:id="@+id/camera_preview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center" />
+
+ <com.android.dialer.callcomposer.camera.camerafocus.RenderOverlay
+ android:id="@+id/focus_visual"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <View
+ android:id="@+id/camera_shutter_visual"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/white"
+ android:visibility="gone" />
+
+ <!-- Need a background on this view in order for the ripple effect to have a place to draw -->
+ <FrameLayout
+ android:id="@+id/camera_button_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/transparent"
+ android:padding="16dp"
+ android:layout_gravity="bottom">
+
+ <ImageButton
+ android:id="@+id/camera_fullscreen"
+ android:layout_width="@dimen/camera_view_button_size"
+ android:layout_height="@dimen/camera_view_button_size"
+ android:layout_gravity="bottom|end"
+ android:layout_marginEnd="@dimen/camera_view_button_margin"
+ android:layout_marginBottom="@dimen/camera_view_button_margin"
+ android:src="@drawable/quantum_ic_fullscreen_white_48"
+ android:background="?android:selectableItemBackgroundBorderless"/>
+
+ <ImageButton
+ android:id="@+id/camera_exit_fullscreen"
+ android:layout_width="@dimen/camera_view_button_size"
+ android:layout_height="@dimen/camera_view_button_size"
+ android:layout_gravity="bottom|end"
+ android:layout_marginEnd="@dimen/camera_view_button_margin"
+ android:layout_marginBottom="@dimen/camera_view_button_margin"
+ android:src="@drawable/quantum_ic_fullscreen_exit_white_48"
+ android:visibility="gone"
+ android:background="?android:selectableItemBackgroundBorderless"/>
+
+ <ImageButton
+ android:id="@+id/camera_capture_button"
+ android:layout_width="@dimen/capture_button_size"
+ android:layout_height="@dimen/capture_button_size"
+ android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginBottom="@dimen/capture_button_bottom_margin"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:src="@drawable/ic_capture"
+ android:scaleType="fitXY"
+ android:contentDescription="@string/camera_take_picture"/>
+
+ <ImageButton
+ android:id="@+id/swap_camera_button"
+ android:layout_width="@dimen/camera_view_button_size"
+ android:layout_height="@dimen/camera_view_button_size"
+ android:layout_gravity="start|bottom"
+ android:layout_marginStart="@dimen/camera_view_button_margin"
+ android:layout_marginBottom="@dimen/camera_view_button_margin"
+ android:src="@drawable/front_back_switch_button_animation"
+ android:background="@drawable/transparent_button_background"
+ android:contentDescription="@string/camera_switch_camera_rear"/>
+
+ <ImageButton
+ android:id="@+id/camera_cancel_button"
+ android:layout_width="@dimen/camera_view_button_size"
+ android:layout_height="@dimen/camera_view_button_size"
+ android:layout_gravity="start|bottom"
+ android:layout_marginStart="@dimen/camera_view_button_margin"
+ android:layout_marginBottom="@dimen/camera_view_button_margin"
+ android:visibility="gone"
+ android:background="@drawable/transparent_button_background"
+ android:src="@drawable/quantum_ic_undo_white_48"
+ android:contentDescription="@string/camera_cancel_recording" />
+ </FrameLayout>
+ </FrameLayout>
+
+ <ProgressBar
+ android:id="@+id/loading"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:visibility="gone"/>
+</com.android.dialer.callcomposer.cameraui.CameraMediaChooserView> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/cameraui/res/values/colors.xml b/java/com/android/dialer/callcomposer/cameraui/res/values/colors.xml
new file mode 100644
index 000000000..d5a839aca
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/cameraui/res/values/colors.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="background_item_grey_pressed">#E0E0E0</color>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/cameraui/res/values/dimens.xml b/java/com/android/dialer/callcomposer/cameraui/res/values/dimens.xml
new file mode 100644
index 000000000..09d4a58fd
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/cameraui/res/values/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources>
+ <dimen name="camera_view_button_margin">22dp</dimen>
+ <dimen name="camera_view_button_size">46dp</dimen>
+ <dimen name="capture_button_size">84dp</dimen>
+ <dimen name="capture_button_bottom_margin">4dp</dimen>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/cameraui/res/values/strings.xml b/java/com/android/dialer/callcomposer/cameraui/res/values/strings.xml
new file mode 100644
index 000000000..999fe8f96
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/cameraui/res/values/strings.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Content description of button to switch to full screen camera -->
+ <string name="camera_switch_full_screen">Switch to full screen camera</string>
+ <!-- Content description of button when after swapped to front -->
+ <string name="camera_switch_camera_facing">Button is now front camera</string>
+ <!-- Content description of button when after swapped to back -->
+ <string name="camera_switch_camera_rear">Button is now back camera</string>
+ <!-- Content description of button to cancel recording video -->
+ <string name="camera_cancel_recording">Stop recording video</string>
+ <!-- Accessibility announcement for when we are using the front facing camera -->
+ <string name="using_front_camera">Using front camera</string>
+ <!-- Accessibility announcement for when we are using the back camera -->
+ <string name="using_back_camera">Using back camera</string>
+ <!-- Content description of button to take a photo -->
+ <string name="camera_take_picture">Take photo</string>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/nano/CallComposerContact.java b/java/com/android/dialer/callcomposer/nano/CallComposerContact.java
new file mode 100644
index 000000000..acb71a0aa
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/nano/CallComposerContact.java
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+
+package com.android.dialer.callcomposer.nano;
+
+/** This file is autogenerated, but javadoc required. */
+@SuppressWarnings("hiding")
+public final class CallComposerContact
+ extends com.google.protobuf.nano.ExtendableMessageNano<CallComposerContact> {
+
+ private static volatile CallComposerContact[] _emptyArray;
+ public static CallComposerContact[] emptyArray() {
+ // Lazily initializes the empty array
+ if (_emptyArray == null) {
+ synchronized (com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
+ if (_emptyArray == null) {
+ _emptyArray = new CallComposerContact[0];
+ }
+ }
+ }
+ return _emptyArray;
+ }
+
+ // optional fixed64 photo_id = 1;
+ public long photoId;
+
+ // optional string photo_uri = 2;
+ public java.lang.String photoUri;
+
+ // optional string contact_uri = 3;
+ public java.lang.String contactUri;
+
+ // optional string name_or_number = 4;
+ public java.lang.String nameOrNumber;
+
+ // optional bool is_business = 5;
+ public boolean isBusiness;
+
+ // optional string number = 6;
+ public java.lang.String number;
+
+ // optional string display_number = 7;
+ public java.lang.String displayNumber;
+
+ // optional string number_label = 8;
+ public java.lang.String numberLabel;
+
+ // @@protoc_insertion_point(class_scope:com.android.dialer.callcomposer.CallComposerContact)
+
+ public CallComposerContact() {
+ clear();
+ }
+
+ public CallComposerContact clear() {
+ photoId = 0L;
+ photoUri = "";
+ contactUri = "";
+ nameOrNumber = "";
+ isBusiness = false;
+ number = "";
+ displayNumber = "";
+ numberLabel = "";
+ unknownFieldData = null;
+ cachedSize = -1;
+ return this;
+ }
+
+ @Override
+ public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)
+ throws java.io.IOException {
+ if (this.photoId != 0L) {
+ output.writeFixed64(1, this.photoId);
+ }
+ if (this.photoUri != null && !this.photoUri.equals("")) {
+ output.writeString(2, this.photoUri);
+ }
+ if (this.contactUri != null && !this.contactUri.equals("")) {
+ output.writeString(3, this.contactUri);
+ }
+ if (this.nameOrNumber != null && !this.nameOrNumber.equals("")) {
+ output.writeString(4, this.nameOrNumber);
+ }
+ if (this.isBusiness != false) {
+ output.writeBool(5, this.isBusiness);
+ }
+ if (this.number != null && !this.number.equals("")) {
+ output.writeString(6, this.number);
+ }
+ if (this.displayNumber != null && !this.displayNumber.equals("")) {
+ output.writeString(7, this.displayNumber);
+ }
+ if (this.numberLabel != null && !this.numberLabel.equals("")) {
+ output.writeString(8, this.numberLabel);
+ }
+ super.writeTo(output);
+ }
+
+ @Override
+ protected int computeSerializedSize() {
+ int size = super.computeSerializedSize();
+ if (this.photoId != 0L) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeFixed64Size(1, this.photoId);
+ }
+ if (this.photoUri != null && !this.photoUri.equals("")) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeStringSize(2, this.photoUri);
+ }
+ if (this.contactUri != null && !this.contactUri.equals("")) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeStringSize(3, this.contactUri);
+ }
+ if (this.nameOrNumber != null && !this.nameOrNumber.equals("")) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeStringSize(
+ 4, this.nameOrNumber);
+ }
+ if (this.isBusiness != false) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeBoolSize(5, this.isBusiness);
+ }
+ if (this.number != null && !this.number.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano.computeStringSize(6, this.number);
+ }
+ if (this.displayNumber != null && !this.displayNumber.equals("")) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeStringSize(
+ 7, this.displayNumber);
+ }
+ if (this.numberLabel != null && !this.numberLabel.equals("")) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeStringSize(8, this.numberLabel);
+ }
+ return size;
+ }
+
+ @Override
+ public CallComposerContact mergeFrom(com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ while (true) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ return this;
+ default:
+ {
+ if (!super.storeUnknownField(input, tag)) {
+ return this;
+ }
+ break;
+ }
+ case 9:
+ {
+ this.photoId = input.readFixed64();
+ break;
+ }
+ case 18:
+ {
+ this.photoUri = input.readString();
+ break;
+ }
+ case 26:
+ {
+ this.contactUri = input.readString();
+ break;
+ }
+ case 34:
+ {
+ this.nameOrNumber = input.readString();
+ break;
+ }
+ case 40:
+ {
+ this.isBusiness = input.readBool();
+ break;
+ }
+ case 50:
+ {
+ this.number = input.readString();
+ break;
+ }
+ case 58:
+ {
+ this.displayNumber = input.readString();
+ break;
+ }
+ case 66:
+ {
+ this.numberLabel = input.readString();
+ break;
+ }
+ }
+ }
+ }
+
+ public static CallComposerContact parseFrom(byte[] data)
+ throws com.google.protobuf.nano.InvalidProtocolBufferNanoException {
+ return com.google.protobuf.nano.MessageNano.mergeFrom(new CallComposerContact(), data);
+ }
+
+ public static CallComposerContact parseFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input) throws java.io.IOException {
+ return new CallComposerContact().mergeFrom(input);
+ }
+}
diff --git a/java/com/android/dialer/callcomposer/res/drawable/call_composer_contact_border.xml b/java/com/android/dialer/callcomposer/res/drawable/call_composer_contact_border.xml
new file mode 100644
index 000000000..b3c36e9e0
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/drawable/call_composer_contact_border.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+
+ <stroke
+ android:width="@dimen/call_composer_contact_photo_border_thickness"
+ android:color="@color/background_dialer_white"/>
+
+ <padding
+ android:bottom="@dimen/call_composer_contact_photo_border_thickness"
+ android:left="@dimen/call_composer_contact_photo_border_thickness"
+ android:right="@dimen/call_composer_contact_photo_border_thickness"
+ android:top="@dimen/call_composer_contact_photo_border_thickness"/>
+</shape> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/res/drawable/gallery_background.xml b/java/com/android/dialer/callcomposer/res/drawable/gallery_background.xml
new file mode 100644
index 000000000..57dce975e
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/drawable/gallery_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/gallery_item_corner_radius"/>
+ <solid android:color="@color/gallery_item_image_color"/>
+</shape>
diff --git a/java/com/android/dialer/callcomposer/res/drawable/gallery_grid_checkbox_background.xml b/java/com/android/dialer/callcomposer/res/drawable/gallery_grid_checkbox_background.xml
new file mode 100644
index 000000000..b6b91b5a6
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/drawable/gallery_grid_checkbox_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/gallery_item_corner_radius"/>
+ <solid android:color="#80000000"/>
+</shape>
diff --git a/java/com/android/dialer/callcomposer/res/drawable/gallery_grid_item_view_background.xml b/java/com/android/dialer/callcomposer/res/drawable/gallery_grid_item_view_background.xml
new file mode 100644
index 000000000..bbae1a821
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/drawable/gallery_grid_item_view_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/gallery_item_corner_radius"/>
+ <solid android:color="@color/background_dialer_white"/>
+</shape>
diff --git a/java/com/android/dialer/callcomposer/res/drawable/gallery_item_selected_drawable.xml b/java/com/android/dialer/callcomposer/res/drawable/gallery_item_selected_drawable.xml
new file mode 100644
index 000000000..5050407c5
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/drawable/gallery_item_selected_drawable.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape
+ android:shape="oval">
+ <stroke
+ android:width="1dp"
+ android:color="@color/dialer_theme_color"/>
+ <solid
+ android:color="@color/background_dialer_white"/>
+ <size
+ android:height="@dimen/gallery_check_size"
+ android:width="@dimen/gallery_check_size"/>
+ </shape>
+ </item>
+ <item>
+ <bitmap
+ android:gravity="center"
+ android:src="@drawable/quantum_ic_check_black_24"
+ android:tint="@color/dialer_theme_color"/>
+ </item>
+</layer-list> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/res/layout/call_composer_activity.xml b/java/com/android/dialer/callcomposer/res/layout/call_composer_activity.xml
new file mode 100644
index 000000000..518b53ffd
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/layout/call_composer_activity.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/call_composer_background_color">
+
+ <LinearLayout
+ android:id="@+id/call_composer_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="bottom"
+ android:background="@android:color/transparent">
+
+ <RelativeLayout
+ android:id="@+id/contact_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:elevation="@dimen/call_composer_contact_container_elevation"
+ android:background="?android:attr/selectableItemBackground">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginTop="@dimen/call_composer_contact_container_margin_top"
+ android:paddingTop="@dimen/call_composer_contact_container_padding_top"
+ android:paddingBottom="@dimen/call_composer_contact_container_padding_bottom"
+ android:background="@color/dialer_theme_color">
+
+ <TextView
+ android:id="@+id/contact_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textColor="@color/background_dialer_white"
+ android:textSize="@dimen/call_composer_name_text_size"/>
+
+ <TextView
+ android:id="@+id/phone_number"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textColor="@color/background_dialer_white"
+ android:textSize="@dimen/call_composer_number_text_size"/>
+ </LinearLayout>
+
+ <QuickContactBadge
+ android:id="@+id/contact_photo"
+ android:layout_width="@dimen/call_composer_contact_photo_size"
+ android:layout_height="@dimen/call_composer_contact_photo_size"
+ android:layout_centerHorizontal="true"
+ android:background="@drawable/call_composer_contact_border"/>
+ </RelativeLayout>
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/call_composer_view_pager"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/call_composer_view_pager_height"/>
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:id="@+id/media_actions"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/call_composer_media_bar_height"
+ android:orientation="horizontal"
+ android:gravity="center_horizontal"
+ android:background="@color/dialer_secondary_color"
+ android:clickable="true">
+
+ <ImageView
+ android:id="@+id/call_composer_camera"
+ android:layout_width="@dimen/call_composer_media_actions_width"
+ android:layout_height="match_parent"
+ android:scaleType="center"
+ android:src="@drawable/quantum_ic_camera_alt_white_24"
+ android:background="?android:attr/selectableItemBackgroundBorderless"/>
+
+ <ImageView
+ android:id="@+id/call_composer_photo"
+ android:layout_width="@dimen/call_composer_media_actions_width"
+ android:layout_height="match_parent"
+ android:scaleType="center"
+ android:src="@drawable/quantum_ic_photo_white_24"
+ android:background="?android:attr/selectableItemBackgroundBorderless"/>
+
+ <ImageView
+ android:id="@+id/call_composer_message"
+ android:layout_width="@dimen/call_composer_media_actions_width"
+ android:layout_height="match_parent"
+ android:scaleType="center"
+ android:src="@drawable/ic_message_24dp"
+ android:background="?android:attr/selectableItemBackgroundBorderless"/>
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/send_and_call_button"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/call_composer_media_bar_height"
+ android:visibility="invisible"
+ android:background="@color/compose_and_call_background">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:drawableStart="@drawable/quantum_ic_call_white_18"
+ android:drawablePadding="@dimen/send_and_call_drawable_padding"
+ android:textAllCaps="true"
+ android:text="@string/send_and_call"
+ android:textSize="@dimen/send_and_call_text_size"
+ android:fontFamily="sans-serif-medium"
+ android:textColor="@color/background_dialer_white"/>
+ </FrameLayout>
+ </FrameLayout>
+ </LinearLayout>
+
+ <Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?attr/actionBarSize"
+ android:visibility="invisible"
+ android:titleTextAppearance="@style/call_composer_toolbar_title_text"
+ android:subtitleTextAppearance="@style/call_composer_toolbar_subtitle_text"
+ android:navigationIcon="@drawable/quantum_ic_close_white_24"
+ android:background="@color/dialer_theme_color"/>
+</FrameLayout> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/res/layout/fragment_camera_composer.xml b/java/com/android/dialer/callcomposer/res/layout/fragment_camera_composer.xml
new file mode 100644
index 000000000..200a3dce7
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/layout/fragment_camera_composer.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ layout="@layout/camera_view"/>
+
+ <include
+ android:id="@+id/permission_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ layout="@layout/permission_view"/>
+</FrameLayout>
diff --git a/java/com/android/dialer/callcomposer/res/layout/fragment_gallery_composer.xml b/java/com/android/dialer/callcomposer/res/layout/fragment_gallery_composer.xml
new file mode 100644
index 000000000..58893ba50
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/layout/fragment_gallery_composer.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/background_dialer_white">
+
+ <GridView
+ android:id="@+id/gallery_grid_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/gallery_item_padding"
+ android:paddingRight="@dimen/gallery_item_padding"
+ android:paddingTop="@dimen/gallery_item_padding"
+ android:numColumns="3"/>
+
+ <include
+ android:id="@+id/permission_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ layout="@layout/permission_view"/>
+</FrameLayout> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/res/layout/fragment_message_composer.xml b/java/com/android/dialer/callcomposer/res/layout/fragment_message_composer.xml
new file mode 100644
index 000000000..97f232b3a
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/layout/fragment_message_composer.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/call_composer_view_pager_height"
+ android:orientation="vertical"
+ android:gravity="bottom"
+ android:background="@color/background_dialer_white">
+
+ <TextView
+ android:id="@+id/message_urgent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/urgent"
+ style="@style/message_composer_textview"/>
+
+ <TextView
+ android:id="@+id/message_chat"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/want_to_chat"
+ style="@style/message_composer_textview"/>
+
+ <TextView
+ android:id="@+id/message_question"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/quick_question"
+ style="@style/message_composer_textview"/>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/message_composer_divider_height"
+ android:background="@color/call_composer_divider"/>
+
+ <RelativeLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <EditText
+ android:id="@+id/custom_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="@dimen/message_composer_item_padding"
+ android:textSize="@dimen/message_compose_item_text_size"
+ android:hint="@string/custom_message_hint"
+ android:textColor="@color/dialer_primary_text_color"
+ android:textColorHint="@color/dialer_edit_text_hint_color"
+ android:background="@color/background_dialer_white"
+ android:textCursorDrawable="@drawable/searchedittext_custom_cursor"
+ android:layout_toLeftOf="@+id/remaining_characters"/>
+
+ <TextView
+ android:id="@+id/remaining_characters"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="@dimen/message_composer_item_padding"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:textSize="@dimen/message_compose_remaining_char_text_size"
+ android:textColor="@color/dialer_edit_text_hint_color"/>
+ </RelativeLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/res/layout/gallery_grid_item_view.xml b/java/com/android/dialer/callcomposer/res/layout/gallery_grid_item_view.xml
new file mode 100644
index 000000000..6c68517bd
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/layout/gallery_grid_item_view.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<com.android.dialer.callcomposer.GalleryGridItemView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="@dimen/gallery_item_padding"
+ android:clickable="true">
+
+ <ImageView
+ android:id="@+id/image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/gallery_grid_item_view_background"
+ android:outlineProvider="background"
+ android:scaleType="centerCrop"/>
+
+ <FrameLayout
+ android:id="@+id/checkbox"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/gallery_grid_checkbox_background"
+ android:outlineProvider="background"
+ android:visibility="gone">
+
+ <ImageView
+ android:layout_width="@dimen/gallery_check_size"
+ android:layout_height="@dimen/gallery_check_size"
+ android:layout_gravity="center"
+ android:src="@drawable/gallery_item_selected_drawable"/>
+ </FrameLayout>
+
+ <ImageView
+ android:id="@+id/gallery"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:src="@drawable/quantum_ic_photo_library_white_24"
+ android:scaleType="center"
+ android:background="@drawable/gallery_background"
+ android:outlineProvider="background"
+ android:visibility="gone"/>
+</com.android.dialer.callcomposer.GalleryGridItemView> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/res/layout/permission_view.xml b/java/com/android/dialer/callcomposer/res/layout/permission_view.xml
new file mode 100644
index 000000000..4daa11d62
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/layout/permission_view.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:clickable="true"
+ android:background="@color/background_dialer_white">
+
+ <ImageView
+ android:id="@+id/permission_icon"
+ android:layout_width="@dimen/permission_image_size"
+ android:layout_height="@dimen/permission_image_size"
+ android:layout_margin="@dimen/permission_item_margin"/>
+
+ <TextView
+ android:id="@+id/permission_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/permission_item_margin"
+ style="@style/TextAppearanceMedium"/>
+
+ <TextView
+ android:id="@+id/allow"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/min_touch_target_size"
+ android:minWidth="@dimen/min_touch_target_size"
+ android:gravity="center"
+ android:text="@string/allow"
+ android:textAllCaps="true"
+ android:textSize="@dimen/allow_permission_text_size"
+ android:textColor="@color/dialer_theme_color"
+ android:background="?android:attr/selectableItemBackground"
+ android:padding="@dimen/permission_allow_padding"
+ android:theme="@style/Theme.AppCompat.Light"/>
+</LinearLayout> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/res/values/colors.xml b/java/com/android/dialer/callcomposer/res/values/colors.xml
new file mode 100644
index 000000000..89e55b79a
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/values/colors.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources>
+ <!-- 50% black -->
+ <color name="call_composer_background_color">#7F000000</color>
+ <color name="call_composer_divider">#12000000</color>
+ <color name="compose_and_call_background">#00BC35</color>
+ <color name="gallery_item_image_color">#607D8B</color>
+ <color name="gallery_item_background_color">#ECEFF1</color>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/res/values/dimens.xml b/java/com/android/dialer/callcomposer/res/values/dimens.xml
new file mode 100644
index 000000000..3ebda7a0f
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/values/dimens.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources>
+ <dimen name="call_composer_view_pager_height">258dp</dimen>
+
+ <!-- Toolbar -->
+ <dimen name="toolbar_title_text_size">16sp</dimen>
+ <dimen name="toolbar_subtitle_text_size">14sp</dimen>
+
+ <!-- Contact bar -->
+ <dimen name="call_composer_contact_photo_border_thickness">2dp</dimen>
+ <dimen name="call_composer_contact_photo_size">116dp</dimen>
+ <dimen name="call_composer_contact_container_margin_top">58dp</dimen>
+ <dimen name="call_composer_contact_container_padding_top">58dp</dimen>
+ <dimen name="call_composer_contact_container_padding_bottom">18dp</dimen>
+ <dimen name="call_composer_name_text_size">32sp</dimen>
+ <dimen name="call_composer_number_text_size">16sp</dimen>
+ <dimen name="call_composer_contact_container_elevation">2dp</dimen>
+
+ <!-- Media bar -->
+ <dimen name="call_composer_media_actions_width">80dp</dimen>
+ <dimen name="call_composer_media_bar_height">48dp</dimen>
+
+ <!-- Send and Call button -->
+ <dimen name="send_and_call_icon_size">18dp</dimen>
+ <dimen name="send_and_call_text_size">16sp</dimen>
+ <dimen name="send_and_call_padding">8dp</dimen>
+ <dimen name="send_and_call_drawable_padding">4dp</dimen>
+
+ <!-- Message Composer -->
+ <dimen name="message_composer_item_padding">16dp</dimen>
+ <dimen name="message_compose_item_text_size">16sp</dimen>
+ <dimen name="message_compose_remaining_char_text_size">12sp</dimen>
+ <dimen name="message_composer_divider_height">1dp</dimen>
+ <integer name="call_composer_message_limit">60</integer>
+
+ <!-- Gallery Composer -->
+ <dimen name="gallery_item_selected_padding">6dp</dimen>
+ <dimen name="gallery_item_padding">3dp</dimen>
+ <dimen name="gallery_check_size">48dp</dimen>
+ <dimen name="gallery_item_corner_radius">2dp</dimen>
+
+ <!-- Permissions view -->
+ <dimen name="permission_image_size">72dp</dimen>
+ <dimen name="allow_permission_text_size">16sp</dimen>
+ <dimen name="permission_item_margin">8dp</dimen>
+ <dimen name="permission_allow_padding">16dp</dimen>
+ <dimen name="min_touch_target_size">48dp</dimen>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/res/values/strings.xml b/java/com/android/dialer/callcomposer/res/values/strings.xml
new file mode 100644
index 000000000..35a8cf9da
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/values/strings.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- A default message to send with a phone call. [CHAR LIMIT=27] -->
+ <string name="urgent">Urgent! Please pick up!</string>
+ <!-- A default message to send with a phone call. [CHAR LIMIT=27] -->
+ <string name="want_to_chat">Want to chat?</string>
+ <!-- A default message to send with a phone call. [CHAR LIMIT=27] -->
+ <string name="quick_question">Quick question…</string>
+ <!-- Hint in a text field to compose a custom message to send with a phone call [CHAR LIMIT=27] -->
+ <string name="custom_message_hint">Write a custom message</string>
+ <!-- Text for a button to make a phone call combined with a picture or text message [CHAR LIMIT=26] -->
+ <string name="send_and_call">Send and call</string>
+ <!-- Accessibility description for each image in the gallery. For example, "image January 17 2015 1 59 pm". -->
+ <string name="gallery_item_description">image <xliff:g id="date">%1$tB %1$te %1$tY %1$tl %1$tM %1$tp</xliff:g></string>
+ <!-- Accessibility description for each image in the gallery when no date is present. -->
+ <string name="gallery_item_description_no_date">image</string>
+ <!-- Content description of button to switch camera to picture more -->
+ <string name="camera_switch_to_still_mode">Take a photo</string>
+ <!-- Error toast message shown when a camera image failed to attach to the message -->
+ <string name="camera_media_failure">Couldn\'t load camera image</string>
+ <!-- Text for a button to ask for device permissions -->
+ <string name="allow">Allow</string>
+ <!-- Text presented to the user explaining that we need Camera permission to take photos -->
+ <string name="camera_permission_text">To take a photo, give access to Camera</string>
+ <!-- Text presented to the user explaining that we need device storage permission to view photos -->
+ <string name="gallery_permission_text">To share an image, give access to Media</string>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/res/values/styles.xml b/java/com/android/dialer/callcomposer/res/values/styles.xml
new file mode 100644
index 000000000..891f6397d
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/values/styles.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources>
+ <style name="Theme.AppCompat.CallComposer" parent="Theme.AppCompat.NoActionBar">
+ <item name="android:colorPrimaryDark">@color/dialer_theme_color_dark</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:colorBackgroundCacheHint">@null</item>
+ <item name="android:windowFrame">@null</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowAnimationStyle">@null</item>
+ <item name="android:windowIsFloating">false</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:listViewStyle">@style/ListViewStyle</item>
+ <!-- We need to use a light ripple behind ActionBar items in order for them to
+ be visible when using some of the darker ActionBar tints -->
+ <item name="android:actionBarItemBackground">@drawable/item_background_material_borderless_dark</item>
+ </style>
+
+ <style name="message_composer_textview">
+ <item name="android:textSize">@dimen/message_compose_item_text_size</item>
+ <item name="android:textColor">@color/dialer_primary_text_color</item>
+ <item name="android:padding">@dimen/message_composer_item_padding</item>
+ <item name="android:background">@drawable/item_background_material_light</item>
+ </style>
+
+ <style name="call_composer_toolbar_title_text">
+ <item name="android:textSize">@dimen/toolbar_title_text_size</item>
+ <item name="android:textColor">@color/background_dialer_white</item>
+ </style>
+
+ <style name="call_composer_toolbar_subtitle_text">
+ <item name="android:textSize">@dimen/toolbar_subtitle_text_size</item>
+ <item name="android:textColor">@color/background_dialer_white</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/util/CopyAndResizeImageTask.java b/java/com/android/dialer/callcomposer/util/CopyAndResizeImageTask.java
new file mode 100644
index 000000000..83580fd38
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/util/CopyAndResizeImageTask.java
@@ -0,0 +1,124 @@
+/*
+ * 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.dialer.callcomposer.util;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.FallibleAsyncTask;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.util.DialerUtils;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+
+/** Task for copying and resizing images to be shared with RCS process. */
+@TargetApi(VERSION_CODES.M)
+public class CopyAndResizeImageTask extends FallibleAsyncTask<Void, Void, File> {
+ public static final int MAX_OUTPUT_RESOLUTION = 1024;
+ private static final String MIME_TYPE = "image/jpeg";
+
+ private final Context context;
+ private final Uri uri;
+ private final Callback callback;
+
+ public CopyAndResizeImageTask(
+ @NonNull Context context, @NonNull Uri uri, @NonNull Callback callback) {
+ this.context = Assert.isNotNull(context);
+ this.uri = Assert.isNotNull(uri);
+ this.callback = Assert.isNotNull(callback);
+ }
+
+ @Nullable
+ @Override
+ protected File doInBackgroundFallible(Void... params) throws Throwable {
+ Bitmap bitmap = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri));
+ bitmap = resizeForEnrichedCalling(bitmap);
+
+ File outputFile = DialerUtils.createShareableFile(context);
+ try (OutputStream outputStream = new FileOutputStream(outputFile)) {
+ // Encode images to jpeg as it is better for camera pictures which we expect to be sending
+ bitmap.compress(CompressFormat.JPEG, 90, outputStream);
+ return outputFile;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(FallibleTaskResult<File> result) {
+ if (result.isFailure()) {
+ callback.onCopyFailed(result.getThrowable());
+ } else {
+ callback.onCopySuccessful(result.getResult(), MIME_TYPE);
+ }
+ }
+
+ public static Bitmap resizeForEnrichedCalling(Bitmap image) {
+ Assert.isWorkerThread();
+
+ int width = image.getWidth();
+ int height = image.getHeight();
+
+ LogUtil.i(
+ "CopyAndResizeImageTask.resizeForEnrichedCalling",
+ "starting height: %d, width: %d",
+ height,
+ width);
+
+ if (width <= MAX_OUTPUT_RESOLUTION && height <= MAX_OUTPUT_RESOLUTION) {
+ LogUtil.i("CopyAndResizeImageTask.resizeForEnrichedCalling", "no resizing needed");
+ return image;
+ }
+
+ if (width > height) {
+ // landscape
+ float ratio = width / (float) MAX_OUTPUT_RESOLUTION;
+ width = MAX_OUTPUT_RESOLUTION;
+ height = (int) (height / ratio);
+ } else if (height > width) {
+ // portrait
+ float ratio = height / (float) MAX_OUTPUT_RESOLUTION;
+ height = MAX_OUTPUT_RESOLUTION;
+ width = (int) (width / ratio);
+ } else {
+ // square
+ height = MAX_OUTPUT_RESOLUTION;
+ width = MAX_OUTPUT_RESOLUTION;
+ }
+
+ LogUtil.i(
+ "CopyAndResizeImageTask.resizeForEnrichedCalling",
+ "ending height: %d, width: %d",
+ height,
+ width);
+
+ return Bitmap.createScaledBitmap(image, width, height, true);
+ }
+
+ /** Callback for callers to know when the task has finished */
+ public interface Callback {
+ void onCopySuccessful(File file, String mimeType);
+
+ void onCopyFailed(Throwable throwable);
+ }
+}
diff --git a/java/com/android/dialer/callintent/CallIntentBuilder.java b/java/com/android/dialer/callintent/CallIntentBuilder.java
new file mode 100644
index 000000000..a2fb564ab
--- /dev/null
+++ b/java/com/android/dialer/callintent/CallIntentBuilder.java
@@ -0,0 +1,108 @@
+/*
+ * 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.dialer.callintent;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
+import android.text.TextUtils;
+import com.android.dialer.callintent.nano.CallInitiationType;
+import com.android.dialer.callintent.nano.CallSpecificAppData;
+import com.android.dialer.common.Assert;
+import com.android.dialer.util.CallUtil;
+
+/** Creates an intent to start a new outgoing call. */
+public class CallIntentBuilder {
+ private final Uri uri;
+ private final CallSpecificAppData callSpecificAppData;
+ @Nullable private PhoneAccountHandle phoneAccountHandle;
+ private boolean isVideoCall;
+ private String callSubject;
+
+ public CallIntentBuilder(@NonNull Uri uri, @NonNull CallSpecificAppData callSpecificAppData) {
+ this.uri = Assert.isNotNull(uri);
+ this.callSpecificAppData = Assert.isNotNull(callSpecificAppData);
+ Assert.checkArgument(
+ callSpecificAppData.callInitiationType != CallInitiationType.Type.UNKNOWN_INITIATION);
+ }
+
+ public CallIntentBuilder(@NonNull Uri uri, int callInitiationType) {
+ this(uri, createCallSpecificAppData(callInitiationType));
+ }
+
+ public CallIntentBuilder(
+ @NonNull String number, @NonNull CallSpecificAppData callSpecificAppData) {
+ this(CallUtil.getCallUri(Assert.isNotNull(number)), callSpecificAppData);
+ }
+
+ public CallIntentBuilder(@NonNull String number, int callInitiationType) {
+ this(CallUtil.getCallUri(Assert.isNotNull(number)), callInitiationType);
+ }
+
+ public CallSpecificAppData getCallSpecificAppData() {
+ return callSpecificAppData;
+ }
+
+ public CallIntentBuilder setPhoneAccountHandle(@Nullable PhoneAccountHandle accountHandle) {
+ this.phoneAccountHandle = accountHandle;
+ return this;
+ }
+
+ public CallIntentBuilder setIsVideoCall(boolean isVideoCall) {
+ this.isVideoCall = isVideoCall;
+ return this;
+ }
+
+ public CallIntentBuilder setCallSubject(String callSubject) {
+ this.callSubject = callSubject;
+ return this;
+ }
+
+ public Intent build() {
+ Intent intent = new Intent(Intent.ACTION_CALL, uri);
+ intent.putExtra(
+ TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
+ isVideoCall ? VideoProfile.STATE_BIDIRECTIONAL : VideoProfile.STATE_AUDIO_ONLY);
+
+ Bundle extras = new Bundle();
+ extras.putLong(Constants.EXTRA_CALL_CREATED_TIME_MILLIS, SystemClock.elapsedRealtime());
+ CallIntentParser.putCallSpecificAppData(extras, callSpecificAppData);
+ intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
+
+ if (phoneAccountHandle != null) {
+ intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+ }
+
+ if (!TextUtils.isEmpty(callSubject)) {
+ intent.putExtra(TelecomManager.EXTRA_CALL_SUBJECT, callSubject);
+ }
+
+ return intent;
+ }
+
+ private static @NonNull CallSpecificAppData createCallSpecificAppData(int callInitiationType) {
+ CallSpecificAppData callSpecificAppData = new CallSpecificAppData();
+ callSpecificAppData.callInitiationType = callInitiationType;
+ return callSpecificAppData;
+ }
+}
diff --git a/java/com/android/dialer/callintent/CallIntentParser.java b/java/com/android/dialer/callintent/CallIntentParser.java
new file mode 100644
index 000000000..40c8ee348
--- /dev/null
+++ b/java/com/android/dialer/callintent/CallIntentParser.java
@@ -0,0 +1,54 @@
+/*
+ * 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.dialer.callintent;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import com.android.dialer.callintent.nano.CallSpecificAppData;
+import com.android.dialer.common.Assert;
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+import com.google.protobuf.nano.MessageNano;
+
+/** Parses data for a call extra to get any dialer specific app data. */
+public class CallIntentParser {
+ @Nullable
+ public static CallSpecificAppData getCallSpecificAppData(@Nullable Bundle extras) {
+ if (extras == null) {
+ return null;
+ }
+
+ byte[] flatArray = extras.getByteArray(Constants.EXTRA_CALL_SPECIFIC_APP_DATA);
+ if (flatArray == null) {
+ return null;
+ }
+ try {
+ return CallSpecificAppData.parseFrom(flatArray);
+ } catch (InvalidProtocolBufferNanoException e) {
+ Assert.fail("unexpected exception: " + e);
+ return null;
+ }
+ }
+
+ public static void putCallSpecificAppData(
+ @NonNull Bundle extras, @NonNull CallSpecificAppData callSpecificAppData) {
+ extras.putByteArray(
+ Constants.EXTRA_CALL_SPECIFIC_APP_DATA, MessageNano.toByteArray(callSpecificAppData));
+ }
+
+ private CallIntentParser() {}
+}
diff --git a/java/com/android/dialer/callintent/Constants.java b/java/com/android/dialer/callintent/Constants.java
new file mode 100644
index 000000000..dd5d48108
--- /dev/null
+++ b/java/com/android/dialer/callintent/Constants.java
@@ -0,0 +1,31 @@
+/*
+ * 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.dialer.callintent;
+
+/** Constants used to construct and parse call intents. These should never be made public. */
+/* package */ class Constants {
+ // This is a Dialer extra that is set for outgoing calls and used by the InCallUI.
+ /* package */ static final String EXTRA_CALL_SPECIFIC_APP_DATA =
+ "com.android.dialer.callintent.CALL_SPECIFIC_APP_DATA";
+
+ // This is a hidden system extra. For outgoing calls Dialer sets it and parses it but for incoming
+ // calls Telecom sets it and Dialer parses it.
+ /* package */ static final String EXTRA_CALL_CREATED_TIME_MILLIS =
+ "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
+
+ private Constants() {}
+}
diff --git a/java/com/android/dialer/callintent/nano/CallInitiationType.java b/java/com/android/dialer/callintent/nano/CallInitiationType.java
new file mode 100644
index 000000000..4badd6e57
--- /dev/null
+++ b/java/com/android/dialer/callintent/nano/CallInitiationType.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 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
+ */
+
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+
+package com.android.dialer.callintent.nano;
+
+@SuppressWarnings("hiding")
+public final class CallInitiationType extends
+ com.google.protobuf.nano.ExtendableMessageNano<CallInitiationType> {
+
+ // enum Type
+ public interface Type {
+ public static final int UNKNOWN_INITIATION = 0;
+ public static final int INCOMING_INITIATION = 1;
+ public static final int DIALPAD = 2;
+ public static final int SPEED_DIAL = 3;
+ public static final int REMOTE_DIRECTORY = 4;
+ public static final int SMART_DIAL = 5;
+ public static final int REGULAR_SEARCH = 6;
+ public static final int CALL_LOG = 7;
+ public static final int CALL_LOG_FILTER = 8;
+ public static final int VOICEMAIL_LOG = 9;
+ public static final int CALL_DETAILS = 10;
+ public static final int QUICK_CONTACTS = 11;
+ public static final int EXTERNAL_INITIATION = 12;
+ public static final int LAUNCHER_SHORTCUT = 13;
+ public static final int CALL_COMPOSER = 14;
+ public static final int MISSED_CALL_NOTIFICATION = 15;
+ public static final int CALL_SUBJECT_DIALOG = 16;
+ }
+
+ private static volatile CallInitiationType[] _emptyArray;
+ public static CallInitiationType[] emptyArray() {
+ // Lazily initializes the empty array
+ if (_emptyArray == null) {
+ synchronized (
+ com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
+ if (_emptyArray == null) {
+ _emptyArray = new CallInitiationType[0];
+ }
+ }
+ }
+ return _emptyArray;
+ }
+
+ // @@protoc_insertion_point(class_scope:com.android.dialer.callintent.CallInitiationType)
+
+ public CallInitiationType() {
+ clear();
+ }
+
+ public CallInitiationType clear() {
+ unknownFieldData = null;
+ cachedSize = -1;
+ return this;
+ }
+
+ @Override
+ public CallInitiationType mergeFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ while (true) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ return this;
+ default: {
+ if (!super.storeUnknownField(input, tag)) {
+ return this;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public static CallInitiationType parseFrom(byte[] data)
+ throws com.google.protobuf.nano.InvalidProtocolBufferNanoException {
+ return com.google.protobuf.nano.MessageNano.mergeFrom(new CallInitiationType(), data);
+ }
+
+ public static CallInitiationType parseFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ return new CallInitiationType().mergeFrom(input);
+ }
+}
diff --git a/java/com/android/dialer/callintent/nano/CallSpecificAppData.java b/java/com/android/dialer/callintent/nano/CallSpecificAppData.java
new file mode 100644
index 000000000..fd00b0a68
--- /dev/null
+++ b/java/com/android/dialer/callintent/nano/CallSpecificAppData.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+
+package com.android.dialer.callintent.nano;
+
+/** This file is autogenerated, but javadoc required. */
+@SuppressWarnings("hiding")
+public final class CallSpecificAppData
+ extends com.google.protobuf.nano.ExtendableMessageNano<CallSpecificAppData> {
+
+ private static volatile CallSpecificAppData[] _emptyArray;
+
+ public static CallSpecificAppData[] emptyArray() {
+ // Lazily initializes the empty array
+ if (_emptyArray == null) {
+ synchronized (com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
+ if (_emptyArray == null) {
+ _emptyArray = new CallSpecificAppData[0];
+ }
+ }
+ }
+ return _emptyArray;
+ }
+
+ // optional int32 call_initiation_type = 1;
+ public int callInitiationType;
+
+ // optional int32 position_of_selected_search_result = 2;
+ public int positionOfSelectedSearchResult;
+
+ // optional int32 characters_in_search_string = 3;
+ public int charactersInSearchString;
+
+ // @@protoc_insertion_point(class_scope:com.android.dialer.callintent.CallSpecificAppData)
+
+ public CallSpecificAppData() {
+ clear();
+ }
+
+ public CallSpecificAppData clear() {
+ callInitiationType = 0;
+ positionOfSelectedSearchResult = 0;
+ charactersInSearchString = 0;
+ unknownFieldData = null;
+ cachedSize = -1;
+ return this;
+ }
+
+ @Override
+ public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)
+ throws java.io.IOException {
+ if (this.callInitiationType != 0) {
+ output.writeInt32(1, this.callInitiationType);
+ }
+ if (this.positionOfSelectedSearchResult != 0) {
+ output.writeInt32(2, this.positionOfSelectedSearchResult);
+ }
+ if (this.charactersInSearchString != 0) {
+ output.writeInt32(3, this.charactersInSearchString);
+ }
+ super.writeTo(output);
+ }
+
+ @Override
+ protected int computeSerializedSize() {
+ int size = super.computeSerializedSize();
+ if (this.callInitiationType != 0) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt32Size(
+ 1, this.callInitiationType);
+ }
+ if (this.positionOfSelectedSearchResult != 0) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt32Size(
+ 2, this.positionOfSelectedSearchResult);
+ }
+ if (this.charactersInSearchString != 0) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt32Size(
+ 3, this.charactersInSearchString);
+ }
+ return size;
+ }
+
+ @Override
+ public CallSpecificAppData mergeFrom(com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ while (true) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ return this;
+ default:
+ {
+ if (!super.storeUnknownField(input, tag)) {
+ return this;
+ }
+ break;
+ }
+ case 8:
+ {
+ this.callInitiationType = input.readInt32();
+ break;
+ }
+ case 16:
+ {
+ this.positionOfSelectedSearchResult = input.readInt32();
+ break;
+ }
+ case 24:
+ {
+ this.charactersInSearchString = input.readInt32();
+ break;
+ }
+ }
+ }
+ }
+
+ public static CallSpecificAppData parseFrom(byte[] data)
+ throws com.google.protobuf.nano.InvalidProtocolBufferNanoException {
+ return com.google.protobuf.nano.MessageNano.mergeFrom(new CallSpecificAppData(), data);
+ }
+
+ public static CallSpecificAppData parseFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input) throws java.io.IOException {
+ return new CallSpecificAppData().mergeFrom(input);
+ }
+}
diff --git a/java/com/android/dialer/common/AndroidManifest.xml b/java/com/android/dialer/common/AndroidManifest.xml
new file mode 100644
index 000000000..ae43d6693
--- /dev/null
+++ b/java/com/android/dialer/common/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.dialer.common">
+</manifest>
diff --git a/java/com/android/dialer/common/Assert.java b/java/com/android/dialer/common/Assert.java
new file mode 100644
index 000000000..00b4f2595
--- /dev/null
+++ b/java/com/android/dialer/common/Assert.java
@@ -0,0 +1,185 @@
+/*
+ * 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.dialer.common;
+
+import android.os.Looper;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+/** Assertions which will result in program termination unless disabled by flags. */
+public class Assert {
+
+ private static boolean areThreadAssertsEnabled = true;
+
+ public static void setAreThreadAssertsEnabled(boolean areThreadAssertsEnabled) {
+ Assert.areThreadAssertsEnabled = areThreadAssertsEnabled;
+ }
+
+ /**
+ * Called when a truly exceptional case occurs.
+ *
+ * @throws AssertionError
+ */
+ public static void fail() {
+ throw new AssertionError("Fail");
+ }
+
+ /**
+ * Called when a truly exceptional case occurs.
+ *
+ * @param reason the optional reason to supply as the exception message
+ * @throws AssertionError
+ */
+ public static void fail(String reason) {
+ throw new AssertionError(reason);
+ }
+
+ /**
+ * Ensures the truth of an expression involving one or more parameters to the calling method.
+ *
+ * @param expression a boolean expression
+ * @throws IllegalArgumentException if {@code expression} is false
+ */
+ public static void checkArgument(boolean expression) {
+ checkArgument(expression, null);
+ }
+
+ /**
+ * Ensures the truth of an expression involving one or more parameters to the calling method.
+ *
+ * @param expression a boolean expression
+ * @param messageTemplate the message to log, possible with format arguments.
+ * @param args optional arguments to be used in the formatted string.
+ * @throws IllegalArgumentException if {@code expression} is false
+ */
+ public static void checkArgument(
+ boolean expression, @Nullable String messageTemplate, Object... args) {
+ if (!expression) {
+ throw new IllegalArgumentException(format(messageTemplate, args));
+ }
+ }
+
+ /**
+ * Ensures the truth of an expression involving the state of the calling instance, but not
+ * involving any parameters to the calling method.
+ *
+ * @param expression a boolean expression
+ * @throws IllegalStateException if {@code expression} is false
+ */
+ public static void checkState(boolean expression) {
+ checkState(expression, null);
+ }
+
+ /**
+ * Ensures the truth of an expression involving the state of the calling instance, but not
+ * involving any parameters to the calling method.
+ *
+ * @param expression a boolean expression
+ * @param messageTemplate the message to log, possible with format arguments.
+ * @param args optional arguments to be used in the formatted string.
+ * @throws IllegalStateException if {@code expression} is false
+ */
+ public static void checkState(
+ boolean expression, @Nullable String messageTemplate, Object... args) {
+ if (!expression) {
+ throw new IllegalStateException(format(messageTemplate, args));
+ }
+ }
+
+ /**
+ * Ensures that an object reference passed as a parameter to the calling method is not null.
+ *
+ * @param reference an object reference
+ * @return the non-null reference that was validated
+ * @throws NullPointerException if {@code reference} is null
+ */
+ @NonNull
+ public static <T> T isNotNull(@Nullable T reference) {
+ return isNotNull(reference, null);
+ }
+
+ /**
+ * Ensures that an object reference passed as a parameter to the calling method is not null.
+ *
+ * @param reference an object reference
+ * @param messageTemplate the message to log, possible with format arguments.
+ * @param args optional arguments to be used in the formatted string.
+ * @return the non-null reference that was validated
+ * @throws NullPointerException if {@code reference} is null
+ */
+ @NonNull
+ public static <T> T isNotNull(
+ @Nullable T reference, @Nullable String messageTemplate, Object... args) {
+ if (reference == null) {
+ throw new NullPointerException(format(messageTemplate, args));
+ }
+ return reference;
+ }
+
+ /**
+ * Ensures that the current thread is the main thread.
+ *
+ * @throws IllegalStateException if called on a background thread
+ */
+ public static void isMainThread() {
+ isMainThread(null);
+ }
+
+ /**
+ * Ensures that the current thread is the main thread.
+ *
+ * @param messageTemplate the message to log, possible with format arguments.
+ * @param args optional arguments to be used in the formatted string.
+ * @throws IllegalStateException if called on a background thread
+ */
+ public static void isMainThread(@Nullable String messageTemplate, Object... args) {
+ if (!areThreadAssertsEnabled) {
+ return;
+ }
+ checkState(Looper.getMainLooper().equals(Looper.myLooper()), messageTemplate, args);
+ }
+
+ /**
+ * Ensures that the current thread is a worker thread.
+ *
+ * @throws IllegalStateException if called on the main thread
+ */
+ public static void isWorkerThread() {
+ isWorkerThread(null);
+ }
+
+ /**
+ * Ensures that the current thread is a worker thread.
+ *
+ * @param messageTemplate the message to log, possible with format arguments.
+ * @param args optional arguments to be used in the formatted string.
+ * @throws IllegalStateException if called on the main thread
+ */
+ public static void isWorkerThread(@Nullable String messageTemplate, Object... args) {
+ if (!areThreadAssertsEnabled) {
+ return;
+ }
+ checkState(!Looper.getMainLooper().equals(Looper.myLooper()), messageTemplate, args);
+ }
+
+ private static String format(@Nullable String messageTemplate, Object... args) {
+ if (messageTemplate == null) {
+ return null;
+ }
+ return String.format(messageTemplate, args);
+ }
+}
diff --git a/java/com/android/dialer/common/AsyncTaskExecutor.java b/java/com/android/dialer/common/AsyncTaskExecutor.java
new file mode 100644
index 000000000..caadfe7ce
--- /dev/null
+++ b/java/com/android/dialer/common/AsyncTaskExecutor.java
@@ -0,0 +1,51 @@
+/*
+ * 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.dialer.common;
+
+import android.os.AsyncTask;
+import android.support.annotation.MainThread;
+import java.util.concurrent.Executor;
+
+/**
+ * Interface used to submit {@link AsyncTask} objects to run in the background.
+ *
+ * <p>This interface has a direct parallel with the {@link Executor} interface. It exists to
+ * decouple the mechanics of AsyncTask submission from the description of how that AsyncTask will
+ * execute.
+ *
+ * <p>One immediate benefit of this approach is that testing becomes much easier, since it is easy
+ * to introduce a mock or fake AsyncTaskExecutor in unit/integration tests, and thus inspect which
+ * tasks have been submitted and control their execution in an orderly manner.
+ *
+ * <p>Another benefit in due course will be the management of the submitted tasks. An extension to
+ * this interface is planned to allow Activities to easily cancel all the submitted tasks that are
+ * still pending in the onDestroy() method of the Activity.
+ */
+public interface AsyncTaskExecutor {
+
+ /**
+ * Executes the given AsyncTask with the default Executor.
+ *
+ * <p>This method <b>must only be called from the ui thread</b>.
+ *
+ * <p>The identifier supplied is any Object that can be used to identify the task later. Most
+ * commonly this will be an enum which the tests can also refer to. {@code null} is also accepted,
+ * though of course this won't help in identifying the task later.
+ */
+ @MainThread
+ <T> AsyncTask<T, ?, ?> submit(Object identifier, AsyncTask<T, ?, ?> task, T... params);
+}
diff --git a/java/com/android/dialer/common/AsyncTaskExecutors.java b/java/com/android/dialer/common/AsyncTaskExecutors.java
new file mode 100644
index 000000000..77bebdb36
--- /dev/null
+++ b/java/com/android/dialer/common/AsyncTaskExecutors.java
@@ -0,0 +1,91 @@
+/*
+ * 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.dialer.common;
+
+import android.os.AsyncTask;
+import android.support.annotation.MainThread;
+import java.util.concurrent.Executor;
+
+/**
+ * Factory methods for creating AsyncTaskExecutors.
+ *
+ * <p>All of the factory methods on this class check first to see if you have set a static {@link
+ * AsyncTaskExecutorFactory} set through the {@link #setFactoryForTest(AsyncTaskExecutorFactory)}
+ * method, and if so delegate to that instead, which is one way of injecting dependencies for
+ * testing classes whose construction cannot be controlled such as {@link android.app.Activity}.
+ */
+public final class AsyncTaskExecutors {
+
+ /**
+ * A single instance of the {@link AsyncTaskExecutorFactory}, to which we delegate if it is
+ * non-null, for injecting when testing.
+ */
+ private static AsyncTaskExecutorFactory mInjectedAsyncTaskExecutorFactory = null;
+
+ /**
+ * Creates an AsyncTaskExecutor that submits tasks to run with {@link AsyncTask#SERIAL_EXECUTOR}.
+ */
+ public static AsyncTaskExecutor createAsyncTaskExecutor() {
+ synchronized (AsyncTaskExecutors.class) {
+ if (mInjectedAsyncTaskExecutorFactory != null) {
+ return mInjectedAsyncTaskExecutorFactory.createAsyncTaskExeuctor();
+ }
+ return new SimpleAsyncTaskExecutor(AsyncTask.SERIAL_EXECUTOR);
+ }
+ }
+
+ /**
+ * Creates an AsyncTaskExecutor that submits tasks to run with {@link
+ * AsyncTask#THREAD_POOL_EXECUTOR}.
+ */
+ public static AsyncTaskExecutor createThreadPoolExecutor() {
+ synchronized (AsyncTaskExecutors.class) {
+ if (mInjectedAsyncTaskExecutorFactory != null) {
+ return mInjectedAsyncTaskExecutorFactory.createAsyncTaskExeuctor();
+ }
+ return new SimpleAsyncTaskExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+ }
+
+ public static void setFactoryForTest(AsyncTaskExecutorFactory factory) {
+ synchronized (AsyncTaskExecutors.class) {
+ mInjectedAsyncTaskExecutorFactory = factory;
+ }
+ }
+
+ /** Interface for creating AsyncTaskExecutor objects. */
+ public interface AsyncTaskExecutorFactory {
+
+ AsyncTaskExecutor createAsyncTaskExeuctor();
+ }
+
+ private static class SimpleAsyncTaskExecutor implements AsyncTaskExecutor {
+
+ private final Executor mExecutor;
+
+ public SimpleAsyncTaskExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+
+ @Override
+ @MainThread
+ public <T> AsyncTask<T, ?, ?> submit(Object identifer, AsyncTask<T, ?, ?> task, T... params) {
+ Assert.isMainThread();
+ return task.executeOnExecutor(mExecutor, params);
+ }
+ }
+}
diff --git a/java/com/android/dialer/common/AutoValue_FallibleAsyncTask_FallibleTaskResult.java b/java/com/android/dialer/common/AutoValue_FallibleAsyncTask_FallibleTaskResult.java
new file mode 100644
index 000000000..f9d7cea90
--- /dev/null
+++ b/java/com/android/dialer/common/AutoValue_FallibleAsyncTask_FallibleTaskResult.java
@@ -0,0 +1,79 @@
+/*
+ * 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.dialer.common;
+
+import android.support.annotation.Nullable;
+import javax.annotation.Generated;
+
+
+ final class AutoValue_FallibleAsyncTask_FallibleTaskResult<ResultT> extends FallibleAsyncTask.FallibleTaskResult<ResultT> {
+
+ private final Throwable throwable;
+ private final ResultT result;
+
+ AutoValue_FallibleAsyncTask_FallibleTaskResult(
+ @Nullable Throwable throwable,
+ @Nullable ResultT result) {
+ this.throwable = throwable;
+ this.result = result;
+ }
+
+ @Nullable
+ @Override
+ public Throwable getThrowable() {
+ return throwable;
+ }
+
+ @Nullable
+ @Override
+ public ResultT getResult() {
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "FallibleTaskResult{"
+ + "throwable=" + throwable + ", "
+ + "result=" + result
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof FallibleAsyncTask.FallibleTaskResult) {
+ FallibleAsyncTask.FallibleTaskResult<?> that = (FallibleAsyncTask.FallibleTaskResult<?>) o;
+ return ((this.throwable == null) ? (that.getThrowable() == null) : this.throwable.equals(that.getThrowable()))
+ && ((this.result == null) ? (that.getResult() == null) : this.result.equals(that.getResult()));
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 1;
+ h *= 1000003;
+ h ^= (throwable == null) ? 0 : this.throwable.hashCode();
+ h *= 1000003;
+ h ^= (result == null) ? 0 : this.result.hashCode();
+ return h;
+ }
+
+}
+
diff --git a/java/com/android/dialer/common/ConfigProvider.java b/java/com/android/dialer/common/ConfigProvider.java
new file mode 100644
index 000000000..c0791e979
--- /dev/null
+++ b/java/com/android/dialer/common/ConfigProvider.java
@@ -0,0 +1,27 @@
+/*
+ * 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.dialer.common;
+
+/** Gets config values from the container application. */
+public interface ConfigProvider {
+
+ String getString(String key, String defaultValue);
+
+ long getLong(String key, long defaultValue);
+
+ boolean getBoolean(String key, boolean defaultValue);
+}
diff --git a/java/com/android/dialer/common/ConfigProviderBindings.java b/java/com/android/dialer/common/ConfigProviderBindings.java
new file mode 100644
index 000000000..92e6cc3ff
--- /dev/null
+++ b/java/com/android/dialer/common/ConfigProviderBindings.java
@@ -0,0 +1,68 @@
+/*
+ * 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.dialer.common;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+
+/** Accessor for getting a {@link ConfigProvider}. */
+public class ConfigProviderBindings {
+
+ private static ConfigProvider configProvider;
+
+ public static ConfigProvider get(@NonNull Context context) {
+ Assert.isNotNull(context);
+ if (configProvider != null) {
+ return configProvider;
+ }
+
+ Context application = context.getApplicationContext();
+ if (application instanceof ConfigProviderFactory) {
+ configProvider = ((ConfigProviderFactory) application).getConfigProvider();
+ }
+
+ if (configProvider == null) {
+ configProvider = new ConfigProviderStub();
+ }
+
+ return configProvider;
+ }
+
+ @VisibleForTesting
+ public static void setForTesting(@Nullable ConfigProvider configProviderForTesting) {
+ configProvider = configProviderForTesting;
+ }
+
+ private static class ConfigProviderStub implements ConfigProvider {
+ @Override
+ public String getString(String key, String defaultValue) {
+ return defaultValue;
+ }
+
+ @Override
+ public long getLong(String key, long defaultValue) {
+ return defaultValue;
+ }
+
+ @Override
+ public boolean getBoolean(String key, boolean defaultValue) {
+ return defaultValue;
+ }
+ }
+}
diff --git a/java/com/android/dialer/common/ConfigProviderFactory.java b/java/com/android/dialer/common/ConfigProviderFactory.java
new file mode 100644
index 000000000..aeb4f303a
--- /dev/null
+++ b/java/com/android/dialer/common/ConfigProviderFactory.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dialer.common;
+
+/**
+ * This interface should be implementated by the Application subclass. It allows dialer code to get
+ * references to a config provider.
+ */
+public interface ConfigProviderFactory {
+
+ ConfigProvider getConfigProvider();
+}
diff --git a/java/com/android/dialer/common/DpUtil.java b/java/com/android/dialer/common/DpUtil.java
new file mode 100644
index 000000000..0388824cd
--- /dev/null
+++ b/java/com/android/dialer/common/DpUtil.java
@@ -0,0 +1,31 @@
+/*
+ * 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.dialer.common;
+
+import android.content.Context;
+
+/** Utility for dp to px conversion */
+public class DpUtil {
+
+ public static float pxToDp(Context context, float px) {
+ return px / context.getResources().getDisplayMetrics().density;
+ }
+
+ public static float dpToPx(Context context, float dp) {
+ return dp * context.getResources().getDisplayMetrics().density;
+ }
+}
diff --git a/java/com/android/dialer/common/FallibleAsyncTask.java b/java/com/android/dialer/common/FallibleAsyncTask.java
new file mode 100644
index 000000000..fbdbda75f
--- /dev/null
+++ b/java/com/android/dialer/common/FallibleAsyncTask.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 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.dialer.common;
+
+import android.os.AsyncTask;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import com.android.dialer.common.FallibleAsyncTask.FallibleTaskResult;
+
+
+/**
+ * A task that runs work in the background, passing Throwables from {@link
+ * #doInBackground(Object[])} to {@link #onPostExecute(Object)} through a {@link
+ * FallibleTaskResult}.
+ *
+ * @param <ParamsT> the type of the parameters sent to the task upon execution
+ * @param <ProgressT> the type of the progress units published during the background computation
+ * @param <ResultT> the type of the result of the background computation
+ */
+public abstract class FallibleAsyncTask<ParamsT, ProgressT, ResultT>
+ extends AsyncTask<ParamsT, ProgressT, FallibleTaskResult<ResultT>> {
+
+ @Override
+ protected final FallibleTaskResult<ResultT> doInBackground(ParamsT... params) {
+ try {
+ return FallibleTaskResult.createSuccessResult(doInBackgroundFallible(params));
+ } catch (Throwable t) {
+ return FallibleTaskResult.createFailureResult(t);
+ }
+ }
+
+ /** Performs background work that may result in a Throwable. */
+ @Nullable
+ protected abstract ResultT doInBackgroundFallible(ParamsT... params) throws Throwable;
+
+ /**
+ * Holds the result of processing from {@link #doInBackground(Object[])}.
+ *
+ * @param <ResultT> the type of the result of the background computation
+ */
+
+ protected abstract static class FallibleTaskResult<ResultT> {
+
+ /** Creates an instance of FallibleTaskResult for the given throwable. */
+ private static <ResultT> FallibleTaskResult<ResultT> createFailureResult(@NonNull Throwable t) {
+ return new AutoValue_FallibleAsyncTask_FallibleTaskResult<>(t, null);
+ }
+
+ /** Creates an instance of FallibleTaskResult for the given result. */
+ private static <ResultT> FallibleTaskResult<ResultT> createSuccessResult(
+ @Nullable ResultT result) {
+ return new AutoValue_FallibleAsyncTask_FallibleTaskResult<>(null, result);
+ }
+
+ /**
+ * Returns the Throwable thrown in {@link #doInBackground(Object[])}, or {@code null} if
+ * background work completed without throwing.
+ */
+ @Nullable
+ public abstract Throwable getThrowable();
+
+ /**
+ * Returns the result of {@link #doInBackground(Object[])}, which may be {@code null}, or {@code
+ * null} if the background work threw a Throwable.
+ *
+ * <p>Use {@link #isFailure()} to determine if a {@code null} return is the result of a
+ * Throwable from the background work.
+ */
+ @Nullable
+ public abstract ResultT getResult();
+
+ /**
+ * Returns {@code true} if this object is the result of background work that threw a Throwable.
+ */
+ public boolean isFailure() {
+ //noinspection ThrowableResultOfMethodCallIgnored
+ return getThrowable() != null;
+ }
+ }
+}
diff --git a/java/com/android/dialer/common/FragmentUtils.java b/java/com/android/dialer/common/FragmentUtils.java
new file mode 100644
index 000000000..cb036959d
--- /dev/null
+++ b/java/com/android/dialer/common/FragmentUtils.java
@@ -0,0 +1,98 @@
+/*
+ * 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.dialer.common;
+
+import android.support.annotation.CheckResult;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+
+/** Utility methods for working with Fragments */
+public class FragmentUtils {
+
+ private static Object parentForTesting;
+
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+ public static void setParentForTesting(Object parentForTesting) {
+ FragmentUtils.parentForTesting = parentForTesting;
+ }
+
+ /**
+ * @return The parent of frag that implements the callbackInterface or null if no such parent can
+ * be found
+ */
+ @CheckResult(suggest = "#checkParent(Fragment, Class)}")
+ @Nullable
+ public static <T> T getParent(@NonNull Fragment fragment, @NonNull Class<T> callbackInterface) {
+ if (callbackInterface.isInstance(parentForTesting)) {
+ @SuppressWarnings("unchecked") // Casts are checked using runtime methods
+ T parent = (T) parentForTesting;
+ return parent;
+ }
+
+ Fragment parentFragment = fragment.getParentFragment();
+ if (callbackInterface.isInstance(parentFragment)) {
+ @SuppressWarnings("unchecked") // Casts are checked using runtime methods
+ T parent = (T) parentFragment;
+ return parent;
+ } else {
+ FragmentActivity activity = fragment.getActivity();
+ if (callbackInterface.isInstance(activity)) {
+ @SuppressWarnings("unchecked") // Casts are checked using runtime methods
+ T parent = (T) activity;
+ return parent;
+ }
+ }
+ return null;
+ }
+
+ /** Returns the parent or throws. Should perform check elsewhere(e.g. onAttach, newInstance). */
+ @NonNull
+ public static <T> T getParentUnsafe(
+ @NonNull Fragment fragment, @NonNull Class<T> callbackInterface) {
+ return Assert.isNotNull(getParent(fragment, callbackInterface));
+ }
+
+ /**
+ * Ensures fragment has a parent that implements the corresponding interface
+ *
+ * @param frag The Fragment whose parents are to be checked
+ * @param callbackInterface The interface class that a parent should implement
+ * @throws IllegalStateException if no parents are found that implement callbackInterface
+ */
+ public static void checkParent(@NonNull Fragment frag, @NonNull Class<?> callbackInterface)
+ throws IllegalStateException {
+ if (parentForTesting != null) {
+ return;
+ }
+ if (FragmentUtils.getParent(frag, callbackInterface) == null) {
+ String parent =
+ frag.getParentFragment() == null
+ ? frag.getActivity().getClass().getName()
+ : frag.getParentFragment().getClass().getName();
+ throw new IllegalStateException(
+ frag.getClass().getName()
+ + " must be added to a parent"
+ + " that implements "
+ + callbackInterface.getName()
+ + ". Instead found "
+ + parent);
+ }
+ }
+}
diff --git a/java/com/android/dialer/common/LogUtil.java b/java/com/android/dialer/common/LogUtil.java
new file mode 100644
index 000000000..32d7b960b
--- /dev/null
+++ b/java/com/android/dialer/common/LogUtil.java
@@ -0,0 +1,214 @@
+/*
+ * 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.dialer.common;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+
+/** Provides logging functions. */
+public class LogUtil {
+
+ public static final String TAG = "Dialer";
+ private static final String SEPARATOR = " - ";
+
+ private LogUtil() {}
+
+ /**
+ * Log at a verbose level. Verbose logs should generally be filtered out, but may be useful when
+ * additional information is needed (e.g. to see how a particular flow evolved). These logs will
+ * not generally be available on production builds.
+ *
+ * @param tag An identifier to allow searching for related logs. Generally of the form
+ * 'Class.method'.
+ * @param msg The message you would like logged, possibly with format arguments.
+ * @param args Optional arguments to be used in the formatted string.
+ * @see {@link String#format(String, Object...)}
+ * @see {@link android.util.Log#v(String, String)}
+ */
+ public static void v(@NonNull String tag, @Nullable String msg, @Nullable Object... args) {
+ println(android.util.Log.VERBOSE, TAG, tag, msg, args);
+ }
+
+ /**
+ * Log at a debug level. Debug logs should provide known-useful information to aid in
+ * troubleshooting or evaluating flow. These logs will not generally be available on production
+ * builds.
+ *
+ * @param tag An identifier to allow searching for related logs. Generally of the form
+ * 'Class.method'
+ * @param msg The message you would like logged, possibly with format arguments
+ * @param args Optional arguments to be used in the formatted string
+ * @see {@link String#format(String, Object...)}
+ * @see {@link android.util.Log#d(String, String)}
+ */
+ public static void d(@NonNull String tag, @Nullable String msg, @Nullable Object... args) {
+ println(android.util.Log.DEBUG, TAG, tag, msg, args);
+ }
+
+ /**
+ * Log at an info level. Info logs provide information that would be useful to have on production
+ * builds for troubleshooting.
+ *
+ * @param tag An identifier to allow searching for related logs. Generally of the form
+ * 'Class.method'.
+ * @param msg The message you would like logged, possibly with format arguments.
+ * @param args Optional arguments to be used in the formatted string.
+ * @see {@link String#format(String, Object...)}
+ * @see {@link android.util.Log#i(String, String)}
+ */
+ public static void i(@NonNull String tag, @Nullable String msg, @Nullable Object... args) {
+ println(android.util.Log.INFO, TAG, tag, msg, args);
+ }
+
+ /**
+ * Log entry into a method at the info level.
+ *
+ * @param tag An identifier to allow searching for related logs. Generally of the form
+ * 'Class.method'.
+ */
+ public static void enterBlock(String tag) {
+ println(android.util.Log.INFO, TAG, tag, "enter");
+ }
+
+ /**
+ * Log at a warn level. Warn logs indicate a possible error (e.g. a default switch branch was hit,
+ * or a null object was expected to be non-null), but recovery is possible. This may be used when
+ * it is not guaranteed that an indeterminate or bad state was entered, just that something may
+ * have gone wrong.
+ *
+ * @param tag An identifier to allow searching for related logs. Generally of the form
+ * 'Class.method'.
+ * @param msg The message you would like logged, possibly with format arguments.
+ * @param args Optional arguments to be used in the formatted string.
+ * @see {@link String#format(String, Object...)}
+ * @see {@link android.util.Log#w(String, String)}
+ */
+ public static void w(@NonNull String tag, @Nullable String msg, @Nullable Object... args) {
+ println(android.util.Log.WARN, TAG, tag, msg, args);
+ }
+
+ /**
+ * Log at an error level. Error logs are used when it is known that an error occurred and is
+ * possibly fatal. This is used to log information that will be useful for troubleshooting a crash
+ * or other severe condition (e.g. error codes, state values, etc.).
+ *
+ * @param tag An identifier to allow searching for related logs. Generally of the form
+ * 'Class.method'.
+ * @param msg The message you would like logged, possibly with format arguments.
+ * @param args Optional arguments to be used in the formatted string.
+ * @see {@link String#format(String, Object...)}
+ * @see {@link android.util.Log#e(String, String)}
+ */
+ public static void e(@NonNull String tag, @Nullable String msg, @Nullable Object... args) {
+ println(android.util.Log.ERROR, TAG, tag, msg, args);
+ }
+
+ /**
+ * Log an exception at an error level. Error logs are used when it is known that an error occurred
+ * and is possibly fatal. This is used to log information that will be useful for troubleshooting
+ * a crash or other severe condition (e.g. error codes, state values, etc.).
+ *
+ * @param tag An identifier to allow searching for related logs. Generally of the form
+ * 'Class.method'.
+ * @param msg The message you would like logged.
+ * @param throwable The exception to log.
+ * @see {@link String#format(String, Object...)}
+ * @see {@link android.util.Log#e(String, String)}
+ */
+ public static void e(@NonNull String tag, @Nullable String msg, @NonNull Throwable throwable) {
+ if (!TextUtils.isEmpty(msg)) {
+ println(android.util.Log.ERROR, TAG, tag, msg);
+ }
+ println(android.util.Log.ERROR, TAG, tag, android.util.Log.getStackTraceString(throwable));
+ }
+
+ /**
+ * Used for log statements where we don't want to log various strings (e.g., usernames) with
+ * default logging to avoid leaking PII in logcat.
+ *
+ * @return text as is if {@value #TAG}'s log level is set to DEBUG or VERBOSE or on non-release
+ * builds; returns a redacted version otherwise.
+ */
+ public static String sanitizePii(@Nullable Object object) {
+ if (object == null) {
+ return "null";
+ }
+ if (isDebugEnabled()) {
+ return object.toString();
+ }
+ return "Redacted-" + object.toString().length() + "-chars";
+ }
+
+ /** Anonymizes char to prevent logging personally identifiable information. */
+ public static char sanitizeDialPadChar(char ch) {
+ if (isDebugEnabled()) {
+ return ch;
+ }
+ if (is12Key(ch)) {
+ return '*';
+ }
+ return ch;
+ }
+
+ /** Anonymizes the phone number to prevent logging personally identifiable information. */
+ public static String sanitizePhoneNumber(@Nullable String phoneNumber) {
+ if (isDebugEnabled()) {
+ return phoneNumber;
+ }
+ if (phoneNumber == null) {
+ return null;
+ }
+ StringBuilder stringBuilder = new StringBuilder(phoneNumber.length());
+ for (char c : phoneNumber.toCharArray()) {
+ stringBuilder.append(sanitizeDialPadChar(c));
+ }
+ return stringBuilder.toString();
+ }
+
+ public static boolean isVerboseEnabled() {
+ return android.util.Log.isLoggable(TAG, android.util.Log.VERBOSE);
+ }
+
+ public static boolean isDebugEnabled() {
+ return android.util.Log.isLoggable(TAG, android.util.Log.DEBUG);
+ }
+
+ private static boolean is12Key(char ch) {
+ return PhoneNumberUtils.is12Key(ch);
+ }
+
+ private static void println(
+ int level,
+ @NonNull String tag,
+ @NonNull String localTag,
+ @Nullable String msg,
+ @Nullable Object... args) {
+ // Formatted message is computed lazily if required.
+ String formattedMsg;
+ // Either null is passed as a single argument or more than one argument is passed.
+ boolean hasArgs = args == null || args.length > 0;
+ if ((level >= android.util.Log.INFO) || android.util.Log.isLoggable(tag, level)) {
+ formattedMsg = localTag;
+ if (!TextUtils.isEmpty(msg)) {
+ formattedMsg += SEPARATOR + (hasArgs ? String.format(msg, args) : msg);
+ }
+ android.util.Log.println(level, tag, formattedMsg);
+ }
+ }
+}
diff --git a/java/com/android/dialer/common/MathUtil.java b/java/com/android/dialer/common/MathUtil.java
new file mode 100644
index 000000000..e811a46e2
--- /dev/null
+++ b/java/com/android/dialer/common/MathUtil.java
@@ -0,0 +1,57 @@
+/*
+ * 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.dialer.common;
+
+/** Utility class for common math operations */
+public class MathUtil {
+
+ /**
+ * Interpolates between two integer values based on percentage.
+ *
+ * @param begin Begin value
+ * @param end End value
+ * @param percent Percentage value, between 0 and 1
+ * @return Interpolated result
+ */
+ public static int lerp(int begin, int end, float percent) {
+ return (int) (begin * (1 - percent) + end * percent);
+ }
+
+ /**
+ * Interpolates between two float values based on percentage.
+ *
+ * @param begin Begin value
+ * @param end End value
+ * @param percent Percentage value, between 0 and 1
+ * @return Interpolated result
+ */
+ public static float lerp(float begin, float end, float percent) {
+ return begin * (1 - percent) + end * percent;
+ }
+
+ /**
+ * Clamps a value between two bounds inclusively.
+ *
+ * @param value Value to be clamped
+ * @param min Lower bound
+ * @param max Upper bound
+ * @return Clamped value
+ */
+ public static float clamp(float value, float min, float max) {
+ return Math.max(min, Math.min(value, max));
+ }
+}
diff --git a/java/com/android/dialer/common/NetworkUtil.java b/java/com/android/dialer/common/NetworkUtil.java
new file mode 100644
index 000000000..47d84243e
--- /dev/null
+++ b/java/com/android/dialer/common/NetworkUtil.java
@@ -0,0 +1,192 @@
+/*
+ * 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.dialer.common;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresPermission;
+import android.support.annotation.StringDef;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/** Utility class for dealing with network */
+public class NetworkUtil {
+
+ /* Returns the current network type. */
+ @RequiresPermission("android.permission.ACCESS_NETWORK_STATE")
+ @NetworkType
+ public static String getCurrentNetworkType(@Nullable Context context) {
+ if (context == null) {
+ return NetworkType.NONE;
+ }
+ ConnectivityManager connectivityManager =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ return getNetworkType(connectivityManager.getActiveNetworkInfo());
+ }
+
+ /* Returns the current network info. */
+ @Nullable
+ @RequiresPermission("android.permission.ACCESS_NETWORK_STATE")
+ public static NetworkInfo getCurrentNetworkInfo(@Nullable Context context) {
+ if (context == null) {
+ return null;
+ }
+ ConnectivityManager connectivityManager =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ return connectivityManager.getActiveNetworkInfo();
+ }
+
+ /**
+ * Returns the current network type as a string. For mobile network types the subtype name of the
+ * network is appended.
+ */
+ @RequiresPermission("android.permission.ACCESS_NETWORK_STATE")
+ public static String getCurrentNetworkTypeName(@Nullable Context context) {
+ if (context == null) {
+ return NetworkType.NONE;
+ }
+ ConnectivityManager connectivityManager =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
+ @NetworkType String networkType = getNetworkType(netInfo);
+ if (isNetworkTypeMobile(networkType)) {
+ return networkType + " (" + netInfo.getSubtypeName() + ")";
+ }
+ return networkType;
+ }
+
+ @NetworkType
+ public static String getNetworkType(@Nullable NetworkInfo netInfo) {
+ if (netInfo == null || !netInfo.isConnected()) {
+ return NetworkType.NONE;
+ }
+ switch (netInfo.getType()) {
+ case ConnectivityManager.TYPE_WIFI:
+ return NetworkType.WIFI;
+ case ConnectivityManager.TYPE_MOBILE:
+ return getMobileNetworkType(netInfo.getSubtype());
+ default:
+ return NetworkType.UNKNOWN;
+ }
+ }
+
+ public static boolean isNetworkTypeMobile(@NetworkType String networkType) {
+ return Objects.equals(networkType, NetworkType.MOBILE_2G)
+ || Objects.equals(networkType, NetworkType.MOBILE_3G)
+ || Objects.equals(networkType, NetworkType.MOBILE_4G);
+ }
+
+ @RequiresPermission("android.permission.ACCESS_NETWORK_STATE")
+ public static String getCurrentNetworkName(Context context) {
+ @NetworkType String networkType = getCurrentNetworkType(context);
+ switch (networkType) {
+ case NetworkType.WIFI:
+ return getWifiNetworkName(context);
+ case NetworkType.MOBILE_2G:
+ case NetworkType.MOBILE_3G:
+ case NetworkType.MOBILE_4G:
+ case NetworkType.MOBILE_UNKNOWN:
+ return getMobileNetworkName(context);
+ default:
+ return "";
+ }
+ }
+
+ private static String getWifiNetworkName(Context context) {
+ WifiManager wifiMgr = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ String name = null;
+ if (context.checkSelfPermission("android.permission.ACCESS_WIFI_STATE")
+ == PackageManager.PERMISSION_GRANTED) {
+ //noinspection MissingPermission
+ WifiInfo wifiInfo = wifiMgr.getConnectionInfo();
+ if (wifiInfo == null) {
+ return "";
+ }
+ name = wifiInfo.getSSID();
+ }
+ return TextUtils.isEmpty(name)
+ ? context.getString(R.string.network_name_wifi)
+ : name.replaceAll("\"", "");
+ }
+
+ private static String getMobileNetworkName(Context context) {
+ TelephonyManager telephonyMgr =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ String name = telephonyMgr.getNetworkOperatorName();
+ return TextUtils.isEmpty(name)
+ ? context.getString(R.string.network_name_mobile)
+ : name.replaceAll("\"", "");
+ }
+
+ @NetworkType
+ private static String getMobileNetworkType(int networkSubtype) {
+ switch (networkSubtype) {
+ case TelephonyManager.NETWORK_TYPE_1xRTT:
+ case TelephonyManager.NETWORK_TYPE_CDMA:
+ case TelephonyManager.NETWORK_TYPE_EDGE:
+ case TelephonyManager.NETWORK_TYPE_GPRS:
+ case TelephonyManager.NETWORK_TYPE_IDEN:
+ return NetworkType.MOBILE_2G;
+ case TelephonyManager.NETWORK_TYPE_EHRPD:
+ case TelephonyManager.NETWORK_TYPE_EVDO_0:
+ case TelephonyManager.NETWORK_TYPE_EVDO_A:
+ case TelephonyManager.NETWORK_TYPE_EVDO_B:
+ case TelephonyManager.NETWORK_TYPE_HSDPA:
+ case TelephonyManager.NETWORK_TYPE_HSPA:
+ case TelephonyManager.NETWORK_TYPE_HSPAP:
+ case TelephonyManager.NETWORK_TYPE_HSUPA:
+ case TelephonyManager.NETWORK_TYPE_UMTS:
+ return NetworkType.MOBILE_3G;
+ case TelephonyManager.NETWORK_TYPE_LTE:
+ return NetworkType.MOBILE_4G;
+ default:
+ return NetworkType.MOBILE_UNKNOWN;
+ }
+ }
+
+ /** Network types. */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(
+ value = {
+ NetworkType.NONE,
+ NetworkType.WIFI,
+ NetworkType.MOBILE_2G,
+ NetworkType.MOBILE_3G,
+ NetworkType.MOBILE_4G,
+ NetworkType.MOBILE_UNKNOWN,
+ NetworkType.UNKNOWN
+ }
+ )
+ public @interface NetworkType {
+
+ String NONE = "NONE";
+ String WIFI = "WIFI";
+ String MOBILE_2G = "MOBILE_2G";
+ String MOBILE_3G = "MOBILE_3G";
+ String MOBILE_4G = "MOBILE_4G";
+ String MOBILE_UNKNOWN = "MOBILE_UNKNOWN";
+ String UNKNOWN = "UNKNOWN";
+ }
+}
diff --git a/java/com/android/dialer/common/UiUtil.java b/java/com/android/dialer/common/UiUtil.java
new file mode 100644
index 000000000..4c4ebea11
--- /dev/null
+++ b/java/com/android/dialer/common/UiUtil.java
@@ -0,0 +1,41 @@
+/*
+ * 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.dialer.common;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+
+/** Utility class for commons functions used with Android UI. */
+public class UiUtil {
+
+ /** Hides the android keyboard. */
+ public static void hideKeyboardFrom(Context context, View view) {
+ InputMethodManager imm =
+ (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+ }
+
+ /** Opens the android keyboard. */
+ public static void openKeyboardFrom(Context context, View view) {
+ InputMethodManager inputMethodManager =
+ (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMethodManager.toggleSoftInputFromWindow(
+ view.getApplicationWindowToken(), InputMethodManager.SHOW_FORCED, 0);
+ }
+}
diff --git a/java/com/android/dialer/common/res/values/strings.xml b/java/com/android/dialer/common/res/values/strings.xml
new file mode 100644
index 000000000..8e9616178
--- /dev/null
+++ b/java/com/android/dialer/common/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="network_name_wifi">Wifi</string>
+ <string name="network_name_mobile">Mobile</string>
+</resources>
diff --git a/java/com/android/dialer/compat/ActivityCompat.java b/java/com/android/dialer/compat/ActivityCompat.java
new file mode 100644
index 000000000..e59b11593
--- /dev/null
+++ b/java/com/android/dialer/compat/ActivityCompat.java
@@ -0,0 +1,29 @@
+/*
+ * 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.dialer.compat;
+
+import android.app.Activity;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+
+/** Utility for calling methods introduced after Marshmallow for Activities. */
+public class ActivityCompat {
+
+ public static boolean isInMultiWindowMode(Activity activity) {
+ return VERSION.SDK_INT >= VERSION_CODES.N && activity.isInMultiWindowMode();
+ }
+}
diff --git a/java/com/android/dialer/compat/AppCompatConstants.java b/java/com/android/dialer/compat/AppCompatConstants.java
new file mode 100644
index 000000000..4a51d3f9e
--- /dev/null
+++ b/java/com/android/dialer/compat/AppCompatConstants.java
@@ -0,0 +1,33 @@
+/*
+ * 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.dialer.compat;
+
+import android.provider.CallLog.Calls;
+
+public final class AppCompatConstants {
+
+ public static final int CALLS_INCOMING_TYPE = Calls.INCOMING_TYPE;
+ public static final int CALLS_OUTGOING_TYPE = Calls.OUTGOING_TYPE;
+ public static final int CALLS_MISSED_TYPE = Calls.MISSED_TYPE;
+ public static final int CALLS_VOICEMAIL_TYPE = Calls.VOICEMAIL_TYPE;
+ // Added to android.provider.CallLog.Calls in N+.
+ public static final int CALLS_REJECTED_TYPE = 5;
+ // Added to android.provider.CallLog.Calls in N+.
+ public static final int CALLS_BLOCKED_TYPE = 6;
+ // Added to android.provider.CallLog.Calls in N+.
+ public static final int CALLS_ANSWERED_EXTERNALLY_TYPE = Calls.ANSWERED_EXTERNALLY_TYPE;
+}
diff --git a/java/com/android/dialer/compat/CompatUtils.java b/java/com/android/dialer/compat/CompatUtils.java
new file mode 100644
index 000000000..673cb709b
--- /dev/null
+++ b/java/com/android/dialer/compat/CompatUtils.java
@@ -0,0 +1,222 @@
+/*
+ * 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.dialer.compat;
+
+import android.os.Build;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.Log;
+import java.lang.reflect.InvocationTargetException;
+
+public final class CompatUtils {
+
+ private static final String TAG = CompatUtils.class.getSimpleName();
+
+ /** PrioritizedMimeType is added in API level 23. */
+ public static boolean hasPrioritizedMimeType() {
+ return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M;
+ }
+
+ /**
+ * Determines if this version is compatible with multi-SIM and the phone account APIs. Can also
+ * force the version to be lower through SdkVersionOverride.
+ *
+ * @return {@code true} if multi-SIM capability is available, {@code false} otherwise.
+ */
+ public static boolean isMSIMCompatible() {
+ return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP)
+ >= Build.VERSION_CODES.LOLLIPOP_MR1;
+ }
+
+ /**
+ * Determines if this version is compatible with video calling. Can also force the version to be
+ * lower through SdkVersionOverride.
+ *
+ * @return {@code true} if video calling is allowed, {@code false} otherwise.
+ */
+ public static boolean isVideoCompatible() {
+ return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP) >= Build.VERSION_CODES.M;
+ }
+
+ /**
+ * Determines if this version is capable of using presence checking for video calling. Support for
+ * video call presence indication is added in SDK 24.
+ *
+ * @return {@code true} if video presence checking is allowed, {@code false} otherwise.
+ */
+ public static boolean isVideoPresenceCompatible() {
+ return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) > Build.VERSION_CODES.M;
+ }
+
+ /**
+ * Determines if this version is compatible with call subject. Can also force the version to be
+ * lower through SdkVersionOverride.
+ *
+ * @return {@code true} if call subject is a feature on this device, {@code false} otherwise.
+ */
+ public static boolean isCallSubjectCompatible() {
+ return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP) >= Build.VERSION_CODES.M;
+ }
+
+ /**
+ * Determines if this version is compatible with a default dialer. Can also force the version to
+ * be lower through {@link SdkVersionOverride}.
+ *
+ * @return {@code true} if default dialer is a feature on this device, {@code false} otherwise.
+ */
+ public static boolean isDefaultDialerCompatible() {
+ return isMarshmallowCompatible();
+ }
+
+ /**
+ * Determines if this version is compatible with Lollipop Mr1-specific APIs. Can also force the
+ * version to be lower through SdkVersionOverride.
+ *
+ * @return {@code true} if runtime sdk is compatible with Lollipop MR1, {@code false} otherwise.
+ */
+ public static boolean isLollipopMr1Compatible() {
+ return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP_MR1)
+ >= Build.VERSION_CODES.LOLLIPOP_MR1;
+ }
+
+ /**
+ * Determines if this version is compatible with Marshmallow-specific APIs. Can also force the
+ * version to be lower through SdkVersionOverride.
+ *
+ * @return {@code true} if runtime sdk is compatible with Marshmallow, {@code false} otherwise.
+ */
+ public static boolean isMarshmallowCompatible() {
+ return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP) >= Build.VERSION_CODES.M;
+ }
+
+ /**
+ * Determines if the given class is available. Can be used to check if system apis exist at
+ * runtime.
+ *
+ * @param className the name of the class to look for.
+ * @return {@code true} if the given class is available, {@code false} otherwise or if className
+ * is empty.
+ */
+ public static boolean isClassAvailable(@Nullable String className) {
+ if (TextUtils.isEmpty(className)) {
+ return false;
+ }
+ try {
+ Class.forName(className);
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ } catch (Throwable t) {
+ Log.e(
+ TAG,
+ "Unexpected exception when checking if class:" + className + " exists at " + "runtime",
+ t);
+ return false;
+ }
+ }
+
+ /**
+ * Determines if the given class's method is available to call. Can be used to check if system
+ * apis exist at runtime.
+ *
+ * @param className the name of the class to look for
+ * @param methodName the name of the method to look for
+ * @param parameterTypes the needed parameter types for the method to look for
+ * @return {@code true} if the given class is available, {@code false} otherwise or if className
+ * or methodName are empty.
+ */
+ public static boolean isMethodAvailable(
+ @Nullable String className, @Nullable String methodName, Class<?>... parameterTypes) {
+ if (TextUtils.isEmpty(className) || TextUtils.isEmpty(methodName)) {
+ return false;
+ }
+
+ try {
+ Class.forName(className).getMethod(methodName, parameterTypes);
+ return true;
+ } catch (ClassNotFoundException | NoSuchMethodException e) {
+ Log.v(TAG, "Could not find method: " + className + "#" + methodName);
+ return false;
+ } catch (Throwable t) {
+ Log.e(
+ TAG,
+ "Unexpected exception when checking if method: "
+ + className
+ + "#"
+ + methodName
+ + " exists at runtime",
+ t);
+ return false;
+ }
+ }
+
+ /**
+ * Invokes a given class's method using reflection. Can be used to call system apis that exist at
+ * runtime but not in the SDK.
+ *
+ * @param instance The instance of the class to invoke the method on.
+ * @param methodName The name of the method to invoke.
+ * @param parameterTypes The needed parameter types for the method.
+ * @param parameters The parameter values to pass into the method.
+ * @return The result of the invocation or {@code null} if instance or methodName are empty, or if
+ * the reflection fails.
+ */
+ @Nullable
+ public static Object invokeMethod(
+ @Nullable Object instance,
+ @Nullable String methodName,
+ Class<?>[] parameterTypes,
+ Object[] parameters) {
+ if (instance == null || TextUtils.isEmpty(methodName)) {
+ return null;
+ }
+
+ String className = instance.getClass().getName();
+ try {
+ return Class.forName(className)
+ .getMethod(methodName, parameterTypes)
+ .invoke(instance, parameters);
+ } catch (ClassNotFoundException
+ | NoSuchMethodException
+ | IllegalArgumentException
+ | IllegalAccessException
+ | InvocationTargetException e) {
+ Log.v(TAG, "Could not invoke method: " + className + "#" + methodName);
+ return null;
+ } catch (Throwable t) {
+ Log.e(
+ TAG,
+ "Unexpected exception when invoking method: "
+ + className
+ + "#"
+ + methodName
+ + " at runtime",
+ t);
+ return null;
+ }
+ }
+
+ /**
+ * Determines if this version is compatible with Lollipop-specific APIs. Can also force the
+ * version to be lower through SdkVersionOverride.
+ *
+ * @return {@code true} if call subject is a feature on this device, {@code false} otherwise.
+ */
+ public static boolean isLollipopCompatible() {
+ return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP)
+ >= Build.VERSION_CODES.LOLLIPOP;
+ }
+}
diff --git a/java/com/android/dialer/compat/PathInterpolatorCompat.java b/java/com/android/dialer/compat/PathInterpolatorCompat.java
new file mode 100644
index 000000000..7139bc4af
--- /dev/null
+++ b/java/com/android/dialer/compat/PathInterpolatorCompat.java
@@ -0,0 +1,120 @@
+/*
+ * 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.dialer.compat;
+
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.os.Build;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+public class PathInterpolatorCompat {
+
+ public static Interpolator create(
+ float controlX1, float controlY1, float controlX2, float controlY2) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ return new PathInterpolator(controlX1, controlY1, controlX2, controlY2);
+ }
+ return new PathInterpolatorBase(controlX1, controlY1, controlX2, controlY2);
+ }
+
+ private static class PathInterpolatorBase implements Interpolator {
+
+ /** Governs the accuracy of the approximation of the {@link Path}. */
+ private static final float PRECISION = 0.002f;
+
+ private final float[] mX;
+ private final float[] mY;
+
+ public PathInterpolatorBase(Path path) {
+ final PathMeasure pathMeasure = new PathMeasure(path, false /* forceClosed */);
+
+ final float pathLength = pathMeasure.getLength();
+ final int numPoints = (int) (pathLength / PRECISION) + 1;
+
+ mX = new float[numPoints];
+ mY = new float[numPoints];
+
+ final float[] position = new float[2];
+ for (int i = 0; i < numPoints; ++i) {
+ final float distance = (i * pathLength) / (numPoints - 1);
+ pathMeasure.getPosTan(distance, position, null /* tangent */);
+
+ mX[i] = position[0];
+ mY[i] = position[1];
+ }
+ }
+
+ public PathInterpolatorBase(float controlX, float controlY) {
+ this(createQuad(controlX, controlY));
+ }
+
+ public PathInterpolatorBase(
+ float controlX1, float controlY1, float controlX2, float controlY2) {
+ this(createCubic(controlX1, controlY1, controlX2, controlY2));
+ }
+
+ private static Path createQuad(float controlX, float controlY) {
+ final Path path = new Path();
+ path.moveTo(0.0f, 0.0f);
+ path.quadTo(controlX, controlY, 1.0f, 1.0f);
+ return path;
+ }
+
+ private static Path createCubic(
+ float controlX1, float controlY1, float controlX2, float controlY2) {
+ final Path path = new Path();
+ path.moveTo(0.0f, 0.0f);
+ path.cubicTo(controlX1, controlY1, controlX2, controlY2, 1.0f, 1.0f);
+ return path;
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ if (t <= 0.0f) {
+ return 0.0f;
+ } else if (t >= 1.0f) {
+ return 1.0f;
+ }
+
+ // Do a binary search for the correct x to interpolate between.
+ int startIndex = 0;
+ int endIndex = mX.length - 1;
+ while (endIndex - startIndex > 1) {
+ int midIndex = (startIndex + endIndex) / 2;
+ if (t < mX[midIndex]) {
+ endIndex = midIndex;
+ } else {
+ startIndex = midIndex;
+ }
+ }
+
+ final float xRange = mX[endIndex] - mX[startIndex];
+ if (xRange == 0) {
+ return mY[startIndex];
+ }
+
+ final float tInRange = t - mX[startIndex];
+ final float fraction = tInRange / xRange;
+
+ final float startY = mY[startIndex];
+ final float endY = mY[endIndex];
+
+ return startY + (fraction * (endY - startY));
+ }
+ }
+}
diff --git a/java/com/android/dialer/compat/SdkVersionOverride.java b/java/com/android/dialer/compat/SdkVersionOverride.java
new file mode 100644
index 000000000..1d253a355
--- /dev/null
+++ b/java/com/android/dialer/compat/SdkVersionOverride.java
@@ -0,0 +1,43 @@
+/*
+ * 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.dialer.compat;
+
+import android.os.Build.VERSION;
+
+/**
+ * Class used to override the current sdk version to test specific branches of compatibility logic.
+ * When such branching occurs, use {@link #getSdkVersion(int)} rather than explicitly calling {@link
+ * VERSION#SDK_INT}. This allows the sdk version to be forced to a specific value.
+ */
+public class SdkVersionOverride {
+
+ /** Flag used to determine if override sdk versions are returned. */
+ private static final boolean ALLOW_OVERRIDE_VERSION = false;
+
+ private SdkVersionOverride() {}
+
+ /**
+ * Gets the sdk version
+ *
+ * @param overrideVersion the version to attempt using
+ * @return overrideVersion if the {@link #ALLOW_OVERRIDE_VERSION} flag is set to {@code true},
+ * otherwise the current version
+ */
+ public static int getSdkVersion(int overrideVersion) {
+ return ALLOW_OVERRIDE_VERSION ? overrideVersion : VERSION.SDK_INT;
+ }
+}
diff --git a/java/com/android/dialer/constants/Constants.java b/java/com/android/dialer/constants/Constants.java
new file mode 100644
index 000000000..77773018a
--- /dev/null
+++ b/java/com/android/dialer/constants/Constants.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 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.dialer.constants;
+
+import android.support.annotation.NonNull;
+import com.android.dialer.common.Assert;
+import com.android.dialer.proguard.UsedByReflection;
+import com.android.dialer.constants.ConstantsImpl;
+
+/**
+ * Utility to access constants that are different across build variants (Google Dialer, AOSP,
+ * etc...). This functionality depends on a an implementation being present in the app that has the
+ * same package and the class name ending in "Impl". For example,
+ * com.android.dialer.constants.ConstantsImpl. This class is found by the module using reflection.
+ */
+@UsedByReflection(value = "Constants.java")
+public abstract class Constants {
+ private static Constants instance = new ConstantsImpl();
+ private static boolean didInitializeInstance;
+
+ @NonNull
+ public static synchronized Constants get() {
+ return instance;
+ }
+
+ @NonNull
+ public abstract String getFilteredNumberProviderAuthority();
+
+ @NonNull
+ public abstract String getFileProviderAuthority();
+
+ protected Constants() {}
+}
diff --git a/java/com/android/dialer/constants/ScheduledJobIds.java b/java/com/android/dialer/constants/ScheduledJobIds.java
new file mode 100644
index 000000000..88e9a3d4f
--- /dev/null
+++ b/java/com/android/dialer/constants/ScheduledJobIds.java
@@ -0,0 +1,31 @@
+/*
+ * 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.dialer.constants;
+
+/**
+ * Registry of scheduled job ids used by the dialer UID.
+ *
+ * <p>Any dialer jobs which use the android JobScheduler should register their IDs here, to avoid
+ * the same ID accidentally being reused.
+ */
+public final class ScheduledJobIds {
+ public static final int SPAM_JOB_WIFI = 50;
+ public static final int SPAM_JOB_ANY_NETWORK = 51;
+
+ // This job refreshes dynamic launcher shortcuts.
+ public static final int SHORTCUT_PERIODIC_JOB = 100;
+}
diff --git a/java/com/android/dialer/constants/aospdialer/ConstantsImpl.java b/java/com/android/dialer/constants/aospdialer/ConstantsImpl.java
new file mode 100644
index 000000000..6b78b986c
--- /dev/null
+++ b/java/com/android/dialer/constants/aospdialer/ConstantsImpl.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.dialer.constants;
+
+import android.support.annotation.NonNull;
+import com.android.dialer.proguard.UsedByReflection;
+
+/** Provider config values for AOSP Dialer. */
+@UsedByReflection(value = "Constants.java")
+public class ConstantsImpl extends Constants {
+
+ @Override
+ @NonNull
+ public String getFilteredNumberProviderAuthority() {
+ return "com.android.dialer.blocking.filterednumberprovider";
+ }
+
+ @Override
+ @NonNull
+ public String getFileProviderAuthority() {
+ return "com.android.dialer.files";
+ }
+}
diff --git a/java/com/android/dialer/database/CallLogQueryHandler.java b/java/com/android/dialer/database/CallLogQueryHandler.java
new file mode 100644
index 000000000..ffca69f40
--- /dev/null
+++ b/java/com/android/dialer/database/CallLogQueryHandler.java
@@ -0,0 +1,369 @@
+/*
+ * 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.dialer.database;
+
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabaseCorruptException;
+import android.database.sqlite.SQLiteDiskIOException;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteFullException;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.CallLog.Calls;
+import android.provider.VoicemailContract.Status;
+import android.provider.VoicemailContract.Voicemails;
+import com.android.contacts.common.database.NoNullCursorAsyncQueryHandler;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.compat.AppCompatConstants;
+import com.android.dialer.compat.SdkVersionOverride;
+import com.android.dialer.phonenumbercache.CallLogQuery;
+import com.android.dialer.telecom.TelecomUtil;
+import com.android.dialer.util.PermissionsUtil;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Handles asynchronous queries to the call log. */
+public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
+
+ /**
+ * Call type similar to Calls.INCOMING_TYPE used to specify all types instead of one particular
+ * type. Exception: excludes Calls.VOICEMAIL_TYPE.
+ */
+ public static final int CALL_TYPE_ALL = -1;
+
+ private static final String TAG = "CallLogQueryHandler";
+ private static final int NUM_LOGS_TO_DISPLAY = 1000;
+ /** The token for the query to fetch the old entries from the call log. */
+ private static final int QUERY_CALLLOG_TOKEN = 54;
+ /** The token for the query to mark all missed calls as old after seeing the call log. */
+ private static final int UPDATE_MARK_AS_OLD_TOKEN = 55;
+ /** The token for the query to mark all missed calls as read after seeing the call log. */
+ private static final int UPDATE_MARK_MISSED_CALL_AS_READ_TOKEN = 56;
+ /** The token for the query to fetch voicemail status messages. */
+ private static final int QUERY_VOICEMAIL_STATUS_TOKEN = 57;
+ /** The token for the query to fetch the number of unread voicemails. */
+ private static final int QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN = 58;
+ /** The token for the query to fetch the number of missed calls. */
+ private static final int QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN = 59;
+
+ private final int mLogLimit;
+ private final WeakReference<Listener> mListener;
+
+ private final Context mContext;
+
+ public CallLogQueryHandler(Context context, ContentResolver contentResolver, Listener listener) {
+ this(context, contentResolver, listener, -1);
+ }
+
+ public CallLogQueryHandler(
+ Context context, ContentResolver contentResolver, Listener listener, int limit) {
+ super(contentResolver);
+ mContext = context.getApplicationContext();
+ mListener = new WeakReference<Listener>(listener);
+ mLogLimit = limit;
+ }
+
+ @Override
+ protected Handler createHandler(Looper looper) {
+ // Provide our special handler that catches exceptions
+ return new CatchingWorkerHandler(looper);
+ }
+
+ /**
+ * Fetches the list of calls from the call log for a given type. This call ignores the new or old
+ * state.
+ *
+ * <p>It will asynchronously update the content of the list view when the fetch completes.
+ */
+ public void fetchCalls(int callType, long newerThan) {
+ cancelFetch();
+ if (PermissionsUtil.hasPhonePermissions(mContext)) {
+ fetchCalls(QUERY_CALLLOG_TOKEN, callType, false /* newOnly */, newerThan);
+ } else {
+ updateAdapterData(null);
+ }
+ }
+
+ public void fetchCalls(int callType) {
+ fetchCalls(callType, 0);
+ }
+
+ public void fetchVoicemailStatus() {
+ if (TelecomUtil.hasReadWriteVoicemailPermissions(mContext)) {
+ startQuery(
+ QUERY_VOICEMAIL_STATUS_TOKEN,
+ null,
+ Status.CONTENT_URI,
+ VoicemailStatusQuery.getProjection(),
+ null,
+ null,
+ null);
+ }
+ }
+
+ public void fetchVoicemailUnreadCount() {
+ if (TelecomUtil.hasReadWriteVoicemailPermissions(mContext)) {
+ // Only count voicemails that have not been read and have not been deleted.
+ startQuery(
+ QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN,
+ null,
+ Voicemails.CONTENT_URI,
+ new String[] {Voicemails._ID},
+ Voicemails.IS_READ + "=0" + " AND " + Voicemails.DELETED + "=0",
+ null,
+ null);
+ }
+ }
+
+ /** Fetches the list of calls in the call log. */
+ private void fetchCalls(int token, int callType, boolean newOnly, long newerThan) {
+ StringBuilder where = new StringBuilder();
+ List<String> selectionArgs = new ArrayList<>();
+
+ // Always hide blocked calls.
+ where.append("(").append(Calls.TYPE).append(" != ?)");
+ selectionArgs.add(Integer.toString(AppCompatConstants.CALLS_BLOCKED_TYPE));
+
+ // Ignore voicemails marked as deleted
+ if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) {
+ where.append(" AND (").append(Voicemails.DELETED).append(" = 0)");
+ }
+
+ if (newOnly) {
+ where.append(" AND (").append(Calls.NEW).append(" = 1)");
+ }
+
+ if (callType > CALL_TYPE_ALL) {
+ where.append(" AND (").append(Calls.TYPE).append(" = ?)");
+ selectionArgs.add(Integer.toString(callType));
+ } else {
+ where.append(" AND NOT ");
+ where.append("(" + Calls.TYPE + " = " + AppCompatConstants.CALLS_VOICEMAIL_TYPE + ")");
+ }
+
+ if (newerThan > 0) {
+ where.append(" AND (").append(Calls.DATE).append(" > ?)");
+ selectionArgs.add(Long.toString(newerThan));
+ }
+
+ final int limit = (mLogLimit == -1) ? NUM_LOGS_TO_DISPLAY : mLogLimit;
+ final String selection = where.length() > 0 ? where.toString() : null;
+ Uri uri =
+ TelecomUtil.getCallLogUri(mContext)
+ .buildUpon()
+ .appendQueryParameter(Calls.LIMIT_PARAM_KEY, Integer.toString(limit))
+ .build();
+ startQuery(
+ token,
+ null,
+ uri,
+ CallLogQuery.getProjection(),
+ selection,
+ selectionArgs.toArray(new String[selectionArgs.size()]),
+ Calls.DEFAULT_SORT_ORDER);
+ }
+
+ /** Cancel any pending fetch request. */
+ private void cancelFetch() {
+ cancelOperation(QUERY_CALLLOG_TOKEN);
+ }
+
+ /** Updates all new calls to mark them as old. */
+ public void markNewCallsAsOld() {
+ if (!PermissionsUtil.hasPhonePermissions(mContext)) {
+ return;
+ }
+ // Mark all "new" calls as not new anymore.
+ StringBuilder where = new StringBuilder();
+ where.append(Calls.NEW);
+ where.append(" = 1");
+
+ ContentValues values = new ContentValues(1);
+ values.put(Calls.NEW, "0");
+
+ startUpdate(
+ UPDATE_MARK_AS_OLD_TOKEN,
+ null,
+ TelecomUtil.getCallLogUri(mContext),
+ values,
+ where.toString(),
+ null);
+ }
+
+ /** Updates all missed calls to mark them as read. */
+ public void markMissedCallsAsRead() {
+ if (!PermissionsUtil.hasPhonePermissions(mContext)) {
+ return;
+ }
+
+ ContentValues values = new ContentValues(1);
+ values.put(Calls.IS_READ, "1");
+
+ startUpdate(
+ UPDATE_MARK_MISSED_CALL_AS_READ_TOKEN,
+ null,
+ Calls.CONTENT_URI,
+ values,
+ getUnreadMissedCallsQuery(),
+ null);
+ }
+
+ /** Fetch all missed calls received since last time the tab was opened. */
+ public void fetchMissedCallsUnreadCount() {
+ if (!PermissionsUtil.hasPhonePermissions(mContext)) {
+ return;
+ }
+
+ startQuery(
+ QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN,
+ null,
+ Calls.CONTENT_URI,
+ new String[] {Calls._ID},
+ getUnreadMissedCallsQuery(),
+ null,
+ null);
+ }
+
+ @Override
+ protected synchronized void onNotNullableQueryComplete(int token, Object cookie, Cursor cursor) {
+ if (cursor == null) {
+ return;
+ }
+ try {
+ if (token == QUERY_CALLLOG_TOKEN) {
+ if (updateAdapterData(cursor)) {
+ cursor = null;
+ }
+ } else if (token == QUERY_VOICEMAIL_STATUS_TOKEN) {
+ updateVoicemailStatus(cursor);
+ } else if (token == QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN) {
+ updateVoicemailUnreadCount(cursor);
+ } else if (token == QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN) {
+ updateMissedCallsUnreadCount(cursor);
+ } else {
+ LogUtil.w(
+ "CallLogQueryHandler.onNotNullableQueryComplete",
+ "unknown query completed: ignoring: " + token);
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ /**
+ * Updates the adapter in the call log fragment to show the new cursor data. Returns true if the
+ * listener took ownership of the cursor.
+ */
+ private boolean updateAdapterData(Cursor cursor) {
+ final Listener listener = mListener.get();
+ if (listener != null) {
+ return listener.onCallsFetched(cursor);
+ }
+ return false;
+ }
+
+ /** @return Query string to get all unread missed calls. */
+ private String getUnreadMissedCallsQuery() {
+ StringBuilder where = new StringBuilder();
+ where.append(Calls.IS_READ).append(" = 0 OR ").append(Calls.IS_READ).append(" IS NULL");
+ where.append(" AND ");
+ where.append(Calls.TYPE).append(" = ").append(Calls.MISSED_TYPE);
+ return where.toString();
+ }
+
+ private void updateVoicemailStatus(Cursor statusCursor) {
+ final Listener listener = mListener.get();
+ if (listener != null) {
+ listener.onVoicemailStatusFetched(statusCursor);
+ }
+ }
+
+ private void updateVoicemailUnreadCount(Cursor statusCursor) {
+ final Listener listener = mListener.get();
+ if (listener != null) {
+ listener.onVoicemailUnreadCountFetched(statusCursor);
+ }
+ }
+
+ private void updateMissedCallsUnreadCount(Cursor statusCursor) {
+ final Listener listener = mListener.get();
+ if (listener != null) {
+ listener.onMissedCallsUnreadCountFetched(statusCursor);
+ }
+ }
+
+ /** Listener to completion of various queries. */
+ public interface Listener {
+
+ /** Called when {@link CallLogQueryHandler#fetchVoicemailStatus()} completes. */
+ void onVoicemailStatusFetched(Cursor statusCursor);
+
+ /** Called when {@link CallLogQueryHandler#fetchVoicemailUnreadCount()} completes. */
+ void onVoicemailUnreadCountFetched(Cursor cursor);
+
+ /** Called when {@link CallLogQueryHandler#fetchMissedCallsUnreadCount()} completes. */
+ void onMissedCallsUnreadCountFetched(Cursor cursor);
+
+ /**
+ * Called when {@link CallLogQueryHandler#fetchCalls(int)} complete. Returns true if takes
+ * ownership of cursor.
+ */
+ boolean onCallsFetched(Cursor combinedCursor);
+ }
+
+ /**
+ * Simple handler that wraps background calls to catch {@link SQLiteException}, such as when the
+ * disk is full.
+ */
+ protected class CatchingWorkerHandler extends AsyncQueryHandler.WorkerHandler {
+
+ public CatchingWorkerHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ try {
+ // Perform same query while catching any exceptions
+ super.handleMessage(msg);
+ } catch (SQLiteDiskIOException e) {
+ LogUtil.e("CallLogQueryHandler.handleMessage", "exception on background worker thread", e);
+ } catch (SQLiteFullException e) {
+ LogUtil.e("CallLogQueryHandler.handleMessage", "exception on background worker thread", e);
+ } catch (SQLiteDatabaseCorruptException e) {
+ LogUtil.e("CallLogQueryHandler.handleMessage", "exception on background worker thread", e);
+ } catch (IllegalArgumentException e) {
+ LogUtil.e("CallLogQueryHandler.handleMessage", "contactsProvider not present on device", e);
+ } catch (SecurityException e) {
+ // Shouldn't happen if we are protecting the entry points correctly,
+ // but just in case.
+ LogUtil.e(
+ "CallLogQueryHandler.handleMessage", "no permission to access ContactsProvider.", e);
+ }
+ }
+ }
+}
diff --git a/java/com/android/dialer/database/Database.java b/java/com/android/dialer/database/Database.java
new file mode 100644
index 000000000..d13f15e48
--- /dev/null
+++ b/java/com/android/dialer/database/Database.java
@@ -0,0 +1,49 @@
+/*
+ * 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.dialer.database;
+
+import android.content.Context;
+import java.util.Objects;
+
+/** Accessor for the database bindings. */
+public class Database {
+
+ private static DatabaseBindings databaseBindings;
+
+ private Database() {}
+
+ public static DatabaseBindings get(Context context) {
+ Objects.requireNonNull(context);
+ if (databaseBindings != null) {
+ return databaseBindings;
+ }
+
+ Context application = context.getApplicationContext();
+ if (application instanceof DatabaseBindingsFactory) {
+ databaseBindings = ((DatabaseBindingsFactory) application).newDatabaseBindings();
+ }
+
+ if (databaseBindings == null) {
+ databaseBindings = new DatabaseBindingsStub();
+ }
+ return databaseBindings;
+ }
+
+ public static void setForTesting(DatabaseBindings databaseBindings) {
+ Database.databaseBindings = databaseBindings;
+ }
+}
diff --git a/java/com/android/dialer/database/DatabaseBindings.java b/java/com/android/dialer/database/DatabaseBindings.java
new file mode 100644
index 000000000..f07b265b3
--- /dev/null
+++ b/java/com/android/dialer/database/DatabaseBindings.java
@@ -0,0 +1,25 @@
+/*
+ * 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.dialer.database;
+
+import android.content.Context;
+
+/** This interface allows the container application to customize the database module. */
+public interface DatabaseBindings {
+
+ DialerDatabaseHelper getDatabaseHelper(Context context);
+}
diff --git a/java/com/android/dialer/database/DatabaseBindingsFactory.java b/java/com/android/dialer/database/DatabaseBindingsFactory.java
new file mode 100644
index 000000000..7fa175ed5
--- /dev/null
+++ b/java/com/android/dialer/database/DatabaseBindingsFactory.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dialer.database;
+
+/**
+ * This interface should be implementated by the Application subclass. It allows the dialer module
+ * to get references to the DatabaseBindings.
+ */
+public interface DatabaseBindingsFactory {
+
+ DatabaseBindings newDatabaseBindings();
+}
diff --git a/java/com/android/dialer/database/DatabaseBindingsStub.java b/java/com/android/dialer/database/DatabaseBindingsStub.java
new file mode 100644
index 000000000..df8186ab0
--- /dev/null
+++ b/java/com/android/dialer/database/DatabaseBindingsStub.java
@@ -0,0 +1,35 @@
+/*
+ * 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.dialer.database;
+
+import android.content.Context;
+
+/** Default implementation for database bindings. */
+public class DatabaseBindingsStub implements DatabaseBindings {
+
+ private DialerDatabaseHelper dialerDatabaseHelper;
+
+ @Override
+ public DialerDatabaseHelper getDatabaseHelper(Context context) {
+ if (dialerDatabaseHelper == null) {
+ dialerDatabaseHelper =
+ new DialerDatabaseHelper(
+ context, DialerDatabaseHelper.DATABASE_NAME, DialerDatabaseHelper.DATABASE_VERSION);
+ }
+ return dialerDatabaseHelper;
+ }
+}
diff --git a/java/com/android/dialer/database/DialerDatabaseHelper.java b/java/com/android/dialer/database/DialerDatabaseHelper.java
new file mode 100644
index 000000000..234958b62
--- /dev/null
+++ b/java/com/android/dialer/database/DialerDatabaseHelper.java
@@ -0,0 +1,1242 @@
+/*
+ * Copyright (C) 2013 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.dialer.database;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteStatement;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.provider.BaseColumns;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import com.android.contacts.common.R;
+import com.android.contacts.common.util.StopWatch;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
+import com.android.dialer.smartdial.SmartDialNameMatcher;
+import com.android.dialer.smartdial.SmartDialPrefix;
+import com.android.dialer.util.PermissionsUtil;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Database helper for smart dial. Designed as a singleton to make sure there is only one access
+ * point to the database. Provides methods to maintain, update, and query the database.
+ */
+public class DialerDatabaseHelper extends SQLiteOpenHelper {
+
+ /**
+ * SmartDial DB version ranges:
+ *
+ * <pre>
+ * 0-98 KitKat
+ * </pre>
+ */
+ public static final int DATABASE_VERSION = 10;
+
+ public static final String DATABASE_NAME = "dialer.db";
+ public static final Uri SMART_DIAL_UPDATED_URI =
+ Uri.parse("content://com.android.dialer/smart_dial_updated");
+ private static final String TAG = "DialerDatabaseHelper";
+ private static final boolean DEBUG = false;
+ /** Saves the last update time of smart dial databases to shared preferences. */
+ private static final String DATABASE_LAST_CREATED_SHARED_PREF = "com.android.dialer";
+
+ private static final String LAST_UPDATED_MILLIS = "last_updated_millis";
+ private static final String DATABASE_VERSION_PROPERTY = "database_version";
+ private static final int MAX_ENTRIES = 20;
+
+ private final Context mContext;
+ private final Object mLock = new Object();
+ private final AtomicBoolean mInUpdate = new AtomicBoolean(false);
+ private boolean mIsTestInstance = false;
+
+ protected DialerDatabaseHelper(Context context, String databaseName, int dbVersion) {
+ super(context, databaseName, null, dbVersion);
+ mContext = Objects.requireNonNull(context, "Context must not be null");
+ }
+
+ public void setIsTestInstance(boolean isTestInstance) {
+ mIsTestInstance = isTestInstance;
+ }
+
+ /**
+ * Creates tables in the database when database is created for the first time.
+ *
+ * @param db The database.
+ */
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ setupTables(db);
+ }
+
+ private void setupTables(SQLiteDatabase db) {
+ dropTables(db);
+ db.execSQL(
+ "CREATE TABLE "
+ + Tables.SMARTDIAL_TABLE
+ + " ("
+ + SmartDialDbColumns._ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SmartDialDbColumns.DATA_ID
+ + " INTEGER, "
+ + SmartDialDbColumns.NUMBER
+ + " TEXT,"
+ + SmartDialDbColumns.CONTACT_ID
+ + " INTEGER,"
+ + SmartDialDbColumns.LOOKUP_KEY
+ + " TEXT,"
+ + SmartDialDbColumns.DISPLAY_NAME_PRIMARY
+ + " TEXT, "
+ + SmartDialDbColumns.PHOTO_ID
+ + " INTEGER, "
+ + SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME
+ + " LONG, "
+ + SmartDialDbColumns.LAST_TIME_USED
+ + " LONG, "
+ + SmartDialDbColumns.TIMES_USED
+ + " INTEGER, "
+ + SmartDialDbColumns.STARRED
+ + " INTEGER, "
+ + SmartDialDbColumns.IS_SUPER_PRIMARY
+ + " INTEGER, "
+ + SmartDialDbColumns.IN_VISIBLE_GROUP
+ + " INTEGER, "
+ + SmartDialDbColumns.IS_PRIMARY
+ + " INTEGER, "
+ + SmartDialDbColumns.CARRIER_PRESENCE
+ + " INTEGER NOT NULL DEFAULT 0"
+ + ");");
+
+ db.execSQL(
+ "CREATE TABLE "
+ + Tables.PREFIX_TABLE
+ + " ("
+ + PrefixColumns._ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + PrefixColumns.PREFIX
+ + " TEXT COLLATE NOCASE, "
+ + PrefixColumns.CONTACT_ID
+ + " INTEGER"
+ + ");");
+
+ db.execSQL(
+ "CREATE TABLE "
+ + Tables.PROPERTIES
+ + " ("
+ + PropertiesColumns.PROPERTY_KEY
+ + " TEXT PRIMARY KEY, "
+ + PropertiesColumns.PROPERTY_VALUE
+ + " TEXT "
+ + ");");
+
+ // This will need to also be updated in setupTablesForFilteredNumberTest and onUpgrade.
+ // Hardcoded so we know on glance what columns are updated in setupTables,
+ // and to be able to guarantee the state of the DB at each upgrade step.
+ db.execSQL(
+ "CREATE TABLE "
+ + Tables.FILTERED_NUMBER_TABLE
+ + " ("
+ + FilteredNumberColumns._ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + FilteredNumberColumns.NORMALIZED_NUMBER
+ + " TEXT UNIQUE,"
+ + FilteredNumberColumns.NUMBER
+ + " TEXT,"
+ + FilteredNumberColumns.COUNTRY_ISO
+ + " TEXT,"
+ + FilteredNumberColumns.TIMES_FILTERED
+ + " INTEGER,"
+ + FilteredNumberColumns.LAST_TIME_FILTERED
+ + " LONG,"
+ + FilteredNumberColumns.CREATION_TIME
+ + " LONG,"
+ + FilteredNumberColumns.TYPE
+ + " INTEGER,"
+ + FilteredNumberColumns.SOURCE
+ + " INTEGER"
+ + ");");
+
+ setProperty(db, DATABASE_VERSION_PROPERTY, String.valueOf(DATABASE_VERSION));
+ if (!mIsTestInstance) {
+ resetSmartDialLastUpdatedTime();
+ }
+ }
+
+ public void dropTables(SQLiteDatabase db) {
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.PREFIX_TABLE);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.SMARTDIAL_TABLE);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.PROPERTIES);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.FILTERED_NUMBER_TABLE);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.VOICEMAIL_ARCHIVE_TABLE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldNumber, int newNumber) {
+ // Disregard the old version and new versions provided by SQLiteOpenHelper, we will read
+ // our own from the database.
+
+ int oldVersion;
+
+ oldVersion = getPropertyAsInt(db, DATABASE_VERSION_PROPERTY, 0);
+
+ if (oldVersion == 0) {
+ LogUtil.e(
+ "DialerDatabaseHelper.onUpgrade", "malformed database version..recreating database");
+ }
+
+ if (oldVersion < 4) {
+ setupTables(db);
+ return;
+ }
+
+ if (oldVersion < 7) {
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.FILTERED_NUMBER_TABLE);
+ db.execSQL(
+ "CREATE TABLE "
+ + Tables.FILTERED_NUMBER_TABLE
+ + " ("
+ + FilteredNumberColumns._ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + FilteredNumberColumns.NORMALIZED_NUMBER
+ + " TEXT UNIQUE,"
+ + FilteredNumberColumns.NUMBER
+ + " TEXT,"
+ + FilteredNumberColumns.COUNTRY_ISO
+ + " TEXT,"
+ + FilteredNumberColumns.TIMES_FILTERED
+ + " INTEGER,"
+ + FilteredNumberColumns.LAST_TIME_FILTERED
+ + " LONG,"
+ + FilteredNumberColumns.CREATION_TIME
+ + " LONG,"
+ + FilteredNumberColumns.TYPE
+ + " INTEGER,"
+ + FilteredNumberColumns.SOURCE
+ + " INTEGER"
+ + ");");
+ oldVersion = 7;
+ }
+
+ if (oldVersion < 8) {
+ upgradeToVersion8(db);
+ oldVersion = 8;
+ }
+
+ if (oldVersion < 10) {
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.VOICEMAIL_ARCHIVE_TABLE);
+ oldVersion = 10;
+ }
+
+ if (oldVersion != DATABASE_VERSION) {
+ throw new IllegalStateException(
+ "error upgrading the database to version " + DATABASE_VERSION);
+ }
+
+ setProperty(db, DATABASE_VERSION_PROPERTY, String.valueOf(DATABASE_VERSION));
+ }
+
+ public void upgradeToVersion8(SQLiteDatabase db) {
+ db.execSQL("ALTER TABLE smartdial_table ADD carrier_presence INTEGER NOT NULL DEFAULT 0");
+ }
+
+ /** Stores a key-value pair in the {@link Tables#PROPERTIES} table. */
+ public void setProperty(String key, String value) {
+ setProperty(getWritableDatabase(), key, value);
+ }
+
+ public void setProperty(SQLiteDatabase db, String key, String value) {
+ final ContentValues values = new ContentValues();
+ values.put(PropertiesColumns.PROPERTY_KEY, key);
+ values.put(PropertiesColumns.PROPERTY_VALUE, value);
+ db.replace(Tables.PROPERTIES, null, values);
+ }
+
+ /** Returns the value from the {@link Tables#PROPERTIES} table. */
+ public String getProperty(String key, String defaultValue) {
+ return getProperty(getReadableDatabase(), key, defaultValue);
+ }
+
+ public String getProperty(SQLiteDatabase db, String key, String defaultValue) {
+ try {
+ String value = null;
+ final Cursor cursor =
+ db.query(
+ Tables.PROPERTIES,
+ new String[] {PropertiesColumns.PROPERTY_VALUE},
+ PropertiesColumns.PROPERTY_KEY + "=?",
+ new String[] {key},
+ null,
+ null,
+ null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ value = cursor.getString(0);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ return value != null ? value : defaultValue;
+ } catch (SQLiteException e) {
+ return defaultValue;
+ }
+ }
+
+ public int getPropertyAsInt(SQLiteDatabase db, String key, int defaultValue) {
+ final String stored = getProperty(db, key, "");
+ try {
+ return Integer.parseInt(stored);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+
+ private void resetSmartDialLastUpdatedTime() {
+ final SharedPreferences databaseLastUpdateSharedPref =
+ mContext.getSharedPreferences(DATABASE_LAST_CREATED_SHARED_PREF, Context.MODE_PRIVATE);
+ final SharedPreferences.Editor editor = databaseLastUpdateSharedPref.edit();
+ editor.putLong(LAST_UPDATED_MILLIS, 0);
+ editor.apply();
+ }
+
+ /** Starts the database upgrade process in the background. */
+ public void startSmartDialUpdateThread() {
+ if (PermissionsUtil.hasContactsPermissions(mContext)) {
+ new SmartDialUpdateAsyncTask().execute();
+ }
+ }
+
+ /**
+ * Removes rows in the smartdial database that matches the contacts that have been deleted by
+ * other apps since last update.
+ *
+ * @param db Database to operate on.
+ * @param deletedContactCursor Cursor containing rows of deleted contacts
+ */
+ @VisibleForTesting
+ void removeDeletedContacts(SQLiteDatabase db, Cursor deletedContactCursor) {
+ if (deletedContactCursor == null) {
+ return;
+ }
+
+ db.beginTransaction();
+ try {
+ while (deletedContactCursor.moveToNext()) {
+ final Long deleteContactId =
+ deletedContactCursor.getLong(DeleteContactQuery.DELETED_CONTACT_ID);
+ db.delete(
+ Tables.SMARTDIAL_TABLE, SmartDialDbColumns.CONTACT_ID + "=" + deleteContactId, null);
+ db.delete(Tables.PREFIX_TABLE, PrefixColumns.CONTACT_ID + "=" + deleteContactId, null);
+ }
+
+ db.setTransactionSuccessful();
+ } finally {
+ deletedContactCursor.close();
+ db.endTransaction();
+ }
+ }
+
+ private Cursor getDeletedContactCursor(String lastUpdateMillis) {
+ return mContext
+ .getContentResolver()
+ .query(
+ DeleteContactQuery.URI,
+ DeleteContactQuery.PROJECTION,
+ DeleteContactQuery.SELECT_UPDATED_CLAUSE,
+ new String[] {lastUpdateMillis},
+ null);
+ }
+
+ /**
+ * Removes potentially corrupted entries in the database. These contacts may be added before the
+ * previous instance of the dialer was destroyed for some reason. For data integrity, we delete
+ * all of them.
+ *
+ * @param db Database pointer to the dialer database.
+ * @param last_update_time Time stamp of last successful update of the dialer database.
+ */
+ private void removePotentiallyCorruptedContacts(SQLiteDatabase db, String last_update_time) {
+ db.delete(
+ Tables.PREFIX_TABLE,
+ PrefixColumns.CONTACT_ID
+ + " IN "
+ + "(SELECT "
+ + SmartDialDbColumns.CONTACT_ID
+ + " FROM "
+ + Tables.SMARTDIAL_TABLE
+ + " WHERE "
+ + SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME
+ + " > "
+ + last_update_time
+ + ")",
+ null);
+ db.delete(
+ Tables.SMARTDIAL_TABLE,
+ SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + " > " + last_update_time,
+ null);
+ }
+
+ /**
+ * Removes rows in the smartdial database that matches updated contacts.
+ *
+ * @param db Database pointer to the smartdial database
+ * @param updatedContactCursor Cursor pointing to the list of recently updated contacts.
+ */
+ @VisibleForTesting
+ void removeUpdatedContacts(SQLiteDatabase db, Cursor updatedContactCursor) {
+ db.beginTransaction();
+ try {
+ updatedContactCursor.moveToPosition(-1);
+ while (updatedContactCursor.moveToNext()) {
+ final Long contactId = updatedContactCursor.getLong(UpdatedContactQuery.UPDATED_CONTACT_ID);
+
+ db.delete(Tables.SMARTDIAL_TABLE, SmartDialDbColumns.CONTACT_ID + "=" + contactId, null);
+ db.delete(Tables.PREFIX_TABLE, PrefixColumns.CONTACT_ID + "=" + contactId, null);
+ }
+
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ /**
+ * Inserts updated contacts as rows to the smartdial table.
+ *
+ * @param db Database pointer to the smartdial database.
+ * @param updatedContactCursor Cursor pointing to the list of recently updated contacts.
+ * @param currentMillis Current time to be recorded in the smartdial table as update timestamp.
+ */
+ @VisibleForTesting
+ protected void insertUpdatedContactsAndNumberPrefix(
+ SQLiteDatabase db, Cursor updatedContactCursor, Long currentMillis) {
+ db.beginTransaction();
+ try {
+ final String sqlInsert =
+ "INSERT INTO "
+ + Tables.SMARTDIAL_TABLE
+ + " ("
+ + SmartDialDbColumns.DATA_ID
+ + ", "
+ + SmartDialDbColumns.NUMBER
+ + ", "
+ + SmartDialDbColumns.CONTACT_ID
+ + ", "
+ + SmartDialDbColumns.LOOKUP_KEY
+ + ", "
+ + SmartDialDbColumns.DISPLAY_NAME_PRIMARY
+ + ", "
+ + SmartDialDbColumns.PHOTO_ID
+ + ", "
+ + SmartDialDbColumns.LAST_TIME_USED
+ + ", "
+ + SmartDialDbColumns.TIMES_USED
+ + ", "
+ + SmartDialDbColumns.STARRED
+ + ", "
+ + SmartDialDbColumns.IS_SUPER_PRIMARY
+ + ", "
+ + SmartDialDbColumns.IN_VISIBLE_GROUP
+ + ", "
+ + SmartDialDbColumns.IS_PRIMARY
+ + ", "
+ + SmartDialDbColumns.CARRIER_PRESENCE
+ + ", "
+ + SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME
+ + ") "
+ + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+ final SQLiteStatement insert = db.compileStatement(sqlInsert);
+
+ final String numberSqlInsert =
+ "INSERT INTO "
+ + Tables.PREFIX_TABLE
+ + " ("
+ + PrefixColumns.CONTACT_ID
+ + ", "
+ + PrefixColumns.PREFIX
+ + ") "
+ + " VALUES (?, ?)";
+ final SQLiteStatement numberInsert = db.compileStatement(numberSqlInsert);
+
+ updatedContactCursor.moveToPosition(-1);
+ while (updatedContactCursor.moveToNext()) {
+ insert.clearBindings();
+
+ // Handle string columns which can possibly be null first. In the case of certain
+ // null columns (due to malformed rows possibly inserted by third-party apps
+ // or sync adapters), skip the phone number row.
+ final String number = updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
+ if (TextUtils.isEmpty(number)) {
+ continue;
+ } else {
+ insert.bindString(2, number);
+ }
+
+ final String lookupKey = updatedContactCursor.getString(PhoneQuery.PHONE_LOOKUP_KEY);
+ if (TextUtils.isEmpty(lookupKey)) {
+ continue;
+ } else {
+ insert.bindString(4, lookupKey);
+ }
+
+ final String displayName = updatedContactCursor.getString(PhoneQuery.PHONE_DISPLAY_NAME);
+ if (displayName == null) {
+ insert.bindString(5, mContext.getResources().getString(R.string.missing_name));
+ } else {
+ insert.bindString(5, displayName);
+ }
+ insert.bindLong(1, updatedContactCursor.getLong(PhoneQuery.PHONE_ID));
+ insert.bindLong(3, updatedContactCursor.getLong(PhoneQuery.PHONE_CONTACT_ID));
+ insert.bindLong(6, updatedContactCursor.getLong(PhoneQuery.PHONE_PHOTO_ID));
+ insert.bindLong(7, updatedContactCursor.getLong(PhoneQuery.PHONE_LAST_TIME_USED));
+ insert.bindLong(8, updatedContactCursor.getInt(PhoneQuery.PHONE_TIMES_USED));
+ insert.bindLong(9, updatedContactCursor.getInt(PhoneQuery.PHONE_STARRED));
+ insert.bindLong(10, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_SUPER_PRIMARY));
+ insert.bindLong(11, updatedContactCursor.getInt(PhoneQuery.PHONE_IN_VISIBLE_GROUP));
+ insert.bindLong(12, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_PRIMARY));
+ insert.bindLong(13, updatedContactCursor.getInt(PhoneQuery.PHONE_CARRIER_PRESENCE));
+ insert.bindLong(14, currentMillis);
+ insert.executeInsert();
+ final String contactPhoneNumber = updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
+ final ArrayList<String> numberPrefixes =
+ SmartDialPrefix.parseToNumberTokens(contactPhoneNumber);
+
+ for (String numberPrefix : numberPrefixes) {
+ numberInsert.bindLong(1, updatedContactCursor.getLong(PhoneQuery.PHONE_CONTACT_ID));
+ numberInsert.bindString(2, numberPrefix);
+ numberInsert.executeInsert();
+ numberInsert.clearBindings();
+ }
+ }
+
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ /**
+ * Inserts prefixes of contact names to the prefix table.
+ *
+ * @param db Database pointer to the smartdial database.
+ * @param nameCursor Cursor pointing to the list of distinct updated contacts.
+ */
+ @VisibleForTesting
+ void insertNamePrefixes(SQLiteDatabase db, Cursor nameCursor) {
+ final int columnIndexName = nameCursor.getColumnIndex(SmartDialDbColumns.DISPLAY_NAME_PRIMARY);
+ final int columnIndexContactId = nameCursor.getColumnIndex(SmartDialDbColumns.CONTACT_ID);
+
+ db.beginTransaction();
+ try {
+ final String sqlInsert =
+ "INSERT INTO "
+ + Tables.PREFIX_TABLE
+ + " ("
+ + PrefixColumns.CONTACT_ID
+ + ", "
+ + PrefixColumns.PREFIX
+ + ") "
+ + " VALUES (?, ?)";
+ final SQLiteStatement insert = db.compileStatement(sqlInsert);
+
+ while (nameCursor.moveToNext()) {
+ /** Computes a list of prefixes of a given contact name. */
+ final ArrayList<String> namePrefixes =
+ SmartDialPrefix.generateNamePrefixes(nameCursor.getString(columnIndexName));
+
+ for (String namePrefix : namePrefixes) {
+ insert.bindLong(1, nameCursor.getLong(columnIndexContactId));
+ insert.bindString(2, namePrefix);
+ insert.executeInsert();
+ insert.clearBindings();
+ }
+ }
+
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ /**
+ * Updates the smart dial and prefix database. This method queries the Delta API to get changed
+ * contacts since last update, and updates the records in smartdial database and prefix database
+ * accordingly. It also queries the deleted contact database to remove newly deleted contacts
+ * since last update.
+ */
+ public void updateSmartDialDatabase() {
+ final SQLiteDatabase db = getWritableDatabase();
+
+ synchronized (mLock) {
+ LogUtil.v("DialerDatabaseHelper.updateSmartDialDatabase", "starting to update database");
+ final StopWatch stopWatch = DEBUG ? StopWatch.start("Updating databases") : null;
+
+ /** Gets the last update time on the database. */
+ final SharedPreferences databaseLastUpdateSharedPref =
+ mContext.getSharedPreferences(DATABASE_LAST_CREATED_SHARED_PREF, Context.MODE_PRIVATE);
+ final String lastUpdateMillis =
+ String.valueOf(databaseLastUpdateSharedPref.getLong(LAST_UPDATED_MILLIS, 0));
+
+ LogUtil.v(
+ "DialerDatabaseHelper.updateSmartDialDatabase", "last updated at " + lastUpdateMillis);
+
+ /** Sets the time after querying the database as the current update time. */
+ final Long currentMillis = System.currentTimeMillis();
+
+ if (DEBUG) {
+ stopWatch.lap("Queried the Contacts database");
+ }
+
+ /** Prevents the app from reading the dialer database when updating. */
+ mInUpdate.getAndSet(true);
+
+ /** Removes contacts that have been deleted. */
+ removeDeletedContacts(db, getDeletedContactCursor(lastUpdateMillis));
+ removePotentiallyCorruptedContacts(db, lastUpdateMillis);
+
+ if (DEBUG) {
+ stopWatch.lap("Finished deleting deleted entries");
+ }
+
+ /**
+ * If the database did not exist before, jump through deletion as there is nothing to delete.
+ */
+ if (!lastUpdateMillis.equals("0")) {
+ /**
+ * Removes contacts that have been updated. Updated contact information will be inserted
+ * later. Note that this has to use a separate result set from updatePhoneCursor, since it
+ * is possible for a contact to be updated (e.g. phone number deleted), but have no results
+ * show up in updatedPhoneCursor (since all of its phone numbers have been deleted).
+ */
+ final Cursor updatedContactCursor =
+ mContext
+ .getContentResolver()
+ .query(
+ UpdatedContactQuery.URI,
+ UpdatedContactQuery.PROJECTION,
+ UpdatedContactQuery.SELECT_UPDATED_CLAUSE,
+ new String[] {lastUpdateMillis},
+ null);
+ if (updatedContactCursor == null) {
+ LogUtil.e(
+ "DialerDatabaseHelper.updateSmartDialDatabase",
+ "smartDial query received null for cursor");
+ return;
+ }
+ try {
+ removeUpdatedContacts(db, updatedContactCursor);
+ } finally {
+ updatedContactCursor.close();
+ }
+ if (DEBUG) {
+ stopWatch.lap("Finished deleting entries belonging to updated contacts");
+ }
+ }
+
+ /**
+ * Queries the contact database to get all phone numbers that have been updated since the last
+ * update time.
+ */
+ final Cursor updatedPhoneCursor =
+ mContext
+ .getContentResolver()
+ .query(
+ PhoneQuery.URI,
+ PhoneQuery.PROJECTION,
+ PhoneQuery.SELECTION,
+ new String[] {lastUpdateMillis},
+ null);
+ if (updatedPhoneCursor == null) {
+ LogUtil.e(
+ "DialerDatabaseHelper.updateSmartDialDatabase",
+ "smartDial query received null for cursor");
+ return;
+ }
+
+ try {
+ /** Inserts recently updated phone numbers to the smartdial database. */
+ insertUpdatedContactsAndNumberPrefix(db, updatedPhoneCursor, currentMillis);
+ if (DEBUG) {
+ stopWatch.lap("Finished building the smart dial table");
+ }
+ } finally {
+ updatedPhoneCursor.close();
+ }
+
+ /**
+ * Gets a list of distinct contacts which have been updated, and adds the name prefixes of
+ * these contacts to the prefix table.
+ */
+ final Cursor nameCursor =
+ db.rawQuery(
+ "SELECT DISTINCT "
+ + SmartDialDbColumns.DISPLAY_NAME_PRIMARY
+ + ", "
+ + SmartDialDbColumns.CONTACT_ID
+ + " FROM "
+ + Tables.SMARTDIAL_TABLE
+ + " WHERE "
+ + SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME
+ + " = "
+ + Long.toString(currentMillis),
+ new String[] {});
+ if (nameCursor != null) {
+ try {
+ if (DEBUG) {
+ stopWatch.lap("Queried the smart dial table for contact names");
+ }
+
+ /** Inserts prefixes of names into the prefix table. */
+ insertNamePrefixes(db, nameCursor);
+ if (DEBUG) {
+ stopWatch.lap("Finished building the name prefix table");
+ }
+ } finally {
+ nameCursor.close();
+ }
+ }
+
+ /** Creates index on contact_id for fast JOIN operation. */
+ db.execSQL(
+ "CREATE INDEX IF NOT EXISTS smartdial_contact_id_index ON "
+ + Tables.SMARTDIAL_TABLE
+ + " ("
+ + SmartDialDbColumns.CONTACT_ID
+ + ");");
+ /** Creates index on last_smartdial_update_time for fast SELECT operation. */
+ db.execSQL(
+ "CREATE INDEX IF NOT EXISTS smartdial_last_update_index ON "
+ + Tables.SMARTDIAL_TABLE
+ + " ("
+ + SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME
+ + ");");
+ /** Creates index on sorting fields for fast sort operation. */
+ db.execSQL(
+ "CREATE INDEX IF NOT EXISTS smartdial_sort_index ON "
+ + Tables.SMARTDIAL_TABLE
+ + " ("
+ + SmartDialDbColumns.STARRED
+ + ", "
+ + SmartDialDbColumns.IS_SUPER_PRIMARY
+ + ", "
+ + SmartDialDbColumns.LAST_TIME_USED
+ + ", "
+ + SmartDialDbColumns.TIMES_USED
+ + ", "
+ + SmartDialDbColumns.IN_VISIBLE_GROUP
+ + ", "
+ + SmartDialDbColumns.DISPLAY_NAME_PRIMARY
+ + ", "
+ + SmartDialDbColumns.CONTACT_ID
+ + ", "
+ + SmartDialDbColumns.IS_PRIMARY
+ + ");");
+ /** Creates index on prefix for fast SELECT operation. */
+ db.execSQL(
+ "CREATE INDEX IF NOT EXISTS nameprefix_index ON "
+ + Tables.PREFIX_TABLE
+ + " ("
+ + PrefixColumns.PREFIX
+ + ");");
+ /** Creates index on contact_id for fast JOIN operation. */
+ db.execSQL(
+ "CREATE INDEX IF NOT EXISTS nameprefix_contact_id_index ON "
+ + Tables.PREFIX_TABLE
+ + " ("
+ + PrefixColumns.CONTACT_ID
+ + ");");
+
+ if (DEBUG) {
+ stopWatch.lap(TAG + "Finished recreating index");
+ }
+
+ /** Updates the database index statistics. */
+ db.execSQL("ANALYZE " + Tables.SMARTDIAL_TABLE);
+ db.execSQL("ANALYZE " + Tables.PREFIX_TABLE);
+ db.execSQL("ANALYZE smartdial_contact_id_index");
+ db.execSQL("ANALYZE smartdial_last_update_index");
+ db.execSQL("ANALYZE nameprefix_index");
+ db.execSQL("ANALYZE nameprefix_contact_id_index");
+ if (DEBUG) {
+ stopWatch.stopAndLog(TAG + "Finished updating index stats", 0);
+ }
+
+ mInUpdate.getAndSet(false);
+
+ final SharedPreferences.Editor editor = databaseLastUpdateSharedPref.edit();
+ editor.putLong(LAST_UPDATED_MILLIS, currentMillis);
+ editor.apply();
+
+ // Notify content observers that smart dial database has been updated.
+ mContext.getContentResolver().notifyChange(SMART_DIAL_UPDATED_URI, null, false);
+ }
+ }
+
+ /**
+ * Returns a list of candidate contacts where the query is a prefix of the dialpad index of the
+ * contact's name or phone number.
+ *
+ * @param query The prefix of a contact's dialpad index.
+ * @return A list of top candidate contacts that will be suggested to user to match their input.
+ */
+ public ArrayList<ContactNumber> getLooseMatches(String query, SmartDialNameMatcher nameMatcher) {
+ final boolean inUpdate = mInUpdate.get();
+ if (inUpdate) {
+ return new ArrayList<>();
+ }
+
+ final SQLiteDatabase db = getReadableDatabase();
+
+ /** Uses SQL query wildcard '%' to represent prefix matching. */
+ final String looseQuery = query + "%";
+
+ final ArrayList<ContactNumber> result = new ArrayList<>();
+
+ final StopWatch stopWatch = DEBUG ? StopWatch.start(":Name Prefix query") : null;
+
+ final String currentTimeStamp = Long.toString(System.currentTimeMillis());
+
+ /** Queries the database to find contacts that have an index matching the query prefix. */
+ final Cursor cursor =
+ db.rawQuery(
+ "SELECT "
+ + SmartDialDbColumns.DATA_ID
+ + ", "
+ + SmartDialDbColumns.DISPLAY_NAME_PRIMARY
+ + ", "
+ + SmartDialDbColumns.PHOTO_ID
+ + ", "
+ + SmartDialDbColumns.NUMBER
+ + ", "
+ + SmartDialDbColumns.CONTACT_ID
+ + ", "
+ + SmartDialDbColumns.LOOKUP_KEY
+ + ", "
+ + SmartDialDbColumns.CARRIER_PRESENCE
+ + " FROM "
+ + Tables.SMARTDIAL_TABLE
+ + " WHERE "
+ + SmartDialDbColumns.CONTACT_ID
+ + " IN "
+ + " (SELECT "
+ + PrefixColumns.CONTACT_ID
+ + " FROM "
+ + Tables.PREFIX_TABLE
+ + " WHERE "
+ + Tables.PREFIX_TABLE
+ + "."
+ + PrefixColumns.PREFIX
+ + " LIKE '"
+ + looseQuery
+ + "')"
+ + " ORDER BY "
+ + SmartDialSortingOrder.SORT_ORDER,
+ new String[] {currentTimeStamp});
+ if (cursor == null) {
+ return result;
+ }
+ try {
+ if (DEBUG) {
+ stopWatch.lap("Prefix query completed");
+ }
+
+ /** Gets the column ID from the cursor. */
+ final int columnDataId = 0;
+ final int columnDisplayNamePrimary = 1;
+ final int columnPhotoId = 2;
+ final int columnNumber = 3;
+ final int columnId = 4;
+ final int columnLookupKey = 5;
+ final int columnCarrierPresence = 6;
+ if (DEBUG) {
+ stopWatch.lap("Found column IDs");
+ }
+
+ final Set<ContactMatch> duplicates = new HashSet<>();
+ int counter = 0;
+ if (DEBUG) {
+ stopWatch.lap("Moved cursor to start");
+ }
+ /** Iterates the cursor to find top contact suggestions without duplication. */
+ while ((cursor.moveToNext()) && (counter < MAX_ENTRIES)) {
+ final long dataID = cursor.getLong(columnDataId);
+ final String displayName = cursor.getString(columnDisplayNamePrimary);
+ final String phoneNumber = cursor.getString(columnNumber);
+ final long id = cursor.getLong(columnId);
+ final long photoId = cursor.getLong(columnPhotoId);
+ final String lookupKey = cursor.getString(columnLookupKey);
+ final int carrierPresence = cursor.getInt(columnCarrierPresence);
+
+ /**
+ * If a contact already exists and another phone number of the contact is being processed,
+ * skip the second instance.
+ */
+ final ContactMatch contactMatch = new ContactMatch(lookupKey, id);
+ if (duplicates.contains(contactMatch)) {
+ continue;
+ }
+
+ /**
+ * If the contact has either the name or number that matches the query, add to the result.
+ */
+ final boolean nameMatches = nameMatcher.matches(displayName);
+ final boolean numberMatches = (nameMatcher.matchesNumber(phoneNumber, query) != null);
+ if (nameMatches || numberMatches) {
+ /** If a contact has not been added, add it to the result and the hash set. */
+ duplicates.add(contactMatch);
+ result.add(
+ new ContactNumber(
+ id, dataID, displayName, phoneNumber, lookupKey, photoId, carrierPresence));
+ counter++;
+ if (DEBUG) {
+ stopWatch.lap("Added one result: Name: " + displayName);
+ }
+ }
+ }
+
+ if (DEBUG) {
+ stopWatch.stopAndLog(TAG + "Finished loading cursor", 0);
+ }
+ } finally {
+ cursor.close();
+ }
+ return result;
+ }
+
+ public interface Tables {
+
+ /** Saves a list of numbers to be blocked. */
+ String FILTERED_NUMBER_TABLE = "filtered_numbers_table";
+ /** Saves the necessary smart dial information of all contacts. */
+ String SMARTDIAL_TABLE = "smartdial_table";
+ /** Saves all possible prefixes to refer to a contacts. */
+ String PREFIX_TABLE = "prefix_table";
+ /** Saves all archived voicemail information. */
+ String VOICEMAIL_ARCHIVE_TABLE = "voicemail_archive_table";
+ /** Database properties for internal use */
+ String PROPERTIES = "properties";
+ }
+
+ public interface SmartDialDbColumns {
+
+ String _ID = "id";
+ String DATA_ID = "data_id";
+ String NUMBER = "phone_number";
+ String CONTACT_ID = "contact_id";
+ String LOOKUP_KEY = "lookup_key";
+ String DISPLAY_NAME_PRIMARY = "display_name";
+ String PHOTO_ID = "photo_id";
+ String LAST_TIME_USED = "last_time_used";
+ String TIMES_USED = "times_used";
+ String STARRED = "starred";
+ String IS_SUPER_PRIMARY = "is_super_primary";
+ String IN_VISIBLE_GROUP = "in_visible_group";
+ String IS_PRIMARY = "is_primary";
+ String CARRIER_PRESENCE = "carrier_presence";
+ String LAST_SMARTDIAL_UPDATE_TIME = "last_smartdial_update_time";
+ }
+
+ public interface PrefixColumns extends BaseColumns {
+
+ String PREFIX = "prefix";
+ String CONTACT_ID = "contact_id";
+ }
+
+ public interface PropertiesColumns {
+
+ String PROPERTY_KEY = "property_key";
+ String PROPERTY_VALUE = "property_value";
+ }
+
+ /** Query options for querying the contact database. */
+ public interface PhoneQuery {
+
+ Uri URI =
+ Phone.CONTENT_URI
+ .buildUpon()
+ .appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
+ .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true")
+ .build();
+
+ String[] PROJECTION =
+ new String[] {
+ Phone._ID, // 0
+ Phone.TYPE, // 1
+ Phone.LABEL, // 2
+ Phone.NUMBER, // 3
+ Phone.CONTACT_ID, // 4
+ Phone.LOOKUP_KEY, // 5
+ Phone.DISPLAY_NAME_PRIMARY, // 6
+ Phone.PHOTO_ID, // 7
+ Data.LAST_TIME_USED, // 8
+ Data.TIMES_USED, // 9
+ Contacts.STARRED, // 10
+ Data.IS_SUPER_PRIMARY, // 11
+ Contacts.IN_VISIBLE_GROUP, // 12
+ Data.IS_PRIMARY, // 13
+ Data.CARRIER_PRESENCE, // 14
+ };
+
+ int PHONE_ID = 0;
+ int PHONE_TYPE = 1;
+ int PHONE_LABEL = 2;
+ int PHONE_NUMBER = 3;
+ int PHONE_CONTACT_ID = 4;
+ int PHONE_LOOKUP_KEY = 5;
+ int PHONE_DISPLAY_NAME = 6;
+ int PHONE_PHOTO_ID = 7;
+ int PHONE_LAST_TIME_USED = 8;
+ int PHONE_TIMES_USED = 9;
+ int PHONE_STARRED = 10;
+ int PHONE_IS_SUPER_PRIMARY = 11;
+ int PHONE_IN_VISIBLE_GROUP = 12;
+ int PHONE_IS_PRIMARY = 13;
+ int PHONE_CARRIER_PRESENCE = 14;
+
+ /** Selects only rows that have been updated after a certain time stamp. */
+ String SELECT_UPDATED_CLAUSE = Phone.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?";
+
+ /**
+ * Ignores contacts that have an unreasonably long lookup key. These are likely to be the result
+ * of multiple (> 50) merged raw contacts, and are likely to cause OutOfMemoryExceptions within
+ * SQLite, or cause memory allocation problems later on when iterating through the cursor set
+ * (see b/13133579)
+ */
+ String SELECT_IGNORE_LOOKUP_KEY_TOO_LONG_CLAUSE = "length(" + Phone.LOOKUP_KEY + ") < 1000";
+
+ String SELECTION = SELECT_UPDATED_CLAUSE + " AND " + SELECT_IGNORE_LOOKUP_KEY_TOO_LONG_CLAUSE;
+ }
+
+ /**
+ * Query for all contacts that have been updated since the last time the smart dial database was
+ * updated.
+ */
+ public interface UpdatedContactQuery {
+
+ Uri URI = ContactsContract.Contacts.CONTENT_URI;
+
+ String[] PROJECTION =
+ new String[] {
+ ContactsContract.Contacts._ID // 0
+ };
+
+ int UPDATED_CONTACT_ID = 0;
+
+ String SELECT_UPDATED_CLAUSE =
+ ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?";
+ }
+
+ /** Query options for querying the deleted contact database. */
+ public interface DeleteContactQuery {
+
+ Uri URI = ContactsContract.DeletedContacts.CONTENT_URI;
+
+ String[] PROJECTION =
+ new String[] {
+ ContactsContract.DeletedContacts.CONTACT_ID, // 0
+ ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP, // 1
+ };
+
+ int DELETED_CONTACT_ID = 0;
+ int DELETED_TIMESTAMP = 1;
+
+ /** Selects only rows that have been deleted after a certain time stamp. */
+ String SELECT_UPDATED_CLAUSE =
+ ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP + " > ?";
+ }
+
+ /**
+ * Gets the sorting order for the smartdial table. This computes a SQL "ORDER BY" argument by
+ * composing contact status and recent contact details together.
+ */
+ private interface SmartDialSortingOrder {
+
+ /** Current contacts - those contacted within the last 3 days (in milliseconds) */
+ long LAST_TIME_USED_CURRENT_MS = 3L * 24 * 60 * 60 * 1000;
+ /** Recent contacts - those contacted within the last 30 days (in milliseconds) */
+ long LAST_TIME_USED_RECENT_MS = 30L * 24 * 60 * 60 * 1000;
+
+ /** Time since last contact. */
+ String TIME_SINCE_LAST_USED_MS =
+ "( ?1 - " + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.LAST_TIME_USED + ")";
+
+ /**
+ * Contacts that have been used in the past 3 days rank higher than contacts that have been used
+ * in the past 30 days, which rank higher than contacts that have not been used in recent 30
+ * days.
+ */
+ String SORT_BY_DATA_USAGE =
+ "(CASE WHEN "
+ + TIME_SINCE_LAST_USED_MS
+ + " < "
+ + LAST_TIME_USED_CURRENT_MS
+ + " THEN 0 "
+ + " WHEN "
+ + TIME_SINCE_LAST_USED_MS
+ + " < "
+ + LAST_TIME_USED_RECENT_MS
+ + " THEN 1 "
+ + " ELSE 2 END)";
+
+ /**
+ * This sort order is similar to that used by the ContactsProvider when returning a list of
+ * frequently called contacts.
+ */
+ String SORT_ORDER =
+ Tables.SMARTDIAL_TABLE
+ + "."
+ + SmartDialDbColumns.STARRED
+ + " DESC, "
+ + Tables.SMARTDIAL_TABLE
+ + "."
+ + SmartDialDbColumns.IS_SUPER_PRIMARY
+ + " DESC, "
+ + SORT_BY_DATA_USAGE
+ + ", "
+ + Tables.SMARTDIAL_TABLE
+ + "."
+ + SmartDialDbColumns.TIMES_USED
+ + " DESC, "
+ + Tables.SMARTDIAL_TABLE
+ + "."
+ + SmartDialDbColumns.IN_VISIBLE_GROUP
+ + " DESC, "
+ + Tables.SMARTDIAL_TABLE
+ + "."
+ + SmartDialDbColumns.DISPLAY_NAME_PRIMARY
+ + ", "
+ + Tables.SMARTDIAL_TABLE
+ + "."
+ + SmartDialDbColumns.CONTACT_ID
+ + ", "
+ + Tables.SMARTDIAL_TABLE
+ + "."
+ + SmartDialDbColumns.IS_PRIMARY
+ + " DESC";
+ }
+
+ /**
+ * Simple data format for a contact, containing only information needed for showing up in smart
+ * dial interface.
+ */
+ public static class ContactNumber {
+
+ public final long id;
+ public final long dataId;
+ public final String displayName;
+ public final String phoneNumber;
+ public final String lookupKey;
+ public final long photoId;
+ public final int carrierPresence;
+
+ public ContactNumber(
+ long id,
+ long dataID,
+ String displayName,
+ String phoneNumber,
+ String lookupKey,
+ long photoId,
+ int carrierPresence) {
+ this.dataId = dataID;
+ this.id = id;
+ this.displayName = displayName;
+ this.phoneNumber = phoneNumber;
+ this.lookupKey = lookupKey;
+ this.photoId = photoId;
+ this.carrierPresence = carrierPresence;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ id, dataId, displayName, phoneNumber, lookupKey, photoId, carrierPresence);
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object instanceof ContactNumber) {
+ final ContactNumber that = (ContactNumber) object;
+ return Objects.equals(this.id, that.id)
+ && Objects.equals(this.dataId, that.dataId)
+ && Objects.equals(this.displayName, that.displayName)
+ && Objects.equals(this.phoneNumber, that.phoneNumber)
+ && Objects.equals(this.lookupKey, that.lookupKey)
+ && Objects.equals(this.photoId, that.photoId)
+ && Objects.equals(this.carrierPresence, that.carrierPresence);
+ }
+ return false;
+ }
+ }
+
+ /** Data format for finding duplicated contacts. */
+ private static class ContactMatch {
+
+ private final String lookupKey;
+ private final long id;
+
+ public ContactMatch(String lookupKey, long id) {
+ this.lookupKey = lookupKey;
+ this.id = id;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(lookupKey, id);
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object instanceof ContactMatch) {
+ final ContactMatch that = (ContactMatch) object;
+ return Objects.equals(this.lookupKey, that.lookupKey) && Objects.equals(this.id, that.id);
+ }
+ return false;
+ }
+ }
+
+ private class SmartDialUpdateAsyncTask extends AsyncTask<Object, Object, Object> {
+
+ @Override
+ protected Object doInBackground(Object... objects) {
+ updateSmartDialDatabase();
+ return null;
+ }
+ }
+}
diff --git a/java/com/android/dialer/database/FilteredNumberContract.java b/java/com/android/dialer/database/FilteredNumberContract.java
new file mode 100644
index 000000000..3efbaafb1
--- /dev/null
+++ b/java/com/android/dialer/database/FilteredNumberContract.java
@@ -0,0 +1,137 @@
+/*
+ * 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.dialer.database;
+
+import android.net.Uri;
+import android.provider.BaseColumns;
+import com.android.dialer.constants.Constants;
+
+/**
+ * The contract between the filtered number provider and applications. Contains definitions for the
+ * supported URIs and columns. Currently only accessible within Dialer.
+ */
+public final class FilteredNumberContract {
+
+ public static final String AUTHORITY = Constants.get().getFilteredNumberProviderAuthority();
+
+ public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+ /** The type of filtering to be applied, e.g. block the number or whitelist the number. */
+ public interface FilteredNumberTypes {
+
+ int UNDEFINED = 0;
+ /** Dialer will disconnect the call without sending the caller to voicemail. */
+ int BLOCKED_NUMBER = 1;
+ }
+
+ /** The original source of the filtered number, e.g. the user manually added it. */
+ public interface FilteredNumberSources {
+
+ int UNDEFINED = 0;
+ /** The user manually added this number through Dialer (e.g. from the call log or InCallUI). */
+ int USER = 1;
+ }
+
+ public interface FilteredNumberColumns {
+
+ // TYPE: INTEGER
+ String _ID = "_id";
+ /**
+ * Represents the number to be filtered, normalized to compare phone numbers for equality.
+ *
+ * <p>TYPE: TEXT
+ */
+ String NORMALIZED_NUMBER = "normalized_number";
+ /**
+ * Represents the number to be filtered, for formatting and used with country iso for contact
+ * lookups.
+ *
+ * <p>TYPE: TEXT
+ */
+ String NUMBER = "number";
+ /**
+ * The country code representing the country detected when the phone number was added to the
+ * database. Most numbers don't have the country code, so a best guess is provided by the
+ * country detector system. The country iso is also needed in order to format phone numbers
+ * correctly.
+ *
+ * <p>TYPE: TEXT
+ */
+ String COUNTRY_ISO = "country_iso";
+ /**
+ * The number of times the number has been filtered by Dialer. When this number is incremented,
+ * LAST_TIME_FILTERED should also be updated to the current time.
+ *
+ * <p>TYPE: INTEGER
+ */
+ String TIMES_FILTERED = "times_filtered";
+ /**
+ * Set to the current time when the phone number is filtered. When this is updated,
+ * TIMES_FILTERED should also be incremented.
+ *
+ * <p>TYPE: LONG
+ */
+ String LAST_TIME_FILTERED = "last_time_filtered";
+ // TYPE: LONG
+ String CREATION_TIME = "creation_time";
+ /**
+ * Indicates the type of filtering to be applied.
+ *
+ * <p>TYPE: INTEGER See {@link FilteredNumberTypes}
+ */
+ String TYPE = "type";
+ /**
+ * Integer representing the original source of the filtered number.
+ *
+ * <p>TYPE: INTEGER See {@link FilteredNumberSources}
+ */
+ String SOURCE = "source";
+ }
+
+ /**
+ * Constants for the table of filtered numbers.
+ *
+ * <h3>Operations</h3>
+ *
+ * <dl>
+ * <dt><b>Insert</b>
+ * <dd>Required fields: NUMBER, NORMALIZED_NUMBER, TYPE, SOURCE. A default value will be used for
+ * the other fields if left null.
+ * <dt><b>Update</b>
+ * <dt><b>Delete</b>
+ * <dt><b>Query</b>
+ * <dd>{@link #CONTENT_URI} can be used for any query, append an ID to retrieve a specific
+ * filtered number entry.
+ * </dl>
+ */
+ public static class FilteredNumber implements BaseColumns {
+
+ public static final String FILTERED_NUMBERS_TABLE = "filtered_numbers_table";
+
+ /**
+ * The MIME type of a {@link android.content.ContentProvider#getType(Uri)} single filtered
+ * number.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/filtered_numbers_table";
+
+ public static final Uri CONTENT_URI =
+ Uri.withAppendedPath(AUTHORITY_URI, FILTERED_NUMBERS_TABLE);
+
+ /** This utility class cannot be instantiated. */
+ private FilteredNumber() {}
+ }
+}
diff --git a/java/com/android/dialer/database/VoicemailStatusQuery.java b/java/com/android/dialer/database/VoicemailStatusQuery.java
new file mode 100644
index 000000000..d9e1b721b
--- /dev/null
+++ b/java/com/android/dialer/database/VoicemailStatusQuery.java
@@ -0,0 +1,91 @@
+/*
+ * 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.dialer.database;
+
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.VoicemailContract.Status;
+import android.support.annotation.RequiresApi;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/** The query for the call voicemail status table. */
+public class VoicemailStatusQuery {
+
+ // TODO: Column indices should be removed in favor of Cursor#getColumnIndex
+ public static final int SOURCE_PACKAGE_INDEX = 0;
+ public static final int SETTINGS_URI_INDEX = 1;
+ public static final int VOICEMAIL_ACCESS_URI_INDEX = 2;
+ public static final int CONFIGURATION_STATE_INDEX = 3;
+ public static final int DATA_CHANNEL_STATE_INDEX = 4;
+ public static final int NOTIFICATION_CHANNEL_STATE_INDEX = 5;
+
+ @RequiresApi(VERSION_CODES.N)
+ public static final int QUOTA_OCCUPIED_INDEX = 6;
+
+ @RequiresApi(VERSION_CODES.N)
+ public static final int QUOTA_TOTAL_INDEX = 7;
+
+ @RequiresApi(VERSION_CODES.N_MR1)
+ // The PHONE_ACCOUNT columns were added in M, but aren't queryable until N MR1
+ public static final int PHONE_ACCOUNT_COMPONENT_NAME = 8;
+
+ @RequiresApi(VERSION_CODES.N_MR1)
+ public static final int PHONE_ACCOUNT_ID = 9;
+
+ @RequiresApi(VERSION_CODES.N_MR1)
+ public static final int SOURCE_TYPE_INDEX = 10;
+
+ private static final String[] PROJECTION_M =
+ new String[] {
+ Status.SOURCE_PACKAGE, // 0
+ Status.SETTINGS_URI, // 1
+ Status.VOICEMAIL_ACCESS_URI, // 2
+ Status.CONFIGURATION_STATE, // 3
+ Status.DATA_CHANNEL_STATE, // 4
+ Status.NOTIFICATION_CHANNEL_STATE // 5
+ };
+
+ @RequiresApi(VERSION_CODES.N)
+ private static final String[] PROJECTION_N;
+
+ @RequiresApi(VERSION_CODES.N_MR1)
+ private static final String[] PROJECTION_NMR1;
+
+ static {
+ List<String> projectionList = new ArrayList<>(Arrays.asList(PROJECTION_M));
+ projectionList.add(Status.QUOTA_OCCUPIED); // 6
+ projectionList.add(Status.QUOTA_TOTAL); // 7
+ PROJECTION_N = projectionList.toArray(new String[projectionList.size()]);
+
+ projectionList.add(Status.PHONE_ACCOUNT_COMPONENT_NAME); // 8
+ projectionList.add(Status.PHONE_ACCOUNT_ID); // 9
+ projectionList.add(Status.SOURCE_TYPE); // 10
+ PROJECTION_NMR1 = projectionList.toArray(new String[projectionList.size()]);
+ }
+
+ public static String[] getProjection() {
+ if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
+ return PROJECTION_NMR1;
+ }
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ return PROJECTION_N;
+ }
+ return PROJECTION_M;
+ }
+}
diff --git a/java/com/android/dialer/debug/AndroidManifest.xml b/java/com/android/dialer/debug/AndroidManifest.xml
new file mode 100644
index 000000000..053d7e789
--- /dev/null
+++ b/java/com/android/dialer/debug/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.dialer.debug">
+</manifest>
diff --git a/java/com/android/dialer/debug/bindings/impl/DebugBindings.java b/java/com/android/dialer/debug/bindings/impl/DebugBindings.java
new file mode 100644
index 000000000..a8b44605c
--- /dev/null
+++ b/java/com/android/dialer/debug/bindings/impl/DebugBindings.java
@@ -0,0 +1,32 @@
+/*
+ * 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.dialer.debug.bindings;
+
+import android.content.Context;
+import com.android.dialer.debug.impl.DebugConnectionService;
+
+/** Hooks into the debug module. */
+public class DebugBindings {
+
+ public static void registerConnectionService(Context context) {
+ DebugConnectionService.register(context);
+ }
+
+ public static void addNewIncomingCall(Context context, String phoneNumber) {
+ DebugConnectionService.addNewIncomingCall(context, phoneNumber);
+ }
+}
diff --git a/java/com/android/dialer/debug/impl/AndroidManifest.xml b/java/com/android/dialer/debug/impl/AndroidManifest.xml
new file mode 100644
index 000000000..b8756614b
--- /dev/null
+++ b/java/com/android/dialer/debug/impl/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.dialer.debug.impl">
+
+ <application>
+
+ <service
+ android:exported="true"
+ android:name=".DebugConnectionService"
+ android:permission="android.permission.BIND_CONNECTION_SERVICE">
+ <intent-filter>
+ <action android:name="android.telecomm.ConnectionService"/>
+ </intent-filter>
+ </service>
+
+ </application>
+
+</manifest>
diff --git a/java/com/android/dialer/debug/impl/DebugConnection.java b/java/com/android/dialer/debug/impl/DebugConnection.java
new file mode 100644
index 000000000..2ef83aa76
--- /dev/null
+++ b/java/com/android/dialer/debug/impl/DebugConnection.java
@@ -0,0 +1,55 @@
+/*
+ * 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.dialer.debug.impl;
+
+import android.telecom.Connection;
+import android.telecom.DisconnectCause;
+import com.android.dialer.common.LogUtil;
+
+class DebugConnection extends Connection {
+
+ @Override
+ public void onAnswer() {
+ LogUtil.i("DebugConnection.onAnswer", null);
+ setActive();
+ }
+
+ @Override
+ public void onReject() {
+ LogUtil.i("DebugConnection.onReject", null);
+ setDisconnected(new DisconnectCause(DisconnectCause.REJECTED));
+ }
+
+ @Override
+ public void onHold() {
+ LogUtil.i("DebugConnection.onHold", null);
+ setOnHold();
+ }
+
+ @Override
+ public void onUnhold() {
+ LogUtil.i("DebugConnection.onUnhold", null);
+ setActive();
+ }
+
+ @Override
+ public void onDisconnect() {
+ LogUtil.i("DebugConnection.onDisconnect", null);
+ setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
+ destroy();
+ }
+}
diff --git a/java/com/android/dialer/debug/impl/DebugConnectionService.java b/java/com/android/dialer/debug/impl/DebugConnectionService.java
new file mode 100644
index 000000000..69aab1e13
--- /dev/null
+++ b/java/com/android/dialer/debug/impl/DebugConnectionService.java
@@ -0,0 +1,103 @@
+/*
+ * 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.dialer.debug.impl;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.telecom.Connection;
+import android.telecom.ConnectionRequest;
+import android.telecom.ConnectionService;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+import com.android.dialer.common.LogUtil;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Simple connection provider to create an incoming call. This is useful for emulators. */
+public class DebugConnectionService extends ConnectionService {
+
+ private static final String PHONE_ACCOUNT_ID = "DEBUG_DIALER";
+
+ public static void register(Context context) {
+ LogUtil.i(
+ "DebugConnectionService.register",
+ context.getSystemService(Context.TELECOM_SERVICE).toString());
+ context.getSystemService(TelecomManager.class).registerPhoneAccount(buildPhoneAccount(context));
+ }
+
+ public static void addNewIncomingCall(Context context, String phoneNumber) {
+ LogUtil.i("DebugConnectionService.addNewIncomingCall", null);
+ Bundle bundle = new Bundle();
+ bundle.putString(TelephonyManager.EXTRA_INCOMING_NUMBER, phoneNumber);
+ try {
+ context
+ .getSystemService(TelecomManager.class)
+ .addNewIncomingCall(getConnectionServiceHandle(context), bundle);
+ } catch (SecurityException e) {
+ LogUtil.i(
+ "DebugConnectionService.addNewIncomingCall",
+ "unable to add call. Make sure to enable the service in Phone app -> Settings -> Calls ->"
+ + " Calling accounts.");
+ }
+ }
+
+ private static PhoneAccount buildPhoneAccount(Context context) {
+ PhoneAccount.Builder builder =
+ new PhoneAccount.Builder(
+ getConnectionServiceHandle(context), "DebugDialerConnectionService");
+ List<String> uriSchemes = new ArrayList<>();
+ uriSchemes.add(PhoneAccount.SCHEME_TEL);
+
+ return builder
+ .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+ .setShortDescription("Debug Dialer Connection Serivce")
+ .setSupportedUriSchemes(uriSchemes)
+ .build();
+ }
+
+ private static PhoneAccountHandle getConnectionServiceHandle(Context context) {
+ ComponentName componentName = new ComponentName(context, DebugConnectionService.class);
+ return new PhoneAccountHandle(componentName, PHONE_ACCOUNT_ID);
+ }
+
+ private static Uri getPhoneNumber(ConnectionRequest request) {
+ String phoneNumber = request.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
+ return Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null);
+ }
+
+ @Override
+ public Connection onCreateOutgoingConnection(
+ PhoneAccountHandle phoneAccount, ConnectionRequest request) {
+ return null;
+ }
+
+ @Override
+ public Connection onCreateIncomingConnection(
+ PhoneAccountHandle phoneAccount, ConnectionRequest request) {
+ LogUtil.i("DebugConnectionService.onCreateIncomingConnection", null);
+ DebugConnection connection = new DebugConnection();
+ connection.setRinging();
+ connection.setAddress(getPhoneNumber(request), TelecomManager.PRESENTATION_ALLOWED);
+ connection.setConnectionCapabilities(
+ Connection.CAPABILITY_MUTE | Connection.CAPABILITY_SUPPORT_HOLD);
+ return connection;
+ }
+}
diff --git a/java/com/android/dialer/dialpadview/AndroidManifest.xml b/java/com/android/dialer/dialpadview/AndroidManifest.xml
new file mode 100644
index 000000000..011a004b0
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.dialer.dialpadview">
+</manifest>
diff --git a/java/com/android/dialer/dialpadview/DialpadKeyButton.java b/java/com/android/dialer/dialpadview/DialpadKeyButton.java
new file mode 100644
index 000000000..24ca9cc86
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/DialpadKeyButton.java
@@ -0,0 +1,231 @@
+/*
+ * 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.dialer.dialpadview;
+
+import android.content.Context;
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+
+/**
+ * Custom class for dialpad buttons.
+ *
+ * <p>When touch exploration mode is enabled for accessibility, this class implements the
+ * lift-to-type interaction model:
+ *
+ * <ul>
+ * <li>Hovering over the button will cause it to gain accessibility focus
+ * <li>Removing the hover pointer while inside the bounds of the button will perform a click action
+ * <li>If long-click is supported, hovering over the button for a longer period of time will switch
+ * to the long-click action
+ * <li>Moving the hover pointer outside of the bounds of the button will restore to the normal click
+ * action
+ * <ul>
+ */
+public class DialpadKeyButton extends FrameLayout {
+
+ /** Timeout before switching to long-click accessibility mode. */
+ private static final int LONG_HOVER_TIMEOUT = ViewConfiguration.getLongPressTimeout() * 2;
+
+ /** Accessibility manager instance used to check touch exploration state. */
+ private AccessibilityManager mAccessibilityManager;
+
+ /** Bounds used to filter HOVER_EXIT events. */
+ private RectF mHoverBounds = new RectF();
+
+ /** Whether this view is currently in the long-hover state. */
+ private boolean mLongHovered;
+
+ /** Alternate content description for long-hover state. */
+ private CharSequence mLongHoverContentDesc;
+
+ /** Backup of standard content description. Used for accessibility. */
+ private CharSequence mBackupContentDesc;
+
+ /** Backup of clickable property. Used for accessibility. */
+ private boolean mWasClickable;
+
+ /** Backup of long-clickable property. Used for accessibility. */
+ private boolean mWasLongClickable;
+
+ /** Runnable used to trigger long-click mode for accessibility. */
+ private Runnable mLongHoverRunnable;
+
+ private OnPressedListener mOnPressedListener;
+
+ public DialpadKeyButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initForAccessibility(context);
+ }
+
+ public DialpadKeyButton(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initForAccessibility(context);
+ }
+
+ public void setOnPressedListener(OnPressedListener onPressedListener) {
+ mOnPressedListener = onPressedListener;
+ }
+
+ private void initForAccessibility(Context context) {
+ mAccessibilityManager =
+ (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ }
+
+ public void setLongHoverContentDescription(CharSequence contentDescription) {
+ mLongHoverContentDesc = contentDescription;
+
+ if (mLongHovered) {
+ super.setContentDescription(mLongHoverContentDesc);
+ }
+ }
+
+ @Override
+ public void setContentDescription(CharSequence contentDescription) {
+ if (mLongHovered) {
+ mBackupContentDesc = contentDescription;
+ } else {
+ super.setContentDescription(contentDescription);
+ }
+ }
+
+ @Override
+ public void setPressed(boolean pressed) {
+ super.setPressed(pressed);
+ if (mOnPressedListener != null) {
+ mOnPressedListener.onPressed(this, pressed);
+ }
+ }
+
+ @Override
+ public void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ mHoverBounds.left = getPaddingLeft();
+ mHoverBounds.right = w - getPaddingRight();
+ mHoverBounds.top = getPaddingTop();
+ mHoverBounds.bottom = h - getPaddingBottom();
+ }
+
+ @Override
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ if (action == AccessibilityNodeInfo.ACTION_CLICK) {
+ simulateClickForAccessibility();
+ return true;
+ }
+
+ return super.performAccessibilityAction(action, arguments);
+ }
+
+ @Override
+ public boolean onHoverEvent(MotionEvent event) {
+ // When touch exploration is turned on, lifting a finger while inside
+ // the button's hover target bounds should perform a click action.
+ if (mAccessibilityManager.isEnabled() && mAccessibilityManager.isTouchExplorationEnabled()) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ // Lift-to-type temporarily disables double-tap activation.
+ mWasClickable = isClickable();
+ mWasLongClickable = isLongClickable();
+ if (mWasLongClickable && mLongHoverContentDesc != null) {
+ if (mLongHoverRunnable == null) {
+ mLongHoverRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ setLongHovered(true);
+ announceForAccessibility(mLongHoverContentDesc);
+ }
+ };
+ }
+ postDelayed(mLongHoverRunnable, LONG_HOVER_TIMEOUT);
+ }
+
+ setClickable(false);
+ setLongClickable(false);
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ if (mHoverBounds.contains(event.getX(), event.getY())) {
+ if (mLongHovered) {
+ performLongClick();
+ } else {
+ simulateClickForAccessibility();
+ }
+ }
+
+ cancelLongHover();
+ setClickable(mWasClickable);
+ setLongClickable(mWasLongClickable);
+ break;
+ }
+ }
+
+ return super.onHoverEvent(event);
+ }
+
+ /**
+ * When accessibility is on, simulate press and release to preserve the semantic meaning of
+ * performClick(). Required for Braille support.
+ */
+ private void simulateClickForAccessibility() {
+ // Checking the press state prevents double activation.
+ if (isPressed()) {
+ return;
+ }
+
+ setPressed(true);
+
+ // Stay consistent with performClick() by sending the event after
+ // setting the pressed state but before performing the action.
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+
+ setPressed(false);
+ }
+
+ private void setLongHovered(boolean enabled) {
+ if (mLongHovered != enabled) {
+ mLongHovered = enabled;
+
+ // Switch between normal and alternate description, if available.
+ if (enabled) {
+ mBackupContentDesc = getContentDescription();
+ super.setContentDescription(mLongHoverContentDesc);
+ } else {
+ super.setContentDescription(mBackupContentDesc);
+ }
+ }
+ }
+
+ private void cancelLongHover() {
+ if (mLongHoverRunnable != null) {
+ removeCallbacks(mLongHoverRunnable);
+ }
+ setLongHovered(false);
+ }
+
+ public interface OnPressedListener {
+
+ void onPressed(View view, boolean pressed);
+ }
+}
diff --git a/java/com/android/dialer/dialpadview/DialpadTextView.java b/java/com/android/dialer/dialpadview/DialpadTextView.java
new file mode 100644
index 000000000..5b1b7bb5d
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/DialpadTextView.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 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.dialer.dialpadview;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+/**
+ * This is a custom text view intended only for rendering the numerals (and star and pound) on the
+ * dialpad. TextView has built in top/bottom padding to help account for ascenders/descenders.
+ *
+ * <p>Since vertical space is at a premium on the dialpad, particularly if the font size is scaled
+ * to a larger default, for the dialpad we use this class to more precisely render characters
+ * according to the precise amount of space they need.
+ */
+public class DialpadTextView extends TextView {
+
+ private Rect mTextBounds = new Rect();
+ private String mTextStr;
+
+ public DialpadTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ /** Draw the text to fit within the height/width which have been specified during measurement. */
+ @Override
+ public void draw(Canvas canvas) {
+ Paint paint = getPaint();
+
+ // Without this, the draw does not respect the style's specified text color.
+ paint.setColor(getCurrentTextColor());
+
+ // The text bounds values are relative and can be negative,, so rather than specifying a
+ // standard origin such as 0, 0, we need to use negative of the left/top bounds.
+ // For example, the bounds may be: Left: 11, Right: 37, Top: -77, Bottom: 0
+ canvas.drawText(mTextStr, -mTextBounds.left, -mTextBounds.top, paint);
+ }
+
+ /**
+ * Calculate the pixel-accurate bounds of the text when rendered, and use that to specify the
+ * height and width.
+ */
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mTextStr = getText().toString();
+ getPaint().getTextBounds(mTextStr, 0, mTextStr.length(), mTextBounds);
+
+ int width = resolveSize(mTextBounds.width(), widthMeasureSpec);
+ int height = resolveSize(mTextBounds.height(), heightMeasureSpec);
+ setMeasuredDimension(width, height);
+ }
+}
diff --git a/java/com/android/dialer/dialpadview/DialpadView.java b/java/com/android/dialer/dialpadview/DialpadView.java
new file mode 100644
index 000000000..4a9b500b7
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/DialpadView.java
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2014 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.dialer.dialpadview;
+
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
+import android.os.Build;
+import android.text.Spannable;
+import android.text.TextUtils;
+import android.text.style.TtsSpan;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.android.dialer.animation.AnimUtils;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.Locale;
+
+/** View that displays a twelve-key phone dialpad. */
+public class DialpadView extends LinearLayout {
+
+ private static final String TAG = DialpadView.class.getSimpleName();
+
+ private static final double DELAY_MULTIPLIER = 0.66;
+ private static final double DURATION_MULTIPLIER = 0.8;
+ // For animation.
+ private static final int KEY_FRAME_DURATION = 33;
+ /** {@code True} if the dialpad is in landscape orientation. */
+ private final boolean mIsLandscape;
+ /** {@code True} if the dialpad is showing in a right-to-left locale. */
+ private final boolean mIsRtl;
+
+ private final int[] mButtonIds =
+ new int[] {
+ R.id.zero,
+ R.id.one,
+ R.id.two,
+ R.id.three,
+ R.id.four,
+ R.id.five,
+ R.id.six,
+ R.id.seven,
+ R.id.eight,
+ R.id.nine,
+ R.id.star,
+ R.id.pound
+ };
+ private EditText mDigits;
+ private ImageButton mDelete;
+ private View mOverflowMenuButton;
+ private ColorStateList mRippleColor;
+ private ViewGroup mRateContainer;
+ private TextView mIldCountry;
+ private TextView mIldRate;
+ private boolean mCanDigitsBeEdited;
+ private int mTranslateDistance;
+
+ public DialpadView(Context context) {
+ this(context, null);
+ }
+
+ public DialpadView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DialpadView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Dialpad);
+ mRippleColor = a.getColorStateList(R.styleable.Dialpad_dialpad_key_button_touch_tint);
+ a.recycle();
+
+ mTranslateDistance =
+ getResources().getDimensionPixelSize(R.dimen.dialpad_key_button_translate_y);
+
+ mIsLandscape =
+ getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
+ mIsRtl =
+ TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ setupKeypad();
+ mDigits = (EditText) findViewById(R.id.digits);
+ mDelete = (ImageButton) findViewById(R.id.deleteButton);
+ mOverflowMenuButton = findViewById(R.id.dialpad_overflow);
+ mRateContainer = (ViewGroup) findViewById(R.id.rate_container);
+ mIldCountry = (TextView) mRateContainer.findViewById(R.id.ild_country);
+ mIldRate = (TextView) mRateContainer.findViewById(R.id.ild_rate);
+
+ AccessibilityManager accessibilityManager =
+ (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+ if (accessibilityManager.isEnabled()) {
+ // The text view must be selected to send accessibility events.
+ mDigits.setSelected(true);
+ }
+ }
+
+ private void setupKeypad() {
+ final int[] letterIds =
+ new int[] {
+ R.string.dialpad_0_letters,
+ R.string.dialpad_1_letters,
+ R.string.dialpad_2_letters,
+ R.string.dialpad_3_letters,
+ R.string.dialpad_4_letters,
+ R.string.dialpad_5_letters,
+ R.string.dialpad_6_letters,
+ R.string.dialpad_7_letters,
+ R.string.dialpad_8_letters,
+ R.string.dialpad_9_letters,
+ R.string.dialpad_star_letters,
+ R.string.dialpad_pound_letters
+ };
+
+ final Resources resources = getContext().getResources();
+
+ DialpadKeyButton dialpadKey;
+ TextView numberView;
+ TextView lettersView;
+
+ final Locale currentLocale = resources.getConfiguration().locale;
+ final NumberFormat nf;
+ // We translate dialpad numbers only for "fa" and not any other locale
+ // ("ar" anybody ?).
+ if ("fa".equals(currentLocale.getLanguage())) {
+ nf = DecimalFormat.getInstance(resources.getConfiguration().locale);
+ } else {
+ nf = DecimalFormat.getInstance(Locale.ENGLISH);
+ }
+
+ for (int i = 0; i < mButtonIds.length; i++) {
+ dialpadKey = (DialpadKeyButton) findViewById(mButtonIds[i]);
+ numberView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_number);
+ lettersView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_letters);
+
+ final String numberString;
+ final CharSequence numberContentDescription;
+ if (mButtonIds[i] == R.id.pound) {
+ numberString = resources.getString(R.string.dialpad_pound_number);
+ numberContentDescription = numberString;
+ } else if (mButtonIds[i] == R.id.star) {
+ numberString = resources.getString(R.string.dialpad_star_number);
+ numberContentDescription = numberString;
+ } else {
+ numberString = nf.format(i);
+ // The content description is used for Talkback key presses. The number is
+ // separated by a "," to introduce a slight delay. Convert letters into a verbatim
+ // span so that they are read as letters instead of as one word.
+ String letters = resources.getString(letterIds[i]);
+ Spannable spannable =
+ Spannable.Factory.getInstance().newSpannable(numberString + "," + letters);
+ spannable.setSpan(
+ (new TtsSpan.VerbatimBuilder(letters)).build(),
+ numberString.length() + 1,
+ numberString.length() + 1 + letters.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ numberContentDescription = spannable;
+ }
+
+ final RippleDrawable rippleBackground =
+ (RippleDrawable) getDrawableCompat(getContext(), R.drawable.btn_dialpad_key);
+ if (mRippleColor != null) {
+ rippleBackground.setColor(mRippleColor);
+ }
+
+ numberView.setText(numberString);
+ numberView.setElegantTextHeight(false);
+ dialpadKey.setContentDescription(numberContentDescription);
+ dialpadKey.setBackground(rippleBackground);
+
+ if (lettersView != null) {
+ lettersView.setText(resources.getString(letterIds[i]));
+ }
+ }
+
+ final DialpadKeyButton one = (DialpadKeyButton) findViewById(R.id.one);
+ one.setLongHoverContentDescription(resources.getText(R.string.description_voicemail_button));
+
+ final DialpadKeyButton zero = (DialpadKeyButton) findViewById(R.id.zero);
+ zero.setLongHoverContentDescription(resources.getText(R.string.description_image_button_plus));
+ }
+
+ private Drawable getDrawableCompat(Context context, int id) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ return context.getDrawable(id);
+ } else {
+ return context.getResources().getDrawable(id);
+ }
+ }
+
+ public void setShowVoicemailButton(boolean show) {
+ View view = findViewById(R.id.dialpad_key_voicemail);
+ if (view != null) {
+ view.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+
+ /**
+ * Whether or not the digits above the dialer can be edited.
+ *
+ * @param canBeEdited If true, the backspace button will be shown and the digits EditText will be
+ * configured to allow text manipulation.
+ */
+ public void setCanDigitsBeEdited(boolean canBeEdited) {
+ View deleteButton = findViewById(R.id.deleteButton);
+ deleteButton.setVisibility(canBeEdited ? View.VISIBLE : View.INVISIBLE);
+ View overflowMenuButton = findViewById(R.id.dialpad_overflow);
+ overflowMenuButton.setVisibility(canBeEdited ? View.VISIBLE : View.GONE);
+
+ EditText digits = (EditText) findViewById(R.id.digits);
+ digits.setClickable(canBeEdited);
+ digits.setLongClickable(canBeEdited);
+ digits.setFocusableInTouchMode(canBeEdited);
+ digits.setCursorVisible(false);
+
+ mCanDigitsBeEdited = canBeEdited;
+ }
+
+ public void setCallRateInformation(String countryName, String displayRate) {
+ if (TextUtils.isEmpty(countryName) && TextUtils.isEmpty(displayRate)) {
+ mRateContainer.setVisibility(View.GONE);
+ return;
+ }
+ mRateContainer.setVisibility(View.VISIBLE);
+ mIldCountry.setText(countryName);
+ mIldRate.setText(displayRate);
+ }
+
+ public boolean canDigitsBeEdited() {
+ return mCanDigitsBeEdited;
+ }
+
+ /**
+ * Always returns true for onHoverEvent callbacks, to fix problems with accessibility due to the
+ * dialpad overlaying other fragments.
+ */
+ @Override
+ public boolean onHoverEvent(MotionEvent event) {
+ return true;
+ }
+
+ public void animateShow() {
+ // This is a hack; without this, the setTranslationY is delayed in being applied, and the
+ // numbers appear at their original position (0) momentarily before animating.
+ final AnimatorListenerAdapter showListener = new AnimatorListenerAdapter() {};
+
+ for (int i = 0; i < mButtonIds.length; i++) {
+ int delay = (int) (getKeyButtonAnimationDelay(mButtonIds[i]) * DELAY_MULTIPLIER);
+ int duration = (int) (getKeyButtonAnimationDuration(mButtonIds[i]) * DURATION_MULTIPLIER);
+ final DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(mButtonIds[i]);
+
+ ViewPropertyAnimator animator = dialpadKey.animate();
+ if (mIsLandscape) {
+ // Landscape orientation requires translation along the X axis.
+ // For RTL locales, ensure we translate negative on the X axis.
+ dialpadKey.setTranslationX((mIsRtl ? -1 : 1) * mTranslateDistance);
+ animator.translationX(0);
+ } else {
+ // Portrait orientation requires translation along the Y axis.
+ dialpadKey.setTranslationY(mTranslateDistance);
+ animator.translationY(0);
+ }
+ animator
+ .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
+ .setStartDelay(delay)
+ .setDuration(duration)
+ .setListener(showListener)
+ .start();
+ }
+ }
+
+ public EditText getDigits() {
+ return mDigits;
+ }
+
+ public ImageButton getDeleteButton() {
+ return mDelete;
+ }
+
+ public View getOverflowMenuButton() {
+ return mOverflowMenuButton;
+ }
+
+ /**
+ * Get the animation delay for the buttons, taking into account whether the dialpad is in
+ * landscape left-to-right, landscape right-to-left, or portrait.
+ *
+ * @param buttonId The button ID.
+ * @return The animation delay.
+ */
+ private int getKeyButtonAnimationDelay(int buttonId) {
+ if (mIsLandscape) {
+ if (mIsRtl) {
+ if (buttonId == R.id.three) {
+ return KEY_FRAME_DURATION * 1;
+ } else if (buttonId == R.id.six) {
+ return KEY_FRAME_DURATION * 2;
+ } else if (buttonId == R.id.nine) {
+ return KEY_FRAME_DURATION * 3;
+ } else if (buttonId == R.id.pound) {
+ return KEY_FRAME_DURATION * 4;
+ } else if (buttonId == R.id.two) {
+ return KEY_FRAME_DURATION * 5;
+ } else if (buttonId == R.id.five) {
+ return KEY_FRAME_DURATION * 6;
+ } else if (buttonId == R.id.eight) {
+ return KEY_FRAME_DURATION * 7;
+ } else if (buttonId == R.id.zero) {
+ return KEY_FRAME_DURATION * 8;
+ } else if (buttonId == R.id.one) {
+ return KEY_FRAME_DURATION * 9;
+ } else if (buttonId == R.id.four) {
+ return KEY_FRAME_DURATION * 10;
+ } else if (buttonId == R.id.seven || buttonId == R.id.star) {
+ return KEY_FRAME_DURATION * 11;
+ }
+ } else {
+ if (buttonId == R.id.one) {
+ return KEY_FRAME_DURATION * 1;
+ } else if (buttonId == R.id.four) {
+ return KEY_FRAME_DURATION * 2;
+ } else if (buttonId == R.id.seven) {
+ return KEY_FRAME_DURATION * 3;
+ } else if (buttonId == R.id.star) {
+ return KEY_FRAME_DURATION * 4;
+ } else if (buttonId == R.id.two) {
+ return KEY_FRAME_DURATION * 5;
+ } else if (buttonId == R.id.five) {
+ return KEY_FRAME_DURATION * 6;
+ } else if (buttonId == R.id.eight) {
+ return KEY_FRAME_DURATION * 7;
+ } else if (buttonId == R.id.zero) {
+ return KEY_FRAME_DURATION * 8;
+ } else if (buttonId == R.id.three) {
+ return KEY_FRAME_DURATION * 9;
+ } else if (buttonId == R.id.six) {
+ return KEY_FRAME_DURATION * 10;
+ } else if (buttonId == R.id.nine || buttonId == R.id.pound) {
+ return KEY_FRAME_DURATION * 11;
+ }
+ }
+ } else {
+ if (buttonId == R.id.one) {
+ return KEY_FRAME_DURATION * 1;
+ } else if (buttonId == R.id.two) {
+ return KEY_FRAME_DURATION * 2;
+ } else if (buttonId == R.id.three) {
+ return KEY_FRAME_DURATION * 3;
+ } else if (buttonId == R.id.four) {
+ return KEY_FRAME_DURATION * 4;
+ } else if (buttonId == R.id.five) {
+ return KEY_FRAME_DURATION * 5;
+ } else if (buttonId == R.id.six) {
+ return KEY_FRAME_DURATION * 6;
+ } else if (buttonId == R.id.seven) {
+ return KEY_FRAME_DURATION * 7;
+ } else if (buttonId == R.id.eight) {
+ return KEY_FRAME_DURATION * 8;
+ } else if (buttonId == R.id.nine) {
+ return KEY_FRAME_DURATION * 9;
+ } else if (buttonId == R.id.star) {
+ return KEY_FRAME_DURATION * 10;
+ } else if (buttonId == R.id.zero || buttonId == R.id.pound) {
+ return KEY_FRAME_DURATION * 11;
+ }
+ }
+
+ Log.wtf(TAG, "Attempted to get animation delay for invalid key button id.");
+ return 0;
+ }
+
+ /**
+ * Get the button animation duration, taking into account whether the dialpad is in landscape
+ * left-to-right, landscape right-to-left, or portrait.
+ *
+ * @param buttonId The button ID.
+ * @return The animation duration.
+ */
+ private int getKeyButtonAnimationDuration(int buttonId) {
+ if (mIsLandscape) {
+ if (mIsRtl) {
+ if (buttonId == R.id.one
+ || buttonId == R.id.four
+ || buttonId == R.id.seven
+ || buttonId == R.id.star) {
+ return KEY_FRAME_DURATION * 8;
+ } else if (buttonId == R.id.two
+ || buttonId == R.id.five
+ || buttonId == R.id.eight
+ || buttonId == R.id.zero) {
+ return KEY_FRAME_DURATION * 9;
+ } else if (buttonId == R.id.three
+ || buttonId == R.id.six
+ || buttonId == R.id.nine
+ || buttonId == R.id.pound) {
+ return KEY_FRAME_DURATION * 10;
+ }
+ } else {
+ if (buttonId == R.id.one
+ || buttonId == R.id.four
+ || buttonId == R.id.seven
+ || buttonId == R.id.star) {
+ return KEY_FRAME_DURATION * 10;
+ } else if (buttonId == R.id.two
+ || buttonId == R.id.five
+ || buttonId == R.id.eight
+ || buttonId == R.id.zero) {
+ return KEY_FRAME_DURATION * 9;
+ } else if (buttonId == R.id.three
+ || buttonId == R.id.six
+ || buttonId == R.id.nine
+ || buttonId == R.id.pound) {
+ return KEY_FRAME_DURATION * 8;
+ }
+ }
+ } else {
+ if (buttonId == R.id.one
+ || buttonId == R.id.two
+ || buttonId == R.id.three
+ || buttonId == R.id.four
+ || buttonId == R.id.five
+ || buttonId == R.id.six) {
+ return KEY_FRAME_DURATION * 10;
+ } else if (buttonId == R.id.seven || buttonId == R.id.eight || buttonId == R.id.nine) {
+ return KEY_FRAME_DURATION * 9;
+ } else if (buttonId == R.id.star || buttonId == R.id.zero || buttonId == R.id.pound) {
+ return KEY_FRAME_DURATION * 8;
+ }
+ }
+
+ Log.wtf(TAG, "Attempted to get animation duration for invalid key button id.");
+ return 0;
+ }
+}
diff --git a/java/com/android/dialer/dialpadview/DigitsEditText.java b/java/com/android/dialer/dialpadview/DigitsEditText.java
new file mode 100644
index 000000000..4a4b9b4e2
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/DigitsEditText.java
@@ -0,0 +1,57 @@
+/*
+ * 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.dialer.dialpadview;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.text.InputType;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.inputmethod.InputMethodManager;
+import com.android.dialer.widget.ResizingTextEditText;
+
+/** EditText which suppresses IME show up. */
+public class DigitsEditText extends ResizingTextEditText {
+
+ public DigitsEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
+ setShowSoftInputOnFocus(false);
+ }
+
+ @Override
+ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+ super.onFocusChanged(focused, direction, previouslyFocusedRect);
+ final InputMethodManager imm =
+ ((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE));
+ if (imm != null && imm.isActive(this)) {
+ imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ final boolean ret = super.onTouchEvent(event);
+ // Must be done after super.onTouchEvent()
+ final InputMethodManager imm =
+ ((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE));
+ if (imm != null && imm.isActive(this)) {
+ imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0);
+ }
+ return ret;
+ }
+}
diff --git a/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_in_bottom.xml b/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_in_bottom.xml
new file mode 100644
index 000000000..4efa80d86
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_in_bottom.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="@integer/dialpad_slide_in_duration"
+ android:fromYDelta="100%p"
+ android:toYDelta="0"/>
diff --git a/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_in_left.xml b/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_in_left.xml
new file mode 100644
index 000000000..4e5a2053c
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_in_left.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2014 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
+ -->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="@integer/dialpad_slide_in_duration"
+ android:fromXDelta="-100%p"
+ android:toXDelta="0"/>
diff --git a/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_in_right.xml b/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_in_right.xml
new file mode 100644
index 000000000..5a6dfaa79
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_in_right.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (c) 2014, 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.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="@integer/dialpad_slide_in_duration"
+ android:fromXDelta="100%p"
+ android:toXDelta="0"/>
diff --git a/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_out_bottom.xml b/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_out_bottom.xml
new file mode 100644
index 000000000..01ac48247
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_out_bottom.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="@integer/dialpad_slide_out_duration"
+ android:fromYDelta="0"
+ android:toYDelta="100%p"/>
diff --git a/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_out_left.xml b/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_out_left.xml
new file mode 100644
index 000000000..5ac1d290f
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_out_left.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2014 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
+ -->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="@integer/dialpad_slide_out_duration"
+ android:fromXDelta="0"
+ android:toXDelta="-100%"/>
diff --git a/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_out_right.xml b/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_out_right.xml
new file mode 100644
index 000000000..5f5690232
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/anim/dialpad_slide_out_right.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (c) 2014, 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.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="@integer/dialpad_slide_out_duration"
+ android:fromXDelta="0"
+ android:toXDelta="100%"/>
diff --git a/java/com/android/dialer/dialpadview/res/drawable-hdpi/dialer_fab.png b/java/com/android/dialer/dialpadview/res/drawable-hdpi/dialer_fab.png
new file mode 100644
index 000000000..3380a899d
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-hdpi/dialer_fab.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-hdpi/fab_green.png b/java/com/android/dialer/dialpadview/res/drawable-hdpi/fab_green.png
new file mode 100644
index 000000000..ff9753c18
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-hdpi/fab_green.png
Binary files differ
diff --git a/res/drawable-hdpi/fab_ic_call.png b/java/com/android/dialer/dialpadview/res/drawable-hdpi/fab_ic_call.png
index 7bf83fa6a..7bf83fa6a 100644
--- a/res/drawable-hdpi/fab_ic_call.png
+++ b/java/com/android/dialer/dialpadview/res/drawable-hdpi/fab_ic_call.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_close_black_24dp.png b/java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_close_black_24dp.png
new file mode 100644
index 000000000..1a9cd75a0
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_close_black_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_dialpad_delete.png b/java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_dialpad_delete.png
new file mode 100644
index 000000000..e588d90e9
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_dialpad_delete.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_dialpad_voicemail.png b/java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_dialpad_voicemail.png
new file mode 100644
index 000000000..4706112d6
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_dialpad_voicemail.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_overflow_menu.png b/java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_overflow_menu.png
new file mode 100644
index 000000000..262e9df91
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-hdpi/ic_overflow_menu.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-mdpi/dialer_fab.png b/java/com/android/dialer/dialpadview/res/drawable-mdpi/dialer_fab.png
new file mode 100644
index 000000000..46630d430
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-mdpi/dialer_fab.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-mdpi/fab_green.png b/java/com/android/dialer/dialpadview/res/drawable-mdpi/fab_green.png
new file mode 100644
index 000000000..947aac142
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-mdpi/fab_green.png
Binary files differ
diff --git a/res/drawable-mdpi/fab_ic_call.png b/java/com/android/dialer/dialpadview/res/drawable-mdpi/fab_ic_call.png
index 790f93590..790f93590 100644
--- a/res/drawable-mdpi/fab_ic_call.png
+++ b/java/com/android/dialer/dialpadview/res/drawable-mdpi/fab_ic_call.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_close_black_24dp.png b/java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_close_black_24dp.png
new file mode 100644
index 000000000..40a1a84e3
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_close_black_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_dialpad_delete.png b/java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_dialpad_delete.png
new file mode 100644
index 000000000..64a52d030
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_dialpad_delete.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_dialpad_voicemail.png b/java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_dialpad_voicemail.png
new file mode 100644
index 000000000..e84d8f4a8
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_dialpad_voicemail.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_overflow_menu.png b/java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_overflow_menu.png
new file mode 100644
index 000000000..0e720ddbd
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-mdpi/ic_overflow_menu.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xhdpi/dialer_fab.png b/java/com/android/dialer/dialpadview/res/drawable-xhdpi/dialer_fab.png
new file mode 100644
index 000000000..5dafee092
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xhdpi/dialer_fab.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xhdpi/fab_green.png b/java/com/android/dialer/dialpadview/res/drawable-xhdpi/fab_green.png
new file mode 100644
index 000000000..e8bab3fec
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xhdpi/fab_green.png
Binary files differ
diff --git a/res/drawable-xhdpi/fab_ic_call.png b/java/com/android/dialer/dialpadview/res/drawable-xhdpi/fab_ic_call.png
index 6bd53f5c5..6bd53f5c5 100644
--- a/res/drawable-xhdpi/fab_ic_call.png
+++ b/java/com/android/dialer/dialpadview/res/drawable-xhdpi/fab_ic_call.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_close_black_24dp.png b/java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_close_black_24dp.png
new file mode 100644
index 000000000..6bc437298
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_close_black_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_dialpad_delete.png b/java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_dialpad_delete.png
new file mode 100644
index 000000000..87bc11364
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_dialpad_delete.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_dialpad_voicemail.png b/java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_dialpad_voicemail.png
new file mode 100644
index 000000000..0b4e18389
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_dialpad_voicemail.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_overflow_menu.png b/java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_overflow_menu.png
new file mode 100644
index 000000000..915607633
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xhdpi/ic_overflow_menu.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/dialer_fab.png b/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/dialer_fab.png
new file mode 100644
index 000000000..2b0dba7bc
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/dialer_fab.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/fab_green.png b/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/fab_green.png
new file mode 100644
index 000000000..7e4fd3e49
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/fab_green.png
Binary files differ
diff --git a/res/drawable-xxhdpi/fab_ic_call.png b/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/fab_ic_call.png
index 6866fa430..6866fa430 100644
--- a/res/drawable-xxhdpi/fab_ic_call.png
+++ b/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/fab_ic_call.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_close_black_24dp.png b/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_close_black_24dp.png
new file mode 100644
index 000000000..51b4401ca
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_close_black_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_dialpad_delete.png b/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_dialpad_delete.png
new file mode 100644
index 000000000..186508a9f
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_dialpad_delete.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_dialpad_voicemail.png b/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_dialpad_voicemail.png
new file mode 100644
index 000000000..a0a7c9d28
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_dialpad_voicemail.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_overflow_menu.png b/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_overflow_menu.png
new file mode 100644
index 000000000..92526f5a6
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xxhdpi/ic_overflow_menu.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/dialer_fab.png b/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/dialer_fab.png
new file mode 100644
index 000000000..59d9b9506
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/dialer_fab.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/fab_green.png b/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/fab_green.png
new file mode 100644
index 000000000..aa8849e86
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/fab_green.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/fab_ic_call.png b/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/fab_ic_call.png
index 7af3396b4..7af3396b4 100644
--- a/InCallUI/res/drawable-xxxhdpi/fab_ic_call.png
+++ b/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/fab_ic_call.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_close_black_24dp.png b/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_close_black_24dp.png
new file mode 100644
index 000000000..df42feecb
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_close_black_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_dialpad_delete.png b/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_dialpad_delete.png
new file mode 100644
index 000000000..c974a8005
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_dialpad_delete.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_dialpad_voicemail.png b/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_dialpad_voicemail.png
new file mode 100644
index 000000000..c6e8be023
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_dialpad_voicemail.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_overflow_menu.png b/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_overflow_menu.png
new file mode 100644
index 000000000..9028bd437
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable-xxxhdpi/ic_overflow_menu.png
Binary files differ
diff --git a/java/com/android/dialer/dialpadview/res/drawable/btn_dialpad_key.xml b/java/com/android/dialer/dialpadview/res/drawable/btn_dialpad_key.xml
new file mode 100644
index 000000000..53cd7a85d
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable/btn_dialpad_key.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight"/> \ No newline at end of file
diff --git a/java/com/android/dialer/dialpadview/res/drawable/dialpad_scrim.xml b/java/com/android/dialer/dialpadview/res/drawable/dialpad_scrim.xml
new file mode 100644
index 000000000..ee0f40ab5
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/drawable/dialpad_scrim.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:angle="270"
+ android:endColor="@android:color/darker_gray"
+ android:startColor="@android:color/transparent"/>
+</shape>
diff --git a/java/com/android/dialer/dialpadview/res/layout-land/dialpad_key.xml b/java/com/android/dialer/dialpadview/res/layout-land/dialpad_key.xml
new file mode 100644
index 000000000..941fdb2ec
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/layout-land/dialpad_key.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<!-- A layout representing a single key in the dialpad -->
+<com.android.dialer.dialpadview.DialpadKeyButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/DialpadKeyButtonStyle">
+
+ <LinearLayout
+ style="@style/DialpadKeyInternalLayoutStyle"
+ android:layout_gravity="right|center_vertical"
+ android:baselineAligned="false"
+ android:orientation="horizontal">
+
+ <!-- Note in the referenced styles that we assign hard widths to these components
+ because we want them to line up vertically when we arrange them in an MxN grid -->
+
+ <com.android.dialer.dialpadview.DialpadTextView
+ android:id="@+id/dialpad_key_number"
+ style="@style/DialpadKeyNumberStyle"
+ android:layout_marginBottom="0dp"
+ android:layout_marginRight="@dimen/dialpad_key_margin_right"
+ android:layout_gravity="right"/>
+
+ <TextView
+ android:id="@+id/dialpad_key_letters"
+ style="@style/DialpadKeyLettersStyle"
+ android:layout_width="@dimen/dialpad_key_text_width"
+ android:layout_gravity="right|center"/>
+ </LinearLayout>
+</com.android.dialer.dialpadview.DialpadKeyButton>
diff --git a/java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_one.xml b/java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_one.xml
new file mode 100644
index 000000000..65a2308fc
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_one.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<com.android.dialer.dialpadview.DialpadKeyButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/one"
+ style="@style/DialpadKeyButtonStyle">
+ <LinearLayout
+ style="@style/DialpadKeyInternalLayoutStyle"
+ android:layout_gravity="right|center_vertical"
+ android:baselineAligned="false"
+ android:orientation="horizontal">
+ <com.android.dialer.dialpadview.DialpadTextView
+ android:id="@+id/dialpad_key_number"
+ style="@style/DialpadKeyNumberStyle"
+ android:layout_marginBottom="0dp"
+ android:layout_marginRight="@dimen/dialpad_key_one_margin_right"
+ android:layout_gravity="right"/>
+ <FrameLayout
+ android:layout_width="@dimen/dialpad_key_text_width"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left|center">
+ <ImageView
+ android:id="@+id/dialpad_key_voicemail"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_dialpad_voicemail"
+ android:tint="@color/dialpad_voicemail_tint"/>
+ </FrameLayout>
+ </LinearLayout>
+</com.android.dialer.dialpadview.DialpadKeyButton>
diff --git a/java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_pound.xml b/java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_pound.xml
new file mode 100644
index 000000000..98c353128
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_pound.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<com.android.dialer.dialpadview.DialpadKeyButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pound"
+ style="@style/DialpadKeyButtonStyle">
+ <LinearLayout
+ style="@style/DialpadKeyInternalLayoutStyle"
+ android:layout_gravity="center_vertical|right"
+ android:orientation="horizontal">
+ <com.android.dialer.dialpadview.DialpadTextView
+ android:id="@id/dialpad_key_number"
+ style="@style/DialpadKeyPoundStyle"
+ android:layout_width="@dimen/dialpad_key_number_width"
+ android:layout_marginRight="@dimen/dialpad_key_margin_right"/>
+ <View
+ style="@style/DialpadKeyLettersStyle"
+ android:layout_width="@dimen/dialpad_key_text_width"/>
+ </LinearLayout>
+</com.android.dialer.dialpadview.DialpadKeyButton>
diff --git a/java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_star.xml b/java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_star.xml
new file mode 100644
index 000000000..b91c71680
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_star.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<com.android.dialer.dialpadview.DialpadKeyButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/star"
+ style="@style/DialpadKeyButtonStyle">
+ <LinearLayout
+ style="@style/DialpadKeyInternalLayoutStyle"
+ android:layout_gravity="center_vertical|right"
+ android:orientation="horizontal">
+ <com.android.dialer.dialpadview.DialpadTextView
+ android:id="@id/dialpad_key_number"
+ style="@style/DialpadKeyStarStyle"
+ android:layout_width="@dimen/dialpad_key_number_width"
+ android:layout_marginRight="@dimen/dialpad_key_margin_right"/>
+ <View
+ style="@style/DialpadKeyLettersStyle"
+ android:layout_width="@dimen/dialpad_key_text_width"/>
+ </LinearLayout>
+</com.android.dialer.dialpadview.DialpadKeyButton>
diff --git a/java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_zero.xml b/java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_zero.xml
new file mode 100644
index 000000000..d885ddf05
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/layout-land/dialpad_key_zero.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<!-- A layout representing the zero key in the dialpad, with the plus sign shifted up because it is
+ smaller than a regular letter -->
+<com.android.dialer.dialpadview.DialpadKeyButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/zero"
+ style="@style/DialpadKeyButtonStyle">
+
+ <LinearLayout
+ style="@style/DialpadKeyInternalLayoutStyle"
+ android:layout_gravity="right|center_vertical"
+ android:baselineAligned="false"
+ android:orientation="horizontal">
+
+ <!-- Note in the referenced styles that we assign hard widths to these components
+ because we want them to line up vertically when we arrange them in an MxN grid -->
+
+ <com.android.dialer.dialpadview.DialpadTextView
+ android:id="@+id/dialpad_key_number"
+ style="@style/DialpadBottomKeyNumberStyle"
+ android:layout_marginBottom="0dp"
+ android:layout_marginRight="@dimen/dialpad_key_margin_right"/>
+
+ <TextView
+ android:id="@+id/dialpad_key_letters"
+ style="@style/DialpadKeyLettersStyle"
+ android:layout_width="@dimen/dialpad_key_text_width"/>
+ </LinearLayout>
+</com.android.dialer.dialpadview.DialpadKeyButton>
diff --git a/java/com/android/dialer/dialpadview/res/layout/dialpad.xml b/java/com/android/dialer/dialpadview/res/layout/dialpad.xml
new file mode 100644
index 000000000..5a14d14ea
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/layout/dialpad.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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.
+-->
+
+<!-- Dialpad in the Phone app. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/dialpad"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+ <Space style="@style/DialpadSpaceStyle"/>
+ <include layout="@layout/dialpad_key_one"/>
+ <include
+ android:id="@+id/two"
+ style="@style/DialpadKeyButtonStyle"
+ layout="@layout/dialpad_key"/>
+ <include
+ android:id="@+id/three"
+ style="@style/DialpadKeyButtonStyle"
+ layout="@layout/dialpad_key"/>
+ <Space style="@style/DialpadSpaceStyle"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+ <Space style="@style/DialpadSpaceStyle"/>
+ <include
+ android:id="@+id/four"
+ style="@style/DialpadKeyButtonStyle"
+ layout="@layout/dialpad_key"/>
+ <include
+ android:id="@+id/five"
+ style="@style/DialpadKeyButtonStyle"
+ layout="@layout/dialpad_key"/>
+ <include
+ android:id="@+id/six"
+ style="@style/DialpadKeyButtonStyle"
+ layout="@layout/dialpad_key"/>
+ <Space style="@style/DialpadSpaceStyle"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+ <Space style="@style/DialpadSpaceStyle"/>
+ <include
+ android:id="@+id/seven"
+ style="@style/DialpadKeyButtonStyle"
+ layout="@layout/dialpad_key"/>
+ <include
+ android:id="@+id/eight"
+ style="@style/DialpadKeyButtonStyle"
+ layout="@layout/dialpad_key"/>
+ <include
+ android:id="@+id/nine"
+ style="@style/DialpadKeyButtonStyle"
+ layout="@layout/dialpad_key"/>
+ <Space style="@style/DialpadSpaceStyle"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+ <Space style="@style/DialpadSpaceStyle"/>
+ <include layout="@layout/dialpad_key_star"/>
+ <include layout="@layout/dialpad_key_zero"/>
+ <include layout="@layout/dialpad_key_pound"/>
+ <Space style="@style/DialpadSpaceStyle"/>
+ </LinearLayout>
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="?attr/dialpad_end_key_spacing"/>
+</LinearLayout>
diff --git a/java/com/android/dialer/dialpadview/res/layout/dialpad_key.xml b/java/com/android/dialer/dialpadview/res/layout/dialpad_key.xml
new file mode 100644
index 000000000..77e4fc53a
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/layout/dialpad_key.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<!-- A layout representing a single key in the dialpad -->
+<com.android.dialer.dialpadview.DialpadKeyButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/DialpadKeyButtonStyle">
+
+ <LinearLayout style="@style/DialpadKeyInternalLayoutStyle">
+
+ <!-- Note in the referenced styles that we assign hard widths to these components
+ because we want them to line up vertically when we arrange them in an MxN grid -->
+
+ <com.android.dialer.dialpadview.DialpadTextView
+ android:id="@+id/dialpad_key_number"
+ style="@style/DialpadKeyNumberStyle"/>
+
+ <TextView
+ android:id="@+id/dialpad_key_letters"
+ style="@style/DialpadKeyLettersStyle"/>
+ </LinearLayout>
+</com.android.dialer.dialpadview.DialpadKeyButton>
diff --git a/java/com/android/dialer/dialpadview/res/layout/dialpad_key_one.xml b/java/com/android/dialer/dialpadview/res/layout/dialpad_key_one.xml
new file mode 100644
index 000000000..36f62c85d
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/layout/dialpad_key_one.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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.
+-->
+<com.android.dialer.dialpadview.DialpadKeyButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/one"
+ style="@style/DialpadKeyButtonStyle">
+ <LinearLayout
+ style="@style/DialpadKeyInternalLayoutStyle">
+ <com.android.dialer.dialpadview.DialpadTextView
+ android:id="@+id/dialpad_key_number"
+ style="@style/DialpadKeyNumberStyle"/>
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <ImageView
+ android:id="@+id/dialpad_key_voicemail"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:paddingTop="@dimen/dialpad_voicemail_icon_padding_top"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_dialpad_voicemail"
+ android:tint="?attr/dialpad_voicemail_tint"/>
+ <!-- Place empty text view so vertical height is same as other dialpad keys. -->
+ <TextView style="@style/DialpadKeyLettersStyle"/>
+ </RelativeLayout>
+ </LinearLayout>
+</com.android.dialer.dialpadview.DialpadKeyButton>
diff --git a/java/com/android/dialer/dialpadview/res/layout/dialpad_key_pound.xml b/java/com/android/dialer/dialpadview/res/layout/dialpad_key_pound.xml
new file mode 100644
index 000000000..d37a6aa78
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/layout/dialpad_key_pound.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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.
+-->
+<com.android.dialer.dialpadview.DialpadKeyButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pound"
+ style="@style/DialpadKeyButtonStyle">
+ <LinearLayout
+ style="@style/DialpadKeyInternalLayoutStyle">
+ <com.android.dialer.dialpadview.DialpadTextView
+ android:id="@id/dialpad_key_number"
+ style="@style/DialpadKeyPoundStyle"/>
+ </LinearLayout>
+</com.android.dialer.dialpadview.DialpadKeyButton>
diff --git a/java/com/android/dialer/dialpadview/res/layout/dialpad_key_star.xml b/java/com/android/dialer/dialpadview/res/layout/dialpad_key_star.xml
new file mode 100644
index 000000000..d288475d0
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/layout/dialpad_key_star.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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.
+-->
+<com.android.dialer.dialpadview.DialpadKeyButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/star"
+ style="@style/DialpadKeyButtonStyle">
+ <LinearLayout
+ style="@style/DialpadKeyInternalLayoutStyle">
+ <com.android.dialer.dialpadview.DialpadTextView
+ android:id="@+id/dialpad_key_number"
+ style="@style/DialpadKeyStarStyle"/>
+ </LinearLayout>
+</com.android.dialer.dialpadview.DialpadKeyButton>
diff --git a/java/com/android/dialer/dialpadview/res/layout/dialpad_key_zero.xml b/java/com/android/dialer/dialpadview/res/layout/dialpad_key_zero.xml
new file mode 100644
index 000000000..943ae48dd
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/layout/dialpad_key_zero.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<!-- A layout representing the zero key in the dialpad, with the plus sign shifted up because it is
+ smaller than a regular letter -->
+<com.android.dialer.dialpadview.DialpadKeyButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/zero"
+ style="@style/DialpadKeyButtonStyle">
+
+ <LinearLayout style="@style/DialpadKeyInternalLayoutStyle">
+
+ <!-- Note in the referenced styles that we assign hard widths to these components
+ because we want them to line up vertically when we arrange them in an MxN grid -->
+
+ <com.android.dialer.dialpadview.DialpadTextView
+ android:id="@+id/dialpad_key_number"
+ style="@style/DialpadBottomKeyNumberStyle"/>
+
+ <TextView
+ android:id="@+id/dialpad_key_letters"
+ style="@style/DialpadKeyLettersStyle"/>
+ </LinearLayout>
+</com.android.dialer.dialpadview.DialpadKeyButton>
diff --git a/java/com/android/dialer/dialpadview/res/layout/dialpad_view.xml b/java/com/android/dialer/dialpadview/res/layout/dialpad_view.xml
new file mode 100644
index 000000000..47112fbb1
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/layout/dialpad_view.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 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
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:theme="?attr/dialpad_style">
+ <include layout="@layout/dialpad_view_unthemed"/>
+</FrameLayout>
diff --git a/java/com/android/dialer/dialpadview/res/layout/dialpad_view_unthemed.xml b/java/com/android/dialer/dialpadview/res/layout/dialpad_view_unthemed.xml
new file mode 100644
index 000000000..4b08c6de7
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/layout/dialpad_view_unthemed.xml
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/dialpad_view"
+ class="com.android.dialer.dialpadview.DialpadView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="bottom"
+ android:background="?attr/dialpad_background"
+ android:clickable="true"
+ android:layoutDirection="ltr"
+ android:orientation="vertical">
+
+ <!-- Text field where call rate is displayed for ILD calls. -->
+ <LinearLayout
+ android:id="@+id/rate_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
+
+ <LinearLayout
+ android:id="@+id/ild_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/ild_margin_height"
+ android:layout_marginBottom="@dimen/ild_margin_height"
+ android:layout_gravity="center_horizontal"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/ild_country"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/ild_rate"
+ android:textStyle="bold"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="4dp"/>
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="#e3e3e3"/>
+
+ </LinearLayout>
+
+ <!-- Text field and possibly soft menu button above the keypad where
+ the digits are displayed. -->
+ <LinearLayout
+ android:id="@+id/digits_container"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/dialpad_digits_adjustable_height"
+ android:orientation="horizontal">
+
+ <ImageButton
+ android:id="@+id/dialpad_back"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_margin="@dimen/dialpad_overflow_margin"
+ android:paddingLeft="@dimen/dialpad_digits_menu_left_padding"
+ android:paddingRight="@dimen/dialpad_digits_menu_right_padding"
+ android:background="@drawable/btn_dialpad_key"
+ android:contentDescription="@string/description_dialpad_back"
+ android:gravity="center"
+ android:src="@drawable/ic_close_black_24dp"
+ android:tint="?attr/dialpad_icon_tint"
+ android:tintMode="src_in"
+ android:visibility="gone"/>
+
+ <ImageButton
+ android:id="@+id/dialpad_overflow"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_margin="@dimen/dialpad_overflow_margin"
+ android:paddingLeft="@dimen/dialpad_digits_menu_left_padding"
+ android:paddingRight="@dimen/dialpad_digits_menu_right_padding"
+ android:background="@drawable/btn_dialpad_key"
+ android:contentDescription="@string/description_dialpad_overflow"
+ android:gravity="center"
+ android:src="@drawable/ic_overflow_menu"
+ android:tint="?attr/dialpad_icon_tint"
+ android:tintMode="src_in"
+ android:visibility="gone"/>
+
+ <view xmlns:ex="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/digits"
+ class="com.android.dialer.dialpadview.DigitsEditText"
+ android:textStyle="normal"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="@android:color/transparent"
+ android:cursorVisible="false"
+ android:focusableInTouchMode="true"
+ android:fontFamily="sans-serif"
+ android:freezesText="true"
+ android:gravity="center"
+ android:maxLines="1"
+ android:scrollHorizontally="true"
+ android:singleLine="true"
+ android:textColor="?attr/dialpad_text_color"
+ android:textCursorDrawable="@null"
+ android:textSize="?attr/dialpad_digits_adjustable_text_size"
+ ex:resizing_text_min_size="@dimen/dialpad_digits_text_min_size"/>
+
+ <ImageButton
+ android:id="@+id/deleteButton"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/dialpad_digits_padding"
+ android:paddingRight="@dimen/dialpad_digits_padding"
+ android:background="@drawable/btn_dialpad_key"
+ android:contentDescription="@string/description_delete_button"
+ android:src="@drawable/ic_dialpad_delete"
+ android:state_enabled="false"
+ android:tint="?attr/dialpad_icon_tint"
+ android:tintMode="src_in"/>
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="#e3e3e3"/>
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dialpad_space_above_keys"/>
+
+ <include layout="@layout/dialpad"/>
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dialpad_space_below_keys"/>
+
+</view>
diff --git a/java/com/android/dialer/dialpadview/res/values-land/dimens.xml b/java/com/android/dialer/dialpadview/res/values-land/dimens.xml
new file mode 100644
index 000000000..617134ad4
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values-land/dimens.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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.
+ -->
+<resources>
+ <dimen name="dialpad_key_margin_right">5dp</dimen>
+ <!-- Right margins for specific keys to align them correctly -->
+ <dimen name="dialpad_key_one_margin_right">3dp</dimen>
+ <dimen name="dialpad_key_text_width">35dp</dimen>
+ <dimen name="dialpad_key_number_width">20sp</dimen>
+ <dimen name="dialpad_symbol_margin_bottom">0dp</dimen>
+
+ <!-- The bottom space of the dialpad to account for the dial button -->
+ <dimen name="dialpad_bottom_space_height">65dp</dimen>
+</resources>
diff --git a/java/com/android/dialer/dialpadview/res/values-land/styles.xml b/java/com/android/dialer/dialpadview/res/values-land/styles.xml
new file mode 100644
index 000000000..f98372509
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values-land/styles.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 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
+ -->
+
+<resources>
+
+ <style name="DialpadKeyNumberStyle">
+ <item name="android:textColor">?attr/dialpad_text_color_primary</item>
+ <item name="android:textSize">?attr/dialpad_key_numbers_size</item>
+ <item name="android:fontFamily">sans-serif-light</item>
+ <item name="android:layout_width">@dimen/dialpad_key_number_width</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_marginBottom">?attr/dialpad_key_number_margin_bottom</item>
+ </style>
+
+ <style name="DialpadKeyLettersStyle">
+ <item name="android:textColor">?attr/dialpad_text_color_secondary</item>
+ <item name="android:textSize">@dimen/dialpad_key_letters_size</item>
+ <item name="android:fontFamily">sans-serif-regular</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">left</item>
+ </style>
+</resources>
diff --git a/java/com/android/dialer/dialpadview/res/values/animation_constants.xml b/java/com/android/dialer/dialpadview/res/values/animation_constants.xml
new file mode 100644
index 000000000..edd19d755
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values/animation_constants.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 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
+ -->
+<resources>
+ <integer name="dialpad_slide_in_duration">400</integer>
+ <integer name="dialpad_slide_out_duration">400</integer>
+</resources>
diff --git a/java/com/android/dialer/dialpadview/res/values/attrs.xml b/java/com/android/dialer/dialpadview/res/values/attrs.xml
new file mode 100644
index 000000000..273879f3e
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values/attrs.xml
@@ -0,0 +1,39 @@
+<!--
+ ~ 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.
+ -->
+
+<resources>
+
+ <attr format="reference" name="dialpad_style"/>
+ <attr format="dimension" name="dialpad_end_key_spacing"/>
+
+ <declare-styleable name="Dialpad">
+ <attr format="color" name="dialpad_key_button_touch_tint"/>
+ <attr format="dimension" name="dialpad_digits_adjustable_text_size"/>
+ <attr format="dimension" name="dialpad_digits_adjustable_height"/>
+ <attr format="dimension" name="dialpad_key_numbers_size"/>
+ <attr format="dimension" name="dialpad_key_number_margin_bottom"/>
+ <attr format="dimension" name="dialpad_zero_key_number_margin_bottom"/>
+ </declare-styleable>
+
+ <declare-styleable name="Theme.Dialpad">
+ <attr format="color" name="dialpad_text_color"/>
+ <attr format="color" name="dialpad_text_color_primary"/>
+ <attr format="color" name="dialpad_text_color_secondary"/>
+ <attr format="color" name="dialpad_icon_tint"/>
+ <attr format="color" name="dialpad_voicemail_tint"/>
+ <attr format="color" name="dialpad_background"/>
+ </declare-styleable>
+</resources>
diff --git a/java/com/android/dialer/dialpadview/res/values/colors.xml b/java/com/android/dialer/dialpadview/res/values/colors.xml
new file mode 100644
index 000000000..d27468db7
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values/colors.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <!-- Colors for the dialpad -->
+ <color name="background_dialpad">#fcfcfc</color>
+ <color name="background_dialpad_pressed">#ececec</color>
+ <color name="dialpad_primary_text_color">@color/dialer_theme_color</color>
+ <color name="dialpad_secondary_text_color">#737373</color>
+ <color name="dialpad_digits_text_color">#333</color>
+ <color name="dialpad_separator_line_color">#dadada</color>
+ <color name="dialpad_icon_tint">#89000000</color>
+ <color name="dialpad_voicemail_tint">#919191</color>
+</resources>
diff --git a/java/com/android/dialer/dialpadview/res/values/dimens.xml b/java/com/android/dialer/dialpadview/res/values/dimens.xml
new file mode 100644
index 000000000..210c81697
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values/dimens.xml
@@ -0,0 +1,48 @@
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <!-- Text dimensions for dialpad keys -->
+ <dimen name="dialpad_key_numbers_default_size">36sp</dimen>
+ <dimen name="dialpad_key_letters_size">12sp</dimen>
+ <dimen name="dialpad_key_pound_size">23sp</dimen>
+ <dimen name="dialpad_key_star_size">36sp</dimen>
+ <dimen name="dialpad_key_height">64dp</dimen>
+ <dimen name="dialpad_key_number_default_margin_bottom">3dp</dimen>
+ <!-- Zero key should have less space between self and text because "+" is smaller -->
+ <dimen name="dialpad_zero_key_number_default_margin_bottom">1dp</dimen>
+ <dimen name="dialpad_symbol_margin_bottom">13dp</dimen>
+ <dimen name="dialpad_key_plus_size">18sp</dimen>
+ <dimen name="dialpad_horizontal_padding">5dp</dimen>
+ <dimen name="dialpad_digits_text_size">34sp</dimen>
+ <dimen name="dialpad_digits_text_min_size">24sp</dimen>
+ <dimen name="dialpad_digits_height">60dp</dimen>
+ <dimen name="dialpad_digits_padding">16dp</dimen>
+ <dimen name="dialpad_digits_menu_left_padding">8dp</dimen>
+ <dimen name="dialpad_digits_menu_right_padding">10dp</dimen>
+ <dimen name="dialpad_center_margin">3dp</dimen>
+ <dimen name="dialpad_button_margin">2dp</dimen>
+ <dimen name="dialpad_voicemail_icon_padding_top">2dp</dimen>
+ <dimen name="dialpad_key_button_translate_y">100dp</dimen>
+ <dimen name="dialpad_overflow_margin">8dp</dimen>
+ <dimen name="dialpad_space_above_keys">14dp</dimen>
+ <dimen name="dialpad_space_below_keys">8dp</dimen>
+ <!-- The bottom space of the dialpad to account for the dial button -->
+ <dimen name="dialpad_bottom_space_height">80dp</dimen>
+
+ <!-- Top/Bottom padding around the ILD rate display box. -->
+ <dimen name="ild_margin_height">10dp</dimen>
+</resources>
diff --git a/java/com/android/dialer/dialpadview/res/values/strings.xml b/java/com/android/dialer/dialpadview/res/values/strings.xml
new file mode 100644
index 000000000..920e6e25c
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values/strings.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources>
+ <string name="dialpad_star_number" translatable="false">*</string>
+ <string name="dialpad_pound_number" translatable="false">#</string>
+
+ <string name="dialpad_0_letters" translatable="false">+</string>
+ <string name="dialpad_1_letters" translatable="false"></string>
+ <string name="dialpad_2_letters" translatable="false">ABC</string>
+ <string name="dialpad_3_letters" translatable="false">DEF</string>
+ <string name="dialpad_4_letters" translatable="false">GHI</string>
+ <string name="dialpad_5_letters" translatable="false">JKL</string>
+ <string name="dialpad_6_letters" translatable="false">MNO</string>
+ <string name="dialpad_7_letters" translatable="false">PQRS</string>
+ <string name="dialpad_8_letters" translatable="false">TUV</string>
+ <string name="dialpad_9_letters" translatable="false">WXYZ</string>
+ <string name="dialpad_star_letters" translatable="false"></string>
+ <string name="dialpad_pound_letters" translatable="false"></string>
+
+ <!-- String describing the back button in the dialpad. -->
+ <string name="description_dialpad_back">Navigate back</string>
+
+ <!-- String describing the overflow menu button in the dialpad. -->
+ <string name="description_dialpad_overflow">More options</string>
+
+ <!-- String describing the Delete/Backspace ImageButton.
+ Used by AccessibilityService to announce the purpose of the button.
+ -->
+ <string name="description_delete_button">backspace</string>
+
+ <!-- String describing the button used to add a plus (+) symbol to the dialpad -->
+ <string name="description_image_button_plus">plus</string>
+
+ <!-- String describing the Voicemail ImageButton.
+ Used by AccessibilityService to announce the purpose of the button.
+ -->
+ <string name="description_voicemail_button">voicemail</string>
+
+</resources>
diff --git a/java/com/android/dialer/dialpadview/res/values/styles.xml b/java/com/android/dialer/dialpadview/res/values/styles.xml
new file mode 100644
index 000000000..2fa2c3f2e
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values/styles.xml
@@ -0,0 +1,118 @@
+<!--
+ ~ 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
+ -->
+
+<resources>
+
+ <style name="DialpadSpaceStyle">
+ <item name="android:layout_width">0dp</item>
+ <item name="android:layout_height">match_parent</item>
+ <item name="android:layout_weight">3</item>
+ </style>
+
+ <style name="DialpadKeyNumberStyle">
+ <item name="android:textColor">?attr/dialpad_text_color_primary</item>
+ <item name="android:textSize">?attr/dialpad_key_numbers_size</item>
+ <item name="android:fontFamily">sans-serif-light</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_marginBottom">?attr/dialpad_key_number_margin_bottom</item>
+ <item name="android:gravity">center</item>
+ </style>
+
+ <style name="DialpadBottomKeyNumberStyle" parent="DialpadKeyNumberStyle">
+ <item name="android:layout_marginBottom">?attr/dialpad_zero_key_number_margin_bottom</item>
+ </style>
+
+ <style name="DialpadKeyStarStyle">
+ <item name="android:textColor">?attr/dialpad_text_color_secondary</item>
+ <item name="android:textSize">@dimen/dialpad_key_star_size</item>
+ <item name="android:fontFamily">sans-serif-light</item>
+ <item name="android:alpha">0.8</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_marginBottom">@dimen/dialpad_symbol_margin_bottom</item>
+ <item name="android:gravity">center</item>
+ </style>
+
+ <style name="DialpadKeyPoundStyle">
+ <item name="android:textColor">?attr/dialpad_text_color_secondary</item>
+ <item name="android:textSize">@dimen/dialpad_key_pound_size</item>
+ <item name="android:fontFamily">sans-serif-light</item>
+ <item name="android:alpha">0.8</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_marginBottom">@dimen/dialpad_symbol_margin_bottom</item>
+ <item name="android:gravity">center</item>
+ </style>
+
+ <style name="DialpadKeyLettersStyle">
+ <item name="android:textColor">?attr/dialpad_text_color_secondary</item>
+ <item name="android:textSize">@dimen/dialpad_key_letters_size</item>
+ <item name="android:fontFamily">sans-serif-regular</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">center_horizontal</item>
+ </style>
+
+ <style name="DialpadKeyButtonStyle">
+ <item name="android:soundEffectsEnabled">false</item>
+ <item name="android:clickable">true</item>
+ <item name="android:layout_width">0dp</item>
+ <item name="android:layout_height">match_parent</item>
+ <item name="android:layout_weight">13</item>
+ <item name="android:minHeight">@dimen/dialpad_key_height</item>
+ <item name="android:background">@drawable/btn_dialpad_key</item>
+ <item name="android:focusable">true</item>
+ </style>
+
+ <style name="DialpadKeyInternalLayoutStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_gravity">center</item>
+ <item name="android:gravity">center</item>
+ <item name="android:orientation">vertical</item>
+ </style>
+
+ <style name="Dialpad">
+ <item name="dialpad_digits_adjustable_height">@dimen/dialpad_digits_height</item>
+ <item name="dialpad_digits_adjustable_text_size">@dimen/dialpad_digits_text_size</item>
+ <item name="dialpad_key_numbers_size">@dimen/dialpad_key_numbers_default_size</item>
+ <item name="dialpad_key_number_margin_bottom">@dimen/dialpad_key_number_default_margin_bottom
+ </item>
+ <item name="dialpad_zero_key_number_margin_bottom">
+ @dimen/dialpad_zero_key_number_default_margin_bottom
+ </item>
+ <item name="dialpad_end_key_spacing">@dimen/dialpad_bottom_space_height</item>
+ </style>
+
+ <style name="Dialpad.Light">
+ <item name="dialpad_text_color">@color/dialpad_digits_text_color</item>
+ <item name="dialpad_text_color_primary">@color/dialpad_primary_text_color</item>
+ <item name="dialpad_text_color_secondary">@color/dialpad_secondary_text_color</item>
+ <item name="dialpad_icon_tint">@color/dialpad_icon_tint</item>
+ <item name="dialpad_voicemail_tint">@color/dialpad_voicemail_tint</item>
+ <item name="dialpad_background">@color/background_dialpad</item>
+ </style>
+
+ <style name="Dialpad.Dark">
+ <item name="dialpad_text_color">@android:color/white</item>
+ <item name="dialpad_text_color_primary">@android:color/white</item>
+ <item name="dialpad_text_color_secondary">#ffd4d6d7</item>
+ <item name="dialpad_icon_tint">@android:color/white</item>
+ <item name="dialpad_voicemail_tint">?attr/dialpad_text_color_secondary</item>
+ <item name="dialpad_background">#00000000</item>
+ </style>
+</resources>
diff --git a/java/com/android/dialer/disabled_lint_checks.txt b/java/com/android/dialer/disabled_lint_checks.txt
new file mode 100644
index 000000000..13a3d05cf
--- /dev/null
+++ b/java/com/android/dialer/disabled_lint_checks.txt
@@ -0,0 +1 @@
+InlinedApi
diff --git a/java/com/android/dialer/enrichedcall/AutoValue_EnrichedCallCapabilities.java b/java/com/android/dialer/enrichedcall/AutoValue_EnrichedCallCapabilities.java
new file mode 100644
index 000000000..14299f92c
--- /dev/null
+++ b/java/com/android/dialer/enrichedcall/AutoValue_EnrichedCallCapabilities.java
@@ -0,0 +1,76 @@
+/*
+ * 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.dialer.enrichedcall;
+
+import javax.annotation.Generated;
+
+@Generated("com.google.auto.value.processor.AutoValueProcessor")
+ final class AutoValue_EnrichedCallCapabilities extends EnrichedCallCapabilities {
+
+ private final boolean supportsCallComposer;
+ private final boolean supportsPostCall;
+
+ AutoValue_EnrichedCallCapabilities(
+ boolean supportsCallComposer,
+ boolean supportsPostCall) {
+ this.supportsCallComposer = supportsCallComposer;
+ this.supportsPostCall = supportsPostCall;
+ }
+
+ @Override
+ public boolean supportsCallComposer() {
+ return supportsCallComposer;
+ }
+
+ @Override
+ public boolean supportsPostCall() {
+ return supportsPostCall;
+ }
+
+ @Override
+ public String toString() {
+ return "EnrichedCallCapabilities{"
+ + "supportsCallComposer=" + supportsCallComposer + ", "
+ + "supportsPostCall=" + supportsPostCall + ", "
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof EnrichedCallCapabilities) {
+ EnrichedCallCapabilities that = (EnrichedCallCapabilities) o;
+ return (this.supportsCallComposer == that.supportsCallComposer())
+ && (this.supportsPostCall == that.supportsPostCall());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 1;
+ h *= 1000003;
+ h ^= this.supportsCallComposer ? 1231 : 1237;
+ h *= 1000003;
+ h ^= this.supportsPostCall ? 1231 : 1237;
+ return h;
+ }
+
+}
+
diff --git a/java/com/android/dialer/enrichedcall/AutoValue_OutgoingCallComposerData.java b/java/com/android/dialer/enrichedcall/AutoValue_OutgoingCallComposerData.java
new file mode 100644
index 000000000..edfefc479
--- /dev/null
+++ b/java/com/android/dialer/enrichedcall/AutoValue_OutgoingCallComposerData.java
@@ -0,0 +1,127 @@
+/*
+ * 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.dialer.enrichedcall;
+
+import android.net.Uri;
+import android.support.annotation.Nullable;
+import javax.annotation.Generated;
+
+@Generated("com.google.auto.value.processor.AutoValueProcessor")
+ final class AutoValue_OutgoingCallComposerData extends OutgoingCallComposerData {
+
+ private final String subject;
+ private final Uri imageUri;
+ private final String imageContentType;
+
+ private AutoValue_OutgoingCallComposerData(
+ @Nullable String subject,
+ @Nullable Uri imageUri,
+ @Nullable String imageContentType) {
+ this.subject = subject;
+ this.imageUri = imageUri;
+ this.imageContentType = imageContentType;
+ }
+
+ @Nullable
+ @Override
+ public String getSubject() {
+ return subject;
+ }
+
+ @Nullable
+ @Override
+ public Uri getImageUri() {
+ return imageUri;
+ }
+
+ @Nullable
+ @Override
+ public String getImageContentType() {
+ return imageContentType;
+ }
+
+ @Override
+ public String toString() {
+ return "OutgoingCallComposerData{"
+ + "subject=" + subject + ", "
+ + "imageUri=" + imageUri + ", "
+ + "imageContentType=" + imageContentType
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof OutgoingCallComposerData) {
+ OutgoingCallComposerData that = (OutgoingCallComposerData) o;
+ return ((this.subject == null) ? (that.getSubject() == null) : this.subject.equals(that.getSubject()))
+ && ((this.imageUri == null) ? (that.getImageUri() == null) : this.imageUri.equals(that.getImageUri()))
+ && ((this.imageContentType == null) ? (that.getImageContentType() == null) : this.imageContentType.equals(that.getImageContentType()));
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 1;
+ h *= 1000003;
+ h ^= (subject == null) ? 0 : this.subject.hashCode();
+ h *= 1000003;
+ h ^= (imageUri == null) ? 0 : this.imageUri.hashCode();
+ h *= 1000003;
+ h ^= (imageContentType == null) ? 0 : this.imageContentType.hashCode();
+ return h;
+ }
+
+ static final class Builder extends OutgoingCallComposerData.Builder {
+ private String subject;
+ private Uri imageUri;
+ private String imageContentType;
+ Builder() {
+ }
+ private Builder(OutgoingCallComposerData source) {
+ this.subject = source.getSubject();
+ this.imageUri = source.getImageUri();
+ this.imageContentType = source.getImageContentType();
+ }
+ @Override
+ public OutgoingCallComposerData.Builder setSubject(@Nullable String subject) {
+ this.subject = subject;
+ return this;
+ }
+ @Override
+ OutgoingCallComposerData.Builder setImageUri(@Nullable Uri imageUri) {
+ this.imageUri = imageUri;
+ return this;
+ }
+ @Override
+ OutgoingCallComposerData.Builder setImageContentType(@Nullable String imageContentType) {
+ this.imageContentType = imageContentType;
+ return this;
+ }
+ @Override
+ OutgoingCallComposerData autoBuild() {
+ return new AutoValue_OutgoingCallComposerData(
+ this.subject,
+ this.imageUri,
+ this.imageContentType);
+ }
+ }
+
+}
diff --git a/java/com/android/dialer/enrichedcall/EnrichedCallCapabilities.java b/java/com/android/dialer/enrichedcall/EnrichedCallCapabilities.java
new file mode 100644
index 000000000..b7d780950
--- /dev/null
+++ b/java/com/android/dialer/enrichedcall/EnrichedCallCapabilities.java
@@ -0,0 +1,36 @@
+/*
+ * 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.dialer.enrichedcall;
+
+
+
+/** Value type holding enriched call capabilities. */
+
+public abstract class EnrichedCallCapabilities {
+
+ public static final EnrichedCallCapabilities NO_CAPABILITIES =
+ EnrichedCallCapabilities.create(false, false);
+
+ public static EnrichedCallCapabilities create(
+ boolean supportsCallComposer, boolean supportsPostCall) {
+ return new AutoValue_EnrichedCallCapabilities(supportsCallComposer, supportsPostCall);
+ }
+
+ public abstract boolean supportsCallComposer();
+
+ public abstract boolean supportsPostCall();
+}
diff --git a/java/com/android/dialer/enrichedcall/EnrichedCallManager.java b/java/com/android/dialer/enrichedcall/EnrichedCallManager.java
new file mode 100644
index 000000000..6af8c409a
--- /dev/null
+++ b/java/com/android/dialer/enrichedcall/EnrichedCallManager.java
@@ -0,0 +1,225 @@
+/*
+ * 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.dialer.enrichedcall;
+
+import android.app.Application;
+import android.support.annotation.IntDef;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import com.android.dialer.common.Assert;
+import com.android.dialer.multimedia.MultimediaData;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Performs all enriched calling logic. */
+public interface EnrichedCallManager {
+
+ /** Factory for {@link EnrichedCallManager}. */
+ interface Factory {
+ EnrichedCallManager getEnrichedCallManager();
+ }
+
+ /** Accessor for {@link EnrichedCallManager}. */
+ class Accessor {
+
+ /**
+ * @throws IllegalArgumentException if application does not implement {@link
+ * EnrichedCallManager.Factory}
+ */
+ @NonNull
+ public static EnrichedCallManager getInstance(@NonNull Application application) {
+ Assert.isNotNull(application);
+
+ return ((EnrichedCallManager.Factory) application).getEnrichedCallManager();
+ }
+ }
+
+ /** Receives updates when enriched call capabilities are ready. */
+ interface CapabilitiesListener {
+
+ /** Callback fired when the capabilities are updated. */
+ @MainThread
+ void onCapabilitiesUpdated();
+ }
+
+ /**
+ * Registers the given {@link CapabilitiesListener}.
+ *
+ * <p>As a result of this method, the listener will receive a call to {@link
+ * CapabilitiesListener#onCapabilitiesUpdated()} after a call to {@link
+ * #requestCapabilities(String)}.
+ */
+ @MainThread
+ void registerCapabilitiesListener(@NonNull CapabilitiesListener listener);
+
+ /**
+ * Starts an asynchronous process to get enriched call capabilities of the given number.
+ *
+ * <p>Registered listeners will receive a call to {@link
+ * CapabilitiesListener#onCapabilitiesUpdated()} on completion.
+ *
+ * @param number the remote number in any format
+ */
+ @MainThread
+ void requestCapabilities(@NonNull String number);
+
+ /**
+ * Unregisters the given {@link CapabilitiesListener}.
+ *
+ * <p>As a result of this method, the listener will not receive capabilities of the given number.
+ */
+ @MainThread
+ void unregisterCapabilitiesListener(@NonNull CapabilitiesListener listener);
+
+ /** Gets the cached capabilities for the given number, else null */
+ @MainThread
+ @Nullable
+ EnrichedCallCapabilities getCapabilities(@NonNull String number);
+
+ /** Clears any cached data, such as capabilities. */
+ @MainThread
+ void clearCachedData();
+
+ /** Possible states for call composer sessions. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ STATE_NONE,
+ STATE_STARTING,
+ STATE_STARTED,
+ STATE_START_FAILED,
+ STATE_MESSAGE_SENT,
+ STATE_MESSAGE_FAILED,
+ STATE_CLOSED,
+ })
+ @interface State {}
+
+ int STATE_NONE = 0;
+ int STATE_STARTING = STATE_NONE + 1;
+ int STATE_STARTED = STATE_STARTING + 1;
+ int STATE_START_FAILED = STATE_STARTED + 1;
+ int STATE_MESSAGE_SENT = STATE_START_FAILED + 1;
+ int STATE_MESSAGE_FAILED = STATE_MESSAGE_SENT + 1;
+ int STATE_CLOSED = STATE_MESSAGE_FAILED + 1;
+
+ /**
+ * Starts a call composer session with the given remote number.
+ *
+ * @param number the remote number in any format
+ * @return the id for the started session, or {@link Session#NO_SESSION_ID} if the session fails
+ */
+ @MainThread
+ long startCallComposerSession(@NonNull String number);
+
+ /**
+ * Sends the given information through an open enriched call session. As per the enriched calling
+ * spec, up to two messages are sent: the first is an enriched call data message that optionally
+ * includes the subject and the second is the optional image data message.
+ *
+ * @param sessionId the id for the session. See {@link #startCallComposerSession(String)}
+ * @param data the {@link MultimediaData}
+ * @throws IllegalArgumentException if there's no open session with the given number
+ * @throws IllegalStateException if the session isn't in the {@link #STATE_STARTED} state
+ */
+ @MainThread
+ void sendCallComposerData(long sessionId, @NonNull MultimediaData data);
+
+ /**
+ * Ends the given call composer session. Ending a session means that the call composer session
+ * will be closed.
+ *
+ * @param sessionId the id of the session to end
+ */
+ @MainThread
+ void endCallComposerSession(long sessionId);
+
+ /**
+ * Called once the capabilities are available for a corresponding call to {@link
+ * #requestCapabilities(String)}.
+ *
+ * @param number the remote number in any format
+ * @param capabilities the supported capabilities
+ */
+ @MainThread
+ void onCapabilitiesReceived(
+ @NonNull String number, @NonNull EnrichedCallCapabilities capabilities);
+
+ /** Receives updates when the state of an enriched call changes. */
+ interface StateChangedListener {
+
+ /**
+ * Callback fired when state changes. Listeners should call {@link #getSession(String)} to
+ * retrieve the new state.
+ */
+ void onEnrichedCallStateChanged();
+ }
+
+ /**
+ * Registers the given {@link StateChangedListener}.
+ *
+ * <p>As a result of this method, the listener will receive updates when the state of any enriched
+ * call changes.
+ */
+ @MainThread
+ void registerStateChangedListener(@NonNull StateChangedListener listener);
+
+ /** Returns the {@link Session} for the given number, or {@code null} if no session exists. */
+ @MainThread
+ @Nullable
+ Session getSession(@NonNull String number);
+
+ /** Returns the {@link Session} for the given sessionId, or {@code null} if no session exists. */
+ @MainThread
+ @Nullable
+ Session getSession(long sessionId);
+
+ /**
+ * Unregisters the given {@link StateChangedListener}.
+ *
+ * <p>As a result of this method, the listener will not receive updates when the state of enriched
+ * calls changes.
+ */
+ @MainThread
+ void unregisterStateChangedListener(@NonNull StateChangedListener listener);
+
+ /**
+ * Called when the status of an enriched call session changes.
+ *
+ *
+ * @throws IllegalArgumentException if the state is invalid
+ */
+ @MainThread
+ void onSessionStatusUpdate(long sessionId, @NonNull String number, int state);
+
+ /**
+ * Called when the status of an enriched call message updates.
+ *
+ *
+ * @throws IllegalArgumentException if the state is invalid
+ * @throws IllegalStateException if there's no session for the given id
+ */
+ @MainThread
+ void onMessageUpdate(long sessionId, @NonNull String messageId, int state);
+
+ /**
+ * Called when call composer data arrives for the given session.
+ *
+ * @throws IllegalStateException if there's no session for the given id
+ */
+ @MainThread
+ void onIncomingCallComposerData(long sessionId, @NonNull MultimediaData multimediaData);
+}
diff --git a/java/com/android/dialer/enrichedcall/EnrichedCallManagerStub.java b/java/com/android/dialer/enrichedcall/EnrichedCallManagerStub.java
new file mode 100644
index 000000000..db9a799d3
--- /dev/null
+++ b/java/com/android/dialer/enrichedcall/EnrichedCallManagerStub.java
@@ -0,0 +1,84 @@
+/*
+ * 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.dialer.enrichedcall;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import com.android.dialer.multimedia.MultimediaData;
+
+/** Stub implementation of {@link EnrichedCallManager}. */
+public final class EnrichedCallManagerStub implements EnrichedCallManager {
+
+ @Override
+ public void registerCapabilitiesListener(@NonNull CapabilitiesListener listener) {}
+
+ @Override
+ public void requestCapabilities(@NonNull String number) {}
+
+ @Override
+ public void unregisterCapabilitiesListener(@NonNull CapabilitiesListener listener) {}
+
+ @Override
+ public EnrichedCallCapabilities getCapabilities(@NonNull String number) {
+ return null;
+ }
+
+ @Override
+ public void clearCachedData() {}
+
+ @Override
+ public long startCallComposerSession(@NonNull String number) {
+ return Session.NO_SESSION_ID;
+ }
+
+ @Override
+ public void sendCallComposerData(long sessionId, @NonNull MultimediaData data) {}
+
+ @Override
+ public void endCallComposerSession(long sessionId) {}
+
+ @Override
+ public void onCapabilitiesReceived(
+ @NonNull String number, @NonNull EnrichedCallCapabilities capabilities) {}
+
+ @Override
+ public void registerStateChangedListener(@NonNull StateChangedListener listener) {}
+
+ @Nullable
+ @Override
+ public Session getSession(@NonNull String number) {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public Session getSession(long sessionId) {
+ return null;
+ }
+
+ @Override
+ public void unregisterStateChangedListener(@NonNull StateChangedListener listener) {}
+
+ @Override
+ public void onSessionStatusUpdate(long sessionId, @NonNull String number, int state) {}
+
+ @Override
+ public void onMessageUpdate(long sessionId, @NonNull String messageId, int state) {}
+
+ @Override
+ public void onIncomingCallComposerData(long sessionId, @NonNull MultimediaData multimediaData) {}
+}
diff --git a/java/com/android/dialer/enrichedcall/OutgoingCallComposerData.java b/java/com/android/dialer/enrichedcall/OutgoingCallComposerData.java
new file mode 100644
index 000000000..a8ee49d4e
--- /dev/null
+++ b/java/com/android/dialer/enrichedcall/OutgoingCallComposerData.java
@@ -0,0 +1,94 @@
+/*
+ * 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.dialer.enrichedcall;
+
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import com.android.dialer.common.Assert;
+
+
+/**
+ * Value type holding references to all data that could be provided for the call composer.
+ *
+ * <p>Note: Either the subject, the image data, or both must be specified, e.g.
+ *
+ * <pre>
+ * OutgoingCallComposerData.builder.build(); // throws exception, no data set
+ * OutgoingCallComposerData
+ * .setSubject(subject)
+ * .build(); // Success
+ * OutgoingCallComposerData
+ * .setImageData(uri, contentType)
+ * .build(); // Success
+ * OutgoingCallComposerData
+ * .setSubject(subject)
+ * .setImageData(uri, contentType)
+ * .build(); // Success
+ * </pre>
+ */
+
+public abstract class OutgoingCallComposerData {
+
+ public static Builder builder() {
+ return new AutoValue_OutgoingCallComposerData.Builder();
+ }
+
+ public boolean hasImageData() {
+ return getImageUri() != null && getImageContentType() != null;
+ }
+
+ @Nullable
+ public abstract String getSubject();
+
+ @Nullable
+ public abstract Uri getImageUri();
+
+ @Nullable
+ public abstract String getImageContentType();
+
+ /** Builds instances of {@link OutgoingCallComposerData}. */
+
+ public abstract static class Builder {
+ public abstract Builder setSubject(String subject);
+
+ public Builder setImageData(@NonNull Uri imageUri, @NonNull String imageContentType) {
+ setImageUri(Assert.isNotNull(imageUri));
+ setImageContentType(Assert.isNotNull(imageContentType));
+ return this;
+ }
+
+ abstract Builder setImageUri(Uri imageUri);
+
+ abstract Builder setImageContentType(String imageContentType);
+
+ abstract OutgoingCallComposerData autoBuild();
+
+ /**
+ * Returns the OutgoingCallComposerData from this builder.
+ *
+ * @return the OutgoingCallComposerData.
+ * @throws IllegalStateException if neither {@link #setSubject(String)} nor {@link
+ * #setImageData(Uri, String)} were called.
+ */
+ public OutgoingCallComposerData build() {
+ OutgoingCallComposerData data = autoBuild();
+ Assert.checkState(data.getSubject() != null || data.hasImageData());
+ return data;
+ }
+ }
+}
diff --git a/java/com/android/dialer/enrichedcall/Session.java b/java/com/android/dialer/enrichedcall/Session.java
new file mode 100644
index 000000000..b0439fae9
--- /dev/null
+++ b/java/com/android/dialer/enrichedcall/Session.java
@@ -0,0 +1,63 @@
+/*
+ * 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.dialer.enrichedcall;
+
+import android.support.annotation.NonNull;
+import com.android.dialer.enrichedcall.EnrichedCallManager.State;
+import com.android.dialer.multimedia.MultimediaData;
+
+/** Holds state information and data about enriched calling sessions. */
+public interface Session {
+
+ /** Id used for sessions that fail to start. */
+ long NO_SESSION_ID = -1;
+
+ /**
+ * An id for the specific case when sending a message fails so early that a message id isn't
+ * created.
+ */
+ String MESSAGE_ID_COULD_NOT_CREATE_ID = "messageIdCouldNotCreateId";
+
+ /**
+ * Returns the id associated with this session, or {@link #NO_SESSION_ID} if this represents a
+ * session that failed to start.
+ */
+ long getSessionId();
+
+ /** Returns the number associated with the remote end of this session. */
+ @NonNull
+ String getRemoteNumber();
+
+ /** Returns the {@link State} for this session. */
+ @State
+ int getState();
+
+ /** Returns the {@link MultimediaData} associated with this session. */
+ @NonNull
+ MultimediaData getMultimediaData();
+
+ /** Returns type of this session, based on some arbitrarily defined type. */
+ int getType();
+
+ /**
+ * Sets the {@link MultimediaData} for this session.
+ *
+ *
+ * @throws IllegalArgumentException if the type is invalid
+ */
+ void setSessionData(@NonNull MultimediaData multimediaData, int type);
+}
diff --git a/java/com/android/dialer/enrichedcall/StubEnrichedCallModule.java b/java/com/android/dialer/enrichedcall/StubEnrichedCallModule.java
new file mode 100644
index 000000000..39c55d040
--- /dev/null
+++ b/java/com/android/dialer/enrichedcall/StubEnrichedCallModule.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 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.dialer.enrichedcall;
+
+import dagger.Module;
+import dagger.Provides;
+import javax.inject.Singleton;
+
+/** Module which binds {@link EnrichedCallManagerStub}. */
+@Module
+public class StubEnrichedCallModule {
+
+ @Provides
+ @Singleton
+ static EnrichedCallManager provideEnrichedCallManager() {
+ return new EnrichedCallManagerStub();
+ }
+}
diff --git a/java/com/android/dialer/enrichedcall/extensions/StateExtension.java b/java/com/android/dialer/enrichedcall/extensions/StateExtension.java
new file mode 100644
index 000000000..8a4f6409d
--- /dev/null
+++ b/java/com/android/dialer/enrichedcall/extensions/StateExtension.java
@@ -0,0 +1,54 @@
+/*
+ * 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.dialer.enrichedcall.extensions;
+
+import android.support.annotation.NonNull;
+import com.android.dialer.common.Assert;
+import com.android.dialer.enrichedcall.EnrichedCallManager;
+import com.android.dialer.enrichedcall.EnrichedCallManager.State;
+
+/** Extends the {@link State} to include a toString method. */
+public class StateExtension {
+
+ /** Returns the string representation for the given {@link State}. */
+ @NonNull
+ public static String toString(@State int callComposerState) {
+ if (callComposerState == EnrichedCallManager.STATE_NONE) {
+ return "STATE_NONE";
+ }
+ if (callComposerState == EnrichedCallManager.STATE_STARTING) {
+ return "STATE_STARTING";
+ }
+ if (callComposerState == EnrichedCallManager.STATE_STARTED) {
+ return "STATE_STARTED";
+ }
+ if (callComposerState == EnrichedCallManager.STATE_START_FAILED) {
+ return "STATE_START_FAILED";
+ }
+ if (callComposerState == EnrichedCallManager.STATE_MESSAGE_SENT) {
+ return "STATE_MESSAGE_SENT";
+ }
+ if (callComposerState == EnrichedCallManager.STATE_MESSAGE_FAILED) {
+ return "STATE_MESSAGE_FAILED";
+ }
+ if (callComposerState == EnrichedCallManager.STATE_CLOSED) {
+ return "STATE_CLOSED";
+ }
+ Assert.checkArgument(false, "Unexpected callComposerState: %d", callComposerState);
+ return null;
+ }
+}
diff --git a/java/com/android/dialer/inject/ApplicationModule.java b/java/com/android/dialer/inject/ApplicationModule.java
new file mode 100644
index 000000000..99e5296ea
--- /dev/null
+++ b/java/com/android/dialer/inject/ApplicationModule.java
@@ -0,0 +1,39 @@
+/*
+ * 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.dialer.inject;
+
+import android.app.Application;
+import android.support.annotation.NonNull;
+import com.android.dialer.common.Assert;
+import dagger.Module;
+import dagger.Provides;
+
+/** Provides the singleton application object. */
+@Module
+public final class ApplicationModule {
+
+ @NonNull private final Application application;
+
+ public ApplicationModule(@NonNull Application application) {
+ this.application = Assert.isNotNull(application);
+ }
+
+ @Provides
+ Application provideApplication() {
+ return application;
+ }
+}
diff --git a/java/com/android/dialer/inject/DialerAppComponent.java b/java/com/android/dialer/inject/DialerAppComponent.java
new file mode 100644
index 000000000..9832ce804
--- /dev/null
+++ b/java/com/android/dialer/inject/DialerAppComponent.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 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.dialer.inject;
+
+import com.android.dialer.enrichedcall.EnrichedCallManager;
+import com.android.dialer.enrichedcall.StubEnrichedCallModule;
+import dagger.Component;
+import javax.inject.Singleton;
+
+/** Core application-wide {@link Component} for the open source dialer app. */
+@Singleton
+@Component(modules = {ApplicationModule.class, StubEnrichedCallModule.class})
+public interface DialerAppComponent {
+ EnrichedCallManager enrichedCallManager();
+}
diff --git a/java/com/android/dialer/interactions/AndroidManifest.xml b/java/com/android/dialer/interactions/AndroidManifest.xml
new file mode 100644
index 000000000..4571a6965
--- /dev/null
+++ b/java/com/android/dialer/interactions/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.dialer.interactions">
+
+ <application>
+
+ <!-- Service to update a contact -->
+ <service
+ android:exported="false"
+ android:name="com.android.dialer.interactions.ContactUpdateService"/>
+
+ <receiver android:name="com.android.dialer.interactions.UndemoteOutgoingCallReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
+ </intent-filter>
+ </receiver>
+
+ </application>
+
+</manifest>
+
diff --git a/java/com/android/dialer/interactions/ContactUpdateService.java b/java/com/android/dialer/interactions/ContactUpdateService.java
new file mode 100644
index 000000000..9b2d701d2
--- /dev/null
+++ b/java/com/android/dialer/interactions/ContactUpdateService.java
@@ -0,0 +1,48 @@
+/*
+ * 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.dialer.interactions;
+
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import com.android.contacts.common.database.ContactUpdateUtils;
+
+/** Service for updating primary number on a contact. */
+public class ContactUpdateService extends IntentService {
+
+ public static final String EXTRA_PHONE_NUMBER_DATA_ID = "phone_number_data_id";
+
+ public ContactUpdateService() {
+ super(ContactUpdateService.class.getSimpleName());
+ setIntentRedelivery(true);
+ }
+
+ /** Creates an intent that sets the selected data item as super primary (default) */
+ public static Intent createSetSuperPrimaryIntent(Context context, long dataId) {
+ Intent serviceIntent = new Intent(context, ContactUpdateService.class);
+ serviceIntent.putExtra(EXTRA_PHONE_NUMBER_DATA_ID, dataId);
+ return serviceIntent;
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ // Currently this service only handles one type of update.
+ long dataId = intent.getLongExtra(EXTRA_PHONE_NUMBER_DATA_ID, -1);
+
+ ContactUpdateUtils.setSuperPrimary(this, dataId);
+ }
+}
diff --git a/java/com/android/dialer/interactions/PhoneNumberInteraction.java b/java/com/android/dialer/interactions/PhoneNumberInteraction.java
new file mode 100644
index 000000000..f36e5319c
--- /dev/null
+++ b/java/com/android/dialer/interactions/PhoneNumberInteraction.java
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) 2010 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.dialer.interactions;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.Loader;
+import android.content.Loader.OnLoadCompleteListener;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.support.annotation.IntDef;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+import com.android.contacts.common.Collapser;
+import com.android.contacts.common.Collapser.Collapsible;
+import com.android.contacts.common.MoreContactUtils;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.callintent.CallIntentParser;
+import com.android.dialer.callintent.nano.CallSpecificAppData;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.TransactionSafeActivity;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Initiates phone calls or a text message. If there are multiple candidates, this class shows a
+ * dialog to pick one. Creating one of these interactions should be done through the static factory
+ * methods.
+ *
+ * <p>Note that this class initiates not only usual *phone* calls but also *SIP* calls.
+ *
+ * <p>TODO: clean up code and documents since it is quite confusing to use "phone numbers" or "phone
+ * calls" here while they can be SIP addresses or SIP calls (See also issue 5039627).
+ */
+public class PhoneNumberInteraction implements OnLoadCompleteListener<Cursor> {
+
+ private static final String TAG = PhoneNumberInteraction.class.getSimpleName();
+ /** The identifier for a permissions request if one is generated. */
+ public static final int REQUEST_READ_CONTACTS = 1;
+
+ private static final String[] PHONE_NUMBER_PROJECTION =
+ new String[] {
+ Phone._ID,
+ Phone.NUMBER,
+ Phone.IS_SUPER_PRIMARY,
+ RawContacts.ACCOUNT_TYPE,
+ RawContacts.DATA_SET,
+ Phone.TYPE,
+ Phone.LABEL,
+ Phone.MIMETYPE,
+ Phone.CONTACT_ID,
+ };
+
+ private static final String PHONE_NUMBER_SELECTION =
+ Data.MIMETYPE
+ + " IN ('"
+ + Phone.CONTENT_ITEM_TYPE
+ + "', "
+ + "'"
+ + SipAddress.CONTENT_ITEM_TYPE
+ + "') AND "
+ + Data.DATA1
+ + " NOT NULL";
+ private static final int UNKNOWN_CONTACT_ID = -1;
+ private final Context mContext;
+ private final int mInteractionType;
+ private final CallSpecificAppData mCallSpecificAppData;
+ private long mContactId = UNKNOWN_CONTACT_ID;
+ private CursorLoader mLoader;
+ private boolean mIsVideoCall;
+
+ /** Error codes for interactions. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ value = {
+ InteractionErrorCode.CONTACT_NOT_FOUND,
+ InteractionErrorCode.CONTACT_HAS_NO_NUMBER,
+ InteractionErrorCode.USER_LEAVING_ACTIVITY,
+ InteractionErrorCode.OTHER_ERROR
+ }
+ )
+ public @interface InteractionErrorCode {
+
+ int CONTACT_NOT_FOUND = 1;
+ int CONTACT_HAS_NO_NUMBER = 2;
+ int OTHER_ERROR = 3;
+ int USER_LEAVING_ACTIVITY = 4;
+ }
+
+ /**
+ * Activities which use this class must implement this. They will be notified if there was an
+ * error performing the interaction. For example, this callback will be invoked on the activity if
+ * the contact URI provided points to a deleted contact, or to a contact without a phone number.
+ */
+ public interface InteractionErrorListener {
+
+ void interactionError(@InteractionErrorCode int interactionErrorCode);
+ }
+
+ /**
+ * Activities which use this class must implement this. They will be notified if the phone number
+ * disambiguation dialog is dismissed.
+ */
+ public interface DisambigDialogDismissedListener {
+ void onDisambigDialogDismissed();
+ }
+
+ private PhoneNumberInteraction(
+ Context context,
+ int interactionType,
+ boolean isVideoCall,
+ CallSpecificAppData callSpecificAppData) {
+ mContext = context;
+ mInteractionType = interactionType;
+ mCallSpecificAppData = callSpecificAppData;
+ mIsVideoCall = isVideoCall;
+
+ Assert.checkArgument(context instanceof InteractionErrorListener);
+ Assert.checkArgument(context instanceof DisambigDialogDismissedListener);
+ Assert.checkArgument(context instanceof ActivityCompat.OnRequestPermissionsResultCallback);
+ }
+
+ private static void performAction(
+ Context context,
+ String phoneNumber,
+ int interactionType,
+ boolean isVideoCall,
+ CallSpecificAppData callSpecificAppData) {
+ Intent intent;
+ switch (interactionType) {
+ case ContactDisplayUtils.INTERACTION_SMS:
+ intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("sms", phoneNumber, null));
+ break;
+ default:
+ intent =
+ new CallIntentBuilder(phoneNumber, callSpecificAppData)
+ .setIsVideoCall(isVideoCall)
+ .build();
+ break;
+ }
+ DialerUtils.startActivityWithErrorToast(context, intent);
+ }
+
+ /**
+ * @param activity that is calling this interaction. This must be of type {@link
+ * TransactionSafeActivity} because we need to check on the activity state after the phone
+ * numbers have been queried for. The activity must implement {@link InteractionErrorListener}
+ * and {@link DisambigDialogDismissedListener}.
+ * @param isVideoCall {@code true} if the call is a video call, {@code false} otherwise.
+ */
+ public static void startInteractionForPhoneCall(
+ TransactionSafeActivity activity,
+ Uri uri,
+ boolean isVideoCall,
+ CallSpecificAppData callSpecificAppData) {
+ new PhoneNumberInteraction(
+ activity, ContactDisplayUtils.INTERACTION_CALL, isVideoCall, callSpecificAppData)
+ .startInteraction(uri);
+ }
+
+ private void performAction(String phoneNumber) {
+ PhoneNumberInteraction.performAction(
+ mContext, phoneNumber, mInteractionType, mIsVideoCall, mCallSpecificAppData);
+ }
+
+ /**
+ * Initiates the interaction to result in either a phone call or sms message for a contact.
+ *
+ * @param uri Contact Uri
+ */
+ private void startInteraction(Uri uri) {
+ // It's possible for a shortcut to have been created, and then Contacts permissions revoked. To
+ // avoid a crash when the user tries to use such a shortcut, check for this condition and ask
+ // the user for the permission.
+ if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_CONTACTS)
+ != PackageManager.PERMISSION_GRANTED) {
+ LogUtil.i("PhoneNumberInteraction.startInteraction", "No contact permissions");
+ ActivityCompat.requestPermissions(
+ (Activity) mContext,
+ new String[] {Manifest.permission.READ_CONTACTS},
+ REQUEST_READ_CONTACTS);
+ return;
+ }
+
+ if (mLoader != null) {
+ mLoader.reset();
+ }
+ final Uri queryUri;
+ final String inputUriAsString = uri.toString();
+ if (inputUriAsString.startsWith(Contacts.CONTENT_URI.toString())) {
+ if (!inputUriAsString.endsWith(Contacts.Data.CONTENT_DIRECTORY)) {
+ queryUri = Uri.withAppendedPath(uri, Contacts.Data.CONTENT_DIRECTORY);
+ } else {
+ queryUri = uri;
+ }
+ } else if (inputUriAsString.startsWith(Data.CONTENT_URI.toString())) {
+ queryUri = uri;
+ } else {
+ throw new UnsupportedOperationException(
+ "Input Uri must be contact Uri or data Uri (input: \"" + uri + "\")");
+ }
+
+ mLoader =
+ new CursorLoader(
+ mContext, queryUri, PHONE_NUMBER_PROJECTION, PHONE_NUMBER_SELECTION, null, null);
+ mLoader.registerListener(0, this);
+ mLoader.startLoading();
+ }
+
+ @Override
+ public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
+ if (cursor == null) {
+ LogUtil.i("PhoneNumberInteraction.onLoadComplete", "null cursor");
+ interactionError(InteractionErrorCode.OTHER_ERROR);
+ return;
+ }
+ try {
+ ArrayList<PhoneItem> phoneList = new ArrayList<>();
+ String primaryPhone = null;
+ if (!isSafeToCommitTransactions()) {
+ LogUtil.i("PhoneNumberInteraction.onLoadComplete", "not safe to commit transaction");
+ interactionError(InteractionErrorCode.USER_LEAVING_ACTIVITY);
+ return;
+ }
+ if (cursor.moveToFirst()) {
+ int contactIdColumn = cursor.getColumnIndexOrThrow(Phone.CONTACT_ID);
+ int isSuperPrimaryColumn = cursor.getColumnIndexOrThrow(Phone.IS_SUPER_PRIMARY);
+ int phoneNumberColumn = cursor.getColumnIndexOrThrow(Phone.NUMBER);
+ int phoneIdColumn = cursor.getColumnIndexOrThrow(Phone._ID);
+ int accountTypeColumn = cursor.getColumnIndexOrThrow(RawContacts.ACCOUNT_TYPE);
+ int dataSetColumn = cursor.getColumnIndexOrThrow(RawContacts.DATA_SET);
+ int phoneTypeColumn = cursor.getColumnIndexOrThrow(Phone.TYPE);
+ int phoneLabelColumn = cursor.getColumnIndexOrThrow(Phone.LABEL);
+ int phoneMimeTpeColumn = cursor.getColumnIndexOrThrow(Phone.MIMETYPE);
+ do {
+ if (mContactId == UNKNOWN_CONTACT_ID) {
+ mContactId = cursor.getLong(contactIdColumn);
+ }
+
+ if (cursor.getInt(isSuperPrimaryColumn) != 0) {
+ // Found super primary, call it.
+ primaryPhone = cursor.getString(phoneNumberColumn);
+ }
+
+ PhoneItem item = new PhoneItem();
+ item.id = cursor.getLong(phoneIdColumn);
+ item.phoneNumber = cursor.getString(phoneNumberColumn);
+ item.accountType = cursor.getString(accountTypeColumn);
+ item.dataSet = cursor.getString(dataSetColumn);
+ item.type = cursor.getInt(phoneTypeColumn);
+ item.label = cursor.getString(phoneLabelColumn);
+ item.mimeType = cursor.getString(phoneMimeTpeColumn);
+
+ phoneList.add(item);
+ } while (cursor.moveToNext());
+ } else {
+ interactionError(InteractionErrorCode.CONTACT_NOT_FOUND);
+ return;
+ }
+
+ if (primaryPhone != null) {
+ performAction(primaryPhone);
+ return;
+ }
+
+ Collapser.collapseList(phoneList, mContext);
+ if (phoneList.size() == 0) {
+ interactionError(InteractionErrorCode.CONTACT_HAS_NO_NUMBER);
+ } else if (phoneList.size() == 1) {
+ PhoneItem item = phoneList.get(0);
+ performAction(item.phoneNumber);
+ } else {
+ // There are multiple candidates. Let the user choose one.
+ showDisambiguationDialog(phoneList);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private void interactionError(@InteractionErrorCode int interactionErrorCode) {
+ // mContext is really the activity -- see ctor docs.
+ ((InteractionErrorListener) mContext).interactionError(interactionErrorCode);
+ }
+
+ private boolean isSafeToCommitTransactions() {
+ return !(mContext instanceof TransactionSafeActivity)
+ || ((TransactionSafeActivity) mContext).isSafeToCommitTransactions();
+ }
+
+ @VisibleForTesting
+ /* package */ CursorLoader getLoader() {
+ return mLoader;
+ }
+
+ private void showDisambiguationDialog(ArrayList<PhoneItem> phoneList) {
+ final Activity activity = (Activity) mContext;
+ if (activity.isDestroyed()) {
+ // Check whether the activity is still running
+ return;
+ }
+ try {
+ PhoneDisambiguationDialogFragment.show(
+ activity.getFragmentManager(),
+ phoneList,
+ mInteractionType,
+ mIsVideoCall,
+ mCallSpecificAppData);
+ } catch (IllegalStateException e) {
+ // ignore to be safe. Shouldn't happen because we checked the
+ // activity wasn't destroyed, but to be safe.
+ }
+ }
+
+ /** A model object for capturing a phone number for a given contact. */
+ @VisibleForTesting
+ /* package */ static class PhoneItem implements Parcelable, Collapsible<PhoneItem> {
+
+ public static final Parcelable.Creator<PhoneItem> CREATOR =
+ new Parcelable.Creator<PhoneItem>() {
+ @Override
+ public PhoneItem createFromParcel(Parcel in) {
+ return new PhoneItem(in);
+ }
+
+ @Override
+ public PhoneItem[] newArray(int size) {
+ return new PhoneItem[size];
+ }
+ };
+ long id;
+ String phoneNumber;
+ String accountType;
+ String dataSet;
+ long type;
+ String label;
+ /** {@link Phone#CONTENT_ITEM_TYPE} or {@link SipAddress#CONTENT_ITEM_TYPE}. */
+ String mimeType;
+
+ private PhoneItem() {}
+
+ private PhoneItem(Parcel in) {
+ this.id = in.readLong();
+ this.phoneNumber = in.readString();
+ this.accountType = in.readString();
+ this.dataSet = in.readString();
+ this.type = in.readLong();
+ this.label = in.readString();
+ this.mimeType = in.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(id);
+ dest.writeString(phoneNumber);
+ dest.writeString(accountType);
+ dest.writeString(dataSet);
+ dest.writeLong(type);
+ dest.writeString(label);
+ dest.writeString(mimeType);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void collapseWith(PhoneItem phoneItem) {
+ // Just keep the number and id we already have.
+ }
+
+ @Override
+ public boolean shouldCollapseWith(PhoneItem phoneItem, Context context) {
+ return MoreContactUtils.shouldCollapse(
+ Phone.CONTENT_ITEM_TYPE, phoneNumber, Phone.CONTENT_ITEM_TYPE, phoneItem.phoneNumber);
+ }
+
+ @Override
+ public String toString() {
+ return phoneNumber;
+ }
+ }
+
+ /** A list adapter that populates the list of contact's phone numbers. */
+ private static class PhoneItemAdapter extends ArrayAdapter<PhoneItem> {
+
+ private final int mInteractionType;
+
+ PhoneItemAdapter(Context context, List<PhoneItem> list, int interactionType) {
+ super(context, R.layout.phone_disambig_item, android.R.id.text2, list);
+ mInteractionType = interactionType;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final View view = super.getView(position, convertView, parent);
+
+ final PhoneItem item = getItem(position);
+ Assert.isNotNull(item, "Null item at position: %d", position);
+ final TextView typeView = (TextView) view.findViewById(android.R.id.text1);
+ CharSequence value =
+ ContactDisplayUtils.getLabelForCallOrSms(
+ (int) item.type, item.label, mInteractionType, getContext());
+
+ typeView.setText(value);
+ return view;
+ }
+ }
+
+ /**
+ * {@link DialogFragment} used for displaying a dialog with a list of phone numbers of which one
+ * will be chosen to make a call or initiate an sms message.
+ *
+ * <p>It is recommended to use {@link #startInteractionForPhoneCall(TransactionSafeActivity, Uri,
+ * boolean, int)} instead of directly using this class, as those methods handle one or multiple
+ * data cases appropriately.
+ *
+ * <p>This fragment may only be attached to activities which implement {@link
+ * DisambigDialogDismissedListener}.
+ */
+ @SuppressWarnings("WeakerAccess") // Made public to let the system reach this class
+ public static class PhoneDisambiguationDialogFragment extends DialogFragment
+ implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
+
+ private static final String ARG_PHONE_LIST = "phoneList";
+ private static final String ARG_INTERACTION_TYPE = "interactionType";
+ private static final String ARG_IS_VIDEO_CALL = "is_video_call";
+
+ private int mInteractionType;
+ private ListAdapter mPhonesAdapter;
+ private List<PhoneItem> mPhoneList;
+ private CallSpecificAppData mCallSpecificAppData;
+ private boolean mIsVideoCall;
+
+ public PhoneDisambiguationDialogFragment() {
+ super();
+ }
+
+ public static void show(
+ FragmentManager fragmentManager,
+ ArrayList<PhoneItem> phoneList,
+ int interactionType,
+ boolean isVideoCall,
+ CallSpecificAppData callSpecificAppData) {
+ PhoneDisambiguationDialogFragment fragment = new PhoneDisambiguationDialogFragment();
+ Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(ARG_PHONE_LIST, phoneList);
+ bundle.putInt(ARG_INTERACTION_TYPE, interactionType);
+ bundle.putBoolean(ARG_IS_VIDEO_CALL, isVideoCall);
+ CallIntentParser.putCallSpecificAppData(bundle, callSpecificAppData);
+ fragment.setArguments(bundle);
+ fragment.show(fragmentManager, TAG);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Activity activity = getActivity();
+ Assert.checkState(activity instanceof DisambigDialogDismissedListener);
+
+ mPhoneList = getArguments().getParcelableArrayList(ARG_PHONE_LIST);
+ mInteractionType = getArguments().getInt(ARG_INTERACTION_TYPE);
+ mIsVideoCall = getArguments().getBoolean(ARG_IS_VIDEO_CALL);
+ mCallSpecificAppData = CallIntentParser.getCallSpecificAppData(getArguments());
+
+ mPhonesAdapter = new PhoneItemAdapter(activity, mPhoneList, mInteractionType);
+ final LayoutInflater inflater = activity.getLayoutInflater();
+ @SuppressLint("InflateParams") // Allowed since dialog view is not available yet
+ final View setPrimaryView = inflater.inflate(R.layout.set_primary_checkbox, null);
+ return new AlertDialog.Builder(activity)
+ .setAdapter(mPhonesAdapter, this)
+ .setTitle(
+ mInteractionType == ContactDisplayUtils.INTERACTION_SMS
+ ? R.string.sms_disambig_title
+ : R.string.call_disambig_title)
+ .setView(setPrimaryView)
+ .create();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+ final AlertDialog alertDialog = (AlertDialog) dialog;
+ if (mPhoneList.size() > which && which >= 0) {
+ final PhoneItem phoneItem = mPhoneList.get(which);
+ final CheckBox checkBox = (CheckBox) alertDialog.findViewById(R.id.setPrimary);
+ if (checkBox.isChecked()) {
+ // Request to mark the data as primary in the background.
+ final Intent serviceIntent =
+ ContactUpdateService.createSetSuperPrimaryIntent(activity, phoneItem.id);
+ activity.startService(serviceIntent);
+ }
+
+ PhoneNumberInteraction.performAction(
+ activity, phoneItem.phoneNumber, mInteractionType, mIsVideoCall, mCallSpecificAppData);
+ } else {
+ dialog.dismiss();
+ }
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialogInterface) {
+ super.onDismiss(dialogInterface);
+ Activity activity = getActivity();
+ if (activity != null) {
+ ((DisambigDialogDismissedListener) activity).onDisambigDialogDismissed();
+ }
+ }
+ }
+}
diff --git a/java/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java b/java/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java
new file mode 100644
index 000000000..68b011a04
--- /dev/null
+++ b/java/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2013 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.dialer.interactions;
+
+import static android.Manifest.permission.READ_CONTACTS;
+import static android.Manifest.permission.WRITE_CONTACTS;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.PinnedPositions;
+import android.text.TextUtils;
+import com.android.dialer.util.PermissionsUtil;
+
+/**
+ * This broadcast receiver is used to listen to outgoing calls and undemote formerly demoted
+ * contacts if a phone call is made to a phone number belonging to that contact.
+ *
+ * <p>NOTE This doesn't work for corp contacts.
+ */
+public class UndemoteOutgoingCallReceiver extends BroadcastReceiver {
+
+ private static final long NO_CONTACT_FOUND = -1;
+
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ if (!PermissionsUtil.hasPermission(context, READ_CONTACTS)
+ || !PermissionsUtil.hasPermission(context, WRITE_CONTACTS)) {
+ return;
+ }
+ if (intent != null && Intent.ACTION_NEW_OUTGOING_CALL.equals(intent.getAction())) {
+ final String number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
+ if (TextUtils.isEmpty(number)) {
+ return;
+ }
+ new Thread() {
+ @Override
+ public void run() {
+ final long id = getContactIdFromPhoneNumber(context, number);
+ if (id != NO_CONTACT_FOUND) {
+ undemoteContactWithId(context, id);
+ }
+ }
+ }.start();
+ }
+ }
+
+ private void undemoteContactWithId(Context context, long id) {
+ // If the contact is not demoted, this will not do anything. Otherwise, it will
+ // restore it to an unpinned position. If it was a frequently called contact, it will
+ // show up once again show up on the favorites screen.
+ if (PermissionsUtil.hasPermission(context, WRITE_CONTACTS)) {
+ try {
+ PinnedPositions.undemote(context.getContentResolver(), id);
+ } catch (SecurityException e) {
+ // Just in case
+ }
+ }
+ }
+
+ private long getContactIdFromPhoneNumber(Context context, String number) {
+ if (!PermissionsUtil.hasPermission(context, READ_CONTACTS)) {
+ return NO_CONTACT_FOUND;
+ }
+ final Uri contactUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
+ final Cursor cursor;
+ try {
+ cursor =
+ context
+ .getContentResolver()
+ .query(contactUri, new String[] {PhoneLookup._ID}, null, null, null);
+ } catch (SecurityException e) {
+ // Just in case
+ return NO_CONTACT_FOUND;
+ }
+ if (cursor == null) {
+ return NO_CONTACT_FOUND;
+ }
+ try {
+ if (cursor.moveToFirst()) {
+ final long id = cursor.getLong(0);
+ return id;
+ } else {
+ return NO_CONTACT_FOUND;
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+}
diff --git a/java/com/android/dialer/interactions/res/layout/phone_disambig_item.xml b/java/com/android/dialer/interactions/res/layout/phone_disambig_item.xml
new file mode 100644
index 000000000..879ea0e96
--- /dev/null
+++ b/java/com/android/dialer/interactions/res/layout/phone_disambig_item.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.contacts.common.widget.ActivityTouchLinearLayout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="30dip"
+ android:paddingEnd="30dip"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@android:id/text1"
+ android:textStyle="bold"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+ <!-- Phone number should be displayed ltr -->
+ <TextView
+ android:id="@android:id/text2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-4dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textDirection="ltr"/>
+
+</view>
diff --git a/java/com/android/dialer/interactions/res/layout/set_primary_checkbox.xml b/java/com/android/dialer/interactions/res/layout/set_primary_checkbox.xml
new file mode 100644
index 000000000..62ef4b76f
--- /dev/null
+++ b/java/com/android/dialer/interactions/res/layout/set_primary_checkbox.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="14dip"
+ android:paddingEnd="15dip"
+ android:orientation="vertical">
+
+ <CheckBox
+ android:id="@+id/setPrimary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="true"
+ android:focusable="true"
+ android:text="@string/make_primary"/>
+</LinearLayout>
diff --git a/java/com/android/dialer/interactions/res/values/strings.xml b/java/com/android/dialer/interactions/res/values/strings.xml
new file mode 100644
index 000000000..eea8795b5
--- /dev/null
+++ b/java/com/android/dialer/interactions/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+
+ <!-- Title for the sms disambiguation dialog -->
+ <string name="sms_disambig_title">Choose number</string>
+
+ <!-- Title for the call disambiguation dialog -->
+ <string name="call_disambig_title">Choose number</string>
+
+ <!-- Message next to disambiguation dialog check box -->
+ <string name="make_primary">Remember this choice</string>
+
+</resources>
diff --git a/java/com/android/dialer/logging/Logger.java b/java/com/android/dialer/logging/Logger.java
new file mode 100644
index 000000000..207c35d9c
--- /dev/null
+++ b/java/com/android/dialer/logging/Logger.java
@@ -0,0 +1,49 @@
+/*
+ * 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.dialer.logging;
+
+import android.content.Context;
+import java.util.Objects;
+
+/** Single entry point for all logging/analytics-related work for all user interactions. */
+public class Logger {
+
+ private static LoggingBindings loggingBindings;
+
+ private Logger() {}
+
+ public static LoggingBindings get(Context context) {
+ Objects.requireNonNull(context);
+ if (loggingBindings != null) {
+ return loggingBindings;
+ }
+
+ Context application = context.getApplicationContext();
+ if (application instanceof LoggingBindingsFactory) {
+ loggingBindings = ((LoggingBindingsFactory) application).newLoggingBindings();
+ }
+
+ if (loggingBindings == null) {
+ loggingBindings = new LoggingBindingsStub();
+ }
+ return loggingBindings;
+ }
+
+ public static void setForTesting(LoggingBindings loggingBindings) {
+ Logger.loggingBindings = loggingBindings;
+ }
+}
diff --git a/java/com/android/dialer/logging/LoggingBindings.java b/java/com/android/dialer/logging/LoggingBindings.java
new file mode 100644
index 000000000..cf921c3fa
--- /dev/null
+++ b/java/com/android/dialer/logging/LoggingBindings.java
@@ -0,0 +1,59 @@
+/*
+ * 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.dialer.logging;
+
+import android.app.Activity;
+
+/** Allows the container application to gather analytics. */
+public interface LoggingBindings {
+
+ /**
+ * Logs an impression for a general dialer event that's not associated with a specific call.
+ *
+ * @param dialerImpression an integer representing what event occurred.
+ * @see com.android.dialer.logging.nano.DialerImpression
+ */
+ void logImpression(int dialerImpression);
+
+ /**
+ * Logs an impression for a general dialer event that's associated with a specific call.
+ *
+ * @param dialerImpression an integer representing what event occurred.
+ * @param callId unique ID of the call.
+ * @param callStartTimeMillis the absolute time when the call started.
+ * @see com.android.dialer.logging.nano.DialerImpression
+ */
+ void logCallImpression(int dialerImpression, String callId, long callStartTimeMillis);
+
+ /**
+ * Logs an interaction that occurred.
+ *
+ * @param interaction an integer representing what interaction occurred.
+ * @see com.android.dialer.logging.nano.InteractionEvent
+ */
+ void logInteraction(int interaction);
+
+ /**
+ * Logs an event indicating that a screen was displayed.
+ *
+ * @param screenEvent an integer representing the displayed screen.
+ * @param activity Parent activity of the displayed screen.
+ * @see com.android.dialer.logging.nano.ScreenEvent
+ */
+ void logScreenView(int screenEvent, Activity activity);
+
+ /** Logs a hit event to the analytics server. */
+ void sendHitEventAnalytics(String category, String action, String label, long value);
+}
diff --git a/java/com/android/dialer/logging/LoggingBindingsFactory.java b/java/com/android/dialer/logging/LoggingBindingsFactory.java
new file mode 100644
index 000000000..0722cf453
--- /dev/null
+++ b/java/com/android/dialer/logging/LoggingBindingsFactory.java
@@ -0,0 +1,24 @@
+/*
+ * 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.dialer.logging;
+
+/**
+ * This interface should be implementated by the Application subclass. It allows this module to get
+ * references to the LoggingBindings.
+ */
+public interface LoggingBindingsFactory {
+
+ LoggingBindings newLoggingBindings();
+}
diff --git a/java/com/android/dialer/logging/LoggingBindingsStub.java b/java/com/android/dialer/logging/LoggingBindingsStub.java
new file mode 100644
index 000000000..89c56eb91
--- /dev/null
+++ b/java/com/android/dialer/logging/LoggingBindingsStub.java
@@ -0,0 +1,36 @@
+/*
+ * 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.dialer.logging;
+
+import android.app.Activity;
+
+/** Default implementation for logging bindings. */
+public class LoggingBindingsStub implements LoggingBindings {
+
+ @Override
+ public void logImpression(int dialerImpression) {}
+
+ @Override
+ public void logCallImpression(int dialerImpression, String callId, long callStartTimeMillis) {}
+
+ @Override
+ public void logInteraction(int interaction) {}
+
+ @Override
+ public void logScreenView(int screenEvent, Activity activity) {}
+
+ @Override
+ public void sendHitEventAnalytics(String category, String action, String label, long value) {}
+}
diff --git a/java/com/android/dialer/logging/nano/ContactLookupResult.java b/java/com/android/dialer/logging/nano/ContactLookupResult.java
new file mode 100644
index 000000000..8960560fb
--- /dev/null
+++ b/java/com/android/dialer/logging/nano/ContactLookupResult.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 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
+ */
+
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+
+package com.android.dialer.logging.nano;
+
+@SuppressWarnings("hiding")
+public final class ContactLookupResult extends
+ com.google.protobuf.nano.ExtendableMessageNano<ContactLookupResult> {
+
+ // enum Type
+ public interface Type {
+ public static final int UNKNOWN_LOOKUP_RESULT_TYPE = 0;
+ public static final int NOT_FOUND = 1;
+ public static final int LOCAL_CONTACT = 2;
+ public static final int LOCAL_CACHE = 3;
+ public static final int REMOTE = 4;
+ public static final int EMERGENCY = 5;
+ public static final int VOICEMAIL = 6;
+ }
+
+ private static volatile ContactLookupResult[] _emptyArray;
+ public static ContactLookupResult[] emptyArray() {
+ // Lazily initializes the empty array
+ if (_emptyArray == null) {
+ synchronized (
+ com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
+ if (_emptyArray == null) {
+ _emptyArray = new ContactLookupResult[0];
+ }
+ }
+ }
+ return _emptyArray;
+ }
+
+ // @@protoc_insertion_point(class_scope:com.android.dialer.logging.ContactLookupResult)
+
+ public ContactLookupResult() {
+ clear();
+ }
+
+ public ContactLookupResult clear() {
+ unknownFieldData = null;
+ cachedSize = -1;
+ return this;
+ }
+
+ @Override
+ public ContactLookupResult mergeFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ while (true) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ return this;
+ default: {
+ if (!super.storeUnknownField(input, tag)) {
+ return this;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public static ContactLookupResult parseFrom(byte[] data)
+ throws com.google.protobuf.nano.InvalidProtocolBufferNanoException {
+ return com.google.protobuf.nano.MessageNano.mergeFrom(new ContactLookupResult(), data);
+ }
+
+ public static ContactLookupResult parseFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ return new ContactLookupResult().mergeFrom(input);
+ }
+}
diff --git a/java/com/android/dialer/logging/nano/ContactSource.java b/java/com/android/dialer/logging/nano/ContactSource.java
new file mode 100644
index 000000000..35d8b8ca1
--- /dev/null
+++ b/java/com/android/dialer/logging/nano/ContactSource.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 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
+ */
+
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+
+package com.android.dialer.logging.nano;
+
+@SuppressWarnings("hiding")
+public final class ContactSource extends
+ com.google.protobuf.nano.ExtendableMessageNano<ContactSource> {
+
+ // enum Type
+ public interface Type {
+ public static final int UNKNOWN_SOURCE_TYPE = 0;
+ public static final int SOURCE_TYPE_DIRECTORY = 1;
+ public static final int SOURCE_TYPE_EXTENDED = 2;
+ public static final int SOURCE_TYPE_PLACES = 3;
+ public static final int SOURCE_TYPE_PROFILE = 4;
+ public static final int SOURCE_TYPE_CNAP = 5;
+ }
+
+ private static volatile ContactSource[] _emptyArray;
+ public static ContactSource[] emptyArray() {
+ // Lazily initializes the empty array
+ if (_emptyArray == null) {
+ synchronized (
+ com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
+ if (_emptyArray == null) {
+ _emptyArray = new ContactSource[0];
+ }
+ }
+ }
+ return _emptyArray;
+ }
+
+ // @@protoc_insertion_point(class_scope:com.android.dialer.logging.ContactSource)
+
+ public ContactSource() {
+ clear();
+ }
+
+ public ContactSource clear() {
+ unknownFieldData = null;
+ cachedSize = -1;
+ return this;
+ }
+
+ @Override
+ public ContactSource mergeFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ while (true) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ return this;
+ default: {
+ if (!super.storeUnknownField(input, tag)) {
+ return this;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public static ContactSource parseFrom(byte[] data)
+ throws com.google.protobuf.nano.InvalidProtocolBufferNanoException {
+ return com.google.protobuf.nano.MessageNano.mergeFrom(new ContactSource(), data);
+ }
+
+ public static ContactSource parseFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ return new ContactSource().mergeFrom(input);
+ }
+}
diff --git a/java/com/android/dialer/logging/nano/DialerImpression.java b/java/com/android/dialer/logging/nano/DialerImpression.java
new file mode 100644
index 000000000..6bb56751f
--- /dev/null
+++ b/java/com/android/dialer/logging/nano/DialerImpression.java
@@ -0,0 +1,178 @@
+/*
+ * 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.dialer.logging.nano;
+
+@SuppressWarnings("hiding")
+public final class DialerImpression extends
+ com.google.protobuf.nano.ExtendableMessageNano<DialerImpression> {
+
+ // enum Type
+ public interface Type {
+ public static final int UNKNOWN_AOSP_EVENT_TYPE = 1000;
+ public static final int APP_LAUNCHED = 1001;
+ public static final int IN_CALL_SCREEN_TURN_ON_SPEAKERPHONE = 1002;
+ public static final int IN_CALL_SCREEN_TURN_ON_WIRED_OR_EARPIECE = 1003;
+ public static final int CALL_LOG_BLOCK_REPORT_SPAM = 1004;
+ public static final int CALL_LOG_BLOCK_NUMBER = 1005;
+ public static final int CALL_LOG_UNBLOCK_NUMBER = 1006;
+ public static final int CALL_LOG_REPORT_AS_NOT_SPAM = 1007;
+ public static final int DIALOG_ACTION_CONFIRM_NUMBER_NOT_SPAM = 1008;
+ public static final int REPORT_AS_NOT_SPAM_VIA_UNBLOCK_NUMBER = 1009;
+ public static final int DIALOG_ACTION_CONFIRM_NUMBER_SPAM_INDIRECTLY_VIA_BLOCK_NUMBER = 1010;
+ public static final int REPORT_CALL_AS_SPAM_VIA_CALL_LOG_BLOCK_REPORT_SPAM_SENT_VIA_BLOCK_NUMBER_DIALOG = 1011;
+ public static final int USER_ACTION_BLOCKED_NUMBER = 1012;
+ public static final int USER_ACTION_UNBLOCKED_NUMBER = 1013;
+ public static final int SPAM_AFTER_CALL_NOTIFICATION_BLOCK_NUMBER = 1014;
+ public static final int SPAM_AFTER_CALL_NOTIFICATION_SHOW_SPAM_DIALOG = 1015;
+ public static final int SPAM_AFTER_CALL_NOTIFICATION_SHOW_NON_SPAM_DIALOG = 1016;
+ public static final int SPAM_AFTER_CALL_NOTIFICATION_ADD_TO_CONTACTS = 1019;
+ public static final int SPAM_AFTER_CALL_NOTIFICATION_MARKED_NUMBER_AS_SPAM = 1020;
+ public static final int SPAM_AFTER_CALL_NOTIFICATION_MARKED_NUMBER_AS_NOT_SPAM_AND_BLOCKED = 1021;
+ public static final int SPAM_AFTER_CALL_NOTIFICATION_REPORT_NUMBER_AS_NOT_SPAM = 1022;
+ public static final int SPAM_AFTER_CALL_NOTIFICATION_ON_DISMISS_SPAM_DIALOG = 1024;
+ public static final int SPAM_AFTER_CALL_NOTIFICATION_ON_DISMISS_NON_SPAM_DIALOG = 1025;
+ public static final int SPAM_NOTIFICATION_SERVICE_ACTION_MARK_NUMBER_AS_SPAM = 1026;
+ public static final int SPAM_NOTIFICATION_SERVICE_ACTION_MARK_NUMBER_AS_NOT_SPAM = 1027;
+ public static final int USER_PARTICIPATED_IN_A_CALL = 1028;
+ public static final int INCOMING_SPAM_CALL = 1029;
+ public static final int INCOMING_NON_SPAM_CALL = 1030;
+ public static final int SPAM_NOTIFICATION_SHOWN_AFTER_THROTTLE = 1041;
+ public static final int SPAM_NOTIFICATION_NOT_SHOWN_AFTER_THROTTLE = 1042;
+ public static final int NON_SPAM_NOTIFICATION_SHOWN_AFTER_THROTTLE = 1043;
+ public static final int NON_SPAM_NOTIFICATION_NOT_SHOWN_AFTER_THROTTLE = 1044;
+ public static final int VOICEMAIL_ALERT_SET_PIN_SHOWN = 1045;
+ public static final int VOICEMAIL_ALERT_SET_PIN_CLICKED = 1046;
+ public static final int USER_DID_NOT_PARTICIPATE_IN_CALL = 1047;
+ public static final int USER_DELETED_CALL_LOG_ITEM = 1048;
+ public static final int CALL_LOG_SEND_MESSAGE = 1049;
+ public static final int CALL_LOG_ADD_TO_CONTACT = 1050;
+ public static final int CALL_LOG_CREATE_NEW_CONTACT = 1051;
+ public static final int VOICEMAIL_DELETE_ENTRY = 1052;
+ public static final int VOICEMAIL_EXPAND_ENTRY = 1053;
+ public static final int VOICEMAIL_PLAY_AUDIO_DIRECTLY = 1054;
+ public static final int VOICEMAIL_PLAY_AUDIO_AFTER_EXPANDING_ENTRY = 1055;
+ public static final int REJECT_INCOMING_CALL_FROM_NOTIFICATION = 1056;
+ public static final int REJECT_INCOMING_CALL_FROM_ANSWER_SCREEN = 1057;
+ public static final int CALL_LOG_CONTEXT_MENU_BLOCK_REPORT_SPAM = 1058;
+ public static final int CALL_LOG_CONTEXT_MENU_BLOCK_NUMBER = 1059;
+ public static final int CALL_LOG_CONTEXT_MENU_UNBLOCK_NUMBER = 1060;
+ public static final int CALL_LOG_CONTEXT_MENU_REPORT_AS_NOT_SPAM = 1061;
+ public static final int NEW_CONTACT_OVERFLOW = 1062;
+ public static final int NEW_CONTACT_FAB = 1063;
+ public static final int VOICEMAIL_VVM3_TOS_SHOWN = 1064;
+ public static final int VOICEMAIL_VVM3_TOS_ACCEPTED = 1065;
+ public static final int VOICEMAIL_VVM3_TOS_DECLINED = 1066;
+ public static final int VOICEMAIL_VVM3_TOS_DECLINE_CLICKED = 1067;
+ public static final int VOICEMAIL_VVM3_TOS_DECLINE_CHANGE_PIN_SHOWN = 1068;
+ public static final int STORAGE_PERMISSION_DISPLAYED = 1069;
+ public static final int CAMERA_PERMISSION_DISPLAYED = 1074;
+ public static final int STORAGE_PERMISSION_REQUESTED = 1070;
+ public static final int CAMERA_PERMISSION_REQUESTED = 1075;
+ public static final int STORAGE_PERMISSION_SETTINGS = 1071;
+ public static final int CAMERA_PERMISSION_SETTINGS = 1076;
+ public static final int STORAGE_PERMISSION_GRANTED = 1072;
+ public static final int CAMERA_PERMISSION_GRANTED = 1077;
+ public static final int STORAGE_PERMISSION_DENIED = 1073;
+ public static final int CAMERA_PERMISSION_DENIED = 1078;
+ public static final int VOICEMAIL_CONFIGURATION_STATE_CORRUPTION_DETECTED_FROM_ACTIVITY = 1079;
+ public static final int VOICEMAIL_CONFIGURATION_STATE_CORRUPTION_DETECTED_FROM_NOTIFICATION = 1080;
+ public static final int BACKUP_ON_BACKUP = 1081;
+ public static final int BACKUP_ON_FULL_BACKUP = 1082;
+ public static final int BACKUP_ON_BACKUP_DISABLED = 1083;
+ public static final int BACKUP_VOICEMAIL_BACKED_UP = 1084;
+ public static final int BACKUP_FULL_BACKED_UP = 1085;
+ public static final int BACKUP_ON_BACKUP_JSON_EXCEPTION = 1086;
+ public static final int BACKUP_ON_QUOTA_EXCEEDED = 1087;
+ public static final int BACKUP_ON_RESTORE = 1088;
+ public static final int BACKUP_RESTORED_FILE = 1089;
+ public static final int BACKUP_RESTORED_VOICEMAIL = 1090;
+ public static final int BACKUP_ON_RESTORE_FINISHED = 1091;
+ public static final int BACKUP_ON_RESTORE_DISABLED = 1092;
+ public static final int BACKUP_ON_RESTORE_JSON_EXCEPTION = 1093;
+ public static final int BACKUP_ON_RESTORE_IO_EXCEPTION = 1094;
+ public static final int BACKUP_MAX_VM_BACKUP_REACHED = 1095;
+ public static final int EVENT_ANSWER_HINT_ACTIVATED = 1096;
+ public static final int EVENT_ANSWER_HINT_DEACTIVATED = 1097;
+ public static final int VVM_TAB_VISIBLE = 1098;
+ public static final int VVM_SHARE_VISIBLE = 1099;
+ public static final int VVM_SHARE_PRESSED = 1100;
+ public static final int OUTGOING_VIDEO_CALL = 1101;
+ public static final int INCOMING_VIDEO_CALL = 1102;
+ public static final int USER_PARTICIPATED_IN_A_VIDEO_CALL = 1103;
+ public static final int BACKUP_ON_RESTORE_VM_DUPLICATE_NOT_RESTORING = 1104;
+ public static final int CALL_LOG_SHARE_AND_CALL = 1105;
+ public static final int CALL_COMPOSER_ACTIVITY_PLACE_RCS_CALL = 1106;
+ public static final int CALL_COMPOSER_ACTIVITY_SEND_AND_CALL_PRESSED_WHEN_SESSION_NOT_READY = 1107;
+ }
+
+ private static volatile DialerImpression[] _emptyArray;
+ public static DialerImpression[] emptyArray() {
+ // Lazily initializes the empty array
+ if (_emptyArray == null) {
+ synchronized (
+ com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
+ if (_emptyArray == null) {
+ _emptyArray = new DialerImpression[0];
+ }
+ }
+ }
+ return _emptyArray;
+ }
+
+ // @@protoc_insertion_point(class_scope:com.android.dialer.logging.DialerImpression)
+
+ public DialerImpression() {
+ clear();
+ }
+
+ public DialerImpression clear() {
+ unknownFieldData = null;
+ cachedSize = -1;
+ return this;
+ }
+
+ @Override
+ public DialerImpression mergeFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ while (true) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ return this;
+ default: {
+ if (!super.storeUnknownField(input, tag)) {
+ return this;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public static DialerImpression parseFrom(byte[] data)
+ throws com.google.protobuf.nano.InvalidProtocolBufferNanoException {
+ return com.google.protobuf.nano.MessageNano.mergeFrom(new DialerImpression(), data);
+ }
+
+ public static DialerImpression parseFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ return new DialerImpression().mergeFrom(input);
+ }
+}
+
diff --git a/java/com/android/dialer/logging/nano/InteractionEvent.java b/java/com/android/dialer/logging/nano/InteractionEvent.java
new file mode 100644
index 000000000..8d9430be9
--- /dev/null
+++ b/java/com/android/dialer/logging/nano/InteractionEvent.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+
+package com.android.dialer.logging.nano;
+
+/** This file is autogenerated, but javadoc required. */
+@SuppressWarnings("hiding")
+public final class InteractionEvent
+ extends com.google.protobuf.nano.ExtendableMessageNano<InteractionEvent> {
+
+ // enum Type
+ /** This file is autogenerated, but javadoc required. */
+ public interface Type {
+ public static final int UNKNOWN = 0;
+ public static final int CALL_BLOCKED = 15;
+ public static final int BLOCK_NUMBER_CALL_LOG = 16;
+ public static final int BLOCK_NUMBER_CALL_DETAIL = 17;
+ public static final int BLOCK_NUMBER_MANAGEMENT_SCREEN = 18;
+ public static final int UNBLOCK_NUMBER_CALL_LOG = 19;
+ public static final int UNBLOCK_NUMBER_CALL_DETAIL = 20;
+ public static final int UNBLOCK_NUMBER_MANAGEMENT_SCREEN = 21;
+ public static final int IMPORT_SEND_TO_VOICEMAIL = 22;
+ public static final int UNDO_BLOCK_NUMBER = 23;
+ public static final int UNDO_UNBLOCK_NUMBER = 24;
+ }
+
+ private static volatile InteractionEvent[] _emptyArray;
+ public static InteractionEvent[] emptyArray() {
+ // Lazily initializes the empty array
+ if (_emptyArray == null) {
+ synchronized (com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
+ if (_emptyArray == null) {
+ _emptyArray = new InteractionEvent[0];
+ }
+ }
+ }
+ return _emptyArray;
+ }
+
+ // @@protoc_insertion_point(class_scope:com.android.dialer.logging.InteractionEvent)
+
+ public InteractionEvent() {
+ clear();
+ }
+
+ public InteractionEvent clear() {
+ unknownFieldData = null;
+ cachedSize = -1;
+ return this;
+ }
+
+ @Override
+ public InteractionEvent mergeFrom(com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ while (true) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ return this;
+ default:
+ {
+ if (!super.storeUnknownField(input, tag)) {
+ return this;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public static InteractionEvent parseFrom(byte[] data)
+ throws com.google.protobuf.nano.InvalidProtocolBufferNanoException {
+ return com.google.protobuf.nano.MessageNano.mergeFrom(new InteractionEvent(), data);
+ }
+
+ public static InteractionEvent parseFrom(com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ return new InteractionEvent().mergeFrom(input);
+ }
+}
diff --git a/java/com/android/dialer/logging/nano/ReportingLocation.java b/java/com/android/dialer/logging/nano/ReportingLocation.java
new file mode 100644
index 000000000..1f05ce414
--- /dev/null
+++ b/java/com/android/dialer/logging/nano/ReportingLocation.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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
+ */
+
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+
+package com.android.dialer.logging.nano;
+
+@SuppressWarnings("hiding")
+public final class ReportingLocation extends
+ com.google.protobuf.nano.ExtendableMessageNano<ReportingLocation> {
+
+ // enum Type
+ public interface Type {
+ public static final int UNKNOWN_REPORTING_LOCATION = 0;
+ public static final int CALL_LOG_HISTORY = 1;
+ public static final int FEEDBACK_PROMPT = 2;
+ }
+
+ private static volatile ReportingLocation[] _emptyArray;
+ public static ReportingLocation[] emptyArray() {
+ // Lazily initializes the empty array
+ if (_emptyArray == null) {
+ synchronized (
+ com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
+ if (_emptyArray == null) {
+ _emptyArray = new ReportingLocation[0];
+ }
+ }
+ }
+ return _emptyArray;
+ }
+
+ // @@protoc_insertion_point(class_scope:com.android.dialer.logging.ReportingLocation)
+
+ public ReportingLocation() {
+ clear();
+ }
+
+ public ReportingLocation clear() {
+ unknownFieldData = null;
+ cachedSize = -1;
+ return this;
+ }
+
+ @Override
+ public ReportingLocation mergeFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ while (true) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ return this;
+ default: {
+ if (!super.storeUnknownField(input, tag)) {
+ return this;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public static ReportingLocation parseFrom(byte[] data)
+ throws com.google.protobuf.nano.InvalidProtocolBufferNanoException {
+ return com.google.protobuf.nano.MessageNano.mergeFrom(new ReportingLocation(), data);
+ }
+
+ public static ReportingLocation parseFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ return new ReportingLocation().mergeFrom(input);
+ }
+}
diff --git a/java/com/android/dialer/logging/nano/ScreenEvent.java b/java/com/android/dialer/logging/nano/ScreenEvent.java
new file mode 100644
index 000000000..be4e5eb9e
--- /dev/null
+++ b/java/com/android/dialer/logging/nano/ScreenEvent.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.
+ */
+
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+
+package com.android.dialer.logging.nano;
+
+/** This file is autogenerated, but javadoc required. */
+@SuppressWarnings("hiding")
+public final class ScreenEvent extends com.google.protobuf.nano.ExtendableMessageNano<ScreenEvent> {
+
+ // enum Type
+ /** This file is autogenerated, but javadoc required. */
+ public interface Type {
+ public static final int UNKNOWN = 0;
+ public static final int DIALPAD = 1;
+ public static final int SPEED_DIAL = 2;
+ public static final int CALL_LOG = 3;
+ public static final int VOICEMAIL_LOG = 4;
+ public static final int ALL_CONTACTS = 5;
+ public static final int REGULAR_SEARCH = 6;
+ public static final int SMART_DIAL_SEARCH = 7;
+ public static final int CALL_LOG_FILTER = 8;
+ public static final int SETTINGS = 9;
+ public static final int IMPORT_EXPORT_CONTACTS = 10;
+ public static final int CLEAR_FREQUENTS = 11;
+ public static final int SEND_FEEDBACK = 12;
+ public static final int INCALL = 13;
+ public static final int INCOMING_CALL = 14;
+ public static final int CONFERENCE_MANAGEMENT = 15;
+ public static final int INCALL_DIALPAD = 16;
+ public static final int CALL_LOG_CONTEXT_MENU = 17;
+ public static final int BLOCKED_NUMBER_MANAGEMENT = 18;
+ public static final int BLOCKED_NUMBER_ADD_NUMBER = 19;
+ public static final int CALL_DETAILS = 20;
+ }
+
+ private static volatile ScreenEvent[] _emptyArray;
+ public static ScreenEvent[] emptyArray() {
+ // Lazily initializes the empty array
+ if (_emptyArray == null) {
+ synchronized (com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
+ if (_emptyArray == null) {
+ _emptyArray = new ScreenEvent[0];
+ }
+ }
+ }
+ return _emptyArray;
+ }
+
+ // @@protoc_insertion_point(class_scope:com.android.dialer.logging.ScreenEvent)
+
+ public ScreenEvent() {
+ clear();
+ }
+
+ public ScreenEvent clear() {
+ unknownFieldData = null;
+ cachedSize = -1;
+ return this;
+ }
+
+ @Override
+ public ScreenEvent mergeFrom(com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ while (true) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ return this;
+ default:
+ {
+ if (!super.storeUnknownField(input, tag)) {
+ return this;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public static ScreenEvent parseFrom(byte[] data)
+ throws com.google.protobuf.nano.InvalidProtocolBufferNanoException {
+ return com.google.protobuf.nano.MessageNano.mergeFrom(new ScreenEvent(), data);
+ }
+
+ public static ScreenEvent parseFrom(com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ return new ScreenEvent().mergeFrom(input);
+ }
+}
diff --git a/java/com/android/dialer/multimedia/AutoValue_MultimediaData.java b/java/com/android/dialer/multimedia/AutoValue_MultimediaData.java
new file mode 100644
index 000000000..cc6815094
--- /dev/null
+++ b/java/com/android/dialer/multimedia/AutoValue_MultimediaData.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 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.dialer.multimedia;
+
+import android.location.Location;
+import android.net.Uri;
+import android.support.annotation.Nullable;
+import javax.annotation.Generated;
+
+@Generated("com.google.auto.value.processor.AutoValueProcessor")
+ final class AutoValue_MultimediaData extends MultimediaData {
+
+ private final String subject;
+ private final Location location;
+ private final Uri imageUri;
+ private final String imageContentType;
+ private final boolean important;
+
+ private AutoValue_MultimediaData(
+ @Nullable String subject,
+ @Nullable Location location,
+ @Nullable Uri imageUri,
+ @Nullable String imageContentType,
+ boolean important) {
+ this.subject = subject;
+ this.location = location;
+ this.imageUri = imageUri;
+ this.imageContentType = imageContentType;
+ this.important = important;
+ }
+
+ @Nullable
+ @Override
+ public String getSubject() {
+ return subject;
+ }
+
+ @Nullable
+ @Override
+ public Location getLocation() {
+ return location;
+ }
+
+ @Nullable
+ @Override
+ public Uri getImageUri() {
+ return imageUri;
+ }
+
+ @Nullable
+ @Override
+ public String getImageContentType() {
+ return imageContentType;
+ }
+
+ @Override
+ public boolean isImportant() {
+ return important;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof MultimediaData) {
+ MultimediaData that = (MultimediaData) o;
+ return ((this.subject == null) ? (that.getSubject() == null) : this.subject.equals(that.getSubject()))
+ && ((this.location == null) ? (that.getLocation() == null) : this.location.equals(that.getLocation()))
+ && ((this.imageUri == null) ? (that.getImageUri() == null) : this.imageUri.equals(that.getImageUri()))
+ && ((this.imageContentType == null) ? (that.getImageContentType() == null) : this.imageContentType.equals(that.getImageContentType()))
+ && (this.important == that.isImportant());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 1;
+ h *= 1000003;
+ h ^= (subject == null) ? 0 : this.subject.hashCode();
+ h *= 1000003;
+ h ^= (location == null) ? 0 : this.location.hashCode();
+ h *= 1000003;
+ h ^= (imageUri == null) ? 0 : this.imageUri.hashCode();
+ h *= 1000003;
+ h ^= (imageContentType == null) ? 0 : this.imageContentType.hashCode();
+ h *= 1000003;
+ h ^= this.important ? 1231 : 1237;
+ return h;
+ }
+
+ static final class Builder extends MultimediaData.Builder {
+ private String subject;
+ private Location location;
+ private Uri imageUri;
+ private String imageContentType;
+ private Boolean important;
+ Builder() {
+ }
+ private Builder(MultimediaData source) {
+ this.subject = source.getSubject();
+ this.location = source.getLocation();
+ this.imageUri = source.getImageUri();
+ this.imageContentType = source.getImageContentType();
+ this.important = source.isImportant();
+ }
+ @Override
+ public MultimediaData.Builder setSubject(@Nullable String subject) {
+ this.subject = subject;
+ return this;
+ }
+ @Override
+ public MultimediaData.Builder setLocation(@Nullable Location location) {
+ this.location = location;
+ return this;
+ }
+ @Override
+ MultimediaData.Builder setImageUri(@Nullable Uri imageUri) {
+ this.imageUri = imageUri;
+ return this;
+ }
+ @Override
+ MultimediaData.Builder setImageContentType(@Nullable String imageContentType) {
+ this.imageContentType = imageContentType;
+ return this;
+ }
+ @Override
+ public MultimediaData.Builder setImportant(boolean important) {
+ this.important = important;
+ return this;
+ }
+ @Override
+ public MultimediaData build() {
+ String missing = "";
+ if (this.important == null) {
+ missing += " important";
+ }
+ if (!missing.isEmpty()) {
+ throw new IllegalStateException("Missing required properties:" + missing);
+ }
+ return new AutoValue_MultimediaData(
+ this.subject,
+ this.location,
+ this.imageUri,
+ this.imageContentType,
+ this.important);
+ }
+ }
+
+}
diff --git a/java/com/android/dialer/multimedia/MultimediaData.java b/java/com/android/dialer/multimedia/MultimediaData.java
new file mode 100644
index 000000000..ebd41a918
--- /dev/null
+++ b/java/com/android/dialer/multimedia/MultimediaData.java
@@ -0,0 +1,100 @@
+/*
+ * 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.dialer.multimedia;
+
+import android.location.Location;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import com.android.dialer.common.LogUtil;
+
+
+/** Holds the data associated with an enriched call session. */
+
+public abstract class MultimediaData {
+
+ public static final MultimediaData EMPTY = builder().build();
+
+ @NonNull
+ public static Builder builder() {
+ return new AutoValue_MultimediaData.Builder().setImportant(false);
+ }
+
+ /** Returns the call composer subject if set, or null if this isn't a call composer session. */
+ @Nullable
+ public abstract String getSubject();
+
+ /** Returns the call composer location if set, or null if this isn't a call composer session. */
+ @Nullable
+ public abstract Location getLocation();
+
+ /** Returns {@code true} if this session contains image data. */
+ public boolean hasImageData() {
+ // imageUri and content are always either both null or nonnull
+ return getImageUri() != null && getImageContentType() != null;
+ }
+
+ /** Returns the call composer photo if set, or null if this isn't a call composer session. */
+ @Nullable
+ public abstract Uri getImageUri();
+
+ /**
+ * Returns the content type of the image, either image/png or image/jpeg, if set, or null if this
+ * isn't a call composer session.
+ */
+ @Nullable
+ public abstract String getImageContentType();
+
+ /** Returns {@code true} if this is a call composer session that's marked as important. */
+ public abstract boolean isImportant();
+
+ /** Returns the string form of this MultimediaData with no PII. */
+ @Override
+ public String toString() {
+ return String.format(
+ "MultimediaData{subject: %s, location: %s, imageUrl: %s, imageContentType: %s, "
+ + "important: %b}",
+ LogUtil.sanitizePii(getSubject()),
+ LogUtil.sanitizePii(getLocation()),
+ LogUtil.sanitizePii(getImageUri()),
+ getImageContentType(),
+ isImportant());
+ }
+
+ /** Creates instances of {@link MultimediaData}. */
+
+ public abstract static class Builder {
+
+ public abstract Builder setSubject(@NonNull String subject);
+
+ public abstract Builder setLocation(@NonNull Location location);
+
+ public Builder setImage(@NonNull Uri image, @NonNull String imageContentType) {
+ setImageUri(image);
+ setImageContentType(imageContentType);
+ return this;
+ }
+
+ abstract Builder setImageUri(@NonNull Uri image);
+
+ abstract Builder setImageContentType(@NonNull String imageContentType);
+
+ public abstract Builder setImportant(boolean isImportant);
+
+ public abstract MultimediaData build();
+ }
+}
diff --git a/java/com/android/dialer/p13n/inference/P13nRanking.java b/java/com/android/dialer/p13n/inference/P13nRanking.java
new file mode 100644
index 000000000..6bfc0352a
--- /dev/null
+++ b/java/com/android/dialer/p13n/inference/P13nRanking.java
@@ -0,0 +1,75 @@
+/*
+ * 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.dialer.p13n.inference;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import com.android.dialer.common.Assert;
+import com.android.dialer.p13n.inference.protocol.P13nRanker;
+import com.android.dialer.p13n.inference.protocol.P13nRankerFactory;
+import java.util.List;
+
+/** Single entry point for all personalized ranking. */
+public final class P13nRanking {
+
+ private static P13nRanker ranker;
+
+ private P13nRanking() {}
+
+ @MainThread
+ @NonNull
+ public static P13nRanker get(@NonNull Context context) {
+ Assert.isNotNull(context);
+ Assert.isMainThread();
+ if (ranker != null) {
+ return ranker;
+ }
+
+ Context application = context.getApplicationContext();
+ if (application instanceof P13nRankerFactory) {
+ ranker = ((P13nRankerFactory) application).newP13nRanker();
+ }
+
+ if (ranker == null) {
+ ranker =
+ new P13nRanker() {
+ @Override
+ public void refresh(@Nullable P13nRefreshCompleteListener listener) {}
+
+ @Override
+ public List<String> rankList(List<String> phoneNumbers) {
+ return phoneNumbers;
+ }
+
+ @NonNull
+ @Override
+ public Cursor rankCursor(
+ @NonNull Cursor phoneQueryResults, int phoneNumberColumnIndex) {
+ return phoneQueryResults;
+ }
+ };
+ }
+ return ranker;
+ }
+
+ public static void setForTesting(@NonNull P13nRanker ranker) {
+ P13nRanking.ranker = ranker;
+ }
+}
diff --git a/java/com/android/dialer/p13n/inference/protocol/P13nRanker.java b/java/com/android/dialer/p13n/inference/protocol/P13nRanker.java
new file mode 100644
index 000000000..9a859a6db
--- /dev/null
+++ b/java/com/android/dialer/p13n/inference/protocol/P13nRanker.java
@@ -0,0 +1,75 @@
+/*
+ * 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.dialer.p13n.inference.protocol;
+
+import android.database.Cursor;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import java.util.List;
+
+/** Provides personalized ranking of outgoing call targets. */
+public interface P13nRanker {
+
+ /**
+ * Re-orders a list of phone numbers according to likelihood they will be the next outgoing call.
+ *
+ * @param phoneNumbers the list of candidate numbers to call (may be in contacts list or not)
+ */
+ @NonNull
+ @MainThread
+ List<String> rankList(@NonNull List<String> phoneNumbers);
+
+ /**
+ * Re-orders a retrieved contact list according to likelihood they will be the next outgoing call.
+ *
+ * <p>A new cursor with reordered data is returned; the input cursor is unmodified except for its
+ * position. If the order is unchanged, this method may return a reference to the unmodified input
+ * cursor directly. The order would be unchanged if the ranking cache is not yet ready, or if the
+ * input cursor is closed or invalid, or if any other error occurs in the ranking process.
+ *
+ * @param phoneQueryResults cursor of results of a Dialer search query
+ * @param phoneNumberColumnIndex column index of the phone number in the cursor data
+ * @return new cursor of data reordered by ranking (or reference to input cursor if order
+ * unchanged)
+ */
+ @NonNull
+ @MainThread
+ Cursor rankCursor(@NonNull Cursor phoneQueryResults, int phoneNumberColumnIndex);
+
+ /**
+ * Refreshes ranking cache (pulls fresh contextual features, pre-caches inference results, etc.).
+ *
+ * <p>Asynchronously runs in background as the process might take a few seconds, notifying a
+ * listener upon completion; meanwhile, any calls to {@link #rankList} will simply return the
+ * input in same order.
+ *
+ * @param listener callback for when ranking refresh has completed; null value skips notification.
+ */
+ @MainThread
+ void refresh(@Nullable P13nRefreshCompleteListener listener);
+
+ /**
+ * Callback class for when ranking refresh has completed.
+ *
+ * <p>Primary use is to notify {@link com.android.dialer.app.DialtactsActivity} that the ranking
+ * functions {@link #rankList} and {@link #rankCursor(Cursor, int)} will now give useful results.
+ */
+ interface P13nRefreshCompleteListener {
+
+ /** Callback for when ranking refresh has completed. */
+ void onP13nRefreshComplete();
+ }
+}
diff --git a/java/com/android/dialer/p13n/inference/protocol/P13nRankerFactory.java b/java/com/android/dialer/p13n/inference/protocol/P13nRankerFactory.java
new file mode 100644
index 000000000..7038cf456
--- /dev/null
+++ b/java/com/android/dialer/p13n/inference/protocol/P13nRankerFactory.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dialer.p13n.inference.protocol;
+
+import android.support.annotation.Nullable;
+
+/**
+ * This interface should be implemented by the Application subclass. It allows this module to get
+ * references to the {@link P13nRanker}.
+ */
+public interface P13nRankerFactory {
+ @Nullable
+ P13nRanker newP13nRanker();
+}
diff --git a/java/com/android/dialer/p13n/logging/P13nLogger.java b/java/com/android/dialer/p13n/logging/P13nLogger.java
new file mode 100644
index 000000000..069a29328
--- /dev/null
+++ b/java/com/android/dialer/p13n/logging/P13nLogger.java
@@ -0,0 +1,35 @@
+/*
+ * 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.dialer.p13n.logging;
+
+import com.android.contacts.common.list.PhoneNumberListAdapter;
+
+/** Allows logging of data for personalization. */
+public interface P13nLogger {
+
+ /**
+ * Logs a search query (text or digits) entered by user.
+ *
+ * @param query search text (or digits) entered by user
+ * @param adapter list adapter providing access to contacts matching search query
+ */
+ void onSearchQuery(String query, PhoneNumberListAdapter adapter);
+
+ /**
+ * Resets logging session (clears searches, re-initializes app entry timestamp, etc.) Should be
+ * called when Dialer app is resumed.
+ */
+ void reset();
+}
diff --git a/java/com/android/dialer/p13n/logging/P13nLoggerFactory.java b/java/com/android/dialer/p13n/logging/P13nLoggerFactory.java
new file mode 100644
index 000000000..7350e99e1
--- /dev/null
+++ b/java/com/android/dialer/p13n/logging/P13nLoggerFactory.java
@@ -0,0 +1,29 @@
+/*
+ * 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.dialer.p13n.logging;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+/**
+ * This interface should be implemented by the Application subclass. It allows this module to get
+ * references to the P13nLogger.
+ */
+public interface P13nLoggerFactory {
+
+ @Nullable
+ P13nLogger newP13nLogger(@NonNull Context context);
+}
diff --git a/java/com/android/dialer/p13n/logging/P13nLogging.java b/java/com/android/dialer/p13n/logging/P13nLogging.java
new file mode 100644
index 000000000..21b97257b
--- /dev/null
+++ b/java/com/android/dialer/p13n/logging/P13nLogging.java
@@ -0,0 +1,60 @@
+/*
+ * 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.dialer.p13n.logging;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import com.android.contacts.common.list.PhoneNumberListAdapter;
+import com.android.dialer.common.Assert;
+
+/** Single entry point for all logging for personalization. */
+public final class P13nLogging {
+
+ private static P13nLogger logger;
+
+ private P13nLogging() {}
+
+ @NonNull
+ public static P13nLogger get(@NonNull Context context) {
+ Assert.isNotNull(context);
+ Assert.isMainThread();
+ if (logger != null) {
+ return logger;
+ }
+
+ Context application = context.getApplicationContext();
+ if (application instanceof P13nLoggerFactory) {
+ logger = ((P13nLoggerFactory) application).newP13nLogger(context);
+ }
+
+ if (logger == null) {
+ logger =
+ new P13nLogger() {
+ @Override
+ public void onSearchQuery(String query, PhoneNumberListAdapter adapter) {}
+
+ @Override
+ public void reset() {}
+ };
+ }
+ return logger;
+ }
+
+ public static void setForTesting(@NonNull P13nLogger logger) {
+ P13nLogging.logger = logger;
+ }
+}
diff --git a/java/com/android/dialer/phonenumbercache/CachedNumberLookupService.java b/java/com/android/dialer/phonenumbercache/CachedNumberLookupService.java
new file mode 100644
index 000000000..03b77b91c
--- /dev/null
+++ b/java/com/android/dialer/phonenumbercache/CachedNumberLookupService.java
@@ -0,0 +1,77 @@
+/*
+ * 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.dialer.phonenumbercache;
+
+import android.content.Context;
+import android.net.Uri;
+import android.support.annotation.Nullable;
+import java.io.InputStream;
+
+public interface CachedNumberLookupService {
+
+ CachedContactInfo buildCachedContactInfo(ContactInfo info);
+
+ /**
+ * Perform a lookup using the cached number lookup service to return contact information stored in
+ * the cache that corresponds to the given number.
+ *
+ * @param context Valid context
+ * @param number Phone number to lookup the cache for
+ * @return A {@link CachedContactInfo} containing the contact information if the phone number is
+ * found in the cache, {@link ContactInfo#EMPTY} if the phone number was not found in the
+ * cache, and null if there was an error when querying the cache.
+ */
+ CachedContactInfo lookupCachedContactFromNumber(Context context, String number);
+
+ void addContact(Context context, CachedContactInfo info);
+
+ boolean isCacheUri(String uri);
+
+ boolean isBusiness(int sourceType);
+
+ boolean canReportAsInvalid(int sourceType, String objectId);
+
+ /** @return return {@link Uri} to the photo or return {@code null} when failing to add photo */
+ @Nullable
+ Uri addPhoto(Context context, String number, InputStream in);
+
+ /**
+ * Remove all cached phone number entries from the cache, regardless of how old they are.
+ *
+ * @param context Valid context
+ */
+ void clearAllCacheEntries(Context context);
+
+ interface CachedContactInfo {
+
+ int SOURCE_TYPE_DIRECTORY = 1;
+ int SOURCE_TYPE_EXTENDED = 2;
+ int SOURCE_TYPE_PLACES = 3;
+ int SOURCE_TYPE_PROFILE = 4;
+ int SOURCE_TYPE_CNAP = 5;
+
+ ContactInfo getContactInfo();
+
+ void setSource(int sourceType, String name, long directoryId);
+
+ void setDirectorySource(String name, long directoryId);
+
+ void setExtendedSource(String name, long directoryId);
+
+ void setLookupKey(String lookupKey);
+ }
+}
diff --git a/java/com/android/dialer/phonenumbercache/CallLogQuery.java b/java/com/android/dialer/phonenumbercache/CallLogQuery.java
new file mode 100644
index 000000000..6d4756927
--- /dev/null
+++ b/java/com/android/dialer/phonenumbercache/CallLogQuery.java
@@ -0,0 +1,107 @@
+/*
+ * 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.dialer.phonenumbercache;
+
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/** The query for the call log table. */
+public final class CallLogQuery {
+
+ public static final int ID = 0;
+ public static final int NUMBER = 1;
+ public static final int DATE = 2;
+ public static final int DURATION = 3;
+ public static final int CALL_TYPE = 4;
+ public static final int COUNTRY_ISO = 5;
+ public static final int VOICEMAIL_URI = 6;
+ public static final int GEOCODED_LOCATION = 7;
+ public static final int CACHED_NAME = 8;
+ public static final int CACHED_NUMBER_TYPE = 9;
+ public static final int CACHED_NUMBER_LABEL = 10;
+ public static final int CACHED_LOOKUP_URI = 11;
+ public static final int CACHED_MATCHED_NUMBER = 12;
+ public static final int CACHED_NORMALIZED_NUMBER = 13;
+ public static final int CACHED_PHOTO_ID = 14;
+ public static final int CACHED_FORMATTED_NUMBER = 15;
+ public static final int IS_READ = 16;
+ public static final int NUMBER_PRESENTATION = 17;
+ public static final int ACCOUNT_COMPONENT_NAME = 18;
+ public static final int ACCOUNT_ID = 19;
+ public static final int FEATURES = 20;
+ public static final int DATA_USAGE = 21;
+ public static final int TRANSCRIPTION = 22;
+ public static final int CACHED_PHOTO_URI = 23;
+
+ @RequiresApi(VERSION_CODES.N)
+ public static final int POST_DIAL_DIGITS = 24;
+
+ @RequiresApi(VERSION_CODES.N)
+ public static final int VIA_NUMBER = 25;
+
+ private static final String[] PROJECTION_M =
+ new String[] {
+ Calls._ID, // 0
+ Calls.NUMBER, // 1
+ Calls.DATE, // 2
+ Calls.DURATION, // 3
+ Calls.TYPE, // 4
+ Calls.COUNTRY_ISO, // 5
+ Calls.VOICEMAIL_URI, // 6
+ Calls.GEOCODED_LOCATION, // 7
+ Calls.CACHED_NAME, // 8
+ Calls.CACHED_NUMBER_TYPE, // 9
+ Calls.CACHED_NUMBER_LABEL, // 10
+ Calls.CACHED_LOOKUP_URI, // 11
+ Calls.CACHED_MATCHED_NUMBER, // 12
+ Calls.CACHED_NORMALIZED_NUMBER, // 13
+ Calls.CACHED_PHOTO_ID, // 14
+ Calls.CACHED_FORMATTED_NUMBER, // 15
+ Calls.IS_READ, // 16
+ Calls.NUMBER_PRESENTATION, // 17
+ Calls.PHONE_ACCOUNT_COMPONENT_NAME, // 18
+ Calls.PHONE_ACCOUNT_ID, // 19
+ Calls.FEATURES, // 20
+ Calls.DATA_USAGE, // 21
+ Calls.TRANSCRIPTION, // 22
+ Calls.CACHED_PHOTO_URI, // 23
+ };
+
+ private static final String[] PROJECTION_N;
+
+ static {
+ List<String> projectionList = new ArrayList<>(Arrays.asList(PROJECTION_M));
+ projectionList.add(CallLog.Calls.POST_DIAL_DIGITS);
+ projectionList.add(CallLog.Calls.VIA_NUMBER);
+ PROJECTION_N = projectionList.toArray(new String[projectionList.size()]);
+ }
+
+ @NonNull
+ public static String[] getProjection() {
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ return PROJECTION_N;
+ }
+ return PROJECTION_M;
+ }
+}
diff --git a/java/com/android/dialer/phonenumbercache/ContactInfo.java b/java/com/android/dialer/phonenumbercache/ContactInfo.java
new file mode 100644
index 000000000..d7a75c34f
--- /dev/null
+++ b/java/com/android/dialer/phonenumbercache/ContactInfo.java
@@ -0,0 +1,165 @@
+/*
+ * 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.dialer.phonenumbercache;
+
+import android.net.Uri;
+import android.text.TextUtils;
+import com.android.contacts.common.ContactsUtils.UserType;
+import com.android.contacts.common.util.UriUtils;
+
+/** Information for a contact as needed by the Call Log. */
+public class ContactInfo {
+
+ public static final ContactInfo EMPTY = new ContactInfo();
+ public Uri lookupUri;
+ /**
+ * Contact lookup key. Note this may be a lookup key for a corp contact, in which case "lookup by
+ * lookup key" doesn't work on the personal profile.
+ */
+ public String lookupKey;
+
+ public String name;
+ public String nameAlternative;
+ public int type;
+ public String label;
+ public String number;
+ public String formattedNumber;
+ /*
+ * ContactInfo.normalizedNumber is a column value returned by PhoneLookup query. By definition,
+ * it's E164 representation.
+ * http://developer.android.com/reference/android/provider/ContactsContract.PhoneLookupColumns.
+ * html#NORMALIZED_NUMBER.
+ *
+ * The fallback value, when PhoneLookup fails or else, should be either null or
+ * PhoneNumberUtils.formatNumberToE164.
+ */
+ public String normalizedNumber;
+ /** The photo for the contact, if available. */
+ public long photoId;
+ /** The high-res photo for the contact, if available. */
+ public Uri photoUri;
+
+ public boolean isBadData;
+ public String objectId;
+ public @UserType long userType;
+ public int sourceType = 0;
+
+ /** @see android.provider.ContactsContract.CommonDataKinds.Phone#CARRIER_PRESENCE */
+ public int carrierPresence;
+
+ @Override
+ public int hashCode() {
+ // Uses only name and contactUri to determine hashcode.
+ // This should be sufficient to have a reasonable distribution of hash codes.
+ // Moreover, there should be no two people with the same lookupUri.
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((lookupUri == null) ? 0 : lookupUri.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ContactInfo other = (ContactInfo) obj;
+ if (!UriUtils.areEqual(lookupUri, other.lookupUri)) {
+ return false;
+ }
+ if (!TextUtils.equals(name, other.name)) {
+ return false;
+ }
+ if (!TextUtils.equals(nameAlternative, other.nameAlternative)) {
+ return false;
+ }
+ if (type != other.type) {
+ return false;
+ }
+ if (!TextUtils.equals(label, other.label)) {
+ return false;
+ }
+ if (!TextUtils.equals(number, other.number)) {
+ return false;
+ }
+ if (!TextUtils.equals(formattedNumber, other.formattedNumber)) {
+ return false;
+ }
+ if (!TextUtils.equals(normalizedNumber, other.normalizedNumber)) {
+ return false;
+ }
+ if (photoId != other.photoId) {
+ return false;
+ }
+ if (!UriUtils.areEqual(photoUri, other.photoUri)) {
+ return false;
+ }
+ if (!TextUtils.equals(objectId, other.objectId)) {
+ return false;
+ }
+ if (userType != other.userType) {
+ return false;
+ }
+ return carrierPresence == other.carrierPresence;
+ }
+
+ @Override
+ public String toString() {
+ return "ContactInfo{"
+ + "lookupUri="
+ + lookupUri
+ + ", name='"
+ + name
+ + '\''
+ + ", nameAlternative='"
+ + nameAlternative
+ + '\''
+ + ", type="
+ + type
+ + ", label='"
+ + label
+ + '\''
+ + ", number='"
+ + number
+ + '\''
+ + ", formattedNumber='"
+ + formattedNumber
+ + '\''
+ + ", normalizedNumber='"
+ + normalizedNumber
+ + '\''
+ + ", photoId="
+ + photoId
+ + ", photoUri="
+ + photoUri
+ + ", objectId='"
+ + objectId
+ + '\''
+ + ", userType="
+ + userType
+ + ", carrierPresence="
+ + carrierPresence
+ + '}';
+ }
+}
diff --git a/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java b/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java
new file mode 100644
index 000000000..6a5e2e6b4
--- /dev/null
+++ b/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java
@@ -0,0 +1,586 @@
+/*
+ * 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.dialer.phonenumbercache;
+
+import android.annotation.TargetApi;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteFullException;
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.CallLog.Calls;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.DisplayNameSources;
+import android.provider.ContactsContract.PhoneLookup;
+import android.support.annotation.Nullable;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.contacts.common.ContactsUtils;
+import com.android.contacts.common.ContactsUtils.UserType;
+import com.android.contacts.common.compat.DirectoryCompat;
+import com.android.contacts.common.util.Constants;
+import com.android.contacts.common.util.UriUtils;
+import com.android.dialer.phonenumbercache.CachedNumberLookupService.CachedContactInfo;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.android.dialer.telecom.TelecomUtil;
+import com.android.dialer.util.PermissionsUtil;
+import java.util.ArrayList;
+import java.util.List;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/** Utility class to look up the contact information for a given number. */
+// This class uses Java 7 language features, so it must target M+
+@TargetApi(VERSION_CODES.M)
+public class ContactInfoHelper {
+
+ private static final String TAG = ContactInfoHelper.class.getSimpleName();
+
+ private final Context mContext;
+ private final String mCurrentCountryIso;
+ private final CachedNumberLookupService mCachedNumberLookupService;
+
+ public ContactInfoHelper(Context context, String currentCountryIso) {
+ mContext = context;
+ mCurrentCountryIso = currentCountryIso;
+ mCachedNumberLookupService = PhoneNumberCache.get(mContext).getCachedNumberLookupService();
+ }
+
+ /**
+ * Creates a JSON-encoded lookup uri for a unknown number without an associated contact
+ *
+ * @param number - Unknown phone number
+ * @return JSON-encoded URI that can be used to perform a lookup when clicking on the quick
+ * contact card.
+ */
+ private static Uri createTemporaryContactUri(String number) {
+ try {
+ final JSONObject contactRows =
+ new JSONObject()
+ .put(
+ Phone.CONTENT_ITEM_TYPE,
+ new JSONObject().put(Phone.NUMBER, number).put(Phone.TYPE, Phone.TYPE_CUSTOM));
+
+ final String jsonString =
+ new JSONObject()
+ .put(Contacts.DISPLAY_NAME, number)
+ .put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.PHONE)
+ .put(Contacts.CONTENT_ITEM_TYPE, contactRows)
+ .toString();
+
+ return Contacts.CONTENT_LOOKUP_URI
+ .buildUpon()
+ .appendPath(Constants.LOOKUP_URI_ENCODED)
+ .appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Long.MAX_VALUE))
+ .encodedFragment(jsonString)
+ .build();
+ } catch (JSONException e) {
+ return null;
+ }
+ }
+
+ public static String lookUpDisplayNameAlternative(
+ Context context, String lookupKey, @UserType long userType, @Nullable Long directoryId) {
+ // Query {@link Contacts#CONTENT_LOOKUP_URI} directly with work lookup key is not allowed.
+ if (lookupKey == null || userType == ContactsUtils.USER_TYPE_WORK) {
+ return null;
+ }
+
+ if (directoryId != null) {
+ // Query {@link Contacts#CONTENT_LOOKUP_URI} with work lookup key is not allowed.
+ if (DirectoryCompat.isEnterpriseDirectoryId(directoryId)) {
+ return null;
+ }
+
+ // Skip this to avoid an extra remote network call for alternative name
+ if (DirectoryCompat.isRemoteDirectoryId(directoryId)) {
+ return null;
+ }
+ }
+
+ final Uri uri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
+ Cursor cursor = null;
+ try {
+ cursor =
+ context
+ .getContentResolver()
+ .query(uri, PhoneQuery.DISPLAY_NAME_ALTERNATIVE_PROJECTION, null, null, null);
+
+ if (cursor != null && cursor.moveToFirst()) {
+ return cursor.getString(PhoneQuery.NAME_ALTERNATIVE);
+ }
+ } catch (IllegalArgumentException e) {
+ // Avoid dialer crash when lookup key is not valid
+ Log.e(TAG, "IllegalArgumentException in lookUpDisplayNameAlternative", e);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ return null;
+ }
+
+ public static Uri getContactInfoLookupUri(String number) {
+ return getContactInfoLookupUri(number, -1);
+ }
+
+ public static Uri getContactInfoLookupUri(String number, long directoryId) {
+ // Get URI for the number in the PhoneLookup table, with a parameter to indicate whether
+ // the number is a SIP number.
+ Uri uri = PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI;
+ if (VERSION.SDK_INT < VERSION_CODES.N) {
+ if (directoryId != -1) {
+ // ENTERPRISE_CONTENT_FILTER_URI in M doesn't support directory lookup
+ uri = PhoneLookup.CONTENT_FILTER_URI;
+ } else {
+ // b/25900607 in M. PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, encodes twice.
+ number = Uri.encode(number);
+ }
+ }
+ Uri.Builder builder =
+ uri.buildUpon()
+ .appendPath(number)
+ .appendQueryParameter(
+ PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS,
+ String.valueOf(PhoneNumberHelper.isUriNumber(number)));
+ if (directoryId != -1) {
+ builder.appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId));
+ }
+ return builder.build();
+ }
+
+ /**
+ * Returns the contact information stored in an entry of the call log.
+ *
+ * @param c A cursor pointing to an entry in the call log.
+ */
+ public static ContactInfo getContactInfo(Cursor c) {
+ ContactInfo info = new ContactInfo();
+ info.lookupUri = UriUtils.parseUriOrNull(c.getString(CallLogQuery.CACHED_LOOKUP_URI));
+ info.name = c.getString(CallLogQuery.CACHED_NAME);
+ info.type = c.getInt(CallLogQuery.CACHED_NUMBER_TYPE);
+ info.label = c.getString(CallLogQuery.CACHED_NUMBER_LABEL);
+ String matchedNumber = c.getString(CallLogQuery.CACHED_MATCHED_NUMBER);
+ String postDialDigits =
+ (VERSION.SDK_INT >= VERSION_CODES.N) ? c.getString(CallLogQuery.POST_DIAL_DIGITS) : "";
+ info.number =
+ (matchedNumber == null) ? c.getString(CallLogQuery.NUMBER) + postDialDigits : matchedNumber;
+
+ info.normalizedNumber = c.getString(CallLogQuery.CACHED_NORMALIZED_NUMBER);
+ info.photoId = c.getLong(CallLogQuery.CACHED_PHOTO_ID);
+ info.photoUri =
+ UriUtils.nullForNonContactsUri(
+ UriUtils.parseUriOrNull(c.getString(CallLogQuery.CACHED_PHOTO_URI)));
+ info.formattedNumber = c.getString(CallLogQuery.CACHED_FORMATTED_NUMBER);
+
+ return info;
+ }
+
+ public ContactInfo lookupNumber(String number, String countryIso) {
+ return lookupNumber(number, countryIso, -1);
+ }
+
+ /**
+ * Returns the contact information for the given number.
+ *
+ * <p>If the number does not match any contact, returns a contact info containing only the number
+ * and the formatted number.
+ *
+ * <p>If an error occurs during the lookup, it returns null.
+ *
+ * @param number the number to look up
+ * @param countryIso the country associated with this number
+ * @param directoryId the id of the directory to lookup
+ */
+ @Nullable
+ @SuppressWarnings("ReferenceEquality")
+ public ContactInfo lookupNumber(String number, String countryIso, long directoryId) {
+ if (TextUtils.isEmpty(number)) {
+ return null;
+ }
+
+ ContactInfo info;
+
+ if (PhoneNumberHelper.isUriNumber(number)) {
+ // The number is a SIP address..
+ info = lookupContactFromUri(getContactInfoLookupUri(number, directoryId));
+ if (info == null || info == ContactInfo.EMPTY) {
+ // If lookup failed, check if the "username" of the SIP address is a phone number.
+ String username = PhoneNumberHelper.getUsernameFromUriNumber(number);
+ if (PhoneNumberUtils.isGlobalPhoneNumber(username)) {
+ info = queryContactInfoForPhoneNumber(username, countryIso, directoryId);
+ }
+ }
+ } else {
+ // Look for a contact that has the given phone number.
+ info = queryContactInfoForPhoneNumber(number, countryIso, directoryId);
+ }
+
+ final ContactInfo updatedInfo;
+ if (info == null) {
+ // The lookup failed.
+ updatedInfo = null;
+ } else {
+ // If we did not find a matching contact, generate an empty contact info for the number.
+ if (info == ContactInfo.EMPTY) {
+ // Did not find a matching contact.
+ updatedInfo = createEmptyContactInfoForNumber(number, countryIso);
+ } else {
+ updatedInfo = info;
+ }
+ }
+ return updatedInfo;
+ }
+
+ private ContactInfo createEmptyContactInfoForNumber(String number, String countryIso) {
+ ContactInfo contactInfo = new ContactInfo();
+ contactInfo.number = number;
+ contactInfo.formattedNumber = formatPhoneNumber(number, null, countryIso);
+ contactInfo.normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
+ contactInfo.lookupUri = createTemporaryContactUri(contactInfo.formattedNumber);
+ return contactInfo;
+ }
+
+ /**
+ * Return the contact info object if the remote directory lookup succeeds, otherwise return an
+ * empty contact info for the number.
+ */
+ public ContactInfo lookupNumberInRemoteDirectory(String number, String countryIso) {
+ if (mCachedNumberLookupService != null) {
+ List<Long> remoteDirectories = getRemoteDirectories(mContext);
+ for (long directoryId : remoteDirectories) {
+ ContactInfo contactInfo = lookupNumber(number, countryIso, directoryId);
+ if (hasName(contactInfo)) {
+ return contactInfo;
+ }
+ }
+ }
+ return createEmptyContactInfoForNumber(number, countryIso);
+ }
+
+ public boolean hasName(ContactInfo contactInfo) {
+ return contactInfo != null && !TextUtils.isEmpty(contactInfo.name);
+ }
+
+ private List<Long> getRemoteDirectories(Context context) {
+ List<Long> remoteDirectories = new ArrayList<>();
+ Uri uri =
+ VERSION.SDK_INT >= VERSION_CODES.N
+ ? Directory.ENTERPRISE_CONTENT_URI
+ : Directory.CONTENT_URI;
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, new String[] {Directory._ID}, null, null, null);
+ int idIndex = cursor.getColumnIndex(Directory._ID);
+ if (cursor == null) {
+ return remoteDirectories;
+ }
+ try {
+ while (cursor.moveToNext()) {
+ long directoryId = cursor.getLong(idIndex);
+ if (DirectoryCompat.isRemoteDirectoryId(directoryId)) {
+ remoteDirectories.add(directoryId);
+ }
+ }
+ } finally {
+ cursor.close();
+ }
+ return remoteDirectories;
+ }
+
+ /**
+ * Looks up a contact using the given URI.
+ *
+ * <p>It returns null if an error occurs, {@link ContactInfo#EMPTY} if no matching contact is
+ * found, or the {@link ContactInfo} for the given contact.
+ *
+ * <p>The {@link ContactInfo#formattedNumber} field is always set to {@code null} in the returned
+ * value.
+ */
+ ContactInfo lookupContactFromUri(Uri uri) {
+ if (uri == null) {
+ return null;
+ }
+ if (!PermissionsUtil.hasContactsPermissions(mContext)) {
+ return ContactInfo.EMPTY;
+ }
+
+ Cursor phoneLookupCursor = null;
+ try {
+ String[] projection = PhoneQuery.getPhoneLookupProjection(uri);
+ phoneLookupCursor = mContext.getContentResolver().query(uri, projection, null, null, null);
+ } catch (NullPointerException e) {
+ // Trap NPE from pre-N CP2
+ return null;
+ }
+ if (phoneLookupCursor == null) {
+ return null;
+ }
+
+ try {
+ if (!phoneLookupCursor.moveToFirst()) {
+ return ContactInfo.EMPTY;
+ }
+ String lookupKey = phoneLookupCursor.getString(PhoneQuery.LOOKUP_KEY);
+ ContactInfo contactInfo = createPhoneLookupContactInfo(phoneLookupCursor, lookupKey);
+ fillAdditionalContactInfo(mContext, contactInfo);
+ return contactInfo;
+ } finally {
+ phoneLookupCursor.close();
+ }
+ }
+
+ private ContactInfo createPhoneLookupContactInfo(Cursor phoneLookupCursor, String lookupKey) {
+ ContactInfo info = new ContactInfo();
+ info.lookupKey = lookupKey;
+ info.lookupUri =
+ Contacts.getLookupUri(phoneLookupCursor.getLong(PhoneQuery.PERSON_ID), lookupKey);
+ info.name = phoneLookupCursor.getString(PhoneQuery.NAME);
+ info.type = phoneLookupCursor.getInt(PhoneQuery.PHONE_TYPE);
+ info.label = phoneLookupCursor.getString(PhoneQuery.LABEL);
+ info.number = phoneLookupCursor.getString(PhoneQuery.MATCHED_NUMBER);
+ info.normalizedNumber = phoneLookupCursor.getString(PhoneQuery.NORMALIZED_NUMBER);
+ info.photoId = phoneLookupCursor.getLong(PhoneQuery.PHOTO_ID);
+ info.photoUri = UriUtils.parseUriOrNull(phoneLookupCursor.getString(PhoneQuery.PHOTO_URI));
+ info.formattedNumber = null;
+ info.userType =
+ ContactsUtils.determineUserType(null, phoneLookupCursor.getLong(PhoneQuery.PERSON_ID));
+
+ return info;
+ }
+
+ private void fillAdditionalContactInfo(Context context, ContactInfo contactInfo) {
+ if (contactInfo.number == null) {
+ return;
+ }
+ Uri uri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(contactInfo.number));
+ try (Cursor cursor =
+ context
+ .getContentResolver()
+ .query(uri, PhoneQuery.ADDITIONAL_CONTACT_INFO_PROJECTION, null, null, null)) {
+ if (cursor == null || !cursor.moveToFirst()) {
+ return;
+ }
+ contactInfo.nameAlternative =
+ cursor.getString(PhoneQuery.ADDITIONAL_CONTACT_INFO_DISPLAY_NAME_ALTERNATIVE);
+ contactInfo.carrierPresence =
+ cursor.getInt(PhoneQuery.ADDITIONAL_CONTACT_INFO_CARRIER_PRESENCE);
+ }
+ }
+
+ /**
+ * Determines the contact information for the given phone number.
+ *
+ * <p>It returns the contact info if found.
+ *
+ * <p>If no contact corresponds to the given phone number, returns {@link ContactInfo#EMPTY}.
+ *
+ * <p>If the lookup fails for some other reason, it returns null.
+ */
+ @SuppressWarnings("ReferenceEquality")
+ private ContactInfo queryContactInfoForPhoneNumber(
+ String number, String countryIso, long directoryId) {
+ if (TextUtils.isEmpty(number)) {
+ return null;
+ }
+
+ ContactInfo info = lookupContactFromUri(getContactInfoLookupUri(number, directoryId));
+ if (info != null && info != ContactInfo.EMPTY) {
+ info.formattedNumber = formatPhoneNumber(number, null, countryIso);
+ } else if (mCachedNumberLookupService != null) {
+ CachedContactInfo cacheInfo =
+ mCachedNumberLookupService.lookupCachedContactFromNumber(mContext, number);
+ if (cacheInfo != null) {
+ info = cacheInfo.getContactInfo().isBadData ? null : cacheInfo.getContactInfo();
+ } else {
+ info = null;
+ }
+ }
+ return info;
+ }
+
+ /**
+ * Format the given phone number
+ *
+ * @param number the number to be formatted.
+ * @param normalizedNumber the normalized number of the given number.
+ * @param countryIso the ISO 3166-1 two letters country code, the country's convention will be
+ * used to format the number if the normalized phone is null.
+ * @return the formatted number, or the given number if it was formatted.
+ */
+ private String formatPhoneNumber(String number, String normalizedNumber, String countryIso) {
+ if (TextUtils.isEmpty(number)) {
+ return "";
+ }
+ // If "number" is really a SIP address, don't try to do any formatting at all.
+ if (PhoneNumberHelper.isUriNumber(number)) {
+ return number;
+ }
+ if (TextUtils.isEmpty(countryIso)) {
+ countryIso = mCurrentCountryIso;
+ }
+ return PhoneNumberUtils.formatNumber(number, normalizedNumber, countryIso);
+ }
+
+ /**
+ * Stores differences between the updated contact info and the current call log contact info.
+ *
+ * @param number The number of the contact.
+ * @param countryIso The country associated with this number.
+ * @param updatedInfo The updated contact info.
+ * @param callLogInfo The call log entry's current contact info.
+ */
+ public void updateCallLogContactInfo(
+ String number, String countryIso, ContactInfo updatedInfo, ContactInfo callLogInfo) {
+ if (!PermissionsUtil.hasPermission(mContext, android.Manifest.permission.WRITE_CALL_LOG)) {
+ return;
+ }
+
+ final ContentValues values = new ContentValues();
+ boolean needsUpdate = false;
+
+ if (callLogInfo != null) {
+ if (!TextUtils.equals(updatedInfo.name, callLogInfo.name)) {
+ values.put(Calls.CACHED_NAME, updatedInfo.name);
+ needsUpdate = true;
+ }
+
+ if (updatedInfo.type != callLogInfo.type) {
+ values.put(Calls.CACHED_NUMBER_TYPE, updatedInfo.type);
+ needsUpdate = true;
+ }
+
+ if (!TextUtils.equals(updatedInfo.label, callLogInfo.label)) {
+ values.put(Calls.CACHED_NUMBER_LABEL, updatedInfo.label);
+ needsUpdate = true;
+ }
+
+ if (!UriUtils.areEqual(updatedInfo.lookupUri, callLogInfo.lookupUri)) {
+ values.put(Calls.CACHED_LOOKUP_URI, UriUtils.uriToString(updatedInfo.lookupUri));
+ needsUpdate = true;
+ }
+
+ // Only replace the normalized number if the new updated normalized number isn't empty.
+ if (!TextUtils.isEmpty(updatedInfo.normalizedNumber)
+ && !TextUtils.equals(updatedInfo.normalizedNumber, callLogInfo.normalizedNumber)) {
+ values.put(Calls.CACHED_NORMALIZED_NUMBER, updatedInfo.normalizedNumber);
+ needsUpdate = true;
+ }
+
+ if (!TextUtils.equals(updatedInfo.number, callLogInfo.number)) {
+ values.put(Calls.CACHED_MATCHED_NUMBER, updatedInfo.number);
+ needsUpdate = true;
+ }
+
+ if (updatedInfo.photoId != callLogInfo.photoId) {
+ values.put(Calls.CACHED_PHOTO_ID, updatedInfo.photoId);
+ needsUpdate = true;
+ }
+
+ final Uri updatedPhotoUriContactsOnly = UriUtils.nullForNonContactsUri(updatedInfo.photoUri);
+ if (!UriUtils.areEqual(updatedPhotoUriContactsOnly, callLogInfo.photoUri)) {
+ values.put(Calls.CACHED_PHOTO_URI, UriUtils.uriToString(updatedPhotoUriContactsOnly));
+ needsUpdate = true;
+ }
+
+ if (!TextUtils.equals(updatedInfo.formattedNumber, callLogInfo.formattedNumber)) {
+ values.put(Calls.CACHED_FORMATTED_NUMBER, updatedInfo.formattedNumber);
+ needsUpdate = true;
+ }
+ } else {
+ // No previous values, store all of them.
+ values.put(Calls.CACHED_NAME, updatedInfo.name);
+ values.put(Calls.CACHED_NUMBER_TYPE, updatedInfo.type);
+ values.put(Calls.CACHED_NUMBER_LABEL, updatedInfo.label);
+ values.put(Calls.CACHED_LOOKUP_URI, UriUtils.uriToString(updatedInfo.lookupUri));
+ values.put(Calls.CACHED_MATCHED_NUMBER, updatedInfo.number);
+ values.put(Calls.CACHED_NORMALIZED_NUMBER, updatedInfo.normalizedNumber);
+ values.put(Calls.CACHED_PHOTO_ID, updatedInfo.photoId);
+ values.put(
+ Calls.CACHED_PHOTO_URI,
+ UriUtils.uriToString(UriUtils.nullForNonContactsUri(updatedInfo.photoUri)));
+ values.put(Calls.CACHED_FORMATTED_NUMBER, updatedInfo.formattedNumber);
+ needsUpdate = true;
+ }
+
+ if (!needsUpdate) {
+ return;
+ }
+
+ try {
+ if (countryIso == null) {
+ mContext
+ .getContentResolver()
+ .update(
+ TelecomUtil.getCallLogUri(mContext),
+ values,
+ Calls.NUMBER + " = ? AND " + Calls.COUNTRY_ISO + " IS NULL",
+ new String[] {number});
+ } else {
+ mContext
+ .getContentResolver()
+ .update(
+ TelecomUtil.getCallLogUri(mContext),
+ values,
+ Calls.NUMBER + " = ? AND " + Calls.COUNTRY_ISO + " = ?",
+ new String[] {number, countryIso});
+ }
+ } catch (SQLiteFullException e) {
+ Log.e(TAG, "Unable to update contact info in call log db", e);
+ }
+ }
+
+ public void updateCachedNumberLookupService(ContactInfo updatedInfo) {
+ if (mCachedNumberLookupService != null) {
+ if (hasName(updatedInfo)) {
+ CachedContactInfo cachedContactInfo =
+ mCachedNumberLookupService.buildCachedContactInfo(updatedInfo);
+ mCachedNumberLookupService.addContact(mContext, cachedContactInfo);
+ }
+ }
+ }
+
+ /**
+ * Given a contact's sourceType, return true if the contact is a business
+ *
+ * @param sourceType sourceType of the contact. This is usually populated by {@link
+ * #mCachedNumberLookupService}.
+ */
+ public boolean isBusiness(int sourceType) {
+ return mCachedNumberLookupService != null && mCachedNumberLookupService.isBusiness(sourceType);
+ }
+
+ /**
+ * This function looks at a contact's source and determines if the user can mark caller ids from
+ * this source as invalid.
+ *
+ * @param sourceType The source type to be checked
+ * @param objectId The ID of the Contact object.
+ * @return true if contacts from this source can be marked with an invalid caller id
+ */
+ public boolean canReportAsInvalid(int sourceType, String objectId) {
+ return mCachedNumberLookupService != null
+ && mCachedNumberLookupService.canReportAsInvalid(sourceType, objectId);
+ }
+}
diff --git a/java/com/android/dialer/phonenumbercache/PhoneLookupUtil.java b/java/com/android/dialer/phonenumbercache/PhoneLookupUtil.java
new file mode 100644
index 000000000..74175e8ba
--- /dev/null
+++ b/java/com/android/dialer/phonenumbercache/PhoneLookupUtil.java
@@ -0,0 +1,40 @@
+/*
+ * 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.dialer.phonenumbercache;
+
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.PhoneLookup;
+
+public final class PhoneLookupUtil {
+
+ private PhoneLookupUtil() {}
+
+ /** @return the column name that stores contact id for phone lookup query. */
+ public static String getContactIdColumnNameForUri(Uri phoneLookupUri) {
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ return PhoneLookup.CONTACT_ID;
+ }
+ // In pre-N, contact id is stored in {@link PhoneLookup#_ID} in non-sip query.
+ boolean isSip =
+ phoneLookupUri.getBooleanQueryParameter(
+ ContactsContract.PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, false);
+ return (isSip) ? PhoneLookup.CONTACT_ID : ContactsContract.PhoneLookup._ID;
+ }
+}
diff --git a/java/com/android/dialer/phonenumbercache/PhoneNumberCache.java b/java/com/android/dialer/phonenumbercache/PhoneNumberCache.java
new file mode 100644
index 000000000..aefa544cb
--- /dev/null
+++ b/java/com/android/dialer/phonenumbercache/PhoneNumberCache.java
@@ -0,0 +1,50 @@
+/*
+ * 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.dialer.phonenumbercache;
+
+import android.content.Context;
+import java.util.Objects;
+
+/** Accessor for the phone number cache bindings. */
+public class PhoneNumberCache {
+
+ private static PhoneNumberCacheBindings phoneNumberCacheBindings;
+
+ private PhoneNumberCache() {}
+
+ public static PhoneNumberCacheBindings get(Context context) {
+ Objects.requireNonNull(context);
+ if (phoneNumberCacheBindings != null) {
+ return phoneNumberCacheBindings;
+ }
+
+ Context application = context.getApplicationContext();
+ if (application instanceof PhoneNumberCacheBindingsFactory) {
+ phoneNumberCacheBindings =
+ ((PhoneNumberCacheBindingsFactory) application).newPhoneNumberCacheBindings();
+ }
+
+ if (phoneNumberCacheBindings == null) {
+ phoneNumberCacheBindings = new PhoneNumberCacheBindingsStub();
+ }
+ return phoneNumberCacheBindings;
+ }
+
+ public static void setForTesting(PhoneNumberCacheBindings phoneNumberCacheBindings) {
+ PhoneNumberCache.phoneNumberCacheBindings = phoneNumberCacheBindings;
+ }
+}
diff --git a/java/com/android/dialer/phonenumbercache/PhoneNumberCacheBindings.java b/java/com/android/dialer/phonenumbercache/PhoneNumberCacheBindings.java
new file mode 100644
index 000000000..6e3ed9d06
--- /dev/null
+++ b/java/com/android/dialer/phonenumbercache/PhoneNumberCacheBindings.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dialer.phonenumbercache;
+
+import android.support.annotation.Nullable;
+
+/** Allows the container application provide a number look up service. */
+public interface PhoneNumberCacheBindings {
+
+ @Nullable
+ CachedNumberLookupService getCachedNumberLookupService();
+}
diff --git a/java/com/android/dialer/phonenumbercache/PhoneNumberCacheBindingsFactory.java b/java/com/android/dialer/phonenumbercache/PhoneNumberCacheBindingsFactory.java
new file mode 100644
index 000000000..3552529ba
--- /dev/null
+++ b/java/com/android/dialer/phonenumbercache/PhoneNumberCacheBindingsFactory.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dialer.phonenumbercache;
+
+/**
+ * This interface should be implementated by the Application subclass. It allows this module to get
+ * references to the PhoneNumberCacheBindings.
+ */
+public interface PhoneNumberCacheBindingsFactory {
+
+ PhoneNumberCacheBindings newPhoneNumberCacheBindings();
+}
diff --git a/java/com/android/dialer/phonenumbercache/PhoneNumberCacheBindingsStub.java b/java/com/android/dialer/phonenumbercache/PhoneNumberCacheBindingsStub.java
new file mode 100644
index 000000000..c7fb97807
--- /dev/null
+++ b/java/com/android/dialer/phonenumbercache/PhoneNumberCacheBindingsStub.java
@@ -0,0 +1,29 @@
+/*
+ * 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.dialer.phonenumbercache;
+
+import android.support.annotation.Nullable;
+
+/** Default implementation of PhoneNumberCacheBindings. */
+public class PhoneNumberCacheBindingsStub implements PhoneNumberCacheBindings {
+
+ @Override
+ @Nullable
+ public CachedNumberLookupService getCachedNumberLookupService() {
+ return null;
+ }
+}
diff --git a/java/com/android/dialer/phonenumbercache/PhoneQuery.java b/java/com/android/dialer/phonenumbercache/PhoneQuery.java
new file mode 100644
index 000000000..5ddd5f846
--- /dev/null
+++ b/java/com/android/dialer/phonenumbercache/PhoneQuery.java
@@ -0,0 +1,96 @@
+/*
+ * 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.dialer.phonenumbercache;
+
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.PhoneLookup;
+
+/** The queries to look up the {@link ContactInfo} for a given number in the Call Log. */
+final class PhoneQuery {
+
+ public static final int PERSON_ID = 0;
+ public static final int NAME = 1;
+ public static final int PHONE_TYPE = 2;
+ public static final int LABEL = 3;
+ public static final int MATCHED_NUMBER = 4;
+ public static final int NORMALIZED_NUMBER = 5;
+ public static final int PHOTO_ID = 6;
+ public static final int LOOKUP_KEY = 7;
+ public static final int PHOTO_URI = 8;
+ /** Projection to look up a contact's DISPLAY_NAME_ALTERNATIVE */
+ public static final String[] DISPLAY_NAME_ALTERNATIVE_PROJECTION =
+ new String[] {
+ Contacts.DISPLAY_NAME_ALTERNATIVE,
+ };
+
+ public static final int NAME_ALTERNATIVE = 0;
+
+ public static final String[] ADDITIONAL_CONTACT_INFO_PROJECTION =
+ new String[] {Phone.DISPLAY_NAME_ALTERNATIVE, Phone.CARRIER_PRESENCE};
+ public static final int ADDITIONAL_CONTACT_INFO_DISPLAY_NAME_ALTERNATIVE = 0;
+ public static final int ADDITIONAL_CONTACT_INFO_CARRIER_PRESENCE = 1;
+
+ /**
+ * Projection to look up the ContactInfo. Does not include DISPLAY_NAME_ALTERNATIVE as that column
+ * isn't available in ContactsCommon.PhoneLookup. We should always use this projection starting
+ * from NYC onward.
+ */
+ private static final String[] PHONE_LOOKUP_PROJECTION =
+ new String[] {
+ PhoneLookup.CONTACT_ID,
+ PhoneLookup.DISPLAY_NAME,
+ PhoneLookup.TYPE,
+ PhoneLookup.LABEL,
+ PhoneLookup.NUMBER,
+ PhoneLookup.NORMALIZED_NUMBER,
+ PhoneLookup.PHOTO_ID,
+ PhoneLookup.LOOKUP_KEY,
+ PhoneLookup.PHOTO_URI
+ };
+ /**
+ * Similar to {@link PHONE_LOOKUP_PROJECTION}. In pre-N, contact id is stored in {@link
+ * PhoneLookup#_ID} in non-sip query.
+ */
+ private static final String[] BACKWARD_COMPATIBLE_NON_SIP_PHONE_LOOKUP_PROJECTION =
+ new String[] {
+ PhoneLookup._ID,
+ PhoneLookup.DISPLAY_NAME,
+ PhoneLookup.TYPE,
+ PhoneLookup.LABEL,
+ PhoneLookup.NUMBER,
+ PhoneLookup.NORMALIZED_NUMBER,
+ PhoneLookup.PHOTO_ID,
+ PhoneLookup.LOOKUP_KEY,
+ PhoneLookup.PHOTO_URI
+ };
+
+ public static String[] getPhoneLookupProjection(Uri phoneLookupUri) {
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ return PHONE_LOOKUP_PROJECTION;
+ }
+ // Pre-N
+ boolean isSip =
+ phoneLookupUri.getBooleanQueryParameter(
+ ContactsContract.PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, false);
+ return (isSip) ? PHONE_LOOKUP_PROJECTION : BACKWARD_COMPATIBLE_NON_SIP_PHONE_LOOKUP_PROJECTION;
+ }
+}
diff --git a/java/com/android/dialer/phonenumberutil/AndroidManifest.xml b/java/com/android/dialer/phonenumberutil/AndroidManifest.xml
new file mode 100644
index 000000000..f7ee4001c
--- /dev/null
+++ b/java/com/android/dialer/phonenumberutil/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.dialer.phonenumberutil">
+</manifest>
diff --git a/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java b/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java
new file mode 100644
index 000000000..ea4396f02
--- /dev/null
+++ b/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2013 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.dialer.phonenumberutil;
+
+import android.content.Context;
+import android.provider.CallLog;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.telecom.TelecomUtil;
+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.Arrays;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+public class PhoneNumberHelper {
+
+ private static final String TAG = "PhoneNumberUtil";
+ private static final Set<String> LEGACY_UNKNOWN_NUMBERS =
+ new HashSet<>(Arrays.asList("-1", "-2", "-3"));
+
+ /** Returns true if it is possible to place a call to the given number. */
+ public static boolean canPlaceCallsTo(CharSequence number, int presentation) {
+ return presentation == CallLog.Calls.PRESENTATION_ALLOWED
+ && !TextUtils.isEmpty(number)
+ && !isLegacyUnknownNumbers(number);
+ }
+
+ /**
+ * Returns true if the given number is the number of the configured voicemail. To be able to
+ * mock-out this, it is not a static method.
+ */
+ public static boolean isVoicemailNumber(
+ Context context, PhoneAccountHandle accountHandle, CharSequence number) {
+ if (TextUtils.isEmpty(number)) {
+ return false;
+ }
+ return TelecomUtil.isVoicemailNumber(context, accountHandle, number.toString());
+ }
+
+ /**
+ * Returns true if the given number is a SIP address. To be able to mock-out this, it is not a
+ * static method.
+ */
+ public static boolean isSipNumber(CharSequence number) {
+ return number != null && isUriNumber(number.toString());
+ }
+
+ public static boolean isUnknownNumberThatCanBeLookedUp(
+ Context context, PhoneAccountHandle accountHandle, CharSequence number, int presentation) {
+ if (presentation == CallLog.Calls.PRESENTATION_UNKNOWN) {
+ return false;
+ }
+ if (presentation == CallLog.Calls.PRESENTATION_RESTRICTED) {
+ return false;
+ }
+ if (presentation == CallLog.Calls.PRESENTATION_PAYPHONE) {
+ return false;
+ }
+ if (TextUtils.isEmpty(number)) {
+ return false;
+ }
+ if (isVoicemailNumber(context, accountHandle, number)) {
+ return false;
+ }
+ if (isLegacyUnknownNumbers(number)) {
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean isLegacyUnknownNumbers(CharSequence number) {
+ return number != null && LEGACY_UNKNOWN_NUMBERS.contains(number.toString());
+ }
+
+ /**
+ * @return a geographical description string for the specified number.
+ * @see com.android.i18n.phonenumbers.PhoneNumberOfflineGeocoder
+ */
+ public static String getGeoDescription(Context context, String number) {
+ LogUtil.v("PhoneNumberHelper.getGeoDescription", "" + LogUtil.sanitizePii(number));
+
+ if (TextUtils.isEmpty(number)) {
+ return null;
+ }
+
+ PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+ PhoneNumberOfflineGeocoder geocoder = PhoneNumberOfflineGeocoder.getInstance();
+
+ Locale locale = context.getResources().getConfiguration().locale;
+ String countryIso = getCurrentCountryIso(context, locale);
+ Phonenumber.PhoneNumber pn = null;
+ try {
+ LogUtil.v(
+ "PhoneNumberHelper.getGeoDescription",
+ "parsing '" + LogUtil.sanitizePii(number) + "' for countryIso '" + countryIso + "'...");
+ pn = util.parse(number, countryIso);
+ LogUtil.v(
+ "PhoneNumberHelper.getGeoDescription", "- parsed number: " + LogUtil.sanitizePii(pn));
+ } catch (NumberParseException e) {
+ LogUtil.e(
+ "PhoneNumberHelper.getGeoDescription",
+ "getGeoDescription: NumberParseException for incoming number '"
+ + LogUtil.sanitizePii(number)
+ + "'");
+ }
+
+ if (pn != null) {
+ String description = geocoder.getDescriptionForNumber(pn, locale);
+ LogUtil.v("PhoneNumberHelper.getGeoDescription", "- got description: '" + description + "'");
+ return description;
+ }
+
+ return null;
+ }
+
+ /**
+ * @return The ISO 3166-1 two letters country code of the country the user is in based on the
+ * network location. If the network location does not exist, fall back to the locale setting.
+ */
+ public static String getCurrentCountryIso(Context context, Locale locale) {
+ // Without framework function calls, this seems to be the most accurate location service
+ // we can rely on.
+ final TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+
+ String countryIso = telephonyManager.getNetworkCountryIso();
+ if (TextUtils.isEmpty(countryIso)) {
+ countryIso = locale.getCountry();
+ LogUtil.i(
+ "PhoneNumberHelper.getCurrentCountryIso",
+ "No CountryDetector; falling back to countryIso based on locale: " + countryIso);
+ }
+ countryIso = countryIso.toUpperCase();
+
+ return countryIso;
+ }
+
+ /**
+ * @return Formatted phone number. e.g. 1-123-456-7890. Returns the original number if formatting
+ * failed.
+ */
+ public static String formatNumber(@Nullable String number, Context context) {
+ // The number can be null e.g. schema is voicemail and uri content is empty.
+ if (number == null) {
+ return null;
+ }
+ String formattedNumber =
+ PhoneNumberUtils.formatNumber(
+ number, PhoneNumberHelper.getCurrentCountryIso(context, Locale.getDefault()));
+ return formattedNumber != null ? formattedNumber : number;
+ }
+
+ /**
+ * Determines if the specified number is actually a URI (i.e. a SIP address) rather than a regular
+ * PSTN phone number, based on whether or not the number contains an "@" character.
+ *
+ * @param number Phone number
+ * @return true if number contains @
+ * <p>TODO: Remove if PhoneNumberUtils.isUriNumber(String number) is made public.
+ */
+ public static boolean isUriNumber(String number) {
+ // Note we allow either "@" or "%40" to indicate a URI, in case
+ // the passed-in string is URI-escaped. (Neither "@" nor "%40"
+ // will ever be found in a legal PSTN number.)
+ return number != null && (number.contains("@") || number.contains("%40"));
+ }
+
+ /**
+ * @param number SIP address of the form "username@domainname" (or the URI-escaped equivalent
+ * "username%40domainname")
+ * <p>TODO: Remove if PhoneNumberUtils.getUsernameFromUriNumber(String number) is made public.
+ * @return the "username" part of the specified SIP address, i.e. the part before the "@"
+ * character (or "%40").
+ */
+ public static String getUsernameFromUriNumber(String number) {
+ // The delimiter between username and domain name can be
+ // either "@" or "%40" (the URI-escaped equivalent.)
+ int delimiterIndex = number.indexOf('@');
+ if (delimiterIndex < 0) {
+ delimiterIndex = number.indexOf("%40");
+ }
+ if (delimiterIndex < 0) {
+ LogUtil.i(
+ "PhoneNumberHelper.getUsernameFromUriNumber",
+ "getUsernameFromUriNumber: no delimiter found in SIP address: "
+ + LogUtil.sanitizePii(number));
+ return number;
+ }
+ return number.substring(0, delimiterIndex);
+ }
+
+
+ private static boolean isVerizon(Context context) {
+ // Verizon MCC/MNC codes copied from com/android/voicemailomtp/res/xml/vvm_config.xml.
+ // TODO: Need a better way to do per carrier and per OEM configurations.
+ switch (context.getSystemService(TelephonyManager.class).getSimOperator()) {
+ case "310004":
+ case "310010":
+ case "310012":
+ case "310013":
+ case "310590":
+ case "310890":
+ case "310910":
+ case "311110":
+ case "311270":
+ case "311271":
+ case "311272":
+ case "311273":
+ case "311274":
+ case "311275":
+ case "311276":
+ case "311277":
+ case "311278":
+ case "311279":
+ case "311280":
+ case "311281":
+ case "311282":
+ case "311283":
+ case "311284":
+ case "311285":
+ case "311286":
+ case "311287":
+ case "311288":
+ case "311289":
+ case "311390":
+ case "311480":
+ case "311481":
+ case "311482":
+ case "311483":
+ case "311484":
+ case "311485":
+ case "311486":
+ case "311487":
+ case "311488":
+ case "311489":
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Gets the label to display for a phone call where the presentation is set as
+ * PRESENTATION_RESTRICTED. For Verizon we want this to be displayed as "Restricted". For all
+ * other carriers we want this to be be displayed as "Private number".
+ */
+ public static CharSequence getDisplayNameForRestrictedNumber(Context context) {
+ if (isVerizon(context)) {
+ return context.getString(R.string.private_num_verizon);
+ } else {
+ return context.getString(R.string.private_num_non_verizon);
+ }
+ }
+}
diff --git a/java/com/android/dialer/phonenumberutil/res/values/strings.xml b/java/com/android/dialer/phonenumberutil/res/values/strings.xml
new file mode 100644
index 000000000..f31883ef6
--- /dev/null
+++ b/java/com/android/dialer/phonenumberutil/res/values/strings.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+
+<resources>
+ <!-- String used to display calls from private numbers in the call log and in call UI. For
+ example, if the user gets an incoming phone call from an unknown number, the caller ID will
+ read "Restricted". We only show this string if the user is on the Verizon network. -->
+ <string name="private_num_verizon">Restricted</string>
+
+ <!-- String used to display calls from private numbers in the call log. For example, if the user
+ gets an incoming phone call from an unknown number, the caller ID will read "Private number".
+ We only show this string if the user is not on the Verizon network. -->
+ <string name="private_num_non_verizon">Private number</string>
+</resources>
diff --git a/java/com/android/dialer/proguard/UsedByReflection.java b/java/com/android/dialer/proguard/UsedByReflection.java
new file mode 100644
index 000000000..200c33ed8
--- /dev/null
+++ b/java/com/android/dialer/proguard/UsedByReflection.java
@@ -0,0 +1,34 @@
+/*
+ * 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.dialer.proguard;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the class, constructor, method or field is used for reflection and therefore cannot
+ * be removed by tools like ProGuard. Use the value parameter to mention a file that uses the
+ * component marked as UsedByReflection.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
+public @interface UsedByReflection {
+
+ String value();
+}
diff --git a/java/com/android/dialer/protos/ProtoParsers.java b/java/com/android/dialer/protos/ProtoParsers.java
new file mode 100644
index 000000000..7dfbc7c5e
--- /dev/null
+++ b/java/com/android/dialer/protos/ProtoParsers.java
@@ -0,0 +1,167 @@
+/*
+ * 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.dialer.protos;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import com.android.dialer.common.Assert;
+import com.google.protobuf.nano.CodedOutputByteBufferNano;
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+import com.google.protobuf.nano.MessageNano;
+import java.io.IOException;
+
+/** Useful methods for using Protocol Buffers with Android. */
+public final class ProtoParsers {
+
+ private ProtoParsers() {}
+
+ /** Retrieve a proto from a Bundle */
+ @SuppressWarnings("unchecked") // We want to eventually optimize away parser classes, so cast
+ public static <T extends MessageNano> T get(Bundle bundle, String key, T defaultInstance)
+ throws InvalidProtocolBufferNanoException {
+ InternalDontUse parcelable = bundle.getParcelable(key);
+ return (T) parcelable.getMessageUnsafe(defaultInstance);
+ }
+
+ /**
+ * Retrieve a proto from a trusted bundle
+ *
+ * @throws RuntimeException if the proto cannot be parsed
+ */
+ public static <T extends MessageNano> T getFromInstanceState(
+ Bundle bundle, String key, T defaultInstance) {
+ try {
+ return get(bundle, key, defaultInstance);
+ } catch (InvalidProtocolBufferNanoException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Stores a proto in a Bundle, for later retrieval by {@link #get(Bundle, String, MessageNano)} or
+ * {@link #getFromInstanceState(Bundle, String, MessageNano)}.
+ */
+ public static void put(Bundle bundle, String key, MessageNano message) {
+ bundle.putParcelable(key, new InternalDontUse<>(null, message));
+ }
+
+ /**
+ * Stores a proto in an Intent, for later retrieval by {@link #get(Bundle, String, MessageNano)}.
+ * Needs separate method because Intent has similar to but different API than Bundle.
+ */
+ public static void put(Intent intent, String key, MessageNano message) {
+ intent.putExtra(key, new InternalDontUse<>(null, message));
+ }
+
+ /** Returns a {@linkplain Parcelable} representation of this protobuf message. */
+ public static <T extends MessageNano> ParcelableProto<T> asParcelable(T message) {
+ return new InternalDontUse<>(null, message);
+ }
+
+ /**
+ * A protobuf message that can be stored in a {@link Parcel}.
+ *
+ * <p><b>Note:</b> This <code>Parcelable</code> can only be used in single app. Attempting to send
+ * it to another app through an <code>Intent</code> will result in an exception due to Proguard
+ * obfusation when the target application attempts to load the <code>ParcelableProto</code> class.
+ */
+ public interface ParcelableProto<T extends MessageNano> extends Parcelable {
+ /**
+ * @throws IllegalStateException if the parceled data does not correspond to the defaultInstance
+ * type.
+ */
+ T getMessage(T defaultInstance);
+ }
+
+ /** Public because of Parcelable requirements. Do not use. */
+ public static final class InternalDontUse<T extends MessageNano> implements ParcelableProto<T> {
+ /* One of these two fields is always populated - since the bytes field never escapes this
+ * object, there is no risk of concurrent modification by multiple threads, and volatile
+ * is sufficient to be thread-safe. */
+ private volatile byte[] bytes;
+ private volatile T message;
+
+ /**
+ * Ideally, we would have type safety here. However, a static field {@link Creator} is required
+ * by {@link Parcelable}. Static fields are inherently not type safe, since only 1 exists per
+ * class (rather than 1 per type).
+ */
+ public static final Parcelable.Creator<InternalDontUse<?>> CREATOR =
+ new Creator<InternalDontUse<?>>() {
+ @Override
+ public InternalDontUse<?> createFromParcel(Parcel parcel) {
+ int serializedSize = parcel.readInt();
+ byte[] array = new byte[serializedSize];
+ parcel.readByteArray(array);
+ return new InternalDontUse<>(array, null);
+ }
+
+ @Override
+ public InternalDontUse<?>[] newArray(int i) {
+ return new InternalDontUse[i];
+ }
+ };
+
+ private InternalDontUse(byte[] bytes, T message) {
+ Assert.checkArgument(bytes != null || message != null);
+ this.bytes = bytes;
+ this.message = message;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ if (bytes == null) {
+ final byte[] flatArray = new byte[message.getSerializedSize()];
+ try {
+ message.writeTo(CodedOutputByteBufferNano.newInstance(flatArray));
+ bytes = flatArray;
+ } catch (IOException impossible) {
+ throw new AssertionError(impossible);
+ }
+ }
+ parcel.writeInt(bytes.length);
+ parcel.writeByteArray(bytes);
+ }
+
+ @Override
+ public T getMessage(T defaultInstance) {
+ try {
+ // The proto should never be invalid if it came from our application, so if it is, throw.
+ return getMessageUnsafe(defaultInstance);
+ } catch (InvalidProtocolBufferNanoException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked") // We're being deserialized, so there's no real type safety
+ T getMessageUnsafe(T defaultInstance) throws InvalidProtocolBufferNanoException {
+ // There's a risk that we'll double-parse the bytes, but that's OK, because it'll end up
+ // as the same immutable object anyway.
+ if (message == null) {
+ message = MessageNano.mergeFrom(defaultInstance, bytes);
+ }
+ return message;
+ }
+ }
+}
diff --git a/java/com/android/dialer/shortcuts/AndroidManifest.xml b/java/com/android/dialer/shortcuts/AndroidManifest.xml
new file mode 100644
index 000000000..e731a3e68
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<!--
+ ~ 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
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.dialer.shortcuts">
+
+ <uses-sdk
+ android:minSdkVersion="23"
+ android:targetSdkVersion="25"/>
+
+ <application>
+
+ <service
+ android:exported="false"
+ android:name=".PeriodicJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE"/>
+
+ <!--
+ Comments for attributes in CallContactActivity:
+ taskAffinity="" -> Open the dialog without opening the dialer app behind it
+ noHistory="true" -> Navigating away finishes activity
+ excludeFromRecents="true" -> Don't show in "recent apps" screen
+
+ We do not export this activity and do not declare an intent filter as a security precaution
+ so that apps other than the dialer cannot attempt to make phone calls using it.
+ -->
+ <activity
+ android:name=".CallContactActivity"
+ android:taskAffinity=""
+ android:noHistory="true"
+ android:excludeFromRecents="true"
+ android:label=""
+ android:exported="false"
+ android:theme="@style/CallContactsTheme"/>
+
+ </application>
+
+</manifest>
diff --git a/java/com/android/dialer/shortcuts/AutoValue_DialerShortcut.java b/java/com/android/dialer/shortcuts/AutoValue_DialerShortcut.java
new file mode 100644
index 000000000..ef995c816
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/AutoValue_DialerShortcut.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2017 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.dialer.shortcuts;
+
+import android.support.annotation.NonNull;
+import javax.annotation.Generated;
+
+@Generated("com.google.auto.value.processor.AutoValueProcessor")
+ final class AutoValue_DialerShortcut extends DialerShortcut {
+
+ private final long contactId;
+ private final String lookupKey;
+ private final String displayName;
+ private final int rank;
+
+ private AutoValue_DialerShortcut(
+ long contactId,
+ String lookupKey,
+ String displayName,
+ int rank) {
+ this.contactId = contactId;
+ this.lookupKey = lookupKey;
+ this.displayName = displayName;
+ this.rank = rank;
+ }
+
+ @Override
+ long getContactId() {
+ return contactId;
+ }
+
+ @NonNull
+ @Override
+ String getLookupKey() {
+ return lookupKey;
+ }
+
+ @NonNull
+ @Override
+ String getDisplayName() {
+ return displayName;
+ }
+
+ @Override
+ int getRank() {
+ return rank;
+ }
+
+ @Override
+ public String toString() {
+ return "DialerShortcut{"
+ + "contactId=" + contactId + ", "
+ + "lookupKey=" + lookupKey + ", "
+ + "displayName=" + displayName + ", "
+ + "rank=" + rank
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof DialerShortcut) {
+ DialerShortcut that = (DialerShortcut) o;
+ return (this.contactId == that.getContactId())
+ && (this.lookupKey.equals(that.getLookupKey()))
+ && (this.displayName.equals(that.getDisplayName()))
+ && (this.rank == that.getRank());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 1;
+ h *= 1000003;
+ h ^= (this.contactId >>> 32) ^ this.contactId;
+ h *= 1000003;
+ h ^= this.lookupKey.hashCode();
+ h *= 1000003;
+ h ^= this.displayName.hashCode();
+ h *= 1000003;
+ h ^= this.rank;
+ return h;
+ }
+
+ static final class Builder extends DialerShortcut.Builder {
+ private Long contactId;
+ private String lookupKey;
+ private String displayName;
+ private Integer rank;
+ Builder() {
+ }
+ private Builder(DialerShortcut source) {
+ this.contactId = source.getContactId();
+ this.lookupKey = source.getLookupKey();
+ this.displayName = source.getDisplayName();
+ this.rank = source.getRank();
+ }
+ @Override
+ DialerShortcut.Builder setContactId(long contactId) {
+ this.contactId = contactId;
+ return this;
+ }
+ @Override
+ DialerShortcut.Builder setLookupKey(String lookupKey) {
+ this.lookupKey = lookupKey;
+ return this;
+ }
+ @Override
+ DialerShortcut.Builder setDisplayName(String displayName) {
+ this.displayName = displayName;
+ return this;
+ }
+ @Override
+ DialerShortcut.Builder setRank(int rank) {
+ this.rank = rank;
+ return this;
+ }
+ @Override
+ DialerShortcut build() {
+ String missing = "";
+ if (this.contactId == null) {
+ missing += " contactId";
+ }
+ if (this.lookupKey == null) {
+ missing += " lookupKey";
+ }
+ if (this.displayName == null) {
+ missing += " displayName";
+ }
+ if (this.rank == null) {
+ missing += " rank";
+ }
+ if (!missing.isEmpty()) {
+ throw new IllegalStateException("Missing required properties:" + missing);
+ }
+ return new AutoValue_DialerShortcut(
+ this.contactId,
+ this.lookupKey,
+ this.displayName,
+ this.rank);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/java/com/android/dialer/shortcuts/CallContactActivity.java b/java/com/android/dialer/shortcuts/CallContactActivity.java
new file mode 100644
index 000000000..1e9a01b39
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/CallContactActivity.java
@@ -0,0 +1,133 @@
+/*
+ * 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.dialer.shortcuts;
+
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+import android.widget.Toast;
+import com.android.dialer.callintent.nano.CallInitiationType;
+import com.android.dialer.callintent.nano.CallSpecificAppData;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.interactions.PhoneNumberInteraction;
+import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorCode;
+import com.android.dialer.util.TransactionSafeActivity;
+
+/**
+ * Invisible activity launched when a shortcut is selected by user. Calls a contact based on URI.
+ */
+public class CallContactActivity extends TransactionSafeActivity
+ implements PhoneNumberInteraction.DisambigDialogDismissedListener,
+ PhoneNumberInteraction.InteractionErrorListener,
+ ActivityCompat.OnRequestPermissionsResultCallback {
+
+ private static final String CONTACT_URI_KEY = "uri_key";
+
+ private Uri contactUri;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if ("com.android.dialer.shortcuts.CALL_CONTACT".equals(getIntent().getAction())) {
+ if (Shortcuts.areDynamicShortcutsEnabled(this)) {
+ LogUtil.i("CallContactActivity.onCreate", "shortcut clicked");
+ contactUri = getIntent().getData();
+ makeCall();
+ } else {
+ LogUtil.i("CallContactActivity.onCreate", "dynamic shortcuts disabled");
+ finish();
+ }
+ }
+ }
+
+ private void makeCall() {
+ CallSpecificAppData callSpecificAppData = new CallSpecificAppData();
+ callSpecificAppData.callInitiationType = CallInitiationType.Type.LAUNCHER_SHORTCUT;
+ PhoneNumberInteraction.startInteractionForPhoneCall(
+ this, contactUri, false /* isVideoCall */, callSpecificAppData);
+ }
+
+ @Override
+ public void onDisambigDialogDismissed() {
+ finish();
+ }
+
+ @Override
+ public void interactionError(@InteractionErrorCode int interactionErrorCode) {
+ // Note: There is some subtlety to how contact lookup keys work that make it difficult to
+ // distinguish the case of the contact missing from the case of the a contact not having a
+ // number. For example, if a contact's phone number is deleted, subsequent lookups based on
+ // lookup key will actually return no results because the phone number was part of the
+ // lookup key. In this case, it would be inaccurate to say the contact can't be found though, so
+ // in all cases we just say the contact can't be found or the contact doesn't have a number.
+ switch (interactionErrorCode) {
+ case InteractionErrorCode.CONTACT_NOT_FOUND:
+ case InteractionErrorCode.CONTACT_HAS_NO_NUMBER:
+ Toast.makeText(
+ this,
+ R.string.dialer_shortcut_contact_not_found_or_has_no_number,
+ Toast.LENGTH_SHORT)
+ .show();
+ break;
+ case InteractionErrorCode.USER_LEAVING_ACTIVITY:
+ case InteractionErrorCode.OTHER_ERROR:
+ default:
+ // If the user is leaving the activity or the error code was "other" there's no useful
+ // information to display but we still need to finish this invisible activity.
+ break;
+ }
+ finish();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(CONTACT_URI_KEY, contactUri);
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ if (savedInstanceState == null) {
+ return;
+ }
+ contactUri = savedInstanceState.getParcelable(CONTACT_URI_KEY);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, String[] permissions, int[] grantResults) {
+ switch (requestCode) {
+ case PhoneNumberInteraction.REQUEST_READ_CONTACTS:
+ {
+ // If request is cancelled, the result arrays are empty.
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ makeCall();
+ } else {
+ Toast.makeText(this, R.string.dialer_shortcut_no_permissions, Toast.LENGTH_SHORT)
+ .show();
+ }
+ finish();
+ break;
+ }
+ default:
+ throw new IllegalStateException("Unsupported request code: " + requestCode);
+ }
+ }
+}
diff --git a/java/com/android/dialer/shortcuts/DialerShortcut.java b/java/com/android/dialer/shortcuts/DialerShortcut.java
new file mode 100644
index 000000000..f2fb3301a
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/DialerShortcut.java
@@ -0,0 +1,190 @@
+/*
+ * 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.dialer.shortcuts;
+
+import android.annotation.TargetApi;
+import android.content.pm.ShortcutInfo;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.provider.ContactsContract.Contacts;
+import android.support.annotation.NonNull;
+
+
+/**
+ * Convenience data structure.
+ *
+ * <p>This differs from {@link ShortcutInfo} in that it doesn't hold an icon or intent, and provides
+ * convenience methods for doing things like constructing labels.
+ */
+@TargetApi(VERSION_CODES.N_MR1) // Shortcuts introduced in N MR1
+
+abstract class DialerShortcut {
+
+ /** Marker value indicates that shortcut has no setRank. Used by pinned shortcuts. */
+ static final int NO_RANK = -1;
+
+ /**
+ * Contact ID from contacts provider. Note that this a numeric row ID from the
+ * ContactsContract.Contacts._ID column.
+ */
+ abstract long getContactId();
+
+ /**
+ * Lookup key from contacts provider. An example lookup key is: "0r8-47392D". This is the value
+ * from ContactsContract.Contacts.LOOKUP_KEY.
+ */
+ @NonNull
+ abstract String getLookupKey();
+
+ /** Display name from contacts provider. */
+ @NonNull
+ abstract String getDisplayName();
+
+ /**
+ * Rank for dynamic shortcuts. This value should be positive or {@link #NO_RANK}.
+ *
+ * <p>For floating shortcuts (pinned shortcuts with no corresponding dynamic shortcut), setRank
+ * has no meaning and the setRank may be set to {@link #NO_RANK}.
+ */
+ abstract int getRank();
+
+ /** The short label for the shortcut. Used when pinning shortcuts, for example. */
+ @NonNull
+ String getShortLabel() {
+ // Be sure to update getDisplayNameFromShortcutInfo when updating this.
+ return getDisplayName();
+ }
+
+ /**
+ * The long label for the shortcut. Used for shortcuts displayed when pressing and holding the app
+ * launcher icon, for example.
+ */
+ @NonNull
+ String getLongLabel() {
+ return getDisplayName();
+ }
+
+ /** The display name for the provided shortcut. */
+ static String getDisplayNameFromShortcutInfo(ShortcutInfo shortcutInfo) {
+ return shortcutInfo.getShortLabel().toString();
+ }
+
+ /**
+ * The id used to identify launcher shortcuts. Used for updating/deleting shortcuts.
+ *
+ * <p>Lookup keys are used for shortcut IDs. See {@link #getLookupKey()}.
+ *
+ * <p>If you change this, you probably also need to change {@link #getLookupKeyFromShortcutInfo}.
+ */
+ @NonNull
+ String getShortcutId() {
+ return getLookupKey();
+ }
+
+ /**
+ * Returns the contact lookup key from the provided {@link ShortcutInfo}.
+ *
+ * <p>Lookup keys are used for shortcut IDs. See {@link #getLookupKey()}.
+ */
+ @NonNull
+ static String getLookupKeyFromShortcutInfo(@NonNull ShortcutInfo shortcutInfo) {
+ return shortcutInfo.getId(); // Lookup keys are used for shortcut IDs.
+ }
+
+ /**
+ * Returns the lookup URI from the provided {@link ShortcutInfo}.
+ *
+ * <p>Lookup URIs are constructed from lookup key and contact ID. Here is an example lookup URI
+ * where lookup key is "0r8-47392D" and contact ID is 8:
+ *
+ * <p>"content://com.android.contacts/contacts/lookup/0r8-47392D/8"
+ */
+ @NonNull
+ static Uri getLookupUriFromShortcutInfo(@NonNull ShortcutInfo shortcutInfo) {
+ long contactId =
+ shortcutInfo.getIntent().getLongExtra(ShortcutInfoFactory.EXTRA_CONTACT_ID, -1);
+ if (contactId == -1) {
+ throw new IllegalStateException("No contact ID found for shortcut: " + shortcutInfo.getId());
+ }
+ String lookupKey = getLookupKeyFromShortcutInfo(shortcutInfo);
+ return Contacts.getLookupUri(contactId, lookupKey);
+ }
+
+ /**
+ * Contacts provider URI which uses the contact lookup key.
+ *
+ * <p>Lookup URIs are constructed from lookup key and contact ID. Here is an example lookup URI
+ * where lookup key is "0r8-47392D" and contact ID is 8:
+ *
+ * <p>"content://com.android.contacts/contacts/lookup/0r8-47392D/8"
+ */
+ @NonNull
+ Uri getLookupUri() {
+ return Contacts.getLookupUri(getContactId(), getLookupKey());
+ }
+
+ /**
+ * Given an existing shortcut with the same shortcut ID, returns true if the existing shortcut
+ * needs to be updated, e.g. if the contact's name or rank has changed.
+ *
+ * <p>Does not detect photo updates.
+ */
+ boolean needsUpdate(@NonNull ShortcutInfo oldInfo) {
+ if (this.getRank() != NO_RANK && oldInfo.getRank() != this.getRank()) {
+ return true;
+ }
+ if (!oldInfo.getShortLabel().equals(this.getShortLabel())) {
+ return true;
+ }
+ if (!oldInfo.getLongLabel().equals(this.getLongLabel())) {
+ return true;
+ }
+ return false;
+ }
+
+ static Builder builder() {
+ return new AutoValue_DialerShortcut.Builder().setRank(NO_RANK);
+ }
+
+
+ abstract static class Builder {
+
+ /**
+ * Sets the contact ID. This should be a value from the contact provider's Contact._ID column.
+ */
+ abstract Builder setContactId(long value);
+
+ /**
+ * Sets the lookup key. This should be a contact lookup key as provided by the contact provider.
+ */
+ abstract Builder setLookupKey(@NonNull String value);
+
+ /** Sets the display name. This should be a value provided by the contact provider. */
+ abstract Builder setDisplayName(@NonNull String value);
+
+ /**
+ * Sets the rank for the shortcut, used for ordering dynamic shortcuts. This is required for
+ * dynamic shortcuts but unused for floating shortcuts because rank has no meaning for floating
+ * shortcuts. (Floating shortcuts are shortcuts which are pinned but have no corresponding
+ * dynamic shortcut.)
+ */
+ abstract Builder setRank(int value);
+
+ /** Builds the immutable {@link DialerShortcut} object from this builder. */
+ abstract DialerShortcut build();
+ }
+}
diff --git a/java/com/android/dialer/shortcuts/DynamicShortcuts.java b/java/com/android/dialer/shortcuts/DynamicShortcuts.java
new file mode 100644
index 000000000..be9e088e1
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/DynamicShortcuts.java
@@ -0,0 +1,243 @@
+/*
+ * 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.dialer.shortcuts;
+
+import android.Manifest;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import android.support.v4.content.ContextCompat;
+import android.util.ArrayMap;
+import com.android.contacts.common.list.ContactEntry;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Handles refreshing of dialer dynamic shortcuts.
+ *
+ * <p>Dynamic shortcuts are the list of shortcuts which is accessible by tapping and holding the
+ * dialer launcher icon from the app drawer or a home screen.
+ *
+ * <p>Dynamic shortcuts are refreshed whenever the dialtacts activity detects changes to favorites
+ * tiles. This class compares the newly updated favorites tiles to the existing list of (previously
+ * published) dynamic shortcuts to compute a delta, which consists of lists of shortcuts which need
+ * to be updated, added, or deleted.
+ *
+ * <p>Dynamic shortcuts should mirror (in order) the contacts displayed in the "tiled favorites" tab
+ * of the dialer application. When selecting a dynamic shortcut, the behavior should be the same as
+ * if the user had tapped on the contact from the tiled favorites tab. Specifically, if the user has
+ * more than one phone number, a number picker should be displayed, and otherwise the contact should
+ * be called directly.
+ *
+ * <p>Note that an icon change by itself does not trigger a shortcut update, because it is not
+ * possible to detect an icon update and we don't want to constantly force update icons, because
+ * that is an expensive operation which requires storage I/O.
+ *
+ * <p>However, the job scheduler uses {@link #updateIcons()} to makes sure icons are forcefully
+ * updated periodically (about once a day).
+ *
+ */
+@TargetApi(VERSION_CODES.N_MR1) // Shortcuts introduced in N MR1
+final class DynamicShortcuts {
+
+ private static final int MAX_DYNAMIC_SHORTCUTS = 3;
+
+ private static class Delta {
+
+ final Map<String, DialerShortcut> shortcutsToUpdateById = new ArrayMap<>();
+ final List<String> shortcutIdsToRemove = new ArrayList<>();
+ final Map<String, DialerShortcut> shortcutsToAddById = new ArrayMap<>();
+ }
+
+ private final Context context;
+ private final ShortcutInfoFactory shortcutInfoFactory;
+
+ DynamicShortcuts(@NonNull Context context, IconFactory iconFactory) {
+ this.context = context;
+ this.shortcutInfoFactory = new ShortcutInfoFactory(context, iconFactory);
+ }
+
+ /**
+ * Performs a "complete refresh" of dynamic shortcuts. This is done by comparing the provided
+ * contact information with the existing dynamic shortcuts in order to compute a delta which
+ * contains shortcuts which should be added, updated, or removed.
+ *
+ * <p>If the delta is non-empty, it is applied by making appropriate calls to the {@link
+ * ShortcutManager} system service.
+ *
+ * <p>This is a slow blocking call which performs file I/O and should not be performed on the main
+ * thread.
+ */
+ @WorkerThread
+ public void refresh(List<ContactEntry> contacts) {
+ Assert.isWorkerThread();
+ LogUtil.enterBlock("DynamicShortcuts.refresh");
+
+ ShortcutManager shortcutManager = getShortcutManager(context);
+
+ if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
+ != PackageManager.PERMISSION_GRANTED) {
+ LogUtil.i("DynamicShortcuts.refresh", "no contact permissions");
+ shortcutManager.removeAllDynamicShortcuts();
+ return;
+ }
+
+ // Fill the available shortcuts with dynamic shortcuts up to a maximum of 3 dynamic shortcuts.
+ int numDynamicShortcutsToCreate =
+ Math.min(
+ MAX_DYNAMIC_SHORTCUTS,
+ shortcutManager.getMaxShortcutCountPerActivity()
+ - shortcutManager.getManifestShortcuts().size());
+
+ Map<String, DialerShortcut> newDynamicShortcutsById =
+ new ArrayMap<>(numDynamicShortcutsToCreate);
+ int rank = 0;
+ for (ContactEntry entry : contacts) {
+ if (newDynamicShortcutsById.size() >= numDynamicShortcutsToCreate) {
+ break;
+ }
+
+ DialerShortcut shortcut =
+ DialerShortcut.builder()
+ .setContactId(entry.id)
+ .setLookupKey(entry.lookupKey)
+ .setDisplayName(entry.getPreferredDisplayName())
+ .setRank(rank++)
+ .build();
+ newDynamicShortcutsById.put(shortcut.getShortcutId(), shortcut);
+ }
+
+ List<ShortcutInfo> oldDynamicShortcuts = new ArrayList<>(shortcutManager.getDynamicShortcuts());
+ Delta delta = computeDelta(oldDynamicShortcuts, newDynamicShortcutsById);
+ applyDelta(delta);
+ }
+
+ /**
+ * Forces an update of all dynamic shortcut icons. This should only be done from job scheduler as
+ * updating icons requires storage I/O.
+ */
+ @WorkerThread
+ void updateIcons() {
+ Assert.isWorkerThread();
+ LogUtil.enterBlock("DynamicShortcuts.updateIcons");
+
+ if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
+ != PackageManager.PERMISSION_GRANTED) {
+ LogUtil.i("DynamicShortcuts.updateIcons", "no contact permissions");
+ return;
+ }
+
+ ShortcutManager shortcutManager = getShortcutManager(context);
+
+ int maxDynamicShortcutsToCreate =
+ shortcutManager.getMaxShortcutCountPerActivity()
+ - shortcutManager.getManifestShortcuts().size();
+ int count = 0;
+
+ List<ShortcutInfo> newShortcuts = new ArrayList<>();
+ for (ShortcutInfo oldInfo : shortcutManager.getDynamicShortcuts()) {
+ newShortcuts.add(shortcutInfoFactory.withUpdatedIcon(oldInfo));
+ if (++count >= maxDynamicShortcutsToCreate) {
+ break;
+ }
+ }
+ LogUtil.i("DynamicShortcuts.updateIcons", "updating %d shortcut icons", newShortcuts.size());
+ shortcutManager.setDynamicShortcuts(newShortcuts);
+ }
+
+ @NonNull
+ private Delta computeDelta(
+ @NonNull List<ShortcutInfo> oldDynamicShortcuts,
+ @NonNull Map<String, DialerShortcut> newDynamicShortcutsById) {
+ Delta delta = new Delta();
+ if (oldDynamicShortcuts.isEmpty()) {
+ delta.shortcutsToAddById.putAll(newDynamicShortcutsById);
+ return delta;
+ }
+
+ for (ShortcutInfo oldInfo : oldDynamicShortcuts) {
+ // Check to see if the new shortcut list contains the existing shortcut.
+ DialerShortcut newShortcut = newDynamicShortcutsById.get(oldInfo.getId());
+ if (newShortcut != null) {
+ if (newShortcut.needsUpdate(oldInfo)) {
+ LogUtil.i("DynamicShortcuts.computeDelta", "contact updated");
+ delta.shortcutsToUpdateById.put(oldInfo.getId(), newShortcut);
+ } // else the shortcut hasn't changed, nothing to do to it
+ } else {
+ // The old shortcut is not in the new shortcut list, remove it.
+ LogUtil.i("DynamicShortcuts.computeDelta", "contact removed");
+ delta.shortcutIdsToRemove.add(oldInfo.getId());
+ }
+ }
+
+ // Add any new shortcuts that were not in the old shortcuts.
+ for (Entry<String, DialerShortcut> entry : newDynamicShortcutsById.entrySet()) {
+ String newId = entry.getKey();
+ DialerShortcut newShortcut = entry.getValue();
+ if (!containsShortcut(oldDynamicShortcuts, newId)) {
+ // The new shortcut was not found in the old shortcut list, so add it.
+ LogUtil.i("DynamicShortcuts.computeDelta", "contact added");
+ delta.shortcutsToAddById.put(newId, newShortcut);
+ }
+ }
+ return delta;
+ }
+
+ private void applyDelta(@NonNull Delta delta) {
+ ShortcutManager shortcutManager = getShortcutManager(context);
+ // Must perform remove before performing add to avoid adding more than supported by system.
+ if (!delta.shortcutIdsToRemove.isEmpty()) {
+ shortcutManager.removeDynamicShortcuts(delta.shortcutIdsToRemove);
+ }
+ if (!delta.shortcutsToUpdateById.isEmpty()) {
+ // Note: This may update pinned shortcuts as well. Pinned shortcuts which are also dynamic
+ // are not updated by the pinned shortcut logic. The reason that they are updated here
+ // instead of in the pinned shortcut logic is because setRank is required and only available
+ // here.
+ shortcutManager.updateShortcuts(
+ shortcutInfoFactory.buildShortcutInfos(delta.shortcutsToUpdateById));
+ }
+ if (!delta.shortcutsToAddById.isEmpty()) {
+ shortcutManager.addDynamicShortcuts(
+ shortcutInfoFactory.buildShortcutInfos(delta.shortcutsToAddById));
+ }
+ }
+
+ private boolean containsShortcut(
+ @NonNull List<ShortcutInfo> shortcutInfos, @NonNull String shortcutId) {
+ for (ShortcutInfo oldInfo : shortcutInfos) {
+ if (oldInfo.getId().equals(shortcutId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static ShortcutManager getShortcutManager(Context context) {
+ //noinspection WrongConstant
+ return (ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE);
+ }
+}
diff --git a/java/com/android/dialer/shortcuts/IconFactory.java b/java/com/android/dialer/shortcuts/IconFactory.java
new file mode 100644
index 000000000..a8c4ada4e
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/IconFactory.java
@@ -0,0 +1,112 @@
+/*
+ * 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.dialer.shortcuts;
+
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
+import com.android.contacts.common.lettertiles.LetterTileDrawable;
+import com.android.dialer.common.Assert;
+import com.android.dialer.util.DrawableConverter;
+import java.io.InputStream;
+
+/** Constructs the icons for dialer shortcuts. */
+class IconFactory {
+
+ private final Context context;
+
+ IconFactory(@NonNull Context context) {
+ this.context = context;
+ }
+
+ /**
+ * Creates an icon for the provided {@link DialerShortcut}.
+ *
+ * <p>The icon is a circle which contains a photo of the contact associated with the shortcut, if
+ * available. If a photo is not available, a circular colored icon with a single letter is instead
+ * created, where the letter is the first letter of the contact's name. If the contact has no
+ * name, a default colored "anonymous" avatar is used.
+ *
+ * <p>These icons should match exactly the favorites tiles in the starred tab of the dialer
+ * application, except that they are circular instead of rectangular.
+ */
+ @WorkerThread
+ @NonNull
+ public Icon create(@NonNull DialerShortcut shortcut) {
+ Assert.isWorkerThread();
+
+ return create(shortcut.getLookupUri(), shortcut.getDisplayName(), shortcut.getLookupKey());
+ }
+
+ /** Same as {@link #create(DialerShortcut)}, but accepts a {@link ShortcutInfo}. */
+ @WorkerThread
+ @NonNull
+ public Icon create(@NonNull ShortcutInfo shortcutInfo) {
+ Assert.isWorkerThread();
+ return create(
+ DialerShortcut.getLookupUriFromShortcutInfo(shortcutInfo),
+ DialerShortcut.getDisplayNameFromShortcutInfo(shortcutInfo),
+ DialerShortcut.getLookupKeyFromShortcutInfo(shortcutInfo));
+ }
+
+ @WorkerThread
+ @NonNull
+ private Icon create(
+ @NonNull Uri lookupUri, @NonNull String displayName, @NonNull String lookupKey) {
+ Assert.isWorkerThread();
+
+ // In testing, there was no difference between high-res and thumbnail.
+ InputStream inputStream =
+ ContactsContract.Contacts.openContactPhotoInputStream(
+ context.getContentResolver(), lookupUri, false /* preferHighres */);
+
+ Drawable drawable;
+ if (inputStream == null) {
+ // No photo for contact; use a letter tile.
+ LetterTileDrawable letterTileDrawable = new LetterTileDrawable(context.getResources());
+ letterTileDrawable.setCanonicalDialerLetterTileDetails(
+ displayName, lookupKey, LetterTileDrawable.SHAPE_CIRCLE, LetterTileDrawable.TYPE_DEFAULT);
+ drawable = letterTileDrawable;
+ } else {
+ // There's a photo, create a circular drawable from it.
+ Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
+ drawable = createCircularDrawable(bitmap);
+ }
+ int iconSize =
+ context.getResources().getDimensionPixelSize(R.dimen.launcher_shortcut_icon_size);
+ return Icon.createWithBitmap(
+ DrawableConverter.drawableToBitmap(drawable, iconSize /* width */, iconSize /* height */));
+ }
+
+ @NonNull
+ private Drawable createCircularDrawable(@NonNull Bitmap bitmap) {
+ RoundedBitmapDrawable roundedBitmapDrawable =
+ RoundedBitmapDrawableFactory.create(context.getResources(), bitmap);
+ roundedBitmapDrawable.setCircular(true);
+ roundedBitmapDrawable.setAntiAlias(true);
+ return roundedBitmapDrawable;
+ }
+}
diff --git a/java/com/android/dialer/shortcuts/PeriodicJobService.java b/java/com/android/dialer/shortcuts/PeriodicJobService.java
new file mode 100644
index 000000000..62c9e37a0
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/PeriodicJobService.java
@@ -0,0 +1,118 @@
+/*
+ * 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.dialer.shortcuts;
+
+import android.annotation.TargetApi;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.v4.os.UserManagerCompat;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.constants.ScheduledJobIds;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * {@link JobService} which starts the periodic job to refresh dynamic and pinned shortcuts.
+ *
+ * <p>Only {@link #schedulePeriodicJob(Context)} should be used by callers.
+ */
+@TargetApi(VERSION_CODES.N_MR1) // Shortcuts introduced in N MR1
+public final class PeriodicJobService extends JobService {
+
+ private static final long REFRESH_PERIOD_MILLIS = TimeUnit.HOURS.toMillis(24);
+
+ private RefreshShortcutsTask refreshShortcutsTask;
+
+ /**
+ * Schedules the periodic job to refresh shortcuts. If called repeatedly, the job will just be
+ * rescheduled.
+ *
+ * <p>The job will not be scheduled if the build version is not at least N MR1 or if the user is
+ * locked.
+ */
+ @MainThread
+ public static void schedulePeriodicJob(@NonNull Context context) {
+ Assert.isMainThread();
+ LogUtil.enterBlock("PeriodicJobService.schedulePeriodicJob");
+
+ if (VERSION.SDK_INT >= VERSION_CODES.N_MR1 && UserManagerCompat.isUserUnlocked(context)) {
+ JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ if (jobScheduler.getPendingJob(ScheduledJobIds.SHORTCUT_PERIODIC_JOB) != null) {
+ LogUtil.i("PeriodicJobService.schedulePeriodicJob", "job already scheduled.");
+ return;
+ }
+ JobInfo jobInfo =
+ new JobInfo.Builder(
+ ScheduledJobIds.SHORTCUT_PERIODIC_JOB,
+ new ComponentName(context, PeriodicJobService.class))
+ .setPeriodic(REFRESH_PERIOD_MILLIS)
+ .setPersisted(true)
+ .setRequiresCharging(true)
+ .setRequiresDeviceIdle(true)
+ .build();
+ jobScheduler.schedule(jobInfo);
+ }
+ }
+
+ /** Cancels the periodic job. */
+ @MainThread
+ public static void cancelJob(@NonNull Context context) {
+ Assert.isMainThread();
+ LogUtil.enterBlock("PeriodicJobService.cancelJob");
+
+ context.getSystemService(JobScheduler.class).cancel(ScheduledJobIds.SHORTCUT_PERIODIC_JOB);
+ }
+
+ @Override
+ @MainThread
+ public boolean onStartJob(@NonNull JobParameters params) {
+ Assert.isMainThread();
+ LogUtil.enterBlock("PeriodicJobService.onStartJob");
+
+ if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
+ (refreshShortcutsTask = new RefreshShortcutsTask(this)).execute(params);
+ } else {
+ // It is possible for the job to have been scheduled on NMR1+ and then the system was
+ // downgraded to < NMR1. In this case, shortcuts are no longer supported so we cancel the job
+ // which creates them.
+ LogUtil.i("PeriodicJobService.onStartJob", "not running on NMR1, cancelling job");
+ cancelJob(this);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ @MainThread
+ public boolean onStopJob(@NonNull JobParameters params) {
+ Assert.isMainThread();
+ LogUtil.enterBlock("PeriodicJobService.onStopJob");
+
+ if (refreshShortcutsTask != null) {
+ refreshShortcutsTask.cancel(false /* mayInterruptIfRunning */);
+ }
+ return false;
+ }
+}
diff --git a/java/com/android/dialer/shortcuts/PinnedShortcuts.java b/java/com/android/dialer/shortcuts/PinnedShortcuts.java
new file mode 100644
index 000000000..bfcc3df81
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/PinnedShortcuts.java
@@ -0,0 +1,159 @@
+/*
+ * 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.dialer.shortcuts;
+
+import android.Manifest;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.provider.ContactsContract.Contacts;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import android.support.v4.content.ContextCompat;
+import android.util.ArrayMap;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Handles refreshing of dialer pinned shortcuts.
+ *
+ * <p>Pinned shortcuts are icons that the user has dragged to their home screen from the dialer
+ * application launcher shortcut menu, which is accessible by tapping and holding the dialer
+ * launcher icon from the app drawer or a home screen.
+ *
+ * <p>When refreshing pinned shortcuts, we check to make sure that pinned contact information is
+ * still up to date (e.g. photo and name). We also check to see if the contact has been deleted from
+ * the user's contacts, and if so, we disable the pinned shortcut.
+ *
+ */
+@TargetApi(VERSION_CODES.N_MR1) // Shortcuts introduced in N MR1
+final class PinnedShortcuts {
+
+ private static final String[] PROJECTION =
+ new String[] {
+ Contacts._ID, Contacts.DISPLAY_NAME_PRIMARY, Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
+ };
+
+ private static class Delta {
+
+ final List<String> shortcutIdsToDisable = new ArrayList<>();
+ final Map<String, DialerShortcut> shortcutsToUpdateById = new ArrayMap<>();
+ }
+
+ private final Context context;
+ private final ShortcutInfoFactory shortcutInfoFactory;
+
+ PinnedShortcuts(@NonNull Context context) {
+ this.context = context;
+ this.shortcutInfoFactory = new ShortcutInfoFactory(context, new IconFactory(context));
+ }
+
+ /**
+ * Performs a "complete refresh" of pinned shortcuts. This is done by (synchronously) querying for
+ * all contacts which currently have pinned shortcuts. The query results are used to compute a
+ * delta which contains a list of shortcuts which need to be updated (e.g. because of name/photo
+ * changes) or disabled (if contacts were deleted). Note that pinned shortcuts cannot be deleted
+ * programmatically and must be deleted by the user.
+ *
+ * <p>If the delta is non-empty, it is applied by making appropriate calls to the {@link
+ * ShortcutManager} system service.
+ *
+ * <p>This is a slow blocking call which performs file I/O and should not be performed on the main
+ * thread.
+ */
+ @WorkerThread
+ public void refresh() {
+ Assert.isWorkerThread();
+ LogUtil.enterBlock("PinnedShortcuts.refresh");
+
+ if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
+ != PackageManager.PERMISSION_GRANTED) {
+ LogUtil.i("PinnedShortcuts.refresh", "no contact permissions");
+ return;
+ }
+
+ Delta delta = new Delta();
+ ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
+ for (ShortcutInfo shortcutInfo : shortcutManager.getPinnedShortcuts()) {
+ if (shortcutInfo.isDeclaredInManifest()) {
+ // We never update/disable the manifest shortcut (the "create new contact" shortcut).
+ continue;
+ }
+ if (shortcutInfo.isDynamic()) {
+ // If the shortcut is both pinned and dynamic, let the logic which updates dynamic shortcuts
+ // handle the update. It would be problematic to try and apply the update here, because the
+ // setRank is nonsensical for pinned shortcuts and therefore could not be calculated.
+ continue;
+ }
+
+ String lookupKey = DialerShortcut.getLookupKeyFromShortcutInfo(shortcutInfo);
+ Uri lookupUri = DialerShortcut.getLookupUriFromShortcutInfo(shortcutInfo);
+
+ try (Cursor cursor =
+ context.getContentResolver().query(lookupUri, PROJECTION, null, null, null)) {
+
+ if (cursor == null || !cursor.moveToNext()) {
+ LogUtil.i("PinnedShortcuts.refresh", "contact disabled");
+ delta.shortcutIdsToDisable.add(shortcutInfo.getId());
+ continue;
+ }
+
+ // Note: The lookup key may have changed but we cannot refresh it because that would require
+ // changing the shortcut ID, which can only be accomplished with a remove and add; but
+ // pinned shortcuts cannot be added or removed.
+ DialerShortcut shortcut =
+ DialerShortcut.builder()
+ .setContactId(cursor.getLong(cursor.getColumnIndexOrThrow(Contacts._ID)))
+ .setLookupKey(lookupKey)
+ .setDisplayName(
+ cursor.getString(cursor.getColumnIndexOrThrow(Contacts.DISPLAY_NAME_PRIMARY)))
+ .build();
+
+ if (shortcut.needsUpdate(shortcutInfo)) {
+ LogUtil.i("PinnedShortcuts.refresh", "contact updated");
+ delta.shortcutsToUpdateById.put(shortcutInfo.getId(), shortcut);
+ }
+ }
+ }
+ applyDelta(delta);
+ }
+
+ private void applyDelta(@NonNull Delta delta) {
+ ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
+ String shortcutDisabledMessage =
+ context.getResources().getString(R.string.dialer_shortcut_disabled_message);
+ if (!delta.shortcutIdsToDisable.isEmpty()) {
+ shortcutManager.disableShortcuts(delta.shortcutIdsToDisable, shortcutDisabledMessage);
+ }
+ if (!delta.shortcutsToUpdateById.isEmpty()) {
+ // Note: This call updates both pinned and dynamic shortcuts, but the delta should contain
+ // no dynamic shortcuts.
+ if (!shortcutManager.updateShortcuts(
+ shortcutInfoFactory.buildShortcutInfos(delta.shortcutsToUpdateById))) {
+ LogUtil.i("PinnedShortcuts.applyDelta", "shortcutManager rate limited.");
+ }
+ }
+ }
+}
diff --git a/java/com/android/dialer/shortcuts/RefreshShortcutsTask.java b/java/com/android/dialer/shortcuts/RefreshShortcutsTask.java
new file mode 100644
index 000000000..086d1dc7a
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/RefreshShortcutsTask.java
@@ -0,0 +1,71 @@
+/*
+ * 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.dialer.shortcuts;
+
+import android.annotation.TargetApi;
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.os.AsyncTask;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+
+/** {@link AsyncTask} used by the periodic job service to refresh dynamic and pinned shortcuts. */
+@TargetApi(VERSION_CODES.N_MR1) // Shortcuts introduced in N MR1
+final class RefreshShortcutsTask extends AsyncTask<JobParameters, Void, JobParameters> {
+
+ private final JobService jobService;
+
+ RefreshShortcutsTask(@NonNull JobService jobService) {
+ this.jobService = jobService;
+ }
+
+ /** @param params array with length 1, provided from PeriodicJobService */
+ @Override
+ @NonNull
+ @WorkerThread
+ protected JobParameters doInBackground(JobParameters... params) {
+ Assert.isWorkerThread();
+ LogUtil.enterBlock("RefreshShortcutsTask.doInBackground");
+
+ // Dynamic shortcuts are refreshed from the UI but icons can become stale, so update them
+ // periodically using the job service.
+ //
+ // The reason that icons can become is stale is that there is no last updated timestamp for
+ // pictures; there is only a last updated timestamp for the entire contact row, which changes
+ // frequently (for example, when they are called their "times_contacted" is incremented).
+ // Relying on such a spuriously updated timestamp would result in too frequent shortcut updates,
+ // so instead we just allow the icon to become stale in the case that the contact's photo is
+ // updated, and then rely on the job service to periodically force update it.
+ new DynamicShortcuts(jobService, new IconFactory(jobService)).updateIcons(); // Blocking
+ new PinnedShortcuts(jobService).refresh(); // Blocking
+
+ return params[0];
+ }
+
+ @Override
+ @MainThread
+ protected void onPostExecute(JobParameters params) {
+ Assert.isMainThread();
+ LogUtil.enterBlock("RefreshShortcutsTask.onPostExecute");
+
+ jobService.jobFinished(params, false /* needsReschedule */);
+ }
+}
diff --git a/java/com/android/dialer/shortcuts/ShortcutInfoFactory.java b/java/com/android/dialer/shortcuts/ShortcutInfoFactory.java
new file mode 100644
index 000000000..cf780bbd7
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/ShortcutInfoFactory.java
@@ -0,0 +1,100 @@
+/*
+ * 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.dialer.shortcuts;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import com.android.dialer.common.Assert;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Creates {@link ShortcutInfo} objects (which are required by shortcut manager system service) from
+ * {@link DialerShortcut} objects (which are package-private convenience data structures).
+ *
+ * <p>The main work this factory does is create shortcut intents. It also delegates to the {@link
+ * IconFactory} to create icons.
+ */
+@TargetApi(VERSION_CODES.N_MR1) // Shortcuts introduced in N MR1
+final class ShortcutInfoFactory {
+
+ /** Key for the contact ID extra (a long) stored as part of the shortcut intent. */
+ static final String EXTRA_CONTACT_ID = "contactId";
+
+ private final Context context;
+ private final IconFactory iconFactory;
+
+ ShortcutInfoFactory(@NonNull Context context, IconFactory iconFactory) {
+ this.context = context;
+ this.iconFactory = iconFactory;
+ }
+
+ /**
+ * Builds a list {@link ShortcutInfo} objects from the provided collection of {@link
+ * DialerShortcut} objects. This primarily means setting the intent and adding the icon, which
+ * {@link DialerShortcut} objects do not hold.
+ */
+ @WorkerThread
+ @NonNull
+ List<ShortcutInfo> buildShortcutInfos(@NonNull Map<String, DialerShortcut> shortcutsById) {
+ Assert.isWorkerThread();
+ List<ShortcutInfo> shortcuts = new ArrayList<>(shortcutsById.size());
+ for (DialerShortcut shortcut : shortcutsById.values()) {
+ Intent intent = new Intent();
+ intent.setClassName(context, "com.android.dialer.shortcuts.CallContactActivity");
+ intent.setData(shortcut.getLookupUri());
+ intent.setAction("com.android.dialer.shortcuts.CALL_CONTACT");
+ intent.putExtra(EXTRA_CONTACT_ID, shortcut.getContactId());
+
+ ShortcutInfo.Builder shortcutInfo =
+ new ShortcutInfo.Builder(context, shortcut.getShortcutId())
+ .setIntent(intent)
+ .setShortLabel(shortcut.getShortLabel())
+ .setLongLabel(shortcut.getLongLabel())
+ .setIcon(iconFactory.create(shortcut));
+
+ if (shortcut.getRank() != DialerShortcut.NO_RANK) {
+ shortcutInfo.setRank(shortcut.getRank());
+ }
+ shortcuts.add(shortcutInfo.build());
+ }
+ return shortcuts;
+ }
+
+ /**
+ * Creates a copy of the provided {@link ShortcutInfo} but with an updated icon fetched from
+ * contacts provider.
+ */
+ @WorkerThread
+ @NonNull
+ ShortcutInfo withUpdatedIcon(ShortcutInfo info) {
+ Assert.isWorkerThread();
+ return new ShortcutInfo.Builder(context, info.getId())
+ .setIntent(info.getIntent())
+ .setShortLabel(info.getShortLabel())
+ .setLongLabel(info.getLongLabel())
+ .setRank(info.getRank())
+ .setIcon(iconFactory.create(info))
+ .build();
+ }
+}
diff --git a/java/com/android/dialer/shortcuts/ShortcutRefresher.java b/java/com/android/dialer/shortcuts/ShortcutRefresher.java
new file mode 100644
index 000000000..f5ff64874
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/ShortcutRefresher.java
@@ -0,0 +1,86 @@
+/*
+ * 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.dialer.shortcuts;
+
+import android.content.Context;
+import android.os.Build;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import com.android.contacts.common.list.ContactEntry;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.AsyncTaskExecutor;
+import com.android.dialer.common.AsyncTaskExecutors;
+import com.android.dialer.common.FallibleAsyncTask;
+import com.android.dialer.common.LogUtil;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Refreshes launcher shortcuts from UI components using provided list of contacts. */
+public final class ShortcutRefresher {
+
+ private static final AsyncTaskExecutor EXECUTOR = AsyncTaskExecutors.createThreadPoolExecutor();
+
+ /** Asynchronously updates launcher shortcuts using the provided list of contacts. */
+ @MainThread
+ public static void refresh(@NonNull Context context, List<ContactEntry> contacts) {
+ Assert.isMainThread();
+ Assert.isNotNull(context);
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) {
+ return;
+ }
+
+ if (!Shortcuts.areDynamicShortcutsEnabled(context)) {
+ return;
+ }
+
+ //noinspection unchecked
+ EXECUTOR.submit(Task.ID, new Task(context), new ArrayList<>(contacts));
+ }
+
+ private static final class Task extends FallibleAsyncTask<List<ContactEntry>, Void, Void> {
+ private static final String ID = "ShortcutRefresher.Task";
+
+ private final Context context;
+
+ Task(Context context) {
+ this.context = context;
+ }
+
+ /**
+ * @param params array containing exactly one element, the list of contacts from favorites
+ * tiles, ordered in tile order.
+ */
+ @SafeVarargs
+ @Override
+ @NonNull
+ @WorkerThread
+ protected final Void doInBackgroundFallible(List<ContactEntry>... params) {
+ Assert.isWorkerThread();
+ LogUtil.enterBlock("ShortcutRefresher.Task.doInBackground");
+
+ // Only dynamic shortcuts are maintained from UI components. Pinned shortcuts are maintained
+ // by the job scheduler. This is because a pinned contact may not necessarily still be in the
+ // favorites tiles, so refreshing it would require an additional database query. We don't want
+ // to incur the cost of that extra database query every time the favorites tiles change.
+ new DynamicShortcuts(context, new IconFactory(context)).refresh(params[0]); // Blocking
+
+ return null;
+ }
+ }
+}
diff --git a/java/com/android/dialer/shortcuts/ShortcutUsageReporter.java b/java/com/android/dialer/shortcuts/ShortcutUsageReporter.java
new file mode 100644
index 000000000..50130fc49
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/ShortcutUsageReporter.java
@@ -0,0 +1,132 @@
+/*
+ * 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.dialer.shortcuts;
+
+import android.Manifest;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ShortcutManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.PhoneLookup;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+import android.support.v4.content.ContextCompat;
+import android.text.TextUtils;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.AsyncTaskExecutor;
+import com.android.dialer.common.AsyncTaskExecutors;
+import com.android.dialer.common.LogUtil;
+
+/**
+ * Reports outgoing calls as shortcut usage.
+ *
+ * <p>Note that all outgoing calls are considered shortcut usage, no matter where they are initiated
+ * from (i.e. from anywhere in the dialer app, or even from other apps).
+ *
+ * <p>This allows launcher applications to provide users with shortcut suggestions, even if the user
+ * isn't already using shortcuts.
+ */
+@TargetApi(VERSION_CODES.N_MR1) // Shortcuts introduced in N_MR1
+public class ShortcutUsageReporter {
+
+ private static final AsyncTaskExecutor EXECUTOR = AsyncTaskExecutors.createThreadPoolExecutor();
+
+ /**
+ * Called when an outgoing call is added to the call list in order to report outgoing calls as
+ * shortcut usage. This should be called exactly once for each outgoing call.
+ *
+ * <p>Asynchronously queries the contacts database for the contact's lookup key which corresponds
+ * to the provided phone number, and uses that to report shortcut usage.
+ *
+ * @param context used to access ShortcutManager system service
+ * @param phoneNumber the phone number being called
+ */
+ @MainThread
+ public static void onOutgoingCallAdded(@NonNull Context context, @Nullable String phoneNumber) {
+ Assert.isMainThread();
+ Assert.isNotNull(context);
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1 || TextUtils.isEmpty(phoneNumber)) {
+ return;
+ }
+
+ EXECUTOR.submit(Task.ID, new Task(context), phoneNumber);
+ }
+
+ private static final class Task extends AsyncTask<String, Void, Void> {
+ private static final String ID = "ShortcutUsageReporter.Task";
+
+ private final Context context;
+
+ public Task(Context context) {
+ this.context = context;
+ }
+
+ /** @param phoneNumbers array with exactly one non-empty phone number */
+ @Override
+ @WorkerThread
+ protected Void doInBackground(@NonNull String... phoneNumbers) {
+ Assert.isWorkerThread();
+
+ String lookupKey = queryForLookupKey(phoneNumbers[0]);
+ if (!TextUtils.isEmpty(lookupKey)) {
+ LogUtil.i("ShortcutUsageReporter.backgroundLogUsage", "%s", lookupKey);
+ ShortcutManager shortcutManager =
+ (ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE);
+
+ // Note: There may not currently exist a shortcut with the provided key, but it is logged
+ // anyway, so that launcher applications at least have the information should the shortcut
+ // be created in the future.
+ shortcutManager.reportShortcutUsed(lookupKey);
+ }
+ return null;
+ }
+
+ @Nullable
+ @WorkerThread
+ private String queryForLookupKey(String phoneNumber) {
+ Assert.isWorkerThread();
+
+ if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
+ != PackageManager.PERMISSION_GRANTED) {
+ LogUtil.i("ShortcutUsageReporter.queryForLookupKey", "No contact permissions");
+ return null;
+ }
+
+ Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
+ try (Cursor cursor =
+ context
+ .getContentResolver()
+ .query(uri, new String[] {Contacts.LOOKUP_KEY}, null, null, null)) {
+
+ if (cursor == null || !cursor.moveToNext()) {
+ return null; // No contact for dialed number
+ }
+ // Arbitrarily use first result.
+ return cursor.getString(cursor.getColumnIndex(Contacts.LOOKUP_KEY));
+ }
+ }
+ }
+}
diff --git a/java/com/android/dialer/shortcuts/Shortcuts.java b/java/com/android/dialer/shortcuts/Shortcuts.java
new file mode 100644
index 000000000..b6a7fa82a
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/Shortcuts.java
@@ -0,0 +1,34 @@
+/*
+ * 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.dialer.shortcuts;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import com.android.dialer.common.ConfigProviderBindings;
+
+/** Checks if dynamic shortcuts should be enabled. */
+public class Shortcuts {
+
+ /** Key for boolean config value which determines whether or not to enable dynamic shortcuts. */
+ private static final String DYNAMIC_SHORTCUTS_ENABLED = "dynamic_shortcuts_enabled";
+
+ static boolean areDynamicShortcutsEnabled(@NonNull Context context) {
+ return ConfigProviderBindings.get(context).getBoolean(DYNAMIC_SHORTCUTS_ENABLED, true);
+ }
+
+ private Shortcuts() {}
+}
diff --git a/java/com/android/dialer/shortcuts/ShortcutsJobScheduler.java b/java/com/android/dialer/shortcuts/ShortcutsJobScheduler.java
new file mode 100644
index 000000000..4cfc4361c
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/ShortcutsJobScheduler.java
@@ -0,0 +1,48 @@
+/*
+ * 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.dialer.shortcuts;
+
+import android.content.Context;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+
+/**
+ * Schedules dialer shortcut jobs.
+ *
+ * <p>A {@link ConfigProvider} value controls whether the jobs which creates shortcuts should be
+ * scheduled or cancelled.
+ */
+public class ShortcutsJobScheduler {
+
+ @MainThread
+ public static void scheduleAllJobs(@NonNull Context context) {
+ LogUtil.enterBlock("ShortcutsJobScheduler.scheduleAllJobs");
+ Assert.isMainThread();
+
+ if (Shortcuts.areDynamicShortcutsEnabled(context)) {
+ LogUtil.i("ShortcutsJobScheduler.scheduleAllJobs", "enabling shortcuts");
+
+ PeriodicJobService.schedulePeriodicJob(context);
+ } else {
+ LogUtil.i("ShortcutsJobScheduler.scheduleAllJobs", "disabling shortcuts");
+
+ PeriodicJobService.cancelJob(context);
+ }
+ }
+}
diff --git a/java/com/android/dialer/shortcuts/res/drawable/ic_shortcut_add_contact.xml b/java/com/android/dialer/shortcuts/res/drawable/ic_shortcut_add_contact.xml
new file mode 100644
index 000000000..c06aec82f
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/res/drawable/ic_shortcut_add_contact.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:bottom="2dp"
+ android:left="2dp"
+ android:right="2dp"
+ android:top="2dp">
+ <shape android:shape="oval">
+ <size
+ android:height="44dp"
+ android:width="44dp"/>
+ <solid android:color="@color/shortcut_add_contact_background_color"/>
+ </shape>
+ </item>
+
+ <item
+ android:bottom="12dp"
+ android:left="10dp"
+ android:right="14dp"
+ android:top="12dp">
+ <bitmap android:src="@drawable/quantum_ic_person_add_white_24"
+ android:tint="@color/shortcut_add_contact_foreground_color"/>
+ </item>
+</layer-list>
diff --git a/java/com/android/dialer/shortcuts/res/values/colors.xml b/java/com/android/dialer/shortcuts/res/values/colors.xml
new file mode 100644
index 000000000..e20309b56
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources>
+ <color name="shortcut_add_contact_foreground_color">#2A56C6</color>
+ <color name="shortcut_add_contact_background_color">#f5f5f5</color>
+</resources>
diff --git a/java/com/android/dialer/shortcuts/res/values/dimens.xml b/java/com/android/dialer/shortcuts/res/values/dimens.xml
new file mode 100644
index 000000000..232125653
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources>
+ <dimen name="launcher_shortcut_icon_size">48dp</dimen>
+</resources>
diff --git a/java/com/android/dialer/shortcuts/res/values/strings.xml b/java/com/android/dialer/shortcuts/res/values/strings.xml
new file mode 100644
index 000000000..1e2c87f12
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/res/values/strings.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources>
+ <!-- Text to display in launcher shortcut for adding a new contact. Short version. [CHAR LIMIT=10] -->
+ <string name="dialer_shortcut_add_contact_short">New contact</string>
+
+ <!-- Text to display in launcher shortcut for adding a new contact. Long version. [CHAR LIMIT=25] -->
+ <string name="dialer_shortcut_add_contact_long">New contact</string>
+
+ <!-- Message to display when the user taps a pinned launcher shortcut (on a
+ homescreen) which has been disabled. A shortcut may be disabled if the
+ contact has been deleted or if it is invalid for some other reason. [CHAR LIMIT=70] -->
+ <string name="dialer_shortcut_disabled_message">Shortcut not working. Drag to remove.</string>
+
+ <!-- Error message to display when a tapping a shortcut fails because the specified contact can't
+ be found or doesn't have any phone numbers. [CHAR LIMIT=70] -->
+ <string name="dialer_shortcut_contact_not_found_or_has_no_number">Contact no longer available.</string>
+
+ <!-- Error message to display when a tapping a shortcut fails because contact permissions are
+ missing. [CHAR LIMIT=70] -->
+ <string name="dialer_shortcut_no_permissions">Cannot call without contact permissions.</string>
+
+</resources>
diff --git a/java/com/android/dialer/shortcuts/res/values/themes.xml b/java/com/android/dialer/shortcuts/res/values/themes.xml
new file mode 100644
index 000000000..085854d89
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/res/values/themes.xml
@@ -0,0 +1,39 @@
+<resources>
+ <!--
+ ~ 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
+ -->
+
+ <!-- CallContactsActivity is an invisible trampoline activity for launcher shortcuts to jump into
+ the calling activity. When the user taps a shortcut they will be taken to either the phone
+ number disambiguation dialog or directly into the incall UI via this activity, but this
+ activity itself should be completely transparent to the user.
+
+ Note that this must inherit from Theme.AppCompat. We inherit from Theme.AppCompat.Light so
+ that the colors of the disambiguation dialog match the colors when it is shown via the
+ favorites tiles tab. -->
+ <style name="CallContactsTheme" parent="Theme.AppCompat.Light">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:windowBackground">@null</item>
+ <item name="android:windowFrame">@null</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowAnimationStyle">@null</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:windowActionBar">false</item>
+ <item name="android:windowDisablePreview">true</item>
+ </style>
+
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/shortcuts/res/xml/shortcuts.xml b/java/com/android/dialer/shortcuts/res/xml/shortcuts.xml
new file mode 100644
index 000000000..5e8f58d1f
--- /dev/null
+++ b/java/com/android/dialer/shortcuts/res/xml/shortcuts.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
+ <shortcut
+ android:enabled="true"
+ android:icon="@drawable/ic_shortcut_add_contact"
+ android:shortcutId="dialer-shortcut-add-contact"
+ android:shortcutLongLabel="@string/dialer_shortcut_add_contact_long"
+ android:shortcutShortLabel="@string/dialer_shortcut_add_contact_short">
+
+ <intent
+ android:action="android.intent.action.INSERT"
+ android:data="content://com.android.contacts/contacts"
+ android:targetPackage="com.google.android.contacts"
+ android:targetClass="com.android.contacts.activities.CompactContactEditorActivity"/>
+ </shortcut>
+</shortcuts>
diff --git a/java/com/android/dialer/simulator/Simulator.java b/java/com/android/dialer/simulator/Simulator.java
new file mode 100644
index 000000000..78058a48f
--- /dev/null
+++ b/java/com/android/dialer/simulator/Simulator.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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.dialer.simulator;
+
+import android.content.Context;
+import android.view.ActionProvider;
+
+/** Used to add menu items to the Dialer menu to test the app using simulated calls and data. */
+public interface Simulator {
+ boolean shouldShow();
+
+ ActionProvider getActionProvider(Context context);
+}
diff --git a/java/com/android/dialer/simulator/impl/AndroidManifest.xml b/java/com/android/dialer/simulator/impl/AndroidManifest.xml
new file mode 100644
index 000000000..a30504d3f
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.dialer.simulator.impl">
+
+ <application>
+
+ <service
+ android:exported="true"
+ android:name=".SimulatorConnectionService"
+ android:permission="android.permission.BIND_CONNECTION_SERVICE">
+ <intent-filter>
+ <action android:name="android.telecomm.ConnectionService"/>
+ </intent-filter>
+ </service>
+
+ </application>
+
+</manifest>
diff --git a/java/com/android/dialer/simulator/impl/AutoValue_SimulatorCallLog_CallEntry.java b/java/com/android/dialer/simulator/impl/AutoValue_SimulatorCallLog_CallEntry.java
new file mode 100644
index 000000000..591819819
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/AutoValue_SimulatorCallLog_CallEntry.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2017 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.dialer.simulator.impl;
+
+import android.support.annotation.NonNull;
+import javax.annotation.Generated;
+
+@Generated("com.google.auto.value.processor.AutoValueProcessor")
+ final class AutoValue_SimulatorCallLog_CallEntry extends SimulatorCallLog.CallEntry {
+
+ private final String number;
+ private final int type;
+ private final int presentation;
+ private final long timeMillis;
+
+ private AutoValue_SimulatorCallLog_CallEntry(
+ String number,
+ int type,
+ int presentation,
+ long timeMillis) {
+ this.number = number;
+ this.type = type;
+ this.presentation = presentation;
+ this.timeMillis = timeMillis;
+ }
+
+ @NonNull
+ @Override
+ String getNumber() {
+ return number;
+ }
+
+ @Override
+ int getType() {
+ return type;
+ }
+
+ @Override
+ int getPresentation() {
+ return presentation;
+ }
+
+ @Override
+ long getTimeMillis() {
+ return timeMillis;
+ }
+
+ @Override
+ public String toString() {
+ return "CallEntry{"
+ + "number=" + number + ", "
+ + "type=" + type + ", "
+ + "presentation=" + presentation + ", "
+ + "timeMillis=" + timeMillis
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof SimulatorCallLog.CallEntry) {
+ SimulatorCallLog.CallEntry that = (SimulatorCallLog.CallEntry) o;
+ return (this.number.equals(that.getNumber()))
+ && (this.type == that.getType())
+ && (this.presentation == that.getPresentation())
+ && (this.timeMillis == that.getTimeMillis());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 1;
+ h *= 1000003;
+ h ^= this.number.hashCode();
+ h *= 1000003;
+ h ^= this.type;
+ h *= 1000003;
+ h ^= this.presentation;
+ h *= 1000003;
+ h ^= (this.timeMillis >>> 32) ^ this.timeMillis;
+ return h;
+ }
+
+ static final class Builder extends SimulatorCallLog.CallEntry.Builder {
+ private String number;
+ private Integer type;
+ private Integer presentation;
+ private Long timeMillis;
+ Builder() {
+ }
+ private Builder(SimulatorCallLog.CallEntry source) {
+ this.number = source.getNumber();
+ this.type = source.getType();
+ this.presentation = source.getPresentation();
+ this.timeMillis = source.getTimeMillis();
+ }
+ @Override
+ SimulatorCallLog.CallEntry.Builder setNumber(String number) {
+ this.number = number;
+ return this;
+ }
+ @Override
+ SimulatorCallLog.CallEntry.Builder setType(int type) {
+ this.type = type;
+ return this;
+ }
+ @Override
+ SimulatorCallLog.CallEntry.Builder setPresentation(int presentation) {
+ this.presentation = presentation;
+ return this;
+ }
+ @Override
+ SimulatorCallLog.CallEntry.Builder setTimeMillis(long timeMillis) {
+ this.timeMillis = timeMillis;
+ return this;
+ }
+ @Override
+ SimulatorCallLog.CallEntry build() {
+ String missing = "";
+ if (this.number == null) {
+ missing += " number";
+ }
+ if (this.type == null) {
+ missing += " type";
+ }
+ if (this.presentation == null) {
+ missing += " presentation";
+ }
+ if (this.timeMillis == null) {
+ missing += " timeMillis";
+ }
+ if (!missing.isEmpty()) {
+ throw new IllegalStateException("Missing required properties:" + missing);
+ }
+ return new AutoValue_SimulatorCallLog_CallEntry(
+ this.number,
+ this.type,
+ this.presentation,
+ this.timeMillis);
+ }
+ }
+
+}
diff --git a/java/com/android/dialer/simulator/impl/AutoValue_SimulatorContacts_Contact.java b/java/com/android/dialer/simulator/impl/AutoValue_SimulatorContacts_Contact.java
new file mode 100644
index 000000000..00295f359
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/AutoValue_SimulatorContacts_Contact.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2017 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.dialer.simulator.impl;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+import javax.annotation.Generated;
+
+@Generated("com.google.auto.value.processor.AutoValueProcessor")
+ final class AutoValue_SimulatorContacts_Contact extends SimulatorContacts.Contact {
+
+ private final String accountType;
+ private final String accountName;
+ private final String name;
+ private final boolean isStarred;
+ private final ByteArrayOutputStream photoStream;
+ private final List<SimulatorContacts.PhoneNumber> phoneNumbers;
+ private final List<SimulatorContacts.Email> emails;
+
+ private AutoValue_SimulatorContacts_Contact(
+ String accountType,
+ String accountName,
+ @Nullable String name,
+ boolean isStarred,
+ @Nullable ByteArrayOutputStream photoStream,
+ List<SimulatorContacts.PhoneNumber> phoneNumbers,
+ List<SimulatorContacts.Email> emails) {
+ this.accountType = accountType;
+ this.accountName = accountName;
+ this.name = name;
+ this.isStarred = isStarred;
+ this.photoStream = photoStream;
+ this.phoneNumbers = phoneNumbers;
+ this.emails = emails;
+ }
+
+ @NonNull
+ @Override
+ String getAccountType() {
+ return accountType;
+ }
+
+ @NonNull
+ @Override
+ String getAccountName() {
+ return accountName;
+ }
+
+ @Nullable
+ @Override
+ String getName() {
+ return name;
+ }
+
+ @Override
+ boolean getIsStarred() {
+ return isStarred;
+ }
+
+ @Nullable
+ @Override
+ ByteArrayOutputStream getPhotoStream() {
+ return photoStream;
+ }
+
+ @NonNull
+ @Override
+ List<SimulatorContacts.PhoneNumber> getPhoneNumbers() {
+ return phoneNumbers;
+ }
+
+ @NonNull
+ @Override
+ List<SimulatorContacts.Email> getEmails() {
+ return emails;
+ }
+
+ @Override
+ public String toString() {
+ return "Contact{"
+ + "accountType=" + accountType + ", "
+ + "accountName=" + accountName + ", "
+ + "name=" + name + ", "
+ + "isStarred=" + isStarred + ", "
+ + "photoStream=" + photoStream + ", "
+ + "phoneNumbers=" + phoneNumbers + ", "
+ + "emails=" + emails
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof SimulatorContacts.Contact) {
+ SimulatorContacts.Contact that = (SimulatorContacts.Contact) o;
+ return (this.accountType.equals(that.getAccountType()))
+ && (this.accountName.equals(that.getAccountName()))
+ && ((this.name == null) ? (that.getName() == null) : this.name.equals(that.getName()))
+ && (this.isStarred == that.getIsStarred())
+ && ((this.photoStream == null) ? (that.getPhotoStream() == null) : this.photoStream.equals(that.getPhotoStream()))
+ && (this.phoneNumbers.equals(that.getPhoneNumbers()))
+ && (this.emails.equals(that.getEmails()));
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 1;
+ h *= 1000003;
+ h ^= this.accountType.hashCode();
+ h *= 1000003;
+ h ^= this.accountName.hashCode();
+ h *= 1000003;
+ h ^= (name == null) ? 0 : this.name.hashCode();
+ h *= 1000003;
+ h ^= this.isStarred ? 1231 : 1237;
+ h *= 1000003;
+ h ^= (photoStream == null) ? 0 : this.photoStream.hashCode();
+ h *= 1000003;
+ h ^= this.phoneNumbers.hashCode();
+ h *= 1000003;
+ h ^= this.emails.hashCode();
+ return h;
+ }
+
+ static final class Builder extends SimulatorContacts.Contact.Builder {
+ private String accountType;
+ private String accountName;
+ private String name;
+ private Boolean isStarred;
+ private ByteArrayOutputStream photoStream;
+ private List<SimulatorContacts.PhoneNumber> phoneNumbers;
+ private List<SimulatorContacts.Email> emails;
+ Builder() {
+ }
+ private Builder(SimulatorContacts.Contact source) {
+ this.accountType = source.getAccountType();
+ this.accountName = source.getAccountName();
+ this.name = source.getName();
+ this.isStarred = source.getIsStarred();
+ this.photoStream = source.getPhotoStream();
+ this.phoneNumbers = source.getPhoneNumbers();
+ this.emails = source.getEmails();
+ }
+ @Override
+ SimulatorContacts.Contact.Builder setAccountType(String accountType) {
+ this.accountType = accountType;
+ return this;
+ }
+ @Override
+ SimulatorContacts.Contact.Builder setAccountName(String accountName) {
+ this.accountName = accountName;
+ return this;
+ }
+ @Override
+ SimulatorContacts.Contact.Builder setName(@Nullable String name) {
+ this.name = name;
+ return this;
+ }
+ @Override
+ SimulatorContacts.Contact.Builder setIsStarred(boolean isStarred) {
+ this.isStarred = isStarred;
+ return this;
+ }
+ @Override
+ SimulatorContacts.Contact.Builder setPhotoStream(@Nullable ByteArrayOutputStream photoStream) {
+ this.photoStream = photoStream;
+ return this;
+ }
+ @Override
+ SimulatorContacts.Contact.Builder setPhoneNumbers(List<SimulatorContacts.PhoneNumber> phoneNumbers) {
+ this.phoneNumbers = phoneNumbers;
+ return this;
+ }
+ @Override
+ SimulatorContacts.Contact.Builder setEmails(List<SimulatorContacts.Email> emails) {
+ this.emails = emails;
+ return this;
+ }
+ @Override
+ SimulatorContacts.Contact build() {
+ String missing = "";
+ if (this.accountType == null) {
+ missing += " accountType";
+ }
+ if (this.accountName == null) {
+ missing += " accountName";
+ }
+ if (this.isStarred == null) {
+ missing += " isStarred";
+ }
+ if (this.phoneNumbers == null) {
+ missing += " phoneNumbers";
+ }
+ if (this.emails == null) {
+ missing += " emails";
+ }
+ if (!missing.isEmpty()) {
+ throw new IllegalStateException("Missing required properties:" + missing);
+ }
+ return new AutoValue_SimulatorContacts_Contact(
+ this.accountType,
+ this.accountName,
+ this.name,
+ this.isStarred,
+ this.photoStream,
+ this.phoneNumbers,
+ this.emails);
+ }
+ }
+
+}
diff --git a/java/com/android/dialer/simulator/impl/AutoValue_SimulatorVoicemail_Voicemail.java b/java/com/android/dialer/simulator/impl/AutoValue_SimulatorVoicemail_Voicemail.java
new file mode 100644
index 000000000..58934801c
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/AutoValue_SimulatorVoicemail_Voicemail.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2017 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.dialer.simulator.impl;
+
+import android.support.annotation.NonNull;
+import javax.annotation.Generated;
+
+@Generated("com.google.auto.value.processor.AutoValueProcessor")
+ final class AutoValue_SimulatorVoicemail_Voicemail extends SimulatorVoicemail.Voicemail {
+
+ private final String phoneNumber;
+ private final String transcription;
+ private final long durationSeconds;
+ private final long timeMillis;
+ private final boolean isRead;
+
+ private AutoValue_SimulatorVoicemail_Voicemail(
+ String phoneNumber,
+ String transcription,
+ long durationSeconds,
+ long timeMillis,
+ boolean isRead) {
+ this.phoneNumber = phoneNumber;
+ this.transcription = transcription;
+ this.durationSeconds = durationSeconds;
+ this.timeMillis = timeMillis;
+ this.isRead = isRead;
+ }
+
+ @NonNull
+ @Override
+ String getPhoneNumber() {
+ return phoneNumber;
+ }
+
+ @NonNull
+ @Override
+ String getTranscription() {
+ return transcription;
+ }
+
+ @Override
+ long getDurationSeconds() {
+ return durationSeconds;
+ }
+
+ @Override
+ long getTimeMillis() {
+ return timeMillis;
+ }
+
+ @Override
+ boolean getIsRead() {
+ return isRead;
+ }
+
+ @Override
+ public String toString() {
+ return "Voicemail{"
+ + "phoneNumber=" + phoneNumber + ", "
+ + "transcription=" + transcription + ", "
+ + "durationSeconds=" + durationSeconds + ", "
+ + "timeMillis=" + timeMillis + ", "
+ + "isRead=" + isRead
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof SimulatorVoicemail.Voicemail) {
+ SimulatorVoicemail.Voicemail that = (SimulatorVoicemail.Voicemail) o;
+ return (this.phoneNumber.equals(that.getPhoneNumber()))
+ && (this.transcription.equals(that.getTranscription()))
+ && (this.durationSeconds == that.getDurationSeconds())
+ && (this.timeMillis == that.getTimeMillis())
+ && (this.isRead == that.getIsRead());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 1;
+ h *= 1000003;
+ h ^= this.phoneNumber.hashCode();
+ h *= 1000003;
+ h ^= this.transcription.hashCode();
+ h *= 1000003;
+ h ^= (this.durationSeconds >>> 32) ^ this.durationSeconds;
+ h *= 1000003;
+ h ^= (this.timeMillis >>> 32) ^ this.timeMillis;
+ h *= 1000003;
+ h ^= this.isRead ? 1231 : 1237;
+ return h;
+ }
+
+ static final class Builder extends SimulatorVoicemail.Voicemail.Builder {
+ private String phoneNumber;
+ private String transcription;
+ private Long durationSeconds;
+ private Long timeMillis;
+ private Boolean isRead;
+ Builder() {
+ }
+ private Builder(SimulatorVoicemail.Voicemail source) {
+ this.phoneNumber = source.getPhoneNumber();
+ this.transcription = source.getTranscription();
+ this.durationSeconds = source.getDurationSeconds();
+ this.timeMillis = source.getTimeMillis();
+ this.isRead = source.getIsRead();
+ }
+ @Override
+ SimulatorVoicemail.Voicemail.Builder setPhoneNumber(String phoneNumber) {
+ this.phoneNumber = phoneNumber;
+ return this;
+ }
+ @Override
+ SimulatorVoicemail.Voicemail.Builder setTranscription(String transcription) {
+ this.transcription = transcription;
+ return this;
+ }
+ @Override
+ SimulatorVoicemail.Voicemail.Builder setDurationSeconds(long durationSeconds) {
+ this.durationSeconds = durationSeconds;
+ return this;
+ }
+ @Override
+ SimulatorVoicemail.Voicemail.Builder setTimeMillis(long timeMillis) {
+ this.timeMillis = timeMillis;
+ return this;
+ }
+ @Override
+ SimulatorVoicemail.Voicemail.Builder setIsRead(boolean isRead) {
+ this.isRead = isRead;
+ return this;
+ }
+ @Override
+ SimulatorVoicemail.Voicemail build() {
+ String missing = "";
+ if (this.phoneNumber == null) {
+ missing += " phoneNumber";
+ }
+ if (this.transcription == null) {
+ missing += " transcription";
+ }
+ if (this.durationSeconds == null) {
+ missing += " durationSeconds";
+ }
+ if (this.timeMillis == null) {
+ missing += " timeMillis";
+ }
+ if (this.isRead == null) {
+ missing += " isRead";
+ }
+ if (!missing.isEmpty()) {
+ throw new IllegalStateException("Missing required properties:" + missing);
+ }
+ return new AutoValue_SimulatorVoicemail_Voicemail(
+ this.phoneNumber,
+ this.transcription,
+ this.durationSeconds,
+ this.timeMillis,
+ this.isRead);
+ }
+ }
+
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorActionProvider.java b/java/com/android/dialer/simulator/impl/SimulatorActionProvider.java
new file mode 100644
index 000000000..6cd573361
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorActionProvider.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 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.dialer.simulator.impl;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.support.annotation.NonNull;
+import android.view.ActionProvider;
+import android.view.MenuItem;
+import android.view.SubMenu;
+import android.view.View;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+
+/** Implements the simulator submenu. */
+final class SimulatorActionProvider extends ActionProvider {
+ @NonNull private final Context context;
+
+ public SimulatorActionProvider(@NonNull Context context) {
+ super(Assert.isNotNull(context));
+ this.context = context;
+ }
+
+ @Override
+ public View onCreateActionView() {
+ LogUtil.enterBlock("SimulatorActionProvider.onCreateActionView(null)");
+ return null;
+ }
+
+ @Override
+ public View onCreateActionView(MenuItem forItem) {
+ LogUtil.enterBlock("SimulatorActionProvider.onCreateActionView(MenuItem)");
+ return null;
+ }
+
+ @Override
+ public boolean hasSubMenu() {
+ LogUtil.enterBlock("SimulatorActionProvider.hasSubMenu");
+ return true;
+ }
+
+ @Override
+ public void onPrepareSubMenu(SubMenu subMenu) {
+ super.onPrepareSubMenu(subMenu);
+ LogUtil.enterBlock("SimulatorActionProvider.onPrepareSubMenu");
+ subMenu.clear();
+ subMenu
+ .add("Add call")
+ .setOnMenuItemClickListener(
+ (item) -> {
+ SimulatorVoiceCall.addNewIncomingCall(context);
+ return true;
+ });
+ subMenu
+ .add("Populate database")
+ .setOnMenuItemClickListener(
+ (item) -> {
+ populateDatabase();
+ return true;
+ });
+ }
+
+ private void populateDatabase() {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ public Void doInBackground(Void... params) {
+ SimulatorContacts.populateContacts(context);
+ SimulatorCallLog.populateCallLog(context);
+ SimulatorVoicemail.populateVoicemail(context);
+ return null;
+ }
+ }.execute();
+ }
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorCallLog.java b/java/com/android/dialer/simulator/impl/SimulatorCallLog.java
new file mode 100644
index 000000000..9ace047d0
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorCallLog.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 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.dialer.simulator.impl;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.os.RemoteException;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import com.android.dialer.common.Assert;
+
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+/** Populates the device database with call log entries. */
+final class SimulatorCallLog {
+ // Phone numbers from https://www.google.com/about/company/facts/locations/
+ private static final CallEntry.Builder[] SIMPLE_CALL_LOG = {
+ CallEntry.builder().setType(Calls.MISSED_TYPE).setNumber("+1-302-6365454"),
+ CallEntry.builder()
+ .setType(Calls.MISSED_TYPE)
+ .setNumber("")
+ .setPresentation(Calls.PRESENTATION_UNKNOWN),
+ CallEntry.builder().setType(Calls.REJECTED_TYPE).setNumber("+1-302-6365454"),
+ CallEntry.builder().setType(Calls.INCOMING_TYPE).setNumber("+1-302-6365454"),
+ CallEntry.builder()
+ .setType(Calls.MISSED_TYPE)
+ .setNumber("1234")
+ .setPresentation(Calls.PRESENTATION_RESTRICTED),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("+1-302-6365454"),
+ CallEntry.builder().setType(Calls.BLOCKED_TYPE).setNumber("+1-302-6365454"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("(425) 739-5600"),
+ CallEntry.builder().setType(Calls.ANSWERED_EXTERNALLY_TYPE).setNumber("(425) 739-5600"),
+ CallEntry.builder().setType(Calls.MISSED_TYPE).setNumber("+1 (425) 739-5600"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("739-5600"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("711"),
+ CallEntry.builder().setType(Calls.INCOMING_TYPE).setNumber("711"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("(425) 739-5600"),
+ CallEntry.builder().setType(Calls.MISSED_TYPE).setNumber("+44 (0) 20 7031 3000"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("+1-650-2530000"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("+1 303-245-0086;123,456"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("+1 303-245-0086"),
+ CallEntry.builder().setType(Calls.INCOMING_TYPE).setNumber("+1-650-2530000"),
+ CallEntry.builder().setType(Calls.MISSED_TYPE).setNumber("650-2530000"),
+ CallEntry.builder().setType(Calls.REJECTED_TYPE).setNumber("2530000"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("+1 404-487-9000"),
+ CallEntry.builder().setType(Calls.INCOMING_TYPE).setNumber("+61 2 9374 4001"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("+33 (0)1 42 68 53 00"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("972-74-746-6245"),
+ CallEntry.builder().setType(Calls.INCOMING_TYPE).setNumber("+971 4 4509500"),
+ CallEntry.builder().setType(Calls.INCOMING_TYPE).setNumber("+971 4 4509500"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("55-31-2128-6800"),
+ CallEntry.builder().setType(Calls.MISSED_TYPE).setNumber("611"),
+ CallEntry.builder().setType(Calls.OUTGOING_TYPE).setNumber("*86 512-343-5283"),
+ };
+
+ @WorkerThread
+ public static void populateCallLog(@NonNull Context context) {
+ Assert.isWorkerThread();
+ ArrayList<ContentProviderOperation> operations = new ArrayList<>();
+ // Do this 4 times to make the call log 4 times bigger.
+ long timeMillis = System.currentTimeMillis();
+ for (int i = 0; i < 4; i++) {
+ for (CallEntry.Builder builder : SIMPLE_CALL_LOG) {
+ CallEntry callEntry = builder.setTimeMillis(timeMillis).build();
+ operations.add(
+ ContentProviderOperation.newInsert(Calls.CONTENT_URI)
+ .withValues(callEntry.getAsContentValues())
+ .withYieldAllowed(true)
+ .build());
+ timeMillis -= TimeUnit.HOURS.toMillis(1);
+ }
+ }
+ try {
+ context.getContentResolver().applyBatch(CallLog.AUTHORITY, operations);
+ } catch (RemoteException | OperationApplicationException e) {
+ Assert.fail("error adding call entries: " + e);
+ }
+ }
+
+
+ abstract static class CallEntry {
+ @NonNull
+ abstract String getNumber();
+
+ abstract int getType();
+
+ abstract int getPresentation();
+
+ abstract long getTimeMillis();
+
+ static Builder builder() {
+ return new AutoValue_SimulatorCallLog_CallEntry.Builder()
+ .setPresentation(Calls.PRESENTATION_ALLOWED);
+ }
+
+ ContentValues getAsContentValues() {
+ ContentValues values = new ContentValues();
+ values.put(Calls.TYPE, getType());
+ values.put(Calls.NUMBER, getNumber());
+ values.put(Calls.NUMBER_PRESENTATION, getPresentation());
+ values.put(Calls.DATE, getTimeMillis());
+ return values;
+ }
+
+
+ abstract static class Builder {
+ abstract Builder setNumber(@NonNull String number);
+
+ abstract Builder setType(int type);
+
+ abstract Builder setPresentation(int presentation);
+
+ abstract Builder setTimeMillis(long timeMillis);
+
+ abstract CallEntry build();
+ }
+ }
+
+ private SimulatorCallLog() {}
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnection.java b/java/com/android/dialer/simulator/impl/SimulatorConnection.java
new file mode 100644
index 000000000..12d095890
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorConnection.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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.dialer.simulator.impl;
+
+import android.telecom.Connection;
+import android.telecom.DisconnectCause;
+import com.android.dialer.common.LogUtil;
+
+/** Represents a single phone call on the device. */
+final class SimulatorConnection extends Connection {
+
+ @Override
+ public void onAnswer() {
+ LogUtil.enterBlock("SimulatorConnection.onAnswer");
+ setActive();
+ }
+
+ @Override
+ public void onReject() {
+ LogUtil.enterBlock("SimulatorConnection.onReject");
+ setDisconnected(new DisconnectCause(DisconnectCause.REJECTED));
+ }
+
+ @Override
+ public void onHold() {
+ LogUtil.enterBlock("SimulatorConnection.onHold");
+ setOnHold();
+ }
+
+ @Override
+ public void onUnhold() {
+ LogUtil.enterBlock("SimulatorConnection.onUnhold");
+ setActive();
+ }
+
+ @Override
+ public void onDisconnect() {
+ LogUtil.enterBlock("SimulatorConnection.onDisconnect");
+ setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
+ destroy();
+ }
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java b/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java
new file mode 100644
index 000000000..322360786
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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.dialer.simulator.impl;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.telecom.Connection;
+import android.telecom.ConnectionRequest;
+import android.telecom.ConnectionService;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+import com.android.dialer.common.LogUtil;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Simple connection provider to create an incoming call. This is useful for emulators. */
+public final class SimulatorConnectionService extends ConnectionService {
+
+ private static final String PHONE_ACCOUNT_ID = "SIMULATOR_ACCOUNT_ID";
+
+ public static void register(Context context) {
+ LogUtil.enterBlock("SimulatorConnectionService.register");
+ context.getSystemService(TelecomManager.class).registerPhoneAccount(buildPhoneAccount(context));
+ }
+
+ private static PhoneAccount buildPhoneAccount(Context context) {
+ PhoneAccount.Builder builder =
+ new PhoneAccount.Builder(
+ getConnectionServiceHandle(context), "Simulator connection service");
+ List<String> uriSchemes = new ArrayList<>();
+ uriSchemes.add(PhoneAccount.SCHEME_TEL);
+
+ return builder
+ .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+ .setShortDescription("Simulator Connection Service")
+ .setSupportedUriSchemes(uriSchemes)
+ .build();
+ }
+
+ public static PhoneAccountHandle getConnectionServiceHandle(Context context) {
+ return new PhoneAccountHandle(
+ new ComponentName(context, SimulatorConnectionService.class), PHONE_ACCOUNT_ID);
+ }
+
+ private static Uri getPhoneNumber(ConnectionRequest request) {
+ String phoneNumber = request.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
+ return Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null);
+ }
+
+ @Override
+ public Connection onCreateOutgoingConnection(
+ PhoneAccountHandle phoneAccount, ConnectionRequest request) {
+ LogUtil.i(
+ "SimulatorConnectionService.onCreateOutgoingConnection",
+ "outgoing calls not supported yet");
+ return null;
+ }
+
+ @Override
+ public Connection onCreateIncomingConnection(
+ PhoneAccountHandle phoneAccount, ConnectionRequest request) {
+ LogUtil.enterBlock("SimulatorConnectionService.onCreateIncomingConnection");
+ SimulatorConnection connection = new SimulatorConnection();
+ connection.setRinging();
+ connection.setAddress(getPhoneNumber(request), TelecomManager.PRESENTATION_ALLOWED);
+ connection.setConnectionCapabilities(
+ Connection.CAPABILITY_MUTE | Connection.CAPABILITY_SUPPORT_HOLD);
+ return connection;
+ }
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorContacts.java b/java/com/android/dialer/simulator/impl/SimulatorContacts.java
new file mode 100644
index 000000000..89315094a
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorContacts.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2017 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.dialer.simulator.impl;
+
+import android.content.ContentProviderOperation;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.os.RemoteException;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+import android.text.TextUtils;
+import com.android.dialer.common.Assert;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Populates the device database with contacts. */
+final class SimulatorContacts {
+ // Phone numbers from https://www.google.com/about/company/facts/locations/
+ private static final Contact[] SIMPLE_CONTACTS = {
+ // US, contact with e164 number.
+ Contact.builder()
+ .setName("Michelangelo")
+ .addPhoneNumber(new PhoneNumber("+1-302-6365454", Phone.TYPE_MOBILE))
+ .addEmail(new Email("m@example.com"))
+ .setIsStarred(true)
+ .setOrangePhoto()
+ .build(),
+ // US, contact with a non-e164 number.
+ Contact.builder()
+ .setName("Leonardo da Vinci")
+ .addPhoneNumber(new PhoneNumber("(425) 739-5600", Phone.TYPE_MOBILE))
+ .addEmail(new Email("l@example.com"))
+ .setIsStarred(true)
+ .setBluePhoto()
+ .build(),
+ // UK, number where the (0) should be dropped.
+ Contact.builder()
+ .setName("Raphael")
+ .addPhoneNumber(new PhoneNumber("+44 (0) 20 7031 3000", Phone.TYPE_MOBILE))
+ .addEmail(new Email("r@example.com"))
+ .setIsStarred(true)
+ .setRedPhoto()
+ .build(),
+ // US and Australia, contact with a long name and multiple phone numbers.
+ Contact.builder()
+ .setName("Donatello di Niccolò di Betto Bardi")
+ .addPhoneNumber(new PhoneNumber("+1-650-2530000", Phone.TYPE_HOME))
+ .addPhoneNumber(new PhoneNumber("+1 404-487-9000", Phone.TYPE_WORK))
+ .addPhoneNumber(new PhoneNumber("+61 2 9374 4001", Phone.TYPE_FAX_HOME))
+ .setIsStarred(true)
+ .setPurplePhoto()
+ .build(),
+ // US, phone number shared with another contact and 2nd phone number with wait and pause.
+ Contact.builder()
+ .setName("Splinter")
+ .addPhoneNumber(new PhoneNumber("+1-650-2530000", Phone.TYPE_HOME))
+ .addPhoneNumber(new PhoneNumber("+1 303-245-0086;123,456", Phone.TYPE_WORK))
+ .build(),
+ // France, number with Japanese name.
+ Contact.builder()
+ .setName("スパイク・スピーゲル")
+ .addPhoneNumber(new PhoneNumber("+33 (0)1 42 68 53 00", Phone.TYPE_MOBILE))
+ .build(),
+ // Israel, RTL name and non-e164 number.
+ Contact.builder()
+ .setName("עקב אריה טברסק")
+ .addPhoneNumber(new PhoneNumber("+33 (0)1 42 68 53 00", Phone.TYPE_MOBILE))
+ .build(),
+ // UAE, RTL name.
+ Contact.builder()
+ .setName("سلام دنیا")
+ .addPhoneNumber(new PhoneNumber("+971 4 4509500", Phone.TYPE_MOBILE))
+ .build(),
+ // Brazil, contact with no name.
+ Contact.builder()
+ .addPhoneNumber(new PhoneNumber("+55-31-2128-6800", Phone.TYPE_MOBILE))
+ .build(),
+ // Short number, contact with no name.
+ Contact.builder().addPhoneNumber(new PhoneNumber("611", Phone.TYPE_MOBILE)).build(),
+ // US, number with an anonymous prefix.
+ Contact.builder()
+ .setName("Anonymous")
+ .addPhoneNumber(new PhoneNumber("*86 512-343-5283", Phone.TYPE_MOBILE))
+ .build(),
+ // None, contact with no phone number.
+ Contact.builder()
+ .setName("No Phone Number")
+ .addEmail(new Email("no@example.com"))
+ .setIsStarred(true)
+ .build(),
+ };
+
+ @WorkerThread
+ static void populateContacts(@NonNull Context context) {
+ Assert.isWorkerThread();
+ ArrayList<ContentProviderOperation> operations = new ArrayList<>();
+ for (Contact contact : SIMPLE_CONTACTS) {
+ addContact(contact, operations);
+ }
+ try {
+ context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations);
+ } catch (RemoteException | OperationApplicationException e) {
+ Assert.fail("error adding contacts: " + e);
+ }
+ }
+
+ private static void addContact(Contact contact, List<ContentProviderOperation> operations) {
+ int index = operations.size();
+
+ operations.add(
+ ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
+ .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, contact.getAccountType())
+ .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, contact.getAccountName())
+ .withValue(ContactsContract.RawContacts.STARRED, contact.getIsStarred())
+ .withYieldAllowed(true)
+ .build());
+
+ if (!TextUtils.isEmpty(contact.getName())) {
+ operations.add(
+ ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index)
+ .withValue(
+ ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
+ .withValue(
+ ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, contact.getName())
+ .build());
+ }
+
+ if (contact.getPhotoStream() != null) {
+ operations.add(
+ ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index)
+ .withValue(
+ ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
+ .withValue(
+ ContactsContract.CommonDataKinds.Photo.PHOTO,
+ contact.getPhotoStream().toByteArray())
+ .build());
+ }
+
+ for (PhoneNumber phoneNumber : contact.getPhoneNumbers()) {
+ operations.add(
+ ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index)
+ .withValue(
+ ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
+ .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneNumber.value)
+ .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneNumber.type)
+ .withValue(ContactsContract.CommonDataKinds.Phone.LABEL, phoneNumber.label)
+ .build());
+ }
+
+ for (Email email : contact.getEmails()) {
+ operations.add(
+ ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index)
+ .withValue(
+ ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
+ .withValue(ContactsContract.CommonDataKinds.Email.DATA, email.value)
+ .withValue(ContactsContract.CommonDataKinds.Email.TYPE, email.type)
+ .withValue(ContactsContract.CommonDataKinds.Email.LABEL, email.label)
+ .build());
+ }
+ }
+
+
+ abstract static class Contact {
+ @NonNull
+ abstract String getAccountType();
+
+ @NonNull
+ abstract String getAccountName();
+
+ @Nullable
+ abstract String getName();
+
+ abstract boolean getIsStarred();
+
+ @Nullable
+ abstract ByteArrayOutputStream getPhotoStream();
+
+ @NonNull
+ abstract List<PhoneNumber> getPhoneNumbers();
+
+ @NonNull
+ abstract List<Email> getEmails();
+
+ static Builder builder() {
+ return new AutoValue_SimulatorContacts_Contact.Builder()
+ .setAccountType("com.google")
+ .setAccountName("foo@example")
+ .setIsStarred(false)
+ .setPhoneNumbers(new ArrayList<>())
+ .setEmails(new ArrayList<>());
+ }
+
+
+ abstract static class Builder {
+ @NonNull private final List<PhoneNumber> phoneNumbers = new ArrayList<>();
+ @NonNull private final List<Email> emails = new ArrayList<>();
+
+ abstract Builder setAccountType(@NonNull String accountType);
+
+ abstract Builder setAccountName(@NonNull String accountName);
+
+ abstract Builder setName(@NonNull String name);
+
+ abstract Builder setIsStarred(boolean isStarred);
+
+ abstract Builder setPhotoStream(ByteArrayOutputStream photoStream);
+
+ abstract Builder setPhoneNumbers(@NonNull List<PhoneNumber> phoneNumbers);
+
+ abstract Builder setEmails(@NonNull List<Email> emails);
+
+ abstract Contact build();
+
+ Builder addPhoneNumber(PhoneNumber phoneNumber) {
+ phoneNumbers.add(phoneNumber);
+ return setPhoneNumbers(phoneNumbers);
+ }
+
+ Builder addEmail(Email email) {
+ emails.add(email);
+ return setEmails(emails);
+ }
+
+ Builder setRedPhoto() {
+ setPhotoStream(getPhotoStreamWithColor(Color.rgb(0xe3, 0x33, 0x1c)));
+ return this;
+ }
+
+ Builder setBluePhoto() {
+ setPhotoStream(getPhotoStreamWithColor(Color.rgb(0x00, 0xaa, 0xe6)));
+ return this;
+ }
+
+ Builder setOrangePhoto() {
+ setPhotoStream(getPhotoStreamWithColor(Color.rgb(0xea, 0x95, 0x00)));
+ return this;
+ }
+
+ Builder setPurplePhoto() {
+ setPhotoStream(getPhotoStreamWithColor(Color.rgb(0x99, 0x5a, 0xa0)));
+ return this;
+ }
+
+ /** Creates a contact photo with a green background and a circle of the given color. */
+ private static ByteArrayOutputStream getPhotoStreamWithColor(int color) {
+ int width = 300;
+ int height = 300;
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ canvas.drawColor(Color.argb(0xff, 0x4c, 0x9c, 0x23));
+ Paint paint = new Paint();
+ paint.setColor(color);
+ paint.setStyle(Paint.Style.FILL);
+ canvas.drawCircle(width / 2, height / 2, width / 3, paint);
+
+ ByteArrayOutputStream photoStream = new ByteArrayOutputStream();
+ bitmap.compress(Bitmap.CompressFormat.PNG, 75, photoStream);
+ return photoStream;
+ }
+ }
+ }
+
+ static class PhoneNumber {
+ public final String value;
+ public final int type;
+ public final String label;
+
+ PhoneNumber(String value, int type) {
+ this.value = value;
+ this.type = type;
+ label = "simulator phone number";
+ }
+ }
+
+ static class Email {
+ public final String value;
+ public final int type;
+ public final String label;
+
+ Email(String simpleEmail) {
+ value = simpleEmail;
+ type = ContactsContract.CommonDataKinds.Email.TYPE_WORK;
+ label = "simulator email";
+ }
+ }
+
+ private SimulatorContacts() {}
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorModule.java b/java/com/android/dialer/simulator/impl/SimulatorModule.java
new file mode 100644
index 000000000..0f8ad3954
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorModule.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 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.dialer.simulator.impl;
+
+import android.content.Context;
+import android.view.ActionProvider;
+import com.android.dialer.simulator.Simulator;
+
+/** The entry point for the simulator module. */
+public final class SimulatorModule implements Simulator {
+ @Override
+ public boolean shouldShow() {
+ return true;
+ }
+
+ @Override
+ public ActionProvider getActionProvider(Context context) {
+ return new SimulatorActionProvider(context);
+ }
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
new file mode 100644
index 000000000..39c1d02a5
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 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.dialer.simulator.impl;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+
+/** Utilities to simulate phone calls. */
+final class SimulatorVoiceCall {
+ public static void addNewIncomingCall(@NonNull Context context) {
+ LogUtil.enterBlock("SimulatorVoiceCall.addNewIncomingCall");
+ SimulatorConnectionService.register(context);
+
+ Bundle bundle = new Bundle();
+ // Set the caller ID to the Google London office.
+ bundle.putString(TelephonyManager.EXTRA_INCOMING_NUMBER, "+44 (0) 20 7031 3000");
+ try {
+ context
+ .getSystemService(TelecomManager.class)
+ .addNewIncomingCall(
+ SimulatorConnectionService.getConnectionServiceHandle(context), bundle);
+ } catch (SecurityException e) {
+ Assert.fail("unable to add call: " + e);
+ }
+ }
+
+ private SimulatorVoiceCall() {}
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorVoicemail.java b/java/com/android/dialer/simulator/impl/SimulatorVoicemail.java
new file mode 100644
index 000000000..ffb9191dc
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorVoicemail.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2017 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.dialer.simulator.impl;
+
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.VoicemailContract.Status;
+import android.provider.VoicemailContract.Voicemails;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.TelephonyManager;
+import com.android.dialer.common.Assert;
+
+import java.util.concurrent.TimeUnit;
+
+/** Populates the device database with voicemail entries. */
+final class SimulatorVoicemail {
+ private static final String ACCOUNT_ID = "ACCOUNT_ID";
+
+ private static final Voicemail.Builder[] SIMPLE_VOICEMAILS = {
+ // Long transcription with an embedded phone number.
+ Voicemail.builder()
+ .setPhoneNumber("+1-302-6365454")
+ .setTranscription(
+ "Hi, this is a very long voicemail. Please call me back at 650 253 0000. "
+ + "I hope you listen to all of it. This is very important. "
+ + "Hi, this is a very long voicemail. "
+ + "I hope you listen to all of it. It's very important.")
+ .setDurationSeconds(10)
+ .setIsRead(false),
+ // RTL transcription.
+ Voicemail.builder()
+ .setPhoneNumber("+1-302-6365454")
+ .setTranscription("هزاران دوست کم اند و یک دشمن زیاد")
+ .setDurationSeconds(60)
+ .setIsRead(true),
+ // Empty number.
+ Voicemail.builder()
+ .setPhoneNumber("")
+ .setTranscription("")
+ .setDurationSeconds(60)
+ .setIsRead(true),
+ // No duration.
+ Voicemail.builder()
+ .setPhoneNumber("+1-302-6365454")
+ .setTranscription("")
+ .setDurationSeconds(0)
+ .setIsRead(true),
+ // Short number.
+ Voicemail.builder()
+ .setPhoneNumber("711")
+ .setTranscription("This is a short voicemail.")
+ .setDurationSeconds(12)
+ .setIsRead(true),
+ };
+
+ @WorkerThread
+ public static void populateVoicemail(@NonNull Context context) {
+ Assert.isWorkerThread();
+ enableVoicemail(context);
+
+ // Do this 4 times to make the voicemail database 4 times bigger.
+ long timeMillis = System.currentTimeMillis();
+ for (int i = 0; i < 4; i++) {
+ for (Voicemail.Builder builder : SIMPLE_VOICEMAILS) {
+ Voicemail voicemail = builder.setTimeMillis(timeMillis).build();
+ context
+ .getContentResolver()
+ .insert(
+ Voicemails.buildSourceUri(context.getPackageName()),
+ voicemail.getAsContentValues(context));
+ timeMillis -= TimeUnit.HOURS.toMillis(2);
+ }
+ }
+ }
+
+ private static void enableVoicemail(@NonNull Context context) {
+ PhoneAccountHandle handle =
+ new PhoneAccountHandle(new ComponentName(context, SimulatorVoicemail.class), ACCOUNT_ID);
+
+ ContentValues values = new ContentValues();
+ values.put(Status.SOURCE_PACKAGE, handle.getComponentName().getPackageName());
+ values.put(Status.SOURCE_TYPE, TelephonyManager.VVM_TYPE_OMTP);
+ values.put(Status.PHONE_ACCOUNT_COMPONENT_NAME, handle.getComponentName().flattenToString());
+ values.put(Status.PHONE_ACCOUNT_ID, handle.getId());
+ values.put(Status.CONFIGURATION_STATE, Status.CONFIGURATION_STATE_OK);
+ values.put(Status.DATA_CHANNEL_STATE, Status.DATA_CHANNEL_STATE_OK);
+ values.put(Status.NOTIFICATION_CHANNEL_STATE, Status.NOTIFICATION_CHANNEL_STATE_OK);
+ context.getContentResolver().insert(Status.buildSourceUri(context.getPackageName()), values);
+ }
+
+
+ abstract static class Voicemail {
+ @NonNull
+ abstract String getPhoneNumber();
+
+ @NonNull
+ abstract String getTranscription();
+
+ abstract long getDurationSeconds();
+
+ abstract long getTimeMillis();
+
+ abstract boolean getIsRead();
+
+ static Builder builder() {
+ return new AutoValue_SimulatorVoicemail_Voicemail.Builder();
+ }
+
+ ContentValues getAsContentValues(Context context) {
+ ContentValues values = new ContentValues();
+ values.put(Voicemails.DATE, getTimeMillis());
+ values.put(Voicemails.NUMBER, getPhoneNumber());
+ values.put(Voicemails.DURATION, getDurationSeconds());
+ values.put(Voicemails.SOURCE_PACKAGE, context.getPackageName());
+ values.put(Voicemails.IS_READ, getIsRead() ? 1 : 0);
+ values.put(Voicemails.TRANSCRIPTION, getTranscription());
+ return values;
+ }
+
+
+ abstract static class Builder {
+ abstract Builder setPhoneNumber(@NonNull String phoneNumber);
+
+ abstract Builder setTranscription(@NonNull String transcription);
+
+ abstract Builder setDurationSeconds(long durationSeconds);
+
+ abstract Builder setTimeMillis(long timeMillis);
+
+ abstract Builder setIsRead(boolean isRead);
+
+ abstract Voicemail build();
+ }
+ }
+
+ private SimulatorVoicemail() {}
+}
diff --git a/java/com/android/dialer/smartdial/LatinSmartDialMap.java b/java/com/android/dialer/smartdial/LatinSmartDialMap.java
new file mode 100644
index 000000000..c512c5d4a
--- /dev/null
+++ b/java/com/android/dialer/smartdial/LatinSmartDialMap.java
@@ -0,0 +1,784 @@
+/*
+ * 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.dialer.smartdial;
+
+public class LatinSmartDialMap implements SmartDialMap {
+
+ private static final char[] LATIN_LETTERS_TO_DIGITS = {
+ '2',
+ '2',
+ '2', // A,B,C -> 2
+ '3',
+ '3',
+ '3', // D,E,F -> 3
+ '4',
+ '4',
+ '4', // G,H,I -> 4
+ '5',
+ '5',
+ '5', // J,K,L -> 5
+ '6',
+ '6',
+ '6', // M,N,O -> 6
+ '7',
+ '7',
+ '7',
+ '7', // P,Q,R,S -> 7
+ '8',
+ '8',
+ '8', // T,U,V -> 8
+ '9',
+ '9',
+ '9',
+ '9' // W,X,Y,Z -> 9
+ };
+
+ @Override
+ public boolean isValidDialpadAlphabeticChar(char ch) {
+ return (ch >= 'a' && ch <= 'z');
+ }
+
+ @Override
+ public boolean isValidDialpadNumericChar(char ch) {
+ return (ch >= '0' && ch <= '9');
+ }
+
+ @Override
+ public boolean isValidDialpadCharacter(char ch) {
+ return (isValidDialpadAlphabeticChar(ch) || isValidDialpadNumericChar(ch));
+ }
+
+ /*
+ * The switch statement in this function was generated using the python code:
+ * from unidecode import unidecode
+ * for i in range(192, 564):
+ * char = unichr(i)
+ * decoded = unidecode(char)
+ * # Unicode characters that decompose into multiple characters i.e.
+ * # into ss are not supported for now
+ * if (len(decoded) == 1 and decoded.isalpha()):
+ * print "case '" + char + "': return '" + unidecode(char) + "';"
+ *
+ * This gives us a way to map characters containing accents/diacritics to their
+ * alphabetic equivalents. The unidecode library can be found at:
+ * http://pypi.python.org/pypi/Unidecode/0.04.1
+ *
+ * Also remaps all upper case latin characters to their lower case equivalents.
+ */
+ @Override
+ public char normalizeCharacter(char ch) {
+ switch (ch) {
+ case 'À':
+ return 'a';
+ case 'Á':
+ return 'a';
+ case 'Â':
+ return 'a';
+ case 'Ã':
+ return 'a';
+ case 'Ä':
+ return 'a';
+ case 'Å':
+ return 'a';
+ case 'Ç':
+ return 'c';
+ case 'È':
+ return 'e';
+ case 'É':
+ return 'e';
+ case 'Ê':
+ return 'e';
+ case 'Ë':
+ return 'e';
+ case 'Ì':
+ return 'i';
+ case 'Í':
+ return 'i';
+ case 'Î':
+ return 'i';
+ case 'Ï':
+ return 'i';
+ case 'Ð':
+ return 'd';
+ case 'Ñ':
+ return 'n';
+ case 'Ò':
+ return 'o';
+ case 'Ó':
+ return 'o';
+ case 'Ô':
+ return 'o';
+ case 'Õ':
+ return 'o';
+ case 'Ö':
+ return 'o';
+ case '×':
+ return 'x';
+ case 'Ø':
+ return 'o';
+ case 'Ù':
+ return 'u';
+ case 'Ú':
+ return 'u';
+ case 'Û':
+ return 'u';
+ case 'Ü':
+ return 'u';
+ case 'Ý':
+ return 'u';
+ case 'à':
+ return 'a';
+ case 'á':
+ return 'a';
+ case 'â':
+ return 'a';
+ case 'ã':
+ return 'a';
+ case 'ä':
+ return 'a';
+ case 'å':
+ return 'a';
+ case 'ç':
+ return 'c';
+ case 'è':
+ return 'e';
+ case 'é':
+ return 'e';
+ case 'ê':
+ return 'e';
+ case 'ë':
+ return 'e';
+ case 'ì':
+ return 'i';
+ case 'í':
+ return 'i';
+ case 'î':
+ return 'i';
+ case 'ï':
+ return 'i';
+ case 'ð':
+ return 'd';
+ case 'ñ':
+ return 'n';
+ case 'ò':
+ return 'o';
+ case 'ó':
+ return 'o';
+ case 'ô':
+ return 'o';
+ case 'õ':
+ return 'o';
+ case 'ö':
+ return 'o';
+ case 'ø':
+ return 'o';
+ case 'ù':
+ return 'u';
+ case 'ú':
+ return 'u';
+ case 'û':
+ return 'u';
+ case 'ü':
+ return 'u';
+ case 'ý':
+ return 'y';
+ case 'ÿ':
+ return 'y';
+ case 'Ā':
+ return 'a';
+ case 'ā':
+ return 'a';
+ case 'Ă':
+ return 'a';
+ case 'ă':
+ return 'a';
+ case 'Ą':
+ return 'a';
+ case 'ą':
+ return 'a';
+ case 'Ć':
+ return 'c';
+ case 'ć':
+ return 'c';
+ case 'Ĉ':
+ return 'c';
+ case 'ĉ':
+ return 'c';
+ case 'Ċ':
+ return 'c';
+ case 'ċ':
+ return 'c';
+ case 'Č':
+ return 'c';
+ case 'č':
+ return 'c';
+ case 'Ď':
+ return 'd';
+ case 'ď':
+ return 'd';
+ case 'Đ':
+ return 'd';
+ case 'đ':
+ return 'd';
+ case 'Ē':
+ return 'e';
+ case 'ē':
+ return 'e';
+ case 'Ĕ':
+ return 'e';
+ case 'ĕ':
+ return 'e';
+ case 'Ė':
+ return 'e';
+ case 'ė':
+ return 'e';
+ case 'Ę':
+ return 'e';
+ case 'ę':
+ return 'e';
+ case 'Ě':
+ return 'e';
+ case 'ě':
+ return 'e';
+ case 'Ĝ':
+ return 'g';
+ case 'ĝ':
+ return 'g';
+ case 'Ğ':
+ return 'g';
+ case 'ğ':
+ return 'g';
+ case 'Ġ':
+ return 'g';
+ case 'ġ':
+ return 'g';
+ case 'Ģ':
+ return 'g';
+ case 'ģ':
+ return 'g';
+ case 'Ĥ':
+ return 'h';
+ case 'ĥ':
+ return 'h';
+ case 'Ħ':
+ return 'h';
+ case 'ħ':
+ return 'h';
+ case 'Ĩ':
+ return 'i';
+ case 'ĩ':
+ return 'i';
+ case 'Ī':
+ return 'i';
+ case 'ī':
+ return 'i';
+ case 'Ĭ':
+ return 'i';
+ case 'ĭ':
+ return 'i';
+ case 'Į':
+ return 'i';
+ case 'į':
+ return 'i';
+ case 'İ':
+ return 'i';
+ case 'ı':
+ return 'i';
+ case 'Ĵ':
+ return 'j';
+ case 'ĵ':
+ return 'j';
+ case 'Ķ':
+ return 'k';
+ case 'ķ':
+ return 'k';
+ case 'ĸ':
+ return 'k';
+ case 'Ĺ':
+ return 'l';
+ case 'ĺ':
+ return 'l';
+ case 'Ļ':
+ return 'l';
+ case 'ļ':
+ return 'l';
+ case 'Ľ':
+ return 'l';
+ case 'ľ':
+ return 'l';
+ case 'Ŀ':
+ return 'l';
+ case 'ŀ':
+ return 'l';
+ case 'Ł':
+ return 'l';
+ case 'ł':
+ return 'l';
+ case 'Ń':
+ return 'n';
+ case 'ń':
+ return 'n';
+ case 'Ņ':
+ return 'n';
+ case 'ņ':
+ return 'n';
+ case 'Ň':
+ return 'n';
+ case 'ň':
+ return 'n';
+ case 'Ō':
+ return 'o';
+ case 'ō':
+ return 'o';
+ case 'Ŏ':
+ return 'o';
+ case 'ŏ':
+ return 'o';
+ case 'Ő':
+ return 'o';
+ case 'ő':
+ return 'o';
+ case 'Ŕ':
+ return 'r';
+ case 'ŕ':
+ return 'r';
+ case 'Ŗ':
+ return 'r';
+ case 'ŗ':
+ return 'r';
+ case 'Ř':
+ return 'r';
+ case 'ř':
+ return 'r';
+ case 'Ś':
+ return 's';
+ case 'ś':
+ return 's';
+ case 'Ŝ':
+ return 's';
+ case 'ŝ':
+ return 's';
+ case 'Ş':
+ return 's';
+ case 'ş':
+ return 's';
+ case 'Š':
+ return 's';
+ case 'š':
+ return 's';
+ case 'Ţ':
+ return 't';
+ case 'ţ':
+ return 't';
+ case 'Ť':
+ return 't';
+ case 'ť':
+ return 't';
+ case 'Ŧ':
+ return 't';
+ case 'ŧ':
+ return 't';
+ case 'Ũ':
+ return 'u';
+ case 'ũ':
+ return 'u';
+ case 'Ū':
+ return 'u';
+ case 'ū':
+ return 'u';
+ case 'Ŭ':
+ return 'u';
+ case 'ŭ':
+ return 'u';
+ case 'Ů':
+ return 'u';
+ case 'ů':
+ return 'u';
+ case 'Ű':
+ return 'u';
+ case 'ű':
+ return 'u';
+ case 'Ų':
+ return 'u';
+ case 'ų':
+ return 'u';
+ case 'Ŵ':
+ return 'w';
+ case 'ŵ':
+ return 'w';
+ case 'Ŷ':
+ return 'y';
+ case 'ŷ':
+ return 'y';
+ case 'Ÿ':
+ return 'y';
+ case 'Ź':
+ return 'z';
+ case 'ź':
+ return 'z';
+ case 'Ż':
+ return 'z';
+ case 'ż':
+ return 'z';
+ case 'Ž':
+ return 'z';
+ case 'ž':
+ return 'z';
+ case 'ſ':
+ return 's';
+ case 'ƀ':
+ return 'b';
+ case 'Ɓ':
+ return 'b';
+ case 'Ƃ':
+ return 'b';
+ case 'ƃ':
+ return 'b';
+ case 'Ɔ':
+ return 'o';
+ case 'Ƈ':
+ return 'c';
+ case 'ƈ':
+ return 'c';
+ case 'Ɖ':
+ return 'd';
+ case 'Ɗ':
+ return 'd';
+ case 'Ƌ':
+ return 'd';
+ case 'ƌ':
+ return 'd';
+ case 'ƍ':
+ return 'd';
+ case 'Ɛ':
+ return 'e';
+ case 'Ƒ':
+ return 'f';
+ case 'ƒ':
+ return 'f';
+ case 'Ɠ':
+ return 'g';
+ case 'Ɣ':
+ return 'g';
+ case 'Ɩ':
+ return 'i';
+ case 'Ɨ':
+ return 'i';
+ case 'Ƙ':
+ return 'k';
+ case 'ƙ':
+ return 'k';
+ case 'ƚ':
+ return 'l';
+ case 'ƛ':
+ return 'l';
+ case 'Ɯ':
+ return 'w';
+ case 'Ɲ':
+ return 'n';
+ case 'ƞ':
+ return 'n';
+ case 'Ɵ':
+ return 'o';
+ case 'Ơ':
+ return 'o';
+ case 'ơ':
+ return 'o';
+ case 'Ƥ':
+ return 'p';
+ case 'ƥ':
+ return 'p';
+ case 'ƫ':
+ return 't';
+ case 'Ƭ':
+ return 't';
+ case 'ƭ':
+ return 't';
+ case 'Ʈ':
+ return 't';
+ case 'Ư':
+ return 'u';
+ case 'ư':
+ return 'u';
+ case 'Ʊ':
+ return 'y';
+ case 'Ʋ':
+ return 'v';
+ case 'Ƴ':
+ return 'y';
+ case 'ƴ':
+ return 'y';
+ case 'Ƶ':
+ return 'z';
+ case 'ƶ':
+ return 'z';
+ case 'ƿ':
+ return 'w';
+ case 'Ǎ':
+ return 'a';
+ case 'ǎ':
+ return 'a';
+ case 'Ǐ':
+ return 'i';
+ case 'ǐ':
+ return 'i';
+ case 'Ǒ':
+ return 'o';
+ case 'ǒ':
+ return 'o';
+ case 'Ǔ':
+ return 'u';
+ case 'ǔ':
+ return 'u';
+ case 'Ǖ':
+ return 'u';
+ case 'ǖ':
+ return 'u';
+ case 'Ǘ':
+ return 'u';
+ case 'ǘ':
+ return 'u';
+ case 'Ǚ':
+ return 'u';
+ case 'ǚ':
+ return 'u';
+ case 'Ǜ':
+ return 'u';
+ case 'ǜ':
+ return 'u';
+ case 'Ǟ':
+ return 'a';
+ case 'ǟ':
+ return 'a';
+ case 'Ǡ':
+ return 'a';
+ case 'ǡ':
+ return 'a';
+ case 'Ǥ':
+ return 'g';
+ case 'ǥ':
+ return 'g';
+ case 'Ǧ':
+ return 'g';
+ case 'ǧ':
+ return 'g';
+ case 'Ǩ':
+ return 'k';
+ case 'ǩ':
+ return 'k';
+ case 'Ǫ':
+ return 'o';
+ case 'ǫ':
+ return 'o';
+ case 'Ǭ':
+ return 'o';
+ case 'ǭ':
+ return 'o';
+ case 'ǰ':
+ return 'j';
+ case 'Dz':
+ return 'd';
+ case 'Ǵ':
+ return 'g';
+ case 'ǵ':
+ return 'g';
+ case 'Ƿ':
+ return 'w';
+ case 'Ǹ':
+ return 'n';
+ case 'ǹ':
+ return 'n';
+ case 'Ǻ':
+ return 'a';
+ case 'ǻ':
+ return 'a';
+ case 'Ǿ':
+ return 'o';
+ case 'ǿ':
+ return 'o';
+ case 'Ȁ':
+ return 'a';
+ case 'ȁ':
+ return 'a';
+ case 'Ȃ':
+ return 'a';
+ case 'ȃ':
+ return 'a';
+ case 'Ȅ':
+ return 'e';
+ case 'ȅ':
+ return 'e';
+ case 'Ȇ':
+ return 'e';
+ case 'ȇ':
+ return 'e';
+ case 'Ȉ':
+ return 'i';
+ case 'ȉ':
+ return 'i';
+ case 'Ȋ':
+ return 'i';
+ case 'ȋ':
+ return 'i';
+ case 'Ȍ':
+ return 'o';
+ case 'ȍ':
+ return 'o';
+ case 'Ȏ':
+ return 'o';
+ case 'ȏ':
+ return 'o';
+ case 'Ȑ':
+ return 'r';
+ case 'ȑ':
+ return 'r';
+ case 'Ȓ':
+ return 'r';
+ case 'ȓ':
+ return 'r';
+ case 'Ȕ':
+ return 'u';
+ case 'ȕ':
+ return 'u';
+ case 'Ȗ':
+ return 'u';
+ case 'ȗ':
+ return 'u';
+ case 'Ș':
+ return 's';
+ case 'ș':
+ return 's';
+ case 'Ț':
+ return 't';
+ case 'ț':
+ return 't';
+ case 'Ȝ':
+ return 'y';
+ case 'ȝ':
+ return 'y';
+ case 'Ȟ':
+ return 'h';
+ case 'ȟ':
+ return 'h';
+ case 'Ȥ':
+ return 'z';
+ case 'ȥ':
+ return 'z';
+ case 'Ȧ':
+ return 'a';
+ case 'ȧ':
+ return 'a';
+ case 'Ȩ':
+ return 'e';
+ case 'ȩ':
+ return 'e';
+ case 'Ȫ':
+ return 'o';
+ case 'ȫ':
+ return 'o';
+ case 'Ȭ':
+ return 'o';
+ case 'ȭ':
+ return 'o';
+ case 'Ȯ':
+ return 'o';
+ case 'ȯ':
+ return 'o';
+ case 'Ȱ':
+ return 'o';
+ case 'ȱ':
+ return 'o';
+ case 'Ȳ':
+ return 'y';
+ case 'ȳ':
+ return 'y';
+ case 'A':
+ return 'a';
+ case 'B':
+ return 'b';
+ case 'C':
+ return 'c';
+ case 'D':
+ return 'd';
+ case 'E':
+ return 'e';
+ case 'F':
+ return 'f';
+ case 'G':
+ return 'g';
+ case 'H':
+ return 'h';
+ case 'I':
+ return 'i';
+ case 'J':
+ return 'j';
+ case 'K':
+ return 'k';
+ case 'L':
+ return 'l';
+ case 'M':
+ return 'm';
+ case 'N':
+ return 'n';
+ case 'O':
+ return 'o';
+ case 'P':
+ return 'p';
+ case 'Q':
+ return 'q';
+ case 'R':
+ return 'r';
+ case 'S':
+ return 's';
+ case 'T':
+ return 't';
+ case 'U':
+ return 'u';
+ case 'V':
+ return 'v';
+ case 'W':
+ return 'w';
+ case 'X':
+ return 'x';
+ case 'Y':
+ return 'y';
+ case 'Z':
+ return 'z';
+ default:
+ return ch;
+ }
+ }
+
+ @Override
+ public byte getDialpadIndex(char ch) {
+ if (ch >= '0' && ch <= '9') {
+ return (byte) (ch - '0');
+ } else if (ch >= 'a' && ch <= 'z') {
+ return (byte) (LATIN_LETTERS_TO_DIGITS[ch - 'a'] - '0');
+ } else {
+ return -1;
+ }
+ }
+
+ @Override
+ public char getDialpadNumericCharacter(char ch) {
+ if (ch >= 'a' && ch <= 'z') {
+ return LATIN_LETTERS_TO_DIGITS[ch - 'a'];
+ }
+ return ch;
+ }
+}
diff --git a/java/com/android/dialer/smartdial/SmartDialMap.java b/java/com/android/dialer/smartdial/SmartDialMap.java
new file mode 100644
index 000000000..9638929a6
--- /dev/null
+++ b/java/com/android/dialer/smartdial/SmartDialMap.java
@@ -0,0 +1,60 @@
+/*
+ * 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.dialer.smartdial;
+
+/**
+ * Note: These methods currently take characters as arguments. For future planned language support,
+ * they will need to be changed to use codepoints instead of characters.
+ *
+ * <p>http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#codePointAt(int)
+ *
+ * <p>If/when this change is made, LatinSmartDialMap(which operates on chars) will continue to work
+ * by simply casting from a codepoint to a character.
+ */
+public interface SmartDialMap {
+
+ /*
+ * Returns true if the provided character can be mapped to a key on the dialpad
+ */
+ boolean isValidDialpadCharacter(char ch);
+
+ /*
+ * Returns true if the provided character is a letter, and can be mapped to a key on the dialpad
+ */
+ boolean isValidDialpadAlphabeticChar(char ch);
+
+ /*
+ * Returns true if the provided character is a digit, and can be mapped to a key on the dialpad
+ */
+ boolean isValidDialpadNumericChar(char ch);
+
+ /*
+ * Get the index of the key on the dialpad which the character corresponds to
+ */
+ byte getDialpadIndex(char ch);
+
+ /*
+ * Get the actual numeric character on the dialpad which the character corresponds to
+ */
+ char getDialpadNumericCharacter(char ch);
+
+ /*
+ * Converts uppercase characters to lower case ones, and on a best effort basis, strips accents
+ * from accented characters.
+ */
+ char normalizeCharacter(char ch);
+}
diff --git a/java/com/android/dialer/smartdial/SmartDialMatchPosition.java b/java/com/android/dialer/smartdial/SmartDialMatchPosition.java
new file mode 100644
index 000000000..8056ad723
--- /dev/null
+++ b/java/com/android/dialer/smartdial/SmartDialMatchPosition.java
@@ -0,0 +1,70 @@
+/*
+ * 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.dialer.smartdial;
+
+import android.util.Log;
+import java.util.ArrayList;
+
+/**
+ * Stores information about a range of characters matched in a display name The integers start and
+ * end indicate that the range start to end (exclusive) correspond to some characters in the query.
+ * Used to highlight certain parts of the contact's display name to indicate that those ranges
+ * matched the user's query.
+ */
+public class SmartDialMatchPosition {
+
+ private static final String TAG = SmartDialMatchPosition.class.getSimpleName();
+
+ public int start;
+ public int end;
+
+ public SmartDialMatchPosition(int start, int end) {
+ this.start = start;
+ this.end = end;
+ }
+
+ /**
+ * Used by {@link SmartDialNameMatcher} to advance the positions of a match position found in a
+ * sub query.
+ *
+ * @param inList ArrayList of SmartDialMatchPositions to modify.
+ * @param toAdvance Offset to modify by.
+ */
+ public static void advanceMatchPositions(
+ ArrayList<SmartDialMatchPosition> inList, int toAdvance) {
+ for (int i = 0; i < inList.size(); i++) {
+ inList.get(i).advance(toAdvance);
+ }
+ }
+
+ /**
+ * Used mainly for debug purposes. Displays contents of an ArrayList of SmartDialMatchPositions.
+ *
+ * @param list ArrayList of SmartDialMatchPositions to print out in a human readable fashion.
+ */
+ public static void print(ArrayList<SmartDialMatchPosition> list) {
+ for (int i = 0; i < list.size(); i++) {
+ SmartDialMatchPosition m = list.get(i);
+ Log.d(TAG, "[" + m.start + "," + m.end + "]");
+ }
+ }
+
+ private void advance(int toAdvance) {
+ this.start += toAdvance;
+ this.end += toAdvance;
+ }
+}
diff --git a/java/com/android/dialer/smartdial/SmartDialNameMatcher.java b/java/com/android/dialer/smartdial/SmartDialNameMatcher.java
new file mode 100644
index 000000000..a1580a0ce
--- /dev/null
+++ b/java/com/android/dialer/smartdial/SmartDialNameMatcher.java
@@ -0,0 +1,434 @@
+/*
+ * 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.dialer.smartdial;
+
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import com.android.dialer.smartdial.SmartDialPrefix.PhoneNumberTokens;
+import java.util.ArrayList;
+
+/**
+ * {@link #SmartDialNameMatcher} contains utility functions to remove accents from accented
+ * characters and normalize a phone number. It also contains the matching logic that determines if a
+ * contact's display name matches a numeric query. The boolean variable {@link #ALLOW_INITIAL_MATCH}
+ * controls the behavior of the matching logic and determines whether we allow matches like 57 -
+ * (J)ohn (S)mith.
+ */
+public class SmartDialNameMatcher {
+
+ public static final SmartDialMap LATIN_SMART_DIAL_MAP = new LatinSmartDialMap();
+ // Whether or not we allow matches like 57 - (J)ohn (S)mith
+ private static final boolean ALLOW_INITIAL_MATCH = true;
+
+ // The maximum length of the initial we will match - typically set to 1 to minimize false
+ // positives
+ private static final int INITIAL_LENGTH_LIMIT = 1;
+
+ private final ArrayList<SmartDialMatchPosition> mMatchPositions = new ArrayList<>();
+ private final SmartDialMap mMap;
+ private String mQuery;
+ private String mNameMatchMask = "";
+ private String mPhoneNumberMatchMask = "";
+
+ // Controls whether to treat an empty query as a match (with anything).
+ private boolean mShouldMatchEmptyQuery = false;
+
+ @VisibleForTesting
+ public SmartDialNameMatcher(String query) {
+ this(query, LATIN_SMART_DIAL_MAP);
+ }
+
+ public SmartDialNameMatcher(String query, SmartDialMap map) {
+ mQuery = query;
+ mMap = map;
+ }
+
+ /**
+ * Strips a phone number of unnecessary characters (spaces, dashes, etc.)
+ *
+ * @param number Phone number we want to normalize
+ * @return Phone number consisting of digits from 0-9
+ */
+ public static String normalizeNumber(String number, SmartDialMap map) {
+ return normalizeNumber(number, 0, map);
+ }
+
+ /**
+ * Strips a phone number of unnecessary characters (spaces, dashes, etc.)
+ *
+ * @param number Phone number we want to normalize
+ * @param offset Offset to start from
+ * @return Phone number consisting of digits from 0-9
+ */
+ public static String normalizeNumber(String number, int offset, SmartDialMap map) {
+ final StringBuilder s = new StringBuilder();
+ for (int i = offset; i < number.length(); i++) {
+ char ch = number.charAt(i);
+ if (map.isValidDialpadNumericChar(ch)) {
+ s.append(ch);
+ }
+ }
+ return s.toString();
+ }
+
+ /**
+ * Constructs empty highlight mask. Bit 0 at a position means there is no match, Bit 1 means there
+ * is a match and should be highlighted in the TextView.
+ *
+ * @param builder StringBuilder object
+ * @param length Length of the desired mask.
+ */
+ private void constructEmptyMask(StringBuilder builder, int length) {
+ for (int i = 0; i < length; ++i) {
+ builder.append("0");
+ }
+ }
+
+ /**
+ * Replaces the 0-bit at a position with 1-bit, indicating that there is a match.
+ *
+ * @param builder StringBuilder object.
+ * @param matchPos Match Positions to mask as 1.
+ */
+ private void replaceBitInMask(StringBuilder builder, SmartDialMatchPosition matchPos) {
+ for (int i = matchPos.start; i < matchPos.end; ++i) {
+ builder.replace(i, i + 1, "1");
+ }
+ }
+
+ /**
+ * Matches a phone number against a query. Let the test application overwrite the NANP setting.
+ *
+ * @param phoneNumber - Raw phone number
+ * @param query - Normalized query (only contains numbers from 0-9)
+ * @param useNanp - Overwriting nanp setting boolean, used for testing.
+ * @return {@literal null} if the number and the query don't match, a valid SmartDialMatchPosition
+ * with the matching positions otherwise
+ */
+ @VisibleForTesting
+ @Nullable
+ public SmartDialMatchPosition matchesNumber(String phoneNumber, String query, boolean useNanp) {
+ if (TextUtils.isEmpty(phoneNumber)) {
+ return mShouldMatchEmptyQuery ? new SmartDialMatchPosition(0, 0) : null;
+ }
+ StringBuilder builder = new StringBuilder();
+ constructEmptyMask(builder, phoneNumber.length());
+ mPhoneNumberMatchMask = builder.toString();
+
+ // Try matching the number as is
+ SmartDialMatchPosition matchPos = matchesNumberWithOffset(phoneNumber, query, 0);
+ if (matchPos == null) {
+ final PhoneNumberTokens phoneNumberTokens = SmartDialPrefix.parsePhoneNumber(phoneNumber);
+
+ if (phoneNumberTokens == null) {
+ return matchPos;
+ }
+ if (phoneNumberTokens.countryCodeOffset != 0) {
+ matchPos = matchesNumberWithOffset(phoneNumber, query, phoneNumberTokens.countryCodeOffset);
+ }
+ if (matchPos == null && phoneNumberTokens.nanpCodeOffset != 0 && useNanp) {
+ matchPos = matchesNumberWithOffset(phoneNumber, query, phoneNumberTokens.nanpCodeOffset);
+ }
+ }
+ if (matchPos != null) {
+ replaceBitInMask(builder, matchPos);
+ mPhoneNumberMatchMask = builder.toString();
+ }
+ return matchPos;
+ }
+
+ /**
+ * Matches a phone number against the saved query, taking care of formatting characters and also
+ * taking into account country code prefixes and special NANP number treatment.
+ *
+ * @param phoneNumber - Raw phone number
+ * @return {@literal null} if the number and the query don't match, a valid SmartDialMatchPosition
+ * with the matching positions otherwise
+ */
+ public SmartDialMatchPosition matchesNumber(String phoneNumber) {
+ return matchesNumber(phoneNumber, mQuery, true);
+ }
+
+ /**
+ * Matches a phone number against a query, taking care of formatting characters and also taking
+ * into account country code prefixes and special NANP number treatment.
+ *
+ * @param phoneNumber - Raw phone number
+ * @param query - Normalized query (only contains numbers from 0-9)
+ * @return {@literal null} if the number and the query don't match, a valid SmartDialMatchPosition
+ * with the matching positions otherwise
+ */
+ public SmartDialMatchPosition matchesNumber(String phoneNumber, String query) {
+ return matchesNumber(phoneNumber, query, true);
+ }
+
+ /**
+ * Matches a phone number against a query, taking care of formatting characters
+ *
+ * @param phoneNumber - Raw phone number
+ * @param query - Normalized query (only contains numbers from 0-9)
+ * @param offset - The position in the number to start the match against (used to ignore leading
+ * prefixes/country codes)
+ * @return {@literal null} if the number and the query don't match, a valid SmartDialMatchPosition
+ * with the matching positions otherwise
+ */
+ private SmartDialMatchPosition matchesNumberWithOffset(
+ String phoneNumber, String query, int offset) {
+ if (TextUtils.isEmpty(phoneNumber) || TextUtils.isEmpty(query)) {
+ return mShouldMatchEmptyQuery ? new SmartDialMatchPosition(offset, offset) : null;
+ }
+ int queryAt = 0;
+ int numberAt = offset;
+ for (int i = offset; i < phoneNumber.length(); i++) {
+ if (queryAt == query.length()) {
+ break;
+ }
+ char ch = phoneNumber.charAt(i);
+ if (mMap.isValidDialpadNumericChar(ch)) {
+ if (ch != query.charAt(queryAt)) {
+ return null;
+ }
+ queryAt++;
+ } else {
+ if (queryAt == 0) {
+ // Found a separator before any part of the query was matched, so advance the
+ // offset to avoid prematurely highlighting separators before the rest of the
+ // query.
+ // E.g. don't highlight the first '-' if we're matching 1-510-111-1111 with
+ // '510'.
+ // However, if the current offset is 0, just include the beginning separators
+ // anyway, otherwise the highlighting ends up looking weird.
+ // E.g. if we're matching (510)-111-1111 with '510', we should include the
+ // first '('.
+ if (offset != 0) {
+ offset++;
+ }
+ }
+ }
+ numberAt++;
+ }
+ return new SmartDialMatchPosition(0 + offset, numberAt);
+ }
+
+ /**
+ * This function iterates through each token in the display name, trying to match the query to the
+ * numeric equivalent of the token.
+ *
+ * <p>A token is defined as a range in the display name delimited by characters that have no latin
+ * alphabet equivalents (e.g. spaces - ' ', periods - ',', underscores - '_' or chinese characters
+ * - '王'). Transliteration from non-latin characters to latin character will be done on a best
+ * effort basis - e.g. 'Ü' - 'u'.
+ *
+ * <p>For example, the display name "Phillips Thomas Jr" contains three tokens: "phillips",
+ * "thomas", and "jr".
+ *
+ * <p>A match must begin at the start of a token. For example, typing 846(Tho) would match
+ * "Phillips Thomas", but 466(hom) would not.
+ *
+ * <p>Also, a match can extend across tokens. For example, typing 37337(FredS) would match (Fred
+ * S)mith.
+ *
+ * @param displayName The normalized(no accented characters) display name we intend to match
+ * against.
+ * @param query The string of digits that we want to match the display name to.
+ * @param matchList An array list of {@link SmartDialMatchPosition}s that we add matched positions
+ * to.
+ * @return Returns true if a combination of the tokens in displayName match the query string
+ * contained in query. If the function returns true, matchList will contain an ArrayList of
+ * match positions (multiple matches correspond to initial matches).
+ */
+ @VisibleForTesting
+ boolean matchesCombination(
+ String displayName, String query, ArrayList<SmartDialMatchPosition> matchList) {
+ StringBuilder builder = new StringBuilder();
+ constructEmptyMask(builder, displayName.length());
+ mNameMatchMask = builder.toString();
+ final int nameLength = displayName.length();
+ final int queryLength = query.length();
+
+ if (nameLength < queryLength) {
+ return false;
+ }
+
+ if (queryLength == 0) {
+ return false;
+ }
+
+ // The current character index in displayName
+ // E.g. 3 corresponds to 'd' in "Fred Smith"
+ int nameStart = 0;
+
+ // The current character in the query we are trying to match the displayName against
+ int queryStart = 0;
+
+ // The start position of the current token we are inspecting
+ int tokenStart = 0;
+
+ // The number of non-alphabetic characters we've encountered so far in the current match.
+ // E.g. if we've currently matched 3733764849 to (Fred Smith W)illiam, then the
+ // seperatorCount should be 2. This allows us to correctly calculate offsets for the match
+ // positions
+ int seperatorCount = 0;
+
+ ArrayList<SmartDialMatchPosition> partial = new ArrayList<SmartDialMatchPosition>();
+ // Keep going until we reach the end of displayName
+ while (nameStart < nameLength && queryStart < queryLength) {
+ char ch = displayName.charAt(nameStart);
+ // Strip diacritics from accented characters if any
+ ch = mMap.normalizeCharacter(ch);
+ if (mMap.isValidDialpadCharacter(ch)) {
+ if (mMap.isValidDialpadAlphabeticChar(ch)) {
+ ch = mMap.getDialpadNumericCharacter(ch);
+ }
+ if (ch != query.charAt(queryStart)) {
+ // Failed to match the current character in the query.
+
+ // Case 1: Failed to match the first character in the query. Skip to the next
+ // token since there is no chance of this token matching the query.
+
+ // Case 2: Previous characters in the query matched, but the current character
+ // failed to match. This happened in the middle of a token. Skip to the next
+ // token since there is no chance of this token matching the query.
+
+ // Case 3: Previous characters in the query matched, but the current character
+ // failed to match. This happened right at the start of the current token. In
+ // this case, we should restart the query and try again with the current token.
+ // Otherwise, we would fail to match a query like "964"(yog) against a name
+ // Yo-Yoghurt because the query match would fail on the 3rd character, and
+ // then skip to the end of the "Yoghurt" token.
+
+ if (queryStart == 0
+ || mMap.isValidDialpadCharacter(
+ mMap.normalizeCharacter(displayName.charAt(nameStart - 1)))) {
+ // skip to the next token, in the case of 1 or 2.
+ while (nameStart < nameLength
+ && mMap.isValidDialpadCharacter(
+ mMap.normalizeCharacter(displayName.charAt(nameStart)))) {
+ nameStart++;
+ }
+ nameStart++;
+ }
+
+ // Restart the query and set the correct token position
+ queryStart = 0;
+ seperatorCount = 0;
+ tokenStart = nameStart;
+ } else {
+ if (queryStart == queryLength - 1) {
+
+ // As much as possible, we prioritize a full token match over a sub token
+ // one so if we find a full token match, we can return right away
+ matchList.add(
+ new SmartDialMatchPosition(tokenStart, queryLength + tokenStart + seperatorCount));
+ for (SmartDialMatchPosition match : matchList) {
+ replaceBitInMask(builder, match);
+ }
+ mNameMatchMask = builder.toString();
+ return true;
+ } else if (ALLOW_INITIAL_MATCH && queryStart < INITIAL_LENGTH_LIMIT) {
+ // we matched the first character.
+ // branch off and see if we can find another match with the remaining
+ // characters in the query string and the remaining tokens
+ // find the next separator in the query string
+ int j;
+ for (j = nameStart; j < nameLength; j++) {
+ if (!mMap.isValidDialpadCharacter(mMap.normalizeCharacter(displayName.charAt(j)))) {
+ break;
+ }
+ }
+ // this means there is at least one character left after the separator
+ if (j < nameLength - 1) {
+ final String remainder = displayName.substring(j + 1);
+ final ArrayList<SmartDialMatchPosition> partialTemp = new ArrayList<>();
+ if (matchesCombination(remainder, query.substring(queryStart + 1), partialTemp)) {
+
+ // store the list of possible match positions
+ SmartDialMatchPosition.advanceMatchPositions(partialTemp, j + 1);
+ partialTemp.add(0, new SmartDialMatchPosition(nameStart, nameStart + 1));
+ // we found a partial token match, store the data in a
+ // temp buffer and return it if we end up not finding a full
+ // token match
+ partial = partialTemp;
+ }
+ }
+ }
+ nameStart++;
+ queryStart++;
+ // we matched the current character in the name against one in the query,
+ // continue and see if the rest of the characters match
+ }
+ } else {
+ // found a separator, we skip this character and continue to the next one
+ nameStart++;
+ if (queryStart == 0) {
+ // This means we found a separator before the start of a token,
+ // so we should increment the token's start position to reflect its true
+ // start position
+ tokenStart = nameStart;
+ } else {
+ // Otherwise this separator was found in the middle of a token being matched,
+ // so increase the separator count
+ seperatorCount++;
+ }
+ }
+ }
+ // if we have no complete match at this point, then we attempt to fall back to the partial
+ // token match(if any). If we don't allow initial matching (ALLOW_INITIAL_MATCH = false)
+ // then partial will always be empty.
+ if (!partial.isEmpty()) {
+ matchList.addAll(partial);
+ for (SmartDialMatchPosition match : matchList) {
+ replaceBitInMask(builder, match);
+ }
+ mNameMatchMask = builder.toString();
+ return true;
+ }
+ return false;
+ }
+
+ public boolean matches(String displayName) {
+ mMatchPositions.clear();
+ return matchesCombination(displayName, mQuery, mMatchPositions);
+ }
+
+ public ArrayList<SmartDialMatchPosition> getMatchPositions() {
+ // Return a clone of mMatchPositions so that the caller can use it without
+ // worrying about it changing
+ return new ArrayList<SmartDialMatchPosition>(mMatchPositions);
+ }
+
+ public String getNameMatchPositionsInString() {
+ return mNameMatchMask;
+ }
+
+ public String getNumberMatchPositionsInString() {
+ return mPhoneNumberMatchMask;
+ }
+
+ public String getQuery() {
+ return mQuery;
+ }
+
+ public void setQuery(String query) {
+ mQuery = query;
+ }
+
+ public void setShouldMatchEmptyQuery(boolean matches) {
+ mShouldMatchEmptyQuery = matches;
+ }
+}
diff --git a/java/com/android/dialer/smartdial/SmartDialPrefix.java b/java/com/android/dialer/smartdial/SmartDialPrefix.java
new file mode 100644
index 000000000..a000e21c5
--- /dev/null
+++ b/java/com/android/dialer/smartdial/SmartDialPrefix.java
@@ -0,0 +1,605 @@
+/*
+ * Copyright (C) 2013 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.dialer.smartdial;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.support.annotation.VisibleForTesting;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Smart Dial utility class to find prefixes of contacts. It contains both methods to find supported
+ * prefix combinations for contact names, and also methods to find supported prefix combinations for
+ * contacts' phone numbers. Each contact name is separated into several tokens, such as first name,
+ * middle name, family name etc. Each phone number is also separated into country code, NANP area
+ * code, and local number if such separation is possible.
+ */
+public class SmartDialPrefix {
+
+ /**
+ * The number of starting and ending tokens in a contact's name considered for initials. For
+ * example, if both constants are set to 2, and a contact's name is "Albert Ben Charles Daniel Ed
+ * Foster", the first two tokens "Albert" "Ben", and last two tokens "Ed" "Foster" can be replaced
+ * by their initials in contact name matching. Users can look up this contact by combinations of
+ * his initials such as "AF" "BF" "EF" "ABF" "BEF" "ABEF" etc, but can not use combinations such
+ * as "CF" "DF" "ACF" "ADF" etc.
+ */
+ private static final int LAST_TOKENS_FOR_INITIALS = 2;
+
+ private static final int FIRST_TOKENS_FOR_INITIALS = 2;
+
+ /** The country code of the user's sim card obtained by calling getSimCountryIso */
+ private static final String PREF_USER_SIM_COUNTRY_CODE =
+ "DialtactsActivity_user_sim_country_code";
+
+ private static final String PREF_USER_SIM_COUNTRY_CODE_DEFAULT = null;
+ /** Dialpad mapping. */
+ private static final SmartDialMap mMap = new LatinSmartDialMap();
+
+ private static String sUserSimCountryCode = PREF_USER_SIM_COUNTRY_CODE_DEFAULT;
+ /** Indicates whether user is in NANP regions. */
+ private static boolean sUserInNanpRegion = false;
+ /** Set of country names that use NANP code. */
+ private static Set<String> sNanpCountries = null;
+ /** Set of supported country codes in front of the phone number. */
+ private static Set<String> sCountryCodes = null;
+
+ private static boolean sNanpInitialized = false;
+
+ /** Initializes the Nanp settings, and finds out whether user is in a NANP region. */
+ public static void initializeNanpSettings(Context context) {
+ final TelephonyManager manager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ if (manager != null) {
+ sUserSimCountryCode = manager.getSimCountryIso();
+ }
+
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+
+ if (sUserSimCountryCode != null) {
+ /** Updates shared preferences with the latest country obtained from getSimCountryIso. */
+ prefs.edit().putString(PREF_USER_SIM_COUNTRY_CODE, sUserSimCountryCode).apply();
+ } else {
+ /** Uses previously stored country code if loading fails. */
+ sUserSimCountryCode =
+ prefs.getString(PREF_USER_SIM_COUNTRY_CODE, PREF_USER_SIM_COUNTRY_CODE_DEFAULT);
+ }
+ /** Queries the NANP country list to find out whether user is in a NANP region. */
+ sUserInNanpRegion = isCountryNanp(sUserSimCountryCode);
+ sNanpInitialized = true;
+ }
+
+ /**
+ * Parses a contact's name into a list of separated tokens.
+ *
+ * @param contactName Contact's name stored in string.
+ * @return A list of name tokens, for example separated first names, last name, etc.
+ */
+ public static ArrayList<String> parseToIndexTokens(String contactName) {
+ final int length = contactName.length();
+ final ArrayList<String> result = new ArrayList<>();
+ char c;
+ final StringBuilder currentIndexToken = new StringBuilder();
+ /**
+ * Iterates through the whole name string. If the current character is a valid character, append
+ * it to the current token. If the current character is not a valid character, for example space
+ * " ", mark the current token as complete and add it to the list of tokens.
+ */
+ for (int i = 0; i < length; i++) {
+ c = mMap.normalizeCharacter(contactName.charAt(i));
+ if (mMap.isValidDialpadCharacter(c)) {
+ /** Converts a character into the number on dialpad that represents the character. */
+ currentIndexToken.append(mMap.getDialpadIndex(c));
+ } else {
+ if (currentIndexToken.length() != 0) {
+ result.add(currentIndexToken.toString());
+ }
+ currentIndexToken.delete(0, currentIndexToken.length());
+ }
+ }
+
+ /** Adds the last token in case it has not been added. */
+ if (currentIndexToken.length() != 0) {
+ result.add(currentIndexToken.toString());
+ }
+ return result;
+ }
+
+ /**
+ * Generates a list of strings that any prefix of any string in the list can be used to look up
+ * the contact's name.
+ *
+ * @param index The contact's name in string.
+ * @return A List of strings, whose prefix can be used to look up the contact.
+ */
+ public static ArrayList<String> generateNamePrefixes(String index) {
+ final ArrayList<String> result = new ArrayList<>();
+
+ /** Parses the name into a list of tokens. */
+ final ArrayList<String> indexTokens = parseToIndexTokens(index);
+
+ if (indexTokens.size() > 0) {
+ /**
+ * Adds the full token combinations to the list. For example, a contact with name "Albert Ben
+ * Ed Foster" can be looked up by any prefix of the following strings "Foster" "EdFoster"
+ * "BenEdFoster" and "AlbertBenEdFoster". This covers all cases of look up that contains only
+ * one token, and that spans multiple continuous tokens.
+ */
+ final StringBuilder fullNameToken = new StringBuilder();
+ for (int i = indexTokens.size() - 1; i >= 0; i--) {
+ fullNameToken.insert(0, indexTokens.get(i));
+ result.add(fullNameToken.toString());
+ }
+
+ /**
+ * Adds initial combinations to the list, with the number of initials restricted by {@link
+ * #LAST_TOKENS_FOR_INITIALS} and {@link #FIRST_TOKENS_FOR_INITIALS}. For example, a contact
+ * with name "Albert Ben Ed Foster" can be looked up by any prefix of the following strings
+ * "EFoster" "BFoster" "BEFoster" "AFoster" "ABFoster" "AEFoster" and "ABEFoster". This covers
+ * all cases of initial lookup.
+ */
+ ArrayList<String> fullNames = new ArrayList<>();
+ fullNames.add(indexTokens.get(indexTokens.size() - 1));
+ final int recursiveNameStart = result.size();
+ int recursiveNameEnd = result.size();
+ String initial = "";
+ for (int i = indexTokens.size() - 2; i >= 0; i--) {
+ if ((i >= indexTokens.size() - LAST_TOKENS_FOR_INITIALS)
+ || (i < FIRST_TOKENS_FOR_INITIALS)) {
+ initial = indexTokens.get(i).substring(0, 1);
+
+ /** Recursively adds initial combinations to the list. */
+ for (int j = 0; j < fullNames.size(); ++j) {
+ result.add(initial + fullNames.get(j));
+ }
+ for (int j = recursiveNameStart; j < recursiveNameEnd; ++j) {
+ result.add(initial + result.get(j));
+ }
+ recursiveNameEnd = result.size();
+ final String currentFullName = fullNames.get(fullNames.size() - 1);
+ fullNames.add(indexTokens.get(i) + currentFullName);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Computes a list of number strings based on tokens of a given phone number. Any prefix of any
+ * string in the list can be used to look up the phone number. The list include the full phone
+ * number, the national number if there is a country code in the phone number, and the local
+ * number if there is an area code in the phone number following the NANP format. For example, if
+ * a user has phone number +41 71 394 8392, the list will contain 41713948392 and 713948392. Any
+ * prefix to either of the strings can be used to look up the phone number. If a user has a phone
+ * number +1 555-302-3029 (NANP format), the list will contain 15553023029, 5553023029, and
+ * 3023029.
+ *
+ * @param number String of user's phone number.
+ * @return A list of strings where any prefix of any entry can be used to look up the number.
+ */
+ public static ArrayList<String> parseToNumberTokens(String number) {
+ final ArrayList<String> result = new ArrayList<>();
+ if (!TextUtils.isEmpty(number)) {
+ /** Adds the full number to the list. */
+ result.add(SmartDialNameMatcher.normalizeNumber(number, mMap));
+
+ final PhoneNumberTokens phoneNumberTokens = parsePhoneNumber(number);
+ if (phoneNumberTokens == null) {
+ return result;
+ }
+
+ if (phoneNumberTokens.countryCodeOffset != 0) {
+ result.add(
+ SmartDialNameMatcher.normalizeNumber(
+ number, phoneNumberTokens.countryCodeOffset, mMap));
+ }
+
+ if (phoneNumberTokens.nanpCodeOffset != 0) {
+ result.add(
+ SmartDialNameMatcher.normalizeNumber(number, phoneNumberTokens.nanpCodeOffset, mMap));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Parses a phone number to find out whether it has country code and NANP area code.
+ *
+ * @param number Raw phone number.
+ * @return a PhoneNumberToken instance with country code, NANP code information.
+ */
+ public static PhoneNumberTokens parsePhoneNumber(String number) {
+ String countryCode = "";
+ int countryCodeOffset = 0;
+ int nanpNumberOffset = 0;
+
+ if (!TextUtils.isEmpty(number)) {
+ String normalizedNumber = SmartDialNameMatcher.normalizeNumber(number, mMap);
+ if (number.charAt(0) == '+') {
+ /** If the number starts with '+', tries to find valid country code. */
+ for (int i = 1; i <= 1 + 3; i++) {
+ if (number.length() <= i) {
+ break;
+ }
+ countryCode = number.substring(1, i);
+ if (isValidCountryCode(countryCode)) {
+ countryCodeOffset = i;
+ break;
+ }
+ }
+ } else {
+ /**
+ * If the number does not start with '+', finds out whether it is in NANP format and has '1'
+ * preceding the number.
+ */
+ if ((normalizedNumber.length() == 11)
+ && (normalizedNumber.charAt(0) == '1')
+ && (sUserInNanpRegion)) {
+ countryCode = "1";
+ countryCodeOffset = number.indexOf(normalizedNumber.charAt(1));
+ if (countryCodeOffset == -1) {
+ countryCodeOffset = 0;
+ }
+ }
+ }
+
+ /** If user is in NANP region, finds out whether a number is in NANP format. */
+ if (sUserInNanpRegion) {
+ String areaCode = "";
+ if (countryCode.equals("") && normalizedNumber.length() == 10) {
+ /**
+ * if the number has no country code but fits the NANP format, extracts the NANP area
+ * code, and finds out offset of the local number.
+ */
+ areaCode = normalizedNumber.substring(0, 3);
+ } else if (countryCode.equals("1") && normalizedNumber.length() == 11) {
+ /**
+ * If the number has country code '1', finds out area code and offset of the local number.
+ */
+ areaCode = normalizedNumber.substring(1, 4);
+ }
+ if (!areaCode.equals("")) {
+ final int areaCodeIndex = number.indexOf(areaCode);
+ if (areaCodeIndex != -1) {
+ nanpNumberOffset = number.indexOf(areaCode) + 3;
+ }
+ }
+ }
+ }
+ return new PhoneNumberTokens(countryCode, countryCodeOffset, nanpNumberOffset);
+ }
+
+ /** Checkes whether a country code is valid. */
+ private static boolean isValidCountryCode(String countryCode) {
+ if (sCountryCodes == null) {
+ sCountryCodes = initCountryCodes();
+ }
+ return sCountryCodes.contains(countryCode);
+ }
+
+ private static Set<String> initCountryCodes() {
+ final HashSet<String> result = new HashSet<String>();
+ result.add("1");
+ result.add("7");
+ result.add("20");
+ result.add("27");
+ result.add("30");
+ result.add("31");
+ result.add("32");
+ result.add("33");
+ result.add("34");
+ result.add("36");
+ result.add("39");
+ result.add("40");
+ result.add("41");
+ result.add("43");
+ result.add("44");
+ result.add("45");
+ result.add("46");
+ result.add("47");
+ result.add("48");
+ result.add("49");
+ result.add("51");
+ result.add("52");
+ result.add("53");
+ result.add("54");
+ result.add("55");
+ result.add("56");
+ result.add("57");
+ result.add("58");
+ result.add("60");
+ result.add("61");
+ result.add("62");
+ result.add("63");
+ result.add("64");
+ result.add("65");
+ result.add("66");
+ result.add("81");
+ result.add("82");
+ result.add("84");
+ result.add("86");
+ result.add("90");
+ result.add("91");
+ result.add("92");
+ result.add("93");
+ result.add("94");
+ result.add("95");
+ result.add("98");
+ result.add("211");
+ result.add("212");
+ result.add("213");
+ result.add("216");
+ result.add("218");
+ result.add("220");
+ result.add("221");
+ result.add("222");
+ result.add("223");
+ result.add("224");
+ result.add("225");
+ result.add("226");
+ result.add("227");
+ result.add("228");
+ result.add("229");
+ result.add("230");
+ result.add("231");
+ result.add("232");
+ result.add("233");
+ result.add("234");
+ result.add("235");
+ result.add("236");
+ result.add("237");
+ result.add("238");
+ result.add("239");
+ result.add("240");
+ result.add("241");
+ result.add("242");
+ result.add("243");
+ result.add("244");
+ result.add("245");
+ result.add("246");
+ result.add("247");
+ result.add("248");
+ result.add("249");
+ result.add("250");
+ result.add("251");
+ result.add("252");
+ result.add("253");
+ result.add("254");
+ result.add("255");
+ result.add("256");
+ result.add("257");
+ result.add("258");
+ result.add("260");
+ result.add("261");
+ result.add("262");
+ result.add("263");
+ result.add("264");
+ result.add("265");
+ result.add("266");
+ result.add("267");
+ result.add("268");
+ result.add("269");
+ result.add("290");
+ result.add("291");
+ result.add("297");
+ result.add("298");
+ result.add("299");
+ result.add("350");
+ result.add("351");
+ result.add("352");
+ result.add("353");
+ result.add("354");
+ result.add("355");
+ result.add("356");
+ result.add("357");
+ result.add("358");
+ result.add("359");
+ result.add("370");
+ result.add("371");
+ result.add("372");
+ result.add("373");
+ result.add("374");
+ result.add("375");
+ result.add("376");
+ result.add("377");
+ result.add("378");
+ result.add("379");
+ result.add("380");
+ result.add("381");
+ result.add("382");
+ result.add("385");
+ result.add("386");
+ result.add("387");
+ result.add("389");
+ result.add("420");
+ result.add("421");
+ result.add("423");
+ result.add("500");
+ result.add("501");
+ result.add("502");
+ result.add("503");
+ result.add("504");
+ result.add("505");
+ result.add("506");
+ result.add("507");
+ result.add("508");
+ result.add("509");
+ result.add("590");
+ result.add("591");
+ result.add("592");
+ result.add("593");
+ result.add("594");
+ result.add("595");
+ result.add("596");
+ result.add("597");
+ result.add("598");
+ result.add("599");
+ result.add("670");
+ result.add("672");
+ result.add("673");
+ result.add("674");
+ result.add("675");
+ result.add("676");
+ result.add("677");
+ result.add("678");
+ result.add("679");
+ result.add("680");
+ result.add("681");
+ result.add("682");
+ result.add("683");
+ result.add("685");
+ result.add("686");
+ result.add("687");
+ result.add("688");
+ result.add("689");
+ result.add("690");
+ result.add("691");
+ result.add("692");
+ result.add("800");
+ result.add("808");
+ result.add("850");
+ result.add("852");
+ result.add("853");
+ result.add("855");
+ result.add("856");
+ result.add("870");
+ result.add("878");
+ result.add("880");
+ result.add("881");
+ result.add("882");
+ result.add("883");
+ result.add("886");
+ result.add("888");
+ result.add("960");
+ result.add("961");
+ result.add("962");
+ result.add("963");
+ result.add("964");
+ result.add("965");
+ result.add("966");
+ result.add("967");
+ result.add("968");
+ result.add("970");
+ result.add("971");
+ result.add("972");
+ result.add("973");
+ result.add("974");
+ result.add("975");
+ result.add("976");
+ result.add("977");
+ result.add("979");
+ result.add("992");
+ result.add("993");
+ result.add("994");
+ result.add("995");
+ result.add("996");
+ result.add("998");
+ return result;
+ }
+
+ public static SmartDialMap getMap() {
+ return mMap;
+ }
+
+ /**
+ * Indicates whether the given country uses NANP numbers
+ *
+ * @param country ISO 3166 country code (case doesn't matter)
+ * @return True if country uses NANP numbers (e.g. US, Canada), false otherwise
+ * @see <a href="https://en.wikipedia.org/wiki/North_American_Numbering_Plan">
+ * https://en.wikipedia.org/wiki/North_American_Numbering_Plan</a>
+ */
+ @VisibleForTesting
+ public static boolean isCountryNanp(String country) {
+ if (TextUtils.isEmpty(country)) {
+ return false;
+ }
+ if (sNanpCountries == null) {
+ sNanpCountries = initNanpCountries();
+ }
+ return sNanpCountries.contains(country.toUpperCase());
+ }
+
+ private static Set<String> initNanpCountries() {
+ final HashSet<String> result = new HashSet<String>();
+ result.add("US"); // United States
+ result.add("CA"); // Canada
+ result.add("AS"); // American Samoa
+ result.add("AI"); // Anguilla
+ result.add("AG"); // Antigua and Barbuda
+ result.add("BS"); // Bahamas
+ result.add("BB"); // Barbados
+ result.add("BM"); // Bermuda
+ result.add("VG"); // British Virgin Islands
+ result.add("KY"); // Cayman Islands
+ result.add("DM"); // Dominica
+ result.add("DO"); // Dominican Republic
+ result.add("GD"); // Grenada
+ result.add("GU"); // Guam
+ result.add("JM"); // Jamaica
+ result.add("PR"); // Puerto Rico
+ result.add("MS"); // Montserrat
+ result.add("MP"); // Northern Mariana Islands
+ result.add("KN"); // Saint Kitts and Nevis
+ result.add("LC"); // Saint Lucia
+ result.add("VC"); // Saint Vincent and the Grenadines
+ result.add("TT"); // Trinidad and Tobago
+ result.add("TC"); // Turks and Caicos Islands
+ result.add("VI"); // U.S. Virgin Islands
+ return result;
+ }
+
+ /**
+ * Returns whether the user is in a region that uses Nanp format based on the sim location.
+ *
+ * @return Whether user is in Nanp region.
+ */
+ public static boolean getUserInNanpRegion() {
+ return sUserInNanpRegion;
+ }
+
+ /** Explicitly setting the user Nanp to the given boolean */
+ @VisibleForTesting
+ public static void setUserInNanpRegion(boolean userInNanpRegion) {
+ sUserInNanpRegion = userInNanpRegion;
+ }
+
+ /** Class to record phone number parsing information. */
+ public static class PhoneNumberTokens {
+
+ /** Country code of the phone number. */
+ final String countryCode;
+
+ /** Offset of national number after the country code. */
+ final int countryCodeOffset;
+
+ /** Offset of local number after NANP area code. */
+ final int nanpCodeOffset;
+
+ public PhoneNumberTokens(String countryCode, int countryCodeOffset, int nanpCodeOffset) {
+ this.countryCode = countryCode;
+ this.countryCodeOffset = countryCodeOffset;
+ this.nanpCodeOffset = nanpCodeOffset;
+ }
+ }
+}
diff --git a/java/com/android/dialer/spam/Spam.java b/java/com/android/dialer/spam/Spam.java
new file mode 100644
index 000000000..692a1a0ad
--- /dev/null
+++ b/java/com/android/dialer/spam/Spam.java
@@ -0,0 +1,49 @@
+/*
+ * 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.dialer.spam;
+
+import android.content.Context;
+import java.util.Objects;
+
+/** Accessor for the spam bindings. */
+public class Spam {
+
+ private static SpamBindings spamBindings;
+
+ private Spam() {}
+
+ public static SpamBindings get(Context context) {
+ Objects.requireNonNull(context);
+ if (spamBindings != null) {
+ return spamBindings;
+ }
+
+ Context application = context.getApplicationContext();
+ if (application instanceof SpamBindingsFactory) {
+ spamBindings = ((SpamBindingsFactory) application).newSpamBindings();
+ }
+
+ if (spamBindings == null) {
+ spamBindings = new SpamBindingsStub();
+ }
+ return spamBindings;
+ }
+
+ public static void setForTesting(SpamBindings spamBindings) {
+ Spam.spamBindings = spamBindings;
+ }
+}
diff --git a/java/com/android/dialer/spam/SpamBindings.java b/java/com/android/dialer/spam/SpamBindings.java
new file mode 100644
index 000000000..b5d18b828
--- /dev/null
+++ b/java/com/android/dialer/spam/SpamBindings.java
@@ -0,0 +1,146 @@
+/*
+ * 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.dialer.spam;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+/** Allows the container application to mark calls as spam. */
+public interface SpamBindings {
+
+ boolean isSpamEnabled();
+
+ boolean isSpamNotificationEnabled();
+
+ boolean isDialogEnabledForSpamNotification();
+
+ boolean isDialogReportSpamCheckedByDefault();
+
+ /** @return what percentage of aftercall notifications to show to the user */
+ int percentOfSpamNotificationsToShow();
+
+ int percentOfNonSpamNotificationsToShow();
+
+ /**
+ * Checks if the given number is suspected of being a spamer.
+ *
+ * @param number The phone number of the call.
+ * @param countryIso The country ISO of the call.
+ * @param listener The callback to be invoked after {@code Info} is fetched.
+ */
+ void checkSpamStatus(String number, String countryIso, Listener listener);
+
+ /**
+ * @param number The number to check if the number is in the user's white list (non spam list)
+ * @param countryIso The country ISO of the call.
+ * @param listener The callback to be invoked after {@code Info} is fetched.
+ */
+ void checkUserMarkedNonSpamStatus(
+ String number, @Nullable String countryIso, @NonNull Listener listener);
+
+ /**
+ * @param number The number to check if it is in user's spam list
+ * @param countryIso The country ISO of the call.
+ * @param listener The callback to be invoked after {@code Info} is fetched.
+ */
+ void checkUserMarkedSpamStatus(
+ String number, @Nullable String countryIso, @NonNull Listener listener);
+
+ /**
+ * @param number The number to check if it is in the global spam list
+ * @param countryIso The country ISO of the call.
+ * @param listener The callback to be invoked after {@code Info} is fetched.
+ */
+ void checkGlobalSpamListStatus(
+ String number, @Nullable String countryIso, @NonNull Listener listener);
+
+ /**
+ * Synchronously checks if the given number is suspected of being a spamer.
+ *
+ * @param number The phone number of the call.
+ * @param countryIso The country ISO of the call.
+ * @return True if the number is spam.
+ */
+ boolean checkSpamStatusSynchronous(String number, String countryIso);
+
+ /**
+ * Reports number as spam.
+ *
+ * @param number The number to be reported.
+ * @param countryIso The country ISO of the number.
+ * @param callType Whether the type of call is missed, voicemail, etc. Example of this is {@link
+ * android.provider.CallLog.Calls#VOICEMAIL_TYPE}.
+ * @param from Where in the dialer this was reported from. Must be one of {@link
+ * com.android.dialer.logging.nano.ReportingLocation}.
+ * @param contactLookupResultType The result of the contact lookup for this phone number. Must be
+ * one of {@link com.android.dialer.logging.nano.ContactLookupResult}.
+ */
+ void reportSpamFromAfterCallNotification(
+ String number, String countryIso, int callType, int from, int contactLookupResultType);
+
+ /**
+ * Reports number as spam.
+ *
+ * @param number The number to be reported.
+ * @param countryIso The country ISO of the number.
+ * @param callType Whether the type of call is missed, voicemail, etc. Example of this is {@link
+ * android.provider.CallLog.Calls#VOICEMAIL_TYPE}.
+ * @param from Where in the dialer this was reported from. Must be one of {@link
+ * com.android.dialer.logging.nano.ReportingLocation}.
+ * @param contactSourceType If we have cached contact information for the phone number, this
+ * indicates its source. Must be one of {@link com.android.dialer.logging.nano.ContactSource}.
+ */
+ void reportSpamFromCallHistory(
+ String number, String countryIso, int callType, int from, int contactSourceType);
+
+ /**
+ * Reports number as not spam.
+ *
+ * @param number The number to be reported.
+ * @param countryIso The country ISO of the number.
+ * @param callType Whether the type of call is missed, voicemail, etc. Example of this is {@link
+ * android.provider.CallLog.Calls#VOICEMAIL_TYPE}.
+ * @param from Where in the dialer this was reported from. Must be one of {@link
+ * com.android.dialer.logging.nano.ReportingLocation}.
+ * @param contactLookupResultType The result of the contact lookup for this phone number. Must be
+ * one of {@link com.android.dialer.logging.nano.ContactLookupResult}.
+ */
+ void reportNotSpamFromAfterCallNotification(
+ String number, String countryIso, int callType, int from, int contactLookupResultType);
+
+ /**
+ * Reports number as not spam.
+ *
+ * @param number The number to be reported.
+ * @param countryIso The country ISO of the number.
+ * @param callType Whether the type of call is missed, voicemail, etc. Example of this is {@link
+ * android.provider.CallLog.Calls#VOICEMAIL_TYPE}.
+ * @param from Where in the dialer this was reported from. Must be one of {@link
+ * com.android.dialer.logging.nano.ReportingLocation}.
+ * @param contactSourceType If we have cached contact information for the phone number, this
+ * indicates its source. Must be one of {@link com.android.dialer.logging.nano.ContactSource}.
+ */
+ void reportNotSpamFromCallHistory(
+ String number, String countryIso, int callType, int from, int contactSourceType);
+
+ /** Callback to be invoked when data is fetched. */
+ interface Listener {
+
+ /** Called when data is fetched. */
+ void onComplete(boolean isSpam);
+ }
+}
diff --git a/java/com/android/dialer/spam/SpamBindingsFactory.java b/java/com/android/dialer/spam/SpamBindingsFactory.java
new file mode 100644
index 000000000..41144e1ee
--- /dev/null
+++ b/java/com/android/dialer/spam/SpamBindingsFactory.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dialer.spam;
+
+/**
+ * This interface should be implementated by the Application subclass. It allows this module to get
+ * references to the SpamBindings.
+ */
+public interface SpamBindingsFactory {
+
+ SpamBindings newSpamBindings();
+}
diff --git a/java/com/android/dialer/spam/SpamBindingsStub.java b/java/com/android/dialer/spam/SpamBindingsStub.java
new file mode 100644
index 000000000..08939530c
--- /dev/null
+++ b/java/com/android/dialer/spam/SpamBindingsStub.java
@@ -0,0 +1,92 @@
+/*
+ * 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.dialer.spam;
+
+/** Default implementation of SpamBindings. */
+public class SpamBindingsStub implements SpamBindings {
+
+ @Override
+ public boolean isSpamEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isSpamNotificationEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isDialogEnabledForSpamNotification() {
+ return false;
+ }
+
+ @Override
+ public boolean isDialogReportSpamCheckedByDefault() {
+ return false;
+ }
+
+ @Override
+ public int percentOfSpamNotificationsToShow() {
+ return 0;
+ }
+
+ @Override
+ public int percentOfNonSpamNotificationsToShow() {
+ return 0;
+ }
+
+ @Override
+ public void checkSpamStatus(String number, String countryIso, Listener listener) {
+ listener.onComplete(false);
+ }
+
+ @Override
+ public void checkUserMarkedNonSpamStatus(String number, String countryIso, Listener listener) {
+ listener.onComplete(false);
+ }
+
+ @Override
+ public void checkUserMarkedSpamStatus(String number, String countryIso, Listener listener) {
+ listener.onComplete(false);
+ }
+
+ @Override
+ public void checkGlobalSpamListStatus(String number, String countryIso, Listener listener) {
+ listener.onComplete(false);
+ }
+
+ @Override
+ public boolean checkSpamStatusSynchronous(String number, String countryIso) {
+ return false;
+ }
+
+ @Override
+ public void reportSpamFromAfterCallNotification(
+ String number, String countryIso, int callType, int from, int contactLookupResultType) {}
+
+ @Override
+ public void reportSpamFromCallHistory(
+ String number, String countryIso, int callType, int from, int contactSourceType) {}
+
+ @Override
+ public void reportNotSpamFromAfterCallNotification(
+ String number, String countryIso, int callType, int from, int contactLookupResultType) {}
+
+ @Override
+ public void reportNotSpamFromCallHistory(
+ String number, String countryIso, int callType, int from, int contactSourceType) {}
+}
diff --git a/java/com/android/dialer/telecom/TelecomUtil.java b/java/com/android/dialer/telecom/TelecomUtil.java
new file mode 100644
index 000000000..a11e7f77a
--- /dev/null
+++ b/java/com/android/dialer/telecom/TelecomUtil.java
@@ -0,0 +1,212 @@
+/*
+ * 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.dialer.telecom;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.provider.CallLog.Calls;
+import android.support.annotation.Nullable;
+import android.support.v4.content.ContextCompat;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.text.TextUtils;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Performs permission checks before calling into TelecomManager. Each method is self-explanatory -
+ * perform the required check and return the fallback default if the permission is missing,
+ * otherwise return the value from TelecomManager.
+ */
+public class TelecomUtil {
+
+ private static final String TAG = "TelecomUtil";
+ private static boolean sWarningLogged = false;
+
+ public static void showInCallScreen(Context context, boolean showDialpad) {
+ if (hasReadPhoneStatePermission(context)) {
+ try {
+ getTelecomManager(context).showInCallScreen(showDialpad);
+ } catch (SecurityException e) {
+ // Just in case
+ Log.w(TAG, "TelecomManager.showInCallScreen called without permission.");
+ }
+ }
+ }
+
+ public static void silenceRinger(Context context) {
+ if (hasModifyPhoneStatePermission(context)) {
+ try {
+ getTelecomManager(context).silenceRinger();
+ } catch (SecurityException e) {
+ // Just in case
+ Log.w(TAG, "TelecomManager.silenceRinger called without permission.");
+ }
+ }
+ }
+
+ public static void cancelMissedCallsNotification(Context context) {
+ if (hasModifyPhoneStatePermission(context)) {
+ try {
+ getTelecomManager(context).cancelMissedCallsNotification();
+ } catch (SecurityException e) {
+ Log.w(TAG, "TelecomManager.cancelMissedCalls called without permission.");
+ }
+ }
+ }
+
+ public static Uri getAdnUriForPhoneAccount(Context context, PhoneAccountHandle handle) {
+ if (hasModifyPhoneStatePermission(context)) {
+ try {
+ return getTelecomManager(context).getAdnUriForPhoneAccount(handle);
+ } catch (SecurityException e) {
+ Log.w(TAG, "TelecomManager.getAdnUriForPhoneAccount called without permission.");
+ }
+ }
+ return null;
+ }
+
+ public static boolean handleMmi(
+ Context context, String dialString, @Nullable PhoneAccountHandle handle) {
+ if (hasModifyPhoneStatePermission(context)) {
+ try {
+ if (handle == null) {
+ return getTelecomManager(context).handleMmi(dialString);
+ } else {
+ return getTelecomManager(context).handleMmi(dialString, handle);
+ }
+ } catch (SecurityException e) {
+ Log.w(TAG, "TelecomManager.handleMmi called without permission.");
+ }
+ }
+ return false;
+ }
+
+ @Nullable
+ public static PhoneAccountHandle getDefaultOutgoingPhoneAccount(
+ Context context, String uriScheme) {
+ if (hasReadPhoneStatePermission(context)) {
+ return getTelecomManager(context).getDefaultOutgoingPhoneAccount(uriScheme);
+ }
+ return null;
+ }
+
+ public static PhoneAccount getPhoneAccount(Context context, PhoneAccountHandle handle) {
+ return getTelecomManager(context).getPhoneAccount(handle);
+ }
+
+ public static List<PhoneAccountHandle> getCallCapablePhoneAccounts(Context context) {
+ if (hasReadPhoneStatePermission(context)) {
+ return getTelecomManager(context).getCallCapablePhoneAccounts();
+ }
+ return new ArrayList<>();
+ }
+
+ public static boolean isInCall(Context context) {
+ if (hasReadPhoneStatePermission(context)) {
+ return getTelecomManager(context).isInCall();
+ }
+ return false;
+ }
+
+ public static boolean isVoicemailNumber(
+ Context context, PhoneAccountHandle accountHandle, String number) {
+ if (hasReadPhoneStatePermission(context)) {
+ return getTelecomManager(context).isVoiceMailNumber(accountHandle, number);
+ }
+ return false;
+ }
+
+ @Nullable
+ public static String getVoicemailNumber(Context context, PhoneAccountHandle accountHandle) {
+ if (hasReadPhoneStatePermission(context)) {
+ return getTelecomManager(context).getVoiceMailNumber(accountHandle);
+ }
+ return null;
+ }
+
+ /**
+ * Tries to place a call using the {@link TelecomManager}.
+ *
+ * @param context context.
+ * @param intent the call intent.
+ * @return {@code true} if we successfully attempted to place the call, {@code false} if it failed
+ * due to a permission check.
+ */
+ public static boolean placeCall(Context context, Intent intent) {
+ if (hasCallPhonePermission(context)) {
+ getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());
+ return true;
+ }
+ return false;
+ }
+
+ public static Uri getCallLogUri(Context context) {
+ return hasReadWriteVoicemailPermissions(context)
+ ? Calls.CONTENT_URI_WITH_VOICEMAIL
+ : Calls.CONTENT_URI;
+ }
+
+ public static boolean hasReadWriteVoicemailPermissions(Context context) {
+ return isDefaultDialer(context)
+ || (hasPermission(context, Manifest.permission.READ_VOICEMAIL)
+ && hasPermission(context, Manifest.permission.WRITE_VOICEMAIL));
+ }
+
+ public static boolean hasModifyPhoneStatePermission(Context context) {
+ return isDefaultDialer(context)
+ || hasPermission(context, Manifest.permission.MODIFY_PHONE_STATE);
+ }
+
+ public static boolean hasReadPhoneStatePermission(Context context) {
+ return isDefaultDialer(context) || hasPermission(context, Manifest.permission.READ_PHONE_STATE);
+ }
+
+ public static boolean hasCallPhonePermission(Context context) {
+ return isDefaultDialer(context) || hasPermission(context, Manifest.permission.CALL_PHONE);
+ }
+
+ private static boolean hasPermission(Context context, String permission) {
+ return ContextCompat.checkSelfPermission(context, permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ public static boolean isDefaultDialer(Context context) {
+ final boolean result =
+ TextUtils.equals(
+ context.getPackageName(), getTelecomManager(context).getDefaultDialerPackage());
+ if (result) {
+ sWarningLogged = false;
+ } else {
+ if (!sWarningLogged) {
+ // Log only once to prevent spam.
+ Log.w(TAG, "Dialer is not currently set to be default dialer");
+ sWarningLogged = true;
+ }
+ }
+ return result;
+ }
+
+ private static TelecomManager getTelecomManager(Context context) {
+ return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+ }
+}
diff --git a/java/com/android/dialer/theme/AndroidManifest.xml b/java/com/android/dialer/theme/AndroidManifest.xml
new file mode 100644
index 000000000..7c1e4effd
--- /dev/null
+++ b/java/com/android/dialer/theme/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.dialer.theme">
+</manifest>
diff --git a/java/com/android/dialer/theme/res/anim/front_back_switch_button_animation.xml b/java/com/android/dialer/theme/res/anim/front_back_switch_button_animation.xml
new file mode 100644
index 000000000..30986457b
--- /dev/null
+++ b/java/com/android/dialer/theme/res/anim/front_back_switch_button_animation.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <set
+ android:ordering="sequentially">
+ <objectAnimator
+ android:duration="500"
+ android:propertyName="rotation"
+ android:valueFrom="0.0"
+ android:valueTo="-180.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in"/>
+ </set>
+</set> \ No newline at end of file
diff --git a/java/com/android/dialer/theme/res/animator/activated_button_elevation.xml b/java/com/android/dialer/theme/res/animator/activated_button_elevation.xml
new file mode 100644
index 000000000..b8ea4e8e6
--- /dev/null
+++ b/java/com/android/dialer/theme/res/animator/activated_button_elevation.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_enabled="true"
+ android:state_activated="true">
+ <objectAnimator
+ android:duration="@android:integer/config_shortAnimTime"
+ android:propertyName="translationZ"
+ android:valueFrom="0dp"
+ android:valueTo="4dp"
+ android:valueType="floatType"/>
+ </item>
+ <item>
+ <objectAnimator
+ android:duration="@android:integer/config_shortAnimTime"
+ android:propertyName="translationZ"
+ android:valueFrom="4dp"
+ android:valueTo="0dp"
+ android:valueType="floatType"/>
+ </item>
+</selector>
diff --git a/java/com/android/dialer/theme/res/animator/button_elevation.xml b/java/com/android/dialer/theme/res/animator/button_elevation.xml
new file mode 100644
index 000000000..8dd019e14
--- /dev/null
+++ b/java/com/android/dialer/theme/res/animator/button_elevation.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_enabled="true"
+ android:state_pressed="true">
+ <objectAnimator
+ android:duration="@android:integer/config_shortAnimTime"
+ android:propertyName="translationZ"
+ android:valueFrom="0dp"
+ android:valueTo="4dp"
+ android:valueType="floatType"/>
+ </item>
+ <item>
+ <objectAnimator
+ android:duration="@android:integer/config_shortAnimTime"
+ android:propertyName="translationZ"
+ android:valueFrom="4dp"
+ android:valueTo="0dp"
+ android:valueType="floatType"/>
+ </item>
+</selector>
diff --git a/java/com/android/dialer/theme/res/drawable/front_back_switch_button.xml b/java/com/android/dialer/theme/res/drawable/front_back_switch_button.xml
new file mode 100644
index 000000000..2dc3eb1fa
--- /dev/null
+++ b/java/com/android/dialer/theme/res/drawable/front_back_switch_button.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="front_back_switch_button"
+ android:width="56dp"
+ android:viewportWidth="56"
+ android:height="56dp"
+ android:viewportHeight="56">
+ <group
+ android:name="layer_3_outlines"
+ android:translateX="32.0015"
+ android:translateY="27.00208">
+ <group
+ android:name="layer_3_outlines_pivot"
+ android:translateX="-4.25"
+ android:translateY="-7.25">
+ <group
+ android:name="group_1"
+ android:translateX="4.25"
+ android:translateY="7.25001">
+ <path
+ android:name="path_1"
+ android:pathData="M 2.0,-5.0 c 0.0,0.0 -2.16999816895,0.0 -2.16999816895,0.0 c 0.0,0.0 -1.83000183105,-2.0 -1.83000183105,-2.0 c 0.0,0.0 -2.0,0.0 -2.0,0.0 c 0.0,0.0 0.0,5.0 0.0,5.0 c 1.65600585938,0.0 3.0,1.34399414062 3.0,3.0 c 0.0,1.65600585938 -1.34399414062,3.0 -3.0,3.0 c 0.0,0.0 -3.0,0.00181579589844 -3.0,0.00181579589844 c 0.0,0.0 0.001953125,2.99415588379 0.001953125,2.99415588379 c 0.0,0.0 2.998046875,0.0040283203125 2.998046875,0.0040283203125 c 0.0,0.0 6.0,0.0 6.0,0.0 c 1.10000610352,0.0 2.0,-0.899993896484 2.0,-2.0 c 0.0,0.0 0.0,-8.0 0.0,-8.0 c 0.0,-1.10000610352 -0.899993896484,-2.0 -2.0,-2.0 Z"
+ android:fillColor="#FFFFFFFF"/>
+ </group>
+ </group>
+ </group>
+ <group
+ android:name="layer_1_outlines"
+ android:translateX="24.00099"
+ android:translateY="26.99992">
+ <group
+ android:name="layer_1_outlines_pivot"
+ android:translateX="-4.249"
+ android:translateY="-7.25">
+ <group
+ android:name="group_2"
+ android:translateX="4.249"
+ android:translateY="7.25">
+ <path
+ android:name="path_2"
+ android:pathData="M 3.99900817871,4.0 c -1.65501403809,-0.00099182128906 -2.99800109863,-1.34399414062 -2.99800109863,-3.0 c 0.0,-1.6549987793 1.34298706055,-2.99899291992 2.99800109863,-3.0 c 0.0,0.0 1.0,0.00008 1.0,0.00008 c 0.0,0.0 0.0,-5.0 0.0,-5.0 c 0.0,0.0 -1.0,-0.00008 -1.0,-0.00008 c 0.0,0.0 -1.99800109863,0.0 -1.99800109863,0.0 c 0.0,0.0 -1.83000183105,2.0 -1.83000183105,2.0 c 0.0,0.0 -2.17001342773,0.0 -2.17001342773,0.0 c -1.1009979248,0.0 -2.0,0.899993896484 -2.0,2.0 c 0.0,0.0 0.0,8.0 0.0,8.0 c 0.0,1.10000610352 0.899002075195,2.0 2.0,2.0 c 0.0,0.0 5.99801635742,0.0 5.99801635742,0.0 c 0.0,0.0 0.0,-3.0 0.0,-3.0 Z"
+ android:fillColor="#FFFFFFFF"/>
+ </group>
+ </group>
+ </group>
+ <group
+ android:name="layer_10_outlines"
+ android:translateX="28.00001"
+ android:translateY="27.99999">
+ <group
+ android:name="layer_10_outlines_pivot"
+ android:translateX="-19.25"
+ android:translateY="-19.25005">
+ <group
+ android:name="group_3"
+ android:translateX="20.25"
+ android:translateY="9.75001">
+ <path
+ android:name="path_3"
+ android:pathData="M 12.4349975586,-3.93499755859 c -3.70999145508,-3.71000671387 -8.57299804688,-5.56500244141 -13.4349975586,-5.56500244141 c -4.8630065918,0.0 -9.72500610352,1.85499572754 -13.4349975586,5.56500244141 c -0.337005615234,0.336990356445 -0.652008056641,0.688003540039 -0.956008911133,1.04399108887 c 0.0,0.0 -2.60899353027,-2.60899353027 -2.60899353027,-2.60899353027 c 0.0,0.0 0.0,7.0 0.0,7.0 c 0.0,0.0 7.0,0.0 7.0,0.0 c 0.0,0.0 -2.97300720215,-2.97300720215 -2.97300720215,-2.97300720215 c 0.300003051758,-0.360992431641 0.616012573242,-0.711990356445 0.952011108398,-1.0479888916 c 3.21099853516,-3.21099853516 7.47999572754,-4.97900390625 12.0209960938,-4.97900390625 c 4.54100036621,0.0 8.80999755859,1.76800537109 12.0209960938,4.97900390625 c 3.31401062012,3.31399536133 4.97100830078,7.66799926758 4.97100830078,12.0209960938 c 0.0,0.0 2.00799560547,0.0 2.00799560547,0.0 c 0.0,-4.86199951172 -1.85499572754,-9.72500610352 -5.56500244141,-13.4349975586 Z"
+ android:fillColor="#FFFFFFFF"/>
+ </group>
+ <group
+ android:name="group_4"
+ android:translateX="18.25"
+ android:translateY="28.75011">
+ <path
+ android:name="path_4"
+ android:pathData="M 18.0,-1.5 c 0.0,0.0 -7.0,0.0 -7.0,0.0 c 0.0,0.0 2.97300720215,2.97300720215 2.97300720215,2.97300720215 c -0.300003051758,0.360992431641 -0.616012573242,0.711990356445 -0.952011108398,1.0479888916 c -3.21099853516,3.21099853516 -7.47999572754,4.97900390625 -12.0209960938,4.97900390625 c -4.54100036621,0.0 -8.80999755859,-1.76800537109 -12.0209960938,-4.97900390625 c -3.31401062012,-3.31399536133 -4.97100830078,-7.66799926758 -4.97100830078,-12.0209960938 c 0.0,0.0 -2.00799560547,0.0 -2.00799560547,0.0 c 0.0,4.86199951172 1.85499572754,9.72500610352 5.56500244141,13.4349975586 c 3.70999145508,3.71000671387 8.57299804688,5.56500244141 13.4349975586,5.56500244141 c 4.8630065918,0.0 9.72500610352,-1.85499572754 13.4349975586,-5.56500244141 c 0.337005615234,-0.336990356445 0.652008056641,-0.688003540039 0.956008911133,-1.04400634766 c 0.0,0.0 2.60899353027,2.60900878906 2.60899353027,2.60900878906 c 0.0,0.0 0.0,-7.0 0.0,-7.0 Z"
+ android:fillColor="#FFFFFFFF"/>
+ </group>
+ </group>
+ </group>
+</vector> \ No newline at end of file
diff --git a/java/com/android/dialer/theme/res/drawable/front_back_switch_button_animation.xml b/java/com/android/dialer/theme/res/drawable/front_back_switch_button_animation.xml
new file mode 100644
index 000000000..14cda1ba8
--- /dev/null
+++ b/java/com/android/dialer/theme/res/drawable/front_back_switch_button_animation.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/front_back_switch_button">
+ <target
+ android:name="layer_10_outlines"
+ android:animation="@anim/front_back_switch_button_animation"/>
+</animated-vector> \ No newline at end of file
diff --git a/java/com/android/dialer/theme/res/values/colors.xml b/java/com/android/dialer/theme/res/values/colors.xml
new file mode 100644
index 000000000..bf43e01af
--- /dev/null
+++ b/java/com/android/dialer/theme/res/values/colors.xml
@@ -0,0 +1,64 @@
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <!-- Note: The following colors are used in the Dialer settings screens. Since Dialer's settings
+ link into the Telephony settings as well, changes to these colors should be mirrored in
+ Telephony:
+
+ Android source path: packages/apps/PhoneCommon/res/values/colors.xml
+ - Local: dialer_theme_color Android Source: dialer_theme_color
+ - Local: dialer_theme_color_dark Android Source: dialer_theme_color_dark
+ Android source path: packages/services/Telecomm/res/values/colors.xml
+ - Local: dialer_theme_color Android Source: theme_color
+ - Local: dialer_theme_color_dark Android Source: dialer_settings_color_dark
+ -->
+ <color name="dialer_theme_color">#2A56C6</color>
+ <color name="dialer_theme_color_dark">#1C3AA9</color>
+
+ <color name="dialer_snackbar_action_text_color">#4285F4</color>
+ <color name="dialer_theme_color_20pct">#332A56C6</color>
+
+ <color name="dialer_secondary_color">#e91e63</color>
+
+ <!-- Primary text color in the Phone app -->
+ <color name="dialer_primary_text_color">#333333</color>
+ <color name="dialer_edit_text_hint_color">#DE78909C</color>
+
+ <!-- Secondary text color in the Phone app -->
+ <color name="dialer_secondary_text_color">#636363</color>
+
+ <!-- Color of the theme of the Dialer app -->
+ <color name="dialtacts_theme_color">@color/dialer_theme_color</color>
+
+ <!-- White background for dialer -->
+ <color name="background_dialer_white">#ffffff</color>
+ <color name="background_dialer_call_log_list_item">@color/background_dialer_white</color>
+
+ <!-- Colors for the notification actions -->
+ <color name="notification_action_accept">#097138</color>
+ <color name="notification_action_dismiss">#A52714</color>
+ <color name="notification_action_end_call">#FFFFFF</color>
+ <color name="notification_action_answer_video">#097138</color>
+
+ <!-- Background color of action bars -->
+ <color name="actionbar_background_color">@color/dialer_theme_color</color>
+
+ <!-- Background color of title bars in recents -->
+ <color name="titlebar_in_recents_background_color">@color/dialer_theme_color_dark</color>
+
+ <color name="blue_grey_100">#CFD8DC</color>
+</resources>
diff --git a/java/com/android/dialer/theme/res/values/dimens.xml b/java/com/android/dialer/theme/res/values/dimens.xml
new file mode 100644
index 000000000..2d11ecc84
--- /dev/null
+++ b/java/com/android/dialer/theme/res/values/dimens.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="call_log_action_icon_margin_start">16dp</dimen>
+ <dimen name="call_log_action_icon_dimen">24dp</dimen>
+ <dimen name="call_log_action_horizontal_padding">24dp</dimen>
+
+ <dimen name="call_log_actions_left_padding">64dp</dimen>
+ <dimen name="call_log_actions_top_padding">8dp</dimen>
+ <dimen name="call_log_actions_bottom_padding">8dp</dimen>
+ <dimen name="call_log_primary_text_size">16sp</dimen>
+ <dimen name="call_log_detail_text_size">12sp</dimen>
+ <dimen name="call_log_day_group_heading_size">14sp</dimen>
+ <dimen name="call_log_voicemail_transcription_text_size">14sp</dimen>
+ <!-- Height of the call log actions section for each call log entry -->
+ <dimen name="call_log_action_height">48dp</dimen>
+ <dimen name="call_log_day_group_padding_top">15dp</dimen>
+ <dimen name="call_log_day_group_padding_bottom">9dp</dimen>
+
+ <!-- Height of the actionBar - this is 8dps bigger than the platform standard to give more
+ room to the search box-->
+ <dimen name="action_bar_height">56dp</dimen>
+ <dimen name="action_bar_height_large">64dp</dimen>
+ <dimen name="action_bar_elevation">3dp</dimen>
+ <dimen name="tab_height">48dp</dimen>
+ <!-- actionbar height + tab height -->
+ <dimen name="actionbar_and_tab_height">107dp</dimen>
+ <dimen name="actionbar_contentInsetStart">72dp</dimen>
+</resources>
diff --git a/java/com/android/dialer/theme/res/values/strings.xml b/java/com/android/dialer/theme/res/values/strings.xml
new file mode 100644
index 000000000..3a954ae14
--- /dev/null
+++ b/java/com/android/dialer/theme/res/values/strings.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <!-- String used to display calls from unknown numbers in the call log -->
+ <string name="unknown">Unknown</string>
+
+ <!-- String used to display calls from pay phone in the call log -->
+ <string name="payphone">Payphone</string>
+
+ <!-- Title for the activity that dials the phone. This is the name
+ used in the Launcher icon. -->
+ <string name="launcherActivityLabel">Phone</string>
+</resources>
diff --git a/java/com/android/dialer/theme/res/values/styles.xml b/java/com/android/dialer/theme/res/values/styles.xml
new file mode 100644
index 000000000..ac94d0687
--- /dev/null
+++ b/java/com/android/dialer/theme/res/values/styles.xml
@@ -0,0 +1,56 @@
+<!--
+ ~ 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
+ -->
+
+<resources>
+
+ <style name="CallLogCardStyle" parent="CardView">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_margin">4dp</item>
+ <item name="android:baselineAligned">false</item>
+ <item name="cardCornerRadius">2dp</item>
+ <item name="cardBackgroundColor">@color/background_dialer_call_log_list_item</item>
+ </style>
+
+ <!-- Inherit from Theme.Material.Light.Dialog instead of Theme.Material.Light.Dialog.Alert
+ since the Alert dialog is private. They are identical anyway. -->
+ <style name="AlertDialogTheme" parent="@android:style/Theme.Material.Light.Dialog">
+ <item name="android:colorAccent">@color/dialtacts_theme_color</item>
+ </style>
+
+ <style name="TextActionStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">@dimen/call_log_action_height</item>
+ <item name="android:gravity">end|center_vertical</item>
+ <item name="android:paddingStart">@dimen/call_log_action_horizontal_padding</item>
+ <item name="android:paddingEnd">@dimen/call_log_action_horizontal_padding</item>
+ <item name="android:textColor">@color/dialtacts_theme_color</item>
+ <item name="android:fontFamily">"sans-serif-medium"</item>
+ <item name="android:focusable">true</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:textAllCaps">true</item>
+ </style>
+
+ <style name="DialerButtonTextStyle" parent="@android:style/TextAppearance.Material.Widget.Button">
+ <item name="android:textColor">#fff</item>
+ </style>
+
+ <style name="DialerActionBarBaseStyle"
+ parent="@style/Widget.AppCompat.Light.ActionBar.Solid.Inverse">
+ <item name="android:background">@color/actionbar_background_color</item>
+ <item name="background">@color/actionbar_background_color</item>
+ </style>
+</resources>
diff --git a/java/com/android/dialer/theme/res/values/themes.xml b/java/com/android/dialer/theme/res/values/themes.xml
new file mode 100644
index 000000000..452b36929
--- /dev/null
+++ b/java/com/android/dialer/theme/res/values/themes.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <style name="DialerThemeBase" parent="@style/Theme.AppCompat.Light.DarkActionBar">
+ <item name="android:textColorPrimary">@color/dialer_primary_text_color</item>
+ <item name="android:textColorSecondary">@color/dialer_secondary_text_color</item>
+ <!-- This is used for title bar color in recents -->
+ <item name="android:colorPrimary">@color/titlebar_in_recents_background_color</item>
+ <item name="android:colorPrimaryDark">@color/dialer_theme_color_dark</item>
+ <item name="android:colorControlActivated">@color/dialer_theme_color</item>
+ <item name="android:colorButtonNormal">@color/dialer_theme_color</item>
+ <item name="android:colorAccent">@color/dialtacts_theme_color</item>
+ <item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
+ <item name="android:textAppearanceButton">@style/DialerButtonTextStyle</item>
+
+ <item name="android:actionBarStyle">@style/DialerActionBarBaseStyle</item>
+ <item name="actionBarStyle">@style/DialerActionBarBaseStyle</item>
+ <item name="android:actionBarSize">@dimen/action_bar_height</item>
+ <item name="actionBarSize">@dimen/action_bar_height</item>
+ </style>
+</resources>
diff --git a/java/com/android/dialer/util/AndroidManifest.xml b/java/com/android/dialer/util/AndroidManifest.xml
new file mode 100644
index 000000000..499df9b4e
--- /dev/null
+++ b/java/com/android/dialer/util/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.dialer.util">
+</manifest>
diff --git a/java/com/android/dialer/util/CallUtil.java b/java/com/android/dialer/util/CallUtil.java
new file mode 100644
index 000000000..81a4bb21e
--- /dev/null
+++ b/java/com/android/dialer/util/CallUtil.java
@@ -0,0 +1,135 @@
+/*
+ * 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.dialer.util;
+
+import android.content.Context;
+import android.net.Uri;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import java.util.List;
+
+/** Utilities related to calls that can be used by non system apps. */
+public class CallUtil {
+
+ /** Indicates that the video calling is not available. */
+ public static final int VIDEO_CALLING_DISABLED = 0;
+
+ /** Indicates that video calling is enabled, regardless of presence status. */
+ public static final int VIDEO_CALLING_ENABLED = 1;
+
+ /**
+ * Indicates that video calling is enabled, but the availability of video call affordances is
+ * determined by the presence status associated with contacts.
+ */
+ public static final int VIDEO_CALLING_PRESENCE = 2;
+
+ /** Return Uri with an appropriate scheme, accepting both SIP and usual phone call numbers. */
+ public static Uri getCallUri(String number) {
+ if (PhoneNumberHelper.isUriNumber(number)) {
+ return Uri.fromParts(PhoneAccount.SCHEME_SIP, number, null);
+ }
+ return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
+ }
+
+ /** @return Uri that directly dials a user's voicemail inbox. */
+ public static Uri getVoicemailUri() {
+ return Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "", null);
+ }
+
+ /**
+ * Determines if video calling is available, and if so whether presence checking is available as
+ * well.
+ *
+ * <p>Returns a bitmask with {@link #VIDEO_CALLING_ENABLED} to indicate that video calling is
+ * available, and {@link #VIDEO_CALLING_PRESENCE} if presence indication is also available.
+ *
+ * @param context The context
+ * @return A bit-mask describing the current video capabilities.
+ */
+ public static int getVideoCallingAvailability(Context context) {
+ if (!PermissionsUtil.hasPermission(context, android.Manifest.permission.READ_PHONE_STATE)
+ || !CompatUtils.isVideoCompatible()) {
+ return VIDEO_CALLING_DISABLED;
+ }
+ TelecomManager telecommMgr = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+ if (telecommMgr == null) {
+ return VIDEO_CALLING_DISABLED;
+ }
+
+ List<PhoneAccountHandle> accountHandles = telecommMgr.getCallCapablePhoneAccounts();
+ for (PhoneAccountHandle accountHandle : accountHandles) {
+ PhoneAccount account = telecommMgr.getPhoneAccount(accountHandle);
+ if (account != null) {
+ if (account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) {
+ // Builds prior to N do not have presence support.
+ if (!CompatUtils.isVideoPresenceCompatible()) {
+ return VIDEO_CALLING_ENABLED;
+ }
+
+ int videoCapabilities = VIDEO_CALLING_ENABLED;
+ if (account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)) {
+ videoCapabilities |= VIDEO_CALLING_PRESENCE;
+ }
+ return videoCapabilities;
+ }
+ }
+ }
+ return VIDEO_CALLING_DISABLED;
+ }
+
+ /**
+ * Determines if one of the call capable phone accounts defined supports video calling.
+ *
+ * @param context The context.
+ * @return {@code true} if one of the call capable phone accounts supports video calling, {@code
+ * false} otherwise.
+ */
+ public static boolean isVideoEnabled(Context context) {
+ return (getVideoCallingAvailability(context) & VIDEO_CALLING_ENABLED) != 0;
+ }
+
+ /**
+ * Determines if one of the call capable phone accounts defined supports calling with a subject
+ * specified.
+ *
+ * @param context The context.
+ * @return {@code true} if one of the call capable phone accounts supports calling with a subject
+ * specified, {@code false} otherwise.
+ */
+ public static boolean isCallWithSubjectSupported(Context context) {
+ if (!PermissionsUtil.hasPermission(context, android.Manifest.permission.READ_PHONE_STATE)
+ || !CompatUtils.isCallSubjectCompatible()) {
+ return false;
+ }
+ TelecomManager telecommMgr = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+ if (telecommMgr == null) {
+ return false;
+ }
+
+ List<PhoneAccountHandle> accountHandles = telecommMgr.getCallCapablePhoneAccounts();
+ for (PhoneAccountHandle accountHandle : accountHandles) {
+ PhoneAccount account = telecommMgr.getPhoneAccount(accountHandle);
+ if (account != null && account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/java/com/android/dialer/util/DialerUtils.java b/java/com/android/dialer/util/DialerUtils.java
new file mode 100644
index 000000000..63f870e73
--- /dev/null
+++ b/java/com/android/dialer/util/DialerUtils.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2014 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.dialer.util;
+
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Point;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Toast;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.telecom.TelecomUtil;
+import java.io.File;
+import java.util.Iterator;
+import java.util.Random;
+
+/** General purpose utility methods for the Dialer. */
+public class DialerUtils {
+
+ /**
+ * Prefix on a dialed number that indicates that the call should be placed through the Wireless
+ * Priority Service.
+ */
+ private static final String WPS_PREFIX = "*272";
+
+ public static final String FILE_PROVIDER_CACHE_DIR = "my_cache";
+
+ private static final Random RANDOM = new Random();
+
+ /**
+ * Attempts to start an activity and displays a toast with the default error message if the
+ * activity is not found, instead of throwing an exception.
+ *
+ * @param context to start the activity with.
+ * @param intent to start the activity with.
+ */
+ public static void startActivityWithErrorToast(Context context, Intent intent) {
+ startActivityWithErrorToast(context, intent, R.string.activity_not_available);
+ }
+
+ /**
+ * Attempts to start an activity and displays a toast with a provided error message if the
+ * activity is not found, instead of throwing an exception.
+ *
+ * @param context to start the activity with.
+ * @param intent to start the activity with.
+ * @param msgId Resource ID of the string to display in an error message if the activity is not
+ * found.
+ */
+ public static void startActivityWithErrorToast(
+ final Context context, final Intent intent, int msgId) {
+ try {
+ if ((Intent.ACTION_CALL.equals(intent.getAction()))) {
+ // All dialer-initiated calls should pass the touch point to the InCallUI
+ Point touchPoint = TouchPointManager.getInstance().getPoint();
+ if (touchPoint.x != 0 || touchPoint.y != 0) {
+ Bundle extras;
+ // Make sure to not accidentally clobber any existing extras
+ if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
+ extras = intent.getParcelableExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
+ } else {
+ extras = new Bundle();
+ }
+ extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint);
+ intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
+ }
+
+ if (shouldWarnForOutgoingWps(context, intent.getData().getSchemeSpecificPart())) {
+ LogUtil.i(
+ "DialUtils.startActivityWithErrorToast",
+ "showing outgoing WPS dialog before placing call");
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setMessage(R.string.outgoing_wps_warning);
+ builder.setPositiveButton(
+ R.string.dialog_continue,
+ new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ placeCallOrMakeToast(context, intent);
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.create().show();
+ } else {
+ placeCallOrMakeToast(context, intent);
+ }
+ } else {
+ context.startActivity(intent);
+ }
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private static void placeCallOrMakeToast(Context context, Intent intent) {
+ final boolean hasCallPermission = TelecomUtil.placeCall(context, intent);
+ if (!hasCallPermission) {
+ // TODO: Make calling activity show request permission dialog and handle
+ // callback results appropriately.
+ Toast.makeText(context, "Cannot place call without Phone permission", Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
+
+ /**
+ * Returns whether the user should be warned about an outgoing WPS call. This checks if there is a
+ * currently active call over LTE. Regardless of the country or carrier, the radio will drop an
+ * active LTE call if a WPS number is dialed, so this warning is necessary.
+ */
+ private static boolean shouldWarnForOutgoingWps(Context context, String number) {
+ if (number != null && number.startsWith(WPS_PREFIX)) {
+ TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+ boolean isOnVolte =
+ VERSION.SDK_INT >= VERSION_CODES.N
+ && telephonyManager.getVoiceNetworkType() == TelephonyManager.NETWORK_TYPE_LTE;
+ boolean hasCurrentActiveCall =
+ telephonyManager.getCallState() == TelephonyManager.CALL_STATE_OFFHOOK;
+ return isOnVolte && hasCurrentActiveCall;
+ }
+ return false;
+ }
+
+ /**
+ * Closes an {@link AutoCloseable}, silently ignoring any checked exceptions. Does nothing if
+ * null.
+ *
+ * @param closeable to close.
+ */
+ public static void closeQuietly(AutoCloseable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ /**
+ * Joins a list of {@link CharSequence} into a single {@link CharSequence} seperated by ", ".
+ *
+ * @param list List of char sequences to join.
+ * @return Joined char sequences.
+ */
+ public static CharSequence join(Iterable<CharSequence> list) {
+ StringBuilder sb = new StringBuilder();
+ final BidiFormatter formatter = BidiFormatter.getInstance();
+ final CharSequence separator = ", ";
+
+ Iterator<CharSequence> itr = list.iterator();
+ boolean firstTime = true;
+ while (itr.hasNext()) {
+ if (firstTime) {
+ firstTime = false;
+ } else {
+ sb.append(separator);
+ }
+ // Unicode wrap the elements of the list to respect RTL for individual strings.
+ sb.append(
+ formatter.unicodeWrap(itr.next().toString(), TextDirectionHeuristics.FIRSTSTRONG_LTR));
+ }
+
+ // Unicode wrap the joined value, to respect locale's RTL ordering for the whole list.
+ return formatter.unicodeWrap(sb.toString());
+ }
+
+ public static void showInputMethod(View view) {
+ final InputMethodManager imm =
+ (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ imm.showSoftInput(view, 0);
+ }
+ }
+
+ public static void hideInputMethod(View view) {
+ final InputMethodManager imm =
+ (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+ }
+ }
+
+ /**
+ * Create a File in the cache directory that Dialer's FileProvider knows about so they can be
+ * shared to other apps.
+ */
+ public static File createShareableFile(Context context) {
+ long fileId = Math.abs(RANDOM.nextLong());
+ File parentDir = new File(context.getCacheDir(), FILE_PROVIDER_CACHE_DIR);
+ if (!parentDir.exists()) {
+ parentDir.mkdirs();
+ }
+ return new File(parentDir, String.valueOf(fileId));
+ }
+
+ /**
+ * Returns default preference for context accessing device protected storage. This is used when
+ * directBoot is enabled (before device unlocked after boot) since the default shared preference
+ * used normally is not available at this moment for N devices. Returns regular default shared
+ * preference for pre-N devices.
+ */
+ @NonNull
+ public static SharedPreferences getDefaultSharedPreferenceForDeviceProtectedStorageContext(
+ @NonNull Context context) {
+ Assert.isNotNull(context);
+ Context deviceProtectedContext =
+ ContextCompat.isDeviceProtectedStorage(context)
+ ? context
+ : ContextCompat.createDeviceProtectedStorageContext(context);
+ // ContextCompat.createDeviceProtectedStorageContext(context) returns null on pre-N, thus fall
+ // back to regular default shared preference for pre-N devices since devices protected context
+ // is not available.
+ return PreferenceManager.getDefaultSharedPreferences(
+ deviceProtectedContext != null ? deviceProtectedContext : context);
+ }
+}
diff --git a/java/com/android/dialer/util/DrawableConverter.java b/java/com/android/dialer/util/DrawableConverter.java
new file mode 100644
index 000000000..5670315c9
--- /dev/null
+++ b/java/com/android/dialer/util/DrawableConverter.java
@@ -0,0 +1,97 @@
+/*
+ * 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.dialer.util;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
+import com.android.dialer.common.LogUtil;
+
+/** Provides utilities for bitmaps and drawables. */
+public class DrawableConverter {
+
+ private DrawableConverter() {}
+
+ /** Converts the provided drawable to a bitmap using the drawable's intrinsic width and height. */
+ @Nullable
+ public static Bitmap drawableToBitmap(@Nullable Drawable drawable) {
+ return drawableToBitmap(drawable, 0, 0);
+ }
+
+ /**
+ * Converts the provided drawable to a bitmap with the specified width and height.
+ *
+ * <p>If both width and height are 0, the drawable's intrinsic width and height are used (but in
+ * that case {@link #drawableToBitmap(Drawable)} should be used).
+ */
+ @Nullable
+ public static Bitmap drawableToBitmap(@Nullable Drawable drawable, int width, int height) {
+ if (drawable == null) {
+ return null;
+ }
+
+ Bitmap bitmap;
+ if (drawable instanceof BitmapDrawable) {
+ bitmap = ((BitmapDrawable) drawable).getBitmap();
+ } else {
+ if (width > 0 || height > 0) {
+ bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ } else if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
+ // Needed for drawables that are just a colour.
+ bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ } else {
+ bitmap =
+ Bitmap.createBitmap(
+ drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight(),
+ Bitmap.Config.ARGB_8888);
+ }
+
+ LogUtil.i(
+ "DrawableConverter.drawableToBitmap",
+ "created bitmap with width: %d, height: %d",
+ bitmap.getWidth(),
+ bitmap.getHeight());
+
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+ }
+ return bitmap;
+ }
+
+ @Nullable
+ public static Drawable getRoundedDrawable(
+ @NonNull Context context, @Nullable Drawable photo, int width, int height) {
+ Bitmap bitmap = drawableToBitmap(photo);
+ if (bitmap != null) {
+ Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, width, height, false);
+ RoundedBitmapDrawable drawable =
+ RoundedBitmapDrawableFactory.create(context.getResources(), scaledBitmap);
+ drawable.setAntiAlias(true);
+ drawable.setCornerRadius(drawable.getIntrinsicHeight() / 2);
+ return drawable;
+ }
+ return null;
+ }
+}
diff --git a/java/com/android/dialer/util/ExpirableCache.java b/java/com/android/dialer/util/ExpirableCache.java
new file mode 100644
index 000000000..2778a572c
--- /dev/null
+++ b/java/com/android/dialer/util/ExpirableCache.java
@@ -0,0 +1,269 @@
+/*
+ * 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.dialer.util;
+
+import android.util.LruCache;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.annotation.concurrent.Immutable;
+import javax.annotation.concurrent.ThreadSafe;
+
+/**
+ * An LRU cache in which all items can be marked as expired at a given time and it is possible to
+ * query whether a particular cached value is expired or not.
+ *
+ * <p>A typical use case for this is caching of values which are expensive to compute but which are
+ * still useful when out of date.
+ *
+ * <p>Consider a cache for contact information:
+ *
+ * <pre>{@code
+ * private ExpirableCache<String, Contact> mContactCache;
+ * }</pre>
+ *
+ * which stores the contact information for a given phone number.
+ *
+ * <p>When we need to store contact information for a given phone number, we can look up the info in
+ * the cache:
+ *
+ * <pre>{@code
+ * CachedValue<Contact> cachedContact = mContactCache.getCachedValue(phoneNumber);
+ * }</pre>
+ *
+ * We might also want to fetch the contact information again if the item is expired.
+ *
+ * <pre>
+ * if (cachedContact.isExpired()) {
+ * fetchContactForNumber(phoneNumber,
+ * new FetchListener() {
+ * &#64;Override
+ * public void onFetched(Contact contact) {
+ * mContactCache.put(phoneNumber, contact);
+ * }
+ * });
+ * }</pre>
+ *
+ * and insert it back into the cache when the fetch completes.
+ *
+ * <p>At a certain point we want to expire the content of the cache because we know the content may
+ * no longer be up-to-date, for instance, when resuming the activity this is shown into:
+ *
+ * <pre>
+ * &#64;Override
+ * protected onResume() {
+ * // We were paused for some time, the cached value might no longer be up to date.
+ * mContactCache.expireAll();
+ * super.onResume();
+ * }
+ * </pre>
+ *
+ * The values will be still available from the cache, but they will be expired.
+ *
+ * <p>If interested only in the value itself, not whether it is expired or not, one should use the
+ * {@link #getPossiblyExpired(Object)} method. If interested only in non-expired values, one should
+ * use the {@link #get(Object)} method instead.
+ *
+ * <p>This class wraps around an {@link LruCache} instance: it follows the {@link LruCache} behavior
+ * for evicting items when the cache is full. It is possible to supply your own subclass of LruCache
+ * by using the {@link #create(LruCache)} method, which can define a custom expiration policy. Since
+ * the underlying cache maps keys to cached values it can determine which items are expired and
+ * which are not, allowing for an implementation that evicts expired items before non expired ones.
+ *
+ * <p>This class is thread-safe.
+ *
+ * @param <K> the type of the keys
+ * @param <V> the type of the values
+ */
+@ThreadSafe
+public class ExpirableCache<K, V> {
+
+ /**
+ * The current generation of items added to the cache.
+ *
+ * <p>Items in the cache can belong to a previous generation, but in that case they would be
+ * expired.
+ *
+ * @see ExpirableCache.CachedValue#isExpired()
+ */
+ private final AtomicInteger mGeneration;
+ /** The underlying cache used to stored the cached values. */
+ private LruCache<K, CachedValue<V>> mCache;
+
+ private ExpirableCache(LruCache<K, CachedValue<V>> cache) {
+ mCache = cache;
+ mGeneration = new AtomicInteger(0);
+ }
+
+ /**
+ * Creates a new {@link ExpirableCache} that wraps the given {@link LruCache}.
+ *
+ * <p>The created cache takes ownership of the cache passed in as an argument.
+ *
+ * @param <K> the type of the keys
+ * @param <V> the type of the values
+ * @param cache the cache to store the value in
+ * @return the newly created expirable cache
+ * @throws IllegalArgumentException if the cache is not empty
+ */
+ public static <K, V> ExpirableCache<K, V> create(LruCache<K, CachedValue<V>> cache) {
+ return new ExpirableCache<K, V>(cache);
+ }
+
+ /**
+ * Creates a new {@link ExpirableCache} with the given maximum size.
+ *
+ * @param <K> the type of the keys
+ * @param <V> the type of the values
+ * @return the newly created expirable cache
+ */
+ public static <K, V> ExpirableCache<K, V> create(int maxSize) {
+ return create(new LruCache<K, CachedValue<V>>(maxSize));
+ }
+
+ /**
+ * Returns the cached value for the given key, or null if no value exists.
+ *
+ * <p>The cached value gives access both to the value associated with the key and whether it is
+ * expired or not.
+ *
+ * <p>If not interested in whether the value is expired, use {@link #getPossiblyExpired(Object)}
+ * instead.
+ *
+ * <p>If only wants values that are not expired, use {@link #get(Object)} instead.
+ *
+ * @param key the key to look up
+ */
+ public CachedValue<V> getCachedValue(K key) {
+ return mCache.get(key);
+ }
+
+ /**
+ * Returns the value for the given key, or null if no value exists.
+ *
+ * <p>When using this method, it is not possible to determine whether the value is expired or not.
+ * Use {@link #getCachedValue(Object)} to achieve that instead. However, if using {@link
+ * #getCachedValue(Object)} to determine if an item is expired, one should use the item within the
+ * {@link CachedValue} and not call {@link #getPossiblyExpired(Object)} to get the value
+ * afterwards, since that is not guaranteed to return the same value or that the newly returned
+ * value is in the same state.
+ *
+ * @param key the key to look up
+ */
+ public V getPossiblyExpired(K key) {
+ CachedValue<V> cachedValue = getCachedValue(key);
+ return cachedValue == null ? null : cachedValue.getValue();
+ }
+
+ /**
+ * Returns the value for the given key only if it is not expired, or null if no value exists or is
+ * expired.
+ *
+ * <p>This method will return null if either there is no value associated with this key or if the
+ * associated value is expired.
+ *
+ * @param key the key to look up
+ */
+ public V get(K key) {
+ CachedValue<V> cachedValue = getCachedValue(key);
+ return cachedValue == null || cachedValue.isExpired() ? null : cachedValue.getValue();
+ }
+
+ /**
+ * Puts an item in the cache.
+ *
+ * <p>Newly added item will not be expired until {@link #expireAll()} is next called.
+ *
+ * @param key the key to look up
+ * @param value the value to associate with the key
+ */
+ public void put(K key, V value) {
+ mCache.put(key, newCachedValue(value));
+ }
+
+ /**
+ * Mark all items currently in the cache as expired.
+ *
+ * <p>Newly added items after this call will be marked as not expired.
+ *
+ * <p>Expiring the items in the cache does not imply they will be evicted.
+ */
+ public void expireAll() {
+ mGeneration.incrementAndGet();
+ }
+
+ /**
+ * Creates a new {@link CachedValue} instance to be stored in this cache.
+ *
+ * <p>Implementation of {@link LruCache#create(K)} can use this method to create a new entry.
+ */
+ public CachedValue<V> newCachedValue(V value) {
+ return new GenerationalCachedValue<V>(value, mGeneration);
+ }
+
+ /**
+ * A cached value stored inside the cache.
+ *
+ * <p>It provides access to the value stored in the cache but also allows to check whether the
+ * value is expired.
+ *
+ * @param <V> the type of value stored in the cache
+ */
+ public interface CachedValue<V> {
+
+ /** Returns the value stored in the cache for a given key. */
+ V getValue();
+
+ /**
+ * Checks whether the value, while still being present in the cache, is expired.
+ *
+ * @return true if the value is expired
+ */
+ boolean isExpired();
+ }
+
+ /** Cached values storing the generation at which they were added. */
+ @Immutable
+ private static class GenerationalCachedValue<V> implements ExpirableCache.CachedValue<V> {
+
+ /** The value stored in the cache. */
+ public final V mValue;
+ /** The generation at which the value was added to the cache. */
+ private final int mGeneration;
+ /** The atomic integer storing the current generation of the cache it belongs to. */
+ private final AtomicInteger mCacheGeneration;
+
+ /**
+ * @param cacheGeneration the atomic integer storing the generation of the cache in which this
+ * value will be stored
+ */
+ public GenerationalCachedValue(V value, AtomicInteger cacheGeneration) {
+ mValue = value;
+ mCacheGeneration = cacheGeneration;
+ // Snapshot the current generation.
+ mGeneration = mCacheGeneration.get();
+ }
+
+ @Override
+ public V getValue() {
+ return mValue;
+ }
+
+ @Override
+ public boolean isExpired() {
+ return mGeneration != mCacheGeneration.get();
+ }
+ }
+}
diff --git a/java/com/android/dialer/util/IntentUtil.java b/java/com/android/dialer/util/IntentUtil.java
new file mode 100644
index 000000000..2f265b5a7
--- /dev/null
+++ b/java/com/android/dialer/util/IntentUtil.java
@@ -0,0 +1,78 @@
+/*
+ * 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.dialer.util;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.ContactsContract;
+
+/** Utilities for creation of intents in Dialer. */
+public class IntentUtil {
+
+ private static final String SMS_URI_PREFIX = "sms:";
+ private static final int NO_PHONE_TYPE = -1;
+
+ public static Intent getSendSmsIntent(CharSequence phoneNumber) {
+ return new Intent(Intent.ACTION_SENDTO, Uri.parse(SMS_URI_PREFIX + phoneNumber));
+ }
+
+ public static Intent getNewContactIntent() {
+ return new Intent(Intent.ACTION_INSERT, ContactsContract.Contacts.CONTENT_URI);
+ }
+
+ public static Intent getNewContactIntent(CharSequence phoneNumber) {
+ return getNewContactIntent(null /* name */, phoneNumber /* phoneNumber */, NO_PHONE_TYPE);
+ }
+
+ public static Intent getNewContactIntent(
+ CharSequence name, CharSequence phoneNumber, int phoneNumberType) {
+ Intent intent = getNewContactIntent();
+ populateContactIntent(intent, name, phoneNumber, phoneNumberType);
+ return intent;
+ }
+
+ public static Intent getAddToExistingContactIntent() {
+ Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
+ return intent;
+ }
+
+ public static Intent getAddToExistingContactIntent(CharSequence phoneNumber) {
+ return getAddToExistingContactIntent(
+ null /* name */, phoneNumber /* phoneNumber */, NO_PHONE_TYPE);
+ }
+
+ public static Intent getAddToExistingContactIntent(
+ CharSequence name, CharSequence phoneNumber, int phoneNumberType) {
+ Intent intent = getAddToExistingContactIntent();
+ populateContactIntent(intent, name, phoneNumber, phoneNumberType);
+ return intent;
+ }
+
+ private static void populateContactIntent(
+ Intent intent, CharSequence name, CharSequence phoneNumber, int phoneNumberType) {
+ if (phoneNumber != null) {
+ intent.putExtra(ContactsContract.Intents.Insert.PHONE, phoneNumber);
+ }
+ if (name != null) {
+ intent.putExtra(ContactsContract.Intents.Insert.NAME, name);
+ }
+ if (phoneNumberType != NO_PHONE_TYPE) {
+ intent.putExtra(ContactsContract.Intents.Insert.PHONE_TYPE, phoneNumberType);
+ }
+ }
+}
diff --git a/java/com/android/dialer/util/MoreStrings.java b/java/com/android/dialer/util/MoreStrings.java
new file mode 100644
index 000000000..5a43b1d10
--- /dev/null
+++ b/java/com/android/dialer/util/MoreStrings.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2013 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.dialer.util;
+
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+
+/** Static utility methods for Strings. */
+public class MoreStrings {
+
+ /**
+ * Returns the given string if it is non-null; the empty string otherwise.
+ *
+ * @param string the string to test and possibly return
+ * @return {@code string} itself if it is non-null; {@code ""} if it is null
+ */
+ public static String nullToEmpty(@Nullable String string) {
+ return (string == null) ? "" : string;
+ }
+
+ /**
+ * Returns the given string if it is nonempty; {@code null} otherwise.
+ *
+ * @param string the string to test and possibly return
+ * @return {@code string} itself if it is nonempty; {@code null} if it is empty or null
+ */
+ @Nullable
+ public static String emptyToNull(@Nullable String string) {
+ return TextUtils.isEmpty(string) ? null : string;
+ }
+
+ public static String toSafeString(String value) {
+ if (value == null) {
+ return null;
+ }
+
+ // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare
+ // sanitized phone numbers.
+ final StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < value.length(); i++) {
+ final char c = value.charAt(i);
+ if (c == '-' || c == '@' || c == '.') {
+ builder.append(c);
+ } else {
+ builder.append('x');
+ }
+ }
+ return builder.toString();
+ }
+}
diff --git a/java/com/android/dialer/util/OrientationUtil.java b/java/com/android/dialer/util/OrientationUtil.java
new file mode 100644
index 000000000..5a8d1ae0f
--- /dev/null
+++ b/java/com/android/dialer/util/OrientationUtil.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dialer.util;
+
+import android.content.Context;
+import android.content.res.Configuration;
+
+/** Static methods related to device orientation. */
+public class OrientationUtil {
+
+ /** @return if the context is in landscape orientation. */
+ public static boolean isLandscape(Context context) {
+ return context.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
+ }
+}
diff --git a/java/com/android/dialer/util/PermissionsUtil.java b/java/com/android/dialer/util/PermissionsUtil.java
new file mode 100644
index 000000000..70b96dfe1
--- /dev/null
+++ b/java/com/android/dialer/util/PermissionsUtil.java
@@ -0,0 +1,121 @@
+/*
+ * 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.dialer.util;
+
+import android.Manifest.permission;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.content.LocalBroadcastManager;
+import com.android.dialer.common.LogUtil;
+
+/** Utility class to help with runtime permissions. */
+public class PermissionsUtil {
+
+ private static final String PERMISSION_PREFERENCE = "dialer_permissions";
+
+ public static boolean hasPhonePermissions(Context context) {
+ return hasPermission(context, permission.CALL_PHONE);
+ }
+
+ public static boolean hasContactsPermissions(Context context) {
+ return hasPermission(context, permission.READ_CONTACTS);
+ }
+
+ public static boolean hasLocationPermissions(Context context) {
+ return hasPermission(context, permission.ACCESS_FINE_LOCATION);
+ }
+
+ public static boolean hasCameraPermissions(Context context) {
+ return hasPermission(context, permission.CAMERA);
+ }
+
+ public static boolean hasPermission(Context context, String permission) {
+ return ContextCompat.checkSelfPermission(context, permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Checks {@link android.content.SharedPreferences} if a permission has been requested before.
+ *
+ * <p>It is important to note that this method only works if you call {@link
+ * PermissionsUtil#permissionRequested(Context, String)} in {@link
+ * android.app.Activity#onRequestPermissionsResult(int, String[], int[])}.
+ */
+ public static boolean isFirstRequest(Context context, String permission) {
+ return context
+ .getSharedPreferences(PERMISSION_PREFERENCE, Context.MODE_PRIVATE)
+ .getBoolean(permission, true);
+ }
+
+ /**
+ * Records in {@link android.content.SharedPreferences} that the specified permission has been
+ * requested at least once.
+ *
+ * <p>This method should be called in {@link android.app.Activity#onRequestPermissionsResult(int,
+ * String[], int[])}.
+ */
+ public static void permissionRequested(Context context, String permission) {
+ context
+ .getSharedPreferences(PERMISSION_PREFERENCE, Context.MODE_PRIVATE)
+ .edit()
+ .putBoolean(permission, false)
+ .apply();
+ }
+
+ /**
+ * Rudimentary methods wrapping the use of a LocalBroadcastManager to simplify the process of
+ * notifying other classes when a particular fragment is notified that a permission is granted.
+ *
+ * <p>To be notified when a permission has been granted, create a new broadcast receiver and
+ * register it using {@link #registerPermissionReceiver(Context, BroadcastReceiver, String)}
+ *
+ * <p>E.g.
+ *
+ * <p>final BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void
+ * onReceive(Context context, Intent intent) { refreshContactsView(); } }
+ *
+ * <p>PermissionsUtil.registerPermissionReceiver(getActivity(), receiver, READ_CONTACTS);
+ *
+ * <p>If you register to listen for multiple permissions, you can identify which permission was
+ * granted by inspecting {@link Intent#getAction()}.
+ *
+ * <p>In the fragment that requests for the permission, be sure to call {@link
+ * #notifyPermissionGranted(Context, String)} when the permission is granted so that any
+ * interested listeners are notified of the change.
+ */
+ public static void registerPermissionReceiver(
+ Context context, BroadcastReceiver receiver, String permission) {
+ LogUtil.i("PermissionsUtil.registerPermissionReceiver", permission);
+ final IntentFilter filter = new IntentFilter(permission);
+ LocalBroadcastManager.getInstance(context).registerReceiver(receiver, filter);
+ }
+
+ public static void unregisterPermissionReceiver(Context context, BroadcastReceiver receiver) {
+ LogUtil.i("PermissionsUtil.unregisterPermissionReceiver", null);
+ LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver);
+ }
+
+ public static void notifyPermissionGranted(Context context, String permission) {
+ LogUtil.i("PermissionsUtil.notifyPermissionGranted", permission);
+ final Intent intent = new Intent(permission);
+ LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
+ }
+}
diff --git a/java/com/android/dialer/util/SettingsUtil.java b/java/com/android/dialer/util/SettingsUtil.java
new file mode 100644
index 000000000..c61c09b6c
--- /dev/null
+++ b/java/com/android/dialer/util/SettingsUtil.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 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.dialer.util;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.sqlite.SQLiteException;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+public class SettingsUtil {
+
+ private static final String DEFAULT_NOTIFICATION_URI_STRING =
+ Settings.System.DEFAULT_NOTIFICATION_URI.toString();
+
+ /**
+ * Queries for a ringtone name, and sets the name using a handler. This is a method was originally
+ * copied from com.android.settings.SoundSettings.
+ *
+ * @param context The application context.
+ * @param handler The handler, which takes the name of the ringtone as a String as a parameter.
+ * @param type The type of sound.
+ * @param key The key to the shared preferences entry being updated.
+ * @param msg An integer identifying the message sent to the handler.
+ */
+ public static void updateRingtoneName(
+ Context context, Handler handler, int type, String key, int msg) {
+ final Uri ringtoneUri;
+ boolean defaultRingtone = false;
+ if (type == RingtoneManager.TYPE_RINGTONE) {
+ // For ringtones, we can just lookup the system default because changing the settings
+ // in Call Settings changes the system default.
+ ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
+ } else {
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ // For voicemail notifications, we use the value saved in Phone's shared preferences.
+ String uriString = prefs.getString(key, DEFAULT_NOTIFICATION_URI_STRING);
+ if (TextUtils.isEmpty(uriString)) {
+ // silent ringtone
+ ringtoneUri = null;
+ } else {
+ if (uriString.equals(DEFAULT_NOTIFICATION_URI_STRING)) {
+ // If it turns out that the voicemail notification is set to the system
+ // default notification, we retrieve the actual URI to prevent it from showing
+ // up as "Unknown Ringtone".
+ defaultRingtone = true;
+ ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
+ } else {
+ ringtoneUri = Uri.parse(uriString);
+ }
+ }
+ }
+ CharSequence summary = context.getString(R.string.ringtone_unknown);
+ // Is it a silent ringtone?
+ if (ringtoneUri == null) {
+ summary = context.getString(R.string.ringtone_silent);
+ } else {
+ // Fetch the ringtone title from the media provider
+ final Ringtone ringtone = RingtoneManager.getRingtone(context, ringtoneUri);
+ if (ringtone != null) {
+ try {
+ final String title = ringtone.getTitle(context);
+ if (!TextUtils.isEmpty(title)) {
+ summary = title;
+ }
+ } catch (SQLiteException sqle) {
+ // Unknown title for the ringtone
+ }
+ }
+ }
+ if (defaultRingtone) {
+ summary = context.getString(R.string.default_notification_description, summary);
+ }
+ handler.sendMessage(handler.obtainMessage(msg, summary));
+ }
+}
diff --git a/java/com/android/dialer/util/TouchPointManager.java b/java/com/android/dialer/util/TouchPointManager.java
new file mode 100644
index 000000000..74f87c477
--- /dev/null
+++ b/java/com/android/dialer/util/TouchPointManager.java
@@ -0,0 +1,60 @@
+/*
+ * 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.dialer.util;
+
+import android.graphics.Point;
+
+/**
+ * Singleton class to keep track of where the user last touched the screen.
+ *
+ * <p>Used to pass on to the InCallUI for animation.
+ */
+public class TouchPointManager {
+
+ public static final String TOUCH_POINT = "touchPoint";
+
+ private static TouchPointManager sInstance = new TouchPointManager();
+
+ private Point mPoint = new Point();
+
+ /** Private constructor. Instance should only be acquired through getInstance(). */
+ private TouchPointManager() {}
+
+ public static TouchPointManager getInstance() {
+ return sInstance;
+ }
+
+ public Point getPoint() {
+ return mPoint;
+ }
+
+ public void setPoint(int x, int y) {
+ mPoint.set(x, y);
+ }
+
+ /**
+ * When a point is initialized, its value is (0,0). Since it is highly unlikely a user will touch
+ * at that exact point, if the point in TouchPointManager is (0,0), it is safe to assume that the
+ * TouchPointManager has not yet collected a touch.
+ *
+ * @return True if there is a valid point saved. Define a valid point as any point that is not
+ * (0,0).
+ */
+ public boolean hasValidPoint() {
+ return mPoint.x != 0 || mPoint.y != 0;
+ }
+}
diff --git a/java/com/android/dialer/util/TransactionSafeActivity.java b/java/com/android/dialer/util/TransactionSafeActivity.java
new file mode 100644
index 000000000..9b5e92ba8
--- /dev/null
+++ b/java/com/android/dialer/util/TransactionSafeActivity.java
@@ -0,0 +1,64 @@
+/*
+ * 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.dialer.util;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+
+/**
+ * A common superclass that keeps track of whether an {@link Activity} has saved its state yet or
+ * not.
+ */
+public abstract class TransactionSafeActivity extends AppCompatActivity {
+
+ private boolean mIsSafeToCommitTransactions;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mIsSafeToCommitTransactions = true;
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mIsSafeToCommitTransactions = true;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mIsSafeToCommitTransactions = true;
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mIsSafeToCommitTransactions = false;
+ }
+
+ /**
+ * Returns true if it is safe to commit {@link FragmentTransaction}s at this time, based on
+ * whether {@link Activity#onSaveInstanceState} has been called or not.
+ *
+ * <p>Make sure that the current activity calls into {@link super.onSaveInstanceState(Bundle
+ * outState)} (if that method is overridden), so the flag is properly set.
+ */
+ public boolean isSafeToCommitTransactions() {
+ return mIsSafeToCommitTransactions;
+ }
+}
diff --git a/java/com/android/dialer/util/ViewUtil.java b/java/com/android/dialer/util/ViewUtil.java
new file mode 100644
index 000000000..de08e41a7
--- /dev/null
+++ b/java/com/android/dialer/util/ViewUtil.java
@@ -0,0 +1,129 @@
+/*
+ * 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.dialer.util;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.Paint;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnPreDrawListener;
+import android.widget.TextView;
+import java.util.Locale;
+
+/** Provides static functions to work with views */
+public class ViewUtil {
+
+ private ViewUtil() {}
+
+ /** Similar to {@link Runnable} but takes a View parameter to operate on */
+ public interface ViewRunnable {
+ void run(@NonNull View view);
+ }
+
+ /**
+ * Returns the width as specified in the LayoutParams
+ *
+ * @throws IllegalStateException Thrown if the view's width is unknown before a layout pass s
+ */
+ public static int getConstantPreLayoutWidth(View view) {
+ // We haven't been layed out yet, so get the size from the LayoutParams
+ final ViewGroup.LayoutParams p = view.getLayoutParams();
+ if (p.width < 0) {
+ throw new IllegalStateException(
+ "Expecting view's width to be a constant rather " + "than a result of the layout pass");
+ }
+ return p.width;
+ }
+
+ /**
+ * Returns a boolean indicating whether or not the view's layout direction is RTL
+ *
+ * @param view - A valid view
+ * @return True if the view's layout direction is RTL
+ */
+ public static boolean isViewLayoutRtl(View view) {
+ return view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ }
+
+ public static boolean isRtl() {
+ return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL;
+ }
+
+ public static void resizeText(TextView textView, int originalTextSize, int minTextSize) {
+ final Paint paint = textView.getPaint();
+ final int width = textView.getWidth();
+ if (width == 0) {
+ return;
+ }
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, originalTextSize);
+ float ratio = width / paint.measureText(textView.getText().toString());
+ if (ratio <= 1.0f) {
+ textView.setTextSize(
+ TypedValue.COMPLEX_UNIT_PX, Math.max(minTextSize, originalTextSize * ratio));
+ }
+ }
+
+ /** Runs a piece of code just before the next draw, after layout and measurement */
+ public static void doOnPreDraw(
+ @NonNull final View view, final boolean drawNextFrame, final Runnable runnable) {
+ view.getViewTreeObserver()
+ .addOnPreDrawListener(
+ new OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ view.getViewTreeObserver().removeOnPreDrawListener(this);
+ runnable.run();
+ return drawNextFrame;
+ }
+ });
+ }
+
+ public static void doOnPreDraw(
+ @NonNull final View view, final boolean drawNextFrame, final ViewRunnable runnable) {
+ view.getViewTreeObserver()
+ .addOnPreDrawListener(
+ new OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ view.getViewTreeObserver().removeOnPreDrawListener(this);
+ runnable.run(view);
+ return drawNextFrame;
+ }
+ });
+ }
+
+ /**
+ * Returns {@code true} if animations should be disabled.
+ *
+ * <p>Animations should be disabled if {@link
+ * android.provider.Settings.Global#ANIMATOR_DURATION_SCALE} is set to 0 through system settings
+ * or the device is in power save mode.
+ */
+ public static boolean areAnimationsDisabled(Context context) {
+ ContentResolver contentResolver = context.getContentResolver();
+ PowerManager powerManager = context.getSystemService(PowerManager.class);
+ return Settings.Global.getFloat(contentResolver, Global.ANIMATOR_DURATION_SCALE, 1.0f) == 0
+ || powerManager.isPowerSaveMode();
+ }
+}
diff --git a/java/com/android/dialer/util/res/values/strings.xml b/java/com/android/dialer/util/res/values/strings.xml
new file mode 100644
index 000000000..43ea6e31a
--- /dev/null
+++ b/java/com/android/dialer/util/res/values/strings.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- The string used to describe a notification if it is the default one in the system. For
+ example, if the user selects the default notification, it will appear as something like
+ Default sound(Capella) in the notification summary.
+ [CHAR LIMIT=40] -->
+ <string name="default_notification_description">Default sound (<xliff:g id="default_sound_title">%1$s</xliff:g>)</string>
+
+ <!-- Choice in the ringtone picker. If chosen, there will be silence instead of a ringtone played. -->
+ <string name="ringtone_silent">None</string>
+
+ <!-- If there is ever a ringtone set for some setting, but that ringtone can no longer be resolved, this is shown instead. For example, if the ringtone was on a SD card and it had been removed, this would be shown for ringtones on that SD card. -->
+ <string name="ringtone_unknown">Unknown ringtone</string>
+
+ <!-- Message displayed when there is no application available to handle a particular action.
+ [CHAR LIMIT=NONE] -->
+ <string name="activity_not_available">No app for that on this device</string>
+
+ <!-- Text of warning to be shown when the user attempts to make an outgoing Wireless
+ Preferred Service call when there is an VoLTE call in progress -->
+ <string name="outgoing_wps_warning">Placing a WPS call will disconnect your existing call.</string>
+
+ <!-- Text for button which indicates that the user wants to proceed with an action. -->
+ <string name="dialog_continue">Continue</string>
+
+</resources>
diff --git a/java/com/android/dialer/voicemailstatus/AndroidManifest.xml b/java/com/android/dialer/voicemailstatus/AndroidManifest.xml
new file mode 100644
index 000000000..a39894c88
--- /dev/null
+++ b/java/com/android/dialer/voicemailstatus/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.dialer.voicemailstatus">
+</manifest>
diff --git a/java/com/android/dialer/voicemailstatus/VisualVoicemailEnabledChecker.java b/java/com/android/dialer/voicemailstatus/VisualVoicemailEnabledChecker.java
new file mode 100644
index 000000000..142bb63ed
--- /dev/null
+++ b/java/com/android/dialer/voicemailstatus/VisualVoicemailEnabledChecker.java
@@ -0,0 +1,111 @@
+/*
+ * 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.dialer.voicemailstatus;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.preference.PreferenceManager;
+import android.support.annotation.Nullable;
+import com.android.dialer.database.CallLogQueryHandler;
+
+/**
+ * Helper class to check whether visual voicemail is enabled.
+ *
+ * <p>Call isVisualVoicemailEnabled() to retrieve the result.
+ *
+ * <p>The result is cached and saved in a SharedPreferences, stored as a boolean in
+ * PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER. Every time a new instance is created, it will try to
+ * restore the cached result from the SharedPreferences.
+ *
+ * <p>Call asyncUpdate() to make a CallLogQuery to check the actual status. This is a async call so
+ * isVisualVoicemailEnabled() will not be affected immediately.
+ *
+ * <p>If the status has changed as a result of asyncUpdate(),
+ * Callback.onVisualVoicemailEnabledStatusChanged() will be called with the new value.
+ */
+public class VisualVoicemailEnabledChecker implements CallLogQueryHandler.Listener {
+
+ public static final String PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER =
+ "has_active_voicemail_provider";
+ private SharedPreferences mPrefs;
+ private boolean mHasActiveVoicemailProvider;
+ private CallLogQueryHandler mCallLogQueryHandler;
+ private VoicemailStatusHelper mVoicemailStatusHelper;
+ private Context mContext;
+ private Callback mCallback;
+
+ public VisualVoicemailEnabledChecker(Context context, @Nullable Callback callback) {
+ mContext = context;
+ mCallback = callback;
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+ mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
+ mHasActiveVoicemailProvider = mPrefs.getBoolean(PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, false);
+ }
+
+ /**
+ * @return whether visual voicemail is enabled. Result is cached, call asyncUpdate() to update the
+ * result.
+ */
+ public boolean isVisualVoicemailEnabled() {
+ return mHasActiveVoicemailProvider;
+ }
+
+ /**
+ * Perform an async query into the system to check the status of visual voicemail. If the status
+ * has changed, Callback.onVisualVoicemailEnabledStatusChanged() will be called.
+ */
+ public void asyncUpdate() {
+ mCallLogQueryHandler = new CallLogQueryHandler(mContext, mContext.getContentResolver(), this);
+ mCallLogQueryHandler.fetchVoicemailStatus();
+ }
+
+ @Override
+ public void onVoicemailStatusFetched(Cursor statusCursor) {
+ boolean hasActiveVoicemailProvider =
+ mVoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor) > 0;
+ if (hasActiveVoicemailProvider != mHasActiveVoicemailProvider) {
+ mHasActiveVoicemailProvider = hasActiveVoicemailProvider;
+ mPrefs.edit().putBoolean(PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, mHasActiveVoicemailProvider);
+ if (mCallback != null) {
+ mCallback.onVisualVoicemailEnabledStatusChanged(mHasActiveVoicemailProvider);
+ }
+ }
+ }
+
+ @Override
+ public void onVoicemailUnreadCountFetched(Cursor cursor) {
+ // Do nothing
+ }
+
+ @Override
+ public void onMissedCallsUnreadCountFetched(Cursor cursor) {
+ // Do nothing
+ }
+
+ @Override
+ public boolean onCallsFetched(Cursor combinedCursor) {
+ // Do nothing
+ return false;
+ }
+
+ public interface Callback {
+
+ /** Callback to notify enabled status has changed to the @param newValue */
+ void onVisualVoicemailEnabledStatusChanged(boolean newValue);
+ }
+}
diff --git a/java/com/android/dialer/voicemailstatus/VoicemailStatusHelper.java b/java/com/android/dialer/voicemailstatus/VoicemailStatusHelper.java
new file mode 100644
index 000000000..16bfe704d
--- /dev/null
+++ b/java/com/android/dialer/voicemailstatus/VoicemailStatusHelper.java
@@ -0,0 +1,96 @@
+/*
+ * 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.dialer.voicemailstatus;
+
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.VoicemailContract.Status;
+import android.support.annotation.VisibleForTesting;
+import java.util.List;
+
+/**
+ * Interface used by the call log UI to determine what user message, if any, related to voicemail
+ * source status needs to be shown. The messages are returned in the order of importance.
+ *
+ * <p>The implementation of this interface interacts with the voicemail content provider to fetch
+ * statuses of all the registered voicemail sources and determines if any status message needs to be
+ * shown. The user of this interface must observe/listen to provider changes and invoke this class
+ * to check if any message needs to be shown.
+ */
+public interface VoicemailStatusHelper {
+
+ /**
+ * Returns a list of messages, in the order or priority that should be shown to the user. An empty
+ * list is returned if no message needs to be shown.
+ *
+ * @param cursor The cursor pointing to the query on {@link Status#CONTENT_URI}. The projection to
+ * be used is defined by the implementation class of this interface.
+ */
+ @VisibleForTesting
+ List<StatusMessage> getStatusMessages(Cursor cursor);
+
+ /**
+ * Returns the number of active voicemail sources installed.
+ *
+ * <p>The number of sources is counted by querying the voicemail status table.
+ */
+ int getNumberActivityVoicemailSources(Cursor cursor);
+
+ @VisibleForTesting
+ class StatusMessage {
+
+ /** Package of the source on behalf of which this message has to be shown. */
+ public final String sourcePackage;
+ /**
+ * The string resource id of the status message that should be shown in the call log page. Set
+ * to -1, if this message is not to be shown in call log.
+ */
+ public final int callLogMessageId;
+ /**
+ * The string resource id of the status message that should be shown in the call details page.
+ * Set to -1, if this message is not to be shown in call details page.
+ */
+ public final int callDetailsMessageId;
+ /** The string resource id of the action message that should be shown. */
+ public final int actionMessageId;
+ /** URI for the corrective action, where applicable. Null if no action URI is available. */
+ public final Uri actionUri;
+
+ public StatusMessage(
+ String sourcePackage,
+ int callLogMessageId,
+ int callDetailsMessageId,
+ int actionMessageId,
+ Uri actionUri) {
+ this.sourcePackage = sourcePackage;
+ this.callLogMessageId = callLogMessageId;
+ this.callDetailsMessageId = callDetailsMessageId;
+ this.actionMessageId = actionMessageId;
+ this.actionUri = actionUri;
+ }
+
+ /** Whether this message should be shown in the call log page. */
+ public boolean showInCallLog() {
+ return callLogMessageId != -1;
+ }
+
+ /** Whether this message should be shown in the call details page. */
+ public boolean showInCallDetails() {
+ return callDetailsMessageId != -1;
+ }
+ }
+}
diff --git a/java/com/android/dialer/voicemailstatus/VoicemailStatusHelperImpl.java b/java/com/android/dialer/voicemailstatus/VoicemailStatusHelperImpl.java
new file mode 100644
index 000000000..404897fde
--- /dev/null
+++ b/java/com/android/dialer/voicemailstatus/VoicemailStatusHelperImpl.java
@@ -0,0 +1,278 @@
+/*
+ * 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.dialer.voicemailstatus;
+
+import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE_CAN_BE_CONFIGURED;
+import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE_OK;
+import static android.provider.VoicemailContract.Status.DATA_CHANNEL_STATE_NO_CONNECTION;
+import static android.provider.VoicemailContract.Status.DATA_CHANNEL_STATE_OK;
+import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING;
+import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION;
+import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_OK;
+
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.VoicemailContract.Status;
+import com.android.contacts.common.util.UriUtils;
+import com.android.dialer.database.VoicemailStatusQuery;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/** Implementation of {@link VoicemailStatusHelper}. */
+public class VoicemailStatusHelperImpl implements VoicemailStatusHelper {
+
+ @Override
+ public List<StatusMessage> getStatusMessages(Cursor cursor) {
+ List<MessageStatusWithPriority> messages =
+ new ArrayList<VoicemailStatusHelperImpl.MessageStatusWithPriority>();
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ MessageStatusWithPriority message = getMessageForStatusEntry(cursor);
+ if (message != null) {
+ messages.add(message);
+ }
+ }
+ // Finally reorder the messages by their priority.
+ return reorderMessages(messages);
+ }
+
+ @Override
+ public int getNumberActivityVoicemailSources(Cursor cursor) {
+ int count = 0;
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ if (isVoicemailSourceActive(cursor)) {
+ ++count;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Returns whether the source status in the cursor corresponds to an active source. A source is
+ * active if its' configuration state is not NOT_CONFIGURED. For most voicemail sources, only OK
+ * and NOT_CONFIGURED are used. The OMTP visual voicemail client has the same behavior pre-NMR1.
+ * NMR1 visual voicemail will only set it to NOT_CONFIGURED when it is deactivated. As soon as
+ * activation is attempted, it will transition into CONFIGURING then into OK or other error state,
+ * NOT_CONFIGURED is never set through an error.
+ */
+ private boolean isVoicemailSourceActive(Cursor cursor) {
+ return cursor.getString(VoicemailStatusQuery.SOURCE_PACKAGE_INDEX) != null
+ && cursor.getInt(VoicemailStatusQuery.CONFIGURATION_STATE_INDEX)
+ != Status.CONFIGURATION_STATE_NOT_CONFIGURED;
+ }
+
+ private List<StatusMessage> reorderMessages(List<MessageStatusWithPriority> messageWrappers) {
+ Collections.sort(
+ messageWrappers,
+ new Comparator<MessageStatusWithPriority>() {
+ @Override
+ public int compare(MessageStatusWithPriority msg1, MessageStatusWithPriority msg2) {
+ return msg1.mPriority - msg2.mPriority;
+ }
+ });
+ List<StatusMessage> reorderMessages = new ArrayList<VoicemailStatusHelper.StatusMessage>();
+ // Copy the ordered message objects into the final list.
+ for (MessageStatusWithPriority messageWrapper : messageWrappers) {
+ reorderMessages.add(messageWrapper.mMessage);
+ }
+ return reorderMessages;
+ }
+
+ /** Returns the message for the status entry pointed to by the cursor. */
+ private MessageStatusWithPriority getMessageForStatusEntry(Cursor cursor) {
+ final String sourcePackage = cursor.getString(VoicemailStatusQuery.SOURCE_PACKAGE_INDEX);
+ if (sourcePackage == null) {
+ return null;
+ }
+ final OverallState overallState =
+ getOverallState(
+ cursor.getInt(VoicemailStatusQuery.CONFIGURATION_STATE_INDEX),
+ cursor.getInt(VoicemailStatusQuery.DATA_CHANNEL_STATE_INDEX),
+ cursor.getInt(VoicemailStatusQuery.NOTIFICATION_CHANNEL_STATE_INDEX));
+ final Action action = overallState.getAction();
+
+ // No source package or no action, means no message shown.
+ if (action == Action.NONE) {
+ return null;
+ }
+
+ Uri actionUri = null;
+ if (action == Action.CALL_VOICEMAIL) {
+ actionUri =
+ UriUtils.parseUriOrNull(
+ cursor.getString(VoicemailStatusQuery.VOICEMAIL_ACCESS_URI_INDEX));
+ // Even if actionUri is null, it is still be useful to show the notification.
+ } else if (action == Action.CONFIGURE_VOICEMAIL) {
+ actionUri =
+ UriUtils.parseUriOrNull(cursor.getString(VoicemailStatusQuery.SETTINGS_URI_INDEX));
+ // If there is no settings URI, there is no point in showing the notification.
+ if (actionUri == null) {
+ return null;
+ }
+ }
+ return new MessageStatusWithPriority(
+ new StatusMessage(
+ sourcePackage,
+ overallState.getCallLogMessageId(),
+ overallState.getCallDetailsMessageId(),
+ action.getMessageId(),
+ actionUri),
+ overallState.getPriority());
+ }
+
+ private OverallState getOverallState(
+ int configurationState, int dataChannelState, int notificationChannelState) {
+ if (configurationState == CONFIGURATION_STATE_OK) {
+ // Voicemail is configured. Let's see how is the data channel.
+ if (dataChannelState == DATA_CHANNEL_STATE_OK) {
+ // Data channel is fine. What about notification channel?
+ if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_OK) {
+ return OverallState.OK;
+ } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING) {
+ return OverallState.NO_DETAILED_NOTIFICATION;
+ } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_NO_CONNECTION) {
+ return OverallState.NO_NOTIFICATIONS;
+ }
+ } else if (dataChannelState == DATA_CHANNEL_STATE_NO_CONNECTION) {
+ // Data channel is not working. What about notification channel?
+ if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_OK) {
+ return OverallState.NO_DATA;
+ } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING) {
+ return OverallState.MESSAGE_WAITING;
+ } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_NO_CONNECTION) {
+ return OverallState.NO_CONNECTION;
+ }
+ }
+ } else if (configurationState == CONFIGURATION_STATE_CAN_BE_CONFIGURED) {
+ // Voicemail not configured. data/notification channel states are irrelevant.
+ return OverallState.INVITE_FOR_CONFIGURATION;
+ } else if (configurationState == Status.CONFIGURATION_STATE_NOT_CONFIGURED) {
+ // Voicemail not configured. data/notification channel states are irrelevant.
+ return OverallState.NOT_CONFIGURED;
+ }
+ // Will reach here only if the source has set an invalid value.
+ return OverallState.INVALID;
+ }
+
+ /** Possible user actions. */
+ public enum Action {
+ NONE(-1),
+ CALL_VOICEMAIL(R.string.voicemail_status_action_call_server),
+ CONFIGURE_VOICEMAIL(R.string.voicemail_status_action_configure);
+
+ private final int mMessageId;
+
+ Action(int messageId) {
+ mMessageId = messageId;
+ }
+
+ public int getMessageId() {
+ return mMessageId;
+ }
+ }
+
+ /**
+ * Overall state of the source status. Each state is associated with the corresponding display
+ * string and the corrective action. The states are also assigned a relative priority which is
+ * used to order the messages from different sources.
+ */
+ private enum OverallState {
+ // TODO: Add separate string for call details and call log pages for the states that needs
+ // to be shown in both.
+ /** Both notification and data channel are not working. */
+ NO_CONNECTION(
+ 0,
+ Action.CALL_VOICEMAIL,
+ R.string.voicemail_status_voicemail_not_available,
+ R.string.voicemail_status_audio_not_available),
+ /** Notifications working, but data channel is not working. Audio cannot be downloaded. */
+ NO_DATA(
+ 1,
+ Action.CALL_VOICEMAIL,
+ R.string.voicemail_status_voicemail_not_available,
+ R.string.voicemail_status_audio_not_available),
+ /** Messages are known to be waiting but data channel is not working. */
+ MESSAGE_WAITING(
+ 2,
+ Action.CALL_VOICEMAIL,
+ R.string.voicemail_status_messages_waiting,
+ R.string.voicemail_status_audio_not_available),
+ /** Notification channel not working, but data channel is. */
+ NO_NOTIFICATIONS(3, Action.CALL_VOICEMAIL, R.string.voicemail_status_voicemail_not_available),
+ /** Invite user to set up voicemail. */
+ INVITE_FOR_CONFIGURATION(
+ 4, Action.CONFIGURE_VOICEMAIL, R.string.voicemail_status_configure_voicemail),
+ /**
+ * No detailed notifications, but data channel is working. This is normal mode of operation for
+ * certain sources. No action needed.
+ */
+ NO_DETAILED_NOTIFICATION(5, Action.NONE, -1),
+ /** Visual voicemail not yet set up. No local action needed. */
+ NOT_CONFIGURED(6, Action.NONE, -1),
+ /** Everything is OK. */
+ OK(7, Action.NONE, -1),
+ /** If one or more state value set by the source is not valid. */
+ INVALID(8, Action.NONE, -1);
+
+ private final int mPriority;
+ private final Action mAction;
+ private final int mCallLogMessageId;
+ private final int mCallDetailsMessageId;
+
+ OverallState(int priority, Action action, int callLogMessageId) {
+ this(priority, action, callLogMessageId, -1);
+ }
+
+ OverallState(int priority, Action action, int callLogMessageId, int callDetailsMessageId) {
+ mPriority = priority;
+ mAction = action;
+ mCallLogMessageId = callLogMessageId;
+ mCallDetailsMessageId = callDetailsMessageId;
+ }
+
+ public Action getAction() {
+ return mAction;
+ }
+
+ public int getPriority() {
+ return mPriority;
+ }
+
+ public int getCallLogMessageId() {
+ return mCallLogMessageId;
+ }
+
+ public int getCallDetailsMessageId() {
+ return mCallDetailsMessageId;
+ }
+ }
+
+ /** A wrapper on {@link StatusMessage} which additionally stores the priority of the message. */
+ private static class MessageStatusWithPriority {
+
+ private final StatusMessage mMessage;
+ private final int mPriority;
+
+ public MessageStatusWithPriority(StatusMessage message, int priority) {
+ mMessage = message;
+ mPriority = priority;
+ }
+ }
+}
diff --git a/java/com/android/dialer/voicemailstatus/res/values/strings.xml b/java/com/android/dialer/voicemailstatus/res/values/strings.xml
new file mode 100644
index 000000000..495ddf2e2
--- /dev/null
+++ b/java/com/android/dialer/voicemailstatus/res/values/strings.xml
@@ -0,0 +1,41 @@
+<!--
+ ~ 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
+ -->
+
+<resources>
+
+ <!-- Voicemail status message shown at the top of call log to notify the user that no new
+ voicemails are currently available. This can happen when both notification as well as data
+ connection to the voicemail server is lost. [CHAR LIMIT=64] -->
+ <string name="voicemail_status_voicemail_not_available">Voicemail updates not available</string>
+ <!-- Voicemail status message shown at the top of call log to notify the user that there is no
+ data connection to the voicemail server, but there are new voicemails waiting on the server.
+ [CHAR LIMIT=64] -->
+ <string name="voicemail_status_messages_waiting">New voicemail waiting. Can\'t load right now.</string>
+ <!-- Voicemail status message shown at the top of call log to invite the user to configure
+ visual voicemail. [CHAR LIMIT=64] -->
+ <string name="voicemail_status_configure_voicemail">Set up your voicemail</string>
+ <!-- Voicemail status message shown at the top of call details screen to notify the user that
+ the audio of this voicemail is not available. [CHAR LIMIT=64] -->
+ <string name="voicemail_status_audio_not_available">Audio not available</string>
+
+ <!-- User action prompt shown next to a voicemail status message to let the user configure
+ visual voicemail. [CHAR LIMIT=20] -->
+ <string name="voicemail_status_action_configure">Set up</string>
+ <!-- User action prompt shown next to a voicemail status message to let the user call voicemail
+ server directly to listen to the voicemails. [CHAR LIMIT=20] -->
+ <string name="voicemail_status_action_call_server">Call voicemail</string>
+
+</resources>
diff --git a/java/com/android/dialer/widget/AndroidManifest.xml b/java/com/android/dialer/widget/AndroidManifest.xml
new file mode 100644
index 000000000..f104cc146
--- /dev/null
+++ b/java/com/android/dialer/widget/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.dialer.widget">
+</manifest>
diff --git a/java/com/android/dialer/widget/ResizingTextEditText.java b/java/com/android/dialer/widget/ResizingTextEditText.java
new file mode 100644
index 000000000..fb894bd14
--- /dev/null
+++ b/java/com/android/dialer/widget/ResizingTextEditText.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 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.dialer.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.widget.EditText;
+import com.android.dialer.util.ViewUtil;
+
+/** EditText which resizes dynamically with respect to text length. */
+public class ResizingTextEditText extends EditText {
+
+ private final int mOriginalTextSize;
+ private final int mMinTextSize;
+
+ public ResizingTextEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mOriginalTextSize = (int) getTextSize();
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ResizingText);
+ mMinTextSize =
+ (int) a.getDimension(R.styleable.ResizingText_resizing_text_min_size, mOriginalTextSize);
+ a.recycle();
+ }
+
+ @Override
+ protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
+ super.onTextChanged(text, start, lengthBefore, lengthAfter);
+ ViewUtil.resizeText(this, mOriginalTextSize, mMinTextSize);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ ViewUtil.resizeText(this, mOriginalTextSize, mMinTextSize);
+ }
+}
diff --git a/java/com/android/dialer/widget/ResizingTextTextView.java b/java/com/android/dialer/widget/ResizingTextTextView.java
new file mode 100644
index 000000000..9b624414d
--- /dev/null
+++ b/java/com/android/dialer/widget/ResizingTextTextView.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 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.dialer.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.widget.TextView;
+import com.android.dialer.util.ViewUtil;
+
+/** TextView which resizes dynamically with respect to text length. */
+public class ResizingTextTextView extends TextView {
+
+ private final int mOriginalTextSize;
+ private final int mMinTextSize;
+
+ public ResizingTextTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mOriginalTextSize = (int) getTextSize();
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ResizingText);
+ mMinTextSize =
+ (int) a.getDimension(R.styleable.ResizingText_resizing_text_min_size, mOriginalTextSize);
+ a.recycle();
+ }
+
+ @Override
+ protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
+ super.onTextChanged(text, start, lengthBefore, lengthAfter);
+ ViewUtil.resizeText(this, mOriginalTextSize, mMinTextSize);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ ViewUtil.resizeText(this, mOriginalTextSize, mMinTextSize);
+ }
+}
diff --git a/java/com/android/dialer/widget/res/values/attrs.xml b/java/com/android/dialer/widget/res/values/attrs.xml
new file mode 100644
index 000000000..bd5c3a4fb
--- /dev/null
+++ b/java/com/android/dialer/widget/res/values/attrs.xml
@@ -0,0 +1,23 @@
+<!--
+ ~ 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.
+ -->
+
+<resources>
+
+ <declare-styleable name="ResizingText">
+ <attr format="dimension" name="resizing_text_min_size"/>
+ </declare-styleable>
+
+</resources>
diff --git a/java/com/android/incallui/AccelerometerListener.java b/java/com/android/incallui/AccelerometerListener.java
new file mode 100644
index 000000000..01f884354
--- /dev/null
+++ b/java/com/android/incallui/AccelerometerListener.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2009 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.incallui;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * This class is used to listen to the accelerometer to monitor the orientation of the phone. The
+ * client of this class is notified when the orientation changes between horizontal and vertical.
+ */
+public class AccelerometerListener {
+
+ // Device orientation
+ public static final int ORIENTATION_UNKNOWN = 0;
+ public static final int ORIENTATION_VERTICAL = 1;
+ public static final int ORIENTATION_HORIZONTAL = 2;
+ private static final String TAG = "AccelerometerListener";
+ private static final boolean DEBUG = true;
+ private static final boolean VDEBUG = false;
+ private static final int ORIENTATION_CHANGED = 1234;
+ private static final int VERTICAL_DEBOUNCE = 100;
+ private static final int HORIZONTAL_DEBOUNCE = 500;
+ private static final double VERTICAL_ANGLE = 50.0;
+ private SensorManager mSensorManager;
+ private Sensor mSensor;
+ // mOrientation is the orientation value most recently reported to the client.
+ private int mOrientation;
+ // mPendingOrientation is the latest orientation computed based on the sensor value.
+ // This is sent to the client after a rebounce delay, at which point it is copied to
+ // mOrientation.
+ private int mPendingOrientation;
+ private OrientationListener mListener;
+ Handler mHandler =
+ new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case ORIENTATION_CHANGED:
+ synchronized (this) {
+ mOrientation = mPendingOrientation;
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "orientation: "
+ + (mOrientation == ORIENTATION_HORIZONTAL
+ ? "horizontal"
+ : (mOrientation == ORIENTATION_VERTICAL ? "vertical" : "unknown")));
+ }
+ if (mListener != null) {
+ mListener.orientationChanged(mOrientation);
+ }
+ }
+ break;
+ }
+ }
+ };
+ SensorEventListener mSensorListener =
+ new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ onSensorEvent(event.values[0], event.values[1], event.values[2]);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // ignore
+ }
+ };
+
+ public AccelerometerListener(Context context) {
+ mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ }
+
+ public void setListener(OrientationListener listener) {
+ mListener = listener;
+ }
+
+ public void enable(boolean enable) {
+ if (DEBUG) {
+ Log.d(TAG, "enable(" + enable + ")");
+ }
+ synchronized (this) {
+ if (enable) {
+ mOrientation = ORIENTATION_UNKNOWN;
+ mPendingOrientation = ORIENTATION_UNKNOWN;
+ mSensorManager.registerListener(
+ mSensorListener, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
+ } else {
+ mSensorManager.unregisterListener(mSensorListener);
+ mHandler.removeMessages(ORIENTATION_CHANGED);
+ }
+ }
+ }
+
+ private void setOrientation(int orientation) {
+ synchronized (this) {
+ if (mPendingOrientation == orientation) {
+ // Pending orientation has not changed, so do nothing.
+ return;
+ }
+
+ // Cancel any pending messages.
+ // We will either start a new timer or cancel alltogether
+ // if the orientation has not changed.
+ mHandler.removeMessages(ORIENTATION_CHANGED);
+
+ if (mOrientation != orientation) {
+ // Set timer to send an event if the orientation has changed since its
+ // previously reported value.
+ mPendingOrientation = orientation;
+ final Message m = mHandler.obtainMessage(ORIENTATION_CHANGED);
+ // set delay to our debounce timeout
+ int delay = (orientation == ORIENTATION_VERTICAL ? VERTICAL_DEBOUNCE : HORIZONTAL_DEBOUNCE);
+ mHandler.sendMessageDelayed(m, delay);
+ } else {
+ // no message is pending
+ mPendingOrientation = ORIENTATION_UNKNOWN;
+ }
+ }
+ }
+
+ private void onSensorEvent(double x, double y, double z) {
+ if (VDEBUG) {
+ Log.d(TAG, "onSensorEvent(" + x + ", " + y + ", " + z + ")");
+ }
+
+ // If some values are exactly zero, then likely the sensor is not powered up yet.
+ // ignore these events to avoid false horizontal positives.
+ if (x == 0.0 || y == 0.0 || z == 0.0) {
+ return;
+ }
+
+ // magnitude of the acceleration vector projected onto XY plane
+ final double xy = Math.hypot(x, y);
+ // compute the vertical angle
+ double angle = Math.atan2(xy, z);
+ // convert to degrees
+ angle = angle * 180.0 / Math.PI;
+ final int orientation =
+ (angle > VERTICAL_ANGLE ? ORIENTATION_VERTICAL : ORIENTATION_HORIZONTAL);
+ if (VDEBUG) {
+ Log.d(TAG, "angle: " + angle + " orientation: " + orientation);
+ }
+ setOrientation(orientation);
+ }
+
+ public interface OrientationListener {
+
+ void orientationChanged(int orientation);
+ }
+}
diff --git a/java/com/android/incallui/AndroidManifest.xml b/java/com/android/incallui/AndroidManifest.xml
new file mode 100644
index 000000000..276b47a5e
--- /dev/null
+++ b/java/com/android/incallui/AndroidManifest.xml
@@ -0,0 +1,121 @@
+<!--
+ ~ 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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.incallui">
+
+ <uses-sdk
+ android:minSdkVersion="23"
+ android:targetSdkVersion="25"/>
+
+ <uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+ <!-- We use this to disable the status bar buttons of home, back and recent
+ during an incoming call. By doing so this allows us to not show the user
+ is viewing the activity in full screen alert, on a fresh system/factory
+ reset state of the app. -->
+ <uses-permission android:name="android.permission.STATUS_BAR"/>
+ <uses-permission android:name="android.permission.CAMERA"/>
+ <!-- Warning: setting the required boolean to true would prevent installation of Dialer on
+ devices which do not support a camera. -->
+ <uses-feature
+ android:name="android.hardware.camera.any"
+ android:required="false"/>
+
+ <!-- Testing location -->
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+
+ <!-- Set android:taskAffinity="com.android.incallui" for all activities to ensure proper
+ navigation. Otherwise system could bring up DialtactsActivity instead, e.g. when user unmerge a
+ call.
+ Set taskAffinity for application is not working because it will be merged and the result is
+ that all activities here still have same taskAffinity as activities under dialer. -->
+ <application>
+ <meta-data android:name="android.telephony.hide_voicemail_settings_menu"
+ android:value="true"/>
+ <activity
+ android:directBootAware="true"
+ android:excludeFromRecents="true"
+ android:exported="false"
+ android:label="@string/phoneAppLabel"
+ android:taskAffinity="com.android.incallui"
+ android:launchMode="singleInstance"
+ android:name="com.android.incallui.InCallActivity"
+ android:resizeableActivity="true"
+ android:screenOrientation="nosensor"
+ android:theme="@style/Theme.InCallScreen">
+ </activity>
+
+ <activity
+ android:directBootAware="true"
+ android:excludeFromRecents="true"
+ android:noHistory="true"
+ android:exported="false"
+ android:label="@string/manageConferenceLabel"
+ android:taskAffinity="com.android.incallui"
+ android:launchMode="singleTask"
+ android:name="com.android.incallui.ManageConferenceActivity"
+ android:resizeableActivity="true"
+ android:theme="@style/Theme.InCallScreen.ManageConference"/>
+
+ <service
+ android:directBootAware="true"
+ android:exported="true"
+ android:name="com.android.incallui.InCallServiceImpl"
+ android:permission="android.permission.BIND_INCALL_SERVICE">
+ <meta-data
+ android:name="android.telecom.IN_CALL_SERVICE_UI"
+ android:value="true"/>
+ <meta-data
+ android:name="android.telecom.IN_CALL_SERVICE_RINGING"
+ android:value="false"/>
+ <meta-data
+ android:name="android.telecom.INCLUDE_EXTERNAL_CALLS"
+ android:value="true"/>
+
+ <intent-filter>
+ <action android:name="android.telecom.InCallService"/>
+ </intent-filter>
+ </service>
+
+ <!--
+ Comments for attributes in SpamNotificationActivity:
+ taskAffinity="" -> Open the dialog without opening the dialer app behind it
+ noHistory="true" -> Navigating away finishes activity
+ excludeFromRecents="true" -> Don't show in "recent apps" screen
+ -->
+ <activity
+ android:excludeFromRecents="true"
+ android:exported="false"
+ android:name="com.android.incallui.spam.SpamNotificationActivity"
+ android:noHistory="true"
+ android:taskAffinity=""
+ android:theme="@style/AfterCallNotificationTheme">
+ </activity>
+
+ <service
+ android:exported="false"
+ android:name="com.android.incallui.spam.SpamNotificationService"/>
+
+ <!-- BroadcastReceiver for receiving Intents from Notification mechanism. -->
+ <receiver
+ android:directBootAware="true"
+ android:exported="false"
+ android:name="com.android.incallui.NotificationBroadcastReceiver"/>
+
+ </application>
+
+</manifest>
+
diff --git a/java/com/android/incallui/AnswerScreenPresenter.java b/java/com/android/incallui/AnswerScreenPresenter.java
new file mode 100644
index 000000000..a21876b2b
--- /dev/null
+++ b/java/com/android/incallui/AnswerScreenPresenter.java
@@ -0,0 +1,110 @@
+/*
+ * 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.incallui;
+
+import android.content.Context;
+import android.support.annotation.FloatRange;
+import android.support.annotation.NonNull;
+import android.support.v4.os.UserManagerCompat;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.incallui.answer.protocol.AnswerScreen;
+import com.android.incallui.answer.protocol.AnswerScreenDelegate;
+import com.android.incallui.answerproximitysensor.AnswerProximitySensor;
+import com.android.incallui.answerproximitysensor.PseudoScreenState;
+import com.android.incallui.call.DialerCall;
+
+/** Manages changes for an incoming call screen. */
+public class AnswerScreenPresenter
+ implements AnswerScreenDelegate, DialerCall.CannedTextResponsesLoadedListener {
+ @NonNull private final Context context;
+ @NonNull private final AnswerScreen answerScreen;
+ @NonNull private final DialerCall call;
+
+ public AnswerScreenPresenter(
+ @NonNull Context context, @NonNull AnswerScreen answerScreen, @NonNull DialerCall call) {
+ LogUtil.i("AnswerScreenPresenter.constructor", null);
+ this.context = Assert.isNotNull(context);
+ this.answerScreen = Assert.isNotNull(answerScreen);
+ this.call = Assert.isNotNull(call);
+ if (isSmsResponseAllowed(call)) {
+ answerScreen.setTextResponses(call.getCannedSmsResponses());
+ }
+ call.addCannedTextResponsesLoadedListener(this);
+
+ PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
+ if (AnswerProximitySensor.shouldUse(context, call)) {
+ new AnswerProximitySensor(context, call, pseudoScreenState);
+ } else {
+ pseudoScreenState.setOn(true);
+ }
+ }
+
+ @Override
+ public void onAnswerScreenUnready() {
+ call.removeCannedTextResponsesLoadedListener(this);
+ }
+
+ @Override
+ public void onDismissDialog() {
+ InCallPresenter.getInstance().onDismissDialog();
+ }
+
+ @Override
+ public void onRejectCallWithMessage(String message) {
+ call.reject(true /* rejectWithMessage */, message);
+ onDismissDialog();
+ }
+
+ @Override
+ public void onAnswer(int videoState) {
+ if (answerScreen.isVideoUpgradeRequest()) {
+ call.acceptUpgradeRequest(videoState);
+ } else {
+ call.answer(videoState);
+ }
+ }
+
+ @Override
+ public void onReject() {
+ if (answerScreen.isVideoUpgradeRequest()) {
+ call.declineUpgradeRequest();
+ } else {
+ call.reject(false /* rejectWithMessage */, null);
+ }
+ }
+
+ @Override
+ public void onCannedTextResponsesLoaded(DialerCall call) {
+ if (isSmsResponseAllowed(call)) {
+ answerScreen.setTextResponses(call.getCannedSmsResponses());
+ }
+ }
+
+ @Override
+ public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) {
+ InCallActivity activity = (InCallActivity) answerScreen.getAnswerScreenFragment().getActivity();
+ if (activity != null) {
+ activity.updateWindowBackgroundColor(progress);
+ }
+ }
+
+ private boolean isSmsResponseAllowed(DialerCall call) {
+ return UserManagerCompat.isUserUnlocked(context)
+ && call.can(android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT);
+ }
+}
diff --git a/java/com/android/incallui/AnswerScreenPresenterStub.java b/java/com/android/incallui/AnswerScreenPresenterStub.java
new file mode 100644
index 000000000..fc47bf5b0
--- /dev/null
+++ b/java/com/android/incallui/AnswerScreenPresenterStub.java
@@ -0,0 +1,44 @@
+/*
+ * 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.incallui;
+
+import android.support.annotation.FloatRange;
+import com.android.incallui.answer.protocol.AnswerScreenDelegate;
+
+/**
+ * Stub implementation of the answer screen delegate. Used to keep the answer fragment visible when
+ * no call exists.
+ */
+public class AnswerScreenPresenterStub implements AnswerScreenDelegate {
+ @Override
+ public void onAnswerScreenUnready() {}
+
+ @Override
+ public void onDismissDialog() {}
+
+ @Override
+ public void onRejectCallWithMessage(String message) {}
+
+ @Override
+ public void onAnswer(int videoState) {}
+
+ @Override
+ public void onReject() {}
+
+ @Override
+ public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) {}
+}
diff --git a/java/com/android/incallui/AudioModeProvider.java b/java/com/android/incallui/AudioModeProvider.java
new file mode 100644
index 000000000..698db0ab9
--- /dev/null
+++ b/java/com/android/incallui/AudioModeProvider.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 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.incallui;
+
+import android.telecom.CallAudioState;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Proxy class for getting and setting the audio mode. */
+public class AudioModeProvider {
+ private static final int SUPPORTED_AUDIO_ROUTE_ALL =
+ CallAudioState.ROUTE_EARPIECE
+ | CallAudioState.ROUTE_BLUETOOTH
+ | CallAudioState.ROUTE_WIRED_HEADSET
+ | CallAudioState.ROUTE_SPEAKER;
+
+ private static final AudioModeProvider instance = new AudioModeProvider();
+ private final List<AudioModeListener> listeners = new ArrayList<>();
+ private CallAudioState audioState =
+ new CallAudioState(false, CallAudioState.ROUTE_EARPIECE, SUPPORTED_AUDIO_ROUTE_ALL);
+
+ public static AudioModeProvider getInstance() {
+ return instance;
+ }
+
+ public void onAudioStateChanged(CallAudioState audioState) {
+ if (!this.audioState.equals(audioState)) {
+ this.audioState = audioState;
+ for (AudioModeListener listener : listeners) {
+ listener.onAudioStateChanged(audioState);
+ }
+ }
+ }
+
+ public void addListener(AudioModeListener listener) {
+ if (!listeners.contains(listener)) {
+ listeners.add(listener);
+ listener.onAudioStateChanged(audioState);
+ }
+ }
+
+ public void removeListener(AudioModeListener listener) {
+ listeners.remove(listener);
+ }
+
+ public CallAudioState getAudioState() {
+ return audioState;
+ }
+
+ /** Notified on changes to audio mode. */
+ public interface AudioModeListener {
+
+ void onAudioStateChanged(CallAudioState audioState);
+ }
+}
diff --git a/java/com/android/incallui/Bindings.java b/java/com/android/incallui/Bindings.java
new file mode 100644
index 000000000..4f142ff96
--- /dev/null
+++ b/java/com/android/incallui/Bindings.java
@@ -0,0 +1,52 @@
+/*
+ * 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.incallui;
+
+import android.content.Context;
+import com.android.incallui.bindings.InCallUiBindings;
+import com.android.incallui.bindings.InCallUiBindingsFactory;
+import com.android.incallui.bindings.InCallUiBindingsStub;
+import java.util.Objects;
+
+/** Accessor for the in call UI bindings. */
+public class Bindings {
+
+ private static InCallUiBindings instance;
+
+ private Bindings() {}
+
+ public static InCallUiBindings get(Context context) {
+ Objects.requireNonNull(context);
+ if (instance != null) {
+ return instance;
+ }
+
+ Context application = context.getApplicationContext();
+ if (application instanceof InCallUiBindingsFactory) {
+ instance = ((InCallUiBindingsFactory) application).newInCallUiBindings();
+ }
+
+ if (instance == null) {
+ instance = new InCallUiBindingsStub();
+ }
+ return instance;
+ }
+
+ public static void setForTesting(InCallUiBindings testInstance) {
+ instance = testInstance;
+ }
+}
diff --git a/java/com/android/incallui/CallButtonPresenter.java b/java/com/android/incallui/CallButtonPresenter.java
new file mode 100644
index 000000000..d6f4cddc9
--- /dev/null
+++ b/java/com/android/incallui/CallButtonPresenter.java
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2013 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.incallui;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.os.UserManagerCompat;
+import android.telecom.CallAudioState;
+import android.telecom.InCallService.VideoCall;
+import android.telecom.VideoProfile;
+import com.android.contacts.common.compat.CallCompat;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.compat.SdkVersionOverride;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.incallui.AudioModeProvider.AudioModeListener;
+import com.android.incallui.InCallCameraManager.Listener;
+import com.android.incallui.InCallPresenter.CanAddCallListener;
+import com.android.incallui.InCallPresenter.InCallDetailsListener;
+import com.android.incallui.InCallPresenter.InCallState;
+import com.android.incallui.InCallPresenter.InCallStateListener;
+import com.android.incallui.InCallPresenter.IncomingCallListener;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.TelecomAdapter;
+import com.android.incallui.call.VideoUtils;
+import com.android.incallui.incall.protocol.InCallButtonIds;
+import com.android.incallui.incall.protocol.InCallButtonUi;
+import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
+
+/** Logic for call buttons. */
+public class CallButtonPresenter
+ implements InCallStateListener,
+ AudioModeListener,
+ IncomingCallListener,
+ InCallDetailsListener,
+ CanAddCallListener,
+ Listener,
+ InCallButtonUiDelegate {
+
+ private static final String KEY_AUTOMATICALLY_MUTED = "incall_key_automatically_muted";
+ private static final String KEY_PREVIOUS_MUTE_STATE = "incall_key_previous_mute_state";
+
+ private final Context mContext;
+ private InCallButtonUi mInCallButtonUi;
+ private DialerCall mCall;
+ private boolean mAutomaticallyMuted = false;
+ private boolean mPreviousMuteState = false;
+ private boolean isInCallButtonUiReady;
+
+ public CallButtonPresenter(Context context) {
+ mContext = context.getApplicationContext();
+ }
+
+ @Override
+ public void onInCallButtonUiReady(InCallButtonUi ui) {
+ Assert.checkState(!isInCallButtonUiReady);
+ mInCallButtonUi = ui;
+ AudioModeProvider.getInstance().addListener(this);
+
+ // register for call state changes last
+ final InCallPresenter inCallPresenter = InCallPresenter.getInstance();
+ inCallPresenter.addListener(this);
+ inCallPresenter.addIncomingCallListener(this);
+ inCallPresenter.addDetailsListener(this);
+ inCallPresenter.addCanAddCallListener(this);
+ inCallPresenter.getInCallCameraManager().addCameraSelectionListener(this);
+
+ // Update the buttons state immediately for the current call
+ onStateChange(InCallState.NO_CALLS, inCallPresenter.getInCallState(), CallList.getInstance());
+ isInCallButtonUiReady = true;
+ }
+
+ @Override
+ public void onInCallButtonUiUnready() {
+ Assert.checkState(isInCallButtonUiReady);
+ mInCallButtonUi = null;
+ InCallPresenter.getInstance().removeListener(this);
+ AudioModeProvider.getInstance().removeListener(this);
+ InCallPresenter.getInstance().removeIncomingCallListener(this);
+ InCallPresenter.getInstance().removeDetailsListener(this);
+ InCallPresenter.getInstance().getInCallCameraManager().removeCameraSelectionListener(this);
+ InCallPresenter.getInstance().removeCanAddCallListener(this);
+ isInCallButtonUiReady = false;
+ }
+
+ @Override
+ public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
+ if (newState == InCallState.OUTGOING) {
+ mCall = callList.getOutgoingCall();
+ } else if (newState == InCallState.INCALL) {
+ mCall = callList.getActiveOrBackgroundCall();
+
+ // When connected to voice mail, automatically shows the dialpad.
+ // (On previous releases we showed it when in-call shows up, before waiting for
+ // OUTGOING. We may want to do that once we start showing "Voice mail" label on
+ // the dialpad too.)
+ if (oldState == InCallState.OUTGOING && mCall != null) {
+ if (CallerInfoUtils.isVoiceMailNumber(mContext, mCall) && getActivity() != null) {
+ getActivity().showDialpadFragment(true /* show */, true /* animate */);
+ }
+ }
+ } else if (newState == InCallState.INCOMING) {
+ if (getActivity() != null) {
+ getActivity().showDialpadFragment(false /* show */, true /* animate */);
+ }
+ mCall = callList.getIncomingCall();
+ } else {
+ mCall = null;
+ }
+ updateUi(newState, mCall);
+ }
+
+ /**
+ * Updates the user interface in response to a change in the details of a call. Currently handles
+ * changes to the call buttons in response to a change in the details for a call. This is
+ * important to ensure changes to the active call are reflected in the available buttons.
+ *
+ * @param call The active call.
+ * @param details The call details.
+ */
+ @Override
+ public void onDetailsChanged(DialerCall call, android.telecom.Call.Details details) {
+ // Only update if the changes are for the currently active call
+ if (mInCallButtonUi != null && call != null && call.equals(mCall)) {
+ updateButtonsState(call);
+ }
+ }
+
+ @Override
+ public void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call) {
+ onStateChange(oldState, newState, CallList.getInstance());
+ }
+
+ @Override
+ public void onCanAddCallChanged(boolean canAddCall) {
+ if (mInCallButtonUi != null && mCall != null) {
+ updateButtonsState(mCall);
+ }
+ }
+
+ @Override
+ public void onAudioStateChanged(CallAudioState audioState) {
+ if (mInCallButtonUi != null) {
+ mInCallButtonUi.setAudioState(audioState);
+ }
+ }
+
+ @Override
+ public CallAudioState getCurrentAudioState() {
+ return AudioModeProvider.getInstance().getAudioState();
+ }
+
+ @Override
+ public void setAudioRoute(int route) {
+ LogUtil.i(
+ "CallButtonPresenter.setAudioRoute",
+ "sending new audio route: " + CallAudioState.audioRouteToString(route));
+ TelecomAdapter.getInstance().setAudioRoute(route);
+ }
+
+ /** Function assumes that bluetooth is not supported. */
+ @Override
+ public void toggleSpeakerphone() {
+ // This function should not be called if bluetooth is available.
+ CallAudioState audioState = getCurrentAudioState();
+ if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
+ // It's clear the UI is wrong, so update the supported mode once again.
+ LogUtil.e(
+ "CallButtonPresenter", "toggling speakerphone not allowed when bluetooth supported.");
+ mInCallButtonUi.setAudioState(audioState);
+ return;
+ }
+
+ int newRoute;
+ if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
+ newRoute = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
+ Logger.get(mContext)
+ .logCallImpression(
+ DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_WIRED_OR_EARPIECE,
+ mCall.getUniqueCallId(),
+ mCall.getTimeAddedMs());
+ } else {
+ newRoute = CallAudioState.ROUTE_SPEAKER;
+ Logger.get(mContext)
+ .logCallImpression(
+ DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_SPEAKERPHONE,
+ mCall.getUniqueCallId(),
+ mCall.getTimeAddedMs());
+ }
+
+ setAudioRoute(newRoute);
+ }
+
+ @Override
+ public void muteClicked(boolean checked) {
+ LogUtil.v("CallButtonPresenter", "turning on mute: " + checked);
+ TelecomAdapter.getInstance().mute(checked);
+ }
+
+ @Override
+ public void holdClicked(boolean checked) {
+ if (mCall == null) {
+ return;
+ }
+ if (checked) {
+ LogUtil.i("CallButtonPresenter", "putting the call on hold: " + mCall);
+ mCall.hold();
+ } else {
+ LogUtil.i("CallButtonPresenter", "removing the call from hold: " + mCall);
+ mCall.unhold();
+ }
+ }
+
+ @Override
+ public void swapClicked() {
+ if (mCall == null) {
+ return;
+ }
+
+ LogUtil.i("CallButtonPresenter", "swapping the call: " + mCall);
+ TelecomAdapter.getInstance().swap(mCall.getId());
+ }
+
+ @Override
+ public void mergeClicked() {
+ TelecomAdapter.getInstance().merge(mCall.getId());
+ }
+
+ @Override
+ public void addCallClicked() {
+ // Automatically mute the current call
+ mAutomaticallyMuted = true;
+ mPreviousMuteState = AudioModeProvider.getInstance().getAudioState().isMuted();
+ // Simulate a click on the mute button
+ muteClicked(true);
+ TelecomAdapter.getInstance().addCall();
+ }
+
+ @Override
+ public void showDialpadClicked(boolean checked) {
+ LogUtil.v("CallButtonPresenter", "show dialpad " + String.valueOf(checked));
+ getActivity().showDialpadFragment(checked /* show */, true /* animate */);
+ }
+
+ @Override
+ public void changeToVideoClicked() {
+ VideoCall videoCall = mCall.getVideoCall();
+ if (videoCall == null) {
+ return;
+ }
+ int currVideoState = mCall.getVideoState();
+ int currUnpausedVideoState = VideoUtils.getUnPausedVideoState(currVideoState);
+ currUnpausedVideoState |= VideoProfile.STATE_BIDIRECTIONAL;
+
+ VideoProfile videoProfile = new VideoProfile(currUnpausedVideoState);
+ videoCall.sendSessionModifyRequest(videoProfile);
+ mCall.setSessionModificationState(
+ DialerCall.SESSION_MODIFICATION_STATE_WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE);
+ }
+
+ @Override
+ public void onEndCallClicked() {
+ LogUtil.i("CallButtonPresenter.onEndCallClicked", "call: " + mCall);
+ if (mCall != null) {
+ mCall.disconnect();
+ }
+ }
+
+ @Override
+ public void showAudioRouteSelector() {
+ mInCallButtonUi.showAudioRouteSelector();
+ }
+
+ /**
+ * Switches the camera between the front-facing and back-facing camera.
+ *
+ * @param useFrontFacingCamera True if we should switch to using the front-facing camera, or false
+ * if we should switch to using the back-facing camera.
+ */
+ @Override
+ public void switchCameraClicked(boolean useFrontFacingCamera) {
+ InCallCameraManager cameraManager = InCallPresenter.getInstance().getInCallCameraManager();
+ cameraManager.setUseFrontFacingCamera(useFrontFacingCamera);
+
+ VideoCall videoCall = mCall.getVideoCall();
+ if (videoCall == null) {
+ return;
+ }
+
+ String cameraId = cameraManager.getActiveCameraId();
+ if (cameraId != null) {
+ final int cameraDir =
+ cameraManager.isUsingFrontFacingCamera()
+ ? DialerCall.VideoSettings.CAMERA_DIRECTION_FRONT_FACING
+ : DialerCall.VideoSettings.CAMERA_DIRECTION_BACK_FACING;
+ mCall.getVideoSettings().setCameraDir(cameraDir);
+ videoCall.setCamera(cameraId);
+ videoCall.requestCameraCapabilities();
+ }
+ }
+
+ @Override
+ public void toggleCameraClicked() {
+ LogUtil.i("CallButtonPresenter.toggleCameraClicked", "");
+ switchCameraClicked(
+ !InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
+ }
+
+ /**
+ * Stop or start client's video transmission.
+ *
+ * @param pause True if pausing the local user's video, or false if starting the local user's
+ * video.
+ */
+ @Override
+ public void pauseVideoClicked(boolean pause) {
+ LogUtil.i("CallButtonPresenter.pauseVideoClicked", "%s", pause ? "pause" : "unpause");
+ VideoCall videoCall = mCall.getVideoCall();
+ if (videoCall == null) {
+ return;
+ }
+
+ int currUnpausedVideoState = VideoUtils.getUnPausedVideoState(mCall.getVideoState());
+ if (pause) {
+ videoCall.setCamera(null);
+ VideoProfile videoProfile =
+ new VideoProfile(currUnpausedVideoState & ~VideoProfile.STATE_TX_ENABLED);
+ videoCall.sendSessionModifyRequest(videoProfile);
+ } else {
+ InCallCameraManager cameraManager = InCallPresenter.getInstance().getInCallCameraManager();
+ videoCall.setCamera(cameraManager.getActiveCameraId());
+ VideoProfile videoProfile =
+ new VideoProfile(currUnpausedVideoState | VideoProfile.STATE_TX_ENABLED);
+ videoCall.sendSessionModifyRequest(videoProfile);
+ mCall.setSessionModificationState(DialerCall.SESSION_MODIFICATION_STATE_WAITING_FOR_RESPONSE);
+ }
+
+ mInCallButtonUi.setVideoPaused(pause);
+ mInCallButtonUi.enableButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, false);
+ }
+
+ private void updateUi(InCallState state, DialerCall call) {
+ LogUtil.v("CallButtonPresenter", "updating call UI for call: ", call);
+
+ if (mInCallButtonUi == null) {
+ return;
+ }
+
+ if (call != null) {
+ mInCallButtonUi.updateInCallButtonUiColors();
+ }
+
+ final boolean isEnabled =
+ state.isConnectingOrConnected() && !state.isIncoming() && call != null;
+ mInCallButtonUi.setEnabled(isEnabled);
+
+ if (call == null) {
+ return;
+ }
+
+ updateButtonsState(call);
+ }
+
+ /**
+ * Updates the buttons applicable for the UI.
+ *
+ * @param call The active call.
+ */
+ private void updateButtonsState(DialerCall call) {
+ LogUtil.v("CallButtonPresenter.updateButtonsState", "");
+ final boolean isVideo = VideoUtils.isVideoCall(call);
+
+ // Common functionality (audio, hold, etc).
+ // Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available:
+ // (1) If the device normally can hold, show HOLD in a disabled state.
+ // (2) If the device doesn't have the concept of hold/swap, remove the button.
+ final boolean showSwap = call.can(android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
+ final boolean showHold =
+ !showSwap
+ && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD)
+ && call.can(android.telecom.Call.Details.CAPABILITY_HOLD);
+ final boolean isCallOnHold = call.getState() == DialerCall.State.ONHOLD;
+
+ final boolean showAddCall =
+ TelecomAdapter.getInstance().canAddCall() && UserManagerCompat.isUserUnlocked(mContext);
+ final boolean showMerge = call.can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
+ final boolean showUpgradeToVideo = !isVideo && hasVideoCallCapabilities(call);
+ final boolean showDowngradeToAudio = isVideo && isDowngradeToAudioSupported(call);
+ final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE);
+
+ final boolean hasCameraPermission =
+ isVideo && VideoUtils.hasCameraPermissionAndAllowedByUser(mContext);
+ // Disabling local video doesn't seem to work when dialing. See b/30256571.
+ final boolean showPauseVideo =
+ isVideo
+ && call.getState() != DialerCall.State.DIALING
+ && call.getState() != DialerCall.State.CONNECTING;
+
+ mInCallButtonUi.showButton(InCallButtonIds.BUTTON_AUDIO, true);
+ mInCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP, showSwap);
+ mInCallButtonUi.showButton(InCallButtonIds.BUTTON_HOLD, showHold);
+ mInCallButtonUi.setHold(isCallOnHold);
+ mInCallButtonUi.showButton(InCallButtonIds.BUTTON_MUTE, showMute);
+ mInCallButtonUi.showButton(InCallButtonIds.BUTTON_ADD_CALL, true);
+ mInCallButtonUi.enableButton(InCallButtonIds.BUTTON_ADD_CALL, showAddCall);
+ mInCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo);
+ mInCallButtonUi.showButton(InCallButtonIds.BUTTON_DOWNGRADE_TO_AUDIO, showDowngradeToAudio);
+ mInCallButtonUi.showButton(
+ InCallButtonIds.BUTTON_SWITCH_CAMERA, isVideo && hasCameraPermission);
+ mInCallButtonUi.showButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, showPauseVideo);
+ if (isVideo) {
+ mInCallButtonUi.setVideoPaused(
+ !VideoUtils.isTransmissionEnabled(call) || !hasCameraPermission);
+ }
+ mInCallButtonUi.showButton(InCallButtonIds.BUTTON_DIALPAD, true);
+ mInCallButtonUi.showButton(InCallButtonIds.BUTTON_MERGE, showMerge);
+
+ mInCallButtonUi.updateButtonStates();
+ }
+
+ private boolean hasVideoCallCapabilities(DialerCall call) {
+ if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) {
+ return call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX)
+ && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX);
+ }
+ // In L, this single flag represents both video transmitting and receiving capabilities
+ return call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX);
+ }
+
+ /**
+ * Determines if downgrading from a video call to an audio-only call is supported. In order to
+ * support downgrade to audio, the SDK version must be >= N and the call should NOT have the
+ * {@link android.telecom.Call.Details#CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO}.
+ *
+ * @param call The call.
+ * @return {@code true} if downgrading to an audio-only call from a video call is supported.
+ */
+ private boolean isDowngradeToAudioSupported(DialerCall call) {
+ return !call.can(CallCompat.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO);
+ }
+
+ @Override
+ public void refreshMuteState() {
+ // Restore the previous mute state
+ if (mAutomaticallyMuted
+ && AudioModeProvider.getInstance().getAudioState().isMuted() != mPreviousMuteState) {
+ if (mInCallButtonUi == null) {
+ return;
+ }
+ muteClicked(mPreviousMuteState);
+ }
+ mAutomaticallyMuted = false;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putBoolean(KEY_AUTOMATICALLY_MUTED, mAutomaticallyMuted);
+ outState.putBoolean(KEY_PREVIOUS_MUTE_STATE, mPreviousMuteState);
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ mAutomaticallyMuted =
+ savedInstanceState.getBoolean(KEY_AUTOMATICALLY_MUTED, mAutomaticallyMuted);
+ mPreviousMuteState = savedInstanceState.getBoolean(KEY_PREVIOUS_MUTE_STATE, mPreviousMuteState);
+ }
+
+ @Override
+ public void onCameraPermissionGranted() {
+ if (mCall != null) {
+ updateButtonsState(mCall);
+ }
+ }
+
+ @Override
+ public void onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera) {
+ if (mInCallButtonUi == null) {
+ return;
+ }
+ mInCallButtonUi.setCameraSwitched(!isUsingFrontFacingCamera);
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ private InCallActivity getActivity() {
+ if (mInCallButtonUi != null) {
+ Fragment fragment = mInCallButtonUi.getInCallButtonUiFragment();
+ if (fragment != null) {
+ return (InCallActivity) fragment.getActivity();
+ }
+ }
+ return null;
+ }
+}
diff --git a/java/com/android/incallui/CallCardPresenter.java b/java/com/android/incallui/CallCardPresenter.java
new file mode 100644
index 000000000..930775772
--- /dev/null
+++ b/java/com/android/incallui/CallCardPresenter.java
@@ -0,0 +1,1110 @@
+/*
+ * Copyright (C) 2013 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.incallui;
+
+import static com.android.contacts.common.compat.CallCompat.Details.PROPERTY_ENTERPRISE_CALL;
+
+import android.Manifest;
+import android.app.Application;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManager;
+import android.os.BatteryManager;
+import android.os.Handler;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.content.ContextCompat;
+import android.telecom.Call.Details;
+import android.telecom.StatusHints;
+import android.telecom.TelecomManager;
+import android.text.TextUtils;
+import android.view.Display;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import com.android.contacts.common.ContactsUtils;
+import com.android.contacts.common.preference.ContactsPreferences;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.ConfigProviderBindings;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.enrichedcall.EnrichedCallManager;
+import com.android.dialer.enrichedcall.Session;
+import com.android.dialer.multimedia.MultimediaData;
+import com.android.incallui.ContactInfoCache.ContactCacheEntry;
+import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback;
+import com.android.incallui.InCallPresenter.InCallDetailsListener;
+import com.android.incallui.InCallPresenter.InCallEventListener;
+import com.android.incallui.InCallPresenter.InCallState;
+import com.android.incallui.InCallPresenter.InCallStateListener;
+import com.android.incallui.InCallPresenter.IncomingCallListener;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.DialerCall.SessionModificationState;
+import com.android.incallui.call.DialerCallListener;
+import com.android.incallui.incall.protocol.ContactPhotoType;
+import com.android.incallui.incall.protocol.InCallScreen;
+import com.android.incallui.incall.protocol.InCallScreenDelegate;
+import com.android.incallui.incall.protocol.PrimaryCallState;
+import com.android.incallui.incall.protocol.PrimaryInfo;
+import com.android.incallui.incall.protocol.SecondaryInfo;
+import java.lang.ref.WeakReference;
+
+/**
+ * Controller for the Call Card Fragment. This class listens for changes to InCallState and passes
+ * it along to the fragment.
+ */
+public class CallCardPresenter
+ implements InCallStateListener,
+ IncomingCallListener,
+ InCallDetailsListener,
+ InCallEventListener,
+ InCallScreenDelegate,
+ DialerCallListener,
+ EnrichedCallManager.StateChangedListener {
+
+ /**
+ * Amount of time to wait before sending an announcement via the accessibility manager. When the
+ * call state changes to an outgoing or incoming state for the first time, the UI can often be
+ * changing due to call updates or contact lookup. This allows the UI to settle to a stable state
+ * to ensure that the correct information is announced.
+ */
+ private static final long ACCESSIBILITY_ANNOUNCEMENT_DELAY_MILLIS = 500;
+
+ /** Flag to allow the user's current location to be shown during emergency calls. */
+ private static final String CONFIG_ENABLE_EMERGENCY_LOCATION = "config_enable_emergency_location";
+
+ private static final boolean CONFIG_ENABLE_EMERGENCY_LOCATION_DEFAULT = true;
+
+ /**
+ * Make it possible to not get location during an emergency call if the battery is too low, since
+ * doing so could trigger gps and thus potentially cause the phone to die in the middle of the
+ * call.
+ */
+ private static final String CONFIG_MIN_BATTERY_PERCENT_FOR_EMERGENCY_LOCATION =
+ "min_battery_percent_for_emergency_location";
+
+ private static final long CONFIG_MIN_BATTERY_PERCENT_FOR_EMERGENCY_LOCATION_DEFAULT = 10;
+
+ private final Context mContext;
+ private final Handler handler = new Handler();
+
+ private DialerCall mPrimary;
+ private DialerCall mSecondary;
+ private ContactCacheEntry mPrimaryContactInfo;
+ private ContactCacheEntry mSecondaryContactInfo;
+ @Nullable private ContactsPreferences mContactsPreferences;
+ private boolean mIsFullscreen = false;
+ private InCallScreen mInCallScreen;
+ private boolean isInCallScreenReady;
+ private boolean shouldSendAccessibilityEvent;
+ private final String locationModule = null;
+ private final Runnable sendAccessibilityEventRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ shouldSendAccessibilityEvent = !sendAccessibilityEvent(mContext, getUi());
+ LogUtil.i(
+ "CallCardPresenter.sendAccessibilityEventRunnable",
+ "still should send: %b",
+ shouldSendAccessibilityEvent);
+ if (!shouldSendAccessibilityEvent) {
+ handler.removeCallbacks(this);
+ }
+ }
+ };
+
+ public CallCardPresenter(Context context) {
+ LogUtil.i("CallCardController.constructor", null);
+ mContext = Assert.isNotNull(context).getApplicationContext();
+ }
+
+ private static boolean hasCallSubject(DialerCall call) {
+ return !TextUtils.isEmpty(call.getCallSubject());
+ }
+
+ @Override
+ public void onInCallScreenDelegateInit(InCallScreen inCallScreen) {
+ Assert.isNotNull(inCallScreen);
+ mInCallScreen = inCallScreen;
+ mContactsPreferences = ContactsPreferencesFactory.newContactsPreferences(mContext);
+
+ // Call may be null if disconnect happened already.
+ DialerCall call = CallList.getInstance().getFirstCall();
+ if (call != null) {
+ mPrimary = call;
+ if (shouldShowNoteSentToast(mPrimary)) {
+ mInCallScreen.showNoteSentToast();
+ }
+ call.addListener(this);
+
+ // start processing lookups right away.
+ if (!call.isConferenceCall()) {
+ startContactInfoSearch(call, true, call.getState() == DialerCall.State.INCOMING);
+ } else {
+ updateContactEntry(null, true);
+ }
+ }
+
+ onStateChange(null, InCallPresenter.getInstance().getInCallState(), CallList.getInstance());
+ }
+
+ @Override
+ public void onInCallScreenReady() {
+ LogUtil.i("CallCardController.onInCallScreenReady", null);
+ Assert.checkState(!isInCallScreenReady);
+ if (mContactsPreferences != null) {
+ mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
+ }
+
+ EnrichedCallManager.Accessor.getInstance(((Application) mContext))
+ .registerStateChangedListener(this);
+
+ // Contact search may have completed before ui is ready.
+ if (mPrimaryContactInfo != null) {
+ updatePrimaryDisplayInfo();
+ }
+
+ // Register for call state changes last
+ InCallPresenter.getInstance().addListener(this);
+ InCallPresenter.getInstance().addIncomingCallListener(this);
+ InCallPresenter.getInstance().addDetailsListener(this);
+ InCallPresenter.getInstance().addInCallEventListener(this);
+ isInCallScreenReady = true;
+ }
+
+ @Override
+ public void onInCallScreenUnready() {
+ LogUtil.i("CallCardController.onInCallScreenUnready", null);
+ Assert.checkState(isInCallScreenReady);
+
+ EnrichedCallManager.Accessor.getInstance(((Application) mContext))
+ .unregisterStateChangedListener(this);
+ // stop getting call state changes
+ InCallPresenter.getInstance().removeListener(this);
+ InCallPresenter.getInstance().removeIncomingCallListener(this);
+ InCallPresenter.getInstance().removeDetailsListener(this);
+ InCallPresenter.getInstance().removeInCallEventListener(this);
+ if (mPrimary != null) {
+ mPrimary.removeListener(this);
+ }
+
+ mPrimary = null;
+ mPrimaryContactInfo = null;
+ mSecondaryContactInfo = null;
+ isInCallScreenReady = false;
+ }
+
+ @Override
+ public void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call) {
+ // same logic should happen as with onStateChange()
+ onStateChange(oldState, newState, CallList.getInstance());
+ }
+
+ @Override
+ public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
+ LogUtil.v("CallCardPresenter.onStateChange", "" + newState);
+ if (mInCallScreen == null) {
+ return;
+ }
+
+ DialerCall primary = null;
+ DialerCall secondary = null;
+
+ if (newState == InCallState.INCOMING) {
+ primary = callList.getIncomingCall();
+ } else if (newState == InCallState.PENDING_OUTGOING || newState == InCallState.OUTGOING) {
+ primary = callList.getOutgoingCall();
+ if (primary == null) {
+ primary = callList.getPendingOutgoingCall();
+ }
+
+ // getCallToDisplay doesn't go through outgoing or incoming calls. It will return the
+ // highest priority call to display as the secondary call.
+ secondary = getCallToDisplay(callList, null, true);
+ } else if (newState == InCallState.INCALL) {
+ primary = getCallToDisplay(callList, null, false);
+ secondary = getCallToDisplay(callList, primary, true);
+ }
+
+ LogUtil.v("CallCardPresenter.onStateChange", "primary call: " + primary);
+ LogUtil.v("CallCardPresenter.onStateChange", "secondary call: " + secondary);
+
+ final boolean primaryChanged =
+ !(DialerCall.areSame(mPrimary, primary) && DialerCall.areSameNumber(mPrimary, primary));
+ final boolean secondaryChanged =
+ !(DialerCall.areSame(mSecondary, secondary)
+ && DialerCall.areSameNumber(mSecondary, secondary));
+
+ mSecondary = secondary;
+ DialerCall previousPrimary = mPrimary;
+ mPrimary = primary;
+
+ if (mPrimary != null) {
+ InCallPresenter.getInstance().onForegroundCallChanged(mPrimary);
+ mInCallScreen.updateInCallScreenColors();
+ }
+
+ if (primaryChanged && shouldShowNoteSentToast(primary)) {
+ mInCallScreen.showNoteSentToast();
+ }
+
+ // Refresh primary call information if either:
+ // 1. Primary call changed.
+ // 2. The call's ability to manage conference has changed.
+ if (shouldRefreshPrimaryInfo(primaryChanged)) {
+ // primary call has changed
+ if (previousPrimary != null) {
+ previousPrimary.removeListener(this);
+ }
+ mPrimary.addListener(this);
+
+ mPrimaryContactInfo =
+ ContactInfoCache.buildCacheEntryFromCall(
+ mContext, mPrimary, mPrimary.getState() == DialerCall.State.INCOMING);
+ updatePrimaryDisplayInfo();
+ maybeStartSearch(mPrimary, true);
+ maybeClearSessionModificationState(mPrimary);
+ }
+
+ if (previousPrimary != null && mPrimary == null) {
+ previousPrimary.removeListener(this);
+ }
+
+ if (mSecondary == null) {
+ // Secondary call may have ended. Update the ui.
+ mSecondaryContactInfo = null;
+ updateSecondaryDisplayInfo();
+ } else if (secondaryChanged) {
+ // secondary call has changed
+ mSecondaryContactInfo =
+ ContactInfoCache.buildCacheEntryFromCall(
+ mContext, mSecondary, mSecondary.getState() == DialerCall.State.INCOMING);
+ updateSecondaryDisplayInfo();
+ maybeStartSearch(mSecondary, false);
+ maybeClearSessionModificationState(mSecondary);
+ }
+
+ // Set the call state
+ int callState = DialerCall.State.IDLE;
+ if (mPrimary != null) {
+ callState = mPrimary.getState();
+ updatePrimaryCallState();
+ } else {
+ getUi().setCallState(PrimaryCallState.createEmptyPrimaryCallState());
+ }
+
+ maybeShowManageConferenceCallButton();
+
+ // Hide the end call button instantly if we're receiving an incoming call.
+ getUi()
+ .setEndCallButtonEnabled(
+ shouldShowEndCallButton(mPrimary, callState),
+ callState != DialerCall.State.INCOMING /* animate */);
+
+ maybeSendAccessibilityEvent(oldState, newState, primaryChanged);
+ }
+
+ @Override
+ public void onDetailsChanged(DialerCall call, Details details) {
+ updatePrimaryCallState();
+
+ if (call.can(Details.CAPABILITY_MANAGE_CONFERENCE)
+ != details.can(Details.CAPABILITY_MANAGE_CONFERENCE)) {
+ maybeShowManageConferenceCallButton();
+ }
+ }
+
+ @Override
+ public void onDialerCallDisconnect() {}
+
+ @Override
+ public void onDialerCallUpdate() {
+ // No-op; specific call updates handled elsewhere.
+ }
+
+ @Override
+ public void onWiFiToLteHandover() {}
+
+ @Override
+ public void onHandoverToWifiFailure() {}
+
+ /** Handles a change to the child number by refreshing the primary call info. */
+ @Override
+ public void onDialerCallChildNumberChange() {
+ LogUtil.v("CallCardPresenter.onDialerCallChildNumberChange", "");
+
+ if (mPrimary == null) {
+ return;
+ }
+ updatePrimaryDisplayInfo();
+ }
+
+ /** Handles a change to the last forwarding number by refreshing the primary call info. */
+ @Override
+ public void onDialerCallLastForwardedNumberChange() {
+ LogUtil.v("CallCardPresenter.onDialerCallLastForwardedNumberChange", "");
+
+ if (mPrimary == null) {
+ return;
+ }
+ updatePrimaryDisplayInfo();
+ updatePrimaryCallState();
+ }
+
+ @Override
+ public void onDialerCallUpgradeToVideo() {}
+
+ /**
+ * Handles a change to the session modification state for a call.
+ *
+ * @param sessionModificationState The new session modification state.
+ */
+ @Override
+ public void onDialerCallSessionModificationStateChange(
+ @SessionModificationState int sessionModificationState) {
+ LogUtil.v(
+ "CallCardPresenter.onDialerCallSessionModificationStateChange",
+ "state: " + sessionModificationState);
+
+ if (mPrimary == null) {
+ return;
+ }
+ getUi()
+ .setEndCallButtonEnabled(
+ sessionModificationState
+ != DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST,
+ true /* shouldAnimate */);
+ updatePrimaryCallState();
+ }
+
+ @Override
+ public void onEnrichedCallStateChanged() {
+ LogUtil.enterBlock("CallCardPresenter.onEnrichedCallStateChanged");
+ updatePrimaryDisplayInfo();
+ }
+
+ private boolean shouldRefreshPrimaryInfo(boolean primaryChanged) {
+ if (mPrimary == null) {
+ return false;
+ }
+ return primaryChanged
+ || mInCallScreen.isManageConferenceVisible() != shouldShowManageConference();
+ }
+
+ private void updatePrimaryCallState() {
+ if (getUi() != null && mPrimary != null) {
+ boolean isWorkCall =
+ mPrimary.hasProperty(PROPERTY_ENTERPRISE_CALL)
+ || (mPrimaryContactInfo != null
+ && mPrimaryContactInfo.userType == ContactsUtils.USER_TYPE_WORK);
+ boolean isHdAudioCall =
+ isPrimaryCallActive() && mPrimary.hasProperty(Details.PROPERTY_HIGH_DEF_AUDIO);
+ // Check for video state change and update the visibility of the contact photo. The contact
+ // photo is hidden when the incoming video surface is shown.
+ // The contact photo visibility can also change in setPrimary().
+ boolean shouldShowContactPhoto =
+ !VideoCallPresenter.showIncomingVideo(mPrimary.getVideoState(), mPrimary.getState());
+ getUi()
+ .setCallState(
+ new PrimaryCallState(
+ mPrimary.getState(),
+ mPrimary.getVideoState(),
+ mPrimary.getSessionModificationState(),
+ mPrimary.getDisconnectCause(),
+ getConnectionLabel(),
+ getCallStateIcon(),
+ getGatewayNumber(),
+ shouldShowCallSubject(mPrimary) ? mPrimary.getCallSubject() : null,
+ mPrimary.getCallbackNumber(),
+ mPrimary.hasProperty(Details.PROPERTY_WIFI),
+ mPrimary.isConferenceCall(),
+ isWorkCall,
+ isHdAudioCall,
+ !TextUtils.isEmpty(mPrimary.getLastForwardedNumber()),
+ shouldShowContactPhoto,
+ mPrimary.getConnectTimeMillis(),
+ CallerInfoUtils.isVoiceMailNumber(mContext, mPrimary),
+ mPrimary.isRemotelyHeld()));
+
+ InCallActivity activity =
+ (InCallActivity) (mInCallScreen.getInCallScreenFragment().getActivity());
+ if (activity != null) {
+ activity.onPrimaryCallStateChanged();
+ }
+ }
+ }
+
+ /** Only show the conference call button if we can manage the conference. */
+ private void maybeShowManageConferenceCallButton() {
+ getUi().showManageConferenceCallButton(shouldShowManageConference());
+ }
+
+ /**
+ * Determines if the manage conference button should be visible, based on the current primary
+ * call.
+ *
+ * @return {@code True} if the manage conference button should be visible.
+ */
+ private boolean shouldShowManageConference() {
+ if (mPrimary == null) {
+ return false;
+ }
+
+ return mPrimary.can(android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE)
+ && !mIsFullscreen;
+ }
+
+ @Override
+ public void onCallStateButtonClicked() {
+ Intent broadcastIntent = Bindings.get(mContext).getCallStateButtonBroadcastIntent(mContext);
+ if (broadcastIntent != null) {
+ LogUtil.v(
+ "CallCardPresenter.onCallStateButtonClicked",
+ "sending call state button broadcast: " + broadcastIntent);
+ mContext.sendBroadcast(broadcastIntent, Manifest.permission.READ_PHONE_STATE);
+ }
+ }
+
+ @Override
+ public void onManageConferenceClicked() {
+ InCallActivity activity =
+ (InCallActivity) (mInCallScreen.getInCallScreenFragment().getActivity());
+ activity.showConferenceFragment(true);
+ }
+
+ @Override
+ public void onShrinkAnimationComplete() {
+ InCallPresenter.getInstance().onShrinkAnimationComplete();
+ }
+
+ @Override
+ public Drawable getDefaultContactPhotoDrawable() {
+ return ContactInfoCache.getInstance(mContext).getDefaultContactPhotoDrawable();
+ }
+
+ private void maybeStartSearch(DialerCall call, boolean isPrimary) {
+ // no need to start search for conference calls which show generic info.
+ if (call != null && !call.isConferenceCall()) {
+ startContactInfoSearch(call, isPrimary, call.getState() == DialerCall.State.INCOMING);
+ }
+ }
+
+ private void maybeClearSessionModificationState(DialerCall call) {
+ @SessionModificationState int state = call.getSessionModificationState();
+ if (state != DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST
+ && state != DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
+ LogUtil.i("CallCardPresenter.maybeClearSessionModificationState", "clearing state");
+ call.setSessionModificationState(DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ }
+ }
+
+ /** Starts a query for more contact data for the save primary and secondary calls. */
+ private void startContactInfoSearch(
+ final DialerCall call, final boolean isPrimary, boolean isIncoming) {
+ final ContactInfoCache cache = ContactInfoCache.getInstance(mContext);
+
+ cache.findInfo(call, isIncoming, new ContactLookupCallback(this, isPrimary));
+ }
+
+ private void onContactInfoComplete(String callId, ContactCacheEntry entry, boolean isPrimary) {
+ final boolean entryMatchesExistingCall =
+ (isPrimary && mPrimary != null && TextUtils.equals(callId, mPrimary.getId()))
+ || (!isPrimary && mSecondary != null && TextUtils.equals(callId, mSecondary.getId()));
+ if (entryMatchesExistingCall) {
+ updateContactEntry(entry, isPrimary);
+ } else {
+ LogUtil.e(
+ "CallCardPresenter.onContactInfoComplete",
+ "dropping stale contact lookup info for " + callId);
+ }
+
+ final DialerCall call = CallList.getInstance().getCallById(callId);
+ if (call != null) {
+ call.getLogState().contactLookupResult = entry.contactLookupResult;
+ }
+ if (entry.contactUri != null) {
+ CallerInfoUtils.sendViewNotification(mContext, entry.contactUri);
+ }
+ }
+
+ private void onImageLoadComplete(String callId, ContactCacheEntry entry) {
+ if (getUi() == null) {
+ return;
+ }
+
+ if (entry.photo != null) {
+ if (mPrimary != null && callId.equals(mPrimary.getId())) {
+ updateContactEntry(entry, true /* isPrimary */);
+ } else if (mSecondary != null && callId.equals(mSecondary.getId())) {
+ updateContactEntry(entry, false /* isPrimary */);
+ }
+ }
+ }
+
+ private void updateContactEntry(ContactCacheEntry entry, boolean isPrimary) {
+ if (isPrimary) {
+ mPrimaryContactInfo = entry;
+ updatePrimaryDisplayInfo();
+ } else {
+ mSecondaryContactInfo = entry;
+ updateSecondaryDisplayInfo();
+ }
+ }
+
+ /**
+ * Get the highest priority call to display. Goes through the calls and chooses which to return
+ * based on priority of which type of call to display to the user. Callers can use the "ignore"
+ * feature to get the second best call by passing a previously found primary call as ignore.
+ *
+ * @param ignore A call to ignore if found.
+ */
+ private DialerCall getCallToDisplay(
+ CallList callList, DialerCall ignore, boolean skipDisconnected) {
+ // Active calls come second. An active call always gets precedent.
+ DialerCall retval = callList.getActiveCall();
+ if (retval != null && retval != ignore) {
+ return retval;
+ }
+
+ // Sometimes there is intemediate state that two calls are in active even one is about
+ // to be on hold.
+ retval = callList.getSecondActiveCall();
+ if (retval != null && retval != ignore) {
+ return retval;
+ }
+
+ // Disconnected calls get primary position if there are no active calls
+ // to let user know quickly what call has disconnected. Disconnected
+ // calls are very short lived.
+ if (!skipDisconnected) {
+ retval = callList.getDisconnectingCall();
+ if (retval != null && retval != ignore) {
+ return retval;
+ }
+ retval = callList.getDisconnectedCall();
+ if (retval != null && retval != ignore) {
+ return retval;
+ }
+ }
+
+ // Then we go to background call (calls on hold)
+ retval = callList.getBackgroundCall();
+ if (retval != null && retval != ignore) {
+ return retval;
+ }
+
+ // Lastly, we go to a second background call.
+ retval = callList.getSecondBackgroundCall();
+
+ return retval;
+ }
+
+ private void updatePrimaryDisplayInfo() {
+ if (mInCallScreen == null) {
+ // TODO: May also occur if search result comes back after ui is destroyed. Look into
+ // removing that case completely.
+ LogUtil.v(
+ "CallCardPresenter.updatePrimaryDisplayInfo",
+ "updatePrimaryDisplayInfo called but ui is null!");
+ return;
+ }
+
+ if (mPrimary == null) {
+ // Clear the primary display info.
+ mInCallScreen.setPrimary(PrimaryInfo.createEmptyPrimaryInfo());
+ return;
+ }
+
+ // Hide the contact photo if we are in a video call and the incoming video surface is
+ // showing.
+ boolean showContactPhoto =
+ !VideoCallPresenter.showIncomingVideo(mPrimary.getVideoState(), mPrimary.getState());
+
+ // DialerCall placed through a work phone account.
+ boolean hasWorkCallProperty = mPrimary.hasProperty(PROPERTY_ENTERPRISE_CALL);
+
+ Session enrichedCallSession =
+ mPrimary.getNumber() == null
+ ? null
+ : EnrichedCallManager.Accessor.getInstance(((Application) mContext))
+ .getSession(mPrimary.getNumber());
+ MultimediaData enrichedCallMultimediaData =
+ enrichedCallSession == null ? null : enrichedCallSession.getMultimediaData();
+
+ if (mPrimary.isConferenceCall()) {
+ LogUtil.v(
+ "CallCardPresenter.updatePrimaryDisplayInfo",
+ "update primary display info for conference call.");
+
+ mInCallScreen.setPrimary(
+ new PrimaryInfo(
+ null /* number */,
+ getConferenceString(mPrimary),
+ false /* nameIsNumber */,
+ null /* location */,
+ null /* label */,
+ getConferencePhoto(mPrimary),
+ ContactPhotoType.DEFAULT_PLACEHOLDER,
+ false /* isSipCall */,
+ showContactPhoto,
+ hasWorkCallProperty,
+ false /* isSpam */,
+ false /* answeringDisconnectsOngoingCall */,
+ shouldShowLocation(),
+ null /* contactInfoLookupKey */,
+ null /* enrichedCallMultimediaData */));
+ } else if (mPrimaryContactInfo != null) {
+ LogUtil.v(
+ "CallCardPresenter.updatePrimaryDisplayInfo",
+ "update primary display info for " + mPrimaryContactInfo);
+
+ String name = getNameForCall(mPrimaryContactInfo);
+ String number;
+
+ boolean isChildNumberShown = !TextUtils.isEmpty(mPrimary.getChildNumber());
+ boolean isForwardedNumberShown = !TextUtils.isEmpty(mPrimary.getLastForwardedNumber());
+ boolean isCallSubjectShown = shouldShowCallSubject(mPrimary);
+
+ if (isCallSubjectShown) {
+ number = null;
+ } else if (isChildNumberShown) {
+ number = mContext.getString(R.string.child_number, mPrimary.getChildNumber());
+ } else if (isForwardedNumberShown) {
+ // Use last forwarded number instead of second line, if present.
+ number = mPrimary.getLastForwardedNumber();
+ } else {
+ number = mPrimaryContactInfo.number;
+ }
+
+ boolean nameIsNumber = name != null && name.equals(mPrimaryContactInfo.number);
+ // DialerCall with caller that is a work contact.
+ boolean isWorkContact = (mPrimaryContactInfo.userType == ContactsUtils.USER_TYPE_WORK);
+ mInCallScreen.setPrimary(
+ new PrimaryInfo(
+ number,
+ name,
+ nameIsNumber,
+ mPrimaryContactInfo.location,
+ isChildNumberShown || isCallSubjectShown ? null : mPrimaryContactInfo.label,
+ mPrimaryContactInfo.photo,
+ mPrimaryContactInfo.photoType,
+ mPrimaryContactInfo.isSipCall,
+ showContactPhoto,
+ hasWorkCallProperty || isWorkContact,
+ mPrimary.isSpam(),
+ mPrimary.answeringDisconnectsForegroundVideoCall(),
+ shouldShowLocation(),
+ mPrimaryContactInfo.lookupKey,
+ enrichedCallMultimediaData));
+ } else {
+ // Clear the primary display info.
+ mInCallScreen.setPrimary(PrimaryInfo.createEmptyPrimaryInfo());
+ }
+
+ mInCallScreen.showLocationUi(null);
+ }
+
+ private boolean shouldShowLocation() {
+ if (isOutgoingEmergencyCall(mPrimary)) {
+ LogUtil.i("CallCardPresenter.shouldShowLocation", "new emergency call");
+ return true;
+ } else if (isIncomingEmergencyCall(mPrimary)) {
+ LogUtil.i("CallCardPresenter.shouldShowLocation", "potential emergency callback");
+ return true;
+ } else if (isIncomingEmergencyCall(mSecondary)) {
+ LogUtil.i("CallCardPresenter.shouldShowLocation", "has potential emergency callback");
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean isOutgoingEmergencyCall(@Nullable DialerCall call) {
+ return call != null && !call.isIncoming() && call.isEmergencyCall();
+ }
+
+ private static boolean isIncomingEmergencyCall(@Nullable DialerCall call) {
+ return call != null && call.isIncoming() && call.isPotentialEmergencyCallback();
+ }
+
+ private boolean hasLocationPermission() {
+ return ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean isBatteryTooLowForEmergencyLocation() {
+ Intent batteryStatus =
+ mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+ int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
+ if (status == BatteryManager.BATTERY_STATUS_CHARGING
+ || status == BatteryManager.BATTERY_STATUS_FULL) {
+ // Plugged in or full battery
+ return false;
+ }
+ int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+ float batteryPercent = (100f * level) / scale;
+ long threshold =
+ ConfigProviderBindings.get(mContext)
+ .getLong(
+ CONFIG_MIN_BATTERY_PERCENT_FOR_EMERGENCY_LOCATION,
+ CONFIG_MIN_BATTERY_PERCENT_FOR_EMERGENCY_LOCATION_DEFAULT);
+ LogUtil.i(
+ "CallCardPresenter.isBatteryTooLowForEmergencyLocation",
+ "percent charged: " + batteryPercent + ", min required charge: " + threshold);
+ return batteryPercent < threshold;
+ }
+
+ private void updateSecondaryDisplayInfo() {
+ if (mInCallScreen == null) {
+ return;
+ }
+
+ if (mSecondary == null) {
+ // Clear the secondary display info.
+ mInCallScreen.setSecondary(SecondaryInfo.createEmptySecondaryInfo(mIsFullscreen));
+ return;
+ }
+
+ if (mSecondary.isConferenceCall()) {
+ mInCallScreen.setSecondary(
+ new SecondaryInfo(
+ true /* show */,
+ getConferenceString(mSecondary),
+ false /* nameIsNumber */,
+ null /* label */,
+ mSecondary.getCallProviderLabel(),
+ true /* isConference */,
+ mSecondary.isVideoCall(),
+ mIsFullscreen));
+ } else if (mSecondaryContactInfo != null) {
+ LogUtil.v("CallCardPresenter.updateSecondaryDisplayInfo", "" + mSecondaryContactInfo);
+ String name = getNameForCall(mSecondaryContactInfo);
+ boolean nameIsNumber = name != null && name.equals(mSecondaryContactInfo.number);
+ mInCallScreen.setSecondary(
+ new SecondaryInfo(
+ true /* show */,
+ name,
+ nameIsNumber,
+ mSecondaryContactInfo.label,
+ mSecondary.getCallProviderLabel(),
+ false /* isConference */,
+ mSecondary.isVideoCall(),
+ mIsFullscreen));
+ } else {
+ // Clear the secondary display info.
+ mInCallScreen.setSecondary(SecondaryInfo.createEmptySecondaryInfo(mIsFullscreen));
+ }
+ }
+
+ /** Returns the gateway number for any existing outgoing call. */
+ private String getGatewayNumber() {
+ if (hasOutgoingGatewayCall()) {
+ return DialerCall.getNumberFromHandle(mPrimary.getGatewayInfo().getGatewayAddress());
+ }
+ return null;
+ }
+
+ /**
+ * Returns the label (line of text above the number/name) for any given call. For example,
+ * "calling via [Account/Google Voice]" for outgoing calls.
+ */
+ private String getConnectionLabel() {
+ if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+ StatusHints statusHints = mPrimary.getStatusHints();
+ if (statusHints != null && !TextUtils.isEmpty(statusHints.getLabel())) {
+ return statusHints.getLabel().toString();
+ }
+
+ if (hasOutgoingGatewayCall() && getUi() != null) {
+ // Return the label for the gateway app on outgoing calls.
+ final PackageManager pm = mContext.getPackageManager();
+ try {
+ ApplicationInfo info =
+ pm.getApplicationInfo(mPrimary.getGatewayInfo().getGatewayProviderPackageName(), 0);
+ return pm.getApplicationLabel(info).toString();
+ } catch (PackageManager.NameNotFoundException e) {
+ LogUtil.e("CallCardPresenter.getConnectionLabel", "gateway Application Not Found.", e);
+ return null;
+ }
+ }
+ return mPrimary.getCallProviderLabel();
+ }
+
+ private Drawable getCallStateIcon() {
+ // Return connection icon if one exists.
+ StatusHints statusHints = mPrimary.getStatusHints();
+ if (statusHints != null && statusHints.getIcon() != null) {
+ Drawable icon = statusHints.getIcon().loadDrawable(mContext);
+ if (icon != null) {
+ return icon;
+ }
+ }
+
+ return null;
+ }
+
+ private boolean hasOutgoingGatewayCall() {
+ // We only display the gateway information while STATE_DIALING so return false for any other
+ // call state.
+ // TODO: mPrimary can be null because this is called from updatePrimaryDisplayInfo which
+ // is also called after a contact search completes (call is not present yet). Split the
+ // UI update so it can receive independent updates.
+ if (mPrimary == null) {
+ return false;
+ }
+ return DialerCall.State.isDialing(mPrimary.getState())
+ && mPrimary.getGatewayInfo() != null
+ && !mPrimary.getGatewayInfo().isEmpty();
+ }
+
+ /** Gets the name to display for the call. */
+ String getNameForCall(ContactCacheEntry contactInfo) {
+ String preferredName =
+ ContactDisplayUtils.getPreferredDisplayName(
+ contactInfo.namePrimary, contactInfo.nameAlternative, mContactsPreferences);
+ if (TextUtils.isEmpty(preferredName)) {
+ return contactInfo.number;
+ }
+ return preferredName;
+ }
+
+ /** Gets the number to display for a call. */
+ String getNumberForCall(ContactCacheEntry contactInfo) {
+ // If the name is empty, we use the number for the name...so don't show a second
+ // number in the number field
+ String preferredName =
+ ContactDisplayUtils.getPreferredDisplayName(
+ contactInfo.namePrimary, contactInfo.nameAlternative, mContactsPreferences);
+ if (TextUtils.isEmpty(preferredName)) {
+ return contactInfo.location;
+ }
+ return contactInfo.number;
+ }
+
+ @Override
+ public void onSecondaryInfoClicked() {
+ if (mSecondary == null) {
+ LogUtil.e(
+ "CallCardPresenter.onSecondaryInfoClicked",
+ "secondary info clicked but no secondary call.");
+ return;
+ }
+
+ LogUtil.i(
+ "CallCardPresenter.onSecondaryInfoClicked", "swapping call to foreground: " + mSecondary);
+ mSecondary.unhold();
+ }
+
+ @Override
+ public void onEndCallClicked() {
+ LogUtil.i("CallCardPresenter.onEndCallClicked", "disconnecting call: " + mPrimary);
+ if (mPrimary != null) {
+ mPrimary.disconnect();
+ }
+ }
+
+ /**
+ * Handles a change to the fullscreen mode of the in-call UI.
+ *
+ * @param isFullscreenMode {@code True} if the in-call UI is entering full screen mode.
+ */
+ @Override
+ public void onFullscreenModeChanged(boolean isFullscreenMode) {
+ mIsFullscreen = isFullscreenMode;
+ if (mInCallScreen == null) {
+ return;
+ }
+ maybeShowManageConferenceCallButton();
+ }
+
+ private boolean isPrimaryCallActive() {
+ return mPrimary != null && mPrimary.getState() == DialerCall.State.ACTIVE;
+ }
+
+ private String getConferenceString(DialerCall call) {
+ boolean isGenericConference = call.hasProperty(Details.PROPERTY_GENERIC_CONFERENCE);
+ LogUtil.v("CallCardPresenter.getConferenceString", "" + isGenericConference);
+
+ final int resId =
+ isGenericConference ? R.string.generic_conference_call_name : R.string.conference_call_name;
+ return mContext.getResources().getString(resId);
+ }
+
+ private Drawable getConferencePhoto(DialerCall call) {
+ boolean isGenericConference = call.hasProperty(Details.PROPERTY_GENERIC_CONFERENCE);
+ LogUtil.v("CallCardPresenter.getConferencePhoto", "" + isGenericConference);
+
+ final int resId = isGenericConference ? R.drawable.img_phone : R.drawable.img_conference;
+ Drawable photo = mContext.getResources().getDrawable(resId);
+ photo.setAutoMirrored(true);
+ return photo;
+ }
+
+ private boolean shouldShowEndCallButton(DialerCall primary, int callState) {
+ if (primary == null) {
+ return false;
+ }
+ if ((!DialerCall.State.isConnectingOrConnected(callState)
+ && callState != DialerCall.State.DISCONNECTING
+ && callState != DialerCall.State.DISCONNECTED)
+ || callState == DialerCall.State.INCOMING) {
+ return false;
+ }
+ if (mPrimary.getSessionModificationState()
+ == DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void onInCallScreenResumed() {
+ if (shouldSendAccessibilityEvent) {
+ handler.postDelayed(sendAccessibilityEventRunnable, ACCESSIBILITY_ANNOUNCEMENT_DELAY_MILLIS);
+ }
+ }
+
+ static boolean sendAccessibilityEvent(Context context, InCallScreen inCallScreen) {
+ AccessibilityManager am =
+ (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ if (!am.isEnabled()) {
+ LogUtil.w("CallCardPresenter.sendAccessibilityEvent", "accessibility is off");
+ return false;
+ }
+ if (inCallScreen == null) {
+ LogUtil.w("CallCardPresenter.sendAccessibilityEvent", "incallscreen is null");
+ return false;
+ }
+ Fragment fragment = inCallScreen.getInCallScreenFragment();
+ if (fragment == null || fragment.getView() == null || fragment.getView().getParent() == null) {
+ LogUtil.w("CallCardPresenter.sendAccessibilityEvent", "fragment/view/parent is null");
+ return false;
+ }
+
+ DisplayManager displayManager =
+ (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ boolean screenIsOn = display.getState() == Display.STATE_ON;
+ LogUtil.d("CallCardPresenter.sendAccessibilityEvent", "screen is on: %b", screenIsOn);
+ if (!screenIsOn) {
+ return false;
+ }
+
+ AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_ANNOUNCEMENT);
+ inCallScreen.dispatchPopulateAccessibilityEvent(event);
+ View view = inCallScreen.getInCallScreenFragment().getView();
+ view.getParent().requestSendAccessibilityEvent(view, event);
+ return true;
+ }
+
+ private void maybeSendAccessibilityEvent(
+ InCallState oldState, final InCallState newState, boolean primaryChanged) {
+ shouldSendAccessibilityEvent = false;
+ if (mContext == null) {
+ return;
+ }
+ final AccessibilityManager am =
+ (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ if (!am.isEnabled()) {
+ return;
+ }
+ // Announce the current call if it's new incoming/outgoing call or primary call is changed
+ // due to switching calls between two ongoing calls (one is on hold).
+ if ((oldState != InCallState.OUTGOING && newState == InCallState.OUTGOING)
+ || (oldState != InCallState.INCOMING && newState == InCallState.INCOMING)
+ || primaryChanged) {
+ LogUtil.i(
+ "CallCardPresenter.maybeSendAccessibilityEvent", "schedule accessibility announcement");
+ shouldSendAccessibilityEvent = true;
+ handler.postDelayed(sendAccessibilityEventRunnable, ACCESSIBILITY_ANNOUNCEMENT_DELAY_MILLIS);
+ }
+ }
+
+ /**
+ * Determines whether the call subject should be visible on the UI. For the call subject to be
+ * visible, the call has to be in an incoming or waiting state, and the subject must not be empty.
+ *
+ * @param call The call.
+ * @return {@code true} if the subject should be shown, {@code false} otherwise.
+ */
+ private boolean shouldShowCallSubject(DialerCall call) {
+ if (call == null) {
+ return false;
+ }
+
+ boolean isIncomingOrWaiting =
+ mPrimary.getState() == DialerCall.State.INCOMING
+ || mPrimary.getState() == DialerCall.State.CALL_WAITING;
+ return isIncomingOrWaiting
+ && !TextUtils.isEmpty(call.getCallSubject())
+ && call.getNumberPresentation() == TelecomManager.PRESENTATION_ALLOWED
+ && call.isCallSubjectSupported();
+ }
+
+ /**
+ * Determines whether the "note sent" toast should be shown. It should be shown for a new outgoing
+ * call with a subject.
+ *
+ * @param call The call
+ * @return {@code true} if the toast should be shown, {@code false} otherwise.
+ */
+ private boolean shouldShowNoteSentToast(DialerCall call) {
+ return call != null
+ && hasCallSubject(call)
+ && (call.getState() == DialerCall.State.DIALING
+ || call.getState() == DialerCall.State.CONNECTING);
+ }
+
+ private InCallScreen getUi() {
+ return mInCallScreen;
+ }
+
+ public static class ContactLookupCallback implements ContactInfoCacheCallback {
+
+ private final WeakReference<CallCardPresenter> mCallCardPresenter;
+ private final boolean mIsPrimary;
+
+ public ContactLookupCallback(CallCardPresenter callCardPresenter, boolean isPrimary) {
+ mCallCardPresenter = new WeakReference<CallCardPresenter>(callCardPresenter);
+ mIsPrimary = isPrimary;
+ }
+
+ @Override
+ public void onContactInfoComplete(String callId, ContactCacheEntry entry) {
+ CallCardPresenter presenter = mCallCardPresenter.get();
+ if (presenter != null) {
+ presenter.onContactInfoComplete(callId, entry, mIsPrimary);
+ }
+ }
+
+ @Override
+ public void onImageLoadComplete(String callId, ContactCacheEntry entry) {
+ CallCardPresenter presenter = mCallCardPresenter.get();
+ if (presenter != null) {
+ presenter.onImageLoadComplete(callId, entry);
+ }
+ }
+ }
+}
diff --git a/java/com/android/incallui/CallerInfo.java b/java/com/android/incallui/CallerInfo.java
new file mode 100644
index 000000000..473bb8f4e
--- /dev/null
+++ b/java/com/android/incallui/CallerInfo.java
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2006 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.incallui;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.RawContacts;
+import android.support.annotation.RequiresApi;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import com.android.contacts.common.ContactsUtils;
+import com.android.contacts.common.ContactsUtils.UserType;
+import com.android.contacts.common.util.TelephonyManagerUtils;
+import com.android.dialer.phonenumbercache.ContactInfoHelper;
+import com.android.dialer.phonenumbercache.PhoneLookupUtil;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+
+/**
+ * Looks up caller information for the given phone number. This is intermediate data and should NOT
+ * be used by any UI.
+ */
+public class CallerInfo {
+
+ private static final String TAG = "CallerInfo";
+
+ // We should always use this projection starting from N onward.
+ @RequiresApi(VERSION_CODES.N)
+ private static final String[] DEFAULT_PHONELOOKUP_PROJECTION =
+ new String[] {
+ PhoneLookup.CONTACT_ID,
+ PhoneLookup.DISPLAY_NAME,
+ PhoneLookup.LOOKUP_KEY,
+ PhoneLookup.NUMBER,
+ PhoneLookup.NORMALIZED_NUMBER,
+ PhoneLookup.LABEL,
+ PhoneLookup.TYPE,
+ PhoneLookup.PHOTO_URI,
+ PhoneLookup.CUSTOM_RINGTONE,
+ PhoneLookup.SEND_TO_VOICEMAIL
+ };
+
+ // In pre-N, contact id is stored in {@link PhoneLookup._ID} in non-sip query.
+ private static final String[] BACKWARD_COMPATIBLE_NON_SIP_DEFAULT_PHONELOOKUP_PROJECTION =
+ new String[] {
+ PhoneLookup._ID,
+ PhoneLookup.DISPLAY_NAME,
+ PhoneLookup.LOOKUP_KEY,
+ PhoneLookup.NUMBER,
+ PhoneLookup.NORMALIZED_NUMBER,
+ PhoneLookup.LABEL,
+ PhoneLookup.TYPE,
+ PhoneLookup.PHOTO_URI,
+ PhoneLookup.CUSTOM_RINGTONE,
+ PhoneLookup.SEND_TO_VOICEMAIL
+ };
+ /**
+ * Please note that, any one of these member variables can be null, and any accesses to them
+ * should be prepared to handle such a case.
+ *
+ * <p>Also, it is implied that phoneNumber is more often populated than name is, (think of calls
+ * being dialed/received using numbers where names are not known to the device), so phoneNumber
+ * should serve as a dependable fallback when name is unavailable.
+ *
+ * <p>One other detail here is that this CallerInfo object reflects information found on a
+ * connection, it is an OUTPUT that serves mainly to display information to the user. In no way is
+ * this object used as input to make a connection, so we can choose to display whatever
+ * human-readable text makes sense to the user for a connection. This is especially relevant for
+ * the phone number field, since it is the one field that is most likely exposed to the user.
+ *
+ * <p>As an example: 1. User dials "911" 2. Device recognizes that this is an emergency number 3.
+ * We use the "Emergency Number" string instead of "911" in the phoneNumber field.
+ *
+ * <p>What we're really doing here is treating phoneNumber as an essential field here, NOT name.
+ * We're NOT always guaranteed to have a name for a connection, but the number should be
+ * displayable.
+ */
+ public String name;
+
+ public String nameAlternative;
+ public String phoneNumber;
+ public String normalizedNumber;
+ public String forwardingNumber;
+ public String geoDescription;
+ public String cnapName;
+ public int numberPresentation;
+ public int namePresentation;
+ public boolean contactExists;
+ public String phoneLabel;
+ /* Split up the phoneLabel into number type and label name */
+ public int numberType;
+ public String numberLabel;
+ public int photoResource;
+ // Contact ID, which will be 0 if a contact comes from the corp CP2.
+ public long contactIdOrZero;
+ public String lookupKeyOrNull;
+ public boolean needUpdate;
+ public Uri contactRefUri;
+ public @UserType long userType;
+ /**
+ * Contact display photo URI. If a contact has no display photo but a thumbnail, it'll be the
+ * thumbnail URI instead.
+ */
+ public Uri contactDisplayPhotoUri;
+ // fields to hold individual contact preference data,
+ // including the send to voicemail flag and the ringtone
+ // uri reference.
+ public Uri contactRingtoneUri;
+ public boolean shouldSendToVoicemail;
+ /**
+ * Drawable representing the caller image. This is essentially a cache for the image data tied
+ * into the connection / callerinfo object.
+ *
+ * <p>This might be a high resolution picture which is more suitable for full-screen image view
+ * than for smaller icons used in some kinds of notifications.
+ *
+ * <p>The {@link #isCachedPhotoCurrent} flag indicates if the image data needs to be reloaded.
+ */
+ public Drawable cachedPhoto;
+ /**
+ * Bitmap representing the caller image which has possibly lower resolution than {@link
+ * #cachedPhoto} and thus more suitable for icons (like notification icons).
+ *
+ * <p>In usual cases this is just down-scaled image of {@link #cachedPhoto}. If the down-scaling
+ * fails, this will just become null.
+ *
+ * <p>The {@link #isCachedPhotoCurrent} flag indicates if the image data needs to be reloaded.
+ */
+ public Bitmap cachedPhotoIcon;
+ /**
+ * Boolean which indicates if {@link #cachedPhoto} and {@link #cachedPhotoIcon} is fresh enough.
+ * If it is false, those images aren't pointing to valid objects.
+ */
+ public boolean isCachedPhotoCurrent;
+ /**
+ * String which holds the call subject sent as extra from the lower layers for this call. This is
+ * used to display the no-caller ID reason for restricted/unknown number presentation.
+ */
+ public String callSubject;
+
+ private boolean mIsEmergency;
+ private boolean mIsVoiceMail;
+
+ public CallerInfo() {
+ // TODO: Move all the basic initialization here?
+ mIsEmergency = false;
+ mIsVoiceMail = false;
+ userType = ContactsUtils.USER_TYPE_CURRENT;
+ }
+
+ public static String[] getDefaultPhoneLookupProjection(Uri phoneLookupUri) {
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ return DEFAULT_PHONELOOKUP_PROJECTION;
+ }
+ // Pre-N
+ boolean isSip =
+ phoneLookupUri.getBooleanQueryParameter(
+ ContactsContract.PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, false);
+ return (isSip)
+ ? DEFAULT_PHONELOOKUP_PROJECTION
+ : BACKWARD_COMPATIBLE_NON_SIP_DEFAULT_PHONELOOKUP_PROJECTION;
+ }
+
+ /**
+ * getCallerInfo given a Cursor.
+ *
+ * @param context the context used to retrieve string constants
+ * @param contactRef the URI to attach to this CallerInfo object
+ * @param cursor the first object in the cursor is used to build the CallerInfo object.
+ * @return the CallerInfo which contains the caller id for the given number. The returned
+ * CallerInfo is null if no number is supplied.
+ */
+ public static CallerInfo getCallerInfo(Context context, Uri contactRef, Cursor cursor) {
+ CallerInfo info = new CallerInfo();
+ info.photoResource = 0;
+ info.phoneLabel = null;
+ info.numberType = 0;
+ info.numberLabel = null;
+ info.cachedPhoto = null;
+ info.isCachedPhotoCurrent = false;
+ info.contactExists = false;
+ info.userType = ContactsUtils.USER_TYPE_CURRENT;
+
+ Log.v(TAG, "getCallerInfo() based on cursor...");
+
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ // TODO: photo_id is always available but not taken
+ // care of here. Maybe we should store it in the
+ // CallerInfo object as well.
+
+ long contactId = 0L;
+ int columnIndex;
+
+ // Look for the name
+ columnIndex = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME);
+ if (columnIndex != -1) {
+ info.name = cursor.getString(columnIndex);
+ }
+
+ // Look for the number
+ columnIndex = cursor.getColumnIndex(PhoneLookup.NUMBER);
+ if (columnIndex != -1) {
+ info.phoneNumber = cursor.getString(columnIndex);
+ }
+
+ // Look for the normalized number
+ columnIndex = cursor.getColumnIndex(PhoneLookup.NORMALIZED_NUMBER);
+ if (columnIndex != -1) {
+ info.normalizedNumber = cursor.getString(columnIndex);
+ }
+
+ // Look for the label/type combo
+ columnIndex = cursor.getColumnIndex(PhoneLookup.LABEL);
+ if (columnIndex != -1) {
+ int typeColumnIndex = cursor.getColumnIndex(PhoneLookup.TYPE);
+ if (typeColumnIndex != -1) {
+ info.numberType = cursor.getInt(typeColumnIndex);
+ info.numberLabel = cursor.getString(columnIndex);
+ info.phoneLabel =
+ Phone.getTypeLabel(context.getResources(), info.numberType, info.numberLabel)
+ .toString();
+ }
+ }
+
+ // cache the lookup key for later use to create lookup URIs
+ columnIndex = cursor.getColumnIndex(PhoneLookup.LOOKUP_KEY);
+ if (columnIndex != -1) {
+ info.lookupKeyOrNull = cursor.getString(columnIndex);
+ }
+
+ // Look for the person_id.
+ columnIndex = getColumnIndexForPersonId(contactRef, cursor);
+ if (columnIndex != -1) {
+ contactId = cursor.getLong(columnIndex);
+ // QuickContacts in M doesn't support enterprise contact id
+ if (contactId != 0
+ && (VERSION.SDK_INT >= VERSION_CODES.N
+ || !Contacts.isEnterpriseContactId(contactId))) {
+ info.contactIdOrZero = contactId;
+ Log.v(TAG, "==> got info.contactIdOrZero: " + info.contactIdOrZero);
+ }
+ } else {
+ // No valid columnIndex, so we can't look up person_id.
+ Log.v(TAG, "Couldn't find contactId column for " + contactRef);
+ // Watch out: this means that anything that depends on
+ // person_id will be broken (like contact photo lookups in
+ // the in-call UI, for example.)
+ }
+
+ // Display photo URI.
+ columnIndex = cursor.getColumnIndex(PhoneLookup.PHOTO_URI);
+ if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
+ info.contactDisplayPhotoUri = Uri.parse(cursor.getString(columnIndex));
+ } else {
+ info.contactDisplayPhotoUri = null;
+ }
+
+ // look for the custom ringtone, create from the string stored
+ // in the database.
+ columnIndex = cursor.getColumnIndex(PhoneLookup.CUSTOM_RINGTONE);
+ if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
+ if (TextUtils.isEmpty(cursor.getString(columnIndex))) {
+ // make it consistent with frameworks/base/.../CallerInfo.java
+ info.contactRingtoneUri = Uri.EMPTY;
+ } else {
+ info.contactRingtoneUri = Uri.parse(cursor.getString(columnIndex));
+ }
+ } else {
+ info.contactRingtoneUri = null;
+ }
+
+ // look for the send to voicemail flag, set it to true only
+ // under certain circumstances.
+ columnIndex = cursor.getColumnIndex(PhoneLookup.SEND_TO_VOICEMAIL);
+ info.shouldSendToVoicemail = (columnIndex != -1) && ((cursor.getInt(columnIndex)) == 1);
+ info.contactExists = true;
+
+ // Determine userType by directoryId and contactId
+ final String directory =
+ contactRef == null
+ ? null
+ : contactRef.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
+ Long directoryId = null;
+ if (directory != null) {
+ try {
+ directoryId = Long.parseLong(directory);
+ } catch (NumberFormatException e) {
+ // do nothing
+ }
+ }
+ info.userType = ContactsUtils.determineUserType(directoryId, contactId);
+
+ info.nameAlternative =
+ ContactInfoHelper.lookUpDisplayNameAlternative(
+ context, info.lookupKeyOrNull, info.userType, directoryId);
+ }
+ cursor.close();
+ }
+
+ info.needUpdate = false;
+ info.name = normalize(info.name);
+ info.contactRefUri = contactRef;
+
+ return info;
+ }
+
+ /**
+ * getCallerInfo given a URI, look up in the call-log database for the uri unique key.
+ *
+ * @param context the context used to get the ContentResolver
+ * @param contactRef the URI used to lookup caller id
+ * @return the CallerInfo which contains the caller id for the given number. The returned
+ * CallerInfo is null if no number is supplied.
+ */
+ private static CallerInfo getCallerInfo(Context context, Uri contactRef) {
+
+ return getCallerInfo(
+ context,
+ contactRef,
+ context.getContentResolver().query(contactRef, null, null, null, null));
+ }
+
+ /**
+ * Performs another lookup if previous lookup fails and it's a SIP call and the peer's username is
+ * all numeric. Look up the username as it could be a PSTN number in the contact database.
+ *
+ * @param context the query context
+ * @param number the original phone number, could be a SIP URI
+ * @param previousResult the result of previous lookup
+ * @return previousResult if it's not the case
+ */
+ static CallerInfo doSecondaryLookupIfNecessary(
+ Context context, String number, CallerInfo previousResult) {
+ if (!previousResult.contactExists && PhoneNumberHelper.isUriNumber(number)) {
+ String username = PhoneNumberHelper.getUsernameFromUriNumber(number);
+ if (PhoneNumberUtils.isGlobalPhoneNumber(username)) {
+ previousResult =
+ getCallerInfo(
+ context,
+ Uri.withAppendedPath(
+ PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, Uri.encode(username)));
+ }
+ }
+ return previousResult;
+ }
+
+ // Accessors
+
+ private static String normalize(String s) {
+ if (s == null || s.length() > 0) {
+ return s;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the column index to use to find the "person_id" field in the specified cursor, based on
+ * the contact URI that was originally queried.
+ *
+ * <p>This is a helper function for the getCallerInfo() method that takes a Cursor. Looking up the
+ * person_id is nontrivial (compared to all the other CallerInfo fields) since the column we need
+ * to use depends on what query we originally ran.
+ *
+ * <p>Watch out: be sure to not do any database access in this method, since it's run from the UI
+ * thread (see comments below for more info.)
+ *
+ * @return the columnIndex to use (with cursor.getLong()) to get the person_id, or -1 if we
+ * couldn't figure out what colum to use.
+ * <p>TODO: Add a unittest for this method. (This is a little tricky to test, since we'll need
+ * a live contacts database to test against, preloaded with at least some phone numbers and
+ * SIP addresses. And we'll probably have to hardcode the column indexes we expect, so the
+ * test might break whenever the contacts schema changes. But we can at least make sure we
+ * handle all the URI patterns we claim to, and that the mime types match what we expect...)
+ */
+ private static int getColumnIndexForPersonId(Uri contactRef, Cursor cursor) {
+ // TODO: This is pretty ugly now, see bug 2269240 for
+ // more details. The column to use depends upon the type of URL:
+ // - content://com.android.contacts/data/phones ==> use the "contact_id" column
+ // - content://com.android.contacts/phone_lookup ==> use the "_ID" column
+ // - content://com.android.contacts/data ==> use the "contact_id" column
+ // If it's none of the above, we leave columnIndex=-1 which means
+ // that the person_id field will be left unset.
+ //
+ // The logic here *used* to be based on the mime type of contactRef
+ // (for example Phone.CONTENT_ITEM_TYPE would tell us to use the
+ // RawContacts.CONTACT_ID column). But looking up the mime type requires
+ // a call to context.getContentResolver().getType(contactRef), which
+ // isn't safe to do from the UI thread since it can cause an ANR if
+ // the contacts provider is slow or blocked (like during a sync.)
+ //
+ // So instead, figure out the column to use for person_id by just
+ // looking at the URI itself.
+
+ Log.v(TAG, "- getColumnIndexForPersonId: contactRef URI = '" + contactRef + "'...");
+ // Warning: Do not enable the following logging (due to ANR risk.)
+ // if (VDBG) Rlog.v(TAG, "- MIME type: "
+ // + context.getContentResolver().getType(contactRef));
+
+ String url = contactRef.toString();
+ String columnName = null;
+ if (url.startsWith("content://com.android.contacts/data/phones")) {
+ // Direct lookup in the Phone table.
+ // MIME type: Phone.CONTENT_ITEM_TYPE (= "vnd.android.cursor.item/phone_v2")
+ Log.v(TAG, "'data/phones' URI; using RawContacts.CONTACT_ID");
+ columnName = RawContacts.CONTACT_ID;
+ } else if (url.startsWith("content://com.android.contacts/data")) {
+ // Direct lookup in the Data table.
+ // MIME type: Data.CONTENT_TYPE (= "vnd.android.cursor.dir/data")
+ Log.v(TAG, "'data' URI; using Data.CONTACT_ID");
+ // (Note Data.CONTACT_ID and RawContacts.CONTACT_ID are equivalent.)
+ columnName = Data.CONTACT_ID;
+ } else if (url.startsWith("content://com.android.contacts/phone_lookup")) {
+ // Lookup in the PhoneLookup table, which provides "fuzzy matching"
+ // for phone numbers.
+ // MIME type: PhoneLookup.CONTENT_TYPE (= "vnd.android.cursor.dir/phone_lookup")
+ Log.v(TAG, "'phone_lookup' URI; using PhoneLookup._ID");
+ columnName = PhoneLookupUtil.getContactIdColumnNameForUri(contactRef);
+ } else {
+ Log.v(TAG, "Unexpected prefix for contactRef '" + url + "'");
+ }
+ int columnIndex = (columnName != null) ? cursor.getColumnIndex(columnName) : -1;
+ Log.v(
+ TAG,
+ "==> Using column '"
+ + columnName
+ + "' (columnIndex = "
+ + columnIndex
+ + ") for person_id lookup...");
+ return columnIndex;
+ }
+
+ /** @return true if the caller info is an emergency number. */
+ public boolean isEmergencyNumber() {
+ return mIsEmergency;
+ }
+
+ /** @return true if the caller info is a voicemail number. */
+ public boolean isVoiceMailNumber() {
+ return mIsVoiceMail;
+ }
+
+ /**
+ * Mark this CallerInfo as an emergency call.
+ *
+ * @param context To lookup the localized 'Emergency Number' string.
+ * @return this instance.
+ */
+ /* package */ CallerInfo markAsEmergency(Context context) {
+ name = context.getString(R.string.emergency_call_dialog_number_for_display);
+ phoneNumber = null;
+
+ photoResource = R.drawable.img_phone;
+ mIsEmergency = true;
+ return this;
+ }
+
+ /**
+ * Mark this CallerInfo as a voicemail call. The voicemail label is obtained from the telephony
+ * manager. Caller must hold the READ_PHONE_STATE permission otherwise the phoneNumber will be set
+ * to null.
+ *
+ * @return this instance.
+ */
+ /* package */ CallerInfo markAsVoiceMail(Context context) {
+ mIsVoiceMail = true;
+
+ try {
+ // For voicemail calls, we display the voice mail tag
+ // instead of the real phone number in the "number"
+ // field.
+ name = TelephonyManagerUtils.getVoiceMailAlphaTag(context);
+ phoneNumber = null;
+ } catch (SecurityException se) {
+ // Should never happen: if this process does not have
+ // permission to retrieve VM tag, it should not have
+ // permission to retrieve VM number and would not call
+ // this method.
+ // Leave phoneNumber untouched.
+ Log.e(TAG, "Cannot access VoiceMail.", se);
+ }
+ // TODO: There is no voicemail picture?
+ // FIXME: FIND ANOTHER ICON
+ // photoResource = android.R.drawable.badge_voicemail;
+ return this;
+ }
+
+ /**
+ * Updates this CallerInfo's geoDescription field, based on the raw phone number in the
+ * phoneNumber field.
+ *
+ * <p>(Note that the various getCallerInfo() methods do *not* set the geoDescription
+ * automatically; you need to call this method explicitly to get it.)
+ *
+ * @param context the context used to look up the current locale / country
+ * @param fallbackNumber if this CallerInfo's phoneNumber field is empty, this specifies a
+ * fallback number to use instead.
+ */
+ public void updateGeoDescription(Context context, String fallbackNumber) {
+ String number = TextUtils.isEmpty(phoneNumber) ? fallbackNumber : phoneNumber;
+ geoDescription = PhoneNumberHelper.getGeoDescription(context, number);
+ }
+
+ /** @return a string debug representation of this instance. */
+ @Override
+ public String toString() {
+ // Warning: never check in this file with VERBOSE_DEBUG = true
+ // because that will result in PII in the system log.
+ final boolean VERBOSE_DEBUG = false;
+
+ if (VERBOSE_DEBUG) {
+ return new StringBuilder(384)
+ .append(super.toString() + " { ")
+ .append("\nname: " + name)
+ .append("\nphoneNumber: " + phoneNumber)
+ .append("\nnormalizedNumber: " + normalizedNumber)
+ .append("\forwardingNumber: " + forwardingNumber)
+ .append("\ngeoDescription: " + geoDescription)
+ .append("\ncnapName: " + cnapName)
+ .append("\nnumberPresentation: " + numberPresentation)
+ .append("\nnamePresentation: " + namePresentation)
+ .append("\ncontactExists: " + contactExists)
+ .append("\nphoneLabel: " + phoneLabel)
+ .append("\nnumberType: " + numberType)
+ .append("\nnumberLabel: " + numberLabel)
+ .append("\nphotoResource: " + photoResource)
+ .append("\ncontactIdOrZero: " + contactIdOrZero)
+ .append("\nneedUpdate: " + needUpdate)
+ .append("\ncontactRefUri: " + contactRefUri)
+ .append("\ncontactRingtoneUri: " + contactRingtoneUri)
+ .append("\ncontactDisplayPhotoUri: " + contactDisplayPhotoUri)
+ .append("\nshouldSendToVoicemail: " + shouldSendToVoicemail)
+ .append("\ncachedPhoto: " + cachedPhoto)
+ .append("\nisCachedPhotoCurrent: " + isCachedPhotoCurrent)
+ .append("\nemergency: " + mIsEmergency)
+ .append("\nvoicemail: " + mIsVoiceMail)
+ .append("\nuserType: " + userType)
+ .append(" }")
+ .toString();
+ } else {
+ return new StringBuilder(128)
+ .append(super.toString() + " { ")
+ .append("name " + ((name == null) ? "null" : "non-null"))
+ .append(", phoneNumber " + ((phoneNumber == null) ? "null" : "non-null"))
+ .append(" }")
+ .toString();
+ }
+ }
+}
diff --git a/java/com/android/incallui/CallerInfoAsyncQuery.java b/java/com/android/incallui/CallerInfoAsyncQuery.java
new file mode 100644
index 000000000..f8d7ac65a
--- /dev/null
+++ b/java/com/android/incallui/CallerInfoAsyncQuery.java
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2006 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.incallui;
+
+import android.Manifest;
+import android.annotation.TargetApi;
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Directory;
+import android.support.annotation.MainThread;
+import android.support.annotation.RequiresPermission;
+import android.support.annotation.WorkerThread;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import com.android.contacts.common.compat.DirectoryCompat;
+import com.android.dialer.phonenumbercache.CachedNumberLookupService;
+import com.android.dialer.phonenumbercache.CachedNumberLookupService.CachedContactInfo;
+import com.android.dialer.phonenumbercache.ContactInfoHelper;
+import com.android.dialer.phonenumbercache.PhoneNumberCache;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Helper class to make it easier to run asynchronous caller-id lookup queries.
+ *
+ * @see CallerInfo
+ */
+@TargetApi(VERSION_CODES.M)
+public class CallerInfoAsyncQuery {
+
+ /** Interface for a CallerInfoAsyncQueryHandler result return. */
+ public interface OnQueryCompleteListener {
+
+ /** Called when the query is complete. */
+ @MainThread
+ void onQueryComplete(int token, Object cookie, CallerInfo ci);
+
+ /** Called when data is loaded. Must be called in worker thread. */
+ @WorkerThread
+ void onDataLoaded(int token, Object cookie, CallerInfo ci);
+ }
+
+ private static final boolean DBG = false;
+ private static final String LOG_TAG = "CallerInfoAsyncQuery";
+
+ private static final int EVENT_NEW_QUERY = 1;
+ private static final int EVENT_ADD_LISTENER = 2;
+ private static final int EVENT_EMERGENCY_NUMBER = 3;
+ private static final int EVENT_VOICEMAIL_NUMBER = 4;
+ // If the CallerInfo query finds no contacts, should we use the
+ // PhoneNumberOfflineGeocoder to look up a "geo description"?
+ // (TODO: This could become a flag in config.xml if it ever needs to be
+ // configured on a per-product basis.)
+ private static final boolean ENABLE_UNKNOWN_NUMBER_GEO_DESCRIPTION = true;
+ /* Directory lookup related code - START */
+ private static final String[] DIRECTORY_PROJECTION = new String[] {Directory._ID};
+
+ /** Private constructor for factory methods. */
+ private CallerInfoAsyncQuery() {}
+
+ @RequiresPermission(Manifest.permission.READ_CONTACTS)
+ public static void startQuery(
+ final int token,
+ final Context context,
+ final CallerInfo info,
+ final OnQueryCompleteListener listener,
+ final Object cookie) {
+ Log.d(LOG_TAG, "##### CallerInfoAsyncQuery startContactProviderQuery()... #####");
+ Log.d(LOG_TAG, "- number: " + info.phoneNumber);
+ Log.d(LOG_TAG, "- cookie: " + cookie);
+
+ OnQueryCompleteListener contactsProviderQueryCompleteListener =
+ new OnQueryCompleteListener() {
+ @Override
+ public void onQueryComplete(int token, Object cookie, CallerInfo ci) {
+ Log.d(LOG_TAG, "contactsProviderQueryCompleteListener done");
+ // If there are no other directory queries, make sure that the listener is
+ // notified of this result. see b/27621628
+ if ((ci != null && ci.contactExists)
+ || !startOtherDirectoriesQuery(token, context, info, listener, cookie)) {
+ if (listener != null && ci != null) {
+ listener.onQueryComplete(token, cookie, ci);
+ }
+ }
+ }
+
+ @Override
+ public void onDataLoaded(int token, Object cookie, CallerInfo ci) {
+ listener.onDataLoaded(token, cookie, ci);
+ }
+ };
+ startDefaultDirectoryQuery(token, context, info, contactsProviderQueryCompleteListener, cookie);
+ }
+
+ // Private methods
+ private static void startDefaultDirectoryQuery(
+ int token,
+ Context context,
+ CallerInfo info,
+ OnQueryCompleteListener listener,
+ Object cookie) {
+ // Construct the URI object and query params, and start the query.
+ Uri uri = ContactInfoHelper.getContactInfoLookupUri(info.phoneNumber);
+ startQueryInternal(token, context, info, listener, cookie, uri);
+ }
+
+ /**
+ * Factory method to start the query based on a CallerInfo object.
+ *
+ * <p>Note: if the number contains an "@" character we treat it as a SIP address, and look it up
+ * directly in the Data table rather than using the PhoneLookup table. TODO: But eventually we
+ * should expose two separate methods, one for numbers and one for SIP addresses, and then have
+ * PhoneUtils.startGetCallerInfo() decide which one to call based on the phone type of the
+ * incoming connection.
+ */
+ private static void startQueryInternal(
+ int token,
+ Context context,
+ CallerInfo info,
+ OnQueryCompleteListener listener,
+ Object cookie,
+ Uri contactRef) {
+ if (DBG) {
+ Log.d(LOG_TAG, "==> contactRef: " + sanitizeUriToString(contactRef));
+ }
+
+ if ((context == null) || (contactRef == null)) {
+ throw new QueryPoolException("Bad context or query uri.");
+ }
+ CallerInfoAsyncQueryHandler handler = new CallerInfoAsyncQueryHandler(context, contactRef);
+
+ //create cookieWrapper, start query
+ CookieWrapper cw = new CookieWrapper();
+ cw.listener = listener;
+ cw.cookie = cookie;
+ cw.number = info.phoneNumber;
+
+ // check to see if these are recognized numbers, and use shortcuts if we can.
+ if (PhoneNumberUtils.isLocalEmergencyNumber(context, info.phoneNumber)) {
+ cw.event = EVENT_EMERGENCY_NUMBER;
+ } else if (info.isVoiceMailNumber()) {
+ cw.event = EVENT_VOICEMAIL_NUMBER;
+ } else {
+ cw.event = EVENT_NEW_QUERY;
+ }
+
+ String[] proejection = CallerInfo.getDefaultPhoneLookupProjection(contactRef);
+ handler.startQuery(
+ token,
+ cw, // cookie
+ contactRef, // uri
+ proejection, // projection
+ null, // selection
+ null, // selectionArgs
+ null); // orderBy
+ }
+
+ // Return value indicates if listener was notified.
+ private static boolean startOtherDirectoriesQuery(
+ int token,
+ Context context,
+ CallerInfo info,
+ OnQueryCompleteListener listener,
+ Object cookie) {
+ long[] directoryIds = getDirectoryIds(context);
+ int size = directoryIds.length;
+ if (size == 0) {
+ return false;
+ }
+
+ DirectoryQueryCompleteListenerFactory listenerFactory =
+ new DirectoryQueryCompleteListenerFactory(context, size, listener);
+
+ // The current implementation of multiple async query runs in single handler thread
+ // in AsyncQueryHandler.
+ // intermediateListener.onQueryComplete is also called from the same caller thread.
+ // TODO(b/26019872): use thread pool instead of single thread.
+ for (int i = 0; i < size; i++) {
+ long directoryId = directoryIds[i];
+ Uri uri = ContactInfoHelper.getContactInfoLookupUri(info.phoneNumber, directoryId);
+ if (DBG) {
+ Log.d(LOG_TAG, "directoryId: " + directoryId + " uri: " + uri);
+ }
+ OnQueryCompleteListener intermediateListener = listenerFactory.newListener(directoryId);
+ startQueryInternal(token, context, info, intermediateListener, cookie, uri);
+ }
+ return true;
+ }
+
+ private static long[] getDirectoryIds(Context context) {
+ ArrayList<Long> results = new ArrayList<>();
+
+ Uri uri = Directory.CONTENT_URI;
+ if (VERSION.SDK_INT >= VERSION_CODES.N) {
+ uri = Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, "directories_enterprise");
+ }
+
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(uri, DIRECTORY_PROJECTION, null, null, null);
+ addDirectoryIdsFromCursor(cursor, results);
+
+ long[] result = new long[results.size()];
+ for (int i = 0; i < results.size(); i++) {
+ result[i] = results.get(i);
+ }
+ return result;
+ }
+
+ private static void addDirectoryIdsFromCursor(Cursor cursor, ArrayList<Long> results) {
+ if (cursor != null) {
+ int idIndex = cursor.getColumnIndex(Directory._ID);
+ while (cursor.moveToNext()) {
+ long id = cursor.getLong(idIndex);
+ if (DirectoryCompat.isRemoteDirectoryId(id)) {
+ results.add(id);
+ }
+ }
+ cursor.close();
+ }
+ }
+
+ private static String sanitizeUriToString(Uri uri) {
+ if (uri != null) {
+ String uriString = uri.toString();
+ int indexOfLastSlash = uriString.lastIndexOf('/');
+ if (indexOfLastSlash > 0) {
+ return uriString.substring(0, indexOfLastSlash) + "/xxxxxxx";
+ } else {
+ return uriString;
+ }
+ } else {
+ return "";
+ }
+ }
+
+ /** Wrap the cookie from the WorkerArgs with additional information needed by our classes. */
+ private static final class CookieWrapper {
+
+ public OnQueryCompleteListener listener;
+ public Object cookie;
+ public int event;
+ public String number;
+ }
+ /* Directory lookup related code - END */
+
+ /** Simple exception used to communicate problems with the query pool. */
+ public static class QueryPoolException extends SQLException {
+
+ public QueryPoolException(String error) {
+ super(error);
+ }
+ }
+
+ private static final class DirectoryQueryCompleteListenerFactory {
+
+ private final OnQueryCompleteListener mListener;
+ private final Context mContext;
+ // Make sure listener to be called once and only once
+ private int mCount;
+ private boolean mIsListenerCalled;
+
+ DirectoryQueryCompleteListenerFactory(
+ Context context, int size, OnQueryCompleteListener listener) {
+ mCount = size;
+ mListener = listener;
+ mIsListenerCalled = false;
+ mContext = context;
+ }
+
+ private void onDirectoryQueryComplete(
+ int token, Object cookie, CallerInfo ci, long directoryId) {
+ boolean shouldCallListener = false;
+ synchronized (this) {
+ mCount = mCount - 1;
+ if (!mIsListenerCalled && (ci.contactExists || mCount == 0)) {
+ mIsListenerCalled = true;
+ shouldCallListener = true;
+ }
+ }
+
+ // Don't call callback in synchronized block because mListener.onQueryComplete may
+ // take long time to complete
+ if (shouldCallListener && mListener != null) {
+ addCallerInfoIntoCache(ci, directoryId);
+ mListener.onQueryComplete(token, cookie, ci);
+ }
+ }
+
+ private void addCallerInfoIntoCache(CallerInfo ci, long directoryId) {
+ CachedNumberLookupService cachedNumberLookupService =
+ PhoneNumberCache.get(mContext).getCachedNumberLookupService();
+ if (ci.contactExists && cachedNumberLookupService != null) {
+ // 1. Cache caller info
+ CachedContactInfo cachedContactInfo =
+ CallerInfoUtils.buildCachedContactInfo(cachedNumberLookupService, ci);
+ String directoryLabel = mContext.getString(R.string.directory_search_label);
+ cachedContactInfo.setDirectorySource(directoryLabel, directoryId);
+ cachedNumberLookupService.addContact(mContext, cachedContactInfo);
+
+ // 2. Cache photo
+ if (ci.contactDisplayPhotoUri != null && ci.normalizedNumber != null) {
+ try (InputStream in =
+ mContext.getContentResolver().openInputStream(ci.contactDisplayPhotoUri)) {
+ if (in != null) {
+ cachedNumberLookupService.addPhoto(mContext, ci.normalizedNumber, in);
+ }
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "failed to fetch directory contact photo", e);
+ }
+ }
+ }
+ }
+
+ public OnQueryCompleteListener newListener(long directoryId) {
+ return new DirectoryQueryCompleteListener(directoryId);
+ }
+
+ private class DirectoryQueryCompleteListener implements OnQueryCompleteListener {
+
+ private final long mDirectoryId;
+
+ DirectoryQueryCompleteListener(long directoryId) {
+ mDirectoryId = directoryId;
+ }
+
+ @Override
+ public void onDataLoaded(int token, Object cookie, CallerInfo ci) {
+ mListener.onDataLoaded(token, cookie, ci);
+ }
+
+ @Override
+ public void onQueryComplete(int token, Object cookie, CallerInfo ci) {
+ onDirectoryQueryComplete(token, cookie, ci, mDirectoryId);
+ }
+ }
+ }
+
+ /** Our own implementation of the AsyncQueryHandler. */
+ private static class CallerInfoAsyncQueryHandler extends AsyncQueryHandler {
+
+ /**
+ * The information relevant to each CallerInfo query. Each query may have multiple listeners, so
+ * each AsyncCursorInfo is associated with 2 or more CookieWrapper objects in the queue (one
+ * with a new query event, and one with a end event, with 0 or more additional listeners in
+ * between).
+ */
+ private Context mQueryContext;
+
+ private Uri mQueryUri;
+ private CallerInfo mCallerInfo;
+
+ /** Asynchronous query handler class for the contact / callerinfo object. */
+ private CallerInfoAsyncQueryHandler(Context context, Uri contactRef) {
+ super(context.getContentResolver());
+ this.mQueryContext = context;
+ this.mQueryUri = contactRef;
+ }
+
+ @Override
+ public void startQuery(
+ int token,
+ Object cookie,
+ Uri uri,
+ String[] projection,
+ String selection,
+ String[] selectionArgs,
+ String orderBy) {
+ if (DBG) {
+ // Show stack trace with the arguments.
+ Log.d(
+ LOG_TAG,
+ "InCall: startQuery: url="
+ + uri
+ + " projection=["
+ + Arrays.toString(projection)
+ + "]"
+ + " selection="
+ + selection
+ + " "
+ + " args=["
+ + Arrays.toString(selectionArgs)
+ + "]",
+ new RuntimeException("STACKTRACE"));
+ }
+ super.startQuery(token, cookie, uri, projection, selection, selectionArgs, orderBy);
+ }
+
+ @Override
+ protected Handler createHandler(Looper looper) {
+ return new CallerInfoWorkerHandler(looper);
+ }
+
+ /**
+ * Overrides onQueryComplete from AsyncQueryHandler.
+ *
+ * <p>This method takes into account the state of this class; we construct the CallerInfo object
+ * only once for each set of listeners. When the query thread has done its work and calls this
+ * method, we inform the remaining listeners in the queue, until we're out of listeners. Once we
+ * get the message indicating that we should expect no new listeners for this CallerInfo object,
+ * we release the AsyncCursorInfo back into the pool.
+ */
+ @Override
+ protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ Log.d(this, "##### onQueryComplete() ##### query complete for token: " + token);
+
+ CookieWrapper cw = (CookieWrapper) cookie;
+
+ if (cw.listener != null) {
+ Log.d(
+ this,
+ "notifying listener: "
+ + cw.listener.getClass().toString()
+ + " for token: "
+ + token
+ + mCallerInfo);
+ cw.listener.onQueryComplete(token, cw.cookie, mCallerInfo);
+ }
+ mQueryContext = null;
+ mQueryUri = null;
+ mCallerInfo = null;
+ }
+
+ protected void updateData(int token, Object cookie, Cursor cursor) {
+ try {
+ Log.d(this, "##### updateData() ##### for token: " + token);
+
+ //get the cookie and notify the listener.
+ CookieWrapper cw = (CookieWrapper) cookie;
+ if (cw == null) {
+ // Normally, this should never be the case for calls originating
+ // from within this code.
+ // However, if there is any code that calls this method, we should
+ // check the parameters to make sure they're viable.
+ Log.d(this, "Cookie is null, ignoring onQueryComplete() request.");
+ return;
+ }
+
+ // check the token and if needed, create the callerinfo object.
+ if (mCallerInfo == null) {
+ if ((mQueryContext == null) || (mQueryUri == null)) {
+ throw new QueryPoolException(
+ "Bad context or query uri, or CallerInfoAsyncQuery already released.");
+ }
+
+ // adjust the callerInfo data as needed, and only if it was set from the
+ // initial query request.
+ // Change the callerInfo number ONLY if it is an emergency number or the
+ // voicemail number, and adjust other data (including photoResource)
+ // accordingly.
+ if (cw.event == EVENT_EMERGENCY_NUMBER) {
+ // Note we're setting the phone number here (refer to javadoc
+ // comments at the top of CallerInfo class).
+ mCallerInfo = new CallerInfo().markAsEmergency(mQueryContext);
+ } else if (cw.event == EVENT_VOICEMAIL_NUMBER) {
+ mCallerInfo = new CallerInfo().markAsVoiceMail(mQueryContext);
+ } else {
+ mCallerInfo = CallerInfo.getCallerInfo(mQueryContext, mQueryUri, cursor);
+ Log.d(this, "==> Got mCallerInfo: " + mCallerInfo);
+
+ CallerInfo newCallerInfo =
+ CallerInfo.doSecondaryLookupIfNecessary(mQueryContext, cw.number, mCallerInfo);
+ if (newCallerInfo != mCallerInfo) {
+ mCallerInfo = newCallerInfo;
+ Log.d(this, "#####async contact look up with numeric username" + mCallerInfo);
+ }
+
+ // Final step: look up the geocoded description.
+ if (ENABLE_UNKNOWN_NUMBER_GEO_DESCRIPTION) {
+ // Note we do this only if we *don't* have a valid name (i.e. if
+ // no contacts matched the phone number of the incoming call),
+ // since that's the only case where the incoming-call UI cares
+ // about this field.
+ //
+ // (TODO: But if we ever want the UI to show the geoDescription
+ // even when we *do* match a contact, we'll need to either call
+ // updateGeoDescription() unconditionally here, or possibly add a
+ // new parameter to CallerInfoAsyncQuery.startQuery() to force
+ // the geoDescription field to be populated.)
+
+ if (TextUtils.isEmpty(mCallerInfo.name)) {
+ // Actually when no contacts match the incoming phone number,
+ // the CallerInfo object is totally blank here (i.e. no name
+ // *or* phoneNumber). So we need to pass in cw.number as
+ // a fallback number.
+ mCallerInfo.updateGeoDescription(mQueryContext, cw.number);
+ }
+ }
+
+ // Use the number entered by the user for display.
+ if (!TextUtils.isEmpty(cw.number)) {
+ mCallerInfo.phoneNumber = cw.number;
+ }
+ }
+
+ Log.d(this, "constructing CallerInfo object for token: " + token);
+
+ if (cw.listener != null) {
+ cw.listener.onDataLoaded(token, cw.cookie, mCallerInfo);
+ }
+ }
+
+ } finally {
+ // The cursor may have been closed in CallerInfo.getCallerInfo()
+ if (cursor != null && !cursor.isClosed()) {
+ cursor.close();
+ }
+ }
+ }
+
+ /**
+ * Our own query worker thread.
+ *
+ * <p>This thread handles the messages enqueued in the looper. The normal sequence of events is
+ * that a new query shows up in the looper queue, followed by 0 or more add listener requests,
+ * and then an end request. Of course, these requests can be interlaced with requests from other
+ * tokens, but is irrelevant to this handler since the handler has no state.
+ *
+ * <p>Note that we depend on the queue to keep things in order; in other words, the looper queue
+ * must be FIFO with respect to input from the synchronous startQuery calls and output to this
+ * handleMessage call.
+ *
+ * <p>This use of the queue is required because CallerInfo objects may be accessed multiple
+ * times before the query is complete. All accesses (listeners) must be queued up and informed
+ * in order when the query is complete.
+ */
+ protected class CallerInfoWorkerHandler extends WorkerHandler {
+
+ public CallerInfoWorkerHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ WorkerArgs args = (WorkerArgs) msg.obj;
+ CookieWrapper cw = (CookieWrapper) args.cookie;
+
+ if (cw == null) {
+ // Normally, this should never be the case for calls originating
+ // from within this code.
+ // However, if there is any code that this Handler calls (such as in
+ // super.handleMessage) that DOES place unexpected messages on the
+ // queue, then we need pass these messages on.
+ Log.d(
+ this,
+ "Unexpected command (CookieWrapper is null): "
+ + msg.what
+ + " ignored by CallerInfoWorkerHandler, passing onto parent.");
+
+ super.handleMessage(msg);
+ } else {
+ Log.d(
+ this,
+ "Processing event: "
+ + cw.event
+ + " token (arg1): "
+ + msg.arg1
+ + " command: "
+ + msg.what
+ + " query URI: "
+ + sanitizeUriToString(args.uri));
+
+ switch (cw.event) {
+ case EVENT_NEW_QUERY:
+ final ContentResolver resolver = mQueryContext.getContentResolver();
+
+ // This should never happen.
+ if (resolver == null) {
+ Log.e(this, "Content Resolver is null!");
+ return;
+ }
+ //start the sql command.
+ Cursor cursor;
+ try {
+ cursor =
+ resolver.query(
+ args.uri,
+ args.projection,
+ args.selection,
+ args.selectionArgs,
+ args.orderBy);
+ // Calling getCount() causes the cursor window to be filled,
+ // which will make the first access on the main thread a lot faster.
+ if (cursor != null) {
+ cursor.getCount();
+ }
+ } catch (Exception e) {
+ Log.e(this, "Exception thrown during handling EVENT_ARG_QUERY", e);
+ cursor = null;
+ }
+
+ args.result = cursor;
+ updateData(msg.arg1, cw, cursor);
+ break;
+
+ // shortcuts to avoid query for recognized numbers.
+ case EVENT_EMERGENCY_NUMBER:
+ case EVENT_VOICEMAIL_NUMBER:
+ case EVENT_ADD_LISTENER:
+ updateData(msg.arg1, cw, (Cursor) args.result);
+ break;
+ default:
+ }
+ Message reply = args.handler.obtainMessage(msg.what);
+ reply.obj = args;
+ reply.arg1 = msg.arg1;
+
+ reply.sendToTarget();
+ }
+ }
+ }
+ }
+}
diff --git a/java/com/android/incallui/CallerInfoUtils.java b/java/com/android/incallui/CallerInfoUtils.java
new file mode 100644
index 000000000..9f57fba65
--- /dev/null
+++ b/java/com/android/incallui/CallerInfoUtils.java
@@ -0,0 +1,279 @@
+/*
+ * 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.incallui;
+
+import android.Manifest.permission;
+import android.content.Context;
+import android.content.Loader;
+import android.content.Loader.OnLoadCompleteListener;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.support.v4.content.ContextCompat;
+import android.telecom.PhoneAccount;
+import android.telecom.TelecomManager;
+import android.text.TextUtils;
+import com.android.contacts.common.model.Contact;
+import com.android.contacts.common.model.ContactLoader;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.phonenumbercache.CachedNumberLookupService;
+import com.android.dialer.phonenumbercache.CachedNumberLookupService.CachedContactInfo;
+import com.android.dialer.phonenumbercache.ContactInfo;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.android.dialer.telecom.TelecomUtil;
+import com.android.dialer.util.PermissionsUtil;
+import com.android.incallui.call.DialerCall;
+import java.util.Arrays;
+
+/** Utility methods for contact and caller info related functionality */
+public class CallerInfoUtils {
+
+ private static final String TAG = CallerInfoUtils.class.getSimpleName();
+
+ private static final int QUERY_TOKEN = -1;
+
+ public CallerInfoUtils() {}
+
+ /**
+ * This is called to get caller info for a call. This will return a CallerInfo object immediately
+ * based off information in the call, but more information is returned to the
+ * OnQueryCompleteListener (which contains information about the phone number label, user's name,
+ * etc).
+ */
+ public static CallerInfo getCallerInfoForCall(
+ Context context,
+ DialerCall call,
+ Object cookie,
+ CallerInfoAsyncQuery.OnQueryCompleteListener listener) {
+ CallerInfo info = buildCallerInfo(context, call);
+
+ // TODO: Have phoneapp send a Uri when it knows the contact that triggered this call.
+
+ if (info.numberPresentation == TelecomManager.PRESENTATION_ALLOWED) {
+ if (PermissionsUtil.hasContactsPermissions(context)) {
+ // Start the query with the number provided from the call.
+ LogUtil.d(
+ "CallerInfoUtils.getCallerInfoForCall",
+ "Actually starting CallerInfoAsyncQuery.startQuery()...");
+
+ //noinspection MissingPermission
+ CallerInfoAsyncQuery.startQuery(QUERY_TOKEN, context, info, listener, cookie);
+ } else {
+ LogUtil.w(
+ "CallerInfoUtils.getCallerInfoForCall",
+ "Dialer doesn't have permission to read contacts."
+ + " Not calling CallerInfoAsyncQuery.startQuery().");
+ }
+ }
+ return info;
+ }
+
+ public static CallerInfo buildCallerInfo(Context context, DialerCall call) {
+ CallerInfo info = new CallerInfo();
+
+ // Store CNAP information retrieved from the Connection (we want to do this
+ // here regardless of whether the number is empty or not).
+ info.cnapName = call.getCnapName();
+ info.name = info.cnapName;
+ info.numberPresentation = call.getNumberPresentation();
+ info.namePresentation = call.getCnapNamePresentation();
+ info.callSubject = call.getCallSubject();
+
+ String number = call.getNumber();
+ if (!TextUtils.isEmpty(number)) {
+ // Don't split it if it's a SIP number.
+ if (!PhoneNumberHelper.isUriNumber(number)) {
+ final String[] numbers = number.split("&");
+ number = numbers[0];
+ if (numbers.length > 1) {
+ info.forwardingNumber = numbers[1];
+ }
+ number = modifyForSpecialCnapCases(context, info, number, info.numberPresentation);
+ }
+ info.phoneNumber = number;
+ }
+
+ // Because the InCallUI is immediately launched before the call is connected, occasionally
+ // a voicemail call will be passed to InCallUI as a "voicemail:" URI without a number.
+ // This call should still be handled as a voicemail call.
+ if ((call.getHandle() != null
+ && PhoneAccount.SCHEME_VOICEMAIL.equals(call.getHandle().getScheme()))
+ || isVoiceMailNumber(context, call)) {
+ info.markAsVoiceMail(context);
+ }
+
+ ContactInfoCache.getInstance(context).maybeInsertCnapInformationIntoCache(context, call, info);
+
+ return info;
+ }
+
+ /**
+ * Creates a new {@link CachedContactInfo} from a {@link CallerInfo}
+ *
+ * @param lookupService the {@link CachedNumberLookupService} used to build a new {@link
+ * CachedContactInfo}
+ * @param {@link CallerInfo} object
+ * @return a CachedContactInfo object created from this CallerInfo
+ * @throws NullPointerException if lookupService or ci are null
+ */
+ public static CachedContactInfo buildCachedContactInfo(
+ CachedNumberLookupService lookupService, CallerInfo ci) {
+ ContactInfo info = new ContactInfo();
+ info.name = ci.name;
+ info.type = ci.numberType;
+ info.label = ci.phoneLabel;
+ info.number = ci.phoneNumber;
+ info.normalizedNumber = ci.normalizedNumber;
+ info.photoUri = ci.contactDisplayPhotoUri;
+ info.userType = ci.userType;
+
+ CachedContactInfo cacheInfo = lookupService.buildCachedContactInfo(info);
+ cacheInfo.setLookupKey(ci.lookupKeyOrNull);
+ return cacheInfo;
+ }
+
+ public static boolean isVoiceMailNumber(Context context, DialerCall call) {
+ if (ContextCompat.checkSelfPermission(context, permission.READ_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ return TelecomUtil.isVoicemailNumber(context, call.getAccountHandle(), call.getNumber());
+ }
+
+ /**
+ * Handles certain "corner cases" for CNAP. When we receive weird phone numbers from the network
+ * to indicate different number presentations, convert them to expected number and presentation
+ * values within the CallerInfo object.
+ *
+ * @param number number we use to verify if we are in a corner case
+ * @param presentation presentation value used to verify if we are in a corner case
+ * @return the new String that should be used for the phone number
+ */
+ /* package */
+ static String modifyForSpecialCnapCases(
+ Context context, CallerInfo ci, String number, int presentation) {
+ // Obviously we return number if ci == null, but still return number if
+ // number == null, because in these cases the correct string will still be
+ // displayed/logged after this function returns based on the presentation value.
+ if (ci == null || number == null) {
+ return number;
+ }
+
+ LogUtil.d(
+ "CallerInfoUtils.modifyForSpecialCnapCases",
+ "modifyForSpecialCnapCases: initially, number="
+ + toLogSafePhoneNumber(number)
+ + ", presentation="
+ + presentation
+ + " ci "
+ + ci);
+
+ // "ABSENT NUMBER" is a possible value we could get from the network as the
+ // phone number, so if this happens, change it to "Unknown" in the CallerInfo
+ // and fix the presentation to be the same.
+ final String[] absentNumberValues = context.getResources().getStringArray(R.array.absent_num);
+ if (Arrays.asList(absentNumberValues).contains(number)
+ && presentation == TelecomManager.PRESENTATION_ALLOWED) {
+ number = context.getString(R.string.unknown);
+ ci.numberPresentation = TelecomManager.PRESENTATION_UNKNOWN;
+ }
+
+ // Check for other special "corner cases" for CNAP and fix them similarly. Corner
+ // cases only apply if we received an allowed presentation from the network, so check
+ // if we think we have an allowed presentation, or if the CallerInfo presentation doesn't
+ // match the presentation passed in for verification (meaning we changed it previously
+ // because it's a corner case and we're being called from a different entry point).
+ if (ci.numberPresentation == TelecomManager.PRESENTATION_ALLOWED
+ || (ci.numberPresentation != presentation
+ && presentation == TelecomManager.PRESENTATION_ALLOWED)) {
+ // For all special strings, change number & numberPrentation.
+ if (isCnapSpecialCaseRestricted(number)) {
+ number = PhoneNumberHelper.getDisplayNameForRestrictedNumber(context).toString();
+ ci.numberPresentation = TelecomManager.PRESENTATION_RESTRICTED;
+ } else if (isCnapSpecialCaseUnknown(number)) {
+ number = context.getString(R.string.unknown);
+ ci.numberPresentation = TelecomManager.PRESENTATION_UNKNOWN;
+ }
+ LogUtil.d(
+ "CallerInfoUtils.modifyForSpecialCnapCases",
+ "SpecialCnap: number="
+ + toLogSafePhoneNumber(number)
+ + "; presentation now="
+ + ci.numberPresentation);
+ }
+ LogUtil.d(
+ "CallerInfoUtils.modifyForSpecialCnapCases",
+ "returning number string=" + toLogSafePhoneNumber(number));
+ return number;
+ }
+
+ private static boolean isCnapSpecialCaseRestricted(String n) {
+ return n.equals("PRIVATE") || n.equals("P") || n.equals("RES") || n.equals("PRIVATENUMBER");
+ }
+
+ private static boolean isCnapSpecialCaseUnknown(String n) {
+ return n.equals("UNAVAILABLE") || n.equals("UNKNOWN") || n.equals("UNA") || n.equals("U");
+ }
+
+ /* package */
+ static String toLogSafePhoneNumber(String number) {
+ // For unknown number, log empty string.
+ if (number == null) {
+ return "";
+ }
+
+ // Todo: Figure out an equivalent for VDBG
+ if (false) {
+ // When VDBG is true we emit PII.
+ return number;
+ }
+
+ // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare
+ // sanitized phone numbers.
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < number.length(); i++) {
+ char c = number.charAt(i);
+ if (c == '-' || c == '@' || c == '.' || c == '&') {
+ builder.append(c);
+ } else {
+ builder.append('x');
+ }
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Send a notification using a {@link ContactLoader} to inform the sync adapter that we are
+ * viewing a particular contact, so that it can download the high-res photo.
+ */
+ public static void sendViewNotification(Context context, Uri contactUri) {
+ final ContactLoader loader =
+ new ContactLoader(context, contactUri, true /* postViewNotification */);
+ loader.registerListener(
+ 0,
+ new OnLoadCompleteListener<Contact>() {
+ @Override
+ public void onLoadComplete(Loader<Contact> loader, Contact contact) {
+ try {
+ loader.reset();
+ } catch (RuntimeException e) {
+ LogUtil.e("CallerInfoUtils.onLoadComplete", "Error resetting loader", e);
+ }
+ }
+ });
+ loader.startLoading();
+ }
+}
diff --git a/java/com/android/incallui/ConferenceManagerFragment.java b/java/com/android/incallui/ConferenceManagerFragment.java
new file mode 100644
index 000000000..8696bb8ec
--- /dev/null
+++ b/java/com/android/incallui/ConferenceManagerFragment.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2013 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.incallui;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.ScreenEvent;
+import com.android.incallui.ConferenceManagerPresenter.ConferenceManagerUi;
+import com.android.incallui.baseui.BaseFragment;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import java.util.List;
+
+/** Fragment that allows the user to manage a conference call. */
+public class ConferenceManagerFragment
+ extends BaseFragment<ConferenceManagerPresenter, ConferenceManagerUi>
+ implements ConferenceManagerPresenter.ConferenceManagerUi {
+
+ private ListView mConferenceParticipantList;
+ private ContactPhotoManager mContactPhotoManager;
+ private ConferenceParticipantListAdapter mConferenceParticipantListAdapter;
+
+ @Override
+ public ConferenceManagerPresenter createPresenter() {
+ return new ConferenceManagerPresenter();
+ }
+
+ @Override
+ public ConferenceManagerPresenter.ConferenceManagerUi getUi() {
+ return this;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ Logger.get(getContext()).logScreenView(ScreenEvent.Type.CONFERENCE_MANAGEMENT, getActivity());
+ }
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ final View parent = inflater.inflate(R.layout.conference_manager_fragment, container, false);
+
+ mConferenceParticipantList = (ListView) parent.findViewById(R.id.participantList);
+ mContactPhotoManager = ContactPhotoManager.getInstance(getActivity().getApplicationContext());
+
+ return parent;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ final CallList calls = CallList.getInstance();
+ getPresenter().init(calls);
+ // Request focus on the list of participants for accessibility purposes. This ensures
+ // that once the list of participants is shown, the first participant is announced.
+ mConferenceParticipantList.requestFocus();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public boolean isFragmentVisible() {
+ return isVisible();
+ }
+
+ @Override
+ public void update(List<DialerCall> participants, boolean parentCanSeparate) {
+ if (mConferenceParticipantListAdapter == null) {
+ mConferenceParticipantListAdapter =
+ new ConferenceParticipantListAdapter(mConferenceParticipantList, mContactPhotoManager);
+
+ mConferenceParticipantList.setAdapter(mConferenceParticipantListAdapter);
+ }
+ mConferenceParticipantListAdapter.updateParticipants(participants, parentCanSeparate);
+ }
+
+ @Override
+ public void refreshCall(DialerCall call) {
+ mConferenceParticipantListAdapter.refreshCall(call);
+ }
+}
diff --git a/java/com/android/incallui/ConferenceManagerPresenter.java b/java/com/android/incallui/ConferenceManagerPresenter.java
new file mode 100644
index 000000000..226741dcd
--- /dev/null
+++ b/java/com/android/incallui/ConferenceManagerPresenter.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2013 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.incallui;
+
+import com.android.incallui.ConferenceManagerPresenter.ConferenceManagerUi;
+import com.android.incallui.InCallPresenter.InCallDetailsListener;
+import com.android.incallui.InCallPresenter.InCallState;
+import com.android.incallui.InCallPresenter.InCallStateListener;
+import com.android.incallui.InCallPresenter.IncomingCallListener;
+import com.android.incallui.baseui.Presenter;
+import com.android.incallui.baseui.Ui;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Logic for call buttons. */
+public class ConferenceManagerPresenter extends Presenter<ConferenceManagerUi>
+ implements InCallStateListener, InCallDetailsListener, IncomingCallListener {
+
+ @Override
+ public void onUiReady(ConferenceManagerUi ui) {
+ super.onUiReady(ui);
+
+ // register for call state changes last
+ InCallPresenter.getInstance().addListener(this);
+ InCallPresenter.getInstance().addIncomingCallListener(this);
+ }
+
+ @Override
+ public void onUiUnready(ConferenceManagerUi ui) {
+ super.onUiUnready(ui);
+
+ InCallPresenter.getInstance().removeListener(this);
+ InCallPresenter.getInstance().removeIncomingCallListener(this);
+ }
+
+ @Override
+ public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
+ if (getUi().isFragmentVisible()) {
+ Log.v(this, "onStateChange" + newState);
+ if (newState == InCallState.INCALL) {
+ final DialerCall call = callList.getActiveOrBackgroundCall();
+ if (call != null && call.isConferenceCall()) {
+ Log.v(
+ this, "Number of existing calls is " + String.valueOf(call.getChildCallIds().size()));
+ update(callList);
+ } else {
+ InCallPresenter.getInstance().showConferenceCallManager(false);
+ }
+ } else {
+ InCallPresenter.getInstance().showConferenceCallManager(false);
+ }
+ }
+ }
+
+ @Override
+ public void onDetailsChanged(DialerCall call, android.telecom.Call.Details details) {
+ boolean canDisconnect =
+ details.can(android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE);
+ boolean canSeparate =
+ details.can(android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE);
+
+ if (call.can(android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE)
+ != canDisconnect
+ || call.can(android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE)
+ != canSeparate) {
+ getUi().refreshCall(call);
+ }
+
+ if (!details.can(android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE)) {
+ InCallPresenter.getInstance().showConferenceCallManager(false);
+ }
+ }
+
+ @Override
+ public void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call) {
+ // When incoming call exists, set conference ui invisible.
+ if (getUi().isFragmentVisible()) {
+ Log.d(this, "onIncomingCall()... Conference ui is showing, hide it.");
+ InCallPresenter.getInstance().showConferenceCallManager(false);
+ }
+ }
+
+ public void init(CallList callList) {
+ update(callList);
+ }
+
+ /**
+ * Updates the conference participant adapter.
+ *
+ * @param callList The callList.
+ */
+ private void update(CallList callList) {
+ // callList is non null, but getActiveOrBackgroundCall() may return null
+ final DialerCall currentCall = callList.getActiveOrBackgroundCall();
+ if (currentCall == null) {
+ return;
+ }
+
+ ArrayList<DialerCall> calls = new ArrayList<>(currentCall.getChildCallIds().size());
+ for (String callerId : currentCall.getChildCallIds()) {
+ calls.add(callList.getCallById(callerId));
+ }
+
+ Log.d(this, "Number of calls is " + String.valueOf(calls.size()));
+
+ // Users can split out a call from the conference call if either the active call or the
+ // holding call is empty. If both are filled, users can not split out another call.
+ final boolean hasActiveCall = (callList.getActiveCall() != null);
+ final boolean hasHoldingCall = (callList.getBackgroundCall() != null);
+ boolean canSeparate = !(hasActiveCall && hasHoldingCall);
+
+ getUi().update(calls, canSeparate);
+ }
+
+ public interface ConferenceManagerUi extends Ui {
+
+ boolean isFragmentVisible();
+
+ void update(List<DialerCall> participants, boolean parentCanSeparate);
+
+ void refreshCall(DialerCall call);
+ }
+}
diff --git a/java/com/android/incallui/ConferenceParticipantListAdapter.java b/java/com/android/incallui/ConferenceParticipantListAdapter.java
new file mode 100644
index 000000000..72c0fcd20
--- /dev/null
+++ b/java/com/android/incallui/ConferenceParticipantListAdapter.java
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2014 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.incallui;
+
+import android.content.Context;
+import android.net.Uri;
+import android.support.annotation.Nullable;
+import android.support.v4.util.ArrayMap;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+import com.android.contacts.common.preference.ContactsPreferences;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.dialer.common.LogUtil;
+import com.android.incallui.ContactInfoCache.ContactCacheEntry;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/** Adapter for a ListView containing conference call participant information. */
+public class ConferenceParticipantListAdapter extends BaseAdapter {
+
+ /** The ListView containing the participant information. */
+ private final ListView mListView;
+ /** Hashmap to make accessing participant info by call Id faster. */
+ private final Map<String, ParticipantInfo> mParticipantsByCallId = new ArrayMap<>();
+ /** ContactsPreferences used to lookup displayName preferences */
+ @Nullable private final ContactsPreferences mContactsPreferences;
+ /** Contact photo manager to retrieve cached contact photo information. */
+ private final ContactPhotoManager mContactPhotoManager;
+ /** Listener used to handle tap of the "disconnect' button for a participant. */
+ private View.OnClickListener mDisconnectListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ DialerCall call = getCallFromView(view);
+ LogUtil.i(
+ "ConferenceParticipantListAdapter.mDisconnectListener.onClick", "call: " + call);
+ if (call != null) {
+ call.disconnect();
+ }
+ }
+ };
+ /** Listener used to handle tap of the "separate' button for a participant. */
+ private View.OnClickListener mSeparateListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ DialerCall call = getCallFromView(view);
+ LogUtil.i("ConferenceParticipantListAdapter.mSeparateListener.onClick", "call: " + call);
+ if (call != null) {
+ call.splitFromConference();
+ }
+ }
+ };
+ /** The conference participants to show in the ListView. */
+ private List<ParticipantInfo> mConferenceParticipants = new ArrayList<>();
+ /** {@code True} if the conference parent supports separating calls from the conference. */
+ private boolean mParentCanSeparate;
+
+ /**
+ * Creates an instance of the ConferenceParticipantListAdapter.
+ *
+ * @param listView The listview.
+ * @param contactPhotoManager The contact photo manager, used to load contact photos.
+ */
+ public ConferenceParticipantListAdapter(
+ ListView listView, ContactPhotoManager contactPhotoManager) {
+
+ mListView = listView;
+ mContactsPreferences = ContactsPreferencesFactory.newContactsPreferences(getContext());
+ mContactPhotoManager = contactPhotoManager;
+ }
+
+ /**
+ * Updates the adapter with the new conference participant information provided.
+ *
+ * @param conferenceParticipants The list of conference participants.
+ * @param parentCanSeparate {@code True} if the parent supports separating calls from the
+ * conference.
+ */
+ public void updateParticipants(
+ List<DialerCall> conferenceParticipants, boolean parentCanSeparate) {
+ if (mContactsPreferences != null) {
+ mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
+ mContactsPreferences.refreshValue(ContactsPreferences.SORT_ORDER_KEY);
+ }
+ mParentCanSeparate = parentCanSeparate;
+ updateParticipantInfo(conferenceParticipants);
+ }
+
+ /**
+ * Determines the number of participants in the conference.
+ *
+ * @return The number of participants.
+ */
+ @Override
+ public int getCount() {
+ return mConferenceParticipants.size();
+ }
+
+ /**
+ * Retrieves an item from the list of participants.
+ *
+ * @param position Position of the item whose data we want within the adapter's data set.
+ * @return The {@link ParticipantInfo}.
+ */
+ @Override
+ public Object getItem(int position) {
+ return mConferenceParticipants.get(position);
+ }
+
+ /**
+ * Retreives the adapter-specific item id for an item at a specified position.
+ *
+ * @param position The position of the item within the adapter's data set whose row id we want.
+ * @return The item id.
+ */
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ /**
+ * Refreshes call information for the call passed in.
+ *
+ * @param call The new call information.
+ */
+ public void refreshCall(DialerCall call) {
+ String callId = call.getId();
+
+ if (mParticipantsByCallId.containsKey(callId)) {
+ ParticipantInfo participantInfo = mParticipantsByCallId.get(callId);
+ participantInfo.setCall(call);
+ refreshView(callId);
+ }
+ }
+
+ private Context getContext() {
+ return mListView.getContext();
+ }
+
+ /**
+ * Attempts to refresh the view for the specified call ID. This ensures the contact info and photo
+ * loaded from cache are updated.
+ *
+ * @param callId The call id.
+ */
+ private void refreshView(String callId) {
+ int first = mListView.getFirstVisiblePosition();
+ int last = mListView.getLastVisiblePosition();
+
+ for (int position = 0; position <= last - first; position++) {
+ View view = mListView.getChildAt(position);
+ String rowCallId = (String) view.getTag();
+ if (rowCallId.equals(callId)) {
+ getView(position + first, view, mListView);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Creates or populates an existing conference participant row.
+ *
+ * @param position The position of the item within the adapter's data set of the item whose view
+ * we want.
+ * @param convertView The old view to reuse, if possible.
+ * @param parent The parent that this view will eventually be attached to
+ * @return The populated view.
+ */
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // Make sure we have a valid convertView to start with
+ final View result =
+ convertView == null
+ ? LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.caller_in_conference, parent, false)
+ : convertView;
+
+ ParticipantInfo participantInfo = mConferenceParticipants.get(position);
+ DialerCall call = participantInfo.getCall();
+ ContactCacheEntry contactCache = participantInfo.getContactCacheEntry();
+
+ final ContactInfoCache cache = ContactInfoCache.getInstance(getContext());
+
+ // If a cache lookup has not yet been performed to retrieve the contact information and
+ // photo, do it now.
+ if (!participantInfo.isCacheLookupComplete()) {
+ cache.findInfo(
+ participantInfo.getCall(),
+ participantInfo.getCall().getState() == DialerCall.State.INCOMING,
+ new ContactLookupCallback(this));
+ }
+
+ boolean thisRowCanSeparate =
+ mParentCanSeparate
+ && call.can(android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE);
+ boolean thisRowCanDisconnect =
+ call.can(android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE);
+
+ setCallerInfoForRow(
+ result,
+ contactCache.namePrimary,
+ ContactDisplayUtils.getPreferredDisplayName(
+ contactCache.namePrimary, contactCache.nameAlternative, mContactsPreferences),
+ contactCache.number,
+ contactCache.label,
+ contactCache.lookupKey,
+ contactCache.displayPhotoUri,
+ thisRowCanSeparate,
+ thisRowCanDisconnect);
+
+ // Tag the row in the conference participant list with the call id to make it easier to
+ // find calls when contact cache information is loaded.
+ result.setTag(call.getId());
+
+ return result;
+ }
+
+ /**
+ * Replaces the contact info for a participant and triggers a refresh of the UI.
+ *
+ * @param callId The call id.
+ * @param entry The new contact info.
+ */
+ /* package */ void updateContactInfo(String callId, ContactCacheEntry entry) {
+ if (mParticipantsByCallId.containsKey(callId)) {
+ ParticipantInfo participantInfo = mParticipantsByCallId.get(callId);
+ participantInfo.setContactCacheEntry(entry);
+ participantInfo.setCacheLookupComplete(true);
+ refreshView(callId);
+ }
+ }
+
+ /**
+ * Sets the caller information for a row in the conference participant list.
+ *
+ * @param view The view to set the details on.
+ * @param callerName The participant's name.
+ * @param callerNumber The participant's phone number.
+ * @param callerNumberType The participant's phone number typ.e
+ * @param lookupKey The lookup key for the participant (for photo lookup).
+ * @param photoUri The URI of the contact photo.
+ * @param thisRowCanSeparate {@code True} if this participant can separate from the conference.
+ * @param thisRowCanDisconnect {@code True} if this participant can be disconnected.
+ */
+ private void setCallerInfoForRow(
+ View view,
+ String callerName,
+ String preferredName,
+ String callerNumber,
+ String callerNumberType,
+ String lookupKey,
+ Uri photoUri,
+ boolean thisRowCanSeparate,
+ boolean thisRowCanDisconnect) {
+
+ final ImageView photoView = (ImageView) view.findViewById(R.id.callerPhoto);
+ final TextView nameTextView = (TextView) view.findViewById(R.id.conferenceCallerName);
+ final TextView numberTextView = (TextView) view.findViewById(R.id.conferenceCallerNumber);
+ final TextView numberTypeTextView =
+ (TextView) view.findViewById(R.id.conferenceCallerNumberType);
+ final View endButton = view.findViewById(R.id.conferenceCallerDisconnect);
+ final View separateButton = view.findViewById(R.id.conferenceCallerSeparate);
+
+ endButton.setVisibility(thisRowCanDisconnect ? View.VISIBLE : View.GONE);
+ if (thisRowCanDisconnect) {
+ endButton.setOnClickListener(mDisconnectListener);
+ } else {
+ endButton.setOnClickListener(null);
+ }
+
+ separateButton.setVisibility(thisRowCanSeparate ? View.VISIBLE : View.GONE);
+ if (thisRowCanSeparate) {
+ separateButton.setOnClickListener(mSeparateListener);
+ } else {
+ separateButton.setOnClickListener(null);
+ }
+
+ DefaultImageRequest imageRequest =
+ (photoUri != null)
+ ? null
+ : new DefaultImageRequest(callerName, lookupKey, true /* isCircularPhoto */);
+
+ mContactPhotoManager.loadDirectoryPhoto(photoView, photoUri, false, true, imageRequest);
+
+ // set the caller name
+ nameTextView.setText(preferredName);
+
+ // set the caller number in subscript, or make the field disappear.
+ if (TextUtils.isEmpty(callerNumber)) {
+ numberTextView.setVisibility(View.GONE);
+ numberTypeTextView.setVisibility(View.GONE);
+ } else {
+ numberTextView.setVisibility(View.VISIBLE);
+ numberTextView.setText(
+ PhoneNumberUtilsCompat.createTtsSpannable(
+ BidiFormatter.getInstance().unicodeWrap(callerNumber, TextDirectionHeuristics.LTR)));
+ numberTypeTextView.setVisibility(View.VISIBLE);
+ numberTypeTextView.setText(callerNumberType);
+ }
+ }
+
+ /**
+ * Updates the participant info list which is bound to the ListView. Stores the call and contact
+ * info for all entries. The list is sorted alphabetically by participant name.
+ *
+ * @param conferenceParticipants The calls which make up the conference participants.
+ */
+ private void updateParticipantInfo(List<DialerCall> conferenceParticipants) {
+ final ContactInfoCache cache = ContactInfoCache.getInstance(getContext());
+ boolean newParticipantAdded = false;
+ Set<String> newCallIds = new ArraySet<>(conferenceParticipants.size());
+
+ // Update or add conference participant info.
+ for (DialerCall call : conferenceParticipants) {
+ String callId = call.getId();
+ newCallIds.add(callId);
+ ContactCacheEntry contactCache = cache.getInfo(callId);
+ if (contactCache == null) {
+ contactCache =
+ ContactInfoCache.buildCacheEntryFromCall(
+ getContext(), call, call.getState() == DialerCall.State.INCOMING);
+ }
+
+ if (mParticipantsByCallId.containsKey(callId)) {
+ ParticipantInfo participantInfo = mParticipantsByCallId.get(callId);
+ participantInfo.setCall(call);
+ participantInfo.setContactCacheEntry(contactCache);
+ } else {
+ newParticipantAdded = true;
+ ParticipantInfo participantInfo = new ParticipantInfo(call, contactCache);
+ mConferenceParticipants.add(participantInfo);
+ mParticipantsByCallId.put(call.getId(), participantInfo);
+ }
+ }
+
+ // Remove any participants that no longer exist.
+ Iterator<Map.Entry<String, ParticipantInfo>> it = mParticipantsByCallId.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<String, ParticipantInfo> entry = it.next();
+ String existingCallId = entry.getKey();
+ if (!newCallIds.contains(existingCallId)) {
+ ParticipantInfo existingInfo = entry.getValue();
+ mConferenceParticipants.remove(existingInfo);
+ it.remove();
+ }
+ }
+
+ if (newParticipantAdded) {
+ // Sort the list of participants by contact name.
+ sortParticipantList();
+ }
+ notifyDataSetChanged();
+ }
+
+ /** Sorts the participant list by contact name. */
+ private void sortParticipantList() {
+ Collections.sort(
+ mConferenceParticipants,
+ new Comparator<ParticipantInfo>() {
+ @Override
+ public int compare(ParticipantInfo p1, ParticipantInfo p2) {
+ // Contact names might be null, so replace with empty string.
+ ContactCacheEntry c1 = p1.getContactCacheEntry();
+ String p1Name =
+ ContactDisplayUtils.getPreferredSortName(
+ c1.namePrimary, c1.nameAlternative, mContactsPreferences);
+ p1Name = p1Name != null ? p1Name : "";
+
+ ContactCacheEntry c2 = p2.getContactCacheEntry();
+ String p2Name =
+ ContactDisplayUtils.getPreferredSortName(
+ c2.namePrimary, c2.nameAlternative, mContactsPreferences);
+ p2Name = p2Name != null ? p2Name : "";
+
+ return p1Name.compareToIgnoreCase(p2Name);
+ }
+ });
+ }
+
+ private DialerCall getCallFromView(View view) {
+ View parent = (View) view.getParent();
+ String callId = (String) parent.getTag();
+ return CallList.getInstance().getCallById(callId);
+ }
+
+ /**
+ * Callback class used when making requests to the {@link ContactInfoCache} to resolve contact
+ * info and contact photos for conference participants.
+ */
+ public static class ContactLookupCallback implements ContactInfoCache.ContactInfoCacheCallback {
+
+ private final WeakReference<ConferenceParticipantListAdapter> mListAdapter;
+
+ public ContactLookupCallback(ConferenceParticipantListAdapter listAdapter) {
+ mListAdapter = new WeakReference<>(listAdapter);
+ }
+
+ /**
+ * Called when contact info has been resolved.
+ *
+ * @param callId The call id.
+ * @param entry The new contact information.
+ */
+ @Override
+ public void onContactInfoComplete(String callId, ContactCacheEntry entry) {
+ update(callId, entry);
+ }
+
+ /**
+ * Called when contact photo has been loaded into the cache.
+ *
+ * @param callId The call id.
+ * @param entry The new contact information.
+ */
+ @Override
+ public void onImageLoadComplete(String callId, ContactCacheEntry entry) {
+ update(callId, entry);
+ }
+
+ /**
+ * Updates the contact information for a participant.
+ *
+ * @param callId The call id.
+ * @param entry The new contact information.
+ */
+ private void update(String callId, ContactCacheEntry entry) {
+ ConferenceParticipantListAdapter listAdapter = mListAdapter.get();
+ if (listAdapter != null) {
+ listAdapter.updateContactInfo(callId, entry);
+ }
+ }
+ }
+
+ /**
+ * Internal class which represents a participant. Includes a reference to the {@link DialerCall}
+ * and the corresponding {@link ContactCacheEntry} for the participant.
+ */
+ private static class ParticipantInfo {
+
+ private DialerCall mCall;
+ private ContactCacheEntry mContactCacheEntry;
+ private boolean mCacheLookupComplete = false;
+
+ public ParticipantInfo(DialerCall call, ContactCacheEntry contactCacheEntry) {
+ mCall = call;
+ mContactCacheEntry = contactCacheEntry;
+ }
+
+ public DialerCall getCall() {
+ return mCall;
+ }
+
+ public void setCall(DialerCall call) {
+ mCall = call;
+ }
+
+ public ContactCacheEntry getContactCacheEntry() {
+ return mContactCacheEntry;
+ }
+
+ public void setContactCacheEntry(ContactCacheEntry entry) {
+ mContactCacheEntry = entry;
+ }
+
+ public boolean isCacheLookupComplete() {
+ return mCacheLookupComplete;
+ }
+
+ public void setCacheLookupComplete(boolean cacheLookupComplete) {
+ mCacheLookupComplete = cacheLookupComplete;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof ParticipantInfo) {
+ ParticipantInfo p = (ParticipantInfo) o;
+ return Objects.equals(p.getCall().getId(), mCall.getId());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return mCall.getId().hashCode();
+ }
+ }
+}
diff --git a/java/com/android/incallui/ContactInfoCache.java b/java/com/android/incallui/ContactInfoCache.java
new file mode 100644
index 000000000..4d4d94a17
--- /dev/null
+++ b/java/com/android/incallui/ContactInfoCache.java
@@ -0,0 +1,759 @@
+/*
+ * Copyright (C) 2013 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.incallui;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.DisplayNameSources;
+import android.support.annotation.AnyThread;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import android.support.v4.os.UserManagerCompat;
+import android.telecom.TelecomManager;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import com.android.contacts.common.ContactsUtils;
+import com.android.dialer.common.Assert;
+import com.android.dialer.logging.nano.ContactLookupResult;
+import com.android.dialer.phonenumbercache.CachedNumberLookupService;
+import com.android.dialer.phonenumbercache.CachedNumberLookupService.CachedContactInfo;
+import com.android.dialer.phonenumbercache.ContactInfo;
+import com.android.dialer.phonenumbercache.PhoneNumberCache;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.android.dialer.util.MoreStrings;
+import com.android.incallui.CallerInfoAsyncQuery.OnQueryCompleteListener;
+import com.android.incallui.ContactsAsyncHelper.OnImageLoadCompleteListener;
+import com.android.incallui.bindings.PhoneNumberService;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.incall.protocol.ContactPhotoType;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Class responsible for querying Contact Information for DialerCall objects. Can perform
+ * asynchronous requests to the Contact Provider for information as well as respond synchronously
+ * for any data that it currently has cached from previous queries. This class always gets called
+ * from the UI thread so it does not need thread protection.
+ */
+public class ContactInfoCache implements OnImageLoadCompleteListener {
+
+ private static final String TAG = ContactInfoCache.class.getSimpleName();
+ private static final int TOKEN_UPDATE_PHOTO_FOR_CALL_STATE = 0;
+ private static ContactInfoCache sCache = null;
+ private final Context mContext;
+ private final PhoneNumberService mPhoneNumberService;
+ // Cache info map needs to be thread-safe since it could be modified by both main thread and
+ // worker thread.
+ private final Map<String, ContactCacheEntry> mInfoMap = new ConcurrentHashMap<>();
+ private final Map<String, Set<ContactInfoCacheCallback>> mCallBacks = new ArrayMap<>();
+ private Drawable mDefaultContactPhotoDrawable;
+ private Drawable mConferencePhotoDrawable;
+
+ private ContactInfoCache(Context context) {
+ mContext = context;
+ mPhoneNumberService = Bindings.get(context).newPhoneNumberService(context);
+ }
+
+ public static synchronized ContactInfoCache getInstance(Context mContext) {
+ if (sCache == null) {
+ sCache = new ContactInfoCache(mContext.getApplicationContext());
+ }
+ return sCache;
+ }
+
+ public static ContactCacheEntry buildCacheEntryFromCall(
+ Context context, DialerCall call, boolean isIncoming) {
+ final ContactCacheEntry entry = new ContactCacheEntry();
+
+ // TODO: get rid of caller info.
+ final CallerInfo info = CallerInfoUtils.buildCallerInfo(context, call);
+ ContactInfoCache.populateCacheEntry(
+ context, info, entry, call.getNumberPresentation(), isIncoming);
+ return entry;
+ }
+
+ /** Populate a cache entry from a call (which got converted into a caller info). */
+ public static void populateCacheEntry(
+ @NonNull Context context,
+ @NonNull CallerInfo info,
+ @NonNull ContactCacheEntry cce,
+ int presentation,
+ boolean isIncoming) {
+ Objects.requireNonNull(info);
+ String displayName = null;
+ String displayNumber = null;
+ String displayLocation = null;
+ String label = null;
+ boolean isSipCall = false;
+
+ // It appears that there is a small change in behaviour with the
+ // PhoneUtils' startGetCallerInfo whereby if we query with an
+ // empty number, we will get a valid CallerInfo object, but with
+ // fields that are all null, and the isTemporary boolean input
+ // parameter as true.
+
+ // In the past, we would see a NULL callerinfo object, but this
+ // ends up causing null pointer exceptions elsewhere down the
+ // line in other cases, so we need to make this fix instead. It
+ // appears that this was the ONLY call to PhoneUtils
+ // .getCallerInfo() that relied on a NULL CallerInfo to indicate
+ // an unknown contact.
+
+ // Currently, info.phoneNumber may actually be a SIP address, and
+ // if so, it might sometimes include the "sip:" prefix. That
+ // prefix isn't really useful to the user, though, so strip it off
+ // if present. (For any other URI scheme, though, leave the
+ // prefix alone.)
+ // TODO: It would be cleaner for CallerInfo to explicitly support
+ // SIP addresses instead of overloading the "phoneNumber" field.
+ // Then we could remove this hack, and instead ask the CallerInfo
+ // for a "user visible" form of the SIP address.
+ String number = info.phoneNumber;
+
+ if (!TextUtils.isEmpty(number)) {
+ isSipCall = PhoneNumberHelper.isUriNumber(number);
+ if (number.startsWith("sip:")) {
+ number = number.substring(4);
+ }
+ }
+
+ if (TextUtils.isEmpty(info.name)) {
+ // No valid "name" in the CallerInfo, so fall back to
+ // something else.
+ // (Typically, we promote the phone number up to the "name" slot
+ // onscreen, and possibly display a descriptive string in the
+ // "number" slot.)
+ if (TextUtils.isEmpty(number)) {
+ // No name *or* number! Display a generic "unknown" string
+ // (or potentially some other default based on the presentation.)
+ displayName = getPresentationString(context, presentation, info.callSubject);
+ Log.d(TAG, " ==> no name *or* number! displayName = " + displayName);
+ } else if (presentation != TelecomManager.PRESENTATION_ALLOWED) {
+ // This case should never happen since the network should never send a phone #
+ // AND a restricted presentation. However we leave it here in case of weird
+ // network behavior
+ displayName = getPresentationString(context, presentation, info.callSubject);
+ Log.d(TAG, " ==> presentation not allowed! displayName = " + displayName);
+ } else if (!TextUtils.isEmpty(info.cnapName)) {
+ // No name, but we do have a valid CNAP name, so use that.
+ displayName = info.cnapName;
+ info.name = info.cnapName;
+ displayNumber = PhoneNumberHelper.formatNumber(number, context);
+ Log.d(
+ TAG,
+ " ==> cnapName available: displayName '"
+ + displayName
+ + "', displayNumber '"
+ + displayNumber
+ + "'");
+ } else {
+ // No name; all we have is a number. This is the typical
+ // case when an incoming call doesn't match any contact,
+ // or if you manually dial an outgoing number using the
+ // dialpad.
+ displayNumber = PhoneNumberHelper.formatNumber(number, context);
+
+ // Display a geographical description string if available
+ // (but only for incoming calls.)
+ if (isIncoming) {
+ // TODO (CallerInfoAsyncQuery cleanup): Fix the CallerInfo
+ // query to only do the geoDescription lookup in the first
+ // place for incoming calls.
+ displayLocation = info.geoDescription; // may be null
+ Log.d(TAG, "Geodescrption: " + info.geoDescription);
+ }
+
+ Log.d(
+ TAG,
+ " ==> no name; falling back to number:"
+ + " displayNumber '"
+ + Log.pii(displayNumber)
+ + "', displayLocation '"
+ + displayLocation
+ + "'");
+ }
+ } else {
+ // We do have a valid "name" in the CallerInfo. Display that
+ // in the "name" slot, and the phone number in the "number" slot.
+ if (presentation != TelecomManager.PRESENTATION_ALLOWED) {
+ // This case should never happen since the network should never send a name
+ // AND a restricted presentation. However we leave it here in case of weird
+ // network behavior
+ displayName = getPresentationString(context, presentation, info.callSubject);
+ Log.d(
+ TAG,
+ " ==> valid name, but presentation not allowed!" + " displayName = " + displayName);
+ } else {
+ // Causes cce.namePrimary to be set as info.name below. CallCardPresenter will
+ // later determine whether to use the name or nameAlternative when presenting
+ displayName = info.name;
+ cce.nameAlternative = info.nameAlternative;
+ displayNumber = PhoneNumberHelper.formatNumber(number, context);
+ label = info.phoneLabel;
+ Log.d(
+ TAG,
+ " ==> name is present in CallerInfo: displayName '"
+ + displayName
+ + "', displayNumber '"
+ + displayNumber
+ + "'");
+ }
+ }
+
+ cce.namePrimary = displayName;
+ cce.number = displayNumber;
+ cce.location = displayLocation;
+ cce.label = label;
+ cce.isSipCall = isSipCall;
+ cce.userType = info.userType;
+
+ if (info.contactExists) {
+ cce.contactLookupResult = ContactLookupResult.Type.LOCAL_CONTACT;
+ }
+ }
+
+ /** Gets name strings based on some special presentation modes and the associated custom label. */
+ private static String getPresentationString(
+ Context context, int presentation, String customLabel) {
+ String name = context.getString(R.string.unknown);
+ if (!TextUtils.isEmpty(customLabel)
+ && ((presentation == TelecomManager.PRESENTATION_UNKNOWN)
+ || (presentation == TelecomManager.PRESENTATION_RESTRICTED))) {
+ name = customLabel;
+ return name;
+ } else {
+ if (presentation == TelecomManager.PRESENTATION_RESTRICTED) {
+ name = PhoneNumberHelper.getDisplayNameForRestrictedNumber(context).toString();
+ } else if (presentation == TelecomManager.PRESENTATION_PAYPHONE) {
+ name = context.getString(R.string.payphone);
+ }
+ }
+ return name;
+ }
+
+ public ContactCacheEntry getInfo(String callId) {
+ return mInfoMap.get(callId);
+ }
+
+ public void maybeInsertCnapInformationIntoCache(
+ Context context, final DialerCall call, final CallerInfo info) {
+ final CachedNumberLookupService cachedNumberLookupService =
+ PhoneNumberCache.get(context).getCachedNumberLookupService();
+ if (!UserManagerCompat.isUserUnlocked(context)) {
+ Log.i(TAG, "User locked, not inserting cnap info into cache");
+ return;
+ }
+ if (cachedNumberLookupService == null
+ || TextUtils.isEmpty(info.cnapName)
+ || mInfoMap.get(call.getId()) != null) {
+ return;
+ }
+ final Context applicationContext = context.getApplicationContext();
+ Log.i(TAG, "Found contact with CNAP name - inserting into cache");
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ ContactInfo contactInfo = new ContactInfo();
+ CachedContactInfo cacheInfo = cachedNumberLookupService.buildCachedContactInfo(contactInfo);
+ cacheInfo.setSource(CachedContactInfo.SOURCE_TYPE_CNAP, "CNAP", 0);
+ contactInfo.name = info.cnapName;
+ contactInfo.number = call.getNumber();
+ contactInfo.type = ContactsContract.CommonDataKinds.Phone.TYPE_MAIN;
+ try {
+ final JSONObject contactRows =
+ new JSONObject()
+ .put(
+ Phone.CONTENT_ITEM_TYPE,
+ new JSONObject()
+ .put(Phone.NUMBER, contactInfo.number)
+ .put(Phone.TYPE, Phone.TYPE_MAIN));
+ final String jsonString =
+ new JSONObject()
+ .put(Contacts.DISPLAY_NAME, contactInfo.name)
+ .put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME)
+ .put(Contacts.CONTENT_ITEM_TYPE, contactRows)
+ .toString();
+ cacheInfo.setLookupKey(jsonString);
+ } catch (JSONException e) {
+ Log.w(TAG, "Creation of lookup key failed when caching CNAP information");
+ }
+ cachedNumberLookupService.addContact(applicationContext, cacheInfo);
+ return null;
+ }
+ }.execute();
+ }
+
+ /**
+ * Requests contact data for the DialerCall object passed in. Returns the data through callback.
+ * If callback is null, no response is made, however the query is still performed and cached.
+ *
+ * @param callback The function to call back when the call is found. Can be null.
+ */
+ @MainThread
+ public void findInfo(
+ @NonNull final DialerCall call,
+ final boolean isIncoming,
+ @NonNull ContactInfoCacheCallback callback) {
+ Assert.isMainThread();
+ Objects.requireNonNull(callback);
+
+ final String callId = call.getId();
+ final ContactCacheEntry cacheEntry = mInfoMap.get(callId);
+ Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);
+
+ // If we have a previously obtained intermediate result return that now
+ if (cacheEntry != null) {
+ Log.d(
+ TAG,
+ "Contact lookup. In memory cache hit; lookup "
+ + (callBacks == null ? "complete" : "still running"));
+ callback.onContactInfoComplete(callId, cacheEntry);
+ // If no other callbacks are in flight, we're done.
+ if (callBacks == null) {
+ return;
+ }
+ }
+
+ // If the entry already exists, add callback
+ if (callBacks != null) {
+ callBacks.add(callback);
+ return;
+ }
+ Log.d(TAG, "Contact lookup. In memory cache miss; searching provider.");
+ // New lookup
+ callBacks = new ArraySet<>();
+ callBacks.add(callback);
+ mCallBacks.put(callId, callBacks);
+
+ /**
+ * Performs a query for caller information. Save any immediate data we get from the query. An
+ * asynchronous query may also be made for any data that we do not already have. Some queries,
+ * such as those for voicemail and emergency call information, will not perform an additional
+ * asynchronous query.
+ */
+ final CallerInfo callerInfo =
+ CallerInfoUtils.getCallerInfoForCall(
+ mContext,
+ call,
+ new DialerCallCookieWrapper(callId, call.getNumberPresentation()),
+ new FindInfoCallback(isIncoming));
+
+ updateCallerInfoInCacheOnAnyThread(
+ callId, call.getNumberPresentation(), callerInfo, isIncoming, false);
+ sendInfoNotifications(callId, mInfoMap.get(callId));
+ }
+
+ @AnyThread
+ private void updateCallerInfoInCacheOnAnyThread(
+ String callId,
+ int numberPresentation,
+ CallerInfo callerInfo,
+ boolean isIncoming,
+ boolean didLocalLookup) {
+ int presentationMode = numberPresentation;
+ if (callerInfo.contactExists
+ || callerInfo.isEmergencyNumber()
+ || callerInfo.isVoiceMailNumber()) {
+ presentationMode = TelecomManager.PRESENTATION_ALLOWED;
+ }
+
+ synchronized (mInfoMap) {
+ ContactCacheEntry cacheEntry = mInfoMap.get(callId);
+ // Ensure we always have a cacheEntry. Replace the existing entry if
+ // it has no name or if we found a local contact.
+ if (cacheEntry == null
+ || TextUtils.isEmpty(cacheEntry.namePrimary)
+ || callerInfo.contactExists) {
+ cacheEntry = buildEntry(mContext, callerInfo, presentationMode, isIncoming);
+ mInfoMap.put(callId, cacheEntry);
+ }
+ if (didLocalLookup) {
+ // Before issuing a request for more data from other services, we only check that the
+ // contact wasn't found in the local DB. We don't check the if the cache entry already
+ // has a name because we allow overriding cnap data with data from other services.
+ if (!callerInfo.contactExists && mPhoneNumberService != null) {
+ Log.d(TAG, "Contact lookup. Local contacts miss, checking remote");
+ final PhoneNumberServiceListener listener = new PhoneNumberServiceListener(callId);
+ mPhoneNumberService.getPhoneNumberInfo(cacheEntry.number, listener, listener, isIncoming);
+ } else if (cacheEntry.displayPhotoUri != null) {
+ Log.d(TAG, "Contact lookup. Local contact found, starting image load");
+ // Load the image with a callback to update the image state.
+ // When the load is finished, onImageLoadComplete() will be called.
+ cacheEntry.hasPhotoToLoad = true;
+ ContactsAsyncHelper.startObtainPhotoAsync(
+ TOKEN_UPDATE_PHOTO_FOR_CALL_STATE,
+ mContext,
+ cacheEntry.displayPhotoUri,
+ ContactInfoCache.this,
+ callId);
+ }
+ }
+ }
+ }
+
+ /**
+ * Implemented for ContactsAsyncHelper.OnImageLoadCompleteListener interface. Update contact photo
+ * when image is loaded in worker thread.
+ */
+ @WorkerThread
+ @Override
+ public void onImageLoaded(int token, Drawable photo, Bitmap photoIcon, Object cookie) {
+ Assert.isWorkerThread();
+ loadImage(photo, photoIcon, cookie);
+ }
+
+ private void loadImage(Drawable photo, Bitmap photoIcon, Object cookie) {
+ Log.d(this, "Image load complete with context: ", mContext);
+ // TODO: may be nice to update the image view again once the newer one
+ // is available on contacts database.
+ String callId = (String) cookie;
+ ContactCacheEntry entry = mInfoMap.get(callId);
+
+ if (entry == null) {
+ Log.e(this, "Image Load received for empty search entry.");
+ clearCallbacks(callId);
+ return;
+ }
+
+ Log.d(this, "setting photo for entry: ", entry);
+
+ // Conference call icons are being handled in CallCardPresenter.
+ if (photo != null) {
+ Log.v(this, "direct drawable: ", photo);
+ entry.photo = photo;
+ entry.photoType = ContactPhotoType.CONTACT;
+ } else if (photoIcon != null) {
+ Log.v(this, "photo icon: ", photoIcon);
+ entry.photo = new BitmapDrawable(mContext.getResources(), photoIcon);
+ entry.photoType = ContactPhotoType.CONTACT;
+ } else {
+ Log.v(this, "unknown photo");
+ entry.photo = null;
+ entry.photoType = ContactPhotoType.DEFAULT_PLACEHOLDER;
+ }
+ }
+
+ /**
+ * Implemented for ContactsAsyncHelper.OnImageLoadCompleteListener interface. make sure that the
+ * call state is reflected after the image is loaded.
+ */
+ @MainThread
+ @Override
+ public void onImageLoadComplete(int token, Drawable photo, Bitmap photoIcon, Object cookie) {
+ Assert.isMainThread();
+ String callId = (String) cookie;
+ ContactCacheEntry entry = mInfoMap.get(callId);
+ sendImageNotifications(callId, entry);
+
+ clearCallbacks(callId);
+ }
+
+ /** Blows away the stored cache values. */
+ public void clearCache() {
+ mInfoMap.clear();
+ mCallBacks.clear();
+ }
+
+ private ContactCacheEntry buildEntry(
+ Context context, CallerInfo info, int presentation, boolean isIncoming) {
+ final ContactCacheEntry cce = new ContactCacheEntry();
+ populateCacheEntry(context, info, cce, presentation, isIncoming);
+
+ // This will only be true for emergency numbers
+ if (info.photoResource != 0) {
+ cce.photo = context.getResources().getDrawable(info.photoResource);
+ } else if (info.isCachedPhotoCurrent) {
+ if (info.cachedPhoto != null) {
+ cce.photo = info.cachedPhoto;
+ cce.photoType = ContactPhotoType.CONTACT;
+ } else {
+ cce.photo = getDefaultContactPhotoDrawable();
+ cce.photoType = ContactPhotoType.DEFAULT_PLACEHOLDER;
+ }
+ } else if (info.contactDisplayPhotoUri == null) {
+ cce.photo = getDefaultContactPhotoDrawable();
+ cce.photoType = ContactPhotoType.DEFAULT_PLACEHOLDER;
+ } else {
+ cce.displayPhotoUri = info.contactDisplayPhotoUri;
+ cce.photo = null;
+ }
+
+ // Support any contact id in N because QuickContacts in N starts supporting enterprise
+ // contact id
+ if (info.lookupKeyOrNull != null
+ && (VERSION.SDK_INT >= VERSION_CODES.N || info.contactIdOrZero != 0)) {
+ cce.lookupUri = Contacts.getLookupUri(info.contactIdOrZero, info.lookupKeyOrNull);
+ } else {
+ Log.v(TAG, "lookup key is null or contact ID is 0 on M. Don't create a lookup uri.");
+ cce.lookupUri = null;
+ }
+
+ cce.lookupKey = info.lookupKeyOrNull;
+ cce.contactRingtoneUri = info.contactRingtoneUri;
+ if (cce.contactRingtoneUri == null || Uri.EMPTY.equals(cce.contactRingtoneUri)) {
+ cce.contactRingtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
+ }
+
+ return cce;
+ }
+
+ /** Sends the updated information to call the callbacks for the entry. */
+ private void sendInfoNotifications(String callId, ContactCacheEntry entry) {
+ final Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);
+ if (callBacks != null) {
+ for (ContactInfoCacheCallback callBack : callBacks) {
+ callBack.onContactInfoComplete(callId, entry);
+ }
+ }
+ }
+
+ private void sendImageNotifications(String callId, ContactCacheEntry entry) {
+ final Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);
+ if (callBacks != null && entry.photo != null) {
+ for (ContactInfoCacheCallback callBack : callBacks) {
+ callBack.onImageLoadComplete(callId, entry);
+ }
+ }
+ }
+
+ private void clearCallbacks(String callId) {
+ mCallBacks.remove(callId);
+ }
+
+ public Drawable getDefaultContactPhotoDrawable() {
+ if (mDefaultContactPhotoDrawable == null) {
+ mDefaultContactPhotoDrawable =
+ mContext.getResources().getDrawable(R.drawable.img_no_image_automirrored);
+ }
+ return mDefaultContactPhotoDrawable;
+ }
+
+ public Drawable getConferenceDrawable() {
+ if (mConferencePhotoDrawable == null) {
+ mConferencePhotoDrawable =
+ mContext.getResources().getDrawable(R.drawable.img_conference_automirrored);
+ }
+ return mConferencePhotoDrawable;
+ }
+
+ /** Callback interface for the contact query. */
+ public interface ContactInfoCacheCallback {
+
+ void onContactInfoComplete(String callId, ContactCacheEntry entry);
+
+ void onImageLoadComplete(String callId, ContactCacheEntry entry);
+ }
+
+ /** This is cached contact info, which should be the ONLY info used by UI. */
+ public static class ContactCacheEntry {
+
+ public String namePrimary;
+ public String nameAlternative;
+ public String number;
+ public String location;
+ public String label;
+ public Drawable photo;
+ @ContactPhotoType public int photoType;
+ public boolean isSipCall;
+ // Note in cache entry whether this is a pending async loading action to know whether to
+ // wait for its callback or not.
+ public boolean hasPhotoToLoad;
+ /** This will be used for the "view" notification. */
+ public Uri contactUri;
+ /** Either a display photo or a thumbnail URI. */
+ public Uri displayPhotoUri;
+
+ public Uri lookupUri; // Sent to NotificationMananger
+ public String lookupKey;
+ public int contactLookupResult = ContactLookupResult.Type.NOT_FOUND;
+ public long userType = ContactsUtils.USER_TYPE_CURRENT;
+ public Uri contactRingtoneUri;
+
+ @Override
+ public String toString() {
+ return "ContactCacheEntry{"
+ + "name='"
+ + MoreStrings.toSafeString(namePrimary)
+ + '\''
+ + ", nameAlternative='"
+ + MoreStrings.toSafeString(nameAlternative)
+ + '\''
+ + ", number='"
+ + MoreStrings.toSafeString(number)
+ + '\''
+ + ", location='"
+ + MoreStrings.toSafeString(location)
+ + '\''
+ + ", label='"
+ + label
+ + '\''
+ + ", photo="
+ + photo
+ + ", isSipCall="
+ + isSipCall
+ + ", contactUri="
+ + contactUri
+ + ", displayPhotoUri="
+ + displayPhotoUri
+ + ", contactLookupResult="
+ + contactLookupResult
+ + ", userType="
+ + userType
+ + ", contactRingtoneUri="
+ + contactRingtoneUri
+ + '}';
+ }
+ }
+
+ private static final class DialerCallCookieWrapper {
+ public final String callId;
+ public final int numberPresentation;
+
+ public DialerCallCookieWrapper(String callId, int numberPresentation) {
+ this.callId = callId;
+ this.numberPresentation = numberPresentation;
+ }
+ }
+
+ private class FindInfoCallback implements OnQueryCompleteListener {
+
+ private final boolean mIsIncoming;
+
+ public FindInfoCallback(boolean isIncoming) {
+ mIsIncoming = isIncoming;
+ }
+
+ @Override
+ public void onDataLoaded(int token, Object cookie, CallerInfo ci) {
+ Assert.isWorkerThread();
+ DialerCallCookieWrapper cw = (DialerCallCookieWrapper) cookie;
+ updateCallerInfoInCacheOnAnyThread(cw.callId, cw.numberPresentation, ci, mIsIncoming, true);
+ }
+
+ @Override
+ public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
+ Assert.isMainThread();
+ DialerCallCookieWrapper cw = (DialerCallCookieWrapper) cookie;
+ String callId = cw.callId;
+ ContactCacheEntry cacheEntry = mInfoMap.get(callId);
+ // This may happen only when InCallPresenter attempt to cleanup.
+ if (cacheEntry == null) {
+ Log.w(TAG, "Contact lookup done, but cache entry is not found.");
+ clearCallbacks(callId);
+ return;
+ }
+ sendInfoNotifications(callId, cacheEntry);
+ if (!cacheEntry.hasPhotoToLoad) {
+ if (callerInfo.contactExists) {
+ Log.d(TAG, "Contact lookup done. Local contact found, no image.");
+ } else {
+ Log.d(
+ TAG,
+ "Contact lookup done. Local contact not found and"
+ + " no remote lookup service available.");
+ }
+ clearCallbacks(callId);
+ }
+ }
+ }
+
+ class PhoneNumberServiceListener
+ implements PhoneNumberService.NumberLookupListener, PhoneNumberService.ImageLookupListener {
+
+ private final String mCallId;
+
+ PhoneNumberServiceListener(String callId) {
+ mCallId = callId;
+ }
+
+ @Override
+ public void onPhoneNumberInfoComplete(final PhoneNumberService.PhoneNumberInfo info) {
+ // If we got a miss, this is the end of the lookup pipeline,
+ // so clear the callbacks and return.
+ if (info == null) {
+ Log.d(TAG, "Contact lookup done. Remote contact not found.");
+ clearCallbacks(mCallId);
+ return;
+ }
+
+ ContactCacheEntry entry = new ContactCacheEntry();
+ entry.namePrimary = info.getDisplayName();
+ entry.number = info.getNumber();
+ entry.contactLookupResult = info.getLookupSource();
+ final int type = info.getPhoneType();
+ final String label = info.getPhoneLabel();
+ if (type == Phone.TYPE_CUSTOM) {
+ entry.label = label;
+ } else {
+ final CharSequence typeStr = Phone.getTypeLabel(mContext.getResources(), type, label);
+ entry.label = typeStr == null ? null : typeStr.toString();
+ }
+ synchronized (mInfoMap) {
+ final ContactCacheEntry oldEntry = mInfoMap.get(mCallId);
+ if (oldEntry != null) {
+ // Location is only obtained from local lookup so persist
+ // the value for remote lookups. Once we have a name this
+ // field is no longer used; it is persisted here in case
+ // the UI is ever changed to use it.
+ entry.location = oldEntry.location;
+ // Contact specific ringtone is obtained from local lookup.
+ entry.contactRingtoneUri = oldEntry.contactRingtoneUri;
+ }
+
+ // If no image and it's a business, switch to using the default business avatar.
+ if (info.getImageUrl() == null && info.isBusiness()) {
+ Log.d(TAG, "Business has no image. Using default.");
+ entry.photo = mContext.getResources().getDrawable(R.drawable.img_business);
+ entry.photoType = ContactPhotoType.BUSINESS;
+ }
+
+ mInfoMap.put(mCallId, entry);
+ }
+ sendInfoNotifications(mCallId, entry);
+
+ entry.hasPhotoToLoad = info.getImageUrl() != null;
+
+ // If there is no image then we should not expect another callback.
+ if (!entry.hasPhotoToLoad) {
+ // We're done, so clear callbacks
+ clearCallbacks(mCallId);
+ }
+ }
+
+ @Override
+ public void onImageFetchComplete(Bitmap bitmap) {
+ loadImage(null, bitmap, mCallId);
+ onImageLoadComplete(TOKEN_UPDATE_PHOTO_FOR_CALL_STATE, null, bitmap, mCallId);
+ }
+ }
+}
diff --git a/java/com/android/incallui/ContactsAsyncHelper.java b/java/com/android/incallui/ContactsAsyncHelper.java
new file mode 100644
index 000000000..08ff74d0e
--- /dev/null
+++ b/java/com/android/incallui/ContactsAsyncHelper.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2008 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.incallui;
+
+import android.app.Notification;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.support.annotation.MainThread;
+import android.support.annotation.WorkerThread;
+import java.io.IOException;
+import java.io.InputStream;
+
+/** Helper class for loading contacts photo asynchronously. */
+public class ContactsAsyncHelper {
+
+ /** Interface for a WorkerHandler result return. */
+ public interface OnImageLoadCompleteListener {
+
+ /**
+ * Called when the image load is complete. Must be called in main thread.
+ *
+ * @param token Integer passed in {@link ContactsAsyncHelper#startObtainPhotoAsync(int, Context,
+ * Uri, OnImageLoadCompleteListener, Object)}.
+ * @param photo Drawable object obtained by the async load.
+ * @param photoIcon Bitmap object obtained by the async load.
+ * @param cookie Object passed in {@link ContactsAsyncHelper#startObtainPhotoAsync(int, Context,
+ * Uri, OnImageLoadCompleteListener, Object)}. Can be null iff. the original cookie is null.
+ */
+ @MainThread
+ void onImageLoadComplete(int token, Drawable photo, Bitmap photoIcon, Object cookie);
+
+ /** Called when image is loaded to udpate data. Must be called in worker thread. */
+ @WorkerThread
+ void onImageLoaded(int token, Drawable photo, Bitmap photoIcon, Object cookie);
+ }
+
+ // constants
+ private static final int EVENT_LOAD_IMAGE = 1;
+ /** Handler run on a worker thread to load photo asynchronously. */
+ private static Handler sThreadHandler;
+ /** For forcing the system to call its constructor */
+ @SuppressWarnings("unused")
+ private static ContactsAsyncHelper sInstance;
+
+ static {
+ sInstance = new ContactsAsyncHelper();
+ }
+
+ private final Handler mResultHandler =
+ /** A handler that handles message to call listener notifying UI change on main thread. */
+ new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ WorkerArgs args = (WorkerArgs) msg.obj;
+ switch (msg.arg1) {
+ case EVENT_LOAD_IMAGE:
+ if (args.listener != null) {
+ Log.d(
+ this,
+ "Notifying listener: "
+ + args.listener.toString()
+ + " image: "
+ + args.displayPhotoUri
+ + " completed");
+ args.listener.onImageLoadComplete(
+ msg.what, args.photo, args.photoIcon, args.cookie);
+ }
+ break;
+ default:
+ }
+ }
+ };
+
+ /** Private constructor for static class */
+ private ContactsAsyncHelper() {
+ HandlerThread thread = new HandlerThread("ContactsAsyncWorker");
+ thread.start();
+ sThreadHandler = new WorkerHandler(thread.getLooper());
+ }
+
+ /**
+ * Starts an asynchronous image load. After finishing the load, {@link
+ * OnImageLoadCompleteListener#onImageLoadComplete(int, Drawable, Bitmap, Object)} will be called.
+ *
+ * @param token Arbitrary integer which will be returned as the first argument of {@link
+ * OnImageLoadCompleteListener#onImageLoadComplete(int, Drawable, Bitmap, Object)}
+ * @param context Context object used to do the time-consuming operation.
+ * @param displayPhotoUri Uri to be used to fetch the photo
+ * @param listener Callback object which will be used when the asynchronous load is done. Can be
+ * null, which means only the asynchronous load is done while there's no way to obtain the
+ * loaded photos.
+ * @param cookie Arbitrary object the caller wants to remember, which will become the fourth
+ * argument of {@link OnImageLoadCompleteListener#onImageLoadComplete(int, Drawable, Bitmap,
+ * Object)}. Can be null, at which the callback will also has null for the argument.
+ */
+ public static final void startObtainPhotoAsync(
+ int token,
+ Context context,
+ Uri displayPhotoUri,
+ OnImageLoadCompleteListener listener,
+ Object cookie) {
+ // in case the source caller info is null, the URI will be null as well.
+ // just update using the placeholder image in this case.
+ if (displayPhotoUri == null) {
+ Log.e("startObjectPhotoAsync", "Uri is missing");
+ return;
+ }
+
+ // Added additional Cookie field in the callee to handle arguments
+ // sent to the callback function.
+
+ // setup arguments
+ WorkerArgs args = new WorkerArgs();
+ args.cookie = cookie;
+ args.context = context;
+ args.displayPhotoUri = displayPhotoUri;
+ args.listener = listener;
+
+ // setup message arguments
+ Message msg = sThreadHandler.obtainMessage(token);
+ msg.arg1 = EVENT_LOAD_IMAGE;
+ msg.obj = args;
+
+ Log.d(
+ "startObjectPhotoAsync",
+ "Begin loading image: " + args.displayPhotoUri + ", displaying default image for now.");
+
+ // notify the thread to begin working
+ sThreadHandler.sendMessage(msg);
+ }
+
+ private static final class WorkerArgs {
+
+ public Context context;
+ public Uri displayPhotoUri;
+ public Drawable photo;
+ public Bitmap photoIcon;
+ public Object cookie;
+ public OnImageLoadCompleteListener listener;
+ }
+
+ /** Thread worker class that handles the task of opening the stream and loading the images. */
+ private class WorkerHandler extends Handler {
+
+ public WorkerHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ WorkerArgs args = (WorkerArgs) msg.obj;
+
+ switch (msg.arg1) {
+ case EVENT_LOAD_IMAGE:
+ InputStream inputStream = null;
+ try {
+ try {
+ inputStream = args.context.getContentResolver().openInputStream(args.displayPhotoUri);
+ } catch (Exception e) {
+ Log.e(this, "Error opening photo input stream", e);
+ }
+
+ if (inputStream != null) {
+ args.photo = Drawable.createFromStream(inputStream, args.displayPhotoUri.toString());
+
+ // This assumes Drawable coming from contact database is usually
+ // BitmapDrawable and thus we can have (down)scaled version of it.
+ args.photoIcon = getPhotoIconWhenAppropriate(args.context, args.photo);
+
+ Log.d(
+ ContactsAsyncHelper.this,
+ "Loading image: "
+ + msg.arg1
+ + " token: "
+ + msg.what
+ + " image URI: "
+ + args.displayPhotoUri);
+ } else {
+ args.photo = null;
+ args.photoIcon = null;
+ Log.d(
+ ContactsAsyncHelper.this,
+ "Problem with image: "
+ + msg.arg1
+ + " token: "
+ + msg.what
+ + " image URI: "
+ + args.displayPhotoUri
+ + ", using default image.");
+ }
+ if (args.listener != null) {
+ args.listener.onImageLoaded(msg.what, args.photo, args.photoIcon, args.cookie);
+ }
+ } finally {
+ if (inputStream != null) {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ Log.e(this, "Unable to close input stream.", e);
+ }
+ }
+ }
+ break;
+ default:
+ }
+
+ // send the reply to the enclosing class.
+ Message reply = ContactsAsyncHelper.this.mResultHandler.obtainMessage(msg.what);
+ reply.arg1 = msg.arg1;
+ reply.obj = msg.obj;
+ reply.sendToTarget();
+ }
+
+ /**
+ * Returns a Bitmap object suitable for {@link Notification}'s large icon. This might return
+ * null when the given Drawable isn't BitmapDrawable, or if the system fails to create a scaled
+ * Bitmap for the Drawable.
+ */
+ private Bitmap getPhotoIconWhenAppropriate(Context context, Drawable photo) {
+ if (!(photo instanceof BitmapDrawable)) {
+ return null;
+ }
+ int iconSize = context.getResources().getDimensionPixelSize(R.dimen.notification_icon_size);
+ Bitmap orgBitmap = ((BitmapDrawable) photo).getBitmap();
+ int orgWidth = orgBitmap.getWidth();
+ int orgHeight = orgBitmap.getHeight();
+ int longerEdge = orgWidth > orgHeight ? orgWidth : orgHeight;
+ // We want downscaled one only when the original icon is too big.
+ if (longerEdge > iconSize) {
+ float ratio = ((float) longerEdge) / iconSize;
+ int newWidth = (int) (orgWidth / ratio);
+ int newHeight = (int) (orgHeight / ratio);
+ // If the longer edge is much longer than the shorter edge, the latter may
+ // become 0 which will cause a crash.
+ if (newWidth <= 0 || newHeight <= 0) {
+ Log.w(this, "Photo icon's width or height become 0.");
+ return null;
+ }
+
+ // It is sure ratio >= 1.0f in any case and thus the newly created Bitmap
+ // should be smaller than the original.
+ return Bitmap.createScaledBitmap(orgBitmap, newWidth, newHeight, true);
+ } else {
+ return orgBitmap;
+ }
+ }
+ }
+}
diff --git a/java/com/android/incallui/ContactsPreferencesFactory.java b/java/com/android/incallui/ContactsPreferencesFactory.java
new file mode 100644
index 000000000..429de7bc9
--- /dev/null
+++ b/java/com/android/incallui/ContactsPreferencesFactory.java
@@ -0,0 +1,56 @@
+/*
+ * 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.incallui;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.support.v4.os.UserManagerCompat;
+import com.android.contacts.common.preference.ContactsPreferences;
+
+/** Factory class for {@link ContactsPreferences}. */
+public class ContactsPreferencesFactory {
+
+ private static boolean sUseTestInstance;
+ private static ContactsPreferences sTestInstance;
+
+ /**
+ * Creates a new {@link ContactsPreferences} object if possible.
+ *
+ * @param context the context to use when creating the ContactsPreferences.
+ * @return a new ContactsPreferences object or {@code null} if the user is locked.
+ */
+ @Nullable
+ public static ContactsPreferences newContactsPreferences(Context context) {
+ if (sUseTestInstance) {
+ return sTestInstance;
+ }
+ if (UserManagerCompat.isUserUnlocked(context)) {
+ return new ContactsPreferences(context);
+ }
+ return null;
+ }
+
+ /**
+ * Sets the instance to be returned by all calls to {@link #newContactsPreferences(Context)}.
+ *
+ * @param testInstance the instance to return.
+ */
+ static void setTestInstance(ContactsPreferences testInstance) {
+ sUseTestInstance = true;
+ sTestInstance = testInstance;
+ }
+}
diff --git a/java/com/android/incallui/DialpadFragment.java b/java/com/android/incallui/DialpadFragment.java
new file mode 100644
index 000000000..7f494aa61
--- /dev/null
+++ b/java/com/android/incallui/DialpadFragment.java
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2013 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.incallui;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.method.DialerKeyListener;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnKeyListener;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+import com.android.dialer.dialpadview.DialpadKeyButton;
+import com.android.dialer.dialpadview.DialpadKeyButton.OnPressedListener;
+import com.android.dialer.dialpadview.DialpadView;
+import com.android.incallui.DialpadPresenter.DialpadUi;
+import com.android.incallui.baseui.BaseFragment;
+import java.util.Map;
+
+/** Fragment for call control buttons */
+public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadUi>
+ implements DialpadUi, OnKeyListener, OnClickListener, OnPressedListener {
+
+ /** Hash Map to map a view id to a character */
+ private static final Map<Integer, Character> mDisplayMap = new ArrayMap<>();
+
+ /** Set up the static maps */
+ static {
+ // Map the buttons to the display characters
+ mDisplayMap.put(R.id.one, '1');
+ mDisplayMap.put(R.id.two, '2');
+ mDisplayMap.put(R.id.three, '3');
+ mDisplayMap.put(R.id.four, '4');
+ mDisplayMap.put(R.id.five, '5');
+ mDisplayMap.put(R.id.six, '6');
+ mDisplayMap.put(R.id.seven, '7');
+ mDisplayMap.put(R.id.eight, '8');
+ mDisplayMap.put(R.id.nine, '9');
+ mDisplayMap.put(R.id.zero, '0');
+ mDisplayMap.put(R.id.pound, '#');
+ mDisplayMap.put(R.id.star, '*');
+ }
+
+ private final int[] mButtonIds =
+ new int[] {
+ R.id.zero,
+ R.id.one,
+ R.id.two,
+ R.id.three,
+ R.id.four,
+ R.id.five,
+ R.id.six,
+ R.id.seven,
+ R.id.eight,
+ R.id.nine,
+ R.id.star,
+ R.id.pound
+ };
+ private EditText mDtmfDialerField;
+ // KeyListener used with the "dialpad digits" EditText widget.
+ private DTMFKeyListener mDialerKeyListener;
+ private DialpadView mDialpadView;
+ private int mCurrentTextColor;
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.dialpad_back) {
+ getActivity().onBackPressed();
+ }
+ }
+
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ Log.d(this, "onKey: keyCode " + keyCode + ", view " + v);
+
+ if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
+ int viewId = v.getId();
+ if (mDisplayMap.containsKey(viewId)) {
+ switch (event.getAction()) {
+ case KeyEvent.ACTION_DOWN:
+ if (event.getRepeatCount() == 0) {
+ getPresenter().processDtmf(mDisplayMap.get(viewId));
+ }
+ break;
+ case KeyEvent.ACTION_UP:
+ getPresenter().stopDtmf();
+ break;
+ }
+ // do not return true [handled] here, since we want the
+ // press / click animation to be handled by the framework.
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public DialpadPresenter createPresenter() {
+ return new DialpadPresenter();
+ }
+
+ @Override
+ public DialpadPresenter.DialpadUi getUi() {
+ return this;
+ }
+
+ // TODO Adds hardware keyboard listener
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ final View parent = inflater.inflate(R.layout.incall_dialpad_fragment, container, false);
+ mDialpadView = (DialpadView) parent.findViewById(R.id.dialpad_view);
+ mDialpadView.setCanDigitsBeEdited(false);
+ mDialpadView.setBackgroundResource(R.color.incall_dialpad_background);
+ mDtmfDialerField = (EditText) parent.findViewById(R.id.digits);
+ if (mDtmfDialerField != null) {
+ mDialerKeyListener = new DTMFKeyListener();
+ mDtmfDialerField.setKeyListener(mDialerKeyListener);
+ // remove the long-press context menus that support
+ // the edit (copy / paste / select) functions.
+ mDtmfDialerField.setLongClickable(false);
+ mDtmfDialerField.setElegantTextHeight(false);
+ configureKeypadListeners();
+ }
+ View backButton = mDialpadView.findViewById(R.id.dialpad_back);
+ backButton.setVisibility(View.VISIBLE);
+ backButton.setOnClickListener(this);
+
+ return parent;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateColors();
+ }
+
+ public void updateColors() {
+ int textColor = InCallPresenter.getInstance().getThemeColorManager().getPrimaryColor();
+
+ if (mCurrentTextColor == textColor) {
+ return;
+ }
+
+ DialpadKeyButton dialpadKey;
+ for (int i = 0; i < mButtonIds.length; i++) {
+ dialpadKey = (DialpadKeyButton) mDialpadView.findViewById(mButtonIds[i]);
+ ((TextView) dialpadKey.findViewById(R.id.dialpad_key_number)).setTextColor(textColor);
+ }
+
+ mCurrentTextColor = textColor;
+ }
+
+ @Override
+ public void onDestroyView() {
+ mDialerKeyListener = null;
+ super.onDestroyView();
+ }
+
+ /**
+ * Getter for Dialpad text.
+ *
+ * @return String containing current Dialpad EditText text.
+ */
+ public String getDtmfText() {
+ return mDtmfDialerField.getText().toString();
+ }
+
+ /**
+ * Sets the Dialpad text field with some text.
+ *
+ * @param text Text to set Dialpad EditText to.
+ */
+ public void setDtmfText(String text) {
+ mDtmfDialerField.setText(PhoneNumberUtilsCompat.createTtsSpannable(text));
+ }
+
+ @Override
+ public void setVisible(boolean on) {
+ if (on) {
+ getView().setVisibility(View.VISIBLE);
+ } else {
+ getView().setVisibility(View.INVISIBLE);
+ }
+ }
+
+ /** Starts the slide up animation for the Dialpad keys when the Dialpad is revealed. */
+ public void animateShowDialpad() {
+ final DialpadView dialpadView = (DialpadView) getView().findViewById(R.id.dialpad_view);
+ dialpadView.animateShow();
+ }
+
+ @Override
+ public void appendDigitsToField(char digit) {
+ if (mDtmfDialerField != null) {
+ // TODO: maybe *don't* manually append this digit if
+ // mDialpadDigits is focused and this key came from the HW
+ // keyboard, since in that case the EditText field will
+ // get the key event directly and automatically appends
+ // whetever the user types.
+ // (Or, a cleaner fix would be to just make mDialpadDigits
+ // *not* handle HW key presses. That seems to be more
+ // complicated than just setting focusable="false" on it,
+ // though.)
+ mDtmfDialerField.getText().append(digit);
+ }
+ }
+
+ /** Called externally (from InCallScreen) to play a DTMF Tone. */
+ /* package */ boolean onDialerKeyDown(KeyEvent event) {
+ Log.d(this, "Notifying dtmf key down.");
+ if (mDialerKeyListener != null) {
+ return mDialerKeyListener.onKeyDown(event);
+ } else {
+ return false;
+ }
+ }
+
+ /** Called externally (from InCallScreen) to cancel the last DTMF Tone played. */
+ public boolean onDialerKeyUp(KeyEvent event) {
+ Log.d(this, "Notifying dtmf key up.");
+ if (mDialerKeyListener != null) {
+ return mDialerKeyListener.onKeyUp(event);
+ } else {
+ return false;
+ }
+ }
+
+ private void configureKeypadListeners() {
+ DialpadKeyButton dialpadKey;
+ for (int i = 0; i < mButtonIds.length; i++) {
+ dialpadKey = (DialpadKeyButton) mDialpadView.findViewById(mButtonIds[i]);
+ dialpadKey.setOnKeyListener(this);
+ dialpadKey.setOnClickListener(this);
+ dialpadKey.setOnPressedListener(this);
+ }
+ }
+
+ @Override
+ public void onPressed(View view, boolean pressed) {
+ if (pressed && mDisplayMap.containsKey(view.getId())) {
+ Log.d(this, "onPressed: " + pressed + " " + mDisplayMap.get(view.getId()));
+ getPresenter().processDtmf(mDisplayMap.get(view.getId()));
+ }
+ if (!pressed) {
+ Log.d(this, "onPressed: " + pressed);
+ getPresenter().stopDtmf();
+ }
+ }
+
+ /**
+ * LinearLayout with getter and setter methods for the translationY property using floats, for
+ * animation purposes.
+ */
+ public static class DialpadSlidingLinearLayout extends LinearLayout {
+
+ public DialpadSlidingLinearLayout(Context context) {
+ super(context);
+ }
+
+ public DialpadSlidingLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public DialpadSlidingLinearLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public float getYFraction() {
+ final int height = getHeight();
+ if (height == 0) {
+ return 0;
+ }
+ return getTranslationY() / height;
+ }
+
+ public void setYFraction(float yFraction) {
+ setTranslationY(yFraction * getHeight());
+ }
+ }
+
+ /**
+ * Our own key listener, specialized for dealing with DTMF codes. 1. Ignore the backspace since it
+ * is irrelevant. 2. Allow ONLY valid DTMF characters to generate a tone and be sent as a DTMF
+ * code. 3. All other remaining characters are handled by the superclass.
+ *
+ * <p>This code is purely here to handle events from the hardware keyboard while the DTMF dialpad
+ * is up.
+ */
+ private class DTMFKeyListener extends DialerKeyListener {
+
+ /**
+ * Overrides the characters used in {@link DialerKeyListener#CHARACTERS} These are the valid
+ * dtmf characters.
+ */
+ public final char[] DTMF_CHARACTERS =
+ new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '#', '*'};
+
+ private DTMFKeyListener() {
+ super();
+ }
+
+ /** Overriden to return correct DTMF-dialable characters. */
+ @Override
+ protected char[] getAcceptedChars() {
+ return DTMF_CHARACTERS;
+ }
+
+ /** special key listener ignores backspace. */
+ @Override
+ public boolean backspace(View view, Editable content, int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Overriden so that with each valid button press, we start sending a dtmf code and play a local
+ * dtmf tone.
+ */
+ @Override
+ public boolean onKeyDown(View view, Editable content, int keyCode, KeyEvent event) {
+ // if (DBG) log("DTMFKeyListener.onKeyDown, keyCode " + keyCode + ", view " + view);
+
+ // find the character
+ char c = (char) lookup(event, content);
+
+ // if not a long press, and parent onKeyDown accepts the input
+ if (event.getRepeatCount() == 0 && super.onKeyDown(view, content, keyCode, event)) {
+
+ boolean keyOK = ok(getAcceptedChars(), c);
+
+ // if the character is a valid dtmf code, start playing the tone and send the
+ // code.
+ if (keyOK) {
+ Log.d(this, "DTMFKeyListener reading '" + c + "' from input.");
+ getPresenter().processDtmf(c);
+ } else {
+ Log.d(this, "DTMFKeyListener rejecting '" + c + "' from input.");
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Overriden so that with each valid button up, we stop sending a dtmf code and the dtmf tone.
+ */
+ @Override
+ public boolean onKeyUp(View view, Editable content, int keyCode, KeyEvent event) {
+ // if (DBG) log("DTMFKeyListener.onKeyUp, keyCode " + keyCode + ", view " + view);
+
+ super.onKeyUp(view, content, keyCode, event);
+
+ // find the character
+ char c = (char) lookup(event, content);
+
+ boolean keyOK = ok(getAcceptedChars(), c);
+
+ if (keyOK) {
+ Log.d(this, "Stopping the tone for '" + c + "'");
+ getPresenter().stopDtmf();
+ return true;
+ }
+
+ return false;
+ }
+
+ /** Handle individual keydown events when we DO NOT have an Editable handy. */
+ public boolean onKeyDown(KeyEvent event) {
+ char c = lookup(event);
+ Log.d(this, "DTMFKeyListener.onKeyDown: event '" + c + "'");
+
+ // if not a long press, and parent onKeyDown accepts the input
+ if (event.getRepeatCount() == 0 && c != 0) {
+ // if the character is a valid dtmf code, start playing the tone and send the
+ // code.
+ if (ok(getAcceptedChars(), c)) {
+ Log.d(this, "DTMFKeyListener reading '" + c + "' from input.");
+ getPresenter().processDtmf(c);
+ return true;
+ } else {
+ Log.d(this, "DTMFKeyListener rejecting '" + c + "' from input.");
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Handle individual keyup events.
+ *
+ * @param event is the event we are trying to stop. If this is null, then we just force-stop the
+ * last tone without checking if the event is an acceptable dialer event.
+ */
+ public boolean onKeyUp(KeyEvent event) {
+ if (event == null) {
+ //the below piece of code sends stopDTMF event unnecessarily even when a null event
+ //is received, hence commenting it.
+ /*if (DBG) log("Stopping the last played tone.");
+ stopTone();*/
+ return true;
+ }
+
+ char c = lookup(event);
+ Log.d(this, "DTMFKeyListener.onKeyUp: event '" + c + "'");
+
+ // TODO: stopTone does not take in character input, we may want to
+ // consider checking for this ourselves.
+ if (ok(getAcceptedChars(), c)) {
+ Log.d(this, "Stopping the tone for '" + c + "'");
+ getPresenter().stopDtmf();
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Find the Dialer Key mapped to this event.
+ *
+ * @return The char value of the input event, otherwise 0 if no matching character was found.
+ */
+ private char lookup(KeyEvent event) {
+ // This code is similar to {@link DialerKeyListener#lookup(KeyEvent, Spannable) lookup}
+ int meta = event.getMetaState();
+ int number = event.getNumber();
+
+ if (!((meta & (KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)) == 0) || (number == 0)) {
+ int match = event.getMatch(getAcceptedChars(), meta);
+ number = (match != 0) ? match : number;
+ }
+
+ return (char) number;
+ }
+
+ /** Check to see if the keyEvent is dialable. */
+ boolean isKeyEventAcceptable(KeyEvent event) {
+ return (ok(getAcceptedChars(), lookup(event)));
+ }
+ }
+}
diff --git a/java/com/android/incallui/DialpadPresenter.java b/java/com/android/incallui/DialpadPresenter.java
new file mode 100644
index 000000000..7a784c279
--- /dev/null
+++ b/java/com/android/incallui/DialpadPresenter.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2013 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.incallui;
+
+import android.telephony.PhoneNumberUtils;
+import com.android.incallui.DialpadPresenter.DialpadUi;
+import com.android.incallui.baseui.Presenter;
+import com.android.incallui.baseui.Ui;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.TelecomAdapter;
+
+/** Logic for call buttons. */
+public class DialpadPresenter extends Presenter<DialpadUi>
+ implements InCallPresenter.InCallStateListener {
+
+ private DialerCall mCall;
+
+ @Override
+ public void onUiReady(DialpadUi ui) {
+ super.onUiReady(ui);
+ InCallPresenter.getInstance().addListener(this);
+ mCall = CallList.getInstance().getOutgoingOrActive();
+ }
+
+ @Override
+ public void onUiUnready(DialpadUi ui) {
+ super.onUiUnready(ui);
+ InCallPresenter.getInstance().removeListener(this);
+ }
+
+ @Override
+ public void onStateChange(
+ InCallPresenter.InCallState oldState,
+ InCallPresenter.InCallState newState,
+ CallList callList) {
+ mCall = callList.getOutgoingOrActive();
+ Log.d(this, "DialpadPresenter mCall = " + mCall);
+ }
+
+ /**
+ * Processes the specified digit as a DTMF key, by playing the appropriate DTMF tone, and
+ * appending the digit to the EditText field that displays the DTMF digits sent so far.
+ */
+ public final void processDtmf(char c) {
+ Log.d(this, "Processing dtmf key " + c);
+ // if it is a valid key, then update the display and send the dtmf tone.
+ if (PhoneNumberUtils.is12Key(c) && mCall != null) {
+ Log.d(this, "updating display and sending dtmf tone for '" + c + "'");
+
+ // Append this key to the "digits" widget.
+ DialpadUi dialpadUi = getUi();
+ if (dialpadUi != null) {
+ dialpadUi.appendDigitsToField(c);
+ }
+ // Plays the tone through Telecom.
+ TelecomAdapter.getInstance().playDtmfTone(mCall.getId(), c);
+ } else {
+ Log.d(this, "ignoring dtmf request for '" + c + "'");
+ }
+ }
+
+ /** Stops the local tone based on the phone type. */
+ public void stopDtmf() {
+ if (mCall != null) {
+ Log.d(this, "stopping remote tone");
+ TelecomAdapter.getInstance().stopDtmfTone(mCall.getId());
+ }
+ }
+
+ public interface DialpadUi extends Ui {
+
+ void setVisible(boolean on);
+
+ void appendDigitsToField(char digit);
+ }
+}
diff --git a/java/com/android/incallui/ExternalCallNotifier.java b/java/com/android/incallui/ExternalCallNotifier.java
new file mode 100644
index 000000000..466e12a6d
--- /dev/null
+++ b/java/com/android/incallui/ExternalCallNotifier.java
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2017 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.incallui;
+
+import android.annotation.TargetApi;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.telecom.Call;
+import android.telecom.PhoneAccount;
+import android.telecom.VideoProfile;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import com.android.contacts.common.ContactsUtils;
+import com.android.contacts.common.compat.CallCompat;
+import com.android.contacts.common.preference.ContactsPreferences;
+import com.android.contacts.common.util.BitmapUtil;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.DialerCallDelegate;
+import com.android.incallui.call.ExternalCallList;
+import com.android.incallui.latencyreport.LatencyReport;
+import com.android.incallui.util.TelecomCallUtil;
+import java.util.Map;
+
+/**
+ * Handles the display of notifications for "external calls".
+ *
+ * <p>External calls are a representation of a call which is in progress on the user's other device
+ * (e.g. another phone, or a watch).
+ */
+public class ExternalCallNotifier implements ExternalCallList.ExternalCallListener {
+
+ /** Tag used with the notification manager to uniquely identify external call notifications. */
+ private static final String NOTIFICATION_TAG = "EXTERNAL_CALL";
+
+ private static final int SUMMARY_ID = -1;
+ private final Context mContext;
+ private final ContactInfoCache mContactInfoCache;
+ private Map<Call, NotificationInfo> mNotifications = new ArrayMap<>();
+ private int mNextUniqueNotificationId;
+ private ContactsPreferences mContactsPreferences;
+ private boolean mShowingSummary;
+
+ /** Initializes a new instance of the external call notifier. */
+ public ExternalCallNotifier(
+ @NonNull Context context, @NonNull ContactInfoCache contactInfoCache) {
+ mContext = context;
+ mContactsPreferences = ContactsPreferencesFactory.newContactsPreferences(mContext);
+ mContactInfoCache = contactInfoCache;
+ }
+
+ /**
+ * Handles the addition of a new external call by showing a new notification. Triggered by {@link
+ * CallList#onCallAdded(android.telecom.Call)}.
+ */
+ @Override
+ public void onExternalCallAdded(android.telecom.Call call) {
+ Log.i(this, "onExternalCallAdded " + call);
+ if (mNotifications.containsKey(call)) {
+ throw new IllegalArgumentException();
+ }
+ NotificationInfo info = new NotificationInfo(call, mNextUniqueNotificationId++);
+ mNotifications.put(call, info);
+
+ showNotifcation(info);
+ }
+
+ /**
+ * Handles the removal of an external call by hiding its associated notification. Triggered by
+ * {@link CallList#onCallRemoved(android.telecom.Call)}.
+ */
+ @Override
+ public void onExternalCallRemoved(android.telecom.Call call) {
+ Log.i(this, "onExternalCallRemoved " + call);
+
+ dismissNotification(call);
+ }
+
+ /** Handles updates to an external call. */
+ @Override
+ public void onExternalCallUpdated(Call call) {
+ if (!mNotifications.containsKey(call)) {
+ throw new IllegalArgumentException();
+ }
+ postNotification(mNotifications.get(call));
+ }
+
+ @Override
+ public void onExternalCallPulled(Call call) {
+ // no-op; if an external call is pulled, it will be removed via onExternalCallRemoved.
+ }
+
+ /**
+ * Initiates a call pull given a notification ID.
+ *
+ * @param notificationId The notification ID associated with the external call which is to be
+ * pulled.
+ */
+ @TargetApi(VERSION_CODES.N_MR1)
+ public void pullExternalCall(int notificationId) {
+ for (NotificationInfo info : mNotifications.values()) {
+ if (info.getNotificationId() == notificationId
+ && CallCompat.canPullExternalCall(info.getCall())) {
+ info.getCall().pullExternalCall();
+ return;
+ }
+ }
+ }
+
+ /**
+ * Shows a notification for a new external call. Performs a contact cache lookup to find any
+ * associated photo and information for the call.
+ */
+ private void showNotifcation(final NotificationInfo info) {
+ // We make a call to the contact info cache to query for supplemental data to what the
+ // call provides. This includes the contact name and photo.
+ // This callback will always get called immediately and synchronously with whatever data
+ // it has available, and may make a subsequent call later (same thread) if it had to
+ // call into the contacts provider for more data.
+ DialerCall dialerCall =
+ new DialerCall(
+ mContext,
+ new DialerCallDelegateStub(),
+ info.getCall(),
+ new LatencyReport(),
+ false /* registerCallback */);
+
+ mContactInfoCache.findInfo(
+ dialerCall,
+ false /* isIncoming */,
+ new ContactInfoCache.ContactInfoCacheCallback() {
+ @Override
+ public void onContactInfoComplete(
+ String callId, ContactInfoCache.ContactCacheEntry entry) {
+
+ // Ensure notification still exists as the external call could have been
+ // removed during async contact info lookup.
+ if (mNotifications.containsKey(info.getCall())) {
+ saveContactInfo(info, entry);
+ }
+ }
+
+ @Override
+ public void onImageLoadComplete(String callId, ContactInfoCache.ContactCacheEntry entry) {
+
+ // Ensure notification still exists as the external call could have been
+ // removed during async contact info lookup.
+ if (mNotifications.containsKey(info.getCall())) {
+ savePhoto(info, entry);
+ }
+ }
+ });
+ }
+
+ /** Dismisses a notification for an external call. */
+ private void dismissNotification(Call call) {
+ if (!mNotifications.containsKey(call)) {
+ throw new IllegalArgumentException();
+ }
+
+ NotificationManager notificationManager =
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.cancel(NOTIFICATION_TAG, mNotifications.get(call).getNotificationId());
+
+ mNotifications.remove(call);
+
+ if (mShowingSummary && mNotifications.size() <= 1) {
+ // Where a summary notification is showing and there is now not enough notifications to
+ // necessitate a summary, cancel the summary.
+ notificationManager.cancel(NOTIFICATION_TAG, SUMMARY_ID);
+ mShowingSummary = false;
+
+ // If there is still a single call requiring a notification, re-post the notification as a
+ // standalone notification without a summary notification.
+ if (mNotifications.size() == 1) {
+ postNotification(mNotifications.values().iterator().next());
+ }
+ }
+ }
+
+ /**
+ * Attempts to build a large icon to use for the notification based on the contact info and post
+ * the updated notification to the notification manager.
+ */
+ private void savePhoto(NotificationInfo info, ContactInfoCache.ContactCacheEntry entry) {
+ Bitmap largeIcon = getLargeIconToDisplay(mContext, entry, info.getCall());
+ if (largeIcon != null) {
+ largeIcon = getRoundedIcon(mContext, largeIcon);
+ }
+ info.setLargeIcon(largeIcon);
+ postNotification(info);
+ }
+
+ /**
+ * Builds and stores the contact information the notification will display and posts the updated
+ * notification to the notification manager.
+ */
+ private void saveContactInfo(NotificationInfo info, ContactInfoCache.ContactCacheEntry entry) {
+ info.setContentTitle(getContentTitle(mContext, mContactsPreferences, entry, info.getCall()));
+ info.setPersonReference(getPersonReference(entry, info.getCall()));
+ postNotification(info);
+ }
+
+ /** Rebuild an existing or show a new notification given {@link NotificationInfo}. */
+ private void postNotification(NotificationInfo info) {
+ Notification.Builder builder = new Notification.Builder(mContext);
+ // Set notification as ongoing since calls are long-running versus a point-in-time notice.
+ builder.setOngoing(true);
+ // Make the notification prioritized over the other normal notifications.
+ builder.setPriority(Notification.PRIORITY_HIGH);
+ builder.setGroup(NOTIFICATION_TAG);
+
+ boolean isVideoCall = VideoProfile.isVideo(info.getCall().getDetails().getVideoState());
+ // Set the content ("Ongoing call on another device")
+ builder.setContentText(
+ mContext.getString(
+ isVideoCall
+ ? R.string.notification_external_video_call
+ : R.string.notification_external_call));
+ builder.setSmallIcon(R.drawable.quantum_ic_call_white_24);
+ builder.setContentTitle(info.getContentTitle());
+ builder.setLargeIcon(info.getLargeIcon());
+ builder.setColor(mContext.getResources().getColor(R.color.dialer_theme_color));
+ builder.addPerson(info.getPersonReference());
+
+ // Where the external call supports being transferred to the local device, add an action
+ // to the notification to initiate the call pull process.
+ if (CallCompat.canPullExternalCall(info.getCall())) {
+
+ Intent intent =
+ new Intent(
+ NotificationBroadcastReceiver.ACTION_PULL_EXTERNAL_CALL,
+ null,
+ mContext,
+ NotificationBroadcastReceiver.class);
+ intent.putExtra(
+ NotificationBroadcastReceiver.EXTRA_NOTIFICATION_ID, info.getNotificationId());
+ builder.addAction(
+ new Notification.Action.Builder(
+ R.drawable.quantum_ic_call_white_24,
+ mContext.getString(
+ isVideoCall
+ ? R.string.notification_take_video_call
+ : R.string.notification_take_call),
+ PendingIntent.getBroadcast(mContext, info.getNotificationId(), intent, 0))
+ .build());
+ }
+
+ /**
+ * This builder is used for the notification shown when the device is locked and the user has
+ * set their notification settings to 'hide sensitive content' {@see
+ * Notification.Builder#setPublicVersion}.
+ */
+ Notification.Builder publicBuilder = new Notification.Builder(mContext);
+ publicBuilder.setSmallIcon(R.drawable.quantum_ic_call_white_24);
+ publicBuilder.setColor(mContext.getResources().getColor(R.color.dialer_theme_color));
+
+ builder.setPublicVersion(publicBuilder.build());
+ Notification notification = builder.build();
+
+ NotificationManager notificationManager =
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.notify(NOTIFICATION_TAG, info.getNotificationId(), notification);
+
+ if (!mShowingSummary && mNotifications.size() > 1) {
+ // If the number of notifications shown is > 1, and we're not already showing a group summary,
+ // build one now. This will ensure the like notifications are grouped together.
+
+ Notification.Builder summary = new Notification.Builder(mContext);
+ // Set notification as ongoing since calls are long-running versus a point-in-time notice.
+ summary.setOngoing(true);
+ // Make the notification prioritized over the other normal notifications.
+ summary.setPriority(Notification.PRIORITY_HIGH);
+ summary.setGroup(NOTIFICATION_TAG);
+ summary.setGroupSummary(true);
+ summary.setSmallIcon(R.drawable.quantum_ic_call_white_24);
+ notificationManager.notify(NOTIFICATION_TAG, SUMMARY_ID, summary.build());
+ mShowingSummary = true;
+ }
+ }
+
+ /**
+ * Finds a large icon to display in a notification for a call. For conference calls, a conference
+ * call icon is used, otherwise if contact info is specified, the user's contact photo or avatar
+ * is used.
+ *
+ * @param context The context.
+ * @param contactInfo The contact cache info.
+ * @param call The call.
+ * @return The large icon to use for the notification.
+ */
+ private @Nullable Bitmap getLargeIconToDisplay(
+ Context context, ContactInfoCache.ContactCacheEntry contactInfo, android.telecom.Call call) {
+
+ Bitmap largeIcon = null;
+ if (call.getDetails().hasProperty(android.telecom.Call.Details.PROPERTY_CONFERENCE)
+ && !call.getDetails()
+ .hasProperty(android.telecom.Call.Details.PROPERTY_GENERIC_CONFERENCE)) {
+
+ largeIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.img_conference);
+ }
+ if (contactInfo.photo != null && (contactInfo.photo instanceof BitmapDrawable)) {
+ largeIcon = ((BitmapDrawable) contactInfo.photo).getBitmap();
+ }
+ return largeIcon;
+ }
+
+ /**
+ * Given a bitmap, returns a rounded version of the icon suitable for display in a notification.
+ *
+ * @param context The context.
+ * @param bitmap The bitmap to round.
+ * @return The rounded bitmap.
+ */
+ private @Nullable Bitmap getRoundedIcon(Context context, @Nullable Bitmap bitmap) {
+ if (bitmap == null) {
+ return null;
+ }
+ final int height =
+ (int) context.getResources().getDimension(android.R.dimen.notification_large_icon_height);
+ final int width =
+ (int) context.getResources().getDimension(android.R.dimen.notification_large_icon_width);
+ return BitmapUtil.getRoundedBitmap(bitmap, width, height);
+ }
+
+ /**
+ * Builds a notification content title for a call. If the call is a conference call, it is
+ * identified as such. Otherwise an attempt is made to show an associated contact name or phone
+ * number.
+ *
+ * @param context The context.
+ * @param contactsPreferences Contacts preferences, used to determine the preferred formatting for
+ * contact names.
+ * @param contactInfo The contact info which was looked up in the contact cache.
+ * @param call The call to generate a title for.
+ * @return The content title.
+ */
+ private @Nullable String getContentTitle(
+ Context context,
+ @Nullable ContactsPreferences contactsPreferences,
+ ContactInfoCache.ContactCacheEntry contactInfo,
+ android.telecom.Call call) {
+
+ if (call.getDetails().hasProperty(android.telecom.Call.Details.PROPERTY_CONFERENCE)
+ && !call.getDetails()
+ .hasProperty(android.telecom.Call.Details.PROPERTY_GENERIC_CONFERENCE)) {
+
+ return context.getResources().getString(R.string.conference_call_name);
+ }
+
+ String preferredName =
+ ContactDisplayUtils.getPreferredDisplayName(
+ contactInfo.namePrimary, contactInfo.nameAlternative, contactsPreferences);
+ if (TextUtils.isEmpty(preferredName)) {
+ return TextUtils.isEmpty(contactInfo.number)
+ ? null
+ : BidiFormatter.getInstance()
+ .unicodeWrap(contactInfo.number, TextDirectionHeuristics.LTR);
+ }
+ return preferredName;
+ }
+
+ /**
+ * Gets a "person reference" for a notification, used by the system to determine whether the
+ * notification should be allowed past notification interruption filters.
+ *
+ * @param contactInfo The contact info from cache.
+ * @param call The call.
+ * @return the person reference.
+ */
+ private String getPersonReference(ContactInfoCache.ContactCacheEntry contactInfo, Call call) {
+
+ String number = TelecomCallUtil.getNumber(call);
+ // Query {@link Contacts#CONTENT_LOOKUP_URI} directly with work lookup key is not allowed.
+ // So, do not pass {@link Contacts#CONTENT_LOOKUP_URI} to NotificationManager to avoid
+ // NotificationManager using it.
+ if (contactInfo.lookupUri != null && contactInfo.userType != ContactsUtils.USER_TYPE_WORK) {
+ return contactInfo.lookupUri.toString();
+ } else if (!TextUtils.isEmpty(number)) {
+ return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null).toString();
+ }
+ return "";
+ }
+
+ private static class DialerCallDelegateStub implements DialerCallDelegate {
+
+ @Override
+ public DialerCall getDialerCallFromTelecomCall(Call telecomCall) {
+ return null;
+ }
+ }
+
+ /** Represents a call and associated cached notification data. */
+ private static class NotificationInfo {
+
+ @NonNull private final Call mCall;
+ private final int mNotificationId;
+ @Nullable private String mContentTitle;
+ @Nullable private Bitmap mLargeIcon;
+ @Nullable private String mPersonReference;
+
+ public NotificationInfo(@NonNull Call call, int notificationId) {
+ mCall = call;
+ mNotificationId = notificationId;
+ }
+
+ public Call getCall() {
+ return mCall;
+ }
+
+ public int getNotificationId() {
+ return mNotificationId;
+ }
+
+ public @Nullable String getContentTitle() {
+ return mContentTitle;
+ }
+
+ public void setContentTitle(@Nullable String contentTitle) {
+ mContentTitle = contentTitle;
+ }
+
+ public @Nullable Bitmap getLargeIcon() {
+ return mLargeIcon;
+ }
+
+ public void setLargeIcon(@Nullable Bitmap largeIcon) {
+ mLargeIcon = largeIcon;
+ }
+
+ public @Nullable String getPersonReference() {
+ return mPersonReference;
+ }
+
+ public void setPersonReference(@Nullable String personReference) {
+ mPersonReference = personReference;
+ }
+ }
+}
diff --git a/java/com/android/incallui/InCallActivity.java b/java/com/android/incallui/InCallActivity.java
new file mode 100644
index 000000000..307415916
--- /dev/null
+++ b/java/com/android/incallui/InCallActivity.java
@@ -0,0 +1,756 @@
+/*
+ * 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.incallui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.GradientDrawable.Orientation;
+import android.os.Bundle;
+import android.support.annotation.ColorInt;
+import android.support.annotation.FloatRange;
+import android.support.annotation.Nullable;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.graphics.ColorUtils;
+import android.telecom.DisconnectCause;
+import android.view.KeyEvent;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.compat.ActivityCompat;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.ScreenEvent;
+import com.android.incallui.answer.bindings.AnswerBindings;
+import com.android.incallui.answer.protocol.AnswerScreen;
+import com.android.incallui.answer.protocol.AnswerScreenDelegate;
+import com.android.incallui.answer.protocol.AnswerScreenDelegateFactory;
+import com.android.incallui.answerproximitysensor.PseudoScreenState;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.DialerCall.State;
+import com.android.incallui.call.VideoUtils;
+import com.android.incallui.incall.bindings.InCallBindings;
+import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
+import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
+import com.android.incallui.incall.protocol.InCallScreen;
+import com.android.incallui.incall.protocol.InCallScreenDelegate;
+import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
+import com.android.incallui.video.bindings.VideoBindings;
+import com.android.incallui.video.protocol.VideoCallScreen;
+import com.android.incallui.video.protocol.VideoCallScreenDelegate;
+import com.android.incallui.video.protocol.VideoCallScreenDelegateFactory;
+
+/** Version of {@link InCallActivity} that shows the new UI */
+public class InCallActivity extends TransactionSafeFragmentActivity
+ implements AnswerScreenDelegateFactory,
+ InCallScreenDelegateFactory,
+ InCallButtonUiDelegateFactory,
+ VideoCallScreenDelegateFactory,
+ PseudoScreenState.StateChangedListener {
+
+ private static final String TAG_IN_CALL_SCREEN = "tag_in_call_screen";
+ private static final String TAG_ANSWER_SCREEN = "tag_answer_screen";
+ private static final String TAG_VIDEO_CALL_SCREEN = "tag_video_call_screen";
+
+ private static final String DID_SHOW_ANSWER_SCREEN_KEY = "did_show_answer_screen";
+ private static final String DID_SHOW_IN_CALL_SCREEN_KEY = "did_show_in_call_screen";
+ private static final String DID_SHOW_VIDEO_CALL_SCREEN_KEY = "did_show_video_call_screen";
+
+ private final InCallActivityCommon common;
+ private boolean didShowAnswerScreen;
+ private boolean didShowInCallScreen;
+ private boolean didShowVideoCallScreen;
+ private int[] backgroundDrawableColors;
+ private GradientDrawable backgroundDrawable;
+ private boolean isVisible;
+ private View pseudoBlackScreenOverlay;
+ private boolean touchDownWhenPseudoScreenOff;
+ private boolean isInShowMainInCallFragment;
+ private boolean needDismissPendingDialogs;
+
+ public InCallActivity() {
+ common = new InCallActivityCommon(this);
+ }
+
+ public static Intent getIntent(
+ Context context,
+ boolean showDialpad,
+ boolean newOutgoingCall,
+ boolean isVideoCall,
+ boolean isForFullScreen) {
+ Intent intent = new Intent(Intent.ACTION_MAIN, null);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setClass(context, InCallActivity.class);
+ InCallActivityCommon.setIntentExtras(intent, showDialpad, newOutgoingCall, isForFullScreen);
+ return intent;
+ }
+
+ @Override
+ protected void onResumeFragments() {
+ super.onResumeFragments();
+ if (needDismissPendingDialogs) {
+ dismissPendingDialogs();
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ LogUtil.i("InCallActivity.onCreate", "");
+ super.onCreate(icicle);
+
+ if (icicle != null) {
+ didShowAnswerScreen = icicle.getBoolean(DID_SHOW_ANSWER_SCREEN_KEY);
+ didShowInCallScreen = icicle.getBoolean(DID_SHOW_IN_CALL_SCREEN_KEY);
+ didShowVideoCallScreen = icicle.getBoolean(DID_SHOW_VIDEO_CALL_SCREEN_KEY);
+ }
+
+ common.onCreate(icicle);
+
+ getWindow()
+ .getDecorView()
+ .setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
+
+ pseudoBlackScreenOverlay = findViewById(R.id.psuedo_black_screen_overlay);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle out) {
+ LogUtil.i("InCallActivity.onSaveInstanceState", "");
+ common.onSaveInstanceState(out);
+ out.putBoolean(DID_SHOW_ANSWER_SCREEN_KEY, didShowAnswerScreen);
+ out.putBoolean(DID_SHOW_IN_CALL_SCREEN_KEY, didShowInCallScreen);
+ out.putBoolean(DID_SHOW_VIDEO_CALL_SCREEN_KEY, didShowVideoCallScreen);
+ super.onSaveInstanceState(out);
+ isVisible = false;
+ }
+
+ @Override
+ protected void onStart() {
+ LogUtil.i("InCallActivity.onStart", "");
+ super.onStart();
+ isVisible = true;
+ showMainInCallFragment();
+ common.onStart();
+ if (ActivityCompat.isInMultiWindowMode(this)
+ && !getResources().getBoolean(R.bool.incall_dialpad_allowed)) {
+ // Hide the dialpad because there may not be enough room
+ showDialpadFragment(false, false);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ LogUtil.i("InCallActivity.onResume", "");
+ super.onResume();
+ common.onResume();
+ PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
+ pseudoScreenState.addListener(this);
+ onPseudoScreenStateChanged(pseudoScreenState.isOn());
+ }
+
+ /** onPause is guaranteed to be called when the InCallActivity goes in the background. */
+ @Override
+ protected void onPause() {
+ LogUtil.i("InCallActivity.onPause", "");
+ super.onPause();
+ common.onPause();
+ InCallPresenter.getInstance().getPseudoScreenState().removeListener(this);
+ }
+
+ @Override
+ protected void onStop() {
+ LogUtil.i("InCallActivity.onStop", "");
+ super.onStop();
+ common.onStop();
+ isVisible = false;
+ }
+
+ @Override
+ protected void onDestroy() {
+ LogUtil.i("InCallActivity.onDestroy", "");
+ super.onDestroy();
+ common.onDestroy();
+ }
+
+ @Override
+ public void finish() {
+ if (shouldCloseActivityOnFinish()) {
+ super.finish();
+ }
+ }
+
+ private boolean shouldCloseActivityOnFinish() {
+ if (!isVisible()) {
+ LogUtil.i(
+ "InCallActivity.shouldCloseActivityOnFinish",
+ "allowing activity to be closed because it's not visible");
+ return true;
+ }
+
+ if (common.hasPendingDialogs()) {
+ LogUtil.i(
+ "InCallActivity.shouldCloseActivityOnFinish", "dialog is visible, not closing activity");
+ return false;
+ }
+
+ AnswerScreen answerScreen = getAnswerScreen();
+ if (answerScreen != null && answerScreen.hasPendingDialogs()) {
+ LogUtil.i(
+ "InCallActivity.shouldCloseActivityOnFinish",
+ "answer screen dialog is visible, not closing activity");
+ return false;
+ }
+
+ LogUtil.i(
+ "InCallActivity.shouldCloseActivityOnFinish",
+ "activity is visible and has no dialogs, allowing activity to close");
+ return true;
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ LogUtil.i("InCallActivity.onNewIntent", "");
+ common.onNewIntent(intent);
+
+ // If the screen is off, we need to make sure it gets turned on for incoming calls.
+ // This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works
+ // when the activity is first created. Therefore, to ensure the screen is turned on
+ // for the call waiting case, we recreate() the current activity. There should be no jank from
+ // this since the screen is already off and will remain so until our new activity is up.
+ if (!isVisible()) {
+ LogUtil.i("InCallActivity.onNewIntent", "Restarting InCallActivity to force screen on.");
+ recreate();
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ LogUtil.i("InCallActivity.onBackPressed", "");
+ if (!common.onBackPressed(didShowInCallScreen || didShowVideoCallScreen)) {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ LogUtil.i("InCallActivity.onOptionsItemSelected", "item: " + item);
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (common.onKeyUp(keyCode, event)) {
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (common.onKeyDown(keyCode, event)) {
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ public boolean isInCallScreenAnimating() {
+ return false;
+ }
+
+ public void showConferenceFragment(boolean show) {
+ if (show) {
+ startActivity(new Intent(this, ManageConferenceActivity.class));
+ }
+ }
+
+ public boolean showDialpadFragment(boolean show, boolean animate) {
+ boolean didChange = common.showDialpadFragment(show, animate);
+ if (didChange) {
+ // Note: onInCallScreenDialpadVisibilityChange is called here to ensure that the dialpad FAB
+ // repositions itself.
+ getInCallScreen().onInCallScreenDialpadVisibilityChange(show);
+ }
+ return didChange;
+ }
+
+ public boolean isDialpadVisible() {
+ return common.isDialpadVisible();
+ }
+
+ public void onForegroundCallChanged(DialerCall newForegroundCall) {
+ common.updateTaskDescription();
+ if (didShowAnswerScreen && newForegroundCall != null) {
+ if (newForegroundCall.getState() == State.DISCONNECTED
+ || newForegroundCall.getState() == State.IDLE) {
+ LogUtil.i(
+ "InCallActivity.onForegroundCallChanged",
+ "rejecting incoming call, not updating " + "window background color");
+ }
+ } else {
+ LogUtil.v("InCallActivity.onForegroundCallChanged", "resetting background color");
+ updateWindowBackgroundColor(0);
+ }
+ }
+
+ public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) {
+ ThemeColorManager themeColorManager = InCallPresenter.getInstance().getThemeColorManager();
+ @ColorInt int top;
+ @ColorInt int middle;
+ @ColorInt int bottom;
+ @ColorInt int gray = 0x66000000;
+
+ if (ActivityCompat.isInMultiWindowMode(this)) {
+ top = themeColorManager.getBackgroundColorSolid();
+ middle = themeColorManager.getBackgroundColorSolid();
+ bottom = themeColorManager.getBackgroundColorSolid();
+ } else {
+ top = themeColorManager.getBackgroundColorTop();
+ middle = themeColorManager.getBackgroundColorMiddle();
+ bottom = themeColorManager.getBackgroundColorBottom();
+ }
+
+ if (progress < 0) {
+ float correctedProgress = Math.abs(progress);
+ top = ColorUtils.blendARGB(top, gray, correctedProgress);
+ middle = ColorUtils.blendARGB(middle, gray, correctedProgress);
+ bottom = ColorUtils.blendARGB(bottom, gray, correctedProgress);
+ }
+
+ boolean backgroundDirty = false;
+ if (backgroundDrawable == null) {
+ backgroundDrawableColors = new int[] {top, middle, bottom};
+ backgroundDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, backgroundDrawableColors);
+ backgroundDirty = true;
+ } else {
+ if (backgroundDrawableColors[0] != top) {
+ backgroundDrawableColors[0] = top;
+ backgroundDirty = true;
+ }
+ if (backgroundDrawableColors[1] != middle) {
+ backgroundDrawableColors[1] = middle;
+ backgroundDirty = true;
+ }
+ if (backgroundDrawableColors[2] != bottom) {
+ backgroundDrawableColors[2] = bottom;
+ backgroundDirty = true;
+ }
+ if (backgroundDirty) {
+ backgroundDrawable.setColors(backgroundDrawableColors);
+ }
+ }
+
+ if (backgroundDirty) {
+ getWindow().setBackgroundDrawable(backgroundDrawable);
+ }
+ }
+
+ public boolean isVisible() {
+ return isVisible;
+ }
+
+ public boolean getCallCardFragmentVisible() {
+ return didShowInCallScreen || didShowVideoCallScreen;
+ }
+
+ public void dismissKeyguard(boolean dismiss) {
+ common.dismissKeyguard(dismiss);
+ }
+
+ public void showPostCharWaitDialog(String callId, String chars) {
+ common.showPostCharWaitDialog(callId, chars);
+ }
+
+ public void maybeShowErrorDialogOnDisconnect(DisconnectCause disconnectCause) {
+ common.maybeShowErrorDialogOnDisconnect(disconnectCause);
+ }
+
+ public void dismissPendingDialogs() {
+ if (isVisible) {
+ LogUtil.i("InCallActivity.dismissPendingDialogs", "");
+ common.dismissPendingDialogs();
+ AnswerScreen answerScreen = getAnswerScreen();
+ if (answerScreen != null) {
+ answerScreen.dismissPendingDialogs();
+ }
+ needDismissPendingDialogs = false;
+ } else {
+ // The activity is not visible and onSaveInstanceState may have been called so defer the
+ // dismissing action.
+ LogUtil.i(
+ "InCallActivity.dismissPendingDialogs", "defer actions since activity is not visible");
+ needDismissPendingDialogs = true;
+ }
+ }
+
+ private void enableInCallOrientationEventListener(boolean enable) {
+ common.enableInCallOrientationEventListener(enable);
+ }
+
+ public void setExcludeFromRecents(boolean exclude) {
+ common.setExcludeFromRecents(exclude);
+ }
+
+ public void onResolveIntent(
+ DialerCall outgoingCall, boolean isNewOutgoingCall, boolean didShowAccountSelectionDialog) {
+ if (didShowAccountSelectionDialog) {
+ hideMainInCallFragment();
+ }
+ }
+
+ @Nullable
+ public FragmentManager getDialpadFragmentManager() {
+ InCallScreen inCallScreen = getInCallScreen();
+ if (inCallScreen != null) {
+ return inCallScreen.getInCallScreenFragment().getChildFragmentManager();
+ }
+ return null;
+ }
+
+ public int getDialpadContainerId() {
+ return getInCallScreen().getAnswerAndDialpadContainerResourceId();
+ }
+
+ @Override
+ public AnswerScreenDelegate newAnswerScreenDelegate(AnswerScreen answerScreen) {
+ DialerCall call = CallList.getInstance().getCallById(answerScreen.getCallId());
+ if (call == null) {
+ // This is a work around for a bug where we attempt to create a new delegate after the call
+ // has already been removed. An example of when this can happen is:
+ // 1. incoming video call in landscape mode
+ // 2. remote party hangs up
+ // 3. activity switches from landscape to portrait
+ // At step #3 the answer fragment will try to create a new answer delegate but the call won't
+ // exist. In this case we'll simply return a stub delegate that does nothing. This is ok
+ // because this new state is transient and the activity will be destroyed soon.
+ LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "call doesn't exist, using stub");
+ return new AnswerScreenPresenterStub();
+ } else {
+ return new AnswerScreenPresenter(
+ this, answerScreen, CallList.getInstance().getCallById(answerScreen.getCallId()));
+ }
+ }
+
+ @Override
+ public InCallScreenDelegate newInCallScreenDelegate() {
+ return new CallCardPresenter(this);
+ }
+
+ @Override
+ public InCallButtonUiDelegate newInCallButtonUiDelegate() {
+ return new CallButtonPresenter(this);
+ }
+
+ @Override
+ public VideoCallScreenDelegate newVideoCallScreenDelegate() {
+ return new VideoCallPresenter();
+ }
+
+ public void onPrimaryCallStateChanged() {
+ LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "");
+ showMainInCallFragment();
+ }
+
+ public void onWiFiToLteHandover(DialerCall call) {
+ common.showWifiToLteHandoverToast(call);
+ }
+
+ public void onHandoverToWifiFailed(DialerCall call) {
+ common.showWifiFailedDialog(call);
+ }
+
+ public void setAllowOrientationChange(boolean allowOrientationChange) {
+ if (!allowOrientationChange) {
+ setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_DISALLOW_ROTATION);
+ } else {
+ setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
+ }
+ enableInCallOrientationEventListener(allowOrientationChange);
+ }
+
+ private void hideMainInCallFragment() {
+ LogUtil.i("InCallActivity.hideMainInCallFragment", "");
+ if (didShowInCallScreen || didShowVideoCallScreen) {
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ hideInCallScreenFragment(transaction);
+ hideVideoCallScreenFragment(transaction);
+ transaction.commitAllowingStateLoss();
+ getSupportFragmentManager().executePendingTransactions();
+ }
+ }
+
+ private void showMainInCallFragment() {
+ // If the activity's onStart method hasn't been called yet then defer doing any work.
+ if (!isVisible) {
+ LogUtil.i("InCallActivity.showMainInCallFragment", "not visible yet/anymore");
+ return;
+ }
+
+ // Don't let this be reentrant.
+ if (isInShowMainInCallFragment) {
+ LogUtil.i("InCallActivity.showMainInCallFragment", "already in method, bailing");
+ return;
+ }
+
+ isInShowMainInCallFragment = true;
+ ShouldShowAnswerUiResult shouldShowAnswerUi = getShouldShowAnswerUi();
+ boolean shouldShowVideoUi = getShouldShowVideoUi();
+ LogUtil.i(
+ "InCallActivity.showMainInCallFragment",
+ "shouldShowAnswerUi: %b, shouldShowVideoUi: %b, "
+ + "didShowAnswerScreen: %b, didShowInCallScreen: %b, didShowVideoCallScreen: %b",
+ shouldShowAnswerUi.shouldShow,
+ shouldShowVideoUi,
+ didShowAnswerScreen,
+ didShowInCallScreen,
+ didShowVideoCallScreen);
+ // Only video call ui allows orientation change.
+ setAllowOrientationChange(shouldShowVideoUi);
+
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ boolean didChangeInCall;
+ boolean didChangeVideo;
+ boolean didChangeAnswer;
+ if (shouldShowAnswerUi.shouldShow) {
+ didChangeInCall = hideInCallScreenFragment(transaction);
+ didChangeVideo = hideVideoCallScreenFragment(transaction);
+ didChangeAnswer = showAnswerScreenFragment(transaction, shouldShowAnswerUi.call);
+ } else if (shouldShowVideoUi) {
+ didChangeInCall = hideInCallScreenFragment(transaction);
+ didChangeVideo = showVideoCallScreenFragment(transaction);
+ didChangeAnswer = hideAnswerScreenFragment(transaction);
+ } else {
+ didChangeInCall = showInCallScreenFragment(transaction);
+ didChangeVideo = hideVideoCallScreenFragment(transaction);
+ didChangeAnswer = hideAnswerScreenFragment(transaction);
+ }
+
+ if (didChangeInCall || didChangeVideo || didChangeAnswer) {
+ transaction.commitNow();
+ Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
+ }
+ isInShowMainInCallFragment = false;
+ }
+
+ private ShouldShowAnswerUiResult getShouldShowAnswerUi() {
+ DialerCall call = CallList.getInstance().getIncomingCall();
+ if (call != null) {
+ LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found incoming call");
+ return new ShouldShowAnswerUiResult(true, call);
+ }
+
+ call = CallList.getInstance().getVideoUpgradeRequestCall();
+ if (call != null) {
+ LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found video upgrade request");
+ return new ShouldShowAnswerUiResult(true, call);
+ }
+
+ // Check if we're showing the answer screen and the call is disconnected. If this condition is
+ // true then we won't switch from the answer UI to the in call UI. This prevents flicker when
+ // the user rejects an incoming call.
+ call = CallList.getInstance().getFirstCall();
+ if (call == null) {
+ call = CallList.getInstance().getBackgroundCall();
+ }
+ if (didShowAnswerScreen && (call == null || call.getState() == State.DISCONNECTED)) {
+ LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found disconnecting incoming call");
+ return new ShouldShowAnswerUiResult(true, call);
+ }
+
+ return new ShouldShowAnswerUiResult(false, null);
+ }
+
+ private boolean getShouldShowVideoUi() {
+ DialerCall call = CallList.getInstance().getFirstCall();
+ if (call == null) {
+ LogUtil.i("InCallActivity.getShouldShowVideoUi", "null call");
+ return false;
+ }
+
+ if (VideoUtils.isVideoCall(call)) {
+ LogUtil.i("InCallActivity.getShouldShowVideoUi", "found video call");
+ return true;
+ }
+
+ if (VideoUtils.hasSentVideoUpgradeRequest(call)) {
+ LogUtil.i("InCallActivity.getShouldShowVideoUi", "upgrading to video");
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean showAnswerScreenFragment(FragmentTransaction transaction, DialerCall call) {
+ // When rejecting a call the active call can become null in which case we should continue
+ // showing the answer screen.
+ if (didShowAnswerScreen && call == null) {
+ return false;
+ }
+
+ boolean isVideoUpgradeRequest = VideoUtils.hasReceivedVideoUpgradeRequest(call);
+ int videoState = isVideoUpgradeRequest ? call.getRequestedVideoState() : call.getVideoState();
+
+ // Check if we're already showing an answer screen for this call.
+ if (didShowAnswerScreen) {
+ AnswerScreen answerScreen = getAnswerScreen();
+ if (answerScreen.getCallId().equals(call.getId())
+ && answerScreen.getVideoState() == videoState
+ && answerScreen.isVideoUpgradeRequest() == isVideoUpgradeRequest) {
+ return false;
+ }
+ LogUtil.i(
+ "InCallActivity.showAnswerScreenFragment",
+ "answer fragment exists but arguments do not match");
+ hideAnswerScreenFragment(transaction);
+ }
+
+ // Show a new answer screen.
+ AnswerScreen answerScreen =
+ AnswerBindings.createAnswerScreen(call.getId(), videoState, isVideoUpgradeRequest);
+ transaction.add(R.id.main, answerScreen.getAnswerScreenFragment(), TAG_ANSWER_SCREEN);
+
+ Logger.get(this).logScreenView(ScreenEvent.Type.INCOMING_CALL, this);
+ didShowAnswerScreen = true;
+ return true;
+ }
+
+ private boolean hideAnswerScreenFragment(FragmentTransaction transaction) {
+ if (!didShowAnswerScreen) {
+ return false;
+ }
+ AnswerScreen answerScreen = getAnswerScreen();
+ if (answerScreen != null) {
+ transaction.remove(answerScreen.getAnswerScreenFragment());
+ }
+
+ didShowAnswerScreen = false;
+ return true;
+ }
+
+ private boolean showInCallScreenFragment(FragmentTransaction transaction) {
+ if (didShowInCallScreen) {
+ return false;
+ }
+ InCallScreen inCallScreen = getInCallScreen();
+ if (inCallScreen == null) {
+ inCallScreen = InCallBindings.createInCallScreen();
+ transaction.add(R.id.main, inCallScreen.getInCallScreenFragment(), TAG_IN_CALL_SCREEN);
+ } else {
+ transaction.show(inCallScreen.getInCallScreenFragment());
+ }
+ Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
+ didShowInCallScreen = true;
+ return true;
+ }
+
+ private boolean hideInCallScreenFragment(FragmentTransaction transaction) {
+ if (!didShowInCallScreen) {
+ return false;
+ }
+ InCallScreen inCallScreen = getInCallScreen();
+ if (inCallScreen != null) {
+ transaction.hide(inCallScreen.getInCallScreenFragment());
+ }
+ didShowInCallScreen = false;
+ return true;
+ }
+
+ private boolean showVideoCallScreenFragment(FragmentTransaction transaction) {
+ if (didShowVideoCallScreen) {
+ return false;
+ }
+
+ VideoCallScreen videoCallScreen = VideoBindings.createVideoCallScreen();
+ transaction.add(R.id.main, videoCallScreen.getVideoCallScreenFragment(), TAG_VIDEO_CALL_SCREEN);
+
+ Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
+ didShowVideoCallScreen = true;
+ return true;
+ }
+
+ private boolean hideVideoCallScreenFragment(FragmentTransaction transaction) {
+ if (!didShowVideoCallScreen) {
+ return false;
+ }
+ VideoCallScreen videoCallScreen = getVideoCallScreen();
+ if (videoCallScreen != null) {
+ transaction.remove(videoCallScreen.getVideoCallScreenFragment());
+ }
+ didShowVideoCallScreen = false;
+ return true;
+ }
+
+ AnswerScreen getAnswerScreen() {
+ return (AnswerScreen) getSupportFragmentManager().findFragmentByTag(TAG_ANSWER_SCREEN);
+ }
+
+ InCallScreen getInCallScreen() {
+ return (InCallScreen) getSupportFragmentManager().findFragmentByTag(TAG_IN_CALL_SCREEN);
+ }
+
+ VideoCallScreen getVideoCallScreen() {
+ return (VideoCallScreen) getSupportFragmentManager().findFragmentByTag(TAG_VIDEO_CALL_SCREEN);
+ }
+
+ @Override
+ public void onPseudoScreenStateChanged(boolean isOn) {
+ LogUtil.i("InCallActivity.onPseudoScreenStateChanged", "isOn: " + isOn);
+ pseudoBlackScreenOverlay.setVisibility(isOn ? View.GONE : View.VISIBLE);
+ }
+
+ /**
+ * For some touch related issue, turning off the screen can be faked by drawing a black view over
+ * the activity. All touch events started when the screen is "off" is rejected.
+ *
+ * @see PseudoScreenState
+ */
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ // Reject any gesture that started when the screen is in the fake off state.
+ if (touchDownWhenPseudoScreenOff) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ touchDownWhenPseudoScreenOff = false;
+ }
+ return true;
+ }
+ // Reject all touch event when the screen is in the fake off state.
+ if (!InCallPresenter.getInstance().getPseudoScreenState().isOn()) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ touchDownWhenPseudoScreenOff = true;
+ LogUtil.i("InCallActivity.dispatchTouchEvent", "touchDownWhenPseudoScreenOff");
+ }
+ return true;
+ }
+ return super.dispatchTouchEvent(event);
+ }
+
+ private static class ShouldShowAnswerUiResult {
+ public final boolean shouldShow;
+ public final DialerCall call;
+
+ ShouldShowAnswerUiResult(boolean shouldShow, DialerCall call) {
+ this.shouldShow = shouldShow;
+ this.call = call;
+ }
+ }
+}
diff --git a/java/com/android/incallui/InCallActivityCommon.java b/java/com/android/incallui/InCallActivityCommon.java
new file mode 100644
index 000000000..a2467dd72
--- /dev/null
+++ b/java/com/android/incallui/InCallActivityCommon.java
@@ -0,0 +1,820 @@
+/*
+ * 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.incallui;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.AppTask;
+import android.app.ActivityManager.TaskDescription;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.content.res.ResourcesCompat;
+import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccountHandle;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.CheckBox;
+import android.widget.Toast;
+import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
+import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment.SelectPhoneAccountListener;
+import com.android.dialer.animation.AnimUtils;
+import com.android.dialer.animation.AnimationListenerAdapter;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.ScreenEvent;
+import com.android.dialer.util.ViewUtil;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.DialerCall.State;
+import com.android.incallui.call.TelecomAdapter;
+import com.android.incallui.wifi.EnableWifiCallingPrompt;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Shared functionality between the new and old in call activity. */
+public class InCallActivityCommon {
+
+ private static final String INTENT_EXTRA_SHOW_DIALPAD = "InCallActivity.show_dialpad";
+ private static final String INTENT_EXTRA_NEW_OUTGOING_CALL = "InCallActivity.new_outgoing_call";
+ private static final String INTENT_EXTRA_FOR_FULL_SCREEN =
+ "InCallActivity.for_full_screen_intent";
+
+ private static final String DIALPAD_TEXT_KEY = "InCallActivity.dialpad_text";
+
+ private static final String TAG_SELECT_ACCOUNT_FRAGMENT = "tag_select_account_fragment";
+ private static final String TAG_DIALPAD_FRAGMENT = "tag_dialpad_fragment";
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ DIALPAD_REQUEST_NONE,
+ DIALPAD_REQUEST_SHOW,
+ DIALPAD_REQUEST_HIDE,
+ })
+ @interface DialpadRequestType {}
+
+ private static final int DIALPAD_REQUEST_NONE = 1;
+ private static final int DIALPAD_REQUEST_SHOW = 2;
+ private static final int DIALPAD_REQUEST_HIDE = 3;
+
+ private final InCallActivity inCallActivity;
+ private boolean dismissKeyguard;
+ private boolean showPostCharWaitDialogOnResume;
+ private String showPostCharWaitDialogCallId;
+ private String showPostCharWaitDialogChars;
+ private Dialog dialog;
+ private InCallOrientationEventListener inCallOrientationEventListener;
+ private Animation dialpadSlideInAnimation;
+ private Animation dialpadSlideOutAnimation;
+ private boolean animateDialpadOnShow;
+ private String dtmfTextToPreopulate;
+ @DialpadRequestType private int showDialpadRequest = DIALPAD_REQUEST_NONE;
+
+ private SelectPhoneAccountListener selectAccountListener =
+ new SelectPhoneAccountListener() {
+ @Override
+ public void onPhoneAccountSelected(
+ PhoneAccountHandle selectedAccountHandle, boolean setDefault, String callId) {
+ DialerCall call = CallList.getInstance().getCallById(callId);
+ LogUtil.i(
+ "InCallActivityCommon.SelectPhoneAccountListener.onPhoneAccountSelected",
+ "call: " + call);
+ if (call != null) {
+ call.phoneAccountSelected(selectedAccountHandle, setDefault);
+ }
+ }
+
+ @Override
+ public void onDialogDismissed(String callId) {
+ DialerCall call = CallList.getInstance().getCallById(callId);
+ LogUtil.i(
+ "InCallActivityCommon.SelectPhoneAccountListener.onDialogDismissed",
+ "disconnecting call: " + call);
+ if (call != null) {
+ call.disconnect();
+ }
+ }
+ };
+
+ public static void setIntentExtras(
+ Intent intent, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {
+ if (showDialpad) {
+ intent.putExtra(INTENT_EXTRA_SHOW_DIALPAD, true);
+ }
+ intent.putExtra(INTENT_EXTRA_NEW_OUTGOING_CALL, newOutgoingCall);
+ intent.putExtra(INTENT_EXTRA_FOR_FULL_SCREEN, isForFullScreen);
+ }
+
+ public InCallActivityCommon(InCallActivity inCallActivity) {
+ this.inCallActivity = inCallActivity;
+ }
+
+ public void onCreate(Bundle icicle) {
+ // set this flag so this activity will stay in front of the keyguard
+ // Have the WindowManager filter out touch events that are "too fat".
+ int flags =
+ WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
+
+ inCallActivity.getWindow().addFlags(flags);
+
+ inCallActivity.setContentView(R.layout.incall_screen);
+
+ internalResolveIntent(inCallActivity.getIntent());
+
+ boolean isLandscape =
+ inCallActivity.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
+ boolean isRtl = ViewUtil.isRtl();
+
+ if (isLandscape) {
+ dialpadSlideInAnimation =
+ AnimationUtils.loadAnimation(
+ inCallActivity, isRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right);
+ dialpadSlideOutAnimation =
+ AnimationUtils.loadAnimation(
+ inCallActivity,
+ isRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right);
+ } else {
+ dialpadSlideInAnimation =
+ AnimationUtils.loadAnimation(inCallActivity, R.anim.dialpad_slide_in_bottom);
+ dialpadSlideOutAnimation =
+ AnimationUtils.loadAnimation(inCallActivity, R.anim.dialpad_slide_out_bottom);
+ }
+
+ dialpadSlideInAnimation.setInterpolator(AnimUtils.EASE_IN);
+ dialpadSlideOutAnimation.setInterpolator(AnimUtils.EASE_OUT);
+
+ dialpadSlideOutAnimation.setAnimationListener(
+ new AnimationListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ performHideDialpadFragment();
+ }
+ });
+
+ if (icicle != null) {
+ // If the dialpad was shown before, set variables indicating it should be shown and
+ // populated with the previous DTMF text. The dialpad is actually shown and populated
+ // in onResume() to ensure the hosting fragment has been inflated and is ready to receive it.
+ if (icicle.containsKey(INTENT_EXTRA_SHOW_DIALPAD)) {
+ boolean showDialpad = icicle.getBoolean(INTENT_EXTRA_SHOW_DIALPAD);
+ showDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_HIDE;
+ animateDialpadOnShow = false;
+ }
+ dtmfTextToPreopulate = icicle.getString(DIALPAD_TEXT_KEY);
+
+ SelectPhoneAccountDialogFragment dialogFragment =
+ (SelectPhoneAccountDialogFragment)
+ inCallActivity.getFragmentManager().findFragmentByTag(TAG_SELECT_ACCOUNT_FRAGMENT);
+ if (dialogFragment != null) {
+ dialogFragment.setListener(selectAccountListener);
+ }
+ }
+
+ inCallOrientationEventListener = new InCallOrientationEventListener(inCallActivity);
+ }
+
+ public void onSaveInstanceState(Bundle out) {
+ // TODO: The dialpad fragment should handle this as part of its own state
+ out.putBoolean(INTENT_EXTRA_SHOW_DIALPAD, isDialpadVisible());
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ if (dialpadFragment != null) {
+ out.putString(DIALPAD_TEXT_KEY, dialpadFragment.getDtmfText());
+ }
+ }
+
+ public void onStart() {
+ // setting activity should be last thing in setup process
+ InCallPresenter.getInstance().setActivity(inCallActivity);
+ enableInCallOrientationEventListener(
+ inCallActivity.getRequestedOrientation()
+ == InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
+
+ InCallPresenter.getInstance().onActivityStarted();
+ }
+
+ public void onResume() {
+ if (InCallPresenter.getInstance().isReadyForTearDown()) {
+ LogUtil.i(
+ "InCallActivityCommon.onResume",
+ "InCallPresenter is ready for tear down, not sending updates");
+ } else {
+ updateTaskDescription();
+ InCallPresenter.getInstance().onUiShowing(true);
+ }
+
+ // If there is a pending request to show or hide the dialpad, handle that now.
+ if (showDialpadRequest != DIALPAD_REQUEST_NONE) {
+ if (showDialpadRequest == DIALPAD_REQUEST_SHOW) {
+ // Exit fullscreen so that the user has access to the dialpad hide/show button and
+ // can hide the dialpad. Important when showing the dialpad from within dialer.
+ InCallPresenter.getInstance().setFullScreen(false, true /* force */);
+
+ inCallActivity.showDialpadFragment(true /* show */, animateDialpadOnShow /* animate */);
+ animateDialpadOnShow = false;
+
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ if (dialpadFragment != null) {
+ dialpadFragment.setDtmfText(dtmfTextToPreopulate);
+ dtmfTextToPreopulate = null;
+ }
+ } else {
+ LogUtil.i("InCallActivityCommon.onResume", "force hide dialpad");
+ if (getDialpadFragment() != null) {
+ inCallActivity.showDialpadFragment(false /* show */, false /* animate */);
+ }
+ }
+ showDialpadRequest = DIALPAD_REQUEST_NONE;
+ }
+
+ if (showPostCharWaitDialogOnResume) {
+ showPostCharWaitDialog(showPostCharWaitDialogCallId, showPostCharWaitDialogChars);
+ }
+
+ CallList.getInstance()
+ .onInCallUiShown(
+ inCallActivity.getIntent().getBooleanExtra(INTENT_EXTRA_FOR_FULL_SCREEN, false));
+ }
+
+ // onPause is guaranteed to be called when the InCallActivity goes
+ // in the background.
+ public void onPause() {
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ if (dialpadFragment != null) {
+ dialpadFragment.onDialerKeyUp(null);
+ }
+
+ InCallPresenter.getInstance().onUiShowing(false);
+ if (inCallActivity.isFinishing()) {
+ InCallPresenter.getInstance().unsetActivity(inCallActivity);
+ }
+ }
+
+ public void onStop() {
+ enableInCallOrientationEventListener(false);
+ InCallPresenter.getInstance().updateIsChangingConfigurations();
+ InCallPresenter.getInstance().onActivityStopped();
+ }
+
+ public void onDestroy() {
+ InCallPresenter.getInstance().unsetActivity(inCallActivity);
+ InCallPresenter.getInstance().updateIsChangingConfigurations();
+ }
+
+ public void onNewIntent(Intent intent) {
+ LogUtil.i("InCallActivityCommon.onNewIntent", "");
+
+ // We're being re-launched with a new Intent. Since it's possible for a
+ // single InCallActivity instance to persist indefinitely (even if we
+ // finish() ourselves), this sequence can potentially happen any time
+ // the InCallActivity needs to be displayed.
+
+ // Stash away the new intent so that we can get it in the future
+ // by calling getIntent(). (Otherwise getIntent() will return the
+ // original Intent from when we first got created!)
+ inCallActivity.setIntent(intent);
+
+ // Activities are always paused before receiving a new intent, so
+ // we can count on our onResume() method being called next.
+
+ // Just like in onCreate(), handle the intent.
+ internalResolveIntent(intent);
+ }
+
+ public boolean onBackPressed(boolean isInCallScreenVisible) {
+ LogUtil.i("InCallActivityCommon.onBackPressed", "");
+
+ // BACK is also used to exit out of any "special modes" of the
+ // in-call UI:
+ if (!inCallActivity.isVisible()) {
+ return true;
+ }
+
+ if (!isInCallScreenVisible) {
+ return true;
+ }
+
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ if (dialpadFragment != null && dialpadFragment.isVisible()) {
+ inCallActivity.showDialpadFragment(false /* show */, true /* animate */);
+ return true;
+ }
+
+ // Always disable the Back key while an incoming call is ringing
+ DialerCall call = CallList.getInstance().getIncomingCall();
+ if (call != null) {
+ LogUtil.i("InCallActivityCommon.onBackPressed", "consume Back press for an incoming call");
+ return true;
+ }
+
+ // Nothing special to do. Fall back to the default behavior.
+ return false;
+ }
+
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ // push input to the dialer.
+ if (dialpadFragment != null
+ && (dialpadFragment.isVisible())
+ && (dialpadFragment.onDialerKeyUp(event))) {
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_CALL) {
+ // Always consume CALL to be sure the PhoneWindow won't do anything with it
+ return true;
+ }
+ return false;
+ }
+
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_CALL:
+ boolean handled = InCallPresenter.getInstance().handleCallKey();
+ if (!handled) {
+ LogUtil.e(
+ "InCallActivityCommon.onKeyDown",
+ "InCallPresenter should always handle KEYCODE_CALL in onKeyDown");
+ }
+ // Always consume CALL to be sure the PhoneWindow won't do anything with it
+ return true;
+
+ // Note there's no KeyEvent.KEYCODE_ENDCALL case here.
+ // The standard system-wide handling of the ENDCALL key
+ // (see PhoneWindowManager's handling of KEYCODE_ENDCALL)
+ // already implements exactly what the UI spec wants,
+ // namely (1) "hang up" if there's a current active call,
+ // or (2) "don't answer" if there's a current ringing call.
+
+ case KeyEvent.KEYCODE_CAMERA:
+ // Disable the CAMERA button while in-call since it's too
+ // easy to press accidentally.
+ return true;
+
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ case KeyEvent.KEYCODE_VOLUME_MUTE:
+ // Ringer silencing handled by PhoneWindowManager.
+ break;
+
+ case KeyEvent.KEYCODE_MUTE:
+ TelecomAdapter.getInstance()
+ .mute(!AudioModeProvider.getInstance().getAudioState().isMuted());
+ return true;
+
+ // Various testing/debugging features, enabled ONLY when VERBOSE == true.
+ case KeyEvent.KEYCODE_SLASH:
+ if (LogUtil.isVerboseEnabled()) {
+ LogUtil.v(
+ "InCallActivityCommon.onKeyDown",
+ "----------- InCallActivity View dump --------------");
+ // Dump starting from the top-level view of the entire activity:
+ Window w = inCallActivity.getWindow();
+ View decorView = w.getDecorView();
+ LogUtil.v("InCallActivityCommon.onKeyDown", "View dump:" + decorView);
+ return true;
+ }
+ break;
+ case KeyEvent.KEYCODE_EQUALS:
+ break;
+ }
+
+ return event.getRepeatCount() == 0 && handleDialerKeyDown(keyCode, event);
+ }
+
+ private boolean handleDialerKeyDown(int keyCode, KeyEvent event) {
+ LogUtil.v("InCallActivityCommon.handleDialerKeyDown", "keyCode %d, event: %s", keyCode, event);
+
+ // As soon as the user starts typing valid dialable keys on the
+ // keyboard (presumably to type DTMF tones) we start passing the
+ // key events to the DTMFDialer's onDialerKeyDown.
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ if (dialpadFragment != null && dialpadFragment.isVisible()) {
+ return dialpadFragment.onDialerKeyDown(event);
+ }
+
+ return false;
+ }
+
+ public void dismissKeyguard(boolean dismiss) {
+ if (dismissKeyguard == dismiss) {
+ return;
+ }
+ dismissKeyguard = dismiss;
+ if (dismiss) {
+ inCallActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ } else {
+ inCallActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ }
+ }
+
+ public void showPostCharWaitDialog(String callId, String chars) {
+ if (inCallActivity.isVisible()) {
+ PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars);
+ fragment.show(inCallActivity.getSupportFragmentManager(), "postCharWait");
+
+ showPostCharWaitDialogOnResume = false;
+ showPostCharWaitDialogCallId = null;
+ showPostCharWaitDialogChars = null;
+ } else {
+ showPostCharWaitDialogOnResume = true;
+ showPostCharWaitDialogCallId = callId;
+ showPostCharWaitDialogChars = chars;
+ }
+ }
+
+ public void maybeShowErrorDialogOnDisconnect(DisconnectCause cause) {
+ LogUtil.i(
+ "InCallActivityCommon.maybeShowErrorDialogOnDisconnect", "disconnect cause: %s", cause);
+
+ if (!inCallActivity.isFinishing()) {
+ if (EnableWifiCallingPrompt.shouldShowPrompt(cause)) {
+ Pair<Dialog, CharSequence> pair =
+ EnableWifiCallingPrompt.createDialog(inCallActivity, cause);
+ showErrorDialog(pair.first, pair.second);
+ } else if (shouldShowDisconnectErrorDialog(cause)) {
+ Pair<Dialog, CharSequence> pair = getDisconnectErrorDialog(inCallActivity, cause);
+ showErrorDialog(pair.first, pair.second);
+ }
+ }
+ }
+
+ /**
+ * When relaunching from the dialer app, {@code showDialpad} indicates whether the dialpad should
+ * be shown on launch.
+ *
+ * @param showDialpad {@code true} to indicate the dialpad should be shown on launch, and {@code
+ * false} to indicate no change should be made to the dialpad visibility.
+ */
+ private void relaunchedFromDialer(boolean showDialpad) {
+ showDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_NONE;
+ animateDialpadOnShow = true;
+
+ if (showDialpadRequest == DIALPAD_REQUEST_SHOW) {
+ // If there's only one line in use, AND it's on hold, then we're sure the user
+ // wants to use the dialpad toward the exact line, so un-hold the holding line.
+ DialerCall call = CallList.getInstance().getActiveOrBackgroundCall();
+ if (call != null && call.getState() == State.ONHOLD) {
+ call.unhold();
+ }
+ }
+ }
+
+ public void dismissPendingDialogs() {
+ if (dialog != null) {
+ dialog.dismiss();
+ dialog = null;
+ }
+ }
+
+ private static boolean shouldShowDisconnectErrorDialog(@NonNull DisconnectCause cause) {
+ return !TextUtils.isEmpty(cause.getDescription())
+ && (cause.getCode() == DisconnectCause.ERROR
+ || cause.getCode() == DisconnectCause.RESTRICTED);
+ }
+
+ private static Pair<Dialog, CharSequence> getDisconnectErrorDialog(
+ @NonNull Context context, @NonNull DisconnectCause cause) {
+ CharSequence message = cause.getDescription();
+ Dialog dialog =
+ new AlertDialog.Builder(context)
+ .setMessage(message)
+ .setPositiveButton(android.R.string.ok, null)
+ .create();
+ return new Pair<>(dialog, message);
+ }
+
+ private void showErrorDialog(Dialog dialog, CharSequence message) {
+ LogUtil.i("InCallActivityCommon.showErrorDialog", "message: %s", message);
+ inCallActivity.dismissPendingDialogs();
+
+ // Show toast if apps is in background when dialog won't be visible.
+ if (!inCallActivity.isVisible()) {
+ Toast.makeText(inCallActivity.getApplicationContext(), message, Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ this.dialog = dialog;
+ dialog.setOnDismissListener(
+ new OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ LogUtil.i("InCallActivityCommon.showErrorDialog", "dialog dismissed");
+ onDialogDismissed();
+ }
+ });
+ dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ dialog.show();
+ }
+
+ private void onDialogDismissed() {
+ dialog = null;
+ CallList.getInstance().onErrorDialogDismissed();
+ InCallPresenter.getInstance().onDismissDialog();
+ }
+
+ public void enableInCallOrientationEventListener(boolean enable) {
+ if (enable) {
+ inCallOrientationEventListener.enable(true);
+ } else {
+ inCallOrientationEventListener.disable();
+ }
+ }
+
+ public void setExcludeFromRecents(boolean exclude) {
+ List<AppTask> tasks = inCallActivity.getSystemService(ActivityManager.class).getAppTasks();
+ int taskId = inCallActivity.getTaskId();
+ for (int i = 0; i < tasks.size(); i++) {
+ ActivityManager.AppTask task = tasks.get(i);
+ try {
+ if (task.getTaskInfo().id == taskId) {
+ task.setExcludeFromRecents(exclude);
+ }
+ } catch (RuntimeException e) {
+ LogUtil.e(
+ "InCallActivityCommon.setExcludeFromRecents",
+ "RuntimeException when excluding task from recents.",
+ e);
+ }
+ }
+ }
+
+ public void showWifiToLteHandoverToast(DialerCall call) {
+ if (call.hasShownWiFiToLteHandoverToast()) {
+ return;
+ }
+ Toast.makeText(
+ inCallActivity, R.string.video_call_wifi_to_lte_handover_toast, Toast.LENGTH_LONG)
+ .show();
+ call.setHasShownWiFiToLteHandoverToast();
+ }
+
+ public void showWifiFailedDialog(final DialerCall call) {
+ if (call.showWifiHandoverAlertAsToast()) {
+ LogUtil.i("InCallActivityCommon.showWifiFailedDialog", "as toast");
+ Toast.makeText(
+ inCallActivity, R.string.video_call_lte_to_wifi_failed_message, Toast.LENGTH_SHORT)
+ .show();
+ return;
+ }
+
+ dismissPendingDialogs();
+
+ AlertDialog.Builder builder =
+ new AlertDialog.Builder(inCallActivity)
+ .setTitle(R.string.video_call_lte_to_wifi_failed_title);
+
+ // This allows us to use the theme of the dialog instead of the activity
+ View dialogCheckBoxView =
+ View.inflate(builder.getContext(), R.layout.video_call_lte_to_wifi_failed, null);
+ final CheckBox wifiHandoverFailureCheckbox =
+ (CheckBox) dialogCheckBoxView.findViewById(R.id.video_call_lte_to_wifi_failed_checkbox);
+ wifiHandoverFailureCheckbox.setChecked(false);
+
+ dialog =
+ builder
+ .setView(dialogCheckBoxView)
+ .setMessage(R.string.video_call_lte_to_wifi_failed_message)
+ .setOnCancelListener(
+ new OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ onDialogDismissed();
+ }
+ })
+ .setPositiveButton(
+ android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ call.setDoNotShowDialogForHandoffToWifiFailure(
+ wifiHandoverFailureCheckbox.isChecked());
+ dialog.cancel();
+ onDialogDismissed();
+ }
+ })
+ .create();
+
+ LogUtil.i("InCallActivityCommon.showWifiFailedDialog", "as dialog");
+ dialog.show();
+ }
+
+ public boolean showDialpadFragment(boolean show, boolean animate) {
+ // If the dialpad is already visible, don't animate in. If it's gone, don't animate out.
+ boolean isDialpadVisible = isDialpadVisible();
+ LogUtil.i(
+ "InCallActivityCommon.showDialpadFragment",
+ "show: %b, animate: %b, " + "isDialpadVisible: %b",
+ show,
+ animate,
+ isDialpadVisible);
+ if (show == isDialpadVisible) {
+ return false;
+ }
+
+ FragmentManager dialpadFragmentManager = inCallActivity.getDialpadFragmentManager();
+ if (dialpadFragmentManager == null) {
+ LogUtil.i(
+ "InCallActivityCommon.showDialpadFragment", "unable to show or hide dialpad fragment");
+ return false;
+ }
+
+ // We don't do a FragmentTransaction on the hide case because it will be dealt with when
+ // the listener is fired after an animation finishes.
+ if (!animate) {
+ if (show) {
+ performShowDialpadFragment(dialpadFragmentManager);
+ } else {
+ performHideDialpadFragment();
+ }
+ } else {
+ if (show) {
+ performShowDialpadFragment(dialpadFragmentManager);
+ getDialpadFragment().animateShowDialpad();
+ }
+ getDialpadFragment()
+ .getView()
+ .startAnimation(show ? dialpadSlideInAnimation : dialpadSlideOutAnimation);
+ }
+
+ ProximitySensor sensor = InCallPresenter.getInstance().getProximitySensor();
+ if (sensor != null) {
+ sensor.onDialpadVisible(show);
+ }
+ showDialpadRequest = DIALPAD_REQUEST_NONE;
+ return true;
+ }
+
+ private void performShowDialpadFragment(@NonNull FragmentManager dialpadFragmentManager) {
+ FragmentTransaction transaction = dialpadFragmentManager.beginTransaction();
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ if (dialpadFragment == null) {
+ transaction.add(
+ inCallActivity.getDialpadContainerId(), new DialpadFragment(), TAG_DIALPAD_FRAGMENT);
+ } else {
+ transaction.show(dialpadFragment);
+ }
+
+ transaction.commitAllowingStateLoss();
+ dialpadFragmentManager.executePendingTransactions();
+
+ Logger.get(inCallActivity).logScreenView(ScreenEvent.Type.INCALL_DIALPAD, inCallActivity);
+ }
+
+ private void performHideDialpadFragment() {
+ FragmentManager fragmentManager = inCallActivity.getDialpadFragmentManager();
+ if (fragmentManager == null) {
+ LogUtil.e(
+ "InCallActivityCommon.performHideDialpadFragment", "child fragment manager is null");
+ return;
+ }
+
+ Fragment fragment = fragmentManager.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
+ if (fragment != null) {
+ FragmentTransaction transaction = fragmentManager.beginTransaction();
+ transaction.hide(fragment);
+ transaction.commitAllowingStateLoss();
+ fragmentManager.executePendingTransactions();
+ }
+ }
+
+ public boolean isDialpadVisible() {
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ return dialpadFragment != null && dialpadFragment.isVisible();
+ }
+
+ /** Returns the {@link DialpadFragment} that's shown by this activity, or {@code null} */
+ @Nullable
+ private DialpadFragment getDialpadFragment() {
+ FragmentManager fragmentManager = inCallActivity.getDialpadFragmentManager();
+ if (fragmentManager == null) {
+ return null;
+ }
+ return (DialpadFragment) fragmentManager.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
+ }
+
+ public void updateTaskDescription() {
+ Resources resources = inCallActivity.getResources();
+ int color;
+ if (resources.getBoolean(R.bool.is_layout_landscape)) {
+ color =
+ ResourcesCompat.getColor(
+ resources, R.color.statusbar_background_color, inCallActivity.getTheme());
+ } else {
+ color = InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor();
+ }
+
+ TaskDescription td =
+ new TaskDescription(resources.getString(R.string.notification_ongoing_call), null, color);
+ inCallActivity.setTaskDescription(td);
+ }
+
+ public boolean hasPendingDialogs() {
+ return dialog != null;
+ }
+
+ private void internalResolveIntent(Intent intent) {
+ if (!intent.getAction().equals(Intent.ACTION_MAIN)) {
+ return;
+ }
+
+ if (intent.hasExtra(INTENT_EXTRA_SHOW_DIALPAD)) {
+ // SHOW_DIALPAD_EXTRA can be used here to specify whether the DTMF
+ // dialpad should be initially visible. If the extra isn't
+ // present at all, we just leave the dialpad in its previous state.
+ boolean showDialpad = intent.getBooleanExtra(INTENT_EXTRA_SHOW_DIALPAD, false);
+ LogUtil.i("InCallActivityCommon.internalResolveIntent", "SHOW_DIALPAD_EXTRA: " + showDialpad);
+
+ relaunchedFromDialer(showDialpad);
+ }
+
+ DialerCall outgoingCall = CallList.getInstance().getOutgoingCall();
+ if (outgoingCall == null) {
+ outgoingCall = CallList.getInstance().getPendingOutgoingCall();
+ }
+
+ boolean isNewOutgoingCall = false;
+ if (intent.getBooleanExtra(INTENT_EXTRA_NEW_OUTGOING_CALL, false)) {
+ isNewOutgoingCall = true;
+ intent.removeExtra(INTENT_EXTRA_NEW_OUTGOING_CALL);
+
+ // InCallActivity is responsible for disconnecting a new outgoing call if there
+ // is no way of making it (i.e. no valid call capable accounts).
+ // If the version is not MSIM compatible, then ignore this code.
+ if (CompatUtils.isMSIMCompatible()
+ && InCallPresenter.isCallWithNoValidAccounts(outgoingCall)) {
+ LogUtil.i(
+ "InCallActivityCommon.internalResolveIntent",
+ "call with no valid accounts, disconnecting");
+ outgoingCall.disconnect();
+ }
+
+ dismissKeyguard(true);
+ }
+
+ boolean didShowAccountSelectionDialog = maybeShowAccountSelectionDialog();
+ inCallActivity.onResolveIntent(outgoingCall, isNewOutgoingCall, didShowAccountSelectionDialog);
+ }
+
+ private boolean maybeShowAccountSelectionDialog() {
+ DialerCall call = CallList.getInstance().getWaitingForAccountCall();
+ if (call == null) {
+ return false;
+ }
+
+ Bundle extras = call.getIntentExtras();
+ List<PhoneAccountHandle> phoneAccountHandles;
+ if (extras != null) {
+ phoneAccountHandles =
+ extras.getParcelableArrayList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS);
+ } else {
+ phoneAccountHandles = new ArrayList<>();
+ }
+
+ DialogFragment dialogFragment =
+ SelectPhoneAccountDialogFragment.newInstance(
+ R.string.select_phone_account_for_calls,
+ true,
+ phoneAccountHandles,
+ selectAccountListener,
+ call.getId());
+ dialogFragment.show(inCallActivity.getFragmentManager(), TAG_SELECT_ACCOUNT_FRAGMENT);
+ return true;
+ }
+}
diff --git a/java/com/android/incallui/InCallCameraManager.java b/java/com/android/incallui/InCallCameraManager.java
new file mode 100644
index 000000000..fdb422643
--- /dev/null
+++ b/java/com/android/incallui/InCallCameraManager.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2014 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.incallui;
+
+import android.content.Context;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraManager;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/** Used to track which camera is used for outgoing video. */
+public class InCallCameraManager {
+
+ private final Set<Listener> mCameraSelectionListeners =
+ Collections.newSetFromMap(new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
+ /** The camera ID for the front facing camera. */
+ private String mFrontFacingCameraId;
+ /** The camera ID for the rear facing camera. */
+ private String mRearFacingCameraId;
+ /** The currently active camera. */
+ private boolean mUseFrontFacingCamera;
+ /**
+ * Indicates whether the list of cameras has been initialized yet. Initialization is delayed until
+ * a video call is present.
+ */
+ private boolean mIsInitialized = false;
+ /** The context. */
+ private Context mContext;
+
+ /**
+ * Initializes the InCall CameraManager.
+ *
+ * @param context The current context.
+ */
+ public InCallCameraManager(Context context) {
+ mUseFrontFacingCamera = true;
+ mContext = context;
+ }
+
+ /**
+ * Sets whether the front facing camera should be used or not.
+ *
+ * @param useFrontFacingCamera {@code True} if the front facing camera is to be used.
+ */
+ public void setUseFrontFacingCamera(boolean useFrontFacingCamera) {
+ mUseFrontFacingCamera = useFrontFacingCamera;
+ for (Listener listener : mCameraSelectionListeners) {
+ listener.onActiveCameraSelectionChanged(mUseFrontFacingCamera);
+ }
+ }
+
+ /**
+ * Determines whether the front facing camera is currently in use.
+ *
+ * @return {@code True} if the front facing camera is in use.
+ */
+ public boolean isUsingFrontFacingCamera() {
+ return mUseFrontFacingCamera;
+ }
+
+ /**
+ * Determines the active camera ID.
+ *
+ * @return The active camera ID.
+ */
+ public String getActiveCameraId() {
+ maybeInitializeCameraList(mContext);
+
+ if (mUseFrontFacingCamera) {
+ return mFrontFacingCameraId;
+ } else {
+ return mRearFacingCameraId;
+ }
+ }
+
+ /** Calls when camera permission is granted by user. */
+ public void onCameraPermissionGranted() {
+ for (Listener listener : mCameraSelectionListeners) {
+ listener.onCameraPermissionGranted();
+ }
+ }
+
+ /**
+ * Get the list of cameras available for use.
+ *
+ * @param context The context.
+ */
+ private void maybeInitializeCameraList(Context context) {
+ if (mIsInitialized || context == null) {
+ return;
+ }
+
+ Log.v(this, "initializeCameraList");
+
+ CameraManager cameraManager = null;
+ try {
+ cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+ } catch (Exception e) {
+ Log.e(this, "Could not get camera service.");
+ return;
+ }
+
+ if (cameraManager == null) {
+ return;
+ }
+
+ String[] cameraIds = {};
+ try {
+ cameraIds = cameraManager.getCameraIdList();
+ } catch (CameraAccessException e) {
+ Log.d(this, "Could not access camera: " + e);
+ // Camera disabled by device policy.
+ return;
+ }
+
+ for (int i = 0; i < cameraIds.length; i++) {
+ CameraCharacteristics c = null;
+ try {
+ c = cameraManager.getCameraCharacteristics(cameraIds[i]);
+ } catch (IllegalArgumentException e) {
+ // Device Id is unknown.
+ } catch (CameraAccessException e) {
+ // Camera disabled by device policy.
+ }
+ if (c != null) {
+ int facingCharacteristic = c.get(CameraCharacteristics.LENS_FACING);
+ if (facingCharacteristic == CameraCharacteristics.LENS_FACING_FRONT) {
+ mFrontFacingCameraId = cameraIds[i];
+ } else if (facingCharacteristic == CameraCharacteristics.LENS_FACING_BACK) {
+ mRearFacingCameraId = cameraIds[i];
+ }
+ }
+ }
+
+ mIsInitialized = true;
+ Log.v(this, "initializeCameraList : done");
+ }
+
+ public void addCameraSelectionListener(Listener listener) {
+ if (listener != null) {
+ mCameraSelectionListeners.add(listener);
+ }
+ }
+
+ public void removeCameraSelectionListener(Listener listener) {
+ if (listener != null) {
+ mCameraSelectionListeners.remove(listener);
+ }
+ }
+
+ public interface Listener {
+
+ void onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera);
+
+ void onCameraPermissionGranted();
+ }
+}
diff --git a/java/com/android/incallui/InCallOrientationEventListener.java b/java/com/android/incallui/InCallOrientationEventListener.java
new file mode 100644
index 000000000..e6b0bc027
--- /dev/null
+++ b/java/com/android/incallui/InCallOrientationEventListener.java
@@ -0,0 +1,194 @@
+/*
+ * 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.incallui;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.support.annotation.IntDef;
+import android.view.OrientationEventListener;
+import com.android.dialer.common.LogUtil;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class listens to Orientation events and overrides onOrientationChanged which gets invoked
+ * when an orientation change occurs. When that happens, we notify InCallUI registrants of the
+ * change.
+ */
+public class InCallOrientationEventListener extends OrientationEventListener {
+
+ public static final int SCREEN_ORIENTATION_0 = 0;
+ public static final int SCREEN_ORIENTATION_90 = 90;
+ public static final int SCREEN_ORIENTATION_180 = 180;
+ public static final int SCREEN_ORIENTATION_270 = 270;
+ public static final int SCREEN_ORIENTATION_360 = 360;
+
+ /** Screen orientation angles one of 0, 90, 180, 270, 360 in degrees. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ SCREEN_ORIENTATION_0,
+ SCREEN_ORIENTATION_90,
+ SCREEN_ORIENTATION_180,
+ SCREEN_ORIENTATION_270,
+ SCREEN_ORIENTATION_360,
+ SCREEN_ORIENTATION_UNKNOWN
+ })
+ public @interface ScreenOrientation {}
+
+ // We use SCREEN_ORIENTATION_USER so that reverse-portrait is not allowed.
+ public static final int ACTIVITY_PREFERENCE_ALLOW_ROTATION = ActivityInfo.SCREEN_ORIENTATION_USER;
+
+ public static final int ACTIVITY_PREFERENCE_DISALLOW_ROTATION =
+ ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+
+ /**
+ * This is to identify dead zones where we won't notify others of orientation changed. Say for e.g
+ * our threshold is x degrees. We will only notify UI when our current rotation is within x
+ * degrees right or left of the screen orientation angles. If it's not within those ranges, we
+ * return SCREEN_ORIENTATION_UNKNOWN and ignore it.
+ */
+ public static final int SCREEN_ORIENTATION_UNKNOWN = -1;
+
+ // Rotation threshold is 10 degrees. So if the rotation angle is within 10 degrees of any of
+ // the above angles, we will notify orientation changed.
+ private static final int ROTATION_THRESHOLD = 10;
+
+ /** Cache the current rotation of the device. */
+ @ScreenOrientation private static int sCurrentOrientation = SCREEN_ORIENTATION_0;
+
+ private boolean mEnabled = false;
+
+ public InCallOrientationEventListener(Context context) {
+ super(context);
+ }
+
+ private static boolean isWithinRange(int value, int begin, int end) {
+ return value >= begin && value < end;
+ }
+
+ private static boolean isWithinThreshold(int value, int center, int threshold) {
+ return isWithinRange(value, center - threshold, center + threshold);
+ }
+
+ private static boolean isInLeftRange(int value, int center, int threshold) {
+ return isWithinRange(value, center - threshold, center);
+ }
+
+ private static boolean isInRightRange(int value, int center, int threshold) {
+ return isWithinRange(value, center, center + threshold);
+ }
+
+ @ScreenOrientation
+ public static int getCurrentOrientation() {
+ return sCurrentOrientation;
+ }
+
+ /**
+ * Handles changes in device orientation. Notifies InCallPresenter of orientation changes.
+ *
+ * <p>Note that this API receives sensor rotation in degrees as a param and we convert that to one
+ * of our screen orientation constants - (one of: {@link #SCREEN_ORIENTATION_0}, {@link
+ * #SCREEN_ORIENTATION_90}, {@link #SCREEN_ORIENTATION_180}, {@link #SCREEN_ORIENTATION_270}).
+ *
+ * @param rotation The new device sensor rotation in degrees
+ */
+ @Override
+ public void onOrientationChanged(int rotation) {
+ if (rotation == OrientationEventListener.ORIENTATION_UNKNOWN) {
+ return;
+ }
+
+ final int orientation = toScreenOrientation(rotation);
+
+ if (orientation != SCREEN_ORIENTATION_UNKNOWN && sCurrentOrientation != orientation) {
+ LogUtil.i(
+ "InCallOrientationEventListener.onOrientationChanged",
+ "orientation: %d -> %d",
+ sCurrentOrientation,
+ orientation);
+ sCurrentOrientation = orientation;
+ InCallPresenter.getInstance().onDeviceOrientationChange(sCurrentOrientation);
+ }
+ }
+
+ /**
+ * Enables the OrientationEventListener and notifies listeners of current orientation if notify
+ * flag is true
+ *
+ * @param notify true or false. Notify device orientation changed if true.
+ */
+ public void enable(boolean notify) {
+ if (mEnabled) {
+ Log.v(this, "enable: Orientation listener is already enabled. Ignoring...");
+ return;
+ }
+
+ super.enable();
+ mEnabled = true;
+ if (notify) {
+ InCallPresenter.getInstance().onDeviceOrientationChange(sCurrentOrientation);
+ }
+ }
+
+ /** Enables the OrientationEventListener with notify flag defaulting to false. */
+ @Override
+ public void enable() {
+ enable(false);
+ }
+
+ /** Disables the OrientationEventListener. */
+ @Override
+ public void disable() {
+ if (!mEnabled) {
+ Log.v(this, "enable: Orientation listener is already disabled. Ignoring...");
+ return;
+ }
+
+ mEnabled = false;
+ super.disable();
+ }
+
+ /** Returns true the OrientationEventListener is enabled, false otherwise. */
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ /**
+ * Converts sensor rotation in degrees to screen orientation constants.
+ *
+ * @param rotation sensor rotation angle in degrees
+ * @return Screen orientation angle in degrees (0, 90, 180, 270). Returns -1 for degrees not
+ * within threshold to identify zones where orientation change should not be trigerred.
+ */
+ @ScreenOrientation
+ private int toScreenOrientation(int rotation) {
+ // Sensor orientation 90 is equivalent to screen orientation 270 and vice versa. This
+ // function returns the screen orientation. So we convert sensor rotation 90 to 270 and
+ // vice versa here.
+ if (isInLeftRange(rotation, SCREEN_ORIENTATION_360, ROTATION_THRESHOLD)
+ || isInRightRange(rotation, SCREEN_ORIENTATION_0, ROTATION_THRESHOLD)) {
+ return SCREEN_ORIENTATION_0;
+ } else if (isWithinThreshold(rotation, SCREEN_ORIENTATION_90, ROTATION_THRESHOLD)) {
+ return SCREEN_ORIENTATION_270;
+ } else if (isWithinThreshold(rotation, SCREEN_ORIENTATION_180, ROTATION_THRESHOLD)) {
+ return SCREEN_ORIENTATION_180;
+ } else if (isWithinThreshold(rotation, SCREEN_ORIENTATION_270, ROTATION_THRESHOLD)) {
+ return SCREEN_ORIENTATION_90;
+ }
+ return SCREEN_ORIENTATION_UNKNOWN;
+ }
+}
diff --git a/java/com/android/incallui/InCallPresenter.java b/java/com/android/incallui/InCallPresenter.java
new file mode 100644
index 000000000..97105fb78
--- /dev/null
+++ b/java/com/android/incallui/InCallPresenter.java
@@ -0,0 +1,1679 @@
+/*
+ * Copyright (C) 2013 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.incallui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.telecom.Call.Details;
+import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+import android.view.Window;
+import android.view.WindowManager;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.compat.CallCompat;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener;
+import com.android.dialer.blocking.FilteredNumbersUtil;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.InteractionEvent;
+import com.android.dialer.telecom.TelecomUtil;
+import com.android.dialer.util.TouchPointManager;
+import com.android.incallui.InCallOrientationEventListener.ScreenOrientation;
+import com.android.incallui.answerproximitysensor.PseudoScreenState;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.DialerCall.SessionModificationState;
+import com.android.incallui.call.ExternalCallList;
+import com.android.incallui.call.InCallVideoCallCallbackNotifier;
+import com.android.incallui.call.TelecomAdapter;
+import com.android.incallui.call.VideoUtils;
+import com.android.incallui.latencyreport.LatencyReport;
+import com.android.incallui.legacyblocking.BlockedNumberContentObserver;
+import com.android.incallui.spam.SpamCallListListener;
+import com.android.incallui.util.TelecomCallUtil;
+import com.android.incallui.videosurface.bindings.VideoSurfaceBindings;
+import com.android.incallui.videosurface.protocol.VideoSurfaceTexture;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Takes updates from the CallList and notifies the InCallActivity (UI) of the changes. Responsible
+ * for starting the activity for a new call and finishing the activity when all calls are
+ * disconnected. Creates and manages the in-call state and provides a listener pattern for the
+ * presenters that want to listen in on the in-call state changes. TODO: This class has become more
+ * of a state machine at this point. Consider renaming.
+ */
+public class InCallPresenter
+ implements CallList.Listener, InCallVideoCallCallbackNotifier.SessionModificationListener {
+
+ private static final String EXTRA_FIRST_TIME_SHOWN =
+ "com.android.incallui.intent.extra.FIRST_TIME_SHOWN";
+
+ private static final long BLOCK_QUERY_TIMEOUT_MS = 1000;
+
+ private static final Bundle EMPTY_EXTRAS = new Bundle();
+
+ private static InCallPresenter sInCallPresenter;
+
+ /**
+ * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is load factor before
+ * resizing, 1 means we only expect a single thread to access the map so make only a single shard
+ */
+ private final Set<InCallStateListener> mListeners =
+ Collections.newSetFromMap(new ConcurrentHashMap<InCallStateListener, Boolean>(8, 0.9f, 1));
+
+ private final List<IncomingCallListener> mIncomingCallListeners = new CopyOnWriteArrayList<>();
+ private final Set<InCallDetailsListener> mDetailsListeners =
+ Collections.newSetFromMap(new ConcurrentHashMap<InCallDetailsListener, Boolean>(8, 0.9f, 1));
+ private final Set<CanAddCallListener> mCanAddCallListeners =
+ Collections.newSetFromMap(new ConcurrentHashMap<CanAddCallListener, Boolean>(8, 0.9f, 1));
+ private final Set<InCallUiListener> mInCallUiListeners =
+ Collections.newSetFromMap(new ConcurrentHashMap<InCallUiListener, Boolean>(8, 0.9f, 1));
+ private final Set<InCallOrientationListener> mOrientationListeners =
+ Collections.newSetFromMap(
+ new ConcurrentHashMap<InCallOrientationListener, Boolean>(8, 0.9f, 1));
+ private final Set<InCallEventListener> mInCallEventListeners =
+ Collections.newSetFromMap(new ConcurrentHashMap<InCallEventListener, Boolean>(8, 0.9f, 1));
+
+ private StatusBarNotifier mStatusBarNotifier;
+ private ExternalCallNotifier mExternalCallNotifier;
+ private ContactInfoCache mContactInfoCache;
+ private Context mContext;
+ private final OnCheckBlockedListener mOnCheckBlockedListener =
+ new OnCheckBlockedListener() {
+ @Override
+ public void onCheckComplete(final Integer id) {
+ if (id != null && id != FilteredNumberAsyncQueryHandler.INVALID_ID) {
+ // Silence the ringer now to prevent ringing and vibration before the call is
+ // terminated when Telecom attempts to add it.
+ TelecomUtil.silenceRinger(mContext);
+ }
+ }
+ };
+ private CallList mCallList;
+ private ExternalCallList mExternalCallList;
+ private InCallActivity mInCallActivity;
+ private ManageConferenceActivity mManageConferenceActivity;
+ private final android.telecom.Call.Callback mCallCallback =
+ new android.telecom.Call.Callback() {
+ @Override
+ public void onPostDialWait(
+ android.telecom.Call telecomCall, String remainingPostDialSequence) {
+ final DialerCall call = mCallList.getDialerCallFromTelecomCall(telecomCall);
+ if (call == null) {
+ Log.w(this, "DialerCall not found in call list: " + telecomCall);
+ return;
+ }
+ onPostDialCharWait(call.getId(), remainingPostDialSequence);
+ }
+
+ @Override
+ public void onDetailsChanged(
+ android.telecom.Call telecomCall, android.telecom.Call.Details details) {
+ final DialerCall call = mCallList.getDialerCallFromTelecomCall(telecomCall);
+ if (call == null) {
+ Log.w(this, "DialerCall not found in call list: " + telecomCall);
+ return;
+ }
+
+ if (details.hasProperty(Details.PROPERTY_IS_EXTERNAL_CALL)
+ && !mExternalCallList.isCallTracked(telecomCall)) {
+
+ // A regular call became an external call so swap call lists.
+ Log.i(this, "Call became external: " + telecomCall);
+ mCallList.onInternalCallMadeExternal(mContext, telecomCall);
+ mExternalCallList.onCallAdded(telecomCall);
+ return;
+ }
+
+ for (InCallDetailsListener listener : mDetailsListeners) {
+ listener.onDetailsChanged(call, details);
+ }
+ }
+
+ @Override
+ public void onConferenceableCallsChanged(
+ android.telecom.Call telecomCall, List<android.telecom.Call> conferenceableCalls) {
+ Log.i(this, "onConferenceableCallsChanged: " + telecomCall);
+ onDetailsChanged(telecomCall, telecomCall.getDetails());
+ }
+ };
+ private InCallState mInCallState = InCallState.NO_CALLS;
+ private ProximitySensor mProximitySensor;
+ private final PseudoScreenState mPseudoScreenState = new PseudoScreenState();
+ private boolean mServiceConnected;
+ private boolean mAccountSelectionCancelled;
+ private InCallCameraManager mInCallCameraManager;
+ private FilteredNumberAsyncQueryHandler mFilteredQueryHandler;
+ private CallList.Listener mSpamCallListListener;
+ /** Whether or not we are currently bound and waiting for Telecom to send us a new call. */
+ private boolean mBoundAndWaitingForOutgoingCall;
+ /** Determines if the InCall UI is in fullscreen mode or not. */
+ private boolean mIsFullScreen = false;
+
+ private PhoneStateListener mPhoneStateListener =
+ new PhoneStateListener() {
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ if (state == TelephonyManager.CALL_STATE_RINGING) {
+ if (FilteredNumbersUtil.hasRecentEmergencyCall(mContext)) {
+ return;
+ }
+ // Check if the number is blocked, to silence the ringer.
+ String countryIso = GeoUtil.getCurrentCountryIso(mContext);
+ mFilteredQueryHandler.isBlockedNumber(
+ mOnCheckBlockedListener, incomingNumber, countryIso);
+ }
+ }
+ };
+ /**
+ * Is true when the activity has been previously started. Some code needs to know not just if the
+ * activity is currently up, but if it had been previously shown in foreground for this in-call
+ * session (e.g., StatusBarNotifier). This gets reset when the session ends in the tear-down
+ * method.
+ */
+ private boolean mIsActivityPreviouslyStarted = false;
+
+ /** Whether or not InCallService is bound to Telecom. */
+ private boolean mServiceBound = false;
+
+ /**
+ * When configuration changes Android kills the current activity and starts a new one. The flag is
+ * used to check if full clean up is necessary (activity is stopped and new activity won't be
+ * started), or if a new activity will be started right after the current one is destroyed, and
+ * therefore no need in release all resources.
+ */
+ private boolean mIsChangingConfigurations = false;
+
+ private boolean mAwaitingCallListUpdate = false;
+
+ private ExternalCallList.ExternalCallListener mExternalCallListener =
+ new ExternalCallList.ExternalCallListener() {
+
+ @Override
+ public void onExternalCallPulled(android.telecom.Call call) {
+ // Note: keep this code in sync with InCallPresenter#onCallAdded
+ LatencyReport latencyReport = new LatencyReport(call);
+ latencyReport.onCallBlockingDone();
+ // Note: External calls do not require spam checking.
+ mCallList.onCallAdded(mContext, call, latencyReport);
+ call.registerCallback(mCallCallback);
+ }
+
+ @Override
+ public void onExternalCallAdded(android.telecom.Call call) {
+ // No-op
+ }
+
+ @Override
+ public void onExternalCallRemoved(android.telecom.Call call) {
+ // No-op
+ }
+
+ @Override
+ public void onExternalCallUpdated(android.telecom.Call call) {
+ // No-op
+ }
+ };
+
+ private ThemeColorManager mThemeColorManager;
+ private VideoSurfaceTexture mLocalVideoSurfaceTexture;
+ private VideoSurfaceTexture mRemoteVideoSurfaceTexture;
+
+ /** Inaccessible constructor. Must use getInstance() to get this singleton. */
+ @VisibleForTesting
+ InCallPresenter() {}
+
+ public static synchronized InCallPresenter getInstance() {
+ if (sInCallPresenter == null) {
+ sInCallPresenter = new InCallPresenter();
+ }
+ return sInCallPresenter;
+ }
+
+ /**
+ * Determines whether or not a call has no valid phone accounts that can be used to make the call
+ * with. Emergency calls do not require a phone account.
+ *
+ * @param call to check accounts for.
+ * @return {@code true} if the call has no call capable phone accounts set, {@code false} if the
+ * call contains a phone account that could be used to initiate it with, or is an emergency
+ * call.
+ */
+ public static boolean isCallWithNoValidAccounts(DialerCall call) {
+ if (call != null && !call.isEmergencyCall()) {
+ Bundle extras = call.getIntentExtras();
+
+ if (extras == null) {
+ extras = EMPTY_EXTRAS;
+ }
+
+ final List<PhoneAccountHandle> phoneAccountHandles =
+ extras.getParcelableArrayList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS);
+
+ if ((call.getAccountHandle() == null
+ && (phoneAccountHandles == null || phoneAccountHandles.isEmpty()))) {
+ Log.i(InCallPresenter.getInstance(), "No valid accounts for call " + call);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public InCallState getInCallState() {
+ return mInCallState;
+ }
+
+ public CallList getCallList() {
+ return mCallList;
+ }
+
+ public void setUp(
+ @NonNull Context context,
+ CallList callList,
+ ExternalCallList externalCallList,
+ StatusBarNotifier statusBarNotifier,
+ ExternalCallNotifier externalCallNotifier,
+ ContactInfoCache contactInfoCache,
+ ProximitySensor proximitySensor) {
+ if (mServiceConnected) {
+ Log.i(this, "New service connection replacing existing one.");
+ if (context != mContext || callList != mCallList) {
+ throw new IllegalStateException();
+ }
+ return;
+ }
+
+ Objects.requireNonNull(context);
+ mContext = context;
+
+ mContactInfoCache = contactInfoCache;
+
+ mStatusBarNotifier = statusBarNotifier;
+ mExternalCallNotifier = externalCallNotifier;
+ addListener(mStatusBarNotifier);
+
+ mProximitySensor = proximitySensor;
+ addListener(mProximitySensor);
+
+ mThemeColorManager =
+ new ThemeColorManager(new InCallUIMaterialColorMapUtils(mContext.getResources()));
+
+ mCallList = callList;
+ mExternalCallList = externalCallList;
+ externalCallList.addExternalCallListener(mExternalCallNotifier);
+ externalCallList.addExternalCallListener(mExternalCallListener);
+
+ // This only gets called by the service so this is okay.
+ mServiceConnected = true;
+
+ // The final thing we do in this set up is add ourselves as a listener to CallList. This
+ // will kick off an update and the whole process can start.
+ mCallList.addListener(this);
+
+ // Create spam call list listener and add it to the list of listeners
+ mSpamCallListListener = new SpamCallListListener(context);
+ mCallList.addListener(mSpamCallListListener);
+
+ VideoPauseController.getInstance().setUp(this);
+ InCallVideoCallCallbackNotifier.getInstance().addSessionModificationListener(this);
+
+ mFilteredQueryHandler = new FilteredNumberAsyncQueryHandler(context);
+ mContext
+ .getSystemService(TelephonyManager.class)
+ .listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+
+ Log.d(this, "Finished InCallPresenter.setUp");
+ }
+
+ /**
+ * Called when the telephony service has disconnected from us. This will happen when there are no
+ * more active calls. However, we may still want to continue showing the UI for certain cases like
+ * showing "Call Ended". What we really want is to wait for the activity and the service to both
+ * disconnect before we tear things down. This method sets a serviceConnected boolean and calls a
+ * secondary method that performs the aforementioned logic.
+ */
+ public void tearDown() {
+ Log.d(this, "tearDown");
+ mCallList.clearOnDisconnect();
+
+ mServiceConnected = false;
+
+ mContext
+ .getSystemService(TelephonyManager.class)
+ .listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+
+ attemptCleanup();
+ VideoPauseController.getInstance().tearDown();
+ InCallVideoCallCallbackNotifier.getInstance().removeSessionModificationListener(this);
+ }
+
+ private void attemptFinishActivity() {
+ final boolean doFinish = (mInCallActivity != null && isActivityStarted());
+ Log.i(this, "Hide in call UI: " + doFinish);
+ if (doFinish) {
+ mInCallActivity.setExcludeFromRecents(true);
+ mInCallActivity.finish();
+
+ if (mAccountSelectionCancelled) {
+ // This finish is a result of account selection cancellation
+ // do not include activity ending transition
+ mInCallActivity.overridePendingTransition(0, 0);
+ }
+ }
+ }
+
+ /**
+ * Called when the UI ends. Attempts to tear down everything if necessary. See {@link #tearDown()}
+ * for more insight on the tear-down process.
+ */
+ public void unsetActivity(InCallActivity inCallActivity) {
+ if (inCallActivity == null) {
+ throw new IllegalArgumentException("unregisterActivity cannot be called with null");
+ }
+ if (mInCallActivity == null) {
+ Log.i(this, "No InCallActivity currently set, no need to unset.");
+ return;
+ }
+ if (mInCallActivity != inCallActivity) {
+ Log.w(
+ this,
+ "Second instance of InCallActivity is trying to unregister when another"
+ + " instance is active. Ignoring.");
+ return;
+ }
+ updateActivity(null);
+ }
+
+ /**
+ * Updates the current instance of {@link InCallActivity} with the provided one. If a {@code null}
+ * activity is provided, it means that the activity was finished and we should attempt to cleanup.
+ */
+ private void updateActivity(InCallActivity inCallActivity) {
+ boolean updateListeners = false;
+ boolean doAttemptCleanup = false;
+
+ if (inCallActivity != null) {
+ if (mInCallActivity == null) {
+ updateListeners = true;
+ Log.i(this, "UI Initialized");
+ } else {
+ // since setActivity is called onStart(), it can be called multiple times.
+ // This is fine and ignorable, but we do not want to update the world every time
+ // this happens (like going to/from background) so we do not set updateListeners.
+ }
+
+ mInCallActivity = inCallActivity;
+ mInCallActivity.setExcludeFromRecents(false);
+
+ // By the time the UI finally comes up, the call may already be disconnected.
+ // If that's the case, we may need to show an error dialog.
+ if (mCallList != null && mCallList.getDisconnectedCall() != null) {
+ maybeShowErrorDialogOnDisconnect(mCallList.getDisconnectedCall());
+ }
+
+ // When the UI comes up, we need to first check the in-call state.
+ // If we are showing NO_CALLS, that means that a call probably connected and
+ // then immediately disconnected before the UI was able to come up.
+ // If we dont have any calls, start tearing down the UI instead.
+ // NOTE: This code relies on {@link #mInCallActivity} being set so we run it after
+ // it has been set.
+ if (mInCallState == InCallState.NO_CALLS) {
+ Log.i(this, "UI Initialized, but no calls left. shut down.");
+ attemptFinishActivity();
+ return;
+ }
+ } else {
+ Log.i(this, "UI Destroyed");
+ updateListeners = true;
+ mInCallActivity = null;
+
+ // We attempt cleanup for the destroy case but only after we recalculate the state
+ // to see if we need to come back up or stay shut down. This is why we do the
+ // cleanup after the call to onCallListChange() instead of directly here.
+ doAttemptCleanup = true;
+ }
+
+ // Messages can come from the telephony layer while the activity is coming up
+ // and while the activity is going down. So in both cases we need to recalculate what
+ // state we should be in after they complete.
+ // Examples: (1) A new incoming call could come in and then get disconnected before
+ // the activity is created.
+ // (2) All calls could disconnect and then get a new incoming call before the
+ // activity is destroyed.
+ //
+ // b/1122139 - We previously had a check for mServiceConnected here as well, but there are
+ // cases where we need to recalculate the current state even if the service in not
+ // connected. In particular the case where startOrFinish() is called while the app is
+ // already finish()ing. In that case, we skip updating the state with the knowledge that
+ // we will check again once the activity has finished. That means we have to recalculate the
+ // state here even if the service is disconnected since we may not have finished a state
+ // transition while finish()ing.
+ if (updateListeners) {
+ onCallListChange(mCallList);
+ }
+
+ if (doAttemptCleanup) {
+ attemptCleanup();
+ }
+ }
+
+ public void setManageConferenceActivity(
+ @Nullable ManageConferenceActivity manageConferenceActivity) {
+ mManageConferenceActivity = manageConferenceActivity;
+ }
+
+ public void onBringToForeground(boolean showDialpad) {
+ Log.i(this, "Bringing UI to foreground.");
+ bringToForeground(showDialpad);
+ }
+
+ public void onCallAdded(final android.telecom.Call call) {
+ LatencyReport latencyReport = new LatencyReport(call);
+ if (shouldAttemptBlocking(call)) {
+ maybeBlockCall(call, latencyReport);
+ } else {
+ if (call.getDetails().hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) {
+ mExternalCallList.onCallAdded(call);
+ } else {
+ latencyReport.onCallBlockingDone();
+ mCallList.onCallAdded(mContext, call, latencyReport);
+ }
+ }
+
+ // Since a call has been added we are no longer waiting for Telecom to send us a call.
+ setBoundAndWaitingForOutgoingCall(false, null);
+ call.registerCallback(mCallCallback);
+ }
+
+ private boolean shouldAttemptBlocking(android.telecom.Call call) {
+ if (call.getState() != android.telecom.Call.STATE_RINGING) {
+ return false;
+ }
+ if (TelecomCallUtil.isEmergencyCall(call)) {
+ Log.i(this, "Not attempting to block incoming emergency call");
+ return false;
+ }
+ if (FilteredNumbersUtil.hasRecentEmergencyCall(mContext)) {
+ Log.i(this, "Not attempting to block incoming call due to recent emergency call");
+ return false;
+ }
+ if (call.getDetails().hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Checks whether a call should be blocked, and blocks it if so. Otherwise, it adds the call to
+ * the CallList so it can proceed as normal. There is a timeout, so if the function for checking
+ * whether a function is blocked does not return in a reasonable time, we proceed with adding the
+ * call anyways.
+ */
+ private void maybeBlockCall(final android.telecom.Call call, final LatencyReport latencyReport) {
+ final String countryIso = GeoUtil.getCurrentCountryIso(mContext);
+ final String number = TelecomCallUtil.getNumber(call);
+ final long timeAdded = System.currentTimeMillis();
+
+ // Though AtomicBoolean's can be scary, don't fear, as in this case it is only used on the
+ // main UI thread. It is needed so we can change its value within different scopes, since
+ // that cannot be done with a final boolean.
+ final AtomicBoolean hasTimedOut = new AtomicBoolean(false);
+
+ final Handler handler = new Handler();
+
+ // Proceed if the query is slow; the call may still be blocked after the query returns.
+ final Runnable runnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ hasTimedOut.set(true);
+ latencyReport.onCallBlockingDone();
+ mCallList.onCallAdded(mContext, call, latencyReport);
+ }
+ };
+ handler.postDelayed(runnable, BLOCK_QUERY_TIMEOUT_MS);
+
+ OnCheckBlockedListener onCheckBlockedListener =
+ new OnCheckBlockedListener() {
+ @Override
+ public void onCheckComplete(final Integer id) {
+ if (isReadyForTearDown()) {
+ Log.i(this, "InCallPresenter is torn down, not adding call");
+ return;
+ }
+ if (!hasTimedOut.get()) {
+ handler.removeCallbacks(runnable);
+ }
+ if (id == null) {
+ if (!hasTimedOut.get()) {
+ latencyReport.onCallBlockingDone();
+ mCallList.onCallAdded(mContext, call, latencyReport);
+ }
+ } else if (id == FilteredNumberAsyncQueryHandler.INVALID_ID) {
+ Log.d(this, "checkForBlockedCall: invalid number, skipping block checking");
+ if (!hasTimedOut.get()) {
+ handler.removeCallbacks(runnable);
+
+ latencyReport.onCallBlockingDone();
+ mCallList.onCallAdded(mContext, call, latencyReport);
+ }
+ } else {
+ Log.i(this, "Rejecting incoming call from blocked number");
+ call.reject(false, null);
+ Logger.get(mContext).logInteraction(InteractionEvent.Type.CALL_BLOCKED);
+
+ /*
+ * If mContext is null, then the InCallPresenter was torn down before the
+ * block check had a chance to complete. The context is no longer valid, so
+ * don't attempt to remove the call log entry.
+ */
+ if (mContext == null) {
+ return;
+ }
+ // Register observer to update the call log.
+ // BlockedNumberContentObserver will unregister after successful log or timeout.
+ BlockedNumberContentObserver contentObserver =
+ new BlockedNumberContentObserver(mContext, new Handler(), number, timeAdded);
+ contentObserver.register();
+ }
+ }
+ };
+
+ mFilteredQueryHandler.isBlockedNumber(onCheckBlockedListener, number, countryIso);
+ }
+
+ public void onCallRemoved(android.telecom.Call call) {
+ if (call.getDetails().hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) {
+ mExternalCallList.onCallRemoved(call);
+ } else {
+ mCallList.onCallRemoved(mContext, call);
+ call.unregisterCallback(mCallCallback);
+ }
+ }
+
+ public void onCanAddCallChanged(boolean canAddCall) {
+ for (CanAddCallListener listener : mCanAddCallListeners) {
+ listener.onCanAddCallChanged(canAddCall);
+ }
+ }
+
+ @Override
+ public void onWiFiToLteHandover(DialerCall call) {
+ if (mInCallActivity != null) {
+ mInCallActivity.onWiFiToLteHandover(call);
+ }
+ }
+
+ @Override
+ public void onHandoverToWifiFailed(DialerCall call) {
+ if (mInCallActivity != null) {
+ mInCallActivity.onHandoverToWifiFailed(call);
+ }
+ }
+
+ /**
+ * Called when there is a change to the call list. Sets the In-Call state for the entire in-call
+ * app based on the information it gets from CallList. Dispatches the in-call state to all
+ * listeners. Can trigger the creation or destruction of the UI based on the states that is
+ * calculates.
+ */
+ @Override
+ public void onCallListChange(CallList callList) {
+ if (mInCallActivity != null && mInCallActivity.isInCallScreenAnimating()) {
+ mAwaitingCallListUpdate = true;
+ return;
+ }
+ if (callList == null) {
+ return;
+ }
+
+ mAwaitingCallListUpdate = false;
+
+ InCallState newState = getPotentialStateFromCallList(callList);
+ InCallState oldState = mInCallState;
+ Log.d(this, "onCallListChange oldState= " + oldState + " newState=" + newState);
+ newState = startOrFinishUi(newState);
+ Log.d(this, "onCallListChange newState changed to " + newState);
+
+ // Set the new state before announcing it to the world
+ Log.i(this, "Phone switching state: " + oldState + " -> " + newState);
+ mInCallState = newState;
+
+ // notify listeners of new state
+ for (InCallStateListener listener : mListeners) {
+ Log.d(this, "Notify " + listener + " of state " + mInCallState.toString());
+ listener.onStateChange(oldState, mInCallState, callList);
+ }
+
+ if (isActivityStarted()) {
+ final boolean hasCall =
+ callList.getActiveOrBackgroundCall() != null || callList.getOutgoingCall() != null;
+ mInCallActivity.dismissKeyguard(hasCall);
+ }
+ }
+
+ /** Called when there is a new incoming call. */
+ @Override
+ public void onIncomingCall(DialerCall call) {
+ InCallState newState = startOrFinishUi(InCallState.INCOMING);
+ InCallState oldState = mInCallState;
+
+ Log.i(this, "Phone switching state: " + oldState + " -> " + newState);
+ mInCallState = newState;
+
+ for (IncomingCallListener listener : mIncomingCallListeners) {
+ listener.onIncomingCall(oldState, mInCallState, call);
+ }
+
+ if (mInCallActivity != null) {
+ // Re-evaluate which fragment is being shown.
+ mInCallActivity.onPrimaryCallStateChanged();
+ }
+ }
+
+ @Override
+ public void onUpgradeToVideo(DialerCall call) {
+ if (call.getSessionModificationState()
+ == DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST
+ && mInCallState == InCallPresenter.InCallState.INCOMING) {
+ LogUtil.i(
+ "InCallPresenter.onUpgradeToVideo",
+ "rejecting upgrade request due to existing incoming call");
+ call.declineUpgradeRequest();
+ }
+
+ if (mInCallActivity != null) {
+ // Re-evaluate which fragment is being shown.
+ mInCallActivity.onPrimaryCallStateChanged();
+ }
+ }
+
+ @Override
+ public void onSessionModificationStateChange(@SessionModificationState int newState) {
+ LogUtil.i("InCallPresenter.onSessionModificationStateChange", "state: %d", newState);
+ if (mProximitySensor == null) {
+ LogUtil.i("InCallPresenter.onSessionModificationStateChange", "proximitySensor is null");
+ return;
+ }
+ mProximitySensor.setIsAttemptingVideoCall(
+ VideoUtils.hasSentVideoUpgradeRequest(newState)
+ || VideoUtils.hasReceivedVideoUpgradeRequest(newState));
+ if (mInCallActivity != null) {
+ // Re-evaluate which fragment is being shown.
+ mInCallActivity.onPrimaryCallStateChanged();
+ }
+ }
+
+ /**
+ * Called when a call becomes disconnected. Called everytime an existing call changes from being
+ * connected (incoming/outgoing/active) to disconnected.
+ */
+ @Override
+ public void onDisconnect(DialerCall call) {
+ maybeShowErrorDialogOnDisconnect(call);
+
+ // We need to do the run the same code as onCallListChange.
+ onCallListChange(mCallList);
+
+ if (isActivityStarted()) {
+ mInCallActivity.dismissKeyguard(false);
+ }
+
+ if (call.isEmergencyCall()) {
+ FilteredNumbersUtil.recordLastEmergencyCallTime(mContext);
+ }
+ }
+
+ @Override
+ public void onUpgradeToVideoRequest(DialerCall call, int videoState) {
+ LogUtil.d(
+ "InCallPresenter.onUpgradeToVideoRequest",
+ "call = " + call + " video state = " + videoState);
+
+ if (call == null) {
+ return;
+ }
+
+ call.setRequestedVideoState(videoState);
+ }
+
+ /** Given the call list, return the state in which the in-call screen should be. */
+ public InCallState getPotentialStateFromCallList(CallList callList) {
+
+ InCallState newState = InCallState.NO_CALLS;
+
+ if (callList == null) {
+ return newState;
+ }
+ if (callList.getIncomingCall() != null) {
+ newState = InCallState.INCOMING;
+ } else if (callList.getWaitingForAccountCall() != null) {
+ newState = InCallState.WAITING_FOR_ACCOUNT;
+ } else if (callList.getPendingOutgoingCall() != null) {
+ newState = InCallState.PENDING_OUTGOING;
+ } else if (callList.getOutgoingCall() != null) {
+ newState = InCallState.OUTGOING;
+ } else if (callList.getActiveCall() != null
+ || callList.getBackgroundCall() != null
+ || callList.getDisconnectedCall() != null
+ || callList.getDisconnectingCall() != null) {
+ newState = InCallState.INCALL;
+ }
+
+ if (newState == InCallState.NO_CALLS) {
+ if (mBoundAndWaitingForOutgoingCall) {
+ return InCallState.OUTGOING;
+ }
+ }
+
+ return newState;
+ }
+
+ public boolean isBoundAndWaitingForOutgoingCall() {
+ return mBoundAndWaitingForOutgoingCall;
+ }
+
+ public void setBoundAndWaitingForOutgoingCall(boolean isBound, PhoneAccountHandle handle) {
+ Log.i(this, "setBoundAndWaitingForOutgoingCall: " + isBound);
+ mBoundAndWaitingForOutgoingCall = isBound;
+ mThemeColorManager.setPendingPhoneAccountHandle(handle);
+ if (isBound && mInCallState == InCallState.NO_CALLS) {
+ mInCallState = InCallState.OUTGOING;
+ }
+ }
+
+ public void onShrinkAnimationComplete() {
+ if (mAwaitingCallListUpdate) {
+ onCallListChange(mCallList);
+ }
+ }
+
+ public void addIncomingCallListener(IncomingCallListener listener) {
+ Objects.requireNonNull(listener);
+ mIncomingCallListeners.add(listener);
+ }
+
+ public void removeIncomingCallListener(IncomingCallListener listener) {
+ if (listener != null) {
+ mIncomingCallListeners.remove(listener);
+ }
+ }
+
+ public void addListener(InCallStateListener listener) {
+ Objects.requireNonNull(listener);
+ mListeners.add(listener);
+ }
+
+ public void removeListener(InCallStateListener listener) {
+ if (listener != null) {
+ mListeners.remove(listener);
+ }
+ }
+
+ public void addDetailsListener(InCallDetailsListener listener) {
+ Objects.requireNonNull(listener);
+ mDetailsListeners.add(listener);
+ }
+
+ public void removeDetailsListener(InCallDetailsListener listener) {
+ if (listener != null) {
+ mDetailsListeners.remove(listener);
+ }
+ }
+
+ public void addCanAddCallListener(CanAddCallListener listener) {
+ Objects.requireNonNull(listener);
+ mCanAddCallListeners.add(listener);
+ }
+
+ public void removeCanAddCallListener(CanAddCallListener listener) {
+ if (listener != null) {
+ mCanAddCallListeners.remove(listener);
+ }
+ }
+
+ public void addOrientationListener(InCallOrientationListener listener) {
+ Objects.requireNonNull(listener);
+ mOrientationListeners.add(listener);
+ }
+
+ public void removeOrientationListener(InCallOrientationListener listener) {
+ if (listener != null) {
+ mOrientationListeners.remove(listener);
+ }
+ }
+
+ public void addInCallEventListener(InCallEventListener listener) {
+ Objects.requireNonNull(listener);
+ mInCallEventListeners.add(listener);
+ }
+
+ public void removeInCallEventListener(InCallEventListener listener) {
+ if (listener != null) {
+ mInCallEventListeners.remove(listener);
+ }
+ }
+
+ public ProximitySensor getProximitySensor() {
+ return mProximitySensor;
+ }
+
+ public PseudoScreenState getPseudoScreenState() {
+ return mPseudoScreenState;
+ }
+
+ /** Returns true if the incall app is the foreground application. */
+ public boolean isShowingInCallUi() {
+ if (!isActivityStarted()) {
+ return false;
+ }
+ if (mManageConferenceActivity != null && mManageConferenceActivity.isVisible()) {
+ return true;
+ }
+ return mInCallActivity.isVisible();
+ }
+
+ /**
+ * Returns true if the activity has been created and is running. Returns true as long as activity
+ * is not destroyed or finishing. This ensures that we return true even if the activity is paused
+ * (not in foreground).
+ */
+ public boolean isActivityStarted() {
+ return (mInCallActivity != null
+ && !mInCallActivity.isDestroyed()
+ && !mInCallActivity.isFinishing());
+ }
+
+ /**
+ * Determines if the In-Call app is currently changing configuration.
+ *
+ * @return {@code true} if the In-Call app is changing configuration.
+ */
+ public boolean isChangingConfigurations() {
+ return mIsChangingConfigurations;
+ }
+
+ /**
+ * Tracks whether the In-Call app is currently in the process of changing configuration (i.e.
+ * screen orientation).
+ */
+ /*package*/
+ void updateIsChangingConfigurations() {
+ mIsChangingConfigurations = false;
+ if (mInCallActivity != null) {
+ mIsChangingConfigurations = mInCallActivity.isChangingConfigurations();
+ }
+ Log.v(this, "updateIsChangingConfigurations = " + mIsChangingConfigurations);
+ }
+
+ /** Called when the activity goes in/out of the foreground. */
+ public void onUiShowing(boolean showing) {
+ // We need to update the notification bar when we leave the UI because that
+ // could trigger it to show again.
+ if (mStatusBarNotifier != null) {
+ mStatusBarNotifier.updateNotification(mCallList);
+ }
+
+ if (mProximitySensor != null) {
+ mProximitySensor.onInCallShowing(showing);
+ }
+
+ Intent broadcastIntent = Bindings.get(mContext).getUiReadyBroadcastIntent(mContext);
+ if (broadcastIntent != null) {
+ broadcastIntent.putExtra(EXTRA_FIRST_TIME_SHOWN, !mIsActivityPreviouslyStarted);
+
+ if (showing) {
+ Log.d(this, "Sending sticky broadcast: ", broadcastIntent);
+ mContext.sendStickyBroadcast(broadcastIntent);
+ } else {
+ Log.d(this, "Removing sticky broadcast: ", broadcastIntent);
+ mContext.removeStickyBroadcast(broadcastIntent);
+ }
+ }
+
+ if (showing) {
+ mIsActivityPreviouslyStarted = true;
+ } else {
+ updateIsChangingConfigurations();
+ }
+
+ for (InCallUiListener listener : mInCallUiListeners) {
+ listener.onUiShowing(showing);
+ }
+
+ if (mInCallActivity != null) {
+ // Re-evaluate which fragment is being shown.
+ mInCallActivity.onPrimaryCallStateChanged();
+ }
+ }
+
+ public void addInCallUiListener(InCallUiListener listener) {
+ mInCallUiListeners.add(listener);
+ }
+
+ public boolean removeInCallUiListener(InCallUiListener listener) {
+ return mInCallUiListeners.remove(listener);
+ }
+
+ /*package*/
+ void onActivityStarted() {
+ Log.d(this, "onActivityStarted");
+ notifyVideoPauseController(true);
+ mStatusBarNotifier.updateNotification(mCallList);
+ }
+
+ /*package*/
+ void onActivityStopped() {
+ Log.d(this, "onActivityStopped");
+ notifyVideoPauseController(false);
+ }
+
+ private void notifyVideoPauseController(boolean showing) {
+ Log.d(
+ this, "notifyVideoPauseController: mIsChangingConfigurations=" + mIsChangingConfigurations);
+ if (!mIsChangingConfigurations) {
+ VideoPauseController.getInstance().onUiShowing(showing);
+ }
+ }
+
+ /** Brings the app into the foreground if possible. */
+ public void bringToForeground(boolean showDialpad) {
+ // Before we bring the incall UI to the foreground, we check to see if:
+ // 1. It is not currently in the foreground
+ // 2. We are in a state where we want to show the incall ui (i.e. there are calls to
+ // be displayed)
+ // If the activity hadn't actually been started previously, yet there are still calls
+ // present (e.g. a call was accepted by a bluetooth or wired headset), we want to
+ // bring it up the UI regardless.
+ if (!isShowingInCallUi() && mInCallState != InCallState.NO_CALLS) {
+ showInCall(showDialpad, false /* newOutgoingCall */, false /* isVideoCall */);
+ }
+ }
+
+ public void onPostDialCharWait(String callId, String chars) {
+ if (isActivityStarted()) {
+ mInCallActivity.showPostCharWaitDialog(callId, chars);
+ }
+ }
+
+ /**
+ * Handles the green CALL key while in-call.
+ *
+ * @return true if we consumed the event.
+ */
+ public boolean handleCallKey() {
+ LogUtil.v("InCallPresenter.handleCallKey", null);
+
+ // The green CALL button means either "Answer", "Unhold", or
+ // "Swap calls", or can be a no-op, depending on the current state
+ // of the Phone.
+
+ /** INCOMING CALL */
+ final CallList calls = mCallList;
+ final DialerCall incomingCall = calls.getIncomingCall();
+ LogUtil.v("InCallPresenter.handleCallKey", "incomingCall: " + incomingCall);
+
+ // (1) Attempt to answer a call
+ if (incomingCall != null) {
+ incomingCall.answer(VideoProfile.STATE_AUDIO_ONLY);
+ return true;
+ }
+
+ /** STATE_ACTIVE CALL */
+ final DialerCall activeCall = calls.getActiveCall();
+ if (activeCall != null) {
+ // TODO: This logic is repeated from CallButtonPresenter.java. We should
+ // consolidate this logic.
+ final boolean canMerge =
+ activeCall.can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
+ final boolean canSwap =
+ activeCall.can(android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
+
+ Log.v(
+ this, "activeCall: " + activeCall + ", canMerge: " + canMerge + ", canSwap: " + canSwap);
+
+ // (2) Attempt actions on conference calls
+ if (canMerge) {
+ TelecomAdapter.getInstance().merge(activeCall.getId());
+ return true;
+ } else if (canSwap) {
+ TelecomAdapter.getInstance().swap(activeCall.getId());
+ return true;
+ }
+ }
+
+ /** BACKGROUND CALL */
+ final DialerCall heldCall = calls.getBackgroundCall();
+ if (heldCall != null) {
+ // We have a hold call so presumeable it will always support HOLD...but
+ // there is no harm in double checking.
+ final boolean canHold = heldCall.can(android.telecom.Call.Details.CAPABILITY_HOLD);
+
+ Log.v(this, "heldCall: " + heldCall + ", canHold: " + canHold);
+
+ // (4) unhold call
+ if (heldCall.getState() == DialerCall.State.ONHOLD && canHold) {
+ heldCall.unhold();
+ return true;
+ }
+ }
+
+ // Always consume hard keys
+ return true;
+ }
+
+ /**
+ * A dialog could have prevented in-call screen from being previously finished. This function
+ * checks to see if there should be any UI left and if not attempts to tear down the UI.
+ */
+ public void onDismissDialog() {
+ Log.i(this, "Dialog dismissed");
+ if (mInCallState == InCallState.NO_CALLS) {
+ attemptFinishActivity();
+ attemptCleanup();
+ }
+ }
+
+ /** Clears the previous fullscreen state. */
+ public void clearFullscreen() {
+ mIsFullScreen = false;
+ }
+
+ /**
+ * Changes the fullscreen mode of the in-call UI.
+ *
+ * @param isFullScreen {@code true} if in-call should be in fullscreen mode, {@code false}
+ * otherwise.
+ */
+ public void setFullScreen(boolean isFullScreen) {
+ setFullScreen(isFullScreen, false /* force */);
+ }
+
+ /**
+ * Changes the fullscreen mode of the in-call UI.
+ *
+ * @param isFullScreen {@code true} if in-call should be in fullscreen mode, {@code false}
+ * otherwise.
+ * @param force {@code true} if fullscreen mode should be set regardless of its current state.
+ */
+ public void setFullScreen(boolean isFullScreen, boolean force) {
+ Log.i(this, "setFullScreen = " + isFullScreen);
+
+ // As a safeguard, ensure we cannot enter fullscreen if the dialpad is shown.
+ if (isDialpadVisible()) {
+ isFullScreen = false;
+ Log.v(this, "setFullScreen overridden as dialpad is shown = " + isFullScreen);
+ }
+
+ if (mIsFullScreen == isFullScreen && !force) {
+ Log.v(this, "setFullScreen ignored as already in that state.");
+ return;
+ }
+ mIsFullScreen = isFullScreen;
+ notifyFullscreenModeChange(mIsFullScreen);
+ }
+
+ /**
+ * @return {@code true} if the in-call ui is currently in fullscreen mode, {@code false}
+ * otherwise.
+ */
+ public boolean isFullscreen() {
+ return mIsFullScreen;
+ }
+
+ /**
+ * Called by the {@link VideoCallPresenter} to inform of a change in full screen video status.
+ *
+ * @param isFullscreenMode {@code True} if entering full screen mode.
+ */
+ public void notifyFullscreenModeChange(boolean isFullscreenMode) {
+ for (InCallEventListener listener : mInCallEventListeners) {
+ listener.onFullscreenModeChanged(isFullscreenMode);
+ }
+ }
+
+ /**
+ * For some disconnected causes, we show a dialog. This calls into the activity to show the dialog
+ * if appropriate for the call.
+ */
+ private void maybeShowErrorDialogOnDisconnect(DialerCall call) {
+ // For newly disconnected calls, we may want to show a dialog on specific error conditions
+ if (isActivityStarted() && call.getState() == DialerCall.State.DISCONNECTED) {
+ if (call.getAccountHandle() == null && !call.isConferenceCall()) {
+ setDisconnectCauseForMissingAccounts(call);
+ }
+ mInCallActivity.maybeShowErrorDialogOnDisconnect(call.getDisconnectCause());
+ }
+ }
+
+ /**
+ * When the state of in-call changes, this is the first method to get called. It determines if the
+ * UI needs to be started or finished depending on the new state and does it.
+ */
+ private InCallState startOrFinishUi(InCallState newState) {
+ Log.d(this, "startOrFinishUi: " + mInCallState + " -> " + newState);
+
+ // TODO: Consider a proper state machine implementation
+
+ // If the state isn't changing we have already done any starting/stopping of activities in
+ // a previous pass...so lets cut out early
+ if (newState == mInCallState) {
+ return newState;
+ }
+
+ // A new Incoming call means that the user needs to be notified of the the call (since
+ // it wasn't them who initiated it). We do this through full screen notifications and
+ // happens indirectly through {@link StatusBarNotifier}.
+ //
+ // The process for incoming calls is as follows:
+ //
+ // 1) CallList - Announces existence of new INCOMING call
+ // 2) InCallPresenter - Gets announcement and calculates that the new InCallState
+ // - should be set to INCOMING.
+ // 3) InCallPresenter - This method is called to see if we need to start or finish
+ // the app given the new state.
+ // 4) StatusBarNotifier - Listens to InCallState changes. InCallPresenter calls
+ // StatusBarNotifier explicitly to issue a FullScreen Notification
+ // that will either start the InCallActivity or show the user a
+ // top-level notification dialog if the user is in an immersive app.
+ // That notification can also start the InCallActivity.
+ // 5) InCallActivity - Main activity starts up and at the end of its onCreate will
+ // call InCallPresenter::setActivity() to let the presenter
+ // know that start-up is complete.
+ //
+ // [ AND NOW YOU'RE IN THE CALL. voila! ]
+ //
+ // Our app is started using a fullScreen notification. We need to do this whenever
+ // we get an incoming call. Depending on the current context of the device, either a
+ // incoming call HUN or the actual InCallActivity will be shown.
+ final boolean startIncomingCallSequence = (InCallState.INCOMING == newState);
+
+ // A dialog to show on top of the InCallUI to select a PhoneAccount
+ final boolean showAccountPicker = (InCallState.WAITING_FOR_ACCOUNT == newState);
+
+ // A new outgoing call indicates that the user just now dialed a number and when that
+ // happens we need to display the screen immediately or show an account picker dialog if
+ // no default is set. However, if the main InCallUI is already visible, we do not want to
+ // re-initiate the start-up animation, so we do not need to do anything here.
+ //
+ // It is also possible to go into an intermediate state where the call has been initiated
+ // but Telecom has not yet returned with the details of the call (handle, gateway, etc.).
+ // This pending outgoing state can also launch the call screen.
+ //
+ // This is different from the incoming call sequence because we do not need to shock the
+ // user with a top-level notification. Just show the call UI normally.
+ boolean callCardFragmentVisible =
+ mInCallActivity != null && mInCallActivity.getCallCardFragmentVisible();
+ final boolean mainUiNotVisible = !isShowingInCallUi() || !callCardFragmentVisible;
+ boolean showCallUi = InCallState.OUTGOING == newState && mainUiNotVisible;
+
+ // Direct transition from PENDING_OUTGOING -> INCALL means that there was an error in the
+ // outgoing call process, so the UI should be brought up to show an error dialog.
+ showCallUi |=
+ (InCallState.PENDING_OUTGOING == mInCallState
+ && InCallState.INCALL == newState
+ && !isShowingInCallUi());
+
+ // Another exception - InCallActivity is in charge of disconnecting a call with no
+ // valid accounts set. Bring the UI up if this is true for the current pending outgoing
+ // call so that:
+ // 1) The call can be disconnected correctly
+ // 2) The UI comes up and correctly displays the error dialog.
+ // TODO: Remove these special case conditions by making InCallPresenter a true state
+ // machine. Telecom should also be the component responsible for disconnecting a call
+ // with no valid accounts.
+ showCallUi |=
+ InCallState.PENDING_OUTGOING == newState
+ && mainUiNotVisible
+ && isCallWithNoValidAccounts(mCallList.getPendingOutgoingCall());
+
+ // The only time that we have an instance of mInCallActivity and it isn't started is
+ // when it is being destroyed. In that case, lets avoid bringing up another instance of
+ // the activity. When it is finally destroyed, we double check if we should bring it back
+ // up so we aren't going to lose anything by avoiding a second startup here.
+ boolean activityIsFinishing = mInCallActivity != null && !isActivityStarted();
+ if (activityIsFinishing) {
+ Log.i(this, "Undo the state change: " + newState + " -> " + mInCallState);
+ return mInCallState;
+ }
+
+ // We're about the bring up the in-call UI for outgoing and incoming call. If we still have
+ // dialogs up, we need to clear them out before showing in-call screen. This is necessary
+ // to fix the bug that dialog will show up when data reaches limit even after makeing new
+ // outgoing call after user ignore it by pressing home button.
+ if ((newState == InCallState.INCOMING || newState == InCallState.PENDING_OUTGOING)
+ && !showCallUi
+ && isActivityStarted()) {
+ mInCallActivity.dismissPendingDialogs();
+ }
+
+ if (showCallUi || showAccountPicker) {
+ Log.i(this, "Start in call UI");
+ showInCall(false /* showDialpad */, !showAccountPicker /* newOutgoingCall */, false);
+ } else if (startIncomingCallSequence) {
+ Log.i(this, "Start Full Screen in call UI");
+
+ if (!startUi()) {
+ // startUI refused to start the UI. This indicates that it needed to restart the
+ // activity. When it finally restarts, it will call us back, so we do not actually
+ // change the state yet (we return mInCallState instead of newState).
+ return mInCallState;
+ }
+ } else if (newState == InCallState.NO_CALLS) {
+ // The new state is the no calls state. Tear everything down.
+ attemptFinishActivity();
+ attemptCleanup();
+ }
+
+ return newState;
+ }
+
+ /**
+ * Sets the DisconnectCause for a call that was disconnected because it was missing a PhoneAccount
+ * or PhoneAccounts to select from.
+ */
+ private void setDisconnectCauseForMissingAccounts(DialerCall call) {
+
+ Bundle extras = call.getIntentExtras();
+ // Initialize the extras bundle to avoid NPE
+ if (extras == null) {
+ extras = new Bundle();
+ }
+
+ final List<PhoneAccountHandle> phoneAccountHandles =
+ extras.getParcelableArrayList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS);
+
+ if (phoneAccountHandles == null || phoneAccountHandles.isEmpty()) {
+ String scheme = call.getHandle().getScheme();
+ final String errorMsg =
+ PhoneAccount.SCHEME_TEL.equals(scheme)
+ ? mContext.getString(R.string.callFailed_simError)
+ : mContext.getString(R.string.incall_error_supp_service_unknown);
+ DisconnectCause disconnectCause =
+ new DisconnectCause(DisconnectCause.ERROR, null, errorMsg, errorMsg);
+ call.setDisconnectCause(disconnectCause);
+ }
+ }
+
+ private boolean startUi() {
+ boolean isCallWaiting =
+ mCallList.getActiveCall() != null && mCallList.getIncomingCall() != null;
+
+ if (isCallWaiting) {
+ showInCall(false, false, false /* isVideoCall */);
+ } else {
+ mStatusBarNotifier.updateNotification(mCallList);
+ }
+ return true;
+ }
+
+ /**
+ * @return {@code true} if the InCallPresenter is ready to be torn down, {@code false} otherwise.
+ * Calling classes should use this as an indication whether to interact with the
+ * InCallPresenter or not.
+ */
+ public boolean isReadyForTearDown() {
+ return mInCallActivity == null && !mServiceConnected && mInCallState == InCallState.NO_CALLS;
+ }
+
+ /**
+ * Checks to see if both the UI is gone and the service is disconnected. If so, tear it all down.
+ */
+ private void attemptCleanup() {
+ if (isReadyForTearDown()) {
+ Log.i(this, "Cleaning up");
+
+ cleanupSurfaces();
+
+ mIsActivityPreviouslyStarted = false;
+ mIsChangingConfigurations = false;
+
+ // blow away stale contact info so that we get fresh data on
+ // the next set of calls
+ if (mContactInfoCache != null) {
+ mContactInfoCache.clearCache();
+ }
+ mContactInfoCache = null;
+
+ if (mProximitySensor != null) {
+ removeListener(mProximitySensor);
+ mProximitySensor.tearDown();
+ }
+ mProximitySensor = null;
+
+ if (mStatusBarNotifier != null) {
+ removeListener(mStatusBarNotifier);
+ }
+ if (mExternalCallNotifier != null && mExternalCallList != null) {
+ mExternalCallList.removeExternalCallListener(mExternalCallNotifier);
+ }
+ mStatusBarNotifier = null;
+
+ if (mCallList != null) {
+ mCallList.removeListener(this);
+ mCallList.removeListener(mSpamCallListListener);
+ }
+ mCallList = null;
+
+ mContext = null;
+ mInCallActivity = null;
+ mManageConferenceActivity = null;
+
+ mListeners.clear();
+ mIncomingCallListeners.clear();
+ mDetailsListeners.clear();
+ mCanAddCallListeners.clear();
+ mOrientationListeners.clear();
+ mInCallEventListeners.clear();
+ mInCallUiListeners.clear();
+
+ Log.d(this, "Finished InCallPresenter.CleanUp");
+ }
+ }
+
+ public void showInCall(boolean showDialpad, boolean newOutgoingCall, boolean isVideoCall) {
+ Log.i(this, "Showing InCallActivity");
+ mContext.startActivity(
+ InCallActivity.getIntent(
+ mContext, showDialpad, newOutgoingCall, isVideoCall, false /* forFullScreen */));
+ }
+
+ public void onServiceBind() {
+ mServiceBound = true;
+ }
+
+ public void onServiceUnbind() {
+ InCallPresenter.getInstance().setBoundAndWaitingForOutgoingCall(false, null);
+ mServiceBound = false;
+ }
+
+ public boolean isServiceBound() {
+ return mServiceBound;
+ }
+
+ public void maybeStartRevealAnimation(Intent intent) {
+ if (intent == null || mInCallActivity != null) {
+ return;
+ }
+ final Bundle extras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
+ if (extras == null) {
+ // Incoming call, just show the in-call UI directly.
+ return;
+ }
+
+ if (extras.containsKey(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS)) {
+ // Account selection dialog will show up so don't show the animation.
+ return;
+ }
+
+ final PhoneAccountHandle accountHandle =
+ intent.getParcelableExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
+ final Point touchPoint = extras.getParcelable(TouchPointManager.TOUCH_POINT);
+ int videoState =
+ extras.getInt(
+ TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, VideoProfile.STATE_AUDIO_ONLY);
+
+ InCallPresenter.getInstance().setBoundAndWaitingForOutgoingCall(true, accountHandle);
+
+ final Intent activityIntent =
+ InCallActivity.getIntent(
+ mContext, false, true, VideoUtils.isVideoCall(videoState), false /* forFullScreen */);
+ activityIntent.putExtra(TouchPointManager.TOUCH_POINT, touchPoint);
+ mContext.startActivity(activityIntent);
+ }
+
+ /**
+ * Retrieves the current in-call camera manager instance, creating if necessary.
+ *
+ * @return The {@link InCallCameraManager}.
+ */
+ public InCallCameraManager getInCallCameraManager() {
+ synchronized (this) {
+ if (mInCallCameraManager == null) {
+ mInCallCameraManager = new InCallCameraManager(mContext);
+ }
+
+ return mInCallCameraManager;
+ }
+ }
+
+ /**
+ * Notifies listeners of changes in orientation and notify calls of rotation angle change.
+ *
+ * @param orientation The screen orientation of the device (one of: {@link
+ * InCallOrientationEventListener#SCREEN_ORIENTATION_0}, {@link
+ * InCallOrientationEventListener#SCREEN_ORIENTATION_90}, {@link
+ * InCallOrientationEventListener#SCREEN_ORIENTATION_180}, {@link
+ * InCallOrientationEventListener#SCREEN_ORIENTATION_270}).
+ */
+ public void onDeviceOrientationChange(@ScreenOrientation int orientation) {
+ Log.d(this, "onDeviceOrientationChange: orientation= " + orientation);
+
+ if (mCallList != null) {
+ mCallList.notifyCallsOfDeviceRotation(orientation);
+ } else {
+ Log.w(this, "onDeviceOrientationChange: CallList is null.");
+ }
+
+ // Notify listeners of device orientation changed.
+ for (InCallOrientationListener listener : mOrientationListeners) {
+ listener.onDeviceOrientationChanged(orientation);
+ }
+ }
+
+ /**
+ * Configures the in-call UI activity so it can change orientations or not. Enables the
+ * orientation event listener if allowOrientationChange is true, disables it if false.
+ *
+ * @param allowOrientationChange {@code true} if the in-call UI can change between portrait and
+ * landscape. {@code false} if the in-call UI should be locked in portrait.
+ */
+ public void setInCallAllowsOrientationChange(boolean allowOrientationChange) {
+ if (mInCallActivity == null) {
+ Log.e(this, "InCallActivity is null. Can't set requested orientation.");
+ return;
+ }
+ mInCallActivity.setAllowOrientationChange(allowOrientationChange);
+ }
+
+ public void enableScreenTimeout(boolean enable) {
+ Log.v(this, "enableScreenTimeout: value=" + enable);
+ if (mInCallActivity == null) {
+ Log.e(this, "enableScreenTimeout: InCallActivity is null.");
+ return;
+ }
+
+ final Window window = mInCallActivity.getWindow();
+ if (enable) {
+ window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ } else {
+ window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+ }
+
+ /**
+ * Hides or shows the conference manager fragment.
+ *
+ * @param show {@code true} if the conference manager should be shown, {@code false} if it should
+ * be hidden.
+ */
+ public void showConferenceCallManager(boolean show) {
+ if (mInCallActivity != null) {
+ mInCallActivity.showConferenceFragment(show);
+ }
+ if (!show && mManageConferenceActivity != null) {
+ mManageConferenceActivity.finish();
+ }
+ }
+
+ /**
+ * Determines if the dialpad is visible.
+ *
+ * @return {@code true} if the dialpad is visible, {@code false} otherwise.
+ */
+ public boolean isDialpadVisible() {
+ if (mInCallActivity == null) {
+ return false;
+ }
+ return mInCallActivity.isDialpadVisible();
+ }
+
+ public ThemeColorManager getThemeColorManager() {
+ return mThemeColorManager;
+ }
+
+ /** Called when the foreground call changes. */
+ public void onForegroundCallChanged(DialerCall newForegroundCall) {
+ mThemeColorManager.onForegroundCallChanged(mContext, newForegroundCall);
+ if (mInCallActivity != null) {
+ mInCallActivity.onForegroundCallChanged(newForegroundCall);
+ }
+ }
+
+ public InCallActivity getActivity() {
+ return mInCallActivity;
+ }
+
+ /** Called when the UI begins, and starts the callstate callbacks if necessary. */
+ public void setActivity(InCallActivity inCallActivity) {
+ if (inCallActivity == null) {
+ throw new IllegalArgumentException("registerActivity cannot be called with null");
+ }
+ if (mInCallActivity != null && mInCallActivity != inCallActivity) {
+ Log.w(this, "Setting a second activity before destroying the first.");
+ }
+ updateActivity(inCallActivity);
+ }
+
+ ExternalCallNotifier getExternalCallNotifier() {
+ return mExternalCallNotifier;
+ }
+
+ VideoSurfaceTexture getLocalVideoSurfaceTexture() {
+ if (mLocalVideoSurfaceTexture == null) {
+ mLocalVideoSurfaceTexture = VideoSurfaceBindings.createLocalVideoSurfaceTexture();
+ }
+ return mLocalVideoSurfaceTexture;
+ }
+
+ VideoSurfaceTexture getRemoteVideoSurfaceTexture() {
+ if (mRemoteVideoSurfaceTexture == null) {
+ mRemoteVideoSurfaceTexture = VideoSurfaceBindings.createRemoteVideoSurfaceTexture();
+ }
+ return mRemoteVideoSurfaceTexture;
+ }
+
+ void cleanupSurfaces() {
+ if (mRemoteVideoSurfaceTexture != null) {
+ mRemoteVideoSurfaceTexture.setDoneWithSurface();
+ mRemoteVideoSurfaceTexture = null;
+ }
+ if (mLocalVideoSurfaceTexture != null) {
+ mLocalVideoSurfaceTexture.setDoneWithSurface();
+ mLocalVideoSurfaceTexture = null;
+ }
+ }
+
+ /** All the main states of InCallActivity. */
+ public enum InCallState {
+ // InCall Screen is off and there are no calls
+ NO_CALLS,
+
+ // Incoming-call screen is up
+ INCOMING,
+
+ // In-call experience is showing
+ INCALL,
+
+ // Waiting for user input before placing outgoing call
+ WAITING_FOR_ACCOUNT,
+
+ // UI is starting up but no call has been initiated yet.
+ // The UI is waiting for Telecom to respond.
+ PENDING_OUTGOING,
+
+ // User is dialing out
+ OUTGOING;
+
+ public boolean isIncoming() {
+ return (this == INCOMING);
+ }
+
+ public boolean isConnectingOrConnected() {
+ return (this == INCOMING || this == OUTGOING || this == INCALL);
+ }
+ }
+
+ /** Interface implemented by classes that need to know about the InCall State. */
+ public interface InCallStateListener {
+
+ // TODO: Enhance state to contain the call objects instead of passing CallList
+ void onStateChange(InCallState oldState, InCallState newState, CallList callList);
+ }
+
+ public interface IncomingCallListener {
+
+ void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call);
+ }
+
+ public interface CanAddCallListener {
+
+ void onCanAddCallChanged(boolean canAddCall);
+ }
+
+ public interface InCallDetailsListener {
+
+ void onDetailsChanged(DialerCall call, android.telecom.Call.Details details);
+ }
+
+ public interface InCallOrientationListener {
+
+ void onDeviceOrientationChanged(@ScreenOrientation int orientation);
+ }
+
+ /**
+ * Interface implemented by classes that need to know about events which occur within the In-Call
+ * UI. Used as a means of communicating between fragments that make up the UI.
+ */
+ public interface InCallEventListener {
+
+ void onFullscreenModeChanged(boolean isFullscreenMode);
+ }
+
+ public interface InCallUiListener {
+
+ void onUiShowing(boolean showing);
+ }
+}
diff --git a/java/com/android/incallui/InCallServiceImpl.java b/java/com/android/incallui/InCallServiceImpl.java
new file mode 100644
index 000000000..33e8393ae
--- /dev/null
+++ b/java/com/android/incallui/InCallServiceImpl.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 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.incallui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.telecom.Call;
+import android.telecom.CallAudioState;
+import android.telecom.InCallService;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.ExternalCallList;
+import com.android.incallui.call.TelecomAdapter;
+
+/**
+ * Used to receive updates about calls from the Telecom component. This service is bound to Telecom
+ * while there exist calls which potentially require UI. This includes ringing (incoming), dialing
+ * (outgoing), and active calls. When the last call is disconnected, Telecom will unbind to the
+ * service triggering InCallActivity (via CallList) to finish soon after.
+ */
+public class InCallServiceImpl extends InCallService {
+
+ @Override
+ public void onCallAudioStateChanged(CallAudioState audioState) {
+ AudioModeProvider.getInstance().onAudioStateChanged(audioState);
+ }
+
+ @Override
+ public void onBringToForeground(boolean showDialpad) {
+ InCallPresenter.getInstance().onBringToForeground(showDialpad);
+ }
+
+ @Override
+ public void onCallAdded(Call call) {
+ InCallPresenter.getInstance().onCallAdded(call);
+ }
+
+ @Override
+ public void onCallRemoved(Call call) {
+ InCallPresenter.getInstance().onCallRemoved(call);
+ }
+
+ @Override
+ public void onCanAddCallChanged(boolean canAddCall) {
+ InCallPresenter.getInstance().onCanAddCallChanged(canAddCall);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ final Context context = getApplicationContext();
+ final ContactInfoCache contactInfoCache = ContactInfoCache.getInstance(context);
+ InCallPresenter.getInstance()
+ .setUp(
+ getApplicationContext(),
+ CallList.getInstance(),
+ new ExternalCallList(),
+ new StatusBarNotifier(context, contactInfoCache),
+ new ExternalCallNotifier(context, contactInfoCache),
+ contactInfoCache,
+ new ProximitySensor(
+ context, AudioModeProvider.getInstance(), new AccelerometerListener(context)));
+ InCallPresenter.getInstance().onServiceBind();
+ InCallPresenter.getInstance().maybeStartRevealAnimation(intent);
+ TelecomAdapter.getInstance().setInCallService(this);
+
+ return super.onBind(intent);
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ super.onUnbind(intent);
+
+ InCallPresenter.getInstance().onServiceUnbind();
+ tearDown();
+
+ return false;
+ }
+
+ private void tearDown() {
+ Log.v(this, "tearDown");
+ // Tear down the InCall system
+ TelecomAdapter.getInstance().clearInCallService();
+ InCallPresenter.getInstance().tearDown();
+ }
+}
diff --git a/java/com/android/incallui/InCallUIMaterialColorMapUtils.java b/java/com/android/incallui/InCallUIMaterialColorMapUtils.java
new file mode 100644
index 000000000..7b06a5e39
--- /dev/null
+++ b/java/com/android/incallui/InCallUIMaterialColorMapUtils.java
@@ -0,0 +1,67 @@
+/*
+ * 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.incallui;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.telecom.PhoneAccount;
+import com.android.contacts.common.util.MaterialColorMapUtils;
+
+public class InCallUIMaterialColorMapUtils extends MaterialColorMapUtils {
+
+ private final TypedArray mPrimaryColors;
+ private final TypedArray mSecondaryColors;
+ private final Resources mResources;
+
+ public InCallUIMaterialColorMapUtils(Resources resources) {
+ super(resources);
+ mPrimaryColors = resources.obtainTypedArray(R.array.background_colors);
+ mSecondaryColors = resources.obtainTypedArray(R.array.background_colors_dark);
+ mResources = resources;
+ }
+
+ /**
+ * {@link Resources#getColor(int) used for compatibility
+ */
+ @SuppressWarnings("deprecation")
+ public static MaterialPalette getDefaultPrimaryAndSecondaryColors(Resources resources) {
+ final int primaryColor = resources.getColor(R.color.dialer_theme_color);
+ final int secondaryColor = resources.getColor(R.color.dialer_theme_color_dark);
+ return new MaterialPalette(primaryColor, secondaryColor);
+ }
+
+ /**
+ * Currently the InCallUI color will only vary by SIM color which is a list of colors defined in
+ * the background_colors array, so first search the list for the matching color and fall back to
+ * the closest matching color if an exact match does not exist.
+ */
+ @Override
+ public MaterialPalette calculatePrimaryAndSecondaryColor(int color) {
+ if (color == PhoneAccount.NO_HIGHLIGHT_COLOR) {
+ return getDefaultPrimaryAndSecondaryColors(mResources);
+ }
+
+ for (int i = 0; i < mPrimaryColors.length(); i++) {
+ if (mPrimaryColors.getColor(i, 0) == color) {
+ return new MaterialPalette(mPrimaryColors.getColor(i, 0), mSecondaryColors.getColor(i, 0));
+ }
+ }
+
+ // The color isn't in the list, so use the superclass to find an approximate color.
+ return super.calculatePrimaryAndSecondaryColor(color);
+ }
+}
diff --git a/java/com/android/incallui/Log.java b/java/com/android/incallui/Log.java
new file mode 100644
index 000000000..c63eccbd4
--- /dev/null
+++ b/java/com/android/incallui/Log.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2013 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.incallui;
+
+import android.net.Uri;
+import android.telecom.PhoneAccount;
+import android.telephony.PhoneNumberUtils;
+import com.android.dialer.common.LogUtil;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/** Manages logging for the entire class. */
+public class Log {
+
+ public static void d(String tag, String msg) {
+ LogUtil.d(tag, msg);
+ }
+
+ public static void d(Object obj, String msg) {
+ LogUtil.d(getPrefix(obj), msg);
+ }
+
+ public static void d(Object obj, String str1, Object str2) {
+ LogUtil.d(getPrefix(obj), str1 + str2);
+ }
+
+ public static void v(Object obj, String msg) {
+ LogUtil.v(getPrefix(obj), msg);
+ }
+
+ public static void v(Object obj, String str1, Object str2) {
+ LogUtil.v(getPrefix(obj), str1 + str2);
+ }
+
+ public static void e(String tag, String msg, Exception e) {
+ LogUtil.e(tag, msg, e);
+ }
+
+ public static void e(String tag, String msg) {
+ LogUtil.e(tag, msg);
+ }
+
+ public static void e(Object obj, String msg, Exception e) {
+ LogUtil.e(getPrefix(obj), msg, e);
+ }
+
+ public static void e(Object obj, String msg) {
+ LogUtil.e(getPrefix(obj), msg);
+ }
+
+ public static void i(String tag, String msg) {
+ LogUtil.i(tag, msg);
+ }
+
+ public static void i(Object obj, String msg) {
+ LogUtil.i(getPrefix(obj), msg);
+ }
+
+ public static void w(Object obj, String msg) {
+ LogUtil.w(getPrefix(obj), msg);
+ }
+
+ public static String piiHandle(Object pii) {
+ if (pii == null || LogUtil.isVerboseEnabled()) {
+ return String.valueOf(pii);
+ }
+
+ if (pii instanceof Uri) {
+ Uri uri = (Uri) pii;
+
+ // All Uri's which are not "tel" go through normal pii() method.
+ if (!PhoneAccount.SCHEME_TEL.equals(uri.getScheme())) {
+ return pii(pii);
+ } else {
+ pii = uri.getSchemeSpecificPart();
+ }
+ }
+
+ String originalString = String.valueOf(pii);
+ StringBuilder stringBuilder = new StringBuilder(originalString.length());
+ for (char c : originalString.toCharArray()) {
+ if (PhoneNumberUtils.isDialable(c)) {
+ stringBuilder.append('*');
+ } else {
+ stringBuilder.append(c);
+ }
+ }
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Redact personally identifiable information for production users. If we are running in verbose
+ * mode, return the original string, otherwise return a SHA-1 hash of the input string.
+ */
+ public static String pii(Object pii) {
+ if (pii == null || LogUtil.isVerboseEnabled()) {
+ return String.valueOf(pii);
+ }
+ return "[" + secureHash(String.valueOf(pii).getBytes()) + "]";
+ }
+
+ private static String secureHash(byte[] input) {
+ MessageDigest messageDigest;
+ try {
+ messageDigest = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ }
+ messageDigest.update(input);
+ byte[] result = messageDigest.digest();
+ return encodeHex(result);
+ }
+
+ private static String encodeHex(byte[] bytes) {
+ StringBuffer hex = new StringBuffer(bytes.length * 2);
+
+ for (int i = 0; i < bytes.length; i++) {
+ int byteIntValue = bytes[i] & 0xff;
+ if (byteIntValue < 0x10) {
+ hex.append("0");
+ }
+ hex.append(Integer.toString(byteIntValue, 16));
+ }
+
+ return hex.toString();
+ }
+
+ private static String getPrefix(Object obj) {
+ return (obj == null ? "" : (obj.getClass().getSimpleName()));
+ }
+}
diff --git a/java/com/android/incallui/ManageConferenceActivity.java b/java/com/android/incallui/ManageConferenceActivity.java
new file mode 100644
index 000000000..6584e4f67
--- /dev/null
+++ b/java/com/android/incallui/ManageConferenceActivity.java
@@ -0,0 +1,86 @@
+/*
+ * 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.incallui;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.AppCompatActivity;
+import android.view.MenuItem;
+
+/** Shows the {@link ConferenceManagerFragment} */
+public class ManageConferenceActivity extends AppCompatActivity {
+
+ private boolean isVisible;
+
+ public boolean isVisible() {
+ return isVisible;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ InCallPresenter.getInstance().setManageConferenceActivity(this);
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ setContentView(R.layout.activity_manage_conference);
+ Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.manageConferencePanel);
+ if (fragment == null) {
+ fragment = new ConferenceManagerFragment();
+ getSupportFragmentManager()
+ .beginTransaction()
+ .replace(R.id.manageConferencePanel, fragment)
+ .commit();
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (isFinishing()) {
+ InCallPresenter.getInstance().setManageConferenceActivity(null);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final int itemId = item.getItemId();
+ if (itemId == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onBackPressed() {
+ InCallPresenter.getInstance().bringToForeground(false);
+ finish();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ isVisible = true;
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ isVisible = false;
+ }
+}
diff --git a/java/com/android/incallui/NotificationBroadcastReceiver.java b/java/com/android/incallui/NotificationBroadcastReceiver.java
new file mode 100644
index 000000000..5c5d255cc
--- /dev/null
+++ b/java/com/android/incallui/NotificationBroadcastReceiver.java
@@ -0,0 +1,165 @@
+/*
+ * 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.incallui;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.RequiresApi;
+import android.telecom.VideoProfile;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.VideoUtils;
+
+/**
+ * Accepts broadcast Intents which will be prepared by {@link StatusBarNotifier} and thus sent from
+ * the notification manager. This should be visible from outside, but shouldn't be exported.
+ */
+public class NotificationBroadcastReceiver extends BroadcastReceiver {
+
+ /**
+ * Intent Action used for hanging up the current call from Notification bar. This will choose
+ * first ringing call, first active call, or first background call (typically in STATE_HOLDING
+ * state).
+ */
+ public static final String ACTION_DECLINE_INCOMING_CALL =
+ "com.android.incallui.ACTION_DECLINE_INCOMING_CALL";
+
+ public static final String ACTION_HANG_UP_ONGOING_CALL =
+ "com.android.incallui.ACTION_HANG_UP_ONGOING_CALL";
+ public static final String ACTION_ANSWER_VIDEO_INCOMING_CALL =
+ "com.android.incallui.ACTION_ANSWER_VIDEO_INCOMING_CALL";
+ public static final String ACTION_ANSWER_VOICE_INCOMING_CALL =
+ "com.android.incallui.ACTION_ANSWER_VOICE_INCOMING_CALL";
+ public static final String ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST =
+ "com.android.incallui.ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST";
+ public static final String ACTION_DECLINE_VIDEO_UPGRADE_REQUEST =
+ "com.android.incallui.ACTION_DECLINE_VIDEO_UPGRADE_REQUEST";
+
+ @RequiresApi(VERSION_CODES.N_MR1)
+ public static final String ACTION_PULL_EXTERNAL_CALL =
+ "com.android.incallui.ACTION_PULL_EXTERNAL_CALL";
+
+ public static final String EXTRA_NOTIFICATION_ID =
+ "com.android.incallui.extra.EXTRA_NOTIFICATION_ID";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ LogUtil.i("NotificationBroadcastReceiver.onReceive", "Broadcast from Notification: " + action);
+
+ // TODO: Commands of this nature should exist in the CallList.
+ if (action.equals(ACTION_ANSWER_VIDEO_INCOMING_CALL)) {
+ answerIncomingCall(context, VideoProfile.STATE_BIDIRECTIONAL);
+ } else if (action.equals(ACTION_ANSWER_VOICE_INCOMING_CALL)) {
+ answerIncomingCall(context, VideoProfile.STATE_AUDIO_ONLY);
+ } else if (action.equals(ACTION_DECLINE_INCOMING_CALL)) {
+ Logger.get(context)
+ .logImpression(DialerImpression.Type.REJECT_INCOMING_CALL_FROM_NOTIFICATION);
+ declineIncomingCall(context);
+ } else if (action.equals(ACTION_HANG_UP_ONGOING_CALL)) {
+ hangUpOngoingCall(context);
+ } else if (action.equals(ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST)) {
+ acceptUpgradeRequest(context);
+ } else if (action.equals(ACTION_DECLINE_VIDEO_UPGRADE_REQUEST)) {
+ declineUpgradeRequest(context);
+ } else if (action.equals(ACTION_PULL_EXTERNAL_CALL)) {
+ context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
+ InCallPresenter.getInstance().getExternalCallNotifier().pullExternalCall(notificationId);
+ }
+ }
+
+ private void acceptUpgradeRequest(Context context) {
+ CallList callList = InCallPresenter.getInstance().getCallList();
+ if (callList == null) {
+ StatusBarNotifier.clearAllCallNotifications(context);
+ LogUtil.e("NotificationBroadcastReceiver.acceptUpgradeRequest", "call list is empty");
+ } else {
+ DialerCall call = callList.getVideoUpgradeRequestCall();
+ if (call != null) {
+ call.acceptUpgradeRequest(call.getRequestedVideoState());
+ }
+ }
+ }
+
+ private void declineUpgradeRequest(Context context) {
+ CallList callList = InCallPresenter.getInstance().getCallList();
+ if (callList == null) {
+ StatusBarNotifier.clearAllCallNotifications(context);
+ LogUtil.e("NotificationBroadcastReceiver.declineUpgradeRequest", "call list is empty");
+ } else {
+ DialerCall call = callList.getVideoUpgradeRequestCall();
+ if (call != null) {
+ call.declineUpgradeRequest();
+ }
+ }
+ }
+
+ private void hangUpOngoingCall(Context context) {
+ CallList callList = InCallPresenter.getInstance().getCallList();
+ if (callList == null) {
+ StatusBarNotifier.clearAllCallNotifications(context);
+ LogUtil.e("NotificationBroadcastReceiver.hangUpOngoingCall", "call list is empty");
+ } else {
+ DialerCall call = callList.getOutgoingCall();
+ if (call == null) {
+ call = callList.getActiveOrBackgroundCall();
+ }
+ LogUtil.i(
+ "NotificationBroadcastReceiver.hangUpOngoingCall", "disconnecting call, call: " + call);
+ if (call != null) {
+ call.disconnect();
+ }
+ }
+ }
+
+ private void answerIncomingCall(Context context, int videoState) {
+ CallList callList = InCallPresenter.getInstance().getCallList();
+ if (callList == null) {
+ StatusBarNotifier.clearAllCallNotifications(context);
+ LogUtil.e("NotificationBroadcastReceiver.answerIncomingCall", "call list is empty");
+ } else {
+ DialerCall call = callList.getIncomingCall();
+ if (call != null) {
+ call.answer(videoState);
+ InCallPresenter.getInstance()
+ .showInCall(
+ false /* showDialpad */,
+ false /* newOutgoingCall */,
+ VideoUtils.isVideoCall(videoState));
+ }
+ }
+ }
+
+ private void declineIncomingCall(Context context) {
+ CallList callList = InCallPresenter.getInstance().getCallList();
+ if (callList == null) {
+ StatusBarNotifier.clearAllCallNotifications(context);
+ LogUtil.e("NotificationBroadcastReceiver.declineIncomingCall", "call list is empty");
+ } else {
+ DialerCall call = callList.getIncomingCall();
+ if (call != null) {
+ call.reject(false /* rejectWithMessage */, null);
+ }
+ }
+ }
+}
diff --git a/java/com/android/incallui/PostCharDialogFragment.java b/java/com/android/incallui/PostCharDialogFragment.java
new file mode 100644
index 000000000..a852f7683
--- /dev/null
+++ b/java/com/android/incallui/PostCharDialogFragment.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2013 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.incallui;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import com.android.incallui.call.TelecomAdapter;
+
+/**
+ * Pop up an alert dialog with OK and Cancel buttons to allow user to Accept or Reject the WAIT
+ * inserted as part of the Dial string.
+ */
+public class PostCharDialogFragment extends DialogFragment {
+
+ private static final String STATE_CALL_ID = "CALL_ID";
+ private static final String STATE_POST_CHARS = "POST_CHARS";
+
+ private String mCallId;
+ private String mPostDialStr;
+
+ public PostCharDialogFragment() {}
+
+ public PostCharDialogFragment(String callId, String postDialStr) {
+ mCallId = callId;
+ mPostDialStr = postDialStr;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+
+ if (mPostDialStr == null && savedInstanceState != null) {
+ mCallId = savedInstanceState.getString(STATE_CALL_ID);
+ mPostDialStr = savedInstanceState.getString(STATE_POST_CHARS);
+ }
+
+ final StringBuilder buf = new StringBuilder();
+ buf.append(getResources().getText(R.string.wait_prompt_str));
+ buf.append(mPostDialStr);
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setMessage(buf.toString());
+
+ builder.setPositiveButton(
+ R.string.pause_prompt_yes,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ TelecomAdapter.getInstance().postDialContinue(mCallId, true);
+ }
+ });
+ builder.setNegativeButton(
+ R.string.pause_prompt_no,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ dialog.cancel();
+ }
+ });
+
+ final AlertDialog dialog = builder.create();
+ return dialog;
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+
+ TelecomAdapter.getInstance().postDialContinue(mCallId, false);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putString(STATE_CALL_ID, mCallId);
+ outState.putString(STATE_POST_CHARS, mPostDialStr);
+ }
+}
diff --git a/java/com/android/incallui/ProximitySensor.java b/java/com/android/incallui/ProximitySensor.java
new file mode 100644
index 000000000..91220627c
--- /dev/null
+++ b/java/com/android/incallui/ProximitySensor.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2013 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.incallui;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.PowerManager;
+import android.support.annotation.NonNull;
+import android.telecom.CallAudioState;
+import android.view.Display;
+import com.android.dialer.common.LogUtil;
+import com.android.incallui.AudioModeProvider.AudioModeListener;
+import com.android.incallui.InCallPresenter.InCallState;
+import com.android.incallui.InCallPresenter.InCallStateListener;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.VideoUtils;
+
+/**
+ * Class manages the proximity sensor for the in-call UI. We enable the proximity sensor while the
+ * user in a phone call. The Proximity sensor turns off the touchscreen and display when the user is
+ * close to the screen to prevent user's cheek from causing touch events. The class requires special
+ * knowledge of the activity and device state to know when the proximity sensor should be enabled
+ * and disabled. Most of that state is fed into this class through public methods.
+ */
+public class ProximitySensor
+ implements AccelerometerListener.OrientationListener, InCallStateListener, AudioModeListener {
+
+ private static final String TAG = ProximitySensor.class.getSimpleName();
+
+ private final PowerManager mPowerManager;
+ private final PowerManager.WakeLock mProximityWakeLock;
+ private final AudioModeProvider mAudioModeProvider;
+ private final AccelerometerListener mAccelerometerListener;
+ private final ProximityDisplayListener mDisplayListener;
+ private int mOrientation = AccelerometerListener.ORIENTATION_UNKNOWN;
+ private boolean mUiShowing = false;
+ private boolean mIsPhoneOffhook = false;
+ private boolean mDialpadVisible;
+ private boolean mIsAttemptingVideoCall;
+ private boolean mIsVideoCall;
+
+ public ProximitySensor(
+ @NonNull Context context,
+ @NonNull AudioModeProvider audioModeProvider,
+ @NonNull AccelerometerListener accelerometerListener) {
+ mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ if (mPowerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
+ mProximityWakeLock =
+ mPowerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
+ } else {
+ LogUtil.i("ProximitySensor.constructor", "Device does not support proximity wake lock.");
+ mProximityWakeLock = null;
+ }
+ mAccelerometerListener = accelerometerListener;
+ mAccelerometerListener.setListener(this);
+
+ mDisplayListener =
+ new ProximityDisplayListener(
+ (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE));
+ mDisplayListener.register();
+
+ mAudioModeProvider = audioModeProvider;
+ mAudioModeProvider.addListener(this);
+ }
+
+ public void tearDown() {
+ mAudioModeProvider.removeListener(this);
+
+ mAccelerometerListener.enable(false);
+ mDisplayListener.unregister();
+
+ turnOffProximitySensor(true);
+ }
+
+ /** Called to identify when the device is laid down flat. */
+ @Override
+ public void orientationChanged(int orientation) {
+ mOrientation = orientation;
+ updateProximitySensorMode();
+ }
+
+ /** Called to keep track of the overall UI state. */
+ @Override
+ public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
+ // We ignore incoming state because we do not want to enable proximity
+ // sensor during incoming call screen. We check hasLiveCall() because a disconnected call
+ // can also put the in-call screen in the INCALL state.
+ boolean hasOngoingCall = InCallState.INCALL == newState && callList.hasLiveCall();
+ boolean isOffhook = (InCallState.OUTGOING == newState) || hasOngoingCall;
+
+ boolean isVideoCall = VideoUtils.isVideoCall(callList.getActiveCall());
+
+ if (isOffhook != mIsPhoneOffhook || mIsVideoCall != isVideoCall) {
+ mIsPhoneOffhook = isOffhook;
+ mIsVideoCall = isVideoCall;
+
+ mOrientation = AccelerometerListener.ORIENTATION_UNKNOWN;
+ mAccelerometerListener.enable(mIsPhoneOffhook);
+
+ updateProximitySensorMode();
+ }
+ }
+
+ @Override
+ public void onAudioStateChanged(CallAudioState audioState) {
+ updateProximitySensorMode();
+ }
+
+ public void onDialpadVisible(boolean visible) {
+ mDialpadVisible = visible;
+ updateProximitySensorMode();
+ }
+
+ public void setIsAttemptingVideoCall(boolean isAttemptingVideoCall) {
+ LogUtil.i(
+ "ProximitySensor.setIsAttemptingVideoCall",
+ "isAttemptingVideoCall: %b",
+ isAttemptingVideoCall);
+ mIsAttemptingVideoCall = isAttemptingVideoCall;
+ updateProximitySensorMode();
+ }
+ /** Used to save when the UI goes in and out of the foreground. */
+ public void onInCallShowing(boolean showing) {
+ if (showing) {
+ mUiShowing = true;
+
+ // We only consider the UI not showing for instances where another app took the foreground.
+ // If we stopped showing because the screen is off, we still consider that showing.
+ } else if (mPowerManager.isScreenOn()) {
+ mUiShowing = false;
+ }
+ updateProximitySensorMode();
+ }
+
+ void onDisplayStateChanged(boolean isDisplayOn) {
+ LogUtil.i("ProximitySensor.onDisplayStateChanged", "isDisplayOn: %b", isDisplayOn);
+ mAccelerometerListener.enable(isDisplayOn);
+ }
+
+ /**
+ * TODO: There is no way to determine if a screen is off due to proximity or if it is legitimately
+ * off, but if ever we can do that in the future, it would be useful here. Until then, this
+ * function will simply return true of the screen is off. TODO: Investigate whether this can be
+ * replaced with the ProximityDisplayListener.
+ */
+ public boolean isScreenReallyOff() {
+ return !mPowerManager.isScreenOn();
+ }
+
+ private void turnOnProximitySensor() {
+ if (mProximityWakeLock != null) {
+ if (!mProximityWakeLock.isHeld()) {
+ LogUtil.i("ProximitySensor.turnOnProximitySensor", "acquiring wake lock");
+ mProximityWakeLock.acquire();
+ } else {
+ LogUtil.i("ProximitySensor.turnOnProximitySensor", "wake lock already acquired");
+ }
+ }
+ }
+
+ private void turnOffProximitySensor(boolean screenOnImmediately) {
+ if (mProximityWakeLock != null) {
+ if (mProximityWakeLock.isHeld()) {
+ LogUtil.i("ProximitySensor.turnOffProximitySensor", "releasing wake lock");
+ int flags = (screenOnImmediately ? 0 : PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY);
+ mProximityWakeLock.release(flags);
+ } else {
+ LogUtil.i("ProximitySensor.turnOffProximitySensor", "wake lock already released");
+ }
+ }
+ }
+
+ /**
+ * Updates the wake lock used to control proximity sensor behavior, based on the current state of
+ * the phone.
+ *
+ * <p>On devices that have a proximity sensor, to avoid false touches during a call, we hold a
+ * PROXIMITY_SCREEN_OFF_WAKE_LOCK wake lock whenever the phone is off hook. (When held, that wake
+ * lock causes the screen to turn off automatically when the sensor detects an object close to the
+ * screen.)
+ *
+ * <p>This method is a no-op for devices that don't have a proximity sensor.
+ *
+ * <p>Proximity wake lock will be released if any of the following conditions are true: the audio
+ * is routed through bluetooth, a wired headset, or the speaker; the user requested, received a
+ * request for, or is in a video call; or the phone is horizontal while in a call.
+ */
+ private synchronized void updateProximitySensorMode() {
+ final int audioRoute = mAudioModeProvider.getAudioState().getRoute();
+
+ boolean screenOnImmediately =
+ (CallAudioState.ROUTE_WIRED_HEADSET == audioRoute
+ || CallAudioState.ROUTE_SPEAKER == audioRoute
+ || CallAudioState.ROUTE_BLUETOOTH == audioRoute
+ || mIsAttemptingVideoCall
+ || mIsVideoCall);
+
+ // We do not keep the screen off when the user is outside in-call screen and we are
+ // horizontal, but we do not force it on when we become horizontal until the
+ // proximity sensor goes negative.
+ final boolean horizontal = (mOrientation == AccelerometerListener.ORIENTATION_HORIZONTAL);
+ screenOnImmediately |= !mUiShowing && horizontal;
+
+ // We do not keep the screen off when dialpad is visible, we are horizontal, and
+ // the in-call screen is being shown.
+ // At that moment we're pretty sure users want to use it, instead of letting the
+ // proximity sensor turn off the screen by their hands.
+ screenOnImmediately |= mDialpadVisible && horizontal;
+
+ LogUtil.i(
+ "ProximitySensor.updateProximitySensorMode",
+ "screenOnImmediately: %b, dialPadVisible: %b, "
+ + "offHook: %b, horizontal: %b, uiShowing: %b, audioRoute: %s",
+ screenOnImmediately,
+ mDialpadVisible,
+ mIsPhoneOffhook,
+ mOrientation == AccelerometerListener.ORIENTATION_HORIZONTAL,
+ mUiShowing,
+ CallAudioState.audioRouteToString(audioRoute));
+
+ if (mIsPhoneOffhook && !screenOnImmediately) {
+ LogUtil.v("ProximitySensor.updateProximitySensorMode", "turning on proximity sensor");
+ // Phone is in use! Arrange for the screen to turn off
+ // automatically when the sensor detects a close object.
+ turnOnProximitySensor();
+ } else {
+ LogUtil.v("ProximitySensor.updateProximitySensorMode", "turning off proximity sensor");
+ // Phone is either idle, or ringing. We don't want any special proximity sensor
+ // behavior in either case.
+ turnOffProximitySensor(screenOnImmediately);
+ }
+ }
+
+ /**
+ * Implementation of a {@link DisplayListener} that maintains a binary state: Screen on vs screen
+ * off. Used by the proximity sensor manager to decide whether or not it needs to listen to
+ * accelerometer events.
+ */
+ public class ProximityDisplayListener implements DisplayListener {
+
+ private DisplayManager mDisplayManager;
+ private boolean mIsDisplayOn = true;
+
+ ProximityDisplayListener(DisplayManager displayManager) {
+ mDisplayManager = displayManager;
+ }
+
+ void register() {
+ mDisplayManager.registerDisplayListener(this, null);
+ }
+
+ void unregister() {
+ mDisplayManager.unregisterDisplayListener(this);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {}
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ final Display display = mDisplayManager.getDisplay(displayId);
+
+ final boolean isDisplayOn = display.getState() != Display.STATE_OFF;
+ // For call purposes, we assume that as long as the screen is not truly off, it is
+ // considered on, even if it is in an unknown or low power idle state.
+ if (isDisplayOn != mIsDisplayOn) {
+ mIsDisplayOn = isDisplayOn;
+ onDisplayStateChanged(mIsDisplayOn);
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {}
+ }
+}
diff --git a/java/com/android/incallui/StatusBarNotifier.java b/java/com/android/incallui/StatusBarNotifier.java
new file mode 100644
index 000000000..c7226753f
--- /dev/null
+++ b/java/com/android/incallui/StatusBarNotifier.java
@@ -0,0 +1,842 @@
+/*
+ * Copyright (C) 2013 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.incallui;
+
+import static com.android.contacts.common.compat.CallCompat.Details.PROPERTY_ENTERPRISE_CALL;
+import static com.android.incallui.NotificationBroadcastReceiver.ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST;
+import static com.android.incallui.NotificationBroadcastReceiver.ACTION_ANSWER_VIDEO_INCOMING_CALL;
+import static com.android.incallui.NotificationBroadcastReceiver.ACTION_ANSWER_VOICE_INCOMING_CALL;
+import static com.android.incallui.NotificationBroadcastReceiver.ACTION_DECLINE_INCOMING_CALL;
+import static com.android.incallui.NotificationBroadcastReceiver.ACTION_DECLINE_VIDEO_UPGRADE_REQUEST;
+import static com.android.incallui.NotificationBroadcastReceiver.ACTION_HANG_UP_ONGOING_CALL;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.media.AudioAttributes;
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.ColorRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.support.annotation.VisibleForTesting;
+import android.telecom.Call.Details;
+import android.telecom.PhoneAccount;
+import android.telecom.TelecomManager;
+import android.text.BidiFormatter;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.TextDirectionHeuristics;
+import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
+import com.android.contacts.common.ContactsUtils;
+import com.android.contacts.common.ContactsUtils.UserType;
+import com.android.contacts.common.preference.ContactsPreferences;
+import com.android.contacts.common.util.BitmapUtil;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.util.DrawableConverter;
+import com.android.incallui.ContactInfoCache.ContactCacheEntry;
+import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback;
+import com.android.incallui.InCallPresenter.InCallState;
+import com.android.incallui.async.PausableExecutorImpl;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.DialerCall.SessionModificationState;
+import com.android.incallui.call.DialerCallListener;
+import com.android.incallui.ringtone.DialerRingtoneManager;
+import com.android.incallui.ringtone.InCallTonePlayer;
+import com.android.incallui.ringtone.ToneGeneratorFactory;
+import java.util.Objects;
+
+/** This class adds Notifications to the status bar for the in-call experience. */
+public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
+
+ // Notification types
+ // Indicates that no notification is currently showing.
+ private static final int NOTIFICATION_NONE = 0;
+ // Notification for an active call. This is non-interruptive, but cannot be dismissed.
+ private static final int NOTIFICATION_IN_CALL = 1;
+ // Notification for incoming calls. This is interruptive and will show up as a HUN.
+ private static final int NOTIFICATION_INCOMING_CALL = 2;
+
+ private static final int PENDING_INTENT_REQUEST_CODE_NON_FULL_SCREEN = 0;
+ private static final int PENDING_INTENT_REQUEST_CODE_FULL_SCREEN = 1;
+
+ private static final long[] VIBRATE_PATTERN = new long[] {0, 1000, 1000};
+
+ private final Context mContext;
+ private final ContactInfoCache mContactInfoCache;
+ private final NotificationManager mNotificationManager;
+ private final DialerRingtoneManager mDialerRingtoneManager;
+ @Nullable private ContactsPreferences mContactsPreferences;
+ private int mCurrentNotification = NOTIFICATION_NONE;
+ private int mCallState = DialerCall.State.INVALID;
+ private int mSavedIcon = 0;
+ private String mSavedContent = null;
+ private Bitmap mSavedLargeIcon;
+ private String mSavedContentTitle;
+ private Uri mRingtone;
+ private StatusBarCallListener mStatusBarCallListener;
+
+ public StatusBarNotifier(@NonNull Context context, @NonNull ContactInfoCache contactInfoCache) {
+ Objects.requireNonNull(context);
+ mContext = context;
+ mContactsPreferences = ContactsPreferencesFactory.newContactsPreferences(mContext);
+ mContactInfoCache = contactInfoCache;
+ mNotificationManager = context.getSystemService(NotificationManager.class);
+ mDialerRingtoneManager =
+ new DialerRingtoneManager(
+ new InCallTonePlayer(new ToneGeneratorFactory(), new PausableExecutorImpl()),
+ CallList.getInstance());
+ mCurrentNotification = NOTIFICATION_NONE;
+ }
+
+ /**
+ * Should only be called from a irrecoverable state where it is necessary to dismiss all
+ * notifications.
+ */
+ static void clearAllCallNotifications(Context backupContext) {
+ Log.i(
+ StatusBarNotifier.class.getSimpleName(),
+ "Something terrible happened. Clear all InCall notifications");
+
+ NotificationManager notificationManager =
+ backupContext.getSystemService(NotificationManager.class);
+ notificationManager.cancel(NOTIFICATION_IN_CALL);
+ notificationManager.cancel(NOTIFICATION_INCOMING_CALL);
+ }
+
+ private static int getWorkStringFromPersonalString(int resId) {
+ if (resId == R.string.notification_ongoing_call) {
+ return R.string.notification_ongoing_work_call;
+ } else if (resId == R.string.notification_ongoing_call_wifi) {
+ return R.string.notification_ongoing_work_call_wifi;
+ } else if (resId == R.string.notification_incoming_call_wifi) {
+ return R.string.notification_incoming_work_call_wifi;
+ } else if (resId == R.string.notification_incoming_call) {
+ return R.string.notification_incoming_work_call;
+ } else {
+ return resId;
+ }
+ }
+
+ /**
+ * Returns PendingIntent for answering a phone call. This will typically be used from Notification
+ * context.
+ */
+ private static PendingIntent createNotificationPendingIntent(Context context, String action) {
+ final Intent intent = new Intent(action, null, context, NotificationBroadcastReceiver.class);
+ return PendingIntent.getBroadcast(context, 0, intent, 0);
+ }
+
+ /** Creates notifications according to the state we receive from {@link InCallPresenter}. */
+ @Override
+ public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
+ Log.d(this, "onStateChange");
+ updateNotification(callList);
+ }
+
+ /**
+ * Updates the phone app's status bar notification *and* launches the incoming call UI in response
+ * to a new incoming call.
+ *
+ * <p>If an incoming call is ringing (or call-waiting), the notification will also include a
+ * "fullScreenIntent" that will cause the InCallScreen to be launched, unless the current
+ * foreground activity is marked as "immersive".
+ *
+ * <p>(This is the mechanism that actually brings up the incoming call UI when we receive a "new
+ * ringing connection" event from the telephony layer.)
+ *
+ * <p>Also note that this method is safe to call even if the phone isn't actually ringing (or,
+ * more likely, if an incoming call *was* ringing briefly but then disconnected). In that case,
+ * we'll simply update or cancel the in-call notification based on the current phone state.
+ *
+ * @see #updateInCallNotification(CallList)
+ */
+ public void updateNotification(CallList callList) {
+ updateInCallNotification(callList);
+ }
+
+ /**
+ * Take down the in-call notification.
+ *
+ * @see #updateInCallNotification(CallList)
+ */
+ private void cancelNotification() {
+ if (mStatusBarCallListener != null) {
+ setStatusBarCallListener(null);
+ }
+ if (mCurrentNotification != NOTIFICATION_NONE) {
+ Log.d(this, "cancelInCall()...");
+ mNotificationManager.cancel(mCurrentNotification);
+ }
+ mCurrentNotification = NOTIFICATION_NONE;
+ }
+
+ /**
+ * Helper method for updateInCallNotification() and updateNotification(): Update the phone app's
+ * status bar notification based on the current telephony state, or cancels the notification if
+ * the phone is totally idle.
+ */
+ private void updateInCallNotification(CallList callList) {
+ Log.d(this, "updateInCallNotification...");
+
+ final DialerCall call = getCallToShow(callList);
+
+ if (call != null) {
+ showNotification(callList, call);
+ } else {
+ cancelNotification();
+ }
+ }
+
+ private void showNotification(final CallList callList, final DialerCall call) {
+ final boolean isIncoming =
+ (call.getState() == DialerCall.State.INCOMING
+ || call.getState() == DialerCall.State.CALL_WAITING);
+ setStatusBarCallListener(new StatusBarCallListener(call));
+
+ // we make a call to the contact info cache to query for supplemental data to what the
+ // call provides. This includes the contact name and photo.
+ // This callback will always get called immediately and synchronously with whatever data
+ // it has available, and may make a subsequent call later (same thread) if it had to
+ // call into the contacts provider for more data.
+ mContactInfoCache.findInfo(
+ call,
+ isIncoming,
+ new ContactInfoCacheCallback() {
+ @Override
+ public void onContactInfoComplete(String callId, ContactCacheEntry entry) {
+ DialerCall call = callList.getCallById(callId);
+ if (call != null) {
+ call.getLogState().contactLookupResult = entry.contactLookupResult;
+ buildAndSendNotification(callList, call, entry);
+ }
+ }
+
+ @Override
+ public void onImageLoadComplete(String callId, ContactCacheEntry entry) {
+ DialerCall call = callList.getCallById(callId);
+ if (call != null) {
+ buildAndSendNotification(callList, call, entry);
+ }
+ }
+ });
+ }
+
+ /** Sets up the main Ui for the notification */
+ private void buildAndSendNotification(
+ CallList callList, DialerCall originalCall, ContactCacheEntry contactInfo) {
+ // This can get called to update an existing notification after contact information has come
+ // back. However, it can happen much later. Before we continue, we need to make sure that
+ // the call being passed in is still the one we want to show in the notification.
+ final DialerCall call = getCallToShow(callList);
+ if (call == null || !call.getId().equals(originalCall.getId())) {
+ return;
+ }
+
+ final int callState = call.getState();
+
+ // Check if data has changed; if nothing is different, don't issue another notification.
+ final int iconResId = getIconToDisplay(call);
+ Bitmap largeIcon = getLargeIconToDisplay(contactInfo, call);
+ final String content = getContentString(call, contactInfo.userType);
+ final String contentTitle = getContentTitle(contactInfo, call);
+
+ final boolean isVideoUpgradeRequest =
+ call.getSessionModificationState()
+ == DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
+ final int notificationType;
+ if (callState == DialerCall.State.INCOMING
+ || callState == DialerCall.State.CALL_WAITING
+ || isVideoUpgradeRequest) {
+ notificationType = NOTIFICATION_INCOMING_CALL;
+ } else {
+ notificationType = NOTIFICATION_IN_CALL;
+ }
+
+ if (!checkForChangeAndSaveData(
+ iconResId,
+ content,
+ largeIcon,
+ contentTitle,
+ callState,
+ notificationType,
+ contactInfo.contactRingtoneUri)) {
+ return;
+ }
+
+ if (largeIcon != null) {
+ largeIcon = getRoundedIcon(largeIcon);
+ }
+
+ // This builder is used for the notification shown when the device is locked and the user
+ // has set their notification settings to 'hide sensitive content'
+ // {@see Notification.Builder#setPublicVersion}.
+ Notification.Builder publicBuilder = new Notification.Builder(mContext);
+ publicBuilder
+ .setSmallIcon(iconResId)
+ .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
+ // Hide work call state for the lock screen notification
+ .setContentTitle(getContentString(call, ContactsUtils.USER_TYPE_CURRENT));
+ setNotificationWhen(call, callState, publicBuilder);
+
+ // Builder for the notification shown when the device is unlocked or the user has set their
+ // notification settings to 'show all notification content'.
+ final Notification.Builder builder = getNotificationBuilder();
+ builder.setPublicVersion(publicBuilder.build());
+
+ // Set up the main intent to send the user to the in-call screen
+ builder.setContentIntent(
+ createLaunchPendingIntent(false /* isFullScreen */, call.isVideoCall()));
+
+ // Set the intent as a full screen intent as well if a call is incoming
+ if (notificationType == NOTIFICATION_INCOMING_CALL) {
+ if (!InCallPresenter.getInstance().isActivityStarted()) {
+ configureFullScreenIntent(
+ builder,
+ createLaunchPendingIntent(true /* isFullScreen */, call.isVideoCall()),
+ callList,
+ call);
+ } else {
+ // If the incall screen is already up, we don't want to show HUN but regular notification
+ // should still be shown. In order to do that the previous one with full screen intent
+ // needs to be cancelled.
+ LogUtil.d(
+ "StatusBarNotifier.buildAndSendNotification",
+ "cancel previous incoming call notification");
+ mNotificationManager.cancel(NOTIFICATION_INCOMING_CALL);
+ }
+ // Set the notification category for incoming calls
+ builder.setCategory(Notification.CATEGORY_CALL);
+ }
+
+ // Set the content
+ builder.setContentText(content);
+ builder.setSmallIcon(iconResId);
+ builder.setContentTitle(contentTitle);
+ builder.setLargeIcon(largeIcon);
+ builder.setColor(mContext.getResources().getColor(R.color.dialer_theme_color));
+
+ if (isVideoUpgradeRequest) {
+ builder.setUsesChronometer(false);
+ addDismissUpgradeRequestAction(builder);
+ addAcceptUpgradeRequestAction(builder);
+ } else {
+ createIncomingCallNotification(call, callState, builder);
+ }
+
+ addPersonReference(builder, contactInfo, call);
+
+ // Fire off the notification
+ Notification notification = builder.build();
+
+ if (mDialerRingtoneManager.shouldPlayRingtone(callState, contactInfo.contactRingtoneUri)) {
+ notification.flags |= Notification.FLAG_INSISTENT;
+ notification.sound = contactInfo.contactRingtoneUri;
+ AudioAttributes.Builder audioAttributes = new AudioAttributes.Builder();
+ audioAttributes.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC);
+ audioAttributes.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE);
+ notification.audioAttributes = audioAttributes.build();
+ if (mDialerRingtoneManager.shouldVibrate(mContext.getContentResolver())) {
+ notification.vibrate = VIBRATE_PATTERN;
+ }
+ }
+ if (mDialerRingtoneManager.shouldPlayCallWaitingTone(callState)) {
+ Log.v(this, "Playing call waiting tone");
+ mDialerRingtoneManager.playCallWaitingTone();
+ }
+ if (mCurrentNotification != notificationType && mCurrentNotification != NOTIFICATION_NONE) {
+ Log.i(this, "Previous notification already showing - cancelling " + mCurrentNotification);
+ mNotificationManager.cancel(mCurrentNotification);
+ }
+
+ Log.i(this, "Displaying notification for " + notificationType);
+ try {
+ mNotificationManager.notify(notificationType, notification);
+ } catch (RuntimeException e) {
+ // TODO(b/34744003): Move the memory stats into silent feedback PSD.
+ ActivityManager activityManager = mContext.getSystemService(ActivityManager.class);
+ ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
+ activityManager.getMemoryInfo(memoryInfo);
+ throw new RuntimeException(
+ String.format(
+ "Error displaying notification with photo type: %d (low memory? %b, availMem: %d)",
+ contactInfo.photoType, memoryInfo.lowMemory, memoryInfo.availMem),
+ e);
+ }
+ call.getLatencyReport().onNotificationShown();
+ mCurrentNotification = notificationType;
+ }
+
+ private void createIncomingCallNotification(
+ DialerCall call, int state, Notification.Builder builder) {
+ setNotificationWhen(call, state, builder);
+
+ // Add hang up option for any active calls (active | onhold), outgoing calls (dialing).
+ if (state == DialerCall.State.ACTIVE
+ || state == DialerCall.State.ONHOLD
+ || DialerCall.State.isDialing(state)) {
+ addHangupAction(builder);
+ } else if (state == DialerCall.State.INCOMING || state == DialerCall.State.CALL_WAITING) {
+ addDismissAction(builder);
+ if (call.isVideoCall()) {
+ addVideoCallAction(builder);
+ } else {
+ addAnswerAction(builder);
+ }
+ }
+ }
+
+ /**
+ * Sets the notification's when section as needed. For active calls, this is explicitly set as the
+ * duration of the call. For all other states, the notification will automatically show the time
+ * at which the notification was created.
+ */
+ private void setNotificationWhen(DialerCall call, int state, Notification.Builder builder) {
+ if (state == DialerCall.State.ACTIVE) {
+ builder.setUsesChronometer(true);
+ builder.setWhen(call.getConnectTimeMillis());
+ } else {
+ builder.setUsesChronometer(false);
+ }
+ }
+
+ /**
+ * Checks the new notification data and compares it against any notification that we are already
+ * displaying. If the data is exactly the same, we return false so that we do not issue a new
+ * notification for the exact same data.
+ */
+ private boolean checkForChangeAndSaveData(
+ int icon,
+ String content,
+ Bitmap largeIcon,
+ String contentTitle,
+ int state,
+ int notificationType,
+ Uri ringtone) {
+
+ // The two are different:
+ // if new title is not null, it should be different from saved version OR
+ // if new title is null, the saved version should not be null
+ final boolean contentTitleChanged =
+ (contentTitle != null && !contentTitle.equals(mSavedContentTitle))
+ || (contentTitle == null && mSavedContentTitle != null);
+
+ // any change means we are definitely updating
+ boolean retval =
+ (mSavedIcon != icon)
+ || !Objects.equals(mSavedContent, content)
+ || (mCallState != state)
+ || (mSavedLargeIcon != largeIcon)
+ || contentTitleChanged
+ || !Objects.equals(mRingtone, ringtone);
+
+ // If we aren't showing a notification right now or the notification type is changing,
+ // definitely do an update.
+ if (mCurrentNotification != notificationType) {
+ if (mCurrentNotification == NOTIFICATION_NONE) {
+ Log.d(this, "Showing notification for first time.");
+ }
+ retval = true;
+ }
+
+ mSavedIcon = icon;
+ mSavedContent = content;
+ mCallState = state;
+ mSavedLargeIcon = largeIcon;
+ mSavedContentTitle = contentTitle;
+ mRingtone = ringtone;
+
+ if (retval) {
+ Log.d(this, "Data changed. Showing notification");
+ }
+
+ return retval;
+ }
+
+ /** Returns the main string to use in the notification. */
+ @VisibleForTesting
+ @Nullable
+ String getContentTitle(ContactCacheEntry contactInfo, DialerCall call) {
+ if (call.isConferenceCall() && !call.hasProperty(Details.PROPERTY_GENERIC_CONFERENCE)) {
+ return mContext.getResources().getString(R.string.conference_call_name);
+ }
+
+ String preferredName =
+ ContactDisplayUtils.getPreferredDisplayName(
+ contactInfo.namePrimary, contactInfo.nameAlternative, mContactsPreferences);
+ if (TextUtils.isEmpty(preferredName)) {
+ return TextUtils.isEmpty(contactInfo.number)
+ ? null
+ : BidiFormatter.getInstance()
+ .unicodeWrap(contactInfo.number, TextDirectionHeuristics.LTR);
+ }
+ return preferredName;
+ }
+
+ private void addPersonReference(
+ Notification.Builder builder, ContactCacheEntry contactInfo, DialerCall call) {
+ // Query {@link Contacts#CONTENT_LOOKUP_URI} directly with work lookup key is not allowed.
+ // So, do not pass {@link Contacts#CONTENT_LOOKUP_URI} to NotificationManager to avoid
+ // NotificationManager using it.
+ if (contactInfo.lookupUri != null && contactInfo.userType != ContactsUtils.USER_TYPE_WORK) {
+ builder.addPerson(contactInfo.lookupUri.toString());
+ } else if (!TextUtils.isEmpty(call.getNumber())) {
+ builder.addPerson(Uri.fromParts(PhoneAccount.SCHEME_TEL, call.getNumber(), null).toString());
+ }
+ }
+
+ /** Gets a large icon from the contact info object to display in the notification. */
+ private Bitmap getLargeIconToDisplay(ContactCacheEntry contactInfo, DialerCall call) {
+ Bitmap largeIcon = null;
+ if (call.isConferenceCall() && !call.hasProperty(Details.PROPERTY_GENERIC_CONFERENCE)) {
+ largeIcon = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.img_conference);
+ }
+ if (contactInfo.photo != null && (contactInfo.photo instanceof BitmapDrawable)) {
+ largeIcon = ((BitmapDrawable) contactInfo.photo).getBitmap();
+ }
+ if (call.isSpam()) {
+ Drawable drawable = mContext.getResources().getDrawable(R.drawable.blocked_contact);
+ largeIcon = DrawableConverter.drawableToBitmap(drawable);
+ }
+ return largeIcon;
+ }
+
+ private Bitmap getRoundedIcon(Bitmap bitmap) {
+ if (bitmap == null) {
+ return null;
+ }
+ final int height =
+ (int) mContext.getResources().getDimension(android.R.dimen.notification_large_icon_height);
+ final int width =
+ (int) mContext.getResources().getDimension(android.R.dimen.notification_large_icon_width);
+ return BitmapUtil.getRoundedBitmap(bitmap, width, height);
+ }
+
+ /**
+ * Returns the appropriate icon res Id to display based on the call for which we want to display
+ * information.
+ */
+ private int getIconToDisplay(DialerCall call) {
+ // Even if both lines are in use, we only show a single item in
+ // the expanded Notifications UI. It's labeled "Ongoing call"
+ // (or "On hold" if there's only one call, and it's on hold.)
+ // Also, we don't have room to display caller-id info from two
+ // different calls. So if both lines are in use, display info
+ // from the foreground call. And if there's a ringing call,
+ // display that regardless of the state of the other calls.
+ if (call.getState() == DialerCall.State.ONHOLD) {
+ return R.drawable.ic_phone_paused_white_24dp;
+ } else if (call.getSessionModificationState()
+ == DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
+ return R.drawable.ic_videocam;
+ }
+ return R.anim.on_going_call;
+ }
+
+ /** Returns the message to use with the notification. */
+ private String getContentString(DialerCall call, @UserType long userType) {
+ boolean isIncomingOrWaiting =
+ call.getState() == DialerCall.State.INCOMING
+ || call.getState() == DialerCall.State.CALL_WAITING;
+
+ if (isIncomingOrWaiting
+ && call.getNumberPresentation() == TelecomManager.PRESENTATION_ALLOWED) {
+
+ if (!TextUtils.isEmpty(call.getChildNumber())) {
+ return mContext.getString(R.string.child_number, call.getChildNumber());
+ } else if (!TextUtils.isEmpty(call.getCallSubject()) && call.isCallSubjectSupported()) {
+ return call.getCallSubject();
+ }
+ }
+
+ int resId = R.string.notification_ongoing_call;
+ if (call.hasProperty(Details.PROPERTY_WIFI)) {
+ resId = R.string.notification_ongoing_call_wifi;
+ }
+
+ if (isIncomingOrWaiting) {
+ if (call.hasProperty(Details.PROPERTY_WIFI)) {
+ resId = R.string.notification_incoming_call_wifi;
+ } else {
+ if (call.isSpam()) {
+ resId = R.string.notification_incoming_spam_call;
+ } else {
+ resId = R.string.notification_incoming_call;
+ }
+ }
+ } else if (call.getState() == DialerCall.State.ONHOLD) {
+ resId = R.string.notification_on_hold;
+ } else if (DialerCall.State.isDialing(call.getState())) {
+ resId = R.string.notification_dialing;
+ } else if (call.getSessionModificationState()
+ == DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
+ resId = R.string.notification_requesting_video_call;
+ }
+
+ // Is the call placed through work connection service.
+ boolean isWorkCall = call.hasProperty(PROPERTY_ENTERPRISE_CALL);
+ if (userType == ContactsUtils.USER_TYPE_WORK || isWorkCall) {
+ resId = getWorkStringFromPersonalString(resId);
+ }
+
+ return mContext.getString(resId);
+ }
+
+ /** Gets the most relevant call to display in the notification. */
+ private DialerCall getCallToShow(CallList callList) {
+ if (callList == null) {
+ return null;
+ }
+ DialerCall call = callList.getIncomingCall();
+ if (call == null) {
+ call = callList.getOutgoingCall();
+ }
+ if (call == null) {
+ call = callList.getVideoUpgradeRequestCall();
+ }
+ if (call == null) {
+ call = callList.getActiveOrBackgroundCall();
+ }
+ return call;
+ }
+
+ private Spannable getActionText(@StringRes int stringRes, @ColorRes int colorRes) {
+ Spannable spannable = new SpannableString(mContext.getText(stringRes));
+ if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
+ // This will only work for cases where the Notification.Builder has a fullscreen intent set
+ // Notification.Builder that does not have a full screen intent will take the color of the
+ // app and the following leads to a no-op.
+ spannable.setSpan(
+ new ForegroundColorSpan(mContext.getColor(colorRes)), 0, spannable.length(), 0);
+ }
+ return spannable;
+ }
+
+ private void addAnswerAction(Notification.Builder builder) {
+ Log.d(this, "Will show \"answer\" action in the incoming call Notification");
+ PendingIntent answerVoicePendingIntent =
+ createNotificationPendingIntent(mContext, ACTION_ANSWER_VOICE_INCOMING_CALL);
+ builder.addAction(
+ R.anim.on_going_call,
+ getActionText(R.string.notification_action_answer, R.color.notification_action_accept),
+ answerVoicePendingIntent);
+ }
+
+ private void addDismissAction(Notification.Builder builder) {
+ Log.d(this, "Will show \"decline\" action in the incoming call Notification");
+ PendingIntent declinePendingIntent =
+ createNotificationPendingIntent(mContext, ACTION_DECLINE_INCOMING_CALL);
+ builder.addAction(
+ R.drawable.ic_close_dk,
+ getActionText(R.string.notification_action_dismiss, R.color.notification_action_dismiss),
+ declinePendingIntent);
+ }
+
+ private void addHangupAction(Notification.Builder builder) {
+ Log.d(this, "Will show \"hang-up\" action in the ongoing active call Notification");
+ PendingIntent hangupPendingIntent =
+ createNotificationPendingIntent(mContext, ACTION_HANG_UP_ONGOING_CALL);
+ builder.addAction(
+ R.drawable.ic_call_end_white_24dp,
+ getActionText(R.string.notification_action_end_call, R.color.notification_action_end_call),
+ hangupPendingIntent);
+ }
+
+ private void addVideoCallAction(Notification.Builder builder) {
+ Log.i(this, "Will show \"video\" action in the incoming call Notification");
+ PendingIntent answerVideoPendingIntent =
+ createNotificationPendingIntent(mContext, ACTION_ANSWER_VIDEO_INCOMING_CALL);
+ builder.addAction(
+ R.drawable.ic_videocam,
+ getActionText(
+ R.string.notification_action_answer_video, R.color.notification_action_answer_video),
+ answerVideoPendingIntent);
+ }
+
+ private void addAcceptUpgradeRequestAction(Notification.Builder builder) {
+ Log.i(this, "Will show \"accept upgrade\" action in the incoming call Notification");
+ PendingIntent acceptVideoPendingIntent =
+ createNotificationPendingIntent(mContext, ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST);
+ builder.addAction(
+ R.drawable.ic_videocam,
+ getActionText(R.string.notification_action_accept, R.color.notification_action_accept),
+ acceptVideoPendingIntent);
+ }
+
+ private void addDismissUpgradeRequestAction(Notification.Builder builder) {
+ Log.i(this, "Will show \"dismiss upgrade\" action in the incoming call Notification");
+ PendingIntent declineVideoPendingIntent =
+ createNotificationPendingIntent(mContext, ACTION_DECLINE_VIDEO_UPGRADE_REQUEST);
+ builder.addAction(
+ R.drawable.ic_videocam,
+ getActionText(R.string.notification_action_dismiss, R.color.notification_action_dismiss),
+ declineVideoPendingIntent);
+ }
+
+ /** Adds fullscreen intent to the builder. */
+ private void configureFullScreenIntent(
+ Notification.Builder builder, PendingIntent intent, CallList callList, DialerCall call) {
+ // Ok, we actually want to launch the incoming call
+ // UI at this point (in addition to simply posting a notification
+ // to the status bar). Setting fullScreenIntent will cause
+ // the InCallScreen to be launched immediately *unless* the
+ // current foreground activity is marked as "immersive".
+ Log.d(this, "- Setting fullScreenIntent: " + intent);
+ builder.setFullScreenIntent(intent, true);
+
+ // Ugly hack alert:
+ //
+ // The NotificationManager has the (undocumented) behavior
+ // that it will *ignore* the fullScreenIntent field if you
+ // post a new Notification that matches the ID of one that's
+ // already active. Unfortunately this is exactly what happens
+ // when you get an incoming call-waiting call: the
+ // "ongoing call" notification is already visible, so the
+ // InCallScreen won't get launched in this case!
+ // (The result: if you bail out of the in-call UI while on a
+ // call and then get a call-waiting call, the incoming call UI
+ // won't come up automatically.)
+ //
+ // The workaround is to just notice this exact case (this is a
+ // call-waiting call *and* the InCallScreen is not in the
+ // foreground) and manually cancel the in-call notification
+ // before (re)posting it.
+ //
+ // TODO: there should be a cleaner way of avoiding this
+ // problem (see discussion in bug 3184149.)
+
+ // If a call is onhold during an incoming call, the call actually comes in as
+ // INCOMING. For that case *and* traditional call-waiting, we want to
+ // cancel the notification.
+ boolean isCallWaiting =
+ (call.getState() == DialerCall.State.CALL_WAITING
+ || (call.getState() == DialerCall.State.INCOMING
+ && callList.getBackgroundCall() != null));
+
+ if (isCallWaiting) {
+ Log.i(this, "updateInCallNotification: call-waiting! force relaunch...");
+ // Cancel the IN_CALL_NOTIFICATION immediately before
+ // (re)posting it; this seems to force the
+ // NotificationManager to launch the fullScreenIntent.
+ mNotificationManager.cancel(NOTIFICATION_IN_CALL);
+ }
+ }
+
+ private Notification.Builder getNotificationBuilder() {
+ final Notification.Builder builder = new Notification.Builder(mContext);
+ builder.setOngoing(true);
+
+ // Make the notification prioritized over the other normal notifications.
+ builder.setPriority(Notification.PRIORITY_HIGH);
+
+ return builder;
+ }
+
+ private PendingIntent createLaunchPendingIntent(boolean isFullScreen, boolean isVideoCall) {
+ Intent intent =
+ InCallActivity.getIntent(
+ mContext,
+ false /* showDialpad */,
+ false /* newOutgoingCall */,
+ isVideoCall,
+ isFullScreen);
+
+ int requestCode = PENDING_INTENT_REQUEST_CODE_NON_FULL_SCREEN;
+ if (isFullScreen) {
+ // Use a unique request code so that the pending intent isn't clobbered by the
+ // non-full screen pending intent.
+ requestCode = PENDING_INTENT_REQUEST_CODE_FULL_SCREEN;
+ }
+
+ // PendingIntent that can be used to launch the InCallActivity. The
+ // system fires off this intent if the user pulls down the windowshade
+ // and clicks the notification's expanded view. It's also used to
+ // launch the InCallActivity immediately when when there's an incoming
+ // call (see the "fullScreenIntent" field below).
+ return PendingIntent.getActivity(mContext, requestCode, intent, 0);
+ }
+
+ private void setStatusBarCallListener(StatusBarCallListener listener) {
+ if (mStatusBarCallListener != null) {
+ mStatusBarCallListener.cleanup();
+ }
+ mStatusBarCallListener = listener;
+ }
+
+ private class StatusBarCallListener implements DialerCallListener {
+
+ private DialerCall mDialerCall;
+
+ StatusBarCallListener(DialerCall dialerCall) {
+ mDialerCall = dialerCall;
+ mDialerCall.addListener(this);
+ }
+
+ void cleanup() {
+ mDialerCall.removeListener(this);
+ }
+
+ @Override
+ public void onDialerCallDisconnect() {}
+
+ @Override
+ public void onDialerCallUpdate() {
+ if (CallList.getInstance().getIncomingCall() == null) {
+ mDialerRingtoneManager.stopCallWaitingTone();
+ }
+ }
+
+ @Override
+ public void onDialerCallChildNumberChange() {}
+
+ @Override
+ public void onDialerCallLastForwardedNumberChange() {}
+
+ @Override
+ public void onDialerCallUpgradeToVideo() {}
+
+ @Override
+ public void onWiFiToLteHandover() {}
+
+ @Override
+ public void onHandoverToWifiFailure() {}
+
+ /**
+ * Responds to changes in the session modification state for the call by dismissing the status
+ * bar notification as required.
+ */
+ @Override
+ public void onDialerCallSessionModificationStateChange(@SessionModificationState int state) {
+ if (state == DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST) {
+ cleanup();
+ updateNotification(CallList.getInstance());
+ }
+ }
+ }
+}
diff --git a/java/com/android/incallui/ThemeColorManager.java b/java/com/android/incallui/ThemeColorManager.java
new file mode 100644
index 000000000..a88ae33cd
--- /dev/null
+++ b/java/com/android/incallui/ThemeColorManager.java
@@ -0,0 +1,142 @@
+/*
+ * 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.incallui;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.support.annotation.ColorInt;
+import android.support.annotation.Nullable;
+import android.support.v4.graphics.ColorUtils;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import com.android.contacts.common.util.MaterialColorMapUtils;
+import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
+import com.android.incallui.call.DialerCall;
+
+/**
+ * Calculates the background color for the in call window. The background color is based on the SIM
+ * and spam status.
+ */
+public class ThemeColorManager {
+ private final MaterialColorMapUtils colorMap;
+ @ColorInt private int primaryColor;
+ @ColorInt private int secondaryColor;
+ @ColorInt private int backgroundColorTop;
+ @ColorInt private int backgroundColorMiddle;
+ @ColorInt private int backgroundColorBottom;
+ @ColorInt private int backgroundColorSolid;
+
+ /**
+ * If there is no actual call currently in the call list, this will be used as a fallback to
+ * determine the theme color for InCallUI.
+ */
+ @Nullable private PhoneAccountHandle pendingPhoneAccountHandle;
+
+ public ThemeColorManager(MaterialColorMapUtils colorMap) {
+ this.colorMap = colorMap;
+ }
+
+ public void setPendingPhoneAccountHandle(@Nullable PhoneAccountHandle pendingPhoneAccountHandle) {
+ this.pendingPhoneAccountHandle = pendingPhoneAccountHandle;
+ }
+
+ public void onForegroundCallChanged(Context context, @Nullable DialerCall newForegroundCall) {
+ if (newForegroundCall == null) {
+ updateThemeColors(context, pendingPhoneAccountHandle, false);
+ } else {
+ updateThemeColors(context, newForegroundCall.getAccountHandle(), newForegroundCall.isSpam());
+ }
+ }
+
+ private void updateThemeColors(
+ Context context, @Nullable PhoneAccountHandle handle, boolean isSpam) {
+ MaterialPalette palette;
+ if (isSpam) {
+ palette =
+ colorMap.calculatePrimaryAndSecondaryColor(R.color.incall_call_spam_background_color);
+ backgroundColorTop = context.getColor(R.color.incall_background_gradient_spam_top);
+ backgroundColorMiddle = context.getColor(R.color.incall_background_gradient_spam_middle);
+ backgroundColorBottom = context.getColor(R.color.incall_background_gradient_spam_bottom);
+ backgroundColorSolid = context.getColor(R.color.incall_background_multiwindow_spam);
+ } else {
+ @ColorInt int highlightColor = getHighlightColor(context, handle);
+ palette = colorMap.calculatePrimaryAndSecondaryColor(highlightColor);
+ backgroundColorTop = context.getColor(R.color.incall_background_gradient_top);
+ backgroundColorMiddle = context.getColor(R.color.incall_background_gradient_middle);
+ backgroundColorBottom = context.getColor(R.color.incall_background_gradient_bottom);
+ backgroundColorSolid = context.getColor(R.color.incall_background_multiwindow);
+ if (highlightColor != PhoneAccount.NO_HIGHLIGHT_COLOR) {
+ // The default background gradient has a subtle alpha. We grab that alpha and apply it to
+ // the phone account color.
+ backgroundColorTop = applyAlpha(palette.mPrimaryColor, backgroundColorTop);
+ backgroundColorMiddle = applyAlpha(palette.mPrimaryColor, backgroundColorMiddle);
+ backgroundColorBottom = applyAlpha(palette.mPrimaryColor, backgroundColorBottom);
+ backgroundColorSolid = applyAlpha(palette.mPrimaryColor, backgroundColorSolid);
+ }
+ }
+
+ primaryColor = palette.mPrimaryColor;
+ secondaryColor = palette.mSecondaryColor;
+ }
+
+ @ColorInt
+ private static int getHighlightColor(Context context, @Nullable PhoneAccountHandle handle) {
+ if (handle != null) {
+ PhoneAccount account = context.getSystemService(TelecomManager.class).getPhoneAccount(handle);
+ if (account != null) {
+ return account.getHighlightColor();
+ }
+ }
+ return PhoneAccount.NO_HIGHLIGHT_COLOR;
+ }
+
+ @ColorInt
+ public int getPrimaryColor() {
+ return primaryColor;
+ }
+
+ @ColorInt
+ public int getSecondaryColor() {
+ return secondaryColor;
+ }
+
+ @ColorInt
+ public int getBackgroundColorTop() {
+ return backgroundColorTop;
+ }
+
+ @ColorInt
+ public int getBackgroundColorMiddle() {
+ return backgroundColorMiddle;
+ }
+
+ @ColorInt
+ public int getBackgroundColorBottom() {
+ return backgroundColorBottom;
+ }
+
+ @ColorInt
+ public int getBackgroundColorSolid() {
+ return backgroundColorSolid;
+ }
+
+ @ColorInt
+ private static int applyAlpha(@ColorInt int color, @ColorInt int sourceColorWithAlpha) {
+ return ColorUtils.setAlphaComponent(color, Color.alpha(sourceColorWithAlpha));
+ }
+}
diff --git a/java/com/android/incallui/TransactionSafeFragmentActivity.java b/java/com/android/incallui/TransactionSafeFragmentActivity.java
new file mode 100644
index 000000000..a6b078cb4
--- /dev/null
+++ b/java/com/android/incallui/TransactionSafeFragmentActivity.java
@@ -0,0 +1,64 @@
+/*
+ * 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.incallui;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+
+/**
+ * A common superclass that keeps track of whether an {@link Activity} has saved its state yet or
+ * not.
+ */
+public abstract class TransactionSafeFragmentActivity extends FragmentActivity {
+
+ private boolean mIsSafeToCommitTransactions;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mIsSafeToCommitTransactions = true;
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mIsSafeToCommitTransactions = true;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mIsSafeToCommitTransactions = true;
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mIsSafeToCommitTransactions = false;
+ }
+
+ /**
+ * Returns true if it is safe to commit {@link FragmentTransaction}s at this time, based on
+ * whether {@link Activity#onSaveInstanceState} has been called or not.
+ *
+ * <p>Make sure that the current activity calls into {@link super.onSaveInstanceState(Bundle
+ * outState)} (if that method is overridden), so the flag is properly set.
+ */
+ public boolean isSafeToCommitTransactions() {
+ return mIsSafeToCommitTransactions;
+ }
+}
diff --git a/java/com/android/incallui/VideoCallPresenter.java b/java/com/android/incallui/VideoCallPresenter.java
new file mode 100644
index 000000000..971b6957a
--- /dev/null
+++ b/java/com/android/incallui/VideoCallPresenter.java
@@ -0,0 +1,1289 @@
+/*
+ * Copyright (C) 2014 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.incallui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Point;
+import android.os.Handler;
+import android.support.annotation.Nullable;
+import android.telecom.Connection;
+import android.telecom.InCallService.VideoCall;
+import android.telecom.VideoProfile;
+import android.telecom.VideoProfile.CameraCapabilities;
+import android.view.Surface;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.ConfigProviderBindings;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.compat.CompatUtils;
+import com.android.incallui.InCallPresenter.InCallDetailsListener;
+import com.android.incallui.InCallPresenter.InCallOrientationListener;
+import com.android.incallui.InCallPresenter.InCallStateListener;
+import com.android.incallui.InCallPresenter.IncomingCallListener;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.DialerCall.SessionModificationState;
+import com.android.incallui.call.DialerCall.State;
+import com.android.incallui.call.InCallVideoCallCallbackNotifier;
+import com.android.incallui.call.InCallVideoCallCallbackNotifier.SurfaceChangeListener;
+import com.android.incallui.call.InCallVideoCallCallbackNotifier.VideoEventListener;
+import com.android.incallui.call.VideoUtils;
+import com.android.incallui.util.AccessibilityUtil;
+import com.android.incallui.video.protocol.VideoCallScreen;
+import com.android.incallui.video.protocol.VideoCallScreenDelegate;
+import com.android.incallui.videosurface.protocol.VideoSurfaceDelegate;
+import com.android.incallui.videosurface.protocol.VideoSurfaceTexture;
+import java.util.Objects;
+
+/**
+ * Logic related to the {@link VideoCallScreen} and for managing changes to the video calling
+ * surfaces based on other user interface events and incoming events from the {@class
+ * VideoCallListener}.
+ *
+ * <p>When a call's video state changes to bi-directional video, the {@link
+ * com.android.incallui.VideoCallPresenter} performs the following negotiation with the telephony
+ * layer:
+ *
+ * <ul>
+ * <li>{@code VideoCallPresenter} creates and informs telephony of the display surface.
+ * <li>{@code VideoCallPresenter} creates the preview surface.
+ * <li>{@code VideoCallPresenter} informs telephony of the currently selected camera.
+ * <li>Telephony layer sends {@link CameraCapabilities}, including the dimensions of the video for
+ * the current camera.
+ * <li>{@code VideoCallPresenter} adjusts size of the preview surface to match the aspect ratio of
+ * the camera.
+ * <li>{@code VideoCallPresenter} informs telephony of the new preview surface.
+ * </ul>
+ *
+ * <p>When downgrading to an audio-only video state, the {@code VideoCallPresenter} nulls both
+ * surfaces.
+ */
+public class VideoCallPresenter
+ implements IncomingCallListener,
+ InCallOrientationListener,
+ InCallStateListener,
+ InCallDetailsListener,
+ SurfaceChangeListener,
+ VideoEventListener,
+ InCallPresenter.InCallEventListener,
+ VideoCallScreenDelegate {
+
+ private static boolean mIsVideoMode = false;
+
+ private final Handler mHandler = new Handler();
+ private VideoCallScreen mVideoCallScreen;
+
+ /** The current context. */
+ private Context mContext;
+
+ @Override
+ public boolean shouldShowCameraPermissionDialog() {
+ if (mPrimaryCall == null) {
+ LogUtil.i("VideoCallPresenter.shouldShowCameraPermissionDialog", "null call");
+ return false;
+ }
+ if (mPrimaryCall.didShowCameraPermission()) {
+ LogUtil.i(
+ "VideoCallPresenter.shouldShowCameraPermissionDialog", "already shown for this call");
+ return false;
+ }
+ if (!ConfigProviderBindings.get(mContext)
+ .getBoolean("camera_permission_dialog_allowed", true)) {
+ LogUtil.i("VideoCallPresenter.shouldShowCameraPermissionDialog", "disabled by config");
+ return false;
+ }
+ return !VideoUtils.hasCameraPermission(mContext) || !VideoUtils.isCameraAllowedByUser(mContext);
+ }
+
+ @Override
+ public void onCameraPermissionDialogShown() {
+ if (mPrimaryCall != null) {
+ mPrimaryCall.setDidShowCameraPermission(true);
+ }
+ }
+
+ /** The call the video surfaces are currently related to */
+ private DialerCall mPrimaryCall;
+ /**
+ * The {@link VideoCall} used to inform the video telephony layer of changes to the video
+ * surfaces.
+ */
+ private VideoCall mVideoCall;
+ /** Determines if the current UI state represents a video call. */
+ private int mCurrentVideoState;
+ /** DialerCall's current state */
+ private int mCurrentCallState = DialerCall.State.INVALID;
+ /** Determines the device orientation (portrait/lanscape). */
+ private int mDeviceOrientation = InCallOrientationEventListener.SCREEN_ORIENTATION_UNKNOWN;
+ /** Tracks the state of the preview surface negotiation with the telephony layer. */
+ private int mPreviewSurfaceState = PreviewSurfaceState.NONE;
+ /**
+ * Determines whether video calls should automatically enter full screen mode after {@link
+ * #mAutoFullscreenTimeoutMillis} milliseconds.
+ */
+ private boolean mIsAutoFullscreenEnabled = false;
+ /**
+ * Determines the number of milliseconds after which a video call will automatically enter
+ * fullscreen mode. Requires {@link #mIsAutoFullscreenEnabled} to be {@code true}.
+ */
+ private int mAutoFullscreenTimeoutMillis = 0;
+ /**
+ * Determines if the countdown is currently running to automatically enter full screen video mode.
+ */
+ private boolean mAutoFullScreenPending = false;
+ /** Whether if the call is remotely held. */
+ private boolean mIsRemotelyHeld = false;
+ /**
+ * Runnable which is posted to schedule automatically entering fullscreen mode. Will not auto
+ * enter fullscreen mode if the dialpad is visible (doing so would make it impossible to exit the
+ * dialpad).
+ */
+ private Runnable mAutoFullscreenRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mAutoFullScreenPending
+ && !InCallPresenter.getInstance().isDialpadVisible()
+ && mIsVideoMode) {
+
+ LogUtil.v("VideoCallPresenter.mAutoFullScreenRunnable", "entering fullscreen mode");
+ InCallPresenter.getInstance().setFullScreen(true);
+ mAutoFullScreenPending = false;
+ } else {
+ LogUtil.v(
+ "VideoCallPresenter.mAutoFullScreenRunnable",
+ "skipping scheduled fullscreen mode.");
+ }
+ }
+ };
+
+ private boolean isVideoCallScreenUiReady;
+
+ private static boolean isCameraRequired(int videoState, int sessionModificationState) {
+ return VideoProfile.isBidirectional(videoState)
+ || VideoProfile.isTransmissionEnabled(videoState)
+ || isVideoUpgrade(sessionModificationState);
+ }
+
+ /**
+ * Determines if the incoming video surface should be shown based on the current videoState and
+ * callState. The video surface is shown when incoming video is not paused, the call is active,
+ * and video reception is enabled.
+ *
+ * @param videoState The current video state.
+ * @param callState The current call state.
+ * @return {@code true} if the incoming video surface should be shown, {@code false} otherwise.
+ */
+ public static boolean showIncomingVideo(int videoState, int callState) {
+ if (!CompatUtils.isVideoCompatible()) {
+ return false;
+ }
+
+ boolean isPaused = VideoProfile.isPaused(videoState);
+ boolean isCallActive = callState == DialerCall.State.ACTIVE;
+
+ return !isPaused && isCallActive && VideoProfile.isReceptionEnabled(videoState);
+ }
+
+ /**
+ * Determines if the outgoing video surface should be shown based on the current videoState. The
+ * video surface is shown if video transmission is enabled.
+ *
+ * @return {@code true} if the the outgoing video surface should be shown, {@code false}
+ * otherwise.
+ */
+ public static boolean showOutgoingVideo(
+ Context context, int videoState, int sessionModificationState) {
+ if (!VideoUtils.hasCameraPermissionAndAllowedByUser(context)) {
+ LogUtil.i("VideoCallPresenter.showOutgoingVideo", "Camera permission is disabled by user.");
+ return false;
+ }
+
+ if (!CompatUtils.isVideoCompatible()) {
+ return false;
+ }
+
+ return VideoProfile.isTransmissionEnabled(videoState)
+ || isVideoUpgrade(sessionModificationState);
+ }
+
+ private static void updateCameraSelection(DialerCall call) {
+ LogUtil.v("VideoCallPresenter.updateCameraSelection", "call=" + call);
+ LogUtil.v("VideoCallPresenter.updateCameraSelection", "call=" + toSimpleString(call));
+
+ final DialerCall activeCall = CallList.getInstance().getActiveCall();
+ int cameraDir;
+
+ // this function should never be called with null call object, however if it happens we
+ // should handle it gracefully.
+ if (call == null) {
+ cameraDir = DialerCall.VideoSettings.CAMERA_DIRECTION_UNKNOWN;
+ LogUtil.e(
+ "VideoCallPresenter.updateCameraSelection",
+ "call is null. Setting camera direction to default value (CAMERA_DIRECTION_UNKNOWN)");
+ }
+
+ // Clear camera direction if this is not a video call.
+ else if (VideoUtils.isAudioCall(call) && !isVideoUpgrade(call)) {
+ cameraDir = DialerCall.VideoSettings.CAMERA_DIRECTION_UNKNOWN;
+ call.getVideoSettings().setCameraDir(cameraDir);
+ }
+
+ // If this is a waiting video call, default to active call's camera,
+ // since we don't want to change the current camera for waiting call
+ // without user's permission.
+ else if (VideoUtils.isVideoCall(activeCall) && VideoUtils.isIncomingVideoCall(call)) {
+ cameraDir = activeCall.getVideoSettings().getCameraDir();
+ }
+
+ // Infer the camera direction from the video state and store it,
+ // if this is an outgoing video call.
+ else if (VideoUtils.isOutgoingVideoCall(call) && !isCameraDirectionSet(call)) {
+ cameraDir = toCameraDirection(call.getVideoState());
+ call.getVideoSettings().setCameraDir(cameraDir);
+ }
+
+ // Use the stored camera dir if this is an outgoing video call for which camera direction
+ // is set.
+ else if (VideoUtils.isOutgoingVideoCall(call)) {
+ cameraDir = call.getVideoSettings().getCameraDir();
+ }
+
+ // Infer the camera direction from the video state and store it,
+ // if this is an active video call and camera direction is not set.
+ else if (VideoUtils.isActiveVideoCall(call) && !isCameraDirectionSet(call)) {
+ cameraDir = toCameraDirection(call.getVideoState());
+ call.getVideoSettings().setCameraDir(cameraDir);
+ }
+
+ // Use the stored camera dir if this is an active video call for which camera direction
+ // is set.
+ else if (VideoUtils.isActiveVideoCall(call)) {
+ cameraDir = call.getVideoSettings().getCameraDir();
+ }
+
+ // For all other cases infer the camera direction but don't store it in the call object.
+ else {
+ cameraDir = toCameraDirection(call.getVideoState());
+ }
+
+ LogUtil.i(
+ "VideoCallPresenter.updateCameraSelection",
+ "setting camera direction to %d, call: %s",
+ cameraDir,
+ call);
+ final InCallCameraManager cameraManager =
+ InCallPresenter.getInstance().getInCallCameraManager();
+ cameraManager.setUseFrontFacingCamera(
+ cameraDir == DialerCall.VideoSettings.CAMERA_DIRECTION_FRONT_FACING);
+ }
+
+ private static int toCameraDirection(int videoState) {
+ return VideoProfile.isTransmissionEnabled(videoState)
+ && !VideoProfile.isBidirectional(videoState)
+ ? DialerCall.VideoSettings.CAMERA_DIRECTION_BACK_FACING
+ : DialerCall.VideoSettings.CAMERA_DIRECTION_FRONT_FACING;
+ }
+
+ private static boolean isCameraDirectionSet(DialerCall call) {
+ return VideoUtils.isVideoCall(call)
+ && call.getVideoSettings().getCameraDir()
+ != DialerCall.VideoSettings.CAMERA_DIRECTION_UNKNOWN;
+ }
+
+ private static String toSimpleString(DialerCall call) {
+ return call == null ? null : call.toSimpleString();
+ }
+
+ /**
+ * Initializes the presenter.
+ *
+ * @param context The current context.
+ */
+ @Override
+ public void initVideoCallScreenDelegate(Context context, VideoCallScreen videoCallScreen) {
+ mContext = context;
+ mVideoCallScreen = videoCallScreen;
+ mIsAutoFullscreenEnabled =
+ mContext.getResources().getBoolean(R.bool.video_call_auto_fullscreen);
+ mAutoFullscreenTimeoutMillis =
+ mContext.getResources().getInteger(R.integer.video_call_auto_fullscreen_timeout);
+ }
+
+ /** Called when the user interface is ready to be used. */
+ @Override
+ public void onVideoCallScreenUiReady() {
+ LogUtil.v("VideoCallPresenter.onVideoCallScreenUiReady", "");
+ Assert.checkState(!isVideoCallScreenUiReady);
+
+ // Do not register any listeners if video calling is not compatible to safeguard against
+ // any accidental calls of video calling code.
+ if (!CompatUtils.isVideoCompatible()) {
+ return;
+ }
+
+ mDeviceOrientation = InCallOrientationEventListener.getCurrentOrientation();
+
+ // Register for call state changes last
+ InCallPresenter.getInstance().addListener(this);
+ InCallPresenter.getInstance().addDetailsListener(this);
+ InCallPresenter.getInstance().addIncomingCallListener(this);
+ InCallPresenter.getInstance().addOrientationListener(this);
+ // To get updates of video call details changes
+ InCallPresenter.getInstance().addInCallEventListener(this);
+ InCallPresenter.getInstance().getLocalVideoSurfaceTexture().setDelegate(new LocalDelegate());
+ InCallPresenter.getInstance().getRemoteVideoSurfaceTexture().setDelegate(new RemoteDelegate());
+
+ // Register for surface and video events from {@link InCallVideoCallListener}s.
+ InCallVideoCallCallbackNotifier.getInstance().addSurfaceChangeListener(this);
+ InCallVideoCallCallbackNotifier.getInstance().addVideoEventListener(this);
+ mCurrentVideoState = VideoProfile.STATE_AUDIO_ONLY;
+ mCurrentCallState = DialerCall.State.INVALID;
+
+ InCallPresenter.InCallState inCallState = InCallPresenter.getInstance().getInCallState();
+ onStateChange(inCallState, inCallState, CallList.getInstance());
+ isVideoCallScreenUiReady = true;
+ }
+
+ /** Called when the user interface is no longer ready to be used. */
+ @Override
+ public void onVideoCallScreenUiUnready() {
+ LogUtil.v("VideoCallPresenter.onVideoCallScreenUiUnready", "");
+ Assert.checkState(isVideoCallScreenUiReady);
+
+ if (!CompatUtils.isVideoCompatible()) {
+ return;
+ }
+
+ cancelAutoFullScreen();
+
+ InCallPresenter.getInstance().removeListener(this);
+ InCallPresenter.getInstance().removeDetailsListener(this);
+ InCallPresenter.getInstance().removeIncomingCallListener(this);
+ InCallPresenter.getInstance().removeOrientationListener(this);
+ InCallPresenter.getInstance().removeInCallEventListener(this);
+ InCallPresenter.getInstance().getLocalVideoSurfaceTexture().setDelegate(null);
+
+ InCallVideoCallCallbackNotifier.getInstance().removeSurfaceChangeListener(this);
+ InCallVideoCallCallbackNotifier.getInstance().removeVideoEventListener(this);
+
+ // Ensure that the call's camera direction is updated (most likely to UNKNOWN). Normally this
+ // happens after any call state changes but we're unregistering from InCallPresenter above so
+ // we won't get any more call state changes. See b/32957114.
+ if (mPrimaryCall != null) {
+ updateCameraSelection(mPrimaryCall);
+ }
+
+ isVideoCallScreenUiReady = false;
+ }
+
+ /**
+ * Handles clicks on the video surfaces. If not currently in fullscreen mode, will set fullscreen.
+ */
+ private void onSurfaceClick() {
+ LogUtil.i("VideoCallPresenter.onSurfaceClick", "");
+ cancelAutoFullScreen();
+ if (!InCallPresenter.getInstance().isFullscreen()) {
+ InCallPresenter.getInstance().setFullScreen(true);
+ } else {
+ InCallPresenter.getInstance().setFullScreen(false);
+ maybeAutoEnterFullscreen(mPrimaryCall);
+ // If Activity is not multiwindow, fullscreen will be driven by SystemUI visibility changes
+ // instead. See #onSystemUiVisibilityChange(boolean)
+
+ // TODO (keyboardr): onSystemUiVisibilityChange isn't being called the first time
+ // visibility changes after orientation change, so this is currently always done as a backup.
+ }
+ }
+
+ @Override
+ public void onSystemUiVisibilityChange(boolean visible) {
+ // If the SystemUI has changed to be visible, take us out of fullscreen mode
+ LogUtil.i("VideoCallPresenter.onSystemUiVisibilityChange", "visible: " + visible);
+ if (visible) {
+ InCallPresenter.getInstance().setFullScreen(false);
+ maybeAutoEnterFullscreen(mPrimaryCall);
+ }
+ }
+
+ @Override
+ public VideoSurfaceTexture getLocalVideoSurfaceTexture() {
+ return InCallPresenter.getInstance().getLocalVideoSurfaceTexture();
+ }
+
+ @Override
+ public VideoSurfaceTexture getRemoteVideoSurfaceTexture() {
+ return InCallPresenter.getInstance().getRemoteVideoSurfaceTexture();
+ }
+
+ @Override
+ public int getDeviceOrientation() {
+ return mDeviceOrientation;
+ }
+
+ /**
+ * This should only be called when user approved the camera permission, which is local action and
+ * does NOT change any call states.
+ */
+ @Override
+ public void onCameraPermissionGranted() {
+ LogUtil.i("VideoCallPresenter.onCameraPermissionGranted", "");
+ VideoUtils.setCameraAllowedByUser(mContext);
+ enableCamera(mPrimaryCall.getVideoCall(), isCameraRequired());
+ showVideoUi(
+ mPrimaryCall.getVideoState(),
+ mPrimaryCall.getState(),
+ mPrimaryCall.getSessionModificationState(),
+ mPrimaryCall.isRemotelyHeld());
+ InCallPresenter.getInstance().getInCallCameraManager().onCameraPermissionGranted();
+ }
+
+ /**
+ * Called when the user interacts with the UI. If a fullscreen timer is pending then we start the
+ * timer from scratch to avoid having the UI disappear while the user is interacting with it.
+ */
+ @Override
+ public void resetAutoFullscreenTimer() {
+ if (mAutoFullScreenPending) {
+ LogUtil.i("VideoCallPresenter.resetAutoFullscreenTimer", "resetting");
+ mHandler.removeCallbacks(mAutoFullscreenRunnable);
+ mHandler.postDelayed(mAutoFullscreenRunnable, mAutoFullscreenTimeoutMillis);
+ }
+ }
+
+ /**
+ * Handles incoming calls.
+ *
+ * @param oldState The old in call state.
+ * @param newState The new in call state.
+ * @param call The call.
+ */
+ @Override
+ public void onIncomingCall(
+ InCallPresenter.InCallState oldState, InCallPresenter.InCallState newState, DialerCall call) {
+ // same logic should happen as with onStateChange()
+ onStateChange(oldState, newState, CallList.getInstance());
+ }
+
+ /**
+ * Handles state changes (including incoming calls)
+ *
+ * @param newState The in call state.
+ * @param callList The call list.
+ */
+ @Override
+ public void onStateChange(
+ InCallPresenter.InCallState oldState,
+ InCallPresenter.InCallState newState,
+ CallList callList) {
+ LogUtil.v(
+ "VideoCallPresenter.onStateChange",
+ "oldState: %s, newState: %s, isVideoMode: %b",
+ oldState,
+ newState,
+ isVideoMode());
+
+ if (newState == InCallPresenter.InCallState.NO_CALLS) {
+ if (isVideoMode()) {
+ exitVideoMode();
+ }
+
+ InCallPresenter.getInstance().cleanupSurfaces();
+ }
+
+ // Determine the primary active call).
+ DialerCall primary = null;
+
+ // Determine the call which is the focus of the user's attention. In the case of an
+ // incoming call waiting call, the primary call is still the active video call, however
+ // the determination of whether we should be in fullscreen mode is based on the type of the
+ // incoming call, not the active video call.
+ DialerCall currentCall = null;
+
+ if (newState == InCallPresenter.InCallState.INCOMING) {
+ // We don't want to replace active video call (primary call)
+ // with a waiting call, since user may choose to ignore/decline the waiting call and
+ // this should have no impact on current active video call, that is, we should not
+ // change the camera or UI unless the waiting VT call becomes active.
+ primary = callList.getActiveCall();
+ currentCall = callList.getIncomingCall();
+ if (!VideoUtils.isActiveVideoCall(primary)) {
+ primary = callList.getIncomingCall();
+ }
+ } else if (newState == InCallPresenter.InCallState.OUTGOING) {
+ currentCall = primary = callList.getOutgoingCall();
+ } else if (newState == InCallPresenter.InCallState.PENDING_OUTGOING) {
+ currentCall = primary = callList.getPendingOutgoingCall();
+ } else if (newState == InCallPresenter.InCallState.INCALL) {
+ currentCall = primary = callList.getActiveCall();
+ }
+
+ final boolean primaryChanged = !Objects.equals(mPrimaryCall, primary);
+ LogUtil.i(
+ "VideoCallPresenter.onStateChange",
+ "primaryChanged: %b, primary: %s, mPrimaryCall: %s",
+ primaryChanged,
+ primary,
+ mPrimaryCall);
+ if (primaryChanged) {
+ onPrimaryCallChanged(primary);
+ } else if (mPrimaryCall != null) {
+ updateVideoCall(primary);
+ }
+ updateCallCache(primary);
+
+ // If the call context changed, potentially exit fullscreen or schedule auto enter of
+ // fullscreen mode.
+ // If the current call context is no longer a video call, exit fullscreen mode.
+ maybeExitFullscreen(currentCall);
+ // Schedule auto-enter of fullscreen mode if the current call context is a video call
+ maybeAutoEnterFullscreen(currentCall);
+ }
+
+ /**
+ * Handles a change to the fullscreen mode of the app.
+ *
+ * @param isFullscreenMode {@code true} if the app is now fullscreen, {@code false} otherwise.
+ */
+ @Override
+ public void onFullscreenModeChanged(boolean isFullscreenMode) {
+ cancelAutoFullScreen();
+ if (mPrimaryCall != null) {
+ updateFullscreenAndGreenScreenMode(
+ mPrimaryCall.getState(), mPrimaryCall.getSessionModificationState());
+ } else {
+ updateFullscreenAndGreenScreenMode(
+ State.INVALID, DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ }
+ }
+
+ private void checkForVideoStateChange(DialerCall call) {
+ final boolean shouldShowVideoUi = shouldShowVideoUiForCall(call);
+ final boolean hasVideoStateChanged = mCurrentVideoState != call.getVideoState();
+
+ LogUtil.v(
+ "VideoCallPresenter.checkForVideoStateChange",
+ "shouldShowVideoUi: %b, hasVideoStateChanged: %b, isVideoMode: %b, previousVideoState: %s,"
+ + " newVideoState: %s",
+ shouldShowVideoUi,
+ hasVideoStateChanged,
+ isVideoMode(),
+ VideoProfile.videoStateToString(mCurrentVideoState),
+ VideoProfile.videoStateToString(call.getVideoState()));
+ if (!hasVideoStateChanged) {
+ return;
+ }
+
+ updateCameraSelection(call);
+
+ if (shouldShowVideoUi) {
+ adjustVideoMode(call);
+ } else if (isVideoMode()) {
+ exitVideoMode();
+ }
+ }
+
+ private void checkForCallStateChange(DialerCall call) {
+ final boolean shouldShowVideoUi = shouldShowVideoUiForCall(call);
+ final boolean hasCallStateChanged =
+ mCurrentCallState != call.getState() || mIsRemotelyHeld != call.isRemotelyHeld();
+ mIsRemotelyHeld = call.isRemotelyHeld();
+
+ LogUtil.v(
+ "VideoCallPresenter.checkForCallStateChange",
+ "shouldShowVideoUi: %b, hasCallStateChanged: %b, isVideoMode: %b",
+ shouldShowVideoUi,
+ hasCallStateChanged,
+ isVideoMode());
+
+ if (!hasCallStateChanged) {
+ return;
+ }
+
+ if (shouldShowVideoUi) {
+ final InCallCameraManager cameraManager =
+ InCallPresenter.getInstance().getInCallCameraManager();
+
+ String prevCameraId = cameraManager.getActiveCameraId();
+ updateCameraSelection(call);
+ String newCameraId = cameraManager.getActiveCameraId();
+
+ if (!Objects.equals(prevCameraId, newCameraId) && VideoUtils.isActiveVideoCall(call)) {
+ enableCamera(call.getVideoCall(), true);
+ }
+ }
+
+ // Make sure we hide or show the video UI if needed.
+ showVideoUi(
+ call.getVideoState(),
+ call.getState(),
+ call.getSessionModificationState(),
+ call.isRemotelyHeld());
+ }
+
+ private void onPrimaryCallChanged(DialerCall newPrimaryCall) {
+ final boolean shouldShowVideoUi = shouldShowVideoUiForCall(newPrimaryCall);
+ final boolean isVideoMode = isVideoMode();
+
+ LogUtil.v(
+ "VideoCallPresenter.onPrimaryCallChanged",
+ "shouldShowVideoUi: %b, isVideoMode: %b",
+ shouldShowVideoUi,
+ isVideoMode);
+
+ if (!shouldShowVideoUi && isVideoMode) {
+ // Terminate video mode if new primary call is not a video call
+ // and we are currently in video mode.
+ LogUtil.i("VideoCallPresenter.onPrimaryCallChanged", "exiting video mode...");
+ exitVideoMode();
+ } else if (shouldShowVideoUi) {
+ LogUtil.i("VideoCallPresenter.onPrimaryCallChanged", "entering video mode...");
+
+ updateCameraSelection(newPrimaryCall);
+ adjustVideoMode(newPrimaryCall);
+ }
+ checkForOrientationAllowedChange(newPrimaryCall);
+ }
+
+ private boolean isVideoMode() {
+ return mIsVideoMode;
+ }
+
+ private void updateCallCache(DialerCall call) {
+ if (call == null) {
+ mCurrentVideoState = VideoProfile.STATE_AUDIO_ONLY;
+ mCurrentCallState = DialerCall.State.INVALID;
+ mVideoCall = null;
+ mPrimaryCall = null;
+ } else {
+ mCurrentVideoState = call.getVideoState();
+ mVideoCall = call.getVideoCall();
+ mCurrentCallState = call.getState();
+ mPrimaryCall = call;
+ }
+ }
+
+ /**
+ * Handles changes to the details of the call. The {@link VideoCallPresenter} is interested in
+ * changes to the video state.
+ *
+ * @param call The call for which the details changed.
+ * @param details The new call details.
+ */
+ @Override
+ public void onDetailsChanged(DialerCall call, android.telecom.Call.Details details) {
+ LogUtil.v(
+ "VideoCallPresenter.onDetailsChanged",
+ "call: %s, details: %s, mPrimaryCall: %s",
+ call,
+ details,
+ mPrimaryCall);
+ if (call == null) {
+ return;
+ }
+ // If the details change is not for the currently active call no update is required.
+ if (!call.equals(mPrimaryCall)) {
+ LogUtil.v("VideoCallPresenter.onDetailsChanged", "details not for current active call");
+ return;
+ }
+
+ updateVideoCall(call);
+
+ updateCallCache(call);
+ }
+
+ private void updateVideoCall(DialerCall call) {
+ checkForVideoCallChange(call);
+ checkForVideoStateChange(call);
+ checkForCallStateChange(call);
+ checkForOrientationAllowedChange(call);
+ updateFullscreenAndGreenScreenMode(call.getState(), call.getSessionModificationState());
+ }
+
+ private void checkForOrientationAllowedChange(@Nullable DialerCall call) {
+ InCallPresenter.getInstance()
+ .setInCallAllowsOrientationChange(VideoUtils.isVideoCall(call) || isVideoUpgrade(call));
+ }
+
+ private void updateFullscreenAndGreenScreenMode(
+ int callState, @SessionModificationState int sessionModificationState) {
+ if (mVideoCallScreen != null) {
+ boolean shouldShowFullscreen = InCallPresenter.getInstance().isFullscreen();
+ boolean shouldShowGreenScreen =
+ callState == State.DIALING
+ || callState == State.CONNECTING
+ || callState == State.INCOMING
+ || isVideoUpgrade(sessionModificationState);
+ mVideoCallScreen.updateFullscreenAndGreenScreenMode(
+ shouldShowFullscreen, shouldShowGreenScreen);
+ }
+ }
+
+ /** Checks for a change to the video call and changes it if required. */
+ private void checkForVideoCallChange(DialerCall call) {
+ final VideoCall videoCall = call.getVideoCall();
+ LogUtil.v(
+ "VideoCallPresenter.checkForVideoCallChange",
+ "videoCall: %s, mVideoCall: %s",
+ videoCall,
+ mVideoCall);
+ if (!Objects.equals(videoCall, mVideoCall)) {
+ changeVideoCall(call);
+ }
+ }
+
+ /**
+ * Handles a change to the video call. Sets the surfaces on the previous call to null and sets the
+ * surfaces on the new video call accordingly.
+ *
+ * @param call The new video call.
+ */
+ private void changeVideoCall(DialerCall call) {
+ final VideoCall videoCall = call == null ? null : call.getVideoCall();
+ LogUtil.i(
+ "VideoCallPresenter.changeVideoCall",
+ "videoCall: %s, mVideoCall: %s",
+ videoCall,
+ mVideoCall);
+ final boolean hasChanged = mVideoCall == null && videoCall != null;
+
+ mVideoCall = videoCall;
+ if (mVideoCall == null) {
+ LogUtil.v("VideoCallPresenter.changeVideoCall", "video call or primary call is null. Return");
+ return;
+ }
+
+ if (shouldShowVideoUiForCall(call) && hasChanged) {
+ adjustVideoMode(call);
+ }
+ }
+
+ private boolean isCameraRequired() {
+ return mPrimaryCall != null
+ && isCameraRequired(
+ mPrimaryCall.getVideoState(), mPrimaryCall.getSessionModificationState());
+ }
+
+ /**
+ * Adjusts the current video mode by setting up the preview and display surfaces as necessary.
+ * Expected to be called whenever the video state associated with a call changes (e.g. a user
+ * turns their camera on or off) to ensure the correct surfaces are shown/hidden. TODO: Need
+ * to adjust size and orientation of preview surface here.
+ */
+ private void adjustVideoMode(DialerCall call) {
+ VideoCall videoCall = call.getVideoCall();
+ int newVideoState = call.getVideoState();
+
+ LogUtil.i(
+ "VideoCallPresenter.adjustVideoMode",
+ "videoCall: %s, videoState: %d",
+ videoCall,
+ newVideoState);
+ if (mVideoCallScreen == null) {
+ LogUtil.e("VideoCallPresenter.adjustVideoMode", "error VideoCallScreen is null so returning");
+ return;
+ }
+
+ showVideoUi(
+ newVideoState, call.getState(), call.getSessionModificationState(), call.isRemotelyHeld());
+
+ // Communicate the current camera to telephony and make a request for the camera
+ // capabilities.
+ if (videoCall != null) {
+ Surface surface = getRemoteVideoSurfaceTexture().getSavedSurface();
+ if (surface != null) {
+ LogUtil.v(
+ "VideoCallPresenter.adjustVideoMode", "calling setDisplaySurface with: " + surface);
+ videoCall.setDisplaySurface(surface);
+ }
+
+ Assert.checkState(
+ mDeviceOrientation != InCallOrientationEventListener.SCREEN_ORIENTATION_UNKNOWN);
+ videoCall.setDeviceOrientation(mDeviceOrientation);
+ enableCamera(videoCall, isCameraRequired(newVideoState, call.getSessionModificationState()));
+ }
+ int previousVideoState = mCurrentVideoState;
+ mCurrentVideoState = newVideoState;
+ mIsVideoMode = true;
+
+ // adjustVideoMode may be called if we are already in a 1-way video state. In this case
+ // we do not want to trigger auto-fullscreen mode.
+ if (!VideoUtils.isVideoCall(previousVideoState) && VideoUtils.isVideoCall(newVideoState)) {
+ maybeAutoEnterFullscreen(call);
+ }
+ }
+
+ private static boolean shouldShowVideoUiForCall(@Nullable DialerCall call) {
+ if (call == null) {
+ return false;
+ }
+
+ if (VideoUtils.isVideoCall(call)) {
+ return true;
+ }
+
+ if (isVideoUpgrade(call)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private void enableCamera(VideoCall videoCall, boolean isCameraRequired) {
+ LogUtil.v(
+ "VideoCallPresenter.enableCamera",
+ "videoCall: %s, enabling: %b",
+ videoCall,
+ isCameraRequired);
+ if (videoCall == null) {
+ LogUtil.i("VideoCallPresenter.enableCamera", "videoCall is null.");
+ return;
+ }
+
+ boolean hasCameraPermission = VideoUtils.hasCameraPermissionAndAllowedByUser(mContext);
+ if (!hasCameraPermission) {
+ videoCall.setCamera(null);
+ mPreviewSurfaceState = PreviewSurfaceState.NONE;
+ // TODO: Inform remote party that the video is off. This is similar to b/30256571.
+ } else if (isCameraRequired) {
+ InCallCameraManager cameraManager = InCallPresenter.getInstance().getInCallCameraManager();
+ videoCall.setCamera(cameraManager.getActiveCameraId());
+ mPreviewSurfaceState = PreviewSurfaceState.CAMERA_SET;
+ videoCall.requestCameraCapabilities();
+ } else {
+ mPreviewSurfaceState = PreviewSurfaceState.NONE;
+ videoCall.setCamera(null);
+ }
+ }
+
+ /** Exits video mode by hiding the video surfaces and making other adjustments (eg. audio). */
+ private void exitVideoMode() {
+ LogUtil.i("VideoCallPresenter.exitVideoMode", "");
+
+ showVideoUi(
+ VideoProfile.STATE_AUDIO_ONLY,
+ DialerCall.State.ACTIVE,
+ DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST,
+ false /* isRemotelyHeld */);
+ enableCamera(mVideoCall, false);
+ InCallPresenter.getInstance().setFullScreen(false);
+
+ mIsVideoMode = false;
+ }
+
+ /**
+ * Based on the current video state and call state, show or hide the incoming and outgoing video
+ * surfaces. The outgoing video surface is shown any time video is transmitting. The incoming
+ * video surface is shown whenever the video is un-paused and active.
+ *
+ * @param videoState The video state.
+ * @param callState The call state.
+ */
+ private void showVideoUi(
+ int videoState,
+ int callState,
+ @SessionModificationState int sessionModificationState,
+ boolean isRemotelyHeld) {
+ if (mVideoCallScreen == null) {
+ LogUtil.e("VideoCallPresenter.showVideoUi", "videoCallScreen is null returning");
+ return;
+ }
+ boolean showIncomingVideo = showIncomingVideo(videoState, callState);
+ boolean showOutgoingVideo = showOutgoingVideo(mContext, videoState, sessionModificationState);
+ LogUtil.i(
+ "VideoCallPresenter.showVideoUi",
+ "showIncoming: %b, showOutgoing: %b, isRemotelyHeld: %b",
+ showIncomingVideo,
+ showOutgoingVideo,
+ isRemotelyHeld);
+ updateRemoteVideoSurfaceDimensions();
+ mVideoCallScreen.showVideoViews(showOutgoingVideo, showIncomingVideo, isRemotelyHeld);
+
+ InCallPresenter.getInstance().enableScreenTimeout(VideoProfile.isAudioOnly(videoState));
+ updateFullscreenAndGreenScreenMode(callState, sessionModificationState);
+ }
+
+ /**
+ * Handles peer video pause state changes.
+ *
+ * @param call The call which paused or un-pausedvideo transmission.
+ * @param paused {@code True} when the video transmission is paused, {@code false} when video
+ * transmission resumes.
+ */
+ @Override
+ public void onPeerPauseStateChanged(DialerCall call, boolean paused) {
+ if (!call.equals(mPrimaryCall)) {
+ return;
+ }
+ }
+
+ /**
+ * Handles peer video dimension changes.
+ *
+ * @param call The call which experienced a peer video dimension change.
+ * @param width The new peer video width .
+ * @param height The new peer video height.
+ */
+ @Override
+ public void onUpdatePeerDimensions(DialerCall call, int width, int height) {
+ LogUtil.i("VideoCallPresenter.onUpdatePeerDimensions", "width: %d, height: %d", width, height);
+ if (mVideoCallScreen == null) {
+ LogUtil.e("VideoCallPresenter.onUpdatePeerDimensions", "videoCallScreen is null");
+ return;
+ }
+ if (!call.equals(mPrimaryCall)) {
+ LogUtil.e(
+ "VideoCallPresenter.onUpdatePeerDimensions", "current call is not equal to primary");
+ return;
+ }
+
+ // Change size of display surface to match the peer aspect ratio
+ if (width > 0 && height > 0 && mVideoCallScreen != null) {
+ getRemoteVideoSurfaceTexture().setSourceVideoDimensions(new Point(width, height));
+ mVideoCallScreen.onRemoteVideoDimensionsChanged();
+ }
+ }
+
+ /**
+ * Handles any video quality changes in the call.
+ *
+ * @param call The call which experienced a video quality change.
+ * @param videoQuality The new video call quality.
+ */
+ @Override
+ public void onVideoQualityChanged(DialerCall call, int videoQuality) {
+ // No-op
+ }
+
+ /**
+ * Handles a change to the dimensions of the local camera. Receiving the camera capabilities
+ * triggers the creation of the video
+ *
+ * @param call The call which experienced the camera dimension change.
+ * @param width The new camera video width.
+ * @param height The new camera video height.
+ */
+ @Override
+ public void onCameraDimensionsChange(DialerCall call, int width, int height) {
+ LogUtil.i(
+ "VideoCallPresenter.onCameraDimensionsChange",
+ "call: %s, width: %d, height: %d",
+ call,
+ width,
+ height);
+ if (mVideoCallScreen == null) {
+ LogUtil.e("VideoCallPresenter.onCameraDimensionsChange", "ui is null");
+ return;
+ }
+
+ if (!call.equals(mPrimaryCall)) {
+ LogUtil.e("VideoCallPresenter.onCameraDimensionsChange", "not the primary call");
+ return;
+ }
+
+ mPreviewSurfaceState = PreviewSurfaceState.CAPABILITIES_RECEIVED;
+ changePreviewDimensions(width, height);
+
+ // Check if the preview surface is ready yet; if it is, set it on the {@code VideoCall}.
+ // If it not yet ready, it will be set when when creation completes.
+ Surface surface = getLocalVideoSurfaceTexture().getSavedSurface();
+ if (surface != null) {
+ mPreviewSurfaceState = PreviewSurfaceState.SURFACE_SET;
+ mVideoCall.setPreviewSurface(surface);
+ }
+ }
+
+ /**
+ * Changes the dimensions of the preview surface.
+ *
+ * @param width The new width.
+ * @param height The new height.
+ */
+ private void changePreviewDimensions(int width, int height) {
+ if (mVideoCallScreen == null) {
+ return;
+ }
+
+ // Resize the surface used to display the preview video
+ getLocalVideoSurfaceTexture().setSurfaceDimensions(new Point(width, height));
+ mVideoCallScreen.onLocalVideoDimensionsChanged();
+ }
+
+ /**
+ * Called when call session event is raised.
+ *
+ * @param event The call session event.
+ */
+ @Override
+ public void onCallSessionEvent(int event) {
+ switch (event) {
+ case Connection.VideoProvider.SESSION_EVENT_RX_PAUSE:
+ LogUtil.v("VideoCallPresenter.onCallSessionEvent", "rx_pause");
+ break;
+ case Connection.VideoProvider.SESSION_EVENT_RX_RESUME:
+ LogUtil.v("VideoCallPresenter.onCallSessionEvent", "rx_resume");
+ break;
+ case Connection.VideoProvider.SESSION_EVENT_CAMERA_FAILURE:
+ LogUtil.v("VideoCallPresenter.onCallSessionEvent", "camera_failure");
+ break;
+ case Connection.VideoProvider.SESSION_EVENT_CAMERA_READY:
+ LogUtil.v("VideoCallPresenter.onCallSessionEvent", "camera_ready");
+ break;
+ default:
+ LogUtil.v("VideoCallPresenter.onCallSessionEvent", "unknown event = : " + event);
+ break;
+ }
+ }
+
+ /**
+ * Handles a change to the call data usage
+ *
+ * @param dataUsage call data usage value
+ */
+ @Override
+ public void onCallDataUsageChange(long dataUsage) {
+ LogUtil.v("VideoCallPresenter.onCallDataUsageChange", "dataUsage=" + dataUsage);
+ }
+
+ /**
+ * Handles changes to the device orientation.
+ *
+ * @param orientation The screen orientation of the device (one of: {@link
+ * InCallOrientationEventListener#SCREEN_ORIENTATION_0}, {@link
+ * InCallOrientationEventListener#SCREEN_ORIENTATION_90}, {@link
+ * InCallOrientationEventListener#SCREEN_ORIENTATION_180}, {@link
+ * InCallOrientationEventListener#SCREEN_ORIENTATION_270}).
+ */
+ @Override
+ public void onDeviceOrientationChanged(int orientation) {
+ LogUtil.i(
+ "VideoCallPresenter.onDeviceOrientationChanged",
+ "orientation: %d -> %d",
+ mDeviceOrientation,
+ orientation);
+ mDeviceOrientation = orientation;
+
+ if (mVideoCallScreen == null) {
+ LogUtil.e("VideoCallPresenter.onDeviceOrientationChanged", "videoCallScreen is null");
+ return;
+ }
+
+ Point previewDimensions = getLocalVideoSurfaceTexture().getSurfaceDimensions();
+ if (previewDimensions == null) {
+ return;
+ }
+ LogUtil.v(
+ "VideoCallPresenter.onDeviceOrientationChanged",
+ "orientation: %d, size: %s",
+ orientation,
+ previewDimensions);
+ changePreviewDimensions(previewDimensions.x, previewDimensions.y);
+
+ mVideoCallScreen.onLocalVideoOrientationChanged();
+ }
+
+ /**
+ * Exits fullscreen mode if the current call context has changed to a non-video call.
+ *
+ * @param call The call.
+ */
+ protected void maybeExitFullscreen(DialerCall call) {
+ if (call == null) {
+ return;
+ }
+
+ if (!VideoUtils.isVideoCall(call) || call.getState() == DialerCall.State.INCOMING) {
+ LogUtil.i("VideoCallPresenter.maybeExitFullscreen", "exiting fullscreen");
+ InCallPresenter.getInstance().setFullScreen(false);
+ }
+ }
+
+ /**
+ * Schedules auto-entering of fullscreen mode. Will not enter full screen mode if any of the
+ * following conditions are met: 1. No call 2. DialerCall is not active 3. The current video state
+ * is not bi-directional. 4. Already in fullscreen mode 5. In accessibility mode
+ *
+ * @param call The current call.
+ */
+ protected void maybeAutoEnterFullscreen(DialerCall call) {
+ if (!mIsAutoFullscreenEnabled) {
+ return;
+ }
+
+ if (call == null
+ || call.getState() != DialerCall.State.ACTIVE
+ || !VideoUtils.isBidirectionalVideoCall(call)
+ || InCallPresenter.getInstance().isFullscreen()
+ || (mContext != null && AccessibilityUtil.isTouchExplorationEnabled(mContext))) {
+ // Ensure any previously scheduled attempt to enter fullscreen is cancelled.
+ cancelAutoFullScreen();
+ return;
+ }
+
+ if (mAutoFullScreenPending) {
+ LogUtil.v("VideoCallPresenter.maybeAutoEnterFullscreen", "already pending.");
+ return;
+ }
+ LogUtil.v("VideoCallPresenter.maybeAutoEnterFullscreen", "scheduled");
+ mAutoFullScreenPending = true;
+ mHandler.removeCallbacks(mAutoFullscreenRunnable);
+ mHandler.postDelayed(mAutoFullscreenRunnable, mAutoFullscreenTimeoutMillis);
+ }
+
+ /** Cancels pending auto fullscreen mode. */
+ @Override
+ public void cancelAutoFullScreen() {
+ if (!mAutoFullScreenPending) {
+ LogUtil.v("VideoCallPresenter.cancelAutoFullScreen", "none pending.");
+ return;
+ }
+ LogUtil.v("VideoCallPresenter.cancelAutoFullScreen", "cancelling pending");
+ mAutoFullScreenPending = false;
+ mHandler.removeCallbacks(mAutoFullscreenRunnable);
+ }
+
+ private void updateRemoteVideoSurfaceDimensions() {
+ Activity activity = mVideoCallScreen.getVideoCallScreenFragment().getActivity();
+ if (activity != null) {
+ Point screenSize = new Point();
+ activity.getWindowManager().getDefaultDisplay().getSize(screenSize);
+ getRemoteVideoSurfaceTexture().setSurfaceDimensions(screenSize);
+ }
+ }
+
+ private static boolean isVideoUpgrade(DialerCall call) {
+ return VideoUtils.hasSentVideoUpgradeRequest(call)
+ || VideoUtils.hasReceivedVideoUpgradeRequest(call);
+ }
+
+ private static boolean isVideoUpgrade(@SessionModificationState int state) {
+ return VideoUtils.hasSentVideoUpgradeRequest(state)
+ || VideoUtils.hasReceivedVideoUpgradeRequest(state);
+ }
+
+ private class LocalDelegate implements VideoSurfaceDelegate {
+ @Override
+ public void onSurfaceCreated(VideoSurfaceTexture videoCallSurface) {
+ if (mVideoCallScreen == null) {
+ LogUtil.e("VideoCallPresenter.LocalDelegate.onSurfaceCreated", "no UI");
+ return;
+ }
+ if (mVideoCall == null) {
+ LogUtil.e("VideoCallPresenter.LocalDelegate.onSurfaceCreated", "no video call");
+ return;
+ }
+
+ // If the preview surface has just been created and we have already received camera
+ // capabilities, but not yet set the surface, we will set the surface now.
+ if (mPreviewSurfaceState == PreviewSurfaceState.CAPABILITIES_RECEIVED) {
+ mPreviewSurfaceState = PreviewSurfaceState.SURFACE_SET;
+ mVideoCall.setPreviewSurface(videoCallSurface.getSavedSurface());
+ } else if (mPreviewSurfaceState == PreviewSurfaceState.NONE && isCameraRequired()) {
+ enableCamera(mVideoCall, true);
+ }
+ }
+
+ @Override
+ public void onSurfaceReleased(VideoSurfaceTexture videoCallSurface) {
+ if (mVideoCall == null) {
+ LogUtil.e("VideoCallPresenter.LocalDelegate.onSurfaceReleased", "no video call");
+ return;
+ }
+
+ mVideoCall.setPreviewSurface(null);
+ enableCamera(mVideoCall, false);
+ }
+
+ @Override
+ public void onSurfaceDestroyed(VideoSurfaceTexture videoCallSurface) {
+ if (mVideoCall == null) {
+ LogUtil.e("VideoCallPresenter.LocalDelegate.onSurfaceDestroyed", "no video call");
+ return;
+ }
+
+ boolean isChangingConfigurations = InCallPresenter.getInstance().isChangingConfigurations();
+ if (!isChangingConfigurations) {
+ enableCamera(mVideoCall, false);
+ } else {
+ LogUtil.i(
+ "VideoCallPresenter.LocalDelegate.onSurfaceDestroyed",
+ "activity is being destroyed due to configuration changes. Not closing the camera.");
+ }
+ }
+
+ @Override
+ public void onSurfaceClick(VideoSurfaceTexture videoCallSurface) {
+ VideoCallPresenter.this.onSurfaceClick();
+ }
+ }
+
+ private class RemoteDelegate implements VideoSurfaceDelegate {
+ @Override
+ public void onSurfaceCreated(VideoSurfaceTexture videoCallSurface) {
+ if (mVideoCallScreen == null) {
+ LogUtil.e("VideoCallPresenter.RemoteDelegate.onSurfaceCreated", "no UI");
+ return;
+ }
+ if (mVideoCall == null) {
+ LogUtil.e("VideoCallPresenter.RemoteDelegate.onSurfaceCreated", "no video call");
+ return;
+ }
+ mVideoCall.setDisplaySurface(videoCallSurface.getSavedSurface());
+ }
+
+ @Override
+ public void onSurfaceReleased(VideoSurfaceTexture videoCallSurface) {
+ if (mVideoCall == null) {
+ LogUtil.e("VideoCallPresenter.RemoteDelegate.onSurfaceReleased", "no video call");
+ return;
+ }
+ mVideoCall.setDisplaySurface(null);
+ }
+
+ @Override
+ public void onSurfaceDestroyed(VideoSurfaceTexture videoCallSurface) {}
+
+ @Override
+ public void onSurfaceClick(VideoSurfaceTexture videoCallSurface) {
+ VideoCallPresenter.this.onSurfaceClick();
+ }
+ }
+
+ /** Defines the state of the preview surface negotiation with the telephony layer. */
+ private static class PreviewSurfaceState {
+
+ /**
+ * The camera has not yet been set on the {@link VideoCall}; negotiation has not yet started.
+ */
+ private static final int NONE = 0;
+
+ /**
+ * The camera has been set on the {@link VideoCall}, but camera capabilities have not yet been
+ * received.
+ */
+ private static final int CAMERA_SET = 1;
+
+ /**
+ * The camera capabilties have been received from telephony, but the surface has not yet been
+ * set on the {@link VideoCall}.
+ */
+ private static final int CAPABILITIES_RECEIVED = 2;
+
+ /** The surface has been set on the {@link VideoCall}. */
+ private static final int SURFACE_SET = 3;
+ }
+}
diff --git a/java/com/android/incallui/VideoPauseController.java b/java/com/android/incallui/VideoPauseController.java
new file mode 100644
index 000000000..2b4357704
--- /dev/null
+++ b/java/com/android/incallui/VideoPauseController.java
@@ -0,0 +1,416 @@
+/*
+ * 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.incallui;
+
+import android.support.annotation.NonNull;
+import android.telecom.VideoProfile;
+import com.android.incallui.InCallPresenter.InCallState;
+import com.android.incallui.InCallPresenter.InCallStateListener;
+import com.android.incallui.InCallPresenter.IncomingCallListener;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.DialerCall.State;
+import com.android.incallui.call.VideoUtils;
+import java.util.Objects;
+
+/**
+ * This class is responsible for generating video pause/resume requests when the InCall UI is sent
+ * to the background and subsequently brought back to the foreground.
+ */
+class VideoPauseController implements InCallStateListener, IncomingCallListener {
+
+ private static final String TAG = "VideoPauseController";
+ private static VideoPauseController sVideoPauseController;
+ private InCallPresenter mInCallPresenter;
+ /** The current call context, if applicable. */
+ private CallContext mPrimaryCallContext = null;
+ /**
+ * Tracks whether the application is in the background. {@code True} if the application is in the
+ * background, {@code false} otherwise.
+ */
+ private boolean mIsInBackground = false;
+
+ /**
+ * Singleton accessor for the {@link VideoPauseController}.
+ *
+ * @return Singleton instance of the {@link VideoPauseController}.
+ */
+ /*package*/
+ static synchronized VideoPauseController getInstance() {
+ if (sVideoPauseController == null) {
+ sVideoPauseController = new VideoPauseController();
+ }
+ return sVideoPauseController;
+ }
+
+ /**
+ * Determines if a given call is the same one stored in a {@link CallContext}.
+ *
+ * @param call The call.
+ * @param callContext The call context.
+ * @return {@code true} if the {@link DialerCall} is the same as the one referenced in the {@link
+ * CallContext}.
+ */
+ private static boolean areSame(DialerCall call, CallContext callContext) {
+ if (call == null && callContext == null) {
+ return true;
+ } else if (call == null || callContext == null) {
+ return false;
+ }
+ return call.equals(callContext.getCall());
+ }
+
+ /**
+ * Determines if a video call can be paused. Only a video call which is active can be paused.
+ *
+ * @param callContext The call context to check.
+ * @return {@code true} if the call is an active video call.
+ */
+ private static boolean canVideoPause(CallContext callContext) {
+ return isVideoCall(callContext) && callContext.getState() == DialerCall.State.ACTIVE;
+ }
+
+ /**
+ * Determines if a call referenced by a {@link CallContext} is a video call.
+ *
+ * @param callContext The call context.
+ * @return {@code true} if the call is a video call, {@code false} otherwise.
+ */
+ private static boolean isVideoCall(CallContext callContext) {
+ return callContext != null && VideoUtils.isVideoCall(callContext.getVideoState());
+ }
+
+ /**
+ * Determines if call is in incoming/waiting state.
+ *
+ * @param call The call context.
+ * @return {@code true} if the call is in incoming or waiting state, {@code false} otherwise.
+ */
+ private static boolean isIncomingCall(CallContext call) {
+ return call != null && isIncomingCall(call.getCall());
+ }
+
+ /**
+ * Determines if a call is in incoming/waiting state.
+ *
+ * @param call The call.
+ * @return {@code true} if the call is in incoming or waiting state, {@code false} otherwise.
+ */
+ private static boolean isIncomingCall(DialerCall call) {
+ return call != null
+ && (call.getState() == DialerCall.State.CALL_WAITING
+ || call.getState() == DialerCall.State.INCOMING);
+ }
+
+ /**
+ * Determines if a call is dialing.
+ *
+ * @param call The call context.
+ * @return {@code true} if the call is dialing, {@code false} otherwise.
+ */
+ private static boolean isDialing(CallContext call) {
+ return call != null && DialerCall.State.isDialing(call.getState());
+ }
+
+ /**
+ * Configures the {@link VideoPauseController} to listen to call events. Configured via the {@link
+ * com.android.incallui.InCallPresenter}.
+ *
+ * @param inCallPresenter The {@link com.android.incallui.InCallPresenter}.
+ */
+ public void setUp(@NonNull InCallPresenter inCallPresenter) {
+ log("setUp");
+ mInCallPresenter = Objects.requireNonNull(inCallPresenter);
+ mInCallPresenter.addListener(this);
+ mInCallPresenter.addIncomingCallListener(this);
+ }
+
+ /**
+ * Cleans up the {@link VideoPauseController} by removing all listeners and clearing its internal
+ * state. Called from {@link com.android.incallui.InCallPresenter}.
+ */
+ public void tearDown() {
+ log("tearDown...");
+ mInCallPresenter.removeListener(this);
+ mInCallPresenter.removeIncomingCallListener(this);
+ clear();
+ }
+
+ /** Clears the internal state for the {@link VideoPauseController}. */
+ private void clear() {
+ mInCallPresenter = null;
+ mPrimaryCallContext = null;
+ mIsInBackground = false;
+ }
+
+ /**
+ * Handles changes in the {@link InCallState}. Triggers pause and resumption of video for the
+ * current foreground call.
+ *
+ * @param oldState The previous {@link InCallState}.
+ * @param newState The current {@link InCallState}.
+ * @param callList List of current call.
+ */
+ @Override
+ public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
+ log("onStateChange, OldState=" + oldState + " NewState=" + newState);
+
+ DialerCall call;
+ if (newState == InCallState.INCOMING) {
+ call = callList.getIncomingCall();
+ } else if (newState == InCallState.WAITING_FOR_ACCOUNT) {
+ call = callList.getWaitingForAccountCall();
+ } else if (newState == InCallState.PENDING_OUTGOING) {
+ call = callList.getPendingOutgoingCall();
+ } else if (newState == InCallState.OUTGOING) {
+ call = callList.getOutgoingCall();
+ } else {
+ call = callList.getActiveCall();
+ }
+
+ boolean hasPrimaryCallChanged = !areSame(call, mPrimaryCallContext);
+ boolean canVideoPause = VideoUtils.canVideoPause(call);
+ log("onStateChange, hasPrimaryCallChanged=" + hasPrimaryCallChanged);
+ log("onStateChange, canVideoPause=" + canVideoPause);
+ log("onStateChange, IsInBackground=" + mIsInBackground);
+
+ if (hasPrimaryCallChanged) {
+ onPrimaryCallChanged(call);
+ return;
+ }
+
+ if (isDialing(mPrimaryCallContext) && canVideoPause && mIsInBackground) {
+ // Bring UI to foreground if outgoing request becomes active while UI is in
+ // background.
+ bringToForeground();
+ } else if (!isVideoCall(mPrimaryCallContext) && canVideoPause && mIsInBackground) {
+ // Bring UI to foreground if VoLTE call becomes active while UI is in
+ // background.
+ bringToForeground();
+ }
+
+ updatePrimaryCallContext(call);
+ }
+
+ /**
+ * Handles a change to the primary call.
+ *
+ * <p>Reject incoming or hangup dialing call: Where the previous call was an incoming call or a
+ * call in dialing state, resume the new primary call. DialerCall swap: Where the new primary call
+ * is incoming, pause video on the previous primary call.
+ *
+ * @param call The new primary call.
+ */
+ private void onPrimaryCallChanged(DialerCall call) {
+ log("onPrimaryCallChanged: New call = " + call);
+ log("onPrimaryCallChanged: Old call = " + mPrimaryCallContext);
+ log("onPrimaryCallChanged, IsInBackground=" + mIsInBackground);
+
+ if (areSame(call, mPrimaryCallContext)) {
+ throw new IllegalStateException();
+ }
+ final boolean canVideoPause = VideoUtils.canVideoPause(call);
+
+ if ((isIncomingCall(mPrimaryCallContext)
+ || isDialing(mPrimaryCallContext)
+ || (call != null && VideoProfile.isPaused(call.getVideoState())))
+ && canVideoPause
+ && !mIsInBackground) {
+ // Send resume request for the active call, if user rejects incoming call, ends dialing
+ // call, or the call was previously in a paused state and UI is in the foreground.
+ sendRequest(call, true);
+ } else if (isIncomingCall(call) && canVideoPause(mPrimaryCallContext)) {
+ // Send pause request if there is an active video call, and we just received a new
+ // incoming call.
+ sendRequest(mPrimaryCallContext.getCall(), false);
+ }
+
+ updatePrimaryCallContext(call);
+ }
+
+ /**
+ * Handles new incoming calls by triggering a change in the primary call.
+ *
+ * @param oldState the old {@link InCallState}.
+ * @param newState the new {@link InCallState}.
+ * @param call the incoming call.
+ */
+ @Override
+ public void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call) {
+ log("onIncomingCall, OldState=" + oldState + " NewState=" + newState + " DialerCall=" + call);
+
+ if (areSame(call, mPrimaryCallContext)) {
+ return;
+ }
+
+ onPrimaryCallChanged(call);
+ }
+
+ /**
+ * Caches a reference to the primary call and stores its previous state.
+ *
+ * @param call The new primary call.
+ */
+ private void updatePrimaryCallContext(DialerCall call) {
+ if (call == null) {
+ mPrimaryCallContext = null;
+ } else if (mPrimaryCallContext != null) {
+ mPrimaryCallContext.update(call);
+ } else {
+ mPrimaryCallContext = new CallContext(call);
+ }
+ }
+
+ /**
+ * Called when UI goes in/out of the foreground.
+ *
+ * @param showing true if UI is in the foreground, false otherwise.
+ */
+ public void onUiShowing(boolean showing) {
+ if (mInCallPresenter == null) {
+ return;
+ }
+
+ final boolean isInCall = mInCallPresenter.getInCallState() == InCallState.INCALL;
+ if (showing) {
+ onResume(isInCall);
+ } else {
+ onPause(isInCall);
+ }
+ }
+
+ /**
+ * Called when UI is brought to the foreground. Sends a session modification request to resume the
+ * outgoing video.
+ *
+ * @param isInCall {@code true} if we are in an active call. A resume request is only sent to the
+ * video provider if we are in a call.
+ */
+ private void onResume(boolean isInCall) {
+ log("onResume");
+
+ mIsInBackground = false;
+ if (canVideoPause(mPrimaryCallContext) && isInCall) {
+ sendRequest(mPrimaryCallContext.getCall(), true);
+ } else {
+ log("onResume. Ignoring...");
+ }
+ }
+
+ /**
+ * Called when UI is sent to the background. Sends a session modification request to pause the
+ * outgoing video.
+ *
+ * @param isInCall {@code true} if we are in an active call. A pause request is only sent to the
+ * video provider if we are in a call.
+ */
+ private void onPause(boolean isInCall) {
+ log("onPause");
+
+ mIsInBackground = true;
+ if (canVideoPause(mPrimaryCallContext) && isInCall) {
+ sendRequest(mPrimaryCallContext.getCall(), false);
+ } else {
+ log("onPause, Ignoring...");
+ }
+ }
+
+ private void bringToForeground() {
+ if (mInCallPresenter != null) {
+ log("Bringing UI to foreground");
+ mInCallPresenter.bringToForeground(false);
+ } else {
+ loge("InCallPresenter is null. Cannot bring UI to foreground");
+ }
+ }
+
+ /**
+ * Sends Pause/Resume request.
+ *
+ * @param call DialerCall to be paused/resumed.
+ * @param resume If true resume request will be sent, otherwise pause request.
+ */
+ private void sendRequest(DialerCall call, boolean resume) {
+ // Check if this call supports pause/un-pause.
+ if (!call.can(android.telecom.Call.Details.CAPABILITY_CAN_PAUSE_VIDEO)) {
+ return;
+ }
+
+ if (resume) {
+ log("sending resume request, call=" + call);
+ call.getVideoCall().sendSessionModifyRequest(VideoUtils.makeVideoUnPauseProfile(call));
+ } else {
+ log("sending pause request, call=" + call);
+ call.getVideoCall().sendSessionModifyRequest(VideoUtils.makeVideoPauseProfile(call));
+ }
+ }
+
+ /**
+ * Logs a debug message.
+ *
+ * @param msg The message.
+ */
+ private void log(String msg) {
+ Log.d(this, TAG + msg);
+ }
+
+ /**
+ * Logs an error message.
+ *
+ * @param msg The message.
+ */
+ private void loge(String msg) {
+ Log.e(this, TAG + msg);
+ }
+
+ /** Keeps track of the current active/foreground call. */
+ private static class CallContext {
+
+ private int mState = State.INVALID;
+ private int mVideoState;
+ private DialerCall mCall;
+
+ public CallContext(@NonNull DialerCall call) {
+ Objects.requireNonNull(call);
+ update(call);
+ }
+
+ public void update(@NonNull DialerCall call) {
+ mCall = Objects.requireNonNull(call);
+ mState = call.getState();
+ mVideoState = call.getVideoState();
+ }
+
+ public int getState() {
+ return mState;
+ }
+
+ public int getVideoState() {
+ return mVideoState;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "CallContext {CallId=%s, State=%s, VideoState=%d}", mCall.getId(), mState, mVideoState);
+ }
+
+ public DialerCall getCall() {
+ return mCall;
+ }
+ }
+}
diff --git a/java/com/android/incallui/answer/bindings/AnswerBindings.java b/java/com/android/incallui/answer/bindings/AnswerBindings.java
new file mode 100644
index 000000000..f7a7a0a95
--- /dev/null
+++ b/java/com/android/incallui/answer/bindings/AnswerBindings.java
@@ -0,0 +1,29 @@
+/*
+ * 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.incallui.answer.bindings;
+
+import com.android.incallui.answer.impl.AnswerFragment;
+import com.android.incallui.answer.protocol.AnswerScreen;
+
+/** Bindings for answer module. */
+public class AnswerBindings {
+
+ public static AnswerScreen createAnswerScreen(
+ String callId, int videoState, boolean isVideoUpgradeRequest) {
+ return AnswerFragment.newInstance(callId, videoState, isVideoUpgradeRequest);
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/AffordanceHolderLayout.java b/java/com/android/incallui/answer/impl/AffordanceHolderLayout.java
new file mode 100644
index 000000000..0f93abe68
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/AffordanceHolderLayout.java
@@ -0,0 +1,178 @@
+/*
+ * 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.incallui.answer.impl;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+import com.android.incallui.answer.impl.affordance.SwipeButtonHelper;
+import com.android.incallui.answer.impl.affordance.SwipeButtonHelper.Callback;
+import com.android.incallui.answer.impl.affordance.SwipeButtonView;
+import com.android.incallui.util.AccessibilityUtil;
+
+/** Layout that delegates touches to its SwipeButtonHelper */
+public class AffordanceHolderLayout extends FrameLayout {
+
+ private SwipeButtonHelper affordanceHelper;
+
+ private Callback affordanceCallback;
+
+ public AffordanceHolderLayout(Context context) {
+ this(context, null);
+ }
+
+ public AffordanceHolderLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AffordanceHolderLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ affordanceHelper =
+ new SwipeButtonHelper(
+ new Callback() {
+ @Override
+ public void onAnimationToSideStarted(
+ boolean rightPage, float translation, float vel) {
+ if (affordanceCallback != null) {
+ affordanceCallback.onAnimationToSideStarted(rightPage, translation, vel);
+ }
+ }
+
+ @Override
+ public void onAnimationToSideEnded() {
+ if (affordanceCallback != null) {
+ affordanceCallback.onAnimationToSideEnded();
+ }
+ }
+
+ @Override
+ public float getMaxTranslationDistance() {
+ if (affordanceCallback != null) {
+ return affordanceCallback.getMaxTranslationDistance();
+ }
+ return 0;
+ }
+
+ @Override
+ public void onSwipingStarted(boolean rightIcon) {
+ if (affordanceCallback != null) {
+ affordanceCallback.onSwipingStarted(rightIcon);
+ }
+ }
+
+ @Override
+ public void onSwipingAborted() {
+ if (affordanceCallback != null) {
+ affordanceCallback.onSwipingAborted();
+ }
+ }
+
+ @Override
+ public void onIconClicked(boolean rightIcon) {
+ if (affordanceCallback != null) {
+ affordanceCallback.onIconClicked(rightIcon);
+ }
+ }
+
+ @Nullable
+ @Override
+ public SwipeButtonView getLeftIcon() {
+ if (affordanceCallback != null) {
+ return affordanceCallback.getLeftIcon();
+ }
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public SwipeButtonView getRightIcon() {
+ if (affordanceCallback != null) {
+ return affordanceCallback.getRightIcon();
+ }
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public View getLeftPreview() {
+ if (affordanceCallback != null) {
+ return affordanceCallback.getLeftPreview();
+ }
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public View getRightPreview() {
+ if (affordanceCallback != null) {
+ affordanceCallback.getRightPreview();
+ }
+ return null;
+ }
+
+ @Override
+ public float getAffordanceFalsingFactor() {
+ if (affordanceCallback != null) {
+ return affordanceCallback.getAffordanceFalsingFactor();
+ }
+ return 1.0f;
+ }
+ },
+ context);
+ }
+
+ public void setAffordanceCallback(@Nullable Callback callback) {
+ affordanceCallback = callback;
+ affordanceHelper.init();
+ }
+
+ public void startHintAnimation(boolean rightIcon, @Nullable Runnable onFinishListener) {
+ affordanceHelper.startHintAnimation(rightIcon, onFinishListener);
+ }
+
+ public void animateHideLeftRightIcon() {
+ affordanceHelper.animateHideLeftRightIcon();
+ }
+
+ public void reset(boolean animate) {
+ affordanceHelper.reset(animate);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (AccessibilityUtil.isTouchExplorationEnabled(getContext())) {
+ return false;
+ }
+ return affordanceHelper.onTouchEvent(event) || super.onInterceptTouchEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return affordanceHelper.onTouchEvent(event) || super.onTouchEvent(event);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ affordanceHelper.onConfigurationChanged();
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/AndroidManifest.xml b/java/com/android/incallui/answer/impl/AndroidManifest.xml
new file mode 100644
index 000000000..482c716db
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.incallui.answer.impl">
+</manifest>
diff --git a/java/com/android/incallui/answer/impl/AnswerFragment.java b/java/com/android/incallui/answer/impl/AnswerFragment.java
new file mode 100644
index 000000000..98439ee7f
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/AnswerFragment.java
@@ -0,0 +1,981 @@
+/*
+ * 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.incallui.answer.impl;
+
+import android.Manifest.permission;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.location.Location;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.FloatRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.support.annotation.VisibleForTesting;
+import android.support.transition.TransitionManager;
+import android.support.v4.app.Fragment;
+import android.telecom.VideoProfile;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.widget.ImageView;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.FragmentUtils;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.MathUtil;
+import com.android.dialer.compat.ActivityCompat;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.multimedia.MultimediaData;
+import com.android.dialer.util.ViewUtil;
+import com.android.incallui.answer.impl.CreateCustomSmsDialogFragment.CreateCustomSmsHolder;
+import com.android.incallui.answer.impl.SmsBottomSheetFragment.SmsSheetHolder;
+import com.android.incallui.answer.impl.affordance.SwipeButtonHelper.Callback;
+import com.android.incallui.answer.impl.affordance.SwipeButtonView;
+import com.android.incallui.answer.impl.answermethod.AnswerMethod;
+import com.android.incallui.answer.impl.answermethod.AnswerMethodFactory;
+import com.android.incallui.answer.impl.answermethod.AnswerMethodHolder;
+import com.android.incallui.answer.impl.utils.Interpolators;
+import com.android.incallui.answer.protocol.AnswerScreen;
+import com.android.incallui.answer.protocol.AnswerScreenDelegate;
+import com.android.incallui.answer.protocol.AnswerScreenDelegateFactory;
+import com.android.incallui.call.DialerCall.State;
+import com.android.incallui.call.VideoUtils;
+import com.android.incallui.contactgrid.ContactGridManager;
+import com.android.incallui.incall.protocol.ContactPhotoType;
+import com.android.incallui.incall.protocol.InCallScreen;
+import com.android.incallui.incall.protocol.InCallScreenDelegate;
+import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
+import com.android.incallui.incall.protocol.PrimaryCallState;
+import com.android.incallui.incall.protocol.PrimaryInfo;
+import com.android.incallui.incall.protocol.SecondaryInfo;
+import com.android.incallui.maps.StaticMapBinding;
+import com.android.incallui.sessiondata.AvatarPresenter;
+import com.android.incallui.sessiondata.MultimediaFragment;
+import com.android.incallui.util.AccessibilityUtil;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/** The new version of the incoming call screen. */
+@SuppressLint("ClickableViewAccessibility")
+public class AnswerFragment extends Fragment
+ implements AnswerScreen,
+ InCallScreen,
+ SmsSheetHolder,
+ CreateCustomSmsHolder,
+ AnswerMethodHolder,
+ MultimediaFragment.Holder {
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ static final String ARG_CALL_ID = "call_id";
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ static final String ARG_VIDEO_STATE = "video_state";
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ static final String ARG_IS_VIDEO_UPGRADE_REQUEST = "is_video_upgrade_request";
+
+ private static final String STATE_HAS_ANIMATED_ENTRY = "hasAnimated";
+
+ private static final int HINT_SECONDARY_SHOW_DURATION_MILLIS = 5000;
+ private static final float ANIMATE_LERP_PROGRESS = 0.5f;
+ private static final int STATUS_BAR_DISABLE_RECENT = 0x01000000;
+ private static final int STATUS_BAR_DISABLE_HOME = 0x00200000;
+ private static final int STATUS_BAR_DISABLE_BACK = 0x00400000;
+
+ private static void fadeToward(View view, float newAlpha) {
+ view.setAlpha(MathUtil.lerp(view.getAlpha(), newAlpha, ANIMATE_LERP_PROGRESS));
+ }
+
+ private static void scaleToward(View view, float newScale) {
+ view.setScaleX(MathUtil.lerp(view.getScaleX(), newScale, ANIMATE_LERP_PROGRESS));
+ view.setScaleY(MathUtil.lerp(view.getScaleY(), newScale, ANIMATE_LERP_PROGRESS));
+ }
+
+ private AnswerScreenDelegate answerScreenDelegate;
+ private InCallScreenDelegate inCallScreenDelegate;
+
+ private View importanceBadge;
+ private SwipeButtonView secondaryButton;
+ private AffordanceHolderLayout affordanceHolderLayout;
+ // Use these flags to prevent user from clicking accept/reject buttons multiple times.
+ // We use separate flags because in some rare cases accepting a call may fail to join the room,
+ // and then user is stuck in the incoming call view until it times out. Two flags at least give
+ // the user a chance to get out of the CallActivity.
+ private boolean buttonAcceptClicked;
+ private boolean buttonRejectClicked;
+ private boolean hasAnimatedEntry;
+ private PrimaryInfo primaryInfo = PrimaryInfo.createEmptyPrimaryInfo();
+ private PrimaryCallState primaryCallState;
+ private ArrayList<CharSequence> textResponses;
+ private SmsBottomSheetFragment textResponsesFragment;
+ private CreateCustomSmsDialogFragment createCustomSmsDialogFragment;
+ private SecondaryBehavior secondaryBehavior = SecondaryBehavior.REJECT_WITH_SMS;
+ private ContactGridManager contactGridManager;
+ private AnswerVideoCallScreen answerVideoCallScreen;
+ private Handler handler = new Handler(Looper.getMainLooper());
+
+ private enum SecondaryBehavior {
+ REJECT_WITH_SMS(
+ R.drawable.quantum_ic_message_white_24,
+ R.string.a11y_description_incoming_call_reject_with_sms,
+ R.string.a11y_incoming_call_reject_with_sms,
+ R.string.call_incoming_swipe_to_decline_with_message) {
+ @Override
+ public void performAction(AnswerFragment fragment) {
+ fragment.showMessageMenu();
+ }
+ },
+
+ ANSWER_VIDEO_AS_AUDIO(
+ R.drawable.quantum_ic_videocam_off_white_24,
+ R.string.a11y_description_incoming_call_answer_video_as_audio,
+ R.string.a11y_incoming_call_answer_video_as_audio,
+ R.string.call_incoming_swipe_to_answer_video_as_audio) {
+ @Override
+ public void performAction(AnswerFragment fragment) {
+ fragment.acceptCallByUser(true /* answerVideoAsAudio */);
+ }
+ };
+
+ @DrawableRes public final int icon;
+ @StringRes public final int contentDescription;
+ @StringRes public final int accessibilityLabel;
+ @StringRes public final int hintText;
+
+ SecondaryBehavior(
+ @DrawableRes int icon,
+ @StringRes int contentDescription,
+ @StringRes int accessibilityLabel,
+ @StringRes int hintText) {
+ this.icon = icon;
+ this.contentDescription = contentDescription;
+ this.accessibilityLabel = accessibilityLabel;
+ this.hintText = hintText;
+ }
+
+ public abstract void performAction(AnswerFragment fragment);
+
+ public void applyToView(ImageView view) {
+ view.setImageResource(icon);
+ view.setContentDescription(view.getContext().getText(contentDescription));
+ }
+ }
+
+ private AccessibilityDelegate accessibilityDelegate =
+ new AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ if (host == secondaryButton) {
+ CharSequence label = getText(secondaryBehavior.accessibilityLabel);
+ info.addAction(new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, label));
+ }
+ }
+
+ @Override
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ if (action == AccessibilityNodeInfo.ACTION_CLICK) {
+ if (host == secondaryButton) {
+ performSecondaryButtonAction();
+ return true;
+ }
+ }
+ return super.performAccessibilityAction(host, action, args);
+ }
+ };
+
+ private Callback affordanceCallback =
+ new Callback() {
+ @Override
+ public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) {}
+
+ @Override
+ public void onAnimationToSideEnded() {
+ performSecondaryButtonAction();
+ }
+
+ @Override
+ public float getMaxTranslationDistance() {
+ View view = getView();
+ if (view == null) {
+ return 0;
+ }
+ return (float) Math.hypot(view.getWidth(), view.getHeight());
+ }
+
+ @Override
+ public void onSwipingStarted(boolean rightIcon) {}
+
+ @Override
+ public void onSwipingAborted() {}
+
+ @Override
+ public void onIconClicked(boolean rightIcon) {
+ affordanceHolderLayout.startHintAnimation(rightIcon, null);
+ getAnswerMethod().setHintText(getText(secondaryBehavior.hintText));
+ handler.removeCallbacks(swipeHintRestoreTimer);
+ handler.postDelayed(swipeHintRestoreTimer, HINT_SECONDARY_SHOW_DURATION_MILLIS);
+ }
+
+ @Override
+ public SwipeButtonView getLeftIcon() {
+ return secondaryButton;
+ }
+
+ @Override
+ public SwipeButtonView getRightIcon() {
+ return null;
+ }
+
+ @Override
+ public View getLeftPreview() {
+ return null;
+ }
+
+ @Override
+ public View getRightPreview() {
+ return null;
+ }
+
+ @Override
+ public float getAffordanceFalsingFactor() {
+ return 1.0f;
+ }
+ };
+
+ private Runnable swipeHintRestoreTimer =
+ new Runnable() {
+ @Override
+ public void run() {
+ restoreSwipeHintTexts();
+ }
+ };
+
+ private void performSecondaryButtonAction() {
+ secondaryBehavior.performAction(this);
+ }
+
+ public static AnswerFragment newInstance(
+ String callId, int videoState, boolean isVideoUpgradeRequest) {
+ Bundle bundle = new Bundle();
+ bundle.putString(ARG_CALL_ID, Assert.isNotNull(callId));
+ bundle.putInt(ARG_VIDEO_STATE, videoState);
+ bundle.putBoolean(ARG_IS_VIDEO_UPGRADE_REQUEST, isVideoUpgradeRequest);
+
+ AnswerFragment instance = new AnswerFragment();
+ instance.setArguments(bundle);
+ return instance;
+ }
+
+ @Override
+ @NonNull
+ public String getCallId() {
+ return Assert.isNotNull(getArguments().getString(ARG_CALL_ID));
+ }
+
+ @Override
+ public int getVideoState() {
+ return getArguments().getInt(ARG_VIDEO_STATE);
+ }
+
+ @Override
+ public boolean isVideoUpgradeRequest() {
+ return getArguments().getBoolean(ARG_IS_VIDEO_UPGRADE_REQUEST);
+ }
+
+ @Override
+ public void setTextResponses(List<String> textResponses) {
+ if (isVideoCall()) {
+ LogUtil.i("AnswerFragment.setTextResponses", "no-op for video calls");
+ } else if (textResponses == null) {
+ LogUtil.i("AnswerFragment.setTextResponses", "no text responses, hiding secondary button");
+ this.textResponses = null;
+ secondaryButton.setVisibility(View.INVISIBLE);
+ } else if (ActivityCompat.isInMultiWindowMode(getActivity())) {
+ LogUtil.i("AnswerFragment.setTextResponses", "in multiwindow, hiding secondary button");
+ this.textResponses = null;
+ secondaryButton.setVisibility(View.INVISIBLE);
+ } else {
+ LogUtil.i("AnswerFragment.setTextResponses", "textResponses.size: " + textResponses.size());
+ this.textResponses = new ArrayList<CharSequence>(textResponses);
+ secondaryButton.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void initSecondaryButton() {
+ secondaryBehavior =
+ isVideoCall() ? SecondaryBehavior.ANSWER_VIDEO_AS_AUDIO : SecondaryBehavior.REJECT_WITH_SMS;
+ secondaryBehavior.applyToView(secondaryButton);
+
+ secondaryButton.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ performSecondaryButtonAction();
+ }
+ });
+ secondaryButton.setClickable(AccessibilityUtil.isAccessibilityEnabled(getContext()));
+ secondaryButton.setFocusable(AccessibilityUtil.isAccessibilityEnabled(getContext()));
+ secondaryButton.setAccessibilityDelegate(accessibilityDelegate);
+
+ if (isVideoCall()) {
+ //noinspection WrongConstant
+ if (!isVideoUpgradeRequest() && VideoProfile.isTransmissionEnabled(getVideoState())) {
+ secondaryButton.setVisibility(View.VISIBLE);
+ } else {
+ secondaryButton.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
+ @Override
+ public boolean hasPendingDialogs() {
+ boolean hasPendingDialogs =
+ textResponsesFragment != null || createCustomSmsDialogFragment != null;
+ LogUtil.i("AnswerFragment.hasPendingDialogs", "" + hasPendingDialogs);
+ return hasPendingDialogs;
+ }
+
+ @Override
+ public void dismissPendingDialogs() {
+ LogUtil.i("AnswerFragment.dismissPendingDialogs", null);
+ if (textResponsesFragment != null) {
+ textResponsesFragment.dismiss();
+ textResponsesFragment = null;
+ }
+
+ if (createCustomSmsDialogFragment != null) {
+ createCustomSmsDialogFragment.dismiss();
+ createCustomSmsDialogFragment = null;
+ }
+ }
+
+ @Override
+ public boolean isShowingLocationUi() {
+ Fragment fragment = getChildFragmentManager().findFragmentById(R.id.incall_location_holder);
+ return fragment != null && fragment.isVisible();
+ }
+
+ @Override
+ public void showLocationUi(@Nullable Fragment locationUi) {
+ boolean isShowing = isShowingLocationUi();
+ if (!isShowing && locationUi != null) {
+ // Show the location fragment.
+ getChildFragmentManager()
+ .beginTransaction()
+ .replace(R.id.incall_location_holder, locationUi)
+ .commitAllowingStateLoss();
+ } else if (isShowing && locationUi == null) {
+ // Hide the location fragment
+ Fragment fragment = getChildFragmentManager().findFragmentById(R.id.incall_location_holder);
+ getChildFragmentManager().beginTransaction().remove(fragment).commitAllowingStateLoss();
+ }
+ }
+
+ @Override
+ public Fragment getAnswerScreenFragment() {
+ return this;
+ }
+
+ private AnswerMethod getAnswerMethod() {
+ return ((AnswerMethod)
+ getChildFragmentManager().findFragmentById(R.id.answer_method_container));
+ }
+
+ @Override
+ public void setPrimary(PrimaryInfo primaryInfo) {
+ LogUtil.i("AnswerFragment.setPrimary", primaryInfo.toString());
+ this.primaryInfo = primaryInfo;
+ updatePrimaryUI();
+ updateImportanceBadgeVisibility();
+ }
+
+ private void updatePrimaryUI() {
+ if (getView() == null) {
+ return;
+ }
+ contactGridManager.setPrimary(primaryInfo);
+ getAnswerMethod().setShowIncomingWillDisconnect(primaryInfo.answeringDisconnectsOngoingCall);
+ getAnswerMethod()
+ .setContactPhoto(
+ primaryInfo.photoType == ContactPhotoType.CONTACT ? primaryInfo.photo : null);
+ updateDataFragment();
+
+ if (primaryInfo.shouldShowLocation) {
+ // Hide the avatar to make room for location
+ contactGridManager.setAvatarHidden(true);
+ }
+ }
+
+ private void updateDataFragment() {
+ if (!isAdded()) {
+ return;
+ }
+ Fragment current = getChildFragmentManager().findFragmentById(R.id.incall_data_container);
+ Fragment newFragment = null;
+
+ MultimediaData multimediaData = getSessionData();
+ if (multimediaData != null
+ && (!TextUtils.isEmpty(multimediaData.getSubject())
+ || (multimediaData.getImageUri() != null)
+ || (multimediaData.getLocation() != null && canShowMap()))) {
+ // Need message fragment
+ String subject = multimediaData.getSubject();
+ Uri imageUri = multimediaData.getImageUri();
+ Location location = multimediaData.getLocation();
+ if (!(current instanceof MultimediaFragment)
+ || !Objects.equals(((MultimediaFragment) current).getSubject(), subject)
+ || !Objects.equals(((MultimediaFragment) current).getImageUri(), imageUri)
+ || !Objects.equals(((MultimediaFragment) current).getLocation(), location)) {
+ // Needs replacement
+ newFragment =
+ MultimediaFragment.newInstance(
+ multimediaData, false /* isInteractive */, true /* showAvatar */);
+ }
+ } else if (shouldShowAvatar()) {
+ // Needs Avatar
+ if (!(current instanceof AvatarFragment)) {
+ // Needs replacement
+ newFragment = new AvatarFragment();
+ }
+ } else {
+ // Needs empty
+ if (current != null) {
+ getChildFragmentManager().beginTransaction().remove(current).commitNow();
+ }
+ contactGridManager.setAvatarImageView(null, 0, false);
+ }
+
+ if (newFragment != null) {
+ getChildFragmentManager()
+ .beginTransaction()
+ .replace(R.id.incall_data_container, newFragment)
+ .commitNow();
+ }
+ }
+
+ private boolean shouldShowAvatar() {
+ return !isVideoCall();
+ }
+
+ private boolean canShowMap() {
+ return StaticMapBinding.get(getActivity().getApplication()) != null;
+ }
+
+ @Override
+ public void updateAvatar(AvatarPresenter avatarContainer) {
+ contactGridManager.setAvatarImageView(
+ avatarContainer.getAvatarImageView(),
+ avatarContainer.getAvatarSize(),
+ avatarContainer.shouldShowAnonymousAvatar());
+ }
+
+ @Override
+ public void setSecondary(@NonNull SecondaryInfo secondaryInfo) {}
+
+ @Override
+ public void setCallState(@NonNull PrimaryCallState primaryCallState) {
+ LogUtil.i("AnswerFragment.setCallState", primaryCallState.toString());
+ this.primaryCallState = primaryCallState;
+ contactGridManager.setCallState(primaryCallState);
+ }
+
+ @Override
+ public void setEndCallButtonEnabled(boolean enabled, boolean animate) {}
+
+ @Override
+ public void showManageConferenceCallButton(boolean visible) {}
+
+ @Override
+ public boolean isManageConferenceVisible() {
+ return false;
+ }
+
+ @Override
+ public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ contactGridManager.dispatchPopulateAccessibilityEvent(event);
+ // Add prompt of how to accept/decline call with swipe gesture.
+ if (AccessibilityUtil.isTouchExplorationEnabled(getContext())) {
+ event
+ .getText()
+ .add(getResources().getString(R.string.a11y_incoming_call_swipe_gesture_prompt));
+ }
+ }
+
+ @Override
+ public void showNoteSentToast() {}
+
+ @Override
+ public void updateInCallScreenColors() {}
+
+ @Override
+ public void onInCallScreenDialpadVisibilityChange(boolean isShowing) {}
+
+ @Override
+ public int getAnswerAndDialpadContainerResourceId() {
+ Assert.fail();
+ return 0;
+ }
+
+ @Override
+ public Fragment getInCallScreenFragment() {
+ return this;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ Bundle arguments = getArguments();
+ Assert.checkState(arguments.containsKey(ARG_CALL_ID));
+ Assert.checkState(arguments.containsKey(ARG_VIDEO_STATE));
+ Assert.checkState(arguments.containsKey(ARG_IS_VIDEO_UPGRADE_REQUEST));
+
+ buttonAcceptClicked = false;
+ buttonRejectClicked = false;
+
+ View view = inflater.inflate(R.layout.fragment_incoming_call, container, false);
+ secondaryButton = (SwipeButtonView) view.findViewById(R.id.incoming_secondary_button);
+
+ affordanceHolderLayout = (AffordanceHolderLayout) view.findViewById(R.id.incoming_container);
+ affordanceHolderLayout.setAffordanceCallback(affordanceCallback);
+
+ importanceBadge = view.findViewById(R.id.incall_important_call_badge);
+ PillDrawable importanceBackground = new PillDrawable();
+ importanceBackground.setColor(getContext().getColor(android.R.color.white));
+ importanceBadge.setBackground(importanceBackground);
+ importanceBadge
+ .getViewTreeObserver()
+ .addOnGlobalLayoutListener(
+ new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ int leftRightPadding = importanceBadge.getHeight() / 2;
+ importanceBadge.setPadding(
+ leftRightPadding,
+ importanceBadge.getPaddingTop(),
+ leftRightPadding,
+ importanceBadge.getPaddingBottom());
+ }
+ });
+ updateImportanceBadgeVisibility();
+
+ boolean isVideoCall = isVideoCall();
+ contactGridManager = new ContactGridManager(view, null, 0, false /* showAnonymousAvatar */);
+
+ Fragment answerMethod =
+ getChildFragmentManager().findFragmentById(R.id.answer_method_container);
+ if (AnswerMethodFactory.needsReplacement(answerMethod)) {
+ getChildFragmentManager()
+ .beginTransaction()
+ .replace(
+ R.id.answer_method_container, AnswerMethodFactory.createAnswerMethod(getActivity()))
+ .commitNow();
+ }
+
+ answerScreenDelegate =
+ FragmentUtils.getParentUnsafe(this, AnswerScreenDelegateFactory.class)
+ .newAnswerScreenDelegate(this);
+
+ initSecondaryButton();
+
+ int flags = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+ if (!ActivityCompat.isInMultiWindowMode(getActivity())
+ && (getActivity().checkSelfPermission(permission.STATUS_BAR)
+ == PackageManager.PERMISSION_GRANTED)) {
+ LogUtil.i("AnswerFragment.onCreateView", "STATUS_BAR permission granted, disabling nav bar");
+ // These flags will suppress the alert that the activity is in full view mode
+ // during an incoming call on a fresh system/factory reset of the app
+ flags |= STATUS_BAR_DISABLE_BACK | STATUS_BAR_DISABLE_HOME | STATUS_BAR_DISABLE_RECENT;
+ }
+ view.setSystemUiVisibility(flags);
+ if (isVideoCall) {
+ if (VideoUtils.hasCameraPermissionAndAllowedByUser(getContext())) {
+ answerVideoCallScreen = new AnswerVideoCallScreen(this, view);
+ } else {
+ view.findViewById(R.id.videocall_video_off).setVisibility(View.VISIBLE);
+ }
+ }
+
+ return view;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ FragmentUtils.checkParent(this, InCallScreenDelegateFactory.class);
+ }
+
+ @Override
+ public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ createInCallScreenDelegate();
+ updateUI();
+
+ if (savedInstanceState == null || !savedInstanceState.getBoolean(STATE_HAS_ANIMATED_ENTRY)) {
+ ViewUtil.doOnPreDraw(view, false, this::animateEntry);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ LogUtil.i("AnswerFragment.onResume", null);
+ inCallScreenDelegate.onInCallScreenResumed();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ LogUtil.i("AnswerFragment.onStart", null);
+
+ updateUI();
+ if (answerVideoCallScreen != null) {
+ answerVideoCallScreen.onStart();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ LogUtil.i("AnswerFragment.onStop", null);
+
+ handler.removeCallbacks(swipeHintRestoreTimer);
+ if (answerVideoCallScreen != null) {
+ answerVideoCallScreen.onStop();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ LogUtil.i("AnswerFragment.onPause", null);
+ }
+
+ @Override
+ public void onDestroyView() {
+ LogUtil.i("AnswerFragment.onDestroyView", null);
+ if (answerVideoCallScreen != null) {
+ answerVideoCallScreen = null;
+ }
+ super.onDestroyView();
+ inCallScreenDelegate.onInCallScreenUnready();
+ answerScreenDelegate.onAnswerScreenUnready();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle bundle) {
+ super.onSaveInstanceState(bundle);
+ bundle.putBoolean(STATE_HAS_ANIMATED_ENTRY, hasAnimatedEntry);
+ }
+
+ private void updateUI() {
+ if (getView() == null) {
+ return;
+ }
+
+ if (primaryInfo != null) {
+ updatePrimaryUI();
+ }
+ if (primaryCallState != null) {
+ contactGridManager.setCallState(primaryCallState);
+ }
+
+ restoreBackgroundMaskColor();
+ }
+
+ @Override
+ public boolean isVideoCall() {
+ return VideoUtils.isVideoCall(getVideoState());
+ }
+
+ @Override
+ public void onAnswerProgressUpdate(@FloatRange(from = -1f, to = 1f) float answerProgress) {
+ // Don't fade the window background for call waiting or video upgrades. Fading the background
+ // shows the system wallpaper which looks bad because on reject we switch to another call.
+ if (primaryCallState.state == State.INCOMING && !isVideoCall()) {
+ answerScreenDelegate.updateWindowBackgroundColor(answerProgress);
+ }
+
+ // Fade and scale contact name and video call text
+ float startDelay = .25f;
+ // Header progress is zero over positiveAdjustedProgress = [0, startDelay],
+ // linearly increases over (startDelay, 1] until reaching 1 when positiveAdjustedProgress = 1
+ float headerProgress = Math.max(0, (Math.abs(answerProgress) - 1) / (1 - startDelay) + 1);
+ fadeToward(contactGridManager.getContainerView(), 1 - headerProgress);
+ scaleToward(contactGridManager.getContainerView(), MathUtil.lerp(1f, .75f, headerProgress));
+
+ if (Math.abs(answerProgress) >= .0001) {
+ affordanceHolderLayout.animateHideLeftRightIcon();
+ handler.removeCallbacks(swipeHintRestoreTimer);
+ restoreSwipeHintTexts();
+ }
+ }
+
+ @Override
+ public void answerFromMethod() {
+ acceptCallByUser(false /* answerVideoAsAudio */);
+ }
+
+ @Override
+ public void rejectFromMethod() {
+ rejectCall();
+ }
+
+ @Override
+ public void resetAnswerProgress() {
+ affordanceHolderLayout.reset(true);
+ restoreBackgroundMaskColor();
+ }
+
+ private void animateEntry(@NonNull View rootView) {
+ contactGridManager.getContainerView().setAlpha(0f);
+ Animator alpha =
+ ObjectAnimator.ofFloat(contactGridManager.getContainerView(), View.ALPHA, 0, 1);
+ Animator topRow = createTranslation(rootView.findViewById(R.id.contactgrid_top_row));
+ Animator contactName = createTranslation(rootView.findViewById(R.id.contactgrid_contact_name));
+ Animator bottomRow = createTranslation(rootView.findViewById(R.id.contactgrid_bottom_row));
+ Animator important = createTranslation(importanceBadge);
+ Animator dataContainer = createTranslation(rootView.findViewById(R.id.incall_data_container));
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet
+ .play(alpha)
+ .with(topRow)
+ .with(contactName)
+ .with(bottomRow)
+ .with(important)
+ .with(dataContainer);
+ animatorSet.setDuration(getResources().getInteger(android.R.integer.config_longAnimTime));
+ animatorSet.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ hasAnimatedEntry = true;
+ }
+ });
+ animatorSet.start();
+ }
+
+ private ObjectAnimator createTranslation(View view) {
+ float translationY = view.getTop() * 0.5f;
+ ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, translationY, 0);
+ animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ return animator;
+ }
+
+ private void acceptCallByUser(boolean answerVideoAsAudio) {
+ LogUtil.i("AnswerFragment.acceptCallByUser", answerVideoAsAudio ? " answerVideoAsAudio" : "");
+ if (!buttonAcceptClicked) {
+ int desiredVideoState = getVideoState();
+ if (answerVideoAsAudio) {
+ desiredVideoState = VideoProfile.STATE_AUDIO_ONLY;
+ }
+
+ // Notify the lower layer first to start signaling ASAP.
+ answerScreenDelegate.onAnswer(desiredVideoState);
+
+ buttonAcceptClicked = true;
+ }
+ }
+
+ private void rejectCall() {
+ LogUtil.i("AnswerFragment.rejectCall", null);
+ if (!buttonRejectClicked) {
+ Context context = getContext();
+ if (context == null) {
+ LogUtil.w(
+ "AnswerFragment.rejectCall",
+ "Null context when rejecting call. Logger call was skipped");
+ } else {
+ Logger.get(context)
+ .logImpression(DialerImpression.Type.REJECT_INCOMING_CALL_FROM_ANSWER_SCREEN);
+ }
+ buttonRejectClicked = true;
+ answerScreenDelegate.onReject();
+ }
+ }
+
+ private void restoreBackgroundMaskColor() {
+ answerScreenDelegate.updateWindowBackgroundColor(0);
+ }
+
+ private void restoreSwipeHintTexts() {
+ if (getAnswerMethod() != null) {
+ getAnswerMethod().setHintText(null);
+ }
+ }
+
+ private void showMessageMenu() {
+ LogUtil.i("AnswerFragment.showMessageMenu", "Show sms menu.");
+
+ textResponsesFragment = SmsBottomSheetFragment.newInstance(textResponses);
+ textResponsesFragment.show(getChildFragmentManager(), null);
+ secondaryButton
+ .animate()
+ .alpha(0)
+ .withEndAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ affordanceHolderLayout.reset(false);
+ secondaryButton.animate().alpha(1);
+ }
+ });
+ }
+
+ @Override
+ public void smsSelected(@Nullable CharSequence text) {
+ LogUtil.i("AnswerFragment.smsSelected", null);
+ textResponsesFragment = null;
+
+ if (text == null) {
+ createCustomSmsDialogFragment = CreateCustomSmsDialogFragment.newInstance();
+ createCustomSmsDialogFragment.show(getChildFragmentManager(), null);
+ return;
+ }
+
+ if (primaryCallState != null && canRejectCallWithSms()) {
+ rejectCall();
+ answerScreenDelegate.onRejectCallWithMessage(text.toString());
+ }
+ }
+
+ @Override
+ public void smsDismissed() {
+ LogUtil.i("AnswerFragment.smsDismissed", null);
+ textResponsesFragment = null;
+ answerScreenDelegate.onDismissDialog();
+ }
+
+ @Override
+ public void customSmsCreated(@NonNull CharSequence text) {
+ LogUtil.i("AnswerFragment.customSmsCreated", null);
+ createCustomSmsDialogFragment = null;
+ if (primaryCallState != null && canRejectCallWithSms()) {
+ rejectCall();
+ answerScreenDelegate.onRejectCallWithMessage(text.toString());
+ }
+ }
+
+ @Override
+ public void customSmsDismissed() {
+ LogUtil.i("AnswerFragment.customSmsDismissed", null);
+ createCustomSmsDialogFragment = null;
+ answerScreenDelegate.onDismissDialog();
+ }
+
+ private boolean canRejectCallWithSms() {
+ return primaryCallState != null
+ && !(primaryCallState.state == State.DISCONNECTED
+ || primaryCallState.state == State.DISCONNECTING
+ || primaryCallState.state == State.IDLE);
+ }
+
+ private void createInCallScreenDelegate() {
+ inCallScreenDelegate =
+ FragmentUtils.getParentUnsafe(this, InCallScreenDelegateFactory.class)
+ .newInCallScreenDelegate();
+ Assert.isNotNull(inCallScreenDelegate);
+ inCallScreenDelegate.onInCallScreenDelegateInit(this);
+ inCallScreenDelegate.onInCallScreenReady();
+ }
+
+ private void updateImportanceBadgeVisibility() {
+ if (!isAdded()) {
+ return;
+ }
+
+ if (!getResources().getBoolean(R.bool.answer_important_call_allowed)) {
+ importanceBadge.setVisibility(View.GONE);
+ return;
+ }
+
+ MultimediaData multimediaData = getSessionData();
+ boolean showImportant = multimediaData != null && multimediaData.isImportant();
+ TransitionManager.beginDelayedTransition((ViewGroup) importanceBadge.getParent());
+ // TODO (keyboardr): Change this back to being View.INVISIBLE once mocks are available to
+ // properly handle smaller screens
+ importanceBadge.setVisibility(showImportant ? View.VISIBLE : View.GONE);
+ }
+
+ @Nullable
+ private MultimediaData getSessionData() {
+ if (primaryInfo == null) {
+ return null;
+ }
+ return primaryInfo.multimediaData;
+ }
+
+ /** Shows the Avatar image if available. */
+ public static class AvatarFragment extends Fragment implements AvatarPresenter {
+
+ private ImageView avatarImageView;
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ return layoutInflater.inflate(R.layout.fragment_avatar, viewGroup, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle bundle) {
+ super.onViewCreated(view, bundle);
+ avatarImageView = ((ImageView) view.findViewById(R.id.contactgrid_avatar));
+ FragmentUtils.getParentUnsafe(this, MultimediaFragment.Holder.class).updateAvatar(this);
+ }
+
+ @NonNull
+ @Override
+ public ImageView getAvatarImageView() {
+ return avatarImageView;
+ }
+
+ @Override
+ public int getAvatarSize() {
+ return getResources().getDimensionPixelSize(R.dimen.answer_avatar_size);
+ }
+
+ @Override
+ public boolean shouldShowAnonymousAvatar() {
+ return false;
+ }
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/AnswerVideoCallScreen.java b/java/com/android/incallui/answer/impl/AnswerVideoCallScreen.java
new file mode 100644
index 000000000..0316a5fab
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/AnswerVideoCallScreen.java
@@ -0,0 +1,127 @@
+/*
+ * 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.incallui.answer.impl;
+
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+import android.view.TextureView;
+import android.view.View;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.FragmentUtils;
+import com.android.dialer.common.LogUtil;
+import com.android.incallui.video.protocol.VideoCallScreen;
+import com.android.incallui.video.protocol.VideoCallScreenDelegate;
+import com.android.incallui.video.protocol.VideoCallScreenDelegateFactory;
+import com.android.incallui.videosurface.bindings.VideoSurfaceBindings;
+
+/** Shows a video preview for an incoming call. */
+public class AnswerVideoCallScreen implements VideoCallScreen {
+ @NonNull private final Fragment fragment;
+ @NonNull private final TextureView textureView;
+ @NonNull private final VideoCallScreenDelegate delegate;
+
+ public AnswerVideoCallScreen(@NonNull Fragment fragment, @NonNull View view) {
+ this.fragment = fragment;
+
+ textureView =
+ Assert.isNotNull((TextureView) view.findViewById(R.id.incoming_preview_texture_view));
+ View overlayView =
+ Assert.isNotNull(view.findViewById(R.id.incoming_preview_texture_view_overlay));
+ view.setBackgroundColor(0xff000000);
+ delegate =
+ FragmentUtils.getParentUnsafe(fragment, VideoCallScreenDelegateFactory.class)
+ .newVideoCallScreenDelegate();
+ delegate.initVideoCallScreenDelegate(fragment.getContext(), this);
+
+ textureView.setVisibility(View.VISIBLE);
+ overlayView.setVisibility(View.VISIBLE);
+ }
+
+ public void onStart() {
+ LogUtil.i("AnswerVideoCallScreen.onStart", null);
+ delegate.onVideoCallScreenUiReady();
+ delegate.getLocalVideoSurfaceTexture().attachToTextureView(textureView);
+ }
+
+ public void onStop() {
+ LogUtil.i("AnswerVideoCallScreen.onStop", null);
+ delegate.onVideoCallScreenUiUnready();
+ }
+
+ @Override
+ public void showVideoViews(
+ boolean shouldShowPreview, boolean shouldShowRemote, boolean isRemotelyHeld) {
+ LogUtil.i(
+ "AnswerVideoCallScreen.showVideoViews",
+ "showPreview: %b, shouldShowRemote: %b",
+ shouldShowPreview,
+ shouldShowRemote);
+ }
+
+ @Override
+ public void onLocalVideoDimensionsChanged() {
+ LogUtil.i("AnswerVideoCallScreen.onLocalVideoDimensionsChanged", null);
+ updatePreviewVideoScaling();
+ }
+
+ @Override
+ public void onRemoteVideoDimensionsChanged() {}
+
+ @Override
+ public void onLocalVideoOrientationChanged() {
+ LogUtil.i("AnswerVideoCallScreen.onLocalVideoOrientationChanged", null);
+ updatePreviewVideoScaling();
+ }
+
+ @Override
+ public void updateFullscreenAndGreenScreenMode(
+ boolean shouldShowFullscreen, boolean shouldShowGreenScreen) {}
+
+ @Override
+ public Fragment getVideoCallScreenFragment() {
+ return fragment;
+ }
+
+ private void updatePreviewVideoScaling() {
+ if (textureView.getWidth() == 0 || textureView.getHeight() == 0) {
+ LogUtil.i(
+ "AnswerVideoCallScreen.updatePreviewVideoScaling", "view layout hasn't finished yet");
+ return;
+ }
+ Point cameraDimensions = delegate.getLocalVideoSurfaceTexture().getSurfaceDimensions();
+ if (cameraDimensions == null) {
+ LogUtil.i("AnswerVideoCallScreen.updatePreviewVideoScaling", "camera dimensions not set");
+ return;
+ }
+ if (isLandscape()) {
+ VideoSurfaceBindings.scaleVideoAndFillView(
+ textureView, cameraDimensions.x, cameraDimensions.y, delegate.getDeviceOrientation());
+ } else {
+ // Landscape, so dimensions are swapped
+ //noinspection SuspiciousNameCombination
+ VideoSurfaceBindings.scaleVideoAndFillView(
+ textureView, cameraDimensions.y, cameraDimensions.x, delegate.getDeviceOrientation());
+ }
+ }
+
+ private boolean isLandscape() {
+ return fragment.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/CreateCustomSmsDialogFragment.java b/java/com/android/incallui/answer/impl/CreateCustomSmsDialogFragment.java
new file mode 100644
index 000000000..b49409258
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/CreateCustomSmsDialogFragment.java
@@ -0,0 +1,137 @@
+/*
+ * 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.incallui.answer.impl;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnShowListener;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AppCompatDialogFragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+import android.widget.Button;
+import android.widget.EditText;
+import com.android.dialer.common.FragmentUtils;
+
+/**
+ * Shows the dialog for users to enter a custom message when rejecting a call with an SMS message.
+ */
+public class CreateCustomSmsDialogFragment extends AppCompatDialogFragment {
+
+ private static final String ARG_ENTERED_TEXT = "enteredText";
+
+ private EditText editText;
+
+ public static CreateCustomSmsDialogFragment newInstance() {
+ return new CreateCustomSmsDialogFragment();
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ View view = View.inflate(builder.getContext(), R.layout.fragment_custom_sms_dialog, null);
+ editText = (EditText) view.findViewById(R.id.custom_sms_input);
+ if (savedInstanceState != null) {
+ editText.setText(savedInstanceState.getCharSequence(ARG_ENTERED_TEXT));
+ }
+ builder
+ .setCancelable(true)
+ .setView(view)
+ .setPositiveButton(
+ R.string.call_incoming_custom_message_send,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ FragmentUtils.getParentUnsafe(
+ CreateCustomSmsDialogFragment.this, CreateCustomSmsHolder.class)
+ .customSmsCreated(editText.getText().toString().trim());
+ dismiss();
+ }
+ })
+ .setNegativeButton(
+ R.string.call_incoming_custom_message_cancel,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ dismiss();
+ }
+ })
+ .setOnCancelListener(
+ new OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialogInterface) {
+ dismiss();
+ }
+ })
+ .setTitle(R.string.call_incoming_respond_via_sms_custom_message);
+ final AlertDialog customMessagePopup = builder.create();
+ customMessagePopup.setOnShowListener(
+ new OnShowListener() {
+ @Override
+ public void onShow(DialogInterface dialogInterface) {
+ ((AlertDialog) dialogInterface)
+ .getButton(AlertDialog.BUTTON_POSITIVE)
+ .setEnabled(false);
+ }
+ });
+
+ editText.addTextChangedListener(
+ new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ Button sendButton = customMessagePopup.getButton(DialogInterface.BUTTON_POSITIVE);
+ sendButton.setEnabled(editable != null && editable.toString().trim().length() != 0);
+ }
+ });
+ customMessagePopup.getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ customMessagePopup.getWindow().addFlags(LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ return customMessagePopup;
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putCharSequence(ARG_ENTERED_TEXT, editText.getText());
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialogInterface) {
+ super.onDismiss(dialogInterface);
+ FragmentUtils.getParentUnsafe(this, CreateCustomSmsHolder.class).customSmsDismissed();
+ }
+
+ /** Call back for {@link CreateCustomSmsDialogFragment} */
+ public interface CreateCustomSmsHolder {
+
+ void customSmsCreated(@NonNull CharSequence text);
+
+ void customSmsDismissed();
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/PillDrawable.java b/java/com/android/incallui/answer/impl/PillDrawable.java
new file mode 100644
index 000000000..57d84c45f
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/PillDrawable.java
@@ -0,0 +1,43 @@
+/*
+ * 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.incallui.answer.impl;
+
+import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
+
+/** Draws a pill-shaped background */
+public class PillDrawable extends GradientDrawable {
+
+ public PillDrawable() {
+ super();
+ setShape(RECTANGLE);
+ }
+
+ @Override
+ protected void onBoundsChange(Rect r) {
+ super.onBoundsChange(r);
+ setCornerRadius(r.height() / 2);
+ }
+
+ @Override
+ public void setShape(int shape) {
+ if (shape != GradientDrawable.RECTANGLE) {
+ throw new UnsupportedOperationException("PillDrawable must be a rectangle");
+ }
+ super.setShape(shape);
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/SmsBottomSheetFragment.java b/java/com/android/incallui/answer/impl/SmsBottomSheetFragment.java
new file mode 100644
index 000000000..085430ea2
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/SmsBottomSheetFragment.java
@@ -0,0 +1,136 @@
+/*
+ * 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.incallui.answer.impl;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.design.widget.BottomSheetDialogFragment;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.android.dialer.common.DpUtil;
+import com.android.dialer.common.FragmentUtils;
+import com.android.dialer.common.LogUtil;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Shows options for rejecting call with SMS */
+public class SmsBottomSheetFragment extends BottomSheetDialogFragment {
+
+ private static final String ARG_OPTIONS = "options";
+
+ public static SmsBottomSheetFragment newInstance(@Nullable ArrayList<CharSequence> options) {
+ SmsBottomSheetFragment fragment = new SmsBottomSheetFragment();
+ Bundle args = new Bundle();
+ args.putCharSequenceArrayList(ARG_OPTIONS, options);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ LinearLayout layout = new LinearLayout(getContext());
+ layout.setOrientation(LinearLayout.VERTICAL);
+ List<CharSequence> items = getArguments().getCharSequenceArrayList(ARG_OPTIONS);
+ if (items != null) {
+ for (CharSequence item : items) {
+ layout.addView(newTextViewItem(item));
+ }
+ }
+ layout.addView(newTextViewItem(null));
+ layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ return layout;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ FragmentUtils.checkParent(this, SmsSheetHolder.class);
+ }
+
+ @Override
+ public Dialog onCreateDialog(final Bundle savedInstanceState) {
+ LogUtil.i("SmsBottomSheetFragment.onCreateDialog", null);
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ return dialog;
+ }
+
+ private TextView newTextViewItem(@Nullable final CharSequence text) {
+ int[] attrs = new int[] {android.R.attr.selectableItemBackground};
+ Context context = new ContextThemeWrapper(getContext(), getTheme());
+ TypedArray typedArray = context.obtainStyledAttributes(attrs);
+ Drawable background = typedArray.getDrawable(0);
+ //noinspection ResourceType
+ typedArray.recycle();
+
+ TextView textView = new TextView(context);
+ textView.setText(text == null ? getString(R.string.call_incoming_message_custom) : text);
+ int padding = (int) DpUtil.dpToPx(context, 16);
+ textView.setPadding(padding, padding, padding, padding);
+ textView.setBackground(background);
+ textView.setTextColor(context.getColor(R.color.blue_grey_100));
+ textView.setTextAppearance(R.style.TextAppearance_AppCompat_Widget_PopupMenu_Large);
+
+ LayoutParams params =
+ new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ textView.setLayoutParams(params);
+
+ textView.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ FragmentUtils.getParentUnsafe(SmsBottomSheetFragment.this, SmsSheetHolder.class)
+ .smsSelected(text);
+ dismiss();
+ }
+ });
+ return textView;
+ }
+
+ @Override
+ public int getTheme() {
+ return R.style.Theme_Design_Light_BottomSheetDialog;
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialogInterface) {
+ super.onDismiss(dialogInterface);
+ FragmentUtils.getParentUnsafe(this, SmsSheetHolder.class).smsDismissed();
+ }
+
+ /** Callback interface for {@link SmsBottomSheetFragment} */
+ public interface SmsSheetHolder {
+
+ void smsSelected(@Nullable CharSequence text);
+
+ void smsDismissed();
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/affordance/AndroidManifest.xml b/java/com/android/incallui/answer/impl/affordance/AndroidManifest.xml
new file mode 100644
index 000000000..960fd71c1
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/affordance/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.incallui.answer.impl.affordance">
+</manifest>
diff --git a/java/com/android/incallui/answer/impl/affordance/SwipeButtonHelper.java b/java/com/android/incallui/answer/impl/affordance/SwipeButtonHelper.java
new file mode 100644
index 000000000..62845b748
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/affordance/SwipeButtonHelper.java
@@ -0,0 +1,642 @@
+/*
+ * 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.incallui.answer.impl.affordance;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import com.android.incallui.answer.impl.utils.FlingAnimationUtils;
+import com.android.incallui.answer.impl.utils.Interpolators;
+
+/** A touch handler of the swipe buttons */
+public class SwipeButtonHelper {
+
+ public static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.87f;
+ public static final long HINT_PHASE1_DURATION = 200;
+ private static final long HINT_PHASE2_DURATION = 350;
+ private static final float BACKGROUND_RADIUS_SCALE_FACTOR = 0.25f;
+ private static final int HINT_CIRCLE_OPEN_DURATION = 500;
+
+ private final Context context;
+ private final Callback callback;
+
+ private FlingAnimationUtils flingAnimationUtils;
+ private VelocityTracker velocityTracker;
+ private boolean swipingInProgress;
+ private float initialTouchX;
+ private float initialTouchY;
+ private float translation;
+ private float translationOnDown;
+ private int touchSlop;
+ private int minTranslationAmount;
+ private int minFlingVelocity;
+ private int hintGrowAmount;
+ @Nullable private SwipeButtonView leftIcon;
+ @Nullable private SwipeButtonView rightIcon;
+ private Animator swipeAnimator;
+ private int minBackgroundRadius;
+ private boolean motionCancelled;
+ private int touchTargetSize;
+ private View targetedView;
+ private boolean touchSlopExeeded;
+ private AnimatorListenerAdapter flingEndListener =
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ swipeAnimator = null;
+ swipingInProgress = false;
+ targetedView = null;
+ }
+ };
+ private Runnable animationEndRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ callback.onAnimationToSideEnded();
+ }
+ };
+
+ public SwipeButtonHelper(Callback callback, Context context) {
+ this.context = context;
+ this.callback = callback;
+ init();
+ }
+
+ public void init() {
+ initIcons();
+ updateIcon(
+ leftIcon,
+ 0.0f,
+ leftIcon != null ? leftIcon.getRestingAlpha() : 0,
+ false,
+ false,
+ true,
+ false);
+ updateIcon(
+ rightIcon,
+ 0.0f,
+ rightIcon != null ? rightIcon.getRestingAlpha() : 0,
+ false,
+ false,
+ true,
+ false);
+ initDimens();
+ }
+
+ private void initDimens() {
+ final ViewConfiguration configuration = ViewConfiguration.get(context);
+ touchSlop = configuration.getScaledPagingTouchSlop();
+ minFlingVelocity = configuration.getScaledMinimumFlingVelocity();
+ minTranslationAmount =
+ context.getResources().getDimensionPixelSize(R.dimen.answer_min_swipe_amount);
+ minBackgroundRadius =
+ context
+ .getResources()
+ .getDimensionPixelSize(R.dimen.answer_affordance_min_background_radius);
+ touchTargetSize =
+ context.getResources().getDimensionPixelSize(R.dimen.answer_affordance_touch_target_size);
+ hintGrowAmount =
+ context.getResources().getDimensionPixelSize(R.dimen.hint_grow_amount_sideways);
+ flingAnimationUtils = new FlingAnimationUtils(context, 0.4f);
+ }
+
+ private void initIcons() {
+ leftIcon = callback.getLeftIcon();
+ rightIcon = callback.getRightIcon();
+ updatePreviews();
+ }
+
+ public void updatePreviews() {
+ if (leftIcon != null) {
+ leftIcon.setPreviewView(callback.getLeftPreview());
+ }
+ if (rightIcon != null) {
+ rightIcon.setPreviewView(callback.getRightPreview());
+ }
+ }
+
+ public boolean onTouchEvent(MotionEvent event) {
+ int action = event.getActionMasked();
+ if (motionCancelled && action != MotionEvent.ACTION_DOWN) {
+ return false;
+ }
+ final float y = event.getY();
+ final float x = event.getX();
+
+ boolean isUp = false;
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ View targetView = getIconAtPosition(x, y);
+ if (targetView == null || (targetedView != null && targetedView != targetView)) {
+ motionCancelled = true;
+ return false;
+ }
+ if (targetedView != null) {
+ cancelAnimation();
+ } else {
+ touchSlopExeeded = false;
+ }
+ startSwiping(targetView);
+ initialTouchX = x;
+ initialTouchY = y;
+ translationOnDown = translation;
+ initVelocityTracker();
+ trackMovement(event);
+ motionCancelled = false;
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ motionCancelled = true;
+ endMotion(true /* forceSnapBack */, x, y);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ trackMovement(event);
+ float xDist = x - initialTouchX;
+ float yDist = y - initialTouchY;
+ float distance = (float) Math.hypot(xDist, yDist);
+ if (!touchSlopExeeded && distance > touchSlop) {
+ touchSlopExeeded = true;
+ }
+ if (swipingInProgress) {
+ if (targetedView == rightIcon) {
+ distance = translationOnDown - distance;
+ distance = Math.min(0, distance);
+ } else {
+ distance = translationOnDown + distance;
+ distance = Math.max(0, distance);
+ }
+ setTranslation(distance, false /* isReset */, false /* animateReset */);
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ isUp = true;
+ //fallthrough_intended
+ case MotionEvent.ACTION_CANCEL:
+ boolean hintOnTheRight = targetedView == rightIcon;
+ trackMovement(event);
+ endMotion(!isUp, x, y);
+ if (!touchSlopExeeded && isUp) {
+ callback.onIconClicked(hintOnTheRight);
+ }
+ break;
+ }
+ return true;
+ }
+
+ private void startSwiping(View targetView) {
+ callback.onSwipingStarted(targetView == rightIcon);
+ swipingInProgress = true;
+ targetedView = targetView;
+ }
+
+ private View getIconAtPosition(float x, float y) {
+ if (leftSwipePossible() && isOnIcon(leftIcon, x, y)) {
+ return leftIcon;
+ }
+ if (rightSwipePossible() && isOnIcon(rightIcon, x, y)) {
+ return rightIcon;
+ }
+ return null;
+ }
+
+ public boolean isOnAffordanceIcon(float x, float y) {
+ return isOnIcon(leftIcon, x, y) || isOnIcon(rightIcon, x, y);
+ }
+
+ private boolean isOnIcon(View icon, float x, float y) {
+ float iconX = icon.getX() + icon.getWidth() / 2.0f;
+ float iconY = icon.getY() + icon.getHeight() / 2.0f;
+ double distance = Math.hypot(x - iconX, y - iconY);
+ return distance <= touchTargetSize / 2;
+ }
+
+ private void endMotion(boolean forceSnapBack, float lastX, float lastY) {
+ if (swipingInProgress) {
+ flingWithCurrentVelocity(forceSnapBack, lastX, lastY);
+ } else {
+ targetedView = null;
+ }
+ if (velocityTracker != null) {
+ velocityTracker.recycle();
+ velocityTracker = null;
+ }
+ }
+
+ private boolean rightSwipePossible() {
+ return rightIcon != null && rightIcon.getVisibility() == View.VISIBLE;
+ }
+
+ private boolean leftSwipePossible() {
+ return leftIcon != null && leftIcon.getVisibility() == View.VISIBLE;
+ }
+
+ public void startHintAnimation(boolean right, @Nullable Runnable onFinishedListener) {
+ cancelAnimation();
+ startHintAnimationPhase1(right, onFinishedListener);
+ }
+
+ private void startHintAnimationPhase1(
+ final boolean right, @Nullable final Runnable onFinishedListener) {
+ final SwipeButtonView targetView = right ? rightIcon : leftIcon;
+ ValueAnimator animator = getAnimatorToRadius(right, hintGrowAmount);
+ if (animator == null) {
+ if (onFinishedListener != null) {
+ onFinishedListener.run();
+ }
+ return;
+ }
+ animator.addListener(
+ new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCancelled) {
+ swipeAnimator = null;
+ targetedView = null;
+ if (onFinishedListener != null) {
+ onFinishedListener.run();
+ }
+ } else {
+ startUnlockHintAnimationPhase2(right, onFinishedListener);
+ }
+ }
+ });
+ animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ animator.setDuration(HINT_PHASE1_DURATION);
+ animator.start();
+ swipeAnimator = animator;
+ targetedView = targetView;
+ }
+
+ /** Phase 2: Move back. */
+ private void startUnlockHintAnimationPhase2(
+ boolean right, @Nullable final Runnable onFinishedListener) {
+ ValueAnimator animator = getAnimatorToRadius(right, 0);
+ if (animator == null) {
+ if (onFinishedListener != null) {
+ onFinishedListener.run();
+ }
+ return;
+ }
+ animator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ swipeAnimator = null;
+ targetedView = null;
+ if (onFinishedListener != null) {
+ onFinishedListener.run();
+ }
+ }
+ });
+ animator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+ animator.setDuration(HINT_PHASE2_DURATION);
+ animator.setStartDelay(HINT_CIRCLE_OPEN_DURATION);
+ animator.start();
+ swipeAnimator = animator;
+ }
+
+ private ValueAnimator getAnimatorToRadius(final boolean right, int radius) {
+ final SwipeButtonView targetView = right ? rightIcon : leftIcon;
+ if (targetView == null) {
+ return null;
+ }
+ ValueAnimator animator = ValueAnimator.ofFloat(targetView.getCircleRadius(), radius);
+ animator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float newRadius = (float) animation.getAnimatedValue();
+ targetView.setCircleRadiusWithoutAnimation(newRadius);
+ float translation = getTranslationFromRadius(newRadius);
+ SwipeButtonHelper.this.translation = right ? -translation : translation;
+ updateIconsFromTranslation(targetView);
+ }
+ });
+ return animator;
+ }
+
+ private void cancelAnimation() {
+ if (swipeAnimator != null) {
+ swipeAnimator.cancel();
+ }
+ }
+
+ private void flingWithCurrentVelocity(boolean forceSnapBack, float lastX, float lastY) {
+ float vel = getCurrentVelocity(lastX, lastY);
+
+ // We snap back if the current translation is not far enough
+ boolean snapBack = isBelowFalsingThreshold();
+
+ // or if the velocity is in the opposite direction.
+ boolean velIsInWrongDirection = vel * translation < 0;
+ snapBack |= Math.abs(vel) > minFlingVelocity && velIsInWrongDirection;
+ vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
+ fling(vel, snapBack || forceSnapBack, translation < 0);
+ }
+
+ private boolean isBelowFalsingThreshold() {
+ return Math.abs(translation) < Math.abs(translationOnDown) + getMinTranslationAmount();
+ }
+
+ private int getMinTranslationAmount() {
+ float factor = callback.getAffordanceFalsingFactor();
+ return (int) (minTranslationAmount * factor);
+ }
+
+ private void fling(float vel, final boolean snapBack, boolean right) {
+ float target =
+ right ? -callback.getMaxTranslationDistance() : callback.getMaxTranslationDistance();
+ target = snapBack ? 0 : target;
+
+ ValueAnimator animator = ValueAnimator.ofFloat(translation, target);
+ flingAnimationUtils.apply(animator, translation, target, vel);
+ animator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ translation = (float) animation.getAnimatedValue();
+ }
+ });
+ animator.addListener(flingEndListener);
+ if (!snapBack) {
+ startFinishingCircleAnimation(vel * 0.375f, animationEndRunnable, right);
+ callback.onAnimationToSideStarted(right, translation, vel);
+ } else {
+ reset(true);
+ }
+ animator.start();
+ swipeAnimator = animator;
+ if (snapBack) {
+ callback.onSwipingAborted();
+ }
+ }
+
+ private void startFinishingCircleAnimation(
+ float velocity, Runnable mAnimationEndRunnable, boolean right) {
+ SwipeButtonView targetView = right ? rightIcon : leftIcon;
+ if (targetView != null) {
+ targetView.finishAnimation(velocity, mAnimationEndRunnable);
+ }
+ }
+
+ private void setTranslation(float translation, boolean isReset, boolean animateReset) {
+ translation = rightSwipePossible() ? translation : Math.max(0, translation);
+ translation = leftSwipePossible() ? translation : Math.min(0, translation);
+ float absTranslation = Math.abs(translation);
+ if (translation != this.translation || isReset) {
+ SwipeButtonView targetView = translation > 0 ? leftIcon : rightIcon;
+ SwipeButtonView otherView = translation > 0 ? rightIcon : leftIcon;
+ float alpha = absTranslation / getMinTranslationAmount();
+
+ // We interpolate the alpha of the other icons to 0
+ float fadeOutAlpha = 1.0f - alpha;
+ fadeOutAlpha = Math.max(fadeOutAlpha, 0.0f);
+
+ boolean animateIcons = isReset && animateReset;
+ boolean forceNoCircleAnimation = isReset && !animateReset;
+ float radius = getRadiusFromTranslation(absTranslation);
+ boolean slowAnimation = isReset && isBelowFalsingThreshold();
+ if (targetView != null) {
+ if (!isReset) {
+ updateIcon(
+ targetView,
+ radius,
+ alpha + fadeOutAlpha * targetView.getRestingAlpha(),
+ false,
+ false,
+ false,
+ false);
+ } else {
+ updateIcon(
+ targetView,
+ 0.0f,
+ fadeOutAlpha * targetView.getRestingAlpha(),
+ animateIcons,
+ slowAnimation,
+ false,
+ forceNoCircleAnimation);
+ }
+ }
+ if (otherView != null) {
+ updateIcon(
+ otherView,
+ 0.0f,
+ fadeOutAlpha * otherView.getRestingAlpha(),
+ animateIcons,
+ slowAnimation,
+ false,
+ forceNoCircleAnimation);
+ }
+
+ this.translation = translation;
+ }
+ }
+
+ private void updateIconsFromTranslation(SwipeButtonView targetView) {
+ float absTranslation = Math.abs(translation);
+ float alpha = absTranslation / getMinTranslationAmount();
+
+ // We interpolate the alpha of the other icons to 0
+ float fadeOutAlpha = 1.0f - alpha;
+ fadeOutAlpha = Math.max(0.0f, fadeOutAlpha);
+
+ // We interpolate the alpha of the targetView to 1
+ SwipeButtonView otherView = targetView == rightIcon ? leftIcon : rightIcon;
+ updateIconAlpha(targetView, alpha + fadeOutAlpha * targetView.getRestingAlpha(), false);
+ if (otherView != null) {
+ updateIconAlpha(otherView, fadeOutAlpha * otherView.getRestingAlpha(), false);
+ }
+ }
+
+ private float getTranslationFromRadius(float circleSize) {
+ float translation = (circleSize - minBackgroundRadius) / BACKGROUND_RADIUS_SCALE_FACTOR;
+ return translation > 0.0f ? translation + touchSlop : 0.0f;
+ }
+
+ private float getRadiusFromTranslation(float translation) {
+ if (translation <= touchSlop) {
+ return 0.0f;
+ }
+ return (translation - touchSlop) * BACKGROUND_RADIUS_SCALE_FACTOR + minBackgroundRadius;
+ }
+
+ public void animateHideLeftRightIcon() {
+ cancelAnimation();
+ updateIcon(rightIcon, 0f, 0f, true, false, false, false);
+ updateIcon(leftIcon, 0f, 0f, true, false, false, false);
+ }
+
+ private void updateIcon(
+ @Nullable SwipeButtonView view,
+ float circleRadius,
+ float alpha,
+ boolean animate,
+ boolean slowRadiusAnimation,
+ boolean force,
+ boolean forceNoCircleAnimation) {
+ if (view == null) {
+ return;
+ }
+ if (view.getVisibility() != View.VISIBLE && !force) {
+ return;
+ }
+ if (forceNoCircleAnimation) {
+ view.setCircleRadiusWithoutAnimation(circleRadius);
+ } else {
+ view.setCircleRadius(circleRadius, slowRadiusAnimation);
+ }
+ updateIconAlpha(view, alpha, animate);
+ }
+
+ private void updateIconAlpha(SwipeButtonView view, float alpha, boolean animate) {
+ float scale = getScale(alpha, view);
+ alpha = Math.min(1.0f, alpha);
+ view.setImageAlpha(alpha, animate);
+ view.setImageScale(scale, animate);
+ }
+
+ private float getScale(float alpha, SwipeButtonView icon) {
+ float scale = alpha / icon.getRestingAlpha() * 0.2f + SwipeButtonView.MIN_ICON_SCALE_AMOUNT;
+ return Math.min(scale, SwipeButtonView.MAX_ICON_SCALE_AMOUNT);
+ }
+
+ private void trackMovement(MotionEvent event) {
+ if (velocityTracker != null) {
+ velocityTracker.addMovement(event);
+ }
+ }
+
+ private void initVelocityTracker() {
+ if (velocityTracker != null) {
+ velocityTracker.recycle();
+ }
+ velocityTracker = VelocityTracker.obtain();
+ }
+
+ private float getCurrentVelocity(float lastX, float lastY) {
+ if (velocityTracker == null) {
+ return 0;
+ }
+ velocityTracker.computeCurrentVelocity(1000);
+ float aX = velocityTracker.getXVelocity();
+ float aY = velocityTracker.getYVelocity();
+ float bX = lastX - initialTouchX;
+ float bY = lastY - initialTouchY;
+ float bLen = (float) Math.hypot(bX, bY);
+ // Project the velocity onto the distance vector: a * b / |b|
+ float projectedVelocity = (aX * bX + aY * bY) / bLen;
+ if (targetedView == rightIcon) {
+ projectedVelocity = -projectedVelocity;
+ }
+ return projectedVelocity;
+ }
+
+ public void onConfigurationChanged() {
+ initDimens();
+ initIcons();
+ }
+
+ public void onRtlPropertiesChanged() {
+ initIcons();
+ }
+
+ public void reset(boolean animate) {
+ cancelAnimation();
+ setTranslation(0.0f, true, animate);
+ motionCancelled = true;
+ if (swipingInProgress) {
+ callback.onSwipingAborted();
+ swipingInProgress = false;
+ }
+ }
+
+ public boolean isSwipingInProgress() {
+ return swipingInProgress;
+ }
+
+ public void launchAffordance(boolean animate, boolean left) {
+ SwipeButtonView targetView = left ? leftIcon : rightIcon;
+ if (swipingInProgress || targetView == null) {
+ // We don't want to mess with the state if the user is actually swiping already.
+ return;
+ }
+ SwipeButtonView otherView = left ? rightIcon : leftIcon;
+ startSwiping(targetView);
+ if (animate) {
+ fling(0, false, !left);
+ updateIcon(otherView, 0.0f, 0, true, false, true, false);
+ } else {
+ callback.onAnimationToSideStarted(!left, translation, 0);
+ translation =
+ left ? callback.getMaxTranslationDistance() : callback.getMaxTranslationDistance();
+ updateIcon(otherView, 0.0f, 0.0f, false, false, true, false);
+ targetView.instantFinishAnimation();
+ flingEndListener.onAnimationEnd(null);
+ animationEndRunnable.run();
+ }
+ }
+
+ /** Callback interface for various actions */
+ public interface Callback {
+
+ /**
+ * Notifies the callback when an animation to a side page was started.
+ *
+ * @param rightPage Is the page animated to the right page?
+ */
+ void onAnimationToSideStarted(boolean rightPage, float translation, float vel);
+
+ /** Notifies the callback the animation to a side page has ended. */
+ void onAnimationToSideEnded();
+
+ float getMaxTranslationDistance();
+
+ void onSwipingStarted(boolean rightIcon);
+
+ void onSwipingAborted();
+
+ void onIconClicked(boolean rightIcon);
+
+ @Nullable
+ SwipeButtonView getLeftIcon();
+
+ @Nullable
+ SwipeButtonView getRightIcon();
+
+ @Nullable
+ View getLeftPreview();
+
+ @Nullable
+ View getRightPreview();
+
+ /** @return The factor the minimum swipe amount should be multiplied with. */
+ float getAffordanceFalsingFactor();
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/affordance/SwipeButtonView.java b/java/com/android/incallui/answer/impl/affordance/SwipeButtonView.java
new file mode 100644
index 000000000..46879ea3f
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/affordance/SwipeButtonView.java
@@ -0,0 +1,505 @@
+/*
+ * 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.incallui.answer.impl.affordance;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ArgbEvaluator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.ImageView;
+import com.android.incallui.answer.impl.utils.FlingAnimationUtils;
+import com.android.incallui.answer.impl.utils.Interpolators;
+
+/** Button that allows swiping to trigger */
+public class SwipeButtonView extends ImageView {
+
+ private static final long CIRCLE_APPEAR_DURATION = 80;
+ private static final long CIRCLE_DISAPPEAR_MAX_DURATION = 200;
+ private static final long NORMAL_ANIMATION_DURATION = 200;
+ public static final float MAX_ICON_SCALE_AMOUNT = 1.5f;
+ public static final float MIN_ICON_SCALE_AMOUNT = 0.8f;
+
+ private final int minBackgroundRadius;
+ private final Paint circlePaint;
+ private final int inverseColor;
+ private final int normalColor;
+ private final ArgbEvaluator colorInterpolator;
+ private final FlingAnimationUtils flingAnimationUtils;
+ private float circleRadius;
+ private int centerX;
+ private int centerY;
+ private ValueAnimator circleAnimator;
+ private ValueAnimator alphaAnimator;
+ private ValueAnimator scaleAnimator;
+ private float circleStartValue;
+ private boolean circleWillBeHidden;
+ private int[] tempPoint = new int[2];
+ private float tmageScale = 1f;
+ private int circleColor;
+ private View previewView;
+ private float circleStartRadius;
+ private float maxCircleSize;
+ private Animator previewClipper;
+ private float restingAlpha = SwipeButtonHelper.SWIPE_RESTING_ALPHA_AMOUNT;
+ private boolean finishing;
+ private boolean launchingAffordance;
+
+ private AnimatorListenerAdapter clipEndListener =
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ previewClipper = null;
+ }
+ };
+ private AnimatorListenerAdapter circleEndListener =
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ circleAnimator = null;
+ }
+ };
+ private AnimatorListenerAdapter scaleEndListener =
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ scaleAnimator = null;
+ }
+ };
+ private AnimatorListenerAdapter alphaEndListener =
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ alphaAnimator = null;
+ }
+ };
+
+ public SwipeButtonView(Context context) {
+ this(context, null);
+ }
+
+ public SwipeButtonView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SwipeButtonView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public SwipeButtonView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ circlePaint = new Paint();
+ circlePaint.setAntiAlias(true);
+ circleColor = 0xffffffff;
+ circlePaint.setColor(circleColor);
+
+ normalColor = 0xffffffff;
+ inverseColor = 0xff000000;
+ minBackgroundRadius =
+ context
+ .getResources()
+ .getDimensionPixelSize(R.dimen.answer_affordance_min_background_radius);
+ colorInterpolator = new ArgbEvaluator();
+ flingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ centerX = getWidth() / 2;
+ centerY = getHeight() / 2;
+ maxCircleSize = getMaxCircleSize();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ drawBackgroundCircle(canvas);
+ canvas.save();
+ canvas.scale(tmageScale, tmageScale, getWidth() / 2, getHeight() / 2);
+ super.onDraw(canvas);
+ canvas.restore();
+ }
+
+ public void setPreviewView(@Nullable View v) {
+ View oldPreviewView = previewView;
+ previewView = v;
+ if (previewView != null) {
+ previewView.setVisibility(launchingAffordance ? oldPreviewView.getVisibility() : INVISIBLE);
+ }
+ }
+
+ private void updateIconColor() {
+ Drawable drawable = getDrawable().mutate();
+ float alpha = circleRadius / minBackgroundRadius;
+ alpha = Math.min(1.0f, alpha);
+ int color = (int) colorInterpolator.evaluate(alpha, normalColor, inverseColor);
+ drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ }
+
+ private void drawBackgroundCircle(Canvas canvas) {
+ if (circleRadius > 0 || finishing) {
+ updateCircleColor();
+ canvas.drawCircle(centerX, centerY, circleRadius, circlePaint);
+ }
+ }
+
+ private void updateCircleColor() {
+ float fraction =
+ 0.5f
+ + 0.5f
+ * Math.max(
+ 0.0f,
+ Math.min(
+ 1.0f, (circleRadius - minBackgroundRadius) / (0.5f * minBackgroundRadius)));
+ if (previewView != null && previewView.getVisibility() == VISIBLE) {
+ float finishingFraction =
+ 1 - Math.max(0, circleRadius - circleStartRadius) / (maxCircleSize - circleStartRadius);
+ fraction *= finishingFraction;
+ }
+ int color =
+ Color.argb(
+ (int) (Color.alpha(circleColor) * fraction),
+ Color.red(circleColor),
+ Color.green(circleColor),
+ Color.blue(circleColor));
+ circlePaint.setColor(color);
+ }
+
+ public void finishAnimation(float velocity, @Nullable final Runnable mAnimationEndRunnable) {
+ cancelAnimator(circleAnimator);
+ cancelAnimator(previewClipper);
+ finishing = true;
+ circleStartRadius = circleRadius;
+ final float maxCircleSize = getMaxCircleSize();
+ Animator animatorToRadius;
+ animatorToRadius = getAnimatorToRadius(maxCircleSize);
+ flingAnimationUtils.applyDismissing(
+ animatorToRadius, circleRadius, maxCircleSize, velocity, maxCircleSize);
+ animatorToRadius.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mAnimationEndRunnable != null) {
+ mAnimationEndRunnable.run();
+ }
+ finishing = false;
+ circleRadius = maxCircleSize;
+ invalidate();
+ }
+ });
+ animatorToRadius.start();
+ setImageAlpha(0, true);
+ if (previewView != null) {
+ previewView.setVisibility(View.VISIBLE);
+ previewClipper =
+ ViewAnimationUtils.createCircularReveal(
+ previewView, getLeft() + centerX, getTop() + centerY, circleRadius, maxCircleSize);
+ flingAnimationUtils.applyDismissing(
+ previewClipper, circleRadius, maxCircleSize, velocity, maxCircleSize);
+ previewClipper.addListener(clipEndListener);
+ previewClipper.start();
+ }
+ }
+
+ public void instantFinishAnimation() {
+ cancelAnimator(previewClipper);
+ if (previewView != null) {
+ previewView.setClipBounds(null);
+ previewView.setVisibility(View.VISIBLE);
+ }
+ circleRadius = getMaxCircleSize();
+ setImageAlpha(0, false);
+ invalidate();
+ }
+
+ private float getMaxCircleSize() {
+ getLocationInWindow(tempPoint);
+ float rootWidth = getRootView().getWidth();
+ float width = tempPoint[0] + centerX;
+ width = Math.max(rootWidth - width, width);
+ float height = tempPoint[1] + centerY;
+ return (float) Math.hypot(width, height);
+ }
+
+ public void setCircleRadius(float circleRadius) {
+ setCircleRadius(circleRadius, false, false);
+ }
+
+ public void setCircleRadius(float circleRadius, boolean slowAnimation) {
+ setCircleRadius(circleRadius, slowAnimation, false);
+ }
+
+ public void setCircleRadiusWithoutAnimation(float circleRadius) {
+ cancelAnimator(circleAnimator);
+ setCircleRadius(circleRadius, false, true);
+ }
+
+ private void setCircleRadius(float circleRadius, boolean slowAnimation, boolean noAnimation) {
+
+ // Check if we need a new animation
+ boolean radiusHidden =
+ (circleAnimator != null && circleWillBeHidden)
+ || (circleAnimator == null && this.circleRadius == 0.0f);
+ boolean nowHidden = circleRadius == 0.0f;
+ boolean radiusNeedsAnimation = (radiusHidden != nowHidden) && !noAnimation;
+ if (!radiusNeedsAnimation) {
+ if (circleAnimator == null) {
+ this.circleRadius = circleRadius;
+ updateIconColor();
+ invalidate();
+ if (nowHidden) {
+ if (previewView != null) {
+ previewView.setVisibility(View.INVISIBLE);
+ }
+ }
+ } else if (!circleWillBeHidden) {
+
+ // We just update the end value
+ float diff = circleRadius - minBackgroundRadius;
+ PropertyValuesHolder[] values = circleAnimator.getValues();
+ values[0].setFloatValues(circleStartValue + diff, circleRadius);
+ circleAnimator.setCurrentPlayTime(circleAnimator.getCurrentPlayTime());
+ }
+ } else {
+ cancelAnimator(circleAnimator);
+ cancelAnimator(previewClipper);
+ ValueAnimator animator = getAnimatorToRadius(circleRadius);
+ Interpolator interpolator =
+ circleRadius == 0.0f
+ ? Interpolators.FAST_OUT_LINEAR_IN
+ : Interpolators.LINEAR_OUT_SLOW_IN;
+ animator.setInterpolator(interpolator);
+ long duration = 250;
+ if (!slowAnimation) {
+ float durationFactor =
+ Math.abs(this.circleRadius - circleRadius) / (float) minBackgroundRadius;
+ duration = (long) (CIRCLE_APPEAR_DURATION * durationFactor);
+ duration = Math.min(duration, CIRCLE_DISAPPEAR_MAX_DURATION);
+ }
+ animator.setDuration(duration);
+ animator.start();
+ if (previewView != null && previewView.getVisibility() == View.VISIBLE) {
+ previewView.setVisibility(View.VISIBLE);
+ previewClipper =
+ ViewAnimationUtils.createCircularReveal(
+ previewView,
+ getLeft() + centerX,
+ getTop() + centerY,
+ this.circleRadius,
+ circleRadius);
+ previewClipper.setInterpolator(interpolator);
+ previewClipper.setDuration(duration);
+ previewClipper.addListener(clipEndListener);
+ previewClipper.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ previewView.setVisibility(View.INVISIBLE);
+ }
+ });
+ previewClipper.start();
+ }
+ }
+ }
+
+ private ValueAnimator getAnimatorToRadius(float circleRadius) {
+ ValueAnimator animator = ValueAnimator.ofFloat(this.circleRadius, circleRadius);
+ circleAnimator = animator;
+ circleStartValue = this.circleRadius;
+ circleWillBeHidden = circleRadius == 0.0f;
+ animator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ SwipeButtonView.this.circleRadius = (float) animation.getAnimatedValue();
+ updateIconColor();
+ invalidate();
+ }
+ });
+ animator.addListener(circleEndListener);
+ return animator;
+ }
+
+ private void cancelAnimator(Animator animator) {
+ if (animator != null) {
+ animator.cancel();
+ }
+ }
+
+ public void setImageScale(float imageScale, boolean animate) {
+ setImageScale(imageScale, animate, -1, null);
+ }
+
+ /**
+ * Sets the scale of the containing image
+ *
+ * @param imageScale The new Scale.
+ * @param animate Should an animation be performed
+ * @param duration If animate, whats the duration? When -1 we take the default duration
+ * @param interpolator If animate, whats the interpolator? When null we take the default
+ * interpolator.
+ */
+ public void setImageScale(
+ float imageScale, boolean animate, long duration, @Nullable Interpolator interpolator) {
+ cancelAnimator(scaleAnimator);
+ if (!animate) {
+ tmageScale = imageScale;
+ invalidate();
+ } else {
+ ValueAnimator animator = ValueAnimator.ofFloat(tmageScale, imageScale);
+ scaleAnimator = animator;
+ animator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ tmageScale = (float) animation.getAnimatedValue();
+ invalidate();
+ }
+ });
+ animator.addListener(scaleEndListener);
+ if (interpolator == null) {
+ interpolator =
+ imageScale == 0.0f
+ ? Interpolators.FAST_OUT_LINEAR_IN
+ : Interpolators.LINEAR_OUT_SLOW_IN;
+ }
+ animator.setInterpolator(interpolator);
+ if (duration == -1) {
+ float durationFactor = Math.abs(tmageScale - imageScale) / (1.0f - MIN_ICON_SCALE_AMOUNT);
+ durationFactor = Math.min(1.0f, durationFactor);
+ duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor);
+ }
+ animator.setDuration(duration);
+ animator.start();
+ }
+ }
+
+ public void setRestingAlpha(float alpha) {
+ restingAlpha = alpha;
+
+ // TODO: Handle the case an animation is playing.
+ setImageAlpha(alpha, false);
+ }
+
+ public float getRestingAlpha() {
+ return restingAlpha;
+ }
+
+ public void setImageAlpha(float alpha, boolean animate) {
+ setImageAlpha(alpha, animate, -1, null, null);
+ }
+
+ /**
+ * Sets the alpha of the containing image
+ *
+ * @param alpha The new alpha.
+ * @param animate Should an animation be performed
+ * @param duration If animate, whats the duration? When -1 we take the default duration
+ * @param interpolator If animate, whats the interpolator? When null we take the default
+ * interpolator.
+ */
+ public void setImageAlpha(
+ float alpha,
+ boolean animate,
+ long duration,
+ @Nullable Interpolator interpolator,
+ @Nullable Runnable runnable) {
+ cancelAnimator(alphaAnimator);
+ alpha = launchingAffordance ? 0 : alpha;
+ int endAlpha = (int) (alpha * 255);
+ final Drawable background = getBackground();
+ if (!animate) {
+ if (background != null) {
+ background.mutate().setAlpha(endAlpha);
+ }
+ setImageAlpha(endAlpha);
+ } else {
+ int currentAlpha = getImageAlpha();
+ ValueAnimator animator = ValueAnimator.ofInt(currentAlpha, endAlpha);
+ alphaAnimator = animator;
+ animator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int alpha = (int) animation.getAnimatedValue();
+ if (background != null) {
+ background.mutate().setAlpha(alpha);
+ }
+ setImageAlpha(alpha);
+ }
+ });
+ animator.addListener(alphaEndListener);
+ if (interpolator == null) {
+ interpolator =
+ alpha == 0.0f ? Interpolators.FAST_OUT_LINEAR_IN : Interpolators.LINEAR_OUT_SLOW_IN;
+ }
+ animator.setInterpolator(interpolator);
+ if (duration == -1) {
+ float durationFactor = Math.abs(currentAlpha - endAlpha) / 255f;
+ durationFactor = Math.min(1.0f, durationFactor);
+ duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor);
+ }
+ animator.setDuration(duration);
+ if (runnable != null) {
+ animator.addListener(getEndListener(runnable));
+ }
+ animator.start();
+ }
+ }
+
+ private Animator.AnimatorListener getEndListener(final Runnable runnable) {
+ return new AnimatorListenerAdapter() {
+ boolean mCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCancelled) {
+ runnable.run();
+ }
+ }
+ };
+ }
+
+ public float getCircleRadius() {
+ return circleRadius;
+ }
+
+ @Override
+ public boolean performClick() {
+ return isClickable() && super.performClick();
+ }
+
+ public void setLaunchingAffordance(boolean launchingAffordance) {
+ this.launchingAffordance = launchingAffordance;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/affordance/res/values/dimens.xml b/java/com/android/incallui/answer/impl/affordance/res/values/dimens.xml
new file mode 100644
index 000000000..71d014dd9
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/affordance/res/values/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <dimen name="answer_min_swipe_amount">110dp</dimen>
+ <dimen name="answer_affordance_min_background_radius">30dp</dimen>
+ <dimen name="answer_affordance_touch_target_size">120dp</dimen>
+ <dimen name="hint_grow_amount_sideways">60dp</dimen>
+</resources>
diff --git a/java/com/android/incallui/answer/impl/answermethod/AndroidManifest.xml b/java/com/android/incallui/answer/impl/answermethod/AndroidManifest.xml
new file mode 100644
index 000000000..9082407f1
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.incallui.answer.impl.answermethod">
+</manifest>
diff --git a/java/com/android/incallui/answer/impl/answermethod/AnswerMethod.java b/java/com/android/incallui/answer/impl/answermethod/AnswerMethod.java
new file mode 100644
index 000000000..5efd3f05b
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/AnswerMethod.java
@@ -0,0 +1,45 @@
+/*
+ * 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.incallui.answer.impl.answermethod;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import com.android.dialer.common.FragmentUtils;
+
+/** A fragment that can be used to answer/reject calls. */
+public abstract class AnswerMethod extends Fragment {
+
+ public abstract void setHintText(@Nullable CharSequence hintText);
+
+ public abstract void setShowIncomingWillDisconnect(boolean incomingWillDisconnect);
+
+ public void setContactPhoto(@Nullable Drawable contactPhoto) {
+ // default implementation does nothing. Only some AnswerMethods show a photo
+ }
+
+ protected AnswerMethodHolder getParent() {
+ return FragmentUtils.getParentUnsafe(this, AnswerMethodHolder.class);
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ FragmentUtils.checkParent(this, AnswerMethodHolder.class);
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/answermethod/AnswerMethodFactory.java b/java/com/android/incallui/answer/impl/answermethod/AnswerMethodFactory.java
new file mode 100644
index 000000000..35f36f727
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/AnswerMethodFactory.java
@@ -0,0 +1,52 @@
+/*
+ * 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.incallui.answer.impl.answermethod;
+
+import android.app.Activity;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import com.android.dialer.compat.ActivityCompat;
+import com.android.incallui.util.AccessibilityUtil;
+
+/** Creates the appropriate {@link AnswerMethod} for the circumstances. */
+public class AnswerMethodFactory {
+
+ @NonNull
+ public static AnswerMethod createAnswerMethod(@NonNull Activity activity) {
+ if (needTwoButton(activity)) {
+ return new TwoButtonMethod();
+ } else {
+ return new FlingUpDownMethod();
+ }
+ }
+
+ public static boolean needsReplacement(@Nullable Fragment answerMethod) {
+ //noinspection SimplifiableIfStatement
+ if (answerMethod == null) {
+ return true;
+ }
+ // If we have already started showing TwoButtonMethod, we should keep showing TwoButtonMethod.
+ // Otherwise check if we need to change to TwoButtonMethod
+ return !(answerMethod instanceof TwoButtonMethod) && needTwoButton(answerMethod.getActivity());
+ }
+
+ private static boolean needTwoButton(@NonNull Activity activity) {
+ return AccessibilityUtil.isTouchExplorationEnabled(activity)
+ || ActivityCompat.isInMultiWindowMode(activity);
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/answermethod/AnswerMethodHolder.java b/java/com/android/incallui/answer/impl/answermethod/AnswerMethodHolder.java
new file mode 100644
index 000000000..4052281b7
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/AnswerMethodHolder.java
@@ -0,0 +1,47 @@
+/*
+ * 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.incallui.answer.impl.answermethod;
+
+import android.support.annotation.FloatRange;
+
+/** Defines callbacks {@link AnswerMethod AnswerMethods} may use to update their parent. */
+public interface AnswerMethodHolder {
+
+ /**
+ * Update animation based on method progress.
+ *
+ * @param answerProgress float representing progress. -1 is fully declined, 1 is fully answered,
+ * and 0 is neutral.
+ */
+ void onAnswerProgressUpdate(@FloatRange(from = -1f, to = 1f) float answerProgress);
+
+ /** Answer the current call. */
+ void answerFromMethod();
+
+ /** Reject the current call. */
+ void rejectFromMethod();
+
+ /** Set AnswerProgress to zero (not due to normal updates). */
+ void resetAnswerProgress();
+
+ /**
+ * Check whether the current call is a video call.
+ *
+ * @return true iff the current call is a video call.
+ */
+ boolean isVideoCall();
+}
diff --git a/java/com/android/incallui/answer/impl/answermethod/FlingUpDownMethod.java b/java/com/android/incallui/answer/impl/answermethod/FlingUpDownMethod.java
new file mode 100644
index 000000000..0bc65818c
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/FlingUpDownMethod.java
@@ -0,0 +1,1149 @@
+/*
+ * 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.incallui.answer.impl.answermethod;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.annotation.ColorInt;
+import android.support.annotation.FloatRange;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.graphics.ColorUtils;
+import android.support.v4.view.animation.FastOutLinearInInterpolator;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.support.v4.view.animation.LinearOutSlowInInterpolator;
+import android.support.v4.view.animation.PathInterpolatorCompat;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.view.animation.BounceInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.android.dialer.common.DpUtil;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.MathUtil;
+import com.android.dialer.util.DrawableConverter;
+import com.android.dialer.util.ViewUtil;
+import com.android.incallui.answer.impl.answermethod.FlingUpDownTouchHandler.OnProgressChangedListener;
+import com.android.incallui.answer.impl.classifier.FalsingManager;
+import com.android.incallui.answer.impl.hint.AnswerHint;
+import com.android.incallui.answer.impl.hint.AnswerHintFactory;
+import com.android.incallui.answer.impl.hint.EventPayloadLoaderImpl;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Answer method that swipes up to answer or down to reject. */
+@SuppressLint("ClickableViewAccessibility")
+public class FlingUpDownMethod extends AnswerMethod implements OnProgressChangedListener {
+
+ private static final float SWIPE_LERP_PROGRESS_FACTOR = 0.5f;
+ private static final long ANIMATE_DURATION_SHORT_MILLIS = 667;
+ private static final long ANIMATE_DURATION_NORMAL_MILLIS = 1_333;
+ private static final long ANIMATE_DURATION_LONG_MILLIS = 1_500;
+ private static final long BOUNCE_ANIMATION_DELAY = 167;
+ private static final long VIBRATION_TIME_MILLIS = 1_833;
+ private static final long SETTLE_ANIMATION_DURATION_MILLIS = 100;
+ private static final int HINT_JUMP_DP = 60;
+ private static final int HINT_DIP_DP = 8;
+ private static final float HINT_SCALE_RATIO = 1.15f;
+ private static final long SWIPE_TO_DECLINE_FADE_IN_DELAY_MILLIS = 333;
+ private static final int HINT_REJECT_SHOW_DURATION_MILLIS = 2000;
+ private static final int ICON_END_CALL_ROTATION_DEGREES = 135;
+ private static final int HINT_REJECT_FADE_TRANSLATION_Y_DP = -8;
+ private static final float SWIPE_TO_ANSWER_MAX_TRANSLATION_Y_DP = 150;
+ private static final int SWIPE_TO_REJECT_MAX_TRANSLATION_Y_DP = 24;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ value = {
+ AnimationState.NONE,
+ AnimationState.ENTRY,
+ AnimationState.BOUNCE,
+ AnimationState.SWIPE,
+ AnimationState.SETTLE,
+ AnimationState.HINT,
+ AnimationState.COMPLETED
+ }
+ )
+ @VisibleForTesting
+ @interface AnimationState {
+
+ int NONE = 0;
+ int ENTRY = 1; // Entry animation for incoming call
+ int BOUNCE = 2; // An idle state in which text and icon slightly bounces off its base repeatedly
+ int SWIPE = 3; // A special state in which text and icon follows the finger movement
+ int SETTLE = 4; // A short animation to reset from swipe and prepare for hint or bounce
+ int HINT = 5; // Jump animation to suggest what to do
+ int COMPLETED = 6; // Animation loop completed. Occurs after user swipes beyond threshold
+ }
+
+ private static void moveTowardY(View view, float newY) {
+ view.setTranslationY(MathUtil.lerp(view.getTranslationY(), newY, SWIPE_LERP_PROGRESS_FACTOR));
+ }
+
+ private static void moveTowardX(View view, float newX) {
+ view.setTranslationX(MathUtil.lerp(view.getTranslationX(), newX, SWIPE_LERP_PROGRESS_FACTOR));
+ }
+
+ private static void fadeToward(View view, float newAlpha) {
+ view.setAlpha(MathUtil.lerp(view.getAlpha(), newAlpha, SWIPE_LERP_PROGRESS_FACTOR));
+ }
+
+ private static void rotateToward(View view, float newRotation) {
+ view.setRotation(MathUtil.lerp(view.getRotation(), newRotation, SWIPE_LERP_PROGRESS_FACTOR));
+ }
+
+ private TextView swipeToAnswerText;
+ private TextView swipeToRejectText;
+ private View contactPuckContainer;
+ private ImageView contactPuckBackground;
+ private ImageView contactPuckIcon;
+ private View incomingDisconnectText;
+ private Animator lockBounceAnim;
+ private AnimatorSet lockEntryAnim;
+ private AnimatorSet lockHintAnim;
+ private AnimatorSet lockSettleAnim;
+ @AnimationState private int animationState = AnimationState.NONE;
+ @AnimationState private int afterSettleAnimationState = AnimationState.NONE;
+ // a value for finger swipe progress. -1 or less for "reject"; 1 or more for "accept".
+ private float swipeProgress;
+ private Animator rejectHintHide;
+ private Animator vibrationAnimator;
+ private Drawable contactPhoto;
+ private boolean incomingWillDisconnect;
+ private FlingUpDownTouchHandler touchHandler;
+ private FalsingManager falsingManager;
+
+ private AnswerHint answerHint;
+
+ @Override
+ public void onCreate(@Nullable Bundle bundle) {
+ super.onCreate(bundle);
+ falsingManager = new FalsingManager(getContext());
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ falsingManager.onScreenOn();
+ if (getView() != null) {
+ if (animationState == AnimationState.SWIPE || animationState == AnimationState.HINT) {
+ swipeProgress = 0;
+ updateContactPuck();
+ onMoveReset(false);
+ } else if (animationState == AnimationState.ENTRY) {
+ // When starting from the lock screen, the activity may be stopped and started briefly.
+ // Don't let that interrupt the entry animation
+ startSwipeToAnswerEntryAnimation();
+ }
+ }
+ }
+
+ @Override
+ public void onStop() {
+ endAnimation();
+ falsingManager.onScreenOff();
+ if (getActivity().isFinishing()) {
+ setAnimationState(AnimationState.COMPLETED);
+ }
+ super.onStop();
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view = layoutInflater.inflate(R.layout.swipe_up_down_method, viewGroup, false);
+
+ contactPuckContainer = view.findViewById(R.id.incoming_call_puck_container);
+ contactPuckBackground = (ImageView) view.findViewById(R.id.incoming_call_puck_bg);
+ contactPuckIcon = (ImageView) view.findViewById(R.id.incoming_call_puck_icon);
+ swipeToAnswerText = (TextView) view.findViewById(R.id.incoming_swipe_to_answer_text);
+ swipeToRejectText = (TextView) view.findViewById(R.id.incoming_swipe_to_reject_text);
+ incomingDisconnectText = view.findViewById(R.id.incoming_will_disconnect_text);
+ incomingDisconnectText.setAlpha(incomingWillDisconnect ? 1 : 0);
+
+ view.setAccessibilityDelegate(
+ new AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.addAction(
+ new AccessibilityAction(
+ R.id.accessibility_action_answer, getString(R.string.call_incoming_answer)));
+ info.addAction(
+ new AccessibilityAction(
+ R.id.accessibility_action_decline, getString(R.string.call_incoming_decline)));
+ }
+
+ @Override
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ if (action == R.id.accessibility_action_answer) {
+ performAccept();
+ return true;
+ } else if (action == R.id.accessibility_action_decline) {
+ performReject();
+ return true;
+ }
+ return super.performAccessibilityAction(host, action, args);
+ }
+ });
+
+ swipeProgress = 0;
+
+ updateContactPuck();
+
+ touchHandler = FlingUpDownTouchHandler.attach(view, this, falsingManager);
+
+ answerHint =
+ new AnswerHintFactory(new EventPayloadLoaderImpl())
+ .create(getContext(), ANIMATE_DURATION_LONG_MILLIS, BOUNCE_ANIMATION_DELAY);
+ answerHint.onCreateView(
+ layoutInflater,
+ (ViewGroup) view.findViewById(R.id.hint_container),
+ contactPuckContainer,
+ swipeToAnswerText);
+ return view;
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle bundle) {
+ super.onViewCreated(view, bundle);
+ setAnimationState(AnimationState.ENTRY);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if (touchHandler != null) {
+ touchHandler.detach();
+ touchHandler = null;
+ }
+ }
+
+ @Override
+ public void onProgressChanged(@FloatRange(from = -1f, to = 1f) float progress) {
+ swipeProgress = progress;
+ if (animationState == AnimationState.SWIPE && getContext() != null && isVisible()) {
+ updateSwipeTextAndPuckForTouch();
+ }
+ }
+
+ @Override
+ public void onTrackingStart() {
+ setAnimationState(AnimationState.SWIPE);
+ }
+
+ @Override
+ public void onTrackingStopped() {}
+
+ @Override
+ public void onMoveReset(boolean showHint) {
+ if (showHint) {
+ showSwipeHint();
+ } else {
+ setAnimationState(AnimationState.BOUNCE);
+ }
+ resetTouchState();
+ getParent().resetAnswerProgress();
+ }
+
+ @Override
+ public void onMoveFinish(boolean accept) {
+ touchHandler.setTouchEnabled(false);
+ answerHint.onAnswered();
+ if (accept) {
+ performAccept();
+ } else {
+ performReject();
+ }
+ }
+
+ @Override
+ public boolean shouldUseFalsing(@NonNull MotionEvent downEvent) {
+ if (contactPuckContainer == null) {
+ return false;
+ }
+
+ float puckCenterX = contactPuckContainer.getX() + (contactPuckContainer.getWidth() / 2);
+ float puckCenterY = contactPuckContainer.getY() + (contactPuckContainer.getHeight() / 2);
+ double radius = contactPuckContainer.getHeight() / 2;
+
+ // Squaring a number is more performant than taking a sqrt, so we compare the square of the
+ // distance with the square of the radius.
+ double distSq =
+ Math.pow(downEvent.getX() - puckCenterX, 2) + Math.pow(downEvent.getY() - puckCenterY, 2);
+ return distSq >= Math.pow(radius, 2);
+ }
+
+ @Override
+ public void setContactPhoto(Drawable contactPhoto) {
+ this.contactPhoto = contactPhoto;
+
+ updateContactPuck();
+ }
+
+ private void updateContactPuck() {
+ if (contactPuckIcon == null) {
+ return;
+ }
+ if (getParent().isVideoCall()) {
+ contactPuckIcon.setImageResource(R.drawable.quantum_ic_videocam_white_24);
+ } else {
+ contactPuckIcon.setImageResource(R.drawable.quantum_ic_call_white_24);
+ }
+
+ int size =
+ contactPuckBackground
+ .getResources()
+ .getDimensionPixelSize(
+ shouldShowPhotoInPuck()
+ ? R.dimen.answer_contact_puck_size_photo
+ : R.dimen.answer_contact_puck_size_no_photo);
+ contactPuckBackground.setImageDrawable(
+ shouldShowPhotoInPuck()
+ ? makeRoundedDrawable(contactPuckBackground.getContext(), contactPhoto, size)
+ : null);
+ ViewGroup.LayoutParams contactPuckParams = contactPuckBackground.getLayoutParams();
+ contactPuckParams.height = size;
+ contactPuckParams.width = size;
+ contactPuckBackground.setLayoutParams(contactPuckParams);
+ contactPuckIcon.setAlpha(shouldShowPhotoInPuck() ? 0f : 1f);
+ }
+
+ private Drawable makeRoundedDrawable(Context context, Drawable contactPhoto, int size) {
+ return DrawableConverter.getRoundedDrawable(context, contactPhoto, size, size);
+ }
+
+ private boolean shouldShowPhotoInPuck() {
+ return getParent().isVideoCall() && contactPhoto != null;
+ }
+
+ @Override
+ public void setHintText(@Nullable CharSequence hintText) {
+ if (hintText == null) {
+ swipeToAnswerText.setText(R.string.call_incoming_swipe_to_answer);
+ swipeToRejectText.setText(R.string.call_incoming_swipe_to_reject);
+ } else {
+ swipeToAnswerText.setText(hintText);
+ swipeToRejectText.setText(null);
+ }
+ }
+
+ @Override
+ public void setShowIncomingWillDisconnect(boolean incomingWillDisconnect) {
+ this.incomingWillDisconnect = incomingWillDisconnect;
+ if (incomingDisconnectText != null) {
+ incomingDisconnectText.animate().alpha(incomingWillDisconnect ? 1 : 0);
+ }
+ }
+
+ private void showSwipeHint() {
+ setAnimationState(AnimationState.HINT);
+ }
+
+ private void updateSwipeTextAndPuckForTouch() {
+ // Clamp progress value between -1 and 1.
+ final float clampedProgress = MathUtil.clamp(swipeProgress, -1 /* min */, 1 /* max */);
+ final float positiveAdjustedProgress = Math.abs(clampedProgress);
+ final boolean isAcceptingFlow = clampedProgress >= 0;
+
+ // Cancel view property animators on views we're about to mutate
+ swipeToAnswerText.animate().cancel();
+ contactPuckIcon.animate().cancel();
+
+ // Since the animation progression is controlled by user gesture instead of real timeline, the
+ // spec timeline can be divided into 9 slots. Each slot is equivalent to 83ms in the spec.
+ // Therefore, we use 9 slots of 83ms to map user gesture into the spec timeline.
+ final float progressSlots = 9;
+
+ // Fade out the "swipe up to answer". It only takes 1 slot to complete the fade.
+ float swipeTextAlpha = Math.max(0, 1 - Math.abs(clampedProgress) * progressSlots);
+ fadeToward(swipeToAnswerText, swipeTextAlpha);
+ // Fade out the "swipe down to dismiss" at the same time. Don't ever increase its alpha
+ fadeToward(swipeToRejectText, Math.min(swipeTextAlpha, swipeToRejectText.getAlpha()));
+ // Fade out the "incoming will disconnect" text
+ fadeToward(incomingDisconnectText, incomingWillDisconnect ? swipeTextAlpha : 0);
+
+ // Move swipe text back to zero.
+ moveTowardX(swipeToAnswerText, 0 /* newX */);
+ moveTowardY(swipeToAnswerText, 0 /* newY */);
+
+ // Animate puck color
+ @ColorInt
+ int destPuckColor =
+ getContext()
+ .getColor(
+ isAcceptingFlow ? R.color.call_accept_background : R.color.call_hangup_background);
+ destPuckColor =
+ ColorUtils.setAlphaComponent(destPuckColor, (int) (0xFF * positiveAdjustedProgress));
+ contactPuckBackground.setBackgroundTintList(ColorStateList.valueOf(destPuckColor));
+ contactPuckBackground.setBackgroundTintMode(Mode.SRC_ATOP);
+ contactPuckBackground.setColorFilter(destPuckColor);
+
+ // Animate decline icon
+ if (isAcceptingFlow || getParent().isVideoCall()) {
+ rotateToward(contactPuckIcon, 0f);
+ } else {
+ rotateToward(contactPuckIcon, positiveAdjustedProgress * ICON_END_CALL_ROTATION_DEGREES);
+ }
+
+ // Fade in icon
+ if (shouldShowPhotoInPuck()) {
+ fadeToward(contactPuckIcon, positiveAdjustedProgress);
+ }
+ float iconProgress = Math.min(1f, positiveAdjustedProgress * 4);
+ @ColorInt
+ int iconColor =
+ ColorUtils.setAlphaComponent(
+ contactPuckIcon.getContext().getColor(R.color.incoming_answer_icon),
+ (int) (0xFF * (1 - iconProgress)));
+ contactPuckIcon.setImageTintList(ColorStateList.valueOf(iconColor));
+
+ // Move puck.
+ if (isAcceptingFlow) {
+ moveTowardY(
+ contactPuckContainer,
+ -clampedProgress * DpUtil.dpToPx(getContext(), SWIPE_TO_ANSWER_MAX_TRANSLATION_Y_DP));
+ } else {
+ moveTowardY(
+ contactPuckContainer,
+ -clampedProgress * DpUtil.dpToPx(getContext(), SWIPE_TO_REJECT_MAX_TRANSLATION_Y_DP));
+ }
+
+ getParent().onAnswerProgressUpdate(clampedProgress);
+ }
+
+ private void startSwipeToAnswerSwipeAnimation() {
+ LogUtil.i("FlingUpDownMethod.startSwipeToAnswerSwipeAnimation", "Start swipe animation.");
+ resetTouchState();
+ endAnimation();
+ }
+
+ private void setPuckTouchState() {
+ contactPuckBackground.setActivated(touchHandler.isTracking());
+ }
+
+ private void resetTouchState() {
+ if (getContext() == null) {
+ // State will be reset in onStart(), so just abort.
+ return;
+ }
+ contactPuckContainer.animate().scaleX(1 /* scaleX */);
+ contactPuckContainer.animate().scaleY(1 /* scaleY */);
+ contactPuckBackground.animate().scaleX(1 /* scaleX */);
+ contactPuckBackground.animate().scaleY(1 /* scaleY */);
+ contactPuckBackground.setBackgroundTintList(null);
+ contactPuckBackground.setColorFilter(null);
+ contactPuckIcon.setImageTintList(
+ ColorStateList.valueOf(getContext().getColor(R.color.incoming_answer_icon)));
+ contactPuckIcon.animate().rotation(0);
+
+ getParent().resetAnswerProgress();
+ setPuckTouchState();
+
+ final float alpha = 1;
+ swipeToAnswerText.animate().alpha(alpha);
+ contactPuckContainer.animate().alpha(alpha);
+ contactPuckBackground.animate().alpha(alpha);
+ contactPuckIcon.animate().alpha(shouldShowPhotoInPuck() ? 0 : alpha);
+ }
+
+ @VisibleForTesting
+ void setAnimationState(@AnimationState int state) {
+ if (state != AnimationState.HINT && animationState == state) {
+ return;
+ }
+
+ if (animationState == AnimationState.COMPLETED) {
+ LogUtil.e(
+ "FlingUpDownMethod.setAnimationState",
+ "Animation loop has completed. Cannot switch to new state: " + state);
+ return;
+ }
+
+ if (state == AnimationState.HINT || state == AnimationState.BOUNCE) {
+ if (animationState == AnimationState.SWIPE) {
+ afterSettleAnimationState = state;
+ state = AnimationState.SETTLE;
+ }
+ }
+
+ LogUtil.i("FlingUpDownMethod.setAnimationState", "animation state: " + state);
+ animationState = state;
+
+ // Start animation after the current one is finished completely.
+ View view = getView();
+ if (view != null) {
+ // As long as the fragment is added, we can start update the animation state.
+ if (isAdded() && (animationState == state)) {
+ updateAnimationState();
+ } else {
+ endAnimation();
+ }
+ }
+ }
+
+ @AnimationState
+ @VisibleForTesting
+ int getAnimationState() {
+ return animationState;
+ }
+
+ private void updateAnimationState() {
+ switch (animationState) {
+ case AnimationState.ENTRY:
+ startSwipeToAnswerEntryAnimation();
+ break;
+ case AnimationState.BOUNCE:
+ startSwipeToAnswerBounceAnimation();
+ break;
+ case AnimationState.SWIPE:
+ startSwipeToAnswerSwipeAnimation();
+ break;
+ case AnimationState.SETTLE:
+ startSwipeToAnswerSettleAnimation();
+ break;
+ case AnimationState.COMPLETED:
+ clearSwipeToAnswerUi();
+ break;
+ case AnimationState.HINT:
+ startSwipeToAnswerHintAnimation();
+ break;
+ case AnimationState.NONE:
+ default:
+ LogUtil.e(
+ "FlingUpDownMethod.updateAnimationState",
+ "Unexpected animation state: " + animationState);
+ break;
+ }
+ }
+
+ private void startSwipeToAnswerEntryAnimation() {
+ LogUtil.i("FlingUpDownMethod.startSwipeToAnswerEntryAnimation", "Swipe entry animation.");
+ endAnimation();
+
+ lockEntryAnim = new AnimatorSet();
+ Animator textUp =
+ ObjectAnimator.ofFloat(
+ swipeToAnswerText,
+ View.TRANSLATION_Y,
+ DpUtil.dpToPx(getContext(), 192 /* dp */),
+ DpUtil.dpToPx(getContext(), -20 /* dp */));
+ textUp.setDuration(ANIMATE_DURATION_NORMAL_MILLIS);
+ textUp.setInterpolator(new LinearOutSlowInInterpolator());
+
+ Animator textDown =
+ ObjectAnimator.ofFloat(
+ swipeToAnswerText,
+ View.TRANSLATION_Y,
+ DpUtil.dpToPx(getContext(), -20) /* dp */,
+ 0 /* end pos */);
+ textDown.setDuration(ANIMATE_DURATION_NORMAL_MILLIS);
+ textUp.setInterpolator(new FastOutSlowInInterpolator());
+
+ // "Swipe down to reject" text fades in with a slight translation
+ swipeToRejectText.setAlpha(0f);
+ Animator rejectTextShow =
+ ObjectAnimator.ofPropertyValuesHolder(
+ swipeToRejectText,
+ PropertyValuesHolder.ofFloat(View.ALPHA, 1f),
+ PropertyValuesHolder.ofFloat(
+ View.TRANSLATION_Y,
+ DpUtil.dpToPx(getContext(), HINT_REJECT_FADE_TRANSLATION_Y_DP),
+ 0f));
+ rejectTextShow.setInterpolator(new FastOutLinearInInterpolator());
+ rejectTextShow.setDuration(ANIMATE_DURATION_SHORT_MILLIS);
+ rejectTextShow.setStartDelay(SWIPE_TO_DECLINE_FADE_IN_DELAY_MILLIS);
+
+ Animator puckUp =
+ ObjectAnimator.ofFloat(
+ contactPuckContainer,
+ View.TRANSLATION_Y,
+ DpUtil.dpToPx(getContext(), 400 /* dp */),
+ DpUtil.dpToPx(getContext(), -12 /* dp */));
+ puckUp.setDuration(ANIMATE_DURATION_LONG_MILLIS);
+ puckUp.setInterpolator(
+ PathInterpolatorCompat.create(
+ 0 /* controlX1 */, 0 /* controlY1 */, 0 /* controlX2 */, 1 /* controlY2 */));
+
+ Animator puckDown =
+ ObjectAnimator.ofFloat(
+ contactPuckContainer,
+ View.TRANSLATION_Y,
+ DpUtil.dpToPx(getContext(), -12 /* dp */),
+ 0 /* end pos */);
+ puckDown.setDuration(ANIMATE_DURATION_NORMAL_MILLIS);
+ puckDown.setInterpolator(new FastOutSlowInInterpolator());
+
+ Animator puckScaleUp =
+ createUniformScaleAnimators(
+ contactPuckBackground,
+ 0.33f /* beginScale */,
+ 1.1f /* endScale */,
+ ANIMATE_DURATION_NORMAL_MILLIS,
+ PathInterpolatorCompat.create(
+ 0.4f /* controlX1 */, 0 /* controlY1 */, 0 /* controlX2 */, 1 /* controlY2 */));
+ Animator puckScaleDown =
+ createUniformScaleAnimators(
+ contactPuckBackground,
+ 1.1f /* beginScale */,
+ 1 /* endScale */,
+ ANIMATE_DURATION_NORMAL_MILLIS,
+ new FastOutSlowInInterpolator());
+
+ // Upward animation chain.
+ lockEntryAnim.play(textUp).with(puckScaleUp).with(puckUp);
+
+ // Downward animation chain.
+ lockEntryAnim.play(textDown).with(puckDown).with(puckScaleDown).after(puckUp);
+
+ lockEntryAnim.play(rejectTextShow).after(puckUp);
+
+ // Add vibration animation.
+ addVibrationAnimator(lockEntryAnim);
+
+ lockEntryAnim.addListener(
+ new AnimatorListenerAdapter() {
+
+ public boolean canceled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ canceled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (!canceled) {
+ onEntryAnimationDone();
+ }
+ }
+ });
+ lockEntryAnim.start();
+ }
+
+ @VisibleForTesting
+ void onEntryAnimationDone() {
+ LogUtil.i("FlingUpDownMethod.onEntryAnimationDone", "Swipe entry anim ends.");
+ if (animationState == AnimationState.ENTRY) {
+ setAnimationState(AnimationState.BOUNCE);
+ }
+ }
+
+ private void startSwipeToAnswerBounceAnimation() {
+ LogUtil.i("FlingUpDownMethod.startSwipeToAnswerBounceAnimation", "Swipe bounce animation.");
+ endAnimation();
+
+ if (ViewUtil.areAnimationsDisabled(getContext())) {
+ swipeToAnswerText.setTranslationY(0);
+ contactPuckContainer.setTranslationY(0);
+ contactPuckBackground.setScaleY(1f);
+ contactPuckBackground.setScaleX(1f);
+ swipeToRejectText.setAlpha(1f);
+ swipeToRejectText.setTranslationY(0);
+ return;
+ }
+
+ lockBounceAnim = createBreatheAnimation();
+
+ answerHint.onBounceStart();
+ lockBounceAnim.addListener(
+ new AnimatorListenerAdapter() {
+ boolean firstPass = true;
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (getContext() != null
+ && lockBounceAnim != null
+ && animationState == AnimationState.BOUNCE) {
+ // AnimatorSet doesn't have repeat settings. Instead, we start a new one after the
+ // previous set is completed, until endAnimation is called.
+ LogUtil.v("FlingUpDownMethod.onAnimationEnd", "Bounce again.");
+
+ // If this is the first time repeating the animation, we should recreate it so its
+ // starting values will be correct
+ if (firstPass) {
+ lockBounceAnim = createBreatheAnimation();
+ lockBounceAnim.addListener(this);
+ }
+ firstPass = false;
+ answerHint.onBounceStart();
+ lockBounceAnim.start();
+ }
+ }
+ });
+ lockBounceAnim.start();
+ }
+
+ private Animator createBreatheAnimation() {
+ AnimatorSet breatheAnimation = new AnimatorSet();
+ float textOffset = DpUtil.dpToPx(getContext(), 42 /* dp */);
+ Animator textUp =
+ ObjectAnimator.ofFloat(
+ swipeToAnswerText, View.TRANSLATION_Y, 0 /* begin pos */, -textOffset);
+ textUp.setInterpolator(new FastOutSlowInInterpolator());
+ textUp.setDuration(ANIMATE_DURATION_NORMAL_MILLIS);
+
+ Animator textDown =
+ ObjectAnimator.ofFloat(swipeToAnswerText, View.TRANSLATION_Y, -textOffset, 0 /* end pos */);
+ textDown.setInterpolator(new FastOutSlowInInterpolator());
+ textDown.setDuration(ANIMATE_DURATION_NORMAL_MILLIS);
+
+ // "Swipe down to reject" text fade in
+ Animator rejectTextShow = ObjectAnimator.ofFloat(swipeToRejectText, View.ALPHA, 1f);
+ rejectTextShow.setInterpolator(new LinearOutSlowInInterpolator());
+ rejectTextShow.setDuration(ANIMATE_DURATION_SHORT_MILLIS);
+ rejectTextShow.setStartDelay(SWIPE_TO_DECLINE_FADE_IN_DELAY_MILLIS);
+
+ // reject hint text translate in
+ Animator rejectTextTranslate =
+ ObjectAnimator.ofFloat(
+ swipeToRejectText,
+ View.TRANSLATION_Y,
+ DpUtil.dpToPx(getContext(), HINT_REJECT_FADE_TRANSLATION_Y_DP),
+ 0f);
+ rejectTextTranslate.setInterpolator(new FastOutSlowInInterpolator());
+ rejectTextTranslate.setDuration(ANIMATE_DURATION_NORMAL_MILLIS);
+
+ // reject hint text fade out
+ Animator rejectTextHide = ObjectAnimator.ofFloat(swipeToRejectText, View.ALPHA, 0f);
+ rejectTextHide.setInterpolator(new FastOutLinearInInterpolator());
+ rejectTextHide.setDuration(ANIMATE_DURATION_SHORT_MILLIS);
+
+ Interpolator curve =
+ PathInterpolatorCompat.create(
+ 0.4f /* controlX1 */, 0 /* controlY1 */, 0 /* controlX2 */, 1 /* controlY2 */);
+ float puckOffset = DpUtil.dpToPx(getContext(), 42 /* dp */);
+ Animator puckUp = ObjectAnimator.ofFloat(contactPuckContainer, View.TRANSLATION_Y, -puckOffset);
+ puckUp.setInterpolator(curve);
+ puckUp.setDuration(ANIMATE_DURATION_LONG_MILLIS);
+
+ final float scale = 1.0625f;
+ Animator puckScaleUp =
+ createUniformScaleAnimators(
+ contactPuckBackground,
+ 1 /* beginScale */,
+ scale,
+ ANIMATE_DURATION_NORMAL_MILLIS,
+ curve);
+
+ Animator puckDown =
+ ObjectAnimator.ofFloat(contactPuckContainer, View.TRANSLATION_Y, 0 /* end pos */);
+ puckDown.setInterpolator(new FastOutSlowInInterpolator());
+ puckDown.setDuration(ANIMATE_DURATION_NORMAL_MILLIS);
+
+ Animator puckScaleDown =
+ createUniformScaleAnimators(
+ contactPuckBackground,
+ scale,
+ 1 /* endScale */,
+ ANIMATE_DURATION_NORMAL_MILLIS,
+ new FastOutSlowInInterpolator());
+
+ // Bounce upward animation chain.
+ breatheAnimation
+ .play(textUp)
+ .with(rejectTextHide)
+ .with(puckUp)
+ .with(puckScaleUp)
+ .after(167 /* delay */);
+
+ // Bounce downward animation chain.
+ breatheAnimation
+ .play(puckDown)
+ .with(textDown)
+ .with(puckScaleDown)
+ .with(rejectTextShow)
+ .with(rejectTextTranslate)
+ .after(puckUp);
+
+ // Add vibration animation to the animator set.
+ addVibrationAnimator(breatheAnimation);
+
+ return breatheAnimation;
+ }
+
+ private void startSwipeToAnswerSettleAnimation() {
+ endAnimation();
+
+ ObjectAnimator puckScale =
+ ObjectAnimator.ofPropertyValuesHolder(
+ contactPuckBackground,
+ PropertyValuesHolder.ofFloat(View.SCALE_X, 1),
+ PropertyValuesHolder.ofFloat(View.SCALE_Y, 1));
+ puckScale.setDuration(SETTLE_ANIMATION_DURATION_MILLIS);
+
+ ObjectAnimator iconRotation = ObjectAnimator.ofFloat(contactPuckIcon, View.ROTATION, 0);
+ iconRotation.setDuration(SETTLE_ANIMATION_DURATION_MILLIS);
+
+ ObjectAnimator swipeToAnswerTextFade =
+ createFadeAnimation(swipeToAnswerText, 1, SETTLE_ANIMATION_DURATION_MILLIS);
+
+ ObjectAnimator contactPuckContainerFade =
+ createFadeAnimation(contactPuckContainer, 1, SETTLE_ANIMATION_DURATION_MILLIS);
+
+ ObjectAnimator contactPuckBackgroundFade =
+ createFadeAnimation(contactPuckBackground, 1, SETTLE_ANIMATION_DURATION_MILLIS);
+
+ ObjectAnimator contactPuckIconFade =
+ createFadeAnimation(
+ contactPuckIcon, shouldShowPhotoInPuck() ? 0 : 1, SETTLE_ANIMATION_DURATION_MILLIS);
+
+ ObjectAnimator contactPuckTranslation =
+ ObjectAnimator.ofPropertyValuesHolder(
+ contactPuckContainer,
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 0),
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0));
+ contactPuckTranslation.setDuration(SETTLE_ANIMATION_DURATION_MILLIS);
+
+ lockSettleAnim = new AnimatorSet();
+ lockSettleAnim
+ .play(puckScale)
+ .with(iconRotation)
+ .with(swipeToAnswerTextFade)
+ .with(contactPuckContainerFade)
+ .with(contactPuckBackgroundFade)
+ .with(contactPuckIconFade)
+ .with(contactPuckTranslation);
+
+ lockSettleAnim.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ afterSettleAnimationState = AnimationState.NONE;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onSettleAnimationDone();
+ }
+ });
+
+ lockSettleAnim.start();
+ }
+
+ @VisibleForTesting
+ void onSettleAnimationDone() {
+ if (afterSettleAnimationState != AnimationState.NONE) {
+ int nextState = afterSettleAnimationState;
+ afterSettleAnimationState = AnimationState.NONE;
+ lockSettleAnim = null;
+
+ setAnimationState(nextState);
+ }
+ }
+
+ private ObjectAnimator createFadeAnimation(View target, float targetAlpha, long duration) {
+ ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(target, View.ALPHA, targetAlpha);
+ objectAnimator.setDuration(duration);
+ return objectAnimator;
+ }
+
+ private void startSwipeToAnswerHintAnimation() {
+ if (rejectHintHide != null) {
+ rejectHintHide.cancel();
+ }
+
+ endAnimation();
+ resetTouchState();
+
+ if (ViewUtil.areAnimationsDisabled(getContext())) {
+ onHintAnimationDone(false);
+ return;
+ }
+
+ lockHintAnim = new AnimatorSet();
+ float jumpOffset = DpUtil.dpToPx(getContext(), HINT_JUMP_DP);
+ float dipOffset = DpUtil.dpToPx(getContext(), HINT_DIP_DP);
+ float scaleSize = HINT_SCALE_RATIO;
+ float textOffset = jumpOffset + (scaleSize - 1) * contactPuckBackground.getHeight();
+ int shortAnimTime =
+ getContext().getResources().getInteger(android.R.integer.config_shortAnimTime);
+ int mediumAnimTime =
+ getContext().getResources().getInteger(android.R.integer.config_mediumAnimTime);
+
+ // Puck squashes to anticipate jump
+ ObjectAnimator puckAnticipate =
+ ObjectAnimator.ofPropertyValuesHolder(
+ contactPuckContainer,
+ PropertyValuesHolder.ofFloat(View.SCALE_Y, .95f),
+ PropertyValuesHolder.ofFloat(View.SCALE_X, 1.05f));
+ puckAnticipate.setRepeatCount(1);
+ puckAnticipate.setRepeatMode(ValueAnimator.REVERSE);
+ puckAnticipate.setDuration(shortAnimTime / 2);
+ puckAnticipate.setInterpolator(new DecelerateInterpolator());
+ puckAnticipate.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ contactPuckContainer.setPivotY(contactPuckContainer.getHeight());
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ contactPuckContainer.setPivotY(contactPuckContainer.getHeight() / 2);
+ }
+ });
+
+ // Ensure puck is at the right starting point for the jump
+ ObjectAnimator puckResetTranslation =
+ ObjectAnimator.ofPropertyValuesHolder(
+ contactPuckContainer,
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0),
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 0));
+ puckResetTranslation.setDuration(shortAnimTime / 2);
+ puckAnticipate.setInterpolator(new DecelerateInterpolator());
+
+ Animator textUp = ObjectAnimator.ofFloat(swipeToAnswerText, View.TRANSLATION_Y, -textOffset);
+ textUp.setInterpolator(new LinearOutSlowInInterpolator());
+ textUp.setDuration(shortAnimTime);
+
+ Animator puckUp = ObjectAnimator.ofFloat(contactPuckContainer, View.TRANSLATION_Y, -jumpOffset);
+ puckUp.setInterpolator(new LinearOutSlowInInterpolator());
+ puckUp.setDuration(shortAnimTime);
+
+ Animator puckScaleUp =
+ createUniformScaleAnimators(
+ contactPuckBackground, 1f, scaleSize, shortAnimTime, new LinearOutSlowInInterpolator());
+
+ Animator rejectHintShow =
+ ObjectAnimator.ofPropertyValuesHolder(
+ swipeToRejectText,
+ PropertyValuesHolder.ofFloat(View.ALPHA, 1f),
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0f));
+ rejectHintShow.setDuration(shortAnimTime);
+
+ Animator rejectHintDip =
+ ObjectAnimator.ofFloat(swipeToRejectText, View.TRANSLATION_Y, dipOffset);
+ rejectHintDip.setInterpolator(new LinearOutSlowInInterpolator());
+ rejectHintDip.setDuration(shortAnimTime);
+
+ Animator textDown = ObjectAnimator.ofFloat(swipeToAnswerText, View.TRANSLATION_Y, 0);
+ textDown.setInterpolator(new LinearOutSlowInInterpolator());
+ textDown.setDuration(mediumAnimTime);
+
+ Animator puckDown = ObjectAnimator.ofFloat(contactPuckContainer, View.TRANSLATION_Y, 0);
+ BounceInterpolator bounce = new BounceInterpolator();
+ puckDown.setInterpolator(bounce);
+ puckDown.setDuration(mediumAnimTime);
+
+ Animator puckScaleDown =
+ createUniformScaleAnimators(
+ contactPuckBackground, scaleSize, 1f, shortAnimTime, new LinearOutSlowInInterpolator());
+
+ Animator rejectHintUp = ObjectAnimator.ofFloat(swipeToRejectText, View.TRANSLATION_Y, 0);
+ rejectHintUp.setInterpolator(new LinearOutSlowInInterpolator());
+ rejectHintUp.setDuration(mediumAnimTime);
+
+ lockHintAnim.play(puckAnticipate).with(puckResetTranslation).before(puckUp);
+ lockHintAnim
+ .play(textUp)
+ .with(puckUp)
+ .with(puckScaleUp)
+ .with(rejectHintDip)
+ .with(rejectHintShow);
+ lockHintAnim.play(textDown).with(puckDown).with(puckScaleDown).with(rejectHintUp).after(puckUp);
+ lockHintAnim.start();
+
+ rejectHintHide = ObjectAnimator.ofFloat(swipeToRejectText, View.ALPHA, 0);
+ rejectHintHide.setStartDelay(HINT_REJECT_SHOW_DURATION_MILLIS);
+ rejectHintHide.addListener(
+ new AnimatorListenerAdapter() {
+
+ private boolean canceled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ canceled = true;
+ rejectHintHide = null;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ onHintAnimationDone(canceled);
+ }
+ });
+ rejectHintHide.start();
+ }
+
+ @VisibleForTesting
+ void onHintAnimationDone(boolean canceled) {
+ if (!canceled && animationState == AnimationState.HINT) {
+ setAnimationState(AnimationState.BOUNCE);
+ }
+ rejectHintHide = null;
+ }
+
+ private void clearSwipeToAnswerUi() {
+ LogUtil.i("FlingUpDownMethod.clearSwipeToAnswerUi", "Clear swipe animation.");
+ endAnimation();
+ swipeToAnswerText.setVisibility(View.GONE);
+ contactPuckContainer.setVisibility(View.GONE);
+ }
+
+ private void endAnimation() {
+ LogUtil.i("FlingUpDownMethod.endAnimation", "End animations.");
+ if (lockSettleAnim != null) {
+ lockSettleAnim.cancel();
+ lockSettleAnim = null;
+ }
+ if (lockBounceAnim != null) {
+ lockBounceAnim.cancel();
+ lockBounceAnim = null;
+ }
+ if (lockEntryAnim != null) {
+ lockEntryAnim.cancel();
+ lockEntryAnim = null;
+ }
+ if (lockHintAnim != null) {
+ lockHintAnim.cancel();
+ lockHintAnim = null;
+ }
+ if (rejectHintHide != null) {
+ rejectHintHide.cancel();
+ rejectHintHide = null;
+ }
+ if (vibrationAnimator != null) {
+ vibrationAnimator.end();
+ vibrationAnimator = null;
+ }
+ answerHint.onBounceEnd();
+ }
+
+ // Create an animator to scale on X/Y directions uniformly.
+ private Animator createUniformScaleAnimators(
+ View target, float begin, float end, long duration, Interpolator interpolator) {
+ ObjectAnimator animator =
+ ObjectAnimator.ofPropertyValuesHolder(
+ target,
+ PropertyValuesHolder.ofFloat(View.SCALE_X, begin, end),
+ PropertyValuesHolder.ofFloat(View.SCALE_Y, begin, end));
+ animator.setDuration(duration);
+ animator.setInterpolator(interpolator);
+ return animator;
+ }
+
+ private void addVibrationAnimator(AnimatorSet animatorSet) {
+ if (vibrationAnimator != null) {
+ vibrationAnimator.end();
+ }
+
+ // Note that we animate the value between 0 and 1, but internally VibrateInterpolator will
+ // translate it into actually X translation value.
+ vibrationAnimator =
+ ObjectAnimator.ofFloat(
+ contactPuckContainer, View.TRANSLATION_X, 0 /* begin value */, 1 /* end value */);
+ vibrationAnimator.setDuration(VIBRATION_TIME_MILLIS);
+ vibrationAnimator.setInterpolator(new VibrateInterpolator(getContext()));
+
+ animatorSet.play(vibrationAnimator).after(0 /* delay */);
+ }
+
+ private void performAccept() {
+ LogUtil.i("FlingUpDownMethod.performAccept", null);
+ swipeToAnswerText.setVisibility(View.GONE);
+ contactPuckContainer.setVisibility(View.GONE);
+
+ // Complete the animation loop.
+ setAnimationState(AnimationState.COMPLETED);
+ getParent().answerFromMethod();
+ }
+
+ private void performReject() {
+ LogUtil.i("FlingUpDownMethod.performReject", null);
+ swipeToAnswerText.setVisibility(View.GONE);
+ contactPuckContainer.setVisibility(View.GONE);
+
+ // Complete the animation loop.
+ setAnimationState(AnimationState.COMPLETED);
+ getParent().rejectFromMethod();
+ }
+
+ /** Custom interpolator class for puck vibration. */
+ private static class VibrateInterpolator implements Interpolator {
+
+ private static final long RAMP_UP_BEGIN_MS = 583;
+ private static final long RAMP_UP_DURATION_MS = 167;
+ private static final long RAMP_UP_END_MS = RAMP_UP_BEGIN_MS + RAMP_UP_DURATION_MS;
+ private static final long RAMP_DOWN_BEGIN_MS = 1_583;
+ private static final long RAMP_DOWN_DURATION_MS = 250;
+ private static final long RAMP_DOWN_END_MS = RAMP_DOWN_BEGIN_MS + RAMP_DOWN_DURATION_MS;
+ private static final long RAMP_TOTAL_TIME_MS = RAMP_DOWN_END_MS;
+ private final float ampMax;
+ private final float freqMax = 80;
+ private Interpolator sliderInterpolator = new FastOutSlowInInterpolator();
+
+ VibrateInterpolator(Context context) {
+ ampMax = DpUtil.dpToPx(context, 1 /* dp */);
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ float slider = 0;
+ float time = t * RAMP_TOTAL_TIME_MS;
+
+ // Calculate the slider value based on RAMP_UP and RAMP_DOWN times. Between RAMP_UP and
+ // RAMP_DOWN, the slider remains the maximum value of 1.
+ if (time > RAMP_UP_BEGIN_MS && time < RAMP_UP_END_MS) {
+ // Ramp up.
+ slider =
+ sliderInterpolator.getInterpolation(
+ (time - RAMP_UP_BEGIN_MS) / (float) RAMP_UP_DURATION_MS);
+ } else if ((time >= RAMP_UP_END_MS) && time <= RAMP_DOWN_BEGIN_MS) {
+ // Vibrate at maximum
+ slider = 1;
+ } else if (time > RAMP_DOWN_BEGIN_MS && time < RAMP_DOWN_END_MS) {
+ // Ramp down.
+ slider =
+ 1
+ - sliderInterpolator.getInterpolation(
+ (time - RAMP_DOWN_BEGIN_MS) / (float) RAMP_DOWN_DURATION_MS);
+ }
+
+ float ampNormalized = ampMax * slider;
+ float freqNormalized = freqMax * slider;
+
+ return (float) (ampNormalized * Math.sin(time * freqNormalized));
+ }
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/answermethod/FlingUpDownTouchHandler.java b/java/com/android/incallui/answer/impl/answermethod/FlingUpDownTouchHandler.java
new file mode 100644
index 000000000..a21073d65
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/FlingUpDownTouchHandler.java
@@ -0,0 +1,496 @@
+/*
+ * 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.incallui.answer.impl.answermethod;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.support.annotation.FloatRange;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.view.ViewConfiguration;
+import com.android.dialer.common.DpUtil;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.MathUtil;
+import com.android.incallui.answer.impl.classifier.FalsingManager;
+import com.android.incallui.answer.impl.utils.FlingAnimationUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Touch handler that keeps track of flings for {@link FlingUpDownMethod}. */
+@SuppressLint("ClickableViewAccessibility")
+class FlingUpDownTouchHandler implements OnTouchListener {
+
+ /** Callback interface for significant events with this touch handler */
+ interface OnProgressChangedListener {
+
+ /**
+ * Called when the visible answer progress has changed. Implementations should use this for
+ * animation, but should not perform accepts or rejects until {@link #onMoveFinish(boolean)} is
+ * called.
+ *
+ * @param progress float representation of the progress with +1f fully accepted, -1f fully
+ * rejected, and 0 neutral.
+ */
+ void onProgressChanged(@FloatRange(from = -1f, to = 1f) float progress);
+
+ /** Called when a touch event has started being tracked. */
+ void onTrackingStart();
+
+ /** Called when touch events stop being tracked. */
+ void onTrackingStopped();
+
+ /**
+ * Called when the progress has fully animated back to neutral. Normal resting animation should
+ * resume, possibly with a hint animation first.
+ *
+ * @param showHint {@code true} iff the hint animation should be run before resuming normal
+ * animation.
+ */
+ void onMoveReset(boolean showHint);
+
+ /**
+ * Called when the progress has animated fully to accept or reject.
+ *
+ * @param accept {@code true} if the call has been accepted, {@code false} if it has been
+ * rejected.
+ */
+ void onMoveFinish(boolean accept);
+
+ /**
+ * Determine whether this gesture should use the {@link FalsingManager} to reject accidental
+ * touches
+ *
+ * @param downEvent the MotionEvent corresponding to the start of the gesture
+ * @return {@code true} if the {@link FalsingManager} should be used to reject accidental
+ * touches for this gesture
+ */
+ boolean shouldUseFalsing(@NonNull MotionEvent downEvent);
+ }
+
+ // Progress that must be moved through to not show the hint animation after gesture completes
+ private static final float HINT_MOVE_THRESHOLD_RATIO = .1f;
+ // Dp touch needs to move upward to be considered fully accepted
+ private static final int ACCEPT_THRESHOLD_DP = 150;
+ // Dp touch needs to move downward to be considered fully rejected
+ private static final int REJECT_THRESHOLD_DP = 150;
+ // Dp touch needs to move for it to not be considered a false touch (if FalsingManager is not
+ // enabled)
+ private static final int FALSING_THRESHOLD_DP = 40;
+
+ // Progress at which a fling in the opposite direction will recenter instead of
+ // accepting/rejecting
+ private static final float PROGRESS_FLING_RECENTER = .1f;
+
+ // Progress at which a slow swipe would continue toward accept/reject after the
+ // touch has been let go, otherwise will recenter
+ private static final float PROGRESS_SWIPE_RECENTER = .8f;
+
+ private static final float REJECT_FLING_THRESHOLD_MODIFIER = 2f;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({FlingTarget.CENTER, FlingTarget.ACCEPT, FlingTarget.REJECT})
+ private @interface FlingTarget {
+ int CENTER = 0;
+ int ACCEPT = 1;
+ int REJECT = -1;
+ }
+
+ /**
+ * Create a new FlingUpDownTouchHandler and attach it to the target. Will call {@link
+ * View#setOnTouchListener(OnTouchListener)} before returning.
+ *
+ * @param target View whose touches are to be listened to
+ * @param listener Callback to listen to major events
+ * @param falsingManager FalsingManager to identify false touches
+ * @return the instance of FlingUpDownTouchHandler that has been added as a touch listener
+ */
+ public static FlingUpDownTouchHandler attach(
+ @NonNull View target,
+ @NonNull OnProgressChangedListener listener,
+ @Nullable FalsingManager falsingManager) {
+ FlingUpDownTouchHandler handler = new FlingUpDownTouchHandler(target, listener, falsingManager);
+ target.setOnTouchListener(handler);
+ return handler;
+ }
+
+ @NonNull private final View target;
+ @NonNull private final OnProgressChangedListener listener;
+
+ private VelocityTracker velocityTracker;
+ private FlingAnimationUtils flingAnimationUtils;
+
+ private boolean touchEnabled = true;
+ private boolean flingEnabled = true;
+ private float currentProgress;
+ private boolean tracking;
+
+ private boolean motionAborted;
+ private boolean touchSlopExceeded;
+ private boolean hintDistanceExceeded;
+ private int trackingPointer;
+ private Animator progressAnimator;
+
+ private float touchSlop;
+ private float initialTouchY;
+ private float acceptThresholdY;
+ private float rejectThresholdY;
+ private float zeroY;
+
+ private boolean touchAboveFalsingThreshold;
+ private float falsingThresholdPx;
+ private boolean touchUsesFalsing;
+
+ private final float acceptThresholdPx;
+ private final float rejectThresholdPx;
+ private final float deadZoneTopPx;
+
+ @Nullable private final FalsingManager falsingManager;
+
+ private FlingUpDownTouchHandler(
+ @NonNull View target,
+ @NonNull OnProgressChangedListener listener,
+ @Nullable FalsingManager falsingManager) {
+ this.target = target;
+ this.listener = listener;
+ Context context = target.getContext();
+ touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ flingAnimationUtils = new FlingAnimationUtils(context, .6f);
+ falsingThresholdPx = DpUtil.dpToPx(context, FALSING_THRESHOLD_DP);
+ acceptThresholdPx = DpUtil.dpToPx(context, ACCEPT_THRESHOLD_DP);
+ rejectThresholdPx = DpUtil.dpToPx(context, REJECT_THRESHOLD_DP);
+
+ deadZoneTopPx =
+ Math.max(
+ context.getResources().getDimension(R.dimen.answer_swipe_dead_zone_top),
+ acceptThresholdPx);
+ this.falsingManager = falsingManager;
+ }
+
+ /** Returns {@code true} iff a touch is being tracked */
+ public boolean isTracking() {
+ return tracking;
+ }
+
+ /**
+ * Sets whether touch events will continue to be listened to
+ *
+ * @param touchEnabled whether future touch events will be listened to
+ */
+ public void setTouchEnabled(boolean touchEnabled) {
+ this.touchEnabled = touchEnabled;
+ }
+
+ /**
+ * Sets whether fling velocity is used to affect accept/reject behavior
+ *
+ * @param flingEnabled whether fling velocity will be used when determining whether to
+ * accept/reject or recenter
+ */
+ public void setFlingEnabled(boolean flingEnabled) {
+ this.flingEnabled = flingEnabled;
+ }
+
+ public void detach() {
+ cancelProgressAnimator();
+ setTouchEnabled(false);
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (falsingManager != null) {
+ falsingManager.onTouchEvent(event);
+ }
+ if (!touchEnabled) {
+ return false;
+ }
+ if (motionAborted && (event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
+ return false;
+ }
+
+ int pointerIndex = event.findPointerIndex(trackingPointer);
+ if (pointerIndex < 0) {
+ pointerIndex = 0;
+ trackingPointer = event.getPointerId(pointerIndex);
+ }
+ final float pointerY = event.getY(pointerIndex);
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ if (pointerY < deadZoneTopPx) {
+ return false;
+ }
+ motionAborted = false;
+ startMotion(pointerY, false, currentProgress);
+ touchAboveFalsingThreshold = false;
+ touchUsesFalsing = listener.shouldUseFalsing(event);
+ if (velocityTracker == null) {
+ initVelocityTracker();
+ }
+ trackMovement(event);
+ cancelProgressAnimator();
+ touchSlopExceeded = progressAnimator != null;
+ onTrackingStarted();
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ final int upPointer = event.getPointerId(event.getActionIndex());
+ if (trackingPointer == upPointer) {
+ // gesture is ongoing, find a new pointer to track
+ int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+ float newY = event.getY(newIndex);
+ trackingPointer = event.getPointerId(newIndex);
+ startMotion(newY, true, currentProgress);
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ motionAborted = true;
+ endMotionEvent(event, pointerY, true);
+ return false;
+ case MotionEvent.ACTION_MOVE:
+ float deltaY = pointerY - initialTouchY;
+
+ if (Math.abs(deltaY) > touchSlop) {
+ touchSlopExceeded = true;
+ }
+ if (Math.abs(deltaY) >= falsingThresholdPx) {
+ touchAboveFalsingThreshold = true;
+ }
+ setCurrentProgress(pointerYToProgress(pointerY));
+ trackMovement(event);
+ break;
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ trackMovement(event);
+ endMotionEvent(event, pointerY, false);
+ }
+ return true;
+ }
+
+ private void endMotionEvent(MotionEvent event, float pointerY, boolean forceCancel) {
+ trackingPointer = -1;
+ if ((tracking && touchSlopExceeded)
+ || Math.abs(pointerY - initialTouchY) > touchSlop
+ || event.getActionMasked() == MotionEvent.ACTION_CANCEL
+ || forceCancel) {
+ float vel = 0f;
+ float vectorVel = 0f;
+ if (velocityTracker != null) {
+ velocityTracker.computeCurrentVelocity(1000);
+ vel = velocityTracker.getYVelocity();
+ vectorVel =
+ Math.copySign(
+ (float) Math.hypot(velocityTracker.getXVelocity(), velocityTracker.getYVelocity()),
+ vel);
+ }
+
+ boolean falseTouch = isFalseTouch();
+ boolean forceRecenter =
+ falseTouch
+ || !touchSlopExceeded
+ || forceCancel
+ || event.getActionMasked() == MotionEvent.ACTION_CANCEL;
+
+ @FlingTarget
+ int target = forceRecenter ? FlingTarget.CENTER : getFlingTarget(pointerY, vectorVel);
+
+ fling(vel, target, falseTouch);
+ onTrackingStopped();
+ } else {
+ onTrackingStopped();
+ setCurrentProgress(0);
+ onMoveEnded();
+ }
+
+ if (velocityTracker != null) {
+ velocityTracker.recycle();
+ velocityTracker = null;
+ }
+ }
+
+ @FlingTarget
+ private int getFlingTarget(float pointerY, float vectorVel) {
+ float progress = pointerYToProgress(pointerY);
+
+ float minVelocityPxPerSecond = flingAnimationUtils.getMinVelocityPxPerSecond();
+ if (vectorVel > 0) {
+ minVelocityPxPerSecond *= REJECT_FLING_THRESHOLD_MODIFIER;
+ }
+ if (!flingEnabled || Math.abs(vectorVel) < minVelocityPxPerSecond) {
+ // Not a fling
+ if (Math.abs(progress) > PROGRESS_SWIPE_RECENTER) {
+ // Progress near one of the edges
+ return progress > 0 ? FlingTarget.ACCEPT : FlingTarget.REJECT;
+ } else {
+ return FlingTarget.CENTER;
+ }
+ }
+
+ boolean sameDirection = vectorVel < 0 == progress > 0;
+ if (!sameDirection && Math.abs(progress) >= PROGRESS_FLING_RECENTER) {
+ // Being flung back toward center
+ return FlingTarget.CENTER;
+ }
+ // Flung toward an edge
+ return vectorVel < 0 ? FlingTarget.ACCEPT : FlingTarget.REJECT;
+ }
+
+ @FloatRange(from = -1f, to = 1f)
+ private float pointerYToProgress(float pointerY) {
+ boolean pointerAboveZero = pointerY > zeroY;
+ float nearestThreshold = pointerAboveZero ? rejectThresholdY : acceptThresholdY;
+
+ float absoluteProgress = (pointerY - zeroY) / (nearestThreshold - zeroY);
+ return MathUtil.clamp(absoluteProgress * (pointerAboveZero ? -1 : 1), -1f, 1f);
+ }
+
+ private boolean isFalseTouch() {
+ if (falsingManager != null && falsingManager.isEnabled()) {
+ if (falsingManager.isFalseTouch()) {
+ if (touchUsesFalsing) {
+ LogUtil.i("FlingUpDownTouchHandler.isFalseTouch", "rejecting false touch");
+ return true;
+ } else {
+ LogUtil.i(
+ "FlingUpDownTouchHandler.isFalseTouch",
+ "Suspected false touch, but not using false touch rejection for this gesture");
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ return !touchAboveFalsingThreshold;
+ }
+
+ private void trackMovement(MotionEvent event) {
+ if (velocityTracker != null) {
+ velocityTracker.addMovement(event);
+ }
+ }
+
+ private void fling(float velocity, @FlingTarget int target, boolean centerBecauseOfFalsing) {
+ ValueAnimator animator = createProgressAnimator(target);
+ if (target == FlingTarget.CENTER) {
+ flingAnimationUtils.apply(animator, currentProgress, target, velocity);
+ } else {
+ flingAnimationUtils.applyDismissing(animator, currentProgress, target, velocity, 1);
+ }
+ if (target == FlingTarget.CENTER && centerBecauseOfFalsing) {
+ velocity = 0;
+ }
+ if (velocity == 0) {
+ animator.setDuration(350);
+ }
+
+ animator.addListener(
+ new AnimatorListenerAdapter() {
+ boolean canceled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ canceled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ progressAnimator = null;
+ if (!canceled) {
+ onMoveEnded();
+ }
+ }
+ });
+ progressAnimator = animator;
+ animator.start();
+ }
+
+ private void onMoveEnded() {
+ if (currentProgress == 0) {
+ listener.onMoveReset(!hintDistanceExceeded);
+ } else {
+ listener.onMoveFinish(currentProgress > 0);
+ }
+ }
+
+ private ValueAnimator createProgressAnimator(float targetProgress) {
+ ValueAnimator animator = ValueAnimator.ofFloat(currentProgress, targetProgress);
+ animator.addUpdateListener(
+ new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ setCurrentProgress((Float) animation.getAnimatedValue());
+ }
+ });
+ return animator;
+ }
+
+ private void initVelocityTracker() {
+ if (velocityTracker != null) {
+ velocityTracker.recycle();
+ }
+ velocityTracker = VelocityTracker.obtain();
+ }
+
+ private void startMotion(float newY, boolean startTracking, float startProgress) {
+ initialTouchY = newY;
+ hintDistanceExceeded = false;
+
+ if (startProgress <= .25) {
+ acceptThresholdY = Math.max(0, initialTouchY - acceptThresholdPx);
+ rejectThresholdY = Math.min(target.getHeight(), initialTouchY + rejectThresholdPx);
+ zeroY = initialTouchY;
+ }
+
+ if (startTracking) {
+ touchSlopExceeded = true;
+ onTrackingStarted();
+ setCurrentProgress(startProgress);
+ }
+ }
+
+ private void onTrackingStarted() {
+ tracking = true;
+ listener.onTrackingStart();
+ }
+
+ private void onTrackingStopped() {
+ tracking = false;
+ listener.onTrackingStopped();
+ }
+
+ private void cancelProgressAnimator() {
+ if (progressAnimator != null) {
+ progressAnimator.cancel();
+ }
+ }
+
+ private void setCurrentProgress(float progress) {
+ if (Math.abs(progress) > HINT_MOVE_THRESHOLD_RATIO) {
+ hintDistanceExceeded = true;
+ }
+ currentProgress = progress;
+ listener.onProgressChanged(progress);
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/answermethod/TwoButtonMethod.java b/java/com/android/incallui/answer/impl/answermethod/TwoButtonMethod.java
new file mode 100644
index 000000000..67b1b9689
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/TwoButtonMethod.java
@@ -0,0 +1,268 @@
+/*
+ * 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.incallui.answer.impl.answermethod;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.os.Bundle;
+import android.support.annotation.FloatRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.compat.ActivityCompat;
+import com.android.incallui.answer.impl.answermethod.FlingUpDownTouchHandler.OnProgressChangedListener;
+import com.android.incallui.util.AccessibilityUtil;
+
+/** Answer method that shows two buttons for answer/reject. */
+public class TwoButtonMethod extends AnswerMethod
+ implements OnClickListener, AnimatorUpdateListener {
+
+ private static final String STATE_HINT_TEXT = "hintText";
+ private static final String STATE_INCOMING_WILL_DISCONNECT = "incomingWillDisconnect";
+
+ private View answerButton;
+ private View answerLabel;
+ private View declineButton;
+ private View declineLabel;
+ private TextView hintTextView;
+ private boolean incomingWillDisconnect;
+ private boolean buttonClicked;
+ private CharSequence hintText;
+ @Nullable private FlingUpDownTouchHandler touchHandler;
+
+ @Override
+ public void onCreate(@Nullable Bundle bundle) {
+ super.onCreate(bundle);
+ if (bundle != null) {
+ incomingWillDisconnect = bundle.getBoolean(STATE_INCOMING_WILL_DISCONNECT);
+ hintText = bundle.getCharSequence(STATE_HINT_TEXT);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle bundle) {
+ super.onSaveInstanceState(bundle);
+ bundle.putBoolean(STATE_INCOMING_WILL_DISCONNECT, incomingWillDisconnect);
+ bundle.putCharSequence(STATE_HINT_TEXT, hintText);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view = layoutInflater.inflate(R.layout.two_button_method, viewGroup, false);
+
+ hintTextView = (TextView) view.findViewById(R.id.two_button_hint_text);
+ updateHintText();
+
+ answerButton = view.findViewById(R.id.two_button_answer_button);
+ answerLabel = view.findViewById(R.id.two_button_answer_label);
+ declineButton = view.findViewById(R.id.two_button_decline_button);
+ declineLabel = view.findViewById(R.id.two_button_decline_label);
+
+ boolean showLabels = getResources().getBoolean(R.bool.two_button_show_button_labels);
+ answerLabel.setVisibility(showLabels ? View.VISIBLE : View.GONE);
+ declineLabel.setVisibility(showLabels ? View.VISIBLE : View.GONE);
+
+ answerButton.setOnClickListener(this);
+ declineButton.setOnClickListener(this);
+
+ if (AccessibilityUtil.isTouchExplorationEnabled(getContext())) {
+ /* Falsing already handled by AccessibilityManager */
+ touchHandler =
+ FlingUpDownTouchHandler.attach(
+ view,
+ new OnProgressChangedListener() {
+ @Override
+ public void onProgressChanged(@FloatRange(from = -1f, to = 1f) float progress) {}
+
+ @Override
+ public void onTrackingStart() {}
+
+ @Override
+ public void onTrackingStopped() {}
+
+ @Override
+ public void onMoveReset(boolean showHint) {}
+
+ @Override
+ public void onMoveFinish(boolean accept) {
+ if (accept) {
+ answerCall();
+ } else {
+ rejectCall();
+ }
+ }
+
+ @Override
+ public boolean shouldUseFalsing(@NonNull MotionEvent downEvent) {
+ return false;
+ }
+ },
+ null /* Falsing already handled by AccessibilityManager */);
+ touchHandler.setFlingEnabled(false);
+ }
+ return view;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if (touchHandler != null) {
+ touchHandler.detach();
+ touchHandler = null;
+ }
+ }
+
+ @Override
+ public void setHintText(@Nullable CharSequence hintText) {
+ this.hintText = hintText;
+ updateHintText();
+ }
+
+ @Override
+ public void setShowIncomingWillDisconnect(boolean incomingWillDisconnect) {
+ this.incomingWillDisconnect = incomingWillDisconnect;
+ updateHintText();
+ }
+
+ private void updateHintText() {
+ if (hintTextView == null) {
+ return;
+ }
+ hintTextView.setVisibility(
+ ActivityCompat.isInMultiWindowMode(getActivity()) ? View.GONE : View.VISIBLE);
+ if (!TextUtils.isEmpty(hintText) && !buttonClicked) {
+ hintTextView.setText(hintText);
+ hintTextView.animate().alpha(1f).start();
+ } else if (incomingWillDisconnect && !buttonClicked) {
+ hintTextView.setText(R.string.call_incoming_will_disconnect);
+ hintTextView.animate().alpha(1f).start();
+ } else {
+ hintTextView.animate().alpha(0f).start();
+ }
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view == answerButton) {
+ answerCall();
+ LogUtil.v("TwoButtonMethod.onClick", "Call answered");
+ } else if (view == declineButton) {
+ rejectCall();
+ LogUtil.v("TwoButtonMethod.onClick", "two_buttonMethod Call rejected");
+ } else {
+ Assert.fail("Unknown click from view: " + view);
+ }
+ buttonClicked = true;
+ }
+
+ private void answerCall() {
+ ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+ animator.addUpdateListener(this);
+ animator.addListener(
+ new AnimatorListenerAdapter() {
+ private boolean canceled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ canceled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!canceled) {
+ getParent().answerFromMethod();
+ }
+ }
+ });
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.play(animator).with(createViewHideAnimation());
+ animatorSet.start();
+ }
+
+ private void rejectCall() {
+ ValueAnimator animator = ValueAnimator.ofFloat(0, -1);
+ animator.addUpdateListener(this);
+ animator.addListener(
+ new AnimatorListenerAdapter() {
+ private boolean canceled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ canceled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!canceled) {
+ getParent().rejectFromMethod();
+ }
+ }
+ });
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.play(animator).with(createViewHideAnimation());
+ animatorSet.start();
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ getParent().onAnswerProgressUpdate(((float) animation.getAnimatedValue()));
+ }
+
+ private Animator createViewHideAnimation() {
+ ObjectAnimator answerButtonHide =
+ ObjectAnimator.ofPropertyValuesHolder(
+ answerButton,
+ PropertyValuesHolder.ofFloat(View.SCALE_X, 0f),
+ PropertyValuesHolder.ofFloat(View.SCALE_Y, 0f));
+
+ ObjectAnimator declineButtonHide =
+ ObjectAnimator.ofPropertyValuesHolder(
+ declineButton,
+ PropertyValuesHolder.ofFloat(View.SCALE_X, 0f),
+ PropertyValuesHolder.ofFloat(View.SCALE_Y, 0f));
+
+ ObjectAnimator answerLabelHide = ObjectAnimator.ofFloat(answerLabel, View.ALPHA, 0f);
+
+ ObjectAnimator declineLabelHide = ObjectAnimator.ofFloat(declineLabel, View.ALPHA, 0f);
+
+ ObjectAnimator hintHide = ObjectAnimator.ofFloat(hintTextView, View.ALPHA, 0f);
+
+ AnimatorSet hideSet = new AnimatorSet();
+ hideSet
+ .play(answerButtonHide)
+ .with(declineButtonHide)
+ .with(answerLabelHide)
+ .with(declineLabelHide)
+ .with(hintHide);
+ return hideSet;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/answermethod/res/drawable/call_answer.xml b/java/com/android/incallui/answer/impl/answermethod/res/drawable/call_answer.xml
new file mode 100644
index 000000000..451c862fa
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/res/drawable/call_answer.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:viewportHeight="32.0"
+ android:viewportWidth="32.0"
+ android:width="24dp">
+ <group
+ android:name="rotationGroup"
+ android:pivotX="12"
+ android:pivotY="12"
+ android:translateX="4"
+ android:translateY="4"
+ android:rotation="0"
+ >
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z"/>
+ </group>
+</vector>
diff --git a/java/com/android/incallui/answer/impl/answermethod/res/drawable/circular_background.xml b/java/com/android/incallui/answer/impl/answermethod/res/drawable/circular_background.xml
new file mode 100644
index 000000000..938ddc2be
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/res/drawable/circular_background.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="#FFFFFFFF"/>
+</shape>
diff --git a/java/com/android/incallui/answer/impl/answermethod/res/layout/swipe_up_down_method.xml b/java/com/android/incallui/answer/impl/answermethod/res/layout/swipe_up_down_method.xml
new file mode 100644
index 000000000..78e097958
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/res/layout/swipe_up_down_method.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/answer_swipe_dead_zone_sides"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layout_marginEnd="@dimen/answer_swipe_dead_zone_sides">
+ <LinearLayout
+ android:id="@+id/incoming_swipe_to_answer_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:accessibilityLiveRegion="polite"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:gravity="center_horizontal|bottom"
+ android:orientation="vertical"
+ android:visibility="visible">
+ <TextView
+ android:id="@+id/incoming_will_disconnect_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="116dp"
+ android:layout_gravity="center_horizontal"
+ android:alpha="0"
+ android:text="@string/call_incoming_will_disconnect"
+ android:textColor="@color/blue_grey_100"
+ android:textSize="16sp"
+ tools:alpha="1"/>
+ <TextView
+ android:id="@+id/incoming_swipe_to_answer_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="18dp"
+ android:layout_gravity="center_horizontal"
+ android:focusable="false"
+ android:text="@string/call_incoming_swipe_to_answer"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance.Hint"/>
+
+ <FrameLayout
+ android:id="@+id/incoming_call_puck_container"
+ android:layout_width="@dimen/answer_contact_puck_size_photo"
+ android:layout_height="@dimen/answer_contact_puck_size_photo"
+ android:layout_marginBottom="10dp"
+ android:layout_gravity="center_horizontal"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:contentDescription="@string/a11y_incoming_call_swipe_to_answer">
+
+ <!-- Puck background and icon are hosted in the separated views to animate separately. -->
+ <ImageView
+ android:id="@+id/incoming_call_puck_bg"
+ android:layout_width="@dimen/answer_contact_puck_size_no_photo"
+ android:layout_height="@dimen/answer_contact_puck_size_no_photo"
+ android:layout_gravity="center"
+ android:background="@drawable/circular_background"
+ android:contentDescription="@null"
+ android:duplicateParentState="true"
+ android:elevation="8dp"
+ android:focusable="false"
+ android:stateListAnimator="@animator/activated_button_elevation"/>
+
+ <ImageView
+ android:id="@+id/incoming_call_puck_icon"
+ android:layout_width="30dp"
+ android:layout_height="30dp"
+ android:layout_gravity="center"
+ android:contentDescription="@null"
+ android:duplicateParentState="true"
+ android:elevation="16dp"
+ android:focusable="false"
+ android:outlineProvider="none"
+ android:src="@drawable/quantum_ic_call_white_24"
+ android:tint="@color/incoming_answer_icon"
+ android:tintMode="src_atop"
+ tools:outlineProvider="background"/>
+
+ </FrameLayout>
+ <TextView
+ android:id="@+id/incoming_swipe_to_reject_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="20dp"
+ android:layout_gravity="center_horizontal"
+ android:alpha="0"
+ android:focusable="false"
+ android:text="@string/call_incoming_swipe_to_reject"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance.Hint"
+ tools:alpha="1"/>
+ </LinearLayout>
+ <FrameLayout
+ android:id="@+id/hint_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"/>
+</FrameLayout>
diff --git a/java/com/android/incallui/answer/impl/answermethod/res/layout/two_button_method.xml b/java/com/android/incallui/answer/impl/answermethod/res/layout/two_button_method.xml
new file mode 100644
index 000000000..f92f3c428
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/res/layout/two_button_method.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="bottom|center_horizontal"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/two_button_hint_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="32dp"
+ android:accessibilityLiveRegion="polite"
+ android:alpha="0"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="@dimen/two_button_bottom_padding"
+ android:gravity="bottom|center_horizontal"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="88dp"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:padding="@dimen/incall_call_button_elevation"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageButton
+ android:id="@+id/two_button_decline_button"
+ style="@style/Answer.Button.Decline"
+ android:layout_width="@dimen/two_button_button_size"
+ android:layout_height="@dimen/two_button_button_size"
+ android:contentDescription="@string/a11y_call_incoming_decline_description"
+ android:src="@drawable/quantum_ic_call_end_white_24"/>
+
+ <TextView
+ android:id="@+id/two_button_decline_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/two_button_label_padding"
+ android:importantForAccessibility="no"
+ android:text="@string/call_incoming_decline"
+ android:textColor="#ffffffff"
+ android:textSize="@dimen/two_button_label_size"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:padding="@dimen/incall_call_button_elevation"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageButton
+ android:id="@+id/two_button_answer_button"
+ style="@style/Answer.Button.Answer"
+ android:layout_width="@dimen/two_button_button_size"
+ android:layout_height="@dimen/two_button_button_size"
+ android:contentDescription="@string/a11y_call_incoming_answer_description"
+ android:src="@drawable/quantum_ic_call_white_24"/>
+
+ <TextView
+ android:id="@+id/two_button_answer_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/two_button_label_padding"
+ android:importantForAccessibility="no"
+ android:text="@string/call_incoming_answer"
+ android:textColor="#ffffffff"
+ android:textSize="@dimen/two_button_label_size"/>
+
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
diff --git a/java/com/android/incallui/answer/impl/answermethod/res/values-h240dp/values.xml b/java/com/android/incallui/answer/impl/answermethod/res/values-h240dp/values.xml
new file mode 100644
index 000000000..7d99b29aa
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/res/values-h240dp/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <bool name="two_button_show_button_labels">true</bool>
+</resources>
diff --git a/java/com/android/incallui/answer/impl/answermethod/res/values-h280dp/dimens.xml b/java/com/android/incallui/answer/impl/answermethod/res/values-h280dp/dimens.xml
new file mode 100644
index 000000000..e7e223d8c
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/res/values-h280dp/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <dimen name="two_button_button_size">64dp</dimen>
+ <dimen name="two_button_label_padding">16dp</dimen>
+</resources>
diff --git a/java/com/android/incallui/answer/impl/answermethod/res/values-h480dp/dimens.xml b/java/com/android/incallui/answer/impl/answermethod/res/values-h480dp/dimens.xml
new file mode 100644
index 000000000..b7b4bd894
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/res/values-h480dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <dimen name="two_button_bottom_padding">60dp</dimen>
+</resources>
diff --git a/java/com/android/incallui/answer/impl/answermethod/res/values/dimens.xml b/java/com/android/incallui/answer/impl/answermethod/res/values/dimens.xml
new file mode 100644
index 000000000..bf160f9ac
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/res/values/dimens.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <dimen name="answer_contact_puck_size_photo">88dp</dimen>
+ <dimen name="answer_contact_puck_size_no_photo">72dp</dimen>
+ <dimen name="two_button_button_size">48dp</dimen>
+ <dimen name="two_button_label_size">12sp</dimen>
+ <dimen name="two_button_label_padding">8dp</dimen>
+ <dimen name="two_button_bottom_padding">24dp</dimen>
+ <dimen name="answer_swipe_dead_zone_sides">50dp</dimen>
+ <dimen name="answer_swipe_dead_zone_top">150dp</dimen>
+</resources>
diff --git a/java/com/android/incallui/answer/impl/answermethod/res/values/ids.xml b/java/com/android/incallui/answer/impl/answermethod/res/values/ids.xml
new file mode 100644
index 000000000..fc03cacbd
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/res/values/ids.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <item name="accessibility_action_answer" type="id"/>
+ <item name="accessibility_action_decline" type="id"/>
+</resources> \ No newline at end of file
diff --git a/java/com/android/incallui/answer/impl/answermethod/res/values/strings.xml b/java/com/android/incallui/answer/impl/answermethod/res/values/strings.xml
new file mode 100644
index 000000000..8b50dbf1a
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/res/values/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="call_incoming_swipe_to_answer">Swipe up to answer</string>
+ <string name="call_incoming_swipe_to_reject">Swipe down to reject</string>
+ <string name="a11y_incoming_call_swipe_to_answer">Swipe up with two fingers to answer or down to reject the call</string>
+ <string name="call_incoming_will_disconnect">Answering this call will end your video call</string>
+
+ <string name="a11y_call_incoming_decline_description">Decline</string>
+ <string name="call_incoming_decline">Decline</string>
+
+ <string name="a11y_call_incoming_answer_description">Answer</string>
+ <string name="call_incoming_answer">Answer</string>
+
+</resources>
diff --git a/java/com/android/incallui/answer/impl/answermethod/res/values/styles.xml b/java/com/android/incallui/answer/impl/answermethod/res/values/styles.xml
new file mode 100644
index 000000000..fd3ca7ca0
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/res/values/styles.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="Dialer.Incall.TextAppearance.Hint">
+ <item name="android:textSize">14sp</item>
+ <item name="android:textStyle">italic</item>
+ </style>
+</resources>
diff --git a/java/com/android/incallui/answer/impl/answermethod/res/values/values.xml b/java/com/android/incallui/answer/impl/answermethod/res/values/values.xml
new file mode 100644
index 000000000..43b2cd273
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/answermethod/res/values/values.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <color name="incoming_or_outgoing_call_screen_mask">@android:color/transparent</color>
+ <color name="call_hangup_background">#DF0000</color>
+ <color name="call_accept_background">#00C853</color>
+ <color name="incoming_answer_icon">#00C853</color>
+ <integer name="button_exit_fade_delay_ms">300</integer>
+ <bool name="two_button_show_button_labels">false</bool>
+</resources>
diff --git a/java/com/android/incallui/answer/impl/classifier/AccelerationClassifier.java b/java/com/android/incallui/answer/impl/classifier/AccelerationClassifier.java
new file mode 100644
index 000000000..ac504444e
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/AccelerationClassifier.java
@@ -0,0 +1,99 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+import android.util.ArrayMap;
+import android.view.MotionEvent;
+import java.util.Map;
+
+/**
+ * A classifier which looks at the speed and distance between successive points of a Stroke. It
+ * looks at two consecutive speeds between two points and calculates the ratio between them. The
+ * final result is the maximum of these values. It does the same for distances. If some speed or
+ * distance is equal to zero then the ratio between this and the next part is not calculated. To the
+ * duration of each part there is added one nanosecond so that it is always possible to calculate
+ * the speed of a part.
+ */
+class AccelerationClassifier extends StrokeClassifier {
+ private final Map<Stroke, Data> mStrokeMap = new ArrayMap<>();
+
+ public AccelerationClassifier(ClassifierData classifierData) {
+ mClassifierData = classifierData;
+ }
+
+ @Override
+ public String getTag() {
+ return "ACC";
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ int action = event.getActionMasked();
+
+ if (action == MotionEvent.ACTION_DOWN) {
+ mStrokeMap.clear();
+ }
+
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
+ Point point = stroke.getPoints().get(stroke.getPoints().size() - 1);
+ if (mStrokeMap.get(stroke) == null) {
+ mStrokeMap.put(stroke, new Data(point));
+ } else {
+ mStrokeMap.get(stroke).addPoint(point);
+ }
+ }
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(Stroke stroke) {
+ Data data = mStrokeMap.get(stroke);
+ return 2 * SpeedRatioEvaluator.evaluate(data.maxSpeedRatio);
+ }
+
+ private static class Data {
+
+ static final float MILLIS_TO_NANOS = 1e6f;
+
+ Point previousPoint;
+ float previousSpeed = 0;
+ float maxSpeedRatio = 0;
+
+ public Data(Point point) {
+ previousPoint = point;
+ }
+
+ public void addPoint(Point point) {
+ float distance = previousPoint.dist(point);
+ float duration = (float) (point.timeOffsetNano - previousPoint.timeOffsetNano + 1);
+ float speed = distance / duration;
+
+ if (duration > 20 * MILLIS_TO_NANOS || duration < 5 * MILLIS_TO_NANOS) {
+ // reject this segment and ensure we won't use data about it in the next round.
+ previousSpeed = 0;
+ previousPoint = point;
+ return;
+ }
+ if (previousSpeed != 0.0f) {
+ maxSpeedRatio = Math.max(maxSpeedRatio, speed / previousSpeed);
+ }
+
+ previousSpeed = speed;
+ previousPoint = point;
+ }
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/AnglesClassifier.java b/java/com/android/incallui/answer/impl/classifier/AnglesClassifier.java
new file mode 100644
index 000000000..dbfbcfc1c
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/AnglesClassifier.java
@@ -0,0 +1,193 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+import android.util.ArrayMap;
+import android.view.MotionEvent;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A classifier which calculates the variance of differences between successive angles in a stroke.
+ * For each stroke it keeps its last three points. If some successive points are the same, it
+ * ignores the repetitions. If a new point is added, the classifier calculates the angle between the
+ * last three points. After that, it calculates the difference between this angle and the previously
+ * calculated angle. Then it calculates the variance of the differences from a stroke. To the
+ * differences there is artificially added value 0.0 and the difference between the first angle and
+ * PI (angles are in radians). It helps with strokes which have few points and punishes more strokes
+ * which are not smooth.
+ *
+ * <p>This classifier also tries to split the stroke into two parts in the place in which the
+ * biggest angle is. It calculates the angle variance of the two parts and sums them up. The reason
+ * the classifier is doing this, is because some human swipes at the beginning go for a moment in
+ * one direction and then they rapidly change direction for the rest of the stroke (like a tick).
+ * The final result is the minimum of angle variance of the whole stroke and the sum of angle
+ * variances of the two parts split up. The classifier tries the tick option only if the first part
+ * is shorter than the second part.
+ *
+ * <p>Additionally, the classifier classifies the angles as left angles (those angles which value is
+ * in [0.0, PI - ANGLE_DEVIATION) interval), straight angles ([PI - ANGLE_DEVIATION, PI +
+ * ANGLE_DEVIATION] interval) and right angles ((PI + ANGLE_DEVIATION, 2 * PI) interval) and then
+ * calculates the percentage of angles which are in the same direction (straight angles can be left
+ * angels or right angles)
+ */
+class AnglesClassifier extends StrokeClassifier {
+ private Map<Stroke, Data> mStrokeMap = new ArrayMap<>();
+
+ public AnglesClassifier(ClassifierData classifierData) {
+ mClassifierData = classifierData;
+ }
+
+ @Override
+ public String getTag() {
+ return "ANG";
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ int action = event.getActionMasked();
+
+ if (action == MotionEvent.ACTION_DOWN) {
+ mStrokeMap.clear();
+ }
+
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
+
+ if (mStrokeMap.get(stroke) == null) {
+ mStrokeMap.put(stroke, new Data());
+ }
+ mStrokeMap.get(stroke).addPoint(stroke.getPoints().get(stroke.getPoints().size() - 1));
+ }
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(Stroke stroke) {
+ Data data = mStrokeMap.get(stroke);
+ return AnglesVarianceEvaluator.evaluate(data.getAnglesVariance())
+ + AnglesPercentageEvaluator.evaluate(data.getAnglesPercentage());
+ }
+
+ private static class Data {
+ private static final float ANGLE_DEVIATION = (float) Math.PI / 20.0f;
+ private static final float MIN_MOVE_DIST_DP = .01f;
+
+ private List<Point> mLastThreePoints = new ArrayList<>();
+ private float mFirstAngleVariance;
+ private float mPreviousAngle;
+ private float mBiggestAngle;
+ private float mSumSquares;
+ private float mSecondSumSquares;
+ private float mSum;
+ private float mSecondSum;
+ private float mCount;
+ private float mSecondCount;
+ private float mFirstLength;
+ private float mLength;
+ private float mAnglesCount;
+ private float mLeftAngles;
+ private float mRightAngles;
+ private float mStraightAngles;
+
+ public Data() {
+ mFirstAngleVariance = 0.0f;
+ mPreviousAngle = (float) Math.PI;
+ mBiggestAngle = 0.0f;
+ mSumSquares = mSecondSumSquares = 0.0f;
+ mSum = mSecondSum = 0.0f;
+ mCount = mSecondCount = 1.0f;
+ mLength = mFirstLength = 0.0f;
+ mAnglesCount = mLeftAngles = mRightAngles = mStraightAngles = 0.0f;
+ }
+
+ public void addPoint(Point point) {
+ // Checking if the added point is different than the previously added point
+ // Repetitions and short distances are being ignored so that proper angles are calculated.
+ if (mLastThreePoints.isEmpty()
+ || (!mLastThreePoints.get(mLastThreePoints.size() - 1).equals(point)
+ && (mLastThreePoints.get(mLastThreePoints.size() - 1).dist(point)
+ > MIN_MOVE_DIST_DP))) {
+ if (!mLastThreePoints.isEmpty()) {
+ mLength += mLastThreePoints.get(mLastThreePoints.size() - 1).dist(point);
+ }
+ mLastThreePoints.add(point);
+ if (mLastThreePoints.size() == 4) {
+ mLastThreePoints.remove(0);
+
+ float angle =
+ mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0), mLastThreePoints.get(2));
+
+ mAnglesCount++;
+ if (angle < Math.PI - ANGLE_DEVIATION) {
+ mLeftAngles++;
+ } else if (angle <= Math.PI + ANGLE_DEVIATION) {
+ mStraightAngles++;
+ } else {
+ mRightAngles++;
+ }
+
+ float difference = angle - mPreviousAngle;
+
+ // If this is the biggest angle of the stroke so then we save the value of
+ // the angle variance so far and start to count the values for the angle
+ // variance of the second part.
+ if (mBiggestAngle < angle) {
+ mBiggestAngle = angle;
+ mFirstLength = mLength;
+ mFirstAngleVariance = getAnglesVariance(mSumSquares, mSum, mCount);
+ mSecondSumSquares = 0.0f;
+ mSecondSum = 0.0f;
+ mSecondCount = 1.0f;
+ } else {
+ mSecondSum += difference;
+ mSecondSumSquares += difference * difference;
+ mSecondCount += 1.0f;
+ }
+
+ mSum += difference;
+ mSumSquares += difference * difference;
+ mCount += 1.0f;
+ mPreviousAngle = angle;
+ }
+ }
+ }
+
+ public float getAnglesVariance(float sumSquares, float sum, float count) {
+ return sumSquares / count - (sum / count) * (sum / count);
+ }
+
+ public float getAnglesVariance() {
+ float anglesVariance = getAnglesVariance(mSumSquares, mSum, mCount);
+ if (mFirstLength < mLength / 2f) {
+ anglesVariance =
+ Math.min(
+ anglesVariance,
+ mFirstAngleVariance
+ + getAnglesVariance(mSecondSumSquares, mSecondSum, mSecondCount));
+ }
+ return anglesVariance;
+ }
+
+ public float getAnglesPercentage() {
+ if (mAnglesCount == 0.0f) {
+ return 1.0f;
+ }
+ return (Math.max(mLeftAngles, mRightAngles) + mStraightAngles) / mAnglesCount;
+ }
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/AnglesPercentageEvaluator.java b/java/com/android/incallui/answer/impl/classifier/AnglesPercentageEvaluator.java
new file mode 100644
index 000000000..49a183596
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/AnglesPercentageEvaluator.java
@@ -0,0 +1,33 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+class AnglesPercentageEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 1.00) {
+ evaluation++;
+ }
+ if (value < 0.90) {
+ evaluation++;
+ }
+ if (value < 0.70) {
+ evaluation++;
+ }
+ return evaluation;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/AnglesVarianceEvaluator.java b/java/com/android/incallui/answer/impl/classifier/AnglesVarianceEvaluator.java
new file mode 100644
index 000000000..db4de6a3b
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/AnglesVarianceEvaluator.java
@@ -0,0 +1,42 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+class AnglesVarianceEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value > 0.05) {
+ evaluation++;
+ }
+ if (value > 0.10) {
+ evaluation++;
+ }
+ if (value > 0.20) {
+ evaluation++;
+ }
+ if (value > 0.40) {
+ evaluation++;
+ }
+ if (value > 0.80) {
+ evaluation++;
+ }
+ if (value > 1.50) {
+ evaluation++;
+ }
+ return evaluation;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/Classifier.java b/java/com/android/incallui/answer/impl/classifier/Classifier.java
new file mode 100644
index 000000000..c6fbff327
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/Classifier.java
@@ -0,0 +1,35 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+import android.hardware.SensorEvent;
+import android.view.MotionEvent;
+
+/** An abstract class for classifiers for touch and sensor events. */
+abstract class Classifier {
+
+ /** Contains all the information about touch events from which the classifier can query */
+ protected ClassifierData mClassifierData;
+
+ /** Informs the classifier that a new touch event has occurred */
+ public void onTouchEvent(MotionEvent event) {}
+
+ /** Informs the classifier that a sensor change occurred */
+ public void onSensorChanged(SensorEvent event) {}
+
+ public abstract String getTag();
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/ClassifierData.java b/java/com/android/incallui/answer/impl/classifier/ClassifierData.java
new file mode 100644
index 000000000..ae07d27a0
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/ClassifierData.java
@@ -0,0 +1,96 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+import android.util.SparseArray;
+import android.view.MotionEvent;
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Contains data which is used to classify interaction sequences on the lockscreen. It does, for
+ * example, provide information on the current touch state.
+ */
+class ClassifierData {
+ private SparseArray<Stroke> mCurrentStrokes = new SparseArray<>();
+ private ArrayList<Stroke> mEndingStrokes = new ArrayList<>();
+ private final float mDpi;
+ private final float mScreenHeight;
+
+ public ClassifierData(float dpi, float screenHeight) {
+ mDpi = dpi;
+ mScreenHeight = screenHeight / dpi;
+ }
+
+ public void update(MotionEvent event) {
+ mEndingStrokes.clear();
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mCurrentStrokes.clear();
+ }
+
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ int id = event.getPointerId(i);
+ if (mCurrentStrokes.get(id) == null) {
+ // TODO (keyboardr): See if there's a way to use event.getEventTimeNanos() instead
+ mCurrentStrokes.put(
+ id, new Stroke(TimeUnit.MILLISECONDS.toNanos(event.getEventTime()), mDpi));
+ }
+ mCurrentStrokes
+ .get(id)
+ .addPoint(
+ event.getX(i), event.getY(i), TimeUnit.MILLISECONDS.toNanos(event.getEventTime()));
+
+ if (action == MotionEvent.ACTION_UP
+ || action == MotionEvent.ACTION_CANCEL
+ || (action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
+ mEndingStrokes.add(getStroke(id));
+ }
+ }
+ }
+
+ void cleanUp(MotionEvent event) {
+ mEndingStrokes.clear();
+ int action = event.getActionMasked();
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ int id = event.getPointerId(i);
+ if (action == MotionEvent.ACTION_UP
+ || action == MotionEvent.ACTION_CANCEL
+ || (action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
+ mCurrentStrokes.remove(id);
+ }
+ }
+ }
+
+ /** @return the list of Strokes which are ending in the recently added MotionEvent */
+ public ArrayList<Stroke> getEndingStrokes() {
+ return mEndingStrokes;
+ }
+
+ /**
+ * @param id the id from MotionEvent
+ * @return the Stroke assigned to the id
+ */
+ public Stroke getStroke(int id) {
+ return mCurrentStrokes.get(id);
+ }
+
+ /** @return the height of the screen in inches */
+ public float getScreenHeight() {
+ return mScreenHeight;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/DirectionClassifier.java b/java/com/android/incallui/answer/impl/classifier/DirectionClassifier.java
new file mode 100644
index 000000000..068626859
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/DirectionClassifier.java
@@ -0,0 +1,37 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+/**
+ * A classifier which looks at the general direction of a stroke and evaluates it depending on the
+ * type of action that takes place.
+ */
+public class DirectionClassifier extends StrokeClassifier {
+ public DirectionClassifier(ClassifierData classifierData) {}
+
+ @Override
+ public String getTag() {
+ return "DIR";
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(Stroke stroke) {
+ Point firstPoint = stroke.getPoints().get(0);
+ Point lastPoint = stroke.getPoints().get(stroke.getPoints().size() - 1);
+ return DirectionEvaluator.evaluate(lastPoint.x - firstPoint.x, lastPoint.y - firstPoint.y);
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/DirectionEvaluator.java b/java/com/android/incallui/answer/impl/classifier/DirectionEvaluator.java
new file mode 100644
index 000000000..cdc1cfe1e
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/DirectionEvaluator.java
@@ -0,0 +1,23 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+class DirectionEvaluator {
+ public static float evaluate(float xDiff, float yDiff) {
+ return Math.abs(yDiff) < Math.abs(xDiff) ? 5.5f : 0.0f;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/DurationCountClassifier.java b/java/com/android/incallui/answer/impl/classifier/DurationCountClassifier.java
new file mode 100644
index 000000000..0b9f1138d
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/DurationCountClassifier.java
@@ -0,0 +1,35 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+/**
+ * A classifier which looks at the ratio between the duration of the stroke and its number of
+ * points.
+ */
+class DurationCountClassifier extends StrokeClassifier {
+ public DurationCountClassifier(ClassifierData classifierData) {}
+
+ @Override
+ public String getTag() {
+ return "DUR";
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(Stroke stroke) {
+ return DurationCountEvaluator.evaluate(stroke.getDurationSeconds() / stroke.getCount());
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/DurationCountEvaluator.java b/java/com/android/incallui/answer/impl/classifier/DurationCountEvaluator.java
new file mode 100644
index 000000000..5b232fe95
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/DurationCountEvaluator.java
@@ -0,0 +1,39 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+class DurationCountEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 0.0105) {
+ evaluation++;
+ }
+ if (value < 0.00909) {
+ evaluation++;
+ }
+ if (value < 0.00667) {
+ evaluation++;
+ }
+ if (value > 0.0333) {
+ evaluation++;
+ }
+ if (value > 0.0500) {
+ evaluation++;
+ }
+ return evaluation;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/EndPointLengthClassifier.java b/java/com/android/incallui/answer/impl/classifier/EndPointLengthClassifier.java
new file mode 100644
index 000000000..95b317638
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/EndPointLengthClassifier.java
@@ -0,0 +1,36 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+/**
+ * A classifier which looks at the distance between the first and the last point from the stroke.
+ */
+class EndPointLengthClassifier extends StrokeClassifier {
+ public EndPointLengthClassifier(ClassifierData classifierData) {
+ mClassifierData = classifierData;
+ }
+
+ @Override
+ public String getTag() {
+ return "END_LNGTH";
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(Stroke stroke) {
+ return EndPointLengthEvaluator.evaluate(stroke.getEndPointLength());
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/EndPointLengthEvaluator.java b/java/com/android/incallui/answer/impl/classifier/EndPointLengthEvaluator.java
new file mode 100644
index 000000000..74bfffba4
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/EndPointLengthEvaluator.java
@@ -0,0 +1,42 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+class EndPointLengthEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 0.05) {
+ evaluation += 2.0f;
+ }
+ if (value < 0.1) {
+ evaluation += 2.0f;
+ }
+ if (value < 0.2) {
+ evaluation += 2.0f;
+ }
+ if (value < 0.3) {
+ evaluation += 2.0f;
+ }
+ if (value < 0.4) {
+ evaluation += 2.0f;
+ }
+ if (value < 0.5) {
+ evaluation += 2.0f;
+ }
+ return evaluation;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/EndPointRatioClassifier.java b/java/com/android/incallui/answer/impl/classifier/EndPointRatioClassifier.java
new file mode 100644
index 000000000..01a35c126
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/EndPointRatioClassifier.java
@@ -0,0 +1,43 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+/**
+ * A classifier which looks at the ratio between the total length covered by the stroke and the
+ * distance between the first and last point from this stroke.
+ */
+class EndPointRatioClassifier extends StrokeClassifier {
+ public EndPointRatioClassifier(ClassifierData classifierData) {
+ mClassifierData = classifierData;
+ }
+
+ @Override
+ public String getTag() {
+ return "END_RTIO";
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(Stroke stroke) {
+ float ratio;
+ if (stroke.getTotalLength() == 0.0f) {
+ ratio = 1.0f;
+ } else {
+ ratio = stroke.getEndPointLength() / stroke.getTotalLength();
+ }
+ return EndPointRatioEvaluator.evaluate(ratio);
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/EndPointRatioEvaluator.java b/java/com/android/incallui/answer/impl/classifier/EndPointRatioEvaluator.java
new file mode 100644
index 000000000..1d64bea8e
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/EndPointRatioEvaluator.java
@@ -0,0 +1,42 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+class EndPointRatioEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 0.85) {
+ evaluation++;
+ }
+ if (value < 0.75) {
+ evaluation++;
+ }
+ if (value < 0.65) {
+ evaluation++;
+ }
+ if (value < 0.55) {
+ evaluation++;
+ }
+ if (value < 0.45) {
+ evaluation++;
+ }
+ if (value < 0.35) {
+ evaluation++;
+ }
+ return evaluation;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/FalsingManager.java b/java/com/android/incallui/answer/impl/classifier/FalsingManager.java
new file mode 100644
index 000000000..fdcc0a3f9
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/FalsingManager.java
@@ -0,0 +1,140 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.PowerManager;
+import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityManager;
+
+/**
+ * When the phone is locked, listens to touch, sensor and phone events and sends them to
+ * HumanInteractionClassifier to determine if touches are coming from a human.
+ */
+public class FalsingManager implements SensorEventListener {
+ private static final int[] CLASSIFIER_SENSORS =
+ new int[] {
+ Sensor.TYPE_PROXIMITY,
+ };
+
+ private final SensorManager mSensorManager;
+ private final HumanInteractionClassifier mHumanInteractionClassifier;
+ private final AccessibilityManager mAccessibilityManager;
+
+ private boolean mSessionActive = false;
+ private boolean mScreenOn;
+
+ public FalsingManager(Context context) {
+ mSensorManager = context.getSystemService(SensorManager.class);
+ mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+ mHumanInteractionClassifier = new HumanInteractionClassifier(context);
+ mScreenOn = context.getSystemService(PowerManager.class).isInteractive();
+ }
+
+ /** Returns {@code true} iff the FalsingManager is enabled and able to classify touches */
+ public boolean isEnabled() {
+ return mHumanInteractionClassifier.isEnabled();
+ }
+
+ /**
+ * Returns {@code true} iff the classifier determined that this is not a human interacting with
+ * the phone.
+ */
+ public boolean isFalseTouch() {
+ // Touch exploration triggers false positives in the classifier and
+ // already sufficiently prevents false unlocks.
+ return !mAccessibilityManager.isTouchExplorationEnabled()
+ && mHumanInteractionClassifier.isFalseTouch();
+ }
+
+ /**
+ * Should be called when the screen turns on and the related Views become visible. This will start
+ * tracking changes if the manager is enabled.
+ */
+ public void onScreenOn() {
+ mScreenOn = true;
+ sessionEntrypoint();
+ }
+
+ /**
+ * Should be called when the screen turns off or the related Views are no longer visible. This
+ * will cause the manager to stop tracking changes.
+ */
+ public void onScreenOff() {
+ mScreenOn = false;
+ sessionExitpoint();
+ }
+
+ /**
+ * Should be called when a new touch event has been received and should be classified.
+ *
+ * @param event MotionEvent to be classified as human or false.
+ */
+ public void onTouchEvent(MotionEvent event) {
+ if (mSessionActive) {
+ mHumanInteractionClassifier.onTouchEvent(event);
+ }
+ }
+
+ @Override
+ public synchronized void onSensorChanged(SensorEvent event) {
+ mHumanInteractionClassifier.onSensorChanged(event);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+ private boolean shouldSessionBeActive() {
+ return isEnabled() && mScreenOn;
+ }
+
+ private boolean sessionEntrypoint() {
+ if (!mSessionActive && shouldSessionBeActive()) {
+ onSessionStart();
+ return true;
+ }
+ return false;
+ }
+
+ private void sessionExitpoint() {
+ if (mSessionActive && !shouldSessionBeActive()) {
+ mSessionActive = false;
+ mSensorManager.unregisterListener(this);
+ }
+ }
+
+ private void onSessionStart() {
+ mSessionActive = true;
+
+ if (mHumanInteractionClassifier.isEnabled()) {
+ registerSensors(CLASSIFIER_SENSORS);
+ }
+ }
+
+ private void registerSensors(int[] sensors) {
+ for (int sensorType : sensors) {
+ Sensor s = mSensorManager.getDefaultSensor(sensorType);
+ if (s != null) {
+ mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME);
+ }
+ }
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/GestureClassifier.java b/java/com/android/incallui/answer/impl/classifier/GestureClassifier.java
new file mode 100644
index 000000000..afd7ea0e7
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/GestureClassifier.java
@@ -0,0 +1,31 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+/**
+ * An abstract class for classifiers which classify the whole gesture (all the strokes which
+ * occurred from DOWN event to UP/CANCEL event)
+ */
+abstract class GestureClassifier extends Classifier {
+
+ /**
+ * @return a non-negative value which is used to determine whether the most recent gesture is a
+ * false interaction; the bigger the value the greater the chance that this a false
+ * interaction.
+ */
+ public abstract float getFalseTouchEvaluation();
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/HistoryEvaluator.java b/java/com/android/incallui/answer/impl/classifier/HistoryEvaluator.java
new file mode 100644
index 000000000..3f302c65f
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/HistoryEvaluator.java
@@ -0,0 +1,115 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+import android.os.SystemClock;
+
+import java.util.ArrayList;
+
+/**
+ * Holds the evaluations for ended strokes and gestures. These values are decreased through time.
+ */
+class HistoryEvaluator {
+ private static final float INTERVAL = 50.0f;
+ private static final float HISTORY_FACTOR = 0.9f;
+ private static final float EPSILON = 1e-5f;
+
+ private final ArrayList<Data> mStrokes = new ArrayList<>();
+ private final ArrayList<Data> mGestureWeights = new ArrayList<>();
+ private long mLastUpdate;
+
+ public HistoryEvaluator() {
+ mLastUpdate = SystemClock.elapsedRealtime();
+ }
+
+ public void addStroke(float evaluation) {
+ decayValue();
+ mStrokes.add(new Data(evaluation));
+ }
+
+ public void addGesture(float evaluation) {
+ decayValue();
+ mGestureWeights.add(new Data(evaluation));
+ }
+
+ /** Calculates the weighted average of strokes and adds to it the weighted average of gestures */
+ public float getEvaluation() {
+ return weightedAverage(mStrokes) + weightedAverage(mGestureWeights);
+ }
+
+ private float weightedAverage(ArrayList<Data> list) {
+ float sumValue = 0.0f;
+ float sumWeight = 0.0f;
+ int size = list.size();
+ for (int i = 0; i < size; i++) {
+ Data data = list.get(i);
+ sumValue += data.evaluation * data.weight;
+ sumWeight += data.weight;
+ }
+
+ if (sumWeight == 0.0f) {
+ return 0.0f;
+ }
+
+ return sumValue / sumWeight;
+ }
+
+ private void decayValue() {
+ long time = SystemClock.elapsedRealtime();
+
+ if (time <= mLastUpdate) {
+ return;
+ }
+
+ // All weights are multiplied by HISTORY_FACTOR after each INTERVAL milliseconds.
+ float factor = (float) Math.pow(HISTORY_FACTOR, (time - mLastUpdate) / INTERVAL);
+
+ decayValue(mStrokes, factor);
+ decayValue(mGestureWeights, factor);
+ mLastUpdate = time;
+ }
+
+ private void decayValue(ArrayList<Data> list, float factor) {
+ int size = list.size();
+ for (int i = 0; i < size; i++) {
+ list.get(i).weight *= factor;
+ }
+
+ // Removing evaluations with such small weights that they do not matter anymore
+ while (!list.isEmpty() && isZero(list.get(0).weight)) {
+ list.remove(0);
+ }
+ }
+
+ private boolean isZero(float x) {
+ return x <= EPSILON && x >= -EPSILON;
+ }
+
+ /**
+ * For each stroke it holds its initial value and the current weight. Initially the weight is set
+ * to 1.0
+ */
+ private static class Data {
+ public float evaluation;
+ public float weight;
+
+ public Data(float evaluation) {
+ this.evaluation = evaluation;
+ weight = 1.0f;
+ }
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/HumanInteractionClassifier.java b/java/com/android/incallui/answer/impl/classifier/HumanInteractionClassifier.java
new file mode 100644
index 000000000..1d3d7ef22
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/HumanInteractionClassifier.java
@@ -0,0 +1,142 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+import android.content.Context;
+import android.hardware.SensorEvent;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+import com.android.dialer.common.ConfigProviderBindings;
+
+/** An classifier trying to determine whether it is a human interacting with the phone or not. */
+class HumanInteractionClassifier extends Classifier {
+
+ private static final String CONFIG_ANSWER_FALSE_TOUCH_DETECTION_ENABLED =
+ "answer_false_touch_detection_enabled";
+
+ private final StrokeClassifier[] mStrokeClassifiers;
+ private final GestureClassifier[] mGestureClassifiers;
+ private final HistoryEvaluator mHistoryEvaluator;
+ private final boolean mEnabled;
+
+ HumanInteractionClassifier(Context context) {
+ DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+
+ // If the phone is rotated to landscape, the calculations would be wrong if xdpi and ydpi
+ // were to be used separately. Due negligible differences in xdpi and ydpi we can just
+ // take the average.
+ // Note that xdpi and ydpi are the physical pixels per inch and are not affected by scaling.
+ float dpi = (displayMetrics.xdpi + displayMetrics.ydpi) / 2.0f;
+ mClassifierData = new ClassifierData(dpi, displayMetrics.heightPixels);
+ mHistoryEvaluator = new HistoryEvaluator();
+ mEnabled =
+ ConfigProviderBindings.get(context)
+ .getBoolean(CONFIG_ANSWER_FALSE_TOUCH_DETECTION_ENABLED, true);
+
+ mStrokeClassifiers =
+ new StrokeClassifier[] {
+ new AnglesClassifier(mClassifierData),
+ new SpeedClassifier(mClassifierData),
+ new DurationCountClassifier(mClassifierData),
+ new EndPointRatioClassifier(mClassifierData),
+ new EndPointLengthClassifier(mClassifierData),
+ new AccelerationClassifier(mClassifierData),
+ new SpeedAnglesClassifier(mClassifierData),
+ new LengthCountClassifier(mClassifierData),
+ new DirectionClassifier(mClassifierData)
+ };
+
+ mGestureClassifiers =
+ new GestureClassifier[] {
+ new PointerCountClassifier(mClassifierData), new ProximityClassifier(mClassifierData)
+ };
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+
+ // If the user is dragging down the notification, they might want to drag it down
+ // enough to see the content, read it for a while and then lift the finger to open
+ // the notification. This kind of motion scores very bad in the Classifier so the
+ // MotionEvents which are close to the current position of the finger are not
+ // sent to the classifiers until the finger moves far enough. When the finger if lifted
+ // up, the last MotionEvent which was far enough from the finger is set as the final
+ // MotionEvent and sent to the Classifiers.
+ addTouchEvent(event);
+ }
+
+ private void addTouchEvent(MotionEvent event) {
+ mClassifierData.update(event);
+
+ for (StrokeClassifier c : mStrokeClassifiers) {
+ c.onTouchEvent(event);
+ }
+
+ for (GestureClassifier c : mGestureClassifiers) {
+ c.onTouchEvent(event);
+ }
+
+ int size = mClassifierData.getEndingStrokes().size();
+ for (int i = 0; i < size; i++) {
+ Stroke stroke = mClassifierData.getEndingStrokes().get(i);
+ float evaluation = 0.0f;
+ for (StrokeClassifier c : mStrokeClassifiers) {
+ float e = c.getFalseTouchEvaluation(stroke);
+ evaluation += e;
+ }
+
+ mHistoryEvaluator.addStroke(evaluation);
+ }
+
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ float evaluation = 0.0f;
+ for (GestureClassifier c : mGestureClassifiers) {
+ float e = c.getFalseTouchEvaluation();
+ evaluation += e;
+ }
+ mHistoryEvaluator.addGesture(evaluation);
+ }
+
+ mClassifierData.cleanUp(event);
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ for (Classifier c : mStrokeClassifiers) {
+ c.onSensorChanged(event);
+ }
+
+ for (Classifier c : mGestureClassifiers) {
+ c.onSensorChanged(event);
+ }
+ }
+
+ boolean isFalseTouch() {
+ float evaluation = mHistoryEvaluator.getEvaluation();
+ return evaluation >= 5.0f;
+ }
+
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ @Override
+ public String getTag() {
+ return "HIC";
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/LengthCountClassifier.java b/java/com/android/incallui/answer/impl/classifier/LengthCountClassifier.java
new file mode 100644
index 000000000..7dd2ab674
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/LengthCountClassifier.java
@@ -0,0 +1,39 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+/**
+ * A classifier which looks at the ratio between the length of the stroke and its number of points.
+ * The number of points is subtracted by 2 because the UP event comes in with some delay and it
+ * should not influence the ratio and also strokes which are long and have a small number of points
+ * are punished more (these kind of strokes are usually bad ones and they tend to score well in
+ * other classifiers).
+ */
+class LengthCountClassifier extends StrokeClassifier {
+ public LengthCountClassifier(ClassifierData classifierData) {}
+
+ @Override
+ public String getTag() {
+ return "LEN_CNT";
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(Stroke stroke) {
+ return LengthCountEvaluator.evaluate(
+ stroke.getTotalLength() / Math.max(1.0f, stroke.getCount() - 2));
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/LengthCountEvaluator.java b/java/com/android/incallui/answer/impl/classifier/LengthCountEvaluator.java
new file mode 100644
index 000000000..2a2225a00
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/LengthCountEvaluator.java
@@ -0,0 +1,45 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+/**
+ * A classifier which looks at the ratio between the length of the stroke and its number of points.
+ */
+class LengthCountEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 0.09) {
+ evaluation++;
+ }
+ if (value < 0.05) {
+ evaluation++;
+ }
+ if (value < 0.02) {
+ evaluation++;
+ }
+ if (value > 0.6) {
+ evaluation++;
+ }
+ if (value > 0.9) {
+ evaluation++;
+ }
+ if (value > 1.2) {
+ evaluation++;
+ }
+ return evaluation;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/Point.java b/java/com/android/incallui/answer/impl/classifier/Point.java
new file mode 100644
index 000000000..5ea48b4ce
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/Point.java
@@ -0,0 +1,95 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+class Point {
+ public float x;
+ public float y;
+ public long timeOffsetNano;
+
+ public Point(float x, float y) {
+ this.x = x;
+ this.y = y;
+ this.timeOffsetNano = 0;
+ }
+
+ public Point(float x, float y, long timeOffsetNano) {
+ this.x = x;
+ this.y = y;
+ this.timeOffsetNano = timeOffsetNano;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof Point)) {
+ return false;
+ }
+ Point otherPoint = ((Point) other);
+ return x == otherPoint.x && y == otherPoint.y;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (x != +0.0f ? Float.floatToIntBits(x) : 0);
+ result = 31 * result + (y != +0.0f ? Float.floatToIntBits(y) : 0);
+ return result;
+ }
+
+ public float dist(Point a) {
+ return (float) Math.hypot(a.x - x, a.y - y);
+ }
+
+ /**
+ * Calculates the cross product of vec(this, a) and vec(this, b) where vec(x,y) is the vector from
+ * point x to point y
+ */
+ public float crossProduct(Point a, Point b) {
+ return (a.x - x) * (b.y - y) - (a.y - y) * (b.x - x);
+ }
+
+ /**
+ * Calculates the dot product of vec(this, a) and vec(this, b) where vec(x,y) is the vector from
+ * point x to point y
+ */
+ public float dotProduct(Point a, Point b) {
+ return (a.x - x) * (b.x - x) + (a.y - y) * (b.y - y);
+ }
+
+ /**
+ * Calculates the angle in radians created by points (a, this, b). If any two of these points are
+ * the same, the method will return 0.0f
+ *
+ * @return the angle in radians
+ */
+ public float getAngle(Point a, Point b) {
+ float dist1 = dist(a);
+ float dist2 = dist(b);
+
+ if (dist1 == 0.0f || dist2 == 0.0f) {
+ return 0.0f;
+ }
+
+ float crossProduct = crossProduct(a, b);
+ float dotProduct = dotProduct(a, b);
+ float cos = Math.min(1.0f, Math.max(-1.0f, dotProduct / dist1 / dist2));
+ float angle = (float) Math.acos(cos);
+ if (crossProduct < 0.0) {
+ angle = 2.0f * (float) Math.PI - angle;
+ }
+ return angle;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/PointerCountClassifier.java b/java/com/android/incallui/answer/impl/classifier/PointerCountClassifier.java
new file mode 100644
index 000000000..070de6c9b
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/PointerCountClassifier.java
@@ -0,0 +1,51 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+import android.view.MotionEvent;
+
+/** A classifier which looks at the total number of traces in the whole gesture. */
+class PointerCountClassifier extends GestureClassifier {
+ private int mCount;
+
+ public PointerCountClassifier(ClassifierData classifierData) {
+ mCount = 0;
+ }
+
+ @Override
+ public String getTag() {
+ return "PTR_CNT";
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ int action = event.getActionMasked();
+
+ if (action == MotionEvent.ACTION_DOWN) {
+ mCount = 1;
+ }
+
+ if (action == MotionEvent.ACTION_POINTER_DOWN) {
+ ++mCount;
+ }
+ }
+
+ @Override
+ public float getFalseTouchEvaluation() {
+ return PointerCountEvaluator.evaluate(mCount);
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/PointerCountEvaluator.java b/java/com/android/incallui/answer/impl/classifier/PointerCountEvaluator.java
new file mode 100644
index 000000000..aa972da8c
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/PointerCountEvaluator.java
@@ -0,0 +1,23 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+class PointerCountEvaluator {
+ public static float evaluate(int value) {
+ return (value - 1) * (value - 1);
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/ProximityClassifier.java b/java/com/android/incallui/answer/impl/classifier/ProximityClassifier.java
new file mode 100644
index 000000000..28701ea6d
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/ProximityClassifier.java
@@ -0,0 +1,97 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.view.MotionEvent;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A classifier which looks at the proximity sensor during the gesture. It calculates the percentage
+ * the proximity sensor showing the near state during the whole gesture
+ */
+class ProximityClassifier extends GestureClassifier {
+ private long mGestureStartTimeNano;
+ private long mNearStartTimeNano;
+ private long mNearDuration;
+ private boolean mNear;
+ private float mAverageNear;
+
+ public ProximityClassifier(ClassifierData classifierData) {}
+
+ @Override
+ public String getTag() {
+ return "PROX";
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) {
+ update(event.values[0] < event.sensor.getMaximumRange(), event.timestamp);
+ }
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ int action = event.getActionMasked();
+
+ if (action == MotionEvent.ACTION_DOWN) {
+ mGestureStartTimeNano = TimeUnit.MILLISECONDS.toNanos(event.getEventTime());
+ mNearStartTimeNano = TimeUnit.MILLISECONDS.toNanos(event.getEventTime());
+ mNearDuration = 0;
+ }
+
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ update(mNear, TimeUnit.MILLISECONDS.toNanos(event.getEventTime()));
+ long duration = TimeUnit.MILLISECONDS.toNanos(event.getEventTime()) - mGestureStartTimeNano;
+
+ if (duration == 0) {
+ mAverageNear = mNear ? 1.0f : 0.0f;
+ } else {
+ mAverageNear = (float) mNearDuration / (float) duration;
+ }
+ }
+ }
+
+ /**
+ * @param near is the sensor showing the near state right now
+ * @param timestampNano time of this event in nanoseconds
+ */
+ private void update(boolean near, long timestampNano) {
+ // This if is necessary because MotionEvents and SensorEvents do not come in
+ // chronological order
+ if (timestampNano > mNearStartTimeNano) {
+ // if the state before was near then add the difference of the current time and
+ // mNearStartTimeNano to mNearDuration.
+ if (mNear) {
+ mNearDuration += timestampNano - mNearStartTimeNano;
+ }
+
+ // if the new state is near, set mNearStartTimeNano equal to this moment.
+ if (near) {
+ mNearStartTimeNano = timestampNano;
+ }
+ }
+ mNear = near;
+ }
+
+ @Override
+ public float getFalseTouchEvaluation() {
+ return ProximityEvaluator.evaluate(mAverageNear);
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/ProximityEvaluator.java b/java/com/android/incallui/answer/impl/classifier/ProximityEvaluator.java
new file mode 100644
index 000000000..14636c644
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/ProximityEvaluator.java
@@ -0,0 +1,28 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+class ProximityEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ float threshold = 0.1f;
+ if (value >= threshold) {
+ evaluation += 2.0f;
+ }
+ return evaluation;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/SpeedAnglesClassifier.java b/java/com/android/incallui/answer/impl/classifier/SpeedAnglesClassifier.java
new file mode 100644
index 000000000..36ae3ad7c
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/SpeedAnglesClassifier.java
@@ -0,0 +1,147 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+import android.util.ArrayMap;
+import android.view.MotionEvent;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A classifier which for each point from a stroke, it creates a point on plane with coordinates
+ * (timeOffsetNano, distanceCoveredUpToThisPoint) (scaled by DURATION_SCALE and LENGTH_SCALE) and
+ * then it calculates the angle variance of these points like the class {@link AnglesClassifier}
+ * (without splitting it into two parts). The classifier ignores the last point of a stroke because
+ * the UP event comes in with some delay and this ruins the smoothness of this curve. Additionally,
+ * the classifier classifies calculates the percentage of angles which value is in [PI -
+ * ANGLE_DEVIATION, 2* PI) interval. The reason why the classifier does that is because the speed of
+ * a good stroke is most often increases, so most of these angels should be in this interval.
+ */
+class SpeedAnglesClassifier extends StrokeClassifier {
+ private Map<Stroke, Data> mStrokeMap = new ArrayMap<>();
+
+ public SpeedAnglesClassifier(ClassifierData classifierData) {
+ mClassifierData = classifierData;
+ }
+
+ @Override
+ public String getTag() {
+ return "SPD_ANG";
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ int action = event.getActionMasked();
+
+ if (action == MotionEvent.ACTION_DOWN) {
+ mStrokeMap.clear();
+ }
+
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
+
+ if (mStrokeMap.get(stroke) == null) {
+ mStrokeMap.put(stroke, new Data());
+ }
+
+ if (action != MotionEvent.ACTION_UP
+ && action != MotionEvent.ACTION_CANCEL
+ && !(action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
+ mStrokeMap.get(stroke).addPoint(stroke.getPoints().get(stroke.getPoints().size() - 1));
+ }
+ }
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(Stroke stroke) {
+ Data data = mStrokeMap.get(stroke);
+ return SpeedVarianceEvaluator.evaluate(data.getAnglesVariance())
+ + SpeedAnglesPercentageEvaluator.evaluate(data.getAnglesPercentage());
+ }
+
+ private static class Data {
+ private static final float DURATION_SCALE = 1e8f;
+ private static final float LENGTH_SCALE = 1.0f;
+ private static final float ANGLE_DEVIATION = (float) Math.PI / 10.0f;
+
+ private List<Point> mLastThreePoints = new ArrayList<>();
+ private Point mPreviousPoint;
+ private float mPreviousAngle;
+ private float mSumSquares;
+ private float mSum;
+ private float mCount;
+ private float mDist;
+ private float mAnglesCount;
+ private float mAcceleratingAngles;
+
+ public Data() {
+ mPreviousPoint = null;
+ mPreviousAngle = (float) Math.PI;
+ mSumSquares = 0.0f;
+ mSum = 0.0f;
+ mCount = 1.0f;
+ mDist = 0.0f;
+ mAnglesCount = mAcceleratingAngles = 0.0f;
+ }
+
+ public void addPoint(Point point) {
+ if (mPreviousPoint != null) {
+ mDist += mPreviousPoint.dist(point);
+ }
+
+ mPreviousPoint = point;
+ Point speedPoint =
+ new Point((float) point.timeOffsetNano / DURATION_SCALE, mDist / LENGTH_SCALE);
+
+ // Checking if the added point is different than the previously added point
+ // Repetitions are being ignored so that proper angles are calculated.
+ if (mLastThreePoints.isEmpty()
+ || !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(speedPoint)) {
+ mLastThreePoints.add(speedPoint);
+ if (mLastThreePoints.size() == 4) {
+ mLastThreePoints.remove(0);
+
+ float angle =
+ mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0), mLastThreePoints.get(2));
+
+ mAnglesCount++;
+ if (angle >= (float) Math.PI - ANGLE_DEVIATION) {
+ mAcceleratingAngles++;
+ }
+
+ float difference = angle - mPreviousAngle;
+ mSum += difference;
+ mSumSquares += difference * difference;
+ mCount += 1.0f;
+ mPreviousAngle = angle;
+ }
+ }
+ }
+
+ public float getAnglesVariance() {
+ return mSumSquares / mCount - (mSum / mCount) * (mSum / mCount);
+ }
+
+ public float getAnglesPercentage() {
+ if (mAnglesCount == 0.0f) {
+ return 1.0f;
+ }
+ return (mAcceleratingAngles) / mAnglesCount;
+ }
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/SpeedAnglesPercentageEvaluator.java b/java/com/android/incallui/answer/impl/classifier/SpeedAnglesPercentageEvaluator.java
new file mode 100644
index 000000000..5a8bc3556
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/SpeedAnglesPercentageEvaluator.java
@@ -0,0 +1,33 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+class SpeedAnglesPercentageEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 1.00) {
+ evaluation++;
+ }
+ if (value < 0.90) {
+ evaluation++;
+ }
+ if (value < 0.70) {
+ evaluation++;
+ }
+ return evaluation;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/SpeedClassifier.java b/java/com/android/incallui/answer/impl/classifier/SpeedClassifier.java
new file mode 100644
index 000000000..f3ade3f49
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/SpeedClassifier.java
@@ -0,0 +1,40 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+/**
+ * A classifier that looks at the speed of the stroke. It calculates the speed of a stroke in inches
+ * per second.
+ */
+class SpeedClassifier extends StrokeClassifier {
+
+ public SpeedClassifier(ClassifierData classifierData) {}
+
+ @Override
+ public String getTag() {
+ return "SPD";
+ }
+
+ @Override
+ public float getFalseTouchEvaluation(Stroke stroke) {
+ float duration = stroke.getDurationSeconds();
+ if (duration == 0.0f) {
+ return SpeedEvaluator.evaluate(0.0f);
+ }
+ return SpeedEvaluator.evaluate(stroke.getTotalLength() / duration);
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/SpeedEvaluator.java b/java/com/android/incallui/answer/impl/classifier/SpeedEvaluator.java
new file mode 100644
index 000000000..4f9aace0e
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/SpeedEvaluator.java
@@ -0,0 +1,36 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+class SpeedEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value < 4.0) {
+ evaluation++;
+ }
+ if (value < 2.2) {
+ evaluation++;
+ }
+ if (value > 35.0) {
+ evaluation++;
+ }
+ if (value > 50.0) {
+ evaluation++;
+ }
+ return evaluation;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/SpeedRatioEvaluator.java b/java/com/android/incallui/answer/impl/classifier/SpeedRatioEvaluator.java
new file mode 100644
index 000000000..7ae111313
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/SpeedRatioEvaluator.java
@@ -0,0 +1,39 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+class SpeedRatioEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value == 0) {
+ return 0;
+ }
+ if (value <= 1.0) {
+ evaluation++;
+ }
+ if (value <= 0.5) {
+ evaluation++;
+ }
+ if (value > 9.0) {
+ evaluation++;
+ }
+ if (value > 18.0) {
+ evaluation++;
+ }
+ return evaluation;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/SpeedVarianceEvaluator.java b/java/com/android/incallui/answer/impl/classifier/SpeedVarianceEvaluator.java
new file mode 100644
index 000000000..211650cbb
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/SpeedVarianceEvaluator.java
@@ -0,0 +1,36 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+class SpeedVarianceEvaluator {
+ public static float evaluate(float value) {
+ float evaluation = 0.0f;
+ if (value > 0.06) {
+ evaluation++;
+ }
+ if (value > 0.15) {
+ evaluation++;
+ }
+ if (value > 0.3) {
+ evaluation++;
+ }
+ if (value > 0.6) {
+ evaluation++;
+ }
+ return evaluation;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/Stroke.java b/java/com/android/incallui/answer/impl/classifier/Stroke.java
new file mode 100644
index 000000000..c542d0f7c
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/Stroke.java
@@ -0,0 +1,72 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+import java.util.ArrayList;
+
+/**
+ * Contains data about a stroke (a single trace, all the events from a given id from the
+ * DOWN/POINTER_DOWN event till the UP/POINTER_UP/CANCEL event.)
+ */
+class Stroke {
+
+ private static final float NANOS_TO_SECONDS = 1e9f;
+
+ private ArrayList<Point> mPoints = new ArrayList<>();
+ private long mStartTimeNano;
+ private long mEndTimeNano;
+ private float mLength;
+ private final float mDpi;
+
+ public Stroke(long eventTimeNano, float dpi) {
+ mDpi = dpi;
+ mStartTimeNano = mEndTimeNano = eventTimeNano;
+ }
+
+ public void addPoint(float x, float y, long eventTimeNano) {
+ mEndTimeNano = eventTimeNano;
+ Point point = new Point(x / mDpi, y / mDpi, eventTimeNano - mStartTimeNano);
+ if (!mPoints.isEmpty()) {
+ mLength += mPoints.get(mPoints.size() - 1).dist(point);
+ }
+ mPoints.add(point);
+ }
+
+ public int getCount() {
+ return mPoints.size();
+ }
+
+ public float getTotalLength() {
+ return mLength;
+ }
+
+ public float getEndPointLength() {
+ return mPoints.get(0).dist(mPoints.get(mPoints.size() - 1));
+ }
+
+ public long getDurationNanos() {
+ return mEndTimeNano - mStartTimeNano;
+ }
+
+ public float getDurationSeconds() {
+ return (float) getDurationNanos() / NANOS_TO_SECONDS;
+ }
+
+ public ArrayList<Point> getPoints() {
+ return mPoints;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/classifier/StrokeClassifier.java b/java/com/android/incallui/answer/impl/classifier/StrokeClassifier.java
new file mode 100644
index 000000000..8abd7e2ec
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/classifier/StrokeClassifier.java
@@ -0,0 +1,28 @@
+/*
+ * 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.incallui.answer.impl.classifier;
+
+/** An abstract class for classifiers which classify each stroke separately. */
+abstract class StrokeClassifier extends Classifier {
+
+ /**
+ * @param stroke the stroke for which the evaluation will be calculated
+ * @return a non-negative value which is used to determine whether this a false touch; the bigger
+ * the value the greater the chance that this a false touch
+ */
+ public abstract float getFalseTouchEvaluation(Stroke stroke);
+}
diff --git a/java/com/android/incallui/answer/impl/hint/AndroidManifest.xml b/java/com/android/incallui/answer/impl/hint/AndroidManifest.xml
new file mode 100644
index 000000000..b5fa6da8f
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<manifest
+ package="com.android.incallui.answer.impl.hint"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <application>
+ <receiver android:name=".EventSecretCodeListener">
+ <intent-filter>
+ <action android:name="android.provider.Telephony.SECRET_CODE" />
+ <data android:scheme="android_secret_code" />
+ </intent-filter>
+ </receiver>
+ </application>
+</manifest>
diff --git a/java/com/android/incallui/answer/impl/hint/AnswerHint.java b/java/com/android/incallui/answer/impl/hint/AnswerHint.java
new file mode 100644
index 000000000..dd3b8228a
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/AnswerHint.java
@@ -0,0 +1,46 @@
+/*
+ * 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.incallui.answer.impl.hint;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/** Interface to overlay a hint of how to answer the call. */
+public interface AnswerHint {
+
+ /**
+ * Inflates the hint's layout into the container.
+ *
+ * <p>TODO: if the hint becomes more dependent on other UI elements of the AnswerFragment,
+ * should put put and hintText into another data structure.
+ */
+ void onCreateView(LayoutInflater inflater, ViewGroup container, View puck, TextView hintText);
+
+ /** Called when the puck bounce animation begins. */
+ void onBounceStart();
+
+ /**
+ * Called when the bounce animation has ended (transitioned into other animations). The hint
+ * should reset itself.
+ */
+ void onBounceEnd();
+
+ /** Called when the call is accepted or rejected through user interaction. */
+ void onAnswered();
+}
diff --git a/java/com/android/incallui/answer/impl/hint/AnswerHintFactory.java b/java/com/android/incallui/answer/impl/hint/AnswerHintFactory.java
new file mode 100644
index 000000000..45395a71f
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/AnswerHintFactory.java
@@ -0,0 +1,133 @@
+/*
+ * 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.incallui.answer.impl.hint;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.preference.PreferenceManager;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.ConfigProvider;
+import com.android.dialer.common.ConfigProviderBindings;
+import com.android.dialer.common.LogUtil;
+import com.android.incallui.util.AccessibilityUtil;
+import java.util.Calendar;
+
+/**
+ * Selects a AnswerHint to show. If there's no suitable hints {@link EmptyAnswerHint} will be used,
+ * which does nothing.
+ */
+public class AnswerHintFactory {
+
+ private static final String CONFIG_ANSWER_HINT_ANSWERED_THRESHOLD_KEY =
+ "answer_hint_answered_threshold";
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ static final String CONFIG_ANSWER_HINT_WHITELISTED_DEVICES_KEY =
+ "answer_hint_whitelisted_devices";
+ // Most popular devices released before NDR1 is whitelisted. Their user are likely to have seen
+ // the legacy UI.
+ private static final String DEFAULT_WHITELISTED_DEVICES_CSV =
+ "/hammerhead//bullhead//angler//shamu//gm4g//gm4g_s//AQ4501//gce_x86_phone//gm4gtkc_s/"
+ + "/Sparkle_V//Mi-498//AQ4502//imobileiq2//A65//H940//m8_google//m0xx//A10//ctih220/"
+ + "/Mi438S//bacon/";
+
+ @VisibleForTesting
+ static final String ANSWERED_COUNT_PREFERENCE_KEY = "answer_hint_answered_count";
+
+ private final EventPayloadLoader eventPayloadLoader;
+
+ public AnswerHintFactory(@NonNull EventPayloadLoader eventPayloadLoader) {
+ this.eventPayloadLoader = Assert.isNotNull(eventPayloadLoader);
+ }
+
+ @NonNull
+ public AnswerHint create(Context context, long puckUpDuration, long puckUpDelay) {
+
+ if (shouldShowAnswerHint(
+ context,
+ ConfigProviderBindings.get(context),
+ getDeviceProtectedPreferences(context),
+ Build.PRODUCT)) {
+ return new DotAnswerHint(context, puckUpDuration, puckUpDelay);
+ }
+
+ // Display the event answer hint if the payload is available.
+ Drawable eventPayload =
+ eventPayloadLoader.loadPayload(
+ context, System.currentTimeMillis(), Calendar.getInstance().getTimeZone());
+ if (eventPayload != null) {
+ return new EventAnswerHint(context, eventPayload, puckUpDuration, puckUpDelay);
+ }
+
+ return new EmptyAnswerHint();
+ }
+
+ public static void increaseAnsweredCount(Context context) {
+ SharedPreferences sharedPreferences = getDeviceProtectedPreferences(context);
+ int answeredCount = sharedPreferences.getInt(ANSWERED_COUNT_PREFERENCE_KEY, 0);
+ sharedPreferences.edit().putInt(ANSWERED_COUNT_PREFERENCE_KEY, answeredCount + 1).apply();
+ }
+
+ @VisibleForTesting
+ static boolean shouldShowAnswerHint(
+ Context context,
+ ConfigProvider configProvider,
+ SharedPreferences sharedPreferences,
+ String device) {
+ if (AccessibilityUtil.isTouchExplorationEnabled(context)) {
+ return false;
+ }
+ // Devices that has the legacy dialer installed are whitelisted as they are likely to go through
+ // a UX change during updates.
+ if (!isDeviceWhitelisted(device, configProvider)) {
+ return false;
+ }
+
+ // If the user has gone through the process a few times we can assume they have learnt the
+ // method.
+ int answeredCount = sharedPreferences.getInt(ANSWERED_COUNT_PREFERENCE_KEY, 0);
+ long threshold = configProvider.getLong(CONFIG_ANSWER_HINT_ANSWERED_THRESHOLD_KEY, 3);
+ LogUtil.i(
+ "AnswerHintFactory.shouldShowAnswerHint",
+ "answerCount: %d, threshold: %d",
+ answeredCount,
+ threshold);
+ return answeredCount < threshold;
+ }
+
+ /**
+ * @param device should be the value of{@link Build#PRODUCT}.
+ * @param configProvider should provide a list of devices quoted with '/' concatenated to a
+ * string.
+ */
+ private static boolean isDeviceWhitelisted(String device, ConfigProvider configProvider) {
+ return configProvider
+ .getString(CONFIG_ANSWER_HINT_WHITELISTED_DEVICES_KEY, DEFAULT_WHITELISTED_DEVICES_CSV)
+ .contains("/" + device + "/");
+ }
+
+ private static SharedPreferences getDeviceProtectedPreferences(Context context) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+ return PreferenceManager.getDefaultSharedPreferences(context);
+ }
+ return PreferenceManager.getDefaultSharedPreferences(
+ context.createDeviceProtectedStorageContext());
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/hint/DotAnswerHint.java b/java/com/android/incallui/answer/impl/hint/DotAnswerHint.java
new file mode 100644
index 000000000..394fe5808
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/DotAnswerHint.java
@@ -0,0 +1,283 @@
+/*
+ * 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.incallui.answer.impl.hint;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.support.annotation.DimenRes;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.widget.TextView;
+
+/** An Answer hint that uses a green swiping dot. */
+public class DotAnswerHint implements AnswerHint {
+
+ private static final float ANSWER_HINT_SMALL_ALPHA = 0.8f;
+ private static final float ANSWER_HINT_MID_ALPHA = 0.5f;
+ private static final float ANSWER_HINT_LARGE_ALPHA = 0.2f;
+
+ private static final long FADE_IN_DELAY_SCALE_MILLIS = 380;
+ private static final long FADE_IN_DURATION_SCALE_MILLIS = 200;
+ private static final long FADE_IN_DELAY_ALPHA_MILLIS = 340;
+ private static final long FADE_IN_DURATION_ALPHA_MILLIS = 50;
+
+ private static final long SWIPE_UP_DURATION_ALPHA_MILLIS = 500;
+
+ private static final long FADE_OUT_DELAY_SCALE_SMALL_MILLIS = 90;
+ private static final long FADE_OUT_DELAY_SCALE_MID_MILLIS = 70;
+ private static final long FADE_OUT_DELAY_SCALE_LARGE_MILLIS = 10;
+ private static final long FADE_OUT_DURATION_SCALE_MILLIS = 100;
+ private static final long FADE_OUT_DELAY_ALPHA_MILLIS = 130;
+ private static final long FADE_OUT_DURATION_ALPHA_MILLIS = 170;
+
+ private final Context context;
+ private final long puckUpDurationMillis;
+ private final long puckUpDelayMillis;
+
+ private View puck;
+
+ private View answerHintSmall;
+ private View answerHintMid;
+ private View answerHintLarge;
+ private View answerHintContainer;
+ private AnimatorSet answerGestureHintAnim;
+
+ public DotAnswerHint(Context context, long puckUpDurationMillis, long puckUpDelayMillis) {
+ this.context = context;
+ this.puckUpDurationMillis = puckUpDurationMillis;
+ this.puckUpDelayMillis = puckUpDelayMillis;
+ }
+
+ @Override
+ public void onCreateView(
+ LayoutInflater inflater, ViewGroup container, View puck, TextView hintText) {
+ this.puck = puck;
+ View view = inflater.inflate(R.layout.dot_hint, container, true);
+ answerHintContainer = view.findViewById(R.id.answer_hint_container);
+ answerHintSmall = view.findViewById(R.id.answer_hint_small);
+ answerHintMid = view.findViewById(R.id.answer_hint_mid);
+ answerHintLarge = view.findViewById(R.id.answer_hint_large);
+ hintText.setTextSize(
+ TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.hint_text_size));
+ }
+
+ @Override
+ public void onBounceStart() {
+ if (answerGestureHintAnim == null) {
+ answerGestureHintAnim = new AnimatorSet();
+ answerHintContainer.setY(puck.getY() + getDimension(R.dimen.hint_initial_offset));
+
+ Animator fadeIn = createFadeIn();
+
+ Animator swipeUp =
+ ObjectAnimator.ofFloat(
+ answerHintContainer,
+ View.TRANSLATION_Y,
+ puck.getY() - getDimension(R.dimen.hint_offset));
+ swipeUp.setInterpolator(new FastOutSlowInInterpolator());
+ swipeUp.setDuration(SWIPE_UP_DURATION_ALPHA_MILLIS);
+
+ Animator fadeOut = createFadeOut();
+
+ answerGestureHintAnim.play(fadeIn).after(puckUpDelayMillis);
+ answerGestureHintAnim.play(swipeUp).after(fadeIn);
+ // The fade out should start fading the alpha just as the puck is dropping. Scaling will start
+ // a bit earlier.
+ answerGestureHintAnim
+ .play(fadeOut)
+ .after(puckUpDelayMillis + puckUpDurationMillis - FADE_OUT_DELAY_ALPHA_MILLIS);
+
+ fadeIn.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ answerHintSmall.setAlpha(0);
+ answerHintSmall.setScaleX(1);
+ answerHintSmall.setScaleY(1);
+ answerHintMid.setAlpha(0);
+ answerHintMid.setScaleX(1);
+ answerHintMid.setScaleY(1);
+ answerHintLarge.setAlpha(0);
+ answerHintLarge.setScaleX(1);
+ answerHintLarge.setScaleY(1);
+ answerHintContainer.setY(puck.getY() + getDimension(R.dimen.hint_initial_offset));
+ answerHintContainer.setVisibility(View.VISIBLE);
+ }
+ });
+ }
+
+ answerGestureHintAnim.start();
+ }
+
+ private Animator createFadeIn() {
+ AnimatorSet set = new AnimatorSet();
+ set.play(
+ createFadeInScaleAndAlpha(
+ answerHintSmall,
+ R.dimen.hint_small_begin_size,
+ R.dimen.hint_small_end_size,
+ ANSWER_HINT_SMALL_ALPHA))
+ .with(
+ createFadeInScaleAndAlpha(
+ answerHintMid,
+ R.dimen.hint_mid_begin_size,
+ R.dimen.hint_mid_end_size,
+ ANSWER_HINT_MID_ALPHA))
+ .with(
+ createFadeInScaleAndAlpha(
+ answerHintLarge,
+ R.dimen.hint_large_begin_size,
+ R.dimen.hint_large_end_size,
+ ANSWER_HINT_LARGE_ALPHA));
+ return set;
+ }
+
+ private Animator createFadeInScaleAndAlpha(
+ View target, @DimenRes int beginSize, @DimenRes int endSize, float endAlpha) {
+ Animator scale =
+ createUniformScaleAnimator(
+ target,
+ getDimension(beginSize),
+ getDimension(beginSize),
+ getDimension(endSize),
+ FADE_IN_DURATION_SCALE_MILLIS,
+ FADE_IN_DELAY_SCALE_MILLIS,
+ new LinearInterpolator());
+ Animator alpha =
+ createAlphaAnimator(
+ target,
+ 0f,
+ endAlpha,
+ FADE_IN_DURATION_ALPHA_MILLIS,
+ FADE_IN_DELAY_ALPHA_MILLIS,
+ new LinearInterpolator());
+ AnimatorSet set = new AnimatorSet();
+ set.play(scale).with(alpha);
+ return set;
+ }
+
+ private Animator createFadeOut() {
+ AnimatorSet set = new AnimatorSet();
+ set.play(
+ createFadeOutScaleAndAlpha(
+ answerHintSmall,
+ R.dimen.hint_small_begin_size,
+ R.dimen.hint_small_end_size,
+ FADE_OUT_DELAY_SCALE_SMALL_MILLIS,
+ ANSWER_HINT_SMALL_ALPHA))
+ .with(
+ createFadeOutScaleAndAlpha(
+ answerHintMid,
+ R.dimen.hint_mid_begin_size,
+ R.dimen.hint_mid_end_size,
+ FADE_OUT_DELAY_SCALE_MID_MILLIS,
+ ANSWER_HINT_MID_ALPHA))
+ .with(
+ createFadeOutScaleAndAlpha(
+ answerHintLarge,
+ R.dimen.hint_large_begin_size,
+ R.dimen.hint_large_end_size,
+ FADE_OUT_DELAY_SCALE_LARGE_MILLIS,
+ ANSWER_HINT_LARGE_ALPHA));
+ return set;
+ }
+
+ private Animator createFadeOutScaleAndAlpha(
+ View target,
+ @DimenRes int beginSize,
+ @DimenRes int endSize,
+ long scaleDelay,
+ float endAlpha) {
+ Animator scale =
+ createUniformScaleAnimator(
+ target,
+ getDimension(beginSize),
+ getDimension(endSize),
+ getDimension(beginSize),
+ FADE_OUT_DURATION_SCALE_MILLIS,
+ scaleDelay,
+ new LinearInterpolator());
+ Animator alpha =
+ createAlphaAnimator(
+ target,
+ endAlpha,
+ 0.0f,
+ FADE_OUT_DURATION_ALPHA_MILLIS,
+ FADE_OUT_DELAY_ALPHA_MILLIS,
+ new LinearInterpolator());
+ AnimatorSet set = new AnimatorSet();
+ set.play(scale).with(alpha);
+ return set;
+ }
+
+ @Override
+ public void onBounceEnd() {
+ if (answerGestureHintAnim != null) {
+ answerGestureHintAnim.end();
+ answerGestureHintAnim = null;
+ answerHintContainer.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void onAnswered() {
+ AnswerHintFactory.increaseAnsweredCount(context);
+ }
+
+ private float getDimension(@DimenRes int id) {
+ return context.getResources().getDimension(id);
+ }
+
+ private static Animator createUniformScaleAnimator(
+ View target,
+ float original,
+ float begin,
+ float end,
+ long duration,
+ long delay,
+ Interpolator interpolator) {
+ float scaleBegin = begin / original;
+ float scaleEnd = end / original;
+ Animator scaleX = ObjectAnimator.ofFloat(target, View.SCALE_X, scaleBegin, scaleEnd);
+ Animator scaleY = ObjectAnimator.ofFloat(target, View.SCALE_Y, scaleBegin, scaleEnd);
+ scaleX.setDuration(duration);
+ scaleY.setDuration(duration);
+ scaleX.setInterpolator(interpolator);
+ scaleY.setInterpolator(interpolator);
+ AnimatorSet set = new AnimatorSet();
+ set.play(scaleX).with(scaleY).after(delay);
+ return set;
+ }
+
+ private static Animator createAlphaAnimator(
+ View target, float begin, float end, long duration, long delay, Interpolator interpolator) {
+ Animator alpha = ObjectAnimator.ofFloat(target, View.ALPHA, begin, end);
+ alpha.setDuration(duration);
+ alpha.setInterpolator(interpolator);
+ alpha.setStartDelay(delay);
+ return alpha;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/hint/EmptyAnswerHint.java b/java/com/android/incallui/answer/impl/hint/EmptyAnswerHint.java
new file mode 100644
index 000000000..e52b4ee36
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/EmptyAnswerHint.java
@@ -0,0 +1,39 @@
+/*
+ * 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.incallui.answer.impl.hint;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/** Does nothing. Used to avoid null checks on AnswerHint. */
+public class EmptyAnswerHint implements AnswerHint {
+
+ @Override
+ public void onCreateView(
+ LayoutInflater inflater, ViewGroup container, View puck, TextView hintText) {}
+
+ @Override
+ public void onBounceStart() {}
+
+ @Override
+ public void onBounceEnd() {}
+
+ @Override
+ public void onAnswered() {}
+}
diff --git a/java/com/android/incallui/answer/impl/hint/EventAnswerHint.java b/java/com/android/incallui/answer/impl/hint/EventAnswerHint.java
new file mode 100644
index 000000000..7ee327d50
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/EventAnswerHint.java
@@ -0,0 +1,235 @@
+/*
+ * 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.incallui.answer.impl.hint;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.DimenRes;
+import android.support.annotation.NonNull;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.android.dialer.common.Assert;
+
+/**
+ * An Answer hint that animates a {@link Drawable} payload with animation similar to {@link
+ * DotAnswerHint}.
+ */
+public final class EventAnswerHint implements AnswerHint {
+
+ private static final long FADE_IN_DELAY_SCALE_MILLIS = 380;
+ private static final long FADE_IN_DURATION_SCALE_MILLIS = 200;
+ private static final long FADE_IN_DELAY_ALPHA_MILLIS = 340;
+ private static final long FADE_IN_DURATION_ALPHA_MILLIS = 50;
+
+ private static final long SWIPE_UP_DURATION_ALPHA_MILLIS = 500;
+
+ private static final long FADE_OUT_DELAY_SCALE_SMALL_MILLIS = 90;
+ private static final long FADE_OUT_DURATION_SCALE_MILLIS = 100;
+ private static final long FADE_OUT_DELAY_ALPHA_MILLIS = 130;
+ private static final long FADE_OUT_DURATION_ALPHA_MILLIS = 170;
+
+ private static final float FADE_SCALE = 1.2f;
+
+ private final Context context;
+ private final Drawable payload;
+ private final long puckUpDurationMillis;
+ private final long puckUpDelayMillis;
+
+ private View puck;
+ private View payloadView;
+ private View answerHintContainer;
+ private AnimatorSet answerGestureHintAnim;
+
+ public EventAnswerHint(
+ @NonNull Context context,
+ @NonNull Drawable payload,
+ long puckUpDurationMillis,
+ long puckUpDelayMillis) {
+ this.context = Assert.isNotNull(context);
+ this.payload = Assert.isNotNull(payload);
+ this.puckUpDurationMillis = puckUpDurationMillis;
+ this.puckUpDelayMillis = puckUpDelayMillis;
+ }
+
+ @Override
+ public void onCreateView(
+ LayoutInflater inflater, ViewGroup container, View puck, TextView hintText) {
+ this.puck = puck;
+ View view = inflater.inflate(R.layout.event_hint, container, true);
+ answerHintContainer = view.findViewById(R.id.answer_hint_container);
+ payloadView = view.findViewById(R.id.payload);
+ hintText.setTextSize(
+ TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.hint_text_size));
+ ((ImageView) payloadView).setImageDrawable(payload);
+ }
+
+ @Override
+ public void onBounceStart() {
+ if (answerGestureHintAnim == null) {
+
+ answerGestureHintAnim = new AnimatorSet();
+ answerHintContainer.setY(puck.getY() + getDimension(R.dimen.hint_initial_offset));
+
+ Animator fadeIn = createFadeIn();
+
+ Animator swipeUp =
+ ObjectAnimator.ofFloat(
+ answerHintContainer,
+ View.TRANSLATION_Y,
+ puck.getY() - getDimension(R.dimen.hint_offset));
+ swipeUp.setInterpolator(new FastOutSlowInInterpolator());
+ swipeUp.setDuration(SWIPE_UP_DURATION_ALPHA_MILLIS);
+
+ Animator fadeOut = createFadeOut();
+
+ answerGestureHintAnim.play(fadeIn).after(puckUpDelayMillis);
+ answerGestureHintAnim.play(swipeUp).after(fadeIn);
+ // The fade out should start fading the alpha just as the puck is dropping. Scaling will start
+ // a bit earlier.
+ answerGestureHintAnim
+ .play(fadeOut)
+ .after(puckUpDelayMillis + puckUpDurationMillis - FADE_OUT_DELAY_ALPHA_MILLIS);
+
+ fadeIn.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ payloadView.setAlpha(0);
+ payloadView.setScaleX(1);
+ payloadView.setScaleY(1);
+ answerHintContainer.setY(puck.getY() + getDimension(R.dimen.hint_initial_offset));
+ answerHintContainer.setVisibility(View.VISIBLE);
+ }
+ });
+ }
+
+ answerGestureHintAnim.start();
+ }
+
+ private Animator createFadeIn() {
+ AnimatorSet set = new AnimatorSet();
+ set.play(createFadeInScaleAndAlpha(payloadView));
+ return set;
+ }
+
+ private static Animator createFadeInScaleAndAlpha(View target) {
+ Animator scale =
+ createUniformScaleAnimator(
+ target,
+ FADE_SCALE,
+ 1.0f,
+ FADE_IN_DURATION_SCALE_MILLIS,
+ FADE_IN_DELAY_SCALE_MILLIS,
+ new LinearInterpolator());
+ Animator alpha =
+ createAlphaAnimator(
+ target,
+ 0f,
+ 1.0f,
+ FADE_IN_DURATION_ALPHA_MILLIS,
+ FADE_IN_DELAY_ALPHA_MILLIS,
+ new LinearInterpolator());
+ AnimatorSet set = new AnimatorSet();
+ set.play(scale).with(alpha);
+ return set;
+ }
+
+ private Animator createFadeOut() {
+ AnimatorSet set = new AnimatorSet();
+ set.play(createFadeOutScaleAndAlpha(payloadView, FADE_OUT_DELAY_SCALE_SMALL_MILLIS));
+ return set;
+ }
+
+ private static Animator createFadeOutScaleAndAlpha(View target, long scaleDelay) {
+ Animator scale =
+ createUniformScaleAnimator(
+ target,
+ 1.0f,
+ FADE_SCALE,
+ FADE_OUT_DURATION_SCALE_MILLIS,
+ scaleDelay,
+ new LinearInterpolator());
+ Animator alpha =
+ createAlphaAnimator(
+ target,
+ 01.0f,
+ 0.0f,
+ FADE_OUT_DURATION_ALPHA_MILLIS,
+ FADE_OUT_DELAY_ALPHA_MILLIS,
+ new LinearInterpolator());
+ AnimatorSet set = new AnimatorSet();
+ set.play(scale).with(alpha);
+ return set;
+ }
+
+ @Override
+ public void onBounceEnd() {
+ if (answerGestureHintAnim != null) {
+ answerGestureHintAnim.end();
+ answerGestureHintAnim = null;
+ answerHintContainer.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void onAnswered() {
+ // Do nothing
+ }
+
+ private float getDimension(@DimenRes int id) {
+ return context.getResources().getDimension(id);
+ }
+
+ private static Animator createUniformScaleAnimator(
+ View target,
+ float scaleBegin,
+ float scaleEnd,
+ long duration,
+ long delay,
+ Interpolator interpolator) {
+ Animator scaleX = ObjectAnimator.ofFloat(target, View.SCALE_X, scaleBegin, scaleEnd);
+ Animator scaleY = ObjectAnimator.ofFloat(target, View.SCALE_Y, scaleBegin, scaleEnd);
+ scaleX.setDuration(duration);
+ scaleY.setDuration(duration);
+ scaleX.setInterpolator(interpolator);
+ scaleY.setInterpolator(interpolator);
+ AnimatorSet set = new AnimatorSet();
+ set.play(scaleX).with(scaleY).after(delay);
+ return set;
+ }
+
+ private static Animator createAlphaAnimator(
+ View target, float begin, float end, long duration, long delay, Interpolator interpolator) {
+ Animator alpha = ObjectAnimator.ofFloat(target, View.ALPHA, begin, end);
+ alpha.setDuration(duration);
+ alpha.setInterpolator(interpolator);
+ alpha.setStartDelay(delay);
+ return alpha;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/hint/EventPayloadLoader.java b/java/com/android/incallui/answer/impl/hint/EventPayloadLoader.java
new file mode 100644
index 000000000..09e3bedf2
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/EventPayloadLoader.java
@@ -0,0 +1,30 @@
+/*
+ * 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.incallui.answer.impl.hint;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import java.util.TimeZone;
+
+/** Loads a {@link Drawable} payload for the {@link EventAnswerHint} if it should be displayed. */
+public interface EventPayloadLoader {
+ @Nullable
+ Drawable loadPayload(
+ @NonNull Context context, long currentTimeUtcMillis, @NonNull TimeZone timeZone);
+}
diff --git a/java/com/android/incallui/answer/impl/hint/EventPayloadLoaderImpl.java b/java/com/android/incallui/answer/impl/hint/EventPayloadLoaderImpl.java
new file mode 100644
index 000000000..bd8d73645
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/EventPayloadLoaderImpl.java
@@ -0,0 +1,118 @@
+/*
+ * 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.incallui.answer.impl.hint;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build.VERSION_CODES;
+import android.preference.PreferenceManager;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.ConfigProvider;
+import com.android.dialer.common.ConfigProviderBindings;
+import com.android.dialer.common.LogUtil;
+import java.io.InputStream;
+import java.util.TimeZone;
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+
+/** Decrypt the event payload to be shown if in a specific time range and the key is received. */
+@TargetApi(VERSION_CODES.M)
+public final class EventPayloadLoaderImpl implements EventPayloadLoader {
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ static final String CONFIG_EVENT_KEY = "event_key";
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ static final String CONFIG_EVENT_BINARY = "event_binary";
+
+ // Time is stored as a UTC UNIX timestamp in milliseconds, but interpreted as local time.
+ // For example, 946684800 (2000/1/1 00:00:00 @UTC) is the new year midnight at every timezone.
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ static final String CONFIG_EVENT_START_UTC_AS_LOCAL_MILLIS = "event_time_start_millis";
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ static final String CONFIG_EVENT_TIME_END_UTC_AS_LOCAL_MILLIS = "event_time_end_millis";
+
+ @Override
+ @Nullable
+ public Drawable loadPayload(
+ @NonNull Context context, long currentTimeUtcMillis, @NonNull TimeZone timeZone) {
+ Assert.isNotNull(context);
+ Assert.isNotNull(timeZone);
+ ConfigProvider configProvider = ConfigProviderBindings.get(context);
+
+ String pbeKey = configProvider.getString(CONFIG_EVENT_KEY, null);
+ if (pbeKey == null) {
+ return null;
+ }
+ long timeRangeStart = configProvider.getLong(CONFIG_EVENT_START_UTC_AS_LOCAL_MILLIS, 0);
+ long timeRangeEnd = configProvider.getLong(CONFIG_EVENT_TIME_END_UTC_AS_LOCAL_MILLIS, 0);
+
+ String eventBinary = configProvider.getString(CONFIG_EVENT_BINARY, null);
+ if (eventBinary == null) {
+ return null;
+ }
+
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ if (!preferences.getBoolean(
+ EventSecretCodeListener.EVENT_ENABLED_WITH_SECRET_CODE_KEY, false)) {
+ long localTimestamp = currentTimeUtcMillis + timeZone.getRawOffset();
+
+ if (localTimestamp < timeRangeStart) {
+ return null;
+ }
+
+ if (localTimestamp > timeRangeEnd) {
+ return null;
+ }
+ }
+
+ // Use openssl aes-128-cbc -in <input> -out <output> -pass <PBEKey> to generate the asset
+ try (InputStream input = context.getAssets().open(eventBinary)) {
+ byte[] encryptedFile = new byte[input.available()];
+ input.read(encryptedFile);
+
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
+
+ byte[] salt = new byte[8];
+ System.arraycopy(encryptedFile, 8, salt, 0, 8);
+ SecretKey key =
+ SecretKeyFactory.getInstance("PBEWITHMD5AND128BITAES-CBC-OPENSSL", "BC")
+ .generateSecret(new PBEKeySpec(pbeKey.toCharArray(), salt, 100));
+ cipher.init(Cipher.DECRYPT_MODE, key);
+
+ byte[] decryptedFile = cipher.doFinal(encryptedFile, 16, encryptedFile.length - 16);
+
+ return new BitmapDrawable(
+ context.getResources(),
+ BitmapFactory.decodeByteArray(decryptedFile, 0, decryptedFile.length));
+ } catch (Exception e) {
+ // Avoid crashing dialer for any reason.
+ LogUtil.e("EventPayloadLoader.loadPayload", "error decrypting payload:", e);
+ return null;
+ }
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/hint/EventSecretCodeListener.java b/java/com/android/incallui/answer/impl/hint/EventSecretCodeListener.java
new file mode 100644
index 000000000..7cf4054a9
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/EventSecretCodeListener.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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.incallui.answer.impl.hint;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.widget.Toast;
+import com.android.dialer.common.ConfigProviderBindings;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+
+/**
+ * Listen to the broadcast when the user dials "*#*#[number]#*#*" to toggle the event answer hint.
+ */
+public class EventSecretCodeListener extends BroadcastReceiver {
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ static final String CONFIG_EVENT_SECRET_CODE = "event_secret_code";
+
+ public static final String EVENT_ENABLED_WITH_SECRET_CODE_KEY = "event_enabled_with_secret_code";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String host = intent.getData().getHost();
+ String secretCode =
+ ConfigProviderBindings.get(context).getString(CONFIG_EVENT_SECRET_CODE, null);
+ if (secretCode == null) {
+ return;
+ }
+ if (!TextUtils.equals(secretCode, host)) {
+ return;
+ }
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ boolean wasEnabled = preferences.getBoolean(EVENT_ENABLED_WITH_SECRET_CODE_KEY, false);
+ if (wasEnabled) {
+ preferences.edit().putBoolean(EVENT_ENABLED_WITH_SECRET_CODE_KEY, false).apply();
+ Toast.makeText(context, R.string.event_deactivated, Toast.LENGTH_SHORT).show();
+ Logger.get(context).logImpression(DialerImpression.Type.EVENT_ANSWER_HINT_DEACTIVATED);
+ LogUtil.i("EventSecretCodeListener.onReceive", "EventAnswerHint disabled");
+ } else {
+ preferences.edit().putBoolean(EVENT_ENABLED_WITH_SECRET_CODE_KEY, true).apply();
+ Toast.makeText(context, R.string.event_activated, Toast.LENGTH_SHORT).show();
+ Logger.get(context).logImpression(DialerImpression.Type.EVENT_ANSWER_HINT_ACTIVATED);
+ LogUtil.i("EventSecretCodeListener.onReceive", "EventAnswerHint enabled");
+ }
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/hint/res/drawable/answer_hint_large.xml b/java/com/android/incallui/answer/impl/hint/res/drawable/answer_hint_large.xml
new file mode 100644
index 000000000..f585ce5c9
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/res/drawable/answer_hint_large.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+ <solid android:color="#00C853"/>
+</shape> \ No newline at end of file
diff --git a/java/com/android/incallui/answer/impl/hint/res/drawable/answer_hint_mid.xml b/java/com/android/incallui/answer/impl/hint/res/drawable/answer_hint_mid.xml
new file mode 100644
index 000000000..f585ce5c9
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/res/drawable/answer_hint_mid.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+ <solid android:color="#00C853"/>
+</shape> \ No newline at end of file
diff --git a/java/com/android/incallui/answer/impl/hint/res/drawable/answer_hint_small.xml b/java/com/android/incallui/answer/impl/hint/res/drawable/answer_hint_small.xml
new file mode 100644
index 000000000..6a24d6a5f
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/res/drawable/answer_hint_small.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+ <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+ <solid android:color="#00C853"/>
+ <stroke android:color="#00C853" android:width="2dp"/>
+ </shape> \ No newline at end of file
diff --git a/java/com/android/incallui/answer/impl/hint/res/layout/dot_hint.xml b/java/com/android/incallui/answer/impl/hint/res/layout/dot_hint.xml
new file mode 100644
index 000000000..84b10e736
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/res/layout/dot_hint.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/answer_hint_container"
+ android:layout_width="160dp"
+ android:layout_height="160dp"
+ android:layout_gravity="center_horizontal"
+ android:visibility="gone">
+ <ImageView
+ android:id="@+id/answer_hint_large"
+ android:layout_width="@dimen/hint_large_begin_size"
+ android:layout_height="@dimen/hint_large_begin_size"
+ android:layout_gravity="center"
+ android:alpha="0"
+ android:src="@drawable/answer_hint_large"/>
+ <ImageView
+ android:id="@+id/answer_hint_mid"
+ android:layout_width="@dimen/hint_mid_begin_size"
+ android:layout_height="@dimen/hint_mid_begin_size"
+ android:src="@drawable/answer_hint_mid"
+ android:alpha="0"
+ android:layout_gravity="center"/>
+ <ImageView
+ android:id="@+id/answer_hint_small"
+ android:layout_width="@dimen/hint_small_begin_size"
+ android:layout_height="@dimen/hint_small_begin_size"
+ android:src="@drawable/answer_hint_small"
+ android:alpha="0"
+ android:layout_gravity="center" />
+</FrameLayout> \ No newline at end of file
diff --git a/java/com/android/incallui/answer/impl/hint/res/layout/event_hint.xml b/java/com/android/incallui/answer/impl/hint/res/layout/event_hint.xml
new file mode 100644
index 000000000..d505014c1
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/res/layout/event_hint.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/answer_hint_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:visibility="gone">
+ <ImageView
+ android:id="@+id/payload"
+ android:layout_width="191dp"
+ android:layout_height="773dp"
+ android:layout_gravity="center"
+ android:alpha="0"
+ android:rotation="-30"
+ android:transformPivotY="90dp"
+ android:clipChildren="false"
+ android:clipToPadding="false"/>
+</FrameLayout> \ No newline at end of file
diff --git a/java/com/android/incallui/answer/impl/hint/res/values/dimens.xml b/java/com/android/incallui/answer/impl/hint/res/values/dimens.xml
new file mode 100644
index 000000000..d86084b74
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/res/values/dimens.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="hint_text_size">18sp</dimen>
+ <dimen name="hint_initial_offset">-100dp</dimen>
+ <dimen name="hint_offset">300dp</dimen>
+ <dimen name="hint_small_begin_size">50dp</dimen>
+ <dimen name="hint_small_end_size">42dp</dimen>
+ <dimen name="hint_mid_begin_size">56dp</dimen>
+ <dimen name="hint_mid_end_size">64dp</dimen>
+ <dimen name="hint_large_begin_size">64dp</dimen>
+ <dimen name="hint_large_end_size">160dp</dimen>
+</resources> \ No newline at end of file
diff --git a/java/com/android/incallui/answer/impl/hint/res/values/strings.xml b/java/com/android/incallui/answer/impl/hint/res/values/strings.xml
new file mode 100644
index 000000000..d76021ae1
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/hint/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="event_activated">Event Activated</string>
+ <string name="event_deactivated">Event Deactvated</string>
+</resources> \ No newline at end of file
diff --git a/java/com/android/incallui/answer/impl/res/anim/incoming_unlocked_icon_entry.xml b/java/com/android/incallui/answer/impl/res/anim/incoming_unlocked_icon_entry.xml
new file mode 100644
index 000000000..6490bbc5b
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/res/anim/incoming_unlocked_icon_entry.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:ordering="together">
+ <alpha
+ android:duration="583"
+ android:fromAlpha="0.0"
+ android:interpolator="@android:anim/accelerate_interpolator"
+ android:startOffset="167"
+ android:toAlpha="1.0"/>
+ <scale
+ android:duration="600"
+ android:fromXScale="0px"
+ android:fromYScale="0px"
+ android:interpolator="@android:anim/accelerate_interpolator"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:toXScale="100%"
+ android:toYScale="100%"/>
+</set>
diff --git a/java/com/android/incallui/answer/impl/res/anim/incoming_unlocked_text_entry.xml b/java/com/android/incallui/answer/impl/res/anim/incoming_unlocked_text_entry.xml
new file mode 100644
index 000000000..9d3195a79
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/res/anim/incoming_unlocked_text_entry.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <alpha
+ android:duration="583"
+ android:fromAlpha="0.0"
+ android:interpolator="@android:anim/accelerate_interpolator"
+ android:startOffset="167"
+ android:toAlpha="1.0"/>
+</set>
diff --git a/java/com/android/incallui/answer/impl/res/layout/fragment_avatar.xml b/java/com/android/incallui/answer/impl/res/layout/fragment_avatar.xml
new file mode 100644
index 000000000..d656ceb4e
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/res/layout/fragment_avatar.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+
+<ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/contactgrid_avatar"
+ android:layout_width="@dimen/answer_avatar_size"
+ android:layout_height="@dimen/answer_avatar_size"
+ android:layout_marginTop="20dp"
+ android:layout_gravity="center_horizontal"
+ android:elevation="@dimen/answer_data_elevation"/>
diff --git a/java/com/android/incallui/answer/impl/res/layout/fragment_custom_sms_dialog.xml b/java/com/android/incallui/answer/impl/res/layout/fragment_custom_sms_dialog.xml
new file mode 100644
index 000000000..c36386ead
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/res/layout/fragment_custom_sms_dialog.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp">
+
+ <EditText
+ android:id="@+id/custom_sms_input"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+</FrameLayout> \ No newline at end of file
diff --git a/java/com/android/incallui/answer/impl/res/layout/fragment_incoming_call.xml b/java/com/android/incallui/answer/impl/res/layout/fragment_incoming_call.xml
new file mode 100644
index 000000000..aa153dd4b
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/res/layout/fragment_incoming_call.xml
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<com.android.incallui.answer.impl.AffordanceHolderLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/incoming_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:keepScreenOn="true">
+
+ <TextureView
+ android:id="@+id/incoming_preview_texture_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:importantForAccessibility="no"
+ android:visibility="gone"/>
+
+ <View
+ android:id="@+id/incoming_preview_texture_view_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/videocall_overlay_background_color"
+ android:visibility="gone"/>
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true">
+
+ <TextView
+ android:id="@+id/videocall_video_off"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:padding="64dp"
+ android:accessibilityTraversalBefore="@+id/videocall_speaker_button"
+ android:drawablePadding="8dp"
+ android:drawableTop="@drawable/quantum_ic_videocam_off_white_36"
+ android:gravity="center"
+ android:text="@string/call_incoming_video_is_off"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance"
+ android:visibility="gone"/>
+
+ <LinearLayout
+ android:id="@+id/incall_contact_grid"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="24dp"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:gravity="top|center_horizontal"
+ android:orientation="vertical">
+
+ <include
+ android:id="@id/contactgrid_top_row"
+ layout="@layout/incall_contactgrid_top_row"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:layout_marginStart="24dp"
+ android:layout_marginEnd="24dp"/>
+
+ <!-- We have to keep deprecated singleLine to allow long text being truncated with ellipses.
+ b/31396406 -->
+ <com.android.incallui.autoresizetext.AutoResizeTextView
+ android:id="@id/contactgrid_contact_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:layout_marginStart="24dp"
+ android:layout_marginEnd="24dp"
+ android:singleLine="true"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance.Large"
+ android:textSize="@dimen/answer_contact_name_text_size"
+ app:autoResizeText_minTextSize="@dimen/answer_contact_name_min_size"
+ tools:ignore="Deprecated"
+ tools:text="Jake Peralta"/>
+
+ <include
+ android:id="@id/contactgrid_bottom_row"
+ layout="@layout/incall_contactgrid_bottom_row"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="24dp"
+ android:layout_marginEnd="24dp"/>
+
+ <TextView
+ android:id="@+id/incall_important_call_badge"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="@dimen/answer_importance_margin_bottom"
+ android:elevation="@dimen/answer_data_elevation"
+ android:gravity="center"
+ android:singleLine="true"
+ android:text="@string/call_incoming_important"
+ android:textAllCaps="true"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance"
+ android:textColor="@android:color/black"/>
+
+ <FrameLayout
+ android:id="@+id/incall_location_holder"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <FrameLayout
+ android:id="@+id/incall_data_container"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/answer_data_size"
+ android:clipChildren="false"
+ android:clipToPadding="false"/>
+
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/answer_method_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"/>
+
+ </FrameLayout>
+
+ <com.android.incallui.answer.impl.affordance.SwipeButtonView
+ android:id="@+id/incoming_secondary_button"
+ android:layout_width="56dp"
+ android:layout_height="56dp"
+ android:layout_gravity="bottom|start"
+ android:scaleType="center"
+ android:src="@drawable/quantum_ic_message_white_24"
+ android:visibility="invisible"
+ tools:visibility="visible"/>
+
+</com.android.incallui.answer.impl.AffordanceHolderLayout>
diff --git a/java/com/android/incallui/answer/impl/res/values-h240dp/dimens.xml b/java/com/android/incallui/answer/impl/res/values-h240dp/dimens.xml
new file mode 100644
index 000000000..ca384ef8d
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/res/values-h240dp/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <dimen name="answer_contact_name_text_size">36sp</dimen>
+ <dimen name="answer_contact_name_min_size">32sp</dimen>
+</resources>
diff --git a/java/com/android/incallui/answer/impl/res/values-h300dp/dimens.xml b/java/com/android/incallui/answer/impl/res/values-h300dp/dimens.xml
new file mode 100644
index 000000000..fdecbb7bf
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/res/values-h300dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <dimen name="answer_contact_name_text_size">54sp</dimen>
+</resources>
diff --git a/java/com/android/incallui/answer/impl/res/values-h480dp/dimens.xml b/java/com/android/incallui/answer/impl/res/values-h480dp/dimens.xml
new file mode 100644
index 000000000..5dc3f2ac5
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/res/values-h480dp/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources>
+ <dimen name="answer_data_size">150dp</dimen>
+ <dimen name="answer_avatar_size">100dp</dimen>
+ <dimen name="answer_importance_margin_bottom">8dp</dimen>
+ <bool name="answer_important_call_allowed">true</bool>
+</resources>
diff --git a/java/com/android/incallui/answer/impl/res/values-h540dp/dimens.xml b/java/com/android/incallui/answer/impl/res/values-h540dp/dimens.xml
new file mode 100644
index 000000000..69716e0bd
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/res/values-h540dp/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources>
+ <dimen name="answer_data_size">258dp</dimen>
+ <dimen name="answer_avatar_size">172dp</dimen>
+ <dimen name="answer_importance_margin_bottom">8dp</dimen>
+</resources>
diff --git a/java/com/android/incallui/answer/impl/res/values/dimens.xml b/java/com/android/incallui/answer/impl/res/values/dimens.xml
new file mode 100644
index 000000000..c48b68f93
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/res/values/dimens.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <dimen name="answer_contact_name_text_size">24sp</dimen>
+ <dimen name="answer_contact_name_min_size">24sp</dimen>
+ <dimen name="answer_data_size">0dp</dimen>
+ <dimen name="answer_avatar_size">0dp</dimen>
+ <dimen name="answer_importance_margin_bottom">0dp</dimen>
+ <bool name="answer_important_call_allowed">false</bool>
+</resources>
diff --git a/java/com/android/incallui/answer/impl/res/values/strings.xml b/java/com/android/incallui/answer/impl/res/values/strings.xml
new file mode 100644
index 000000000..7fc91fce4
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/res/values/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="call_incoming_swipe_to_decline_with_message">Swipe from icon to decline with message</string>
+ <string name="call_incoming_swipe_to_answer_video_as_audio">Swipe from icon to answer as an audio call</string>
+ <string name="call_incoming_message_custom">Write your own…</string>
+ <string name="call_incoming_audio_handset">Handset</string>
+ <string name="call_incoming_audio_speakerphone">Speakerphone</string>
+ <!-- "Respond via SMS" option that lets you compose a custom response. [CHAR LIMIT=30] -->
+ <string name="call_incoming_respond_via_sms_custom_message">Write your own…</string>
+ <!-- "Custom Message" Cancel alert dialog button -->
+ <string name="call_incoming_custom_message_cancel">Cancel</string>
+ <!-- "Custom Message" Send alert dialog button -->
+ <string name="call_incoming_custom_message_send">Send</string>
+ <string name="a11y_incoming_call_reject_with_sms">Reject this call with a message</string>
+ <string name="a11y_incoming_call_answer_video_as_audio">Answer as audio call</string>
+ <string name="a11y_description_incoming_call_reject_with_sms">Reject with message</string>
+ <string name="a11y_description_incoming_call_answer_video_as_audio">Answer as audio call</string>
+
+ <!-- Text indicates the video local camera is off. [CHAR LIMIT=40] -->
+ <string name="call_incoming_video_is_off">Video is off</string>
+
+ <!-- Voice prompt of swipe gesture when accessibility is turned on. -->
+ <string description="The message announced to accessibility assistance on incoming call."
+ name="a11y_incoming_call_swipe_gesture_prompt">Two finger swipe up to answer. Two finger swipe down to decline.</string>
+ <string name="call_incoming_important">Important call</string>
+</resources>
diff --git a/java/com/android/incallui/answer/impl/utils/FlingAnimationUtils.java b/java/com/android/incallui/answer/impl/utils/FlingAnimationUtils.java
new file mode 100644
index 000000000..3acb2a205
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/utils/FlingAnimationUtils.java
@@ -0,0 +1,293 @@
+/*
+ * 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.incallui.answer.impl.utils;
+
+import android.animation.Animator;
+import android.content.Context;
+import android.view.ViewPropertyAnimator;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+/** Utility class to calculate general fling animation when the finger is released. */
+public class FlingAnimationUtils {
+
+ private static final float LINEAR_OUT_SLOW_IN_X2 = 0.35f;
+ private static final float LINEAR_OUT_FASTER_IN_X2 = 0.5f;
+ private static final float LINEAR_OUT_FASTER_IN_Y2_MIN = 0.4f;
+ private static final float LINEAR_OUT_FASTER_IN_Y2_MAX = 0.5f;
+ private static final float MIN_VELOCITY_DP_PER_SECOND = 250;
+ private static final float HIGH_VELOCITY_DP_PER_SECOND = 3000;
+
+ /** Crazy math. http://en.wikipedia.org/wiki/B%C3%A9zier_curve */
+ private static final float LINEAR_OUT_SLOW_IN_START_GRADIENT = 1.0f / LINEAR_OUT_SLOW_IN_X2;
+
+ private Interpolator linearOutSlowIn;
+
+ private float minVelocityPxPerSecond;
+ private float maxLengthSeconds;
+ private float highVelocityPxPerSecond;
+
+ private AnimatorProperties mAnimatorProperties = new AnimatorProperties();
+
+ public FlingAnimationUtils(Context ctx, float maxLengthSeconds) {
+ this.maxLengthSeconds = maxLengthSeconds;
+ linearOutSlowIn = new PathInterpolator(0, 0, LINEAR_OUT_SLOW_IN_X2, 1);
+ minVelocityPxPerSecond =
+ MIN_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
+ highVelocityPxPerSecond =
+ HIGH_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
+ */
+ public void apply(Animator animator, float currValue, float endValue, float velocity) {
+ apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
+ */
+ public void apply(
+ ViewPropertyAnimator animator, float currValue, float endValue, float velocity) {
+ apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
+ * @param maxDistance the maximum distance for this interaction; the maximum animation length gets
+ * multiplied by the ratio between the actual distance and this value
+ */
+ public void apply(
+ Animator animator, float currValue, float endValue, float velocity, float maxDistance) {
+ AnimatorProperties properties = getProperties(currValue, endValue, velocity, maxDistance);
+ animator.setDuration(properties.duration);
+ animator.setInterpolator(properties.interpolator);
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
+ * @param maxDistance the maximum distance for this interaction; the maximum animation length gets
+ * multiplied by the ratio between the actual distance and this value
+ */
+ public void apply(
+ ViewPropertyAnimator animator,
+ float currValue,
+ float endValue,
+ float velocity,
+ float maxDistance) {
+ AnimatorProperties properties = getProperties(currValue, endValue, velocity, maxDistance);
+ animator.setDuration(properties.duration);
+ animator.setInterpolator(properties.interpolator);
+ }
+
+ private AnimatorProperties getProperties(
+ float currValue, float endValue, float velocity, float maxDistance) {
+ float maxLengthSeconds =
+ (float) (this.maxLengthSeconds * Math.sqrt(Math.abs(endValue - currValue) / maxDistance));
+ float diff = Math.abs(endValue - currValue);
+ float velAbs = Math.abs(velocity);
+ float durationSeconds = LINEAR_OUT_SLOW_IN_START_GRADIENT * diff / velAbs;
+ if (durationSeconds <= maxLengthSeconds) {
+ mAnimatorProperties.interpolator = linearOutSlowIn;
+ } else if (velAbs >= minVelocityPxPerSecond) {
+
+ // Cross fade between fast-out-slow-in and linear interpolator with current velocity.
+ durationSeconds = maxLengthSeconds;
+ VelocityInterpolator velocityInterpolator =
+ new VelocityInterpolator(durationSeconds, velAbs, diff);
+ mAnimatorProperties.interpolator =
+ new InterpolatorInterpolator(velocityInterpolator, linearOutSlowIn, linearOutSlowIn);
+ } else {
+
+ // Just use a normal interpolator which doesn't take the velocity into account.
+ durationSeconds = maxLengthSeconds;
+ mAnimatorProperties.interpolator = Interpolators.FAST_OUT_SLOW_IN;
+ }
+ mAnimatorProperties.duration = (long) (durationSeconds * 1000);
+ return mAnimatorProperties;
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion for the case when the animation is making something
+ * disappear.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
+ * @param maxDistance the maximum distance for this interaction; the maximum animation length gets
+ * multiplied by the ratio between the actual distance and this value
+ */
+ public void applyDismissing(
+ Animator animator, float currValue, float endValue, float velocity, float maxDistance) {
+ AnimatorProperties properties =
+ getDismissingProperties(currValue, endValue, velocity, maxDistance);
+ animator.setDuration(properties.duration);
+ animator.setInterpolator(properties.interpolator);
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion for the case when the animation is making something
+ * disappear.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
+ * @param maxDistance the maximum distance for this interaction; the maximum animation length gets
+ * multiplied by the ratio between the actual distance and this value
+ */
+ public void applyDismissing(
+ ViewPropertyAnimator animator,
+ float currValue,
+ float endValue,
+ float velocity,
+ float maxDistance) {
+ AnimatorProperties properties =
+ getDismissingProperties(currValue, endValue, velocity, maxDistance);
+ animator.setDuration(properties.duration);
+ animator.setInterpolator(properties.interpolator);
+ }
+
+ private AnimatorProperties getDismissingProperties(
+ float currValue, float endValue, float velocity, float maxDistance) {
+ float maxLengthSeconds =
+ (float)
+ (this.maxLengthSeconds * Math.pow(Math.abs(endValue - currValue) / maxDistance, 0.5f));
+ float diff = Math.abs(endValue - currValue);
+ float velAbs = Math.abs(velocity);
+ float y2 = calculateLinearOutFasterInY2(velAbs);
+
+ float startGradient = y2 / LINEAR_OUT_FASTER_IN_X2;
+ Interpolator mLinearOutFasterIn = new PathInterpolator(0, 0, LINEAR_OUT_FASTER_IN_X2, y2);
+ float durationSeconds = startGradient * diff / velAbs;
+ if (durationSeconds <= maxLengthSeconds) {
+ mAnimatorProperties.interpolator = mLinearOutFasterIn;
+ } else if (velAbs >= minVelocityPxPerSecond) {
+
+ // Cross fade between linear-out-faster-in and linear interpolator with current
+ // velocity.
+ durationSeconds = maxLengthSeconds;
+ VelocityInterpolator velocityInterpolator =
+ new VelocityInterpolator(durationSeconds, velAbs, diff);
+ InterpolatorInterpolator superInterpolator =
+ new InterpolatorInterpolator(velocityInterpolator, mLinearOutFasterIn, linearOutSlowIn);
+ mAnimatorProperties.interpolator = superInterpolator;
+ } else {
+
+ // Just use a normal interpolator which doesn't take the velocity into account.
+ durationSeconds = maxLengthSeconds;
+ mAnimatorProperties.interpolator = Interpolators.FAST_OUT_LINEAR_IN;
+ }
+ mAnimatorProperties.duration = (long) (durationSeconds * 1000);
+ return mAnimatorProperties;
+ }
+
+ /**
+ * Calculates the y2 control point for a linear-out-faster-in path interpolator depending on the
+ * velocity. The faster the velocity, the more "linear" the interpolator gets.
+ *
+ * @param velocity the velocity of the gesture.
+ * @return the y2 control point for a cubic bezier path interpolator
+ */
+ private float calculateLinearOutFasterInY2(float velocity) {
+ float t =
+ (velocity - minVelocityPxPerSecond) / (highVelocityPxPerSecond - minVelocityPxPerSecond);
+ t = Math.max(0, Math.min(1, t));
+ return (1 - t) * LINEAR_OUT_FASTER_IN_Y2_MIN + t * LINEAR_OUT_FASTER_IN_Y2_MAX;
+ }
+
+ /** @return the minimum velocity a gesture needs to have to be considered a fling */
+ public float getMinVelocityPxPerSecond() {
+ return minVelocityPxPerSecond;
+ }
+
+ /** An interpolator which interpolates two interpolators with an interpolator. */
+ private static final class InterpolatorInterpolator implements Interpolator {
+
+ private Interpolator mInterpolator1;
+ private Interpolator mInterpolator2;
+ private Interpolator mCrossfader;
+
+ InterpolatorInterpolator(
+ Interpolator interpolator1, Interpolator interpolator2, Interpolator crossfader) {
+ mInterpolator1 = interpolator1;
+ mInterpolator2 = interpolator2;
+ mCrossfader = crossfader;
+ }
+
+ @Override
+ public float getInterpolation(float input) {
+ float t = mCrossfader.getInterpolation(input);
+ return (1 - t) * mInterpolator1.getInterpolation(input)
+ + t * mInterpolator2.getInterpolation(input);
+ }
+ }
+
+ /** An interpolator which interpolates with a fixed velocity. */
+ private static final class VelocityInterpolator implements Interpolator {
+
+ private float mDurationSeconds;
+ private float mVelocity;
+ private float mDiff;
+
+ private VelocityInterpolator(float durationSeconds, float velocity, float diff) {
+ mDurationSeconds = durationSeconds;
+ mVelocity = velocity;
+ mDiff = diff;
+ }
+
+ @Override
+ public float getInterpolation(float input) {
+ float time = input * mDurationSeconds;
+ return time * mVelocity / mDiff;
+ }
+ }
+
+ private static class AnimatorProperties {
+
+ Interpolator interpolator;
+ long duration;
+ }
+}
diff --git a/java/com/android/incallui/answer/impl/utils/Interpolators.java b/java/com/android/incallui/answer/impl/utils/Interpolators.java
new file mode 100644
index 000000000..efc68f78a
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/utils/Interpolators.java
@@ -0,0 +1,30 @@
+/*
+ * 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.incallui.answer.impl.utils;
+
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+/**
+ * Common interpolators used in answer methods.
+ */
+public class Interpolators {
+
+ public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+ public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
+ public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+}
diff --git a/java/com/android/incallui/answer/protocol/AnswerScreen.java b/java/com/android/incallui/answer/protocol/AnswerScreen.java
new file mode 100644
index 000000000..0c374eb7f
--- /dev/null
+++ b/java/com/android/incallui/answer/protocol/AnswerScreen.java
@@ -0,0 +1,38 @@
+/*
+ * 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.incallui.answer.protocol;
+
+import android.support.v4.app.Fragment;
+import java.util.List;
+
+/** Interface for the answer module. */
+public interface AnswerScreen {
+
+ String getCallId();
+
+ int getVideoState();
+
+ boolean isVideoUpgradeRequest();
+
+ void setTextResponses(List<String> textResponses);
+
+ boolean hasPendingDialogs();
+
+ void dismissPendingDialogs();
+
+ Fragment getAnswerScreenFragment();
+}
diff --git a/java/com/android/incallui/answer/protocol/AnswerScreenDelegate.java b/java/com/android/incallui/answer/protocol/AnswerScreenDelegate.java
new file mode 100644
index 000000000..9934497cf
--- /dev/null
+++ b/java/com/android/incallui/answer/protocol/AnswerScreenDelegate.java
@@ -0,0 +1,44 @@
+/*
+ * 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.incallui.answer.protocol;
+
+import android.support.annotation.FloatRange;
+
+/** Callbacks implemented by the container app for this module. */
+public interface AnswerScreenDelegate {
+
+ void onAnswerScreenUnready();
+
+ void onDismissDialog();
+
+ void onRejectCallWithMessage(String message);
+
+ void onAnswer(int videoState);
+
+ void onReject();
+
+ /**
+ * Sets the window background color based on foreground call's theme and the given progress. This
+ * is called from the answer UI to animate the accept and reject action.
+ *
+ * <p>When the user is rejecting we animate the background color to a mostly transparent gray. The
+ * end effect is that the home screen shows through.
+ *
+ * @param progress float from -1 to 1. -1 is fully rejected, 1 is fully accepted, and 0 is neutral
+ */
+ void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress);
+}
diff --git a/java/com/android/incallui/answer/protocol/AnswerScreenDelegateFactory.java b/java/com/android/incallui/answer/protocol/AnswerScreenDelegateFactory.java
new file mode 100644
index 000000000..a09cb1a40
--- /dev/null
+++ b/java/com/android/incallui/answer/protocol/AnswerScreenDelegateFactory.java
@@ -0,0 +1,23 @@
+/*
+ * 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.incallui.answer.protocol;
+
+/** Used to create an instance of the delegate, should be implemented by the container activity. */
+public interface AnswerScreenDelegateFactory {
+
+ AnswerScreenDelegate newAnswerScreenDelegate(AnswerScreen answerScreen);
+}
diff --git a/java/com/android/incallui/answerproximitysensor/AnswerProximitySensor.java b/java/com/android/incallui/answerproximitysensor/AnswerProximitySensor.java
new file mode 100644
index 000000000..edc3db34b
--- /dev/null
+++ b/java/com/android/incallui/answerproximitysensor/AnswerProximitySensor.java
@@ -0,0 +1,150 @@
+/*
+ * 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.incallui.answerproximitysensor;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.PowerManager;
+import android.view.Display;
+import com.android.dialer.common.ConfigProviderBindings;
+import com.android.dialer.common.LogUtil;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.DialerCall.SessionModificationState;
+import com.android.incallui.call.DialerCall.State;
+import com.android.incallui.call.DialerCallListener;
+
+/**
+ * This class prevents users from accidentally answering calls by keeping the screen off until the
+ * proximity sensor is unblocked. If the screen is already on or if this is a call waiting call then
+ * nothing is done.
+ */
+public class AnswerProximitySensor
+ implements DialerCallListener, AnswerProximityWakeLock.ScreenOnListener {
+
+ private static final String CONFIG_ANSWER_PROXIMITY_SENSOR_ENABLED =
+ "answer_proximity_sensor_enabled";
+ private static final String CONFIG_ANSWER_PSEUDO_PROXIMITY_WAKE_LOCK_ENABLED =
+ "answer_pseudo_proximity_wake_lock_enabled";
+
+ private final DialerCall call;
+ private final AnswerProximityWakeLock answerProximityWakeLock;
+
+ public static boolean shouldUse(Context context, DialerCall call) {
+ // Don't use the AnswerProximitySensor for call waiting and other states. Those states are
+ // handled by the general ProximitySensor code.
+ if (call.getState() != State.INCOMING) {
+ LogUtil.i("AnswerProximitySensor.shouldUse", "call state is not incoming");
+ return false;
+ }
+
+ if (!ConfigProviderBindings.get(context)
+ .getBoolean(CONFIG_ANSWER_PROXIMITY_SENSOR_ENABLED, true)) {
+ LogUtil.i("AnswerProximitySensor.shouldUse", "disabled by config");
+ return false;
+ }
+
+ if (!context
+ .getSystemService(PowerManager.class)
+ .isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
+ LogUtil.i("AnswerProximitySensor.shouldUse", "wake lock level not supported");
+ return false;
+ }
+
+ if (isDefaultDisplayOn(context)) {
+ LogUtil.i("AnswerProximitySensor.shouldUse", "display is already on");
+ return false;
+ }
+
+ return true;
+ }
+
+ public AnswerProximitySensor(
+ Context context, DialerCall call, PseudoScreenState pseudoScreenState) {
+ this.call = call;
+
+ LogUtil.i("AnswerProximitySensor.constructor", "acquiring lock");
+ if (ConfigProviderBindings.get(context)
+ .getBoolean(CONFIG_ANSWER_PSEUDO_PROXIMITY_WAKE_LOCK_ENABLED, true)) {
+ answerProximityWakeLock = new PseudoProximityWakeLock(context, pseudoScreenState);
+ } else {
+ // TODO: choose a wake lock implementation base on framework/device.
+ // These bugs requires the PseudoProximityWakeLock workaround:
+ // b/30439151 Proximity sensor not working on M
+ // b/31499931 fautly touch input when screen is off on marlin/sailfish
+ answerProximityWakeLock = new SystemProximityWakeLock(context);
+ }
+ answerProximityWakeLock.setScreenOnListener(this);
+ answerProximityWakeLock.acquire();
+
+ call.addListener(this);
+ }
+
+ private void cleanup() {
+ call.removeListener(this);
+ releaseProximityWakeLock();
+ }
+
+ private void releaseProximityWakeLock() {
+ if (answerProximityWakeLock.isHeld()) {
+ LogUtil.i("AnswerProximitySensor.releaseProximityWakeLock", "releasing lock");
+ answerProximityWakeLock.release();
+ }
+ }
+
+ private static boolean isDefaultDisplayOn(Context context) {
+ Display display =
+ context.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY);
+ return display.getState() == Display.STATE_ON;
+ }
+
+ @Override
+ public void onDialerCallDisconnect() {
+ LogUtil.i("AnswerProximitySensor.onDialerCallDisconnect", null);
+ cleanup();
+ }
+
+ @Override
+ public void onDialerCallUpdate() {
+ if (call.getState() != State.INCOMING) {
+ LogUtil.i("AnswerProximitySensor.onDialerCallUpdate", "no longer incoming, cleaning up");
+ cleanup();
+ }
+ }
+
+ @Override
+ public void onDialerCallChildNumberChange() {}
+
+ @Override
+ public void onDialerCallLastForwardedNumberChange() {}
+
+ @Override
+ public void onDialerCallUpgradeToVideo() {}
+
+ @Override
+ public void onWiFiToLteHandover() {}
+
+ @Override
+ public void onHandoverToWifiFailure() {}
+
+ @Override
+ public void onDialerCallSessionModificationStateChange(@SessionModificationState int state) {}
+
+ @Override
+ public void onScreenOn() {
+ cleanup();
+ }
+}
diff --git a/java/com/android/incallui/answerproximitysensor/AnswerProximityWakeLock.java b/java/com/android/incallui/answerproximitysensor/AnswerProximityWakeLock.java
new file mode 100644
index 000000000..94abe9c85
--- /dev/null
+++ b/java/com/android/incallui/answerproximitysensor/AnswerProximityWakeLock.java
@@ -0,0 +1,37 @@
+/*
+ * 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.incallui.answerproximitysensor;
+
+/**
+ * Interface to wrap around the {@link android.os.PowerManager.WakeLock} for custom implementations.
+ */
+public interface AnswerProximityWakeLock {
+
+ /** Called when the wake lock turned the screen back on. */
+ interface ScreenOnListener {
+
+ void onScreenOn();
+ }
+
+ void acquire();
+
+ void release();
+
+ boolean isHeld();
+
+ void setScreenOnListener(ScreenOnListener listener);
+}
diff --git a/java/com/android/incallui/answerproximitysensor/PseudoProximityWakeLock.java b/java/com/android/incallui/answerproximitysensor/PseudoProximityWakeLock.java
new file mode 100644
index 000000000..c7844d47d
--- /dev/null
+++ b/java/com/android/incallui/answerproximitysensor/PseudoProximityWakeLock.java
@@ -0,0 +1,85 @@
+/*
+ * 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.incallui.answerproximitysensor;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.support.annotation.Nullable;
+import com.android.dialer.common.LogUtil;
+
+/**
+ * A fake PROXIMITY_SCREEN_OFF_WAKE_LOCK implemented by the app. It will use {@link
+ * PseudoScreenState} to fake a black screen when the proximity sensor is near.
+ */
+public class PseudoProximityWakeLock implements AnswerProximityWakeLock, SensorEventListener {
+
+ private final Context context;
+ private final PseudoScreenState pseudoScreenState;
+ private final Sensor proximitySensor;
+
+ @Nullable private ScreenOnListener listener;
+ private boolean isHeld;
+
+ public PseudoProximityWakeLock(Context context, PseudoScreenState pseudoScreenState) {
+ this.context = context;
+ this.pseudoScreenState = pseudoScreenState;
+ pseudoScreenState.setOn(true);
+ proximitySensor =
+ context.getSystemService(SensorManager.class).getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ }
+
+ @Override
+ public void acquire() {
+ isHeld = true;
+ context
+ .getSystemService(SensorManager.class)
+ .registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+
+ @Override
+ public void release() {
+ isHeld = false;
+ context.getSystemService(SensorManager.class).unregisterListener(this);
+ pseudoScreenState.setOn(true);
+ }
+
+ @Override
+ public boolean isHeld() {
+ return isHeld;
+ }
+
+ @Override
+ public void setScreenOnListener(ScreenOnListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent sensorEvent) {
+ boolean near = sensorEvent.values[0] < sensorEvent.sensor.getMaximumRange();
+ LogUtil.i("AnswerProximitySensor.PseudoProximityWakeLock.onSensorChanged", "near: " + near);
+ pseudoScreenState.setOn(!near);
+ if (!near && listener != null) {
+ listener.onScreenOn();
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int i) {}
+}
diff --git a/java/com/android/incallui/answerproximitysensor/PseudoScreenState.java b/java/com/android/incallui/answerproximitysensor/PseudoScreenState.java
new file mode 100644
index 000000000..eda0ee720
--- /dev/null
+++ b/java/com/android/incallui/answerproximitysensor/PseudoScreenState.java
@@ -0,0 +1,66 @@
+/*
+ * 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.incallui.answerproximitysensor;
+
+import android.util.ArraySet;
+import java.util.Set;
+
+/**
+ * Stores a fake screen on/off state for the {@link InCallActivity}. If InCallActivity see the state
+ * is off, it will draw a black view over the activity pretending the screen is off.
+ *
+ * <p>If the screen is already touched when the screen is turned on, the OS behavior is sending a
+ * new DOWN event once the point started moving and then behave as a normal gesture. To prevent
+ * accidental answer/rejects, touches that started when the screen is off should be ignored.
+ *
+ * <p>b/31499931 on certain devices with N-DR1, if the screen is already touched when the screen is
+ * turned on, a "DOWN MOVE UP" will be sent for each movement before the touch is actually released.
+ * These events is hard to discern from other normal events, and keeping the screen on reduces its'
+ * probability.
+ */
+public class PseudoScreenState {
+
+ /** Notifies when the on state has changed. */
+ public interface StateChangedListener {
+ void onPseudoScreenStateChanged(boolean isOn);
+ }
+
+ private final Set<StateChangedListener> listeners = new ArraySet<>();
+
+ private boolean on = true;
+
+ public boolean isOn() {
+ return on;
+ }
+
+ public void setOn(boolean value) {
+ if (on != value) {
+ on = value;
+ for (StateChangedListener listener : listeners) {
+ listener.onPseudoScreenStateChanged(on);
+ }
+ }
+ }
+
+ public void addListener(StateChangedListener listener) {
+ listeners.add(listener);
+ }
+
+ public void removeListener(StateChangedListener listener) {
+ listeners.remove(listener);
+ }
+}
diff --git a/java/com/android/incallui/answerproximitysensor/SystemProximityWakeLock.java b/java/com/android/incallui/answerproximitysensor/SystemProximityWakeLock.java
new file mode 100644
index 000000000..776e9a42d
--- /dev/null
+++ b/java/com/android/incallui/answerproximitysensor/SystemProximityWakeLock.java
@@ -0,0 +1,90 @@
+/*
+ * 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.incallui.answerproximitysensor;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.PowerManager;
+import android.support.annotation.Nullable;
+import android.view.Display;
+import com.android.dialer.common.LogUtil;
+
+/** The normal PROXIMITY_SCREEN_OFF_WAKE_LOCK provided by the OS. */
+public class SystemProximityWakeLock implements AnswerProximityWakeLock, DisplayListener {
+
+ private static final String TAG = "SystemProximityWakeLock";
+
+ private final Context context;
+ private final PowerManager.WakeLock wakeLock;
+
+ @Nullable private ScreenOnListener listener;
+
+ public SystemProximityWakeLock(Context context) {
+ this.context = context;
+ wakeLock =
+ context
+ .getSystemService(PowerManager.class)
+ .newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
+ }
+
+ @Override
+ public void acquire() {
+ wakeLock.acquire();
+ context.getSystemService(DisplayManager.class).registerDisplayListener(this, null);
+ }
+
+ @Override
+ public void release() {
+ wakeLock.release();
+ context.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
+ }
+
+ @Override
+ public boolean isHeld() {
+ return wakeLock.isHeld();
+ }
+
+ @Override
+ public void setScreenOnListener(ScreenOnListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {}
+
+ @Override
+ public void onDisplayRemoved(int displayId) {}
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ if (isDefaultDisplayOn(context)) {
+ LogUtil.i("SystemProximityWakeLock.onDisplayChanged", "display turned on");
+ if (listener != null) {
+ listener.onScreenOn();
+ }
+ }
+ }
+ }
+
+ private static boolean isDefaultDisplayOn(Context context) {
+ Display display =
+ context.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY);
+ return display.getState() != Display.STATE_OFF;
+ }
+}
diff --git a/java/com/android/incallui/async/PausableExecutor.java b/java/com/android/incallui/async/PausableExecutor.java
new file mode 100644
index 000000000..e10757e67
--- /dev/null
+++ b/java/com/android/incallui/async/PausableExecutor.java
@@ -0,0 +1,56 @@
+/*
+ * 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.incallui.async;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Executor that can be used to easily synchronize testing and production code. Production code
+ * should call {@link #milestone()} at points in the code where the state of the system is worthy of
+ * testing. In a test scenario, this method will pause execution until the test acknowledges the
+ * milestone through the use of {@link #ackMilestoneForTesting()}.
+ */
+public interface PausableExecutor extends Executor {
+
+ /**
+ * Method called from asynchronous production code to inform this executor that it has reached a
+ * point that puts the system into a state worth testing. TestableExecutors intended for use in a
+ * testing environment should cause the calling thread to block. In the production environment
+ * this should be a no-op.
+ */
+ void milestone();
+
+ /**
+ * Method called from the test code to inform this executor that the state of the production
+ * system at the current milestone has been sufficiently tested. Every milestone must be
+ * acknowledged.
+ */
+ void ackMilestoneForTesting();
+
+ /**
+ * Method called from the test code to inform this executor that the tests are finished with all
+ * milestones. Future calls to {@link #milestone()} or {@link #awaitMilestoneForTesting()} should
+ * return immediately.
+ */
+ void ackAllMilestonesForTesting();
+
+ /**
+ * Method called from the test code to block until a milestone has been reached in the production
+ * code.
+ */
+ void awaitMilestoneForTesting() throws InterruptedException;
+}
diff --git a/java/com/android/incallui/async/PausableExecutorImpl.java b/java/com/android/incallui/async/PausableExecutorImpl.java
new file mode 100644
index 000000000..687606129
--- /dev/null
+++ b/java/com/android/incallui/async/PausableExecutorImpl.java
@@ -0,0 +1,40 @@
+/*
+ * 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.incallui.async;
+
+import java.util.concurrent.Executors;
+
+/** {@link PausableExecutor} intended for use in production environments. */
+public class PausableExecutorImpl implements PausableExecutor {
+
+ @Override
+ public void milestone() {}
+
+ @Override
+ public void ackMilestoneForTesting() {}
+
+ @Override
+ public void ackAllMilestonesForTesting() {}
+
+ @Override
+ public void awaitMilestoneForTesting() {}
+
+ @Override
+ public void execute(Runnable command) {
+ Executors.newSingleThreadExecutor().execute(command);
+ }
+}
diff --git a/java/com/android/incallui/audioroute/AndroidManifest.xml b/java/com/android/incallui/audioroute/AndroidManifest.xml
new file mode 100644
index 000000000..36431f1ee
--- /dev/null
+++ b/java/com/android/incallui/audioroute/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.incallui.audioroute">
+</manifest>
diff --git a/java/com/android/incallui/audioroute/AudioRouteSelectorDialogFragment.java b/java/com/android/incallui/audioroute/AudioRouteSelectorDialogFragment.java
new file mode 100644
index 000000000..c757477f1
--- /dev/null
+++ b/java/com/android/incallui/audioroute/AudioRouteSelectorDialogFragment.java
@@ -0,0 +1,114 @@
+/*
+ * 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.incallui.audioroute;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff.Mode;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.design.widget.BottomSheetDialogFragment;
+import android.telecom.CallAudioState;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.TextView;
+import com.android.dialer.common.FragmentUtils;
+import com.android.dialer.common.LogUtil;
+
+/** Shows picker for audio routes */
+public class AudioRouteSelectorDialogFragment extends BottomSheetDialogFragment {
+
+ private static final String ARG_AUDIO_STATE = "audio_state";
+
+ /** Called when an audio route is picked */
+ public interface AudioRouteSelectorPresenter {
+ void onAudioRouteSelected(int audioRoute);
+ }
+
+ public static AudioRouteSelectorDialogFragment newInstance(CallAudioState audioState) {
+ AudioRouteSelectorDialogFragment fragment = new AudioRouteSelectorDialogFragment();
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_AUDIO_STATE, audioState);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ FragmentUtils.checkParent(this, AudioRouteSelectorPresenter.class);
+ }
+
+ @Override
+ public Dialog onCreateDialog(final Bundle savedInstanceState) {
+ LogUtil.i("AudioRouteSelectorDialogFragment.onCreateDialog", null);
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ return dialog;
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view = layoutInflater.inflate(R.layout.audioroute_selector, viewGroup, false);
+ CallAudioState audioState = getArguments().getParcelable(ARG_AUDIO_STATE);
+
+ initItem(
+ (TextView) view.findViewById(R.id.audioroute_bluetooth),
+ CallAudioState.ROUTE_BLUETOOTH,
+ audioState);
+ initItem(
+ (TextView) view.findViewById(R.id.audioroute_speaker),
+ CallAudioState.ROUTE_SPEAKER,
+ audioState);
+ initItem(
+ (TextView) view.findViewById(R.id.audioroute_headset),
+ CallAudioState.ROUTE_WIRED_HEADSET,
+ audioState);
+ initItem(
+ (TextView) view.findViewById(R.id.audioroute_earpiece),
+ CallAudioState.ROUTE_EARPIECE,
+ audioState);
+ return view;
+ }
+
+ private void initItem(TextView item, final int itemRoute, CallAudioState audioState) {
+ int selectedColor = getResources().getColor(R.color.dialer_theme_color);
+ if ((audioState.getSupportedRouteMask() & itemRoute) == 0) {
+ item.setVisibility(View.GONE);
+ } else if (audioState.getRoute() == itemRoute) {
+ item.setTextColor(selectedColor);
+ item.setCompoundDrawableTintList(ColorStateList.valueOf(selectedColor));
+ item.setCompoundDrawableTintMode(Mode.SRC_ATOP);
+ }
+ item.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dismiss();
+ FragmentUtils.getParentUnsafe(
+ AudioRouteSelectorDialogFragment.this, AudioRouteSelectorPresenter.class)
+ .onAudioRouteSelected(itemRoute);
+ }
+ });
+ }
+}
diff --git a/java/com/android/incallui/audioroute/res/drawable-hdpi/ic_phone_audio_grey600_24dp.png b/java/com/android/incallui/audioroute/res/drawable-hdpi/ic_phone_audio_grey600_24dp.png
new file mode 100644
index 000000000..4ea921a3e
--- /dev/null
+++ b/java/com/android/incallui/audioroute/res/drawable-hdpi/ic_phone_audio_grey600_24dp.png
Binary files differ
diff --git a/java/com/android/incallui/audioroute/res/drawable-mdpi/ic_phone_audio_grey600_24dp.png b/java/com/android/incallui/audioroute/res/drawable-mdpi/ic_phone_audio_grey600_24dp.png
new file mode 100644
index 000000000..acef550ac
--- /dev/null
+++ b/java/com/android/incallui/audioroute/res/drawable-mdpi/ic_phone_audio_grey600_24dp.png
Binary files differ
diff --git a/java/com/android/incallui/audioroute/res/drawable-xhdpi/ic_phone_audio_grey600_24dp.png b/java/com/android/incallui/audioroute/res/drawable-xhdpi/ic_phone_audio_grey600_24dp.png
new file mode 100644
index 000000000..a30aa5c0c
--- /dev/null
+++ b/java/com/android/incallui/audioroute/res/drawable-xhdpi/ic_phone_audio_grey600_24dp.png
Binary files differ
diff --git a/java/com/android/incallui/audioroute/res/drawable-xxhdpi/ic_phone_audio_grey600_24dp.png b/java/com/android/incallui/audioroute/res/drawable-xxhdpi/ic_phone_audio_grey600_24dp.png
new file mode 100644
index 000000000..beb85a80a
--- /dev/null
+++ b/java/com/android/incallui/audioroute/res/drawable-xxhdpi/ic_phone_audio_grey600_24dp.png
Binary files differ
diff --git a/java/com/android/incallui/audioroute/res/layout/audioroute_selector.xml b/java/com/android/incallui/audioroute/res/layout/audioroute_selector.xml
new file mode 100644
index 000000000..ef2220e8f
--- /dev/null
+++ b/java/com/android/incallui/audioroute/res/layout/audioroute_selector.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ tools:layout_gravity="bottom">
+ <TextView
+ android:id="@+id/audioroute_bluetooth"
+ style="@style/AudioRouteItem"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:drawableStart="@drawable/quantum_ic_bluetooth_audio_grey600_24"
+ android:text="@string/audioroute_bluetooth"/>
+ <TextView
+ android:id="@+id/audioroute_speaker"
+ style="@style/AudioRouteItem"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:drawableStart="@drawable/quantum_ic_volume_up_grey600_24"
+ android:text="@string/audioroute_speaker"/>
+ <TextView
+ android:id="@+id/audioroute_earpiece"
+ style="@style/AudioRouteItem"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:drawableStart="@drawable/ic_phone_audio_grey600_24dp"
+ android:text="@string/audioroute_phone"/>
+ <TextView
+ android:id="@+id/audioroute_headset"
+ style="@style/AudioRouteItem"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:drawableStart="@drawable/quantum_ic_headset_grey600_24"
+ android:text="@string/audioroute_headset"/>
+
+</LinearLayout>
diff --git a/java/com/android/incallui/audioroute/res/values/strings.xml b/java/com/android/incallui/audioroute/res/values/strings.xml
new file mode 100644
index 000000000..b16639354
--- /dev/null
+++ b/java/com/android/incallui/audioroute/res/values/strings.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="audioroute_bluetooth">Bluetooth</string>
+ <string name="audioroute_speaker">Speaker</string>
+ <string name="audioroute_phone">Phone</string>
+ <string name="audioroute_headset">Wired headset</string>
+</resources>
diff --git a/java/com/android/incallui/audioroute/res/values/styles.xml b/java/com/android/incallui/audioroute/res/values/styles.xml
new file mode 100644
index 000000000..4484b7092
--- /dev/null
+++ b/java/com/android/incallui/audioroute/res/values/styles.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <style name="AudioRouteItem">
+ <item name="android:padding">16dp</item>
+ <item name="android:background">?android:selectableItemBackground</item>
+ <item name="android:drawablePadding">24dp</item>
+ <item name="android:gravity">center_vertical</item>
+ <item name="android:textAppearance">
+ @style/TextAppearance.AppCompat.Light.Widget.PopupMenu.Large
+ </item>
+ <item name="android:textColor">?android:textColorSecondary</item>
+ </style>
+</resources>
diff --git a/java/com/android/incallui/autoresizetext/AndroidManifest.xml b/java/com/android/incallui/autoresizetext/AndroidManifest.xml
new file mode 100644
index 000000000..53a8961e4
--- /dev/null
+++ b/java/com/android/incallui/autoresizetext/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.incallui.autoresizetext">
+
+ <uses-sdk
+ android:minSdkVersion="23"
+ android:targetSdkVersion="25"/>
+
+ <application />
+</manifest>
diff --git a/java/com/android/incallui/autoresizetext/AutoResizeTextView.java b/java/com/android/incallui/autoresizetext/AutoResizeTextView.java
new file mode 100644
index 000000000..eedcbe5bb
--- /dev/null
+++ b/java/com/android/incallui/autoresizetext/AutoResizeTextView.java
@@ -0,0 +1,316 @@
+/*
+ * 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.incallui.autoresizetext;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.RectF;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.text.Layout.Alignment;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.SparseIntArray;
+import android.util.TypedValue;
+import android.widget.TextView;
+import javax.annotation.Nullable;
+
+/**
+ * A TextView that automatically scales its text to completely fill its allotted width.
+ *
+ * <p>Note: In some edge cases, the binary search algorithm to find the best fit may slightly
+ * overshoot / undershoot its constraints. See b/26704434. No minimal repro case has been
+ * found yet. A known workaround is the solution provided on StackOverflow:
+ * http://stackoverflow.com/a/5535672
+ */
+public class AutoResizeTextView extends TextView {
+ private static final int NO_LINE_LIMIT = -1;
+ private static final float DEFAULT_MIN_TEXT_SIZE = 16.0f;
+ private static final int DEFAULT_RESIZE_STEP_UNIT = TypedValue.COMPLEX_UNIT_PX;
+
+ private final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+ private final RectF availableSpaceRect = new RectF();
+ private final SparseIntArray textSizesCache = new SparseIntArray();
+ private final TextPaint textPaint = new TextPaint();
+ private int resizeStepUnit = DEFAULT_RESIZE_STEP_UNIT;
+ private float minTextSize = DEFAULT_MIN_TEXT_SIZE;
+ private float maxTextSize;
+ private int maxWidth;
+ private int maxLines;
+ private float lineSpacingMultiplier = 1.0f;
+ private float lineSpacingExtra = 0.0f;
+
+ public AutoResizeTextView(Context context) {
+ super(context, null, 0);
+ initialize(context, null, 0, 0);
+ }
+
+ public AutoResizeTextView(Context context, AttributeSet attrs) {
+ super(context, attrs, 0);
+ initialize(context, attrs, 0, 0);
+ }
+
+ public AutoResizeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initialize(context, attrs, defStyleAttr, 0);
+ }
+
+ public AutoResizeTextView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ initialize(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ private void initialize(
+ Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ TypedArray typedArray = context.getTheme().obtainStyledAttributes(
+ attrs, R.styleable.AutoResizeTextView, defStyleAttr, defStyleRes);
+ readAttrs(typedArray);
+ textPaint.set(getPaint());
+ }
+
+ /** Overridden because getMaxLines is only defined in JB+. */
+ @Override
+ public final int getMaxLines() {
+ if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
+ return super.getMaxLines();
+ } else {
+ return maxLines;
+ }
+ }
+
+ /** Overridden because getMaxLines is only defined in JB+. */
+ @Override
+ public final void setMaxLines(int maxLines) {
+ super.setMaxLines(maxLines);
+ this.maxLines = maxLines;
+ }
+
+ /** Overridden because getLineSpacingMultiplier is only defined in JB+. */
+ @Override
+ public final float getLineSpacingMultiplier() {
+ if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
+ return super.getLineSpacingMultiplier();
+ } else {
+ return lineSpacingMultiplier;
+ }
+ }
+
+ /** Overridden because getLineSpacingExtra is only defined in JB+. */
+ @Override
+ public final float getLineSpacingExtra() {
+ if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
+ return super.getLineSpacingExtra();
+ } else {
+ return lineSpacingExtra;
+ }
+ }
+
+ /**
+ * Overridden because getLineSpacingMultiplier and getLineSpacingExtra are only defined in JB+.
+ */
+ @Override
+ public final void setLineSpacing(float add, float mult) {
+ super.setLineSpacing(add, mult);
+ lineSpacingMultiplier = mult;
+ lineSpacingExtra = add;
+ }
+
+ /**
+ * Although this overrides the setTextSize method from the TextView base class, it changes the
+ * semantics a bit: Calling setTextSize now specifies the maximum text size to be used by this
+ * view. If the text can't fit with that text size, the text size will be scaled down, up to the
+ * minimum text size specified in {@link #setMinTextSize}.
+ *
+ * <p>Note that the final size unit will be truncated to the nearest integer value of the
+ * specified unit.
+ */
+ @Override
+ public final void setTextSize(int unit, float size) {
+ float maxTextSize = TypedValue.applyDimension(unit, size, displayMetrics);
+ if (this.maxTextSize != maxTextSize) {
+ this.maxTextSize = maxTextSize;
+ // TODO: It's not actually necessary to clear the whole cache here. To optimize cache
+ // deletion we'd have to delete all entries in the cache with a value equal or larger than
+ // MIN(old_max_size, new_max_size) when changing maxTextSize; and all entries with a value
+ // equal or smaller than MAX(old_min_size, new_min_size) when changing minTextSize.
+ textSizesCache.clear();
+ requestLayout();
+ }
+ }
+
+ /**
+ * Sets the lower text size limit and invalidate the view.
+ *
+ * <p>The parameters follow the same behavior as they do in {@link #setTextSize}.
+ *
+ * <p>Note that the final size unit will be truncated to the nearest integer value of the
+ * specified unit.
+ */
+ public final void setMinTextSize(int unit, float size) {
+ float minTextSize = TypedValue.applyDimension(unit, size, displayMetrics);
+ if (this.minTextSize != minTextSize) {
+ this.minTextSize = minTextSize;
+ textSizesCache.clear();
+ requestLayout();
+ }
+ }
+
+ /**
+ * Sets the unit to use as step units when computing the resized font size. This view's text
+ * contents will always be rendered as a whole integer value in the unit specified here. For
+ * example, if the unit is {@link TypedValue#COMPLEX_UNIT_SP}, then the text size may end up
+ * being 13sp or 14sp, but never 13.5sp.
+ *
+ * <p>By default, the AutoResizeTextView uses the unit {@link TypedValue#COMPLEX_UNIT_PX}.
+ *
+ * @param unit the unit type to use; must be a known unit type from {@link TypedValue}.
+ */
+ public final void setResizeStepUnit(int unit) {
+ if (resizeStepUnit != unit) {
+ resizeStepUnit = unit;
+ requestLayout();
+ }
+ }
+
+ private void readAttrs(TypedArray typedArray) {
+ resizeStepUnit = typedArray.getInt(
+ R.styleable.AutoResizeTextView_autoResizeText_resizeStepUnit, DEFAULT_RESIZE_STEP_UNIT);
+ minTextSize = (int) typedArray.getDimension(
+ R.styleable.AutoResizeTextView_autoResizeText_minTextSize, DEFAULT_MIN_TEXT_SIZE);
+ maxTextSize = (int) getTextSize();
+ }
+
+ private void adjustTextSize() {
+ int maxWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
+ int maxHeight = getMeasuredHeight() - getPaddingBottom() - getPaddingTop();
+
+ if (maxWidth <= 0 || maxHeight <= 0) {
+ return;
+ }
+
+ this.maxWidth = maxWidth;
+ availableSpaceRect.right = maxWidth;
+ availableSpaceRect.bottom = maxHeight;
+ int minSizeInStepSizeUnits = (int) Math.ceil(convertToResizeStepUnits(minTextSize));
+ int maxSizeInStepSizeUnits = (int) Math.floor(convertToResizeStepUnits(maxTextSize));
+ float textSize = computeTextSize(
+ minSizeInStepSizeUnits, maxSizeInStepSizeUnits, availableSpaceRect);
+ super.setTextSize(resizeStepUnit, textSize);
+ }
+
+ private boolean suggestedSizeFitsInSpace(float suggestedSizeInPx, RectF availableSpace) {
+ textPaint.setTextSize(suggestedSizeInPx);
+ String text = getText().toString();
+ int maxLines = getMaxLines();
+ if (maxLines == 1) {
+ // If single line, check the line's height and width.
+ return textPaint.getFontSpacing() <= availableSpace.bottom
+ && textPaint.measureText(text) <= availableSpace.right;
+ } else {
+ // If multiline, lay the text out, then check the number of lines, the layout's height,
+ // and each line's width.
+ StaticLayout layout = new StaticLayout(text,
+ textPaint,
+ maxWidth,
+ Alignment.ALIGN_NORMAL,
+ getLineSpacingMultiplier(),
+ getLineSpacingExtra(),
+ true);
+
+ // Return false if we need more than maxLines. The text is obviously too big in this case.
+ if (maxLines != NO_LINE_LIMIT && layout.getLineCount() > maxLines) {
+ return false;
+ }
+ // Return false if the height of the layout is too big.
+ return layout.getHeight() <= availableSpace.bottom;
+ }
+ }
+
+ /**
+ * Computes the final text size to use for this text view, factoring in any previously
+ * cached computations.
+ *
+ * @param minSize the minimum text size to allow, in units of {@link #resizeStepUnit}
+ * @param maxSize the maximum text size to allow, in units of {@link #resizeStepUnit}
+ */
+ private float computeTextSize(int minSize, int maxSize, RectF availableSpace) {
+ CharSequence text = getText();
+ if (text != null && textSizesCache.get(text.hashCode()) != 0) {
+ return textSizesCache.get(text.hashCode());
+ }
+ int size = binarySearchSizes(minSize, maxSize, availableSpace);
+ textSizesCache.put(text == null ? 0 : text.hashCode(), size);
+ return size;
+ }
+
+ /**
+ * Performs a binary search to find the largest font size that will still fit within the size
+ * available to this view.
+ * @param minSize the minimum text size to allow, in units of {@link #resizeStepUnit}
+ * @param maxSize the maximum text size to allow, in units of {@link #resizeStepUnit}
+ */
+ private int binarySearchSizes(int minSize, int maxSize, RectF availableSpace) {
+ int bestSize = minSize;
+ int low = minSize + 1;
+ int high = maxSize;
+ int sizeToTry;
+ while (low <= high) {
+ sizeToTry = (low + high) / 2;
+ float dimension = TypedValue.applyDimension(resizeStepUnit, sizeToTry, displayMetrics);
+ if (suggestedSizeFitsInSpace(dimension, availableSpace)) {
+ bestSize = low;
+ low = sizeToTry + 1;
+ } else {
+ high = sizeToTry - 1;
+ bestSize = high;
+ }
+ }
+ return bestSize;
+ }
+
+ private float convertToResizeStepUnits(float dimension) {
+ // To figure out the multiplier between a raw dimension and the resizeStepUnit, we invert the
+ // conversion of 1 resizeStepUnit to a raw dimension.
+ float multiplier = 1 / TypedValue.applyDimension(resizeStepUnit, 1, displayMetrics);
+ return dimension * multiplier;
+ }
+
+ @Override
+ protected final void onTextChanged(
+ final CharSequence text, final int start, final int before, final int after) {
+ super.onTextChanged(text, start, before, after);
+ adjustTextSize();
+ }
+
+ @Override
+ protected final void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
+ super.onSizeChanged(width, height, oldWidth, oldHeight);
+ if (width != oldWidth || height != oldHeight) {
+ textSizesCache.clear();
+ adjustTextSize();
+ }
+ }
+
+ @Override
+ protected final void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ adjustTextSize();
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+}
diff --git a/java/com/android/incallui/autoresizetext/res/values/attrs.xml b/java/com/android/incallui/autoresizetext/res/values/attrs.xml
new file mode 100644
index 000000000..e62feb9c8
--- /dev/null
+++ b/java/com/android/incallui/autoresizetext/res/values/attrs.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<resources>
+ <declare-styleable name="AutoResizeTextView">
+ <!--
+ The unit to use when computing step increments for the resize operation. That is, the
+ resized text will be guaranteed to be a whole number (integer) value in the unit
+ specified. For example, if the unit is scaled pixels (sp), then the font size might be
+ 13sp or 14sp, but not 13.5sp.
+
+ The enum values must match the values from android.util.TypedValue.
+ -->
+ <attr name="autoResizeText_resizeStepUnit" format="enum">
+ <!-- Must match TypedValue.COMPLEX_UNIT_PX. -->
+ <enum name="unitPx" value="0" />
+ <!-- Must match TypedValue.COMPLEX_UNIT_DIP. -->
+ <enum name="unitDip" value="1" />
+ <!-- Must match TypedValue.COMPLEX_UNIT_SP. -->
+ <enum name="unitSp" value="2" />
+ <!-- Must match TypedValue.COMPLEX_UNIT_PT. -->
+ <enum name="unitPt" value="3" />
+ <!-- Must match TypedValue.COMPLEX_UNIT_IN. -->
+ <enum name="unitIn" value="4" />
+ <!-- Must match TypedValue.COMPLEX_UNIT_MM. -->
+ <enum name="unitMm" value="5" />
+ </attr>
+ <!--
+ The minimum text size to use in this view. Text size will be scale down to fit the text
+ in this view, but no smaller than the minimum size specified in this attribute.
+ -->
+ <attr name="autoResizeText_minTextSize" format="dimension" />
+ </declare-styleable>
+</resources>
diff --git a/java/com/android/incallui/baseui/BaseFragment.java b/java/com/android/incallui/baseui/BaseFragment.java
new file mode 100644
index 000000000..58b8c6f8d
--- /dev/null
+++ b/java/com/android/incallui/baseui/BaseFragment.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013 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.incallui.baseui;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+
+/** Parent for all fragments that use Presenters and Ui design. */
+public abstract class BaseFragment<T extends Presenter<U>, U extends Ui> extends Fragment {
+
+ private static final String KEY_FRAGMENT_HIDDEN = "key_fragment_hidden";
+
+ private T mPresenter;
+
+ protected BaseFragment() {
+ mPresenter = createPresenter();
+ }
+
+ public abstract T createPresenter();
+
+ public abstract U getUi();
+
+ /**
+ * Presenter will be available after onActivityCreated().
+ *
+ * @return The presenter associated with this fragment.
+ */
+ public T getPresenter() {
+ return mPresenter;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ mPresenter.onUiReady(getUi());
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ mPresenter.onRestoreInstanceState(savedInstanceState);
+ if (savedInstanceState.getBoolean(KEY_FRAGMENT_HIDDEN)) {
+ getFragmentManager().beginTransaction().hide(this).commit();
+ }
+ }
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mPresenter.onUiDestroy(getUi());
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mPresenter.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_FRAGMENT_HIDDEN, isHidden());
+ }
+}
diff --git a/java/com/android/incallui/baseui/Presenter.java b/java/com/android/incallui/baseui/Presenter.java
new file mode 100644
index 000000000..581ad47c7
--- /dev/null
+++ b/java/com/android/incallui/baseui/Presenter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013 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.incallui.baseui;
+
+import android.os.Bundle;
+
+/** Base class for Presenters. */
+public abstract class Presenter<U extends Ui> {
+
+ private U mUi;
+
+ /**
+ * Called after the UI view has been created. That is when fragment.onViewCreated() is called.
+ *
+ * @param ui The Ui implementation that is now ready to be used.
+ */
+ public void onUiReady(U ui) {
+ mUi = ui;
+ }
+
+ /** Called when the UI view is destroyed in Fragment.onDestroyView(). */
+ public final void onUiDestroy(U ui) {
+ onUiUnready(ui);
+ mUi = null;
+ }
+
+ /**
+ * To be overriden by Presenter implementations. Called when the fragment is being destroyed but
+ * before ui is set to null.
+ */
+ public void onUiUnready(U ui) {}
+
+ public void onSaveInstanceState(Bundle outState) {}
+
+ public void onRestoreInstanceState(Bundle savedInstanceState) {}
+
+ public U getUi() {
+ return mUi;
+ }
+}
diff --git a/InCallUI/src/com/android/incallui/Ui.java b/java/com/android/incallui/baseui/Ui.java
index e453ccb1c..439e41550 100644
--- a/InCallUI/src/com/android/incallui/Ui.java
+++ b/java/com/android/incallui/baseui/Ui.java
@@ -14,11 +14,7 @@
* limitations under the License
*/
-package com.android.incallui;
+package com.android.incallui.baseui;
-/**
- * Base class for all presenter ui.
- */
-public interface Ui {
-
-}
+/** Base class for all presenter ui. */
+public interface Ui {}
diff --git a/java/com/android/incallui/bindings/ContactUtils.java b/java/com/android/incallui/bindings/ContactUtils.java
new file mode 100644
index 000000000..d2d365d81
--- /dev/null
+++ b/java/com/android/incallui/bindings/ContactUtils.java
@@ -0,0 +1,33 @@
+/*
+ * 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.incallui.bindings;
+
+import android.location.Address;
+import android.util.Pair;
+import java.util.Calendar;
+import java.util.List;
+
+/** Utility functions to help manipulate contact data. */
+public interface ContactUtils {
+
+ boolean retrieveContactInteractionsFromLookupKey(String lookupKey, Listener listener);
+
+ interface Listener {
+
+ void onContactInteractionsFound(Address address, List<Pair<Calendar, Calendar>> openingHours);
+ }
+}
diff --git a/java/com/android/incallui/bindings/DistanceHelper.java b/java/com/android/incallui/bindings/DistanceHelper.java
new file mode 100644
index 000000000..6b2200dca
--- /dev/null
+++ b/java/com/android/incallui/bindings/DistanceHelper.java
@@ -0,0 +1,36 @@
+/*
+ * 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.incallui.bindings;
+
+import android.location.Address;
+
+/** Superclass for a helper class to get the current location and distance to other locations. */
+public interface DistanceHelper {
+
+ float DISTANCE_NOT_FOUND = -1;
+ float MILES_PER_METER = (float) 0.000621371192;
+ float KILOMETERS_PER_METER = (float) 0.001;
+
+ void cleanUp();
+
+ float calculateDistance(Address address);
+
+ interface Listener {
+
+ void onLocationReady();
+ }
+}
diff --git a/java/com/android/incallui/bindings/InCallUiBindings.java b/java/com/android/incallui/bindings/InCallUiBindings.java
new file mode 100644
index 000000000..d3d3a8b37
--- /dev/null
+++ b/java/com/android/incallui/bindings/InCallUiBindings.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 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.incallui.bindings;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.annotation.Nullable;
+import com.android.dialer.common.ConfigProvider;
+
+/** This interface allows the container application to customize the in call UI. */
+public interface InCallUiBindings {
+
+ @Nullable
+ PhoneNumberService newPhoneNumberService(Context context);
+
+ /** @return An {@link Intent} to be broadcast when the InCallUI is visible. */
+ @Nullable
+ Intent getUiReadyBroadcastIntent(Context context);
+
+ /**
+ * @return An {@link Intent} to be broadcast when the call state button in the InCallUI is touched
+ * while in a call.
+ */
+ @Nullable
+ Intent getCallStateButtonBroadcastIntent(Context context);
+
+ @Nullable
+ DistanceHelper newDistanceHelper(Context context, DistanceHelper.Listener listener);
+
+ @Nullable
+ ContactUtils getContactUtilsInstance(Context context);
+
+ ConfigProvider getConfigProvider();
+}
diff --git a/java/com/android/incallui/bindings/InCallUiBindingsFactory.java b/java/com/android/incallui/bindings/InCallUiBindingsFactory.java
new file mode 100644
index 000000000..57c186d90
--- /dev/null
+++ b/java/com/android/incallui/bindings/InCallUiBindingsFactory.java
@@ -0,0 +1,26 @@
+/*
+ * 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.incallui.bindings;
+
+/**
+ * This interface should be implementated by the Application subclass. It allows the in call UI
+ * module to get references to the InCallUiBindings.
+ */
+public interface InCallUiBindingsFactory {
+
+ InCallUiBindings newInCallUiBindings();
+}
diff --git a/java/com/android/incallui/bindings/InCallUiBindingsStub.java b/java/com/android/incallui/bindings/InCallUiBindingsStub.java
new file mode 100644
index 000000000..7b42fb375
--- /dev/null
+++ b/java/com/android/incallui/bindings/InCallUiBindingsStub.java
@@ -0,0 +1,81 @@
+/*
+ * 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.incallui.bindings;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.annotation.Nullable;
+import com.android.dialer.common.ConfigProvider;
+
+/** Default implementation for InCallUi bindings. */
+public class InCallUiBindingsStub implements InCallUiBindings {
+ private ConfigProvider configProvider;
+
+ @Override
+ @Nullable
+ public PhoneNumberService newPhoneNumberService(Context context) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ public Intent getUiReadyBroadcastIntent(Context context) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ public Intent getCallStateButtonBroadcastIntent(Context context) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ public DistanceHelper newDistanceHelper(Context context, DistanceHelper.Listener listener) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ public ContactUtils getContactUtilsInstance(Context context) {
+ return null;
+ }
+
+ @Override
+ public ConfigProvider getConfigProvider() {
+ if (configProvider == null) {
+ configProvider =
+ new ConfigProvider() {
+ @Override
+ public String getString(String key, String defaultValue) {
+ return defaultValue;
+ }
+
+ @Override
+ public long getLong(String key, long defaultValue) {
+ return defaultValue;
+ }
+
+ @Override
+ public boolean getBoolean(String key, boolean defaultValue) {
+ return defaultValue;
+ }
+ };
+ }
+ return configProvider;
+ }
+}
diff --git a/java/com/android/incallui/bindings/PhoneNumberService.java b/java/com/android/incallui/bindings/PhoneNumberService.java
new file mode 100644
index 000000000..bd2741a1d
--- /dev/null
+++ b/java/com/android/incallui/bindings/PhoneNumberService.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 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.incallui.bindings;
+
+import android.graphics.Bitmap;
+
+/** Provides phone number lookup services. */
+public interface PhoneNumberService {
+
+ /**
+ * Get a phone number number asynchronously.
+ *
+ * @param phoneNumber The phone number to lookup.
+ * @param listener The listener to notify when the phone number lookup is complete.
+ * @param imageListener The listener to notify when the image lookup is complete.
+ */
+ void getPhoneNumberInfo(
+ String phoneNumber,
+ NumberLookupListener listener,
+ ImageLookupListener imageListener,
+ boolean isIncoming);
+
+ interface NumberLookupListener {
+
+ /**
+ * Callback when a phone number has been looked up.
+ *
+ * @param info The looked up information. Or (@literal null} if there are no results.
+ */
+ void onPhoneNumberInfoComplete(PhoneNumberInfo info);
+ }
+
+ interface ImageLookupListener {
+
+ /**
+ * Callback when a image has been fetched.
+ *
+ * @param bitmap The fetched image.
+ */
+ void onImageFetchComplete(Bitmap bitmap);
+ }
+
+ interface PhoneNumberInfo {
+
+ String getDisplayName();
+
+ String getNumber();
+
+ int getPhoneType();
+
+ String getPhoneLabel();
+
+ String getNormalizedNumber();
+
+ String getImageUrl();
+
+ String getLookupKey();
+
+ boolean isBusiness();
+
+ int getLookupSource();
+ }
+}
diff --git a/java/com/android/incallui/call/CallList.java b/java/com/android/incallui/call/CallList.java
new file mode 100644
index 000000000..862c71cf9
--- /dev/null
+++ b/java/com/android/incallui/call/CallList.java
@@ -0,0 +1,763 @@
+/*
+ * Copyright (C) 2017 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.incallui.call;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Trace;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.os.BuildCompat;
+import android.telecom.Call;
+import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccount;
+import android.util.ArrayMap;
+import com.android.contacts.common.GeoUtil;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.blocking.FilteredNumbersUtil;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.shortcuts.ShortcutUsageReporter;
+import com.android.dialer.spam.Spam;
+import com.android.dialer.spam.SpamBindings;
+import com.android.incallui.call.DialerCall.SessionModificationState;
+import com.android.incallui.call.DialerCall.State;
+import com.android.incallui.latencyreport.LatencyReport;
+import com.android.incallui.util.TelecomCallUtil;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Maintains the list of active calls and notifies interested classes of changes to the call list as
+ * they are received from the telephony stack. Primary listener of changes to this class is
+ * InCallPresenter.
+ */
+public class CallList implements DialerCallDelegate {
+
+ private static final int DISCONNECTED_CALL_SHORT_TIMEOUT_MS = 200;
+ private static final int DISCONNECTED_CALL_MEDIUM_TIMEOUT_MS = 2000;
+ private static final int DISCONNECTED_CALL_LONG_TIMEOUT_MS = 5000;
+
+ private static final int EVENT_DISCONNECTED_TIMEOUT = 1;
+
+ private static CallList sInstance = new CallList();
+
+ private final Map<String, DialerCall> mCallById = new ArrayMap<>();
+ private final Map<android.telecom.Call, DialerCall> mCallByTelecomCall = new ArrayMap<>();
+
+ /**
+ * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is load factor before
+ * resizing, 1 means we only expect a single thread to access the map so make only a single shard
+ */
+ private final Set<Listener> mListeners =
+ Collections.newSetFromMap(new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
+
+ private final Set<DialerCall> mPendingDisconnectCalls =
+ Collections.newSetFromMap(new ConcurrentHashMap<DialerCall, Boolean>(8, 0.9f, 1));
+ /** Handles the timeout for destroying disconnected calls. */
+ private final Handler mHandler =
+ new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_DISCONNECTED_TIMEOUT:
+ LogUtil.d("CallList.handleMessage", "EVENT_DISCONNECTED_TIMEOUT ", msg.obj);
+ finishDisconnectedCall((DialerCall) msg.obj);
+ break;
+ default:
+ LogUtil.e("CallList.handleMessage", "Message not expected: " + msg.what);
+ break;
+ }
+ }
+ };
+
+ /**
+ * USED ONLY FOR TESTING Testing-only constructor. Instance should only be acquired through
+ * getInstance().
+ */
+ @VisibleForTesting
+ public CallList() {}
+
+ /** Static singleton accessor method. */
+ public static CallList getInstance() {
+ return sInstance;
+ }
+
+ public void onCallAdded(
+ final Context context, final android.telecom.Call telecomCall, LatencyReport latencyReport) {
+ Trace.beginSection("onCallAdded");
+ final DialerCall call =
+ new DialerCall(context, this, telecomCall, latencyReport, true /* registerCallback */);
+ final DialerCallListenerImpl dialerCallListener = new DialerCallListenerImpl(call);
+ call.addListener(dialerCallListener);
+ LogUtil.d("CallList.onCallAdded", "callState=" + call.getState());
+ if (Spam.get(context).isSpamEnabled()) {
+ String number = TelecomCallUtil.getNumber(telecomCall);
+ Spam.get(context)
+ .checkSpamStatus(
+ number,
+ null,
+ new SpamBindings.Listener() {
+ @Override
+ public void onComplete(boolean isSpam) {
+ if (isSpam) {
+ if (call.getState() != DialerCall.State.INCOMING
+ && call.getState() != DialerCall.State.CALL_WAITING) {
+ LogUtil.i(
+ "CallList.onCallAdded",
+ "marking spam call as not spam because it's not an incoming call");
+ isSpam = false;
+ } else if (isPotentialEmergencyCallback(context, call)) {
+ LogUtil.i(
+ "CallList.onCallAdded",
+ "marking spam call as not spam because an emergency call was made on this"
+ + " device recently");
+ isSpam = false;
+ }
+ }
+
+ Logger.get(context)
+ .logCallImpression(
+ isSpam
+ ? DialerImpression.Type.INCOMING_SPAM_CALL
+ : DialerImpression.Type.INCOMING_NON_SPAM_CALL,
+ call.getUniqueCallId(),
+ call.getTimeAddedMs());
+ call.setSpam(isSpam);
+ dialerCallListener.onDialerCallUpdate();
+ }
+ });
+
+ updateUserMarkedSpamStatus(call, context, number, dialerCallListener);
+ }
+
+ FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler =
+ new FilteredNumberAsyncQueryHandler(context);
+
+ filteredNumberAsyncQueryHandler.isBlockedNumber(
+ new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
+ @Override
+ public void onCheckComplete(Integer id) {
+ if (id != null && id != FilteredNumberAsyncQueryHandler.INVALID_ID) {
+ call.setBlockedStatus(true);
+ dialerCallListener.onDialerCallUpdate();
+ }
+ }
+ },
+ call.getNumber(),
+ GeoUtil.getCurrentCountryIso(context));
+
+ if (call.getState() == DialerCall.State.INCOMING
+ || call.getState() == DialerCall.State.CALL_WAITING) {
+ onIncoming(call);
+ } else {
+ dialerCallListener.onDialerCallUpdate();
+ }
+
+ if (call.getState() != State.INCOMING) {
+ // Only report outgoing calls
+ ShortcutUsageReporter.onOutgoingCallAdded(context, call.getNumber());
+ }
+
+ Trace.endSection();
+ }
+
+ private static boolean isPotentialEmergencyCallback(Context context, DialerCall call) {
+ if (BuildCompat.isAtLeastO()) {
+ return call.isPotentialEmergencyCallback();
+ } else {
+ long timestampMillis = FilteredNumbersUtil.getLastEmergencyCallTimeMillis(context);
+ return call.isInEmergencyCallbackWindow(timestampMillis);
+ }
+ }
+
+ @Override
+ public DialerCall getDialerCallFromTelecomCall(Call telecomCall) {
+ return mCallByTelecomCall.get(telecomCall);
+ }
+
+ public void updateUserMarkedSpamStatus(
+ final DialerCall call,
+ final Context context,
+ String number,
+ final DialerCallListenerImpl dialerCallListener) {
+
+ Spam.get(context)
+ .checkUserMarkedNonSpamStatus(
+ number,
+ null,
+ new SpamBindings.Listener() {
+ @Override
+ public void onComplete(boolean isInUserWhiteList) {
+ call.setIsInUserWhiteList(isInUserWhiteList);
+ }
+ });
+
+ Spam.get(context)
+ .checkGlobalSpamListStatus(
+ number,
+ null,
+ new SpamBindings.Listener() {
+ @Override
+ public void onComplete(boolean isInGlobalSpamList) {
+ call.setIsInGlobalSpamList(isInGlobalSpamList);
+ }
+ });
+
+ Spam.get(context)
+ .checkUserMarkedSpamStatus(
+ number,
+ null,
+ new SpamBindings.Listener() {
+ @Override
+ public void onComplete(boolean isInUserSpamList) {
+ call.setIsInUserSpamList(isInUserSpamList);
+ }
+ });
+ }
+
+ public void onCallRemoved(Context context, android.telecom.Call telecomCall) {
+ if (mCallByTelecomCall.containsKey(telecomCall)) {
+ DialerCall call = mCallByTelecomCall.get(telecomCall);
+ Assert.checkArgument(!call.isExternalCall());
+
+ // Don't log an already logged call. logCall() might be called multiple times
+ // for the same call due to b/24109437.
+ if (call.getLogState() != null && !call.getLogState().isLogged) {
+ getLegacyBindings(context).logCall(call);
+ call.getLogState().isLogged = true;
+ }
+
+ if (updateCallInMap(call)) {
+ LogUtil.w(
+ "CallList.onCallRemoved", "Removing call not previously disconnected " + call.getId());
+ }
+ }
+ }
+
+ InCallUiLegacyBindings getLegacyBindings(Context context) {
+ Objects.requireNonNull(context);
+
+ Context application = context.getApplicationContext();
+ InCallUiLegacyBindings legacyInstance = null;
+ if (application instanceof InCallUiLegacyBindingsFactory) {
+ legacyInstance = ((InCallUiLegacyBindingsFactory) application).newInCallUiLegacyBindings();
+ }
+
+ if (legacyInstance == null) {
+ legacyInstance = new InCallUiLegacyBindingsStub();
+ }
+ return legacyInstance;
+ }
+
+ /**
+ * Handles the case where an internal call has become an exteral call. We need to
+ *
+ * @param context
+ * @param telecomCall
+ */
+ public void onInternalCallMadeExternal(Context context, android.telecom.Call telecomCall) {
+
+ if (mCallByTelecomCall.containsKey(telecomCall)) {
+ DialerCall call = mCallByTelecomCall.get(telecomCall);
+
+ // Don't log an already logged call. logCall() might be called multiple times
+ // for the same call due to b/24109437.
+ if (call.getLogState() != null && !call.getLogState().isLogged) {
+ getLegacyBindings(context).logCall(call);
+ call.getLogState().isLogged = true;
+ }
+
+ // When removing a call from the call list because it became an external call, we need to
+ // ensure the callback is unregistered -- this is normally only done when calls disconnect.
+ // However, the call won't be disconnected in this case. Also, logic in updateCallInMap
+ // would just re-add the call anyways.
+ call.unregisterCallback();
+ mCallById.remove(call.getId());
+ mCallByTelecomCall.remove(telecomCall);
+ }
+ }
+
+ /** Called when a single call has changed. */
+ private void onIncoming(DialerCall call) {
+ if (updateCallInMap(call)) {
+ LogUtil.i("CallList.onIncoming", String.valueOf(call));
+ }
+
+ for (Listener listener : mListeners) {
+ listener.onIncomingCall(call);
+ }
+ }
+
+ public void addListener(@NonNull Listener listener) {
+ Objects.requireNonNull(listener);
+
+ mListeners.add(listener);
+
+ // Let the listener know about the active calls immediately.
+ listener.onCallListChange(this);
+ }
+
+ public void removeListener(@Nullable Listener listener) {
+ if (listener != null) {
+ mListeners.remove(listener);
+ }
+ }
+
+ /**
+ * TODO: Change so that this function is not needed. Instead of assuming there is an active call,
+ * the code should rely on the status of a specific DialerCall and allow the presenters to update
+ * the DialerCall object when the active call changes.
+ */
+ public DialerCall getIncomingOrActive() {
+ DialerCall retval = getIncomingCall();
+ if (retval == null) {
+ retval = getActiveCall();
+ }
+ return retval;
+ }
+
+ public DialerCall getOutgoingOrActive() {
+ DialerCall retval = getOutgoingCall();
+ if (retval == null) {
+ retval = getActiveCall();
+ }
+ return retval;
+ }
+
+ /** A call that is waiting for {@link PhoneAccount} selection */
+ public DialerCall getWaitingForAccountCall() {
+ return getFirstCallWithState(DialerCall.State.SELECT_PHONE_ACCOUNT);
+ }
+
+ public DialerCall getPendingOutgoingCall() {
+ return getFirstCallWithState(DialerCall.State.CONNECTING);
+ }
+
+ public DialerCall getOutgoingCall() {
+ DialerCall call = getFirstCallWithState(DialerCall.State.DIALING);
+ if (call == null) {
+ call = getFirstCallWithState(DialerCall.State.REDIALING);
+ }
+ if (call == null) {
+ call = getFirstCallWithState(DialerCall.State.PULLING);
+ }
+ return call;
+ }
+
+ public DialerCall getActiveCall() {
+ return getFirstCallWithState(DialerCall.State.ACTIVE);
+ }
+
+ public DialerCall getSecondActiveCall() {
+ return getCallWithState(DialerCall.State.ACTIVE, 1);
+ }
+
+ public DialerCall getBackgroundCall() {
+ return getFirstCallWithState(DialerCall.State.ONHOLD);
+ }
+
+ public DialerCall getDisconnectedCall() {
+ return getFirstCallWithState(DialerCall.State.DISCONNECTED);
+ }
+
+ public DialerCall getDisconnectingCall() {
+ return getFirstCallWithState(DialerCall.State.DISCONNECTING);
+ }
+
+ public DialerCall getSecondBackgroundCall() {
+ return getCallWithState(DialerCall.State.ONHOLD, 1);
+ }
+
+ public DialerCall getActiveOrBackgroundCall() {
+ DialerCall call = getActiveCall();
+ if (call == null) {
+ call = getBackgroundCall();
+ }
+ return call;
+ }
+
+ public DialerCall getIncomingCall() {
+ DialerCall call = getFirstCallWithState(DialerCall.State.INCOMING);
+ if (call == null) {
+ call = getFirstCallWithState(DialerCall.State.CALL_WAITING);
+ }
+
+ return call;
+ }
+
+ public DialerCall getFirstCall() {
+ DialerCall result = getIncomingCall();
+ if (result == null) {
+ result = getPendingOutgoingCall();
+ }
+ if (result == null) {
+ result = getOutgoingCall();
+ }
+ if (result == null) {
+ result = getFirstCallWithState(DialerCall.State.ACTIVE);
+ }
+ if (result == null) {
+ result = getDisconnectingCall();
+ }
+ if (result == null) {
+ result = getDisconnectedCall();
+ }
+ return result;
+ }
+
+ public boolean hasLiveCall() {
+ DialerCall call = getFirstCall();
+ return call != null && call != getDisconnectingCall() && call != getDisconnectedCall();
+ }
+
+ /**
+ * Returns the first call found in the call map with the upgrade to video modification state.
+ *
+ * @return The first call with the upgrade to video state.
+ */
+ public DialerCall getVideoUpgradeRequestCall() {
+ for (DialerCall call : mCallById.values()) {
+ if (call.getSessionModificationState()
+ == DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
+ return call;
+ }
+ }
+ return null;
+ }
+
+ public DialerCall getCallById(String callId) {
+ return mCallById.get(callId);
+ }
+
+ /** Returns first call found in the call map with the specified state. */
+ public DialerCall getFirstCallWithState(int state) {
+ return getCallWithState(state, 0);
+ }
+
+ /**
+ * Returns the [position]th call found in the call map with the specified state. TODO: Improve
+ * this logic to sort by call time.
+ */
+ public DialerCall getCallWithState(int state, int positionToFind) {
+ DialerCall retval = null;
+ int position = 0;
+ for (DialerCall call : mCallById.values()) {
+ if (call.getState() == state) {
+ if (position >= positionToFind) {
+ retval = call;
+ break;
+ } else {
+ position++;
+ }
+ }
+ }
+
+ return retval;
+ }
+
+ /**
+ * This is called when the service disconnects, either expectedly or unexpectedly. For the
+ * expected case, it's because we have no calls left. For the unexpected case, it is likely a
+ * crash of phone and we need to clean up our calls manually. Without phone, there can be no
+ * active calls, so this is relatively safe thing to do.
+ */
+ public void clearOnDisconnect() {
+ for (DialerCall call : mCallById.values()) {
+ final int state = call.getState();
+ if (state != DialerCall.State.IDLE
+ && state != DialerCall.State.INVALID
+ && state != DialerCall.State.DISCONNECTED) {
+
+ call.setState(DialerCall.State.DISCONNECTED);
+ call.setDisconnectCause(new DisconnectCause(DisconnectCause.UNKNOWN));
+ updateCallInMap(call);
+ }
+ }
+ notifyGenericListeners();
+ }
+
+ /**
+ * Called when the user has dismissed an error dialog. This indicates acknowledgement of the
+ * disconnect cause, and that any pending disconnects should immediately occur.
+ */
+ public void onErrorDialogDismissed() {
+ final Iterator<DialerCall> iterator = mPendingDisconnectCalls.iterator();
+ while (iterator.hasNext()) {
+ DialerCall call = iterator.next();
+ iterator.remove();
+ finishDisconnectedCall(call);
+ }
+ }
+
+ /**
+ * Processes an update for a single call.
+ *
+ * @param call The call to update.
+ */
+ private void onUpdateCall(DialerCall call) {
+ LogUtil.d("CallList.onUpdateCall", String.valueOf(call));
+ if (!mCallById.containsKey(call.getId()) && call.isExternalCall()) {
+ // When a regular call becomes external, it is removed from the call list, and there may be
+ // pending updates to Telecom which are queued up on the Telecom call's handler which we no
+ // longer wish to cause updates to the call in the CallList. Bail here if the list of tracked
+ // calls doesn't contain the call which received the update.
+ return;
+ }
+
+ if (updateCallInMap(call)) {
+ LogUtil.i("CallList.onUpdateCall", String.valueOf(call));
+ }
+ }
+
+ /**
+ * Sends a generic notification to all listeners that something has changed. It is up to the
+ * listeners to call back to determine what changed.
+ */
+ private void notifyGenericListeners() {
+ for (Listener listener : mListeners) {
+ listener.onCallListChange(this);
+ }
+ }
+
+ private void notifyListenersOfDisconnect(DialerCall call) {
+ for (Listener listener : mListeners) {
+ listener.onDisconnect(call);
+ }
+ }
+
+ /**
+ * Updates the call entry in the local map.
+ *
+ * @return false if no call previously existed and no call was added, otherwise true.
+ */
+ private boolean updateCallInMap(DialerCall call) {
+ Objects.requireNonNull(call);
+
+ boolean updated = false;
+
+ if (call.getState() == DialerCall.State.DISCONNECTED) {
+ // update existing (but do not add!!) disconnected calls
+ if (mCallById.containsKey(call.getId())) {
+ // For disconnected calls, we want to keep them alive for a few seconds so that the
+ // UI has a chance to display anything it needs when a call is disconnected.
+
+ // Set up a timer to destroy the call after X seconds.
+ final Message msg = mHandler.obtainMessage(EVENT_DISCONNECTED_TIMEOUT, call);
+ mHandler.sendMessageDelayed(msg, getDelayForDisconnect(call));
+ mPendingDisconnectCalls.add(call);
+
+ mCallById.put(call.getId(), call);
+ mCallByTelecomCall.put(call.getTelecomCall(), call);
+ updated = true;
+ }
+ } else if (!isCallDead(call)) {
+ mCallById.put(call.getId(), call);
+ mCallByTelecomCall.put(call.getTelecomCall(), call);
+ updated = true;
+ } else if (mCallById.containsKey(call.getId())) {
+ mCallById.remove(call.getId());
+ mCallByTelecomCall.remove(call.getTelecomCall());
+ updated = true;
+ }
+
+ return updated;
+ }
+
+ private int getDelayForDisconnect(DialerCall call) {
+ if (call.getState() != DialerCall.State.DISCONNECTED) {
+ throw new IllegalStateException();
+ }
+
+ final int cause = call.getDisconnectCause().getCode();
+ final int delay;
+ switch (cause) {
+ case DisconnectCause.LOCAL:
+ delay = DISCONNECTED_CALL_SHORT_TIMEOUT_MS;
+ break;
+ case DisconnectCause.REMOTE:
+ case DisconnectCause.ERROR:
+ delay = DISCONNECTED_CALL_MEDIUM_TIMEOUT_MS;
+ break;
+ case DisconnectCause.REJECTED:
+ case DisconnectCause.MISSED:
+ case DisconnectCause.CANCELED:
+ // no delay for missed/rejected incoming calls and canceled outgoing calls.
+ delay = 0;
+ break;
+ default:
+ delay = DISCONNECTED_CALL_LONG_TIMEOUT_MS;
+ break;
+ }
+
+ return delay;
+ }
+
+ private boolean isCallDead(DialerCall call) {
+ final int state = call.getState();
+ return DialerCall.State.IDLE == state || DialerCall.State.INVALID == state;
+ }
+
+ /** Sets up a call for deletion and notifies listeners of change. */
+ private void finishDisconnectedCall(DialerCall call) {
+ if (mPendingDisconnectCalls.contains(call)) {
+ mPendingDisconnectCalls.remove(call);
+ }
+ call.setState(DialerCall.State.IDLE);
+ updateCallInMap(call);
+ notifyGenericListeners();
+ }
+
+ /**
+ * Notifies all video calls of a change in device orientation.
+ *
+ * @param rotation The new rotation angle (in degrees).
+ */
+ public void notifyCallsOfDeviceRotation(int rotation) {
+ for (DialerCall call : mCallById.values()) {
+ // First, ensure that the call videoState has video enabled (there is no need to set
+ // device orientation on a voice call which has not yet been upgraded to video).
+ // Second, ensure a VideoCall is set on the call so that the change can be sent to the
+ // provider (a VideoCall can be present for a call that does not currently have video,
+ // but can be upgraded to video).
+
+ // NOTE: is it necessary to use this order because getVideoCall references the class
+ // VideoProfile which is not available on APIs <23 (M).
+ if (VideoUtils.isVideoCall(call) && call.getVideoCall() != null) {
+ call.getVideoCall().setDeviceOrientation(rotation);
+ }
+ }
+ }
+
+ public void onInCallUiShown(boolean forFullScreenIntent) {
+ for (DialerCall call : mCallById.values()) {
+ call.getLatencyReport().onInCallUiShown(forFullScreenIntent);
+ }
+ }
+
+ /** Listener interface for any class that wants to be notified of changes to the call list. */
+ public interface Listener {
+
+ /**
+ * Called when a new incoming call comes in. This is the only method that gets called for
+ * incoming calls. Listeners that want to perform an action on incoming call should respond in
+ * this method because {@link #onCallListChange} does not automatically get called for incoming
+ * calls.
+ */
+ void onIncomingCall(DialerCall call);
+
+ /**
+ * Called when a new modify call request comes in This is the only method that gets called for
+ * modify requests.
+ */
+ void onUpgradeToVideo(DialerCall call);
+
+ /** Called when the session modification state of a call changes. */
+ void onSessionModificationStateChange(@SessionModificationState int newState);
+
+ /**
+ * Called anytime there are changes to the call list. The change can be switching call states,
+ * updating information, etc. This method will NOT be called for new incoming calls and for
+ * calls that switch to disconnected state. Listeners must add actions to those method
+ * implementations if they want to deal with those actions.
+ */
+ void onCallListChange(CallList callList);
+
+ /**
+ * Called when a call switches to the disconnected state. This is the only method that will get
+ * called upon disconnection.
+ */
+ void onDisconnect(DialerCall call);
+
+ void onWiFiToLteHandover(DialerCall call);
+
+ /**
+ * Called when a user is in a video call and the call is unable to be handed off successfully to
+ * WiFi
+ */
+ void onHandoverToWifiFailed(DialerCall call);
+ }
+
+ private class DialerCallListenerImpl implements DialerCallListener {
+
+ private final DialerCall mCall;
+
+ DialerCallListenerImpl(DialerCall call) {
+ Assert.isNotNull(call);
+ mCall = call;
+ }
+
+ @Override
+ public void onDialerCallDisconnect() {
+ if (updateCallInMap(mCall)) {
+ LogUtil.i("DialerCallListenerImpl.onDialerCallDisconnect", String.valueOf(mCall));
+ // notify those listening for all disconnects
+ notifyListenersOfDisconnect(mCall);
+ }
+ }
+
+ @Override
+ public void onDialerCallUpdate() {
+ Trace.beginSection("onUpdate");
+ onUpdateCall(mCall);
+ notifyGenericListeners();
+ Trace.endSection();
+ }
+
+ @Override
+ public void onDialerCallChildNumberChange() {}
+
+ @Override
+ public void onDialerCallLastForwardedNumberChange() {}
+
+ @Override
+ public void onDialerCallUpgradeToVideo() {
+ for (Listener listener : mListeners) {
+ listener.onUpgradeToVideo(mCall);
+ }
+ }
+
+ @Override
+ public void onWiFiToLteHandover() {
+ for (Listener listener : mListeners) {
+ listener.onWiFiToLteHandover(mCall);
+ }
+ }
+
+ @Override
+ public void onHandoverToWifiFailure() {
+ for (Listener listener : mListeners) {
+ listener.onHandoverToWifiFailed(mCall);
+ }
+ }
+
+ @Override
+ public void onDialerCallSessionModificationStateChange(@SessionModificationState int state) {
+ for (Listener listener : mListeners) {
+ listener.onSessionModificationStateChange(state);
+ }
+ }
+ }
+}
diff --git a/java/com/android/incallui/call/DialerCall.java b/java/com/android/incallui/call/DialerCall.java
new file mode 100644
index 000000000..bd8f006dd
--- /dev/null
+++ b/java/com/android/incallui/call/DialerCall.java
@@ -0,0 +1,1401 @@
+/*
+ * Copyright (C) 2013 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.incallui.call;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.Trace;
+import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
+import android.telecom.Call;
+import android.telecom.Call.Details;
+import android.telecom.Connection;
+import android.telecom.DisconnectCause;
+import android.telecom.GatewayInfo;
+import android.telecom.InCallService.VideoCall;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.StatusHints;
+import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import com.android.contacts.common.compat.CallCompat;
+import com.android.contacts.common.compat.TelephonyManagerCompat;
+import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
+import com.android.dialer.callintent.CallIntentParser;
+import com.android.dialer.callintent.nano.CallInitiationType;
+import com.android.dialer.callintent.nano.CallSpecificAppData;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.ConfigProviderBindings;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.logging.nano.ContactLookupResult;
+import com.android.dialer.util.CallUtil;
+import com.android.incallui.latencyreport.LatencyReport;
+import com.android.incallui.util.TelecomCallUtil;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+
+/** Describes a single call and its state. */
+public class DialerCall {
+
+ public static final int CALL_HISTORY_STATUS_UNKNOWN = 0;
+ public static final int CALL_HISTORY_STATUS_PRESENT = 1;
+ public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2;
+ private static final String ID_PREFIX = "DialerCall_";
+ private static final String CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS =
+ "emergency_callback_window_millis";
+ private static int sIdCounter = 0;
+
+ /**
+ * The unique call ID for every call. This will help us to identify each call and allow us the
+ * ability to stitch impressions to calls if needed.
+ */
+ private final String uniqueCallId = UUID.randomUUID().toString();
+
+ private final Call mTelecomCall;
+ private final LatencyReport mLatencyReport;
+ private final String mId;
+ private final List<String> mChildCallIds = new ArrayList<>();
+ private final VideoSettings mVideoSettings = new VideoSettings();
+ private final LogState mLogState = new LogState();
+ private final Context mContext;
+ private final DialerCallDelegate mDialerCallDelegate;
+ private final List<DialerCallListener> mListeners = new CopyOnWriteArrayList<>();
+ private final List<CannedTextResponsesLoadedListener> mCannedTextResponsesLoadedListeners =
+ new CopyOnWriteArrayList<>();
+
+ private boolean mIsEmergencyCall;
+ private Uri mHandle;
+ private int mState = State.INVALID;
+ private DisconnectCause mDisconnectCause;
+
+ private boolean hasShownWiFiToLteHandoverToast;
+ private boolean doNotShowDialogForHandoffToWifiFailure;
+
+ @SessionModificationState private int mSessionModificationState;
+ private int mVideoState;
+ /** mRequestedVideoState is used to store requested upgrade / downgrade video state */
+ private int mRequestedVideoState = VideoProfile.STATE_AUDIO_ONLY;
+
+ private InCallVideoCallCallback mVideoCallCallback;
+ private boolean mIsVideoCallCallbackRegistered;
+ private String mChildNumber;
+ private String mLastForwardedNumber;
+ private String mCallSubject;
+ private PhoneAccountHandle mPhoneAccountHandle;
+ @CallHistoryStatus private int mCallHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
+ private boolean mIsSpam;
+ private boolean mIsBlocked;
+ private boolean isInUserSpamList;
+ private boolean isInUserWhiteList;
+ private boolean isInGlobalSpamList;
+ private boolean didShowCameraPermission;
+ private String callProviderLabel;
+ private String callbackNumber;
+
+ public static String getNumberFromHandle(Uri handle) {
+ return handle == null ? "" : handle.getSchemeSpecificPart();
+ }
+
+ /**
+ * Whether the call is put on hold by remote party. This is different than the {@link
+ * State.ONHOLD} state which indicates that the call is being held locally on the device.
+ */
+ private boolean isRemotelyHeld;
+
+ /**
+ * Indicates whether the phone account associated with this call supports specifying a call
+ * subject.
+ */
+ private boolean mIsCallSubjectSupported;
+
+ private final Call.Callback mTelecomCallCallback =
+ new Call.Callback() {
+ @Override
+ public void onStateChanged(Call call, int newState) {
+ LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
+ update();
+ }
+
+ @Override
+ public void onParentChanged(Call call, Call newParent) {
+ LogUtil.v(
+ "TelecomCallCallback.onParentChanged", "call=" + call + " newParent=" + newParent);
+ update();
+ }
+
+ @Override
+ public void onChildrenChanged(Call call, List<Call> children) {
+ update();
+ }
+
+ @Override
+ public void onDetailsChanged(Call call, Call.Details details) {
+ LogUtil.v("TelecomCallCallback.onStateChanged", " call=" + call + " details=" + details);
+ update();
+ }
+
+ @Override
+ public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {
+ LogUtil.v(
+ "TelecomCallCallback.onStateChanged",
+ "call=" + call + " cannedTextResponses=" + cannedTextResponses);
+ for (CannedTextResponsesLoadedListener listener : mCannedTextResponsesLoadedListeners) {
+ listener.onCannedTextResponsesLoaded(DialerCall.this);
+ }
+ }
+
+ @Override
+ public void onPostDialWait(Call call, String remainingPostDialSequence) {
+ LogUtil.v(
+ "TelecomCallCallback.onStateChanged",
+ "call=" + call + " remainingPostDialSequence=" + remainingPostDialSequence);
+ update();
+ }
+
+ @Override
+ public void onVideoCallChanged(Call call, VideoCall videoCall) {
+ LogUtil.v(
+ "TelecomCallCallback.onStateChanged", "call=" + call + " videoCall=" + videoCall);
+ update();
+ }
+
+ @Override
+ public void onCallDestroyed(Call call) {
+ LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call);
+ call.unregisterCallback(this);
+ }
+
+ @Override
+ public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {
+ LogUtil.v(
+ "DialerCall.onConferenceableCallsChanged",
+ "call %s, conferenceable calls: %d",
+ call,
+ conferenceableCalls.size());
+ update();
+ }
+
+ @Override
+ public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) {
+ LogUtil.v(
+ "DialerCall.onConnectionEvent",
+ "Call: " + call + ", Event: " + event + ", Extras: " + extras);
+ switch (event) {
+ // The Previous attempt to Merge two calls together has failed in Telecom. We must
+ // now update the UI to possibly re-enable the Merge button based on the number of
+ // currently conferenceable calls available or Connection Capabilities.
+ case android.telecom.Connection.EVENT_CALL_MERGE_FAILED:
+ update();
+ break;
+ case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE:
+ notifyWiFiToLteHandover();
+ break;
+ case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED:
+ notifyHandoverToWifiFailed();
+ break;
+ case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD:
+ isRemotelyHeld = true;
+ update();
+ break;
+ case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD:
+ isRemotelyHeld = false;
+ update();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+ private long mTimeAddedMs;
+
+ public DialerCall(
+ Context context,
+ DialerCallDelegate dialerCallDelegate,
+ Call telecomCall,
+ LatencyReport latencyReport,
+ boolean registerCallback) {
+ Assert.isNotNull(context);
+ mContext = context;
+ mDialerCallDelegate = dialerCallDelegate;
+ mTelecomCall = telecomCall;
+ mLatencyReport = latencyReport;
+ mId = ID_PREFIX + Integer.toString(sIdCounter++);
+
+ updateFromTelecomCall(registerCallback);
+
+ if (registerCallback) {
+ mTelecomCall.registerCallback(mTelecomCallCallback);
+ }
+
+ mTimeAddedMs = System.currentTimeMillis();
+ parseCallSpecificAppData();
+ }
+
+ private static int translateState(int state) {
+ switch (state) {
+ case Call.STATE_NEW:
+ case Call.STATE_CONNECTING:
+ return DialerCall.State.CONNECTING;
+ case Call.STATE_SELECT_PHONE_ACCOUNT:
+ return DialerCall.State.SELECT_PHONE_ACCOUNT;
+ case Call.STATE_DIALING:
+ return DialerCall.State.DIALING;
+ case Call.STATE_PULLING_CALL:
+ return DialerCall.State.PULLING;
+ case Call.STATE_RINGING:
+ return DialerCall.State.INCOMING;
+ case Call.STATE_ACTIVE:
+ return DialerCall.State.ACTIVE;
+ case Call.STATE_HOLDING:
+ return DialerCall.State.ONHOLD;
+ case Call.STATE_DISCONNECTED:
+ return DialerCall.State.DISCONNECTED;
+ case Call.STATE_DISCONNECTING:
+ return DialerCall.State.DISCONNECTING;
+ default:
+ return DialerCall.State.INVALID;
+ }
+ }
+
+ public static boolean areSame(DialerCall call1, DialerCall call2) {
+ if (call1 == null && call2 == null) {
+ return true;
+ } else if (call1 == null || call2 == null) {
+ return false;
+ }
+
+ // otherwise compare call Ids
+ return call1.getId().equals(call2.getId());
+ }
+
+ public static boolean areSameNumber(DialerCall call1, DialerCall call2) {
+ if (call1 == null && call2 == null) {
+ return true;
+ } else if (call1 == null || call2 == null) {
+ return false;
+ }
+
+ // otherwise compare call Numbers
+ return TextUtils.equals(call1.getNumber(), call2.getNumber());
+ }
+
+ public void addListener(DialerCallListener listener) {
+ Assert.isMainThread();
+ mListeners.add(listener);
+ }
+
+ public void removeListener(DialerCallListener listener) {
+ Assert.isMainThread();
+ mListeners.remove(listener);
+ }
+
+ public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
+ Assert.isMainThread();
+ mCannedTextResponsesLoadedListeners.add(listener);
+ }
+
+ public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
+ Assert.isMainThread();
+ mCannedTextResponsesLoadedListeners.remove(listener);
+ }
+
+ public void notifyWiFiToLteHandover() {
+ LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
+ for (DialerCallListener listener : mListeners) {
+ listener.onWiFiToLteHandover();
+ }
+ }
+
+ public void notifyHandoverToWifiFailed() {
+ LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
+ for (DialerCallListener listener : mListeners) {
+ listener.onHandoverToWifiFailure();
+ }
+ }
+
+ /* package-private */ Call getTelecomCall() {
+ return mTelecomCall;
+ }
+
+ public StatusHints getStatusHints() {
+ return mTelecomCall.getDetails().getStatusHints();
+ }
+
+ /**
+ * @return video settings of the call, null if the call is not a video call.
+ * @see VideoProfile
+ */
+ public VideoSettings getVideoSettings() {
+ return mVideoSettings;
+ }
+
+ private void update() {
+ Trace.beginSection("Update");
+ int oldState = getState();
+ // We want to potentially register a video call callback here.
+ updateFromTelecomCall(true /* registerCallback */);
+ if (oldState != getState() && getState() == DialerCall.State.DISCONNECTED) {
+ for (DialerCallListener listener : mListeners) {
+ listener.onDialerCallDisconnect();
+ }
+ } else {
+ for (DialerCallListener listener : mListeners) {
+ listener.onDialerCallUpdate();
+ }
+ }
+ Trace.endSection();
+ }
+
+ private void updateFromTelecomCall(boolean registerCallback) {
+ LogUtil.v("DialerCall.updateFromTelecomCall", mTelecomCall.toString());
+ final int translatedState = translateState(mTelecomCall.getState());
+ if (mState != State.BLOCKED) {
+ setState(translatedState);
+ setDisconnectCause(mTelecomCall.getDetails().getDisconnectCause());
+ maybeCancelVideoUpgrade(mTelecomCall.getDetails().getVideoState());
+ }
+
+ if (registerCallback && mTelecomCall.getVideoCall() != null) {
+ if (mVideoCallCallback == null) {
+ mVideoCallCallback = new InCallVideoCallCallback(this);
+ }
+ mTelecomCall.getVideoCall().registerCallback(mVideoCallCallback);
+ mIsVideoCallCallbackRegistered = true;
+ }
+
+ mChildCallIds.clear();
+ final int numChildCalls = mTelecomCall.getChildren().size();
+ for (int i = 0; i < numChildCalls; i++) {
+ mChildCallIds.add(
+ mDialerCallDelegate
+ .getDialerCallFromTelecomCall(mTelecomCall.getChildren().get(i))
+ .getId());
+ }
+
+ // The number of conferenced calls can change over the course of the call, so use the
+ // maximum number of conferenced child calls as the metric for conference call usage.
+ mLogState.conferencedCalls = Math.max(numChildCalls, mLogState.conferencedCalls);
+
+ updateFromCallExtras(mTelecomCall.getDetails().getExtras());
+
+ // If the handle of the call has changed, update state for the call determining if it is an
+ // emergency call.
+ Uri newHandle = mTelecomCall.getDetails().getHandle();
+ if (!Objects.equals(mHandle, newHandle)) {
+ mHandle = newHandle;
+ updateEmergencyCallState();
+ }
+
+ // If the phone account handle of the call is set, cache capability bit indicating whether
+ // the phone account supports call subjects.
+ PhoneAccountHandle newPhoneAccountHandle = mTelecomCall.getDetails().getAccountHandle();
+ if (!Objects.equals(mPhoneAccountHandle, newPhoneAccountHandle)) {
+ mPhoneAccountHandle = newPhoneAccountHandle;
+
+ if (mPhoneAccountHandle != null) {
+ PhoneAccount phoneAccount =
+ mContext.getSystemService(TelecomManager.class).getPhoneAccount(mPhoneAccountHandle);
+ if (phoneAccount != null) {
+ mIsCallSubjectSupported =
+ phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
+ }
+ }
+ }
+
+ if (mSessionModificationState
+ == DialerCall.SESSION_MODIFICATION_STATE_WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE
+ && isVideoCall()) {
+ // We find out in {@link InCallVideoCallCallback.onSessionModifyResponseReceived}
+ // whether the video upgrade request was accepted. We don't clear the session modification
+ // state right away though to avoid having the UI switch from video to voice to video.
+ // Once the underlying telecom call updates to video mode it's safe to clear the state.
+ LogUtil.i(
+ "DialerCall.updateFromTelecomCall",
+ "upgraded to video, clearing session modification state");
+ setSessionModificationState(DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ }
+ }
+
+ /**
+ * Tests corruption of the {@code callExtras} bundle by calling {@link
+ * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will
+ * be thrown and caught by this function.
+ *
+ * @param callExtras the bundle to verify
+ * @return {@code true} if the bundle is corrupted, {@code false} otherwise.
+ */
+ protected boolean areCallExtrasCorrupted(Bundle callExtras) {
+ /**
+ * There's currently a bug in Telephony service (b/25613098) that could corrupt the extras
+ * bundle, resulting in a IllegalArgumentException while validating data under {@link
+ * Bundle#containsKey(String)}.
+ */
+ try {
+ callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS);
+ return false;
+ } catch (IllegalArgumentException e) {
+ LogUtil.e(
+ "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e);
+ return true;
+ }
+ }
+
+ protected void updateFromCallExtras(Bundle callExtras) {
+ if (callExtras == null || areCallExtrasCorrupted(callExtras)) {
+ /**
+ * If the bundle is corrupted, abandon information update as a work around. These are not
+ * critical for the dialer to function.
+ */
+ return;
+ }
+ // Check for a change in the child address and notify any listeners.
+ if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
+ String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS);
+ if (!Objects.equals(childNumber, mChildNumber)) {
+ mChildNumber = childNumber;
+ for (DialerCallListener listener : mListeners) {
+ listener.onDialerCallChildNumberChange();
+ }
+ }
+ }
+
+ // Last forwarded number comes in as an array of strings. We want to choose the
+ // last item in the array. The forwarding numbers arrive independently of when the
+ // call is originally set up, so we need to notify the the UI of the change.
+ if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) {
+ ArrayList<String> lastForwardedNumbers =
+ callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER);
+
+ if (lastForwardedNumbers != null) {
+ String lastForwardedNumber = null;
+ if (!lastForwardedNumbers.isEmpty()) {
+ lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1);
+ }
+
+ if (!Objects.equals(lastForwardedNumber, mLastForwardedNumber)) {
+ mLastForwardedNumber = lastForwardedNumber;
+ for (DialerCallListener listener : mListeners) {
+ listener.onDialerCallLastForwardedNumberChange();
+ }
+ }
+ }
+ }
+
+ // DialerCall subject is present in the extras at the start of call, so we do not need to
+ // notify any other listeners of this.
+ if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) {
+ String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT);
+ if (!Objects.equals(mCallSubject, callSubject)) {
+ mCallSubject = callSubject;
+ }
+ }
+ }
+
+ /**
+ * Determines if a received upgrade to video request should be cancelled. This can happen if
+ * another InCall UI responds to the upgrade to video request.
+ *
+ * @param newVideoState The new video state.
+ */
+ private void maybeCancelVideoUpgrade(int newVideoState) {
+ boolean isVideoStateChanged = mVideoState != newVideoState;
+
+ if (mSessionModificationState
+ == DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST
+ && isVideoStateChanged) {
+
+ LogUtil.i("DialerCall.maybeCancelVideoUpgrade", "cancelling upgrade notification");
+ setSessionModificationState(DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ }
+ mVideoState = newVideoState;
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ public boolean hasShownWiFiToLteHandoverToast() {
+ return hasShownWiFiToLteHandoverToast;
+ }
+
+ public void setHasShownWiFiToLteHandoverToast() {
+ hasShownWiFiToLteHandoverToast = true;
+ }
+
+ public boolean showWifiHandoverAlertAsToast() {
+ return doNotShowDialogForHandoffToWifiFailure;
+ }
+
+ public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
+ doNotShowDialogForHandoffToWifiFailure = bool;
+ }
+
+ public long getTimeAddedMs() {
+ return mTimeAddedMs;
+ }
+
+ @Nullable
+ public String getNumber() {
+ return TelecomCallUtil.getNumber(mTelecomCall);
+ }
+
+ public void blockCall() {
+ mTelecomCall.reject(false, null);
+ setState(State.BLOCKED);
+ }
+
+ @Nullable
+ public Uri getHandle() {
+ return mTelecomCall == null ? null : mTelecomCall.getDetails().getHandle();
+ }
+
+ public boolean isEmergencyCall() {
+ return mIsEmergencyCall;
+ }
+
+ public boolean isPotentialEmergencyCallback() {
+ // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system
+ // is actually in emergency callback mode (ie data is disabled).
+ if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
+ return true;
+ }
+ // We want to treat any incoming call that arrives a short time after an outgoing emergency call
+ // as a potential emergency callback.
+ if (getExtras() != null
+ && getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0)
+ > 0) {
+ long lastEmergencyCallMillis =
+ getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0);
+ if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean isInEmergencyCallbackWindow(long timestampMillis) {
+ long emergencyCallbackWindowMillis =
+ ConfigProviderBindings.get(mContext)
+ .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5));
+ return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis;
+ }
+
+ public int getState() {
+ if (mTelecomCall != null && mTelecomCall.getParent() != null) {
+ return State.CONFERENCED;
+ } else {
+ return mState;
+ }
+ }
+
+ public void setState(int state) {
+ mState = state;
+ if (mState == State.INCOMING) {
+ mLogState.isIncoming = true;
+ } else if (mState == State.DISCONNECTED) {
+ mLogState.duration =
+ getConnectTimeMillis() == 0 ? 0 : System.currentTimeMillis() - getConnectTimeMillis();
+ }
+ }
+
+ public int getNumberPresentation() {
+ return mTelecomCall == null ? -1 : mTelecomCall.getDetails().getHandlePresentation();
+ }
+
+ public int getCnapNamePresentation() {
+ return mTelecomCall == null ? -1 : mTelecomCall.getDetails().getCallerDisplayNamePresentation();
+ }
+
+ @Nullable
+ public String getCnapName() {
+ return mTelecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
+ }
+
+ public Bundle getIntentExtras() {
+ return mTelecomCall.getDetails().getIntentExtras();
+ }
+
+ @Nullable
+ public Bundle getExtras() {
+ return mTelecomCall == null ? null : mTelecomCall.getDetails().getExtras();
+ }
+
+ /** @return The child number for the call, or {@code null} if none specified. */
+ public String getChildNumber() {
+ return mChildNumber;
+ }
+
+ /** @return The last forwarded number for the call, or {@code null} if none specified. */
+ public String getLastForwardedNumber() {
+ return mLastForwardedNumber;
+ }
+
+ /** @return The call subject, or {@code null} if none specified. */
+ public String getCallSubject() {
+ return mCallSubject;
+ }
+
+ /**
+ * @return {@code true} if the call's phone account supports call subjects, {@code false}
+ * otherwise.
+ */
+ public boolean isCallSubjectSupported() {
+ return mIsCallSubjectSupported;
+ }
+
+ /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
+ public DisconnectCause getDisconnectCause() {
+ if (mState == State.DISCONNECTED || mState == State.IDLE) {
+ return mDisconnectCause;
+ }
+
+ return new DisconnectCause(DisconnectCause.UNKNOWN);
+ }
+
+ public void setDisconnectCause(DisconnectCause disconnectCause) {
+ mDisconnectCause = disconnectCause;
+ mLogState.disconnectCause = mDisconnectCause;
+ }
+
+ /** Returns the possible text message responses. */
+ public List<String> getCannedSmsResponses() {
+ return mTelecomCall.getCannedTextResponses();
+ }
+
+ /** Checks if the call supports the given set of capabilities supplied as a bit mask. */
+ public boolean can(int capabilities) {
+ int supportedCapabilities = mTelecomCall.getDetails().getCallCapabilities();
+
+ if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
+ // We allow you to merge if the capabilities allow it or if it is a call with
+ // conferenceable calls.
+ if (mTelecomCall.getConferenceableCalls().isEmpty()
+ && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
+ // Cannot merge calls if there are no calls to merge with.
+ return false;
+ }
+ capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE;
+ }
+ return (capabilities == (capabilities & supportedCapabilities));
+ }
+
+ public boolean hasProperty(int property) {
+ return mTelecomCall.getDetails().hasProperty(property);
+ }
+
+ public String getUniqueCallId() {
+ return uniqueCallId;
+ }
+
+ /** Gets the time when the call first became active. */
+ public long getConnectTimeMillis() {
+ return mTelecomCall.getDetails().getConnectTimeMillis();
+ }
+
+ public boolean isConferenceCall() {
+ return hasProperty(Call.Details.PROPERTY_CONFERENCE);
+ }
+
+ @Nullable
+ public GatewayInfo getGatewayInfo() {
+ return mTelecomCall == null ? null : mTelecomCall.getDetails().getGatewayInfo();
+ }
+
+ @Nullable
+ public PhoneAccountHandle getAccountHandle() {
+ return mTelecomCall == null ? null : mTelecomCall.getDetails().getAccountHandle();
+ }
+
+ /**
+ * @return The {@link VideoCall} instance associated with the {@link Call}. Will return {@code
+ * null} until {@link #updateFromTelecomCall(boolean)} has registered a valid callback on the
+ * {@link VideoCall}.
+ */
+ public VideoCall getVideoCall() {
+ return mTelecomCall == null || !mIsVideoCallCallbackRegistered
+ ? null
+ : mTelecomCall.getVideoCall();
+ }
+
+ public List<String> getChildCallIds() {
+ return mChildCallIds;
+ }
+
+ public String getParentId() {
+ Call parentCall = mTelecomCall.getParent();
+ if (parentCall != null) {
+ return mDialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId();
+ }
+ return null;
+ }
+
+ public int getVideoState() {
+ return mTelecomCall.getDetails().getVideoState();
+ }
+
+ public boolean isVideoCall() {
+ return CallUtil.isVideoEnabled(mContext) && VideoUtils.isVideoCall(getVideoState());
+ }
+
+ /**
+ * Determines if the call handle is an emergency number or not and caches the result to avoid
+ * repeated calls to isEmergencyNumber.
+ */
+ private void updateEmergencyCallState() {
+ mIsEmergencyCall = TelecomCallUtil.isEmergencyCall(mTelecomCall);
+ }
+
+ /**
+ * Gets the video state which was requested via a session modification request.
+ *
+ * @return The video state.
+ */
+ public int getRequestedVideoState() {
+ return mRequestedVideoState;
+ }
+
+ /**
+ * Handles incoming session modification requests. Stores the pending video request and sets the
+ * session modification state to {@link
+ * DialerCall#SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST} so that we can keep
+ * track of the fact the request was received. Only upgrade requests require user confirmation and
+ * will be handled by this method. The remote user can turn off their own camera without
+ * confirmation.
+ *
+ * @param videoState The requested video state.
+ */
+ public void setRequestedVideoState(int videoState) {
+ LogUtil.v("DialerCall.setRequestedVideoState", "videoState: " + videoState);
+ if (videoState == getVideoState()) {
+ LogUtil.e("DialerCall.setRequestedVideoState", "clearing session modification state");
+ setSessionModificationState(DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ return;
+ }
+
+ mRequestedVideoState = videoState;
+ setSessionModificationState(
+ DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST);
+ for (DialerCallListener listener : mListeners) {
+ listener.onDialerCallUpgradeToVideo();
+ }
+
+ LogUtil.i(
+ "DialerCall.setRequestedVideoState",
+ "mSessionModificationState: %d, videoState: %d",
+ mSessionModificationState,
+ videoState);
+ update();
+ }
+
+ /**
+ * Gets the current video session modification state.
+ *
+ * @return The session modification state.
+ */
+ @SessionModificationState
+ public int getSessionModificationState() {
+ return mSessionModificationState;
+ }
+
+ /**
+ * Set the session modification state. Used to keep track of pending video session modification
+ * operations and to inform listeners of these changes.
+ *
+ * @param state the new session modification state.
+ */
+ public void setSessionModificationState(@SessionModificationState int state) {
+ boolean hasChanged = mSessionModificationState != state;
+ if (hasChanged) {
+ LogUtil.i(
+ "DialerCall.setSessionModificationState", "%d -> %d", mSessionModificationState, state);
+ mSessionModificationState = state;
+ for (DialerCallListener listener : mListeners) {
+ listener.onDialerCallSessionModificationStateChange(state);
+ }
+ }
+ }
+
+ public LogState getLogState() {
+ return mLogState;
+ }
+
+ /**
+ * Determines if the call is an external call.
+ *
+ * <p>An external call is one which does not exist locally for the {@link
+ * android.telecom.ConnectionService} it is associated with.
+ *
+ * <p>External calls are only supported in N and higher.
+ *
+ * @return {@code true} if the call is an external call, {@code false} otherwise.
+ */
+ public boolean isExternalCall() {
+ return VERSION.SDK_INT >= VERSION_CODES.N
+ && hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL);
+ }
+
+ /**
+ * Determines if the external call is pullable.
+ *
+ * <p>An external call is one which does not exist locally for the {@link
+ * android.telecom.ConnectionService} it is associated with. An external call may be "pullable",
+ * which means that the user can request it be transferred to the current device.
+ *
+ * <p>External calls are only supported in N and higher.
+ *
+ * @return {@code true} if the call is an external call, {@code false} otherwise.
+ */
+ public boolean isPullableExternalCall() {
+ return VERSION.SDK_INT >= VERSION_CODES.N
+ && (mTelecomCall.getDetails().getCallCapabilities()
+ & CallCompat.Details.CAPABILITY_CAN_PULL_CALL)
+ == CallCompat.Details.CAPABILITY_CAN_PULL_CALL;
+ }
+
+ /**
+ * Determines if answering this call will cause an ongoing video call to be dropped.
+ *
+ * @return {@code true} if answering this call will drop an ongoing video call, {@code false}
+ * otherwise.
+ */
+ public boolean answeringDisconnectsForegroundVideoCall() {
+ Bundle extras = getExtras();
+ if (extras == null
+ || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) {
+ return false;
+ }
+ return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL);
+ }
+
+ private void parseCallSpecificAppData() {
+ if (isExternalCall()) {
+ return;
+ }
+
+ mLogState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras());
+ if (mLogState.callSpecificAppData == null) {
+ mLogState.callSpecificAppData = new CallSpecificAppData();
+ mLogState.callSpecificAppData.callInitiationType =
+ CallInitiationType.Type.EXTERNAL_INITIATION;
+ }
+ if (getState() == State.INCOMING) {
+ mLogState.callSpecificAppData.callInitiationType =
+ CallInitiationType.Type.INCOMING_INITIATION;
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (mTelecomCall == null) {
+ // This should happen only in testing since otherwise we would never have a null
+ // Telecom call.
+ return String.valueOf(mId);
+ }
+
+ return String.format(
+ Locale.US,
+ "[%s, %s, %s, %s, children:%s, parent:%s, "
+ + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, VideoSettings:%s]",
+ mId,
+ State.toString(getState()),
+ Details.capabilitiesToString(mTelecomCall.getDetails().getCallCapabilities()),
+ Details.propertiesToString(mTelecomCall.getDetails().getCallProperties()),
+ mChildCallIds,
+ getParentId(),
+ this.mTelecomCall.getConferenceableCalls(),
+ VideoProfile.videoStateToString(mTelecomCall.getDetails().getVideoState()),
+ mSessionModificationState,
+ getVideoSettings());
+ }
+
+ public String toSimpleString() {
+ return super.toString();
+ }
+
+ @CallHistoryStatus
+ public int getCallHistoryStatus() {
+ return mCallHistoryStatus;
+ }
+
+ public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
+ mCallHistoryStatus = callHistoryStatus;
+ }
+
+ public boolean didShowCameraPermission() {
+ return didShowCameraPermission;
+ }
+
+ public void setDidShowCameraPermission(boolean didShow) {
+ didShowCameraPermission = didShow;
+ }
+
+ public boolean isInGlobalSpamList() {
+ return isInGlobalSpamList;
+ }
+
+ public void setIsInGlobalSpamList(boolean inSpamList) {
+ isInGlobalSpamList = inSpamList;
+ }
+
+ public boolean isInUserSpamList() {
+ return isInUserSpamList;
+ }
+
+ public void setIsInUserSpamList(boolean inSpamList) {
+ isInUserSpamList = inSpamList;
+ }
+
+ public boolean isInUserWhiteList() {
+ return isInUserWhiteList;
+ }
+
+ public void setIsInUserWhiteList(boolean inWhiteList) {
+ isInUserWhiteList = inWhiteList;
+ }
+
+ public boolean isSpam() {
+ return mIsSpam;
+ }
+
+ public void setSpam(boolean isSpam) {
+ mIsSpam = isSpam;
+ }
+
+ public boolean isBlocked() {
+ return mIsBlocked;
+ }
+
+ public void setBlockedStatus(boolean isBlocked) {
+ mIsBlocked = isBlocked;
+ }
+
+ public boolean isRemotelyHeld() {
+ return isRemotelyHeld;
+ }
+
+ public boolean isIncoming() {
+ return mLogState.isIncoming;
+ }
+
+ public LatencyReport getLatencyReport() {
+ return mLatencyReport;
+ }
+
+ public void unregisterCallback() {
+ mTelecomCall.unregisterCallback(mTelecomCallCallback);
+ }
+
+ public void acceptUpgradeRequest(int videoState) {
+ LogUtil.i("DialerCall.acceptUpgradeRequest", "videoState: " + videoState);
+ VideoProfile videoProfile = new VideoProfile(videoState);
+ getVideoCall().sendSessionModifyResponse(videoProfile);
+ setSessionModificationState(DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ }
+
+ public void declineUpgradeRequest() {
+ LogUtil.i("DialerCall.declineUpgradeRequest", "");
+ VideoProfile videoProfile = new VideoProfile(getVideoState());
+ getVideoCall().sendSessionModifyResponse(videoProfile);
+ setSessionModificationState(DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ }
+
+ public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
+ LogUtil.i(
+ "DialerCall.phoneAccountSelected",
+ "accountHandle: %s, setDefault: %b",
+ accountHandle,
+ setDefault);
+ mTelecomCall.phoneAccountSelected(accountHandle, setDefault);
+ }
+
+ public void disconnect() {
+ LogUtil.i("DialerCall.disconnect", "");
+ setState(DialerCall.State.DISCONNECTING);
+ for (DialerCallListener listener : mListeners) {
+ listener.onDialerCallUpdate();
+ }
+ mTelecomCall.disconnect();
+ }
+
+ public void hold() {
+ LogUtil.i("DialerCall.hold", "");
+ mTelecomCall.hold();
+ }
+
+ public void unhold() {
+ LogUtil.i("DialerCall.unhold", "");
+ mTelecomCall.unhold();
+ }
+
+ public void splitFromConference() {
+ LogUtil.i("DialerCall.splitFromConference", "");
+ mTelecomCall.splitFromConference();
+ }
+
+ public void answer(int videoState) {
+ LogUtil.i("DialerCall.answer", "videoState: " + videoState);
+ mTelecomCall.answer(videoState);
+ }
+
+ public void reject(boolean rejectWithMessage, String message) {
+ LogUtil.i("DialerCall.reject", "");
+ mTelecomCall.reject(rejectWithMessage, message);
+ }
+
+ /** Return the string label to represent the call provider */
+ public String getCallProviderLabel() {
+ if (callProviderLabel == null) {
+ PhoneAccount account = getPhoneAccount();
+ if (account != null && !TextUtils.isEmpty(account.getLabel())) {
+ List<PhoneAccountHandle> accounts =
+ mContext.getSystemService(TelecomManager.class).getCallCapablePhoneAccounts();
+ if (accounts != null && accounts.size() > 1) {
+ callProviderLabel = account.getLabel().toString();
+ }
+ }
+ if (callProviderLabel == null) {
+ callProviderLabel = "";
+ }
+ }
+ return callProviderLabel;
+ }
+
+ private PhoneAccount getPhoneAccount() {
+ PhoneAccountHandle accountHandle = getAccountHandle();
+ if (accountHandle == null) {
+ return null;
+ }
+ return mContext.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
+ }
+
+ public String getCallbackNumber() {
+ if (callbackNumber == null) {
+ // Show the emergency callback number if either:
+ // 1. This is an emergency call.
+ // 2. The phone is in Emergency Callback Mode, which means we should show the callback
+ // number.
+ boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
+
+ if (isEmergencyCall() || showCallbackNumber) {
+ callbackNumber = getSubscriptionNumber();
+ } else {
+ StatusHints statusHints = getTelecomCall().getDetails().getStatusHints();
+ if (statusHints != null) {
+ Bundle extras = statusHints.getExtras();
+ if (extras != null) {
+ callbackNumber = extras.getString(TelecomManager.EXTRA_CALL_BACK_NUMBER);
+ }
+ }
+ }
+
+ String simNumber =
+ mContext.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle());
+ if (!showCallbackNumber && PhoneNumberUtils.compare(callbackNumber, simNumber)) {
+ LogUtil.v(
+ "DialerCall.getCallbackNumber",
+ "numbers are the same (and callback number is not being forced to show);"
+ + " not showing the callback number");
+ callbackNumber = "";
+ }
+ if (callbackNumber == null) {
+ callbackNumber = "";
+ }
+ }
+ return callbackNumber;
+ }
+
+ private String getSubscriptionNumber() {
+ // If it's an emergency call, and they're not populating the callback number,
+ // then try to fall back to the phone sub info (to hopefully get the SIM's
+ // number directly from the telephony layer).
+ PhoneAccountHandle accountHandle = getAccountHandle();
+ if (accountHandle != null) {
+ PhoneAccount account =
+ mContext.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle);
+ if (account != null) {
+ return getNumberFromHandle(account.getSubscriptionAddress());
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN}
+ * means there is no result.
+ */
+ @IntDef({
+ CALL_HISTORY_STATUS_UNKNOWN,
+ CALL_HISTORY_STATUS_PRESENT,
+ CALL_HISTORY_STATUS_NOT_PRESENT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallHistoryStatus {}
+
+ /* Defines different states of this call */
+ public static class State {
+
+ public static final int INVALID = 0;
+ public static final int NEW = 1; /* The call is new. */
+ public static final int IDLE = 2; /* The call is idle. Nothing active */
+ public static final int ACTIVE = 3; /* There is an active call */
+ public static final int INCOMING = 4; /* A normal incoming phone call */
+ public static final int CALL_WAITING = 5; /* Incoming call while another is active */
+ public static final int DIALING = 6; /* An outgoing call during dial phase */
+ public static final int REDIALING = 7; /* Subsequent dialing attempt after a failure */
+ public static final int ONHOLD = 8; /* An active phone call placed on hold */
+ public static final int DISCONNECTING = 9; /* A call is being ended. */
+ public static final int DISCONNECTED = 10; /* State after a call disconnects */
+ public static final int CONFERENCED = 11; /* DialerCall part of a conference call */
+ public static final int SELECT_PHONE_ACCOUNT = 12; /* Waiting for account selection */
+ public static final int CONNECTING = 13; /* Waiting for Telecom broadcast to finish */
+ public static final int BLOCKED = 14; /* The number was found on the block list */
+ public static final int PULLING = 15; /* An external call being pulled to the device */
+
+ public static boolean isConnectingOrConnected(int state) {
+ switch (state) {
+ case ACTIVE:
+ case INCOMING:
+ case CALL_WAITING:
+ case CONNECTING:
+ case DIALING:
+ case PULLING:
+ case REDIALING:
+ case ONHOLD:
+ case CONFERENCED:
+ return true;
+ default:
+ }
+ return false;
+ }
+
+ public static boolean isDialing(int state) {
+ return state == DIALING || state == PULLING || state == REDIALING;
+ }
+
+ public static String toString(int state) {
+ switch (state) {
+ case INVALID:
+ return "INVALID";
+ case NEW:
+ return "NEW";
+ case IDLE:
+ return "IDLE";
+ case ACTIVE:
+ return "ACTIVE";
+ case INCOMING:
+ return "INCOMING";
+ case CALL_WAITING:
+ return "CALL_WAITING";
+ case DIALING:
+ return "DIALING";
+ case PULLING:
+ return "PULLING";
+ case REDIALING:
+ return "REDIALING";
+ case ONHOLD:
+ return "ONHOLD";
+ case DISCONNECTING:
+ return "DISCONNECTING";
+ case DISCONNECTED:
+ return "DISCONNECTED";
+ case CONFERENCED:
+ return "CONFERENCED";
+ case SELECT_PHONE_ACCOUNT:
+ return "SELECT_PHONE_ACCOUNT";
+ case CONNECTING:
+ return "CONNECTING";
+ case BLOCKED:
+ return "BLOCKED";
+ default:
+ return "UNKNOWN";
+ }
+ }
+ }
+
+ /**
+ * Defines different states of session modify requests, which are used to upgrade to video, or
+ * downgrade to audio.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ SESSION_MODIFICATION_STATE_NO_REQUEST,
+ SESSION_MODIFICATION_STATE_WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE,
+ SESSION_MODIFICATION_STATE_REQUEST_FAILED,
+ SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST,
+ SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT,
+ SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_FAILED,
+ SESSION_MODIFICATION_STATE_REQUEST_REJECTED,
+ SESSION_MODIFICATION_STATE_WAITING_FOR_RESPONSE
+ })
+ public @interface SessionModificationState {}
+
+ public static final int SESSION_MODIFICATION_STATE_NO_REQUEST = 0;
+ public static final int SESSION_MODIFICATION_STATE_WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE = 1;
+ public static final int SESSION_MODIFICATION_STATE_REQUEST_FAILED = 2;
+ public static final int SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST = 3;
+ public static final int SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT = 4;
+ public static final int SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_FAILED = 5;
+ public static final int SESSION_MODIFICATION_STATE_REQUEST_REJECTED = 6;
+ public static final int SESSION_MODIFICATION_STATE_WAITING_FOR_RESPONSE = 7;
+
+ public static class VideoSettings {
+
+ public static final int CAMERA_DIRECTION_UNKNOWN = -1;
+ public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT;
+ public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK;
+
+ private int mCameraDirection = CAMERA_DIRECTION_UNKNOWN;
+
+ /**
+ * Gets the camera direction. if camera direction is set to CAMERA_DIRECTION_UNKNOWN, the video
+ * state of the call should be used to infer the camera direction.
+ *
+ * @see {@link CameraCharacteristics#LENS_FACING_FRONT}
+ * @see {@link CameraCharacteristics#LENS_FACING_BACK}
+ */
+ public int getCameraDir() {
+ return mCameraDirection;
+ }
+
+ /**
+ * Sets the camera direction. if camera direction is set to CAMERA_DIRECTION_UNKNOWN, the video
+ * state of the call should be used to infer the camera direction.
+ *
+ * @see {@link CameraCharacteristics#LENS_FACING_FRONT}
+ * @see {@link CameraCharacteristics#LENS_FACING_BACK}
+ */
+ public void setCameraDir(int cameraDirection) {
+ if (cameraDirection == CAMERA_DIRECTION_FRONT_FACING
+ || cameraDirection == CAMERA_DIRECTION_BACK_FACING) {
+ mCameraDirection = cameraDirection;
+ } else {
+ mCameraDirection = CAMERA_DIRECTION_UNKNOWN;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "(CameraDir:" + getCameraDir() + ")";
+ }
+ }
+
+ /**
+ * Tracks any state variables that is useful for logging. There is some amount of overlap with
+ * existing call member variables, but this duplication helps to ensure that none of these logging
+ * variables will interface with/and affect call logic.
+ */
+ public static class LogState {
+
+ public DisconnectCause disconnectCause;
+ public boolean isIncoming = false;
+ public int contactLookupResult = ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE;
+ public CallSpecificAppData callSpecificAppData;
+ // If this was a conference call, the total number of calls involved in the conference.
+ public int conferencedCalls = 0;
+ public long duration = 0;
+ public boolean isLogged = false;
+
+ private static String lookupToString(int lookupType) {
+ switch (lookupType) {
+ case ContactLookupResult.Type.LOCAL_CONTACT:
+ return "Local";
+ case ContactLookupResult.Type.LOCAL_CACHE:
+ return "Cache";
+ case ContactLookupResult.Type.REMOTE:
+ return "Remote";
+ case ContactLookupResult.Type.EMERGENCY:
+ return "Emergency";
+ case ContactLookupResult.Type.VOICEMAIL:
+ return "Voicemail";
+ default:
+ return "Not found";
+ }
+ }
+
+ private static String initiationToString(CallSpecificAppData callSpecificAppData) {
+ if (callSpecificAppData == null) {
+ return "null";
+ }
+ switch (callSpecificAppData.callInitiationType) {
+ case CallInitiationType.Type.INCOMING_INITIATION:
+ return "Incoming";
+ case CallInitiationType.Type.DIALPAD:
+ return "Dialpad";
+ case CallInitiationType.Type.SPEED_DIAL:
+ return "Speed Dial";
+ case CallInitiationType.Type.REMOTE_DIRECTORY:
+ return "Remote Directory";
+ case CallInitiationType.Type.SMART_DIAL:
+ return "Smart Dial";
+ case CallInitiationType.Type.REGULAR_SEARCH:
+ return "Regular Search";
+ case CallInitiationType.Type.CALL_LOG:
+ return "DialerCall Log";
+ case CallInitiationType.Type.CALL_LOG_FILTER:
+ return "DialerCall Log Filter";
+ case CallInitiationType.Type.VOICEMAIL_LOG:
+ return "Voicemail Log";
+ case CallInitiationType.Type.CALL_DETAILS:
+ return "DialerCall Details";
+ case CallInitiationType.Type.QUICK_CONTACTS:
+ return "Quick Contacts";
+ case CallInitiationType.Type.EXTERNAL_INITIATION:
+ return "External";
+ case CallInitiationType.Type.LAUNCHER_SHORTCUT:
+ return "Launcher Shortcut";
+ default:
+ return "Unknown: " + callSpecificAppData.callInitiationType;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ Locale.US,
+ "["
+ + "%s, " // DisconnectCause toString already describes the object type
+ + "isIncoming: %s, "
+ + "contactLookup: %s, "
+ + "callInitiation: %s, "
+ + "duration: %s"
+ + "]",
+ disconnectCause,
+ isIncoming,
+ lookupToString(contactLookupResult),
+ initiationToString(callSpecificAppData),
+ duration);
+ }
+ }
+
+ /** Called when canned text responses have been loaded. */
+ public interface CannedTextResponsesLoadedListener {
+ void onCannedTextResponsesLoaded(DialerCall call);
+ }
+}
diff --git a/java/com/android/incallui/call/DialerCallDelegate.java b/java/com/android/incallui/call/DialerCallDelegate.java
new file mode 100644
index 000000000..463b4916a
--- /dev/null
+++ b/java/com/android/incallui/call/DialerCallDelegate.java
@@ -0,0 +1,25 @@
+/*
+ * 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.incallui.call;
+
+import android.telecom.Call;
+
+/** Callback from the call module to the container. */
+public interface DialerCallDelegate {
+
+ DialerCall getDialerCallFromTelecomCall(Call telecomCall);
+}
diff --git a/java/com/android/incallui/call/DialerCallListener.java b/java/com/android/incallui/call/DialerCallListener.java
new file mode 100644
index 000000000..b426cd72e
--- /dev/null
+++ b/java/com/android/incallui/call/DialerCallListener.java
@@ -0,0 +1,39 @@
+/*
+ * 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.incallui.call;
+
+import com.android.incallui.call.DialerCall.SessionModificationState;
+
+/** Used to monitor state changes in a dialer call. */
+public interface DialerCallListener {
+
+ void onDialerCallDisconnect();
+
+ void onDialerCallUpdate();
+
+ void onDialerCallChildNumberChange();
+
+ void onDialerCallLastForwardedNumberChange();
+
+ void onDialerCallUpgradeToVideo();
+
+ void onDialerCallSessionModificationStateChange(@SessionModificationState int state);
+
+ void onWiFiToLteHandover();
+
+ void onHandoverToWifiFailure();
+}
diff --git a/java/com/android/incallui/call/ExternalCallList.java b/java/com/android/incallui/call/ExternalCallList.java
new file mode 100644
index 000000000..52a7a304b
--- /dev/null
+++ b/java/com/android/incallui/call/ExternalCallList.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 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.incallui.call;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.NonNull;
+import android.telecom.Call;
+import android.util.ArraySet;
+import com.android.contacts.common.compat.CallCompat;
+import com.android.dialer.common.LogUtil;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Tracks the external calls known to the InCall UI.
+ *
+ * <p>External calls are those with {@code android.telecom.Call.Details#PROPERTY_IS_EXTERNAL_CALL}.
+ */
+public class ExternalCallList {
+
+ private final Set<Call> mExternalCalls = new ArraySet<>();
+ private final Set<ExternalCallListener> mExternalCallListeners =
+ Collections.newSetFromMap(new ConcurrentHashMap<ExternalCallListener, Boolean>(8, 0.9f, 1));
+ /** Handles {@link android.telecom.Call.Callback} callbacks. */
+ private final Call.Callback mTelecomCallCallback =
+ new Call.Callback() {
+ @Override
+ public void onDetailsChanged(Call call, Call.Details details) {
+ notifyExternalCallUpdated(call);
+ }
+ };
+
+ /** Begins tracking an external call and notifies listeners of the new call. */
+ public void onCallAdded(Call telecomCall) {
+ if (!telecomCall.getDetails().hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) {
+ throw new IllegalArgumentException();
+ }
+ mExternalCalls.add(telecomCall);
+ telecomCall.registerCallback(mTelecomCallCallback, new Handler(Looper.getMainLooper()));
+ notifyExternalCallAdded(telecomCall);
+ }
+
+ /** Stops tracking an external call and notifies listeners of the removal of the call. */
+ public void onCallRemoved(Call telecomCall) {
+ if (!mExternalCalls.contains(telecomCall)) {
+ // This can happen on M for external calls from blocked numbers
+ LogUtil.i("ExternalCallList.onCallRemoved", "attempted to remove unregistered call");
+ return;
+ }
+ mExternalCalls.remove(telecomCall);
+ telecomCall.unregisterCallback(mTelecomCallCallback);
+ notifyExternalCallRemoved(telecomCall);
+ }
+
+ /** Adds a new listener to external call events. */
+ public void addExternalCallListener(@NonNull ExternalCallListener listener) {
+ mExternalCallListeners.add(listener);
+ }
+
+ /** Removes a listener to external call events. */
+ public void removeExternalCallListener(@NonNull ExternalCallListener listener) {
+ if (!mExternalCallListeners.contains(listener)) {
+ LogUtil.i(
+ "ExternalCallList.removeExternalCallListener",
+ "attempt to remove unregistered listener.");
+ }
+ mExternalCallListeners.remove(listener);
+ }
+
+ public boolean isCallTracked(@NonNull android.telecom.Call telecomCall) {
+ return mExternalCalls.contains(telecomCall);
+ }
+
+ /** Notifies listeners of the addition of a new external call. */
+ private void notifyExternalCallAdded(Call call) {
+ for (ExternalCallListener listener : mExternalCallListeners) {
+ listener.onExternalCallAdded(call);
+ }
+ }
+
+ /** Notifies listeners of the removal of an external call. */
+ private void notifyExternalCallRemoved(Call call) {
+ for (ExternalCallListener listener : mExternalCallListeners) {
+ listener.onExternalCallRemoved(call);
+ }
+ }
+
+ /** Notifies listeners of changes to an external call. */
+ private void notifyExternalCallUpdated(Call call) {
+ if (!call.getDetails().hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) {
+ // A previous external call has been pulled and is now a regular call, so we will remove
+ // it from the external call listener and ensure that the CallList is informed of the
+ // change.
+ onCallRemoved(call);
+
+ for (ExternalCallListener listener : mExternalCallListeners) {
+ listener.onExternalCallPulled(call);
+ }
+ } else {
+ for (ExternalCallListener listener : mExternalCallListeners) {
+ listener.onExternalCallUpdated(call);
+ }
+ }
+ }
+
+ /**
+ * Defines events which the {@link ExternalCallList} exposes to interested components (e.g. {@link
+ * com.android.incallui.ExternalCallNotifier ExternalCallNotifier}).
+ */
+ public interface ExternalCallListener {
+
+ void onExternalCallAdded(Call call);
+
+ void onExternalCallRemoved(Call call);
+
+ void onExternalCallUpdated(Call call);
+
+ void onExternalCallPulled(Call call);
+ }
+}
diff --git a/java/com/android/incallui/call/InCallServiceListener.java b/java/com/android/incallui/call/InCallServiceListener.java
new file mode 100644
index 000000000..e48ce9d79
--- /dev/null
+++ b/java/com/android/incallui/call/InCallServiceListener.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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.incallui.call;
+
+import android.telecom.InCallService;
+
+/**
+ * Interface implemented by In-Call components that maintain a reference to the Telecom API {@code
+ * InCallService} object. Clarifies the expectations associated with the relevant method calls.
+ */
+public interface InCallServiceListener {
+
+ /**
+ * Called once at {@code InCallService} startup time with a valid instance. At that time, there
+ * will be no existing {@code DialerCall}s.
+ *
+ * @param inCallService The {@code InCallService} object.
+ */
+ void setInCallService(InCallService inCallService);
+
+ /**
+ * Called once at {@code InCallService} shutdown time. At that time, any {@code DialerCall}s will
+ * have transitioned through the disconnected state and will no longer exist.
+ */
+ void clearInCallService();
+}
diff --git a/java/com/android/incallui/call/InCallUiLegacyBindings.java b/java/com/android/incallui/call/InCallUiLegacyBindings.java
new file mode 100644
index 000000000..1b0ed4542
--- /dev/null
+++ b/java/com/android/incallui/call/InCallUiLegacyBindings.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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.incallui.call;
+
+/**
+ * These are old bindings between InCallUi and the container application. All new bindings should be
+ * added to the bindings module and not here.
+ */
+public interface InCallUiLegacyBindings {
+
+ void logCall(DialerCall call);
+}
diff --git a/java/com/android/incallui/call/InCallUiLegacyBindingsFactory.java b/java/com/android/incallui/call/InCallUiLegacyBindingsFactory.java
new file mode 100644
index 000000000..8604976f7
--- /dev/null
+++ b/java/com/android/incallui/call/InCallUiLegacyBindingsFactory.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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.incallui.call;
+
+/**
+ * This interface should be implementated by the Application subclass. It allows the in call UI
+ * module to get references to the InCallUiLegacyBindings.
+ */
+public interface InCallUiLegacyBindingsFactory {
+
+ InCallUiLegacyBindings newInCallUiLegacyBindings();
+}
diff --git a/java/com/android/incallui/call/InCallUiLegacyBindingsStub.java b/java/com/android/incallui/call/InCallUiLegacyBindingsStub.java
new file mode 100644
index 000000000..8869c64b2
--- /dev/null
+++ b/java/com/android/incallui/call/InCallUiLegacyBindingsStub.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2017 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.incallui.call;
+
+/** Default implementation for in call UI legacy bindings. */
+public class InCallUiLegacyBindingsStub implements InCallUiLegacyBindings {
+
+ @Override
+ public void logCall(DialerCall call) {}
+}
diff --git a/java/com/android/incallui/call/InCallVideoCallCallback.java b/java/com/android/incallui/call/InCallVideoCallCallback.java
new file mode 100644
index 000000000..f897ac9dd
--- /dev/null
+++ b/java/com/android/incallui/call/InCallVideoCallCallback.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2014 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.incallui.call;
+
+import android.os.Handler;
+import android.support.annotation.Nullable;
+import android.telecom.Connection;
+import android.telecom.Connection.VideoProvider;
+import android.telecom.InCallService.VideoCall;
+import android.telecom.VideoProfile;
+import android.telecom.VideoProfile.CameraCapabilities;
+import com.android.dialer.common.LogUtil;
+import com.android.incallui.call.DialerCall.SessionModificationState;
+
+/** Implements the InCallUI VideoCall Callback. */
+public class InCallVideoCallCallback extends VideoCall.Callback implements Runnable {
+
+ private static final int CLEAR_FAILED_REQUEST_TIMEOUT_MILLIS = 4000;
+
+ private final DialerCall call;
+ @Nullable private Handler handler;
+ @SessionModificationState private int newSessionModificationState;
+
+ public InCallVideoCallCallback(DialerCall call) {
+ this.call = call;
+ }
+
+ @Override
+ public void onSessionModifyRequestReceived(VideoProfile videoProfile) {
+ LogUtil.i(
+ "InCallVideoCallCallback.onSessionModifyRequestReceived", "videoProfile: " + videoProfile);
+ int previousVideoState = VideoUtils.getUnPausedVideoState(call.getVideoState());
+ int newVideoState = VideoUtils.getUnPausedVideoState(videoProfile.getVideoState());
+
+ boolean wasVideoCall = VideoUtils.isVideoCall(previousVideoState);
+ boolean isVideoCall = VideoUtils.isVideoCall(newVideoState);
+
+ if (wasVideoCall && !isVideoCall) {
+ LogUtil.v(
+ "InCallVideoCallCallback.onSessionModifyRequestReceived",
+ "call downgraded to " + newVideoState);
+ } else if (previousVideoState != newVideoState) {
+ InCallVideoCallCallbackNotifier.getInstance().upgradeToVideoRequest(call, newVideoState);
+ }
+ }
+
+ /**
+ * @param status Status of the session modify request. Valid values are {@link
+ * Connection.VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS}, {@link
+ * Connection.VideoProvider#SESSION_MODIFY_REQUEST_FAIL}, {@link
+ * Connection.VideoProvider#SESSION_MODIFY_REQUEST_INVALID}
+ * @param responseProfile The actual profile changes made by the peer device.
+ */
+ @Override
+ public void onSessionModifyResponseReceived(
+ int status, VideoProfile requestedProfile, VideoProfile responseProfile) {
+ LogUtil.i(
+ "InCallVideoCallCallback.onSessionModifyResponseReceived",
+ "status: %d, "
+ + "requestedProfile: %s, responseProfile: %s, current session modification state: %d",
+ status,
+ requestedProfile,
+ responseProfile,
+ call.getSessionModificationState());
+
+ if (call.getSessionModificationState()
+ == DialerCall.SESSION_MODIFICATION_STATE_WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE) {
+ if (handler == null) {
+ handler = new Handler();
+ } else {
+ handler.removeCallbacks(this);
+ }
+
+ newSessionModificationState = getDialerSessionModifyStateTelecomStatus(status);
+ if (status != VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) {
+ // This will update the video UI to display the error message.
+ call.setSessionModificationState(newSessionModificationState);
+ }
+
+ // Wait for 4 seconds and then clean the session modification state. This allows the video UI
+ // to stay up so that the user can read the error message.
+ //
+ // If the other person accepted the upgrade request then this will keep the video UI up until
+ // the call's video state change. Without this we would switch to the voice call and then
+ // switch back to video UI.
+ handler.postDelayed(this, CLEAR_FAILED_REQUEST_TIMEOUT_MILLIS);
+ } else if (call.getSessionModificationState()
+ == DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
+ call.setSessionModificationState(DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ } else if (call.getSessionModificationState()
+ == DialerCall.SESSION_MODIFICATION_STATE_WAITING_FOR_RESPONSE) {
+ call.setSessionModificationState(getDialerSessionModifyStateTelecomStatus(status));
+ } else {
+ LogUtil.i(
+ "InCallVideoCallCallback.onSessionModifyResponseReceived",
+ "call is not waiting for " + "response, doing nothing");
+ }
+ }
+
+ @SessionModificationState
+ private int getDialerSessionModifyStateTelecomStatus(int telecomStatus) {
+ switch (telecomStatus) {
+ case VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS:
+ return DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST;
+ case VideoProvider.SESSION_MODIFY_REQUEST_FAIL:
+ case VideoProvider.SESSION_MODIFY_REQUEST_INVALID:
+ // Check if it's already video call, which means the request is not video upgrade request.
+ if (VideoUtils.isVideoCall(call.getVideoState())) {
+ return DialerCall.SESSION_MODIFICATION_STATE_REQUEST_FAILED;
+ } else {
+ return DialerCall.SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_FAILED;
+ }
+ case VideoProvider.SESSION_MODIFY_REQUEST_TIMED_OUT:
+ return DialerCall.SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT;
+ case VideoProvider.SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE:
+ return DialerCall.SESSION_MODIFICATION_STATE_REQUEST_REJECTED;
+ default:
+ LogUtil.e(
+ "InCallVideoCallCallback.getDialerSessionModifyStateTelecomStatus",
+ "unknown status: %d",
+ telecomStatus);
+ return DialerCall.SESSION_MODIFICATION_STATE_REQUEST_FAILED;
+ }
+ }
+
+ @Override
+ public void onCallSessionEvent(int event) {
+ InCallVideoCallCallbackNotifier.getInstance().callSessionEvent(event);
+ }
+
+ @Override
+ public void onPeerDimensionsChanged(int width, int height) {
+ InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(call, width, height);
+ }
+
+ @Override
+ public void onVideoQualityChanged(int videoQuality) {
+ InCallVideoCallCallbackNotifier.getInstance().videoQualityChanged(call, videoQuality);
+ }
+
+ /**
+ * Handles a change to the call data usage. No implementation as the in-call UI does not display
+ * data usage.
+ *
+ * @param dataUsage The updated data usage.
+ */
+ @Override
+ public void onCallDataUsageChanged(long dataUsage) {
+ LogUtil.v("InCallVideoCallCallback.onCallDataUsageChanged", "dataUsage = " + dataUsage);
+ InCallVideoCallCallbackNotifier.getInstance().callDataUsageChanged(dataUsage);
+ }
+
+ /**
+ * Handles changes to the camera capabilities. No implementation as the in-call UI does not make
+ * use of camera capabilities.
+ *
+ * @param cameraCapabilities The changed camera capabilities.
+ */
+ @Override
+ public void onCameraCapabilitiesChanged(CameraCapabilities cameraCapabilities) {
+ if (cameraCapabilities != null) {
+ InCallVideoCallCallbackNotifier.getInstance()
+ .cameraDimensionsChanged(
+ call, cameraCapabilities.getWidth(), cameraCapabilities.getHeight());
+ }
+ }
+
+ /**
+ * Called 4 seconds after the remote user responds to the video upgrade request. We use this to
+ * clear the session modify state.
+ */
+ @Override
+ public void run() {
+ if (call.getSessionModificationState() == newSessionModificationState) {
+ LogUtil.i("InCallVideoCallCallback.onSessionModifyResponseReceived", "clearing state");
+ call.setSessionModificationState(DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ } else {
+ LogUtil.i(
+ "InCallVideoCallCallback.onSessionModifyResponseReceived",
+ "session modification state has changed, not clearing state");
+ }
+ }
+}
diff --git a/java/com/android/incallui/call/InCallVideoCallCallbackNotifier.java b/java/com/android/incallui/call/InCallVideoCallCallbackNotifier.java
new file mode 100644
index 000000000..4a949263c
--- /dev/null
+++ b/java/com/android/incallui/call/InCallVideoCallCallbackNotifier.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2014 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.incallui.call;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import com.android.dialer.common.LogUtil;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Class used by {@link InCallService.VideoCallCallback} to notify interested parties of incoming
+ * events.
+ */
+public class InCallVideoCallCallbackNotifier {
+
+ /** Singleton instance of this class. */
+ private static InCallVideoCallCallbackNotifier sInstance = new InCallVideoCallCallbackNotifier();
+
+ /**
+ * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is load factor before
+ * resizing, 1 means we only expect a single thread to access the map so make only a single shard
+ */
+ private final Set<SessionModificationListener> mSessionModificationListeners =
+ Collections.newSetFromMap(
+ new ConcurrentHashMap<SessionModificationListener, Boolean>(8, 0.9f, 1));
+
+ private final Set<VideoEventListener> mVideoEventListeners =
+ Collections.newSetFromMap(new ConcurrentHashMap<VideoEventListener, Boolean>(8, 0.9f, 1));
+ private final Set<SurfaceChangeListener> mSurfaceChangeListeners =
+ Collections.newSetFromMap(new ConcurrentHashMap<SurfaceChangeListener, Boolean>(8, 0.9f, 1));
+
+ /** Private constructor. Instance should only be acquired through getInstance(). */
+ private InCallVideoCallCallbackNotifier() {}
+
+ /** Static singleton accessor method. */
+ public static InCallVideoCallCallbackNotifier getInstance() {
+ return sInstance;
+ }
+
+ /**
+ * Adds a new {@link SessionModificationListener}.
+ *
+ * @param listener The listener.
+ */
+ public void addSessionModificationListener(@NonNull SessionModificationListener listener) {
+ Objects.requireNonNull(listener);
+ mSessionModificationListeners.add(listener);
+ }
+
+ /**
+ * Remove a {@link SessionModificationListener}.
+ *
+ * @param listener The listener.
+ */
+ public void removeSessionModificationListener(@Nullable SessionModificationListener listener) {
+ if (listener != null) {
+ mSessionModificationListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Adds a new {@link VideoEventListener}.
+ *
+ * @param listener The listener.
+ */
+ public void addVideoEventListener(@NonNull VideoEventListener listener) {
+ Objects.requireNonNull(listener);
+ mVideoEventListeners.add(listener);
+ }
+
+ /**
+ * Remove a {@link VideoEventListener}.
+ *
+ * @param listener The listener.
+ */
+ public void removeVideoEventListener(@Nullable VideoEventListener listener) {
+ if (listener != null) {
+ mVideoEventListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Adds a new {@link SurfaceChangeListener}.
+ *
+ * @param listener The listener.
+ */
+ public void addSurfaceChangeListener(@NonNull SurfaceChangeListener listener) {
+ Objects.requireNonNull(listener);
+ mSurfaceChangeListeners.add(listener);
+ }
+
+ /**
+ * Remove a {@link SurfaceChangeListener}.
+ *
+ * @param listener The listener.
+ */
+ public void removeSurfaceChangeListener(@Nullable SurfaceChangeListener listener) {
+ if (listener != null) {
+ mSurfaceChangeListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Inform listeners of an upgrade to video request for a call.
+ *
+ * @param call The call.
+ * @param videoState The video state we want to upgrade to.
+ */
+ public void upgradeToVideoRequest(DialerCall call, int videoState) {
+ LogUtil.v(
+ "InCallVideoCallCallbackNotifier.upgradeToVideoRequest",
+ "call = " + call + " new video state = " + videoState);
+ for (SessionModificationListener listener : mSessionModificationListeners) {
+ listener.onUpgradeToVideoRequest(call, videoState);
+ }
+ }
+
+ /**
+ * Inform listeners of a call session event.
+ *
+ * @param event The call session event.
+ */
+ public void callSessionEvent(int event) {
+ for (VideoEventListener listener : mVideoEventListeners) {
+ listener.onCallSessionEvent(event);
+ }
+ }
+
+ /**
+ * Inform listeners of a downgrade to audio.
+ *
+ * @param call The call.
+ * @param paused The paused state.
+ */
+ public void peerPausedStateChanged(DialerCall call, boolean paused) {
+ for (VideoEventListener listener : mVideoEventListeners) {
+ listener.onPeerPauseStateChanged(call, paused);
+ }
+ }
+
+ /**
+ * Inform listeners of any change in the video quality of the call
+ *
+ * @param call The call.
+ * @param videoQuality The updated video quality of the call.
+ */
+ public void videoQualityChanged(DialerCall call, int videoQuality) {
+ for (VideoEventListener listener : mVideoEventListeners) {
+ listener.onVideoQualityChanged(call, videoQuality);
+ }
+ }
+
+ /**
+ * Inform listeners of a change to peer dimensions.
+ *
+ * @param call The call.
+ * @param width New peer width.
+ * @param height New peer height.
+ */
+ public void peerDimensionsChanged(DialerCall call, int width, int height) {
+ for (SurfaceChangeListener listener : mSurfaceChangeListeners) {
+ listener.onUpdatePeerDimensions(call, width, height);
+ }
+ }
+
+ /**
+ * Inform listeners of a change to camera dimensions.
+ *
+ * @param call The call.
+ * @param width The new camera video width.
+ * @param height The new camera video height.
+ */
+ public void cameraDimensionsChanged(DialerCall call, int width, int height) {
+ for (SurfaceChangeListener listener : mSurfaceChangeListeners) {
+ listener.onCameraDimensionsChange(call, width, height);
+ }
+ }
+
+ /**
+ * Inform listeners of a change to call data usage.
+ *
+ * @param dataUsage data usage value
+ */
+ public void callDataUsageChanged(long dataUsage) {
+ for (VideoEventListener listener : mVideoEventListeners) {
+ listener.onCallDataUsageChange(dataUsage);
+ }
+ }
+
+ /** Listener interface for any class that wants to be notified of upgrade to video request. */
+ public interface SessionModificationListener {
+
+ /**
+ * Called when a peer request is received to upgrade an audio-only call to a video call.
+ *
+ * @param call The call the request was received for.
+ * @param videoState The requested video state.
+ */
+ void onUpgradeToVideoRequest(DialerCall call, int videoState);
+ }
+
+ /**
+ * Listener interface for any class that wants to be notified of video events, including pause and
+ * un-pause of peer video, video quality changes.
+ */
+ public interface VideoEventListener {
+
+ /**
+ * Called when the peer pauses or un-pauses video transmission.
+ *
+ * @param call The call which paused or un-paused video transmission.
+ * @param paused {@code True} when the video transmission is paused, {@code false} otherwise.
+ */
+ void onPeerPauseStateChanged(DialerCall call, boolean paused);
+
+ /**
+ * Called when the video quality changes.
+ *
+ * @param call The call whose video quality changes.
+ * @param videoCallQuality - values are QUALITY_HIGH, MEDIUM, LOW and UNKNOWN.
+ */
+ void onVideoQualityChanged(DialerCall call, int videoCallQuality);
+
+ /*
+ * Called when call data usage value is requested or when call data usage value is updated
+ * because of a call state change
+ *
+ * @param dataUsage call data usage value
+ */
+ void onCallDataUsageChange(long dataUsage);
+
+ /**
+ * Called when call session event is raised.
+ *
+ * @param event The call session event.
+ */
+ void onCallSessionEvent(int event);
+ }
+
+ /**
+ * Listener interface for any class that wants to be notified of changes to the video surfaces.
+ */
+ public interface SurfaceChangeListener {
+
+ /**
+ * Called when the peer video feed changes dimensions. This can occur when the peer rotates
+ * their device, changing the aspect ratio of the video signal.
+ *
+ * @param call The call which experienced a peer video
+ */
+ void onUpdatePeerDimensions(DialerCall call, int width, int height);
+
+ /**
+ * Called when the local camera changes dimensions. This occurs when a change in camera occurs.
+ *
+ * @param call The call which experienced the camera dimension change.
+ * @param width The new camera video width.
+ * @param height The new camera video height.
+ */
+ void onCameraDimensionsChange(DialerCall call, int width, int height);
+ }
+}
diff --git a/java/com/android/incallui/call/TelecomAdapter.java b/java/com/android/incallui/call/TelecomAdapter.java
new file mode 100644
index 000000000..ebf4ecf4f
--- /dev/null
+++ b/java/com/android/incallui/call/TelecomAdapter.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2017 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.incallui.call;
+
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.os.Looper;
+import android.support.annotation.MainThread;
+import android.telecom.InCallService;
+import com.android.dialer.common.LogUtil;
+import java.util.List;
+
+/** Wrapper around Telecom APIs. */
+public final class TelecomAdapter implements InCallServiceListener {
+
+ private static final String ADD_CALL_MODE_KEY = "add_call_mode";
+
+ private static TelecomAdapter sInstance;
+ private InCallService mInCallService;
+
+ private TelecomAdapter() {}
+
+ @MainThread
+ public static TelecomAdapter getInstance() {
+ if (!Looper.getMainLooper().isCurrentThread()) {
+ throw new IllegalStateException();
+ }
+ if (sInstance == null) {
+ sInstance = new TelecomAdapter();
+ }
+ return sInstance;
+ }
+
+ @Override
+ public void setInCallService(InCallService inCallService) {
+ mInCallService = inCallService;
+ }
+
+ @Override
+ public void clearInCallService() {
+ mInCallService = null;
+ }
+
+ private android.telecom.Call getTelecomCallById(String callId) {
+ DialerCall call = CallList.getInstance().getCallById(callId);
+ return call == null ? null : call.getTelecomCall();
+ }
+
+ public void mute(boolean shouldMute) {
+ if (mInCallService != null) {
+ mInCallService.setMuted(shouldMute);
+ } else {
+ LogUtil.e("TelecomAdapter.mute", "mInCallService is null");
+ }
+ }
+
+ public void setAudioRoute(int route) {
+ if (mInCallService != null) {
+ mInCallService.setAudioRoute(route);
+ } else {
+ LogUtil.e("TelecomAdapter.setAudioRoute", "mInCallService is null");
+ }
+ }
+
+ public void merge(String callId) {
+ android.telecom.Call call = getTelecomCallById(callId);
+ if (call != null) {
+ List<android.telecom.Call> conferenceable = call.getConferenceableCalls();
+ if (!conferenceable.isEmpty()) {
+ call.conference(conferenceable.get(0));
+ } else {
+ if (call.getDetails().can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE)) {
+ call.mergeConference();
+ }
+ }
+ } else {
+ LogUtil.e("TelecomAdapter.merge", "call not in call list " + callId);
+ }
+ }
+
+ public void swap(String callId) {
+ android.telecom.Call call = getTelecomCallById(callId);
+ if (call != null) {
+ if (call.getDetails().can(android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE)) {
+ call.swapConference();
+ }
+ } else {
+ LogUtil.e("TelecomAdapter.swap", "call not in call list " + callId);
+ }
+ }
+
+ public void addCall() {
+ if (mInCallService != null) {
+ Intent intent = new Intent(Intent.ACTION_DIAL);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ // when we request the dialer come up, we also want to inform
+ // it that we're going through the "add call" option from the
+ // InCallScreen / PhoneUtils.
+ intent.putExtra(ADD_CALL_MODE_KEY, true);
+ try {
+ LogUtil.d("TelecomAdapter.addCall", "Sending the add DialerCall intent");
+ mInCallService.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ // This is rather rare but possible.
+ // Note: this method is used even when the phone is encrypted. At that moment
+ // the system may not find any Activity which can accept this Intent.
+ LogUtil.e("TelecomAdapter.addCall", "Activity for adding calls isn't found.", e);
+ }
+ }
+ }
+
+ public void playDtmfTone(String callId, char digit) {
+ android.telecom.Call call = getTelecomCallById(callId);
+ if (call != null) {
+ call.playDtmfTone(digit);
+ } else {
+ LogUtil.e("TelecomAdapter.playDtmfTone", "call not in call list " + callId);
+ }
+ }
+
+ public void stopDtmfTone(String callId) {
+ android.telecom.Call call = getTelecomCallById(callId);
+ if (call != null) {
+ call.stopDtmfTone();
+ } else {
+ LogUtil.e("TelecomAdapter.stopDtmfTone", "call not in call list " + callId);
+ }
+ }
+
+ public void postDialContinue(String callId, boolean proceed) {
+ android.telecom.Call call = getTelecomCallById(callId);
+ if (call != null) {
+ call.postDialContinue(proceed);
+ } else {
+ LogUtil.e("TelecomAdapter.postDialContinue", "call not in call list " + callId);
+ }
+ }
+
+ public boolean canAddCall() {
+ if (mInCallService != null) {
+ return mInCallService.canAddCall();
+ }
+ return false;
+ }
+}
diff --git a/java/com/android/incallui/call/VideoUtils.java b/java/com/android/incallui/call/VideoUtils.java
new file mode 100644
index 000000000..80fbfb1cc
--- /dev/null
+++ b/java/com/android/incallui/call/VideoUtils.java
@@ -0,0 +1,151 @@
+/*
+ * 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.incallui.call;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.content.ContextCompat;
+import android.telecom.VideoProfile;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.util.DialerUtils;
+import com.android.incallui.call.DialerCall.SessionModificationState;
+import java.util.Objects;
+
+public class VideoUtils {
+
+ private static final String PREFERENCE_CAMERA_ALLOWED_BY_USER = "camera_allowed_by_user";
+
+ public static boolean isVideoCall(@Nullable DialerCall call) {
+ return call != null && isVideoCall(call.getVideoState());
+ }
+
+ public static boolean isVideoCall(int videoState) {
+ return CompatUtils.isVideoCompatible()
+ && (VideoProfile.isTransmissionEnabled(videoState)
+ || VideoProfile.isReceptionEnabled(videoState));
+ }
+
+ public static boolean hasSentVideoUpgradeRequest(@Nullable DialerCall call) {
+ return call != null && hasSentVideoUpgradeRequest(call.getSessionModificationState());
+ }
+
+ public static boolean hasSentVideoUpgradeRequest(@SessionModificationState int state) {
+ return state == DialerCall.SESSION_MODIFICATION_STATE_WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE
+ || state == DialerCall.SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_FAILED
+ || state == DialerCall.SESSION_MODIFICATION_STATE_REQUEST_REJECTED
+ || state == DialerCall.SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT;
+ }
+
+ public static boolean hasReceivedVideoUpgradeRequest(@Nullable DialerCall call) {
+ return call != null && hasReceivedVideoUpgradeRequest(call.getSessionModificationState());
+ }
+
+ public static boolean hasReceivedVideoUpgradeRequest(@SessionModificationState int state) {
+ return state == DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
+ }
+
+ public static boolean isBidirectionalVideoCall(DialerCall call) {
+ return CompatUtils.isVideoCompatible() && VideoProfile.isBidirectional(call.getVideoState());
+ }
+
+ public static boolean isTransmissionEnabled(DialerCall call) {
+ if (!CompatUtils.isVideoCompatible()) {
+ return false;
+ }
+
+ return VideoProfile.isTransmissionEnabled(call.getVideoState());
+ }
+
+ public static boolean isIncomingVideoCall(DialerCall call) {
+ if (!VideoUtils.isVideoCall(call)) {
+ return false;
+ }
+ final int state = call.getState();
+ return (state == DialerCall.State.INCOMING) || (state == DialerCall.State.CALL_WAITING);
+ }
+
+ public static boolean isActiveVideoCall(DialerCall call) {
+ return VideoUtils.isVideoCall(call) && call.getState() == DialerCall.State.ACTIVE;
+ }
+
+ public static boolean isOutgoingVideoCall(DialerCall call) {
+ if (!VideoUtils.isVideoCall(call)) {
+ return false;
+ }
+ final int state = call.getState();
+ return DialerCall.State.isDialing(state)
+ || state == DialerCall.State.CONNECTING
+ || state == DialerCall.State.SELECT_PHONE_ACCOUNT;
+ }
+
+ public static boolean isAudioCall(DialerCall call) {
+ if (!CompatUtils.isVideoCompatible()) {
+ return true;
+ }
+
+ return call != null && VideoProfile.isAudioOnly(call.getVideoState());
+ }
+
+ // TODO (ims-vt) Check if special handling is needed for CONF calls.
+ public static boolean canVideoPause(DialerCall call) {
+ return isVideoCall(call) && call.getState() == DialerCall.State.ACTIVE;
+ }
+
+ public static VideoProfile makeVideoPauseProfile(@NonNull DialerCall call) {
+ Objects.requireNonNull(call);
+ if (VideoProfile.isAudioOnly(call.getVideoState())) {
+ throw new IllegalStateException();
+ }
+ return new VideoProfile(getPausedVideoState(call.getVideoState()));
+ }
+
+ public static VideoProfile makeVideoUnPauseProfile(@NonNull DialerCall call) {
+ Objects.requireNonNull(call);
+ return new VideoProfile(getUnPausedVideoState(call.getVideoState()));
+ }
+
+ public static int getUnPausedVideoState(int videoState) {
+ return videoState & (~VideoProfile.STATE_PAUSED);
+ }
+
+ public static int getPausedVideoState(int videoState) {
+ return videoState | VideoProfile.STATE_PAUSED;
+ }
+
+ public static boolean hasCameraPermissionAndAllowedByUser(@NonNull Context context) {
+ return isCameraAllowedByUser(context) && hasCameraPermission(context);
+ }
+
+ public static boolean hasCameraPermission(@NonNull Context context) {
+ return ContextCompat.checkSelfPermission(context, android.Manifest.permission.CAMERA)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ public static boolean isCameraAllowedByUser(@NonNull Context context) {
+ return DialerUtils.getDefaultSharedPreferenceForDeviceProtectedStorageContext(context)
+ .getBoolean(PREFERENCE_CAMERA_ALLOWED_BY_USER, false);
+ }
+
+ public static void setCameraAllowedByUser(@NonNull Context context) {
+ DialerUtils.getDefaultSharedPreferenceForDeviceProtectedStorageContext(context)
+ .edit()
+ .putBoolean(PREFERENCE_CAMERA_ALLOWED_BY_USER, true)
+ .apply();
+ }
+}
diff --git a/java/com/android/incallui/commontheme/AndroidManifest.xml b/java/com/android/incallui/commontheme/AndroidManifest.xml
new file mode 100644
index 000000000..1d5914f07
--- /dev/null
+++ b/java/com/android/incallui/commontheme/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.incallui.commontheme">
+</manifest>
diff --git a/java/com/android/incallui/commontheme/res/animator/button_state.xml b/java/com/android/incallui/commontheme/res/animator/button_state.xml
new file mode 100644
index 000000000..70958d610
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/animator/button_state.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:state_enabled="true">
+ <set>
+ <objectAnimator android:propertyName="translationZ"
+ android:duration="100"
+ android:valueTo="4dp"
+ android:valueType="floatType"/>
+ <objectAnimator android:propertyName="elevation"
+ android:duration="0"
+ android:valueTo="@dimen/incall_call_button_elevation"
+ android:valueType="floatType"/>
+ </set>
+ </item>
+ <!-- base state -->
+ <item android:state_enabled="true">
+ <set>
+ <objectAnimator android:propertyName="translationZ"
+ android:duration="100"
+ android:valueTo="0"
+ android:startDelay="100"
+ android:valueType="floatType"/>
+ <objectAnimator android:propertyName="elevation"
+ android:duration="0"
+ android:valueTo="@dimen/incall_call_button_elevation"
+ android:valueType="floatType" />
+ </set>
+ </item>
+ ...
+</selector> \ No newline at end of file
diff --git a/java/com/android/incallui/commontheme/res/animator/disabled_alpha.xml b/java/com/android/incallui/commontheme/res/animator/disabled_alpha.xml
new file mode 100644
index 000000000..8d78f0017
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/animator/disabled_alpha.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_enabled="false">
+ <set>
+ <objectAnimator
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_shortAnimTime"
+ android:valueTo=".3f"
+ android:valueType="floatType"/>
+ </set>
+ </item>
+ <item>
+ <set>
+ <objectAnimator
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_shortAnimTime"
+ android:valueTo="1f"
+ android:valueType="floatType"/>
+ </set>
+ </item>
+</selector>
diff --git a/java/com/android/incallui/commontheme/res/color/incall_button_ripple.xml b/java/com/android/incallui/commontheme/res/color/incall_button_ripple.xml
new file mode 100644
index 000000000..cd474c5e5
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/color/incall_button_ripple.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#80888888" android:state_checked="true"/>
+ <item android:color="#80ffffff"/>
+</selector>
diff --git a/java/com/android/incallui/commontheme/res/color/incall_button_white.xml b/java/com/android/incallui/commontheme/res/color/incall_button_white.xml
new file mode 100644
index 000000000..5df441ff0
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/color/incall_button_white.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/white" android:state_enabled="true"/>
+ <item android:color="#99ffffff" android:state_enabled="false"/>
+</selector>
diff --git a/java/com/android/incallui/commontheme/res/drawable-hdpi/ic_phone_audio_white_36dp.png b/java/com/android/incallui/commontheme/res/drawable-hdpi/ic_phone_audio_white_36dp.png
new file mode 100644
index 000000000..26f3fe001
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/drawable-hdpi/ic_phone_audio_white_36dp.png
Binary files differ
diff --git a/java/com/android/incallui/commontheme/res/drawable-mdpi/ic_phone_audio_white_36dp.png b/java/com/android/incallui/commontheme/res/drawable-mdpi/ic_phone_audio_white_36dp.png
new file mode 100644
index 000000000..5b0a9d663
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/drawable-mdpi/ic_phone_audio_white_36dp.png
Binary files differ
diff --git a/java/com/android/incallui/commontheme/res/drawable-xhdpi/ic_phone_audio_white_36dp.png b/java/com/android/incallui/commontheme/res/drawable-xhdpi/ic_phone_audio_white_36dp.png
new file mode 100644
index 000000000..d595b190d
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/drawable-xhdpi/ic_phone_audio_white_36dp.png
Binary files differ
diff --git a/java/com/android/incallui/commontheme/res/drawable-xxhdpi/ic_phone_audio_white_36dp.png b/java/com/android/incallui/commontheme/res/drawable-xxhdpi/ic_phone_audio_white_36dp.png
new file mode 100644
index 000000000..fb7cf161b
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/drawable-xxhdpi/ic_phone_audio_white_36dp.png
Binary files differ
diff --git a/java/com/android/incallui/commontheme/res/drawable-xxxhdpi/ic_phone_audio_white_36dp.png b/java/com/android/incallui/commontheme/res/drawable-xxxhdpi/ic_phone_audio_white_36dp.png
new file mode 100644
index 000000000..4bb58d9f5
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/drawable-xxxhdpi/ic_phone_audio_white_36dp.png
Binary files differ
diff --git a/java/com/android/incallui/commontheme/res/drawable/answer_answer_background.xml b/java/com/android/incallui/commontheme/res/drawable/answer_answer_background.xml
new file mode 100644
index 000000000..090506aa6
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/drawable/answer_answer_background.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#80FFFFFF">
+ <item>
+ <shape
+ android:shape="oval">
+ <solid android:color="#09ad00"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/java/com/android/incallui/commontheme/res/drawable/answer_decline_background.xml b/java/com/android/incallui/commontheme/res/drawable/answer_decline_background.xml
new file mode 100644
index 000000000..abfd56ecf
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/drawable/answer_decline_background.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#80FFFFFF">
+ <item>
+ <shape
+ android:shape="oval">
+ <solid android:color="#DF0000"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/java/com/android/incallui/commontheme/res/drawable/incall_end_call_background.xml b/java/com/android/incallui/commontheme/res/drawable/incall_end_call_background.xml
new file mode 100644
index 000000000..3c9f4bc0b
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/drawable/incall_end_call_background.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#80FFFFFF">
+ <item>
+ <shape
+ android:shape="oval">
+ <solid android:color="#FFDF0000"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/java/com/android/incallui/commontheme/res/values-w260dp-h520dp/dimens.xml b/java/com/android/incallui/commontheme/res/values-w260dp-h520dp/dimens.xml
new file mode 100644
index 000000000..e1390597a
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/values-w260dp-h520dp/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <dimen name="incall_end_call_button_size">64dp</dimen>
+ <drawable name="incall_end_call_icon">@drawable/quantum_ic_call_end_white_36</drawable>
+</resources>
diff --git a/java/com/android/incallui/commontheme/res/values-w520dp-h260dp-land/dimens.xml b/java/com/android/incallui/commontheme/res/values-w520dp-h260dp-land/dimens.xml
new file mode 100644
index 000000000..e1390597a
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/values-w520dp-h260dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <dimen name="incall_end_call_button_size">64dp</dimen>
+ <drawable name="incall_end_call_icon">@drawable/quantum_ic_call_end_white_36</drawable>
+</resources>
diff --git a/java/com/android/incallui/commontheme/res/values/colors.xml b/java/com/android/incallui/commontheme/res/values/colors.xml
new file mode 100644
index 000000000..d38e34716
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/values/colors.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- 50% black background drawn over the video to make it easier to see text and buttons. -->
+ <color name="videocall_overlay_background_color">#7E000000</color>
+</resources> \ No newline at end of file
diff --git a/java/com/android/incallui/commontheme/res/values/dimens.xml b/java/com/android/incallui/commontheme/res/values/dimens.xml
new file mode 100644
index 000000000..649ba2cde
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/values/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <dimen name="incall_end_call_button_size">48dp</dimen>
+ <dimen name="incall_call_button_elevation">8dp</dimen>
+ <drawable name="incall_end_call_icon">@drawable/quantum_ic_call_end_white_24</drawable>
+</resources>
diff --git a/java/com/android/incallui/commontheme/res/values/strings.xml b/java/com/android/incallui/commontheme/res/values/strings.xml
new file mode 100644
index 000000000..6f346a34d
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/values/strings.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="incall_content_description_end_call">End call</string>
+
+ <string name="incall_content_description_muted">Muted</string>
+
+ <string name="incall_content_description_unmuted">Unmuted</string>
+
+ <string name="incall_content_description_swap_calls">Swap calls</string>
+
+ <string name="incall_content_description_merge_calls">Merge calls</string>
+
+ <string name="incall_content_description_earpiece">Handset earpiece</string>
+
+ <string name="incall_content_description_speaker">Speaker</string>
+
+ <string name="incall_content_description_bluetooth">Bluetooth</string>
+
+ <string name="incall_content_description_headset">Wired headset</string>
+
+ <!-- Text for the onscreen "Hold" button when it is not selected. Pressing it will put
+ the call on hold. -->
+ <string name="incall_content_description_hold">Hold call</string>
+ <!-- Text for the onscreen "Hold" button when it is selected. Pressing it will resume
+ the call from a previously held state. -->
+ <string name="incall_content_description_unhold">Resume call</string>
+
+ <string name="incall_content_description_video_on">Video on</string>
+
+ <string name="incall_content_description_video_off">Video off</string>
+
+ <string name="incall_content_description_swap_video">Swap video</string>
+
+</resources>
diff --git a/java/com/android/incallui/commontheme/res/values/styles.xml b/java/com/android/incallui/commontheme/res/values/styles.xml
new file mode 100644
index 000000000..311f9cf4b
--- /dev/null
+++ b/java/com/android/incallui/commontheme/res/values/styles.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+
+ <style name="Dialer.Incall.TextAppearance.Large">
+ <item name="android:textColor">?android:textColorPrimary</item>
+ <item name="android:textSize">36sp</item>
+ <item name="android:fontFamily">sans-serif-light</item>
+ </style>
+
+ <style name="Dialer.Incall.TextAppearance.Label">
+ <item name="android:textColor">?android:textColorPrimary</item>
+ <item name="android:textSize">12sp</item>
+ </style>
+
+ <style name="Dialer.Incall.TextAppearance" parent="android:TextAppearance.Material">
+ <item name="android:textColor">?android:textColorSecondary</item>
+ <item name="android:textSize">18sp</item>
+ </style>
+
+ <style name="Incall.Button.End" parent="android:Widget.Material.Button">
+ <item name="android:background">@drawable/incall_end_call_background</item>
+ <item name="android:elevation">8dp</item>
+ <item name="android:layout_height">@dimen/incall_end_call_button_size</item>
+ <item name="android:layout_width">@dimen/incall_end_call_button_size</item>
+ <item name="android:padding">8dp</item>
+ <item name="android:src">@drawable/incall_end_call_icon</item>
+ <item name="android:stateListAnimator">@animator/disabled_alpha</item>
+ </style>
+
+ <style name="Answer.Button" parent="android:Widget.Material.Button">
+ <item name="android:stateListAnimator">@animator/button_state</item>
+ </style>
+
+ <style name="Answer.Button.Answer">
+ <item name="android:background">@drawable/answer_answer_background</item>
+ </style>
+
+ <style name="Answer.Button.Decline">
+ <item name="android:background">@drawable/answer_decline_background</item>
+ </style>
+
+</resources>
diff --git a/java/com/android/incallui/contactgrid/AndroidManifest.xml b/java/com/android/incallui/contactgrid/AndroidManifest.xml
new file mode 100644
index 000000000..520010548
--- /dev/null
+++ b/java/com/android/incallui/contactgrid/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.incallui.contactgrid">
+</manifest>
diff --git a/java/com/android/incallui/contactgrid/BottomRow.java b/java/com/android/incallui/contactgrid/BottomRow.java
new file mode 100644
index 000000000..aaf7e8214
--- /dev/null
+++ b/java/com/android/incallui/contactgrid/BottomRow.java
@@ -0,0 +1,142 @@
+/*
+ * 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.incallui.contactgrid;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.telephony.PhoneNumberUtils;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
+import android.text.TextUtils;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+import com.android.incallui.call.DialerCall.State;
+import com.android.incallui.incall.protocol.PrimaryCallState;
+import com.android.incallui.incall.protocol.PrimaryInfo;
+
+/**
+ * Gets the content of the bottom row. For example:
+ *
+ * <ul>
+ * <li>Mobile +1 (650) 253-0000
+ * <li>[HD icon] 00:15
+ * <li>Call ended
+ * <li>Hanging up
+ * </ul>
+ */
+public class BottomRow {
+
+ /** Content of the bottom row. */
+ public static class Info {
+
+ @Nullable public final CharSequence label;
+ public final boolean isTimerVisible;
+ public final boolean isWorkIconVisible;
+ public final boolean isHdIconVisible;
+ public final boolean isForwardIconVisible;
+ public final boolean isSpamIconVisible;
+ public final boolean shouldPopulateAccessibilityEvent;
+
+ public Info(
+ @Nullable CharSequence label,
+ boolean isTimerVisible,
+ boolean isWorkIconVisible,
+ boolean isHdIconVisible,
+ boolean isForwardIconVisible,
+ boolean isSpamIconVisible,
+ boolean shouldPopulateAccessibilityEvent) {
+ this.label = label;
+ this.isTimerVisible = isTimerVisible;
+ this.isWorkIconVisible = isWorkIconVisible;
+ this.isHdIconVisible = isHdIconVisible;
+ this.isForwardIconVisible = isForwardIconVisible;
+ this.isSpamIconVisible = isSpamIconVisible;
+ this.shouldPopulateAccessibilityEvent = shouldPopulateAccessibilityEvent;
+ }
+ }
+
+ private BottomRow() {}
+
+ public static Info getInfo(Context context, PrimaryCallState state, PrimaryInfo primaryInfo) {
+ CharSequence label;
+ boolean isTimerVisible = state.state == State.ACTIVE;
+ boolean isForwardIconVisible = state.isForwardedNumber;
+ boolean isWorkIconVisible = state.isWorkCall;
+ boolean isHdIconVisible = state.isHdAudioCall && !isForwardIconVisible;
+ boolean isSpamIconVisible = false;
+ boolean shouldPopulateAccessibilityEvent = true;
+
+ if (isIncoming(state) && primaryInfo.isSpam) {
+ label = context.getString(R.string.contact_grid_incoming_suspected_spam);
+ isSpamIconVisible = true;
+ isHdIconVisible = false;
+ } else if (state.state == State.DISCONNECTING) {
+ // While in the DISCONNECTING state we display a "Hanging up" message in order to make the UI
+ // feel more responsive. (In GSM it's normal to see a delay of a couple of seconds while
+ // negotiating the disconnect with the network, so the "Hanging up" state at least lets the
+ // user know that we're doing something. This state is currently not used with CDMA.)
+ label = context.getString(R.string.incall_hanging_up);
+ } else if (state.state == State.DISCONNECTED) {
+ label = state.disconnectCause.getLabel();
+ if (TextUtils.isEmpty(label)) {
+ label = context.getString(R.string.incall_call_ended);
+ }
+ } else if (!TextUtils.isEmpty(state.callbackNumber)) {
+ // This is used for carriers like Project Fi to show the callback number for emergency calls.
+ label =
+ context.getString(
+ R.string.contact_grid_callback_number,
+ PhoneNumberUtils.formatNumber(state.callbackNumber));
+ isTimerVisible = false;
+ } else {
+ label = getLabelForPhoneNumber(primaryInfo);
+ shouldPopulateAccessibilityEvent = primaryInfo.nameIsNumber;
+ }
+
+ return new Info(
+ label,
+ isTimerVisible,
+ isWorkIconVisible,
+ isHdIconVisible,
+ isForwardIconVisible,
+ isSpamIconVisible,
+ shouldPopulateAccessibilityEvent);
+ }
+
+ private static CharSequence getLabelForPhoneNumber(PrimaryInfo primaryInfo) {
+ if (primaryInfo.nameIsNumber) {
+ return primaryInfo.location;
+ }
+ if (!TextUtils.isEmpty(primaryInfo.number)) {
+ CharSequence spannedNumber = spanDisplayNumber(primaryInfo.number);
+ if (primaryInfo.label == null) {
+ return spannedNumber;
+ } else {
+ return TextUtils.concat(primaryInfo.label, " ", spannedNumber);
+ }
+ }
+ return null;
+ }
+
+ private static CharSequence spanDisplayNumber(String displayNumber) {
+ return PhoneNumberUtilsCompat.createTtsSpannable(
+ BidiFormatter.getInstance().unicodeWrap(displayNumber, TextDirectionHeuristics.LTR));
+ }
+
+ private static boolean isIncoming(PrimaryCallState state) {
+ return state.state == State.INCOMING || state.state == State.CALL_WAITING;
+ }
+}
diff --git a/java/com/android/incallui/contactgrid/ContactGridManager.java b/java/com/android/incallui/contactgrid/ContactGridManager.java
new file mode 100644
index 000000000..81c225163
--- /dev/null
+++ b/java/com/android/incallui/contactgrid/ContactGridManager.java
@@ -0,0 +1,315 @@
+/*
+ * 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.incallui.contactgrid;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.Chronometer;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.ViewAnimator;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+import com.android.contacts.common.lettertiles.LetterTileDrawable;
+import com.android.dialer.common.Assert;
+import com.android.dialer.util.DrawableConverter;
+import com.android.incallui.incall.protocol.ContactPhotoType;
+import com.android.incallui.incall.protocol.PrimaryCallState;
+import com.android.incallui.incall.protocol.PrimaryInfo;
+import java.util.List;
+
+/** Utility to manage the Contact grid */
+public class ContactGridManager {
+
+ private final Context context;
+ private final View contactGridLayout;
+
+ // Row 0: Captain Holt ON HOLD
+ // Row 0: Calling...
+ // Row 0: [Wi-Fi icon] Calling via Starbucks Wi-Fi
+ // Row 0: [Wi-Fi icon] Starbucks Wi-Fi
+ // Row 0: Hey Jake, pick up!
+ private ImageView connectionIconImageView;
+ private TextView statusTextView;
+
+ // Row 1: Jake Peralta [Contact photo]
+ // Row 1: Walgreens
+ // Row 1: +1 (650) 253-0000
+ private TextView contactNameTextView;
+ @Nullable private ImageView avatarImageView;
+
+ // Row 2: Mobile +1 (650) 253-0000
+ // Row 2: [HD icon] 00:15
+ // Row 2: Call ended
+ // Row 2: Hanging up
+ // Row 2: [Alert sign] Suspected spam caller
+ // Row 2: Your emergency callback number: +1 (650) 253-0000
+ private ImageView workIconImageView;
+ private ImageView hdIconImageView;
+ private ImageView forwardIconImageView;
+ private ImageView spamIconImageView;
+ private ViewAnimator bottomTextSwitcher;
+ private TextView bottomTextView;
+ private Chronometer bottomTimerView;
+ private int avatarSize;
+ private boolean hideAvatar;
+ private boolean showAnonymousAvatar;
+ private boolean middleRowVisible = true;
+
+ private PrimaryInfo primaryInfo = PrimaryInfo.createEmptyPrimaryInfo();
+ private PrimaryCallState primaryCallState = PrimaryCallState.createEmptyPrimaryCallState();
+ private final LetterTileDrawable letterTile;
+
+
+ public ContactGridManager(
+ View view, @Nullable ImageView avatarImageView, int avatarSize, boolean showAnonymousAvatar) {
+ context = view.getContext();
+ Assert.isNotNull(context);
+
+ this.avatarImageView = avatarImageView;
+ this.avatarSize = avatarSize;
+ this.showAnonymousAvatar = showAnonymousAvatar;
+ connectionIconImageView = (ImageView) view.findViewById(R.id.contactgrid_connection_icon);
+ statusTextView = (TextView) view.findViewById(R.id.contactgrid_status_text);
+ contactNameTextView = (TextView) view.findViewById(R.id.contactgrid_contact_name);
+ workIconImageView = (ImageView) view.findViewById(R.id.contactgrid_workIcon);
+ hdIconImageView = (ImageView) view.findViewById(R.id.contactgrid_hdIcon);
+ forwardIconImageView = (ImageView) view.findViewById(R.id.contactgrid_forwardIcon);
+ spamIconImageView = (ImageView) view.findViewById(R.id.contactgrid_spamIcon);
+ bottomTextSwitcher = (ViewAnimator) view.findViewById(R.id.contactgrid_bottom_text_switcher);
+ bottomTextView = (TextView) view.findViewById(R.id.contactgrid_bottom_text);
+ bottomTimerView = (Chronometer) view.findViewById(R.id.contactgrid_bottom_timer);
+
+ contactGridLayout = (View) contactNameTextView.getParent();
+ letterTile = new LetterTileDrawable(context.getResources());
+ }
+
+ public void show() {
+ contactGridLayout.setVisibility(View.VISIBLE);
+ }
+
+ public void hide() {
+ contactGridLayout.setVisibility(View.GONE);
+ }
+
+ public void setAvatarHidden(boolean hide) {
+ if (hide != hideAvatar) {
+ hideAvatar = hide;
+ updatePrimaryNameAndPhoto();
+ }
+ }
+
+ public boolean isAvatarHidden() {
+ return hideAvatar;
+ }
+
+ public View getContainerView() {
+ return contactGridLayout;
+ }
+
+ public void setIsMiddleRowVisible(boolean isMiddleRowVisible) {
+ if (middleRowVisible == isMiddleRowVisible) {
+ return;
+ }
+ middleRowVisible = isMiddleRowVisible;
+
+ contactNameTextView.setVisibility(isMiddleRowVisible ? View.VISIBLE : View.GONE);
+ updateAvatarVisibility();
+ }
+
+ public void setPrimary(PrimaryInfo primaryInfo) {
+ this.primaryInfo = primaryInfo;
+ updatePrimaryNameAndPhoto();
+ updateBottomRow();
+ }
+
+ public void setCallState(PrimaryCallState primaryCallState) {
+ this.primaryCallState = primaryCallState;
+ updatePrimaryNameAndPhoto();
+ updateBottomRow();
+ updateTopRow();
+ }
+
+ public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ dispatchPopulateAccessibilityEvent(event, statusTextView);
+ dispatchPopulateAccessibilityEvent(event, contactNameTextView);
+ BottomRow.Info info = BottomRow.getInfo(context, primaryCallState, primaryInfo);
+ if (info.shouldPopulateAccessibilityEvent) {
+ dispatchPopulateAccessibilityEvent(event, bottomTextView);
+ }
+ }
+
+ public void setAvatarImageView(
+ @Nullable ImageView avatarImageView, int avatarSize, boolean showAnonymousAvatar) {
+ this.avatarImageView = avatarImageView;
+ this.avatarSize = avatarSize;
+ this.showAnonymousAvatar = showAnonymousAvatar;
+ updatePrimaryNameAndPhoto();
+ }
+
+ private void dispatchPopulateAccessibilityEvent(AccessibilityEvent event, View view) {
+ final List<CharSequence> eventText = event.getText();
+ int size = eventText.size();
+ view.dispatchPopulateAccessibilityEvent(event);
+ // If no text added write null to keep relative position.
+ if (size == eventText.size()) {
+ eventText.add(null);
+ }
+ }
+
+ private boolean updateAvatarVisibility() {
+ if (avatarImageView == null) {
+ return false;
+ }
+
+ if (!middleRowVisible) {
+ avatarImageView.setVisibility(View.GONE);
+ return false;
+ }
+
+ boolean hasPhoto =
+ primaryInfo.photo != null && primaryInfo.photoType == ContactPhotoType.CONTACT;
+ if (!hasPhoto && !showAnonymousAvatar) {
+ avatarImageView.setVisibility(View.GONE);
+ return false;
+ }
+
+ avatarImageView.setVisibility(View.VISIBLE);
+ return true;
+ }
+
+ /**
+ * Updates row 0. For example:
+ *
+ * <ul>
+ * <li>Captain Holt ON HOLD
+ * <li>Calling...
+ * <li>[Wi-Fi icon] Calling via Starbucks Wi-Fi
+ * <li>[Wi-Fi icon] Starbucks Wi-Fi
+ * <li>Call from
+ * </ul>
+ */
+ private void updateTopRow() {
+ TopRow.Info info = TopRow.getInfo(context, primaryCallState);
+ if (TextUtils.isEmpty(info.label)) {
+ // Use INVISIBLE here to prevent the rows below this one from moving up and down.
+ statusTextView.setVisibility(View.INVISIBLE);
+ statusTextView.setText(null);
+ } else {
+ statusTextView.setText(info.label);
+ statusTextView.setVisibility(View.VISIBLE);
+ statusTextView.setSingleLine(info.labelIsSingleLine);
+ }
+
+ if (info.icon == null) {
+ connectionIconImageView.setVisibility(View.GONE);
+ } else {
+ connectionIconImageView.setVisibility(View.VISIBLE);
+ connectionIconImageView.setImageDrawable(info.icon);
+ }
+ }
+
+ /**
+ * Updates row 1. For example:
+ *
+ * <ul>
+ * <li>Jake Peralta [Contact photo]
+ * <li>Walgreens
+ * <li>+1 (650) 253-0000
+ * </ul>
+ */
+ private void updatePrimaryNameAndPhoto() {
+ if (TextUtils.isEmpty(primaryInfo.name)) {
+ contactNameTextView.setText(null);
+ } else {
+ contactNameTextView.setText(
+ primaryInfo.nameIsNumber
+ ? PhoneNumberUtilsCompat.createTtsSpannable(primaryInfo.name)
+ : primaryInfo.name);
+
+ // Set direction of the name field
+ int nameDirection = View.TEXT_DIRECTION_INHERIT;
+ if (primaryInfo.nameIsNumber) {
+ nameDirection = View.TEXT_DIRECTION_LTR;
+ }
+ contactNameTextView.setTextDirection(nameDirection);
+ }
+
+ if (avatarImageView != null) {
+ if (hideAvatar) {
+ avatarImageView.setVisibility(View.GONE);
+ } else if (avatarImageView != null && avatarSize > 0 && updateAvatarVisibility()) {
+ boolean hasPhoto =
+ primaryInfo.photo != null && primaryInfo.photoType == ContactPhotoType.CONTACT;
+ // Contact has a photo, don't render a letter tile.
+ if (hasPhoto) {
+ avatarImageView.setBackground(
+ DrawableConverter.getRoundedDrawable(
+ context, primaryInfo.photo, avatarSize, avatarSize));
+ // Contact has a name, that isn't a number.
+ } else {
+ int contactType =
+ primaryCallState.isVoiceMailNumber
+ ? LetterTileDrawable.TYPE_VOICEMAIL
+ : LetterTileDrawable.TYPE_DEFAULT;
+ letterTile.setCanonicalDialerLetterTileDetails(
+ primaryInfo.name,
+ primaryInfo.contactInfoLookupKey,
+ LetterTileDrawable.SHAPE_CIRCLE,
+ contactType);
+ avatarImageView.setBackground(letterTile);
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates row 2. For example:
+ *
+ * <ul>
+ * <li>Mobile +1 (650) 253-0000
+ * <li>[HD icon] 00:15
+ * <li>Call ended
+ * <li>Hanging up
+ * </ul>
+ */
+ private void updateBottomRow() {
+ BottomRow.Info info = BottomRow.getInfo(context, primaryCallState, primaryInfo);
+
+ bottomTextView.setText(info.label);
+ bottomTextView.setAllCaps(info.isSpamIconVisible);
+ workIconImageView.setVisibility(info.isWorkIconVisible ? View.VISIBLE : View.GONE);
+ hdIconImageView.setVisibility(info.isHdIconVisible ? View.VISIBLE : View.GONE);
+ forwardIconImageView.setVisibility(info.isForwardIconVisible ? View.VISIBLE : View.GONE);
+ spamIconImageView.setVisibility(info.isSpamIconVisible ? View.VISIBLE : View.GONE);
+
+ if (info.isTimerVisible) {
+ bottomTextSwitcher.setDisplayedChild(1);
+ bottomTimerView.setBase(
+ primaryCallState.connectTimeMillis
+ - System.currentTimeMillis()
+ + SystemClock.elapsedRealtime());
+ bottomTimerView.start();
+ } else {
+ bottomTextSwitcher.setDisplayedChild(0);
+ bottomTimerView.stop();
+ }
+ }
+}
diff --git a/java/com/android/incallui/contactgrid/TopRow.java b/java/com/android/incallui/contactgrid/TopRow.java
new file mode 100644
index 000000000..a340fd0a0
--- /dev/null
+++ b/java/com/android/incallui/contactgrid/TopRow.java
@@ -0,0 +1,168 @@
+/*
+ * 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.incallui.contactgrid;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import com.android.dialer.common.Assert;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.DialerCall.State;
+import com.android.incallui.call.VideoUtils;
+import com.android.incallui.incall.protocol.PrimaryCallState;
+
+/**
+ * Gets the content of the top row. For example:
+ *
+ * <ul>
+ * <li>Captain Holt ON HOLD
+ * <li>Calling...
+ * <li>[Wi-Fi icon] Calling via Starbucks Wi-Fi
+ * <li>[Wi-Fi icon] Starbucks Wi-Fi
+ * <li>Call from
+ * </ul>
+ */
+public class TopRow {
+
+ /** Content of the top row. */
+ public static class Info {
+
+ @Nullable public final CharSequence label;
+ @Nullable public final Drawable icon;
+ public final boolean labelIsSingleLine;
+
+ public Info(@Nullable CharSequence label, @Nullable Drawable icon, boolean labelIsSingleLine) {
+ this.label = label;
+ this.icon = icon;
+ this.labelIsSingleLine = labelIsSingleLine;
+ }
+ }
+
+ private TopRow() {}
+
+ public static Info getInfo(Context context, PrimaryCallState state) {
+ CharSequence label = null;
+ Drawable icon = state.connectionIcon;
+ boolean labelIsSingleLine = true;
+
+ if (state.isWifi && icon == null) {
+ icon = context.getDrawable(R.drawable.quantum_ic_network_wifi_white_24);
+ }
+
+ if (state.state == State.INCOMING || state.state == State.CALL_WAITING) {
+ // Call from
+ // [Wi-Fi icon] Video call from
+ // Hey Jake, pick up!
+ if (!TextUtils.isEmpty(state.callSubject)) {
+ label = state.callSubject;
+ labelIsSingleLine = false;
+ } else {
+ label = getLabelForIncoming(context, state);
+ }
+ } else if (VideoUtils.hasSentVideoUpgradeRequest(state.sessionModificationState)
+ || VideoUtils.hasReceivedVideoUpgradeRequest(state.sessionModificationState)) {
+ label = getLabelForVideoRequest(context, state);
+ } else if (state.state == State.PULLING) {
+ label = context.getString(R.string.incall_transferring);
+ } else if (state.state == State.DIALING || state.state == State.CONNECTING) {
+ // [Wi-Fi icon] Calling via Google Guest
+ // Calling...
+ label = getLabelForDialing(context, state);
+ } else if (state.state == State.ACTIVE && state.isRemotelyHeld) {
+ label = context.getString(R.string.incall_remotely_held);
+ } else {
+ // Video calling...
+ // [Wi-Fi icon] Starbucks Wi-Fi
+ label = getConnectionLabel(state);
+ }
+
+ return new Info(label, icon, labelIsSingleLine);
+ }
+
+ private static CharSequence getLabelForIncoming(Context context, PrimaryCallState state) {
+ if (VideoUtils.isVideoCall(state.videoState)) {
+ return getLabelForIncomingVideo(context, state.isWifi);
+ } else if (state.isWifi && !TextUtils.isEmpty(state.connectionLabel)) {
+ return state.connectionLabel;
+ } else if (isAccount(state)) {
+ return context.getString(R.string.contact_grid_incoming_via_template, state.connectionLabel);
+ } else if (state.isWorkCall) {
+ return context.getString(R.string.contact_grid_incoming_work_call);
+ } else {
+ return context.getString(R.string.contact_grid_incoming_voice_call);
+ }
+ }
+
+ private static CharSequence getLabelForIncomingVideo(Context context, boolean isWifi) {
+ if (isWifi) {
+ return context.getString(R.string.contact_grid_incoming_wifi_video_call);
+ } else {
+ return context.getString(R.string.contact_grid_incoming_video_call);
+ }
+ }
+
+ private static CharSequence getLabelForDialing(Context context, PrimaryCallState state) {
+ if (!TextUtils.isEmpty(state.connectionLabel) && !state.isWifi) {
+ return context.getString(R.string.incall_calling_via_template, state.connectionLabel);
+ } else {
+ if (VideoUtils.isVideoCall(state.videoState)) {
+ if (state.isWifi) {
+ return context.getString(R.string.incall_wifi_video_call_requesting);
+ } else {
+ return context.getString(R.string.incall_video_call_requesting);
+ }
+ }
+ return context.getString(R.string.incall_connecting);
+ }
+ }
+
+ private static CharSequence getConnectionLabel(PrimaryCallState state) {
+ if (!TextUtils.isEmpty(state.connectionLabel)
+ && (isAccount(state) || state.isWifi || state.isConference)) {
+ // We normally don't show a "call state label" at all when active
+ // (but we can use the call state label to display the provider name).
+ return state.connectionLabel;
+ } else {
+ return null;
+ }
+ }
+
+ private static CharSequence getLabelForVideoRequest(Context context, PrimaryCallState state) {
+ switch (state.sessionModificationState) {
+ case DialerCall.SESSION_MODIFICATION_STATE_WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE:
+ return context.getString(R.string.incall_video_call_requesting);
+ case DialerCall.SESSION_MODIFICATION_STATE_REQUEST_FAILED:
+ case DialerCall.SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_FAILED:
+ return context.getString(R.string.incall_video_call_request_failed);
+ case DialerCall.SESSION_MODIFICATION_STATE_REQUEST_REJECTED:
+ return context.getString(R.string.incall_video_call_request_rejected);
+ case DialerCall.SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT:
+ return context.getString(R.string.incall_video_call_request_timed_out);
+ case DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST:
+ return getLabelForIncomingVideo(context, state.isWifi);
+ case DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST:
+ default:
+ Assert.fail();
+ return null;
+ }
+ }
+
+ private static boolean isAccount(PrimaryCallState state) {
+ return !TextUtils.isEmpty(state.connectionLabel) && TextUtils.isEmpty(state.gatewayNumber);
+ }
+}
diff --git a/java/com/android/incallui/contactgrid/res/layout/incall_contactgrid_bottom_row.xml b/java/com/android/incallui/contactgrid/res/layout/incall_contactgrid_bottom_row.xml
new file mode 100644
index 000000000..3900be556
--- /dev/null
+++ b/java/com/android/incallui/contactgrid/res/layout/incall_contactgrid_bottom_row.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_horizontal"
+ tools:showIn="@layout/incall_contact_grid">
+ <ImageView
+ android:id="@id/contactgrid_workIcon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginEnd="8dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_work_profile"
+ android:tint="#ffffff"
+ tools:visibility="gone"
+ />
+ <ImageView
+ android:id="@id/contactgrid_hdIcon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginEnd="8dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/quantum_ic_hd_white_24"
+ tools:visibility="gone"
+ />
+ <ImageView
+ android:id="@id/contactgrid_forwardIcon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginEnd="8dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/quantum_ic_forward_white_24"
+ tools:visibility="gone"
+ />
+ <ImageView
+ android:id="@+id/contactgrid_spamIcon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginEnd="8dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/quantum_ic_report_white_18"
+ tools:visibility="gone"
+ />
+ <ViewAnimator
+ android:id="@+id/contactgrid_bottom_text_switcher"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="2dp"
+ android:measureAllChildren="false">
+ <TextView
+ android:id="@+id/contactgrid_bottom_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:singleLine="true"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance"
+ tools:gravity="start"
+ tools:text="Mobile +1 (650) 253-0000"/>
+ <Chronometer
+ android:id="@+id/contactgrid_bottom_timer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:singleLine="true"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance"
+ tools:gravity="center"/>
+ </ViewAnimator>
+</LinearLayout>
diff --git a/java/com/android/incallui/contactgrid/res/layout/incall_contactgrid_top_row.xml b/java/com/android/incallui/contactgrid/res/layout/incall_contactgrid_top_row.xml
new file mode 100644
index 000000000..59359c9c1
--- /dev/null
+++ b/java/com/android/incallui/contactgrid/res/layout/incall_contactgrid_top_row.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="horizontal"
+ tools:showIn="@layout/incall_contact_grid">
+ <ImageView
+ android:id="@id/contactgrid_connection_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginEnd="10dp"
+ android:scaleType="fitCenter"
+ tools:src="@android:drawable/sym_def_app_icon"
+ tools:visibility="visible"
+ />
+ <TextView
+ android:id="@id/contactgrid_status_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance"
+ tools:text="Captain Holt"/>
+</LinearLayout>
diff --git a/java/com/android/incallui/contactgrid/res/values/ids.xml b/java/com/android/incallui/contactgrid/res/values/ids.xml
new file mode 100644
index 000000000..821dc9d98
--- /dev/null
+++ b/java/com/android/incallui/contactgrid/res/values/ids.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <item name="contactgrid_connection_icon" type="id"/>
+ <item name="contactgrid_status_text" type="id"/>
+ <item name="contactgrid_contact_name" type="id"/>
+ <item name="contactgrid_workIcon" type="id"/>
+ <item name="contactgrid_hdIcon" type="id"/>
+ <item name="contactgrid_forwardIcon" type="id"/>
+ <item name="contactgrid_spamIcon" type="id"/>
+ <item name="contactgrid_bottom_text" type="id"/>
+ <item name="contactgrid_bottom_timer" type="id"/>
+ <item name="contactgrid_avatar" type="id"/>
+ <item name="contactgrid_top_row" type="id"/>
+ <item name="contactgrid_bottom_row" type="id"/>
+</resources>
diff --git a/java/com/android/incallui/contactgrid/res/values/strings.xml b/java/com/android/incallui/contactgrid/res/values/strings.xml
new file mode 100644
index 000000000..385f843b1
--- /dev/null
+++ b/java/com/android/incallui/contactgrid/res/values/strings.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Title displayed in the overlay for outgoing calls which include the name of the provider.
+ [CHAR LIMIT=40] -->
+ <string name="incall_calling_via_template">Calling via <xliff:g id="provider_name">%s</xliff:g></string>
+
+ <!-- Displayed above the contact name during an outgoing phone call. Indicates that the call is
+ in the connecting stage. -->
+ <string name="incall_connecting">Calling…</string>
+
+ <!-- Displayed above the contact name when an external call is being pulled to the local
+ device. -->
+ <string name="incall_transferring">Transferring…</string>
+
+ <!-- Displayed above the contact name when the user requests an upgrade from a voice call to a
+ video call. -->
+ <string name="incall_video_call_requesting">Video calling…</string>
+
+ <!-- Displayed above the contact name when the user requests an upgrade from a voice call to a
+ Wi-Fi video call. -->
+ <string name="incall_wifi_video_call_requesting">Wi-Fi video calling…</string>
+
+ <!-- Displayed above the contact name when the user's video upgrade failed due to an unknown
+ reason. -->
+ <string name="incall_video_call_request_failed">Unable to connect</string>
+
+ <!-- Displayed above the contact name when the user's video upgrade was declined by the remote
+ party. -->
+ <string name="incall_video_call_request_rejected">Call declined</string>
+
+ <!-- Displayed above the contact name when no response was received for the user's upgrade
+ requests and we timed out. -->
+ <string name="incall_video_call_request_timed_out">Call timed out</string>
+
+ <!-- In-call screen: status label for a call that's in the process of hanging up
+ [CHAR LIMIT=25] -->
+ <string name="incall_hanging_up">Hanging up</string>
+
+ <!-- In-call screen: status label displayed briefly after a call ends [CHAR LIMIT=25] -->
+ <string name="incall_call_ended">Call ended</string>
+
+ <!-- In-call screen: label shown at the top of the screen when a call is on hold by the remote
+ party [CHAR LIMIT=25] -->
+ <string name="incall_remotely_held">On hold</string>
+
+ <!-- Displayed in the answer call screen for incoming video calls. -->
+ <string name="contact_grid_incoming_video_call">Video call from</string>
+
+ <!-- Displayed in the answer call screen for incoming video calls over Wi-F. -->
+ <string name="contact_grid_incoming_wifi_video_call">Wi-Fi video call from</string>
+
+ <!-- Displayed in the answer call screen for incoming voice calls. -->
+ <string name="contact_grid_incoming_voice_call">Call from</string>
+
+ <!-- Displayed in the answer call screen for incoming voice calls. -->
+ <string name="contact_grid_incoming_work_call">Work call from</string>
+
+ <!-- Displayed in the answer call screen for incoming calls via a phone account. -->
+ <string name="contact_grid_incoming_via_template">Incoming via <xliff:g id="provider_name">%s</xliff:g></string>
+
+ <!-- Displayed in the answer call screen for incoming spam calls. -->
+ <string name="contact_grid_incoming_suspected_spam">Suspected spam caller</string>
+
+ <!-- In-call screen: string shown to the user when their outgoing number is different than the
+ number reported by TelephonyManager#getLine1Number(). This is used for carriers like
+ Project Fi so that users can give their number to emergency responders. -->
+ <string name="contact_grid_callback_number">Callback number: <xliff:g id="dark_number">%1$s</xliff:g></string>
+</resources>
diff --git a/java/com/android/incallui/hold/AndroidManifest.xml b/java/com/android/incallui/hold/AndroidManifest.xml
new file mode 100644
index 000000000..2aedce903
--- /dev/null
+++ b/java/com/android/incallui/hold/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.incallui.hold">
+</manifest>
diff --git a/java/com/android/incallui/hold/OnHoldFragment.java b/java/com/android/incallui/hold/OnHoldFragment.java
new file mode 100644
index 000000000..c6952131b
--- /dev/null
+++ b/java/com/android/incallui/hold/OnHoldFragment.java
@@ -0,0 +1,102 @@
+/*
+ * 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.incallui.hold;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.telephony.PhoneNumberUtils;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
+import android.transition.TransitionManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.android.dialer.common.Assert;
+import com.android.incallui.incall.protocol.SecondaryInfo;
+
+/** Shows banner UI for background call */
+public class OnHoldFragment extends Fragment {
+
+ private static final String ARG_INFO = "info";
+ private boolean padTopInset = true;
+ private int topInset;
+
+ public static OnHoldFragment newInstance(@NonNull SecondaryInfo info) {
+ OnHoldFragment fragment = new OnHoldFragment();
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_INFO, info);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ final View view = layoutInflater.inflate(R.layout.incall_on_hold_banner, viewGroup, false);
+
+ SecondaryInfo secondaryInfo = getArguments().getParcelable(ARG_INFO);
+ secondaryInfo = Assert.isNotNull(secondaryInfo);
+
+ ((TextView) view.findViewById(R.id.hold_contact_name))
+ .setText(
+ secondaryInfo.nameIsNumber
+ ? PhoneNumberUtils.createTtsSpannable(
+ BidiFormatter.getInstance()
+ .unicodeWrap(secondaryInfo.name, TextDirectionHeuristics.LTR))
+ : secondaryInfo.name);
+ ((ImageView) view.findViewById(R.id.hold_phone_icon))
+ .setImageResource(
+ secondaryInfo.isVideoCall
+ ? R.drawable.quantum_ic_videocam_white_18
+ : R.drawable.quantum_ic_call_white_18);
+ view.addOnAttachStateChangeListener(
+ new OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ topInset = v.getRootWindowInsets().getSystemWindowInsetTop();
+ applyInset();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {}
+ });
+ return view;
+ }
+
+ public void setPadTopInset(boolean padTopInset) {
+ this.padTopInset = padTopInset;
+ applyInset();
+ }
+
+ private void applyInset() {
+ if (getView() == null) {
+ return;
+ }
+
+ int newPadding = padTopInset ? topInset : 0;
+ if (newPadding != getView().getPaddingTop()) {
+ TransitionManager.beginDelayedTransition(((ViewGroup) getView().getParent()));
+ getView().setPadding(0, newPadding, 0, 0);
+ }
+ }
+}
diff --git a/java/com/android/incallui/hold/res/layout/incall_on_hold_banner.xml b/java/com/android/incallui/hold/res/layout/incall_on_hold_banner.xml
new file mode 100644
index 000000000..c213af5da
--- /dev/null
+++ b/java/com/android/incallui/hold/res/layout/incall_on_hold_banner.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="#CC212121"
+ android:fitsSystemWindows="true">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:gravity="center_vertical">
+
+ <ImageView
+ android:id="@+id/hold_phone_icon"
+ android:layout_width="18dp"
+ android:layout_height="18dp"
+ android:src="@drawable/quantum_ic_call_white_18"
+ android:contentDescription="@null"/>
+
+ <TextView
+ android:id="@+id/hold_contact_name"
+ style="@style/Dialer.Incall.TextAppearance"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="24dp"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textColor="@android:color/white"
+ tools:text="Jake Peralta Really Longname"/>
+
+ <TextView
+ style="@style/Dialer.Incall.TextAppearance"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAllCaps="true"
+ android:textColor="@android:color/white"
+ android:text="@string/incall_on_hold"/>
+ </LinearLayout>
+</FrameLayout>
diff --git a/java/com/android/incallui/hold/res/values/strings.xml b/java/com/android/incallui/hold/res/values/strings.xml
new file mode 100644
index 000000000..2e66bcf6c
--- /dev/null
+++ b/java/com/android/incallui/hold/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="incall_on_hold">On hold</string>
+
+</resources>
diff --git a/java/com/android/incallui/incall/bindings/InCallBindings.java b/java/com/android/incallui/incall/bindings/InCallBindings.java
new file mode 100644
index 000000000..8bbbc68e1
--- /dev/null
+++ b/java/com/android/incallui/incall/bindings/InCallBindings.java
@@ -0,0 +1,28 @@
+/*
+ * 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.incallui.incall.bindings;
+
+import com.android.incallui.incall.impl.InCallFragment;
+import com.android.incallui.incall.protocol.InCallScreen;
+
+/** Bindings for the in call module. */
+public class InCallBindings {
+
+ public static InCallScreen createInCallScreen() {
+ return new InCallFragment();
+ }
+}
diff --git a/java/com/android/incallui/incall/impl/AndroidManifest.xml b/java/com/android/incallui/incall/impl/AndroidManifest.xml
new file mode 100644
index 000000000..a0e3110d8
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.incall.incall.impl">
+</manifest>
diff --git a/java/com/android/incallui/incall/impl/AutoValue_MappedButtonConfig_MappingInfo.java b/java/com/android/incallui/incall/impl/AutoValue_MappedButtonConfig_MappingInfo.java
new file mode 100644
index 000000000..addebc484
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/AutoValue_MappedButtonConfig_MappingInfo.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 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.incallui.incall.impl;
+
+import javax.annotation.Generated;
+
+@Generated("com.google.auto.value.processor.AutoValueProcessor")
+ final class AutoValue_MappedButtonConfig_MappingInfo extends MappedButtonConfig.MappingInfo {
+
+ private final int slot;
+ private final int slotOrder;
+ private final int conflictOrder;
+
+ private AutoValue_MappedButtonConfig_MappingInfo(
+ int slot,
+ int slotOrder,
+ int conflictOrder) {
+ this.slot = slot;
+ this.slotOrder = slotOrder;
+ this.conflictOrder = conflictOrder;
+ }
+
+ @Override
+ public int getSlot() {
+ return slot;
+ }
+
+ @Override
+ public int getSlotOrder() {
+ return slotOrder;
+ }
+
+ @Override
+ public int getConflictOrder() {
+ return conflictOrder;
+ }
+
+ @Override
+ public String toString() {
+ return "MappingInfo{"
+ + "slot=" + slot + ", "
+ + "slotOrder=" + slotOrder + ", "
+ + "conflictOrder=" + conflictOrder
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof MappedButtonConfig.MappingInfo) {
+ MappedButtonConfig.MappingInfo that = (MappedButtonConfig.MappingInfo) o;
+ return (this.slot == that.getSlot())
+ && (this.slotOrder == that.getSlotOrder())
+ && (this.conflictOrder == that.getConflictOrder());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 1;
+ h *= 1000003;
+ h ^= this.slot;
+ h *= 1000003;
+ h ^= this.slotOrder;
+ h *= 1000003;
+ h ^= this.conflictOrder;
+ return h;
+ }
+
+ static final class Builder extends MappedButtonConfig.MappingInfo.Builder {
+ private Integer slot;
+ private Integer slotOrder;
+ private Integer conflictOrder;
+ Builder() {
+ }
+ private Builder(MappedButtonConfig.MappingInfo source) {
+ this.slot = source.getSlot();
+ this.slotOrder = source.getSlotOrder();
+ this.conflictOrder = source.getConflictOrder();
+ }
+ @Override
+ public MappedButtonConfig.MappingInfo.Builder setSlot(int slot) {
+ this.slot = slot;
+ return this;
+ }
+ @Override
+ public MappedButtonConfig.MappingInfo.Builder setSlotOrder(int slotOrder) {
+ this.slotOrder = slotOrder;
+ return this;
+ }
+ @Override
+ public MappedButtonConfig.MappingInfo.Builder setConflictOrder(int conflictOrder) {
+ this.conflictOrder = conflictOrder;
+ return this;
+ }
+ @Override
+ public MappedButtonConfig.MappingInfo build() {
+ String missing = "";
+ if (this.slot == null) {
+ missing += " slot";
+ }
+ if (this.slotOrder == null) {
+ missing += " slotOrder";
+ }
+ if (this.conflictOrder == null) {
+ missing += " conflictOrder";
+ }
+ if (!missing.isEmpty()) {
+ throw new IllegalStateException("Missing required properties:" + missing);
+ }
+ return new AutoValue_MappedButtonConfig_MappingInfo(
+ this.slot,
+ this.slotOrder,
+ this.conflictOrder);
+ }
+ }
+
+}
diff --git a/java/com/android/incallui/incall/impl/ButtonChooser.java b/java/com/android/incallui/incall/impl/ButtonChooser.java
new file mode 100644
index 000000000..55b82f015
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/ButtonChooser.java
@@ -0,0 +1,114 @@
+/*
+ * 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.incallui.incall.impl;
+
+import android.support.annotation.NonNull;
+import com.android.dialer.common.Assert;
+import com.android.incallui.incall.protocol.InCallButtonIds;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * Determines where logical buttons should be placed in the {@link InCallFragment} based on the
+ * provided mapping.
+ *
+ * <p>The button placement returned by a call to {@link #getButtonPlacement(int, Set)} is created as
+ * follows: one button is placed at each UI slot, using the provided mapping to resolve conflicts.
+ * Any allowed buttons that were not chosen for their desired slot are filled in at the end of the
+ * list until it becomes the proper size.
+ */
+@Immutable
+final class ButtonChooser {
+
+ private final MappedButtonConfig config;
+
+ public ButtonChooser(@NonNull MappedButtonConfig config) {
+ this.config = Assert.isNotNull(config);
+ }
+
+ /**
+ * Returns the buttons that should be shown in the {@link InCallFragment}, ordered appropriately.
+ *
+ * @param numUiButtons the number of ui buttons available.
+ * @param allowedButtons the {@link InCallButtonIds} that can be shown.
+ * @param disabledButtons the {@link InCallButtonIds} that can be shown but in disabled stats.
+ * @return an immutable list whose size is at most {@code numUiButtons}, containing the buttons to
+ * show.
+ */
+ @NonNull
+ public List<Integer> getButtonPlacement(
+ int numUiButtons,
+ @NonNull Set<Integer> allowedButtons,
+ @NonNull Set<Integer> disabledButtons) {
+ Assert.isNotNull(allowedButtons);
+ Assert.checkArgument(numUiButtons >= 0);
+
+ if (numUiButtons == 0 || allowedButtons.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ List<Integer> placedButtons = new ArrayList<>();
+ List<Integer> conflicts = new ArrayList<>();
+ placeButtonsInSlots(numUiButtons, allowedButtons, placedButtons, conflicts);
+ placeConflictsInOpenSlots(
+ numUiButtons, allowedButtons, disabledButtons, placedButtons, conflicts);
+ return Collections.unmodifiableList(placedButtons);
+ }
+
+ private void placeButtonsInSlots(
+ int numUiButtons,
+ @NonNull Set<Integer> allowedButtons,
+ @NonNull List<Integer> placedButtons,
+ @NonNull List<Integer> conflicts) {
+ List<Integer> configuredSlots = config.getOrderedMappedSlots();
+ for (int i = 0; i < configuredSlots.size() && placedButtons.size() < numUiButtons; ++i) {
+ int slotNumber = configuredSlots.get(i);
+ List<Integer> potentialButtons = config.getButtonsForSlot(slotNumber);
+ Collections.sort(potentialButtons, config.getSlotComparator());
+ for (int j = 0; j < potentialButtons.size(); ++j) {
+ if (allowedButtons.contains(potentialButtons.get(j))) {
+ placedButtons.add(potentialButtons.get(j));
+ conflicts.addAll(potentialButtons.subList(j + 1, potentialButtons.size()));
+ break;
+ }
+ }
+ }
+ }
+
+ private void placeConflictsInOpenSlots(
+ int numUiButtons,
+ @NonNull Set<Integer> allowedButtons,
+ @NonNull Set<Integer> disabledButtons,
+ @NonNull List<Integer> placedButtons,
+ @NonNull List<Integer> conflicts) {
+ Collections.sort(conflicts, config.getConflictComparator());
+ for (Integer conflict : conflicts) {
+ if (placedButtons.size() >= numUiButtons) {
+ return;
+ }
+ // If the conflict button is allowed but disabled, don't place it since it probably will
+ // move when it's enabled.
+ if (!allowedButtons.contains(conflict) || disabledButtons.contains(conflict)) {
+ continue;
+ }
+ placedButtons.add(conflict);
+ }
+ }
+}
diff --git a/java/com/android/incallui/incall/impl/ButtonChooserFactory.java b/java/com/android/incallui/incall/impl/ButtonChooserFactory.java
new file mode 100644
index 000000000..1b168a6f7
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/ButtonChooserFactory.java
@@ -0,0 +1,100 @@
+/*
+ * 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.incallui.incall.impl;
+
+import android.support.v4.util.ArrayMap;
+import android.telephony.TelephonyManager;
+import com.android.incallui.incall.impl.MappedButtonConfig.MappingInfo;
+import com.android.incallui.incall.protocol.InCallButtonIds;
+import java.util.Map;
+
+/**
+ * Creates {@link ButtonChooser} objects, based on the current network and phone type.
+ */
+class ButtonChooserFactory {
+
+ /**
+ * Creates the appropriate {@link ButtonChooser} based on the given information.
+ *
+ * @param voiceNetworkType the result of a call to {@link TelephonyManager#getVoiceNetworkType()}.
+ * @param isWiFi {@code true} if the call is made over WiFi, {@code false} otherwise.
+ * @param phoneType the result of a call to {@link TelephonyManager#getPhoneType()}.
+ * @return the ButtonChooser.
+ */
+ public static ButtonChooser newButtonChooser(
+ int voiceNetworkType, boolean isWiFi, int phoneType) {
+ if (voiceNetworkType == TelephonyManager.NETWORK_TYPE_LTE || isWiFi) {
+ return newImsAndWiFiButtonChooser();
+ }
+
+ if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
+ return newCdmaButtonChooser();
+ }
+
+ if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
+ return newGsmButtonChooser();
+ }
+
+ return newImsAndWiFiButtonChooser();
+ }
+
+ private static ButtonChooser newImsAndWiFiButtonChooser() {
+ Map<Integer, MappingInfo> mapping = createCommonMapping();
+ mapping.put(
+ InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE,
+ MappingInfo.builder(4).setSlotOrder(0).build());
+ mapping.put(
+ InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO, MappingInfo.builder(4).setSlotOrder(10).build());
+ mapping.put(
+ InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY, MappingInfo.builder(5).setSlotOrder(0).build());
+ mapping.put(InCallButtonIds.BUTTON_HOLD, MappingInfo.builder(5).setSlotOrder(10).build());
+
+ return new ButtonChooser(new MappedButtonConfig(mapping));
+ }
+
+ private static ButtonChooser newCdmaButtonChooser() {
+ Map<Integer, MappingInfo> mapping = createCommonMapping();
+ mapping.put(
+ InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE,
+ MappingInfo.builder(4).setSlotOrder(0).build());
+ mapping.put(InCallButtonIds.BUTTON_SWAP, MappingInfo.builder(5).setSlotOrder(0).build());
+
+ return new ButtonChooser(new MappedButtonConfig(mapping));
+ }
+
+ private static ButtonChooser newGsmButtonChooser() {
+ Map<Integer, MappingInfo> mapping = createCommonMapping();
+ mapping.put(
+ InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY, MappingInfo.builder(4).setSlotOrder(0).build());
+ mapping.put(
+ InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE,
+ MappingInfo.builder(4).setSlotOrder(10).build());
+ mapping.put(InCallButtonIds.BUTTON_HOLD, MappingInfo.builder(5).setSlotOrder(0).build());
+
+ return new ButtonChooser(new MappedButtonConfig(mapping));
+ }
+
+ private static Map<Integer, MappingInfo> createCommonMapping() {
+ Map<Integer, MappingInfo> mapping = new ArrayMap<>();
+ mapping.put(InCallButtonIds.BUTTON_MUTE, MappingInfo.builder(0).build());
+ mapping.put(InCallButtonIds.BUTTON_DIALPAD, MappingInfo.builder(1).build());
+ mapping.put(InCallButtonIds.BUTTON_AUDIO, MappingInfo.builder(2).build());
+ mapping.put(InCallButtonIds.BUTTON_MERGE, MappingInfo.builder(3).setSlotOrder(0).build());
+ mapping.put(InCallButtonIds.BUTTON_ADD_CALL, MappingInfo.builder(3).build());
+ return mapping;
+ }
+}
diff --git a/java/com/android/incallui/incall/impl/ButtonController.java b/java/com/android/incallui/incall/impl/ButtonController.java
new file mode 100644
index 000000000..95a38be44
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/ButtonController.java
@@ -0,0 +1,584 @@
+/*
+ * 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.incallui.incall.impl;
+
+import android.support.annotation.CallSuper;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import android.telecom.CallAudioState;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.View.OnClickListener;
+import com.android.dialer.common.Assert;
+import com.android.incallui.incall.impl.CheckableLabeledButton.OnCheckedChangeListener;
+import com.android.incallui.incall.protocol.InCallButtonIds;
+import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
+import com.android.incallui.incall.protocol.InCallScreenDelegate;
+
+/** Manages a single button. */
+interface ButtonController {
+
+ boolean isEnabled();
+
+ void setEnabled(boolean isEnabled);
+
+ boolean isAllowed();
+
+ void setAllowed(boolean isAllowed);
+
+ void setChecked(boolean isChecked);
+
+ @InCallButtonIds
+ int getInCallButtonId();
+
+ void setButton(CheckableLabeledButton button);
+
+ final class Controllers {
+
+ private static void resetButton(CheckableLabeledButton button) {
+ if (button != null) {
+ button.setOnCheckedChangeListener(null);
+ button.setOnClickListener(null);
+ }
+ }
+ }
+
+ abstract class CheckableButtonController implements ButtonController, OnCheckedChangeListener {
+
+ @NonNull protected final InCallButtonUiDelegate delegate;
+ @InCallButtonIds protected final int buttonId;
+ @StringRes protected final int checkedDescription;
+ @StringRes protected final int uncheckedDescription;
+ protected boolean isEnabled;
+ protected boolean isAllowed;
+ protected boolean isChecked;
+ protected CheckableLabeledButton button;
+
+ protected CheckableButtonController(
+ @NonNull InCallButtonUiDelegate delegate,
+ @InCallButtonIds int buttonId,
+ @StringRes int checkedContentDescription,
+ @StringRes int uncheckedContentDescription) {
+ Assert.isNotNull(delegate);
+ this.delegate = delegate;
+ this.buttonId = buttonId;
+ this.checkedDescription = checkedContentDescription;
+ this.uncheckedDescription = uncheckedContentDescription;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return isEnabled;
+ }
+
+ @Override
+ public void setEnabled(boolean isEnabled) {
+ this.isEnabled = isEnabled;
+ if (button != null) {
+ button.setEnabled(isEnabled);
+ }
+ }
+
+ @Override
+ public boolean isAllowed() {
+ return isAllowed;
+ }
+
+ @Override
+ public void setAllowed(boolean isAllowed) {
+ this.isAllowed = isAllowed;
+ if (button != null) {
+ button.setVisibility(isAllowed ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+
+ @Override
+ public void setChecked(boolean isChecked) {
+ this.isChecked = isChecked;
+ if (button != null) {
+ button.setChecked(isChecked);
+ }
+ }
+
+ @Override
+ @InCallButtonIds
+ public int getInCallButtonId() {
+ return buttonId;
+ }
+
+ @Override
+ @CallSuper
+ public void setButton(CheckableLabeledButton button) {
+ Controllers.resetButton(this.button);
+
+ this.button = button;
+ if (button != null) {
+ button.setEnabled(isEnabled);
+ button.setVisibility(isAllowed ? View.VISIBLE : View.INVISIBLE);
+ button.setChecked(isChecked);
+ button.setOnClickListener(null);
+ button.setOnCheckedChangeListener(this);
+ button.setContentDescription(
+ button.getContext().getText(isChecked ? checkedDescription : uncheckedDescription));
+ button.setShouldShowMoreIndicator(false);
+ }
+ }
+
+ @Override
+ public void onCheckedChanged(CheckableLabeledButton checkableLabeledButton, boolean isChecked) {
+ button.setContentDescription(
+ button.getContext().getText(isChecked ? checkedDescription : uncheckedDescription));
+ doCheckedChanged(isChecked);
+ }
+
+ protected abstract void doCheckedChanged(boolean isChecked);
+ }
+
+ abstract class SimpleCheckableButtonController extends CheckableButtonController {
+
+ @StringRes private final int label;
+ @DrawableRes private final int icon;
+
+ protected SimpleCheckableButtonController(
+ @NonNull InCallButtonUiDelegate delegate,
+ @InCallButtonIds int buttonId,
+ @StringRes int checkedContentDescription,
+ @StringRes int uncheckedContentDescription,
+ @StringRes int label,
+ @DrawableRes int icon) {
+ super(
+ delegate,
+ buttonId,
+ checkedContentDescription == 0 ? label : checkedContentDescription,
+ uncheckedContentDescription == 0 ? label : uncheckedContentDescription);
+ this.label = label;
+ this.icon = icon;
+ }
+
+ @Override
+ @CallSuper
+ public void setButton(CheckableLabeledButton button) {
+ super.setButton(button);
+ if (button != null) {
+ button.setLabelText(label);
+ button.setIconDrawable(icon);
+ }
+ }
+ }
+
+ abstract class NonCheckableButtonController implements ButtonController, OnClickListener {
+
+ protected final InCallButtonUiDelegate delegate;
+ @InCallButtonIds protected final int buttonId;
+ @StringRes protected final int contentDescription;
+ protected boolean isEnabled;
+ protected boolean isAllowed;
+ protected CheckableLabeledButton button;
+
+ protected NonCheckableButtonController(
+ InCallButtonUiDelegate delegate,
+ @InCallButtonIds int buttonId,
+ @StringRes int contentDescription) {
+ this.delegate = delegate;
+ this.buttonId = buttonId;
+ this.contentDescription = contentDescription;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return isEnabled;
+ }
+
+ @Override
+ public void setEnabled(boolean isEnabled) {
+ this.isEnabled = isEnabled;
+ if (button != null) {
+ button.setEnabled(isEnabled);
+ }
+ }
+
+ @Override
+ public boolean isAllowed() {
+ return isAllowed;
+ }
+
+ @Override
+ public void setAllowed(boolean isAllowed) {
+ this.isAllowed = isAllowed;
+ if (button != null) {
+ button.setVisibility(isAllowed ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+
+ @Override
+ public void setChecked(boolean isChecked) {
+ Assert.fail();
+ }
+
+ @Override
+ @InCallButtonIds
+ public int getInCallButtonId() {
+ return buttonId;
+ }
+
+ @Override
+ @CallSuper
+ public void setButton(CheckableLabeledButton button) {
+ Controllers.resetButton(this.button);
+
+ this.button = button;
+ if (button != null) {
+ button.setEnabled(isEnabled);
+ button.setVisibility(isAllowed ? View.VISIBLE : View.INVISIBLE);
+ button.setChecked(false);
+ button.setOnCheckedChangeListener(null);
+ button.setOnClickListener(this);
+ button.setContentDescription(button.getContext().getText(contentDescription));
+ button.setShouldShowMoreIndicator(false);
+ }
+ }
+ }
+
+ abstract class SimpleNonCheckableButtonController extends NonCheckableButtonController {
+
+ @StringRes private final int label;
+ @DrawableRes private final int icon;
+
+ protected SimpleNonCheckableButtonController(
+ InCallButtonUiDelegate delegate,
+ @InCallButtonIds int buttonId,
+ @StringRes int contentDescription,
+ @StringRes int label,
+ @DrawableRes int icon) {
+ super(delegate, buttonId, contentDescription == 0 ? label : contentDescription);
+ this.label = label;
+ this.icon = icon;
+ }
+
+ @Override
+ @CallSuper
+ public void setButton(CheckableLabeledButton button) {
+ super.setButton(button);
+ if (button != null) {
+ button.setLabelText(label);
+ button.setIconDrawable(icon);
+ }
+ }
+ }
+
+ class MuteButtonController extends SimpleCheckableButtonController {
+
+ public MuteButtonController(InCallButtonUiDelegate delegate) {
+ super(
+ delegate,
+ InCallButtonIds.BUTTON_MUTE,
+ R.string.incall_content_description_muted,
+ R.string.incall_content_description_unmuted,
+ R.string.incall_label_mute,
+ R.drawable.quantum_ic_mic_off_white_36);
+ }
+
+ @Override
+ public void doCheckedChanged(boolean isChecked) {
+ delegate.muteClicked(isChecked);
+ }
+ }
+
+ class SpeakerButtonController
+ implements ButtonController, OnCheckedChangeListener, OnClickListener {
+
+ @NonNull private final InCallButtonUiDelegate delegate;
+ private boolean isEnabled;
+ private boolean isAllowed;
+ private boolean isChecked;
+ private CheckableLabeledButton button;
+
+ @StringRes private int label = R.string.incall_label_speaker;
+ @DrawableRes private int icon = R.drawable.quantum_ic_volume_up_white_36;
+ private boolean checkable;
+ private CharSequence contentDescription;
+ private CharSequence checkedContentDescription;
+ private CharSequence uncheckedContentDescription;
+
+ public SpeakerButtonController(@NonNull InCallButtonUiDelegate delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return isEnabled;
+ }
+
+ @Override
+ public void setEnabled(boolean isEnabled) {
+ this.isEnabled = isEnabled;
+ if (button != null) {
+ button.setEnabled(isEnabled && isAllowed);
+ }
+ }
+
+ @Override
+ public boolean isAllowed() {
+ return isAllowed;
+ }
+
+ @Override
+ public void setAllowed(boolean isAllowed) {
+ this.isAllowed = isAllowed;
+ if (button != null) {
+ button.setEnabled(isEnabled && isAllowed);
+ }
+ }
+
+ @Override
+ public void setChecked(boolean isChecked) {
+ this.isChecked = isChecked;
+ if (button != null) {
+ button.setChecked(isChecked);
+ }
+ }
+
+ @Override
+ public int getInCallButtonId() {
+ return InCallButtonIds.BUTTON_AUDIO;
+ }
+
+ @Override
+ public void setButton(CheckableLabeledButton button) {
+ this.button = button;
+ if (button != null) {
+ button.setEnabled(isEnabled && isAllowed);
+ button.setVisibility(View.VISIBLE);
+ button.setChecked(isChecked);
+ button.setOnClickListener(checkable ? null : this);
+ button.setOnCheckedChangeListener(checkable ? this : null);
+ button.setLabelText(label);
+ button.setIconDrawable(icon);
+ button.setContentDescription(
+ isChecked ? checkedContentDescription : uncheckedContentDescription);
+ button.setShouldShowMoreIndicator(!checkable);
+ }
+ }
+
+ public void setAudioState(CallAudioState audioState) {
+ @StringRes int contentDescriptionResId;
+ if ((audioState.getSupportedRouteMask() & CallAudioState.ROUTE_BLUETOOTH)
+ == CallAudioState.ROUTE_BLUETOOTH) {
+ checkable = false;
+ isChecked = false;
+ label = R.string.incall_label_audio;
+
+ if ((audioState.getRoute() & CallAudioState.ROUTE_BLUETOOTH)
+ == CallAudioState.ROUTE_BLUETOOTH) {
+ icon = R.drawable.quantum_ic_bluetooth_audio_white_36;
+ contentDescriptionResId = R.string.incall_content_description_bluetooth;
+ } else if ((audioState.getRoute() & CallAudioState.ROUTE_SPEAKER)
+ == CallAudioState.ROUTE_SPEAKER) {
+ icon = R.drawable.quantum_ic_volume_up_white_36;
+ contentDescriptionResId = R.string.incall_content_description_speaker;
+ } else if ((audioState.getRoute() & CallAudioState.ROUTE_WIRED_HEADSET)
+ == CallAudioState.ROUTE_WIRED_HEADSET) {
+ icon = R.drawable.quantum_ic_headset_white_36;
+ contentDescriptionResId = R.string.incall_content_description_headset;
+ } else {
+ icon = R.drawable.ic_phone_audio_white_36dp;
+ contentDescriptionResId = R.string.incall_content_description_earpiece;
+ }
+ } else {
+ checkable = true;
+ isChecked = audioState.getRoute() == CallAudioState.ROUTE_SPEAKER;
+ label = R.string.incall_label_speaker;
+ icon = R.drawable.quantum_ic_volume_up_white_36;
+ contentDescriptionResId = R.string.incall_content_description_speaker;
+ }
+
+ contentDescription = delegate.getContext().getText(contentDescriptionResId);
+ checkedContentDescription =
+ TextUtils.concat(
+ contentDescription,
+ delegate.getContext().getText(R.string.incall_talkback_speaker_on));
+ uncheckedContentDescription =
+ TextUtils.concat(
+ contentDescription,
+ delegate.getContext().getText(R.string.incall_talkback_speaker_off));
+ setButton(button);
+ }
+
+ @Override
+ public void onClick(View v) {
+ delegate.showAudioRouteSelector();
+ }
+
+ @Override
+ public void onCheckedChanged(CheckableLabeledButton checkableLabeledButton, boolean isChecked) {
+ checkableLabeledButton.setContentDescription(
+ isChecked ? checkedContentDescription : uncheckedContentDescription);
+ delegate.toggleSpeakerphone();
+ }
+ }
+
+ class DialpadButtonController extends SimpleCheckableButtonController {
+
+ public DialpadButtonController(@NonNull InCallButtonUiDelegate delegate) {
+ super(
+ delegate,
+ InCallButtonIds.BUTTON_DIALPAD,
+ 0,
+ 0,
+ R.string.incall_label_dialpad,
+ R.drawable.quantum_ic_dialpad_white_36);
+ }
+
+ @Override
+ public void doCheckedChanged(boolean isChecked) {
+ delegate.showDialpadClicked(isChecked);
+ }
+ }
+
+ class HoldButtonController extends SimpleCheckableButtonController {
+
+ public HoldButtonController(@NonNull InCallButtonUiDelegate delegate) {
+ super(
+ delegate,
+ InCallButtonIds.BUTTON_HOLD,
+ R.string.incall_content_description_unhold,
+ R.string.incall_content_description_hold,
+ R.string.incall_label_hold,
+ R.drawable.quantum_ic_pause_white_36);
+ }
+
+ @Override
+ public void doCheckedChanged(boolean isChecked) {
+ delegate.holdClicked(isChecked);
+ }
+ }
+
+ class AddCallButtonController extends SimpleNonCheckableButtonController {
+
+ public AddCallButtonController(@NonNull InCallButtonUiDelegate delegate) {
+ super(
+ delegate,
+ InCallButtonIds.BUTTON_ADD_CALL,
+ 0,
+ R.string.incall_label_add_call,
+ R.drawable.ic_addcall_white);
+ Assert.isNotNull(delegate);
+ }
+
+ @Override
+ public void onClick(View view) {
+ delegate.addCallClicked();
+ }
+ }
+
+ class SwapButtonController extends SimpleNonCheckableButtonController {
+
+ public SwapButtonController(@NonNull InCallButtonUiDelegate delegate) {
+ super(
+ delegate,
+ InCallButtonIds.BUTTON_SWAP,
+ R.string.incall_content_description_swap_calls,
+ R.string.incall_label_swap,
+ R.drawable.quantum_ic_swap_calls_white_36);
+ Assert.isNotNull(delegate);
+ }
+
+ @Override
+ public void onClick(View view) {
+ delegate.swapClicked();
+ }
+ }
+
+ class MergeButtonController extends SimpleNonCheckableButtonController {
+
+ public MergeButtonController(@NonNull InCallButtonUiDelegate delegate) {
+ super(
+ delegate,
+ InCallButtonIds.BUTTON_MERGE,
+ R.string.incall_content_description_merge_calls,
+ R.string.incall_label_merge,
+ R.drawable.quantum_ic_call_merge_white_36);
+ Assert.isNotNull(delegate);
+ }
+
+ @Override
+ public void onClick(View view) {
+ delegate.mergeClicked();
+ }
+ }
+
+ class UpgradeToVideoButtonController extends SimpleNonCheckableButtonController {
+
+ public UpgradeToVideoButtonController(@NonNull InCallButtonUiDelegate delegate) {
+ super(
+ delegate,
+ InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO,
+ 0,
+ R.string.incall_label_videocall,
+ R.drawable.quantum_ic_videocam_white_36);
+ Assert.isNotNull(delegate);
+ }
+
+ @Override
+ public void onClick(View view) {
+ delegate.changeToVideoClicked();
+ }
+ }
+
+ class ManageConferenceButtonController extends SimpleNonCheckableButtonController {
+
+ private final InCallScreenDelegate inCallScreenDelegate;
+
+ public ManageConferenceButtonController(@NonNull InCallScreenDelegate inCallScreenDelegate) {
+ super(
+ null,
+ InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE,
+ R.string.a11y_description_incall_label_manage_content,
+ R.string.incall_label_manage,
+ R.drawable.quantum_ic_group_white_36);
+ Assert.isNotNull(inCallScreenDelegate);
+ this.inCallScreenDelegate = inCallScreenDelegate;
+ }
+
+ @Override
+ public void onClick(View view) {
+ inCallScreenDelegate.onManageConferenceClicked();
+ }
+ }
+
+ class SwitchToSecondaryButtonController extends SimpleNonCheckableButtonController {
+
+ private final InCallScreenDelegate inCallScreenDelegate;
+
+ public SwitchToSecondaryButtonController(InCallScreenDelegate inCallScreenDelegate) {
+ super(
+ null,
+ InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY,
+ R.string.incall_content_description_swap_calls,
+ R.string.incall_label_swap,
+ R.drawable.quantum_ic_swap_calls_white_36);
+ Assert.isNotNull(inCallScreenDelegate);
+ this.inCallScreenDelegate = inCallScreenDelegate;
+ }
+
+ @Override
+ public void onClick(View view) {
+ inCallScreenDelegate.onSecondaryInfoClicked();
+ }
+ }
+}
diff --git a/java/com/android/incallui/incall/impl/CheckableLabeledButton.java b/java/com/android/incallui/incall/impl/CheckableLabeledButton.java
new file mode 100644
index 000000000..a681adcb4
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/CheckableLabeledButton.java
@@ -0,0 +1,286 @@
+/*
+ * 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.incallui.incall.impl;
+
+import android.animation.AnimatorInflater;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.StringRes;
+import android.text.TextUtils.TruncateAt;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.SoundEffectConstants;
+import android.widget.Checkable;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/** A button to show on the incall screen */
+public class CheckableLabeledButton extends LinearLayout implements Checkable {
+
+ private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
+ private static final float DISABLED_STATE_OPACITY = .3f;
+ private boolean broadcasting;
+ private boolean isChecked;
+ private OnCheckedChangeListener onCheckedChangeListener;
+ private ImageView iconView;
+ private TextView labelView;
+ private Drawable background;
+ private Drawable backgroundMore;
+
+ public CheckableLabeledButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs);
+ }
+
+ public CheckableLabeledButton(Context context) {
+ this(context, null);
+ }
+
+ private void init(Context context, AttributeSet attrs) {
+ setOrientation(VERTICAL);
+ setGravity(Gravity.CENTER_HORIZONTAL);
+ Drawable icon;
+ CharSequence labelText;
+ boolean enabled;
+
+ backgroundMore = getResources().getDrawable(R.drawable.incall_button_background_more, null);
+ background = getResources().getDrawable(R.drawable.incall_button_background, null);
+
+ TypedArray typedArray =
+ context.obtainStyledAttributes(attrs, R.styleable.CheckableLabeledButton);
+ icon = typedArray.getDrawable(R.styleable.CheckableLabeledButton_incall_icon);
+ labelText = typedArray.getString(R.styleable.CheckableLabeledButton_incall_labelText);
+ enabled = typedArray.getBoolean(R.styleable.CheckableLabeledButton_android_enabled, true);
+ typedArray.recycle();
+
+ int paddingSize = getResources().getDimensionPixelOffset(R.dimen.incall_button_padding);
+ setPadding(paddingSize, paddingSize, paddingSize, paddingSize);
+
+ int iconSize = getResources().getDimensionPixelSize(R.dimen.incall_labeled_button_size);
+
+ iconView = new ImageView(context, null, android.R.style.Widget_Material_Button_Colored);
+ LayoutParams iconParams = generateDefaultLayoutParams();
+ iconParams.width = iconSize;
+ iconParams.height = iconSize;
+ iconView.setLayoutParams(iconParams);
+ iconView.setScaleType(ScaleType.CENTER_INSIDE);
+ iconView.setImageDrawable(icon);
+ iconView.setImageTintMode(Mode.SRC_IN);
+ iconView.setImageTintList(getResources().getColorStateList(R.color.incall_button_icon, null));
+ iconView.setBackground(getResources().getDrawable(R.drawable.incall_button_background, null));
+ iconView.setDuplicateParentStateEnabled(true);
+ iconView.setElevation(getResources().getDimension(R.dimen.incall_button_elevation));
+ iconView.setStateListAnimator(
+ AnimatorInflater.loadStateListAnimator(context, R.animator.incall_button_elevation));
+ addView(iconView);
+
+ labelView = new TextView(context);
+ LayoutParams labelParams = generateDefaultLayoutParams();
+ labelParams.width = LayoutParams.WRAP_CONTENT;
+ labelParams.height = LayoutParams.WRAP_CONTENT;
+ labelParams.topMargin =
+ context.getResources().getDimensionPixelOffset(R.dimen.incall_button_label_margin);
+ labelView.setLayoutParams(labelParams);
+ labelView.setTextAppearance(R.style.Dialer_Incall_TextAppearance_Label);
+ labelView.setText(labelText);
+ labelView.setSingleLine();
+ labelView.setMaxEms(9);
+ labelView.setEllipsize(TruncateAt.END);
+ labelView.setGravity(Gravity.CENTER);
+ labelView.setDuplicateParentStateEnabled(true);
+ addView(labelView);
+
+ setFocusable(true);
+ setClickable(true);
+ setEnabled(enabled);
+ setOutlineProvider(null);
+ }
+
+ @Override
+ public void refreshDrawableState() {
+ super.refreshDrawableState();
+ iconView.setAlpha(isEnabled() ? 1f : DISABLED_STATE_OPACITY);
+ labelView.setAlpha(isEnabled() ? 1f : DISABLED_STATE_OPACITY);
+ }
+
+ public void setIconDrawable(@DrawableRes int drawableRes) {
+ iconView.setImageResource(drawableRes);
+ }
+
+ public void setLabelText(@StringRes int stringRes) {
+ labelView.setText(stringRes);
+ }
+
+ /** Shows or hides a little down arrow to indicate that the button will pop up a menu. */
+ public void setShouldShowMoreIndicator(boolean shouldShow) {
+ iconView.setBackground(shouldShow ? backgroundMore : background);
+ }
+
+ @Override
+ public boolean isChecked() {
+ return isChecked;
+ }
+
+ @Override
+ public void setChecked(boolean checked) {
+ performSetChecked(checked);
+ }
+
+ @Override
+ public void toggle() {
+ userRequestedSetChecked(!isChecked());
+ }
+
+ @Override
+ public int[] onCreateDrawableState(int extraSpace) {
+ final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
+ if (isChecked()) {
+ mergeDrawableStates(drawableState, CHECKED_STATE_SET);
+ }
+ return drawableState;
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+ invalidate();
+ }
+
+ public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
+ this.onCheckedChangeListener = listener;
+ }
+
+ @Override
+ public boolean performClick() {
+ if (!isCheckable()) {
+ return super.performClick();
+ }
+
+ toggle();
+ final boolean handled = super.performClick();
+ if (!handled) {
+ // View only makes a sound effect if the onClickListener was
+ // called, so we'll need to make one here instead.
+ playSoundEffect(SoundEffectConstants.CLICK);
+ }
+ return handled;
+ }
+
+ private boolean isCheckable() {
+ return onCheckedChangeListener != null;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState savedState = (SavedState) state;
+ super.onRestoreInstanceState(savedState.getSuperState());
+ performSetChecked(savedState.isChecked);
+ requestLayout();
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ return new SavedState(isChecked(), super.onSaveInstanceState());
+ }
+
+ /**
+ * Called when the state of the button should be updated, this should not be the result of user
+ * interaction.
+ *
+ * @param checked {@code true} if the button should be in the checked state, {@code false}
+ * otherwise.
+ */
+ private void performSetChecked(boolean checked) {
+ if (isChecked() == checked) {
+ return;
+ }
+ isChecked = checked;
+ refreshDrawableState();
+ }
+
+ /**
+ * Called when the user interacts with a button. This should not result in the button updating
+ * state, rather the request should be propagated to the associated listener.
+ *
+ * @param checked {@code true} if the button should be in the checked state, {@code false}
+ * otherwise.
+ */
+ private void userRequestedSetChecked(boolean checked) {
+ if (isChecked() == checked) {
+ return;
+ }
+ if (broadcasting) {
+ return;
+ }
+ broadcasting = true;
+ if (onCheckedChangeListener != null) {
+ onCheckedChangeListener.onCheckedChanged(this, checked);
+ }
+ broadcasting = false;
+ }
+
+ /** Callback interface to notify when the button's checked state has changed */
+ public interface OnCheckedChangeListener {
+
+ void onCheckedChanged(CheckableLabeledButton checkableLabeledButton, boolean isChecked);
+ }
+
+ private static class SavedState extends BaseSavedState {
+
+ public static final Creator<SavedState> CREATOR =
+ new Creator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ public final boolean isChecked;
+
+ private SavedState(boolean isChecked, Parcelable superState) {
+ super(superState);
+ this.isChecked = isChecked;
+ }
+
+ protected SavedState(Parcel in) {
+ super(in);
+ isChecked = in.readByte() != 0;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeByte((byte) (isChecked ? 1 : 0));
+ }
+ }
+}
diff --git a/java/com/android/incallui/incall/impl/InCallButtonGridFragment.java b/java/com/android/incallui/incall/impl/InCallButtonGridFragment.java
new file mode 100644
index 000000000..db0b5b9b8
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/InCallButtonGridFragment.java
@@ -0,0 +1,137 @@
+/*
+ * 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.incallui.incall.impl;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.util.ArraySet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.FragmentUtils;
+import com.android.incallui.incall.protocol.InCallButtonIds;
+import java.util.List;
+import java.util.Set;
+
+/** Fragment for the in call buttons (mute, speaker, ect.). */
+public class InCallButtonGridFragment extends Fragment {
+
+ private static final int BUTTON_COUNT = 6;
+ private static final int BUTTONS_PER_ROW = 3;
+
+ private CheckableLabeledButton[] buttons = new CheckableLabeledButton[BUTTON_COUNT];
+ private OnButtonGridCreatedListener buttonGridListener;
+
+ public static Fragment newInstance() {
+ return new InCallButtonGridFragment();
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle bundle) {
+ super.onCreate(bundle);
+ buttonGridListener = FragmentUtils.getParent(this, OnButtonGridCreatedListener.class);
+ Assert.isNotNull(buttonGridListener);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle bundle) {
+ View view = inflater.inflate(R.layout.incall_button_grid, parent, false);
+
+ buttons[0] = ((CheckableLabeledButton) view.findViewById(R.id.incall_first_button));
+ buttons[1] = ((CheckableLabeledButton) view.findViewById(R.id.incall_second_button));
+ buttons[2] = ((CheckableLabeledButton) view.findViewById(R.id.incall_third_button));
+ buttons[3] = ((CheckableLabeledButton) view.findViewById(R.id.incall_fourth_button));
+ buttons[4] = ((CheckableLabeledButton) view.findViewById(R.id.incall_fifth_button));
+ buttons[5] = ((CheckableLabeledButton) view.findViewById(R.id.incall_sixth_button));
+
+ return view;
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle bundle) {
+ super.onViewCreated(view, bundle);
+ buttonGridListener.onButtonGridCreated(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ buttonGridListener.onButtonGridDestroyed();
+ }
+
+ public void onInCallScreenDialpadVisibilityChange(boolean isShowing) {
+ for (CheckableLabeledButton button : buttons) {
+ button.setImportantForAccessibility(
+ isShowing
+ ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ }
+ }
+
+ public int updateButtonStates(
+ List<ButtonController> buttonControllers,
+ @Nullable ButtonChooser buttonChooser,
+ int voiceNetworkType,
+ int phoneType) {
+ Set<Integer> allowedButtons = new ArraySet<>();
+ Set<Integer> disabledButtons = new ArraySet<>();
+ for (ButtonController controller : buttonControllers) {
+ if (controller.isAllowed()) {
+ allowedButtons.add(controller.getInCallButtonId());
+ if (!controller.isEnabled()) {
+ disabledButtons.add(controller.getInCallButtonId());
+ }
+ }
+ }
+
+ for (ButtonController controller : buttonControllers) {
+ controller.setButton(null);
+ }
+
+ if (buttonChooser == null) {
+ buttonChooser =
+ ButtonChooserFactory.newButtonChooser(voiceNetworkType, false /* isWiFi */, phoneType);
+ }
+
+ int numVisibleButtons = getResources().getInteger(R.integer.incall_num_rows) * BUTTONS_PER_ROW;
+ List<Integer> buttonsToPlace =
+ buttonChooser.getButtonPlacement(numVisibleButtons, allowedButtons, disabledButtons);
+
+ for (int i = 0; i < BUTTON_COUNT; ++i) {
+ if (i >= buttonsToPlace.size()) {
+ buttons[i].setVisibility(View.INVISIBLE);
+ continue;
+ }
+ @InCallButtonIds int button = buttonsToPlace.get(i);
+ buttonGridListener.getButtonController(button).setButton(buttons[i]);
+ }
+
+ return numVisibleButtons;
+ }
+
+ /** Interface to let the listener know the status of the button grid. */
+ public interface OnButtonGridCreatedListener {
+ void onButtonGridCreated(InCallButtonGridFragment inCallButtonGridFragment);
+ void onButtonGridDestroyed();
+
+ ButtonController getButtonController(@InCallButtonIds int id);
+ }
+}
diff --git a/java/com/android/incallui/incall/impl/InCallFragment.java b/java/com/android/incallui/incall/impl/InCallFragment.java
new file mode 100644
index 000000000..ef8a1edd8
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/InCallFragment.java
@@ -0,0 +1,501 @@
+/*
+ * 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.incallui.incall.impl;
+
+import android.Manifest.permission;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.TabLayout;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.ViewPager;
+import android.telecom.CallAudioState;
+import android.telephony.TelephonyManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.Toast;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.FragmentUtils;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.multimedia.MultimediaData;
+import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment;
+import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment.AudioRouteSelectorPresenter;
+import com.android.incallui.contactgrid.ContactGridManager;
+import com.android.incallui.hold.OnHoldFragment;
+import com.android.incallui.incall.impl.ButtonController.SpeakerButtonController;
+import com.android.incallui.incall.impl.InCallButtonGridFragment.OnButtonGridCreatedListener;
+import com.android.incallui.incall.protocol.InCallButtonIds;
+import com.android.incallui.incall.protocol.InCallButtonIdsExtension;
+import com.android.incallui.incall.protocol.InCallButtonUi;
+import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
+import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
+import com.android.incallui.incall.protocol.InCallScreen;
+import com.android.incallui.incall.protocol.InCallScreenDelegate;
+import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
+import com.android.incallui.incall.protocol.PrimaryCallState;
+import com.android.incallui.incall.protocol.PrimaryInfo;
+import com.android.incallui.incall.protocol.SecondaryInfo;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Fragment that shows UI for an ongoing voice call. */
+public class InCallFragment extends Fragment
+ implements InCallScreen,
+ InCallButtonUi,
+ OnClickListener,
+ AudioRouteSelectorPresenter,
+ OnButtonGridCreatedListener {
+
+ private List<ButtonController> buttonControllers = new ArrayList<>();
+ private View endCallButton;
+ private TabLayout tabLayout;
+ private ViewPager pager;
+ private InCallPagerAdapter adapter;
+ private ContactGridManager contactGridManager;
+ private InCallScreenDelegate inCallScreenDelegate;
+ private InCallButtonUiDelegate inCallButtonUiDelegate;
+ private InCallButtonGridFragment inCallButtonGridFragment;
+ @Nullable private ButtonChooser buttonChooser;
+ private SecondaryInfo savedSecondaryInfo;
+ private int voiceNetworkType;
+ private int phoneType;
+ private boolean stateRestored;
+
+ private static boolean isSupportedButton(@InCallButtonIds int id) {
+ return id == InCallButtonIds.BUTTON_AUDIO
+ || id == InCallButtonIds.BUTTON_MUTE
+ || id == InCallButtonIds.BUTTON_DIALPAD
+ || id == InCallButtonIds.BUTTON_HOLD
+ || id == InCallButtonIds.BUTTON_SWAP
+ || id == InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO
+ || id == InCallButtonIds.BUTTON_ADD_CALL
+ || id == InCallButtonIds.BUTTON_MERGE
+ || id == InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ if (savedSecondaryInfo != null) {
+ setSecondary(savedSecondaryInfo);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ inCallButtonUiDelegate =
+ FragmentUtils.getParent(this, InCallButtonUiDelegateFactory.class)
+ .newInCallButtonUiDelegate();
+ if (savedInstanceState != null) {
+ inCallButtonUiDelegate.onRestoreInstanceState(savedInstanceState);
+ stateRestored = true;
+ }
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ @NonNull LayoutInflater layoutInflater,
+ @Nullable ViewGroup viewGroup,
+ @Nullable Bundle bundle) {
+ LogUtil.i("InCallFragment.onCreateView", null);
+ final View view = layoutInflater.inflate(R.layout.frag_incall_voice, viewGroup, false);
+ contactGridManager =
+ new ContactGridManager(
+ view,
+ (ImageView) view.findViewById(R.id.contactgrid_avatar),
+ getResources().getDimensionPixelSize(R.dimen.incall_avatar_size),
+ true /* showAnonymousAvatar */);
+
+ tabLayout = (TabLayout) view.findViewById(R.id.incall_tab_dots);
+ pager = (ViewPager) view.findViewById(R.id.incall_pager);
+
+ endCallButton = view.findViewById(R.id.incall_end_call);
+ endCallButton.setOnClickListener(this);
+
+ if (ContextCompat.checkSelfPermission(getContext(), permission.READ_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ } else {
+
+ voiceNetworkType =
+ VERSION.SDK_INT >= VERSION_CODES.N
+ ? getContext().getSystemService(TelephonyManager.class).getVoiceNetworkType()
+ : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+ phoneType = getContext().getSystemService(TelephonyManager.class).getPhoneType();
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ inCallButtonUiDelegate.refreshMuteState();
+ inCallScreenDelegate.onInCallScreenResumed();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle bundle) {
+ LogUtil.i("InCallFragment.onViewCreated", null);
+ super.onViewCreated(view, bundle);
+ inCallScreenDelegate =
+ FragmentUtils.getParent(this, InCallScreenDelegateFactory.class).newInCallScreenDelegate();
+ Assert.isNotNull(inCallScreenDelegate);
+
+ buttonControllers.add(new ButtonController.MuteButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(new ButtonController.SpeakerButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(new ButtonController.DialpadButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(new ButtonController.HoldButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(new ButtonController.AddCallButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(new ButtonController.SwapButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(new ButtonController.MergeButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(
+ new ButtonController.UpgradeToVideoButtonController(inCallButtonUiDelegate));
+ buttonControllers.add(
+ new ButtonController.ManageConferenceButtonController(inCallScreenDelegate));
+ buttonControllers.add(
+ new ButtonController.SwitchToSecondaryButtonController(inCallScreenDelegate));
+
+ inCallScreenDelegate.onInCallScreenDelegateInit(this);
+ inCallScreenDelegate.onInCallScreenReady();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ inCallScreenDelegate.onInCallScreenUnready();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ inCallButtonUiDelegate.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view == endCallButton) {
+ LogUtil.i("InCallFragment.onClick", "end call button clicked");
+ inCallScreenDelegate.onEndCallClicked();
+ } else {
+ LogUtil.e("InCallFragment.onClick", "unknown view: " + view);
+ Assert.fail();
+ }
+ }
+
+ @Override
+ public void setPrimary(@NonNull PrimaryInfo primaryInfo) {
+ LogUtil.i("InCallFragment.setPrimary", primaryInfo.toString());
+ if (adapter == null) {
+ initAdapter(primaryInfo.multimediaData);
+ }
+ contactGridManager.setPrimary(primaryInfo);
+
+ if (primaryInfo.shouldShowLocation) {
+ // Hide the avatar to make room for location
+ contactGridManager.setAvatarHidden(true);
+
+ // Need to widen the contact grid to fit location information
+ View contactGridView = getView().findViewById(R.id.incall_contact_grid);
+ ViewGroup.LayoutParams params = contactGridView.getLayoutParams();
+ if (params instanceof ViewGroup.MarginLayoutParams) {
+ ((ViewGroup.MarginLayoutParams) params).setMarginStart(0);
+ ((ViewGroup.MarginLayoutParams) params).setMarginEnd(0);
+ }
+ contactGridView.setLayoutParams(params);
+
+ // Need to let the dialpad move up a little further when location info is being shown
+ View dialpadView = getView().findViewById(R.id.incall_dialpad_container);
+ params = dialpadView.getLayoutParams();
+ if (params instanceof RelativeLayout.LayoutParams) {
+ ((RelativeLayout.LayoutParams) params).removeRule(RelativeLayout.BELOW);
+ }
+ dialpadView.setLayoutParams(params);
+ }
+ }
+
+ private void initAdapter(MultimediaData multimediaData) {
+ adapter = new InCallPagerAdapter(getChildFragmentManager(), multimediaData);
+ pager.setAdapter(adapter);
+
+ if (adapter.getCount() > 1) {
+ tabLayout.setVisibility(pager.getVisibility());
+ tabLayout.setupWithViewPager(pager, true);
+ if (!stateRestored) {
+ new Handler()
+ .postDelayed(
+ new Runnable() {
+ @Override
+ public void run() {
+ // In order to prevent user confusion and educate the user on our UI, we animate
+ // the view pager to the button grid after 2 seconds show them when the UI is
+ // that they are more familiar with.
+ pager.setCurrentItem(adapter.getButtonGridPosition());
+ }
+ },
+ 2000);
+ }
+ } else {
+ tabLayout.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void setSecondary(@NonNull SecondaryInfo secondaryInfo) {
+ LogUtil.i("InCallFragment.setSecondary", secondaryInfo.toString());
+ getButtonController(InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY)
+ .setEnabled(secondaryInfo.shouldShow);
+ getButtonController(InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY)
+ .setAllowed(secondaryInfo.shouldShow);
+ updateButtonStates();
+
+ if (!isAdded()) {
+ savedSecondaryInfo = secondaryInfo;
+ return;
+ }
+ savedSecondaryInfo = null;
+ FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
+ Fragment oldBanner = getChildFragmentManager().findFragmentById(R.id.incall_on_hold_banner);
+ if (secondaryInfo.shouldShow) {
+ transaction.replace(R.id.incall_on_hold_banner, OnHoldFragment.newInstance(secondaryInfo));
+ } else {
+ if (oldBanner != null) {
+ transaction.remove(oldBanner);
+ }
+ }
+ transaction.setCustomAnimations(R.anim.abc_slide_in_top, R.anim.abc_slide_out_top);
+ transaction.commitAllowingStateLoss();
+ }
+
+ @Override
+ public void setCallState(@NonNull PrimaryCallState primaryCallState) {
+ LogUtil.i("InCallFragment.setCallState", primaryCallState.toString());
+ contactGridManager.setCallState(primaryCallState);
+ buttonChooser =
+ ButtonChooserFactory.newButtonChooser(voiceNetworkType, primaryCallState.isWifi, phoneType);
+ updateButtonStates();
+ }
+
+ @Override
+ public void setEndCallButtonEnabled(boolean enabled, boolean animate) {
+ if (endCallButton != null) {
+ endCallButton.setEnabled(enabled);
+ }
+ }
+
+ @Override
+ public void showManageConferenceCallButton(boolean visible) {
+ getButtonController(InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE).setAllowed(visible);
+ getButtonController(InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE).setEnabled(visible);
+ updateButtonStates();
+ }
+
+ @Override
+ public boolean isManageConferenceVisible() {
+ return getButtonController(InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE).isAllowed();
+ }
+
+ @Override
+ public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ contactGridManager.dispatchPopulateAccessibilityEvent(event);
+ }
+
+ @Override
+ public void showNoteSentToast() {
+ LogUtil.i("InCallFragment.showNoteSentToast", null);
+ Toast.makeText(getContext(), R.string.incall_note_sent, Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void updateInCallScreenColors() {}
+
+ @Override
+ public void onInCallScreenDialpadVisibilityChange(boolean isShowing) {
+ LogUtil.i("InCallFragment.onInCallScreenDialpadVisibilityChange", "isShowing: " + isShowing);
+ // Take note that the dialpad button isShowing
+ getButtonController(InCallButtonIds.BUTTON_DIALPAD).setChecked(isShowing);
+
+ // This check is needed because there is a race condition where we attempt to update
+ // ButtonGridFragment before it is ready, so we check whether it is ready first and once it is
+ // ready, #onButtonGridCreated will mark the dialpad button as isShowing.
+ if (inCallButtonGridFragment != null) {
+ // Update the Android Button's state to isShowing.
+ inCallButtonGridFragment.onInCallScreenDialpadVisibilityChange(isShowing);
+ }
+ }
+
+ @Override
+ public int getAnswerAndDialpadContainerResourceId() {
+ return R.id.incall_dialpad_container;
+ }
+
+ @Override
+ public Fragment getInCallScreenFragment() {
+ return this;
+ }
+
+ @Override
+ public void showButton(@InCallButtonIds int buttonId, boolean show) {
+ LogUtil.v(
+ "InCallFragment.showButton",
+ "buttionId: %s, show: %b",
+ InCallButtonIdsExtension.toString(buttonId),
+ show);
+ if (isSupportedButton(buttonId)) {
+ getButtonController(buttonId).setAllowed(show);
+ }
+ }
+
+ @Override
+ public void enableButton(@InCallButtonIds int buttonId, boolean enable) {
+ LogUtil.v(
+ "InCallFragment.enableButton",
+ "buttonId: %s, enable: %b",
+ InCallButtonIdsExtension.toString(buttonId),
+ enable);
+ if (isSupportedButton(buttonId)) {
+ getButtonController(buttonId).setEnabled(enable);
+ }
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ LogUtil.v("InCallFragment.setEnabled", "enabled: " + enabled);
+ for (ButtonController buttonController : buttonControllers) {
+ buttonController.setEnabled(enabled);
+ }
+ }
+
+ @Override
+ public void setHold(boolean value) {
+ getButtonController(InCallButtonIds.BUTTON_HOLD).setChecked(value);
+ }
+
+ @Override
+ public void setCameraSwitched(boolean isBackFacingCamera) {}
+
+ @Override
+ public void setVideoPaused(boolean isPaused) {}
+
+ @Override
+ public void setAudioState(CallAudioState audioState) {
+ LogUtil.i("InCallFragment.setAudioState", "audioState: " + audioState);
+ ((SpeakerButtonController) getButtonController(InCallButtonIds.BUTTON_AUDIO))
+ .setAudioState(audioState);
+ getButtonController(InCallButtonIds.BUTTON_MUTE).setChecked(audioState.isMuted());
+ }
+
+ @Override
+ public void updateButtonStates() {
+ // When the incall screen is ready, this method is called from #setSecondary, even though the
+ // incall button ui is not ready yet. This method is called again once the incall button ui is
+ // ready though, so this operation is safe and will be executed asap.
+ if (inCallButtonGridFragment == null) {
+ return;
+ }
+ int numVisibleButtons =
+ inCallButtonGridFragment.updateButtonStates(
+ buttonControllers, buttonChooser, voiceNetworkType, phoneType);
+
+ int visibility = numVisibleButtons == 0 ? View.GONE : View.VISIBLE;
+ pager.setVisibility(visibility);
+ if (adapter != null && adapter.getCount() > 1) {
+ tabLayout.setVisibility(visibility);
+ }
+ }
+
+ @Override
+ public void updateInCallButtonUiColors() {}
+
+ @Override
+ public Fragment getInCallButtonUiFragment() {
+ return this;
+ }
+
+ @Override
+ public void showAudioRouteSelector() {
+ AudioRouteSelectorDialogFragment.newInstance(inCallButtonUiDelegate.getCurrentAudioState())
+ .show(getChildFragmentManager(), null);
+ }
+
+ @Override
+ public void onAudioRouteSelected(int audioRoute) {
+ inCallButtonUiDelegate.setAudioRoute(audioRoute);
+ }
+
+ @NonNull
+ @Override
+ public ButtonController getButtonController(@InCallButtonIds int id) {
+ for (ButtonController buttonController : buttonControllers) {
+ if (buttonController.getInCallButtonId() == id) {
+ return buttonController;
+ }
+ }
+ Assert.fail();
+ return null;
+ }
+
+ @Override
+ public void onButtonGridCreated(InCallButtonGridFragment inCallButtonGridFragment) {
+ LogUtil.i("InCallFragment.onButtonGridCreated", "InCallUiReady");
+ this.inCallButtonGridFragment = inCallButtonGridFragment;
+ inCallButtonUiDelegate.onInCallButtonUiReady(this);
+ updateButtonStates();
+ }
+
+ @Override
+ public void onButtonGridDestroyed() {
+ LogUtil.i("InCallFragment.onButtonGridCreated", "InCallUiUnready");
+ inCallButtonUiDelegate.onInCallButtonUiUnready();
+ this.inCallButtonGridFragment = null;
+ }
+
+ @Override
+ public boolean isShowingLocationUi() {
+ Fragment fragment = getChildFragmentManager().findFragmentById(R.id.incall_location_holder);
+ return fragment != null && fragment.isVisible();
+ }
+
+ @Override
+ public void showLocationUi(@Nullable Fragment locationUi) {
+ boolean isShowing = isShowingLocationUi();
+ if (!isShowing && locationUi != null) {
+ // Show the location fragment.
+ getChildFragmentManager()
+ .beginTransaction()
+ .replace(R.id.incall_location_holder, locationUi)
+ .commitAllowingStateLoss();
+ } else if (isShowing && locationUi == null) {
+ // Hide the location fragment
+ Fragment fragment = getChildFragmentManager().findFragmentById(R.id.incall_location_holder);
+ getChildFragmentManager().beginTransaction().remove(fragment).commitAllowingStateLoss();
+ }
+ }
+}
diff --git a/java/com/android/incallui/incall/impl/InCallPagerAdapter.java b/java/com/android/incallui/incall/impl/InCallPagerAdapter.java
new file mode 100644
index 000000000..50eb4c8c3
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/InCallPagerAdapter.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 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.incallui.incall.impl;
+
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.text.TextUtils;
+import com.android.dialer.multimedia.MultimediaData;
+import com.android.incallui.sessiondata.MultimediaFragment;
+
+/** View pager adapter for in call ui. */
+public class InCallPagerAdapter extends FragmentPagerAdapter {
+
+ @Nullable private final MultimediaData attachments;
+
+ public InCallPagerAdapter(FragmentManager fragmentManager, MultimediaData attachments) {
+ super(fragmentManager);
+ this.attachments = attachments;
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ if (position == getButtonGridPosition()) {
+ return InCallButtonGridFragment.newInstance();
+ } else {
+ // TODO: handle fragment invalidation for when the data changes.
+ return MultimediaFragment.newInstance(attachments, true, false);
+ }
+ }
+
+ @Override
+ public int getCount() {
+ if (attachments != null
+ && (!TextUtils.isEmpty(attachments.getSubject()) || attachments.hasImageData())) {
+ return 2;
+ }
+ return 1;
+ }
+
+ public int getButtonGridPosition() {
+ return getCount() - 1;
+ }
+}
diff --git a/java/com/android/incallui/incall/impl/MappedButtonConfig.java b/java/com/android/incallui/incall/impl/MappedButtonConfig.java
new file mode 100644
index 000000000..ecdb5dfea
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/MappedButtonConfig.java
@@ -0,0 +1,193 @@
+/*
+ * 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.incallui.incall.impl;
+
+import android.support.annotation.NonNull;
+import android.support.v4.util.ArrayMap;
+import android.util.ArraySet;
+import com.android.dialer.common.Assert;
+import com.android.incallui.incall.protocol.InCallButtonIds;
+import com.android.incallui.incall.protocol.InCallButtonIdsExtension;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * Determines logical button slot and ordering based on a provided mapping.
+ *
+ * <p>The provided mapping is declared with the following pieces of information: key, the {@link
+ * InCallButtonIds} for which the mapping applies; {@link MappingInfo#getSlot()}, the arbitrarily
+ * indexed slot into which the InCallButtonId desires to be placed; {@link
+ * MappingInfo#getSlotOrder()}, the slotOrder, used to choose the correct InCallButtonId when
+ * multiple desire to be placed in the same slot; and {@link MappingInfo#getConflictOrder()}, the
+ * conflictOrder, used to determine the overall order for InCallButtonIds that weren't chosen for
+ * their desired slot.
+ */
+@Immutable
+final class MappedButtonConfig {
+
+ @NonNull private final Map<Integer, MappingInfo> mapping;
+ @NonNull private final List<Integer> orderedMappedSlots;
+
+ /**
+ * Creates this MappedButtonConfig with the given mapping of {@link InCallButtonIds} to their
+ * corresponding slots and order.
+ *
+ * @param mapping the mapping.
+ */
+ public MappedButtonConfig(@NonNull Map<Integer, MappingInfo> mapping) {
+ this.mapping = new ArrayMap<>();
+ this.mapping.putAll(Assert.isNotNull(mapping));
+ this.orderedMappedSlots = findOrderedMappedSlots();
+ }
+
+ private List<Integer> findOrderedMappedSlots() {
+ Set<Integer> slots = new ArraySet<>();
+ for (Entry<Integer, MappingInfo> entry : mapping.entrySet()) {
+ slots.add(entry.getValue().getSlot());
+ }
+ List<Integer> orderedSlots = new ArrayList<>(slots);
+ Collections.sort(orderedSlots);
+ return orderedSlots;
+ }
+
+ /** Returns an immutable list of the slots for which this class has button mapping. */
+ @NonNull
+ public List<Integer> getOrderedMappedSlots() {
+ if (mapping.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return Collections.unmodifiableList(orderedMappedSlots);
+ }
+
+ /**
+ * Returns a list of {@link InCallButtonIds} that are configured to be placed in the given ui
+ * slot. The slot can be based from any index, as long as it matches the provided mapping.
+ */
+ @NonNull
+ public List<Integer> getButtonsForSlot(int slot) {
+ List<Integer> buttons = new ArrayList<>();
+ for (Entry<Integer, MappingInfo> entry : mapping.entrySet()) {
+ if (entry.getValue().getSlot() == slot) {
+ buttons.add(entry.getKey());
+ }
+ }
+ return buttons;
+ }
+
+ /**
+ * Returns a {@link Comparator} capable of ordering {@link InCallButtonIds} that are configured to
+ * be placed in the same slot. InCallButtonIds are sorted based on the natural ordering of {@link
+ * MappingInfo#getSlotOrder()}.
+ *
+ * <p>Note: the returned Comparator's compare method will throw an {@link
+ * IllegalArgumentException} if called with InCallButtonIds that have no configuration or are not
+ * to be placed in the same slot.
+ */
+ @NonNull
+ public Comparator<Integer> getSlotComparator() {
+ return new Comparator<Integer>() {
+ @Override
+ public int compare(Integer lhs, Integer rhs) {
+ MappingInfo lhsInfo = lookupMappingInfo(lhs);
+ MappingInfo rhsInfo = lookupMappingInfo(rhs);
+ if (lhsInfo.getSlot() != rhsInfo.getSlot()) {
+ throw new IllegalArgumentException("lhs and rhs don't go in the same slot");
+ }
+ return lhsInfo.getSlotOrder() - rhsInfo.getSlotOrder();
+ }
+ };
+ }
+
+ /**
+ * Returns a {@link Comparator} capable of ordering {@link InCallButtonIds} by their conflict
+ * score. This comparator should be used when multiple InCallButtonIds could have been shown in
+ * the same slot. InCallButtonIds are sorted based on the natural ordering of {@link
+ * MappingInfo#getConflictOrder()}.
+ *
+ * <p>Note: the returned Comparator's compare method will throw an {@link
+ * IllegalArgumentException} if called with InCallButtonIds that have no configuration.
+ */
+ @NonNull
+ public Comparator<Integer> getConflictComparator() {
+ return new Comparator<Integer>() {
+ @Override
+ public int compare(Integer lhs, Integer rhs) {
+ MappingInfo lhsInfo = lookupMappingInfo(lhs);
+ MappingInfo rhsInfo = lookupMappingInfo(rhs);
+ return lhsInfo.getConflictOrder() - rhsInfo.getConflictOrder();
+ }
+ };
+ }
+
+ @NonNull
+ private MappingInfo lookupMappingInfo(@InCallButtonIds int button) {
+ MappingInfo info = mapping.get(button);
+ if (info == null) {
+ throw new IllegalArgumentException(
+ "Unknown InCallButtonId: " + InCallButtonIdsExtension.toString(button));
+ }
+ return info;
+ }
+
+ /** Holds information about button mapping. */
+
+ abstract static class MappingInfo {
+
+ /** The Ui slot into which a given button desires to be placed. */
+ public abstract int getSlot();
+
+ /**
+ * Returns an integer used to determine which button is chosen for a slot when multiple buttons
+ * desire to be placed in the same slot. Follows from the natural ordering of integers, i.e. a
+ * lower slotOrder results in the button being chosen.
+ */
+ public abstract int getSlotOrder();
+
+ /**
+ * Returns an integer used to determine the order in which buttons that weren't chosen for their
+ * desired slot are placed into the Ui. Follows from the natural ordering of integers, i.e. a
+ * lower conflictOrder results in the button being chosen.
+ */
+ public abstract int getConflictOrder();
+
+ static Builder builder(int slot) {
+ return new AutoValue_MappedButtonConfig_MappingInfo.Builder()
+ .setSlot(slot)
+ .setSlotOrder(Integer.MAX_VALUE)
+ .setConflictOrder(Integer.MAX_VALUE);
+ }
+
+ /** Class used to build instances of {@link MappingInfo}. */
+
+ abstract static class Builder {
+ public abstract Builder setSlot(int slot);
+
+ public abstract Builder setSlotOrder(int slotOrder);
+
+ public abstract Builder setConflictOrder(int conflictOrder);
+
+ public abstract MappingInfo build();
+ }
+ }
+}
diff --git a/java/com/android/incallui/incall/impl/res/animator/incall_button_elevation.xml b/java/com/android/incallui/incall/impl/res/animator/incall_button_elevation.xml
new file mode 100644
index 000000000..69215adda
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/animator/incall_button_elevation.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_enabled="true"
+ android:state_pressed="true">
+ <objectAnimator
+ android:duration="@android:integer/config_shortAnimTime"
+ android:propertyName="translationZ"
+ android:valueTo="8dp"
+ android:valueType="floatType"/>
+
+ </item>
+ <item
+ android:state_checked="true"
+ android:state_enabled="true"
+ android:state_pressed="false">
+ <objectAnimator
+ android:duration="@android:integer/config_shortAnimTime"
+ android:propertyName="translationZ"
+ android:valueTo="4dp"
+ android:valueType="floatType"/>
+
+ </item>
+ <item>
+ <objectAnimator
+ android:duration="@android:integer/config_shortAnimTime"
+ android:propertyName="translationZ"
+ android:valueTo="0dp"
+ android:valueType="floatType"/>
+ </item>
+</selector>
diff --git a/java/com/android/incallui/incall/impl/res/color/incall_button_icon.xml b/java/com/android/incallui/incall/impl/res/color/incall_button_icon.xml
new file mode 100644
index 000000000..6d8556759
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/color/incall_button_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#FF01579B" android:state_checked="true"/>
+ <item android:color="#FFFFFFFF"/>
+</selector>
diff --git a/java/com/android/incallui/incall/impl/res/drawable-mdpi/ic_addcall_white.png b/java/com/android/incallui/incall/impl/res/drawable-mdpi/ic_addcall_white.png
new file mode 100644
index 000000000..a60805258
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-mdpi/ic_addcall_white.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xhdpi/ic_addcall_white.png b/java/com/android/incallui/incall/impl/res/drawable-xhdpi/ic_addcall_white.png
new file mode 100644
index 000000000..d2a843c38
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xhdpi/ic_addcall_white.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable/incall_button_background.xml b/java/com/android/incallui/incall/impl/res/drawable/incall_button_background.xml
new file mode 100644
index 000000000..c8bd29568
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable/incall_button_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <selector>
+ <item
+ android:drawable="@drawable/incall_button_background_checked"
+ android:state_checked="true"/>
+ <item android:drawable="@drawable/incall_button_background_unchecked"/>
+ </selector>
+ </item>
+ <item>
+ <ripple android:color="@color/incall_button_ripple">
+ <item
+ android:id="@android:id/mask"
+ android:gravity="center">
+ <shape android:shape="oval">
+ <solid android:color="@android:color/white"/>
+ </shape>
+ </item>
+ </ripple>
+ </item>
+</layer-list>
diff --git a/java/com/android/incallui/incall/impl/res/drawable/incall_button_background_checked.xml b/java/com/android/incallui/incall/impl/res/drawable/incall_button_background_checked.xml
new file mode 100644
index 000000000..73c6947e2
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable/incall_button_background_checked.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="@color/incall_button_white"/>
+</shape>
diff --git a/java/com/android/incallui/incall/impl/res/drawable/incall_button_background_more.xml b/java/com/android/incallui/incall/impl/res/drawable/incall_button_background_more.xml
new file mode 100644
index 000000000..6755f0fae
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable/incall_button_background_more.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <selector>
+ <item
+ android:drawable="@drawable/incall_button_background_checked"
+ android:state_checked="true"/>
+ <item android:drawable="@drawable/incall_button_background_unchecked"/>
+ </selector>
+ </item>
+ <item>
+ <ripple android:color="@color/incall_button_ripple">
+ <item
+ android:id="@android:id/mask"
+ android:gravity="center">
+ <shape android:shape="oval">
+ <solid android:color="@android:color/white"/>
+ </shape>
+ </item>
+ </ripple>
+ </item>
+
+ <!-- This adds a little down arrow to indicate that the button will pop up a menu. Use an explicit
+ <bitmap> to avoid scaling the icon up to the full size of the button. -->
+ <item>
+ <bitmap
+ android:gravity="end"
+ android:src="@drawable/quantum_ic_arrow_drop_down_white_18"/>
+ </item>
+</layer-list>
diff --git a/java/com/android/incallui/incall/impl/res/drawable/incall_button_background_unchecked.xml b/java/com/android/incallui/incall/impl/res/drawable/incall_button_background_unchecked.xml
new file mode 100644
index 000000000..f7ffa4d50
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable/incall_button_background_unchecked.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="@android:color/transparent"/>
+</shape>
diff --git a/java/com/android/incallui/incall/impl/res/drawable/incall_ic_add_call.xml b/java/com/android/incallui/incall/impl/res/drawable/incall_ic_add_call.xml
new file mode 100644
index 000000000..4daf0527c
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable/incall_ic_add_call.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/ic_addcall_white"/>
+</selector>
diff --git a/java/com/android/incallui/incall/impl/res/drawable/incall_ic_dialpad.xml b/java/com/android/incallui/incall/impl/res/drawable/incall_ic_dialpad.xml
new file mode 100644
index 000000000..091142bef
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable/incall_ic_dialpad.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/quantum_ic_dialpad_white_36"/>
+</selector>
diff --git a/java/com/android/incallui/incall/impl/res/drawable/incall_ic_manage.xml b/java/com/android/incallui/incall/impl/res/drawable/incall_ic_manage.xml
new file mode 100644
index 000000000..a48e4c4ed
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable/incall_ic_manage.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/quantum_ic_group_white_36"/>
+</selector>
diff --git a/java/com/android/incallui/incall/impl/res/drawable/incall_ic_merge.xml b/java/com/android/incallui/incall/impl/res/drawable/incall_ic_merge.xml
new file mode 100644
index 000000000..61d75556e
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable/incall_ic_merge.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/quantum_ic_call_merge_white_36"/>
+</selector>
diff --git a/java/com/android/incallui/incall/impl/res/drawable/incall_ic_pause.xml b/java/com/android/incallui/incall/impl/res/drawable/incall_ic_pause.xml
new file mode 100644
index 000000000..6aa8ab8ce
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable/incall_ic_pause.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/quantum_ic_pause_white_36"/>
+</selector>
diff --git a/java/com/android/incallui/incall/impl/res/drawable/tab_indicator_default.xml b/java/com/android/incallui/incall/impl/res/drawable/tab_indicator_default.xml
new file mode 100644
index 000000000..6a55b35dc
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable/tab_indicator_default.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape
+ android:innerRadius="0dp"
+ android:shape="ring"
+ android:thickness="2dp"
+ android:useLevel="false">
+ <solid android:color="@android:color/darker_gray"/>
+ </shape>
+ </item>
+</layer-list> \ No newline at end of file
diff --git a/java/com/android/incallui/incall/impl/res/drawable/tab_indicator_selected.xml b/java/com/android/incallui/incall/impl/res/drawable/tab_indicator_selected.xml
new file mode 100644
index 000000000..fc673c6ed
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable/tab_indicator_selected.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape
+ android:innerRadius="0dp"
+ android:shape="ring"
+ android:thickness="4dp"
+ android:useLevel="false">
+ <solid android:color="@color/background_dialer_white"/>
+ </shape>
+ </item>
+</layer-list> \ No newline at end of file
diff --git a/java/com/android/incallui/incall/impl/res/drawable/tab_selector.xml b/java/com/android/incallui/incall/impl/res/drawable/tab_selector.xml
new file mode 100644
index 000000000..303a49bd8
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable/tab_selector.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/tab_indicator_selected"
+ android:state_selected="true"/>
+ <item android:drawable="@drawable/tab_indicator_default"/>
+</selector> \ No newline at end of file
diff --git a/java/com/android/incallui/incall/impl/res/layout/call_composer_data_fragment.xml b/java/com/android/incallui/incall/impl/res/layout/call_composer_data_fragment.xml
new file mode 100644
index 000000000..335ac8ae2
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/layout/call_composer_data_fragment.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView
+ android:id="@+id/subject"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:padding="8dp"
+ android:textSize="24sp"
+ android:textColor="@color/primary_text_color"
+ android:background="@color/background_dialer_white"/>
+</FrameLayout> \ No newline at end of file
diff --git a/java/com/android/incallui/incall/impl/res/layout/frag_incall_voice.xml b/java/com/android/incallui/incall/impl/res/layout/frag_incall_voice.xml
new file mode 100644
index 000000000..9b950462c
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/layout/frag_incall_voice.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true">
+
+ <LinearLayout
+ android:id="@id/incall_contact_grid"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dp"
+ android:layout_marginStart="@dimen/incall_window_margin_horizontal"
+ android:layout_marginEnd="@dimen/incall_window_margin_horizontal"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@id/contactgrid_avatar"
+ android:layout_width="@dimen/incall_avatar_size"
+ android:layout_height="@dimen/incall_avatar_size"
+ android:layout_marginBottom="8dp"
+ android:elevation="2dp"/>
+
+ <include
+ layout="@layout/incall_contactgrid_top_row"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <!-- We have to keep deprecated singleLine to allow long text being truncated with ellipses.
+ b/31396406 -->
+ <com.android.incallui.autoresizetext.AutoResizeTextView
+ android:id="@id/contactgrid_contact_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:singleLine="true"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance.Large"
+ app:autoResizeText_minTextSize="28sp"
+ tools:text="Jake Peralta"
+ tools:ignore="Deprecated"/>
+
+ <include
+ layout="@layout/incall_contactgrid_bottom_row"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <FrameLayout
+ android:id="@+id/incall_location_holder"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+ </LinearLayout>
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/incall_pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_above="@+id/incall_tab_dots"
+ android:layout_below="@+id/incall_contact_grid"
+ android:layout_centerHorizontal="true"/>
+
+ <android.support.design.widget.TabLayout
+ android:id="@+id/incall_tab_dots"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/incall_end_call"
+ android:visibility="gone"
+ app:tabBackground="@drawable/tab_selector"
+ app:tabGravity="center"
+ app:tabIndicatorHeight="0dp"/>
+
+ <FrameLayout
+ android:id="@+id/incall_dialpad_container"
+ style="@style/DialpadContainer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ tools:background="@android:color/white"
+ tools:visibility="gone"/>
+ <ImageButton
+ android:id="@+id/incall_end_call"
+ style="@style/Incall.Button.End"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="36dp"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:contentDescription="@string/incall_content_description_end_call"/>
+ </RelativeLayout>
+
+ <FrameLayout
+ android:id="@id/incall_on_hold_banner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"/>
+</FrameLayout>
diff --git a/java/com/android/incallui/incall/impl/res/layout/incall_button_grid.xml b/java/com/android/incallui/incall/impl/res/layout/incall_button_grid.xml
new file mode 100644
index 000000000..59e99440e
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/layout/incall_button_grid.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/incall_window_margin_horizontal"
+ android:layout_marginEnd="@dimen/incall_window_margin_horizontal"
+ tools:showIn="@layout/frag_incall_voice">
+ <GridLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:columnCount="3"
+ android:orientation="horizontal">
+ <com.android.incallui.incall.impl.CheckableLabeledButton
+ android:id="@+id/incall_first_button"
+ android:layout_width="0dp"
+ android:layout_columnWeight="1"
+ android:enabled="false"
+ android:gravity="center"
+ app:incall_labelText="@string/incall_label_mute"
+ tools:background="#FFFF0000"
+ tools:layout_height="@dimen/tools_button_height"
+ tools:layout_width="@dimen/incall_labeled_button_size"/>
+ <com.android.incallui.incall.impl.CheckableLabeledButton
+ android:id="@+id/incall_second_button"
+ android:layout_width="0dp"
+ android:layout_columnWeight="1"
+ android:enabled="false"
+ android:gravity="center"
+ app:incall_labelText="@string/incall_label_dialpad"
+ tools:background="#FFFF0000"
+ tools:layout_height="@dimen/tools_button_height"
+ tools:layout_width="@dimen/incall_labeled_button_size"/>
+ <com.android.incallui.incall.impl.CheckableLabeledButton
+ android:id="@+id/incall_third_button"
+ android:layout_width="0dp"
+ android:layout_columnWeight="1"
+ android:enabled="false"
+ android:gravity="center"
+ app:incall_labelText="@string/incall_label_speaker"
+ tools:background="#FFFF0000"
+ tools:layout_height="@dimen/tools_button_height"
+ tools:layout_width="@dimen/incall_labeled_button_size"/>
+ <com.android.incallui.incall.impl.CheckableLabeledButton
+ android:id="@+id/incall_fourth_button"
+ android:layout_marginTop="@dimen/incall_button_vertical_padding"
+ android:layout_width="0dp"
+ android:layout_columnWeight="1"
+ android:gravity="center"
+ app:incall_labelText="@string/incall_label_add_call"
+ tools:background="#FFFF0000"
+ tools:layout_height="@dimen/tools_button_height"
+ tools:layout_width="@dimen/incall_labeled_button_size"/>
+ <com.android.incallui.incall.impl.CheckableLabeledButton
+ android:id="@+id/incall_fifth_button"
+ android:layout_width="0dp"
+ android:layout_columnWeight="1"
+ android:layout_marginTop="@dimen/incall_button_vertical_padding"
+ android:gravity="center"
+ app:incall_labelText="@string/incall_label_hold"
+ tools:background="#FFFF0000"
+ tools:layout_height="@dimen/tools_button_height"
+ tools:layout_width="@dimen/incall_labeled_button_size"/>
+ <com.android.incallui.incall.impl.CheckableLabeledButton
+ android:id="@+id/incall_sixth_button"
+ android:layout_width="0dp"
+ android:layout_columnWeight="1"
+ android:layout_marginTop="@dimen/incall_button_vertical_padding"
+ android:gravity="center"
+ app:incall_labelText="@string/incall_label_videocall"
+ tools:background="#FFFF0000"
+ tools:layout_height="@dimen/tools_button_height"
+ tools:layout_width="@dimen/incall_labeled_button_size"/>
+ </GridLayout>
+</FrameLayout>
diff --git a/java/com/android/incallui/incall/impl/res/values-h320dp/dimens.xml b/java/com/android/incallui/incall/impl/res/values-h320dp/dimens.xml
new file mode 100644
index 000000000..1fe0c4db9
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/values-h320dp/dimens.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <bool name="incall_dialpad_allowed">true</bool>
+ <integer name="incall_num_rows">1</integer>
+</resources>
diff --git a/java/com/android/incallui/incall/impl/res/values-h385dp/dimens.xml b/java/com/android/incallui/incall/impl/res/values-h385dp/dimens.xml
new file mode 100644
index 000000000..aac42c563
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/values-h385dp/dimens.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="incall_avatar_size">64dp</dimen>
+ <dimen name="incall_avatar_marginBottom">8dp</dimen>
+</resources>
diff --git a/java/com/android/incallui/incall/impl/res/values-h480dp/dimens.xml b/java/com/android/incallui/incall/impl/res/values-h480dp/dimens.xml
new file mode 100644
index 000000000..ef1a800ac
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/values-h480dp/dimens.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <integer name="incall_num_rows">2</integer>
+</resources>
diff --git a/java/com/android/incallui/incall/impl/res/values-h580dp/dimens.xml b/java/com/android/incallui/incall/impl/res/values-h580dp/dimens.xml
new file mode 100644
index 000000000..1f37cd504
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/values-h580dp/dimens.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="incall_avatar_size">88dp</dimen>
+</resources>
diff --git a/java/com/android/incallui/incall/impl/res/values-h580dp/styles.xml b/java/com/android/incallui/incall/impl/res/values-h580dp/styles.xml
new file mode 100644
index 000000000..b58ef4819
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/values-h580dp/styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+
+ <style name="DialpadContainer">
+ <item name="android:layout_below">@id/incall_contact_grid</item>
+ <item name="android:layout_marginTop">8dp</item>
+ </style>
+</resources>
diff --git a/java/com/android/incallui/incall/impl/res/values-w260dp-h520dp/dimens.xml b/java/com/android/incallui/incall/impl/res/values-w260dp-h520dp/dimens.xml
new file mode 100644
index 000000000..e73eb934c
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/values-w260dp-h520dp/dimens.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="incall_button_horizontal_padding">16dp</dimen>
+ <dimen name="incall_button_vertical_padding">16dp</dimen>
+ <dimen name="incall_labeled_button_size">64dp</dimen>
+ <dimen name="tools_button_height">92dp</dimen>
+</resources>
diff --git a/java/com/android/incallui/incall/impl/res/values-w300dp-h540dp/dimens.xml b/java/com/android/incallui/incall/impl/res/values-w300dp-h540dp/dimens.xml
new file mode 100644
index 000000000..502ae72dc
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/values-w300dp-h540dp/dimens.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="incall_button_horizontal_padding">32dp</dimen>
+ <dimen name="incall_button_vertical_padding">32dp</dimen>
+</resources>
diff --git a/java/com/android/incallui/incall/impl/res/values/attrs.xml b/java/com/android/incallui/incall/impl/res/values/attrs.xml
new file mode 100644
index 000000000..ed1b2a853
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/values/attrs.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <declare-styleable name="CheckableLabeledButton">
+ <attr format="reference" name="incall_icon"/>
+ <attr format="string|reference" name="incall_labelText"/>
+ <attr name="android:enabled"/>
+ </declare-styleable>
+</resources>
diff --git a/java/com/android/incallui/incall/impl/res/values/dimens.xml b/java/com/android/incallui/incall/impl/res/values/dimens.xml
new file mode 100644
index 000000000..249788785
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/values/dimens.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="incall_button_label_margin">8dp</dimen>
+ <dimen name="incall_button_elevation">0dp</dimen>
+ <dimen name="incall_end_call_spacing">116dp</dimen>
+ <dimen name="incall_button_padding">4dp</dimen>
+ <dimen name="incall_button_horizontal_padding">8dp</dimen>
+ <dimen name="incall_button_vertical_padding">8dp</dimen>
+ <dimen name="incall_avatar_size">0dp</dimen>
+ <dimen name="incall_avatar_marginBottom">0dp</dimen>
+ <dimen name="incall_labeled_button_size">48dp</dimen>
+ <dimen name="tools_button_height">76dp</dimen>
+ <dimen name="incall_window_margin_horizontal">24dp</dimen>
+
+ <bool name="incall_dialpad_allowed">false</bool>
+ <integer name="incall_num_rows">0</integer>
+</resources>
diff --git a/java/com/android/incallui/incall/impl/res/values/ids.xml b/java/com/android/incallui/incall/impl/res/values/ids.xml
new file mode 100644
index 000000000..e1368f95d
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/values/ids.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <item name="incall_on_hold_banner" type="id"/>
+ <item name="incall_button_grid" type="id"/>
+ <item name="incall_contact_grid" type="id"/>
+</resources>
diff --git a/java/com/android/incallui/incall/impl/res/values/strings.xml b/java/com/android/incallui/incall/impl/res/values/strings.xml
new file mode 100644
index 000000000..054ca9687
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/values/strings.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <!-- Button shown during a phone call to upgrade to video.
+ [CHAR LIMIT=12] -->
+ <string name="incall_label_videocall">Video call</string>
+
+ <!-- Button shown during a phone call to put the call on hold.
+ [CHAR LIMIT=12] -->
+ <string name="incall_label_hold">Hold</string>
+
+ <!-- Button shown during a phone call to add a new phone call.
+ [CHAR LIMIT=12] -->
+ <string name="incall_label_add_call">Add call</string>
+
+ <!-- Button shown during a phone call to mute the microphone.
+ [CHAR LIMIT=12] -->
+ <string name="incall_label_mute">Mute</string>
+
+ <!-- Button shown during a phone call to show the dialpad.
+ [CHAR LIMIT=12] -->
+ <string name="incall_label_dialpad">Keypad</string>
+
+ <!-- Button shown during a phone to route audio from earpiece to speaker phone.
+ [CHAR LIMIT=12] -->
+ <string name="incall_label_speaker">Speaker</string>
+
+ <!-- Talkback text for speaker button status. [CHAR LIMIT=12] -->
+ <string name="incall_talkback_speaker_on">, is on</string>
+
+ <!-- Talkback text for speaker button status. [CHAR LIMIT=12] -->
+ <string name="incall_talkback_speaker_off">, is Off</string>
+
+ <!-- Button shown during a phone to merge two ongoing calls.
+ [CHAR LIMIT=12] -->
+ <string name="incall_label_merge">Merge</string>
+
+ <!-- Button shown during a phone to show the manage conference call screen.
+ [CHAR LIMIT=12] -->
+ <string name="incall_label_manage">Manage</string>
+
+ <string name="a11y_description_incall_label_manage_content">Manage callers</string>
+
+ <!-- Button shown during a phone to swap from the foreground call to the background call.
+ [CHAR LIMIT=12] -->
+ <string name="incall_label_swap">Swap</string>
+
+ <!-- Button shown during a phone to switch the audio route.
+ [CHAR LIMIT=12] -->
+ <string name="incall_label_audio">Sound</string>
+
+ <!-- Used to inform the user that the note associated with an outgoing call has been sent.
+ [CHAR LIMIT=32] -->
+ <string name="incall_note_sent">Note sent</string>
+
+</resources> \ No newline at end of file
diff --git a/java/com/android/incallui/incall/impl/res/values/styles.xml b/java/com/android/incallui/incall/impl/res/values/styles.xml
new file mode 100644
index 000000000..2392574a3
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/values/styles.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+
+ <style name="DialpadContainer">
+ <item name="android:layout_alignParentTop">true</item>
+ </style>
+</resources>
diff --git a/java/com/android/incallui/incall/protocol/ContactPhotoType.java b/java/com/android/incallui/incall/protocol/ContactPhotoType.java
new file mode 100644
index 000000000..d79b7550b
--- /dev/null
+++ b/java/com/android/incallui/incall/protocol/ContactPhotoType.java
@@ -0,0 +1,35 @@
+/*
+ * 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.incallui.incall.protocol;
+
+import android.support.annotation.IntDef;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Types of contact photos we can have. */
+@Retention(RetentionPolicy.SOURCE)
+@IntDef({
+ ContactPhotoType.DEFAULT_PLACEHOLDER,
+ ContactPhotoType.BUSINESS,
+ ContactPhotoType.CONTACT,
+})
+public @interface ContactPhotoType {
+
+ int DEFAULT_PLACEHOLDER = 0;
+ int BUSINESS = 1;
+ int CONTACT = 2;
+}
diff --git a/java/com/android/incallui/incall/protocol/InCallButtonIds.java b/java/com/android/incallui/incall/protocol/InCallButtonIds.java
new file mode 100644
index 000000000..50ebc6413
--- /dev/null
+++ b/java/com/android/incallui/incall/protocol/InCallButtonIds.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2013 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.incallui.incall.protocol;
+
+import android.support.annotation.IntDef;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Ids for buttons in the in call UI. */
+@Retention(RetentionPolicy.SOURCE)
+@IntDef({
+ InCallButtonIds.BUTTON_AUDIO,
+ InCallButtonIds.BUTTON_MUTE,
+ InCallButtonIds.BUTTON_DIALPAD,
+ InCallButtonIds.BUTTON_HOLD,
+ InCallButtonIds.BUTTON_SWAP,
+ InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO,
+ InCallButtonIds.BUTTON_SWITCH_CAMERA,
+ InCallButtonIds.BUTTON_DOWNGRADE_TO_AUDIO,
+ InCallButtonIds.BUTTON_ADD_CALL,
+ InCallButtonIds.BUTTON_MERGE,
+ InCallButtonIds.BUTTON_PAUSE_VIDEO,
+ InCallButtonIds.BUTTON_MANAGE_VIDEO_CONFERENCE,
+ InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE,
+ InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY,
+ InCallButtonIds.BUTTON_COUNT,
+})
+public @interface InCallButtonIds {
+
+ int BUTTON_AUDIO = 0;
+ int BUTTON_MUTE = 1;
+ int BUTTON_DIALPAD = 2;
+ int BUTTON_HOLD = 3;
+ int BUTTON_SWAP = 4;
+ int BUTTON_UPGRADE_TO_VIDEO = 5;
+ int BUTTON_SWITCH_CAMERA = 6;
+ int BUTTON_DOWNGRADE_TO_AUDIO = 7;
+ int BUTTON_ADD_CALL = 8;
+ int BUTTON_MERGE = 9;
+ int BUTTON_PAUSE_VIDEO = 10;
+ int BUTTON_MANAGE_VIDEO_CONFERENCE = 11;
+ int BUTTON_MANAGE_VOICE_CONFERENCE = 12;
+ int BUTTON_SWITCH_TO_SECONDARY = 13;
+ int BUTTON_COUNT = 14;
+}
diff --git a/java/com/android/incallui/incall/protocol/InCallButtonIdsExtension.java b/java/com/android/incallui/incall/protocol/InCallButtonIdsExtension.java
new file mode 100644
index 000000000..6d802e346
--- /dev/null
+++ b/java/com/android/incallui/incall/protocol/InCallButtonIdsExtension.java
@@ -0,0 +1,61 @@
+/*
+ * 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.incallui.incall.protocol;
+
+/** Utility class for {@link InCallButtonIds}. */
+public class InCallButtonIdsExtension {
+
+ /**
+ * Converts the given {@link InCallButtonIds} to a human readable string.
+ *
+ * @param id the id to convert.
+ * @return the human readable string.
+ */
+ public static String toString(@InCallButtonIds int id) {
+ if (id == InCallButtonIds.BUTTON_AUDIO) {
+ return "AUDIO";
+ } else if (id == InCallButtonIds.BUTTON_MUTE) {
+ return "MUTE";
+ } else if (id == InCallButtonIds.BUTTON_DIALPAD) {
+ return "DIALPAD";
+ } else if (id == InCallButtonIds.BUTTON_HOLD) {
+ return "HOLD";
+ } else if (id == InCallButtonIds.BUTTON_SWAP) {
+ return "SWAP";
+ } else if (id == InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO) {
+ return "UPGRADE_TO_VIDEO";
+ } else if (id == InCallButtonIds.BUTTON_DOWNGRADE_TO_AUDIO) {
+ return "DOWNGRADE_TO_AUDIO";
+ } else if (id == InCallButtonIds.BUTTON_SWITCH_CAMERA) {
+ return "SWITCH_CAMERA";
+ } else if (id == InCallButtonIds.BUTTON_ADD_CALL) {
+ return "ADD_CALL";
+ } else if (id == InCallButtonIds.BUTTON_MERGE) {
+ return "MERGE";
+ } else if (id == InCallButtonIds.BUTTON_PAUSE_VIDEO) {
+ return "PAUSE_VIDEO";
+ } else if (id == InCallButtonIds.BUTTON_MANAGE_VIDEO_CONFERENCE) {
+ return "MANAGE_VIDEO_CONFERENCE";
+ } else if (id == InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE) {
+ return "MANAGE_VOICE_CONFERENCE";
+ } else if (id == InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY) {
+ return "SWITCH_TO_SECONDARY";
+ } else {
+ return "INVALID_BUTTON: " + id;
+ }
+ }
+}
diff --git a/java/com/android/incallui/incall/protocol/InCallButtonUi.java b/java/com/android/incallui/incall/protocol/InCallButtonUi.java
new file mode 100644
index 000000000..96d741af3
--- /dev/null
+++ b/java/com/android/incallui/incall/protocol/InCallButtonUi.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2013 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.incallui.incall.protocol;
+
+import android.support.v4.app.Fragment;
+import android.telecom.CallAudioState;
+
+/** Interface for the call button UI. */
+public interface InCallButtonUi {
+
+ void showButton(@InCallButtonIds int buttonId, boolean show);
+
+ void enableButton(@InCallButtonIds int buttonId, boolean enable);
+
+ void setEnabled(boolean on);
+
+ void setHold(boolean on);
+
+ void setCameraSwitched(boolean isBackFacingCamera);
+
+ void setVideoPaused(boolean isPaused);
+
+ void setAudioState(CallAudioState audioState);
+
+ /**
+ * Once showButton() has been called on each of the individual buttons in the UI, call this to
+ * configure the overflow menu appropriately.
+ */
+ void updateButtonStates();
+
+ void updateInCallButtonUiColors();
+
+ Fragment getInCallButtonUiFragment();
+
+ void showAudioRouteSelector();
+}
diff --git a/java/com/android/incallui/incall/protocol/InCallButtonUiDelegate.java b/java/com/android/incallui/incall/protocol/InCallButtonUiDelegate.java
new file mode 100644
index 000000000..5e69f0e2d
--- /dev/null
+++ b/java/com/android/incallui/incall/protocol/InCallButtonUiDelegate.java
@@ -0,0 +1,67 @@
+/*
+ * 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.incallui.incall.protocol;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.telecom.CallAudioState;
+
+/** Callbacks from the module out to the container. */
+public interface InCallButtonUiDelegate {
+
+ void onInCallButtonUiReady(InCallButtonUi inCallButtonUi);
+
+ void onInCallButtonUiUnready();
+
+ void onSaveInstanceState(Bundle outState);
+
+ void onRestoreInstanceState(Bundle savedInstanceState);
+
+ void refreshMuteState();
+
+ void addCallClicked();
+
+ void muteClicked(boolean checked);
+
+ void mergeClicked();
+
+ void holdClicked(boolean checked);
+
+ void swapClicked();
+
+ void showDialpadClicked(boolean checked);
+
+ void changeToVideoClicked();
+
+ void switchCameraClicked(boolean useFrontFacingCamera);
+
+ void toggleCameraClicked();
+
+ void pauseVideoClicked(boolean pause);
+
+ void toggleSpeakerphone();
+
+ CallAudioState getCurrentAudioState();
+
+ void setAudioRoute(int route);
+
+ void onEndCallClicked();
+
+ void showAudioRouteSelector();
+
+ Context getContext();
+}
diff --git a/java/com/android/incallui/incall/protocol/InCallButtonUiDelegateFactory.java b/java/com/android/incallui/incall/protocol/InCallButtonUiDelegateFactory.java
new file mode 100644
index 000000000..ca7d11951
--- /dev/null
+++ b/java/com/android/incallui/incall/protocol/InCallButtonUiDelegateFactory.java
@@ -0,0 +1,23 @@
+/*
+ * 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.incallui.incall.protocol;
+
+/** Callbacks from the module out to the container. */
+public interface InCallButtonUiDelegateFactory {
+
+ InCallButtonUiDelegate newInCallButtonUiDelegate();
+}
diff --git a/java/com/android/incallui/incall/protocol/InCallScreen.java b/java/com/android/incallui/incall/protocol/InCallScreen.java
new file mode 100644
index 000000000..612ad26f5
--- /dev/null
+++ b/java/com/android/incallui/incall/protocol/InCallScreen.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 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.incallui.incall.protocol;
+
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+import android.view.accessibility.AccessibilityEvent;
+
+/** Interface for the call card module. */
+public interface InCallScreen {
+
+ void setPrimary(@NonNull PrimaryInfo primaryInfo);
+
+ void setSecondary(@NonNull SecondaryInfo secondaryInfo);
+
+ void setCallState(@NonNull PrimaryCallState primaryCallState);
+
+ void setEndCallButtonEnabled(boolean enabled, boolean animate);
+
+ void showManageConferenceCallButton(boolean visible);
+
+ boolean isManageConferenceVisible();
+
+ void dispatchPopulateAccessibilityEvent(AccessibilityEvent event);
+
+ void showNoteSentToast();
+
+ void updateInCallScreenColors();
+
+ void onInCallScreenDialpadVisibilityChange(boolean isShowing);
+
+ int getAnswerAndDialpadContainerResourceId();
+
+ void showLocationUi(Fragment locationUi);
+
+ boolean isShowingLocationUi();
+
+ Fragment getInCallScreenFragment();
+}
diff --git a/java/com/android/incallui/incall/protocol/InCallScreenDelegate.java b/java/com/android/incallui/incall/protocol/InCallScreenDelegate.java
new file mode 100644
index 000000000..b39f9f4a2
--- /dev/null
+++ b/java/com/android/incallui/incall/protocol/InCallScreenDelegate.java
@@ -0,0 +1,43 @@
+/*
+ * 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.incallui.incall.protocol;
+
+import android.graphics.drawable.Drawable;
+
+/** Callbacks from the module out to the container. */
+public interface InCallScreenDelegate {
+
+ void onInCallScreenDelegateInit(InCallScreen inCallScreen);
+
+ void onInCallScreenReady();
+
+ void onInCallScreenUnready();
+
+ void onEndCallClicked();
+
+ void onSecondaryInfoClicked();
+
+ void onCallStateButtonClicked();
+
+ void onManageConferenceClicked();
+
+ void onShrinkAnimationComplete();
+
+ void onInCallScreenResumed();
+
+ Drawable getDefaultContactPhotoDrawable();
+}
diff --git a/java/com/android/incallui/incall/protocol/InCallScreenDelegateFactory.java b/java/com/android/incallui/incall/protocol/InCallScreenDelegateFactory.java
new file mode 100644
index 000000000..6706691c8
--- /dev/null
+++ b/java/com/android/incallui/incall/protocol/InCallScreenDelegateFactory.java
@@ -0,0 +1,23 @@
+/*
+ * 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.incallui.incall.protocol;
+
+/** Callbacks from the module out to the container. */
+public interface InCallScreenDelegateFactory {
+
+ InCallScreenDelegate newInCallScreenDelegate();
+}
diff --git a/java/com/android/incallui/incall/protocol/PrimaryCallState.java b/java/com/android/incallui/incall/protocol/PrimaryCallState.java
new file mode 100644
index 000000000..782090832
--- /dev/null
+++ b/java/com/android/incallui/incall/protocol/PrimaryCallState.java
@@ -0,0 +1,114 @@
+/*
+ * 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.incallui.incall.protocol;
+
+import android.graphics.drawable.Drawable;
+import android.telecom.DisconnectCause;
+import android.telecom.VideoProfile;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.DialerCall.SessionModificationState;
+import java.util.Locale;
+
+/** State of the primary call. */
+public class PrimaryCallState {
+ public final int state;
+ public final int videoState;
+ @SessionModificationState public final int sessionModificationState;
+ public final DisconnectCause disconnectCause;
+ public final String connectionLabel;
+ public final Drawable connectionIcon;
+ public final String gatewayNumber;
+ public final String callSubject;
+ public final String callbackNumber;
+ public final boolean isWifi;
+ public final boolean isConference;
+ public final boolean isWorkCall;
+ public final boolean isHdAudioCall;
+ public final boolean isForwardedNumber;
+ public final boolean shouldShowContactPhoto;
+ public final long connectTimeMillis;
+ public final boolean isVoiceMailNumber;
+ public final boolean isRemotelyHeld;
+
+ // TODO: Convert to autovalue. b/34502119
+ public static PrimaryCallState createEmptyPrimaryCallState() {
+ return new PrimaryCallState(
+ DialerCall.State.IDLE,
+ VideoProfile.STATE_AUDIO_ONLY,
+ DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST,
+ new DisconnectCause(DisconnectCause.UNKNOWN),
+ null, /* connectionLabel */
+ null, /* connectionIcon */
+ null, /* gatewayNumber */
+ null, /* callSubject */
+ null, /* callbackNumber */
+ false /* isWifi */,
+ false /* isConference */,
+ false /* isWorkCall */,
+ false /* isHdAudioCall */,
+ false /* isForwardedNumber */,
+ false /* shouldShowContactPhoto */,
+ 0,
+ false /* isVoiceMailNumber */,
+ false /* isRemotelyHeld */);
+ }
+
+ public PrimaryCallState(
+ int state,
+ int videoState,
+ @SessionModificationState int sessionModificationState,
+ DisconnectCause disconnectCause,
+ String connectionLabel,
+ Drawable connectionIcon,
+ String gatewayNumber,
+ String callSubject,
+ String callbackNumber,
+ boolean isWifi,
+ boolean isConference,
+ boolean isWorkCall,
+ boolean isHdAudioCall,
+ boolean isForwardedNumber,
+ boolean shouldShowContactPhoto,
+ long connectTimeMillis,
+ boolean isVoiceMailNumber,
+ boolean isRemotelyHeld) {
+ this.state = state;
+ this.videoState = videoState;
+ this.sessionModificationState = sessionModificationState;
+ this.disconnectCause = disconnectCause;
+ this.connectionLabel = connectionLabel;
+ this.connectionIcon = connectionIcon;
+ this.gatewayNumber = gatewayNumber;
+ this.callSubject = callSubject;
+ this.callbackNumber = callbackNumber;
+ this.isWifi = isWifi;
+ this.isConference = isConference;
+ this.isWorkCall = isWorkCall;
+ this.isHdAudioCall = isHdAudioCall;
+ this.isForwardedNumber = isForwardedNumber;
+ this.shouldShowContactPhoto = shouldShowContactPhoto;
+ this.connectTimeMillis = connectTimeMillis;
+ this.isVoiceMailNumber = isVoiceMailNumber;
+ this.isRemotelyHeld = isRemotelyHeld;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ Locale.US, "PrimaryCallState, state: %d, connectionLabel: %s", state, connectionLabel);
+ }
+}
diff --git a/java/com/android/incallui/incall/protocol/PrimaryInfo.java b/java/com/android/incallui/incall/protocol/PrimaryInfo.java
new file mode 100644
index 000000000..1833ed22e
--- /dev/null
+++ b/java/com/android/incallui/incall/protocol/PrimaryInfo.java
@@ -0,0 +1,112 @@
+/*
+ * 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.incallui.incall.protocol;
+
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.multimedia.MultimediaData;
+import java.util.Locale;
+
+/** Information about the primary call. */
+public class PrimaryInfo {
+ @Nullable public final String number;
+ @Nullable public final String name;
+ public final boolean nameIsNumber;
+ // This is from contacts and shows the type of number. For example, "Mobile".
+ @Nullable public final String label;
+ @Nullable public final String location;
+ @Nullable public final Drawable photo;
+ @ContactPhotoType public final int photoType;
+ public final boolean isSipCall;
+ public final boolean isContactPhotoShown;
+ public final boolean isWorkCall;
+ public final boolean isSpam;
+ public final boolean answeringDisconnectsOngoingCall;
+ public final boolean shouldShowLocation;
+ // Used for consistent LetterTile coloring.
+ @Nullable public final String contactInfoLookupKey;
+ @Nullable public final MultimediaData multimediaData;
+
+ // TODO: Convert to autovalue. b/34502119
+ public static PrimaryInfo createEmptyPrimaryInfo() {
+ return new PrimaryInfo(
+ null,
+ null,
+ false,
+ null,
+ null,
+ null,
+ ContactPhotoType.DEFAULT_PLACEHOLDER,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ null,
+ null);
+ }
+
+ public PrimaryInfo(
+ @Nullable String number,
+ @Nullable String name,
+ boolean nameIsNumber,
+ @Nullable String location,
+ @Nullable String label,
+ @Nullable Drawable photo,
+ @ContactPhotoType int phototType,
+ boolean isSipCall,
+ boolean isContactPhotoShown,
+ boolean isWorkCall,
+ boolean isSpam,
+ boolean answeringDisconnectsOngoingCall,
+ boolean shouldShowLocation,
+ @Nullable String contactInfoLookupKey,
+ @Nullable MultimediaData multimediaData) {
+ this.number = number;
+ this.name = name;
+ this.nameIsNumber = nameIsNumber;
+ this.location = location;
+ this.label = label;
+ this.photo = photo;
+ this.photoType = phototType;
+ this.isSipCall = isSipCall;
+ this.isContactPhotoShown = isContactPhotoShown;
+ this.isWorkCall = isWorkCall;
+ this.isSpam = isSpam;
+ this.answeringDisconnectsOngoingCall = answeringDisconnectsOngoingCall;
+ this.shouldShowLocation = shouldShowLocation;
+ this.contactInfoLookupKey = contactInfoLookupKey;
+ this.multimediaData = multimediaData;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ Locale.US,
+ "PrimaryInfo, number: %s, name: %s, location: %s, label: %s, "
+ + "photo: %s, photoType: %d, isPhotoVisible: %b",
+ LogUtil.sanitizePhoneNumber(number),
+ LogUtil.sanitizePii(name),
+ LogUtil.sanitizePii(location),
+ label,
+ photo,
+ photoType,
+ isContactPhotoShown);
+ }
+}
diff --git a/java/com/android/incallui/incall/protocol/SecondaryInfo.java b/java/com/android/incallui/incall/protocol/SecondaryInfo.java
new file mode 100644
index 000000000..cadfca6bf
--- /dev/null
+++ b/java/com/android/incallui/incall/protocol/SecondaryInfo.java
@@ -0,0 +1,109 @@
+/*
+ * 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.incallui.incall.protocol;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import com.android.dialer.common.LogUtil;
+import java.util.Locale;
+
+/** Information about the secondary call. */
+public class SecondaryInfo implements Parcelable {
+ public final boolean shouldShow;
+ public final String name;
+ public final boolean nameIsNumber;
+ public final String label;
+ public final String providerLabel;
+ public final boolean isConference;
+ public final boolean isVideoCall;
+ public final boolean isFullscreen;
+
+ public static SecondaryInfo createEmptySecondaryInfo(boolean isFullScreen) {
+ return new SecondaryInfo(false, null, false, null, null, false, false, isFullScreen);
+ }
+
+ public SecondaryInfo(
+ boolean shouldShow,
+ String name,
+ boolean nameIsNumber,
+ String label,
+ String providerLabel,
+ boolean isConference,
+ boolean isVideoCall,
+ boolean isFullscreen) {
+ this.shouldShow = shouldShow;
+ this.name = name;
+ this.nameIsNumber = nameIsNumber;
+ this.label = label;
+ this.providerLabel = providerLabel;
+ this.isConference = isConference;
+ this.isVideoCall = isVideoCall;
+ this.isFullscreen = isFullscreen;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ Locale.US,
+ "SecondaryInfo, show: %b, name: %s, label: %s, " + "providerLabel: %s",
+ shouldShow,
+ LogUtil.sanitizePii(name),
+ label,
+ providerLabel);
+ }
+
+ protected SecondaryInfo(Parcel in) {
+ shouldShow = in.readByte() != 0;
+ name = in.readString();
+ nameIsNumber = in.readByte() != 0;
+ label = in.readString();
+ providerLabel = in.readString();
+ isConference = in.readByte() != 0;
+ isVideoCall = in.readByte() != 0;
+ isFullscreen = in.readByte() != 0;
+ }
+
+ public static final Creator<SecondaryInfo> CREATOR =
+ new Creator<SecondaryInfo>() {
+ @Override
+ public SecondaryInfo createFromParcel(Parcel in) {
+ return new SecondaryInfo(in);
+ }
+
+ @Override
+ public SecondaryInfo[] newArray(int size) {
+ return new SecondaryInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte((byte) (shouldShow ? 1 : 0));
+ dest.writeString(name);
+ dest.writeByte((byte) (nameIsNumber ? 1 : 0));
+ dest.writeString(label);
+ dest.writeString(providerLabel);
+ dest.writeByte((byte) (isConference ? 1 : 0));
+ dest.writeByte((byte) (isVideoCall ? 1 : 0));
+ dest.writeByte((byte) (isFullscreen ? 1 : 0));
+ }
+}
diff --git a/java/com/android/incallui/latencyreport/LatencyReport.java b/java/com/android/incallui/latencyreport/LatencyReport.java
new file mode 100644
index 000000000..2e1fbd590
--- /dev/null
+++ b/java/com/android/incallui/latencyreport/LatencyReport.java
@@ -0,0 +1,140 @@
+/*
+ * 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.incallui.latencyreport;
+
+import android.os.Bundle;
+import android.os.SystemClock;
+
+/** Tracks latency information for a call. */
+public class LatencyReport {
+
+ public static final long INVALID_TIME = -1;
+ // The following are hidden constants from android.telecom.TelecomManager.
+ private static final String EXTRA_CALL_CREATED_TIME_MILLIS =
+ "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
+ private static final String EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS =
+ "android.telecom.extra.CALL_TELECOM_ROUTING_START_TIME_MILLIS";
+ private static final String EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS =
+ "android.telecom.extra.CALL_TELECOM_ROUTING_END_TIME_MILLIS";
+ private final boolean mWasIncoming;
+
+ // Time elapsed since boot when the call was created by the connection service.
+ private final long mCreatedTimeMillis;
+
+ // Time elapsed since boot when telecom began processing the call.
+ private final long mTelecomRoutingStartTimeMillis;
+
+ // Time elapsed since boot when telecom finished processing the call. This includes things like
+ // looking up contact info and call blocking but before showing any UI.
+ private final long mTelecomRoutingEndTimeMillis;
+
+ // Time elapsed since boot when the call was added to the InCallUi.
+ private final long mCallAddedTimeMillis;
+
+ // Time elapsed since boot when the call was added and call blocking evaluation was completed.
+ private long mCallBlockingTimeMillis = INVALID_TIME;
+
+ // Time elapsed since boot when the call notification was shown.
+ private long mCallNotificationTimeMillis = INVALID_TIME;
+
+ // Time elapsed since boot when the InCallUI was shown.
+ private long mInCallUiShownTimeMillis = INVALID_TIME;
+
+ // Whether the call was shown to the user as a heads up notification instead of a full screen
+ // UI.
+ private boolean mDidDisplayHeadsUpNotification;
+
+ public LatencyReport() {
+ mWasIncoming = false;
+ mCreatedTimeMillis = INVALID_TIME;
+ mTelecomRoutingStartTimeMillis = INVALID_TIME;
+ mTelecomRoutingEndTimeMillis = INVALID_TIME;
+ mCallAddedTimeMillis = SystemClock.elapsedRealtime();
+ }
+
+ public LatencyReport(android.telecom.Call telecomCall) {
+ mWasIncoming = telecomCall.getState() == android.telecom.Call.STATE_RINGING;
+ Bundle extras = telecomCall.getDetails().getIntentExtras();
+ if (extras == null) {
+ mCreatedTimeMillis = INVALID_TIME;
+ mTelecomRoutingStartTimeMillis = INVALID_TIME;
+ mTelecomRoutingEndTimeMillis = INVALID_TIME;
+ } else {
+ mCreatedTimeMillis = extras.getLong(EXTRA_CALL_CREATED_TIME_MILLIS, INVALID_TIME);
+ mTelecomRoutingStartTimeMillis =
+ extras.getLong(EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS, INVALID_TIME);
+ mTelecomRoutingEndTimeMillis =
+ extras.getLong(EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS, INVALID_TIME);
+ }
+ mCallAddedTimeMillis = SystemClock.elapsedRealtime();
+ }
+
+ public boolean getWasIncoming() {
+ return mWasIncoming;
+ }
+
+ public long getCreatedTimeMillis() {
+ return mCreatedTimeMillis;
+ }
+
+ public long getTelecomRoutingStartTimeMillis() {
+ return mTelecomRoutingStartTimeMillis;
+ }
+
+ public long getTelecomRoutingEndTimeMillis() {
+ return mTelecomRoutingEndTimeMillis;
+ }
+
+ public long getCallAddedTimeMillis() {
+ return mCallAddedTimeMillis;
+ }
+
+ public long getCallBlockingTimeMillis() {
+ return mCallBlockingTimeMillis;
+ }
+
+ public void onCallBlockingDone() {
+ if (mCallBlockingTimeMillis == INVALID_TIME) {
+ mCallBlockingTimeMillis = SystemClock.elapsedRealtime();
+ }
+ }
+
+ public long getCallNotificationTimeMillis() {
+ return mCallNotificationTimeMillis;
+ }
+
+ public void onNotificationShown() {
+ if (mCallNotificationTimeMillis == INVALID_TIME) {
+ mCallNotificationTimeMillis = SystemClock.elapsedRealtime();
+ }
+ }
+
+ public long getInCallUiShownTimeMillis() {
+ return mInCallUiShownTimeMillis;
+ }
+
+ public void onInCallUiShown(boolean forFullScreenIntent) {
+ if (mInCallUiShownTimeMillis == INVALID_TIME) {
+ mInCallUiShownTimeMillis = SystemClock.elapsedRealtime();
+ mDidDisplayHeadsUpNotification = mWasIncoming && !forFullScreenIntent;
+ }
+ }
+
+ public boolean getDidDisplayHeadsUpNotification() {
+ return mDidDisplayHeadsUpNotification;
+ }
+}
diff --git a/java/com/android/incallui/legacyblocking/BlockedNumberContentObserver.java b/java/com/android/incallui/legacyblocking/BlockedNumberContentObserver.java
new file mode 100644
index 000000000..9b5335b69
--- /dev/null
+++ b/java/com/android/incallui/legacyblocking/BlockedNumberContentObserver.java
@@ -0,0 +1,105 @@
+/*
+ * 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.incallui.legacyblocking;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.CallLog;
+import android.support.annotation.NonNull;
+import com.android.dialer.common.AsyncTaskExecutor;
+import com.android.dialer.common.AsyncTaskExecutors;
+import com.android.dialer.common.LogUtil;
+import java.util.Objects;
+
+/**
+ * Observes the {@link CallLog} to delete the CallLog entry for a blocked call after it is added.
+ * Automatically de-registers itself {@link #TIMEOUT_MS} ms after registration or if the entry is
+ * found and deleted.
+ */
+public class BlockedNumberContentObserver extends ContentObserver
+ implements DeleteBlockedCallTask.Listener {
+
+ /**
+ * The time after which a {@link BlockedNumberContentObserver} will be automatically unregistered.
+ */
+ public static final int TIMEOUT_MS = 5000;
+
+ @NonNull private final Context context;
+ @NonNull private final Handler handler;
+ private final String number;
+ private final long timeAddedMillis;
+ private final Runnable timeoutRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ unregister();
+ }
+ };
+
+ private final AsyncTaskExecutor asyncTaskExecutor = AsyncTaskExecutors.createThreadPoolExecutor();
+
+ /**
+ * Creates the BlockedNumberContentObserver to delete the new {@link CallLog} entry from the given
+ * blocked number.
+ *
+ * @param number The blocked number.
+ * @param timeAddedMillis The time at which the call from the blocked number was placed.
+ */
+ public BlockedNumberContentObserver(
+ @NonNull Context context, @NonNull Handler handler, String number, long timeAddedMillis) {
+ super(handler);
+ this.context = Objects.requireNonNull(context, "context").getApplicationContext();
+ this.handler = Objects.requireNonNull(handler);
+ this.number = number;
+ this.timeAddedMillis = timeAddedMillis;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ LogUtil.i(
+ "BlockedNumberContentObserver.onChange",
+ "attempting to remove call log entry from blocked number");
+ asyncTaskExecutor.submit(
+ DeleteBlockedCallTask.IDENTIFIER,
+ new DeleteBlockedCallTask(context, this, number, timeAddedMillis));
+ }
+
+ @Override
+ public void onDeleteBlockedCallTaskComplete(boolean didFindEntry) {
+ if (didFindEntry) {
+ unregister();
+ }
+ }
+
+ /**
+ * Registers this {@link ContentObserver} to listen for changes to the {@link CallLog}. If the
+ * CallLog entry is not found before {@link #TIMEOUT_MS}, this ContentObserver automatically
+ * un-registers itself.
+ */
+ public void register() {
+ LogUtil.i("BlockedNumberContentObserver.register", null);
+ context.getContentResolver().registerContentObserver(CallLog.CONTENT_URI, true, this);
+ handler.postDelayed(timeoutRunnable, TIMEOUT_MS);
+ }
+
+ private void unregister() {
+ LogUtil.i("BlockedNumberContentObserver.unregister", null);
+ handler.removeCallbacks(timeoutRunnable);
+ context.getContentResolver().unregisterContentObserver(this);
+ }
+}
diff --git a/java/com/android/incallui/legacyblocking/DeleteBlockedCallTask.java b/java/com/android/incallui/legacyblocking/DeleteBlockedCallTask.java
new file mode 100644
index 000000000..a3f2dfa4d
--- /dev/null
+++ b/java/com/android/incallui/legacyblocking/DeleteBlockedCallTask.java
@@ -0,0 +1,124 @@
+/*
+ * 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.incallui.legacyblocking;
+
+import android.Manifest.permission;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.os.AsyncTask;
+import android.os.Build.VERSION_CODES;
+import android.provider.CallLog;
+import android.support.v4.content.ContextCompat;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.telecom.TelecomUtil;
+import java.util.Objects;
+
+/**
+ * Deletes a blocked call from the call log. This is only used on Android Marshmallow. On later
+ * versions of the OS, call blocking is implemented in the system and there's no need to mess with
+ * the call log.
+ */
+@TargetApi(VERSION_CODES.M)
+public class DeleteBlockedCallTask extends AsyncTask<Void, Void, Long> {
+
+ public static final String IDENTIFIER = "DeleteBlockedCallTask";
+
+ // Try to identify if a call log entry corresponds to a number which was blocked. We match by
+ // by comparing its creation time to the time it was added in the InCallUi and seeing if they
+ // fall within a certain threshold.
+ private static final int MATCH_BLOCKED_CALL_THRESHOLD_MS = 3000;
+
+ private final Context context;
+ private final Listener listener;
+ private final String number;
+ private final long timeAddedMillis;
+
+ /**
+ * Creates the task to delete the new {@link CallLog} entry from the given blocked number.
+ *
+ * @param number The blocked number.
+ * @param timeAddedMillis The time at which the call from the blocked number was placed.
+ */
+ public DeleteBlockedCallTask(
+ Context context, Listener listener, String number, long timeAddedMillis) {
+ this.context = Objects.requireNonNull(context);
+ this.listener = Objects.requireNonNull(listener);
+ this.number = number;
+ this.timeAddedMillis = timeAddedMillis;
+ }
+
+ @Override
+ public Long doInBackground(Void... params) {
+ if (ContextCompat.checkSelfPermission(context, permission.READ_CALL_LOG)
+ != PackageManager.PERMISSION_GRANTED
+ || ContextCompat.checkSelfPermission(context, permission.WRITE_CALL_LOG)
+ != PackageManager.PERMISSION_GRANTED) {
+ LogUtil.i("DeleteBlockedCallTask.doInBackground", "missing call log permissions");
+ return -1L;
+ }
+
+ // First, lookup the call log entry of the most recent call with this number.
+ try (Cursor cursor =
+ context
+ .getContentResolver()
+ .query(
+ TelecomUtil.getCallLogUri(context),
+ CallLogDeleteBlockedCallQuery.PROJECTION,
+ CallLog.Calls.NUMBER + "= ?",
+ new String[] {number},
+ CallLog.Calls.DATE + " DESC LIMIT 1")) {
+
+ // If match is found, delete this call log entry and return the call log entry id.
+ if (cursor != null && cursor.moveToFirst()) {
+ long creationTime = cursor.getLong(CallLogDeleteBlockedCallQuery.DATE_COLUMN_INDEX);
+ if (timeAddedMillis > creationTime
+ && timeAddedMillis - creationTime < MATCH_BLOCKED_CALL_THRESHOLD_MS) {
+ long callLogEntryId = cursor.getLong(CallLogDeleteBlockedCallQuery.ID_COLUMN_INDEX);
+ context
+ .getContentResolver()
+ .delete(
+ TelecomUtil.getCallLogUri(context),
+ CallLog.Calls._ID + " IN (" + callLogEntryId + ")",
+ null);
+ return callLogEntryId;
+ }
+ }
+ }
+ return -1L;
+ }
+
+ @Override
+ public void onPostExecute(Long callLogEntryId) {
+ listener.onDeleteBlockedCallTaskComplete(callLogEntryId >= 0);
+ }
+
+ /** Callback invoked when delete is complete. */
+ public interface Listener {
+
+ void onDeleteBlockedCallTaskComplete(boolean didFindEntry);
+ }
+
+ private static class CallLogDeleteBlockedCallQuery {
+
+ static final String[] PROJECTION = new String[] {CallLog.Calls._ID, CallLog.Calls.DATE};
+
+ static final int ID_COLUMN_INDEX = 0;
+ static final int DATE_COLUMN_INDEX = 1;
+ }
+}
diff --git a/java/com/android/incallui/maps/StaticMapBinding.java b/java/com/android/incallui/maps/StaticMapBinding.java
new file mode 100644
index 000000000..9d24ef27a
--- /dev/null
+++ b/java/com/android/incallui/maps/StaticMapBinding.java
@@ -0,0 +1,51 @@
+/*
+ * 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.incallui.maps;
+
+import android.app.Application;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+
+/** Utility for getting a {@link StaticMapFactory} */
+public class StaticMapBinding {
+
+ @Nullable
+ public static StaticMapFactory get(@NonNull Application application) {
+ if (useTestingInstance) {
+ return testingInstance;
+ }
+ if (application instanceof StaticMapFactory) {
+ return ((StaticMapFactory) application);
+ }
+ return null;
+ }
+
+ private static StaticMapFactory testingInstance;
+ private static boolean useTestingInstance;
+
+ @VisibleForTesting
+ public static void setForTesting(@Nullable StaticMapFactory staticMapFactory) {
+ testingInstance = staticMapFactory;
+ useTestingInstance = true;
+ }
+
+ @VisibleForTesting
+ public static void clearForTesting() {
+ useTestingInstance = false;
+ }
+}
diff --git a/java/com/android/incallui/maps/StaticMapFactory.java b/java/com/android/incallui/maps/StaticMapFactory.java
new file mode 100644
index 000000000..a35013886
--- /dev/null
+++ b/java/com/android/incallui/maps/StaticMapFactory.java
@@ -0,0 +1,28 @@
+/*
+ * 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.incallui.maps;
+
+import android.location.Location;
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+
+/** A Factory that can create Fragments for showing a static map */
+public interface StaticMapFactory {
+
+ @NonNull
+ Fragment getStaticMap(@NonNull Location location);
+}
diff --git a/java/com/android/incallui/res/anim/activity_open_enter.xml b/java/com/android/incallui/res/anim/activity_open_enter.xml
new file mode 100644
index 000000000..71cc096b9
--- /dev/null
+++ b/java/com/android/incallui/res/anim/activity_open_enter.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:zAdjustment="top">
+ <alpha
+ android:duration="300"
+ android:fillAfter="true"
+ android:fillBefore="false"
+ android:fillEnabled="true"
+ android:fromAlpha="0.0"
+ android:interpolator="@anim/decelerate_cubic"
+ android:toAlpha="1.0"/>
+ <scale
+ android:duration="300"
+ android:fillAfter="true"
+ android:fillBefore="false"
+ android:fillEnabled="true"
+ android:fromXScale=".8"
+ android:fromYScale=".8"
+ android:interpolator="@anim/decelerate_cubic"
+ android:pivotX="50%p"
+ android:pivotY="50%p"
+ android:toXScale="1.0"
+ android:toYScale="1.0"/>
+</set> \ No newline at end of file
diff --git a/java/com/android/incallui/res/anim/activity_open_exit.xml b/java/com/android/incallui/res/anim/activity_open_exit.xml
new file mode 100644
index 000000000..9b36bb358
--- /dev/null
+++ b/java/com/android/incallui/res/anim/activity_open_exit.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:background="#ff000000"
+ android:zAdjustment="normal">
+ <alpha
+ android:duration="300"
+ android:fillAfter="true"
+ android:fillBefore="false"
+ android:fillEnabled="true"
+ android:fromAlpha="1.0"
+ android:interpolator="@anim/decelerate_quint"
+ android:toAlpha="0.0"/>
+</set> \ No newline at end of file
diff --git a/InCallUI/res/anim/decelerate_cubic.xml b/java/com/android/incallui/res/anim/decelerate_cubic.xml
index f98809165..c2f41597b 100644
--- a/InCallUI/res/anim/decelerate_cubic.xml
+++ b/java/com/android/incallui/res/anim/decelerate_cubic.xml
@@ -18,4 +18,4 @@
-->
<decelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:factor="1.5" />
+ android:factor="1.5"/>
diff --git a/InCallUI/res/anim/decelerate_quint.xml b/java/com/android/incallui/res/anim/decelerate_quint.xml
index ff2d5a9d0..e55e99c0b 100644
--- a/InCallUI/res/anim/decelerate_quint.xml
+++ b/java/com/android/incallui/res/anim/decelerate_quint.xml
@@ -18,4 +18,4 @@
-->
<decelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:factor="2.5" />
+ android:factor="2.5"/>
diff --git a/java/com/android/incallui/res/anim/on_going_call.xml b/java/com/android/incallui/res/anim/on_going_call.xml
new file mode 100644
index 000000000..3a2e2ba1a
--- /dev/null
+++ b/java/com/android/incallui/res/anim/on_going_call.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
+ android:oneshot="false">
+ <item
+ android:drawable="@drawable/ic_ongoing_phone_24px_01"
+ android:duration="200"/>
+ <item
+ android:drawable="@drawable/ic_ongoing_phone_24px_02"
+ android:duration="200"/>
+ <item
+ android:drawable="@drawable/ic_ongoing_phone_24px_03"
+ android:duration="200"/>
+ <item
+ android:drawable="@drawable/ic_ongoing_phone_24px_04"
+ android:duration="200"/>
+ <item
+ android:drawable="@drawable/ic_ongoing_phone_24px_05"
+ android:duration="200"/>
+ <item
+ android:drawable="@drawable/ic_ongoing_phone_24px_06"
+ android:duration="200"/>
+ <item
+ android:drawable="@drawable/ic_ongoing_phone_24px_07"
+ android:duration="200"/>
+ <item
+ android:drawable="@drawable/ic_ongoing_phone_24px_08"
+ android:duration="200"/>
+ <item
+ android:drawable="@drawable/ic_ongoing_phone_24px_09"
+ android:duration="200"/>
+</animation-list> \ No newline at end of file
diff --git a/InCallUI/res/color/ota_title_color.xml b/java/com/android/incallui/res/color/ota_title_color.xml
index 14a283a6b..bf36f56b9 100644
--- a/InCallUI/res/color/ota_title_color.xml
+++ b/java/com/android/incallui/res/color/ota_title_color.xml
@@ -16,6 +16,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="#FFA6C839"/>
+ <item android:color="#FFA6C839"/>
</selector>
diff --git a/InCallUI/res/drawable-hdpi/ic_block_grey600_24dp.png b/java/com/android/incallui/res/drawable-hdpi/ic_block_grey600_24dp.png
index 1e9294c12..1e9294c12 100644
--- a/InCallUI/res/drawable-hdpi/ic_block_grey600_24dp.png
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_block_grey600_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_call_end_white_24dp.png b/java/com/android/incallui/res/drawable-hdpi/ic_call_end_white_24dp.png
index 757d339c4..757d339c4 100644
--- a/InCallUI/res/drawable-hdpi/ic_call_end_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_call_end_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_call_split_white_24dp.png b/java/com/android/incallui/res/drawable-hdpi/ic_call_split_white_24dp.png
index 4e3dbf55d..4e3dbf55d 100644
--- a/InCallUI/res/drawable-hdpi/ic_call_split_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_call_split_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_close_grey600_24dp.png b/java/com/android/incallui/res/drawable-hdpi/ic_close_grey600_24dp.png
index 9ab350e9a..9ab350e9a 100644
--- a/InCallUI/res/drawable-hdpi/ic_close_grey600_24dp.png
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_close_grey600_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_location_on_white_24dp.png b/java/com/android/incallui/res/drawable-hdpi/ic_location_on_white_24dp.png
index 7c281c3f5..7c281c3f5 100644
--- a/InCallUI/res/drawable-hdpi/ic_location_on_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_location_on_white_24dp.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_01.png b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_01.png
new file mode 100644
index 000000000..e4ff6db13
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_01.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_02.png b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_02.png
new file mode 100644
index 000000000..bc2b3d2f8
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_02.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_03.png b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_03.png
new file mode 100644
index 000000000..fa936cbdc
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_03.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_04.png b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_04.png
new file mode 100644
index 000000000..ef5137976
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_04.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_05.png b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_05.png
new file mode 100644
index 000000000..3712d164d
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_05.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_06.png b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_06.png
new file mode 100644
index 000000000..c6a4216a3
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_06.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_07.png b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_07.png
new file mode 100644
index 000000000..e4ff6db13
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_07.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_08.png b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_08.png
new file mode 100644
index 000000000..e4ff6db13
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_08.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_09.png b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_09.png
new file mode 100644
index 000000000..e4ff6db13
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_ongoing_phone_24px_09.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_person_add_grey600_24dp.png b/java/com/android/incallui/res/drawable-hdpi/ic_person_add_grey600_24dp.png
index 185d03393..185d03393 100644
--- a/InCallUI/res/drawable-hdpi/ic_person_add_grey600_24dp.png
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_person_add_grey600_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_phone_paused_white_24dp.png b/java/com/android/incallui/res/drawable-hdpi/ic_phone_paused_white_24dp.png
index a2177f58a..a2177f58a 100644
--- a/InCallUI/res/drawable-hdpi/ic_phone_paused_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_phone_paused_white_24dp.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-hdpi/ic_question_mark.png b/java/com/android/incallui/res/drawable-hdpi/ic_question_mark.png
new file mode 100644
index 000000000..bd9489c85
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_question_mark.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_schedule_white_24dp.png b/java/com/android/incallui/res/drawable-hdpi/ic_schedule_white_24dp.png
index f3581d104..f3581d104 100644
--- a/InCallUI/res/drawable-hdpi/ic_schedule_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-hdpi/ic_schedule_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/img_business.png b/java/com/android/incallui/res/drawable-hdpi/img_business.png
index f70634262..f70634262 100644
--- a/InCallUI/res/drawable-hdpi/img_business.png
+++ b/java/com/android/incallui/res/drawable-hdpi/img_business.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/img_conference.png b/java/com/android/incallui/res/drawable-hdpi/img_conference.png
index 3d9f683a5..3d9f683a5 100644
--- a/InCallUI/res/drawable-hdpi/img_conference.png
+++ b/java/com/android/incallui/res/drawable-hdpi/img_conference.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/img_no_image.png b/java/com/android/incallui/res/drawable-hdpi/img_no_image.png
index fd0ab3211..fd0ab3211 100644
--- a/InCallUI/res/drawable-hdpi/img_no_image.png
+++ b/java/com/android/incallui/res/drawable-hdpi/img_no_image.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/img_phone.png b/java/com/android/incallui/res/drawable-hdpi/img_phone.png
index 748312e6e..748312e6e 100644
--- a/InCallUI/res/drawable-hdpi/img_phone.png
+++ b/java/com/android/incallui/res/drawable-hdpi/img_phone.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_block_grey600_24dp.png b/java/com/android/incallui/res/drawable-mdpi/ic_block_grey600_24dp.png
index edd666b73..edd666b73 100644
--- a/InCallUI/res/drawable-mdpi/ic_block_grey600_24dp.png
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_block_grey600_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_call_end_white_24dp.png b/java/com/android/incallui/res/drawable-mdpi/ic_call_end_white_24dp.png
index 17eb4824e..17eb4824e 100644
--- a/InCallUI/res/drawable-mdpi/ic_call_end_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_call_end_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_call_split_white_24dp.png b/java/com/android/incallui/res/drawable-mdpi/ic_call_split_white_24dp.png
index cb7ee1f35..cb7ee1f35 100644
--- a/InCallUI/res/drawable-mdpi/ic_call_split_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_call_split_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_close_grey600_24dp.png b/java/com/android/incallui/res/drawable-mdpi/ic_close_grey600_24dp.png
index 73faf52eb..73faf52eb 100644
--- a/InCallUI/res/drawable-mdpi/ic_close_grey600_24dp.png
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_close_grey600_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_location_on_white_24dp.png b/java/com/android/incallui/res/drawable-mdpi/ic_location_on_white_24dp.png
index 933eb5148..933eb5148 100644
--- a/InCallUI/res/drawable-mdpi/ic_location_on_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_location_on_white_24dp.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_01.png b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_01.png
new file mode 100644
index 000000000..ae31e047e
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_01.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_02.png b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_02.png
new file mode 100644
index 000000000..67b2b1622
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_02.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_03.png b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_03.png
new file mode 100644
index 000000000..46abea337
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_03.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_04.png b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_04.png
new file mode 100644
index 000000000..0d787ffa4
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_04.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_05.png b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_05.png
new file mode 100644
index 000000000..2da4b40d6
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_05.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_06.png b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_06.png
new file mode 100644
index 000000000..a34cf4d56
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_06.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_07.png b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_07.png
new file mode 100644
index 000000000..ae31e047e
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_07.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_08.png b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_08.png
new file mode 100644
index 000000000..ae31e047e
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_08.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_09.png b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_09.png
new file mode 100644
index 000000000..ae31e047e
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_ongoing_phone_24px_09.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_person_add_grey600_24dp.png b/java/com/android/incallui/res/drawable-mdpi/ic_person_add_grey600_24dp.png
index ec3237086..ec3237086 100644
--- a/InCallUI/res/drawable-mdpi/ic_person_add_grey600_24dp.png
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_person_add_grey600_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_phone_paused_white_24dp.png b/java/com/android/incallui/res/drawable-mdpi/ic_phone_paused_white_24dp.png
index 7dc920b2b..7dc920b2b 100644
--- a/InCallUI/res/drawable-mdpi/ic_phone_paused_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_phone_paused_white_24dp.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-mdpi/ic_question_mark.png b/java/com/android/incallui/res/drawable-mdpi/ic_question_mark.png
new file mode 100644
index 000000000..594d0b9f7
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_question_mark.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_schedule_white_24dp.png b/java/com/android/incallui/res/drawable-mdpi/ic_schedule_white_24dp.png
index 501ee842e..501ee842e 100644
--- a/InCallUI/res/drawable-mdpi/ic_schedule_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-mdpi/ic_schedule_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/img_business.png b/java/com/android/incallui/res/drawable-mdpi/img_business.png
index 90738a7ee..90738a7ee 100644
--- a/InCallUI/res/drawable-mdpi/img_business.png
+++ b/java/com/android/incallui/res/drawable-mdpi/img_business.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/img_conference.png b/java/com/android/incallui/res/drawable-mdpi/img_conference.png
index 0694dbd55..0694dbd55 100644
--- a/InCallUI/res/drawable-mdpi/img_conference.png
+++ b/java/com/android/incallui/res/drawable-mdpi/img_conference.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/img_no_image.png b/java/com/android/incallui/res/drawable-mdpi/img_no_image.png
index 014a1c414..014a1c414 100644
--- a/InCallUI/res/drawable-mdpi/img_no_image.png
+++ b/java/com/android/incallui/res/drawable-mdpi/img_no_image.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/img_phone.png b/java/com/android/incallui/res/drawable-mdpi/img_phone.png
index 41a1d339d..41a1d339d 100644
--- a/InCallUI/res/drawable-mdpi/img_phone.png
+++ b/java/com/android/incallui/res/drawable-mdpi/img_phone.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_block_grey600_24dp.png b/java/com/android/incallui/res/drawable-xhdpi/ic_block_grey600_24dp.png
index 36210a8cb..36210a8cb 100644
--- a/InCallUI/res/drawable-xhdpi/ic_block_grey600_24dp.png
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_block_grey600_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_call_end_white_24dp.png b/java/com/android/incallui/res/drawable-xhdpi/ic_call_end_white_24dp.png
index b00d82edd..b00d82edd 100644
--- a/InCallUI/res/drawable-xhdpi/ic_call_end_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_call_end_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_call_split_white_24dp.png b/java/com/android/incallui/res/drawable-xhdpi/ic_call_split_white_24dp.png
index 218cb1214..218cb1214 100644
--- a/InCallUI/res/drawable-xhdpi/ic_call_split_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_call_split_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_close_grey600_24dp.png b/java/com/android/incallui/res/drawable-xhdpi/ic_close_grey600_24dp.png
index a3896c5c6..a3896c5c6 100644
--- a/InCallUI/res/drawable-xhdpi/ic_close_grey600_24dp.png
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_close_grey600_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_location_on_white_24dp.png b/java/com/android/incallui/res/drawable-xhdpi/ic_location_on_white_24dp.png
index 814ca8ddc..814ca8ddc 100644
--- a/InCallUI/res/drawable-xhdpi/ic_location_on_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_location_on_white_24dp.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_01.png b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_01.png
new file mode 100644
index 000000000..80ad50b59
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_01.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_02.png b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_02.png
new file mode 100644
index 000000000..1fb69a477
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_02.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_03.png b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_03.png
new file mode 100644
index 000000000..2578be1e2
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_03.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_04.png b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_04.png
new file mode 100644
index 000000000..9a5b91fe5
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_04.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_05.png b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_05.png
new file mode 100644
index 000000000..69b472b00
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_05.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_06.png b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_06.png
new file mode 100644
index 000000000..118ea33d0
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_06.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_07.png b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_07.png
new file mode 100644
index 000000000..80ad50b59
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_07.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_08.png b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_08.png
new file mode 100644
index 000000000..80ad50b59
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_08.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_09.png b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_09.png
new file mode 100644
index 000000000..80ad50b59
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_ongoing_phone_24px_09.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_person_add_grey600_24dp.png b/java/com/android/incallui/res/drawable-xhdpi/ic_person_add_grey600_24dp.png
index e56481ed7..e56481ed7 100644
--- a/InCallUI/res/drawable-xhdpi/ic_person_add_grey600_24dp.png
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_person_add_grey600_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_phone_paused_white_24dp.png b/java/com/android/incallui/res/drawable-xhdpi/ic_phone_paused_white_24dp.png
index a8becf485..a8becf485 100644
--- a/InCallUI/res/drawable-xhdpi/ic_phone_paused_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_phone_paused_white_24dp.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xhdpi/ic_question_mark.png b/java/com/android/incallui/res/drawable-xhdpi/ic_question_mark.png
new file mode 100644
index 000000000..ec915f610
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_question_mark.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_schedule_white_24dp.png b/java/com/android/incallui/res/drawable-xhdpi/ic_schedule_white_24dp.png
index 2e27936a4..2e27936a4 100644
--- a/InCallUI/res/drawable-xhdpi/ic_schedule_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-xhdpi/ic_schedule_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/img_business.png b/java/com/android/incallui/res/drawable-xhdpi/img_business.png
index 7b04d956f..7b04d956f 100644
--- a/InCallUI/res/drawable-xhdpi/img_business.png
+++ b/java/com/android/incallui/res/drawable-xhdpi/img_business.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/img_conference.png b/java/com/android/incallui/res/drawable-xhdpi/img_conference.png
index b0dbcc2dc..b0dbcc2dc 100644
--- a/InCallUI/res/drawable-xhdpi/img_conference.png
+++ b/java/com/android/incallui/res/drawable-xhdpi/img_conference.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/img_no_image.png b/java/com/android/incallui/res/drawable-xhdpi/img_no_image.png
index 4022207d0..4022207d0 100644
--- a/InCallUI/res/drawable-xhdpi/img_no_image.png
+++ b/java/com/android/incallui/res/drawable-xhdpi/img_no_image.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/img_phone.png b/java/com/android/incallui/res/drawable-xhdpi/img_phone.png
index 2e0ceec0f..2e0ceec0f 100644
--- a/InCallUI/res/drawable-xhdpi/img_phone.png
+++ b/java/com/android/incallui/res/drawable-xhdpi/img_phone.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_block_grey600_24dp.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_block_grey600_24dp.png
index 9f5120373..9f5120373 100644
--- a/InCallUI/res/drawable-xxhdpi/ic_block_grey600_24dp.png
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_block_grey600_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_call_end_white_24dp.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_call_end_white_24dp.png
index aeabe4a81..aeabe4a81 100644
--- a/InCallUI/res/drawable-xxhdpi/ic_call_end_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_call_end_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_call_split_white_24dp.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_call_split_white_24dp.png
index 5ea577716..5ea577716 100644
--- a/InCallUI/res/drawable-xxhdpi/ic_call_split_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_call_split_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_close_grey600_24dp.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_close_grey600_24dp.png
index 22d7aa55e..22d7aa55e 100644
--- a/InCallUI/res/drawable-xxhdpi/ic_close_grey600_24dp.png
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_close_grey600_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_location_on_white_24dp.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_location_on_white_24dp.png
index 078b10d4f..078b10d4f 100644
--- a/InCallUI/res/drawable-xxhdpi/ic_location_on_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_location_on_white_24dp.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_01.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_01.png
new file mode 100644
index 000000000..871a1ee75
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_01.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_02.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_02.png
new file mode 100644
index 000000000..028e43b6e
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_02.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_03.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_03.png
new file mode 100644
index 000000000..b7dd070e1
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_03.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_04.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_04.png
new file mode 100644
index 000000000..887c803f8
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_04.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_05.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_05.png
new file mode 100644
index 000000000..c6ec16893
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_05.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_06.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_06.png
new file mode 100644
index 000000000..d0b1e8649
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_06.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_07.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_07.png
new file mode 100644
index 000000000..871a1ee75
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_07.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_08.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_08.png
new file mode 100644
index 000000000..871a1ee75
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_08.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_09.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_09.png
new file mode 100644
index 000000000..871a1ee75
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_ongoing_phone_24px_09.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_person_add_grey600_24dp.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_person_add_grey600_24dp.png
index c17dfe05f..c17dfe05f 100644
--- a/InCallUI/res/drawable-xxhdpi/ic_person_add_grey600_24dp.png
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_person_add_grey600_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_phone_paused_white_24dp.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_phone_paused_white_24dp.png
index baf0cf27f..baf0cf27f 100644
--- a/InCallUI/res/drawable-xxhdpi/ic_phone_paused_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_phone_paused_white_24dp.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xxhdpi/ic_question_mark.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_question_mark.png
new file mode 100644
index 000000000..e3f6d285e
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_question_mark.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_schedule_white_24dp.png b/java/com/android/incallui/res/drawable-xxhdpi/ic_schedule_white_24dp.png
index bfc72736a..bfc72736a 100644
--- a/InCallUI/res/drawable-xxhdpi/ic_schedule_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-xxhdpi/ic_schedule_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/img_business.png b/java/com/android/incallui/res/drawable-xxhdpi/img_business.png
index c17e4c9d8..c17e4c9d8 100644
--- a/InCallUI/res/drawable-xxhdpi/img_business.png
+++ b/java/com/android/incallui/res/drawable-xxhdpi/img_business.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/img_conference.png b/java/com/android/incallui/res/drawable-xxhdpi/img_conference.png
index a8dba5ed0..a8dba5ed0 100644
--- a/InCallUI/res/drawable-xxhdpi/img_conference.png
+++ b/java/com/android/incallui/res/drawable-xxhdpi/img_conference.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/img_no_image.png b/java/com/android/incallui/res/drawable-xxhdpi/img_no_image.png
index 2cf7f23a0..2cf7f23a0 100644
--- a/InCallUI/res/drawable-xxhdpi/img_no_image.png
+++ b/java/com/android/incallui/res/drawable-xxhdpi/img_no_image.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/img_phone.png b/java/com/android/incallui/res/drawable-xxhdpi/img_phone.png
index 4eaaba509..4eaaba509 100644
--- a/InCallUI/res/drawable-xxhdpi/img_phone.png
+++ b/java/com/android/incallui/res/drawable-xxhdpi/img_phone.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_block_grey600_24dp.png b/java/com/android/incallui/res/drawable-xxxhdpi/ic_block_grey600_24dp.png
index 01df2b52b..01df2b52b 100644
--- a/InCallUI/res/drawable-xxxhdpi/ic_block_grey600_24dp.png
+++ b/java/com/android/incallui/res/drawable-xxxhdpi/ic_block_grey600_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_call_end_white_24dp.png b/java/com/android/incallui/res/drawable-xxxhdpi/ic_call_end_white_24dp.png
index a6e8a7bc1..a6e8a7bc1 100644
--- a/InCallUI/res/drawable-xxxhdpi/ic_call_end_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-xxxhdpi/ic_call_end_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_call_split_white_24dp.png b/java/com/android/incallui/res/drawable-xxxhdpi/ic_call_split_white_24dp.png
index 600cec8e6..600cec8e6 100644
--- a/InCallUI/res/drawable-xxxhdpi/ic_call_split_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-xxxhdpi/ic_call_split_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_close_grey600_24dp.png b/java/com/android/incallui/res/drawable-xxxhdpi/ic_close_grey600_24dp.png
index 7d1c061f7..7d1c061f7 100644
--- a/InCallUI/res/drawable-xxxhdpi/ic_close_grey600_24dp.png
+++ b/java/com/android/incallui/res/drawable-xxxhdpi/ic_close_grey600_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_location_on_white_24dp.png b/java/com/android/incallui/res/drawable-xxxhdpi/ic_location_on_white_24dp.png
index 8bcb6f620..8bcb6f620 100644
--- a/InCallUI/res/drawable-xxxhdpi/ic_location_on_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-xxxhdpi/ic_location_on_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_person_add_grey600_24dp.png b/java/com/android/incallui/res/drawable-xxxhdpi/ic_person_add_grey600_24dp.png
index e24919737..e24919737 100644
--- a/InCallUI/res/drawable-xxxhdpi/ic_person_add_grey600_24dp.png
+++ b/java/com/android/incallui/res/drawable-xxxhdpi/ic_person_add_grey600_24dp.png
Binary files differ
diff --git a/java/com/android/incallui/res/drawable-xxxhdpi/ic_question_mark.png b/java/com/android/incallui/res/drawable-xxxhdpi/ic_question_mark.png
new file mode 100644
index 000000000..1a6bf1eb3
--- /dev/null
+++ b/java/com/android/incallui/res/drawable-xxxhdpi/ic_question_mark.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_schedule_white_24dp.png b/java/com/android/incallui/res/drawable-xxxhdpi/ic_schedule_white_24dp.png
index b94f4dfa1..b94f4dfa1 100644
--- a/InCallUI/res/drawable-xxxhdpi/ic_schedule_white_24dp.png
+++ b/java/com/android/incallui/res/drawable-xxxhdpi/ic_schedule_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/img_business.png b/java/com/android/incallui/res/drawable-xxxhdpi/img_business.png
index 88f14e999..88f14e999 100644
--- a/InCallUI/res/drawable-xxxhdpi/img_business.png
+++ b/java/com/android/incallui/res/drawable-xxxhdpi/img_business.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/img_conference.png b/java/com/android/incallui/res/drawable-xxxhdpi/img_conference.png
index eb42b5552..eb42b5552 100644
--- a/InCallUI/res/drawable-xxxhdpi/img_conference.png
+++ b/java/com/android/incallui/res/drawable-xxxhdpi/img_conference.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/img_no_image.png b/java/com/android/incallui/res/drawable-xxxhdpi/img_no_image.png
index 216574222..216574222 100644
--- a/InCallUI/res/drawable-xxxhdpi/img_no_image.png
+++ b/java/com/android/incallui/res/drawable-xxxhdpi/img_no_image.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/img_phone.png b/java/com/android/incallui/res/drawable-xxxhdpi/img_phone.png
index 7cbfbd75e..7cbfbd75e 100644
--- a/InCallUI/res/drawable-xxxhdpi/img_phone.png
+++ b/java/com/android/incallui/res/drawable-xxxhdpi/img_phone.png
Binary files differ
diff --git a/InCallUI/res/drawable/img_conference_automirrored.xml b/java/com/android/incallui/res/drawable/img_conference_automirrored.xml
index fa1fd4920..78b2876bc 100644
--- a/InCallUI/res/drawable/img_conference_automirrored.xml
+++ b/java/com/android/incallui/res/drawable/img_conference_automirrored.xml
@@ -17,5 +17,5 @@
-->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/img_conference"
- android:autoMirrored="true" /> \ No newline at end of file
+ android:autoMirrored="true"
+ android:src="@drawable/img_conference"/> \ No newline at end of file
diff --git a/InCallUI/res/drawable/img_no_image_automirrored.xml b/java/com/android/incallui/res/drawable/img_no_image_automirrored.xml
index f0cf0db31..9a9ec9706 100644
--- a/InCallUI/res/drawable/img_no_image_automirrored.xml
+++ b/java/com/android/incallui/res/drawable/img_no_image_automirrored.xml
@@ -17,5 +17,5 @@
-->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/img_no_image"
- android:autoMirrored="true" /> \ No newline at end of file
+ android:autoMirrored="true"
+ android:src="@drawable/img_no_image"/> \ No newline at end of file
diff --git a/java/com/android/incallui/res/drawable/incall_background_gradient.xml b/java/com/android/incallui/res/drawable/incall_background_gradient.xml
new file mode 100644
index 000000000..5dd927f0f
--- /dev/null
+++ b/java/com/android/incallui/res/drawable/incall_background_gradient.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:angle="270"
+ android:startColor="@color/incall_background_gradient_top"
+ android:centerColor="@color/incall_background_gradient_middle"
+ android:endColor="@color/incall_background_gradient_bottom"/>
+</shape>
diff --git a/java/com/android/incallui/res/drawable/spam_notification_icon.xml b/java/com/android/incallui/res/drawable/spam_notification_icon.xml
new file mode 100644
index 000000000..266897838
--- /dev/null
+++ b/java/com/android/incallui/res/drawable/spam_notification_icon.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="@color/incall_call_spam_background_color"/>
+ <size
+ android:height="@dimen/notification_large_icon_height"
+ android:width="@dimen/notification_large_icon_width"/>
+ </shape>
+ </item>
+
+ <item
+ android:drawable="@drawable/ic_report_white_36dp"
+ android:gravity="center"/>
+
+</layer-list> \ No newline at end of file
diff --git a/java/com/android/incallui/res/drawable/unknown_notification_icon.xml b/java/com/android/incallui/res/drawable/unknown_notification_icon.xml
new file mode 100644
index 000000000..5ab07eccd
--- /dev/null
+++ b/java/com/android/incallui/res/drawable/unknown_notification_icon.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="@color/unknown_number_color"/>
+ <size
+ android:height="@dimen/notification_large_icon_height"
+ android:width="@dimen/notification_large_icon_width"/>
+ </shape>
+ </item>
+
+ <item
+ android:drawable="@drawable/ic_question_mark"
+ android:gravity="center"/>
+
+</layer-list> \ No newline at end of file
diff --git a/java/com/android/incallui/res/layout/activity_manage_conference.xml b/java/com/android/incallui/res/layout/activity_manage_conference.xml
new file mode 100644
index 000000000..60512938c
--- /dev/null
+++ b/java/com/android/incallui/res/layout/activity_manage_conference.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/manageConferencePanel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+</FrameLayout>
diff --git a/java/com/android/incallui/res/layout/caller_in_conference.xml b/java/com/android/incallui/res/layout/caller_in_conference.xml
new file mode 100644
index 000000000..3a6773d20
--- /dev/null
+++ b/java/com/android/incallui/res/layout/caller_in_conference.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="8dp"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <!-- Caller information -->
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/callerPhoto"
+ android:layout_width="@dimen/contact_browser_list_item_photo_size"
+ android:layout_height="@dimen/contact_browser_list_item_photo_size"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:paddingBottom="2dp"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <!-- Name or number of this caller -->
+ <TextView
+ android:id="@+id/conferenceCallerName"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="2dp"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="@color/conference_call_manager_caller_name_text_color"
+ android:textSize="16sp"/>
+
+ <!-- Number of this caller if name is supplied above -->
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="bottom"
+ android:orientation="horizontal">
+
+ <!-- Number -->
+ <TextView
+ android:id="@+id/conferenceCallerNumber"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
+ android:ellipsize="marquee"
+ android:singleLine="true"
+ android:textColor="@color/conference_call_manager_secondary_text_color"
+ android:textSize="14sp"/>
+
+ <!-- Number type -->
+ <TextView
+ android:id="@+id/conferenceCallerNumberType"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:gravity="start"
+ android:singleLine="true"
+ android:textAllCaps="true"
+ android:textColor="@color/conference_call_manager_secondary_text_color"
+ android:textSize="12sp"/>
+
+ </LinearLayout> <!-- End of caller number -->
+
+ </LinearLayout> <!-- End of caller information -->
+
+ </LinearLayout>
+
+ <!-- "Separate" (i.e. "go private") button for this caller -->
+ <ImageView
+ android:id="@+id/conferenceCallerSeparate"
+ android:layout_width="@dimen/conference_call_manager_button_dimension"
+ android:layout_height="@dimen/conference_call_manager_button_dimension"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:clickable="true"
+ android:contentDescription="@string/goPrivate"
+ android:scaleType="center"
+ android:src="@drawable/ic_call_split_white_24dp"
+ android:tint="@color/conference_call_manager_icon_color"/>
+
+ <!-- "Disconnect" button which terminates the connection with this caller. -->
+ <ImageButton
+ android:id="@+id/conferenceCallerDisconnect"
+ android:layout_width="@dimen/conference_call_manager_button_dimension"
+ android:layout_height="@dimen/conference_call_manager_button_dimension"
+ android:layout_marginStart="8dp"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:clickable="true"
+ android:contentDescription="@string/conference_caller_disconnect_content_description"
+ android:scaleType="center"
+ android:src="@drawable/ic_call_end_white_24dp"
+ android:tint="@color/conference_call_manager_icon_color"/>
+
+</LinearLayout> <!-- End of single list element -->
diff --git a/java/com/android/incallui/res/layout/conference_manager_fragment.xml b/java/com/android/incallui/res/layout/conference_manager_fragment.xml
new file mode 100644
index 000000000..c0cc4cdcf
--- /dev/null
+++ b/java/com/android/incallui/res/layout/conference_manager_fragment.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<!-- The "Manage conference" UI. This panel is displayed (instead of
+ the inCallPanel) when the user clicks the "Manage conference"
+ button while on a conference call. -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/manageConferencePanel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <!-- List of conference participants. -->
+ <ListView
+ android:id="@+id/participantList"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:divider="@null"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:listSelector="@null"/>
+</FrameLayout>
diff --git a/java/com/android/incallui/res/layout/incall_dialpad_fragment.xml b/java/com/android/incallui/res/layout/incall_dialpad_fragment.xml
new file mode 100644
index 000000000..0621d48aa
--- /dev/null
+++ b/java/com/android/incallui/res/layout/incall_dialpad_fragment.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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.
+-->
+
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/dtmf_twelve_key_dialer_view"
+ class="com.android.incallui.DialpadFragment$DialpadSlidingLinearLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <include layout="@layout/dialpad_view"/>
+</view>
diff --git a/java/com/android/incallui/res/layout/incall_screen.xml b/java/com/android/incallui/res/layout/incall_screen.xml
new file mode 100644
index 000000000..9090fb287
--- /dev/null
+++ b/java/com/android/incallui/res/layout/incall_screen.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<!-- In-call Phone UI; see InCallActivity.java. -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <FrameLayout
+ android:id="@+id/main"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <View
+ android:id="@+id/psuedo_black_screen_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#000000"
+ android:visibility="gone"
+ android:keepScreenOn="true"/>
+</FrameLayout>
diff --git a/java/com/android/incallui/res/layout/video_call_lte_to_wifi_failed.xml b/java/com/android/incallui/res/layout/video_call_lte_to_wifi_failed.xml
new file mode 100644
index 000000000..bdc4eaff1
--- /dev/null
+++ b/java/com/android/incallui/res/layout/video_call_lte_to_wifi_failed.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="25dp"
+ android:orientation="vertical">
+
+ <CheckBox
+ android:id="@+id/video_call_lte_to_wifi_failed_checkbox"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/video_call_lte_to_wifi_failed_do_not_show"
+ android:textSize="@dimen/video_call_lte_to_wifi_failed_do_not_show_text_size"/>
+</LinearLayout>
diff --git a/java/com/android/incallui/res/values-sw360dp/dimens.xml b/java/com/android/incallui/res/values-sw360dp/dimens.xml
new file mode 100644
index 000000000..ad782e809
--- /dev/null
+++ b/java/com/android/incallui/res/values-sw360dp/dimens.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2013 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
+ -->
+
+<resources>
+
+ <!-- The InCallUI dialpad will sometimes want digits sizes that are different from dialer. -->
+ <dimen name="incall_dialpad_key_number_margin_bottom">
+ @dimen/dialpad_key_number_default_margin_bottom
+ </dimen>
+ <!-- Zero key should have less space between self and text because "+" is smaller -->
+ <dimen name="incall_dialpad_zero_key_number_margin_bottom">
+ @dimen/dialpad_zero_key_number_default_margin_bottom
+ </dimen>
+ <dimen name="incall_dialpad_digits_adjustable_text_size">@dimen/dialpad_digits_text_size</dimen>
+ <dimen name="incall_dialpad_digits_adjustable_height">@dimen/dialpad_digits_height</dimen>
+ <dimen name="incall_dialpad_key_numbers_size">@dimen/dialpad_key_numbers_default_size</dimen>
+
+</resources>
diff --git a/java/com/android/incallui/res/values-w500dp-land/colors.xml b/java/com/android/incallui/res/values-w500dp-land/colors.xml
new file mode 100644
index 000000000..4b0e33ea7
--- /dev/null
+++ b/java/com/android/incallui/res/values-w500dp-land/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <!-- Background color for status bar. For portrait this will be ignored. -->
+ <color name="statusbar_background_color">#000000</color>
+</resources>
diff --git a/java/com/android/incallui/res/values-w500dp-land/dimens.xml b/java/com/android/incallui/res/values-w500dp-land/dimens.xml
new file mode 100644
index 000000000..81090fc80
--- /dev/null
+++ b/java/com/android/incallui/res/values-w500dp-land/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+
+ <!-- Whether or not the landscape mode layout is currently being used -->
+ <bool name="is_layout_landscape">true</bool>
+
+</resources>
diff --git a/java/com/android/incallui/res/values/animation_constants.xml b/java/com/android/incallui/res/values/animation_constants.xml
new file mode 100644
index 000000000..ac50db21c
--- /dev/null
+++ b/java/com/android/incallui/res/values/animation_constants.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 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
+ -->
+<resources>
+ <integer name="reveal_animation_duration">333</integer>
+</resources>
diff --git a/java/com/android/incallui/res/values/colors.xml b/java/com/android/incallui/res/values/colors.xml
new file mode 100644
index 000000000..0c73cdb10
--- /dev/null
+++ b/java/com/android/incallui/res/values/colors.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2013 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
+ -->
+
+<resources>
+
+ <color name="incall_action_bar_background_color">@color/dialer_theme_color</color>
+ <color name="incall_action_bar_text_color">#ffffff</color>
+
+ <!-- Put on top of each photo, implying 80% darker than usual. -->
+ <color name="on_hold_dim_effect">#cc000000</color>
+
+ <color name="conference_call_manager_caller_name_text_color">#4d4d4d</color>
+ <color name="conference_call_manager_icon_color">#999999</color>
+ <!-- Used with some smaller texts in manage conference screen. -->
+ <color name="conference_call_manager_secondary_text_color">#999999</color>
+
+ <color name="incall_dialpad_background">#ffffff</color>
+ <color name="incall_dialpad_background_pressed">#ccaaaaaa</color>
+ <color name="incall_window_scrim">#b2000000</color>
+
+ <!-- Background color for status bar. For portrait this will be ignored. -->
+ <color name="statusbar_background_color">@color/dialer_theme_color</color>
+
+ <color name="translucent_shadow">#33999999</color>
+
+ <!-- 20% opacity, theme color. -->
+ <color name="incall_dialpad_touch_tint">@color/dialer_theme_color_20pct</color>
+
+ <!-- Background colors for InCallUI. This is a set of colors which pass WCAG
+ AA and all have a contrast ratio over 5:1.
+
+ These colors are also used by InCallUIMaterialColorMapUtils to generate
+ primary activity colors.
+
+ -->
+ <array name="background_colors">
+ <item>#00796B</item>
+ <item>#3367D6</item>
+ <item>#303F9F</item>
+ <item>#7B1FA2</item>
+ <item>#C2185B</item>
+ <item>#C53929</item>
+ <item>#A52714</item>
+ </array>
+
+ <!-- Darker versions of background_colors, two shades darker. These colors are used for the
+ status bar. -->
+ <array name="background_colors_dark">
+ <item>#00695C</item>
+ <item>#2A56C6</item>
+ <item>#283593</item>
+ <item>#6A1B9A</item>
+ <item>#AD1457</item>
+ <item>#B93221</item>
+ <item>#841F10</item>
+ </array>
+
+ <!-- Background color for spam. This color must match one of background_colors above. -->
+ <color name="incall_call_spam_background_color">@color/blocked_contact_background</color>
+
+ <!-- Ripple color used over light backgrounds. -->
+ <color name="ripple_light">#40000000</color>
+
+ <!-- Background color for large notification icon in after call from unknown numbers -->
+ <color name="unknown_number_color">#F4B400</color>
+
+ <color name="incall_background_gradient_top">#E91141BB</color>
+ <color name="incall_background_gradient_middle">#E91141BB</color>
+ <color name="incall_background_gradient_bottom">#CC229FEB</color>
+
+ <color name="incall_background_multiwindow">#E91141BB</color>
+
+ <color name="incall_background_gradient_spam_top">#E5A30B0B</color>
+ <color name="incall_background_gradient_spam_middle">#D6C01111</color>
+ <color name="incall_background_gradient_spam_bottom">#B8E55135</color>
+
+ <color name="incall_background_multiwindow_spam">#E9C22E2E</color>
+</resources>
diff --git a/java/com/android/incallui/res/values/config.xml b/java/com/android/incallui/res/values/config.xml
new file mode 100644
index 000000000..0f3c983b7
--- /dev/null
+++ b/java/com/android/incallui/res/values/config.xml
@@ -0,0 +1,23 @@
+<!--
+ ~ 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
+ -->
+<resources>
+ <!-- Determines video calls will automatically enter fullscreen mode after the start of the
+ call. -->
+ <bool name="video_call_auto_fullscreen">true</bool>
+ <!-- The number of milliseconds after which a video call will automatically enter fullscreen
+ mode (requires video_call_auto_fullscreen to be true). -->
+ <integer name="video_call_auto_fullscreen_timeout">5000</integer>
+</resources>
diff --git a/java/com/android/incallui/res/values/dimens.xml b/java/com/android/incallui/res/values/dimens.xml
new file mode 100644
index 000000000..18816f645
--- /dev/null
+++ b/java/com/android/incallui/res/values/dimens.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2013 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
+ -->
+
+<resources>
+ <dimen name="incall_action_bar_elevation">3dp</dimen>
+
+ <!-- Margin between the bottom of the "call card" photo
+ and the top of the in-call button cluster. -->
+ <dimen name="in_call_touch_ui_upper_margin">2dp</dimen>
+
+ <!-- Padding at the top and bottom edges of the "provider information" -->
+ <dimen name="provider_info_top_bottom_padding">8dp</dimen>
+
+ <!-- Right padding for name and number fields in the call banner.
+ This padding is used to ensure that ultra-long names or
+ numbers won't overlap the elapsed time indication. -->
+ <dimen name="call_banner_name_number_right_padding">50sp</dimen>
+
+ <!-- The InCallUI dialpad will sometimes want digits sizes that are different
+ from dialer. Note, these are the default sizes for small devices. Larger
+ screen sizes apply the values in values-sw360dp/dimens.xml. -->
+ <dimen name="incall_dialpad_key_number_margin_bottom">1dp</dimen>
+ <!-- Zero key should have less space between self and text because "+" is smaller -->
+ <dimen name="incall_dialpad_zero_key_number_margin_bottom">0dp</dimen>
+ <dimen name="incall_dialpad_digits_adjustable_text_size">20sp</dimen>
+ <dimen name="incall_dialpad_digits_adjustable_height">50dp</dimen>
+ <dimen name="incall_dialpad_key_numbers_size">36sp</dimen>
+
+ <!-- Dimensions for OTA Call Card -->
+ <dimen name="otaactivate_layout_marginTop">10dp</dimen>
+ <dimen name="otalistenprogress_layout_marginTop">5dp</dimen>
+ <dimen name="otasuccessfail_layout_marginTop">10dp</dimen>
+
+ <!-- Dimension used to possibly down-scale high-res photo into what is suitable
+ for notification's large icon. -->
+ <dimen name="notification_icon_size">64dp</dimen>
+
+ <!-- Height of translucent shadow effect -->
+ <dimen name="translucent_shadow_height">2dp</dimen>
+
+ <!-- The smaller dimension of the video preview. When in portrait orientation this is the
+ width of the preview. When in landscape, this is the height. -->
+ <dimen name="video_preview_small_dimension">90dp</dimen>
+
+ <dimen name="conference_call_manager_button_dimension">48dp</dimen>
+
+ <!-- Whether or not the landscape mode layout is currently being used -->
+ <bool name="is_layout_landscape">false</bool>
+
+ <dimen name="video_call_lte_to_wifi_failed_do_not_show_text_size">16sp</dimen>
+
+</resources>
diff --git a/java/com/android/incallui/res/values/strings.xml b/java/com/android/incallui/res/values/strings.xml
new file mode 100644
index 000000000..252d131de
--- /dev/null
+++ b/java/com/android/incallui/res/values/strings.xml
@@ -0,0 +1,367 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2013 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
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Official label of the phone app, as seen in "Manage Applications"
+ and other settings UIs. -->
+ <string name="phoneAppLabel" product="default">Phone</string>
+
+ <!-- Official label for the in-call UI. DO NOT TRANSLATE. -->
+ <string name="inCallLabel" translate="false">InCallUI</string>
+
+ <!-- In-call screen: status label for a conference call -->
+ <string name="confCall">Conference call</string>
+ <!-- In-call screen: call lost dialog text -->
+ <string name="call_lost">Call dropped</string>
+
+ <!-- MMI dialog strings -->
+ <!-- Dialog label when an MMI code starts running -->
+
+ <!-- post dial -->
+ <!-- In-call screen: body text of the dialog that appears when we encounter
+ the "wait" character in a phone number to be dialed; this dialog asks the
+ user if it's OK to send the numbers following the "wait". -->
+ <string name="wait_prompt_str">Send the following tones?\n</string>
+ <!-- In-call screen: body text of the dialog that appears when we encounter
+ the "PAUSE" character in a phone number to be dialed; this dialog gives
+ informative message to the user to show the sending numbers following the "Pause". -->
+ <string name="pause_prompt_str">Sending tones\n</string>
+ <!-- In-call screen: button label on the "wait" prompt dialog -->
+ <string name="send_button">Send</string>
+ <!-- In-call screen: button label on the "wait" prompt dialog in CDMA Mode-->
+ <string name="pause_prompt_yes">Yes</string>
+ <!-- In-call screen: button label on the "wait" prompt dialog in CDMA Mode-->
+ <string name="pause_prompt_no">No</string>
+ <!-- In-call screen: on the "wild" character dialog, this is the label
+ for a text widget that lets the user enter the digits that should
+ replace the "wild" character. -->
+ <string name="wild_prompt_str">Replace wild character with</string>
+
+ <!-- In-call screen: status label for a conference call -->
+ <string name="caller_manage_header">Conference call <xliff:g id="conf_call_time">%s</xliff:g></string>
+
+ <!-- Used in FakePhoneActivity test code. DO NOT TRANSLATE. -->
+ <string name="fake_phone_activity_phoneNumber_text" translatable="false">(650) 555-1234</string>
+ <!-- Used in FakePhoneActivity test code. DO NOT TRANSLATE. -->
+ <string name="fake_phone_activity_infoText_text" translatable="false">Incoming phone number</string>
+ <!-- Used in FakePhoneActivity test code. DO NOT TRANSLATE. -->
+ <string name="fake_phone_activity_placeCall_text" translatable="false">Fake Incoming Call</string>
+
+ <!-- Call settings screen, Set voicemail dialog title -->
+ <string name="voicemail_settings_number_label">Voicemail number</string>
+
+ <!-- Notification strings -->
+ <!-- The "label" of the in-call Notification for a dialing call, used
+ as the format string for a Chronometer widget. [CHAR LIMIT=60] -->
+ <string name="notification_dialing">Dialing</string>
+ <!-- Missed call notification message used for a single missed call, including
+ the caller-id info from the missed call -->
+ <string name="notification_missedCallTicker">Missed call from <xliff:g id="missed_call_from">%s</xliff:g></string>
+ <!-- The "label" of the in-call Notification for an ongoing call. [CHAR LIMIT=60] -->
+ <string name="notification_ongoing_call">Ongoing call</string>
+ <!-- The "label" of the in-call Notification for an ongoing work call. [CHAR LIMIT=60] -->
+ <string name="notification_ongoing_work_call">Ongoing work call</string>
+ <!-- The "label" of the in-call Notification for an ongoing call, which is being made over
+ Wi-Fi. [CHAR LIMIT=60] -->
+ <string name="notification_ongoing_call_wifi">Ongoing Wi-Fi call</string>
+ <!-- The "label" of the in-call Notification for an ongoing work call, which is being made
+ over Wi-Fi. [CHAR LIMIT=60] -->
+ <string name="notification_ongoing_work_call_wifi">Ongoing Wi-Fi work call</string>
+ <!-- The "label" of the in-call Notification for a call that's on hold -->
+ <string name="notification_on_hold">On hold</string>
+ <!-- The "label" of the in-call Notification for an incoming ringing call. [CHAR LIMIT=60] -->
+ <string name="notification_incoming_call">Incoming call</string>
+ <!-- The "label" of the in-call Notification for an incoming ringing call. [CHAR LIMIT=60] -->
+ <string name="notification_incoming_work_call">Incoming work call</string>
+ <!-- The "label" of the in-call Notification for an incoming ringing call,
+ which is being made over Wi-Fi. [CHAR LIMIT=60] -->
+ <string name="notification_incoming_call_wifi">Incoming Wi-Fi call</string>
+ <!-- The "label" of the in-call Notification for an incoming ringing work call,
+ which is being made over Wi-Fi. [CHAR LIMIT=60] -->
+ <string name="notification_incoming_work_call_wifi">Incoming Wi-Fi work call</string>
+ <!-- The "label" of the in-call Notification for an incoming ringing spam call. -->
+ <string name="notification_incoming_spam_call">Incoming suspected spam call</string>
+ <!-- The "label" of the in-call Notification for upgrading an existing call to a video call. -->
+ <string name="notification_requesting_video_call">Incoming video request</string>
+ <!-- Label for the "Voicemail" notification item, when expanded. -->
+ <string name="notification_voicemail_title">New voicemail</string>
+ <!-- Label for the expanded "Voicemail" notification item,
+ including a count of messages. -->
+ <string name="notification_voicemail_title_count">New voicemail (<xliff:g id="count">%d</xliff:g>)</string>
+ <!-- Message displayed in the "Voicemail" notification item, allowing the user
+ to dial the indicated number. -->
+ <string name="notification_voicemail_text_format">Dial <xliff:g id="voicemail_number">%s</xliff:g></string>
+ <!-- Message displayed in the "Voicemail" notification item,
+ indicating that there's no voicemail number available -->
+ <string name="notification_voicemail_no_vm_number">Voicemail number unknown</string>
+ <!-- Label for the "No service" notification item, when expanded. -->
+ <string name="notification_network_selection_title">No service</string>
+ <!-- Label for the expanded "No service" notification item, including the
+ operator name set by user -->
+ <string name="notification_network_selection_text">Selected network (<xliff:g id="operator_name">%s</xliff:g>) unavailable</string>
+ <!-- Label for the "Answer call" action. This is the displayed label for the action that answers
+ an incoming call. [CHAR LIMIT=12] -->
+ <string name="notification_action_answer">Answer</string>
+ <!-- Label for "end call" Action.
+ It is displayed in the "Ongoing call" notification, which is shown
+ when the user is outside the in-call screen while the phone call is still
+ active. [CHAR LIMIT=12] -->
+ <string name="notification_action_end_call">Hang up</string>
+ <!-- Label for "Video Call" notification action. This is a displayed on the notification for an
+ incoming video call, and answers the call as a video call. [CHAR LIMIT=12] -->
+ <string name="notification_action_answer_video">Video</string>
+ <!-- Label for "Voice" notification action. This is a displayed on the notification for an
+ incoming video call, and answers the call as an audio call. [CHAR LIMIT=12] -->
+ <string name="notification_action_answer_voice">Voice</string>
+ <!-- Label for "Accept" notification action. This is somewhat generic, and may refer to
+ scenarios such as accepting an incoming call or accepting a video call request.
+ [CHAR LIMIT=12] -->
+ <string name="notification_action_accept">Accept</string>
+ <!-- Label for "Dismiss" notification action. This is somewhat generic, and may refer to
+ scenarios such as declining an incoming call or declining a video call request.
+ [CHAR LIMIT=12] -->
+ <string name="notification_action_dismiss">Decline</string>
+
+ <!-- The "label" of the in-call Notification for an ongoing external call.
+ External calls are a representation of a call which is in progress on the user's other
+ device (e.g. another phone or a watch).
+ [CHAR LIMIT=60] -->
+ <string name="notification_external_call">Ongoing call on another device</string>
+ <!-- The "label" of the in-call Notification for an ongoing external video call.
+ External calls are a representation of a call which is in progress on the user's other
+ device (e.g. another phone or a watch).
+ [CHAR LIMIT=60] -->
+ <string name="notification_external_video_call">Ongoing video call on another device</string>
+ <!-- Notification action displayed for external call notifications. External calls are a
+ representation of a call which is in progress on the user's other device (e.g. another
+ phone or a watch). The "take call" action initiates the process of pulling an external
+ call to the current device.
+ [CHAR LIMIT=30] -->
+ <string name="notification_take_call">Take Call</string>
+ <!-- Notification action displayed for external call notifications. External calls are a
+ representation of a call which is in progress on the user's other device (e.g. another
+ phone or a watch). The "take video call" action initiates the process of pulling an external
+ video call to the current device.
+ [CHAR LIMIT=30] -->
+ <string name="notification_take_video_call">Take Video Call</string>
+ <!-- In-call screen: call failure message displayed in an error dialog -->
+ <string name="incall_error_power_off">To place a call, first turn off Airplane mode.</string>
+ <!-- In-call screen: call failure message displayed in an error dialog.
+ This string is currently unused (see comments in InCallActivity.java.) -->
+ <string name="incall_error_emergency_only">Not registered on network.</string>
+ <!-- In-call screen: call failure message displayed in an error dialog -->
+ <string name="incall_error_out_of_service">Cellular network not available.</string>
+ <!-- In-call screen: call failure message displayed in an error dialog -->
+ <string name="incall_error_no_phone_number_supplied">To place a call, enter a valid number.</string>
+ <!-- In-call screen: call failure message displayed in an error dialog -->
+ <string name="incall_error_call_failed">Can\'t call.</string>
+ <!-- In-call screen: status message displayed in a dialog when starting an MMI -->
+ <string name="incall_status_dialed_mmi">Starting MMI sequence\u2026</string>
+ <!-- In-call screen: message displayed in an error dialog -->
+ <string name="incall_error_supp_service_unknown">Service not supported.</string>
+ <!-- In-call screen: message displayed in an error dialog -->
+ <string name="incall_error_supp_service_switch">Can\'t switch calls.</string>
+ <!-- In-call screen: message displayed in an error dialog -->
+ <string name="incall_error_supp_service_separate">Can\'t separate call.</string>
+ <!-- In-call screen: message displayed in an error dialog -->
+ <string name="incall_error_supp_service_transfer">Can\'t transfer.</string>
+ <!-- In-call screen: message displayed in an error dialog -->
+ <string name="incall_error_supp_service_conference">Can\'t conference.</string>
+ <!-- In-call screen: message displayed in an error dialog -->
+ <string name="incall_error_supp_service_reject">Can\'t reject call.</string>
+ <!-- In-call screen: message displayed in an error dialog -->
+ <string name="incall_error_supp_service_hangup">Can\'t release call(s).</string>
+
+ <!-- Dialog title for the "radio enable" UI for emergency calls -->
+ <string name="emergency_enable_radio_dialog_title">Emergency call</string>
+ <!-- Status message for the "radio enable" UI for emergency calls -->
+ <string name="emergency_enable_radio_dialog_message">Turning on radio\u2026</string>
+ <!-- Status message for the "radio enable" UI for emergency calls -->
+ <string name="emergency_enable_radio_dialog_retry">No service. Trying again\u2026</string>
+
+ <!-- Dialer text on Emergency Dialer -->
+ <!-- Emergency dialer: message displayed in an error dialog -->
+ <string name="dial_emergency_error">Can\'t call. <xliff:g id="non_emergency_number">%s</xliff:g> is not an emergency number.</string>
+ <!-- Emergency dialer: message displayed in an error dialog -->
+ <string name="dial_emergency_empty_error">Can\'t call. Dial an emergency number.</string>
+
+ <!-- Displayed in the text entry box in the dialer when in landscape mode to guide the user
+ to dial using the physical keyboard -->
+ <string name="dialerKeyboardHintText">Use keyboard to dial</string>
+
+ <!-- Message indicating that Video Started flowing for IMS-VT calls -->
+ <string name="player_started">Player Started</string>
+ <!-- Message indicating that Video Stopped flowing for IMS-VT calls -->
+ <string name="player_stopped">Player Stopped</string>
+ <!-- Message indicating that camera failure has occurred for the selected camera and
+ as result camera is not ready -->
+ <string name="camera_not_ready">Camera not ready</string>
+ <!-- Message indicating that camera is ready/available -->
+ <string name="camera_ready">Camera ready</string>
+ <!-- Message indicating unknown call session event -->
+ <string name="unknown_call_session_event">"Unkown call session event"</string>
+
+ <!-- For incoming calls, this is a string we can get from a CDMA network instead of
+ the actual phone number, to indicate there's no number present. DO NOT TRANSLATE. -->
+ <string-array name="absent_num" translatable="false">
+ <item>ABSENT NUMBER</item>
+ <item>ABSENTNUMBER</item>
+ </string-array>
+
+ <!-- Preference for Voicemail service provider under "Voicemail" settings.
+ [CHAR LIMIT=40] -->
+ <string name="voicemail_provider">Service</string>
+
+ <!-- Preference for Voicemail setting of each provider.
+ [CHAR LIMIT=40] -->
+ <string name="voicemail_settings">Setup</string>
+
+ <!-- String to display in voicemail number summary when no voicemail num is set -->
+ <string name="voicemail_number_not_set">&lt;Not set&gt;</string>
+
+ <!-- Title displayed above settings coming after voicemail in the call features screen -->
+ <string name="other_settings">Other call settings</string>
+
+ <!-- Use this to describe the separate conference call button; currently for screen readers through accessibility. -->
+ <string name="goPrivate">go private</string>
+ <!-- Use this to describe the select contact button in EditPhoneNumberPreference; currently for screen readers through accessibility. -->
+ <string name="selectContact">select contact</string>
+
+ <!-- Dialog title for the vibration settings for voicemail notifications [CHAR LIMIT=40] -->
+ <string msgid="8731372580674292759" name="voicemail_notification_vibrate_when_title">Vibrate</string>
+ <!-- Dialog title for the vibration settings for voice mail notifications [CHAR LIMIT=40]-->
+ <string msgid="8995274609647451109" name="voicemail_notification_vibarte_when_dialog_title">Vibrate</string>
+
+ <!-- Voicemail ringtone title. The user clicks on this preference to select
+ which sound to play when a voicemail notification is received.
+ [CHAR LIMIT=30] -->
+ <string name="voicemail_notification_ringtone_title">Sound</string>
+
+ <!-- The default value value for voicemail notification. -->
+ <string name="voicemail_notification_vibrate_when_default" translatable="false">never</string>
+
+ <!-- Actual values used in our code for voicemail notifications. DO NOT TRANSLATE -->
+ <string-array name="voicemail_notification_vibrate_when_values" translatable="false">
+ <item>always</item>
+ <item>silent</item>
+ <item>never</item>
+ </string-array>
+
+ <!-- Title for the category "ringtone", which is shown above ringtone and vibration
+ related settings.
+ [CHAR LIMIT=30] -->
+ <string name="preference_category_ringtone">Ringtone &amp; Vibrate</string>
+
+ <!-- Label for "Manage conference call" panel [CHAR LIMIT=40] -->
+ <string name="manageConferenceLabel">Manage conference call</string>
+
+ <!-- This can be used in any application wanting to disable the text "Emergency number" -->
+ <string name="emergency_call_dialog_number_for_display">Emergency number</string>
+
+ <!-- Used to inform the user that a call was received via a number other than the primary
+ phone number associated with their device. [CHAR LIMIT=16] -->
+ <string name="child_number">via <xliff:g example="650-555-1212" id="child_number">%s</xliff:g></string>
+
+ <!-- Title for the call context with a person-type contact. [CHAR LIMIT=40] -->
+ <string name="person_contact_context_title">Recent messages</string>
+
+ <!-- Title for the call context with a business-type contact. [CHAR LIMIT=40] -->
+ <string name="business_contact_context_title">Business info</string>
+
+ <!-- Distance strings for business caller ID context. -->
+
+ <!-- Used to inform the user how far away a location is in miles. [CHAR LIMIT=NONE] -->
+ <string name="distance_imperial_away"><xliff:g id="distance">%.1f</xliff:g> mi away</string>
+ <!-- Used to inform the user how far away a location is in kilometers. [CHAR LIMIT=NONE] -->
+ <string name="distance_metric_away"><xliff:g id="distance">%.1f</xliff:g> km away</string>
+ <!-- A shortened way to display a business address. Formatted [street address], [city/locality]. -->
+ <string name="display_address"><xliff:g id="street_address">%1$s</xliff:g>, <xliff:g id="locality">%2$s</xliff:g></string>
+ <!-- Used to indicate hours of operation for a location as a time span. e.g. "11 am - 9 pm" [CHAR LIMIT=NONE] -->
+ <string name="open_time_span"><xliff:g id="open_time">%1$s</xliff:g> - <xliff:g id="close_time">%2$s</xliff:g></string>
+ <!-- Used to indicate a series of opening hours for a location.
+ This first argument may be one or more time spans. e.g. "11 am - 9 pm, 9 pm - 11 pm"
+ The second argument is an additional time span. e.g. "11 pm - 1 am"
+ The string is used to build a list of opening hours.
+ [CHAR LIMIT=NONE] -->
+ <string name="opening_hours"><xliff:g id="earlier_times">%1$s</xliff:g>, <xliff:g id="later_time">%2$s</xliff:g></string>
+ <!-- Used to express when a location will open the next day. [CHAR LIMIT=NONE] -->
+ <string name="opens_tomorrow_at">Opens tomorrow at <xliff:g id="open_time">%s</xliff:g></string>
+ <!-- Used to express the next time at which a location will be open today. [CHAR LIMIT=NONE] -->
+ <string name="opens_today_at">Opens today at <xliff:g id="open_time">%s</xliff:g></string>
+ <!-- Used to express the next time at which a location will close today. [CHAR LIMIT=NONE] -->
+ <string name="closes_today_at">Closes at <xliff:g id="close_time">%s</xliff:g></string>
+ <!-- Used to express the next time at which a location closed today if it is already closed. [CHAR LIMIT=NONE] -->
+ <string name="closed_today_at">Closed today at <xliff:g id="close_time">%s</xliff:g></string>
+ <!-- Displayed when a place is open. -->
+ <string name="open_now">Open now</string>
+ <!-- Displayed when a place is closed. -->
+ <string name="closed_now">Closed now</string>
+
+ <!-- Title for the notification to the user after a call from an unknown number ends. [CHAR LIMIT=100] -->
+ <string name="non_spam_notification_title">Know <xliff:g id="number">%1$s</xliff:g>?</string>
+ <!-- Title for the notification to the user after a call from an spammer ends. [CHAR LIMIT=100] -->
+ <string name="spam_notification_title">Is <xliff:g id="number">%1$s</xliff:g> spam?</string>
+ <!-- Text for the toast shown after the user presses block/report spam. [CHAR LIMIT=100] -->
+ <string name="spam_notification_block_report_toast_text"><xliff:g id="number">%1$s</xliff:g> blocked and call was reported as spam.</string>
+ <!-- Text for the toast shown after the user presses not spam. [CHAR LIMIT=100] -->
+ <string name="spam_notification_not_spam_toast_text">Call from <xliff:g id="number">%1$s</xliff:g> reported as not spam.</string>
+ <!-- Text displayed in the collapsed notification to the user after a non-spam call ends. [CHAR LIMIT=100] -->
+ <string name="spam_notification_non_spam_call_collapsed_text">Tap to add to contacts or block spam number.</string>
+ <!-- Text displayed in the expanded notification to the user after a non-spam call ends. [CHAR LIMIT=NONE] -->
+ <string name="spam_notification_non_spam_call_expanded_text">This is the first time this number called you. If this call was spam, you can block this number and report it.</string>
+ <!-- Text displayed in the collapsed notification to the user after a spam call ends. [CHAR LIMIT=100] -->
+ <string name="spam_notification_spam_call_collapsed_text">Tap to report as NOT SPAM, or block it.</string>
+ <!-- Text displayed in the expanded notification to the user after a spam call ends. [CHAR LIMIT=NONE] -->
+ <string name="spam_notification_spam_call_expanded_text">We suspected this to be a spammer. If this call wasn\'t spam, tap "NOT SPAM" to report our mistake.</string>
+ <!-- Text for the reporting spam action in the after call prompt. [CHAR LIMIT=20] -->
+ <string name="spam_notification_report_spam_action_text">Block &amp; report</string>
+ <!-- Text for the adding to contacts action in the after call prompt. [CHAR LIMIT=20] -->
+ <string name="spam_notification_add_contact_action_text">Add contact</string>
+ <!-- Text for the reporting as not spam action in the after call prompt. [CHAR LIMIT=20] -->
+ <string name="spam_notification_not_spam_action_text">Not spam</string>
+ <!-- Text for the blocking spam action in the after call prompt. [CHAR LIMIT=20] -->
+ <string name="spam_notification_block_spam_action_text">Block number</string>
+ <!-- Text for the adding to contacts action in the after call dialog. [CHAR LIMIT=40] -->
+ <string name="spam_notification_dialog_add_contact_action_text">Add to contacts</string>
+ <!-- Text for the blocking and reporting spam action in the after call dialog. [CHAR LIMIT=40] -->
+ <string name="spam_notification_dialog_block_report_spam_action_text">Block &amp; report spam</string>
+ <!-- Text for the marking a call as not spam in the after call dialog. [CHAR LIMIT=40] -->
+ <string name="spam_notification_dialog_was_not_spam_action_text">Not spam</string>
+
+ <string name="callFailed_simError">No SIM or SIM error</string>
+
+ <string name="conference_caller_disconnect_content_description">End call</string>
+
+ <!-- Name for a conference call. Shown in the in call UI and in notifications. -->
+ <string name="conference_call_name">Conference call</string>
+
+ <!-- Name for a generic conference call. Shown in the in call UI. This is used in CDMA where we
+ don't know the precise state of participants in the conference. -->
+ <string name="generic_conference_call_name">In call</string>
+
+ <!-- Displayed when handover from WiFi to Lte occurs during a video call -->
+ <string name="video_call_wifi_to_lte_handover_toast">Continuing call using cellular data…</string>
+
+ <!-- Displayed when WiFi handover from LTE fails during a video call. -->
+ <string name="video_call_lte_to_wifi_failed_title">Couldn\'t switch to Wi-Fi network</string>
+ <string name="video_call_lte_to_wifi_failed_message">Video call will remain on cellular network. Standard
+ data charges may apply.
+ </string>
+ <string name="video_call_lte_to_wifi_failed_do_not_show">Do not show this again</string>
+
+</resources>
diff --git a/java/com/android/incallui/res/values/styles.xml b/java/com/android/incallui/res/values/styles.xml
new file mode 100644
index 000000000..96e3d4d59
--- /dev/null
+++ b/java/com/android/incallui/res/values/styles.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2013 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
+ -->
+
+<resources>
+ <drawable name="grayBg">#FF333333</drawable>
+
+ <!-- Theme for the InCallActivity activity. Should have a transparent background for the
+ circular reveal animation for a new outgoing call to work correctly. We don't just use
+ Theme.Black.NoTitleBar directly, since we want any popups or dialogs from the
+ InCallActivity to have the correct Material style. -->
+ <style name="Theme.InCallScreen" parent="@style/Theme.AppCompat.NoActionBar">
+ <item name="android:textColorPrimary">#ffffff</item>
+ <item name="android:textColorSecondary">#DDFFFFFF</item>
+ <item name="android:colorPrimary">@color/dialer_theme_color</item>
+ <item name="android:colorPrimaryDark">@color/dialer_theme_color_dark</item>
+
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ <item name="android:navigationBarColor">@android:color/transparent</item>
+ <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+
+ <item name="dialpad_key_button_touch_tint">@color/incall_dialpad_touch_tint</item>
+ <item name="dialpad_style">@style/InCallDialpad</item>
+ <item name="android:windowAnimationStyle">@null</item>
+ <item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
+
+ <item name="android:windowBackground">@drawable/incall_background_gradient</item>
+ <item name="android:windowShowWallpaper">true</item>
+ </style>
+
+ <style name="Theme.InCallScreen.ManageConference" parent="DialerThemeBase">
+ </style>
+
+ <style name="InCallDialpad" parent="Dialpad.Light">
+ <item name="dialpad_key_number_margin_bottom">
+ @dimen/incall_dialpad_key_number_margin_bottom
+ </item>
+ <item name="dialpad_zero_key_number_margin_bottom">
+ @dimen/incall_dialpad_zero_key_number_margin_bottom
+ </item>
+ <item name="dialpad_digits_adjustable_text_size">
+ @dimen/incall_dialpad_digits_adjustable_text_size
+ </item>
+ <item name="dialpad_digits_adjustable_height">
+ @dimen/incall_dialpad_digits_adjustable_height
+ </item>
+ <item name="dialpad_key_numbers_size">
+ @dimen/incall_dialpad_key_numbers_size
+ </item>
+ <item name="dialpad_end_key_spacing">
+ @dimen/incall_end_call_spacing
+ </item>
+ </style>
+
+ <style name="AfterCallNotificationTheme" parent="@style/Theme.AppCompat.Light.Dialog.MinWidth">
+ <!-- This colorAccent is to style checkboxes in the dialogs -->
+ <item name="colorAccent">@color/dialer_theme_color</item>
+ <!-- This is needed to make any alert dialogs in this activity take up minimum space -->
+ <item name="android:alertDialogTheme">@style/AfterCallDialogStyle</item>
+ </style>
+
+ <style name="AfterCallDialogStyle" parent="@style/Theme.AppCompat.Light.Dialog.MinWidth">
+ <!-- This colorAccent is to style text in the dialogs -->
+ <item name="android:colorAccent">@color/dialer_theme_color</item>
+ </style>
+
+</resources>
diff --git a/java/com/android/incallui/ringtone/DialerRingtoneManager.java b/java/com/android/incallui/ringtone/DialerRingtoneManager.java
new file mode 100644
index 000000000..5ebd93378
--- /dev/null
+++ b/java/com/android/incallui/ringtone/DialerRingtoneManager.java
@@ -0,0 +1,134 @@
+/*
+ * 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.incallui.ringtone;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.Settings;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall.State;
+import java.util.Objects;
+
+/**
+ * Class that determines when ringtones should be played and can play the call waiting tone when
+ * necessary.
+ */
+public class DialerRingtoneManager {
+
+ /*
+ * Flag used to determine if the Dialer is responsible for playing ringtones for incoming calls.
+ * Once we're ready to enable Dialer Ringing, these flags should be removed.
+ */
+ private static final boolean IS_DIALER_RINGING_ENABLED = false;
+ private final InCallTonePlayer mInCallTonePlayer;
+ private final CallList mCallList;
+ private Boolean mIsDialerRingingEnabledForTesting;
+
+ /**
+ * Creates the DialerRingtoneManager with the given {@link InCallTonePlayer}.
+ *
+ * @param inCallTonePlayer the tone player used to play in-call tones.
+ * @param callList the CallList used to check for {@link State#CALL_WAITING}
+ * @throws NullPointerException if inCallTonePlayer or callList are null
+ */
+ public DialerRingtoneManager(
+ @NonNull InCallTonePlayer inCallTonePlayer, @NonNull CallList callList) {
+ mInCallTonePlayer = Objects.requireNonNull(inCallTonePlayer);
+ mCallList = Objects.requireNonNull(callList);
+ }
+
+ /**
+ * Determines if a ringtone should be played for the given call state (see {@link State}) and
+ * {@link Uri}.
+ *
+ * @param callState the call state for the call being checked.
+ * @param ringtoneUri the ringtone to potentially play.
+ * @return {@code true} if the ringtone should be played, {@code false} otherwise.
+ */
+ public boolean shouldPlayRingtone(int callState, @Nullable Uri ringtoneUri) {
+ return isDialerRingingEnabled()
+ && translateCallStateForCallWaiting(callState) == State.INCOMING
+ && ringtoneUri != null;
+ }
+
+ /**
+ * Determines if an incoming call should vibrate as well as ring.
+ *
+ * @param resolver {@link ContentResolver} used to look up the {@link
+ * Settings.System#VIBRATE_WHEN_RINGING} setting.
+ * @return {@code true} if the call should vibrate, {@code false} otherwise.
+ */
+ public boolean shouldVibrate(ContentResolver resolver) {
+ return Settings.System.getInt(resolver, Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
+ }
+
+ /**
+ * The incoming callState is never set as {@link State#CALL_WAITING} because {@link
+ * DialerCall#translateState(int)} doesn't account for that case, check for it here
+ */
+ private int translateCallStateForCallWaiting(int callState) {
+ if (callState != State.INCOMING) {
+ return callState;
+ }
+ return mCallList.getActiveCall() == null ? State.INCOMING : State.CALL_WAITING;
+ }
+
+ private boolean isDialerRingingEnabled() {
+ boolean enabledFlag =
+ mIsDialerRingingEnabledForTesting != null
+ ? mIsDialerRingingEnabledForTesting
+ : IS_DIALER_RINGING_ENABLED;
+ return VERSION.SDK_INT >= VERSION_CODES.N && enabledFlag;
+ }
+
+ /**
+ * Determines if a call waiting tone should be played for the the given call state (see {@link
+ * State}).
+ *
+ * @param callState the call state for the call being checked.
+ * @return {@code true} if the call waiting tone should be played, {@code false} otherwise.
+ */
+ public boolean shouldPlayCallWaitingTone(int callState) {
+ return isDialerRingingEnabled()
+ && translateCallStateForCallWaiting(callState) == State.CALL_WAITING
+ && !mInCallTonePlayer.isPlayingTone();
+ }
+
+ /** Plays the call waiting tone. */
+ public void playCallWaitingTone() {
+ if (!isDialerRingingEnabled()) {
+ return;
+ }
+ mInCallTonePlayer.play(InCallTonePlayer.TONE_CALL_WAITING);
+ }
+
+ /** Stops playing the call waiting tone. */
+ public void stopCallWaitingTone() {
+ if (!isDialerRingingEnabled()) {
+ return;
+ }
+ mInCallTonePlayer.stop();
+ }
+
+ void setDialerRingingEnabledForTesting(boolean status) {
+ mIsDialerRingingEnabledForTesting = status;
+ }
+}
diff --git a/java/com/android/incallui/ringtone/InCallTonePlayer.java b/java/com/android/incallui/ringtone/InCallTonePlayer.java
new file mode 100644
index 000000000..c76b41d72
--- /dev/null
+++ b/java/com/android/incallui/ringtone/InCallTonePlayer.java
@@ -0,0 +1,168 @@
+/*
+ * 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.incallui.ringtone;
+
+import android.media.AudioManager;
+import android.media.ToneGenerator;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import com.android.incallui.Log;
+import com.android.incallui.async.PausableExecutor;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Class responsible for playing in-call related tones in a background thread. This class only
+ * allows one tone to be played at a time.
+ */
+public class InCallTonePlayer {
+
+ public static final int TONE_CALL_WAITING = 4;
+
+ public static final int VOLUME_RELATIVE_HIGH_PRIORITY = 80;
+
+ @NonNull private final ToneGeneratorFactory mToneGeneratorFactory;
+ @NonNull private final PausableExecutor mExecutor;
+ private @Nullable CountDownLatch mNumPlayingTones;
+
+ /**
+ * Creates a new InCallTonePlayer.
+ *
+ * @param toneGeneratorFactory the {@link ToneGeneratorFactory} used to create {@link
+ * ToneGenerator}s.
+ * @param executor the {@link PausableExecutor} used to play tones in a background thread.
+ * @throws NullPointerException if audioModeProvider, toneGeneratorFactory, or executor are {@code
+ * null}.
+ */
+ public InCallTonePlayer(
+ @NonNull ToneGeneratorFactory toneGeneratorFactory, @NonNull PausableExecutor executor) {
+ mToneGeneratorFactory = Objects.requireNonNull(toneGeneratorFactory);
+ mExecutor = Objects.requireNonNull(executor);
+ }
+
+ /** @return {@code true} if a tone is currently playing, {@code false} otherwise. */
+ public boolean isPlayingTone() {
+ return mNumPlayingTones != null && mNumPlayingTones.getCount() > 0;
+ }
+
+ /**
+ * Plays the given tone in a background thread.
+ *
+ * @param tone the tone to play.
+ * @throws IllegalStateException if a tone is already playing.
+ * @throws IllegalArgumentException if the tone is invalid.
+ */
+ public void play(int tone) {
+ if (isPlayingTone()) {
+ throw new IllegalStateException("Tone already playing");
+ }
+ final ToneGeneratorInfo info = getToneGeneratorInfo(tone);
+ mNumPlayingTones = new CountDownLatch(1);
+ mExecutor.execute(
+ new Runnable() {
+ @Override
+ public void run() {
+ playOnBackgroundThread(info);
+ }
+ });
+ }
+
+ private ToneGeneratorInfo getToneGeneratorInfo(int tone) {
+ switch (tone) {
+ case TONE_CALL_WAITING:
+ /*
+ * DialerCall waiting tones play until they're stopped either by the user accepting or
+ * declining the call so the tone length is set at what's effectively forever. The
+ * tone is played at a high priority volume and through STREAM_VOICE_CALL since it's
+ * call related and using that stream will route it through bluetooth devices
+ * appropriately.
+ */
+ return new ToneGeneratorInfo(
+ ToneGenerator.TONE_SUP_CALL_WAITING,
+ VOLUME_RELATIVE_HIGH_PRIORITY,
+ Integer.MAX_VALUE,
+ AudioManager.STREAM_VOICE_CALL);
+ default:
+ throw new IllegalArgumentException("Bad tone: " + tone);
+ }
+ }
+
+ private void playOnBackgroundThread(ToneGeneratorInfo info) {
+ ToneGenerator toneGenerator = null;
+ try {
+ Log.v(this, "Starting tone " + info);
+ toneGenerator = mToneGeneratorFactory.newInCallToneGenerator(info.stream, info.volume);
+ toneGenerator.startTone(info.tone);
+ /*
+ * During tests, this will block until the tests call mExecutor.ackMilestone. This call
+ * allows for synchronization to the point where the tone has started playing.
+ */
+ mExecutor.milestone();
+ if (mNumPlayingTones != null) {
+ mNumPlayingTones.await(info.toneLengthMillis, TimeUnit.MILLISECONDS);
+ // Allows for synchronization to the point where the tone has completed playing.
+ mExecutor.milestone();
+ }
+ } catch (InterruptedException e) {
+ Log.w(this, "Interrupted while playing in-call tone.");
+ } finally {
+ if (toneGenerator != null) {
+ toneGenerator.release();
+ }
+ if (mNumPlayingTones != null) {
+ mNumPlayingTones.countDown();
+ }
+ // Allows for synchronization to the point where this background thread has cleaned up.
+ mExecutor.milestone();
+ }
+ }
+
+ /** Stops playback of the current tone. */
+ public void stop() {
+ if (mNumPlayingTones != null) {
+ mNumPlayingTones.countDown();
+ }
+ }
+
+ private static class ToneGeneratorInfo {
+
+ public final int tone;
+ public final int volume;
+ public final int toneLengthMillis;
+ public final int stream;
+
+ public ToneGeneratorInfo(int toneGeneratorType, int volume, int toneLengthMillis, int stream) {
+ this.tone = toneGeneratorType;
+ this.volume = volume;
+ this.toneLengthMillis = toneLengthMillis;
+ this.stream = stream;
+ }
+
+ @Override
+ public String toString() {
+ return "ToneGeneratorInfo{"
+ + "toneLengthMillis="
+ + toneLengthMillis
+ + ", tone="
+ + tone
+ + ", volume="
+ + volume
+ + '}';
+ }
+ }
+}
diff --git a/java/com/android/incallui/ringtone/ToneGeneratorFactory.java b/java/com/android/incallui/ringtone/ToneGeneratorFactory.java
new file mode 100644
index 000000000..cd7b11aa9
--- /dev/null
+++ b/java/com/android/incallui/ringtone/ToneGeneratorFactory.java
@@ -0,0 +1,34 @@
+/*
+ * 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.incallui.ringtone;
+
+import android.media.ToneGenerator;
+
+/** Factory used to create {@link ToneGenerator}s. */
+public class ToneGeneratorFactory {
+
+ /**
+ * Creates a new {@link ToneGenerator} to use while in a call.
+ *
+ * @param stream the stream through which to play tones.
+ * @param volume the volume at which to play tones.
+ * @return a new ToneGenerator.
+ */
+ public ToneGenerator newInCallToneGenerator(int stream, int volume) {
+ return new ToneGenerator(stream, volume);
+ }
+}
diff --git a/java/com/android/incallui/sessiondata/AndroidManifest.xml b/java/com/android/incallui/sessiondata/AndroidManifest.xml
new file mode 100644
index 000000000..11babd94d
--- /dev/null
+++ b/java/com/android/incallui/sessiondata/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<!--
+ ~ 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
+ -->
+<manifest
+ package="com.android.incallui.sessiondata">
+</manifest>
diff --git a/java/com/android/incallui/sessiondata/AvatarPresenter.java b/java/com/android/incallui/sessiondata/AvatarPresenter.java
new file mode 100644
index 000000000..e7303b90a
--- /dev/null
+++ b/java/com/android/incallui/sessiondata/AvatarPresenter.java
@@ -0,0 +1,31 @@
+/*
+ * 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.incallui.sessiondata;
+
+import android.support.annotation.Nullable;
+import android.widget.ImageView;
+
+/** Interface for interacting with Fragments that can be put in the data container */
+public interface AvatarPresenter {
+
+ @Nullable
+ ImageView getAvatarImageView();
+
+ int getAvatarSize();
+
+ boolean shouldShowAnonymousAvatar();
+}
diff --git a/java/com/android/incallui/sessiondata/MultimediaFragment.java b/java/com/android/incallui/sessiondata/MultimediaFragment.java
new file mode 100644
index 000000000..d6f671d58
--- /dev/null
+++ b/java/com/android/incallui/sessiondata/MultimediaFragment.java
@@ -0,0 +1,231 @@
+/*
+ * 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.incallui.sessiondata;
+
+import android.graphics.drawable.Drawable;
+import android.location.Location;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.app.Fragment;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.FragmentUtils;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.multimedia.MultimediaData;
+import com.android.incallui.maps.StaticMapBinding;
+import com.android.incallui.maps.StaticMapFactory;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.DataSource;
+import com.bumptech.glide.load.engine.GlideException;
+import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
+import com.bumptech.glide.request.RequestListener;
+import com.bumptech.glide.request.target.Target;
+
+/**
+ * Displays info from {@link MultimediaData MultimediaData}.
+ *
+ * <p>Currently displays image, location (as a map), and message that come bundled with
+ * MultimediaData when calling {@link #newInstance(MultimediaData, boolean, boolean)}.
+ */
+public class MultimediaFragment extends Fragment implements AvatarPresenter {
+
+ private static final String ARG_SUBJECT = "subject";
+ private static final String ARG_IMAGE = "image";
+ private static final String ARG_LOCATION = "location";
+ private static final String ARG_INTERACTIVE = "interactive";
+ private static final String ARG_SHOW_AVATAR = "show_avatar";
+ private ImageView avatarImageView;
+ // TODO: add click listeners
+ @SuppressWarnings("unused")
+ private boolean isInteractive;
+
+ private boolean showAvatar;
+ private StaticMapFactory mapFactory;
+
+ public static MultimediaFragment newInstance(
+ @NonNull MultimediaData multimediaData, boolean isInteractive, boolean showAvatar) {
+ return newInstance(
+ multimediaData.getSubject(),
+ multimediaData.getImageUri(),
+ multimediaData.getLocation(),
+ isInteractive,
+ showAvatar);
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ public static MultimediaFragment newInstance(
+ @Nullable String subject,
+ @Nullable Uri imageUri,
+ @Nullable Location location,
+ boolean isInteractive,
+ boolean showAvatar) {
+ Bundle args = new Bundle();
+ args.putString(ARG_SUBJECT, subject);
+ args.putParcelable(ARG_IMAGE, imageUri);
+ args.putParcelable(ARG_LOCATION, location);
+ args.putBoolean(ARG_INTERACTIVE, isInteractive);
+ args.putBoolean(ARG_SHOW_AVATAR, showAvatar);
+ MultimediaFragment fragment = new MultimediaFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle bundle) {
+ super.onCreate(bundle);
+ isInteractive = getArguments().getBoolean(ARG_INTERACTIVE);
+ showAvatar = getArguments().getBoolean(ARG_SHOW_AVATAR);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ boolean hasImage = getImageUri() != null;
+ boolean hasSubject = !TextUtils.isEmpty(getSubject());
+ boolean hasMap = getLocation() != null;
+ if (hasMap) {
+ mapFactory = StaticMapBinding.get(getActivity().getApplication());
+ }
+ if (mapFactory != null) {
+ if (hasImage) {
+ if (hasSubject) {
+ return layoutInflater.inflate(
+ R.layout.fragment_composer_text_image_frag, viewGroup, false);
+ } else {
+ return layoutInflater.inflate(R.layout.fragment_composer_image_frag, viewGroup, false);
+ }
+ } else if (hasSubject) {
+ return layoutInflater.inflate(R.layout.fragment_composer_text_frag, viewGroup, false);
+ } else {
+ return layoutInflater.inflate(R.layout.fragment_composer_frag, viewGroup, false);
+ }
+ } else if (hasImage) {
+ if (hasSubject) {
+ return layoutInflater.inflate(R.layout.fragment_composer_text_image, viewGroup, false);
+ } else {
+ return layoutInflater.inflate(R.layout.fragment_composer_image, viewGroup, false);
+ }
+ } else {
+ return layoutInflater.inflate(R.layout.fragment_composer_text, viewGroup, false);
+ }
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle bundle) {
+ super.onViewCreated(view, bundle);
+ TextView messageText = (TextView) view.findViewById(R.id.answer_message_text);
+ if (messageText != null) {
+ messageText.setText(getSubject());
+ }
+ ImageView mainImage = (ImageView) view.findViewById(R.id.answer_message_image);
+ if (mainImage != null) {
+ Glide.with(this)
+ .load(getImageUri())
+ .transition(DrawableTransitionOptions.withCrossFade())
+ .listener(
+ new RequestListener<Drawable>() {
+ @Override
+ public boolean onLoadFailed(
+ @Nullable GlideException e,
+ Object model,
+ Target<Drawable> target,
+ boolean isFirstResource) {
+ view.findViewById(R.id.loading_spinner).setVisibility(View.GONE);
+ LogUtil.e("MultimediaFragment.onLoadFailed", null, e);
+ // TODO(b/34720074) handle error cases nicely
+ return false; // Let Glide handle the rest
+ }
+
+ @Override
+ public boolean onResourceReady(
+ Drawable drawable,
+ Object model,
+ Target<Drawable> target,
+ DataSource dataSource,
+ boolean isFirstResource) {
+ view.findViewById(R.id.loading_spinner).setVisibility(View.GONE);
+ return false;
+ }
+ })
+ .into(mainImage);
+ mainImage.setClipToOutline(true);
+ }
+ FrameLayout fragmentHolder = (FrameLayout) view.findViewById(R.id.answer_message_frag);
+ if (fragmentHolder != null) {
+ fragmentHolder.setClipToOutline(true);
+ Fragment mapFragment =
+ Assert.isNotNull(mapFactory).getStaticMap(Assert.isNotNull(getLocation()));
+ getChildFragmentManager()
+ .beginTransaction()
+ .replace(R.id.answer_message_frag, mapFragment)
+ .commitNow();
+ }
+ avatarImageView = ((ImageView) view.findViewById(R.id.answer_message_avatar));
+ avatarImageView.setVisibility(showAvatar ? View.VISIBLE : View.GONE);
+
+ Holder parent = FragmentUtils.getParent(this, Holder.class);
+ if (parent != null) {
+ parent.updateAvatar(this);
+ }
+ }
+
+ @Nullable
+ @Override
+ public ImageView getAvatarImageView() {
+ return avatarImageView;
+ }
+
+ @Override
+ public int getAvatarSize() {
+ return getResources().getDimensionPixelSize(R.dimen.answer_message_avatar_size);
+ }
+
+ @Override
+ public boolean shouldShowAnonymousAvatar() {
+ return showAvatar;
+ }
+
+ @Nullable
+ public String getSubject() {
+ return getArguments().getString(ARG_SUBJECT);
+ }
+
+ @Nullable
+ public Uri getImageUri() {
+ return getArguments().getParcelable(ARG_IMAGE);
+ }
+
+ @Nullable
+ public Location getLocation() {
+ return getArguments().getParcelable(ARG_LOCATION);
+ }
+
+ /** Interface for notifying the fragment parent of changes. */
+ public interface Holder {
+ void updateAvatar(AvatarPresenter sessionDataScreen);
+ }
+}
diff --git a/java/com/android/incallui/sessiondata/res/drawable/answer_data_background.xml b/java/com/android/incallui/sessiondata/res/drawable/answer_data_background.xml
new file mode 100644
index 000000000..8826f904b
--- /dev/null
+++ b/java/com/android/incallui/sessiondata/res/drawable/answer_data_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="16dp"/>
+ <solid android:color="@android:color/white"/>
+</shape>
diff --git a/java/com/android/incallui/sessiondata/res/layout/fragment_composer_frag.xml b/java/com/android/incallui/sessiondata/res/layout/fragment_composer_frag.xml
new file mode 100644
index 000000000..ed2bee0d1
--- /dev/null
+++ b/java/com/android/incallui/sessiondata/res/layout/fragment_composer_frag.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="24dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@id/answer_message_avatar"
+ android:layout_width="@dimen/answer_message_avatar_size"
+ android:layout_height="@dimen/answer_message_avatar_size"
+ android:elevation="@dimen/answer_data_elevation"/>
+
+ <FrameLayout
+ android:id="@id/answer_message_frag"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginBottom="4dp"
+ android:layout_marginStart="8dp"
+ android:background="@drawable/answer_data_background"
+ android:elevation="@dimen/answer_data_elevation"
+ android:outlineProvider="background"/>
+</LinearLayout>
diff --git a/java/com/android/incallui/sessiondata/res/layout/fragment_composer_image.xml b/java/com/android/incallui/sessiondata/res/layout/fragment_composer_image.xml
new file mode 100644
index 000000000..7000f83b5
--- /dev/null
+++ b/java/com/android/incallui/sessiondata/res/layout/fragment_composer_image.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="24dp">
+
+ <ImageView
+ android:id="@id/answer_message_avatar"
+ android:layout_width="@dimen/answer_message_avatar_size"
+ android:layout_height="@dimen/answer_message_avatar_size"
+ android:elevation="@dimen/answer_data_elevation"/>
+
+ <ImageView
+ android:id="@id/answer_message_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginStart="8dp"
+ android:layout_centerInParent="true"
+ android:layout_toEndOf="@+id/answer_message_avatar"
+ android:background="@drawable/answer_data_background"
+ android:elevation="@dimen/answer_data_elevation"
+ android:outlineProvider="background"
+ android:adjustViewBounds="true"
+ android:scaleType="fitXY"/>
+
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/loading_spinner"
+ android:layout_centerInParent="true"/>
+</RelativeLayout>
diff --git a/java/com/android/incallui/sessiondata/res/layout/fragment_composer_image_frag.xml b/java/com/android/incallui/sessiondata/res/layout/fragment_composer_image_frag.xml
new file mode 100644
index 000000000..9959f4dcc
--- /dev/null
+++ b/java/com/android/incallui/sessiondata/res/layout/fragment_composer_image_frag.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="16dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="24dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@id/answer_message_avatar"
+ android:layout_width="@dimen/answer_message_avatar_size"
+ android:layout_height="@dimen/answer_message_avatar_size"
+ android:layout_rowSpan="2"
+ android:elevation="@dimen/answer_data_elevation"/>
+
+ <ImageView
+ android:id="@id/answer_message_image"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginStart="8dp"
+ android:layout_columnWeight="1"
+ android:layout_rowWeight="1"
+ android:background="@drawable/answer_data_background"
+ android:elevation="@dimen/answer_data_elevation"
+ android:outlineProvider="background"
+ android:scaleType="centerCrop"/>
+
+ <FrameLayout
+ android:id="@id/answer_message_frag"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:layout_marginStart="8dp"
+ android:layout_column="1"
+ android:layout_columnWeight="1"
+ android:layout_row="1"
+ android:layout_rowWeight="1"
+ android:background="@drawable/answer_data_background"
+ android:elevation="@dimen/answer_data_elevation"
+ android:outlineProvider="background"/>
+</GridLayout>
diff --git a/java/com/android/incallui/sessiondata/res/layout/fragment_composer_text.xml b/java/com/android/incallui/sessiondata/res/layout/fragment_composer_text.xml
new file mode 100644
index 000000000..c69973042
--- /dev/null
+++ b/java/com/android/incallui/sessiondata/res/layout/fragment_composer_text.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="24dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@id/answer_message_avatar"
+ android:layout_width="@dimen/answer_message_avatar_size"
+ android:layout_height="@dimen/answer_message_avatar_size"
+ android:elevation="@dimen/answer_data_elevation"/>
+
+ <TextView
+ android:id="@id/answer_message_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginBottom="4dp"
+ android:layout_marginStart="8dp"
+ android:padding="18dp"
+ android:background="@drawable/answer_data_background"
+ android:elevation="@dimen/answer_data_elevation"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance.Message"/>
+</LinearLayout>
diff --git a/java/com/android/incallui/sessiondata/res/layout/fragment_composer_text_frag.xml b/java/com/android/incallui/sessiondata/res/layout/fragment_composer_text_frag.xml
new file mode 100644
index 000000000..5a1cf728b
--- /dev/null
+++ b/java/com/android/incallui/sessiondata/res/layout/fragment_composer_text_frag.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="16dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="24dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@id/answer_message_avatar"
+ android:layout_width="@dimen/answer_message_avatar_size"
+ android:layout_height="@dimen/answer_message_avatar_size"
+ android:layout_rowSpan="2"
+ android:elevation="@dimen/answer_data_elevation"/>
+
+ <TextView
+ android:id="@id/answer_message_text"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginStart="8dp"
+ android:layout_columnWeight="1"
+ android:layout_rowWeight="1"
+ android:padding="18dp"
+ android:background="@drawable/answer_data_background"
+ android:elevation="@dimen/answer_data_elevation"
+ android:gravity="center_vertical"
+ android:maxLines="2"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance.Message"/>
+
+ <FrameLayout
+ android:id="@id/answer_message_frag"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:layout_marginStart="8dp"
+ android:layout_column="1"
+ android:layout_columnWeight="1"
+ android:layout_row="1"
+ android:layout_rowWeight="1"
+ android:background="@drawable/answer_data_background"
+ android:elevation="@dimen/answer_data_elevation"
+ android:outlineProvider="background"/>
+</GridLayout>
diff --git a/java/com/android/incallui/sessiondata/res/layout/fragment_composer_text_image.xml b/java/com/android/incallui/sessiondata/res/layout/fragment_composer_text_image.xml
new file mode 100644
index 000000000..995565455
--- /dev/null
+++ b/java/com/android/incallui/sessiondata/res/layout/fragment_composer_text_image.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="16dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="24dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@id/answer_message_avatar"
+ android:layout_width="@dimen/answer_message_avatar_size"
+ android:layout_height="@dimen/answer_message_avatar_size"
+ android:layout_rowSpan="2"
+ android:elevation="@dimen/answer_data_elevation"/>
+
+ <TextView
+ android:id="@id/answer_message_text"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginStart="8dp"
+ android:layout_columnWeight="1"
+ android:layout_rowWeight="1"
+ android:padding="18dp"
+ android:background="@drawable/answer_data_background"
+ android:elevation="@dimen/answer_data_elevation"
+ android:gravity="center_vertical"
+ android:maxLines="2"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance.Message"/>
+
+ <ImageView
+ android:id="@id/answer_message_image"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:layout_marginStart="8dp"
+ android:layout_column="1"
+ android:layout_columnWeight="1"
+ android:layout_row="1"
+ android:layout_rowWeight="1"
+ android:background="@drawable/answer_data_background"
+ android:elevation="@dimen/answer_data_elevation"
+ android:outlineProvider="background"
+ android:scaleType="centerCrop"/>
+</GridLayout>
diff --git a/java/com/android/incallui/sessiondata/res/layout/fragment_composer_text_image_frag.xml b/java/com/android/incallui/sessiondata/res/layout/fragment_composer_text_image_frag.xml
new file mode 100644
index 000000000..387c5cf68
--- /dev/null
+++ b/java/com/android/incallui/sessiondata/res/layout/fragment_composer_text_image_frag.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="16dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="24dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@id/answer_message_avatar"
+ android:layout_width="@dimen/answer_message_avatar_size"
+ android:layout_height="@dimen/answer_message_avatar_size"
+ android:layout_rowSpan="2"
+ android:elevation="@dimen/answer_data_elevation"/>
+
+ <TextView
+ android:id="@id/answer_message_text"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginStart="8dp"
+ android:layout_columnWeight="2"
+ android:layout_columnSpan="2"
+ android:layout_rowWeight="1"
+ android:padding="18dp"
+ android:background="@drawable/answer_data_background"
+ android:elevation="@dimen/answer_data_elevation"
+ android:gravity="center_vertical"
+ android:maxLines="2"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance.Message"/>
+
+ <ImageView
+ android:id="@id/answer_message_image"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:layout_marginStart="8dp"
+ android:layout_column="1"
+ android:layout_columnWeight="1"
+ android:layout_row="1"
+ android:layout_rowWeight="1"
+ android:background="@drawable/answer_data_background"
+ android:elevation="@dimen/answer_data_elevation"
+ android:outlineProvider="background"
+ android:scaleType="centerCrop"/>
+
+ <FrameLayout
+ android:id="@id/answer_message_frag"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:layout_marginStart="8dp"
+ android:layout_column="2"
+ android:layout_columnWeight="1"
+ android:layout_row="1"
+ android:layout_rowWeight="1"
+ android:background="@drawable/answer_data_background"
+ android:elevation="@dimen/answer_data_elevation"
+ android:outlineProvider="background"/>
+</GridLayout>
diff --git a/java/com/android/incallui/sessiondata/res/values/dimens.xml b/java/com/android/incallui/sessiondata/res/values/dimens.xml
new file mode 100644
index 000000000..76c7edb1b
--- /dev/null
+++ b/java/com/android/incallui/sessiondata/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <dimen name="answer_message_avatar_size">40dp</dimen>
+ <dimen name="answer_data_elevation">2dp</dimen>
+</resources>
diff --git a/java/com/android/incallui/sessiondata/res/values/ids.xml b/java/com/android/incallui/sessiondata/res/values/ids.xml
new file mode 100644
index 000000000..077474c81
--- /dev/null
+++ b/java/com/android/incallui/sessiondata/res/values/ids.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <item name="answer_message_avatar" type="id"/>
+ <item name="answer_message_text" type="id"/>
+ <item name="answer_message_image" type="id"/>
+ <item name="answer_message_frag" type="id"/>
+</resources>
diff --git a/java/com/android/incallui/sessiondata/res/values/styles.xml b/java/com/android/incallui/sessiondata/res/values/styles.xml
new file mode 100644
index 000000000..dd898a4e2
--- /dev/null
+++ b/java/com/android/incallui/sessiondata/res/values/styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <style name="Dialer.Incall.TextAppearance.Message" parent="Dialer.Incall.TextAppearance">
+ <item name="android:fontFamily">sans-serif</item>
+ <item name="android:textColor">@android:color/black</item>
+ <item name="android:textSize">24sp</item>
+ </style>
+</resources>
diff --git a/java/com/android/incallui/spam/NumberInCallHistoryTask.java b/java/com/android/incallui/spam/NumberInCallHistoryTask.java
new file mode 100644
index 000000000..a225606f6
--- /dev/null
+++ b/java/com/android/incallui/spam/NumberInCallHistoryTask.java
@@ -0,0 +1,107 @@
+/*
+ * 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.incallui.spam;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.os.AsyncTask;
+import android.os.Build.VERSION_CODES;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.support.annotation.NonNull;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import com.android.dialer.common.AsyncTaskExecutor;
+import com.android.dialer.common.AsyncTaskExecutors;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.telecom.TelecomUtil;
+import com.android.dialer.util.PermissionsUtil;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.DialerCall.CallHistoryStatus;
+import java.util.Objects;
+
+/** Checks if the number is in the call history. */
+@TargetApi(VERSION_CODES.M)
+public class NumberInCallHistoryTask extends AsyncTask<Void, Void, Integer> {
+
+ public static final String TASK_ID = "number_in_call_history_status";
+
+ private final Context context;
+ private final Listener listener;
+ private final String number;
+ private final String countryIso;
+
+ public NumberInCallHistoryTask(
+ @NonNull Context context, @NonNull Listener listener, String number, String countryIso) {
+ this.context = Objects.requireNonNull(context);
+ this.listener = Objects.requireNonNull(listener);
+ this.number = number;
+ this.countryIso = countryIso;
+ }
+
+ public void submitTask() {
+ if (!PermissionsUtil.hasPhonePermissions(context)) {
+ return;
+ }
+ AsyncTaskExecutor asyncTaskExecutor = AsyncTaskExecutors.createThreadPoolExecutor();
+ asyncTaskExecutor.submit(TASK_ID, this);
+ }
+
+ @Override
+ @CallHistoryStatus
+ public Integer doInBackground(Void... params) {
+ String numberToQuery = number;
+ String fieldToQuery = Calls.NUMBER;
+ String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
+
+ // If we can normalize the number successfully, look in "normalized_number"
+ // field instead. Otherwise, look for number in "number" field.
+ if (!TextUtils.isEmpty(normalizedNumber)) {
+ numberToQuery = normalizedNumber;
+ fieldToQuery = Calls.CACHED_NORMALIZED_NUMBER;
+ }
+ try (Cursor cursor =
+ context
+ .getContentResolver()
+ .query(
+ TelecomUtil.getCallLogUri(context),
+ new String[] {CallLog.Calls._ID},
+ fieldToQuery + " = ?",
+ new String[] {numberToQuery},
+ null)) {
+ return cursor != null && cursor.getCount() > 0
+ ? DialerCall.CALL_HISTORY_STATUS_PRESENT
+ : DialerCall.CALL_HISTORY_STATUS_NOT_PRESENT;
+ } catch (SQLiteException e) {
+ LogUtil.e("NumberInCallHistoryTask.doInBackground", "query call log error", e);
+ return DialerCall.CALL_HISTORY_STATUS_UNKNOWN;
+ }
+ }
+
+ @Override
+ public void onPostExecute(@CallHistoryStatus Integer callHistoryStatus) {
+ listener.onComplete(callHistoryStatus);
+ }
+
+ /** Callback for the async task. */
+ public interface Listener {
+
+ void onComplete(@CallHistoryStatus int callHistoryStatus);
+ }
+}
diff --git a/java/com/android/incallui/spam/SpamCallListListener.java b/java/com/android/incallui/spam/SpamCallListListener.java
new file mode 100644
index 000000000..0897842de
--- /dev/null
+++ b/java/com/android/incallui/spam/SpamCallListListener.java
@@ -0,0 +1,364 @@
+/*
+ * 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.incallui.spam;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.telecom.DisconnectCause;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+import com.android.dialer.blocking.FilteredNumberCompat;
+import com.android.dialer.blocking.FilteredNumbersUtil;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.ContactLookupResult;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.spam.Spam;
+import com.android.incallui.R;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.DialerCall.CallHistoryStatus;
+import com.android.incallui.call.DialerCall.SessionModificationState;
+import java.util.Random;
+
+/**
+ * Creates notifications after a call ends if the call matched the criteria (incoming, accepted,
+ * etc).
+ */
+public class SpamCallListListener implements CallList.Listener {
+
+ static final int NOTIFICATION_ID = 1;
+ private static final String TAG = "SpamCallListListener";
+ private final Context context;
+ private final Random random;
+
+ public SpamCallListListener(Context context) {
+ this.context = context;
+ this.random = new Random();
+ }
+
+ public SpamCallListListener(Context context, Random rand) {
+ this.context = context;
+ this.random = rand;
+ }
+
+ private static String pii(String pii) {
+ return com.android.incallui.Log.pii(pii);
+ }
+
+ @Override
+ public void onIncomingCall(final DialerCall call) {
+ String number = call.getNumber();
+ if (TextUtils.isEmpty(number)) {
+ return;
+ }
+ NumberInCallHistoryTask.Listener listener =
+ new NumberInCallHistoryTask.Listener() {
+ @Override
+ public void onComplete(@CallHistoryStatus int callHistoryStatus) {
+ call.setCallHistoryStatus(callHistoryStatus);
+ }
+ };
+ new NumberInCallHistoryTask(context, listener, number, GeoUtil.getCurrentCountryIso(context))
+ .submitTask();
+ }
+
+ @Override
+ public void onUpgradeToVideo(DialerCall call) {}
+
+ @Override
+ public void onSessionModificationStateChange(@SessionModificationState int newState) {}
+
+ @Override
+ public void onCallListChange(CallList callList) {}
+
+ @Override
+ public void onWiFiToLteHandover(DialerCall call) {}
+
+ @Override
+ public void onHandoverToWifiFailed(DialerCall call) {}
+
+ @Override
+ public void onDisconnect(DialerCall call) {
+ if (!shouldShowAfterCallNotification(call)) {
+ return;
+ }
+ String e164Number =
+ PhoneNumberUtils.formatNumberToE164(
+ call.getNumber(), GeoUtil.getCurrentCountryIso(context));
+ if (!FilteredNumbersUtil.canBlockNumber(context, e164Number, call.getNumber())
+ || !FilteredNumberCompat.canAttemptBlockOperations(context)) {
+ return;
+ }
+ if (e164Number == null) {
+ return;
+ }
+ showNotification(call);
+ }
+
+ /** Posts the intent for displaying the after call spam notification to the user. */
+ private void showNotification(DialerCall call) {
+ if (call.isSpam()) {
+ maybeShowSpamCallNotification(call);
+ } else {
+ LogUtil.d(TAG, "Showing not spam notification for number=" + pii(call.getNumber()));
+ maybeShowNonSpamCallNotification(call);
+ }
+ }
+
+ /** Determines if the after call notification should be shown for the specified call. */
+ private boolean shouldShowAfterCallNotification(DialerCall call) {
+ if (!Spam.get(context).isSpamNotificationEnabled()) {
+ return false;
+ }
+
+ String number = call.getNumber();
+ if (TextUtils.isEmpty(number)) {
+ return false;
+ }
+
+ DialerCall.LogState logState = call.getLogState();
+ if (!logState.isIncoming) {
+ return false;
+ }
+
+ if (logState.duration <= 0) {
+ return false;
+ }
+
+ if (logState.contactLookupResult != ContactLookupResult.Type.NOT_FOUND
+ && logState.contactLookupResult != ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE) {
+ return false;
+ }
+
+ int callHistoryStatus = call.getCallHistoryStatus();
+ if (callHistoryStatus == DialerCall.CALL_HISTORY_STATUS_PRESENT) {
+ return false;
+ } else if (callHistoryStatus == DialerCall.CALL_HISTORY_STATUS_UNKNOWN) {
+ LogUtil.i(TAG, "DialerCall history status is unknown, returning false");
+ return false;
+ }
+
+ // Check if call disconnected because of either user hanging up
+ int disconnectCause = call.getDisconnectCause().getCode();
+ if (disconnectCause != DisconnectCause.LOCAL && disconnectCause != DisconnectCause.REMOTE) {
+ return false;
+ }
+
+ LogUtil.i(TAG, "shouldShowAfterCallNotification, returning true");
+ return true;
+ }
+
+ /**
+ * Creates a notification builder with properties common among the two after call notifications.
+ */
+ private Notification.Builder createAfterCallNotificationBuilder(DialerCall call) {
+ return new Notification.Builder(context)
+ .setContentIntent(
+ createActivityPendingIntent(call, SpamNotificationActivity.ACTION_SHOW_DIALOG))
+ .setCategory(Notification.CATEGORY_STATUS)
+ .setPriority(Notification.PRIORITY_DEFAULT)
+ .setColor(context.getColor(R.color.dialer_theme_color))
+ .setSmallIcon(R.drawable.ic_call_end_white_24dp);
+ }
+
+ private CharSequence getDisplayNumber(DialerCall call) {
+ String formattedNumber =
+ PhoneNumberUtils.formatNumber(call.getNumber(), GeoUtil.getCurrentCountryIso(context));
+ return PhoneNumberUtilsCompat.createTtsSpannable(formattedNumber);
+ }
+
+ /** Display a notification with two actions: "add contact" and "report spam". */
+ private void showNonSpamCallNotification(DialerCall call) {
+ Notification.Builder notificationBuilder =
+ createAfterCallNotificationBuilder(call)
+ .setLargeIcon(Icon.createWithResource(context, R.drawable.unknown_notification_icon))
+ .setContentText(
+ context.getString(R.string.spam_notification_non_spam_call_collapsed_text))
+ .setStyle(
+ new Notification.BigTextStyle()
+ .bigText(
+ context.getString(R.string.spam_notification_non_spam_call_expanded_text)))
+ // Add contact
+ .addAction(
+ new Notification.Action.Builder(
+ R.drawable.ic_person_add_grey600_24dp,
+ context.getString(R.string.spam_notification_add_contact_action_text),
+ createActivityPendingIntent(
+ call, SpamNotificationActivity.ACTION_ADD_TO_CONTACTS))
+ .build())
+ // Block/report spam
+ .addAction(
+ new Notification.Action.Builder(
+ R.drawable.ic_block_grey600_24dp,
+ context.getString(R.string.spam_notification_report_spam_action_text),
+ createBlockReportSpamPendingIntent(call))
+ .build())
+ .setContentTitle(
+ context.getString(R.string.non_spam_notification_title, getDisplayNumber(call)));
+ ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
+ .notify(call.getNumber(), NOTIFICATION_ID, notificationBuilder.build());
+ }
+
+ private boolean shouldThrottleSpamNotification() {
+ int randomNumber = random.nextInt(100);
+ int thresholdForShowing = Spam.get(context).percentOfSpamNotificationsToShow();
+ if (thresholdForShowing == 0) {
+ LogUtil.d(
+ TAG,
+ "shouldThrottleSpamNotification, not showing - percentOfSpamNotificationsToShow is 0");
+ return true;
+ } else if (randomNumber < thresholdForShowing) {
+ LogUtil.d(
+ TAG,
+ "shouldThrottleSpamNotification, showing " + randomNumber + " < " + thresholdForShowing);
+ return false;
+ } else {
+ LogUtil.d(
+ TAG,
+ "shouldThrottleSpamNotification, not showing "
+ + randomNumber
+ + " >= "
+ + thresholdForShowing);
+ return true;
+ }
+ }
+
+ private boolean shouldThrottleNonSpamNotification() {
+ int randomNumber = random.nextInt(100);
+ int thresholdForShowing = Spam.get(context).percentOfNonSpamNotificationsToShow();
+ if (thresholdForShowing == 0) {
+ LogUtil.d(TAG, "Not showing non spam notification: percentOfNonSpamNotificationsToShow is 0");
+ return true;
+ } else if (randomNumber < thresholdForShowing) {
+ LogUtil.d(
+ TAG, "Showing non spam notification: " + randomNumber + " < " + thresholdForShowing);
+ return false;
+ } else {
+ LogUtil.d(
+ TAG, "Not showing non spam notification:" + randomNumber + " >= " + thresholdForShowing);
+ return true;
+ }
+ }
+
+ private void maybeShowSpamCallNotification(DialerCall call) {
+ if (shouldThrottleSpamNotification()) {
+ Logger.get(context)
+ .logCallImpression(
+ DialerImpression.Type.SPAM_NOTIFICATION_NOT_SHOWN_AFTER_THROTTLE,
+ call.getUniqueCallId(),
+ call.getTimeAddedMs());
+ } else {
+ Logger.get(context)
+ .logCallImpression(
+ DialerImpression.Type.SPAM_NOTIFICATION_SHOWN_AFTER_THROTTLE,
+ call.getUniqueCallId(),
+ call.getTimeAddedMs());
+ showSpamCallNotification(call);
+ }
+ }
+
+ private void maybeShowNonSpamCallNotification(DialerCall call) {
+ if (shouldThrottleNonSpamNotification()) {
+ Logger.get(context)
+ .logCallImpression(
+ DialerImpression.Type.NON_SPAM_NOTIFICATION_NOT_SHOWN_AFTER_THROTTLE,
+ call.getUniqueCallId(),
+ call.getTimeAddedMs());
+ } else {
+ Logger.get(context)
+ .logCallImpression(
+ DialerImpression.Type.NON_SPAM_NOTIFICATION_SHOWN_AFTER_THROTTLE,
+ call.getUniqueCallId(),
+ call.getTimeAddedMs());
+ showNonSpamCallNotification(call);
+ }
+ }
+
+ /** Display a notification with the action "not spam". */
+ private void showSpamCallNotification(DialerCall call) {
+ Notification.Builder notificationBuilder =
+ createAfterCallNotificationBuilder(call)
+ .setLargeIcon(Icon.createWithResource(context, R.drawable.spam_notification_icon))
+ .setContentText(context.getString(R.string.spam_notification_spam_call_collapsed_text))
+ .setStyle(
+ new Notification.BigTextStyle()
+ .bigText(context.getString(R.string.spam_notification_spam_call_expanded_text)))
+ // Not spam
+ .addAction(
+ new Notification.Action.Builder(
+ R.drawable.ic_close_grey600_24dp,
+ context.getString(R.string.spam_notification_not_spam_action_text),
+ createNotSpamPendingIntent(call))
+ .build())
+ // Block/report spam
+ .addAction(
+ new Notification.Action.Builder(
+ R.drawable.ic_block_grey600_24dp,
+ context.getString(R.string.spam_notification_block_spam_action_text),
+ createBlockReportSpamPendingIntent(call))
+ .build())
+ .setContentTitle(
+ context.getString(R.string.spam_notification_title, getDisplayNumber(call)));
+ ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
+ .notify(call.getNumber(), NOTIFICATION_ID, notificationBuilder.build());
+ }
+
+ /**
+ * Creates a pending intent for block/report spam action. If enabled, this intent is forwarded to
+ * the {@link SpamNotificationActivity}, otherwise to the {@link SpamNotificationService}.
+ */
+ private PendingIntent createBlockReportSpamPendingIntent(DialerCall call) {
+ String action = SpamNotificationActivity.ACTION_MARK_NUMBER_AS_SPAM;
+ return Spam.get(context).isDialogEnabledForSpamNotification()
+ ? createActivityPendingIntent(call, action)
+ : createServicePendingIntent(call, action);
+ }
+
+ /**
+ * Creates a pending intent for not spam action. If enabled, this intent is forwarded to the
+ * {@link SpamNotificationActivity}, otherwise to the {@link SpamNotificationService}.
+ */
+ private PendingIntent createNotSpamPendingIntent(DialerCall call) {
+ String action = SpamNotificationActivity.ACTION_MARK_NUMBER_AS_NOT_SPAM;
+ return Spam.get(context).isDialogEnabledForSpamNotification()
+ ? createActivityPendingIntent(call, action)
+ : createServicePendingIntent(call, action);
+ }
+
+ /** Creates a pending intent for {@link SpamNotificationService}. */
+ private PendingIntent createServicePendingIntent(DialerCall call, String action) {
+ Intent intent =
+ SpamNotificationService.createServiceIntent(context, call, action, NOTIFICATION_ID);
+ return PendingIntent.getService(
+ context, (int) System.currentTimeMillis(), intent, PendingIntent.FLAG_ONE_SHOT);
+ }
+
+ /** Creates a pending intent for {@link SpamNotificationActivity}. */
+ private PendingIntent createActivityPendingIntent(DialerCall call, String action) {
+ Intent intent =
+ SpamNotificationActivity.createActivityIntent(context, call, action, NOTIFICATION_ID);
+ return PendingIntent.getActivity(
+ context, (int) System.currentTimeMillis(), intent, PendingIntent.FLAG_ONE_SHOT);
+ }
+}
diff --git a/java/com/android/incallui/spam/SpamNotificationActivity.java b/java/com/android/incallui/spam/SpamNotificationActivity.java
new file mode 100644
index 000000000..88d6bdfda
--- /dev/null
+++ b/java/com/android/incallui/spam/SpamNotificationActivity.java
@@ -0,0 +1,483 @@
+/*
+ * 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.incallui.spam;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.CallLog;
+import android.provider.ContactsContract;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentActivity;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+import com.android.dialer.blocking.BlockReportSpamDialogs;
+import com.android.dialer.blocking.BlockedNumbersMigrator;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.blocking.FilteredNumberCompat;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.logging.nano.ReportingLocation;
+import com.android.dialer.spam.Spam;
+import com.android.incallui.R;
+import com.android.incallui.call.DialerCall;
+
+/** Creates the after call notification dialogs. */
+public class SpamNotificationActivity extends FragmentActivity {
+
+ /** Action to add number to contacts. */
+ static final String ACTION_ADD_TO_CONTACTS = "com.android.incallui.spam.ACTION_ADD_TO_CONTACTS";
+ /** Action to show dialog. */
+ static final String ACTION_SHOW_DIALOG = "com.android.incallui.spam.ACTION_SHOW_DIALOG";
+ /** Action to mark a number as spam. */
+ static final String ACTION_MARK_NUMBER_AS_SPAM =
+ "com.android.incallui.spam.ACTION_MARK_NUMBER_AS_SPAM";
+ /** Action to mark a number as not spam. */
+ static final String ACTION_MARK_NUMBER_AS_NOT_SPAM =
+ "com.android.incallui.spam.ACTION_MARK_NUMBER_AS_NOT_SPAM";
+
+ private static final String TAG = "SpamNotifications";
+ private static final String EXTRA_NOTIFICATION_ID = "notification_id";
+ private static final String EXTRA_CALL_INFO = "call_info";
+
+ private static final String CALL_INFO_KEY_PHONE_NUMBER = "phone_number";
+ private static final String CALL_INFO_KEY_IS_SPAM = "is_spam";
+ private static final String CALL_INFO_KEY_CALL_ID = "call_id";
+ private static final String CALL_INFO_KEY_START_TIME_MILLIS = "call_start_time_millis";
+ private static final String CALL_INFO_CONTACT_LOOKUP_RESULT_TYPE = "contact_lookup_result_type";
+ private final DialogInterface.OnDismissListener dismissListener =
+ new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ if (!isFinishing()) {
+ finish();
+ }
+ }
+ };
+ private FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler;
+
+ /**
+ * Creates an intent to start this activity.
+ *
+ * @return Intent intent that starts this activity.
+ */
+ public static Intent createActivityIntent(
+ Context context, DialerCall call, String action, int notificationId) {
+ Intent intent = new Intent(context, SpamNotificationActivity.class);
+ intent.setAction(action);
+ // This ensures only one activity of this kind exists at a time.
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId);
+ intent.putExtra(EXTRA_CALL_INFO, newCallInfoBundle(call));
+ return intent;
+ }
+
+ /** Creates the intent to insert a contact. */
+ private static Intent createInsertContactsIntent(String number) {
+ Intent intent = new Intent(ContactsContract.Intents.Insert.ACTION);
+ // This ensures that the edit contact number field gets updated if called more than once.
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
+ intent.putExtra(ContactsContract.Intents.Insert.PHONE, number);
+ return intent;
+ }
+
+ /** Returns the formatted version of the given number. */
+ private static String getFormattedNumber(String number) {
+ return PhoneNumberUtilsCompat.createTtsSpannable(number).toString();
+ }
+
+ private static void logCallImpression(Context context, Bundle bundle, int impression) {
+ Logger.get(context)
+ .logCallImpression(
+ impression,
+ bundle.getString(CALL_INFO_KEY_CALL_ID),
+ bundle.getLong(CALL_INFO_KEY_START_TIME_MILLIS, 0));
+ }
+
+ private static Bundle newCallInfoBundle(DialerCall call) {
+ Bundle bundle = new Bundle();
+ bundle.putString(CALL_INFO_KEY_PHONE_NUMBER, call.getNumber());
+ bundle.putBoolean(CALL_INFO_KEY_IS_SPAM, call.isSpam());
+ bundle.putString(CALL_INFO_KEY_CALL_ID, call.getUniqueCallId());
+ bundle.putLong(CALL_INFO_KEY_START_TIME_MILLIS, call.getTimeAddedMs());
+ bundle.putInt(CALL_INFO_CONTACT_LOOKUP_RESULT_TYPE, call.getLogState().contactLookupResult);
+ return bundle;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ LogUtil.i(TAG, "onCreate");
+ super.onCreate(savedInstanceState);
+ setFinishOnTouchOutside(true);
+ filteredNumberAsyncQueryHandler = new FilteredNumberAsyncQueryHandler(this);
+ cancelNotification();
+ }
+
+ @Override
+ protected void onResume() {
+ LogUtil.i(TAG, "onResume");
+ super.onResume();
+ Intent intent = getIntent();
+ String number = getCallInfo().getString(CALL_INFO_KEY_PHONE_NUMBER);
+ boolean isSpam = getCallInfo().getBoolean(CALL_INFO_KEY_IS_SPAM);
+ int contactLookupResultType = getCallInfo().getInt(CALL_INFO_CONTACT_LOOKUP_RESULT_TYPE, 0);
+ switch (intent.getAction()) {
+ case ACTION_ADD_TO_CONTACTS:
+ logCallImpression(DialerImpression.Type.SPAM_AFTER_CALL_NOTIFICATION_ADD_TO_CONTACTS);
+ startActivity(createInsertContactsIntent(number));
+ finish();
+ break;
+ case ACTION_MARK_NUMBER_AS_SPAM:
+ assertDialogsEnabled();
+ maybeShowBlockReportSpamDialog(number, contactLookupResultType);
+ break;
+ case ACTION_MARK_NUMBER_AS_NOT_SPAM:
+ assertDialogsEnabled();
+ maybeShowNotSpamDialog(number, contactLookupResultType);
+ break;
+ case ACTION_SHOW_DIALOG:
+ if (isSpam) {
+ showSpamFullDialog();
+ } else {
+ showNonSpamDialog();
+ }
+ break;
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ LogUtil.d(TAG, "onPause");
+ // Finish activity on pause (e.g: orientation change or back button pressed)
+ filteredNumberAsyncQueryHandler = null;
+ if (!isFinishing()) {
+ finish();
+ }
+ super.onPause();
+ }
+
+ /** Creates and displays the dialog for whitelisting a number. */
+ private void maybeShowNotSpamDialog(final String number, final int contactLookupResultType) {
+ if (Spam.get(this).isDialogEnabledForSpamNotification()) {
+ BlockReportSpamDialogs.ReportNotSpamDialogFragment.newInstance(
+ getFormattedNumber(number),
+ new BlockReportSpamDialogs.OnConfirmListener() {
+ @Override
+ public void onClick() {
+ reportNotSpamAndFinish(number, contactLookupResultType);
+ }
+ },
+ dismissListener)
+ .show(getFragmentManager(), BlockReportSpamDialogs.NOT_SPAM_DIALOG_TAG);
+ } else {
+ reportNotSpamAndFinish(number, contactLookupResultType);
+ }
+ }
+
+ /** Creates and displays the dialog for blocking/reporting a number as spam. */
+ private void maybeShowBlockReportSpamDialog(
+ final String number, final int contactLookupResultType) {
+ if (Spam.get(this).isDialogEnabledForSpamNotification()) {
+ maybeShowBlockNumberMigrationDialog(
+ new BlockedNumbersMigrator.Listener() {
+ @Override
+ public void onComplete() {
+ BlockReportSpamDialogs.BlockReportSpamDialogFragment.newInstance(
+ getFormattedNumber(number),
+ Spam.get(SpamNotificationActivity.this).isDialogReportSpamCheckedByDefault(),
+ new BlockReportSpamDialogs.OnSpamDialogClickListener() {
+ @Override
+ public void onClick(boolean isSpamChecked) {
+ blockReportNumberAndFinish(
+ number, isSpamChecked, contactLookupResultType);
+ }
+ },
+ dismissListener)
+ .show(getFragmentManager(), BlockReportSpamDialogs.BLOCK_REPORT_SPAM_DIALOG_TAG);
+ }
+ });
+ } else {
+ blockReportNumberAndFinish(number, true, contactLookupResultType);
+ }
+ }
+
+ /**
+ * Displays the dialog for the first time unknown calls with actions "Add contact", "Block/report
+ * spam", and "Dismiss".
+ */
+ private void showNonSpamDialog() {
+ logCallImpression(DialerImpression.Type.SPAM_AFTER_CALL_NOTIFICATION_SHOW_NON_SPAM_DIALOG);
+ FirstTimeNonSpamCallDialogFragment.newInstance(getCallInfo())
+ .show(getSupportFragmentManager(), FirstTimeNonSpamCallDialogFragment.TAG);
+ }
+
+ /**
+ * Displays the dialog for first time spam calls with actions "Not spam", "Block", and "Dismiss".
+ */
+ private void showSpamFullDialog() {
+ logCallImpression(DialerImpression.Type.SPAM_AFTER_CALL_NOTIFICATION_SHOW_SPAM_DIALOG);
+ FirstTimeSpamCallDialogFragment.newInstance(getCallInfo())
+ .show(getSupportFragmentManager(), FirstTimeSpamCallDialogFragment.TAG);
+ }
+
+ /** Checks if the user has migrated to the new blocking and display a dialog if necessary. */
+ private void maybeShowBlockNumberMigrationDialog(BlockedNumbersMigrator.Listener listener) {
+ if (!FilteredNumberCompat.maybeShowBlockNumberMigrationDialog(
+ this, getFragmentManager(), listener)) {
+ listener.onComplete();
+ }
+ }
+
+ /** Block and report the number as spam. */
+ private void blockReportNumberAndFinish(
+ String number, boolean reportAsSpam, int contactLookupResultType) {
+ if (reportAsSpam) {
+ logCallImpression(DialerImpression.Type.SPAM_AFTER_CALL_NOTIFICATION_MARKED_NUMBER_AS_SPAM);
+ Spam.get(this)
+ .reportSpamFromAfterCallNotification(
+ number,
+ getCountryIso(),
+ CallLog.Calls.INCOMING_TYPE,
+ ReportingLocation.Type.FEEDBACK_PROMPT,
+ contactLookupResultType);
+ }
+
+ logCallImpression(DialerImpression.Type.SPAM_AFTER_CALL_NOTIFICATION_BLOCK_NUMBER);
+ filteredNumberAsyncQueryHandler.blockNumber(null, number, getCountryIso());
+ // TODO: DialerCall finish() after block/reporting async tasks complete (b/28441936)
+ finish();
+ }
+
+ /** Report the number as not spam. */
+ private void reportNotSpamAndFinish(String number, int contactLookupResultType) {
+ logCallImpression(DialerImpression.Type.SPAM_AFTER_CALL_NOTIFICATION_REPORT_NUMBER_AS_NOT_SPAM);
+ Spam.get(this)
+ .reportNotSpamFromAfterCallNotification(
+ number,
+ getCountryIso(),
+ CallLog.Calls.INCOMING_TYPE,
+ ReportingLocation.Type.FEEDBACK_PROMPT,
+ contactLookupResultType);
+ // TODO: DialerCall finish() after async task completes (b/28441936)
+ finish();
+ }
+
+ /** Cancels the notification associated with the number. */
+ private void cancelNotification() {
+ int notificationId = getIntent().getIntExtra(EXTRA_NOTIFICATION_ID, 1);
+ String number = getCallInfo().getString(CALL_INFO_KEY_PHONE_NUMBER);
+ ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE))
+ .cancel(number, notificationId);
+ }
+
+ private String getCountryIso() {
+ return GeoUtil.getCurrentCountryIso(this);
+ }
+
+ private void assertDialogsEnabled() {
+ if (!Spam.get(this).isDialogEnabledForSpamNotification()) {
+ throw new IllegalStateException(
+ "Cannot start this activity with given action because dialogs are not enabled.");
+ }
+ }
+
+ private Bundle getCallInfo() {
+ return getIntent().getBundleExtra(EXTRA_CALL_INFO);
+ }
+
+ private void logCallImpression(int impression) {
+ logCallImpression(this, getCallInfo(), impression);
+ }
+
+ /** Dialog that displays "Not spam", "Block/report spam" and "Dismiss". */
+ public static class FirstTimeSpamCallDialogFragment extends DialogFragment {
+
+ public static final String TAG = "FirstTimeSpamDialog";
+
+ private boolean dismissed;
+ private Context applicationContext;
+
+ private static DialogFragment newInstance(Bundle bundle) {
+ FirstTimeSpamCallDialogFragment fragment = new FirstTimeSpamCallDialogFragment();
+ fragment.setArguments(bundle);
+ return fragment;
+ }
+
+ @Override
+ public void onPause() {
+ dismiss();
+ super.onPause();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ logCallImpression(
+ applicationContext,
+ getArguments(),
+ DialerImpression.Type.SPAM_AFTER_CALL_NOTIFICATION_ON_DISMISS_SPAM_DIALOG);
+ super.onDismiss(dialog);
+ // If dialog was not dismissed by user pressing one of the buttons, finish activity
+ if (!dismissed && getActivity() != null && !getActivity().isFinishing()) {
+ getActivity().finish();
+ }
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ applicationContext = context.getApplicationContext();
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+ final SpamNotificationActivity spamNotificationActivity =
+ (SpamNotificationActivity) getActivity();
+ final String number = getArguments().getString(CALL_INFO_KEY_PHONE_NUMBER);
+ final int contactLookupResultType =
+ getArguments().getInt(CALL_INFO_CONTACT_LOOKUP_RESULT_TYPE, 0);
+
+ return new AlertDialog.Builder(getActivity())
+ .setCancelable(false)
+ .setTitle(getString(R.string.spam_notification_title, getFormattedNumber(number)))
+ .setMessage(getString(R.string.spam_notification_spam_call_expanded_text))
+ .setNeutralButton(
+ getString(R.string.notification_action_dismiss),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ }
+ })
+ .setPositiveButton(
+ getString(R.string.spam_notification_dialog_was_not_spam_action_text),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismissed = true;
+ dismiss();
+ spamNotificationActivity.maybeShowNotSpamDialog(number, contactLookupResultType);
+ }
+ })
+ .setNegativeButton(
+ getString(R.string.spam_notification_block_spam_action_text),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismissed = true;
+ dismiss();
+ spamNotificationActivity.maybeShowBlockReportSpamDialog(
+ number, contactLookupResultType);
+ }
+ })
+ .create();
+ }
+ }
+
+ /** Dialog that displays "Add contact", "Block/report spam" and "Dismiss". */
+ public static class FirstTimeNonSpamCallDialogFragment extends DialogFragment {
+
+ public static final String TAG = "FirstTimeNonSpamDialog";
+
+ private boolean dismissed;
+ private Context context;
+
+ private static DialogFragment newInstance(Bundle bundle) {
+ FirstTimeNonSpamCallDialogFragment fragment = new FirstTimeNonSpamCallDialogFragment();
+ fragment.setArguments(bundle);
+ return fragment;
+ }
+
+ @Override
+ public void onPause() {
+ // Dismiss on pause e.g: orientation change
+ dismiss();
+ super.onPause();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ super.onDismiss(dialog);
+ logCallImpression(
+ context,
+ getArguments(),
+ DialerImpression.Type.SPAM_AFTER_CALL_NOTIFICATION_ON_DISMISS_NON_SPAM_DIALOG);
+ // If dialog was not dismissed by user pressing one of the buttons, finish activity
+ if (!dismissed && getActivity() != null && !getActivity().isFinishing()) {
+ getActivity().finish();
+ }
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ this.context = context.getApplicationContext();
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+ final SpamNotificationActivity spamNotificationActivity =
+ (SpamNotificationActivity) getActivity();
+ final String number = getArguments().getString(CALL_INFO_KEY_PHONE_NUMBER);
+ final int contactLookupResultType =
+ getArguments().getInt(CALL_INFO_CONTACT_LOOKUP_RESULT_TYPE, 0);
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(getString(R.string.non_spam_notification_title, getFormattedNumber(number)))
+ .setCancelable(false)
+ .setMessage(getString(R.string.spam_notification_non_spam_call_expanded_text))
+ .setNeutralButton(
+ getString(R.string.notification_action_dismiss),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ }
+ })
+ .setPositiveButton(
+ getString(R.string.spam_notification_dialog_add_contact_action_text),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismissed = true;
+ dismiss();
+ startActivity(createInsertContactsIntent(number));
+ }
+ })
+ .setNegativeButton(
+ getString(R.string.spam_notification_dialog_block_report_spam_action_text),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismissed = true;
+ dismiss();
+ spamNotificationActivity.maybeShowBlockReportSpamDialog(
+ number, contactLookupResultType);
+ }
+ })
+ .create();
+ }
+ }
+}
diff --git a/java/com/android/incallui/spam/SpamNotificationService.java b/java/com/android/incallui/spam/SpamNotificationService.java
new file mode 100644
index 000000000..bf107f789
--- /dev/null
+++ b/java/com/android/incallui/spam/SpamNotificationService.java
@@ -0,0 +1,132 @@
+/*
+ * 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.incallui.spam;
+
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.provider.CallLog;
+import android.support.annotation.Nullable;
+import com.android.contacts.common.GeoUtil;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.logging.nano.ReportingLocation;
+import com.android.dialer.spam.Spam;
+import com.android.incallui.call.DialerCall;
+
+/**
+ * This service determines if the device is locked/unlocked and takes an action based on the state.
+ * A service is used to to determine this, as opposed to an activity, because the user must unlock
+ * the device before a notification can start an activity. This is not the case for a service, and
+ * intents can be sent to this service even from the lock screen. This allows users to quickly
+ * report a number as spam or not spam from their lock screen.
+ */
+public class SpamNotificationService extends Service {
+
+ private static final String TAG = "SpamNotificationSvc";
+
+ private static final String EXTRA_PHONE_NUMBER = "service_phone_number";
+ private static final String EXTRA_CALL_ID = "service_call_id";
+ private static final String EXTRA_CALL_START_TIME_MILLIS = "service_call_start_time_millis";
+ private static final String EXTRA_NOTIFICATION_ID = "service_notification_id";
+ private static final String EXTRA_CONTACT_LOOKUP_RESULT_TYPE =
+ "service_contact_lookup_result_type";
+ /** Creates an intent to start this service. */
+ public static Intent createServiceIntent(
+ Context context, DialerCall call, String action, int notificationId) {
+ Intent intent = new Intent(context, SpamNotificationService.class);
+ intent.setAction(action);
+ intent.putExtra(EXTRA_PHONE_NUMBER, call.getNumber());
+ intent.putExtra(EXTRA_CALL_ID, call.getUniqueCallId());
+ intent.putExtra(EXTRA_CALL_START_TIME_MILLIS, call.getTimeAddedMs());
+ intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId);
+ intent.putExtra(EXTRA_CONTACT_LOOKUP_RESULT_TYPE, call.getLogState().contactLookupResult);
+ return intent;
+ }
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ // Return null because clients cannot bind to this service
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ LogUtil.d(TAG, "onStartCommand");
+ if (intent == null) {
+ LogUtil.d(TAG, "Null intent");
+ stopSelf();
+ // Return {@link #START_NOT_STICKY} so service is not restarted.
+ return START_NOT_STICKY;
+ }
+ String number = intent.getStringExtra(EXTRA_PHONE_NUMBER);
+ int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, 1);
+ String countryIso = GeoUtil.getCurrentCountryIso(this);
+ int contactLookupResultType = intent.getIntExtra(EXTRA_CONTACT_LOOKUP_RESULT_TYPE, 0);
+
+ ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE))
+ .cancel(number, notificationId);
+
+ switch (intent.getAction()) {
+ case SpamNotificationActivity.ACTION_MARK_NUMBER_AS_SPAM:
+ logCallImpression(
+ intent, DialerImpression.Type.SPAM_NOTIFICATION_SERVICE_ACTION_MARK_NUMBER_AS_SPAM);
+ Spam.get(this)
+ .reportSpamFromAfterCallNotification(
+ number,
+ countryIso,
+ CallLog.Calls.INCOMING_TYPE,
+ ReportingLocation.Type.FEEDBACK_PROMPT,
+ contactLookupResultType);
+ new FilteredNumberAsyncQueryHandler(this).blockNumber(null, number, countryIso);
+ break;
+ case SpamNotificationActivity.ACTION_MARK_NUMBER_AS_NOT_SPAM:
+ logCallImpression(
+ intent, DialerImpression.Type.SPAM_NOTIFICATION_SERVICE_ACTION_MARK_NUMBER_AS_NOT_SPAM);
+ Spam.get(this)
+ .reportNotSpamFromAfterCallNotification(
+ number,
+ countryIso,
+ CallLog.Calls.INCOMING_TYPE,
+ ReportingLocation.Type.FEEDBACK_PROMPT,
+ contactLookupResultType);
+ break;
+ }
+ // TODO: call stopSelf() after async tasks complete (b/28441936)
+ stopSelf();
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ LogUtil.d(TAG, "onDestroy");
+ }
+
+ private void logCallImpression(Intent intent, int impression) {
+ Logger.get(this)
+ .logCallImpression(
+ impression,
+ intent.getStringExtra(EXTRA_CALL_ID),
+ intent.getLongExtra(EXTRA_CALL_START_TIME_MILLIS, 0));
+ }
+}
diff --git a/java/com/android/incallui/util/AccessibilityUtil.java b/java/com/android/incallui/util/AccessibilityUtil.java
new file mode 100644
index 000000000..65753484a
--- /dev/null
+++ b/java/com/android/incallui/util/AccessibilityUtil.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 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.incallui.util;
+
+import android.content.Context;
+import android.view.accessibility.AccessibilityManager;
+
+public class AccessibilityUtil {
+
+ public static boolean isAccessibilityEnabled(Context context) {
+ AccessibilityManager accessibilityManager =
+ context.getSystemService(AccessibilityManager.class);
+ return accessibilityManager.isEnabled();
+ }
+
+ public static boolean isTouchExplorationEnabled(Context context) {
+ AccessibilityManager accessibilityManager =
+ context.getSystemService(AccessibilityManager.class);
+ return accessibilityManager.isTouchExplorationEnabled();
+ }
+}
diff --git a/java/com/android/incallui/util/TelecomCallUtil.java b/java/com/android/incallui/util/TelecomCallUtil.java
new file mode 100644
index 000000000..8855543b1
--- /dev/null
+++ b/java/com/android/incallui/util/TelecomCallUtil.java
@@ -0,0 +1,51 @@
+/*
+ * 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.incallui.util;
+
+import android.net.Uri;
+import android.telecom.Call;
+import android.telephony.PhoneNumberUtils;
+
+/**
+ * Class to provide a standard interface for obtaining information from the underlying
+ * android.telecom.Call. Much of this should be obtained through the incall.Call, but on occasion we
+ * need to interact with the telecom.Call directly (eg. call blocking, before the incall.Call has
+ * been created).
+ */
+public class TelecomCallUtil {
+
+ // Whether the call handle is an emergency number.
+ public static boolean isEmergencyCall(Call call) {
+ Uri handle = call.getDetails().getHandle();
+ return PhoneNumberUtils.isEmergencyNumber(handle == null ? "" : handle.getSchemeSpecificPart());
+ }
+
+ public static String getNumber(Call call) {
+ if (call == null) {
+ return null;
+ }
+ if (call.getDetails().getGatewayInfo() != null) {
+ return call.getDetails().getGatewayInfo().getOriginalAddress().getSchemeSpecificPart();
+ }
+ Uri handle = getHandle(call);
+ return handle == null ? null : handle.getSchemeSpecificPart();
+ }
+
+ public static Uri getHandle(Call call) {
+ return call == null ? null : call.getDetails().getHandle();
+ }
+}
diff --git a/java/com/android/incallui/video/bindings/VideoBindings.java b/java/com/android/incallui/video/bindings/VideoBindings.java
new file mode 100644
index 000000000..934ff078a
--- /dev/null
+++ b/java/com/android/incallui/video/bindings/VideoBindings.java
@@ -0,0 +1,28 @@
+/*
+ * 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.incallui.video.bindings;
+
+import com.android.incallui.video.impl.VideoCallFragment;
+import com.android.incallui.video.protocol.VideoCallScreen;
+
+/** Bindings for video module. */
+public class VideoBindings {
+
+ public static VideoCallScreen createVideoCallScreen() {
+ return new VideoCallFragment();
+ }
+}
diff --git a/java/com/android/incallui/video/impl/AndroidManifest.xml b/java/com/android/incallui/video/impl/AndroidManifest.xml
new file mode 100644
index 000000000..a36828e29
--- /dev/null
+++ b/java/com/android/incallui/video/impl/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.incallui.video.impl">
+</manifest>
diff --git a/java/com/android/incallui/video/impl/CameraPermissionDialogFragment.java b/java/com/android/incallui/video/impl/CameraPermissionDialogFragment.java
new file mode 100644
index 000000000..291fce4a0
--- /dev/null
+++ b/java/com/android/incallui/video/impl/CameraPermissionDialogFragment.java
@@ -0,0 +1,62 @@
+/*
+ * 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.incallui.video.impl;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import com.android.dialer.common.FragmentUtils;
+
+/** Dialog fragment to ask for camera permission from user. */
+public class CameraPermissionDialogFragment extends DialogFragment {
+
+ static CameraPermissionDialogFragment newInstance() {
+ CameraPermissionDialogFragment fragment = new CameraPermissionDialogFragment();
+ return fragment;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle bundle) {
+ return new AlertDialog.Builder(getContext())
+ .setTitle(R.string.camera_permission_dialog_title)
+ .setMessage(R.string.camera_permission_dialog_message)
+ .setPositiveButton(
+ R.string.camera_permission_dialog_positive_button,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ VideoCallFragment fragment =
+ FragmentUtils.getParentUnsafe(
+ CameraPermissionDialogFragment.this, VideoCallFragment.class);
+ fragment.onCameraPermissionGranted();
+ }
+ })
+ .setNegativeButton(
+ R.string.camera_permission_dialog_negative_button,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ })
+ .create();
+ }
+}
diff --git a/java/com/android/incallui/video/impl/CheckableImageButton.java b/java/com/android/incallui/video/impl/CheckableImageButton.java
new file mode 100644
index 000000000..320f0571a
--- /dev/null
+++ b/java/com/android/incallui/video/impl/CheckableImageButton.java
@@ -0,0 +1,222 @@
+/*
+ * 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.incallui.video.impl;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.view.SoundEffectConstants;
+import android.widget.Checkable;
+import android.widget.ImageButton;
+
+/** Image button that maintains a checked state. */
+public class CheckableImageButton extends ImageButton implements Checkable {
+
+ private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
+
+ /** Callback interface to notify when the button's checked state has changed */
+ public interface OnCheckedChangeListener {
+
+ void onCheckedChanged(CheckableImageButton button, boolean isChecked);
+ }
+
+ private boolean broadcasting;
+ private boolean isChecked;
+ private OnCheckedChangeListener onCheckedChangeListener;
+ private CharSequence contentDescriptionChecked;
+ private CharSequence contentDescriptionUnchecked;
+
+ public CheckableImageButton(Context context) {
+ this(context, null);
+ }
+
+ public CheckableImageButton(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CheckableImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context, attrs);
+ }
+
+ private void init(Context context, AttributeSet attrs) {
+ TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CheckableImageButton);
+ setChecked(typedArray.getBoolean(R.styleable.CheckableImageButton_android_checked, false));
+ contentDescriptionChecked =
+ typedArray.getText(R.styleable.CheckableImageButton_contentDescriptionChecked);
+ contentDescriptionUnchecked =
+ typedArray.getText(R.styleable.CheckableImageButton_contentDescriptionUnchecked);
+ typedArray.recycle();
+
+ updateContentDescription();
+ setClickable(true);
+ setFocusable(true);
+ }
+
+ @Override
+ public void setChecked(boolean checked) {
+ performSetChecked(checked);
+ }
+
+ /**
+ * Called when the state of the button should be updated, this should not be the result of user
+ * interaction.
+ *
+ * @param checked {@code true} if the button should be in the checked state, {@code false}
+ * otherwise.
+ */
+ private void performSetChecked(boolean checked) {
+ if (isChecked() == checked) {
+ return;
+ }
+ isChecked = checked;
+ CharSequence contentDescription = updateContentDescription();
+ announceForAccessibility(contentDescription);
+ refreshDrawableState();
+ }
+
+ private CharSequence updateContentDescription() {
+ CharSequence contentDescription =
+ isChecked ? contentDescriptionChecked : contentDescriptionUnchecked;
+ setContentDescription(contentDescription);
+ return contentDescription;
+ }
+
+ /**
+ * Called when the user interacts with a button. This should not result in the button updating
+ * state, rather the request should be propagated to the associated listener.
+ *
+ * @param checked {@code true} if the button should be in the checked state, {@code false}
+ * otherwise.
+ */
+ private void userRequestedSetChecked(boolean checked) {
+ if (isChecked() == checked) {
+ return;
+ }
+ if (broadcasting) {
+ return;
+ }
+ broadcasting = true;
+ if (onCheckedChangeListener != null) {
+ onCheckedChangeListener.onCheckedChanged(this, checked);
+ }
+ broadcasting = false;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return isChecked;
+ }
+
+ @Override
+ public void toggle() {
+ userRequestedSetChecked(!isChecked());
+ }
+
+ @Override
+ public int[] onCreateDrawableState(int extraSpace) {
+ final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
+ if (isChecked()) {
+ mergeDrawableStates(drawableState, CHECKED_STATE_SET);
+ }
+ return drawableState;
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+ invalidate();
+ }
+
+ public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
+ this.onCheckedChangeListener = listener;
+ }
+
+ @Override
+ public boolean performClick() {
+ if (!isCheckable()) {
+ return super.performClick();
+ }
+
+ toggle();
+ final boolean handled = super.performClick();
+ if (!handled) {
+ // View only makes a sound effect if the onClickListener was
+ // called, so we'll need to make one here instead.
+ playSoundEffect(SoundEffectConstants.CLICK);
+ }
+ return handled;
+ }
+
+ private boolean isCheckable() {
+ return onCheckedChangeListener != null;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState savedState = (SavedState) state;
+ super.onRestoreInstanceState(savedState.getSuperState());
+ performSetChecked(savedState.isChecked);
+ requestLayout();
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ return new SavedState(isChecked(), super.onSaveInstanceState());
+ }
+
+ private static class SavedState extends BaseSavedState {
+
+ public final boolean isChecked;
+
+ private SavedState(boolean isChecked, Parcelable superState) {
+ super(superState);
+ this.isChecked = isChecked;
+ }
+
+ protected SavedState(Parcel in) {
+ super(in);
+ isChecked = in.readByte() != 0;
+ }
+
+ public static final Creator<SavedState> CREATOR =
+ new Creator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeByte((byte) (isChecked ? 1 : 0));
+ }
+ }
+}
diff --git a/java/com/android/incallui/video/impl/SpeakerButtonController.java b/java/com/android/incallui/video/impl/SpeakerButtonController.java
new file mode 100644
index 000000000..e12032abf
--- /dev/null
+++ b/java/com/android/incallui/video/impl/SpeakerButtonController.java
@@ -0,0 +1,118 @@
+/*
+ * 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.incallui.video.impl;
+
+import android.support.annotation.DrawableRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import android.telecom.CallAudioState;
+import android.view.View;
+import android.view.View.OnClickListener;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
+import com.android.incallui.video.impl.CheckableImageButton.OnCheckedChangeListener;
+import com.android.incallui.video.protocol.VideoCallScreenDelegate;
+
+/** Manages a single button. */
+public class SpeakerButtonController implements OnCheckedChangeListener, OnClickListener {
+
+ @NonNull private final InCallButtonUiDelegate inCallButtonUiDelegate;
+ @NonNull private final VideoCallScreenDelegate videoCallScreenDelegate;
+
+ @NonNull private CheckableImageButton button;
+
+ @DrawableRes private int icon = R.drawable.quantum_ic_volume_up_white_36;
+
+ private boolean isChecked;
+ private boolean checkable;
+ private boolean isEnabled;
+ private CharSequence contentDescription;
+
+ public SpeakerButtonController(
+ @NonNull CheckableImageButton button,
+ @NonNull InCallButtonUiDelegate inCallButtonUiDelegate,
+ @NonNull VideoCallScreenDelegate videoCallScreenDelegate) {
+ this.inCallButtonUiDelegate = Assert.isNotNull(inCallButtonUiDelegate);
+ this.videoCallScreenDelegate = Assert.isNotNull(videoCallScreenDelegate);
+ this.button = Assert.isNotNull(button);
+ }
+
+ public void setEnabled(boolean isEnabled) {
+ this.isEnabled = isEnabled;
+ }
+
+ public void updateButtonState() {
+ button.setVisibility(View.VISIBLE);
+ button.setEnabled(isEnabled);
+ button.setChecked(isChecked);
+ button.setOnClickListener(checkable ? null : this);
+ button.setOnCheckedChangeListener(checkable ? this : null);
+ button.setImageResource(icon);
+ button.setContentDescription(contentDescription);
+ }
+
+ public void setAudioState(CallAudioState audioState) {
+ LogUtil.i("SpeakerButtonController.setSupportedAudio", "audioState: " + audioState);
+
+ @StringRes int contentDescriptionResId;
+ if ((audioState.getSupportedRouteMask() & CallAudioState.ROUTE_BLUETOOTH)
+ == CallAudioState.ROUTE_BLUETOOTH) {
+ checkable = false;
+ isChecked = false;
+
+ if ((audioState.getRoute() & CallAudioState.ROUTE_BLUETOOTH)
+ == CallAudioState.ROUTE_BLUETOOTH) {
+ icon = R.drawable.quantum_ic_bluetooth_audio_white_36;
+ contentDescriptionResId = R.string.incall_content_description_bluetooth;
+ } else if ((audioState.getRoute() & CallAudioState.ROUTE_SPEAKER)
+ == CallAudioState.ROUTE_SPEAKER) {
+ icon = R.drawable.quantum_ic_volume_up_white_36;
+ contentDescriptionResId = R.string.incall_content_description_speaker;
+ } else if ((audioState.getRoute() & CallAudioState.ROUTE_WIRED_HEADSET)
+ == CallAudioState.ROUTE_WIRED_HEADSET) {
+ icon = R.drawable.quantum_ic_headset_white_36;
+ contentDescriptionResId = R.string.incall_content_description_headset;
+ } else {
+ icon = R.drawable.ic_phone_audio_white_36dp;
+ contentDescriptionResId = R.string.incall_content_description_earpiece;
+ }
+ } else {
+ checkable = true;
+ isChecked = audioState.getRoute() == CallAudioState.ROUTE_SPEAKER;
+ icon = R.drawable.quantum_ic_volume_up_white_36;
+ contentDescriptionResId = R.string.incall_content_description_speaker;
+ }
+
+ contentDescription = button.getContext().getText(contentDescriptionResId);
+ updateButtonState();
+ }
+
+ @Override
+ public void onCheckedChanged(CheckableImageButton button, boolean isChecked) {
+ LogUtil.i("SpeakerButtonController.onCheckedChanged", null);
+ inCallButtonUiDelegate.toggleSpeakerphone();
+ videoCallScreenDelegate.resetAutoFullscreenTimer();
+ }
+
+ @Override
+ public void onClick(View view) {
+ LogUtil.i("SpeakerButtonController.onClick", null);
+ inCallButtonUiDelegate.showAudioRouteSelector();
+ videoCallScreenDelegate.resetAutoFullscreenTimer();
+ }
+}
diff --git a/java/com/android/incallui/video/impl/SwitchOnHoldCallController.java b/java/com/android/incallui/video/impl/SwitchOnHoldCallController.java
new file mode 100644
index 000000000..372b56b4e
--- /dev/null
+++ b/java/com/android/incallui/video/impl/SwitchOnHoldCallController.java
@@ -0,0 +1,91 @@
+/*
+ * 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.incallui.video.impl;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.view.View.OnClickListener;
+import com.android.dialer.common.Assert;
+import com.android.incallui.incall.protocol.InCallScreenDelegate;
+import com.android.incallui.incall.protocol.SecondaryInfo;
+import com.android.incallui.video.protocol.VideoCallScreenDelegate;
+
+/** Manages the swap button and on hold banner. */
+public class SwitchOnHoldCallController implements OnClickListener {
+
+ @NonNull private InCallScreenDelegate inCallScreenDelegate;
+ @NonNull private VideoCallScreenDelegate videoCallScreenDelegate;
+
+ @NonNull private View switchOnHoldButton;
+
+ @NonNull private View onHoldBanner;
+
+ private boolean isVisible;
+
+ private boolean isEnabled;
+
+ @Nullable private SecondaryInfo secondaryInfo;
+
+ public SwitchOnHoldCallController(
+ @NonNull View switchOnHoldButton,
+ @NonNull View onHoldBanner,
+ @NonNull InCallScreenDelegate inCallScreenDelegate,
+ @NonNull VideoCallScreenDelegate videoCallScreenDelegate) {
+ this.switchOnHoldButton = Assert.isNotNull(switchOnHoldButton);
+ switchOnHoldButton.setOnClickListener(this);
+ this.onHoldBanner = Assert.isNotNull(onHoldBanner);
+ this.inCallScreenDelegate = Assert.isNotNull(inCallScreenDelegate);
+ this.videoCallScreenDelegate = Assert.isNotNull(videoCallScreenDelegate);
+ }
+
+ public void setEnabled(boolean isEnabled) {
+ this.isEnabled = isEnabled;
+ updateButtonState();
+ }
+
+ public void setVisible(boolean isVisible) {
+ this.isVisible = isVisible;
+ updateButtonState();
+ }
+
+ public void setOnScreen() {
+ isVisible = hasSecondaryInfo();
+ updateButtonState();
+ }
+
+ public void setSecondaryInfo(@Nullable SecondaryInfo secondaryInfo) {
+ this.secondaryInfo = secondaryInfo;
+ isVisible = hasSecondaryInfo();
+ }
+
+ private boolean hasSecondaryInfo() {
+ return secondaryInfo != null && secondaryInfo.shouldShow;
+ }
+
+ public void updateButtonState() {
+ switchOnHoldButton.setEnabled(isEnabled);
+ switchOnHoldButton.setVisibility(isVisible ? View.VISIBLE : View.INVISIBLE);
+ onHoldBanner.setVisibility(isVisible ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ @Override
+ public void onClick(View view) {
+ inCallScreenDelegate.onSecondaryInfoClicked();
+ videoCallScreenDelegate.resetAutoFullscreenTimer();
+ }
+}
diff --git a/java/com/android/incallui/video/impl/VideoCallFragment.java b/java/com/android/incallui/video/impl/VideoCallFragment.java
new file mode 100644
index 000000000..77a67d032
--- /dev/null
+++ b/java/com/android/incallui/video/impl/VideoCallFragment.java
@@ -0,0 +1,1215 @@
+/*
+ * 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.incallui.video.impl;
+
+import android.Manifest.permission;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Outline;
+import android.graphics.Point;
+import android.graphics.drawable.Animatable;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.ScriptIntrinsicBlur;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.view.animation.FastOutLinearInInterpolator;
+import android.support.v4.view.animation.LinearOutSlowInInterpolator;
+import android.telecom.CallAudioState;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.Surface;
+import android.view.TextureView;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnSystemUiVisibilityChangeListener;
+import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.view.ViewOutlineProvider;
+import android.view.ViewTreeObserver;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.FragmentUtils;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.compat.ActivityCompat;
+import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment;
+import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment.AudioRouteSelectorPresenter;
+import com.android.incallui.call.VideoUtils;
+import com.android.incallui.contactgrid.ContactGridManager;
+import com.android.incallui.hold.OnHoldFragment;
+import com.android.incallui.incall.protocol.InCallButtonIds;
+import com.android.incallui.incall.protocol.InCallButtonIdsExtension;
+import com.android.incallui.incall.protocol.InCallButtonUi;
+import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
+import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
+import com.android.incallui.incall.protocol.InCallScreen;
+import com.android.incallui.incall.protocol.InCallScreenDelegate;
+import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
+import com.android.incallui.incall.protocol.PrimaryCallState;
+import com.android.incallui.incall.protocol.PrimaryInfo;
+import com.android.incallui.incall.protocol.SecondaryInfo;
+import com.android.incallui.video.impl.CheckableImageButton.OnCheckedChangeListener;
+import com.android.incallui.video.protocol.VideoCallScreen;
+import com.android.incallui.video.protocol.VideoCallScreenDelegate;
+import com.android.incallui.video.protocol.VideoCallScreenDelegateFactory;
+import com.android.incallui.videosurface.bindings.VideoSurfaceBindings;
+import com.android.incallui.videosurface.protocol.VideoSurfaceTexture;
+
+/** Contains UI elements for a video call. */
+public class VideoCallFragment extends Fragment
+ implements InCallScreen,
+ InCallButtonUi,
+ VideoCallScreen,
+ OnClickListener,
+ OnCheckedChangeListener,
+ AudioRouteSelectorPresenter,
+ OnSystemUiVisibilityChangeListener {
+
+ private static final float BLUR_PREVIEW_RADIUS = 16.0f;
+ private static final float BLUR_PREVIEW_SCALE_FACTOR = 1.0f;
+ private static final float BLUR_REMOTE_RADIUS = 25.0f;
+ private static final float BLUR_REMOTE_SCALE_FACTOR = 0.25f;
+ private static final float ASPECT_RATIO_MATCH_THRESHOLD = 0.2f;
+
+ private static final int CAMERA_PERMISSION_REQUEST_CODE = 1;
+ private static final String CAMERA_PERMISSION_DIALOG_FRAMENT_TAG =
+ "CameraPermissionDialogFragment";
+ private static final long CAMERA_PERMISSION_DIALOG_DELAY_IN_MILLIS = 2000L;
+ private static final long VIDEO_OFF_VIEW_FADE_OUT_DELAY_IN_MILLIS = 2000L;
+
+ private final ViewOutlineProvider circleOutlineProvider =
+ new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ int x = view.getWidth() / 2;
+ int y = view.getHeight() / 2;
+ int radius = Math.min(x, y);
+ outline.setOval(x - radius, y - radius, x + radius, y + radius);
+ }
+ };
+ private InCallScreenDelegate inCallScreenDelegate;
+ private VideoCallScreenDelegate videoCallScreenDelegate;
+ private InCallButtonUiDelegate inCallButtonUiDelegate;
+ private View endCallButton;
+ private CheckableImageButton speakerButton;
+ private SpeakerButtonController speakerButtonController;
+ private CheckableImageButton muteButton;
+ private CheckableImageButton cameraOffButton;
+ private ImageButton swapCameraButton;
+ private View switchOnHoldButton;
+ private View onHoldContainer;
+ private SwitchOnHoldCallController switchOnHoldCallController;
+ private TextView remoteVideoOff;
+ private ImageView remoteOffBlurredImageView;
+ private View mutePreviewOverlay;
+ private View previewOffOverlay;
+ private ImageView previewOffBlurredImageView;
+ private View controls;
+ private View controlsContainer;
+ private TextureView previewTextureView;
+ private TextureView remoteTextureView;
+ private View greenScreenBackgroundView;
+ private View fullscreenBackgroundView;
+ private boolean shouldShowRemote;
+ private boolean shouldShowPreview;
+ private boolean isInFullscreenMode;
+ private boolean isInGreenScreenMode;
+ private boolean hasInitializedScreenModes;
+ private boolean isRemotelyHeld;
+ private ContactGridManager contactGridManager;
+ private SecondaryInfo savedSecondaryInfo;
+ private final Runnable cameraPermissionDialogRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ if (videoCallScreenDelegate.shouldShowCameraPermissionDialog()) {
+ LogUtil.i("VideoCallFragment.cameraPermissionDialogRunnable", "showing dialog");
+ checkCameraPermission();
+ }
+ }
+ };
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ LogUtil.i("VideoCallFragment.onCreate", null);
+
+ inCallButtonUiDelegate =
+ FragmentUtils.getParent(this, InCallButtonUiDelegateFactory.class)
+ .newInCallButtonUiDelegate();
+ if (savedInstanceState != null) {
+ inCallButtonUiDelegate.onRestoreInstanceState(savedInstanceState);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ LogUtil.i("VideoCallFragment.onRequestPermissionsResult", "Camera permission granted.");
+ videoCallScreenDelegate.onCameraPermissionGranted();
+ } else {
+ LogUtil.i("VideoCallFragment.onRequestPermissionsResult", "Camera permission denied.");
+ }
+ }
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ LogUtil.i("VideoCallFragment.onCreateView", null);
+
+ View view =
+ layoutInflater.inflate(
+ isLandscape() ? R.layout.frag_videocall_land : R.layout.frag_videocall,
+ viewGroup,
+ false);
+ contactGridManager =
+ new ContactGridManager(view, null /* no avatar */, 0, false /* showAnonymousAvatar */);
+
+ controls = view.findViewById(R.id.videocall_video_controls);
+ controls.setVisibility(
+ ActivityCompat.isInMultiWindowMode(getActivity()) ? View.GONE : View.VISIBLE);
+ controlsContainer = view.findViewById(R.id.videocall_video_controls_container);
+ speakerButton = (CheckableImageButton) view.findViewById(R.id.videocall_speaker_button);
+ muteButton = (CheckableImageButton) view.findViewById(R.id.videocall_mute_button);
+ muteButton.setOnCheckedChangeListener(this);
+ mutePreviewOverlay = view.findViewById(R.id.videocall_video_preview_mute_overlay);
+ cameraOffButton = (CheckableImageButton) view.findViewById(R.id.videocall_mute_video);
+ cameraOffButton.setOnCheckedChangeListener(this);
+ previewOffOverlay = view.findViewById(R.id.videocall_video_preview_off_overlay);
+ previewOffBlurredImageView =
+ (ImageView) view.findViewById(R.id.videocall_preview_off_blurred_image_view);
+ swapCameraButton = (ImageButton) view.findViewById(R.id.videocall_switch_video);
+ swapCameraButton.setOnClickListener(this);
+ view.findViewById(R.id.videocall_switch_controls)
+ .setVisibility(
+ ActivityCompat.isInMultiWindowMode(getActivity()) ? View.GONE : View.VISIBLE);
+ switchOnHoldButton = view.findViewById(R.id.videocall_switch_on_hold);
+ onHoldContainer = view.findViewById(R.id.videocall_on_hold_banner);
+ remoteVideoOff = (TextView) view.findViewById(R.id.videocall_remote_video_off);
+ remoteVideoOff.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
+ remoteOffBlurredImageView =
+ (ImageView) view.findViewById(R.id.videocall_remote_off_blurred_image_view);
+ endCallButton = view.findViewById(R.id.videocall_end_call);
+ endCallButton.setOnClickListener(this);
+ previewTextureView = (TextureView) view.findViewById(R.id.videocall_video_preview);
+ previewTextureView.setClipToOutline(true);
+ previewOffOverlay.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ checkCameraPermission();
+ }
+ });
+ remoteTextureView = (TextureView) view.findViewById(R.id.videocall_video_remote);
+ greenScreenBackgroundView = view.findViewById(R.id.videocall_green_screen_background);
+ fullscreenBackgroundView = view.findViewById(R.id.videocall_fullscreen_background);
+
+ // We need the texture view size to be able to scale the remote video. At this point the view
+ // layout won't be complete so add a layout listener.
+ ViewTreeObserver observer = remoteTextureView.getViewTreeObserver();
+ observer.addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ LogUtil.i("VideoCallFragment.onGlobalLayout", null);
+ updateRemoteVideoScaling();
+ updatePreviewVideoScaling();
+ updateVideoOffViews();
+ // Remove the listener so we don't continually re-layout.
+ ViewTreeObserver observer = remoteTextureView.getViewTreeObserver();
+ if (observer.isAlive()) {
+ observer.removeOnGlobalLayoutListener(this);
+ }
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle bundle) {
+ super.onViewCreated(view, bundle);
+ LogUtil.i("VideoCallFragment.onViewCreated", null);
+
+ inCallScreenDelegate =
+ FragmentUtils.getParentUnsafe(this, InCallScreenDelegateFactory.class)
+ .newInCallScreenDelegate();
+ videoCallScreenDelegate =
+ FragmentUtils.getParentUnsafe(this, VideoCallScreenDelegateFactory.class)
+ .newVideoCallScreenDelegate();
+
+ speakerButtonController =
+ new SpeakerButtonController(speakerButton, inCallButtonUiDelegate, videoCallScreenDelegate);
+ switchOnHoldCallController =
+ new SwitchOnHoldCallController(
+ switchOnHoldButton, onHoldContainer, inCallScreenDelegate, videoCallScreenDelegate);
+
+ videoCallScreenDelegate.initVideoCallScreenDelegate(getContext(), this);
+
+ inCallScreenDelegate.onInCallScreenDelegateInit(this);
+ inCallScreenDelegate.onInCallScreenReady();
+ inCallButtonUiDelegate.onInCallButtonUiReady(this);
+
+ view.setOnSystemUiVisibilityChangeListener(this);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ inCallButtonUiDelegate.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ LogUtil.i("VideoCallFragment.onDestroyView", null);
+ inCallButtonUiDelegate.onInCallButtonUiUnready();
+ inCallScreenDelegate.onInCallScreenUnready();
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ if (savedSecondaryInfo != null) {
+ setSecondary(savedSecondaryInfo);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ LogUtil.i("VideoCallFragment.onResume", null);
+ inCallScreenDelegate.onInCallScreenResumed();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ LogUtil.i("VideoCallFragment.onStart", null);
+ inCallButtonUiDelegate.refreshMuteState();
+ videoCallScreenDelegate.onVideoCallScreenUiReady();
+ getView().postDelayed(cameraPermissionDialogRunnable, CAMERA_PERMISSION_DIALOG_DELAY_IN_MILLIS);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ LogUtil.i("VideoCallFragment.onPause", null);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ LogUtil.i("VideoCallFragment.onStop", null);
+ getView().removeCallbacks(cameraPermissionDialogRunnable);
+ videoCallScreenDelegate.onVideoCallScreenUiUnready();
+ }
+
+ private void exitFullscreenMode() {
+ LogUtil.i("VideoCallFragment.exitFullscreenMode", null);
+
+ if (!getView().isAttachedToWindow()) {
+ LogUtil.i("VideoCallFragment.exitFullscreenMode", "not attached");
+ return;
+ }
+
+ showSystemUI();
+
+ LinearOutSlowInInterpolator linearOutSlowInInterpolator = new LinearOutSlowInInterpolator();
+
+ // Animate the controls to the shown state.
+ controls
+ .animate()
+ .translationX(0)
+ .translationY(0)
+ .setInterpolator(linearOutSlowInInterpolator)
+ .alpha(1)
+ .start();
+
+ // Animate onHold to the shown state.
+ switchOnHoldButton
+ .animate()
+ .translationX(0)
+ .translationY(0)
+ .setInterpolator(linearOutSlowInInterpolator)
+ .alpha(1)
+ .withStartAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ switchOnHoldCallController.setOnScreen();
+ }
+ });
+
+ View contactGridView = contactGridManager.getContainerView();
+ // Animate contact grid to the shown state.
+ contactGridView
+ .animate()
+ .translationX(0)
+ .translationY(0)
+ .setInterpolator(linearOutSlowInInterpolator)
+ .alpha(1)
+ .withStartAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ contactGridManager.show();
+ }
+ });
+
+ endCallButton
+ .animate()
+ .translationX(0)
+ .translationY(0)
+ .setInterpolator(linearOutSlowInInterpolator)
+ .alpha(1)
+ .withStartAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ endCallButton.setVisibility(View.VISIBLE);
+ }
+ })
+ .start();
+
+ // Animate all the preview controls up to make room for the navigation bar.
+ // In green screen mode we don't need this because the preview takes up the whole screen and has
+ // a fixed position.
+ if (!isInGreenScreenMode) {
+ Point previewOffsetStartShown = getPreviewOffsetStartShown();
+ for (View view : getAllPreviewRelatedViews()) {
+ // Animate up with the preview offset above the navigation bar.
+ view.animate()
+ .translationX(previewOffsetStartShown.x)
+ .translationY(previewOffsetStartShown.y)
+ .setInterpolator(new AccelerateDecelerateInterpolator())
+ .start();
+ }
+ }
+
+ updateOverlayBackground();
+ }
+
+ private void showSystemUI() {
+ View view = getView();
+ if (view != null) {
+ // Code is more expressive with all flags present, even though some may be combined
+ //noinspection PointlessBitwiseExpression
+ view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+ }
+ }
+
+ /** Set view flags to hide the system UI. System UI will return on any touch event */
+ private void hideSystemUI() {
+ View view = getView();
+ if (view != null) {
+ view.setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+ }
+ }
+
+ private Point getControlsOffsetEndHidden(View controls) {
+ if (isLandscape()) {
+ return new Point(0, getOffsetBottom(controls));
+ } else {
+ return new Point(getOffsetStart(controls), 0);
+ }
+ }
+
+ private Point getSwitchOnHoldOffsetEndHidden(View swapCallButton) {
+ if (isLandscape()) {
+ return new Point(0, getOffsetTop(swapCallButton));
+ } else {
+ return new Point(getOffsetEnd(swapCallButton), 0);
+ }
+ }
+
+ private Point getContactGridOffsetEndHidden(View view) {
+ return new Point(0, getOffsetTop(view));
+ }
+
+ private Point getEndCallOffsetEndHidden(View endCallButton) {
+ if (isLandscape()) {
+ return new Point(getOffsetEnd(endCallButton), 0);
+ } else {
+ return new Point(0, ((MarginLayoutParams) endCallButton.getLayoutParams()).bottomMargin);
+ }
+ }
+
+ private Point getPreviewOffsetStartShown() {
+ // No insets in multiwindow mode, and rootWindowInsets will get the display's insets.
+ if (ActivityCompat.isInMultiWindowMode(getActivity())) {
+ return new Point();
+ }
+ if (isLandscape()) {
+ int stableInsetEnd =
+ getView().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+ ? getView().getRootWindowInsets().getStableInsetLeft()
+ : -getView().getRootWindowInsets().getStableInsetRight();
+ return new Point(stableInsetEnd, 0);
+ } else {
+ return new Point(0, -getView().getRootWindowInsets().getStableInsetBottom());
+ }
+ }
+
+ private View[] getAllPreviewRelatedViews() {
+ return new View[] {
+ previewTextureView, previewOffOverlay, previewOffBlurredImageView, mutePreviewOverlay,
+ };
+ }
+
+ private int getOffsetTop(View view) {
+ return -(view.getHeight() + ((MarginLayoutParams) view.getLayoutParams()).topMargin);
+ }
+
+ private int getOffsetBottom(View view) {
+ return view.getHeight() + ((MarginLayoutParams) view.getLayoutParams()).bottomMargin;
+ }
+
+ private int getOffsetStart(View view) {
+ int offset = view.getWidth() + ((MarginLayoutParams) view.getLayoutParams()).getMarginStart();
+ if (view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ offset = -offset;
+ }
+ return -offset;
+ }
+
+ private int getOffsetEnd(View view) {
+ int offset = view.getWidth() + ((MarginLayoutParams) view.getLayoutParams()).getMarginEnd();
+ if (view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ offset = -offset;
+ }
+ return offset;
+ }
+
+ private void enterFullscreenMode() {
+ LogUtil.i("VideoCallFragment.enterFullscreenMode", null);
+
+ hideSystemUI();
+
+ Interpolator fastOutLinearInInterpolator = new FastOutLinearInInterpolator();
+
+ // Animate controls to the hidden state.
+ Point offset = getControlsOffsetEndHidden(controls);
+ controls
+ .animate()
+ .translationX(offset.x)
+ .translationY(offset.y)
+ .setInterpolator(fastOutLinearInInterpolator)
+ .alpha(0)
+ .start();
+
+ // Animate onHold to the hidden state.
+ offset = getSwitchOnHoldOffsetEndHidden(switchOnHoldButton);
+ switchOnHoldButton
+ .animate()
+ .translationX(offset.x)
+ .translationY(offset.y)
+ .setInterpolator(fastOutLinearInInterpolator)
+ .alpha(0);
+
+ View contactGridView = contactGridManager.getContainerView();
+ // Animate contact grid to the hidden state.
+ offset = getContactGridOffsetEndHidden(contactGridView);
+ contactGridView
+ .animate()
+ .translationX(offset.x)
+ .translationY(offset.y)
+ .setInterpolator(fastOutLinearInInterpolator)
+ .alpha(0);
+
+ offset = getEndCallOffsetEndHidden(endCallButton);
+ // Use a fast out interpolator to quickly fade out the button. This is important because the
+ // button can't draw under the navigation bar which means that it'll look weird if it just
+ // abruptly disappears when it reaches the edge of the naivgation bar.
+ endCallButton
+ .animate()
+ .translationX(offset.x)
+ .translationY(offset.y)
+ .setInterpolator(fastOutLinearInInterpolator)
+ .alpha(0)
+ .withEndAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ endCallButton.setVisibility(View.INVISIBLE);
+ }
+ })
+ .setInterpolator(new FastOutLinearInInterpolator())
+ .start();
+
+ // Animate all the preview controls down now that the navigation bar is hidden.
+ // In green screen mode we don't need this because the preview takes up the whole screen and has
+ // a fixed position.
+ if (!isInGreenScreenMode) {
+ for (View view : getAllPreviewRelatedViews()) {
+ // Animate down with the navigation bar hidden.
+ view.animate()
+ .translationX(0)
+ .translationY(0)
+ .setInterpolator(new AccelerateDecelerateInterpolator())
+ .start();
+ }
+ }
+ updateOverlayBackground();
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v == endCallButton) {
+ LogUtil.i("VideoCallFragment.onClick", "end call button clicked");
+ inCallButtonUiDelegate.onEndCallClicked();
+ videoCallScreenDelegate.resetAutoFullscreenTimer();
+ } else if (v == swapCameraButton) {
+ if (swapCameraButton.getDrawable() instanceof Animatable) {
+ ((Animatable) swapCameraButton.getDrawable()).start();
+ }
+ inCallButtonUiDelegate.toggleCameraClicked();
+ videoCallScreenDelegate.resetAutoFullscreenTimer();
+ }
+ }
+
+ @Override
+ public void onCheckedChanged(CheckableImageButton button, boolean isChecked) {
+ if (button == cameraOffButton) {
+ if (!isChecked && !VideoUtils.hasCameraPermissionAndAllowedByUser(getContext())) {
+ LogUtil.i("VideoCallFragment.onCheckedChanged", "show camera permission dialog");
+ checkCameraPermission();
+ } else {
+ inCallButtonUiDelegate.pauseVideoClicked(isChecked);
+ videoCallScreenDelegate.resetAutoFullscreenTimer();
+ }
+ } else if (button == muteButton) {
+ inCallButtonUiDelegate.muteClicked(isChecked);
+ videoCallScreenDelegate.resetAutoFullscreenTimer();
+ }
+ }
+
+ @Override
+ public void showVideoViews(
+ boolean shouldShowPreview, boolean shouldShowRemote, boolean isRemotelyHeld) {
+ LogUtil.i(
+ "VideoCallFragment.showVideoViews",
+ "showPreview: %b, shouldShowRemote: %b",
+ shouldShowPreview,
+ shouldShowRemote);
+ this.shouldShowPreview = shouldShowPreview;
+ this.shouldShowRemote = shouldShowRemote;
+ this.isRemotelyHeld = isRemotelyHeld;
+
+ videoCallScreenDelegate.getLocalVideoSurfaceTexture().attachToTextureView(previewTextureView);
+ videoCallScreenDelegate.getRemoteVideoSurfaceTexture().attachToTextureView(remoteTextureView);
+
+ updateVideoOffViews();
+ updateRemoteVideoScaling();
+ }
+
+ /**
+ * This method scales the video feed inside the texture view, it doesn't change the texture view's
+ * size. In the old UI we would change the view size to match the aspect ratio of the video. In
+ * the new UI the view is always square (with the circular clip) so we have to do additional work
+ * to make sure the non-square video doesn't look squished.
+ */
+ @Override
+ public void onLocalVideoDimensionsChanged() {
+ LogUtil.i("VideoCallFragment.onLocalVideoDimensionsChanged", null);
+ updatePreviewVideoScaling();
+ }
+
+ @Override
+ public void onLocalVideoOrientationChanged() {
+ LogUtil.i("VideoCallFragment.onLocalVideoOrientationChanged", null);
+ updatePreviewVideoScaling();
+ }
+
+ /** Called when the remote video's dimensions change. */
+ @Override
+ public void onRemoteVideoDimensionsChanged() {
+ LogUtil.i("VideoCallFragment.onRemoteVideoDimensionsChanged", null);
+ updateRemoteVideoScaling();
+ }
+
+ @Override
+ public void updateFullscreenAndGreenScreenMode(
+ boolean shouldShowFullscreen, boolean shouldShowGreenScreen) {
+ LogUtil.i(
+ "VideoCallFragment.updateFullscreenAndGreenScreenMode",
+ "shouldShowFullscreen: %b, shouldShowGreenScreen: %b",
+ shouldShowFullscreen,
+ shouldShowGreenScreen);
+
+ if (getActivity() == null) {
+ LogUtil.i("VideoCallFragment.updateFullscreenAndGreenScreenMode", "not attached to activity");
+ return;
+ }
+
+ // Check if anything is actually going to change. The first time this function is called we
+ // force a change by checking the hasInitializedScreenModes flag. We also force both fullscreen
+ // and green screen modes to update even if only one has changed. That's because they both
+ // depend on each other.
+ if (hasInitializedScreenModes
+ && shouldShowGreenScreen == isInGreenScreenMode
+ && shouldShowFullscreen == isInFullscreenMode) {
+ LogUtil.i(
+ "VideoCallFragment.updateFullscreenAndGreenScreenMode", "no change to screen modes");
+ return;
+ }
+ hasInitializedScreenModes = true;
+ isInGreenScreenMode = shouldShowGreenScreen;
+ isInFullscreenMode = shouldShowFullscreen;
+
+ if (getView().isAttachedToWindow() && !ActivityCompat.isInMultiWindowMode(getActivity())) {
+ controlsContainer.onApplyWindowInsets(getView().getRootWindowInsets());
+ }
+ if (shouldShowGreenScreen) {
+ enterGreenScreenMode();
+ } else {
+ exitGreenScreenMode();
+ }
+ if (shouldShowFullscreen) {
+ enterFullscreenMode();
+ } else {
+ exitFullscreenMode();
+ }
+ updateVideoOffViews();
+
+ OnHoldFragment onHoldFragment =
+ ((OnHoldFragment)
+ getChildFragmentManager().findFragmentById(R.id.videocall_on_hold_banner));
+ if (onHoldFragment != null) {
+ onHoldFragment.setPadTopInset(!isInFullscreenMode);
+ }
+ }
+
+ @Override
+ public Fragment getVideoCallScreenFragment() {
+ return this;
+ }
+
+ @Override
+ public void showButton(@InCallButtonIds int buttonId, boolean show) {
+ LogUtil.v(
+ "VideoCallFragment.showButton",
+ "buttonId: %s, show: %b",
+ InCallButtonIdsExtension.toString(buttonId),
+ show);
+ if (buttonId == InCallButtonIds.BUTTON_AUDIO) {
+ speakerButtonController.setEnabled(show);
+ } else if (buttonId == InCallButtonIds.BUTTON_MUTE) {
+ muteButton.setEnabled(show);
+ } else if (buttonId == InCallButtonIds.BUTTON_PAUSE_VIDEO) {
+ cameraOffButton.setEnabled(show);
+ } else if (buttonId == InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY) {
+ switchOnHoldCallController.setVisible(show);
+ } else if (buttonId == InCallButtonIds.BUTTON_SWITCH_CAMERA) {
+ swapCameraButton.setEnabled(show);
+ }
+ }
+
+ @Override
+ public void enableButton(@InCallButtonIds int buttonId, boolean enable) {
+ LogUtil.v(
+ "VideoCallFragment.setEnabled",
+ "buttonId: %s, enable: %b",
+ InCallButtonIdsExtension.toString(buttonId),
+ enable);
+ if (buttonId == InCallButtonIds.BUTTON_AUDIO) {
+ speakerButtonController.setEnabled(enable);
+ } else if (buttonId == InCallButtonIds.BUTTON_MUTE) {
+ muteButton.setEnabled(enable);
+ } else if (buttonId == InCallButtonIds.BUTTON_PAUSE_VIDEO) {
+ cameraOffButton.setEnabled(enable);
+ } else if (buttonId == InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY) {
+ switchOnHoldCallController.setEnabled(enable);
+ }
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ LogUtil.v("VideoCallFragment.setEnabled", "enabled: " + enabled);
+ speakerButtonController.setEnabled(enabled);
+ muteButton.setEnabled(enabled);
+ cameraOffButton.setEnabled(enabled);
+ switchOnHoldCallController.setEnabled(enabled);
+ }
+
+ @Override
+ public void setHold(boolean value) {
+ LogUtil.i("VideoCallFragment.setHold", "value: " + value);
+ }
+
+ @Override
+ public void setCameraSwitched(boolean isBackFacingCamera) {
+ LogUtil.i("VideoCallFragment.setCameraSwitched", "isBackFacingCamera: " + isBackFacingCamera);
+ }
+
+ @Override
+ public void setVideoPaused(boolean isPaused) {
+ LogUtil.i("VideoCallFragment.setVideoPaused", "isPaused: " + isPaused);
+ cameraOffButton.setChecked(isPaused);
+ }
+
+ @Override
+ public void setAudioState(CallAudioState audioState) {
+ LogUtil.i("VideoCallFragment.setAudioState", "audioState: " + audioState);
+ speakerButtonController.setAudioState(audioState);
+ muteButton.setChecked(audioState.isMuted());
+ updateMutePreviewOverlayVisibility();
+ }
+
+ @Override
+ public void updateButtonStates() {
+ LogUtil.i("VideoCallFragment.updateButtonState", null);
+ speakerButtonController.updateButtonState();
+ switchOnHoldCallController.updateButtonState();
+ }
+
+ @Override
+ public void updateInCallButtonUiColors() {}
+
+ @Override
+ public Fragment getInCallButtonUiFragment() {
+ return this;
+ }
+
+ @Override
+ public void showAudioRouteSelector() {
+ LogUtil.i("VideoCallFragment.showAudioRouteSelector", null);
+ AudioRouteSelectorDialogFragment.newInstance(inCallButtonUiDelegate.getCurrentAudioState())
+ .show(getChildFragmentManager(), null);
+ }
+
+ @Override
+ public void onAudioRouteSelected(int audioRoute) {
+ LogUtil.i("VideoCallFragment.onAudioRouteSelected", "audioRoute: " + audioRoute);
+ inCallButtonUiDelegate.setAudioRoute(audioRoute);
+ }
+
+ @Override
+ public void setPrimary(@NonNull PrimaryInfo primaryInfo) {
+ LogUtil.i("VideoCallFragment.setPrimary", primaryInfo.toString());
+ contactGridManager.setPrimary(primaryInfo);
+ }
+
+ @Override
+ public void setSecondary(@NonNull SecondaryInfo secondaryInfo) {
+ LogUtil.i("VideoCallFragment.setSecondary", secondaryInfo.toString());
+ if (!isAdded()) {
+ savedSecondaryInfo = secondaryInfo;
+ return;
+ }
+ savedSecondaryInfo = null;
+ switchOnHoldCallController.setSecondaryInfo(secondaryInfo);
+ updateButtonStates();
+ FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
+ Fragment oldBanner = getChildFragmentManager().findFragmentById(R.id.videocall_on_hold_banner);
+ if (secondaryInfo.shouldShow) {
+ OnHoldFragment onHoldFragment = OnHoldFragment.newInstance(secondaryInfo);
+ onHoldFragment.setPadTopInset(!isInFullscreenMode);
+ transaction.replace(R.id.videocall_on_hold_banner, onHoldFragment);
+ } else {
+ if (oldBanner != null) {
+ transaction.remove(oldBanner);
+ }
+ }
+ transaction.setCustomAnimations(R.anim.abc_slide_in_top, R.anim.abc_slide_out_top);
+ transaction.commitAllowingStateLoss();
+ }
+
+ @Override
+ public void setCallState(@NonNull PrimaryCallState primaryCallState) {
+ LogUtil.i("VideoCallFragment.setCallState", primaryCallState.toString());
+ contactGridManager.setCallState(primaryCallState);
+ }
+
+ @Override
+ public void setEndCallButtonEnabled(boolean enabled, boolean animate) {
+ LogUtil.i("VideoCallFragment.setEndCallButtonEnabled", "enabled: " + enabled);
+ }
+
+ @Override
+ public void showManageConferenceCallButton(boolean visible) {
+ LogUtil.i("VideoCallFragment.showManageConferenceCallButton", "visible: " + visible);
+ }
+
+ @Override
+ public boolean isManageConferenceVisible() {
+ LogUtil.i("VideoCallFragment.isManageConferenceVisible", null);
+ return false;
+ }
+
+ @Override
+ public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ contactGridManager.dispatchPopulateAccessibilityEvent(event);
+ }
+
+ @Override
+ public void showNoteSentToast() {
+ LogUtil.i("VideoCallFragment.showNoteSentToast", null);
+ }
+
+ @Override
+ public void updateInCallScreenColors() {
+ LogUtil.i("VideoCallFragment.updateColors", null);
+ }
+
+ @Override
+ public void onInCallScreenDialpadVisibilityChange(boolean isShowing) {
+ LogUtil.i("VideoCallFragment.onInCallScreenDialpadVisibilityChange", null);
+ }
+
+ @Override
+ public int getAnswerAndDialpadContainerResourceId() {
+ return 0;
+ }
+
+ @Override
+ public Fragment getInCallScreenFragment() {
+ return this;
+ }
+
+ @Override
+ public boolean isShowingLocationUi() {
+ return false;
+ }
+
+ @Override
+ public void showLocationUi(Fragment locationUi) {
+ LogUtil.e("VideoCallFragment.showLocationUi", "Emergency video calling not supported");
+ // Do nothing
+ }
+
+ private void updatePreviewVideoScaling() {
+ if (previewTextureView.getWidth() == 0 || previewTextureView.getHeight() == 0) {
+ LogUtil.i("VideoCallFragment.updatePreviewVideoScaling", "view layout hasn't finished yet");
+ return;
+ }
+ VideoSurfaceTexture localVideoSurfaceTexture =
+ videoCallScreenDelegate.getLocalVideoSurfaceTexture();
+ Point cameraDimensions = localVideoSurfaceTexture.getSurfaceDimensions();
+ if (cameraDimensions == null) {
+ LogUtil.i(
+ "VideoCallFragment.updatePreviewVideoScaling", "camera dimensions haven't been set");
+ return;
+ }
+ if (isLandscape()) {
+ VideoSurfaceBindings.scaleVideoAndFillView(
+ previewTextureView,
+ cameraDimensions.x,
+ cameraDimensions.y,
+ videoCallScreenDelegate.getDeviceOrientation());
+ } else {
+ VideoSurfaceBindings.scaleVideoAndFillView(
+ previewTextureView,
+ cameraDimensions.y,
+ cameraDimensions.x,
+ videoCallScreenDelegate.getDeviceOrientation());
+ }
+ }
+
+ private void updateRemoteVideoScaling() {
+ VideoSurfaceTexture remoteVideoSurfaceTexture =
+ videoCallScreenDelegate.getRemoteVideoSurfaceTexture();
+ Point videoSize = remoteVideoSurfaceTexture.getSourceVideoDimensions();
+ if (videoSize == null) {
+ LogUtil.i("VideoCallFragment.updateRemoteVideoScaling", "video size is null");
+ return;
+ }
+ if (remoteTextureView.getWidth() == 0 || remoteTextureView.getHeight() == 0) {
+ LogUtil.i("VideoCallFragment.updateRemoteVideoScaling", "view layout hasn't finished yet");
+ return;
+ }
+
+ // If the video and display aspect ratio's are close then scale video to fill display
+ float videoAspectRatio = ((float) videoSize.x) / videoSize.y;
+ float displayAspectRatio =
+ ((float) remoteTextureView.getWidth()) / remoteTextureView.getHeight();
+ float delta = Math.abs(videoAspectRatio - displayAspectRatio);
+ float sum = videoAspectRatio + displayAspectRatio;
+ if (delta / sum < ASPECT_RATIO_MATCH_THRESHOLD) {
+ VideoSurfaceBindings.scaleVideoAndFillView(remoteTextureView, videoSize.x, videoSize.y, 0);
+ } else {
+ VideoSurfaceBindings.scaleVideoMaintainingAspectRatio(
+ remoteTextureView, videoSize.x, videoSize.y);
+ }
+ }
+
+ private boolean isLandscape() {
+ // Choose orientation based on display orientation, not window orientation
+ int rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();
+ return rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270;
+ }
+
+ private void enterGreenScreenMode() {
+ LogUtil.i("VideoCallFragment.enterGreenScreenMode", null);
+ RelativeLayout.LayoutParams params =
+ new RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
+ params.addRule(RelativeLayout.ALIGN_PARENT_START);
+ params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+ previewTextureView.setLayoutParams(params);
+ previewTextureView.setOutlineProvider(null);
+ updatePreviewVideoScaling();
+ updateOverlayBackground();
+ contactGridManager.setIsMiddleRowVisible(true);
+ updateMutePreviewOverlayVisibility();
+
+ previewOffBlurredImageView.setLayoutParams(params);
+ previewOffBlurredImageView.setOutlineProvider(null);
+ previewOffBlurredImageView.setClipToOutline(false);
+ }
+
+ private void exitGreenScreenMode() {
+ LogUtil.i("VideoCallFragment.exitGreenScreenMode", null);
+ Resources resources = getResources();
+ RelativeLayout.LayoutParams params =
+ new RelativeLayout.LayoutParams(
+ (int) resources.getDimension(R.dimen.videocall_preview_width),
+ (int) resources.getDimension(R.dimen.videocall_preview_height));
+ params.setMargins(
+ 0, 0, 0, (int) resources.getDimension(R.dimen.videocall_preview_margin_bottom));
+ if (isLandscape()) {
+ params.addRule(RelativeLayout.ALIGN_PARENT_END);
+ params.setMarginEnd((int) resources.getDimension(R.dimen.videocall_preview_margin_end));
+ } else {
+ params.addRule(RelativeLayout.ALIGN_PARENT_START);
+ params.setMarginStart((int) resources.getDimension(R.dimen.videocall_preview_margin_start));
+ }
+ params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
+ previewTextureView.setLayoutParams(params);
+ previewTextureView.setOutlineProvider(circleOutlineProvider);
+ updatePreviewVideoScaling();
+ updateOverlayBackground();
+ contactGridManager.setIsMiddleRowVisible(false);
+ updateMutePreviewOverlayVisibility();
+
+ previewOffBlurredImageView.setLayoutParams(params);
+ previewOffBlurredImageView.setOutlineProvider(circleOutlineProvider);
+ previewOffBlurredImageView.setClipToOutline(true);
+ }
+
+ private void updateVideoOffViews() {
+ // Always hide the preview off and remote off views in green screen mode.
+ boolean previewEnabled = isInGreenScreenMode || shouldShowPreview;
+ previewOffOverlay.setVisibility(previewEnabled ? View.GONE : View.VISIBLE);
+ updateBlurredImageView(
+ previewTextureView,
+ previewOffBlurredImageView,
+ shouldShowPreview,
+ BLUR_PREVIEW_RADIUS,
+ BLUR_PREVIEW_SCALE_FACTOR);
+
+ boolean remoteEnabled = isInGreenScreenMode || shouldShowRemote;
+ boolean isResumed = remoteEnabled && !isRemotelyHeld;
+ if (isResumed) {
+ boolean wasRemoteVideoOff =
+ TextUtils.equals(
+ remoteVideoOff.getText(),
+ remoteVideoOff.getResources().getString(R.string.videocall_remote_video_off));
+ // The text needs to be updated and hidden after enough delay in order to be announced by
+ // talkback.
+ remoteVideoOff.setText(
+ wasRemoteVideoOff
+ ? R.string.videocall_remote_video_on
+ : R.string.videocall_remotely_resumed);
+ remoteVideoOff.postDelayed(
+ new Runnable() {
+ @Override
+ public void run() {
+ remoteVideoOff.setVisibility(View.GONE);
+ }
+ },
+ VIDEO_OFF_VIEW_FADE_OUT_DELAY_IN_MILLIS);
+ } else {
+ remoteVideoOff.setText(
+ isRemotelyHeld ? R.string.videocall_remotely_held : R.string.videocall_remote_video_off);
+ remoteVideoOff.setVisibility(View.VISIBLE);
+ }
+ LogUtil.i("VideoCallFragment.updateVideoOffViews", "calling updateBlurredImageView");
+ updateBlurredImageView(
+ remoteTextureView,
+ remoteOffBlurredImageView,
+ shouldShowRemote,
+ BLUR_REMOTE_RADIUS,
+ BLUR_REMOTE_SCALE_FACTOR);
+ }
+
+ private void updateBlurredImageView(
+ TextureView textureView,
+ ImageView blurredImageView,
+ boolean isVideoEnabled,
+ float blurRadius,
+ float scaleFactor) {
+ boolean didBlur = false;
+ long startTimeMillis = SystemClock.elapsedRealtime();
+ if (!isVideoEnabled) {
+ int width = Math.round(textureView.getWidth() * scaleFactor);
+ int height = Math.round(textureView.getHeight() * scaleFactor);
+ // This call takes less than 10 milliseconds.
+ Bitmap bitmap = textureView.getBitmap(width, height);
+ if (bitmap != null) {
+ // TODO: When the view is first displayed after a rotation the bitmap is empty
+ // and thus this blur has no effect.
+ // This call can take 100 milliseconds.
+ blur(getContext(), bitmap, blurRadius);
+
+ // TODO: Figure out why only have to apply the transform in landscape mode
+ if (width > height) {
+ bitmap =
+ Bitmap.createBitmap(
+ bitmap,
+ 0,
+ 0,
+ bitmap.getWidth(),
+ bitmap.getHeight(),
+ textureView.getTransform(null),
+ true);
+ }
+
+ blurredImageView.setImageBitmap(bitmap);
+ blurredImageView.setVisibility(View.VISIBLE);
+ didBlur = true;
+ }
+ }
+ if (!didBlur) {
+ blurredImageView.setImageBitmap(null);
+ blurredImageView.setVisibility(View.GONE);
+ }
+
+ LogUtil.i(
+ "VideoCallFragment.updateBlurredImageView",
+ "didBlur: %b, took %d millis",
+ didBlur,
+ (SystemClock.elapsedRealtime() - startTimeMillis));
+ }
+
+ private void updateOverlayBackground() {
+ if (isInGreenScreenMode) {
+ // We want to darken the preview view to make text and buttons readable. The fullscreen
+ // background is below the preview view so use the green screen background instead.
+ animateSetVisibility(greenScreenBackgroundView, View.VISIBLE);
+ animateSetVisibility(fullscreenBackgroundView, View.GONE);
+ } else if (!isInFullscreenMode) {
+ // We want to darken the remote view to make text and buttons readable. The green screen
+ // background is above the preview view so it would darken the preview too. Use the fullscreen
+ // background instead.
+ animateSetVisibility(greenScreenBackgroundView, View.GONE);
+ animateSetVisibility(fullscreenBackgroundView, View.VISIBLE);
+ } else {
+ animateSetVisibility(greenScreenBackgroundView, View.GONE);
+ animateSetVisibility(fullscreenBackgroundView, View.GONE);
+ }
+ }
+
+ private void updateMutePreviewOverlayVisibility() {
+ // Normally the mute overlay shows on the bottom right of the preview bubble. In green screen
+ // mode the preview is fullscreen so there's no where to anchor it.
+ mutePreviewOverlay.setVisibility(
+ muteButton.isChecked() && !isInGreenScreenMode ? View.VISIBLE : View.GONE);
+ }
+
+ private static void animateSetVisibility(final View view, final int visibility) {
+ if (view.getVisibility() == visibility) {
+ return;
+ }
+
+ int startAlpha;
+ int endAlpha;
+ if (visibility == View.GONE) {
+ startAlpha = 1;
+ endAlpha = 0;
+ } else if (visibility == View.VISIBLE) {
+ startAlpha = 0;
+ endAlpha = 1;
+ } else {
+ Assert.fail();
+ return;
+ }
+
+ view.setAlpha(startAlpha);
+ view.setVisibility(View.VISIBLE);
+ view.animate()
+ .alpha(endAlpha)
+ .withEndAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ view.setVisibility(visibility);
+ }
+ })
+ .start();
+ }
+
+ private static void blur(Context context, Bitmap image, float blurRadius) {
+ RenderScript renderScript = RenderScript.create(context);
+ ScriptIntrinsicBlur blurScript =
+ ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
+ Allocation allocationIn = Allocation.createFromBitmap(renderScript, image);
+ Allocation allocationOut = Allocation.createFromBitmap(renderScript, image);
+ blurScript.setRadius(blurRadius);
+ blurScript.setInput(allocationIn);
+ blurScript.forEach(allocationOut);
+ allocationOut.copyTo(image);
+ }
+
+ @Override
+ public void onSystemUiVisibilityChange(int visibility) {
+ boolean navBarVisible = (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
+ videoCallScreenDelegate.onSystemUiVisibilityChange(navBarVisible);
+ }
+
+ protected void onCameraPermissionGranted() {
+ videoCallScreenDelegate.onCameraPermissionGranted();
+ }
+
+ private void checkCameraPermission() {
+ // Checks if user has consent of camera permission and the permission is granted.
+ // If camera permission is revoked, shows system permission dialog.
+ // If camera permission is granted but user doesn't have consent of camera permission
+ // (which means it's first time making video call), shows custom dialog instead. This
+ // will only be shown to user once.
+ if (!VideoUtils.hasCameraPermissionAndAllowedByUser(getContext())) {
+ videoCallScreenDelegate.onCameraPermissionDialogShown();
+ if (!VideoUtils.hasCameraPermission(getContext())) {
+ requestPermissions(new String[] {permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE);
+ } else {
+ CameraPermissionDialogFragment.newInstance()
+ .show(getChildFragmentManager(), CAMERA_PERMISSION_DIALOG_FRAMENT_TAG);
+ }
+ }
+ }
+}
diff --git a/java/com/android/incallui/video/impl/res/color/videocall_button_icon_tint.xml b/java/com/android/incallui/video/impl/res/color/videocall_button_icon_tint.xml
new file mode 100644
index 000000000..b46607b1b
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/color/videocall_button_icon_tint.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#ff000000" android:state_checked="true"/>
+ <item android:color="#ffffffff"/>
+</selector>
diff --git a/java/com/android/incallui/video/impl/res/drawable-hdpi/ic_switch_camera.png b/java/com/android/incallui/video/impl/res/drawable-hdpi/ic_switch_camera.png
new file mode 100644
index 000000000..b5c6f0a87
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-hdpi/ic_switch_camera.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_checked.png b/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_checked.png
new file mode 100644
index 000000000..2ab2f21a7
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_checked.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_checked_disabled.png b/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_checked_disabled.png
new file mode 100644
index 000000000..2deaadd76
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_checked_disabled.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_checked_pressed.png b/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_checked_pressed.png
new file mode 100644
index 000000000..c4147fa62
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_checked_pressed.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_default.png b/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_default.png
new file mode 100644
index 000000000..c59e21504
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_default.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_disabled.png b/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_disabled.png
new file mode 100644
index 000000000..95d6824f5
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_disabled.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_pressed.png b/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_pressed.png
new file mode 100644
index 000000000..9a525a374
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-hdpi/video_button_bg_pressed.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-mdpi/ic_switch_camera.png b/java/com/android/incallui/video/impl/res/drawable-mdpi/ic_switch_camera.png
new file mode 100644
index 000000000..f3427a02e
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-mdpi/ic_switch_camera.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_checked.png b/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_checked.png
new file mode 100644
index 000000000..c3ff7b2bb
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_checked.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_checked_disabled.png b/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_checked_disabled.png
new file mode 100644
index 000000000..c75281332
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_checked_disabled.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_checked_pressed.png b/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_checked_pressed.png
new file mode 100644
index 000000000..fd16baef7
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_checked_pressed.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_default.png b/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_default.png
new file mode 100644
index 000000000..3fe2446e3
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_default.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_disabled.png b/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_disabled.png
new file mode 100644
index 000000000..1ff3e7c25
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_disabled.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_pressed.png b/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_pressed.png
new file mode 100644
index 000000000..aa7289af1
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-mdpi/video_button_bg_pressed.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-xhdpi/ic_switch_camera.png b/java/com/android/incallui/video/impl/res/drawable-xhdpi/ic_switch_camera.png
new file mode 100644
index 000000000..491547189
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-xhdpi/ic_switch_camera.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_checked.png b/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_checked.png
new file mode 100644
index 000000000..799a78ebb
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_checked.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_checked_disabled.png b/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_checked_disabled.png
new file mode 100644
index 000000000..4d5e03320
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_checked_disabled.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_checked_pressed.png b/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_checked_pressed.png
new file mode 100644
index 000000000..62cd1a477
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_checked_pressed.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_default.png b/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_default.png
new file mode 100644
index 000000000..c68ad909a
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_default.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_disabled.png b/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_disabled.png
new file mode 100644
index 000000000..e5c3fc48d
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_disabled.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_pressed.png b/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_pressed.png
new file mode 100644
index 000000000..583c3de82
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-xhdpi/video_button_bg_pressed.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-xxhdpi/ic_switch_camera.png b/java/com/android/incallui/video/impl/res/drawable-xxhdpi/ic_switch_camera.png
new file mode 100644
index 000000000..19a9344e9
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-xxhdpi/ic_switch_camera.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_checked.png b/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_checked.png
new file mode 100644
index 000000000..5a7702bbc
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_checked.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_checked_disabled.png b/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_checked_disabled.png
new file mode 100644
index 000000000..a0be8d17d
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_checked_disabled.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_checked_pressed.png b/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_checked_pressed.png
new file mode 100644
index 000000000..5671bfa06
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_checked_pressed.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_default.png b/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_default.png
new file mode 100644
index 000000000..527b3c47e
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_default.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_disabled.png b/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_disabled.png
new file mode 100644
index 000000000..996185890
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_disabled.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_pressed.png b/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_pressed.png
new file mode 100644
index 000000000..56295b10f
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-xxhdpi/video_button_bg_pressed.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable-xxxhdpi/ic_switch_camera.png b/java/com/android/incallui/video/impl/res/drawable-xxxhdpi/ic_switch_camera.png
new file mode 100644
index 000000000..529c0a4d5
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable-xxxhdpi/ic_switch_camera.png
Binary files differ
diff --git a/java/com/android/incallui/video/impl/res/drawable/videocall_background_circle_white.xml b/java/com/android/incallui/video/impl/res/drawable/videocall_background_circle_white.xml
new file mode 100644
index 000000000..ee514c776
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable/videocall_background_circle_white.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#80888888">
+ <item>
+ <shape
+ android:shape="oval">
+ <solid android:color="@color/incall_button_white"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/java/com/android/incallui/video/impl/res/drawable/videocall_video_button_background.xml b/java/com/android/incallui/video/impl/res/drawable/videocall_video_button_background.xml
new file mode 100644
index 000000000..5e4841327
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/drawable/videocall_video_button_background.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/incall_button_ripple">
+ <item android:id="@android:id/mask">
+ <inset android:inset="5dp">
+ <shape android:shape="oval">
+ <solid android:color="@android:color/white"/>
+ </shape>
+ </inset>
+ </item>
+ <item>
+ <selector>
+ <item
+ android:drawable="@drawable/video_button_bg_checked_pressed"
+ android:state_checked="true"
+ android:state_pressed="true"/>
+ <item
+ android:drawable="@drawable/video_button_bg_checked"
+ android:state_checked="true"/>
+ <item
+ android:drawable="@drawable/video_button_bg_pressed"
+ android:state_pressed="true"/>
+ <item
+ android:drawable="@drawable/video_button_bg_default"/>
+ </selector>
+ </item>
+</ripple>
diff --git a/java/com/android/incallui/video/impl/res/layout-v21/switch_camera_button.xml b/java/com/android/incallui/video/impl/res/layout-v21/switch_camera_button.xml
new file mode 100644
index 000000000..1fb1bb088
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/layout-v21/switch_camera_button.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/videocall_switch_video"
+ style="@style/Incall.Button.VideoCall"
+ android:contentDescription="@string/incall_content_description_swap_video"
+ android:src="@drawable/front_back_switch_button_animation"/>
diff --git a/java/com/android/incallui/video/impl/res/layout/frag_videocall.xml b/java/com/android/incallui/video/impl/res/layout/frag_videocall.xml
new file mode 100644
index 000000000..dc663dda1
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/layout/frag_videocall.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/black"
+ android:orientation="vertical">
+
+ <TextureView
+ android:id="@+id/videocall_video_remote"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:importantForAccessibility="no"/>
+
+ <ImageView
+ android:id="@+id/videocall_remote_off_blurred_image_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:scaleType="fitCenter"/>
+
+ <TextView
+ android:gravity="center"
+ android:id="@+id/videocall_remote_video_off"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:accessibilityTraversalBefore="@+id/videocall_speaker_button"
+ android:drawablePadding="8dp"
+ android:drawableTop="@drawable/quantum_ic_videocam_off_white_36"
+ android:padding="64dp"
+ android:text="@string/videocall_remote_video_off"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance"
+ android:visibility="gone"
+ tools:visibility="visible"/>
+
+ <View
+ android:id="@+id/videocall_fullscreen_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentStart="true"
+ android:background="@color/videocall_overlay_background_color"/>
+
+ <TextureView
+ android:id="@+id/videocall_video_preview"
+ android:layout_width="@dimen/videocall_preview_width"
+ android:layout_height="@dimen/videocall_preview_height"
+ android:layout_marginBottom="@dimen/videocall_preview_margin_bottom"
+ android:layout_marginStart="@dimen/videocall_preview_margin_start"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentStart="true"
+ android:importantForAccessibility="no"/>
+
+ <ImageView
+ android:id="@+id/videocall_preview_off_blurred_image_view"
+ android:layout_width="@dimen/videocall_preview_width"
+ android:layout_height="@dimen/videocall_preview_height"
+ android:layout_marginBottom="@dimen/videocall_preview_margin_bottom"
+ android:layout_marginStart="@dimen/videocall_preview_margin_start"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentStart="true"
+ android:scaleType="center"/>
+
+ <View
+ android:id="@+id/videocall_green_screen_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentStart="true"
+ android:background="@color/videocall_overlay_background_color"/>
+
+ <ImageView
+ android:id="@+id/videocall_video_preview_off_overlay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/videocall_video_preview"
+ android:layout_alignLeft="@+id/videocall_video_preview"
+ android:layout_alignRight="@+id/videocall_video_preview"
+ android:layout_alignTop="@+id/videocall_video_preview"
+ android:scaleType="center"
+ android:src="@drawable/quantum_ic_videocam_off_white_36"
+ android:visibility="gone"
+ android:importantForAccessibility="no"
+ tools:visibility="visible"/>
+
+ <ImageView
+ android:id="@+id/videocall_video_preview_mute_overlay"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_alignBottom="@+id/videocall_video_preview"
+ android:layout_alignRight="@+id/videocall_video_preview"
+ android:background="@drawable/videocall_background_circle_white"
+ android:contentDescription="@string/incall_content_description_muted"
+ android:scaleType="center"
+ android:src="@drawable/quantum_ic_mic_off_black_24"
+ android:visibility="gone"
+ tools:visibility="visible"/>
+
+ <include
+ layout="@layout/videocall_controls"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <FrameLayout
+ android:id="@+id/videocall_on_hold_banner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"/>
+
+</RelativeLayout>
diff --git a/java/com/android/incallui/video/impl/res/layout/frag_videocall_land.xml b/java/com/android/incallui/video/impl/res/layout/frag_videocall_land.xml
new file mode 100644
index 000000000..2353deea1
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/layout/frag_videocall_land.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/black">
+
+ <TextureView
+ android:id="@+id/videocall_video_remote"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:importantForAccessibility="no"/>
+
+ <ImageView
+ android:id="@+id/videocall_remote_off_blurred_image_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:scaleType="fitCenter"/>
+
+ <TextView
+ android:gravity="center"
+ android:id="@+id/videocall_remote_video_off"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:accessibilityTraversalBefore="@+id/videocall_speaker_button"
+ android:drawablePadding="8dp"
+ android:drawableTop="@drawable/quantum_ic_videocam_off_white_36"
+ android:padding="64dp"
+ android:text="@string/videocall_remote_video_off"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance"
+ android:visibility="gone"
+ tools:visibility="visible"/>
+
+ <View
+ android:id="@+id/videocall_fullscreen_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentStart="true"
+ android:background="@color/videocall_overlay_background_color"/>
+
+ <TextureView
+ android:id="@+id/videocall_video_preview"
+ android:layout_width="@dimen/videocall_preview_width"
+ android:layout_height="@dimen/videocall_preview_height"
+ android:layout_marginEnd="@dimen/videocall_preview_margin_end"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true"
+ android:importantForAccessibility="no"/>
+
+ <ImageView
+ android:id="@+id/videocall_preview_off_blurred_image_view"
+ android:layout_width="@dimen/videocall_preview_width"
+ android:layout_height="@dimen/videocall_preview_height"
+ android:layout_marginEnd="@dimen/videocall_preview_margin_end"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true"
+ android:scaleType="center"/>
+
+ <View
+ android:id="@+id/videocall_green_screen_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentStart="true"
+ android:background="@color/videocall_overlay_background_color"/>
+
+ <ImageView
+ android:id="@+id/videocall_video_preview_off_overlay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/videocall_video_preview"
+ android:layout_alignLeft="@+id/videocall_video_preview"
+ android:layout_alignRight="@+id/videocall_video_preview"
+ android:layout_alignTop="@+id/videocall_video_preview"
+ android:scaleType="center"
+ android:src="@drawable/quantum_ic_videocam_off_white_36"
+ android:visibility="gone"
+ android:importantForAccessibility="no"
+ tools:visibility="visible"/>
+
+ <ImageView
+ android:id="@+id/videocall_video_preview_mute_overlay"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_alignBottom="@+id/videocall_video_preview"
+ android:layout_alignRight="@+id/videocall_video_preview"
+ android:background="@drawable/videocall_background_circle_white"
+ android:contentDescription="@string/incall_content_description_muted"
+ android:scaleType="center"
+ android:src="@drawable/quantum_ic_mic_off_black_24"
+ android:visibility="gone"
+ tools:visibility="visible"/>
+
+ <include
+ layout="@layout/videocall_controls_land"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <FrameLayout
+ android:id="@+id/videocall_on_hold_banner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"/>
+
+</RelativeLayout>
diff --git a/java/com/android/incallui/video/impl/res/layout/switch_camera_button.xml b/java/com/android/incallui/video/impl/res/layout/switch_camera_button.xml
new file mode 100644
index 000000000..87c2e1b6c
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/layout/switch_camera_button.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/videocall_switch_video"
+ style="@style/Incall.Button.VideoCall"
+ android:contentDescription="@string/incall_content_description_swap_video"
+ android:src="@drawable/ic_switch_camera"/>
diff --git a/java/com/android/incallui/video/impl/res/layout/video_contact_grid.xml b/java/com/android/incallui/video/impl/res/layout/video_contact_grid.xml
new file mode 100644
index 000000000..ad984f36e
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/layout/video_contact_grid.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:paddingTop="16dp"
+ android:orientation="vertical">
+
+ <include
+ layout="@layout/incall_contactgrid_top_row"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <!-- We have to keep deprecated singleLine to allow long text being truncated with ellipses.
+ b/31396406 -->
+ <com.android.incallui.autoresizetext.AutoResizeTextView
+ android:id="@id/contactgrid_contact_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="@style/Dialer.Incall.TextAppearance.Large"
+ app:autoResizeText_minTextSize="28sp"
+ tools:text="Jake Peralta"
+ tools:ignore="Deprecated"/>
+
+ <include
+ layout="@layout/incall_contactgrid_bottom_row"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+</LinearLayout>
diff --git a/java/com/android/incallui/video/impl/res/layout/videocall_controls.xml b/java/com/android/incallui/video/impl/res/layout/videocall_controls.xml
new file mode 100644
index 000000000..b3141bdf3
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/layout/videocall_controls.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/videocall_video_controls_container"
+ android:fitsSystemWindows="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <include
+ android:id="@+id/incall_contact_grid"
+ layout="@layout/video_contact_grid"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginStart="24dp"
+ android:layout_marginEnd="24dp"/>
+
+ <!-- This placeholder matches the position of the preview UI and is used to
+ anchor video buttons. This is needed in greenscreen mode when the
+ preview is fullscreen but we want the controls to be positioned as
+ normal. -->
+ <Space
+ android:id="@+id/videocall_video_preview_placeholder"
+ android:layout_width="@dimen/videocall_preview_width"
+ android:layout_height="@dimen/videocall_preview_height"
+ android:layout_marginBottom="@dimen/videocall_preview_margin_bottom"
+ android:layout_marginStart="@dimen/videocall_preview_margin_start"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentStart="true"
+ android:visibility="invisible"/>
+
+ <LinearLayout
+ android:id="@+id/videocall_video_controls"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/videocall_video_preview_placeholder"
+ android:layout_alignEnd="@+id/videocall_video_preview_placeholder"
+ android:layout_alignStart="@+id/videocall_video_preview_placeholder"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:visibility="invisible"
+ tools:visibility="visible">
+ <com.android.incallui.video.impl.CheckableImageButton
+ android:id="@+id/videocall_speaker_button"
+ style="@style/Incall.Button.VideoCall"
+ android:layout_width="@dimen/videocall_button_size"
+ android:layout_height="@dimen/videocall_button_size"
+ android:layout_marginBottom="@dimen/videocall_button_spacing"
+ android:checked="true"
+ android:src="@drawable/quantum_ic_volume_up_white_36"
+ app:contentDescriptionChecked="@string/incall_content_description_speaker"
+ app:contentDescriptionUnchecked="@string/incall_content_description_earpiece"
+ />
+ <com.android.incallui.video.impl.CheckableImageButton
+ android:id="@+id/videocall_mute_button"
+ style="@style/Incall.Button.VideoCall"
+ android:layout_width="@dimen/videocall_button_size"
+ android:layout_height="@dimen/videocall_button_size"
+ android:layout_marginBottom="@dimen/videocall_button_spacing"
+ android:src="@drawable/quantum_ic_mic_off_white_36"
+ app:contentDescriptionChecked="@string/incall_content_description_muted"
+ app:contentDescriptionUnchecked="@string/incall_content_description_unmuted"
+ />
+ <com.android.incallui.video.impl.CheckableImageButton
+ android:id="@+id/videocall_mute_video"
+ style="@style/Incall.Button.VideoCall"
+ android:layout_width="@dimen/videocall_button_size"
+ android:layout_height="@dimen/videocall_button_size"
+ android:layout_marginBottom="@dimen/videocall_button_spacing"
+ android:src="@drawable/quantum_ic_videocam_off_white_36"
+ app:contentDescriptionChecked="@string/incall_content_description_video_off"
+ app:contentDescriptionUnchecked="@string/incall_content_description_video_on"
+ />
+ <include
+ layout="@layout/switch_camera_button"
+ android:layout_width="@dimen/videocall_button_size"
+ android:layout_height="@dimen/videocall_button_size"
+ android:layout_marginBottom="@dimen/videocall_button_spacing"/>
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/videocall_switch_controls"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="36dp"
+ android:layout_marginEnd="24dp"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true">
+ <ImageButton
+ android:id="@+id/videocall_switch_on_hold"
+ style="@style/Incall.Button.VideoCall"
+ android:layout_width="@dimen/videocall_button_size"
+ android:layout_height="@dimen/videocall_button_size"
+ android:contentDescription="@string/incall_content_description_swap_calls"
+ android:src="@drawable/quantum_ic_swap_calls_white_36"
+ android:visibility="gone"
+ tools:visibility="visible"
+ />
+ </FrameLayout>
+
+ <ImageButton
+ android:id="@+id/videocall_end_call"
+ style="@style/Incall.Button.End"
+ android:layout_marginBottom="36dp"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:contentDescription="@string/incall_content_description_end_call"
+ android:visibility="visible"/>
+
+</RelativeLayout>
diff --git a/java/com/android/incallui/video/impl/res/layout/videocall_controls_land.xml b/java/com/android/incallui/video/impl/res/layout/videocall_controls_land.xml
new file mode 100644
index 000000000..d71b3c00e
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/layout/videocall_controls_land.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/videocall_video_controls_container"
+ android:fitsSystemWindows="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <include
+ android:id="@+id/incall_contact_grid"
+ layout="@layout/video_contact_grid"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginStart="24dp"
+ android:layout_marginEnd="24dp"/>
+
+ <!-- This placeholder matches the position of the preview UI and is used to
+ anchor video buttons. This is needed in greenscreen mode when the
+ preview is fullscreen but we want the controls to be positioned as
+ normal. -->
+ <Space
+ android:id="@+id/videocall_video_preview_placeholder"
+ android:layout_width="@dimen/videocall_preview_width"
+ android:layout_height="@dimen/videocall_preview_height"
+ android:layout_marginEnd="@dimen/videocall_preview_margin_end"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true"
+ android:visibility="invisible"/>
+
+ <LinearLayout
+ android:id="@+id/videocall_video_controls"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/videocall_video_preview_placeholder"
+ android:layout_alignTop="@+id/videocall_video_preview_placeholder"
+ android:layout_toStartOf="@+id/videocall_video_preview_placeholder"
+ android:gravity="center_horizontal"
+ android:orientation="horizontal"
+ android:visibility="invisible"
+ tools:visibility="visible">
+ <com.android.incallui.video.impl.CheckableImageButton
+ android:id="@+id/videocall_speaker_button"
+ style="@style/Incall.Button.VideoCall"
+ android:layout_width="@dimen/videocall_button_size"
+ android:layout_height="@dimen/videocall_button_size"
+ android:layout_marginEnd="24dp"
+ android:checked="true"
+ android:src="@drawable/quantum_ic_volume_up_white_36"
+ app:contentDescriptionChecked="@string/incall_content_description_speaker"
+ app:contentDescriptionUnchecked="@string/incall_content_description_earpiece"
+ />
+ <com.android.incallui.video.impl.CheckableImageButton
+ android:id="@+id/videocall_mute_button"
+ style="@style/Incall.Button.VideoCall"
+ android:layout_width="@dimen/videocall_button_size"
+ android:layout_height="@dimen/videocall_button_size"
+ android:layout_marginEnd="24dp"
+ android:scaleType="center"
+ android:src="@drawable/quantum_ic_mic_off_white_36"
+ app:contentDescriptionChecked="@string/incall_content_description_muted"
+ app:contentDescriptionUnchecked="@string/incall_content_description_unmuted"
+ />
+ <com.android.incallui.video.impl.CheckableImageButton
+ android:id="@+id/videocall_mute_video"
+ style="@style/Incall.Button.VideoCall"
+ android:layout_width="@dimen/videocall_button_size"
+ android:layout_height="@dimen/videocall_button_size"
+ android:layout_marginEnd="24dp"
+ android:scaleType="center"
+ android:src="@drawable/quantum_ic_videocam_off_white_36"
+ app:contentDescriptionChecked="@string/incall_content_description_video_off"
+ app:contentDescriptionUnchecked="@string/incall_content_description_video_on"
+ />
+ <include
+ layout="@layout/switch_camera_button"
+ android:layout_width="@dimen/videocall_button_size"
+ android:layout_height="@dimen/videocall_button_size"
+ android:layout_marginEnd="24dp"/>
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/videocall_switch_controls"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="36dp"
+ android:layout_marginEnd="36dp"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentTop="true">
+ <ImageButton
+ android:id="@+id/videocall_switch_on_hold"
+ style="@style/Incall.Button.VideoCall"
+ android:layout_width="@dimen/videocall_button_size"
+ android:layout_height="@dimen/videocall_button_size"
+ android:contentDescription="@string/incall_content_description_swap_calls"
+ android:src="@drawable/quantum_ic_swap_calls_white_36"
+ android:visibility="gone"
+ tools:visibility="visible"
+ />
+ </FrameLayout>
+
+ <ImageButton
+ android:id="@+id/videocall_end_call"
+ style="@style/Incall.Button.End"
+ android:layout_marginEnd="36dp"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:contentDescription="@string/incall_content_description_end_call"
+ android:visibility="visible"
+ tools:visibility="visible"/>
+
+</RelativeLayout>
diff --git a/java/com/android/incallui/video/impl/res/values-h580dp/dimens.xml b/java/com/android/incallui/video/impl/res/values-h580dp/dimens.xml
new file mode 100644
index 000000000..b1a86a0fa
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/values-h580dp/dimens.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="videocall_button_spacing">16dp</dimen>
+ <dimen name="videocall_button_size">72dp</dimen>
+ <dimen name="videocall_preview_width">88dp</dimen>
+ <dimen name="videocall_preview_height">88dp</dimen>
+</resources>
diff --git a/java/com/android/incallui/video/impl/res/values-w460dp/dimens.xml b/java/com/android/incallui/video/impl/res/values-w460dp/dimens.xml
new file mode 100644
index 000000000..b1a86a0fa
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/values-w460dp/dimens.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="videocall_button_spacing">16dp</dimen>
+ <dimen name="videocall_button_size">72dp</dimen>
+ <dimen name="videocall_preview_width">88dp</dimen>
+ <dimen name="videocall_preview_height">88dp</dimen>
+</resources>
diff --git a/java/com/android/incallui/video/impl/res/values/attrs.xml b/java/com/android/incallui/video/impl/res/values/attrs.xml
new file mode 100644
index 000000000..e4cd8af89
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/values/attrs.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <declare-styleable name="CheckableImageButton">
+ <attr name="android:checked"/>
+ <attr name="contentDescriptionChecked" format="reference|string"/>
+ <attr name="contentDescriptionUnchecked" format="reference|string"/>
+ </declare-styleable>
+</resources>
diff --git a/java/com/android/incallui/video/impl/res/values/dimens.xml b/java/com/android/incallui/video/impl/res/values/dimens.xml
new file mode 100644
index 000000000..45860036f
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/values/dimens.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="videocall_preview_width">72dp</dimen>
+ <dimen name="videocall_preview_height">72dp</dimen>
+ <dimen name="videocall_preview_margin_bottom">24dp</dimen>
+ <dimen name="videocall_preview_margin_start">24dp</dimen>
+ <dimen name="videocall_preview_margin_end">24dp</dimen>
+ <dimen name="videocall_button_spacing">8dp</dimen>
+ <dimen name="videocall_button_size">60dp</dimen>
+</resources>
diff --git a/java/com/android/incallui/video/impl/res/values/strings.xml b/java/com/android/incallui/video/impl/res/values/strings.xml
new file mode 100644
index 000000000..2b72b8004
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/values/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <!-- Text indicates the video from remote party is off. [CHAR LIMIT=40] -->
+ <string name="videocall_remote_video_off">Their video is off</string>
+
+ <!-- Text indicates the video from remote party is on. [CHAR LIMIT=40] -->
+ <string name="videocall_remote_video_on">Their video is on</string>
+
+ <!-- Text indicates the call is held by remote party. [CHAR LIMIT=20] -->
+ <string name="videocall_remotely_held">Call on hold</string>
+
+ <!-- Text indicates the call is resumed from held by remote party. [CHAR LIMIT=20] -->
+ <string name="videocall_remotely_resumed">Call resumed</string>
+
+ <!-- Title of dialog to ask user for camera permission. [CHAR LIMIT=30] -->
+ <string name="camera_permission_dialog_title">Allow video?</string>
+
+ <!-- Message of dialog to ask user for camera permission. [CHAR LIMIT=100] -->
+ <string name="camera_permission_dialog_message">The Phone app wants to use your camera for video calls.</string>
+
+ <!-- Text of button to be confirmed for camera permission by user. [CHAR LIMIT=20] -->
+ <string name="camera_permission_dialog_positive_button">Allow</string>
+
+ <!-- Text of button to be declined for camera permission by user. [CHAR LIMIT=20] -->
+ <string name="camera_permission_dialog_negative_button">Deny</string>
+
+</resources>
diff --git a/java/com/android/incallui/video/impl/res/values/styles.xml b/java/com/android/incallui/video/impl/res/values/styles.xml
new file mode 100644
index 000000000..b94400875
--- /dev/null
+++ b/java/com/android/incallui/video/impl/res/values/styles.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <style name="Incall.Button.VideoCall" parent="Widget.AppCompat.ImageButton">
+ <item name="android:background">@drawable/videocall_video_button_background</item>
+ <item name="android:scaleType">center</item>
+ <item name="android:tint">@color/videocall_button_icon_tint</item>
+ <item name="android:tintMode">src_atop</item>
+ <item name="android:stateListAnimator">@animator/disabled_alpha</item>
+ </style>
+</resources>
diff --git a/java/com/android/incallui/video/protocol/VideoCallScreen.java b/java/com/android/incallui/video/protocol/VideoCallScreen.java
new file mode 100644
index 000000000..0eaf692e2
--- /dev/null
+++ b/java/com/android/incallui/video/protocol/VideoCallScreen.java
@@ -0,0 +1,36 @@
+/*
+ * 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.incallui.video.protocol;
+
+import android.support.v4.app.Fragment;
+
+/** Interface for call video call module. */
+public interface VideoCallScreen {
+
+ void showVideoViews(boolean shouldShowPreview, boolean shouldShowRemote, boolean isRemotelyHeld);
+
+ void onLocalVideoDimensionsChanged();
+
+ void onLocalVideoOrientationChanged();
+
+ void onRemoteVideoDimensionsChanged();
+
+ void updateFullscreenAndGreenScreenMode(
+ boolean shouldShowFullscreen, boolean shouldShowGreenScreen);
+
+ Fragment getVideoCallScreenFragment();
+}
diff --git a/java/com/android/incallui/video/protocol/VideoCallScreenDelegate.java b/java/com/android/incallui/video/protocol/VideoCallScreenDelegate.java
new file mode 100644
index 000000000..bbd86ee6a
--- /dev/null
+++ b/java/com/android/incallui/video/protocol/VideoCallScreenDelegate.java
@@ -0,0 +1,48 @@
+/*
+ * 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.incallui.video.protocol;
+
+import android.content.Context;
+import com.android.incallui.videosurface.protocol.VideoSurfaceTexture;
+
+/** Callbacks from the module out to the container. */
+public interface VideoCallScreenDelegate {
+
+ void initVideoCallScreenDelegate(Context context, VideoCallScreen videoCallScreen);
+
+ void onVideoCallScreenUiReady();
+
+ void onVideoCallScreenUiUnready();
+
+ void cancelAutoFullScreen();
+
+ void resetAutoFullscreenTimer();
+
+ void onSystemUiVisibilityChange(boolean visible);
+
+ void onCameraPermissionGranted();
+
+ boolean shouldShowCameraPermissionDialog();
+
+ void onCameraPermissionDialogShown();
+
+ VideoSurfaceTexture getLocalVideoSurfaceTexture();
+
+ VideoSurfaceTexture getRemoteVideoSurfaceTexture();
+
+ int getDeviceOrientation();
+}
diff --git a/java/com/android/incallui/video/protocol/VideoCallScreenDelegateFactory.java b/java/com/android/incallui/video/protocol/VideoCallScreenDelegateFactory.java
new file mode 100644
index 000000000..285857a23
--- /dev/null
+++ b/java/com/android/incallui/video/protocol/VideoCallScreenDelegateFactory.java
@@ -0,0 +1,23 @@
+/*
+ * 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.incallui.video.protocol;
+
+/** Callbacks from the module out to the container. */
+public interface VideoCallScreenDelegateFactory {
+
+ VideoCallScreenDelegate newVideoCallScreenDelegate();
+}
diff --git a/java/com/android/incallui/videosurface/bindings/VideoSurfaceBindings.java b/java/com/android/incallui/videosurface/bindings/VideoSurfaceBindings.java
new file mode 100644
index 000000000..96fccb451
--- /dev/null
+++ b/java/com/android/incallui/videosurface/bindings/VideoSurfaceBindings.java
@@ -0,0 +1,44 @@
+/*
+ * 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.incallui.videosurface.bindings;
+
+import android.view.TextureView;
+import com.android.incallui.videosurface.impl.VideoScale;
+import com.android.incallui.videosurface.impl.VideoSurfaceTextureImpl;
+import com.android.incallui.videosurface.protocol.VideoSurfaceTexture;
+
+/** Bindings for video surface module. */
+public class VideoSurfaceBindings {
+
+ public static VideoSurfaceTexture createLocalVideoSurfaceTexture() {
+ return new VideoSurfaceTextureImpl(VideoSurfaceTexture.SURFACE_TYPE_LOCAL);
+ }
+
+ public static VideoSurfaceTexture createRemoteVideoSurfaceTexture() {
+ return new VideoSurfaceTextureImpl(VideoSurfaceTexture.SURFACE_TYPE_REMOTE);
+ }
+
+ public static void scaleVideoAndFillView(
+ TextureView textureView, float videoWidth, float videoHeight, float rotationDegrees) {
+ VideoScale.scaleVideoAndFillView(textureView, videoWidth, videoHeight, rotationDegrees);
+ }
+
+ public static void scaleVideoMaintainingAspectRatio(
+ TextureView textureView, int videoWidth, int videoHeight) {
+ VideoScale.scaleVideoMaintainingAspectRatio(textureView, videoWidth, videoHeight);
+ }
+}
diff --git a/java/com/android/incallui/videosurface/impl/VideoScale.java b/java/com/android/incallui/videosurface/impl/VideoScale.java
new file mode 100644
index 000000000..1444f5900
--- /dev/null
+++ b/java/com/android/incallui/videosurface/impl/VideoScale.java
@@ -0,0 +1,147 @@
+/*
+ * 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.incallui.videosurface.impl;
+
+import android.graphics.Matrix;
+import android.view.TextureView;
+import com.android.dialer.common.LogUtil;
+
+/** Utilities to scale the preview and remote video. */
+public class VideoScale {
+ /**
+ * Scales the video in the given view such that the video takes up the entire view. To maintain
+ * aspect ratio the video will be scaled to be larger than the view.
+ */
+ public static void scaleVideoAndFillView(
+ TextureView textureView, float videoWidth, float videoHeight, float rotationDegrees) {
+ float viewWidth = textureView.getWidth();
+ float viewHeight = textureView.getHeight();
+ float viewAspectRatio = viewWidth / viewHeight;
+ float videoAspectRatio = videoWidth / videoHeight;
+ float scaleWidth = 1.0f;
+ float scaleHeight = 1.0f;
+
+ if (viewAspectRatio > videoAspectRatio) {
+ // Scale to exactly fit the width of the video. The top and bottom will be cropped.
+ float scaleFactor = viewWidth / videoWidth;
+ float desiredScaledHeight = videoHeight * scaleFactor;
+ scaleHeight = desiredScaledHeight / viewHeight;
+ } else {
+ // Scale to exactly fit the height of the video. The sides will be cropped.
+ float scaleFactor = viewHeight / videoHeight;
+ float desiredScaledWidth = videoWidth * scaleFactor;
+ scaleWidth = desiredScaledWidth / viewWidth;
+ }
+
+ if (rotationDegrees == 90.0f || rotationDegrees == 270.0f) {
+ // We're in landscape mode but the camera feed is still drawing in portrait mode. Normally,
+ // scale of 1.0 means that the video feed stretches to fit the view. In this case the X axis
+ // is scaled to fit the height and the Y axis is scaled to fit the width.
+ float scaleX = scaleWidth;
+ float scaleY = scaleHeight;
+ scaleWidth = viewHeight / viewWidth * scaleY;
+ scaleHeight = viewWidth / viewHeight * scaleX;
+
+ // This flips the view vertically. Without this the camera feed would be upside down.
+ scaleWidth = scaleWidth * -1.0f;
+ // This flips the view horizontally. Without this the camera feed would be mirrored (left
+ // side would appear on right).
+ scaleHeight = scaleHeight * -1.0f;
+ }
+
+ LogUtil.i(
+ "VideoScale.scaleVideoAndFillView",
+ "view: %f x %f, video: %f x %f scale: %f x %f, rotation: %f",
+ viewWidth,
+ viewHeight,
+ videoWidth,
+ videoHeight,
+ scaleWidth,
+ scaleHeight,
+ rotationDegrees);
+
+ Matrix transform = new Matrix();
+ transform.setScale(
+ scaleWidth,
+ scaleHeight,
+ // This performs the scaling from the horizontal middle of the view.
+ viewWidth / 2.0f,
+ // This perform the scaling from vertical middle of the view.
+ viewHeight / 2.0f);
+ if (rotationDegrees != 0) {
+ transform.postRotate(rotationDegrees, viewWidth / 2.0f, viewHeight / 2.0f);
+ }
+ textureView.setTransform(transform);
+ }
+
+ /**
+ * Scales the video in the given view such that all of the video is visible. This will result in
+ * black bars on the top and bottom or the sides of the video.
+ */
+ public static void scaleVideoMaintainingAspectRatio(
+ TextureView textureView, int videoWidth, int videoHeight) {
+ int viewWidth = textureView.getWidth();
+ int viewHeight = textureView.getHeight();
+ float scaleWidth = 1.0f;
+ float scaleHeight = 1.0f;
+
+ if (viewWidth > viewHeight) {
+ // Landscape layout.
+ if (viewHeight * videoWidth > viewWidth * videoHeight) {
+ // Current display height is too much. Correct it.
+ int desiredHeight = viewWidth * videoHeight / videoWidth;
+ scaleWidth = (float) desiredHeight / (float) viewHeight;
+ } else if (viewHeight * videoWidth < viewWidth * videoHeight) {
+ // Current display width is too much. Correct it.
+ int desiredWidth = viewHeight * videoWidth / videoHeight;
+ scaleWidth = (float) desiredWidth / (float) viewWidth;
+ }
+ } else {
+ // Portrait layout.
+ if (viewHeight * videoWidth > viewWidth * videoHeight) {
+ // Current display height is too much. Correct it.
+ int desiredHeight = viewWidth * videoHeight / videoWidth;
+ scaleHeight = (float) desiredHeight / (float) viewHeight;
+ } else if (viewHeight * videoWidth < viewWidth * videoHeight) {
+ // Current display width is too much. Correct it.
+ int desiredWidth = viewHeight * videoWidth / videoHeight;
+ scaleHeight = (float) desiredWidth / (float) viewWidth;
+ }
+ }
+
+ LogUtil.i(
+ "VideoScale.scaleVideoMaintainingAspectRatio",
+ "view: %d x %d, video: %d x %d scale: %f x %f",
+ viewWidth,
+ viewHeight,
+ videoWidth,
+ videoHeight,
+ scaleWidth,
+ scaleHeight);
+ Matrix transform = new Matrix();
+ transform.setScale(
+ scaleWidth,
+ scaleHeight,
+ // This performs the scaling from the horizontal middle of the view.
+ viewWidth / 2.0f,
+ // This perform the scaling from vertical middle of the view.
+ viewHeight / 2.0f);
+ textureView.setTransform(transform);
+ }
+
+ private VideoScale() {}
+}
diff --git a/java/com/android/incallui/videosurface/impl/VideoSurfaceTextureImpl.java b/java/com/android/incallui/videosurface/impl/VideoSurfaceTextureImpl.java
new file mode 100644
index 000000000..21160cadb
--- /dev/null
+++ b/java/com/android/incallui/videosurface/impl/VideoSurfaceTextureImpl.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2014 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.incallui.videosurface.impl;
+
+import android.graphics.Point;
+import android.graphics.SurfaceTexture;
+import android.view.Surface;
+import android.view.TextureView;
+import android.view.View;
+import com.android.dialer.common.LogUtil;
+import com.android.incallui.videosurface.protocol.VideoSurfaceDelegate;
+import com.android.incallui.videosurface.protocol.VideoSurfaceTexture;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Represents a {@link TextureView} and its associated {@link SurfaceTexture} and {@link Surface}.
+ * Used to manage the lifecycle of these objects across device orientation changes.
+ */
+public class VideoSurfaceTextureImpl implements VideoSurfaceTexture {
+ @SurfaceType private final int surfaceType;
+ private VideoSurfaceDelegate delegate;
+ private TextureView textureView;
+ private Surface savedSurface;
+ private SurfaceTexture savedSurfaceTexture;
+ private Point surfaceDimensions;
+ private Point sourceVideoDimensions;
+ private boolean isDoneWithSurface;
+
+ public VideoSurfaceTextureImpl(@SurfaceType int surfaceType) {
+ this.surfaceType = surfaceType;
+ }
+
+ @Override
+ public void setDelegate(VideoSurfaceDelegate delegate) {
+ LogUtil.i("VideoSurfaceTextureImpl.setDelegate", "delegate: " + delegate + " " + toString());
+ this.delegate = delegate;
+ }
+
+ @Override
+ public int getSurfaceType() {
+ return surfaceType;
+ }
+
+ @Override
+ public Surface getSavedSurface() {
+ return savedSurface;
+ }
+
+ @Override
+ public void setSurfaceDimensions(Point surfaceDimensions) {
+ LogUtil.i(
+ "VideoSurfaceTextureImpl.setSurfaceDimensions",
+ "surfaceDimensions: " + surfaceDimensions + " " + toString());
+ this.surfaceDimensions = surfaceDimensions;
+ if (surfaceDimensions != null && savedSurfaceTexture != null) {
+ savedSurfaceTexture.setDefaultBufferSize(surfaceDimensions.x, surfaceDimensions.y);
+ }
+ }
+
+ @Override
+ public Point getSurfaceDimensions() {
+ return surfaceDimensions;
+ }
+
+ @Override
+ public void setSourceVideoDimensions(Point sourceVideoDimensions) {
+ this.sourceVideoDimensions = sourceVideoDimensions;
+ }
+
+ @Override
+ public Point getSourceVideoDimensions() {
+ return sourceVideoDimensions;
+ }
+
+ @Override
+ public void attachToTextureView(TextureView textureView) {
+ if (this.textureView == textureView) {
+ return;
+ }
+ LogUtil.i("VideoSurfaceTextureImpl.attachToTextureView", toString());
+
+ if (this.textureView != null) {
+ this.textureView.setOnClickListener(null);
+ // Don't clear the surface texture listener. This is important because our listener prevents
+ // the surface from being released so that it can be reused later.
+ }
+
+ this.textureView = textureView;
+ textureView.setSurfaceTextureListener(new SurfaceTextureListener());
+ textureView.setOnClickListener(new OnClickListener());
+
+ boolean areSameSurfaces = Objects.equals(savedSurfaceTexture, textureView.getSurfaceTexture());
+ LogUtil.i("VideoSurfaceTextureImpl.attachToTextureView", "areSameSurfaces: " + areSameSurfaces);
+ if (savedSurfaceTexture != null && !areSameSurfaces) {
+ textureView.setSurfaceTexture(savedSurfaceTexture);
+ if (surfaceDimensions != null && createSurface(surfaceDimensions.x, surfaceDimensions.y)) {
+ onSurfaceCreated();
+ }
+ }
+ isDoneWithSurface = false;
+ }
+
+ @Override
+ public void setDoneWithSurface() {
+ LogUtil.i("VideoSurfaceTextureImpl.setDoneWithSurface", toString());
+ isDoneWithSurface = true;
+ if (textureView != null && textureView.isAvailable()) {
+ return;
+ }
+ if (savedSurface != null) {
+ onSurfaceReleased();
+ savedSurface.release();
+ savedSurface = null;
+ }
+ if (savedSurfaceTexture != null) {
+ savedSurfaceTexture.release();
+ savedSurfaceTexture = null;
+ }
+ }
+
+ private boolean createSurface(int width, int height) {
+ LogUtil.i(
+ "VideoSurfaceTextureImpl.createSurface",
+ "width: " + width + ", height: " + height + " " + toString());
+ if (savedSurfaceTexture != null) {
+ savedSurfaceTexture.setDefaultBufferSize(width, height);
+ savedSurface = new Surface(savedSurfaceTexture);
+ return true;
+ }
+ return false;
+ }
+
+ private void onSurfaceCreated() {
+ if (delegate != null) {
+ delegate.onSurfaceCreated(this);
+ } else {
+ LogUtil.e("VideoSurfaceTextureImpl.onSurfaceCreated", "delegate is null. " + toString());
+ }
+ }
+
+ private void onSurfaceReleased() {
+ if (delegate != null) {
+ delegate.onSurfaceReleased(this);
+ } else {
+ LogUtil.e("VideoSurfaceTextureImpl.onSurfaceReleased", "delegate is null. " + toString());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ Locale.US,
+ "VideoSurfaceTextureImpl<%s%s%s%s>",
+ (surfaceType == SURFACE_TYPE_LOCAL ? "local, " : "remote, "),
+ (savedSurface == null ? "no-surface, " : ""),
+ (savedSurfaceTexture == null ? "no-texture, " : ""),
+ (surfaceDimensions == null
+ ? "(-1 x -1)"
+ : (surfaceDimensions.x + " x " + surfaceDimensions.y)));
+ }
+
+ private class SurfaceTextureListener implements TextureView.SurfaceTextureListener {
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture newSurfaceTexture, int width, int height) {
+ LogUtil.i(
+ "SurfaceTextureListener.onSurfaceTextureAvailable",
+ "newSurfaceTexture: "
+ + newSurfaceTexture
+ + " "
+ + VideoSurfaceTextureImpl.this.toString());
+
+ // Where there is no saved {@link SurfaceTexture} available, use the newly created one.
+ // If a saved {@link SurfaceTexture} is available, we are re-creating after an
+ // orientation change.
+ boolean surfaceCreated;
+ if (savedSurfaceTexture == null) {
+ savedSurfaceTexture = newSurfaceTexture;
+ surfaceCreated = createSurface(width, height);
+ } else {
+ // A saved SurfaceTexture was found.
+ LogUtil.i(
+ "SurfaceTextureListener.onSurfaceTextureAvailable", "replacing with cached surface...");
+ textureView.setSurfaceTexture(savedSurfaceTexture);
+ surfaceCreated = true;
+ }
+
+ // Inform the delegate that the surface is available.
+ if (surfaceCreated) {
+ onSurfaceCreated();
+ }
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture destroyedSurfaceTexture) {
+ LogUtil.i(
+ "SurfaceTextureListener.onSurfaceTextureDestroyed",
+ "destroyedSurfaceTexture: "
+ + destroyedSurfaceTexture
+ + " "
+ + VideoSurfaceTextureImpl.this.toString());
+ if (delegate != null) {
+ delegate.onSurfaceDestroyed(VideoSurfaceTextureImpl.this);
+ } else {
+ LogUtil.e("SurfaceTextureListener.onSurfaceTextureDestroyed", "delegate is null");
+ }
+
+ if (isDoneWithSurface) {
+ onSurfaceReleased();
+ if (savedSurface != null) {
+ savedSurface.release();
+ savedSurface = null;
+ }
+ }
+ return isDoneWithSurface;
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
+ }
+
+ private class OnClickListener implements View.OnClickListener {
+ @Override
+ public void onClick(View view) {
+ if (delegate != null) {
+ delegate.onSurfaceClick(VideoSurfaceTextureImpl.this);
+ } else {
+ LogUtil.e("OnClickListener.onClick", "delegate is null");
+ }
+ }
+ }
+}
diff --git a/java/com/android/incallui/videosurface/protocol/VideoSurfaceDelegate.java b/java/com/android/incallui/videosurface/protocol/VideoSurfaceDelegate.java
new file mode 100644
index 000000000..8fa585a72
--- /dev/null
+++ b/java/com/android/incallui/videosurface/protocol/VideoSurfaceDelegate.java
@@ -0,0 +1,29 @@
+/*
+ * 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.incallui.videosurface.protocol;
+
+/** Callbacks from the video surface. */
+public interface VideoSurfaceDelegate {
+
+ void onSurfaceCreated(VideoSurfaceTexture videoCallSurface);
+
+ void onSurfaceReleased(VideoSurfaceTexture videoCallSurface);
+
+ void onSurfaceDestroyed(VideoSurfaceTexture videoCallSurface);
+
+ void onSurfaceClick(VideoSurfaceTexture videoCallSurface);
+}
diff --git a/java/com/android/incallui/videosurface/protocol/VideoSurfaceTexture.java b/java/com/android/incallui/videosurface/protocol/VideoSurfaceTexture.java
new file mode 100644
index 000000000..411b45f56
--- /dev/null
+++ b/java/com/android/incallui/videosurface/protocol/VideoSurfaceTexture.java
@@ -0,0 +1,57 @@
+/*
+ * 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.incallui.videosurface.protocol;
+
+import android.graphics.Point;
+import android.support.annotation.IntDef;
+import android.view.Surface;
+import android.view.TextureView;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Represents a surface texture for a video feed. */
+public interface VideoSurfaceTexture {
+
+ /** Whether this represents the preview or remote display. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ SURFACE_TYPE_LOCAL,
+ SURFACE_TYPE_REMOTE,
+ })
+ @interface SurfaceType {}
+
+ int SURFACE_TYPE_LOCAL = 1;
+ int SURFACE_TYPE_REMOTE = 2;
+
+ void setDelegate(VideoSurfaceDelegate delegate);
+
+ int getSurfaceType();
+
+ Surface getSavedSurface();
+
+ void setSurfaceDimensions(Point surfaceDimensions);
+
+ Point getSurfaceDimensions();
+
+ void setSourceVideoDimensions(Point sourceVideoDimensions);
+
+ Point getSourceVideoDimensions();
+
+ void attachToTextureView(TextureView textureView);
+
+ void setDoneWithSurface();
+}
diff --git a/java/com/android/incallui/wifi/AndroidManifest.xml b/java/com/android/incallui/wifi/AndroidManifest.xml
new file mode 100644
index 000000000..843f8f3e6
--- /dev/null
+++ b/java/com/android/incallui/wifi/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.incallui.wifi">
+</manifest>
diff --git a/java/com/android/incallui/wifi/EnableWifiCallingPrompt.java b/java/com/android/incallui/wifi/EnableWifiCallingPrompt.java
new file mode 100644
index 000000000..85603bfb1
--- /dev/null
+++ b/java/com/android/incallui/wifi/EnableWifiCallingPrompt.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 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.incallui.wifi;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.support.annotation.NonNull;
+import android.telecom.DisconnectCause;
+import android.util.Pair;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+
+/** Prompts the user to enable Wi-Fi calling. */
+public class EnableWifiCallingPrompt {
+ // This is a hidden constant in android.telecom.DisconnectCause. Telecom sets this as a disconnect
+ // reason if it wants us to prompt the user to enable Wi-Fi calling. In Android-O we might
+ // consider using a more explicit way to signal this.
+ private static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
+ private static final String ACTION_WIFI_CALLING_SETTINGS =
+ "android.settings.WIFI_CALLING_SETTINGS";
+ private static final String ANDROID_SETTINGS_PACKAGE = "com.android.settings";
+
+ public static boolean shouldShowPrompt(@NonNull DisconnectCause cause) {
+ Assert.isNotNull(cause);
+ if (cause.getReason() != null && cause.getReason().startsWith(REASON_WIFI_ON_BUT_WFC_OFF)) {
+ LogUtil.i(
+ "EnableWifiCallingPrompt.shouldShowPrompt",
+ "showing prompt for disconnect cause: %s",
+ cause);
+ return true;
+ }
+ return false;
+ }
+
+ @NonNull
+ public static Pair<Dialog, CharSequence> createDialog(
+ final @NonNull Context context, @NonNull DisconnectCause cause) {
+ Assert.isNotNull(context);
+ Assert.isNotNull(cause);
+ CharSequence message = cause.getDescription();
+ Dialog dialog =
+ new AlertDialog.Builder(context)
+ .setMessage(message)
+ .setPositiveButton(
+ R.string.incall_enable_wifi_calling_button,
+ new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ openWifiCallingSettings(context);
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .create();
+ return new Pair<Dialog, CharSequence>(dialog, message);
+ }
+
+ private static void openWifiCallingSettings(@NonNull Context context) {
+ LogUtil.i("EnableWifiCallingPrompt.openWifiCallingSettings", "opening settings");
+ context.startActivity(
+ new Intent(ACTION_WIFI_CALLING_SETTINGS).setPackage(ANDROID_SETTINGS_PACKAGE));
+ }
+
+ private EnableWifiCallingPrompt() {}
+}
diff --git a/java/com/android/incallui/wifi/res/values/strings.xml b/java/com/android/incallui/wifi/res/values/strings.xml
new file mode 100644
index 000000000..1b52b9fdc
--- /dev/null
+++ b/java/com/android/incallui/wifi/res/values/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Button to enable Wi-Fi calling. This is displayed in a dialog after a phone call disconnects
+ because there is no cellular service.
+ [CHAR LIMIT=20] -->
+ <string name="incall_enable_wifi_calling_button">Enable</string>
+
+</resources>
diff --git a/java/com/android/voicemailomtp/ActivationTask.java b/java/com/android/voicemailomtp/ActivationTask.java
new file mode 100644
index 000000000..7de81e685
--- /dev/null
+++ b/java/com/android/voicemailomtp/ActivationTask.java
@@ -0,0 +1,305 @@
+/*
+ * 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.voicemailomtp;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import com.android.voicemailomtp.protocol.VisualVoicemailProtocol;
+import com.android.voicemailomtp.scheduling.BaseTask;
+import com.android.voicemailomtp.scheduling.RetryPolicy;
+import com.android.voicemailomtp.sms.StatusMessage;
+import com.android.voicemailomtp.sms.StatusSmsFetcher;
+import com.android.voicemailomtp.sync.OmtpVvmSourceManager;
+import com.android.voicemailomtp.sync.OmtpVvmSyncService;
+import com.android.voicemailomtp.sync.SyncTask;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Task to activate the visual voicemail service. A request to activate VVM will be sent to the
+ * carrier, which will respond with a STATUS SMS. The credentials will be updated from the SMS. If
+ * the user is not provisioned provisioning will be attempted. Activation happens when the phone
+ * boots, the SIM is inserted, signal returned when VVM is not activated yet, and when the carrier
+ * spontaneously sent a STATUS SMS.
+ */
+@TargetApi(VERSION_CODES.CUR_DEVELOPMENT)
+public class ActivationTask extends BaseTask {
+
+ private static final String TAG = "VvmActivationTask";
+
+ private static final int RETRY_TIMES = 4;
+ private static final int RETRY_INTERVAL_MILLIS = 5_000;
+
+ private static final String EXTRA_MESSAGE_DATA_BUNDLE = "extra_message_data_bundle";
+
+ @Nullable
+ private static DeviceProvisionedObserver sDeviceProvisionedObserver;
+
+ private final RetryPolicy mRetryPolicy;
+
+ private Bundle mMessageData;
+
+ public ActivationTask() {
+ super(TASK_ACTIVATION);
+ mRetryPolicy = new RetryPolicy(RETRY_TIMES, RETRY_INTERVAL_MILLIS);
+ addPolicy(mRetryPolicy);
+ }
+
+ /**
+ * Has the user gone through the setup wizard yet.
+ */
+ private static boolean isDeviceProvisioned(Context context) {
+ return Settings.Global.getInt(
+ context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) == 1;
+ }
+
+ /**
+ * @param messageData The optional bundle from {@link android.provider.VoicemailContract#
+ * EXTRA_VOICEMAIL_SMS_FIELDS}, if the task is initiated by a status SMS. If null the task will
+ * request a status SMS itself.
+ */
+ public static void start(Context context, PhoneAccountHandle phoneAccountHandle,
+ @Nullable Bundle messageData) {
+ if (!isDeviceProvisioned(context)) {
+ VvmLog.i(TAG, "Activation requested while device is not provisioned, postponing");
+ // Activation might need information such as system language to be set, so wait until
+ // the setup wizard is finished. The data bundle from the SMS will be re-requested upon
+ // activation.
+ queueActivationAfterProvisioned(context, phoneAccountHandle);
+ return;
+ }
+
+ Intent intent = BaseTask.createIntent(context, ActivationTask.class, phoneAccountHandle);
+ if (messageData != null) {
+ intent.putExtra(EXTRA_MESSAGE_DATA_BUNDLE, messageData);
+ }
+ context.startService(intent);
+ }
+
+ public void onCreate(Context context, Intent intent, int flags, int startId) {
+ super.onCreate(context, intent, flags, startId);
+ mMessageData = intent.getParcelableExtra(EXTRA_MESSAGE_DATA_BUNDLE);
+ }
+
+ @Override
+ public Intent createRestartIntent() {
+ Intent intent = super.createRestartIntent();
+ // mMessageData is discarded, request a fresh STATUS SMS for retries.
+ return intent;
+ }
+
+ @Override
+ @WorkerThread
+ public void onExecuteInBackgroundThread() {
+ Assert.isNotMainThread();
+
+ PhoneAccountHandle phoneAccountHandle = getPhoneAccountHandle();
+ if (phoneAccountHandle == null) {
+ // This should never happen
+ VvmLog.e(TAG, "null PhoneAccountHandle");
+ return;
+ }
+
+ OmtpVvmCarrierConfigHelper helper =
+ new OmtpVvmCarrierConfigHelper(getContext(), phoneAccountHandle);
+ if (!helper.isValid()) {
+ VvmLog.i(TAG, "VVM not supported on phoneAccountHandle " + phoneAccountHandle);
+ VoicemailStatus.disable(getContext(), phoneAccountHandle);
+ return;
+ }
+
+ // OmtpVvmCarrierConfigHelper can start the activation process; it will pass in a vvm
+ // content provider URI which we will use. On some occasions, setting that URI will
+ // fail, so we will perform a few attempts to ensure that the vvm content provider has
+ // a good chance of being started up.
+ if (!VoicemailStatus.edit(getContext(), phoneAccountHandle)
+ .setType(helper.getVvmType())
+ .apply()) {
+ VvmLog.e(TAG, "Failed to configure content provider - " + helper.getVvmType());
+ fail();
+ }
+ VvmLog.i(TAG, "VVM content provider configured - " + helper.getVvmType());
+
+ if (!OmtpVvmSourceManager.getInstance(getContext())
+ .isVvmSourceRegistered(phoneAccountHandle)) {
+ // This account has not been activated before during the lifetime of this boot.
+ VisualVoicemailPreferences preferences = new VisualVoicemailPreferences(getContext(),
+ phoneAccountHandle);
+ if (preferences.getString(OmtpConstants.SERVER_ADDRESS, null) == null) {
+ // Only show the "activating" message if activation has not been completed before.
+ // Subsequent activations are more of a status check and usually does not
+ // concern the user.
+ helper.handleEvent(VoicemailStatus.edit(getContext(), phoneAccountHandle),
+ OmtpEvents.CONFIG_ACTIVATING);
+ } else {
+ // The account has been activated on this device before. Pretend it is already
+ // activated. If there are any activation error it will overwrite this status.
+ helper.handleEvent(VoicemailStatus.edit(getContext(), phoneAccountHandle),
+ OmtpEvents.CONFIG_ACTIVATING_SUBSEQUENT);
+ }
+
+ }
+ if (!hasSignal(getContext(), phoneAccountHandle)) {
+ VvmLog.i(TAG, "Service lost during activation, aborting");
+ // Restore the "NO SIGNAL" state since it will be overwritten by the CONFIG_ACTIVATING
+ // event.
+ helper.handleEvent(VoicemailStatus.edit(getContext(), phoneAccountHandle),
+ OmtpEvents.NOTIFICATION_SERVICE_LOST);
+ // Don't retry, a new activation will be started after the signal returned.
+ return;
+ }
+
+ helper.activateSmsFilter();
+ VoicemailStatus.Editor status = mRetryPolicy.getVoicemailStatusEditor();
+
+ VisualVoicemailProtocol protocol = helper.getProtocol();
+
+ Bundle data;
+ if (mMessageData != null) {
+ // The content of STATUS SMS is provided to launch this task, no need to request it
+ // again.
+ data = mMessageData;
+ } else {
+ try (StatusSmsFetcher fetcher = new StatusSmsFetcher(getContext(),
+ phoneAccountHandle)) {
+ protocol.startActivation(helper, fetcher.getSentIntent());
+ // Both the fetcher and OmtpMessageReceiver will be triggered, but
+ // OmtpMessageReceiver will just route the SMS back to ActivationTask, which will be
+ // rejected because the task is still running.
+ data = fetcher.get();
+ } catch (TimeoutException e) {
+ // The carrier is expected to return an STATUS SMS within STATUS_SMS_TIMEOUT_MILLIS
+ // handleEvent() will do the logging.
+ helper.handleEvent(status, OmtpEvents.CONFIG_STATUS_SMS_TIME_OUT);
+ fail();
+ return;
+ } catch (CancellationException e) {
+ VvmLog.e(TAG, "Unable to send status request SMS");
+ fail();
+ return;
+ } catch (InterruptedException | ExecutionException | IOException e) {
+ VvmLog.e(TAG, "can't get future STATUS SMS", e);
+ fail();
+ return;
+ }
+ }
+
+ StatusMessage message = new StatusMessage(data);
+ VvmLog.d(TAG, "STATUS SMS received: st=" + message.getProvisioningStatus()
+ + ", rc=" + message.getReturnCode());
+
+ if (message.getProvisioningStatus().equals(OmtpConstants.SUBSCRIBER_READY)) {
+ VvmLog.d(TAG, "subscriber ready, no activation required");
+ updateSource(getContext(), phoneAccountHandle, status, message);
+ } else {
+ if (helper.supportsProvisioning()) {
+ VvmLog.i(TAG, "Subscriber not ready, start provisioning");
+ helper.startProvisioning(this, phoneAccountHandle, status, message, data);
+
+ } else if (message.getProvisioningStatus().equals(OmtpConstants.SUBSCRIBER_NEW)) {
+ VvmLog.i(TAG, "Subscriber new but provisioning is not supported");
+ // Ignore the non-ready state and attempt to use the provided info as is.
+ // This is probably caused by not completing the new user tutorial.
+ updateSource(getContext(), phoneAccountHandle, status, message);
+ } else {
+ VvmLog.i(TAG, "Subscriber not ready but provisioning is not supported");
+ helper.handleEvent(status, OmtpEvents.CONFIG_SERVICE_NOT_AVAILABLE);
+ }
+ }
+ }
+
+ public static void updateSource(Context context, PhoneAccountHandle phone,
+ VoicemailStatus.Editor status, StatusMessage message) {
+ OmtpVvmSourceManager vvmSourceManager =
+ OmtpVvmSourceManager.getInstance(context);
+
+ if (OmtpConstants.SUCCESS.equals(message.getReturnCode())) {
+ OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(context, phone);
+ helper.handleEvent(status, OmtpEvents.CONFIG_REQUEST_STATUS_SUCCESS);
+
+ // Save the IMAP credentials in preferences so they are persistent and can be retrieved.
+ VisualVoicemailPreferences prefs = new VisualVoicemailPreferences(context, phone);
+ message.putStatus(prefs.edit()).apply();
+
+ // Add the source to indicate that it is active.
+ vvmSourceManager.addSource(phone);
+
+ SyncTask.start(context, phone, OmtpVvmSyncService.SYNC_FULL_SYNC);
+ } else {
+ VvmLog.e(TAG, "Visual voicemail not available for subscriber.");
+ }
+ }
+
+ private static boolean hasSignal(Context context, PhoneAccountHandle phoneAccountHandle) {
+ TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
+ .createForPhoneAccountHandle(phoneAccountHandle);
+ return telephonyManager.getServiceState().getState() == ServiceState.STATE_IN_SERVICE;
+ }
+
+ private static void queueActivationAfterProvisioned(Context context,
+ PhoneAccountHandle phoneAccountHandle) {
+ if (sDeviceProvisionedObserver == null) {
+ sDeviceProvisionedObserver = new DeviceProvisionedObserver(context);
+ context.getContentResolver()
+ .registerContentObserver(Settings.Global.getUriFor(Global.DEVICE_PROVISIONED),
+ false, sDeviceProvisionedObserver);
+ }
+ sDeviceProvisionedObserver.addPhoneAcountHandle(phoneAccountHandle);
+ }
+
+ private static class DeviceProvisionedObserver extends ContentObserver {
+
+ private final Context mContext;
+ private final Set<PhoneAccountHandle> mPhoneAccountHandles = new HashSet<>();
+
+ private DeviceProvisionedObserver(Context context) {
+ super(null);
+ mContext = context;
+ }
+
+ public void addPhoneAcountHandle(PhoneAccountHandle phoneAccountHandle) {
+ mPhoneAccountHandles.add(phoneAccountHandle);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ if (isDeviceProvisioned(mContext)) {
+ VvmLog.i(TAG, "device provisioned, resuming activation");
+ for (PhoneAccountHandle phoneAccountHandle : mPhoneAccountHandles) {
+ start(mContext, phoneAccountHandle, null);
+ }
+ mContext.getContentResolver().unregisterContentObserver(sDeviceProvisionedObserver);
+ sDeviceProvisionedObserver = null;
+ }
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/AndroidManifest.xml b/java/com/android/voicemailomtp/AndroidManifest.xml
new file mode 100644
index 000000000..282a923d2
--- /dev/null
+++ b/java/com/android/voicemailomtp/AndroidManifest.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ package="com.android.voicemailomtp"
+>
+
+ <uses-sdk
+ android:minSdkVersion="23"
+ android:targetSdkVersion="25" />
+
+ <application
+ android:allowBackup="false"
+ android:supportsRtl="true"
+ android:usesCleartextTraffic="true"
+ android:defaultToDeviceProtectedStorage="true"
+ android:directBootAware="true">
+
+ <activity android:name="com.android.voicemailomtp.settings.VoicemailSettingsActivity"
+ android:label="@string/voicemail_settings_label">
+ <intent-filter >
+ <!-- DO NOT RENAME. There are existing apps which use this string. -->
+ <action android:name="com.android.voicemailomtp.CallFeaturesSetting.ADD_VOICEMAIL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.telephony.action.CONFIGURE_VOICEMAIL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <receiver android:name="com.android.voicemailomtp.sms.OmtpMessageReceiver"
+ android:exported="false"
+ androidprv:systemUserOnly="true">
+ <intent-filter>
+ <action android:name="com.android.vociemailomtp.sms.sms_received"/>
+ </intent-filter>
+ </receiver>
+
+ <receiver
+ android:name="com.android.voicemailomtp.fetch.FetchVoicemailReceiver"
+ android:exported="true"
+ android:permission="com.android.voicemail.permission.READ_VOICEMAIL"
+ androidprv:systemUserOnly="true">
+ <intent-filter>
+ <action android:name="android.intent.action.FETCH_VOICEMAIL" />
+ <data
+ android:scheme="content"
+ android:host="com.android.voicemail"
+ android:mimeType="vnd.android.cursor.item/voicemail" />
+ </intent-filter>
+ </receiver>
+ <receiver
+ android:name="com.android.voicemailomtp.sync.OmtpVvmSyncReceiver"
+ android:exported="true"
+ android:permission="com.android.voicemail.permission.READ_VOICEMAIL"
+ androidprv:systemUserOnly="true">
+ <intent-filter>
+ <action android:name="android.provider.action.SYNC_VOICEMAIL"/>
+ </intent-filter>
+ </receiver>
+ <receiver
+ android:name="com.android.voicemailomtp.sync.VoicemailProviderChangeReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.PROVIDER_CHANGED" />
+ <data
+ android:scheme="content"
+ android:host="com.android.voicemail"
+ android:mimeType="vnd.android.cursor.dir/voicemails"/>
+ </intent-filter>
+ </receiver>
+
+ <service
+ android:name="com.android.voicemailomtp.scheduling.TaskSchedulerService"
+ android:exported="false" />
+
+ <service
+ android:name="com.android.voicemailomtp.OmtpService"
+ android:permission="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.telephony.VisualVoicemailService"/>
+ </intent-filter>
+ </service>
+ <activity android:name=".settings.VoicemailChangePinActivity"
+ android:exported="false"
+ android:windowSoftInputMode="stateVisible|adjustResize">
+ </activity>
+ </application>
+</manifest>
diff --git a/java/com/android/voicemailomtp/Assert.java b/java/com/android/voicemailomtp/Assert.java
new file mode 100644
index 000000000..1d295bed1
--- /dev/null
+++ b/java/com/android/voicemailomtp/Assert.java
@@ -0,0 +1,62 @@
+/*
+ * 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.voicemailomtp;
+
+import android.os.Looper;
+
+/**
+ * Assertions which will result in program termination.
+ */
+public class Assert {
+
+ private static Boolean sIsMainThreadForTest;
+
+ public static void isTrue(boolean condition) {
+ if (!condition) {
+ throw new AssertionError("Expected condition to be true");
+ }
+ }
+
+ public static void isMainThread() {
+ if (sIsMainThreadForTest != null) {
+ isTrue(sIsMainThreadForTest);
+ return;
+ }
+ isTrue(Looper.getMainLooper().equals(Looper.myLooper()));
+ }
+
+ public static void isNotMainThread() {
+ if (sIsMainThreadForTest != null) {
+ isTrue(!sIsMainThreadForTest);
+ return;
+ }
+ isTrue(!Looper.getMainLooper().equals(Looper.myLooper()));
+ }
+
+ public static void fail() {
+ throw new AssertionError("Fail");
+ }
+
+ /**
+ * Override the main thread status for tests. Set to null to revert to normal behavior
+ */
+ @NeededForTesting
+ public static void setIsMainThreadForTesting(Boolean isMainThread) {
+ sIsMainThreadForTest = isMainThread;
+ }
+}
diff --git a/java/com/android/voicemailomtp/DefaultOmtpEventHandler.java b/java/com/android/voicemailomtp/DefaultOmtpEventHandler.java
new file mode 100644
index 000000000..6a4b5104a
--- /dev/null
+++ b/java/com/android/voicemailomtp/DefaultOmtpEventHandler.java
@@ -0,0 +1,202 @@
+/*
+ * 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.voicemailomtp;
+
+import android.content.Context;
+import android.provider.VoicemailContract;
+import android.provider.VoicemailContract.Status;
+
+import com.android.voicemailomtp.VoicemailStatus;
+import com.android.voicemailomtp.OmtpEvents.Type;
+
+public class DefaultOmtpEventHandler {
+
+ private static final String TAG = "DefErrorCodeHandler";
+
+ public static void handleEvent(Context context, OmtpVvmCarrierConfigHelper config,
+ VoicemailStatus.Editor status, OmtpEvents event) {
+ switch (event.getType()) {
+ case Type.CONFIGURATION:
+ handleConfigurationEvent(context, status, event);
+ break;
+ case Type.DATA_CHANNEL:
+ handleDataChannelEvent(context, status, event);
+ break;
+ case Type.NOTIFICATION_CHANNEL:
+ handleNotificationChannelEvent(context, config, status, event);
+ break;
+ case Type.OTHER:
+ handleOtherEvent(context, status, event);
+ break;
+ default:
+ VvmLog.wtf(TAG, "invalid event type " + event.getType() + " for " + event);
+ }
+ }
+
+ private static void handleConfigurationEvent(Context context, VoicemailStatus.Editor status,
+ OmtpEvents event) {
+ switch (event) {
+ case CONFIG_DEFAULT_PIN_REPLACED:
+ case CONFIG_REQUEST_STATUS_SUCCESS:
+ case CONFIG_PIN_SET:
+ status
+ .setConfigurationState(VoicemailContract.Status.CONFIGURATION_STATE_OK)
+ .setNotificationChannelState(Status.NOTIFICATION_CHANNEL_STATE_OK)
+ .apply();
+ break;
+ case CONFIG_ACTIVATING:
+ // Wipe all errors from the last activation. All errors shown should be new errors
+ // for this activation.
+ status
+ .setConfigurationState(Status.CONFIGURATION_STATE_CONFIGURING)
+ .setNotificationChannelState(Status.NOTIFICATION_CHANNEL_STATE_OK)
+ .setDataChannelState(Status.DATA_CHANNEL_STATE_OK).apply();
+ break;
+ case CONFIG_ACTIVATING_SUBSEQUENT:
+ status
+ .setConfigurationState(Status.CONFIGURATION_STATE_OK)
+ .setNotificationChannelState(Status.NOTIFICATION_CHANNEL_STATE_OK)
+ .setDataChannelState(Status.DATA_CHANNEL_STATE_OK).apply();
+ break;
+ case CONFIG_SERVICE_NOT_AVAILABLE:
+ status
+ .setConfigurationState(Status.CONFIGURATION_STATE_FAILED)
+ .apply();
+ break;
+ case CONFIG_STATUS_SMS_TIME_OUT:
+ status
+ .setConfigurationState(Status.CONFIGURATION_STATE_FAILED)
+ .apply();
+ break;
+ default:
+ VvmLog.wtf(TAG, "invalid configuration event " + event);
+ }
+ }
+
+ private static void handleDataChannelEvent(Context context, VoicemailStatus.Editor status,
+ OmtpEvents event) {
+ switch (event) {
+ case DATA_IMAP_OPERATION_STARTED:
+ case DATA_IMAP_OPERATION_COMPLETED:
+ status
+ .setDataChannelState(Status.DATA_CHANNEL_STATE_OK)
+ .apply();
+ break;
+
+ case DATA_NO_CONNECTION:
+ status
+ .setDataChannelState(Status.DATA_CHANNEL_STATE_NO_CONNECTION)
+ .apply();
+ break;
+
+ case DATA_NO_CONNECTION_CELLULAR_REQUIRED:
+ status
+ .setDataChannelState(
+ Status.DATA_CHANNEL_STATE_NO_CONNECTION_CELLULAR_REQUIRED)
+ .apply();
+ break;
+ case DATA_INVALID_PORT:
+ status
+ .setDataChannelState(
+ VoicemailContract.Status.DATA_CHANNEL_STATE_BAD_CONFIGURATION)
+ .apply();
+ break;
+ case DATA_CANNOT_RESOLVE_HOST_ON_NETWORK:
+ status
+ .setDataChannelState(
+ VoicemailContract.Status.DATA_CHANNEL_STATE_SERVER_CONNECTION_ERROR)
+ .apply();
+ break;
+ case DATA_SSL_INVALID_HOST_NAME:
+ case DATA_CANNOT_ESTABLISH_SSL_SESSION:
+ case DATA_IOE_ON_OPEN:
+ case DATA_GENERIC_IMAP_IOE:
+ status
+ .setDataChannelState(
+ VoicemailContract.Status.DATA_CHANNEL_STATE_COMMUNICATION_ERROR)
+ .apply();
+ break;
+ case DATA_BAD_IMAP_CREDENTIAL:
+ case DATA_AUTH_UNKNOWN_USER:
+ case DATA_AUTH_UNKNOWN_DEVICE:
+ case DATA_AUTH_INVALID_PASSWORD:
+ case DATA_AUTH_MAILBOX_NOT_INITIALIZED:
+ case DATA_AUTH_SERVICE_NOT_PROVISIONED:
+ case DATA_AUTH_SERVICE_NOT_ACTIVATED:
+ case DATA_AUTH_USER_IS_BLOCKED:
+ status
+ .setDataChannelState(
+ VoicemailContract.Status.DATA_CHANNEL_STATE_BAD_CONFIGURATION)
+ .apply();
+ break;
+
+ case DATA_REJECTED_SERVER_RESPONSE:
+ case DATA_INVALID_INITIAL_SERVER_RESPONSE:
+ case DATA_MAILBOX_OPEN_FAILED:
+ case DATA_SSL_EXCEPTION:
+ case DATA_ALL_SOCKET_CONNECTION_FAILED:
+ status
+ .setDataChannelState(
+ VoicemailContract.Status.DATA_CHANNEL_STATE_SERVER_ERROR)
+ .apply();
+ break;
+
+ default:
+ VvmLog.wtf(TAG, "invalid data channel event " + event);
+ }
+ }
+
+ private static void handleNotificationChannelEvent(Context context,
+ OmtpVvmCarrierConfigHelper config, VoicemailStatus.Editor status, OmtpEvents event) {
+ switch (event) {
+ case NOTIFICATION_IN_SERVICE:
+ status
+ .setNotificationChannelState(Status.NOTIFICATION_CHANNEL_STATE_OK)
+ // Clear the error state. A sync should follow signal return so any error
+ // will be reposted.
+ .setDataChannelState(Status.DATA_CHANNEL_STATE_OK)
+ .apply();
+ break;
+ case NOTIFICATION_SERVICE_LOST:
+ status.setNotificationChannelState(Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
+ if (config.isCellularDataRequired()) {
+ status.setDataChannelState(
+ Status.DATA_CHANNEL_STATE_NO_CONNECTION_CELLULAR_REQUIRED);
+ }
+ status.apply();
+ break;
+ default:
+ VvmLog.wtf(TAG, "invalid notification channel event " + event);
+ }
+ }
+
+ private static void handleOtherEvent(Context context, VoicemailStatus.Editor status,
+ OmtpEvents event) {
+ switch (event) {
+ case OTHER_SOURCE_REMOVED:
+ status
+ .setConfigurationState(Status.CONFIGURATION_STATE_NOT_CONFIGURED)
+ .setNotificationChannelState(
+ Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION)
+ .setDataChannelState(Status.DATA_CHANNEL_STATE_NO_CONNECTION)
+ .apply();
+ break;
+ default:
+ VvmLog.wtf(TAG, "invalid other event " + event);
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/NeededForTesting.java b/java/com/android/voicemailomtp/NeededForTesting.java
new file mode 100644
index 000000000..20517fed8
--- /dev/null
+++ b/java/com/android/voicemailomtp/NeededForTesting.java
@@ -0,0 +1,25 @@
+/*
+ * 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.voicemailomtp;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.SOURCE)
+public @interface NeededForTesting {
+
+}
diff --git a/java/com/android/voicemailomtp/OmtpConstants.java b/java/com/android/voicemailomtp/OmtpConstants.java
new file mode 100644
index 000000000..da2b998b6
--- /dev/null
+++ b/java/com/android/voicemailomtp/OmtpConstants.java
@@ -0,0 +1,248 @@
+/*
+ * 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.voicemailomtp;
+
+import android.support.annotation.IntDef;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Wrapper class to hold relevant OMTP constants as defined in the OMTP spec. <p> In essence this is
+ * a programmatic representation of the relevant portions of OMTP spec.
+ */
+public class OmtpConstants {
+ public static final String SMS_FIELD_SEPARATOR = ";";
+ public static final String SMS_KEY_VALUE_SEPARATOR = "=";
+ public static final String SMS_PREFIX_SEPARATOR = ":";
+
+ public static final String SYNC_SMS_PREFIX = "SYNC";
+ public static final String STATUS_SMS_PREFIX = "STATUS";
+
+ // This is the format designated by the OMTP spec.
+ public static final String DATE_TIME_FORMAT = "dd/MM/yyyy HH:mm Z";
+
+ /** OMTP protocol versions. */
+ public static final String PROTOCOL_VERSION1_1 = "11";
+ public static final String PROTOCOL_VERSION1_2 = "12";
+ public static final String PROTOCOL_VERSION1_3 = "13";
+
+ ///////////////////////// Client/Mobile originated SMS //////////////////////
+
+ /** Mobile Originated requests */
+ public static final String ACTIVATE_REQUEST = "Activate";
+ public static final String DEACTIVATE_REQUEST = "Deactivate";
+ public static final String STATUS_REQUEST = "Status";
+
+ /** fields that can be present in a Mobile Originated OMTP SMS */
+ public static final String CLIENT_TYPE = "ct";
+ public static final String APPLICATION_PORT = "pt";
+ public static final String PROTOCOL_VERSION = "pv";
+
+
+ //////////////////////////////// Sync SMS fields ////////////////////////////
+
+ /**
+ * Sync SMS fields.
+ * <p>
+ * Each string constant is the field's key in the SMS body which is used by the parser to
+ * identify the field's value, if present, in the SMS body.
+ */
+
+ /**
+ * The event that triggered this SYNC SMS.
+ * See {@link OmtpConstants#SYNC_TRIGGER_EVENT_VALUES}
+ */
+ public static final String SYNC_TRIGGER_EVENT = "ev";
+ public static final String MESSAGE_UID = "id";
+ public static final String MESSAGE_LENGTH = "l";
+ public static final String NUM_MESSAGE_COUNT = "c";
+ /** See {@link OmtpConstants#CONTENT_TYPE_VALUES} */
+ public static final String CONTENT_TYPE = "t";
+ public static final String SENDER = "s";
+ public static final String TIME = "dt";
+
+ /**
+ * SYNC message trigger events.
+ * <p>
+ * These are the possible values of {@link OmtpConstants#SYNC_TRIGGER_EVENT}.
+ */
+ public static final String NEW_MESSAGE = "NM";
+ public static final String MAILBOX_UPDATE = "MBU";
+ public static final String GREETINGS_UPDATE = "GU";
+
+ public static final String[] SYNC_TRIGGER_EVENT_VALUES = {
+ NEW_MESSAGE,
+ MAILBOX_UPDATE,
+ GREETINGS_UPDATE
+ };
+
+ /**
+ * Content types supported by OMTP VVM.
+ * <p>
+ * These are the possible values of {@link OmtpConstants#CONTENT_TYPE}.
+ */
+ public static final String VOICE = "v";
+ public static final String VIDEO = "o";
+ public static final String FAX = "f";
+ /** Voice message deposited by an external application */
+ public static final String INFOTAINMENT = "i";
+ /** Empty Call Capture - i.e. voicemail with no voice message. */
+ public static final String ECC = "e";
+
+ public static final String[] CONTENT_TYPE_VALUES = {VOICE, VIDEO, FAX, INFOTAINMENT, ECC};
+
+ ////////////////////////////// Status SMS fields ////////////////////////////
+
+ /**
+ * Status SMS fields.
+ * <p>
+ * Each string constant is the field's key in the SMS body which is used by the parser to
+ * identify the field's value, if present, in the SMS body.
+ */
+ /** See {@link OmtpConstants#PROVISIONING_STATUS_VALUES} */
+ public static final String PROVISIONING_STATUS = "st";
+ /** See {@link OmtpConstants#RETURN_CODE_VALUES} */
+ public static final String RETURN_CODE = "rc";
+ /** URL to send users to for activation VVM */
+ public static final String SUBSCRIPTION_URL = "rs";
+ /** IMAP4/SMTP server IP address or fully qualified domain name */
+ public static final String SERVER_ADDRESS = "srv";
+ /** Phone number to access voicemails through Telephony User Interface */
+ public static final String TUI_ACCESS_NUMBER = "tui";
+ public static final String TUI_PASSWORD_LENGTH = "pw_len";
+ /** Number to send client origination SMS */
+ public static final String CLIENT_SMS_DESTINATION_NUMBER = "dn";
+ public static final String IMAP_PORT = "ipt";
+ public static final String IMAP_USER_NAME = "u";
+ public static final String IMAP_PASSWORD = "pw";
+ public static final String SMTP_PORT = "spt";
+ public static final String SMTP_USER_NAME = "smtp_u";
+ public static final String SMTP_PASSWORD = "smtp_pw";
+
+ /**
+ * User provisioning status values.
+ * <p>
+ * Referred by {@link OmtpConstants#PROVISIONING_STATUS}.
+ */
+ public static final String SUBSCRIBER_NEW = "N";
+ public static final String SUBSCRIBER_READY = "R";
+ public static final String SUBSCRIBER_PROVISIONED = "P";
+ public static final String SUBSCRIBER_UNKNOWN = "U";
+ public static final String SUBSCRIBER_BLOCKED = "B";
+
+ public static final String[] PROVISIONING_STATUS_VALUES = {
+ SUBSCRIBER_NEW,
+ SUBSCRIBER_READY,
+ SUBSCRIBER_PROVISIONED,
+ SUBSCRIBER_UNKNOWN,
+ SUBSCRIBER_BLOCKED
+ };
+
+ /**
+ * The return code included in a status message.
+ * <p>
+ * These are the possible values of {@link OmtpConstants#RETURN_CODE}.
+ */
+ public static final String SUCCESS = "0";
+ public static final String SYSTEM_ERROR = "1";
+ public static final String SUBSCRIBER_ERROR = "2";
+ public static final String MAILBOX_UNKNOWN = "3";
+ public static final String VVM_NOT_ACTIVATED = "4";
+ public static final String VVM_NOT_PROVISIONED = "5";
+ public static final String VVM_CLIENT_UKNOWN = "6";
+ public static final String VVM_MAILBOX_NOT_INITIALIZED = "7";
+
+ public static final String[] RETURN_CODE_VALUES = {
+ SUCCESS,
+ SYSTEM_ERROR,
+ SUBSCRIBER_ERROR,
+ MAILBOX_UNKNOWN,
+ VVM_NOT_ACTIVATED,
+ VVM_NOT_PROVISIONED,
+ VVM_CLIENT_UKNOWN,
+ VVM_MAILBOX_NOT_INITIALIZED,
+ };
+
+ /**
+ * A map of all the field keys to the possible values they can have.
+ */
+ public static final Map<String, String[]> possibleValuesMap = new HashMap<String, String[]>() {{
+ put(SYNC_TRIGGER_EVENT, SYNC_TRIGGER_EVENT_VALUES);
+ put(CONTENT_TYPE, CONTENT_TYPE_VALUES);
+ put(PROVISIONING_STATUS, PROVISIONING_STATUS_VALUES);
+ put(RETURN_CODE, RETURN_CODE_VALUES);
+ }};
+
+ /**
+ * IMAP command extensions
+ */
+
+ /**
+ * OMTP spec v1.3 2.3.1 Change password request syntax
+ *
+ * This changes the PIN to access the Telephone User Interface, the traditional voicemail
+ * system.
+ */
+ public static final String IMAP_CHANGE_TUI_PWD_FORMAT = "XCHANGE_TUI_PWD PWD=%1$s OLD_PWD=%2$s";
+
+ /**
+ * OMTP spec v1.3 2.4.1 Change languate request syntax
+ *
+ * This changes the language in the Telephone User Interface.
+ */
+ public static final String IMAP_CHANGE_VM_LANG_FORMAT = "XCHANGE_VM_LANG LANG=%1$s";
+
+ /**
+ * OMTP spec v1.3 2.5.1 Close NUT Request syntax
+ *
+ * This disables the new user tutorial, the message played to new users calling in the Telephone
+ * User Interface.
+ */
+ public static final String IMAP_CLOSE_NUT = "XCLOSE_NUT";
+
+ /**
+ * Possible NO responses for CHANGE_TUI_PWD
+ */
+
+ public static final String RESPONSE_CHANGE_PIN_TOO_SHORT = "password too short";
+ public static final String RESPONSE_CHANGE_PIN_TOO_LONG = "password too long";
+ public static final String RESPONSE_CHANGE_PIN_TOO_WEAK = "password too weak";
+ public static final String RESPONSE_CHANGE_PIN_MISMATCH = "old password mismatch";
+ public static final String RESPONSE_CHANGE_PIN_INVALID_CHARACTER =
+ "password contains invalid characters";
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {CHANGE_PIN_SUCCESS, CHANGE_PIN_TOO_SHORT, CHANGE_PIN_TOO_LONG,
+ CHANGE_PIN_TOO_WEAK, CHANGE_PIN_MISMATCH, CHANGE_PIN_INVALID_CHARACTER,
+ CHANGE_PIN_SYSTEM_ERROR})
+
+ public @interface ChangePinResult {
+
+ }
+
+ public static final int CHANGE_PIN_SUCCESS = 0;
+ public static final int CHANGE_PIN_TOO_SHORT = 1;
+ public static final int CHANGE_PIN_TOO_LONG = 2;
+ public static final int CHANGE_PIN_TOO_WEAK = 3;
+ public static final int CHANGE_PIN_MISMATCH = 4;
+ public static final int CHANGE_PIN_INVALID_CHARACTER = 5;
+ public static final int CHANGE_PIN_SYSTEM_ERROR = 6;
+
+ /** Indicates the client is Google visual voicemail version 1.0. */
+ public static final String CLIENT_TYPE_GOOGLE_10 = "google.vvm.10";
+}
diff --git a/java/com/android/voicemailomtp/OmtpEvents.java b/java/com/android/voicemailomtp/OmtpEvents.java
new file mode 100644
index 000000000..d5c2a8b03
--- /dev/null
+++ b/java/com/android/voicemailomtp/OmtpEvents.java
@@ -0,0 +1,156 @@
+/*
+ * 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.voicemailomtp;
+
+import android.support.annotation.IntDef;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Events internal to the OMTP client. These should be translated into {@link
+ * android.provider.VoicemailContract.Status} error codes before writing into the voicemail status
+ * table.
+ */
+public enum OmtpEvents {
+
+ // Configuration State
+ CONFIG_REQUEST_STATUS_SUCCESS(Type.CONFIGURATION, true),
+
+ CONFIG_PIN_SET(Type.CONFIGURATION, true),
+ // The voicemail PIN is replaced with a generated PIN, user should change it.
+ CONFIG_DEFAULT_PIN_REPLACED(Type.CONFIGURATION, true),
+ CONFIG_ACTIVATING(Type.CONFIGURATION, true),
+ // There are already activation records, this is only a book-keeping activation.
+ CONFIG_ACTIVATING_SUBSEQUENT(Type.CONFIGURATION, true),
+ CONFIG_STATUS_SMS_TIME_OUT(Type.CONFIGURATION),
+ CONFIG_SERVICE_NOT_AVAILABLE(Type.CONFIGURATION),
+
+ // Data channel State
+
+ // A new sync has started, old errors in data channel should be cleared.
+ DATA_IMAP_OPERATION_STARTED(Type.DATA_CHANNEL, true),
+ // Successfully downloaded/uploaded data from the server, which means the data channel is clear.
+ DATA_IMAP_OPERATION_COMPLETED(Type.DATA_CHANNEL, true),
+ // The port provided in the STATUS SMS is invalid.
+ DATA_INVALID_PORT(Type.DATA_CHANNEL),
+ // No connection to the internet, and the carrier requires cellular data
+ DATA_NO_CONNECTION_CELLULAR_REQUIRED(Type.DATA_CHANNEL),
+ // No connection to the internet.
+ DATA_NO_CONNECTION(Type.DATA_CHANNEL),
+ // Address lookup for the server hostname failed. DNS error?
+ DATA_CANNOT_RESOLVE_HOST_ON_NETWORK(Type.DATA_CHANNEL),
+ // All destination address that resolves to the server hostname are rejected or timed out
+ DATA_ALL_SOCKET_CONNECTION_FAILED(Type.DATA_CHANNEL),
+ // Failed to establish SSL with the server, either with a direct SSL connection or by
+ // STARTTLS command
+ DATA_CANNOT_ESTABLISH_SSL_SESSION(Type.DATA_CHANNEL),
+ // Identity of the server cannot be verified.
+ DATA_SSL_INVALID_HOST_NAME(Type.DATA_CHANNEL),
+ // The server rejected our username/password
+ DATA_BAD_IMAP_CREDENTIAL(Type.DATA_CHANNEL),
+
+ DATA_AUTH_UNKNOWN_USER(Type.DATA_CHANNEL),
+ DATA_AUTH_UNKNOWN_DEVICE(Type.DATA_CHANNEL),
+ DATA_AUTH_INVALID_PASSWORD(Type.DATA_CHANNEL),
+ DATA_AUTH_MAILBOX_NOT_INITIALIZED(Type.DATA_CHANNEL),
+ DATA_AUTH_SERVICE_NOT_PROVISIONED(Type.DATA_CHANNEL),
+ DATA_AUTH_SERVICE_NOT_ACTIVATED(Type.DATA_CHANNEL),
+ DATA_AUTH_USER_IS_BLOCKED(Type.DATA_CHANNEL),
+
+ // A command to the server didn't result with an "OK" or continuation request
+ DATA_REJECTED_SERVER_RESPONSE(Type.DATA_CHANNEL),
+ // The server did not greet us with a "OK", possibly not a IMAP server.
+ DATA_INVALID_INITIAL_SERVER_RESPONSE(Type.DATA_CHANNEL),
+ // An IOException occurred while trying to open an ImapConnection
+ // TODO: reduce scope
+ DATA_IOE_ON_OPEN(Type.DATA_CHANNEL),
+ // The SELECT command on a mailbox is rejected
+ DATA_MAILBOX_OPEN_FAILED(Type.DATA_CHANNEL),
+ // An IOException has occurred
+ // TODO: reduce scope
+ DATA_GENERIC_IMAP_IOE(Type.DATA_CHANNEL),
+ // An SslException has occurred while opening an ImapConnection
+ // TODO: reduce scope
+ DATA_SSL_EXCEPTION(Type.DATA_CHANNEL),
+
+ // Notification Channel
+
+ // Cell signal restored, can received VVM SMSs
+ NOTIFICATION_IN_SERVICE(Type.NOTIFICATION_CHANNEL, true),
+ // Cell signal lost, cannot received VVM SMSs
+ NOTIFICATION_SERVICE_LOST(Type.NOTIFICATION_CHANNEL, false),
+
+
+ // Other
+ OTHER_SOURCE_REMOVED(Type.OTHER, false),
+
+ // VVM3
+ VVM3_NEW_USER_SETUP_FAILED,
+ // Table 4. client internal error handling
+ VVM3_VMG_DNS_FAILURE,
+ VVM3_SPG_DNS_FAILURE,
+ VVM3_VMG_CONNECTION_FAILED,
+ VVM3_SPG_CONNECTION_FAILED,
+ VVM3_VMG_TIMEOUT,
+ VVM3_STATUS_SMS_TIMEOUT,
+
+ VVM3_SUBSCRIBER_PROVISIONED,
+ VVM3_SUBSCRIBER_BLOCKED,
+ VVM3_SUBSCRIBER_UNKNOWN;
+
+ public static class Type {
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({CONFIGURATION, DATA_CHANNEL, NOTIFICATION_CHANNEL, OTHER})
+ public @interface Values {
+
+ }
+
+ public static final int CONFIGURATION = 1;
+ public static final int DATA_CHANNEL = 2;
+ public static final int NOTIFICATION_CHANNEL = 3;
+ public static final int OTHER = 4;
+ }
+
+ private final int mType;
+ private final boolean mIsSuccess;
+
+ OmtpEvents(int type, boolean isSuccess) {
+ mType = type;
+ mIsSuccess = isSuccess;
+ }
+
+ OmtpEvents(int type) {
+ mType = type;
+ mIsSuccess = false;
+ }
+
+ OmtpEvents() {
+ mType = Type.OTHER;
+ mIsSuccess = false;
+ }
+
+ @Type.Values
+ public int getType() {
+ return mType;
+ }
+
+ public boolean isSuccess() {
+ return mIsSuccess;
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/OmtpService.java b/java/com/android/voicemailomtp/OmtpService.java
new file mode 100644
index 000000000..261a7cb32
--- /dev/null
+++ b/java/com/android/voicemailomtp/OmtpService.java
@@ -0,0 +1,65 @@
+/*
+ * 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.voicemailomtp;
+
+import android.content.Intent;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.VisualVoicemailService;
+import android.telephony.VisualVoicemailSms;
+
+import com.android.voicemailomtp.sync.OmtpVvmSourceManager;
+
+public class OmtpService extends VisualVoicemailService {
+
+ private static String TAG = "VvmOmtpService";
+
+ public static final String ACTION_SMS_RECEIVED = "com.android.vociemailomtp.sms.sms_received";
+
+ public static final String EXTRA_VOICEMAIL_SMS = "extra_voicemail_sms";
+
+ @Override
+ public void onCellServiceConnected(VisualVoicemailTask task,
+ final PhoneAccountHandle phoneAccountHandle) {
+ VvmLog.i(TAG, "onCellServiceConnected");
+ ActivationTask
+ .start(OmtpService.this, phoneAccountHandle, null);
+ task.finish();
+ }
+
+ @Override
+ public void onSmsReceived(VisualVoicemailTask task, final VisualVoicemailSms sms) {
+ VvmLog.i(TAG, "onSmsReceived");
+ Intent intent = new Intent(ACTION_SMS_RECEIVED);
+ intent.setPackage(getPackageName());
+ intent.putExtra(EXTRA_VOICEMAIL_SMS, sms);
+ sendBroadcast(intent);
+ task.finish();
+ }
+
+ @Override
+ public void onSimRemoved(final VisualVoicemailTask task,
+ final PhoneAccountHandle phoneAccountHandle) {
+ VvmLog.i(TAG, "onSimRemoved");
+ OmtpVvmSourceManager.getInstance(OmtpService.this).removeSource(phoneAccountHandle);
+ task.finish();
+ }
+
+ @Override
+ public void onStopped(VisualVoicemailTask task) {
+ VvmLog.i(TAG, "onStopped");
+ }
+}
diff --git a/java/com/android/voicemailomtp/OmtpVvmCarrierConfigHelper.java b/java/com/android/voicemailomtp/OmtpVvmCarrierConfigHelper.java
new file mode 100644
index 000000000..b3e72d215
--- /dev/null
+++ b/java/com/android/voicemailomtp/OmtpVvmCarrierConfigHelper.java
@@ -0,0 +1,423 @@
+/*
+ * 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.voicemailomtp;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.telephony.VisualVoicemailService;
+import android.telephony.VisualVoicemailSmsFilterSettings;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import com.android.voicemailomtp.protocol.VisualVoicemailProtocol;
+import com.android.voicemailomtp.protocol.VisualVoicemailProtocolFactory;
+import com.android.voicemailomtp.sms.StatusMessage;
+
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * Manages carrier dependent visual voicemail configuration values. The primary source is the value
+ * retrieved from CarrierConfigManager. If CarrierConfigManager does not provide the config
+ * (KEY_VVM_TYPE_STRING is empty, or "hidden" configs), then the value hardcoded in telephony will
+ * be used (in res/xml/vvm_config.xml)
+ *
+ * Hidden configs are new configs that are planned for future APIs, or miscellaneous settings that
+ * may clutter CarrierConfigManager too much.
+ *
+ * The current hidden configs are: {@link #getSslPort()} {@link #getDisabledCapabilities()}
+ */
+public class OmtpVvmCarrierConfigHelper {
+
+ private static final String TAG = "OmtpVvmCarrierCfgHlpr";
+
+ static final String KEY_VVM_TYPE_STRING = CarrierConfigManager.KEY_VVM_TYPE_STRING;
+ static final String KEY_VVM_DESTINATION_NUMBER_STRING =
+ CarrierConfigManager.KEY_VVM_DESTINATION_NUMBER_STRING;
+ static final String KEY_VVM_PORT_NUMBER_INT =
+ CarrierConfigManager.KEY_VVM_PORT_NUMBER_INT;
+ static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING =
+ CarrierConfigManager.KEY_CARRIER_VVM_PACKAGE_NAME_STRING;
+ static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY =
+ "carrier_vvm_package_name_string_array";
+ static final String KEY_VVM_PREFETCH_BOOL =
+ CarrierConfigManager.KEY_VVM_PREFETCH_BOOL;
+ static final String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL =
+ CarrierConfigManager.KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL;
+
+ /**
+ * @see #getSslPort()
+ */
+ static final String KEY_VVM_SSL_PORT_NUMBER_INT =
+ "vvm_ssl_port_number_int";
+
+ /**
+ * @see #isLegacyModeEnabled()
+ */
+ static final String KEY_VVM_LEGACY_MODE_ENABLED_BOOL =
+ "vvm_legacy_mode_enabled_bool";
+
+ /**
+ * Ban a capability reported by the server from being used. The array of string should be a
+ * subset of the capabilities returned IMAP CAPABILITY command.
+ *
+ * @see #getDisabledCapabilities()
+ */
+ static final String KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY =
+ "vvm_disabled_capabilities_string_array";
+ static final String KEY_VVM_CLIENT_PREFIX_STRING =
+ "vvm_client_prefix_string";
+
+ private final Context mContext;
+ private final PersistableBundle mCarrierConfig;
+ private final String mVvmType;
+ private final VisualVoicemailProtocol mProtocol;
+ private final PersistableBundle mTelephonyConfig;
+
+ private PhoneAccountHandle mPhoneAccountHandle;
+
+ public OmtpVvmCarrierConfigHelper(Context context, PhoneAccountHandle handle) {
+ mContext = context;
+ mPhoneAccountHandle = handle;
+ mCarrierConfig = getCarrierConfig();
+
+ TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ mTelephonyConfig = new TelephonyVvmConfigManager(context.getResources())
+ .getConfig(telephonyManager.createForPhoneAccountHandle(mPhoneAccountHandle)
+ .getSimOperator());
+
+ mVvmType = getVvmType();
+ mProtocol = VisualVoicemailProtocolFactory.create(mContext.getResources(), mVvmType);
+
+ }
+
+ @VisibleForTesting
+ OmtpVvmCarrierConfigHelper(Context context, PersistableBundle carrierConfig,
+ PersistableBundle telephonyConfig) {
+ mContext = context;
+ mCarrierConfig = carrierConfig;
+ mTelephonyConfig = telephonyConfig;
+ mVvmType = getVvmType();
+ mProtocol = VisualVoicemailProtocolFactory.create(mContext.getResources(), mVvmType);
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Nullable
+ public PhoneAccountHandle getPhoneAccountHandle() {
+ return mPhoneAccountHandle;
+ }
+
+ /**
+ * return whether the carrier's visual voicemail is supported, with KEY_VVM_TYPE_STRING set as a
+ * known protocol.
+ */
+ public boolean isValid() {
+ return mProtocol != null;
+ }
+
+ @Nullable
+ public String getVvmType() {
+ return (String) getValue(KEY_VVM_TYPE_STRING);
+ }
+
+ @Nullable
+ public VisualVoicemailProtocol getProtocol() {
+ return mProtocol;
+ }
+
+ /**
+ * @returns arbitrary String stored in the config file. Used for protocol specific values.
+ */
+ @Nullable
+ public String getString(String key) {
+ return (String) getValue(key);
+ }
+
+ @Nullable
+ public Set<String> getCarrierVvmPackageNames() {
+ Set<String> names = getCarrierVvmPackageNames(mCarrierConfig);
+ if (names != null) {
+ return names;
+ }
+ return getCarrierVvmPackageNames(mTelephonyConfig);
+ }
+
+ private static Set<String> getCarrierVvmPackageNames(@Nullable PersistableBundle bundle) {
+ if (bundle == null) {
+ return null;
+ }
+ Set<String> names = new ArraySet<>();
+ if (bundle.containsKey(KEY_CARRIER_VVM_PACKAGE_NAME_STRING)) {
+ names.add(bundle.getString(KEY_CARRIER_VVM_PACKAGE_NAME_STRING));
+ }
+ if (bundle.containsKey(KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY)) {
+ names.addAll(Arrays.asList(
+ bundle.getStringArray(KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY)));
+ }
+ if (names.isEmpty()) {
+ return null;
+ }
+ return names;
+ }
+
+ /**
+ * For checking upon sim insertion whether visual voicemail should be enabled. This method does
+ * so by checking if the carrier's voicemail app is installed.
+ */
+ public boolean isEnabledByDefault() {
+ if (!isValid()) {
+ return false;
+ }
+
+ Set<String> carrierPackages = getCarrierVvmPackageNames();
+ if (carrierPackages == null) {
+ return true;
+ }
+ for (String packageName : carrierPackages) {
+ try {
+ mContext.getPackageManager().getPackageInfo(packageName, 0);
+ return false;
+ } catch (NameNotFoundException e) {
+ // Do nothing.
+ }
+ }
+ return true;
+ }
+
+ public boolean isCellularDataRequired() {
+ return (boolean) getValue(KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL, false);
+ }
+
+ public boolean isPrefetchEnabled() {
+ return (boolean) getValue(KEY_VVM_PREFETCH_BOOL, true);
+ }
+
+
+ public int getApplicationPort() {
+ return (int) getValue(KEY_VVM_PORT_NUMBER_INT, 0);
+ }
+
+ @Nullable
+ public String getDestinationNumber() {
+ return (String) getValue(KEY_VVM_DESTINATION_NUMBER_STRING);
+ }
+
+ /**
+ * Hidden config.
+ *
+ * @return Port to start a SSL IMAP connection directly.
+ *
+ * TODO: make config public and add to CarrierConfigManager
+ */
+ public int getSslPort() {
+ return (int) getValue(KEY_VVM_SSL_PORT_NUMBER_INT, 0);
+ }
+
+ /**
+ * Hidden Config.
+ *
+ * <p>Sometimes the server states it supports a certain feature but we found they have bug on
+ * the server side. For example, in b/28717550 the server reported AUTH=DIGEST-MD5 capability
+ * but using it to login will cause subsequent response to be erroneous.
+ *
+ * @return A set of capabilities that is reported by the IMAP CAPABILITY command, but determined
+ * to have issues and should not be used.
+ */
+ @Nullable
+ public Set<String> getDisabledCapabilities() {
+ Set<String> disabledCapabilities = getDisabledCapabilities(mCarrierConfig);
+ if (disabledCapabilities != null) {
+ return disabledCapabilities;
+ }
+ return getDisabledCapabilities(mTelephonyConfig);
+ }
+
+ @Nullable
+ private static Set<String> getDisabledCapabilities(@Nullable PersistableBundle bundle) {
+ if (bundle == null) {
+ return null;
+ }
+ if (!bundle.containsKey(KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY)) {
+ return null;
+ }
+ ArraySet<String> result = new ArraySet<String>();
+ result.addAll(
+ Arrays.asList(bundle.getStringArray(KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY)));
+ return result;
+ }
+
+ public String getClientPrefix() {
+ String prefix = (String) getValue(KEY_VVM_CLIENT_PREFIX_STRING);
+ if (prefix != null) {
+ return prefix;
+ }
+ return "//VVM";
+ }
+
+ /**
+ * Should legacy mode be used when the OMTP VVM client is disabled?
+ *
+ * <p>Legacy mode is a mode that on the carrier side visual voicemail is still activated, but on
+ * the client side all network operations are disabled. SMSs are still monitored so a new
+ * message SYNC SMS will be translated to show a message waiting indicator, like traditional
+ * voicemails.
+ *
+ * <p>This is for carriers that does not support VVM deactivation so voicemail can continue to
+ * function without the data cost.
+ */
+ public boolean isLegacyModeEnabled() {
+ return (boolean) getValue(KEY_VVM_LEGACY_MODE_ENABLED_BOOL, false);
+ }
+
+ public void startActivation() {
+ PhoneAccountHandle phoneAccountHandle = getPhoneAccountHandle();
+ if (phoneAccountHandle == null) {
+ // This should never happen
+ // Error logged in getPhoneAccountHandle().
+ return;
+ }
+
+ if (mVvmType == null || mVvmType.isEmpty()) {
+ // The VVM type is invalid; we should never have gotten here in the first place since
+ // this is loaded initially in the constructor, and callers should check isValid()
+ // before trying to start activation anyways.
+ VvmLog.e(TAG, "startActivation : vvmType is null or empty for account " +
+ phoneAccountHandle);
+ return;
+ }
+
+ if (mProtocol != null) {
+ ActivationTask.start(mContext, mPhoneAccountHandle, null);
+ }
+ }
+
+ public void activateSmsFilter() {
+ VisualVoicemailService.setSmsFilterSettings(mContext, getPhoneAccountHandle(),
+ new VisualVoicemailSmsFilterSettings.Builder()
+ .setClientPrefix(getClientPrefix())
+ .build());
+ }
+
+ public void startDeactivation() {
+ if (!isLegacyModeEnabled()) {
+ // SMS should still be filtered in legacy mode
+ VisualVoicemailService.setSmsFilterSettings(mContext, getPhoneAccountHandle(), null);
+ }
+ if (mProtocol != null) {
+ mProtocol.startDeactivation(this);
+ }
+ }
+
+ public boolean supportsProvisioning() {
+ if (mProtocol != null) {
+ return mProtocol.supportsProvisioning();
+ }
+ return false;
+ }
+
+ public void startProvisioning(ActivationTask task, PhoneAccountHandle phone,
+ VoicemailStatus.Editor status, StatusMessage message, Bundle data) {
+ if (mProtocol != null) {
+ mProtocol.startProvisioning(task, phone, this, status, message, data);
+ }
+ }
+
+ public void requestStatus(@Nullable PendingIntent sentIntent) {
+ if (mProtocol != null) {
+ mProtocol.requestStatus(this, sentIntent);
+ }
+ }
+
+ public void handleEvent(VoicemailStatus.Editor status, OmtpEvents event) {
+ VvmLog.i(TAG, "OmtpEvent:" + event);
+ if (mProtocol != null) {
+ mProtocol.handleEvent(mContext, this, status, event);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("OmtpVvmCarrierConfigHelper [");
+ builder.append("phoneAccountHandle: ").append(mPhoneAccountHandle)
+ .append(", carrierConfig: ").append(mCarrierConfig != null)
+ .append(", telephonyConfig: ").append(mTelephonyConfig != null)
+ .append(", type: ").append(getVvmType())
+ .append(", destinationNumber: ").append(getDestinationNumber())
+ .append(", applicationPort: ").append(getApplicationPort())
+ .append(", sslPort: ").append(getSslPort())
+ .append(", isEnabledByDefault: ").append(isEnabledByDefault())
+ .append(", isCellularDataRequired: ").append(isCellularDataRequired())
+ .append(", isPrefetchEnabled: ").append(isPrefetchEnabled())
+ .append(", isLegacyModeEnabled: ").append(isLegacyModeEnabled())
+ .append("]");
+ return builder.toString();
+ }
+
+ @Nullable
+ private PersistableBundle getCarrierConfig() {
+
+ CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (carrierConfigManager == null) {
+ VvmLog.w(TAG, "No carrier config service found.");
+ return null;
+ }
+
+ PersistableBundle config = TelephonyManagerStub
+ .getCarrirConfigForPhoneAccountHandle(getContext(), mPhoneAccountHandle);
+
+ if (TextUtils.isEmpty(config.getString(CarrierConfigManager.KEY_VVM_TYPE_STRING))) {
+ return null;
+ }
+ return config;
+ }
+
+ @Nullable
+ private Object getValue(String key) {
+ return getValue(key, null);
+ }
+
+ @Nullable
+ private Object getValue(String key, Object defaultValue) {
+ Object result;
+ if (mCarrierConfig != null) {
+ result = mCarrierConfig.get(key);
+ if (result != null) {
+ return result;
+ }
+ }
+ if (mTelephonyConfig != null) {
+ result = mTelephonyConfig.get(key);
+ if (result != null) {
+ return result;
+ }
+ }
+ return defaultValue;
+ }
+
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/SubscriptionInfoHelper.java b/java/com/android/voicemailomtp/SubscriptionInfoHelper.java
new file mode 100644
index 000000000..b916247ad
--- /dev/null
+++ b/java/com/android/voicemailomtp/SubscriptionInfoHelper.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (C) 2014 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.voicemailomtp;
+
+import android.app.ActionBar;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.text.TextUtils;
+
+/**
+ * Helper for manipulating intents or components with subscription-related information.
+ *
+ * In settings, subscription ids and labels are passed along to indicate that settings
+ * are being changed for particular subscriptions. This helper provides functions for
+ * helping extract this info and perform common operations using this info.
+ */
+public class SubscriptionInfoHelper {
+ public static final int NO_SUB_ID = -1;
+
+ // Extra on intent containing the id of a subscription.
+ public static final String SUB_ID_EXTRA =
+ "com.android.voicemailomtp.settings.SubscriptionInfoHelper.SubscriptionId";
+ // Extra on intent containing the label of a subscription.
+ private static final String SUB_LABEL_EXTRA =
+ "com.android.voicemailomtp.settings.SubscriptionInfoHelper.SubscriptionLabel";
+
+ private static Context mContext;
+
+ private static int mSubId = NO_SUB_ID;
+ private static String mSubLabel;
+
+ /**
+ * Instantiates the helper, by extracting the subscription id and label from the intent.
+ */
+ public SubscriptionInfoHelper(Context context, Intent intent) {
+ mContext = context;
+ mSubId = intent.getIntExtra(SUB_ID_EXTRA, NO_SUB_ID);
+ mSubLabel = intent.getStringExtra(SUB_LABEL_EXTRA);
+ }
+
+ /**
+ * Sets the action bar title to the string specified by the given resource id, formatting
+ * it with the subscription label. This assumes the resource string is formattable with a
+ * string-type specifier.
+ *
+ * If the subscription label does not exists, leave the existing title.
+ */
+ public void setActionBarTitle(ActionBar actionBar, Resources res, int resId) {
+ if (actionBar == null || TextUtils.isEmpty(mSubLabel)) {
+ return;
+ }
+
+ String title = String.format(res.getString(resId), mSubLabel);
+ actionBar.setTitle(title);
+ }
+
+ public int getSubId() {
+ return mSubId;
+ }
+}
diff --git a/java/com/android/voicemailomtp/TelephonyManagerStub.java b/java/com/android/voicemailomtp/TelephonyManagerStub.java
new file mode 100644
index 000000000..e2e5dacdb
--- /dev/null
+++ b/java/com/android/voicemailomtp/TelephonyManagerStub.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 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.voicemailomtp;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build.VERSION_CODES;
+import android.os.PersistableBundle;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import java.lang.reflect.Method;
+
+/**
+ * Temporary stub for public APIs that should be added into telephony manager.
+ *
+ * <p>TODO(b/32637799) remove this.
+ */
+@TargetApi(VERSION_CODES.CUR_DEVELOPMENT)
+public class TelephonyManagerStub {
+
+ private static final String TAG = "TelephonyManagerStub";
+
+ public static void showVoicemailNotification(int voicemailCount) {
+
+ }
+
+ /**
+ * Dismisses the message waiting (voicemail) indicator.
+ *
+ * @param subId the subscription id we should dismiss the notification for.
+ */
+ public static void clearMwiIndicator(int subId) {
+
+ }
+
+ public static void setShouldCheckVisualVoicemailConfigurationForMwi(int subId,
+ boolean enabled) {
+
+ }
+
+ public static int getSubIdForPhoneAccount(Context context, PhoneAccount phoneAccount) {
+ // Hidden
+ TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+ try {
+ Method method = TelephonyManager.class
+ .getMethod("getSubIdForPhoneAccount", PhoneAccount.class);
+ return (int) method.invoke(telephonyManager, phoneAccount);
+ } catch (Exception e) {
+ VvmLog.e(TAG, "reflection call to getSubIdForPhoneAccount failed:", e);
+ }
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ public static String getNetworkSpecifierForPhoneAccountHandle(Context context,
+ PhoneAccountHandle phoneAccountHandle) {
+ return String.valueOf(SubscriptionManager.getDefaultDataSubscriptionId());
+ }
+
+ public static PersistableBundle getCarrirConfigForPhoneAccountHandle(Context context,
+ PhoneAccountHandle phoneAccountHandle) {
+ return context.getSystemService(CarrierConfigManager.class).getConfig();
+ }
+}
diff --git a/java/com/android/voicemailomtp/TelephonyVvmConfigManager.java b/java/com/android/voicemailomtp/TelephonyVvmConfigManager.java
new file mode 100644
index 000000000..ab13d36ad
--- /dev/null
+++ b/java/com/android/voicemailomtp/TelephonyVvmConfigManager.java
@@ -0,0 +1,154 @@
+/*
+ * 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.voicemailomtp;
+
+import android.content.res.Resources;
+import android.os.PersistableBundle;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.util.ArrayMap;
+import com.android.voicemailomtp.utils.XmlUtils;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Load and caches telephony vvm config from res/xml/vvm_config.xml
+ */
+public class TelephonyVvmConfigManager {
+
+ private static final String TAG = "TelephonyVvmCfgMgr";
+
+ private static final boolean USE_DEBUG_CONFIG = false;
+
+ private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
+
+ static final String KEY_MCCMNC = "mccmnc";
+
+ private static Map<String, PersistableBundle> sCachedConfigs;
+
+ private final Map<String, PersistableBundle> mConfigs;
+
+ public TelephonyVvmConfigManager(Resources resources) {
+ if (sCachedConfigs == null) {
+ sCachedConfigs = loadConfigs(resources.getXml(R.xml.vvm_config));
+ }
+ mConfigs = sCachedConfigs;
+ }
+
+ @VisibleForTesting
+ TelephonyVvmConfigManager(XmlPullParser parser) {
+ mConfigs = loadConfigs(parser);
+ }
+
+ @Nullable
+ public PersistableBundle getConfig(String mccMnc) {
+ if (USE_DEBUG_CONFIG) {
+ return mConfigs.get("TEST");
+ }
+ return mConfigs.get(mccMnc);
+ }
+
+ private static Map<String, PersistableBundle> loadConfigs(XmlPullParser parser) {
+ Map<String, PersistableBundle> configs = new ArrayMap<>();
+ try {
+ ArrayList list = readBundleList(parser);
+ for (Object object : list) {
+ if (!(object instanceof PersistableBundle)) {
+ throw new IllegalArgumentException("PersistableBundle expected, got " + object);
+ }
+ PersistableBundle bundle = (PersistableBundle) object;
+ String[] mccMncs = bundle.getStringArray(KEY_MCCMNC);
+ if (mccMncs == null) {
+ throw new IllegalArgumentException("MCCMNC is null");
+ }
+ for (String mccMnc : mccMncs) {
+ configs.put(mccMnc, bundle);
+ }
+ }
+ } catch (IOException | XmlPullParserException e) {
+ throw new RuntimeException(e);
+ }
+ return configs;
+ }
+
+ @Nullable
+ public static ArrayList readBundleList(XmlPullParser in) throws IOException,
+ XmlPullParserException {
+ final int outerDepth = in.getDepth();
+ int event;
+ while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+ (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
+ if (event == XmlPullParser.START_TAG) {
+ final String startTag = in.getName();
+ final String[] tagName = new String[1];
+ in.next();
+ return XmlUtils.readThisListXml(in, startTag, tagName,
+ new MyReadMapCallback(), false);
+ }
+ }
+ return null;
+ }
+
+ public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
+ XmlPullParserException {
+ final int outerDepth = in.getDepth();
+ final String startTag = in.getName();
+ final String[] tagName = new String[1];
+ int event;
+ while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+ (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
+ if (event == XmlPullParser.START_TAG) {
+ ArrayMap<String, ?> map =
+ XmlUtils.readThisArrayMapXml(in, startTag, tagName,
+ new MyReadMapCallback());
+ PersistableBundle result = new PersistableBundle();
+ for (Entry<String, ?> entry : map.entrySet()) {
+ Object value = entry.getValue();
+ if (value instanceof Integer) {
+ result.putInt(entry.getKey(), (int) value);
+ } else if (value instanceof Boolean) {
+ result.putBoolean(entry.getKey(), (boolean) value);
+ } else if (value instanceof String) {
+ result.putString(entry.getKey(), (String) value);
+ } else if (value instanceof String[]) {
+ result.putStringArray(entry.getKey(), (String[]) value);
+ } else if (value instanceof PersistableBundle) {
+ result.putPersistableBundle(entry.getKey(), (PersistableBundle) value);
+ }
+ }
+ return result;
+ }
+ }
+ return PersistableBundle.EMPTY;
+ }
+
+ static class MyReadMapCallback implements XmlUtils.ReadMapCallback {
+
+ @Override
+ public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
+ throws XmlPullParserException, IOException {
+ if (TAG_PERSISTABLEMAP.equals(tag)) {
+ return restoreFromXml(in);
+ }
+ throw new XmlPullParserException("Unknown tag=" + tag);
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/VisualVoicemailPreferences.java b/java/com/android/voicemailomtp/VisualVoicemailPreferences.java
new file mode 100644
index 000000000..5bc2c6951
--- /dev/null
+++ b/java/com/android/voicemailomtp/VisualVoicemailPreferences.java
@@ -0,0 +1,143 @@
+/*
+ * 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.voicemailomtp;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import java.util.Set;
+
+/**
+ * Save visual voicemail values in shared preferences to be retrieved later. Because a voicemail
+ * source is tied 1:1 to a phone account, the phone account handle is used in the key for each
+ * voicemail source and the associated data.
+ */
+public class VisualVoicemailPreferences {
+
+ private static final String VISUAL_VOICEMAIL_SHARED_PREFS_KEY_PREFIX =
+ "visual_voicemail_";
+
+ private final SharedPreferences mPreferences;
+ private final PhoneAccountHandle mPhoneAccountHandle;
+
+ public VisualVoicemailPreferences(Context context, PhoneAccountHandle phoneAccountHandle) {
+ mPreferences = PreferenceManager.getDefaultSharedPreferences(context);
+ mPhoneAccountHandle = phoneAccountHandle;
+ }
+
+ public class Editor {
+
+ private final SharedPreferences.Editor mEditor;
+
+ private Editor() {
+ mEditor = mPreferences.edit();
+ }
+
+ public void apply() {
+ mEditor.apply();
+ }
+
+ public Editor putBoolean(String key, boolean value) {
+ mEditor.putBoolean(getKey(key), value);
+ return this;
+ }
+
+ @NeededForTesting
+ public Editor putFloat(String key, float value) {
+ mEditor.putFloat(getKey(key), value);
+ return this;
+ }
+
+ public Editor putInt(String key, int value) {
+ mEditor.putInt(getKey(key), value);
+ return this;
+ }
+
+ @NeededForTesting
+ public Editor putLong(String key, long value) {
+ mEditor.putLong(getKey(key), value);
+ return this;
+ }
+
+ public Editor putString(String key, String value) {
+ mEditor.putString(getKey(key), value);
+ return this;
+ }
+
+ @NeededForTesting
+ public Editor putStringSet(String key, Set<String> value) {
+ mEditor.putStringSet(getKey(key), value);
+ return this;
+ }
+ }
+
+ public Editor edit() {
+ return new Editor();
+ }
+
+ public boolean getBoolean(String key, boolean defValue) {
+ return getValue(key, defValue);
+ }
+
+ @NeededForTesting
+ public float getFloat(String key, float defValue) {
+ return getValue(key, defValue);
+ }
+
+ public int getInt(String key, int defValue) {
+ return getValue(key, defValue);
+ }
+
+ @NeededForTesting
+ public long getLong(String key, long defValue) {
+ return getValue(key, defValue);
+ }
+
+ public String getString(String key, String defValue) {
+ return getValue(key, defValue);
+ }
+
+ @Nullable
+ public String getString(String key) {
+ return getValue(key, null);
+ }
+
+ @NeededForTesting
+ public Set<String> getStringSet(String key, Set<String> defValue) {
+ return getValue(key, defValue);
+ }
+
+ public boolean contains(String key) {
+ return mPreferences.contains(getKey(key));
+ }
+
+ private <T> T getValue(String key, T defValue) {
+ if (!contains(key)) {
+ return defValue;
+ }
+ Object object = mPreferences.getAll().get(getKey(key));
+ if (object == null) {
+ return defValue;
+ }
+ return (T) object;
+ }
+
+ private String getKey(String key) {
+ return VISUAL_VOICEMAIL_SHARED_PREFS_KEY_PREFIX + key + "_" + mPhoneAccountHandle.getId();
+ }
+}
diff --git a/java/com/android/voicemailomtp/Voicemail.java b/java/com/android/voicemailomtp/Voicemail.java
new file mode 100644
index 000000000..9d8395142
--- /dev/null
+++ b/java/com/android/voicemailomtp/Voicemail.java
@@ -0,0 +1,330 @@
+/*
+ * 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.voicemailomtp;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telecom.PhoneAccountHandle;
+import android.text.TextUtils;
+
+/**
+ * Represents a single voicemail stored in the voicemail content provider.
+ */
+public class Voicemail implements Parcelable {
+
+ private final Long mTimestamp;
+ private final String mNumber;
+ private final PhoneAccountHandle mPhoneAccount;
+ private final Long mId;
+ private final Long mDuration;
+ private final String mSource;
+ private final String mProviderData;
+ private final Uri mUri;
+ private final Boolean mIsRead;
+ private final Boolean mHasContent;
+ private final String mTranscription;
+
+ private Voicemail(Long timestamp, String number, PhoneAccountHandle phoneAccountHandle, Long id,
+ Long duration, String source, String providerData, Uri uri, Boolean isRead,
+ Boolean hasContent, String transcription) {
+ mTimestamp = timestamp;
+ mNumber = number;
+ mPhoneAccount = phoneAccountHandle;
+ mId = id;
+ mDuration = duration;
+ mSource = source;
+ mProviderData = providerData;
+ mUri = uri;
+ mIsRead = isRead;
+ mHasContent = hasContent;
+ mTranscription = transcription;
+ }
+
+ /**
+ * Create a {@link Builder} for a new {@link Voicemail} to be inserted. <p> The number and the
+ * timestamp are mandatory for insertion.
+ */
+ public static Builder createForInsertion(long timestamp, String number) {
+ return new Builder().setNumber(number).setTimestamp(timestamp);
+ }
+
+ /**
+ * Create a {@link Builder} for a {@link Voicemail} to be updated (or deleted). <p> The id and
+ * source data fields are mandatory for update - id is necessary for updating the database and
+ * source data is necessary for updating the server.
+ */
+ public static Builder createForUpdate(long id, String sourceData) {
+ return new Builder().setId(id).setSourceData(sourceData);
+ }
+
+ /**
+ * Builder pattern for creating a {@link Voicemail}. The builder must be created with the {@link
+ * #createForInsertion(long, String)} method. <p> This class is <b>not thread safe</b>
+ */
+ public static class Builder {
+
+ private Long mBuilderTimestamp;
+ private String mBuilderNumber;
+ private PhoneAccountHandle mBuilderPhoneAccount;
+ private Long mBuilderId;
+ private Long mBuilderDuration;
+ private String mBuilderSourcePackage;
+ private String mBuilderSourceData;
+ private Uri mBuilderUri;
+ private Boolean mBuilderIsRead;
+ private boolean mBuilderHasContent;
+ private String mBuilderTranscription;
+
+ /**
+ * You should use the correct factory method to construct a builder.
+ */
+ private Builder() {
+ }
+
+ public Builder setNumber(String number) {
+ mBuilderNumber = number;
+ return this;
+ }
+
+ public Builder setTimestamp(long timestamp) {
+ mBuilderTimestamp = timestamp;
+ return this;
+ }
+
+ public Builder setPhoneAccount(PhoneAccountHandle phoneAccount) {
+ mBuilderPhoneAccount = phoneAccount;
+ return this;
+ }
+
+ public Builder setId(long id) {
+ mBuilderId = id;
+ return this;
+ }
+
+ public Builder setDuration(long duration) {
+ mBuilderDuration = duration;
+ return this;
+ }
+
+ public Builder setSourcePackage(String sourcePackage) {
+ mBuilderSourcePackage = sourcePackage;
+ return this;
+ }
+
+ public Builder setSourceData(String sourceData) {
+ mBuilderSourceData = sourceData;
+ return this;
+ }
+
+ public Builder setUri(Uri uri) {
+ mBuilderUri = uri;
+ return this;
+ }
+
+ public Builder setIsRead(boolean isRead) {
+ mBuilderIsRead = isRead;
+ return this;
+ }
+
+ public Builder setHasContent(boolean hasContent) {
+ mBuilderHasContent = hasContent;
+ return this;
+ }
+
+ public Builder setTranscription(String transcription) {
+ mBuilderTranscription = transcription;
+ return this;
+ }
+
+ public Voicemail build() {
+ mBuilderId = mBuilderId == null ? -1 : mBuilderId;
+ mBuilderTimestamp = mBuilderTimestamp == null ? 0 : mBuilderTimestamp;
+ mBuilderDuration = mBuilderDuration == null ? 0 : mBuilderDuration;
+ mBuilderIsRead = mBuilderIsRead == null ? false : mBuilderIsRead;
+ return new Voicemail(mBuilderTimestamp, mBuilderNumber, mBuilderPhoneAccount,
+ mBuilderId, mBuilderDuration, mBuilderSourcePackage, mBuilderSourceData,
+ mBuilderUri, mBuilderIsRead, mBuilderHasContent, mBuilderTranscription);
+ }
+ }
+
+ /**
+ * The identifier of the voicemail in the content provider. <p> This may be missing in the case
+ * of a new {@link Voicemail} that we plan to insert into the content provider, since until it
+ * has been inserted we don't know what id it should have. If none is specified, we return -1.
+ */
+ public long getId() {
+ return mId;
+ }
+
+ /**
+ * The number of the person leaving the voicemail, empty string if unknown, null if not set.
+ */
+ public String getNumber() {
+ return mNumber;
+ }
+
+ /**
+ * The phone account associated with the voicemail, null if not set.
+ */
+ public PhoneAccountHandle getPhoneAccount() {
+ return mPhoneAccount;
+ }
+
+ /**
+ * The timestamp the voicemail was received, in millis since the epoch, zero if not set.
+ */
+ public long getTimestampMillis() {
+ return mTimestamp;
+ }
+
+ /**
+ * Gets the duration of the voicemail in millis, or zero if the field is not set.
+ */
+ public long getDuration() {
+ return mDuration;
+ }
+
+ /**
+ * Returns the package name of the source that added this voicemail, or null if this field is
+ * not set.
+ */
+ public String getSourcePackage() {
+ return mSource;
+ }
+
+ /**
+ * Returns the application-specific data type stored with the voicemail, or null if this field
+ * is not set. <p> Source data is typically used as an identifier to uniquely identify the
+ * voicemail against the voicemail server. This is likely to be something like the IMAP UID, or
+ * some other server-generated identifying string.
+ */
+ public String getSourceData() {
+ return mProviderData;
+ }
+
+ /**
+ * Gets the Uri that can be used to refer to this voicemail, and to make it play. <p> Returns
+ * null if we don't know the Uri.
+ */
+ public Uri getUri() {
+ return mUri;
+ }
+
+ /**
+ * Tells us if the voicemail message has been marked as read. <p> Always returns false if this
+ * field has not been set, i.e. if hasRead() returns false.
+ */
+ public boolean isRead() {
+ return mIsRead;
+ }
+
+ /**
+ * Tells us if there is content stored at the Uri.
+ */
+ public boolean hasContent() {
+ return mHasContent;
+ }
+
+ /**
+ * Returns the text transcription of this voicemail, or null if this field is not set.
+ */
+ public String getTranscription() {
+ return mTranscription;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mTimestamp);
+ writeCharSequence(dest, mNumber);
+ if (mPhoneAccount == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ mPhoneAccount.writeToParcel(dest, flags);
+ }
+ dest.writeLong(mId);
+ dest.writeLong(mDuration);
+ writeCharSequence(dest, mSource);
+ writeCharSequence(dest, mProviderData);
+ if (mUri == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ mUri.writeToParcel(dest, flags);
+ }
+ if (mIsRead) {
+ dest.writeInt(1);
+ } else {
+ dest.writeInt(0);
+ }
+ if (mHasContent) {
+ dest.writeInt(1);
+ } else {
+ dest.writeInt(0);
+ }
+ writeCharSequence(dest, mTranscription);
+ }
+
+ public static final Creator<Voicemail> CREATOR
+ = new Creator<Voicemail>() {
+ @Override
+ public Voicemail createFromParcel(Parcel in) {
+ return new Voicemail(in);
+ }
+
+ @Override
+ public Voicemail[] newArray(int size) {
+ return new Voicemail[size];
+ }
+ };
+
+ private Voicemail(Parcel in) {
+ mTimestamp = in.readLong();
+ mNumber = (String) readCharSequence(in);
+ if (in.readInt() > 0) {
+ mPhoneAccount = PhoneAccountHandle.CREATOR.createFromParcel(in);
+ } else {
+ mPhoneAccount = null;
+ }
+ mId = in.readLong();
+ mDuration = in.readLong();
+ mSource = (String) readCharSequence(in);
+ mProviderData = (String) readCharSequence(in);
+ if (in.readInt() > 0) {
+ mUri = Uri.CREATOR.createFromParcel(in);
+ } else {
+ mUri = null;
+ }
+ mIsRead = in.readInt() > 0 ? true : false;
+ mHasContent = in.readInt() > 0 ? true : false;
+ mTranscription = (String) readCharSequence(in);
+ }
+
+ private static CharSequence readCharSequence(Parcel in) {
+ return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ }
+
+ public static void writeCharSequence(Parcel dest, CharSequence val) {
+ TextUtils.writeToParcel(val, dest, 0);
+ }
+}
diff --git a/java/com/android/voicemailomtp/VoicemailStatus.java b/java/com/android/voicemailomtp/VoicemailStatus.java
new file mode 100644
index 000000000..63007932e
--- /dev/null
+++ b/java/com/android/voicemailomtp/VoicemailStatus.java
@@ -0,0 +1,158 @@
+/*
+ * 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.voicemailomtp;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.provider.VoicemailContract;
+import android.provider.VoicemailContract.Status;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+
+public class VoicemailStatus {
+
+ private static final String TAG = "VvmStatus";
+
+ public static class Editor {
+
+ private final Context mContext;
+ @Nullable
+ private final PhoneAccountHandle mPhoneAccountHandle;
+
+ private ContentValues mValues = new ContentValues();
+
+ private Editor(Context context, PhoneAccountHandle phoneAccountHandle) {
+ mContext = context;
+ mPhoneAccountHandle = phoneAccountHandle;
+ if (mPhoneAccountHandle == null) {
+ VvmLog.w(TAG, "VoicemailStatus.Editor created with null phone account, status will"
+ + " not be written");
+ }
+ }
+
+ @Nullable
+ public PhoneAccountHandle getPhoneAccountHandle() {
+ return mPhoneAccountHandle;
+ }
+
+ public Editor setType(String type) {
+ mValues.put(Status.SOURCE_TYPE, type);
+ return this;
+ }
+
+ public Editor setConfigurationState(int configurationState) {
+ mValues.put(Status.CONFIGURATION_STATE, configurationState);
+ return this;
+ }
+
+ public Editor setDataChannelState(int dataChannelState) {
+ mValues.put(Status.DATA_CHANNEL_STATE, dataChannelState);
+ return this;
+ }
+
+ public Editor setNotificationChannelState(int notificationChannelState) {
+ mValues.put(Status.NOTIFICATION_CHANNEL_STATE, notificationChannelState);
+ return this;
+ }
+
+ public Editor setQuota(int occupied, int total) {
+ if (occupied == VoicemailContract.Status.QUOTA_UNAVAILABLE
+ && total == VoicemailContract.Status.QUOTA_UNAVAILABLE) {
+ return this;
+ }
+
+ mValues.put(Status.QUOTA_OCCUPIED, occupied);
+ mValues.put(Status.QUOTA_TOTAL, total);
+ return this;
+ }
+
+ /**
+ * Apply the changes to the {@link VoicemailStatus} {@link #Editor}.
+ *
+ * @return {@code true} if the changes were successfully applied, {@code false} otherwise.
+ */
+ public boolean apply() {
+ if (mPhoneAccountHandle == null) {
+ return false;
+ }
+ mValues.put(Status.PHONE_ACCOUNT_COMPONENT_NAME,
+ mPhoneAccountHandle.getComponentName().flattenToString());
+ mValues.put(Status.PHONE_ACCOUNT_ID, mPhoneAccountHandle.getId());
+ ContentResolver contentResolver = mContext.getContentResolver();
+ Uri statusUri = VoicemailContract.Status.buildSourceUri(mContext.getPackageName());
+ try {
+ contentResolver.insert(statusUri, mValues);
+ } catch (IllegalArgumentException iae) {
+ VvmLog.e(TAG, "apply :: failed to insert content resolver ", iae);
+ mValues.clear();
+ return false;
+ }
+ mValues.clear();
+ return true;
+ }
+
+ public ContentValues getValues() {
+ return mValues;
+ }
+ }
+
+ /**
+ * A voicemail status editor that the decision of whether to actually write to the database can
+ * be deferred. This object will be passed around as a usual {@link Editor}, but {@link
+ * #apply()} doesn't do anything. If later the creator of this object decides any status changes
+ * written to it should be committed, {@link #deferredApply()} should be called.
+ */
+ public static class DeferredEditor extends Editor {
+
+ private DeferredEditor(Context context, PhoneAccountHandle phoneAccountHandle) {
+ super(context, phoneAccountHandle);
+ }
+
+ @Override
+ public boolean apply() {
+ // Do nothing
+ return true;
+ }
+
+ public void deferredApply() {
+ super.apply();
+ }
+ }
+
+ public static Editor edit(Context context, PhoneAccountHandle phoneAccountHandle) {
+ return new Editor(context, phoneAccountHandle);
+ }
+
+ /**
+ * Reset the status to the "disabled" state, which the UI should not show anything for this
+ * phoneAccountHandle.
+ */
+ public static void disable(Context context, PhoneAccountHandle phoneAccountHandle) {
+ edit(context, phoneAccountHandle)
+ .setConfigurationState(Status.CONFIGURATION_STATE_NOT_CONFIGURED)
+ .setDataChannelState(Status.DATA_CHANNEL_STATE_NO_CONNECTION)
+ .setNotificationChannelState(Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION)
+ .apply();
+ }
+
+ public static DeferredEditor deferredEdit(Context context,
+ PhoneAccountHandle phoneAccountHandle) {
+ return new DeferredEditor(context, phoneAccountHandle);
+ }
+}
diff --git a/java/com/android/voicemailomtp/VvmLog.java b/java/com/android/voicemailomtp/VvmLog.java
new file mode 100644
index 000000000..2add66a53
--- /dev/null
+++ b/java/com/android/voicemailomtp/VvmLog.java
@@ -0,0 +1,179 @@
+/*
+ * 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.voicemailomtp;
+
+import android.util.Log;
+import com.android.voicemailomtp.utils.IndentingPrintWriter;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayDeque;
+import java.util.Calendar;
+import java.util.Deque;
+import java.util.Iterator;
+
+/**
+ * Helper methods for adding to OMTP visual voicemail local logs.
+ */
+public class VvmLog {
+
+ private static final int MAX_OMTP_VVM_LOGS = 100;
+
+ private static final LocalLog sLocalLog = new LocalLog(MAX_OMTP_VVM_LOGS);
+
+ public static void log(String tag, String log) {
+ sLocalLog.log(tag + ": " + log);
+ }
+
+ public static void dump(FileDescriptor fd, PrintWriter printwriter, String[] args) {
+ IndentingPrintWriter indentingPrintWriter = new IndentingPrintWriter(printwriter, " ");
+ indentingPrintWriter.increaseIndent();
+ sLocalLog.dump(fd, indentingPrintWriter, args);
+ indentingPrintWriter.decreaseIndent();
+ }
+
+ public static int e(String tag, String log) {
+ log(tag, log);
+ return Log.e(tag, log);
+ }
+
+ public static int e(String tag, String log, Throwable e) {
+ log(tag, log + " " + e);
+ return Log.e(tag, log, e);
+ }
+
+ public static int w(String tag, String log) {
+ log(tag, log);
+ return Log.w(tag, log);
+ }
+
+ public static int w(String tag, String log, Throwable e) {
+ log(tag, log + " " + e);
+ return Log.w(tag, log, e);
+ }
+
+ public static int i(String tag, String log) {
+ log(tag, log);
+ return Log.i(tag, log);
+ }
+
+ public static int i(String tag, String log, Throwable e) {
+ log(tag, log + " " + e);
+ return Log.i(tag, log, e);
+ }
+
+ public static int d(String tag, String log) {
+ log(tag, log);
+ return Log.d(tag, log);
+ }
+
+ public static int d(String tag, String log, Throwable e) {
+ log(tag, log + " " + e);
+ return Log.d(tag, log, e);
+ }
+
+ public static int v(String tag, String log) {
+ log(tag, log);
+ return Log.v(tag, log);
+ }
+
+ public static int v(String tag, String log, Throwable e) {
+ log(tag, log + " " + e);
+ return Log.v(tag, log, e);
+ }
+
+ public static int wtf(String tag, String log) {
+ log(tag, log);
+ return Log.wtf(tag, log);
+ }
+
+ public static int wtf(String tag, String log, Throwable e) {
+ log(tag, log + " " + e);
+ return Log.wtf(tag, log, e);
+ }
+
+ /**
+ * Redact personally identifiable information for production users. If we are running in verbose
+ * mode, return the original string, otherwise return a SHA-1 hash of the input string.
+ */
+ public static String pii(Object pii) {
+ if (pii == null) {
+ return String.valueOf(pii);
+ }
+ return "[PII]";
+ }
+
+ public static class LocalLog {
+
+ private final Deque<String> mLog;
+ private final int mMaxLines;
+
+ public LocalLog(int maxLines) {
+ mMaxLines = Math.max(0, maxLines);
+ mLog = new ArrayDeque<>(mMaxLines);
+ }
+
+ public void log(String msg) {
+ if (mMaxLines <= 0) {
+ return;
+ }
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(System.currentTimeMillis());
+ append(String.format("%tm-%td %tH:%tM:%tS.%tL - %s", c, c, c, c, c, c, msg));
+ }
+
+ private synchronized void append(String logLine) {
+ while (mLog.size() >= mMaxLines) {
+ mLog.remove();
+ }
+ mLog.add(logLine);
+ }
+
+ public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ Iterator<String> itr = mLog.iterator();
+ while (itr.hasNext()) {
+ pw.println(itr.next());
+ }
+ }
+
+ public synchronized void reverseDump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ Iterator<String> itr = mLog.descendingIterator();
+ while (itr.hasNext()) {
+ pw.println(itr.next());
+ }
+ }
+
+ public static class ReadOnlyLocalLog {
+
+ private final LocalLog mLog;
+
+ ReadOnlyLocalLog(LocalLog log) {
+ mLog = log;
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mLog.dump(fd, pw, args);
+ }
+
+ public void reverseDump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mLog.reverseDump(fd, pw, args);
+ }
+ }
+
+ public ReadOnlyLocalLog readOnlyLocalLog() {
+ return new ReadOnlyLocalLog(this);
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/VvmPackageInstallReceiver.java b/java/com/android/voicemailomtp/VvmPackageInstallReceiver.java
new file mode 100644
index 000000000..7d9eee9f8
--- /dev/null
+++ b/java/com/android/voicemailomtp/VvmPackageInstallReceiver.java
@@ -0,0 +1,70 @@
+/*
+ * 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.voicemailomtp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.voicemailomtp.settings.VisualVoicemailSettingsUtil;
+import com.android.voicemailomtp.sync.OmtpVvmSourceManager;
+
+import java.util.Set;
+
+/**
+ * When a new package is installed, check if it matches any of the vvm carrier apps of the currently
+ * enabled dialer vvm sources.
+ */
+public class VvmPackageInstallReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "VvmPkgInstallReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getData() == null) {
+ return;
+ }
+
+ String packageName = intent.getData().getSchemeSpecificPart();
+ if (packageName == null) {
+ return;
+ }
+
+ OmtpVvmSourceManager vvmSourceManager = OmtpVvmSourceManager.getInstance(context);
+ Set<PhoneAccountHandle> phoneAccounts = vvmSourceManager.getOmtpVvmSources();
+ for (PhoneAccountHandle phoneAccount : phoneAccounts) {
+ if (VisualVoicemailSettingsUtil.isEnabledUserSet(context, phoneAccount)) {
+ // Skip the check if this voicemail source's setting is overridden by the user.
+ continue;
+ }
+
+ OmtpVvmCarrierConfigHelper carrierConfigHelper = new OmtpVvmCarrierConfigHelper(
+ context, phoneAccount);
+ if (carrierConfigHelper.getCarrierVvmPackageNames() == null) {
+ continue;
+ }
+ if (carrierConfigHelper.getCarrierVvmPackageNames().contains(packageName)) {
+ // Force deactivate the client. The user can re-enable it in the settings.
+ // There are no need to update the settings for deactivation. At this point, if the
+ // default value is used it should be false because a carrier package is present.
+ VvmLog.i(TAG, "Carrier VVM package installed, disabling system VVM client");
+ OmtpVvmSourceManager.getInstance(context).removeSource(phoneAccount);
+ carrierConfigHelper.startDeactivation();
+ }
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/VvmPhoneStateListener.java b/java/com/android/voicemailomtp/VvmPhoneStateListener.java
new file mode 100644
index 000000000..1a3013d1f
--- /dev/null
+++ b/java/com/android/voicemailomtp/VvmPhoneStateListener.java
@@ -0,0 +1,103 @@
+/*
+ * 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.voicemailomtp;
+
+import android.content.Context;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+
+import com.android.voicemailomtp.sync.OmtpVvmSourceManager;
+import com.android.voicemailomtp.sync.OmtpVvmSyncService;
+import com.android.voicemailomtp.sync.SyncTask;
+import com.android.voicemailomtp.sync.VoicemailStatusQueryHelper;
+
+/**
+ * Check if service is lost and indicate this in the voicemail status.
+ */
+public class VvmPhoneStateListener extends PhoneStateListener {
+
+ private static final String TAG = "VvmPhoneStateListener";
+
+ private PhoneAccountHandle mPhoneAccount;
+ private Context mContext;
+ private int mPreviousState = -1;
+
+ public VvmPhoneStateListener(Context context, PhoneAccountHandle accountHandle) {
+ // TODO: b/32637799 too much trouble to call super constructor through reflection,
+ // just use non-phoneAccountHandle version for now.
+ super();
+ mContext = context;
+ mPhoneAccount = accountHandle;
+ }
+
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ if (mPhoneAccount == null) {
+ VvmLog.e(TAG, "onServiceStateChanged on phoneAccount " + mPhoneAccount
+ + " with invalid phoneAccountHandle, ignoring");
+ return;
+ }
+
+ int state = serviceState.getState();
+ if (state == mPreviousState || (state != ServiceState.STATE_IN_SERVICE
+ && mPreviousState != ServiceState.STATE_IN_SERVICE)) {
+ // Only interested in state changes or transitioning into or out of "in service".
+ // Otherwise just quit.
+ mPreviousState = state;
+ return;
+ }
+
+ OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(mContext, mPhoneAccount);
+
+ if (state == ServiceState.STATE_IN_SERVICE) {
+ VoicemailStatusQueryHelper voicemailStatusQueryHelper =
+ new VoicemailStatusQueryHelper(mContext);
+ if (voicemailStatusQueryHelper.isVoicemailSourceConfigured(mPhoneAccount)) {
+ if (!voicemailStatusQueryHelper.isNotificationsChannelActive(mPhoneAccount)) {
+ VvmLog
+ .v(TAG, "Notifications channel is active for " + mPhoneAccount);
+ helper.handleEvent(VoicemailStatus.edit(mContext, mPhoneAccount),
+ OmtpEvents.NOTIFICATION_IN_SERVICE);
+ }
+ }
+
+ if (OmtpVvmSourceManager.getInstance(mContext).isVvmSourceRegistered(mPhoneAccount)) {
+ VvmLog
+ .v(TAG, "Signal returned: requesting resync for " + mPhoneAccount);
+ // If the source is already registered, run a full sync in case something was missed
+ // while signal was down.
+ SyncTask.start(mContext, mPhoneAccount, OmtpVvmSyncService.SYNC_FULL_SYNC);
+ } else {
+ VvmLog.v(TAG,
+ "Signal returned: reattempting activation for " + mPhoneAccount);
+ // Otherwise initiate an activation because this means that an OMTP source was
+ // recognized but either the activation text was not successfully sent or a response
+ // was not received.
+ helper.startActivation();
+ }
+ } else {
+ VvmLog.v(TAG, "Notifications channel is inactive for " + mPhoneAccount);
+
+ if (!OmtpVvmSourceManager.getInstance(mContext).isVvmSourceRegistered(mPhoneAccount)) {
+ return;
+ }
+ helper.handleEvent(VoicemailStatus.edit(mContext, mPhoneAccount),
+ OmtpEvents.NOTIFICATION_SERVICE_LOST);
+ }
+ mPreviousState = state;
+ }
+}
diff --git a/java/com/android/voicemailomtp/fetch/FetchVoicemailReceiver.java b/java/com/android/voicemailomtp/fetch/FetchVoicemailReceiver.java
new file mode 100644
index 000000000..85fea80d7
--- /dev/null
+++ b/java/com/android/voicemailomtp/fetch/FetchVoicemailReceiver.java
@@ -0,0 +1,219 @@
+/*
+ * 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.voicemailomtp.fetch;
+
+import android.annotation.TargetApi;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Network;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.provider.VoicemailContract;
+import android.provider.VoicemailContract.Voicemails;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.os.BuildCompat;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import com.android.voicemailomtp.VoicemailStatus;
+import com.android.voicemailomtp.VvmLog;
+import com.android.voicemailomtp.imap.ImapHelper;
+import com.android.voicemailomtp.imap.ImapHelper.InitializingException;
+import com.android.voicemailomtp.sync.OmtpVvmSourceManager;
+import com.android.voicemailomtp.sync.VvmNetworkRequestCallback;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+@TargetApi(VERSION_CODES.CUR_DEVELOPMENT)
+public class FetchVoicemailReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "FetchVoicemailReceiver";
+
+ final static String[] PROJECTION = new String[]{
+ Voicemails.SOURCE_DATA, // 0
+ Voicemails.PHONE_ACCOUNT_ID, // 1
+ Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, // 2
+ };
+
+ public static final int SOURCE_DATA = 0;
+ public static final int PHONE_ACCOUNT_ID = 1;
+ public static final int PHONE_ACCOUNT_COMPONENT_NAME = 2;
+
+ // Number of retries
+ private static final int NETWORK_RETRY_COUNT = 3;
+
+ private ContentResolver mContentResolver;
+ private Uri mUri;
+ private VvmNetworkRequestCallback mNetworkCallback;
+ private Context mContext;
+ private String mUid;
+ private PhoneAccountHandle mPhoneAccount;
+ private int mRetryCount = NETWORK_RETRY_COUNT;
+
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ if (VoicemailContract.ACTION_FETCH_VOICEMAIL.equals(intent.getAction())) {
+ VvmLog.i(TAG, "ACTION_FETCH_VOICEMAIL received");
+ mContext = context;
+ mContentResolver = context.getContentResolver();
+ mUri = intent.getData();
+
+ if (mUri == null) {
+ VvmLog.w(TAG,
+ VoicemailContract.ACTION_FETCH_VOICEMAIL + " intent sent with no data");
+ return;
+ }
+
+ if (!context.getPackageName().equals(
+ mUri.getQueryParameter(VoicemailContract.PARAM_KEY_SOURCE_PACKAGE))) {
+ // Ignore if the fetch request is for a voicemail not from this package.
+ VvmLog.e(TAG,
+ "ACTION_FETCH_VOICEMAIL from foreign pacakge " + context.getPackageName());
+ return;
+ }
+
+ Cursor cursor = mContentResolver.query(mUri, PROJECTION, null, null, null);
+ if (cursor == null) {
+ VvmLog.i(TAG, "ACTION_FETCH_VOICEMAIL query returned null");
+ return;
+ }
+ try {
+ if (cursor.moveToFirst()) {
+ mUid = cursor.getString(SOURCE_DATA);
+ String accountId = cursor.getString(PHONE_ACCOUNT_ID);
+ if (TextUtils.isEmpty(accountId)) {
+ TelephonyManager telephonyManager = (TelephonyManager)
+ context.getSystemService(Context.TELEPHONY_SERVICE);
+ accountId = telephonyManager.getSimSerialNumber();
+
+ if (TextUtils.isEmpty(accountId)) {
+ VvmLog.e(TAG, "Account null and no default sim found.");
+ return;
+ }
+ }
+
+ mPhoneAccount = new PhoneAccountHandle(
+ ComponentName.unflattenFromString(
+ cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME)),
+ cursor.getString(PHONE_ACCOUNT_ID));
+ if (!OmtpVvmSourceManager.getInstance(context)
+ .isVvmSourceRegistered(mPhoneAccount)) {
+ mPhoneAccount = getAccountFromMarshmallowAccount(context, mPhoneAccount);
+ if (mPhoneAccount == null) {
+ VvmLog.w(TAG, "Account not registered - cannot retrieve message.");
+ return;
+ }
+ VvmLog.i(TAG, "Fetching voicemail with Marshmallow PhoneAccountHandle");
+ }
+ VvmLog.i(TAG, "Requesting network to fetch voicemail");
+ mNetworkCallback = new fetchVoicemailNetworkRequestCallback(context,
+ mPhoneAccount);
+ mNetworkCallback.requestNetwork();
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+
+ /**
+ * In ag/930496 the format of PhoneAccountHandle has changed between Marshmallow and Nougat.
+ * This method attempts to search the account from the old database in registered sources using
+ * the old format. There's a chance of M phone account collisions on multi-SIM devices, but
+ * visual voicemail is not supported on M multi-SIM.
+ */
+ @Nullable
+ private static PhoneAccountHandle getAccountFromMarshmallowAccount(Context context,
+ PhoneAccountHandle oldAccount) {
+ if (!BuildCompat.isAtLeastN()) {
+ return null;
+ }
+ for (PhoneAccountHandle handle : OmtpVvmSourceManager.getInstance(context)
+ .getOmtpVvmSources()) {
+ if (getIccSerialNumberFromFullIccSerialNumber(handle.getId())
+ .equals(oldAccount.getId())) {
+ return handle;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * getIccSerialNumber() is used for ID before N, and getFullIccSerialNumber() after.
+ * getIccSerialNumber() stops at the first hex char.
+ */
+ @NonNull
+ private static String getIccSerialNumberFromFullIccSerialNumber(@NonNull String id) {
+ for(int i =0;i<id.length();i++){
+ if(!Character.isDigit(id.charAt(i))){
+ return id.substring(0,i);
+ }
+ }
+ return id;
+ }
+
+ private class fetchVoicemailNetworkRequestCallback extends VvmNetworkRequestCallback {
+
+ public fetchVoicemailNetworkRequestCallback(Context context,
+ PhoneAccountHandle phoneAccount) {
+ super(context, phoneAccount, VoicemailStatus.edit(context, phoneAccount));
+ }
+
+ @Override
+ public void onAvailable(final Network network) {
+ super.onAvailable(network);
+ fetchVoicemail(network, getVoicemailStatusEditor());
+ }
+ }
+
+ private void fetchVoicemail(final Network network, final VoicemailStatus.Editor status) {
+ Executor executor = Executors.newCachedThreadPool();
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ while (mRetryCount > 0) {
+ VvmLog.i(TAG, "fetching voicemail, retry count=" + mRetryCount);
+ try (ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount,
+ network, status)) {
+ boolean success = imapHelper.fetchVoicemailPayload(
+ new VoicemailFetchedCallback(mContext, mUri, mPhoneAccount),
+ mUid);
+ if (!success && mRetryCount > 0) {
+ VvmLog.i(TAG, "fetch voicemail failed, retrying");
+ mRetryCount--;
+ } else {
+ return;
+ }
+ } catch (InitializingException e) {
+ VvmLog.w(TAG, "Can't retrieve Imap credentials ", e);
+ return;
+ }
+ }
+ } finally {
+ if (mNetworkCallback != null) {
+ mNetworkCallback.releaseNetwork();
+ }
+ }
+ }
+ });
+ }
+}
diff --git a/java/com/android/voicemailomtp/fetch/VoicemailFetchedCallback.java b/java/com/android/voicemailomtp/fetch/VoicemailFetchedCallback.java
new file mode 100644
index 000000000..7479c4c4e
--- /dev/null
+++ b/java/com/android/voicemailomtp/fetch/VoicemailFetchedCallback.java
@@ -0,0 +1,101 @@
+/*
+ * 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.voicemailomtp.fetch;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.provider.VoicemailContract.Voicemails;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import com.android.voicemailomtp.R;
+import com.android.voicemailomtp.VvmLog;
+import com.android.voicemailomtp.imap.VoicemailPayload;
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.commons.io.IOUtils;
+
+/**
+ * Callback for when a voicemail payload is fetched. It copies the returned stream to the data
+ * file corresponding to the voicemail.
+ */
+public class VoicemailFetchedCallback {
+ private static final String TAG = "VoicemailFetchedCallback";
+
+ private final Context mContext;
+ private final ContentResolver mContentResolver;
+ private final Uri mUri;
+ private final PhoneAccountHandle mPhoneAccountHandle;
+
+ public VoicemailFetchedCallback(Context context, Uri uri,
+ PhoneAccountHandle phoneAccountHandle) {
+ mContext = context;
+ mContentResolver = context.getContentResolver();
+ mUri = uri;
+ mPhoneAccountHandle = phoneAccountHandle;
+ }
+
+ /**
+ * Saves the voicemail payload data into the voicemail provider then sets the "has_content" bit
+ * of the voicemail to "1".
+ *
+ * @param voicemailPayload The object containing the content data for the voicemail
+ */
+ public void setVoicemailContent(@Nullable VoicemailPayload voicemailPayload) {
+ if (voicemailPayload == null) {
+ VvmLog.i(TAG, "Payload not found, message has unsupported format");
+ ContentValues values = new ContentValues();
+ values.put(Voicemails.TRANSCRIPTION,
+ mContext.getString(R.string.vvm_unsupported_message_format,
+ mContext.getSystemService(TelecomManager.class)
+ .getVoiceMailNumber(mPhoneAccountHandle)));
+ updateVoicemail(values);
+ return;
+ }
+
+ VvmLog.d(TAG, String.format("Writing new voicemail content: %s", mUri));
+ OutputStream outputStream = null;
+
+ try {
+ outputStream = mContentResolver.openOutputStream(mUri);
+ byte[] inputBytes = voicemailPayload.getBytes();
+ if (inputBytes != null) {
+ outputStream.write(inputBytes);
+ }
+ } catch (IOException e) {
+ VvmLog.w(TAG, String.format("File not found for %s", mUri));
+ return;
+ } finally {
+ IOUtils.closeQuietly(outputStream);
+ }
+
+ // Update mime_type & has_content after we are done with file update.
+ ContentValues values = new ContentValues();
+ values.put(Voicemails.MIME_TYPE, voicemailPayload.getMimeType());
+ values.put(Voicemails.HAS_CONTENT, true);
+ updateVoicemail(values);
+ }
+
+ private void updateVoicemail(ContentValues values) {
+ int updatedCount = mContentResolver.update(mUri, values, null, null);
+ if (updatedCount != 1) {
+ VvmLog
+ .e(TAG, "Updating voicemail should have updated 1 row, was: " + updatedCount);
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/imap/ImapHelper.java b/java/com/android/voicemailomtp/imap/ImapHelper.java
new file mode 100644
index 000000000..b2a40fb64
--- /dev/null
+++ b/java/com/android/voicemailomtp/imap/ImapHelper.java
@@ -0,0 +1,711 @@
+/*
+ * 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.voicemailomtp.imap;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkInfo;
+import android.provider.VoicemailContract;
+import android.telecom.PhoneAccountHandle;
+import android.util.Base64;
+
+import com.android.voicemailomtp.OmtpConstants;
+import com.android.voicemailomtp.OmtpConstants.ChangePinResult;
+import com.android.voicemailomtp.OmtpEvents;
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.VisualVoicemailPreferences;
+import com.android.voicemailomtp.Voicemail;
+import com.android.voicemailomtp.VoicemailStatus;
+import com.android.voicemailomtp.VvmLog;
+import com.android.voicemailomtp.fetch.VoicemailFetchedCallback;
+import com.android.voicemailomtp.mail.Address;
+import com.android.voicemailomtp.mail.Body;
+import com.android.voicemailomtp.mail.BodyPart;
+import com.android.voicemailomtp.mail.FetchProfile;
+import com.android.voicemailomtp.mail.Flag;
+import com.android.voicemailomtp.mail.Message;
+import com.android.voicemailomtp.mail.MessagingException;
+import com.android.voicemailomtp.mail.Multipart;
+import com.android.voicemailomtp.mail.TempDirectory;
+import com.android.voicemailomtp.mail.internet.MimeMessage;
+import com.android.voicemailomtp.mail.store.ImapConnection;
+import com.android.voicemailomtp.mail.store.ImapFolder;
+import com.android.voicemailomtp.mail.store.ImapStore;
+import com.android.voicemailomtp.mail.store.imap.ImapConstants;
+import com.android.voicemailomtp.mail.store.imap.ImapResponse;
+import com.android.voicemailomtp.mail.utils.LogUtils;
+import com.android.voicemailomtp.sync.OmtpVvmSyncService.TranscriptionFetchedCallback;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import org.apache.commons.io.IOUtils;
+
+/**
+ * A helper interface to abstract commands sent across IMAP interface for a given account.
+ */
+public class ImapHelper implements Closeable {
+
+ private static final String TAG = "ImapHelper";
+
+ private ImapFolder mFolder;
+ private ImapStore mImapStore;
+
+ private final Context mContext;
+ private final PhoneAccountHandle mPhoneAccount;
+ private final Network mNetwork;
+ private final VoicemailStatus.Editor mStatus;
+
+ VisualVoicemailPreferences mPrefs;
+ private static final String PREF_KEY_QUOTA_OCCUPIED = "quota_occupied_";
+ private static final String PREF_KEY_QUOTA_TOTAL = "quota_total_";
+
+ private int mQuotaOccupied;
+ private int mQuotaTotal;
+
+ private final OmtpVvmCarrierConfigHelper mConfig;
+
+ public class InitializingException extends Exception {
+
+ public InitializingException(String message) {
+ super(message);
+ }
+ }
+
+ public ImapHelper(Context context, PhoneAccountHandle phoneAccount, Network network,
+ VoicemailStatus.Editor status)
+ throws InitializingException {
+ this(context,
+ new OmtpVvmCarrierConfigHelper(
+ context,
+ phoneAccount),
+ phoneAccount,
+ network,
+ status);
+ }
+
+ public ImapHelper(Context context, OmtpVvmCarrierConfigHelper config,
+ PhoneAccountHandle phoneAccount, Network network, VoicemailStatus.Editor status)
+ throws InitializingException {
+ mContext = context;
+ mPhoneAccount = phoneAccount;
+ mNetwork = network;
+ mStatus = status;
+ mConfig = config;
+ mPrefs = new VisualVoicemailPreferences(context,
+ phoneAccount);
+
+ try {
+ TempDirectory.setTempDirectory(context);
+
+ String username = mPrefs.getString(OmtpConstants.IMAP_USER_NAME, null);
+ String password = mPrefs.getString(OmtpConstants.IMAP_PASSWORD, null);
+ String serverName = mPrefs.getString(OmtpConstants.SERVER_ADDRESS, null);
+ int port = Integer.parseInt(
+ mPrefs.getString(OmtpConstants.IMAP_PORT, null));
+ int auth = ImapStore.FLAG_NONE;
+
+ int sslPort = mConfig.getSslPort();
+ if (sslPort != 0) {
+ port = sslPort;
+ auth = ImapStore.FLAG_SSL;
+ }
+
+ mImapStore = new ImapStore(
+ context, this, username, password, port, serverName, auth, network);
+ } catch (NumberFormatException e) {
+ handleEvent(OmtpEvents.DATA_INVALID_PORT);
+ LogUtils.w(TAG, "Could not parse port number");
+ throw new InitializingException("cannot initialize ImapHelper:" + e.toString());
+ }
+
+ mQuotaOccupied = mPrefs
+ .getInt(PREF_KEY_QUOTA_OCCUPIED, VoicemailContract.Status.QUOTA_UNAVAILABLE);
+ mQuotaTotal = mPrefs
+ .getInt(PREF_KEY_QUOTA_TOTAL, VoicemailContract.Status.QUOTA_UNAVAILABLE);
+ }
+
+ @Override
+ public void close() {
+ mImapStore.closeConnection();
+ }
+
+ public boolean isRoaming() {
+ ConnectivityManager connectivityManager = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ NetworkInfo info = connectivityManager.getNetworkInfo(mNetwork);
+ if (info == null) {
+ return false;
+ }
+ return info.isRoaming();
+ }
+
+ public OmtpVvmCarrierConfigHelper getConfig() {
+ return mConfig;
+ }
+
+ public ImapConnection connect() {
+ return mImapStore.getConnection();
+ }
+
+ /**
+ * The caller thread will block until the method returns.
+ */
+ public boolean markMessagesAsRead(List<Voicemail> voicemails) {
+ return setFlags(voicemails, Flag.SEEN);
+ }
+
+ /**
+ * The caller thread will block until the method returns.
+ */
+ public boolean markMessagesAsDeleted(List<Voicemail> voicemails) {
+ return setFlags(voicemails, Flag.DELETED);
+ }
+
+ public void handleEvent(OmtpEvents event) {
+ mConfig.handleEvent(mStatus, event);
+ }
+
+ /**
+ * Set flags on the server for a given set of voicemails.
+ *
+ * @param voicemails The voicemails to set flags for.
+ * @param flags The flags to set on the voicemails.
+ * @return {@code true} if the operation completes successfully, {@code false} otherwise.
+ */
+ private boolean setFlags(List<Voicemail> voicemails, String... flags) {
+ if (voicemails.size() == 0) {
+ return false;
+ }
+ try {
+ mFolder = openImapFolder(ImapFolder.MODE_READ_WRITE);
+ if (mFolder != null) {
+ mFolder.setFlags(convertToImapMessages(voicemails), flags, true);
+ return true;
+ }
+ return false;
+ } catch (MessagingException e) {
+ LogUtils.e(TAG, e, "Messaging exception");
+ return false;
+ } finally {
+ closeImapFolder();
+ }
+ }
+
+ /**
+ * Fetch a list of voicemails from the server.
+ *
+ * @return A list of voicemail objects containing data about voicemails stored on the server.
+ */
+ public List<Voicemail> fetchAllVoicemails() {
+ List<Voicemail> result = new ArrayList<Voicemail>();
+ Message[] messages;
+ try {
+ mFolder = openImapFolder(ImapFolder.MODE_READ_WRITE);
+ if (mFolder == null) {
+ // This means we were unable to successfully open the folder.
+ return null;
+ }
+
+ // This method retrieves lightweight messages containing only the uid of the message.
+ messages = mFolder.getMessages(null);
+
+ for (Message message : messages) {
+ // Get the voicemail details (message structure).
+ MessageStructureWrapper messageStructureWrapper = fetchMessageStructure(message);
+ if (messageStructureWrapper != null) {
+ result.add(getVoicemailFromMessageStructure(messageStructureWrapper));
+ }
+ }
+ return result;
+ } catch (MessagingException e) {
+ LogUtils.e(TAG, e, "Messaging Exception");
+ return null;
+ } finally {
+ closeImapFolder();
+ }
+ }
+
+ /**
+ * Extract voicemail details from the message structure. Also fetch transcription if a
+ * transcription exists.
+ */
+ private Voicemail getVoicemailFromMessageStructure(
+ MessageStructureWrapper messageStructureWrapper) throws MessagingException {
+ Message messageDetails = messageStructureWrapper.messageStructure;
+
+ TranscriptionFetchedListener listener = new TranscriptionFetchedListener();
+ if (messageStructureWrapper.transcriptionBodyPart != null) {
+ FetchProfile fetchProfile = new FetchProfile();
+ fetchProfile.add(messageStructureWrapper.transcriptionBodyPart);
+
+ mFolder.fetch(new Message[]{messageDetails}, fetchProfile, listener);
+ }
+
+ // Found an audio attachment, this is a valid voicemail.
+ long time = messageDetails.getSentDate().getTime();
+ String number = getNumber(messageDetails.getFrom());
+ boolean isRead = Arrays.asList(messageDetails.getFlags()).contains(Flag.SEEN);
+ return Voicemail.createForInsertion(time, number)
+ .setPhoneAccount(mPhoneAccount)
+ .setSourcePackage(mContext.getPackageName())
+ .setSourceData(messageDetails.getUid())
+ .setIsRead(isRead)
+ .setTranscription(listener.getVoicemailTranscription())
+ .build();
+ }
+
+ /**
+ * The "from" field of a visual voicemail IMAP message is the number of the caller who left the
+ * message. Extract this number from the list of "from" addresses.
+ *
+ * @param fromAddresses A list of addresses that comprise the "from" line.
+ * @return The number of the voicemail sender.
+ */
+ private String getNumber(Address[] fromAddresses) {
+ if (fromAddresses != null && fromAddresses.length > 0) {
+ if (fromAddresses.length != 1) {
+ LogUtils.w(TAG, "More than one from addresses found. Using the first one.");
+ }
+ String sender = fromAddresses[0].getAddress();
+ int atPos = sender.indexOf('@');
+ if (atPos != -1) {
+ // Strip domain part of the address.
+ sender = sender.substring(0, atPos);
+ }
+ return sender;
+ }
+ return null;
+ }
+
+ /**
+ * Fetches the structure of the given message and returns a wrapper containing the message
+ * structure and the transcription structure (if applicable).
+ *
+ * @throws MessagingException if fetching the structure of the message fails
+ */
+ private MessageStructureWrapper fetchMessageStructure(Message message)
+ throws MessagingException {
+ LogUtils.d(TAG, "Fetching message structure for " + message.getUid());
+
+ MessageStructureFetchedListener listener = new MessageStructureFetchedListener();
+
+ FetchProfile fetchProfile = new FetchProfile();
+ fetchProfile.addAll(Arrays.asList(FetchProfile.Item.FLAGS, FetchProfile.Item.ENVELOPE,
+ FetchProfile.Item.STRUCTURE));
+
+ // The IMAP folder fetch method will call "messageRetrieved" on the listener when the
+ // message is successfully retrieved.
+ mFolder.fetch(new Message[]{message}, fetchProfile, listener);
+ return listener.getMessageStructure();
+ }
+
+ public boolean fetchVoicemailPayload(VoicemailFetchedCallback callback, final String uid) {
+ try {
+ mFolder = openImapFolder(ImapFolder.MODE_READ_WRITE);
+ if (mFolder == null) {
+ // This means we were unable to successfully open the folder.
+ return false;
+ }
+ Message message = mFolder.getMessage(uid);
+ if (message == null) {
+ return false;
+ }
+ VoicemailPayload voicemailPayload = fetchVoicemailPayload(message);
+ callback.setVoicemailContent(voicemailPayload);
+ return true;
+ } catch (MessagingException e) {
+ } finally {
+ closeImapFolder();
+ }
+ return false;
+ }
+
+ /**
+ * Fetches the body of the given message and returns the parsed voicemail payload.
+ *
+ * @throws MessagingException if fetching the body of the message fails
+ */
+ private VoicemailPayload fetchVoicemailPayload(Message message)
+ throws MessagingException {
+ LogUtils.d(TAG, "Fetching message body for " + message.getUid());
+
+ MessageBodyFetchedListener listener = new MessageBodyFetchedListener();
+
+ FetchProfile fetchProfile = new FetchProfile();
+ fetchProfile.add(FetchProfile.Item.BODY);
+
+ mFolder.fetch(new Message[]{message}, fetchProfile, listener);
+ return listener.getVoicemailPayload();
+ }
+
+ public boolean fetchTranscription(TranscriptionFetchedCallback callback, String uid) {
+ try {
+ mFolder = openImapFolder(ImapFolder.MODE_READ_WRITE);
+ if (mFolder == null) {
+ // This means we were unable to successfully open the folder.
+ return false;
+ }
+
+ Message message = mFolder.getMessage(uid);
+ if (message == null) {
+ return false;
+ }
+
+ MessageStructureWrapper messageStructureWrapper = fetchMessageStructure(message);
+ if (messageStructureWrapper != null) {
+ TranscriptionFetchedListener listener = new TranscriptionFetchedListener();
+ if (messageStructureWrapper.transcriptionBodyPart != null) {
+ FetchProfile fetchProfile = new FetchProfile();
+ fetchProfile.add(messageStructureWrapper.transcriptionBodyPart);
+
+ // This method is called synchronously so the transcription will be populated
+ // in the listener once the next method is called.
+ mFolder.fetch(new Message[]{message}, fetchProfile, listener);
+ callback.setVoicemailTranscription(listener.getVoicemailTranscription());
+ }
+ }
+ return true;
+ } catch (MessagingException e) {
+ LogUtils.e(TAG, e, "Messaging Exception");
+ return false;
+ } finally {
+ closeImapFolder();
+ }
+ }
+
+
+ @ChangePinResult
+ public int changePin(String oldPin, String newPin)
+ throws MessagingException {
+ ImapConnection connection = mImapStore.getConnection();
+ try {
+ String command = getConfig().getProtocol()
+ .getCommand(OmtpConstants.IMAP_CHANGE_TUI_PWD_FORMAT);
+ connection.sendCommand(
+ String.format(Locale.US, command, newPin, oldPin), true);
+ return getChangePinResultFromImapResponse(connection.readResponse());
+ } catch (IOException ioe) {
+ VvmLog.e(TAG, "changePin: ", ioe);
+ return OmtpConstants.CHANGE_PIN_SYSTEM_ERROR;
+ } finally {
+ connection.destroyResponses();
+ }
+ }
+
+ public void changeVoicemailTuiLanguage(String languageCode)
+ throws MessagingException {
+ ImapConnection connection = mImapStore.getConnection();
+ try {
+ String command = getConfig().getProtocol()
+ .getCommand(OmtpConstants.IMAP_CHANGE_VM_LANG_FORMAT);
+ connection.sendCommand(
+ String.format(Locale.US, command, languageCode), true);
+ } catch (IOException ioe) {
+ LogUtils.e(TAG, ioe.toString());
+ } finally {
+ connection.destroyResponses();
+ }
+ }
+
+ public void closeNewUserTutorial() throws MessagingException {
+ ImapConnection connection = mImapStore.getConnection();
+ try {
+ String command = getConfig().getProtocol()
+ .getCommand(OmtpConstants.IMAP_CLOSE_NUT);
+ connection.executeSimpleCommand(command, false);
+ } catch (IOException ioe) {
+ throw new MessagingException(MessagingException.SERVER_ERROR, ioe.toString());
+ } finally {
+ connection.destroyResponses();
+ }
+ }
+
+ @ChangePinResult
+ private static int getChangePinResultFromImapResponse(ImapResponse response)
+ throws MessagingException {
+ if (!response.isTagged()) {
+ throw new MessagingException(MessagingException.SERVER_ERROR,
+ "tagged response expected");
+ }
+ if (!response.isOk()) {
+ String message = response.getStringOrEmpty(1).getString();
+ LogUtils.d(TAG, "change PIN failed: " + message);
+ if (OmtpConstants.RESPONSE_CHANGE_PIN_TOO_SHORT.equals(message)) {
+ return OmtpConstants.CHANGE_PIN_TOO_SHORT;
+ }
+ if (OmtpConstants.RESPONSE_CHANGE_PIN_TOO_LONG.equals(message)) {
+ return OmtpConstants.CHANGE_PIN_TOO_LONG;
+ }
+ if (OmtpConstants.RESPONSE_CHANGE_PIN_TOO_WEAK.equals(message)) {
+ return OmtpConstants.CHANGE_PIN_TOO_WEAK;
+ }
+ if (OmtpConstants.RESPONSE_CHANGE_PIN_MISMATCH.equals(message)) {
+ return OmtpConstants.CHANGE_PIN_MISMATCH;
+ }
+ if (OmtpConstants.RESPONSE_CHANGE_PIN_INVALID_CHARACTER.equals(message)) {
+ return OmtpConstants.CHANGE_PIN_INVALID_CHARACTER;
+ }
+ return OmtpConstants.CHANGE_PIN_SYSTEM_ERROR;
+ }
+ LogUtils.d(TAG, "change PIN succeeded");
+ return OmtpConstants.CHANGE_PIN_SUCCESS;
+ }
+
+ public void updateQuota() {
+ try {
+ mFolder = openImapFolder(ImapFolder.MODE_READ_WRITE);
+ if (mFolder == null) {
+ // This means we were unable to successfully open the folder.
+ return;
+ }
+ updateQuota(mFolder);
+ } catch (MessagingException e) {
+ LogUtils.e(TAG, e, "Messaging Exception");
+ } finally {
+ closeImapFolder();
+ }
+ }
+
+ private void updateQuota(ImapFolder folder) throws MessagingException {
+ setQuota(folder.getQuota());
+ }
+
+ private void setQuota(ImapFolder.Quota quota) {
+ if (quota == null) {
+ return;
+ }
+ if (quota.occupied == mQuotaOccupied && quota.total == mQuotaTotal) {
+ VvmLog.v(TAG, "Quota hasn't changed");
+ return;
+ }
+ mQuotaOccupied = quota.occupied;
+ mQuotaTotal = quota.total;
+ VoicemailStatus.edit(mContext, mPhoneAccount)
+ .setQuota(mQuotaOccupied, mQuotaTotal)
+ .apply();
+ mPrefs.edit()
+ .putInt(PREF_KEY_QUOTA_OCCUPIED, mQuotaOccupied)
+ .putInt(PREF_KEY_QUOTA_TOTAL, mQuotaTotal)
+ .apply();
+ VvmLog.v(TAG, "Quota changed to " + mQuotaOccupied + "/" + mQuotaTotal);
+ }
+
+ /**
+ * A wrapper to hold a message with its header details and the structure for transcriptions (so
+ * they can be fetched in the future).
+ */
+ public class MessageStructureWrapper {
+
+ public Message messageStructure;
+ public BodyPart transcriptionBodyPart;
+
+ public MessageStructureWrapper() {
+ }
+ }
+
+ /**
+ * Listener for the message structure being fetched.
+ */
+ private final class MessageStructureFetchedListener
+ implements ImapFolder.MessageRetrievalListener {
+
+ private MessageStructureWrapper mMessageStructure;
+
+ public MessageStructureFetchedListener() {
+ }
+
+ public MessageStructureWrapper getMessageStructure() {
+ return mMessageStructure;
+ }
+
+ @Override
+ public void messageRetrieved(Message message) {
+ LogUtils.d(TAG, "Fetched message structure for " + message.getUid());
+ LogUtils.d(TAG, "Message retrieved: " + message);
+ try {
+ mMessageStructure = getMessageOrNull(message);
+ if (mMessageStructure == null) {
+ LogUtils.d(TAG, "This voicemail does not have an attachment...");
+ return;
+ }
+ } catch (MessagingException e) {
+ LogUtils.e(TAG, e, "Messaging Exception");
+ closeImapFolder();
+ }
+ }
+
+ /**
+ * Check if this IMAP message is a valid voicemail and whether it contains a transcription.
+ *
+ * @param message The IMAP message.
+ * @return The MessageStructureWrapper object corresponding to an IMAP message and
+ * transcription.
+ */
+ private MessageStructureWrapper getMessageOrNull(Message message)
+ throws MessagingException {
+ if (!message.getMimeType().startsWith("multipart/")) {
+ LogUtils.w(TAG, "Ignored non multi-part message");
+ return null;
+ }
+
+ MessageStructureWrapper messageStructureWrapper = new MessageStructureWrapper();
+
+ Multipart multipart = (Multipart) message.getBody();
+ for (int i = 0; i < multipart.getCount(); ++i) {
+ BodyPart bodyPart = multipart.getBodyPart(i);
+ String bodyPartMimeType = bodyPart.getMimeType().toLowerCase();
+ LogUtils.d(TAG, "bodyPart mime type: " + bodyPartMimeType);
+
+ if (bodyPartMimeType.startsWith("audio/")) {
+ messageStructureWrapper.messageStructure = message;
+ } else if (bodyPartMimeType.startsWith("text/")) {
+ messageStructureWrapper.transcriptionBodyPart = bodyPart;
+ } else {
+ VvmLog.v(TAG, "Unknown bodyPart MIME: " + bodyPartMimeType);
+ }
+ }
+
+ if (messageStructureWrapper.messageStructure != null) {
+ return messageStructureWrapper;
+ }
+
+ // No attachment found, this is not a voicemail.
+ return null;
+ }
+ }
+
+ /**
+ * Listener for the message body being fetched.
+ */
+ private final class MessageBodyFetchedListener implements ImapFolder.MessageRetrievalListener {
+
+ private VoicemailPayload mVoicemailPayload;
+
+ /**
+ * Returns the fetch voicemail payload.
+ */
+ public VoicemailPayload getVoicemailPayload() {
+ return mVoicemailPayload;
+ }
+
+ @Override
+ public void messageRetrieved(Message message) {
+ LogUtils.d(TAG, "Fetched message body for " + message.getUid());
+ LogUtils.d(TAG, "Message retrieved: " + message);
+ try {
+ mVoicemailPayload = getVoicemailPayloadFromMessage(message);
+ } catch (MessagingException e) {
+ LogUtils.e(TAG, "Messaging Exception:", e);
+ } catch (IOException e) {
+ LogUtils.e(TAG, "IO Exception:", e);
+ }
+ }
+
+ private VoicemailPayload getVoicemailPayloadFromMessage(Message message)
+ throws MessagingException, IOException {
+ Multipart multipart = (Multipart) message.getBody();
+ List<String> mimeTypes = new ArrayList<>();
+ for (int i = 0; i < multipart.getCount(); ++i) {
+ BodyPart bodyPart = multipart.getBodyPart(i);
+ String bodyPartMimeType = bodyPart.getMimeType().toLowerCase();
+ mimeTypes.add(bodyPartMimeType);
+ if (bodyPartMimeType.startsWith("audio/")) {
+ byte[] bytes = getDataFromBody(bodyPart.getBody());
+ LogUtils.d(TAG, String.format("Fetched %s bytes of data", bytes.length));
+ return new VoicemailPayload(bodyPartMimeType, bytes);
+ }
+ }
+ LogUtils.e(TAG, "No audio attachment found on this voicemail, mimeTypes:" + mimeTypes);
+ return null;
+ }
+ }
+
+ /**
+ * Listener for the transcription being fetched.
+ */
+ private final class TranscriptionFetchedListener implements
+ ImapFolder.MessageRetrievalListener {
+
+ private String mVoicemailTranscription;
+
+ /**
+ * Returns the fetched voicemail transcription.
+ */
+ public String getVoicemailTranscription() {
+ return mVoicemailTranscription;
+ }
+
+ @Override
+ public void messageRetrieved(Message message) {
+ LogUtils.d(TAG, "Fetched transcription for " + message.getUid());
+ try {
+ mVoicemailTranscription = new String(getDataFromBody(message.getBody()));
+ } catch (MessagingException e) {
+ LogUtils.e(TAG, "Messaging Exception:", e);
+ } catch (IOException e) {
+ LogUtils.e(TAG, "IO Exception:", e);
+ }
+ }
+ }
+
+ private ImapFolder openImapFolder(String modeReadWrite) {
+ try {
+ if (mImapStore == null) {
+ return null;
+ }
+ ImapFolder folder = new ImapFolder(mImapStore, ImapConstants.INBOX);
+ folder.open(modeReadWrite);
+ return folder;
+ } catch (MessagingException e) {
+ LogUtils.e(TAG, e, "Messaging Exception");
+ }
+ return null;
+ }
+
+ private Message[] convertToImapMessages(List<Voicemail> voicemails) {
+ Message[] messages = new Message[voicemails.size()];
+ for (int i = 0; i < voicemails.size(); ++i) {
+ messages[i] = new MimeMessage();
+ messages[i].setUid(voicemails.get(i).getSourceData());
+ }
+ return messages;
+ }
+
+ private void closeImapFolder() {
+ if (mFolder != null) {
+ mFolder.close(true);
+ }
+ }
+
+ private byte[] getDataFromBody(Body body) throws IOException, MessagingException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ BufferedOutputStream bufferedOut = new BufferedOutputStream(out);
+ try {
+ body.writeTo(bufferedOut);
+ return Base64.decode(out.toByteArray(), Base64.DEFAULT);
+ } finally {
+ IOUtils.closeQuietly(bufferedOut);
+ IOUtils.closeQuietly(out);
+ }
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/imap/VoicemailPayload.java b/java/com/android/voicemailomtp/imap/VoicemailPayload.java
new file mode 100644
index 000000000..04c69dea5
--- /dev/null
+++ b/java/com/android/voicemailomtp/imap/VoicemailPayload.java
@@ -0,0 +1,38 @@
+/*
+ * 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.voicemailomtp.imap;
+
+/**
+ * The payload for a voicemail, usually audio data.
+ */
+public class VoicemailPayload {
+ private final String mMimeType;
+ private final byte[] mBytes;
+
+ public VoicemailPayload(String mimeType, byte[] bytes) {
+ mMimeType = mimeType;
+ mBytes = bytes;
+ }
+
+ public byte[] getBytes() {
+ return mBytes;
+ }
+
+ public String getMimeType() {
+ return mMimeType;
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/Address.java b/java/com/android/voicemailomtp/mail/Address.java
new file mode 100644
index 000000000..ed3f44c03
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/Address.java
@@ -0,0 +1,541 @@
+/*
+ * 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.voicemailomtp.mail;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.VisibleForTesting;
+import android.text.Html;
+import android.text.TextUtils;
+import android.text.util.Rfc822Token;
+import android.text.util.Rfc822Tokenizer;
+import com.android.voicemailomtp.mail.utils.LogUtils;
+import java.util.ArrayList;
+import java.util.regex.Pattern;
+import org.apache.james.mime4j.codec.EncoderUtil;
+import org.apache.james.mime4j.decoder.DecoderUtil;
+
+/**
+ * This class represent email address.
+ *
+ * RFC822 email address may have following format.
+ * "name" <address> (comment)
+ * "name" <address>
+ * name <address>
+ * address
+ * Name and comment part should be MIME/base64 encoded in header if necessary.
+ *
+ */
+public class Address implements Parcelable {
+ public static final String ADDRESS_DELIMETER = ",";
+ /**
+ * Address part, in the form local_part@domain_part. No surrounding angle brackets.
+ */
+ private String mAddress;
+
+ /**
+ * Name part. No surrounding double quote, and no MIME/base64 encoding.
+ * This must be null if Address has no name part.
+ */
+ private String mPersonal;
+
+ /**
+ * When personal is set, it will return the first token of the personal
+ * string. Otherwise, it will return the e-mail address up to the '@' sign.
+ */
+ private String mSimplifiedName;
+
+ // Regex that matches address surrounded by '<>' optionally. '^<?([^>]+)>?$'
+ private static final Pattern REMOVE_OPTIONAL_BRACKET = Pattern.compile("^<?([^>]+)>?$");
+ // Regex that matches personal name surrounded by '""' optionally. '^"?([^"]+)"?$'
+ private static final Pattern REMOVE_OPTIONAL_DQUOTE = Pattern.compile("^\"?([^\"]*)\"?$");
+ // Regex that matches escaped character '\\([\\"])'
+ private static final Pattern UNQUOTE = Pattern.compile("\\\\([\\\\\"])");
+
+ // TODO: LOCAL_PART and DOMAIN_PART_PART are too permissive and can be improved.
+ // TODO: Fix this to better constrain comments.
+ /** Regex for the local part of an email address. */
+ private static final String LOCAL_PART = "[^@]+";
+ /** Regex for each part of the domain part, i.e. the thing between the dots. */
+ private static final String DOMAIN_PART_PART = "[[\\w][\\d]\\-\\(\\)\\[\\]]+";
+ /** Regex for the domain part, which is two or more {@link #DOMAIN_PART_PART} separated by . */
+ private static final String DOMAIN_PART =
+ "(" + DOMAIN_PART_PART + "\\.)+" + DOMAIN_PART_PART;
+
+ /** Pattern to check if an email address is valid. */
+ private static final Pattern EMAIL_ADDRESS =
+ Pattern.compile("\\A" + LOCAL_PART + "@" + DOMAIN_PART + "\\z");
+
+ private static final Address[] EMPTY_ADDRESS_ARRAY = new Address[0];
+
+ // delimiters are chars that do not appear in an email address, used by fromHeader
+ private static final char LIST_DELIMITER_EMAIL = '\1';
+ private static final char LIST_DELIMITER_PERSONAL = '\2';
+
+ private static final String LOG_TAG = "Email Address";
+
+ @VisibleForTesting
+ public Address(String address) {
+ setAddress(address);
+ }
+
+ public Address(String address, String personal) {
+ setPersonal(personal);
+ setAddress(address);
+ }
+
+ /**
+ * Returns a simplified string for this e-mail address.
+ * When a name is known, it will return the first token of that name. Otherwise, it will
+ * return the e-mail address up to the '@' sign.
+ */
+ public String getSimplifiedName() {
+ if (mSimplifiedName == null) {
+ if (TextUtils.isEmpty(mPersonal) && !TextUtils.isEmpty(mAddress)) {
+ int atSign = mAddress.indexOf('@');
+ mSimplifiedName = (atSign != -1) ? mAddress.substring(0, atSign) : "";
+ } else if (!TextUtils.isEmpty(mPersonal)) {
+
+ // TODO: use Contacts' NameSplitter for more reliable first-name extraction
+
+ int end = mPersonal.indexOf(' ');
+ while (end > 0 && mPersonal.charAt(end - 1) == ',') {
+ end--;
+ }
+ mSimplifiedName = (end < 1) ? mPersonal : mPersonal.substring(0, end);
+
+ } else {
+ LogUtils.w(LOG_TAG, "Unable to get a simplified name");
+ mSimplifiedName = "";
+ }
+ }
+ return mSimplifiedName;
+ }
+
+ public static synchronized Address getEmailAddress(String rawAddress) {
+ if (TextUtils.isEmpty(rawAddress)) {
+ return null;
+ }
+ String name, address;
+ final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(rawAddress);
+ if (tokens.length > 0) {
+ final String tokenizedName = tokens[0].getName();
+ name = tokenizedName != null ? Html.fromHtml(tokenizedName.trim()).toString()
+ : "";
+ address = Html.fromHtml(tokens[0].getAddress()).toString();
+ } else {
+ name = "";
+ address = rawAddress == null ?
+ "" : Html.fromHtml(rawAddress).toString();
+ }
+ return new Address(address, name);
+ }
+
+ public String getAddress() {
+ return mAddress;
+ }
+
+ public void setAddress(String address) {
+ mAddress = REMOVE_OPTIONAL_BRACKET.matcher(address).replaceAll("$1");
+ }
+
+ /**
+ * Get name part as UTF-16 string. No surrounding double quote, and no MIME/base64 encoding.
+ *
+ * @return Name part of email address. Returns null if it is omitted.
+ */
+ public String getPersonal() {
+ return mPersonal;
+ }
+
+ /**
+ * Set personal part from UTF-16 string. Optional surrounding double quote will be removed.
+ * It will be also unquoted and MIME/base64 decoded.
+ *
+ * @param personal name part of email address as UTF-16 string. Null is acceptable.
+ */
+ public void setPersonal(String personal) {
+ mPersonal = decodeAddressPersonal(personal);
+ }
+
+ /**
+ * Decodes name from UTF-16 string. Optional surrounding double quote will be removed.
+ * It will be also unquoted and MIME/base64 decoded.
+ *
+ * @param personal name part of email address as UTF-16 string. Null is acceptable.
+ */
+ public static String decodeAddressPersonal(String personal) {
+ if (personal != null) {
+ personal = REMOVE_OPTIONAL_DQUOTE.matcher(personal).replaceAll("$1");
+ personal = UNQUOTE.matcher(personal).replaceAll("$1");
+ personal = DecoderUtil.decodeEncodedWords(personal);
+ if (personal.length() == 0) {
+ personal = null;
+ }
+ }
+ return personal;
+ }
+
+ /**
+ * This method is used to check that all the addresses that the user
+ * entered in a list (e.g. To:) are valid, so that none is dropped.
+ */
+ @VisibleForTesting
+ public static boolean isAllValid(String addressList) {
+ // This code mimics the parse() method below.
+ // I don't know how to better avoid the code-duplication.
+ if (addressList != null && addressList.length() > 0) {
+ Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(addressList);
+ for (int i = 0, length = tokens.length; i < length; ++i) {
+ Rfc822Token token = tokens[i];
+ String address = token.getAddress();
+ if (!TextUtils.isEmpty(address) && !isValidAddress(address)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Parse a comma-delimited list of addresses in RFC822 format and return an
+ * array of Address objects.
+ *
+ * @param addressList Address list in comma-delimited string.
+ * @return An array of 0 or more Addresses.
+ */
+ public static Address[] parse(String addressList) {
+ if (addressList == null || addressList.length() == 0) {
+ return EMPTY_ADDRESS_ARRAY;
+ }
+ Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(addressList);
+ ArrayList<Address> addresses = new ArrayList<Address>();
+ for (int i = 0, length = tokens.length; i < length; ++i) {
+ Rfc822Token token = tokens[i];
+ String address = token.getAddress();
+ if (!TextUtils.isEmpty(address)) {
+ if (isValidAddress(address)) {
+ String name = token.getName();
+ if (TextUtils.isEmpty(name)) {
+ name = null;
+ }
+ addresses.add(new Address(address, name));
+ }
+ }
+ }
+ return addresses.toArray(new Address[addresses.size()]);
+ }
+
+ /**
+ * Checks whether a string email address is valid.
+ * E.g. name@domain.com is valid.
+ */
+ @VisibleForTesting
+ static boolean isValidAddress(final String address) {
+ return EMAIL_ADDRESS.matcher(address).find();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Address) {
+ // It seems that the spec says that the "user" part is case-sensitive,
+ // while the domain part in case-insesitive.
+ // So foo@yahoo.com and Foo@yahoo.com are different.
+ // This may seem non-intuitive from the user POV, so we
+ // may re-consider it if it creates UI trouble.
+ // A problem case is "replyAll" sending to both
+ // a@b.c and to A@b.c, which turn out to be the same on the server.
+ // Leave unchanged for now (i.e. case-sensitive).
+ return getAddress().equals(((Address) o).getAddress());
+ }
+ return super.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return getAddress().hashCode();
+ }
+
+ /**
+ * Get human readable address string.
+ * Do not use this for email header.
+ *
+ * @return Human readable address string. Not quoted and not encoded.
+ */
+ @Override
+ public String toString() {
+ if (mPersonal != null && !mPersonal.equals(mAddress)) {
+ if (mPersonal.matches(".*[\\(\\)<>@,;:\\\\\".\\[\\]].*")) {
+ return ensureQuotedString(mPersonal) + " <" + mAddress + ">";
+ } else {
+ return mPersonal + " <" + mAddress + ">";
+ }
+ } else {
+ return mAddress;
+ }
+ }
+
+ /**
+ * Ensures that the given string starts and ends with the double quote character. The string is
+ * not modified in any way except to add the double quote character to start and end if it's not
+ * already there.
+ *
+ * sample -> "sample"
+ * "sample" -> "sample"
+ * ""sample"" -> "sample"
+ * "sample"" -> "sample"
+ * sa"mp"le -> "sa"mp"le"
+ * "sa"mp"le" -> "sa"mp"le"
+ * (empty string) -> ""
+ * " -> ""
+ */
+ private static String ensureQuotedString(String s) {
+ if (s == null) {
+ return null;
+ }
+ if (!s.matches("^\".*\"$")) {
+ return "\"" + s + "\"";
+ } else {
+ return s;
+ }
+ }
+
+ /**
+ * Get human readable comma-delimited address string.
+ *
+ * @param addresses Address array
+ * @return Human readable comma-delimited address string.
+ */
+ @VisibleForTesting
+ public static String toString(Address[] addresses) {
+ return toString(addresses, ADDRESS_DELIMETER);
+ }
+
+ /**
+ * Get human readable address strings joined with the specified separator.
+ *
+ * @param addresses Address array
+ * @param separator Separator
+ * @return Human readable comma-delimited address string.
+ */
+ public static String toString(Address[] addresses, String separator) {
+ if (addresses == null || addresses.length == 0) {
+ return null;
+ }
+ if (addresses.length == 1) {
+ return addresses[0].toString();
+ }
+ StringBuilder sb = new StringBuilder(addresses[0].toString());
+ for (int i = 1; i < addresses.length; i++) {
+ sb.append(separator);
+ // TODO: investigate why this .trim() is needed.
+ sb.append(addresses[i].toString().trim());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Get RFC822/MIME compatible address string.
+ *
+ * @return RFC822/MIME compatible address string.
+ * It may be surrounded by double quote or quoted and MIME/base64 encoded if necessary.
+ */
+ public String toHeader() {
+ if (mPersonal != null) {
+ return EncoderUtil.encodeAddressDisplayName(mPersonal) + " <" + mAddress + ">";
+ } else {
+ return mAddress;
+ }
+ }
+
+ /**
+ * Get RFC822/MIME compatible comma-delimited address string.
+ *
+ * @param addresses Address array
+ * @return RFC822/MIME compatible comma-delimited address string.
+ * it may be surrounded by double quoted or quoted and MIME/base64 encoded if necessary.
+ */
+ public static String toHeader(Address[] addresses) {
+ if (addresses == null || addresses.length == 0) {
+ return null;
+ }
+ if (addresses.length == 1) {
+ return addresses[0].toHeader();
+ }
+ StringBuilder sb = new StringBuilder(addresses[0].toHeader());
+ for (int i = 1; i < addresses.length; i++) {
+ // We need space character to be able to fold line.
+ sb.append(", ");
+ sb.append(addresses[i].toHeader());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Get Human friendly address string.
+ *
+ * @return the personal part of this Address, or the address part if the
+ * personal part is not available
+ */
+ @VisibleForTesting
+ public String toFriendly() {
+ if (mPersonal != null && mPersonal.length() > 0) {
+ return mPersonal;
+ } else {
+ return mAddress;
+ }
+ }
+
+ /**
+ * Creates a comma-delimited list of addresses in the "friendly" format (see toFriendly() for
+ * details on the per-address conversion).
+ *
+ * @param addresses Array of Address[] values
+ * @return A comma-delimited string listing all of the addresses supplied. Null if source
+ * was null or empty.
+ */
+ @VisibleForTesting
+ public static String toFriendly(Address[] addresses) {
+ if (addresses == null || addresses.length == 0) {
+ return null;
+ }
+ if (addresses.length == 1) {
+ return addresses[0].toFriendly();
+ }
+ StringBuilder sb = new StringBuilder(addresses[0].toFriendly());
+ for (int i = 1; i < addresses.length; i++) {
+ sb.append(", ");
+ sb.append(addresses[i].toFriendly());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Returns exactly the same result as Address.toString(Address.fromHeader(addressList)).
+ */
+ @VisibleForTesting
+ public static String fromHeaderToString(String addressList) {
+ return toString(fromHeader(addressList));
+ }
+
+ /**
+ * Returns exactly the same result as Address.toHeader(Address.parse(addressList)).
+ */
+ @VisibleForTesting
+ public static String parseToHeader(String addressList) {
+ return Address.toHeader(Address.parse(addressList));
+ }
+
+ /**
+ * Returns null if the addressList has 0 addresses, otherwise returns the first address.
+ * The same as Address.fromHeader(addressList)[0] for non-empty list.
+ * This is an utility method that offers some performance optimization opportunities.
+ */
+ @VisibleForTesting
+ public static Address firstAddress(String addressList) {
+ Address[] array = fromHeader(addressList);
+ return array.length > 0 ? array[0] : null;
+ }
+
+ /**
+ * This method exists to convert an address list formatted in a deprecated legacy format to the
+ * standard RFC822 header format. {@link #fromHeader(String)} is capable of reading the legacy
+ * format and the RFC822 format. {@link #toHeader()} always produces the RFC822 format.
+ *
+ * This implementation is brute-force, and could be replaced with a more efficient version
+ * if desired.
+ */
+ public static String reformatToHeader(String addressList) {
+ return toHeader(fromHeader(addressList));
+ }
+
+ /**
+ * @param addressList a CSV of RFC822 addresses or the deprecated legacy string format
+ * @return array of addresses parsed from <code>addressList</code>
+ */
+ @VisibleForTesting
+ public static Address[] fromHeader(String addressList) {
+ if (addressList == null || addressList.length() == 0) {
+ return EMPTY_ADDRESS_ARRAY;
+ }
+ // IF we're CSV, just parse
+ if ((addressList.indexOf(LIST_DELIMITER_PERSONAL) == -1) &&
+ (addressList.indexOf(LIST_DELIMITER_EMAIL) == -1)) {
+ return Address.parse(addressList);
+ }
+ // Otherwise, do backward-compatible unpack
+ ArrayList<Address> addresses = new ArrayList<Address>();
+ int length = addressList.length();
+ int pairStartIndex = 0;
+ int pairEndIndex;
+
+ /* addressEndIndex is only re-scanned (indexOf()) when a LIST_DELIMITER_PERSONAL
+ is used, not for every email address; i.e. not for every iteration of the while().
+ This reduces the theoretical complexity from quadratic to linear,
+ and provides some speed-up in practice by removing redundant scans of the string.
+ */
+ int addressEndIndex = addressList.indexOf(LIST_DELIMITER_PERSONAL);
+
+ while (pairStartIndex < length) {
+ pairEndIndex = addressList.indexOf(LIST_DELIMITER_EMAIL, pairStartIndex);
+ if (pairEndIndex == -1) {
+ pairEndIndex = length;
+ }
+ Address address;
+ if (addressEndIndex == -1 || pairEndIndex <= addressEndIndex) {
+ // in this case the DELIMITER_PERSONAL is in a future pair,
+ // so don't use personal, and don't update addressEndIndex
+ address = new Address(addressList.substring(pairStartIndex, pairEndIndex), null);
+ } else {
+ address = new Address(addressList.substring(pairStartIndex, addressEndIndex),
+ addressList.substring(addressEndIndex + 1, pairEndIndex));
+ // only update addressEndIndex when we use the LIST_DELIMITER_PERSONAL
+ addressEndIndex = addressList.indexOf(LIST_DELIMITER_PERSONAL, pairEndIndex + 1);
+ }
+ addresses.add(address);
+ pairStartIndex = pairEndIndex + 1;
+ }
+ return addresses.toArray(new Address[addresses.size()]);
+ }
+
+ public static final Creator<Address> CREATOR = new Creator<Address>() {
+ @Override
+ public Address createFromParcel(Parcel parcel) {
+ return new Address(parcel);
+ }
+
+ @Override
+ public Address[] newArray(int size) {
+ return new Address[size];
+ }
+ };
+
+ public Address(Parcel in) {
+ setPersonal(in.readString());
+ setAddress(in.readString());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mPersonal);
+ out.writeString(mAddress);
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/AuthenticationFailedException.java b/java/com/android/voicemailomtp/mail/AuthenticationFailedException.java
new file mode 100644
index 000000000..995d5d348
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/AuthenticationFailedException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.voicemailomtp.mail;
+
+public class AuthenticationFailedException extends MessagingException {
+ public static final long serialVersionUID = -1;
+
+ public AuthenticationFailedException(String message) {
+ super(MessagingException.AUTHENTICATION_FAILED, message);
+ }
+
+ public AuthenticationFailedException(int exceptionType, String message) {
+ super(exceptionType, message);
+ }
+
+ public AuthenticationFailedException(String message, Throwable throwable) {
+ super(MessagingException.AUTHENTICATION_FAILED, message, throwable);
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/Base64Body.java b/java/com/android/voicemailomtp/mail/Base64Body.java
new file mode 100644
index 000000000..6e1deff44
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/Base64Body.java
@@ -0,0 +1,62 @@
+/*
+ * 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.voicemailomtp.mail;
+
+import android.util.Base64;
+import android.util.Base64OutputStream;
+
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class Base64Body implements Body {
+ private final InputStream mSource;
+ // Because we consume the input stream, we can only write out once
+ private boolean mAlreadyWritten;
+
+ public Base64Body(InputStream source) {
+ mSource = source;
+ }
+
+ @Override
+ public InputStream getInputStream() throws MessagingException {
+ return mSource;
+ }
+
+ /**
+ * This method consumes the input stream, so can only be called once
+ * @param out Stream to write to
+ * @throws IllegalStateException If called more than once
+ * @throws IOException
+ * @throws MessagingException
+ */
+ @Override
+ public void writeTo(OutputStream out)
+ throws IllegalStateException, IOException, MessagingException {
+ if (mAlreadyWritten) {
+ throw new IllegalStateException("Base64Body can only be written once");
+ }
+ mAlreadyWritten = true;
+ try {
+ final Base64OutputStream b64out = new Base64OutputStream(out, Base64.DEFAULT);
+ IOUtils.copyLarge(mSource, b64out);
+ } finally {
+ mSource.close();
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/Body.java b/java/com/android/voicemailomtp/mail/Body.java
new file mode 100644
index 000000000..393e1823c
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/Body.java
@@ -0,0 +1,25 @@
+/*
+ * 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.voicemailomtp.mail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public interface Body {
+ public InputStream getInputStream() throws MessagingException;
+ public void writeTo(OutputStream out) throws IOException, MessagingException;
+}
diff --git a/java/com/android/voicemailomtp/mail/BodyPart.java b/java/com/android/voicemailomtp/mail/BodyPart.java
new file mode 100644
index 000000000..62390a43e
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/BodyPart.java
@@ -0,0 +1,24 @@
+/*
+ * 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.voicemailomtp.mail;
+
+public abstract class BodyPart implements Part {
+ protected Multipart mParent;
+
+ public Multipart getParent() {
+ return mParent;
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/CertificateValidationException.java b/java/com/android/voicemailomtp/mail/CertificateValidationException.java
new file mode 100644
index 000000000..8ebe5480b
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/CertificateValidationException.java
@@ -0,0 +1,29 @@
+/*
+ * 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.voicemailomtp.mail;
+
+public class CertificateValidationException extends MessagingException {
+ public static final long serialVersionUID = -1;
+
+ public CertificateValidationException(String message) {
+ super(MessagingException.CERTIFICATE_VALIDATION_ERROR, message);
+ }
+
+ public CertificateValidationException(String message, Throwable throwable) {
+ super(MessagingException.CERTIFICATE_VALIDATION_ERROR, message, throwable);
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/FetchProfile.java b/java/com/android/voicemailomtp/mail/FetchProfile.java
new file mode 100644
index 000000000..d050692cc
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/FetchProfile.java
@@ -0,0 +1,84 @@
+/*
+ * 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.voicemailomtp.mail;
+
+import java.util.ArrayList;
+
+/**
+ * <pre>
+ * A FetchProfile is a list of items that should be downloaded in bulk for a set of messages.
+ * FetchProfile can contain the following objects:
+ * FetchProfile.Item: Described below.
+ * Message: Indicates that the body of the entire message should be fetched.
+ * Synonymous with FetchProfile.Item.BODY.
+ * Part: Indicates that the given Part should be fetched. The provider
+ * is expected have previously created the given BodyPart and stored
+ * any information it needs to download the content.
+ * </pre>
+ */
+public class FetchProfile extends ArrayList<Fetchable> {
+ /**
+ * Default items available for pre-fetching. It should be expected that any
+ * item fetched by using these items could potentially include all of the
+ * previous items.
+ */
+ public enum Item implements Fetchable {
+ /**
+ * Download the flags of the message.
+ */
+ FLAGS,
+
+ /**
+ * Download the envelope of the message. This should include at minimum
+ * the size and the following headers: date, subject, from, content-type, to, cc
+ */
+ ENVELOPE,
+
+ /**
+ * Download the structure of the message. This maps directly to IMAP's BODYSTRUCTURE
+ * and may map to other providers.
+ * The provider should, if possible, fill in a properly formatted MIME structure in
+ * the message without actually downloading any message data. If the provider is not
+ * capable of this operation it should specifically set the body of the message to null
+ * so that upper levels can detect that a full body download is needed.
+ */
+ STRUCTURE,
+
+ /**
+ * A sane portion of the entire message, cut off at a provider determined limit.
+ * This should generally be around 50kB.
+ */
+ BODY_SANE,
+
+ /**
+ * The entire message.
+ */
+ BODY,
+ }
+
+ /**
+ * @return the first {@link Part} in this collection, or null if it doesn't contain
+ * {@link Part}.
+ */
+ public Part getFirstPart() {
+ for (Fetchable o : this) {
+ if (o instanceof Part) {
+ return (Part) o;
+ }
+ }
+ return null;
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/Fetchable.java b/java/com/android/voicemailomtp/mail/Fetchable.java
new file mode 100644
index 000000000..1d8d0005b
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/Fetchable.java
@@ -0,0 +1,23 @@
+/*
+ * 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.voicemailomtp.mail;
+
+/**
+ * Interface for classes that can be added to {@link FetchProfile}.
+ * i.e. {@link Part} and its subclasses, and {@link FetchProfile.Item}.
+ */
+public interface Fetchable {
+}
diff --git a/java/com/android/voicemailomtp/mail/FixedLengthInputStream.java b/java/com/android/voicemailomtp/mail/FixedLengthInputStream.java
new file mode 100644
index 000000000..65655efd5
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/FixedLengthInputStream.java
@@ -0,0 +1,79 @@
+/*
+ * 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.voicemailomtp.mail;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A filtering InputStream that stops allowing reads after the given length has been read. This
+ * is used to allow a client to read directly from an underlying protocol stream without reading
+ * past where the protocol handler intended the client to read.
+ */
+public class FixedLengthInputStream extends InputStream {
+ private final InputStream mIn;
+ private final int mLength;
+ private int mCount;
+
+ public FixedLengthInputStream(InputStream in, int length) {
+ this.mIn = in;
+ this.mLength = length;
+ }
+
+ @Override
+ public int available() throws IOException {
+ return mLength - mCount;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (mCount < mLength) {
+ mCount++;
+ return mIn.read();
+ } else {
+ return -1;
+ }
+ }
+
+ @Override
+ public int read(byte[] b, int offset, int length) throws IOException {
+ if (mCount < mLength) {
+ int d = mIn.read(b, offset, Math.min(mLength - mCount, length));
+ if (d == -1) {
+ return -1;
+ } else {
+ mCount += d;
+ return d;
+ }
+ } else {
+ return -1;
+ }
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ public int getLength() {
+ return mLength;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("FixedLengthInputStream(in=%s, length=%d)", mIn.toString(), mLength);
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/Flag.java b/java/com/android/voicemailomtp/mail/Flag.java
new file mode 100644
index 000000000..a9f927099
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/Flag.java
@@ -0,0 +1,29 @@
+/*
+ * 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.voicemailomtp.mail;
+
+/**
+ * Flags that can be applied to Messages.
+ */
+public class Flag {
+ // If adding new flags: ALL FLAGS MUST BE UPPER CASE.
+ public static final String DELETED = "deleted";
+ public static final String SEEN = "seen";
+ public static final String ANSWERED = "answered";
+ public static final String FLAGGED = "flagged";
+ public static final String DRAFT = "draft";
+ public static final String RECENT = "recent";
+}
diff --git a/java/com/android/voicemailomtp/mail/MailTransport.java b/java/com/android/voicemailomtp/mail/MailTransport.java
new file mode 100644
index 000000000..3bf851fd8
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/MailTransport.java
@@ -0,0 +1,344 @@
+/*
+ * 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.voicemailomtp.mail;
+
+import android.content.Context;
+import android.net.Network;
+import android.support.annotation.VisibleForTesting;
+import com.android.voicemailomtp.OmtpEvents;
+import com.android.voicemailomtp.imap.ImapHelper;
+import com.android.voicemailomtp.mail.store.ImapStore;
+import com.android.voicemailomtp.mail.utils.LogUtils;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+/**
+ * Make connection and perform operations on mail server by reading and writing lines.
+ */
+public class MailTransport {
+ private static final String TAG = "MailTransport";
+
+ // TODO protected eventually
+ /*protected*/ public static final int SOCKET_CONNECT_TIMEOUT = 10000;
+ /*protected*/ public static final int SOCKET_READ_TIMEOUT = 60000;
+
+ private static final HostnameVerifier HOSTNAME_VERIFIER =
+ HttpsURLConnection.getDefaultHostnameVerifier();
+
+ private final Context mContext;
+ private final ImapHelper mImapHelper;
+ private final Network mNetwork;
+ private final String mHost;
+ private final int mPort;
+ private Socket mSocket;
+ private BufferedInputStream mIn;
+ private BufferedOutputStream mOut;
+ private final int mFlags;
+ private SocketCreator mSocketCreator;
+ private InetSocketAddress mAddress;
+
+ public MailTransport(Context context, ImapHelper imapHelper, Network network, String address,
+ int port, int flags) {
+ mContext = context;
+ mImapHelper = imapHelper;
+ mNetwork = network;
+ mHost = address;
+ mPort = port;
+ mFlags = flags;
+ }
+
+ /**
+ * Returns a new transport, using the current transport as a model. The new transport is
+ * configured identically, but not opened or connected in any way.
+ */
+ @Override
+ public MailTransport clone() {
+ return new MailTransport(mContext, mImapHelper, mNetwork, mHost, mPort, mFlags);
+ }
+
+ public boolean canTrySslSecurity() {
+ return (mFlags & ImapStore.FLAG_SSL) != 0;
+ }
+
+ public boolean canTrustAllCertificates() {
+ return (mFlags & ImapStore.FLAG_TRUST_ALL) != 0;
+ }
+
+ /**
+ * Attempts to open a connection using the Uri supplied for connection parameters. Will attempt
+ * an SSL connection if indicated.
+ */
+ public void open() throws MessagingException {
+ LogUtils.d(TAG, "*** IMAP open " + mHost + ":" + String.valueOf(mPort));
+
+ List<InetSocketAddress> socketAddresses = new ArrayList<InetSocketAddress>();
+
+ if (mNetwork == null) {
+ socketAddresses.add(new InetSocketAddress(mHost, mPort));
+ } else {
+ try {
+ InetAddress[] inetAddresses = mNetwork.getAllByName(mHost);
+ if (inetAddresses.length == 0) {
+ throw new MessagingException(MessagingException.IOERROR,
+ "Host name " + mHost + "cannot be resolved on designated network");
+ }
+ for (int i = 0; i < inetAddresses.length; i++) {
+ socketAddresses.add(new InetSocketAddress(inetAddresses[i], mPort));
+ }
+ } catch (IOException ioe) {
+ LogUtils.d(TAG, ioe.toString());
+ mImapHelper.handleEvent(OmtpEvents.DATA_CANNOT_RESOLVE_HOST_ON_NETWORK);
+ throw new MessagingException(MessagingException.IOERROR, ioe.toString());
+ }
+ }
+
+ boolean success = false;
+ while (socketAddresses.size() > 0) {
+ mSocket = createSocket();
+ try {
+ mAddress = socketAddresses.remove(0);
+ mSocket.connect(mAddress, SOCKET_CONNECT_TIMEOUT);
+
+ if (canTrySslSecurity()) {
+ /*
+ SSLSocket cannot be created with a connection timeout, so instead of doing a
+ direct SSL connection, we connect with a normal connection and upgrade it into
+ SSL
+ */
+ reopenTls();
+ } else {
+ mIn = new BufferedInputStream(mSocket.getInputStream(), 1024);
+ mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512);
+ mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
+ }
+ success = true;
+ return;
+ } catch (IOException ioe) {
+ LogUtils.d(TAG, ioe.toString());
+ if (socketAddresses.size() == 0) {
+ // Only throw an error when there are no more sockets to try.
+ mImapHelper.handleEvent(OmtpEvents.DATA_ALL_SOCKET_CONNECTION_FAILED);
+ throw new MessagingException(MessagingException.IOERROR, ioe.toString());
+ }
+ } finally {
+ if (!success) {
+ try {
+ mSocket.close();
+ mSocket = null;
+ } catch (IOException ioe) {
+ throw new MessagingException(MessagingException.IOERROR, ioe.toString());
+ }
+
+ }
+ }
+ }
+ }
+
+ // For testing. We need something that can replace the behavior of "new Socket()"
+ @VisibleForTesting
+ interface SocketCreator {
+
+ Socket createSocket() throws MessagingException;
+ }
+
+ @VisibleForTesting
+ void setSocketCreator(SocketCreator creator) {
+ mSocketCreator = creator;
+ }
+
+ protected Socket createSocket() throws MessagingException {
+ if (mSocketCreator != null) {
+ return mSocketCreator.createSocket();
+ }
+
+ if (mNetwork == null) {
+ LogUtils.v(TAG, "createSocket: network not specified");
+ return new Socket();
+ }
+
+ try {
+ LogUtils.v(TAG, "createSocket: network specified");
+ return mNetwork.getSocketFactory().createSocket();
+ } catch (IOException ioe) {
+ LogUtils.d(TAG, ioe.toString());
+ throw new MessagingException(MessagingException.IOERROR, ioe.toString());
+ }
+ }
+
+ /**
+ * Attempts to reopen a normal connection into a TLS connection.
+ */
+ public void reopenTls() throws MessagingException {
+ try {
+ LogUtils.d(TAG, "open: converting to TLS socket");
+ mSocket = HttpsURLConnection.getDefaultSSLSocketFactory()
+ .createSocket(mSocket, mAddress.getHostName(), mAddress.getPort(), true);
+ // After the socket connects to an SSL server, confirm that the hostname is as
+ // expected
+ if (!canTrustAllCertificates()) {
+ verifyHostname(mSocket, mHost);
+ }
+ mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
+ mIn = new BufferedInputStream(mSocket.getInputStream(), 1024);
+ mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512);
+
+ } catch (SSLException e) {
+ LogUtils.d(TAG, e.toString());
+ throw new CertificateValidationException(e.getMessage(), e);
+ } catch (IOException ioe) {
+ LogUtils.d(TAG, ioe.toString());
+ throw new MessagingException(MessagingException.IOERROR, ioe.toString());
+ }
+ }
+
+ /**
+ * Lightweight version of SSLCertificateSocketFactory.verifyHostname, which provides this
+ * service but is not in the public API.
+ *
+ * Verify the hostname of the certificate used by the other end of a
+ * connected socket. It is harmless to call this method redundantly if the hostname has already
+ * been verified.
+ *
+ * <p>Wildcard certificates are allowed to verify any matching hostname,
+ * so "foo.bar.example.com" is verified if the peer has a certificate
+ * for "*.example.com".
+ *
+ * @param socket An SSL socket which has been connected to a server
+ * @param hostname The expected hostname of the remote server
+ * @throws IOException if something goes wrong handshaking with the server
+ * @throws SSLPeerUnverifiedException if the server cannot prove its identity
+ */
+ private void verifyHostname(Socket socket, String hostname) throws IOException {
+ // The code at the start of OpenSSLSocketImpl.startHandshake()
+ // ensures that the call is idempotent, so we can safely call it.
+ SSLSocket ssl = (SSLSocket) socket;
+ ssl.startHandshake();
+
+ SSLSession session = ssl.getSession();
+ if (session == null) {
+ mImapHelper.handleEvent(OmtpEvents.DATA_CANNOT_ESTABLISH_SSL_SESSION);
+ throw new SSLException("Cannot verify SSL socket without session");
+ }
+ // TODO: Instead of reporting the name of the server we think we're connecting to,
+ // we should be reporting the bad name in the certificate. Unfortunately this is buried
+ // in the verifier code and is not available in the verifier API, and extracting the
+ // CN & alts is beyond the scope of this patch.
+ if (!HOSTNAME_VERIFIER.verify(hostname, session)) {
+ mImapHelper.handleEvent(OmtpEvents.DATA_SSL_INVALID_HOST_NAME);
+ throw new SSLPeerUnverifiedException("Certificate hostname not useable for server: "
+ + session.getPeerPrincipal());
+ }
+ }
+
+ public boolean isOpen() {
+ return (mIn != null && mOut != null &&
+ mSocket != null && mSocket.isConnected() && !mSocket.isClosed());
+ }
+
+ /**
+ * Close the connection. MUST NOT return any exceptions - must be "best effort" and safe.
+ */
+ public void close() {
+ try {
+ mIn.close();
+ } catch (Exception e) {
+ // May fail if the connection is already closed.
+ }
+ try {
+ mOut.close();
+ } catch (Exception e) {
+ // May fail if the connection is already closed.
+ }
+ try {
+ mSocket.close();
+ } catch (Exception e) {
+ // May fail if the connection is already closed.
+ }
+ mIn = null;
+ mOut = null;
+ mSocket = null;
+ }
+
+ public String getHost() {
+ return mHost;
+ }
+
+ public InputStream getInputStream() {
+ return mIn;
+ }
+
+ public OutputStream getOutputStream() {
+ return mOut;
+ }
+
+ /**
+ * Writes a single line to the server using \r\n termination.
+ */
+ public void writeLine(String s, String sensitiveReplacement) throws IOException {
+ if (sensitiveReplacement != null) {
+ LogUtils.d(TAG, ">>> " + sensitiveReplacement);
+ } else {
+ LogUtils.d(TAG, ">>> " + s);
+ }
+
+ OutputStream out = getOutputStream();
+ out.write(s.getBytes());
+ out.write('\r');
+ out.write('\n');
+ out.flush();
+ }
+
+ /**
+ * Reads a single line from the server, using either \r\n or \n as the delimiter. The
+ * delimiter char(s) are not included in the result.
+ */
+ public String readLine(boolean loggable) throws IOException {
+ StringBuffer sb = new StringBuffer();
+ InputStream in = getInputStream();
+ int d;
+ while ((d = in.read()) != -1) {
+ if (((char)d) == '\r') {
+ continue;
+ } else if (((char)d) == '\n') {
+ break;
+ } else {
+ sb.append((char)d);
+ }
+ }
+ if (d == -1) {
+ LogUtils.d(TAG, "End of stream reached while trying to read line.");
+ }
+ String ret = sb.toString();
+ if (loggable) {
+ LogUtils.d(TAG, "<<< " + ret);
+ }
+ return ret;
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/MeetingInfo.java b/java/com/android/voicemailomtp/mail/MeetingInfo.java
new file mode 100644
index 000000000..0505bbf2c
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/MeetingInfo.java
@@ -0,0 +1,29 @@
+/*
+ * 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.voicemailomtp.mail;
+
+public class MeetingInfo {
+ // Predefined tags; others can be added
+ public static final String MEETING_DTSTAMP = "DTSTAMP";
+ public static final String MEETING_UID = "UID";
+ public static final String MEETING_ORGANIZER_EMAIL = "ORGMAIL";
+ public static final String MEETING_DTSTART = "DTSTART";
+ public static final String MEETING_DTEND = "DTEND";
+ public static final String MEETING_TITLE = "TITLE";
+ public static final String MEETING_LOCATION = "LOC";
+ public static final String MEETING_RESPONSE_REQUESTED = "RESPONSE";
+ public static final String MEETING_ALL_DAY = "ALLDAY";
+}
diff --git a/java/com/android/voicemailomtp/mail/Message.java b/java/com/android/voicemailomtp/mail/Message.java
new file mode 100644
index 000000000..41555690f
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/Message.java
@@ -0,0 +1,144 @@
+/*
+ * 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.voicemailomtp.mail;
+
+import android.support.annotation.VisibleForTesting;
+import java.util.Date;
+import java.util.HashSet;
+
+public abstract class Message implements Part, Body {
+ public static final Message[] EMPTY_ARRAY = new Message[0];
+
+ public static final String RECIPIENT_TYPE_TO = "to";
+ public static final String RECIPIENT_TYPE_CC = "cc";
+ public static final String RECIPIENT_TYPE_BCC = "bcc";
+ public enum RecipientType {
+ TO, CC, BCC,
+ }
+
+ protected String mUid;
+
+ private HashSet<String> mFlags = null;
+
+ protected Date mInternalDate;
+
+ public String getUid() {
+ return mUid;
+ }
+
+ public void setUid(String uid) {
+ this.mUid = uid;
+ }
+
+ public abstract String getSubject() throws MessagingException;
+
+ public abstract void setSubject(String subject) throws MessagingException;
+
+ public Date getInternalDate() {
+ return mInternalDate;
+ }
+
+ public void setInternalDate(Date internalDate) {
+ this.mInternalDate = internalDate;
+ }
+
+ public abstract Date getReceivedDate() throws MessagingException;
+
+ public abstract Date getSentDate() throws MessagingException;
+
+ public abstract void setSentDate(Date sentDate) throws MessagingException;
+
+ public abstract Address[] getRecipients(String type) throws MessagingException;
+
+ public abstract void setRecipients(String type, Address[] addresses)
+ throws MessagingException;
+
+ public void setRecipient(String type, Address address) throws MessagingException {
+ setRecipients(type, new Address[] {
+ address
+ });
+ }
+
+ public abstract Address[] getFrom() throws MessagingException;
+
+ public abstract void setFrom(Address from) throws MessagingException;
+
+ public abstract Address[] getReplyTo() throws MessagingException;
+
+ public abstract void setReplyTo(Address[] from) throws MessagingException;
+
+ // Always use these instead of getHeader("Message-ID") or setHeader("Message-ID");
+ public abstract void setMessageId(String messageId) throws MessagingException;
+ public abstract String getMessageId() throws MessagingException;
+
+ @Override
+ public boolean isMimeType(String mimeType) throws MessagingException {
+ return getContentType().startsWith(mimeType);
+ }
+
+ private HashSet<String> getFlagSet() {
+ if (mFlags == null) {
+ mFlags = new HashSet<String>();
+ }
+ return mFlags;
+ }
+
+ /*
+ * TODO Refactor Flags at some point to be able to store user defined flags.
+ */
+ public String[] getFlags() {
+ return getFlagSet().toArray(new String[] {});
+ }
+
+ /**
+ * Set/clear a flag directly, without involving overrides of {@link #setFlag} in subclasses.
+ * Only used for testing.
+ */
+ @VisibleForTesting
+ private final void setFlagDirectlyForTest(String flag, boolean set) throws MessagingException {
+ if (set) {
+ getFlagSet().add(flag);
+ } else {
+ getFlagSet().remove(flag);
+ }
+ }
+
+ public void setFlag(String flag, boolean set) throws MessagingException {
+ setFlagDirectlyForTest(flag, set);
+ }
+
+ /**
+ * This method calls setFlag(String, boolean)
+ * @param flags
+ * @param set
+ */
+ public void setFlags(String[] flags, boolean set) throws MessagingException {
+ for (String flag : flags) {
+ setFlag(flag, set);
+ }
+ }
+
+ public boolean isSet(String flag) {
+ return getFlagSet().contains(flag);
+ }
+
+ public abstract void saveChanges() throws MessagingException;
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + ':' + mUid;
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/MessageDateComparator.java b/java/com/android/voicemailomtp/mail/MessageDateComparator.java
new file mode 100644
index 000000000..37071034a
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/MessageDateComparator.java
@@ -0,0 +1,34 @@
+/*
+ * 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.voicemailomtp.mail;
+
+import java.util.Comparator;
+
+public class MessageDateComparator implements Comparator<Message> {
+ @Override
+ public int compare(Message o1, Message o2) {
+ try {
+ if (o1.getSentDate() == null) {
+ return 1;
+ } else if (o2.getSentDate() == null) {
+ return -1;
+ } else
+ return o2.getSentDate().compareTo(o1.getSentDate());
+ } catch (Exception e) {
+ return 0;
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/MessagingException.java b/java/com/android/voicemailomtp/mail/MessagingException.java
new file mode 100644
index 000000000..28550527f
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/MessagingException.java
@@ -0,0 +1,139 @@
+/*
+ * 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.voicemailomtp.mail;
+
+/**
+ * This exception is used for most types of failures that occur during server interactions.
+ *
+ * Data passed through this exception should be considered non-localized. Any strings should
+ * either be internal-only (for debugging) or server-generated.
+ *
+ * TO DO: Does it make sense to further collapse AuthenticationFailedException and
+ * CertificateValidationException and any others into this?
+ */
+public class MessagingException extends Exception {
+ public static final long serialVersionUID = -1;
+
+ public static final int NO_ERROR = -1;
+ /** Any exception that does not specify a specific issue */
+ public static final int UNSPECIFIED_EXCEPTION = 0;
+ /** Connection or IO errors */
+ public static final int IOERROR = 1;
+ /** The configuration requested TLS but the server did not support it. */
+ public static final int TLS_REQUIRED = 2;
+ /** Authentication is required but the server did not support it. */
+ public static final int AUTH_REQUIRED = 3;
+ /** General security failures */
+ public static final int GENERAL_SECURITY = 4;
+ /** Authentication failed */
+ public static final int AUTHENTICATION_FAILED = 5;
+ /** Attempt to create duplicate account */
+ public static final int DUPLICATE_ACCOUNT = 6;
+ /** Required security policies reported - advisory only */
+ public static final int SECURITY_POLICIES_REQUIRED = 7;
+ /** Required security policies not supported */
+ public static final int SECURITY_POLICIES_UNSUPPORTED = 8;
+ /** The protocol (or protocol version) isn't supported */
+ public static final int PROTOCOL_VERSION_UNSUPPORTED = 9;
+ /** The server's SSL certificate couldn't be validated */
+ public static final int CERTIFICATE_VALIDATION_ERROR = 10;
+ /** Authentication failed during autodiscover */
+ public static final int AUTODISCOVER_AUTHENTICATION_FAILED = 11;
+ /** Autodiscover completed with a result (non-error) */
+ public static final int AUTODISCOVER_AUTHENTICATION_RESULT = 12;
+ /** Ambiguous failure; server error or bad credentials */
+ public static final int AUTHENTICATION_FAILED_OR_SERVER_ERROR = 13;
+ /** The server refused access */
+ public static final int ACCESS_DENIED = 14;
+ /** The server refused access */
+ public static final int ATTACHMENT_NOT_FOUND = 15;
+ /** A client SSL certificate is required for connections to the server */
+ public static final int CLIENT_CERTIFICATE_REQUIRED = 16;
+ /** The client SSL certificate specified is invalid */
+ public static final int CLIENT_CERTIFICATE_ERROR = 17;
+ /** The server indicates it does not support OAuth authentication */
+ public static final int OAUTH_NOT_SUPPORTED = 18;
+ /** The server indicates it experienced an internal error */
+ public static final int SERVER_ERROR = 19;
+
+ protected int mExceptionType;
+ // Exception type-specific data
+ protected Object mExceptionData;
+
+ public MessagingException(String message, Throwable throwable) {
+ this(UNSPECIFIED_EXCEPTION, message, throwable);
+ }
+
+ public MessagingException(int exceptionType, String message, Throwable throwable) {
+ super(message, throwable);
+ mExceptionType = exceptionType;
+ mExceptionData = null;
+ }
+
+ /**
+ * Constructs a MessagingException with an exceptionType and a null message.
+ * @param exceptionType The exception type to set for this exception.
+ */
+ public MessagingException(int exceptionType) {
+ this(exceptionType, null, null);
+ }
+
+ /**
+ * Constructs a MessagingException with a message.
+ * @param message the message for this exception
+ */
+ public MessagingException(String message) {
+ this(UNSPECIFIED_EXCEPTION, message, null);
+ }
+
+ /**
+ * Constructs a MessagingException with an exceptionType and a message.
+ * @param exceptionType The exception type to set for this exception.
+ */
+ public MessagingException(int exceptionType, String message) {
+ this(exceptionType, message, null);
+ }
+
+ /**
+ * Constructs a MessagingException with an exceptionType, a message, and data
+ * @param exceptionType The exception type to set for this exception.
+ * @param message the message for the exception (or null)
+ * @param data exception-type specific data for the exception (or null)
+ */
+ public MessagingException(int exceptionType, String message, Object data) {
+ super(message);
+ mExceptionType = exceptionType;
+ mExceptionData = data;
+ }
+
+ /**
+ * Return the exception type. Will be OTHER_EXCEPTION if not explicitly set.
+ *
+ * @return Returns the exception type.
+ */
+ public int getExceptionType() {
+ return mExceptionType;
+ }
+ /**
+ * Return the exception data. Will be null if not explicitly set.
+ *
+ * @return Returns the exception data.
+ */
+ public Object getExceptionData() {
+ return mExceptionData;
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/Multipart.java b/java/com/android/voicemailomtp/mail/Multipart.java
new file mode 100644
index 000000000..b45ebab3d
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/Multipart.java
@@ -0,0 +1,62 @@
+/*
+ * 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.voicemailomtp.mail;
+
+import java.util.ArrayList;
+
+public abstract class Multipart implements Body {
+ protected Part mParent;
+
+ protected ArrayList<BodyPart> mParts = new ArrayList<BodyPart>();
+
+ protected String mContentType;
+
+ public void addBodyPart(BodyPart part) throws MessagingException {
+ mParts.add(part);
+ }
+
+ public void addBodyPart(BodyPart part, int index) throws MessagingException {
+ mParts.add(index, part);
+ }
+
+ public BodyPart getBodyPart(int index) throws MessagingException {
+ return mParts.get(index);
+ }
+
+ public String getContentType() throws MessagingException {
+ return mContentType;
+ }
+
+ public int getCount() throws MessagingException {
+ return mParts.size();
+ }
+
+ public boolean removeBodyPart(BodyPart part) throws MessagingException {
+ return mParts.remove(part);
+ }
+
+ public void removeBodyPart(int index) throws MessagingException {
+ mParts.remove(index);
+ }
+
+ public Part getParent() throws MessagingException {
+ return mParent;
+ }
+
+ public void setParent(Part parent) throws MessagingException {
+ this.mParent = parent;
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/PackedString.java b/java/com/android/voicemailomtp/mail/PackedString.java
new file mode 100644
index 000000000..585759611
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/PackedString.java
@@ -0,0 +1,175 @@
+/*
+ * 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.voicemailomtp.mail;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A utility class for creating and modifying Strings that are tagged and packed together.
+ *
+ * Uses non-printable (control chars) for internal delimiters; Intended for regular displayable
+ * strings only, so please use base64 or other encoding if you need to hide any binary data here.
+ *
+ * Binary compatible with Address.pack() format, which should migrate to use this code.
+ */
+public class PackedString {
+
+ /**
+ * Packing format is:
+ * element : [ value ] or [ value TAG-DELIMITER tag ]
+ * packed-string : [ element ] [ ELEMENT-DELIMITER [ element ] ]*
+ */
+ private static final char DELIMITER_ELEMENT = '\1';
+ private static final char DELIMITER_TAG = '\2';
+
+ private String mString;
+ private HashMap<String, String> mExploded;
+ private static final HashMap<String, String> EMPTY_MAP = new HashMap<String, String>();
+
+ /**
+ * Create a packed string using an already-packed string (e.g. from database)
+ * @param string packed string
+ */
+ public PackedString(String string) {
+ mString = string;
+ mExploded = null;
+ }
+
+ /**
+ * Get the value referred to by a given tag. If the tag does not exist, return null.
+ * @param tag identifier of string of interest
+ * @return returns value, or null if no string is found
+ */
+ public String get(String tag) {
+ if (mExploded == null) {
+ mExploded = explode(mString);
+ }
+ return mExploded.get(tag);
+ }
+
+ /**
+ * Return a map of all of the values referred to by a given tag. This is a shallow
+ * copy, don't edit the values.
+ * @return a map of the values in the packed string
+ */
+ public Map<String, String> unpack() {
+ if (mExploded == null) {
+ mExploded = explode(mString);
+ }
+ return new HashMap<String,String>(mExploded);
+ }
+
+ /**
+ * Read out all values into a map.
+ */
+ private static HashMap<String, String> explode(String packed) {
+ if (packed == null || packed.length() == 0) {
+ return EMPTY_MAP;
+ }
+ HashMap<String, String> map = new HashMap<String, String>();
+
+ int length = packed.length();
+ int elementStartIndex = 0;
+ int elementEndIndex = 0;
+ int tagEndIndex = packed.indexOf(DELIMITER_TAG);
+
+ while (elementStartIndex < length) {
+ elementEndIndex = packed.indexOf(DELIMITER_ELEMENT, elementStartIndex);
+ if (elementEndIndex == -1) {
+ elementEndIndex = length;
+ }
+ String tag;
+ String value;
+ if (tagEndIndex == -1 || elementEndIndex <= tagEndIndex) {
+ // in this case the DELIMITER_PERSONAL is in a future pair (or not found)
+ // so synthesize a positional tag for the value, and don't update tagEndIndex
+ value = packed.substring(elementStartIndex, elementEndIndex);
+ tag = Integer.toString(map.size());
+ } else {
+ value = packed.substring(elementStartIndex, tagEndIndex);
+ tag = packed.substring(tagEndIndex + 1, elementEndIndex);
+ // scan forward for next tag, if any
+ tagEndIndex = packed.indexOf(DELIMITER_TAG, elementEndIndex + 1);
+ }
+ map.put(tag, value);
+ elementStartIndex = elementEndIndex + 1;
+ }
+
+ return map;
+ }
+
+ /**
+ * Builder class for creating PackedString values. Can also be used for editing existing
+ * PackedString representations.
+ */
+ static public class Builder {
+ HashMap<String, String> mMap;
+
+ /**
+ * Create a builder that's empty (for filling)
+ */
+ public Builder() {
+ mMap = new HashMap<String, String>();
+ }
+
+ /**
+ * Create a builder using the values of an existing PackedString (for editing).
+ */
+ public Builder(String packed) {
+ mMap = explode(packed);
+ }
+
+ /**
+ * Add a tagged value
+ * @param tag identifier of string of interest
+ * @param value the value to record in this position. null to delete entry.
+ */
+ public void put(String tag, String value) {
+ if (value == null) {
+ mMap.remove(tag);
+ } else {
+ mMap.put(tag, value);
+ }
+ }
+
+ /**
+ * Get the value referred to by a given tag. If the tag does not exist, return null.
+ * @param tag identifier of string of interest
+ * @return returns value, or null if no string is found
+ */
+ public String get(String tag) {
+ return mMap.get(tag);
+ }
+
+ /**
+ * Pack the values and return a single, encoded string
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<String,String> entry : mMap.entrySet()) {
+ if (sb.length() > 0) {
+ sb.append(DELIMITER_ELEMENT);
+ }
+ sb.append(entry.getValue());
+ sb.append(DELIMITER_TAG);
+ sb.append(entry.getKey());
+ }
+ return sb.toString();
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/Part.java b/java/com/android/voicemailomtp/mail/Part.java
new file mode 100644
index 000000000..51f8a4c38
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/Part.java
@@ -0,0 +1,51 @@
+/*
+ * 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.voicemailomtp.mail;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public interface Part extends Fetchable {
+ public void addHeader(String name, String value) throws MessagingException;
+
+ public void removeHeader(String name) throws MessagingException;
+
+ public void setHeader(String name, String value) throws MessagingException;
+
+ public Body getBody() throws MessagingException;
+
+ public String getContentType() throws MessagingException;
+
+ public String getDisposition() throws MessagingException;
+
+ public String getContentId() throws MessagingException;
+
+ public String[] getHeader(String name) throws MessagingException;
+
+ public void setExtendedHeader(String name, String value) throws MessagingException;
+
+ public String getExtendedHeader(String name) throws MessagingException;
+
+ public int getSize() throws MessagingException;
+
+ public boolean isMimeType(String mimeType) throws MessagingException;
+
+ public String getMimeType() throws MessagingException;
+
+ public void setBody(Body body) throws MessagingException;
+
+ public void writeTo(OutputStream out) throws IOException, MessagingException;
+}
diff --git a/java/com/android/voicemailomtp/mail/PeekableInputStream.java b/java/com/android/voicemailomtp/mail/PeekableInputStream.java
new file mode 100644
index 000000000..c1181d189
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/PeekableInputStream.java
@@ -0,0 +1,80 @@
+/*
+ * 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.voicemailomtp.mail;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A filtering InputStream that allows single byte "peeks" without consuming the byte. The
+ * client of this stream can call peek() to see the next available byte in the stream
+ * and a subsequent read will still return the peeked byte.
+ */
+public class PeekableInputStream extends InputStream {
+ private final InputStream mIn;
+ private boolean mPeeked;
+ private int mPeekedByte;
+
+ public PeekableInputStream(InputStream in) {
+ this.mIn = in;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (!mPeeked) {
+ return mIn.read();
+ } else {
+ mPeeked = false;
+ return mPeekedByte;
+ }
+ }
+
+ public int peek() throws IOException {
+ if (!mPeeked) {
+ mPeekedByte = read();
+ mPeeked = true;
+ }
+ return mPeekedByte;
+ }
+
+ @Override
+ public int read(byte[] b, int offset, int length) throws IOException {
+ if (!mPeeked) {
+ return mIn.read(b, offset, length);
+ } else {
+ b[0] = (byte)mPeekedByte;
+ mPeeked = false;
+ int r = mIn.read(b, offset + 1, length - 1);
+ if (r == -1) {
+ return 1;
+ } else {
+ return r + 1;
+ }
+ }
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("PeekableInputStream(in=%s, peeked=%b, peekedByte=%d)",
+ mIn.toString(), mPeeked, mPeekedByte);
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/TempDirectory.java b/java/com/android/voicemailomtp/mail/TempDirectory.java
new file mode 100644
index 000000000..dfae36026
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/TempDirectory.java
@@ -0,0 +1,41 @@
+/*
+ * 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.voicemailomtp.mail;
+
+import android.content.Context;
+
+import java.io.File;
+
+/**
+ * TempDirectory caches the directory used for caching file. It is set up during application
+ * initialization.
+ */
+public class TempDirectory {
+ private static File sTempDirectory = null;
+
+ public static void setTempDirectory(Context context) {
+ sTempDirectory = context.getCacheDir();
+ }
+
+ public static File getTempDirectory() {
+ if (sTempDirectory == null) {
+ throw new RuntimeException(
+ "TempDirectory not set. " +
+ "If in a unit test, call Email.setTempDirectory(context) in setUp().");
+ }
+ return sTempDirectory;
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/internet/BinaryTempFileBody.java b/java/com/android/voicemailomtp/mail/internet/BinaryTempFileBody.java
new file mode 100644
index 000000000..52c43de16
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/internet/BinaryTempFileBody.java
@@ -0,0 +1,91 @@
+/*
+ * 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.voicemailomtp.mail.internet;
+
+import com.android.voicemailomtp.mail.Body;
+import com.android.voicemailomtp.mail.MessagingException;
+import com.android.voicemailomtp.mail.TempDirectory;
+
+import org.apache.commons.io.IOUtils;
+
+import android.util.Base64;
+import android.util.Base64OutputStream;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * A Body that is backed by a temp file. The Body exposes a getOutputStream method that allows
+ * the user to write to the temp file. After the write the body is available via getInputStream
+ * and writeTo one time. After writeTo is called, or the InputStream returned from
+ * getInputStream is closed the file is deleted and the Body should be considered disposed of.
+ */
+public class BinaryTempFileBody implements Body {
+ private File mFile;
+
+ /**
+ * An alternate way to put data into a BinaryTempFileBody is to simply supply an already-
+ * created file. Note that this file will be deleted after it is read.
+ * @param filePath The file containing the data to be stored on disk temporarily
+ */
+ public void setFile(String filePath) {
+ mFile = new File(filePath);
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ mFile = File.createTempFile("body", null, TempDirectory.getTempDirectory());
+ mFile.deleteOnExit();
+ return new FileOutputStream(mFile);
+ }
+
+ @Override
+ public InputStream getInputStream() throws MessagingException {
+ try {
+ return new BinaryTempFileBodyInputStream(new FileInputStream(mFile));
+ }
+ catch (IOException ioe) {
+ throw new MessagingException("Unable to open body", ioe);
+ }
+ }
+
+ @Override
+ public void writeTo(OutputStream out) throws IOException, MessagingException {
+ InputStream in = getInputStream();
+ Base64OutputStream base64Out = new Base64OutputStream(
+ out, Base64.CRLF | Base64.NO_CLOSE);
+ IOUtils.copy(in, base64Out);
+ base64Out.close();
+ mFile.delete();
+ in.close();
+ }
+
+ class BinaryTempFileBodyInputStream extends FilterInputStream {
+ public BinaryTempFileBodyInputStream(InputStream in) {
+ super(in);
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ mFile.delete();
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/internet/MimeBodyPart.java b/java/com/android/voicemailomtp/mail/internet/MimeBodyPart.java
new file mode 100644
index 000000000..8a9c45cf9
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/internet/MimeBodyPart.java
@@ -0,0 +1,207 @@
+/*
+ * 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.voicemailomtp.mail.internet;
+
+import com.android.voicemailomtp.mail.Body;
+import com.android.voicemailomtp.mail.BodyPart;
+import com.android.voicemailomtp.mail.MessagingException;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.regex.Pattern;
+
+/**
+ * TODO this is a close approximation of Message, need to update along with
+ * Message.
+ */
+public class MimeBodyPart extends BodyPart {
+ protected MimeHeader mHeader = new MimeHeader();
+ protected MimeHeader mExtendedHeader;
+ protected Body mBody;
+ protected int mSize;
+
+ // regex that matches content id surrounded by "<>" optionally.
+ private static final Pattern REMOVE_OPTIONAL_BRACKETS = Pattern.compile("^<?([^>]+)>?$");
+ // regex that matches end of line.
+ private static final Pattern END_OF_LINE = Pattern.compile("\r?\n");
+
+ public MimeBodyPart() throws MessagingException {
+ this(null);
+ }
+
+ public MimeBodyPart(Body body) throws MessagingException {
+ this(body, null);
+ }
+
+ public MimeBodyPart(Body body, String mimeType) throws MessagingException {
+ if (mimeType != null) {
+ setHeader(MimeHeader.HEADER_CONTENT_TYPE, mimeType);
+ }
+ setBody(body);
+ }
+
+ protected String getFirstHeader(String name) throws MessagingException {
+ return mHeader.getFirstHeader(name);
+ }
+
+ @Override
+ public void addHeader(String name, String value) throws MessagingException {
+ mHeader.addHeader(name, value);
+ }
+
+ @Override
+ public void setHeader(String name, String value) throws MessagingException {
+ mHeader.setHeader(name, value);
+ }
+
+ @Override
+ public String[] getHeader(String name) throws MessagingException {
+ return mHeader.getHeader(name);
+ }
+
+ @Override
+ public void removeHeader(String name) throws MessagingException {
+ mHeader.removeHeader(name);
+ }
+
+ @Override
+ public Body getBody() throws MessagingException {
+ return mBody;
+ }
+
+ @Override
+ public void setBody(Body body) throws MessagingException {
+ this.mBody = body;
+ if (body instanceof com.android.voicemailomtp.mail.Multipart) {
+ com.android.voicemailomtp.mail.Multipart multipart =
+ ((com.android.voicemailomtp.mail.Multipart)body);
+ multipart.setParent(this);
+ setHeader(MimeHeader.HEADER_CONTENT_TYPE, multipart.getContentType());
+ }
+ else if (body instanceof TextBody) {
+ String contentType = String.format("%s;\n charset=utf-8", getMimeType());
+ String name = MimeUtility.getHeaderParameter(getContentType(), "name");
+ if (name != null) {
+ contentType += String.format(";\n name=\"%s\"", name);
+ }
+ setHeader(MimeHeader.HEADER_CONTENT_TYPE, contentType);
+ setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
+ }
+ }
+
+ @Override
+ public String getContentType() throws MessagingException {
+ String contentType = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE);
+ if (contentType == null) {
+ return "text/plain";
+ } else {
+ return contentType;
+ }
+ }
+
+ @Override
+ public String getDisposition() throws MessagingException {
+ String contentDisposition = getFirstHeader(MimeHeader.HEADER_CONTENT_DISPOSITION);
+ if (contentDisposition == null) {
+ return null;
+ } else {
+ return contentDisposition;
+ }
+ }
+
+ @Override
+ public String getContentId() throws MessagingException {
+ String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID);
+ if (contentId == null) {
+ return null;
+ } else {
+ // remove optionally surrounding brackets.
+ return REMOVE_OPTIONAL_BRACKETS.matcher(contentId).replaceAll("$1");
+ }
+ }
+
+ @Override
+ public String getMimeType() throws MessagingException {
+ return MimeUtility.getHeaderParameter(getContentType(), null);
+ }
+
+ @Override
+ public boolean isMimeType(String mimeType) throws MessagingException {
+ return getMimeType().equals(mimeType);
+ }
+
+ public void setSize(int size) {
+ this.mSize = size;
+ }
+
+ @Override
+ public int getSize() throws MessagingException {
+ return mSize;
+ }
+
+ /**
+ * Set extended header
+ *
+ * @param name Extended header name
+ * @param value header value - flattened by removing CR-NL if any
+ * remove header if value is null
+ * @throws MessagingException
+ */
+ @Override
+ public void setExtendedHeader(String name, String value) throws MessagingException {
+ if (value == null) {
+ if (mExtendedHeader != null) {
+ mExtendedHeader.removeHeader(name);
+ }
+ return;
+ }
+ if (mExtendedHeader == null) {
+ mExtendedHeader = new MimeHeader();
+ }
+ mExtendedHeader.setHeader(name, END_OF_LINE.matcher(value).replaceAll(""));
+ }
+
+ /**
+ * Get extended header
+ *
+ * @param name Extended header name
+ * @return header value - null if header does not exist
+ * @throws MessagingException
+ */
+ @Override
+ public String getExtendedHeader(String name) throws MessagingException {
+ if (mExtendedHeader == null) {
+ return null;
+ }
+ return mExtendedHeader.getFirstHeader(name);
+ }
+
+ /**
+ * Write the MimeMessage out in MIME format.
+ */
+ @Override
+ public void writeTo(OutputStream out) throws IOException, MessagingException {
+ BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
+ mHeader.writeTo(out);
+ writer.write("\r\n");
+ writer.flush();
+ if (mBody != null) {
+ mBody.writeTo(out);
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/internet/MimeHeader.java b/java/com/android/voicemailomtp/mail/internet/MimeHeader.java
new file mode 100644
index 000000000..4b0aea749
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/internet/MimeHeader.java
@@ -0,0 +1,161 @@
+/*
+ * 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.voicemailomtp.mail.internet;
+
+import com.android.voicemailomtp.mail.MessagingException;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+
+public class MimeHeader {
+ /**
+ * Application specific header that contains Store specific information about an attachment.
+ * In IMAP this contains the IMAP BODYSTRUCTURE part id so that the ImapStore can later
+ * retrieve the attachment at will from the server.
+ * The info is recorded from this header on LocalStore.appendMessage and is put back
+ * into the MIME data by LocalStore.fetch.
+ */
+ public static final String HEADER_ANDROID_ATTACHMENT_STORE_DATA = "X-Android-Attachment-StoreData";
+
+ public static final String HEADER_CONTENT_TYPE = "Content-Type";
+ public static final String HEADER_CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
+ public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition";
+ public static final String HEADER_CONTENT_ID = "Content-ID";
+
+ /**
+ * Fields that should be omitted when writing the header using writeTo()
+ */
+ private static final String[] WRITE_OMIT_FIELDS = {
+// HEADER_ANDROID_ATTACHMENT_DOWNLOADED,
+// HEADER_ANDROID_ATTACHMENT_ID,
+ HEADER_ANDROID_ATTACHMENT_STORE_DATA
+ };
+
+ protected final ArrayList<Field> mFields = new ArrayList<Field>();
+
+ public void clear() {
+ mFields.clear();
+ }
+
+ public String getFirstHeader(String name) throws MessagingException {
+ String[] header = getHeader(name);
+ if (header == null) {
+ return null;
+ }
+ return header[0];
+ }
+
+ public void addHeader(String name, String value) throws MessagingException {
+ mFields.add(new Field(name, value));
+ }
+
+ public void setHeader(String name, String value) throws MessagingException {
+ if (name == null || value == null) {
+ return;
+ }
+ removeHeader(name);
+ addHeader(name, value);
+ }
+
+ public String[] getHeader(String name) throws MessagingException {
+ ArrayList<String> values = new ArrayList<String>();
+ for (Field field : mFields) {
+ if (field.name.equalsIgnoreCase(name)) {
+ values.add(field.value);
+ }
+ }
+ if (values.size() == 0) {
+ return null;
+ }
+ return values.toArray(new String[] {});
+ }
+
+ public void removeHeader(String name) throws MessagingException {
+ ArrayList<Field> removeFields = new ArrayList<Field>();
+ for (Field field : mFields) {
+ if (field.name.equalsIgnoreCase(name)) {
+ removeFields.add(field);
+ }
+ }
+ mFields.removeAll(removeFields);
+ }
+
+ /**
+ * Write header into String
+ *
+ * @return CR-NL separated header string except the headers in writeOmitFields
+ * null if header is empty
+ */
+ public String writeToString() {
+ if (mFields.size() == 0) {
+ return null;
+ }
+ StringBuilder builder = new StringBuilder();
+ for (Field field : mFields) {
+ if (!arrayContains(WRITE_OMIT_FIELDS, field.name)) {
+ builder.append(field.name + ": " + field.value + "\r\n");
+ }
+ }
+ return builder.toString();
+ }
+
+ public void writeTo(OutputStream out) throws IOException, MessagingException {
+ BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
+ for (Field field : mFields) {
+ if (!arrayContains(WRITE_OMIT_FIELDS, field.name)) {
+ writer.write(field.name + ": " + field.value + "\r\n");
+ }
+ }
+ writer.flush();
+ }
+
+ private static class Field {
+ final String name;
+ final String value;
+
+ public Field(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return name + "=" + value;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return (mFields == null) ? null : mFields.toString();
+ }
+
+ public final static boolean arrayContains(Object[] a, Object o) {
+ int index = arrayIndex(a, o);
+ return (index >= 0);
+ }
+
+ public final static int arrayIndex(Object[] a, Object o) {
+ for (int i = 0, count = a.length; i < count; i++) {
+ if (a[i].equals(o)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/internet/MimeMessage.java b/java/com/android/voicemailomtp/mail/internet/MimeMessage.java
new file mode 100644
index 000000000..a11cd6d83
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/internet/MimeMessage.java
@@ -0,0 +1,675 @@
+/*
+ * 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.voicemailomtp.mail.internet;
+
+import com.android.voicemailomtp.mail.Address;
+import com.android.voicemailomtp.mail.Body;
+import com.android.voicemailomtp.mail.BodyPart;
+import com.android.voicemailomtp.mail.Message;
+import com.android.voicemailomtp.mail.MessagingException;
+import com.android.voicemailomtp.mail.Multipart;
+import com.android.voicemailomtp.mail.Part;
+import com.android.voicemailomtp.mail.utils.LogUtils;
+
+import org.apache.james.mime4j.BodyDescriptor;
+import org.apache.james.mime4j.ContentHandler;
+import org.apache.james.mime4j.EOLConvertingInputStream;
+import org.apache.james.mime4j.MimeStreamParser;
+import org.apache.james.mime4j.field.DateTimeField;
+import org.apache.james.mime4j.field.Field;
+
+import android.text.TextUtils;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Stack;
+import java.util.regex.Pattern;
+
+/**
+ * An implementation of Message that stores all of its metadata in RFC 822 and
+ * RFC 2045 style headers.
+ *
+ * NOTE: Automatic generation of a local message-id is becoming unwieldy and should be removed.
+ * It would be better to simply do it explicitly on local creation of new outgoing messages.
+ */
+public class MimeMessage extends Message {
+ private MimeHeader mHeader;
+ private MimeHeader mExtendedHeader;
+
+ // NOTE: The fields here are transcribed out of headers, and values stored here will supersede
+ // the values found in the headers. Use caution to prevent any out-of-phase errors. In
+ // particular, any adds/changes/deletes here must be echoed by changes in the parse() function.
+ private Address[] mFrom;
+ private Address[] mTo;
+ private Address[] mCc;
+ private Address[] mBcc;
+ private Address[] mReplyTo;
+ private Date mSentDate;
+ private Body mBody;
+ protected int mSize;
+ private boolean mInhibitLocalMessageId = false;
+ private boolean mComplete = true;
+
+ // Shared random source for generating local message-id values
+ private static final java.util.Random sRandom = new java.util.Random();
+
+ // In MIME, en_US-like date format should be used. In other words "MMM" should be encoded to
+ // "Jan", not the other localized format like "Ene" (meaning January in locale es).
+ // This conversion is used when generating outgoing MIME messages. Incoming MIME date
+ // headers are parsed by org.apache.james.mime4j.field.DateTimeField which does not have any
+ // localization code.
+ private static final SimpleDateFormat DATE_FORMAT =
+ new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);
+
+ // regex that matches content id surrounded by "<>" optionally.
+ private static final Pattern REMOVE_OPTIONAL_BRACKETS = Pattern.compile("^<?([^>]+)>?$");
+ // regex that matches end of line.
+ private static final Pattern END_OF_LINE = Pattern.compile("\r?\n");
+
+ public MimeMessage() {
+ mHeader = null;
+ }
+
+ /**
+ * Generate a local message id. This is only used when none has been assigned, and is
+ * installed lazily. Any remote (typically server-assigned) message id takes precedence.
+ * @return a long, locally-generated message-ID value
+ */
+ private static String generateMessageId() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("<");
+ for (int i = 0; i < 24; i++) {
+ // We'll use a 5-bit range (0..31)
+ final int value = sRandom.nextInt() & 31;
+ final char c = "0123456789abcdefghijklmnopqrstuv".charAt(value);
+ sb.append(c);
+ }
+ sb.append(".");
+ sb.append(Long.toString(System.currentTimeMillis()));
+ sb.append("@email.android.com>");
+ return sb.toString();
+ }
+
+ /**
+ * Parse the given InputStream using Apache Mime4J to build a MimeMessage.
+ *
+ * @param in InputStream providing message content
+ * @throws IOException
+ * @throws MessagingException
+ */
+ public MimeMessage(InputStream in) throws IOException, MessagingException {
+ parse(in);
+ }
+
+ private MimeStreamParser init() {
+ // Before parsing the input stream, clear all local fields that may be superceded by
+ // the new incoming message.
+ getMimeHeaders().clear();
+ mInhibitLocalMessageId = true;
+ mFrom = null;
+ mTo = null;
+ mCc = null;
+ mBcc = null;
+ mReplyTo = null;
+ mSentDate = null;
+ mBody = null;
+
+ final MimeStreamParser parser = new MimeStreamParser();
+ parser.setContentHandler(new MimeMessageBuilder());
+ return parser;
+ }
+
+ protected void parse(InputStream in) throws IOException, MessagingException {
+ final MimeStreamParser parser = init();
+ parser.parse(new EOLConvertingInputStream(in));
+ mComplete = !parser.getPrematureEof();
+ }
+
+ public void parse(InputStream in, EOLConvertingInputStream.Callback callback)
+ throws IOException, MessagingException {
+ final MimeStreamParser parser = init();
+ parser.parse(new EOLConvertingInputStream(in, getSize(), callback));
+ mComplete = !parser.getPrematureEof();
+ }
+
+ /**
+ * Return the internal mHeader value, with very lazy initialization.
+ * The goal is to save memory by not creating the headers until needed.
+ */
+ private MimeHeader getMimeHeaders() {
+ if (mHeader == null) {
+ mHeader = new MimeHeader();
+ }
+ return mHeader;
+ }
+
+ @Override
+ public Date getReceivedDate() throws MessagingException {
+ return null;
+ }
+
+ @Override
+ public Date getSentDate() throws MessagingException {
+ if (mSentDate == null) {
+ try {
+ DateTimeField field = (DateTimeField)Field.parse("Date: "
+ + MimeUtility.unfoldAndDecode(getFirstHeader("Date")));
+ mSentDate = field.getDate();
+ // TODO: We should make it more clear what exceptions can be thrown here,
+ // and whether they reflect a normal or error condition.
+ } catch (Exception e) {
+ LogUtils.v(LogUtils.TAG, "Message missing Date header");
+ }
+ }
+ if (mSentDate == null) {
+ // If we still don't have a date, fall back to "Delivery-date"
+ try {
+ DateTimeField field = (DateTimeField)Field.parse("Date: "
+ + MimeUtility.unfoldAndDecode(getFirstHeader("Delivery-date")));
+ mSentDate = field.getDate();
+ // TODO: We should make it more clear what exceptions can be thrown here,
+ // and whether they reflect a normal or error condition.
+ } catch (Exception e) {
+ LogUtils.v(LogUtils.TAG, "Message also missing Delivery-Date header");
+ }
+ }
+ return mSentDate;
+ }
+
+ @Override
+ public void setSentDate(Date sentDate) throws MessagingException {
+ setHeader("Date", DATE_FORMAT.format(sentDate));
+ this.mSentDate = sentDate;
+ }
+
+ @Override
+ public String getContentType() throws MessagingException {
+ final String contentType = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE);
+ if (contentType == null) {
+ return "text/plain";
+ } else {
+ return contentType;
+ }
+ }
+
+ @Override
+ public String getDisposition() throws MessagingException {
+ return getFirstHeader(MimeHeader.HEADER_CONTENT_DISPOSITION);
+ }
+
+ @Override
+ public String getContentId() throws MessagingException {
+ final String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID);
+ if (contentId == null) {
+ return null;
+ } else {
+ // remove optionally surrounding brackets.
+ return REMOVE_OPTIONAL_BRACKETS.matcher(contentId).replaceAll("$1");
+ }
+ }
+
+ public boolean isComplete() {
+ return mComplete;
+ }
+
+ @Override
+ public String getMimeType() throws MessagingException {
+ return MimeUtility.getHeaderParameter(getContentType(), null);
+ }
+
+ @Override
+ public int getSize() throws MessagingException {
+ return mSize;
+ }
+
+ /**
+ * Returns a list of the given recipient type from this message. If no addresses are
+ * found the method returns an empty array.
+ */
+ @Override
+ public Address[] getRecipients(String type) throws MessagingException {
+ if (type == RECIPIENT_TYPE_TO) {
+ if (mTo == null) {
+ mTo = Address.parse(MimeUtility.unfold(getFirstHeader("To")));
+ }
+ return mTo;
+ } else if (type == RECIPIENT_TYPE_CC) {
+ if (mCc == null) {
+ mCc = Address.parse(MimeUtility.unfold(getFirstHeader("CC")));
+ }
+ return mCc;
+ } else if (type == RECIPIENT_TYPE_BCC) {
+ if (mBcc == null) {
+ mBcc = Address.parse(MimeUtility.unfold(getFirstHeader("BCC")));
+ }
+ return mBcc;
+ } else {
+ throw new MessagingException("Unrecognized recipient type.");
+ }
+ }
+
+ @Override
+ public void setRecipients(String type, Address[] addresses) throws MessagingException {
+ final int TO_LENGTH = 4; // "To: "
+ final int CC_LENGTH = 4; // "Cc: "
+ final int BCC_LENGTH = 5; // "Bcc: "
+ if (type == RECIPIENT_TYPE_TO) {
+ if (addresses == null || addresses.length == 0) {
+ removeHeader("To");
+ this.mTo = null;
+ } else {
+ setHeader("To", MimeUtility.fold(Address.toHeader(addresses), TO_LENGTH));
+ this.mTo = addresses;
+ }
+ } else if (type == RECIPIENT_TYPE_CC) {
+ if (addresses == null || addresses.length == 0) {
+ removeHeader("CC");
+ this.mCc = null;
+ } else {
+ setHeader("CC", MimeUtility.fold(Address.toHeader(addresses), CC_LENGTH));
+ this.mCc = addresses;
+ }
+ } else if (type == RECIPIENT_TYPE_BCC) {
+ if (addresses == null || addresses.length == 0) {
+ removeHeader("BCC");
+ this.mBcc = null;
+ } else {
+ setHeader("BCC", MimeUtility.fold(Address.toHeader(addresses), BCC_LENGTH));
+ this.mBcc = addresses;
+ }
+ } else {
+ throw new MessagingException("Unrecognized recipient type.");
+ }
+ }
+
+ /**
+ * Returns the unfolded, decoded value of the Subject header.
+ */
+ @Override
+ public String getSubject() throws MessagingException {
+ return MimeUtility.unfoldAndDecode(getFirstHeader("Subject"));
+ }
+
+ @Override
+ public void setSubject(String subject) throws MessagingException {
+ final int HEADER_NAME_LENGTH = 9; // "Subject: "
+ setHeader("Subject", MimeUtility.foldAndEncode2(subject, HEADER_NAME_LENGTH));
+ }
+
+ @Override
+ public Address[] getFrom() throws MessagingException {
+ if (mFrom == null) {
+ String list = MimeUtility.unfold(getFirstHeader("From"));
+ if (list == null || list.length() == 0) {
+ list = MimeUtility.unfold(getFirstHeader("Sender"));
+ }
+ mFrom = Address.parse(list);
+ }
+ return mFrom;
+ }
+
+ @Override
+ public void setFrom(Address from) throws MessagingException {
+ final int FROM_LENGTH = 6; // "From: "
+ if (from != null) {
+ setHeader("From", MimeUtility.fold(from.toHeader(), FROM_LENGTH));
+ this.mFrom = new Address[] {
+ from
+ };
+ } else {
+ this.mFrom = null;
+ }
+ }
+
+ @Override
+ public Address[] getReplyTo() throws MessagingException {
+ if (mReplyTo == null) {
+ mReplyTo = Address.parse(MimeUtility.unfold(getFirstHeader("Reply-to")));
+ }
+ return mReplyTo;
+ }
+
+ @Override
+ public void setReplyTo(Address[] replyTo) throws MessagingException {
+ final int REPLY_TO_LENGTH = 10; // "Reply-to: "
+ if (replyTo == null || replyTo.length == 0) {
+ removeHeader("Reply-to");
+ mReplyTo = null;
+ } else {
+ setHeader("Reply-to", MimeUtility.fold(Address.toHeader(replyTo), REPLY_TO_LENGTH));
+ mReplyTo = replyTo;
+ }
+ }
+
+ /**
+ * Set the mime "Message-ID" header
+ * @param messageId the new Message-ID value
+ * @throws MessagingException
+ */
+ @Override
+ public void setMessageId(String messageId) throws MessagingException {
+ setHeader("Message-ID", messageId);
+ }
+
+ /**
+ * Get the mime "Message-ID" header. This value will be preloaded with a locally-generated
+ * random ID, if the value has not previously been set. Local generation can be inhibited/
+ * overridden by explicitly clearing the headers, removing the message-id header, etc.
+ * @return the Message-ID header string, or null if explicitly has been set to null
+ */
+ @Override
+ public String getMessageId() throws MessagingException {
+ String messageId = getFirstHeader("Message-ID");
+ if (messageId == null && !mInhibitLocalMessageId) {
+ messageId = generateMessageId();
+ setMessageId(messageId);
+ }
+ return messageId;
+ }
+
+ @Override
+ public void saveChanges() throws MessagingException {
+ throw new MessagingException("saveChanges not yet implemented");
+ }
+
+ @Override
+ public Body getBody() throws MessagingException {
+ return mBody;
+ }
+
+ @Override
+ public void setBody(Body body) throws MessagingException {
+ this.mBody = body;
+ if (body instanceof Multipart) {
+ final Multipart multipart = ((Multipart)body);
+ multipart.setParent(this);
+ setHeader(MimeHeader.HEADER_CONTENT_TYPE, multipart.getContentType());
+ setHeader("MIME-Version", "1.0");
+ }
+ else if (body instanceof TextBody) {
+ setHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\n charset=utf-8",
+ getMimeType()));
+ setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
+ }
+ }
+
+ protected String getFirstHeader(String name) throws MessagingException {
+ return getMimeHeaders().getFirstHeader(name);
+ }
+
+ @Override
+ public void addHeader(String name, String value) throws MessagingException {
+ getMimeHeaders().addHeader(name, value);
+ }
+
+ @Override
+ public void setHeader(String name, String value) throws MessagingException {
+ getMimeHeaders().setHeader(name, value);
+ }
+
+ @Override
+ public String[] getHeader(String name) throws MessagingException {
+ return getMimeHeaders().getHeader(name);
+ }
+
+ @Override
+ public void removeHeader(String name) throws MessagingException {
+ getMimeHeaders().removeHeader(name);
+ if ("Message-ID".equalsIgnoreCase(name)) {
+ mInhibitLocalMessageId = true;
+ }
+ }
+
+ /**
+ * Set extended header
+ *
+ * @param name Extended header name
+ * @param value header value - flattened by removing CR-NL if any
+ * remove header if value is null
+ * @throws MessagingException
+ */
+ @Override
+ public void setExtendedHeader(String name, String value) throws MessagingException {
+ if (value == null) {
+ if (mExtendedHeader != null) {
+ mExtendedHeader.removeHeader(name);
+ }
+ return;
+ }
+ if (mExtendedHeader == null) {
+ mExtendedHeader = new MimeHeader();
+ }
+ mExtendedHeader.setHeader(name, END_OF_LINE.matcher(value).replaceAll(""));
+ }
+
+ /**
+ * Get extended header
+ *
+ * @param name Extended header name
+ * @return header value - null if header does not exist
+ * @throws MessagingException
+ */
+ @Override
+ public String getExtendedHeader(String name) throws MessagingException {
+ if (mExtendedHeader == null) {
+ return null;
+ }
+ return mExtendedHeader.getFirstHeader(name);
+ }
+
+ /**
+ * Set entire extended headers from String
+ *
+ * @param headers Extended header and its value - "CR-NL-separated pairs
+ * if null or empty, remove entire extended headers
+ * @throws MessagingException
+ */
+ public void setExtendedHeaders(String headers) throws MessagingException {
+ if (TextUtils.isEmpty(headers)) {
+ mExtendedHeader = null;
+ } else {
+ mExtendedHeader = new MimeHeader();
+ for (final String header : END_OF_LINE.split(headers)) {
+ final String[] tokens = header.split(":", 2);
+ if (tokens.length != 2) {
+ throw new MessagingException("Illegal extended headers: " + headers);
+ }
+ mExtendedHeader.setHeader(tokens[0].trim(), tokens[1].trim());
+ }
+ }
+ }
+
+ /**
+ * Get entire extended headers as String
+ *
+ * @return "CR-NL-separated extended headers - null if extended header does not exist
+ */
+ public String getExtendedHeaders() {
+ if (mExtendedHeader != null) {
+ return mExtendedHeader.writeToString();
+ }
+ return null;
+ }
+
+ /**
+ * Write message header and body to output stream
+ *
+ * @param out Output steam to write message header and body.
+ */
+ @Override
+ public void writeTo(OutputStream out) throws IOException, MessagingException {
+ final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
+ // Force creation of local message-id
+ getMessageId();
+ getMimeHeaders().writeTo(out);
+ // mExtendedHeader will not be write out to external output stream,
+ // because it is intended to internal use.
+ writer.write("\r\n");
+ writer.flush();
+ if (mBody != null) {
+ mBody.writeTo(out);
+ }
+ }
+
+ @Override
+ public InputStream getInputStream() throws MessagingException {
+ return null;
+ }
+
+ class MimeMessageBuilder implements ContentHandler {
+ private final Stack<Object> stack = new Stack<Object>();
+
+ public MimeMessageBuilder() {
+ }
+
+ private void expect(Class<?> c) {
+ if (!c.isInstance(stack.peek())) {
+ throw new IllegalStateException("Internal stack error: " + "Expected '"
+ + c.getName() + "' found '" + stack.peek().getClass().getName() + "'");
+ }
+ }
+
+ @Override
+ public void startMessage() {
+ if (stack.isEmpty()) {
+ stack.push(MimeMessage.this);
+ } else {
+ expect(Part.class);
+ try {
+ final MimeMessage m = new MimeMessage();
+ ((Part)stack.peek()).setBody(m);
+ stack.push(m);
+ } catch (MessagingException me) {
+ throw new Error(me);
+ }
+ }
+ }
+
+ @Override
+ public void endMessage() {
+ expect(MimeMessage.class);
+ stack.pop();
+ }
+
+ @Override
+ public void startHeader() {
+ expect(Part.class);
+ }
+
+ @Override
+ public void field(String fieldData) {
+ expect(Part.class);
+ try {
+ final String[] tokens = fieldData.split(":", 2);
+ ((Part)stack.peek()).addHeader(tokens[0], tokens[1].trim());
+ } catch (MessagingException me) {
+ throw new Error(me);
+ }
+ }
+
+ @Override
+ public void endHeader() {
+ expect(Part.class);
+ }
+
+ @Override
+ public void startMultipart(BodyDescriptor bd) {
+ expect(Part.class);
+
+ final Part e = (Part)stack.peek();
+ try {
+ final MimeMultipart multiPart = new MimeMultipart(e.getContentType());
+ e.setBody(multiPart);
+ stack.push(multiPart);
+ } catch (MessagingException me) {
+ throw new Error(me);
+ }
+ }
+
+ @Override
+ public void body(BodyDescriptor bd, InputStream in) throws IOException {
+ expect(Part.class);
+ final Body body = MimeUtility.decodeBody(in, bd.getTransferEncoding());
+ try {
+ ((Part)stack.peek()).setBody(body);
+ } catch (MessagingException me) {
+ throw new Error(me);
+ }
+ }
+
+ @Override
+ public void endMultipart() {
+ stack.pop();
+ }
+
+ @Override
+ public void startBodyPart() {
+ expect(MimeMultipart.class);
+
+ try {
+ final MimeBodyPart bodyPart = new MimeBodyPart();
+ ((MimeMultipart)stack.peek()).addBodyPart(bodyPart);
+ stack.push(bodyPart);
+ } catch (MessagingException me) {
+ throw new Error(me);
+ }
+ }
+
+ @Override
+ public void endBodyPart() {
+ expect(BodyPart.class);
+ stack.pop();
+ }
+
+ @Override
+ public void epilogue(InputStream is) throws IOException {
+ expect(MimeMultipart.class);
+ final StringBuilder sb = new StringBuilder();
+ int b;
+ while ((b = is.read()) != -1) {
+ sb.append((char)b);
+ }
+ // TODO: why is this commented out?
+ // ((Multipart) stack.peek()).setEpilogue(sb.toString());
+ }
+
+ @Override
+ public void preamble(InputStream is) throws IOException {
+ expect(MimeMultipart.class);
+ final StringBuilder sb = new StringBuilder();
+ int b;
+ while ((b = is.read()) != -1) {
+ sb.append((char)b);
+ }
+ try {
+ ((MimeMultipart)stack.peek()).setPreamble(sb.toString());
+ } catch (MessagingException me) {
+ throw new Error(me);
+ }
+ }
+
+ @Override
+ public void raw(InputStream is) throws IOException {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/internet/MimeMultipart.java b/java/com/android/voicemailomtp/mail/internet/MimeMultipart.java
new file mode 100644
index 000000000..111924336
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/internet/MimeMultipart.java
@@ -0,0 +1,112 @@
+/*
+ * 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.voicemailomtp.mail.internet;
+
+import com.android.voicemailomtp.mail.BodyPart;
+import com.android.voicemailomtp.mail.MessagingException;
+import com.android.voicemailomtp.mail.Multipart;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+public class MimeMultipart extends Multipart {
+ protected String mPreamble;
+
+ protected String mContentType;
+
+ protected String mBoundary;
+
+ protected String mSubType;
+
+ public MimeMultipart() throws MessagingException {
+ mBoundary = generateBoundary();
+ setSubType("mixed");
+ }
+
+ public MimeMultipart(String contentType) throws MessagingException {
+ this.mContentType = contentType;
+ try {
+ mSubType = MimeUtility.getHeaderParameter(contentType, null).split("/")[1];
+ mBoundary = MimeUtility.getHeaderParameter(contentType, "boundary");
+ if (mBoundary == null) {
+ throw new MessagingException("MultiPart does not contain boundary: " + contentType);
+ }
+ } catch (Exception e) {
+ throw new MessagingException(
+ "Invalid MultiPart Content-Type; must contain subtype and boundary. ("
+ + contentType + ")", e);
+ }
+ }
+
+ public String generateBoundary() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("----");
+ for (int i = 0; i < 30; i++) {
+ sb.append(Integer.toString((int)(Math.random() * 35), 36));
+ }
+ return sb.toString().toUpperCase();
+ }
+
+ public String getPreamble() throws MessagingException {
+ return mPreamble;
+ }
+
+ public void setPreamble(String preamble) throws MessagingException {
+ this.mPreamble = preamble;
+ }
+
+ @Override
+ public String getContentType() throws MessagingException {
+ return mContentType;
+ }
+
+ public void setSubType(String subType) throws MessagingException {
+ this.mSubType = subType;
+ mContentType = String.format("multipart/%s; boundary=\"%s\"", subType, mBoundary);
+ }
+
+ @Override
+ public void writeTo(OutputStream out) throws IOException, MessagingException {
+ BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
+
+ if (mPreamble != null) {
+ writer.write(mPreamble + "\r\n");
+ }
+
+ for (int i = 0, count = mParts.size(); i < count; i++) {
+ BodyPart bodyPart = mParts.get(i);
+ writer.write("--" + mBoundary + "\r\n");
+ writer.flush();
+ bodyPart.writeTo(out);
+ writer.write("\r\n");
+ }
+
+ writer.write("--" + mBoundary + "--\r\n");
+ writer.flush();
+ }
+
+ @Override
+ public InputStream getInputStream() throws MessagingException {
+ return null;
+ }
+
+ public String getSubTypeForTest() {
+ return mSubType;
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/internet/MimeUtility.java b/java/com/android/voicemailomtp/mail/internet/MimeUtility.java
new file mode 100644
index 000000000..4d310b0f5
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/internet/MimeUtility.java
@@ -0,0 +1,416 @@
+/*
+ * 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.voicemailomtp.mail.internet;
+
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Base64DataException;
+import android.util.Base64InputStream;
+
+import com.android.voicemailomtp.mail.Body;
+import com.android.voicemailomtp.mail.BodyPart;
+import com.android.voicemailomtp.mail.Message;
+import com.android.voicemailomtp.mail.MessagingException;
+import com.android.voicemailomtp.mail.Multipart;
+import com.android.voicemailomtp.mail.Part;
+import com.android.voicemailomtp.VvmLog;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.james.mime4j.codec.EncoderUtil;
+import org.apache.james.mime4j.decoder.DecoderUtil;
+import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
+import org.apache.james.mime4j.util.CharsetUtil;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class MimeUtility {
+ private static final String LOG_TAG = "Email";
+
+ public static final String MIME_TYPE_RFC822 = "message/rfc822";
+ private final static Pattern PATTERN_CR_OR_LF = Pattern.compile("\r|\n");
+
+ /**
+ * Replace sequences of CRLF+WSP with WSP. Tries to preserve original string
+ * object whenever possible.
+ */
+ public static String unfold(String s) {
+ if (s == null) {
+ return null;
+ }
+ Matcher patternMatcher = PATTERN_CR_OR_LF.matcher(s);
+ if (patternMatcher.find()) {
+ patternMatcher.reset();
+ s = patternMatcher.replaceAll("");
+ }
+ return s;
+ }
+
+ public static String decode(String s) {
+ if (s == null) {
+ return null;
+ }
+ return DecoderUtil.decodeEncodedWords(s);
+ }
+
+ public static String unfoldAndDecode(String s) {
+ return decode(unfold(s));
+ }
+
+ // TODO implement proper foldAndEncode
+ // NOTE: When this really works, we *must* remove all calls to foldAndEncode2() to prevent
+ // duplication of encoding.
+ public static String foldAndEncode(String s) {
+ return s;
+ }
+
+ /**
+ * INTERIM version of foldAndEncode that will be used only by Subject: headers.
+ * This is safer than implementing foldAndEncode() (see above) and risking unknown damage
+ * to other headers.
+ *
+ * TODO: Copy this code to foldAndEncode(), get rid of this function, confirm all working OK.
+ *
+ * @param s original string to encode and fold
+ * @param usedCharacters number of characters already used up by header name
+
+ * @return the String ready to be transmitted
+ */
+ public static String foldAndEncode2(String s, int usedCharacters) {
+ // james.mime4j.codec.EncoderUtil.java
+ // encode: encodeIfNecessary(text, usage, numUsedInHeaderName)
+ // Usage.TEXT_TOKENlooks like the right thing for subjects
+ // use WORD_ENTITY for address/names
+
+ String encoded = EncoderUtil.encodeIfNecessary(s, EncoderUtil.Usage.TEXT_TOKEN,
+ usedCharacters);
+
+ return fold(encoded, usedCharacters);
+ }
+
+ /**
+ * INTERIM: From newer version of org.apache.james (but we don't want to import
+ * the entire MimeUtil class).
+ *
+ * Splits the specified string into a multiple-line representation with
+ * lines no longer than 76 characters (because the line might contain
+ * encoded words; see <a href='http://www.faqs.org/rfcs/rfc2047.html'>RFC
+ * 2047</a> section 2). If the string contains non-whitespace sequences
+ * longer than 76 characters a line break is inserted at the whitespace
+ * character following the sequence resulting in a line longer than 76
+ * characters.
+ *
+ * @param s
+ * string to split.
+ * @param usedCharacters
+ * number of characters already used up. Usually the number of
+ * characters for header field name plus colon and one space.
+ * @return a multiple-line representation of the given string.
+ */
+ public static String fold(String s, int usedCharacters) {
+ final int maxCharacters = 76;
+
+ final int length = s.length();
+ if (usedCharacters + length <= maxCharacters)
+ return s;
+
+ StringBuilder sb = new StringBuilder();
+
+ int lastLineBreak = -usedCharacters;
+ int wspIdx = indexOfWsp(s, 0);
+ while (true) {
+ if (wspIdx == length) {
+ sb.append(s.substring(Math.max(0, lastLineBreak)));
+ return sb.toString();
+ }
+
+ int nextWspIdx = indexOfWsp(s, wspIdx + 1);
+
+ if (nextWspIdx - lastLineBreak > maxCharacters) {
+ sb.append(s.substring(Math.max(0, lastLineBreak), wspIdx));
+ sb.append("\r\n");
+ lastLineBreak = wspIdx;
+ }
+
+ wspIdx = nextWspIdx;
+ }
+ }
+
+ /**
+ * INTERIM: From newer version of org.apache.james (but we don't want to import
+ * the entire MimeUtil class).
+ *
+ * Search for whitespace.
+ */
+ private static int indexOfWsp(String s, int fromIndex) {
+ final int len = s.length();
+ for (int index = fromIndex; index < len; index++) {
+ char c = s.charAt(index);
+ if (c == ' ' || c == '\t')
+ return index;
+ }
+ return len;
+ }
+
+ /**
+ * Returns the named parameter of a header field. If name is null the first
+ * parameter is returned, or if there are no additional parameters in the
+ * field the entire field is returned. Otherwise the named parameter is
+ * searched for in a case insensitive fashion and returned. If the parameter
+ * cannot be found the method returns null.
+ *
+ * TODO: quite inefficient with the inner trimming & splitting.
+ * TODO: Also has a latent bug: uses "startsWith" to match the name, which can false-positive.
+ * TODO: The doc says that for a null name you get the first param, but you get the header.
+ * Should probably just fix the doc, but if other code assumes that behavior, fix the code.
+ * TODO: Need to decode %-escaped strings, as in: filename="ab%22d".
+ * ('+' -> ' ' conversion too? check RFC)
+ *
+ * @param header
+ * @param name
+ * @return the entire header (if name=null), the found parameter, or null
+ */
+ public static String getHeaderParameter(String header, String name) {
+ if (header == null) {
+ return null;
+ }
+ String[] parts = unfold(header).split(";");
+ if (name == null) {
+ return parts[0].trim();
+ }
+ String lowerCaseName = name.toLowerCase();
+ for (String part : parts) {
+ if (part.trim().toLowerCase().startsWith(lowerCaseName)) {
+ String[] parameterParts = part.split("=", 2);
+ if (parameterParts.length < 2) {
+ return null;
+ }
+ String parameter = parameterParts[1].trim();
+ if (parameter.startsWith("\"") && parameter.endsWith("\"")) {
+ return parameter.substring(1, parameter.length() - 1);
+ } else {
+ return parameter;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Reads the Part's body and returns a String based on any charset conversion that needed
+ * to be done.
+ * @param part The part containing a body
+ * @return a String containing the converted text in the body, or null if there was no text
+ * or an error during conversion.
+ */
+ public static String getTextFromPart(Part part) {
+ try {
+ if (part != null && part.getBody() != null) {
+ InputStream in = part.getBody().getInputStream();
+ String mimeType = part.getMimeType();
+ if (mimeType != null && MimeUtility.mimeTypeMatches(mimeType, "text/*")) {
+ /*
+ * Now we read the part into a buffer for further processing. Because
+ * the stream is now wrapped we'll remove any transfer encoding at this point.
+ */
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ IOUtils.copy(in, out);
+ in.close();
+ in = null; // we want all of our memory back, and close might not release
+
+ /*
+ * We've got a text part, so let's see if it needs to be processed further.
+ */
+ String charset = getHeaderParameter(part.getContentType(), "charset");
+ if (charset != null) {
+ /*
+ * See if there is conversion from the MIME charset to the Java one.
+ */
+ charset = CharsetUtil.toJavaCharset(charset);
+ }
+ /*
+ * No encoding, so use us-ascii, which is the standard.
+ */
+ if (charset == null) {
+ charset = "ASCII";
+ }
+ /*
+ * Convert and return as new String
+ */
+ String result = out.toString(charset);
+ out.close();
+ return result;
+ }
+ }
+
+ }
+ catch (OutOfMemoryError oom) {
+ /*
+ * If we are not able to process the body there's nothing we can do about it. Return
+ * null and let the upper layers handle the missing content.
+ */
+ VvmLog.e(LOG_TAG, "Unable to getTextFromPart " + oom.toString());
+ }
+ catch (Exception e) {
+ /*
+ * If we are not able to process the body there's nothing we can do about it. Return
+ * null and let the upper layers handle the missing content.
+ */
+ VvmLog.e(LOG_TAG, "Unable to getTextFromPart " + e.toString());
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the given mimeType matches the matchAgainst specification. The comparison
+ * ignores case and the matchAgainst string may include "*" for a wildcard (e.g. "image/*").
+ *
+ * @param mimeType A MIME type to check.
+ * @param matchAgainst A MIME type to check against. May include wildcards.
+ * @return true if the mimeType matches
+ */
+ public static boolean mimeTypeMatches(String mimeType, String matchAgainst) {
+ Pattern p = Pattern.compile(matchAgainst.replaceAll("\\*", "\\.\\*"),
+ Pattern.CASE_INSENSITIVE);
+ return p.matcher(mimeType).matches();
+ }
+
+ /**
+ * Returns true if the given mimeType matches any of the matchAgainst specifications. The
+ * comparison ignores case and the matchAgainst strings may include "*" for a wildcard
+ * (e.g. "image/*").
+ *
+ * @param mimeType A MIME type to check.
+ * @param matchAgainst An array of MIME types to check against. May include wildcards.
+ * @return true if the mimeType matches any of the matchAgainst strings
+ */
+ public static boolean mimeTypeMatches(String mimeType, String[] matchAgainst) {
+ for (String matchType : matchAgainst) {
+ if (mimeTypeMatches(mimeType, matchType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Given an input stream and a transfer encoding, return a wrapped input stream for that
+ * encoding (or the original if none is required)
+ * @param in the input stream
+ * @param contentTransferEncoding the content transfer encoding
+ * @return a properly wrapped stream
+ */
+ public static InputStream getInputStreamForContentTransferEncoding(InputStream in,
+ String contentTransferEncoding) {
+ if (contentTransferEncoding != null) {
+ contentTransferEncoding =
+ MimeUtility.getHeaderParameter(contentTransferEncoding, null);
+ if ("quoted-printable".equalsIgnoreCase(contentTransferEncoding)) {
+ in = new QuotedPrintableInputStream(in);
+ }
+ else if ("base64".equalsIgnoreCase(contentTransferEncoding)) {
+ in = new Base64InputStream(in, Base64.DEFAULT);
+ }
+ }
+ return in;
+ }
+
+ /**
+ * Removes any content transfer encoding from the stream and returns a Body.
+ */
+ public static Body decodeBody(InputStream in, String contentTransferEncoding)
+ throws IOException {
+ /*
+ * We'll remove any transfer encoding by wrapping the stream.
+ */
+ in = getInputStreamForContentTransferEncoding(in, contentTransferEncoding);
+ BinaryTempFileBody tempBody = new BinaryTempFileBody();
+ OutputStream out = tempBody.getOutputStream();
+ try {
+ IOUtils.copy(in, out);
+ } catch (Base64DataException bde) {
+ // TODO Need to fix this somehow
+ //String warning = "\n\n" + Email.getMessageDecodeErrorString();
+ //out.write(warning.getBytes());
+ } finally {
+ out.close();
+ }
+ return tempBody;
+ }
+
+ /**
+ * Recursively scan a Part (usually a Message) and sort out which of its children will be
+ * "viewable" and which will be attachments.
+ *
+ * @param part The part to be broken down
+ * @param viewables This arraylist will be populated with all parts that appear to be
+ * the "message" (e.g. text/plain & text/html)
+ * @param attachments This arraylist will be populated with all parts that appear to be
+ * attachments (including inlines)
+ * @throws MessagingException
+ */
+ public static void collectParts(Part part, ArrayList<Part> viewables,
+ ArrayList<Part> attachments) throws MessagingException {
+ String disposition = part.getDisposition();
+ String dispositionType = MimeUtility.getHeaderParameter(disposition, null);
+ // If a disposition is not specified, default to "inline"
+ boolean inline =
+ TextUtils.isEmpty(dispositionType) || "inline".equalsIgnoreCase(dispositionType);
+ // The lower-case mime type
+ String mimeType = part.getMimeType().toLowerCase();
+
+ if (part.getBody() instanceof Multipart) {
+ // If the part is Multipart but not alternative it's either mixed or
+ // something we don't know about, which means we treat it as mixed
+ // per the spec. We just process its pieces recursively.
+ MimeMultipart mp = (MimeMultipart)part.getBody();
+ boolean foundHtml = false;
+ if (mp.getSubTypeForTest().equals("alternative")) {
+ for (int i = 0; i < mp.getCount(); i++) {
+ if (mp.getBodyPart(i).isMimeType("text/html")) {
+ foundHtml = true;
+ break;
+ }
+ }
+ }
+ for (int i = 0; i < mp.getCount(); i++) {
+ // See if we have text and html
+ BodyPart bp = mp.getBodyPart(i);
+ // If there's html, don't bother loading text
+ if (foundHtml && bp.isMimeType("text/plain")) {
+ continue;
+ }
+ collectParts(bp, viewables, attachments);
+ }
+ } else if (part.getBody() instanceof Message) {
+ // If the part is an embedded message we just continue to process
+ // it, pulling any viewables or attachments into the running list.
+ Message message = (Message)part.getBody();
+ collectParts(message, viewables, attachments);
+ } else if (inline && (mimeType.startsWith("text") || (mimeType.startsWith("image")))) {
+ // We'll treat text and images as viewables
+ viewables.add(part);
+ } else {
+ // Everything else is an attachment.
+ attachments.add(part);
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/internet/TextBody.java b/java/com/android/voicemailomtp/mail/internet/TextBody.java
new file mode 100644
index 000000000..578193eff
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/internet/TextBody.java
@@ -0,0 +1,63 @@
+/*
+ * 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.voicemailomtp.mail.internet;
+
+import android.util.Base64;
+
+import com.android.voicemailomtp.mail.Body;
+import com.android.voicemailomtp.mail.MessagingException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+public class TextBody implements Body {
+ String mBody;
+
+ public TextBody(String body) {
+ this.mBody = body;
+ }
+
+ @Override
+ public void writeTo(OutputStream out) throws IOException, MessagingException {
+ byte[] bytes = mBody.getBytes("UTF-8");
+ out.write(Base64.encode(bytes, Base64.CRLF));
+ }
+
+ /**
+ * Get the text of the body in it's unencoded format.
+ * @return
+ */
+ public String getText() {
+ return mBody;
+ }
+
+ /**
+ * Returns an InputStream that reads this body's text in UTF-8 format.
+ */
+ @Override
+ public InputStream getInputStream() throws MessagingException {
+ try {
+ byte[] b = mBody.getBytes("UTF-8");
+ return new ByteArrayInputStream(b);
+ }
+ catch (UnsupportedEncodingException usee) {
+ return null;
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/store/ImapConnection.java b/java/com/android/voicemailomtp/mail/store/ImapConnection.java
new file mode 100644
index 000000000..61dcf1281
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/store/ImapConnection.java
@@ -0,0 +1,413 @@
+/*
+ * 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.voicemailomtp.mail.store;
+
+import android.util.ArraySet;
+import android.util.Base64;
+import com.android.voicemailomtp.mail.AuthenticationFailedException;
+import com.android.voicemailomtp.mail.CertificateValidationException;
+import com.android.voicemailomtp.mail.MailTransport;
+import com.android.voicemailomtp.mail.MessagingException;
+import com.android.voicemailomtp.mail.store.ImapStore.ImapException;
+import com.android.voicemailomtp.mail.store.imap.DigestMd5Utils;
+import com.android.voicemailomtp.mail.store.imap.ImapConstants;
+import com.android.voicemailomtp.mail.store.imap.ImapResponse;
+import com.android.voicemailomtp.mail.store.imap.ImapResponseParser;
+import com.android.voicemailomtp.mail.store.imap.ImapUtility;
+import com.android.voicemailomtp.mail.utils.LogUtils;
+import com.android.voicemailomtp.OmtpEvents;
+import com.android.voicemailomtp.VvmLog;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.net.ssl.SSLException;
+
+/**
+ * A cacheable class that stores the details for a single IMAP connection.
+ */
+public class ImapConnection {
+ private final String TAG = "ImapConnection";
+
+ private String mLoginPhrase;
+ private ImapStore mImapStore;
+ private MailTransport mTransport;
+ private ImapResponseParser mParser;
+ private Set<String> mCapabilities = new ArraySet<>();
+
+ static final String IMAP_REDACTED_LOG = "[IMAP command redacted]";
+
+ /**
+ * Next tag to use. All connections associated to the same ImapStore instance share the same
+ * counter to make tests simpler.
+ * (Some of the tests involve multiple connections but only have a single counter to track the
+ * tag.)
+ */
+ private final AtomicInteger mNextCommandTag = new AtomicInteger(0);
+
+ ImapConnection(ImapStore store) {
+ setStore(store);
+ }
+
+ void setStore(ImapStore store) {
+ // TODO: maybe we should throw an exception if the connection is not closed here,
+ // if it's not currently closed, then we won't reopen it, so if the credentials have
+ // changed, the connection will not be reestablished.
+ mImapStore = store;
+ mLoginPhrase = null;
+ }
+
+ /**
+ * Generates and returns the phrase to be used for authentication. This will be a LOGIN with
+ * username and password.
+ *
+ * @return the login command string to sent to the IMAP server
+ */
+ String getLoginPhrase() {
+ if (mLoginPhrase == null) {
+ if (mImapStore.getUsername() != null && mImapStore.getPassword() != null) {
+ // build the LOGIN string once (instead of over-and-over again.)
+ // apply the quoting here around the built-up password
+ mLoginPhrase = ImapConstants.LOGIN + " " + mImapStore.getUsername() + " "
+ + ImapUtility.imapQuoted(mImapStore.getPassword());
+ }
+ }
+ return mLoginPhrase;
+ }
+
+ public void open() throws IOException, MessagingException {
+ if (mTransport != null && mTransport.isOpen()) {
+ return;
+ }
+
+ try {
+ // copy configuration into a clean transport, if necessary
+ if (mTransport == null) {
+ mTransport = mImapStore.cloneTransport();
+ }
+
+ mTransport.open();
+
+ createParser();
+
+ // The server should greet us with something like
+ // * OK IMAP4rev1 Server
+ // consume the response before doing anything else.
+ ImapResponse response = mParser.readResponse(false);
+ if (!response.isOk()) {
+ mImapStore.getImapHelper()
+ .handleEvent(OmtpEvents.DATA_INVALID_INITIAL_SERVER_RESPONSE);
+ throw new MessagingException(
+ MessagingException.AUTHENTICATION_FAILED_OR_SERVER_ERROR,
+ "Invalid server initial response");
+ }
+
+ queryCapability();
+
+ maybeDoStartTls();
+
+ // LOGIN
+ doLogin();
+ } catch (SSLException e) {
+ LogUtils.d(TAG, "SSLException ", e);
+ mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_SSL_EXCEPTION);
+ throw new CertificateValidationException(e.getMessage(), e);
+ } catch (IOException ioe) {
+ LogUtils.d(TAG, "IOException", ioe);
+ mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_IOE_ON_OPEN);
+ throw ioe;
+ } finally {
+ destroyResponses();
+ }
+ }
+
+ void logout() {
+ try {
+ sendCommand(ImapConstants.LOGOUT, false);
+ if (!mParser.readResponse(true).is(0, ImapConstants.BYE)) {
+ VvmLog.e(TAG, "Server did not respond LOGOUT with BYE");
+ }
+ if (!mParser.readResponse(false).isOk()) {
+ VvmLog.e(TAG, "Server did not respond OK after LOGOUT");
+ }
+ } catch (IOException | MessagingException e) {
+ VvmLog.e(TAG, "Error while logging out:" + e);
+ }
+ }
+
+ /**
+ * Closes the connection and releases all resources. This connection can not be used again
+ * until {@link #setStore(ImapStore)} is called.
+ */
+ void close() {
+ if (mTransport != null) {
+ logout();
+ mTransport.close();
+ mTransport = null;
+ }
+ destroyResponses();
+ mParser = null;
+ mImapStore = null;
+ }
+
+ /**
+ * Attempts to convert the connection into secure connection.
+ */
+ private void maybeDoStartTls() throws IOException, MessagingException {
+ // STARTTLS is required in the OMTP standard but not every implementation support it.
+ // Make sure the server does have this capability
+ if (hasCapability(ImapConstants.CAPABILITY_STARTTLS)) {
+ executeSimpleCommand(ImapConstants.STARTTLS);
+ mTransport.reopenTls();
+ createParser();
+ // The cached capabilities should be refreshed after TLS is established.
+ queryCapability();
+ }
+ }
+
+ /**
+ * Logs into the IMAP server
+ */
+ private void doLogin() throws IOException, MessagingException, AuthenticationFailedException {
+ try {
+ if (mCapabilities.contains(ImapConstants.CAPABILITY_AUTH_DIGEST_MD5)) {
+ doDigestMd5Auth();
+ } else {
+ executeSimpleCommand(getLoginPhrase(), true);
+ }
+ } catch (ImapException ie) {
+ LogUtils.d(TAG, "ImapException", ie);
+ String status = ie.getStatus();
+ String statusMessage = ie.getStatusMessage();
+ String alertText = ie.getAlertText();
+
+ if (ImapConstants.NO.equals(status)) {
+ switch (statusMessage) {
+ case ImapConstants.NO_UNKNOWN_USER:
+ mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_AUTH_UNKNOWN_USER);
+ break;
+ case ImapConstants.NO_UNKNOWN_CLIENT:
+ mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_AUTH_UNKNOWN_DEVICE);
+ break;
+ case ImapConstants.NO_INVALID_PASSWORD:
+ mImapStore.getImapHelper()
+ .handleEvent(OmtpEvents.DATA_AUTH_INVALID_PASSWORD);
+ break;
+ case ImapConstants.NO_MAILBOX_NOT_INITIALIZED:
+ mImapStore.getImapHelper()
+ .handleEvent(OmtpEvents.DATA_AUTH_MAILBOX_NOT_INITIALIZED);
+ break;
+ case ImapConstants.NO_SERVICE_IS_NOT_PROVISIONED:
+ mImapStore.getImapHelper()
+ .handleEvent(OmtpEvents.DATA_AUTH_SERVICE_NOT_PROVISIONED);
+ break;
+ case ImapConstants.NO_SERVICE_IS_NOT_ACTIVATED:
+ mImapStore.getImapHelper()
+ .handleEvent(OmtpEvents.DATA_AUTH_SERVICE_NOT_ACTIVATED);
+ break;
+ case ImapConstants.NO_USER_IS_BLOCKED:
+ mImapStore.getImapHelper()
+ .handleEvent(OmtpEvents.DATA_AUTH_USER_IS_BLOCKED);
+ break;
+ case ImapConstants.NO_APPLICATION_ERROR:
+ mImapStore.getImapHelper()
+ .handleEvent(OmtpEvents.DATA_REJECTED_SERVER_RESPONSE);
+ default:
+ mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_BAD_IMAP_CREDENTIAL);
+ }
+ throw new AuthenticationFailedException(alertText, ie);
+ }
+
+ mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_REJECTED_SERVER_RESPONSE);
+ throw new MessagingException(alertText, ie);
+ }
+ }
+
+ private void doDigestMd5Auth() throws IOException, MessagingException {
+
+ // Initiate the authentication.
+ // The server will issue us a challenge, asking to run MD5 on the nonce with our password
+ // and other data, including the cnonce we randomly generated.
+ //
+ // C: a AUTHENTICATE DIGEST-MD5
+ // S: (BASE64) realm="elwood.innosoft.com",nonce="OA6MG9tEQGm2hh",qop="auth",
+ // algorithm=md5-sess,charset=utf-8
+ List<ImapResponse> responses = executeSimpleCommand(
+ ImapConstants.AUTHENTICATE + " " + ImapConstants.AUTH_DIGEST_MD5);
+ String decodedChallenge = decodeBase64(responses.get(0).getStringOrEmpty(0).getString());
+
+ Map<String, String> challenge = DigestMd5Utils.parseDigestMessage(decodedChallenge);
+ DigestMd5Utils.Data data = new DigestMd5Utils.Data(mImapStore, mTransport, challenge);
+
+ String response = data.createResponse();
+ // Respond to the challenge. If the server accepts it, it will reply a response-auth which
+ // is the MD5 of our password and the cnonce we've provided, to prove the server does know
+ // the password.
+ //
+ // C: (BASE64) charset=utf-8,username="chris",realm="elwood.innosoft.com",
+ // nonce="OA6MG9tEQGm2hh",nc=00000001,cnonce="OA6MHXh6VqTrRk",
+ // digest-uri="imap/elwood.innosoft.com",
+ // response=d388dad90d4bbd760a152321f2143af7,qop=auth
+ // S: (BASE64) rspauth=ea40f60335c427b5527b84dbabcdfffd
+
+ responses = executeContinuationResponse(encodeBase64(response), true);
+
+ // Verify response-auth.
+ // If failed verifyResponseAuth() will throw a MessagingException, terminating the
+ // connection
+ String decodedResponseAuth = decodeBase64(responses.get(0).getStringOrEmpty(0).getString());
+ data.verifyResponseAuth(decodedResponseAuth);
+
+ // Send a empty response to indicate we've accepted the response-auth
+ //
+ // C: (empty)
+ // S: a OK User logged in
+ executeContinuationResponse("", false);
+
+ }
+
+ private static String decodeBase64(String string) {
+ return new String(Base64.decode(string, Base64.DEFAULT));
+ }
+
+ private static String encodeBase64(String string) {
+ return Base64.encodeToString(string.getBytes(), Base64.NO_WRAP);
+ }
+
+ private void queryCapability() throws IOException, MessagingException {
+ List<ImapResponse> responses = executeSimpleCommand(ImapConstants.CAPABILITY);
+ mCapabilities.clear();
+ Set<String> disabledCapabilities = mImapStore.getImapHelper().getConfig()
+ .getDisabledCapabilities();
+ for (ImapResponse response : responses) {
+ if (response.isTagged()) {
+ continue;
+ }
+ for (int i = 0; i < response.size(); i++) {
+ String capability = response.getStringOrEmpty(i).getString();
+ if (disabledCapabilities != null) {
+ if (!disabledCapabilities.contains(capability)) {
+ mCapabilities.add(capability);
+ }
+ } else {
+ mCapabilities.add(capability);
+ }
+ }
+ }
+
+ LogUtils.d(TAG, "Capabilities: " + mCapabilities.toString());
+ }
+
+ private boolean hasCapability(String capability) {
+ return mCapabilities.contains(capability);
+ }
+ /**
+ * Create an {@link ImapResponseParser} from {@code mTransport.getInputStream()} and
+ * set it to {@link #mParser}.
+ *
+ * If we already have an {@link ImapResponseParser}, we
+ * {@link #destroyResponses()} and throw it away.
+ */
+ private void createParser() {
+ destroyResponses();
+ mParser = new ImapResponseParser(mTransport.getInputStream());
+ }
+
+
+ public void destroyResponses() {
+ if (mParser != null) {
+ mParser.destroyResponses();
+ }
+ }
+
+ public ImapResponse readResponse() throws IOException, MessagingException {
+ return mParser.readResponse(false);
+ }
+
+ public List<ImapResponse> executeSimpleCommand(String command)
+ throws IOException, MessagingException{
+ return executeSimpleCommand(command, false);
+ }
+
+ /**
+ * Send a single command to the server. The command will be preceded by an IMAP command
+ * tag and followed by \r\n (caller need not supply them).
+ * Execute a simple command at the server, a simple command being one that is sent in a single
+ * line of text
+ *
+ * @param command the command to send to the server
+ * @param sensitive whether the command should be redacted in logs (used for login)
+ * @return a list of ImapResponses
+ * @throws IOException
+ * @throws MessagingException
+ */
+ public List<ImapResponse> executeSimpleCommand(String command, boolean sensitive)
+ throws IOException, MessagingException {
+ // TODO: It may be nice to catch IOExceptions and close the connection here.
+ // Currently, we expect callers to do that, but if they fail to we'll be in a broken state.
+ sendCommand(command, sensitive);
+ return getCommandResponses();
+ }
+
+ public String sendCommand(String command, boolean sensitive)
+ throws IOException, MessagingException {
+ open();
+
+ if (mTransport == null) {
+ throw new IOException("Null transport");
+ }
+ String tag = Integer.toString(mNextCommandTag.incrementAndGet());
+ String commandToSend = tag + " " + command;
+ mTransport.writeLine(commandToSend, (sensitive ? IMAP_REDACTED_LOG : command));
+ return tag;
+ }
+
+ List<ImapResponse> executeContinuationResponse(String response, boolean sensitive)
+ throws IOException, MessagingException {
+ mTransport.writeLine(response, (sensitive ? IMAP_REDACTED_LOG : response));
+ return getCommandResponses();
+ }
+
+ /**
+ * Read and return all of the responses from the most recent command sent to the server
+ *
+ * @return a list of ImapResponses
+ * @throws IOException
+ * @throws MessagingException
+ */
+ List<ImapResponse> getCommandResponses()
+ throws IOException, MessagingException {
+ final List<ImapResponse> responses = new ArrayList<ImapResponse>();
+ ImapResponse response;
+ do {
+ response = mParser.readResponse(false);
+ responses.add(response);
+ } while (!(response.isTagged() || response.isContinuationRequest()));
+
+ if (!(response.isOk() || response.isContinuationRequest())) {
+ final String toString = response.toString();
+ final String status = response.getStatusOrEmpty().getString();
+ final String statusMessage = response.getStatusResponseTextOrEmpty().getString();
+ final String alert = response.getAlertTextOrEmpty().getString();
+ final String responseCode = response.getResponseCodeOrEmpty().getString();
+ destroyResponses();
+ throw new ImapException(toString, status, statusMessage, alert, responseCode);
+ }
+ return responses;
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/store/ImapFolder.java b/java/com/android/voicemailomtp/mail/store/ImapFolder.java
new file mode 100644
index 000000000..eca349876
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/store/ImapFolder.java
@@ -0,0 +1,784 @@
+/*
+ * 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.voicemailomtp.mail.store;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.Base64DataException;
+import com.android.voicemailomtp.OmtpEvents;
+import com.android.voicemailomtp.mail.AuthenticationFailedException;
+import com.android.voicemailomtp.mail.Body;
+import com.android.voicemailomtp.mail.FetchProfile;
+import com.android.voicemailomtp.mail.Flag;
+import com.android.voicemailomtp.mail.Message;
+import com.android.voicemailomtp.mail.MessagingException;
+import com.android.voicemailomtp.mail.Part;
+import com.android.voicemailomtp.mail.internet.BinaryTempFileBody;
+import com.android.voicemailomtp.mail.internet.MimeBodyPart;
+import com.android.voicemailomtp.mail.internet.MimeHeader;
+import com.android.voicemailomtp.mail.internet.MimeMultipart;
+import com.android.voicemailomtp.mail.internet.MimeUtility;
+import com.android.voicemailomtp.mail.store.ImapStore.ImapException;
+import com.android.voicemailomtp.mail.store.ImapStore.ImapMessage;
+import com.android.voicemailomtp.mail.store.imap.ImapConstants;
+import com.android.voicemailomtp.mail.store.imap.ImapElement;
+import com.android.voicemailomtp.mail.store.imap.ImapList;
+import com.android.voicemailomtp.mail.store.imap.ImapResponse;
+import com.android.voicemailomtp.mail.store.imap.ImapString;
+import com.android.voicemailomtp.mail.utils.LogUtils;
+import com.android.voicemailomtp.mail.utils.Utility;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+
+public class ImapFolder {
+ private static final String TAG = "ImapFolder";
+ private final static String[] PERMANENT_FLAGS =
+ { Flag.DELETED, Flag.SEEN, Flag.FLAGGED, Flag.ANSWERED };
+ private static final int COPY_BUFFER_SIZE = 16*1024;
+
+ private final ImapStore mStore;
+ private final String mName;
+ private int mMessageCount = -1;
+ private ImapConnection mConnection;
+ private String mMode;
+ private boolean mExists;
+ /** A set of hashes that can be used to track dirtiness */
+ Object mHash[];
+
+ public static final String MODE_READ_ONLY = "mode_read_only";
+ public static final String MODE_READ_WRITE = "mode_read_write";
+
+ public ImapFolder(ImapStore store, String name) {
+ mStore = store;
+ mName = name;
+ }
+
+ /**
+ * Callback for each message retrieval.
+ */
+ public interface MessageRetrievalListener {
+ public void messageRetrieved(Message message);
+ }
+
+ private void destroyResponses() {
+ if (mConnection != null) {
+ mConnection.destroyResponses();
+ }
+ }
+
+ public void open(String mode) throws MessagingException {
+ try {
+ if (isOpen()) {
+ throw new AssertionError("Duplicated open on ImapFolder");
+ }
+ synchronized (this) {
+ mConnection = mStore.getConnection();
+ }
+ // * FLAGS (\Answered \Flagged \Deleted \Seen \Draft NonJunk
+ // $MDNSent)
+ // * OK [PERMANENTFLAGS (\Answered \Flagged \Deleted \Seen \Draft
+ // NonJunk $MDNSent \*)] Flags permitted.
+ // * 23 EXISTS
+ // * 0 RECENT
+ // * OK [UIDVALIDITY 1125022061] UIDs valid
+ // * OK [UIDNEXT 57576] Predicted next UID
+ // 2 OK [READ-WRITE] Select completed.
+ try {
+ doSelect();
+ } catch (IOException ioe) {
+ throw ioExceptionHandler(mConnection, ioe);
+ } finally {
+ destroyResponses();
+ }
+ } catch (AuthenticationFailedException e) {
+ // Don't cache this connection, so we're forced to try connecting/login again
+ mConnection = null;
+ close(false);
+ throw e;
+ } catch (MessagingException e) {
+ mExists = false;
+ close(false);
+ throw e;
+ }
+ }
+
+ public boolean isOpen() {
+ return mExists && mConnection != null;
+ }
+
+ public String getMode() {
+ return mMode;
+ }
+
+ public void close(boolean expunge) {
+ if (expunge) {
+ try {
+ expunge();
+ } catch (MessagingException e) {
+ LogUtils.e(TAG, e, "Messaging Exception");
+ }
+ }
+ mMessageCount = -1;
+ synchronized (this) {
+ mConnection = null;
+ }
+ }
+
+ public int getMessageCount() {
+ return mMessageCount;
+ }
+
+ String[] getSearchUids(List<ImapResponse> responses) {
+ // S: * SEARCH 2 3 6
+ final ArrayList<String> uids = new ArrayList<String>();
+ for (ImapResponse response : responses) {
+ if (!response.isDataResponse(0, ImapConstants.SEARCH)) {
+ continue;
+ }
+ // Found SEARCH response data
+ for (int i = 1; i < response.size(); i++) {
+ ImapString s = response.getStringOrEmpty(i);
+ if (s.isString()) {
+ uids.add(s.getString());
+ }
+ }
+ }
+ return uids.toArray(Utility.EMPTY_STRINGS);
+ }
+
+ @VisibleForTesting
+ String[] searchForUids(String searchCriteria) throws MessagingException {
+ checkOpen();
+ try {
+ try {
+ final String command = ImapConstants.UID_SEARCH + " " + searchCriteria;
+ final String[] result = getSearchUids(mConnection.executeSimpleCommand(command));
+ LogUtils.d(TAG, "searchForUids '" + searchCriteria + "' results: " +
+ result.length);
+ return result;
+ } catch (ImapException me) {
+ LogUtils.d(TAG, "ImapException in search: " + searchCriteria, me);
+ return Utility.EMPTY_STRINGS; // Not found
+ } catch (IOException ioe) {
+ LogUtils.d(TAG, "IOException in search: " + searchCriteria, ioe);
+ mStore.getImapHelper().handleEvent(OmtpEvents.DATA_GENERIC_IMAP_IOE);
+ throw ioExceptionHandler(mConnection, ioe);
+ }
+ } finally {
+ destroyResponses();
+ }
+ }
+
+ @Nullable
+ public Message getMessage(String uid) throws MessagingException {
+ checkOpen();
+
+ final String[] uids = searchForUids(ImapConstants.UID + " " + uid);
+ for (int i = 0; i < uids.length; i++) {
+ if (uids[i].equals(uid)) {
+ return new ImapMessage(uid, this);
+ }
+ }
+ LogUtils.e(TAG, "UID " + uid + " not found on server");
+ return null;
+ }
+
+ @VisibleForTesting
+ protected static boolean isAsciiString(String str) {
+ int len = str.length();
+ for (int i = 0; i < len; i++) {
+ char c = str.charAt(i);
+ if (c >= 128) return false;
+ }
+ return true;
+ }
+
+ public Message[] getMessages(String[] uids) throws MessagingException {
+ if (uids == null) {
+ uids = searchForUids("1:* NOT DELETED");
+ }
+ return getMessagesInternal(uids);
+ }
+
+ public Message[] getMessagesInternal(String[] uids) {
+ final ArrayList<Message> messages = new ArrayList<Message>(uids.length);
+ for (int i = 0; i < uids.length; i++) {
+ final String uid = uids[i];
+ final ImapMessage message = new ImapMessage(uid, this);
+ messages.add(message);
+ }
+ return messages.toArray(Message.EMPTY_ARRAY);
+ }
+
+ public void fetch(Message[] messages, FetchProfile fp,
+ MessageRetrievalListener listener) throws MessagingException {
+ try {
+ fetchInternal(messages, fp, listener);
+ } catch (RuntimeException e) { // Probably a parser error.
+ LogUtils.w(TAG, "Exception detected: " + e.getMessage());
+ throw e;
+ }
+ }
+
+ public void fetchInternal(Message[] messages, FetchProfile fp,
+ MessageRetrievalListener listener) throws MessagingException {
+ if (messages.length == 0) {
+ return;
+ }
+ checkOpen();
+ HashMap<String, Message> messageMap = new HashMap<String, Message>();
+ for (Message m : messages) {
+ messageMap.put(m.getUid(), m);
+ }
+
+ /*
+ * Figure out what command we are going to run:
+ * FLAGS - UID FETCH (FLAGS)
+ * ENVELOPE - UID FETCH (INTERNALDATE UID RFC822.SIZE FLAGS BODY.PEEK[
+ * HEADER.FIELDS (date subject from content-type to cc)])
+ * STRUCTURE - UID FETCH (BODYSTRUCTURE)
+ * BODY_SANE - UID FETCH (BODY.PEEK[]<0.N>) where N = max bytes returned
+ * BODY - UID FETCH (BODY.PEEK[])
+ * Part - UID FETCH (BODY.PEEK[ID]) where ID = mime part ID
+ */
+
+ final LinkedHashSet<String> fetchFields = new LinkedHashSet<String>();
+
+ fetchFields.add(ImapConstants.UID);
+ if (fp.contains(FetchProfile.Item.FLAGS)) {
+ fetchFields.add(ImapConstants.FLAGS);
+ }
+ if (fp.contains(FetchProfile.Item.ENVELOPE)) {
+ fetchFields.add(ImapConstants.INTERNALDATE);
+ fetchFields.add(ImapConstants.RFC822_SIZE);
+ fetchFields.add(ImapConstants.FETCH_FIELD_HEADERS);
+ }
+ if (fp.contains(FetchProfile.Item.STRUCTURE)) {
+ fetchFields.add(ImapConstants.BODYSTRUCTURE);
+ }
+
+ if (fp.contains(FetchProfile.Item.BODY_SANE)) {
+ fetchFields.add(ImapConstants.FETCH_FIELD_BODY_PEEK_SANE);
+ }
+ if (fp.contains(FetchProfile.Item.BODY)) {
+ fetchFields.add(ImapConstants.FETCH_FIELD_BODY_PEEK);
+ }
+
+ // TODO Why are we only fetching the first part given?
+ final Part fetchPart = fp.getFirstPart();
+ if (fetchPart != null) {
+ final String[] partIds =
+ fetchPart.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA);
+ // TODO Why can a single part have more than one Id? And why should we only fetch
+ // the first id if there are more than one?
+ if (partIds != null) {
+ fetchFields.add(ImapConstants.FETCH_FIELD_BODY_PEEK_BARE
+ + "[" + partIds[0] + "]");
+ }
+ }
+
+ try {
+ mConnection.sendCommand(String.format(Locale.US,
+ ImapConstants.UID_FETCH + " %s (%s)", ImapStore.joinMessageUids(messages),
+ Utility.combine(fetchFields.toArray(new String[fetchFields.size()]), ' ')
+ ), false);
+ ImapResponse response;
+ do {
+ response = null;
+ try {
+ response = mConnection.readResponse();
+
+ if (!response.isDataResponse(1, ImapConstants.FETCH)) {
+ continue; // Ignore
+ }
+ final ImapList fetchList = response.getListOrEmpty(2);
+ final String uid = fetchList.getKeyedStringOrEmpty(ImapConstants.UID)
+ .getString();
+ if (TextUtils.isEmpty(uid)) continue;
+
+ ImapMessage message = (ImapMessage) messageMap.get(uid);
+ if (message == null) continue;
+
+ if (fp.contains(FetchProfile.Item.FLAGS)) {
+ final ImapList flags =
+ fetchList.getKeyedListOrEmpty(ImapConstants.FLAGS);
+ for (int i = 0, count = flags.size(); i < count; i++) {
+ final ImapString flag = flags.getStringOrEmpty(i);
+ if (flag.is(ImapConstants.FLAG_DELETED)) {
+ message.setFlagInternal(Flag.DELETED, true);
+ } else if (flag.is(ImapConstants.FLAG_ANSWERED)) {
+ message.setFlagInternal(Flag.ANSWERED, true);
+ } else if (flag.is(ImapConstants.FLAG_SEEN)) {
+ message.setFlagInternal(Flag.SEEN, true);
+ } else if (flag.is(ImapConstants.FLAG_FLAGGED)) {
+ message.setFlagInternal(Flag.FLAGGED, true);
+ }
+ }
+ }
+ if (fp.contains(FetchProfile.Item.ENVELOPE)) {
+ final Date internalDate = fetchList.getKeyedStringOrEmpty(
+ ImapConstants.INTERNALDATE).getDateOrNull();
+ final int size = fetchList.getKeyedStringOrEmpty(
+ ImapConstants.RFC822_SIZE).getNumberOrZero();
+ final String header = fetchList.getKeyedStringOrEmpty(
+ ImapConstants.BODY_BRACKET_HEADER, true).getString();
+
+ message.setInternalDate(internalDate);
+ message.setSize(size);
+ message.parse(Utility.streamFromAsciiString(header));
+ }
+ if (fp.contains(FetchProfile.Item.STRUCTURE)) {
+ ImapList bs = fetchList.getKeyedListOrEmpty(
+ ImapConstants.BODYSTRUCTURE);
+ if (!bs.isEmpty()) {
+ try {
+ parseBodyStructure(bs, message, ImapConstants.TEXT);
+ } catch (MessagingException e) {
+ LogUtils.v(TAG, e, "Error handling message");
+ message.setBody(null);
+ }
+ }
+ }
+ if (fp.contains(FetchProfile.Item.BODY)
+ || fp.contains(FetchProfile.Item.BODY_SANE)) {
+ // Body is keyed by "BODY[]...".
+ // Previously used "BODY[..." but this can be confused with "BODY[HEADER..."
+ // TODO Should we accept "RFC822" as well??
+ ImapString body = fetchList.getKeyedStringOrEmpty("BODY[]", true);
+ InputStream bodyStream = body.getAsStream();
+ message.parse(bodyStream);
+ }
+ if (fetchPart != null) {
+ InputStream bodyStream =
+ fetchList.getKeyedStringOrEmpty("BODY[", true).getAsStream();
+ String encodings[] = fetchPart.getHeader(
+ MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING);
+
+ String contentTransferEncoding = null;
+ if (encodings != null && encodings.length > 0) {
+ contentTransferEncoding = encodings[0];
+ } else {
+ // According to http://tools.ietf.org/html/rfc2045#section-6.1
+ // "7bit" is the default.
+ contentTransferEncoding = "7bit";
+ }
+
+ try {
+ // TODO Don't create 2 temp files.
+ // decodeBody creates BinaryTempFileBody, but we could avoid this
+ // if we implement ImapStringBody.
+ // (We'll need to share a temp file. Protect it with a ref-count.)
+ message.setBody(decodeBody(mStore.getContext(), bodyStream,
+ contentTransferEncoding, fetchPart.getSize(), listener));
+ } catch(Exception e) {
+ // TODO: Figure out what kinds of exceptions might actually be thrown
+ // from here. This blanket catch-all is because we're not sure what to
+ // do if we don't have a contentTransferEncoding, and we don't have
+ // time to figure out what exceptions might be thrown.
+ LogUtils.e(TAG, "Error fetching body %s", e);
+ }
+ }
+
+ if (listener != null) {
+ listener.messageRetrieved(message);
+ }
+ } finally {
+ destroyResponses();
+ }
+ } while (!response.isTagged());
+ } catch (IOException ioe) {
+ mStore.getImapHelper().handleEvent(OmtpEvents.DATA_GENERIC_IMAP_IOE);
+ throw ioExceptionHandler(mConnection, ioe);
+ }
+ }
+
+ /**
+ * Removes any content transfer encoding from the stream and returns a Body.
+ * This code is taken/condensed from MimeUtility.decodeBody
+ */
+ private static Body decodeBody(Context context,InputStream in, String contentTransferEncoding,
+ int size, MessageRetrievalListener listener) throws IOException {
+ // Get a properly wrapped input stream
+ in = MimeUtility.getInputStreamForContentTransferEncoding(in, contentTransferEncoding);
+ BinaryTempFileBody tempBody = new BinaryTempFileBody();
+ OutputStream out = tempBody.getOutputStream();
+ try {
+ byte[] buffer = new byte[COPY_BUFFER_SIZE];
+ int n = 0;
+ int count = 0;
+ while (-1 != (n = in.read(buffer))) {
+ out.write(buffer, 0, n);
+ count += n;
+ }
+ } catch (Base64DataException bde) {
+ String warning = "\n\nThere was an error while decoding the message.";
+ out.write(warning.getBytes());
+ } finally {
+ out.close();
+ }
+ return tempBody;
+ }
+
+ public String[] getPermanentFlags() {
+ return PERMANENT_FLAGS;
+ }
+
+ /**
+ * Handle any untagged responses that the caller doesn't care to handle themselves.
+ * @param responses
+ */
+ private void handleUntaggedResponses(List<ImapResponse> responses) {
+ for (ImapResponse response : responses) {
+ handleUntaggedResponse(response);
+ }
+ }
+
+ /**
+ * Handle an untagged response that the caller doesn't care to handle themselves.
+ * @param response
+ */
+ private void handleUntaggedResponse(ImapResponse response) {
+ if (response.isDataResponse(1, ImapConstants.EXISTS)) {
+ mMessageCount = response.getStringOrEmpty(0).getNumberOrZero();
+ }
+ }
+
+ private static void parseBodyStructure(ImapList bs, Part part, String id)
+ throws MessagingException {
+ if (bs.getElementOrNone(0).isList()) {
+ /*
+ * This is a multipart/*
+ */
+ MimeMultipart mp = new MimeMultipart();
+ for (int i = 0, count = bs.size(); i < count; i++) {
+ ImapElement e = bs.getElementOrNone(i);
+ if (e.isList()) {
+ /*
+ * For each part in the message we're going to add a new BodyPart and parse
+ * into it.
+ */
+ MimeBodyPart bp = new MimeBodyPart();
+ if (id.equals(ImapConstants.TEXT)) {
+ parseBodyStructure(bs.getListOrEmpty(i), bp, Integer.toString(i + 1));
+
+ } else {
+ parseBodyStructure(bs.getListOrEmpty(i), bp, id + "." + (i + 1));
+ }
+ mp.addBodyPart(bp);
+
+ } else {
+ if (e.isString()) {
+ mp.setSubType(bs.getStringOrEmpty(i).getString().toLowerCase(Locale.US));
+ }
+ break; // Ignore the rest of the list.
+ }
+ }
+ part.setBody(mp);
+ } else {
+ /*
+ * This is a body. We need to add as much information as we can find out about
+ * it to the Part.
+ */
+
+ /*
+ body type
+ body subtype
+ body parameter parenthesized list
+ body id
+ body description
+ body encoding
+ body size
+ */
+
+ final ImapString type = bs.getStringOrEmpty(0);
+ final ImapString subType = bs.getStringOrEmpty(1);
+ final String mimeType =
+ (type.getString() + "/" + subType.getString()).toLowerCase(Locale.US);
+
+ final ImapList bodyParams = bs.getListOrEmpty(2);
+ final ImapString cid = bs.getStringOrEmpty(3);
+ final ImapString encoding = bs.getStringOrEmpty(5);
+ final int size = bs.getStringOrEmpty(6).getNumberOrZero();
+
+ if (MimeUtility.mimeTypeMatches(mimeType, MimeUtility.MIME_TYPE_RFC822)) {
+ // A body type of type MESSAGE and subtype RFC822
+ // contains, immediately after the basic fields, the
+ // envelope structure, body structure, and size in
+ // text lines of the encapsulated message.
+ // [MESSAGE, RFC822, [NAME, filename.eml], NIL, NIL, 7BIT, 5974, NIL,
+ // [INLINE, [FILENAME*0, Fwd: Xxx..., FILENAME*1, filename.eml]], NIL]
+ /*
+ * This will be caught by fetch and handled appropriately.
+ */
+ throw new MessagingException("BODYSTRUCTURE " + MimeUtility.MIME_TYPE_RFC822
+ + " not yet supported.");
+ }
+
+ /*
+ * Set the content type with as much information as we know right now.
+ */
+ final StringBuilder contentType = new StringBuilder(mimeType);
+
+ /*
+ * If there are body params we might be able to get some more information out
+ * of them.
+ */
+ for (int i = 1, count = bodyParams.size(); i < count; i += 2) {
+
+ // TODO We need to convert " into %22, but
+ // because MimeUtility.getHeaderParameter doesn't recognize it,
+ // we can't fix it for now.
+ contentType.append(String.format(";\n %s=\"%s\"",
+ bodyParams.getStringOrEmpty(i - 1).getString(),
+ bodyParams.getStringOrEmpty(i).getString()));
+ }
+
+ part.setHeader(MimeHeader.HEADER_CONTENT_TYPE, contentType.toString());
+
+ // Extension items
+ final ImapList bodyDisposition;
+
+ if (type.is(ImapConstants.TEXT) && bs.getElementOrNone(9).isList()) {
+ // If media-type is TEXT, 9th element might be: [body-fld-lines] := number
+ // So, if it's not a list, use 10th element.
+ // (Couldn't find evidence in the RFC if it's ALWAYS 10th element.)
+ bodyDisposition = bs.getListOrEmpty(9);
+ } else {
+ bodyDisposition = bs.getListOrEmpty(8);
+ }
+
+ final StringBuilder contentDisposition = new StringBuilder();
+
+ if (bodyDisposition.size() > 0) {
+ final String bodyDisposition0Str =
+ bodyDisposition.getStringOrEmpty(0).getString().toLowerCase(Locale.US);
+ if (!TextUtils.isEmpty(bodyDisposition0Str)) {
+ contentDisposition.append(bodyDisposition0Str);
+ }
+
+ final ImapList bodyDispositionParams = bodyDisposition.getListOrEmpty(1);
+ if (!bodyDispositionParams.isEmpty()) {
+ /*
+ * If there is body disposition information we can pull some more
+ * information about the attachment out.
+ */
+ for (int i = 1, count = bodyDispositionParams.size(); i < count; i += 2) {
+
+ // TODO We need to convert " into %22. See above.
+ contentDisposition.append(String.format(Locale.US, ";\n %s=\"%s\"",
+ bodyDispositionParams.getStringOrEmpty(i - 1)
+ .getString().toLowerCase(Locale.US),
+ bodyDispositionParams.getStringOrEmpty(i).getString()));
+ }
+ }
+ }
+
+ if ((size > 0)
+ && (MimeUtility.getHeaderParameter(contentDisposition.toString(), "size")
+ == null)) {
+ contentDisposition.append(String.format(Locale.US, ";\n size=%d", size));
+ }
+
+ if (contentDisposition.length() > 0) {
+ /*
+ * Set the content disposition containing at least the size. Attachment
+ * handling code will use this down the road.
+ */
+ part.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION,
+ contentDisposition.toString());
+ }
+
+ /*
+ * Set the Content-Transfer-Encoding header. Attachment code will use this
+ * to parse the body.
+ */
+ if (!encoding.isEmpty()) {
+ part.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING,
+ encoding.getString());
+ }
+
+ /*
+ * Set the Content-ID header.
+ */
+ if (!cid.isEmpty()) {
+ part.setHeader(MimeHeader.HEADER_CONTENT_ID, cid.getString());
+ }
+
+ if (size > 0) {
+ if (part instanceof ImapMessage) {
+ ((ImapMessage) part).setSize(size);
+ } else if (part instanceof MimeBodyPart) {
+ ((MimeBodyPart) part).setSize(size);
+ } else {
+ throw new MessagingException("Unknown part type " + part.toString());
+ }
+ }
+ part.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, id);
+ }
+
+ }
+
+ public Message[] expunge() throws MessagingException {
+ checkOpen();
+ try {
+ handleUntaggedResponses(mConnection.executeSimpleCommand(ImapConstants.EXPUNGE));
+ } catch (IOException ioe) {
+ mStore.getImapHelper().handleEvent(OmtpEvents.DATA_GENERIC_IMAP_IOE);
+ throw ioExceptionHandler(mConnection, ioe);
+ } finally {
+ destroyResponses();
+ }
+ return null;
+ }
+
+ public void setFlags(Message[] messages, String[] flags, boolean value)
+ throws MessagingException {
+ checkOpen();
+
+ String allFlags = "";
+ if (flags.length > 0) {
+ StringBuilder flagList = new StringBuilder();
+ for (int i = 0, count = flags.length; i < count; i++) {
+ String flag = flags[i];
+ if (flag == Flag.SEEN) {
+ flagList.append(" " + ImapConstants.FLAG_SEEN);
+ } else if (flag == Flag.DELETED) {
+ flagList.append(" " + ImapConstants.FLAG_DELETED);
+ } else if (flag == Flag.FLAGGED) {
+ flagList.append(" " + ImapConstants.FLAG_FLAGGED);
+ } else if (flag == Flag.ANSWERED) {
+ flagList.append(" " + ImapConstants.FLAG_ANSWERED);
+ }
+ }
+ allFlags = flagList.substring(1);
+ }
+ try {
+ mConnection.executeSimpleCommand(String.format(Locale.US,
+ ImapConstants.UID_STORE + " %s %s" + ImapConstants.FLAGS_SILENT + " (%s)",
+ ImapStore.joinMessageUids(messages),
+ value ? "+" : "-",
+ allFlags));
+
+ } catch (IOException ioe) {
+ mStore.getImapHelper().handleEvent(OmtpEvents.DATA_GENERIC_IMAP_IOE);
+ throw ioExceptionHandler(mConnection, ioe);
+ } finally {
+ destroyResponses();
+ }
+ }
+
+ /**
+ * Selects the folder for use. Before performing any operations on this folder, it
+ * must be selected.
+ */
+ private void doSelect() throws IOException, MessagingException {
+ final List<ImapResponse> responses = mConnection.executeSimpleCommand(
+ String.format(Locale.US, ImapConstants.SELECT + " \"%s\"", mName));
+
+ // Assume the folder is opened read-write; unless we are notified otherwise
+ mMode = MODE_READ_WRITE;
+ int messageCount = -1;
+ for (ImapResponse response : responses) {
+ if (response.isDataResponse(1, ImapConstants.EXISTS)) {
+ messageCount = response.getStringOrEmpty(0).getNumberOrZero();
+ } else if (response.isOk()) {
+ final ImapString responseCode = response.getResponseCodeOrEmpty();
+ if (responseCode.is(ImapConstants.READ_ONLY)) {
+ mMode = MODE_READ_ONLY;
+ } else if (responseCode.is(ImapConstants.READ_WRITE)) {
+ mMode = MODE_READ_WRITE;
+ }
+ } else if (response.isTagged()) { // Not OK
+ mStore.getImapHelper().handleEvent(OmtpEvents.DATA_MAILBOX_OPEN_FAILED);
+ throw new MessagingException("Can't open mailbox: "
+ + response.getStatusResponseTextOrEmpty());
+ }
+ }
+ if (messageCount == -1) {
+ throw new MessagingException("Did not find message count during select");
+ }
+ mMessageCount = messageCount;
+ mExists = true;
+ }
+
+ public class Quota {
+
+ public final int occupied;
+ public final int total;
+
+ public Quota(int occupied, int total) {
+ this.occupied = occupied;
+ this.total = total;
+ }
+ }
+
+ public Quota getQuota() throws MessagingException {
+ try {
+ final List<ImapResponse> responses = mConnection.executeSimpleCommand(
+ String.format(Locale.US, ImapConstants.GETQUOTAROOT + " \"%s\"", mName));
+
+ for (ImapResponse response : responses) {
+ if (!response.isDataResponse(0, ImapConstants.QUOTA)) {
+ continue;
+ }
+ ImapList list = response.getListOrEmpty(2);
+ for (int i = 0; i < list.size(); i += 3) {
+ if (!list.getStringOrEmpty(i).is("voice")) {
+ continue;
+ }
+ return new Quota(
+ list.getStringOrEmpty(i + 1).getNumber(-1),
+ list.getStringOrEmpty(i + 2).getNumber(-1));
+ }
+ }
+ } catch (IOException ioe) {
+ mStore.getImapHelper().handleEvent(OmtpEvents.DATA_GENERIC_IMAP_IOE);
+ throw ioExceptionHandler(mConnection, ioe);
+ } finally {
+ destroyResponses();
+ }
+ return null;
+ }
+
+ private void checkOpen() throws MessagingException {
+ if (!isOpen()) {
+ throw new MessagingException("Folder " + mName + " is not open.");
+ }
+ }
+
+ private MessagingException ioExceptionHandler(ImapConnection connection, IOException ioe) {
+ LogUtils.d(TAG, "IO Exception detected: ", ioe);
+ connection.close();
+ if (connection == mConnection) {
+ mConnection = null; // To prevent close() from returning the connection to the pool.
+ close(false);
+ }
+ return new MessagingException(MessagingException.IOERROR, "IO Error", ioe);
+ }
+
+ public Message createMessage(String uid) {
+ return new ImapMessage(uid, this);
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/ImapStore.java b/java/com/android/voicemailomtp/mail/store/ImapStore.java
new file mode 100644
index 000000000..f3e0c098e
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/store/ImapStore.java
@@ -0,0 +1,176 @@
+/*
+ * 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.voicemailomtp.mail.store;
+
+import android.content.Context;
+import android.net.Network;
+
+import com.android.voicemailomtp.mail.MailTransport;
+import com.android.voicemailomtp.mail.Message;
+import com.android.voicemailomtp.mail.MessagingException;
+import com.android.voicemailomtp.mail.internet.MimeMessage;
+import com.android.voicemailomtp.imap.ImapHelper;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ImapStore {
+ /**
+ * A global suggestion to Store implementors on how much of the body
+ * should be returned on FetchProfile.Item.BODY_SANE requests. We'll use 125k now.
+ */
+ public static final int FETCH_BODY_SANE_SUGGESTED_SIZE = (125 * 1024);
+ private final Context mContext;
+ private final ImapHelper mHelper;
+ private final String mUsername;
+ private final String mPassword;
+ private final MailTransport mTransport;
+ private ImapConnection mConnection;
+
+ public static final int FLAG_NONE = 0x00; // No flags
+ public static final int FLAG_SSL = 0x01; // Use SSL
+ public static final int FLAG_TLS = 0x02; // Use TLS
+ public static final int FLAG_AUTHENTICATE = 0x04; // Use name/password for authentication
+ public static final int FLAG_TRUST_ALL = 0x08; // Trust all certificates
+ public static final int FLAG_OAUTH = 0x10; // Use OAuth for authentication
+
+ /**
+ * Contains all the information necessary to log into an imap server
+ */
+ public ImapStore(Context context, ImapHelper helper, String username, String password, int port,
+ String serverName, int flags, Network network) {
+ mContext = context;
+ mHelper = helper;
+ mUsername = username;
+ mPassword = password;
+ mTransport = new MailTransport(context, this.getImapHelper(),
+ network, serverName, port, flags);
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public ImapHelper getImapHelper() {
+ return mHelper;
+ }
+
+ public String getUsername() {
+ return mUsername;
+ }
+
+ public String getPassword() {
+ return mPassword;
+ }
+
+ /** Returns a clone of the transport associated with this store. */
+ MailTransport cloneTransport() {
+ return mTransport.clone();
+ }
+
+ /**
+ * Returns UIDs of Messages joined with "," as the separator.
+ */
+ static String joinMessageUids(Message[] messages) {
+ StringBuilder sb = new StringBuilder();
+ boolean notFirst = false;
+ for (Message m : messages) {
+ if (notFirst) {
+ sb.append(',');
+ }
+ sb.append(m.getUid());
+ notFirst = true;
+ }
+ return sb.toString();
+ }
+
+ static class ImapMessage extends MimeMessage {
+ private ImapFolder mFolder;
+
+ ImapMessage(String uid, ImapFolder folder) {
+ mUid = uid;
+ mFolder = folder;
+ }
+
+ public void setSize(int size) {
+ mSize = size;
+ }
+
+ @Override
+ public void parse(InputStream in) throws IOException, MessagingException {
+ super.parse(in);
+ }
+
+ public void setFlagInternal(String flag, boolean set) throws MessagingException {
+ super.setFlag(flag, set);
+ }
+
+ @Override
+ public void setFlag(String flag, boolean set) throws MessagingException {
+ super.setFlag(flag, set);
+ mFolder.setFlags(new Message[] { this }, new String[] { flag }, set);
+ }
+ }
+
+ static class ImapException extends MessagingException {
+ private static final long serialVersionUID = 1L;
+
+ private final String mStatus;
+ private final String mStatusMessage;
+ private final String mAlertText;
+ private final String mResponseCode;
+
+ public ImapException(String message, String status, String statusMessage, String alertText,
+ String responseCode) {
+ super(message);
+ mStatus = status;
+ mStatusMessage = statusMessage;
+ mAlertText = alertText;
+ mResponseCode = responseCode;
+ }
+
+ public String getStatus() {
+ return mStatus;
+ }
+
+ public String getStatusMessage() {
+ return mStatusMessage;
+ }
+
+ public String getAlertText() {
+ return mAlertText;
+ }
+
+ public String getResponseCode() {
+ return mResponseCode;
+ }
+ }
+
+ public void closeConnection() {
+ if (mConnection != null) {
+ mConnection.close();
+ mConnection = null;
+ }
+ }
+
+ public ImapConnection getConnection() {
+ if (mConnection == null) {
+ mConnection = new ImapConnection(this);
+ }
+ return mConnection;
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/DigestMd5Utils.java b/java/com/android/voicemailomtp/mail/store/imap/DigestMd5Utils.java
new file mode 100644
index 000000000..b78f55293
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/store/imap/DigestMd5Utils.java
@@ -0,0 +1,335 @@
+/*
+ * 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.voicemailomtp.mail.store.imap;
+
+import android.annotation.TargetApi;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.util.ArrayMap;
+import android.util.Base64;
+import com.android.voicemailomtp.VvmLog;
+import com.android.voicemailomtp.mail.MailTransport;
+import com.android.voicemailomtp.mail.MessagingException;
+import com.android.voicemailomtp.mail.store.ImapStore;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Map;
+
+@SuppressWarnings("AndroidApiChecker") // Map.getOrDefault() is java8
+@TargetApi(VERSION_CODES.CUR_DEVELOPMENT)
+public class DigestMd5Utils {
+
+ private static final String TAG = "DigestMd5Utils";
+
+ private static final String DIGEST_CHARSET = "CHARSET";
+ private static final String DIGEST_USERNAME = "username";
+ private static final String DIGEST_REALM = "realm";
+ private static final String DIGEST_NONCE = "nonce";
+ private static final String DIGEST_NC = "nc";
+ private static final String DIGEST_CNONCE = "cnonce";
+ private static final String DIGEST_URI = "digest-uri";
+ private static final String DIGEST_RESPONSE = "response";
+ private static final String DIGEST_QOP = "qop";
+
+ private static final String RESPONSE_AUTH_HEADER = "rspauth=";
+ private static final String HEX_CHARS = "0123456789abcdef";
+
+ /**
+ * Represents the set of data we need to generate the DIGEST-MD5 response.
+ */
+ public static class Data {
+
+ private static final String CHARSET = "utf-8";
+
+ public String username;
+ public String password;
+ public String realm;
+ public String nonce;
+ public String nc;
+ public String cnonce;
+ public String digestUri;
+ public String qop;
+
+ @VisibleForTesting
+ Data() {
+ // Do nothing
+ }
+
+ public Data(ImapStore imapStore, MailTransport transport, Map<String, String> challenge) {
+ username = imapStore.getUsername();
+ password = imapStore.getPassword();
+ realm = challenge.getOrDefault(DIGEST_REALM, "");
+ nonce = challenge.get(DIGEST_NONCE);
+ cnonce = createCnonce();
+ nc = "00000001"; // Subsequent Authentication not supported, nounce count always 1.
+ qop = "auth"; // Other config not supported
+ digestUri = "imap/" + transport.getHost();
+ }
+
+ private static String createCnonce() {
+ SecureRandom generator = new SecureRandom();
+
+ // At least 64 bits of entropy is required
+ byte[] rawBytes = new byte[8];
+ generator.nextBytes(rawBytes);
+
+ return Base64.encodeToString(rawBytes, Base64.NO_WRAP);
+ }
+
+ /**
+ * Verify the response-auth returned by the server is correct.
+ */
+ public void verifyResponseAuth(String response)
+ throws MessagingException {
+ if (!response.startsWith(RESPONSE_AUTH_HEADER)) {
+ throw new MessagingException("response-auth expected");
+ }
+ if (!response.substring(RESPONSE_AUTH_HEADER.length())
+ .equals(DigestMd5Utils.getResponse(this, true))) {
+ throw new MessagingException("invalid response-auth return from the server.");
+ }
+ }
+
+ public String createResponse() {
+ String response = getResponse(this, false);
+ ResponseBuilder builder = new ResponseBuilder();
+ builder
+ .append(DIGEST_CHARSET, CHARSET)
+ .appendQuoted(DIGEST_USERNAME, username)
+ .appendQuoted(DIGEST_REALM, realm)
+ .appendQuoted(DIGEST_NONCE, nonce)
+ .append(DIGEST_NC, nc)
+ .appendQuoted(DIGEST_CNONCE, cnonce)
+ .appendQuoted(DIGEST_URI, digestUri)
+ .append(DIGEST_RESPONSE, response)
+ .append(DIGEST_QOP, qop);
+ return builder.toString();
+ }
+
+ private static class ResponseBuilder {
+
+ private StringBuilder mBuilder = new StringBuilder();
+
+ public ResponseBuilder appendQuoted(String key, String value) {
+ if (mBuilder.length() != 0) {
+ mBuilder.append(",");
+ }
+ mBuilder.append(key).append("=\"").append(value).append("\"");
+ return this;
+ }
+
+ public ResponseBuilder append(String key, String value) {
+ if (mBuilder.length() != 0) {
+ mBuilder.append(",");
+ }
+ mBuilder.append(key).append("=").append(value);
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return mBuilder.toString();
+ }
+ }
+ }
+
+ /*
+ response-value =
+ toHex( getKeyDigest ( toHex(getMd5(a1)),
+ { nonce-value, ":" nc-value, ":",
+ cnonce-value, ":", qop-value, ":", toHex(getMd5(a2)) }))
+ * @param isResponseAuth is the response the one the server is returning us. response-auth has
+ * different a2 format.
+ */
+ @VisibleForTesting
+ static String getResponse(Data data, boolean isResponseAuth) {
+ StringBuilder a1 = new StringBuilder();
+ a1.append(new String(
+ getMd5(data.username + ":" + data.realm + ":" + data.password),
+ StandardCharsets.ISO_8859_1));
+ a1.append(":").append(data.nonce).append(":").append(data.cnonce);
+
+ StringBuilder a2 = new StringBuilder();
+ if (!isResponseAuth) {
+ a2.append("AUTHENTICATE");
+ }
+ a2.append(":").append(data.digestUri);
+
+ return toHex(getKeyDigest(
+ toHex(getMd5(a1.toString())),
+ data.nonce + ":" + data.nc + ":" + data.cnonce + ":" + data.qop + ":" + toHex(
+ getMd5(a2.toString()))
+ ));
+ }
+
+ /**
+ * Let getMd5(s) be the 16 octet MD5 hash [RFC 1321] of the octet string s.
+ */
+ private static byte[] getMd5(String s) {
+ try {
+ MessageDigest digester = MessageDigest.getInstance("MD5");
+ digester.update(s.getBytes(StandardCharsets.ISO_8859_1));
+ return digester.digest();
+ } catch (NoSuchAlgorithmException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ /**
+ * Let getKeyDigest(k, s) be getMd5({k, ":", s}), i.e., the 16 octet hash of the string k, a colon and the
+ * string s.
+ */
+ private static byte[] getKeyDigest(String k, String s) {
+ StringBuilder builder = new StringBuilder(k).append(":").append(s);
+ return getMd5(builder.toString());
+ }
+
+ /**
+ * Let toHex(n) be the representation of the 16 octet MD5 hash n as a string of 32 hex digits
+ * (with alphabetic characters always in lower case, since MD5 is case sensitive).
+ */
+ private static String toHex(byte[] n) {
+ StringBuilder result = new StringBuilder();
+ for (byte b : n) {
+ int unsignedByte = b & 0xFF;
+ result.append(HEX_CHARS.charAt(unsignedByte / 16))
+ .append(HEX_CHARS.charAt(unsignedByte % 16));
+ }
+ return result.toString();
+ }
+
+ public static Map<String, String> parseDigestMessage(String message) throws MessagingException {
+ Map<String, String> result = new DigestMessageParser(message).parse();
+ if (!result.containsKey(DIGEST_NONCE)) {
+ throw new MessagingException("nonce missing from server DIGEST-MD5 challenge");
+ }
+ return result;
+ }
+
+ /**
+ * Parse the key-value pair returned by the server.
+ */
+ private static class DigestMessageParser {
+
+ private final String mMessage;
+ private int mPosition = 0;
+ private Map<String, String> mResult = new ArrayMap<>();
+
+ public DigestMessageParser(String message) {
+ mMessage = message;
+ }
+
+ @Nullable
+ public Map<String, String> parse() {
+ try {
+ while (mPosition < mMessage.length()) {
+ parsePair();
+ if (mPosition != mMessage.length()) {
+ expect(',');
+ }
+ }
+ } catch (IndexOutOfBoundsException e) {
+ VvmLog.e(TAG, e.toString());
+ return null;
+ }
+ return mResult;
+ }
+
+ private void parsePair() {
+ String key = parseKey();
+ expect('=');
+ String value = parseValue();
+ mResult.put(key, value);
+ }
+
+ private void expect(char c) {
+ if (pop() != c) {
+ throw new IllegalStateException(
+ "unexpected character " + mMessage.charAt(mPosition));
+ }
+ }
+
+ private char pop() {
+ char result = peek();
+ mPosition++;
+ return result;
+ }
+
+ private char peek() {
+ return mMessage.charAt(mPosition);
+ }
+
+ private void goToNext(char c) {
+ while (peek() != c) {
+ mPosition++;
+ }
+ }
+
+ private String parseKey() {
+ int start = mPosition;
+ goToNext('=');
+ return mMessage.substring(start, mPosition);
+ }
+
+ private String parseValue() {
+ if (peek() == '"') {
+ return parseQuotedValue();
+ } else {
+ return parseUnquotedValue();
+ }
+ }
+
+ private String parseQuotedValue() {
+ expect('"');
+ StringBuilder result = new StringBuilder();
+ while (true) {
+ char c = pop();
+ if (c == '\\') {
+ result.append(pop());
+ } else if (c == '"') {
+ break;
+ } else {
+ result.append(c);
+ }
+ }
+ return result.toString();
+ }
+
+ private String parseUnquotedValue() {
+ StringBuilder result = new StringBuilder();
+ while (true) {
+ char c = pop();
+ if (c == '\\') {
+ result.append(pop());
+ } else if (c == ',') {
+ mPosition--;
+ break;
+ } else {
+ result.append(c);
+ }
+
+ if (mPosition == mMessage.length()) {
+ break;
+ }
+ }
+ return result.toString();
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapConstants.java b/java/com/android/voicemailomtp/mail/store/imap/ImapConstants.java
new file mode 100644
index 000000000..d8e75752f
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/store/imap/ImapConstants.java
@@ -0,0 +1,144 @@
+/*
+ * 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.voicemailomtp.mail.store.imap;
+
+import com.android.voicemailomtp.mail.store.ImapStore;
+
+import java.util.Locale;
+
+public final class ImapConstants {
+ private ImapConstants() {}
+
+ public static final String FETCH_FIELD_BODY_PEEK_BARE = "BODY.PEEK";
+ public static final String FETCH_FIELD_BODY_PEEK = FETCH_FIELD_BODY_PEEK_BARE + "[]";
+ public static final String FETCH_FIELD_BODY_PEEK_SANE = String.format(
+ Locale.US, "BODY.PEEK[]<0.%d>", ImapStore.FETCH_BODY_SANE_SUGGESTED_SIZE);
+ public static final String FETCH_FIELD_HEADERS =
+ "BODY.PEEK[HEADER.FIELDS (date subject from content-type to cc message-id)]";
+
+ public static final String ALERT = "ALERT";
+ public static final String APPEND = "APPEND";
+ public static final String AUTHENTICATE = "AUTHENTICATE";
+ public static final String BAD = "BAD";
+ public static final String BADCHARSET = "BADCHARSET";
+ public static final String BODY = "BODY";
+ public static final String BODY_BRACKET_HEADER = "BODY[HEADER";
+ public static final String BODYSTRUCTURE = "BODYSTRUCTURE";
+ public static final String BYE = "BYE";
+ public static final String CAPABILITY = "CAPABILITY";
+ public static final String CHECK = "CHECK";
+ public static final String CLOSE = "CLOSE";
+ public static final String COPY = "COPY";
+ public static final String COPYUID = "COPYUID";
+ public static final String CREATE = "CREATE";
+ public static final String DELETE = "DELETE";
+ public static final String EXAMINE = "EXAMINE";
+ public static final String EXISTS = "EXISTS";
+ public static final String EXPUNGE = "EXPUNGE";
+ public static final String FETCH = "FETCH";
+ public static final String FLAG_ANSWERED = "\\ANSWERED";
+ public static final String FLAG_DELETED = "\\DELETED";
+ public static final String FLAG_FLAGGED = "\\FLAGGED";
+ public static final String FLAG_NO_SELECT = "\\NOSELECT";
+ public static final String FLAG_SEEN = "\\SEEN";
+ public static final String FLAGS = "FLAGS";
+ public static final String FLAGS_SILENT = "FLAGS.SILENT";
+ public static final String ID = "ID";
+ public static final String INBOX = "INBOX";
+ public static final String INTERNALDATE = "INTERNALDATE";
+ public static final String LIST = "LIST";
+ public static final String LOGIN = "LOGIN";
+ public static final String LOGOUT = "LOGOUT";
+ public static final String LSUB = "LSUB";
+ public static final String NAMESPACE = "NAMESPACE";
+ public static final String NO = "NO";
+ public static final String NOOP = "NOOP";
+ public static final String OK = "OK";
+ public static final String PARSE = "PARSE";
+ public static final String PERMANENTFLAGS = "PERMANENTFLAGS";
+ public static final String PREAUTH = "PREAUTH";
+ public static final String READ_ONLY = "READ-ONLY";
+ public static final String READ_WRITE = "READ-WRITE";
+ public static final String RENAME = "RENAME";
+ public static final String RFC822_SIZE = "RFC822.SIZE";
+ public static final String SEARCH = "SEARCH";
+ public static final String SELECT = "SELECT";
+ public static final String STARTTLS = "STARTTLS";
+ public static final String STATUS = "STATUS";
+ public static final String STORE = "STORE";
+ public static final String SUBSCRIBE = "SUBSCRIBE";
+ public static final String TEXT = "TEXT";
+ public static final String TRYCREATE = "TRYCREATE";
+ public static final String UID = "UID";
+ public static final String UID_COPY = "UID COPY";
+ public static final String UID_FETCH = "UID FETCH";
+ public static final String UID_SEARCH = "UID SEARCH";
+ public static final String UID_STORE = "UID STORE";
+ public static final String UIDNEXT = "UIDNEXT";
+ public static final String UIDPLUS = "UIDPLUS";
+ public static final String UIDVALIDITY = "UIDVALIDITY";
+ public static final String UNSEEN = "UNSEEN";
+ public static final String UNSUBSCRIBE = "UNSUBSCRIBE";
+ public static final String XOAUTH2 = "XOAUTH2";
+ public static final String APPENDUID = "APPENDUID";
+ public static final String NIL = "NIL";
+
+ /**
+ * NO responses
+ */
+ public static final String NO_COMMAND_NOT_ALLOWED = "command not allowed";
+ public static final String NO_RESERVATION_FAILED = "reservation failed";
+ public static final String NO_APPLICATION_ERROR = "application error";
+ public static final String NO_INVALID_PARAMETER = "invalid parameter";
+ public static final String NO_INVALID_COMMAND = "invalid command";
+ public static final String NO_UNKNOWN_COMMAND = "unknown command";
+ // AUTHENTICATE
+ // The subscriber can not be located in the system.
+ public static final String NO_UNKNOWN_USER = "unknown user";
+ // The Client Type or Protocol Version is unknown.
+ public static final String NO_UNKNOWN_CLIENT = "unknown client";
+ // The password received from the client does not match the password defined in the subscriber's profile.
+ public static final String NO_INVALID_PASSWORD = "invalid password";
+ // The subscriber's mailbox has not yet been initialised via the TUI
+ public static final String NO_MAILBOX_NOT_INITIALIZED = "mailbox not initialized";
+ // The subscriber has not been provisioned for the VVM service.
+ public static final String NO_SERVICE_IS_NOT_PROVISIONED =
+ "service is not provisioned";
+ // The subscriber is provisioned for the VVM service but the VVM service is currently not active
+ public static final String NO_SERVICE_IS_NOT_ACTIVATED = "service is not activated";
+ // The Voice Mail Blocked flag in the subscriber's profile is set to YES.
+ public static final String NO_USER_IS_BLOCKED = "user is blocked";
+
+ /**
+ * extensions
+ */
+ public static final String GETQUOTA = "GETQUOTA";
+ public static final String GETQUOTAROOT = "GETQUOTAROOT";
+ public static final String QUOTAROOT = "QUOTAROOT";
+ public static final String QUOTA = "QUOTA";
+
+ /**
+ * capabilities
+ */
+ public static final String CAPABILITY_AUTH_DIGEST_MD5 = "AUTH=DIGEST-MD5";
+ public static final String CAPABILITY_STARTTLS = "STARTTLS";
+
+ /**
+ * authentication
+ */
+ public static final String AUTH_DIGEST_MD5 = "DIGEST-MD5";
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapElement.java b/java/com/android/voicemailomtp/mail/store/imap/ImapElement.java
new file mode 100644
index 000000000..9f272e31c
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/store/imap/ImapElement.java
@@ -0,0 +1,120 @@
+/*
+ * 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.voicemailomtp.mail.store.imap;
+
+/**
+ * Class representing "element"s in IMAP responses.
+ *
+ * <p>Class hierarchy:
+ * <pre>
+ * ImapElement
+ * |
+ * |-- ImapElement.NONE (for 'index out of range')
+ * |
+ * |-- ImapList (isList() == true)
+ * | |
+ * | |-- ImapList.EMPTY
+ * | |
+ * | --- ImapResponse
+ * |
+ * --- ImapString (isString() == true)
+ * |
+ * |-- ImapString.EMPTY
+ * |
+ * |-- ImapSimpleString
+ * |
+ * |-- ImapMemoryLiteral
+ * |
+ * --- ImapTempFileLiteral
+ * </pre>
+ */
+public abstract class ImapElement {
+ /**
+ * An element that is returned by {@link ImapList#getElementOrNone} to indicate an index
+ * is out of range.
+ */
+ public static final ImapElement NONE = new ImapElement() {
+ @Override public void destroy() {
+ // Don't call super.destroy().
+ // It's a shared object. We don't want the mDestroyed to be set on this.
+ }
+
+ @Override public boolean isList() {
+ return false;
+ }
+
+ @Override public boolean isString() {
+ return false;
+ }
+
+ @Override public String toString() {
+ return "[NO ELEMENT]";
+ }
+
+ @Override
+ public boolean equalsForTest(ImapElement that) {
+ return super.equalsForTest(that);
+ }
+ };
+
+ private boolean mDestroyed = false;
+
+ public abstract boolean isList();
+
+ public abstract boolean isString();
+
+ protected boolean isDestroyed() {
+ return mDestroyed;
+ }
+
+ /**
+ * Clean up the resources used by the instance.
+ * It's for removing a temp file used by {@link ImapTempFileLiteral}.
+ */
+ public void destroy() {
+ mDestroyed = true;
+ }
+
+ /**
+ * Throws {@link RuntimeException} if it's already destroyed.
+ */
+ protected final void checkNotDestroyed() {
+ if (mDestroyed) {
+ throw new RuntimeException("Already destroyed");
+ }
+ }
+
+ /**
+ * Return a string that represents this object; it's purely for the debug purpose. Don't
+ * mistake it for {@link ImapString#getString}.
+ *
+ * Abstract to force subclasses to implement it.
+ */
+ @Override
+ public abstract String toString();
+
+ /**
+ * The equals implementation that is intended to be used only for unit testing.
+ * (Because it may be heavy and has a special sense of "equal" for testing.)
+ */
+ public boolean equalsForTest(ImapElement that) {
+ if (that == null) {
+ return false;
+ }
+ return this.getClass() == that.getClass(); // Has to be the same class.
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapList.java b/java/com/android/voicemailomtp/mail/store/imap/ImapList.java
new file mode 100644
index 000000000..970423cbd
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/store/imap/ImapList.java
@@ -0,0 +1,235 @@
+/*
+ * 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.voicemailomtp.mail.store.imap;
+
+import java.util.ArrayList;
+
+/**
+ * Class represents an IMAP list.
+ */
+public class ImapList extends ImapElement {
+ /**
+ * {@link ImapList} representing an empty list.
+ */
+ public static final ImapList EMPTY = new ImapList() {
+ @Override public void destroy() {
+ // Don't call super.destroy().
+ // It's a shared object. We don't want the mDestroyed to be set on this.
+ }
+
+ @Override void add(ImapElement e) {
+ throw new RuntimeException();
+ }
+ };
+
+ private ArrayList<ImapElement> mList = new ArrayList<ImapElement>();
+
+ /* package */ void add(ImapElement e) {
+ if (e == null) {
+ throw new RuntimeException("Can't add null");
+ }
+ mList.add(e);
+ }
+
+ @Override
+ public final boolean isString() {
+ return false;
+ }
+
+ @Override
+ public final boolean isList() {
+ return true;
+ }
+
+ public final int size() {
+ return mList.size();
+ }
+
+ public final boolean isEmpty() {
+ return size() == 0;
+ }
+
+ /**
+ * Return true if the element at {@code index} exists, is string, and equals to {@code s}.
+ * (case insensitive)
+ */
+ public final boolean is(int index, String s) {
+ return is(index, s, false);
+ }
+
+ /**
+ * Same as {@link #is(int, String)}, but does the prefix match if {@code prefixMatch}.
+ */
+ public final boolean is(int index, String s, boolean prefixMatch) {
+ if (!prefixMatch) {
+ return getStringOrEmpty(index).is(s);
+ } else {
+ return getStringOrEmpty(index).startsWith(s);
+ }
+ }
+
+ /**
+ * Return the element at {@code index}.
+ * If {@code index} is out of range, returns {@link ImapElement#NONE}.
+ */
+ public final ImapElement getElementOrNone(int index) {
+ return (index >= mList.size()) ? ImapElement.NONE : mList.get(index);
+ }
+
+ /**
+ * Return the element at {@code index} if it's a list.
+ * If {@code index} is out of range or not a list, returns {@link ImapList#EMPTY}.
+ */
+ public final ImapList getListOrEmpty(int index) {
+ ImapElement el = getElementOrNone(index);
+ return el.isList() ? (ImapList) el : EMPTY;
+ }
+
+ /**
+ * Return the element at {@code index} if it's a string.
+ * If {@code index} is out of range or not a string, returns {@link ImapString#EMPTY}.
+ */
+ public final ImapString getStringOrEmpty(int index) {
+ ImapElement el = getElementOrNone(index);
+ return el.isString() ? (ImapString) el : ImapString.EMPTY;
+ }
+
+ /**
+ * Return an element keyed by {@code key}. Return null if not found. {@code key} has to be
+ * at an even index.
+ */
+ /* package */ final ImapElement getKeyedElementOrNull(String key, boolean prefixMatch) {
+ for (int i = 1; i < size(); i += 2) {
+ if (is(i-1, key, prefixMatch)) {
+ return mList.get(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return an {@link ImapList} keyed by {@code key}.
+ * Return {@link ImapList#EMPTY} if not found.
+ */
+ public final ImapList getKeyedListOrEmpty(String key) {
+ return getKeyedListOrEmpty(key, false);
+ }
+
+ /**
+ * Return an {@link ImapList} keyed by {@code key}.
+ * Return {@link ImapList#EMPTY} if not found.
+ */
+ public final ImapList getKeyedListOrEmpty(String key, boolean prefixMatch) {
+ ImapElement e = getKeyedElementOrNull(key, prefixMatch);
+ return (e != null) ? ((ImapList) e) : ImapList.EMPTY;
+ }
+
+ /**
+ * Return an {@link ImapString} keyed by {@code key}.
+ * Return {@link ImapString#EMPTY} if not found.
+ */
+ public final ImapString getKeyedStringOrEmpty(String key) {
+ return getKeyedStringOrEmpty(key, false);
+ }
+
+ /**
+ * Return an {@link ImapString} keyed by {@code key}.
+ * Return {@link ImapString#EMPTY} if not found.
+ */
+ public final ImapString getKeyedStringOrEmpty(String key, boolean prefixMatch) {
+ ImapElement e = getKeyedElementOrNull(key, prefixMatch);
+ return (e != null) ? ((ImapString) e) : ImapString.EMPTY;
+ }
+
+ /**
+ * Return true if it contains {@code s}.
+ */
+ public final boolean contains(String s) {
+ for (int i = 0; i < size(); i++) {
+ if (getStringOrEmpty(i).is(s)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void destroy() {
+ if (mList != null) {
+ for (ImapElement e : mList) {
+ e.destroy();
+ }
+ mList = null;
+ }
+ super.destroy();
+ }
+
+ @Override
+ public String toString() {
+ return mList.toString();
+ }
+
+ /**
+ * Return the text representations of the contents concatenated with ",".
+ */
+ public final String flatten() {
+ return flatten(new StringBuilder()).toString();
+ }
+
+ /**
+ * Returns text representations (i.e. getString()) of contents joined together with
+ * "," as the separator.
+ *
+ * Only used for building the capability string passed to vendor policies.
+ *
+ * We can't use toString(), because it's for debugging (meaning the format may change any time),
+ * and it won't expand literals.
+ */
+ private final StringBuilder flatten(StringBuilder sb) {
+ sb.append('[');
+ for (int i = 0; i < mList.size(); i++) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ final ImapElement e = getElementOrNone(i);
+ if (e.isList()) {
+ getListOrEmpty(i).flatten(sb);
+ } else if (e.isString()) {
+ sb.append(getStringOrEmpty(i).getString());
+ }
+ }
+ sb.append(']');
+ return sb;
+ }
+
+ @Override
+ public boolean equalsForTest(ImapElement that) {
+ if (!super.equalsForTest(that)) {
+ return false;
+ }
+ ImapList thatList = (ImapList) that;
+ if (size() != thatList.size()) {
+ return false;
+ }
+ for (int i = 0; i < size(); i++) {
+ if (!mList.get(i).equalsForTest(thatList.getElementOrNone(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapMemoryLiteral.java b/java/com/android/voicemailomtp/mail/store/imap/ImapMemoryLiteral.java
new file mode 100644
index 000000000..ad60ca7a4
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/store/imap/ImapMemoryLiteral.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 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.voicemailomtp.mail.store.imap;
+
+import com.android.voicemailomtp.mail.FixedLengthInputStream;
+import com.android.voicemailomtp.VvmLog;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Subclass of {@link ImapString} used for literals backed by an in-memory byte array.
+ */
+public class ImapMemoryLiteral extends ImapString {
+ private final String TAG = "ImapMemoryLiteral";
+ private byte[] mData;
+
+ /* package */ ImapMemoryLiteral(FixedLengthInputStream in) throws IOException {
+ // We could use ByteArrayOutputStream and IOUtils.copy, but it'd perform an unnecessary
+ // copy....
+ mData = new byte[in.getLength()];
+ int pos = 0;
+ while (pos < mData.length) {
+ int read = in.read(mData, pos, mData.length - pos);
+ if (read < 0) {
+ break;
+ }
+ pos += read;
+ }
+ if (pos != mData.length) {
+ VvmLog.w(TAG, "length mismatch");
+ }
+ }
+
+ @Override
+ public void destroy() {
+ mData = null;
+ super.destroy();
+ }
+
+ @Override
+ public String getString() {
+ try {
+ return new String(mData, "US-ASCII");
+ } catch (UnsupportedEncodingException e) {
+ VvmLog.e(TAG, "Unsupported encoding: ", e);
+ }
+ return null;
+ }
+
+ @Override
+ public InputStream getAsStream() {
+ return new ByteArrayInputStream(mData);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{%d byte literal(memory)}", mData.length);
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapResponse.java b/java/com/android/voicemailomtp/mail/store/imap/ImapResponse.java
new file mode 100644
index 000000000..412f16d8a
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/store/imap/ImapResponse.java
@@ -0,0 +1,158 @@
+/*
+ * 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.voicemailomtp.mail.store.imap;
+
+/**
+ * Class represents an IMAP response.
+ */
+public class ImapResponse extends ImapList {
+ private final String mTag;
+ private final boolean mIsContinuationRequest;
+
+ /* package */ ImapResponse(String tag, boolean isContinuationRequest) {
+ mTag = tag;
+ mIsContinuationRequest = isContinuationRequest;
+ }
+
+ /* package */ static boolean isStatusResponse(String symbol) {
+ return ImapConstants.OK.equalsIgnoreCase(symbol)
+ || ImapConstants.NO.equalsIgnoreCase(symbol)
+ || ImapConstants.BAD.equalsIgnoreCase(symbol)
+ || ImapConstants.PREAUTH.equalsIgnoreCase(symbol)
+ || ImapConstants.BYE.equalsIgnoreCase(symbol);
+ }
+
+ /**
+ * @return whether it's a tagged response.
+ */
+ public boolean isTagged() {
+ return mTag != null;
+ }
+
+ /**
+ * @return whether it's a continuation request.
+ */
+ public boolean isContinuationRequest() {
+ return mIsContinuationRequest;
+ }
+
+ public boolean isStatusResponse() {
+ return isStatusResponse(getStringOrEmpty(0).getString());
+ }
+
+ /**
+ * @return whether it's an OK response.
+ */
+ public boolean isOk() {
+ return is(0, ImapConstants.OK);
+ }
+
+ /**
+ * @return whether it's an BAD response.
+ */
+ public boolean isBad() {
+ return is(0, ImapConstants.BAD);
+ }
+
+ /**
+ * @return whether it's an NO response.
+ */
+ public boolean isNo() {
+ return is(0, ImapConstants.NO);
+ }
+
+ /**
+ * @return whether it's an {@code responseType} data response. (i.e. not tagged).
+ * @param index where {@code responseType} should appear. e.g. 1 for "FETCH"
+ * @param responseType e.g. "FETCH"
+ */
+ public final boolean isDataResponse(int index, String responseType) {
+ return !isTagged() && getStringOrEmpty(index).is(responseType);
+ }
+
+ /**
+ * @return Response code (RFC 3501 7.1) if it's a status response.
+ *
+ * e.g. "ALERT" for "* OK [ALERT] System shutdown in 10 minutes"
+ */
+ public ImapString getResponseCodeOrEmpty() {
+ if (!isStatusResponse()) {
+ return ImapString.EMPTY; // Not a status response.
+ }
+ return getListOrEmpty(1).getStringOrEmpty(0);
+ }
+
+ /**
+ * @return Alert message it it has ALERT response code.
+ *
+ * e.g. "System shutdown in 10 minutes" for "* OK [ALERT] System shutdown in 10 minutes"
+ */
+ public ImapString getAlertTextOrEmpty() {
+ if (!getResponseCodeOrEmpty().is(ImapConstants.ALERT)) {
+ return ImapString.EMPTY; // Not an ALERT
+ }
+ // The 3rd element contains all the rest of line.
+ return getStringOrEmpty(2);
+ }
+
+ /**
+ * @return Response text in a status response.
+ */
+ public ImapString getStatusResponseTextOrEmpty() {
+ if (!isStatusResponse()) {
+ return ImapString.EMPTY;
+ }
+ return getStringOrEmpty(getElementOrNone(1).isList() ? 2 : 1);
+ }
+
+ public ImapString getStatusOrEmpty() {
+ if (!isStatusResponse()) {
+ return ImapString.EMPTY;
+ }
+ return getStringOrEmpty(0);
+ }
+
+ @Override
+ public String toString() {
+ String tag = mTag;
+ if (isContinuationRequest()) {
+ tag = "+";
+ }
+ return "#" + tag + "# " + super.toString();
+ }
+
+ @Override
+ public boolean equalsForTest(ImapElement that) {
+ if (!super.equalsForTest(that)) {
+ return false;
+ }
+ final ImapResponse thatResponse = (ImapResponse) that;
+ if (mTag == null) {
+ if (thatResponse.mTag != null) {
+ return false;
+ }
+ } else {
+ if (!mTag.equals(thatResponse.mTag)) {
+ return false;
+ }
+ }
+ if (mIsContinuationRequest != thatResponse.mIsContinuationRequest) {
+ return false;
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapResponseParser.java b/java/com/android/voicemailomtp/mail/store/imap/ImapResponseParser.java
new file mode 100644
index 000000000..692596f14
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/store/imap/ImapResponseParser.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2010 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.voicemailomtp.mail.store.imap;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.voicemailomtp.mail.FixedLengthInputStream;
+import com.android.voicemailomtp.mail.MessagingException;
+import com.android.voicemailomtp.mail.PeekableInputStream;
+import com.android.voicemailomtp.VvmLog;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+/**
+ * IMAP response parser.
+ */
+public class ImapResponseParser {
+ private static final String TAG = "ImapResponseParser";
+
+ /**
+ * Literal larger than this will be stored in temp file.
+ */
+ public static final int LITERAL_KEEP_IN_MEMORY_THRESHOLD = 2 * 1024 * 1024;
+
+ /** Input stream */
+ private final PeekableInputStream mIn;
+
+ private final int mLiteralKeepInMemoryThreshold;
+
+ /** StringBuilder used by readUntil() */
+ private final StringBuilder mBufferReadUntil = new StringBuilder();
+
+ /** StringBuilder used by parseBareString() */
+ private final StringBuilder mParseBareString = new StringBuilder();
+
+ /**
+ * We store all {@link ImapResponse} in it. {@link #destroyResponses()} must be called from
+ * time to time to destroy them and clear it.
+ */
+ private final ArrayList<ImapResponse> mResponsesToDestroy = new ArrayList<ImapResponse>();
+
+ /**
+ * Exception thrown when we receive BYE. It derives from IOException, so it'll be treated
+ * in the same way EOF does.
+ */
+ public static class ByeException extends IOException {
+ public static final String MESSAGE = "Received BYE";
+ public ByeException() {
+ super(MESSAGE);
+ }
+ }
+
+ /**
+ * Public constructor for normal use.
+ */
+ public ImapResponseParser(InputStream in) {
+ this(in, LITERAL_KEEP_IN_MEMORY_THRESHOLD);
+ }
+
+ /**
+ * Constructor for testing to override the literal size threshold.
+ */
+ /* package for test */ ImapResponseParser(InputStream in, int literalKeepInMemoryThreshold) {
+ mIn = new PeekableInputStream(in);
+ mLiteralKeepInMemoryThreshold = literalKeepInMemoryThreshold;
+ }
+
+ private static IOException newEOSException() {
+ final String message = "End of stream reached";
+ VvmLog.d(TAG, message);
+ return new IOException(message);
+ }
+
+ /**
+ * Peek next one byte.
+ *
+ * Throws IOException() if reaches EOF. As long as logical response lines end with \r\n,
+ * we shouldn't see EOF during parsing.
+ */
+ private int peek() throws IOException {
+ final int next = mIn.peek();
+ if (next == -1) {
+ throw newEOSException();
+ }
+ return next;
+ }
+
+ /**
+ * Read and return one byte from {@link #mIn}, and put it in {@link #mDiscourseLogger}.
+ *
+ * Throws IOException() if reaches EOF. As long as logical response lines end with \r\n,
+ * we shouldn't see EOF during parsing.
+ */
+ private int readByte() throws IOException {
+ int next = mIn.read();
+ if (next == -1) {
+ throw newEOSException();
+ }
+ return next;
+ }
+
+ /**
+ * Destroy all the {@link ImapResponse}s stored in the internal storage and clear it.
+ *
+ * @see #readResponse()
+ */
+ public void destroyResponses() {
+ for (ImapResponse r : mResponsesToDestroy) {
+ r.destroy();
+ }
+ mResponsesToDestroy.clear();
+ }
+
+ /**
+ * Reads the next response available on the stream and returns an
+ * {@link ImapResponse} object that represents it.
+ *
+ * <p>When this method successfully returns an {@link ImapResponse}, the {@link ImapResponse}
+ * is stored in the internal storage. When the {@link ImapResponse} is no longer used
+ * {@link #destroyResponses} should be called to destroy all the responses in the array.
+ *
+ * @param byeExpected is a untagged BYE response expected? If not proper cleanup will be done
+ * and {@link ByeException} will be thrown.
+ * @return the parsed {@link ImapResponse} object.
+ * @exception ByeException when detects BYE and <code>byeExpected</code> is false.
+ */
+ public ImapResponse readResponse(boolean byeExpected) throws IOException, MessagingException {
+ ImapResponse response = null;
+ try {
+ response = parseResponse();
+ } catch (RuntimeException e) {
+ // Parser crash -- log network activities.
+ onParseError(e);
+ throw e;
+ } catch (IOException e) {
+ // Network error, or received an unexpected char.
+ onParseError(e);
+ throw e;
+ }
+
+ // Handle this outside of try-catch. We don't have to dump protocol log when getting BYE.
+ if (!byeExpected && response.is(0, ImapConstants.BYE)) {
+ Log.w(TAG, ByeException.MESSAGE);
+ response.destroy();
+ throw new ByeException();
+ }
+ mResponsesToDestroy.add(response);
+ return response;
+ }
+
+ private void onParseError(Exception e) {
+ // Read a few more bytes, so that the log will contain some more context, even if the parser
+ // crashes in the middle of a response.
+ // This also makes sure the byte in question will be logged, no matter where it crashes.
+ // e.g. when parseAtom() peeks and finds at an unexpected char, it throws an exception
+ // before actually reading it.
+ // However, we don't want to read too much, because then it may get into an email message.
+ try {
+ for (int i = 0; i < 4; i++) {
+ int b = readByte();
+ if (b == -1 || b == '\n') {
+ break;
+ }
+ }
+ } catch (IOException ignore) {
+ }
+ VvmLog.w(TAG, "Exception detected: " + e.getMessage());
+ }
+
+ /**
+ * Read next byte from stream and throw it away. If the byte is different from {@code expected}
+ * throw {@link MessagingException}.
+ */
+ /* package for test */ void expect(char expected) throws IOException {
+ final int next = readByte();
+ if (expected != next) {
+ throw new IOException(String.format("Expected %04x (%c) but got %04x (%c)",
+ (int) expected, expected, next, (char) next));
+ }
+ }
+
+ /**
+ * Read bytes until we find {@code end}, and return all as string.
+ * The {@code end} will be read (rather than peeked) and won't be included in the result.
+ */
+ /* package for test */ String readUntil(char end) throws IOException {
+ mBufferReadUntil.setLength(0);
+ for (;;) {
+ final int ch = readByte();
+ if (ch != end) {
+ mBufferReadUntil.append((char) ch);
+ } else {
+ return mBufferReadUntil.toString();
+ }
+ }
+ }
+
+ /**
+ * Read all bytes until \r\n.
+ */
+ /* package */ String readUntilEol() throws IOException {
+ String ret = readUntil('\r');
+ expect('\n'); // TODO Should this really be error?
+ return ret;
+ }
+
+ /**
+ * Parse and return the response line.
+ */
+ private ImapResponse parseResponse() throws IOException, MessagingException {
+ // We need to destroy the response if we get an exception.
+ // So, we first store the response that's being built in responseToDestroy, until it's
+ // completely built, at which point we copy it into responseToReturn and null out
+ // responseToDestroyt.
+ // If responseToDestroy is not null in finally, we destroy it because that means
+ // we got an exception somewhere.
+ ImapResponse responseToDestroy = null;
+ final ImapResponse responseToReturn;
+
+ try {
+ final int ch = peek();
+ if (ch == '+') { // Continuation request
+ readByte(); // skip +
+ expect(' ');
+ responseToDestroy = new ImapResponse(null, true);
+
+ // If it's continuation request, we don't really care what's in it.
+ responseToDestroy.add(new ImapSimpleString(readUntilEol()));
+
+ // Response has successfully been built. Let's return it.
+ responseToReturn = responseToDestroy;
+ responseToDestroy = null;
+ } else {
+ // Status response or response data
+ final String tag;
+ if (ch == '*') {
+ tag = null;
+ readByte(); // skip *
+ expect(' ');
+ } else {
+ tag = readUntil(' ');
+ }
+ responseToDestroy = new ImapResponse(tag, false);
+
+ final ImapString firstString = parseBareString();
+ responseToDestroy.add(firstString);
+
+ // parseBareString won't eat a space after the string, so we need to skip it,
+ // if exists.
+ // If the next char is not ' ', it should be EOL.
+ if (peek() == ' ') {
+ readByte(); // skip ' '
+
+ if (responseToDestroy.isStatusResponse()) { // It's a status response
+
+ // Is there a response code?
+ final int next = peek();
+ if (next == '[') {
+ responseToDestroy.add(parseList('[', ']'));
+ if (peek() == ' ') { // Skip following space
+ readByte();
+ }
+ }
+
+ String rest = readUntilEol();
+ if (!TextUtils.isEmpty(rest)) {
+ // The rest is free-form text.
+ responseToDestroy.add(new ImapSimpleString(rest));
+ }
+ } else { // It's a response data.
+ parseElements(responseToDestroy, '\0');
+ }
+ } else {
+ expect('\r');
+ expect('\n');
+ }
+
+ // Response has successfully been built. Let's return it.
+ responseToReturn = responseToDestroy;
+ responseToDestroy = null;
+ }
+ } finally {
+ if (responseToDestroy != null) {
+ // We get an exception.
+ responseToDestroy.destroy();
+ }
+ }
+
+ return responseToReturn;
+ }
+
+ private ImapElement parseElement() throws IOException, MessagingException {
+ final int next = peek();
+ switch (next) {
+ case '(':
+ return parseList('(', ')');
+ case '[':
+ return parseList('[', ']');
+ case '"':
+ readByte(); // Skip "
+ return new ImapSimpleString(readUntil('"'));
+ case '{':
+ return parseLiteral();
+ case '\r': // CR
+ readByte(); // Consume \r
+ expect('\n'); // Should be followed by LF.
+ return null;
+ case '\n': // LF // There shouldn't be a bare LF, but just in case.
+ readByte(); // Consume \n
+ return null;
+ default:
+ return parseBareString();
+ }
+ }
+
+ /**
+ * Parses an atom.
+ *
+ * Special case: If an atom contains '[', everything until the next ']' will be considered
+ * a part of the atom.
+ * (e.g. "BODY[HEADER.FIELDS ("DATE" ...)]" will become a single ImapString)
+ *
+ * If the value is "NIL", returns an empty string.
+ */
+ private ImapString parseBareString() throws IOException, MessagingException {
+ mParseBareString.setLength(0);
+ for (;;) {
+ final int ch = peek();
+
+ // TODO Can we clean this up? (This condition is from the old parser.)
+ if (ch == '(' || ch == ')' || ch == '{' || ch == ' ' ||
+ // ']' is not part of atom (it's in resp-specials)
+ ch == ']' ||
+ // docs claim that flags are \ atom but atom isn't supposed to
+ // contain
+ // * and some flags contain *
+ // ch == '%' || ch == '*' ||
+ ch == '%' ||
+ // TODO probably should not allow \ and should recognize
+ // it as a flag instead
+ // ch == '"' || ch == '\' ||
+ ch == '"' || (0x00 <= ch && ch <= 0x1f) || ch == 0x7f) {
+ if (mParseBareString.length() == 0) {
+ throw new MessagingException("Expected string, none found.");
+ }
+ String s = mParseBareString.toString();
+
+ // NIL will be always converted into the empty string.
+ if (ImapConstants.NIL.equalsIgnoreCase(s)) {
+ return ImapString.EMPTY;
+ }
+ return new ImapSimpleString(s);
+ } else if (ch == '[') {
+ // Eat all until next ']'
+ mParseBareString.append((char) readByte());
+ mParseBareString.append(readUntil(']'));
+ mParseBareString.append(']'); // readUntil won't include the end char.
+ } else {
+ mParseBareString.append((char) readByte());
+ }
+ }
+ }
+
+ private void parseElements(ImapList list, char end)
+ throws IOException, MessagingException {
+ for (;;) {
+ for (;;) {
+ final int next = peek();
+ if (next == end) {
+ return;
+ }
+ if (next != ' ') {
+ break;
+ }
+ // Skip space
+ readByte();
+ }
+ final ImapElement el = parseElement();
+ if (el == null) { // EOL
+ return;
+ }
+ list.add(el);
+ }
+ }
+
+ private ImapList parseList(char opening, char closing)
+ throws IOException, MessagingException {
+ expect(opening);
+ final ImapList list = new ImapList();
+ parseElements(list, closing);
+ expect(closing);
+ return list;
+ }
+
+ private ImapString parseLiteral() throws IOException, MessagingException {
+ expect('{');
+ final int size;
+ try {
+ size = Integer.parseInt(readUntil('}'));
+ } catch (NumberFormatException nfe) {
+ throw new MessagingException("Invalid length in literal");
+ }
+ if (size < 0) {
+ throw new MessagingException("Invalid negative length in literal");
+ }
+ expect('\r');
+ expect('\n');
+ FixedLengthInputStream in = new FixedLengthInputStream(mIn, size);
+ if (size > mLiteralKeepInMemoryThreshold) {
+ return new ImapTempFileLiteral(in);
+ } else {
+ return new ImapMemoryLiteral(in);
+ }
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapSimpleString.java b/java/com/android/voicemailomtp/mail/store/imap/ImapSimpleString.java
new file mode 100644
index 000000000..22d8141a0
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/store/imap/ImapSimpleString.java
@@ -0,0 +1,62 @@
+/*
+ * 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.voicemailomtp.mail.store.imap;
+
+import com.android.voicemailomtp.VvmLog;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Subclass of {@link ImapString} used for non literals.
+ */
+public class ImapSimpleString extends ImapString {
+ private final String TAG = "ImapSimpleString";
+ private String mString;
+
+ /* package */ ImapSimpleString(String string) {
+ mString = (string != null) ? string : "";
+ }
+
+ @Override
+ public void destroy() {
+ mString = null;
+ super.destroy();
+ }
+
+ @Override
+ public String getString() {
+ return mString;
+ }
+
+ @Override
+ public InputStream getAsStream() {
+ try {
+ return new ByteArrayInputStream(mString.getBytes("US-ASCII"));
+ } catch (UnsupportedEncodingException e) {
+ VvmLog.e(TAG, "Unsupported encoding: ", e);
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ // Purposefully not return just mString, in order to prevent using it instead of getString.
+ return "\"" + mString + "\"";
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapString.java b/java/com/android/voicemailomtp/mail/store/imap/ImapString.java
new file mode 100644
index 000000000..83efb6479
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/store/imap/ImapString.java
@@ -0,0 +1,192 @@
+/*
+ * 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.voicemailomtp.mail.store.imap;
+
+import com.android.voicemailomtp.VvmLog;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * Class represents an IMAP "element" that is not a list.
+ *
+ * An atom, quoted string, literal, are all represented by this. Values like OK, STATUS are too.
+ * Also, this class class may contain more arbitrary value like "BODY[HEADER.FIELDS ("DATE")]".
+ * See {@link ImapResponseParser}.
+ */
+public abstract class ImapString extends ImapElement {
+ private static final byte[] EMPTY_BYTES = new byte[0];
+
+ public static final ImapString EMPTY = new ImapString() {
+ @Override public void destroy() {
+ // Don't call super.destroy().
+ // It's a shared object. We don't want the mDestroyed to be set on this.
+ }
+
+ @Override public String getString() {
+ return "";
+ }
+
+ @Override public InputStream getAsStream() {
+ return new ByteArrayInputStream(EMPTY_BYTES);
+ }
+
+ @Override public String toString() {
+ return "";
+ }
+ };
+
+ // This is used only for parsing IMAP's FETCH ENVELOPE command, in which
+ // en_US-like date format is used like "01-Jan-2009 11:20:39 -0800", so this should be
+ // handled by Locale.US
+ private final static SimpleDateFormat DATE_TIME_FORMAT =
+ new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US);
+
+ private boolean mIsInteger;
+ private int mParsedInteger;
+ private Date mParsedDate;
+
+ @Override
+ public final boolean isList() {
+ return false;
+ }
+
+ @Override
+ public final boolean isString() {
+ return true;
+ }
+
+ /**
+ * @return true if and only if the length of the string is larger than 0.
+ *
+ * Note: IMAP NIL is considered an empty string. See {@link ImapResponseParser
+ * #parseBareString}.
+ * On the other hand, a quoted/literal string with value NIL (i.e. "NIL" and {3}\r\nNIL) is
+ * treated literally.
+ */
+ public final boolean isEmpty() {
+ return getString().length() == 0;
+ }
+
+ public abstract String getString();
+
+ public abstract InputStream getAsStream();
+
+ /**
+ * @return whether it can be parsed as a number.
+ */
+ public final boolean isNumber() {
+ if (mIsInteger) {
+ return true;
+ }
+ try {
+ mParsedInteger = Integer.parseInt(getString());
+ mIsInteger = true;
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ /**
+ * @return value parsed as a number, or 0 if the string is not a number.
+ */
+ public final int getNumberOrZero() {
+ return getNumber(0);
+ }
+
+ /**
+ * @return value parsed as a number, or {@code defaultValue} if the string is not a number.
+ */
+ public final int getNumber(int defaultValue) {
+ if (!isNumber()) {
+ return defaultValue;
+ }
+ return mParsedInteger;
+ }
+
+ /**
+ * @return whether it can be parsed as a date using {@link #DATE_TIME_FORMAT}.
+ */
+ public final boolean isDate() {
+ if (mParsedDate != null) {
+ return true;
+ }
+ if (isEmpty()) {
+ return false;
+ }
+ try {
+ mParsedDate = DATE_TIME_FORMAT.parse(getString());
+ return true;
+ } catch (ParseException e) {
+ VvmLog.w("ImapString", getString() + " can't be parsed as a date.");
+ return false;
+ }
+ }
+
+ /**
+ * @return value it can be parsed as a {@link Date}, or null otherwise.
+ */
+ public final Date getDateOrNull() {
+ if (!isDate()) {
+ return null;
+ }
+ return mParsedDate;
+ }
+
+ /**
+ * @return whether the value case-insensitively equals to {@code s}.
+ */
+ public final boolean is(String s) {
+ if (s == null) {
+ return false;
+ }
+ return getString().equalsIgnoreCase(s);
+ }
+
+
+ /**
+ * @return whether the value case-insensitively starts with {@code s}.
+ */
+ public final boolean startsWith(String prefix) {
+ if (prefix == null) {
+ return false;
+ }
+ final String me = this.getString();
+ if (me.length() < prefix.length()) {
+ return false;
+ }
+ return me.substring(0, prefix.length()).equalsIgnoreCase(prefix);
+ }
+
+ // To force subclasses to implement it.
+ @Override
+ public abstract String toString();
+
+ @Override
+ public final boolean equalsForTest(ImapElement that) {
+ if (!super.equalsForTest(that)) {
+ return false;
+ }
+ ImapString thatString = (ImapString) that;
+ return getString().equals(thatString.getString());
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapTempFileLiteral.java b/java/com/android/voicemailomtp/mail/store/imap/ImapTempFileLiteral.java
new file mode 100644
index 000000000..efe5c3848
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/store/imap/ImapTempFileLiteral.java
@@ -0,0 +1,123 @@
+/*
+ * 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.voicemailomtp.mail.store.imap;
+
+import com.android.voicemailomtp.mail.FixedLengthInputStream;
+import com.android.voicemailomtp.mail.TempDirectory;
+import com.android.voicemailomtp.mail.utils.Utility;
+import com.android.voicemailomtp.mail.utils.LogUtils;
+
+import org.apache.commons.io.IOUtils;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Subclass of {@link ImapString} used for literals backed by a temp file.
+ */
+public class ImapTempFileLiteral extends ImapString {
+ private final String TAG = "ImapTempFileLiteral";
+
+ /* package for test */ final File mFile;
+
+ /** Size is purely for toString() */
+ private final int mSize;
+
+ /* package */ ImapTempFileLiteral(FixedLengthInputStream stream) throws IOException {
+ mSize = stream.getLength();
+ mFile = File.createTempFile("imap", ".tmp", TempDirectory.getTempDirectory());
+
+ // Unfortunately, we can't really use deleteOnExit(), because temp filenames are random
+ // so it'd simply cause a memory leak.
+ // deleteOnExit() simply adds filenames to a static list and the list will never shrink.
+ // mFile.deleteOnExit();
+ OutputStream out = new FileOutputStream(mFile);
+ IOUtils.copy(stream, out);
+ out.close();
+ }
+
+ /**
+ * Make sure we delete the temp file.
+ *
+ * We should always be calling {@link ImapResponse#destroy()}, but it's here as a last resort.
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ destroy();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ @Override
+ public InputStream getAsStream() {
+ checkNotDestroyed();
+ try {
+ return new FileInputStream(mFile);
+ } catch (FileNotFoundException e) {
+ // It's probably possible if we're low on storage and the system clears the cache dir.
+ LogUtils.w(TAG, "ImapTempFileLiteral: Temp file not found");
+
+ // Return 0 byte stream as a dummy...
+ return new ByteArrayInputStream(new byte[0]);
+ }
+ }
+
+ @Override
+ public String getString() {
+ checkNotDestroyed();
+ try {
+ byte[] bytes = IOUtils.toByteArray(getAsStream());
+ // Prevent crash from OOM; we've seen this, but only rarely and not reproducibly
+ if (bytes.length > ImapResponseParser.LITERAL_KEEP_IN_MEMORY_THRESHOLD) {
+ throw new IOException();
+ }
+ return Utility.fromAscii(bytes);
+ } catch (IOException e) {
+ LogUtils.w(TAG, "ImapTempFileLiteral: Error while reading temp file", e);
+ return "";
+ }
+ }
+
+ @Override
+ public void destroy() {
+ try {
+ if (!isDestroyed() && mFile.exists()) {
+ mFile.delete();
+ }
+ } catch (RuntimeException re) {
+ // Just log and ignore.
+ LogUtils.w(TAG, "Failed to remove temp file: " + re.getMessage());
+ }
+ super.destroy();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{%d byte literal(file)}", mSize);
+ }
+
+ public boolean tempFileExistsForTest() {
+ return mFile.exists();
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapUtility.java b/java/com/android/voicemailomtp/mail/store/imap/ImapUtility.java
new file mode 100644
index 000000000..b045eb32f
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/store/imap/ImapUtility.java
@@ -0,0 +1,125 @@
+/*
+ * 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.voicemailomtp.mail.store.imap;
+
+import com.android.voicemailomtp.mail.utils.LogUtils;
+
+import java.util.ArrayList;
+
+/**
+ * Utility methods for use with IMAP.
+ */
+public class ImapUtility {
+ public static final String TAG = "ImapUtility";
+ /**
+ * Apply quoting rules per IMAP RFC,
+ * quoted = DQUOTE *QUOTED-CHAR DQUOTE
+ * QUOTED-CHAR = <any TEXT-CHAR except quoted-specials> / "\" quoted-specials
+ * quoted-specials = DQUOTE / "\"
+ *
+ * This is used primarily for IMAP login, but might be useful elsewhere.
+ *
+ * NOTE: Not very efficient - you may wish to preflight this, or perhaps it should check
+ * for trouble chars before calling the replace functions.
+ *
+ * @param s The string to be quoted.
+ * @return A copy of the string, having undergone quoting as described above
+ */
+ public static String imapQuoted(String s) {
+
+ // First, quote any backslashes by replacing \ with \\
+ // regex Pattern: \\ (Java string const = \\\\)
+ // Substitute: \\\\ (Java string const = \\\\\\\\)
+ String result = s.replaceAll("\\\\", "\\\\\\\\");
+
+ // Then, quote any double-quotes by replacing " with \"
+ // regex Pattern: " (Java string const = \")
+ // Substitute: \\" (Java string const = \\\\\")
+ result = result.replaceAll("\"", "\\\\\"");
+
+ // return string with quotes around it
+ return "\"" + result + "\"";
+ }
+
+ /**
+ * Gets all of the values in a sequence set per RFC 3501. Any ranges are expanded into a
+ * list of individual numbers. If the set is invalid, an empty array is returned.
+ * <pre>
+ * sequence-number = nz-number / "*"
+ * sequence-range = sequence-number ":" sequence-number
+ * sequence-set = (sequence-number / sequence-range) *("," sequence-set)
+ * </pre>
+ */
+ public static String[] getImapSequenceValues(String set) {
+ ArrayList<String> list = new ArrayList<String>();
+ if (set != null) {
+ String[] setItems = set.split(",");
+ for (String item : setItems) {
+ if (item.indexOf(':') == -1) {
+ // simple item
+ try {
+ Integer.parseInt(item); // Don't need the value; just ensure it's valid
+ list.add(item);
+ } catch (NumberFormatException e) {
+ LogUtils.d(TAG, "Invalid UID value", e);
+ }
+ } else {
+ // range
+ for (String rangeItem : getImapRangeValues(item)) {
+ list.add(rangeItem);
+ }
+ }
+ }
+ }
+ String[] stringList = new String[list.size()];
+ return list.toArray(stringList);
+ }
+
+ /**
+ * Expand the given number range into a list of individual numbers. If the range is not valid,
+ * an empty array is returned.
+ * <pre>
+ * sequence-number = nz-number / "*"
+ * sequence-range = sequence-number ":" sequence-number
+ * sequence-set = (sequence-number / sequence-range) *("," sequence-set)
+ * </pre>
+ */
+ public static String[] getImapRangeValues(String range) {
+ ArrayList<String> list = new ArrayList<String>();
+ try {
+ if (range != null) {
+ int colonPos = range.indexOf(':');
+ if (colonPos > 0) {
+ int first = Integer.parseInt(range.substring(0, colonPos));
+ int second = Integer.parseInt(range.substring(colonPos + 1));
+ if (first < second) {
+ for (int i = first; i <= second; i++) {
+ list.add(Integer.toString(i));
+ }
+ } else {
+ for (int i = first; i >= second; i--) {
+ list.add(Integer.toString(i));
+ }
+ }
+ }
+ }
+ } catch (NumberFormatException e) {
+ LogUtils.d(TAG, "Invalid range value", e);
+ }
+ String[] stringList = new String[list.size()];
+ return list.toArray(stringList);
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/utility/CountingOutputStream.java b/java/com/android/voicemailomtp/mail/utility/CountingOutputStream.java
new file mode 100644
index 000000000..fdf81d44a
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/utility/CountingOutputStream.java
@@ -0,0 +1,48 @@
+/*
+ * 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.voicemailomtp.mail.utility;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A simple pass-thru OutputStream that also counts how many bytes are written to it and
+ * makes that count available to callers.
+ */
+public class CountingOutputStream extends OutputStream {
+ private long mCount;
+ private final OutputStream mOutputStream;
+
+ public CountingOutputStream(OutputStream outputStream) {
+ mOutputStream = outputStream;
+ }
+
+ public long getCount() {
+ return mCount;
+ }
+
+ @Override
+ public void write(byte[] buffer, int offset, int count) throws IOException {
+ mOutputStream.write(buffer, offset, count);
+ mCount += count;
+ }
+
+ @Override
+ public void write(int oneByte) throws IOException {
+ mOutputStream.write(oneByte);
+ mCount++;
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/utility/EOLConvertingOutputStream.java b/java/com/android/voicemailomtp/mail/utility/EOLConvertingOutputStream.java
new file mode 100644
index 000000000..5b93a92ab
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/utility/EOLConvertingOutputStream.java
@@ -0,0 +1,48 @@
+/*
+ * 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.voicemailomtp.mail.utility;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class EOLConvertingOutputStream extends FilterOutputStream {
+ int lastChar;
+
+ public EOLConvertingOutputStream(OutputStream out) {
+ super(out);
+ }
+
+ @Override
+ public void write(int oneByte) throws IOException {
+ if (oneByte == '\n') {
+ if (lastChar != '\r') {
+ super.write('\r');
+ }
+ }
+ super.write(oneByte);
+ lastChar = oneByte;
+ }
+
+ @Override
+ public void flush() throws IOException {
+ if (lastChar == '\r') {
+ super.write('\n');
+ lastChar = '\n';
+ }
+ super.flush();
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/utils/LogUtils.java b/java/com/android/voicemailomtp/mail/utils/LogUtils.java
new file mode 100644
index 000000000..a213a835e
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/utils/LogUtils.java
@@ -0,0 +1,413 @@
+/**
+ * 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.voicemailomtp.mail.utils;
+
+import android.net.Uri;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.voicemailomtp.VvmLog;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class LogUtils {
+ public static final String TAG = "Email Log";
+
+ // "GMT" + "+" or "-" + 4 digits
+ private static final Pattern DATE_CLEANUP_PATTERN_WRONG_TIMEZONE =
+ Pattern.compile("GMT([-+]\\d{4})$");
+
+ private static final String ACCOUNT_PREFIX = "account:";
+
+ /**
+ * Priority constant for the println method; use LogUtils.v.
+ */
+ public static final int VERBOSE = Log.VERBOSE;
+
+ /**
+ * Priority constant for the println method; use LogUtils.d.
+ */
+ public static final int DEBUG = Log.DEBUG;
+
+ /**
+ * Priority constant for the println method; use LogUtils.i.
+ */
+ public static final int INFO = Log.INFO;
+
+ /**
+ * Priority constant for the println method; use LogUtils.w.
+ */
+ public static final int WARN = Log.WARN;
+
+ /**
+ * Priority constant for the println method; use LogUtils.e.
+ */
+ public static final int ERROR = Log.ERROR;
+
+ /**
+ * Used to enable/disable logging that we don't want included in production releases. This should
+ * be set to DEBUG for production releases, and VERBOSE for internal builds.
+ */
+ private static final int MAX_ENABLED_LOG_LEVEL = DEBUG;
+
+ private static Boolean sDebugLoggingEnabledForTests = null;
+
+ /**
+ * Enable debug logging for unit tests.
+ */
+ @VisibleForTesting
+ public static void setDebugLoggingEnabledForTests(boolean enabled) {
+ setDebugLoggingEnabledForTestsInternal(enabled);
+ }
+
+ protected static void setDebugLoggingEnabledForTestsInternal(boolean enabled) {
+ sDebugLoggingEnabledForTests = Boolean.valueOf(enabled);
+ }
+
+ /**
+ * Returns true if the build configuration prevents debug logging.
+ */
+ @VisibleForTesting
+ public static boolean buildPreventsDebugLogging() {
+ return MAX_ENABLED_LOG_LEVEL > VERBOSE;
+ }
+
+ /**
+ * Returns a boolean indicating whether debug logging is enabled.
+ */
+ protected static boolean isDebugLoggingEnabled(String tag) {
+ if (buildPreventsDebugLogging()) {
+ return false;
+ }
+ if (sDebugLoggingEnabledForTests != null) {
+ return sDebugLoggingEnabledForTests.booleanValue();
+ }
+ return Log.isLoggable(tag, Log.DEBUG) || Log.isLoggable(TAG, Log.DEBUG);
+ }
+
+ /**
+ * Returns a String for the specified content provider uri. This will do
+ * sanitation of the uri to remove PII if debug logging is not enabled.
+ */
+ public static String contentUriToString(final Uri uri) {
+ return contentUriToString(TAG, uri);
+ }
+
+ /**
+ * Returns a String for the specified content provider uri. This will do
+ * sanitation of the uri to remove PII if debug logging is not enabled.
+ */
+ public static String contentUriToString(String tag, Uri uri) {
+ if (isDebugLoggingEnabled(tag)) {
+ // Debug logging has been enabled, so log the uri as is
+ return uri.toString();
+ } else {
+ // Debug logging is not enabled, we want to remove the email address from the uri.
+ List<String> pathSegments = uri.getPathSegments();
+
+ Uri.Builder builder = new Uri.Builder()
+ .scheme(uri.getScheme())
+ .authority(uri.getAuthority())
+ .query(uri.getQuery())
+ .fragment(uri.getFragment());
+
+ // This assumes that the first path segment is the account
+ final String account = pathSegments.get(0);
+
+ builder = builder.appendPath(sanitizeAccountName(account));
+ for (int i = 1; i < pathSegments.size(); i++) {
+ builder.appendPath(pathSegments.get(i));
+ }
+ return builder.toString();
+ }
+ }
+
+ /**
+ * Sanitizes an account name. If debug logging is not enabled, a sanitized name
+ * is returned.
+ */
+ public static String sanitizeAccountName(String accountName) {
+ if (TextUtils.isEmpty(accountName)) {
+ return "";
+ }
+
+ return ACCOUNT_PREFIX + sanitizeName(TAG, accountName);
+ }
+
+ public static String sanitizeName(final String tag, final String name) {
+ if (TextUtils.isEmpty(name)) {
+ return "";
+ }
+
+ if (isDebugLoggingEnabled(tag)) {
+ return name;
+ }
+
+ return String.valueOf(name.hashCode());
+ }
+
+ /**
+ * Checks to see whether or not a log for the specified tag is loggable at the specified level.
+ */
+ public static boolean isLoggable(String tag, int level) {
+ if (MAX_ENABLED_LOG_LEVEL > level) {
+ return false;
+ }
+ return Log.isLoggable(tag, level) || Log.isLoggable(TAG, level);
+ }
+
+ /**
+ * Send a {@link #VERBOSE} log message.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param format the format string (see {@link java.util.Formatter#format})
+ * @param args
+ * the list of arguments passed to the formatter. If there are
+ * more arguments than required by {@code format},
+ * additional arguments are ignored.
+ */
+ public static int v(String tag, String format, Object... args) {
+ if (isLoggable(tag, VERBOSE)) {
+ return VvmLog.v(tag, String.format(format, args));
+ }
+ return 0;
+ }
+
+ /**
+ * Send a {@link #VERBOSE} log message.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param tr An exception to log
+ * @param format the format string (see {@link java.util.Formatter#format})
+ * @param args
+ * the list of arguments passed to the formatter. If there are
+ * more arguments than required by {@code format},
+ * additional arguments are ignored.
+ */
+ public static int v(String tag, Throwable tr, String format, Object... args) {
+ if (isLoggable(tag, VERBOSE)) {
+ return VvmLog.v(tag, String.format(format, args), tr);
+ }
+ return 0;
+ }
+
+ /**
+ * Send a {@link #DEBUG} log message.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param format the format string (see {@link java.util.Formatter#format})
+ * @param args
+ * the list of arguments passed to the formatter. If there are
+ * more arguments than required by {@code format},
+ * additional arguments are ignored.
+ */
+ public static int d(String tag, String format, Object... args) {
+ if (isLoggable(tag, DEBUG)) {
+ return VvmLog.d(tag, String.format(format, args));
+ }
+ return 0;
+ }
+
+ /**
+ * Send a {@link #DEBUG} log message.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param tr An exception to log
+ * @param format the format string (see {@link java.util.Formatter#format})
+ * @param args
+ * the list of arguments passed to the formatter. If there are
+ * more arguments than required by {@code format},
+ * additional arguments are ignored.
+ */
+ public static int d(String tag, Throwable tr, String format, Object... args) {
+ if (isLoggable(tag, DEBUG)) {
+ return VvmLog.d(tag, String.format(format, args), tr);
+ }
+ return 0;
+ }
+
+ /**
+ * Send a {@link #INFO} log message.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param format the format string (see {@link java.util.Formatter#format})
+ * @param args
+ * the list of arguments passed to the formatter. If there are
+ * more arguments than required by {@code format},
+ * additional arguments are ignored.
+ */
+ public static int i(String tag, String format, Object... args) {
+ if (isLoggable(tag, INFO)) {
+ return VvmLog.i(tag, String.format(format, args));
+ }
+ return 0;
+ }
+
+ /**
+ * Send a {@link #INFO} log message.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param tr An exception to log
+ * @param format the format string (see {@link java.util.Formatter#format})
+ * @param args
+ * the list of arguments passed to the formatter. If there are
+ * more arguments than required by {@code format},
+ * additional arguments are ignored.
+ */
+ public static int i(String tag, Throwable tr, String format, Object... args) {
+ if (isLoggable(tag, INFO)) {
+ return VvmLog.i(tag, String.format(format, args), tr);
+ }
+ return 0;
+ }
+
+ /**
+ * Send a {@link #WARN} log message.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param format the format string (see {@link java.util.Formatter#format})
+ * @param args
+ * the list of arguments passed to the formatter. If there are
+ * more arguments than required by {@code format},
+ * additional arguments are ignored.
+ */
+ public static int w(String tag, String format, Object... args) {
+ if (isLoggable(tag, WARN)) {
+ return VvmLog.w(tag, String.format(format, args));
+ }
+ return 0;
+ }
+
+ /**
+ * Send a {@link #WARN} log message.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param tr An exception to log
+ * @param format the format string (see {@link java.util.Formatter#format})
+ * @param args
+ * the list of arguments passed to the formatter. If there are
+ * more arguments than required by {@code format},
+ * additional arguments are ignored.
+ */
+ public static int w(String tag, Throwable tr, String format, Object... args) {
+ if (isLoggable(tag, WARN)) {
+ return VvmLog.w(tag, String.format(format, args), tr);
+ }
+ return 0;
+ }
+
+ /**
+ * Send a {@link #ERROR} log message.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param format the format string (see {@link java.util.Formatter#format})
+ * @param args
+ * the list of arguments passed to the formatter. If there are
+ * more arguments than required by {@code format},
+ * additional arguments are ignored.
+ */
+ public static int e(String tag, String format, Object... args) {
+ if (isLoggable(tag, ERROR)) {
+ return VvmLog.e(tag, String.format(format, args));
+ }
+ return 0;
+ }
+
+ /**
+ * Send a {@link #ERROR} log message.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param tr An exception to log
+ * @param format the format string (see {@link java.util.Formatter#format})
+ * @param args
+ * the list of arguments passed to the formatter. If there are
+ * more arguments than required by {@code format},
+ * additional arguments are ignored.
+ */
+ public static int e(String tag, Throwable tr, String format, Object... args) {
+ if (isLoggable(tag, ERROR)) {
+ return VvmLog.e(tag, String.format(format, args), tr);
+ }
+ return 0;
+ }
+
+ /**
+ * What a Terrible Failure: Report a condition that should never happen.
+ * The error will always be logged at level ASSERT with the call stack.
+ * Depending on system configuration, a report may be added to the
+ * {@link android.os.DropBoxManager} and/or the process may be terminated
+ * immediately with an error dialog.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param format the format string (see {@link java.util.Formatter#format})
+ * @param args
+ * the list of arguments passed to the formatter. If there are
+ * more arguments than required by {@code format},
+ * additional arguments are ignored.
+ */
+ public static int wtf(String tag, String format, Object... args) {
+ return VvmLog.wtf(tag, String.format(format, args), new Error());
+ }
+
+ /**
+ * What a Terrible Failure: Report a condition that should never happen.
+ * The error will always be logged at level ASSERT with the call stack.
+ * Depending on system configuration, a report may be added to the
+ * {@link android.os.DropBoxManager} and/or the process may be terminated
+ * immediately with an error dialog.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param tr An exception to log
+ * @param format the format string (see {@link java.util.Formatter#format})
+ * @param args
+ * the list of arguments passed to the formatter. If there are
+ * more arguments than required by {@code format},
+ * additional arguments are ignored.
+ */
+ public static int wtf(String tag, Throwable tr, String format, Object... args) {
+ return VvmLog.wtf(tag, String.format(format, args), tr);
+ }
+
+
+ /**
+ * Try to make a date MIME(RFC 2822/5322)-compliant.
+ *
+ * It fixes:
+ * - "Thu, 10 Dec 09 15:08:08 GMT-0700" to "Thu, 10 Dec 09 15:08:08 -0700"
+ * (4 digit zone value can't be preceded by "GMT")
+ * We got a report saying eBay sends a date in this format
+ */
+ public static String cleanUpMimeDate(String date) {
+ if (TextUtils.isEmpty(date)) {
+ return date;
+ }
+ date = DATE_CLEANUP_PATTERN_WRONG_TIMEZONE.matcher(date).replaceFirst("$1");
+ return date;
+ }
+
+
+ public static String byteToHex(int b) {
+ return byteToHex(new StringBuilder(), b).toString();
+ }
+
+ public static StringBuilder byteToHex(StringBuilder sb, int b) {
+ b &= 0xFF;
+ sb.append("0123456789ABCDEF".charAt(b >> 4));
+ sb.append("0123456789ABCDEF".charAt(b & 0xF));
+ return sb;
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/mail/utils/Utility.java b/java/com/android/voicemailomtp/mail/utils/Utility.java
new file mode 100644
index 000000000..c7286fa64
--- /dev/null
+++ b/java/com/android/voicemailomtp/mail/utils/Utility.java
@@ -0,0 +1,80 @@
+/**
+ * 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.voicemailomtp.mail.utils;
+
+import java.io.ByteArrayInputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+
+/**
+ * Simple utility methods used in email functions.
+ */
+public class Utility {
+ public static final Charset ASCII = Charset.forName("US-ASCII");
+
+ public static final String[] EMPTY_STRINGS = new String[0];
+
+ /**
+ * Returns a concatenated string containing the output of every Object's
+ * toString() method, each separated by the given separator character.
+ */
+ public static String combine(Object[] parts, char separator) {
+ if (parts == null) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < parts.length; i++) {
+ sb.append(parts[i].toString());
+ if (i < parts.length - 1) {
+ sb.append(separator);
+ }
+ }
+ return sb.toString();
+ }
+
+ /** Converts a String to ASCII bytes */
+ public static byte[] toAscii(String s) {
+ return encode(ASCII, s);
+ }
+
+ /** Builds a String from ASCII bytes */
+ public static String fromAscii(byte[] b) {
+ return decode(ASCII, b);
+ }
+
+ private static byte[] encode(Charset charset, String s) {
+ if (s == null) {
+ return null;
+ }
+ final ByteBuffer buffer = charset.encode(CharBuffer.wrap(s));
+ final byte[] bytes = new byte[buffer.limit()];
+ buffer.get(bytes);
+ return bytes;
+ }
+
+ private static String decode(Charset charset, byte[] b) {
+ if (b == null) {
+ return null;
+ }
+ final CharBuffer cb = charset.decode(ByteBuffer.wrap(b));
+ return new String(cb.array(), 0, cb.length());
+ }
+
+ public static ByteArrayInputStream streamFromAsciiString(String ascii) {
+ return new ByteArrayInputStream(toAscii(ascii));
+ }
+}
diff --git a/java/com/android/voicemailomtp/permissions.xml b/java/com/android/voicemailomtp/permissions.xml
new file mode 100644
index 000000000..9326d803a
--- /dev/null
+++ b/java/com/android/voicemailomtp/permissions.xml
@@ -0,0 +1,21 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.voicemailomtp">
+
+ <uses-sdk
+ android:minSdkVersion="23"
+ android:targetSdkVersion="25"/>
+
+ <!-- Applications using this module should merge these permissions using android_manifest_merge -->
+
+ <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
+ <uses-permission android:name="com.android.voicemail.permission.WRITE_VOICEMAIL" />
+ <uses-permission android:name="com.android.voicemail.permission.READ_VOICEMAIL" />
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.SEND_SMS"/>
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+
+ <application/>
+</manifest>
diff --git a/java/com/android/voicemailomtp/protocol/CvvmProtocol.java b/java/com/android/voicemailomtp/protocol/CvvmProtocol.java
new file mode 100644
index 000000000..48ed99709
--- /dev/null
+++ b/java/com/android/voicemailomtp/protocol/CvvmProtocol.java
@@ -0,0 +1,59 @@
+/*
+ * 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.voicemailomtp.protocol;
+
+import android.content.Context;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.SmsManager;
+
+import com.android.voicemailomtp.OmtpConstants;
+import com.android.voicemailomtp.sms.OmtpCvvmMessageSender;
+import com.android.voicemailomtp.sms.OmtpMessageSender;
+
+/**
+ * A flavor of OMTP protocol with a different mobile originated (MO) format
+ *
+ * Used by carriers such as T-Mobile
+ */
+public class CvvmProtocol extends VisualVoicemailProtocol {
+
+ private static String IMAP_CHANGE_TUI_PWD_FORMAT = "CHANGE_TUI_PWD PWD=%1$s OLD_PWD=%2$s";
+ private static String IMAP_CHANGE_VM_LANG_FORMAT = "CHANGE_VM_LANG Lang=%1$s";
+ private static String IMAP_CLOSE_NUT = "CLOSE_NUT";
+
+ @Override
+ public OmtpMessageSender createMessageSender(Context context,
+ PhoneAccountHandle phoneAccountHandle, short applicationPort,
+ String destinationNumber) {
+ return new OmtpCvvmMessageSender(context, phoneAccountHandle, applicationPort,
+ destinationNumber);
+ }
+
+ @Override
+ public String getCommand(String command) {
+ if (command == OmtpConstants.IMAP_CHANGE_TUI_PWD_FORMAT) {
+ return IMAP_CHANGE_TUI_PWD_FORMAT;
+ }
+ if (command == OmtpConstants.IMAP_CLOSE_NUT) {
+ return IMAP_CLOSE_NUT;
+ }
+ if (command == OmtpConstants.IMAP_CHANGE_VM_LANG_FORMAT) {
+ return IMAP_CHANGE_VM_LANG_FORMAT;
+ }
+ return super.getCommand(command);
+ }
+}
diff --git a/java/com/android/voicemailomtp/protocol/OmtpProtocol.java b/java/com/android/voicemailomtp/protocol/OmtpProtocol.java
new file mode 100644
index 000000000..d88a23285
--- /dev/null
+++ b/java/com/android/voicemailomtp/protocol/OmtpProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * 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.voicemailomtp.protocol;
+
+import android.content.Context;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.SmsManager;
+
+import com.android.voicemailomtp.OmtpConstants;
+import com.android.voicemailomtp.sms.OmtpMessageSender;
+import com.android.voicemailomtp.sms.OmtpStandardMessageSender;
+
+public class OmtpProtocol extends VisualVoicemailProtocol {
+
+ @Override
+ public OmtpMessageSender createMessageSender(Context context,
+ PhoneAccountHandle phoneAccountHandle, short applicationPort,
+ String destinationNumber) {
+ return new OmtpStandardMessageSender(context, phoneAccountHandle, applicationPort,
+ destinationNumber,
+ null, OmtpConstants.PROTOCOL_VERSION1_1, null);
+ }
+}
diff --git a/java/com/android/voicemailomtp/protocol/ProtocolHelper.java b/java/com/android/voicemailomtp/protocol/ProtocolHelper.java
new file mode 100644
index 000000000..4fca199bf
--- /dev/null
+++ b/java/com/android/voicemailomtp/protocol/ProtocolHelper.java
@@ -0,0 +1,43 @@
+/*
+ * 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.voicemailomtp.protocol;
+
+import android.telephony.SmsManager;
+import android.text.TextUtils;
+
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.VvmLog;
+import com.android.voicemailomtp.sms.OmtpMessageSender;
+
+public class ProtocolHelper {
+
+ private static final String TAG = "ProtocolHelper";
+
+ public static OmtpMessageSender getMessageSender(VisualVoicemailProtocol protocol,
+ OmtpVvmCarrierConfigHelper config) {
+
+ int applicationPort = config.getApplicationPort();
+ String destinationNumber = config.getDestinationNumber();
+ if (TextUtils.isEmpty(destinationNumber)) {
+ VvmLog.w(TAG, "No destination number for this carrier.");
+ return null;
+ }
+
+ return protocol.createMessageSender(config.getContext(), config.getPhoneAccountHandle(),
+ (short) applicationPort, destinationNumber);
+ }
+}
diff --git a/java/com/android/voicemailomtp/protocol/VisualVoicemailProtocol.java b/java/com/android/voicemailomtp/protocol/VisualVoicemailProtocol.java
new file mode 100644
index 000000000..9ff2ed167
--- /dev/null
+++ b/java/com/android/voicemailomtp/protocol/VisualVoicemailProtocol.java
@@ -0,0 +1,100 @@
+/*
+ * 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.voicemailomtp.protocol;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.SmsManager;
+import com.android.voicemailomtp.ActivationTask;
+import com.android.voicemailomtp.DefaultOmtpEventHandler;
+import com.android.voicemailomtp.OmtpEvents;
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.VoicemailStatus;
+import com.android.voicemailomtp.sms.OmtpMessageSender;
+import com.android.voicemailomtp.sms.StatusMessage;
+
+public abstract class VisualVoicemailProtocol {
+
+ /**
+ * Activation should cause the carrier to respond with a STATUS SMS.
+ */
+ public void startActivation(OmtpVvmCarrierConfigHelper config, PendingIntent sentIntent) {
+ OmtpMessageSender messageSender = ProtocolHelper.getMessageSender(this, config);
+ if (messageSender != null) {
+ messageSender.requestVvmActivation(sentIntent);
+ }
+ }
+
+ public void startDeactivation(OmtpVvmCarrierConfigHelper config) {
+ OmtpMessageSender messageSender = ProtocolHelper.getMessageSender(this, config);
+ if (messageSender != null) {
+ messageSender.requestVvmDeactivation(null);
+ }
+ }
+
+ public boolean supportsProvisioning() {
+ return false;
+ }
+
+ public void startProvisioning(ActivationTask task, PhoneAccountHandle handle,
+ OmtpVvmCarrierConfigHelper config, VoicemailStatus.Editor editor, StatusMessage message,
+ Bundle data) {
+ // Do nothing
+ }
+
+ public void requestStatus(OmtpVvmCarrierConfigHelper config,
+ @Nullable PendingIntent sentIntent) {
+ OmtpMessageSender messageSender = ProtocolHelper.getMessageSender(this, config);
+ if (messageSender != null) {
+ messageSender.requestVvmStatus(sentIntent);
+ }
+ }
+
+ public abstract OmtpMessageSender createMessageSender(Context context,
+ PhoneAccountHandle phoneAccountHandle,
+ short applicationPort, String destinationNumber);
+
+ /**
+ * Translate an OMTP IMAP command to the protocol specific one. For example, changing the TUI
+ * password on OMTP is XCHANGE_TUI_PWD, but on CVVM and VVM3 it is CHANGE_TUI_PWD.
+ *
+ * @param command A String command in {@link com.android.voicemailomtp.OmtpConstants}, the exact
+ * instance should be used instead of its' value.
+ * @returns Translated command, or {@code null} if not available in this protocol
+ */
+ public String getCommand(String command) {
+ return command;
+ }
+
+ public void handleEvent(Context context, OmtpVvmCarrierConfigHelper config,
+ VoicemailStatus.Editor status, OmtpEvents event) {
+ DefaultOmtpEventHandler.handleEvent(context, config, status, event);
+ }
+
+ /**
+ * Given an VVM SMS with an unknown {@code event}, let the protocol attempt to translate it into
+ * an equivalent STATUS SMS. Returns {@code null} if it cannot be translated.
+ */
+ @Nullable
+ public Bundle translateStatusSmsBundle(OmtpVvmCarrierConfigHelper config, String event,
+ Bundle data) {
+ return null;
+ }
+}
diff --git a/java/com/android/voicemailomtp/protocol/VisualVoicemailProtocolFactory.java b/java/com/android/voicemailomtp/protocol/VisualVoicemailProtocolFactory.java
new file mode 100644
index 000000000..b74f503c6
--- /dev/null
+++ b/java/com/android/voicemailomtp/protocol/VisualVoicemailProtocolFactory.java
@@ -0,0 +1,47 @@
+/*
+ * 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.voicemailomtp.protocol;
+
+import android.content.res.Resources;
+import android.support.annotation.Nullable;
+import android.telephony.TelephonyManager;
+import com.android.voicemailomtp.VvmLog;
+
+public class VisualVoicemailProtocolFactory {
+
+ private static final String TAG = "VvmProtocolFactory";
+
+ private static final String VVM_TYPE_VVM3 = "vvm_type_vvm3";
+
+ @Nullable
+ public static VisualVoicemailProtocol create(Resources resources, String type) {
+ if (type == null) {
+ return null;
+ }
+ switch (type) {
+ case TelephonyManager.VVM_TYPE_OMTP:
+ return new OmtpProtocol();
+ case TelephonyManager.VVM_TYPE_CVVM:
+ return new CvvmProtocol();
+ case VVM_TYPE_VVM3:
+ return new Vvm3Protocol();
+ default:
+ VvmLog.e(TAG, "Unexpected visual voicemail type: " + type);
+ }
+ return null;
+ }
+}
diff --git a/java/com/android/voicemailomtp/protocol/Vvm3EventHandler.java b/java/com/android/voicemailomtp/protocol/Vvm3EventHandler.java
new file mode 100644
index 000000000..72646386c
--- /dev/null
+++ b/java/com/android/voicemailomtp/protocol/Vvm3EventHandler.java
@@ -0,0 +1,271 @@
+/*
+ * 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.voicemailomtp.protocol;
+
+import android.content.Context;
+import android.support.annotation.IntDef;
+import android.util.Log;
+import com.android.voicemailomtp.DefaultOmtpEventHandler;
+import com.android.voicemailomtp.OmtpEvents;
+import com.android.voicemailomtp.OmtpEvents.Type;
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.VoicemailStatus;
+import com.android.voicemailomtp.settings.VoicemailChangePinActivity;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Handles {@link OmtpEvents} when {@link Vvm3Protocol} is being used. This handler writes custom
+ * error codes into the voicemail status table so support on the dialer side is required.
+ *
+ * TODO(b/29577838) disable VVM3 by default so support on system dialer can be ensured.
+ */
+public class Vvm3EventHandler {
+
+ private static final String TAG = "Vvm3EventHandler";
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({VMS_DNS_FAILURE, VMG_DNS_FAILURE, SPG_DNS_FAILURE, VMS_NO_CELLULAR, VMG_NO_CELLULAR,
+ SPG_NO_CELLULAR, VMS_TIMEOUT, VMG_TIMEOUT, STATUS_SMS_TIMEOUT, SUBSCRIBER_BLOCKED,
+ UNKNOWN_USER, UNKNOWN_DEVICE, INVALID_PASSWORD, MAILBOX_NOT_INITIALIZED,
+ SERVICE_NOT_PROVISIONED, SERVICE_NOT_ACTIVATED, USER_BLOCKED, IMAP_GETQUOTA_ERROR,
+ IMAP_SELECT_ERROR, IMAP_ERROR, VMG_INTERNAL_ERROR, VMG_DB_ERROR,
+ VMG_COMMUNICATION_ERROR, SPG_URL_NOT_FOUND, VMG_UNKNOWN_ERROR, PIN_NOT_SET})
+ public @interface ErrorCode {
+
+ }
+
+ public static final int VMS_DNS_FAILURE = -9001;
+ public static final int VMG_DNS_FAILURE = -9002;
+ public static final int SPG_DNS_FAILURE = -9003;
+ public static final int VMS_NO_CELLULAR = -9004;
+ public static final int VMG_NO_CELLULAR = -9005;
+ public static final int SPG_NO_CELLULAR = -9006;
+ public static final int VMS_TIMEOUT = -9007;
+ public static final int VMG_TIMEOUT = -9008;
+ public static final int STATUS_SMS_TIMEOUT = -9009;
+
+ public static final int SUBSCRIBER_BLOCKED = -9990;
+ public static final int UNKNOWN_USER = -9991;
+ public static final int UNKNOWN_DEVICE = -9992;
+ public static final int INVALID_PASSWORD = -9993;
+ public static final int MAILBOX_NOT_INITIALIZED = -9994;
+ public static final int SERVICE_NOT_PROVISIONED = -9995;
+ public static final int SERVICE_NOT_ACTIVATED = -9996;
+ public static final int USER_BLOCKED = -9998;
+ public static final int IMAP_GETQUOTA_ERROR = -9997;
+ public static final int IMAP_SELECT_ERROR = -9989;
+ public static final int IMAP_ERROR = -9999;
+
+ public static final int VMG_INTERNAL_ERROR = -101;
+ public static final int VMG_DB_ERROR = -102;
+ public static final int VMG_COMMUNICATION_ERROR = -103;
+ public static final int SPG_URL_NOT_FOUND = -301;
+
+ // Non VVM3 codes:
+ public static final int VMG_UNKNOWN_ERROR = -1;
+ public static final int PIN_NOT_SET = -100;
+ // STATUS SMS returned st=U and rc!=2. The user cannot be provisioned and must contact customer
+ // support.
+ public static final int SUBSCRIBER_UNKNOWN = -99;
+
+
+ public static void handleEvent(Context context, OmtpVvmCarrierConfigHelper config,
+ VoicemailStatus.Editor status, OmtpEvents event) {
+ boolean handled = false;
+ switch (event.getType()) {
+ case Type.CONFIGURATION:
+ handled = handleConfigurationEvent(context, status, event);
+ break;
+ case Type.DATA_CHANNEL:
+ handled = handleDataChannelEvent(status, event);
+ break;
+ case Type.NOTIFICATION_CHANNEL:
+ handled = handleNotificationChannelEvent(status, event);
+ break;
+ case Type.OTHER:
+ handled = handleOtherEvent(status, event);
+ break;
+ default:
+ Log.wtf(TAG, "invalid event type " + event.getType() + " for " + event);
+ }
+ if (!handled) {
+ DefaultOmtpEventHandler.handleEvent(context, config, status, event);
+ }
+ }
+
+ private static boolean handleConfigurationEvent(Context context, VoicemailStatus.Editor status,
+ OmtpEvents event) {
+ switch (event) {
+ case CONFIG_REQUEST_STATUS_SUCCESS:
+ if (status.getPhoneAccountHandle() == null) {
+ // This should never happen.
+ Log.e(TAG, "status editor has null phone account handle");
+ return true;
+ }
+
+ if (!VoicemailChangePinActivity
+ .isDefaultOldPinSet(context, status.getPhoneAccountHandle())) {
+ return false;
+ } else {
+ postError(status, PIN_NOT_SET);
+ }
+ break;
+ case CONFIG_DEFAULT_PIN_REPLACED:
+ postError(status, PIN_NOT_SET);
+ break;
+ case CONFIG_STATUS_SMS_TIME_OUT:
+ postError(status, STATUS_SMS_TIMEOUT);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean handleDataChannelEvent(VoicemailStatus.Editor status, OmtpEvents event) {
+ switch (event) {
+ case DATA_NO_CONNECTION:
+ case DATA_NO_CONNECTION_CELLULAR_REQUIRED:
+ case DATA_ALL_SOCKET_CONNECTION_FAILED:
+ postError(status, VMS_NO_CELLULAR);
+ break;
+ case DATA_SSL_INVALID_HOST_NAME:
+ case DATA_CANNOT_ESTABLISH_SSL_SESSION:
+ case DATA_IOE_ON_OPEN:
+ postError(status, VMS_TIMEOUT);
+ break;
+ case DATA_CANNOT_RESOLVE_HOST_ON_NETWORK:
+ postError(status, VMS_DNS_FAILURE);
+ break;
+ case DATA_BAD_IMAP_CREDENTIAL:
+ postError(status, IMAP_ERROR);
+ break;
+ case DATA_AUTH_UNKNOWN_USER:
+ postError(status, UNKNOWN_USER);
+ break;
+ case DATA_AUTH_UNKNOWN_DEVICE:
+ postError(status, UNKNOWN_DEVICE);
+ break;
+ case DATA_AUTH_INVALID_PASSWORD:
+ postError(status, INVALID_PASSWORD);
+ break;
+ case DATA_AUTH_MAILBOX_NOT_INITIALIZED:
+ postError(status, MAILBOX_NOT_INITIALIZED);
+ break;
+ case DATA_AUTH_SERVICE_NOT_PROVISIONED:
+ postError(status, SERVICE_NOT_PROVISIONED);
+ break;
+ case DATA_AUTH_SERVICE_NOT_ACTIVATED:
+ postError(status, SERVICE_NOT_ACTIVATED);
+ break;
+ case DATA_AUTH_USER_IS_BLOCKED:
+ postError(status, USER_BLOCKED);
+ break;
+ case DATA_REJECTED_SERVER_RESPONSE:
+ case DATA_INVALID_INITIAL_SERVER_RESPONSE:
+ case DATA_SSL_EXCEPTION:
+ postError(status, IMAP_ERROR);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean handleNotificationChannelEvent(VoicemailStatus.Editor status,
+ OmtpEvents event) {
+ return false;
+ }
+
+ private static boolean handleOtherEvent(VoicemailStatus.Editor status,
+ OmtpEvents event) {
+ switch (event) {
+ case VVM3_NEW_USER_SETUP_FAILED:
+ postError(status, MAILBOX_NOT_INITIALIZED);
+ break;
+ case VVM3_VMG_DNS_FAILURE:
+ postError(status, VMG_DNS_FAILURE);
+ break;
+ case VVM3_SPG_DNS_FAILURE:
+ postError(status, SPG_DNS_FAILURE);
+ break;
+ case VVM3_VMG_CONNECTION_FAILED:
+ postError(status, VMG_NO_CELLULAR);
+ break;
+ case VVM3_SPG_CONNECTION_FAILED:
+ postError(status, SPG_NO_CELLULAR);
+ break;
+ case VVM3_VMG_TIMEOUT:
+ postError(status, VMG_TIMEOUT);
+ break;
+ case VVM3_SUBSCRIBER_PROVISIONED:
+ postError(status, SERVICE_NOT_ACTIVATED);
+ break;
+ case VVM3_SUBSCRIBER_BLOCKED:
+ postError(status, SUBSCRIBER_BLOCKED);
+ break;
+ case VVM3_SUBSCRIBER_UNKNOWN:
+ postError(status, SUBSCRIBER_UNKNOWN);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ private static void postError(VoicemailStatus.Editor editor, @ErrorCode int errorCode) {
+ switch (errorCode) {
+ case VMG_DNS_FAILURE:
+ case SPG_DNS_FAILURE:
+ case VMG_NO_CELLULAR:
+ case SPG_NO_CELLULAR:
+ case VMG_TIMEOUT:
+ case SUBSCRIBER_BLOCKED:
+ case UNKNOWN_USER:
+ case UNKNOWN_DEVICE:
+ case INVALID_PASSWORD:
+ case MAILBOX_NOT_INITIALIZED:
+ case SERVICE_NOT_PROVISIONED:
+ case SERVICE_NOT_ACTIVATED:
+ case USER_BLOCKED:
+ case VMG_UNKNOWN_ERROR:
+ case SPG_URL_NOT_FOUND:
+ case VMG_INTERNAL_ERROR:
+ case VMG_DB_ERROR:
+ case VMG_COMMUNICATION_ERROR:
+ case PIN_NOT_SET:
+ case SUBSCRIBER_UNKNOWN:
+ editor.setConfigurationState(errorCode);
+ break;
+ case VMS_NO_CELLULAR:
+ case VMS_DNS_FAILURE:
+ case VMS_TIMEOUT:
+ case IMAP_GETQUOTA_ERROR:
+ case IMAP_SELECT_ERROR:
+ case IMAP_ERROR:
+ editor.setDataChannelState(errorCode);
+ break;
+ case STATUS_SMS_TIMEOUT:
+ editor.setNotificationChannelState(errorCode);
+ break;
+ default:
+ Log.wtf(TAG, "unknown error code: " + errorCode);
+ }
+ editor.apply();
+ }
+}
diff --git a/java/com/android/voicemailomtp/protocol/Vvm3Protocol.java b/java/com/android/voicemailomtp/protocol/Vvm3Protocol.java
new file mode 100644
index 000000000..652d1010a
--- /dev/null
+++ b/java/com/android/voicemailomtp/protocol/Vvm3Protocol.java
@@ -0,0 +1,301 @@
+/*
+ * 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.voicemailomtp.protocol;
+
+import android.annotation.TargetApi;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.net.Network;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import android.text.TextUtils;
+import com.android.voicemailomtp.ActivationTask;
+import com.android.voicemailomtp.OmtpConstants;
+import com.android.voicemailomtp.OmtpEvents;
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.VisualVoicemailPreferences;
+import com.android.voicemailomtp.VoicemailStatus;
+import com.android.voicemailomtp.VvmLog;
+import com.android.voicemailomtp.imap.ImapHelper;
+import com.android.voicemailomtp.imap.ImapHelper.InitializingException;
+import com.android.voicemailomtp.mail.MessagingException;
+import com.android.voicemailomtp.settings.VisualVoicemailSettingsUtil;
+import com.android.voicemailomtp.settings.VoicemailChangePinActivity;
+import com.android.voicemailomtp.sms.OmtpMessageSender;
+import com.android.voicemailomtp.sms.StatusMessage;
+import com.android.voicemailomtp.sms.Vvm3MessageSender;
+import com.android.voicemailomtp.sync.VvmNetworkRequest;
+import com.android.voicemailomtp.sync.VvmNetworkRequest.NetworkWrapper;
+import com.android.voicemailomtp.sync.VvmNetworkRequest.RequestFailedException;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.Locale;
+
+/**
+ * A flavor of OMTP protocol with a different provisioning process
+ *
+ * <p>Used by carriers such as Verizon Wireless
+ */
+@TargetApi(VERSION_CODES.CUR_DEVELOPMENT)
+public class Vvm3Protocol extends VisualVoicemailProtocol {
+
+ private static final String TAG = "Vvm3Protocol";
+
+ private static final String SMS_EVENT_UNRECOGNIZED = "UNRECOGNIZED";
+ private static final String SMS_EVENT_UNRECOGNIZED_CMD = "cmd";
+ private static final String SMS_EVENT_UNRECOGNIZED_STATUS = "STATUS";
+ private static final String DEFAULT_VMG_URL_KEY = "default_vmg_url";
+
+ private static final String IMAP_CHANGE_TUI_PWD_FORMAT = "CHANGE_TUI_PWD PWD=%1$s OLD_PWD=%2$s";
+ private static final String IMAP_CHANGE_VM_LANG_FORMAT = "CHANGE_VM_LANG Lang=%1$s";
+ private static final String IMAP_CLOSE_NUT = "CLOSE_NUT";
+
+ private static final String ISO639_Spanish = "es";
+
+ /**
+ * For VVM3, if the STATUS SMS returns {@link StatusMessage#getProvisioningStatus()} of {@link
+ * OmtpConstants#SUBSCRIBER_UNKNOWN} and {@link StatusMessage#getReturnCode()} of this value,
+ * the user can self-provision visual voicemail service. For other response codes, the user must
+ * contact customer support to resolve the issue.
+ */
+ private static final String VVM3_UNKNOWN_SUBSCRIBER_CAN_SUBSCRIBE_RESPONSE_CODE = "2";
+
+ // Default prompt level when using the telephone user interface.
+ // Standard prompt when the user call into the voicemail, and no prompts when someone else is
+ // leaving a voicemail.
+ private static final String VVM3_VM_LANGUAGE_ENGLISH_STANDARD_NO_GUEST_PROMPTS = "5";
+ private static final String VVM3_VM_LANGUAGE_SPANISH_STANDARD_NO_GUEST_PROMPTS = "6";
+
+ private static final int DEFAULT_PIN_LENGTH = 6;
+
+ @Override
+ public void startActivation(OmtpVvmCarrierConfigHelper config,
+ @Nullable PendingIntent sentIntent) {
+ // VVM3 does not support activation SMS.
+ // Send a status request which will start the provisioning process if the user is not
+ // provisioned.
+ VvmLog.i(TAG, "Activating");
+ config.requestStatus(sentIntent);
+ }
+
+ @Override
+ public void startDeactivation(OmtpVvmCarrierConfigHelper config) {
+ // VVM3 does not support deactivation.
+ // do nothing.
+ }
+
+ @Override
+ public boolean supportsProvisioning() {
+ return true;
+ }
+
+ @Override
+ public void startProvisioning(ActivationTask task, PhoneAccountHandle phoneAccountHandle,
+ OmtpVvmCarrierConfigHelper config, VoicemailStatus.Editor status, StatusMessage message,
+ Bundle data) {
+ VvmLog.i(TAG, "start vvm3 provisioning");
+ if (OmtpConstants.SUBSCRIBER_UNKNOWN.equals(message.getProvisioningStatus())) {
+ VvmLog.i(TAG, "Provisioning status: Unknown");
+ if (VVM3_UNKNOWN_SUBSCRIBER_CAN_SUBSCRIBE_RESPONSE_CODE
+ .equals(message.getReturnCode())) {
+ VvmLog.i(TAG, "Self provisioning available, subscribing");
+ new Vvm3Subscriber(task, phoneAccountHandle, config, status, data).subscribe();
+ } else {
+ config.handleEvent(status, OmtpEvents.VVM3_SUBSCRIBER_UNKNOWN);
+ }
+ } else if (OmtpConstants.SUBSCRIBER_NEW.equals(message.getProvisioningStatus())) {
+ VvmLog.i(TAG, "setting up new user");
+ // Save the IMAP credentials in preferences so they are persistent and can be retrieved.
+ VisualVoicemailPreferences prefs =
+ new VisualVoicemailPreferences(config.getContext(), phoneAccountHandle);
+ message.putStatus(prefs.edit()).apply();
+
+ startProvisionNewUser(task, phoneAccountHandle, config, status, message);
+ } else if (OmtpConstants.SUBSCRIBER_PROVISIONED.equals(message.getProvisioningStatus())) {
+ VvmLog.i(TAG, "User provisioned but not activated, disabling VVM");
+ VisualVoicemailSettingsUtil
+ .setEnabled(config.getContext(), phoneAccountHandle, false);
+ } else if (OmtpConstants.SUBSCRIBER_BLOCKED.equals(message.getProvisioningStatus())) {
+ VvmLog.i(TAG, "User blocked");
+ config.handleEvent(status, OmtpEvents.VVM3_SUBSCRIBER_BLOCKED);
+ }
+ }
+
+ @Override
+ public OmtpMessageSender createMessageSender(Context context,
+ PhoneAccountHandle phoneAccountHandle, short applicationPort,
+ String destinationNumber) {
+ return new Vvm3MessageSender(context, phoneAccountHandle, applicationPort,
+ destinationNumber);
+ }
+
+ @Override
+ public void handleEvent(Context context, OmtpVvmCarrierConfigHelper config,
+ VoicemailStatus.Editor status, OmtpEvents event) {
+ Vvm3EventHandler.handleEvent(context, config, status, event);
+ }
+
+ @Override
+ public String getCommand(String command) {
+ if (command == OmtpConstants.IMAP_CHANGE_TUI_PWD_FORMAT) {
+ return IMAP_CHANGE_TUI_PWD_FORMAT;
+ }
+ if (command == OmtpConstants.IMAP_CLOSE_NUT) {
+ return IMAP_CLOSE_NUT;
+ }
+ if (command == OmtpConstants.IMAP_CHANGE_VM_LANG_FORMAT) {
+ return IMAP_CHANGE_VM_LANG_FORMAT;
+ }
+ return super.getCommand(command);
+ }
+
+ @Override
+ public Bundle translateStatusSmsBundle(OmtpVvmCarrierConfigHelper config, String event,
+ Bundle data) {
+ // UNRECOGNIZED?cmd=STATUS is the response of a STATUS request when the user is provisioned
+ // with iPhone visual voicemail without VoLTE. Translate it into an unprovisioned status
+ // so provisioning can be done.
+ if (!SMS_EVENT_UNRECOGNIZED.equals(event)) {
+ return null;
+ }
+ if (!SMS_EVENT_UNRECOGNIZED_STATUS.equals(data.getString(SMS_EVENT_UNRECOGNIZED_CMD))) {
+ return null;
+ }
+ Bundle bundle = new Bundle();
+ bundle.putString(OmtpConstants.PROVISIONING_STATUS, OmtpConstants.SUBSCRIBER_UNKNOWN);
+ bundle.putString(OmtpConstants.RETURN_CODE,
+ VVM3_UNKNOWN_SUBSCRIBER_CAN_SUBSCRIBE_RESPONSE_CODE);
+ String vmgUrl = config.getString(DEFAULT_VMG_URL_KEY);
+ if (TextUtils.isEmpty(vmgUrl)) {
+ VvmLog.e(TAG, "Unable to translate STATUS SMS: VMG URL is not set in config");
+ return null;
+ }
+ bundle.putString(Vvm3Subscriber.VMG_URL_KEY, vmgUrl);
+ VvmLog.i(TAG, "UNRECOGNIZED?cmd=STATUS translated into unprovisioned STATUS SMS");
+ return bundle;
+ }
+
+ private void startProvisionNewUser(ActivationTask task, PhoneAccountHandle phoneAccountHandle,
+ OmtpVvmCarrierConfigHelper config, VoicemailStatus.Editor status,
+ StatusMessage message) {
+ try (NetworkWrapper wrapper = VvmNetworkRequest
+ .getNetwork(config, phoneAccountHandle, status)) {
+ Network network = wrapper.get();
+
+ VvmLog.i(TAG, "new user: network available");
+ try (ImapHelper helper = new ImapHelper(config.getContext(), phoneAccountHandle,
+ network, status)) {
+ // VVM3 has inconsistent error language code to OMTP. Just issue a raw command
+ // here.
+ // TODO(b/29082671): use LocaleList
+ if (Locale.getDefault().getLanguage()
+ .equals(new Locale(ISO639_Spanish).getLanguage())) {
+ // Spanish
+ helper.changeVoicemailTuiLanguage(
+ VVM3_VM_LANGUAGE_SPANISH_STANDARD_NO_GUEST_PROMPTS);
+ } else {
+ // English
+ helper.changeVoicemailTuiLanguage(
+ VVM3_VM_LANGUAGE_ENGLISH_STANDARD_NO_GUEST_PROMPTS);
+ }
+ VvmLog.i(TAG, "new user: language set");
+
+ if (setPin(config.getContext(), phoneAccountHandle, helper, message)) {
+ // Only close new user tutorial if the PIN has been changed.
+ helper.closeNewUserTutorial();
+ VvmLog.i(TAG, "new user: NUT closed");
+
+ config.requestStatus(null);
+ }
+ } catch (InitializingException | MessagingException | IOException e) {
+ config.handleEvent(status, OmtpEvents.VVM3_NEW_USER_SETUP_FAILED);
+ task.fail();
+ VvmLog.e(TAG, e.toString());
+ }
+ } catch (RequestFailedException e) {
+ config.handleEvent(status, OmtpEvents.DATA_NO_CONNECTION_CELLULAR_REQUIRED);
+ task.fail();
+ }
+
+ }
+
+
+ private static boolean setPin(Context context, PhoneAccountHandle phoneAccountHandle,
+ ImapHelper helper, StatusMessage message)
+ throws IOException, MessagingException {
+ String defaultPin = getDefaultPin(message);
+ if (defaultPin == null) {
+ VvmLog.i(TAG, "cannot generate default PIN");
+ return false;
+ }
+
+ if (VoicemailChangePinActivity.isDefaultOldPinSet(context, phoneAccountHandle)) {
+ // The pin was already set
+ VvmLog.i(TAG, "PIN already set");
+ return true;
+ }
+ String newPin = generatePin(getMinimumPinLength(context, phoneAccountHandle));
+ if (helper.changePin(defaultPin, newPin) == OmtpConstants.CHANGE_PIN_SUCCESS) {
+ VoicemailChangePinActivity.setDefaultOldPIN(context, phoneAccountHandle, newPin);
+ helper.handleEvent(OmtpEvents.CONFIG_DEFAULT_PIN_REPLACED);
+ }
+ VvmLog.i(TAG, "new user: PIN set");
+ return true;
+ }
+
+ @Nullable
+ private static String getDefaultPin(StatusMessage message) {
+ // The IMAP username is [phone number]@example.com
+ String username = message.getImapUserName();
+ try {
+ String number = username.substring(0, username.indexOf('@'));
+ if (number.length() < 4) {
+ VvmLog.e(TAG, "unable to extract number from IMAP username");
+ return null;
+ }
+ return "1" + number.substring(number.length() - 4);
+ } catch (StringIndexOutOfBoundsException e) {
+ VvmLog.e(TAG, "unable to extract number from IMAP username");
+ return null;
+ }
+
+ }
+
+ private static int getMinimumPinLength(Context context, PhoneAccountHandle phoneAccountHandle) {
+ VisualVoicemailPreferences preferences = new VisualVoicemailPreferences(context,
+ phoneAccountHandle);
+ // The OMTP pin length format is {min}-{max}
+ String[] lengths = preferences.getString(OmtpConstants.TUI_PASSWORD_LENGTH, "").split("-");
+ if (lengths.length == 2) {
+ try {
+ return Integer.parseInt(lengths[0]);
+ } catch (NumberFormatException e) {
+ return DEFAULT_PIN_LENGTH;
+ }
+ }
+ return DEFAULT_PIN_LENGTH;
+ }
+
+ private static String generatePin(int length) {
+ SecureRandom random = new SecureRandom();
+ return String.format(Locale.US, "%010d", Math.abs(random.nextLong()))
+ .substring(0, length);
+
+ }
+}
diff --git a/java/com/android/voicemailomtp/protocol/Vvm3Subscriber.java b/java/com/android/voicemailomtp/protocol/Vvm3Subscriber.java
new file mode 100644
index 000000000..0a4d792b2
--- /dev/null
+++ b/java/com/android/voicemailomtp/protocol/Vvm3Subscriber.java
@@ -0,0 +1,326 @@
+/*
+ * 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.voicemailomtp.protocol;
+
+import android.annotation.TargetApi;
+import android.net.Network;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.support.annotation.WorkerThread;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.TelephonyManager;
+import android.text.Html;
+import android.text.Spanned;
+import android.text.style.URLSpan;
+import android.util.ArrayMap;
+import com.android.voicemailomtp.ActivationTask;
+import com.android.voicemailomtp.Assert;
+import com.android.voicemailomtp.OmtpEvents;
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.VoicemailStatus;
+import com.android.voicemailomtp.VvmLog;
+import com.android.voicemailomtp.sync.VvmNetworkRequest;
+import com.android.voicemailomtp.sync.VvmNetworkRequest.NetworkWrapper;
+import com.android.voicemailomtp.sync.VvmNetworkRequest.RequestFailedException;
+import com.android.volley.AuthFailureError;
+import com.android.volley.Request;
+import com.android.volley.RequestQueue;
+import com.android.volley.toolbox.HurlStack;
+import com.android.volley.toolbox.RequestFuture;
+import com.android.volley.toolbox.StringRequest;
+import com.android.volley.toolbox.Volley;
+import java.io.IOException;
+import java.net.CookieHandler;
+import java.net.CookieManager;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Class to subscribe to basic VVM3 visual voicemail, for example, Verizon. Subscription is required
+ * when the user is unprovisioned. This could happen when the user is on a legacy service, or
+ * switched over from devices that used other type of visual voicemail.
+ *
+ * <p>The STATUS SMS will come with a URL to the voicemail management gateway. From it we can find
+ * the self provisioning gateway URL that we can modify voicemail services.
+ *
+ * <p>A request to the self provisioning gateway to activate basic visual voicemail will return us
+ * with a web page. If the user hasn't subscribe to it yet it will contain a link to confirm the
+ * subscription. This link should be clicked through cellular network, and have cookies enabled.
+ *
+ * <p>After the process is completed, the carrier should send us another STATUS SMS with a new or
+ * ready user.
+ */
+@TargetApi(VERSION_CODES.CUR_DEVELOPMENT)
+public class Vvm3Subscriber {
+
+ private static final String TAG = "Vvm3Subscriber";
+
+ private static final String OPERATION_GET_SPG_URL = "retrieveSPGURL";
+ private static final String SPG_URL_TAG = "spgurl";
+ private static final String TRANSACTION_ID_TAG = "transactionid";
+ //language=XML
+ private static final String VMG_XML_REQUEST_FORMAT = ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<VMGVVMRequest>"
+ + " <MessageHeader>"
+ + " <transactionid>%1$s</transactionid>"
+ + " </MessageHeader>"
+ + " <MessageBody>"
+ + " <mdn>%2$s</mdn>"
+ + " <operation>%3$s</operation>"
+ + " <source>Device</source>"
+ + " <devicemodel>%4$s</devicemodel>"
+ + " </MessageBody>"
+ + "</VMGVVMRequest>";
+
+ static final String VMG_URL_KEY = "vmg_url";
+
+ // Self provisioning POST key/values. VVM3 API 2.1.0 12.3
+ private static final String SPG_VZW_MDN_PARAM = "VZW_MDN";
+ private static final String SPG_VZW_SERVICE_PARAM = "VZW_SERVICE";
+ private static final String SPG_VZW_SERVICE_BASIC = "BVVM";
+ private static final String SPG_DEVICE_MODEL_PARAM = "DEVICE_MODEL";
+ // Value for all android device
+ private static final String SPG_DEVICE_MODEL_ANDROID = "DROID_4G";
+ private static final String SPG_APP_TOKEN_PARAM = "APP_TOKEN";
+ private static final String SPG_APP_TOKEN = "q8e3t5u2o1";
+ private static final String SPG_LANGUAGE_PARAM = "SPG_LANGUAGE_PARAM";
+ private static final String SPG_LANGUAGE_EN = "ENGLISH";
+
+ private static final String BASIC_SUBSCRIBE_LINK_TEXT = "Subscribe to Basic Visual Voice Mail";
+
+ private static final int REQUEST_TIMEOUT_SECONDS = 30;
+
+ private final ActivationTask mTask;
+ private final PhoneAccountHandle mHandle;
+ private final OmtpVvmCarrierConfigHelper mHelper;
+ private final VoicemailStatus.Editor mStatus;
+ private final Bundle mData;
+
+ private final String mNumber;
+
+ private RequestQueue mRequestQueue;
+
+ private static class ProvisioningException extends Exception {
+
+ public ProvisioningException(String message) {
+ super(message);
+ }
+ }
+
+ static {
+ // Set the default cookie handler to retain session data for the self provisioning gateway.
+ // Note; this is not ideal as it is application-wide, and can easily get clobbered.
+ // But it seems to be the preferred way to manage cookie for HttpURLConnection, and manually
+ // managing cookies will greatly increase complexity.
+ CookieManager cookieManager = new CookieManager();
+ CookieHandler.setDefault(cookieManager);
+ }
+
+ @WorkerThread
+ public Vvm3Subscriber(ActivationTask task, PhoneAccountHandle handle,
+ OmtpVvmCarrierConfigHelper helper, VoicemailStatus.Editor status, Bundle data) {
+ Assert.isNotMainThread();
+ mTask = task;
+ mHandle = handle;
+ mHelper = helper;
+ mStatus = status;
+ mData = data;
+
+ // Assuming getLine1Number() will work with VVM3. For unprovisioned users the IMAP username
+ // is not included in the status SMS, thus no other way to get the current phone number.
+ mNumber = mHelper.getContext().getSystemService(TelephonyManager.class)
+ .createForPhoneAccountHandle(mHandle).getLine1Number();
+ }
+
+ @WorkerThread
+ public void subscribe() {
+ Assert.isNotMainThread();
+ // Cellular data is required to subscribe.
+ // processSubscription() is called after network is available.
+ VvmLog.i(TAG, "Subscribing");
+
+ try (NetworkWrapper wrapper = VvmNetworkRequest.getNetwork(mHelper, mHandle, mStatus)) {
+ Network network = wrapper.get();
+ VvmLog.d(TAG, "provisioning: network available");
+ mRequestQueue = Volley
+ .newRequestQueue(mHelper.getContext(), new NetworkSpecifiedHurlStack(network));
+ processSubscription();
+ } catch (RequestFailedException e) {
+ mHelper.handleEvent(mStatus, OmtpEvents.VVM3_VMG_CONNECTION_FAILED);
+ mTask.fail();
+ }
+ }
+
+ private void processSubscription() {
+ try {
+ String gatewayUrl = getSelfProvisioningGateway();
+ String selfProvisionResponse = getSelfProvisionResponse(gatewayUrl);
+ String subscribeLink = findSubscribeLink(selfProvisionResponse);
+ clickSubscribeLink(subscribeLink);
+ } catch (ProvisioningException e) {
+ VvmLog.e(TAG, e.toString());
+ mTask.fail();
+ }
+ }
+
+ /**
+ * Get the URL to perform self-provisioning from the voicemail management gateway.
+ */
+ private String getSelfProvisioningGateway() throws ProvisioningException {
+ VvmLog.i(TAG, "retrieving SPG URL");
+ String response = vvm3XmlRequest(OPERATION_GET_SPG_URL);
+ return extractText(response, SPG_URL_TAG);
+ }
+
+ /**
+ * Sent a request to the self-provisioning gateway, which will return us with a webpage. The
+ * page might contain a "Subscribe to Basic Visual Voice Mail" link to complete the
+ * subscription. The cookie from this response and cellular data is required to click the link.
+ */
+ private String getSelfProvisionResponse(String url) throws ProvisioningException {
+ VvmLog.i(TAG, "Retrieving self provisioning response");
+
+ RequestFuture<String> future = RequestFuture.newFuture();
+
+ StringRequest stringRequest = new StringRequest(Request.Method.POST, url, future, future) {
+ @Override
+ protected Map<String, String> getParams() {
+ Map<String, String> params = new ArrayMap<>();
+ params.put(SPG_VZW_MDN_PARAM, mNumber);
+ params.put(SPG_VZW_SERVICE_PARAM, SPG_VZW_SERVICE_BASIC);
+ params.put(SPG_DEVICE_MODEL_PARAM, SPG_DEVICE_MODEL_ANDROID);
+ params.put(SPG_APP_TOKEN_PARAM, SPG_APP_TOKEN);
+ // Language to display the subscription page. The page is never shown to the user
+ // so just use English.
+ params.put(SPG_LANGUAGE_PARAM, SPG_LANGUAGE_EN);
+ return params;
+ }
+ };
+
+ mRequestQueue.add(stringRequest);
+ try {
+ return future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ mHelper.handleEvent(mStatus, OmtpEvents.VVM3_SPG_CONNECTION_FAILED);
+ throw new ProvisioningException(e.toString());
+ }
+ }
+
+ private void clickSubscribeLink(String subscribeLink) throws ProvisioningException {
+ VvmLog.i(TAG, "Clicking subscribe link");
+ RequestFuture<String> future = RequestFuture.newFuture();
+
+ StringRequest stringRequest = new StringRequest(Request.Method.POST,
+ subscribeLink, future, future);
+ mRequestQueue.add(stringRequest);
+ try {
+ // A new STATUS SMS will be sent after this request.
+ future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ } catch (TimeoutException | ExecutionException | InterruptedException e) {
+ mHelper.handleEvent(mStatus, OmtpEvents.VVM3_SPG_CONNECTION_FAILED);
+ throw new ProvisioningException(e.toString());
+ }
+ // It could take very long for the STATUS SMS to return. Waiting for it is unreliable.
+ // Just leave the CONFIG STATUS as CONFIGURING and end the task. The user can always
+ // manually retry if it took too long.
+ }
+
+ private String vvm3XmlRequest(String operation) throws ProvisioningException {
+ VvmLog.d(TAG, "Sending vvm3XmlRequest for " + operation);
+ String voicemailManagementGateway = mData.getString(VMG_URL_KEY);
+ if (voicemailManagementGateway == null) {
+ VvmLog.e(TAG, "voicemailManagementGateway url unknown");
+ return null;
+ }
+ String transactionId = createTransactionId();
+ String body = String.format(Locale.US, VMG_XML_REQUEST_FORMAT,
+ transactionId, mNumber, operation, Build.MODEL);
+
+ RequestFuture<String> future = RequestFuture.newFuture();
+ StringRequest stringRequest = new StringRequest(Request.Method.POST,
+ voicemailManagementGateway, future, future) {
+ @Override
+ public byte[] getBody() throws AuthFailureError {
+ return body.getBytes();
+ }
+ };
+ mRequestQueue.add(stringRequest);
+
+ try {
+ String response = future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ if (!transactionId.equals(extractText(response, TRANSACTION_ID_TAG))) {
+ throw new ProvisioningException("transactionId mismatch");
+ }
+ return response;
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ mHelper.handleEvent(mStatus, OmtpEvents.VVM3_VMG_CONNECTION_FAILED);
+ throw new ProvisioningException(e.toString());
+ }
+ }
+
+ private String findSubscribeLink(String response) throws ProvisioningException {
+ Spanned doc = Html.fromHtml(response, Html.FROM_HTML_MODE_LEGACY);
+ URLSpan[] spans = doc.getSpans(0, doc.length(), URLSpan.class);
+ StringBuilder fulltext = new StringBuilder();
+ for (URLSpan span : spans) {
+ String text = doc.subSequence(doc.getSpanStart(span), doc.getSpanEnd(span)).toString();
+ if (BASIC_SUBSCRIBE_LINK_TEXT.equals(text)) {
+ return span.getURL();
+ }
+ fulltext.append(text);
+ }
+ throw new ProvisioningException("Subscribe link not found: " + fulltext);
+ }
+
+ private String createTransactionId() {
+ return String.valueOf(Math.abs(new Random().nextLong()));
+ }
+
+ private String extractText(String xml, String tag) throws ProvisioningException {
+ Pattern pattern = Pattern.compile("<" + tag + ">(.*)<\\/" + tag + ">");
+ Matcher matcher = pattern.matcher(xml);
+ if (matcher.find()) {
+ return matcher.group(1);
+ }
+ throw new ProvisioningException("Tag " + tag + " not found in xml response");
+ }
+
+ private static class NetworkSpecifiedHurlStack extends HurlStack {
+
+ private final Network mNetwork;
+
+ public NetworkSpecifiedHurlStack(Network network) {
+ mNetwork = network;
+ }
+
+ @Override
+ protected HttpURLConnection createConnection(URL url) throws IOException {
+ return (HttpURLConnection) mNetwork.openConnection(url);
+ }
+
+ }
+}
diff --git a/java/com/android/voicemailomtp/res/layout/voicemail_change_pin.xml b/java/com/android/voicemailomtp/res/layout/voicemail_change_pin.xml
new file mode 100644
index 000000000..b0db64b12
--- /dev/null
+++ b/java/com/android/voicemailomtp/res/layout/voicemail_change_pin.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2014, 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.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+ <!-- header text ('Enter Pin') -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="48dp"
+ android:paddingStart="48dp"
+ android:paddingEnd="48dp">
+ <TextView
+ android:id="@+id/headerText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:lines="2"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"
+ android:accessibilityLiveRegion="polite"/>
+
+ <!-- hint text ('PIN too short') -->
+ <TextView
+ android:id="@+id/hintText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:lines="2" />
+
+ <!-- error text ('PIN too short') -->
+ <TextView
+ android:id="@+id/errorText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:lines="2"
+ android:textColor="@android:color/holo_red_dark"/>
+
+ <!-- Password entry field -->
+ <EditText
+ android:id="@+id/pin_entry"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:imeOptions="actionNext|flagNoExtractUi"
+ android:inputType="numberPassword"
+ android:textSize="24sp"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:gravity="end"
+ android:orientation="horizontal">
+
+ <!-- left : cancel -->
+ <Button
+ android:id="@+id/cancel_button"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="@string/change_pin_cancel_label"/>
+
+ <!-- right : continue -->
+ <Button
+ android:id="@+id/next_button"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="@string/change_pin_continue_label"/>
+
+ </LinearLayout>
+</LinearLayout>
diff --git a/java/com/android/voicemailomtp/res/values/arrays.xml b/java/com/android/voicemailomtp/res/values/arrays.xml
new file mode 100644
index 000000000..95714cf4d
--- /dev/null
+++ b/java/com/android/voicemailomtp/res/values/arrays.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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.
+-->
+<resources>
+
+</resources>
diff --git a/java/com/android/voicemailomtp/res/values/attrs.xml b/java/com/android/voicemailomtp/res/values/attrs.xml
new file mode 100644
index 000000000..d1c7329d5
--- /dev/null
+++ b/java/com/android/voicemailomtp/res/values/attrs.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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.
+-->
+
+<resources>
+
+ <attr name="preferenceBackgroundColor" format="color" />
+</resources>
diff --git a/java/com/android/voicemailomtp/res/values/colors.xml b/java/com/android/voicemailomtp/res/values/colors.xml
new file mode 100644
index 000000000..8a897ab94
--- /dev/null
+++ b/java/com/android/voicemailomtp/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+
+</resources>
diff --git a/java/com/android/voicemailomtp/res/values/config.xml b/java/com/android/voicemailomtp/res/values/config.xml
new file mode 100644
index 000000000..2f5603083
--- /dev/null
+++ b/java/com/android/voicemailomtp/res/values/config.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<resources>
+
+</resources>
diff --git a/java/com/android/voicemailomtp/res/values/dimens.xml b/java/com/android/voicemailomtp/res/values/dimens.xml
new file mode 100644
index 000000000..e66ca0921
--- /dev/null
+++ b/java/com/android/voicemailomtp/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+
+</resources>
diff --git a/java/com/android/voicemailomtp/res/values/ids.xml b/java/com/android/voicemailomtp/res/values/ids.xml
new file mode 100644
index 000000000..84c685a14
--- /dev/null
+++ b/java/com/android/voicemailomtp/res/values/ids.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+
+</resources> \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/res/values/strings.xml b/java/com/android/voicemailomtp/res/values/strings.xml
new file mode 100644
index 000000000..7a1407371
--- /dev/null
+++ b/java/com/android/voicemailomtp/res/values/strings.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Title of the "Voicemail" settings screen, with a text label identifying which SIM the settings are for. -->
+ <string translatable="false" name="voicemail_settings_with_label">Voicemail (<xliff:g id="subscriptionlabel" example="Mock Carrier">%s</xliff:g>)</string>
+
+ <!-- Call settings screen, setting option name -->
+ <string translatable="false" name="voicemail_settings_label">Voicemail</string>
+
+ <!-- DO NOT TRANSLATE. Internal key for a visual voicemail preference. -->
+ <string translatable="false" name="voicemail_visual_voicemail_key">
+ voicemail_visual_voicemail_key
+ </string>
+ <!-- DO NOT TRANSLATE. Internal key for a voicemail change pin preference. -->
+ <string translatable="false" name="voicemail_change_pin_key">voicemail_change_pin_key</string>
+
+ <!-- Visual voicemail on/off title [CHAR LIMIT=40] -->
+ <string translatable="false" name="voicemail_visual_voicemail_switch_title">Visual Voicemail</string>
+
+ <!-- Voicemail change PIN dialog title [CHAR LIMIT=40] -->
+ <string translatable="false" name="voicemail_set_pin_dialog_title">Set PIN</string>
+ <!-- Voicemail change PIN dialog title [CHAR LIMIT=40] -->
+ <string translatable="false" name="voicemail_change_pin_dialog_title">Change PIN</string>
+
+ <!-- Hint for the old PIN field in the change vociemail PIN dialog -->
+ <string translatable="false" name="vm_change_pin_old_pin">Old PIN</string>
+ <!-- Hint for the new PIN field in the change vociemail PIN dialog -->
+ <string translatable="false" name="vm_change_pin_new_pin">New PIN</string>
+
+ <!-- Message on the dialog when PIN changing is in progress -->
+ <string translatable="false" name="vm_change_pin_progress_message">Please wait.</string>
+ <!-- Error message for the voicemail PIN change if the PIN is too short -->
+ <string translatable="false" name="vm_change_pin_error_too_short">The new PIN is too short.</string>
+ <!-- Error message for the voicemail PIN change if the PIN is too long -->
+ <string translatable="false" name="vm_change_pin_error_too_long">The new PIN is too long.</string>
+ <!-- Error message for the voicemail PIN change if the PIN is too weak -->
+ <string translatable="false" name="vm_change_pin_error_too_weak">The new PIN is too weak. A strong password should not have continuous sequence or repeated digits.</string>
+ <!-- Error message for the voicemail PIN change if the old PIN entered doesn't match -->
+ <string translatable="false" name="vm_change_pin_error_mismatch">The old PIN does not match.</string>
+ <!-- Error message for the voicemail PIN change if the new PIN contains invalid character -->
+ <string translatable="false" name="vm_change_pin_error_invalid">The new PIN contains invalid characters.</string>
+ <!-- Error message for the voicemail PIN change if operation has failed -->
+ <string translatable="false" name="vm_change_pin_error_system_error">Unable to change PIN</string>
+ <!-- Message to replace the transcription if a visual voicemail message is not supported-->
+ <string translatable="false" name="vvm_unsupported_message_format">Unsupported message type, call <xliff:g id="number" example="*86">%s</xliff:g> to listen.</string>
+
+ <!-- The title for the change voicemail PIN activity -->
+ <string translatable="false" name="change_pin_title">Change Voicemail PIN</string>
+ <!-- The label for the continue button in change voicemail PIN activity -->
+ <string translatable="false" name="change_pin_continue_label">Continue</string>
+ <!-- The label for the cancel button in change voicemail PIN activity -->
+ <string translatable="false" name="change_pin_cancel_label">Cancel</string>
+ <!-- The label for the ok button in change voicemail PIN activity -->
+ <string translatable="false" name="change_pin_ok_label">Ok</string>
+ <!-- The title for the enter old pin step in change voicemail PIN activity -->
+ <string translatable="false" name="change_pin_enter_old_pin_header">Confirm your old PIN</string>
+ <!-- The hint for the enter old pin step in change voicemail PIN activity -->
+ <string translatable="false" name="change_pin_enter_old_pin_hint">Enter your voicemail PIN to continue.</string>
+ <!-- The title for the enter new pin step in change voicemail PIN activity -->
+ <string translatable="false" name="change_pin_enter_new_pin_header">Set a new PIN</string>
+ <!-- The hint for the enter new pin step in change voicemail PIN activity -->
+ <string translatable="false" name="change_pin_enter_new_pin_hint">PIN must be <xliff:g id="min" example="4">%1$d</xliff:g>-<xliff:g id="max" example="7">%2$d</xliff:g> digits.</string>
+ <!-- The title for the confirm new pin step in change voicemail PIN activity -->
+ <string translatable="false" name="change_pin_confirm_pin_header">Confirm your PIN</string>
+ <!-- The error message for th confirm new pin step in change voicemail PIN activity, if the pin doen't match the one previously entered -->
+ <string translatable="false" name="change_pin_confirm_pins_dont_match">PINs don\'t match</string>
+ <!-- The toast to show after the voicemail PIN has been successfully changed -->
+ <string translatable="false" name="change_pin_succeeded">Voicemail PIN updated</string>
+ <!-- The error message to show if the server reported an error while attempting to change the voicemail PIN -->
+ <string translatable="false" name="change_pin_system_error">Unable to set PIN</string>
+</resources>
diff --git a/java/com/android/voicemailomtp/res/values/styles.xml b/java/com/android/voicemailomtp/res/values/styles.xml
new file mode 100644
index 000000000..8a897ab94
--- /dev/null
+++ b/java/com/android/voicemailomtp/res/values/styles.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+
+</resources>
diff --git a/java/com/android/voicemailomtp/res/xml/voicemail_settings.xml b/java/com/android/voicemailomtp/res/xml/voicemail_settings.xml
new file mode 100644
index 000000000..03bc34efc
--- /dev/null
+++ b/java/com/android/voicemailomtp/res/xml/voicemail_settings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ android:title="@string/voicemail_settings_label">
+
+ <SwitchPreference
+ android:key="@string/voicemail_visual_voicemail_key"
+ android:title="@string/voicemail_visual_voicemail_switch_title"/>"
+
+ <Preference
+ android:key="@string/voicemail_change_pin_key"
+ android:title="@string/voicemail_change_pin_dialog_title"/>
+</PreferenceScreen>
diff --git a/java/com/android/voicemailomtp/res/xml/vvm_config.xml b/java/com/android/voicemailomtp/res/xml/vvm_config.xml
new file mode 100644
index 000000000..19c667e13
--- /dev/null
+++ b/java/com/android/voicemailomtp/res/xml/vvm_config.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<list name="carrier_config_list">
+ <pbundle_as_map>
+ <!-- Test -->
+ <string-array name="mccmnc">
+ <item value="TEST"/>
+ </string-array>
+ </pbundle_as_map>
+
+ <pbundle_as_map>
+ <!-- Orange France -->
+ <string-array name="mccmnc">
+ <item value="20801"/>
+ <item value="20802"/>
+ </string-array>
+
+ <int name="vvm_port_number_int" value="20481"/>
+ <string name="vvm_destination_number_string">21101</string>
+ <string-array name="carrier_vvm_package_name_string_array">
+ <item value="com.orange.vvm"/>
+ </string-array>
+ <string name="vvm_type_string">vvm_type_omtp</string>
+ <boolean name="vvm_cellular_data_required_bool" value="true"/>
+ <string-array name="vvm_disabled_capabilities_string_array">
+ <!-- b/32365569 -->
+ <item value="STARTTLS"/>
+ </string-array>
+ </pbundle_as_map>
+
+ <pbundle_as_map>
+ <!-- T-Mobile USA-->
+ <string-array name="mccmnc">
+ <item value="310160"/>
+ <item value="310200"/>
+ <item value="310210"/>
+ <item value="310220"/>
+ <item value="310230"/>
+ <item value="310240"/>
+ <item value="310250"/>
+ <item value="310260"/>
+ <item value="310270"/>
+ <item value="310300"/>
+ <item value="310310"/>
+ <item value="310490"/>
+ <item value="310530"/>
+ <item value="310590"/>
+ <item value="310640"/>
+ <item value="310660"/>
+ <item value="310800"/>
+ </string-array>
+
+ <int name="vvm_port_number_int" value="1808"/>
+ <int name="vvm_ssl_port_number_int" value="993"/>
+ <string name="vvm_destination_number_string">122</string>
+ <string-array name="carrier_vvm_package_name_string_array">
+ <item value="com.tmobile.vvm.application"/>
+ </string-array>
+ <string name="vvm_type_string">vvm_type_cvvm</string>>
+ <string-array name="vvm_disabled_capabilities_string_array">
+ <!-- b/28717550 -->
+ <item value="AUTH=DIGEST-MD5"/>
+ </string-array>
+ </pbundle_as_map>
+
+ <pbundle_as_map>
+ <!-- Verizon USA -->
+ <string-array name="mccmnc">
+ <item value="310004"/>
+ <item value="310010"/>
+ <item value="310012"/>
+ <item value="310013"/>
+ <item value="310590"/>
+ <item value="310890"/>
+ <item value="310910"/>
+ <item value="311110"/>
+ <item value="311270"/>
+ <item value="311271"/>
+ <item value="311272"/>
+ <item value="311273"/>
+ <item value="311274"/>
+ <item value="311275"/>
+ <item value="311276"/>
+ <item value="311277"/>
+ <item value="311278"/>
+ <item value="311279"/>
+ <item value="311280"/>
+ <item value="311281"/>
+ <item value="311282"/>
+ <item value="311283"/>
+ <item value="311284"/>
+ <item value="311285"/>
+ <item value="311286"/>
+ <item value="311287"/>
+ <item value="311288"/>
+ <item value="311289"/>
+ <item value="311390"/>
+ <item value="311480"/>
+ <item value="311481"/>
+ <item value="311482"/>
+ <item value="311483"/>
+ <item value="311484"/>
+ <item value="311485"/>
+ <item value="311486"/>
+ <item value="311487"/>
+ <item value="311488"/>
+ <item value="311489"/>
+ </string-array>
+
+ <int name="vvm_port_number_int" value="0"/>
+ <string name="vvm_destination_number_string">900080006200</string>
+ <string name="vvm_type_string">vvm_type_vvm3</string>
+ <string name="vvm_client_prefix_string">//VZWVVM</string>
+ <boolean name="vvm_cellular_data_required_bool" value="true"/>
+ <boolean name="vvm_legacy_mode_enabled_bool" value="true"/>
+ <!-- VVM3 specific value for the voicemail management gateway to use if the SMS didn't provide
+ one -->
+ <string name="default_vmg_url">https://mobile.vzw.com/VMGIMS/VMServices</string>
+ </pbundle_as_map>
+</list>
diff --git a/java/com/android/voicemailomtp/scheduling/BaseTask.java b/java/com/android/voicemailomtp/scheduling/BaseTask.java
new file mode 100644
index 000000000..8097bb4dc
--- /dev/null
+++ b/java/com/android/voicemailomtp/scheduling/BaseTask.java
@@ -0,0 +1,206 @@
+/*
+ * 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.voicemailomtp.scheduling;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.SystemClock;
+import android.support.annotation.CallSuper;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.SubscriptionManager;
+import com.android.voicemailomtp.Assert;
+import com.android.voicemailomtp.NeededForTesting;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides common utilities for task implementations, such as execution time and managing {@link
+ * Policy}
+ */
+public abstract class BaseTask implements Task {
+
+ private static final String EXTRA_PHONE_ACCOUNT_HANDLE = "extra_phone_account_handle";
+
+ private Context mContext;
+
+ private int mId;
+ private PhoneAccountHandle mPhoneAccountHandle;
+
+ private boolean mHasStarted;
+ private volatile boolean mHasFailed;
+
+ @NonNull
+ private final List<Policy> mPolicies = new ArrayList<>();
+
+ private long mExecutionTime;
+
+ private static Clock sClock = new Clock();
+
+ protected BaseTask(int id) {
+ mId = id;
+ mExecutionTime = getTimeMillis();
+ }
+
+ /**
+ * Modify the task ID to prevent arbitrary task from executing. Can only be called before {@link
+ * #onCreate(Context, Intent, int, int)} returns.
+ */
+ @MainThread
+ public void setId(int id) {
+ Assert.isMainThread();
+ mId = id;
+ }
+
+ @MainThread
+ public boolean hasStarted() {
+ Assert.isMainThread();
+ return mHasStarted;
+ }
+
+ @MainThread
+ public boolean hasFailed() {
+ Assert.isMainThread();
+ return mHasFailed;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public PhoneAccountHandle getPhoneAccountHandle() {
+ return mPhoneAccountHandle;
+ }
+ /**
+ * Should be call in the constructor or {@link Policy#onCreate(BaseTask, Intent, int, int)} will
+ * be missed.
+ */
+ @MainThread
+ public BaseTask addPolicy(Policy policy) {
+ Assert.isMainThread();
+ mPolicies.add(policy);
+ return this;
+ }
+
+ /**
+ * Indicate the task has failed. {@link Policy#onFail()} will be triggered once the execution
+ * ends. This mechanism is used by policies for actions such as determining whether to schedule
+ * a retry. Must be call inside {@link #onExecuteInBackgroundThread()}
+ */
+ @WorkerThread
+ public void fail() {
+ Assert.isNotMainThread();
+ mHasFailed = true;
+ }
+
+ @MainThread
+ public void setExecutionTime(long timeMillis) {
+ Assert.isMainThread();
+ mExecutionTime = timeMillis;
+ }
+
+ public long getTimeMillis() {
+ return sClock.getTimeMillis();
+ }
+
+ /**
+ * Creates an intent that can be used to restart the current task. Derived class should build
+ * their intent upon this.
+ */
+ public Intent createRestartIntent() {
+ return createIntent(getContext(), this.getClass(), mPhoneAccountHandle);
+ }
+
+ /**
+ * Creates an intent that can be used to start the {@link TaskSchedulerService}. Derived class
+ * should build their intent upon this.
+ */
+ public static Intent createIntent(Context context, Class<? extends BaseTask> task,
+ PhoneAccountHandle phoneAccountHandle) {
+ Intent intent = TaskSchedulerService.createIntent(context, task);
+ intent.putExtra(EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+ return intent;
+ }
+
+ @Override
+ public TaskId getId() {
+ return new TaskId(mId, mPhoneAccountHandle);
+ }
+
+ @Override
+ @CallSuper
+ public void onCreate(Context context, Intent intent, int flags, int startId) {
+ mContext = context;
+ mPhoneAccountHandle = intent.getParcelableExtra(EXTRA_PHONE_ACCOUNT_HANDLE);
+ for (Policy policy : mPolicies) {
+ policy.onCreate(this, intent, flags, startId);
+ }
+ }
+
+ @Override
+ public long getReadyInMilliSeconds() {
+ return mExecutionTime - getTimeMillis();
+ }
+
+ @Override
+ @CallSuper
+ public void onBeforeExecute() {
+ for (Policy policy : mPolicies) {
+ policy.onBeforeExecute();
+ }
+ mHasStarted = true;
+ }
+
+ @Override
+ @CallSuper
+ public void onCompleted() {
+ if (mHasFailed) {
+ for (Policy policy : mPolicies) {
+ policy.onFail();
+ }
+ }
+
+ for (Policy policy : mPolicies) {
+ policy.onCompleted();
+ }
+ }
+
+ @Override
+ public void onDuplicatedTaskAdded(Task task) {
+ for (Policy policy : mPolicies) {
+ policy.onDuplicatedTaskAdded();
+ }
+ }
+
+ @NeededForTesting
+ static class Clock {
+
+ public long getTimeMillis() {
+ return SystemClock.elapsedRealtime();
+ }
+ }
+
+ /**
+ * Used to replace the clock with an deterministic clock
+ */
+ @NeededForTesting
+ static void setClockForTesting(Clock clock) {
+ sClock = clock;
+ }
+}
diff --git a/java/com/android/voicemailomtp/scheduling/BlockerTask.java b/java/com/android/voicemailomtp/scheduling/BlockerTask.java
new file mode 100644
index 000000000..55ad9a7fd
--- /dev/null
+++ b/java/com/android/voicemailomtp/scheduling/BlockerTask.java
@@ -0,0 +1,55 @@
+/*
+ * 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.voicemailomtp.scheduling;
+
+import android.content.Context;
+import android.content.Intent;
+
+import com.android.voicemailomtp.VvmLog;
+
+/**
+ * Task to block another task of the same ID from being queued for a certain amount of time.
+ */
+public class BlockerTask extends BaseTask {
+
+ private static final String TAG = "BlockerTask";
+
+ public static final String EXTRA_TASK_ID = "extra_task_id";
+ public static final String EXTRA_BLOCK_FOR_MILLIS = "extra_block_for_millis";
+
+ public BlockerTask() {
+ super(TASK_INVALID);
+ }
+
+ @Override
+ public void onCreate(Context context, Intent intent, int flags, int startId) {
+ super.onCreate(context, intent, flags, startId);
+ setId(intent.getIntExtra(EXTRA_TASK_ID, TASK_INVALID));
+ setExecutionTime(getTimeMillis() + intent.getIntExtra(EXTRA_BLOCK_FOR_MILLIS, 0));
+ }
+
+ @Override
+ public void onExecuteInBackgroundThread() {
+ // Do nothing.
+ }
+
+ @Override
+ public void onDuplicatedTaskAdded(Task task) {
+ VvmLog
+ .v(TAG, task.toString() + "blocked, " + getReadyInMilliSeconds() + "millis remaining");
+ }
+}
diff --git a/java/com/android/voicemailomtp/scheduling/MinimalIntervalPolicy.java b/java/com/android/voicemailomtp/scheduling/MinimalIntervalPolicy.java
new file mode 100644
index 000000000..bef449b30
--- /dev/null
+++ b/java/com/android/voicemailomtp/scheduling/MinimalIntervalPolicy.java
@@ -0,0 +1,69 @@
+/*
+ * 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.voicemailomtp.scheduling;
+
+import android.content.Intent;
+
+import com.android.voicemailomtp.scheduling.Task.TaskId;
+
+/**
+ * If a task with this policy succeeds, a {@link BlockerTask} with the same {@link TaskId} of the
+ * task will be queued immediately, preventing the same task from running for a certain amount of
+ * time.
+ */
+public class MinimalIntervalPolicy implements Policy {
+
+ BaseTask mTask;
+ TaskId mId;
+ int mBlockForMillis;
+
+ public MinimalIntervalPolicy(int blockForMillis) {
+ mBlockForMillis = blockForMillis;
+ }
+
+ @Override
+ public void onCreate(BaseTask task, Intent intent, int flags, int startId) {
+ mTask = task;
+ mId = mTask.getId();
+ }
+
+ @Override
+ public void onBeforeExecute() {
+
+ }
+
+ @Override
+ public void onCompleted() {
+ if (!mTask.hasFailed()) {
+ Intent intent = mTask
+ .createIntent(mTask.getContext(), BlockerTask.class, mId.phoneAccountHandle);
+ intent.putExtra(BlockerTask.EXTRA_TASK_ID, mId.id);
+ intent.putExtra(BlockerTask.EXTRA_BLOCK_FOR_MILLIS, mBlockForMillis);
+ mTask.getContext().startService(intent);
+ }
+ }
+
+ @Override
+ public void onFail() {
+
+ }
+
+ @Override
+ public void onDuplicatedTaskAdded() {
+
+ }
+}
diff --git a/java/com/android/voicemailomtp/scheduling/Policy.java b/java/com/android/voicemailomtp/scheduling/Policy.java
new file mode 100644
index 000000000..4a475d2ed
--- /dev/null
+++ b/java/com/android/voicemailomtp/scheduling/Policy.java
@@ -0,0 +1,36 @@
+/*
+ * 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.voicemailomtp.scheduling;
+
+import android.content.Intent;
+
+/**
+ * A set of listeners managed by {@link BaseTask} for common behaviors such as retrying. Call {@link
+ * BaseTask#addPolicy(Policy)} to add a policy.
+ */
+public interface Policy {
+
+ void onCreate(BaseTask task, Intent intent, int flags, int startId);
+
+ void onBeforeExecute();
+
+ void onCompleted();
+
+ void onFail();
+
+ void onDuplicatedTaskAdded();
+}
diff --git a/java/com/android/voicemailomtp/scheduling/PostponePolicy.java b/java/com/android/voicemailomtp/scheduling/PostponePolicy.java
new file mode 100644
index 000000000..27a82f0ef
--- /dev/null
+++ b/java/com/android/voicemailomtp/scheduling/PostponePolicy.java
@@ -0,0 +1,69 @@
+/*
+ * 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.voicemailomtp.scheduling;
+
+import android.content.Intent;
+
+import com.android.voicemailomtp.VvmLog;
+
+/**
+ * A task with Postpone policy will not be executed immediately. It will wait for a while and if a
+ * duplicated task is queued during the duration, the task will be postponed further. The task will
+ * only be executed if no new task was added in postponeMillis. Useful to batch small tasks in quick
+ * succession together.
+ */
+public class PostponePolicy implements Policy {
+
+ private static final String TAG = "PostponePolicy";
+
+ private final int mPostponeMillis;
+ private BaseTask mTask;
+
+ public PostponePolicy(int postponeMillis) {
+ mPostponeMillis = postponeMillis;
+ }
+
+ @Override
+ public void onCreate(BaseTask task, Intent intent, int flags, int startId) {
+ mTask = task;
+ mTask.setExecutionTime(mTask.getTimeMillis() + mPostponeMillis);
+ }
+
+ @Override
+ public void onBeforeExecute() {
+ // Do nothing
+ }
+
+ @Override
+ public void onCompleted() {
+ // Do nothing
+ }
+
+ @Override
+ public void onFail() {
+ // Do nothing
+ }
+
+ @Override
+ public void onDuplicatedTaskAdded() {
+ if (mTask.hasStarted()) {
+ return;
+ }
+ VvmLog.d(TAG, "postponing " + mTask);
+ mTask.setExecutionTime(mTask.getTimeMillis() + mPostponeMillis);
+ }
+}
diff --git a/java/com/android/voicemailomtp/scheduling/RetryPolicy.java b/java/com/android/voicemailomtp/scheduling/RetryPolicy.java
new file mode 100644
index 000000000..463657483
--- /dev/null
+++ b/java/com/android/voicemailomtp/scheduling/RetryPolicy.java
@@ -0,0 +1,117 @@
+/*
+ * 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.voicemailomtp.scheduling;
+
+import android.content.Intent;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.voicemailomtp.VoicemailStatus;
+import com.android.voicemailomtp.VvmLog;
+
+/**
+ * A task with this policy will automatically re-queue itself if {@link BaseTask#fail()} has been
+ * called during {@link BaseTask#onExecuteInBackgroundThread()}. A task will be retried at most
+ * <code>retryLimit</code> times and with a <code>retryDelayMillis</code> interval in between.
+ */
+public class RetryPolicy implements Policy {
+
+ private static final String TAG = "RetryPolicy";
+ private static final String EXTRA_RETRY_COUNT = "extra_retry_count";
+
+ private final int mRetryLimit;
+ private final int mRetryDelayMillis;
+
+ private BaseTask mTask;
+
+ private int mRetryCount;
+ private boolean mFailed;
+
+ private VoicemailStatus.DeferredEditor mVoicemailStatusEditor;
+
+ public RetryPolicy(int retryLimit, int retryDelayMillis) {
+ mRetryLimit = retryLimit;
+ mRetryDelayMillis = retryDelayMillis;
+ }
+
+ private boolean hasMoreRetries() {
+ return mRetryCount < mRetryLimit;
+ }
+
+ /**
+ * Error status should only be set if retries has exhausted or the task is successful. Status
+ * writes to this editor will be deferred until the task has ended, and will only be committed
+ * if the task is successful or there are no retries left.
+ */
+ public VoicemailStatus.Editor getVoicemailStatusEditor() {
+ return mVoicemailStatusEditor;
+ }
+
+ @Override
+ public void onCreate(BaseTask task, Intent intent, int flags, int startId) {
+ mTask = task;
+ mRetryCount = intent.getIntExtra(EXTRA_RETRY_COUNT, 0);
+ if (mRetryCount > 0) {
+ VvmLog.d(TAG, "retry #" + mRetryCount + " for " + mTask + " queued, executing in "
+ + mRetryDelayMillis);
+ mTask.setExecutionTime(mTask.getTimeMillis() + mRetryDelayMillis);
+ }
+ PhoneAccountHandle phoneAccountHandle = task.getPhoneAccountHandle();
+ if (phoneAccountHandle == null) {
+ VvmLog.e(TAG,
+ "null phone account for phoneAccountHandle " + task.getPhoneAccountHandle());
+ // This should never happen, but continue on if it does. The status write will be
+ // discarded.
+ }
+ mVoicemailStatusEditor = VoicemailStatus
+ .deferredEdit(task.getContext(), phoneAccountHandle);
+ }
+
+ @Override
+ public void onBeforeExecute() {
+
+ }
+
+ @Override
+ public void onCompleted() {
+ if (!mFailed || !hasMoreRetries()) {
+ if (!mFailed) {
+ VvmLog.d(TAG, mTask.toString() + " completed successfully");
+ }
+ if (!hasMoreRetries()) {
+ VvmLog.d(TAG, "Retry limit for " + mTask + " reached");
+ }
+ VvmLog.i(TAG, "committing deferred status: " + mVoicemailStatusEditor.getValues());
+ mVoicemailStatusEditor.deferredApply();
+ return;
+ }
+ VvmLog.i(TAG, "discarding deferred status: " + mVoicemailStatusEditor.getValues());
+ Intent intent = mTask.createRestartIntent();
+ intent.putExtra(EXTRA_RETRY_COUNT, mRetryCount + 1);
+
+ mTask.getContext().startService(intent);
+ }
+
+ @Override
+ public void onFail() {
+ mFailed = true;
+ }
+
+ @Override
+ public void onDuplicatedTaskAdded() {
+
+ }
+}
diff --git a/java/com/android/voicemailomtp/scheduling/Task.java b/java/com/android/voicemailomtp/scheduling/Task.java
new file mode 100644
index 000000000..61c35396b
--- /dev/null
+++ b/java/com/android/voicemailomtp/scheduling/Task.java
@@ -0,0 +1,133 @@
+/*
+ * 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.voicemailomtp.scheduling;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.annotation.MainThread;
+import android.support.annotation.WorkerThread;
+import android.telecom.PhoneAccountHandle;
+
+import java.util.Objects;
+
+/**
+ * A task for {@link TaskSchedulerService} to execute. Since the task is sent through a intent to
+ * the scheduler, The task must be constructable with the intent. Specifically, It must have a
+ * constructor with zero arguments, and have all relevant data packed inside the intent. Use {@link
+ * TaskSchedulerService#createIntent(Context, Class)} to create a intent that will construct the
+ * Task.
+ *
+ * <p>Only {@link #onExecuteInBackgroundThread()} is run on the worker thread.
+ */
+public interface Task {
+
+ /**
+ * TaskId to indicate it has not be set. If a task does not provide a default TaskId it should
+ * be set before {@link Task#onCreate(Context, Intent, int, int) returns}
+ */
+ int TASK_INVALID = -1;
+
+ /**
+ * TaskId to indicate it should always be queued regardless of duplicates. {@link
+ * Task#onDuplicatedTaskAdded(Task)} will never be called on tasks with this TaskId.
+ */
+ int TASK_ALLOW_DUPLICATES = -2;
+
+ int TASK_UPLOAD = 1;
+ int TASK_SYNC = 2;
+ int TASK_ACTIVATION = 3;
+
+ /**
+ * Used to differentiate between types of tasks. If a task with the same TaskId is already in
+ * the queue the new task will be rejected.
+ */
+ class TaskId {
+
+ /**
+ * Indicates the operation type of the task.
+ */
+ public final int id;
+ /**
+ * Same operation for a different phoneAccountHandle is allowed. phoneAccountHandle is used
+ * to differentiate phone accounts in multi-SIM scenario. For example, each SIM can queue a
+ * sync task for their own.
+ */
+ public final PhoneAccountHandle phoneAccountHandle;
+
+ public TaskId(int id, PhoneAccountHandle phoneAccountHandle) {
+ this.id = id;
+ this.phoneAccountHandle = phoneAccountHandle;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof TaskId)) {
+ return false;
+ }
+ TaskId other = (TaskId) object;
+ return id == other.id && phoneAccountHandle.equals(other.phoneAccountHandle);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, phoneAccountHandle);
+ }
+ }
+
+ TaskId getId();
+
+ @MainThread
+ void onCreate(Context context, Intent intent, int flags, int startId);
+
+ /**
+ * @return number of milliSeconds the scheduler should wait before running this task. A value
+ * less than {@link TaskSchedulerService#READY_TOLERANCE_MILLISECONDS} will be considered ready.
+ * If no tasks are ready, the scheduler will sleep for this amount of time before doing another
+ * check (it will still wake if a new task is added). The first task in the queue that is ready
+ * will be executed.
+ */
+ @MainThread
+ long getReadyInMilliSeconds();
+
+ /**
+ * Called on the main thread when the scheduler is about to send the task into the worker
+ * thread, calling {@link #onExecuteInBackgroundThread()}
+ */
+ @MainThread
+ void onBeforeExecute();
+
+ /**
+ * The actual payload of the task, executed on the worker thread.
+ */
+ @WorkerThread
+ void onExecuteInBackgroundThread();
+
+ /**
+ * Called on the main thread when {@link #onExecuteInBackgroundThread()} has finished or thrown
+ * an uncaught exception. The task is already removed from the queue at this point, and a same
+ * task can be queued again.
+ */
+ @MainThread
+ void onCompleted();
+
+ /**
+ * Another task with the same TaskId has been added. Necessary data can be retrieved from the
+ * other task, and after this returns the task will be discarded.
+ */
+ @MainThread
+ void onDuplicatedTaskAdded(Task task);
+}
diff --git a/java/com/android/voicemailomtp/scheduling/TaskSchedulerService.java b/java/com/android/voicemailomtp/scheduling/TaskSchedulerService.java
new file mode 100644
index 000000000..90b50e913
--- /dev/null
+++ b/java/com/android/voicemailomtp/scheduling/TaskSchedulerService.java
@@ -0,0 +1,392 @@
+/*
+ * 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.voicemailomtp.scheduling;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.SystemClock;
+import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.annotation.WorkerThread;
+import com.android.voicemailomtp.Assert;
+import com.android.voicemailomtp.NeededForTesting;
+import com.android.voicemailomtp.VvmLog;
+import com.android.voicemailomtp.scheduling.Task.TaskId;
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+/**
+ * A service to queue and run {@link Task} on a worker thread. Only one task will be ran at a time,
+ * and same task cannot exist in the queue at the same time. The service will be started when a
+ * intent is received, and stopped when there are no more tasks in the queue.
+ */
+public class TaskSchedulerService extends Service {
+
+ private static final String TAG = "VvmTaskScheduler";
+
+ private static final String ACTION_WAKEUP = "action_wakeup";
+
+ private static final int READY_TOLERANCE_MILLISECONDS = 100;
+
+ /**
+ * Threshold to determine whether to do a short or long sleep when a task is scheduled in the
+ * future.
+ *
+ * <p>A short sleep will continue to held the wake lock and use {@link
+ * Handler#postDelayed(Runnable, long)} to wait for the next task.
+ *
+ * <p>A long sleep will release the wake lock and set a {@link AlarmManager} alarm. The alarm is
+ * exact and will wake up the device. Note: as this service is run in the telephony process it
+ * does not seem to be restricted by doze or sleep, it will fire exactly at the moment. The
+ * unbundled version should take doze into account.
+ */
+ private static final int SHORT_SLEEP_THRESHOLD_MILLISECONDS = 60_000;
+ /**
+ * When there are no more tasks to be run the service should be stopped. But when all tasks has
+ * finished there might still be more tasks in the message queue waiting to be processed,
+ * especially the ones submitted in {@link Task#onCompleted()}. Wait for a while before stopping
+ * the service to make sure there are no pending messages.
+ */
+ private static final int STOP_DELAY_MILLISECONDS = 5_000;
+ private static final String EXTRA_CLASS_NAME = "extra_class_name";
+
+ private static final String WAKE_LOCK_TAG = "TaskSchedulerService_wakelock";
+
+ // The thread to run tasks on
+ private volatile WorkerThreadHandler mWorkerThreadHandler;
+
+ private Context mContext = this;
+ /**
+ * Used by tests to turn task handling into a single threaded process by calling {@link
+ * Handler#handleMessage(Message)} directly
+ */
+ private MessageSender mMessageSender = new MessageSender();
+
+ private MainThreadHandler mMainThreadHandler;
+
+ private WakeLock mWakeLock;
+
+ /**
+ * Main thread only, access through {@link #getTasks()}
+ */
+ private final Queue<Task> mTasks = new ArrayDeque<>();
+ private boolean mWorkerThreadIsBusy = false;
+
+ private final Runnable mStopServiceWithDelay = new Runnable() {
+ @Override
+ public void run() {
+ VvmLog.d(TAG, "Stopping service");
+ stopSelf();
+ }
+ };
+ /**
+ * Should attempt to run the next task when a task has finished or been added.
+ */
+ private boolean mTaskAutoRunDisabledForTesting = false;
+
+ @VisibleForTesting
+ final class WorkerThreadHandler extends Handler {
+
+ public WorkerThreadHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ @WorkerThread
+ public void handleMessage(Message msg) {
+ Assert.isNotMainThread();
+ Task task = (Task) msg.obj;
+ try {
+ VvmLog.v(TAG, "executing task " + task);
+ task.onExecuteInBackgroundThread();
+ } catch (Throwable throwable) {
+ VvmLog.e(TAG, "Exception while executing task " + task + ":", throwable);
+ }
+
+ Message schedulerMessage = mMainThreadHandler.obtainMessage();
+ schedulerMessage.obj = task;
+ mMessageSender.send(schedulerMessage);
+ }
+ }
+
+ @VisibleForTesting
+ final class MainThreadHandler extends Handler {
+
+ public MainThreadHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ @MainThread
+ public void handleMessage(Message msg) {
+ Assert.isMainThread();
+ Task task = (Task) msg.obj;
+ getTasks().remove(task);
+ task.onCompleted();
+ mWorkerThreadIsBusy = false;
+ maybeRunNextTask();
+ }
+ }
+
+ @Override
+ @MainThread
+ public void onCreate() {
+ super.onCreate();
+ mWakeLock = getSystemService(PowerManager.class)
+ .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG);
+ mWakeLock.setReferenceCounted(false);
+ HandlerThread thread = new HandlerThread("VvmTaskSchedulerService");
+ thread.start();
+
+ mWorkerThreadHandler = new WorkerThreadHandler(thread.getLooper());
+ mMainThreadHandler = new MainThreadHandler(Looper.getMainLooper());
+ }
+
+ @Override
+ public void onDestroy() {
+ mWorkerThreadHandler.getLooper().quit();
+ mWakeLock.release();
+ }
+
+ @Override
+ @MainThread
+ public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
+ Assert.isMainThread();
+ // maybeRunNextTask() will release the wakelock either by entering a long sleep or stopping
+ // the service.
+ mWakeLock.acquire();
+ if (ACTION_WAKEUP.equals(intent.getAction())) {
+ VvmLog.d(TAG, "woke up by AlarmManager");
+ } else {
+ Task task = createTask(intent, flags, startId);
+ if (task == null) {
+ VvmLog.e(TAG, "cannot create task form intent");
+ } else {
+ addTask(task);
+ }
+ }
+ maybeRunNextTask();
+ // STICKY means the service will be automatically restarted will the last intent if it is
+ // killed.
+ return START_NOT_STICKY;
+ }
+
+ @MainThread
+ @VisibleForTesting
+ void addTask(Task task) {
+ Assert.isMainThread();
+ if (task.getId().id == Task.TASK_INVALID) {
+ throw new AssertionError("Task id was not set to a valid value before adding.");
+ }
+ if (task.getId().id != Task.TASK_ALLOW_DUPLICATES) {
+ Task oldTask = getTask(task.getId());
+ if (oldTask != null) {
+ oldTask.onDuplicatedTaskAdded(task);
+ return;
+ }
+ }
+ mMainThreadHandler.removeCallbacks(mStopServiceWithDelay);
+ getTasks().add(task);
+ maybeRunNextTask();
+ }
+
+ @MainThread
+ @Nullable
+ private Task getTask(TaskId taskId) {
+ Assert.isMainThread();
+ for (Task task : getTasks()) {
+ if (task.getId().equals(taskId)) {
+ return task;
+ }
+ }
+ return null;
+ }
+
+ @MainThread
+ private Queue<Task> getTasks() {
+ Assert.isMainThread();
+ return mTasks;
+ }
+
+ /**
+ * Create an intent that will queue the <code>task</code>
+ */
+ public static Intent createIntent(Context context, Class<? extends Task> task) {
+ Intent intent = new Intent(context, TaskSchedulerService.class);
+ intent.putExtra(EXTRA_CLASS_NAME, task.getName());
+ return intent;
+ }
+
+ @VisibleForTesting
+ @MainThread
+ @Nullable
+ Task createTask(@Nullable Intent intent, int flags, int startId) {
+ Assert.isMainThread();
+ if (intent == null) {
+ return null;
+ }
+ String className = intent.getStringExtra(EXTRA_CLASS_NAME);
+ VvmLog.d(TAG, "create task:" + className);
+ if (className == null) {
+ throw new IllegalArgumentException("EXTRA_CLASS_NAME expected");
+ }
+ try {
+ Task task = (Task) Class.forName(className).newInstance();
+ task.onCreate(mContext, intent, flags, startId);
+ return task;
+ } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @MainThread
+ private void maybeRunNextTask() {
+ Assert.isMainThread();
+ if (mWorkerThreadIsBusy) {
+ return;
+ }
+ if (mTaskAutoRunDisabledForTesting) {
+ // If mTaskAutoRunDisabledForTesting is true, runNextTask() must be explicitly called
+ // to run the next task.
+ return;
+ }
+
+ runNextTask();
+ }
+
+ @VisibleForTesting
+ @MainThread
+ void runNextTask() {
+ Assert.isMainThread();
+ // The current alarm is no longer valid, a new one will be set up if required.
+ getSystemService(AlarmManager.class).cancel(getWakeupIntent());
+ if (getTasks().isEmpty()) {
+ prepareStop();
+ return;
+ }
+ Long minimalWaitTime = null;
+ for (Task task : getTasks()) {
+ long waitTime = task.getReadyInMilliSeconds();
+ if (waitTime < READY_TOLERANCE_MILLISECONDS) {
+ task.onBeforeExecute();
+ Message message = mWorkerThreadHandler.obtainMessage();
+ message.obj = task;
+ mWorkerThreadIsBusy = true;
+ mMessageSender.send(message);
+ return;
+ } else {
+ if (minimalWaitTime == null || waitTime < minimalWaitTime) {
+ minimalWaitTime = waitTime;
+ }
+ }
+ }
+ VvmLog.d(TAG, "minimal wait time:" + minimalWaitTime);
+ if (!mTaskAutoRunDisabledForTesting && minimalWaitTime != null) {
+ // No tasks are currently ready. Sleep until the next one should be.
+ // If a new task is added during the sleep the service will wake immediately.
+ sleep(minimalWaitTime);
+ }
+ }
+
+ private void sleep(long timeMillis) {
+ if (timeMillis < SHORT_SLEEP_THRESHOLD_MILLISECONDS) {
+ mMainThreadHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ maybeRunNextTask();
+ }
+ }, timeMillis);
+ return;
+ }
+
+ // Tasks does not have a strict timing requirement, use AlarmManager.set() so the OS could
+ // optimize the battery usage. As this service currently run in the telephony process the
+ // OS give it privileges to behave the same as setExact(), but set() is the targeted
+ // behavior once this is unbundled.
+ getSystemService(AlarmManager.class).set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + timeMillis,
+ getWakeupIntent());
+ mWakeLock.release();
+ VvmLog.d(TAG, "Long sleep for " + timeMillis + " millis");
+ }
+
+ private PendingIntent getWakeupIntent() {
+ Intent intent = new Intent(ACTION_WAKEUP, null, this, getClass());
+ return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+ }
+
+ private void prepareStop() {
+ VvmLog.d(TAG,
+ "No more tasks, stopping service if no task are added in "
+ + STOP_DELAY_MILLISECONDS + " millis");
+ mMainThreadHandler.postDelayed(mStopServiceWithDelay, STOP_DELAY_MILLISECONDS);
+ }
+
+ static class MessageSender {
+
+ public void send(Message message) {
+ message.sendToTarget();
+ }
+ }
+
+ @NeededForTesting
+ void setContextForTest(Context context) {
+ mContext = context;
+ }
+
+ @NeededForTesting
+ void setTaskAutoRunDisabledForTest(boolean value) {
+ mTaskAutoRunDisabledForTesting = value;
+ }
+
+ @NeededForTesting
+ void setMessageSenderForTest(MessageSender sender) {
+ mMessageSender = sender;
+ }
+
+ @NeededForTesting
+ void clearTasksForTest() {
+ mTasks.clear();
+ }
+
+ @Override
+ @Nullable
+ public IBinder onBind(Intent intent) {
+ return new LocalBinder();
+ }
+
+ @NeededForTesting
+ class LocalBinder extends Binder {
+
+ @NeededForTesting
+ public TaskSchedulerService getService() {
+ return TaskSchedulerService.this;
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/settings/VisualVoicemailSettingsUtil.java b/java/com/android/voicemailomtp/settings/VisualVoicemailSettingsUtil.java
new file mode 100644
index 000000000..5cec52842
--- /dev/null
+++ b/java/com/android/voicemailomtp/settings/VisualVoicemailSettingsUtil.java
@@ -0,0 +1,77 @@
+/*
+ * 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.voicemailomtp.settings;
+
+import android.content.Context;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.VisualVoicemailPreferences;
+import com.android.voicemailomtp.sync.OmtpVvmSourceManager;
+
+/**
+ * Save whether or not a particular account is enabled in shared to be retrieved later.
+ */
+public class VisualVoicemailSettingsUtil {
+
+ private static final String IS_ENABLED_KEY = "is_enabled";
+
+
+ public static void setEnabled(Context context, PhoneAccountHandle phoneAccount,
+ boolean isEnabled) {
+ new VisualVoicemailPreferences(context, phoneAccount).edit()
+ .putBoolean(IS_ENABLED_KEY, isEnabled)
+ .apply();
+ OmtpVvmCarrierConfigHelper config = new OmtpVvmCarrierConfigHelper(context, phoneAccount);
+ if (isEnabled) {
+ OmtpVvmSourceManager.getInstance(context).addPhoneStateListener(phoneAccount);
+ config.startActivation();
+ } else {
+ OmtpVvmSourceManager.getInstance(context).removeSource(phoneAccount);
+ config.startDeactivation();
+ }
+ }
+
+ public static boolean isEnabled(Context context,
+ PhoneAccountHandle phoneAccount) {
+ if (phoneAccount == null) {
+ return false;
+ }
+
+ VisualVoicemailPreferences prefs = new VisualVoicemailPreferences(context, phoneAccount);
+ if (prefs.contains(IS_ENABLED_KEY)) {
+ // isEnableByDefault is a bit expensive, so don't use it as default value of
+ // getBoolean(). The "false" here should never be actually used.
+ return prefs.getBoolean(IS_ENABLED_KEY, false);
+ }
+ return new OmtpVvmCarrierConfigHelper(context, phoneAccount).isEnabledByDefault();
+ }
+
+ /**
+ * Whether the client enabled status is explicitly set by user or by default(Whether carrier VVM
+ * app is installed). This is used to determine whether to disable the client when the carrier
+ * VVM app is installed. If the carrier VVM app is installed the client should give priority to
+ * it if the settings are not touched.
+ */
+ public static boolean isEnabledUserSet(Context context,
+ PhoneAccountHandle phoneAccount) {
+ if (phoneAccount == null) {
+ return false;
+ }
+ VisualVoicemailPreferences prefs = new VisualVoicemailPreferences(context, phoneAccount);
+ return prefs.contains(IS_ENABLED_KEY);
+ }
+}
diff --git a/java/com/android/voicemailomtp/settings/VoicemailChangePinActivity.java b/java/com/android/voicemailomtp/settings/VoicemailChangePinActivity.java
new file mode 100644
index 000000000..e679e9970
--- /dev/null
+++ b/java/com/android/voicemailomtp/settings/VoicemailChangePinActivity.java
@@ -0,0 +1,634 @@
+/*
+ * 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.voicemailomtp.settings;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
+import android.net.Network;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.InputFilter.LengthFilter;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.WindowManager;
+import android.view.inputmethod.EditorInfo;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+import android.widget.Toast;
+import com.android.voicemailomtp.OmtpConstants;
+import com.android.voicemailomtp.OmtpConstants.ChangePinResult;
+import com.android.voicemailomtp.OmtpEvents;
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.R;
+import com.android.voicemailomtp.VisualVoicemailPreferences;
+import com.android.voicemailomtp.VoicemailStatus;
+import com.android.voicemailomtp.VvmLog;
+import com.android.voicemailomtp.imap.ImapHelper;
+import com.android.voicemailomtp.imap.ImapHelper.InitializingException;
+import com.android.voicemailomtp.mail.MessagingException;
+import com.android.voicemailomtp.sync.VvmNetworkRequestCallback;
+
+/**
+ * Dialog to change the voicemail PIN. The TUI (Telephony User Interface) PIN is used when accessing
+ * traditional voicemail through phone call. The intent to launch this activity must contain {@link
+ * #EXTRA_PHONE_ACCOUNT_HANDLE}
+ */
+@TargetApi(VERSION_CODES.CUR_DEVELOPMENT)
+public class VoicemailChangePinActivity extends Activity
+ implements OnClickListener, OnEditorActionListener, TextWatcher {
+
+ private static final String TAG = "VmChangePinActivity";
+
+ public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "extra_phone_account_handle";
+
+ private static final String KEY_DEFAULT_OLD_PIN = "default_old_pin";
+
+ private static final int MESSAGE_HANDLE_RESULT = 1;
+
+ private PhoneAccountHandle mPhoneAccountHandle;
+ private OmtpVvmCarrierConfigHelper mConfig;
+
+ private int mPinMinLength;
+ private int mPinMaxLength;
+
+ private State mUiState = State.Initial;
+ private String mOldPin;
+ private String mFirstPin;
+
+ private ProgressDialog mProgressDialog;
+
+ private TextView mHeaderText;
+ private TextView mHintText;
+ private TextView mErrorText;
+ private EditText mPinEntry;
+ private Button mCancelButton;
+ private Button mNextButton;
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == MESSAGE_HANDLE_RESULT) {
+ mUiState.handleResult(VoicemailChangePinActivity.this, message.arg1);
+ }
+ }
+ };
+
+ private enum State {
+ /**
+ * Empty state to handle initial state transition. Will immediately switch into {@link
+ * #VerifyOldPin} if a default PIN has been set by the OMTP client, or {@link #EnterOldPin}
+ * if not.
+ */
+ Initial,
+ /**
+ * Prompt the user to enter old PIN. The PIN will be verified with the server before
+ * proceeding to {@link #EnterNewPin}.
+ */
+ EnterOldPin {
+ @Override
+ public void onEnter(VoicemailChangePinActivity activity) {
+ activity.setHeader(R.string.change_pin_enter_old_pin_header);
+ activity.mHintText.setText(R.string.change_pin_enter_old_pin_hint);
+ activity.mNextButton.setText(R.string.change_pin_continue_label);
+ activity.mErrorText.setText(null);
+ }
+
+ @Override
+ public void onInputChanged(VoicemailChangePinActivity activity) {
+ activity.setNextEnabled(activity.getCurrentPasswordInput().length() > 0);
+ }
+
+
+ @Override
+ public void handleNext(VoicemailChangePinActivity activity) {
+ activity.mOldPin = activity.getCurrentPasswordInput();
+ activity.verifyOldPin();
+ }
+
+ @Override
+ public void handleResult(VoicemailChangePinActivity activity,
+ @ChangePinResult int result) {
+ if (result == OmtpConstants.CHANGE_PIN_SUCCESS) {
+ activity.updateState(State.EnterNewPin);
+ } else {
+ CharSequence message = activity.getChangePinResultMessage(result);
+ activity.showError(message);
+ activity.mPinEntry.setText("");
+ }
+ }
+ },
+ /**
+ * The default old PIN is found. Show a blank screen while verifying with the server to make
+ * sure the PIN is still valid. If the PIN is still valid, proceed to {@link #EnterNewPin}.
+ * If not, the user probably changed the PIN through other means, proceed to {@link
+ * #EnterOldPin}. If any other issue caused the verifying to fail, show an error and exit.
+ */
+ VerifyOldPin {
+ @Override
+ public void onEnter(VoicemailChangePinActivity activity) {
+ activity.findViewById(android.R.id.content).setVisibility(View.INVISIBLE);
+ activity.verifyOldPin();
+ }
+
+ @Override
+ public void handleResult(final VoicemailChangePinActivity activity,
+ @ChangePinResult int result) {
+ if (result == OmtpConstants.CHANGE_PIN_SUCCESS) {
+ activity.updateState(State.EnterNewPin);
+ } else if (result == OmtpConstants.CHANGE_PIN_SYSTEM_ERROR) {
+ activity.getWindow().setSoftInputMode(
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
+ activity.showError(activity.getString(R.string.change_pin_system_error),
+ new OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ activity.finish();
+ }
+ });
+ } else {
+ VvmLog.e(TAG, "invalid default old PIN: " + activity
+ .getChangePinResultMessage(result));
+ // If the default old PIN is rejected by the server, the PIN is probably changed
+ // through other means, or the generated pin is invalid
+ // Wipe the default old PIN so the old PIN input box will be shown to the user
+ // on the next time.
+ setDefaultOldPIN(activity, activity.mPhoneAccountHandle, null);
+ activity.handleOmtpEvent(OmtpEvents.CONFIG_PIN_SET);
+ activity.updateState(State.EnterOldPin);
+ }
+ }
+
+ @Override
+ public void onLeave(VoicemailChangePinActivity activity) {
+ activity.findViewById(android.R.id.content).setVisibility(View.VISIBLE);
+ }
+ },
+ /**
+ * Let the user enter the new PIN and validate the format. Only length is enforced, PIN
+ * strength check relies on the server. After a valid PIN is entered, proceed to {@link
+ * #ConfirmNewPin}
+ */
+ EnterNewPin {
+ @Override
+ public void onEnter(VoicemailChangePinActivity activity) {
+ activity.mHeaderText.setText(R.string.change_pin_enter_new_pin_header);
+ activity.mNextButton.setText(R.string.change_pin_continue_label);
+ activity.mHintText.setText(
+ activity.getString(R.string.change_pin_enter_new_pin_hint,
+ activity.mPinMinLength, activity.mPinMaxLength));
+ }
+
+ @Override
+ public void onInputChanged(VoicemailChangePinActivity activity) {
+ String password = activity.getCurrentPasswordInput();
+ if (password.length() == 0) {
+ activity.setNextEnabled(false);
+ return;
+ }
+ CharSequence error = activity.validatePassword(password);
+ if (error != null) {
+ activity.mErrorText.setText(error);
+ activity.setNextEnabled(false);
+ } else {
+ activity.mErrorText.setText(null);
+ activity.setNextEnabled(true);
+ }
+ }
+
+ @Override
+ public void handleNext(VoicemailChangePinActivity activity) {
+ CharSequence errorMsg;
+ errorMsg = activity.validatePassword(activity.getCurrentPasswordInput());
+ if (errorMsg != null) {
+ activity.showError(errorMsg);
+ return;
+ }
+ activity.mFirstPin = activity.getCurrentPasswordInput();
+ activity.updateState(State.ConfirmNewPin);
+ }
+ },
+ /**
+ * Let the user type in the same PIN again to avoid typos. If the PIN matches then perform a
+ * PIN change to the server. Finish the activity if succeeded. Return to {@link
+ * #EnterOldPin} if the old PIN is rejected, {@link #EnterNewPin} for other failure.
+ */
+ ConfirmNewPin {
+ @Override
+ public void onEnter(VoicemailChangePinActivity activity) {
+ activity.mHeaderText.setText(R.string.change_pin_confirm_pin_header);
+ activity.mHintText.setText(null);
+ activity.mNextButton.setText(R.string.change_pin_ok_label);
+ }
+
+ @Override
+ public void onInputChanged(VoicemailChangePinActivity activity) {
+ if (activity.getCurrentPasswordInput().length() == 0) {
+ activity.setNextEnabled(false);
+ return;
+ }
+ if (activity.getCurrentPasswordInput().equals(activity.mFirstPin)) {
+ activity.setNextEnabled(true);
+ activity.mErrorText.setText(null);
+ } else {
+ activity.setNextEnabled(false);
+ activity.mErrorText.setText(R.string.change_pin_confirm_pins_dont_match);
+ }
+ }
+
+ @Override
+ public void handleResult(VoicemailChangePinActivity activity,
+ @ChangePinResult int result) {
+ if (result == OmtpConstants.CHANGE_PIN_SUCCESS) {
+ // If the PIN change succeeded we no longer know what the old (current) PIN is.
+ // Wipe the default old PIN so the old PIN input box will be shown to the user
+ // on the next time.
+ setDefaultOldPIN(activity, activity.mPhoneAccountHandle, null);
+ activity.handleOmtpEvent(OmtpEvents.CONFIG_PIN_SET);
+
+ activity.finish();
+
+ Toast.makeText(activity, activity.getString(R.string.change_pin_succeeded),
+ Toast.LENGTH_SHORT).show();
+ } else {
+ CharSequence message = activity.getChangePinResultMessage(result);
+ VvmLog.i(TAG, "Change PIN failed: " + message);
+ activity.showError(message);
+ if (result == OmtpConstants.CHANGE_PIN_MISMATCH) {
+ // Somehow the PIN has changed, prompt to enter the old PIN again.
+ activity.updateState(State.EnterOldPin);
+ } else {
+ // The new PIN failed to fulfil other restrictions imposed by the server.
+ activity.updateState(State.EnterNewPin);
+ }
+
+ }
+
+ }
+
+ @Override
+ public void handleNext(VoicemailChangePinActivity activity) {
+ activity.processPinChange(activity.mOldPin, activity.mFirstPin);
+ }
+ };
+
+ /**
+ * The activity has switched from another state to this one.
+ */
+ public void onEnter(VoicemailChangePinActivity activity) {
+ // Do nothing
+ }
+
+ /**
+ * The user has typed something into the PIN input field. Also called after {@link
+ * #onEnter(VoicemailChangePinActivity)}
+ */
+ public void onInputChanged(VoicemailChangePinActivity activity) {
+ // Do nothing
+ }
+
+ /**
+ * The asynchronous call to change the PIN on the server has returned.
+ */
+ public void handleResult(VoicemailChangePinActivity activity, @ChangePinResult int result) {
+ // Do nothing
+ }
+
+ /**
+ * The user has pressed the "next" button.
+ */
+ public void handleNext(VoicemailChangePinActivity activity) {
+ // Do nothing
+ }
+
+ /**
+ * The activity has switched from this state to another one.
+ */
+ public void onLeave(VoicemailChangePinActivity activity) {
+ // Do nothing
+ }
+
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mPhoneAccountHandle = getIntent().getParcelableExtra(EXTRA_PHONE_ACCOUNT_HANDLE);
+ mConfig = new OmtpVvmCarrierConfigHelper(this, mPhoneAccountHandle);
+ setContentView(R.layout.voicemail_change_pin);
+ setTitle(R.string.change_pin_title);
+
+ readPinLength();
+
+ View view = findViewById(android.R.id.content);
+
+ mCancelButton = (Button) view.findViewById(R.id.cancel_button);
+ mCancelButton.setOnClickListener(this);
+ mNextButton = (Button) view.findViewById(R.id.next_button);
+ mNextButton.setOnClickListener(this);
+
+ mPinEntry = (EditText) view.findViewById(R.id.pin_entry);
+ mPinEntry.setOnEditorActionListener(this);
+ mPinEntry.addTextChangedListener(this);
+ if (mPinMaxLength != 0) {
+ mPinEntry.setFilters(new InputFilter[]{new LengthFilter(mPinMaxLength)});
+ }
+
+
+ mHeaderText = (TextView) view.findViewById(R.id.headerText);
+ mHintText = (TextView) view.findViewById(R.id.hintText);
+ mErrorText = (TextView) view.findViewById(R.id.errorText);
+
+ if (isDefaultOldPinSet(this, mPhoneAccountHandle)) {
+ mOldPin = getDefaultOldPin(this, mPhoneAccountHandle);
+ updateState(State.VerifyOldPin);
+ } else {
+ updateState(State.EnterOldPin);
+ }
+ }
+
+ private void handleOmtpEvent(OmtpEvents event) {
+ mConfig.handleEvent(getVoicemailStatusEditor(), event);
+ }
+
+ private VoicemailStatus.Editor getVoicemailStatusEditor() {
+ // This activity does not have any automatic retry mechanism, errors should be written right
+ // away.
+ return VoicemailStatus.edit(this, mPhoneAccountHandle);
+ }
+
+ /**
+ * Extracts the pin length requirement sent by the server with a STATUS SMS.
+ */
+ private void readPinLength() {
+ VisualVoicemailPreferences preferences = new VisualVoicemailPreferences(this,
+ mPhoneAccountHandle);
+ // The OMTP pin length format is {min}-{max}
+ String[] lengths = preferences.getString(OmtpConstants.TUI_PASSWORD_LENGTH, "").split("-");
+ if (lengths.length == 2) {
+ try {
+ mPinMinLength = Integer.parseInt(lengths[0]);
+ mPinMaxLength = Integer.parseInt(lengths[1]);
+ } catch (NumberFormatException e) {
+ mPinMinLength = 0;
+ mPinMaxLength = 0;
+ }
+ } else {
+ mPinMinLength = 0;
+ mPinMaxLength = 0;
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateState(mUiState);
+
+ }
+
+ public void handleNext() {
+ if (mPinEntry.length() == 0) {
+ return;
+ }
+ mUiState.handleNext(this);
+ }
+
+ public void onClick(View v) {
+ if (v.getId() == R.id.next_button) {
+ handleNext();
+ } else if (v.getId() == R.id.cancel_button) {
+ finish();
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (!mNextButton.isEnabled()) {
+ return true;
+ }
+ // Check if this was the result of hitting the enter or "done" key
+ if (actionId == EditorInfo.IME_NULL
+ || actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT) {
+ handleNext();
+ return true;
+ }
+ return false;
+ }
+
+ public void afterTextChanged(Editable s) {
+ mUiState.onInputChanged(this);
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // Do nothing
+ }
+
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ // Do nothing
+ }
+
+ /**
+ * After replacing the default PIN with a random PIN, call this to store the random PIN. The
+ * stored PIN will be automatically entered when the user attempts to change the PIN.
+ */
+ public static void setDefaultOldPIN(Context context, PhoneAccountHandle phoneAccountHandle,
+ String pin) {
+ new VisualVoicemailPreferences(context, phoneAccountHandle)
+ .edit().putString(KEY_DEFAULT_OLD_PIN, pin).apply();
+ }
+
+ public static boolean isDefaultOldPinSet(Context context,
+ PhoneAccountHandle phoneAccountHandle) {
+ return getDefaultOldPin(context, phoneAccountHandle) != null;
+ }
+
+ private static String getDefaultOldPin(Context context, PhoneAccountHandle phoneAccountHandle) {
+ return new VisualVoicemailPreferences(context, phoneAccountHandle)
+ .getString(KEY_DEFAULT_OLD_PIN);
+ }
+
+ private String getCurrentPasswordInput() {
+ return mPinEntry.getText().toString();
+ }
+
+ private void updateState(State state) {
+ State previousState = mUiState;
+ mUiState = state;
+ if (previousState != state) {
+ previousState.onLeave(this);
+ mPinEntry.setText("");
+ mUiState.onEnter(this);
+ }
+ mUiState.onInputChanged(this);
+ }
+
+ /**
+ * Validates PIN and returns a message to display if PIN fails test.
+ *
+ * @param password the raw password the user typed in
+ * @return error message to show to user or null if password is OK
+ */
+ private CharSequence validatePassword(String password) {
+ if (mPinMinLength == 0 && mPinMaxLength == 0) {
+ // Invalid length requirement is sent by the server, just accept anything and let the
+ // server decide.
+ return null;
+ }
+
+ if (password.length() < mPinMinLength) {
+ return getString(R.string.vm_change_pin_error_too_short);
+ }
+ return null;
+ }
+
+ private void setHeader(int text) {
+ mHeaderText.setText(text);
+ mPinEntry.setContentDescription(mHeaderText.getText());
+ }
+
+ /**
+ * Get the corresponding message for the {@link ChangePinResult}.<code>result</code> must not
+ * {@link OmtpConstants#CHANGE_PIN_SUCCESS}
+ */
+ private CharSequence getChangePinResultMessage(@ChangePinResult int result) {
+ switch (result) {
+ case OmtpConstants.CHANGE_PIN_TOO_SHORT:
+ return getString(R.string.vm_change_pin_error_too_short);
+ case OmtpConstants.CHANGE_PIN_TOO_LONG:
+ return getString(R.string.vm_change_pin_error_too_long);
+ case OmtpConstants.CHANGE_PIN_TOO_WEAK:
+ return getString(R.string.vm_change_pin_error_too_weak);
+ case OmtpConstants.CHANGE_PIN_INVALID_CHARACTER:
+ return getString(R.string.vm_change_pin_error_invalid);
+ case OmtpConstants.CHANGE_PIN_MISMATCH:
+ return getString(R.string.vm_change_pin_error_mismatch);
+ case OmtpConstants.CHANGE_PIN_SYSTEM_ERROR:
+ return getString(R.string.vm_change_pin_error_system_error);
+ default:
+ VvmLog.wtf(TAG, "Unexpected ChangePinResult " + result);
+ return null;
+ }
+ }
+
+ private void verifyOldPin() {
+ processPinChange(mOldPin, mOldPin);
+ }
+
+ private void setNextEnabled(boolean enabled) {
+ mNextButton.setEnabled(enabled);
+ }
+
+
+ private void showError(CharSequence message) {
+ showError(message, null);
+ }
+
+ private void showError(CharSequence message, @Nullable OnDismissListener callback) {
+ new AlertDialog.Builder(this)
+ .setMessage(message)
+ .setPositiveButton(android.R.string.ok, null)
+ .setOnDismissListener(callback)
+ .show();
+ }
+
+ /**
+ * Asynchronous call to change the PIN on the server.
+ */
+ private void processPinChange(String oldPin, String newPin) {
+ mProgressDialog = new ProgressDialog(this);
+ mProgressDialog.setCancelable(false);
+ mProgressDialog.setMessage(getString(R.string.vm_change_pin_progress_message));
+ mProgressDialog.show();
+
+ ChangePinNetworkRequestCallback callback = new ChangePinNetworkRequestCallback(oldPin,
+ newPin);
+ callback.requestNetwork();
+ }
+
+ private class ChangePinNetworkRequestCallback extends VvmNetworkRequestCallback {
+
+ private final String mOldPin;
+ private final String mNewPin;
+
+ public ChangePinNetworkRequestCallback(String oldPin, String newPin) {
+ super(mConfig, mPhoneAccountHandle,
+ VoicemailChangePinActivity.this.getVoicemailStatusEditor());
+ mOldPin = oldPin;
+ mNewPin = newPin;
+ }
+
+ @Override
+ public void onAvailable(Network network) {
+ super.onAvailable(network);
+ try (ImapHelper helper =
+ new ImapHelper(VoicemailChangePinActivity.this, mPhoneAccountHandle, network,
+ getVoicemailStatusEditor())) {
+
+ @ChangePinResult int result =
+ helper.changePin(mOldPin, mNewPin);
+ sendResult(result);
+ } catch (InitializingException | MessagingException e) {
+ VvmLog.e(TAG, "ChangePinNetworkRequestCallback: onAvailable: ", e);
+ sendResult(OmtpConstants.CHANGE_PIN_SYSTEM_ERROR);
+ }
+ }
+
+ @Override
+ public void onFailed(String reason) {
+ super.onFailed(reason);
+ sendResult(OmtpConstants.CHANGE_PIN_SYSTEM_ERROR);
+ }
+
+ private void sendResult(@ChangePinResult int result) {
+ VvmLog.i(TAG, "Change PIN result: " + result);
+ if (mProgressDialog.isShowing() && !VoicemailChangePinActivity.this.isDestroyed() &&
+ !VoicemailChangePinActivity.this.isFinishing()) {
+ mProgressDialog.dismiss();
+ } else {
+ VvmLog.i(TAG, "Dialog not visible, not dismissing");
+ }
+ mHandler.obtainMessage(MESSAGE_HANDLE_RESULT, result, 0).sendToTarget();
+ releaseNetwork();
+ }
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/settings/VoicemailSettingsActivity.java b/java/com/android/voicemailomtp/settings/VoicemailSettingsActivity.java
new file mode 100644
index 000000000..ac0df6fab
--- /dev/null
+++ b/java/com/android/voicemailomtp/settings/VoicemailSettingsActivity.java
@@ -0,0 +1,222 @@
+/**
+ * Copyright (C) 2014 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.voicemailomtp.settings;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.preference.SwitchPreference;
+import android.telecom.PhoneAccountHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.MenuItem;
+
+import com.android.voicemailomtp.OmtpConstants;
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.R;
+import com.android.voicemailomtp.SubscriptionInfoHelper;
+import com.android.voicemailomtp.VisualVoicemailPreferences;
+
+public class VoicemailSettingsActivity extends PreferenceActivity implements
+ Preference.OnPreferenceChangeListener {
+ private static final String LOG_TAG = VoicemailSettingsActivity.class.getSimpleName();
+ private static final boolean DBG = true;
+
+ /**
+ * Intent action to bring up Voicemail Provider settings
+ * DO NOT RENAME. There are existing apps which use this intent value.
+ */
+ public static final String ACTION_ADD_VOICEMAIL =
+ "com.android.voicemailomtp.CallFeaturesSetting.ADD_VOICEMAIL";
+
+ /**
+ * Intent action to bring up the {@code VoicemailSettingsActivity}.
+ * DO NOT RENAME. There are existing apps which use this intent value.
+ */
+ public static final String ACTION_CONFIGURE_VOICEMAIL =
+ "com.android.voicemailomtp.CallFeaturesSetting.CONFIGURE_VOICEMAIL";
+
+ // Extra put in the return from VM provider config containing voicemail number to set
+ public static final String VM_NUMBER_EXTRA = "com.android.voicemailomtp.VoicemailNumber";
+ // Extra put in the return from VM provider config containing call forwarding number to set
+ public static final String FWD_NUMBER_EXTRA = "com.android.voicemailomtp.ForwardingNumber";
+ // Extra put in the return from VM provider config containing call forwarding number to set
+ public static final String FWD_NUMBER_TIME_EXTRA = "com.android.voicemailomtp.ForwardingNumberTime";
+ // If the VM provider returns non null value in this extra we will force the user to
+ // choose another VM provider
+ public static final String SIGNOUT_EXTRA = "com.android.voicemailomtp.Signout";
+
+ /**
+ * String Extra put into ACTION_ADD_VOICEMAIL call to indicate which provider should be hidden
+ * in the list of providers presented to the user. This allows a provider which is being
+ * disabled (e.g. GV user logging out) to force the user to pick some other provider.
+ */
+ public static final String IGNORE_PROVIDER_EXTRA = "com.android.voicemailomtp.ProviderToIgnore";
+
+ /**
+ * String Extra put into ACTION_ADD_VOICEMAIL to indicate that the voicemail setup screen should
+ * be opened.
+ */
+ public static final String SETUP_VOICEMAIL_EXTRA = "com.android.voicemailomtp.SetupVoicemail";
+
+ /** Event for Async voicemail change call */
+ private static final int EVENT_VOICEMAIL_CHANGED = 500;
+ private static final int EVENT_FORWARDING_CHANGED = 501;
+ private static final int EVENT_FORWARDING_GET_COMPLETED = 502;
+
+ /** Handle to voicemail pref */
+ private static final int VOICEMAIL_PREF_ID = 1;
+ private static final int VOICEMAIL_PROVIDER_CFG_ID = 2;
+
+ /**
+ * Used to indicate that the voicemail preference should be shown.
+ */
+ private boolean mShowVoicemailPreference = false;
+
+ private int mSubId;
+ private PhoneAccountHandle mPhoneAccountHandle;
+ private SubscriptionInfoHelper mSubscriptionInfoHelper;
+ private OmtpVvmCarrierConfigHelper mOmtpVvmCarrierConfigHelper;
+
+ private SwitchPreference mVoicemailVisualVoicemail;
+ private Preference mVoicemailChangePinPreference;
+
+ //*********************************************************************************************
+ // Preference Activity Methods
+ //*********************************************************************************************
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Show the voicemail preference in onResume if the calling intent specifies the
+ // ACTION_ADD_VOICEMAIL action.
+ mShowVoicemailPreference = (icicle == null) &&
+ TextUtils.equals(getIntent().getAction(), ACTION_ADD_VOICEMAIL);
+
+ mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent());
+ mSubscriptionInfoHelper.setActionBarTitle(
+ getActionBar(), getResources(), R.string.voicemail_settings_with_label);
+ mSubId = mSubscriptionInfoHelper.getSubId();
+ // TODO: scrap this activity.
+ /*
+ mPhoneAccountHandle = PhoneAccountHandleConverter
+ .fromSubId(this, mSubId);
+
+ mOmtpVvmCarrierConfigHelper = new OmtpVvmCarrierConfigHelper(
+ this, mSubId);
+ */
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ PreferenceScreen preferenceScreen = getPreferenceScreen();
+ if (preferenceScreen != null) {
+ preferenceScreen.removeAll();
+ }
+
+ addPreferencesFromResource(R.xml.voicemail_settings);
+
+ PreferenceScreen prefSet = getPreferenceScreen();
+
+ mVoicemailVisualVoicemail = (SwitchPreference) findPreference(
+ getResources().getString(R.string.voicemail_visual_voicemail_key));
+
+ mVoicemailChangePinPreference = findPreference(
+ getResources().getString(R.string.voicemail_change_pin_key));
+ Intent changePinIntent = new Intent(new Intent(this, VoicemailChangePinActivity.class));
+ changePinIntent.putExtra(VoicemailChangePinActivity.EXTRA_PHONE_ACCOUNT_HANDLE,
+ mPhoneAccountHandle);
+
+ mVoicemailChangePinPreference.setIntent(changePinIntent);
+ if (VoicemailChangePinActivity.isDefaultOldPinSet(this, mPhoneAccountHandle)) {
+ mVoicemailChangePinPreference.setTitle(R.string.voicemail_set_pin_dialog_title);
+ } else {
+ mVoicemailChangePinPreference.setTitle(R.string.voicemail_change_pin_dialog_title);
+ }
+
+ if (mOmtpVvmCarrierConfigHelper.isValid()) {
+ mVoicemailVisualVoicemail.setOnPreferenceChangeListener(this);
+ mVoicemailVisualVoicemail.setChecked(
+ VisualVoicemailSettingsUtil.isEnabled(this, mPhoneAccountHandle));
+ if (!isVisualVoicemailActivated()) {
+ prefSet.removePreference(mVoicemailChangePinPreference);
+ }
+ } else {
+ prefSet.removePreference(mVoicemailVisualVoicemail);
+ prefSet.removePreference(mVoicemailChangePinPreference);
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * Implemented to support onPreferenceChangeListener to look for preference changes.
+ *
+ * @param preference is the preference to be changed
+ * @param objValue should be the value of the selection, NOT its localized
+ * display value.
+ */
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object objValue) {
+ if (DBG) log("onPreferenceChange: \"" + preference + "\" changed to \"" + objValue + "\"");
+ if (preference.getKey().equals(mVoicemailVisualVoicemail.getKey())) {
+ boolean isEnabled = (boolean) objValue;
+ VisualVoicemailSettingsUtil
+ .setEnabled(this, mPhoneAccountHandle, isEnabled);
+ PreferenceScreen prefSet = getPreferenceScreen();
+ if (isVisualVoicemailActivated()) {
+ prefSet.addPreference(mVoicemailChangePinPreference);
+ } else {
+ prefSet.removePreference(mVoicemailChangePinPreference);
+ }
+ }
+
+ // Always let the preference setting proceed.
+ return true;
+ }
+
+ private boolean isVisualVoicemailActivated() {
+ if (!VisualVoicemailSettingsUtil.isEnabled(this, mPhoneAccountHandle)) {
+ return false;
+ }
+ VisualVoicemailPreferences preferences = new VisualVoicemailPreferences(this,
+ mPhoneAccountHandle);
+ return preferences.getString(OmtpConstants.SERVER_ADDRESS, null) != null;
+
+ }
+
+ private static void log(String msg) {
+ Log.d(LOG_TAG, msg);
+ }
+}
diff --git a/java/com/android/voicemailomtp/sms/LegacyModeSmsHandler.java b/java/com/android/voicemailomtp/sms/LegacyModeSmsHandler.java
new file mode 100644
index 000000000..bb722bffc
--- /dev/null
+++ b/java/com/android/voicemailomtp/sms/LegacyModeSmsHandler.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 Google Inc. All Rights Reserved.
+ *
+ * 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.voicemailomtp.sms;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.VisualVoicemailSms;
+
+import com.android.voicemailomtp.OmtpConstants;
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.TelephonyManagerStub;
+import com.android.voicemailomtp.VvmLog;
+
+/**
+ * Class ot handle voicemail SMS under legacy mode
+ *
+ * @see OmtpVvmCarrierConfigHelper#isLegacyModeEnabled()
+ */
+public class LegacyModeSmsHandler {
+
+ private static final String TAG = "LegacyModeSmsHandler";
+
+ public static void handle(Context context, VisualVoicemailSms sms) {
+ VvmLog.v(TAG, "processing VVM SMS on legacy mode");
+ String eventType = sms.getPrefix();
+ Bundle data = sms.getFields();
+ PhoneAccountHandle handle = sms.getPhoneAccountHandle();
+
+ if (eventType.equals(OmtpConstants.SYNC_SMS_PREFIX)) {
+ SyncMessage message = new SyncMessage(data);
+ VvmLog.v(TAG, "Received SYNC sms for " + handle +
+ " with event " + message.getSyncTriggerEvent());
+
+ switch (message.getSyncTriggerEvent()) {
+ case OmtpConstants.NEW_MESSAGE:
+ case OmtpConstants.MAILBOX_UPDATE:
+ // The user has called into the voicemail and the new message count could
+ // change.
+ // For some carriers new message count could be set to 0 even if there are still
+ // unread messages, to clear the message waiting indicator.
+ VvmLog.v(TAG, "updating MWI");
+
+ // Setting voicemail message count to non-zero will show the telephony voicemail
+ // notification, and zero will clear it.
+ TelephonyManagerStub.showVoicemailNotification(message.getNewMessageCount());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/sms/OmtpCvvmMessageSender.java b/java/com/android/voicemailomtp/sms/OmtpCvvmMessageSender.java
new file mode 100644
index 000000000..63af2c13d
--- /dev/null
+++ b/java/com/android/voicemailomtp/sms/OmtpCvvmMessageSender.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.voicemailomtp.sms;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.SmsManager;
+import com.android.voicemailomtp.OmtpConstants;
+
+/**
+ * An implementation of the OmtpMessageSender for T-Mobile.
+ */
+public class OmtpCvvmMessageSender extends OmtpMessageSender {
+ public OmtpCvvmMessageSender(Context context, PhoneAccountHandle phoneAccountHandle,
+ short applicationPort, String destinationNumber) {
+ super(context, phoneAccountHandle, applicationPort, destinationNumber);
+ }
+
+ @Override
+ public void requestVvmActivation(@Nullable PendingIntent sentIntent) {
+ sendCvvmMessage(OmtpConstants.ACTIVATE_REQUEST, sentIntent);
+ }
+
+ @Override
+ public void requestVvmDeactivation(@Nullable PendingIntent sentIntent) {
+ sendCvvmMessage(OmtpConstants.DEACTIVATE_REQUEST, sentIntent);
+ }
+
+ @Override
+ public void requestVvmStatus(@Nullable PendingIntent sentIntent) {
+ sendCvvmMessage(OmtpConstants.STATUS_REQUEST, sentIntent);
+ }
+
+ private void sendCvvmMessage(String request, PendingIntent sentIntent) {
+ StringBuilder sb = new StringBuilder().append(request);
+ sb.append(OmtpConstants.SMS_PREFIX_SEPARATOR);
+ appendField(sb, "dt", "15");
+ sendSms(sb.toString(), sentIntent);
+ }
+}
diff --git a/java/com/android/voicemailomtp/sms/OmtpMessageReceiver.java b/java/com/android/voicemailomtp/sms/OmtpMessageReceiver.java
new file mode 100644
index 000000000..c4ad2085f
--- /dev/null
+++ b/java/com/android/voicemailomtp/sms/OmtpMessageReceiver.java
@@ -0,0 +1,162 @@
+/*
+ * 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.voicemailomtp.sms;
+
+import android.annotation.TargetApi;
+import android.content.BroadcastReceiver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.VisualVoicemailSms;
+import com.android.voicemailomtp.ActivationTask;
+import com.android.voicemailomtp.OmtpConstants;
+import com.android.voicemailomtp.OmtpService;
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.Voicemail;
+import com.android.voicemailomtp.VvmLog;
+import com.android.voicemailomtp.protocol.VisualVoicemailProtocol;
+import com.android.voicemailomtp.settings.VisualVoicemailSettingsUtil;
+import com.android.voicemailomtp.sync.OmtpVvmSyncService;
+import com.android.voicemailomtp.sync.SyncOneTask;
+import com.android.voicemailomtp.sync.SyncTask;
+import com.android.voicemailomtp.sync.VoicemailsQueryHelper;
+import com.android.voicemailomtp.utils.VoicemailDatabaseUtil;
+
+/** Receive SMS messages and send for processing by the OMTP visual voicemail source. */
+@TargetApi(VERSION_CODES.CUR_DEVELOPMENT)
+public class OmtpMessageReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "OmtpMessageReceiver";
+
+ private Context mContext;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mContext = context;
+ VisualVoicemailSms sms = intent.getExtras().getParcelable(OmtpService.EXTRA_VOICEMAIL_SMS);
+ PhoneAccountHandle phone = sms.getPhoneAccountHandle();
+
+ if (phone == null) {
+ // This should never happen
+ VvmLog.i(TAG, "Received message for null phone account");
+ return;
+ }
+
+ if (!context.getSystemService(UserManager.class).isUserUnlocked()) {
+ VvmLog.i(TAG, "Received message on locked device");
+ // LegacyModeSmsHandler can handle new message notifications without storage access
+ LegacyModeSmsHandler.handle(context, sms);
+ // A full sync will happen after the device is unlocked, so nothing else need to be
+ // done.
+ return;
+ }
+
+ OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(mContext, phone);
+ if (!VisualVoicemailSettingsUtil.isEnabled(mContext, phone)) {
+ if (helper.isLegacyModeEnabled()) {
+ LegacyModeSmsHandler.handle(context, sms);
+ } else {
+ VvmLog.i(TAG, "Received vvm message for disabled vvm source.");
+ }
+ return;
+ }
+
+ String eventType = sms.getPrefix();
+ Bundle data = sms.getFields();
+
+ if (eventType == null || data == null) {
+ VvmLog.e(TAG, "Unparsable VVM SMS received, ignoring");
+ return;
+ }
+
+ if (eventType.equals(OmtpConstants.SYNC_SMS_PREFIX)) {
+ SyncMessage message = new SyncMessage(data);
+
+ VvmLog.v(TAG, "Received SYNC sms for " + phone +
+ " with event " + message.getSyncTriggerEvent());
+ processSync(phone, message);
+ } else if (eventType.equals(OmtpConstants.STATUS_SMS_PREFIX)) {
+ VvmLog.v(TAG, "Received Status sms for " + phone);
+ // If the STATUS SMS is initiated by ActivationTask the TaskSchedulerService will reject
+ // the follow request. Providing the data will also prevent ActivationTask from
+ // requesting another STATUS SMS. The following task will only run if the carrier
+ // spontaneous send a STATUS SMS, in that case, the VVM service should be reactivated.
+ ActivationTask.start(context, phone, data);
+ } else {
+ VvmLog.w(TAG, "Unknown prefix: " + eventType);
+ VisualVoicemailProtocol protocol = helper.getProtocol();
+ if (protocol == null) {
+ return;
+ }
+ Bundle statusData = helper.getProtocol()
+ .translateStatusSmsBundle(helper, eventType, data);
+ if (statusData != null) {
+ VvmLog.i(TAG, "Protocol recognized the SMS as STATUS, activating");
+ ActivationTask.start(context, phone, data);
+ }
+ }
+ }
+
+ /**
+ * A sync message has two purposes: to signal a new voicemail message, and to indicate the
+ * voicemails on the server have changed remotely (usually through the TUI). Save the new
+ * message to the voicemail provider if it is the former case and perform a full sync in the
+ * latter case.
+ *
+ * @param message The sync message to extract data from.
+ */
+ private void processSync(PhoneAccountHandle phone, SyncMessage message) {
+ switch (message.getSyncTriggerEvent()) {
+ case OmtpConstants.NEW_MESSAGE:
+ if (!OmtpConstants.VOICE.equals(message.getContentType())) {
+ VvmLog.i(TAG, "Non-voice message of type '" + message.getContentType()
+ + "' received, ignoring");
+ return;
+ }
+
+ Voicemail.Builder builder = Voicemail.createForInsertion(
+ message.getTimestampMillis(), message.getSender())
+ .setPhoneAccount(phone)
+ .setSourceData(message.getId())
+ .setDuration(message.getLength())
+ .setSourcePackage(mContext.getPackageName());
+ Voicemail voicemail = builder.build();
+
+ VoicemailsQueryHelper queryHelper = new VoicemailsQueryHelper(mContext);
+ if (queryHelper.isVoicemailUnique(voicemail)) {
+ Uri uri = VoicemailDatabaseUtil.insert(mContext, voicemail);
+ voicemail = builder.setId(ContentUris.parseId(uri)).setUri(uri).build();
+ SyncOneTask.start(mContext, phone, voicemail);
+ }
+ break;
+ case OmtpConstants.MAILBOX_UPDATE:
+ SyncTask.start(mContext, phone, OmtpVvmSyncService.SYNC_DOWNLOAD_ONLY);
+ break;
+ case OmtpConstants.GREETINGS_UPDATE:
+ // Not implemented in V1
+ break;
+ default:
+ VvmLog.e(TAG,
+ "Unrecognized sync trigger event: " + message.getSyncTriggerEvent());
+ break;
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/sms/OmtpMessageSender.java b/java/com/android/voicemailomtp/sms/OmtpMessageSender.java
new file mode 100644
index 000000000..2323e4bcf
--- /dev/null
+++ b/java/com/android/voicemailomtp/sms/OmtpMessageSender.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.voicemailomtp.sms;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.SmsManager;
+import android.telephony.VisualVoicemailService;
+import com.android.voicemailomtp.OmtpConstants;
+import com.android.voicemailomtp.TelephonyManagerStub;
+import com.android.voicemailomtp.VvmLog;
+import java.io.UnsupportedEncodingException;
+import java.util.Locale;
+
+/**
+ * Send client originated OMTP messages to the OMTP server.
+ * <p>
+ * Uses {@link PendingIntent} instead of a call back to notify when the message is
+ * sent. This is primarily to keep the implementation simple and reuse what the underlying
+ * {@link SmsManager} interface provides.
+ * <p>
+ * Provides simple APIs to send different types of mobile originated OMTP SMS to the VVM server.
+ */
+public abstract class OmtpMessageSender {
+ protected static final String TAG = "OmtpMessageSender";
+ protected final Context mContext;
+ protected final PhoneAccountHandle mPhoneAccountHandle;
+ protected final short mApplicationPort;
+ protected final String mDestinationNumber;
+
+
+ public OmtpMessageSender(Context context, PhoneAccountHandle phoneAccountHandle,
+ short applicationPort,
+ String destinationNumber) {
+ mContext = context;
+ mPhoneAccountHandle = phoneAccountHandle;
+ mApplicationPort = applicationPort;
+ mDestinationNumber = destinationNumber;
+ }
+
+ /**
+ * Sends a request to the VVM server to activate VVM for the current subscriber.
+ *
+ * @param sentIntent If not NULL this PendingIntent is broadcast when the message is
+ * successfully sent, or failed.
+ */
+ public void requestVvmActivation(@Nullable PendingIntent sentIntent) {}
+
+ /**
+ * Sends a request to the VVM server to deactivate VVM for the current subscriber.
+ *
+ * @param sentIntent If not NULL this PendingIntent is broadcast when the message is
+ * successfully sent, or failed.
+ */
+ public void requestVvmDeactivation(@Nullable PendingIntent sentIntent) {}
+
+ /**
+ * Send a request to the VVM server to get account status of the current subscriber.
+ *
+ * @param sentIntent If not NULL this PendingIntent is broadcast when the message is
+ * successfully sent, or failed.
+ */
+ public void requestVvmStatus(@Nullable PendingIntent sentIntent) {}
+
+ protected void sendSms(String text, PendingIntent sentIntent) {
+ VisualVoicemailService
+ .sendVisualVoicemailSms(mContext, mPhoneAccountHandle, mDestinationNumber,
+ mApplicationPort, text, sentIntent);
+ }
+
+ protected void appendField(StringBuilder sb, String field, Object value) {
+ sb.append(field).append(OmtpConstants.SMS_KEY_VALUE_SEPARATOR).append(value);
+ }
+}
diff --git a/java/com/android/voicemailomtp/sms/OmtpStandardMessageSender.java b/java/com/android/voicemailomtp/sms/OmtpStandardMessageSender.java
new file mode 100644
index 000000000..aa8374781
--- /dev/null
+++ b/java/com/android/voicemailomtp/sms/OmtpStandardMessageSender.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.voicemailomtp.sms;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.SmsManager;
+import android.text.TextUtils;
+import com.android.voicemailomtp.OmtpConstants;
+
+/**
+ * A implementation of the OmtpMessageSender using the standard OMTP sms protocol.
+ */
+public class OmtpStandardMessageSender extends OmtpMessageSender {
+ private final String mClientType;
+ private final String mProtocolVersion;
+ private final String mClientPrefix;
+
+ /**
+ * Creates a new instance of OmtpStandardMessageSender.
+ *
+ * @param applicationPort If set to a value > 0 then a binary sms is sent to this port number.
+ * Otherwise, a standard text SMS is sent.
+ * @param destinationNumber Destination number to be used.
+ * @param clientType The "ct" field to be set in the MO message. This is the value used by the
+ * VVM server to identify the client. Certain VVM servers require a specific agreed
+ * value for this field.
+ * @param protocolVersion OMTP protocol version.
+ * @param clientPrefix The client prefix requested to be used by the server in its MT messages.
+ */
+ public OmtpStandardMessageSender(Context context, PhoneAccountHandle phoneAccountHandle,
+ short applicationPort,
+ String destinationNumber, String clientType, String protocolVersion,
+ String clientPrefix) {
+ super(context, phoneAccountHandle, applicationPort, destinationNumber);
+ mClientType = clientType;
+ mProtocolVersion = protocolVersion;
+ mClientPrefix = clientPrefix;
+ }
+
+ // Activate message:
+ // V1.1: Activate:pv=<value>;ct=<value>
+ // V1.2: Activate:pv=<value>;ct=<value>;pt=<value>;<Clientprefix>
+ // V1.3: Activate:pv=<value>;ct=<value>;pt=<value>;<Clientprefix>
+ @Override
+ public void requestVvmActivation(@Nullable PendingIntent sentIntent) {
+ StringBuilder sb = new StringBuilder().append(OmtpConstants.ACTIVATE_REQUEST);
+
+ appendProtocolVersionAndClientType(sb);
+ if (TextUtils.equals(mProtocolVersion, OmtpConstants.PROTOCOL_VERSION1_2) ||
+ TextUtils.equals(mProtocolVersion, OmtpConstants.PROTOCOL_VERSION1_3)) {
+ appendApplicationPort(sb);
+ appendClientPrefix(sb);
+ }
+
+ sendSms(sb.toString(), sentIntent);
+ }
+
+ // Deactivate message:
+ // V1.1: Deactivate:pv=<value>;ct=<string>
+ // V1.2: Deactivate:pv=<value>;ct=<string>
+ // V1.3: Deactivate:pv=<value>;ct=<string>
+ @Override
+ public void requestVvmDeactivation(@Nullable PendingIntent sentIntent) {
+ StringBuilder sb = new StringBuilder().append(OmtpConstants.DEACTIVATE_REQUEST);
+ appendProtocolVersionAndClientType(sb);
+
+ sendSms(sb.toString(), sentIntent);
+ }
+
+ // Status message:
+ // V1.1: STATUS
+ // V1.2: STATUS
+ // V1.3: STATUS:pv=<value>;ct=<value>;pt=<value>;<Clientprefix>
+ @Override
+ public void requestVvmStatus(@Nullable PendingIntent sentIntent) {
+ StringBuilder sb = new StringBuilder().append(OmtpConstants.STATUS_REQUEST);
+
+ if (TextUtils.equals(mProtocolVersion, OmtpConstants.PROTOCOL_VERSION1_3)) {
+ appendProtocolVersionAndClientType(sb);
+ appendApplicationPort(sb);
+ appendClientPrefix(sb);
+ }
+
+ sendSms(sb.toString(), sentIntent);
+ }
+
+ private void appendProtocolVersionAndClientType(StringBuilder sb) {
+ sb.append(OmtpConstants.SMS_PREFIX_SEPARATOR);
+ appendField(sb, OmtpConstants.PROTOCOL_VERSION, mProtocolVersion);
+ sb.append(OmtpConstants.SMS_FIELD_SEPARATOR);
+ appendField(sb, OmtpConstants.CLIENT_TYPE, mClientType);
+ }
+
+ private void appendApplicationPort(StringBuilder sb) {
+ sb.append(OmtpConstants.SMS_FIELD_SEPARATOR);
+ appendField(sb, OmtpConstants.APPLICATION_PORT, mApplicationPort);
+ }
+
+ private void appendClientPrefix(StringBuilder sb) {
+ sb.append(OmtpConstants.SMS_FIELD_SEPARATOR);
+ sb.append(mClientPrefix);
+ }
+}
diff --git a/java/com/android/voicemailomtp/sms/StatusMessage.java b/java/com/android/voicemailomtp/sms/StatusMessage.java
new file mode 100644
index 000000000..3dfd4973e
--- /dev/null
+++ b/java/com/android/voicemailomtp/sms/StatusMessage.java
@@ -0,0 +1,209 @@
+/*
+ * 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.voicemailomtp.sms;
+
+import android.os.Bundle;
+import com.android.voicemailomtp.NeededForTesting;
+import com.android.voicemailomtp.OmtpConstants;
+import com.android.voicemailomtp.VisualVoicemailPreferences;
+import com.android.voicemailomtp.VvmLog;
+
+/**
+ * Structured data representation of OMTP STATUS message.
+ *
+ * The getters will return null if the field was not set in the message body or it could not be
+ * parsed.
+ */
+public class StatusMessage {
+ // NOTE: Following Status SMS fields are not yet parsed, as they do not seem
+ // to be useful for initial omtp source implementation.
+ // lang, g_len, vs_len, pw_len, pm, gm, vtc, vt
+
+ private final String mProvisioningStatus;
+ private final String mStatusReturnCode;
+ private final String mSubscriptionUrl;
+ private final String mServerAddress;
+ private final String mTuiAccessNumber;
+ private final String mClientSmsDestinationNumber;
+ private final String mImapPort;
+ private final String mImapUserName;
+ private final String mImapPassword;
+ private final String mSmtpPort;
+ private final String mSmtpUserName;
+ private final String mSmtpPassword;
+ private final String mTuiPasswordLength;
+
+ @Override
+ public String toString() {
+ return "StatusMessage [mProvisioningStatus=" + mProvisioningStatus
+ + ", mStatusReturnCode=" + mStatusReturnCode
+ + ", mSubscriptionUrl=" + mSubscriptionUrl
+ + ", mServerAddress=" + mServerAddress
+ + ", mTuiAccessNumber=" + mTuiAccessNumber
+ + ", mClientSmsDestinationNumber=" + mClientSmsDestinationNumber
+ + ", mImapPort=" + mImapPort
+ + ", mImapUserName=" + mImapUserName
+ + ", mImapPassword=" + VvmLog.pii(mImapPassword)
+ + ", mSmtpPort=" + mSmtpPort
+ + ", mSmtpUserName=" + mSmtpUserName
+ + ", mSmtpPassword=" + VvmLog.pii(mSmtpPassword)
+ + ", mTuiPasswordLength=" + mTuiPasswordLength + "]";
+ }
+
+ public StatusMessage(Bundle wrappedData) {
+ mProvisioningStatus = unquote(getString(wrappedData, OmtpConstants.PROVISIONING_STATUS));
+ mStatusReturnCode = getString(wrappedData, OmtpConstants.RETURN_CODE);
+ mSubscriptionUrl = getString(wrappedData, OmtpConstants.SUBSCRIPTION_URL);
+ mServerAddress = getString(wrappedData, OmtpConstants.SERVER_ADDRESS);
+ mTuiAccessNumber = getString(wrappedData, OmtpConstants.TUI_ACCESS_NUMBER);
+ mClientSmsDestinationNumber = getString(wrappedData,
+ OmtpConstants.CLIENT_SMS_DESTINATION_NUMBER);
+ mImapPort = getString(wrappedData, OmtpConstants.IMAP_PORT);
+ mImapUserName = getString(wrappedData, OmtpConstants.IMAP_USER_NAME);
+ mImapPassword = getString(wrappedData, OmtpConstants.IMAP_PASSWORD);
+ mSmtpPort = getString(wrappedData, OmtpConstants.SMTP_PORT);
+ mSmtpUserName = getString(wrappedData, OmtpConstants.SMTP_USER_NAME);
+ mSmtpPassword = getString(wrappedData, OmtpConstants.SMTP_PASSWORD);
+ mTuiPasswordLength = getString(wrappedData, OmtpConstants.TUI_PASSWORD_LENGTH);
+ }
+
+ private static String unquote(String string) {
+ if (string.length() < 2) {
+ return string;
+ }
+ if (string.startsWith("\"") && string.endsWith("\"")) {
+ return string.substring(1, string.length() - 1);
+ }
+ return string;
+ }
+
+ /**
+ * @return the subscriber's VVM provisioning status.
+ */
+ public String getProvisioningStatus() {
+ return mProvisioningStatus;
+ }
+
+ /**
+ * @return the return-code of the status SMS.
+ */
+ public String getReturnCode() {
+ return mStatusReturnCode;
+ }
+
+ /**
+ * @return the URL of the voicemail server. This is the URL to send the users to for subscribing
+ * to the visual voicemail service.
+ */
+ @NeededForTesting
+ public String getSubscriptionUrl() {
+ return mSubscriptionUrl;
+ }
+
+ /**
+ * @return the voicemail server address. Either server IP address or fully qualified domain
+ * name.
+ */
+ public String getServerAddress() {
+ return mServerAddress;
+ }
+
+ /**
+ * @return the Telephony User Interface number to call to access voicemails directly from the
+ * IVR.
+ */
+ @NeededForTesting
+ public String getTuiAccessNumber() {
+ return mTuiAccessNumber;
+ }
+
+ /**
+ * @return the number to which client originated SMSes should be sent to.
+ */
+ @NeededForTesting
+ public String getClientSmsDestinationNumber() {
+ return mClientSmsDestinationNumber;
+ }
+
+ /**
+ * @return the IMAP server port to talk to.
+ */
+ public String getImapPort() {
+ return mImapPort;
+ }
+
+ /**
+ * @return the IMAP user name to be used for authentication.
+ */
+ public String getImapUserName() {
+ return mImapUserName;
+ }
+
+ /**
+ * @return the IMAP password to be used for authentication.
+ */
+ public String getImapPassword() {
+ return mImapPassword;
+ }
+
+ /**
+ * @return the SMTP server port to talk to.
+ */
+ @NeededForTesting
+ public String getSmtpPort() {
+ return mSmtpPort;
+ }
+
+ /**
+ * @return the SMTP user name to be used for SMTP authentication.
+ */
+ @NeededForTesting
+ public String getSmtpUserName() {
+ return mSmtpUserName;
+ }
+
+ /**
+ * @return the SMTP password to be used for SMTP authentication.
+ */
+ @NeededForTesting
+ public String getSmtpPassword() {
+ return mSmtpPassword;
+ }
+
+ public String getTuiPasswordLength() {
+ return mTuiPasswordLength;
+ }
+
+ private static String getString(Bundle bundle, String key) {
+ String value = bundle.getString(key);
+ if (value == null) {
+ return "";
+ }
+ return value;
+ }
+
+ /**
+ * Saves a StatusMessage to the {@link VisualVoicemailPreferences}. Not all fields are saved.
+ */
+ public VisualVoicemailPreferences.Editor putStatus(VisualVoicemailPreferences.Editor editor) {
+ return editor
+ .putString(OmtpConstants.IMAP_PORT, getImapPort())
+ .putString(OmtpConstants.SERVER_ADDRESS, getServerAddress())
+ .putString(OmtpConstants.IMAP_USER_NAME, getImapUserName())
+ .putString(OmtpConstants.IMAP_PASSWORD, getImapPassword())
+ .putString(OmtpConstants.TUI_PASSWORD_LENGTH, getTuiPasswordLength());
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/sms/StatusSmsFetcher.java b/java/com/android/voicemailomtp/sms/StatusSmsFetcher.java
new file mode 100644
index 000000000..4e10c0e43
--- /dev/null
+++ b/java/com/android/voicemailomtp/sms/StatusSmsFetcher.java
@@ -0,0 +1,162 @@
+/*
+ * 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.voicemailomtp.sms;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.SmsManager;
+import android.telephony.VisualVoicemailSms;
+import com.android.voicemailomtp.Assert;
+import com.android.voicemailomtp.OmtpConstants;
+import com.android.voicemailomtp.OmtpService;
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.VvmLog;
+import com.android.voicemailomtp.protocol.VisualVoicemailProtocol;
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/** Intercepts a incoming STATUS SMS with a blocking call. */
+@SuppressWarnings("AndroidApiChecker") /* CompletableFuture is java8*/
+@TargetApi(VERSION_CODES.CUR_DEVELOPMENT)
+public class StatusSmsFetcher extends BroadcastReceiver implements Closeable {
+
+ private static final String TAG = "VvmStatusSmsFetcher";
+
+ private static final long STATUS_SMS_TIMEOUT_MILLIS = 60_000;
+
+ private static final String ACTION_REQUEST_SENT_INTENT
+ = "com.android.voicemailomtp.sms.REQUEST_SENT";
+ private static final int ACTION_REQUEST_SENT_REQUEST_CODE = 0;
+
+ private CompletableFuture<Bundle> mFuture = new CompletableFuture<>();
+
+ private final Context mContext;
+ private final PhoneAccountHandle mPhoneAccountHandle;
+
+ public StatusSmsFetcher(Context context, PhoneAccountHandle phoneAccountHandle) {
+ mContext = context;
+ mPhoneAccountHandle = phoneAccountHandle;
+ IntentFilter filter = new IntentFilter(ACTION_REQUEST_SENT_INTENT);
+ filter.addAction(OmtpService.ACTION_SMS_RECEIVED);
+ context.registerReceiver(this, filter);
+ }
+
+ @Override
+ public void close() throws IOException {
+ mContext.unregisterReceiver(this);
+ }
+
+ @WorkerThread
+ @Nullable
+ public Bundle get() throws InterruptedException, ExecutionException, TimeoutException,
+ CancellationException {
+ Assert.isNotMainThread();
+ return mFuture.get(STATUS_SMS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ }
+
+ public PendingIntent getSentIntent() {
+ Intent intent = new Intent(ACTION_REQUEST_SENT_INTENT);
+ intent.setPackage(mContext.getPackageName());
+ // Because the receiver is registered dynamically, implicit intent must be used.
+ // There should only be a single status SMS request at a time.
+ return PendingIntent.getBroadcast(mContext, ACTION_REQUEST_SENT_REQUEST_CODE, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ }
+
+ @Override
+ @MainThread
+ public void onReceive(Context context, Intent intent) {
+ Assert.isMainThread();
+ if (ACTION_REQUEST_SENT_INTENT.equals(intent.getAction())) {
+ int resultCode = getResultCode();
+
+ if (resultCode == Activity.RESULT_OK) {
+ VvmLog.d(TAG, "Request SMS successfully sent");
+ return;
+ }
+
+ VvmLog.e(TAG, "Request SMS send failed: " + sentSmsResultToString(resultCode));
+ mFuture.cancel(true);
+ return;
+ }
+
+ VisualVoicemailSms sms = intent.getExtras().getParcelable(OmtpService.EXTRA_VOICEMAIL_SMS);
+
+ if (!mPhoneAccountHandle.equals(sms.getPhoneAccountHandle())) {
+ return;
+ }
+ String eventType = sms.getPrefix();
+
+ if (eventType.equals(OmtpConstants.STATUS_SMS_PREFIX)) {
+ mFuture.complete(sms.getFields());
+ return;
+ }
+
+ if (eventType.equals(OmtpConstants.SYNC_SMS_PREFIX)) {
+ return;
+ }
+
+ VvmLog.i(TAG, "VVM SMS with event " + eventType
+ + " received, attempting to translate to STATUS SMS");
+ OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(context,
+ mPhoneAccountHandle);
+ VisualVoicemailProtocol protocol = helper.getProtocol();
+ if (protocol == null) {
+ return;
+ }
+ Bundle translatedBundle = protocol.translateStatusSmsBundle(helper, eventType,
+ sms.getFields());
+
+ if (translatedBundle != null) {
+ VvmLog.i(TAG, "Translated to STATUS SMS");
+ mFuture.complete(translatedBundle);
+ }
+ }
+
+ private static String sentSmsResultToString(int resultCode) {
+ switch (resultCode) {
+ case Activity.RESULT_OK:
+ return "OK";
+ case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
+ return "RESULT_ERROR_GENERIC_FAILURE";
+ case SmsManager.RESULT_ERROR_NO_SERVICE:
+ return "RESULT_ERROR_GENERIC_FAILURE";
+ case SmsManager.RESULT_ERROR_NULL_PDU:
+ return "RESULT_ERROR_GENERIC_FAILURE";
+ case SmsManager.RESULT_ERROR_RADIO_OFF:
+ return "RESULT_ERROR_GENERIC_FAILURE";
+ default:
+ return "UNKNOWN CODE: " + resultCode;
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/sms/SyncMessage.java b/java/com/android/voicemailomtp/sms/SyncMessage.java
new file mode 100644
index 000000000..89cfc0f19
--- /dev/null
+++ b/java/com/android/voicemailomtp/sms/SyncMessage.java
@@ -0,0 +1,166 @@
+/*
+ * 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.voicemailomtp.sms;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import com.android.voicemailomtp.NeededForTesting;
+import com.android.voicemailomtp.OmtpConstants;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+/**
+ * Structured data representation of an OMTP SYNC message.
+ *
+ * Getters will return null if the field was not set in the message body or it could not be parsed.
+ */
+public class SyncMessage {
+ // Sync event that triggered this message.
+ private final String mSyncTriggerEvent;
+ // Total number of new messages on the server.
+ private final int mNewMessageCount;
+ // UID of the new message.
+ private final String mMessageId;
+ // Length of the message.
+ private final int mMessageLength;
+ // Content type (voice, video, fax...) of the new message.
+ private final String mContentType;
+ // Sender of the new message.
+ private final String mSender;
+ // Timestamp (in millis) of the new message.
+ private final long mMsgTimeMillis;
+
+ @Override
+ public String toString() {
+ return "SyncMessage [mSyncTriggerEvent=" + mSyncTriggerEvent
+ + ", mNewMessageCount=" + mNewMessageCount
+ + ", mMessageId=" + mMessageId
+ + ", mMessageLength=" + mMessageLength
+ + ", mContentType=" + mContentType
+ + ", mSender=" + mSender
+ + ", mMsgTimeMillis=" + mMsgTimeMillis + "]";
+ }
+
+ public SyncMessage(Bundle wrappedData) {
+ mSyncTriggerEvent = getString(wrappedData, OmtpConstants.SYNC_TRIGGER_EVENT);
+ mMessageId = getString(wrappedData, OmtpConstants.MESSAGE_UID);
+ mMessageLength = getInt(wrappedData, OmtpConstants.MESSAGE_LENGTH);
+ mContentType = getString(wrappedData, OmtpConstants.CONTENT_TYPE);
+ mSender = getString(wrappedData, OmtpConstants.SENDER);
+ mNewMessageCount = getInt(wrappedData, OmtpConstants.NUM_MESSAGE_COUNT);
+ mMsgTimeMillis = parseTime(wrappedData.getString(OmtpConstants.TIME));
+ }
+
+ private static long parseTime(@Nullable String value) {
+ if (value == null) {
+ return 0L;
+ }
+ try {
+ return new SimpleDateFormat(
+ OmtpConstants.DATE_TIME_FORMAT, Locale.US)
+ .parse(value).getTime();
+ } catch (ParseException e) {
+ return 0L;
+ }
+ }
+ /**
+ * @return the event that triggered the sync message. This is a mandatory field and must always
+ * be set.
+ */
+ public String getSyncTriggerEvent() {
+ return mSyncTriggerEvent;
+ }
+
+ /**
+ * @return the number of new messages stored on the voicemail server.
+ */
+ @NeededForTesting
+ public int getNewMessageCount() {
+ return mNewMessageCount;
+ }
+
+ /**
+ * @return the message ID of the new message.
+ * <p>
+ * Expected to be set only for
+ * {@link com.android.voicemailomtp.OmtpConstants#NEW_MESSAGE}
+ */
+ public String getId() {
+ return mMessageId;
+ }
+
+ /**
+ * @return the content type of the new message.
+ * <p>
+ * Expected to be set only for
+ * {@link com.android.voicemailomtp.OmtpConstants#NEW_MESSAGE}
+ */
+ @NeededForTesting
+ public String getContentType() {
+ return mContentType;
+ }
+
+ /**
+ * @return the message length of the new message.
+ * <p>
+ * Expected to be set only for
+ * {@link com.android.voicemailomtp.OmtpConstants#NEW_MESSAGE}
+ */
+ public int getLength() {
+ return mMessageLength;
+ }
+
+ /**
+ * @return the sender's phone number of the new message specified as MSISDN.
+ * <p>
+ * Expected to be set only for
+ * {@link com.android.voicemailomtp.OmtpConstants#NEW_MESSAGE}
+ */
+ public String getSender() {
+ return mSender;
+ }
+
+ /**
+ * @return the timestamp as milliseconds for the new message.
+ * <p>
+ * Expected to be set only for
+ * {@link com.android.voicemailomtp.OmtpConstants#NEW_MESSAGE}
+ */
+ public long getTimestampMillis() {
+ return mMsgTimeMillis;
+ }
+
+ private static int getInt(Bundle wrappedData, String key) {
+ String value = wrappedData.getString(key);
+ if (value == null) {
+ return 0;
+ }
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+
+ private static String getString(Bundle wrappedData, String key) {
+ String value = wrappedData.getString(key);
+ if (value == null) {
+ return "";
+ }
+ return value;
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/sms/Vvm3MessageSender.java b/java/com/android/voicemailomtp/sms/Vvm3MessageSender.java
new file mode 100644
index 000000000..02e465967
--- /dev/null
+++ b/java/com/android/voicemailomtp/sms/Vvm3MessageSender.java
@@ -0,0 +1,56 @@
+/*
+ * 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.voicemailomtp.sms;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.SmsManager;
+
+public class Vvm3MessageSender extends OmtpMessageSender {
+
+ /**
+ * Creates a new instance of Vvm3MessageSender.
+ *
+ * @param applicationPort If set to a value > 0 then a binary sms is sent to this port number.
+ * Otherwise, a standard text SMS is sent.
+ */
+ public Vvm3MessageSender(Context context, PhoneAccountHandle phoneAccountHandle,
+ short applicationPort, String destinationNumber) {
+ super(context, phoneAccountHandle, applicationPort, destinationNumber);
+ }
+
+ @Override
+ public void requestVvmActivation(@Nullable PendingIntent sentIntent) {
+ // Activation not supported for VVM3, send a status request instead.
+ requestVvmStatus(sentIntent);
+ }
+
+ @Override
+ public void requestVvmDeactivation(@Nullable PendingIntent sentIntent) {
+ // Deactivation not supported for VVM3, do nothing
+ }
+
+
+ @Override
+ public void requestVvmStatus(@Nullable PendingIntent sentIntent) {
+ // Status message:
+ // STATUS
+ StringBuilder sb = new StringBuilder().append("STATUS");
+ sendSms(sb.toString(), sentIntent);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/commons/io/IOUtils.java b/java/com/android/voicemailomtp/src/org/apache/commons/io/IOUtils.java
new file mode 100644
index 000000000..b41450790
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/commons/io/IOUtils.java
@@ -0,0 +1,1202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.io;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * General IO stream manipulation utilities.
+ * <p>
+ * This class provides static utility methods for input/output operations.
+ * <ul>
+ * <li>closeQuietly - these methods close a stream ignoring nulls and exceptions
+ * <li>toXxx/read - these methods read data from a stream
+ * <li>write - these methods write data to a stream
+ * <li>copy - these methods copy all the data from one stream to another
+ * <li>contentEquals - these methods compare the content of two streams
+ * </ul>
+ * <p>
+ * The byte-to-char methods and char-to-byte methods involve a conversion step.
+ * Two methods are provided in each case, one that uses the platform default
+ * encoding and the other which allows you to specify an encoding. You are
+ * encouraged to always specify an encoding because relying on the platform
+ * default can lead to unexpected results, for example when moving from
+ * development to production.
+ * <p>
+ * All the methods in this class that read a stream are buffered internally.
+ * This means that there is no cause to use a <code>BufferedInputStream</code>
+ * or <code>BufferedReader</code>. The default buffer size of 4K has been shown
+ * to be efficient in tests.
+ * <p>
+ * Wherever possible, the methods in this class do <em>not</em> flush or close
+ * the stream. This is to avoid making non-portable assumptions about the
+ * streams' origin and further use. Thus the caller is still responsible for
+ * closing streams after use.
+ * <p>
+ * Origin of code: Excalibur.
+ *
+ * @author Peter Donald
+ * @author Jeff Turner
+ * @author Matthew Hawthorne
+ * @author Stephen Colebourne
+ * @author Gareth Davis
+ * @author Ian Springer
+ * @author Niall Pemberton
+ * @author Sandy McArthur
+ * @version $Id: IOUtils.java 481854 2006-12-03 18:30:07Z scolebourne $
+ */
+public class IOUtils {
+ // NOTE: This class is focussed on InputStream, OutputStream, Reader and
+ // Writer. Each method should take at least one of these as a parameter,
+ // or return one of them.
+
+ /**
+ * The Unix directory separator character.
+ */
+ public static final char DIR_SEPARATOR_UNIX = '/';
+ /**
+ * The Windows directory separator character.
+ */
+ public static final char DIR_SEPARATOR_WINDOWS = '\\';
+ /**
+ * The system directory separator character.
+ */
+ public static final char DIR_SEPARATOR = File.separatorChar;
+ /**
+ * The Unix line separator string.
+ */
+ public static final String LINE_SEPARATOR_UNIX = "\n";
+ /**
+ * The Windows line separator string.
+ */
+ public static final String LINE_SEPARATOR_WINDOWS = "\r\n";
+ /**
+ * The system line separator string.
+ */
+ public static final String LINE_SEPARATOR;
+ static {
+ // avoid security issues
+ StringWriter buf = new StringWriter(4);
+ PrintWriter out = new PrintWriter(buf);
+ out.println();
+ LINE_SEPARATOR = buf.toString();
+ }
+
+ /**
+ * The default buffer size to use.
+ */
+ private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+
+ /**
+ * Instances should NOT be constructed in standard programming.
+ */
+ public IOUtils() {
+ super();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Unconditionally close an <code>Reader</code>.
+ * <p>
+ * Equivalent to {@link Reader#close()}, except any exceptions will be ignored.
+ * This is typically used in finally blocks.
+ *
+ * @param input the Reader to close, may be null or already closed
+ */
+ public static void closeQuietly(Reader input) {
+ try {
+ if (input != null) {
+ input.close();
+ }
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+
+ /**
+ * Unconditionally close a <code>Writer</code>.
+ * <p>
+ * Equivalent to {@link Writer#close()}, except any exceptions will be ignored.
+ * This is typically used in finally blocks.
+ *
+ * @param output the Writer to close, may be null or already closed
+ */
+ public static void closeQuietly(Writer output) {
+ try {
+ if (output != null) {
+ output.close();
+ }
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+
+ /**
+ * Unconditionally close an <code>InputStream</code>.
+ * <p>
+ * Equivalent to {@link InputStream#close()}, except any exceptions will be ignored.
+ * This is typically used in finally blocks.
+ *
+ * @param input the InputStream to close, may be null or already closed
+ */
+ public static void closeQuietly(InputStream input) {
+ try {
+ if (input != null) {
+ input.close();
+ }
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+
+ /**
+ * Unconditionally close an <code>OutputStream</code>.
+ * <p>
+ * Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored.
+ * This is typically used in finally blocks.
+ *
+ * @param output the OutputStream to close, may be null or already closed
+ */
+ public static void closeQuietly(OutputStream output) {
+ try {
+ if (output != null) {
+ output.close();
+ }
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+
+ // read toByteArray
+ //-----------------------------------------------------------------------
+ /**
+ * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedInputStream</code>.
+ *
+ * @param input the <code>InputStream</code> to read from
+ * @return the requested byte array
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs
+ */
+ public static byte[] toByteArray(InputStream input) throws IOException {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ copy(input, output);
+ return output.toByteArray();
+ }
+
+ /**
+ * Get the contents of a <code>Reader</code> as a <code>byte[]</code>
+ * using the default character encoding of the platform.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedReader</code>.
+ *
+ * @param input the <code>Reader</code> to read from
+ * @return the requested byte array
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs
+ */
+ public static byte[] toByteArray(Reader input) throws IOException {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ copy(input, output);
+ return output.toByteArray();
+ }
+
+ /**
+ * Get the contents of a <code>Reader</code> as a <code>byte[]</code>
+ * using the specified character encoding.
+ * <p>
+ * Character encoding names can be found at
+ * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedReader</code>.
+ *
+ * @param input the <code>Reader</code> to read from
+ * @param encoding the encoding to use, null means platform default
+ * @return the requested byte array
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static byte[] toByteArray(Reader input, String encoding)
+ throws IOException {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ copy(input, output, encoding);
+ return output.toByteArray();
+ }
+
+ /**
+ * Get the contents of a <code>String</code> as a <code>byte[]</code>
+ * using the default character encoding of the platform.
+ * <p>
+ * This is the same as {@link String#getBytes()}.
+ *
+ * @param input the <code>String</code> to convert
+ * @return the requested byte array
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs (never occurs)
+ * @deprecated Use {@link String#getBytes()}
+ */
+ @Deprecated
+ public static byte[] toByteArray(String input) throws IOException {
+ return input.getBytes();
+ }
+
+ // read char[]
+ //-----------------------------------------------------------------------
+ /**
+ * Get the contents of an <code>InputStream</code> as a character array
+ * using the default character encoding of the platform.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedInputStream</code>.
+ *
+ * @param is the <code>InputStream</code> to read from
+ * @return the requested character array
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static char[] toCharArray(InputStream is) throws IOException {
+ CharArrayWriter output = new CharArrayWriter();
+ copy(is, output);
+ return output.toCharArray();
+ }
+
+ /**
+ * Get the contents of an <code>InputStream</code> as a character array
+ * using the specified character encoding.
+ * <p>
+ * Character encoding names can be found at
+ * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedInputStream</code>.
+ *
+ * @param is the <code>InputStream</code> to read from
+ * @param encoding the encoding to use, null means platform default
+ * @return the requested character array
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static char[] toCharArray(InputStream is, String encoding)
+ throws IOException {
+ CharArrayWriter output = new CharArrayWriter();
+ copy(is, output, encoding);
+ return output.toCharArray();
+ }
+
+ /**
+ * Get the contents of a <code>Reader</code> as a character array.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedReader</code>.
+ *
+ * @param input the <code>Reader</code> to read from
+ * @return the requested character array
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static char[] toCharArray(Reader input) throws IOException {
+ CharArrayWriter sw = new CharArrayWriter();
+ copy(input, sw);
+ return sw.toCharArray();
+ }
+
+ // read toString
+ //-----------------------------------------------------------------------
+ /**
+ * Get the contents of an <code>InputStream</code> as a String
+ * using the default character encoding of the platform.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedInputStream</code>.
+ *
+ * @param input the <code>InputStream</code> to read from
+ * @return the requested String
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs
+ */
+ public static String toString(InputStream input) throws IOException {
+ StringWriter sw = new StringWriter();
+ copy(input, sw);
+ return sw.toString();
+ }
+
+ /**
+ * Get the contents of an <code>InputStream</code> as a String
+ * using the specified character encoding.
+ * <p>
+ * Character encoding names can be found at
+ * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedInputStream</code>.
+ *
+ * @param input the <code>InputStream</code> to read from
+ * @param encoding the encoding to use, null means platform default
+ * @return the requested String
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs
+ */
+ public static String toString(InputStream input, String encoding)
+ throws IOException {
+ StringWriter sw = new StringWriter();
+ copy(input, sw, encoding);
+ return sw.toString();
+ }
+
+ /**
+ * Get the contents of a <code>Reader</code> as a String.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedReader</code>.
+ *
+ * @param input the <code>Reader</code> to read from
+ * @return the requested String
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs
+ */
+ public static String toString(Reader input) throws IOException {
+ StringWriter sw = new StringWriter();
+ copy(input, sw);
+ return sw.toString();
+ }
+
+ /**
+ * Get the contents of a <code>byte[]</code> as a String
+ * using the default character encoding of the platform.
+ *
+ * @param input the byte array to read from
+ * @return the requested String
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs (never occurs)
+ * @deprecated Use {@link String#String(byte[])}
+ */
+ @Deprecated
+ public static String toString(byte[] input) throws IOException {
+ return new String(input);
+ }
+
+ /**
+ * Get the contents of a <code>byte[]</code> as a String
+ * using the specified character encoding.
+ * <p>
+ * Character encoding names can be found at
+ * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+ *
+ * @param input the byte array to read from
+ * @param encoding the encoding to use, null means platform default
+ * @return the requested String
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs (never occurs)
+ * @deprecated Use {@link String#String(byte[],String)}
+ */
+ @Deprecated
+ public static String toString(byte[] input, String encoding)
+ throws IOException {
+ if (encoding == null) {
+ return new String(input);
+ } else {
+ return new String(input, encoding);
+ }
+ }
+
+ // readLines
+ //-----------------------------------------------------------------------
+ /**
+ * Get the contents of an <code>InputStream</code> as a list of Strings,
+ * one entry per line, using the default character encoding of the platform.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedInputStream</code>.
+ *
+ * @param input the <code>InputStream</code> to read from, not null
+ * @return the list of Strings, never null
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static List<String> readLines(InputStream input) throws IOException {
+ InputStreamReader reader = new InputStreamReader(input);
+ return readLines(reader);
+ }
+
+ /**
+ * Get the contents of an <code>InputStream</code> as a list of Strings,
+ * one entry per line, using the specified character encoding.
+ * <p>
+ * Character encoding names can be found at
+ * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedInputStream</code>.
+ *
+ * @param input the <code>InputStream</code> to read from, not null
+ * @param encoding the encoding to use, null means platform default
+ * @return the list of Strings, never null
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static List<String> readLines(InputStream input, String encoding) throws IOException {
+ if (encoding == null) {
+ return readLines(input);
+ } else {
+ InputStreamReader reader = new InputStreamReader(input, encoding);
+ return readLines(reader);
+ }
+ }
+
+ /**
+ * Get the contents of a <code>Reader</code> as a list of Strings,
+ * one entry per line.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedReader</code>.
+ *
+ * @param input the <code>Reader</code> to read from, not null
+ * @return the list of Strings, never null
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static List<String> readLines(Reader input) throws IOException {
+ BufferedReader reader = new BufferedReader(input);
+ List<String> list = new ArrayList<String>();
+ String line = reader.readLine();
+ while (line != null) {
+ list.add(line);
+ line = reader.readLine();
+ }
+ return list;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Convert the specified string to an input stream, encoded as bytes
+ * using the default character encoding of the platform.
+ *
+ * @param input the string to convert
+ * @return an input stream
+ * @since Commons IO 1.1
+ */
+ public static InputStream toInputStream(String input) {
+ byte[] bytes = input.getBytes();
+ return new ByteArrayInputStream(bytes);
+ }
+
+ /**
+ * Convert the specified string to an input stream, encoded as bytes
+ * using the specified character encoding.
+ * <p>
+ * Character encoding names can be found at
+ * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+ *
+ * @param input the string to convert
+ * @param encoding the encoding to use, null means platform default
+ * @throws IOException if the encoding is invalid
+ * @return an input stream
+ * @since Commons IO 1.1
+ */
+ public static InputStream toInputStream(String input, String encoding) throws IOException {
+ byte[] bytes = encoding != null ? input.getBytes(encoding) : input.getBytes();
+ return new ByteArrayInputStream(bytes);
+ }
+
+ // write byte[]
+ //-----------------------------------------------------------------------
+ /**
+ * Writes bytes from a <code>byte[]</code> to an <code>OutputStream</code>.
+ *
+ * @param data the byte array to write, do not modify during output,
+ * null ignored
+ * @param output the <code>OutputStream</code> to write to
+ * @throws NullPointerException if output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void write(byte[] data, OutputStream output)
+ throws IOException {
+ if (data != null) {
+ output.write(data);
+ }
+ }
+
+ /**
+ * Writes bytes from a <code>byte[]</code> to chars on a <code>Writer</code>
+ * using the default character encoding of the platform.
+ * <p>
+ * This method uses {@link String#String(byte[])}.
+ *
+ * @param data the byte array to write, do not modify during output,
+ * null ignored
+ * @param output the <code>Writer</code> to write to
+ * @throws NullPointerException if output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void write(byte[] data, Writer output) throws IOException {
+ if (data != null) {
+ output.write(new String(data));
+ }
+ }
+
+ /**
+ * Writes bytes from a <code>byte[]</code> to chars on a <code>Writer</code>
+ * using the specified character encoding.
+ * <p>
+ * Character encoding names can be found at
+ * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+ * <p>
+ * This method uses {@link String#String(byte[], String)}.
+ *
+ * @param data the byte array to write, do not modify during output,
+ * null ignored
+ * @param output the <code>Writer</code> to write to
+ * @param encoding the encoding to use, null means platform default
+ * @throws NullPointerException if output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void write(byte[] data, Writer output, String encoding)
+ throws IOException {
+ if (data != null) {
+ if (encoding == null) {
+ write(data, output);
+ } else {
+ output.write(new String(data, encoding));
+ }
+ }
+ }
+
+ // write char[]
+ //-----------------------------------------------------------------------
+ /**
+ * Writes chars from a <code>char[]</code> to a <code>Writer</code>
+ * using the default character encoding of the platform.
+ *
+ * @param data the char array to write, do not modify during output,
+ * null ignored
+ * @param output the <code>Writer</code> to write to
+ * @throws NullPointerException if output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void write(char[] data, Writer output) throws IOException {
+ if (data != null) {
+ output.write(data);
+ }
+ }
+
+ /**
+ * Writes chars from a <code>char[]</code> to bytes on an
+ * <code>OutputStream</code>.
+ * <p>
+ * This method uses {@link String#String(char[])} and
+ * {@link String#getBytes()}.
+ *
+ * @param data the char array to write, do not modify during output,
+ * null ignored
+ * @param output the <code>OutputStream</code> to write to
+ * @throws NullPointerException if output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void write(char[] data, OutputStream output)
+ throws IOException {
+ if (data != null) {
+ output.write(new String(data).getBytes());
+ }
+ }
+
+ /**
+ * Writes chars from a <code>char[]</code> to bytes on an
+ * <code>OutputStream</code> using the specified character encoding.
+ * <p>
+ * Character encoding names can be found at
+ * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+ * <p>
+ * This method uses {@link String#String(char[])} and
+ * {@link String#getBytes(String)}.
+ *
+ * @param data the char array to write, do not modify during output,
+ * null ignored
+ * @param output the <code>OutputStream</code> to write to
+ * @param encoding the encoding to use, null means platform default
+ * @throws NullPointerException if output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void write(char[] data, OutputStream output, String encoding)
+ throws IOException {
+ if (data != null) {
+ if (encoding == null) {
+ write(data, output);
+ } else {
+ output.write(new String(data).getBytes(encoding));
+ }
+ }
+ }
+
+ // write String
+ //-----------------------------------------------------------------------
+ /**
+ * Writes chars from a <code>String</code> to a <code>Writer</code>.
+ *
+ * @param data the <code>String</code> to write, null ignored
+ * @param output the <code>Writer</code> to write to
+ * @throws NullPointerException if output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void write(String data, Writer output) throws IOException {
+ if (data != null) {
+ output.write(data);
+ }
+ }
+
+ /**
+ * Writes chars from a <code>String</code> to bytes on an
+ * <code>OutputStream</code> using the default character encoding of the
+ * platform.
+ * <p>
+ * This method uses {@link String#getBytes()}.
+ *
+ * @param data the <code>String</code> to write, null ignored
+ * @param output the <code>OutputStream</code> to write to
+ * @throws NullPointerException if output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void write(String data, OutputStream output)
+ throws IOException {
+ if (data != null) {
+ output.write(data.getBytes());
+ }
+ }
+
+ /**
+ * Writes chars from a <code>String</code> to bytes on an
+ * <code>OutputStream</code> using the specified character encoding.
+ * <p>
+ * Character encoding names can be found at
+ * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+ * <p>
+ * This method uses {@link String#getBytes(String)}.
+ *
+ * @param data the <code>String</code> to write, null ignored
+ * @param output the <code>OutputStream</code> to write to
+ * @param encoding the encoding to use, null means platform default
+ * @throws NullPointerException if output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void write(String data, OutputStream output, String encoding)
+ throws IOException {
+ if (data != null) {
+ if (encoding == null) {
+ write(data, output);
+ } else {
+ output.write(data.getBytes(encoding));
+ }
+ }
+ }
+
+ // write StringBuffer
+ //-----------------------------------------------------------------------
+ /**
+ * Writes chars from a <code>StringBuffer</code> to a <code>Writer</code>.
+ *
+ * @param data the <code>StringBuffer</code> to write, null ignored
+ * @param output the <code>Writer</code> to write to
+ * @throws NullPointerException if output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void write(StringBuffer data, Writer output)
+ throws IOException {
+ if (data != null) {
+ output.write(data.toString());
+ }
+ }
+
+ /**
+ * Writes chars from a <code>StringBuffer</code> to bytes on an
+ * <code>OutputStream</code> using the default character encoding of the
+ * platform.
+ * <p>
+ * This method uses {@link String#getBytes()}.
+ *
+ * @param data the <code>StringBuffer</code> to write, null ignored
+ * @param output the <code>OutputStream</code> to write to
+ * @throws NullPointerException if output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void write(StringBuffer data, OutputStream output)
+ throws IOException {
+ if (data != null) {
+ output.write(data.toString().getBytes());
+ }
+ }
+
+ /**
+ * Writes chars from a <code>StringBuffer</code> to bytes on an
+ * <code>OutputStream</code> using the specified character encoding.
+ * <p>
+ * Character encoding names can be found at
+ * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+ * <p>
+ * This method uses {@link String#getBytes(String)}.
+ *
+ * @param data the <code>StringBuffer</code> to write, null ignored
+ * @param output the <code>OutputStream</code> to write to
+ * @param encoding the encoding to use, null means platform default
+ * @throws NullPointerException if output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void write(StringBuffer data, OutputStream output,
+ String encoding) throws IOException {
+ if (data != null) {
+ if (encoding == null) {
+ write(data, output);
+ } else {
+ output.write(data.toString().getBytes(encoding));
+ }
+ }
+ }
+
+ // writeLines
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the <code>toString()</code> value of each item in a collection to
+ * an <code>OutputStream</code> line by line, using the default character
+ * encoding of the platform and the specified line ending.
+ *
+ * @param lines the lines to write, null entries produce blank lines
+ * @param lineEnding the line separator to use, null is system default
+ * @param output the <code>OutputStream</code> to write to, not null, not closed
+ * @throws NullPointerException if the output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void writeLines(Collection<Object> lines, String lineEnding,
+ OutputStream output) throws IOException {
+ if (lines == null) {
+ return;
+ }
+ if (lineEnding == null) {
+ lineEnding = LINE_SEPARATOR;
+ }
+ for (Iterator<Object> it = lines.iterator(); it.hasNext(); ) {
+ Object line = it.next();
+ if (line != null) {
+ output.write(line.toString().getBytes());
+ }
+ output.write(lineEnding.getBytes());
+ }
+ }
+
+ /**
+ * Writes the <code>toString()</code> value of each item in a collection to
+ * an <code>OutputStream</code> line by line, using the specified character
+ * encoding and the specified line ending.
+ * <p>
+ * Character encoding names can be found at
+ * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+ *
+ * @param lines the lines to write, null entries produce blank lines
+ * @param lineEnding the line separator to use, null is system default
+ * @param output the <code>OutputStream</code> to write to, not null, not closed
+ * @param encoding the encoding to use, null means platform default
+ * @throws NullPointerException if the output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void writeLines(Collection<Object> lines, String lineEnding,
+ OutputStream output, String encoding) throws IOException {
+ if (encoding == null) {
+ writeLines(lines, lineEnding, output);
+ } else {
+ if (lines == null) {
+ return;
+ }
+ if (lineEnding == null) {
+ lineEnding = LINE_SEPARATOR;
+ }
+ for (Iterator<Object> it = lines.iterator(); it.hasNext(); ) {
+ Object line = it.next();
+ if (line != null) {
+ output.write(line.toString().getBytes(encoding));
+ }
+ output.write(lineEnding.getBytes(encoding));
+ }
+ }
+ }
+
+ /**
+ * Writes the <code>toString()</code> value of each item in a collection to
+ * a <code>Writer</code> line by line, using the specified line ending.
+ *
+ * @param lines the lines to write, null entries produce blank lines
+ * @param lineEnding the line separator to use, null is system default
+ * @param writer the <code>Writer</code> to write to, not null, not closed
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void writeLines(Collection<Object> lines, String lineEnding,
+ Writer writer) throws IOException {
+ if (lines == null) {
+ return;
+ }
+ if (lineEnding == null) {
+ lineEnding = LINE_SEPARATOR;
+ }
+ for (Iterator<Object> it = lines.iterator(); it.hasNext(); ) {
+ Object line = it.next();
+ if (line != null) {
+ writer.write(line.toString());
+ }
+ writer.write(lineEnding);
+ }
+ }
+
+ // copy from InputStream
+ //-----------------------------------------------------------------------
+ /**
+ * Copy bytes from an <code>InputStream</code> to an
+ * <code>OutputStream</code>.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedInputStream</code>.
+ * <p>
+ * Large streams (over 2GB) will return a bytes copied value of
+ * <code>-1</code> after the copy has completed since the correct
+ * number of bytes cannot be returned as an int. For large streams
+ * use the <code>copyLarge(InputStream, OutputStream)</code> method.
+ *
+ * @param input the <code>InputStream</code> to read from
+ * @param output the <code>OutputStream</code> to write to
+ * @return the number of bytes copied
+ * @throws NullPointerException if the input or output is null
+ * @throws IOException if an I/O error occurs
+ * @throws ArithmeticException if the byte count is too large
+ * @since Commons IO 1.1
+ */
+ public static int copy(InputStream input, OutputStream output) throws IOException {
+ long count = copyLarge(input, output);
+ if (count > Integer.MAX_VALUE) {
+ return -1;
+ }
+ return (int) count;
+ }
+
+ /**
+ * Copy bytes from a large (over 2GB) <code>InputStream</code> to an
+ * <code>OutputStream</code>.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedInputStream</code>.
+ *
+ * @param input the <code>InputStream</code> to read from
+ * @param output the <code>OutputStream</code> to write to
+ * @return the number of bytes copied
+ * @throws NullPointerException if the input or output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.3
+ */
+ public static long copyLarge(InputStream input, OutputStream output)
+ throws IOException {
+ byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+ long count = 0;
+ int n = 0;
+ while (-1 != (n = input.read(buffer))) {
+ output.write(buffer, 0, n);
+ count += n;
+ }
+ return count;
+ }
+
+ /**
+ * Copy bytes from an <code>InputStream</code> to chars on a
+ * <code>Writer</code> using the default character encoding of the platform.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedInputStream</code>.
+ * <p>
+ * This method uses {@link InputStreamReader}.
+ *
+ * @param input the <code>InputStream</code> to read from
+ * @param output the <code>Writer</code> to write to
+ * @throws NullPointerException if the input or output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void copy(InputStream input, Writer output)
+ throws IOException {
+ InputStreamReader in = new InputStreamReader(input);
+ copy(in, output);
+ }
+
+ /**
+ * Copy bytes from an <code>InputStream</code> to chars on a
+ * <code>Writer</code> using the specified character encoding.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedInputStream</code>.
+ * <p>
+ * Character encoding names can be found at
+ * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+ * <p>
+ * This method uses {@link InputStreamReader}.
+ *
+ * @param input the <code>InputStream</code> to read from
+ * @param output the <code>Writer</code> to write to
+ * @param encoding the encoding to use, null means platform default
+ * @throws NullPointerException if the input or output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void copy(InputStream input, Writer output, String encoding)
+ throws IOException {
+ if (encoding == null) {
+ copy(input, output);
+ } else {
+ InputStreamReader in = new InputStreamReader(input, encoding);
+ copy(in, output);
+ }
+ }
+
+ // copy from Reader
+ //-----------------------------------------------------------------------
+ /**
+ * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedReader</code>.
+ * <p>
+ * Large streams (over 2GB) will return a chars copied value of
+ * <code>-1</code> after the copy has completed since the correct
+ * number of chars cannot be returned as an int. For large streams
+ * use the <code>copyLarge(Reader, Writer)</code> method.
+ *
+ * @param input the <code>Reader</code> to read from
+ * @param output the <code>Writer</code> to write to
+ * @return the number of characters copied
+ * @throws NullPointerException if the input or output is null
+ * @throws IOException if an I/O error occurs
+ * @throws ArithmeticException if the character count is too large
+ * @since Commons IO 1.1
+ */
+ public static int copy(Reader input, Writer output) throws IOException {
+ long count = copyLarge(input, output);
+ if (count > Integer.MAX_VALUE) {
+ return -1;
+ }
+ return (int) count;
+ }
+
+ /**
+ * Copy chars from a large (over 2GB) <code>Reader</code> to a <code>Writer</code>.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedReader</code>.
+ *
+ * @param input the <code>Reader</code> to read from
+ * @param output the <code>Writer</code> to write to
+ * @return the number of characters copied
+ * @throws NullPointerException if the input or output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.3
+ */
+ public static long copyLarge(Reader input, Writer output) throws IOException {
+ char[] buffer = new char[DEFAULT_BUFFER_SIZE];
+ long count = 0;
+ int n = 0;
+ while (-1 != (n = input.read(buffer))) {
+ output.write(buffer, 0, n);
+ count += n;
+ }
+ return count;
+ }
+
+ /**
+ * Copy chars from a <code>Reader</code> to bytes on an
+ * <code>OutputStream</code> using the default character encoding of the
+ * platform, and calling flush.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedReader</code>.
+ * <p>
+ * Due to the implementation of OutputStreamWriter, this method performs a
+ * flush.
+ * <p>
+ * This method uses {@link OutputStreamWriter}.
+ *
+ * @param input the <code>Reader</code> to read from
+ * @param output the <code>OutputStream</code> to write to
+ * @throws NullPointerException if the input or output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void copy(Reader input, OutputStream output)
+ throws IOException {
+ OutputStreamWriter out = new OutputStreamWriter(output);
+ copy(input, out);
+ // XXX Unless anyone is planning on rewriting OutputStreamWriter, we
+ // have to flush here.
+ out.flush();
+ }
+
+ /**
+ * Copy chars from a <code>Reader</code> to bytes on an
+ * <code>OutputStream</code> using the specified character encoding, and
+ * calling flush.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedReader</code>.
+ * <p>
+ * Character encoding names can be found at
+ * <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
+ * <p>
+ * Due to the implementation of OutputStreamWriter, this method performs a
+ * flush.
+ * <p>
+ * This method uses {@link OutputStreamWriter}.
+ *
+ * @param input the <code>Reader</code> to read from
+ * @param output the <code>OutputStream</code> to write to
+ * @param encoding the encoding to use, null means platform default
+ * @throws NullPointerException if the input or output is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static void copy(Reader input, OutputStream output, String encoding)
+ throws IOException {
+ if (encoding == null) {
+ copy(input, output);
+ } else {
+ OutputStreamWriter out = new OutputStreamWriter(output, encoding);
+ copy(input, out);
+ // XXX Unless anyone is planning on rewriting OutputStreamWriter,
+ // we have to flush here.
+ out.flush();
+ }
+ }
+
+ // content equals
+ //-----------------------------------------------------------------------
+ /**
+ * Compare the contents of two Streams to determine if they are equal or
+ * not.
+ * <p>
+ * This method buffers the input internally using
+ * <code>BufferedInputStream</code> if they are not already buffered.
+ *
+ * @param input1 the first stream
+ * @param input2 the second stream
+ * @return true if the content of the streams are equal or they both don't
+ * exist, false otherwise
+ * @throws NullPointerException if either input is null
+ * @throws IOException if an I/O error occurs
+ */
+ public static boolean contentEquals(InputStream input1, InputStream input2)
+ throws IOException {
+ if (!(input1 instanceof BufferedInputStream)) {
+ input1 = new BufferedInputStream(input1);
+ }
+ if (!(input2 instanceof BufferedInputStream)) {
+ input2 = new BufferedInputStream(input2);
+ }
+
+ int ch = input1.read();
+ while (-1 != ch) {
+ int ch2 = input2.read();
+ if (ch != ch2) {
+ return false;
+ }
+ ch = input1.read();
+ }
+
+ int ch2 = input2.read();
+ return (ch2 == -1);
+ }
+
+ /**
+ * Compare the contents of two Readers to determine if they are equal or
+ * not.
+ * <p>
+ * This method buffers the input internally using
+ * <code>BufferedReader</code> if they are not already buffered.
+ *
+ * @param input1 the first reader
+ * @param input2 the second reader
+ * @return true if the content of the readers are equal or they both don't
+ * exist, false otherwise
+ * @throws NullPointerException if either input is null
+ * @throws IOException if an I/O error occurs
+ * @since Commons IO 1.1
+ */
+ public static boolean contentEquals(Reader input1, Reader input2)
+ throws IOException {
+ if (!(input1 instanceof BufferedReader)) {
+ input1 = new BufferedReader(input1);
+ }
+ if (!(input2 instanceof BufferedReader)) {
+ input2 = new BufferedReader(input2);
+ }
+
+ int ch = input1.read();
+ while (-1 != ch) {
+ int ch2 = input2.read();
+ if (ch != ch2) {
+ return false;
+ }
+ ch = input1.read();
+ }
+
+ int ch2 = input2.read();
+ return (ch2 == -1);
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/BodyDescriptor.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/BodyDescriptor.java
new file mode 100644
index 000000000..867c43d86
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/BodyDescriptor.java
@@ -0,0 +1,392 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Encapsulates the values of the MIME-specific header fields
+ * (which starts with <code>Content-</code>).
+ *
+ *
+ * @version $Id: BodyDescriptor.java,v 1.4 2005/02/11 10:08:37 ntherning Exp $
+ */
+public class BodyDescriptor {
+ private static Log log = LogFactory.getLog(BodyDescriptor.class);
+
+ private String mimeType = "text/plain";
+ private String boundary = null;
+ private String charset = "us-ascii";
+ private String transferEncoding = "7bit";
+ private Map<String, String> parameters = new HashMap<String, String>();
+ private boolean contentTypeSet = false;
+ private boolean contentTransferEncSet = false;
+
+ /**
+ * Creates a new root <code>BodyDescriptor</code> instance.
+ */
+ public BodyDescriptor() {
+ this(null);
+ }
+
+ /**
+ * Creates a new <code>BodyDescriptor</code> instance.
+ *
+ * @param parent the descriptor of the parent or <code>null</code> if this
+ * is the root descriptor.
+ */
+ public BodyDescriptor(BodyDescriptor parent) {
+ if (parent != null && parent.isMimeType("multipart/digest")) {
+ mimeType = "message/rfc822";
+ } else {
+ mimeType = "text/plain";
+ }
+ }
+
+ /**
+ * Should be called for each <code>Content-</code> header field of
+ * a MIME message or part.
+ *
+ * @param name the field name.
+ * @param value the field value.
+ */
+ public void addField(String name, String value) {
+
+ name = name.trim().toLowerCase();
+
+ if (name.equals("content-transfer-encoding") && !contentTransferEncSet) {
+ contentTransferEncSet = true;
+
+ value = value.trim().toLowerCase();
+ if (value.length() > 0) {
+ transferEncoding = value;
+ }
+
+ } else if (name.equals("content-type") && !contentTypeSet) {
+ contentTypeSet = true;
+
+ value = value.trim();
+
+ /*
+ * Unfold Content-Type value
+ */
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+ if (c == '\r' || c == '\n') {
+ continue;
+ }
+ sb.append(c);
+ }
+
+ Map<String, String> params = getHeaderParams(sb.toString());
+
+ String main = params.get("");
+ if (main != null) {
+ main = main.toLowerCase().trim();
+ int index = main.indexOf('/');
+ boolean valid = false;
+ if (index != -1) {
+ String type = main.substring(0, index).trim();
+ String subtype = main.substring(index + 1).trim();
+ if (type.length() > 0 && subtype.length() > 0) {
+ main = type + "/" + subtype;
+ valid = true;
+ }
+ }
+
+ if (!valid) {
+ main = null;
+ }
+ }
+ String b = params.get("boundary");
+
+ if (main != null
+ && ((main.startsWith("multipart/") && b != null)
+ || !main.startsWith("multipart/"))) {
+
+ mimeType = main;
+ }
+
+ if (isMultipart()) {
+ boundary = b;
+ }
+
+ String c = params.get("charset");
+ if (c != null) {
+ c = c.trim();
+ if (c.length() > 0) {
+ charset = c.toLowerCase();
+ }
+ }
+
+ /*
+ * Add all other parameters to parameters.
+ */
+ parameters.putAll(params);
+ parameters.remove("");
+ parameters.remove("boundary");
+ parameters.remove("charset");
+ }
+ }
+
+ private Map<String, String> getHeaderParams(String headerValue) {
+ Map<String, String> result = new HashMap<String, String>();
+
+ // split main value and parameters
+ String main;
+ String rest;
+ if (headerValue.indexOf(";") == -1) {
+ main = headerValue;
+ rest = null;
+ } else {
+ main = headerValue.substring(0, headerValue.indexOf(";"));
+ rest = headerValue.substring(main.length() + 1);
+ }
+
+ result.put("", main);
+ if (rest != null) {
+ char[] chars = rest.toCharArray();
+ StringBuffer paramName = new StringBuffer();
+ StringBuffer paramValue = new StringBuffer();
+
+ final byte READY_FOR_NAME = 0;
+ final byte IN_NAME = 1;
+ final byte READY_FOR_VALUE = 2;
+ final byte IN_VALUE = 3;
+ final byte IN_QUOTED_VALUE = 4;
+ final byte VALUE_DONE = 5;
+ final byte ERROR = 99;
+
+ byte state = READY_FOR_NAME;
+ boolean escaped = false;
+ for (int i = 0; i < chars.length; i++) {
+ char c = chars[i];
+
+ switch (state) {
+ case ERROR:
+ if (c == ';')
+ state = READY_FOR_NAME;
+ break;
+
+ case READY_FOR_NAME:
+ if (c == '=') {
+ log.error("Expected header param name, got '='");
+ state = ERROR;
+ break;
+ }
+
+ paramName = new StringBuffer();
+ paramValue = new StringBuffer();
+
+ state = IN_NAME;
+ // $FALL-THROUGH$
+
+ case IN_NAME:
+ if (c == '=') {
+ if (paramName.length() == 0)
+ state = ERROR;
+ else
+ state = READY_FOR_VALUE;
+ break;
+ }
+
+ // not '='... just add to name
+ paramName.append(c);
+ break;
+
+ case READY_FOR_VALUE:
+ boolean fallThrough = false;
+ switch (c) {
+ case ' ':
+ case '\t':
+ break; // ignore spaces, especially before '"'
+
+ case '"':
+ state = IN_QUOTED_VALUE;
+ break;
+
+ default:
+ state = IN_VALUE;
+ fallThrough = true;
+ break;
+ }
+ if (!fallThrough)
+ break;
+
+ // $FALL-THROUGH$
+
+ case IN_VALUE:
+ fallThrough = false;
+ switch (c) {
+ case ';':
+ case ' ':
+ case '\t':
+ result.put(
+ paramName.toString().trim().toLowerCase(),
+ paramValue.toString().trim());
+ state = VALUE_DONE;
+ fallThrough = true;
+ break;
+ default:
+ paramValue.append(c);
+ break;
+ }
+ if (!fallThrough)
+ break;
+
+ // $FALL-THROUGH$
+
+ case VALUE_DONE:
+ switch (c) {
+ case ';':
+ state = READY_FOR_NAME;
+ break;
+
+ case ' ':
+ case '\t':
+ break;
+
+ default:
+ state = ERROR;
+ break;
+ }
+ break;
+
+ case IN_QUOTED_VALUE:
+ switch (c) {
+ case '"':
+ if (!escaped) {
+ // don't trim quoted strings; the spaces could be intentional.
+ result.put(
+ paramName.toString().trim().toLowerCase(),
+ paramValue.toString());
+ state = VALUE_DONE;
+ } else {
+ escaped = false;
+ paramValue.append(c);
+ }
+ break;
+
+ case '\\':
+ if (escaped) {
+ paramValue.append('\\');
+ }
+ escaped = !escaped;
+ break;
+
+ default:
+ if (escaped) {
+ paramValue.append('\\');
+ }
+ escaped = false;
+ paramValue.append(c);
+ break;
+ }
+ break;
+
+ }
+ }
+
+ // done looping. check if anything is left over.
+ if (state == IN_VALUE) {
+ result.put(
+ paramName.toString().trim().toLowerCase(),
+ paramValue.toString().trim());
+ }
+ }
+
+ return result;
+ }
+
+
+ public boolean isMimeType(String mimeType) {
+ return this.mimeType.equals(mimeType.toLowerCase());
+ }
+
+ /**
+ * Return true if the BodyDescriptor belongs to a message
+ */
+ public boolean isMessage() {
+ return mimeType.equals("message/rfc822");
+ }
+
+ /**
+ * Return true if the BodyDescripotro belongs to a multipart
+ */
+ public boolean isMultipart() {
+ return mimeType.startsWith("multipart/");
+ }
+
+ /**
+ * Return the MimeType
+ */
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ /**
+ * Return the boundary
+ */
+ public String getBoundary() {
+ return boundary;
+ }
+
+ /**
+ * Return the charset
+ */
+ public String getCharset() {
+ return charset;
+ }
+
+ /**
+ * Return all parameters for the BodyDescriptor
+ */
+ public Map<String, String> getParameters() {
+ return parameters;
+ }
+
+ /**
+ * Return the TransferEncoding
+ */
+ public String getTransferEncoding() {
+ return transferEncoding;
+ }
+
+ /**
+ * Return true if it's base64 encoded
+ */
+ public boolean isBase64Encoded() {
+ return "base64".equals(transferEncoding);
+ }
+
+ /**
+ * Return true if it's quoted-printable
+ */
+ public boolean isQuotedPrintableEncoded() {
+ return "quoted-printable".equals(transferEncoding);
+ }
+
+ @Override
+ public String toString() {
+ return mimeType;
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/CloseShieldInputStream.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/CloseShieldInputStream.java
new file mode 100644
index 000000000..d9f3b078a
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/CloseShieldInputStream.java
@@ -0,0 +1,129 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * InputStream that shields its underlying input stream from
+ * being closed.
+ *
+ *
+ * @version $Id: CloseShieldInputStream.java,v 1.2 2004/10/02 12:41:10 ntherning Exp $
+ */
+public class CloseShieldInputStream extends InputStream {
+
+ /**
+ * Underlying InputStream
+ */
+ private InputStream is;
+
+ public CloseShieldInputStream(InputStream is) {
+ this.is = is;
+ }
+
+ public InputStream getUnderlyingStream() {
+ return is;
+ }
+
+ /**
+ * @see java.io.InputStream#read()
+ */
+ public int read() throws IOException {
+ checkIfClosed();
+ return is.read();
+ }
+
+ /**
+ * @see java.io.InputStream#available()
+ */
+ public int available() throws IOException {
+ checkIfClosed();
+ return is.available();
+ }
+
+
+ /**
+ * Set the underlying InputStream to null
+ */
+ public void close() throws IOException {
+ is = null;
+ }
+
+ /**
+ * @see java.io.FilterInputStream#reset()
+ */
+ public synchronized void reset() throws IOException {
+ checkIfClosed();
+ is.reset();
+ }
+
+ /**
+ * @see java.io.FilterInputStream#markSupported()
+ */
+ public boolean markSupported() {
+ if (is == null)
+ return false;
+ return is.markSupported();
+ }
+
+ /**
+ * @see java.io.FilterInputStream#mark(int)
+ */
+ public synchronized void mark(int readlimit) {
+ if (is != null)
+ is.mark(readlimit);
+ }
+
+ /**
+ * @see java.io.FilterInputStream#skip(long)
+ */
+ public long skip(long n) throws IOException {
+ checkIfClosed();
+ return is.skip(n);
+ }
+
+ /**
+ * @see java.io.FilterInputStream#read(byte[])
+ */
+ public int read(byte b[]) throws IOException {
+ checkIfClosed();
+ return is.read(b);
+ }
+
+ /**
+ * @see java.io.FilterInputStream#read(byte[], int, int)
+ */
+ public int read(byte b[], int off, int len) throws IOException {
+ checkIfClosed();
+ return is.read(b, off, len);
+ }
+
+ /**
+ * Check if the underlying InputStream is null. If so throw an Exception
+ *
+ * @throws IOException if the underlying InputStream is null
+ */
+ private void checkIfClosed() throws IOException {
+ if (is == null)
+ throw new IOException("Stream is closed");
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/ContentHandler.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/ContentHandler.java
new file mode 100644
index 000000000..b437e739e
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/ContentHandler.java
@@ -0,0 +1,177 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * <p>
+ * Receives notifications of the content of a plain RFC822 or MIME message.
+ * Implement this interface and register an instance of that implementation
+ * with a <code>MimeStreamParser</code> instance using its
+ * {@link org.apache.james.mime4j.MimeStreamParser#setContentHandler(ContentHandler)}
+ * method. The parser uses the <code>ContentHandler</code> instance to report
+ * basic message-related events like the start and end of the body of a
+ * part in a multipart MIME entity.
+ * </p>
+ * <p>
+ * Events will be generated in the order the corresponding elements occur in
+ * the message stream parsed by the parser. E.g.:
+ * <pre>
+ * startMessage()
+ * startHeader()
+ * field(...)
+ * field(...)
+ * ...
+ * endHeader()
+ * startMultipart()
+ * preamble(...)
+ * startBodyPart()
+ * startHeader()
+ * field(...)
+ * field(...)
+ * ...
+ * endHeader()
+ * body()
+ * endBodyPart()
+ * startBodyPart()
+ * startHeader()
+ * field(...)
+ * field(...)
+ * ...
+ * endHeader()
+ * body()
+ * endBodyPart()
+ * epilogue(...)
+ * endMultipart()
+ * endMessage()
+ * </pre>
+ * The above shows an example of a MIME message consisting of a multipart
+ * body containing two body parts.
+ * </p>
+ * <p>
+ * See MIME RFCs 2045-2049 for more information on the structure of MIME
+ * messages and RFC 822 and 2822 for the general structure of Internet mail
+ * messages.
+ * </p>
+ *
+ *
+ * @version $Id: ContentHandler.java,v 1.3 2004/10/02 12:41:10 ntherning Exp $
+ */
+public interface ContentHandler {
+ /**
+ * Called when a new message starts (a top level message or an embedded
+ * rfc822 message).
+ */
+ void startMessage();
+
+ /**
+ * Called when a message ends.
+ */
+ void endMessage();
+
+ /**
+ * Called when a new body part starts inside a
+ * <code>multipart/*</code> entity.
+ */
+ void startBodyPart();
+
+ /**
+ * Called when a body part ends.
+ */
+ void endBodyPart();
+
+ /**
+ * Called when a header (of a message or body part) is about to be parsed.
+ */
+ void startHeader();
+
+ /**
+ * Called for each field of a header.
+ *
+ * @param fieldData the raw contents of the field
+ * (<code>Field-Name: field value</code>). The value will not be
+ * unfolded.
+ */
+ void field(String fieldData);
+
+ /**
+ * Called when there are no more header fields in a message or body part.
+ */
+ void endHeader();
+
+ /**
+ * Called for the preamble (whatever comes before the first body part)
+ * of a <code>multipart/*</code> entity.
+ *
+ * @param is used to get the contents of the preamble.
+ * @throws IOException should be thrown on I/O errors.
+ */
+ void preamble(InputStream is) throws IOException;
+
+ /**
+ * Called for the epilogue (whatever comes after the final body part)
+ * of a <code>multipart/*</code> entity.
+ *
+ * @param is used to get the contents of the epilogue.
+ * @throws IOException should be thrown on I/O errors.
+ */
+ void epilogue(InputStream is) throws IOException;
+
+ /**
+ * Called when the body of a multipart entity is about to be parsed.
+ *
+ * @param bd encapsulates the values (either read from the
+ * message stream or, if not present, determined implictly
+ * as described in the
+ * MIME rfc:s) of the <code>Content-Type</code> and
+ * <code>Content-Transfer-Encoding</code> header fields.
+ */
+ void startMultipart(BodyDescriptor bd);
+
+ /**
+ * Called when the body of an entity has been parsed.
+ */
+ void endMultipart();
+
+ /**
+ * Called when the body of a discrete (non-multipart) entity is about to
+ * be parsed.
+ *
+ * @param bd see {@link #startMultipart(BodyDescriptor)}
+ * @param is the contents of the body. NOTE: this is the raw body contents
+ * - it will not be decoded if encoded. The <code>bd</code>
+ * parameter should be used to determine how the stream data
+ * should be decoded.
+ * @throws IOException should be thrown on I/O errors.
+ */
+ void body(BodyDescriptor bd, InputStream is) throws IOException;
+
+ /**
+ * Called when a new entity (message or body part) starts and the
+ * parser is in <code>raw</code> mode.
+ *
+ * @param is the raw contents of the entity.
+ * @throws IOException should be thrown on I/O errors.
+ * @see MimeStreamParser#setRaw(boolean)
+ */
+ void raw(InputStream is) throws IOException;
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/EOLConvertingInputStream.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/EOLConvertingInputStream.java
new file mode 100644
index 000000000..d6ef706b2
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/EOLConvertingInputStream.java
@@ -0,0 +1,139 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+
+/**
+ * InputStream which converts <code>\r</code>
+ * bytes not followed by <code>\n</code> and <code>\n</code> not
+ * preceded by <code>\r</code> to <code>\r\n</code>.
+ *
+ *
+ * @version $Id: EOLConvertingInputStream.java,v 1.4 2004/11/29 13:15:42 ntherning Exp $
+ */
+public class EOLConvertingInputStream extends InputStream {
+ /** Converts single '\r' to '\r\n' */
+ public static final int CONVERT_CR = 1;
+ /** Converts single '\n' to '\r\n' */
+ public static final int CONVERT_LF = 2;
+ /** Converts single '\r' and '\n' to '\r\n' */
+ public static final int CONVERT_BOTH = 3;
+
+ private PushbackInputStream in = null;
+ private int previous = 0;
+ private int flags = CONVERT_BOTH;
+ private int size = 0;
+ private int pos = 0;
+ private int nextTenPctPos;
+ private int tenPctSize;
+ private Callback callback;
+
+ public interface Callback {
+ public void report(int bytesRead);
+ }
+
+ /**
+ * Creates a new <code>EOLConvertingInputStream</code>
+ * instance converting bytes in the given <code>InputStream</code>.
+ * The flag <code>CONVERT_BOTH</code> is the default.
+ *
+ * @param in the <code>InputStream</code> to read from.
+ */
+ public EOLConvertingInputStream(InputStream _in) {
+ super();
+ in = new PushbackInputStream(_in, 2);
+ }
+
+ /**
+ * Creates a new <code>EOLConvertingInputStream</code>
+ * instance converting bytes in the given <code>InputStream</code>.
+ *
+ * @param _in the <code>InputStream</code> to read from.
+ * @param _size the size of the input stream (need not be exact)
+ * @param _callback a callback reporting when each 10% of stream's size is reached
+ */
+ public EOLConvertingInputStream(InputStream _in, int _size, Callback _callback) {
+ this(_in);
+ size = _size;
+ tenPctSize = size / 10;
+ nextTenPctPos = tenPctSize;
+ callback = _callback;
+ }
+
+ /**
+ * Closes the underlying stream.
+ *
+ * @throws IOException on I/O errors.
+ */
+ public void close() throws IOException {
+ in.close();
+ }
+
+ private int readByte() throws IOException {
+ int b = in.read();
+ if (b != -1) {
+ if (callback != null && pos++ == nextTenPctPos) {
+ nextTenPctPos += tenPctSize;
+ if (callback != null) {
+ callback.report(pos);
+ }
+ }
+ }
+ return b;
+ }
+
+ private void unreadByte(int c) throws IOException {
+ in.unread(c);
+ pos--;
+ }
+
+ /**
+ * @see java.io.InputStream#read()
+ */
+ public int read() throws IOException {
+ int b = readByte();
+
+ if (b == -1) {
+ pos = size;
+ return -1;
+ }
+
+ if ((flags & CONVERT_CR) != 0 && b == '\r') {
+ int c = readByte();
+ if (c != -1) {
+ unreadByte(c);
+ }
+ if (c != '\n') {
+ unreadByte('\n');
+ }
+ } else if ((flags & CONVERT_LF) != 0 && b == '\n' && previous != '\r') {
+ b = '\r';
+ unreadByte('\n');
+ }
+
+ previous = b;
+
+ return b;
+ }
+
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/Log.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/Log.java
new file mode 100644
index 000000000..5eeead5f3
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/Log.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2009 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 org.apache.james.mime4j;
+
+/**
+ * Empty stub for the apache logging library.
+ */
+public class Log {
+ private static final String LOG_TAG = "Email Log";
+
+ public Log(Class mClazz) {
+ }
+
+ public boolean isDebugEnabled() {
+ return false;
+ }
+
+ public boolean isErrorEnabled() {
+ return true;
+ }
+
+ public boolean isFatalEnabled() {
+ return true;
+ }
+
+ public boolean isInfoEnabled() {
+ return false;
+ }
+
+ public boolean isTraceEnabled() {
+ return false;
+ }
+
+ public boolean isWarnEnabled() {
+ return true;
+ }
+
+ public void trace(Object message) {
+ if (!isTraceEnabled()) return;
+ android.util.Log.v(LOG_TAG, toString(message, null));
+ }
+
+ public void trace(Object message, Throwable t) {
+ if (!isTraceEnabled()) return;
+ android.util.Log.v(LOG_TAG, toString(message, t));
+ }
+
+ public void debug(Object message) {
+ if (!isDebugEnabled()) return;
+ android.util.Log.d(LOG_TAG, toString(message, null));
+ }
+
+ public void debug(Object message, Throwable t) {
+ if (!isDebugEnabled()) return;
+ android.util.Log.d(LOG_TAG, toString(message, t));
+ }
+
+ public void info(Object message) {
+ if (!isInfoEnabled()) return;
+ android.util.Log.i(LOG_TAG, toString(message, null));
+ }
+
+ public void info(Object message, Throwable t) {
+ if (!isInfoEnabled()) return;
+ android.util.Log.i(LOG_TAG, toString(message, t));
+ }
+
+ public void warn(Object message) {
+ android.util.Log.w(LOG_TAG, toString(message, null));
+ }
+
+ public void warn(Object message, Throwable t) {
+ android.util.Log.w(LOG_TAG, toString(message, t));
+ }
+
+ public void error(Object message) {
+ android.util.Log.e(LOG_TAG, toString(message, null));
+ }
+
+ public void error(Object message, Throwable t) {
+ android.util.Log.e(LOG_TAG, toString(message, t));
+ }
+
+ public void fatal(Object message) {
+ android.util.Log.e(LOG_TAG, toString(message, null));
+ }
+
+ public void fatal(Object message, Throwable t) {
+ android.util.Log.e(LOG_TAG, toString(message, t));
+ }
+
+ private static String toString(Object o, Throwable t) {
+ String m = (o == null) ? "(null)" : o.toString();
+ if (t == null) {
+ return m;
+ } else {
+ return m + " " + t.getMessage();
+ }
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/LogFactory.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/LogFactory.java
new file mode 100644
index 000000000..ed6e3de3d
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/LogFactory.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2009 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 org.apache.james.mime4j;
+
+/**
+ * Empty stub for the apache logging library.
+ */
+public final class LogFactory {
+ private LogFactory() {
+ }
+
+ public static Log getLog(Class clazz) {
+ return new Log(clazz);
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/MimeBoundaryInputStream.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/MimeBoundaryInputStream.java
new file mode 100644
index 000000000..c6d6f248a
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/MimeBoundaryInputStream.java
@@ -0,0 +1,184 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+
+/**
+ * Stream that constrains itself to a single MIME body part.
+ * After the stream ends (i.e. read() returns -1) {@link #hasMoreParts()}
+ * can be used to determine if a final boundary has been seen or not.
+ * If {@link #parentEOF()} is <code>true</code> an unexpected end of stream
+ * has been detected in the parent stream.
+ *
+ *
+ *
+ * @version $Id: MimeBoundaryInputStream.java,v 1.2 2004/11/29 13:15:42 ntherning Exp $
+ */
+public class MimeBoundaryInputStream extends InputStream {
+
+ private PushbackInputStream s = null;
+ private byte[] boundary = null;
+ private boolean first = true;
+ private boolean eof = false;
+ private boolean parenteof = false;
+ private boolean moreParts = true;
+
+ /**
+ * Creates a new MimeBoundaryInputStream.
+ * @param s The underlying stream.
+ * @param boundary Boundary string (not including leading hyphens).
+ */
+ public MimeBoundaryInputStream(InputStream s, String boundary)
+ throws IOException {
+
+ this.s = new PushbackInputStream(s, boundary.length() + 4);
+
+ boundary = "--" + boundary;
+ this.boundary = new byte[boundary.length()];
+ for (int i = 0; i < this.boundary.length; i++) {
+ this.boundary[i] = (byte) boundary.charAt(i);
+ }
+
+ /*
+ * By reading one byte we will update moreParts to be as expected
+ * before any bytes have been read.
+ */
+ int b = read();
+ if (b != -1) {
+ this.s.unread(b);
+ }
+ }
+
+ /**
+ * Closes the underlying stream.
+ *
+ * @throws IOException on I/O errors.
+ */
+ public void close() throws IOException {
+ s.close();
+ }
+
+ /**
+ * Determines if the underlying stream has more parts (this stream has
+ * not seen an end boundary).
+ *
+ * @return <code>true</code> if there are more parts in the underlying
+ * stream, <code>false</code> otherwise.
+ */
+ public boolean hasMoreParts() {
+ return moreParts;
+ }
+
+ /**
+ * Determines if the parent stream has reached EOF
+ *
+ * @return <code>true</code> if EOF has been reached for the parent stream,
+ * <code>false</code> otherwise.
+ */
+ public boolean parentEOF() {
+ return parenteof;
+ }
+
+ /**
+ * Consumes all unread bytes of this stream. After a call to this method
+ * this stream will have reached EOF.
+ *
+ * @throws IOException on I/O errors.
+ */
+ public void consume() throws IOException {
+ while (read() != -1) {
+ }
+ }
+
+ /**
+ * @see java.io.InputStream#read()
+ */
+ public int read() throws IOException {
+ if (eof) {
+ return -1;
+ }
+
+ if (first) {
+ first = false;
+ if (matchBoundary()) {
+ return -1;
+ }
+ }
+
+ int b1 = s.read();
+ int b2 = s.read();
+
+ if (b1 == '\r' && b2 == '\n') {
+ if (matchBoundary()) {
+ return -1;
+ }
+ }
+
+ if (b2 != -1) {
+ s.unread(b2);
+ }
+
+ parenteof = b1 == -1;
+ eof = parenteof;
+
+ return b1;
+ }
+
+ private boolean matchBoundary() throws IOException {
+
+ for (int i = 0; i < boundary.length; i++) {
+ int b = s.read();
+ if (b != boundary[i]) {
+ if (b != -1) {
+ s.unread(b);
+ }
+ for (int j = i - 1; j >= 0; j--) {
+ s.unread(boundary[j]);
+ }
+ return false;
+ }
+ }
+
+ /*
+ * We have a match. Is it an end boundary?
+ */
+ int prev = s.read();
+ int curr = s.read();
+ moreParts = !(prev == '-' && curr == '-');
+ do {
+ if (curr == '\n' && prev == '\r') {
+ break;
+ }
+ prev = curr;
+ } while ((curr = s.read()) != -1);
+
+ if (curr == -1) {
+ moreParts = false;
+ parenteof = true;
+ }
+
+ eof = true;
+
+ return true;
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/MimeStreamParser.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/MimeStreamParser.java
new file mode 100644
index 000000000..a8aad5a38
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/MimeStreamParser.java
@@ -0,0 +1,324 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j;
+
+import org.apache.james.mime4j.decoder.Base64InputStream;
+import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.BitSet;
+import java.util.LinkedList;
+
+/**
+ * <p>
+ * Parses MIME (or RFC822) message streams of bytes or characters and reports
+ * parsing events to a <code>ContentHandler</code> instance.
+ * </p>
+ * <p>
+ * Typical usage:<br/>
+ * <pre>
+ * ContentHandler handler = new MyHandler();
+ * MimeStreamParser parser = new MimeStreamParser();
+ * parser.setContentHandler(handler);
+ * parser.parse(new BufferedInputStream(new FileInputStream("mime.msg")));
+ * </pre>
+ * <strong>NOTE:</strong> All lines must end with CRLF
+ * (<code>\r\n</code>). If you are unsure of the line endings in your stream
+ * you should wrap it in a {@link org.apache.james.mime4j.EOLConvertingInputStream} instance.
+ *
+ *
+ * @version $Id: MimeStreamParser.java,v 1.8 2005/02/11 10:12:02 ntherning Exp $
+ */
+public class MimeStreamParser {
+ private static final Log log = LogFactory.getLog(MimeStreamParser.class);
+
+ private static BitSet fieldChars = null;
+
+ private RootInputStream rootStream = null;
+ private LinkedList<BodyDescriptor> bodyDescriptors = new LinkedList<BodyDescriptor>();
+ private ContentHandler handler = null;
+ private boolean raw = false;
+ private boolean prematureEof = false;
+
+ static {
+ fieldChars = new BitSet();
+ for (int i = 0x21; i <= 0x39; i++) {
+ fieldChars.set(i);
+ }
+ for (int i = 0x3b; i <= 0x7e; i++) {
+ fieldChars.set(i);
+ }
+ }
+
+ /**
+ * Creates a new <code>MimeStreamParser</code> instance.
+ */
+ public MimeStreamParser() {
+ }
+
+ /**
+ * Parses a stream of bytes containing a MIME message.
+ *
+ * @param is the stream to parse.
+ * @throws IOException on I/O errors.
+ */
+ public void parse(InputStream is) throws IOException {
+ rootStream = new RootInputStream(is);
+ parseMessage(rootStream);
+ }
+
+ /**
+ * Determines if this parser is currently in raw mode.
+ *
+ * @return <code>true</code> if in raw mode, <code>false</code>
+ * otherwise.
+ * @see #setRaw(boolean)
+ */
+ public boolean isRaw() {
+ return raw;
+ }
+
+ /**
+ * Enables or disables raw mode. In raw mode all future entities
+ * (messages or body parts) in the stream will be reported to the
+ * {@link ContentHandler#raw(InputStream)} handler method only.
+ * The stream will contain the entire unparsed entity contents
+ * including header fields and whatever is in the body.
+ *
+ * @param raw <code>true</code> enables raw mode, <code>false</code>
+ * disables it.
+ */
+ public void setRaw(boolean raw) {
+ this.raw = raw;
+ }
+
+ /**
+ * Finishes the parsing and stops reading lines.
+ * NOTE: No more lines will be parsed but the parser
+ * will still call
+ * {@link ContentHandler#endMultipart()},
+ * {@link ContentHandler#endBodyPart()},
+ * {@link ContentHandler#endMessage()}, etc to match previous calls
+ * to
+ * {@link ContentHandler#startMultipart(BodyDescriptor)},
+ * {@link ContentHandler#startBodyPart()},
+ * {@link ContentHandler#startMessage()}, etc.
+ */
+ public void stop() {
+ rootStream.truncate();
+ }
+
+ /**
+ * Parses an entity which consists of a header followed by a body containing
+ * arbitrary data, body parts or an embedded message.
+ *
+ * @param is the stream to parse.
+ * @throws IOException on I/O errors.
+ */
+ private void parseEntity(InputStream is) throws IOException {
+ BodyDescriptor bd = parseHeader(is);
+
+ if (bd.isMultipart()) {
+ bodyDescriptors.addFirst(bd);
+
+ handler.startMultipart(bd);
+
+ MimeBoundaryInputStream tempIs =
+ new MimeBoundaryInputStream(is, bd.getBoundary());
+ handler.preamble(new CloseShieldInputStream(tempIs));
+ tempIs.consume();
+
+ while (tempIs.hasMoreParts()) {
+ tempIs = new MimeBoundaryInputStream(is, bd.getBoundary());
+ parseBodyPart(tempIs);
+ tempIs.consume();
+ if (tempIs.parentEOF()) {
+ prematureEof = true;
+// if (log.isWarnEnabled()) {
+// log.warn("Line " + rootStream.getLineNumber()
+// + ": Body part ended prematurely. "
+// + "Higher level boundary detected or "
+// + "EOF reached.");
+// }
+ break;
+ }
+ }
+
+ handler.epilogue(new CloseShieldInputStream(is));
+
+ handler.endMultipart();
+
+ bodyDescriptors.removeFirst();
+
+ } else if (bd.isMessage()) {
+ if (bd.isBase64Encoded()) {
+ log.warn("base64 encoded message/rfc822 detected");
+ is = new EOLConvertingInputStream(
+ new Base64InputStream(is));
+ } else if (bd.isQuotedPrintableEncoded()) {
+ log.warn("quoted-printable encoded message/rfc822 detected");
+ is = new EOLConvertingInputStream(
+ new QuotedPrintableInputStream(is));
+ }
+ bodyDescriptors.addFirst(bd);
+ parseMessage(is);
+ bodyDescriptors.removeFirst();
+ } else {
+ handler.body(bd, new CloseShieldInputStream(is));
+ }
+
+ /*
+ * Make sure the stream has been consumed.
+ */
+ while (is.read() != -1) {
+ }
+ }
+
+ private void parseMessage(InputStream is) throws IOException {
+ if (raw) {
+ handler.raw(new CloseShieldInputStream(is));
+ } else {
+ handler.startMessage();
+ parseEntity(is);
+ handler.endMessage();
+ }
+ }
+
+ public boolean getPrematureEof() {
+ return prematureEof;
+ }
+
+ private void parseBodyPart(InputStream is) throws IOException {
+ if (raw) {
+ handler.raw(new CloseShieldInputStream(is));
+ } else {
+ handler.startBodyPart();
+ parseEntity(is);
+ handler.endBodyPart();
+ }
+ }
+
+ /**
+ * Parses a header.
+ *
+ * @param is the stream to parse.
+ * @return a <code>BodyDescriptor</code> describing the body following
+ * the header.
+ */
+ private BodyDescriptor parseHeader(InputStream is) throws IOException {
+ BodyDescriptor bd = new BodyDescriptor(bodyDescriptors.isEmpty()
+ ? null : (BodyDescriptor) bodyDescriptors.getFirst());
+
+ handler.startHeader();
+
+ int lineNumber = rootStream.getLineNumber();
+
+ StringBuffer sb = new StringBuffer();
+ int curr = 0;
+ int prev = 0;
+ while ((curr = is.read()) != -1) {
+ if (curr == '\n' && (prev == '\n' || prev == 0)) {
+ /*
+ * [\r]\n[\r]\n or an immediate \r\n have been seen.
+ */
+ sb.deleteCharAt(sb.length() - 1);
+ break;
+ }
+ sb.append((char) curr);
+ prev = curr == '\r' ? prev : curr;
+ }
+
+// if (curr == -1 && log.isWarnEnabled()) {
+// log.warn("Line " + rootStream.getLineNumber()
+// + ": Unexpected end of headers detected. "
+// + "Boundary detected in header or EOF reached.");
+// }
+
+ int start = 0;
+ int pos = 0;
+ int startLineNumber = lineNumber;
+ while (pos < sb.length()) {
+ while (pos < sb.length() && sb.charAt(pos) != '\r') {
+ pos++;
+ }
+ if (pos < sb.length() - 1 && sb.charAt(pos + 1) != '\n') {
+ pos++;
+ continue;
+ }
+
+ if (pos >= sb.length() - 2 || fieldChars.get(sb.charAt(pos + 2))) {
+
+ /*
+ * field should be the complete field data excluding the
+ * trailing \r\n.
+ */
+ String field = sb.substring(start, pos);
+ start = pos + 2;
+
+ /*
+ * Check for a valid field.
+ */
+ int index = field.indexOf(':');
+ boolean valid = false;
+ if (index != -1 && fieldChars.get(field.charAt(0))) {
+ valid = true;
+ String fieldName = field.substring(0, index).trim();
+ for (int i = 0; i < fieldName.length(); i++) {
+ if (!fieldChars.get(fieldName.charAt(i))) {
+ valid = false;
+ break;
+ }
+ }
+
+ if (valid) {
+ handler.field(field);
+ bd.addField(fieldName, field.substring(index + 1));
+ }
+ }
+
+ if (!valid && log.isWarnEnabled()) {
+ log.warn("Line " + startLineNumber
+ + ": Ignoring invalid field: '" + field.trim() + "'");
+ }
+
+ startLineNumber = lineNumber;
+ }
+
+ pos += 2;
+ lineNumber++;
+ }
+
+ handler.endHeader();
+
+ return bd;
+ }
+
+ /**
+ * Sets the <code>ContentHandler</code> to use when reporting
+ * parsing events.
+ *
+ * @param h the <code>ContentHandler</code>.
+ */
+ public void setContentHandler(ContentHandler h) {
+ this.handler = h;
+ }
+
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/RootInputStream.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/RootInputStream.java
new file mode 100644
index 000000000..cc8b2411c
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/RootInputStream.java
@@ -0,0 +1,111 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * <code>InputStream</code> used by the parser to wrap the original user
+ * supplied stream. This stream keeps track of the current line number and
+ * can also be truncated. When truncated the stream will appear to have
+ * reached end of file. This is used by the parser's
+ * {@link org.apache.james.mime4j.MimeStreamParser#stop()} method.
+ *
+ *
+ * @version $Id: RootInputStream.java,v 1.2 2004/10/02 12:41:10 ntherning Exp $
+ */
+class RootInputStream extends InputStream {
+ private InputStream is = null;
+ private int lineNumber = 1;
+ private int prev = -1;
+ private boolean truncated = false;
+
+ /**
+ * Creates a new <code>RootInputStream</code>.
+ *
+ * @param in the stream to read from.
+ */
+ public RootInputStream(InputStream is) {
+ this.is = is;
+ }
+
+ /**
+ * Gets the current line number starting at 1
+ * (the number of <code>\r\n</code> read so far plus 1).
+ *
+ * @return the current line number.
+ */
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ /**
+ * Truncates this <code>InputStream</code>. After this call any
+ * call to {@link #read()}, {@link #read(byte[]) or
+ * {@link #read(byte[], int, int)} will return
+ * -1 as if end-of-file had been reached.
+ */
+ public void truncate() {
+ this.truncated = true;
+ }
+
+ /**
+ * @see java.io.InputStream#read()
+ */
+ public int read() throws IOException {
+ if (truncated) {
+ return -1;
+ }
+
+ int b = is.read();
+ if (prev == '\r' && b == '\n') {
+ lineNumber++;
+ }
+ prev = b;
+ return b;
+ }
+
+ /**
+ *
+ * @see java.io.InputStream#read(byte[], int, int)
+ */
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (truncated) {
+ return -1;
+ }
+
+ int n = is.read(b, off, len);
+ for (int i = off; i < off + n; i++) {
+ if (prev == '\r' && b[i] == '\n') {
+ lineNumber++;
+ }
+ prev = b[i];
+ }
+ return n;
+ }
+
+ /**
+ * @see java.io.InputStream#read(byte[])
+ */
+ public int read(byte[] b) throws IOException {
+ return read(b, 0, b.length);
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/codec/EncoderUtil.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/codec/EncoderUtil.java
new file mode 100644
index 000000000..6841bc998
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/codec/EncoderUtil.java
@@ -0,0 +1,630 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.codec;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.BitSet;
+import java.util.Locale;
+
+import org.apache.james.mime4j.util.CharsetUtil;
+
+/**
+ * ANDROID: THIS CLASS IS COPIED FROM A NEWER VERSION OF MIME4J
+ */
+
+/**
+ * Static methods for encoding header field values. This includes encoded-words
+ * as defined in <a href='http://www.faqs.org/rfcs/rfc2047.html'>RFC 2047</a>
+ * or display-names of an e-mail address, for example.
+ *
+ */
+public class EncoderUtil {
+
+ // This array is a lookup table that translates 6-bit positive integer index
+ // values into their "Base64 Alphabet" equivalents as specified in Table 1
+ // of RFC 2045.
+ // ANDROID: THIS TABLE IS COPIED FROM BASE64OUTPUTSTREAM
+ static final byte[] BASE64_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F',
+ 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
+ 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+ 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', '+', '/' };
+
+ // Byte used to pad output.
+ private static final byte BASE64_PAD = '=';
+
+ private static final BitSet Q_REGULAR_CHARS = initChars("=_?");
+
+ private static final BitSet Q_RESTRICTED_CHARS = initChars("=_?\"#$%&'(),.:;<>@[\\]^`{|}~");
+
+ private static final int MAX_USED_CHARACTERS = 50;
+
+ private static final String ENC_WORD_PREFIX = "=?";
+ private static final String ENC_WORD_SUFFIX = "?=";
+
+ private static final int ENCODED_WORD_MAX_LENGTH = 75; // RFC 2047
+
+ private static final BitSet TOKEN_CHARS = initChars("()<>@,;:\\\"/[]?=");
+
+ private static final BitSet ATEXT_CHARS = initChars("()<>@.,;:\\\"[]");
+
+ private static BitSet initChars(String specials) {
+ BitSet bs = new BitSet(128);
+ for (char ch = 33; ch < 127; ch++) {
+ if (specials.indexOf(ch) == -1) {
+ bs.set(ch);
+ }
+ }
+ return bs;
+ }
+
+ /**
+ * Selects one of the two encodings specified in RFC 2047.
+ */
+ public enum Encoding {
+ /** The B encoding (identical to base64 defined in RFC 2045). */
+ B,
+ /** The Q encoding (similar to quoted-printable defined in RFC 2045). */
+ Q
+ }
+
+ /**
+ * Indicates the intended usage of an encoded word.
+ */
+ public enum Usage {
+ /**
+ * Encoded word is used to replace a 'text' token in any Subject or
+ * Comments header field.
+ */
+ TEXT_TOKEN,
+ /**
+ * Encoded word is used to replace a 'word' entity within a 'phrase',
+ * for example, one that precedes an address in a From, To, or Cc
+ * header.
+ */
+ WORD_ENTITY
+ }
+
+ private EncoderUtil() {
+ }
+
+ /**
+ * Encodes the display-name portion of an address. See <a
+ * href='http://www.faqs.org/rfcs/rfc5322.html'>RFC 5322</a> section 3.4
+ * and <a href='http://www.faqs.org/rfcs/rfc2047.html'>RFC 2047</a> section
+ * 5.3. The specified string should not be folded.
+ *
+ * @param displayName
+ * display-name to encode.
+ * @return encoded display-name.
+ */
+ public static String encodeAddressDisplayName(String displayName) {
+ // display-name = phrase
+ // phrase = 1*( encoded-word / word )
+ // word = atom / quoted-string
+ // atom = [CFWS] 1*atext [CFWS]
+ // CFWS = comment or folding white space
+
+ if (isAtomPhrase(displayName)) {
+ return displayName;
+ } else if (hasToBeEncoded(displayName, 0)) {
+ return encodeEncodedWord(displayName, Usage.WORD_ENTITY);
+ } else {
+ return quote(displayName);
+ }
+ }
+
+ /**
+ * Encodes the local part of an address specification as described in RFC
+ * 5322 section 3.4.1. Leading and trailing CFWS should have been removed
+ * before calling this method. The specified string should not contain any
+ * illegal (control or non-ASCII) characters.
+ *
+ * @param localPart
+ * the local part to encode
+ * @return the encoded local part.
+ */
+ public static String encodeAddressLocalPart(String localPart) {
+ // local-part = dot-atom / quoted-string
+ // dot-atom = [CFWS] dot-atom-text [CFWS]
+ // CFWS = comment or folding white space
+
+ if (isDotAtomText(localPart)) {
+ return localPart;
+ } else {
+ return quote(localPart);
+ }
+ }
+
+ /**
+ * Encodes the specified strings into a header parameter as described in RFC
+ * 2045 section 5.1 and RFC 2183 section 2. The specified strings should not
+ * contain any illegal (control or non-ASCII) characters.
+ *
+ * @param name
+ * parameter name.
+ * @param value
+ * parameter value.
+ * @return encoded result.
+ */
+ public static String encodeHeaderParameter(String name, String value) {
+ name = name.toLowerCase(Locale.US);
+
+ // value := token / quoted-string
+ if (isToken(value)) {
+ return name + "=" + value;
+ } else {
+ return name + "=" + quote(value);
+ }
+ }
+
+ /**
+ * Shortcut method that encodes the specified text into an encoded-word if
+ * the text has to be encoded.
+ *
+ * @param text
+ * text to encode.
+ * @param usage
+ * whether the encoded-word is to be used to replace a text token
+ * or a word entity (see RFC 822).
+ * @param usedCharacters
+ * number of characters already used up (<code>0 <= usedCharacters <= 50</code>).
+ * @return the specified text if encoding is not necessary or an encoded
+ * word or a sequence of encoded words otherwise.
+ */
+ public static String encodeIfNecessary(String text, Usage usage,
+ int usedCharacters) {
+ if (hasToBeEncoded(text, usedCharacters))
+ return encodeEncodedWord(text, usage, usedCharacters);
+ else
+ return text;
+ }
+
+ /**
+ * Determines if the specified string has to encoded into an encoded-word.
+ * Returns <code>true</code> if the text contains characters that don't
+ * fall into the printable ASCII character set or if the text contains a
+ * 'word' (sequence of non-whitespace characters) longer than 77 characters
+ * (including characters already used up in the line).
+ *
+ * @param text
+ * text to analyze.
+ * @param usedCharacters
+ * number of characters already used up (<code>0 <= usedCharacters <= 50</code>).
+ * @return <code>true</code> if the specified text has to be encoded into
+ * an encoded-word, <code>false</code> otherwise.
+ */
+ public static boolean hasToBeEncoded(String text, int usedCharacters) {
+ if (text == null)
+ throw new IllegalArgumentException();
+ if (usedCharacters < 0 || usedCharacters > MAX_USED_CHARACTERS)
+ throw new IllegalArgumentException();
+
+ int nonWhiteSpaceCount = usedCharacters;
+
+ for (int idx = 0; idx < text.length(); idx++) {
+ char ch = text.charAt(idx);
+ if (ch == '\t' || ch == ' ') {
+ nonWhiteSpaceCount = 0;
+ } else {
+ nonWhiteSpaceCount++;
+ if (nonWhiteSpaceCount > 77) {
+ // Line cannot be folded into multiple lines with no more
+ // than 78 characters each. Encoding as encoded-words makes
+ // that possible. One character has to be reserved for
+ // folding white space; that leaves 77 characters.
+ return true;
+ }
+
+ if (ch < 32 || ch >= 127) {
+ // non-printable ascii character has to be encoded
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Encodes the specified text into an encoded word or a sequence of encoded
+ * words separated by space. The text is separated into a sequence of
+ * encoded words if it does not fit in a single one.
+ * <p>
+ * The charset to encode the specified text into a byte array and the
+ * encoding to use for the encoded-word are detected automatically.
+ * <p>
+ * This method assumes that zero characters have already been used up in the
+ * current line.
+ *
+ * @param text
+ * text to encode.
+ * @param usage
+ * whether the encoded-word is to be used to replace a text token
+ * or a word entity (see RFC 822).
+ * @return the encoded word (or sequence of encoded words if the given text
+ * does not fit in a single encoded word).
+ * @see #hasToBeEncoded(String, int)
+ */
+ public static String encodeEncodedWord(String text, Usage usage) {
+ return encodeEncodedWord(text, usage, 0, null, null);
+ }
+
+ /**
+ * Encodes the specified text into an encoded word or a sequence of encoded
+ * words separated by space. The text is separated into a sequence of
+ * encoded words if it does not fit in a single one.
+ * <p>
+ * The charset to encode the specified text into a byte array and the
+ * encoding to use for the encoded-word are detected automatically.
+ *
+ * @param text
+ * text to encode.
+ * @param usage
+ * whether the encoded-word is to be used to replace a text token
+ * or a word entity (see RFC 822).
+ * @param usedCharacters
+ * number of characters already used up (<code>0 <= usedCharacters <= 50</code>).
+ * @return the encoded word (or sequence of encoded words if the given text
+ * does not fit in a single encoded word).
+ * @see #hasToBeEncoded(String, int)
+ */
+ public static String encodeEncodedWord(String text, Usage usage,
+ int usedCharacters) {
+ return encodeEncodedWord(text, usage, usedCharacters, null, null);
+ }
+
+ /**
+ * Encodes the specified text into an encoded word or a sequence of encoded
+ * words separated by space. The text is separated into a sequence of
+ * encoded words if it does not fit in a single one.
+ *
+ * @param text
+ * text to encode.
+ * @param usage
+ * whether the encoded-word is to be used to replace a text token
+ * or a word entity (see RFC 822).
+ * @param usedCharacters
+ * number of characters already used up (<code>0 <= usedCharacters <= 50</code>).
+ * @param charset
+ * the Java charset that should be used to encode the specified
+ * string into a byte array. A suitable charset is detected
+ * automatically if this parameter is <code>null</code>.
+ * @param encoding
+ * the encoding to use for the encoded-word (either B or Q). A
+ * suitable encoding is automatically chosen if this parameter is
+ * <code>null</code>.
+ * @return the encoded word (or sequence of encoded words if the given text
+ * does not fit in a single encoded word).
+ * @see #hasToBeEncoded(String, int)
+ */
+ public static String encodeEncodedWord(String text, Usage usage,
+ int usedCharacters, Charset charset, Encoding encoding) {
+ if (text == null)
+ throw new IllegalArgumentException();
+ if (usedCharacters < 0 || usedCharacters > MAX_USED_CHARACTERS)
+ throw new IllegalArgumentException();
+
+ if (charset == null)
+ charset = determineCharset(text);
+
+ String mimeCharset = CharsetUtil.toMimeCharset(charset.name());
+ if (mimeCharset == null) {
+ // cannot happen if charset was originally null
+ throw new IllegalArgumentException("Unsupported charset");
+ }
+
+ byte[] bytes = encode(text, charset);
+
+ if (encoding == null)
+ encoding = determineEncoding(bytes, usage);
+
+ if (encoding == Encoding.B) {
+ String prefix = ENC_WORD_PREFIX + mimeCharset + "?B?";
+ return encodeB(prefix, text, usedCharacters, charset, bytes);
+ } else {
+ String prefix = ENC_WORD_PREFIX + mimeCharset + "?Q?";
+ return encodeQ(prefix, text, usage, usedCharacters, charset, bytes);
+ }
+ }
+
+ /**
+ * Encodes the specified byte array using the B encoding defined in RFC
+ * 2047.
+ *
+ * @param bytes
+ * byte array to encode.
+ * @return encoded string.
+ */
+ public static String encodeB(byte[] bytes) {
+ StringBuilder sb = new StringBuilder();
+
+ int idx = 0;
+ final int end = bytes.length;
+ for (; idx < end - 2; idx += 3) {
+ int data = (bytes[idx] & 0xff) << 16 | (bytes[idx + 1] & 0xff) << 8
+ | bytes[idx + 2] & 0xff;
+ sb.append((char) BASE64_TABLE[data >> 18 & 0x3f]);
+ sb.append((char) BASE64_TABLE[data >> 12 & 0x3f]);
+ sb.append((char) BASE64_TABLE[data >> 6 & 0x3f]);
+ sb.append((char) BASE64_TABLE[data & 0x3f]);
+ }
+
+ if (idx == end - 2) {
+ int data = (bytes[idx] & 0xff) << 16 | (bytes[idx + 1] & 0xff) << 8;
+ sb.append((char) BASE64_TABLE[data >> 18 & 0x3f]);
+ sb.append((char) BASE64_TABLE[data >> 12 & 0x3f]);
+ sb.append((char) BASE64_TABLE[data >> 6 & 0x3f]);
+ sb.append((char) BASE64_PAD);
+
+ } else if (idx == end - 1) {
+ int data = (bytes[idx] & 0xff) << 16;
+ sb.append((char) BASE64_TABLE[data >> 18 & 0x3f]);
+ sb.append((char) BASE64_TABLE[data >> 12 & 0x3f]);
+ sb.append((char) BASE64_PAD);
+ sb.append((char) BASE64_PAD);
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Encodes the specified byte array using the Q encoding defined in RFC
+ * 2047.
+ *
+ * @param bytes
+ * byte array to encode.
+ * @param usage
+ * whether the encoded-word is to be used to replace a text token
+ * or a word entity (see RFC 822).
+ * @return encoded string.
+ */
+ public static String encodeQ(byte[] bytes, Usage usage) {
+ BitSet qChars = usage == Usage.TEXT_TOKEN ? Q_REGULAR_CHARS
+ : Q_RESTRICTED_CHARS;
+
+ StringBuilder sb = new StringBuilder();
+
+ final int end = bytes.length;
+ for (int idx = 0; idx < end; idx++) {
+ int v = bytes[idx] & 0xff;
+ if (v == 32) {
+ sb.append('_');
+ } else if (!qChars.get(v)) {
+ sb.append('=');
+ sb.append(hexDigit(v >>> 4));
+ sb.append(hexDigit(v & 0xf));
+ } else {
+ sb.append((char) v);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Tests whether the specified string is a token as defined in RFC 2045
+ * section 5.1.
+ *
+ * @param str
+ * string to test.
+ * @return <code>true</code> if the specified string is a RFC 2045 token,
+ * <code>false</code> otherwise.
+ */
+ public static boolean isToken(String str) {
+ // token := 1*<any (US-ASCII) CHAR except SPACE, CTLs, or tspecials>
+ // tspecials := "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" /
+ // <"> / "/" / "[" / "]" / "?" / "="
+ // CTL := 0.- 31., 127.
+
+ final int length = str.length();
+ if (length == 0)
+ return false;
+
+ for (int idx = 0; idx < length; idx++) {
+ char ch = str.charAt(idx);
+ if (!TOKEN_CHARS.get(ch))
+ return false;
+ }
+
+ return true;
+ }
+
+ private static boolean isAtomPhrase(String str) {
+ // atom = [CFWS] 1*atext [CFWS]
+
+ boolean containsAText = false;
+
+ final int length = str.length();
+ for (int idx = 0; idx < length; idx++) {
+ char ch = str.charAt(idx);
+ if (ATEXT_CHARS.get(ch)) {
+ containsAText = true;
+ } else if (!CharsetUtil.isWhitespace(ch)) {
+ return false;
+ }
+ }
+
+ return containsAText;
+ }
+
+ // RFC 5322 section 3.2.3
+ private static boolean isDotAtomText(String str) {
+ // dot-atom-text = 1*atext *("." 1*atext)
+ // atext = ALPHA / DIGIT / "!" / "#" / "$" / "%" / "&" / "'" / "*" /
+ // "+" / "-" / "/" / "=" / "?" / "^" / "_" / "`" / "{" / "|" / "}" / "~"
+
+ char prev = '.';
+
+ final int length = str.length();
+ if (length == 0)
+ return false;
+
+ for (int idx = 0; idx < length; idx++) {
+ char ch = str.charAt(idx);
+
+ if (ch == '.') {
+ if (prev == '.' || idx == length - 1)
+ return false;
+ } else {
+ if (!ATEXT_CHARS.get(ch))
+ return false;
+ }
+
+ prev = ch;
+ }
+
+ return true;
+ }
+
+ // RFC 5322 section 3.2.4
+ private static String quote(String str) {
+ // quoted-string = [CFWS] DQUOTE *([FWS] qcontent) [FWS] DQUOTE [CFWS]
+ // qcontent = qtext / quoted-pair
+ // qtext = %d33 / %d35-91 / %d93-126
+ // quoted-pair = ("\" (VCHAR / WSP))
+ // VCHAR = %x21-7E
+ // DQUOTE = %x22
+
+ String escaped = str.replaceAll("[\\\\\"]", "\\\\$0");
+ return "\"" + escaped + "\"";
+ }
+
+ private static String encodeB(String prefix, String text,
+ int usedCharacters, Charset charset, byte[] bytes) {
+ int encodedLength = bEncodedLength(bytes);
+
+ int totalLength = prefix.length() + encodedLength
+ + ENC_WORD_SUFFIX.length();
+ if (totalLength <= ENCODED_WORD_MAX_LENGTH - usedCharacters) {
+ return prefix + encodeB(bytes) + ENC_WORD_SUFFIX;
+ } else {
+ int splitOffset = text.offsetByCodePoints(text.length() / 2, -1);
+
+ String part1 = text.substring(0, splitOffset);
+ byte[] bytes1 = encode(part1, charset);
+ String word1 = encodeB(prefix, part1, usedCharacters, charset,
+ bytes1);
+
+ String part2 = text.substring(splitOffset);
+ byte[] bytes2 = encode(part2, charset);
+ String word2 = encodeB(prefix, part2, 0, charset, bytes2);
+
+ return word1 + " " + word2;
+ }
+ }
+
+ private static int bEncodedLength(byte[] bytes) {
+ return (bytes.length + 2) / 3 * 4;
+ }
+
+ private static String encodeQ(String prefix, String text, Usage usage,
+ int usedCharacters, Charset charset, byte[] bytes) {
+ int encodedLength = qEncodedLength(bytes, usage);
+
+ int totalLength = prefix.length() + encodedLength
+ + ENC_WORD_SUFFIX.length();
+ if (totalLength <= ENCODED_WORD_MAX_LENGTH - usedCharacters) {
+ return prefix + encodeQ(bytes, usage) + ENC_WORD_SUFFIX;
+ } else {
+ int splitOffset = text.offsetByCodePoints(text.length() / 2, -1);
+
+ String part1 = text.substring(0, splitOffset);
+ byte[] bytes1 = encode(part1, charset);
+ String word1 = encodeQ(prefix, part1, usage, usedCharacters,
+ charset, bytes1);
+
+ String part2 = text.substring(splitOffset);
+ byte[] bytes2 = encode(part2, charset);
+ String word2 = encodeQ(prefix, part2, usage, 0, charset, bytes2);
+
+ return word1 + " " + word2;
+ }
+ }
+
+ private static int qEncodedLength(byte[] bytes, Usage usage) {
+ BitSet qChars = usage == Usage.TEXT_TOKEN ? Q_REGULAR_CHARS
+ : Q_RESTRICTED_CHARS;
+
+ int count = 0;
+
+ for (int idx = 0; idx < bytes.length; idx++) {
+ int v = bytes[idx] & 0xff;
+ if (v == 32) {
+ count++;
+ } else if (!qChars.get(v)) {
+ count += 3;
+ } else {
+ count++;
+ }
+ }
+
+ return count;
+ }
+
+ private static byte[] encode(String text, Charset charset) {
+ ByteBuffer buffer = charset.encode(text);
+ byte[] bytes = new byte[buffer.limit()];
+ buffer.get(bytes);
+ return bytes;
+ }
+
+ private static Charset determineCharset(String text) {
+ // it is an important property of iso-8859-1 that it directly maps
+ // unicode code points 0000 to 00ff to byte values 00 to ff.
+ boolean ascii = true;
+ final int len = text.length();
+ for (int index = 0; index < len; index++) {
+ char ch = text.charAt(index);
+ if (ch > 0xff) {
+ return CharsetUtil.UTF_8;
+ }
+ if (ch > 0x7f) {
+ ascii = false;
+ }
+ }
+ return ascii ? CharsetUtil.US_ASCII : CharsetUtil.ISO_8859_1;
+ }
+
+ private static Encoding determineEncoding(byte[] bytes, Usage usage) {
+ if (bytes.length == 0)
+ return Encoding.Q;
+
+ BitSet qChars = usage == Usage.TEXT_TOKEN ? Q_REGULAR_CHARS
+ : Q_RESTRICTED_CHARS;
+
+ int qEncoded = 0;
+ for (int i = 0; i < bytes.length; i++) {
+ int v = bytes[i] & 0xff;
+ if (v != 32 && !qChars.get(v)) {
+ qEncoded++;
+ }
+ }
+
+ int percentage = qEncoded * 100 / bytes.length;
+ return percentage > 30 ? Encoding.B : Encoding.Q;
+ }
+
+ private static char hexDigit(int i) {
+ return i < 10 ? (char) (i + '0') : (char) (i - 10 + 'A');
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/Base64InputStream.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/Base64InputStream.java
new file mode 100644
index 000000000..77f5d7d4a
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/Base64InputStream.java
@@ -0,0 +1,151 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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. *
+ ****************************************************************/
+
+/**
+ * Modified to improve efficiency by Android 21-Aug-2009
+ */
+
+package org.apache.james.mime4j.decoder;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Performs Base-64 decoding on an underlying stream.
+ *
+ *
+ * @version $Id: Base64InputStream.java,v 1.3 2004/11/29 13:15:47 ntherning Exp $
+ */
+public class Base64InputStream extends InputStream {
+ private final InputStream s;
+ private int outCount = 0;
+ private int outIndex = 0;
+ private final int[] outputBuffer = new int[3];
+ private final byte[] inputBuffer = new byte[4];
+ private boolean done = false;
+
+ public Base64InputStream(InputStream s) {
+ this.s = s;
+ }
+
+ /**
+ * Closes the underlying stream.
+ *
+ * @throws IOException on I/O errors.
+ */
+ @Override
+ public void close() throws IOException {
+ s.close();
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (outIndex == outCount) {
+ fillBuffer();
+ if (outIndex == outCount) {
+ return -1;
+ }
+ }
+
+ return outputBuffer[outIndex++];
+ }
+
+ /**
+ * Retrieve data from the underlying stream, decode it,
+ * and put the results in the byteq.
+ * @throws IOException
+ */
+ private void fillBuffer() throws IOException {
+ outCount = 0;
+ outIndex = 0;
+ int inCount = 0;
+
+ int i;
+ // "done" is needed for the two successive '=' at the end
+ while (!done) {
+ switch (i = s.read()) {
+ case -1:
+ // No more input - just return, let outputBuffer drain out, and be done
+ return;
+ case '=':
+ // once we meet the first '=', avoid reading the second '='
+ done = true;
+ decodeAndEnqueue(inCount);
+ return;
+ default:
+ byte sX = TRANSLATION[i];
+ if (sX < 0) continue;
+ inputBuffer[inCount++] = sX;
+ if (inCount == 4) {
+ decodeAndEnqueue(inCount);
+ return;
+ }
+ break;
+ }
+ }
+ }
+
+ private void decodeAndEnqueue(int len) {
+ int accum = 0;
+ accum |= inputBuffer[0] << 18;
+ accum |= inputBuffer[1] << 12;
+ accum |= inputBuffer[2] << 6;
+ accum |= inputBuffer[3];
+
+ // There's a bit of duplicated code here because we want to have straight-through operation
+ // for the most common case of len==4
+ if (len == 4) {
+ outputBuffer[0] = (accum >> 16) & 0xFF;
+ outputBuffer[1] = (accum >> 8) & 0xFF;
+ outputBuffer[2] = (accum) & 0xFF;
+ outCount = 3;
+ return;
+ } else if (len == 3) {
+ outputBuffer[0] = (accum >> 16) & 0xFF;
+ outputBuffer[1] = (accum >> 8) & 0xFF;
+ outCount = 2;
+ return;
+ } else { // len == 2
+ outputBuffer[0] = (accum >> 16) & 0xFF;
+ outCount = 1;
+ return;
+ }
+ }
+
+ private static byte[] TRANSLATION = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 */
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xA0 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB0 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD0 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xE0 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* 0xF0 */
+ };
+
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/ByteQueue.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/ByteQueue.java
new file mode 100644
index 000000000..6d7ccef52
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/ByteQueue.java
@@ -0,0 +1,62 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.decoder;
+
+import java.util.Iterator;
+
+public class ByteQueue {
+
+ private UnboundedFifoByteBuffer buf;
+ private int initialCapacity = -1;
+
+ public ByteQueue() {
+ buf = new UnboundedFifoByteBuffer();
+ }
+
+ public ByteQueue(int initialCapacity) {
+ buf = new UnboundedFifoByteBuffer(initialCapacity);
+ this.initialCapacity = initialCapacity;
+ }
+
+ public void enqueue(byte b) {
+ buf.add(b);
+ }
+
+ public byte dequeue() {
+ return buf.remove();
+ }
+
+ public int count() {
+ return buf.size();
+ }
+
+ public void clear() {
+ if (initialCapacity != -1)
+ buf = new UnboundedFifoByteBuffer(initialCapacity);
+ else
+ buf = new UnboundedFifoByteBuffer();
+ }
+
+ public Iterator iterator() {
+ return buf.iterator();
+ }
+
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/DecoderUtil.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/DecoderUtil.java
new file mode 100644
index 000000000..48fe07dee
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/DecoderUtil.java
@@ -0,0 +1,284 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.decoder;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+import org.apache.james.mime4j.util.CharsetUtil;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Static methods for decoding strings, byte arrays and encoded words.
+ *
+ *
+ * @version $Id: DecoderUtil.java,v 1.3 2005/02/07 15:33:59 ntherning Exp $
+ */
+public class DecoderUtil {
+ private static Log log = LogFactory.getLog(DecoderUtil.class);
+
+ /**
+ * Decodes a string containing quoted-printable encoded data.
+ *
+ * @param s the string to decode.
+ * @return the decoded bytes.
+ */
+ public static byte[] decodeBaseQuotedPrintable(String s) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ try {
+ byte[] bytes = s.getBytes("US-ASCII");
+
+ QuotedPrintableInputStream is = new QuotedPrintableInputStream(
+ new ByteArrayInputStream(bytes));
+
+ int b = 0;
+ while ((b = is.read()) != -1) {
+ baos.write(b);
+ }
+ } catch (IOException e) {
+ /*
+ * This should never happen!
+ */
+ log.error(e);
+ }
+
+ return baos.toByteArray();
+ }
+
+ /**
+ * Decodes a string containing base64 encoded data.
+ *
+ * @param s the string to decode.
+ * @return the decoded bytes.
+ */
+ public static byte[] decodeBase64(String s) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ try {
+ byte[] bytes = s.getBytes("US-ASCII");
+
+ Base64InputStream is = new Base64InputStream(
+ new ByteArrayInputStream(bytes));
+
+ int b = 0;
+ while ((b = is.read()) != -1) {
+ baos.write(b);
+ }
+ } catch (IOException e) {
+ /*
+ * This should never happen!
+ */
+ log.error(e);
+ }
+
+ return baos.toByteArray();
+ }
+
+ /**
+ * Decodes an encoded word encoded with the 'B' encoding (described in
+ * RFC 2047) found in a header field body.
+ *
+ * @param encodedWord the encoded word to decode.
+ * @param charset the Java charset to use.
+ * @return the decoded string.
+ * @throws UnsupportedEncodingException if the given Java charset isn't
+ * supported.
+ */
+ public static String decodeB(String encodedWord, String charset)
+ throws UnsupportedEncodingException {
+
+ return new String(decodeBase64(encodedWord), charset);
+ }
+
+ /**
+ * Decodes an encoded word encoded with the 'Q' encoding (described in
+ * RFC 2047) found in a header field body.
+ *
+ * @param encodedWord the encoded word to decode.
+ * @param charset the Java charset to use.
+ * @return the decoded string.
+ * @throws UnsupportedEncodingException if the given Java charset isn't
+ * supported.
+ */
+ public static String decodeQ(String encodedWord, String charset)
+ throws UnsupportedEncodingException {
+
+ /*
+ * Replace _ with =20
+ */
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < encodedWord.length(); i++) {
+ char c = encodedWord.charAt(i);
+ if (c == '_') {
+ sb.append("=20");
+ } else {
+ sb.append(c);
+ }
+ }
+
+ return new String(decodeBaseQuotedPrintable(sb.toString()), charset);
+ }
+
+ /**
+ * Decodes a string containing encoded words as defined by RFC 2047.
+ * Encoded words in have the form
+ * =?charset?enc?Encoded word?= where enc is either 'Q' or 'q' for
+ * quoted-printable and 'B' or 'b' for Base64.
+ *
+ * ANDROID: COPIED FROM A NEWER VERSION OF MIME4J
+ *
+ * @param body the string to decode.
+ * @return the decoded string.
+ */
+ public static String decodeEncodedWords(String body) {
+
+ // ANDROID: Most strings will not include "=?" so a quick test can prevent unneeded
+ // object creation. This could also be handled via lazy creation of the StringBuilder.
+ if (body.indexOf("=?") == -1) {
+ return body;
+ }
+
+ int previousEnd = 0;
+ boolean previousWasEncoded = false;
+
+ StringBuilder sb = new StringBuilder();
+
+ while (true) {
+ int begin = body.indexOf("=?", previousEnd);
+
+ // ANDROID: The mime4j original version has an error here. It gets confused if
+ // the encoded string begins with an '=' (just after "?Q?"). This patch seeks forward
+ // to find the two '?' in the "header", before looking for the final "?=".
+ if (begin == -1) {
+ break;
+ }
+ int qm1 = body.indexOf('?', begin + 2);
+ if (qm1 == -1) {
+ break;
+ }
+ int qm2 = body.indexOf('?', qm1 + 1);
+ if (qm2 == -1) {
+ break;
+ }
+ int end = body.indexOf("?=", qm2 + 1);
+ if (end == -1) {
+ break;
+ }
+ end += 2;
+
+ String sep = body.substring(previousEnd, begin);
+
+ String decoded = decodeEncodedWord(body, begin, end);
+ if (decoded == null) {
+ sb.append(sep);
+ sb.append(body.substring(begin, end));
+ } else {
+ if (!previousWasEncoded || !CharsetUtil.isWhitespace(sep)) {
+ sb.append(sep);
+ }
+ sb.append(decoded);
+ }
+
+ previousEnd = end;
+ previousWasEncoded = decoded != null;
+ }
+
+ if (previousEnd == 0)
+ return body;
+
+ sb.append(body.substring(previousEnd));
+ return sb.toString();
+ }
+
+ // return null on error. Begin is index of '=?' in body.
+ public static String decodeEncodedWord(String body, int begin, int end) {
+ // Skip the '?=' chars in body and scan forward from there for next '?'
+ int qm1 = body.indexOf('?', begin + 2);
+ if (qm1 == -1 || qm1 == end - 2)
+ return null;
+
+ int qm2 = body.indexOf('?', qm1 + 1);
+ if (qm2 == -1 || qm2 == end - 2)
+ return null;
+
+ String mimeCharset = body.substring(begin + 2, qm1);
+ String encoding = body.substring(qm1 + 1, qm2);
+ String encodedText = body.substring(qm2 + 1, end - 2);
+
+ String charset = CharsetUtil.toJavaCharset(mimeCharset);
+ if (charset == null) {
+ if (log.isWarnEnabled()) {
+ log.warn("MIME charset '" + mimeCharset + "' in encoded word '"
+ + body.substring(begin, end) + "' doesn't have a "
+ + "corresponding Java charset");
+ }
+ return null;
+ } else if (!CharsetUtil.isDecodingSupported(charset)) {
+ if (log.isWarnEnabled()) {
+ log.warn("Current JDK doesn't support decoding of charset '"
+ + charset + "' (MIME charset '" + mimeCharset
+ + "' in encoded word '" + body.substring(begin, end)
+ + "')");
+ }
+ return null;
+ }
+
+ if (encodedText.length() == 0) {
+ if (log.isWarnEnabled()) {
+ log.warn("Missing encoded text in encoded word: '"
+ + body.substring(begin, end) + "'");
+ }
+ return null;
+ }
+
+ try {
+ if (encoding.equalsIgnoreCase("Q")) {
+ return DecoderUtil.decodeQ(encodedText, charset);
+ } else if (encoding.equalsIgnoreCase("B")) {
+ return DecoderUtil.decodeB(encodedText, charset);
+ } else {
+ if (log.isWarnEnabled()) {
+ log.warn("Warning: Unknown encoding in encoded word '"
+ + body.substring(begin, end) + "'");
+ }
+ return null;
+ }
+ } catch (UnsupportedEncodingException e) {
+ // should not happen because of isDecodingSupported check above
+ if (log.isWarnEnabled()) {
+ log.warn("Unsupported encoding in encoded word '"
+ + body.substring(begin, end) + "'", e);
+ }
+ return null;
+ } catch (RuntimeException e) {
+ if (log.isWarnEnabled()) {
+ log.warn("Could not decode encoded word '"
+ + body.substring(begin, end) + "'", e);
+ }
+ return null;
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/QuotedPrintableInputStream.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/QuotedPrintableInputStream.java
new file mode 100644
index 000000000..e43f398f9
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/QuotedPrintableInputStream.java
@@ -0,0 +1,229 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.decoder;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+
+/**
+ * Performs Quoted-Printable decoding on an underlying stream.
+ *
+ *
+ *
+ * @version $Id: QuotedPrintableInputStream.java,v 1.3 2004/11/29 13:15:47 ntherning Exp $
+ */
+public class QuotedPrintableInputStream extends InputStream {
+ private static Log log = LogFactory.getLog(QuotedPrintableInputStream.class);
+
+ private InputStream stream;
+ ByteQueue byteq = new ByteQueue();
+ ByteQueue pushbackq = new ByteQueue();
+ private byte state = 0;
+
+ public QuotedPrintableInputStream(InputStream stream) {
+ this.stream = stream;
+ }
+
+ /**
+ * Closes the underlying stream.
+ *
+ * @throws IOException on I/O errors.
+ */
+ public void close() throws IOException {
+ stream.close();
+ }
+
+ public int read() throws IOException {
+ fillBuffer();
+ if (byteq.count() == 0)
+ return -1;
+ else {
+ byte val = byteq.dequeue();
+ if (val >= 0)
+ return val;
+ else
+ return val & 0xFF;
+ }
+ }
+
+ /**
+ * Pulls bytes out of the underlying stream and places them in the
+ * pushback queue. This is necessary (vs. reading from the
+ * underlying stream directly) to detect and filter out "transport
+ * padding" whitespace, i.e., all whitespace that appears immediately
+ * before a CRLF.
+ *
+ * @throws IOException Underlying stream threw IOException.
+ */
+ private void populatePushbackQueue() throws IOException {
+ //Debug.verify(pushbackq.count() == 0, "PopulatePushbackQueue called when pushback queue was not empty!");
+
+ if (pushbackq.count() != 0)
+ return;
+
+ while (true) {
+ int i = stream.read();
+ switch (i) {
+ case -1:
+ // stream is done
+ pushbackq.clear(); // discard any whitespace preceding EOF
+ return;
+ case ' ':
+ case '\t':
+ pushbackq.enqueue((byte)i);
+ break;
+ case '\r':
+ case '\n':
+ pushbackq.clear(); // discard any whitespace preceding EOL
+ pushbackq.enqueue((byte)i);
+ return;
+ default:
+ pushbackq.enqueue((byte)i);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Causes the pushback queue to get populated if it is empty, then
+ * consumes and decodes bytes out of it until one or more bytes are
+ * in the byte queue. This decoding step performs the actual QP
+ * decoding.
+ *
+ * @throws IOException Underlying stream threw IOException.
+ */
+ private void fillBuffer() throws IOException {
+ byte msdChar = 0; // first digit of escaped num
+ while (byteq.count() == 0) {
+ if (pushbackq.count() == 0) {
+ populatePushbackQueue();
+ if (pushbackq.count() == 0)
+ return;
+ }
+
+ byte b = (byte)pushbackq.dequeue();
+
+ switch (state) {
+ case 0: // start state, no bytes pending
+ if (b != '=') {
+ byteq.enqueue(b);
+ break; // state remains 0
+ } else {
+ state = 1;
+ break;
+ }
+ case 1: // encountered "=" so far
+ if (b == '\r') {
+ state = 2;
+ break;
+ } else if ((b >= '0' && b <= '9') || (b >= 'A' && b <= 'F') || (b >= 'a' && b <= 'f')) {
+ state = 3;
+ msdChar = b; // save until next digit encountered
+ break;
+ } else if (b == '=') {
+ /*
+ * Special case when == is encountered.
+ * Emit one = and stay in this state.
+ */
+ if (log.isWarnEnabled()) {
+ log.warn("Malformed MIME; got ==");
+ }
+ byteq.enqueue((byte)'=');
+ break;
+ } else {
+ if (log.isWarnEnabled()) {
+ log.warn("Malformed MIME; expected \\r or "
+ + "[0-9A-Z], got " + b);
+ }
+ state = 0;
+ byteq.enqueue((byte)'=');
+ byteq.enqueue(b);
+ break;
+ }
+ case 2: // encountered "=\r" so far
+ if (b == '\n') {
+ state = 0;
+ break;
+ } else {
+ if (log.isWarnEnabled()) {
+ log.warn("Malformed MIME; expected "
+ + (int)'\n' + ", got " + b);
+ }
+ state = 0;
+ byteq.enqueue((byte)'=');
+ byteq.enqueue((byte)'\r');
+ byteq.enqueue(b);
+ break;
+ }
+ case 3: // encountered =<digit> so far; expecting another <digit> to complete the octet
+ if ((b >= '0' && b <= '9') || (b >= 'A' && b <= 'F') || (b >= 'a' && b <= 'f')) {
+ byte msd = asciiCharToNumericValue(msdChar);
+ byte low = asciiCharToNumericValue(b);
+ state = 0;
+ byteq.enqueue((byte)((msd << 4) | low));
+ break;
+ } else {
+ if (log.isWarnEnabled()) {
+ log.warn("Malformed MIME; expected "
+ + "[0-9A-Z], got " + b);
+ }
+ state = 0;
+ byteq.enqueue((byte)'=');
+ byteq.enqueue(msdChar);
+ byteq.enqueue(b);
+ break;
+ }
+ default: // should never happen
+ log.error("Illegal state: " + state);
+ state = 0;
+ byteq.enqueue(b);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Converts '0' => 0, 'A' => 10, etc.
+ * @param c ASCII character value.
+ * @return Numeric value of hexadecimal character.
+ */
+ private byte asciiCharToNumericValue(byte c) {
+ if (c >= '0' && c <= '9') {
+ return (byte)(c - '0');
+ } else if (c >= 'A' && c <= 'Z') {
+ return (byte)(0xA + (c - 'A'));
+ } else if (c >= 'a' && c <= 'z') {
+ return (byte)(0xA + (c - 'a'));
+ } else {
+ /*
+ * This should never happen since all calls to this method
+ * are preceded by a check that c is in [0-9A-Za-z]
+ */
+ throw new IllegalArgumentException((char) c
+ + " is not a hexadecimal digit");
+ }
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/UnboundedFifoByteBuffer.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/UnboundedFifoByteBuffer.java
new file mode 100644
index 000000000..f01194fd1
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/decoder/UnboundedFifoByteBuffer.java
@@ -0,0 +1,272 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.decoder;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * UnboundedFifoByteBuffer is a very efficient buffer implementation.
+ * According to performance testing, it exhibits a constant access time, but it
+ * also outperforms ArrayList when used for the same purpose.
+ * <p>
+ * The removal order of an <code>UnboundedFifoByteBuffer</code> is based on the insertion
+ * order; elements are removed in the same order in which they were added.
+ * The iteration order is the same as the removal order.
+ * <p>
+ * The {@link #remove()} and {@link #get()} operations perform in constant time.
+ * The {@link #add(Object)} operation performs in amortized constant time. All
+ * other operations perform in linear time or worse.
+ * <p>
+ * Note that this implementation is not synchronized. The following can be
+ * used to provide synchronized access to your <code>UnboundedFifoByteBuffer</code>:
+ * <pre>
+ * Buffer fifo = BufferUtils.synchronizedBuffer(new UnboundedFifoByteBuffer());
+ * </pre>
+ * <p>
+ * This buffer prevents null objects from being added.
+ *
+ * @since Commons Collections 3.0 (previously in main package v2.1)
+ * @version $Revision: 1.1 $ $Date: 2004/08/24 06:52:02 $
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+class UnboundedFifoByteBuffer {
+
+ protected byte[] buffer;
+ protected int head;
+ protected int tail;
+
+ /**
+ * Constructs an UnboundedFifoByteBuffer with the default number of elements.
+ * It is exactly the same as performing the following:
+ *
+ * <pre>
+ * new UnboundedFifoByteBuffer(32);
+ * </pre>
+ */
+ public UnboundedFifoByteBuffer() {
+ this(32);
+ }
+
+ /**
+ * Constructs an UnboundedFifoByteBuffer with the specified number of elements.
+ * The integer must be a positive integer.
+ *
+ * @param initialSize the initial size of the buffer
+ * @throws IllegalArgumentException if the size is less than 1
+ */
+ public UnboundedFifoByteBuffer(int initialSize) {
+ if (initialSize <= 0) {
+ throw new IllegalArgumentException("The size must be greater than 0");
+ }
+ buffer = new byte[initialSize + 1];
+ head = 0;
+ tail = 0;
+ }
+
+ /**
+ * Returns the number of elements stored in the buffer.
+ *
+ * @return this buffer's size
+ */
+ public int size() {
+ int size = 0;
+
+ if (tail < head) {
+ size = buffer.length - head + tail;
+ } else {
+ size = tail - head;
+ }
+
+ return size;
+ }
+
+ /**
+ * Returns true if this buffer is empty; false otherwise.
+ *
+ * @return true if this buffer is empty
+ */
+ public boolean isEmpty() {
+ return (size() == 0);
+ }
+
+ /**
+ * Adds the given element to this buffer.
+ *
+ * @param b the byte to add
+ * @return true, always
+ */
+ public boolean add(final byte b) {
+
+ if (size() + 1 >= buffer.length) {
+ byte[] tmp = new byte[((buffer.length - 1) * 2) + 1];
+
+ int j = 0;
+ for (int i = head; i != tail;) {
+ tmp[j] = buffer[i];
+ buffer[i] = 0;
+
+ j++;
+ i++;
+ if (i == buffer.length) {
+ i = 0;
+ }
+ }
+
+ buffer = tmp;
+ head = 0;
+ tail = j;
+ }
+
+ buffer[tail] = b;
+ tail++;
+ if (tail >= buffer.length) {
+ tail = 0;
+ }
+ return true;
+ }
+
+ /**
+ * Returns the next object in the buffer.
+ *
+ * @return the next object in the buffer
+ * @throws BufferUnderflowException if this buffer is empty
+ */
+ public byte get() {
+ if (isEmpty()) {
+ throw new IllegalStateException("The buffer is already empty");
+ }
+
+ return buffer[head];
+ }
+
+ /**
+ * Removes the next object from the buffer
+ *
+ * @return the removed object
+ * @throws BufferUnderflowException if this buffer is empty
+ */
+ public byte remove() {
+ if (isEmpty()) {
+ throw new IllegalStateException("The buffer is already empty");
+ }
+
+ byte element = buffer[head];
+
+ head++;
+ if (head >= buffer.length) {
+ head = 0;
+ }
+
+ return element;
+ }
+
+ /**
+ * Increments the internal index.
+ *
+ * @param index the index to increment
+ * @return the updated index
+ */
+ private int increment(int index) {
+ index++;
+ if (index >= buffer.length) {
+ index = 0;
+ }
+ return index;
+ }
+
+ /**
+ * Decrements the internal index.
+ *
+ * @param index the index to decrement
+ * @return the updated index
+ */
+ private int decrement(int index) {
+ index--;
+ if (index < 0) {
+ index = buffer.length - 1;
+ }
+ return index;
+ }
+
+ /**
+ * Returns an iterator over this buffer's elements.
+ *
+ * @return an iterator over this buffer's elements
+ */
+ public Iterator iterator() {
+ return new Iterator() {
+
+ private int index = head;
+ private int lastReturnedIndex = -1;
+
+ public boolean hasNext() {
+ return index != tail;
+
+ }
+
+ public Object next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ lastReturnedIndex = index;
+ index = increment(index);
+ return new Byte(buffer[lastReturnedIndex]);
+ }
+
+ public void remove() {
+ if (lastReturnedIndex == -1) {
+ throw new IllegalStateException();
+ }
+
+ // First element can be removed quickly
+ if (lastReturnedIndex == head) {
+ UnboundedFifoByteBuffer.this.remove();
+ lastReturnedIndex = -1;
+ return;
+ }
+
+ // Other elements require us to shift the subsequent elements
+ int i = lastReturnedIndex + 1;
+ while (i != tail) {
+ if (i >= buffer.length) {
+ buffer[i - 1] = buffer[0];
+ i = 0;
+ } else {
+ buffer[i - 1] = buffer[i];
+ i++;
+ }
+ }
+
+ lastReturnedIndex = -1;
+ tail = decrement(tail);
+ buffer[tail] = 0;
+ index = decrement(index);
+ }
+
+ };
+ }
+
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/AddressListField.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/AddressListField.java
new file mode 100644
index 000000000..df9f39835
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/AddressListField.java
@@ -0,0 +1,65 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+import org.apache.james.mime4j.field.address.AddressList;
+import org.apache.james.mime4j.field.address.parser.ParseException;
+
+public class AddressListField extends Field {
+ private AddressList addressList;
+ private ParseException parseException;
+
+ protected AddressListField(String name, String body, String raw, AddressList addressList, ParseException parseException) {
+ super(name, body, raw);
+ this.addressList = addressList;
+ this.parseException = parseException;
+ }
+
+ public AddressList getAddressList() {
+ return addressList;
+ }
+
+ public ParseException getParseException() {
+ return parseException;
+ }
+
+ public static class Parser implements FieldParser {
+ private static Log log = LogFactory.getLog(Parser.class);
+
+ public Field parse(final String name, final String body, final String raw) {
+ AddressList addressList = null;
+ ParseException parseException = null;
+ try {
+ addressList = AddressList.parse(body);
+ }
+ catch (ParseException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Parsing value '" + body + "': "+ e.getMessage());
+ }
+ parseException = e;
+ }
+ return new AddressListField(name, body, raw, addressList, parseException);
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/ContentTransferEncodingField.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/ContentTransferEncodingField.java
new file mode 100644
index 000000000..73d8d2339
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/ContentTransferEncodingField.java
@@ -0,0 +1,88 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field;
+
+
+
+/**
+ * Represents a <code>Content-Transfer-Encoding</code> field.
+ *
+ *
+ * @version $Id: ContentTransferEncodingField.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $
+ */
+public class ContentTransferEncodingField extends Field {
+ /**
+ * The <code>7bit</code> encoding.
+ */
+ public static final String ENC_7BIT = "7bit";
+ /**
+ * The <code>8bit</code> encoding.
+ */
+ public static final String ENC_8BIT = "8bit";
+ /**
+ * The <code>binary</code> encoding.
+ */
+ public static final String ENC_BINARY = "binary";
+ /**
+ * The <code>quoted-printable</code> encoding.
+ */
+ public static final String ENC_QUOTED_PRINTABLE = "quoted-printable";
+ /**
+ * The <code>base64</code> encoding.
+ */
+ public static final String ENC_BASE64 = "base64";
+
+ private String encoding;
+
+ protected ContentTransferEncodingField(String name, String body, String raw, String encoding) {
+ super(name, body, raw);
+ this.encoding = encoding;
+ }
+
+ /**
+ * Gets the encoding defined in this field.
+ *
+ * @return the encoding or an empty string if not set.
+ */
+ public String getEncoding() {
+ return encoding;
+ }
+
+ /**
+ * Gets the encoding of the given field if. Returns the default
+ * <code>7bit</code> if not set or if
+ * <code>f</code> is <code>null</code>.
+ *
+ * @return the encoding.
+ */
+ public static String getEncoding(ContentTransferEncodingField f) {
+ if (f != null && f.getEncoding().length() != 0) {
+ return f.getEncoding();
+ }
+ return ENC_7BIT;
+ }
+
+ public static class Parser implements FieldParser {
+ public Field parse(final String name, final String body, final String raw) {
+ final String encoding = body.trim().toLowerCase();
+ return new ContentTransferEncodingField(name, body, raw, encoding);
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/ContentTypeField.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/ContentTypeField.java
new file mode 100644
index 000000000..ad9f7f9ac
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/ContentTypeField.java
@@ -0,0 +1,259 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+import org.apache.james.mime4j.field.contenttype.parser.ContentTypeParser;
+import org.apache.james.mime4j.field.contenttype.parser.ParseException;
+import org.apache.james.mime4j.field.contenttype.parser.TokenMgrError;
+
+/**
+ * Represents a <code>Content-Type</code> field.
+ *
+ * <p>TODO: Remove dependency on Java 1.4 regexps</p>
+ *
+ *
+ * @version $Id: ContentTypeField.java,v 1.6 2005/01/27 14:16:31 ntherning Exp $
+ */
+public class ContentTypeField extends Field {
+
+ /**
+ * The prefix of all <code>multipart</code> MIME types.
+ */
+ public static final String TYPE_MULTIPART_PREFIX = "multipart/";
+ /**
+ * The <code>multipart/digest</code> MIME type.
+ */
+ public static final String TYPE_MULTIPART_DIGEST = "multipart/digest";
+ /**
+ * The <code>text/plain</code> MIME type.
+ */
+ public static final String TYPE_TEXT_PLAIN = "text/plain";
+ /**
+ * The <code>message/rfc822</code> MIME type.
+ */
+ public static final String TYPE_MESSAGE_RFC822 = "message/rfc822";
+ /**
+ * The name of the <code>boundary</code> parameter.
+ */
+ public static final String PARAM_BOUNDARY = "boundary";
+ /**
+ * The name of the <code>charset</code> parameter.
+ */
+ public static final String PARAM_CHARSET = "charset";
+
+ private String mimeType = "";
+ private Map<String, String> parameters = null;
+ private ParseException parseException;
+
+ protected ContentTypeField(String name, String body, String raw, String mimeType, Map<String, String> parameters, ParseException parseException) {
+ super(name, body, raw);
+ this.mimeType = mimeType;
+ this.parameters = parameters;
+ this.parseException = parseException;
+ }
+
+ /**
+ * Gets the exception that was raised during parsing of
+ * the field value, if any; otherwise, null.
+ */
+ public ParseException getParseException() {
+ return parseException;
+ }
+
+ /**
+ * Gets the MIME type defined in this Content-Type field.
+ *
+ * @return the MIME type or an empty string if not set.
+ */
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ /**
+ * Gets the MIME type defined in the child's
+ * Content-Type field or derives a MIME type from the parent
+ * if child is <code>null</code> or hasn't got a MIME type value set.
+ * If child's MIME type is multipart but no boundary
+ * has been set the MIME type of child will be derived from
+ * the parent.
+ *
+ * @param child the child.
+ * @param parent the parent.
+ * @return the MIME type.
+ */
+ public static String getMimeType(ContentTypeField child,
+ ContentTypeField parent) {
+
+ if (child == null || child.getMimeType().length() == 0
+ || child.isMultipart() && child.getBoundary() == null) {
+
+ if (parent != null && parent.isMimeType(TYPE_MULTIPART_DIGEST)) {
+ return TYPE_MESSAGE_RFC822;
+ } else {
+ return TYPE_TEXT_PLAIN;
+ }
+ }
+
+ return child.getMimeType();
+ }
+
+ /**
+ * Gets the value of a parameter. Parameter names are case-insensitive.
+ *
+ * @param name the name of the parameter to get.
+ * @return the parameter value or <code>null</code> if not set.
+ */
+ public String getParameter(String name) {
+ return parameters != null
+ ? parameters.get(name.toLowerCase())
+ : null;
+ }
+
+ /**
+ * Gets all parameters.
+ *
+ * @return the parameters.
+ */
+ public Map<String, String> getParameters() {
+ if (parameters != null) {
+ return Collections.unmodifiableMap(parameters);
+ }
+ return Collections.emptyMap();
+ }
+
+ /**
+ * Gets the value of the <code>boundary</code> parameter if set.
+ *
+ * @return the <code>boundary</code> parameter value or <code>null</code>
+ * if not set.
+ */
+ public String getBoundary() {
+ return getParameter(PARAM_BOUNDARY);
+ }
+
+ /**
+ * Gets the value of the <code>charset</code> parameter if set.
+ *
+ * @return the <code>charset</code> parameter value or <code>null</code>
+ * if not set.
+ */
+ public String getCharset() {
+ return getParameter(PARAM_CHARSET);
+ }
+
+ /**
+ * Gets the value of the <code>charset</code> parameter if set for the
+ * given field. Returns the default <code>us-ascii</code> if not set or if
+ * <code>f</code> is <code>null</code>.
+ *
+ * @return the <code>charset</code> parameter value.
+ */
+ public static String getCharset(ContentTypeField f) {
+ if (f != null) {
+ if (f.getCharset() != null && f.getCharset().length() > 0) {
+ return f.getCharset();
+ }
+ }
+ return "us-ascii";
+ }
+
+ /**
+ * Determines if the MIME type of this field matches the given one.
+ *
+ * @param mimeType the MIME type to match against.
+ * @return <code>true</code> if the MIME type of this field matches,
+ * <code>false</code> otherwise.
+ */
+ public boolean isMimeType(String mimeType) {
+ return this.mimeType.equalsIgnoreCase(mimeType);
+ }
+
+ /**
+ * Determines if the MIME type of this field is <code>multipart/*</code>.
+ *
+ * @return <code>true</code> if this field is has a <code>multipart/*</code>
+ * MIME type, <code>false</code> otherwise.
+ */
+ public boolean isMultipart() {
+ return mimeType.startsWith(TYPE_MULTIPART_PREFIX);
+ }
+
+ public static class Parser implements FieldParser {
+ private static Log log = LogFactory.getLog(Parser.class);
+
+ public Field parse(final String name, final String body, final String raw) {
+ ParseException parseException = null;
+ String mimeType = "";
+ Map<String, String> parameters = null;
+
+ ContentTypeParser parser = new ContentTypeParser(new StringReader(body));
+ try {
+ parser.parseAll();
+ }
+ catch (ParseException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Parsing value '" + body + "': "+ e.getMessage());
+ }
+ parseException = e;
+ }
+ catch (TokenMgrError e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Parsing value '" + body + "': "+ e.getMessage());
+ }
+ parseException = new ParseException(e.getMessage());
+ }
+
+ try {
+ final String type = parser.getType();
+ final String subType = parser.getSubType();
+
+ if (type != null && subType != null) {
+ mimeType = (type + "/" + parser.getSubType()).toLowerCase();
+
+ ArrayList<String> paramNames = parser.getParamNames();
+ ArrayList<String> paramValues = parser.getParamValues();
+
+ if (paramNames != null && paramValues != null) {
+ for (int i = 0; i < paramNames.size() && i < paramValues.size(); i++) {
+ if (parameters == null)
+ parameters = new HashMap<String, String>((int)(paramNames.size() * 1.3 + 1));
+ String paramName = paramNames.get(i).toLowerCase();
+ String paramValue = paramValues.get(i);
+ parameters.put(paramName, paramValue);
+ }
+ }
+ }
+ }
+ catch (NullPointerException npe) {
+ }
+ return new ContentTypeField(name, body, raw, mimeType, parameters, parseException);
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/DateTimeField.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/DateTimeField.java
new file mode 100644
index 000000000..1e6c8e250
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/DateTimeField.java
@@ -0,0 +1,73 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field;
+
+//BEGIN android-changed: Stubbing out logging
+
+import com.android.voicemailomtp.mail.utils.LogUtils;
+
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END
+import org.apache.james.mime4j.field.datetime.DateTime;
+import org.apache.james.mime4j.field.datetime.parser.ParseException;
+
+import java.util.Date;
+
+public class DateTimeField extends Field {
+ private Date date;
+ private ParseException parseException;
+
+ protected DateTimeField(String name, String body, String raw, Date date, ParseException parseException) {
+ super(name, body, raw);
+ this.date = date;
+ this.parseException = parseException;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public ParseException getParseException() {
+ return parseException;
+ }
+
+ public static class Parser implements FieldParser {
+ private static Log log = LogFactory.getLog(Parser.class);
+
+ public Field parse(final String name, String body, final String raw) {
+ Date date = null;
+ ParseException parseException = null;
+ //BEGIN android-changed
+ body = LogUtils.cleanUpMimeDate(body);
+ //END android-changed
+ try {
+ date = DateTime.parse(body).getDate();
+ }
+ catch (ParseException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Parsing value '" + body + "': "+ e.getMessage());
+ }
+ parseException = e;
+ }
+ return new DateTimeField(name, body, raw, date, parseException);
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/DefaultFieldParser.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/DefaultFieldParser.java
new file mode 100644
index 000000000..3695afe3e
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/DefaultFieldParser.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2006 the mime4j 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 org.apache.james.mime4j.field;
+
+public class DefaultFieldParser extends DelegatingFieldParser {
+
+ public DefaultFieldParser() {
+ setFieldParser(Field.CONTENT_TRANSFER_ENCODING, new ContentTransferEncodingField.Parser());
+ setFieldParser(Field.CONTENT_TYPE, new ContentTypeField.Parser());
+
+ final DateTimeField.Parser dateTimeParser = new DateTimeField.Parser();
+ setFieldParser(Field.DATE, dateTimeParser);
+ setFieldParser(Field.RESENT_DATE, dateTimeParser);
+
+ final MailboxListField.Parser mailboxListParser = new MailboxListField.Parser();
+ setFieldParser(Field.FROM, mailboxListParser);
+ setFieldParser(Field.RESENT_FROM, mailboxListParser);
+
+ final MailboxField.Parser mailboxParser = new MailboxField.Parser();
+ setFieldParser(Field.SENDER, mailboxParser);
+ setFieldParser(Field.RESENT_SENDER, mailboxParser);
+
+ final AddressListField.Parser addressListParser = new AddressListField.Parser();
+ setFieldParser(Field.TO, addressListParser);
+ setFieldParser(Field.RESENT_TO, addressListParser);
+ setFieldParser(Field.CC, addressListParser);
+ setFieldParser(Field.RESENT_CC, addressListParser);
+ setFieldParser(Field.BCC, addressListParser);
+ setFieldParser(Field.RESENT_BCC, addressListParser);
+ setFieldParser(Field.REPLY_TO, addressListParser);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/DelegatingFieldParser.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/DelegatingFieldParser.java
new file mode 100644
index 000000000..32b69ec13
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/DelegatingFieldParser.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2006 the mime4j 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 org.apache.james.mime4j.field;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class DelegatingFieldParser implements FieldParser {
+
+ private Map<String, FieldParser> parsers = new HashMap<String, FieldParser>();
+ private FieldParser defaultParser = new UnstructuredField.Parser();
+
+ /**
+ * Sets the parser used for the field named <code>name</code>.
+ * @param name the name of the field
+ * @param parser the parser for fields named <code>name</code>
+ */
+ public void setFieldParser(final String name, final FieldParser parser) {
+ parsers.put(name.toLowerCase(), parser);
+ }
+
+ public FieldParser getParser(final String name) {
+ final FieldParser field = parsers.get(name.toLowerCase());
+ if(field==null) {
+ return defaultParser;
+ }
+ return field;
+ }
+
+ public Field parse(final String name, final String body, final String raw) {
+ final FieldParser parser = getParser(name);
+ return parser.parse(name, body, raw);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/Field.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/Field.java
new file mode 100644
index 000000000..4dea5c5cf
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/Field.java
@@ -0,0 +1,192 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * The base class of all field classes.
+ *
+ *
+ * @version $Id: Field.java,v 1.6 2004/10/25 07:26:46 ntherning Exp $
+ */
+public abstract class Field {
+ public static final String SENDER = "Sender";
+ public static final String FROM = "From";
+ public static final String TO = "To";
+ public static final String CC = "Cc";
+ public static final String BCC = "Bcc";
+ public static final String REPLY_TO = "Reply-To";
+ public static final String RESENT_SENDER = "Resent-Sender";
+ public static final String RESENT_FROM = "Resent-From";
+ public static final String RESENT_TO = "Resent-To";
+ public static final String RESENT_CC = "Resent-Cc";
+ public static final String RESENT_BCC = "Resent-Bcc";
+
+ public static final String DATE = "Date";
+ public static final String RESENT_DATE = "Resent-Date";
+
+ public static final String SUBJECT = "Subject";
+ public static final String CONTENT_TYPE = "Content-Type";
+ public static final String CONTENT_TRANSFER_ENCODING =
+ "Content-Transfer-Encoding";
+
+ private static final String FIELD_NAME_PATTERN =
+ "^([\\x21-\\x39\\x3b-\\x7e]+)[ \t]*:";
+ private static final Pattern fieldNamePattern =
+ Pattern.compile(FIELD_NAME_PATTERN);
+
+ private static final DefaultFieldParser parser = new DefaultFieldParser();
+
+ private final String name;
+ private final String body;
+ private final String raw;
+
+ protected Field(final String name, final String body, final String raw) {
+ this.name = name;
+ this.body = body;
+ this.raw = raw;
+ }
+
+ /**
+ * Parses the given string and returns an instance of the
+ * <code>Field</code> class. The type of the class returned depends on
+ * the field name:
+ * <table>
+ * <tr>
+ * <td><em>Field name</em></td><td><em>Class returned</em></td>
+ * <td>Content-Type</td><td>org.apache.james.mime4j.field.ContentTypeField</td>
+ * <td>other</td><td>org.apache.james.mime4j.field.UnstructuredField</td>
+ * </tr>
+ * </table>
+ *
+ * @param s the string to parse.
+ * @return a <code>Field</code> instance.
+ * @throws IllegalArgumentException on parse errors.
+ */
+ public static Field parse(final String raw) {
+
+ /*
+ * Unfold the field.
+ */
+ final String unfolded = raw.replaceAll("\r|\n", "");
+
+ /*
+ * Split into name and value.
+ */
+ final Matcher fieldMatcher = fieldNamePattern.matcher(unfolded);
+ if (!fieldMatcher.find()) {
+ throw new IllegalArgumentException("Invalid field in string");
+ }
+ final String name = fieldMatcher.group(1);
+
+ String body = unfolded.substring(fieldMatcher.end());
+ if (body.length() > 0 && body.charAt(0) == ' ') {
+ body = body.substring(1);
+ }
+
+ return parser.parse(name, body, raw);
+ }
+
+ /**
+ * Gets the default parser used to parse fields.
+ * @return the default field parser
+ */
+ public static DefaultFieldParser getParser() {
+ return parser;
+ }
+
+ /**
+ * Gets the name of the field (<code>Subject</code>,
+ * <code>From</code>, etc).
+ *
+ * @return the field name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Gets the original raw field string.
+ *
+ * @return the original raw field string.
+ */
+ public String getRaw() {
+ return raw;
+ }
+
+ /**
+ * Gets the unfolded, unparsed and possibly encoded (see RFC 2047) field
+ * body string.
+ *
+ * @return the unfolded unparsed field body string.
+ */
+ public String getBody() {
+ return body;
+ }
+
+ /**
+ * Determines if this is a <code>Content-Type</code> field.
+ *
+ * @return <code>true</code> if this is a <code>Content-Type</code> field,
+ * <code>false</code> otherwise.
+ */
+ public boolean isContentType() {
+ return CONTENT_TYPE.equalsIgnoreCase(name);
+ }
+
+ /**
+ * Determines if this is a <code>Subject</code> field.
+ *
+ * @return <code>true</code> if this is a <code>Subject</code> field,
+ * <code>false</code> otherwise.
+ */
+ public boolean isSubject() {
+ return SUBJECT.equalsIgnoreCase(name);
+ }
+
+ /**
+ * Determines if this is a <code>From</code> field.
+ *
+ * @return <code>true</code> if this is a <code>From</code> field,
+ * <code>false</code> otherwise.
+ */
+ public boolean isFrom() {
+ return FROM.equalsIgnoreCase(name);
+ }
+
+ /**
+ * Determines if this is a <code>To</code> field.
+ *
+ * @return <code>true</code> if this is a <code>To</code> field,
+ * <code>false</code> otherwise.
+ */
+ public boolean isTo() {
+ return TO.equalsIgnoreCase(name);
+ }
+
+ /**
+ * @see #getRaw()
+ */
+ public String toString() {
+ return raw;
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/FieldParser.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/FieldParser.java
new file mode 100644
index 000000000..78aaf1334
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/FieldParser.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2006 the mime4j 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 org.apache.james.mime4j.field;
+
+public interface FieldParser {
+
+ Field parse(final String name, final String body, final String raw);
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/MailboxField.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/MailboxField.java
new file mode 100644
index 000000000..f15980055
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/MailboxField.java
@@ -0,0 +1,70 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+import org.apache.james.mime4j.field.address.AddressList;
+import org.apache.james.mime4j.field.address.Mailbox;
+import org.apache.james.mime4j.field.address.MailboxList;
+import org.apache.james.mime4j.field.address.parser.ParseException;
+
+public class MailboxField extends Field {
+ private final Mailbox mailbox;
+ private final ParseException parseException;
+
+ protected MailboxField(final String name, final String body, final String raw, final Mailbox mailbox, final ParseException parseException) {
+ super(name, body, raw);
+ this.mailbox = mailbox;
+ this.parseException = parseException;
+ }
+
+ public Mailbox getMailbox() {
+ return mailbox;
+ }
+
+ public ParseException getParseException() {
+ return parseException;
+ }
+
+ public static class Parser implements FieldParser {
+ private static Log log = LogFactory.getLog(Parser.class);
+
+ public Field parse(final String name, final String body, final String raw) {
+ Mailbox mailbox = null;
+ ParseException parseException = null;
+ try {
+ MailboxList mailboxList = AddressList.parse(body).flatten();
+ if (mailboxList.size() > 0) {
+ mailbox = mailboxList.get(0);
+ }
+ }
+ catch (ParseException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Parsing value '" + body + "': "+ e.getMessage());
+ }
+ parseException = e;
+ }
+ return new MailboxField(name, body, raw, mailbox, parseException);
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/MailboxListField.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/MailboxListField.java
new file mode 100644
index 000000000..23378d4fa
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/MailboxListField.java
@@ -0,0 +1,67 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+import org.apache.james.mime4j.field.address.AddressList;
+import org.apache.james.mime4j.field.address.MailboxList;
+import org.apache.james.mime4j.field.address.parser.ParseException;
+
+public class MailboxListField extends Field {
+
+ private MailboxList mailboxList;
+ private ParseException parseException;
+
+ protected MailboxListField(final String name, final String body, final String raw, final MailboxList mailboxList, final ParseException parseException) {
+ super(name, body, raw);
+ this.mailboxList = mailboxList;
+ this.parseException = parseException;
+ }
+
+ public MailboxList getMailboxList() {
+ return mailboxList;
+ }
+
+ public ParseException getParseException() {
+ return parseException;
+ }
+
+ public static class Parser implements FieldParser {
+ private static Log log = LogFactory.getLog(Parser.class);
+
+ public Field parse(final String name, final String body, final String raw) {
+ MailboxList mailboxList = null;
+ ParseException parseException = null;
+ try {
+ mailboxList = AddressList.parse(body).flatten();
+ }
+ catch (ParseException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Parsing value '" + body + "': "+ e.getMessage());
+ }
+ parseException = e;
+ }
+ return new MailboxListField(name, body, raw, mailboxList, parseException);
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/UnstructuredField.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/UnstructuredField.java
new file mode 100644
index 000000000..6084e4435
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/UnstructuredField.java
@@ -0,0 +1,49 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field;
+
+import org.apache.james.mime4j.decoder.DecoderUtil;
+
+
+/**
+ * Simple unstructured field such as <code>Subject</code>.
+ *
+ *
+ * @version $Id: UnstructuredField.java,v 1.3 2004/10/25 07:26:46 ntherning Exp $
+ */
+public class UnstructuredField extends Field {
+ private String value;
+
+ protected UnstructuredField(String name, String body, String raw, String value) {
+ super(name, body, raw);
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public static class Parser implements FieldParser {
+ public Field parse(final String name, final String body, final String raw) {
+ final String value = DecoderUtil.decodeEncodedWords(body);
+ return new UnstructuredField(name, body, raw, value);
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Address.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Address.java
new file mode 100644
index 000000000..3e24e91aa
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Address.java
@@ -0,0 +1,52 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field.address;
+
+import java.util.ArrayList;
+
+/**
+ * The abstract base for classes that represent RFC2822 addresses.
+ * This includes groups and mailboxes.
+ *
+ * Currently, no public methods are introduced on this class.
+ *
+ *
+ */
+public abstract class Address {
+
+ /**
+ * Adds any mailboxes represented by this address
+ * into the given ArrayList. Note that this method
+ * has default (package) access, so a doAddMailboxesTo
+ * method is needed to allow the behavior to be
+ * overridden by subclasses.
+ */
+ final void addMailboxesTo(ArrayList<Address> results) {
+ doAddMailboxesTo(results);
+ }
+
+ /**
+ * Adds any mailboxes represented by this address
+ * into the given ArrayList. Must be overridden by
+ * concrete subclasses.
+ */
+ protected abstract void doAddMailboxesTo(ArrayList<Address> results);
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/AddressList.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/AddressList.java
new file mode 100644
index 000000000..1829e79aa
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/AddressList.java
@@ -0,0 +1,138 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field.address;
+
+import org.apache.james.mime4j.field.address.parser.AddressListParser;
+import org.apache.james.mime4j.field.address.parser.ParseException;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+
+/**
+ * An immutable, random-access list of Address objects.
+ *
+ *
+ */
+public class AddressList {
+
+ private ArrayList<Address> addresses;
+
+ /**
+ * @param addresses An ArrayList that contains only Address objects.
+ * @param dontCopy true iff it is not possible for the addresses ArrayList to be modified by someone else.
+ */
+ public AddressList(ArrayList<Address> addresses, boolean dontCopy) {
+ if (addresses != null)
+ this.addresses = (dontCopy ? addresses : new ArrayList<Address>(addresses));
+ else
+ this.addresses = new ArrayList<Address>(0);
+ }
+
+ /**
+ * The number of elements in this list.
+ */
+ public int size() {
+ return addresses.size();
+ }
+
+ /**
+ * Gets an address.
+ */
+ public Address get(int index) {
+ if (0 > index || size() <= index)
+ throw new IndexOutOfBoundsException();
+ return addresses.get(index);
+ }
+
+ /**
+ * Returns a flat list of all mailboxes represented
+ * in this address list. Use this if you don't care
+ * about grouping.
+ */
+ public MailboxList flatten() {
+ // in the common case, all addresses are mailboxes
+ boolean groupDetected = false;
+ for (int i = 0; i < size(); i++) {
+ if (!(get(i) instanceof Mailbox)) {
+ groupDetected = true;
+ break;
+ }
+ }
+
+ if (!groupDetected)
+ return new MailboxList(addresses, true);
+
+ ArrayList<Address> results = new ArrayList<Address>();
+ for (int i = 0; i < size(); i++) {
+ Address addr = get(i);
+ addr.addMailboxesTo(results);
+ }
+
+ // copy-on-construct this time, because subclasses
+ // could have held onto a reference to the results
+ return new MailboxList(results, false);
+ }
+
+ /**
+ * Dumps a representation of this address list to
+ * stdout, for debugging purposes.
+ */
+ public void print() {
+ for (int i = 0; i < size(); i++) {
+ Address addr = get(i);
+ System.out.println(addr.toString());
+ }
+ }
+
+ /**
+ * Parse the address list string, such as the value
+ * of a From, To, Cc, Bcc, Sender, or Reply-To
+ * header.
+ *
+ * The string MUST be unfolded already.
+ */
+ public static AddressList parse(String rawAddressList) throws ParseException {
+ AddressListParser parser = new AddressListParser(new StringReader(rawAddressList));
+ return Builder.getInstance().buildAddressList(parser.parse());
+ }
+
+ /**
+ * Test console.
+ */
+ public static void main(String[] args) throws Exception {
+ java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
+ while (true) {
+ try {
+ System.out.print("> ");
+ String line = reader.readLine();
+ if (line.length() == 0 || line.toLowerCase().equals("exit") || line.toLowerCase().equals("quit")) {
+ System.out.println("Goodbye.");
+ return;
+ }
+ AddressList list = parse(line);
+ list.print();
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ Thread.sleep(300);
+ }
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Builder.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Builder.java
new file mode 100644
index 000000000..3bcd15b6f
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Builder.java
@@ -0,0 +1,243 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field.address;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.apache.james.mime4j.decoder.DecoderUtil;
+import org.apache.james.mime4j.field.address.parser.ASTaddr_spec;
+import org.apache.james.mime4j.field.address.parser.ASTaddress;
+import org.apache.james.mime4j.field.address.parser.ASTaddress_list;
+import org.apache.james.mime4j.field.address.parser.ASTangle_addr;
+import org.apache.james.mime4j.field.address.parser.ASTdomain;
+import org.apache.james.mime4j.field.address.parser.ASTgroup_body;
+import org.apache.james.mime4j.field.address.parser.ASTlocal_part;
+import org.apache.james.mime4j.field.address.parser.ASTmailbox;
+import org.apache.james.mime4j.field.address.parser.ASTname_addr;
+import org.apache.james.mime4j.field.address.parser.ASTphrase;
+import org.apache.james.mime4j.field.address.parser.ASTroute;
+import org.apache.james.mime4j.field.address.parser.Node;
+import org.apache.james.mime4j.field.address.parser.SimpleNode;
+import org.apache.james.mime4j.field.address.parser.Token;
+
+/**
+ * Transforms the JJTree-generated abstract syntax tree
+ * into a graph of org.apache.james.mime4j.field.address objects.
+ *
+ *
+ */
+class Builder {
+
+ private static Builder singleton = new Builder();
+
+ public static Builder getInstance() {
+ return singleton;
+ }
+
+
+
+ public AddressList buildAddressList(ASTaddress_list node) {
+ ArrayList<Address> list = new ArrayList<Address>();
+ for (int i = 0; i < node.jjtGetNumChildren(); i++) {
+ ASTaddress childNode = (ASTaddress) node.jjtGetChild(i);
+ Address address = buildAddress(childNode);
+ list.add(address);
+ }
+ return new AddressList(list, true);
+ }
+
+ private Address buildAddress(ASTaddress node) {
+ ChildNodeIterator it = new ChildNodeIterator(node);
+ Node n = it.nextNode();
+ if (n instanceof ASTaddr_spec) {
+ return buildAddrSpec((ASTaddr_spec)n);
+ }
+ else if (n instanceof ASTangle_addr) {
+ return buildAngleAddr((ASTangle_addr)n);
+ }
+ else if (n instanceof ASTphrase) {
+ String name = buildString((ASTphrase)n, false);
+ Node n2 = it.nextNode();
+ if (n2 instanceof ASTgroup_body) {
+ return new Group(name, buildGroupBody((ASTgroup_body)n2));
+ }
+ else if (n2 instanceof ASTangle_addr) {
+ name = DecoderUtil.decodeEncodedWords(name);
+ return new NamedMailbox(name, buildAngleAddr((ASTangle_addr)n2));
+ }
+ else {
+ throw new IllegalStateException();
+ }
+ }
+ else {
+ throw new IllegalStateException();
+ }
+ }
+
+
+
+ private MailboxList buildGroupBody(ASTgroup_body node) {
+ ArrayList<Address> results = new ArrayList<Address>();
+ ChildNodeIterator it = new ChildNodeIterator(node);
+ while (it.hasNext()) {
+ Node n = it.nextNode();
+ if (n instanceof ASTmailbox)
+ results.add(buildMailbox((ASTmailbox)n));
+ else
+ throw new IllegalStateException();
+ }
+ return new MailboxList(results, true);
+ }
+
+ private Mailbox buildMailbox(ASTmailbox node) {
+ ChildNodeIterator it = new ChildNodeIterator(node);
+ Node n = it.nextNode();
+ if (n instanceof ASTaddr_spec) {
+ return buildAddrSpec((ASTaddr_spec)n);
+ }
+ else if (n instanceof ASTangle_addr) {
+ return buildAngleAddr((ASTangle_addr)n);
+ }
+ else if (n instanceof ASTname_addr) {
+ return buildNameAddr((ASTname_addr)n);
+ }
+ else {
+ throw new IllegalStateException();
+ }
+ }
+
+ private NamedMailbox buildNameAddr(ASTname_addr node) {
+ ChildNodeIterator it = new ChildNodeIterator(node);
+ Node n = it.nextNode();
+ String name;
+ if (n instanceof ASTphrase) {
+ name = buildString((ASTphrase)n, false);
+ }
+ else {
+ throw new IllegalStateException();
+ }
+
+ n = it.nextNode();
+ if (n instanceof ASTangle_addr) {
+ name = DecoderUtil.decodeEncodedWords(name);
+ return new NamedMailbox(name, buildAngleAddr((ASTangle_addr) n));
+ }
+ else {
+ throw new IllegalStateException();
+ }
+ }
+
+ private Mailbox buildAngleAddr(ASTangle_addr node) {
+ ChildNodeIterator it = new ChildNodeIterator(node);
+ DomainList route = null;
+ Node n = it.nextNode();
+ if (n instanceof ASTroute) {
+ route = buildRoute((ASTroute)n);
+ n = it.nextNode();
+ }
+ else if (n instanceof ASTaddr_spec)
+ ; // do nothing
+ else
+ throw new IllegalStateException();
+
+ if (n instanceof ASTaddr_spec)
+ return buildAddrSpec(route, (ASTaddr_spec)n);
+ else
+ throw new IllegalStateException();
+ }
+
+ private DomainList buildRoute(ASTroute node) {
+ ArrayList<String> results = new ArrayList<String>(node.jjtGetNumChildren());
+ ChildNodeIterator it = new ChildNodeIterator(node);
+ while (it.hasNext()) {
+ Node n = it.nextNode();
+ if (n instanceof ASTdomain)
+ results.add(buildString((ASTdomain)n, true));
+ else
+ throw new IllegalStateException();
+ }
+ return new DomainList(results, true);
+ }
+
+ private Mailbox buildAddrSpec(ASTaddr_spec node) {
+ return buildAddrSpec(null, node);
+ }
+ private Mailbox buildAddrSpec(DomainList route, ASTaddr_spec node) {
+ ChildNodeIterator it = new ChildNodeIterator(node);
+ String localPart = buildString((ASTlocal_part)it.nextNode(), true);
+ String domain = buildString((ASTdomain)it.nextNode(), true);
+ return new Mailbox(route, localPart, domain);
+ }
+
+
+ private String buildString(SimpleNode node, boolean stripSpaces) {
+ Token head = node.firstToken;
+ Token tail = node.lastToken;
+ StringBuffer out = new StringBuffer();
+
+ while (head != tail) {
+ out.append(head.image);
+ head = head.next;
+ if (!stripSpaces)
+ addSpecials(out, head.specialToken);
+ }
+ out.append(tail.image);
+
+ return out.toString();
+ }
+
+ private void addSpecials(StringBuffer out, Token specialToken) {
+ if (specialToken != null) {
+ addSpecials(out, specialToken.specialToken);
+ out.append(specialToken.image);
+ }
+ }
+
+ private static class ChildNodeIterator implements Iterator<Node> {
+
+ private SimpleNode simpleNode;
+ private int index;
+ private int len;
+
+ public ChildNodeIterator(SimpleNode simpleNode) {
+ this.simpleNode = simpleNode;
+ this.len = simpleNode.jjtGetNumChildren();
+ this.index = 0;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean hasNext() {
+ return index < len;
+ }
+
+ public Node next() {
+ return nextNode();
+ }
+
+ public Node nextNode() {
+ return simpleNode.jjtGetChild(index++);
+ }
+
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/DomainList.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/DomainList.java
new file mode 100644
index 000000000..49b0f3be5
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/DomainList.java
@@ -0,0 +1,76 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field.address;
+
+import java.util.ArrayList;
+
+/**
+ * An immutable, random-access list of Strings (that
+ * are supposedly domain names or domain literals).
+ *
+ *
+ */
+public class DomainList {
+ private ArrayList<String> domains;
+
+ /**
+ * @param domains An ArrayList that contains only String objects.
+ * @param dontCopy true iff it is not possible for the domains ArrayList to be modified by someone else.
+ */
+ public DomainList(ArrayList<String> domains, boolean dontCopy) {
+ if (domains != null)
+ this.domains = (dontCopy ? domains : new ArrayList<String>(domains));
+ else
+ this.domains = new ArrayList<String>(0);
+ }
+
+ /**
+ * The number of elements in this list.
+ */
+ public int size() {
+ return domains.size();
+ }
+
+ /**
+ * Gets the domain name or domain literal at the
+ * specified index.
+ * @throws IndexOutOfBoundsException If index is &lt; 0 or &gt;= size().
+ */
+ public String get(int index) {
+ if (0 > index || size() <= index)
+ throw new IndexOutOfBoundsException();
+ return domains.get(index);
+ }
+
+ /**
+ * Returns the list of domains formatted as a route
+ * string (not including the trailing ':').
+ */
+ public String toRouteString() {
+ StringBuffer out = new StringBuffer();
+ for (int i = 0; i < domains.size(); i++) {
+ out.append("@");
+ out.append(get(i));
+ if (i + 1 < domains.size())
+ out.append(",");
+ }
+ return out.toString();
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Group.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Group.java
new file mode 100644
index 000000000..c0ab7f724
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Group.java
@@ -0,0 +1,75 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field.address;
+
+import java.util.ArrayList;
+
+/**
+ * A named group of zero or more mailboxes.
+ *
+ *
+ */
+public class Group extends Address {
+ private String name;
+ private MailboxList mailboxList;
+
+ /**
+ * @param name The group name.
+ * @param mailboxes The mailboxes in this group.
+ */
+ public Group(String name, MailboxList mailboxes) {
+ this.name = name;
+ this.mailboxList = mailboxes;
+ }
+
+ /**
+ * Returns the group name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the mailboxes in this group.
+ */
+ public MailboxList getMailboxes() {
+ return mailboxList;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append(name);
+ buf.append(":");
+ for (int i = 0; i < mailboxList.size(); i++) {
+ buf.append(mailboxList.get(i).toString());
+ if (i + 1 < mailboxList.size())
+ buf.append(",");
+ }
+ buf.append(";");
+ return buf.toString();
+ }
+
+ @Override
+ protected void doAddMailboxesTo(ArrayList<Address> results) {
+ for (int i = 0; i < mailboxList.size(); i++)
+ results.add(mailboxList.get(i));
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Mailbox.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Mailbox.java
new file mode 100644
index 000000000..25f2548d4
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/Mailbox.java
@@ -0,0 +1,121 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field.address;
+
+import java.util.ArrayList;
+
+/**
+ * Represents a single e-mail address.
+ *
+ *
+ */
+public class Mailbox extends Address {
+ private DomainList route;
+ private String localPart;
+ private String domain;
+
+ /**
+ * Creates a mailbox without a route. Routes are obsolete.
+ * @param localPart The part of the e-mail address to the left of the "@".
+ * @param domain The part of the e-mail address to the right of the "@".
+ */
+ public Mailbox(String localPart, String domain) {
+ this(null, localPart, domain);
+ }
+
+ /**
+ * Creates a mailbox with a route. Routes are obsolete.
+ * @param route The zero or more domains that make up the route. Can be null.
+ * @param localPart The part of the e-mail address to the left of the "@".
+ * @param domain The part of the e-mail address to the right of the "@".
+ */
+ public Mailbox(DomainList route, String localPart, String domain) {
+ this.route = route;
+ this.localPart = localPart;
+ this.domain = domain;
+ }
+
+ /**
+ * Returns the route list.
+ */
+ public DomainList getRoute() {
+ return route;
+ }
+
+ /**
+ * Returns the left part of the e-mail address
+ * (before "@").
+ */
+ public String getLocalPart() {
+ return localPart;
+ }
+
+ /**
+ * Returns the right part of the e-mail address
+ * (after "@").
+ */
+ public String getDomain() {
+ return domain;
+ }
+
+ /**
+ * Formats the address as a string, not including
+ * the route.
+ *
+ * @see #getAddressString(boolean)
+ */
+ public String getAddressString() {
+ return getAddressString(false);
+ }
+
+ /**
+ * Note that this value may not be usable
+ * for transport purposes, only display purposes.
+ *
+ * For example, if the unparsed address was
+ *
+ * <"Joe Cheng"@joecheng.com>
+ *
+ * this method would return
+ *
+ * <Joe Cheng@joecheng.com>
+ *
+ * which is not valid for transport; the local part
+ * would need to be re-quoted.
+ *
+ * @param includeRoute true if the route should be included if it exists.
+ */
+ public String getAddressString(boolean includeRoute) {
+ return "<" + (!includeRoute || route == null ? "" : route.toRouteString() + ":")
+ + localPart
+ + (domain == null ? "" : "@")
+ + domain + ">";
+ }
+
+ @Override
+ protected final void doAddMailboxesTo(ArrayList<Address> results) {
+ results.add(this);
+ }
+
+ @Override
+ public String toString() {
+ return getAddressString();
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/MailboxList.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/MailboxList.java
new file mode 100644
index 000000000..2c9efb37f
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/MailboxList.java
@@ -0,0 +1,71 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field.address;
+
+import java.util.ArrayList;
+
+/**
+ * An immutable, random-access list of Mailbox objects.
+ *
+ *
+ */
+public class MailboxList {
+
+ private ArrayList<Address> mailboxes;
+
+ /**
+ * @param mailboxes An ArrayList that contains only Mailbox objects.
+ * @param dontCopy true iff it is not possible for the mailboxes ArrayList to be modified by someone else.
+ */
+ public MailboxList(ArrayList<Address> mailboxes, boolean dontCopy) {
+ if (mailboxes != null)
+ this.mailboxes = (dontCopy ? mailboxes : new ArrayList<Address>(mailboxes));
+ else
+ this.mailboxes = new ArrayList<Address>(0);
+ }
+
+ /**
+ * The number of elements in this list.
+ */
+ public int size() {
+ return mailboxes.size();
+ }
+
+ /**
+ * Gets an address.
+ */
+ public Mailbox get(int index) {
+ if (0 > index || size() <= index)
+ throw new IndexOutOfBoundsException();
+ return (Mailbox)mailboxes.get(index);
+ }
+
+ /**
+ * Dumps a representation of this mailbox list to
+ * stdout, for debugging purposes.
+ */
+ public void print() {
+ for (int i = 0; i < size(); i++) {
+ Mailbox mailbox = get(i);
+ System.out.println(mailbox.toString());
+ }
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/NamedMailbox.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/NamedMailbox.java
new file mode 100644
index 000000000..4b8306037
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/NamedMailbox.java
@@ -0,0 +1,71 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field.address;
+
+/**
+ * A Mailbox that has a name/description.
+ *
+ *
+ */
+public class NamedMailbox extends Mailbox {
+ private String name;
+
+ /**
+ * @see Mailbox#Mailbox(String, String)
+ */
+ public NamedMailbox(String name, String localPart, String domain) {
+ super(localPart, domain);
+ this.name = name;
+ }
+
+ /**
+ * @see Mailbox#Mailbox(DomainList, String, String)
+ */
+ public NamedMailbox(String name, DomainList route, String localPart, String domain) {
+ super(route, localPart, domain);
+ this.name = name;
+ }
+
+ /**
+ * Creates a named mailbox based on an unnamed mailbox.
+ */
+ public NamedMailbox(String name, Mailbox baseMailbox) {
+ super(baseMailbox.getRoute(), baseMailbox.getLocalPart(), baseMailbox.getDomain());
+ this.name = name;
+ }
+
+ /**
+ * Returns the name of the mailbox.
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * Same features (or problems) as Mailbox.getAddressString(boolean),
+ * only more so.
+ *
+ * @see Mailbox#getAddressString(boolean)
+ */
+ @Override
+ public String getAddressString(boolean includeRoute) {
+ return (name == null ? "" : name + " ") + super.getAddressString(includeRoute);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTaddr_spec.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTaddr_spec.java
new file mode 100644
index 000000000..4d56d000b
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTaddr_spec.java
@@ -0,0 +1,19 @@
+/* Generated By:JJTree: Do not edit this line. ASTaddr_spec.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+public class ASTaddr_spec extends SimpleNode {
+ public ASTaddr_spec(int id) {
+ super(id);
+ }
+
+ public ASTaddr_spec(AddressListParser p, int id) {
+ super(p, id);
+ }
+
+
+ /** Accept the visitor. **/
+ public Object jjtAccept(AddressListParserVisitor visitor, Object data) {
+ return visitor.visit(this, data);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTaddress.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTaddress.java
new file mode 100644
index 000000000..47bdeda8e
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTaddress.java
@@ -0,0 +1,19 @@
+/* Generated By:JJTree: Do not edit this line. ASTaddress.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+public class ASTaddress extends SimpleNode {
+ public ASTaddress(int id) {
+ super(id);
+ }
+
+ public ASTaddress(AddressListParser p, int id) {
+ super(p, id);
+ }
+
+
+ /** Accept the visitor. **/
+ public Object jjtAccept(AddressListParserVisitor visitor, Object data) {
+ return visitor.visit(this, data);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTaddress_list.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTaddress_list.java
new file mode 100644
index 000000000..737840e38
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTaddress_list.java
@@ -0,0 +1,19 @@
+/* Generated By:JJTree: Do not edit this line. ASTaddress_list.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+public class ASTaddress_list extends SimpleNode {
+ public ASTaddress_list(int id) {
+ super(id);
+ }
+
+ public ASTaddress_list(AddressListParser p, int id) {
+ super(p, id);
+ }
+
+
+ /** Accept the visitor. **/
+ public Object jjtAccept(AddressListParserVisitor visitor, Object data) {
+ return visitor.visit(this, data);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTangle_addr.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTangle_addr.java
new file mode 100644
index 000000000..8cb8f421f
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTangle_addr.java
@@ -0,0 +1,19 @@
+/* Generated By:JJTree: Do not edit this line. ASTangle_addr.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+public class ASTangle_addr extends SimpleNode {
+ public ASTangle_addr(int id) {
+ super(id);
+ }
+
+ public ASTangle_addr(AddressListParser p, int id) {
+ super(p, id);
+ }
+
+
+ /** Accept the visitor. **/
+ public Object jjtAccept(AddressListParserVisitor visitor, Object data) {
+ return visitor.visit(this, data);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTdomain.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTdomain.java
new file mode 100644
index 000000000..b52664386
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTdomain.java
@@ -0,0 +1,19 @@
+/* Generated By:JJTree: Do not edit this line. ASTdomain.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+public class ASTdomain extends SimpleNode {
+ public ASTdomain(int id) {
+ super(id);
+ }
+
+ public ASTdomain(AddressListParser p, int id) {
+ super(p, id);
+ }
+
+
+ /** Accept the visitor. **/
+ public Object jjtAccept(AddressListParserVisitor visitor, Object data) {
+ return visitor.visit(this, data);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTgroup_body.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTgroup_body.java
new file mode 100644
index 000000000..f6017b9fc
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTgroup_body.java
@@ -0,0 +1,19 @@
+/* Generated By:JJTree: Do not edit this line. ASTgroup_body.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+public class ASTgroup_body extends SimpleNode {
+ public ASTgroup_body(int id) {
+ super(id);
+ }
+
+ public ASTgroup_body(AddressListParser p, int id) {
+ super(p, id);
+ }
+
+
+ /** Accept the visitor. **/
+ public Object jjtAccept(AddressListParserVisitor visitor, Object data) {
+ return visitor.visit(this, data);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTlocal_part.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTlocal_part.java
new file mode 100644
index 000000000..5c244fa3e
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTlocal_part.java
@@ -0,0 +1,19 @@
+/* Generated By:JJTree: Do not edit this line. ASTlocal_part.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+public class ASTlocal_part extends SimpleNode {
+ public ASTlocal_part(int id) {
+ super(id);
+ }
+
+ public ASTlocal_part(AddressListParser p, int id) {
+ super(p, id);
+ }
+
+
+ /** Accept the visitor. **/
+ public Object jjtAccept(AddressListParserVisitor visitor, Object data) {
+ return visitor.visit(this, data);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTmailbox.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTmailbox.java
new file mode 100644
index 000000000..aeb469da1
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTmailbox.java
@@ -0,0 +1,19 @@
+/* Generated By:JJTree: Do not edit this line. ASTmailbox.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+public class ASTmailbox extends SimpleNode {
+ public ASTmailbox(int id) {
+ super(id);
+ }
+
+ public ASTmailbox(AddressListParser p, int id) {
+ super(p, id);
+ }
+
+
+ /** Accept the visitor. **/
+ public Object jjtAccept(AddressListParserVisitor visitor, Object data) {
+ return visitor.visit(this, data);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTname_addr.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTname_addr.java
new file mode 100644
index 000000000..846c73167
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTname_addr.java
@@ -0,0 +1,19 @@
+/* Generated By:JJTree: Do not edit this line. ASTname_addr.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+public class ASTname_addr extends SimpleNode {
+ public ASTname_addr(int id) {
+ super(id);
+ }
+
+ public ASTname_addr(AddressListParser p, int id) {
+ super(p, id);
+ }
+
+
+ /** Accept the visitor. **/
+ public Object jjtAccept(AddressListParserVisitor visitor, Object data) {
+ return visitor.visit(this, data);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTphrase.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTphrase.java
new file mode 100644
index 000000000..7d711c529
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTphrase.java
@@ -0,0 +1,19 @@
+/* Generated By:JJTree: Do not edit this line. ASTphrase.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+public class ASTphrase extends SimpleNode {
+ public ASTphrase(int id) {
+ super(id);
+ }
+
+ public ASTphrase(AddressListParser p, int id) {
+ super(p, id);
+ }
+
+
+ /** Accept the visitor. **/
+ public Object jjtAccept(AddressListParserVisitor visitor, Object data) {
+ return visitor.visit(this, data);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTroute.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTroute.java
new file mode 100644
index 000000000..54ea11523
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ASTroute.java
@@ -0,0 +1,19 @@
+/* Generated By:JJTree: Do not edit this line. ASTroute.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+public class ASTroute extends SimpleNode {
+ public ASTroute(int id) {
+ super(id);
+ }
+
+ public ASTroute(AddressListParser p, int id) {
+ super(p, id);
+ }
+
+
+ /** Accept the visitor. **/
+ public Object jjtAccept(AddressListParserVisitor visitor, Object data) {
+ return visitor.visit(this, data);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParser.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParser.java
new file mode 100644
index 000000000..8094df0ad
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParser.java
@@ -0,0 +1,977 @@
+/* Generated By:JJTree&JavaCC: Do not edit this line. AddressListParser.java */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.address.parser;
+
+public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeConstants, AddressListParserConstants {/*@bgen(jjtree)*/
+ protected JJTAddressListParserState jjtree = new JJTAddressListParserState();public static void main(String args[]) throws ParseException {
+ while (true) {
+ try {
+ AddressListParser parser = new AddressListParser(System.in);
+ parser.parseLine();
+ ((SimpleNode)parser.jjtree.rootNode()).dump("> ");
+ } catch (Exception x) {
+ x.printStackTrace();
+ return;
+ }
+ }
+ }
+
+ private static void log(String msg) {
+ System.out.print(msg);
+ }
+
+ public ASTaddress_list parse() throws ParseException {
+ try {
+ parseAll();
+ return (ASTaddress_list)jjtree.rootNode();
+ } catch (TokenMgrError tme) {
+ throw new ParseException(tme.getMessage());
+ }
+ }
+
+
+ void jjtreeOpenNodeScope(Node n) {
+ ((SimpleNode)n).firstToken = getToken(1);
+ }
+
+ void jjtreeCloseNodeScope(Node n) {
+ ((SimpleNode)n).lastToken = getToken(0);
+ }
+
+ final public void parseLine() throws ParseException {
+ address_list();
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 1:
+ jj_consume_token(1);
+ break;
+ default:
+ jj_la1[0] = jj_gen;
+ ;
+ }
+ jj_consume_token(2);
+ }
+
+ final public void parseAll() throws ParseException {
+ address_list();
+ jj_consume_token(0);
+ }
+
+ final public void address_list() throws ParseException {
+ /*@bgen(jjtree) address_list */
+ ASTaddress_list jjtn000 = new ASTaddress_list(JJTADDRESS_LIST);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+ try {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 6:
+ case DOTATOM:
+ case QUOTEDSTRING:
+ address();
+ break;
+ default:
+ jj_la1[1] = jj_gen;
+ ;
+ }
+ label_1:
+ while (true) {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 3:
+ ;
+ break;
+ default:
+ jj_la1[2] = jj_gen;
+ break label_1;
+ }
+ jj_consume_token(3);
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 6:
+ case DOTATOM:
+ case QUOTEDSTRING:
+ address();
+ break;
+ default:
+ jj_la1[3] = jj_gen;
+ ;
+ }
+ }
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ {if (true) throw (RuntimeException)jjte000;}
+ }
+ if (jjte000 instanceof ParseException) {
+ {if (true) throw (ParseException)jjte000;}
+ }
+ {if (true) throw (Error)jjte000;}
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+ }
+
+ final public void address() throws ParseException {
+ /*@bgen(jjtree) address */
+ ASTaddress jjtn000 = new ASTaddress(JJTADDRESS);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+ try {
+ if (jj_2_1(2147483647)) {
+ addr_spec();
+ } else {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 6:
+ angle_addr();
+ break;
+ case DOTATOM:
+ case QUOTEDSTRING:
+ phrase();
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 4:
+ group_body();
+ break;
+ case 6:
+ angle_addr();
+ break;
+ default:
+ jj_la1[4] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ break;
+ default:
+ jj_la1[5] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ {if (true) throw (RuntimeException)jjte000;}
+ }
+ if (jjte000 instanceof ParseException) {
+ {if (true) throw (ParseException)jjte000;}
+ }
+ {if (true) throw (Error)jjte000;}
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+ }
+
+ final public void mailbox() throws ParseException {
+ /*@bgen(jjtree) mailbox */
+ ASTmailbox jjtn000 = new ASTmailbox(JJTMAILBOX);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+ try {
+ if (jj_2_2(2147483647)) {
+ addr_spec();
+ } else {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 6:
+ angle_addr();
+ break;
+ case DOTATOM:
+ case QUOTEDSTRING:
+ name_addr();
+ break;
+ default:
+ jj_la1[6] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ {if (true) throw (RuntimeException)jjte000;}
+ }
+ if (jjte000 instanceof ParseException) {
+ {if (true) throw (ParseException)jjte000;}
+ }
+ {if (true) throw (Error)jjte000;}
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+ }
+
+ final public void name_addr() throws ParseException {
+ /*@bgen(jjtree) name_addr */
+ ASTname_addr jjtn000 = new ASTname_addr(JJTNAME_ADDR);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+ try {
+ phrase();
+ angle_addr();
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ {if (true) throw (RuntimeException)jjte000;}
+ }
+ if (jjte000 instanceof ParseException) {
+ {if (true) throw (ParseException)jjte000;}
+ }
+ {if (true) throw (Error)jjte000;}
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+ }
+
+ final public void group_body() throws ParseException {
+ /*@bgen(jjtree) group_body */
+ ASTgroup_body jjtn000 = new ASTgroup_body(JJTGROUP_BODY);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+ try {
+ jj_consume_token(4);
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 6:
+ case DOTATOM:
+ case QUOTEDSTRING:
+ mailbox();
+ break;
+ default:
+ jj_la1[7] = jj_gen;
+ ;
+ }
+ label_2:
+ while (true) {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 3:
+ ;
+ break;
+ default:
+ jj_la1[8] = jj_gen;
+ break label_2;
+ }
+ jj_consume_token(3);
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 6:
+ case DOTATOM:
+ case QUOTEDSTRING:
+ mailbox();
+ break;
+ default:
+ jj_la1[9] = jj_gen;
+ ;
+ }
+ }
+ jj_consume_token(5);
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ {if (true) throw (RuntimeException)jjte000;}
+ }
+ if (jjte000 instanceof ParseException) {
+ {if (true) throw (ParseException)jjte000;}
+ }
+ {if (true) throw (Error)jjte000;}
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+ }
+
+ final public void angle_addr() throws ParseException {
+ /*@bgen(jjtree) angle_addr */
+ ASTangle_addr jjtn000 = new ASTangle_addr(JJTANGLE_ADDR);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+ try {
+ jj_consume_token(6);
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 8:
+ route();
+ break;
+ default:
+ jj_la1[10] = jj_gen;
+ ;
+ }
+ addr_spec();
+ jj_consume_token(7);
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ {if (true) throw (RuntimeException)jjte000;}
+ }
+ if (jjte000 instanceof ParseException) {
+ {if (true) throw (ParseException)jjte000;}
+ }
+ {if (true) throw (Error)jjte000;}
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+ }
+
+ final public void route() throws ParseException {
+ /*@bgen(jjtree) route */
+ ASTroute jjtn000 = new ASTroute(JJTROUTE);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+ try {
+ jj_consume_token(8);
+ domain();
+ label_3:
+ while (true) {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 3:
+ case 8:
+ ;
+ break;
+ default:
+ jj_la1[11] = jj_gen;
+ break label_3;
+ }
+ label_4:
+ while (true) {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 3:
+ ;
+ break;
+ default:
+ jj_la1[12] = jj_gen;
+ break label_4;
+ }
+ jj_consume_token(3);
+ }
+ jj_consume_token(8);
+ domain();
+ }
+ jj_consume_token(4);
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ {if (true) throw (RuntimeException)jjte000;}
+ }
+ if (jjte000 instanceof ParseException) {
+ {if (true) throw (ParseException)jjte000;}
+ }
+ {if (true) throw (Error)jjte000;}
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+ }
+
+ final public void phrase() throws ParseException {
+ /*@bgen(jjtree) phrase */
+ ASTphrase jjtn000 = new ASTphrase(JJTPHRASE);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+ try {
+ label_5:
+ while (true) {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case DOTATOM:
+ jj_consume_token(DOTATOM);
+ break;
+ case QUOTEDSTRING:
+ jj_consume_token(QUOTEDSTRING);
+ break;
+ default:
+ jj_la1[13] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case DOTATOM:
+ case QUOTEDSTRING:
+ ;
+ break;
+ default:
+ jj_la1[14] = jj_gen;
+ break label_5;
+ }
+ }
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+ }
+
+ final public void addr_spec() throws ParseException {
+ /*@bgen(jjtree) addr_spec */
+ ASTaddr_spec jjtn000 = new ASTaddr_spec(JJTADDR_SPEC);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+ try {
+ local_part();
+ jj_consume_token(8);
+ domain();
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ {if (true) throw (RuntimeException)jjte000;}
+ }
+ if (jjte000 instanceof ParseException) {
+ {if (true) throw (ParseException)jjte000;}
+ }
+ {if (true) throw (Error)jjte000;}
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+ }
+
+ final public void local_part() throws ParseException {
+ /*@bgen(jjtree) local_part */
+ ASTlocal_part jjtn000 = new ASTlocal_part(JJTLOCAL_PART);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);Token t;
+ try {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case DOTATOM:
+ t = jj_consume_token(DOTATOM);
+ break;
+ case QUOTEDSTRING:
+ t = jj_consume_token(QUOTEDSTRING);
+ break;
+ default:
+ jj_la1[15] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ label_6:
+ while (true) {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 9:
+ case DOTATOM:
+ case QUOTEDSTRING:
+ ;
+ break;
+ default:
+ jj_la1[16] = jj_gen;
+ break label_6;
+ }
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 9:
+ t = jj_consume_token(9);
+ break;
+ default:
+ jj_la1[17] = jj_gen;
+ ;
+ }
+ if (t.image.charAt(t.image.length() - 1) != '.' || t.kind == AddressListParserConstants.QUOTEDSTRING)
+ {if (true) throw new ParseException("Words in local part must be separated by '.'");}
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case DOTATOM:
+ t = jj_consume_token(DOTATOM);
+ break;
+ case QUOTEDSTRING:
+ t = jj_consume_token(QUOTEDSTRING);
+ break;
+ default:
+ jj_la1[18] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+ }
+
+ final public void domain() throws ParseException {
+ /*@bgen(jjtree) domain */
+ ASTdomain jjtn000 = new ASTdomain(JJTDOMAIN);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);Token t;
+ try {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case DOTATOM:
+ t = jj_consume_token(DOTATOM);
+ label_7:
+ while (true) {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 9:
+ case DOTATOM:
+ ;
+ break;
+ default:
+ jj_la1[19] = jj_gen;
+ break label_7;
+ }
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 9:
+ t = jj_consume_token(9);
+ break;
+ default:
+ jj_la1[20] = jj_gen;
+ ;
+ }
+ if (t.image.charAt(t.image.length() - 1) != '.')
+ {if (true) throw new ParseException("Atoms in domain names must be separated by '.'");}
+ t = jj_consume_token(DOTATOM);
+ }
+ break;
+ case DOMAINLITERAL:
+ jj_consume_token(DOMAINLITERAL);
+ break;
+ default:
+ jj_la1[21] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+ }
+
+ final private boolean jj_2_1(int xla) {
+ jj_la = xla; jj_lastpos = jj_scanpos = token;
+ try { return !jj_3_1(); }
+ catch(LookaheadSuccess ls) { return true; }
+ finally { jj_save(0, xla); }
+ }
+
+ final private boolean jj_2_2(int xla) {
+ jj_la = xla; jj_lastpos = jj_scanpos = token;
+ try { return !jj_3_2(); }
+ catch(LookaheadSuccess ls) { return true; }
+ finally { jj_save(1, xla); }
+ }
+
+ final private boolean jj_3R_11() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_scan_token(9)) jj_scanpos = xsp;
+ xsp = jj_scanpos;
+ if (jj_scan_token(14)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(31)) return true;
+ }
+ return false;
+ }
+
+ final private boolean jj_3R_13() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_scan_token(9)) jj_scanpos = xsp;
+ if (jj_scan_token(DOTATOM)) return true;
+ return false;
+ }
+
+ final private boolean jj_3R_8() {
+ if (jj_3R_9()) return true;
+ if (jj_scan_token(8)) return true;
+ if (jj_3R_10()) return true;
+ return false;
+ }
+
+ final private boolean jj_3_1() {
+ if (jj_3R_8()) return true;
+ return false;
+ }
+
+ final private boolean jj_3R_12() {
+ if (jj_scan_token(DOTATOM)) return true;
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_13()) { jj_scanpos = xsp; break; }
+ }
+ return false;
+ }
+
+ final private boolean jj_3R_10() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_12()) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(18)) return true;
+ }
+ return false;
+ }
+
+ final private boolean jj_3_2() {
+ if (jj_3R_8()) return true;
+ return false;
+ }
+
+ final private boolean jj_3R_9() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_scan_token(14)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(31)) return true;
+ }
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_11()) { jj_scanpos = xsp; break; }
+ }
+ return false;
+ }
+
+ public AddressListParserTokenManager token_source;
+ SimpleCharStream jj_input_stream;
+ public Token token, jj_nt;
+ private int jj_ntk;
+ private Token jj_scanpos, jj_lastpos;
+ private int jj_la;
+ public boolean lookingAhead = false;
+ private boolean jj_semLA;
+ private int jj_gen;
+ final private int[] jj_la1 = new int[22];
+ static private int[] jj_la1_0;
+ static private int[] jj_la1_1;
+ static {
+ jj_la1_0();
+ jj_la1_1();
+ }
+ private static void jj_la1_0() {
+ jj_la1_0 = new int[] {0x2,0x80004040,0x8,0x80004040,0x50,0x80004040,0x80004040,0x80004040,0x8,0x80004040,0x100,0x108,0x8,0x80004000,0x80004000,0x80004000,0x80004200,0x200,0x80004000,0x4200,0x200,0x44000,};
+ }
+ private static void jj_la1_1() {
+ jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,};
+ }
+ final private JJCalls[] jj_2_rtns = new JJCalls[2];
+ private boolean jj_rescan = false;
+ private int jj_gc = 0;
+
+ public AddressListParser(java.io.InputStream stream) {
+ this(stream, null);
+ }
+ public AddressListParser(java.io.InputStream stream, String encoding) {
+ try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+ token_source = new AddressListParserTokenManager(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 22; i++) jj_la1[i] = -1;
+ for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+ }
+
+ public void ReInit(java.io.InputStream stream) {
+ ReInit(stream, null);
+ }
+ public void ReInit(java.io.InputStream stream, String encoding) {
+ try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+ token_source.ReInit(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jjtree.reset();
+ jj_gen = 0;
+ for (int i = 0; i < 22; i++) jj_la1[i] = -1;
+ for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+ }
+
+ public AddressListParser(java.io.Reader stream) {
+ jj_input_stream = new SimpleCharStream(stream, 1, 1);
+ token_source = new AddressListParserTokenManager(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 22; i++) jj_la1[i] = -1;
+ for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+ }
+
+ public void ReInit(java.io.Reader stream) {
+ jj_input_stream.ReInit(stream, 1, 1);
+ token_source.ReInit(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jjtree.reset();
+ jj_gen = 0;
+ for (int i = 0; i < 22; i++) jj_la1[i] = -1;
+ for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+ }
+
+ public AddressListParser(AddressListParserTokenManager tm) {
+ token_source = tm;
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 22; i++) jj_la1[i] = -1;
+ for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+ }
+
+ public void ReInit(AddressListParserTokenManager tm) {
+ token_source = tm;
+ token = new Token();
+ jj_ntk = -1;
+ jjtree.reset();
+ jj_gen = 0;
+ for (int i = 0; i < 22; i++) jj_la1[i] = -1;
+ for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+ }
+
+ final private Token jj_consume_token(int kind) throws ParseException {
+ Token oldToken;
+ if ((oldToken = token).next != null) token = token.next;
+ else token = token.next = token_source.getNextToken();
+ jj_ntk = -1;
+ if (token.kind == kind) {
+ jj_gen++;
+ if (++jj_gc > 100) {
+ jj_gc = 0;
+ for (int i = 0; i < jj_2_rtns.length; i++) {
+ JJCalls c = jj_2_rtns[i];
+ while (c != null) {
+ if (c.gen < jj_gen) c.first = null;
+ c = c.next;
+ }
+ }
+ }
+ return token;
+ }
+ token = oldToken;
+ jj_kind = kind;
+ throw generateParseException();
+ }
+
+ static private final class LookaheadSuccess extends java.lang.Error { }
+ final private LookaheadSuccess jj_ls = new LookaheadSuccess();
+ final private boolean jj_scan_token(int kind) {
+ if (jj_scanpos == jj_lastpos) {
+ jj_la--;
+ if (jj_scanpos.next == null) {
+ jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken();
+ } else {
+ jj_lastpos = jj_scanpos = jj_scanpos.next;
+ }
+ } else {
+ jj_scanpos = jj_scanpos.next;
+ }
+ if (jj_rescan) {
+ int i = 0; Token tok = token;
+ while (tok != null && tok != jj_scanpos) { i++; tok = tok.next; }
+ if (tok != null) jj_add_error_token(kind, i);
+ }
+ if (jj_scanpos.kind != kind) return true;
+ if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls;
+ return false;
+ }
+
+ final public Token getNextToken() {
+ if (token.next != null) token = token.next;
+ else token = token.next = token_source.getNextToken();
+ jj_ntk = -1;
+ jj_gen++;
+ return token;
+ }
+
+ final public Token getToken(int index) {
+ Token t = lookingAhead ? jj_scanpos : token;
+ for (int i = 0; i < index; i++) {
+ if (t.next != null) t = t.next;
+ else t = t.next = token_source.getNextToken();
+ }
+ return t;
+ }
+
+ final private int jj_ntk() {
+ if ((jj_nt=token.next) == null)
+ return (jj_ntk = (token.next=token_source.getNextToken()).kind);
+ else
+ return (jj_ntk = jj_nt.kind);
+ }
+
+ private java.util.Vector<int[]> jj_expentries = new java.util.Vector<int[]>();
+ private int[] jj_expentry;
+ private int jj_kind = -1;
+ private int[] jj_lasttokens = new int[100];
+ private int jj_endpos;
+
+ private void jj_add_error_token(int kind, int pos) {
+ if (pos >= 100) return;
+ if (pos == jj_endpos + 1) {
+ jj_lasttokens[jj_endpos++] = kind;
+ } else if (jj_endpos != 0) {
+ jj_expentry = new int[jj_endpos];
+ for (int i = 0; i < jj_endpos; i++) {
+ jj_expentry[i] = jj_lasttokens[i];
+ }
+ boolean exists = false;
+ for (java.util.Enumeration<int[]> e = jj_expentries.elements(); e.hasMoreElements();) {
+ int[] oldentry = e.nextElement();
+ if (oldentry.length == jj_expentry.length) {
+ exists = true;
+ for (int i = 0; i < jj_expentry.length; i++) {
+ if (oldentry[i] != jj_expentry[i]) {
+ exists = false;
+ break;
+ }
+ }
+ if (exists) break;
+ }
+ }
+ if (!exists) jj_expentries.addElement(jj_expentry);
+ if (pos != 0) jj_lasttokens[(jj_endpos = pos) - 1] = kind;
+ }
+ }
+
+ public ParseException generateParseException() {
+ jj_expentries.removeAllElements();
+ boolean[] la1tokens = new boolean[34];
+ for (int i = 0; i < 34; i++) {
+ la1tokens[i] = false;
+ }
+ if (jj_kind >= 0) {
+ la1tokens[jj_kind] = true;
+ jj_kind = -1;
+ }
+ for (int i = 0; i < 22; i++) {
+ if (jj_la1[i] == jj_gen) {
+ for (int j = 0; j < 32; j++) {
+ if ((jj_la1_0[i] & (1<<j)) != 0) {
+ la1tokens[j] = true;
+ }
+ if ((jj_la1_1[i] & (1<<j)) != 0) {
+ la1tokens[32+j] = true;
+ }
+ }
+ }
+ }
+ for (int i = 0; i < 34; i++) {
+ if (la1tokens[i]) {
+ jj_expentry = new int[1];
+ jj_expentry[0] = i;
+ jj_expentries.addElement(jj_expentry);
+ }
+ }
+ jj_endpos = 0;
+ jj_rescan_token();
+ jj_add_error_token(0, 0);
+ int[][] exptokseq = new int[jj_expentries.size()][];
+ for (int i = 0; i < jj_expentries.size(); i++) {
+ exptokseq[i] = jj_expentries.elementAt(i);
+ }
+ return new ParseException(token, exptokseq, tokenImage);
+ }
+
+ final public void enable_tracing() {
+ }
+
+ final public void disable_tracing() {
+ }
+
+ final private void jj_rescan_token() {
+ jj_rescan = true;
+ for (int i = 0; i < 2; i++) {
+ try {
+ JJCalls p = jj_2_rtns[i];
+ do {
+ if (p.gen > jj_gen) {
+ jj_la = p.arg; jj_lastpos = jj_scanpos = p.first;
+ switch (i) {
+ case 0: jj_3_1(); break;
+ case 1: jj_3_2(); break;
+ }
+ }
+ p = p.next;
+ } while (p != null);
+ } catch(LookaheadSuccess ls) { }
+ }
+ jj_rescan = false;
+ }
+
+ final private void jj_save(int index, int xla) {
+ JJCalls p = jj_2_rtns[index];
+ while (p.gen > jj_gen) {
+ if (p.next == null) { p = p.next = new JJCalls(); break; }
+ p = p.next;
+ }
+ p.gen = jj_gen + xla - jj_la; p.first = token; p.arg = xla;
+ }
+
+ static final class JJCalls {
+ int gen;
+ Token first;
+ int arg;
+ JJCalls next;
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParser.jj b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParser.jj
new file mode 100644
index 000000000..c14277bc6
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParser.jj
@@ -0,0 +1,595 @@
+/*@bgen(jjtree) Generated By:JJTree: Do not edit this line. /Users/jason/Projects/apache-mime4j-0.3/target/generated-sources/jjtree/org/apache/james/mime4j/field/address/parser/AddressListParser.jj */
+/*@egen*//****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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. *
+ ****************************************************************/
+
+
+/**
+ * RFC2822 address list parser.
+ *
+ * Created 9/17/2004
+ * by Joe Cheng <code@joecheng.com>
+ */
+
+options {
+ STATIC=false;
+ LOOKAHEAD=1;
+ //DEBUG_PARSER=true;
+ //DEBUG_TOKEN_MANAGER=true;
+}
+
+PARSER_BEGIN(AddressListParser)
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.address.parser;
+
+public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeConstants/*@egen*/ {/*@bgen(jjtree)*/
+ protected JJTAddressListParserState jjtree = new JJTAddressListParserState();
+
+/*@egen*/
+ public static void main(String args[]) throws ParseException {
+ while (true) {
+ try {
+ AddressListParser parser = new AddressListParser(System.in);
+ parser.parseLine();
+ ((SimpleNode)parser.jjtree.rootNode()).dump("> ");
+ } catch (Exception x) {
+ x.printStackTrace();
+ return;
+ }
+ }
+ }
+
+ private static void log(String msg) {
+ System.out.print(msg);
+ }
+
+ public ASTaddress_list parse() throws ParseException {
+ try {
+ parseAll();
+ return (ASTaddress_list)jjtree.rootNode();
+ } catch (TokenMgrError tme) {
+ throw new ParseException(tme.getMessage());
+ }
+ }
+
+
+ void jjtreeOpenNodeScope(Node n) {
+ ((SimpleNode)n).firstToken = getToken(1);
+ }
+
+ void jjtreeCloseNodeScope(Node n) {
+ ((SimpleNode)n).lastToken = getToken(0);
+ }
+}
+
+PARSER_END(AddressListParser)
+
+void parseLine() :
+{}
+{
+ address_list() ["\r"] "\n"
+}
+
+void parseAll() :
+{}
+{
+ address_list() <EOF>
+}
+
+void address_list() :
+{/*@bgen(jjtree) address_list */
+ ASTaddress_list jjtn000 = new ASTaddress_list(JJTADDRESS_LIST);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+/*@egen*/}
+{/*@bgen(jjtree) address_list */
+ try {
+/*@egen*/
+ [ address() ]
+ (
+ ","
+ [ address() ]
+ )*/*@bgen(jjtree)*/
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ throw (RuntimeException)jjte000;
+ }
+ if (jjte000 instanceof ParseException) {
+ throw (ParseException)jjte000;
+ }
+ throw (Error)jjte000;
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+/*@egen*/
+}
+
+void address() :
+{/*@bgen(jjtree) address */
+ ASTaddress jjtn000 = new ASTaddress(JJTADDRESS);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+/*@egen*/}
+{/*@bgen(jjtree) address */
+ try {
+/*@egen*/
+ LOOKAHEAD(2147483647)
+ addr_spec()
+| angle_addr()
+| ( phrase() (group_body() | angle_addr()) )/*@bgen(jjtree)*/
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ throw (RuntimeException)jjte000;
+ }
+ if (jjte000 instanceof ParseException) {
+ throw (ParseException)jjte000;
+ }
+ throw (Error)jjte000;
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+/*@egen*/
+}
+
+void mailbox() :
+{/*@bgen(jjtree) mailbox */
+ ASTmailbox jjtn000 = new ASTmailbox(JJTMAILBOX);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+/*@egen*/}
+{/*@bgen(jjtree) mailbox */
+ try {
+/*@egen*/
+ LOOKAHEAD(2147483647)
+ addr_spec()
+| angle_addr()
+| name_addr()/*@bgen(jjtree)*/
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ throw (RuntimeException)jjte000;
+ }
+ if (jjte000 instanceof ParseException) {
+ throw (ParseException)jjte000;
+ }
+ throw (Error)jjte000;
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+/*@egen*/
+}
+
+void name_addr() :
+{/*@bgen(jjtree) name_addr */
+ ASTname_addr jjtn000 = new ASTname_addr(JJTNAME_ADDR);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+/*@egen*/}
+{/*@bgen(jjtree) name_addr */
+ try {
+/*@egen*/
+ phrase() angle_addr()/*@bgen(jjtree)*/
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ throw (RuntimeException)jjte000;
+ }
+ if (jjte000 instanceof ParseException) {
+ throw (ParseException)jjte000;
+ }
+ throw (Error)jjte000;
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+/*@egen*/
+}
+
+void group_body() :
+{/*@bgen(jjtree) group_body */
+ ASTgroup_body jjtn000 = new ASTgroup_body(JJTGROUP_BODY);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+/*@egen*/}
+{/*@bgen(jjtree) group_body */
+ try {
+/*@egen*/
+ ":"
+ [ mailbox() ]
+ (
+ ","
+ [ mailbox() ]
+ )*
+ ";"/*@bgen(jjtree)*/
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ throw (RuntimeException)jjte000;
+ }
+ if (jjte000 instanceof ParseException) {
+ throw (ParseException)jjte000;
+ }
+ throw (Error)jjte000;
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+/*@egen*/
+}
+
+void angle_addr() :
+{/*@bgen(jjtree) angle_addr */
+ ASTangle_addr jjtn000 = new ASTangle_addr(JJTANGLE_ADDR);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+/*@egen*/}
+{/*@bgen(jjtree) angle_addr */
+ try {
+/*@egen*/
+ "<" [ route() ] addr_spec() ">"/*@bgen(jjtree)*/
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ throw (RuntimeException)jjte000;
+ }
+ if (jjte000 instanceof ParseException) {
+ throw (ParseException)jjte000;
+ }
+ throw (Error)jjte000;
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+/*@egen*/
+}
+
+void route() :
+{/*@bgen(jjtree) route */
+ ASTroute jjtn000 = new ASTroute(JJTROUTE);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+/*@egen*/}
+{/*@bgen(jjtree) route */
+ try {
+/*@egen*/
+ "@" domain() ( (",")* "@" domain() )* ":"/*@bgen(jjtree)*/
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ throw (RuntimeException)jjte000;
+ }
+ if (jjte000 instanceof ParseException) {
+ throw (ParseException)jjte000;
+ }
+ throw (Error)jjte000;
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+/*@egen*/
+}
+
+void phrase() :
+{/*@bgen(jjtree) phrase */
+ ASTphrase jjtn000 = new ASTphrase(JJTPHRASE);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+/*@egen*/}
+{/*@bgen(jjtree) phrase */
+try {
+/*@egen*/
+( <DOTATOM>
+| <QUOTEDSTRING>
+)+/*@bgen(jjtree)*/
+} finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+}
+/*@egen*/
+}
+
+void addr_spec() :
+{/*@bgen(jjtree) addr_spec */
+ ASTaddr_spec jjtn000 = new ASTaddr_spec(JJTADDR_SPEC);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+/*@egen*/}
+{/*@bgen(jjtree) addr_spec */
+ try {
+/*@egen*/
+ ( local_part() "@" domain() )/*@bgen(jjtree)*/
+ } catch (Throwable jjte000) {
+ if (jjtc000) {
+ jjtree.clearNodeScope(jjtn000);
+ jjtc000 = false;
+ } else {
+ jjtree.popNode();
+ }
+ if (jjte000 instanceof RuntimeException) {
+ throw (RuntimeException)jjte000;
+ }
+ if (jjte000 instanceof ParseException) {
+ throw (ParseException)jjte000;
+ }
+ throw (Error)jjte000;
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+/*@egen*/
+}
+
+void local_part() :
+{/*@bgen(jjtree) local_part */
+ ASTlocal_part jjtn000 = new ASTlocal_part(JJTLOCAL_PART);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+/*@egen*/ Token t; }
+{/*@bgen(jjtree) local_part */
+ try {
+/*@egen*/
+ ( t=<DOTATOM> | t=<QUOTEDSTRING> )
+ ( [t="."]
+ {
+ if (t.image.charAt(t.image.length() - 1) != '.' || t.kind == AddressListParserConstants.QUOTEDSTRING)
+ throw new ParseException("Words in local part must be separated by '.'");
+ }
+ ( t=<DOTATOM> | t=<QUOTEDSTRING> )
+ )*/*@bgen(jjtree)*/
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+/*@egen*/
+}
+
+void domain() :
+{/*@bgen(jjtree) domain */
+ ASTdomain jjtn000 = new ASTdomain(JJTDOMAIN);
+ boolean jjtc000 = true;
+ jjtree.openNodeScope(jjtn000);
+ jjtreeOpenNodeScope(jjtn000);
+/*@egen*/ Token t; }
+{/*@bgen(jjtree) domain */
+ try {
+/*@egen*/
+ ( t=<DOTATOM>
+ ( [t="."]
+ {
+ if (t.image.charAt(t.image.length() - 1) != '.')
+ throw new ParseException("Atoms in domain names must be separated by '.'");
+ }
+ t=<DOTATOM>
+ )*
+ )
+| <DOMAINLITERAL>/*@bgen(jjtree)*/
+ } finally {
+ if (jjtc000) {
+ jjtree.closeNodeScope(jjtn000, true);
+ jjtreeCloseNodeScope(jjtn000);
+ }
+ }
+/*@egen*/
+}
+
+SPECIAL_TOKEN :
+{
+ < WS: ( [" ", "\t"] )+ >
+}
+
+TOKEN :
+{
+ < #ALPHA: ["a" - "z", "A" - "Z"] >
+| < #DIGIT: ["0" - "9"] >
+| < #ATEXT: ( <ALPHA> | <DIGIT>
+ | "!" | "#" | "$" | "%"
+ | "&" | "'" | "*" | "+"
+ | "-" | "/" | "=" | "?"
+ | "^" | "_" | "`" | "{"
+ | "|" | "}" | "~"
+ )>
+| < DOTATOM: <ATEXT> ( <ATEXT> | "." )* >
+}
+
+TOKEN_MGR_DECLS :
+{
+ // Keeps track of how many levels of comment nesting
+ // we've encountered. This is only used when the 2nd
+ // level is reached, for example ((this)), not (this).
+ // This is because the outermost level must be treated
+ // specially anyway, because the outermost ")" has a
+ // different token type than inner ")" instances.
+ static int commentNest;
+}
+
+MORE :
+{
+ // domain literal
+ "[" : INDOMAINLITERAL
+}
+
+<INDOMAINLITERAL>
+MORE :
+{
+ < <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
+| < ~["[", "]", "\\"] >
+}
+
+<INDOMAINLITERAL>
+TOKEN :
+{
+ < DOMAINLITERAL: "]" > { matchedToken.image = image.toString(); }: DEFAULT
+}
+
+MORE :
+{
+ // starts a comment
+ "(" : INCOMMENT
+}
+
+<INCOMMENT>
+SKIP :
+{
+ // ends a comment
+ < COMMENT: ")" > : DEFAULT
+ // if this is ever changed to not be a SKIP, need
+ // to make sure matchedToken.token = token.toString()
+ // is called.
+}
+
+<INCOMMENT>
+MORE :
+{
+ < <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
+| "(" { commentNest = 1; } : NESTED_COMMENT
+| < <ANY>>
+}
+
+<NESTED_COMMENT>
+MORE :
+{
+ < <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
+| "(" { ++commentNest; }
+| ")" { --commentNest; if (commentNest == 0) SwitchTo(INCOMMENT); }
+| < <ANY>>
+}
+
+
+// QUOTED STRINGS
+
+MORE :
+{
+ "\"" { image.deleteCharAt(image.length() - 1); } : INQUOTEDSTRING
+}
+
+<INQUOTEDSTRING>
+MORE :
+{
+ < <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
+| < (~["\"", "\\"])+ >
+}
+
+<INQUOTEDSTRING>
+TOKEN :
+{
+ < QUOTEDSTRING: "\"" > { matchedToken.image = image.substring(0, image.length() - 1); } : DEFAULT
+}
+
+// GLOBALS
+
+<*>
+TOKEN :
+{
+ < #QUOTEDPAIR: "\\" <ANY> >
+| < #ANY: ~[] >
+}
+
+// ERROR!
+/*
+
+<*>
+TOKEN :
+{
+ < UNEXPECTED_CHAR: <ANY> >
+}
+
+*/ \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserConstants.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserConstants.java
new file mode 100644
index 000000000..006a082c1
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserConstants.java
@@ -0,0 +1,76 @@
+/* Generated By:JJTree&JavaCC: Do not edit this line. AddressListParserConstants.java */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.address.parser;
+
+public interface AddressListParserConstants {
+
+ int EOF = 0;
+ int WS = 10;
+ int ALPHA = 11;
+ int DIGIT = 12;
+ int ATEXT = 13;
+ int DOTATOM = 14;
+ int DOMAINLITERAL = 18;
+ int COMMENT = 20;
+ int QUOTEDSTRING = 31;
+ int QUOTEDPAIR = 32;
+ int ANY = 33;
+
+ int DEFAULT = 0;
+ int INDOMAINLITERAL = 1;
+ int INCOMMENT = 2;
+ int NESTED_COMMENT = 3;
+ int INQUOTEDSTRING = 4;
+
+ String[] tokenImage = {
+ "<EOF>",
+ "\"\\r\"",
+ "\"\\n\"",
+ "\",\"",
+ "\":\"",
+ "\";\"",
+ "\"<\"",
+ "\">\"",
+ "\"@\"",
+ "\".\"",
+ "<WS>",
+ "<ALPHA>",
+ "<DIGIT>",
+ "<ATEXT>",
+ "<DOTATOM>",
+ "\"[\"",
+ "<token of kind 16>",
+ "<token of kind 17>",
+ "\"]\"",
+ "\"(\"",
+ "\")\"",
+ "<token of kind 21>",
+ "\"(\"",
+ "<token of kind 23>",
+ "<token of kind 24>",
+ "\"(\"",
+ "\")\"",
+ "<token of kind 27>",
+ "\"\\\"\"",
+ "<token of kind 29>",
+ "<token of kind 30>",
+ "\"\\\"\"",
+ "<QUOTEDPAIR>",
+ "<ANY>",
+ };
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserTokenManager.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserTokenManager.java
new file mode 100644
index 000000000..d2dd88dd3
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserTokenManager.java
@@ -0,0 +1,1009 @@
+/* Generated By:JJTree&JavaCC: Do not edit this line. AddressListParserTokenManager.java */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.address.parser;
+
+public class AddressListParserTokenManager implements AddressListParserConstants
+{
+ // Keeps track of how many levels of comment nesting
+ // we've encountered. This is only used when the 2nd
+ // level is reached, for example ((this)), not (this).
+ // This is because the outermost level must be treated
+ // specially anyway, because the outermost ")" has a
+ // different token type than inner ")" instances.
+ static int commentNest;
+ public java.io.PrintStream debugStream = System.out;
+ public void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
+private final int jjStopStringLiteralDfa_0(int pos, long active0)
+{
+ switch (pos)
+ {
+ default :
+ return -1;
+ }
+}
+private final int jjStartNfa_0(int pos, long active0)
+{
+ return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1);
+}
+private final int jjStopAtPos(int pos, int kind)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ return pos + 1;
+}
+private final int jjStartNfaWithStates_0(int pos, int kind, int state)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return pos + 1; }
+ return jjMoveNfa_0(state, pos + 1);
+}
+private final int jjMoveStringLiteralDfa0_0()
+{
+ switch(curChar)
+ {
+ case 10:
+ return jjStopAtPos(0, 2);
+ case 13:
+ return jjStopAtPos(0, 1);
+ case 34:
+ return jjStopAtPos(0, 28);
+ case 40:
+ return jjStopAtPos(0, 19);
+ case 44:
+ return jjStopAtPos(0, 3);
+ case 46:
+ return jjStopAtPos(0, 9);
+ case 58:
+ return jjStopAtPos(0, 4);
+ case 59:
+ return jjStopAtPos(0, 5);
+ case 60:
+ return jjStopAtPos(0, 6);
+ case 62:
+ return jjStopAtPos(0, 7);
+ case 64:
+ return jjStopAtPos(0, 8);
+ case 91:
+ return jjStopAtPos(0, 15);
+ default :
+ return jjMoveNfa_0(1, 0);
+ }
+}
+private final void jjCheckNAdd(int state)
+{
+ if (jjrounds[state] != jjround)
+ {
+ jjstateSet[jjnewStateCnt++] = state;
+ jjrounds[state] = jjround;
+ }
+}
+private final void jjAddStates(int start, int end)
+{
+ do {
+ jjstateSet[jjnewStateCnt++] = jjnextStates[start];
+ } while (start++ != end);
+}
+private final void jjCheckNAddTwoStates(int state1, int state2)
+{
+ jjCheckNAdd(state1);
+ jjCheckNAdd(state2);
+}
+private final void jjCheckNAddStates(int start, int end)
+{
+ do {
+ jjCheckNAdd(jjnextStates[start]);
+ } while (start++ != end);
+}
+private final void jjCheckNAddStates(int start)
+{
+ jjCheckNAdd(jjnextStates[start]);
+ jjCheckNAdd(jjnextStates[start + 1]);
+}
+private final int jjMoveNfa_0(int startState, int curPos)
+{
+ int[] nextStates;
+ int startsAt = 0;
+ jjnewStateCnt = 3;
+ int i = 1;
+ jjstateSet[0] = startState;
+ int j, kind = 0x7fffffff;
+ for (;;)
+ {
+ if (++jjround == 0x7fffffff)
+ ReInitRounds();
+ if (curChar < 64)
+ {
+ long l = 1L << curChar;
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 1:
+ if ((0xa3ffacfa00000000L & l) != 0L)
+ {
+ if (kind > 14)
+ kind = 14;
+ jjCheckNAdd(2);
+ }
+ else if ((0x100000200L & l) != 0L)
+ {
+ if (kind > 10)
+ kind = 10;
+ jjCheckNAdd(0);
+ }
+ break;
+ case 0:
+ if ((0x100000200L & l) == 0L)
+ break;
+ kind = 10;
+ jjCheckNAdd(0);
+ break;
+ case 2:
+ if ((0xa3ffecfa00000000L & l) == 0L)
+ break;
+ if (kind > 14)
+ kind = 14;
+ jjCheckNAdd(2);
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else if (curChar < 128)
+ {
+ long l = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 1:
+ case 2:
+ if ((0x7fffffffc7fffffeL & l) == 0L)
+ break;
+ if (kind > 14)
+ kind = 14;
+ jjCheckNAdd(2);
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else
+ {
+ int i2 = (curChar & 0xff) >> 6;
+ long l2 = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ if (kind != 0x7fffffff)
+ {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+ return curPos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return curPos; }
+ }
+}
+private final int jjStopStringLiteralDfa_2(int pos, long active0)
+{
+ switch (pos)
+ {
+ default :
+ return -1;
+ }
+}
+private final int jjStartNfa_2(int pos, long active0)
+{
+ return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0), pos + 1);
+}
+private final int jjStartNfaWithStates_2(int pos, int kind, int state)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return pos + 1; }
+ return jjMoveNfa_2(state, pos + 1);
+}
+private final int jjMoveStringLiteralDfa0_2()
+{
+ switch(curChar)
+ {
+ case 40:
+ return jjStopAtPos(0, 22);
+ case 41:
+ return jjStopAtPos(0, 20);
+ default :
+ return jjMoveNfa_2(0, 0);
+ }
+}
+static final long[] jjbitVec0 = {
+ 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+private final int jjMoveNfa_2(int startState, int curPos)
+{
+ int[] nextStates;
+ int startsAt = 0;
+ jjnewStateCnt = 3;
+ int i = 1;
+ jjstateSet[0] = startState;
+ int j, kind = 0x7fffffff;
+ for (;;)
+ {
+ if (++jjround == 0x7fffffff)
+ ReInitRounds();
+ if (curChar < 64)
+ {
+ long l = 1L << curChar;
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if (kind > 23)
+ kind = 23;
+ break;
+ case 1:
+ if (kind > 21)
+ kind = 21;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else if (curChar < 128)
+ {
+ long l = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if (kind > 23)
+ kind = 23;
+ if (curChar == 92)
+ jjstateSet[jjnewStateCnt++] = 1;
+ break;
+ case 1:
+ if (kind > 21)
+ kind = 21;
+ break;
+ case 2:
+ if (kind > 23)
+ kind = 23;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else
+ {
+ int i2 = (curChar & 0xff) >> 6;
+ long l2 = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 23)
+ kind = 23;
+ break;
+ case 1:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 21)
+ kind = 21;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ if (kind != 0x7fffffff)
+ {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+ return curPos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return curPos; }
+ }
+}
+private final int jjStopStringLiteralDfa_4(int pos, long active0)
+{
+ switch (pos)
+ {
+ default :
+ return -1;
+ }
+}
+private final int jjStartNfa_4(int pos, long active0)
+{
+ return jjMoveNfa_4(jjStopStringLiteralDfa_4(pos, active0), pos + 1);
+}
+private final int jjStartNfaWithStates_4(int pos, int kind, int state)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return pos + 1; }
+ return jjMoveNfa_4(state, pos + 1);
+}
+private final int jjMoveStringLiteralDfa0_4()
+{
+ switch(curChar)
+ {
+ case 34:
+ return jjStopAtPos(0, 31);
+ default :
+ return jjMoveNfa_4(0, 0);
+ }
+}
+private final int jjMoveNfa_4(int startState, int curPos)
+{
+ int[] nextStates;
+ int startsAt = 0;
+ jjnewStateCnt = 3;
+ int i = 1;
+ jjstateSet[0] = startState;
+ int j, kind = 0x7fffffff;
+ for (;;)
+ {
+ if (++jjround == 0x7fffffff)
+ ReInitRounds();
+ if (curChar < 64)
+ {
+ long l = 1L << curChar;
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ case 2:
+ if ((0xfffffffbffffffffL & l) == 0L)
+ break;
+ if (kind > 30)
+ kind = 30;
+ jjCheckNAdd(2);
+ break;
+ case 1:
+ if (kind > 29)
+ kind = 29;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else if (curChar < 128)
+ {
+ long l = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if ((0xffffffffefffffffL & l) != 0L)
+ {
+ if (kind > 30)
+ kind = 30;
+ jjCheckNAdd(2);
+ }
+ else if (curChar == 92)
+ jjstateSet[jjnewStateCnt++] = 1;
+ break;
+ case 1:
+ if (kind > 29)
+ kind = 29;
+ break;
+ case 2:
+ if ((0xffffffffefffffffL & l) == 0L)
+ break;
+ if (kind > 30)
+ kind = 30;
+ jjCheckNAdd(2);
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else
+ {
+ int i2 = (curChar & 0xff) >> 6;
+ long l2 = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ case 2:
+ if ((jjbitVec0[i2] & l2) == 0L)
+ break;
+ if (kind > 30)
+ kind = 30;
+ jjCheckNAdd(2);
+ break;
+ case 1:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 29)
+ kind = 29;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ if (kind != 0x7fffffff)
+ {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+ return curPos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return curPos; }
+ }
+}
+private final int jjStopStringLiteralDfa_3(int pos, long active0)
+{
+ switch (pos)
+ {
+ default :
+ return -1;
+ }
+}
+private final int jjStartNfa_3(int pos, long active0)
+{
+ return jjMoveNfa_3(jjStopStringLiteralDfa_3(pos, active0), pos + 1);
+}
+private final int jjStartNfaWithStates_3(int pos, int kind, int state)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return pos + 1; }
+ return jjMoveNfa_3(state, pos + 1);
+}
+private final int jjMoveStringLiteralDfa0_3()
+{
+ switch(curChar)
+ {
+ case 40:
+ return jjStopAtPos(0, 25);
+ case 41:
+ return jjStopAtPos(0, 26);
+ default :
+ return jjMoveNfa_3(0, 0);
+ }
+}
+private final int jjMoveNfa_3(int startState, int curPos)
+{
+ int[] nextStates;
+ int startsAt = 0;
+ jjnewStateCnt = 3;
+ int i = 1;
+ jjstateSet[0] = startState;
+ int j, kind = 0x7fffffff;
+ for (;;)
+ {
+ if (++jjround == 0x7fffffff)
+ ReInitRounds();
+ if (curChar < 64)
+ {
+ long l = 1L << curChar;
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if (kind > 27)
+ kind = 27;
+ break;
+ case 1:
+ if (kind > 24)
+ kind = 24;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else if (curChar < 128)
+ {
+ long l = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if (kind > 27)
+ kind = 27;
+ if (curChar == 92)
+ jjstateSet[jjnewStateCnt++] = 1;
+ break;
+ case 1:
+ if (kind > 24)
+ kind = 24;
+ break;
+ case 2:
+ if (kind > 27)
+ kind = 27;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else
+ {
+ int i2 = (curChar & 0xff) >> 6;
+ long l2 = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 27)
+ kind = 27;
+ break;
+ case 1:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 24)
+ kind = 24;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ if (kind != 0x7fffffff)
+ {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+ return curPos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return curPos; }
+ }
+}
+private final int jjStopStringLiteralDfa_1(int pos, long active0)
+{
+ switch (pos)
+ {
+ default :
+ return -1;
+ }
+}
+private final int jjStartNfa_1(int pos, long active0)
+{
+ return jjMoveNfa_1(jjStopStringLiteralDfa_1(pos, active0), pos + 1);
+}
+private final int jjStartNfaWithStates_1(int pos, int kind, int state)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return pos + 1; }
+ return jjMoveNfa_1(state, pos + 1);
+}
+private final int jjMoveStringLiteralDfa0_1()
+{
+ switch(curChar)
+ {
+ case 93:
+ return jjStopAtPos(0, 18);
+ default :
+ return jjMoveNfa_1(0, 0);
+ }
+}
+private final int jjMoveNfa_1(int startState, int curPos)
+{
+ int[] nextStates;
+ int startsAt = 0;
+ jjnewStateCnt = 3;
+ int i = 1;
+ jjstateSet[0] = startState;
+ int j, kind = 0x7fffffff;
+ for (;;)
+ {
+ if (++jjround == 0x7fffffff)
+ ReInitRounds();
+ if (curChar < 64)
+ {
+ long l = 1L << curChar;
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if (kind > 17)
+ kind = 17;
+ break;
+ case 1:
+ if (kind > 16)
+ kind = 16;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else if (curChar < 128)
+ {
+ long l = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if ((0xffffffffc7ffffffL & l) != 0L)
+ {
+ if (kind > 17)
+ kind = 17;
+ }
+ else if (curChar == 92)
+ jjstateSet[jjnewStateCnt++] = 1;
+ break;
+ case 1:
+ if (kind > 16)
+ kind = 16;
+ break;
+ case 2:
+ if ((0xffffffffc7ffffffL & l) != 0L && kind > 17)
+ kind = 17;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else
+ {
+ int i2 = (curChar & 0xff) >> 6;
+ long l2 = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 17)
+ kind = 17;
+ break;
+ case 1:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 16)
+ kind = 16;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ if (kind != 0x7fffffff)
+ {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+ return curPos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return curPos; }
+ }
+}
+static final int[] jjnextStates = {
+};
+public static final String[] jjstrLiteralImages = {
+"", "\15", "\12", "\54", "\72", "\73", "\74", "\76", "\100", "\56", null, null,
+null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+null, null, null, null, null, null, null, null, };
+public static final String[] lexStateNames = {
+ "DEFAULT",
+ "INDOMAINLITERAL",
+ "INCOMMENT",
+ "NESTED_COMMENT",
+ "INQUOTEDSTRING",
+};
+public static final int[] jjnewLexState = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 0, 2, 0, -1, 3, -1, -1,
+ -1, -1, -1, 4, -1, -1, 0, -1, -1,
+};
+static final long[] jjtoToken = {
+ 0x800443ffL,
+};
+static final long[] jjtoSkip = {
+ 0x100400L,
+};
+static final long[] jjtoSpecial = {
+ 0x400L,
+};
+static final long[] jjtoMore = {
+ 0x7feb8000L,
+};
+protected SimpleCharStream input_stream;
+private final int[] jjrounds = new int[3];
+private final int[] jjstateSet = new int[6];
+StringBuffer image;
+int jjimageLen;
+int lengthOfMatch;
+protected char curChar;
+public AddressListParserTokenManager(SimpleCharStream stream){
+ if (SimpleCharStream.staticFlag)
+ throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+ input_stream = stream;
+}
+public AddressListParserTokenManager(SimpleCharStream stream, int lexState){
+ this(stream);
+ SwitchTo(lexState);
+}
+public void ReInit(SimpleCharStream stream)
+{
+ jjmatchedPos = jjnewStateCnt = 0;
+ curLexState = defaultLexState;
+ input_stream = stream;
+ ReInitRounds();
+}
+private final void ReInitRounds()
+{
+ int i;
+ jjround = 0x80000001;
+ for (i = 3; i-- > 0;)
+ jjrounds[i] = 0x80000000;
+}
+public void ReInit(SimpleCharStream stream, int lexState)
+{
+ ReInit(stream);
+ SwitchTo(lexState);
+}
+public void SwitchTo(int lexState)
+{
+ if (lexState >= 5 || lexState < 0)
+ throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE);
+ else
+ curLexState = lexState;
+}
+
+protected Token jjFillToken()
+{
+ Token t = Token.newToken(jjmatchedKind);
+ t.kind = jjmatchedKind;
+ String im = jjstrLiteralImages[jjmatchedKind];
+ t.image = (im == null) ? input_stream.GetImage() : im;
+ t.beginLine = input_stream.getBeginLine();
+ t.beginColumn = input_stream.getBeginColumn();
+ t.endLine = input_stream.getEndLine();
+ t.endColumn = input_stream.getEndColumn();
+ return t;
+}
+
+int curLexState = 0;
+int defaultLexState = 0;
+int jjnewStateCnt;
+int jjround;
+int jjmatchedPos;
+int jjmatchedKind;
+
+public Token getNextToken()
+{
+ int kind;
+ Token specialToken = null;
+ Token matchedToken;
+ int curPos = 0;
+
+ EOFLoop :
+ for (;;)
+ {
+ try
+ {
+ curChar = input_stream.BeginToken();
+ }
+ catch(java.io.IOException e)
+ {
+ jjmatchedKind = 0;
+ matchedToken = jjFillToken();
+ matchedToken.specialToken = specialToken;
+ return matchedToken;
+ }
+ image = null;
+ jjimageLen = 0;
+
+ for (;;)
+ {
+ switch(curLexState)
+ {
+ case 0:
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_0();
+ break;
+ case 1:
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_1();
+ break;
+ case 2:
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_2();
+ break;
+ case 3:
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_3();
+ break;
+ case 4:
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_4();
+ break;
+ }
+ if (jjmatchedKind != 0x7fffffff)
+ {
+ if (jjmatchedPos + 1 < curPos)
+ input_stream.backup(curPos - jjmatchedPos - 1);
+ if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+ {
+ matchedToken = jjFillToken();
+ matchedToken.specialToken = specialToken;
+ TokenLexicalActions(matchedToken);
+ if (jjnewLexState[jjmatchedKind] != -1)
+ curLexState = jjnewLexState[jjmatchedKind];
+ return matchedToken;
+ }
+ else if ((jjtoSkip[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+ {
+ if ((jjtoSpecial[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+ {
+ matchedToken = jjFillToken();
+ if (specialToken == null)
+ specialToken = matchedToken;
+ else
+ {
+ matchedToken.specialToken = specialToken;
+ specialToken = (specialToken.next = matchedToken);
+ }
+ }
+ if (jjnewLexState[jjmatchedKind] != -1)
+ curLexState = jjnewLexState[jjmatchedKind];
+ continue EOFLoop;
+ }
+ MoreLexicalActions();
+ if (jjnewLexState[jjmatchedKind] != -1)
+ curLexState = jjnewLexState[jjmatchedKind];
+ curPos = 0;
+ jjmatchedKind = 0x7fffffff;
+ try {
+ curChar = input_stream.readChar();
+ continue;
+ }
+ catch (java.io.IOException e1) { }
+ }
+ int error_line = input_stream.getEndLine();
+ int error_column = input_stream.getEndColumn();
+ String error_after = null;
+ boolean EOFSeen = false;
+ try { input_stream.readChar(); input_stream.backup(1); }
+ catch (java.io.IOException e1) {
+ EOFSeen = true;
+ error_after = curPos <= 1 ? "" : input_stream.GetImage();
+ if (curChar == '\n' || curChar == '\r') {
+ error_line++;
+ error_column = 0;
+ }
+ else
+ error_column++;
+ }
+ if (!EOFSeen) {
+ input_stream.backup(1);
+ error_after = curPos <= 1 ? "" : input_stream.GetImage();
+ }
+ throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR);
+ }
+ }
+}
+
+void MoreLexicalActions()
+{
+ jjimageLen += (lengthOfMatch = jjmatchedPos + 1);
+ switch(jjmatchedKind)
+ {
+ case 16 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ image.deleteCharAt(image.length() - 2);
+ break;
+ case 21 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ image.deleteCharAt(image.length() - 2);
+ break;
+ case 22 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ commentNest = 1;
+ break;
+ case 24 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ image.deleteCharAt(image.length() - 2);
+ break;
+ case 25 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ ++commentNest;
+ break;
+ case 26 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ --commentNest; if (commentNest == 0) SwitchTo(INCOMMENT);
+ break;
+ case 28 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ image.deleteCharAt(image.length() - 1);
+ break;
+ case 29 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ image.deleteCharAt(image.length() - 2);
+ break;
+ default :
+ break;
+ }
+}
+void TokenLexicalActions(Token matchedToken)
+{
+ switch(jjmatchedKind)
+ {
+ case 18 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
+ matchedToken.image = image.toString();
+ break;
+ case 31 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
+ matchedToken.image = image.substring(0, image.length() - 1);
+ break;
+ default :
+ break;
+ }
+}
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserTreeConstants.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserTreeConstants.java
new file mode 100644
index 000000000..5987f19d8
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserTreeConstants.java
@@ -0,0 +1,35 @@
+/* Generated By:JJTree: Do not edit this line. /Users/jason/Projects/apache-mime4j-0.3/target/generated-sources/jjtree/org/apache/james/mime4j/field/address/parser/AddressListParserTreeConstants.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+public interface AddressListParserTreeConstants
+{
+ public int JJTVOID = 0;
+ public int JJTADDRESS_LIST = 1;
+ public int JJTADDRESS = 2;
+ public int JJTMAILBOX = 3;
+ public int JJTNAME_ADDR = 4;
+ public int JJTGROUP_BODY = 5;
+ public int JJTANGLE_ADDR = 6;
+ public int JJTROUTE = 7;
+ public int JJTPHRASE = 8;
+ public int JJTADDR_SPEC = 9;
+ public int JJTLOCAL_PART = 10;
+ public int JJTDOMAIN = 11;
+
+
+ public String[] jjtNodeName = {
+ "void",
+ "address_list",
+ "address",
+ "mailbox",
+ "name_addr",
+ "group_body",
+ "angle_addr",
+ "route",
+ "phrase",
+ "addr_spec",
+ "local_part",
+ "domain",
+ };
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserVisitor.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserVisitor.java
new file mode 100644
index 000000000..8ec2fe7d2
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/AddressListParserVisitor.java
@@ -0,0 +1,19 @@
+/* Generated By:JJTree: Do not edit this line. /Users/jason/Projects/apache-mime4j-0.3/target/generated-sources/jjtree/org/apache/james/mime4j/field/address/parser/AddressListParserVisitor.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+public interface AddressListParserVisitor
+{
+ public Object visit(SimpleNode node, Object data);
+ public Object visit(ASTaddress_list node, Object data);
+ public Object visit(ASTaddress node, Object data);
+ public Object visit(ASTmailbox node, Object data);
+ public Object visit(ASTname_addr node, Object data);
+ public Object visit(ASTgroup_body node, Object data);
+ public Object visit(ASTangle_addr node, Object data);
+ public Object visit(ASTroute node, Object data);
+ public Object visit(ASTphrase node, Object data);
+ public Object visit(ASTaddr_spec node, Object data);
+ public Object visit(ASTlocal_part node, Object data);
+ public Object visit(ASTdomain node, Object data);
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/BaseNode.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/BaseNode.java
new file mode 100644
index 000000000..780974616
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/BaseNode.java
@@ -0,0 +1,30 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field.address.parser;
+
+import org.apache.james.mime4j.field.address.parser.Node;
+import org.apache.james.mime4j.field.address.parser.Token;
+
+public abstract class BaseNode implements Node {
+
+ public Token firstToken;
+ public Token lastToken;
+
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/JJTAddressListParserState.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/JJTAddressListParserState.java
new file mode 100644
index 000000000..08b5c5bef
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/JJTAddressListParserState.java
@@ -0,0 +1,123 @@
+/* Generated By:JJTree: Do not edit this line. /Users/jason/Projects/apache-mime4j-0.3/target/generated-sources/jjtree/org/apache/james/mime4j/field/address/parser/JJTAddressListParserState.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+class JJTAddressListParserState {
+ private java.util.Stack<Node> nodes;
+ private java.util.Stack<Integer> marks;
+
+ private int sp; // number of nodes on stack
+ private int mk; // current mark
+ private boolean node_created;
+
+ JJTAddressListParserState() {
+ nodes = new java.util.Stack<Node>();
+ marks = new java.util.Stack<Integer>();
+ sp = 0;
+ mk = 0;
+ }
+
+ /* Determines whether the current node was actually closed and
+ pushed. This should only be called in the final user action of a
+ node scope. */
+ boolean nodeCreated() {
+ return node_created;
+ }
+
+ /* Call this to reinitialize the node stack. It is called
+ automatically by the parser's ReInit() method. */
+ void reset() {
+ nodes.removeAllElements();
+ marks.removeAllElements();
+ sp = 0;
+ mk = 0;
+ }
+
+ /* Returns the root node of the AST. It only makes sense to call
+ this after a successful parse. */
+ Node rootNode() {
+ return nodes.elementAt(0);
+ }
+
+ /* Pushes a node on to the stack. */
+ void pushNode(Node n) {
+ nodes.push(n);
+ ++sp;
+ }
+
+ /* Returns the node on the top of the stack, and remove it from the
+ stack. */
+ Node popNode() {
+ if (--sp < mk) {
+ mk = marks.pop().intValue();
+ }
+ return nodes.pop();
+ }
+
+ /* Returns the node currently on the top of the stack. */
+ Node peekNode() {
+ return nodes.peek();
+ }
+
+ /* Returns the number of children on the stack in the current node
+ scope. */
+ int nodeArity() {
+ return sp - mk;
+ }
+
+
+ void clearNodeScope(Node n) {
+ while (sp > mk) {
+ popNode();
+ }
+ mk = marks.pop().intValue();
+ }
+
+
+ void openNodeScope(Node n) {
+ marks.push(new Integer(mk));
+ mk = sp;
+ n.jjtOpen();
+ }
+
+
+ /* A definite node is constructed from a specified number of
+ children. That number of nodes are popped from the stack and
+ made the children of the definite node. Then the definite node
+ is pushed on to the stack. */
+ void closeNodeScope(Node n, int num) {
+ mk = marks.pop().intValue();
+ while (num-- > 0) {
+ Node c = popNode();
+ c.jjtSetParent(n);
+ n.jjtAddChild(c, num);
+ }
+ n.jjtClose();
+ pushNode(n);
+ node_created = true;
+ }
+
+
+ /* A conditional node is constructed if its condition is true. All
+ the nodes that have been pushed since the node was opened are
+ made children of the the conditional node, which is then pushed
+ on to the stack. If the condition is false the node is not
+ constructed and they are left on the stack. */
+ void closeNodeScope(Node n, boolean condition) {
+ if (condition) {
+ int a = nodeArity();
+ mk = marks.pop().intValue();
+ while (a-- > 0) {
+ Node c = popNode();
+ c.jjtSetParent(n);
+ n.jjtAddChild(c, a);
+ }
+ n.jjtClose();
+ pushNode(n);
+ node_created = true;
+ } else {
+ mk = marks.pop().intValue();
+ node_created = false;
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/Node.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/Node.java
new file mode 100644
index 000000000..158892016
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/Node.java
@@ -0,0 +1,37 @@
+/* Generated By:JJTree: Do not edit this line. Node.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+/* All AST nodes must implement this interface. It provides basic
+ machinery for constructing the parent and child relationships
+ between nodes. */
+
+public interface Node {
+
+ /** This method is called after the node has been made the current
+ node. It indicates that child nodes can now be added to it. */
+ public void jjtOpen();
+
+ /** This method is called after all the child nodes have been
+ added. */
+ public void jjtClose();
+
+ /** This pair of methods are used to inform the node of its
+ parent. */
+ public void jjtSetParent(Node n);
+ public Node jjtGetParent();
+
+ /** This method tells the node to add its argument to the node's
+ list of children. */
+ public void jjtAddChild(Node n, int i);
+
+ /** This method returns a child node. The children are numbered
+ from zero, left to right. */
+ public Node jjtGetChild(int i);
+
+ /** Return the number of children the node has. */
+ public int jjtGetNumChildren();
+
+ /** Accept the visitor. **/
+ public Object jjtAccept(AddressListParserVisitor visitor, Object data);
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ParseException.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ParseException.java
new file mode 100644
index 000000000..e20146fb6
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/ParseException.java
@@ -0,0 +1,207 @@
+/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 3.0 */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.address.parser;
+
+/**
+ * This exception is thrown when parse errors are encountered.
+ * You can explicitly create objects of this exception type by
+ * calling the method generateParseException in the generated
+ * parser.
+ *
+ * You can modify this class to customize your error reporting
+ * mechanisms so long as you retain the public fields.
+ */
+public class ParseException extends Exception {
+
+ /**
+ * This constructor is used by the method "generateParseException"
+ * in the generated parser. Calling this constructor generates
+ * a new object of this type with the fields "currentToken",
+ * "expectedTokenSequences", and "tokenImage" set. The boolean
+ * flag "specialConstructor" is also set to true to indicate that
+ * this constructor was used to create this object.
+ * This constructor calls its super class with the empty string
+ * to force the "toString" method of parent class "Throwable" to
+ * print the error message in the form:
+ * ParseException: <result of getMessage>
+ */
+ public ParseException(Token currentTokenVal,
+ int[][] expectedTokenSequencesVal,
+ String[] tokenImageVal
+ )
+ {
+ super("");
+ specialConstructor = true;
+ currentToken = currentTokenVal;
+ expectedTokenSequences = expectedTokenSequencesVal;
+ tokenImage = tokenImageVal;
+ }
+
+ /**
+ * The following constructors are for use by you for whatever
+ * purpose you can think of. Constructing the exception in this
+ * manner makes the exception behave in the normal way - i.e., as
+ * documented in the class "Throwable". The fields "errorToken",
+ * "expectedTokenSequences", and "tokenImage" do not contain
+ * relevant information. The JavaCC generated code does not use
+ * these constructors.
+ */
+
+ public ParseException() {
+ super();
+ specialConstructor = false;
+ }
+
+ public ParseException(String message) {
+ super(message);
+ specialConstructor = false;
+ }
+
+ /**
+ * This variable determines which constructor was used to create
+ * this object and thereby affects the semantics of the
+ * "getMessage" method (see below).
+ */
+ protected boolean specialConstructor;
+
+ /**
+ * This is the last token that has been consumed successfully. If
+ * this object has been created due to a parse error, the token
+ * followng this token will (therefore) be the first error token.
+ */
+ public Token currentToken;
+
+ /**
+ * Each entry in this array is an array of integers. Each array
+ * of integers represents a sequence of tokens (by their ordinal
+ * values) that is expected at this point of the parse.
+ */
+ public int[][] expectedTokenSequences;
+
+ /**
+ * This is a reference to the "tokenImage" array of the generated
+ * parser within which the parse error occurred. This array is
+ * defined in the generated ...Constants interface.
+ */
+ public String[] tokenImage;
+
+ /**
+ * This method has the standard behavior when this object has been
+ * created using the standard constructors. Otherwise, it uses
+ * "currentToken" and "expectedTokenSequences" to generate a parse
+ * error message and returns it. If this object has been created
+ * due to a parse error, and you do not catch it (it gets thrown
+ * from the parser), then this method is called during the printing
+ * of the final stack trace, and hence the correct error message
+ * gets displayed.
+ */
+ public String getMessage() {
+ if (!specialConstructor) {
+ return super.getMessage();
+ }
+ StringBuffer expected = new StringBuffer();
+ int maxSize = 0;
+ for (int i = 0; i < expectedTokenSequences.length; i++) {
+ if (maxSize < expectedTokenSequences[i].length) {
+ maxSize = expectedTokenSequences[i].length;
+ }
+ for (int j = 0; j < expectedTokenSequences[i].length; j++) {
+ expected.append(tokenImage[expectedTokenSequences[i][j]]).append(" ");
+ }
+ if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
+ expected.append("...");
+ }
+ expected.append(eol).append(" ");
+ }
+ String retval = "Encountered \"";
+ Token tok = currentToken.next;
+ for (int i = 0; i < maxSize; i++) {
+ if (i != 0) retval += " ";
+ if (tok.kind == 0) {
+ retval += tokenImage[0];
+ break;
+ }
+ retval += add_escapes(tok.image);
+ tok = tok.next;
+ }
+ retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+ retval += "." + eol;
+ if (expectedTokenSequences.length == 1) {
+ retval += "Was expecting:" + eol + " ";
+ } else {
+ retval += "Was expecting one of:" + eol + " ";
+ }
+ retval += expected.toString();
+ return retval;
+ }
+
+ /**
+ * The end of line string for this machine.
+ */
+ protected String eol = System.getProperty("line.separator", "\n");
+
+ /**
+ * Used to convert raw characters to their escaped version
+ * when these raw version cannot be used as part of an ASCII
+ * string literal.
+ */
+ protected String add_escapes(String str) {
+ StringBuffer retval = new StringBuffer();
+ char ch;
+ for (int i = 0; i < str.length(); i++) {
+ switch (str.charAt(i))
+ {
+ case 0 :
+ continue;
+ case '\b':
+ retval.append("\\b");
+ continue;
+ case '\t':
+ retval.append("\\t");
+ continue;
+ case '\n':
+ retval.append("\\n");
+ continue;
+ case '\f':
+ retval.append("\\f");
+ continue;
+ case '\r':
+ retval.append("\\r");
+ continue;
+ case '\"':
+ retval.append("\\\"");
+ continue;
+ case '\'':
+ retval.append("\\\'");
+ continue;
+ case '\\':
+ retval.append("\\\\");
+ continue;
+ default:
+ if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+ String s = "0000" + Integer.toString(ch, 16);
+ retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+ } else {
+ retval.append(ch);
+ }
+ continue;
+ }
+ }
+ return retval.toString();
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/SimpleCharStream.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/SimpleCharStream.java
new file mode 100644
index 000000000..c9ba0b444
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/SimpleCharStream.java
@@ -0,0 +1,454 @@
+/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 4.0 */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.address.parser;
+
+/**
+ * An implementation of interface CharStream, where the stream is assumed to
+ * contain only ASCII characters (without unicode processing).
+ */
+
+public class SimpleCharStream
+{
+ public static final boolean staticFlag = false;
+ int bufsize;
+ int available;
+ int tokenBegin;
+ public int bufpos = -1;
+ protected int bufline[];
+ protected int bufcolumn[];
+
+ protected int column = 0;
+ protected int line = 1;
+
+ protected boolean prevCharIsCR = false;
+ protected boolean prevCharIsLF = false;
+
+ protected java.io.Reader inputStream;
+
+ protected char[] buffer;
+ protected int maxNextCharInd = 0;
+ protected int inBuf = 0;
+ protected int tabSize = 8;
+
+ protected void setTabSize(int i) { tabSize = i; }
+ protected int getTabSize(int i) { return tabSize; }
+
+
+ protected void ExpandBuff(boolean wrapAround)
+ {
+ char[] newbuffer = new char[bufsize + 2048];
+ int newbufline[] = new int[bufsize + 2048];
+ int newbufcolumn[] = new int[bufsize + 2048];
+
+ try
+ {
+ if (wrapAround)
+ {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ System.arraycopy(buffer, 0, newbuffer,
+ bufsize - tokenBegin, bufpos);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos);
+ bufcolumn = newbufcolumn;
+
+ maxNextCharInd = (bufpos += (bufsize - tokenBegin));
+ }
+ else
+ {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ bufcolumn = newbufcolumn;
+
+ maxNextCharInd = (bufpos -= tokenBegin);
+ }
+ }
+ catch (Throwable t)
+ {
+ throw new Error(t.getMessage());
+ }
+
+
+ bufsize += 2048;
+ available = bufsize;
+ tokenBegin = 0;
+ }
+
+ protected void FillBuff() throws java.io.IOException
+ {
+ if (maxNextCharInd == available)
+ {
+ if (available == bufsize)
+ {
+ if (tokenBegin > 2048)
+ {
+ bufpos = maxNextCharInd = 0;
+ available = tokenBegin;
+ }
+ else if (tokenBegin < 0)
+ bufpos = maxNextCharInd = 0;
+ else
+ ExpandBuff(false);
+ }
+ else if (available > tokenBegin)
+ available = bufsize;
+ else if ((tokenBegin - available) < 2048)
+ ExpandBuff(true);
+ else
+ available = tokenBegin;
+ }
+
+ int i;
+ try {
+ if ((i = inputStream.read(buffer, maxNextCharInd,
+ available - maxNextCharInd)) == -1)
+ {
+ inputStream.close();
+ throw new java.io.IOException();
+ }
+ else
+ maxNextCharInd += i;
+ return;
+ }
+ catch(java.io.IOException e) {
+ --bufpos;
+ backup(0);
+ if (tokenBegin == -1)
+ tokenBegin = bufpos;
+ throw e;
+ }
+ }
+
+ public char BeginToken() throws java.io.IOException
+ {
+ tokenBegin = -1;
+ char c = readChar();
+ tokenBegin = bufpos;
+
+ return c;
+ }
+
+ protected void UpdateLineColumn(char c)
+ {
+ column++;
+
+ if (prevCharIsLF)
+ {
+ prevCharIsLF = false;
+ line += (column = 1);
+ }
+ else if (prevCharIsCR)
+ {
+ prevCharIsCR = false;
+ if (c == '\n')
+ {
+ prevCharIsLF = true;
+ }
+ else
+ line += (column = 1);
+ }
+
+ switch (c)
+ {
+ case '\r' :
+ prevCharIsCR = true;
+ break;
+ case '\n' :
+ prevCharIsLF = true;
+ break;
+ case '\t' :
+ column--;
+ column += (tabSize - (column % tabSize));
+ break;
+ default :
+ break;
+ }
+
+ bufline[bufpos] = line;
+ bufcolumn[bufpos] = column;
+ }
+
+ public char readChar() throws java.io.IOException
+ {
+ if (inBuf > 0)
+ {
+ --inBuf;
+
+ if (++bufpos == bufsize)
+ bufpos = 0;
+
+ return buffer[bufpos];
+ }
+
+ if (++bufpos >= maxNextCharInd)
+ FillBuff();
+
+ char c = buffer[bufpos];
+
+ UpdateLineColumn(c);
+ return (c);
+ }
+
+ /**
+ * @deprecated
+ * @see #getEndColumn
+ */
+ @Deprecated
+ public int getColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ /**
+ * @deprecated
+ * @see #getEndLine
+ */
+ @Deprecated
+ public int getLine() {
+ return bufline[bufpos];
+ }
+
+ public int getEndColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ public int getEndLine() {
+ return bufline[bufpos];
+ }
+
+ public int getBeginColumn() {
+ return bufcolumn[tokenBegin];
+ }
+
+ public int getBeginLine() {
+ return bufline[tokenBegin];
+ }
+
+ public void backup(int amount) {
+
+ inBuf += amount;
+ if ((bufpos -= amount) < 0)
+ bufpos += bufsize;
+ }
+
+ public SimpleCharStream(java.io.Reader dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ inputStream = dstream;
+ line = startline;
+ column = startcolumn - 1;
+
+ available = bufsize = buffersize;
+ buffer = new char[buffersize];
+ bufline = new int[buffersize];
+ bufcolumn = new int[buffersize];
+ }
+
+ public SimpleCharStream(java.io.Reader dstream, int startline,
+ int startcolumn)
+ {
+ this(dstream, startline, startcolumn, 4096);
+ }
+
+ public SimpleCharStream(java.io.Reader dstream)
+ {
+ this(dstream, 1, 1, 4096);
+ }
+ public void ReInit(java.io.Reader dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ inputStream = dstream;
+ line = startline;
+ column = startcolumn - 1;
+
+ if (buffer == null || buffersize != buffer.length)
+ {
+ available = bufsize = buffersize;
+ buffer = new char[buffersize];
+ bufline = new int[buffersize];
+ bufcolumn = new int[buffersize];
+ }
+ prevCharIsLF = prevCharIsCR = false;
+ tokenBegin = inBuf = maxNextCharInd = 0;
+ bufpos = -1;
+ }
+
+ public void ReInit(java.io.Reader dstream, int startline,
+ int startcolumn)
+ {
+ ReInit(dstream, startline, startcolumn, 4096);
+ }
+
+ public void ReInit(java.io.Reader dstream)
+ {
+ ReInit(dstream, 1, 1, 4096);
+ }
+ public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+ {
+ this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+ }
+
+ public SimpleCharStream(java.io.InputStream dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+ }
+
+ public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn) throws java.io.UnsupportedEncodingException
+ {
+ this(dstream, encoding, startline, startcolumn, 4096);
+ }
+
+ public SimpleCharStream(java.io.InputStream dstream, int startline,
+ int startcolumn)
+ {
+ this(dstream, startline, startcolumn, 4096);
+ }
+
+ public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+ {
+ this(dstream, encoding, 1, 1, 4096);
+ }
+
+ public SimpleCharStream(java.io.InputStream dstream)
+ {
+ this(dstream, 1, 1, 4096);
+ }
+
+ public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+ {
+ ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+ }
+
+ public void ReInit(java.io.InputStream dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+ }
+
+ public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+ {
+ ReInit(dstream, encoding, 1, 1, 4096);
+ }
+
+ public void ReInit(java.io.InputStream dstream)
+ {
+ ReInit(dstream, 1, 1, 4096);
+ }
+ public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn) throws java.io.UnsupportedEncodingException
+ {
+ ReInit(dstream, encoding, startline, startcolumn, 4096);
+ }
+ public void ReInit(java.io.InputStream dstream, int startline,
+ int startcolumn)
+ {
+ ReInit(dstream, startline, startcolumn, 4096);
+ }
+ public String GetImage()
+ {
+ if (bufpos >= tokenBegin)
+ return new String(buffer, tokenBegin, bufpos - tokenBegin + 1);
+ else
+ return new String(buffer, tokenBegin, bufsize - tokenBegin) +
+ new String(buffer, 0, bufpos + 1);
+ }
+
+ public char[] GetSuffix(int len)
+ {
+ char[] ret = new char[len];
+
+ if ((bufpos + 1) >= len)
+ System.arraycopy(buffer, bufpos - len + 1, ret, 0, len);
+ else
+ {
+ System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0,
+ len - bufpos - 1);
+ System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1);
+ }
+
+ return ret;
+ }
+
+ public void Done()
+ {
+ buffer = null;
+ bufline = null;
+ bufcolumn = null;
+ }
+
+ /**
+ * Method to adjust line and column numbers for the start of a token.
+ */
+ public void adjustBeginLineColumn(int newLine, int newCol)
+ {
+ int start = tokenBegin;
+ int len;
+
+ if (bufpos >= tokenBegin)
+ {
+ len = bufpos - tokenBegin + inBuf + 1;
+ }
+ else
+ {
+ len = bufsize - tokenBegin + bufpos + 1 + inBuf;
+ }
+
+ int i = 0, j = 0, k = 0;
+ int nextColDiff = 0, columnDiff = 0;
+
+ while (i < len &&
+ bufline[j = start % bufsize] == bufline[k = ++start % bufsize])
+ {
+ bufline[j] = newLine;
+ nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j];
+ bufcolumn[j] = newCol + columnDiff;
+ columnDiff = nextColDiff;
+ i++;
+ }
+
+ if (i < len)
+ {
+ bufline[j] = newLine++;
+ bufcolumn[j] = newCol + columnDiff;
+
+ while (i++ < len)
+ {
+ if (bufline[j = start % bufsize] != bufline[++start % bufsize])
+ bufline[j] = newLine++;
+ else
+ bufline[j] = newLine;
+ }
+ }
+
+ line = bufline[j];
+ column = bufcolumn[j];
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/SimpleNode.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/SimpleNode.java
new file mode 100644
index 000000000..9bf537e60
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/SimpleNode.java
@@ -0,0 +1,87 @@
+/* Generated By:JJTree: Do not edit this line. SimpleNode.java */
+
+package org.apache.james.mime4j.field.address.parser;
+
+public class SimpleNode extends org.apache.james.mime4j.field.address.parser.BaseNode implements Node {
+ protected Node parent;
+ protected Node[] children;
+ protected int id;
+ protected AddressListParser parser;
+
+ public SimpleNode(int i) {
+ id = i;
+ }
+
+ public SimpleNode(AddressListParser p, int i) {
+ this(i);
+ parser = p;
+ }
+
+ public void jjtOpen() {
+ }
+
+ public void jjtClose() {
+ }
+
+ public void jjtSetParent(Node n) { parent = n; }
+ public Node jjtGetParent() { return parent; }
+
+ public void jjtAddChild(Node n, int i) {
+ if (children == null) {
+ children = new Node[i + 1];
+ } else if (i >= children.length) {
+ Node c[] = new Node[i + 1];
+ System.arraycopy(children, 0, c, 0, children.length);
+ children = c;
+ }
+ children[i] = n;
+ }
+
+ public Node jjtGetChild(int i) {
+ return children[i];
+ }
+
+ public int jjtGetNumChildren() {
+ return (children == null) ? 0 : children.length;
+ }
+
+ /** Accept the visitor. **/
+ public Object jjtAccept(AddressListParserVisitor visitor, Object data) {
+ return visitor.visit(this, data);
+ }
+
+ /** Accept the visitor. **/
+ public Object childrenAccept(AddressListParserVisitor visitor, Object data) {
+ if (children != null) {
+ for (int i = 0; i < children.length; ++i) {
+ children[i].jjtAccept(visitor, data);
+ }
+ }
+ return data;
+ }
+
+ /* You can override these two methods in subclasses of SimpleNode to
+ customize the way the node appears when the tree is dumped. If
+ your output uses more than one line you should override
+ toString(String), otherwise overriding toString() is probably all
+ you need to do. */
+
+ public String toString() { return AddressListParserTreeConstants.jjtNodeName[id]; }
+ public String toString(String prefix) { return prefix + toString(); }
+
+ /* Override this method if you want to customize how the node dumps
+ out its children. */
+
+ public void dump(String prefix) {
+ System.out.println(toString(prefix));
+ if (children != null) {
+ for (int i = 0; i < children.length; ++i) {
+ SimpleNode n = (SimpleNode)children[i];
+ if (n != null) {
+ n.dump(prefix + " ");
+ }
+ }
+ }
+ }
+}
+
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/Token.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/Token.java
new file mode 100644
index 000000000..2382e8e92
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/Token.java
@@ -0,0 +1,96 @@
+/* Generated By:JavaCC: Do not edit this line. Token.java Version 3.0 */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.address.parser;
+
+/**
+ * Describes the input token stream.
+ */
+
+public class Token {
+
+ /**
+ * An integer that describes the kind of this token. This numbering
+ * system is determined by JavaCCParser, and a table of these numbers is
+ * stored in the file ...Constants.java.
+ */
+ public int kind;
+
+ /**
+ * beginLine and beginColumn describe the position of the first character
+ * of this token; endLine and endColumn describe the position of the
+ * last character of this token.
+ */
+ public int beginLine, beginColumn, endLine, endColumn;
+
+ /**
+ * The string image of the token.
+ */
+ public String image;
+
+ /**
+ * A reference to the next regular (non-special) token from the input
+ * stream. If this is the last token from the input stream, or if the
+ * token manager has not read tokens beyond this one, this field is
+ * set to null. This is true only if this token is also a regular
+ * token. Otherwise, see below for a description of the contents of
+ * this field.
+ */
+ public Token next;
+
+ /**
+ * This field is used to access special tokens that occur prior to this
+ * token, but after the immediately preceding regular (non-special) token.
+ * If there are no such special tokens, this field is set to null.
+ * When there are more than one such special token, this field refers
+ * to the last of these special tokens, which in turn refers to the next
+ * previous special token through its specialToken field, and so on
+ * until the first special token (whose specialToken field is null).
+ * The next fields of special tokens refer to other special tokens that
+ * immediately follow it (without an intervening regular token). If there
+ * is no such token, this field is null.
+ */
+ public Token specialToken;
+
+ /**
+ * Returns the image.
+ */
+ public String toString()
+ {
+ return image;
+ }
+
+ /**
+ * Returns a new Token object, by default. However, if you want, you
+ * can create and return subclass objects based on the value of ofKind.
+ * Simply add the cases to the switch for all those special cases.
+ * For example, if you have a subclass of Token called IDToken that
+ * you want to create if ofKind is ID, simlpy add something like :
+ *
+ * case MyParserConstants.ID : return new IDToken();
+ *
+ * to the following switch statement. Then you can cast matchedToken
+ * variable to the appropriate type and use it in your lexical actions.
+ */
+ public static final Token newToken(int ofKind)
+ {
+ switch(ofKind)
+ {
+ default : return new Token();
+ }
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/TokenMgrError.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/TokenMgrError.java
new file mode 100644
index 000000000..0299c8523
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/address/parser/TokenMgrError.java
@@ -0,0 +1,148 @@
+/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 3.0 */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.address.parser;
+
+public class TokenMgrError extends Error
+{
+ /*
+ * Ordinals for various reasons why an Error of this type can be thrown.
+ */
+
+ /**
+ * Lexical error occured.
+ */
+ static final int LEXICAL_ERROR = 0;
+
+ /**
+ * An attempt wass made to create a second instance of a static token manager.
+ */
+ static final int STATIC_LEXER_ERROR = 1;
+
+ /**
+ * Tried to change to an invalid lexical state.
+ */
+ static final int INVALID_LEXICAL_STATE = 2;
+
+ /**
+ * Detected (and bailed out of) an infinite loop in the token manager.
+ */
+ static final int LOOP_DETECTED = 3;
+
+ /**
+ * Indicates the reason why the exception is thrown. It will have
+ * one of the above 4 values.
+ */
+ int errorCode;
+
+ /**
+ * Replaces unprintable characters by their espaced (or unicode escaped)
+ * equivalents in the given string
+ */
+ protected static final String addEscapes(String str) {
+ StringBuffer retval = new StringBuffer();
+ char ch;
+ for (int i = 0; i < str.length(); i++) {
+ switch (str.charAt(i))
+ {
+ case 0 :
+ continue;
+ case '\b':
+ retval.append("\\b");
+ continue;
+ case '\t':
+ retval.append("\\t");
+ continue;
+ case '\n':
+ retval.append("\\n");
+ continue;
+ case '\f':
+ retval.append("\\f");
+ continue;
+ case '\r':
+ retval.append("\\r");
+ continue;
+ case '\"':
+ retval.append("\\\"");
+ continue;
+ case '\'':
+ retval.append("\\\'");
+ continue;
+ case '\\':
+ retval.append("\\\\");
+ continue;
+ default:
+ if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+ String s = "0000" + Integer.toString(ch, 16);
+ retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+ } else {
+ retval.append(ch);
+ }
+ continue;
+ }
+ }
+ return retval.toString();
+ }
+
+ /**
+ * Returns a detailed message for the Error when it is thrown by the
+ * token manager to indicate a lexical error.
+ * Parameters :
+ * EOFSeen : indicates if EOF caused the lexicl error
+ * curLexState : lexical state in which this error occured
+ * errorLine : line number when the error occured
+ * errorColumn : column number when the error occured
+ * errorAfter : prefix that was seen before this error occured
+ * curchar : the offending character
+ * Note: You can customize the lexical error message by modifying this method.
+ */
+ protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) {
+ return("Lexical error at line " +
+ errorLine + ", column " +
+ errorColumn + ". Encountered: " +
+ (EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") +
+ "after : \"" + addEscapes(errorAfter) + "\"");
+ }
+
+ /**
+ * You can also modify the body of this method to customize your error messages.
+ * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not
+ * of end-users concern, so you can return something like :
+ *
+ * "Internal Error : Please file a bug report .... "
+ *
+ * from this method for such cases in the release version of your parser.
+ */
+ public String getMessage() {
+ return super.getMessage();
+ }
+
+ /*
+ * Constructors of various flavors follow.
+ */
+
+ public TokenMgrError() {
+ }
+
+ public TokenMgrError(String message, int reason) {
+ super(message);
+ errorCode = reason;
+ }
+
+ public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) {
+ this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParser.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParser.java
new file mode 100644
index 000000000..cacf3af21
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParser.java
@@ -0,0 +1,268 @@
+/* Generated By:JavaCC: Do not edit this line. ContentTypeParser.java */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.contenttype.parser;
+
+import java.util.ArrayList;
+import java.util.Vector;
+
+public class ContentTypeParser implements ContentTypeParserConstants {
+
+ private String type;
+ private String subtype;
+ private ArrayList<String> paramNames = new ArrayList<String>();
+ private ArrayList<String> paramValues = new ArrayList<String>();
+
+ public String getType() { return type; }
+ public String getSubType() { return subtype; }
+ public ArrayList<String> getParamNames() { return paramNames; }
+ public ArrayList<String> getParamValues() { return paramValues; }
+
+ public static void main(String args[]) throws ParseException {
+ while (true) {
+ try {
+ ContentTypeParser parser = new ContentTypeParser(System.in);
+ parser.parseLine();
+ } catch (Exception x) {
+ x.printStackTrace();
+ return;
+ }
+ }
+ }
+
+ final public void parseLine() throws ParseException {
+ parse();
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 1:
+ jj_consume_token(1);
+ break;
+ default:
+ jj_la1[0] = jj_gen;
+ ;
+ }
+ jj_consume_token(2);
+ }
+
+ final public void parseAll() throws ParseException {
+ parse();
+ jj_consume_token(0);
+ }
+
+ final public void parse() throws ParseException {
+ Token type;
+ Token subtype;
+ type = jj_consume_token(ATOKEN);
+ jj_consume_token(3);
+ subtype = jj_consume_token(ATOKEN);
+ this.type = type.image;
+ this.subtype = subtype.image;
+ label_1:
+ while (true) {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 4:
+ ;
+ break;
+ default:
+ jj_la1[1] = jj_gen;
+ break label_1;
+ }
+ jj_consume_token(4);
+ parameter();
+ }
+ }
+
+ final public void parameter() throws ParseException {
+ Token attrib;
+ String val;
+ attrib = jj_consume_token(ATOKEN);
+ jj_consume_token(5);
+ val = value();
+ paramNames.add(attrib.image);
+ paramValues.add(val);
+ }
+
+ final public String value() throws ParseException {
+ Token t;
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case ATOKEN:
+ t = jj_consume_token(ATOKEN);
+ break;
+ case QUOTEDSTRING:
+ t = jj_consume_token(QUOTEDSTRING);
+ break;
+ default:
+ jj_la1[2] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {if (true) return t.image;}
+ throw new Error("Missing return statement in function");
+ }
+
+ public ContentTypeParserTokenManager token_source;
+ SimpleCharStream jj_input_stream;
+ public Token token, jj_nt;
+ private int jj_ntk;
+ private int jj_gen;
+ final private int[] jj_la1 = new int[3];
+ static private int[] jj_la1_0;
+ static {
+ jj_la1_0();
+ }
+ private static void jj_la1_0() {
+ jj_la1_0 = new int[] {0x2,0x10,0x280000,};
+ }
+
+ public ContentTypeParser(java.io.InputStream stream) {
+ this(stream, null);
+ }
+ public ContentTypeParser(java.io.InputStream stream, String encoding) {
+ try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+ token_source = new ContentTypeParserTokenManager(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 3; i++) jj_la1[i] = -1;
+ }
+
+ public void ReInit(java.io.InputStream stream) {
+ ReInit(stream, null);
+ }
+ public void ReInit(java.io.InputStream stream, String encoding) {
+ try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+ token_source.ReInit(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 3; i++) jj_la1[i] = -1;
+ }
+
+ public ContentTypeParser(java.io.Reader stream) {
+ jj_input_stream = new SimpleCharStream(stream, 1, 1);
+ token_source = new ContentTypeParserTokenManager(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 3; i++) jj_la1[i] = -1;
+ }
+
+ public void ReInit(java.io.Reader stream) {
+ jj_input_stream.ReInit(stream, 1, 1);
+ token_source.ReInit(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 3; i++) jj_la1[i] = -1;
+ }
+
+ public ContentTypeParser(ContentTypeParserTokenManager tm) {
+ token_source = tm;
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 3; i++) jj_la1[i] = -1;
+ }
+
+ public void ReInit(ContentTypeParserTokenManager tm) {
+ token_source = tm;
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 3; i++) jj_la1[i] = -1;
+ }
+
+ final private Token jj_consume_token(int kind) throws ParseException {
+ Token oldToken;
+ if ((oldToken = token).next != null) token = token.next;
+ else token = token.next = token_source.getNextToken();
+ jj_ntk = -1;
+ if (token.kind == kind) {
+ jj_gen++;
+ return token;
+ }
+ token = oldToken;
+ jj_kind = kind;
+ throw generateParseException();
+ }
+
+ final public Token getNextToken() {
+ if (token.next != null) token = token.next;
+ else token = token.next = token_source.getNextToken();
+ jj_ntk = -1;
+ jj_gen++;
+ return token;
+ }
+
+ final public Token getToken(int index) {
+ Token t = token;
+ for (int i = 0; i < index; i++) {
+ if (t.next != null) t = t.next;
+ else t = t.next = token_source.getNextToken();
+ }
+ return t;
+ }
+
+ final private int jj_ntk() {
+ if ((jj_nt=token.next) == null)
+ return (jj_ntk = (token.next=token_source.getNextToken()).kind);
+ else
+ return (jj_ntk = jj_nt.kind);
+ }
+
+ private Vector<int[]> jj_expentries = new Vector<int[]>();
+ private int[] jj_expentry;
+ private int jj_kind = -1;
+
+ public ParseException generateParseException() {
+ jj_expentries.removeAllElements();
+ boolean[] la1tokens = new boolean[24];
+ for (int i = 0; i < 24; i++) {
+ la1tokens[i] = false;
+ }
+ if (jj_kind >= 0) {
+ la1tokens[jj_kind] = true;
+ jj_kind = -1;
+ }
+ for (int i = 0; i < 3; i++) {
+ if (jj_la1[i] == jj_gen) {
+ for (int j = 0; j < 32; j++) {
+ if ((jj_la1_0[i] & (1<<j)) != 0) {
+ la1tokens[j] = true;
+ }
+ }
+ }
+ }
+ for (int i = 0; i < 24; i++) {
+ if (la1tokens[i]) {
+ jj_expentry = new int[1];
+ jj_expentry[0] = i;
+ jj_expentries.addElement(jj_expentry);
+ }
+ }
+ int[][] exptokseq = new int[jj_expentries.size()][];
+ for (int i = 0; i < jj_expentries.size(); i++) {
+ exptokseq[i] = jj_expentries.elementAt(i);
+ }
+ return new ParseException(token, exptokseq, tokenImage);
+ }
+
+ final public void enable_tracing() {
+ }
+
+ final public void disable_tracing() {
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserConstants.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserConstants.java
new file mode 100644
index 000000000..d933d800d
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserConstants.java
@@ -0,0 +1,62 @@
+/* Generated By:JavaCC: Do not edit this line. ContentTypeParserConstants.java */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.contenttype.parser;
+
+public interface ContentTypeParserConstants {
+
+ int EOF = 0;
+ int WS = 6;
+ int COMMENT = 8;
+ int QUOTEDSTRING = 19;
+ int DIGITS = 20;
+ int ATOKEN = 21;
+ int QUOTEDPAIR = 22;
+ int ANY = 23;
+
+ int DEFAULT = 0;
+ int INCOMMENT = 1;
+ int NESTED_COMMENT = 2;
+ int INQUOTEDSTRING = 3;
+
+ String[] tokenImage = {
+ "<EOF>",
+ "\"\\r\"",
+ "\"\\n\"",
+ "\"/\"",
+ "\";\"",
+ "\"=\"",
+ "<WS>",
+ "\"(\"",
+ "\")\"",
+ "<token of kind 9>",
+ "\"(\"",
+ "<token of kind 11>",
+ "<token of kind 12>",
+ "\"(\"",
+ "\")\"",
+ "<token of kind 15>",
+ "\"\\\"\"",
+ "<token of kind 17>",
+ "<token of kind 18>",
+ "\"\\\"\"",
+ "<DIGITS>",
+ "<ATOKEN>",
+ "<QUOTEDPAIR>",
+ "<ANY>",
+ };
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserTokenManager.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserTokenManager.java
new file mode 100644
index 000000000..25b7abafa
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ContentTypeParserTokenManager.java
@@ -0,0 +1,877 @@
+/* Generated By:JavaCC: Do not edit this line. ContentTypeParserTokenManager.java */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.contenttype.parser;
+import java.util.ArrayList;
+
+public class ContentTypeParserTokenManager implements ContentTypeParserConstants
+{
+ // Keeps track of how many levels of comment nesting
+ // we've encountered. This is only used when the 2nd
+ // level is reached, for example ((this)), not (this).
+ // This is because the outermost level must be treated
+ // specially anyway, because the outermost ")" has a
+ // different token type than inner ")" instances.
+ static int commentNest;
+ public java.io.PrintStream debugStream = System.out;
+ public void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
+private final int jjStopStringLiteralDfa_0(int pos, long active0)
+{
+ switch (pos)
+ {
+ default :
+ return -1;
+ }
+}
+private final int jjStartNfa_0(int pos, long active0)
+{
+ return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1);
+}
+private final int jjStopAtPos(int pos, int kind)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ return pos + 1;
+}
+private final int jjStartNfaWithStates_0(int pos, int kind, int state)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return pos + 1; }
+ return jjMoveNfa_0(state, pos + 1);
+}
+private final int jjMoveStringLiteralDfa0_0()
+{
+ switch(curChar)
+ {
+ case 10:
+ return jjStartNfaWithStates_0(0, 2, 2);
+ case 13:
+ return jjStartNfaWithStates_0(0, 1, 2);
+ case 34:
+ return jjStopAtPos(0, 16);
+ case 40:
+ return jjStopAtPos(0, 7);
+ case 47:
+ return jjStopAtPos(0, 3);
+ case 59:
+ return jjStopAtPos(0, 4);
+ case 61:
+ return jjStopAtPos(0, 5);
+ default :
+ return jjMoveNfa_0(3, 0);
+ }
+}
+private final void jjCheckNAdd(int state)
+{
+ if (jjrounds[state] != jjround)
+ {
+ jjstateSet[jjnewStateCnt++] = state;
+ jjrounds[state] = jjround;
+ }
+}
+private final void jjAddStates(int start, int end)
+{
+ do {
+ jjstateSet[jjnewStateCnt++] = jjnextStates[start];
+ } while (start++ != end);
+}
+private final void jjCheckNAddTwoStates(int state1, int state2)
+{
+ jjCheckNAdd(state1);
+ jjCheckNAdd(state2);
+}
+private final void jjCheckNAddStates(int start, int end)
+{
+ do {
+ jjCheckNAdd(jjnextStates[start]);
+ } while (start++ != end);
+}
+private final void jjCheckNAddStates(int start)
+{
+ jjCheckNAdd(jjnextStates[start]);
+ jjCheckNAdd(jjnextStates[start + 1]);
+}
+static final long[] jjbitVec0 = {
+ 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+private final int jjMoveNfa_0(int startState, int curPos)
+{
+ int[] nextStates;
+ int startsAt = 0;
+ jjnewStateCnt = 3;
+ int i = 1;
+ jjstateSet[0] = startState;
+ int j, kind = 0x7fffffff;
+ for (;;)
+ {
+ if (++jjround == 0x7fffffff)
+ ReInitRounds();
+ if (curChar < 64)
+ {
+ long l = 1L << curChar;
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 3:
+ if ((0x3ff6cfafffffdffL & l) != 0L)
+ {
+ if (kind > 21)
+ kind = 21;
+ jjCheckNAdd(2);
+ }
+ else if ((0x100000200L & l) != 0L)
+ {
+ if (kind > 6)
+ kind = 6;
+ jjCheckNAdd(0);
+ }
+ if ((0x3ff000000000000L & l) != 0L)
+ {
+ if (kind > 20)
+ kind = 20;
+ jjCheckNAdd(1);
+ }
+ break;
+ case 0:
+ if ((0x100000200L & l) == 0L)
+ break;
+ kind = 6;
+ jjCheckNAdd(0);
+ break;
+ case 1:
+ if ((0x3ff000000000000L & l) == 0L)
+ break;
+ if (kind > 20)
+ kind = 20;
+ jjCheckNAdd(1);
+ break;
+ case 2:
+ if ((0x3ff6cfafffffdffL & l) == 0L)
+ break;
+ if (kind > 21)
+ kind = 21;
+ jjCheckNAdd(2);
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else if (curChar < 128)
+ {
+ long l = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 3:
+ case 2:
+ if ((0xffffffffc7fffffeL & l) == 0L)
+ break;
+ kind = 21;
+ jjCheckNAdd(2);
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else
+ {
+ int i2 = (curChar & 0xff) >> 6;
+ long l2 = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 3:
+ case 2:
+ if ((jjbitVec0[i2] & l2) == 0L)
+ break;
+ if (kind > 21)
+ kind = 21;
+ jjCheckNAdd(2);
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ if (kind != 0x7fffffff)
+ {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+ return curPos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return curPos; }
+ }
+}
+private final int jjStopStringLiteralDfa_1(int pos, long active0)
+{
+ switch (pos)
+ {
+ default :
+ return -1;
+ }
+}
+private final int jjStartNfa_1(int pos, long active0)
+{
+ return jjMoveNfa_1(jjStopStringLiteralDfa_1(pos, active0), pos + 1);
+}
+private final int jjStartNfaWithStates_1(int pos, int kind, int state)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return pos + 1; }
+ return jjMoveNfa_1(state, pos + 1);
+}
+private final int jjMoveStringLiteralDfa0_1()
+{
+ switch(curChar)
+ {
+ case 40:
+ return jjStopAtPos(0, 10);
+ case 41:
+ return jjStopAtPos(0, 8);
+ default :
+ return jjMoveNfa_1(0, 0);
+ }
+}
+private final int jjMoveNfa_1(int startState, int curPos)
+{
+ int[] nextStates;
+ int startsAt = 0;
+ jjnewStateCnt = 3;
+ int i = 1;
+ jjstateSet[0] = startState;
+ int j, kind = 0x7fffffff;
+ for (;;)
+ {
+ if (++jjround == 0x7fffffff)
+ ReInitRounds();
+ if (curChar < 64)
+ {
+ long l = 1L << curChar;
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if (kind > 11)
+ kind = 11;
+ break;
+ case 1:
+ if (kind > 9)
+ kind = 9;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else if (curChar < 128)
+ {
+ long l = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if (kind > 11)
+ kind = 11;
+ if (curChar == 92)
+ jjstateSet[jjnewStateCnt++] = 1;
+ break;
+ case 1:
+ if (kind > 9)
+ kind = 9;
+ break;
+ case 2:
+ if (kind > 11)
+ kind = 11;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else
+ {
+ int i2 = (curChar & 0xff) >> 6;
+ long l2 = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 11)
+ kind = 11;
+ break;
+ case 1:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 9)
+ kind = 9;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ if (kind != 0x7fffffff)
+ {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+ return curPos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return curPos; }
+ }
+}
+private final int jjStopStringLiteralDfa_3(int pos, long active0)
+{
+ switch (pos)
+ {
+ default :
+ return -1;
+ }
+}
+private final int jjStartNfa_3(int pos, long active0)
+{
+ return jjMoveNfa_3(jjStopStringLiteralDfa_3(pos, active0), pos + 1);
+}
+private final int jjStartNfaWithStates_3(int pos, int kind, int state)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return pos + 1; }
+ return jjMoveNfa_3(state, pos + 1);
+}
+private final int jjMoveStringLiteralDfa0_3()
+{
+ switch(curChar)
+ {
+ case 34:
+ return jjStopAtPos(0, 19);
+ default :
+ return jjMoveNfa_3(0, 0);
+ }
+}
+private final int jjMoveNfa_3(int startState, int curPos)
+{
+ int[] nextStates;
+ int startsAt = 0;
+ jjnewStateCnt = 3;
+ int i = 1;
+ jjstateSet[0] = startState;
+ int j, kind = 0x7fffffff;
+ for (;;)
+ {
+ if (++jjround == 0x7fffffff)
+ ReInitRounds();
+ if (curChar < 64)
+ {
+ long l = 1L << curChar;
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ case 2:
+ if ((0xfffffffbffffffffL & l) == 0L)
+ break;
+ if (kind > 18)
+ kind = 18;
+ jjCheckNAdd(2);
+ break;
+ case 1:
+ if (kind > 17)
+ kind = 17;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else if (curChar < 128)
+ {
+ long l = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if ((0xffffffffefffffffL & l) != 0L)
+ {
+ if (kind > 18)
+ kind = 18;
+ jjCheckNAdd(2);
+ }
+ else if (curChar == 92)
+ jjstateSet[jjnewStateCnt++] = 1;
+ break;
+ case 1:
+ if (kind > 17)
+ kind = 17;
+ break;
+ case 2:
+ if ((0xffffffffefffffffL & l) == 0L)
+ break;
+ if (kind > 18)
+ kind = 18;
+ jjCheckNAdd(2);
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else
+ {
+ int i2 = (curChar & 0xff) >> 6;
+ long l2 = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ case 2:
+ if ((jjbitVec0[i2] & l2) == 0L)
+ break;
+ if (kind > 18)
+ kind = 18;
+ jjCheckNAdd(2);
+ break;
+ case 1:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 17)
+ kind = 17;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ if (kind != 0x7fffffff)
+ {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+ return curPos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return curPos; }
+ }
+}
+private final int jjStopStringLiteralDfa_2(int pos, long active0)
+{
+ switch (pos)
+ {
+ default :
+ return -1;
+ }
+}
+private final int jjStartNfa_2(int pos, long active0)
+{
+ return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0), pos + 1);
+}
+private final int jjStartNfaWithStates_2(int pos, int kind, int state)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return pos + 1; }
+ return jjMoveNfa_2(state, pos + 1);
+}
+private final int jjMoveStringLiteralDfa0_2()
+{
+ switch(curChar)
+ {
+ case 40:
+ return jjStopAtPos(0, 13);
+ case 41:
+ return jjStopAtPos(0, 14);
+ default :
+ return jjMoveNfa_2(0, 0);
+ }
+}
+private final int jjMoveNfa_2(int startState, int curPos)
+{
+ int[] nextStates;
+ int startsAt = 0;
+ jjnewStateCnt = 3;
+ int i = 1;
+ jjstateSet[0] = startState;
+ int j, kind = 0x7fffffff;
+ for (;;)
+ {
+ if (++jjround == 0x7fffffff)
+ ReInitRounds();
+ if (curChar < 64)
+ {
+ long l = 1L << curChar;
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if (kind > 15)
+ kind = 15;
+ break;
+ case 1:
+ if (kind > 12)
+ kind = 12;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else if (curChar < 128)
+ {
+ long l = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if (kind > 15)
+ kind = 15;
+ if (curChar == 92)
+ jjstateSet[jjnewStateCnt++] = 1;
+ break;
+ case 1:
+ if (kind > 12)
+ kind = 12;
+ break;
+ case 2:
+ if (kind > 15)
+ kind = 15;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else
+ {
+ int i2 = (curChar & 0xff) >> 6;
+ long l2 = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 15)
+ kind = 15;
+ break;
+ case 1:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 12)
+ kind = 12;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ if (kind != 0x7fffffff)
+ {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+ return curPos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return curPos; }
+ }
+}
+static final int[] jjnextStates = {
+};
+public static final String[] jjstrLiteralImages = {
+"", "\15", "\12", "\57", "\73", "\75", null, null, null, null, null, null,
+null, null, null, null, null, null, null, null, null, null, null, null, };
+public static final String[] lexStateNames = {
+ "DEFAULT",
+ "INCOMMENT",
+ "NESTED_COMMENT",
+ "INQUOTEDSTRING",
+};
+public static final int[] jjnewLexState = {
+ -1, -1, -1, -1, -1, -1, -1, 1, 0, -1, 2, -1, -1, -1, -1, -1, 3, -1, -1, 0, -1, -1, -1, -1,
+};
+static final long[] jjtoToken = {
+ 0x38003fL,
+};
+static final long[] jjtoSkip = {
+ 0x140L,
+};
+static final long[] jjtoSpecial = {
+ 0x40L,
+};
+static final long[] jjtoMore = {
+ 0x7fe80L,
+};
+protected SimpleCharStream input_stream;
+private final int[] jjrounds = new int[3];
+private final int[] jjstateSet = new int[6];
+StringBuffer image;
+int jjimageLen;
+int lengthOfMatch;
+protected char curChar;
+public ContentTypeParserTokenManager(SimpleCharStream stream){
+ if (SimpleCharStream.staticFlag)
+ throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+ input_stream = stream;
+}
+public ContentTypeParserTokenManager(SimpleCharStream stream, int lexState){
+ this(stream);
+ SwitchTo(lexState);
+}
+public void ReInit(SimpleCharStream stream)
+{
+ jjmatchedPos = jjnewStateCnt = 0;
+ curLexState = defaultLexState;
+ input_stream = stream;
+ ReInitRounds();
+}
+private final void ReInitRounds()
+{
+ int i;
+ jjround = 0x80000001;
+ for (i = 3; i-- > 0;)
+ jjrounds[i] = 0x80000000;
+}
+public void ReInit(SimpleCharStream stream, int lexState)
+{
+ ReInit(stream);
+ SwitchTo(lexState);
+}
+public void SwitchTo(int lexState)
+{
+ if (lexState >= 4 || lexState < 0)
+ throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE);
+ else
+ curLexState = lexState;
+}
+
+protected Token jjFillToken()
+{
+ Token t = Token.newToken(jjmatchedKind);
+ t.kind = jjmatchedKind;
+ String im = jjstrLiteralImages[jjmatchedKind];
+ t.image = (im == null) ? input_stream.GetImage() : im;
+ t.beginLine = input_stream.getBeginLine();
+ t.beginColumn = input_stream.getBeginColumn();
+ t.endLine = input_stream.getEndLine();
+ t.endColumn = input_stream.getEndColumn();
+ return t;
+}
+
+int curLexState = 0;
+int defaultLexState = 0;
+int jjnewStateCnt;
+int jjround;
+int jjmatchedPos;
+int jjmatchedKind;
+
+public Token getNextToken()
+{
+ int kind;
+ Token specialToken = null;
+ Token matchedToken;
+ int curPos = 0;
+
+ EOFLoop :
+ for (;;)
+ {
+ try
+ {
+ curChar = input_stream.BeginToken();
+ }
+ catch(java.io.IOException e)
+ {
+ jjmatchedKind = 0;
+ matchedToken = jjFillToken();
+ matchedToken.specialToken = specialToken;
+ return matchedToken;
+ }
+ image = null;
+ jjimageLen = 0;
+
+ for (;;)
+ {
+ switch(curLexState)
+ {
+ case 0:
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_0();
+ break;
+ case 1:
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_1();
+ break;
+ case 2:
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_2();
+ break;
+ case 3:
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_3();
+ break;
+ }
+ if (jjmatchedKind != 0x7fffffff)
+ {
+ if (jjmatchedPos + 1 < curPos)
+ input_stream.backup(curPos - jjmatchedPos - 1);
+ if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+ {
+ matchedToken = jjFillToken();
+ matchedToken.specialToken = specialToken;
+ TokenLexicalActions(matchedToken);
+ if (jjnewLexState[jjmatchedKind] != -1)
+ curLexState = jjnewLexState[jjmatchedKind];
+ return matchedToken;
+ }
+ else if ((jjtoSkip[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+ {
+ if ((jjtoSpecial[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+ {
+ matchedToken = jjFillToken();
+ if (specialToken == null)
+ specialToken = matchedToken;
+ else
+ {
+ matchedToken.specialToken = specialToken;
+ specialToken = (specialToken.next = matchedToken);
+ }
+ }
+ if (jjnewLexState[jjmatchedKind] != -1)
+ curLexState = jjnewLexState[jjmatchedKind];
+ continue EOFLoop;
+ }
+ MoreLexicalActions();
+ if (jjnewLexState[jjmatchedKind] != -1)
+ curLexState = jjnewLexState[jjmatchedKind];
+ curPos = 0;
+ jjmatchedKind = 0x7fffffff;
+ try {
+ curChar = input_stream.readChar();
+ continue;
+ }
+ catch (java.io.IOException e1) { }
+ }
+ int error_line = input_stream.getEndLine();
+ int error_column = input_stream.getEndColumn();
+ String error_after = null;
+ boolean EOFSeen = false;
+ try { input_stream.readChar(); input_stream.backup(1); }
+ catch (java.io.IOException e1) {
+ EOFSeen = true;
+ error_after = curPos <= 1 ? "" : input_stream.GetImage();
+ if (curChar == '\n' || curChar == '\r') {
+ error_line++;
+ error_column = 0;
+ }
+ else
+ error_column++;
+ }
+ if (!EOFSeen) {
+ input_stream.backup(1);
+ error_after = curPos <= 1 ? "" : input_stream.GetImage();
+ }
+ throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR);
+ }
+ }
+}
+
+void MoreLexicalActions()
+{
+ jjimageLen += (lengthOfMatch = jjmatchedPos + 1);
+ switch(jjmatchedKind)
+ {
+ case 9 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ image.deleteCharAt(image.length() - 2);
+ break;
+ case 10 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ commentNest = 1;
+ break;
+ case 12 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ image.deleteCharAt(image.length() - 2);
+ break;
+ case 13 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ ++commentNest;
+ break;
+ case 14 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ --commentNest; if (commentNest == 0) SwitchTo(INCOMMENT);
+ break;
+ case 16 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ image.deleteCharAt(image.length() - 1);
+ break;
+ case 17 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ image.deleteCharAt(image.length() - 2);
+ break;
+ default :
+ break;
+ }
+}
+void TokenLexicalActions(Token matchedToken)
+{
+ switch(jjmatchedKind)
+ {
+ case 19 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
+ matchedToken.image = image.substring(0, image.length() - 1);
+ break;
+ default :
+ break;
+ }
+}
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ParseException.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ParseException.java
new file mode 100644
index 000000000..d9b69b25c
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/ParseException.java
@@ -0,0 +1,207 @@
+/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 3.0 */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.contenttype.parser;
+
+/**
+ * This exception is thrown when parse errors are encountered.
+ * You can explicitly create objects of this exception type by
+ * calling the method generateParseException in the generated
+ * parser.
+ *
+ * You can modify this class to customize your error reporting
+ * mechanisms so long as you retain the public fields.
+ */
+public class ParseException extends Exception {
+
+ /**
+ * This constructor is used by the method "generateParseException"
+ * in the generated parser. Calling this constructor generates
+ * a new object of this type with the fields "currentToken",
+ * "expectedTokenSequences", and "tokenImage" set. The boolean
+ * flag "specialConstructor" is also set to true to indicate that
+ * this constructor was used to create this object.
+ * This constructor calls its super class with the empty string
+ * to force the "toString" method of parent class "Throwable" to
+ * print the error message in the form:
+ * ParseException: <result of getMessage>
+ */
+ public ParseException(Token currentTokenVal,
+ int[][] expectedTokenSequencesVal,
+ String[] tokenImageVal
+ )
+ {
+ super("");
+ specialConstructor = true;
+ currentToken = currentTokenVal;
+ expectedTokenSequences = expectedTokenSequencesVal;
+ tokenImage = tokenImageVal;
+ }
+
+ /**
+ * The following constructors are for use by you for whatever
+ * purpose you can think of. Constructing the exception in this
+ * manner makes the exception behave in the normal way - i.e., as
+ * documented in the class "Throwable". The fields "errorToken",
+ * "expectedTokenSequences", and "tokenImage" do not contain
+ * relevant information. The JavaCC generated code does not use
+ * these constructors.
+ */
+
+ public ParseException() {
+ super();
+ specialConstructor = false;
+ }
+
+ public ParseException(String message) {
+ super(message);
+ specialConstructor = false;
+ }
+
+ /**
+ * This variable determines which constructor was used to create
+ * this object and thereby affects the semantics of the
+ * "getMessage" method (see below).
+ */
+ protected boolean specialConstructor;
+
+ /**
+ * This is the last token that has been consumed successfully. If
+ * this object has been created due to a parse error, the token
+ * followng this token will (therefore) be the first error token.
+ */
+ public Token currentToken;
+
+ /**
+ * Each entry in this array is an array of integers. Each array
+ * of integers represents a sequence of tokens (by their ordinal
+ * values) that is expected at this point of the parse.
+ */
+ public int[][] expectedTokenSequences;
+
+ /**
+ * This is a reference to the "tokenImage" array of the generated
+ * parser within which the parse error occurred. This array is
+ * defined in the generated ...Constants interface.
+ */
+ public String[] tokenImage;
+
+ /**
+ * This method has the standard behavior when this object has been
+ * created using the standard constructors. Otherwise, it uses
+ * "currentToken" and "expectedTokenSequences" to generate a parse
+ * error message and returns it. If this object has been created
+ * due to a parse error, and you do not catch it (it gets thrown
+ * from the parser), then this method is called during the printing
+ * of the final stack trace, and hence the correct error message
+ * gets displayed.
+ */
+ public String getMessage() {
+ if (!specialConstructor) {
+ return super.getMessage();
+ }
+ StringBuffer expected = new StringBuffer();
+ int maxSize = 0;
+ for (int i = 0; i < expectedTokenSequences.length; i++) {
+ if (maxSize < expectedTokenSequences[i].length) {
+ maxSize = expectedTokenSequences[i].length;
+ }
+ for (int j = 0; j < expectedTokenSequences[i].length; j++) {
+ expected.append(tokenImage[expectedTokenSequences[i][j]]).append(" ");
+ }
+ if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
+ expected.append("...");
+ }
+ expected.append(eol).append(" ");
+ }
+ String retval = "Encountered \"";
+ Token tok = currentToken.next;
+ for (int i = 0; i < maxSize; i++) {
+ if (i != 0) retval += " ";
+ if (tok.kind == 0) {
+ retval += tokenImage[0];
+ break;
+ }
+ retval += add_escapes(tok.image);
+ tok = tok.next;
+ }
+ retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+ retval += "." + eol;
+ if (expectedTokenSequences.length == 1) {
+ retval += "Was expecting:" + eol + " ";
+ } else {
+ retval += "Was expecting one of:" + eol + " ";
+ }
+ retval += expected.toString();
+ return retval;
+ }
+
+ /**
+ * The end of line string for this machine.
+ */
+ protected String eol = System.getProperty("line.separator", "\n");
+
+ /**
+ * Used to convert raw characters to their escaped version
+ * when these raw version cannot be used as part of an ASCII
+ * string literal.
+ */
+ protected String add_escapes(String str) {
+ StringBuffer retval = new StringBuffer();
+ char ch;
+ for (int i = 0; i < str.length(); i++) {
+ switch (str.charAt(i))
+ {
+ case 0 :
+ continue;
+ case '\b':
+ retval.append("\\b");
+ continue;
+ case '\t':
+ retval.append("\\t");
+ continue;
+ case '\n':
+ retval.append("\\n");
+ continue;
+ case '\f':
+ retval.append("\\f");
+ continue;
+ case '\r':
+ retval.append("\\r");
+ continue;
+ case '\"':
+ retval.append("\\\"");
+ continue;
+ case '\'':
+ retval.append("\\\'");
+ continue;
+ case '\\':
+ retval.append("\\\\");
+ continue;
+ default:
+ if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+ String s = "0000" + Integer.toString(ch, 16);
+ retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+ } else {
+ retval.append(ch);
+ }
+ continue;
+ }
+ }
+ return retval.toString();
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/SimpleCharStream.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/SimpleCharStream.java
new file mode 100644
index 000000000..ae035b717
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/SimpleCharStream.java
@@ -0,0 +1,454 @@
+/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 4.0 */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.contenttype.parser;
+
+/**
+ * An implementation of interface CharStream, where the stream is assumed to
+ * contain only ASCII characters (without unicode processing).
+ */
+
+public class SimpleCharStream
+{
+ public static final boolean staticFlag = false;
+ int bufsize;
+ int available;
+ int tokenBegin;
+ public int bufpos = -1;
+ protected int bufline[];
+ protected int bufcolumn[];
+
+ protected int column = 0;
+ protected int line = 1;
+
+ protected boolean prevCharIsCR = false;
+ protected boolean prevCharIsLF = false;
+
+ protected java.io.Reader inputStream;
+
+ protected char[] buffer;
+ protected int maxNextCharInd = 0;
+ protected int inBuf = 0;
+ protected int tabSize = 8;
+
+ protected void setTabSize(int i) { tabSize = i; }
+ protected int getTabSize(int i) { return tabSize; }
+
+
+ protected void ExpandBuff(boolean wrapAround)
+ {
+ char[] newbuffer = new char[bufsize + 2048];
+ int newbufline[] = new int[bufsize + 2048];
+ int newbufcolumn[] = new int[bufsize + 2048];
+
+ try
+ {
+ if (wrapAround)
+ {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ System.arraycopy(buffer, 0, newbuffer,
+ bufsize - tokenBegin, bufpos);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos);
+ bufcolumn = newbufcolumn;
+
+ maxNextCharInd = (bufpos += (bufsize - tokenBegin));
+ }
+ else
+ {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ bufcolumn = newbufcolumn;
+
+ maxNextCharInd = (bufpos -= tokenBegin);
+ }
+ }
+ catch (Throwable t)
+ {
+ throw new Error(t.getMessage());
+ }
+
+
+ bufsize += 2048;
+ available = bufsize;
+ tokenBegin = 0;
+ }
+
+ protected void FillBuff() throws java.io.IOException
+ {
+ if (maxNextCharInd == available)
+ {
+ if (available == bufsize)
+ {
+ if (tokenBegin > 2048)
+ {
+ bufpos = maxNextCharInd = 0;
+ available = tokenBegin;
+ }
+ else if (tokenBegin < 0)
+ bufpos = maxNextCharInd = 0;
+ else
+ ExpandBuff(false);
+ }
+ else if (available > tokenBegin)
+ available = bufsize;
+ else if ((tokenBegin - available) < 2048)
+ ExpandBuff(true);
+ else
+ available = tokenBegin;
+ }
+
+ int i;
+ try {
+ if ((i = inputStream.read(buffer, maxNextCharInd,
+ available - maxNextCharInd)) == -1)
+ {
+ inputStream.close();
+ throw new java.io.IOException();
+ }
+ else
+ maxNextCharInd += i;
+ return;
+ }
+ catch(java.io.IOException e) {
+ --bufpos;
+ backup(0);
+ if (tokenBegin == -1)
+ tokenBegin = bufpos;
+ throw e;
+ }
+ }
+
+ public char BeginToken() throws java.io.IOException
+ {
+ tokenBegin = -1;
+ char c = readChar();
+ tokenBegin = bufpos;
+
+ return c;
+ }
+
+ protected void UpdateLineColumn(char c)
+ {
+ column++;
+
+ if (prevCharIsLF)
+ {
+ prevCharIsLF = false;
+ line += (column = 1);
+ }
+ else if (prevCharIsCR)
+ {
+ prevCharIsCR = false;
+ if (c == '\n')
+ {
+ prevCharIsLF = true;
+ }
+ else
+ line += (column = 1);
+ }
+
+ switch (c)
+ {
+ case '\r' :
+ prevCharIsCR = true;
+ break;
+ case '\n' :
+ prevCharIsLF = true;
+ break;
+ case '\t' :
+ column--;
+ column += (tabSize - (column % tabSize));
+ break;
+ default :
+ break;
+ }
+
+ bufline[bufpos] = line;
+ bufcolumn[bufpos] = column;
+ }
+
+ public char readChar() throws java.io.IOException
+ {
+ if (inBuf > 0)
+ {
+ --inBuf;
+
+ if (++bufpos == bufsize)
+ bufpos = 0;
+
+ return buffer[bufpos];
+ }
+
+ if (++bufpos >= maxNextCharInd)
+ FillBuff();
+
+ char c = buffer[bufpos];
+
+ UpdateLineColumn(c);
+ return (c);
+ }
+
+ /**
+ * @deprecated
+ * @see #getEndColumn
+ */
+ @Deprecated
+ public int getColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ /**
+ * @deprecated
+ * @see #getEndLine
+ */
+ @Deprecated
+ public int getLine() {
+ return bufline[bufpos];
+ }
+
+ public int getEndColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ public int getEndLine() {
+ return bufline[bufpos];
+ }
+
+ public int getBeginColumn() {
+ return bufcolumn[tokenBegin];
+ }
+
+ public int getBeginLine() {
+ return bufline[tokenBegin];
+ }
+
+ public void backup(int amount) {
+
+ inBuf += amount;
+ if ((bufpos -= amount) < 0)
+ bufpos += bufsize;
+ }
+
+ public SimpleCharStream(java.io.Reader dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ inputStream = dstream;
+ line = startline;
+ column = startcolumn - 1;
+
+ available = bufsize = buffersize;
+ buffer = new char[buffersize];
+ bufline = new int[buffersize];
+ bufcolumn = new int[buffersize];
+ }
+
+ public SimpleCharStream(java.io.Reader dstream, int startline,
+ int startcolumn)
+ {
+ this(dstream, startline, startcolumn, 4096);
+ }
+
+ public SimpleCharStream(java.io.Reader dstream)
+ {
+ this(dstream, 1, 1, 4096);
+ }
+ public void ReInit(java.io.Reader dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ inputStream = dstream;
+ line = startline;
+ column = startcolumn - 1;
+
+ if (buffer == null || buffersize != buffer.length)
+ {
+ available = bufsize = buffersize;
+ buffer = new char[buffersize];
+ bufline = new int[buffersize];
+ bufcolumn = new int[buffersize];
+ }
+ prevCharIsLF = prevCharIsCR = false;
+ tokenBegin = inBuf = maxNextCharInd = 0;
+ bufpos = -1;
+ }
+
+ public void ReInit(java.io.Reader dstream, int startline,
+ int startcolumn)
+ {
+ ReInit(dstream, startline, startcolumn, 4096);
+ }
+
+ public void ReInit(java.io.Reader dstream)
+ {
+ ReInit(dstream, 1, 1, 4096);
+ }
+ public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+ {
+ this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+ }
+
+ public SimpleCharStream(java.io.InputStream dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+ }
+
+ public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn) throws java.io.UnsupportedEncodingException
+ {
+ this(dstream, encoding, startline, startcolumn, 4096);
+ }
+
+ public SimpleCharStream(java.io.InputStream dstream, int startline,
+ int startcolumn)
+ {
+ this(dstream, startline, startcolumn, 4096);
+ }
+
+ public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+ {
+ this(dstream, encoding, 1, 1, 4096);
+ }
+
+ public SimpleCharStream(java.io.InputStream dstream)
+ {
+ this(dstream, 1, 1, 4096);
+ }
+
+ public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+ {
+ ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+ }
+
+ public void ReInit(java.io.InputStream dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+ }
+
+ public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+ {
+ ReInit(dstream, encoding, 1, 1, 4096);
+ }
+
+ public void ReInit(java.io.InputStream dstream)
+ {
+ ReInit(dstream, 1, 1, 4096);
+ }
+ public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn) throws java.io.UnsupportedEncodingException
+ {
+ ReInit(dstream, encoding, startline, startcolumn, 4096);
+ }
+ public void ReInit(java.io.InputStream dstream, int startline,
+ int startcolumn)
+ {
+ ReInit(dstream, startline, startcolumn, 4096);
+ }
+ public String GetImage()
+ {
+ if (bufpos >= tokenBegin)
+ return new String(buffer, tokenBegin, bufpos - tokenBegin + 1);
+ else
+ return new String(buffer, tokenBegin, bufsize - tokenBegin) +
+ new String(buffer, 0, bufpos + 1);
+ }
+
+ public char[] GetSuffix(int len)
+ {
+ char[] ret = new char[len];
+
+ if ((bufpos + 1) >= len)
+ System.arraycopy(buffer, bufpos - len + 1, ret, 0, len);
+ else
+ {
+ System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0,
+ len - bufpos - 1);
+ System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1);
+ }
+
+ return ret;
+ }
+
+ public void Done()
+ {
+ buffer = null;
+ bufline = null;
+ bufcolumn = null;
+ }
+
+ /**
+ * Method to adjust line and column numbers for the start of a token.
+ */
+ public void adjustBeginLineColumn(int newLine, int newCol)
+ {
+ int start = tokenBegin;
+ int len;
+
+ if (bufpos >= tokenBegin)
+ {
+ len = bufpos - tokenBegin + inBuf + 1;
+ }
+ else
+ {
+ len = bufsize - tokenBegin + bufpos + 1 + inBuf;
+ }
+
+ int i = 0, j = 0, k = 0;
+ int nextColDiff = 0, columnDiff = 0;
+
+ while (i < len &&
+ bufline[j = start % bufsize] == bufline[k = ++start % bufsize])
+ {
+ bufline[j] = newLine;
+ nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j];
+ bufcolumn[j] = newCol + columnDiff;
+ columnDiff = nextColDiff;
+ i++;
+ }
+
+ if (i < len)
+ {
+ bufline[j] = newLine++;
+ bufcolumn[j] = newCol + columnDiff;
+
+ while (i++ < len)
+ {
+ if (bufline[j = start % bufsize] != bufline[++start % bufsize])
+ bufline[j] = newLine++;
+ else
+ bufline[j] = newLine;
+ }
+ }
+
+ line = bufline[j];
+ column = bufcolumn[j];
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/Token.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/Token.java
new file mode 100644
index 000000000..34e65eec0
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/Token.java
@@ -0,0 +1,96 @@
+/* Generated By:JavaCC: Do not edit this line. Token.java Version 3.0 */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.contenttype.parser;
+
+/**
+ * Describes the input token stream.
+ */
+
+public class Token {
+
+ /**
+ * An integer that describes the kind of this token. This numbering
+ * system is determined by JavaCCParser, and a table of these numbers is
+ * stored in the file ...Constants.java.
+ */
+ public int kind;
+
+ /**
+ * beginLine and beginColumn describe the position of the first character
+ * of this token; endLine and endColumn describe the position of the
+ * last character of this token.
+ */
+ public int beginLine, beginColumn, endLine, endColumn;
+
+ /**
+ * The string image of the token.
+ */
+ public String image;
+
+ /**
+ * A reference to the next regular (non-special) token from the input
+ * stream. If this is the last token from the input stream, or if the
+ * token manager has not read tokens beyond this one, this field is
+ * set to null. This is true only if this token is also a regular
+ * token. Otherwise, see below for a description of the contents of
+ * this field.
+ */
+ public Token next;
+
+ /**
+ * This field is used to access special tokens that occur prior to this
+ * token, but after the immediately preceding regular (non-special) token.
+ * If there are no such special tokens, this field is set to null.
+ * When there are more than one such special token, this field refers
+ * to the last of these special tokens, which in turn refers to the next
+ * previous special token through its specialToken field, and so on
+ * until the first special token (whose specialToken field is null).
+ * The next fields of special tokens refer to other special tokens that
+ * immediately follow it (without an intervening regular token). If there
+ * is no such token, this field is null.
+ */
+ public Token specialToken;
+
+ /**
+ * Returns the image.
+ */
+ public String toString()
+ {
+ return image;
+ }
+
+ /**
+ * Returns a new Token object, by default. However, if you want, you
+ * can create and return subclass objects based on the value of ofKind.
+ * Simply add the cases to the switch for all those special cases.
+ * For example, if you have a subclass of Token called IDToken that
+ * you want to create if ofKind is ID, simlpy add something like :
+ *
+ * case MyParserConstants.ID : return new IDToken();
+ *
+ * to the following switch statement. Then you can cast matchedToken
+ * variable to the appropriate type and use it in your lexical actions.
+ */
+ public static final Token newToken(int ofKind)
+ {
+ switch(ofKind)
+ {
+ default : return new Token();
+ }
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/TokenMgrError.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/TokenMgrError.java
new file mode 100644
index 000000000..ea5a7826e
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/contenttype/parser/TokenMgrError.java
@@ -0,0 +1,148 @@
+/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 3.0 */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.contenttype.parser;
+
+public class TokenMgrError extends Error
+{
+ /*
+ * Ordinals for various reasons why an Error of this type can be thrown.
+ */
+
+ /**
+ * Lexical error occured.
+ */
+ static final int LEXICAL_ERROR = 0;
+
+ /**
+ * An attempt wass made to create a second instance of a static token manager.
+ */
+ static final int STATIC_LEXER_ERROR = 1;
+
+ /**
+ * Tried to change to an invalid lexical state.
+ */
+ static final int INVALID_LEXICAL_STATE = 2;
+
+ /**
+ * Detected (and bailed out of) an infinite loop in the token manager.
+ */
+ static final int LOOP_DETECTED = 3;
+
+ /**
+ * Indicates the reason why the exception is thrown. It will have
+ * one of the above 4 values.
+ */
+ int errorCode;
+
+ /**
+ * Replaces unprintable characters by their espaced (or unicode escaped)
+ * equivalents in the given string
+ */
+ protected static final String addEscapes(String str) {
+ StringBuffer retval = new StringBuffer();
+ char ch;
+ for (int i = 0; i < str.length(); i++) {
+ switch (str.charAt(i))
+ {
+ case 0 :
+ continue;
+ case '\b':
+ retval.append("\\b");
+ continue;
+ case '\t':
+ retval.append("\\t");
+ continue;
+ case '\n':
+ retval.append("\\n");
+ continue;
+ case '\f':
+ retval.append("\\f");
+ continue;
+ case '\r':
+ retval.append("\\r");
+ continue;
+ case '\"':
+ retval.append("\\\"");
+ continue;
+ case '\'':
+ retval.append("\\\'");
+ continue;
+ case '\\':
+ retval.append("\\\\");
+ continue;
+ default:
+ if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+ String s = "0000" + Integer.toString(ch, 16);
+ retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+ } else {
+ retval.append(ch);
+ }
+ continue;
+ }
+ }
+ return retval.toString();
+ }
+
+ /**
+ * Returns a detailed message for the Error when it is thrown by the
+ * token manager to indicate a lexical error.
+ * Parameters :
+ * EOFSeen : indicates if EOF caused the lexicl error
+ * curLexState : lexical state in which this error occured
+ * errorLine : line number when the error occured
+ * errorColumn : column number when the error occured
+ * errorAfter : prefix that was seen before this error occured
+ * curchar : the offending character
+ * Note: You can customize the lexical error message by modifying this method.
+ */
+ protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) {
+ return("Lexical error at line " +
+ errorLine + ", column " +
+ errorColumn + ". Encountered: " +
+ (EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") +
+ "after : \"" + addEscapes(errorAfter) + "\"");
+ }
+
+ /**
+ * You can also modify the body of this method to customize your error messages.
+ * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not
+ * of end-users concern, so you can return something like :
+ *
+ * "Internal Error : Please file a bug report .... "
+ *
+ * from this method for such cases in the release version of your parser.
+ */
+ public String getMessage() {
+ return super.getMessage();
+ }
+
+ /*
+ * Constructors of various flavors follow.
+ */
+
+ public TokenMgrError() {
+ }
+
+ public TokenMgrError(String message, int reason) {
+ super(message);
+ errorCode = reason;
+ }
+
+ public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) {
+ this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/DateTime.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/DateTime.java
new file mode 100644
index 000000000..506ff54e5
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/DateTime.java
@@ -0,0 +1,127 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.field.datetime;
+
+import org.apache.james.mime4j.field.datetime.parser.DateTimeParser;
+import org.apache.james.mime4j.field.datetime.parser.ParseException;
+import org.apache.james.mime4j.field.datetime.parser.TokenMgrError;
+
+import java.util.Date;
+import java.util.Calendar;
+import java.util.TimeZone;
+import java.util.GregorianCalendar;
+import java.io.StringReader;
+
+public class DateTime {
+ private final Date date;
+ private final int year;
+ private final int month;
+ private final int day;
+ private final int hour;
+ private final int minute;
+ private final int second;
+ private final int timeZone;
+
+ public DateTime(String yearString, int month, int day, int hour, int minute, int second, int timeZone) {
+ this.year = convertToYear(yearString);
+ this.date = convertToDate(year, month, day, hour, minute, second, timeZone);
+ this.month = month;
+ this.day = day;
+ this.hour = hour;
+ this.minute = minute;
+ this.second = second;
+ this.timeZone = timeZone;
+ }
+
+ private int convertToYear(String yearString) {
+ int year = Integer.parseInt(yearString);
+ switch (yearString.length()) {
+ case 1:
+ case 2:
+ if (year >= 0 && year < 50)
+ return 2000 + year;
+ else
+ return 1900 + year;
+ case 3:
+ return 1900 + year;
+ default:
+ return year;
+ }
+ }
+
+ public static Date convertToDate(int year, int month, int day, int hour, int minute, int second, int timeZone) {
+ Calendar c = new GregorianCalendar(TimeZone.getTimeZone("GMT+0"));
+ c.set(year, month - 1, day, hour, minute, second);
+ c.set(Calendar.MILLISECOND, 0);
+
+ if (timeZone != Integer.MIN_VALUE) {
+ int minutes = ((timeZone / 100) * 60) + timeZone % 100;
+ c.add(Calendar.MINUTE, -1 * minutes);
+ }
+
+ return c.getTime();
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public int getYear() {
+ return year;
+ }
+
+ public int getMonth() {
+ return month;
+ }
+
+ public int getDay() {
+ return day;
+ }
+
+ public int getHour() {
+ return hour;
+ }
+
+ public int getMinute() {
+ return minute;
+ }
+
+ public int getSecond() {
+ return second;
+ }
+
+ public int getTimeZone() {
+ return timeZone;
+ }
+
+ public void print() {
+ System.out.println(getYear() + " " + getMonth() + " " + getDay() + "; " + getHour() + " " + getMinute() + " " + getSecond() + " " + getTimeZone());
+ }
+
+
+ public static DateTime parse(String dateString) throws ParseException {
+ try {
+ return new DateTimeParser(new StringReader(dateString)).parseAll();
+ }
+ catch (TokenMgrError err) {
+ throw new ParseException(err.getMessage());
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParser.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParser.java
new file mode 100644
index 000000000..43edebb5c
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParser.java
@@ -0,0 +1,570 @@
+/* Generated By:JavaCC: Do not edit this line. DateTimeParser.java */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.datetime.parser;
+
+import org.apache.james.mime4j.field.datetime.DateTime;
+
+import java.util.Vector;
+
+public class DateTimeParser implements DateTimeParserConstants {
+ private static final boolean ignoreMilitaryZoneOffset = true;
+
+ public static void main(String args[]) throws ParseException {
+ while (true) {
+ try {
+ DateTimeParser parser = new DateTimeParser(System.in);
+ parser.parseLine();
+ } catch (Exception x) {
+ x.printStackTrace();
+ return;
+ }
+ }
+ }
+
+ private static int parseDigits(Token token) {
+ return Integer.parseInt(token.image, 10);
+ }
+
+ private static int getMilitaryZoneOffset(char c) {
+ if (ignoreMilitaryZoneOffset)
+ return 0;
+
+ c = Character.toUpperCase(c);
+
+ switch (c) {
+ case 'A': return 1;
+ case 'B': return 2;
+ case 'C': return 3;
+ case 'D': return 4;
+ case 'E': return 5;
+ case 'F': return 6;
+ case 'G': return 7;
+ case 'H': return 8;
+ case 'I': return 9;
+ case 'K': return 10;
+ case 'L': return 11;
+ case 'M': return 12;
+
+ case 'N': return -1;
+ case 'O': return -2;
+ case 'P': return -3;
+ case 'Q': return -4;
+ case 'R': return -5;
+ case 'S': return -6;
+ case 'T': return -7;
+ case 'U': return -8;
+ case 'V': return -9;
+ case 'W': return -10;
+ case 'X': return -11;
+ case 'Y': return -12;
+
+ case 'Z': return 0;
+ default: return 0;
+ }
+ }
+
+ private static class Time {
+ private int hour;
+ private int minute;
+ private int second;
+ private int zone;
+
+ public Time(int hour, int minute, int second, int zone) {
+ this.hour = hour;
+ this.minute = minute;
+ this.second = second;
+ this.zone = zone;
+ }
+
+ public int getHour() { return hour; }
+ public int getMinute() { return minute; }
+ public int getSecond() { return second; }
+ public int getZone() { return zone; }
+ }
+
+ private static class Date {
+ private String year;
+ private int month;
+ private int day;
+
+ public Date(String year, int month, int day) {
+ this.year = year;
+ this.month = month;
+ this.day = day;
+ }
+
+ public String getYear() { return year; }
+ public int getMonth() { return month; }
+ public int getDay() { return day; }
+ }
+
+ final public DateTime parseLine() throws ParseException {
+ DateTime dt;
+ dt = date_time();
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 1:
+ jj_consume_token(1);
+ break;
+ default:
+ jj_la1[0] = jj_gen;
+ ;
+ }
+ jj_consume_token(2);
+ {if (true) return dt;}
+ throw new Error("Missing return statement in function");
+ }
+
+ final public DateTime parseAll() throws ParseException {
+ DateTime dt;
+ dt = date_time();
+ jj_consume_token(0);
+ {if (true) return dt;}
+ throw new Error("Missing return statement in function");
+ }
+
+ final public DateTime date_time() throws ParseException {
+ Date d; Time t;
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ day_of_week();
+ jj_consume_token(3);
+ break;
+ default:
+ jj_la1[1] = jj_gen;
+ ;
+ }
+ d = date();
+ t = time();
+ {if (true) return new DateTime(
+ d.getYear(),
+ d.getMonth(),
+ d.getDay(),
+ t.getHour(),
+ t.getMinute(),
+ t.getSecond(),
+ t.getZone());} // time zone offset
+
+ throw new Error("Missing return statement in function");
+ }
+
+ final public String day_of_week() throws ParseException {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 4:
+ jj_consume_token(4);
+ break;
+ case 5:
+ jj_consume_token(5);
+ break;
+ case 6:
+ jj_consume_token(6);
+ break;
+ case 7:
+ jj_consume_token(7);
+ break;
+ case 8:
+ jj_consume_token(8);
+ break;
+ case 9:
+ jj_consume_token(9);
+ break;
+ case 10:
+ jj_consume_token(10);
+ break;
+ default:
+ jj_la1[2] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {if (true) return token.image;}
+ throw new Error("Missing return statement in function");
+ }
+
+ final public Date date() throws ParseException {
+ int d, m; String y;
+ d = day();
+ m = month();
+ y = year();
+ {if (true) return new Date(y, m, d);}
+ throw new Error("Missing return statement in function");
+ }
+
+ final public int day() throws ParseException {
+ Token t;
+ t = jj_consume_token(DIGITS);
+ {if (true) return parseDigits(t);}
+ throw new Error("Missing return statement in function");
+ }
+
+ final public int month() throws ParseException {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 11:
+ jj_consume_token(11);
+ {if (true) return 1;}
+ break;
+ case 12:
+ jj_consume_token(12);
+ {if (true) return 2;}
+ break;
+ case 13:
+ jj_consume_token(13);
+ {if (true) return 3;}
+ break;
+ case 14:
+ jj_consume_token(14);
+ {if (true) return 4;}
+ break;
+ case 15:
+ jj_consume_token(15);
+ {if (true) return 5;}
+ break;
+ case 16:
+ jj_consume_token(16);
+ {if (true) return 6;}
+ break;
+ case 17:
+ jj_consume_token(17);
+ {if (true) return 7;}
+ break;
+ case 18:
+ jj_consume_token(18);
+ {if (true) return 8;}
+ break;
+ case 19:
+ jj_consume_token(19);
+ {if (true) return 9;}
+ break;
+ case 20:
+ jj_consume_token(20);
+ {if (true) return 10;}
+ break;
+ case 21:
+ jj_consume_token(21);
+ {if (true) return 11;}
+ break;
+ case 22:
+ jj_consume_token(22);
+ {if (true) return 12;}
+ break;
+ default:
+ jj_la1[3] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ final public String year() throws ParseException {
+ Token t;
+ t = jj_consume_token(DIGITS);
+ {if (true) return t.image;}
+ throw new Error("Missing return statement in function");
+ }
+
+ final public Time time() throws ParseException {
+ int h, m, s=0, z;
+ h = hour();
+ jj_consume_token(23);
+ m = minute();
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 23:
+ jj_consume_token(23);
+ s = second();
+ break;
+ default:
+ jj_la1[4] = jj_gen;
+ ;
+ }
+ z = zone();
+ {if (true) return new Time(h, m, s, z);}
+ throw new Error("Missing return statement in function");
+ }
+
+ final public int hour() throws ParseException {
+ Token t;
+ t = jj_consume_token(DIGITS);
+ {if (true) return parseDigits(t);}
+ throw new Error("Missing return statement in function");
+ }
+
+ final public int minute() throws ParseException {
+ Token t;
+ t = jj_consume_token(DIGITS);
+ {if (true) return parseDigits(t);}
+ throw new Error("Missing return statement in function");
+ }
+
+ final public int second() throws ParseException {
+ Token t;
+ t = jj_consume_token(DIGITS);
+ {if (true) return parseDigits(t);}
+ throw new Error("Missing return statement in function");
+ }
+
+ final public int zone() throws ParseException {
+ Token t, u; int z;
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case OFFSETDIR:
+ t = jj_consume_token(OFFSETDIR);
+ u = jj_consume_token(DIGITS);
+ z=parseDigits(u)*(t.image.equals("-") ? -1 : 1);
+ break;
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case MILITARY_ZONE:
+ z = obs_zone();
+ break;
+ default:
+ jj_la1[5] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {if (true) return z;}
+ throw new Error("Missing return statement in function");
+ }
+
+ final public int obs_zone() throws ParseException {
+ Token t; int z;
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case 25:
+ jj_consume_token(25);
+ z=0;
+ break;
+ case 26:
+ jj_consume_token(26);
+ z=0;
+ break;
+ case 27:
+ jj_consume_token(27);
+ z=-5;
+ break;
+ case 28:
+ jj_consume_token(28);
+ z=-4;
+ break;
+ case 29:
+ jj_consume_token(29);
+ z=-6;
+ break;
+ case 30:
+ jj_consume_token(30);
+ z=-5;
+ break;
+ case 31:
+ jj_consume_token(31);
+ z=-7;
+ break;
+ case 32:
+ jj_consume_token(32);
+ z=-6;
+ break;
+ case 33:
+ jj_consume_token(33);
+ z=-8;
+ break;
+ case 34:
+ jj_consume_token(34);
+ z=-7;
+ break;
+ case MILITARY_ZONE:
+ t = jj_consume_token(MILITARY_ZONE);
+ z=getMilitaryZoneOffset(t.image.charAt(0));
+ break;
+ default:
+ jj_la1[6] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {if (true) return z * 100;}
+ throw new Error("Missing return statement in function");
+ }
+
+ public DateTimeParserTokenManager token_source;
+ SimpleCharStream jj_input_stream;
+ public Token token, jj_nt;
+ private int jj_ntk;
+ private int jj_gen;
+ final private int[] jj_la1 = new int[7];
+ static private int[] jj_la1_0;
+ static private int[] jj_la1_1;
+ static {
+ jj_la1_0();
+ jj_la1_1();
+ }
+ private static void jj_la1_0() {
+ jj_la1_0 = new int[] {0x2,0x7f0,0x7f0,0x7ff800,0x800000,0xff000000,0xfe000000,};
+ }
+ private static void jj_la1_1() {
+ jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0xf,0xf,};
+ }
+
+ public DateTimeParser(java.io.InputStream stream) {
+ this(stream, null);
+ }
+ public DateTimeParser(java.io.InputStream stream, String encoding) {
+ try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+ token_source = new DateTimeParserTokenManager(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 7; i++) jj_la1[i] = -1;
+ }
+
+ public void ReInit(java.io.InputStream stream) {
+ ReInit(stream, null);
+ }
+ public void ReInit(java.io.InputStream stream, String encoding) {
+ try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+ token_source.ReInit(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 7; i++) jj_la1[i] = -1;
+ }
+
+ public DateTimeParser(java.io.Reader stream) {
+ jj_input_stream = new SimpleCharStream(stream, 1, 1);
+ token_source = new DateTimeParserTokenManager(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 7; i++) jj_la1[i] = -1;
+ }
+
+ public void ReInit(java.io.Reader stream) {
+ jj_input_stream.ReInit(stream, 1, 1);
+ token_source.ReInit(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 7; i++) jj_la1[i] = -1;
+ }
+
+ public DateTimeParser(DateTimeParserTokenManager tm) {
+ token_source = tm;
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 7; i++) jj_la1[i] = -1;
+ }
+
+ public void ReInit(DateTimeParserTokenManager tm) {
+ token_source = tm;
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 7; i++) jj_la1[i] = -1;
+ }
+
+ final private Token jj_consume_token(int kind) throws ParseException {
+ Token oldToken;
+ if ((oldToken = token).next != null) token = token.next;
+ else token = token.next = token_source.getNextToken();
+ jj_ntk = -1;
+ if (token.kind == kind) {
+ jj_gen++;
+ return token;
+ }
+ token = oldToken;
+ jj_kind = kind;
+ throw generateParseException();
+ }
+
+ final public Token getNextToken() {
+ if (token.next != null) token = token.next;
+ else token = token.next = token_source.getNextToken();
+ jj_ntk = -1;
+ jj_gen++;
+ return token;
+ }
+
+ final public Token getToken(int index) {
+ Token t = token;
+ for (int i = 0; i < index; i++) {
+ if (t.next != null) t = t.next;
+ else t = t.next = token_source.getNextToken();
+ }
+ return t;
+ }
+
+ final private int jj_ntk() {
+ if ((jj_nt=token.next) == null)
+ return (jj_ntk = (token.next=token_source.getNextToken()).kind);
+ else
+ return (jj_ntk = jj_nt.kind);
+ }
+
+ private Vector<int[]> jj_expentries = new Vector<int[]>();
+ private int[] jj_expentry;
+ private int jj_kind = -1;
+
+ public ParseException generateParseException() {
+ jj_expentries.removeAllElements();
+ boolean[] la1tokens = new boolean[49];
+ for (int i = 0; i < 49; i++) {
+ la1tokens[i] = false;
+ }
+ if (jj_kind >= 0) {
+ la1tokens[jj_kind] = true;
+ jj_kind = -1;
+ }
+ for (int i = 0; i < 7; i++) {
+ if (jj_la1[i] == jj_gen) {
+ for (int j = 0; j < 32; j++) {
+ if ((jj_la1_0[i] & (1<<j)) != 0) {
+ la1tokens[j] = true;
+ }
+ if ((jj_la1_1[i] & (1<<j)) != 0) {
+ la1tokens[32+j] = true;
+ }
+ }
+ }
+ }
+ for (int i = 0; i < 49; i++) {
+ if (la1tokens[i]) {
+ jj_expentry = new int[1];
+ jj_expentry[0] = i;
+ jj_expentries.addElement(jj_expentry);
+ }
+ }
+ int[][] exptokseq = new int[jj_expentries.size()][];
+ for (int i = 0; i < jj_expentries.size(); i++) {
+ exptokseq[i] = jj_expentries.elementAt(i);
+ }
+ return new ParseException(token, exptokseq, tokenImage);
+ }
+
+ final public void enable_tracing() {
+ }
+
+ final public void disable_tracing() {
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserConstants.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserConstants.java
new file mode 100644
index 000000000..2c203db2e
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserConstants.java
@@ -0,0 +1,86 @@
+/* Generated By:JavaCC: Do not edit this line. DateTimeParserConstants.java */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.datetime.parser;
+
+public interface DateTimeParserConstants {
+
+ int EOF = 0;
+ int OFFSETDIR = 24;
+ int MILITARY_ZONE = 35;
+ int WS = 36;
+ int COMMENT = 38;
+ int DIGITS = 46;
+ int QUOTEDPAIR = 47;
+ int ANY = 48;
+
+ int DEFAULT = 0;
+ int INCOMMENT = 1;
+ int NESTED_COMMENT = 2;
+
+ String[] tokenImage = {
+ "<EOF>",
+ "\"\\r\"",
+ "\"\\n\"",
+ "\",\"",
+ "\"Mon\"",
+ "\"Tue\"",
+ "\"Wed\"",
+ "\"Thu\"",
+ "\"Fri\"",
+ "\"Sat\"",
+ "\"Sun\"",
+ "\"Jan\"",
+ "\"Feb\"",
+ "\"Mar\"",
+ "\"Apr\"",
+ "\"May\"",
+ "\"Jun\"",
+ "\"Jul\"",
+ "\"Aug\"",
+ "\"Sep\"",
+ "\"Oct\"",
+ "\"Nov\"",
+ "\"Dec\"",
+ "\":\"",
+ "<OFFSETDIR>",
+ "\"UT\"",
+ "\"GMT\"",
+ "\"EST\"",
+ "\"EDT\"",
+ "\"CST\"",
+ "\"CDT\"",
+ "\"MST\"",
+ "\"MDT\"",
+ "\"PST\"",
+ "\"PDT\"",
+ "<MILITARY_ZONE>",
+ "<WS>",
+ "\"(\"",
+ "\")\"",
+ "<token of kind 39>",
+ "\"(\"",
+ "<token of kind 41>",
+ "<token of kind 42>",
+ "\"(\"",
+ "\")\"",
+ "<token of kind 45>",
+ "<DIGITS>",
+ "<QUOTEDPAIR>",
+ "<ANY>",
+ };
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserTokenManager.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserTokenManager.java
new file mode 100644
index 000000000..4b2d2fd95
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/DateTimeParserTokenManager.java
@@ -0,0 +1,882 @@
+/* Generated By:JavaCC: Do not edit this line. DateTimeParserTokenManager.java */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.datetime.parser;
+import org.apache.james.mime4j.field.datetime.DateTime;
+import java.util.Calendar;
+
+public class DateTimeParserTokenManager implements DateTimeParserConstants
+{
+ // Keeps track of how many levels of comment nesting
+ // we've encountered. This is only used when the 2nd
+ // level is reached, for example ((this)), not (this).
+ // This is because the outermost level must be treated
+ // specially anyway, because the outermost ")" has a
+ // different token type than inner ")" instances.
+ static int commentNest;
+ public java.io.PrintStream debugStream = System.out;
+ public void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
+private final int jjStopStringLiteralDfa_0(int pos, long active0)
+{
+ switch (pos)
+ {
+ case 0:
+ if ((active0 & 0x7fe7cf7f0L) != 0L)
+ {
+ jjmatchedKind = 35;
+ return -1;
+ }
+ return -1;
+ case 1:
+ if ((active0 & 0x7fe7cf7f0L) != 0L)
+ {
+ if (jjmatchedPos == 0)
+ {
+ jjmatchedKind = 35;
+ jjmatchedPos = 0;
+ }
+ return -1;
+ }
+ return -1;
+ default :
+ return -1;
+ }
+}
+private final int jjStartNfa_0(int pos, long active0)
+{
+ return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1);
+}
+private final int jjStopAtPos(int pos, int kind)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ return pos + 1;
+}
+private final int jjStartNfaWithStates_0(int pos, int kind, int state)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return pos + 1; }
+ return jjMoveNfa_0(state, pos + 1);
+}
+private final int jjMoveStringLiteralDfa0_0()
+{
+ switch(curChar)
+ {
+ case 10:
+ return jjStopAtPos(0, 2);
+ case 13:
+ return jjStopAtPos(0, 1);
+ case 40:
+ return jjStopAtPos(0, 37);
+ case 44:
+ return jjStopAtPos(0, 3);
+ case 58:
+ return jjStopAtPos(0, 23);
+ case 65:
+ return jjMoveStringLiteralDfa1_0(0x44000L);
+ case 67:
+ return jjMoveStringLiteralDfa1_0(0x60000000L);
+ case 68:
+ return jjMoveStringLiteralDfa1_0(0x400000L);
+ case 69:
+ return jjMoveStringLiteralDfa1_0(0x18000000L);
+ case 70:
+ return jjMoveStringLiteralDfa1_0(0x1100L);
+ case 71:
+ return jjMoveStringLiteralDfa1_0(0x4000000L);
+ case 74:
+ return jjMoveStringLiteralDfa1_0(0x30800L);
+ case 77:
+ return jjMoveStringLiteralDfa1_0(0x18000a010L);
+ case 78:
+ return jjMoveStringLiteralDfa1_0(0x200000L);
+ case 79:
+ return jjMoveStringLiteralDfa1_0(0x100000L);
+ case 80:
+ return jjMoveStringLiteralDfa1_0(0x600000000L);
+ case 83:
+ return jjMoveStringLiteralDfa1_0(0x80600L);
+ case 84:
+ return jjMoveStringLiteralDfa1_0(0xa0L);
+ case 85:
+ return jjMoveStringLiteralDfa1_0(0x2000000L);
+ case 87:
+ return jjMoveStringLiteralDfa1_0(0x40L);
+ default :
+ return jjMoveNfa_0(0, 0);
+ }
+}
+private final int jjMoveStringLiteralDfa1_0(long active0)
+{
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) {
+ jjStopStringLiteralDfa_0(0, active0);
+ return 1;
+ }
+ switch(curChar)
+ {
+ case 68:
+ return jjMoveStringLiteralDfa2_0(active0, 0x550000000L);
+ case 77:
+ return jjMoveStringLiteralDfa2_0(active0, 0x4000000L);
+ case 83:
+ return jjMoveStringLiteralDfa2_0(active0, 0x2a8000000L);
+ case 84:
+ if ((active0 & 0x2000000L) != 0L)
+ return jjStopAtPos(1, 25);
+ break;
+ case 97:
+ return jjMoveStringLiteralDfa2_0(active0, 0xaa00L);
+ case 99:
+ return jjMoveStringLiteralDfa2_0(active0, 0x100000L);
+ case 101:
+ return jjMoveStringLiteralDfa2_0(active0, 0x481040L);
+ case 104:
+ return jjMoveStringLiteralDfa2_0(active0, 0x80L);
+ case 111:
+ return jjMoveStringLiteralDfa2_0(active0, 0x200010L);
+ case 112:
+ return jjMoveStringLiteralDfa2_0(active0, 0x4000L);
+ case 114:
+ return jjMoveStringLiteralDfa2_0(active0, 0x100L);
+ case 117:
+ return jjMoveStringLiteralDfa2_0(active0, 0x70420L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(0, active0);
+}
+private final int jjMoveStringLiteralDfa2_0(long old0, long active0)
+{
+ if (((active0 &= old0)) == 0L)
+ return jjStartNfa_0(0, old0);
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) {
+ jjStopStringLiteralDfa_0(1, active0);
+ return 2;
+ }
+ switch(curChar)
+ {
+ case 84:
+ if ((active0 & 0x4000000L) != 0L)
+ return jjStopAtPos(2, 26);
+ else if ((active0 & 0x8000000L) != 0L)
+ return jjStopAtPos(2, 27);
+ else if ((active0 & 0x10000000L) != 0L)
+ return jjStopAtPos(2, 28);
+ else if ((active0 & 0x20000000L) != 0L)
+ return jjStopAtPos(2, 29);
+ else if ((active0 & 0x40000000L) != 0L)
+ return jjStopAtPos(2, 30);
+ else if ((active0 & 0x80000000L) != 0L)
+ return jjStopAtPos(2, 31);
+ else if ((active0 & 0x100000000L) != 0L)
+ return jjStopAtPos(2, 32);
+ else if ((active0 & 0x200000000L) != 0L)
+ return jjStopAtPos(2, 33);
+ else if ((active0 & 0x400000000L) != 0L)
+ return jjStopAtPos(2, 34);
+ break;
+ case 98:
+ if ((active0 & 0x1000L) != 0L)
+ return jjStopAtPos(2, 12);
+ break;
+ case 99:
+ if ((active0 & 0x400000L) != 0L)
+ return jjStopAtPos(2, 22);
+ break;
+ case 100:
+ if ((active0 & 0x40L) != 0L)
+ return jjStopAtPos(2, 6);
+ break;
+ case 101:
+ if ((active0 & 0x20L) != 0L)
+ return jjStopAtPos(2, 5);
+ break;
+ case 103:
+ if ((active0 & 0x40000L) != 0L)
+ return jjStopAtPos(2, 18);
+ break;
+ case 105:
+ if ((active0 & 0x100L) != 0L)
+ return jjStopAtPos(2, 8);
+ break;
+ case 108:
+ if ((active0 & 0x20000L) != 0L)
+ return jjStopAtPos(2, 17);
+ break;
+ case 110:
+ if ((active0 & 0x10L) != 0L)
+ return jjStopAtPos(2, 4);
+ else if ((active0 & 0x400L) != 0L)
+ return jjStopAtPos(2, 10);
+ else if ((active0 & 0x800L) != 0L)
+ return jjStopAtPos(2, 11);
+ else if ((active0 & 0x10000L) != 0L)
+ return jjStopAtPos(2, 16);
+ break;
+ case 112:
+ if ((active0 & 0x80000L) != 0L)
+ return jjStopAtPos(2, 19);
+ break;
+ case 114:
+ if ((active0 & 0x2000L) != 0L)
+ return jjStopAtPos(2, 13);
+ else if ((active0 & 0x4000L) != 0L)
+ return jjStopAtPos(2, 14);
+ break;
+ case 116:
+ if ((active0 & 0x200L) != 0L)
+ return jjStopAtPos(2, 9);
+ else if ((active0 & 0x100000L) != 0L)
+ return jjStopAtPos(2, 20);
+ break;
+ case 117:
+ if ((active0 & 0x80L) != 0L)
+ return jjStopAtPos(2, 7);
+ break;
+ case 118:
+ if ((active0 & 0x200000L) != 0L)
+ return jjStopAtPos(2, 21);
+ break;
+ case 121:
+ if ((active0 & 0x8000L) != 0L)
+ return jjStopAtPos(2, 15);
+ break;
+ default :
+ break;
+ }
+ return jjStartNfa_0(1, active0);
+}
+private final void jjCheckNAdd(int state)
+{
+ if (jjrounds[state] != jjround)
+ {
+ jjstateSet[jjnewStateCnt++] = state;
+ jjrounds[state] = jjround;
+ }
+}
+private final void jjAddStates(int start, int end)
+{
+ do {
+ jjstateSet[jjnewStateCnt++] = jjnextStates[start];
+ } while (start++ != end);
+}
+private final void jjCheckNAddTwoStates(int state1, int state2)
+{
+ jjCheckNAdd(state1);
+ jjCheckNAdd(state2);
+}
+private final void jjCheckNAddStates(int start, int end)
+{
+ do {
+ jjCheckNAdd(jjnextStates[start]);
+ } while (start++ != end);
+}
+private final void jjCheckNAddStates(int start)
+{
+ jjCheckNAdd(jjnextStates[start]);
+ jjCheckNAdd(jjnextStates[start + 1]);
+}
+private final int jjMoveNfa_0(int startState, int curPos)
+{
+ int[] nextStates;
+ int startsAt = 0;
+ jjnewStateCnt = 4;
+ int i = 1;
+ jjstateSet[0] = startState;
+ int j, kind = 0x7fffffff;
+ for (;;)
+ {
+ if (++jjround == 0x7fffffff)
+ ReInitRounds();
+ if (curChar < 64)
+ {
+ long l = 1L << curChar;
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if ((0x3ff000000000000L & l) != 0L)
+ {
+ if (kind > 46)
+ kind = 46;
+ jjCheckNAdd(3);
+ }
+ else if ((0x100000200L & l) != 0L)
+ {
+ if (kind > 36)
+ kind = 36;
+ jjCheckNAdd(2);
+ }
+ else if ((0x280000000000L & l) != 0L)
+ {
+ if (kind > 24)
+ kind = 24;
+ }
+ break;
+ case 2:
+ if ((0x100000200L & l) == 0L)
+ break;
+ kind = 36;
+ jjCheckNAdd(2);
+ break;
+ case 3:
+ if ((0x3ff000000000000L & l) == 0L)
+ break;
+ kind = 46;
+ jjCheckNAdd(3);
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else if (curChar < 128)
+ {
+ long l = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if ((0x7fffbfe07fffbfeL & l) != 0L)
+ kind = 35;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else
+ {
+ int i2 = (curChar & 0xff) >> 6;
+ long l2 = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ if (kind != 0x7fffffff)
+ {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 4 - (jjnewStateCnt = startsAt)))
+ return curPos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return curPos; }
+ }
+}
+private final int jjStopStringLiteralDfa_1(int pos, long active0)
+{
+ switch (pos)
+ {
+ default :
+ return -1;
+ }
+}
+private final int jjStartNfa_1(int pos, long active0)
+{
+ return jjMoveNfa_1(jjStopStringLiteralDfa_1(pos, active0), pos + 1);
+}
+private final int jjStartNfaWithStates_1(int pos, int kind, int state)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return pos + 1; }
+ return jjMoveNfa_1(state, pos + 1);
+}
+private final int jjMoveStringLiteralDfa0_1()
+{
+ switch(curChar)
+ {
+ case 40:
+ return jjStopAtPos(0, 40);
+ case 41:
+ return jjStopAtPos(0, 38);
+ default :
+ return jjMoveNfa_1(0, 0);
+ }
+}
+static final long[] jjbitVec0 = {
+ 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+private final int jjMoveNfa_1(int startState, int curPos)
+{
+ int[] nextStates;
+ int startsAt = 0;
+ jjnewStateCnt = 3;
+ int i = 1;
+ jjstateSet[0] = startState;
+ int j, kind = 0x7fffffff;
+ for (;;)
+ {
+ if (++jjround == 0x7fffffff)
+ ReInitRounds();
+ if (curChar < 64)
+ {
+ long l = 1L << curChar;
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if (kind > 41)
+ kind = 41;
+ break;
+ case 1:
+ if (kind > 39)
+ kind = 39;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else if (curChar < 128)
+ {
+ long l = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if (kind > 41)
+ kind = 41;
+ if (curChar == 92)
+ jjstateSet[jjnewStateCnt++] = 1;
+ break;
+ case 1:
+ if (kind > 39)
+ kind = 39;
+ break;
+ case 2:
+ if (kind > 41)
+ kind = 41;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else
+ {
+ int i2 = (curChar & 0xff) >> 6;
+ long l2 = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 41)
+ kind = 41;
+ break;
+ case 1:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 39)
+ kind = 39;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ if (kind != 0x7fffffff)
+ {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+ return curPos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return curPos; }
+ }
+}
+private final int jjStopStringLiteralDfa_2(int pos, long active0)
+{
+ switch (pos)
+ {
+ default :
+ return -1;
+ }
+}
+private final int jjStartNfa_2(int pos, long active0)
+{
+ return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0), pos + 1);
+}
+private final int jjStartNfaWithStates_2(int pos, int kind, int state)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return pos + 1; }
+ return jjMoveNfa_2(state, pos + 1);
+}
+private final int jjMoveStringLiteralDfa0_2()
+{
+ switch(curChar)
+ {
+ case 40:
+ return jjStopAtPos(0, 43);
+ case 41:
+ return jjStopAtPos(0, 44);
+ default :
+ return jjMoveNfa_2(0, 0);
+ }
+}
+private final int jjMoveNfa_2(int startState, int curPos)
+{
+ int[] nextStates;
+ int startsAt = 0;
+ jjnewStateCnt = 3;
+ int i = 1;
+ jjstateSet[0] = startState;
+ int j, kind = 0x7fffffff;
+ for (;;)
+ {
+ if (++jjround == 0x7fffffff)
+ ReInitRounds();
+ if (curChar < 64)
+ {
+ long l = 1L << curChar;
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if (kind > 45)
+ kind = 45;
+ break;
+ case 1:
+ if (kind > 42)
+ kind = 42;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else if (curChar < 128)
+ {
+ long l = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if (kind > 45)
+ kind = 45;
+ if (curChar == 92)
+ jjstateSet[jjnewStateCnt++] = 1;
+ break;
+ case 1:
+ if (kind > 42)
+ kind = 42;
+ break;
+ case 2:
+ if (kind > 45)
+ kind = 45;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else
+ {
+ int i2 = (curChar & 0xff) >> 6;
+ long l2 = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 45)
+ kind = 45;
+ break;
+ case 1:
+ if ((jjbitVec0[i2] & l2) != 0L && kind > 42)
+ kind = 42;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ if (kind != 0x7fffffff)
+ {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt)))
+ return curPos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return curPos; }
+ }
+}
+static final int[] jjnextStates = {
+};
+public static final String[] jjstrLiteralImages = {
+"", "\15", "\12", "\54", "\115\157\156", "\124\165\145", "\127\145\144",
+"\124\150\165", "\106\162\151", "\123\141\164", "\123\165\156", "\112\141\156",
+"\106\145\142", "\115\141\162", "\101\160\162", "\115\141\171", "\112\165\156",
+"\112\165\154", "\101\165\147", "\123\145\160", "\117\143\164", "\116\157\166",
+"\104\145\143", "\72", null, "\125\124", "\107\115\124", "\105\123\124", "\105\104\124",
+"\103\123\124", "\103\104\124", "\115\123\124", "\115\104\124", "\120\123\124",
+"\120\104\124", null, null, null, null, null, null, null, null, null, null, null, null, null,
+null, };
+public static final String[] lexStateNames = {
+ "DEFAULT",
+ "INCOMMENT",
+ "NESTED_COMMENT",
+};
+public static final int[] jjnewLexState = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 0, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+static final long[] jjtoToken = {
+ 0x400fffffffffL,
+};
+static final long[] jjtoSkip = {
+ 0x5000000000L,
+};
+static final long[] jjtoSpecial = {
+ 0x1000000000L,
+};
+static final long[] jjtoMore = {
+ 0x3fa000000000L,
+};
+protected SimpleCharStream input_stream;
+private final int[] jjrounds = new int[4];
+private final int[] jjstateSet = new int[8];
+StringBuffer image;
+int jjimageLen;
+int lengthOfMatch;
+protected char curChar;
+public DateTimeParserTokenManager(SimpleCharStream stream){
+ if (SimpleCharStream.staticFlag)
+ throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+ input_stream = stream;
+}
+public DateTimeParserTokenManager(SimpleCharStream stream, int lexState){
+ this(stream);
+ SwitchTo(lexState);
+}
+public void ReInit(SimpleCharStream stream)
+{
+ jjmatchedPos = jjnewStateCnt = 0;
+ curLexState = defaultLexState;
+ input_stream = stream;
+ ReInitRounds();
+}
+private final void ReInitRounds()
+{
+ int i;
+ jjround = 0x80000001;
+ for (i = 4; i-- > 0;)
+ jjrounds[i] = 0x80000000;
+}
+public void ReInit(SimpleCharStream stream, int lexState)
+{
+ ReInit(stream);
+ SwitchTo(lexState);
+}
+public void SwitchTo(int lexState)
+{
+ if (lexState >= 3 || lexState < 0)
+ throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE);
+ else
+ curLexState = lexState;
+}
+
+protected Token jjFillToken()
+{
+ Token t = Token.newToken(jjmatchedKind);
+ t.kind = jjmatchedKind;
+ String im = jjstrLiteralImages[jjmatchedKind];
+ t.image = (im == null) ? input_stream.GetImage() : im;
+ t.beginLine = input_stream.getBeginLine();
+ t.beginColumn = input_stream.getBeginColumn();
+ t.endLine = input_stream.getEndLine();
+ t.endColumn = input_stream.getEndColumn();
+ return t;
+}
+
+int curLexState = 0;
+int defaultLexState = 0;
+int jjnewStateCnt;
+int jjround;
+int jjmatchedPos;
+int jjmatchedKind;
+
+public Token getNextToken()
+{
+ int kind;
+ Token specialToken = null;
+ Token matchedToken;
+ int curPos = 0;
+
+ EOFLoop :
+ for (;;)
+ {
+ try
+ {
+ curChar = input_stream.BeginToken();
+ }
+ catch(java.io.IOException e)
+ {
+ jjmatchedKind = 0;
+ matchedToken = jjFillToken();
+ matchedToken.specialToken = specialToken;
+ return matchedToken;
+ }
+ image = null;
+ jjimageLen = 0;
+
+ for (;;)
+ {
+ switch(curLexState)
+ {
+ case 0:
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_0();
+ break;
+ case 1:
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_1();
+ break;
+ case 2:
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_2();
+ break;
+ }
+ if (jjmatchedKind != 0x7fffffff)
+ {
+ if (jjmatchedPos + 1 < curPos)
+ input_stream.backup(curPos - jjmatchedPos - 1);
+ if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+ {
+ matchedToken = jjFillToken();
+ matchedToken.specialToken = specialToken;
+ if (jjnewLexState[jjmatchedKind] != -1)
+ curLexState = jjnewLexState[jjmatchedKind];
+ return matchedToken;
+ }
+ else if ((jjtoSkip[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+ {
+ if ((jjtoSpecial[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
+ {
+ matchedToken = jjFillToken();
+ if (specialToken == null)
+ specialToken = matchedToken;
+ else
+ {
+ matchedToken.specialToken = specialToken;
+ specialToken = (specialToken.next = matchedToken);
+ }
+ }
+ if (jjnewLexState[jjmatchedKind] != -1)
+ curLexState = jjnewLexState[jjmatchedKind];
+ continue EOFLoop;
+ }
+ MoreLexicalActions();
+ if (jjnewLexState[jjmatchedKind] != -1)
+ curLexState = jjnewLexState[jjmatchedKind];
+ curPos = 0;
+ jjmatchedKind = 0x7fffffff;
+ try {
+ curChar = input_stream.readChar();
+ continue;
+ }
+ catch (java.io.IOException e1) { }
+ }
+ int error_line = input_stream.getEndLine();
+ int error_column = input_stream.getEndColumn();
+ String error_after = null;
+ boolean EOFSeen = false;
+ try { input_stream.readChar(); input_stream.backup(1); }
+ catch (java.io.IOException e1) {
+ EOFSeen = true;
+ error_after = curPos <= 1 ? "" : input_stream.GetImage();
+ if (curChar == '\n' || curChar == '\r') {
+ error_line++;
+ error_column = 0;
+ }
+ else
+ error_column++;
+ }
+ if (!EOFSeen) {
+ input_stream.backup(1);
+ error_after = curPos <= 1 ? "" : input_stream.GetImage();
+ }
+ throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR);
+ }
+ }
+}
+
+void MoreLexicalActions()
+{
+ jjimageLen += (lengthOfMatch = jjmatchedPos + 1);
+ switch(jjmatchedKind)
+ {
+ case 39 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ image.deleteCharAt(image.length() - 2);
+ break;
+ case 40 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ commentNest = 1;
+ break;
+ case 42 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ image.deleteCharAt(image.length() - 2);
+ break;
+ case 43 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ ++commentNest;
+ break;
+ case 44 :
+ if (image == null)
+ image = new StringBuffer();
+ image.append(input_stream.GetSuffix(jjimageLen));
+ jjimageLen = 0;
+ --commentNest; if (commentNest == 0) SwitchTo(INCOMMENT);
+ break;
+ default :
+ break;
+ }
+}
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/ParseException.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/ParseException.java
new file mode 100644
index 000000000..13b3ff097
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/ParseException.java
@@ -0,0 +1,207 @@
+/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 3.0 */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.datetime.parser;
+
+/**
+ * This exception is thrown when parse errors are encountered.
+ * You can explicitly create objects of this exception type by
+ * calling the method generateParseException in the generated
+ * parser.
+ *
+ * You can modify this class to customize your error reporting
+ * mechanisms so long as you retain the public fields.
+ */
+public class ParseException extends Exception {
+
+ /**
+ * This constructor is used by the method "generateParseException"
+ * in the generated parser. Calling this constructor generates
+ * a new object of this type with the fields "currentToken",
+ * "expectedTokenSequences", and "tokenImage" set. The boolean
+ * flag "specialConstructor" is also set to true to indicate that
+ * this constructor was used to create this object.
+ * This constructor calls its super class with the empty string
+ * to force the "toString" method of parent class "Throwable" to
+ * print the error message in the form:
+ * ParseException: <result of getMessage>
+ */
+ public ParseException(Token currentTokenVal,
+ int[][] expectedTokenSequencesVal,
+ String[] tokenImageVal
+ )
+ {
+ super("");
+ specialConstructor = true;
+ currentToken = currentTokenVal;
+ expectedTokenSequences = expectedTokenSequencesVal;
+ tokenImage = tokenImageVal;
+ }
+
+ /**
+ * The following constructors are for use by you for whatever
+ * purpose you can think of. Constructing the exception in this
+ * manner makes the exception behave in the normal way - i.e., as
+ * documented in the class "Throwable". The fields "errorToken",
+ * "expectedTokenSequences", and "tokenImage" do not contain
+ * relevant information. The JavaCC generated code does not use
+ * these constructors.
+ */
+
+ public ParseException() {
+ super();
+ specialConstructor = false;
+ }
+
+ public ParseException(String message) {
+ super(message);
+ specialConstructor = false;
+ }
+
+ /**
+ * This variable determines which constructor was used to create
+ * this object and thereby affects the semantics of the
+ * "getMessage" method (see below).
+ */
+ protected boolean specialConstructor;
+
+ /**
+ * This is the last token that has been consumed successfully. If
+ * this object has been created due to a parse error, the token
+ * followng this token will (therefore) be the first error token.
+ */
+ public Token currentToken;
+
+ /**
+ * Each entry in this array is an array of integers. Each array
+ * of integers represents a sequence of tokens (by their ordinal
+ * values) that is expected at this point of the parse.
+ */
+ public int[][] expectedTokenSequences;
+
+ /**
+ * This is a reference to the "tokenImage" array of the generated
+ * parser within which the parse error occurred. This array is
+ * defined in the generated ...Constants interface.
+ */
+ public String[] tokenImage;
+
+ /**
+ * This method has the standard behavior when this object has been
+ * created using the standard constructors. Otherwise, it uses
+ * "currentToken" and "expectedTokenSequences" to generate a parse
+ * error message and returns it. If this object has been created
+ * due to a parse error, and you do not catch it (it gets thrown
+ * from the parser), then this method is called during the printing
+ * of the final stack trace, and hence the correct error message
+ * gets displayed.
+ */
+ public String getMessage() {
+ if (!specialConstructor) {
+ return super.getMessage();
+ }
+ StringBuffer expected = new StringBuffer();
+ int maxSize = 0;
+ for (int i = 0; i < expectedTokenSequences.length; i++) {
+ if (maxSize < expectedTokenSequences[i].length) {
+ maxSize = expectedTokenSequences[i].length;
+ }
+ for (int j = 0; j < expectedTokenSequences[i].length; j++) {
+ expected.append(tokenImage[expectedTokenSequences[i][j]]).append(" ");
+ }
+ if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
+ expected.append("...");
+ }
+ expected.append(eol).append(" ");
+ }
+ String retval = "Encountered \"";
+ Token tok = currentToken.next;
+ for (int i = 0; i < maxSize; i++) {
+ if (i != 0) retval += " ";
+ if (tok.kind == 0) {
+ retval += tokenImage[0];
+ break;
+ }
+ retval += add_escapes(tok.image);
+ tok = tok.next;
+ }
+ retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+ retval += "." + eol;
+ if (expectedTokenSequences.length == 1) {
+ retval += "Was expecting:" + eol + " ";
+ } else {
+ retval += "Was expecting one of:" + eol + " ";
+ }
+ retval += expected.toString();
+ return retval;
+ }
+
+ /**
+ * The end of line string for this machine.
+ */
+ protected String eol = System.getProperty("line.separator", "\n");
+
+ /**
+ * Used to convert raw characters to their escaped version
+ * when these raw version cannot be used as part of an ASCII
+ * string literal.
+ */
+ protected String add_escapes(String str) {
+ StringBuffer retval = new StringBuffer();
+ char ch;
+ for (int i = 0; i < str.length(); i++) {
+ switch (str.charAt(i))
+ {
+ case 0 :
+ continue;
+ case '\b':
+ retval.append("\\b");
+ continue;
+ case '\t':
+ retval.append("\\t");
+ continue;
+ case '\n':
+ retval.append("\\n");
+ continue;
+ case '\f':
+ retval.append("\\f");
+ continue;
+ case '\r':
+ retval.append("\\r");
+ continue;
+ case '\"':
+ retval.append("\\\"");
+ continue;
+ case '\'':
+ retval.append("\\\'");
+ continue;
+ case '\\':
+ retval.append("\\\\");
+ continue;
+ default:
+ if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+ String s = "0000" + Integer.toString(ch, 16);
+ retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+ } else {
+ retval.append(ch);
+ }
+ continue;
+ }
+ }
+ return retval.toString();
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/SimpleCharStream.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/SimpleCharStream.java
new file mode 100644
index 000000000..2724529f7
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/SimpleCharStream.java
@@ -0,0 +1,454 @@
+/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 4.0 */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.datetime.parser;
+
+/**
+ * An implementation of interface CharStream, where the stream is assumed to
+ * contain only ASCII characters (without unicode processing).
+ */
+
+public class SimpleCharStream
+{
+ public static final boolean staticFlag = false;
+ int bufsize;
+ int available;
+ int tokenBegin;
+ public int bufpos = -1;
+ protected int bufline[];
+ protected int bufcolumn[];
+
+ protected int column = 0;
+ protected int line = 1;
+
+ protected boolean prevCharIsCR = false;
+ protected boolean prevCharIsLF = false;
+
+ protected java.io.Reader inputStream;
+
+ protected char[] buffer;
+ protected int maxNextCharInd = 0;
+ protected int inBuf = 0;
+ protected int tabSize = 8;
+
+ protected void setTabSize(int i) { tabSize = i; }
+ protected int getTabSize(int i) { return tabSize; }
+
+
+ protected void ExpandBuff(boolean wrapAround)
+ {
+ char[] newbuffer = new char[bufsize + 2048];
+ int newbufline[] = new int[bufsize + 2048];
+ int newbufcolumn[] = new int[bufsize + 2048];
+
+ try
+ {
+ if (wrapAround)
+ {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ System.arraycopy(buffer, 0, newbuffer,
+ bufsize - tokenBegin, bufpos);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos);
+ bufcolumn = newbufcolumn;
+
+ maxNextCharInd = (bufpos += (bufsize - tokenBegin));
+ }
+ else
+ {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ bufcolumn = newbufcolumn;
+
+ maxNextCharInd = (bufpos -= tokenBegin);
+ }
+ }
+ catch (Throwable t)
+ {
+ throw new Error(t.getMessage());
+ }
+
+
+ bufsize += 2048;
+ available = bufsize;
+ tokenBegin = 0;
+ }
+
+ protected void FillBuff() throws java.io.IOException
+ {
+ if (maxNextCharInd == available)
+ {
+ if (available == bufsize)
+ {
+ if (tokenBegin > 2048)
+ {
+ bufpos = maxNextCharInd = 0;
+ available = tokenBegin;
+ }
+ else if (tokenBegin < 0)
+ bufpos = maxNextCharInd = 0;
+ else
+ ExpandBuff(false);
+ }
+ else if (available > tokenBegin)
+ available = bufsize;
+ else if ((tokenBegin - available) < 2048)
+ ExpandBuff(true);
+ else
+ available = tokenBegin;
+ }
+
+ int i;
+ try {
+ if ((i = inputStream.read(buffer, maxNextCharInd,
+ available - maxNextCharInd)) == -1)
+ {
+ inputStream.close();
+ throw new java.io.IOException();
+ }
+ else
+ maxNextCharInd += i;
+ return;
+ }
+ catch(java.io.IOException e) {
+ --bufpos;
+ backup(0);
+ if (tokenBegin == -1)
+ tokenBegin = bufpos;
+ throw e;
+ }
+ }
+
+ public char BeginToken() throws java.io.IOException
+ {
+ tokenBegin = -1;
+ char c = readChar();
+ tokenBegin = bufpos;
+
+ return c;
+ }
+
+ protected void UpdateLineColumn(char c)
+ {
+ column++;
+
+ if (prevCharIsLF)
+ {
+ prevCharIsLF = false;
+ line += (column = 1);
+ }
+ else if (prevCharIsCR)
+ {
+ prevCharIsCR = false;
+ if (c == '\n')
+ {
+ prevCharIsLF = true;
+ }
+ else
+ line += (column = 1);
+ }
+
+ switch (c)
+ {
+ case '\r' :
+ prevCharIsCR = true;
+ break;
+ case '\n' :
+ prevCharIsLF = true;
+ break;
+ case '\t' :
+ column--;
+ column += (tabSize - (column % tabSize));
+ break;
+ default :
+ break;
+ }
+
+ bufline[bufpos] = line;
+ bufcolumn[bufpos] = column;
+ }
+
+ public char readChar() throws java.io.IOException
+ {
+ if (inBuf > 0)
+ {
+ --inBuf;
+
+ if (++bufpos == bufsize)
+ bufpos = 0;
+
+ return buffer[bufpos];
+ }
+
+ if (++bufpos >= maxNextCharInd)
+ FillBuff();
+
+ char c = buffer[bufpos];
+
+ UpdateLineColumn(c);
+ return (c);
+ }
+
+ /**
+ * @deprecated
+ * @see #getEndColumn
+ */
+ @Deprecated
+ public int getColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ /**
+ * @deprecated
+ * @see #getEndLine
+ */
+ @Deprecated
+ public int getLine() {
+ return bufline[bufpos];
+ }
+
+ public int getEndColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ public int getEndLine() {
+ return bufline[bufpos];
+ }
+
+ public int getBeginColumn() {
+ return bufcolumn[tokenBegin];
+ }
+
+ public int getBeginLine() {
+ return bufline[tokenBegin];
+ }
+
+ public void backup(int amount) {
+
+ inBuf += amount;
+ if ((bufpos -= amount) < 0)
+ bufpos += bufsize;
+ }
+
+ public SimpleCharStream(java.io.Reader dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ inputStream = dstream;
+ line = startline;
+ column = startcolumn - 1;
+
+ available = bufsize = buffersize;
+ buffer = new char[buffersize];
+ bufline = new int[buffersize];
+ bufcolumn = new int[buffersize];
+ }
+
+ public SimpleCharStream(java.io.Reader dstream, int startline,
+ int startcolumn)
+ {
+ this(dstream, startline, startcolumn, 4096);
+ }
+
+ public SimpleCharStream(java.io.Reader dstream)
+ {
+ this(dstream, 1, 1, 4096);
+ }
+ public void ReInit(java.io.Reader dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ inputStream = dstream;
+ line = startline;
+ column = startcolumn - 1;
+
+ if (buffer == null || buffersize != buffer.length)
+ {
+ available = bufsize = buffersize;
+ buffer = new char[buffersize];
+ bufline = new int[buffersize];
+ bufcolumn = new int[buffersize];
+ }
+ prevCharIsLF = prevCharIsCR = false;
+ tokenBegin = inBuf = maxNextCharInd = 0;
+ bufpos = -1;
+ }
+
+ public void ReInit(java.io.Reader dstream, int startline,
+ int startcolumn)
+ {
+ ReInit(dstream, startline, startcolumn, 4096);
+ }
+
+ public void ReInit(java.io.Reader dstream)
+ {
+ ReInit(dstream, 1, 1, 4096);
+ }
+ public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+ {
+ this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+ }
+
+ public SimpleCharStream(java.io.InputStream dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+ }
+
+ public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn) throws java.io.UnsupportedEncodingException
+ {
+ this(dstream, encoding, startline, startcolumn, 4096);
+ }
+
+ public SimpleCharStream(java.io.InputStream dstream, int startline,
+ int startcolumn)
+ {
+ this(dstream, startline, startcolumn, 4096);
+ }
+
+ public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+ {
+ this(dstream, encoding, 1, 1, 4096);
+ }
+
+ public SimpleCharStream(java.io.InputStream dstream)
+ {
+ this(dstream, 1, 1, 4096);
+ }
+
+ public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+ {
+ ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+ }
+
+ public void ReInit(java.io.InputStream dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+ }
+
+ public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+ {
+ ReInit(dstream, encoding, 1, 1, 4096);
+ }
+
+ public void ReInit(java.io.InputStream dstream)
+ {
+ ReInit(dstream, 1, 1, 4096);
+ }
+ public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn) throws java.io.UnsupportedEncodingException
+ {
+ ReInit(dstream, encoding, startline, startcolumn, 4096);
+ }
+ public void ReInit(java.io.InputStream dstream, int startline,
+ int startcolumn)
+ {
+ ReInit(dstream, startline, startcolumn, 4096);
+ }
+ public String GetImage()
+ {
+ if (bufpos >= tokenBegin)
+ return new String(buffer, tokenBegin, bufpos - tokenBegin + 1);
+ else
+ return new String(buffer, tokenBegin, bufsize - tokenBegin) +
+ new String(buffer, 0, bufpos + 1);
+ }
+
+ public char[] GetSuffix(int len)
+ {
+ char[] ret = new char[len];
+
+ if ((bufpos + 1) >= len)
+ System.arraycopy(buffer, bufpos - len + 1, ret, 0, len);
+ else
+ {
+ System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0,
+ len - bufpos - 1);
+ System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1);
+ }
+
+ return ret;
+ }
+
+ public void Done()
+ {
+ buffer = null;
+ bufline = null;
+ bufcolumn = null;
+ }
+
+ /**
+ * Method to adjust line and column numbers for the start of a token.
+ */
+ public void adjustBeginLineColumn(int newLine, int newCol)
+ {
+ int start = tokenBegin;
+ int len;
+
+ if (bufpos >= tokenBegin)
+ {
+ len = bufpos - tokenBegin + inBuf + 1;
+ }
+ else
+ {
+ len = bufsize - tokenBegin + bufpos + 1 + inBuf;
+ }
+
+ int i = 0, j = 0, k = 0;
+ int nextColDiff = 0, columnDiff = 0;
+
+ while (i < len &&
+ bufline[j = start % bufsize] == bufline[k = ++start % bufsize])
+ {
+ bufline[j] = newLine;
+ nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j];
+ bufcolumn[j] = newCol + columnDiff;
+ columnDiff = nextColDiff;
+ i++;
+ }
+
+ if (i < len)
+ {
+ bufline[j] = newLine++;
+ bufcolumn[j] = newCol + columnDiff;
+
+ while (i++ < len)
+ {
+ if (bufline[j = start % bufsize] != bufline[++start % bufsize])
+ bufline[j] = newLine++;
+ else
+ bufline[j] = newLine;
+ }
+ }
+
+ line = bufline[j];
+ column = bufcolumn[j];
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/Token.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/Token.java
new file mode 100644
index 000000000..0927a0921
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/Token.java
@@ -0,0 +1,96 @@
+/* Generated By:JavaCC: Do not edit this line. Token.java Version 3.0 */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.datetime.parser;
+
+/**
+ * Describes the input token stream.
+ */
+
+public class Token {
+
+ /**
+ * An integer that describes the kind of this token. This numbering
+ * system is determined by JavaCCParser, and a table of these numbers is
+ * stored in the file ...Constants.java.
+ */
+ public int kind;
+
+ /**
+ * beginLine and beginColumn describe the position of the first character
+ * of this token; endLine and endColumn describe the position of the
+ * last character of this token.
+ */
+ public int beginLine, beginColumn, endLine, endColumn;
+
+ /**
+ * The string image of the token.
+ */
+ public String image;
+
+ /**
+ * A reference to the next regular (non-special) token from the input
+ * stream. If this is the last token from the input stream, or if the
+ * token manager has not read tokens beyond this one, this field is
+ * set to null. This is true only if this token is also a regular
+ * token. Otherwise, see below for a description of the contents of
+ * this field.
+ */
+ public Token next;
+
+ /**
+ * This field is used to access special tokens that occur prior to this
+ * token, but after the immediately preceding regular (non-special) token.
+ * If there are no such special tokens, this field is set to null.
+ * When there are more than one such special token, this field refers
+ * to the last of these special tokens, which in turn refers to the next
+ * previous special token through its specialToken field, and so on
+ * until the first special token (whose specialToken field is null).
+ * The next fields of special tokens refer to other special tokens that
+ * immediately follow it (without an intervening regular token). If there
+ * is no such token, this field is null.
+ */
+ public Token specialToken;
+
+ /**
+ * Returns the image.
+ */
+ public String toString()
+ {
+ return image;
+ }
+
+ /**
+ * Returns a new Token object, by default. However, if you want, you
+ * can create and return subclass objects based on the value of ofKind.
+ * Simply add the cases to the switch for all those special cases.
+ * For example, if you have a subclass of Token called IDToken that
+ * you want to create if ofKind is ID, simlpy add something like :
+ *
+ * case MyParserConstants.ID : return new IDToken();
+ *
+ * to the following switch statement. Then you can cast matchedToken
+ * variable to the appropriate type and use it in your lexical actions.
+ */
+ public static final Token newToken(int ofKind)
+ {
+ switch(ofKind)
+ {
+ default : return new Token();
+ }
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/TokenMgrError.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/TokenMgrError.java
new file mode 100644
index 000000000..e7043c1b7
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/field/datetime/parser/TokenMgrError.java
@@ -0,0 +1,148 @@
+/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 3.0 */
+/*
+ * Copyright 2004 the mime4j 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 org.apache.james.mime4j.field.datetime.parser;
+
+public class TokenMgrError extends Error
+{
+ /*
+ * Ordinals for various reasons why an Error of this type can be thrown.
+ */
+
+ /**
+ * Lexical error occured.
+ */
+ static final int LEXICAL_ERROR = 0;
+
+ /**
+ * An attempt wass made to create a second instance of a static token manager.
+ */
+ static final int STATIC_LEXER_ERROR = 1;
+
+ /**
+ * Tried to change to an invalid lexical state.
+ */
+ static final int INVALID_LEXICAL_STATE = 2;
+
+ /**
+ * Detected (and bailed out of) an infinite loop in the token manager.
+ */
+ static final int LOOP_DETECTED = 3;
+
+ /**
+ * Indicates the reason why the exception is thrown. It will have
+ * one of the above 4 values.
+ */
+ int errorCode;
+
+ /**
+ * Replaces unprintable characters by their espaced (or unicode escaped)
+ * equivalents in the given string
+ */
+ protected static final String addEscapes(String str) {
+ StringBuffer retval = new StringBuffer();
+ char ch;
+ for (int i = 0; i < str.length(); i++) {
+ switch (str.charAt(i))
+ {
+ case 0 :
+ continue;
+ case '\b':
+ retval.append("\\b");
+ continue;
+ case '\t':
+ retval.append("\\t");
+ continue;
+ case '\n':
+ retval.append("\\n");
+ continue;
+ case '\f':
+ retval.append("\\f");
+ continue;
+ case '\r':
+ retval.append("\\r");
+ continue;
+ case '\"':
+ retval.append("\\\"");
+ continue;
+ case '\'':
+ retval.append("\\\'");
+ continue;
+ case '\\':
+ retval.append("\\\\");
+ continue;
+ default:
+ if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+ String s = "0000" + Integer.toString(ch, 16);
+ retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+ } else {
+ retval.append(ch);
+ }
+ continue;
+ }
+ }
+ return retval.toString();
+ }
+
+ /**
+ * Returns a detailed message for the Error when it is thrown by the
+ * token manager to indicate a lexical error.
+ * Parameters :
+ * EOFSeen : indicates if EOF caused the lexicl error
+ * curLexState : lexical state in which this error occured
+ * errorLine : line number when the error occured
+ * errorColumn : column number when the error occured
+ * errorAfter : prefix that was seen before this error occured
+ * curchar : the offending character
+ * Note: You can customize the lexical error message by modifying this method.
+ */
+ protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) {
+ return("Lexical error at line " +
+ errorLine + ", column " +
+ errorColumn + ". Encountered: " +
+ (EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") +
+ "after : \"" + addEscapes(errorAfter) + "\"");
+ }
+
+ /**
+ * You can also modify the body of this method to customize your error messages.
+ * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not
+ * of end-users concern, so you can return something like :
+ *
+ * "Internal Error : Please file a bug report .... "
+ *
+ * from this method for such cases in the release version of your parser.
+ */
+ public String getMessage() {
+ return super.getMessage();
+ }
+
+ /*
+ * Constructors of various flavors follow.
+ */
+
+ public TokenMgrError() {
+ }
+
+ public TokenMgrError(String message, int reason) {
+ super(message);
+ errorCode = reason;
+ }
+
+ public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) {
+ this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
+ }
+}
diff --git a/java/com/android/voicemailomtp/src/org/apache/james/mime4j/util/CharsetUtil.java b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/util/CharsetUtil.java
new file mode 100644
index 000000000..4e712fcdd
--- /dev/null
+++ b/java/com/android/voicemailomtp/src/org/apache/james/mime4j/util/CharsetUtil.java
@@ -0,0 +1,1249 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you 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 org.apache.james.mime4j.util;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.TreeSet;
+
+//BEGIN android-changed: Stubbing out logging
+import org.apache.james.mime4j.Log;
+import org.apache.james.mime4j.LogFactory;
+//END android-changed
+
+/**
+ * Utility class for working with character sets. It is somewhat similar to
+ * the Java 1.4 <code>java.nio.charset.Charset</code> class but knows many
+ * more aliases and is compatible with Java 1.3. It will use a simple detection
+ * mechanism to detect what character sets the current VM supports. This will
+ * be a sub-set of the character sets listed in the
+ * <a href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">
+ * Java 1.5 (J2SE5.0) Supported Encodings</a> document.
+ * <p>
+ * The <a href="http://www.iana.org/assignments/character-sets">
+ * IANA Character Sets</a> document has been used to determine the preferred
+ * MIME character set names and to get a list of known aliases.
+ * <p>
+ * This is a complete list of the character sets known to this class:
+ * <table>
+ * <tr>
+ * <td>Canonical (Java) name</td>
+ * <td>MIME preferred</td>
+ * <td>Aliases</td>
+ * </tr>
+ * <tr>
+ * <td>ASCII</td>
+ * <td>US-ASCII</td>
+ * <td>ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ISO646-US us IBM367 cp367 csASCII ascii7 646 iso_646.irv:1983 </td>
+ * </tr>
+ * <tr>
+ * <td>Big5</td>
+ * <td>Big5</td>
+ * <td>csBig5 CN-Big5 BIG-FIVE BIGFIVE </td>
+ * </tr>
+ * <tr>
+ * <td>Big5_HKSCS</td>
+ * <td>Big5-HKSCS</td>
+ * <td>big5hkscs </td>
+ * </tr>
+ * <tr>
+ * <td>Big5_Solaris</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp037</td>
+ * <td>IBM037</td>
+ * <td>ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl csIBM037 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp1006</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1025</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1026</td>
+ * <td>IBM1026</td>
+ * <td>csIBM1026 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp1046</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1047</td>
+ * <td>IBM1047</td>
+ * <td>IBM-1047 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp1097</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1098</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1112</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1122</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1123</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1124</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1140</td>
+ * <td>IBM01140</td>
+ * <td>CCSID01140 CP01140 ebcdic-us-37+euro </td>
+ * </tr>
+ * <tr>
+ * <td>Cp1141</td>
+ * <td>IBM01141</td>
+ * <td>CCSID01141 CP01141 ebcdic-de-273+euro </td>
+ * </tr>
+ * <tr>
+ * <td>Cp1142</td>
+ * <td>IBM01142</td>
+ * <td>CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro </td>
+ * </tr>
+ * <tr>
+ * <td>Cp1143</td>
+ * <td>IBM01143</td>
+ * <td>CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro </td>
+ * </tr>
+ * <tr>
+ * <td>Cp1144</td>
+ * <td>IBM01144</td>
+ * <td>CCSID01144 CP01144 ebcdic-it-280+euro </td>
+ * </tr>
+ * <tr>
+ * <td>Cp1145</td>
+ * <td>IBM01145</td>
+ * <td>CCSID01145 CP01145 ebcdic-es-284+euro </td>
+ * </tr>
+ * <tr>
+ * <td>Cp1146</td>
+ * <td>IBM01146</td>
+ * <td>CCSID01146 CP01146 ebcdic-gb-285+euro </td>
+ * </tr>
+ * <tr>
+ * <td>Cp1147</td>
+ * <td>IBM01147</td>
+ * <td>CCSID01147 CP01147 ebcdic-fr-297+euro </td>
+ * </tr>
+ * <tr>
+ * <td>Cp1148</td>
+ * <td>IBM01148</td>
+ * <td>CCSID01148 CP01148 ebcdic-international-500+euro </td>
+ * </tr>
+ * <tr>
+ * <td>Cp1149</td>
+ * <td>IBM01149</td>
+ * <td>CCSID01149 CP01149 ebcdic-is-871+euro </td>
+ * </tr>
+ * <tr>
+ * <td>Cp1250</td>
+ * <td>windows-1250</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1251</td>
+ * <td>windows-1251</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1252</td>
+ * <td>windows-1252</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1253</td>
+ * <td>windows-1253</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1254</td>
+ * <td>windows-1254</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1255</td>
+ * <td>windows-1255</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1256</td>
+ * <td>windows-1256</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1257</td>
+ * <td>windows-1257</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1258</td>
+ * <td>windows-1258</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1381</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp1383</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp273</td>
+ * <td>IBM273</td>
+ * <td>csIBM273 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp277</td>
+ * <td>IBM277</td>
+ * <td>EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp278</td>
+ * <td>IBM278</td>
+ * <td>CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp280</td>
+ * <td>IBM280</td>
+ * <td>ebcdic-cp-it csIBM280 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp284</td>
+ * <td>IBM284</td>
+ * <td>ebcdic-cp-es csIBM284 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp285</td>
+ * <td>IBM285</td>
+ * <td>ebcdic-cp-gb csIBM285 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp297</td>
+ * <td>IBM297</td>
+ * <td>ebcdic-cp-fr csIBM297 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp33722</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp420</td>
+ * <td>IBM420</td>
+ * <td>ebcdic-cp-ar1 csIBM420 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp424</td>
+ * <td>IBM424</td>
+ * <td>ebcdic-cp-he csIBM424 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp437</td>
+ * <td>IBM437</td>
+ * <td>437 csPC8CodePage437 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp500</td>
+ * <td>IBM500</td>
+ * <td>ebcdic-cp-be ebcdic-cp-ch csIBM500 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp737</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp775</td>
+ * <td>IBM775</td>
+ * <td>csPC775Baltic </td>
+ * </tr>
+ * <tr>
+ * <td>Cp838</td>
+ * <td>IBM-Thai</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp850</td>
+ * <td>IBM850</td>
+ * <td>850 csPC850Multilingual </td>
+ * </tr>
+ * <tr>
+ * <td>Cp852</td>
+ * <td>IBM852</td>
+ * <td>852 csPCp852 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp855</td>
+ * <td>IBM855</td>
+ * <td>855 csIBM855 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp856</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp857</td>
+ * <td>IBM857</td>
+ * <td>857 csIBM857 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp858</td>
+ * <td>IBM00858</td>
+ * <td>CCSID00858 CP00858 PC-Multilingual-850+euro </td>
+ * </tr>
+ * <tr>
+ * <td>Cp860</td>
+ * <td>IBM860</td>
+ * <td>860 csIBM860 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp861</td>
+ * <td>IBM861</td>
+ * <td>861 cp-is csIBM861 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp862</td>
+ * <td>IBM862</td>
+ * <td>862 csPC862LatinHebrew </td>
+ * </tr>
+ * <tr>
+ * <td>Cp863</td>
+ * <td>IBM863</td>
+ * <td>863 csIBM863 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp864</td>
+ * <td>IBM864</td>
+ * <td>cp864 csIBM864 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp865</td>
+ * <td>IBM865</td>
+ * <td>865 csIBM865 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp866</td>
+ * <td>IBM866</td>
+ * <td>866 csIBM866 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp868</td>
+ * <td>IBM868</td>
+ * <td>cp-ar csIBM868 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp869</td>
+ * <td>IBM869</td>
+ * <td>cp-gr csIBM869 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp870</td>
+ * <td>IBM870</td>
+ * <td>ebcdic-cp-roece ebcdic-cp-yu csIBM870 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp871</td>
+ * <td>IBM871</td>
+ * <td>ebcdic-cp-is csIBM871 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp875</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp918</td>
+ * <td>IBM918</td>
+ * <td>ebcdic-cp-ar2 csIBM918 </td>
+ * </tr>
+ * <tr>
+ * <td>Cp921</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp922</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp930</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp933</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp935</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp937</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp939</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp942</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp942C</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp943</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp943C</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp948</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp949</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp949C</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp950</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp964</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>Cp970</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>EUC_CN</td>
+ * <td>GB2312</td>
+ * <td>x-EUC-CN csGB2312 euccn euc-cn gb2312-80 gb2312-1980 CN-GB CN-GB-ISOIR165 </td>
+ * </tr>
+ * <tr>
+ * <td>EUC_JP</td>
+ * <td>EUC-JP</td>
+ * <td>csEUCPkdFmtJapanese Extended_UNIX_Code_Packed_Format_for_Japanese eucjis x-eucjp eucjp x-euc-jp </td>
+ * </tr>
+ * <tr>
+ * <td>EUC_JP_LINUX</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>EUC_JP_Solaris</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>EUC_KR</td>
+ * <td>EUC-KR</td>
+ * <td>csEUCKR ksc5601 5601 ksc5601_1987 ksc_5601 ksc5601-1987 ks_c_5601-1987 euckr </td>
+ * </tr>
+ * <tr>
+ * <td>EUC_TW</td>
+ * <td>EUC-TW</td>
+ * <td>x-EUC-TW cns11643 euctw </td>
+ * </tr>
+ * <tr>
+ * <td>GB18030</td>
+ * <td>GB18030</td>
+ * <td>gb18030-2000 </td>
+ * </tr>
+ * <tr>
+ * <td>GBK</td>
+ * <td>windows-936</td>
+ * <td>CP936 MS936 ms_936 x-mswin-936 </td>
+ * </tr>
+ * <tr>
+ * <td>ISCII91</td>
+ * <td>?</td>
+ * <td>x-ISCII91 iscii </td>
+ * </tr>
+ * <tr>
+ * <td>ISO2022CN</td>
+ * <td>ISO-2022-CN</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>ISO2022JP</td>
+ * <td>ISO-2022-JP</td>
+ * <td>csISO2022JP JIS jis_encoding csjisencoding </td>
+ * </tr>
+ * <tr>
+ * <td>ISO2022KR</td>
+ * <td>ISO-2022-KR</td>
+ * <td>csISO2022KR </td>
+ * </tr>
+ * <tr>
+ * <td>ISO2022_CN_CNS</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>ISO2022_CN_GB</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>ISO8859_1</td>
+ * <td>ISO-8859-1</td>
+ * <td>ISO_8859-1:1987 iso-ir-100 ISO_8859-1 latin1 l1 IBM819 CP819 csISOLatin1 8859_1 819 IBM-819 ISO8859-1 ISO_8859_1 </td>
+ * </tr>
+ * <tr>
+ * <td>ISO8859_13</td>
+ * <td>ISO-8859-13</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>ISO8859_15</td>
+ * <td>ISO-8859-15</td>
+ * <td>ISO_8859-15 Latin-9 8859_15 csISOlatin9 IBM923 cp923 923 L9 IBM-923 ISO8859-15 LATIN9 LATIN0 csISOlatin0 ISO8859_15_FDIS </td>
+ * </tr>
+ * <tr>
+ * <td>ISO8859_2</td>
+ * <td>ISO-8859-2</td>
+ * <td>ISO_8859-2:1987 iso-ir-101 ISO_8859-2 latin2 l2 csISOLatin2 8859_2 iso8859_2 </td>
+ * </tr>
+ * <tr>
+ * <td>ISO8859_3</td>
+ * <td>ISO-8859-3</td>
+ * <td>ISO_8859-3:1988 iso-ir-109 ISO_8859-3 latin3 l3 csISOLatin3 8859_3 </td>
+ * </tr>
+ * <tr>
+ * <td>ISO8859_4</td>
+ * <td>ISO-8859-4</td>
+ * <td>ISO_8859-4:1988 iso-ir-110 ISO_8859-4 latin4 l4 csISOLatin4 8859_4 </td>
+ * </tr>
+ * <tr>
+ * <td>ISO8859_5</td>
+ * <td>ISO-8859-5</td>
+ * <td>ISO_8859-5:1988 iso-ir-144 ISO_8859-5 cyrillic csISOLatinCyrillic 8859_5 </td>
+ * </tr>
+ * <tr>
+ * <td>ISO8859_6</td>
+ * <td>ISO-8859-6</td>
+ * <td>ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ECMA-114 ASMO-708 arabic csISOLatinArabic 8859_6 </td>
+ * </tr>
+ * <tr>
+ * <td>ISO8859_7</td>
+ * <td>ISO-8859-7</td>
+ * <td>ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ELOT_928 ECMA-118 greek greek8 csISOLatinGreek 8859_7 sun_eu_greek </td>
+ * </tr>
+ * <tr>
+ * <td>ISO8859_8</td>
+ * <td>ISO-8859-8</td>
+ * <td>ISO_8859-8:1988 iso-ir-138 ISO_8859-8 hebrew csISOLatinHebrew 8859_8 </td>
+ * </tr>
+ * <tr>
+ * <td>ISO8859_9</td>
+ * <td>ISO-8859-9</td>
+ * <td>ISO_8859-9:1989 iso-ir-148 ISO_8859-9 latin5 l5 csISOLatin5 8859_9 </td>
+ * </tr>
+ * <tr>
+ * <td>JISAutoDetect</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>JIS_C6626-1983</td>
+ * <td>JIS_C6626-1983</td>
+ * <td>x-JIS0208 JIS0208 csISO87JISX0208 x0208 JIS_X0208-1983 iso-ir-87 </td>
+ * </tr>
+ * <tr>
+ * <td>JIS_X0201</td>
+ * <td>JIS_X0201</td>
+ * <td>X0201 JIS0201 csHalfWidthKatakana </td>
+ * </tr>
+ * <tr>
+ * <td>JIS_X0212-1990</td>
+ * <td>JIS_X0212-1990</td>
+ * <td>iso-ir-159 x0212 JIS0212 csISO159JISX02121990 </td>
+ * </tr>
+ * <tr>
+ * <td>KOI8_R</td>
+ * <td>KOI8-R</td>
+ * <td>csKOI8R koi8 </td>
+ * </tr>
+ * <tr>
+ * <td>MS874</td>
+ * <td>windows-874</td>
+ * <td>cp874 </td>
+ * </tr>
+ * <tr>
+ * <td>MS932</td>
+ * <td>Windows-31J</td>
+ * <td>windows-932 csWindows31J x-ms-cp932 </td>
+ * </tr>
+ * <tr>
+ * <td>MS949</td>
+ * <td>windows-949</td>
+ * <td>windows949 ms_949 x-windows-949 </td>
+ * </tr>
+ * <tr>
+ * <td>MS950</td>
+ * <td>windows-950</td>
+ * <td>x-windows-950 </td>
+ * </tr>
+ * <tr>
+ * <td>MS950_HKSCS</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>MacArabic</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>MacCentralEurope</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>MacCroatian</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>MacCyrillic</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>MacDingbat</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>MacGreek</td>
+ * <td>MacGreek</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>MacHebrew</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>MacIceland</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>MacRoman</td>
+ * <td>MacRoman</td>
+ * <td>Macintosh MAC csMacintosh </td>
+ * </tr>
+ * <tr>
+ * <td>MacRomania</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>MacSymbol</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>MacThai</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>MacTurkish</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>MacUkraine</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>SJIS</td>
+ * <td>Shift_JIS</td>
+ * <td>MS_Kanji csShiftJIS shift-jis x-sjis pck </td>
+ * </tr>
+ * <tr>
+ * <td>TIS620</td>
+ * <td>TIS-620</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>UTF-16</td>
+ * <td>UTF-16</td>
+ * <td>UTF_16 </td>
+ * </tr>
+ * <tr>
+ * <td>UTF8</td>
+ * <td>UTF-8</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>UnicodeBig</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>UnicodeBigUnmarked</td>
+ * <td>UTF-16BE</td>
+ * <td>X-UTF-16BE UTF_16BE ISO-10646-UCS-2 </td>
+ * </tr>
+ * <tr>
+ * <td>UnicodeLittle</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>UnicodeLittleUnmarked</td>
+ * <td>UTF-16LE</td>
+ * <td>UTF_16LE X-UTF-16LE </td>
+ * </tr>
+ * <tr>
+ * <td>x-Johab</td>
+ * <td>johab</td>
+ * <td>johab cp1361 ms1361 ksc5601-1992 ksc5601_1992 </td>
+ * </tr>
+ * <tr>
+ * <td>x-iso-8859-11</td>
+ * <td>?</td>
+ * <td></td>
+ * </tr>
+ * </table>
+ *
+ *
+ * @version $Id: CharsetUtil.java,v 1.1 2004/10/25 07:26:46 ntherning Exp $
+ */
+public class CharsetUtil {
+ private static Log log = LogFactory.getLog(CharsetUtil.class);
+
+ private static class Charset implements Comparable<Charset> {
+ private String canonical = null;
+ private String mime = null;
+ private String[] aliases = null;
+
+ private Charset(String canonical, String mime, String[] aliases) {
+ this.canonical = canonical;
+ this.mime = mime;
+ this.aliases = aliases;
+ }
+
+ public int compareTo(Charset c) {
+ return this.canonical.compareTo(c.canonical);
+ }
+ }
+
+ private static Charset[] JAVA_CHARSETS = {
+ new Charset("ISO8859_1", "ISO-8859-1",
+ new String[] {"ISO_8859-1:1987", "iso-ir-100", "ISO_8859-1",
+ "latin1", "l1", "IBM819", "CP819",
+ "csISOLatin1", "8859_1", "819", "IBM-819",
+ "ISO8859-1", "ISO_8859_1"}),
+ new Charset("ISO8859_2", "ISO-8859-2",
+ new String[] {"ISO_8859-2:1987", "iso-ir-101", "ISO_8859-2",
+ "latin2", "l2", "csISOLatin2", "8859_2",
+ "iso8859_2"}),
+ new Charset("ISO8859_3", "ISO-8859-3", new String[] {"ISO_8859-3:1988", "iso-ir-109", "ISO_8859-3", "latin3", "l3", "csISOLatin3", "8859_3"}),
+ new Charset("ISO8859_4", "ISO-8859-4",
+ new String[] {"ISO_8859-4:1988", "iso-ir-110", "ISO_8859-4",
+ "latin4", "l4", "csISOLatin4", "8859_4"}),
+ new Charset("ISO8859_5", "ISO-8859-5",
+ new String[] {"ISO_8859-5:1988", "iso-ir-144", "ISO_8859-5",
+ "cyrillic", "csISOLatinCyrillic", "8859_5"}),
+ new Charset("ISO8859_6", "ISO-8859-6", new String[] {"ISO_8859-6:1987", "iso-ir-127", "ISO_8859-6", "ECMA-114", "ASMO-708", "arabic", "csISOLatinArabic", "8859_6"}),
+ new Charset("ISO8859_7", "ISO-8859-7",
+ new String[] {"ISO_8859-7:1987", "iso-ir-126", "ISO_8859-7",
+ "ELOT_928", "ECMA-118", "greek", "greek8",
+ "csISOLatinGreek", "8859_7", "sun_eu_greek"}),
+ new Charset("ISO8859_8", "ISO-8859-8", new String[] {"ISO_8859-8:1988", "iso-ir-138", "ISO_8859-8", "hebrew", "csISOLatinHebrew", "8859_8"}),
+ new Charset("ISO8859_9", "ISO-8859-9",
+ new String[] {"ISO_8859-9:1989", "iso-ir-148", "ISO_8859-9",
+ "latin5", "l5", "csISOLatin5", "8859_9"}),
+
+ new Charset("ISO8859_13", "ISO-8859-13", new String[] {}),
+ new Charset("ISO8859_15", "ISO-8859-15",
+ new String[] {"ISO_8859-15", "Latin-9", "8859_15",
+ "csISOlatin9", "IBM923", "cp923", "923", "L9",
+ "IBM-923", "ISO8859-15", "LATIN9", "LATIN0",
+ "csISOlatin0", "ISO8859_15_FDIS"}),
+ new Charset("KOI8_R", "KOI8-R", new String[] {"csKOI8R", "koi8"}),
+ new Charset("ASCII", "US-ASCII",
+ new String[] {"ANSI_X3.4-1968", "iso-ir-6",
+ "ANSI_X3.4-1986", "ISO_646.irv:1991",
+ "ISO646-US", "us", "IBM367", "cp367",
+ "csASCII", "ascii7", "646", "iso_646.irv:1983"}),
+ new Charset("UTF8", "UTF-8", new String[] {}),
+ new Charset("UTF-16", "UTF-16", new String[] {"UTF_16"}),
+ new Charset("UnicodeBigUnmarked", "UTF-16BE", new String[] {"X-UTF-16BE", "UTF_16BE", "ISO-10646-UCS-2"}),
+ new Charset("UnicodeLittleUnmarked", "UTF-16LE", new String[] {"UTF_16LE", "X-UTF-16LE"}),
+ new Charset("Big5", "Big5", new String[] {"csBig5", "CN-Big5", "BIG-FIVE", "BIGFIVE"}),
+ new Charset("Big5_HKSCS", "Big5-HKSCS", new String[] {"big5hkscs"}),
+ new Charset("EUC_JP", "EUC-JP",
+ new String[] {"csEUCPkdFmtJapanese",
+ "Extended_UNIX_Code_Packed_Format_for_Japanese",
+ "eucjis", "x-eucjp", "eucjp", "x-euc-jp"}),
+ new Charset("EUC_KR", "EUC-KR",
+ new String[] {"csEUCKR", "ksc5601", "5601", "ksc5601_1987",
+ "ksc_5601", "ksc5601-1987", "ks_c_5601-1987",
+ "euckr"}),
+ new Charset("GB18030", "GB18030", new String[] {"gb18030-2000"}),
+ new Charset("EUC_CN", "GB2312", new String[] {"x-EUC-CN", "csGB2312", "euccn", "euc-cn", "gb2312-80", "gb2312-1980", "CN-GB", "CN-GB-ISOIR165"}),
+ new Charset("GBK", "windows-936", new String[] {"CP936", "MS936", "ms_936", "x-mswin-936"}),
+
+ new Charset("Cp037", "IBM037", new String[] {"ebcdic-cp-us", "ebcdic-cp-ca", "ebcdic-cp-wt", "ebcdic-cp-nl", "csIBM037"}),
+ new Charset("Cp273", "IBM273", new String[] {"csIBM273"}),
+ new Charset("Cp277", "IBM277", new String[] {"EBCDIC-CP-DK", "EBCDIC-CP-NO", "csIBM277"}),
+ new Charset("Cp278", "IBM278", new String[] {"CP278", "ebcdic-cp-fi", "ebcdic-cp-se", "csIBM278"}),
+ new Charset("Cp280", "IBM280", new String[] {"ebcdic-cp-it", "csIBM280"}),
+ new Charset("Cp284", "IBM284", new String[] {"ebcdic-cp-es", "csIBM284"}),
+ new Charset("Cp285", "IBM285", new String[] {"ebcdic-cp-gb", "csIBM285"}),
+ new Charset("Cp297", "IBM297", new String[] {"ebcdic-cp-fr", "csIBM297"}),
+ new Charset("Cp420", "IBM420", new String[] {"ebcdic-cp-ar1", "csIBM420"}),
+ new Charset("Cp424", "IBM424", new String[] {"ebcdic-cp-he", "csIBM424"}),
+ new Charset("Cp437", "IBM437", new String[] {"437", "csPC8CodePage437"}),
+ new Charset("Cp500", "IBM500", new String[] {"ebcdic-cp-be", "ebcdic-cp-ch", "csIBM500"}),
+ new Charset("Cp775", "IBM775", new String[] {"csPC775Baltic"}),
+ new Charset("Cp838", "IBM-Thai", new String[] {}),
+ new Charset("Cp850", "IBM850", new String[] {"850", "csPC850Multilingual"}),
+ new Charset("Cp852", "IBM852", new String[] {"852", "csPCp852"}),
+ new Charset("Cp855", "IBM855", new String[] {"855", "csIBM855"}),
+ new Charset("Cp857", "IBM857", new String[] {"857", "csIBM857"}),
+ new Charset("Cp858", "IBM00858",
+ new String[] {"CCSID00858", "CP00858",
+ "PC-Multilingual-850+euro"}),
+ new Charset("Cp860", "IBM860", new String[] {"860", "csIBM860"}),
+ new Charset("Cp861", "IBM861", new String[] {"861", "cp-is", "csIBM861"}),
+ new Charset("Cp862", "IBM862", new String[] {"862", "csPC862LatinHebrew"}),
+ new Charset("Cp863", "IBM863", new String[] {"863", "csIBM863"}),
+ new Charset("Cp864", "IBM864", new String[] {"cp864", "csIBM864"}),
+ new Charset("Cp865", "IBM865", new String[] {"865", "csIBM865"}),
+ new Charset("Cp866", "IBM866", new String[] {"866", "csIBM866"}),
+ new Charset("Cp868", "IBM868", new String[] {"cp-ar", "csIBM868"}),
+ new Charset("Cp869", "IBM869", new String[] {"cp-gr", "csIBM869"}),
+ new Charset("Cp870", "IBM870", new String[] {"ebcdic-cp-roece", "ebcdic-cp-yu", "csIBM870"}),
+ new Charset("Cp871", "IBM871", new String[] {"ebcdic-cp-is", "csIBM871"}),
+ new Charset("Cp918", "IBM918", new String[] {"ebcdic-cp-ar2", "csIBM918"}),
+ new Charset("Cp1026", "IBM1026", new String[] {"csIBM1026"}),
+ new Charset("Cp1047", "IBM1047", new String[] {"IBM-1047"}),
+ new Charset("Cp1140", "IBM01140",
+ new String[] {"CCSID01140", "CP01140",
+ "ebcdic-us-37+euro"}),
+ new Charset("Cp1141", "IBM01141",
+ new String[] {"CCSID01141", "CP01141",
+ "ebcdic-de-273+euro"}),
+ new Charset("Cp1142", "IBM01142", new String[] {"CCSID01142", "CP01142", "ebcdic-dk-277+euro", "ebcdic-no-277+euro"}),
+ new Charset("Cp1143", "IBM01143", new String[] {"CCSID01143", "CP01143", "ebcdic-fi-278+euro", "ebcdic-se-278+euro"}),
+ new Charset("Cp1144", "IBM01144", new String[] {"CCSID01144", "CP01144", "ebcdic-it-280+euro"}),
+ new Charset("Cp1145", "IBM01145", new String[] {"CCSID01145", "CP01145", "ebcdic-es-284+euro"}),
+ new Charset("Cp1146", "IBM01146", new String[] {"CCSID01146", "CP01146", "ebcdic-gb-285+euro"}),
+ new Charset("Cp1147", "IBM01147", new String[] {"CCSID01147", "CP01147", "ebcdic-fr-297+euro"}),
+ new Charset("Cp1148", "IBM01148", new String[] {"CCSID01148", "CP01148", "ebcdic-international-500+euro"}),
+ new Charset("Cp1149", "IBM01149", new String[] {"CCSID01149", "CP01149", "ebcdic-is-871+euro"}),
+ new Charset("Cp1250", "windows-1250", new String[] {}),
+ new Charset("Cp1251", "windows-1251", new String[] {}),
+ new Charset("Cp1252", "windows-1252", new String[] {}),
+ new Charset("Cp1253", "windows-1253", new String[] {}),
+ new Charset("Cp1254", "windows-1254", new String[] {}),
+ new Charset("Cp1255", "windows-1255", new String[] {}),
+ new Charset("Cp1256", "windows-1256", new String[] {}),
+ new Charset("Cp1257", "windows-1257", new String[] {}),
+ new Charset("Cp1258", "windows-1258", new String[] {}),
+ new Charset("ISO2022CN", "ISO-2022-CN", new String[] {}),
+ new Charset("ISO2022JP", "ISO-2022-JP", new String[] {"csISO2022JP", "JIS", "jis_encoding", "csjisencoding"}),
+ new Charset("ISO2022KR", "ISO-2022-KR", new String[] {"csISO2022KR"}),
+ new Charset("JIS_X0201", "JIS_X0201", new String[] {"X0201", "JIS0201", "csHalfWidthKatakana"}),
+ new Charset("JIS_X0212-1990", "JIS_X0212-1990", new String[] {"iso-ir-159", "x0212", "JIS0212", "csISO159JISX02121990"}),
+ new Charset("JIS_C6626-1983", "JIS_C6626-1983", new String[] {"x-JIS0208", "JIS0208", "csISO87JISX0208", "x0208", "JIS_X0208-1983", "iso-ir-87"}),
+ new Charset("SJIS", "Shift_JIS", new String[] {"MS_Kanji", "csShiftJIS", "shift-jis", "x-sjis", "pck"}),
+ new Charset("TIS620", "TIS-620", new String[] {}),
+ new Charset("MS932", "Windows-31J", new String[] {"windows-932", "csWindows31J", "x-ms-cp932"}),
+ new Charset("EUC_TW", "EUC-TW", new String[] {"x-EUC-TW", "cns11643", "euctw"}),
+ new Charset("x-Johab", "johab", new String[] {"johab", "cp1361", "ms1361", "ksc5601-1992", "ksc5601_1992"}),
+ new Charset("MS950_HKSCS", "", new String[] {}),
+ new Charset("MS874", "windows-874", new String[] {"cp874"}),
+ new Charset("MS949", "windows-949", new String[] {"windows949", "ms_949", "x-windows-949"}),
+ new Charset("MS950", "windows-950", new String[] {"x-windows-950"}),
+
+ new Charset("Cp737", null, new String[] {}),
+ new Charset("Cp856", null, new String[] {}),
+ new Charset("Cp875", null, new String[] {}),
+ new Charset("Cp921", null, new String[] {}),
+ new Charset("Cp922", null, new String[] {}),
+ new Charset("Cp930", null, new String[] {}),
+ new Charset("Cp933", null, new String[] {}),
+ new Charset("Cp935", null, new String[] {}),
+ new Charset("Cp937", null, new String[] {}),
+ new Charset("Cp939", null, new String[] {}),
+ new Charset("Cp942", null, new String[] {}),
+ new Charset("Cp942C", null, new String[] {}),
+ new Charset("Cp943", null, new String[] {}),
+ new Charset("Cp943C", null, new String[] {}),
+ new Charset("Cp948", null, new String[] {}),
+ new Charset("Cp949", null, new String[] {}),
+ new Charset("Cp949C", null, new String[] {}),
+ new Charset("Cp950", null, new String[] {}),
+ new Charset("Cp964", null, new String[] {}),
+ new Charset("Cp970", null, new String[] {}),
+ new Charset("Cp1006", null, new String[] {}),
+ new Charset("Cp1025", null, new String[] {}),
+ new Charset("Cp1046", null, new String[] {}),
+ new Charset("Cp1097", null, new String[] {}),
+ new Charset("Cp1098", null, new String[] {}),
+ new Charset("Cp1112", null, new String[] {}),
+ new Charset("Cp1122", null, new String[] {}),
+ new Charset("Cp1123", null, new String[] {}),
+ new Charset("Cp1124", null, new String[] {}),
+ new Charset("Cp1381", null, new String[] {}),
+ new Charset("Cp1383", null, new String[] {}),
+ new Charset("Cp33722", null, new String[] {}),
+ new Charset("Big5_Solaris", null, new String[] {}),
+ new Charset("EUC_JP_LINUX", null, new String[] {}),
+ new Charset("EUC_JP_Solaris", null, new String[] {}),
+ new Charset("ISCII91", null, new String[] {"x-ISCII91", "iscii"}),
+ new Charset("ISO2022_CN_CNS", null, new String[] {}),
+ new Charset("ISO2022_CN_GB", null, new String[] {}),
+ new Charset("x-iso-8859-11", null, new String[] {}),
+ new Charset("JISAutoDetect", null, new String[] {}),
+ new Charset("MacArabic", null, new String[] {}),
+ new Charset("MacCentralEurope", null, new String[] {}),
+ new Charset("MacCroatian", null, new String[] {}),
+ new Charset("MacCyrillic", null, new String[] {}),
+ new Charset("MacDingbat", null, new String[] {}),
+ new Charset("MacGreek", "MacGreek", new String[] {}),
+ new Charset("MacHebrew", null, new String[] {}),
+ new Charset("MacIceland", null, new String[] {}),
+ new Charset("MacRoman", "MacRoman", new String[] {"Macintosh", "MAC", "csMacintosh"}),
+ new Charset("MacRomania", null, new String[] {}),
+ new Charset("MacSymbol", null, new String[] {}),
+ new Charset("MacThai", null, new String[] {}),
+ new Charset("MacTurkish", null, new String[] {}),
+ new Charset("MacUkraine", null, new String[] {}),
+ new Charset("UnicodeBig", null, new String[] {}),
+ new Charset("UnicodeLittle", null, new String[] {})
+ };
+
+ /**
+ * Contains the canonical names of character sets which can be used to
+ * decode bytes into Java chars.
+ */
+ private static TreeSet<String> decodingSupported = null;
+
+ /**
+ * Contains the canonical names of character sets which can be used to
+ * encode Java chars into bytes.
+ */
+ private static TreeSet<String> encodingSupported = null;
+
+ /**
+ * Maps character set names to Charset objects. All possible names of
+ * a charset will be mapped to the Charset.
+ */
+ private static HashMap<String, Charset> charsetMap = null;
+
+ static {
+ decodingSupported = new TreeSet<String>();
+ encodingSupported = new TreeSet<String>();
+ byte[] dummy = new byte[] {'d', 'u', 'm', 'm', 'y'};
+ for (int i = 0; i < JAVA_CHARSETS.length; i++) {
+ try {
+ String s = new String(dummy, JAVA_CHARSETS[i].canonical);
+ decodingSupported.add(JAVA_CHARSETS[i].canonical.toLowerCase(Locale.US));
+ } catch (UnsupportedOperationException e) {
+ } catch (UnsupportedEncodingException e) {
+ }
+ try {
+ "dummy".getBytes(JAVA_CHARSETS[i].canonical);
+ encodingSupported.add(JAVA_CHARSETS[i].canonical.toLowerCase(Locale.US));
+ } catch (UnsupportedOperationException e) {
+ } catch (UnsupportedEncodingException e) {
+ }
+ }
+
+ charsetMap = new HashMap<String, Charset>();
+ for (int i = 0; i < JAVA_CHARSETS.length; i++) {
+ Charset c = JAVA_CHARSETS[i];
+ charsetMap.put(c.canonical.toLowerCase(Locale.US), c);
+ if (c.mime != null) {
+ charsetMap.put(c.mime.toLowerCase(Locale.US), c);
+ }
+ if (c.aliases != null) {
+ for (int j = 0; j < c.aliases.length; j++) {
+ charsetMap.put(c.aliases[j].toLowerCase(Locale.US), c);
+ }
+ }
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("Character sets which support decoding: "
+ + decodingSupported);
+ log.debug("Character sets which support encoding: "
+ + encodingSupported);
+ }
+ }
+
+ /**
+ * ANDROID: THE FOLLOWING SET OF STATIC STRINGS ARE COPIED FROM A NEWER VERSION OF MIME4J
+ */
+
+ /** carriage return - line feed sequence */
+ public static final String CRLF = "\r\n";
+
+ /** US-ASCII CR, carriage return (13) */
+ public static final int CR = '\r';
+
+ /** US-ASCII LF, line feed (10) */
+ public static final int LF = '\n';
+
+ /** US-ASCII SP, space (32) */
+ public static final int SP = ' ';
+
+ /** US-ASCII HT, horizontal-tab (9)*/
+ public static final int HT = '\t';
+
+ public static final java.nio.charset.Charset US_ASCII = java.nio.charset.Charset
+ .forName("US-ASCII");
+
+ public static final java.nio.charset.Charset ISO_8859_1 = java.nio.charset.Charset
+ .forName("ISO-8859-1");
+
+ public static final java.nio.charset.Charset UTF_8 = java.nio.charset.Charset
+ .forName("UTF-8");
+
+ /**
+ * Returns <code>true</code> if the specified character is a whitespace
+ * character (CR, LF, SP or HT).
+ *
+ * ANDROID: COPIED FROM A NEWER VERSION OF MIME4J
+ *
+ * @param ch
+ * character to test.
+ * @return <code>true</code> if the specified character is a whitespace
+ * character, <code>false</code> otherwise.
+ */
+ public static boolean isWhitespace(char ch) {
+ return ch == SP || ch == HT || ch == CR || ch == LF;
+ }
+
+ /**
+ * Returns <code>true</code> if the specified string consists entirely of
+ * whitespace characters.
+ *
+ * ANDROID: COPIED FROM A NEWER VERSION OF MIME4J
+ *
+ * @param s
+ * string to test.
+ * @return <code>true</code> if the specified string consists entirely of
+ * whitespace characters, <code>false</code> otherwise.
+ */
+ public static boolean isWhitespace(final String s) {
+ if (s == null) {
+ throw new IllegalArgumentException("String may not be null");
+ }
+ final int len = s.length();
+ for (int i = 0; i < len; i++) {
+ if (!isWhitespace(s.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Determines if the VM supports encoding (chars to bytes) the
+ * specified character set. NOTE: the given character set name may
+ * not be known to the VM even if this method returns <code>true</code>.
+ * Use {@link #toJavaCharset(String)} to get the canonical Java character
+ * set name.
+ *
+ * @param charsetName the characters set name.
+ * @return <code>true</code> if encoding is supported, <code>false</code>
+ * otherwise.
+ */
+ public static boolean isEncodingSupported(String charsetName) {
+ return encodingSupported.contains(charsetName.toLowerCase(Locale.US));
+ }
+
+ /**
+ * Determines if the VM supports decoding (bytes to chars) the
+ * specified character set. NOTE: the given character set name may
+ * not be known to the VM even if this method returns <code>true</code>.
+ * Use {@link #toJavaCharset(String)} to get the canonical Java character
+ * set name.
+ *
+ * @param charsetName the characters set name.
+ * @return <code>true</code> if decoding is supported, <code>false</code>
+ * otherwise.
+ */
+ public static boolean isDecodingSupported(String charsetName) {
+ return decodingSupported.contains(charsetName.toLowerCase(Locale.US));
+ }
+
+ /**
+ * Gets the preferred MIME character set name for the specified
+ * character set or <code>null</code> if not known.
+ *
+ * @param charsetName the character set name to look for.
+ * @return the MIME preferred name or <code>null</code> if not known.
+ */
+ public static String toMimeCharset(String charsetName) {
+ Charset c = charsetMap.get(charsetName.toLowerCase(Locale.US));
+ if (c != null) {
+ return c.mime;
+ }
+ return null;
+ }
+
+ /**
+ * Gets the canonical Java character set name for the specified
+ * character set or <code>null</code> if not known. This should be
+ * called before doing any conversions using the Java API. NOTE:
+ * you must use {@link #isEncodingSupported(String)} or
+ * {@link #isDecodingSupported(String)} to make sure the returned
+ * Java character set is supported by the current VM.
+ *
+ * @param charsetName the character set name to look for.
+ * @return the canonical Java name or <code>null</code> if not known.
+ */
+ public static String toJavaCharset(String charsetName) {
+ Charset c = charsetMap.get(charsetName.toLowerCase(Locale.US));
+ if (c != null) {
+ return c.canonical;
+ }
+ return null;
+ }
+
+ public static java.nio.charset.Charset getCharset(String charsetName) {
+ String defaultCharset = "ISO-8859-1";
+
+ // Use the default chareset if given charset is null
+ if(charsetName == null) charsetName = defaultCharset;
+
+ try {
+ return java.nio.charset.Charset.forName(charsetName);
+ } catch (IllegalCharsetNameException e) {
+ log.info("Illegal charset " + charsetName + ", fallback to " +
+ defaultCharset + ": " + e);
+ // Use default charset on exception
+ return java.nio.charset.Charset.forName(defaultCharset);
+ } catch (UnsupportedCharsetException ex) {
+ log.info("Unsupported charset " + charsetName + ", fallback to " +
+ defaultCharset + ": " + ex);
+ // Use default charset on exception
+ return java.nio.charset.Charset.forName(defaultCharset);
+ }
+
+ }
+ /*
+ * Uncomment the code below and run the main method to regenerate the
+ * Javadoc table above when the known charsets change.
+ */
+
+ /*
+ private static String dumpHtmlTable() {
+ LinkedList l = new LinkedList(Arrays.asList(JAVA_CHARSETS));
+ Collections.sort(l);
+ StringBuffer sb = new StringBuffer();
+ sb.append(" * <table>\n");
+ sb.append(" * <tr>\n");
+ sb.append(" * <td>Canonical (Java) name</td>\n");
+ sb.append(" * <td>MIME preferred</td>\n");
+ sb.append(" * <td>Aliases</td>\n");
+ sb.append(" * </tr>\n");
+
+ for (Iterator it = l.iterator(); it.hasNext();) {
+ Charset c = (Charset) it.next();
+ sb.append(" * <tr>\n");
+ sb.append(" * <td>" + c.canonical + "</td>\n");
+ sb.append(" * <td>" + (c.mime == null ? "?" : c.mime)+ "</td>\n");
+ sb.append(" * <td>");
+ for (int i = 0; c.aliases != null && i < c.aliases.length; i++) {
+ sb.append(c.aliases[i] + " ");
+ }
+ sb.append("</td>\n");
+ sb.append(" * </tr>\n");
+ }
+ sb.append(" * </table>\n");
+ return sb.toString();
+ }
+
+ public static void main(String[] args) {
+ System.out.println(dumpHtmlTable());
+ }*/
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/sync/OmtpVvmSourceManager.java b/java/com/android/voicemailomtp/sync/OmtpVvmSourceManager.java
new file mode 100644
index 000000000..ad3c025cf
--- /dev/null
+++ b/java/com/android/voicemailomtp/sync/OmtpVvmSourceManager.java
@@ -0,0 +1,120 @@
+/*
+ * 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.voicemailomtp.sync;
+
+import android.content.Context;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+
+import com.android.voicemailomtp.VoicemailStatus;
+import com.android.voicemailomtp.VvmPhoneStateListener;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A singleton class designed to remember the active OMTP visual voicemail sources. Because a
+ * voicemail source is tied 1:1 to a phone account, the phone account handle is used as the key
+ * for each voicemail source and the associated data.
+ */
+public class OmtpVvmSourceManager {
+ public static final String TAG = "OmtpVvmSourceManager";
+
+ private static OmtpVvmSourceManager sInstance = new OmtpVvmSourceManager();
+
+ private Context mContext;
+ private TelephonyManager mTelephonyManager;
+ // Each phone account is associated with a phone state listener for updates to whether the
+ // device is able to sync.
+ private Set<PhoneAccountHandle> mActiveVvmSources;
+ private Map<PhoneAccountHandle, PhoneStateListener> mPhoneStateListenerMap;
+
+ /**
+ * Private constructor. Instance should only be acquired through getInstance().
+ */
+ private OmtpVvmSourceManager() {}
+
+ public static OmtpVvmSourceManager getInstance(Context context) {
+ sInstance.setup(context);
+ return sInstance;
+ }
+
+ /**
+ * Set the context and system services so they do not need to be retrieved every time.
+ * @param context The context to get the subscription and telephony manager for.
+ */
+ private void setup(Context context) {
+ if (mContext == null) {
+ mContext = context;
+ mTelephonyManager = (TelephonyManager)
+ mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ mActiveVvmSources = Collections.newSetFromMap(
+ new ConcurrentHashMap<PhoneAccountHandle, Boolean>(8, 0.9f, 1));
+ mPhoneStateListenerMap =
+ new ConcurrentHashMap<PhoneAccountHandle, PhoneStateListener>(8, 0.9f, 1);
+ }
+ }
+
+ public void addSource(PhoneAccountHandle phoneAccount) {
+ mActiveVvmSources.add(phoneAccount);
+ }
+
+ public void removeSource(PhoneAccountHandle phoneAccount) {
+ // TODO: should use OmtpVvmCarrierConfigHelper to handle the event. But currently it
+ // couldn't handle events on removed SIMs
+ VoicemailStatus.disable(mContext, phoneAccount);
+ removePhoneStateListener(phoneAccount);
+ mActiveVvmSources.remove(phoneAccount);
+ }
+
+ public void addPhoneStateListener(PhoneAccountHandle phoneAccount) {
+ if (!mPhoneStateListenerMap.containsKey(phoneAccount)) {
+ VvmPhoneStateListener phoneStateListener = new VvmPhoneStateListener(mContext,
+ phoneAccount);
+ mPhoneStateListenerMap.put(phoneAccount, phoneStateListener);
+ mTelephonyManager.createForPhoneAccountHandle(phoneAccount)
+ .listen(phoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+ }
+ }
+
+ public void removePhoneStateListener(PhoneAccountHandle phoneAccount) {
+ PhoneStateListener phoneStateListener =
+ mPhoneStateListenerMap.remove(phoneAccount);
+ mTelephonyManager.createForPhoneAccountHandle(phoneAccount).listen(phoneStateListener, 0);
+ }
+
+ public Set<PhoneAccountHandle> getOmtpVvmSources() {
+ return mActiveVvmSources;
+ }
+
+ /**
+ * Check if a certain account is registered.
+ *
+ * @param phoneAccount The account to look for.
+ * @return {@code true} if the account is in the list of registered OMTP voicemail sources.
+ * {@code false} otherwise.
+ */
+ public boolean isVvmSourceRegistered(PhoneAccountHandle phoneAccount) {
+ if (phoneAccount == null) {
+ return false;
+ }
+
+ return mActiveVvmSources.contains(phoneAccount);
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/sync/OmtpVvmSyncReceiver.java b/java/com/android/voicemailomtp/sync/OmtpVvmSyncReceiver.java
new file mode 100644
index 000000000..971a1c5a8
--- /dev/null
+++ b/java/com/android/voicemailomtp/sync/OmtpVvmSyncReceiver.java
@@ -0,0 +1,61 @@
+/*
+ * 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.voicemailomtp.sync;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.VoicemailContract;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+
+import com.android.voicemailomtp.ActivationTask;
+import com.android.voicemailomtp.VvmLog;
+import com.android.voicemailomtp.settings.VisualVoicemailSettingsUtil;
+
+import java.util.List;
+
+public class OmtpVvmSyncReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "OmtpVvmSyncReceiver";
+
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ if (VoicemailContract.ACTION_SYNC_VOICEMAIL.equals(intent.getAction())) {
+ VvmLog.v(TAG, "Sync intent received");
+ for (PhoneAccountHandle source : OmtpVvmSourceManager.getInstance(context)
+ .getOmtpVvmSources()) {
+ SyncTask.start(context, source, OmtpVvmSyncService.SYNC_FULL_SYNC);
+ }
+ activateUnactivatedAccounts(context);
+ }
+ }
+
+ private static void activateUnactivatedAccounts(Context context) {
+ List<PhoneAccountHandle> accounts =
+ context.getSystemService(TelecomManager.class).getCallCapablePhoneAccounts();
+ for (PhoneAccountHandle phoneAccount : accounts) {
+ if (!VisualVoicemailSettingsUtil.isEnabled(context, phoneAccount)) {
+ continue;
+ }
+ if (!OmtpVvmSourceManager.getInstance(context).isVvmSourceRegistered(phoneAccount)) {
+ VvmLog.i(TAG, "Unactivated account " + phoneAccount + " found, activating");
+ ActivationTask.start(context, phoneAccount, null);
+ }
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/sync/OmtpVvmSyncService.java b/java/com/android/voicemailomtp/sync/OmtpVvmSyncService.java
new file mode 100644
index 000000000..a3418cc28
--- /dev/null
+++ b/java/com/android/voicemailomtp/sync/OmtpVvmSyncService.java
@@ -0,0 +1,278 @@
+/*
+ * 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.voicemailomtp.sync;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.net.Network;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.telecom.PhoneAccountHandle;
+import android.text.TextUtils;
+import com.android.voicemailomtp.ActivationTask;
+import com.android.voicemailomtp.Assert;
+import com.android.voicemailomtp.OmtpEvents;
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.Voicemail;
+import com.android.voicemailomtp.VoicemailStatus;
+import com.android.voicemailomtp.VvmLog;
+import com.android.voicemailomtp.fetch.VoicemailFetchedCallback;
+import com.android.voicemailomtp.imap.ImapHelper;
+import com.android.voicemailomtp.imap.ImapHelper.InitializingException;
+import com.android.voicemailomtp.scheduling.BaseTask;
+import com.android.voicemailomtp.settings.VisualVoicemailSettingsUtil;
+import com.android.voicemailomtp.sync.VvmNetworkRequest.NetworkWrapper;
+import com.android.voicemailomtp.sync.VvmNetworkRequest.RequestFailedException;
+import com.android.voicemailomtp.utils.VoicemailDatabaseUtil;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Sync OMTP visual voicemail. */
+@TargetApi(VERSION_CODES.CUR_DEVELOPMENT)
+public class OmtpVvmSyncService {
+
+ private static final String TAG = OmtpVvmSyncService.class.getSimpleName();
+
+ /**
+ * Signifies a sync with both uploading to the server and downloading from the server.
+ */
+ public static final String SYNC_FULL_SYNC = "full_sync";
+ /**
+ * Only upload to the server.
+ */
+ public static final String SYNC_UPLOAD_ONLY = "upload_only";
+ /**
+ * Only download from the server.
+ */
+ public static final String SYNC_DOWNLOAD_ONLY = "download_only";
+ /**
+ * Only download single voicemail transcription.
+ */
+ public static final String SYNC_DOWNLOAD_ONE_TRANSCRIPTION =
+ "download_one_transcription";
+
+ private final Context mContext;
+
+ // Record the timestamp of the last full sync so that duplicate syncs can be reduced.
+ private static final String LAST_FULL_SYNC_TIMESTAMP = "last_full_sync_timestamp";
+ // Constant indicating that there has never been a full sync.
+ public static final long NO_PRIOR_FULL_SYNC = -1;
+
+ private VoicemailsQueryHelper mQueryHelper;
+
+ public OmtpVvmSyncService(Context context) {
+ mContext = context;
+ mQueryHelper = new VoicemailsQueryHelper(mContext);
+ }
+
+ public void sync(BaseTask task, String action, PhoneAccountHandle phoneAccount,
+ Voicemail voicemail, VoicemailStatus.Editor status) {
+ Assert.isTrue(phoneAccount != null);
+ VvmLog.v(TAG, "Sync requested: " + action + " - for account: " + phoneAccount);
+ setupAndSendRequest(task, phoneAccount, voicemail, action, status);
+ }
+
+ private void setupAndSendRequest(BaseTask task, PhoneAccountHandle phoneAccount,
+ Voicemail voicemail, String action, VoicemailStatus.Editor status) {
+ if (!VisualVoicemailSettingsUtil.isEnabled(mContext, phoneAccount)) {
+ VvmLog.v(TAG, "Sync requested for disabled account");
+ return;
+ }
+ if (!OmtpVvmSourceManager.getInstance(mContext).isVvmSourceRegistered(phoneAccount)) {
+ ActivationTask.start(mContext, phoneAccount, null);
+ return;
+ }
+
+ OmtpVvmCarrierConfigHelper config = new OmtpVvmCarrierConfigHelper(mContext, phoneAccount);
+ // DATA_IMAP_OPERATION_STARTED posting should not be deferred. This event clears all data
+ // channel errors, which should happen when the task starts, not when it ends. It is the
+ // "Sync in progress..." status.
+ config.handleEvent(VoicemailStatus.edit(mContext, phoneAccount),
+ OmtpEvents.DATA_IMAP_OPERATION_STARTED);
+ try (NetworkWrapper network = VvmNetworkRequest.getNetwork(config, phoneAccount, status)) {
+ if (network == null) {
+ VvmLog.e(TAG, "unable to acquire network");
+ task.fail();
+ return;
+ }
+ doSync(task, network.get(), phoneAccount, voicemail, action, status);
+ } catch (RequestFailedException e) {
+ config.handleEvent(status, OmtpEvents.DATA_NO_CONNECTION_CELLULAR_REQUIRED);
+ task.fail();
+ }
+ }
+
+ private void doSync(BaseTask task, Network network, PhoneAccountHandle phoneAccount,
+ Voicemail voicemail, String action, VoicemailStatus.Editor status) {
+ try (ImapHelper imapHelper = new ImapHelper(mContext, phoneAccount, network, status)) {
+ boolean success;
+ if (voicemail == null) {
+ success = syncAll(action, imapHelper, phoneAccount);
+ } else {
+ success = syncOne(imapHelper, voicemail, phoneAccount);
+ }
+ if (success) {
+ // TODO: b/30569269 failure should interrupt all subsequent task via exceptions
+ imapHelper.updateQuota();
+ imapHelper.handleEvent(OmtpEvents.DATA_IMAP_OPERATION_COMPLETED);
+ } else {
+ task.fail();
+ }
+ } catch (InitializingException e) {
+ VvmLog.w(TAG, "Can't retrieve Imap credentials.", e);
+ return;
+ }
+ }
+
+ private boolean syncAll(String action, ImapHelper imapHelper, PhoneAccountHandle account) {
+ boolean uploadSuccess = true;
+ boolean downloadSuccess = true;
+
+ if (SYNC_FULL_SYNC.equals(action) || SYNC_UPLOAD_ONLY.equals(action)) {
+ uploadSuccess = upload(imapHelper);
+ }
+ if (SYNC_FULL_SYNC.equals(action) || SYNC_DOWNLOAD_ONLY.equals(action)) {
+ downloadSuccess = download(imapHelper, account);
+ }
+
+ VvmLog.v(TAG, "upload succeeded: [" + String.valueOf(uploadSuccess)
+ + "] download succeeded: [" + String.valueOf(downloadSuccess) + "]");
+
+ return uploadSuccess && downloadSuccess;
+ }
+
+ private boolean syncOne(ImapHelper imapHelper, Voicemail voicemail,
+ PhoneAccountHandle account) {
+ if (shouldPerformPrefetch(account, imapHelper)) {
+ VoicemailFetchedCallback callback = new VoicemailFetchedCallback(mContext,
+ voicemail.getUri(), account);
+ imapHelper.fetchVoicemailPayload(callback, voicemail.getSourceData());
+ }
+
+ return imapHelper.fetchTranscription(
+ new TranscriptionFetchedCallback(mContext, voicemail),
+ voicemail.getSourceData());
+ }
+
+ private boolean upload(ImapHelper imapHelper) {
+ List<Voicemail> readVoicemails = mQueryHelper.getReadVoicemails();
+ List<Voicemail> deletedVoicemails = mQueryHelper.getDeletedVoicemails();
+
+ boolean success = true;
+
+ if (deletedVoicemails.size() > 0) {
+ if (imapHelper.markMessagesAsDeleted(deletedVoicemails)) {
+ // We want to delete selectively instead of all the voicemails for this provider
+ // in case the state changed since the IMAP query was completed.
+ mQueryHelper.deleteFromDatabase(deletedVoicemails);
+ } else {
+ success = false;
+ }
+ }
+
+ if (readVoicemails.size() > 0) {
+ if (imapHelper.markMessagesAsRead(readVoicemails)) {
+ mQueryHelper.markCleanInDatabase(readVoicemails);
+ } else {
+ success = false;
+ }
+ }
+
+ return success;
+ }
+
+ private boolean download(ImapHelper imapHelper, PhoneAccountHandle account) {
+ List<Voicemail> serverVoicemails = imapHelper.fetchAllVoicemails();
+ List<Voicemail> localVoicemails = mQueryHelper.getAllVoicemails();
+
+ if (localVoicemails == null || serverVoicemails == null) {
+ // Null value means the query failed.
+ return false;
+ }
+
+ Map<String, Voicemail> remoteMap = buildMap(serverVoicemails);
+
+ // Go through all the local voicemails and check if they are on the server.
+ // They may be read or deleted on the server but not locally. Perform the
+ // appropriate local operation if the status differs from the server. Remove
+ // the messages that exist both locally and on the server to know which server
+ // messages to insert locally.
+ for (int i = 0; i < localVoicemails.size(); i++) {
+ Voicemail localVoicemail = localVoicemails.get(i);
+ Voicemail remoteVoicemail = remoteMap.remove(localVoicemail.getSourceData());
+ if (remoteVoicemail == null) {
+ mQueryHelper.deleteFromDatabase(localVoicemail);
+ } else {
+ if (remoteVoicemail.isRead() != localVoicemail.isRead()) {
+ mQueryHelper.markReadInDatabase(localVoicemail);
+ }
+
+ if (!TextUtils.isEmpty(remoteVoicemail.getTranscription()) &&
+ TextUtils.isEmpty(localVoicemail.getTranscription())) {
+ mQueryHelper.updateWithTranscription(localVoicemail,
+ remoteVoicemail.getTranscription());
+ }
+ }
+ }
+
+ // The leftover messages are messages that exist on the server but not locally.
+ boolean prefetchEnabled = shouldPerformPrefetch(account, imapHelper);
+ for (Voicemail remoteVoicemail : remoteMap.values()) {
+ Uri uri = VoicemailDatabaseUtil.insert(mContext, remoteVoicemail);
+ if (prefetchEnabled) {
+ VoicemailFetchedCallback fetchedCallback =
+ new VoicemailFetchedCallback(mContext, uri, account);
+ imapHelper.fetchVoicemailPayload(fetchedCallback, remoteVoicemail.getSourceData());
+ }
+ }
+
+ return true;
+ }
+
+ private boolean shouldPerformPrefetch(PhoneAccountHandle account, ImapHelper imapHelper) {
+ OmtpVvmCarrierConfigHelper carrierConfigHelper =
+ new OmtpVvmCarrierConfigHelper(mContext, account);
+ return carrierConfigHelper.isPrefetchEnabled() && !imapHelper.isRoaming();
+ }
+
+ /**
+ * Builds a map from provider data to message for the given collection of voicemails.
+ */
+ private Map<String, Voicemail> buildMap(List<Voicemail> messages) {
+ Map<String, Voicemail> map = new HashMap<String, Voicemail>();
+ for (Voicemail message : messages) {
+ map.put(message.getSourceData(), message);
+ }
+ return map;
+ }
+
+ public class TranscriptionFetchedCallback {
+
+ private Context mContext;
+ private Voicemail mVoicemail;
+
+ public TranscriptionFetchedCallback(Context context, Voicemail voicemail) {
+ mContext = context;
+ mVoicemail = voicemail;
+ }
+
+ public void setVoicemailTranscription(String transcription) {
+ VoicemailsQueryHelper queryHelper = new VoicemailsQueryHelper(mContext);
+ queryHelper.updateWithTranscription(mVoicemail, transcription);
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/sync/SyncOneTask.java b/java/com/android/voicemailomtp/sync/SyncOneTask.java
new file mode 100644
index 000000000..9264e6c08
--- /dev/null
+++ b/java/com/android/voicemailomtp/sync/SyncOneTask.java
@@ -0,0 +1,82 @@
+/*
+ * 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.voicemailomtp.sync;
+
+import android.content.Context;
+import android.content.Intent;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.voicemailomtp.Voicemail;
+import com.android.voicemailomtp.VoicemailStatus;
+import com.android.voicemailomtp.scheduling.BaseTask;
+import com.android.voicemailomtp.scheduling.RetryPolicy;
+
+/**
+ * Task to download a single voicemail from the server. This task is initiated by a SMS notifying
+ * the new voicemail arrival, and ignores the duplicated tasks constraint.
+ */
+public class SyncOneTask extends BaseTask {
+
+ private static final int RETRY_TIMES = 2;
+ private static final int RETRY_INTERVAL_MILLIS = 5_000;
+
+ private static final String EXTRA_PHONE_ACCOUNT_HANDLE = "extra_phone_account_handle";
+ private static final String EXTRA_SYNC_TYPE = "extra_sync_type";
+ private static final String EXTRA_VOICEMAIL = "extra_voicemail";
+
+ private PhoneAccountHandle mPhone;
+ private String mSyncType;
+ private Voicemail mVoicemail;
+
+ public static void start(Context context, PhoneAccountHandle phone, Voicemail voicemail) {
+ Intent intent = BaseTask
+ .createIntent(context, SyncOneTask.class, phone);
+ intent.putExtra(EXTRA_PHONE_ACCOUNT_HANDLE, phone);
+ intent.putExtra(EXTRA_SYNC_TYPE, OmtpVvmSyncService.SYNC_DOWNLOAD_ONE_TRANSCRIPTION);
+ intent.putExtra(EXTRA_VOICEMAIL, voicemail);
+ context.startService(intent);
+ }
+
+ public SyncOneTask() {
+ super(TASK_ALLOW_DUPLICATES);
+ addPolicy(new RetryPolicy(RETRY_TIMES, RETRY_INTERVAL_MILLIS));
+ }
+
+ public void onCreate(Context context, Intent intent, int flags, int startId) {
+ super.onCreate(context, intent, flags, startId);
+ mPhone = intent.getParcelableExtra(EXTRA_PHONE_ACCOUNT_HANDLE);
+ mSyncType = intent.getStringExtra(EXTRA_SYNC_TYPE);
+ mVoicemail = intent.getParcelableExtra(EXTRA_VOICEMAIL);
+ }
+
+ @Override
+ public void onExecuteInBackgroundThread() {
+ OmtpVvmSyncService service = new OmtpVvmSyncService(getContext());
+ service.sync(this, mSyncType, mPhone, mVoicemail,
+ VoicemailStatus.edit(getContext(), mPhone));
+ }
+
+ @Override
+ public Intent createRestartIntent() {
+ Intent intent = super.createRestartIntent();
+ intent.putExtra(EXTRA_PHONE_ACCOUNT_HANDLE, mPhone);
+ intent.putExtra(EXTRA_SYNC_TYPE, mSyncType);
+ intent.putExtra(EXTRA_VOICEMAIL, mVoicemail);
+ return intent;
+ }
+
+}
diff --git a/java/com/android/voicemailomtp/sync/SyncTask.java b/java/com/android/voicemailomtp/sync/SyncTask.java
new file mode 100644
index 000000000..41b22f22c
--- /dev/null
+++ b/java/com/android/voicemailomtp/sync/SyncTask.java
@@ -0,0 +1,79 @@
+/*
+ * 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.voicemailomtp.sync;
+
+import android.content.Context;
+import android.content.Intent;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.voicemailomtp.scheduling.BaseTask;
+import com.android.voicemailomtp.scheduling.MinimalIntervalPolicy;
+import com.android.voicemailomtp.scheduling.RetryPolicy;
+
+/**
+ * System initiated sync request.
+ */
+public class SyncTask extends BaseTask {
+
+ // Try sync for a total of 5 times, should take around 5 minutes before finally giving up.
+ private static final int RETRY_TIMES = 4;
+ private static final int RETRY_INTERVAL_MILLIS = 5_000;
+ private static final int MINIMAL_INTERVAL_MILLIS = 60_000;
+
+ private static final String EXTRA_PHONE_ACCOUNT_HANDLE = "extra_phone_account_handle";
+ private static final String EXTRA_SYNC_TYPE = "extra_sync_type";
+
+ private final RetryPolicy mRetryPolicy;
+
+ private PhoneAccountHandle mPhone;
+ private String mSyncType;
+
+ public static void start(Context context, PhoneAccountHandle phone, String syncType) {
+ Intent intent = BaseTask
+ .createIntent(context, SyncTask.class, phone);
+ intent.putExtra(EXTRA_PHONE_ACCOUNT_HANDLE, phone);
+ intent.putExtra(EXTRA_SYNC_TYPE, syncType);
+ context.startService(intent);
+ }
+
+ public SyncTask() {
+ super(TASK_SYNC);
+ mRetryPolicy = new RetryPolicy(RETRY_TIMES, RETRY_INTERVAL_MILLIS);
+ addPolicy(mRetryPolicy);
+ addPolicy(new MinimalIntervalPolicy(MINIMAL_INTERVAL_MILLIS));
+ }
+
+ public void onCreate(Context context, Intent intent, int flags, int startId) {
+ super.onCreate(context, intent, flags, startId);
+ mPhone = intent.getParcelableExtra(EXTRA_PHONE_ACCOUNT_HANDLE);
+ mSyncType = intent.getStringExtra(EXTRA_SYNC_TYPE);
+ }
+
+ @Override
+ public void onExecuteInBackgroundThread() {
+ OmtpVvmSyncService service = new OmtpVvmSyncService(getContext());
+ service.sync(this, mSyncType, mPhone, null, mRetryPolicy.getVoicemailStatusEditor());
+ }
+
+ @Override
+ public Intent createRestartIntent() {
+ Intent intent = super.createRestartIntent();
+ intent.putExtra(EXTRA_PHONE_ACCOUNT_HANDLE, mPhone);
+ intent.putExtra(EXTRA_SYNC_TYPE, mSyncType);
+ return intent;
+ }
+}
diff --git a/java/com/android/voicemailomtp/sync/UploadTask.java b/java/com/android/voicemailomtp/sync/UploadTask.java
new file mode 100644
index 000000000..30a16812b
--- /dev/null
+++ b/java/com/android/voicemailomtp/sync/UploadTask.java
@@ -0,0 +1,68 @@
+/*
+ * 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.voicemailomtp.sync;
+
+import android.content.Context;
+import android.content.Intent;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.voicemailomtp.VoicemailStatus;
+import com.android.voicemailomtp.VvmLog;
+import com.android.voicemailomtp.scheduling.BaseTask;
+import com.android.voicemailomtp.scheduling.PostponePolicy;
+
+/**
+ * Upload task triggered by database changes. Will wait until the database has been stable for
+ * {@link #POSTPONE_MILLIS} to execute.
+ */
+public class UploadTask extends BaseTask {
+
+ private static final String TAG = "VvmUploadTask";
+
+ private static final int POSTPONE_MILLIS = 5_000;
+
+ public UploadTask() {
+ super(TASK_UPLOAD);
+ addPolicy(new PostponePolicy(POSTPONE_MILLIS));
+ }
+
+ public static void start(Context context, PhoneAccountHandle phoneAccountHandle) {
+ Intent intent = BaseTask
+ .createIntent(context, UploadTask.class, phoneAccountHandle);
+ context.startService(intent);
+ }
+
+ @Override
+ public void onCreate(Context context, Intent intent, int flags, int startId) {
+ super.onCreate(context, intent, flags, startId);
+ }
+
+ @Override
+ public void onExecuteInBackgroundThread() {
+ OmtpVvmSyncService service = new OmtpVvmSyncService(getContext());
+
+ PhoneAccountHandle phoneAccountHandle = getPhoneAccountHandle();
+ if (phoneAccountHandle == null) {
+ // This should never happen
+ VvmLog.e(TAG, "null phone account for phoneAccountHandle " + getPhoneAccountHandle());
+ return;
+ }
+ service.sync(this, OmtpVvmSyncService.SYNC_UPLOAD_ONLY,
+ phoneAccountHandle, null,
+ VoicemailStatus.edit(getContext(), phoneAccountHandle));
+ }
+}
diff --git a/java/com/android/voicemailomtp/sync/VoicemailProviderChangeReceiver.java b/java/com/android/voicemailomtp/sync/VoicemailProviderChangeReceiver.java
new file mode 100644
index 000000000..ade9ef12d
--- /dev/null
+++ b/java/com/android/voicemailomtp/sync/VoicemailProviderChangeReceiver.java
@@ -0,0 +1,41 @@
+/*
+ * 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.voicemailomtp.sync;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.VoicemailContract;
+import android.telecom.PhoneAccountHandle;
+
+/**
+ * Receives changes to the voicemail provider so they can be sent to the voicemail server.
+ */
+public class VoicemailProviderChangeReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean isSelfChanged = intent.getBooleanExtra(VoicemailContract.EXTRA_SELF_CHANGE, false);
+ OmtpVvmSourceManager vvmSourceManager =
+ OmtpVvmSourceManager.getInstance(context);
+ if (vvmSourceManager.getOmtpVvmSources().size() > 0 && !isSelfChanged) {
+ for (PhoneAccountHandle source : OmtpVvmSourceManager.getInstance(context)
+ .getOmtpVvmSources()) {
+ UploadTask.start(context, source);
+ }
+ }
+ }
+}
diff --git a/java/com/android/voicemailomtp/sync/VoicemailStatusQueryHelper.java b/java/com/android/voicemailomtp/sync/VoicemailStatusQueryHelper.java
new file mode 100644
index 000000000..89ba0b494
--- /dev/null
+++ b/java/com/android/voicemailomtp/sync/VoicemailStatusQueryHelper.java
@@ -0,0 +1,113 @@
+/*
+ * 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.voicemailomtp.sync;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.VoicemailContract;
+import android.provider.VoicemailContract.Status;
+import android.telecom.PhoneAccountHandle;
+
+/**
+ * Construct queries to interact with the voicemail status table.
+ */
+public class VoicemailStatusQueryHelper {
+
+ final static String[] PROJECTION = new String[] {
+ Status._ID, // 0
+ Status.CONFIGURATION_STATE, // 1
+ Status.NOTIFICATION_CHANNEL_STATE, // 2
+ Status.SOURCE_PACKAGE // 3
+ };
+
+ public static final int _ID = 0;
+ public static final int CONFIGURATION_STATE = 1;
+ public static final int NOTIFICATION_CHANNEL_STATE = 2;
+ public static final int SOURCE_PACKAGE = 3;
+
+ private Context mContext;
+ private ContentResolver mContentResolver;
+ private Uri mSourceUri;
+
+ public VoicemailStatusQueryHelper(Context context) {
+ mContext = context;
+ mContentResolver = context.getContentResolver();
+ mSourceUri = VoicemailContract.Status.buildSourceUri(mContext.getPackageName());
+ }
+
+ /**
+ * Check if the configuration state for the voicemail source is "ok", meaning that the
+ * source is set up.
+ *
+ * @param phoneAccount The phone account for the voicemail source to check.
+ * @return {@code true} if the voicemail source is configured, {@code} false otherwise,
+ * including if the voicemail source is not registered in the table.
+ */
+ public boolean isVoicemailSourceConfigured(PhoneAccountHandle phoneAccount) {
+ return isFieldEqualTo(phoneAccount, CONFIGURATION_STATE, Status.CONFIGURATION_STATE_OK);
+ }
+
+ /**
+ * Check if the notifications channel of a voicemail source is active. That is, when a new
+ * voicemail is available, if the server able to notify the device.
+ *
+ * @return {@code true} if notifications channel is active, {@code false} otherwise.
+ */
+ public boolean isNotificationsChannelActive(PhoneAccountHandle phoneAccount) {
+ return isFieldEqualTo(phoneAccount, NOTIFICATION_CHANNEL_STATE,
+ Status.NOTIFICATION_CHANNEL_STATE_OK);
+ }
+
+ /**
+ * Check if a field for an entry in the status table is equal to a specific value.
+ *
+ * @param phoneAccount The phone account of the voicemail source to query for.
+ * @param columnIndex The column index of the field in the returned query.
+ * @param value The value to compare against.
+ * @return {@code true} if the stored value is equal to the provided value. {@code false}
+ * otherwise.
+ */
+ private boolean isFieldEqualTo(PhoneAccountHandle phoneAccount, int columnIndex, int value) {
+ Cursor cursor = null;
+ if (phoneAccount != null) {
+ String phoneAccountComponentName = phoneAccount.getComponentName().flattenToString();
+ String phoneAccountId = phoneAccount.getId();
+ if (phoneAccountComponentName == null || phoneAccountId == null) {
+ return false;
+ }
+ try {
+ String whereClause =
+ Status.PHONE_ACCOUNT_COMPONENT_NAME + "=? AND " +
+ Status.PHONE_ACCOUNT_ID + "=? AND " + Status.SOURCE_PACKAGE + "=?";
+ String[] whereArgs = { phoneAccountComponentName, phoneAccountId,
+ mContext.getPackageName()};
+ cursor = mContentResolver.query(
+ mSourceUri, PROJECTION, whereClause, whereArgs, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ return cursor.getInt(columnIndex) == value;
+ }
+ }
+ finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/java/com/android/voicemailomtp/sync/VoicemailsQueryHelper.java b/java/com/android/voicemailomtp/sync/VoicemailsQueryHelper.java
new file mode 100644
index 000000000..1450e3d1b
--- /dev/null
+++ b/java/com/android/voicemailomtp/sync/VoicemailsQueryHelper.java
@@ -0,0 +1,244 @@
+/*
+ * 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.voicemailomtp.sync;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.VoicemailContract;
+import android.provider.VoicemailContract.Voicemails;
+import android.telecom.PhoneAccountHandle;
+import com.android.voicemailomtp.Voicemail;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Construct queries to interact with the voicemails table.
+ */
+public class VoicemailsQueryHelper {
+ final static String[] PROJECTION = new String[] {
+ Voicemails._ID, // 0
+ Voicemails.SOURCE_DATA, // 1
+ Voicemails.IS_READ, // 2
+ Voicemails.DELETED, // 3
+ Voicemails.TRANSCRIPTION // 4
+ };
+
+ public static final int _ID = 0;
+ public static final int SOURCE_DATA = 1;
+ public static final int IS_READ = 2;
+ public static final int DELETED = 3;
+ public static final int TRANSCRIPTION = 4;
+
+ final static String READ_SELECTION = Voicemails.DIRTY + "=1 AND "
+ + Voicemails.DELETED + "!=1 AND " + Voicemails.IS_READ + "=1";
+ final static String DELETED_SELECTION = Voicemails.DELETED + "=1";
+
+ private Context mContext;
+ private ContentResolver mContentResolver;
+ private Uri mSourceUri;
+
+ public VoicemailsQueryHelper(Context context) {
+ mContext = context;
+ mContentResolver = context.getContentResolver();
+ mSourceUri = VoicemailContract.Voicemails.buildSourceUri(mContext.getPackageName());
+ }
+
+ /**
+ * Get all the local read voicemails that have not been synced to the server.
+ *
+ * @return A list of read voicemails.
+ */
+ public List<Voicemail> getReadVoicemails() {
+ return getLocalVoicemails(READ_SELECTION);
+ }
+
+ /**
+ * Get all the locally deleted voicemails that have not been synced to the server.
+ *
+ * @return A list of deleted voicemails.
+ */
+ public List<Voicemail> getDeletedVoicemails() {
+ return getLocalVoicemails(DELETED_SELECTION);
+ }
+
+ /**
+ * Get all voicemails locally stored.
+ *
+ * @return A list of all locally stored voicemails.
+ */
+ public List<Voicemail> getAllVoicemails() {
+ return getLocalVoicemails(null);
+ }
+
+ /**
+ * Utility method to make queries to the voicemail database.
+ *
+ * @param selection A filter declaring which rows to return. {@code null} returns all rows.
+ * @return A list of voicemails according to the selection statement.
+ */
+ private List<Voicemail> getLocalVoicemails(String selection) {
+ Cursor cursor = mContentResolver.query(mSourceUri, PROJECTION, selection, null, null);
+ if (cursor == null) {
+ return null;
+ }
+ try {
+ List<Voicemail> voicemails = new ArrayList<Voicemail>();
+ while (cursor.moveToNext()) {
+ final long id = cursor.getLong(_ID);
+ final String sourceData = cursor.getString(SOURCE_DATA);
+ final boolean isRead = cursor.getInt(IS_READ) == 1;
+ final String transcription = cursor.getString(TRANSCRIPTION);
+ Voicemail voicemail = Voicemail
+ .createForUpdate(id, sourceData)
+ .setIsRead(isRead)
+ .setTranscription(transcription).build();
+ voicemails.add(voicemail);
+ }
+ return voicemails;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Deletes a list of voicemails from the voicemail content provider.
+ *
+ * @param voicemails The list of voicemails to delete
+ * @return The number of voicemails deleted
+ */
+ public int deleteFromDatabase(List<Voicemail> voicemails) {
+ int count = voicemails.size();
+ if (count == 0) {
+ return 0;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < count; i++) {
+ if (i > 0) {
+ sb.append(",");
+ }
+ sb.append(voicemails.get(i).getId());
+ }
+
+ String selectionStatement = String.format(Voicemails._ID + " IN (%s)", sb.toString());
+ return mContentResolver.delete(Voicemails.CONTENT_URI, selectionStatement, null);
+ }
+
+ /**
+ * Utility method to delete a single voicemail.
+ */
+ public void deleteFromDatabase(Voicemail voicemail) {
+ mContentResolver.delete(Voicemails.CONTENT_URI, Voicemails._ID + "=?",
+ new String[] { Long.toString(voicemail.getId()) });
+ }
+
+ public int markReadInDatabase(List<Voicemail> voicemails) {
+ int count = voicemails.size();
+ for (int i = 0; i < count; i++) {
+ markReadInDatabase(voicemails.get(i));
+ }
+ return count;
+ }
+
+ /**
+ * Utility method to mark single message as read.
+ */
+ public void markReadInDatabase(Voicemail voicemail) {
+ Uri uri = ContentUris.withAppendedId(mSourceUri, voicemail.getId());
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Voicemails.IS_READ, "1");
+ mContentResolver.update(uri, contentValues, null, null);
+ }
+
+ /**
+ * Sends an update command to the voicemail content provider for a list of voicemails. From the
+ * view of the provider, since the updater is the owner of the entry, a blank "update" means
+ * that the voicemail source is indicating that the server has up-to-date information on the
+ * voicemail. This flips the "dirty" bit to "0".
+ *
+ * @param voicemails The list of voicemails to update
+ * @return The number of voicemails updated
+ */
+ public int markCleanInDatabase(List<Voicemail> voicemails) {
+ int count = voicemails.size();
+ for (int i = 0; i < count; i++) {
+ markCleanInDatabase(voicemails.get(i));
+ }
+ return count;
+ }
+
+ /**
+ * Utility method to mark single message as clean.
+ */
+ public void markCleanInDatabase(Voicemail voicemail) {
+ Uri uri = ContentUris.withAppendedId(mSourceUri, voicemail.getId());
+ ContentValues contentValues = new ContentValues();
+ mContentResolver.update(uri, contentValues, null, null);
+ }
+
+ /**
+ * Utility method to add a transcription to the voicemail.
+ */
+ public void updateWithTranscription(Voicemail voicemail, String transcription) {
+ Uri uri = ContentUris.withAppendedId(mSourceUri, voicemail.getId());
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Voicemails.TRANSCRIPTION, transcription);
+ mContentResolver.update(uri, contentValues, null, null);
+ }
+
+ /**
+ * Voicemail is unique if the tuple of (phone account component name, phone account id, source
+ * data) is unique. If the phone account is missing, we also consider this unique since it's
+ * simply an "unknown" account.
+ * @param voicemail The voicemail to check if it is unique.
+ * @return {@code true} if the voicemail is unique, {@code false} otherwise.
+ */
+ public boolean isVoicemailUnique(Voicemail voicemail) {
+ Cursor cursor = null;
+ PhoneAccountHandle phoneAccount = voicemail.getPhoneAccount();
+ if (phoneAccount != null) {
+ String phoneAccountComponentName = phoneAccount.getComponentName().flattenToString();
+ String phoneAccountId = phoneAccount.getId();
+ String sourceData = voicemail.getSourceData();
+ if (phoneAccountComponentName == null || phoneAccountId == null || sourceData == null) {
+ return true;
+ }
+ try {
+ String whereClause =
+ Voicemails.PHONE_ACCOUNT_COMPONENT_NAME + "=? AND " +
+ Voicemails.PHONE_ACCOUNT_ID + "=? AND " + Voicemails.SOURCE_DATA + "=?";
+ String[] whereArgs = { phoneAccountComponentName, phoneAccountId, sourceData };
+ cursor = mContentResolver.query(
+ mSourceUri, PROJECTION, whereClause, whereArgs, null);
+ if (cursor.getCount() == 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/java/com/android/voicemailomtp/sync/VvmNetworkRequest.java b/java/com/android/voicemailomtp/sync/VvmNetworkRequest.java
new file mode 100644
index 000000000..966b940c2
--- /dev/null
+++ b/java/com/android/voicemailomtp/sync/VvmNetworkRequest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.voicemailomtp.sync;
+
+import android.annotation.TargetApi;
+import android.net.Network;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.NonNull;
+import android.telecom.PhoneAccountHandle;
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.VoicemailStatus;
+import com.android.voicemailomtp.VvmLog;
+import java.io.Closeable;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+/**
+ * Class to retrieve a {@link Network} synchronously. {@link #getNetwork(OmtpVvmCarrierConfigHelper,
+ * PhoneAccountHandle)} will block until a suitable network is retrieved or it has failed.
+ */
+@SuppressWarnings("AndroidApiChecker") /* CompletableFuture is java8*/
+@TargetApi(VERSION_CODES.CUR_DEVELOPMENT)
+public class VvmNetworkRequest {
+
+ private static final String TAG = "VvmNetworkRequest";
+
+ /**
+ * A wrapper around a Network returned by a {@link VvmNetworkRequestCallback}, which should be
+ * closed once not needed anymore.
+ */
+ public static class NetworkWrapper implements Closeable {
+
+ private final Network mNetwork;
+ private final VvmNetworkRequestCallback mCallback;
+
+ private NetworkWrapper(Network network, VvmNetworkRequestCallback callback) {
+ mNetwork = network;
+ mCallback = callback;
+ }
+
+ public Network get() {
+ return mNetwork;
+ }
+
+ @Override
+ public void close() {
+ mCallback.releaseNetwork();
+ }
+ }
+
+ public static class RequestFailedException extends Exception {
+
+ private RequestFailedException(Throwable cause) {
+ super(cause);
+ }
+ }
+
+ @NonNull
+ public static NetworkWrapper getNetwork(OmtpVvmCarrierConfigHelper config,
+ PhoneAccountHandle handle, VoicemailStatus.Editor status) throws RequestFailedException {
+ FutureNetworkRequestCallback callback = new FutureNetworkRequestCallback(config, handle,
+ status);
+ callback.requestNetwork();
+ try {
+ return callback.getFuture().get();
+ } catch (InterruptedException | ExecutionException e) {
+ callback.releaseNetwork();
+ VvmLog.e(TAG, "can't get future network", e);
+ throw new RequestFailedException(e);
+ }
+ }
+
+ private static class FutureNetworkRequestCallback extends VvmNetworkRequestCallback {
+
+ /**
+ * {@link CompletableFuture#get()} will block until {@link CompletableFuture#
+ * complete(Object) } has been called on the other thread.
+ */
+ private final CompletableFuture<NetworkWrapper> mFuture = new CompletableFuture<>();
+
+ public FutureNetworkRequestCallback(OmtpVvmCarrierConfigHelper config,
+ PhoneAccountHandle phoneAccount, VoicemailStatus.Editor status) {
+ super(config, phoneAccount, status);
+ }
+
+ public Future<NetworkWrapper> getFuture() {
+ return mFuture;
+ }
+
+ @Override
+ public void onAvailable(Network network) {
+ super.onAvailable(network);
+ mFuture.complete(new NetworkWrapper(network, this));
+ }
+
+ @Override
+ public void onFailed(String reason) {
+ super.onFailed(reason);
+ mFuture.complete(null);
+ }
+
+ }
+}
diff --git a/java/com/android/voicemailomtp/sync/VvmNetworkRequestCallback.java b/java/com/android/voicemailomtp/sync/VvmNetworkRequestCallback.java
new file mode 100644
index 000000000..8481a9d16
--- /dev/null
+++ b/java/com/android/voicemailomtp/sync/VvmNetworkRequestCallback.java
@@ -0,0 +1,171 @@
+/*
+ * 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.voicemailomtp.sync;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.CallSuper;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.voicemailomtp.OmtpEvents;
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.TelephonyManagerStub;
+import com.android.voicemailomtp.VoicemailStatus;
+import com.android.voicemailomtp.VvmLog;
+
+/**
+ * Base class for network request call backs for visual voicemail syncing with the Imap server. This
+ * handles retries and network requests.
+ */
+public abstract class VvmNetworkRequestCallback extends ConnectivityManager.NetworkCallback {
+
+ private static final String TAG = "VvmNetworkRequest";
+
+ // Timeout used to call ConnectivityManager.requestNetwork
+ private static final int NETWORK_REQUEST_TIMEOUT_MILLIS = 60 * 1000;
+
+ public static final String NETWORK_REQUEST_FAILED_TIMEOUT = "timeout";
+ public static final String NETWORK_REQUEST_FAILED_LOST = "lost";
+
+ protected Context mContext;
+ protected PhoneAccountHandle mPhoneAccount;
+ protected NetworkRequest mNetworkRequest;
+ private ConnectivityManager mConnectivityManager;
+ private final OmtpVvmCarrierConfigHelper mCarrierConfigHelper;
+ private final VoicemailStatus.Editor mStatus;
+ private boolean mRequestSent = false;
+ private boolean mResultReceived = false;
+
+ public VvmNetworkRequestCallback(Context context, PhoneAccountHandle phoneAccount,
+ VoicemailStatus.Editor status) {
+ mContext = context;
+ mPhoneAccount = phoneAccount;
+ mStatus = status;
+ mCarrierConfigHelper = new OmtpVvmCarrierConfigHelper(context, mPhoneAccount);
+ mNetworkRequest = createNetworkRequest();
+ }
+
+ public VvmNetworkRequestCallback(OmtpVvmCarrierConfigHelper config,
+ PhoneAccountHandle phoneAccount, VoicemailStatus.Editor status) {
+ mContext = config.getContext();
+ mPhoneAccount = phoneAccount;
+ mStatus = status;
+ mCarrierConfigHelper = config;
+ mNetworkRequest = createNetworkRequest();
+ }
+
+ public VoicemailStatus.Editor getVoicemailStatusEditor() {
+ return mStatus;
+ }
+
+ /**
+ * @return NetworkRequest for a proper transport type. Use only cellular network if the carrier
+ * requires it. Otherwise use whatever available.
+ */
+ private NetworkRequest createNetworkRequest() {
+
+ NetworkRequest.Builder builder = new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+ if (mCarrierConfigHelper.isCellularDataRequired()) {
+ VvmLog.d(TAG, "Transport type: CELLULAR");
+ builder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(TelephonyManagerStub
+ .getNetworkSpecifierForPhoneAccountHandle(mContext, mPhoneAccount));
+ } else {
+ VvmLog.d(TAG, "Transport type: ANY");
+ }
+ return builder.build();
+ }
+
+ public NetworkRequest getNetworkRequest() {
+ return mNetworkRequest;
+ }
+
+ @Override
+ @CallSuper
+ public void onLost(Network network) {
+ VvmLog.d(TAG, "onLost");
+ mResultReceived = true;
+ onFailed(NETWORK_REQUEST_FAILED_LOST);
+ }
+
+ @Override
+ @CallSuper
+ public void onAvailable(Network network) {
+ super.onAvailable(network);
+ mResultReceived = true;
+ }
+
+ @CallSuper
+ public void onUnavailable() {
+ // TODO: b/32637799 this is hidden, do we really need this?
+ mResultReceived = true;
+ onFailed(NETWORK_REQUEST_FAILED_TIMEOUT);
+ }
+
+ public void requestNetwork() {
+ if (mRequestSent == true) {
+ VvmLog.e(TAG, "requestNetwork() called twice");
+ return;
+ }
+ mRequestSent = true;
+ getConnectivityManager().requestNetwork(getNetworkRequest(), this);
+ /**
+ * Somehow requestNetwork() with timeout doesn't work, and it's a hidden method.
+ * Implement our own timeout mechanism instead.
+ */
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (mResultReceived == false) {
+ onFailed(NETWORK_REQUEST_FAILED_TIMEOUT);
+ }
+ }
+ }, NETWORK_REQUEST_TIMEOUT_MILLIS);
+ }
+
+ public void releaseNetwork() {
+ VvmLog.d(TAG, "releaseNetwork");
+ getConnectivityManager().unregisterNetworkCallback(this);
+ }
+
+ public ConnectivityManager getConnectivityManager() {
+ if (mConnectivityManager == null) {
+ mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ }
+ return mConnectivityManager;
+ }
+
+ @CallSuper
+ public void onFailed(String reason) {
+ VvmLog.d(TAG, "onFailed: " + reason);
+ if (mCarrierConfigHelper.isCellularDataRequired()) {
+ mCarrierConfigHelper
+ .handleEvent(mStatus, OmtpEvents.DATA_NO_CONNECTION_CELLULAR_REQUIRED);
+ } else {
+ mCarrierConfigHelper.handleEvent(mStatus, OmtpEvents.DATA_NO_CONNECTION);
+ }
+ releaseNetwork();
+ }
+}
diff --git a/java/com/android/voicemailomtp/utils/IndentingPrintWriter.java b/java/com/android/voicemailomtp/utils/IndentingPrintWriter.java
new file mode 100644
index 000000000..eda7c4ee3
--- /dev/null
+++ b/java/com/android/voicemailomtp/utils/IndentingPrintWriter.java
@@ -0,0 +1,160 @@
+/*
+ * 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.voicemailomtp.utils;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Arrays;
+
+/**
+ * Lightweight wrapper around {@link PrintWriter} that automatically indents newlines based on
+ * internal state. It also automatically wraps long lines based on given line length. <p> Delays
+ * writing indent until first actual write on a newline, enabling indent modification after
+ * newline.
+ */
+public class IndentingPrintWriter extends PrintWriter {
+
+ private final String mSingleIndent;
+ private final int mWrapLength;
+
+ /**
+ * Mutable version of current indent
+ */
+ private StringBuilder mIndentBuilder = new StringBuilder();
+ /**
+ * Cache of current {@link #mIndentBuilder} value
+ */
+ private char[] mCurrentIndent;
+ /**
+ * Length of current line being built, excluding any indent
+ */
+ private int mCurrentLength;
+
+ /**
+ * Flag indicating if we're currently sitting on an empty line, and that next write should be
+ * prefixed with the current indent.
+ */
+ private boolean mEmptyLine = true;
+
+ private char[] mSingleChar = new char[1];
+
+ public IndentingPrintWriter(Writer writer, String singleIndent) {
+ this(writer, singleIndent, -1);
+ }
+
+ public IndentingPrintWriter(Writer writer, String singleIndent, int wrapLength) {
+ super(writer);
+ mSingleIndent = singleIndent;
+ mWrapLength = wrapLength;
+ }
+
+ public void increaseIndent() {
+ mIndentBuilder.append(mSingleIndent);
+ mCurrentIndent = null;
+ }
+
+ public void decreaseIndent() {
+ mIndentBuilder.delete(0, mSingleIndent.length());
+ mCurrentIndent = null;
+ }
+
+ public void printPair(String key, Object value) {
+ print(key + "=" + String.valueOf(value) + " ");
+ }
+
+ public void printPair(String key, Object[] value) {
+ print(key + "=" + Arrays.toString(value) + " ");
+ }
+
+ public void printHexPair(String key, int value) {
+ print(key + "=0x" + Integer.toHexString(value) + " ");
+ }
+
+ @Override
+ public void println() {
+ write('\n');
+ }
+
+ @Override
+ public void write(int c) {
+ mSingleChar[0] = (char) c;
+ write(mSingleChar, 0, 1);
+ }
+
+ @Override
+ public void write(String s, int off, int len) {
+ final char[] buf = new char[len];
+ s.getChars(off, len - off, buf, 0);
+ write(buf, 0, len);
+ }
+
+ @Override
+ public void write(char[] buf, int offset, int count) {
+ final int indentLength = mIndentBuilder.length();
+ final int bufferEnd = offset + count;
+ int lineStart = offset;
+ int lineEnd = offset;
+
+ // March through incoming buffer looking for newlines
+ while (lineEnd < bufferEnd) {
+ char ch = buf[lineEnd++];
+ mCurrentLength++;
+ if (ch == '\n') {
+ maybeWriteIndent();
+ super.write(buf, lineStart, lineEnd - lineStart);
+ lineStart = lineEnd;
+ mEmptyLine = true;
+ mCurrentLength = 0;
+ }
+
+ // Wrap if we've pushed beyond line length
+ if (mWrapLength > 0 && mCurrentLength >= mWrapLength - indentLength) {
+ if (!mEmptyLine) {
+ // Give ourselves a fresh line to work with
+ super.write('\n');
+ mEmptyLine = true;
+ mCurrentLength = lineEnd - lineStart;
+ } else {
+ // We need more than a dedicated line, slice it hard
+ maybeWriteIndent();
+ super.write(buf, lineStart, lineEnd - lineStart);
+ super.write('\n');
+ mEmptyLine = true;
+ lineStart = lineEnd;
+ mCurrentLength = 0;
+ }
+ }
+ }
+
+ if (lineStart != lineEnd) {
+ maybeWriteIndent();
+ super.write(buf, lineStart, lineEnd - lineStart);
+ }
+ }
+
+ private void maybeWriteIndent() {
+ if (mEmptyLine) {
+ mEmptyLine = false;
+ if (mIndentBuilder.length() != 0) {
+ if (mCurrentIndent == null) {
+ mCurrentIndent = mIndentBuilder.toString().toCharArray();
+ }
+ super.write(mCurrentIndent, 0, mCurrentIndent.length);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/utils/VoicemailDatabaseUtil.java b/java/com/android/voicemailomtp/utils/VoicemailDatabaseUtil.java
new file mode 100644
index 000000000..f94070ecd
--- /dev/null
+++ b/java/com/android/voicemailomtp/utils/VoicemailDatabaseUtil.java
@@ -0,0 +1,90 @@
+/*
+ * 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.voicemailomtp.utils;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.provider.VoicemailContract.Voicemails;
+import android.telecom.PhoneAccountHandle;
+import com.android.voicemailomtp.Voicemail;
+import java.util.List;
+
+public class VoicemailDatabaseUtil {
+
+ /**
+ * Inserts a new voicemail into the voicemail content provider.
+ *
+ * @param context The context of the app doing the inserting
+ * @param voicemail Data to be inserted
+ * @return {@link Uri} of the newly inserted {@link Voicemail}
+ * @hide
+ */
+ public static Uri insert(Context context, Voicemail voicemail) {
+ ContentResolver contentResolver = context.getContentResolver();
+ ContentValues contentValues = getContentValues(voicemail);
+ return contentResolver
+ .insert(Voicemails.buildSourceUri(context.getPackageName()), contentValues);
+ }
+
+ /**
+ * Inserts a list of voicemails into the voicemail content provider.
+ *
+ * @param context The context of the app doing the inserting
+ * @param voicemails Data to be inserted
+ * @return the number of voicemails inserted
+ * @hide
+ */
+ public static int insert(Context context, List<Voicemail> voicemails) {
+ ContentResolver contentResolver = context.getContentResolver();
+ int count = voicemails.size();
+ for (int i = 0; i < count; i++) {
+ ContentValues contentValues = getContentValues(voicemails.get(i));
+ contentResolver
+ .insert(Voicemails.buildSourceUri(context.getPackageName()), contentValues);
+ }
+ return count;
+ }
+
+
+ /**
+ * Maps structured {@link Voicemail} to {@link ContentValues} in content provider.
+ */
+ private static ContentValues getContentValues(Voicemail voicemail) {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Voicemails.DATE, String.valueOf(voicemail.getTimestampMillis()));
+ contentValues.put(Voicemails.NUMBER, voicemail.getNumber());
+ contentValues.put(Voicemails.DURATION, String.valueOf(voicemail.getDuration()));
+ contentValues.put(Voicemails.SOURCE_PACKAGE, voicemail.getSourcePackage());
+ contentValues.put(Voicemails.SOURCE_DATA, voicemail.getSourceData());
+ contentValues.put(Voicemails.IS_READ, voicemail.isRead() ? 1 : 0);
+
+ PhoneAccountHandle phoneAccount = voicemail.getPhoneAccount();
+ if (phoneAccount != null) {
+ contentValues.put(Voicemails.PHONE_ACCOUNT_COMPONENT_NAME,
+ phoneAccount.getComponentName().flattenToString());
+ contentValues.put(Voicemails.PHONE_ACCOUNT_ID, phoneAccount.getId());
+ }
+
+ if (voicemail.getTranscription() != null) {
+ contentValues.put(Voicemails.TRANSCRIPTION, voicemail.getTranscription());
+ }
+
+ return contentValues;
+ }
+}
diff --git a/java/com/android/voicemailomtp/utils/VvmDumpHandler.java b/java/com/android/voicemailomtp/utils/VvmDumpHandler.java
new file mode 100644
index 000000000..5768a9c19
--- /dev/null
+++ b/java/com/android/voicemailomtp/utils/VvmDumpHandler.java
@@ -0,0 +1,46 @@
+/*
+ * 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.voicemailomtp.utils;
+
+import android.content.Context;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+
+import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper;
+import com.android.voicemailomtp.VvmLog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public class VvmDumpHandler {
+
+ public static void dump(Context context, FileDescriptor fd, PrintWriter writer,
+ String[] args) {
+ IndentingPrintWriter indentedWriter = new IndentingPrintWriter(writer, " ");
+ indentedWriter.println("******* OmtpVvm *******");
+ indentedWriter.println("======= Configs =======");
+ indentedWriter.increaseIndent();
+ for (PhoneAccountHandle handle : context.getSystemService(TelecomManager.class)
+ .getCallCapablePhoneAccounts()) {
+ OmtpVvmCarrierConfigHelper config = new OmtpVvmCarrierConfigHelper(context, handle);
+ indentedWriter.println(config.toString());
+ }
+ indentedWriter.decreaseIndent();
+ indentedWriter.println("======== Logs =========");
+ VvmLog.dump(fd, indentedWriter, args);
+ }
+}
diff --git a/java/com/android/voicemailomtp/utils/XmlUtils.java b/java/com/android/voicemailomtp/utils/XmlUtils.java
new file mode 100644
index 000000000..768247e27
--- /dev/null
+++ b/java/com/android/voicemailomtp/utils/XmlUtils.java
@@ -0,0 +1,245 @@
+/*
+ * 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.voicemailomtp.utils;
+
+import android.util.ArrayMap;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class XmlUtils {
+
+ public static final ArrayMap<String, ?> readThisArrayMapXml(XmlPullParser parser, String endTag,
+ String[] name, ReadMapCallback callback)
+ throws XmlPullParserException, java.io.IOException {
+ ArrayMap<String, Object> map = new ArrayMap<>();
+
+ int eventType = parser.getEventType();
+ do {
+ if (eventType == XmlPullParser.START_TAG) {
+ Object val = readThisValueXml(parser, name, callback, true);
+ map.put(name[0], val);
+ } else if (eventType == XmlPullParser.END_TAG) {
+ if (parser.getName().equals(endTag)) {
+ return map;
+ }
+ throw new XmlPullParserException(
+ "Expected " + endTag + " end tag at: " + parser.getName());
+ }
+ eventType = parser.next();
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ throw new XmlPullParserException(
+ "Document ended before " + endTag + " end tag");
+ }
+
+ /**
+ * Read an ArrayList object from an XmlPullParser. The XML data could previously have been
+ * generated by writeListXml(). The XmlPullParser must be positioned <em>after</em> the tag
+ * that begins the list.
+ *
+ * @param parser The XmlPullParser from which to read the list data.
+ * @param endTag Name of the tag that will end the list, usually "list".
+ * @param name An array of one string, used to return the name attribute of the list's tag.
+ * @return HashMap The newly generated list.
+ */
+ public static final ArrayList readThisListXml(XmlPullParser parser, String endTag,
+ String[] name, ReadMapCallback callback, boolean arrayMap)
+ throws XmlPullParserException, java.io.IOException {
+ ArrayList list = new ArrayList();
+
+ int eventType = parser.getEventType();
+ do {
+ if (eventType == XmlPullParser.START_TAG) {
+ Object val = readThisValueXml(parser, name, callback, arrayMap);
+ list.add(val);
+ } else if (eventType == XmlPullParser.END_TAG) {
+ if (parser.getName().equals(endTag)) {
+ return list;
+ }
+ throw new XmlPullParserException(
+ "Expected " + endTag + " end tag at: " + parser.getName());
+ }
+ eventType = parser.next();
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ throw new XmlPullParserException(
+ "Document ended before " + endTag + " end tag");
+ }
+
+ /**
+ * Read a String[] object from an XmlPullParser. The XML data could previously have been
+ * generated by writeStringArrayXml(). The XmlPullParser must be positioned <em>after</em> the
+ * tag that begins the list.
+ *
+ * @param parser The XmlPullParser from which to read the list data.
+ * @param endTag Name of the tag that will end the list, usually "string-array".
+ * @param name An array of one string, used to return the name attribute of the list's tag.
+ * @return Returns a newly generated String[].
+ */
+ public static String[] readThisStringArrayXml(XmlPullParser parser, String endTag,
+ String[] name) throws XmlPullParserException, java.io.IOException {
+
+ parser.next();
+
+ List<String> array = new ArrayList<>();
+
+ int eventType = parser.getEventType();
+ do {
+ if (eventType == XmlPullParser.START_TAG) {
+ if (parser.getName().equals("item")) {
+ try {
+ array.add(parser.getAttributeValue(null, "value"));
+ } catch (NullPointerException e) {
+ throw new XmlPullParserException("Need value attribute in item");
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException("Not a number in value attribute in item");
+ }
+ } else {
+ throw new XmlPullParserException("Expected item tag at: " + parser.getName());
+ }
+ } else if (eventType == XmlPullParser.END_TAG) {
+ if (parser.getName().equals(endTag)) {
+ return array.toArray(new String[0]);
+ } else if (parser.getName().equals("item")) {
+
+ } else {
+ throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
+ parser.getName());
+ }
+ }
+ eventType = parser.next();
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ throw new XmlPullParserException("Document ended before " + endTag + " end tag");
+ }
+
+ private static Object readThisValueXml(XmlPullParser parser, String[] name,
+ ReadMapCallback callback, boolean arrayMap)
+ throws XmlPullParserException, java.io.IOException {
+ final String valueName = parser.getAttributeValue(null, "name");
+ final String tagName = parser.getName();
+
+ Object res;
+
+ if (tagName.equals("null")) {
+ res = null;
+ } else if (tagName.equals("string")) {
+ String value = "";
+ int eventType;
+ while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.END_TAG) {
+ if (parser.getName().equals("string")) {
+ name[0] = valueName;
+ return value;
+ }
+ throw new XmlPullParserException(
+ "Unexpected end tag in <string>: " + parser.getName());
+ } else if (eventType == XmlPullParser.TEXT) {
+ value += parser.getText();
+ } else if (eventType == XmlPullParser.START_TAG) {
+ throw new XmlPullParserException(
+ "Unexpected start tag in <string>: " + parser.getName());
+ }
+ }
+ throw new XmlPullParserException(
+ "Unexpected end of document in <string>");
+ } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) {
+ // all work already done by readThisPrimitiveValueXml
+ } else if (tagName.equals("string-array")) {
+ res = readThisStringArrayXml(parser, "string-array", name);
+ name[0] = valueName;
+ return res;
+ } else if (tagName.equals("list")) {
+ parser.next();
+ res = readThisListXml(parser, "list", name, callback, arrayMap);
+ name[0] = valueName;
+ return res;
+ } else if (callback != null) {
+ res = callback.readThisUnknownObjectXml(parser, tagName);
+ name[0] = valueName;
+ return res;
+ } else {
+ throw new XmlPullParserException("Unknown tag: " + tagName);
+ }
+
+ // Skip through to end tag.
+ int eventType;
+ while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.END_TAG) {
+ if (parser.getName().equals(tagName)) {
+ name[0] = valueName;
+ return res;
+ }
+ throw new XmlPullParserException(
+ "Unexpected end tag in <" + tagName + ">: " + parser.getName());
+ } else if (eventType == XmlPullParser.TEXT) {
+ throw new XmlPullParserException(
+ "Unexpected text in <" + tagName + ">: " + parser.getName());
+ } else if (eventType == XmlPullParser.START_TAG) {
+ throw new XmlPullParserException(
+ "Unexpected start tag in <" + tagName + ">: " + parser.getName());
+ }
+ }
+ throw new XmlPullParserException(
+ "Unexpected end of document in <" + tagName + ">");
+ }
+
+ private static final Object readThisPrimitiveValueXml(XmlPullParser parser, String tagName)
+ throws XmlPullParserException, java.io.IOException {
+ try {
+ if (tagName.equals("int")) {
+ return Integer.parseInt(parser.getAttributeValue(null, "value"));
+ } else if (tagName.equals("long")) {
+ return Long.valueOf(parser.getAttributeValue(null, "value"));
+ } else if (tagName.equals("float")) {
+ return Float.valueOf(parser.getAttributeValue(null, "value"));
+ } else if (tagName.equals("double")) {
+ return Double.valueOf(parser.getAttributeValue(null, "value"));
+ } else if (tagName.equals("boolean")) {
+ return Boolean.valueOf(parser.getAttributeValue(null, "value"));
+ } else {
+ return null;
+ }
+ } catch (NullPointerException e) {
+ throw new XmlPullParserException("Need value attribute in <" + tagName + ">");
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException(
+ "Not a number in value attribute in <" + tagName + ">");
+ }
+ }
+
+ public interface ReadMapCallback {
+
+ /**
+ * Called from readThisMapXml when a START_TAG is not recognized. The input stream is
+ * positioned within the start tag so that attributes can be read using in.getAttribute.
+ *
+ * @param in the XML input stream
+ * @param tag the START_TAG that was not recognized.
+ * @return the Object parsed from the stream which will be put into the map.
+ * @throws XmlPullParserException if the START_TAG is not recognized.
+ * @throws IOException on XmlPullParser serialization errors.
+ */
+ Object readThisUnknownObjectXml(XmlPullParser in, String tag)
+ throws XmlPullParserException, IOException;
+ }
+} \ No newline at end of file
diff --git a/proguard.flags b/proguard.flags
index 6eed9983d..a5e34b712 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -1,22 +1,73 @@
-# Xml files containing onClick (menus and layouts) require that proguard not
-# remove their handlers.
--keepclassmembers class * extends android.app.Activity {
- public void *(android.view.View);
- public void *(android.view.MenuItem);
+#
+# 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
+#
+
+-keepattributes SourceFile,LineNumberTable
+-dontpreverify
+-dontskipnonpubliclibraryclasses
+-dontusemixedcaseclassnames
+-keepclasseswithmembernames class * {
+ native <methods>;
}
--keep class com.android.contacts.common.** { *;}
+-keepclassmembers enum * {
+ public static **[] values();
+}
-# Any class or method annotated with NeededForTesting or NeededForReflection.
--keep @com.android.contacts.common.testing.NeededForTesting class *
--keepclassmembers class * {
-@com.android.contacts.common.testing.NeededForTesting *;
-@com.android.dialer.NeededForReflection *;
+-keepclassmembers class * implements android.os.Parcelable {
+ public static *** CREATOR;
+}
+
+-keepclassmembers class * implements java.io.Serializable {
+ static final long serialVersionUID;
+ private static final java.io.ObjectStreamField[] serialPersistentFields;
+ private void writeObject(java.io.ObjectOutputStream);
+ private void readObject(java.io.ObjectInputStream);
+ java.lang.Object writeReplace();
+ java.lang.Object readResolve();
+}
+
+-dontwarn com.google.common.**
+-dontwarn com.google.errorprone.annotations.**
+-dontwarn android.app.Notification
+-dontnote android.graphics.Insets
+-dontnote libcore.icu.ICU
+-keep class libcore.icu.ICU { *** get(...);}
+-dontnote android.support.v4.app.NotificationCompatJellybean
+
+-allowaccessmodification
+-renamesourcefileattribute PG
+
+-assumenosideeffects class android.util.Log {
+ static *** i(...);
+ static *** d(...);
+ static *** v(...);
+ static *** isLoggable(...);
}
-# For design libraries
--keep public class * extends android.support.design.widget.CoordinatorLayout$Behavior {
+-dontwarn org.apache.http.**
+
+# Used in com.android.dialer.answer.impl.SmsBottomSheetFragment
+-keep class android.support.design.widget.BottomSheetBehavior {
public <init>(android.content.Context, android.util.AttributeSet);
+ public <init>();
}
--verbose
+# Keep the annotation, classes, methods, and fields marked as UsedByReflection
+-keep class com.android.dialer.proguard.UsedByReflection
+-keep @com.android.dialer.proguard.UsedByReflection class *
+-keepclassmembers class * {
+ @com.android.dialer.proguard.UsedByReflection *;
+}
diff --git a/res/drawable-hdpi/fab_blue.png b/res/drawable-hdpi/fab_blue.png
deleted file mode 100644
index 8ff3d2918..000000000
--- a/res/drawable-hdpi/fab_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_videocam_24dp.png b/res/drawable-hdpi/ic_videocam_24dp.png
deleted file mode 100644
index ecfce9446..000000000
--- a/res/drawable-hdpi/ic_videocam_24dp.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/fab_blue.png b/res/drawable-mdpi/fab_blue.png
deleted file mode 100644
index 2ca6b4bdf..000000000
--- a/res/drawable-mdpi/fab_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_videocam_24dp.png b/res/drawable-mdpi/ic_videocam_24dp.png
deleted file mode 100644
index cbb5be2ea..000000000
--- a/res/drawable-mdpi/ic_videocam_24dp.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/fab_blue.png b/res/drawable-xhdpi/fab_blue.png
deleted file mode 100644
index 300b07eb4..000000000
--- a/res/drawable-xhdpi/fab_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_videocam_24dp.png b/res/drawable-xhdpi/ic_videocam_24dp.png
deleted file mode 100644
index 814e515bc..000000000
--- a/res/drawable-xhdpi/ic_videocam_24dp.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/fab_blue.png b/res/drawable-xxhdpi/fab_blue.png
deleted file mode 100644
index 76d68ac6a..000000000
--- a/res/drawable-xxhdpi/fab_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_videocam_24dp.png b/res/drawable-xxhdpi/ic_videocam_24dp.png
deleted file mode 100644
index c21679891..000000000
--- a/res/drawable-xxhdpi/ic_videocam_24dp.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/fab_blue.png b/res/drawable-xxxhdpi/fab_blue.png
deleted file mode 100644
index 1dd8a9260..000000000
--- a/res/drawable-xxxhdpi/fab_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/fab_ic_call.png b/res/drawable-xxxhdpi/fab_ic_call.png
deleted file mode 100644
index 7af3396b4..000000000
--- a/res/drawable-xxxhdpi/fab_ic_call.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/blocked_contact.xml b/res/drawable/blocked_contact.xml
deleted file mode 100644
index 0957585b4..000000000
--- a/res/drawable/blocked_contact.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ 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
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item>
- <shape android:shape="oval">
- <solid android:color="@color/blocked_contact_background" />
- <size android:width="24dp" android:height="24dp" />
- </shape>
- </item>
-
- <item android:drawable="@drawable/ic_report_24dp"
- android:width="18dp"
- android:height="18dp"
- android:gravity="center" />
-
-</layer-list>
diff --git a/res/drawable/ic_call_detail_block.xml b/res/drawable/ic_call_detail_block.xml
deleted file mode 100644
index 9ec8e03f5..000000000
--- a/res/drawable/ic_call_detail_block.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_block_24dp"
- android:tint="@color/call_detail_footer_icon_tint" />
diff --git a/res/drawable/ic_pause.xml b/res/drawable/ic_pause.xml
deleted file mode 100644
index 7015a6647..000000000
--- a/res/drawable/ic_pause.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item android:state_enabled="false">
- <bitmap
- android:src="@drawable/ic_pause_24dp"
- android:tint="@color/voicemail_icon_disabled_tint" />
- </item>
-
- <item>
- <bitmap
- android:src="@drawable/ic_pause_24dp"
- android:tint="@color/voicemail_playpause_icon_tint" />
- </item>
-
-</selector>
diff --git a/res/drawable/ic_play_arrow.xml b/res/drawable/ic_play_arrow.xml
deleted file mode 100644
index 1a9ee978f..000000000
--- a/res/drawable/ic_play_arrow.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- android:autoMirrored="true">
-
- <item android:state_enabled="false">
- <bitmap
- android:src="@drawable/ic_play_arrow_24dp"
- android:tint="@color/voicemail_icon_disabled_tint" />
- </item>
-
- <item>
- <bitmap
- android:src="@drawable/ic_play_arrow_24dp"
- android:tint="@color/voicemail_playpause_icon_tint" />
- </item>
-
-</selector>
diff --git a/res/drawable/seekbar_drawable.xml b/res/drawable/seekbar_drawable.xml
deleted file mode 100644
index 96bbee398..000000000
--- a/res/drawable/seekbar_drawable.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="true">
- <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@android:id/background">
- <shape android:shape="line">
- <stroke
- android:width="2dip"
- android:color="@color/voicemail_playback_seek_bar_yet_to_play"
- />
- </shape>
- </item>
- <!-- I am not defining a secondary progress colour - we don't use it. -->
- <item android:id="@android:id/progress">
- <clip>
- <shape android:shape="line">
- <stroke
- android:width="2dip"
- android:color="@color/voicemail_playback_seek_bar_already_played"
- />
- </shape>
- </clip>
- </item>
- </layer-list>
- </item>
- <item>
- <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@android:id/background">
- <shape android:shape="line">
- <stroke
- android:width="2dip"
- android:color="@color/voicemail_playback_seek_bar_yet_to_play"
- />
- </shape>
- </item>
- <!-- I am not defining a secondary progress colour - we don't use it. -->
- <item android:id="@android:id/progress">
- <clip>
- <shape android:shape="line">
- <stroke
- android:width="2dip"
- android:color="@color/voicemail_playback_seek_bar_yet_to_play"
- />
- </shape>
- </clip>
- </item>
- </layer-list>
- </item>
-</selector>
diff --git a/res/layout-land/dialpad_fragment.xml b/res/layout-land/dialpad_fragment.xml
deleted file mode 100644
index 70a38ae20..000000000
--- a/res/layout-land/dialpad_fragment.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<view class="com.android.dialer.dialpad.DialpadFragment$DialpadSlidingRelativeLayout"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <!-- spacer view -->
- <View
- android:id="@+id/spacer"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="4"
- android:background="#00000000" />
-
- <!-- Dialpad shadow -->
- <View
- android:layout_width="@dimen/shadow_length"
- android:layout_height="match_parent"
- android:background="@drawable/shadow_fade_left" />
-
- <RelativeLayout
- android:layout_height="match_parent"
- android:layout_width="0dp"
- android:layout_weight="6">
-
- <include layout="@layout/dialpad_view"
- android:layout_height="match_parent"
- android:layout_width="match_parent" />
-
- <!-- "Dialpad chooser" UI, shown only when the user brings up the
- Dialer while a call is already in progress.
- When this UI is visible, the other Dialer elements
- (the textfield/button and the dialpad) are hidden. -->
-
- <ListView android:id="@+id/dialpadChooser"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/background_dialer_light"
- android:visibility="gone" />
-
- <!-- Margin bottom and alignParentBottom don't work well together, so use a Space instead. -->
- <Space android:id="@+id/dialpad_floating_action_button_margin_bottom"
- android:layout_width="match_parent"
- android:layout_height="@dimen/floating_action_button_margin_bottom"
- android:layout_alignParentBottom="true" />
-
- <FrameLayout
- android:id="@+id/dialpad_floating_action_button_container"
- android:background="@drawable/fab_green"
- android:layout_width="@dimen/floating_action_button_width"
- android:layout_height="@dimen/floating_action_button_height"
- android:layout_above="@id/dialpad_floating_action_button_margin_bottom"
- android:layout_centerHorizontal="true">
-
- <ImageButton
- android:id="@+id/dialpad_floating_action_button"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/floating_action_button"
- android:contentDescription="@string/description_dial_button"
- android:src="@drawable/fab_ic_call"/>
-
- </FrameLayout>
-
- </RelativeLayout>
-
- </LinearLayout>
-</view>
diff --git a/res/layout/account_filter_header_for_phone_favorite.xml b/res/layout/account_filter_header_for_phone_favorite.xml
deleted file mode 100644
index b55210a10..000000000
--- a/res/layout/account_filter_header_for_phone_favorite.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- Layout showing the type of account filter for phone favorite screen
- (or, new phone "all" screen).
- This is very similar to account_filter_header.xml but different in its
- top padding. -->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/account_filter_header_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingTop="8dip"
- android:layout_marginStart="@dimen/contact_browser_list_header_left_margin"
- android:layout_marginEnd="@dimen/contact_browser_list_header_right_margin"
- android:background="?android:attr/selectableItemBackground"
- android:visibility="gone">
- <TextView
- android:id="@+id/account_filter_header"
- style="@style/ContactListSeparatorTextViewStyle"
- android:paddingStart="@dimen/contact_browser_list_item_text_indent" />
- <TextView
- android:id="@+id/contact_list_all_empty"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="?android:attr/textColorSecondary"
- android:paddingStart="8dip"
- android:paddingTop="@dimen/contact_phone_list_empty_description_padding"
- android:paddingBottom="@dimen/contact_phone_list_empty_description_padding"
- android:textSize="@dimen/contact_phone_list_empty_description_size"
- android:text="@string/listFoundAllContactsZero"
- android:visibility="gone"/>
-</LinearLayout>
diff --git a/res/layout/all_contacts_fragment.xml b/res/layout/all_contacts_fragment.xml
deleted file mode 100644
index 2ca013a19..000000000
--- a/res/layout/all_contacts_fragment.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/pinned_header_list_layout"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <!-- Shown only when an Account filter is set.
- - paddingTop should be here to show "shade" effect correctly. -->
- <!-- TODO: Remove the filter header. -->
- <include layout="@layout/account_filter_header" />
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1" >
- <view
- class="com.android.contacts.common.list.PinnedHeaderListView"
- style="@style/DialtactsTheme"
- android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginStart="?attr/contact_browser_list_padding_left"
- android:layout_marginEnd="?attr/contact_browser_list_padding_right"
- android:paddingTop="18dp"
- android:fastScrollEnabled="true"
- android:fadingEdge="none"
- android:nestedScrollingEnabled="true" />
-
- <com.android.dialer.widget.EmptyContentView
- android:id="@+id/empty_list_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:visibility="gone"/>
-
- </FrameLayout>
-</LinearLayout>
diff --git a/res/layout/block_report_spam_dialog.xml b/res/layout/block_report_spam_dialog.xml
deleted file mode 100644
index a731decfc..000000000
--- a/res/layout/block_report_spam_dialog.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="25dp">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/block_report_number_alert_details"
- android:layout_marginBottom="10dp"
- android:textColor="@color/call_log_primary_color"
- android:textSize="@dimen/blocked_number_settings_description_text_size"/>
-
- <CheckBox
- android:id="@+id/report_number_as_spam_action"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/checkbox_report_as_spam_action"
- android:textSize="@dimen/blocked_number_settings_description_text_size"/>
-</LinearLayout> \ No newline at end of file
diff --git a/res/layout/blocked_number_footer.xml b/res/layout/blocked_number_footer.xml
deleted file mode 100644
index 17425c840..000000000
--- a/res/layout/blocked_number_footer.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:focusable="false">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:padding="@dimen/blocked_number_container_padding"
- android:background="@android:color/white"
- android:focusable="true">
-
- <TextView android:id="@+id/blocked_number_footer_textview"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="@color/blocked_number_secondary_text_color"
- android:textSize="@dimen/blocked_number_settings_description_text_size"
- android:text="@string/block_number_footer_message_vvm"/>
- </LinearLayout>
-</LinearLayout>
diff --git a/res/layout/blocked_number_fragment.xml b/res/layout/blocked_number_fragment.xml
deleted file mode 100644
index 4bc20857e..000000000
--- a/res/layout/blocked_number_fragment.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/blocked_number_fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="@color/blocked_number_background">
-
- <ListView android:id="@id/android:list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:divider="@null"
- android:headerDividersEnabled="false" />
-
-</LinearLayout>
diff --git a/res/layout/blocked_number_header.xml b/res/layout/blocked_number_header.xml
deleted file mode 100644
index e4b795fd8..000000000
--- a/res/layout/blocked_number_header.xml
+++ /dev/null
@@ -1,217 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:card_view="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:focusable="false"
- android:orientation="vertical">
-
- <LinearLayout
- android:id="@+id/blocked_numbers_disabled_for_emergency"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="27dp"
- android:paddingBottom="29dp"
- android:paddingStart="@dimen/blocked_number_container_padding"
- android:paddingEnd="44dp"
- android:background="@color/blocked_number_disabled_emergency_background_color"
- android:focusable="true"
- android:orientation="vertical"
- android:visibility="gone">
-
- <TextView
- style="@style/BlockedNumbersDescriptionTextStyle"
- android:textStyle="bold"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/blocked_numbers_disabled_emergency_header_label"/>
-
- <TextView
- style="@style/BlockedNumbersDescriptionTextStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/blocked_numbers_disabled_emergency_desc"/>
-
- </LinearLayout>
-
- <android.support.v7.widget.CardView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- card_view:cardCornerRadius="0dp">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@android:color/white"
- android:focusable="true"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/blocked_number_text_view"
- style="@android:style/TextAppearance.Material.Subhead"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:paddingStart="@dimen/blocked_number_container_padding"
- android:gravity="center_vertical"
- android:text="@string/block_list"
- android:textColor="@color/blocked_number_header_color"/>
-
- <RelativeLayout
- android:id="@+id/import_settings"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone">
-
- <TextView
- android:id="@+id/import_description"
- style="@style/BlockedNumbersDescriptionTextStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="11dp"
- android:paddingBottom="27dp"
- android:paddingStart="@dimen/blocked_number_container_padding"
- android:paddingEnd="@dimen/blocked_number_container_padding"
- android:text="@string/blocked_call_settings_import_description"
- android:textColor="@color/secondary_text_color"
- android:textSize="@dimen/blocked_number_settings_description_text_size"/>
-
- <Button
- android:id="@+id/import_button"
- style="@style/DialerFlatButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/blocked_number_container_padding"
- android:layout_alignParentEnd="true"
- android:layout_below="@id/import_description"
- android:text="@string/blocked_call_settings_import_button"/>
-
- <Button
- android:id="@+id/view_numbers_button"
- style="@style/DialerFlatButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="8dp"
- android:layout_below="@id/import_description"
- android:layout_toStartOf="@id/import_button"
- android:text="@string/blocked_call_settings_view_numbers_button"/>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:layout_marginTop="8dp"
- android:layout_below="@id/import_button"
- android:background="@color/divider_line_color"/>
-
- </RelativeLayout>
-
- <LinearLayout
- android:id="@+id/migrate_promo"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="gone">
-
- <TextView
- android:id="@+id/migrate_promo_header"
- style="@android:style/TextAppearance.Material.Subhead"
- android:layout_width="match_parent"
- android:layout_height="48dp"
- android:paddingStart="@dimen/blocked_number_container_padding"
- android:paddingEnd="@dimen/blocked_number_container_padding"
- android:gravity="center_vertical"
- android:textStyle="bold"
- android:text="@string/migrate_blocked_numbers_dialog_title"
- android:textColor="@color/blocked_number_header_color"/>
-
- <TextView
- android:id="@+id/migrate_promo_description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/blocked_number_container_padding"
- android:layout_marginEnd="@dimen/blocked_number_container_padding"
- android:layout_marginBottom="@dimen/blocked_number_container_padding"
- android:text="@string/migrate_blocked_numbers_dialog_message"
- android:textColor="@color/secondary_text_color"/>
-
- <Button
- android:id="@+id/migrate_promo_allow_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/migrate_blocked_numbers_dialog_allow_button"
- android:layout_marginStart="@dimen/blocked_number_container_padding"
- android:layout_marginEnd="@dimen/blocked_number_container_padding"
- android:layout_gravity="end"
- style="@style/DialerPrimaryFlatButtonStyle"
- android:layout_marginBottom="@dimen/blocked_number_container_padding"/>
-
- <View
- style="@style/FullWidthDivider"/>
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/add_number_linear_layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="@dimen/blocked_number_add_top_margin"
- android:paddingBottom="@dimen/blocked_number_add_bottom_margin"
- android:paddingStart="@dimen/blocked_number_horizontal_margin"
- android:background="?android:attr/selectableItemBackground"
- android:baselineAligned="false"
- android:clickable="true"
- android:contentDescription="@string/addBlockedNumber"
- android:focusable="true"
- android:gravity="center_vertical"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/add_number_icon"
- android:layout_width="@dimen/contact_photo_size"
- android:layout_height="@dimen/contact_photo_size"
- android:importantForAccessibility="no"/>
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginStart="@dimen/blocked_number_horizontal_margin"
- android:gravity="center_vertical"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/add_number_textview"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:includeFontPadding="false"
- android:text="@string/addBlockedNumber"
- android:textColor="@color/blocked_number_primary_text_color"
- android:textSize="@dimen/blocked_number_primary_text_size"/>
- </LinearLayout>
-
- </LinearLayout>
-
- <View
- android:id="@+id/blocked_number_list_divider"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:layout_marginStart="72dp"
- android:background="@color/divider_line_color"/>
-
- </LinearLayout>
-
- </android.support.v7.widget.CardView>
-
-</LinearLayout>
diff --git a/res/layout/blocked_number_item.xml b/res/layout/blocked_number_item.xml
deleted file mode 100644
index a4997f257..000000000
--- a/res/layout/blocked_number_item.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/caller_information"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/blocked_number_horizontal_margin"
- android:baselineAligned="false"
- android:orientation="horizontal"
- android:focusable="true"
- android:gravity="center_vertical"
- android:background="@android:color/white">
-
- <QuickContactBadge
- android:id="@+id/quick_contact_photo"
- android:layout_width="@dimen/contact_photo_size"
- android:layout_height="@dimen/contact_photo_size"
- android:focusable="true"
- android:layout_marginTop="@dimen/blocked_number_top_margin"
- android:layout_marginBottom="@dimen/blocked_number_bottom_margin"/>
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:layout_weight="1"
- android:orientation="vertical"
- android:layout_marginStart="@dimen/blocked_number_horizontal_margin">
-
- <TextView
- android:id="@+id/caller_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="@color/blocked_number_primary_text_color"
- android:textSize="@dimen/blocked_number_primary_text_size"
- android:includeFontPadding="false"
- android:singleLine="true"/>
-
- <TextView
- android:id="@+id/caller_number"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="@color/blocked_number_secondary_text_color"
- android:textSize="@dimen/blocked_number_settings_description_text_size"
- android:singleLine="true" />
- </LinearLayout>
-
- <ImageView
- android:id="@+id/delete_button"
- android:layout_width="@dimen/blocked_number_delete_icon_size"
- android:layout_height="@dimen/blocked_number_delete_icon_size"
- android:layout_marginEnd="24dp"
- android:background="?android:attr/selectableItemBackgroundBorderless"
- android:src="@drawable/ic_remove"
- android:scaleType="center"
- android:tint="@color/blocked_number_icon_tint"
- android:contentDescription="@string/description_blocked_number_list_delete" />
-
-</LinearLayout>
diff --git a/res/layout/call_detail.xml b/res/layout/call_detail.xml
deleted file mode 100644
index 7d9124888..000000000
--- a/res/layout/call_detail.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/call_detail"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@color/background_dialer_call_log" >
-
- <!--
- The list view is under everything.
- It contains a first header element which is hidden under the controls UI.
- When scrolling, the controls move up until the name bar hits the top.
- -->
- <ListView
- android:id="@+id/history"
- android:layout_width="match_parent"
- android:layout_height="fill_parent" />
-
-</FrameLayout>
diff --git a/res/layout/call_detail_footer.xml b/res/layout/call_detail_footer.xml
deleted file mode 100644
index 6ee76e62e..000000000
--- a/res/layout/call_detail_footer.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <View
- android:layout_width="match_parent"
- android:layout_height="@dimen/divider_line_thickness"
- android:background="@color/call_log_action_divider" />
-
- <TextView android:id="@+id/call_detail_action_block"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/action_block_number"
- android:drawableStart="@drawable/ic_call_detail_block"
- style="@style/CallDetailActionItemStyle" />
-
- <TextView android:id="@+id/call_detail_action_copy"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/action_copy_number_text"
- android:drawableStart="@drawable/ic_call_detail_content_copy"
- style="@style/CallDetailActionItemStyle" />
-
- <TextView android:id="@+id/call_detail_action_edit_before_call"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/action_edit_number_before_call"
- android:drawableStart="@drawable/ic_call_detail_edit"
- android:visibility="gone"
- style="@style/CallDetailActionItemStyle" />
-
- <TextView android:id="@+id/call_detail_action_report"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/action_report_number"
- android:drawableStart="@drawable/ic_call_detail_report"
- android:visibility="gone"
- style="@style/CallDetailActionItemStyle" />
-
-</LinearLayout>
diff --git a/res/layout/call_detail_header.xml b/res/layout/call_detail_header.xml
deleted file mode 100644
index 6eceb80ee..000000000
--- a/res/layout/call_detail_header.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/caller_information"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/call_detail_horizontal_margin"
- android:paddingTop="@dimen/call_detail_top_margin"
- android:paddingBottom="@dimen/call_detail_bottom_margin"
- android:baselineAligned="false"
- android:orientation="horizontal"
- android:elevation="@dimen/call_detail_elevation"
- android:focusable="true"
- android:background="@color/background_dialer_white" >
-
- <QuickContactBadge
- android:id="@+id/quick_contact_photo"
- android:layout_width="@dimen/contact_photo_size"
- android:layout_height="@dimen/contact_photo_size"
- android:layout_alignParentStart="true"
- android:layout_gravity="top"
- android:layout_marginTop="3dp"
- android:focusable="true" />
-
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical"
- android:gravity="center_vertical"
- android:layout_marginStart="@dimen/call_detail_horizontal_margin">
-
- <TextView
- android:id="@+id/caller_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="?attr/call_log_primary_text_color"
- android:textSize="@dimen/call_log_primary_text_size"
- android:includeFontPadding="false"
- android:layout_marginTop="2dp"
- android:layout_marginBottom="3dp"
- android:singleLine="true" />
-
- <TextView
- android:id="@+id/caller_number"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="?attr/call_log_secondary_text_color"
- android:textSize="@dimen/call_log_detail_text_size"
- android:layout_marginBottom="1dp"
- android:singleLine="true" />
-
- <TextView
- android:id="@+id/phone_account_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="?attr/call_log_secondary_text_color"
- android:textSize="@dimen/call_log_detail_text_size"
- android:singleLine="true"
- android:visibility="gone" />
-
- </LinearLayout>
-
- <ImageView
- android:id="@+id/call_back_button"
- android:layout_width="@dimen/call_log_list_item_primary_action_dimen"
- android:layout_height="@dimen/call_log_list_item_primary_action_dimen"
- android:layout_marginEnd="16dp"
- android:background="?android:attr/selectableItemBackgroundBorderless"
- android:src="@drawable/ic_call_24dp"
- android:scaleType="center"
- android:tint="@color/call_log_list_item_primary_action_icon_tint"
- android:contentDescription="@string/description_call_log_call_action"
- android:visibility="gone" />
-
-</LinearLayout>
diff --git a/res/layout/call_detail_history_item.xml b/res/layout/call_detail_history_item.xml
deleted file mode 100644
index 10d9605a1..000000000
--- a/res/layout/call_detail_history_item.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="@dimen/call_log_inner_margin"
- android:paddingBottom="@dimen/call_log_inner_margin"
- android:paddingStart="@dimen/call_detail_horizontal_margin"
- android:paddingEnd="@dimen/call_log_outer_margin"
- android:orientation="vertical" >
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
- <view
- class="com.android.dialer.calllog.CallTypeIconsView"
- android:id="@+id/call_type_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
- <TextView
- android:id="@+id/call_type_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/call_log_icon_margin"
- android:textColor="?attr/call_log_primary_text_color"
- android:textSize="@dimen/call_log_primary_text_size" />
- </LinearLayout>
- <TextView
- android:id="@+id/date"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="?attr/call_log_secondary_text_color"
- android:textSize="@dimen/call_log_detail_text_size" />
- <TextView
- android:id="@+id/duration"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="?attr/call_log_secondary_text_color"
- android:textSize="@dimen/call_log_detail_text_size" />
-</LinearLayout>
diff --git a/res/layout/call_log_activity.xml b/res/layout/call_log_activity.xml
deleted file mode 100644
index aa1a6f44d..000000000
--- a/res/layout/call_log_activity.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/calllog_frame"
- android:orientation="vertical">
- <com.android.contacts.common.list.ViewPagerTabs
- android:id="@+id/viewpager_header"
- android:layout_width="match_parent"
- android:layout_height="@dimen/tab_height"
- android:textAllCaps="true"
- android:orientation="horizontal"
- android:layout_gravity="top"
- android:elevation="@dimen/tab_elevation"
- style="@style/DialtactsActionBarTabTextStyle" />
- <android.support.v4.view.ViewPager
- android:id="@+id/call_log_pager"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1" />
- <RelativeLayout
- android:id="@+id/floating_action_button_container"
- android:layout_width="0dp"
- android:layout_height="0dp" />
-</LinearLayout>
diff --git a/res/layout/call_log_fragment.xml b/res/layout/call_log_fragment.xml
deleted file mode 100644
index aad7d8e77..000000000
--- a/res/layout/call_log_fragment.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- Layout parameters are set programmatically. -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@color/background_dialer_call_log">
-
- <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@color/background_dialer_call_log"
- android:clipToPadding="false"
- android:paddingStart="@dimen/call_log_horizontal_margin"
- android:paddingEnd="@dimen/call_log_horizontal_margin"
- android:paddingBottom="@dimen/floating_action_button_list_bottom_padding" />
-
- <com.android.dialer.widget.EmptyContentView
- android:id="@+id/empty_list_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:visibility="gone" />
-
-</FrameLayout>
diff --git a/res/layout/call_log_list_item.xml b/res/layout/call_log_list_item.xml
deleted file mode 100644
index 660bca36e..000000000
--- a/res/layout/call_log_list_item.xml
+++ /dev/null
@@ -1,174 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/call_log_list_item"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <!-- Day group heading. Used to show a "today", "yesterday", "last week" or "other" heading
- above a group of call log entries. -->
- <TextView
- android:id="@+id/call_log_day_group_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="start"
- android:layout_marginStart="@dimen/call_log_start_margin"
- android:layout_marginEnd="@dimen/call_log_outer_margin"
- android:fontFamily="sans-serif-medium"
- android:textColor="@color/call_log_day_group_heading_color"
- android:textSize="@dimen/call_log_day_group_heading_size"
- android:paddingTop="@dimen/call_log_day_group_padding_top"
- android:paddingBottom="@dimen/call_log_day_group_padding_bottom" />
-
- <android.support.v7.widget.CardView
- android:id="@+id/call_log_row"
- style="@style/CallLogCardStyle">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <!-- Primary area containing the contact badge and caller information -->
- <LinearLayout
- android:id="@+id/primary_action_view"
- android:background="?android:attr/selectableItemBackground"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/call_log_start_margin"
- android:paddingEnd="@dimen/call_log_outer_margin"
- android:paddingTop="@dimen/call_log_vertical_padding"
- android:paddingBottom="@dimen/call_log_vertical_padding"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:focusable="true"
- android:nextFocusRight="@+id/call_back_action"
- android:nextFocusLeft="@+id/quick_contact_photo" >
-
- <QuickContactBadge
- android:id="@+id/quick_contact_photo"
- android:layout_width="@dimen/contact_photo_size"
- android:layout_height="@dimen/contact_photo_size"
- android:paddingTop="2dp"
- android:nextFocusRight="@id/primary_action_view"
- android:layout_gravity="top"
- android:focusable="true" />
-
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical"
- android:gravity="center_vertical"
- android:layout_marginStart="@dimen/call_log_list_item_info_margin_start">
-
- <TextView
- android:id="@+id/name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/call_log_name_margin_bottom"
- android:layout_marginEnd="@dimen/call_log_icon_margin"
- android:textColor="@color/call_log_primary_color"
- android:textSize="@dimen/call_log_primary_text_size"
- android:singleLine="true" />
-
- <LinearLayout
- android:id="@+id/call_type"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <view
- class="com.android.dialer.calllog.CallTypeIconsView"
- android:id="@+id/call_type_icons"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/call_log_icon_margin"
- android:layout_gravity="center_vertical" />
-
- <ImageView android:id="@+id/work_profile_icon"
- android:src="@drawable/ic_work_profile"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/call_log_icon_margin"
- android:scaleType="center"
- android:visibility="gone" />
-
- <TextView
- android:id="@+id/call_location_and_date"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/call_log_icon_margin"
- android:layout_gravity="center_vertical"
- android:textColor="@color/call_log_detail_color"
- android:textSize="@dimen/call_log_detail_text_size"
- android:singleLine="true" />
-
- </LinearLayout>
-
- <TextView
- android:id="@+id/call_account_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/call_log_call_account_margin_bottom"
- android:layout_marginEnd="@dimen/call_log_icon_margin"
- android:textColor="?attr/call_log_secondary_text_color"
- android:textSize="@dimen/call_log_detail_text_size"
- android:visibility="gone"
- android:singleLine="true" />
-
- <TextView
- android:id="@+id/voicemail_transcription"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/call_log_icon_margin"
- android:textColor="@color/call_log_voicemail_transcript_color"
- android:textSize="@dimen/call_log_voicemail_transcription_text_size"
- android:ellipsize="marquee"
- android:visibility="gone"
- android:singleLine="false"
- android:maxLines="10"/>
-
- </LinearLayout>
-
- <ImageView
- android:id="@+id/primary_action_button"
- android:layout_width="@dimen/call_log_list_item_primary_action_dimen"
- android:layout_height="@dimen/call_log_list_item_primary_action_dimen"
- android:layout_gravity="center_vertical"
- android:layout_marginEnd="@dimen/call_log_icon_margin"
- android:background="?android:attr/selectableItemBackgroundBorderless"
- android:scaleType="center"
- android:tint="@color/call_log_list_item_primary_action_icon_tint"
- android:visibility="gone" />
-
- </LinearLayout>
-
- <!-- Viewstub with additional expandable actions for a call log entry -->
- <ViewStub android:id="@+id/call_log_entry_actions_stub"
- android:inflatedId="@+id/call_log_entry_actions"
- android:layout="@layout/call_log_list_item_actions"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom" />
-
- </LinearLayout>
-
- </android.support.v7.widget.CardView>
-
-</LinearLayout>
diff --git a/res/layout/call_log_list_item_actions.xml b/res/layout/call_log_list_item_actions.xml
deleted file mode 100644
index 4aad6195a..000000000
--- a/res/layout/call_log_list_item_actions.xml
+++ /dev/null
@@ -1,202 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/call_log_action_container"
- android:gravity="center_vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="visible"
- android:importantForAccessibility="1">
-
- <com.android.dialer.voicemail.VoicemailPlaybackLayout
- android:id="@+id/voicemail_playback_layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@color/call_log_action_divider" />
-
- <LinearLayout
- android:id="@+id/call_action"
- android:paddingTop="@dimen/call_log_actions_top_padding"
- style="@style/CallLogActionStyle">
-
- <ImageView
- style="@style/CallLogActionIconStyle"
- android:src="@drawable/ic_call_24dp" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="center_vertical">
- <TextView
- android:id="@+id/call_action_text"
- style="@style/CallLogActionTextStyle"
- android:text="@string/description_call_log_call_action" />
-
- <TextView
- android:id="@+id/call_type_or_location_text"
- style="@style/CallLogActionSupportTextStyle"/>
- </LinearLayout>
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/video_call_action"
- style="@style/CallLogActionStyle">
-
- <ImageView
- style="@style/CallLogActionIconStyle"
- android:src="@drawable/ic_videocam_24dp" />
-
- <TextView
- style="@style/CallLogActionTextStyle"
- android:text="@string/call_log_action_video_call" />
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/create_new_contact_action"
- style="@style/CallLogActionStyle">
-
- <ImageView
- style="@style/CallLogActionIconStyle"
- android:src="@drawable/ic_person_add_24dp" />
-
- <TextView
- style="@style/CallLogActionTextStyle"
- android:text="@string/search_shortcut_create_new_contact" />
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/add_to_existing_contact_action"
- style="@style/CallLogActionStyle">
-
- <ImageView
- style="@style/CallLogActionIconStyle"
- android:src="@drawable/ic_person_24dp" />
-
- <TextView
- style="@style/CallLogActionTextStyle"
- android:text="@string/search_shortcut_add_to_contact" />
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/send_message_action"
- style="@style/CallLogActionStyle">
-
- <ImageView
- style="@style/CallLogActionIconStyle"
- android:src="@drawable/ic_message_24dp" />
-
- <TextView
- style="@style/CallLogActionTextStyle"
- android:text="@string/call_log_action_send_message" />
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/call_with_note_action"
- style="@style/CallLogActionStyle">
-
- <ImageView
- style="@style/CallLogActionIconStyle"
- android:src="@drawable/ic_call_note_white_24dp" />
-
- <TextView
- style="@style/CallLogActionTextStyle"
- android:text="@string/call_with_a_note" />
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/block_report_action"
- style="@style/CallLogActionStyle"
- android:visibility="gone">
-
- <ImageView
- style="@style/CallLogActionIconStyle"
- android:src="@drawable/ic_block_24dp"/>
-
- <TextView
- style="@style/CallLogActionTextStyle"
- android:text="@string/call_log_action_block_report_number" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/block_action"
- style="@style/CallLogActionStyle"
- android:visibility="gone">
-
- <ImageView
- style="@style/CallLogActionIconStyle"
- android:src="@drawable/ic_block_24dp"/>
-
- <TextView
- style="@style/CallLogActionTextStyle"
- android:text="@string/call_log_action_block_number" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/unblock_action"
- style="@style/CallLogActionStyle"
- android:visibility="gone">
-
- <ImageView
- style="@style/CallLogActionIconStyle"
- android:src="@drawable/ic_unblock"/>
-
- <TextView
- style="@style/CallLogActionTextStyle"
- android:text="@string/call_log_action_unblock_number" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/report_not_spam_action"
- style="@style/CallLogActionStyle"
- android:visibility="gone">
-
- <ImageView
- style="@style/CallLogActionIconStyle"
- android:src="@drawable/ic_unblock"/>
-
- <TextView
- style="@style/CallLogActionTextStyle"
- android:text="@string/call_log_action_remove_spam" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/details_action"
- style="@style/CallLogActionStyle">
-
- <ImageView
- style="@style/CallLogActionIconStyle"
- android:src="@drawable/ic_info_outline_24dp" />
-
- <TextView
- style="@style/CallLogActionTextStyle"
- android:text="@string/call_log_action_details" />
-
- </LinearLayout>
-
-</LinearLayout>
diff --git a/res/layout/dialpad_chooser_list_item.xml b/res/layout/dialpad_chooser_list_item.xml
deleted file mode 100644
index 9a4903698..000000000
--- a/res/layout/dialpad_chooser_list_item.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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.
--->
-
-<!-- Layout of a single item in the Dialer's "Dialpad chooser" UI. -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <ImageView android:id="@+id/icon"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:scaleType="center" />
-
- <TextView android:id="@+id/text"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="@color/dialpad_primary_text_color"
- android:layout_gravity="center_vertical"
- android:layout_width="0dip"
- android:layout_weight="1"
- android:layout_height="wrap_content" />
-
-</LinearLayout>
diff --git a/res/layout/dialpad_fragment.xml b/res/layout/dialpad_fragment.xml
deleted file mode 100644
index 21cb58654..000000000
--- a/res/layout/dialpad_fragment.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<view class="com.android.dialer.dialpad.DialpadFragment$DialpadSlidingRelativeLayout"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <!-- spacer view -->
- <View
- android:id="@+id/spacer"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:background="#00000000" />
- <!-- Dialpad shadow -->
- <View
- android:layout_width="match_parent"
- android:layout_height="@dimen/shadow_length"
- android:background="@drawable/shadow_fade_up" />
- <include layout="@layout/dialpad_view" />
- <!-- "Dialpad chooser" UI, shown only when the user brings up the
- Dialer while a call is already in progress.
- When this UI is visible, the other Dialer elements
- (the textfield/button and the dialpad) are hidden. -->
- <ListView android:id="@+id/dialpadChooser"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/background_dialer_light"
- android:visibility="gone" />
-
- </LinearLayout>
-
- <!-- Margin bottom and alignParentBottom don't work well together, so use a Space instead. -->
- <Space android:id="@+id/dialpad_floating_action_button_margin_bottom"
- android:layout_width="match_parent"
- android:layout_height="@dimen/floating_action_button_margin_bottom"
- android:layout_alignParentBottom="true" />
-
- <FrameLayout
- android:id="@+id/dialpad_floating_action_button_container"
- android:background="@drawable/fab_green"
- android:layout_width="@dimen/floating_action_button_width"
- android:layout_height="@dimen/floating_action_button_height"
- android:layout_above="@id/dialpad_floating_action_button_margin_bottom"
- android:layout_centerHorizontal="true">
-
- <ImageButton
- android:id="@+id/dialpad_floating_action_button"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/floating_action_button"
- android:contentDescription="@string/description_dial_button"
- android:src="@drawable/fab_ic_call"/>
-
- </FrameLayout>
-
-</view>
diff --git a/res/layout/dialtacts_activity.xml b/res/layout/dialtacts_activity.xml
deleted file mode 100644
index 782d4f310..000000000
--- a/res/layout/dialtacts_activity.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-<android.support.design.widget.CoordinatorLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/dialtacts_mainlayout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:clipChildren="false"
- android:background="@color/background_dialer_light">
-
- <FrameLayout
- android:id="@+id/dialtacts_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="false">
- <!-- The main contacts grid -->
- <FrameLayout
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:id="@+id/dialtacts_frame"
- android:clipChildren="false" />
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/floating_action_button_container"
- android:background="@drawable/fab_blue"
- android:layout_width="@dimen/floating_action_button_width"
- android:layout_height="@dimen/floating_action_button_height"
- android:layout_marginBottom="@dimen/floating_action_button_margin_bottom"
- android:layout_gravity="center_horizontal|bottom"
- app:layout_behavior="com.android.dialer.FloatingActionButtonBehavior">
-
- <ImageButton
- android:id="@+id/floating_action_button"
- android:background="@drawable/floating_action_button"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:contentDescription="@string/action_menu_dialpad_button"
- android:src="@drawable/fab_ic_dial"/>
-
- </FrameLayout>
-
- <!-- Host container for the contact tile drag shadow -->
- <FrameLayout
- android:id="@+id/activity_overlay"
- android:layout_height="match_parent"
- android:layout_width="match_parent">
- <ImageView
- android:id="@+id/contact_tile_drag_shadow_overlay"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:importantForAccessibility="no" />
- </FrameLayout>
-
-</android.support.design.widget.CoordinatorLayout>
diff --git a/res/layout/empty_content_view.xml b/res/layout/empty_content_view.xml
deleted file mode 100644
index 97ac4c79c..000000000
--- a/res/layout/empty_content_view.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <ImageView
- android:id="@+id/emptyListViewImage"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:gravity="center_horizontal" />
-
- <TextView
- android:id="@+id/emptyListViewMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal|top"
- android:textSize="@dimen/empty_list_message_text_size"
- android:textColor="@color/empty_list_text_color"
- android:paddingRight="16dp"
- android:paddingLeft="16dp"
- android:paddingTop="8dp"
- android:paddingBottom="8dp" />
-
- <TextView
- android:id="@+id/emptyListViewAction"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:layout_gravity="center_horizontal"
- android:paddingRight="16dp"
- android:paddingLeft="16dp"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:background="?android:attr/selectableItemBackground"
- android:clickable="true"
- style="@style/TextActionStyle" />
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="40dp" />
-
-</merge>
diff --git a/res/layout/keyguard_preview.xml b/res/layout/keyguard_preview.xml
deleted file mode 100644
index 16243eb6a..000000000
--- a/res/layout/keyguard_preview.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_height="match_parent"
- android:layout_width="match_parent">
- <View
- android:layout_width="match_parent"
- android:layout_height="25dp"
- android:background="@color/dialer_theme_color_dark" />
- <View
- android:layout_width="match_parent"
- android:layout_weight="1"
- android:layout_height="0dp"
- android:background="#ffffff" />
-</LinearLayout>
diff --git a/res/layout/lists_fragment.xml b/res/layout/lists_fragment.xml
deleted file mode 100644
index a073151e2..000000000
--- a/res/layout/lists_fragment.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/lists_frame"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:animateLayoutChanges="true" >
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <!-- TODO: Apply background color to ActionBar instead of a FrameLayout. For now, this is
- the easiest way to preserve correct pane scrolling and searchbar collapse/expand
- behaviors. -->
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="@dimen/action_bar_height_large"
- android:background="@color/actionbar_background_color"
- android:elevation="@dimen/tab_elevation" />
-
- <com.android.contacts.common.list.ViewPagerTabs
- android:id="@+id/lists_pager_header"
- android:layout_width="match_parent"
- android:layout_height="@dimen/tab_height"
- android:textAllCaps="true"
- android:orientation="horizontal"
- android:layout_gravity="top"
- android:elevation="@dimen/tab_elevation"
- style="@style/DialtactsActionBarTabTextStyle" />
-
- <android.support.v4.view.ViewPager
- android:id="@+id/lists_pager"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1" />
-
- </LinearLayout>
-
- <!-- Sets android:importantForAccessibility="no" to avoid being announced when navigating with
- talkback enabled. It will still be announced when user drag or drop contact onto it.
- This is required since drag and drop event is only sent to views are visible when drag
- starts. -->
- <com.android.dialer.list.RemoveView
- android:id="@+id/remove_view"
- android:layout_width="match_parent"
- android:layout_height="@dimen/tab_height"
- android:layout_marginTop="@dimen/action_bar_height_large"
- android:contentDescription="@string/remove_contact"
- android:importantForAccessibility="no" >
-
- <LinearLayout
- android:id="@+id/remove_view_content"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:background="@color/actionbar_background_color"
- android:gravity="center"
- android:orientation="horizontal"
- android:visibility="gone">
-
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_marginBottom="8dp"
- android:id="@+id/remove_view_icon"
- android:src="@drawable/ic_remove"
- android:tint="@color/remove_text_color" />
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/remove_view_text"
- android:textSize="@dimen/remove_text_size"
- android:textColor="@color/remove_text_color"
- android:text="@string/remove_contact" />
-
- </LinearLayout>
-
- </com.android.dialer.list.RemoveView >
-
-</FrameLayout>
diff --git a/res/layout/phone_disambig_item.xml b/res/layout/phone_disambig_item.xml
deleted file mode 100755
index 27bbda134..000000000
--- a/res/layout/phone_disambig_item.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<view class="com.android.contacts.common.widget.ActivityTouchLinearLayout"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingStart="30dip"
- android:paddingEnd="30dip"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:gravity="center_vertical">
-
- <TextView
- android:id="@android:id/text1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textStyle="bold"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <!-- Phone number should be displayed ltr -->
- <TextView
- android:id="@android:id/text2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="-4dip"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textDirection="ltr" />
-
-</view>
diff --git a/res/layout/phone_favorite_tile_view.xml b/res/layout/phone_favorite_tile_view.xml
deleted file mode 100644
index aa82ca0dc..000000000
--- a/res/layout/phone_favorite_tile_view.xml
+++ /dev/null
@@ -1,128 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<view
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/contact_tile"
- class="com.android.dialer.list.PhoneFavoriteSquareTileView"
- android:paddingEnd="@dimen/contact_tile_divider_width"
- android:paddingBottom="@dimen/contact_tile_divider_width">
-
- <RelativeLayout
- android:id="@+id/contact_favorite_card"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:focusable="true"
- android:nextFocusRight="@+id/contact_tile_secondary_button">
-
- <com.android.contacts.common.widget.LayoutSuppressingImageView
- android:id="@+id/contact_tile_image"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <View
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="6" />
- <View
- android:id="@+id/shadow_overlay"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="4"
- android:background="@drawable/shadow_contact_photo" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/contact_tile_text_side_padding"
- android:paddingEnd="@dimen/contact_tile_text_side_padding"
- android:paddingBottom="@dimen/contact_tile_text_bottom_padding"
- android:layout_alignParentBottom="true"
- android:orientation="vertical" >
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="center_vertical">
- <TextView
- android:id="@+id/contact_tile_name"
- android:layout_weight="1"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:textColor="@color/contact_tile_name_color"
- android:fontFamily="sans-serif-medium"
- android:singleLine="true"
- android:textSize="15sp"
- android:fadingEdge="horizontal"
- android:fadingEdgeLength="3dip"
- android:ellipsize="marquee"
- android:textAlignment="viewStart" />
- <ImageView
- android:id="@+id/contact_star_icon"
- android:layout_width="@dimen/favorites_star_icon_size"
- android:layout_height="@dimen/favorites_star_icon_size"
- android:layout_marginStart="3dp"
- android:src="@drawable/ic_star"
- android:visibility="gone" />
- </LinearLayout>
- <TextView
- android:id="@+id/contact_tile_phone_type"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:textColor="@color/contact_tile_name_color"
- android:fontFamily="sans-serif"
- android:singleLine="true"
- android:textSize="11sp"
- android:fadingEdge="horizontal"
- android:fadingEdgeLength="3dip"
- android:ellipsize="marquee"
- android:textAlignment="viewStart" />
- </LinearLayout>
-
- <View
- android:id="@+id/contact_tile_push_state"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:importantForAccessibility="no"
- android:background="@drawable/item_background_material_dark" />
-
- <ImageButton
- android:id="@id/contact_tile_secondary_button"
- android:src="@drawable/ic_more_vert_24dp"
- android:background="@drawable/item_background_material_dark"
- android:layout_height="@dimen/contact_tile_info_button_height_and_width"
- android:layout_width="@dimen/contact_tile_info_button_height_and_width"
- android:paddingLeft="4dp"
- android:paddingRight="9dp"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
- android:paddingTop="8dp"
- android:paddingBottom="4dp"
- android:layout_alignParentTop="true"
- android:layout_alignParentRight="true"
- android:layout_alignParentEnd="true"
- android:scaleType="center"
- android:contentDescription="@string/description_view_contact_detail" />
-
- </RelativeLayout>
-</view>
diff --git a/res/layout/search_edittext.xml b/res/layout/search_edittext.xml
deleted file mode 100644
index 8eda6960c..000000000
--- a/res/layout/search_edittext.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<view class="com.android.dialer.widget.SearchEditTextLayout"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/search_view_container"
- android:orientation="horizontal"
- android:layout_marginTop="@dimen/search_top_margin"
- android:layout_marginBottom="@dimen/search_bottom_margin"
- android:layout_marginLeft="@dimen/search_margin_horizontal"
- android:layout_marginRight="@dimen/search_margin_horizontal"
- android:background="@drawable/rounded_corner"
- android:elevation="@dimen/search_box_elevation">
-
- <LinearLayout
- android:id="@+id/search_box_collapsed"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingStart="@dimen/search_box_left_padding"
- android:gravity="center_vertical"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/search_magnifying_glass"
- android:layout_height="@dimen/search_box_icon_size"
- android:layout_width="@dimen/search_box_icon_size"
- android:padding="@dimen/search_box_search_icon_padding"
- android:scaleType="center"
- android:src="@drawable/ic_ab_search"
- android:importantForAccessibility="no"
- android:tint="@color/searchbox_icon_tint" />
-
- <TextView
- android:id="@+id/search_box_start_search"
- android:layout_height="wrap_content"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_marginLeft="@dimen/search_box_collapsed_text_margin_left"
- android:textSize="@dimen/search_collapsed_text_size"
- android:fontFamily="@string/search_font_family"
- android:textColorHint="@color/searchbox_hint_text_color"
- android:gravity="center_vertical"
- android:hint="@string/dialer_hint_find_contact" />
-
- <ImageView
- android:id="@+id/voice_search_button"
- android:layout_width="@dimen/search_box_icon_size"
- android:layout_height="match_parent"
- android:src="@drawable/ic_mic_grey600"
- android:scaleType="center"
- android:clickable="true"
- android:contentDescription="@string/description_start_voice_search"
- android:background="?android:attr/selectableItemBackgroundBorderless"
- android:tint="@color/searchbox_icon_tint" />
-
- <ImageButton
- android:id="@+id/dialtacts_options_menu_button"
- android:layout_width="@dimen/search_box_icon_size"
- android:layout_height="match_parent"
- android:paddingEnd="@dimen/search_box_right_padding"
- android:scaleType="center"
- android:background="?android:attr/selectableItemBackgroundBorderless"
- android:src="@drawable/ic_overflow_menu"
- android:contentDescription="@string/action_menu_overflow_description"
- android:tint="@color/searchbox_icon_tint" />
-
- </LinearLayout>
-
- <include layout="@layout/search_bar_expanded" />
-
-</view>
diff --git a/res/layout/set_primary_checkbox.xml b/res/layout/set_primary_checkbox.xml
deleted file mode 100644
index b997327ed..000000000
--- a/res/layout/set_primary_checkbox.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="14dip"
- android:paddingEnd="15dip"
- android:orientation="vertical">
-
- <CheckBox
- android:id="@+id/setPrimary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:focusable="true"
- android:clickable="true"
- android:text="@string/make_primary"/>
-</LinearLayout>
diff --git a/res/layout/speed_dial_fragment.xml b/res/layout/speed_dial_fragment.xml
deleted file mode 100644
index 5b6ce4fb8..000000000
--- a/res/layout/speed_dial_fragment.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="false">
-
- <FrameLayout
- android:id="@+id/contact_tile_frame"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true"
- android:paddingStart="@dimen/favorites_row_start_padding"
- android:paddingEnd="@dimen/favorites_row_end_padding" >
- <com.android.dialer.list.PhoneFavoriteListView
- android:id="@+id/contact_tile_list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingTop="@dimen/favorites_row_top_padding"
- android:numColumns="@integer/contact_tile_column_count_in_favorites"
- android:clipToPadding="false"
- android:fadingEdge="none"
- android:divider="@null"
- android:paddingBottom="@dimen/floating_action_button_list_bottom_padding"
- android:nestedScrollingEnabled="true" />
- </FrameLayout>
-
- <com.android.dialer.widget.EmptyContentView
- android:id="@+id/empty_list_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:visibility="gone"/>
-
-</FrameLayout>
diff --git a/res/layout/view_numbers_to_import_fragment.xml b/res/layout/view_numbers_to_import_fragment.xml
deleted file mode 100644
index 1836f8dbd..000000000
--- a/res/layout/view_numbers_to_import_fragment.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:card_view="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="@color/blocked_number_background">
-
- <ListView android:id="@id/android:list"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:divider="@null"
- android:headerDividersEnabled="false" />
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:background="@android:color/white">
-
- <Button android:id="@+id/import_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:layout_marginEnd="@dimen/blocked_number_container_padding"
- android:text="@string/blocked_call_settings_import_button"
- style="@style/DialerFlatButtonStyle" />
-
- <Button android:id="@+id/cancel_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/import_description"
- android:layout_toLeftOf="@id/import_button"
- android:text="@android:string/cancel"
- style="@style/DialerFlatButtonStyle" />
-
- </RelativeLayout>
-
-</LinearLayout>
diff --git a/res/layout/voicemail_playback_layout.xml b/res/layout/voicemail_playback_layout.xml
deleted file mode 100644
index 178e888bb..000000000
--- a/res/layout/voicemail_playback_layout.xml
+++ /dev/null
@@ -1,138 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="64dp"
- android:layout_marginEnd="24dp"
- android:orientation="vertical"
- android:background="@color/background_dialer_call_log_list_item">
-
- <TextView
- android:id="@+id/playback_state_text"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:gravity="center"
- android:textSize="14sp" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:paddingTop="@dimen/voicemail_playback_top_padding">
-
- <TextView
- android:id="@+id/playback_position_text"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="14sp"
- android:importantForAccessibility="no" />
-
- <SeekBar
- android:id="@+id/playback_seek"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:progressDrawable="@drawable/seekbar_drawable"
- android:thumb="@drawable/ic_voicemail_seek_handle"
- android:progress="0"
- android:max="0"
- android:contentDescription="@string/description_playback_seek" />
-
- <TextView
- android:id="@+id/total_duration_text"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textSize="14sp"
- android:importantForAccessibility="no" />
-
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="center">
-
- <Space
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"/>
-
- <ImageButton android:id="@+id/playback_speakerphone"
- style="@style/VoicemailPlaybackLayoutButtonStyle"
- android:src="@drawable/ic_volume_down_24dp"
- android:tint="@color/voicemail_icon_tint"
- android:contentDescription="@string/description_playback_speakerphone" />
-
- <Space
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"/>
-
- <ImageButton android:id="@+id/playback_start_stop"
- style="@style/VoicemailPlaybackLayoutButtonStyle"
- android:src="@drawable/ic_play_arrow"
- android:contentDescription="@string/voicemail_play_start_pause" />
-
- <Space
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"/>
-
- <ImageButton android:id="@+id/delete_voicemail"
- style="@style/VoicemailPlaybackLayoutButtonStyle"
- android:src="@drawable/ic_delete_24dp"
- android:tint="@color/voicemail_icon_tint"
- android:contentDescription="@string/call_log_trash_voicemail" />
-
- <Space android:id="@+id/space_before_share_voicemail"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:visibility="gone" />
-
- <ImageButton android:id="@+id/share_voicemail"
- style="@style/VoicemailPlaybackLayoutButtonStyle"
- android:src="@drawable/ic_share_white_24dp"
- android:tint="@color/voicemail_icon_tint"
- android:contentDescription="@string/call_log_share_voicemail"
- android:visibility="gone" />
-
- <Space android:id="@+id/space_before_archive_voicemail"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:visibility="gone" />
-
- <ImageButton android:id="@+id/archive_voicemail"
- style="@style/VoicemailPlaybackLayoutButtonStyle"
- android:src="@drawable/ic_archive_white_24dp"
- android:tint="@color/voicemail_icon_tint"
- android:contentDescription="@string/call_log_archive_voicemail"
- android:visibility="gone" />
-
- <Space
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"/>
-
- </LinearLayout>
-
-</LinearLayout>
diff --git a/res/layout/voicemail_promo_card.xml b/res/layout/voicemail_promo_card.xml
deleted file mode 100644
index ba4ac59a7..000000000
--- a/res/layout/voicemail_promo_card.xml
+++ /dev/null
@@ -1,99 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2015 Google Inc. All Rights Reserved. -->
-
-<android.support.v7.widget.CardView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:card_view="http://schemas.android.com/apk/res-auto"
- android:id="@+id/promo_card"
- style="@style/CallLogCardStyle"
- android:orientation="vertical"
- android:gravity="center_vertical"
- card_view:cardBackgroundColor="@color/visual_voicemail_promo_card_background">
-
- <LinearLayout
- android:id="@+id/promo_card_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/promo_card_start_padding"
- android:paddingEnd="@dimen/promo_card_main_padding"
- android:paddingTop="@dimen/promo_card_top_padding"
- android:paddingBottom="@dimen/promo_card_main_padding"
- android:orientation="horizontal"
- android:gravity="top">
-
- <ImageView
- android:id="@+id/promo_card_icon"
- android:layout_width="@dimen/promo_card_icon_size"
- android:layout_height="@dimen/promo_card_icon_size"
- android:layout_gravity="top"
- android:src="@drawable/ic_voicemail_24dp"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/promo_card_main_padding"
- android:orientation="vertical"
- android:gravity="center_vertical">
-
- <TextView
- android:id="@+id/promo_card_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/promo_card_title_padding"
- android:layout_gravity="center_vertical"
- android:textColor="@color/background_dialer_white"
- android:textSize="@dimen/call_log_primary_text_size"
- android:textStyle="bold"
- android:text="@string/visual_voicemail_title"
- android:singleLine="false"/>
-
- <TextView
- android:id="@+id/promo_card_details"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="@color/background_dialer_white"
- android:textSize="@dimen/call_log_detail_text_size"
- android:text="@string/visual_voicemail_text"
- android:lineSpacingExtra="@dimen/promo_card_line_spacing"
- android:singleLine="false"/>
- </LinearLayout>
- </LinearLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@color/visual_voicemail_promo_card_divider"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingEnd="@dimen/promo_card_action_end_padding"
- android:paddingTop="@dimen/promo_card_action_vertical_padding"
- android:paddingBottom="@dimen/promo_card_action_vertical_padding"
- android:orientation="horizontal"
- android:gravity="end">
-
- <TextView
- android:id="@+id/secondary_action"
- style="@style/PromoCardActionStyle"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/visual_voicemail_settings"
- android:nextFocusLeft="@+id/promo_card"
- android:nextFocusRight="@+id/primary_action"
- android:paddingEnd="@dimen/promo_card_action_between_padding"/>
-
- <TextView
- android:id="@+id/primary_action"
- style="@style/PromoCardActionStyle"
- android:background="?android:attr/selectableItemBackground"
- android:text="@android:string/ok"
- android:nextFocusLeft="@+id/secondary_action"
- android:nextFocusRight="@+id/promo_card"/>
- </LinearLayout>
- </LinearLayout>
-</android.support.v7.widget.CardView>
diff --git a/res/menu/call_log_options.xml b/res/menu/call_log_options.xml
deleted file mode 100644
index da38d864d..000000000
--- a/res/menu/call_log_options.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:id="@+id/delete_all"
- android:title="@string/call_log_delete_all"
- android:showAsAction="never"
- android:orderInCategory="1"/>
-</menu>
diff --git a/res/menu/dialpad_options.xml b/res/menu/dialpad_options.xml
deleted file mode 100644
index 63fca07da..000000000
--- a/res/menu/dialpad_options.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item
- android:id="@+id/menu_2s_pause"
- android:title="@string/add_2sec_pause"
- android:showAsAction="withText" />
- <item
- android:id="@+id/menu_add_wait"
- android:title="@string/add_wait"
- android:showAsAction="withText" />
- <item
- android:id="@+id/menu_call_with_note"
- android:title="@string/call_with_a_note"
- android:showAsAction="withText" />
-</menu>
diff --git a/res/menu/dialtacts_options.xml b/res/menu/dialtacts_options.xml
deleted file mode 100644
index 0f068f505..000000000
--- a/res/menu/dialtacts_options.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item
- android:id="@+id/menu_history"
- android:icon="@drawable/ic_menu_history_lt"
- android:title="@string/action_menu_call_history_description" />
- <item
- android:id="@+id/menu_archive"
- android:title="@string/voicemail_archive_activity_title"
- android:visible="false" />
- <item
- android:id="@+id/menu_import_export"
- android:title="@string/menu_import_export" />
- <item
- android:id="@+id/menu_clear_frequents"
- android:title="@string/menu_clear_frequents" />
- <item
- android:id="@+id/menu_add_contact"
- android:title="@string/menu_newContact"/>
- <item
- android:id="@+id/menu_call_settings"
- android:title="@string/dialer_settings_label" />
-
-</menu>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
deleted file mode 100644
index 42ebf279c..000000000
--- a/res/values-af/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Foon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Foon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Foonbelblad"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Foon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Oproepgeskiedenis"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Gee foutiewe nommer aan"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopieer nommer"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopieer transkripsie"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blokkeer nommer"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> is geblokkeer"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Deblokkeer nommer"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> is gedeblokkeer"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ONTDOEN"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Vee uit"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Wysig nommer voor oproep"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Vee oproepgeskiedenis uit"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Vee stemboodskap uit"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Argiveer stemboodskap"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Deel stemboodskap"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Stemboodskap is uitgevee"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Stemboodskap is geargiveer"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ONTDOEN"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"GAAN NA ARGIEF"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Vee oproepgeskiedenis uit?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Dit sal alle oproepe uit jou geskiedenis vee"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Vee tans oproepgeskiedenis uit …"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Foon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Gemiste oproep"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Gemiste werkoproep"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Gemiste oproepe"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> gemiste oproepe"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Bel terug"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Boodskap"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Stemboodskappe </item>
- <item quantity="one">Stemboodskap</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Speel"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nuwe stemboodskap van <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Kon nie stemboodskap speel nie"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Laai tans stemboodskap …"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Argiveer tans stemboodskap …"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Kon nie stemboodskap laai nie"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Slegs oproepe met stemboodskappe"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Slegs inkomende oproepe"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Slegs uitgaande oproepe"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Slegs gemisde oproepe"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Visuele stemboodskapdiens"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Kyk en luister na jou stemboodskapdiens sonder dat jy \'n nommer hoef te bel. Dataheffings kan van toepassing wees."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Instellings"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Stemboodskapopdaterings is nie beskikbaar nie"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Nuwe stemboodskap wag. Kan nie nou laai nie."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Stel jou stemboodskapdiens op"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Oudio is nie beskikbaar nie"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Stel op"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Bel stemboodskap"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>)<xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Kies nommer"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Kies nommer"</string>
- <string name="make_primary" msgid="5829291915305113983">"Onthou hierdie keuse"</string>
- <string name="description_search_button" msgid="3660807558587384889">"soek"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"bel"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"nommer om te skakel"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Speel of stop terugspeel"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Skakel luidsprekerfoon aan of af"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Soek terugspeelposisie"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Verlaag terugspeelkoers"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Verhoog terugspeelkoers"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Oproepgeskiedenis"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Meer opsies"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"belblad"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Wys slegs uitgaande"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Wys slegs inkomende"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Wys slegs misgeloop"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Wys net stemboodskappe"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Wys alle oproepe"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Voeg 2-sek.-pouse by"</string>
- <string name="add_wait" msgid="3360818652790319634">"Voeg wagtyd by"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Instellings"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Nuwe kontak"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Alle kontakte"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Oproepdetails"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Besonderhede is nie beskikbaar nie"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Gebruik raak-nommerbord"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Keer terug na oproep wat besig is"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Voeg oproep by"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Inkomende oproep"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Uitgaande oproep"</string>
- <string name="type_missed" msgid="2720502601640509542">"Gemiste oproep"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Inkomende video-oproep"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Uitgaande video-oproep"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Gemiste video-oproep"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Stemboodskap"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Afgekeurde oproep"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Geblokkeerde oproep"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Inkomende oproepe"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Speel stemboodskap"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Bekyk kontak <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Bel <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Kontakbesonderhede van <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> oproepe."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Video-oproep."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Stuur SMS aan <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Ongeluisterde stemboodskap"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Begin stemsoektog"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Bel <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Onbekend"</string>
- <string name="voicemail" msgid="3851469869202611441">"Stemboodskap"</string>
- <string name="private_num" msgid="6374339738119166953">"Private nommer"</string>
- <string name="payphone" msgid="7726415831153618726">"Betaalfoon"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> sek."</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min. <xliff:g id="SECONDS">%s</xliff:g> sek."</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> om <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Kan nie hierdie nommer bel nie"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Om stemboodskapdiens op te stel, gaan na Kieslys &gt; Instellings."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Om stemboodskapdiens te bel, skakel eers vliegtuigmodus af."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Laai tans…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Laai tans van SIM-kaart af…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM-kaartkontakte"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Geen kontakte-program beskikbaar nie"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Stemsoektog is nie beskikbaar nie"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Kan nie \'n foonoproep maak nie want die Foon-program is gedeaktiveer."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Geen program daarvoor op hierdie toestel nie"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Deursoek kontakte"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Gee nommer of soek in kontakte"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Jou oproepgeskiedenis is leeg"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Maak \'n oproep"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Jy het geen gemiste oproepe nie."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Jou stemboodskapdiens se inkassie is leeg."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Jou stemboodskap-argief is leeg."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Wys net gunstelinge"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Oproepgeskiedenis"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Stemboodskap-argief"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Alles"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Gemis"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Stemboodskap"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Nuwe, vereenvoudigde blokkering"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Om jou beter te beskerm, moet Phone die manier verander waarop blokkering werk. Jou geblokkeerde nommers sal nou sowel oproepe as SMS\'e keer en kan met ander programme gedeel word."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Laat toe"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Blokkeer <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Oproepe van hierdie nommer af sal geblokkeer word en stemboodskappe sal outomaties uitgevee word."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Oproepe van hierdie nommer af sal geblokkeer word, maar die beller sal dalk steeds vir jou stemboodskappe kan los."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Jy sal nie meer oproepe of SMS\'e van hierdie nommer ontvang nie."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOKKEER"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Deblokkeer <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DEBLOKKEER"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Spoedbel"</string>
- <string name="tab_history" msgid="2563144697322434940">"Oproepgeskiedenis"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontakte"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Stemboodskap"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Verwyder uit gunstelinge"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Ontdoen"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Bel <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Skep nuwe kontak"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Voeg by \'n kontak"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Stuur SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Maak video-oproep"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blokkeer nommer"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> nuwe gemiste oproepe"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Daar is nog niemand op jou spoedbel nie"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Voeg \'n gunsteling by"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Jy het nog nie enige kontakte nie"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Voeg \'n kontak by"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Raak prent om al die nommers te sien of raak en hou om te herorganiseer"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Verwyder"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Video-oproep"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Stuur \'n boodskap"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Oproepbesonderhede"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Bel <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Oproep gemis vanaf <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Oproep geantwoord vanaf <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Ongeleeste stempos vanaf <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Stempos vanaf <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Oproep na <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"op <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"via <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"via <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"op <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, via <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> via <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Bel"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Bel <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Maak video-oproep na <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Luister na stemboodskap vanaf <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Speel stemboodskap vanaf <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Laat wag stemboodskap vanaf <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Vee stemboodskap vanaf <xliff:g id="NAMEORNUMBER">^1</xliff:g> uit"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> nuwe stemboodskappe</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> nuwe stemboodskap</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Skep kontak vir <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Voeg <xliff:g id="NAMEORNUMBER">^1</xliff:g> by \'n bestaande kontak"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Oproepbesonderhede vir <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Uit oproepgeskiedenis gevee"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Vandag"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Gister"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Ouer"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Oproepelys"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Skakel luidspreker aan."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Skakel luidspreker af."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Speel vinniger."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Speel stadiger."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Begin of laat wag speel."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Vertoonopsies"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Klanke en vibrasie"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Toeganklikheid"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Foonluitoon"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibreer ook vir oproepe"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Belbladklanke"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Toonlengte vir belblad"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normaal"</item>
- <item msgid="6177579030803486015">"Lank"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Vinnige antwoorde"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Oproepe"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Oproepblokkering"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Oproepblokkering is tydelik af"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Oproepblokkering is gedeaktiveer omdat jy die afgelope 48 uur nooddienste van hierdie foon af gekontak het. Dit sal outomaties heraktiveer word sodra die 48-uurtydperk verstryk."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Voer nommers in"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Jy het sommige bellers vroeër gemerk om outomaties via ander programme na stemboodskapdiens gestuur te word."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Bekyk nommers"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Voer in"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Kon nie invoer nie"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Kon nie stemboodskap argiveer nie."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Deblokkeer nommer"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Voeg nommer by"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Oproepe van hierdie nommers af sal geblokkeer word en stemboodskappe sal outomaties uitgevee word."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Oproepe van hierdie nommers af sal geblokkeer word, maar die bellers sal dalk steeds vir jou stemboodskappe kan los."</string>
- <string name="block_list" msgid="7760188925338078011">"Geblokkeerde nommers"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> is ongeldig."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> is reeds geblokkeer."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Oproepblokkering is vir 48 uur gedeaktiveer"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Gedeaktiveer omdat \'n noodoproep gemaak is."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Oproeprekeninge"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Skakel aan"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Stel toestemmings"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Skakel die kontaktetoestemming aan om spoedbel te aktiveer."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Skakel die foontoestemming aan om jou oproeprekord te sien."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Skakel die kontaktetoestemming aan om jou kontakte te sien."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Skakel die foontoestemming aan om na jou stemboodskapdiens te gaan."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Skakel die Kontakte-toestemmings aan om jou kontakte te deursoek."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Skakel die foontoestemming aan om \'n oproep te maak."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Foonprogram het nie toestemming om stelselinstellings te stel nie."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Geblokkeer"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> is aktief"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blokkeer/gee strooipos aan"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blokkeer"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Nie strooipos nie"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Deblokkeer"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Strooipos"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Blokkeer <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Toekomstige oproepe en stemboodskappe vanaf hierdie nommer sal geblokkeer word."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Gee oproep aan as strooipos"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Toekomstige oproepe en stemboodskappe vanaf hierdie nommer sal geblokkeer word. Hierdie oproep sal as strooipos aangegee word."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Deblokkeer <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Hierdie nommer sal gedeblokkeer word en aangegee word as nie strooipos nie. Toekomstige oproepe en stemboodskappe sal nie as strooipos geïdentifiseer word nie."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Witlys <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Witlys"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Toekomstige oproepe en stemboodskappe vanaf hierdie nommer sal nie as strooipos geïdentifiseer word nie. Hierdie nommer sal aangegee word as nie strooipos nie."</string>
-</resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
deleted file mode 100644
index deb697f6d..000000000
--- a/res/values-am/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"ስልክ"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"ስልክ"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"የስልክ መደወያ ሰሌዳ"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"ስልክ"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"የጥሪ ታሪክ"</string>
- <string name="action_report_number" msgid="4635403959812186162">"ትክክለኛ ያልሆነ ቁጥርን ሪፓርት አድርግ"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"ቁጥር ቅዳ"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"ወደ ጽሑፍ የተገለበጠውን ቅዳ"</string>
- <string name="action_block_number" msgid="1482657602262262134">"ቁጥርን አግድ"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> ታግዷል"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"ቁጥርን አታግድ"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"የ<xliff:g id="NUMBER">%1$s</xliff:g> እገዳ ተነስቷል"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ቀልብስ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"ሰርዝ"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"ከመደወል በፊት ቁጥር አርትዕ"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"የጥሪ ታሪክን አጽዳ"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"የድምፅ መልዕክት ሰርዝ"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"የድምፅ መልዕክት በማህደር አስቀምጥ"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"የድምፅ መልዕክት አጋራ"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"የድምጽ ፖስታ ተፈልጎ ተገኝቷል"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"የድምፅ መልዕክት በማህደር ተቀምጧል"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ቀልብስ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ወደ መዝገብ ሂድ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"የጥሪ ታሪክ ይጽዳ?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"ይሄ ሁሉንም ጥሪዎች ከታሪክዎ ይሰርዛቸዋል"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"የጥሪ ታሪክን በማጽዳት ላይ…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"ስልክ"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"ያመለጠ ጥሪ"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"ያመለጠ የሥራ ጥሪ"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"ያመለጡ ጥሪዎች"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ያመለጡ ጥሪዎች"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"የኋላ ጥሪ"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"መልእክት"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> የድምፅ መልዕክቶች </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> የድምፅ መልዕክቶች </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"አጫውት"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>፤<xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"ከ<xliff:g id="CALLER">%1$s</xliff:g> አዲስ የድምፅመልዕክት"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"የድምጽ መልዕክትን ማጫወት አልተቻለም"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"የድምጽ መልዕክትን በመጫን ላይ…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"የድምፅ መልዕክት ማህደር ውስጥ በማስቀመጥ ላይ…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"የድምጽ መልዕክትን መጫን አልተቻለም"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"የድምጽ መልዕክት ያላቸው ጥሪዎች ብቻ"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"ገቢ ጥሪዎች ብቻ"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"ወጪ ጥሪዎች ብቻ"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"ያመለጡ ጥሪዎች ብቻ"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"የሚታይ የድምጽ መልዕክት"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"ቁጥር ሳይደውሉ የእርስዎን የድምጽ መልዕክት ይመልከቱና ያድምጡ። የውሂብ ክፍያዎች ተግባራዊ ይሆኑ ይሆናል።"</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"ቅንብሮች"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"የድምጽ መልዕክት ዝማኔ አይገኝም"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"አዲስ የድምጽ መልዕክት በመጠበቅ ላይ። አሁን መጫን አይቻልም።"</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"የእርስዎን ድምጽ መልዕክት ያዋቅሩ"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"ተሰሚ አይገኝም"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"አዘጋጅ"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"የድምፅመልዕክት ደውል"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"ቁጥር ምረጥ"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"ቁጥር ምረጥ"</string>
- <string name="make_primary" msgid="5829291915305113983">"ይህን ምርጫ አስታውስ"</string>
- <string name="description_search_button" msgid="3660807558587384889">"ፍለጋ"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"ደውል"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"ቁጥር ለመደወል"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"መልሶ ማጫወት አጫውት ወይም አቁም"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"የስልክ ድምጽ ማጉያን ያብሩ ወይም ያጥፉ"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"የመልሶ ማጫወት ቦታ ይፈልጉ"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"የመልሶ ማጫወት ፍጥነት ቀንስ"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"የመልሶ ማጫወት ፍጥነት ጨምር"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"የስልክ ጥሪ ታሪክ"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"ተጨማሪ አማራጮች"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"የመደወያ ሰሌዳ"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"ወጪዎቹን ብቻ አሳይ"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"ገቢዎቹን ብቻ አሳይ"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"ያመለጡትን ብቻ አሳይ"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"የድምፅ መልዕክቶች ብቻ አሳይ"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"ሁሉንም ጥሪዎች አሳይ"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2 ሴኮንድ ፋታ አክል"</string>
- <string name="add_wait" msgid="3360818652790319634">"መጠበቅ አክል"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"ቅንብሮች"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"አዲስ ዕውቅያ"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"ሁሉም ዕውቂያዎች"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"የጥሪ ዝርዝሮች"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"ዝርዝሮች አይገኙም"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"የድምፅ ቁልፍ ሰሌዳን ንካ"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"በመካሄድ ላይ ወዳለው ጥሪ ተመለስ"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"ጥሪ ያክሉ"</string>
- <string name="type_incoming" msgid="6502076603836088532">"ገቢ ጥሪ"</string>
- <string name="type_outgoing" msgid="343108709599392641">"ወጪ ጥሪ"</string>
- <string name="type_missed" msgid="2720502601640509542">"ያመለጠ ጥሪ"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"ገቢ የቪዲዮ ጥሪ"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"ወጪ የቪዲዮ ጥሪ"</string>
- <string name="type_missed_video" msgid="954396897034220545">"ያመለጠ የቪዲዮ ጥሪ"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"የድምፅ መልዕክት"</string>
- <string name="type_rejected" msgid="7783201828312472691">"ያልተነሳ ጥሪ"</string>
- <string name="type_blocked" msgid="3521686227115330015">"የታገደ ጥሪ"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"ገቢ ጥሪዎች"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"የድምፅ መልዕክት አጫውት"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"ዕውቂያ <xliff:g id="NAME">%1$s</xliff:g> ዕይ"</string>
- <string name="description_call" msgid="3443678121983852666">"ጥሪ <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"የ<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> እውቂያ ዝርዝሮች"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> ጥሪዎች።"</string>
- <string name="description_video_call" msgid="2933838090743214204">"የቪዲዮ ጥሪ።"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"ኤስኤምኤስ ለ<xliff:g id="NAME">%1$s</xliff:g> ላክ"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"ያልተሰማ የድምፅመልዕክት"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"በድምፅ ፍለጋ ይጀምሩ"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"ደውል<xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"ያልታወቀ"</string>
- <string name="voicemail" msgid="3851469869202611441">"የድምፅ መልዕክት"</string>
- <string name="private_num" msgid="6374339738119166953">"የግል ቁጥር"</string>
- <string name="payphone" msgid="7726415831153618726">"የሕዝብ ስልክ"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> ሰከንድ"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> ደቂቃ <xliff:g id="SECONDS">%s</xliff:g> ሴከ"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g> ላይ"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"ይህን ቁጥር መደወል አልተቻለም"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"ድምጽ መልዕክትን ለማደራጀት ወደ ምናሌ &gt; ቅንብሮች ሂድ::"</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"የድምጽ መልዕክት ጥሪ ለማድረግ፣ በመጀመሪያ የአውሮፕላን ሁነታን አጥፋ።"</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"በመስቀል ላይ…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"ከSIM ካርድ ላይ በመጫን ላይ..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"የSIM ካርድ ዕውቂያዎች"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"ምንም የእውቂያዎች መተግበሪያ አይገኝም"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"የድምጽ ፍለጋ አይገኝም"</string>
- <string name="call_not_available" msgid="8941576511946492225">"የስልክ መተግበሪያው ስለተሰናከለ የስልክ ጥሪ ማድረግ አይቻልም።"</string>
- <string name="activity_not_available" msgid="2287665636817987623">"ለዚያ የሚሆን መተግበሪያ በዚህ መሣሪያ ላይ የለም"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"እውቂያዎችን ይፈልጉ"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"ቁጥር ያክሉ ወይም እውቂያዎችን ይፈልጉ"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"የእርስዎ የጥሪ ታሪክ ባዶ ነው"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"ደውል"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"ምንም ያመለጡዎት ጥሪዎች የሉዎትም።"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"የእርስዎ የድምጽ መልዕክት ገቢ መልዕክት ባዶ ነው።"</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"የድምፅ መልዕክት መዝገብዎ ባዶ ነው።"</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"ተወዳጆችን ብቻ አሳይ"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"የጥሪ ታሪክ"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"የድምፅ መልዕክት መዝገብ"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"ሁሉም"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"ያመለጡ"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"የድምፅ መልዕክት"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"አዲስ፣ የተቃለለ ማገድ"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"እርስዎን በተሻለ ሁኔታ መጠበቅ እንዲችል ስልክ ማገድ እንዴት እንደሚሰራ መቀየር አለበት። የታገዱ ቁጥሮችዎ አሁን ጥሪዎችም ጽሑፎችም መላክ ያቆማሉ፣ እና ከሌሎች መተግበሪያዎች ጋር ሊጋሩ ይችላሉ።"</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"ፍቀድ"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> ይታገድ?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"ከዚህ ቁጥር የሚመጡ ጥሪዎች ይታገዳሉ፣ እና የድምጽ መልዕክቶች በራስ-ሰር ይሰረዛሉ።"</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"ከዚህ ቁጥር የሚመጡ ጥሪዎች ይታገዳሉ፣ ነገር ግን ደዋዩ አሁንም የድምጽ መልዕክቶችን ሊተዉልዎ ይችላል።"</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"ከአሁን በኋላ ከዚህ ቁጥር የሚመጡ ጥሪዎችን ወይም ጽሑፎችን አይቀበሉም።"</string>
- <string name="block_number_ok" msgid="770551992296781873">"አግድ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"የ<xliff:g id="NUMBER">%1$s</xliff:g> እግድ ይነሳ?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"እገዳ አንሳ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"ፈጣን ደውል"</string>
- <string name="tab_history" msgid="2563144697322434940">"የጥሪ ታሪክ"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"ዕውቂያዎች"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"የድምፅ መልዕክት"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"ከተወዳጆች ውስጥ ተወግዷል።"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"ቀልብስ"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"ለ<xliff:g id="NUMBER">%s</xliff:g> ደውል"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"አዲስ እውቂያ ይፍጠሩ"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"ወደ እውቂያ አክል"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"ኤስኤምኤስ ላክ"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"የቪዲዮ ጥሪ አድርግ"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"ቁጥርን አግድ"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> አዲስ ያልተመለሱ ጥሪዎች"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"በፈጥኖ መደወያ ላይ ገና ማንም የለዎትም"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"ተወዳጅ አክል"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"ገና ምንም እውቂያዎች የሉዎትም"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"ዕውቂያ አክል"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"ሁሉንም ቁጥሮች ለማየት ምስል ይንኩ ወይም ዳግም ለመደርደር ነክተው ይያዙት"</string>
- <string name="remove_contact" msgid="1080555335283662961">"አስወግድ"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"የቪዲዮ ጥሪ"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"መልእክት ላክ"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"የጥሪ ዝርዝሮች"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ይደውሉ"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"ከ<xliff:g id="NAMEORNUMBER">^1</xliff:g>፣ <xliff:g id="TYPEORLOCATION">^2</xliff:g>፣ <xliff:g id="TIMEOFCALL">^3</xliff:g>፣ <xliff:g id="PHONEACCOUNT">^4</xliff:g> ያመለጠ ጥሪ።"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"ከ<xliff:g id="NAMEORNUMBER">^1</xliff:g>፣ <xliff:g id="TYPEORLOCATION">^2</xliff:g>፣ <xliff:g id="TIMEOFCALL">^3</xliff:g>፣ <xliff:g id="PHONEACCOUNT">^4</xliff:g> መልስ የተሰጠው ጥሪ።"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"ያልተነበበ የድምጽ ፖስታ ከ<xliff:g id="NAMEORNUMBER">^1</xliff:g>፣ <xliff:g id="TYPEORLOCATION">^2</xliff:g>፣ <xliff:g id="TIMEOFCALL">^3</xliff:g>፣ <xliff:g id="PHONEACCOUNT">^4</xliff:g>።"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"የድምጽ ፖስታ ከ<xliff:g id="NAMEORNUMBER">^1</xliff:g>፣ <xliff:g id="TYPEORLOCATION">^2</xliff:g>፣ <xliff:g id="TIMEOFCALL">^3</xliff:g>፣ <xliff:g id="PHONEACCOUNT">^4</xliff:g>።"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"ወደ <xliff:g id="NAMEORNUMBER">^1</xliff:g>፣ <xliff:g id="TYPEORLOCATION">^2</xliff:g>፣ <xliff:g id="TIMEOFCALL">^3</xliff:g>፣ <xliff:g id="PHONEACCOUNT">^4</xliff:g> ደውል።"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"በ<xliff:g id="PHONEACCOUNT">^1</xliff:g> ላይ"</string>
- <string name="description_via_number" msgid="3503311803959108316">"በ <xliff:g id="NUMBER">%1$s</xliff:g> በኩል"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"በ <xliff:g id="NUMBER">%1$s</xliff:g> በኩል"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"በ <xliff:g id="PHONEACCOUNT">%1$s</xliff:g> ላይ፣ በ <xliff:g id="NUMBER">%2$s</xliff:g> በኩል"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> በ <xliff:g id="NUMBER">%2$s</xliff:g> በኩል"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"ደውል"</string>
- <string name="description_call_action" msgid="4000549004089776147">"ወደ <xliff:g id="NAMEORNUMBER">^1</xliff:g> ይደውሉ"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"ቪዲዮ ጥሪ <xliff:g id="NAMEORNUMBER">^1</xliff:g>።"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"የ<xliff:g id="NAMEORNUMBER">^1</xliff:g>ን የድምጽ መልዕክት ያዳምጡ"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"የድምጽ መልዕክት ያጫውቱ ከ<xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"የድምጽ መልዕክት ለአፍታ ያቁሙ ከ<xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"የድምጽ መልዕክት ይሰርዙ ከ<xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> አዲስ የድምጽ መልዕክቶች</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> አዲስ የድምጽ መልዕክቶች</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"ለ<xliff:g id="NAMEORNUMBER">^1</xliff:g> እውቂያ ፍጠር"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>ን ወደ አሁን ያለ እውቂያ አክል"</string>
- <string name="description_details_action" msgid="2433827152749491785">"የ<xliff:g id="NAMEORNUMBER">^1</xliff:g> የጥሪ ዝርዝሮች"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"ከጥሪ ታሪክ ተሰርዟል"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"ዛሬ"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"ትላንት"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"የቆየ"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"የጥሪዎች ዝርዝር"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"ድምጽ ማጉያን አብራ።"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"ድምጽ ማጉያን አጥፋ።"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"በፍጥነት አጫውት።"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"በዝግታ አጫውት።"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"መልሰህ አጫውትን አስጀምር ወይም ለአፍታ አቁም።"</string>
- <string name="list_delimeter" msgid="4571593167738725100">"፣ "</string>
- <string name="display_options_title" msgid="7812852361055667468">"የማሳያ አማራጮች"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"ድምጾች እና ንዝረት"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"ተደራሽነት"</string>
- <string name="ringtone_title" msgid="760362035635084653">"የስልክ ጥሪ ቅላጼ"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"እንዲሁም ለጥሪዎችም ንዘር"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"የመደወያ ሰሌዳ ቅላጼዎች"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"የስልክ ሰሌዳ ድምጽ ርዝመት"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"መደበኛ"</item>
- <item msgid="6177579030803486015">"ረጅም"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"ፈጣን ምላሾች"</string>
- <string name="call_settings_label" msgid="313434211353070209">"ጥሪዎች"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"ጥሪን ማገድ"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"ጥሪን ማገድ ለጊዜው ተሰናክሏል"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"ባለፉት 48 ሰዓቶች ውስጥ ከዚህ ስልክ ሆነው የአስቸኳይ አደጋ አገልግሎቶችን ስላነጋገሩ ጥሪን ማገድ ተሰናክሏል። አንዴ የ48 ሰዓቱ ጊዜ ካለፈ በኋላ በራስ-ሰር ዳግም ይነቃል።"</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"ቁጥሮችን አስመጣ"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"ከዚህ ቀደም አንዳንድ ደዋዮች በሌሎች መተግበሪያዎች በኩል በራስ-ሰር ወደ የድምፅ መልዕክት እንዲላኩ ምልክት አድርገባቸው ነበር።"</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"ቁጥሮችን ይመልከቱ"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"አስመጣ"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"ማስመጣት አልተሳካም"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"የድምፅ መልዕክት ማህደር ውስጥ ማስቀመጥ አልተሳካም።"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"ቁጥርን አታግድ"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"ቁጥር አክል"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"ከእነዚህ ቁትሮች የሚመጡ ጥሪዎች ይታገዳሉ፣ እና የድምጽ መልዕክቶች በራስ-ሰር ይሰረዛሉ።"</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"ከእነዚህ ቁትሮች የሚመጡ ጥሪዎች ይታገዳሉ፣ ነገር ግን አሁንም የድምጽ መልዕክቶችን ሊተዉልዎ ይችላሉ።"</string>
- <string name="block_list" msgid="7760188925338078011">"የታገዱ ቁጥሮች"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> ልክ ያልኾነ ነው።"</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> ቀድሞውኑ ታግዷል።"</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"የጥሪ እገዳ ለ48 ሰዓቶች ተሰናክሏል።"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"የአስቸኳይ አደጋ ጥሪ ስለተደረገ ተሰናክሏል።"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"የመደወያ መለያዎች"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"አብራ"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"ፍቃዶችን አዋቅር"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"ፈጥኖ መደወያን ለማንቃት የእውቂያዎች ፍቃዱን ያብሩ።"</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"የጥሪ ምዝግብ ማስታወአሽዎን ለማየት የስልክ ፍቃዱን ያብሩ።"</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"የእርስዎን እውቂያዎች ለማየት የእውቂያዎች ፍቃዱን ያብሩ።"</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"የድምፅ መልዕክትዎን ለመድረስ የስልክ ፍቃዱን ያብሩ።"</string>
- <string name="permission_no_search" msgid="84152933267902056">"የእርስዎን እውቂያዎች ለመከታተል የእውቂያዎች ፍቃዶችን ያብሩ።"</string>
- <string name="permission_place_call" msgid="2101287782615887547">"ስልክ ለመደወል የስልክ ፍቃዱን ያብሩ።"</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"የስልክ መተግበሪያ ወደ የስርዓት ቅንብሮች የመጻፍ ፍቃድ የለውም።"</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"የታገዱ"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ነቅቷል"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"አይፈለጌ መልእክትን አግድ/ሪፖርት አድርግ"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"አግድ"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"አይፈለጌ መልእክት አይደለም"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"እገዳውን አንሳ"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"አይፈለጌ መልዕክት"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> ይታገድ?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"ከዚህ ቁጥር የሚመጡ ሁሉም የወደፊት ጥሪዎች እና የድምፅ መልእክቶች ይታገዳሉ።"</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"ጥሪውን እንደ አይፈለጌ መልዕክት ሪፖርት አድርግ"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"ከዚህ ቁጥር የሚመጡ ሁሉም የወደፊት ጥሪዎች እና የድምፅ መልእክቶች ይታገዳሉ። ይህ ጥሪ እንደ አይፈለጌ ሪፖርት ይደረጋል።"</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"የ<xliff:g id="NUMBER">%1$s</xliff:g> እግድ ይነሳ?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"ይህ ቁጥር እግዱ ይነሳለታል እንዲሁም አይፈለጌ እንዳልሆነ ሪፖርት ይደረጋል። የወደፊት ጥሪዎች እና የድምፅ መልዕክቶች እንደ አይፈለጌ አይቆጠሩም።"</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> የክብር ዝርዝር ውስጥ ይግባ?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"የክብር ዝርዝር"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"ከዚህ ቁጥር የሚመጡ የወደፊት ጥሪዎች እና የድምፅ መልእክቶች እንደ አይፈለጌ አይቆጠሩም። ይህ ቁጥር እንደ አይፈለጌ እንዳልሆነ ሪፖርት ይደረጋል።"</string>
-</resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
deleted file mode 100644
index 7f98f3311..000000000
--- a/res/values-ar/strings.xml
+++ /dev/null
@@ -1,296 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"الهاتف"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"الهاتف"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"لوحة اتصال الهاتف"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"الهاتف"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"سجل المكالمات"</string>
- <string name="action_report_number" msgid="4635403959812186162">"الإبلاغ عن رقم غير دقيق"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"نسخ الرقم"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"نسخ الكتابة الصوتية"</string>
- <string name="action_block_number" msgid="1482657602262262134">"حظر الرقم"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"تم حظر <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"إلغاء حظر الرقم"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"تم إلغاء حظر <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="block_number_undo" msgid="591338370336724156">"تراجع"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"حذف"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"تعديل الرقم قبل الاتصال"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"محو سجل المكالمات"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"حذف رسالة البريد الصوتي"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"البريد الصوتي للأرشيف"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"مشاركة بريد صوتي"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"تم حذف الرسالة الصوتية"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"البريد الصوتي المؤرشف"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"تراجع"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"انتقال إلى الأرشيف"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"هل تريد محو سجل المكالمات؟"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"سيؤدي ذلك إلى حذف جميع المكالمات من السجل"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"جارٍ محو سجل المكالمات…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"الهاتف"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"مكالمة فائتة"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"مكالمة عمل فائتة"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"المكالمات الفائتة"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> من المكالمات الفائتة"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"معاودة الاتصال"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"رسالة"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="zero">لا تتوفر رسائل بريد صوتي (<xliff:g id="COUNT">%1$d</xliff:g>) </item>
- <item quantity="two">رسالتا بريد صوتي (<xliff:g id="COUNT">%1$d</xliff:g>) </item>
- <item quantity="few"> <xliff:g id="COUNT">%1$d</xliff:g> رسائل بريد صوتي </item>
- <item quantity="many"> <xliff:g id="COUNT">%1$d</xliff:g> رسالة بريد صوتي </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> من رسائل البريد الصوتي </item>
- <item quantity="one">رسالة بريد صوتي</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"تشغيل"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>، <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"بريد صوتي جديد من <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"تعذر تشغيل البريد الصوتي"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"جارٍ تحميل البريد الصوتي…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"تجري أرشفة البريد الصوتي..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"تعذر تحميل البريد الصوتي"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"المكالمات التي تشتمل على بريد صوتي فقط"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"المكالمات الواردة فقط"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"المكالمات الصادرة فقط"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"المكالمات الفائتة فقط"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"بريد صوتي مرئي"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"يمكنك الاطلاع على بريدك الصوتي والاستماع إليه، بدون الاضطرار إلى الاتصال برقم. ويمكن أن يتم فرض رسوم للبيانات."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"الإعدادات"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"تحديثات البريد الصوتي غير متاحة"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"ليس هناك بريد صوتي قيد الانتظار. لا يمكن التحميل في الوقت الحالي."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"إعداد البريد الصوتي"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"الصوت غير متاح"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"إعداد"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"اتصال ببريد صوتي..."</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"اختيار رقم"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"اختيار رقم"</string>
- <string name="make_primary" msgid="5829291915305113983">"تذكر هذا الاختيار"</string>
- <string name="description_search_button" msgid="3660807558587384889">"بحث"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"طلب"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"الرقم الذي سيتم طلبه"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"بدء التشغيل أو إيقافه"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"تشغيل مكبر الصوت أو تعطيله"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"طلب موضع تشغيل"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"خفض معدل التشغيل"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"زيادة معدل التشغيل"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"سجل المكالمات"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"مزيد من الخيارات"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"لوحة الطلب"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"عرض الصادر فقط"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"عرض الوارد فقط"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"عرض الفائت فقط"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"إظهار رسائل البريد الصوتي فقط"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"عرض جميع المكالمات"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"إضافة ثانيتين إيقاف مؤقت"</string>
- <string name="add_wait" msgid="3360818652790319634">"إضافة انتظار"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"الإعدادات"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"جهة اتصال جديدة"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"جميع جهات الاتصال"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"تفاصيل المكالمة"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"التفاصيل غير متاحة"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"استخدام لوحة مفاتيح نغمات باللمس"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"عودة إلى المكالمة الجارية"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"إضافة مكالمة"</string>
- <string name="type_incoming" msgid="6502076603836088532">"مكالمة واردة"</string>
- <string name="type_outgoing" msgid="343108709599392641">"مكالمة صادرة"</string>
- <string name="type_missed" msgid="2720502601640509542">"مكالمة فائتة"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"مكالمة فيديو واردة"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"مكالمة فيديو صادرة"</string>
- <string name="type_missed_video" msgid="954396897034220545">"مكالمة فيديو فائتة"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"البريد الصوتي"</string>
- <string name="type_rejected" msgid="7783201828312472691">"مكالمة مرفوضة"</string>
- <string name="type_blocked" msgid="3521686227115330015">"مكالمة محظورة"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"المكالمات الواردة"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"تشغيل البريد الصوتي"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"عرض جهة الاتصال <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"الاتصال بـ <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"تفاصيل جهة الاتصال بـ <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> من المكالمات."</string>
- <string name="description_video_call" msgid="2933838090743214204">"مكالمة فيديو."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"‏إرسال رسالة قصيرة SMS إلى <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"البريد الصوتي غير المسموع"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"بدء البحث الصوتي"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"الاتصال بالرقم <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"غير معروف"</string>
- <string name="voicemail" msgid="3851469869202611441">"البريد الصوتي"</string>
- <string name="private_num" msgid="6374339738119166953">"رقم خاص"</string>
- <string name="payphone" msgid="7726415831153618726">"هاتف يعمل بالعملة"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> ثانية"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> دقيقة <xliff:g id="SECONDS">%s</xliff:g> ثانية"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> في <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"لا يمكن الاتصال بهذا الرقم"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"‏لإعداد البريد الصوتي، انتقل إلى القائمة &gt; الإعدادات."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"للاتصال بالبريد الصوتي، يجب أولاً إيقاف وضع الطائرة."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"جارٍ التحميل..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"‏جارٍ التحميل من شريحة SIM…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"‏شريحة SIM وجهات الاتصال"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"لا يتوفر تطبيق لجهات الاتصال"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"البحث الصوتي غير متاح"</string>
- <string name="call_not_available" msgid="8941576511946492225">"يتعذر إجراء مكالمة هاتفية نظرًا لأنه تم تعطيل تطبيق الهاتف."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"لا يوجد تطبيق لإجراء ذلك على هذا الجهاز"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"البحث في قائمة جهات الاتصال"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"إضافة رقم أو البحث في جهات الاتصال"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"سجل مكالماتك فارغ"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"إجراء مكالمة"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"ليست لديك أية مكالمات لم يتم الرد عليها."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"مجلد بريدك الوارد الصوتي فارغ."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"أرشيف البريد الصوتي الخاص بك فارغ."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"عرض المفضلة فقط"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"سجل المكالمات"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"أرشيف البريد الصوتي"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"الكل"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"فائتة"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"البريد الصوتي"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"حظر جديد ومبسط"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"لحمايتك بشكل أفضل، يحتاج الهاتف إلى تغيير آلية تنفيذ الحظر. تؤدي الآن ميزة حظر الأرقام إلى إيقاف كل من المكالمات والرسائل النصية، كما أن ذلك الحظر يمكن أن يمتد إلى تطبيقات أخرى."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"سماح"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"هل تريد حظر الرقم <xliff:g id="NUMBER">%1$s</xliff:g>؟"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"سيتم حظر المكالمات الواردة من هذا الرقم كما سيتم تلقائيًا حذف الرسائل الصوتية."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"سيتم حظر المكالمات من هذا الرقم، إلا أنه سيظل بإمكان المتصل ترك رسائل صوتية."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"لن تتلقي المكالمات أو المراسلات النصية بعد الآن من هذا الرقم."</string>
- <string name="block_number_ok" msgid="770551992296781873">"حظر"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"هل تريد إزالة حظر <xliff:g id="NUMBER">%1$s</xliff:g>؟"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"إلغاء الحظر"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"اتصال سريع"</string>
- <string name="tab_history" msgid="2563144697322434940">"سجل المكالمات"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"جهات الاتصال"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"البريد الصوتي"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"تمت إزالة جهة الاتصال من المفضلة"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"تراجع"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"الاتصال بالرقم <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"إنشاء جهة اتصال جديدة"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"إضافة إلى جهة اتصال"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"‏إرسال رسالة قصيرة SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"إجراء مكالمة فيديو"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"حظر الرقم"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> من المكالمات الجديدة الفائتة"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"لم تتم إضافة أية جهة اتصال إلى قائمة الاتصال السريع حتى الآن"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"إضافة مفضلة"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"ليست لديك أية جهات اتصال حتى الآن"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"إضافة جهة اتصال"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"المس الصورة للاطلاع على جميع الأرقام أو المس مع الاستمرار لإعادة الترتيب"</string>
- <string name="remove_contact" msgid="1080555335283662961">"إزالة"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"مكالمة فيديو"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"إرسال رسالة"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"تفاصيل المكالمة"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"الاتصال بـ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"مكالمة لم يرد عليها من <xliff:g id="NAMEORNUMBER">^1</xliff:g>، <xliff:g id="TYPEORLOCATION">^2</xliff:g>، <xliff:g id="TIMEOFCALL">^3</xliff:g>، <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"تم الرد على مكالمة من <xliff:g id="NAMEORNUMBER">^1</xliff:g>، <xliff:g id="TYPEORLOCATION">^2</xliff:g>، <xliff:g id="TIMEOFCALL">^3</xliff:g>، <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"البريد الصوتي غير المقروء من <xliff:g id="NAMEORNUMBER">^1</xliff:g>، <xliff:g id="TYPEORLOCATION">^2</xliff:g>، <xliff:g id="TIMEOFCALL">^3</xliff:g>، <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"البريد الصوتي من <xliff:g id="NAMEORNUMBER">^1</xliff:g>، <xliff:g id="TYPEORLOCATION">^2</xliff:g>، <xliff:g id="TIMEOFCALL">^3</xliff:g>، <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"اتصال بـ <xliff:g id="NAMEORNUMBER">^1</xliff:g>، <xliff:g id="TYPEORLOCATION">^2</xliff:g>، <xliff:g id="TIMEOFCALL">^3</xliff:g>، <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"من <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"من خلال <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"من خلال <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"على <xliff:g id="PHONEACCOUNT">%1$s</xliff:g> من خلال <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> من خلال <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"اتصال"</string>
- <string name="description_call_action" msgid="4000549004089776147">"الاتصال بـ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"إجراء مكالمة فيديو مع <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"استماع إلى بريد صوتي من <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"تشغيل البريد الصوتي من <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"إيقاف البريد الصوتي من <xliff:g id="NAMEORNUMBER">^1</xliff:g> مؤقتًا"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"حذف البريد الصوتي من <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="zero">ليست هناك أية رسائل صوتية جديدة (<xliff:g id="COUNT_1">%d</xliff:g>)</item>
- <item quantity="two">رسالتان صوتيتان جديدتان (<xliff:g id="COUNT_1">%d</xliff:g>)</item>
- <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> رسائل صوتية</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> رسالة صوتية</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> من الرسائل الصوتية</item>
- <item quantity="one">رسالة صوتية جديدة (<xliff:g id="COUNT_0">%d</xliff:g>)</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"إنشاء جهة اتصال لـ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"إضافة <xliff:g id="NAMEORNUMBER">^1</xliff:g> إلى جهة اتصال حالية"</string>
- <string name="description_details_action" msgid="2433827152749491785">"تفاصيل الاتصال لـ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"تم الحذف من سجل المكالمات"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"اليوم"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"أمس"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"أقدم"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"قائمة المكالمات"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"تشغيل مكبر الصوت."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"تعطيل مكبر الصوت."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"تشغيل أسرع."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"تشغيل أقل سرعة."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"بدء التشغيل أو إيقافه مؤقتًا."</string>
- <string name="list_delimeter" msgid="4571593167738725100">"، "</string>
- <string name="display_options_title" msgid="7812852361055667468">"خيارات العرض"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"الأصوات والاهتزاز"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"إمكانية الوصول"</string>
- <string name="ringtone_title" msgid="760362035635084653">"نغمة رنين الهاتف"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"الاهتزاز أيضًا مع المكالمات"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"نغمات لوحة الاتصال"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"طول نغمة لوحة الاتصال"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"عادية"</item>
- <item msgid="6177579030803486015">"طويلة"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"الردود السريعة"</string>
- <string name="call_settings_label" msgid="313434211353070209">"المكالمات"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"حظر المكالمات"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"حظر المكالمات معطّل مؤقتًا"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"تم تعطيل حظر المكالمات لأنك اتصلت بخدمات الطوارئ خلال 48 ساعة ماضية. وستتم إعادة تمكينه تلقائيًا بعد انتهاء هذه الفترة التي تبلغ 48 ساعة."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"استيراد الأرقام"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"سبق لك تعيين بعض المتصلين على الإرسال تلقائيًا للبريد الصوتي عبر التطبيقات الأخرى."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"عرض الأرقام"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"استيراد"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"أخفق الاستيراد"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"فشلت أرشفة البريد الصوتي."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"إلغاء حظر الرقم"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"إضافة رقم"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"سيتم حظر المكالمات من هذه الأرقام وسيتم حذف الرسائل الصوتية تلقائيًا."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"سيتم حظر المكالمات من هذه الأرقام، إلا أنه قد يظل بإمكانك ترك رسائلك الصوتية."</string>
- <string name="block_list" msgid="7760188925338078011">"الأرقام المحظورة"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> غير صالح."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"تم حظر <xliff:g id="NUMBER">%1$s</xliff:g>."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"تم تعطيل حظر المكالمات لمدة 48 ساعة."</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"تم التعطيل نظرًا لإجراء مكالمة طوارئ."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"حسابات الاتصال"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"تشغيل"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"تعيين الأذونات"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"لتمكين الاتصال السريع، شغِّل إذن جهات الاتصال."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"للاطلاع على سجل المكالمات، شغِّل إذن الهاتف."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"للاطلاع على جهات الاتصال، شغِّل إذن جهات الاتصال."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"للوصول إلى البريد الصوتي، شغِّل إذن الهاتف."</string>
- <string name="permission_no_search" msgid="84152933267902056">"للبحث عن جهات الاتصال، عليك تشغيل أذونات جهات الاتصال."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"لإجراء مكالمة، شغِّل إذن الهاتف."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"ليس لدى تطبيق الهاتف إذن لتعديل إعدادات النظام."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"تم الحظر"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> في الوضع النشط"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"حظر/إبلاغ عن رقم غير مرغوب فيه"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"حظر"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"ليس رقمًا غير مرغوب فيه"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"إلغاء الحظر"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"تعليق غير مرغوب فيه"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"هل تريد حظر الرقم <xliff:g id="NUMBER">%1$s</xliff:g>؟"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"سيتم حظر المكالمات المستقبلية والبريد الصوتي من هذا الرقم."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"الإبلاغ عن المكالمة كغير مرغوب فيها"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"سيتم حظر المكالمات المستقبلية والبريد الصوتي من هذا الرقم والإبلاغ عن المكالمة كغير مرغوب فيها."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"هل تريد إزالة حظر <xliff:g id="NUMBER">%1$s</xliff:g>؟"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"ستتم إزالة الحظر عن هذا الرقم ولن يتم الإبلاغ عنه باعتباره غير مرغوب فيه. لن يتم تعريف المكالمات المستقبلية والبريد الصوتي باعتبارهما غير مرغوب فيهما."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"القائمة البيضاء <xliff:g id="NUMBER">%1$s</xliff:g>؟"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"إدراج في القائمة البيضاء"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"لن يتم تعريف المكالمات المستقبلية والبريد الصوتي من هذا الرقم كغير مرغوب فيهما. لن يتم الإبلاغ عن هذا الرقم كغير مرغوب فيه."</string>
-</resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
deleted file mode 100644
index df78e7f90..000000000
--- a/res/values-az/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Telefon Yığım Pedi"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Zəng tarixçəsi"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Qeyri-dəqiq sayı bildirin"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Nömrəni kopyalayın"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Transkripsiyanı kopyalayın"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Nömrəni blok edin"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> blok edilib"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Nömrəni blokdan çıxarın"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> blokdan çıxarılıb"</string>
- <string name="block_number_undo" msgid="591338370336724156">"LƏĞV EDİN"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Silin"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Zəng etmədən öncə nömrəyə düzəliş edin"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Zəng tarixçəsini təmizləyin"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Səsli məktubu silin"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Səsli poçtu arxivləşdirin"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Səsli məktubu paylaşın"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Səsli məktub silindi"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Səsli poçt arxivləşdirildi"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"GERİ ALIN"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ARXİVƏ GEDİN"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Zəng tarixçəsi təmizlənsin?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Bu, tarixçənizdən bütün zəngləri siləcəkdir"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Zəng tarixçəsi silinir…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Buraxılmış zəng"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Buraxılmış iş çağrısı"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Buraxılmış zənglər"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> buraxılmış zənglər"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Geriyə zəng"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Mesaj"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Səsli poçt </item>
- <item quantity="one">Səsli poçt</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Oxudun"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> adlı şəxsdən yeni səsli məktub"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Səsli poçtu səsləndirmək mümkün deyil"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Səsli poçt yüklənir…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Səsli poçt arxivləşdirilir…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Səsli poçtu yükləmək mümkün olmadı"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Yalnız səsli məktublu zənglər"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Yalnız daxil olan zənglər"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Yalnız gedən zənglər"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Yalnız buraxılmış zənglər"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Vizual Səsli Mesaj"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Nömrə yığmağa ehtiyac olmadan səsli mesaja baxın və dinləyin. Data xərci tutula bilər."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Ayarlar"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Səsli poçt yeniləmələri mövcud deyil"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Yeni səsli poçt gözləyir. İndi yükləmək mümkün deyil."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Səsli poçtunuzu qurun"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio mövcud deyil"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Quraşdırın"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Səsli poçta zəng edin"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Nömrə seçin"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Nömrə seçin"</string>
- <string name="make_primary" msgid="5829291915305113983">"Bu seçimi yadda saxla"</string>
- <string name="description_search_button" msgid="3660807558587384889">"axtarış"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"nömrə yığın"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"yığmaq üçün nömrə"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Oxudun və ya dayandırın"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Spikerfonu aktiv və ya deaktiv edin"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Oxutma pozisiyası axtarın"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Oxutma reytinqini azaldın"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Oxutma reytinqini artırın"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Çağrı Tarixçəsi"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Daha çox seçim"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"nömrə yığımı paneli"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Yalnız gedəni göstərin"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Yalnız gələni göstərin"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Yalnız buraxılmışı göstərin"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Yalnız səsli mesajları göstərin"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Bütün zəngləri göstərin"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2 saniyəlik pauza əlavə edin"</string>
- <string name="add_wait" msgid="3360818652790319634">"Gözləmə əlavə edin"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Ayarlar"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Yeni kontakt"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Bütün kontaktlar"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Zəng detalları"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Təfərrüatlar mövcud deyil"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Toxunma ton klaviaturasını istifadə edin"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Davam edən zəngə qayıdın"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Zəng əlavə edin"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Gələn zəng"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Gedən zəng"</string>
- <string name="type_missed" msgid="2720502601640509542">"Buraxılmış zəng"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Gələn video zəng"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Gedən video zəng"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Buraxılmış video zəng"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Səsli poçt"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Rədd edilmiş çağrı"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blok edilmiş çağrı"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Gələn zənglər"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Səsli məktubu oxudun"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> adlı kontakta baxın"</string>
- <string name="description_call" msgid="3443678121983852666">"Zəng edin: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> üçün kontakt detalları"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> zəng."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Video çağrı."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g> adlı şəxsə SMS göndər"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Eşidilməmiş səsli mesaj"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Səs axtarışına başlayın"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> zəng edin"</string>
- <string name="unknown" msgid="740067747858270469">"Naməlum"</string>
- <string name="voicemail" msgid="3851469869202611441">"Səsli poçt"</string>
- <string name="private_num" msgid="6374339738119166953">"Şəxsi nömrə"</string>
- <string name="payphone" msgid="7726415831153618726">"Taksofon"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> san"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> dəq <xliff:g id="SECONDS">%s</xliff:g> san"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> tarixində <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Bu nömrəyə zəng etmək mümkün deyil"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Səsli poçtu ayarlamaq üçün Menyu, sonra isə &gt; Ayarlara daxil olun."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Səsli poçta zəng etmək üçün Təyyarə rejimini söndürün."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Yüklənir…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"SIM kartdan yüklənir..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM kart kontaktları"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Əlaqələr proqramı mövcud deyil"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Səsli axtarış mövcud deyil"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Telefon tətbiqi deaktiv edildiyinə görə telefon zəngi etmək mümkün deyil."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Bu cihazda onun üçün heç bir proqram yoxdur"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Kontakt axtarın"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Nömrə əlavə edin və ya kontaktlarda axtarın"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Zəng tarixçəniz boşdur"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Zəng edin"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Buraxılmış heç bir zənginiz yoxdur."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Səsli poçt qutunuz boşdur."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Səsli poçt arxiviliniz boşdur."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Yalnız seçilmişləri göstər"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Zəng Tarixçəsi"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Səsli Poçt Arxivi"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Bütün"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Buraxılmış"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Səsli poçt"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Yeni, sadələşdirilmiş bloklama"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Sizi daha yaxşı qorumaq üçün, Phone blok etmənin işləmə formasını dəyişməlidir. Bloklanmış nömrələriniz indi zəng və mətnləri dayandıracaq və digər tətbiqlər ilə paylaşıla bilər."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"İcazə verin"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> nömrəsi blok edilsin?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Bu nömrədən zənglər blok ediləcək və səsli məktublar avtomatik silinəcək."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Bu nömrədən zənglər bağlanacaq, amma zəng edən səsli zəng qoya bilər."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Bu nömrədən daha zəng və ya mesaj almayacaqsınız."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOK"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> nömrəsi blokdan çıxarılsın?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"BLOKDAN ÇIXARIN"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Sürətli nömrə yığımı"</string>
- <string name="tab_history" msgid="2563144697322434940">"Zəng Tarixçəsi"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontaktlar"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Səsli poçt"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Seçilmişlərdən silindi"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Geri qaytar"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> nömrəsinə zəng edin"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Yeni kontakt yaradın"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Kontakta əlavə edin"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS göndərin"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Video zəng edin"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Nömrəni blok edin"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> yeni buraxılmış zəng"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Hələ sürətli zəng siyahınızda hec kim yoxdur"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Sevimlilərə əlavə edin"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Hələ heç bir kontaktınız yoxdur"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Kontakt əlavə edin"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Bütün nömrələri görmək üçün təsvirə toxunun və ya yenidən qaydaya salmaq üçün toxunun və saxlayın"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Yığışdır"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Video zəng"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Mesaj göndərin"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Zəng detalları"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> nömrəsinə zəng edin"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Buraxılmış zənglər: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Zəngləri cavablandırıb: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> hesabına gələn oxunmamış səsli mesaj."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> hesabına gələn səsli mesaj."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Zəng et: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"Zəngdədir: <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> vasitəsilə"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> vasitəsilə"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> üzərində, <xliff:g id="NUMBER">%2$s</xliff:g> vasitəsilə"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> <xliff:g id="NUMBER">%2$s</xliff:g> vasitəsilə"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Çağrı"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Çağrı <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> video zəng edin."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> tərəfdən səsli mesajı dinləyin"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> nömrəsindən gələn səsli mesajı oxudun"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> nömrəsindən gələn səsli mesajı durdurun"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> nömrəsindən gələn səsli mesajı silin"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> yeni səsli mesaj</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> yeni səsli mesaj</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> üçün kontakt yaradın"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Mövcud kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g> əlavə edin"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> üçün detalları çağırın"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Zəng tarixçəsindən silindi"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Bu gün"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Dünən"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Keçmi"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Zənglər siyahısı"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Dinamiki aktiv et."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Dinamiki deaktiv et."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Daha sürətlə oxut."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Daha yavaş oxut."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Oxunuşu başlat və ya durdur"</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Ekran seçimləri"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Səslər və vibrasiya"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Giriş imkanı"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Telefon zəng səsi"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Həmçinin zənglər üçün vibrasiya olsun"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Yığım tonları"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Yığım paneli ton uzunluğu"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"Uzun"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Qısa cavablar"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Zənglər"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Zəng blok edilir"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Çağrının blok edilməsi müvəqqəti olaraq deaktiv edilib"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Son 48 saat ərzində bu telefondan təcili yardım xidmətləri ilə əlaqə saxladığınız üçün Çağrı Bloklanması deaktiv edildi. 48 saatlıq müddət başa çatdıqda avtomatik olaraq yenidən aktiv ediləcək."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Nömrələri import edin"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Əvvəlcədən qeyd etdiyiniz bəzi zəng edənlər digər tətbiqlərin vasiyəsilə avtomatik olaraq səsli mesaja yönləndiriləcək."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Rəqəmlərə baxın"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"İmport"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Import alınmadı"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Səsli poçtu arxivləşdirmək uğursuz oldu."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Nömrəni blokdan çıxarın"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Nömrə əlavə edin"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Bu nömrələrdən zənglər blok ediləcək və səsli məktublar avtomatik silinəcək."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Bu nömrələrdən zənglər blok ediləcək, amma yenə də səsli məktub qoya bilərsiniz."</string>
- <string name="block_list" msgid="7760188925338078011">"Bloklanmış nömrələr"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> yanlışdır."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> artıq bloklanıb."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Çağrının bloklanması 48 saatlıq deaktiv edildi"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Təcili zəng olduğu üçün deaktiv edildi."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Hesabların çağrılması"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Aktiv edin"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"İcazələri quraşdırın"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Sürətli yığımı aktivləşdirmək üçün, Kontakt icazələrini aktivləşdirin."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Çağrı jurnalınızı görmək üçün Telefon icazəsini aktivləşdirin."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Kontaktlarınızı görmək üçün Kontakt icazəsini aktivləşdirin."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Səsli poçta daxil olmaq üçün, Telefon icazəsini aktivləşdirin."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Kontaktlarınızı axtarmaq üçün, Kontakt icazələrini aktiv edin."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Zəng etmək üçün, Telefon icazəsini aktivləşdirin."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Telefon tətbiqinin sistem ayarlarına yazmaq icazəsi yoxdur."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Bloklanıb"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> aktivdir"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Spamı blok edin/bildirin"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blok edin"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Spam deyil"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Blokdan çıxarın"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> nömrəsi blok edilsin?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Bu nömrədən gələcək zəng və səsli mesajlar blok ediləcək."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Zəngi spam olaraq bildirin"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Bu nömrədən gələcək zəng və səsli mesajlar blok ediləcək."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> nömrəsi blokdan çıxarılsın?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Bu nömrə blokdan çıxarılacaq və spam kimi qeyd olunmayacaq. Gələcək zəng və səsli məktublar spam kimi müəyyən edilməyəcək."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> ağ siyahıya salınsın?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Ağ siyahı"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Bu nömrədən gələn zəng və səsli mesaj spam kimi müəyyən edilməyəcək. Bu nömr\\ spam kimi bildirilməyəcək."</string>
-</resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index 53fe1e277..000000000
--- a/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,290 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Numerička tastatura telefona"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Istorija poziva"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Prijavi netačan broj"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopiraj broj"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopiraj transkripciju"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blokiraj broj"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> je blokiran"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Deblokiraj broj"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> je deblokiran"</string>
- <string name="block_number_undo" msgid="591338370336724156">"OPOZOVI"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Izbriši"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Izmeni broj pre poziva"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Obriši istoriju poziva"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Izbriši govornu poruku"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arhiviraj govornu poštu"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Deli govornu poruku"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Gov. pošta je izbrisana"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Govorna pošta je arhivirana"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"OPOZOVI"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ARHIVA"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Želite da obrišete istoriju poziva?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Ovo će izbrisati sve pozive iz istorije"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Briše se istorija poziva…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Propušten poziv"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Propušten poziv za Work"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Propušteni pozivi"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"Broj propuštenih poziva: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Uzvrati poziv"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Pošalji SMS"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> govorna poruka </item>
- <item quantity="few"> <xliff:g id="COUNT">%1$d</xliff:g> govorne poruke </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> govornih poruka </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Pusti"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nova govorna poruka od <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Puštanje govorne pošte nije uspelo"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Govorna pošta se učitava…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Govorna pošta se arhivira…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Učitavanje govorne pošte nije uspelo"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Samo pozivi sa govornom poštom"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Samo dolazni pozivi"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Samo odlazni pozivi"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Samo propušteni pozivi"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Vizuelna govorna pošta"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Pregledajte i slušajte govornu poštu bez pozivanja broja. Možda će biti naplaćeni troškovi za prenos podataka."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Podešavanja"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Ažuriranja govorne pošte nisu dostupna"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Imate novu govornu poštu. Trenutno ne može da se učita."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Podesite govornu poštu"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio nije dostupan"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Podesi"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Zovi govornu poštu"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Izbor broja"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Izbor broja"</string>
- <string name="make_primary" msgid="5829291915305113983">"Zapamti ovaj izbor"</string>
- <string name="description_search_button" msgid="3660807558587384889">"pretraži"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"biranje"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"broj za biranje"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Pokretanje ili zaustavljanje reprodukcije"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Uključivanje ili isključivanje spikerfona"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Traženje pozicije u reprodukciji"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Smanjivanje brzine reprodukcije"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Povećavanje brzine reprodukcije"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Istorija poziva"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Još opcija"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"numerička tastatura"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Prikaži samo odlazne"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Prikaži samo dolazne"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Prikaži samo propuštene"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Prikaži samo govorne poruke"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Prikaži sve pozive"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Dodaj pauzu od 2 sekunde"</string>
- <string name="add_wait" msgid="3360818652790319634">"Dodaj čekanje"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Podešavanja"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Novi kontakt"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Svi kontakti"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Detalji poziva"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Detalji nisu dostupni"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Upotrebite brojčanik za tonsko biranje"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Vrati se na poziv koji je u toku"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Dodaj poziv"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Dolazni poziv"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Odlazni poziv"</string>
- <string name="type_missed" msgid="2720502601640509542">"Propušten poziv"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Dolazni video poziv"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Odlazni video poziv"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Propušten video poziv"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Govorna pošta"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Odbijen poziv"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blokiran poziv"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Dolazni pozivi"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Puštanje govorne pošte"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Prikaži kontakt <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Pozovi korisnika <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Detalji o kontaktu za <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> poziva."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Video poziv."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Slanje SMS-a za <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Nepreslušana govorna pošta"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Pokretanje glasovne pretrage"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Pozovi <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Nepoznato"</string>
- <string name="voicemail" msgid="3851469869202611441">"Govorna pošta"</string>
- <string name="private_num" msgid="6374339738119166953">"Privatan broj"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefonska govornica"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> sek"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> sek"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> u <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Nije moguće pozvati ovaj broj"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Da biste podesili govornu poštu, idite u Meni &gt; Podešavanja."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Da biste pozvali govornu poštu, prvo isključite režim aviona."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Učitava se…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Učitava se sa SIM kartice…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"Kontakti na SIM kartici"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Nema dostupne aplikacije za kontakte"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Glasovna pretraga nije dostupna"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Nije moguće uputiti telefonski poziv jer je aplikacija Telefon onemogućena."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Na ovom uređaju nema aplikacija za to"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Pretražite kontakte"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Dodajte broj ili pretražite kontakte"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Istorija poziva je prazna"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Pozovi"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Nemate nijedan propušten poziv."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Prijemno sanduče govorne pošte je prazno."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Arhiva govorne pošte je prazna."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Prikaži samo omiljene"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Istorija poziva"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arhiva govorne pošte"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Svi"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Propušteni"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Govorna pošta"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Novo, jednostavnije blokiranje"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Telefon treba da promeni način na koji blokiranje funkcioniše da bi vam pružio bolju zaštitu. Sa blokiranih brojeva više nećete primati ni pozive ni SMS-ove, ali možete da ih delite sa drugim aplikacijama."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Dozvoli"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Želite li da blokirate <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Pozivi sa ovog broja će biti blokirani i poruke govorne pošte će se automatski brisati."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Pozivi sa ovog broja će biti blokirani, ali pozivalac i dalje može da vam ostavlja poruke govorne pošte."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Više nećete primati pozive ni SMS-ove sa ovog broja."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOKIRAJ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Želite li da deblokirate <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DEBLOKIRAJ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Brzo biranje"</string>
- <string name="tab_history" msgid="2563144697322434940">"Istorija poziva"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontakti"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Govorna pošta"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Uklonjeno je iz omiljenih"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Opozovi"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Pozovi <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Napravi novi kontakt"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Dodaj u kontakt"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Pošalji SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Uputi video poziv"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blokiraj broj"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"Novih propuštenih poziva: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Nemate nijedan kontakt na brzom biranju"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Dodaj omiljen kontakt"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Još uvek nemate nijedan kontakt"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Dodaj kontakt"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Dodirnite sliku da biste videli sve brojeve ili dodirnite i zadržite da biste promenili raspored"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Ukloni"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Video poziv"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Pošalji poruku"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Detalji poziva"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Pozovi <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Propušteni poziv: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Primljeni poziv: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Nepročitana govorna poruka od <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Govorna poruka od <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Pozvali ste: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"na nalogu <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"preko <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"preko <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"na <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, preko <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> preko <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Pozovi"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Pozovi <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Uputite video poziv kontaktu <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Pusti govornu poštu od <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Reprodukuj govornu poštu kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pauziraj govornu poštu kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Izbriši govornu poštu kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> nova poruka govorne pošte</item>
- <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> nove poruke govorne pošte</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> novih poruka govorne pošte</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Napravite kontakt za <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Dodajte <xliff:g id="NAMEORNUMBER">^1</xliff:g> postojećem kontaktu"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Detalji poziva za <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Izbrisano iz istorije poziva"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Danas"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Juče"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Stariji"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Lista poziva"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Uključite zvučnik."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Isključite zvučnik."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Brža reprodukcija."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Sporija reprodukcija."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Započnite ili pauzirajte reprodukciju."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Opcije prikaza"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Zvuci i vibracija"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Pristupačnost"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Zvuk zvona telefona"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibriraj i za pozive"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Zvuci numeričke tastature"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Trajanje tonova numeričke tastature"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normalno"</item>
- <item msgid="6177579030803486015">"Dugačko"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Brzi odgovori"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Pozivi"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Blokiranje poziva"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Blokiranje poziva je privremeno isključeno"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Blokiranje poziva je onemogućeno zato što ste kontaktirali službe za pomoć u hitnim slučajevima sa ovog telefona u poslednjih 48 sati. Automatski će biti ponovo omogućeno kada istekne period od 48 sati."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Uvezi brojeve"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Ranije ste označili neke pozivaoce koje automatski treba preusmeriti na govornu poštu preko drugih aplikacija."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Prikaži brojeve"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Uvezi"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Uvoz nije uspeo"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Arhiviranje govorne pošte nije uspelo."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Deblokiraj broj"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Dodaj broj"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Pozivi sa ovih brojeva će biti blokirani i poruke govorne pošte će se automatski brisati."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Pozivi sa ovih brojeva će biti blokirani, ali pozivaoci sa ovih brojeva će i dalje moći da vam ostavljaju poruke govorne pošte."</string>
- <string name="block_list" msgid="7760188925338078011">"Blokirani brojevi"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> je nevažeći."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> je već blokiran."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Blokiranje poziva je onemogućeno na 48 sati"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Onemogućeno je zato što je upućen hitan poziv."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Nalozi za pozivanje"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Uključi"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Podesi dozvole"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Da biste omogućili brzo biranje, uključite dozvolu za Kontakte."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Da biste videli evidenciju poziva, uključite dozvolu za Telefon."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Da biste videli kontakte, uključite dozvolu za Kontakte."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Da biste pristupili govornoj pošti, uključite dozvolu za Telefon."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Da biste pretražili kontakte, uključite dozvole za Kontakte."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Da biste uputili poziv, uključite dozvolu za Telefon."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Aplikacija Telefon nema dozvolu za upisivanje u sistemska podešavanja."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Blokiran"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> je aktivan"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blokiraj/prijavi kao nepoželjan"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blokiraj"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Nije nepoželjan"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Deblokiraj"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Nepoželjan"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Želite da blokirate <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Blokiraćemo buduće pozive i govorne poruke sa ovog broja."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Prijavi poziv kao nepoželjan"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Blokiraćemo buduće pozive i govorne poruke sa ovog broja. Prijavićemo ovaj poziv kao nepoželjan."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Želite da deblokirate <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Deblokiraćemo broj i prijaviti da nije nepoželjan, budući pozivi i govorne poruke neće biti nepoželjni"</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Želite da stavite <xliff:g id="NUMBER">%1$s</xliff:g> na belu listu?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Stavi na belu listu"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Budući pozivi i govorne poruke sa ovog broja neće biti nepoželjni. Prijavićemo da on nije nepoželjan."</string>
-</resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
deleted file mode 100644
index 4a500ac40..000000000
--- a/res/values-be/strings.xml
+++ /dev/null
@@ -1,292 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Тэлефон"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Тэлефон"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Панэль набору тэлефона"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Тэлефон"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Гісторыя выклікаў"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Паведаміць аб недакладным нумары"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Скапіраваць нумар"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Капіраваць транскрыпцыю"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Заблакіраваць нумар"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> заблакіраваны"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Разблакіраваць нумар"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> разблакіраваны"</string>
- <string name="block_number_undo" msgid="591338370336724156">"АДРАБІЦЬ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Выдаліць"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Змяніць нумар перад тым, як тэлефанаваць"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Ачысціць гісторыю выклікаў"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Выдаліць галас. паведамленне"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Архіваванне галас.паведамлення"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Абагуліць галасавую пошту"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Павед.галасавой пошты выдалена"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Галас. паведамленне архівавана"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"АДРАБІЦЬ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ПЕРАЙСЦІ Ў АРХІЎ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Ачысціць гісторыю выклікаў?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Гэта выдаліць усе выклікі з вашай гісторыі"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Ачыстка гісторыі выклікаў…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Патэлефанаваць"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Прапушчаны выклік"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Прапушчаны выклік па працы"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Прапушчаныя выклікі"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"Прапушчаных выклікаў: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Перазваніць"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Паведамленне"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> Паведамленне галасавой пошты </item>
- <item quantity="few"> <xliff:g id="COUNT">%1$d</xliff:g> Паведамленні галасавой пошты </item>
- <item quantity="many"> <xliff:g id="COUNT">%1$d</xliff:g> Паведамленняў галасавой пошты </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Паведамлення галасавой пошты </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Прайграць"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Новае паведамл. ад <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Не ўдалося прайграць галасавую пошту"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Загрузка галасавой пошты…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Архіваванне галасавой пошты..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Не ўдалося загрузіць галасавую пошту"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Толькi выклiкі з галасавой поштай"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Толькi ўваходныя выклiкi"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Толькi выходныя выклiкi"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Толькі прапушчаныя выклiкi"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Візуальная галасавая пошта"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Праглядайце і праслухоўвайце свае паведамленні галасавой пошты без неабходнасці набіраць нумар. Могуць спаганяцца плацяжы за перадачу даных."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Налады"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Абнаўленні галасавой пошты недаступныя"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Чакаецца новае галас.паведамленне. Немагчыма загрузіць яго зараз"</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Наладзьце сваю галасавую пошту"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Аўдыя недаступнае"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Стварыць"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Выкл. гал. пошту"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"( <xliff:g id="COUNT">%1$d</xliff:g> ) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Выбар нумару"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Выбар нумару"</string>
- <string name="make_primary" msgid="5829291915305113983">"Запомніць гэты выбар"</string>
- <string name="description_search_button" msgid="3660807558587384889">"пошук"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"набор"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"нумар для набору"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Пачаць ці спыніць прайграванне"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Уключыць ці адключыць гучную сувязь"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Шукаць становішча прайгравання"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Панізіць хуткасць прайгравання"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Павялічыць хуткасць прайгравання"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Гісторыя выклікаў"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Дадатковыя параметры"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"панэль набору"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Паказаць толькі выходныя"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Паказаць толькі ўваходныя"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Паказаць толькі прапушчаныя"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Паказаць толькі галас. пошту"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Паказаць усе выклікі"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Дадаць 2-секундную паўзу"</string>
- <string name="add_wait" msgid="3360818652790319634">"Дадаць чаканне"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Налады"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Новы кантакт"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Усе кантакты"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Падрабязнасці выкліку"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Падрабязнасцей няма"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Выкарыстанне тонавай клавіятуры"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Звярнуцца да бягучага выкліку"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Дадаць выклік"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Уваходны выклік"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Выходны выклік"</string>
- <string name="type_missed" msgid="2720502601640509542">"Прапушчаны выклік"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Уваходны відэавыклік"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Выходны відэавыклік"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Прапушчаны відэавыклік"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Галасавая пошта"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Адхілены выклік"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Заблакіраваны выклік"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Уваходныя выклікі"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Прайграць паведамленне галасавой пошты"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Прагледзець кантакт <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Выклік карыстальнiка <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Падрабязнасці кантакту для <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"Выклікаў: <xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Відэавыклік."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Адправіць SMS абаненту <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Непраслуханая галасавая пошта"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Пачаць галасавы пошук"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Выклікаць <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Невядомы"</string>
- <string name="voicemail" msgid="3851469869202611441">"Галасавая пошта"</string>
- <string name="private_num" msgid="6374339738119166953">"Прыватны нумар"</string>
- <string name="payphone" msgid="7726415831153618726">"Таксафон"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> с"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> хв <xliff:g id="SECONDS">%s</xliff:g> с"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> у <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Немагчыма выклікаць гэты нумар"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Каб наладзіць галасавую пошту, націсніце \"Меню\" i перайдзiце ў налады."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Каб зрабiць выклік галасавой пошты, спачатку адключыце рэжым палёту."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Загрузка..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Загрузка з SIM-карты..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Кантакты SIM-карты"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Праграмы для аперацый з кантактамі няма"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Галасавы пошук недаступны"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Немагчыма зрабіць выклік па тэлефоне, таму што праграма Тэлефон была адключана."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"На прыладзе няма праграмы для гэтага"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Пошук у кантактах"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Дадайце нумар ці шукайце ў кантактах"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Ваша гісторыя выклікаў пустая"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Зрабіць выклік"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"У вас няма прапушчаных выклікаў."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Ваша галасавая пошта пустая."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Ваш архіў галасавой пошты пусты."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Паказаць толькі ўпадабаныя"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Гісторыя выклікаў"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Архіў галасавой пошты"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Усе"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Прапушчаныя"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Галасавая пошта"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Новае спрошчанае блакіраванне"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Каб лепш вас абараняць, Тэлефон павінен змяніць спосаб блакіроўкі. Зараз вашы заблакіраваныя нумары не могуць выкарыстоўвацца для выклікаў і перадачы тэкставых паведамленняў і могуць выкарыстоўвацца сумесна з іншымі праграмамі."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Дазволіць"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Заблакіраваць <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Выклікі з гэтага нумара будуць блакіравацца, а паведамленні галасавой пошты - аўтаматычна выдаляцца."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Выклікі з гэтага нумара будуць блакіравацца, але абанент усё яшчэ зможа пакінуць вам паведамленне галасавой пошты."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Вы больш не будзеце атрымліваць выклікі або SMS з гэтага нумара."</string>
- <string name="block_number_ok" msgid="770551992296781873">"БЛАКІРАВАЦЬ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Разблакіраваць <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"РАЗБЛАКІРАВАЦЬ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Хуткі набор"</string>
- <string name="tab_history" msgid="2563144697322434940">"Гісторыя выклікаў"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Кантакты"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Галасавая пошта"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Выдалена з выбранага"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Адрабіць"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Выклікаць <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Стварыць новы кантакт"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Дадаць у кантакт"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Адправiць SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Зрабіць відэавыклік"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Заблакіраваць нумар"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"Новых прапушчаных выклікаў: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"У вас пакуль нікога няма на хуткім наборы"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Дадаць улюбёны"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"У вас пакуль няма ніякіх кантактаў"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Дадаць кантакт"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Дакраніцеся да відарыса, каб пабачыць усе нумары, ці націсніце і ўтрымлівайце, каб змяніць іх парадак"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Выдаліць"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Відэавыклік"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Адправiць паведамленне"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Падрабязнасці выкліку"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Выклікаць <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Прапушчаны выклік ад <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Адказаны выклік ад <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Непрачытанае паведамленне галасавой пошты ад <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Паведамленне галасавой пошты ад <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Выклік абаненту <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"на <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"праз <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"праз <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"на <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, праз <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> праз <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Выклікаць"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Выклікаць <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Відэавыклік ад <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Праслухаць галасавое паведамленне ад <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Прайграць галасавое паведамленне ад <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Прыпыніць галасавое паведамленне ад <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Выдаліць галасавое паведамленне ад <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> новае паведамленне галасавой пошты</item>
- <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> новыя паведамленні галасавой пошты</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> новых паведамленняў галасавой пошты</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> новага паведамлення галасавой пошты</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Стварыць кантакт для <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Дадаць <xliff:g id="NAMEORNUMBER">^1</xliff:g> да існуючага кантакту"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Падрабязнасці кантакту для <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Выдалена з гісторыі выклікаў."</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Сёння"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Учора"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Больш старыя"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Спіс выклікаў"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Уключыць дынамік."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Адключыць дынамік."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Прайграваць хутчэй."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Прайграваць павольней."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Пачаць ці прыпыніць прайграванне."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Параметры адлюстравання"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Гукі і вібрацыя"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Спецыяльныя магчымасці"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Рынгтон тэлефона"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Уключыць вібрацыю для выклікаў"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Гукі панэлі набору"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Даўжыня гукаў панэлі набору"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Звычайны"</item>
- <item msgid="6177579030803486015">"Доўгі"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Хуткія адказы"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Выклікі"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Блакіраванне выклікаў"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Блакіроўка выклікаў часова адключана"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Блакіроўка выклікаў будзе адключана, таму што вы выклікалі экстранныя службы з гэтага тэлефона на працягу апошніх 48 гадзін. Яна будзе аўтаматычна адноўлена пасля заканчэння перыяду ў 48 гадзін."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Імпартаваць нумары"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Вы пазначалі праз іншыя праграмы некаторых абанентаў, якіх трэба аўтаматычна адпраўляць у галасавую пошту ."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Прагледзець нумары"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Імпартаваць"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Імпарт не ўдаўся"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Не ўдалося архіваваць галасавую пошту."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Разблакіраваць нумар"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Дадаць нумар"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Выклікі з гэтых нумароў будуць блакіравацца, а паведамленні галасавой пошты - аўтаматычна выдаляцца."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Выклікі з гэтых нумароў будуць блакіравацца, але з іх яшчэ можна пакінуць вам паведамленні галасавой пошты."</string>
- <string name="block_list" msgid="7760188925338078011">"Заблакіраваныя нумары"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> несапраўдны."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> ужо заблакіраваны."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Блакіроўка выклікаў адключана на 48 гадзін"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Адключана, таму што быў зроблены экстранны выклік."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Уліковыя запісы для выклікаў"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Уключыць"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Задаць дазволы"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Каб уключыць хуткі набор, уключыце дазвол для Кантактаў."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Каб прагледзець свой журнал выклікаў, уключыце дазволы Тэлефона."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Каб бачыць свае кантакты, уключыце дазвол для Кантактаў."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Каб атрымаць доступ да галасавой пошты, уключыце дазвол для Тэлефона."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Каб шукаць свае кантакты, уключыце дазвол для Кантактаў."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Каб зрабіць выклік, уключыце дазвол для Тэлефона."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Праграма Тэлефон не мае дазволу на запіс у налады сістэмы."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Заблакiраваны"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"Актыўная размова з <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Заблакір./паведаміць пра спам"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Заблакiраваць"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Не спам"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Разблакiраваць"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Спам"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Заблакіраваць <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Будучыя выклікі i паведамленнi галасавой пошты з гэтага нумара будуць блакіравацца."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Заявіць, што выклік з\'яўляецца спамам"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Будучыя выклікі i паведамленнi галасавой пошты з гэтага нумара будуць блакіравацца. Можна заявіць, што гэты выклiк з\'яўляецца спамам."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Разблакіраваць <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Гэты нумар будзе разблак. і заяўлены як няспамерскі. Буд.выкл. i гал.пошта не будуць вызн. як спам."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Дадаць <xliff:g id="NUMBER">%1$s</xliff:g> у белы спіс?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Белы спіс"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Буд.выкл. i галас.пошта з гэтага нумара не будуць вызн.як спам. Нумар будзе заяўлены як няспамерскі."</string>
-</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
deleted file mode 100644
index 63c6b5ace..000000000
--- a/res/values-bg/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Телефон"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Телефон"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Клавиатура за набиране"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Телефон"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"История на обажданията"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Сигнал за неправилен номер"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Копиране на номера"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Копиране на преписа"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Блокиране на номера"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Блокирахте <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Отблокиране на номера"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Отблокирахте <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ОТМЯНА"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Изтриване"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Редактиране на номера преди обаждане"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Изчистване на историята"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Изтриване на гласова поща"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Архивиране на гласовата поща"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Споделяне на гласова поща"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Гл. поща е изтрита"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Гласовата поща бе архивирана"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ОТМЯНА"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"КЪМ АРХИВА"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Да се изчисти ли историята на обажд.?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Така ще се изтрият всички обаждания от историята ви"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Историята на обажд. се изчиства…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Телефон"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Пропуснато обаждане"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Пропуснато служебно обаждане"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Пропуснати обаждания"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> пропуснати обаждания"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Обратно обаждане"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Съобщение"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> гласови съобщения </item>
- <item quantity="one">Гласово съобщение</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Пускане"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Нова гласова поща от <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Гл. поща не можа да се възпроизведе"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Гласовата поща се зарежда…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Гласовата поща се архивира…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Гласовата поща не можа да се зареди"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Само обаждания с гласова поща"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Само входящи обаждания"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Само изходящи обаждания"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Само пропуснати обаждания"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Визуална гласова поща"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Преглеждайте и прослушвайте гласовата си поща, без да се налага да се обаждате на номер. Може да бъдете таксувани за данни."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Настройки"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Няма налични актуализации на гласовата поща"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Има нова гласова поща, която не може да се зареди в момента."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Настройте гласовата си поща"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Няма звук"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Настройка"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Чуйте гл. си поща"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Избиране на номер"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Избиране на номер"</string>
- <string name="make_primary" msgid="5829291915305113983">"Запомняне на този избор"</string>
- <string name="description_search_button" msgid="3660807558587384889">"търсене"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"набиране"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"номер за набиране"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Пускане или спиране на възпроизвеждането"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Включване или изключване на високоговорителя"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Търсене на позиция за възпроизвеждане"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Намаляване на скоростта на възпроизвеждане"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Увеличаване на скоростта на възпроизвеждане"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"История на обажданията"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Още опции"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"клавиатура за набиране"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Показване само на изходящите"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Показване само на входящите"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Показване само на пропуснатите"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Показване само на гл. поща"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Показване на всички обаждания"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Добавяне на пауза от 2 сек"</string>
- <string name="add_wait" msgid="3360818652790319634">"Добавяне на изчакване"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Настройки"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Нов контакт"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Всички контакти"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Подробности за обаждане"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Данните не са налице"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Използване на тонова клавиатура"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Назад към текущото обаждане"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Добавяне на обаждане"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Вх. обаждане"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Изх. обаждане"</string>
- <string name="type_missed" msgid="2720502601640509542">"Пропуснато обаждане"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Входящо видеообаждане"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Изходящо видеообаждане"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Пропуснато видеообаждане"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Гласова поща"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Отхвърлено обаждане"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Блокирано обаждане"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Вх. обаждания"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Пускане на гласовата поща"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Преглед на контактa <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Обаждане до <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Подробности за контакта за <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> обаждания."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Видеообаждане."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Изпращане на SMS до <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Непрослушана гласова поща"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Стартиране на гласово търсене"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Обаждане на <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Неизвестен"</string>
- <string name="voicemail" msgid="3851469869202611441">"Гласова поща"</string>
- <string name="private_num" msgid="6374339738119166953">"Частен номер"</string>
- <string name="payphone" msgid="7726415831153618726">"Обществен телефон"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> сек"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> мин <xliff:g id="SECONDS">%s</xliff:g> сек"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> в <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Не можете да се обадите на този номер"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"За да настроите гласовата поща, отворете „Меню“ &gt; „Настройки“."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"За да чуете гласовата си поща, първо изключете самолетния режим."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Зарежда се…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Зарежда се от SIM карта..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Контакти от SIM карта"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Няма налично приложение за контакти"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Гласовото търсене не е налице"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Не може да се осъществи телефонно обаждане, защото приложението Телефон е деактивирано."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"На устройството няма приложение за това действие"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Търсене в контактите"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Доб. номер или потърс. контакт"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Историята на обажданията ви е празна"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Извършване на обаждане"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Нямате пропуснати обаждания."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Входящата ви гласова поща е празна."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Архивът на гласовата ви поща е празен."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Показване само на любимите"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"История на обажданията"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Архив на гласовата поща"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Всички"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Пропуснати"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Гл. поща"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Ново и лесно блокиране"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"За да ви защитава по-добре, Phone трябва да промени начина на работа на функцията за блокиране. Вече няма да получавате обаждания и текстови съобщения от блокираните номера. Възможно е тези номера да бъдат споделени с други приложения."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Разрешаване"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Да се блокира ли <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Обажданията от този номер ще бъдат блокирани, а гласовите съобщения – автоматично изтривани."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Обажданията от този номер ще бъдат блокирани, но обаждащият се пак може да е в състояние да ви оставя гласови съобщения."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Повече няма да получавате обаждания или SMS от този номер."</string>
- <string name="block_number_ok" msgid="770551992296781873">"БЛОКИРАНЕ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Да се отблокира ли <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ОТБЛОКИРАНЕ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Бързо набиране"</string>
- <string name="tab_history" msgid="2563144697322434940">"История на обажданията"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Контакти"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Гласова поща"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Премахнато от любимите"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Отмяна"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Обаждане на <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Създаване на нов контакт"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Добавяне към контакт"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Изпращане на SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Провеждане на видеообаждане"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Блокиране на номера"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> нови пропуснати обаждания"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Още нямате контакти за бързо набиране"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Добавяне на любим контакт"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Още нямате контакти"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Добавяне на контакт"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Докоснете изображението, за да видите всички номера, или го натиснете и задръжте за пренареждане"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Премахване"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Видеообаждане"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Изпращане на съобщение"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Подробности за обаждането"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Обаждане до <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Пропуснато обаждане от <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Прието обаждане от <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Непрочетено гласово съобщение от <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Гласово съобщение от <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Обаждане до <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"на <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"през <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"през <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"в/ъв <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, през <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> през <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Обаждане"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Обаждане на <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Видеообаждане до <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Прослушване на гласовата поща от <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Пускане на гласовата поща от <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Поставяне на пауза на гласовата поща от <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Изтриване на гласовата поща от <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> нови гласови съобщения</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> ново гласово съобщение</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Създаване на контакт за <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Добавяне на <xliff:g id="NAMEORNUMBER">^1</xliff:g> към съществуващ контакт"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Подробности за обаждането за <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Изтрито от историята на обажданията"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Днес"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Вчера"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"По-стари"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Списък с обаждания"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Включване на високоговорителя."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Изключване на високоговорителя."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"По-бързо възпроизвеждане."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"По-бавно възпроизвеждане."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Стартиране или поставяне на пауза на възпроизвеждането."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Опции за показване"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Звуци и вибриране"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Достъпност"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Мелодия на телефона"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Вибриране и при обаждания"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Звук при набиране"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Продължителност на звука при набиране"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Нормално"</item>
- <item msgid="6177579030803486015">"Продължително"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Бързи отговори"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Обаждания"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Блокиране на обажданията"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Блокир. на обажданията е временно изкл."</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Блокирането на обажданията е деактивирано, защото в рамките на последните 48 часа сте се свързали със спешните служби от този телефон. То автоматично ще бъде активирано отново, когато периодът от 48 часа изтече."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Импортиране на номерата"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"По-рано означихте обажданията от някои контакти автоматично да се прехвърлят към гласова поща чрез други приложения."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Преглед на номерата"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Импортиране"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Импортирането не бе успешно"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Неуспешно архивиране на гласовата поща."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Отблокиране на номера"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Добавяне на номер"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Обажданията от тези номера ще бъдат блокирани, а гласовите съобщения – автоматично изтривани."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Обажданията от тези номера ще бъдат блокирани, но обаждащите се от тях пак може да са в състояние да ви оставят гласови съобщения."</string>
- <string name="block_list" msgid="7760188925338078011">"Блокирани номера"</string>
- <string name="invalidNumber" msgid="619058581062192851">"Номер <xliff:g id="NUMBER">%1$s</xliff:g> е невалиден."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"Номер <xliff:g id="NUMBER">%1$s</xliff:g> вече е блокиран."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Блокирането на обажданията е деактивирано за 48 часа"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Деактивирано, тъй като бе извършено спешно обаждане."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Профили за обаждане"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Включване"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Задаване на разрешенията"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"За да активирате бързото набиране, включете разрешението за Контакти."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"За да видите списъка с обажданията си, включете разрешението за Телефон."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"За да видите контактите си, включете разрешението за Контакти."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"За да осъществите достъп до гласовата си поща, включете разрешението за Телефон."</string>
- <string name="permission_no_search" msgid="84152933267902056">"За да търсите в контактите си, включете разрешенията за тях."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"За да извършите обаждане, включете разрешението за Телефон."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Приложението Телефон няма разрешение да записва в системните настройки."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Блокирано"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> е активно"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Блокиране/сигнал за спам"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Блокиране"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Не е спам"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Отблокиране"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Спам"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Да се блокира ли <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Бъдещите обаждания и съобщения в гласовата поща от този номер ще бъдат блокирани."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Сигнал за спам за обаждането"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Обажданията и съобщ. в гласовата поща от номера ще бъдат блокирани. Обаждането ще се смята за спам."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Да се отблокира ли <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Номерът ще бъде отблокиран. Той, обажданията и съобщ. в гласовата поща от него няма да бъдат спам."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> да се постави ли в белия списък?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Бял списък"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Обажданията и съобщенията в гласовата поща от този номер, както и самият той, няма да се смятат за спам."</string>
-</resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
deleted file mode 100644
index c2273f834..000000000
--- a/res/values-bn/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"ফোন"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"ফোন"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"ফোন ডায়ালপ্যাড"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"ফোন"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"পুরোনো কলের তালিকা"</string>
- <string name="action_report_number" msgid="4635403959812186162">"ভুল নম্বর অভিযোগ করুন"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"নম্বর প্রতিলিপি করুন"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"ট্রান্সক্রিপশান প্রতিলিপি করুন"</string>
- <string name="action_block_number" msgid="1482657602262262134">"নম্বর অবরোধ করুন"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> অবরোধ করা হয়েছে"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"নম্বর অবরোধ মুক্ত করুন"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> অবরোধ মুক্ত করা হয়েছে"</string>
- <string name="block_number_undo" msgid="591338370336724156">"পূর্বাবস্থায় ফিরুন"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"মুছুন"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"কল করার আগে নম্বর সম্পাদনা করুন"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"পুরোনো কলের তালিকা সাফ করুন"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"ভয়েসমেল মুছুন"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"ভয়েসমেল সংরক্ষণাগারভুক্ত করুন"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"ভয়েসমেল শেয়ার করুন"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"ভয়েসমেল মোছা হয়েছে"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"ভয়েসমেল সংরক্ষণাগারভুক্ত"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"পূর্বাবস্থায় ফিরুন"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"সংরক্ষণাগারে যান"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"পুরোনো কলের তালিকা সাফ করবেন?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"এটি আপনার ইতিহাস থেকে সমস্ত কল মুছে দেবে"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"পুরোনো কলের তালিকা সাফ করা হচ্ছে…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"ফোন"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"মিস করা কল"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"কাজের কল মিস করেছেন"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"মিস করা কলগুলি"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>টি মিস করা কল"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"ঘুরিয়ে কল করুন"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"বার্তা"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g>টি ভয়েসমেল </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g>টি ভয়েসমেল </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"শুনুন"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> এর থেকে নতুন ভয়েসমেল"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"ভয়েসমেল প্লে করা যায়নি"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"ভয়েসমেল লোড করা হচ্ছে..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"ভয়েসমেল সংরক্ষণাগারভুক্ত করা হচ্ছে…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"ভয়েসমেল লোড করা যায়নি"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"কেবলমাত্র ভয়েসমেলের সাথে কলগুলি"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"কেবলমাত্র ইনকামিং কলগুলি"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"কেবলমাত্র আউটগোয়িং কলগুলি"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"কেবলমাত্র মিসড কলগুলি"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"ভিজ্যুয়াল ভয়েসমেল"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"কল করার একটি নম্বর ছাড়াই আপনার ভয়েসমেল দেখুন এবং শুনুন। ডেটা চার্জ প্রযোজ্য হতে পারে।"</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"সেটিংস"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"ভয়েসমেলের আপডেটগুলি অনুপলব্ধ"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"নতুন ভয়েসমেল অপেক্ষা করছে৷ এখনই লোড করা যাবে না৷"</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"আপনার ভয়েসমেল সেট আপ করুন"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"অডিও অনুপলব্ধ"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"সেট আপ"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"ভয়েসমেলে কল করুন"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"নম্বর বেছে নিন"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"নম্বর বেছে নিন"</string>
- <string name="make_primary" msgid="5829291915305113983">"এই পছন্দটিকে মনে রাখুন"</string>
- <string name="description_search_button" msgid="3660807558587384889">"অনুসন্ধান"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"ডায়াল করুন"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"ডায়াল করার জন্য নম্বর"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"প্লেব্যাক প্লে করুন বা থামান"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"স্পিকার চালু বা বন্ধ করুন"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"প্লেব্যাক অবস্থান খুঁজুন"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"প্লেব্যাকের হার হ্রাস করুন"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"প্লেব্যাকের হার বৃদ্ধি করুন"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"পুরোনো কলের তালিকা"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"আরো বিকল্প"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"ডায়াল প্যাড"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"কেবলমাত্র আউটগোয়িং দেখান"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"কেবলমাত্র ইনকামিং দেখান"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"কেবলমাত্র মিসড দেখান"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"কেবলমাত্র ভয়েসমেলগুলি দেখান"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"সমস্ত কল দেখান"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"২- সেকেন্ড বিরতি যোগ করুন"</string>
- <string name="add_wait" msgid="3360818652790319634">"অপেক্ষা যোগ করুন"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"সেটিংস"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"নতুন পরিচিতি"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"সকল পরিচিতি"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"কলের বিবরণ"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"বিশদ বিবরণ অনুপলব্ধ"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"স্পর্শ স্বর কীপ্যাড ব্যবহার করুন"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"প্রগতিতে থাকা কলে প্রত্যাবর্তন"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"কল যোগ করুন"</string>
- <string name="type_incoming" msgid="6502076603836088532">"ইনকামিং কল"</string>
- <string name="type_outgoing" msgid="343108709599392641">"আউটগোয়িং কল"</string>
- <string name="type_missed" msgid="2720502601640509542">"মিসড কল"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"ইনকামিং ভিডিও কল"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"আউটগোয়িং ভিডিও কল"</string>
- <string name="type_missed_video" msgid="954396897034220545">"মিস করা ভিডিও কল"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"ভয়েসমেল"</string>
- <string name="type_rejected" msgid="7783201828312472691">"অস্বীকৃত কল"</string>
- <string name="type_blocked" msgid="3521686227115330015">"অবরুদ্ধ কল"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"ইনকামিং কলগুলি"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"ভয়েসমেল প্লে করুন"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> পরিচিতি দেখুন"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g> কে কল করুন"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> পরিচিতির বিশদ বিবরণ"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>টি কল৷"</string>
- <string name="description_video_call" msgid="2933838090743214204">"ভিডিও কল।"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g> কে SMS পাঠান"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"না শোনা ভয়েসমেল"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"ভয়েস অনুসন্ধান শুরু করুন"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> এ কল করুন"</string>
- <string name="unknown" msgid="740067747858270469">"অজানা"</string>
- <string name="voicemail" msgid="3851469869202611441">"ভয়েসমেল"</string>
- <string name="private_num" msgid="6374339738119166953">"ব্যক্তিগত নম্বর"</string>
- <string name="payphone" msgid="7726415831153618726">"পে ফোন"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> সেকেন্ড"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> মিনিট <xliff:g id="SECONDS">%s</xliff:g> সেকেন্ড"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> তারিখে <xliff:g id="TIME">%2$s</xliff:g>\'টায়"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"এই নম্বরে কল করতে পারবেন না"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"ভয়েসমেল সেট আপ করতে, মেনু &gt; সেটিংস এ যান৷"</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"ভয়েসমেলে কল করতে, সবার আগে বিমানমোড বন্ধ করুন৷"</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"লোড হচ্ছে..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"সিম কার্ড থেকে লোড করা হচ্ছে…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"সিম কার্ডের পরিচিতিগুলি"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"কোনো পরিচিতি অ্যাপ্লিকেশান উপলব্ধ নয়"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"ভয়েস অনুসন্ধান অনুপলব্ধ"</string>
- <string name="call_not_available" msgid="8941576511946492225">"কোনো ফোন কল করা যাবে না কারণ ফোন অ্যাপ্লিকেশানটি অক্ষম করা হয়েছে৷"</string>
- <string name="activity_not_available" msgid="2287665636817987623">"এর জন্য এই ডিভাইসে কোনো অ্যাপ্লিকেশান নেই"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"পরিচিতিগুলি অনুসন্ধান করুন"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"সংখ্যা যোগ করুন বা পরিচিতিগুলি অনুসন্ধান করুন"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"আপনার পুরোনো কলের তালিকা খালি আছে"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"একটি কল করুন"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"আপনার কোনো মিসড কল নেই।"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"আপনার ভয়েসমেলের ইনবক্স খালি রয়েছে।"</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"আপনার ভয়েসমেলের সংরক্ষণাগারটি খালি।"</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"কেবলমাত্র পছন্দসইগুলি দেখান"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"পুরোনো কলের তালিকা"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"ভয়েসমেলের সংরক্ষাণাগার"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"সমস্ত"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"মিসড"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"ভয়েসমেল"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"নতুন, সরলীকৃত অবরুদ্ধ করার ব্যবস্থা"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"আপনাকে আরো ভালো সুরক্ষা দিতে, \'ফোন\' এর অবরুদ্ধ করার আচরণ পরিবর্তন করতে হবে৷ আপনার অবরুদ্ধ করা নম্বরগুলি থেকে আসা কলগুলি এবং পাঠ্যবার্তা উভয়ই থামানো হবে এবং অন্যান্য অ্যাপ্লিকেশানের সাথে শেয়ার করা হতে পারে৷"</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"অনুমতি দিন"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> অবরোধ করবেন?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"এই নম্বর থেকে আসা কলগুলি অবরোধ করা হবে এবং ভয়েসমেলগুলি স্বয়ংক্রিয়ভাবে মুছে ফেলা হবে৷"</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"এই নম্বর থেকে আসা কলগুলি অবরোধ করা হবে, কিন্তু কলার হয়ত এখনও আপনাকে ভয়েসমেলগুলি পাঠাতে পারবে৷"</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"আপনি এই নম্বর থেকে আর কল বা পাঠ্যবার্তাগুলি পাবেন না৷"</string>
- <string name="block_number_ok" msgid="770551992296781873">"অবরোধ করুন"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> অবরোধ মুক্ত করবেন?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"অবরোধ মুক্ত করুন"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"দ্রুত ডায়াল"</string>
- <string name="tab_history" msgid="2563144697322434940">"পুরোনো কলের তালিকা"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"পরিচিতিগুলি"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"ভয়েস মেল"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"পছন্দসই থেকে সরানো হয়েছে"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"পূর্বাবস্থায় ফিরুন"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> কে কল করুন"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"নতুন পরিচিতি তৈরি করুন"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"একটি পরিচিতিতে যোগ করুন"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS পাঠান"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"ভিডিও কল করুন"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"নম্বর অবরোধ করুন"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g>টি নতুন মিসড কল"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"আপনার স্পীড ডায়ালে এখনও পর্যন্ত কেউ নেই"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"আপনার পছন্দের একটি পরিচিতি যোগ করুন"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"আপনার এখনও পর্যন্ত কোনো পরিচিতি নেই"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"একটি পরিচিতি যোগ করুন"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"সমস্ত নম্বর দেখতে চিত্রে স্পর্শ করুন বা রেকর্ড করতে ধরে রাখুন"</string>
- <string name="remove_contact" msgid="1080555335283662961">"সরান"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"ভিডিও কল"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"একটি বার্তা পাঠান"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"কলের বিশদ বিবরণ"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> এর কল"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> থেকে মিস হওয়া কল৷"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> এর থেকে উত্তর দেওয়া কল৷"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> , <xliff:g id="TYPEORLOCATION">^2</xliff:g> , <xliff:g id="TIMEOFCALL">^3</xliff:g> , <xliff:g id="PHONEACCOUNT">^4</xliff:g> এর থেকে অপঠিত ভয়েসমেল৷"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> , <xliff:g id="TYPEORLOCATION">^2</xliff:g> , <xliff:g id="TIMEOFCALL">^3</xliff:g> , <xliff:g id="PHONEACCOUNT">^4</xliff:g> এর থেকে ভয়েসমেল৷"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> এ কল করুন৷"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> এ"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> এর মাধ্যমে"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> এর মাধ্যমে"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> এ, <xliff:g id="NUMBER">%2$s</xliff:g> এর মাধ্যমে"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="NUMBER">%2$s</xliff:g> এর মাধ্যমে <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"কল করুন"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> এর কল"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-এ ভিডিও কল করুন।"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> এর থেকে আসা ভয়েসমেল শোনা হয়েছে"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> এর থেকে ভয়েসমেল প্লে করুন"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> এর থেকে ভয়েসমেলে বিরাম দিন"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> এর থেকে ভয়েসমেল মুছুন"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g>টি নতুন ভয়েসমেল</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>টি নতুন ভয়েসমেল</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> এর জন্য পরিচিতি তৈরি করুন"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"বিদ্যমান পরিচিতিতে <xliff:g id="NAMEORNUMBER">^1</xliff:g> যোগ করুন"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> এর কলের বিবরণ"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"কলের ইতিহাস থেকে মোছা হয়েছে"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"আজ"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"গতকাল"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"পুরোনো"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"কলগুলির তালিকা"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"স্পিকার চালু করুন৷"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"স্পিকার বন্ধ করুন৷"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"দ্রুত প্লে করুন৷"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"ধীরে প্লে করুন৷"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"প্লেব্যাক শুরু করুন বা বিরতি দিন৷"</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"প্রদর্শনের বিকল্পগুলি"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"শব্দ এবং কম্পন"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"অ্যাক্সেসযোগ্যতা"</string>
- <string name="ringtone_title" msgid="760362035635084653">"ফোন রিংটোন"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"এছাড়াও কল এলে কম্পিত করুন"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"ডায়ালপ্যাড টোনগুলি"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"ডায়ালপ্যাড স্বরের দৈর্ঘ্য"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"স্বাভাবিক"</item>
- <item msgid="6177579030803486015">"দীর্ঘ"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"দ্রুত প্রতিক্রিয়াগুলি"</string>
- <string name="call_settings_label" msgid="313434211353070209">"কল"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"কল অবরোধ করা"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"কল অবরোধ সাময়িকভাবে বন্ধ রয়েছে"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"কল অবরোধ অক্ষম করা হয়েছে কারণ আপনি গত ২৮ ঘন্টার মধ্যে এই ফোন থেকে জরুরি পরিষেবায় যোগাযোগ করেছেন। ২৮ ঘন্টার সময়সীমা পেরিয়ে গেলেই এটি স্বয়ংক্রিয়ভাবে আবার সক্ষম হবে।"</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"নম্বরগুলি আমদানি করুন"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"আপনি আগে থেকেই অন্য অ্যাপ্লিকেশানগুলির মাধ্যমে স্বয়ংক্রিয়ভাবে ভয়েস মেল পাঠানোর জন্য কিছু কলারকে চিহ্নিত করেছেন৷"</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"নম্বরগুলি দেখুন"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"আমদানি করুন"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"আমদানি ব্যর্থ হয়েছে"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"ভয়েসমেল সংরক্ষণাগারভুক্ত করতে ব্যর্থ হয়েছে।"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"নম্বর অবরোধ মুক্ত করুন"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"একটি নম্বর যোগ করুন"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"এই নম্বরগুলি থেকে আসা কলগুলি অবরোধ করা হবে এবং ভয়েসমেলগুলি স্বয়ংক্রিয়ভাবে মুছে ফেলা হবে৷"</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"এই নম্বরগুলি থেকে আসা কলগুলি অবরোধ করা হবে, কিন্তু তারা হয়ত এখনও আপনাকে ভয়েসমেলগুলি পাঠাতে পারবে৷"</string>
- <string name="block_list" msgid="7760188925338078011">"অবরুদ্ধ নম্বরগুলি"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> অবৈধ৷"</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> ইতিমধ্যেই অববোধ করা রয়েছে৷"</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"৪৮ ঘন্টার জন্য কল অবরোধ করা অক্ষম করা হয়েছে"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"কোনো জরুরি কল করার কারণে অক্ষম করা হয়েছে৷"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"কলিং অ্যাকাউন্টগুলি"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"চালু করুন"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"অনুমতিগুলি সেট করুন"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"স্পীড ডায়াল সক্ষম করতে, পরিচিতিগুলির অনুমতি চালু করুন।"</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"আপনার কল লগ দেখতে, ফোনের অনুমতি চালু করুন।"</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"আপনার পরিচিতিগুলি দেখতে, পরিচিতিগুলির অনুমতি চালু করুন।"</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"আপনার ভয়েসমেল অ্যাক্সেস করতে, ফোনের অনুমতি চালু করুন।"</string>
- <string name="permission_no_search" msgid="84152933267902056">"আপনার পরিচিতিগুলি অনুসন্ধান করতে, পরিচিতির অনুমতিগুলি চালু করুন।"</string>
- <string name="permission_place_call" msgid="2101287782615887547">"একটি কল করতে, ফোনের অনুমতি চালু করুন।"</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"ফোনের অ্যাপ্লিকেশানকে সিস্টেম সেটিংসে লেখার অনুমতি দেওয়া হয়নি।"</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"অবরুদ্ধ"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> সক্রিয় আছে"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"অবরুদ্ধ করুন/স্প্যাম হিসাবে অভিযোগ করুন"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"অবরুদ্ধ করুন"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"স্প্যাম নয়"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"অবরোধ মুক্ত করুন"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"স্প্যাম"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> অবরোধ করবেন?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"এই নম্বর থেকে আসা ভবিষ্যৎ কল এবং ভয়েসমেলগুলি অবরোধ করা হবে৷"</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"কলটিকে স্প্যাম হিসাবে অভিযোগ করুন"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"এই নম্বর থেকে আসা ভবিষ্যৎ কল এবং ভয়েসমেলগুলি অবরোধ করা হবে৷ এই কলটিকে স্প্যাম হিসাবে প্রতিবেদন করা হবে৷"</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> অবরোধ মুক্ত করবেন?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"এই নম্বরটিকে অবরোধ মুক্ত করা হবে এবং \'স্প্যাম নয়\' হিসাবে প্রতিবেদ করা হবে৷ এই নম্বর থেকে আসা ভবিষ্যৎ কল এবং ভয়েসমেলগুলি স্প্যাম হিসাবে শনাক্ত করা হবে না৷"</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"পরিচ্ছন্ন তলিকায় <xliff:g id="NUMBER">%1$s</xliff:g> যোগ করবেন?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"পরিচ্ছন্ন তলিকা"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"এই নম্বর থেকে আসা ভবিষ্যৎ কল এবং ভয়েসমেলগুলিকে স্প্যাম হিসাবে শনাক্ত করা হবে না৷ এই কলটিকে \'স্প্যাম নয়\' হিসাবে প্রতিবেদন করা হবে৷"</string>
-</resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
deleted file mode 100644
index 4a53222c8..000000000
--- a/res/values-bs/strings.xml
+++ /dev/null
@@ -1,290 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Telefonska tipkovnica telefona"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Historija poziva"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Prijavi netačan broj"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopiraj broj"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopiraj transkripciju"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blokiraj broj"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> je blokiran"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Deblokiraj broj"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> je deblokiran"</string>
- <string name="block_number_undo" msgid="591338370336724156">"PONIŠTI"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Izbriši"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Uredi broj prije poziva"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Obriši historiju poziva"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Izbriši govornu poštu"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arhiviraj govornu poštu"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Podijeli govornu poštu"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Govorna pošta je izbrisana"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Govorna pošta arhivirana"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"PONIŠTI"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"IDI U ARH."</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Obrisati historiju poziva?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Ovo će izbrisati sve pozive iz historije"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Brisanje historije poziva u toku…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Propušteni poziv"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Propušteni poslovni poziv"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Propušteni pozivi"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"Propuštenih poziva: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Pozovi ovaj broj"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Poruka"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> Poruka govorne pošte </item>
- <item quantity="few"> <xliff:g id="COUNT">%1$d</xliff:g> Poruke govorne pošte </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Poruka govorne pošte </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Pokreni"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nova govorna pošta od <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Nije moguće pokrenuti govornu poštu"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Učitavanje govorne pošte…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Arhiviranje govorne pošte..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Nije moguće učitati govornu poštu"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Samo pozivi s govornom poštom"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Samo dolazni pozivi"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Samo odlazni pozivi"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Samo propušteni pozivi"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Vizuelna govorna pošta"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Pogledajte i preslušajte govornu poštu bez pozivanja broja. Mogući su troškovi prijenosa podataka."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Postavke"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Ažuriranja govorne pošte nisu dostupna"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Nova govorna pošta čeka. Trenutno nije moguće učitati."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Postavite govornu poštu"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Zvuk nije dostupan"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Postavljanje"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Pozovi govornu poštu"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Izaberite broj"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Izaberite broj"</string>
- <string name="make_primary" msgid="5829291915305113983">"Zapamti ovaj izbor"</string>
- <string name="description_search_button" msgid="3660807558587384889">"pretraga"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"biraj"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"broj za biranje"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Pokreni ili zaustavi pokretanje"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Uključi ili isključi zvučnik"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Traženje položaja reprodukcije"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Smanjenje brzine reprodukcije"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Povećavanje brzine reprodukcije"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Historija poziva"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Više opcija"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"telefonska tipkovnica"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Prikaži samo odlazne"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Prikaži samo dolazne"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Prikaži samo propuštene"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Prikaži samo govornu poštu"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Prikaži sve pozive"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Dodaj pauzu od 2 sekunde"</string>
- <string name="add_wait" msgid="3360818652790319634">"Dodaj čekanje"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Postavke"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Novi kontakt"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Svi kontakti"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Detalji o pozivu"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Detalji nisu dostupni"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Koristi tastaturu za tonsko biranje"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Povratak na poziv u toku"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Dodaj poziv"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Dolazni poziv"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Odlazni poziv"</string>
- <string name="type_missed" msgid="2720502601640509542">"Propušteni poziv"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Dolazni videopoziv"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Odlazni videopoziv"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Propušteni videopoziv"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Govorna pošta"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Odbijeni poziv"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blokiran poziv"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Dolazni pozivi"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Pokretanje govorne pošte"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Prikaži kontakt <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Pozovi kontakt <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Detalji o kontaktu <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"Broj poziva: <xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videopoziv."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Pošalji SMS kontaktu <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Nepreslušana govorna pošta"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Pokreni glasovnu pretragu"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Pozovi <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Nepoznato"</string>
- <string name="voicemail" msgid="3851469869202611441">"Govorna pošta"</string>
- <string name="private_num" msgid="6374339738119166953">"Privatni broj"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefonska govornica"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> sek."</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min. <xliff:g id="SECONDS">%s</xliff:g> sek."</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> u <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Nije moguće pozvati ovaj broj"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Da postavite govornu poštu, idite na Meni &gt; Postavke."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Da pozovete govornu poštu, isključite Način rada u avionu."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Učitavanje..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Učitavanje sa SIM kartice…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"Kontakti sa SIM kartice"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Nije dostupna nijedna aplikacija za kontakte"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Glasovna pretraga nije dostupna"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Nije moguće uputiti poziv jer je aplikacija Telefon onemogućena."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Na ovom uređaju nema aplikacije za to"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Traži kontakte"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Dodajte broj ili tražite kontakte"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Vaša historija poziva je prazna."</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Pozovi"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Nemate propuštenih poziva."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Vaše sanduče govorne pošte je prazno."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Vaša arhiva govorne pošte je prazna."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Prikaži samo favorite"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Historija poziva"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arhiva govorne pošte"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Sve"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Propušteni"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Govorna pošta"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Novo, jednostavnije blokiranje"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Radi bolje zaštite, Telefon treba promijeniti način rada blokiranja. S blokiranih brojeva od sada nećete primati ni pozive ni tekstualne poruke i blokirani brojevi će se moći dijeliti s drugim aplikacijama."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Dozvoli"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Blokirati broj <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Pozivi s ovog broja će biti blokirani, a govorna pošta će se automatski brisati."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Pozivi s ovog broja će biti blokirani, ali pozivalac će vam moći ostavljati govornu poštu."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Više nećete primati pozive ili tekstualne poruke s ovog broja."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOKIRAJ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Deblokirati broj <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DEBLOKIRAJ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Brzo biranje"</string>
- <string name="tab_history" msgid="2563144697322434940">"Historija poziva"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontakti"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Govorna pošta"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Uklonjeno iz favorita"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Poništi"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Pozovi <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Napravi novi kontakt"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Dodaj u kontakt"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Pošalji SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Uputi videopoziv"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blokiraj broj"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"Broj novih propuštenih poziva: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Još nemate nikog na brzom biranju"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Dodaj u favorite"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Još nemate nijedan kontakt"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Dodaj kontakt"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Dodirnite sliku da vidite sve brojeve ili dodirnite i držite da promijenite raspored."</string>
- <string name="remove_contact" msgid="1080555335283662961">"Ukloni"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videopoziv"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Pošalji poruku"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Detalji o pozivu"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Pozovi kontakt <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Propušteni poziv od kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Odgovoreno na poziv od kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Nepročitana govorna pošta od kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Govorna pošta od kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Pozovi kontakt <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"na <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"preko <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"preko <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"na <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, preko <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> preko <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Pozovi"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Pozovi kontakt <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Uputi videopoziv kontaktu <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Preslušaj govornu poštu od kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Pokreni govornu poštu od kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pauziraj govornu poštu od kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Izbriši govornu poštu od kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> nova poruka govorne pošte</item>
- <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> nove poruke govorne pošte</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> novih poruka govorne pošte</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Napravi kontakt za <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Dodaj <xliff:g id="NAMEORNUMBER">^1</xliff:g> postojećem kontaktu"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Detalji poziva za <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Izbrisano iz historije poziva"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Danas"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Jučer"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Starije"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Lista poziva"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Uključite zvučnik."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Isključivanje zvučnika."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Brže pokretanje."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Sporije pokretanje.."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Pokreni ili pauziraj pokretanje"</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Opcije prikaza"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Zvukovi i vibracija"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Pristupačnost"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Melodija zvona telefona"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Također vibriraj za pozive"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tonovi telefonske tipkovnice"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Dužina tonova telefonske tipkovnice"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normalno"</item>
- <item msgid="6177579030803486015">"Dugo"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Brzi odgovori"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Pozivi"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Blokiranje poziva"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Blokiranje poziva je privremeno isključeno"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Blokiranje poziva je onemogućeno jer ste kontaktirali hitnu službu s ovog telefona u proteklih 48 sati. Automatski će se ponovo omogućiti kada istekne 48 sati."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Uvezi brojeve"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Ranije ste označili da se neki pozivaoci automatski šalju na govornu poštu preko drugih aplikacija."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Prikaži brojeve"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Uvezi"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Uvoz nije uspio"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Arhiviranje govorne pošte nije uspjelo."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Deblokiraj broj"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Dodaj broj"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Pozivi s ovih brojeva će biti blokirani, a govorna pošta će se automatski brisati."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Pozivi s ovih brojeva će biti blokirani, ali pozivaoci će vam moći ostavljati govornu poštu."</string>
- <string name="block_list" msgid="7760188925338078011">"Blokirani brojevi"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> je nevažeći."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> je već blokiran."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Blokiranje poziva je onemogućeno na 48 sati"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Onemogućeno jer je upućen hitni poziv."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Računi za pozivanje"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Uključi"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Postavi dozvole"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Da omogućite brzo biranje, uključite dozvolu za Kontakte."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Da vidite popis poziva, uključite dozvolu za Telefon."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Da vidite kontakte, uključite dozvolu za Kontakte."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Da pristupite govornoj pošti,uključite dozvolu za telefon."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Da pretražujete kontakte, uključite dozvole za Kontakte."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Da uputite poziv, uključite dozvolu za Telefon."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Aplikacija za telefon nema dozvolu za pisanje u postavkama sistema."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Blokirano"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"Osoba <xliff:g id="NAMEORNUMBER">^1</xliff:g> je aktivna"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blokiraj/prijavi než. sadržaj"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blokiraj"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Ovo nije neželjeni sadržaj"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Deblokiraj"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Neželjena pošta"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Blokirati broj <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Blokiraćemo buduće pozive i govornu poštu s ovog broja."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Prijavi poziv kao neželjen"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Blokiraćemo buduće pozive i govornu poštu s ovog broja. Ovaj poziv će biti prijavljen kao neželjen."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Deblokirati broj <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Odblokiraćemo broj i prijaviti da nije neželjen. Budući pozivi i govorna pošta neće biti neželjeni."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Dozvoliti broj <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Dozvoli"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Budući pozivi i govorna pošta s ovog broja neće biti neželjeni. Prijavićemo da broj nije neželjen."</string>
-</resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
deleted file mode 100644
index bb5bf47eb..000000000
--- a/res/values-ca/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telèfon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telèfon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Teclat del telèfon"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telèfon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Historial de trucades"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Informa d\'un número incorrecte"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Copia el número"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Copia la transcripció"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Bloqueja el número"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"S\'ha bloquejat el número <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Desbloqueja el número"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"S\'ha desbloquejat el número <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="block_number_undo" msgid="591338370336724156">"DESFÉS"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Suprimeix"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Edita el número abans de trucar"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Esborra l\'historial de trucades"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Suprimeix la bústia de veu"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arxiva el missatge de veu"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Comparteix el missatge de veu"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Correu de veu suprimit"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Missatge de veu arxivat"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"DESFÉS"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"OBRE ARXIU"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Esborrar l\'historial de trucades?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Se suprimiran totes les trucades de l\'historial."</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Esborrant historial de trucades..."</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telèfon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Trucada perduda"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Trucada perduda de feina"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Trucades perdudes"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> trucades perdudes"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Torna la trucada"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Missatge"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> bústies de veu </item>
- <item quantity="one">Bústia de veu</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Reprodueix"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nou missatge de veu de <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Error en reproduir el missatge de veu."</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"S\'està carregant la bústia de veu..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"S\'estan arxivant els missatges de veu…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"No s\'ha pogut carregar la bústia de veu."</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Només trucades amb bústia de veu"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Només trucades entrants"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Només trucades sortints"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Només trucades perdudes"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Bústia de veu visual"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Consulta i escolta els missatges de la bústia de veu sense haver de trucar a cap número de telèfon. És possible que s\'apliquin càrrecs de dades."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Configuració"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"No hi ha cap actualització de correu de veu disponible."</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Hi ha un correu de veu nou en espera. Ara no es pot carregar."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Configura la bústia de veu."</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"L\'àudio no està disponible."</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Configura"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Truca a bústia veu"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Tria d\'un número"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Tria d\'un número"</string>
- <string name="make_primary" msgid="5829291915305113983">"Recorda aquesta selecció"</string>
- <string name="description_search_button" msgid="3660807558587384889">"cerca"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"marca"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"número que es marcarà"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Reprodueix o atura la reproducció"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Activa o desactiva el mans lliures"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Cerca la posició de la reproducció"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Disminueix la velocitat de la reproducció"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Augmenta la velocitat de la reproducció"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Historial de trucades"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Més opcions"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"teclat"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Mostra només trucades sortints"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Mostra només trucades entrants"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Mostra només trucades perdudes"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Mostra només missatges de veu"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Mostra totes les trucades"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Afegeix una pausa de 2 segons"</string>
- <string name="add_wait" msgid="3360818652790319634">"Afegeix espera"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Configuració"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Contacte nou"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Tots els contactes"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Detalls de la trucada"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Informació no disponible"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Utilitza el teclat de tons"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Torna a la trucada en curs"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Afegeix una trucada"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Trucada entrant"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Trucada de sortida"</string>
- <string name="type_missed" msgid="2720502601640509542">"Trucada perduda"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Videotrucada entrant"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Videotrucada sortint"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Videotrucada perduda"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Bústia de veu"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Trucada rebutjada"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Trucada bloquejada"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Trucades entrants"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Reprodueix el missatge de veu"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Visualitza el contacte <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Truca a <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Dades de contacte de: <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> trucades"</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videotrucada"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Envia un SMS a <xliff:g id="NAME">%1$s</xliff:g>."</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Missatge de veu no escoltat"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Inicia la cerca per veu"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Truca al <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Desconegut"</string>
- <string name="voicemail" msgid="3851469869202611441">"Bústia de veu"</string>
- <string name="private_num" msgid="6374339738119166953">"Número privat"</string>
- <string name="payphone" msgid="7726415831153618726">"Telèfon públic"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> a les <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"No es pot trucar a aquest número."</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Per configurar la bústia de veu, vés a Menú &gt; Configuració."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Per trucar a la bústia de veu, primer has de desactivar el mode d\'avió."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"S\'està carregant…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"S\'està carregant des de la targeta SIM..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Contactes de la targeta SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"No hi ha cap contacte disponible."</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"La cerca per veu no està disponible."</string>
- <string name="call_not_available" msgid="8941576511946492225">"No es pot fer una trucada telefònica perquè s\'ha desactivat l\'aplicació de telèfon."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"No hi ha cap aplicació per a això en aquest dispositiu."</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Cerca contactes"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Afegeix núm. o cerca contactes"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"El teu historial de trucades és buit"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Fes una trucada"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"No tens cap trucada perduda."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"La safata d\'entrada de la bústia de veu està buida."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"L\'arxiu de missatges de veu està buit."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Mostra només els preferits"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Historial de trucades"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arxiu de missatges de veu"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Totes"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Perdudes"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Missatge de veu"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Bloqueig nou i simplificat"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Per protegir-te millor, l\'aplicació Telèfon ha de canviar la manera com funciona el bloqueig. No rebràs trucades ni missatges de text dels números bloquejats, i pot ser que compartim aquests números amb altres aplicacions."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Permet"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Vols bloquejar el número <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Es bloquejaran les trucades d\'aquest número i els missatges de veu se suprimiran automàticament."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Es bloquejaran les trucades d\'aquest número, però és possible que continuïn deixant-te missatges de veu."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Deixaràs de rebre trucades i missatges de text d\'aquest número."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOQUEJA"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Vols desbloquejar el número <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DESBLOQUEJA"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Marcatge ràpid"</string>
- <string name="tab_history" msgid="2563144697322434940">"Historial de trucades"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Contactes"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Bústia de veu"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Eliminat dels preferits"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Desfés"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Truca al <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Crea un contacte"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Afegeix a un contacte"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Envia SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Fes una videotrucada"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Bloqueja el número"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> trucades perdudes noves"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Encara no tens cap contacte al marcatge ràpid"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Afegeix un preferit"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Encara no tens cap contacte"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Afegeix un contacte"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Toca la imatge per veure tots els números i toca-la i mantén-la premuda per reordenar-los."</string>
- <string name="remove_contact" msgid="1080555335283662961">"Suprimeix"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videotrucada"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Envia un missatge"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Detalls de la trucada"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Truca a <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Trucada perduda de: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Trucada resposta de: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Missatge de veu no escoltat del contacte <xliff:g id="NAMEORNUMBER">^1</xliff:g>: <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Missatge de veu del contacte <xliff:g id="NAMEORNUMBER">^1</xliff:g>: <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Trucada a <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"a <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"al número <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"al número <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"a <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, al número <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> al número <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Truca"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Truca a <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Videotrucada amb <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Escolta el missatge a la bústia de veu de: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Reprodueix el missatge de veu de: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Posa en pausa el missatge de veu de: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Suprimeix el missatge de veu de: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> nous correus de veu</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> nou correu de veu</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Crea un contacte per a <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Afegeix <xliff:g id="NAMEORNUMBER">^1</xliff:g> a un contacte existent"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Detalls de la trucada de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"S\'ha suprimit de l\'historial de trucades."</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Avui"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Ahir"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Més antiga"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Llista de trucades"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Activa l\'altaveu."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Desactiva l\'altaveu."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Reprodueix més ràpidament."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Reprodueix més lentament."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Inicia la reproducció o la posa en pausa."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Opcions de visualització"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Sons i vibració"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Accessibilitat"</string>
- <string name="ringtone_title" msgid="760362035635084653">"So de trucada"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibra també en trucades"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tons del teclat"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Durada del to del teclat"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"Llarg"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Respostes ràpides"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Trucades"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Bloqueig de trucades"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Bloqueig de trucades desactivat tempor."</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"El bloqueig de trucades s\'ha desactivat perquè has contactat amb els serveis d\'emergència des d\'aquest telèfon durant les últimes 48 hores. Es tornarà a activar automàticament una vegada transcorregut el període de 48 hores."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importa els números"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Has indicat que les trucades d\'alguns contactes s\'enviïn automàticament a la bústia de veu mitjançant altres aplicacions."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Mostra els números"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importa"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Error en la importació"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Error en arxivar el missatge de veu."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Desbloqueja el número"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Afegeix un número"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Es bloquejaran les trucades d\'aquests números i els missatges de veu se suprimiran automàticament."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Es bloquejaran les trucades d\'aquests números, però és possible que continuïn deixant-te missatges de veu."</string>
- <string name="block_list" msgid="7760188925338078011">"Números bloquejats"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> no és vàlid."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> ja està bloquejat."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"El bloqueig de trucades s\'ha desactivat durant 48 hores"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"S\'ha desactivat perquè s\'ha fet una trucada d\'emergència."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Comptes de trucades"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Activa"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Defineix els permisos"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Activa el permís Contactes per activar el marcatge ràpid."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Activa el permís Telèfon per veure el teu registre de trucades."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Activa el permís Contactes per veure els teus contactes."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Activa el permís Telèfon per accedir a la bústia de veu."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Per cercar els teus contactes, activa els permisos de Contactes."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Activa el permís Telèfon per fer una trucada."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"L\'aplicació Telèfon no té permís per escriure a la configuració del sistema."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Bloquejat"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> és la trucada activa"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Bloqueja o marca com a brossa"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Bloqueja"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"No és una trucada brossa"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Desbloqueja"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Trucada brossa"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Vols bloquejar el número <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"En endavant, es bloquejaran les trucades i els missatges de veu d\'aquest número."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Marca la trucada com a brossa"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Bloquejarem les trucades i missatges de veu d\'aquest número. Aquesta trucada es marcarà com a brossa."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Vols desbloquejar el número <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Aquest número es desbloquejarà i no es considerarà brossa; les trucades i els missatges de veu futurs, tampoc."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Vols afegir el número <xliff:g id="NUMBER">%1$s</xliff:g> a la llista blanca?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Afegeix a la llista blanca"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"En endavant, les trucades i els missatges de veu d\'aquest número (i el número mateix) no es consideraran brossa."</string>
-</resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
deleted file mode 100644
index 543a8976b..000000000
--- a/res/values-cs/strings.xml
+++ /dev/null
@@ -1,292 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Číselník telefonu"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Historie volání"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Nahlásit nesprávné číslo"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopírovat číslo"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopírovat přepis"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blokovat číslo"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Číslo <xliff:g id="NUMBER">%1$s</xliff:g> zablokováno"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Zrušit blokování čísla"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Číslo <xliff:g id="NUMBER">%1$s</xliff:g> odblokováno"</string>
- <string name="block_number_undo" msgid="591338370336724156">"VRÁTIT"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Smazat"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Před voláním upravit číslo"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Vymazat historii volání"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Smazat hlasovou zprávu"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Archivovat hlasovou zprávu"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Sdílet hlasovou zprávu"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Hlas. zpráva smazána"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Hlasová zpráva archivována"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"VRÁTIT"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"DO ARCHIVU"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Vymazat historii hovorů?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Tímto z historie smažete všechny hovory."</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Mazání historie volání…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Zmeškaný hovor"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Zmeškaný pracovní hovor"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Zmeškané hovory"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"Zmeškané hovory: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Zavolat zpět"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Zpráva"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="few"> <xliff:g id="COUNT">%1$d</xliff:g> hlasové zprávy </item>
- <item quantity="many"> <xliff:g id="COUNT">%1$d</xliff:g> hlasové zprávy </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> hlasových zpráv </item>
- <item quantity="one">Hlasová zpráva</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Přehrát"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nová hlasová zpráva – <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Hlasovou schránku nelze přehrát."</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Načítání hlasové schránky…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Archivace hlasové zprávy…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Hlasovou schránku nelze načíst."</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Jen hovory s hlasovou schránkou"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Jen příchozí hovory"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Jen odchozí hovory"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Jen zmeškané hovory"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Vizuální hlasová schránka"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Dívejte se na hlasové zprávy a poslouchejte je, aniž byste někam museli volat. Mohou být účtovány poplatky za datový přenos."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Nastavení"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Aktualizace hlasové schránky nejsou k dispozici."</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Máte novou hlasovou zprávu. Aktuálně ji však nelze načíst."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Nastavte si hlasovou schránku."</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Zvuk není k dispozici."</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Nastavit"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Volat hlas. schránku"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Zvolte číslo"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Zvolte číslo"</string>
- <string name="make_primary" msgid="5829291915305113983">"Zapamatovat tuto volbu"</string>
- <string name="description_search_button" msgid="3660807558587384889">"vyhledat"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"vytáčení"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"vytáčené číslo"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Spuštění a zastavení přehrávání"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Zapnutí a vypnutí reproduktoru"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Vyhledání pozice přehrávání"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Snížení rychlosti přehrávání"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Zvýšení rychlosti přehrávání"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Historie volání"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Více možností"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"číselník"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Zobrazit pouze odchozí"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Zobrazit pouze příchozí"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Zobrazit pouze zmeškané"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Zobrazit pouze hlas. schránku"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Zobrazit všechny hovory"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Přidat pauzu 2 s"</string>
- <string name="add_wait" msgid="3360818652790319634">"Přidat čekání"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Nastavení"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Nový kontakt"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Všechny kontakty"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Podrobnosti hovoru"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Podrobnosti nejsou k dispozici."</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Použít dotykovou tónovou klávesnici"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Návrat k probíhajícímu hovoru"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Přidat hovor"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Příchozí hovor"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Odchozí volání"</string>
- <string name="type_missed" msgid="2720502601640509542">"Zmeškaný hovor"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Příchozí videohovor"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Odchozí videohovor"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Zmeškaný videohovor"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Hlasová schránka"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Zamítnutý hovor"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blokovaný hovor"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Příchozí volání"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Přehrát hlasovou schránku"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Zobrazit kontakt <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Volat kontakt <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Podrobnosti kontaktu pro <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"Počet hovorů: <xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>"</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videohovor"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Odeslat zprávu SMS kontaktu <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Nevyslechnutá hlasová zpráva"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Spustit hlasové vyhledávání"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Zavolat <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Neznámé"</string>
- <string name="voicemail" msgid="3851469869202611441">"Hlasová schránka"</string>
- <string name="private_num" msgid="6374339738119166953">"Soukromé číslo"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefonní automat"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> v <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Na toto číslo nelze volat."</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Chcete-li nastavit hlasovou schránku, přejděte do části Menu &gt; Nastavení."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Chcete-li volat hlasovou schránku, nejdříve vypněte režim Letadlo."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Načítá se..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Načítání ze SIM karty..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Kontakty na SIM kartě"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Není k dispozici žádná aplikace pro práci s kontakty."</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Hlasové vyhledávání není k dispozici."</string>
- <string name="call_not_available" msgid="8941576511946492225">"Telefonický hovor nelze uskutečnit, protože aplikace Telefon byla zakázána."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Pro tuto akci v zařízení nemáte žádnou aplikaci."</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Prohledat kontakty"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Přidejte číslo nebo vyhledejte kontakty"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Historie volání je prázdná"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Zavolat"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Nemáte žádné zmeškané hovory."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Hlasová schránka je prázdná."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Archiv vašich hlasových zpráv je prázdný."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Zobrazit pouze oblíbené"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Historie volání"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Archiv hlasových zpráv"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Všechny"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Zmeškané"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Schránka"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Nové zjednodušené blokování"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Z důvodu zvýšení zabezpečení potřebuje aplikace Telefon změnit nastavení blokování. Blokovaná čísla vám nyní nebudou moci volat ani psát SMS a mohou být sdílena s dalšími aplikacemi."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Povolit"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Blokovat číslo <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Volání z tohoto čísla budou zablokována. Hlasové zprávy budou automaticky smazány."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Volání z tohoto čísla budou zablokována, avšak volající vám bude moci zanechat hlasovou zprávu."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Z tohoto čísla už nebudete přijímat hovory ani zprávy SMS."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOKOVAT"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Odblokovat číslo <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ODBLOKOVAT"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Rychlá volba"</string>
- <string name="tab_history" msgid="2563144697322434940">"Historie volání"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontakty"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Hlasová schránka"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Odebráno z oblíbených"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Vrátit zpět"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Zavolat <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Vytvořit nový kontakt"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Přidat ke kontaktu"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Odeslat SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Uskutečnit videohovor"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blokovat číslo"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"Nové zmeškané hovory: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"V rychlém vytáčení zatím nemáte žádný kontakt."</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Přidat oblíbený kontakt"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Zatím nemáte žádné kontakty"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Přidat kontakt"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Klepnutím na obrázek zobrazíte všechna čísla. Klepnutím a podržením upravíte uspořádání."</string>
- <string name="remove_contact" msgid="1080555335283662961">"Odstranit"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videohovor"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Odeslat zprávu"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Podrobnosti hovoru"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Volat kontakt <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Zmeškaný hovor: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Přijatý hovor: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Nepřečtená hlasová zpráva od <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Hlasová zpráva od <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Odchozí hovor: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"v účtu <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"z čísla <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"z čísla <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"na účet <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, z čísla <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"na účet <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, z čísla <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Volat"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Volat kontakt <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Videohovor s kontaktem <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Poslech hlasové schránky od <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Přehrát hlasovou zprávu od kontaktu <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pozastavit hlasovou zprávu od kontaktu <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Smazat hlasovou zprávu od kontaktu <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> nové zprávy v hlasové schránce</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> nové zprávy v hlasové schránce</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> nových zpráv v hlasové schránce</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> nová zpráva v hlasové schránce</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Pro záznam <xliff:g id="NAMEORNUMBER">^1</xliff:g> se vytvoří nový kontakt"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Záznam <xliff:g id="NAMEORNUMBER">^1</xliff:g> se přidá k existujícímu kontaktu"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Podrobnosti volání <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Smazáno z historie volání"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Dnes"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Včera"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Starší"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Seznam volání"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Zapnout reproduktor."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Vypnout reproduktor."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Přehrávat rychleji."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Přehrávat pomaleji."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Spustit nebo pozastavit přehrávání."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Možnosti zobrazení"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Zvuky a vibrace"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Přístupnost"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Vyzváněcí tón telefonu"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"U hovorů také vibrovat"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tóny číselníku"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Délka tónu číselníku"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normální"</item>
- <item msgid="6177579030803486015">"Dlouhé"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Rychlé odpovědi"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Volání"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Blokování hovorů"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Blokování hovorů je dočasně vypnuto"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Protože jste z tohoto telefonu během posledních 48 hodin volali na tísňovou linku, bylo blokování hovorů vypnuto. Po uplynutí 48 hodin se automaticky znovu zapne."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importovat čísla"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Pomocí dalších aplikací jste dříve některé volající označili k automatickému přesměrování do hlasové schránky."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Zobrazit čísla"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importovat"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Import se nezdařil."</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Hlasovou zprávu se nepodařilo archivovat"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Zrušit blokování čísla"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Přidat číslo"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Volání z těchto čísel budou zablokována. Hlasové zprávy budou automaticky smazány."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Volání z těchto čísel budou zablokována, avšak volající vám budou moci zanechat hlasovou zprávu."</string>
- <string name="block_list" msgid="7760188925338078011">"Blokovaná čísla"</string>
- <string name="invalidNumber" msgid="619058581062192851">"Číslo <xliff:g id="NUMBER">%1$s</xliff:g> je neplatné."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"Číslo <xliff:g id="NUMBER">%1$s</xliff:g> je již blokováno."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Blokování hovorů je vypnuto na 48 hodin"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Vypnuto z důvodu provedeného tísňového volání"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Účty pro volání"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Zapnout"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Nastavit oprávnění"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Chcete-li povolit rychlé vytáčení, aktivujte oprávnění Kontakty."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Chcete-li zobrazit seznam hovorů, aktivujte oprávnění Telefon."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Chcete-li zobrazit kontakty, aktivujte oprávnění Kontakty."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Chcete-li přejít do hlasové schránky, aktivujte oprávnění Telefon."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Chcete-li vyhledat kontakty, zapněte oprávnění Kontakty."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Chcete-li uskutečnit hovor, aktivujte oprávnění Telefon."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Aplikace Telefon nemá oprávnění provádět zápis do nastavení systému."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Blokováno"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> je aktivní"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Zablokovat / nahlásit spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Zablokovat"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Není spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Odblokovat"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Zablokovat číslo <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Budoucí hovory a hlasové zprávy z tohoto čísla budou blokovány."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Nahlásit hovor jako spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Budoucí hovory a hlasové zprávy z tohoto čísla budou blokovány. Hovor bude nahlášen jako spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Odblokovat číslo <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Toto číslo bude odblokováno a bude nahlášeno, že se nejedná o spam. Budoucí hovory a hlasové zprávy nebudou označeny jako spam."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Přidat číslo <xliff:g id="NUMBER">%1$s</xliff:g> na seznam povolených?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Přidat na seznam povolených"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Budoucí hovory a hlasové zprávy z tohoto čísla nebudou označeny jako spam. Bude nahlášeno, že toto číslo není spam."</string>
-</resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
deleted file mode 100644
index 701228344..000000000
--- a/res/values-da/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Opkald"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Opkald"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Telefontastatur"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Opkald"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Opkaldshistorik"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Rapportér et forkert nummer"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopiér nummeret"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopiér transskriptionen"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Bloker nummeret"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> er blokeret"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Ophæv blokering af nummeret"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> er ikke blokeret"</string>
- <string name="block_number_undo" msgid="591338370336724156">"FORTRYD"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Slet"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Rediger nummeret før opkald"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Ryd opkaldshistorik"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Slet talemeddelelsen"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arkivér talebesked"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Del talebeskeden"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Talebeskeden blev slettet"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Talebeskeden blev arkiveret"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"FORTRYD"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"GÅ TIL ARKIV"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Vil du rydde opkaldshistorikken?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Dette vil slette alle opkald fra din historik"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Opkaldshistorik ryddes..."</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Ubesvaret opkald"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Ubesvaret arbejdsopkald"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Ubesvarede opkald"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ubesvarede opkald"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Ring tilbage"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Besked"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"><xliff:g id="COUNT">%1$d</xliff:g> talebeskeder </item>
- <item quantity="other"><xliff:g id="COUNT">%1$d</xliff:g> talebeskeder </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Spil"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Ny besked fra <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Telefonsvarerbesked kan ikke afspilles"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Telefonsvarerbesked indlæses..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Arkiverer talebeskeden…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Telefonsvarerbesked kan ikke indlæses"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Kun opkald med telefonsvarer"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Kun indgående opkald"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Kun udgående opkald"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Kun ubesvarede opkald"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Visuel telefonsvarer"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Se og lyt til din telefonsvarer uden at skulle ringe til et nummer. Du kan blive opkrævet gebyr for dataforbrug."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Indstillinger"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Nye telefonsvarerbeskeder er ikke tilgængelige"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Ny telefonsvarerbesked venter. Den kan ikke indlæses lige nu."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Konfigurer din telefonsvarer"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Lyd er ikke tilgængelig"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Konfigurer"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Ring til tlfsvarer"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Vælg nummer"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Vælg nummer"</string>
- <string name="make_primary" msgid="5829291915305113983">"Husk dette valg"</string>
- <string name="description_search_button" msgid="3660807558587384889">"søg"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"ring op"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"nummer at ringe op"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Start eller stop afspilning"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Slå medhør til eller fra"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Søg efter afspilningsposition"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Skru ned for afspilningshastigheden"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Skru op for afspilningshastigheden"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Opkaldshistorik"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Flere valgmuligheder"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"tastatur"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Vis kun udgående"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Vis kun indgående"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Vis kun ubesvarede"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Vis kun telefonsvarerbeskeder"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Vis alle opkald"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Tilføj pause på 2 sek."</string>
- <string name="add_wait" msgid="3360818652790319634">"Tilføj ventetid"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Indstillinger"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Ny kontaktperson"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Alle kontaktpersoner"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Opkaldsinfo"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Detaljerne er ikke tilgængelige"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Brug numerisk tastatur"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Vend tilbage til igangværende opkald"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Tilføj opkald"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Indgående opkald"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Udgående opkald"</string>
- <string name="type_missed" msgid="2720502601640509542">"Ubesvaret opkald"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Indgående videoopkald"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Udgående videoopkald"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Ubesvaret videoopkald"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Telefonsvarer"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Afvist opkald"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blokeret opkald"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Indgående opkald"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Afspil telefonsvarerbesked"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Vis kontaktpersonen <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Ring til <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Kontaktoplysninger for <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> opkald."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videoopkald."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Send sms-besked til <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Uaflyttet besked på telefonsvareren"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Start talesøgning"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Ring til <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Ukendt"</string>
- <string name="voicemail" msgid="3851469869202611441">"Telefonsvarer"</string>
- <string name="private_num" msgid="6374339738119166953">"Privat nummer"</string>
- <string name="payphone" msgid="7726415831153618726">"Mønttelefon"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> sek."</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min. <xliff:g id="SECONDS">%s</xliff:g> sek."</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> kl. <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Det er ikke muligt at ringe til dette nummer"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Hvis du vil konfigurere telefonsvareren, skal du gå til Menu &gt; Indstillinger."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Hvis du vil ringe til telefonsvareren, skal du først slå Flytilstand fra."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Indlæser…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI-nummer"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Indlæser fra SIM-kort ..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Kontakter på SIM-kort"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Der er ingen app til kontaktpersoner"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Talesøgning er ikke tilgængelig"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Det er ikke muligt at foretage et telefonopkald, fordi applikationen Telefon er deaktiveret."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Der findes Ingen app til det på denne enhed"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Søg efter kontaktpersoner"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Tilføj nummer, eller søg i kontaktpersoner"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Din opkaldshistorik er tom"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Foretag et opkald"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Du har ingen ubesvarede opkald."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Indbakken for din telefonsvarer er tom."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Dit arkiv med talebeskeder er tomt."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Vis kun foretrukne"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Opkaldshistorik"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arkiv med talebeskeder"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Alle"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Ubesvarede"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Talebesked"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Ny, forenklet blokering"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Phone er nødt til at ændre, hvordan blokering fungerer, for bedre at kunne beskytte dig. Dine blokerede numre forhindrer både opkald og sms-beskeder, og de kan muligvis deles med andre apps."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Tillad"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Vil du blokere <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Opkald fra dette nummer blokeres, og beskeder i telefonsvareren slettes automatisk."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Opkald fra dette nummer bliver blokeret, men der kan muligvis stadig lægges beskeder i din telefonsvarer."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Du modtager ikke længere opkald eller sms-beskeder fra dette nummer."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOKER"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Vil du fjerne blokeringen af <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"FJERN BLOKERING"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Hurtigopkald"</string>
- <string name="tab_history" msgid="2563144697322434940">"Opkaldshistorik"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontaktpersoner"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Telefonsvarer"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Fjernet fra foretrukne"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Fortryd"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Ring til <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Opret ny kontaktperson"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Føj til en kontaktperson"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Send sms"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Foretag videoopkald"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Bloker nummer"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> nye ubesvarede opkald"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Der er endnu ingen kontaktpersoner i Hurtigopkald"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Angiv en kontaktperson som foretrukken"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Du har endnu ikke nogen kontaktpersoner"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Tilføj en kontaktperson"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Tryk på billedet for at se alle numre, eller tryk på billedet, og hold det nede for at omarrangere"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Fjern"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videoopkald"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Send en sms-besked"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Opkaldsinfo"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Ring til <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Mistet opkald fra <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Besvaret opkald fra <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Ulæst talebesked fra <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Talebesked fra <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Ring til <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"på <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"via <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"via <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"til <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, via <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> via <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Ring op"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Ring til <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Videoopkald <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Lyt til indtalt besked fra <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Afspil talemeddelelsen fra <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Sæt talemeddelelsen fra <xliff:g id="NAMEORNUMBER">^1</xliff:g> på pause"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Slet talemeddelelsen fra <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> nye talemeddelelser</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> nye talemeddelelser</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Opret en kontaktperson for <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Føj <xliff:g id="NAMEORNUMBER">^1</xliff:g> til en eksisterende kontaktperson"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Opkaldsoplysninger for <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Slettet fra opkaldshistorik"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"I dag"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"I går"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Ældre"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Opkaldsliste"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Slå højttaler til."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Slå højttaler fra."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Afspil hurtigere."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Afspil langsommere."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Start afspilningen, eller sæt den på pause."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Valgmuligheder for visning"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Lyde og vibration"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Hjælpefunktioner"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Ringetone for opkald"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibrer også ved opkald"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Toner for numerisk tastatur"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Tonelængde for numerisk tastatur"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"Lang"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Hurtige svar"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Opkald"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Opkaldsblokering"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Opkaldsblokering er midlertidigt slået fra"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Opkaldsblokering er blevet deaktiveret, da du inden for de sidste 48 timer har kontaktet en alarmcentral. Blokeringen aktiveres automatisk igen, når perioden på 48 timer er udløbet."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importér numre"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Du har tidligere angivet, at nogle opkaldere automatisk skal sendes til telefonsvareren via andre apps."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Se numre"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importér"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Importen mislykkedes"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Talebeskeden kunne ikke arkiveres."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Ophæv blokering af nummer"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Tilføj nummer"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Opkald fra disse numre blokeres, og beskeder i telefonsvareren slettes automatisk."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Opkald fra disse numre bliver blokeret, men der kan muligvis stadig lægges beskeder i din telefonsvarer."</string>
- <string name="block_list" msgid="7760188925338078011">"Blokerede telefonnumre"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> er ugyldigt."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> er allerede blokeret."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Opkaldsblokering er blevet deaktiveret i 48 timer"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Deaktiveret, da du har foretaget et nødopkald."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Opkaldskonti"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Aktivér"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Angiv tilladelser"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Slå tilladelsen Kontaktpersoner til for at aktivere hurtigopkald."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Slå tilladelsen Telefon til for at se din opkaldsliste."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Slå tilladelsen Kontaktpersoner til for at se dine kontaktpersoner."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Slå tilladelsen Telefon til for at få adgang til din telefonsvarer,"</string>
- <string name="permission_no_search" msgid="84152933267902056">"Hvis du vil søge i dine kontaktpersoner, skal du slå tilladelserne Kontaktpersoner til."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Slå tilladelsen Telefon til for at foretage et opkald."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Opkaldsappen har ikke tilladelse til at ændre systemindstillinger."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Blokeret"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> er aktivt"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Bloker/rapportér spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Bloker"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Ikke spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Ophæv blokering"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Vil du blokere <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Fremtidige opkald og talebeskeder fra dette nummer blokeres."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Rapportér opkaldet som spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Fremtidige opkald og talebeskeder fra dette nummer blokeres. Dette opkald rapporteres som spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Vil du ophæve blokeringen af <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Blokeringen af nummeret ophæves, og spam rapporteres ikke. Spam registreres ikke for nye opkald og talebeskeder."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Vil du hvidliste <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Hvidlist"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Fremtidige opkald og talebeskeder fra dette nummer registreres ikke som spam. Dette nummer rapporteres ikke som spam."</string>
-</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
deleted file mode 100644
index ca373ca8e..000000000
--- a/res/values-de/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Wähltasten verwenden"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Anrufliste"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Falsche Nummer melden"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Nummer kopieren"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Transkription kopieren"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Nummer blockieren"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> blockiert"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Blockierung der Nummer aufheben"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Blockierung von <xliff:g id="NUMBER">%1$s</xliff:g> aufgehoben"</string>
- <string name="block_number_undo" msgid="591338370336724156">"Rückgängig"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Löschen"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Nummer vor Anruf bearbeiten"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Anrufliste löschen"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Mailbox-Nachricht löschen"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Mailbox-Nachricht archivieren"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Mailbox-Nachricht teilen"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Mailbox gelöscht"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Mailbox-Nachricht archiviert"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"Rückgängig"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ZUM ARCHIV"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Anrufliste löschen?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Alle Anrufe werden aus deinem Verlauf gelöscht."</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Anrufliste wird gelöscht…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Verpasster Anruf"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Verpasster geschäftlicher Anruf"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Entgangene Anrufe"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> entgangene Anrufe"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Zurückrufen"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Nachricht"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Mailbox-Nachrichten </item>
- <item quantity="one">Mailbox-Nachricht</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Anhören"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Neue Mailbox-Nachricht von <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Mailbox-Nachricht-Wiedergabe nicht möglich"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Mailbox-Nachricht wird geladen…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Mailbox-Nachricht wird archiviert…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Laden der Mailbox-Nachricht nicht möglich"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Nur Mailbox-Anrufe"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Nur eingehende Anrufe"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Nur ausgehende Anrufe"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Nur entgangene Anrufe"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Visuelle Mailbox"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Du kannst deine Mailbox-Nachrichten einsehen und abhören, ohne eine Nummer anrufen zu müssen. Es können Datengebühren anfallen."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Einstellungen"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Keine Mailbox-Aktualisierungen verfügbar"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Neue Mailbox-Nachricht wartet. Laden momentan nicht möglich."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Richte deine Mailbox ein."</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio nicht verfügbar"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Einrichten"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Mailbox anrufen"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Nummer auswählen"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Nummer auswählen"</string>
- <string name="make_primary" msgid="5829291915305113983">"Auswahl speichern"</string>
- <string name="description_search_button" msgid="3660807558587384889">"Suchen"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"wählen"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"Zu wählende Nummer"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Wiedergabe starten oder stoppen"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Freisprechfunktion aktivieren oder deaktivieren"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Wiedergabeposition suchen"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Wiedergabegeschwindigkeit verringern"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Wiedergabegeschwindigkeit erhöhen"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Anrufliste"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Mehr Optionen"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"Wähltasten"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Nur ausgehende Anrufe anzeigen"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Nur eingehende Anrufe anzeigen"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Nur entgangene Anrufe anzeigen"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Nur Mailbox-Nachr. anzeigen"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Alle Anrufe anzeigen"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2 Sekunden Pause hinzufügen"</string>
- <string name="add_wait" msgid="3360818652790319634">"Warten hinzufügen"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Einstellungen"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Neuer Kontakt"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Alle Kontakte"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Anrufdetails"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Details nicht verfügbar"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Telefontastatur verwenden"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Zurück zum aktuellen Anruf"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Anruf hinzufügen"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Eingehender Anruf"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Ausgehender Anruf"</string>
- <string name="type_missed" msgid="2720502601640509542">"Entgangener Anruf"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Eingehender Videoanruf"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Ausgehender Videoanruf"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Entgangener Videoanruf"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Mailbox"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Abgelehnter Anruf"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blockierter Anruf"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Eingehende Anrufe"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Mailbox abhören"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Kontakt <xliff:g id="NAME">%1$s</xliff:g> anzeigen"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g> anrufen"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Kontaktdetails für <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> Anrufe"</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videoanruf"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"SMS an <xliff:g id="NAME">%1$s</xliff:g> senden"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Nicht abgehörte Mailbox-Nachricht"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Sprachsuche starten"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> wählen"</string>
- <string name="unknown" msgid="740067747858270469">"Unbekannt"</string>
- <string name="voicemail" msgid="3851469869202611441">"Mailbox"</string>
- <string name="private_num" msgid="6374339738119166953">"Private Nummer"</string>
- <string name="payphone" msgid="7726415831153618726">"Münztelefon"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> um <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Diese Nummer kann nicht angerufen werden."</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Konfiguriere deine Mailbox unter \"Menü\" &gt; \"Einstellungen\"."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Deaktiviere zunächst den Flugmodus, um die Mailbox anzurufen."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Wird geladen…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Ladevorgang von SIM-Karte läuft..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Kontakte auf SIM-Karte"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Keine Kontakte-App verfügbar"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Sprachsuche nicht verfügbar"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Es kann kein Anruf getätigt werden, da die App \"Telefon\" deaktiviert wurde."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Keine entsprechende App auf diesem Gerät"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"In Kontakten suchen"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Nummer hinzufügen oder in Kontakten suchen"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Deine Anrufliste ist leer."</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Anrufen"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Keine entgangenen Anrufe"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Dein Mailbox-Posteingang ist leer."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Das Archiv für Mailbox-Nachrichten ist leer."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Nur Favoriten anzeigen"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Anrufliste"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Archiv für Mailbox-Nachrichten"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Alle"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Entgangen"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Mailbox-Nachrichten"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Neue einfache Blockierfunktion"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Um dich besser schützen zu können, müssen die Einstellungen der Blockierung beim Telefon geändert werden. Von blockierten Nummern erhältst du jetzt keine Anrufe und Nachrichten mehr und diese Nummern können auch an andere Apps weitergegeben werden."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Zulassen"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> blockieren?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Anrufe von dieser Nummer werden blockiert und Mailbox-Nachrichten automatisch gelöscht."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Anrufe von dieser Nummer werden blockiert. Der Anrufer hat aber unter Umständen weiterhin die Möglichkeit, dir Mailbox-Nachrichten zu hinterlassen."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Du erhältst von dieser Nummer keine Anrufe oder Nachrichten mehr."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOCKIEREN"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Blockierung von <xliff:g id="NUMBER">%1$s</xliff:g> aufheben?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"BLOCKIERUNG AUFHEBEN"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Schnellauswahl"</string>
- <string name="tab_history" msgid="2563144697322434940">"Anrufliste"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontakte"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Mailbox"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Aus Favoriten entfernt"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Rückgängig machen"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> anrufen"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Neuen Kontakt erstellen"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Zu Kontakt hinzufügen"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS senden"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Videoanruf starten"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Nummer blockieren"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> neue entgangene Anrufe"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Du hast noch für niemanden eine Kurzwahl festgelegt."</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Favoriten hinzufügen"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Du hast noch keine Kontakte."</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Kontakt hinzufügen"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Bild berühren, um alle Nummern zu sehen, oder Bild berühren und halten, um sie neu anzuordnen"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Entfernen"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videoanruf"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"SMS senden"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Anrufdetails"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> anrufen"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Anruf von <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> verpasst"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Anruf von <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> angenommen"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Ungelesene Mailbox-Nachricht von <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Mailbox-Nachricht von <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Ausgehender Anruf an <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"mit <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"über <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"über <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"mit <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, über <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> über <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Anrufen"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> anrufen"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> über Videoanruf anrufen"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Mailboxnachricht von <xliff:g id="NAMEORNUMBER">^1</xliff:g> anhören"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Mailbox-Nachricht von <xliff:g id="NAMEORNUMBER">^1</xliff:g> abhören"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Mailbox-Nachricht von <xliff:g id="NAMEORNUMBER">^1</xliff:g> pausieren"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Mailbox-Nachricht von <xliff:g id="NAMEORNUMBER">^1</xliff:g> löschen"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> neue Mailboxnachrichten</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> neue Mailboxnachricht</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Kontakt für <xliff:g id="NAMEORNUMBER">^1</xliff:g> erstellen"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> zu vorhandenem Kontakt hinzufügen"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Anrufdetails für <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Aus Anrufliste gelöscht"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Heute"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Gestern"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Ältere"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Anrufliste"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Lautsprecher einschalten"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Lautsprecher ausschalten"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Schneller wiedergeben"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Langsamer wiedergeben"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Wiedergabe starten oder pausieren"</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Anzeigeoptionen"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Töne und Vibration"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Bedienungshilfen"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Klingelton"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Bei Anrufen auch vibrieren"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Wähltastentöne"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Länge der Wähltastentöne"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"Lang"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Kurzantworten"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Anrufe"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Anrufblockierung"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Anrufblockierung vorübergehend aus"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Die Anrufblockierung wurde deaktiviert, weil du innerhalb der letzten 48 Stunden mit diesem Telefon den Notruf gewählt hast. Nach Ablauf dieser 48-Stunden-Frist wird die Blockierung automatisch wieder aktiviert."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Nummern importieren"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Du hast zuvor einige Anrufer markiert, die automatisch über andere Apps an die Mailbox gesendet werden."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Nummern anzeigen"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importieren"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Import fehlgeschlagen"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Mailbox-Nachricht nicht archiviert."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Blockierung der Nummer aufheben"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Nummer hinzufügen"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Anrufe von diesen Nummern werden blockiert und Mailbox-Nachrichten automatisch gelöscht."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Anrufe von diesen Nummern werden blockiert. Die Anrufer haben aber unter Umständen weiterhin die Möglichkeit, dir Mailbox-Nachrichten zu hinterlassen."</string>
- <string name="block_list" msgid="7760188925338078011">"Blockierte Nummern"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> ist ungültig."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> ist bereits blockiert."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Anrufblockierung für 48 Stunden deaktiviert"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Deaktiviert, da ein Notruf getätigt wurde."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Anrufkonten"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Aktivieren"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Berechtigungen festlegen"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Aktiviere die Berechtigung \"Kontakte\", um die Kurzwahlfunktion zu verwenden."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Aktiviere die Berechtigung \"Telefon\", um dein Anrufprotokoll abzurufen."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Aktiviere die Berechtigung \"Kontakte\", um deine Kontakte abzurufen."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Aktiviere die Berechtigung \"Telefon\", um auf deine Mailbox zuzugreifen."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Aktiviere die Berechtigungen \"Kontakte\", um deine Kontakte zu suchen."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Aktiviere die Berechtigung \"Telefon\", um einen Anruf zu tätigen."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Die App \"Telefon\" ist nicht berechtigt, die Systemeinstellungen zu überschreiben."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Blockiert"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ist aktiv"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blockieren/Spam melden"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blockieren"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Kein Spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Blockierung aufheben"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> blockieren?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Zukünftige Anrufe und Sprachnachrichten von dieser Nummer werden blockiert."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Anruf als Spam melden"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Zukünftige Anrufe und Sprachnachrichten von dieser Nummer werden blockiert. Der Anruf wird als Spam gemeldet."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Blockierung von <xliff:g id="NUMBER">%1$s</xliff:g> aufheben?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Die Blockierung dieser Nummer wird aufgehoben und sie wird als \"Kein Spam\" gemeldet. Zukünftige Anrufe und Sprachnachrichten werden nicht als Spam gekennzeichnet."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> zu weißer Liste hinzufügen?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Weiße Liste"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Zukünftige Anrufe und Sprachnachrichten von dieser Nummer werden nicht als Spam gekennzeichnet. Die Nummer wird als \"Kein Spam\" gemeldet."</string>
-</resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
deleted file mode 100644
index a6bbdfd27..000000000
--- a/res/values-el/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Τηλέφωνο"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Τηλέφωνο"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Πληκτρολόγιο κλήσης τηλεφώνου"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Τηλέφωνο"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Ιστορικό κλήσεων"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Αναφορά ανακριβούς αριθμού"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Αντιγραφή αριθμού"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Αντιγραφή μεταγραφής"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Αποκλεισμός αριθμού"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Ο αριθμός <xliff:g id="NUMBER">%1$s</xliff:g> αποκλείστηκε"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Κατάργηση αποκλεισμού αριθμού"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Ο αποκλεισμός του αριθμού <xliff:g id="NUMBER">%1$s</xliff:g> καταργήθηκε"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ΑΝΑΙΡΕΣΗ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Διαγραφή"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Επεξεργασία αριθμού πριν την κλήση"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Διαγραφή ιστορικού κλήσεων"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Διαγραφή αυτόματου τηλεφωνητή"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Αρχειοθ. μην. αυτόμ. τηλεφων."</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Κοινή χρήση αυτόμ. τηλεφωνητή"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Μήν.αυτ. τηλ. διαγρ."</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Μήν. αυτ. τηλεφ. αρχειοθετήθ."</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ΑΝΑΙΡΕΣΗ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ΜΤΒ ΣΕ ΑΡΧ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Διαγραφή ιστορικού κλήσεων;"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Αυτό θα διαγράψει όλες τις κλήσεις από το ιστορικό σας"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Διαγραφή ιστορικού κλήσεων…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Τηλέφωνο"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Αναπάντητη κλήση"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Αναπάντητη κλήση εργασίας"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Αναπάντητες κλήσεις"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> αναπάντητες κλήσεις"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Επανάκληση"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Μήνυμα"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Μηνύματα αυτόμ. τηλεφωνητή </item>
- <item quantity="one">Μήνυμα αυτόματου τηλεφωνητή</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Αναπαραγωγή"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Νέα μην. αυτ. τηλεφ. από <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Αδύνατη αναπαραγωγή φωνητικού μηνύματος"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Φόρτωση φωνητικών μηνυμάτων…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Αρχειοθέτηση μηνυμ. αυτόμ. τηλεφωνητή…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Αδύνατη φόρτωση φωνητικών μηνυμάτων"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Μόνο κλήσεις με ηχητικά μηνύματα"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Μόνο εισερχόμενες κλήσεις"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Μόνο εξερχόμενες κλήσεις"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Μόνο αναπάντητες κλήσεις"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Οπτικός αυτόματος τηλεφωνητής"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Δείτε και ακούστε τα μηνύματα αυτόματου τηλεφωνητή, χωρίς να καλέσετε κάποιον αριθμό. Ενδέχεται να ισχύουν χρεώσεις δεδομένων."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Ρυθμίσεις"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Μη διαθέσιμες ενημερώσεις αυτόματου τηλεφωνητή"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Νέο φωνητικό μήνυμα σε αναμονή. Δεν είναι δυνατή η φόρτωση τώρα."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Ρύθμιση του αυτόματου τηλεφωνητή σας"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Δεν υπάρχει διαθέσιμος ήχος"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Ρύθμιση"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Κλήση αυτόμ. τηλεφ."</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Επιλέξτε αριθμό"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Επιλέξτε αριθμό"</string>
- <string name="make_primary" msgid="5829291915305113983">"Διατήρηση αυτής της ρύθμισης"</string>
- <string name="description_search_button" msgid="3660807558587384889">"αναζήτηση"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"κλήση"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"αριθμός για κλήση"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Αναπαραγωγή ή διακοπή αναπαραγωγής"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Ενεργοποίηση ή απενεργοποίηση μεγαφώνου"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Αναζήτηση θέσης αναπαραγωγής"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Μείωση ρυθμού αναπαραγωγής"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Αύξηση ρυθμού αναπαραγωγής"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Ιστορικό κλήσεων"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Περισσότερες επιλογές"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"πληκτρολόγιο κλήσης"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Εμφάνιση μόνο εξερχόμενων"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Εμφάνιση μόνο εισερχόμενων"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Εμφάνιση μόνο αναπάντητων"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Εμφ. μόνο μην. αυτόμ. τηλεφων."</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Εμφάνιση όλων"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Προσθήκη παύσης 2 δευτερολέπτων"</string>
- <string name="add_wait" msgid="3360818652790319634">"Προσθήκη αναμονής"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Ρυθμίσεις"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Νέα επαφή"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Όλες οι επαφές"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Στοιχ. κλήσης"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Οι λεπτομέρειες δεν είναι διαθέσιμες"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Χρησιμοποιήστε το πληκτρολόγιο αφής ηχητικών τόνων"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Επιστροφή στην κλήση που βρίσκεται σε εξέλιξη"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Προσθήκη κλήσης"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Εισερχόμενη κλήση"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Εξερχόμενη κλήση"</string>
- <string name="type_missed" msgid="2720502601640509542">"Αναπάντητη κλήση"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Εισερχόμενη βιντεοκλήση"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Εξερχόμενη βιντεοκλήση"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Αναπάντητη βιντεοκλήση"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Αυτόματος τηλεφωνητής"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Απορριφθείσα κλήση"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Αποκλεισμένη κλήση"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Εισερχόμενες κλήσεις"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Αναπαραγωγή μηνύματος αυτόματου τηλεφωνητή"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Προβολή επαφής <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Κλήση του χρήστη <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Στοιχεία επικοινωνίας του <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> κλήσεις."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Βιντεοκλήση."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Αποστολή SMS στο χρήστη <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Μηνύματα αυτόματου τηλεφωνητή που δεν έχετε ακούσει"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Έναρξη φωνητικής αναζήτησης"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Κλήση <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Άγνωστος"</string>
- <string name="voicemail" msgid="3851469869202611441">"Αυτόματος τηλεφωνητής"</string>
- <string name="private_num" msgid="6374339738119166953">"Απόκρυψη"</string>
- <string name="payphone" msgid="7726415831153618726">"Τηλέφωνο με χρέωση"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> δευτερόλεπτα"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> λεπτά <xliff:g id="SECONDS">%s</xliff:g> δευτερόλεπτα"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> στις <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Δεν μπορείτε να καλέσετε αυτόν τον αριθμό"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Για τη r;yumish του αυτόματου τηλεφωνητή, μεταβείτε στο στοιχείο Μενού &gt; Ρυθμίσεις."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Για κλήση αυτόματου τηλεφωνητή, πρώτα απενεργοποιήστε τη λειτουργία πτήσης."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Φόρτωση…"</string>
- <string name="imei" msgid="3045126336951684285">"Αριθμός ΙΜΕΙ"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Φόρτωση από κάρτα SIM…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"Επαφές στην κάρτα SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Δεν υπάρχουν διαθέσιμες εφαρμογές επαφών"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Η φωνητική αναζήτηση δεν είναι διαθέσιμη"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Δεν είναι δυνατή η πραγματοποίηση τηλεφωνικής κλήσης επειδή η εφαρμογή \"Τηλέφωνο\" έχει απενεργοποιηθεί."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Δεν υπάρχει εφαρμογή γι\' αυτήν την ενέργεια σε αυτήν τη συσκευή"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Αναζήτηση στις επαφές"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Προσθήκη αριθμού ή αναζήτηση επαφών"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Το ιστορικό κλήσεων είναι κενό"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Πραγματοποίηση κλήσης"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Δεν υπάρχουν αναπάντητες κλήσεις."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Ο φάκελος εισερχομένων του αυτόματου τηλεφωνητή σας είναι κενός."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Δεν υπάρχουν αρχειοθετημένα μηνύματα αυτόματου τηλεφωνητή."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Εμφάνιση μόνο των αγαπημένων"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Ιστορικό κλήσεων"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Αρχειοθετημένα μηνύματα αυτόματου τηλεφωνητή"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Όλα"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Αναπάντητες"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Φωνητικό μήνυμα"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Νέος, απλούστερος αποκλεισμός"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Για την καλύτερη δυνατή προστασία σας, το Phone πρέπει να αλλάξει τον τρόπο με τον οποίο λειτουργεί ο αποκλεισμός. Οι αποκλεισμένοι αριθμοί σας δεν θα επιτρέπουν πλέον τη λήψη τόσο κλήσεων όσο και μηνυμάτων κειμένου και μπορεί να κοινοποιηθούν σε άλλες εφαρμογές."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Αποδοχή"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Να αποκλειστεί ο αριθμός <xliff:g id="NUMBER">%1$s</xliff:g>;"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Οι μελλοντικές κλήσεις από αυτόν τον αριθμό θα αποκλείονται και τα μηνύματα στον αυτόματο τηλεφωνητή θα διαγράφονται αυτόματα."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Οι κλήσεις από αυτόν τον αριθμό θα αποκλείονται, αλλά ο καλών ενδέχεται να μπορεί να αφήνει μηνύματα στον αυτόματο τηλεφωνητή."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Δεν θα λαμβάνετε πλέον κλήσεις ή μηνύματα κειμένου από αυτόν τον αριθμό."</string>
- <string name="block_number_ok" msgid="770551992296781873">"ΑΠΟΚΛΕΙΣΜΟΣ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Να καταργηθεί ο αποκλεισμός του αριθμού <xliff:g id="NUMBER">%1$s</xliff:g>;"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ΚΑΤΑΡΓΗΣΗ ΑΠΟΚΛΕΙΣΜΟΥ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Γρήγορη κλήση"</string>
- <string name="tab_history" msgid="2563144697322434940">"Ιστορικό κλήσεων"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Επαφές"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Αυτόματος τηλεφωνητής"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Καταργήθηκε από τα αγαπημένα"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Αναίρεση"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Καλέστε το <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Δημιουργία νέας επαφής"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Προσθήκη σε μια επαφή"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Αποστολή SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Πραγματοποίηση βιντεοκλήσης"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Αποκλεισμός αριθμού"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> νέες αναπάντητες κλήσεις"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Δεν έχετε ορίσει ακόμη κάποια επαφή στις ταχείες κλήσεις"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Προσθέστε ένα αγαπημένο"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Δεν έχετε επαφές ακόμη"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Προσθήκη επαφής"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Πατήστε την εικόνα για να δείτε όλους τους αριθμούς ή αγγίξτε παρατεταμένα για αναδιάταξη"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Κατάργηση"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Βιντεοκλήση"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Αποστολή μηνύματος"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Λεπτομέρειες κλήσης"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Κλήση <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Αναπάντητη κλήση από <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Ληφθείσα κλήση από <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Μη αναγνωσμένο μήνυμα αυτόματου τηλεφωνητή από <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Μήνυμα αυτόματου τηλεφωνητή από <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Κλήση προς <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"σε <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"μέσω <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"μέσω <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"στον λογαριασμό <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, μέσω <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> μέσω <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Κλήση"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Κλήση <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Βιντεοκλήση <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Ακρόαση μηνύματος αυτόματου τηλεφωνητή από το χρήστη <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Ακρόαση μηνύματος αυτόματου τηλεφωνητή από <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Παύση μηνύματος αυτόματου τηλεφωνητή από <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Διαγραφή μηνύματος αυτόματου τηλεφωνητή από <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> νέα φωνητικά μηνύματα</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> νέο φωνητικό μήνυμα</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Δημιουργία επαφής για <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Προσθήκη <xliff:g id="NAMEORNUMBER">^1</xliff:g> σε υπάρχουσα επαφή"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Λεπτομέρειες κλήσης για <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Διαγράφηκε από το ιστορικό κλήσεων"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Σήμερα"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Χθες"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Παλαιότερες"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Λίστα κλήσεων"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Ενεργοποίηση ηχείου."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Απενεργοποίηση ηχείου."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Αναπαραγωγή πιο γρήγορα."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Αναπαραγωγή πιο αργά."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Έναρξη ή παύση αναπαραγωγής."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Επιλογές εμφάνισης"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Ήχοι και δόνηση"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Προσβασιμότητα"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Ήχος κλήσης τηλεφώνου"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Δόνηση στις κλήσεις"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Τόνοι πληκτρολογίου κλήσης"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Διάρκεια ήχου πληκτρολογίου κλήσης"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Κανονική"</item>
- <item msgid="6177579030803486015">"Παρατεταμένη"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Γρήγορες απαντήσεις"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Κλήσεις"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Φραγή κλήσεων"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Προσωρινά απενεργοποιημένη φραγή κλήσεων"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Η φραγή κλήσεων έχει απενεργοποιηθεί προσωρινά επειδή επικοινωνήσατε με τις υπηρεσίες έκτακτης ανάγκης από αυτό το τηλέφωνο μέσα στις τελευταίες 48 ώρες. Θα ενεργοποιηθεί ξανά αυτόματα μόλις λήξει η περίοδος των 48 ωρών."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Εισαγωγή αριθμών"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Στο παρελθόν ρυθμίσατε ορισμένους καλούντες ώστε να αποστέλλονται αυτόματα στον τηλεφωνητή μέσω άλλων εφαρμογών."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Προβολή αριθμών"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Εισαγωγή"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Αποτυχία εισαγωγής"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Απέτυχε η αρχειοθέτ. μην. αυτόμ. τηλεφ."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Κατάργηση αποκλεισμού αριθμού"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Προσθήκη αριθμού"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Οι κλήσεις από αυτούς τους αριθμούς θα αποκλείονται και τα μηνύματα στον αυτόματο τηλεφωνητή θα διαγράφονται αυτόματα."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Οι κλήσεις από αυτούς τους αριθμούς θα αποκλείονται, αλλά ενδέχεται να λαμβάνετε από αυτούς μηνύματα στον αυτόματο τηλεφωνητή."</string>
- <string name="block_list" msgid="7760188925338078011">"Αποκλεισμένοι αριθμοί"</string>
- <string name="invalidNumber" msgid="619058581062192851">"Ο αριθμός <xliff:g id="NUMBER">%1$s</xliff:g> δεν είναι έγκυρος."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"Ο αριθμός <xliff:g id="NUMBER">%1$s</xliff:g> αποκλείστηκε ήδη."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Η φραγή κλήσεων απενεργοποιήθηκε για 48 ώρες"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Απενεργοποιημένο επειδή πραγματοποιήθηκε κλήση έκτακτης ανάγκης."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Λογαριασμοί κλήσης"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Ενεργοποίηση"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Ορισμός αδειών"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Για να ενεργοποιήσετε την ταχεία κλήση, ενεργοποιήστε την άδεια επαφών."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Για να δείτε το αρχείο καταγραφής κλήσεών σας, ενεργοποιήστε την άδεια τηλεφώνου."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Για να δείτε τις επαφές σας, ενεργοποιήστε την άδεια επαφών."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Για να αποκτήσετε πρόσβαση στον τηλεφωνητή σας, ενεργοποιήστε την άδεια τηλεφώνου."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Για να κάνετε αναζήτηση στις επαφές σας, ενεργοποιήστε τις άδειες \"Επαφές\"."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Για να πραγματοποιήσετε μια κλήση, ενεργοποιήστε την άδεια τηλεφώνου."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Η εφαρμογή \"Τηλέφωνο\" δεν έχει άδεια εγγραφής στις ρυθμίσεις συστήματος."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Αποκλεισμένος"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"Ο χρήστης <xliff:g id="NAMEORNUMBER">^1</xliff:g> είναι ενεργός"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Αποκλεισμός/αναφορά ανεπιθύμ."</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Αποκλεισμός"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Μη ανεπιθύμητος"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Κατάργηση αποκλεισμού"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Ανεπιθύμητα"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Να αποκλειστεί ο αριθμός <xliff:g id="NUMBER">%1$s</xliff:g>;"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Οι μελλοντικές κλήσεις και φωνητικά μηνύματα από αυτόν τον αριθμό θα αποκλειστούν."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Αναφορά κλήσ. ως ανεπιθύμητης;"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Μελλοντικές κλ. και φων. μηνύμ. από αυτόν τον αρ. θα αποκλειστούν. Η κλήση θα αναφερθεί ως ανεπιθύμ."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Να καταργηθεί ο αποκλεισμός του αριθμού <xliff:g id="NUMBER">%1$s</xliff:g>;"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Ο αποκλ. του αρ. θα καταργ. Θα αναφερθεί ως μη ανεπιθ. Μελλοντικές κλ. και φων. μην. δεν θα αναγν. ως ανεπιθ."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Προσθήκη αριθμού <xliff:g id="NUMBER">%1$s</xliff:g> στη λίστα επιτρεπόμενων;"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Λίστα επιτρεπόμενων"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Μελλοντ. κλ. και φων. μην. από τον αρ. δεν θα αναγνωρ. ως ανεπιθ. Αυτός ο αρ. θα αναφερθεί ως μη ανεπιθ."</string>
-</resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
deleted file mode 100644
index d6d8b8d31..000000000
--- a/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Phone"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Phone"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Phone Dialpad"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Phone"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Call history"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Report inaccurate number"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Copy number"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Copy transcription"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Block number"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> blocked"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Unblock number"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> unblocked"</string>
- <string name="block_number_undo" msgid="591338370336724156">"UNDO"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"delete"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Edit number before call"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Clear call history"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Delete voicemail"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Archive voicemail"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Share voicemail"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Voicemail deleted"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Voicemail archived"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"UNDO"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"TO ARCHIVE"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Clear call history?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"This will delete all calls from your history"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Clearing call history…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Phone"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Missed call"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Missed work call"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Missed calls"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> missed calls"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Call back"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Message"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Voicemails </item>
- <item quantity="one">Voicemail</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Play"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"New voicemail from <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Couldn\'t play voicemail"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Loading voicemail…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Archiving voicemail…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Couldn\'t load voicemail"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Calls with voicemail only"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Incoming calls only"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Outgoing calls only"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Missed calls only"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Visual voicemail"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"See and listen to your voicemail, without having to call a number. Data charges may apply."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Settings"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Voicemail updates not available"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"New voicemail waiting. Can\'t load right now."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Set up your voicemail"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio not available"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Set up"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Call voicemail"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Choose number"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Choose number"</string>
- <string name="make_primary" msgid="5829291915305113983">"Remember this choice"</string>
- <string name="description_search_button" msgid="3660807558587384889">"search"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"dial"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"number to dial"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Play or stop playback"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Switch on or off speakerphone"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Seek playback position"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Decrease playback rate"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Increase playback rate"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Call history"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"More options"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"dial pad"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Show outgoing only"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Show incoming only"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Show missed only"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Show voicemails only"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Show all calls"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Add 2-sec pause"</string>
- <string name="add_wait" msgid="3360818652790319634">"Add wait"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Settings"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"New contact"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"All contacts"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Call details"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Details not available"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Use touch tone keypad"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Return to call in progress"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Add call"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Incoming call"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Outgoing call"</string>
- <string name="type_missed" msgid="2720502601640509542">"Missed call"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Incoming video call"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Outgoing video call"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Missed video call"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Voicemail"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Declined call"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blocked call"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Incoming calls"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Play voicemail"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"View contact <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Call <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Contact details for <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> calls."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Video call."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Send SMS to <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Unheard voicemail"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Start voice search"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Call <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Unknown"</string>
- <string name="voicemail" msgid="3851469869202611441">"Voicemail"</string>
- <string name="private_num" msgid="6374339738119166953">"Private number"</string>
- <string name="payphone" msgid="7726415831153618726">"Payphone"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> sec"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> sec"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> at <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Can\'t call this number"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"To set up voicemail, go to Menu &gt; Settings."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"To call voicemail, first turn off Aeroplane mode."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Loading…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Loading from SIM card…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM card contacts"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"No contacts app available"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Voice search not available"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Cannot make a phone call because the Phone application has been disabled."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"No app for that on this device"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Search contacts"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Add number or search contacts"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Your call history is empty"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Make a call"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"You have no missed calls."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Your voicemail inbox is empty."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Your voicemail archive is empty."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Show favourites only"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Call history"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Voicemail archive"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"All"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Missed"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Voicemail"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"New, simplified blocking"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"To protect you better, Phone needs to change how blocking works. Your blocked numbers will now stop both calls and texts and may be shared with other apps."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Allow"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Block <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Calls from this number will be blocked and voicemails will be automatically deleted."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Calls from this number will be blocked, but the caller may still be able to leave you voicemails."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"You will no longer receive calls or texts from this number."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOCK"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Unblock <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"UNBLOCK"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Speed dial"</string>
- <string name="tab_history" msgid="2563144697322434940">"Call history"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Contacts"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Voicemail"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Removed from favourites"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Undo"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Call <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Create new contact"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Add to a contact"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Send SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Make video call"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Block number"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> new missed calls"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"No one is on your speed dial yet"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Add a favourite"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"You don\'t have any contacts yet"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Add a contact"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Touch image to see all numbers or touch &amp; hold to reorder"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Remove"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Video call"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Send a message"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Call details"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Call <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Missed call from <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Answered call from <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Unread voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Call to <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"on <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"via <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"via <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"on <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, via <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, via <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Call"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Call <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Video call <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Listen to voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Play voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pause voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Delete voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> new voicemails</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> new voicemail</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Create contact for <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Add <xliff:g id="NAMEORNUMBER">^1</xliff:g> to existing contact"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Call details for <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Deleted from call history"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Today"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Yesterday"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Older"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Calls list"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Turn speaker on."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Turn speaker off."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Play faster."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Play slower."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Start or pause playback."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Display options"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Sounds and vibration"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Accessibility"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Phone ringtone"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Also vibrate for calls"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Dialpad tones"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Dialpad tone length"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"Long"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Quick responses"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Calls"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Call blocking"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Call blocking temporarily off"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Call blocking has been disabled because you contacted emergency services from this phone within the last 48 hours. It will be automatically re-enabled once the 48 hour period expires."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Import numbers"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"You previously marked some callers to be automatically sent to voicemail via other apps."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"View Numbers"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Import"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Import failed"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Failed to archive voicemail."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Unblock number"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Add number"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Calls from these numbers will be blocked and voicemails will be automatically deleted."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Calls from these numbers will be blocked, but they may still be able to leave you voicemails."</string>
- <string name="block_list" msgid="7760188925338078011">"Blocked numbers"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> is invalid."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> is already blocked."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Call blocking disabled for 48 hours"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Disabled because an emergency call was made."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Calling accounts"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Turn on"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Set permissions"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"To enable speed dial, turn on the Contacts permission."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"To see your call log, turn on the Phone permission."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"To see your contacts, turn on the Contacts permission."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"To access your voicemail, turn on the Phone permission."</string>
- <string name="permission_no_search" msgid="84152933267902056">"To search your contacts, turn on the Contacts permissions."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"To place a call, turn on the Phone permission."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Phone app does not have permission to write to system settings."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Blocked"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> is active"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Block/report spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Block"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Not spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Unblock"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Block <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Future calls and voicemail from this number will be blocked."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Report call as spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Future calls and voicemail from this number will be blocked. This call will be reported as spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Unblock <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"No. to be unblocked and reported as not spam. Future calls &amp; voicemail won\'t be identified as spam."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Whitelist <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Whitelist"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Future calls &amp; voicemail from this no. won\'t be called spam. Number will be reported as not spam."</string>
-</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
deleted file mode 100644
index d6d8b8d31..000000000
--- a/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Phone"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Phone"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Phone Dialpad"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Phone"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Call history"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Report inaccurate number"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Copy number"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Copy transcription"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Block number"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> blocked"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Unblock number"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> unblocked"</string>
- <string name="block_number_undo" msgid="591338370336724156">"UNDO"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"delete"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Edit number before call"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Clear call history"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Delete voicemail"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Archive voicemail"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Share voicemail"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Voicemail deleted"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Voicemail archived"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"UNDO"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"TO ARCHIVE"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Clear call history?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"This will delete all calls from your history"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Clearing call history…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Phone"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Missed call"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Missed work call"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Missed calls"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> missed calls"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Call back"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Message"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Voicemails </item>
- <item quantity="one">Voicemail</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Play"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"New voicemail from <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Couldn\'t play voicemail"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Loading voicemail…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Archiving voicemail…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Couldn\'t load voicemail"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Calls with voicemail only"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Incoming calls only"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Outgoing calls only"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Missed calls only"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Visual voicemail"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"See and listen to your voicemail, without having to call a number. Data charges may apply."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Settings"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Voicemail updates not available"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"New voicemail waiting. Can\'t load right now."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Set up your voicemail"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio not available"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Set up"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Call voicemail"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Choose number"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Choose number"</string>
- <string name="make_primary" msgid="5829291915305113983">"Remember this choice"</string>
- <string name="description_search_button" msgid="3660807558587384889">"search"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"dial"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"number to dial"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Play or stop playback"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Switch on or off speakerphone"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Seek playback position"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Decrease playback rate"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Increase playback rate"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Call history"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"More options"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"dial pad"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Show outgoing only"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Show incoming only"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Show missed only"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Show voicemails only"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Show all calls"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Add 2-sec pause"</string>
- <string name="add_wait" msgid="3360818652790319634">"Add wait"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Settings"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"New contact"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"All contacts"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Call details"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Details not available"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Use touch tone keypad"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Return to call in progress"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Add call"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Incoming call"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Outgoing call"</string>
- <string name="type_missed" msgid="2720502601640509542">"Missed call"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Incoming video call"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Outgoing video call"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Missed video call"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Voicemail"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Declined call"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blocked call"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Incoming calls"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Play voicemail"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"View contact <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Call <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Contact details for <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> calls."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Video call."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Send SMS to <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Unheard voicemail"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Start voice search"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Call <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Unknown"</string>
- <string name="voicemail" msgid="3851469869202611441">"Voicemail"</string>
- <string name="private_num" msgid="6374339738119166953">"Private number"</string>
- <string name="payphone" msgid="7726415831153618726">"Payphone"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> sec"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> sec"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> at <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Can\'t call this number"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"To set up voicemail, go to Menu &gt; Settings."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"To call voicemail, first turn off Aeroplane mode."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Loading…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Loading from SIM card…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM card contacts"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"No contacts app available"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Voice search not available"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Cannot make a phone call because the Phone application has been disabled."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"No app for that on this device"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Search contacts"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Add number or search contacts"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Your call history is empty"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Make a call"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"You have no missed calls."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Your voicemail inbox is empty."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Your voicemail archive is empty."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Show favourites only"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Call history"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Voicemail archive"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"All"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Missed"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Voicemail"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"New, simplified blocking"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"To protect you better, Phone needs to change how blocking works. Your blocked numbers will now stop both calls and texts and may be shared with other apps."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Allow"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Block <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Calls from this number will be blocked and voicemails will be automatically deleted."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Calls from this number will be blocked, but the caller may still be able to leave you voicemails."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"You will no longer receive calls or texts from this number."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOCK"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Unblock <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"UNBLOCK"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Speed dial"</string>
- <string name="tab_history" msgid="2563144697322434940">"Call history"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Contacts"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Voicemail"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Removed from favourites"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Undo"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Call <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Create new contact"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Add to a contact"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Send SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Make video call"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Block number"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> new missed calls"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"No one is on your speed dial yet"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Add a favourite"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"You don\'t have any contacts yet"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Add a contact"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Touch image to see all numbers or touch &amp; hold to reorder"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Remove"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Video call"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Send a message"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Call details"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Call <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Missed call from <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Answered call from <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Unread voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Call to <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"on <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"via <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"via <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"on <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, via <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, via <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Call"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Call <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Video call <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Listen to voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Play voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pause voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Delete voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> new voicemails</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> new voicemail</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Create contact for <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Add <xliff:g id="NAMEORNUMBER">^1</xliff:g> to existing contact"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Call details for <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Deleted from call history"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Today"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Yesterday"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Older"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Calls list"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Turn speaker on."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Turn speaker off."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Play faster."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Play slower."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Start or pause playback."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Display options"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Sounds and vibration"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Accessibility"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Phone ringtone"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Also vibrate for calls"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Dialpad tones"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Dialpad tone length"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"Long"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Quick responses"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Calls"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Call blocking"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Call blocking temporarily off"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Call blocking has been disabled because you contacted emergency services from this phone within the last 48 hours. It will be automatically re-enabled once the 48 hour period expires."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Import numbers"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"You previously marked some callers to be automatically sent to voicemail via other apps."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"View Numbers"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Import"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Import failed"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Failed to archive voicemail."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Unblock number"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Add number"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Calls from these numbers will be blocked and voicemails will be automatically deleted."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Calls from these numbers will be blocked, but they may still be able to leave you voicemails."</string>
- <string name="block_list" msgid="7760188925338078011">"Blocked numbers"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> is invalid."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> is already blocked."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Call blocking disabled for 48 hours"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Disabled because an emergency call was made."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Calling accounts"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Turn on"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Set permissions"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"To enable speed dial, turn on the Contacts permission."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"To see your call log, turn on the Phone permission."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"To see your contacts, turn on the Contacts permission."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"To access your voicemail, turn on the Phone permission."</string>
- <string name="permission_no_search" msgid="84152933267902056">"To search your contacts, turn on the Contacts permissions."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"To place a call, turn on the Phone permission."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Phone app does not have permission to write to system settings."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Blocked"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> is active"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Block/report spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Block"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Not spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Unblock"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Block <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Future calls and voicemail from this number will be blocked."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Report call as spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Future calls and voicemail from this number will be blocked. This call will be reported as spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Unblock <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"No. to be unblocked and reported as not spam. Future calls &amp; voicemail won\'t be identified as spam."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Whitelist <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Whitelist"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Future calls &amp; voicemail from this no. won\'t be called spam. Number will be reported as not spam."</string>
-</resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
deleted file mode 100644
index d6d8b8d31..000000000
--- a/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Phone"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Phone"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Phone Dialpad"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Phone"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Call history"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Report inaccurate number"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Copy number"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Copy transcription"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Block number"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> blocked"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Unblock number"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> unblocked"</string>
- <string name="block_number_undo" msgid="591338370336724156">"UNDO"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"delete"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Edit number before call"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Clear call history"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Delete voicemail"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Archive voicemail"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Share voicemail"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Voicemail deleted"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Voicemail archived"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"UNDO"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"TO ARCHIVE"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Clear call history?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"This will delete all calls from your history"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Clearing call history…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Phone"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Missed call"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Missed work call"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Missed calls"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> missed calls"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Call back"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Message"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Voicemails </item>
- <item quantity="one">Voicemail</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Play"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"New voicemail from <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Couldn\'t play voicemail"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Loading voicemail…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Archiving voicemail…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Couldn\'t load voicemail"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Calls with voicemail only"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Incoming calls only"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Outgoing calls only"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Missed calls only"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Visual voicemail"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"See and listen to your voicemail, without having to call a number. Data charges may apply."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Settings"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Voicemail updates not available"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"New voicemail waiting. Can\'t load right now."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Set up your voicemail"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio not available"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Set up"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Call voicemail"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Choose number"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Choose number"</string>
- <string name="make_primary" msgid="5829291915305113983">"Remember this choice"</string>
- <string name="description_search_button" msgid="3660807558587384889">"search"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"dial"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"number to dial"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Play or stop playback"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Switch on or off speakerphone"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Seek playback position"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Decrease playback rate"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Increase playback rate"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Call history"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"More options"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"dial pad"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Show outgoing only"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Show incoming only"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Show missed only"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Show voicemails only"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Show all calls"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Add 2-sec pause"</string>
- <string name="add_wait" msgid="3360818652790319634">"Add wait"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Settings"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"New contact"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"All contacts"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Call details"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Details not available"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Use touch tone keypad"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Return to call in progress"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Add call"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Incoming call"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Outgoing call"</string>
- <string name="type_missed" msgid="2720502601640509542">"Missed call"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Incoming video call"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Outgoing video call"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Missed video call"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Voicemail"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Declined call"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blocked call"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Incoming calls"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Play voicemail"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"View contact <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Call <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Contact details for <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> calls."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Video call."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Send SMS to <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Unheard voicemail"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Start voice search"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Call <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Unknown"</string>
- <string name="voicemail" msgid="3851469869202611441">"Voicemail"</string>
- <string name="private_num" msgid="6374339738119166953">"Private number"</string>
- <string name="payphone" msgid="7726415831153618726">"Payphone"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> sec"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> sec"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> at <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Can\'t call this number"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"To set up voicemail, go to Menu &gt; Settings."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"To call voicemail, first turn off Aeroplane mode."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Loading…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Loading from SIM card…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM card contacts"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"No contacts app available"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Voice search not available"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Cannot make a phone call because the Phone application has been disabled."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"No app for that on this device"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Search contacts"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Add number or search contacts"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Your call history is empty"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Make a call"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"You have no missed calls."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Your voicemail inbox is empty."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Your voicemail archive is empty."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Show favourites only"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Call history"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Voicemail archive"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"All"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Missed"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Voicemail"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"New, simplified blocking"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"To protect you better, Phone needs to change how blocking works. Your blocked numbers will now stop both calls and texts and may be shared with other apps."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Allow"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Block <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Calls from this number will be blocked and voicemails will be automatically deleted."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Calls from this number will be blocked, but the caller may still be able to leave you voicemails."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"You will no longer receive calls or texts from this number."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOCK"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Unblock <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"UNBLOCK"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Speed dial"</string>
- <string name="tab_history" msgid="2563144697322434940">"Call history"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Contacts"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Voicemail"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Removed from favourites"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Undo"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Call <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Create new contact"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Add to a contact"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Send SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Make video call"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Block number"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> new missed calls"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"No one is on your speed dial yet"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Add a favourite"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"You don\'t have any contacts yet"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Add a contact"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Touch image to see all numbers or touch &amp; hold to reorder"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Remove"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Video call"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Send a message"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Call details"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Call <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Missed call from <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Answered call from <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Unread voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Call to <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"on <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"via <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"via <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"on <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, via <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, via <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Call"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Call <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Video call <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Listen to voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Play voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pause voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Delete voicemail from <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> new voicemails</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> new voicemail</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Create contact for <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Add <xliff:g id="NAMEORNUMBER">^1</xliff:g> to existing contact"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Call details for <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Deleted from call history"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Today"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Yesterday"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Older"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Calls list"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Turn speaker on."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Turn speaker off."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Play faster."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Play slower."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Start or pause playback."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Display options"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Sounds and vibration"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Accessibility"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Phone ringtone"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Also vibrate for calls"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Dialpad tones"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Dialpad tone length"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"Long"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Quick responses"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Calls"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Call blocking"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Call blocking temporarily off"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Call blocking has been disabled because you contacted emergency services from this phone within the last 48 hours. It will be automatically re-enabled once the 48 hour period expires."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Import numbers"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"You previously marked some callers to be automatically sent to voicemail via other apps."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"View Numbers"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Import"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Import failed"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Failed to archive voicemail."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Unblock number"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Add number"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Calls from these numbers will be blocked and voicemails will be automatically deleted."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Calls from these numbers will be blocked, but they may still be able to leave you voicemails."</string>
- <string name="block_list" msgid="7760188925338078011">"Blocked numbers"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> is invalid."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> is already blocked."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Call blocking disabled for 48 hours"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Disabled because an emergency call was made."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Calling accounts"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Turn on"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Set permissions"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"To enable speed dial, turn on the Contacts permission."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"To see your call log, turn on the Phone permission."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"To see your contacts, turn on the Contacts permission."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"To access your voicemail, turn on the Phone permission."</string>
- <string name="permission_no_search" msgid="84152933267902056">"To search your contacts, turn on the Contacts permissions."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"To place a call, turn on the Phone permission."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Phone app does not have permission to write to system settings."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Blocked"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> is active"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Block/report spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Block"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Not spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Unblock"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Block <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Future calls and voicemail from this number will be blocked."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Report call as spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Future calls and voicemail from this number will be blocked. This call will be reported as spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Unblock <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"No. to be unblocked and reported as not spam. Future calls &amp; voicemail won\'t be identified as spam."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Whitelist <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Whitelist"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Future calls &amp; voicemail from this no. won\'t be called spam. Number will be reported as not spam."</string>
-</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
deleted file mode 100644
index 9d4812305..000000000
--- a/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Teléfono"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Teléfono"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Teclado del teléfono"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Teléfono"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Historial de llamadas"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Informar número incorrecto"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Copiar número"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Copiar transcripción"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Bloquear número"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> bloqueado"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Desbloquear número"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> desbloqueado"</string>
- <string name="block_number_undo" msgid="591338370336724156">"DESHACER"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Borrar"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Editar número antes de realizar llamada"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Eliminar el historial de llamadas"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Borrar buzón de voz"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Archivar un mensaje de voz"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Compartir buzón de voz"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Buzón de voz borrado"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Se archivó el mensaje de voz"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"DESHACER"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"IR A ARCHIVO"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"¿Eliminar el historial de llamadas?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Se borrarán todas las llamadas del historial."</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Eliminando historial de llamadas…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Teléfono"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Llamada perdida"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Llamada de trabajo perdida"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Llamadas perdidas"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> llamadas perdidas"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Llamar"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Mensaje"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> mensajes de voz </item>
- <item quantity="one">mensaje de voz</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Reproducir"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nuevo mensaje de voz de <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Error al reproducir el buzón de voz"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Cargando buzón de voz…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Archivando el mensaje de voz…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Error al cargar el buzón de voz"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Solo llamadas con buzón de voz"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Solo llamadas entrantes"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Solo llamadas salientes"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Solo llamadas perdidas"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Buzón de voz visual"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Puedes ver y escuchar los buzones de voz sin tener que llamar a ningún número. Es posible que se apliquen cargos de datos."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Configuración"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"No hay notificaciones disponibles del buzón de voz."</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Nuevo mensaje de buzón de voz en espera; no se puede cargar."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Configurar el buzón de voz"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio no disponible"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Configurar"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Llamar buzón de voz"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Elige un número"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Elige un número"</string>
- <string name="make_primary" msgid="5829291915305113983">"Recuerda esta opción"</string>
- <string name="description_search_button" msgid="3660807558587384889">"búsqueda"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"marcar"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"número para marcar"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Reproducir o detener la reproducción"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Activar o desactivar el altavoz"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Busca la posición de reproducción"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Disminuir la velocidad de reproducción"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Aumentar la velocidad de reproducción"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Historial de llamadas"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Más opciones"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"teclado"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Mostrar solo salientes"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Mostrar solo entrantes"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Mostrar solo perdidas"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Mostrar solo mensajes de voz"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Mostrar todas las llamadas"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Agregar pausa de 2 segundos"</string>
- <string name="add_wait" msgid="3360818652790319634">"Agregar espera"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Configuración"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Nuevo contacto"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Todos los contactos"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Detalles de llamada"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Detalles no disponibles"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Usar teclado numérico"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Regresar a la llamada en curso"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Agregar llamada"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Llamada entrante"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Llamada saliente"</string>
- <string name="type_missed" msgid="2720502601640509542">"Llamada perdida"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Videollamada entrante"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Videollamada saliente"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Te perdiste una videollamada."</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Mensaje de voz"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Llamada rechazada"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Llamada bloqueada"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Llamadas entrantes"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Reproducir mensaje de voz"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Ver contacto <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Llamar a <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Datos de contacto de <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> llamadas"</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videollamada"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Enviar SMS a <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Buzón de voz no escuchado"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Iniciar búsqueda por voz"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Llamar al <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Desconocido"</string>
- <string name="voicemail" msgid="3851469869202611441">"Correo de voz"</string>
- <string name="private_num" msgid="6374339738119166953">"Número privado"</string>
- <string name="payphone" msgid="7726415831153618726">"Teléfono público"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"El <xliff:g id="DATE">%1$s</xliff:g> a la hora <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"No se puede llamar a este número."</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Para configurar el buzón de voz, ve a a Menú &gt; Configuración."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Para escuchar los mensajes de tu buzón de voz, desactiva primero el modo avión."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Cargando..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Cargando desde tarjeta SIM..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Contactos de tarjeta SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"No hay aplicaciones de contactos disponibles."</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Búsqueda por voz no disponible"</string>
- <string name="call_not_available" msgid="8941576511946492225">"No se pueden realizar llamadas porque se inhabilitó la aplicación Teléfono."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"No hay una aplicación para esa acción en este dispositivo."</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Buscar contactos"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Agregar número o buscar contactos"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Tu historial de llamadas está vacío"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Hacer una llamada"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"No tienes llamadas perdidas"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"La bandeja de entrada del buzón de voz está vacía."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Tu archivo de mensajes de voz está vacío."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Mostrar solo favoritos"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Historial de llamadas"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Archivo de mensajes de voz"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Todo"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Perdidas"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Buzón voz"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Nuevo bloqueo simplificado"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Para brindarte mayor protección, el teléfono necesita cambiar el comportamiento de bloqueo. Se detendrán las llamadas y los mensajes de texto de los números bloqueados, y es posible que estos se compartan con otras apps."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Permitir"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"¿Deseas bloquear el <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Se bloquearán las llamadas que provengan de este número y se borrarán los mensajes del buzón de voz de forma automática."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Se bloquearán las llamadas que provengan de este número, pero es posible que la persona que llama pueda dejar mensajes en el buzón de voz."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Ya no recibirás llamadas o mensajes de textos de este número."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOQUEAR"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"¿Deseas desbloquear <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DESBLOQUEAR"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Marcado rápido"</string>
- <string name="tab_history" msgid="2563144697322434940">"Historial de llamadas"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Contactos"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Buzón de voz"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Eliminado de favoritos"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Deshacer"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Llamar al <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Crear contacto nuevo"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Agregar a un contacto"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Enviar SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Realizar videollamada"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Bloquear número"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> llamadas perdidas nuevas"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Aún no tienes contactos en la opción de marcado rápido"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Agregar un favorito"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Aún no tienes contactos"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Agregar un contacto"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Toca la imagen para ver todos los números o mantenla presionada para cambiar el orden."</string>
- <string name="remove_contact" msgid="1080555335283662961">"Eliminar"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videollamada"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Enviar un mensaje"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Detalles de la llamada"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Llamar a <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Llamada perdida de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Llamada contestada de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Mensaje de voz no leído de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Mensaje de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Llamar a <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"en <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"a través de <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"a través de <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"en <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, a través de <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> a través de <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Llamar"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Llamar a <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Realizar una videollamada a <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Escuchar buzón de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Reproducir buzón de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pausar buzón de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Borrar buzón de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> mensajes de voz nuevos</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> mensaje de voz nuevo</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Crear un contacto para <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Agregar <xliff:g id="NAMEORNUMBER">^1</xliff:g> a un contacto existente"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Detalles de llamada de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Se eliminó del historial de llamadas."</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Hoy"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Ayer"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Antiguos"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Lista de llamadas"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Activar altavoz"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Desactivar altavoz"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Aumentar velocidad de reproducción"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Disminuir velocidad de reproducción"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Iniciar o pausar la reproducción"</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Opciones de visualización"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Sonidos y vibración"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Accesibilidad"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Tono del teléfono"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibrar también en llamadas"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tonos del teclado"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Longitud del tono del teclado"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"Largo"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Respuestas rápidas"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Llamadas"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Bloqueo de llamadas"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Bloqueo de llamadas desactivado temporalmente"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Se inhabilitó el bloqueo de llamadas porque te comunicaste con servicios de emergencia en las últimas 48 horas desde este teléfono. Se volverá a habilitar de forma automática una vez que venza el período de 48 horas."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importar números"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Ya marcaste algunos emisores para que se envíen de forma automática al buzón de voz por medio de otras apps."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Ver números"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importar"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Error al importar"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"No se pudo archivar el mensaje de voz."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Desbloquear número"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Agregar número"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Se bloquearán las llamadas que provengan de estos números y se borrarán los mensajes del buzón de voz de forma automática."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Se bloquearán las llamadas que provengan de estos números, pero es posible que se puedan dejar mensajes en el buzón de voz."</string>
- <string name="block_list" msgid="7760188925338078011">"Números bloqueados"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> no es válido."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> ya está bloqueado."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"El bloqueo de llamadas se inhabilitó durante 48 horas"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Se inhabilitó porque se realizó una llamada de emergencia."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Cuentas telefónicas"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Activar"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Configurar permisos"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Para habilitar el marcado rápido, activa el permiso Contactos."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Para ver el registro de llamadas, activa el permiso Teléfono."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Para ver los contactos, activa el permiso Contactos."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Para acceder al buzón de voz, activa el permiso Teléfono."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Para buscar contactos, activa el permiso Contactos."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Para realizar una llamada, activa el permiso Teléfono."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"La aplicación de teléfono no tiene permiso para modificar la configuración del sistema."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Bloqueado"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> está activa"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Bloquear/marcar como spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Bloquear"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"No es spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Desbloquear"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"¿Deseas bloquear el <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Los correos de voz y llamadas de este número se bloquearán a partir de ahora."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Denunciar llamada como spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Los correos de voz y llamadas de este número se bloquearán. Esta llamada se denunciará como spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"¿Deseas desbloquear <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"El número se desbloqueará y se informará que no es spam. Los correos de voz y llamadas no se considerarán spam."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"¿Incluir <xliff:g id="NUMBER">%1$s</xliff:g> en la lista blanca?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Incluir en la lista blanca"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Los correos de voz y llamadas de este número no se identificarán como spam. Se informará que el número no es spam."</string>
-</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
deleted file mode 100644
index 77537ee73..000000000
--- a/res/values-es/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Teléfono"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Teléfono"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Teclado del teléfono"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Teléfono"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Historial de llamadas"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Informar sobre número incorrecto"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Copiar número"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Copiar transcripción"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Bloquear número"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> bloqueado"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Desbloquear número"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> desbloqueado"</string>
- <string name="block_number_undo" msgid="591338370336724156">"DESHACER"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Eliminar"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Editar número antes de llamar"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Borrar historial de llamadas"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Eliminar mensaje de voz"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Archivar mensaje de voz"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Compartir mensaje de voz"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Buzón voz eliminado"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Mensaje de voz archivado"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"DESHACER"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"IR A ARCHIVO"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"¿Borrar historial de llamadas?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Se eliminarán todas las llamadas del historial."</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Borrando historial de llamadas…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Teléfono"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Llamada perdida"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Llamada de trabajo perdida"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Llamadas perdidas"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> llamadas perdidas"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Llamar"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Mensaje"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> mensajes de voz </item>
- <item quantity="one"> mensaje de voz</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Reproducir"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nuevo mensaje de voz de <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Error al reproducir el buzón de voz"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Cargando buzón de voz…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Archivando el mensaje de voz…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Error al cargar el buzón de voz"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Solo llamadas con mensajes de voz"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Solo llamadas entrantes"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Solo llamadas salientes"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Solo llamadas perdidas"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Buzón de voz visual"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Consulta y escucha tu buzón de voz sin tener que llamar a ningún número (se pueden aplicar cargos de datos)."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Ajustes"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Actualizaciones de buzón de voz no disponibles"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Nuevo mensaje de voz en espera. No se puede cargar ahora."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Configurar tu buzón de voz"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio no disponible"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Configurar"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Llamar a buzón de voz"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Elige un número"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Elige un número"</string>
- <string name="make_primary" msgid="5829291915305113983">"Recordar esta opción"</string>
- <string name="description_search_button" msgid="3660807558587384889">"buscar"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"marcar"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"número que se va a marcar"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Iniciar o detener la reproducción"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Activar o desactivar el altavoz"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Buscar posición de reproducción"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Disminuir la velocidad de reproducción"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Aumentar la velocidad de reproducción"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Historial de llamadas"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Más opciones"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"teclado"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Mostrar solo llamadas salientes"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Mostrar solo llamadas entrantes"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Mostrar solo llamadas perdidas"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Solo mensajes de voz"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Mostrar todas las llamadas"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Añadir pausa de 2 segundos"</string>
- <string name="add_wait" msgid="3360818652790319634">"Añadir espera"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Ajustes"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Nuevo contacto"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Todos los contactos"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Detalles de la llamada"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Detalles no disponibles"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Usar teclado táctil"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Volver a la llamada"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Añadir llamada"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Llamada entrante"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Llamada saliente"</string>
- <string name="type_missed" msgid="2720502601640509542">"Llamada perdida"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Videollamada entrante"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Videollamada realizada"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Videollamada perdida"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Buzón de voz"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Llamada rechazada"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Llamada bloqueada"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Llamadas entrantes"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Reproducir mensaje de voz"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Ver contacto <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Llamar a <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Información de contacto de <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> llamadas."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videollamada."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Enviar SMS a <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Mensaje de voz sin oír"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Iniciar búsqueda por voz"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Llamar a <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Desconocidos"</string>
- <string name="voicemail" msgid="3851469869202611441">"Buzón de voz"</string>
- <string name="private_num" msgid="6374339738119166953">"Número privado"</string>
- <string name="payphone" msgid="7726415831153618726">"Teléfono público"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min y <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> a las <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"No se puede llamar a este número"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Para configurar el buzón de voz, toca la tecla de menú y, a continuación, toca Ajustes."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Para llamar al buzón de voz, debes desactivar el modo avión."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Cargando..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Cargando desde tarjeta SIM…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"Contactos de tarjeta SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"No hay aplicaciones de contactos disponibles"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"La búsqueda por voz no está disponible"</string>
- <string name="call_not_available" msgid="8941576511946492225">"No se puede hacer llamadas porque se ha inhabilitado la aplicación Teléfono."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"No hay aplicaciones para esa acción en este dispositivo"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Buscar contactos"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Añade número o busca contactos"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Tu historial de llamadas está vacío"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Hacer una llamada"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"No tienes llamadas perdidas."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"La bandeja de entrada del buzón de voz está vacía."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Tu archivo de mensajes de voz está vacío."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Mostrar solo favoritos"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Historial de llamadas"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Archivo de mensajes de voz"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Todas"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Perdidas"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Mensaje de voz"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Nuevo bloqueo, más sencillo"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Para garantizar tu seguridad, Teléfono necesita cambiar cómo funciona el bloqueo. Ya no recibirás llamadas ni SMS de los números bloqueados y es posible que estos se compartan con otras aplicaciones."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Permitir"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"¿Bloquear el número <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Se bloquearán las llamadas de este número y se eliminarán los mensajes del buzón de voz automáticamente."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Se bloquearán las llamadas de este número, pero la persona que llame puede seguir dejando mensajes en el buzón de voz."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Ya no recibirás llamadas ni SMS de este número."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOQUEAR"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"¿Desbloquear el número <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DESBLOQUEAR"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Marcación rápida"</string>
- <string name="tab_history" msgid="2563144697322434940">"Historial de llamadas"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Contactos"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Buzón de voz"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Eliminado de favoritos"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Deshacer"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Llamar a <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Crear nuevo contacto"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Añadir a un contacto"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Enviar SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Hacer videollamada"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Bloquear número"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> llamadas perdidas nuevas"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Aún no tienes contactos en la función de marcación rápida"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Añadir un favorito"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Aún no tienes contactos"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Añadir un contacto"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Toca la imagen para ver todos los números o mantenla pulsada para cambiar el orden"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Eliminar"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videollamada"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Enviar un mensaje"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Detalles de la llamada"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Llamar a <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Llamada perdida de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Has respondido una llamada de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Mensaje de voz sin leer de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Mensaje de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Llamar a <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"en <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"a través del <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"a través del <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"en <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, a través del <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> a través del <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Llamar"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Llamar a <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Videollamada a <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Escuchar el buzón de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Reproducir mensaje de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pausar mensaje de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Eliminar mensaje de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> mensajes de voz nuevos</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> mensaje de voz nuevo</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Crear contacto para <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Añadir <xliff:g id="NAMEORNUMBER">^1</xliff:g> a un contacto"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Detalles de llamada de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Eliminada del historial de llamadas"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Hoy"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Ayer"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Más antiguo"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Lista de llamadas"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Activar altavoz."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Desactivar altavoz."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Reproducir más rápido."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Reproducir más lento."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Iniciar o pausar reproducción."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Opciones de visualización"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Sonido y vibración"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Accesibilidad"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Tono del teléfono"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibrar también en llamadas"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tonos del teclado"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Duración del tono del teclado"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Tono normal"</item>
- <item msgid="6177579030803486015">"Tono largo"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Respuestas rápidas"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Llamadas"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Bloqueo de llamadas"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Bloqueo de llamadas desactivado temporalmente"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Se ha inhabilitado el bloqueo de llamadas porque te has puesto en contacto con los servicios de emergencia desde este teléfono en las últimas 48 horas. Se volverá a habilitar automáticamente cuando finalice este periodo de tiempo."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importar números"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Anteriormente marcaste algunas llamadas para que se enviaran automáticamente al buzón de voz a través de otras aplicaciones."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Ver números"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importar"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Error al importar"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"No se ha podido archivar mensaje de voz."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Desbloquear número"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Añadir número"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Se bloquearán las llamadas de estos números y se eliminarán los mensajes del buzón de voz automáticamente."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Se bloquearán las llamadas de estos números, pero podrán seguir dejando mensajes en el buzón de voz."</string>
- <string name="block_list" msgid="7760188925338078011">"Números bloqueados"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> no es válido."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> ya está bloqueado."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Bloqueo de llamadas inhabilitado durante 48 horas"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Inhabilitado porque has hecho una llamada de emergencia."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Cuentas de llamadas"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Activar"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Establecer permisos"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Para habilitar la marcación rápida, activa el permiso la aplicación Contactos."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Para ver el registro de llamadas, activa el permiso de la aplicación Teléfono."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Para ver tus contactos, activa el permiso de la aplicación Contactos."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Para acceder al buzón de voz, activa el permiso de la aplicación Teléfono."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Para poder buscar tus contactos, activa los permisos de contactos."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Para hacer una llamada, activa el permiso de la aplicación Teléfono."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"La aplicación Teléfono no tiene permiso para editar los ajustes del sistema."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Bloqueado"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"Llamada activa: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Bloquear / Marcar como spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Bloquear"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"No es spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Desbloquear"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"¿Bloquear el número <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Se bloquearán las llamadas y los mensajes de voz de este número."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Marcar la llamada como spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Se bloquearán los futuros mensajes de voz y llamadas del número y se marcará la llamada como spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"¿Desbloquear el número <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Este número se desbloqueará. No se considerarán spam ni él ni sus llamadas o mensajes de voz."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"¿Incluir el <xliff:g id="NUMBER">%1$s</xliff:g> en la lista blanca?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Incluir en la lista blanca"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Ni este número ni los próximos mensajes de voz y llamadas procedentes de él se considerarán spam."</string>
-</resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
deleted file mode 100644
index f7372a84a..000000000
--- a/res/values-et/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Telefoni valimisklahvistik"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Kõneajalugu"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Valest numbrist teavitamine"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopeeri number"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopeeri transkribeerimine"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blokeeri number"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Number <xliff:g id="NUMBER">%1$s</xliff:g> on blokeeritud"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Deblokeeri number"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Number <xliff:g id="NUMBER">%1$s</xliff:g> on deblokeeritud"</string>
- <string name="block_number_undo" msgid="591338370336724156">"VÕTA TAG."</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Kustuta"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Muuda enne helistamist numbrit"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Kustuta kõneajalugu"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Kustuta kõnepost"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Kõnepostisõnumi arhiivimine"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Jaga kõnepostisõnumit"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Kõnepost kustutati"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Kõnepostisõnum arhiiviti"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"VÕTA TAGASI"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"AVA ARHIIV"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Kas kustutada kõneajalugu?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"See kustutab ajaloost kõik kõned"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Kõneajaloo kustutamine ..."</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Vastamata kõne"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Vastamata kõne töölt"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Vastamata kõned"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> vastamata kõnet"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Helista tagasi"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Saada sõnum"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> kõneposti teadet </item>
- <item quantity="one">Kõneposti teade</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Esitamine"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Uus kõnepostisõnum kasutajalt <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Kõneposti ei õnnestunud esitada"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Kõneposti laadimine ..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Kõnepostisõnumi arhiivimine …"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Kõneposti laadimine ebaõnnestus"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Ainult kõnepostiga kõned"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Ainult sissetulevad kõned"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Ainult väljuvad kõned"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Ainult vastamata kõned"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Visuaalne kõnepost"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Vaadake ja kuulake oma kõnepostisõnumeid numbrile helistamata. Lisanduda võivad andmesidetasud."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Seaded"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Kõneposti värskendused ei ole saadaval"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Uus kõnepostiteade. Praegu ei saa seda laadida."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Kõneposti seadistamine"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Heli ei ole saadaval"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Seadistamine"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Helista kõneposti"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Valige number"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Valige number"</string>
- <string name="make_primary" msgid="5829291915305113983">"Pea see valik meeles"</string>
- <string name="description_search_button" msgid="3660807558587384889">"otsing"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"Helista"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"number valimiseks"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Taasesituse alustamine või peatamine"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Valjuhääldi sisse- või väljalülitamine"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Taasesituse positsiooni otsimine"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Taasesituse kiiruse vähendamine"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Taasesituse kiiruse suurendamine"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Kõneajalugu"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Rohkem valikuid"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"valimisklahvistik"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Kuva ainult väljuvad"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Kuva ainult sissetulevad"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Kuva ainult vastamata"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Kuva ainult kõnepostisõnumeid"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Kuva kõik kõned"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Lisa 2-sekundiline paus"</string>
- <string name="add_wait" msgid="3360818652790319634">"Lisa ootamine"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Seaded"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Uus kontakt"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Kõik kontaktid"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Kõne üksikasjad"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Üksikasjad ei ole saadaval"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Kasuta puutetooniga klahvistikku"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Tagasi käimasolevale kõnele"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Lisa kõne"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Sissetulev kõne"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Väljuv kõne"</string>
- <string name="type_missed" msgid="2720502601640509542">"Vastamata kõne"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Sissetulev videokõne"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Väljaminev videokõne"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Vastamata videokõne"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Kõnepost"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Keeldutud kõne"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blokeeritud kõne"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Sissetulevad kõned"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Esita kõnepostisõnum"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Kuva kontakt <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Helista: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Kontakti <xliff:g id="NAMEORNUMBER">%1$s</xliff:g> üksikasjad"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> kõnet."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videokõne."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"SMS-i saatmine kontaktile <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Kuulamata kõnepostisõnum"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Häälotsingu alustamine"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Helista: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Tundmatu"</string>
- <string name="voicemail" msgid="3851469869202611441">"Kõnepost"</string>
- <string name="private_num" msgid="6374339738119166953">"Varjatud number"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefoniautomaat"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> kell <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Sellele numbrile ei saa helistada"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Kõneposti seadistamiseks minge valikusse Menüü &gt; Seaded."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Kõneposti kuulamiseks lülitage lennurežiim välja."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Laadimine ..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Laadimine SIM-kaardilt ..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM-kaardi kontaktid"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Kontaktide rakendus pole saadaval"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Häälotsing ei ole saadaval"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Helistada ei saa, sest telefonirakendus on keelatud."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Seadmes pole selleks sobilikku rakendust"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Otsige kontakte"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Lisage nr või otsige kontakte"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Teie kõneajalugu on tühi"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Helista"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Vastamata kõnesid pole."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Kõneposti postkast on tühi."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Teie kõnepostisõnumite arhiiv on tühi."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Kuva ainult lemmikud"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Kõneajalugu"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Kõnepostisõnumite arhiiv"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Kõik"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Vastamata"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Kõnepost"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Uus lihtsustatud blokeerimine"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Teie paremaks kaitsmiseks peab rakendus Telefon muutma blokeerimise tööpõhimõtet. Blokeeritud numbrite puhul blokeeritakse nüüd nii helistamine kui ka tekstsõnumid ja blokeeritud numbreid võidakse jagada teiste rakendustega."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Luba"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Kas soovite blokeerida numbri <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Kõned sellelt numbrilt blokeeritakse ja kõnepostisõnumid kustutatakse automaatselt."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Kõned sellelt numbrilt blokeeritakse, kuid helistaja saab võib-olla siiski teile kõnepostisõnumeid jätta."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Te ei saa enam sellelt numbrilt kõnesid ega tekstsõnumeid."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOKEERI"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Kas soovite deblokeerida numbri <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DEBLOKEERI"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Kiirvalimine"</string>
- <string name="tab_history" msgid="2563144697322434940">"Kõneajalugu"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontaktid"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Kõnepost"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Eemaldatud lemmikute hulgast"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Võta tagasi"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Helista <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Loo uus kontakt"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Lisa kontaktile"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Saada SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Videokõne tegemine"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blokeeri number"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> uus/uut vastamata kõne(t)"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Te pole veel kedagi kiirvalimisse lisanud"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Lisa lemmik"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Teil pole veel kontakte"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Lisa kontakt"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Puudutage kujutist, et näha kõiki numbreid, või puudutage pikalt järjestuse muutmiseks"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Eemalda"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videokõne"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Saada sõnum"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Kõne üksikasjad"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Helistamine: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Vastamata kõne: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Vastati kõnele: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Lugemata kõnepost kasutajalt <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Kõnepost kasutajalt <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Kõne: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"kontol <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"numbri <xliff:g id="NUMBER">%1$s</xliff:g> kaudu"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"numbri <xliff:g id="NUMBER">%1$s</xliff:g> kaudu"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"kontol <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, numbri <xliff:g id="NUMBER">%2$s</xliff:g> kaudu"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> numbri <xliff:g id="NUMBER">%2$s</xliff:g> kaudu"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Helistamine"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Helistamine: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Videokõne kirjele <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Kontakti <xliff:g id="NAMEORNUMBER">^1</xliff:g> kõneposti kuulamine"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Kõnepostisõnumite esita kontaktilt <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Kõnepostisõnumite peatamine kontaktilt <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Kõnepostisõnumite kustutamine kontaktilt <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> uut kõnepostisõnumit</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> uus kõnepostisõnum</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Kontakti loomine kirjele <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Kirje <xliff:g id="NAMEORNUMBER">^1</xliff:g> lisamine olemasolevale kontaktile"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Kontakti <xliff:g id="NAMEORNUMBER">^1</xliff:g> kõne üksikasjad"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Kustutatud kõneajaloost"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Täna"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Eile"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Vanem"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Kõneloend"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Kõlari sisselülitamine."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Kõlari väljalülitamine."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Kiirem esitus."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Aeglasem esitus."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Esituse alustamine või peatamine."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Kuvamisvalikud"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Helid ja vibratsioon"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Juurdepääsetavus"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Telefoni helin"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibreeri ka kõnede puhul"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Valimisklahvistiku toonid"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Valimisklahvistiku tooni pikkus"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Tavaline"</item>
- <item msgid="6177579030803486015">"Pikk"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Kiirvastused"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Kõned"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Kõnede blokeerimine"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Kõnede blokeerimine on ajutiselt väljas"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Kõnede blokeerimine on keelatud, kuna võtsite sellelt telefonilt viimase 48 tunni jooksul ühendust hädaabiteenustega. See lubatakse 48 tunni möödumisel automaatselt uuesti."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Impordi numbrid"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Märkisite varem, et osad helistajad saadetaks muude rakenduste kaudu automaatselt kõneposti."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Vaata numbreid"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Impordi"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Importimine ebaõnnestus"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Kõnepostisõnumi arhiivimine ebaõnnestus."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Numbri deblokeerimine"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Lisa number"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Kõned nendelt numbritelt blokeeritakse ja kõnepostisõnumid kustutatakse automaatselt."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Kõned nendelt numbritelt blokeeritakse, kuid helistajad saavad võib-olla siiski teile kõnepostisõnumeid jätta."</string>
- <string name="block_list" msgid="7760188925338078011">"Blokeeritud numbrid"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> on kehtetu."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> on juba blokeeritud."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Kõnede blokeerimine on 48 tunniks keelatud"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Keelatud, kuna tehti hädaabikõne."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Helistamiskontod"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Lülita sisse"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Määra load"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Kiirvalimise lubamiseks lülitage sisse kontaktiluba."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Kõnelogi vaatamiseks lülitage sisse telefoniluba."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Kontaktide vaatamiseks lülitage sisse kontaktiluba."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Kõnepostile juurdepääsemiseks lülitage sisse telefoniluba."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Kontaktide otsimiseks lülitage sisse kontaktiload."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Helistamiseks lülitage sisse telefoniluba."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Telefonirakendusel pole luba süsteemiseadetesse kirjutada."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Blokeeritud"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> on aktiivne"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blokeeri / teavita rämpssisust"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blokeeri"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Pole rämpssisu"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Deblokeeri"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Rämpssisu"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Kas soovite blokeerida numbri <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Tulevased kõned ja kõnepostisõnumid sellelt numbrilt blokeeritakse."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Liigita kõne rämpspostiks"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Tulevased kõned ja kõnepostisõnumid sellelt numbrilt blokeeritakse. Kõne liigitatakse rämpspostiks."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Kas soovite numbri <xliff:g id="NUMBER">%1$s</xliff:g> deblokeerida?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"See number deblokeeritakse ja seda ei liigitata rämpspostiks. Tulevasi kõnesid ja kõnepostisõnumeid ei liigitata rämpspostiks."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Kas lisada number <xliff:g id="NUMBER">%1$s</xliff:g> lubatud üksuste loendisse?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Lisa lubatud üksuste loendisse"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Tulevasi kõnesid ja kõnepostisõnumeid sellelt numbrilt ei liigitata rämpspostiks. Seda numbrit ei liigitata rämpspostiks."</string>
-</resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
deleted file mode 100644
index dca7d38d4..000000000
--- a/res/values-eu/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefonoa"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Deitu"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Telefonoaren markagailua"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefonoa"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Deien historia"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Jakinarazi zenbakia okerra dela"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopiatu zenbakia"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopiatu transkripzioa"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blokeatu zenbakia"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> blokeatuta"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Desblokeatu zenbakia"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> desblokeatuta"</string>
- <string name="block_number_undo" msgid="591338370336724156">"DESEGIN"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Ezabatu"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Editatu zenbakia deitu aurretik"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Garbitu deien historia"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Ezabatu ahots-mezua"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Artxibatu ahots-mezua"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Partekatu ahots-mezua"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Ahots-mezua ezabatzea"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Artxibatu da ahots-mezua"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"DESEGIN"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"JOAN ARTXIBORA"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Deien historia garbitu?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Historiako dei guztiak ezabatuko dira"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Deien historia garbitzen…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefonoa"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Galdutako deia"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Laneko dei bat galdu duzu"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Dei galduak"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> dei galdu"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Itzuli deia"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Mezua"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> Erantzungailuko <xliff:g id="COUNT">%1$d</xliff:g> mezu </item>
- <item quantity="one">Erantzungailuko mezua</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Erreproduzitu"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> deitzailearen ahots-mezu berria"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Ezin izan dira erreproduzitu ahots-mezuak"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Erantzungailua kargatzen…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Ahots-mezua artxibatzen…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Ezin izan da kargatu erantzungailua"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Erantzungailuko deiak soilik"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Sarrerako deiak soilik"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Irteerako deiak soilik"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Dei galduak soilik"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Ikusizko erantzungailua"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Ikusi eta entzun erantzungailuko mezuak, inora deitu beharrik gabe. Agian datu-konexioaren kostuak ordaindu beharko dituzu."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Ezarpenak"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Ez dago erantzungailuaren berririk"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Ahots-mezu berri bat duzu zain. Une honetan ezin da kargatu."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Konfiguratu erantzungailua"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audioa ez dago erabilgarri"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Konfiguratu"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Deitu erantzungail."</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Aukeratu zenbakia"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Aukeratu zenbakia"</string>
- <string name="make_primary" msgid="5829291915305113983">"Gogoratu aukera hau"</string>
- <string name="description_search_button" msgid="3660807558587384889">"bilatu"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"markatu"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"markatu beharreko zenbakia"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Erreproduzitu edo pausatu erreprodukzioa"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Aktibatu edo desaktibatu bozgorailua"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Bilatu erreprodukzioaren posizioa"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Mantsotu erreprodukzioaren abiadura"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Bizkortu erreprodukzioaren abiadura"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Deien historia"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Aukera gehiago"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"markagailua"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Erakutsi irteerakoak soilik"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Erakutsi sarrerakoak soilik"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Erakutsi galduak soilik"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Erakutsi erantzungailukoak soilik"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Erakutsi dei guztiak"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Gehitu 2 segundoko pausa"</string>
- <string name="add_wait" msgid="3360818652790319634">"Gehitu itxaronaldia"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Ezarpenak"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Kontaktu berria"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Kontaktu guztiak"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Deiaren xehetasunak"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Ez dago xehetasunik erabilgarri"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Erabili ukipen-tonuak dituen teklatua"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Itzuli abian den deira"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Gehitu deia"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Sarrerako deia"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Irteerako deia"</string>
- <string name="type_missed" msgid="2720502601640509542">"Galdutako deia"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Sarrerako bideo-deia"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Irteerako bideo-deia"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Bideo-dei galdua"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Erantzungailua"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Baztertutako deia"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blokeatutako deia"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Sarrerako deiak"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Erreproduzitu erantzungailuko ahots-mezuak"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Ikusi <xliff:g id="NAME">%1$s</xliff:g> kontaktua"</string>
- <string name="description_call" msgid="3443678121983852666">"Deitu <xliff:g id="NAME">%1$s</xliff:g> erabiltzaileari"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> kontaktuaren xehetasunak"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> dei."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Bideo-deia."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Bidali SMSa <xliff:g id="NAME">%1$s</xliff:g> kontaktuari"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Entzun ez diren erantzungailuko ahots-mezuak"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Hasi ahots bidezko bilaketa"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Deitu <xliff:g id="NUMBER">%s</xliff:g> zenbakira"</string>
- <string name="unknown" msgid="740067747858270469">"Ezezaguna"</string>
- <string name="voicemail" msgid="3851469869202611441">"Erantzungailua"</string>
- <string name="private_num" msgid="6374339738119166953">"Zenbaki pribatua"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefono publikoa"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g>)"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Ezin da zenbaki horretara deitu"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Erantzungailua konfiguratzeko, joan Menua &gt; Ezarpenak atalera."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Erantzungailua entzuteko, Hegaldi modua desaktibatu behar duzu."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Kargatzen…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"SIM txarteletik kargatzen…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM txarteleko kontaktuak"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Ez dago kontaktuen aplikaziorik erabilgarri"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Ahots bidezko bilaketa ez dago erabilgarri"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Ezin da telefono-deirik egin Telefonoa aplikazioa desgaitu egin delako."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Ez dago hori egin dezakeen aplikaziorik gailu honetan"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Bilatu kontaktuetan"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Gehitu zk. edo bilatu kontaktua"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Hutsik dago deien historia"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Deitu"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Ez duzu dei galdurik."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Hutsik dago erantzungailuaren sarrerako ontzia."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Ahots-mezuen artxiboa hutsik dago."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Erakutsi gogokoak soilik"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Deien historia"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Ahots-mezuen artxiboa"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Guztiak"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Galduak"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Erantzungailuko deiak"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Blokeatzeko aukera berri sinplifikatua"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Zu hobeto babesteko, blokeatzeko funtzioa aldatu behar du Telefonoa aplikazioak. Aurrerantzean, blokeatutako zenbakien deiak eta testu-mezuak jasotzeari utziko zaio, eta baliteke zenbaki horiek beste aplikazioekin partekatzea."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Baimendu"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> blokeatu nahi duzu?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Zenbaki honetatik jasotzen diren deiak blokeatu egingo dira, eta mezuak automatikoki ezabatuko dira erantzungailutik."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Zenbaki honetatik jasotzen diren deiak blokeatu egingo dira, baina deitzaileak mezuak utzi ahal izango dizkizu erantzungailuan."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Ez duzu deirik edo testu-mezurik jasoko zenbaki horretatik."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOKEATU"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> desblokeatu nahi duzu?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DESBLOKEATU"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Markatze bizkorra"</string>
- <string name="tab_history" msgid="2563144697322434940">"Deien historia"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontaktuak"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Erantzungailua"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Gogokoetatik kendu da"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Desegin"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Deitu <xliff:g id="NUMBER">%s</xliff:g> zenbakira"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Sortu kontaktua"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Gehitu kontaktuetan"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Bidali SMS mezua"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Egin bideo-deia"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blokeatu zenbakia"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> dei galdu berri"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Ez duzu inor markatze bizkorrean oraindik"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Gehitu gogokoa"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Ez duzu kontakturik oraindik"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Gehitu kontaktua"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Ukitu irudia zenbaki guztiak ikusteko, edo eduki ezazu sakatuta berrantolatzeko"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Kendu"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Bideo-deia"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Bidali mezua"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Deiaren xehetasunak"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Deitu: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Galdutako deiaren xehetasunak: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Erantzundako deiaren xehetasunak: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Erantzungailuko mezua, irakurri gabe: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Erantzungailuko mezua: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Egindako deiaren xehetasunak: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> kontuan"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> bidez"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> bidez"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> kontuan, <xliff:g id="NUMBER">%2$s</xliff:g> bidez"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, <xliff:g id="NUMBER">%2$s</xliff:g> bidez"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Deitu"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Deitu <xliff:g id="NAMEORNUMBER">^1</xliff:g> deitzaileari"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Egin bideo-deia (<xliff:g id="NAMEORNUMBER">^1</xliff:g>)."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Deitzaile honek erantzungailuan utzitako ahots-mezuak entzutea: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Erreproduzitu erantzungailuko <xliff:g id="NAMEORNUMBER">^1</xliff:g> deitzailearen mezua"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pausatu erantzungailuko <xliff:g id="NAMEORNUMBER">^1</xliff:g> deitzailearen mezua"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Ezabatu erantzungailuko <xliff:g id="NAMEORNUMBER">^1</xliff:g> deitzailearen mezua"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> ahots-postako mezu berri</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> ahots-postako mezu berri</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Sortu kontaktua (<xliff:g id="NAMEORNUMBER">^1</xliff:g>)"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Gehitu <xliff:g id="NAMEORNUMBER">^1</xliff:g> lehendik dagoen kontaktu batean"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> kontaktuaren xehetasunak"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Ezabatu deien historiatik"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Gaur"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Atzo"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Zaharrak"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Deien zerrenda"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Aktibatu bozgorailua."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Desaktibatu bozgorailua."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Erreproduzitu bizkorrago."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Erreproduzitu mantsoago."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Hasi edo gelditu erreprodukzioa."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Bistaratze-aukerak"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Soinuak eta dardara"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Erabilerraztasuna"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Telefonoaren tonua"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Dardara deiak jasotzean ere"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Markagailuaren tonuak"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Markagailuaren tonuaren iraupena"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normala"</item>
- <item msgid="6177579030803486015">"Luzea"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Erantzun bizkorrak"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Deiak"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Deien blokeoa"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Deien blokeoa aldi baterako desgaituta"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Deiak blokeatzeko aukera desgaitu egin da, azken 48 orduetan larrialdi-zerbitzuekin harremanetan jarri zarelako telefono honetatik. Berriro gaituko da 48 orduko epea igaro ondoren."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Inportatu zenbakiak"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Deitzaile batzuk beste aplikazio batzuen bidez erantzungailura automatikoki bidaltzea aukeratu duzu."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Ikusi zenbakiak"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Inportatu"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Ezin izan da inportatu"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Ezin izan da artxibatu ahots-mezua."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Desblokeatu zenbakia"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Gehitu zenbakia"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Zenbaki hauetatik jasotzen diren deiak blokeatu egingo dira, eta mezuak automatikoki ezabatuko dira erantzungailutik."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Zenbaki hauetatik jasotzen diren deiak blokeatu egingo dira, baina deitzaileek mezuak utzi ahal izango dizkizute erantzungailuan."</string>
- <string name="block_list" msgid="7760188925338078011">"Blokeatutako zenbakiak"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> zenbakiak ez du balio."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> blokeatuta dago dagoeneko."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Deiak blokeatzeko aukera desgaituta egongo da 48 orduan"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Desgaituta dago, larrialdi-dei bat egin delako."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Deiak egiteko kontuak"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Aktibatu"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Ezarri baimenak"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Markatze bizkorra gaitzeko, aktibatu Kontaktuak erabiltzeko baimena."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Deien erregistroa ikusteko, aktibatu Telefonoa erabiltzeko baimena."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Kontaktuak ikusteko, aktibatu Kontaktuak erabiltzeko baimena."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Erantzungailuan sartzeko, aktibatu Telefonoa erabiltzeko baimena."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Kontaktuak bilatzeko, aktibatu kontaktuak atzitzeko baimenak."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Deiak egiteko, aktibatu Telefonoa erabiltzeko baimena."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Telefonoaren aplikazioak ez du baimenik sistemaren ezarpenetan ezer idazteko."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Blokeatuta"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> aktibo dago"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blokeatu eta salatu spama dela"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blokeatu"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Ez da spama"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Desblokeatu"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spama"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> blokeatu nahi duzu?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Aurrerantzean, blokeatu egingo dira zenbaki honen deiak eta ahots-mezuak."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Salatu deia spama dela"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Blokeatu egingo dira zenbaki honen deiak eta ahots-mezuak. Dei hau spama dela salatuko da."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> desblokeatu nahi duzu?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Desblokeatu egingo da zenbaki hau. Ez dira identifikatuko spam gisa deiak eta ahots-mezuak."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Sarbidea eman nahi diozu <xliff:g id="NUMBER">%1$s</xliff:g> zenbakiari?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Eman sarbidea"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Aurrerantzean, ez dira identifikatuko spam gisa zenbaki honen deiak eta ahots-mezuak."</string>
-</resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
deleted file mode 100644
index b0b37efeb..000000000
--- a/res/values-fa/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"تلفن"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"تلفن"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"صفحه شماره‌گیری تلفن"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"تلفن"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"سابقه تماس"</string>
- <string name="action_report_number" msgid="4635403959812186162">"گزارش شماره نادرست"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"کپی کردن شماره"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"کپی کردن آوانویسی"</string>
- <string name="action_block_number" msgid="1482657602262262134">"مسدود کردن شماره"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> مسدود شد"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"رفع انسداد شماره"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> گشوده شد"</string>
- <string name="block_number_undo" msgid="591338370336724156">"واگرد"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"حذف"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"ویرایش شماره قبل از تماس"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"پاک کردن سابقه تماس"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"حذف پست صوتی"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"بایگانی پست صوتی"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"اشتراک‌گذاری پست صوتی"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"پست صوتی حذف شد"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"پست صوتی بایگانی شد"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"واگرد"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"رفتن به بایگانی"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"سابقه تماس پاک شود؟"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"این کار همه تماس‌ها را از سابقه شما حذف می‌کند"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"در حال پاک کردن سابقه تماس..."</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"تلفن"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"تماس بی‌پاسخ"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"تماس کاری بی‌پاسخ"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"تماس‌های بی‌پاسخ"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> تماس بی‌پاسخ"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"پاسخ تماس"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"پیام"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> پست صوتی </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> پست صوتی </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"پخش"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>، <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"پست صوتی جدید از <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"پست صوتی پخش نشد"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"در حال بارگیری پست صوتی..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"در حال بایگانی کردن پست صوتی..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"پست صوتی بارگیری نشد"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"فقط تماس‌های دارای پست صوتی"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"فقط تماس‌های دریافتی"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"فقط تماس‌های خروجی"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"فقط تماس‌های بی‌پاسخ"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"پست صوتی تصویری"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"بدون اینکه مجبور به گرفتن شماره‌ای باشید، پست صوتی را ببینید و به آن گوش دهید. ممکن است هزینه مصرف داده اعمال شود."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"تنظیمات"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"به‌روزرسانی‌های پست صوتی در دسترس نیستند"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"پست صوتی جدید در انتظار است. در حال حاضر نمی‌تواند بارگیری شود."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"پست صوتی‌تان را تنظیم کنید"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"صوت در دسترس نیست"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"راه‌اندازی"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"تماس با پست صوتی"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"انتخاب شماره"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"انتخاب شماره"</string>
- <string name="make_primary" msgid="5829291915305113983">"این گزینه را به خاطر بسپار"</string>
- <string name="description_search_button" msgid="3660807558587384889">"جستجو"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"شماره گیری"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"شماره برای شماره گیری"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"پخش یا توقف بازپخش"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"روشن یا خاموش کردن بلندگوی تلفن"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"جستجوی موقعیت بازپخش"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"کاهش سرعت بازپخش"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"افزایش سرعت بازپخش"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"سابقه تماس"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"گزینه‌های بیشتر"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"صفحه شماره‌گیری"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"فقط نمایش خروجی"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"فقط نمایش ورودی"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"فقط نمایش بی‌پاسخ"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"فقط نمایش پست‌های صوتی"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"نمایش همه مکالمات"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"افزودن یک مکث ۲ ثانیه‌ای"</string>
- <string name="add_wait" msgid="3360818652790319634">"افزودن انتظار"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"تنظیمات"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"مخاطب جدید"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"همه مخاطبین"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"جزئیات تماس"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"جزئیات در دسترس نیست"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"استفاده از صفحه‌کلید لمسی"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"برگشت به تماس درحال انجام"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"افزودن تماس"</string>
- <string name="type_incoming" msgid="6502076603836088532">"تماس ورودی"</string>
- <string name="type_outgoing" msgid="343108709599392641">"تماس خروجی"</string>
- <string name="type_missed" msgid="2720502601640509542">"تماس بی پاسخ"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"تماس ویدئویی ورودی"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"تماس ویدئویی خروجی"</string>
- <string name="type_missed_video" msgid="954396897034220545">"تماس ویدئویی بی‌پاسخ"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"پست صوتی"</string>
- <string name="type_rejected" msgid="7783201828312472691">"تماس ردشده"</string>
- <string name="type_blocked" msgid="3521686227115330015">"تماس مسدودشده"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"تماس‌های ورودی"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"پخش پست صوتی"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"مشاهده مخاطب <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"تماس با <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"جزئیات تماس برای <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> تماس."</string>
- <string name="description_video_call" msgid="2933838090743214204">"تماس ویدئویی."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"ارسال پیامک به <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"پست صوتی شنیده نشده"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"شروع جستجوی صوتی"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"تماس با <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"ناشناس"</string>
- <string name="voicemail" msgid="3851469869202611441">"پست صوتی"</string>
- <string name="private_num" msgid="6374339738119166953">"شماره خصوصی"</string>
- <string name="payphone" msgid="7726415831153618726">"تلفن عمومی"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> ثانیه"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> دقیقه <xliff:g id="SECONDS">%s</xliff:g> ثانیه"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> ساعت <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"تماس با این شماره ممکن نیست"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"‏برای راه‌اندازی پست صوتی به منو &gt; تنظیمات بروید."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"برای تماس با پست صوتی، ابتدا حالت هواپیما را غیرفعال کنید."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"در حال بارکردن…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"در حال بارگیری سیم کارت..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"مخاطبین سیم کارت"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"هیچ برنامه مخاطبی در دسترس نیست"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"جستجوی شفاهی در دسترس نیست"</string>
- <string name="call_not_available" msgid="8941576511946492225">"برقراری تماس تلفنی ممکن نیست، زیرا برنامه تلفن غیرفعال شده است."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"هیچ برنامه‌ای برای انجام این کار در این دستگاه نصب نیست"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"جستجوی مخاطبین"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"افزودن شماره یا جستجوی مخاطب"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"سابقه تماستان خالی است"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"برقراری تماس تلفنی"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"تماس بی‌پاسخی ندارید."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"صندوق پست صوتی‌تان خالی است."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"بایگانی پست صوتی خالی است."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"فقط نمایش موارد دلخواه"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"سابقه تماس"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"بایگانی پست صوتی"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"همه موارد"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"بی‌پاسخ"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"پست صوتی"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"مسدود کردن جدید و ساده‌شده"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"برای محافظت بهتر از شما، «تلفن» باید عملکرد مسدود کردن را تغییر دهد. شماره‌های مسدود شده دیگر تماس و پیام نوشتاری ارسال/دریافت نخواهند کرد و ممکن است با برنامه‌های دیگر به اشتراک گذاشته شوند."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"اجازه دادن"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> مسدود شود؟"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"تماس‌‌ها از این شماره مسدود می‌شود و پست‌‌های صوتی به‌طور خودکار حذف می‌شود."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"تماس‌ها از این شماره مسدود می‌شود، اما ممکن است تماس‌گیرنده همچنان بتواند برایتان پست صوتی بگذارد."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"دیگر از این شماره، تماس یا پیام نوشتاری دریافت نمی‌کنید."</string>
- <string name="block_number_ok" msgid="770551992296781873">"مسدود کردن"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> رفع انسداد شود؟"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"رفع انسداد"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"شماره‌گیری سریع"</string>
- <string name="tab_history" msgid="2563144697322434940">"سابقه تماس"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"مخاطبین"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"پست صوتی"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"از موارد دلخواه حذف شد"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"واگرد"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"تماس با <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"ایجاد مخاطب جدید"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"افزودن به مخاطب"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"ارسال پیامک"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"برقراری تماس ویدئویی"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"مسدود کردن شماره"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> تماس‌ بی‌پاسخ جدید"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"هنوز کسی در فهرست شماره‌گیری سریع شما نیست"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"افزودن مورد دلخواه"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"هنوز هیچ مخاطبی ندارید"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"افزودن مخاطب"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"برای مشاهده همه شماره‌ها، تصویر را لمس کنید یا برای ترتیب مجدد لمس کنید و نگه دارید"</string>
- <string name="remove_contact" msgid="1080555335283662961">"حذف"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"تماس ویدئویی"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"ارسال پیام"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"جزئیات تماس"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"تماس با <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"تماس از دست رفته از <xliff:g id="NAMEORNUMBER">^1</xliff:g>، ‏<xliff:g id="TYPEORLOCATION">^2</xliff:g>، ‏<xliff:g id="TIMEOFCALL">^3</xliff:g>، ‏<xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"تماس پاسخ داده شده از <xliff:g id="NAMEORNUMBER">^1</xliff:g>، ‏<xliff:g id="TYPEORLOCATION">^2</xliff:g>، ‏<xliff:g id="TIMEOFCALL">^3</xliff:g>، ‏<xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"پست صوتی خوانده نشده از <xliff:g id="NAMEORNUMBER">^1</xliff:g>،‏ <xliff:g id="TYPEORLOCATION">^2</xliff:g>،‏ <xliff:g id="TIMEOFCALL">^3</xliff:g>،‏ <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"پست صوتی از <xliff:g id="NAMEORNUMBER">^1</xliff:g>،‏ <xliff:g id="TYPEORLOCATION">^2</xliff:g>،‏ <xliff:g id="TIMEOFCALL">^3</xliff:g>،‏ <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"تماس با <xliff:g id="NAMEORNUMBER">^1</xliff:g>، ‏<xliff:g id="TYPEORLOCATION">^2</xliff:g>، ‏<xliff:g id="TIMEOFCALL">^3</xliff:g>، ‏<xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"در <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"از طریق <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"از طریق <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"در <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>، از طریق <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> از طریق <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"تماس"</string>
- <string name="description_call_action" msgid="4000549004089776147">"تماس با <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"تماس ویدئویی با <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"گوش دادن به پست صوتی از <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"پخش پست صوتی از <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"مکث پست صوتی از <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"حذف پست صوتی از <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one">‏<xliff:g id="COUNT_1">%d</xliff:g> پست صوتی جدید</item>
- <item quantity="other">‏<xliff:g id="COUNT_1">%d</xliff:g> پست صوتی جدید</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"ایجاد مخاطب برای <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"افزودن <xliff:g id="NAMEORNUMBER">^1</xliff:g> به مخاطب موجود"</string>
- <string name="description_details_action" msgid="2433827152749491785">"جزئیات تماس برای <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"از سابقه تماس حذف شد"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"امروز"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"دیروز"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"قدیمی‌تر"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"فهرست تماس‌ها"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"روشن کردن بلندگو."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"خاموش کردن بلندگو."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"پخش سریع‌تر"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"پخش آهسته‌تر."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"شروع یا مکث بازپخش."</string>
- <string name="list_delimeter" msgid="4571593167738725100">"، "</string>
- <string name="display_options_title" msgid="7812852361055667468">"گزینه‌های نمایش"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"صدا و لرزش"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"دسترس‌پذیری"</string>
- <string name="ringtone_title" msgid="760362035635084653">"آهنگ زنگ تلفن"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"تماس‌‌ها لرزش هم داشته باشند"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"صداهای صفحه شماره‌گیری"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"طول آهنگ صفحه شماره‌گیری"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"عادی"</item>
- <item msgid="6177579030803486015">"طولانی"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"پاسخ‌های سریع"</string>
- <string name="call_settings_label" msgid="313434211353070209">"تماس‌ها"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"مسدود کردن تماس"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"انسداد تماس موقتاً خاموش است"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"چون در ۴۸ ساعت گذشته با این تلفن با سرویس‌های اضطراری تماس گرفتید، انسداد تماس غیرفعال شده است. پس از گذشت ۴۸ ساعت، این قابلیت دوباره فعال می‌شود."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"وارد کردن شماره‌ها"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"قبلاً ازطریق برنامه‌های دیگر، برخی از تماس‌گیرندگان را برای ارسال خودکار به پست صوتی علامت زده‌اید."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"مشاهده شماره‌ها"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"وارد کردن"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"وارد کردن انجام نشد"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"پست صوتی بایگانی نشد."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"رفع انسداد شماره"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"افزودن شماره"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"تماس‌‌ها از این شماره‌ها مسدود می‌شود و پست‌‌های صوتی به‌طور خودکار حذف می‌شود."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"تماس‌‌ها از این شماره‌ها مسدود می‌شود اما ممکن است همچنان بتوانند برای شما پست صوتی بگذارند."</string>
- <string name="block_list" msgid="7760188925338078011">"شماره‌های مسدود شده"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> نامعتبر است."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> درحال‌حاضر مسدود شده است."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"مسدود کردن تماس برای ۴۸ ساعت غیرفعال شد"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"به علت برقرار شدن تماس اضطراری، غیرفعال شد."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"حساب‌های تماس"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"روشن کردن"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"تنظیم مجوزها"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"برای فعال کردن شماره‌گیری سریع، مجوز «مخاطبین» را روشن کنید."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"برای دیدن گزارش تماستان، مجوز «تلفن» را روشن کنید."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"برای دیدن مخاطبینتان، مجوز «مخاطبین» را روشن کنید."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"برای دسترسی به پست صوتی‌تان، مجوز «تلفن» را روشن کنید."</string>
- <string name="permission_no_search" msgid="84152933267902056">"برای جستجوی مخاطبینتان، مجوزهای مخاطبین را روشن کنید."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"برای برقراری تماس، مجوز «تلفن» را روشن کنید."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"برنامه تلفن اجازه نوشتن در تنظیمات سیستم را ندارد."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"مسدودشده"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> فعال است"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"مسدود کردن/گزارش هرزنامه"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"مسدود کردن"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"هرزنامه نیست"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"رفع انسداد"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"هرزنامه"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> مسدود شود؟"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"تماس‌ها و پست‌های صوتی بعدی از این شماره مسدود می‌شود."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"گزارش تماس به‌عنوان هرزنامه"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"تماس‌ها و پست‌های صوتی بعدی از این شماره مسدود می‌شود. این تماس به‌عنوان هرزنامه گزارش خواهد شد."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> رفع انسداد شود؟"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"این شماره گشوده می‌شود و به‌عنوان غیرهرزنامه گزارش می‌شود. تماس‌ها و پست‌های صوتی بعدی به‌عنوان هرزنامه شناسایی نمی‌شوند."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> در فهرست مجاز قرار بگیرد؟"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"فهرست مجاز"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"تماس‌ها و پست‌های صوتی بعدی از این شماره به‌عنوان هرزنامه شناسایی نمی‌شوند. این شماره به‌عنوان غیرهرزنامه گزارش می‌شود."</string>
-</resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
deleted file mode 100644
index 2832686a4..000000000
--- a/res/values-fi/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Puhelin"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Puhelin"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Puhelimen näppäimistö"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Puhelin"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Soittohistoria"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Ilmoita epätarkasta numerosta"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopioi numero"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopioi transkriptio"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Estä numero"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Numero <xliff:g id="NUMBER">%1$s</xliff:g> estetty"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Kumoa numeron esto"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Numeron <xliff:g id="NUMBER">%1$s</xliff:g> esto kumottu"</string>
- <string name="block_number_undo" msgid="591338370336724156">"KUMOA"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Poista"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Muokkaa numeroa ennen puhelua"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Tyhjennä soittohistoria"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Poista vastaajaviesti"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arkistoi vastaajaviesti"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Jaa vastaajaviesti"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Viesti poistettiin."</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Vastaajaviesti arkistoitiin."</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"KUMOA"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ARKISTOON"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Tyhjennetäänkö soittohistoria?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Kaikki soittohistorian tiedot poistetaan"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Tyhjennetään soittohistoriaa…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Puhelin"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Vastaamaton puhelu"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Vastaamaton työpuhelu"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Vastaamattomat puhelut"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> vastaamatonta puhelua"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Soita takaisin"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Viesti"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> vastaajaviestiä </item>
- <item quantity="one">Vastaajaviesti</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Toista"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Uusi vastaajaviesti: <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Viestin toistaminen epäonnistui"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Ladataan puhelinvastaajaa…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Arkistoidaan vastaajaviestiä…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Puhelinvastaajan lataaminen epäonnistui"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Vain vastaajaan menneet puhelut"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Vain saapuvat puhelut"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Vain soitetut puhelut"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Vain vastaamattomat puhelut"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Visuaalinen puhelinvastaaja"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Voit tarkistaa ja kuunnella vastaajaviestit soittamatta palveluun. Tiedonsiirto voi olla maksullista."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Asetukset"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Puhelinvastaajan tiedot eivät ole käytettävissä"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Uusia viestejä vastaajassa. Lataus ei onnistu tällä hetkellä."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Määritä puhelinvastaajan asetukset"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Ääni ei ole käytettävissä"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Asetusten määritys"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Soita vastaajaan"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Valitse numero"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Valitse numero"</string>
- <string name="make_primary" msgid="5829291915305113983">"Muista valinta"</string>
- <string name="description_search_button" msgid="3660807558587384889">"haku"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"soita"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"numero johon soitetaan"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Aloita tai lopeta toisto"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Ota kaiutin käyttöön tai poista käytöstä"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Toisto-osoitin"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Hidasta toistoa"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Nopeuta toistoa"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Soittohistoria"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Lisää vaihtoehtoja"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"näppäimistö"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Näytä vain soitetut"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Näytä vain saapuneet"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Näytä vain vastaamattomat"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Näytä vain vastaajaviestit"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Näytä kaikki puhelut"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Lisää 2 sekunnin tauko"</string>
- <string name="add_wait" msgid="3360818652790319634">"Lisää tauko"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Asetukset"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Uusi yhteystieto"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Kaikki yhteystiedot"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Puhelun tiedot"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Tiedot eivät ole käytettävissä"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Käytä näppäimistöä"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Palaa käynnissä olevaan puheluun"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Lisää puhelu"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Saapuva puhelu"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Lähtevä puhelu"</string>
- <string name="type_missed" msgid="2720502601640509542">"Vastaamatta jäänyt puhelu"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Saapuva videopuhelu"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Soitettava videopuhelu"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Vastaamaton videopuhelu"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Vastaaja"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Hylätty puhelu"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Estetty puhelu"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Saapuvat puhelut"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Toista vastaajaviesti"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Näytä yhteystieto <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Soita: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Yhteystiedot: <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> puhelua."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videopuhelu."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Lähetä tekstiviesti: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Kuuntelematon vastaajaviesti"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Aloita puhehaku"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Soita <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Tuntematon"</string>
- <string name="voicemail" msgid="3851469869202611441">"Vastaaja"</string>
- <string name="private_num" msgid="6374339738119166953">"Yksityinen numero"</string>
- <string name="payphone" msgid="7726415831153618726">"Maksupuhelin"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> klo <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>.<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Numeroon ei voi soittaa"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Määritä puhelinvastaajan asetukset kohdassa Valikko &gt; Asetukset."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Poista lentokonetila käytöstä ennen vastaajaan soittamista."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Ladataan..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI-koodi"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Ladataan SIM-kortilta…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM-kortin yhteystiedot"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Ei käytettävissä olevaa yhteystietosovellusta"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Puhehaku ei ole käytettävissä"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Puhelua ei voi soittaa, koska Puhelin-sovellus on poistettu käytöstä."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Laitteessa ei ole kyseiseen toimintoon tarvittavaa sovellusta"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Hae yhteystietoja"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Lisää numero tai hae yhteystiedoista"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Soittohistoriasi on tyhjä."</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Soita puhelu"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Sinulla ei ole vastaamattomia puheluita."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Puhelinvastaajasi postilaatikko on tyhjä."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Vastaaja-arkisto on tyhjä."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Näytä vain suosikit"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Soittohistoria"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arkistoidut vastaajaviestit"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Kaikki"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Vastaamattomat"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Vastaaja"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Uusi kätevämpi estotapa"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Puhelimen on muokattava estoperiaatetta, jotta suojaus toimii paremmin. Nyt estetyistä numeroista saapuvat puhelut ja tekstiviestit estetään, ja estotiedot voidaan jakaa muille sovelluksille."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Salli"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Estetäänkö <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Tästä numerosta tulevat puhelut estetään, ja vastaajaviestit poistetaan automaattisesti."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Tästä numerosta tulevat puhelut estetään, mutta soittaja voi silti mahdollisesti jättää vastaajaviestejä."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Tästä numerosta tulevat puhelut ja tekstiviestit estetään."</string>
- <string name="block_number_ok" msgid="770551992296781873">"ESTÄ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Kumotaanko numeron <xliff:g id="NUMBER">%1$s</xliff:g> esto?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"KUMOA ESTO"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Pikavalinta"</string>
- <string name="tab_history" msgid="2563144697322434940">"Soittohistoria"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Yhteystiedot"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Puhelinvastaaja"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Poistettu suosikeista"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Kumoa"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Soita <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Luo uusi yhteystieto"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Lisää yhteystietoihin"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Lähetä tekstiviesti"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Soita videopuhelu"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Estä numero"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> uutta vastaamatonta puhelua"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Pikavalinnassa ei ole vielä yhtään yhteystietoa."</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Lisää suosikki"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Sinulla ei ole yhteystietoja."</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Lisää yhteystieto"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Katso kaikki numerot koskettamalla kuvaa tai järjestele tietoja koskettamalla kuvaa pitkään"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Poista"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videopuhelu"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Lähetä viesti"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Puhelun tiedot"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Soita: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Vastaamaton puhelu soittajalta <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Vastattu puhelu soittajalta <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Lukematon vastaajaviesti: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Vastaajaviesti: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Soita vastaanottajalle <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"numerosta <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"numerosta <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"tililtä <xliff:g id="PHONEACCOUNT">%1$s</xliff:g> numerosta <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> numerosta <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Soita"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Soita: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Soita videopuhelu: <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Kuuntele vastaajaviesti: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Toista vastaajaviesti lähettäjältä <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Keskeytä vastaajaviesti lähettäjältä <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Poista vastaajaviesti lähettäjältä <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> uutta vastaajaviestiä</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> uusi vastaajaviesti</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Luo kontakti: <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Lisää <xliff:g id="NAMEORNUMBER">^1</xliff:g> olemassa olevaan kontaktiin."</string>
- <string name="description_details_action" msgid="2433827152749491785">"Yhteystiedon <xliff:g id="NAMEORNUMBER">^1</xliff:g> puhelutiedot"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Poistettu soittohistoriasta"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Tänään"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Eilen"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Vanhempi"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Puheluluettelo"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Ota kaiutin käyttöön."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Poista kaiutin käytöstä."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Toista nopeammin."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Toista hitaammin."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Aloita tai keskeytä toisto."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Näyttöasetukset"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Äänet ja värinä"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Esteettömyys"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Puhelimen soittoääni"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Värinä myös puheluille"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Näppäimistön äänet"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Näppäimistön äänen pituus"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normaali"</item>
- <item msgid="6177579030803486015">"Pitkä"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Pikavastaukset"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Puhelut"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Puhelujen esto"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Puheluesto väliaikaisesti pois käytöstä"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Puheluiden estäminen on poistettu käytöstä, koska olet ottanut yhteyttä hätäpalveluihin tästä puhelimesta viimeisen 48 tunnin aikana. Esto otetaan automaattisesti uudelleen käyttöön, kun puhelusta on kulunut 48 tuntia."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Tuo numerot"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Valitsit aiemmin muilla sovelluksilla, että tiettyjen soittajien puhelut siirretään automaattisesti vastaajaan."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Näytä numerot"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Tuo"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Tuonti epäonnistui."</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Vastaajaviestin arkistointi epäonnistui."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Poista numeron esto"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Lisää numero"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Näistä numeroista tulevat puhelut estetään, ja vastaajaviestit poistetaan automaattisesti."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Näistä numeroista tulevat puhelut estetään, mutta soittajat voivat silti mahdollisesti jättää vastaajaviestejä."</string>
- <string name="block_list" msgid="7760188925338078011">"Estetyt numerot"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> on virheellinen."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> on jo estetty."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Puhelujen esto poistettiin käytöstä 48 tunnin ajaksi"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Toiminto poistettiin käytöstä hätäpuhelun vuoksi."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Puhelutilit"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Ota käyttöön"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Määritä käyttöoikeudet"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Jos haluat käyttää pikavalintaa, ota Yhteystiedot-käyttöoikeus käyttöön."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Jos haluat katsella puhelulokiasi, ota Puhelin-käyttöoikeus käyttöön."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Jos haluat katsella yhteystietojasi, ota Yhteystiedot-käyttöoikeus käyttöön."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Jos haluat käyttää puhelinvastaajaa, ota Puhelin-käyttöoikeus käyttöön."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Jos haluat hakea kontaktejasi, ota käyttöön kontaktien käyttöoikeudet."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Jos haluat soittaa puheluja, ota Puhelin-käyttöoikeus käyttöön."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Puhelinsovelluksella ei ole oikeutta muokata järjestelmän asetuksia."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Estetyt"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> on aktiivinen."</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Estä / ilmoita häiriköinnistä"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Estä"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Ei ole häirikkösoittaja"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Kumoa esto"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Häirikköpuhelut"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Estetäänkö <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Kaikki tästä numerosta tulevat puhelut ja vastaajaviestit estetään jatkossa."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Ilmoita häirikköpuheluksi"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Kaikki puhelut ja vastaajaviestit tästä numerosta estetään. Puhelu ilmoitetaan häirikköpuheluksi."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Kumotaanko numeron <xliff:g id="NUMBER">%1$s</xliff:g> esto?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Numeron esto ja häirikkötila kumotaan. Puhelut ja vastaajaviestit tästä numerosta eivät ole häirikköpuheluja."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Lisätäänkö <xliff:g id="NUMBER">%1$s</xliff:g> sallittujen luetteloon?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Sallittujen luettelo"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Puhelut ja vastaajaviestit tästä numerosta eivät ole häirikköpuheluja. Numeroa ei ilmoiteta häirikkönumeroksi."</string>
-</resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
deleted file mode 100644
index 4007c8185..000000000
--- a/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Téléphone"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Téléphone"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Clavier du téléphone"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Téléphone"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Historique des appels"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Signaler un numéro incorrect"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Copier le numéro"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Copier la transcription"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Bloquer le numéro"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Le numéro « <xliff:g id="NUMBER">%1$s</xliff:g> » est bloqué"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Débloquer le numéro"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Le numéro « <xliff:g id="NUMBER">%1$s</xliff:g> » est débloqué"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ANNULER"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Supprimer"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Modifier le numéro avant l\'appel"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Effacer l\'historique des appels"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Supprimer le message vocal"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Archiver les messages vocaux"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Partager les messages vocaux"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Mess. vocal supprimé"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Messages vocaux archivés"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ANNULER"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ARCHIVE"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Effacer l\'historique des appels?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Tous les appels seront supprimés de votre historique."</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Suppression historique des appels…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Téléphone"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Appel manqué"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Appel professionnel manqué"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Appels manqués"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> appels manqués"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Rappeler"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Message"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"><xliff:g id="COUNT">%1$d</xliff:g> message vocal </item>
- <item quantity="other"><xliff:g id="COUNT">%1$d</xliff:g> messages vocaux </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Lire"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nouveau message vocal de <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Impossible de lire le message vocal"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Chargement du message vocal en cours…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Archivage des messages vocaux en cours…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Impossible de charger la messagerie vocale"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Seulement les appels avec message vocal"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Seulement les appels entrants"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Seulement les appels sortants"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Seulement les appels manqués"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Messagerie vocale visuelle"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Affichez et écoutez vos messages vocaux, sans devoir appeler un numéro. Des frais supplémentaires liés à la consommation de données peuvent s\'appliquer."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Paramètres"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Impossible de mettre à jour la messagerie vocale"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Nouveau message vocal. Impossible de le charger pour le moment."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Configurez votre messagerie vocale"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio non disponible"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Configurer"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Appeler mess. vocale"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Sélectionner un numéro"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Sélectionner un numéro"</string>
- <string name="make_primary" msgid="5829291915305113983">"Mémoriser ce choix"</string>
- <string name="description_search_button" msgid="3660807558587384889">"rechercher"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"composer"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"numéro à composer"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Lire ou arrêter la lecture"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Activer ou désactiver le haut-parleur"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Rechercher une position de lecture"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Réduire la vitesse de lecture"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Accroître la vitesse de lecture"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Historique des appels"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Plus d\'options"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"clavier numérique"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Afficher appels sortants uniq."</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Afficher appels entrants uniq."</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Afficher appels manqués uniq."</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Messages vocaux uniquement"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Afficher tous les appels"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Ajouter une pause de 2 s"</string>
- <string name="add_wait" msgid="3360818652790319634">"Ajouter une attente"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Paramètres"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Nouveau contact"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Tous les contacts"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Détails sur l\'appel"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Les détails ne sont pas disponibles"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Utiliser le clavier DTMF"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Reprendre l\'appel en cours"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Autre appel"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Appel entrant"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Appel sortant"</string>
- <string name="type_missed" msgid="2720502601640509542">"Appel manqué"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Appel vidéo entrant"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Appel vidéo sortant"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Appel vidéo manqué"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Messagerie vocale"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Appel refusé"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Appel bloqué"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Appels entrants"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Écouter le message vocal"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Afficher le contact <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Appeler <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Coordonnées de <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> appels."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Appel vidéo."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Envoyer un texto à <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Nouveau message vocal"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Démarrer la recherche vocale"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Appeler le <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Inconnu"</string>
- <string name="voicemail" msgid="3851469869202611441">"Messagerie vocale"</string>
- <string name="private_num" msgid="6374339738119166953">"Numéro privé"</string>
- <string name="payphone" msgid="7726415831153618726">"Cabine téléphonique"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min et <xliff:g id="SECONDS">%s</xliff:g> sec"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> à <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Vous ne pouvez pas appeler ce numéro"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Pour configurer la messagerie vocale, accédez à Menu &gt; Paramètres."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Veuillez désactiver le mode Avion avant d\'appeler la messagerie vocale."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Chargement en cours..."</string>
- <string name="imei" msgid="3045126336951684285">"Code IIEM"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Chargement depuis la carte SIM..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Contacts de carte SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Aucune application de gestion des contacts n\'est disponible"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Recherche vocale non disponible"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Impossible d\'effectuer un appel téléphonique, car l\'application Téléphone a été désactivée."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Cette action ne peut être effectuée par aucune application sur cet appareil."</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Rechercher des contacts"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Ajouter nº ou chercher contact"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Votre historique des appels est vide"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Faire un appel"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Vous n\'avez aucun appel manqué."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"La boîte de réception de votre messagerie vocale est vide."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Votre archive de messages vocaux est vide."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Afficher les contacts favoris uniquement"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Historique des appels"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Archive des messages vocaux"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Tous"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Manqués"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Mess. voc."</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Nouveau blocage simplifié"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Afin de mieux vous protéger, l\'application Téléphone doit modifier la façon dont le blocage fonctionne. Le blocage de numéros s\'appliquera désormais aux appels et aux messages texte et pourra être partagé avec d\'autres applications."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Autoriser"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Bloquer le numéro <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Les appels provenant de ce numéro seront bloqués et les messages vocaux seront automatiquement supprimés."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Les appels provenant de ce numéro seront bloqués, mais il se peut que l\'appelant puisse quand même vous laisser des messages vocaux."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Vous ne recevrez plus d\'appels ni de messages texte de ce numéro."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOQUER"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Débloquer le numéro <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DÉBLOQUER"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Composition abrégée"</string>
- <string name="tab_history" msgid="2563144697322434940">"Historique des appels"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Contacts"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Messagerie vocale"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Supprimé des favoris"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Annuler"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Appeler le <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Créer un contact"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Ajouter à un contact"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Envoyer un texto"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Faire un appel vidéo"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Bloquer le numéro"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> nouveaux appels manqués"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Aucun contact ne figure dans vos numéros de composition abrégée"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Ajouter un favori"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Vous n\'avez pas encore de contacts"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Ajouter un contact"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Touchez l\'image pour afficher tous les numéros, ou maintenez votre doigt sur l\'écran pour les réorganiser"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Supprimer"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Appel vidéo"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Envoyer un message"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Détails de l\'appel"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Appeler <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Appel manqué : <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Appel répondu : <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Message vocal non écouté de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Message vocal de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Appel effectué : <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"sur <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"au <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"au <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"sur <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, au <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, au <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Appeler"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Appeler <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Appel vidéo avec <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Écouter le message vocal de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Jouer le message vocal de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Interrompre le message vocal de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Supprimer le message vocal de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> nouveau message vocal</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> nouveaux messages vocaux</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Créer un contact pour <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Ajouter <xliff:g id="NAMEORNUMBER">^1</xliff:g> à un contact existant"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Détails de l\'appel pour <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"L\'appel a bien été supprimé de l\'historique"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Aujourd\'hui"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Hier"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Plus anciens"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Liste des appels"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Activer le haut-parleur."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Désactiver le haut-parleur."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Lire plus vite."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Lire moins vite."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Lancer ou interrompre la lecture."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Options d\'affichage"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Sons et vibration"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Accessibilité"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Sonnerie du téléphone"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibrer aussi pour les appels"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tonalités du clavier"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Durée des tonalités du pavé numérique"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normale"</item>
- <item msgid="6177579030803486015">"Longue"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Réponses rapides"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Appels"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Blocage des appels"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Blocage appels désactivé temporairement"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Le blocage des appels a été désactivé, car vous avez communiqué avec les services d\'urgence à partir de ce téléphone au cours des dernières 48 heures. Le blocage sera réactivé automatiquement après 48 heures."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importer les numéros"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Vous avez indiqué que certains appelants devaient automatiquement être renvoyés vers la messagerie vocale par d\'autres applications."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Afficher les numéros"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importer"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Échec de l\'importation"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Échec de l\'archivage des messages vocaux"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Débloquer le numéro"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Ajouter un numéro"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Les appels provenant de ces numéros seront bloqués et les messages vocaux seront automatiquement supprimés."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Les appels provenant de ces numéros seront bloqués, mais il se peut que les appelants puissent quand même vous laisser des messages vocaux."</string>
- <string name="block_list" msgid="7760188925338078011">"Numéros bloqués"</string>
- <string name="invalidNumber" msgid="619058581062192851">"Le numéro <xliff:g id="NUMBER">%1$s</xliff:g> n\'est pas valide."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"Le numéro <xliff:g id="NUMBER">%1$s</xliff:g> est déjà bloqué."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Blocage des appels désactivé pendant 48 heures"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Désactivé, car un appel d\'urgence a été effectué"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Comptes d\'appel"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Activer"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Définir les autorisations"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Pour activer la composition abrégée, activez l\'autorisation Contacts."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Pour consulter votre journal d\'appels, activez l\'autorisation Téléphone."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Pour consulter vos contacts, activez l\'autorisation Contacts."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Pour accéder à votre messagerie vocale, activez l\'autorisation Téléphone."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Pour rechercher vos contacts et les lieux à proximité, activez les autorisations Contacts."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Pour faire un appel, activez l\'autorisation Téléphone."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"L\'application Téléphone n\'est pas autorisée à modifier les paramètres du système."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Bloqué"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"Appel en cours avec : <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Bloquer/signaler comme pourriel"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Bloquer"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"N\'est pas un pourriel"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Débloquer"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Pourriel"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Bloquer le numéro <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Les futurs appels et messages vocaux en provenance de ce numéro seront bloqués."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Signaler l\'appel comme pourriel"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Les appels et messages vocaux de ce numéro seront bloqués. Cet appel sera signalé comme pourriel."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Débloquer le numéro <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Ce numéro sera débloqué. Les futurs appels et messages vocaux ne seront pas signalés comme pourriel."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Ajouter le numéro <xliff:g id="NUMBER">%1$s</xliff:g> à la liste blanche?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Liste blanche"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Cet appel et les futurs appels et messages vocaux de ce numéro ne seront pas signalés comme pourriel."</string>
-</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
deleted file mode 100644
index 4022b75b7..000000000
--- a/res/values-fr/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Téléphone"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Téléphone"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Clavier du téléphone"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Tél."</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Historique des appels"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Signaler un numéro incorrect"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Copier le numéro"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Copier la transcription"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Bloquer le numéro"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Le numéro \"<xliff:g id="NUMBER">%1$s</xliff:g>\" est bloqué."</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Débloquer le numéro"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Le numéro \"<xliff:g id="NUMBER">%1$s</xliff:g>\" est débloqué."</string>
- <string name="block_number_undo" msgid="591338370336724156">"ANNULER"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Supprimer"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Modifier numéro avant d\'appeler"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Supprimer l\'historique des appels"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Supprimer le message vocal"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Archiver le message vocal"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Partager le message vocal"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Mess. vocal supprimé"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Message vocal archivé"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ANNULER"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ACCÉDER À L\'ARCHIVE"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Supprimer l\'historique des appels ?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Tous les appels seront supprimés de votre historique."</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Suppression historique des appels…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Téléphoner"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Appel manqué"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Appel professionnel manqué"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Appels manqués"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> appels manqués"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Rappeler"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Envoyer un SMS"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> message vocal </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> messages vocaux </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Lire"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nouveau message vocal de <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Impossible de lire le message vocal."</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Chargement du message vocal en cours…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Archivage du message vocal…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Impossible de charger le message vocal."</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Seulement les appels avec message vocal"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Seulement les appels entrants"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Seulement les appels sortants"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Seulement les appels manqués"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Messagerie vocale visuelle"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Affichez et écoutez vos messages vocaux, sans devoir appeler un numéro. Des frais supplémentaires liés à la consommation de données peuvent être facturés."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Paramètres"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Impossible de mettre à jour la messagerie vocale."</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Nouveau message vocal. Impossible de le charger pour le moment."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Configurer votre messagerie vocale"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio non disponible"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Configuration"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Appeler mess. vocale"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Sélectionner un numéro"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Sélectionner un numéro"</string>
- <string name="make_primary" msgid="5829291915305113983">"Mémoriser ce choix"</string>
- <string name="description_search_button" msgid="3660807558587384889">"rechercher"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"composer"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"numéro à composer"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Lancer ou interrompre la lecture"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Activer ou désactiver le haut-parleur"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Rechercher une position de lecture"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Réduire la vitesse de lecture"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Accroître la vitesse de lecture"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Historique des appels"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Plus d\'options"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"clavier numérique"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Afficher appels sortants uniq."</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Afficher appels entrants uniq."</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Afficher appels manqués uniq."</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Messages vocaux uniquement"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Afficher tous les appels"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Ajouter une pause de 2 s"</string>
- <string name="add_wait" msgid="3360818652790319634">"Ajouter une attente"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Paramètres"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Nouveau contact"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Tous les contacts"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Informations sur l\'appel"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Détails non disponibles"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Utiliser le clavier DTMF"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Reprendre l\'appel en cours"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Autre appel"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Appel entrant"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Appel sortant"</string>
- <string name="type_missed" msgid="2720502601640509542">"Appel manqué"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Appel vidéo entrant"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Appel vidéo sortant"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Appel vidéo manqué"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Messagerie vocale"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Appel refusé"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Appel bloqué"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Appels entrants"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Écouter le message vocal"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Afficher le contact <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Appeler <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Coordonnées associées à <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> appels"</string>
- <string name="description_video_call" msgid="2933838090743214204">"Appel vidéo"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Envoyer un SMS à <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Nouveau message vocal"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Démarrer la recherche vocale"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Appeler le <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Inconnu"</string>
- <string name="voicemail" msgid="3851469869202611441">"Messagerie vocale"</string>
- <string name="private_num" msgid="6374339738119166953">"Numéro privé"</string>
- <string name="payphone" msgid="7726415831153618726">"Cabine téléphonique"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> secondes"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min et <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> à <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Impossible d\'appeler ce numéro."</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Pour configurer la messagerie vocale, accédez à Menu &gt; Paramètres."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Veuillez désactiver le mode Avion avant d\'appeler la messagerie vocale."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Chargement…"</string>
- <string name="imei" msgid="3045126336951684285">"Code IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Chargement depuis la carte SIM..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Contacts de carte SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Aucune application de gestion des contacts n\'est disponible."</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Recherche vocale non disponible"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Impossible d\'effectuer un appel téléphonique, car l\'application Téléphone a été désactivée."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Cette action ne peut être effectuée via aucune application sur cet appareil."</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Rechercher dans vos contacts"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Ajouter num. ou rech. contacts"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Votre historique des appels est vide."</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Passer un appel"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Vous n\'avez aucun appel manqué."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"La boîte de réception de la messagerie vocale est vide."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"L\'archive de messages vocaux est vide."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Afficher les contacts ajoutés aux favoris uniquement"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Historique des appels"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Archive de messages vocaux"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Tous"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Manqués"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Mess. vocale"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Nouveau processus de blocage simplifié"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Afin de mieux vous protéger, la fonctionnalité de blocage de numéros doit être modifiée. Le blocage pourra désormais être appliqué aux appels entrants comme aux SMS entrants, et être étendu à d\'autres applications."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Autoriser"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Bloquer le numéro \"<xliff:g id="NUMBER">%1$s</xliff:g>\" ?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Les appels associés à ce numéro seront bloqués, et les messages vocaux seront automatiquement supprimés."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Les appels associés à ce numéro seront bloqués, mais l\'appelant pourra peut-être toujours vous laisser des messages vocaux."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Vous ne recevrez plus d\'appels ni de SMS provenant de ce numéro."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOQUER"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Débloquer le numéro \"<xliff:g id="NUMBER">%1$s</xliff:g>\" ?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DÉBLOQUER"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Numérotation abrégée"</string>
- <string name="tab_history" msgid="2563144697322434940">"Historique des appels"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Contacts"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Messagerie vocale"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Supprimé des favoris."</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Annuler"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Appeler le <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Créer un contact"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Ajouter à un contact"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Envoyer un SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Passer un appel vidéo"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Bloquer le numéro"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"Nouveaux appels manqués : <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Vous n\'avez encore défini la numérotation abrégée pour aucun contact."</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Ajouter un favori"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Vous n\'avez pas encore de contacts."</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Ajouter un contact"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Appuyer sur l\'image pour afficher tous les numéros, ou appuyer dessus de manière prolongée pour réorganiser ces derniers"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Supprimer"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Appel vidéo"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Envoyer un message"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Informations sur l\'appel"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Appeler <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Appel manqué <xliff:g id="TIMEOFCALL">^3</xliff:g> (appelant : <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>)"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Appel reçu <xliff:g id="TIMEOFCALL">^3</xliff:g> (appelant : <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>)"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Message vocal à écouter de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Message vocal de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Appel émis <xliff:g id="TIMEOFCALL">^3</xliff:g> (destinataire : <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>)"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"sur <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"au <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"au <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"sur <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, au <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> au <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Appeler"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Appeler <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Appel vidéo de \"<xliff:g id="NAMEORNUMBER">^1</xliff:g>\""</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Écouter le message vocal laissé par le numéro ou le contact \"<xliff:g id="NAMEORNUMBER">^1</xliff:g>\""</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Lire le message vocal de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Interrompre le message vocal de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Supprimer le message vocal de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> nouveau message vocal</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> nouveaux messages vocaux</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Créer un contact pour \"<xliff:g id="NAMEORNUMBER">^1</xliff:g>\""</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Ajouter \"<xliff:g id="NAMEORNUMBER">^1</xliff:g>\" à un contact"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Informations sur l\'appel pour <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"L\'appel a bien été supprimé de l\'historique."</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Aujourd\'hui"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Hier"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Plus anciens"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Liste des appels"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Activer le haut-parleur"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Désactiver le haut-parleur"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Lire plus vite"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Lire moins vite"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Lancer ou suspendre la lecture"</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Options d\'affichage"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Sons et vibreur"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Accessibilité"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Sonnerie du téléphone"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibrer aussi pour les appels"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Son du clavier"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Durée de la tonalité du clavier"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normale"</item>
- <item msgid="6177579030803486015">"Longue"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Réponses rapides"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Appels"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Blocage des appels"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Blocage appels désactivé temporairement"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Le blocage des appels a été désactivé, car vous avez contacté les services d\'urgence à l\'aide de ce téléphone au cours des dernières 48 heures. Le blocage sera réactivé automatiquement après 48 heures."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importer les numéros"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Vous avez indiqué que certains appelants devaient automatiquement être renvoyés vers la messagerie vocale via d\'autres applications."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Afficher les numéros"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importer"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Échec lors de l\'importation."</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Échec de l\'archivage du message vocal."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Débloquer le numéro"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Ajouter un numéro"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Les appels associés à ces numéros seront bloqués, et les messages vocaux seront automatiquement supprimés."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Les appels associés à ces numéros seront bloqués, mais les appelants pourront peut-être toujours vous laisser des messages vocaux."</string>
- <string name="block_list" msgid="7760188925338078011">"Numéros bloqués"</string>
- <string name="invalidNumber" msgid="619058581062192851">"Le numéro \"<xliff:g id="NUMBER">%1$s</xliff:g>\" n\'est pas valide."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"Le numéro \"<xliff:g id="NUMBER">%1$s</xliff:g>\" est déjà bloqué."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Blocage des appels désactivé pendant 48 heures"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Désactivé, car un appel d\'urgence a été effectué"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Comptes téléphoniques"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Activer"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Définir les autorisations"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Pour activer la numérotation abrégée, activez l\'autorisation Contacts."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Pour consulter votre journal d\'appels, activez l\'autorisation Téléphone."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Pour consulter vos contacts, activez l\'autorisation Contacts."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Pour accéder à votre messagerie vocale, activez l\'autorisation Téléphone."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Pour rechercher vos contacts, activez l\'autorisation Contacts."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Pour passer un appel, activez l\'autorisation Téléphone."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"L\'application Téléphone n\'est pas autorisée à modifier les paramètres du système."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Bloqué"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> est actif."</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Bloquer/Signaler le spam vocal"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Bloquer"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Numéro fiable"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Débloquer"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Indésirable"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Bloquer le numéro \"<xliff:g id="NUMBER">%1$s</xliff:g>\" ?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Les appels et les messages vocaux provenant de ce numéro seront bloqués."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Signaler comme indésirable"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Cet appel sera signalé comme indésirable. Les appels et messages vocaux de ce numéro seront bloqués."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Débloquer le numéro \"<xliff:g id="NUMBER">%1$s</xliff:g>\" ?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Ce numéro sera débloqué. Les appels et messages vocaux provenant de celui-ci seront acceptés."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Ajouter le numéro \"<xliff:g id="NUMBER">%1$s</xliff:g>\" à la liste blanche ?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Ajouter à la liste blanche"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Ce numéro sera signalé comme fiable. Les appels et messages vocaux qui en proviennent seront acceptés."</string>
-</resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
deleted file mode 100644
index 46cbc7d37..000000000
--- a/res/values-gl/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Teléfono"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Teléfono"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Teclado de marcación do teléfono"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Teléfono"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Historial de chamadas"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Informar dun número incorrecto"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Copiar número"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Copiar transcrición"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Bloquear número"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Bloqueouse o <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Desbloquear número"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Desbloqueouse o <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="block_number_undo" msgid="591338370336724156">"DESFACER"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Eliminar"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Editar número antes de chamar"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Borrar historial de chamadas"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Eliminar correo de voz"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arquivar correo de voz"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Compartir correo de voz"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Correo voz eliminado"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Correo de voz arquivado"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"DESFACER"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ARQUIVO"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Borrar o historial de chamadas?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Esta acción eliminará todas as chamadas do teu historial"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Borrando historial de chamadas…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Teléfono"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Chamada perdida"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Chamada de traballo perdida"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Chamadas perdidas"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> chamadas perdidas"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Devolver chamada"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Mensaxe"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> correos de voz </item>
- <item quantity="one">Correo de voz</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Reproducir"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Novo correo de voz de <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Non se puido reproducir o correo de voz"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Cargando correo de voz…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Arquivando correo de voz…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Non se puido cargar o correo de voz"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Só chamadas con correo de voz"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Só chamadas entrantes"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Só chamadas saíntes"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Só chamadas perdidas"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Correo de voz visual"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Consulta e escoita o teu correo de voz sen ter que chamar a un número. É posible que se apliquen tarifas de datos."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Configuración"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Actualizacións de correo de voz non dispoñibles"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Novo correo de voz en espera. Non se pode cargar neste momento."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Configura o teu correo de voz"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio non dispoñible"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Configurar"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Chamar correo de voz"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Escoller número"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Escoller número"</string>
- <string name="make_primary" msgid="5829291915305113983">"Lembrar esta opción"</string>
- <string name="description_search_button" msgid="3660807558587384889">"buscar"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"marcar"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"número que se vai marcar"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Reproducir ou deter a reprodución"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Activar ou desactivar o altofalante"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Buscar posición de reprodución"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Reducir a velocidade da reprodución"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Aumentar a velocidade de reprodución"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Historial de chamadas"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Máis opcións"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"teclado de marcación"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Mostrar só as saíntes"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Mostrar só as entrantes"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Mostrar só as perdidas"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Mostrar só os correos de voz"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Mostrar todas as chamadas"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Engadir pausa de 2 segundos"</string>
- <string name="add_wait" msgid="3360818652790319634">"Engadir espera"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Configuración"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Novo contacto"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Todos os contactos"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Detalles da chamada"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Detalles non dispoñibles"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Usar teclado de tons táctiles"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Volver á chamada en curso"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Engadir chamada"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Chamada entrante"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Chamada saínte"</string>
- <string name="type_missed" msgid="2720502601640509542">"Chamada perdida"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Videochamada entrante"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Videochamada saínte"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Videochamada perdida"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Correo de voz"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Chamada rexeitada"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Chamada bloqueada"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Chamadas entrantes"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Reproducir correo de voz"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Ver contacto <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Chamar a <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Detalles de contacto de <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> chamadas"</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videochamada"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Envía unha SMS a <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Correo de voz sen escoitar"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Iniciar busca por voz"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Chamar ao <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Descoñecidos"</string>
- <string name="voicemail" msgid="3851469869202611441">"Correo de voz"</string>
- <string name="private_num" msgid="6374339738119166953">"Número privado"</string>
- <string name="payphone" msgid="7726415831153618726">"Teléfono público"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> ás <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> (<xliff:g id="DURATION">%2$s</xliff:g>)"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Non é posible chamar a este número"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Para configurar o correo de voz, vai a Menú &gt; Configuración."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Para chamar ao correo de voz, primeiro desactiva o modo avión."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Cargando..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Cargando da tarxeta SIM..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Contactos da tarxeta SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Non hai aplicacións de contactos dispoñibles"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Busca de voz non dispoñible"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Non se pode facer unha chamada telefónica porque se desactivou a aplicación de teléfono."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Non hai ningunha aplicación para esa acción neste dispositivo"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Buscar contactos"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Engade número/busca contactos"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"O teu historial de chamadas está baleiro"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Facer unha chamada"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Non tes chamadas perdidas."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"A caixa de entrada do correo de voz está baleira."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"O teu arquivo de correo de voz está baleiro."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Mostrar só os favoritos"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Historial de chamadas"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arquivo de correo de voz"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Todas"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Perdidas"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Correo de voz"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Novo bloqueo simplificado"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Para protexerte mellor, a aplicación do teléfono necesita cambiar o funcionamento do bloqueo. Os teus números bloqueados agora deterán as chamadas e as mensaxes de texto e, ademais, pode que se compartan con outras aplicacións."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Permitir"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Queres bloquear o <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Bloquearanse as chamadas deste número e eliminaranse automaticamente os correos de voz."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Bloquearanse as chamadas deste número, pero é posible que o emisor da chamada aínda poida deixarche correos de voz."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Xa non recibirás máis chamadas nin mensaxes de texto deste número."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOQUEAR"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Queres desbloquear o <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DESBLOQUEAR"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Marcación rápida"</string>
- <string name="tab_history" msgid="2563144697322434940">"Historial de chamadas"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Contactos"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Correo de voz"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Eliminado dos favoritos"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Desfacer"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Chamar a <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Crear novo contacto"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Engadir a un contacto"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Enviar SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Realizar unha videochamada"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Bloquear número"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> chamadas perdidas novas"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Aínda non tes ningún contacto na marcación directa"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Engadir un favorito"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Aínda non tes contactos"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Engadir un contacto"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Toca a imaxe para ver todos os números ou mantena premida para reordenala"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Eliminar"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videochamada"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Enviar unha mensaxe"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Detalles da chamada"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Chamar a <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Chamada perdida desde <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Chamada respondida desde <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Correo de voz non lido de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Correo de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Chamada a <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"en <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"a través do <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"a través do <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"en <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, a través do <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> a través do <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Chamar"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Chamar a <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Videochamada a <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Escoitar o correo de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Reproducir correo de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pausar correo de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Eliminar correo de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> correos de voz novos</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> correo de voz novo</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Crear contacto para <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Engadir <xliff:g id="NAMEORNUMBER">^1</xliff:g> ao contacto existente"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Detalles da chamada para <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Eliminouse do historial de chamadas"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Hoxe"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Onte"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Anteriores"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Lista de chamadas"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Activar o altofalante."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Desactivar o altofalante."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Reproducir máis rápido."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Reproducir máis lento."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Iniciar ou pausar a reprodución."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Opcións de visualización"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Sons e vibración"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Accesibilidade"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Ton de chamada do teléfono"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibrar tamén nas chamadas"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tons do teclado de marcación"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Duración do ton do teclado de marcación"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"Longa"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Respostas rápidas"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Chamadas"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Bloqueo de chamadas"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"O bloqueo de chamadas desactivouse temporalmente"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"O bloqueo de chamadas desactivouse porque contactaches cos servizos de emerxencias desde este teléfono nas últimas 48 horas. Volverase activar automaticamente unha vez que pase o período de 48 horas."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importar números"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Previamente marcaches algúns emisores da chamada para que se envíen automaticamente ao correo de voz a través doutras aplicacións."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Ver números"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importar"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Produciuse un erro na importación"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Erro ao arquivar o correo de voz."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Desbloquear número"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Engadir número"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Bloquearanse as chamadas destes números e eliminaranse automaticamente os correos de voz."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Bloquearanse as chamadas destes números, pero é posible que aínda poidan deixarche correos de voz."</string>
- <string name="block_list" msgid="7760188925338078011">"Números bloqueados"</string>
- <string name="invalidNumber" msgid="619058581062192851">"O <xliff:g id="NUMBER">%1$s</xliff:g> non e válido."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"O <xliff:g id="NUMBER">%1$s</xliff:g> xa está bloqueado."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Desactivouse o bloqueo de chamadas durante 48 horas"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Desactivouse porque se realizou unha chamada de emerxencia."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Contas de chamadas"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Activar"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Establecer permisos"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Para activar a marcación rápida, activa o permiso de Contactos."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Para ver o teu rexistro de chamadas, activa o permiso de Teléfono."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Para ver os teus contactos, activa o permiso de Contactos."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Para acceder ao correo de voz, activa o permiso de Teléfono."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Para buscar os teus contactos, activa os permisos de Contactos."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Para facer unha chamada, activa o permiso de Teléfono."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"A aplicación Teléfono non ten permiso para modificar a configuración do sistema."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Bloqueado"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"Chamada activa: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Bloquear/marcar como spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Bloquear"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Non é spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Desbloquear"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Queres bloquear o <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Bloquearanse as próximas chamadas e correos de voz deste número."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Informar de que a chamada é spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Bloquearanse as futuras chamadas e correos de voz deste número. Informarase de que a chamada é spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Queres desbloquear o <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Desbloquearase o número e informarase de que non é spam. Aceptaranse as futuras chamadas e correos de voz."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Queres incluír o <xliff:g id="NUMBER">%1$s</xliff:g> na lista branca?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Lista branca"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Non se definirán como spam as futuras chamadas e correos de voz deste número. Tampouco se informará de que é spam."</string>
-</resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
deleted file mode 100644
index 138dace0b..000000000
--- a/res/values-gu/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"ફોન"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"ફોન"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"ફોન ડાયલપેડ"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"ફોન"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"કૉલ ઇતિહાસ"</string>
- <string name="action_report_number" msgid="4635403959812186162">"અચોક્કસ નંબરની જાણ કરો"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"નંબર કૉપિ કરો"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"ટ્રાંસ્ક્રિપ્શન કૉપિ કરો"</string>
- <string name="action_block_number" msgid="1482657602262262134">"નંબર અવરોધિત કરો"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> અવરોધિત કર્યો"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"નંબર અનાવરોધિત કરો"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> અનાવરોધિત કર્યો"</string>
- <string name="block_number_undo" msgid="591338370336724156">"પૂર્વવત્ કરો"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"કાઢી નાખો"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"કૉલ કરતાં પહેલાં નંબર સંપાદિત કરો"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"કૉલ ઇતિહાસ સાફ કરો"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"વૉઇસમેઇલ કાઢી નાખો"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"વૉઇસમેઇલને આર્કાઇવ કરો"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"વૉઇસમેઇલ શેર કરો"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"વૉઇસમેઇલ કાઢી નાખ્યો"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"વૉઇસમેઇલ આર્કાઇવ કર્યો"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"પૂર્વવત્ કરો"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"આર્કાઇવ પર જાઓ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"કૉલ ઇતિહાસ સાફ કરીએ?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"આ તમારા ઇતિહાસમાંથી તમામ કૉલ્સ કાઢી નાખશે"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"કૉલ ઇતિહાસ સાફ કરી રહ્યાં છે…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"ફોન"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"છૂટેલો કૉલ"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"ચૂકી ગયેલ કાર્ય કૉલ"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"છૂટેલા કૉલ્સ"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> છૂટેલા કૉલ"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"કૉલ બૅક કરો"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"સંદેશ"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> વૉઇસમેઇલ્સ </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> વૉઇસમેઇલ્સ </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"ચલાવો"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> તરફથી નવો વૉઇસમેઇલ."</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"વૉઇસમેઇલ ચલાવી શકાઈ નથી"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"વૉઇસમેઇલ લોડ કરી રહ્યું છે…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"વૉઇસમેઇલને આર્કાઇવ કરી રહ્યાં છે…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"વૉઇસમેઇલ લોડ કરી શકાઈ નથી"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"ફક્ત વૉઇસમેઇલ સાથેના કૉલ્સ"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"ફક્ત ઇનકમિંગ કૉલ્સ"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"ફક્ત આઉટગોઇંગ કૉલ્સ"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"ફક્ત છૂટી ગયેલ કૉલ્સ"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"વિઝ્યુઅલ વૉઇસમેઇલ"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"કોઈ નંબર પર કૉલ કર્યા વગર તમારી વૉઇસમેઇલ જુઓ અને સાંભળો. ડેટા શુલ્ક લાગુ પડી શકે છે."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"સેટિંગ્સ"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"વૉઇસમેઇલ અપડેટ્સ ઉપલબ્ધ નથી"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"નવી વૉઇસમેઇલ રાહ જુએ છે. અત્યારે લોડ કરી શકતાં નથી."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"તમારા વૉઇસમેઇલને સેટ કરો"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"ઑડિઓ ઉપલબ્ધ નથી"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"સેટ અપ"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"વૉઇસમેઇલ પર કૉલ કરો"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"નંબર પસંદ કરો"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"નંબર પસંદ કરો"</string>
- <string name="make_primary" msgid="5829291915305113983">"આ પસંદ યાદ રાખો"</string>
- <string name="description_search_button" msgid="3660807558587384889">"શોધો"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"ડાયલ કરો"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"ડાયલ કરવા માટેનો નંબર"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"પ્લેબેક ચલાવો અથવા રોકો"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"સ્પીકરફોન ચાલુ કે બંધ કરો"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"પ્લેબેક સ્થિતિ શોધો"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"પ્લેબેક રેટ ઘટાડો"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"પ્લેબેક રેટ વધારો"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"કૉલ ઇતિહાસ"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"વધુ વિકલ્પો"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"ડાયલ પેડ"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"ફક્ત આઉટગોઇંગ બતાવો"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"ફક્ત આવનારા બતાવો"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"ફક્ત છૂટેલ બતાવો"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"ફક્ત વૉઇસમેઇલ્સ બતાવો"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"તમામ કૉલ્સ બતાવો"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2-સંકડનો વિરામ ઉમેરો"</string>
- <string name="add_wait" msgid="3360818652790319634">"પ્રતીક્ષા ઉમેરો"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"સેટિંગ્સ"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"નવો સંપર્ક"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"તમામ સંપર્કો"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"કૉલની વિગતો"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"વિગતો ઉપલબ્ધ નથી"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"ટચ ટોન કીપેડનો ઉપયોગ કરો"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"કૉલ પર પાછા આવવું પ્રગતિ પર છે"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"કૉલ ઉમેરો"</string>
- <string name="type_incoming" msgid="6502076603836088532">"ઇનકમિંગ કૉલ"</string>
- <string name="type_outgoing" msgid="343108709599392641">"આઉટગોઇંગ કૉલ"</string>
- <string name="type_missed" msgid="2720502601640509542">"છૂટેલો કૉલ"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"ઇનકમિંગ વિડિઓ કૉલ"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"આઉટગોઇંગ વિડિઓ કૉલ"</string>
- <string name="type_missed_video" msgid="954396897034220545">"વિડિઓ કૉલ ચૂકી ગયાં"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"વૉઇસમેઇલ"</string>
- <string name="type_rejected" msgid="7783201828312472691">"નકારેલો કૉલ"</string>
- <string name="type_blocked" msgid="3521686227115330015">"અવરોધિત કૉલ"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"ઇનકમિંગ કૉલ્સ"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"વૉઇસમેઇલ ચલાવો"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> સંપર્ક જુઓ"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g> ને કૉલ કરો"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> માટે સંપર્ક વિગતો"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> કૉલ્સ."</string>
- <string name="description_video_call" msgid="2933838090743214204">"વિડિઓ કૉલ."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g> ને SMS મોકલો"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"વણસાંભળેલ વૉઇસમેઇલ"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"વૉઇસ શોધ શરૂ કરો"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> ને કૉલ કરો"</string>
- <string name="unknown" msgid="740067747858270469">"અજાણ્યાં"</string>
- <string name="voicemail" msgid="3851469869202611441">"વૉઇસમેઇલ"</string>
- <string name="private_num" msgid="6374339738119166953">"ખાનગી નંબર"</string>
- <string name="payphone" msgid="7726415831153618726">"પેફોન"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> સેકંડ"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> મિ <xliff:g id="SECONDS">%s</xliff:g> સે"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> નાં રોજ <xliff:g id="TIME">%2$s</xliff:g> વાગ્યે"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"આ નંબર પર કૉલ કરી શકતાં નથી"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"વૉઇસમેઇલ સેટ કરવા માટે, મેનૂ &gt; સેટિંગ્સ પર જાઓ."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"વૉઇસમેઇલ પર કૉલ કરવા માટે, પહેલાં એરપ્લેન મોડને બંધ કરો."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"લોડ કરી રહ્યું છે..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"SIM કાર્ડમાંથી લોડ કરી રહ્યું છે…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM કાર્ડ સંપર્કો"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"કોઈ સંપર્કો ઍપ્લિકેશન ઉપલબ્ધ નથી"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"વૉઇસ શોધ ઉપલબ્ધ નથી"</string>
- <string name="call_not_available" msgid="8941576511946492225">"ફોન કૉલ કરી શકાતો નથી કારણ કે ફોન ઍપ્લિકેશન અક્ષમ કરવામાં આવી છે."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"આ ઉપકરણ પર તે માટે કોઈ ઍપ્લિકેશન નથી"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"સંપર્કો શોધો"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"નંબર ઉમેરો અથવા સંપર્કો શોધો"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"તમારો કૉલ ઇતિહાસ ખાલી છે"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"કૉલ કરો"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"તમારી પાસે કોઇ છૂટેલાં કૉલ્સ નથી."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"તમારું વૉઇસમેઇલ ઇનબોક્સ ખાલી છે."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"તમારું વૉઇસમેઇલ આર્કાઇવ ખાલી છે."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"ફક્ત મનપસંદ બતાવો"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"કૉલ ઇતિહાસ"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"વૉઇસમેઇલ આર્કાઇવ"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"તમામ"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"છૂટેલ"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"વૉઇસમેઇલ"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"અવરોધિત કરવાની નવી, સરળ રીત"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"તમારી બહેતર સુરક્ષા માટે, ફોનને, અવરોધિત કરવું કેવી રીતે કાર્ય કરે છે તે બદલવાની જરૂર છે. તમારા અવરોધિત નંબર્સ હવે કૉલ્સ અને ટેક્સ્ટ્સ બન્નેને રોકશે અને તે અન્ય ઍપ્લિકેશનો સાથે શેર કરવામાં આવી શકે છે."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"મંજૂરી આપો"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> ને અવરોધિત કરીએ?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"આ નંબરથી આવનારા કૉલ્સ અવરોધિત કરવામાં આવશે અને વૉઇસમેઇલ્સ આપમેળે કાઢી નાખવામાં આવશે."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"આ નંબરથી આવનારા કૉલ્સ અવરોધિત કરવામાં આવશે, પરંતુ કૉલર હજી પણ તમારા માટે વૉઇસમેઇલ્સ મૂકવામાં સમર્થ હોઈ શકે છે."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"તમે હવે આ નંબરથી કૉલ્સ અથવા ટેક્સ્ટ પ્રાપ્ત કરશો નહીં."</string>
- <string name="block_number_ok" msgid="770551992296781873">"અવરોધિત કરો"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> ને અનાવરોધિત કરીએ?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"અનાવરોધિત કરો"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"સ્પીડ ડાયલ"</string>
- <string name="tab_history" msgid="2563144697322434940">"કૉલ ઇતિહાસ"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"સંપર્કો"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"વૉઇસમેઇલ"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"મનપસંદમાંથી દૂર કર્યું"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"પૂર્વવત્ કરો"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> ને કૉલ કરો"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"નવો સંપર્ક બનાવો"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"સંપર્કમાં ઉમેરો"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS મોકલો"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"વિડિઓ કૉલ કરો"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"નંબર અવરોધિત કરો"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> નવા છૂટેલા કૉલ્સ"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"તમારા ઝડપી ડાયલ પર હજી સુધી કોઇ નથી"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"એક મનપસંદ ઉમેરો"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"તમે હજી સુધી કોઇપણ સંપર્કો ધરાવતાં નથી"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"સંપર્ક ઉમેરો"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"બધા નંબર્સ જોવા માટે છબી ટેપ કરો અથવા પુનઃક્રમાંકિત કરવા માટે પકડી રાખો"</string>
- <string name="remove_contact" msgid="1080555335283662961">"દૂર કરો"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"વિડિઓ કૉલ"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"એક સંદેશ મોકલો"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"કૉલની વિગતો"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ને કૉલ કરો"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> નો છૂટેલ કૉલ"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> નો જવાબી કૉલ"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> તરફથી ન વાંચેલો વૉઇસમેઇલ"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> તરફથી વૉઇસમેઇલ"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> પર કૉલ."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> પર"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> મારફતે"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> મારફતે"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> પર, <xliff:g id="NUMBER">%2$s</xliff:g> મારફતે"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> પર, <xliff:g id="NUMBER">%2$s</xliff:g> મારફતે"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"કૉલ કરો"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ને કૉલ કરો"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ને વિડિઓ કૉલ કરો."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ના વૉઇસમેઇલને સાંભળો"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> માંથી વૉઇસમેઇલ ચલાવો"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> માંથી વૉઇસમેઇલ થોભાવો"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> માંથી વૉઇસમેઇલ કાઢી નાખો"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> નવી વૉઇસમેઇલ્સ</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> નવી વૉઇસમેઇલ્સ</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> માટે સંપર્ક બનાવો"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ને અસ્તિત્વમાંના સંપર્કમાં ઉમેરો"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> માટે કૉલ વિગતો"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"કૉલ ઇતિહાસમાંથી કાઢી નાખી"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"આજે"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"ગઈ કાલે"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"જૂનું"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"કૉલ્સની સૂચિ"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"સ્પીકર ચાલુ કરો."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"સ્પીકર બંધ કરો."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"વધુ ઝડપથી ચલાવો."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"વધુ ધીરેથી ચલાવો."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"પ્લેબેક ચલાવો અથવા થોભાવો"</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"પ્રદર્શન વિકલ્પો"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"ધ્વનિ અને વાઇબ્રેશન"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"ઍક્સેસિબિલિટી"</string>
- <string name="ringtone_title" msgid="760362035635084653">"ફોન રિંગટોન"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"કૉલ્સ માટે વાઇબ્રેટ પણ કરો"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"ડાયલપેડ ટોન્સ"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Dialpad ટોનની લંબાઈ"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"સામાન્ય"</item>
- <item msgid="6177579030803486015">"લાંબુ"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"ઝડપી પ્રતિસાદ"</string>
- <string name="call_settings_label" msgid="313434211353070209">"કૉલ્સ"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"કૉલ અવરોધ"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"કૉલ અવરોધિત કરવાનું અસ્થાયીરૂપે બંધ છે"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"કૉલ અવરોધિત કરવાનું અક્ષમ કરવામાં આવ્યું છે કારણ કે તમે આ ફોનમાંથી છેલ્લા 48 કલાકમાં કટોકટીની સેવાઓનો સંપર્ક કર્યો હતો. એકવાર 48 કલાકનો સમયગાળો સમાપ્ત થાય, પછી તે આપમેળે ફરીથી સક્ષમ કરવામાં આવશે."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"નંબર્સ આયાત કરો"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"તમે પહેલાં કેટલાક કૉલર્સને અન્ય ઍપ્લિકેશનો મારફતે વૉઇસમેઇલ પર આપમેળે મોકલવા માટે ચિહ્નિત કર્યા."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"સંખ્યા જુઓ"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"આયાત કરો"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"આયાત નિષ્ફળ થયું"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"વૉઇસમેઇલને આર્કાઇવ કરવામાં નિષ્ફળ થયાં."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"નંબર અનાવરોધિત કરો"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"નંબર ઉમેરો"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"આ નંબરથી આવનારા કૉલ્સ અવરોધિત કરવામાં આવશે અને વૉઇસમેઇલ્સ આપમેળે કાઢી નાખવામાં આવશે."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"આ નંબરથી આવનારા કૉલ્સ અવરોધિત કરવામાં આવશે, પરંતુ તેઓ હજી પણ તમારા માટે વૉઇસમેઇલ્સ મૂકવામાં સમર્થ હોઈ શકે છે."</string>
- <string name="block_list" msgid="7760188925338078011">"અવરોધિત નંબરો"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> અમાન્ય છે."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> ને પહેલેથી અવરોધિત કરવામાં આવ્યો છે."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"કૉલ અવરોધિત કરવાનું 48 કલાક માટે અક્ષમ કરાયું"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"અક્ષમ કરાયું કારણ કે એક કટોકટીનો કૉલ કરવામાં આવ્યો હતો."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"કૉલિંગ એકાઉન્ટ્સ"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"ચાલુ કરો"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"પરવાનગીઓ સેટ કરો"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"ઝડપી ડાયલ સક્ષમ કરવા માટે, સંપર્કોની પરવાનગી ચાલુ કરો."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"તમારો કૉલ લૉગ જોવા માટે, ફોન પરવાનગી ચાલુ કરો."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"તમારા સંપર્કો જોવા માટે, સંપર્કોની પરવાનગી ચાલુ કરો."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"તમારી વૉઇસમેઇલને ઍક્સેસ કરવા માટે, ફોન પરવાનગી ચાલુ કરો."</string>
- <string name="permission_no_search" msgid="84152933267902056">"તમારા સંપર્કો શોધવા માટે, સંપર્કોની પરવાનગીઓ ચાલુ કરો."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"કૉલ કરવા માટે, ફોન પરવાનગી ચાલુ કરો."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"ફોન એપ્લિકેશનને સિસ્ટમ સેટિંગ્સ પર લખવાની પરવાનગી નથી."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"અવરોધિત"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> સક્રિય છે"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"સ્પામની જાણ કરો/અવરોધિત કરો"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"અવરોધિત કરો"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"સ્પામ નથી"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"અનાવરોધિત કરો"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"સ્પામ"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> ને અવરોધિત કરીએ?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"આ નંબર પરથી આવનારા ભવિષ્યના કૉલ્સ અને વૉઇસમેઇલ્સ અવરોધિત કરવામાં આવશે."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"કૉલની સ્પામ તરીકે જાણ કરો"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"આ નંબર પરથી આવનારા ભાવિ કૉલ અને વૉઇસમેઇલ અવરોધિત કરવામાં આવશે. આ કૉલની સ્પામ તરીકે જાણ કરવામાં આવશે."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> ને અનાવરોધિત કરીએ?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"આ નંબર અનાવરોધિત કરી અને સ્પામ નથી તરીકે જાણ કરશે. ભાવિ કૉલ અને વૉઇસમેઇલ સ્પામ તરીકે ઓળખાશે નહીં."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> ને વ્હાઇટલિસ્ટ કરીએ?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"વ્હાઇટલિસ્ટ"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"આ નંબરથી ભાવિ કૉલ અને વૉઇસમેઇલ સ્પામ તરીકે ઓળખાશે નહીં. આ નંબરની સ્પામ નથી તરીકે જાણ કરવામાં આવશે."</string>
-</resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
deleted file mode 100644
index 56e3721d3..000000000
--- a/res/values-hi/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"फ़ोन"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"फ़ोन"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"फ़ोन डायलपैड"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"फ़ोन"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"कॉल इतिहास"</string>
- <string name="action_report_number" msgid="4635403959812186162">"गलत नंबर की रिपोर्ट करें"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"नंबर कॉपी करें"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"ट्रांसक्रिप्शन को कॉपी करें"</string>
- <string name="action_block_number" msgid="1482657602262262134">"नंबर अवरुद्ध करें"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> अवरोधित किया गया"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"नंबर अनवरोधित करें"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> अनवरोधित किया गया"</string>
- <string name="block_number_undo" msgid="591338370336724156">"वापस लाएं"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"हटाएं"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"कॉल करने से पहले नंबर संपादित करें"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"कॉल इतिहास साफ़ करें"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"वॉइसमेल हटाएं"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"वॉइसमेल संग्रहीत करें"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"वॉइसमेल साझा करें"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"वॉइसमेल हटाया गया"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"वॉइसमेल संग्रहीत किया गया"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"वापस लाएं"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"संग्रह पर जाएं"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"कॉल इतिहास साफ़ करें?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"इससे आपके इतिहास से सभी कॉल हटा दिए जाएंगे"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"कॉल इतिहास साफ़ किया जा रहा है…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"फ़ोन"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"छूटा कॉल"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"कार्यस्थल का छूटा हुआ कॉल"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"छूटे कॉल"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> छूटे कॉल"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"वापस कॉल करें"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"संदेश"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> वॉइसमेल </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> वॉइसमेल </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"चलाएं"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> की ओर से नया ध्‍वनिमेल"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"वॉइसमेल नहीं चलाया जा सका"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"वॉइसमेल लोड हो रहा है…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"वॉइसमेल संग्रहीत किया जा रहा है…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"वॉइसमेल लोड नहीं किया जा सका"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"केवल वॉयस मेल वाले कॉल"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"केवल इनकमिंग कॉल"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"केवल आउटगोइंग कॉल"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"केवल छूटे कॉल"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"विज़ुअल वॉइसमेल"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"किसी नंबर पर कॉल करने की आवश्‍यकता के बिना, अपना वॉइसमेल देखें और सुनें. डेटा शुल्‍क लागू हो सकते हैं."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"सेटिंग"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"वॉइसमेल अपडेट उपलब्‍ध नहीं हैं"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"नया वॉइसमेल प्रतीक्षा में है. इस समय लोड नहीं किया जा सकता."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"अपना वॉइसमेल सेट करें"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"ऑडियो उपलब्‍ध नहीं है"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"सेट करें"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"वॉयस मेल कॉल करें"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"नंबर चुनें"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"नंबर चुनें"</string>
- <string name="make_primary" msgid="5829291915305113983">"यह विकल्प याद रखें"</string>
- <string name="description_search_button" msgid="3660807558587384889">"खोजें"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"डायल करें"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"डायल करने के लिए नंबर"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"प्लेबैक चलाएं या बंद करें"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"स्पीकरफ़ोन को चालू या बंद पर स्विच करें"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"प्लेबैक स्थिति का पता लगाएं"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"प्लेबैक दर घटाएं"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"प्लेबैक दर बढ़ाएं"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"कॉल इतिहास"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"अधिक विकल्प"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"डायलपैड"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"केवल आउटगोइंग ही दिखाएं"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"केवल इनकमिंग ही दिखाएं"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"केवल छूटे हुए ही दिखाएं"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"केवल ध्‍वनि‍मेल दि‍खाएं"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"सभी कॉल दि‍खाएं"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2-सेकंड का विराम जोड़ें"</string>
- <string name="add_wait" msgid="3360818652790319634">"प्रतीक्षा का समय बढ़ाएं"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"सेटिंग"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"नया संपर्क"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"सभी संपर्क"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"कॉल विवरण"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"विवरण उपलब्‍ध नहीं है"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"टच टोन कीपैड का उपयोग करें"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"कॉल पर लौटना प्रगति पर है"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"कॉल जोड़ें"</string>
- <string name="type_incoming" msgid="6502076603836088532">"इनकमिंग कॉल"</string>
- <string name="type_outgoing" msgid="343108709599392641">"आउटगोइंग कॉल"</string>
- <string name="type_missed" msgid="2720502601640509542">"छूटी कॉल"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"इनकमिंग वीडियो कॉल"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"जावक वीडियो कॉल"</string>
- <string name="type_missed_video" msgid="954396897034220545">"छूटे वीडियो कॉल"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"वॉयस मेल"</string>
- <string name="type_rejected" msgid="7783201828312472691">"अस्वीकृत किया गया कॉल"</string>
- <string name="type_blocked" msgid="3521686227115330015">"अवरुद्ध किए गए कॉल"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"इनकमिंग कॉल"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"ध्‍वनिमेल चलाएं"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> संपर्क देखें"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g> को कॉल करें"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> का संपर्क विवरण"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> कॉल."</string>
- <string name="description_video_call" msgid="2933838090743214204">"वीडियो कॉल."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g> को SMS भेजें"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"नहीं सुना गया वॉयस मेल"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"बोलकर खोजें शुरु करें"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> पर कॉल करें"</string>
- <string name="unknown" msgid="740067747858270469">"अज्ञात"</string>
- <string name="voicemail" msgid="3851469869202611441">"वॉयस मेल"</string>
- <string name="private_num" msgid="6374339738119166953">"निजी नंबर"</string>
- <string name="payphone" msgid="7726415831153618726">"पे-फ़ोन"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> सेकंड"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> मि. <xliff:g id="SECONDS">%s</xliff:g> से."</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> को <xliff:g id="TIME">%2$s</xliff:g> बजे"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"इस नंबर पर कॉल नहीं किया जा सकता"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"ध्‍वनिमेल सेट करने के लिए, मेनू &gt; सेटिंग पर जाएं."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"ध्‍वनिमेल कॉल करने के लिए, पहले हवाई जहाज़ मोड बंद करें."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"लोड हो रही है..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"सिम कार्ड से लोड हो रहा है…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"सिम कार्ड के संपर्क"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"कोई भी संपर्क ऐप उपलब्‍ध नहीं है"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"बोलकर खोजना उपलब्‍ध नहीं है"</string>
- <string name="call_not_available" msgid="8941576511946492225">"फ़ोन कॉल नहीं किया जा सकता क्योंकि फ़ोन ऐप्लिकेशन अक्षम कर दिया गया है."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"उसके लिए इस डिवाइस पर कोई एेप नहीं है"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"संपर्क खोजें"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"नंबर जोड़ें या संपर्क खोजें"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"आपका कॉल इतिहास खाली है"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"कॉल करें"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"आपके पास कोई भी छूटा हुआ कॉल नहीं है."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"आपका वॉइसमेल इनबाॅक्‍स खाली है."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"आपका वॉइसमेल संग्रह खाली है."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"केवल पसंदीदा दिखाएं"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"कॉल इतिहास"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"वॉइसमेल संग्रह"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"सभी"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"छूटे हुए"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"वॉयस मेल"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"नया, सरल अवरोधन"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"आपकी बेहतर सुरक्षा के लिए, फ़ोन के अवरुद्ध करने के तरीके को बदलने की आवश्यकता है. आपके अवरोधित नंबर अब कॉल और लेख संदेश दोनों को रोक देंगे तथा उन्हें अन्य ऐप्लिकेशन के साथ साझा किया जा सकता है."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"अनुमति दें"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> को अवरुद्ध करें?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"इस नंबर से आने वाले कॉल अवरुद्ध कर दिए जाएंगे और वॉइसमेल अपने आप हटा दिए जाएंगे."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"इस नंबर से आने वाले कॉल अवरुद्ध कर दिए जाएंगे, लेकिन कॉलर अब भी आपके लिए वॉइसमेल भेज सकेंगे."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"अब आपको इस नंबर से कॉल या लेख संदेश प्राप्त नहीं होंगे."</string>
- <string name="block_number_ok" msgid="770551992296781873">"अवरुद्ध करें"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> को अनवरोधित करें?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"अनवरोधित करें"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"स्पीड डायल"</string>
- <string name="tab_history" msgid="2563144697322434940">"कॉल इतिहास"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"संपर्क"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"वॉइसमेल"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"पसंदीदा से निकाल दिया गया"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"वापस लाएं"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> पर कॉल करें"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"नया संपर्क बनाएं"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"किसी संपर्क में जोड़ें"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS भेजें"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"वीडियो कॉल करें"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"नंबर अवरुद्ध करें"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> छूटे हुए नए कॉल"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"आपके स्‍पीड डायल पर अभी तक कोई भी नहीं है"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"कोई पसंदीदा जोड़ें"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"आपके पास अभी कोई भी संपर्क नहीं है"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"कोई संपर्क जोड़ें"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"सभी नंबर देखने के लिए चित्र को स्‍पर्श करें या पुन: क्रमित करने के लिए स्‍पर्श करके रखें"</string>
- <string name="remove_contact" msgid="1080555335283662961">"निकालें"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"वीडियो कॉल"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"संदेश भेजें"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"कॉल विवरण"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> को कॉल करें"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> से छूटा हुआ कॉल."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> से आए कॉल का उत्तर दिया गया."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> के अपठित वॉइसमेल."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> के वॉइसमेल."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> पर किया गया कॉल."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> पर"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> से"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> से"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> पर, <xliff:g id="NUMBER">%2$s</xliff:g> से"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> <xliff:g id="NUMBER">%2$s</xliff:g> से"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"कॉल करें"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> को कॉल करें"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> को वीडियो कॉल करें."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> की वॉयस मेल सुनें"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> का वॉइसमेल चलाएं"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> का वॉइसमेल रोकें"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> का वॉइसमेल हटाएं"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> नए वॉइसमेल</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> नए वॉइसमेल</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> के लिए संपर्क बनाएं"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> को मौजूदा संपर्क में जोड़ें"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> के कॉल विवरण"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"कॉल इतिहास से हटाएं"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"आज"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"कल"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"पुराना"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"कॉल की सूची"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"स्पीकर चालू करें."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"स्पीकर बंद करें."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"तेज़ी से चलाएं."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"धीरे चलाएं."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"प्लेबैक प्रारंभ करें या रोकें."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"प्रदर्शन विकल्प"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"ध्‍वनि और कंपन"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"सरल उपयोग"</string>
- <string name="ringtone_title" msgid="760362035635084653">"फ़ोन रिंगटोन"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"कॉल के लिए भी कंपन"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"डायलपैड टोन"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"डायलपैड टोन की अवधि"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"सामान्य"</item>
- <item msgid="6177579030803486015">"लंबी"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"झटपट उत्तर"</string>
- <string name="call_settings_label" msgid="313434211353070209">"कॉल"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"कॉल अवरुद्ध करें"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"कॉल अवरुद्ध करना अस्‍थायी रूप से बंद है"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"कॉल अवरुद्ध किए जाने को अक्षम कर दिया गया है क्योंकि पिछले 48 घंटों में आपने इस फ़ोन से आपातकालीन सेवाओं से संपर्क किया है. 48 घंटे की अवधि बीत जाने पर यह अपने आप फिर से सक्षम हो जाएगी."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"नंबर आयात करें"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"आपने पहले कुछ कॉलर को अपने आप अन्य ऐप्स के द्वारा वॉइसमेल भेजे जाने के लिए चिह्नित किया था."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"नंबर देखें"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"आयात करें"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"आयात करना विफल रहा"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"वॉइसमेल संग्रहीत नहीं कर सके."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"नंबर अनवरोधित करें"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"नंबर जोड़ें"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"इन नंबर से आने वाले कॉल अवरुद्ध कर दिए जाएंगे और वॉइसमेल अपने आप हटा दिए जाएंगे."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"इन नंबर से आने वाले कॉल अवरुद्ध कर दिए जाएंगे, लेकिन वे अब भी आपके लिए वॉइसमेल भेज सकेंगे."</string>
- <string name="block_list" msgid="7760188925338078011">"अवरोधित नंबर"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> अमान्य है."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> पहले से अवरोधित है."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"कॉल अवरुद्ध करना 48 घंटे के लिए अक्षम कर दिया गया"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"आपातकालीन कॉल किए जाने के कारण अक्षम कर दिया गया."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"कॉलिंग खाते"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"चालू करें"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"अनुमतियां सेट करें"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"स्पीड डायल सक्षम करने के लिए, संपर्क अनुमति चालू करें."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"अपना कॉल लॉग देखने के लिए, फ़ोन अनुमति चालू करें."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"अपने संपर्क देखने के लिए, संपर्क अनुमति चालू करें."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"अपना वॉइसमेल ऐक्सेस करने के लिए, फ़ोन अनुमति चालू करें."</string>
- <string name="permission_no_search" msgid="84152933267902056">"अपने संपर्कों की खोज करने के लिए, संपर्क अनुमतियों को चालू करें."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"कॉल करने के लिए, फ़ोन अनुमति चालू करें."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"फ़ोन ऐप को सिस्टम सेटिंग में लिखने की अनुमति नहीं है."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"अवरोधित"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> सक्रिय है"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"अवरुद्ध करें/स्पैम की रिपोर्ट करें"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"अवरुद्ध करें"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"स्पैम नहीं है"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"अनवरोधित करें"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"स्पैम"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> को अवरुद्ध करें?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"इस नंबर से आने वाले भविष्य के कॉल और वॉइसमेल अवरुद्ध कर दिए जाएंगे."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"कॉल को स्पैम रिपोर्ट करें"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"इस नंबर से आने वाले भविष्य के कॉल व वॉइसमेल रोक दिए जाएंगे. कॉल स्पैम के रूप में रिपोर्ट किया जाएगा."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> को अनवरोधित करें?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"नंबर अनवरोधित हो जाएगा व स्पैम रिपोर्ट नहीं किया जाएगा. आगे के कॉल व वॉइसमेल स्पैम नहीं माने जाएंगे."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> को श्वेतसूची में डालें?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"श्वेतसूची"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"भविष्य में इस नंबर के कॉल व वॉइसमेल स्पैम नहीं माने जाएंगे. यह नंबर स्पैम रिपोर्ट नहीं किया जाएगा."</string>
-</resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
deleted file mode 100644
index de6eb33ce..000000000
--- a/res/values-hr/strings.xml
+++ /dev/null
@@ -1,290 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Numerička tipkovnica telefona"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Povijest poziva"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Prijavi netočan broj"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopiraj broj"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopiraj prijepis"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blokiraj broj"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> blokiran"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Deblokiraj broj"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> deblokiran"</string>
- <string name="block_number_undo" msgid="591338370336724156">"PONIŠTI"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Izbriši"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Uredi broj prije pozivanja"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Izbriši povijest poziva"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Izbriši govornu poštu"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arhiviranje govorne pošte"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Dijeljenje govorne pošte"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Gov. pošta izbrisana"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Govorna je pošta arhivirana"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"PONIŠTI"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"IDI NA ARHIVIRANO"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Želite li izbrisati povijest poziva?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Izbrisat će se svi pozivi iz vaše povijesti"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Brisanje povijesti poziva…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Propušteni poziv"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Propušten poslovni poziv"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Propušteni pozivi"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"Propušteni pozivi (<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>)"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Uzvrati poziv"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Poruka"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> poruka govorne pošte </item>
- <item quantity="few"> <xliff:g id="COUNT">%1$d</xliff:g> poruke govorne pošte </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> poruka govorne pošte </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Reproduciraj"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nova govorna pošta od kontakta <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Nije moguće reproducirati govornu poštu"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Učitavanje govorne pošte…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Arhiviranje govorne pošte..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Govorna pošta nije učitana"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Samo pozivi s govornom poštom"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Samo dolazni pozivi"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Samo odlazni pozivi"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Samo propušteni pozivi"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Vizualna govorna pošta"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Pogledajte i poslušajte govornu poštu bez pozivanja broja. Možda se naplaćuje podatkovna usluga."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Postavke"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Ažuriranja govorne pošte nisu dostupna"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Nova govorna pošta na čekanju. Učitavanje zasad nije moguće."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Postavite govornu poštu"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Zvuk nije dostupan"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Postavljanje"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Zovi govornu poštu"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Odaberite broj"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Odaberite broj"</string>
- <string name="make_primary" msgid="5829291915305113983">"Zapamti ovaj izbor"</string>
- <string name="description_search_button" msgid="3660807558587384889">"pretraži"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"biraj"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"broj za pozivanje"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Pokretanje ili zaustavljanje reprodukcije"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Uključivanje ili isključivanje zvučnika"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Traženje položaja reprodukcije"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Smanjenje brzine reprodukcije"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Povećanje brzine reprodukcije"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Povijest poziva"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Više opcija"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"površina za biranje brojeva"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Prikaži samo odlazne"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Prikaži samo dolazne"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Prikaži samo propuštene"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Prikaži samo govorne pošte"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Prikaz svih poziva"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Dodaj pauzu od 2 s."</string>
- <string name="add_wait" msgid="3360818652790319634">"Dodaj čekanje"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Postavke"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Novi kontakt"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Svi kontakti"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Pojedinosti poziva"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Pojedinosti nisu dostupne"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Koristi dodirnu zvučnu tipkovnicu"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Natrag na poziv u tijeku"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Dodaj poziv"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Dolazni poziv"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Odlazni poziv"</string>
- <string name="type_missed" msgid="2720502601640509542">"Propušteni poziv"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Dolazni videopoziv"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Odlazni videopoziv"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Propušten videopoziv"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Govorna pošta"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Odbijeni poziv"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blokirani poziv"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Dolazni pozivi"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Reprodukcija govorne pošte"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Prikaz kontakta <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Pozovi: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Pojedinosti o kontaktu <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"Broj poziva: <xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videopoziv."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Slanje SMS-a kontaktu <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Nepreslušana govorna pošta"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Pokretanje glasovnog pretraživanja"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Nazovite <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Nepoznato"</string>
- <string name="voicemail" msgid="3851469869202611441">"Govorna pošta"</string>
- <string name="private_num" msgid="6374339738119166953">"Privatni broj"</string>
- <string name="payphone" msgid="7726415831153618726">"Javna telefonska govornica"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> u <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>.<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Nije moguće nazvati taj broj"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Da biste postavili govornu poštu, idite na Izbornik &gt; Postavke."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Da biste nazvali govornu poštu, najprije isključite način rada u zrakoplovu."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Učitavanje..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Učitavanje sa SIM kartice..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Kontakti SIM kartice"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Nije dostupna nijedna aplikacija za kontakte"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Glasovno pretraživanje nije dostupno"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Telefoniranje nije moguće jer je aplikacija Telefon onemogućena."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Na ovom uređaju nema aplikacije za to"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Pretražite kontakte"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Dodaj broj ili potraži kontakt"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Vaša je povijest poziva prazna"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Uputite poziv"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Nemate propuštene pozive."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Nemate pristiglih poruka govorne pošte."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Arhiv govorne pošte je prazan."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Prikaži samo favorite"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Povijest poziva"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arhivirana govorna pošta"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Sve"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Propušteni"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Gov. pošta"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Novo, jednostavnije blokiranje"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Da bi vas bolje zaštitio, Phone mora promijeniti način funkcioniranja blokiranja. S blokiranih brojeva više nećete primati pozive ni SMS poruke. Osim toga, blokirani brojevi moći će se dijeliti s drugim aplikacijama."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Omogući"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Želite li blokirati <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Pozivi s tog broja blokirat će se, a govorna pošta automatski će se brisati."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Pozivi s tog broja blokirat će se, ali pozivatelj će vam moći ostavljati govornu poštu."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Više nećete primati pozive ni poruke s tog broja."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOKIRAJ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Želite li deblokirati <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DEBLOKIRAJ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Brzo biranje"</string>
- <string name="tab_history" msgid="2563144697322434940">"Povijest poziva"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontakti"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Govorna pošta"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Uklonjeno iz favorita"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Poništi"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Nazovi <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Izrada novog kontakta"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Dodaj kontaktu"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Pošalji SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Uputite videopoziv"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blokiraj broj"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"Broj novih propuštenih poziva: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Još nemate nikog na brzom biranju"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Dodaj omiljeni kontakt"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Još nemate nijedan kontakt"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Dodajte kontakt"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Dodirnite sliku da biste vidjeli sve brojeve ili dodirnite i zadržite da biste promijenili raspored"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Ukloni"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videopoziv"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Pošalji poruku"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Pojedinosti poziva"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Nazovi <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Propušten poziv: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Prihvaćen poziv: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Nepročitana govorna pošta kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Govorna pošta kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Poziv: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"na <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"putem <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"putem <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"na računu <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, putem broja <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> putem <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Poziv"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Pozovi <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Videopoziv <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Slušanje govorne pošte kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Reproduciranje govorne pošte od: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pauziranje govorne pošte od: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Brisanje govorne pošte od: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> nova govorna pošta</item>
- <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> nove govorne pošte</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> novih govornih pošta</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Izrada kontakta za <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Dodavanje kontakta <xliff:g id="NAMEORNUMBER">^1</xliff:g> postojećem kontaktu"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Pojedinosti o pozivu za kontakt <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Izbrisano iz povijesti poziva"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Danas"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Jučer"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Stariji"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Popis poziva"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Uključivanje zvučnika."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Isključivanje zvučnika."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Brža reprodukcija."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Sporija reprodukcija."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Pokretanje ili pauziranje reprodukcije."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Opcije prikaza"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Zvukovi i vibracije"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Pristupačnost"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Melodija zvona telefona"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibracija i za pozive"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Ton pri biranju brojeva"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Duljina zvuka tipkovnice"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Uobičajena"</item>
- <item msgid="6177579030803486015">"Duga"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Brzi odgovori"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Pozivi"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Blokiranje poziva"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Blokiranje poziva privremeno isključeno"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Blokiranje poziva onemogućeno je jer ste kontaktirali hitne službe s ovog telefona u posljednjih 48 sati. Automatski će se ponovo omogućiti kada prođe 48 sati."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Uvoz brojeva"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Ranije ste na drugim aplikacijama naveli da se neki pozivatelji automatski šalju na govornu poštu."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Prikaži brojeve"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Uvezi"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Uvoz nije uspio"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Arhiviranje govorne pošte nije uspjelo."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Deblokiranje broja"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Dodaj broj"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Pozivi s tih brojeva blokirat će se, a govorna pošta automatski će se brisati."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Pozivi s tih brojeva blokirat će se, ali pozivatelji će vam moći ostavljati govornu poštu."</string>
- <string name="block_list" msgid="7760188925338078011">"Blokirani brojevi"</string>
- <string name="invalidNumber" msgid="619058581062192851">"Broj <xliff:g id="NUMBER">%1$s</xliff:g> nije važeći."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"Broj <xliff:g id="NUMBER">%1$s</xliff:g> već je blokiran."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Blokiranje poziva onemogućeno je na 48 sati"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Onemogućeno jer je upućen hitni poziv."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Pozivanje računa"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Uključi"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Postavi dopuštenja"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Da biste omogućili brzo biranje, uključite dopuštenje za kontakte."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Da biste vidjeli zapisnik poziva, uključite dopuštenje za telefon."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Da biste vidjeli svoje kontakte, uključite dopuštenje za kontakte."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Da biste pristupili govornoj pošti, uključite dopuštenje za telefon."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Da biste pretraživali kontakte, uključite dopuštenja za kontakte."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Da biste nazvali nekog, uključite dopuštenje za telefon."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Aplikacija Telefon nema dopuštenje za pisanje u postavke sustava."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Blokirano"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"Aktivan/a: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blokiraj/prijavi neželjen broj"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blokiraj"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Nije neželjeni broj"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Deblokiraj"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Neželjeni broj"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Želite li blokirati <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Budući pozivi i govorna pošta s tog broja blokirat će se."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Prijavite poziv kao neželjeni"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Budući pozivi i govorna pošta s tog broja blokirat će se. Taj će se poziv prijaviti kao neželjeni."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Želite li deblokirati <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Taj će se broj deblokirati i neće biti prijavljen kao neželjeni. Budući pozivi i govorna pošta neće se označiti kao neželjeni."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Želite li staviti <xliff:g id="NUMBER">%1$s</xliff:g> na popis dopuštenih?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Stavi na popis dopuštenih"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Budući pozivi i govorna pošta s tog broja neće se označiti kao neželjeni sadržaj. Taj se broj neće prijaviti kao neželjeni."</string>
-</resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
deleted file mode 100644
index 6349eb663..000000000
--- a/res/values-hu/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Telefon tárcsázója"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Híváslista"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Pontatlan szám jelentése"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Szám másolása"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Átírás másolása"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Szám tiltása"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"A következő szám letiltva: <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Szám tiltásának feloldása"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"A következő szám tiltása feloldva: <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="block_number_undo" msgid="591338370336724156">"VISSZAVONÁS"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Törlés"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Szám szerkesztése hívás előtt"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"A híváslista törlése"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Hangposta törlése"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Hangpostaüzenet archiválása"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Hangposta megosztása"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Hangposta törölve"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Hangpostaüzenet archiválva"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"VISSZAVONÁS"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ARCHÍVUM"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Törli a híváslistát?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Ezzel törli az összes hívást az előzmények közül"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Híváslista törlése…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefonhívás"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Elmulasztott hívás"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Nem fogadott munkahelyi hívás"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Elmulasztott hívások"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> elmulasztott hívás"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Visszahívás"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Üzenet"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> hangpostaüzenet </item>
- <item quantity="one">Hangpostaüzenet</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Lejátszás"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Új hangüzenet tőle: <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Nem sikerült lejátszani a hangpostát"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Hangposta betöltése…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Hangpostaüzenet archiválása…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Nem sikerült betölteni a hangpostát"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Csak hangpostahívások"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Csak bejövő hívások"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Csak kimenő hívások"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Csak nem fogadott hívások"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Vizuális hangpostaüzenet"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Telefonálás nélkül tekintheti meg és hallgathatja vissza hangpostafiókja tartalmát. A művelet adatforgalmi díjakkal járhat."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Beállítások"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Nem érhetők el hangpostafrissítések"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Új hangpostaüzenet várakozik; jelenleg nem lehet betölteni."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Állítsa be hangpostáját"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Nem áll rendelkezésre hang"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Beállítás"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Hangposta hívása"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Válassza ki a számot"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Válassza ki a számot"</string>
- <string name="make_primary" msgid="5829291915305113983">"Választás megjegyzése"</string>
- <string name="description_search_button" msgid="3660807558587384889">"keresés"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"tárcsázás"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"a tárcsázandó szám"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Lejátszás indítása vagy leállítása"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Kihangosító be- vagy kikapcsolása"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Lejátszási pozíció módosítása"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Lejátszás sebességének csökkentése"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Lejátszás sebességének növelése"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Hívási előzmények"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"További beállítások"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"tárcsázó"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Csak a kimenők megjelenítése"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Csak a bejövők megjelenítése"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Csak a nem fogadottak"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Csak a hangüzenetek"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Összes hívás megjelenítése"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2 mp-es szünet hozzáadása"</string>
- <string name="add_wait" msgid="3360818652790319634">"Várakozás hozzáadása"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Beállítások"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Új névjegy"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Összes névjegy"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Hívás adatai"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Nem érhetők el a részletek"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Hangkódos telefonbillentyűzet használata"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Vissza a folyamatban lévő híváshoz"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Hívás hozzáadása"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Bejövő hívás"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Kimenő hívás"</string>
- <string name="type_missed" msgid="2720502601640509542">"Nem fogadott hívás"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Bejövő videohívás"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Kimenő videohívás"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Nem fogadott videohívás"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Hangposta"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Elutasított hívás"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Letiltott hívás"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Bejövő hívások"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Hangposta lejátszása"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> névjegyének megtekintése"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g> hívása"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> részletes adatai."</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> hívás"</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videohívás."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"SMS küldése <xliff:g id="NAME">%1$s</xliff:g> részére"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Nem lejátszott hangüzenet"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Hangalapú keresés indítása"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Hívás: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Ismeretlen"</string>
- <string name="voicemail" msgid="3851469869202611441">"Hangposta"</string>
- <string name="private_num" msgid="6374339738119166953">"Magántelefonszám"</string>
- <string name="payphone" msgid="7726415831153618726">"Nyilvános telefon"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> másodperc"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> perc <xliff:g id="SECONDS">%s</xliff:g> másodperc"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Nem lehet felhívni ezt a számot"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"A hangposta beállításához válassza a Menü &gt; Beállítások pontot."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Hangposta hívásához kapcsolja ki a Repülőgép üzemmódot."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Betöltés..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Betöltés a SIM kártyáról..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Névjegyek a SIM kártyán"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Nincs elérhető névjegyek alkalmazás"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"A hangalapú keresés nem érhető el"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Nem lehet telefonhívást kezdeményezni, mert a Telefon alkalmazást letiltották."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Nincs megfelelő alkalmazás a művelethez ezen az eszközön"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Névjegyek keresése"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Adjon meg egy számot, vagy keressen a névjegyek között"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"A híváslista üres"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Hívásindítás"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Nincsenek nem fogadott hívások."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Hangpostafiókjában nincsenek beérkezett üzenetek."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"A hangpostaüzenet-archívum üres."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Csak kedvencek megjelenítése"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Híváslista"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Hangpostaüzenet-archívum"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Összes"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Nem fogadott"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Hangposta"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Új, egyszerűbb letiltás"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Az Ön fokozottabb védelme érdekében a telefonnak módosítania kell a tiltás működését. A letiltott számok most már a hívásokat és az SMS-eket is megakadályozzák, és megoszthatók más alkalmazásokkal is."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Engedélyezés"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Letiltja a következő számot: <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Az erről a számról érkező hívásokat a rendszer letiltja, továbbá automatikusan törli az onnan érkező hangpostaüzeneteket is."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Az erről a számról érkező hívásokat letiltja a rendszer, azonban a hívó fél továbbra is hagyhat hangpostaüzeneteket."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"A továbbiakban nem fogad hívásokat vagy SMS-eket erről a számról."</string>
- <string name="block_number_ok" msgid="770551992296781873">"LETILTÁS"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Feloldja a következő szám tiltását: <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"TILTÁS FELOLDÁSA"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Gyorshívó"</string>
- <string name="tab_history" msgid="2563144697322434940">"Híváslista"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Címtár"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Hangposta"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Eltávolítva a kedvencek közül"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Visszavonás"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Hívás: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Új névjegy létrehozása"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Hozzáadás névjegyhez"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS küldése"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Videohívás kezdeményezése"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Szám tiltása"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> új nem fogadott hívás"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Még semelyik telefonszám sincs gyorshívón"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Kedvenc hozzáadása"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Még nem rendelkezik egyetlen névjeggyel sem"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Névjegy hozzáadása"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Érintse meg a képet az összes szám megjelenítéséhez, vagy tartsa nyomva az átrendezéshez"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Eltávolítás"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videohívás"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Üzenet küldése"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Hívás adatai"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> hívása"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Elmulasztott hívás: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Fogadott hívás: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Olvasatlan hangpostaüzenet a következőtől: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Hangpostaüzenet a következőtől: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Kimenő hívás: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"szám: <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"szám: <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"fiók: <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, szám: <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, szám: <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Telefonhívás"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> hívása"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"A következő hívása videokapcsolattal: <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> hangpostaüzenetének meghallgatása"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Hangposta lejátszása a következőnél: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Hangposta szüneteltetése a következőnél: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Hangposta törlése a következőnél: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> új hangpostaüzenet</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> új hangpostaüzenet</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Névjegy létrehozása a következőhöz: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"A(z) <xliff:g id="NAMEORNUMBER">^1</xliff:g> hozzáadása meglévő névjegyhez"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> – hívásrészletek"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Törölve a híváslistáról"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Ma"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Tegnap"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Korábbi"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Híváslista"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Hangszóró bekapcsolása."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Hangszóró kikapcsolása."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Gyorsabb lejátszás."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Lassabb lejátszás."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Lejátszás indítása vagy szüneteltetése."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Megjelenítési beállítások"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Hangok és rezgés"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Kisegítő lehetőségek"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Telefon csengőhangja"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"A hívásoknál rezegjen is"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tárcsázó hangjai"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Tárcsázási hang hossza"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normál"</item>
- <item msgid="6177579030803486015">"Hosszú"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Gyors válaszok"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Hívások"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Hívásletiltás"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Hívástiltás átmenetileg felfüggesztve"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Felfüggesztettük a hívások tiltását, mert az elmúlt 48 órában tárcsázta a segélyhívót erről a telefonról. A funkciót automatikusan újból engedélyezzük 48 óra elteltével."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Számok importálása"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Más alkalmazásokban korábban beállította, hogy bizonyos személyeket automatikusan a hangpostafiókba irányítsa a rendszer, amikor felhívják Önt."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Számok megtekintése"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importálás"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Az importálás nem sikerült"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Sikertelen hangpostaüzenet-archiválás."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Szám tiltásának feloldása"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Szám hozzáadása"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Az ezekről a számokról érkező hívásokat a rendszer letiltja, továbbá automatikusan törli az onnan érkező hangpostaüzeneteket is."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Az ezekről a számokról érkező hívásokat letiltja a rendszer, azonban a hívó felek továbbra is hagyhatnak hangpostaüzeneteket."</string>
- <string name="block_list" msgid="7760188925338078011">"Letiltott számok"</string>
- <string name="invalidNumber" msgid="619058581062192851">"A(z) <xliff:g id="NUMBER">%1$s</xliff:g> érvénytelen."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"A következő szám már le van tiltva: <xliff:g id="NUMBER">%1$s</xliff:g>."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"A hívások tiltása funkció kikapcsolva 48 órára"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Letiltva vészhívás miatt."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Telefonos fiókok"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Engedélyezés"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Engedélyek beállítása"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Gyorshívás engedélyezéséhez kapcsolja be a Névjegyek engedélyt."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"A hívásnapló megtekintéséhez kapcsolja be a Telefon engedélyt."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"A névjegyek megtekintéséhez kapcsolja be a Névjegyek engedélyt."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"A hangposta eléréséhez kapcsolja be a Telefon engedélyt."</string>
- <string name="permission_no_search" msgid="84152933267902056">"A névjegyek kereséséhez adja meg a Névjegyek engedélyeket."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Hívásindításhoz kapcsolja be a Telefon engedélyt."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"A Telefon alkalmazásnak nincs engedélye szerkeszteni a rendszerbeállításokat."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Letiltva"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> aktív"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Letiltás/spam bejelentése"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Letiltás"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Nem spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Tiltás feloldása"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Letiltja a következő számot: <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"A jövőben az erről a számról érkező hívások és hangpostaüzenetek le lesznek tiltva."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Hívás bejelentése spamként"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"A számról érkező hívások és hangpostaüzenetek le lesznek tiltva. A hívás spamként lesz bejelentve."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Feloldja a következő szám tiltását: <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"A rendszer feloldja a szám tiltását, és nem spamként jelenti be. A hívások és üzenetek nem spamek."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Szeretné az engedélyezőlistára helyezni a következő telefonszámot: <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Engedélyezőlista"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"A számról érkező hívások és üzenetek nem spamek. A szám „nem spam” típusúként lesz bejelentve."</string>
-</resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
deleted file mode 100644
index 473f58f1d..000000000
--- a/res/values-hy/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Հեռախոս"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Հեռախոս"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Հեռախոսի թվաշար"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Հեռախոս"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Զանգերի պատմությունը"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Հաղորդել սխալ համարի մասին"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Պատճենել համարը"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Պատճենել տառադարձությունը"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Արգելափակել համարը"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Արգելափակվել է <xliff:g id="NUMBER">%1$s</xliff:g> համար"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Արգելաբացել համարը"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Արգելաբացվել է <xliff:g id="NUMBER">%1$s</xliff:g> համար"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ՀԵՏԱՐԿԵԼ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Ջնջել"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Փոփոխել համարը զանգելուց առաջ"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Ջնջել զանգերի պատմությունը"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Ջնջել ձայնային փոստը"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Արխիվացնել ձայնային փոստը"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Ուղարկել ձայնային փոստը"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Ձայնային փոստը ջնջվեց"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Ձայնային փոստն արխիվացվեց"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ՀԵՏԱՐԿԵԼ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ԱՆՑՆԵԼ ԱՐԽԻՎ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Մաքրե՞լ զանգերի պատմությունը:"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Այս գործողությունը ամբողջովին կջնջի զանգերի պատմությունը"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Զանգերի պատմության մաքրում…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Հեռախոս"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Բաց թողնված զանգ"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Բաց թողնված աշխատանքային զանգ"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Բաց թողնված զանգեր"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> բաց թողնված զանգ"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Հետ զանգել"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Հաղորդագրություն"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> ձայնային փոստ </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> ձայնային փոստ </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Նվագարկել"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Նոր ձայնային փոստ <xliff:g id="CALLER">%1$s</xliff:g>-ից"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Հաղորդագրությունը չհաջողվեց նվագարկել"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Ձայնային հաղորդագրության բեռնում…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Ձայնային փոստի արխիվացում…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Հաղորդագրությունը չհաջողվեց բեռնել"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Միայն ձայնային փոստով զանգերը"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Միայն մուտքային զանգեր"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Միայն ելքային զանգերը"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Միայն բաց թողնված զանգերը"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Տեսողական ձայնային փոստ"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Դիտեք և ունկնդրեք ձեր ձայնային փոստի հաղորդագրությունները առանց զանգ կատարելու անհրաժեշտության: Տվյալների փոխանցման համար կարող են վճարներ գանձվել:"</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Կարգավորումներ"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Ձայնային փոստի տվյալները չի հաջողվում բեռնել"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Նոր ձայնային հաղորդագրություն կա: Այս պահին հնարավոր չէ բեռնել:"</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Կարգավորեք ձայնային փոստը"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Ձայնային տարբերակը հասանելի չէ"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Կարգավորել"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Միանալ ձայնային փոստին"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Ընտրել համարը"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Ընտրել համարը"</string>
- <string name="make_primary" msgid="5829291915305113983">"Հիշել այս ընտրությունը"</string>
- <string name="description_search_button" msgid="3660807558587384889">"որոնել"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"համարարկել"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"համարհավաքման հեռախոսահամարը"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Միացնել կամ անջատել նվագարկումը"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Միացնել կամ անջատել բարձրախոսը"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Փնտրել նվագարկման դիրքը"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Իջեցնել նվագարկման վարկանիշը"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Բարձրացնել նվագարկման վարկանիշը"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Զանգերի պատմությունը"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Այլ ընտրանքներ"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"թվաշար"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Ցույց տալ միայն ելքայինները"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Ցույց տալ միայն մուտքայինները"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Ցույց տալ միայն բաց թողնվածները"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Ցուցադրել միայն ձայնային փոստերը"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Ցուցադրել բոլոր զանգերը"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Ավելացնել 2 վայրկյան դադար"</string>
- <string name="add_wait" msgid="3360818652790319634">"Ավելացնել սպասում"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Կարգավորումներ"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Նոր կոնտակտ"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Բոլոր կոնտակտները"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Զանգի մանրամասները"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Մանրամասները հասանելի չեն"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Օգտագործել հնչերանգներով ստեղնաշարը"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Վերադառնալ ընթացիկ զանգին"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Ավելացնել զանգ"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Մուտքային զանգ"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Ելքային զանգ"</string>
- <string name="type_missed" msgid="2720502601640509542">"Բաց թողնված զանգ"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Մուտքային տեսազանգ"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Ելքային տեսազանգ"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Բաց թողնված տեսազանգ"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Ձայնային փոստ"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Մերժված զանգ"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Արգելափակված զանգ"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Մուտքային զանգեր"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Նվագարկել ձայնային փոստը"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Դիտել <xliff:g id="NAME">%1$s</xliff:g> կոնտակտը"</string>
- <string name="description_call" msgid="3443678121983852666">"Զանգել <xliff:g id="NAME">%1$s</xliff:g>-ին"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g>-ի կոնտակտային տվյալները"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> զանգ:"</string>
- <string name="description_video_call" msgid="2933838090743214204">"Տեսազանգ"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Ուղարկել SMS <xliff:g id="NAME">%1$s</xliff:g>-ին"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Չլսված ձայնային փոստ"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Սկսկեք ձայնային որոնումը"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Զանգել <xliff:g id="NUMBER">%s</xliff:g>-ին"</string>
- <string name="unknown" msgid="740067747858270469">"Անհայտ"</string>
- <string name="voicemail" msgid="3851469869202611441">"Ձայնային փոստ"</string>
- <string name="private_num" msgid="6374339738119166953">"Փակ համար"</string>
- <string name="payphone" msgid="7726415831153618726">"Հանրային հեռախոս"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> վրկ"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> րոպե <xliff:g id="SECONDS">%s</xliff:g> վայրկյան"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g>-ին, ժամը <xliff:g id="TIME">%2$s</xliff:g>-ին"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Հնարավոր չէ զանգահարել այս համարով"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Ձայնային փոստը կարգավորելու համար գնացեք Ցանկ &gt; Կարգավորումներ:"</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Ձայնային փոստին զանգելու համար նախ անջատեք թռիչքային ռեժիմը:"</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Բեռնում..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"SIM քարտը բեռնվում է..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM քարտի կոնտակտները"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Կոնտակտների հավելված չկա"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Ձայնային որոնումը հասանելի չէ"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Հնարավոր չէ զանգահարել, քանի որ Հեռախոս հավելվածն անջատված է:"</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Սարքի վրա համապատասխան հավելված չկա"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Կոնտակտների որոնում"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Ավելացրեք համար/որոնեք կոնտակտներ"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Զանգերի մատյանը դատարկ է"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Զանգել"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Բաց թողնված զանգեր չունեք:"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Ձայնային փոստի մուտքի արկղը դատարկ է:"</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Ձեր ձայնային փոստի արխիվը դատարկ է:"</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Ցույց տալ միայն ընտրյալները"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Զանգերի պատմությունը"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Ձայնային փոստի արխիվ"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Բոլորը"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Բաց թողնված"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Ձայնափոստ"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Նոր, պարզեցված արգելափակում"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Ձեզ ավելի լավ պաշտպանելու նպատակով Հեռախոսին անհրաժեշտ է փոխել արգելափակման կարգավորումները: Արգելափակված համարներից զանգերի և տեքստային հաղորդագրությունների ստացումը կկասեցվի, իսկ այդ համարները կարող են տրամադրվել այլ հավելվածներին:"</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Թույլատրել"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Արգելափակե՞լ <xliff:g id="NUMBER">%1$s</xliff:g> համարը:"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Այս համարից զանգերը կարգելափակվեն, իսկ ձայնային փոստի հաղորդագրություններն ավտոմատ կերպով կջնջվեն:"</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Այս համարից զանգերը կարգելափակվեն, սակայն զանգողը կկարողանա ձեզ համար ձայնային փոստի հաղորդագրություններ թողնել:"</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Այս համարից զանգեր և SMS-ներ այլևս չեք ստանա:"</string>
- <string name="block_number_ok" msgid="770551992296781873">"ԱՐԳԵԼԱՓԱԿԵԼ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Արգելաբացե՞լ <xliff:g id="NUMBER">%1$s</xliff:g> համարը:"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ԱՐԳԵԼԱԲԱՑԵԼ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Արագ համարարկում"</string>
- <string name="tab_history" msgid="2563144697322434940">"Զանգերի պատմությունը"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Կոնտակտներ"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Ձայնային փոստ"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Հեռացված է ընտրյալներից"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Հետարկել"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Զանգել <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Ստեղծել նոր կոնտակտ"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Ավելացնել կոնտակտին"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Ուղարկել SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Կատարել տեսազանգ"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Արգելափակել համարը"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> նոր բաց թողնված զանգ"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Արագ համարահավաքման ցանկը դատարկ է"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Ավելացնել կոնտակտ"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Դեռ կոնտակտներ չունեք"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Ավելացնել կոնտակտ"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Բոլոր համարները տեսնելու համար հպեք պատկերին: Վերադասավորելու համար հպեք և պահեք:"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Հեռացնել"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Տեսազանգ"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Ուղարկել հաղորդագրություն"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Զանգի մանրամասները"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Զանգել <xliff:g id="NAMEORNUMBER">^1</xliff:g>-ին"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Բաց է թողնվել զանգ <xliff:g id="NAMEORNUMBER">^1</xliff:g> կոնտակտից, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>:"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Ընդունվել է զանգ <xliff:g id="NAMEORNUMBER">^1</xliff:g> կոնտակտից, <xliff:g id="TYPEORLOCATION">^2</xliff:g> , <xliff:g id="TIMEOFCALL">^3</xliff:g> , <xliff:g id="PHONEACCOUNT">^4</xliff:g>:"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Չկարդացված ձայնային փոստի հաղորդագրություն <xliff:g id="NAMEORNUMBER">^1</xliff:g>-ից, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>:"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Ձայնային փոստի հաղորդագրություն <xliff:g id="NAMEORNUMBER">^1</xliff:g>-ից, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>:"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Զանգ <xliff:g id="NAMEORNUMBER">^1</xliff:g> կոնտակտին, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>:"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> համարի միջոցով"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> համարի միջոցով"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>-ին <xliff:g id="NUMBER">%2$s</xliff:g> համարի միջոցով"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>-ին <xliff:g id="NUMBER">%2$s</xliff:g> համարի միջոցով"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Զանգել"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Զանգել <xliff:g id="NAMEORNUMBER">^1</xliff:g>-ին"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Տեսազանգ <xliff:g id="NAMEORNUMBER">^1</xliff:g> կոնտակտին:"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Լսել ձայնային փոստը <xliff:g id="NAMEORNUMBER">^1</xliff:g>-ից"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Նվագարկել <xliff:g id="NAMEORNUMBER">^1</xliff:g>-ի ձայնային փոստի հաղորդագրությունները"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Դադարեցնել <xliff:g id="NAMEORNUMBER">^1</xliff:g>-ից ձայնային փոստի հաղորդագրությունների ստացումը"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Ջնջել <xliff:g id="NAMEORNUMBER">^1</xliff:g>-ի ձայնային փոստի հաղորդագրությունները"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> նոր ձայնային փոստ</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> նոր ձայնային փոստ</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Ավելացնել <xliff:g id="NAMEORNUMBER">^1</xliff:g> կոնտակտը"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Ավելացնել <xliff:g id="NAMEORNUMBER">^1</xliff:g>-ը առկա կոնտակտին"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> զանգի մասին տվյալներ"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Ջնջվել է զանգերի պատմությունից"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Այսօր"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Երեկ"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Ավելի հին"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Զանգերի ցանկ"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Միացնել բարձրախոսը:"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Անջատել բարձրախոսը:"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Նվագարկել ավելի արագ:"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Նվագարկել ավելի դանդաղ:"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Սկսել կամ դադարեցնել նվագարկումը:"</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Ցուցադրման ընտրանքներ"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Ձայներ և թրթռում"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Մատչելիություն"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Հեռախոսի զանգերանգ"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Նաև թրթռալ զանգերի ժամանակ"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Թվաշարի ձայներանգներ"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Թվաշարի ձայնային ազդանշանի երկարություն"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Սովորական"</item>
- <item msgid="6177579030803486015">"Երկար"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Արագ պատասխաններ"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Զանգեր"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Զանգերի արգելափակում"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Զանգերի արգելափակումը կասեցվել է"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Զանգերի արգելափակումը կասեցվել է, քանի որ վերջին 48 ժամվա ընթացքում դուք այս հեռախոսից զանգել եք արտակարգ իրավիճակների ծառայություններին: 48 ժամ տևողությամբ ժամանակահատվածի ավարտից հետո այն ավտոմատ կերպով կվերամիացվի:"</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Ներմուծել համարները"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Համաձայն ձեր նախկին կարգավորումների՝ որոշ զանգողների պետք է ինքնաշխատորեն ուղարկվի ձայնային փոստ այլ հավելվածների միջոցով:"</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Կոնտակտների թիվը"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Ներմուծում"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Ներմուծումը չհաջողվեց"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Չհաջողվեց արխիվացնել ձայնային փոստը:"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Արգելաբացել համարը"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Ավելացնել համար"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Այս համարներից զանգերը կարգելափակվեն, իսկ ձայնային փոստի հաղորդագրություններն ավտոմատ կերպով կջնջվեն:"</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Այս համարներից զանգերը կարգելափակվեն, սակայն նրանք կկարողանան ձեզ համար ձայնային փոստի հաղորդագրություններ թողնել:"</string>
- <string name="block_list" msgid="7760188925338078011">"Արգելափակված համարներ"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> համարը սխալ է:"</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> համարն արդեն արգելափակված է:"</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Զանգերի արգելափակումն անջատվել է 48 ժամով"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Անջատվել է, քանի որ շտապ կանչ է կատարվել։"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Հաշիվներ զանգերի համար"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Միացնել"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Թույլտվությունների սահմանում"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Արագ համարահավաքը թույլատրելու համար միացրեք Կոնտակտների թույլտվությունը:"</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Ձեր զանգերի մատյանը տեսնելու համար միացրեք Հեռախոսի թույլտվությունը:"</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Ձեր կոնտակտները տեսնելու համար միացրեք Կոնտակտների թույլտվությունը:"</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Ձայնային փոստն օգտագործելու համար միացրեք Հեռախոսի թույլտվությունը:"</string>
- <string name="permission_no_search" msgid="84152933267902056">"Կոնտակտները որոնելու համար միացրեք Կոնտակտների թույլտվությունները:"</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Զանգ կատարելու համար միացրեք Հեռախոսի թույլտվությունը:"</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Հեռախոս հավելվածը համակարգի կարգավորումները գրելու թույլտվություն չունի:"</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Արգելափակված"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-ն ակտիվ է"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Արգելափակել/Նշել որպես լցոն"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Արգելափակել"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Լցոն չէ"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Արգելաբացել"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Լցոն"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Արգելափակե՞լ <xliff:g id="NUMBER">%1$s</xliff:g> համարը:"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Այս համարից զանգերն ու ձայնային հաղորդագրությունները կարգելափակվեն:"</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Նշել զանգը որպես լցոն"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Այս համարից զանգերն ու ձայնային հաղորդագրությունները կարգելափակվեն: Այս զանգը կնշվի որպես լցոն:"</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Արգելաբացե՞լ <xliff:g id="NUMBER">%1$s</xliff:g> համարը:"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Այս համարը կարգելաբացվի և կնշվի որպես ոչ լցոն: Այս համարից զանգերն ու հաղորդագրությունները չեն նշվի որպես լցոն:"</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Գրանցե՞լ <xliff:g id="NUMBER">%1$s</xliff:g> համարը սպիտակ ցուցակում:"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Սպիտակ ցուցակ"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Այս համարից զանգերն ու ձայնային հաղորդագրությունները չեն նշվի որպես լցոն: Այս համարը կնշվի որպես ոչ լցոն:"</string>
-</resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
deleted file mode 100644
index 79e31ea7f..000000000
--- a/res/values-in/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telepon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telepon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Tombol Nomor Ponsel"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telepon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Riwayat panggilan"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Laporkan nomor yang tidak akurat"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Salin nomor"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Salin transkripsi"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blokir nomor"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> diblokir"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Bebaskan nomor"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> dibebaskan"</string>
- <string name="block_number_undo" msgid="591338370336724156">"URUNGKAN"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Hapus"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Edit nomor sebelum memanggil"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Hapus riwayat panggilan"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Hapus pesan suara"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arsipkan pesan suara"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Bagikan pesan suara"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Pesan suara dihapus"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Pesan suara diarsipkan"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"URUNGKAN"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"BUKA ARSIP"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Hapus riwayat panggilan?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Tindakan ini akan menghapus semua panggilan telepon dari riwayat"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Menghapus riwayat panggilan..."</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telepon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Panggilan tak terjawab"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Panggilan tak terjawab di telepon kerja"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Panggilan tak terjawab"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> panggilan tak terjawab"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Telepon balik"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Pesan"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Pesan suara </item>
- <item quantity="one">Pesan suara</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Putar"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Pesan suara baru dari <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Tidak dapat memutar pesan suara"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Memuat pesan suara..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Mengarsipkan pesan suara…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Tidak dapat memuat pesan suara"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Hanya panggilan dengan pesan suara"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Hanya panggilan masuk"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Hanya panggilan keluar"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Hanya panggilan tak terjawab"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Pesan suara visual"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Lihat dan dengarkan pesan suara, tanpa harus menelepon si pengirim. Dapat dikenakan tagihan data."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Setelan"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Pembaruan pesan suara tidak tersedia"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Pesan suara baru sedang menunggu. Tidak dapat memuat sekarang."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Siapkan pesan suara"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio tidak tersedia"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Siapkan"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Telepon pesan suara"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Pilih nomor"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Pilih nomor"</string>
- <string name="make_primary" msgid="5829291915305113983">"Ingat pilihan ini"</string>
- <string name="description_search_button" msgid="3660807558587384889">"telusuri"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"panggil"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"nomor untuk dipanggil"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Memutar atau menghentikan pemutaran"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Mengaktifkan/menonaktifkan pengeras suara ponsel"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Mencari posisi pemutaran"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Menurunkan laju pemutaran"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Meningkatkan laju pemutaran"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Riwayat Panggilan"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Opsi lainnya"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"tombol nomor"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Tampilkan panggilan keluar"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Tampilkan panggilan masuk saja"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Tampilkan panggilan terlewat"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Tampilkan pesan suara saja"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Tampilkan semua panggilan"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Tambahkan jeda 2 dtk"</string>
- <string name="add_wait" msgid="3360818652790319634">"Tambahkan tunggu"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Setelan"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Kontak baru"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Semua kontak"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Detail panggilan"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Detail tidak tersedia"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Gunakan keypad nada sentuh"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Kembali ke panggilan sedang berlangsung"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Tambahkan panggilan"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Panggilan masuk"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Panggilan keluar"</string>
- <string name="type_missed" msgid="2720502601640509542">"Panggilan tak terjawab"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Video call masuk"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Video call keluar"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Video call tak terjawab"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Kotak Pesan"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Panggilan ditolak"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Panggilan diblokir"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Panggilan masuk"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Putar pesan suara"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Lihat kontak <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Telepon <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Detail kontak untuk <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> panggilan."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Video call."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Kirim SMS ke <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Pesan suara yang belum didengar"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Memulai penelusuran suara"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Telepon <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Tidak diketahui"</string>
- <string name="voicemail" msgid="3851469869202611441">"Kotak Pesan"</string>
- <string name="private_num" msgid="6374339738119166953">"Nomor pribadi"</string>
- <string name="payphone" msgid="7726415831153618726">"Telepon Umum"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> dtk"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> mnt <xliff:g id="SECONDS">%s</xliff:g> dtk"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> pukul <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>.<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Tidak dapat menelepon nomor ini"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Untuk menyiapkan kotak pesan, buka Menu &gt; Setelan."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Untuk memanggil pesan suara, pertama-tama matikan mode Pesawat."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Memuat..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Memuat dari kartu SIM..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Kontak pada kartu SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Aplikasi kontak tidak tersedia"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Penelusuran suara tidak tersedia"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Tidak dapat melakukan panggilan telepon karena aplikasi Telepon telah dinonaktifkan."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Tidak ada aplikasi untuk tindakan tersebut di perangkat ini"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Telusuri kontak"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Tambahkan nomor atau telusuri kontak"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Riwayat panggilan kosong"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Lakukan panggilan telepon"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Tidak ada panggilan yang tidak terjawab."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Kotak masuk pesan suara kosong."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Arsip pesan suara Anda kosong."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Hanya tampilkan favorit"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Riwayat Panggilan"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arsip Pesan Suara"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Semua"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Tak Dijawab"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"PesanSuara"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Pemblokiran baru yang disederhanakan"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Untuk semakin melindungi Anda, Telepon harus mengubah cara kerja pemblokiran. Sekarang nomor yang diblokir akan menghentikan panggilan telepon dan SMS, dan mungkin dibagikan dengan aplikasi lain."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Izinkan"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Blokir <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Panggilan telepon dari nomor ini akan diblokir dan pesan suara akan dihapus secara otomatis."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Panggilan telepon dari nomor ini akan diblokir, tetapi penelepon mungkin masih dapat meninggalkan pesan suara."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Anda tidak akan menerima telepon atau SMS lagi dari nomor ini."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOKIR"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Bebaskan <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"BEBASKAN"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Panggilan cepat"</string>
- <string name="tab_history" msgid="2563144697322434940">"Riwayat Panggilan"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontak"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Pesan suara"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Dihapus dari favorit"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Batalkan"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Telepon <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Buat kontak baru"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Tambah ke kontak"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Kirim SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Lakukan video call"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blokir nomor"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> panggilan tidak terjawab baru"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Daftar panggilan cepat masih kosong"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Tambahkan favorit"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Anda belum memiliki kontak"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Tambahkan kontak"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Sentuh foto untuk melihat semua nomor atau sentuh &amp; tahan untuk menyusun ulang"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Hapus"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Panggilan video"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Kirim pesan"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Detail panggilan"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Telepon <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Panggilan tak terjawab dari <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Panggilan terjawab dari <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Pesan suara yang belum dibaca dari <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Pesan suara dari <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Panggilan ke <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"di <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"melalui <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"melalui <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"di <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, melalui <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> melalui <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Telepon"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Telepon <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Lakukan panggilan video ke <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Dengarkan kotak pesan dari <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Putar pesan suara dari <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Jeda pesan suara dari <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Hapus pesan suara dari <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> pesan suara baru</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> pesan suara baru</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Buat kontak untuk <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Tambahkan <xliff:g id="NAMEORNUMBER">^1</xliff:g> ke akun yang ada"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Detail panggilan telepon untuk <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Dihapus dari riwayat panggilan"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Hari ini"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Kemarin"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Lebih lama"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Daftar panggilan telepon"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Aktifkan pengeras suara."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Nonaktifkan pengeras suara."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Putar lebih cepat."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Putar lebih lambat."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Mulai atau jeda pemutaran."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Opsi tampilan"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Suara dan getaran"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Aksesibilitas"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Nada dering ponsel"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Getarkan juga untuk panggilan"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Nada tombol nomor"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Panjang nada tombol nomor"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"Panjang"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Respons cepat"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Panggilan telepon"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Pemblokiran panggilan telepon"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Blokir panggilan dinonaktifkan sementara"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Pemblokiran panggilan telepon telah dinonaktifkan karena Anda menghubungi layanan darurat dari telepon ini dalam 48 jam terakhir. Akan diaktifkan kembali secara otomatis setelah masa 48 jam berakhir."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Impor nomor"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Anda sebelumnya menandai beberapa penelepon agar dialihkan secara otomatis ke pesan suara melalui aplikasi lain."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Lihat Nomor"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Impor"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Impor gagal"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Gagal mengarsipkan pesan suara."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Bebaskan nomor"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Tambahkan nomor"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Panggilan telepon dari nomor ini akan diblokir dan pesan suara akan dihapus secara otomatis."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Panggilan telepon dari nomor ini akan diblokir, tetapi penelepon mungkin masih dapat meninggalkan pesan suara."</string>
- <string name="block_list" msgid="7760188925338078011">"Nomor yang diblokir"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> tidak valid."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> sudah diblokir."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Pemblokiran panggilan dinonaktifkan selama 48 jam"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Dinonaktifkan karena panggilan darurat dilakukan."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Akun panggilan"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Aktifkan"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Setel izin"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Untuk mengaktifkan panggilan cepat, aktifkan izin Kontak."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Untuk melihat log panggilan, aktifkan izin Telepon."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Untuk melihat kontak, aktifkan izin Kontak."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Untuk mengakses pesan suara, aktifkan izin Telepon."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Untuk menelusuri kontak, aktifkan izin Kontak."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Untuk melakukan panggilan, aktifkan izin Telepon."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Aplikasi telepon tidak memiliki izin untuk menulis ke setelan sistem."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Diblokir"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> aktif"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blokir/laporkan spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blokir"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Bukan spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Bebaskan"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Blokir <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Telepon dan pesan suara mendatang dari nomor ini akan diblokir."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Laporkan telepon sebagai spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Telepon berikutnya dari nomor ini akan diblokir dan dilaporkan sebagai spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Bebaskan <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Nomor dibebaskan &amp; dilaporkan bukan spam. Telepon &amp; pesan suara dari nomor ini dianggap bukan spam."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Tambahkan <xliff:g id="NUMBER">%1$s</xliff:g> ke daftar putih?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Daftar putih"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Telepon dan pesan suara berikut dari nomor ini tidak akan dianggap &amp; tidak dilaporkan sebagai spam."</string>
-</resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
deleted file mode 100644
index 3d9251a9e..000000000
--- a/res/values-is/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Sími"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Sími"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Takkaborð síma"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Sími"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Símtalaferill"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Tilkynna rangt númer"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Afrita númer"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Afrita umritun"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Setja númer á bannlista"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Lokað fyrir <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Taka númer af bannlista"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> tekið af bannlista"</string>
- <string name="block_number_undo" msgid="591338370336724156">"AFTURKALLA"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Eyða"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Breyta númeri áður en hringt er"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Hreinsa símtalaferil"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Eyða talhólfsskilaboðum"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Setja talhólfsskilaboð í geymslu"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Deila talhólfsskilaboðum"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Talhólfi eytt"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Talhólfsskilaboð sett í geymslu"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"AFTURKALLA"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"FARA Í GEYMSLU"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Hreinsa símtalaferil?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Þetta eyðir öllum símtölum af ferlinum"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Hreinsar símtalaferil…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Sími"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Ósvarað símtal"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Ósvarað vinnusímtal"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Ósvöruð símtöl"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ósvöruð símtöl"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Hringja til baka"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Skilaboð"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> talhólfsskilaboð </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> talhólfsskilaboð </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Spila"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Ný talhólfsskilaboð frá <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Ekki tókst að spila talhólfsskilaboð"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Hleður talhólfsskilaboð…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Setur talhólfsskilaboð í geymslu..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Ekki tókst að hlaða talhólfsskilaboð"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Símtöl með talhólfi eingöngu"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Móttekin símtöl eingöngu"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Hringd símtöl eingöngu"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Ósvöruð símtöl eingöngu"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Myndrænt talhólf"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Skoðaðu og hlustaðu á talhólfið þitt án þess að þurfa að hringja. Getur haft gagnakostnað í för með sér."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Stillingar"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Talhólfsuppfærslur ekki í boði"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Ný talhólfsskilaboð bíða. Ekki er hægt að hlaða þau eins og er."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Settu upp talhólfið þitt"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Hljóð er ekki í boði"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Uppsetning"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Hringja í talhólf"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Veldu símanúmer"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Veldu símanúmer"</string>
- <string name="make_primary" msgid="5829291915305113983">"Muna þetta val"</string>
- <string name="description_search_button" msgid="3660807558587384889">"leita"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"hringja"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"númer til að hringja í"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Hefja eða stöðva spilun"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Kveikja eða slökkva á hátalara"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Breyta spilunarstöðu"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Draga úr spilunarhraða"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Auka spilunarhraða"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Símtalaferill"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Fleiri valkostir"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"takkaborð"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Sýna hringd símtöl eingöngu"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Sýnir móttekin símtöl eingöngu"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Sýna ósvöruð símtöl eingöngu"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Sýna talhólfsskilaboð eingöngu"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Sýna öll símtöl"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Bæta við 2 sekúndna töf"</string>
- <string name="add_wait" msgid="3360818652790319634">"Bæta töf við"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Stillingar"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Nýr tengiliður"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Allir tengiliðir"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Símtalsupplýsingar"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Upplýsingar ekki í boði"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Nota snertitónatakkaborð"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Fara aftur í símtal í gangi"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Bæta við símtali"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Móttekið símtal"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Hringt símtal"</string>
- <string name="type_missed" msgid="2720502601640509542">"Ósvarað símtal"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Myndsímtal berst"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Myndsímtal hringt"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Ósvarað myndsímtal"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Talhólfsskilaboð"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Símtali hafnað"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Símtal á bannlista"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Móttekin símtöl"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Spila talhólfsskilaboð"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Skoða tengiliðinn <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Hringja í <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Tengiliðaupplýsingar um <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> símtöl."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Myndsímtal"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Senda SMS til <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Óspiluð talhólfsskilaboð"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Hefja raddleit"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Hringja í <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Óþekkt"</string>
- <string name="voicemail" msgid="3851469869202611441">"Talhólf"</string>
- <string name="private_num" msgid="6374339738119166953">"Leyninúmer"</string>
- <string name="payphone" msgid="7726415831153618726">"Símasjálfsali"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> sek."</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> mín. og <xliff:g id="SECONDS">%s</xliff:g> sek."</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> kl. <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Ekki er hægt að hringja í þetta númer"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Til að setja upp talhólf þarftu að opna valmyndina og velja Stillingar."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Til að hringja í talhólfið þarftu fyrst að slökkva á flugstillingu."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Hleður…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Hleður af SIM-kortinu…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"Tengiliðir á SIM-korti"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Ekkert tengiliðaforrit í boði"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Raddleit er ekki í boði"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Ekki er hægt að hringja vegna þess að forritið Sími hefur verið gert óvirkt."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Ekkert forrit fyrir þetta er í tækinu"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Leita að tengiliðum"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Bættu við númeri eða tengilið"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Símtalaferillinn er auður"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Hringja"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Þú ert ekki með nein ósvöruð símtöl."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Talhólfið þitt er tómt."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Talhólfsskilaboðageymslan er tóm."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Sýna aðeins uppáhaldstengiliði"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Símtalaferill"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Geymsla talhólfsskilaboða"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Allt"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Ósvöruð"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Talhólf"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Nýr, einfaldari bannlisti"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Til að vernda þig enn betur þarf símaforritið að breyta því hvernig farið er með bannlista. Séu númer á bannlista verður bæði lokað fyrir símtöl og skilaboð frá þeim og hugsanlega verður þeim deilt með öðrum forritum."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Leyfa"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Setja <xliff:g id="NUMBER">%1$s</xliff:g> á bannlista?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Lokað verður fyrir símtöl frá þessu númeri og talhólfsskilaboðum sjálfkrafa eytt."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Lokað verður fyrir símtöl frá þessu númeri. Hugsanlegt er að sá sem hringir geti engu að síður sent þér talhólfsskilaboð."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Þú munt ekki lengur fá símtöl eða textaskilaboð úr þessu númeri."</string>
- <string name="block_number_ok" msgid="770551992296781873">"SETJA Á BANNLISTA"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Taka <xliff:g id="NUMBER">%1$s</xliff:g> af bannlista?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"TAKA AF BANNLISTA"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Hraðval"</string>
- <string name="tab_history" msgid="2563144697322434940">"Símtalaferill"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Tengiliðir"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Talhólf"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Fjarlægður úr uppáhaldi"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Afturkalla"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Hringja í <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Búa til nýjan tengilið"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Bæta við tengilið"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Senda SMS-skilaboð"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Hringja myndsímtal"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Setja númer á bannlista"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> ný ósvöruð símtöl"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Þú ert ekki með neinn í hraðvali enn sem komið er"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Bæta uppáhaldi við"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Þú ert ekki með neina tengiliði enn sem komið er"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Bæta tengilið við"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Snertu myndina til að sjá öll númer eða haltu henni inni til að endurraða"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Fjarlægja"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Myndsímtal"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Senda skilaboð"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Símtalsupplýsingar"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Hringja í <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Ósvarað símtal frá <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Svarað símtal frá <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Ólesin talhólfsskilaboð frá <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Talhólfsskilaboð frá <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Símtal til <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"í gegnum <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"í gegnum <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"í gegnum <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"á <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, í gegnum <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> í gegnum <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Hringja"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Hringja í <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Hringja myndsímtal í <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Hlusta á talhólfsskilaboð frá <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Spila talhólfsskilaboð sem <xliff:g id="NAMEORNUMBER">^1</xliff:g> sendi"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Gera hlé á talhólfsskilaboðum sem <xliff:g id="NAMEORNUMBER">^1</xliff:g> sendi"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Eyða talhólfsskilaboðum sem <xliff:g id="NAMEORNUMBER">^1</xliff:g> sendi"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> ný talhólfsskilaboð</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> ný talhólfsskilaboð</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Búa til tengilið fyrir <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Bæta <xliff:g id="NAMEORNUMBER">^1</xliff:g> við fyrirliggjandi tengilið"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Símtalsupplýsingar fyrir <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Eytt af símtalaferli"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Í dag"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Í gær"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Eldra"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Símtalalisti"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Kveikja á hátalara."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Slökkva á hátalara."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Spila hraðar."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Spila hægar."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Hefja eða gera hlé á spilun."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Birtingarvalkostir"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Hljóð og titringur"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Aðgengi"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Hringitónn síma"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Titra líka fyrir símtöl"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tónar takkaborðs"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Lengd takkatóns"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Venjulegur"</item>
- <item msgid="6177579030803486015">"Langur"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Snarsvör"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Símtöl"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Lokað fyrir símtöl"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Tímabundið slökkt á lokun fyrir símtöl"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Lokun fyrir símtöl hefur verið gerð óvirk vegna þess að þú hafðir samband við neyðarþjónustu úr þessum síma á undanförnum tveimur sólarhringum. Lokunin verður aftur virk að þessum tveimur sólarhringum liðnum."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Flytja inn númer"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Þú merktir áður nokkra hringjendur þannig að þeir verði sjálfkrafa sendir í talhólfið í gegnum önnur forrit."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Skoða tölur"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Flytja inn"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Innflutningur mistókst"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Ekki tókst að setja talhólfsskilaboð í geymslu."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Taka númer af bannlista"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Bæta númeri við"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Lokað verður fyrir símtöl frá þessum númerum og talhólfsskilaboðum sjálfkrafa eytt."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Lokað verður fyrir símtöl frá þessum númerum. Hugsanlegt er að þeir sem hringja geti engu að síður sent þér talhólfsskilaboð."</string>
- <string name="block_list" msgid="7760188925338078011">"Númer á bannlista"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> er ógilt."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> er nú þegar á bannlista."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Slökkt á lokun símtala í 48 klukkustundir"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Óvirkt vegna þess að neyðarsímtal var hringt."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Símtalareikningar"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Kveikja"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Stilla heimildir"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Kveiktu á tengiliðaheimildinni til að gera hraðval virkt."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Kveiktu á heimild símaforritsins til að sjá símtalaskrána þína."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Kveiktu á tengiliðaheimildinni til að sjá tengiliðina þína."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Kveiktu á heimild símaforritsins til að fá aðgang að talhólfinu þínu."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Kveiktu á heimildunum fyrir tengiliði til að leita að tengiliðum."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Kveiktu á heimild símaforritsins til að hringja símtal."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Símaforritið hefur ekki heimild til að breyta kerfisstillingum."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Á bannlista"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"Símtal við <xliff:g id="NAMEORNUMBER">^1</xliff:g> er í gangi"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Setja á bannlista / tilkynna"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Setja á bannlista"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Ekki rusl"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Taka af bannlista"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Ruslnúmer"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Setja <xliff:g id="NUMBER">%1$s</xliff:g> á bannlista?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Lokað verður fyrir frekari símtöl og talhólfsskilaboð frá þessu númeri."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Tilkynna símtal sem ruslefni"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Lokað verður fyrir símtöl og talhólfsskilaboð frá þessu númeri. Þetta símtal verður tilkynnt."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Taka <xliff:g id="NUMBER">%1$s</xliff:g> af bannlista?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Þetta númer verður tekið af bannlista og ekki tilkynnt sem ruslefni. Frekari símtöl og skilaboð verða ekki tilkynnt."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Bæta <xliff:g id="NUMBER">%1$s</xliff:g> á undanþágulista?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Bæta á undanþágulista"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Lokað verður fyrir símtöl og talhólfsskilaboð frá þessu númeri. Þetta símtal verður ekki tilkynnt."</string>
-</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
deleted file mode 100644
index 7411984de..000000000
--- a/res/values-it/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefono"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefono"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Tastierino del telefono"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefono"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Cronologia chiamate"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Segnala numero sbagliato"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Copia numero"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Copia trascrizione"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blocca numero"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> bloccato"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Sblocca numero"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> sbloccato"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ANNULLA"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Elimina"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Modifica numero prima di effettuare la chiamata"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Cancella cronologia chiamate"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Elimina messaggi della segreteria"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Archivia messaggio vocale"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Condividi messaggio vocale"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Mes vocali eliminati"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Messaggio vocale archiviato"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ANNULLA"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"VAI AD ARCHIVIO"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Cancellare la cronologia chiamate?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Verranno eliminate tutte le chiamate dalla cronologia"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Cancellazione cronologia chiamate…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefono"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Chiamata persa"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Chiamata di lavoro persa"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Chiamate perse"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> chiamate perse"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Richiama"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Messaggio"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> messaggi in segreteria </item>
- <item quantity="one">Messaggio in segreteria</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Riproduci"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nuovo messaggio vocale da <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Impossibile riprodurre i messaggi vocali"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Caricamento dei messaggi vocali…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Archiviazione messaggio vocale…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Impossibile caricare i messaggi vocali"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Solo chiamate con segreteria"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Solo chiamate in arrivo"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Solo chiamate in uscita"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Solo chiamate perse"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Leggi la segreteria"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Leggi e ascolta i tuoi messaggi in segreteria senza chiamare alcun numero. Potrebbero essere applicate le tariffe previste per il traffico di dati."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Impostazioni"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Aggiornamenti della segreteria non disponibili"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Nuovo messaggio vocale in attesa. Impossibile caricare al momento."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Configura la segreteria"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio non disponibile"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Imposta"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Chiama segreteria"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Scegli numero"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Scegli numero"</string>
- <string name="make_primary" msgid="5829291915305113983">"Memorizza questa scelta"</string>
- <string name="description_search_button" msgid="3660807558587384889">"cerca"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"componi"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"numero da comporre"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Avvia o interrompi riproduzione"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Attiva o disattiva vivavoce"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Cerca posizione di riproduzione"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Diminuisci velocità di riproduzione"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Aumenta velocità di riproduzione"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Cronologia chiamate"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Altre opzioni"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"tastierino"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Mostra solo in uscita"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Mostra solo in arrivo"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Mostra solo senza risposta"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Mostra solo messaggi vocali"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Mostra tutte le chiamate"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Aggiungi pausa 2 sec"</string>
- <string name="add_wait" msgid="3360818652790319634">"Aggiungi attesa"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Impostazioni"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Nuovo contatto"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Tutti i contatti"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Dettagli chiamata"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Dettagli non disponibili"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Usa tastierino per selezione a toni"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Torna alla chiamata in corso"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Aggiungi chiamata"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Chiamata in arrivo"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Chiamata effettuata"</string>
- <string name="type_missed" msgid="2720502601640509542">"Chiamata persa"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Videochiamata in arrivo"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Videochiamata in uscita"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Videochiamata persa"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Segreteria"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Chiamata rifiutata"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Chiamata bloccata"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Chiamate in arrivo"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Riproduci messaggio vocale"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Visualizza <xliff:g id="NAME">%1$s</xliff:g> contatto"</string>
- <string name="description_call" msgid="3443678121983852666">"Chiama <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Dettagli contatto <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> chiamate."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videochiamata."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Invia SMS a <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Messaggio vocale non ascoltato"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Avvia la ricerca vocale"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Chiama <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Sconosciuto"</string>
- <string name="voicemail" msgid="3851469869202611441">"Segreteria"</string>
- <string name="private_num" msgid="6374339738119166953">"Numero privato"</string>
- <string name="payphone" msgid="7726415831153618726">"Cabina telefonica"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> secondi"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> alle ore <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Impossibile chiamare questo numero"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Per configurare la segreteria, seleziona Menu &gt; Impostazioni."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Per chiamare la segreteria, disattiva la modalità aereo."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Caricamento..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Caricamento da SIM..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Contatti SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Nessuna app di contatti disponibile"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Ricerca vocale non disponibile"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Impossibile effettuare una telefonata perché l\'applicazione Telefono è stata disattivata."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Sul dispositivo non sono presenti app per tale azione"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Cerca nei contatti"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Aggiungi numero/cerca contatti"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"La cronologia delle chiamate è vuota"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Fai una chiamata"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Nessuna chiamata persa."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"La segreteria è vuota."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"L\'archivio dei messaggi vocali è vuoto."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Mostra solo i preferiti"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Cronologia chiamate"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Archivio messaggi vocali"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Tutte"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Perse"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Segreteria"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Nuovo blocco semplificato"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Per una migliore protezione, l\'app Telefono deve modificare il funzionamento del blocco. Ora gli SMS e le chiamate provenienti dai numeri bloccati non verranno ricevuti. I numeri bloccati, inoltre, possono essere condivisi con altre app."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Consenti"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Bloccare il numero <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Le chiamate da questo numero verranno bloccate e i messaggi in segreteria verranno automaticamente eliminati."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Le chiamate da questo numero verranno bloccate, ma il chiamante potrebbe lasciarti messaggi in segreteria."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Non riceverai più chiamate o SMS da questo numero."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOCCA"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Sbloccare il numero <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"SBLOCCA"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Composizione rapida"</string>
- <string name="tab_history" msgid="2563144697322434940">"Cronologia chiamate"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Contatti"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Segreteria"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Rimosso dai preferiti"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Annulla"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Chiama <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Crea nuovo contatto"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Aggiungi a un contatto"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Invia SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Fai una videochiamata"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blocca numero"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> nuove chiamate perse"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Nessun preferito disponibile nella Composizione rapida"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Aggiungi un preferito"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Nessun contatto disponibile"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Aggiungi un contatto"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Tocca l\'immagine per visualizzare tutti i numeri, oppure tocca e tieni premuto per cambiare l\'ordine"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Rimuovi"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videochiamata"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Invia un messaggio"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Dettagli chiamata"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Chiama <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Chiamata senza risposta di <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Risposta alla chiamata di <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Messaggio in segreteria da ascoltare da <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Messaggio in segreteria da <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Chiamata a <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"da o verso <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"tramite <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"tramite <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"su <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, tramite <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> tramite <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Chiama"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Chiama <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Videochiamata <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Ascolta il messaggio vocale di: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Riproduci i messaggi di <xliff:g id="NAMEORNUMBER">^1</xliff:g> in segreteria"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Metti in pausa i messaggi di <xliff:g id="NAMEORNUMBER">^1</xliff:g> in segreteria"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Elimina i messaggi di <xliff:g id="NAMEORNUMBER">^1</xliff:g> dalla segreteria"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> nuovi messaggi vocali</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> nuovo messaggio vocale</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Crea contatto per <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Aggiungi <xliff:g id="NAMEORNUMBER">^1</xliff:g> al contatto esistente"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Dettagli chiamata per <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Eliminato dalla cronologia chiamate"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Oggi"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Ieri"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Meno recenti"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Elenco chiamate"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Attiva altoparlante."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Disattiva altoparlante."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Riproduci più velocemente."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Riproduci più lentamente."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Avvia o metti in pausa la riproduzione."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Opzioni di visualizzazione"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Suoni e vibrazione"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Accessibilità"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Suoneria telefono"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibrazione per le chiamate"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Toni tastierino"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Durata tono tastierino"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normale"</item>
- <item msgid="6177579030803486015">"Lunga"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Risposte rapide"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Chiamate"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Blocco delle chiamate"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Blocco chiamate temporaneam. disattivato"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Il blocco chiamate è stato disattivato perché hai contattato servizi di emergenza da questo telefono nelle ultime 48 ore. Verrà riattivato automaticamente una volta trascorso il periodo di 48 ore."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importa numeri"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Hai già contrassegnato alcuni chiamanti da inviare automaticamente alla segreteria tramite altre app."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Visualizza numeri"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importa"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Importazione non riuscita"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Impossibile archiviare messaggio vocale."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Sblocca numero"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Aggiungi numero"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Le chiamate da questi numeri verranno bloccate e i messaggi in segreteria verranno automaticamente eliminati."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Le chiamate da questi numeri verranno bloccate, ma i chiamanti potrebbero lasciarti messaggi in segreteria."</string>
- <string name="block_list" msgid="7760188925338078011">"Numeri bloccati"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> non è valido."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> è già bloccato."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Il blocco chiamate è stato disattivato per 48 ore"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Funzione disattivata: è stata fatta una chiamata di emergenza."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Account di chiamata"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Attiva"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Imposta autorizzazioni"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Per attivare la composizione rapida, attiva l\'autorizzazione Contatti."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Per accedere al registro chiamate, attiva l\'autorizzazione sul telefono."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Per accedere ai tuoi contatti, attiva l\'autorizzazione Contatti."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Per accedere alla segreteria, attiva l\'autorizzazione sul telefono."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Per cercare nei tuoi contatti, attiva le autorizzazioni Contatti."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Per fare una chiamata, attiva l\'autorizzazione sul telefono."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"L\'app Telefono non dispone dell\'autorizzazione per modificare le impostazioni di sistema."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Bloccato"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> è attivo"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blocca/Segnala come spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blocca"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Non spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Sblocca"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Bloccare il numero <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Le chiamate e i messaggi vocali futuri da questo numero verranno bloccati."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Segnala la chiamata come spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Le chiamate e i messaggi vocali futuri da questo numero verranno bloccati. Questa chiamata verrà segnalata come spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Sbloccare il numero <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Questo numero verrà sbloccato e segnalato come non spam. Le chiamate e i messaggi vocali futuri non verranno identificati come spam."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Autorizzare <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Autorizza"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Le chiamate e i messaggi vocali futuri da questo numero non verranno identificati come spam. Questo numero verrà segnalato come non spam."</string>
-</resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
deleted file mode 100644
index 467020bd0..000000000
--- a/res/values-iw/strings.xml
+++ /dev/null
@@ -1,292 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"טלפון"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"טלפון"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"לוח חיוג של הטלפון"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"טלפון"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"היסטוריית שיחות"</string>
- <string name="action_report_number" msgid="4635403959812186162">"דיווח על מספר לא מדויק"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"העתק מספר"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"העתק תמלול"</string>
- <string name="action_block_number" msgid="1482657602262262134">"חסום מספר"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> חסום"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"בטל חסימת מספר"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"בוטלה החסימה של <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="block_number_undo" msgid="591338370336724156">"בטל"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"מחיקה"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"ערוך את המספר לפני השיחה"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"ניקוי היסטוריית השיחות"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"מחיקת דואר קולי"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"העברת דואר קולי לארכיון"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"שיתוף של הדואר הקולי"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"דואר קולי נמחק"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"הדואר הקולי הועבר לארכיון"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"בטל"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"לארכיון"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"האם לנקות את היסטוריית השיחות?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"פעולה זו תמחק את כל השיחות מההיסטוריה שלך"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"מנקה היסטוריית שיחות…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"טלפון"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"שיחה שלא נענתה"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"שיחה עסקית שלא נענתה"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"שיחות שלא נענו"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> שיחות שלא נענו"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"התקשר חזרה"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"הודעה"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="two"> <xliff:g id="COUNT">%1$d</xliff:g> הודעות דואר קולי </item>
- <item quantity="many"> <xliff:g id="COUNT">%1$d</xliff:g> הודעות דואר קולי </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> הודעות דואר קולי </item>
- <item quantity="one">הודעת דואר קולי</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"הפעל"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"דואר קולי חדש מאת <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"לא ניתן היה להשמיע דואר קולי"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"טוען דואר קולי…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"מעביר את הדואר הקולי לארכיון..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"לא ניתן היה לטעון דואר קולי"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"שיחות עם דואר קולי בלבד"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"שיחות נכנסות בלבד"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"שיחות יוצאות בלבד"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"שיחות שלא נענו בלבד"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"דואר קולי ויזואלי"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"הצג את הדואר הקולי והאזן לו בלי להתקשר למספר אחר. ייתכנו חיובים על צריכת נתונים."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"הגדרות"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"עדכוני דואר קולי אינם זמינים"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"יש דואר קולי חדש בהמתנה. לא ניתן לטעון כעת."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"הגדר את הדואר הקולי שלך"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"אודיו אינו זמין"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"הגדר"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"התקשר לדואר קולי"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"בחר מספר"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"בחר מספר"</string>
- <string name="make_primary" msgid="5829291915305113983">"זכור בחירה זו"</string>
- <string name="description_search_button" msgid="3660807558587384889">"חפש"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"חייג"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"מספר לחיוג"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"הפעל או הפסק הפעלה"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"הפעל או כבה את רמקול הטלפון"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"חפש מיקום בהפעלה"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"האט את מהירות ההפעלה"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"הגבר את מהירות ההפעלה"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"היסטוריית שיחות"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"אפשרויות נוספות"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"לוח חיוג"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"רק שיחות יוצאות"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"רק שיחות נכנסות"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"רק שיחות שלא נענו"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"הודעות דואר קולי בלבד"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"כל השיחות"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"הוסף השהיה של 2 שניות"</string>
- <string name="add_wait" msgid="3360818652790319634">"הוסף המתנה"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"הגדרות"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"איש קשר חדש"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"כל אנשי הקשר"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"פרטי שיחה"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"אין פרטים זמינים"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"השתמש במקלדת עם חיוג צלילים"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"חזור לשיחה פעילה"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"הוסף שיחה"</string>
- <string name="type_incoming" msgid="6502076603836088532">"שיחה נכנסת"</string>
- <string name="type_outgoing" msgid="343108709599392641">"שיחה יוצאת"</string>
- <string name="type_missed" msgid="2720502601640509542">"שיחה שלא נענתה"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"שיחת וידאו נכנסת"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"שיחת וידאו יוצאת"</string>
- <string name="type_missed_video" msgid="954396897034220545">"שיחת וידאו שלא נענתה"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"דואר קולי"</string>
- <string name="type_rejected" msgid="7783201828312472691">"שיחה שנדחתה"</string>
- <string name="type_blocked" msgid="3521686227115330015">"שיחה חסומה"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"שיחות נכנסות"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"הפעל דואר קולי"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"הצג את איש הקשר <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"התקשר אל <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"פרטי יצירת קשר עבור <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> שיחות."</string>
- <string name="description_video_call" msgid="2933838090743214204">"שיחת וידאו."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"‏שלח SMS אל <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"דואר קולי שעדיין לא נשמע"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"התחל חיפוש קולי"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"התקשר אל <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"לא ידוע"</string>
- <string name="voicemail" msgid="3851469869202611441">"דואר קולי"</string>
- <string name="private_num" msgid="6374339738119166953">"מספר פרטי"</string>
- <string name="payphone" msgid="7726415831153618726">"טלפון ציבורי"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> שניות"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> דק\' <xliff:g id="SECONDS">%s</xliff:g> שנ\'"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> ב-<xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"לא ניתן להתקשר אל המספר הזה"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"‏כדי להגדיר את הדואר הקולי, עבור אל \'תפריט\' &gt; \'הגדרות\'."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"כדי להתקשר לדואר קולי, ראשית כבה את מצב הטיסה."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"טוען..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"‏טוען מכרטיס SIM…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"‏אנשי קשר בכרטיס SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"אין אפליקציה זמינה עבור אנשי קשר"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"חיפוש קולי אינו זמין"</string>
- <string name="call_not_available" msgid="8941576511946492225">"לא ניתן לבצע שיחת טלפון מפני שאפליקציית הטלפון הושבתה."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"אין אפליקציה עבור הפעולה הזו במכשיר הזה"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"חפש אנשי קשר"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"הוסף מספר או חפש אנשי קשר"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"היסטוריית השיחות שלך ריקה"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"התקשר"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"אין שיחות שלא נענו."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"תיבת הדואר הקולי ריקה."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"הארכיון של הדואר הקולי ריק."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"הצג מועדפים בלבד"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"היסטוריית שיחות"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"הארכיון של הדואר הקולי"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"הכל"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"שיחה שלא נענתה"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"דואר קולי"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"תהליך חסימה חדש ופשוט"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"כדי להגן עליך טוב יותר, הטלפון צריך לשנות את האופן שבו החסימה פועלת. לא תוכל לקבל שיחות והודעות טקסט ממספרים חסומים, וייתכן שהם ישותפו עם אפליקציות אחרות."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"אפשר"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"האם לחסום את המספר <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"שיחות ממספר זה ייחסמו והודעות דואר קולי יימחקו באופן אוטומטי."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"שיחות ממספר זה ייחסמו, אבל המתקשר עדיין יוכל להשאיר לך הודעות דואר קולי."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"לא תקבל יותר שיחות או הודעות טקסט מהמספר הזה."</string>
- <string name="block_number_ok" msgid="770551992296781873">"חסום"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"האם לבטל את חסימת המספר <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ביטול חסימה"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"חיוג מהיר"</string>
- <string name="tab_history" msgid="2563144697322434940">"היסטוריית שיחות"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"אנשי קשר"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"דואר קולי"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"הוסר מהמועדפים"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"בטל"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"התקשר אל <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"צור איש קשר חדש"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"הוסף לאיש קשר"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"‏שלח SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"בצע שיחת וידאו"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"חסום מספר"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> שיחות חדשות שלא נענו"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"עדיין לא הוגדר חיוג מהיר לאף איש קשר"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"הוסף פריט מועדף"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"עדיין אין לך אנשי קשר"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"הוסף איש קשר"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"גע בתמונה כדי לראות את כל המספרים או גע נגיעה ממושכת כדי לשנות את הסדר"</string>
- <string name="remove_contact" msgid="1080555335283662961">"הסר"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"שיחת וידאו"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"שלח הודעה"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"פרטי שיחה"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"התקשר אל <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"שיחה לא נענתה מ-<xliff:g id="NAMEORNUMBER">^1</xliff:g>‏, <xliff:g id="TYPEORLOCATION">^2</xliff:g>‏, <xliff:g id="TIMEOFCALL">^3</xliff:g>‏, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"נענתה שיחה מ-<xliff:g id="NAMEORNUMBER">^1</xliff:g>‏, <xliff:g id="TYPEORLOCATION">^2</xliff:g>‏, <xliff:g id="TIMEOFCALL">^3</xliff:g>‏, <xliff:g id="PHONEACCOUNT">^4</xliff:g>‏."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"דואר קולי שלא נקרא מ-<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"דואר קולי מ-<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"שיחה אל <xliff:g id="NAMEORNUMBER">^1</xliff:g>‏, <xliff:g id="TYPEORLOCATION">^2</xliff:g>‏, <xliff:g id="TIMEOFCALL">^3</xliff:g>‏, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"ב-<xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"דרך <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"דרך <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"ב-<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, דרך <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> דרך <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"התקשר"</string>
- <string name="description_call_action" msgid="4000549004089776147">"התקשר אל <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"שיחת וידאו עם <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"האזן לדואר קולי מאת <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"השמע דואר קולי מאת <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"השהה דואר קולי מאת <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"מחק דואר קולי מאת <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="two"><xliff:g id="COUNT_1">%d</xliff:g> הודעות דואר קולי חדשות</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> הודעות דואר קולי חדשות</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> הודעות דואר קולי חדשות</item>
- <item quantity="one">הודעת דואר קולי חדשה <xliff:g id="COUNT_0">%d</xliff:g></item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"צור איש קשר בשביל <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"הוסף את <xliff:g id="NAMEORNUMBER">^1</xliff:g> לאיש קשר קיים"</string>
- <string name="description_details_action" msgid="2433827152749491785">"פרטי שיחה עבור <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"נמחקה מהיסטוריית השיחות"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"היום"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"אתמול"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"ישנות יותר"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"רשימת שיחות"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"הפעל את הרמקול."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"כבה את הרמקול."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"הפעל מהר יותר."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"הפעל לאט יותר."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"התחל או השהה הפעלה."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"אפשרויות תצוגה"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"צלילים ורטט"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"נגישות"</string>
- <string name="ringtone_title" msgid="760362035635084653">"רינגטון של טלפון"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"הפעל רטט גם עבור שיחות"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"צלילי לוח החיוג"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"אורך צליל של לוח חיוג"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"רגיל"</item>
- <item msgid="6177579030803486015">"ארוך"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"תגובות מהירות"</string>
- <string name="call_settings_label" msgid="313434211353070209">"שיחות"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"חסימת שיחות"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"חסימת השיחות מושבתת באופן זמני"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"חסימת השיחות הושבתה מפני שיצרת קשר מטלפון זה עם שירותי חירום במהלך 48 השעות האחרונות. הפונקציה תופעל מחדש באופן אוטומטי בתום 48 השעות."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"יבא מספרים"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"סימנת בעבר באמצעות יישומים אחרים כמה מתקשרים שיישלחו באופן אוטומטי לדואר קולי."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"הצג מספרים"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"יבא"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"הייבוא נכשל"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"העברת הדואר הקולי לארכיון נכשלה."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"בטל חסימת מספר"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"הוסף מספר"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"שיחות ממספרים אלה ייחסמו והודעות דואר קולי יימחקו באופן אוטומטי."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"שיחות ממספרים אלה ייחסמו, אבל המתקשרים עדיין יוכלו להשאיר לך הודעות דואר קולי."</string>
- <string name="block_list" msgid="7760188925338078011">"מספרים חסומים"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> אינו חוקי."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> כבר חסום."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"חסימת השיחות הושבתה ל-48 שעות"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"מושבתת מפני שבוצעה שיחת חירום."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"חשבונות לביצוע שיחות"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"הפעל"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"הגדר הרשאות"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"כדי להפעיל חיוג מהיר, הפעל את ההרשאה \'אנשי קשר\'."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"כדי לראות את יומן השיחות, הפעל את ההרשאה \'טלפון\'."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"כדי להציג את אנשי הקשר, הפעל את ההרשאה \'אנשי קשר\'."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"כדי לגשת לדואר הקולי, הפעל את ההרשאה \'טלפון\'."</string>
- <string name="permission_no_search" msgid="84152933267902056">"כדי לחפש באנשי הקשר, הפעל את ההרשאה \'אנשי קשר\'."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"כדי להתקשר, הפעל את ההרשאה \'טלפון\'."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"לאפליקציית הטלפון אין הרשאה לכתוב בהגדרות המערכת."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"חסום"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> זמין/ה"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"חסום/דווח על ספאם"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"חסום"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"לא ספאם"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"בטל חסימה"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"ספאם"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"האם לחסום את <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"שיחות והודעות קוליות שיתקבלו מהמספר הזה ייחסמו."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"דיווח על השיחה כספאם"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"שיחות והודעות קוליות שיתקבלו מהמספר הזה ייחסמו והשיחה תדווח כספאם."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"האם לבטל את החסימה של <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"החסימה של המספר הזה תבוטל וידווח שהוא אינו ספאם. שיחות והודעות קוליות שיתקבלו ממנו לא יזוהו כספאם."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"האם להוסיף את <xliff:g id="NUMBER">%1$s</xliff:g> לרשימת ההיתרים?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"רשימת היתרים"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"המספר הזה לא ידווח כספאם. שיחות והודעות קוליות שיתקבלו ממנו לא יזוהו כספאם."</string>
-</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
deleted file mode 100644
index bb3ee56e6..000000000
--- a/res/values-ja/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"電話"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"電話"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"電話ダイヤルパッド"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"電話"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"通話履歴"</string>
- <string name="action_report_number" msgid="4635403959812186162">"不正確な番号を報告"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"番号をコピー"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"音声文字変換をコピー"</string>
- <string name="action_block_number" msgid="1482657602262262134">"番号をブロック"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g>をブロックしました"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"番号のブロックを解除"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g>のブロックを解除しました"</string>
- <string name="block_number_undo" msgid="591338370336724156">"元に戻す"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"削除"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"発信前に番号を編集"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"通話履歴を消去"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"ボイスメールを削除"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"ボイスメールをアーカイブする"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"ボイスメールを共有"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"ボイスメールの削除"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"ボイスメールをアーカイブしました"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"元に戻す"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"アーカイブ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"通話履歴を消去しますか?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"消去すると、すべての通話が履歴から削除されます"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"通話履歴の消去中…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"電話"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"不在着信"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"仕事の通話の不在着信"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"不在着信"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"不在着信 <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> 件"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"折り返す"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"メッセージ"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"><xliff:g id="COUNT">%1$d</xliff:g>件のボイスメール</item>
- <item quantity="one">1件のボイスメール</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"再生"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>、<xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g>から新着ボイスメール"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"ボイスメールを再生できませんでした"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"ボイスメールを読み込んでいます…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"ボイスメールをアーカイブしています…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"ボイスメールを読み込めませんでした"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"ボイスメールのある着信のみ"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"着信のみ"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"発信のみ"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"不在着信のみ"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"ビジュアルボイスメール"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"電話を発信せずにボイスメールを確認して聞くことができます。データ通信料が発生する場合があります。"</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"設定"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"新着ボイスメールは現在利用できません"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"新着ボイスメールがあります。現在は読み込めません。"</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"ボイスメールを設定してください"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"音声を利用できません"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"セットアップ"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"ボイスメール呼び出し"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>)<xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"番号を選択"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"番号を選択"</string>
- <string name="make_primary" msgid="5829291915305113983">"この選択を保存"</string>
- <string name="description_search_button" msgid="3660807558587384889">"検索"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"発信"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"発信番号"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"再生を開始または停止する"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"スピーカーフォンのON/OFFを切り替える"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"再生位置を探す"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"再生速度を下げる"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"再生速度を上げる"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"通話履歴"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"その他のオプション"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"ダイヤルパッド"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"発信のみを表示"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"着信のみを表示"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"不在着信のみを表示"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"ボイスメールのみ表示"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"すべての通話を表示"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2秒間の停止を追加"</string>
- <string name="add_wait" msgid="3360818652790319634">"待機を追加"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"設定"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"連絡先を新規登録"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"すべての連絡先"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"通話詳細"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"詳細情報がありません"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"プッシュホン式キーパッドを使う"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"通話に戻る"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"別の通話を追加"</string>
- <string name="type_incoming" msgid="6502076603836088532">"通話着信"</string>
- <string name="type_outgoing" msgid="343108709599392641">"発信履歴"</string>
- <string name="type_missed" msgid="2720502601640509542">"不在着信"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"ビデオハングアウト着信"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"ビデオハングアウト発信"</string>
- <string name="type_missed_video" msgid="954396897034220545">"ビデオハングアウト不在着信"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"ボイスメール"</string>
- <string name="type_rejected" msgid="7783201828312472691">"拒否された通話"</string>
- <string name="type_blocked" msgid="3521686227115330015">"ブロックされた通話"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"着信"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"ボイスメールを再生"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g>の連絡先を表示"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g>に発信"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g>の連絡先の詳細"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"通話回数は<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>回。"</string>
- <string name="description_video_call" msgid="2933838090743214204">"ビデオハングアウト"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g>さんにSMSを送信"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"未再生のボイスメール"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"音声検索を開始"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g>に発信"</string>
- <string name="unknown" msgid="740067747858270469">"通知不可能"</string>
- <string name="voicemail" msgid="3851469869202611441">"ボイスメール"</string>
- <string name="private_num" msgid="6374339738119166953">"非通知設定"</string>
- <string name="payphone" msgid="7726415831153618726">"公衆電話"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g>秒"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g>分<xliff:g id="SECONDS">%s</xliff:g>秒"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g>、<xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g> 分 <xliff:g id="SECONDS">%2$02d</xliff:g> 秒"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g>(<xliff:g id="DURATION">%2$s</xliff:g>)"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"この番号に電話できません"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"ボイスメールをセットアップするには、MENUキー&gt;[設定]をタップします。"</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"機内モードをOFFにしてからボイスメールを呼び出してください。"</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"読み込んでいます..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI(端末識別番号)"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"SIMカードから読み取り中..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIMカードの連絡先"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"連絡先アプリがありません"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"音声検索を利用できません"</string>
- <string name="call_not_available" msgid="8941576511946492225">"電話アプリが無効になっているため発信できません。"</string>
- <string name="activity_not_available" msgid="2287665636817987623">"この操作を行うアプリが端末上にありません"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"連絡先を検索"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"番号を追加するか連絡先を検索"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"通話履歴はありません"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"発信"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"不在着信はありません。"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"ボイスメール受信トレイは空です。"</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"ボイスメールのアーカイブが空です。"</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"お気に入りのみを表示"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"通話履歴"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"ボイスメールのアーカイブ"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"すべて"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"不在着信"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"ボイスメール"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"新しい、簡素化されたブロック"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"セキュリティを強化するため、スマートフォンはブロック動作を変更する必要があります。ブロックされた電話番号からの通話とテキスト メッセージの両方が停止されます。また、ブロックされた電話番号は他のアプリと共有できます。"</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"許可"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> をブロックしますか?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"この番号からの着信はブロックされ、ボイスメールは自動的に削除されます。"</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"この番号からの着信はブロックされますが、発信者は今後もボイスメールを残すことができます。"</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"今後はこの番号からの通話やテキスト メッセージは受信しなくなります。"</string>
- <string name="block_number_ok" msgid="770551992296781873">"ブロック"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g>のブロックを解除しますか?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ブロックを解除"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"クイックアクセス"</string>
- <string name="tab_history" msgid="2563144697322434940">"通話履歴"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"連絡先"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"ボイスメール"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"お気に入りから削除されました"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"元に戻す"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g>に発信"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"新しい連絡先を作成"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"連絡先に追加"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMSを送信"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"ビデオハングアウト"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"番号をブロック"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g>件の不在着信"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"クイックアクセスに登録済みの連絡先はまだありません"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"お気に入りを追加"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"連絡先はまだありません"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"連絡先を追加"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"画像をタップするとすべての番号を表示でき、押し続けると並べ替えることができます"</string>
- <string name="remove_contact" msgid="1080555335283662961">"削除"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"ビデオハングアウト"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"メッセージを送信"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"通話の詳細"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>に発信"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="TIMEOFCALL">^3</xliff:g>の<xliff:g id="TYPEORLOCATION">^2</xliff:g><xliff:g id="NAMEORNUMBER">^1</xliff:g>(<xliff:g id="PHONEACCOUNT">^4</xliff:g>)からの不在着信。"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="TIMEOFCALL">^3</xliff:g>の<xliff:g id="TYPEORLOCATION">^2</xliff:g><xliff:g id="NAMEORNUMBER">^1</xliff:g>(<xliff:g id="PHONEACCOUNT">^4</xliff:g>)からの着信。"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="TIMEOFCALL">^3</xliff:g>の<xliff:g id="TYPEORLOCATION">^2</xliff:g><xliff:g id="NAMEORNUMBER">^1</xliff:g>(<xliff:g id="PHONEACCOUNT">^4</xliff:g>)からの未読のボイスメール。"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="TIMEOFCALL">^3</xliff:g>の<xliff:g id="TYPEORLOCATION">^2</xliff:g><xliff:g id="NAMEORNUMBER">^1</xliff:g>(<xliff:g id="PHONEACCOUNT">^4</xliff:g>)からのボイスメール。"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="TIMEOFCALL">^3</xliff:g>の<xliff:g id="TYPEORLOCATION">^2</xliff:g><xliff:g id="NAMEORNUMBER">^1</xliff:g>(<xliff:g id="PHONEACCOUNT">^4</xliff:g>)への発信。"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> で受信"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> で受信"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>(<xliff:g id="NUMBER">%2$s</xliff:g>)で受信"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>(<xliff:g id="NUMBER">%2$s</xliff:g>)で受信"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"発信"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>に発信します"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>にビデオハングアウト発信します。"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>からのボイスメールを再生"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>からのボイスメールを再生"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>からのボイスメールを一時停止"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>からのボイスメールを削除"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>件の新着ボイスメール</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>件の新着ボイスメール</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>の連絡先を作成します"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"既存の連絡先に<xliff:g id="NAMEORNUMBER">^1</xliff:g>を追加します"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>の通話の詳細"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"通話履歴から削除しました"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"今日"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"昨日"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"以前の着信"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"通話リスト"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"スピーカーをONにします。"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"スピーカーをOFFにします。"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"速く再生します。"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"遅く再生します。"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"再生を開始または一時停止します。"</string>
- <string name="list_delimeter" msgid="4571593167738725100">"、 "</string>
- <string name="display_options_title" msgid="7812852361055667468">"表示オプション"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"音とバイブレーション"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"ユーザー補助機能"</string>
- <string name="ringtone_title" msgid="760362035635084653">"着信音"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"着信時にバイブレーションもON"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"ダイヤルパッドの音"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"ダイヤルパッドの音の長さ"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"標準"</item>
- <item msgid="6177579030803486015">"長め"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"クイック返信"</string>
- <string name="call_settings_label" msgid="313434211353070209">"通話"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"着信のブロック"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"着信のブロックは一時的にOFFです"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"このスマートフォンから緊急通報番号への発信が過去48時間以内に行われているため、着信のブロックは無効になっています。48時間経過すると、着信のブロックは自動的に有効になります。"</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"番号をインポート"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"以前に一部の発信者について、他のアプリを通じて自動的にボイスメールに転送するようマークを付けています。"</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"番号を表示"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"インポート"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"インポートに失敗しました"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"ボイスメールをアーカイブできませんでした。"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"番号のブロックを解除"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"電話番号を追加"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"この番号からの着信はブロックされ、ボイスメールは自動的に削除されます。"</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"この番号からの着信はブロックされますが、発信者は今後もボイスメールを残すことができます。"</string>
- <string name="block_list" msgid="7760188925338078011">"ブロックした番号"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g>は無効な番号です。"</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g>は既にブロックしています。"</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"着信のブロックは48時間経過するまで無効になりました"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"緊急通報を行ったため無効になりました。"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"通話アカウント"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"ONにする"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"権限を設定"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"クイックアクセスを有効にするには、連絡先権限をONにしてください。"</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"通話履歴を表示するには、電話権限をONにしてください。"</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"連絡先を表示するには、連絡先権限をONにしてください。"</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"ボイスメールにアクセスするには、電話権限をONにしてください。"</string>
- <string name="permission_no_search" msgid="84152933267902056">"連絡先を検索するには、連絡先権限をONにしてください。"</string>
- <string name="permission_place_call" msgid="2101287782615887547">"電話をかけるには、電話権限をONにしてください。"</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"電話アプリにはシステム設定への書き込み権限がありません。"</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"ブロック済み"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> さんが通話中です"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"ブロック / 迷惑電話を報告"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"ブロック"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"迷惑電話ではない"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"ブロックを解除"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"迷惑電話"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> をブロックしますか?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"今後はこの番号からの通話やボイスメールはブロックされます。"</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"通話を迷惑電話として報告する"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"今後はこの番号からの通話やボイスメールはブロックされます。この通話は迷惑電話として報告されます。"</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> のブロックを解除しますか?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"この番号のブロックが解除され、この番号からの通話とボイスメールは迷惑電話とは見なされなくなります。"</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> をホワイトリストに登録しますか?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"ホワイトリストに登録"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"今後はこの番号からの通話やボイスメールは迷惑電話とは見なされません。この番号は迷惑電話ではないと報告されます。"</string>
-</resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
deleted file mode 100644
index 05a01b144..000000000
--- a/res/values-ka/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"ტელეფონი"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"ტელეფონი"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"ტელეფონის ციფერბლატი"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"ტელეფონი"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"საუბრის ისტორია"</string>
- <string name="action_report_number" msgid="4635403959812186162">"არაზუსტი ნომრის შეტყობინება"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"ნომრის კოპირება"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"ტრანსკრიპტის კოპირება"</string>
- <string name="action_block_number" msgid="1482657602262262134">"ნომრის დაბლოკვა"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> დაბლოკილია"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"ნომრის განბლოკვა"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> განბლოკილია"</string>
- <string name="block_number_undo" msgid="591338370336724156">"დაბრუნება"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"წაშლა"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"ნომრის რედაქტირება დარეკვამდე"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"საუბრის ისტორიის გასუფთავება"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"ხმოვანი ფოსტის წაშლა"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"ხმოვანი ფოსტის დაარქივება"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"ხმოვანი ფოსტის გაზიარება"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"ხმოვანი ფოსტა წაიშალა"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"ხმოვანი ფოსტა დაარქივებულია"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"დაბრუნება"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"არქივი"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"გასუფთავდეს ზარის ისტორია?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"ეს წაშლის ყველა ზარს თქვენი ისტორიიდან"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"მიმდ. ზარ. ისტ. გასუფთავება…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"დარეკვა"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"გამოტოვებული ზარი"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"გამოტოვებული ზარი (სამსახური)"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"გამოტოვებული ზარები"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> გამოტოვებული ზარი"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"გადარეკვა"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"შეტყობინება"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> ხმოვანი ფოსტა </item>
- <item quantity="one">ხმოვანი ფოსტა</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"დაკვრა"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"ახალი ხმოვანი ფოსტა <xliff:g id="CALLER">%1$s</xliff:g>-ისგან"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"ვერ ხორციელდება ხმოვანი ფოსტა ჩართვა"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"ხმოვანი ფოსტა იტვირთება…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"ხმოვანი ფოსტის დაარქივება…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"ვერ ხორციელდება ხმოვანი ფოსტის ჩატვირთვა"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"მხოლოდ ზარები ხმოვანი ფოსტით"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"მხოლოდ შემომავალი ზარები"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"მხოლოდ გამავალი ზარები"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"მხოლოდ გამოტოვებული ზარები"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"ვიზუალური ხმოვანი ფოსტა"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"ნახეთ და მოუსმინეთ თქვენს ხმოვან ფოსტას ნომერზე დარეკვის გარეშე. შესაძლოა, გამოიწვიოს დამატებითი ხარჯები."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"პარამეტრები"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"არ არის შესაძლებელი ხმოვანი ფოსტის განახლებები"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"გელოდებათ ახალი ხმოვანი ფოსტა. ახლავე ჩატვირთვა ვერ ხორციელდება."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"დააყენეთ თქვენი ხმოვანი ფოსტა"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"აუდიო არ არის ხელმისაწვდომი"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"კონფიგურაცია"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"ხმოვან ფოსტასთან დაკავშირება"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>-ში"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"ნომრის შერჩევა"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"ნომრის შერჩევა"</string>
- <string name="make_primary" msgid="5829291915305113983">"დაიმახსოვრე ეს არჩევანი"</string>
- <string name="description_search_button" msgid="3660807558587384889">"ძიება"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"დარეკვა"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"ასაკრეფი ნომერი"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"დაკვრის შეჩერება ან გაშვება"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"სპიკერები შეგიძლიათ ჩართოთ და გამორთოთ."</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"დაკვრის პოზიციის მოძებნა"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"დაკვრის კოეფიციენტის შემცირება"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"დაკვრის კოეფიციენტის გაზრდა"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"საუბრის ისტორია"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"სხვა პარამეტრები"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"ციფერბლატი"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"მხოლოდ გამავალის ჩვენება"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"მხოლოდ შემომავალის ჩვენება"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"მხოლოდ გამოტოვებულის ჩვენება"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"მხოლოდ ხმოვანი ფოსტის ჩვენება"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"ყველა ზარის ჩვენება"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"ორწამიანი პაუზის დამატება"</string>
- <string name="add_wait" msgid="3360818652790319634">"ლოდინის დამატება"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"პარამეტრები"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"ახალი კონტაქტი"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"ყველა კონტაქტი"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"ზარის დეტალები"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"დეტალები არ არის ხელმისაწვდომი"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"ტონალური კლავიატურის გამოყენება"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"მიმდინარე ზარზე დაბრუნება"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"ზარის დამატება"</string>
- <string name="type_incoming" msgid="6502076603836088532">"შემომავალი ზარი"</string>
- <string name="type_outgoing" msgid="343108709599392641">"გამავალი ზარი"</string>
- <string name="type_missed" msgid="2720502601640509542">"გამოტოვებული ზარი"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"შემომავალი ვიდეოზარი"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"გამავალი ვიდეოზარი"</string>
- <string name="type_missed_video" msgid="954396897034220545">"გამოტოვებული ვიდეოზარი"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"ხმოვანი ფოსტა"</string>
- <string name="type_rejected" msgid="7783201828312472691">"უარყოფილი ზარი"</string>
- <string name="type_blocked" msgid="3521686227115330015">"დაბლოკილი ზარი"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"შემომავალი ზარები"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"ხმოვანი ფოსტის ჩართვა"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"კონტაქტის <xliff:g id="NAME">%1$s</xliff:g> ნახვა"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g>-თან დარეკვა"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g>-ის კონტაქტის დეტალები"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> ზარი."</string>
- <string name="description_video_call" msgid="2933838090743214204">"ვიდეოზარი."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"გააგზავნეთ SMS <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"მოსასმენი ხმოვანი ფოსტა"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"ხმოვანი ძიების დაწყება"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"დარეკვა <xliff:g id="NUMBER">%s</xliff:g>-ზე"</string>
- <string name="unknown" msgid="740067747858270469">"უცნობი"</string>
- <string name="voicemail" msgid="3851469869202611441">"ხმოვანი ფოსტა"</string>
- <string name="private_num" msgid="6374339738119166953">"დაფარული ნომერი"</string>
- <string name="payphone" msgid="7726415831153618726">"გადახდის ტელეფონი"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> წმ"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> მინ <xliff:g id="SECONDS">%s</xliff:g> წამ"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"ვერ ხორციელდება ამ ნომერზე დარეკვა"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"ხმოვანი ფოსტის დასაყენებლად გადადით მენიუს &gt; პარამეტრებზე."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"ხმოვან ფოსტასთან დასაკავშირებლად პირველ რიგში გამორთეთ თვითმფრინავის რეჟიმი."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"იტვირთება…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"იტვირთება SIM ბარათიდან…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM ბარათის კონტაქტები"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"კონტაქტების აპლიკაცია არ არის ხელმისაწვდომი"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"ხმოვანი ძიება არ არის ხელმისაწვდომი"</string>
- <string name="call_not_available" msgid="8941576511946492225">"სატელეფონო ზარის განხორციელება ვერ ხერხდება, ვინაიდან ტელეფონის აპლიკაცია გაუქმებულია."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"ამ მოწყობილობაზე არ არის შესაბამისი აპლიკაცია"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"კონტაქტების ძიება"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"დაამატეთ ნომერი ან მოიძიეთ კონტაქტებიდან"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"თქვენი საუბრის ისტორია ცარიელია"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"დარეკვა"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"გამოტოვებული ზარები არ გაქვთ."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"თქვენი ხმოვანი ელფოსტის შემოსულები ცარიელია."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"თქვენი ხმოვანი ფოსტის არქივი ცარიელია."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"მხოლოდ რჩეულების ჩვენება"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"საუბრის ისტორია"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"ხმოვანი ფოსტის არქივი"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"ყველა"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"გამოტოვებული"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"ხმოვანი ფოსტა"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"დაბლოკვის ახალი, მარტივი ხერხი"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"თქვენი უკეთ დაცვის მიზნით, ტელეფონმა დაბლოკვის წესი უნდა შეცვალოს. თქვენ მიერ დაბლოკილი ნომრებიდან როგორც ზარები, ისე ტექსტური შეტყობინებები შეწყდება და შესაძლოა მოხდეს ამ ნომრების სხვა აპებთან გაზიარება."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"დაშვება"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"გსურთ, დაბლოკოთ <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"ამ ნომრიდან შემოსული ზარები დაიბლოკება, ხოლო ხმოვანი ფოსტა ავტომატურად წაიშლება."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"ამ ნომრიდან შემოსული ზარები დაიბლოკება, თუმცა აბონენტს მაინც შეეძლება თქვენთვის ხმოვანი ფოსტის დატოვება."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"ამ ნომრიდან ზარებს ან ტექსტურ შეტყობინებებს აღარ მიიღებთ."</string>
- <string name="block_number_ok" msgid="770551992296781873">"დაბლოკვა"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"გსურთ, განბლოკოთ <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"განბლოკვა"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"სწრაფი დარეკვა"</string>
- <string name="tab_history" msgid="2563144697322434940">"საუბრის ისტორია"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"კონტაქტები"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"ხმოვანი ფოსტა"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"წაიშალა რჩეულებიდან"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"დაბრუნება"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"დარეკვა <xliff:g id="NUMBER">%s</xliff:g>-ზე"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"ახალი კონტაქტის შექმნა"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"კონტაქტისადმი დამატება"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS-ის გაგზავნა"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"ვიდეოზარის განხორციელება"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"ნომრის დაბლოკვა"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> ახალი გაცდენილი ზარი"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"სწრაფი აკრეფისთვის რჩეულები ჯერ არ გყავთ"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"რჩეული კონტაქტის დამატება"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"კონტაქტები ჯერ არ გაქვთ"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"კონტაქტის დამატება"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"ყველა ნომრის სანახავად შეეხეთ სურათს ან და გეჭიროთ მასზე ხელახალი შეკვეთისათვის"</string>
- <string name="remove_contact" msgid="1080555335283662961">"ამოშლა"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"ვიდეო ზარი"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"შეტყობინების გაგზავნა"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"ზარის მონაცემები"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-თან დარეკვა"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"გამოტოვებული ზარი აბონენტისგან: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"უპასუხო ზარი აბონენტისგან <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"წაუკითხავი ხმოვანი ფოსტა აბონენტისგან: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"ხმოვანი ფოსტა აბონენტისგან: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"ზარი აბონენტთან <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g>-ზე"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g>-ის მეშვეობით"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g>-ის მეშვეობით"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>-ზე, <xliff:g id="NUMBER">%2$s</xliff:g>-ის მეშვეობით"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, <xliff:g id="NUMBER">%2$s</xliff:g>-ის მეშვეობით"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"დარეკვა"</string>
- <string name="description_call_action" msgid="4000549004089776147">"დარეკვა <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"ვიდეო ზარი <xliff:g id="NAMEORNUMBER">^1</xliff:g>-თან."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-ის ხმოვანი ფოსტის მოსმენა"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-გან ხმოვანი ფოსტის მოსმენა"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-გან ხმოვანი ფოსტის დაპაუზება"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-გან ხმოვანი ფოსტის წაშლა"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> ახალი ხმოვანი ფოსტა</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> ახალი ხმოვანი ფოსტა</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-ისთვის კონტაქტის შექმნა"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-ის დამატება არსებულ კონტაქტზე"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ზარის დეტალები"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"ზარის ისტორიიდან წაშლილი"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"დღეს"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"გუშინ"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"უფრო ძველი"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"ზარების სია"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"სპიკერის ჩართვა."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"სპიკერის გამორთვა."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"დაკვრის აჩქარება."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"დაკვრის შენელება."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"დაკვრის დაწყება ან პაუზა."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"ეკრანის პარამეტრები"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"ხმა და ვიბრაცია"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"მარტივი წვდომა"</string>
- <string name="ringtone_title" msgid="760362035635084653">"ტელეფონის ზარი"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"ზარებზე ასევე ვიბრირება"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"ციფერბლატის ტონები"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"ციფერბლატის ტონის ხანგრძლივობა"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"ჩვეულებრივი"</item>
- <item msgid="6177579030803486015">"გრძელი"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"სწრაფი პასუხი"</string>
- <string name="call_settings_label" msgid="313434211353070209">"ზარები"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"ზარების დაბლოკვა"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"ზარების დაბლოკვა დროებით გამოირთო"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"ზარების დაბლოკვა გაითიშა, რადგან ბოლო 48 საათში ამ ტელეფონიდან საგანგებო სამსახურებს დაუკავშირდით. 48 საათის გასვლის შემდეგ ის ავტომატურად ჩაირთვება."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"ნომრების იმპორტი"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"სხვა აპების მეშვეობით თქვენ მიერ ადრე ზოგიერთი აბონენტის ხმოვან ფოსტაზე ავტომატური გადამისამართება მოინიშნა."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"ნომრების ნახვა"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"იმპორტი"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"იმპორტი ვერ მოხერხდა"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"ხმოვანი ფოსტის დაარქივება ვერ მოხერხდა."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"ნომრის განბლოკვა"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"ნომრის დამატება"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"ამ ნომრებიდან შემოსული ზარები დაიბლოკება, ხოლო ხმოვანი ფოსტა ავტომატურად წაიშლება."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"ამ ნომრებიდან შემოსული ზარები დაიბლოკება, თუმცა აბონენტებს მაინც შეეძლებათ თქვენთვის ხმოვანი ფოსტის დატოვება."</string>
- <string name="block_list" msgid="7760188925338078011">"დაბლოკილი ნომრები"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> არასწორია."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> უკვე დაბლოკილია."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"ზარების დაბლოკვა გაითიშა 48 საათით"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"გათიშულია, რადგან განხორციელდა საგანგებო ზარი."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"ანგარიშების გამოძახება"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"ჩართვა"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"ნებართვების დაყენება"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"სწრაფი აკრეფის გასააქტიურებლად, ჩართეთ კონტაქტების ნებართვა."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"ზარების ჟურნალის სანახავად, ჩართეთ ტელეფონის ნებართვა."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"კონტაქტების სანახავად, ჩართეთ კონტაქტების ნებართვა."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"ხმოვან ფოსტაზე წვდომისთვის, ჩართეთ ტელეფონის ნებართვა."</string>
- <string name="permission_no_search" msgid="84152933267902056">"თქვენი კონტაქტების მოსაძებნად ჩართეთ კონტაქტების ნებართვები."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"ზარის განსახორციელებლად, ჩართეთ ტელეფონის ნებართვა."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"ტელეფონის აპს სისტემის პარამეტრებში ჩაწერის ნებართვა არ აქვს."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"დაბლოკილი"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> აქტიურია"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"დაბლოკვა/სპამის შეტყობინება"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"დაბლოკვა"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"არ არის სპამი"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"განბლოკვა"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"სპამი"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"გსურთ, დაბლოკოთ <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"ამ ნომრიდან დაიბლოკება მომავალი ზარები და ხმოვანი ფოსტა."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"ზარზე სპამის შეტყ. გაგზავნა"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"ამ ნომრიდან დაიბლოკება მომავალი ზარები და ხმოვანი ფოსტა. ამ ზარზე სპამის შეტყობინება გაიგზავნება."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"გსურთ, განბლოკოთ <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"ეს ნომ.განიბლოკ. და სპამ.გაუქმ.შეტყ. გაიგზავნება. მომავ.ზარები/ხმ.ფოსტა ამ ნომრიდან სპამად არ ჩაითვ."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"გსურთ, შეიტანოთ <xliff:g id="NUMBER">%1$s</xliff:g> ნებადართული ერთეულების სიაში?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"ნებადართული ერთეულების სია"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"მომავ.ზარები/ხმ.ფოსტა ამ ნომრიდან სპამად არ ჩაითვლება. ამ ნომ. შესახებ სპამ.გაუქმ.შეტყ. გაიგზავნება."</string>
-</resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
deleted file mode 100644
index f6f8e766c..000000000
--- a/res/values-kk/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Телефон"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Телефон"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Телефон теру тақтасы"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Телефон"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Қоңыраулар тарихы"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Қате нөмір туралы есеп беру"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Нөмірді көшіру"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Транскрипцияны көшіру"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Нөмірді бөгеу"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> бөгелген"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Нөмірге рұқсат беру"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> бөгеуден шығарылған"</string>
- <string name="block_number_undo" msgid="591338370336724156">"КЕРІ ОРЫНДАУ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Жою"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Қоңырау алдында нөмірді өзгерту"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Қоңыраулар тарихын тазалау"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Дауыстық хабарды жою"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Дауыстық поштаны мұрағаттау"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Дауыстық хабармен бөлісу"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Дауыстық хабар жойылды"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Дауыстық пошта мұрағатталды"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"КЕРІ ҚАЙТАРУ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"МҰР-ҚА ӨТУ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Қоңыраулар тарихын тазалау керек пе?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Тарихтағы барлық қоңыраулар жойылады"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Қоңыраулар тарихы тазалануда…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Телефон"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Өткізіп алған қоңырау"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Өткізіп алынған жұмыс қоңырауы"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Қабылданбаған қоңыраулар"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> өткізіп алған қоңырау"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Кері қоңырау шалу"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Хабар"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> дауыстық хабар </item>
- <item quantity="one">Дауыстық хабар</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Ойнау"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> жіберген жаңа дауыс-хабар"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Дауыстық хабарды ойнату мүмкін болмады"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Дауыстық хабар жүктелуде…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Дауыстық пошта мұрағатталуда…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Дауыстық хабарды жүктеу мүмкін болмады"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Дауыс хабары бар қоңыраулар ғана"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Келген қоңыраулар ғана"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Шығыс қоңыраулары ғана"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Қабылданбаған қоңыраулар ғана"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Визуалды дауыстық хабар"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Нөмірге қоңырау шалмастан, дауыстық поштаңызды қараңыз және тыңдаңыз. Деректер ақысы алынуы мүмкін."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Параметрлер"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Дауыстық пошта жаңартулары қол жетімді емес"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Жаңа дауыстық хабар күтуде. Дәл қазір жүктеу мүмкін емес."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Дауыс-хабарды реттеу"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Аудио қол жетімді емес"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Орнату"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Дауыс-хабарға қоңырау шалу"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Нөмірді таңдау"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Нөмірді таңдау"</string>
- <string name="make_primary" msgid="5829291915305113983">"Бұл таңдау есте сақталсын."</string>
- <string name="description_search_button" msgid="3660807558587384889">"іздеу"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"теру"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"теретін нөмір"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Ойнату немесе ойнатуды тоқтату"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Спикерфонды қосу немесе өшіру"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Ойнату орнын іздеу"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Ойнату жылдамдығын азайту"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Ойнату жылдамдығын арттыру"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Қоңырау тарихы"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Басқа опциялар"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"теру тақтасы"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Шығыс қоңырауларды ғана көрсету"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Келгендерді ғана көрсету"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Қабылданбағандарды ғана көрсету"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Дауыс-хабарларын ғана көрсету"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Барлық қоңырауларды көрсету"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2 сек үзіліс қосу"</string>
- <string name="add_wait" msgid="3360818652790319634">"Күтуді қосу"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Параметрлер"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Жаңа контакт"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Барлық контактілер"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Қоңырау деректері"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Мәліметтер қол жетімді емес"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Сенсорлы әуенді пернетақта"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Қосылып тұрған қоңырауға оралу"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Қоңырау қосу"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Келген қоңырау"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Шығыс қоңырау"</string>
- <string name="type_missed" msgid="2720502601640509542">"Қабылданбаған қоңырау"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Кіріс бейне қоңырау"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Шығыс бейне қоңырау"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Өткізіп алынған бейне қоңырау"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Дауыстық пошта"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Қабылданбаған қоңырау"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Тыйым салынған қоңырау"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Келген қоңыраулар"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Дауыс-хабарды ойнату"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> контактісін көру"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g> нөміріне қоңырау шалу"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> контакт деректері"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> қоңыраулар."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Бейне қоңырау."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Мынаған SMS жіберу: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Естілмеген дауыс-хабар"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Дауыс іздеуді бастау"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> нөміріне қоңырау шалу"</string>
- <string name="unknown" msgid="740067747858270469">"Белгісіз"</string>
- <string name="voicemail" msgid="3851469869202611441">"Дауыстық пошта"</string>
- <string name="private_num" msgid="6374339738119166953">"Жеке нөмір"</string>
- <string name="payphone" msgid="7726415831153618726">"Автомат-телефон"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> сек."</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> мин <xliff:g id="SECONDS">%s</xliff:g> сек"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Бұл нөмірге қоңырау шалу мүмкін емес"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Дауыс-хабарын жасақтау үшін Mәзір &gt; Параметрлер тармақтарына кіріңіз."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Дауыс-хабарына қоңырау шалу үшін ұшақ режимін өшіру қажет."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Жүктелуде..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI (Халықаралық мобильдік құрылғы анықтағышы)"</string>
- <string name="meid" msgid="6210568493746275750">"MEID (ұялы жабдық анықтағыш)"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"SIM картасынан жүктеу…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM картасының контактілері"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Контактілер қолданбасы қол жетімді емес"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Дауыс арқылы іздеу қол жетімді емес"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Телефон қоңырауын шалу мүмкін емес, өйткені «Телефон» қолданбасы өшірілген."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Бұл үшін осы құрылғыда қолданба жоқ"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Контактілерді іздеу"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Нөмірді енгізіңіз немесе контактілерден іздеп табыңыз"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Қоңыраулар тарихы бос"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Қоңырау шалу"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Қабылданбаған қоңыраулар жоқ."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Дауыстық поштаның \"Кіріс\" қалтасы бос."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Дауыстық пошта мұрағатыңыз бос."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Сүйіктілерді ғана көрсету"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Қоңыраулар тарихы"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Дауыстық пошта мұрағаты"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Барлық"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Қабылданбаған"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Дауыстық хабар"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Жаңа, қарапайым бөгеу"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Қорғанысыңызды арттыру үшін телефон бөгеудің қолданылу жолын өзгертуі керек. Бөгелген нөмірлер енді қоңырауларды да, мәтіндік хабарларды да тоқтатып, басқа қолданбаларда көрсетілуі мүмкін."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Рұқсат беру"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> бөгеу керек пе?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Осы нөмірден болашақ қоңыраулар бөгеледі және дауыстық хабарлар автоматты түрде жойылады."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Осы нөмірден қоңыраулар бөгеледі, бірақ қоңырау шалушы әлі де сізге дауыстық хабарлар қалдыра алуы мүмкін."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Бұдан былай осы нөмірден қоңыраулар немесе мәтіндік хабарлар алмайсыз."</string>
- <string name="block_number_ok" msgid="770551992296781873">"БӨГЕУ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> бөгеуден шығару керек пе?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"БӨГЕУДЕН ШЫҒАРУ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Жылдам теру"</string>
- <string name="tab_history" msgid="2563144697322434940">"Қоңыраулар тарихы"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Контактілер"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Дауыстық хабар"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Сүйіктілерден алынған"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Кері орындау"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> нөміріне қоңырау шалу"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Жаңа контакт жасау"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Контактіге қосу"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS жіберу"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Бейне қоңырау шалу"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Нөмірді бөгеу"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> жаңа қабылданбаған қоңыраулар"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Жылдам теруде әлі ешкім жоқ"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Таңдаулыны қосу"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Әлі ешқандай контактілер жоқ"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Контакт қосу"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Барлық нөмірлерді көру үшін түртіңіз немесе ретін өзгерту үшін түртіп, ұстап тұрыңыз"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Алып тастау"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Бейне қоңырау"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Хабар жіберу"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Қоңырау мәліметтері"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> нөміріне қоңырау шалу"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> қоңырауы өткізіп алынды, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> қоңырауына жауап берілді, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Оқылмаған дауыстық хабар: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Дауыстық хабар: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> нөміріне қоңырау шалу, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> ішінде"</string>
- <string name="description_via_number" msgid="3503311803959108316">"Нөмірі: <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"Нөмірі: <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>. Нөмірі: <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>. Нөмірі: <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Қоңырау шалу"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> нөміріне қоңырау шалу"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> бейне қоңырау шалу."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> дауыстық хабарын тыңдау"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> дауыстық хабары ойнатылады"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> дауыстық хабары кідіртіледі"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> жіберген дауыстық хабар жойылады"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> жаңа дауыс поштасы</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> жаңа дауыс поштасы</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> үшін контакт жасау"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> бар контактіге қосу"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> үшін қоңырау мәліметтері"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Қоңыраулар тарихынан жойылды"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Бүгін"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Кеше"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Ескілеу"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Қоңыраулар тізімі"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Динамикті қосу."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Динамикті өшіру."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Тезірек ойнату."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Баяуырақ ойнату."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Ойнатуды бастау немесе кідірту."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Көрсету опциялары"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Дыбыстар мен діріл"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Арнайы мүмкіндіктер"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Телефон қоңырау әуені"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Қоңырау кезінде дірілдету"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Теру тақтасының үндері"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Теру тақтасы дыбысының ұзындығы"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Қалыпты"</item>
- <item msgid="6177579030803486015">"Ұзақ"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Жылдам жауаптар"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Қоңыраулар"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Қоңырауларға тыйым салу"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Қоңырауларға тыйым салу уақытша өшірулі"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Қоңырауларға тыйым салу өшірілді, өйткені сіз соңғы 48 сағат ішінде осы телефоннан төтенше қызметтерге хабарластыңыз. Ол 48 сағаттық кезең өткеннен кейін автоматты түрде қайта қосылады."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Нөмірлерді импорттау"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Сіз бұрын кейбір қоңырау шалушыларды басқа қолданбалар арқылы дауыс поштасына автоматты түрде жіберу үшін белгілеген."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Нөмірлерді көру"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Импорттау"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Импорттау сәтсіз аяқталды"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Дауыстық пошта мұрағатталған жоқ."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Нөмірді бөгеуден шығару"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Нөмір қосу"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Осы нөмірлерден қоңыраулар бөгеледі және дауыстық хабарлар автоматты түрде жойылады."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Осы нөмірлерден қоңыраулар бөгеледі, бірақ олар әлі де сізге дауыстық хабарлар қалдыра алуы мүмкін."</string>
- <string name="block_list" msgid="7760188925338078011">"Бөгелген нөмірлер"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> жарамсыз."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> бөгеліп қойылған."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Қоңырауларға тыйым салу 48 сағат бойы өшірілген"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Өшірілген, өйткені төтенше қоңырау соғылған."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Қоңырау шалу есептік жазбалары"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Қосу"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Рұқсаттар орнату"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Жылдам теруді қосу үшін \"Контактілер\" рұқсатын қосыңыз."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Қоңыраулар журналы көру үшін \"Телефон\" рұқсатын қосыңыз."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Контактілерді көру үшін \"Контактілер\" рұқсатын қосыңыз."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Дауыс поштасына қатынасу үшін \"Телефон\" рұқсатын қосыңыз."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Контактілерді іздеу үшін \"Контактілер\" рұқсаттарын қосыңыз."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Қоңырауды шалу үшін \"Телефон\" рұқсатын қосыңыз."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Телефон қолданбасында жүйелік параметрлерге жазуға рұқсат жоқ."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Бөгелген"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> белсенді"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Бөгеу/спам туралы есеп беру"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Бөгеу"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Спам емес"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Бөгеуді алу"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Спам"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> бөгеу керек пе?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Осы нөмірден келетін болашақ қоңыраулар және дауыстық хабарлар бөгеледі."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Спам деп есеп беру"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Осы нөмірден келетін қоңыраулар мен дауыстық хабарлар бөгеледі. Бұл қоңырау спам деп есеп беріледі."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> бөгеуден шығару керек пе?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Нөмір бөгеуден алынып, спам емес деп белгіленді. Қоңырау мен дауыстық пошта спам деп анықталмайды."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> нөмірін ақ тізімге қосу керек пе?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Ақ тізімге қосу"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Келетін қоңыраулар мен дауыстық пошта спам деп анықталмайды. Нөмір спам емес деп белгіленді."</string>
-</resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
deleted file mode 100644
index b7cac1e05..000000000
--- a/res/values-km/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"ទូរស័ព្ទ"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"ទូរសព្ទ"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"បន្ទះលេខទូរស័ព្ទ"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"ទូរស័ព្ទ"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"ប្រវត្តិ​ហៅ"</string>
- <string name="action_report_number" msgid="4635403959812186162">"រាយការណ៍ពីលេខដែលមិនត្រឹមត្រូវ"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"ថតចម្លងលេខទូរស័ព្ទ"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"ថតចម្លងសំណៅពីសារសម្លេង"</string>
- <string name="action_block_number" msgid="1482657602262262134">"រារាំងលេខ"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"បានរារាំង <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"ឈប់រារាំងលេខ"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"បានឈប់រារាំង <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="block_number_undo" msgid="591338370336724156">"មិនធ្វើវិញ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"លុប"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"កែ​លេខ​មុន​ពេល​ហៅ"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"សម្អាត​ប្រវត្តិហៅ"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"លុប​សារ​ជា​សំឡេង"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"រក្សាទុក​សារ​ជាសំឡេង​ក្នុង​ប័ណ្ណសារ"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"ចែករំលែក​សារ​ជា​សំឡេង"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"បានលុបសារសម្លេង"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"បាន​រក្សាទុក​សារ​ជាសំឡេង​ក្នុង​ប័ណ្ណសារ"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"​មិន​ធ្វើវិញ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ទៅ​កាន់​ប័ណ្ណសារ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"សម្អាតប្រវត្តិហៅ?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"វានឹងលុបការហៅទាំងអស់ចេញពីប្រវត្តិរបស់អ្នក"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"កំពុងជម្រះប្រវត្តិហៅ…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"ទូរស័ព្ទ"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"ខកខាន​ទទួល"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"បានខកខានការហៅចូលពីកន្លែងការងារ"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"ខកខាន​ទទួល"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"ខកខានទទួល <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ដង"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"ហៅ​ទៅ​វិញ"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"សារ"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other">សារជាសម្លេង <xliff:g id="COUNT">%1$d</xliff:g> </item>
- <item quantity="one">សារជាសម្លេង</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"ចាក់"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"សារ​ជា​សំឡេង​ថ្មី​ពី <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"មិនអាចចាក់សារជាសម្លេងបានទេ"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"កំពុងផ្ទុកសារជាសម្លេង…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"កំពុង​​រក្សាទុក​សារ​ជាសំឡេង​ក្នុង​ប័ណ្ណសារ..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"មិនអាចផ្ទុកសារជាសម្លេងបានទេ"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"តែ​ការ​ហៅ​ជា​សារ​សំឡេង"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"តែ​ការ​ហៅ​ចូល"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"តែ​ការ​ហៅ​ចេញ​ប៉ុណ្ណោះ"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"តែ​លេខ​ខកខាន​ទទួល"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"សារជាសំឡេងអាចមើលឃើញ"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"មើល និងស្តាប់សារជាសំឡេងរបស់អ្នក ដោយមិនចាំបាច់ហៅទៅលេខណាមួយឡើយ។ អាចគិតប្រាក់លើការប្រើប្រាស់ទិន្នន័យ។"</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"ការកំណត់"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"មិនមានការអាប់ដេតសារជាសម្លេងទេ"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"សារជាសម្លេងថ្មីកំពុងរង់ចាំ។ មិនអាចផ្ទុកឥឡូវនេះបានទេ។"</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"ដំឡើងសារជាសម្លេងរបស់អ្នក"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"មិនមានសម្លេងទេ"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"រៀបចំ"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"ហៅ​ជា​សារ​សំឡេង"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"ជ្រើស​លេខ"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"ជ្រើស​លេខ"</string>
- <string name="make_primary" msgid="5829291915305113983">"ចងចាំ​ជម្រើស​នេះ"</string>
- <string name="description_search_button" msgid="3660807558587384889">"ស្វែងរក"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"dial"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"លេខ​ដែល​ត្រូវ​ហៅ"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"ចាក់ ឬ​បញ្ឈប់​ការ​ចាក់​ឡើងវិញ"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"បិទ ឬ​បើក​អូប៉ាល័រទូរស័ព្ទ"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"រកមើល​ទីតាំង​ចាក់​ឡើងវិញ"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"បន្ថយ​អត្រា​ចាក់​ឡើង​វិញ"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"បង្កើន​អត្រា​ចាក់​ឡើងវិញ"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"ប្រវត្តិ​ហៅ"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"ជម្រើស​បន្ថែម"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"បន្ទះ​លេខ"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"បង្ហាញ​តែ​ការ​ហៅ​ចេញ"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"បង្ហាញ​តែ​ការ​ហៅ​ចូល"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"បង្ហាញ​តែ​ការ​ខកខាន​ទទួល"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"បង្ហាញ​តែ​សារ​ជា​សំឡេង"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"បង្ហាញ​ការ​ហៅ​ទាំងអស់"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"បន្ថែម​ការ​ផ្អាក ២វិ."</string>
- <string name="add_wait" msgid="3360818652790319634">"បញ្ចូល​ការ​រង់ចាំ"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"ការកំណត់"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"ទំនាក់ទំនង​ថ្មី"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"ទំនាក់ទំនង​ទាំងអស់"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"ព័ត៌មាន​លម្អិត​អំពីការ​ហៅ"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"មិនមានព័ត៌មានលំអិតទេ"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"ប្រើ​សំឡេង​ប៉ះ​បន្ទះ​លេខ"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"កំពុង​ត្រឡប់​ទៅកាន់​ការ​ហៅ"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"បន្ថែម​ការ​ហៅ"</string>
- <string name="type_incoming" msgid="6502076603836088532">"ការ​ហៅ​ចូល"</string>
- <string name="type_outgoing" msgid="343108709599392641">"ការ​ហៅ​ចេញ"</string>
- <string name="type_missed" msgid="2720502601640509542">"ខកខាន​ទទួល"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"ការ​ហៅចូលជា​វីដេអូ​"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"ការ​ហៅ​ជា​វីដេអូ​ចេញ"</string>
- <string name="type_missed_video" msgid="954396897034220545">"ខកខាន​ទទួល​ការ​ហៅ​ជា​វីដេអូ"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"សារ​ជា​សំឡេង"</string>
- <string name="type_rejected" msgid="7783201828312472691">"ការហៅដែលបានបដិសេធ"</string>
- <string name="type_blocked" msgid="3521686227115330015">"ការហៅដែលបានរារាំង"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"ការ​ហៅ​ចូល"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"ចាក់​សារ​ជា​សំឡេង"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"មើល​ទំ​នាក់ទំនង <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"ហៅ <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"ព័ត៌មាន​លម្អិត​ទំនាក់ទំនង​សម្រាប់ <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"ការ​ហៅ <xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> ។"</string>
- <string name="description_video_call" msgid="2933838090743214204">"ការ​ហៅ​ជា​វីដេអូ​។"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"ផ្ញើសារ SMS ទៅ <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"សារ​ជា​សំឡេង​ដែល​មិន​បាន​ឮ"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"ចាប់ផ្ដើម​ស្វែងរក​ជា​សំឡេង"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"ហៅ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"មិន​ស្គាល់"</string>
- <string name="voicemail" msgid="3851469869202611441">"សារ​ជា​សំឡេង"</string>
- <string name="private_num" msgid="6374339738119166953">"លេខ​ឯកជន"</string>
- <string name="payphone" msgid="7726415831153618726">"ទូរស័ព្ទសាធារណៈ"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> វិនាទី"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> នាទី <xliff:g id="SECONDS">%s</xliff:g> វិនាទី"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> នៅម៉ោង <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"មិនអាចហៅលេខនេះបានទេ"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"ដើម្បី​កំណត់​សារ​ជា​សំឡេង ចូល​ម៉ឺនុយ &gt; ការ​កំណត់។"</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"ដើម្បី​ហៅ​សារ​ជា​សំឡេង ដំបូង​ត្រូវ​បិទ​របៀប​ពេល​ជិះ​យន្តហោះ។"</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"កំពុង​ផ្ទុក..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"កំពុង​ផ្ទុក​ពី​ស៊ីម​កាត..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"ទំនាក់ទំនង​នៅ​ក្នុង​ស៊ីម​កាត"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"មិនមានកម្មវិធីទំនាក់ទំនងទេ"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"ការស្វែងរកជាសម្លេងមិនមានទេ"</string>
- <string name="call_not_available" msgid="8941576511946492225">"មិន​អាច​ហៅ​បាន​ទេ​ ព្រោះ​កម្មវិធី​ទូរស័ព្ទ​ត្រូវ​បាន​បិទ។"</string>
- <string name="activity_not_available" msgid="2287665636817987623">"មិនមានកម្មវិធីសម្រាប់សកម្មភាពនេះនៅលើឧបករណ៍នេះទេ"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"ស្វែងរកទំនាក់ទំនង"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"បន្ថែមលេខទូរស័ព្ទ ឬស្វែងរកទំនាក់ទំនង"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"ប្រវត្តិហៅរបស់អ្នកទទេ"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"ហៅទូរស័ព្ទ"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"អ្នកមិនមានការខកខានទទួលទូរស័ព្ទទេ។"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"ប្រអប់ទទួលសារជាសំឡេងរបស់អ្នកទទេ។"</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"បណ្ណសារជាសំឡេងរបស់អ្នកគឺទទេ។"</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"បង្ហាញ​តែ​​និយម​ប្រើ​ប៉ុណ្ណោះ"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"ប្រវត្តិហៅ"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"បណ្ណសារសារជាសំឡេង"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"ទាំង​អស់"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"ខកខាន​ទទួល"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"សារ​ជា​សំឡេង"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"ថ្មី ការ​ទប់ស្កាត់​ដែល​ងាយស្រួល"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"ដើម្បីការពារអ្នកឲ្យបានប្រសើរជាងមុន ទូរសព្ទ​ត្រូវធ្វើការផ្លាស់ប្តូររបៀប​​​ដែល​ការ​ទប់ស្កាត់​ដំណើរការ។ លេខដែល​បាន​ទប់ស្កាត់​របស់អ្នក​ឥឡូវ​នឹងបញ្ឈប់​ទាំង​ការហៅ និងការផ្ញើសារ និងអាច​ត្រូវបានចែករំលែកជាមួយកម្មវិធីផ្សេងទៀត។"</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"អនុញ្ញាត"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"រារាំង <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"ការហៅចេញពីលេខនេះនឹងត្រូវបានរារាំង ហើយសារជាសំឡេងនឹងត្រូវបានលុបដោយស្វ័យប្រវត្តិ។"</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"ការហៅចេញពីលេខនេះនឹងត្រូវបានរារាំង ប៉ុន្តែអ្នកហៅនៅតែអាចទុកសារជាសំឡេងឲ្យអ្នកបាន។"</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"អ្នក​នឹង​លែង​ទទួល​​ការ​ហៅ ឬ​​សារ​​អត្ថបទ​ពី​លេខ​នេះ​ទៀត​ហើយ។"</string>
- <string name="block_number_ok" msgid="770551992296781873">"រារាំង"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"ឈប់រារាំង <xliff:g id="NUMBER">%1$s</xliff:g> ឬ?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ឈប់រារាំង"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"ហៅទូរស័ព្ទល្បឿនលឿន"</string>
- <string name="tab_history" msgid="2563144697322434940">"ប្រវត្តិហៅ"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"ទំនាក់ទំនង"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"សារជាសំឡេង"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"បាន​លុប​ចេញពី​ទំនាក់ទំនង​ដែល​និយម​ប្រើ"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"មិនធ្វើវិញ"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"ហៅ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"បង្កើតទំនាក់ទំនងថ្មី"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"បញ្ចូល​ទៅទំនាក់ទំនង"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"ផ្ញើសារ SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"ការ​ហៅ​ជា​វីដេអូ"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"រារាំងលេខ"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"ខកខាន​ទទួល​ថ្មី <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"គ្មាននរណាម្នាក់នៅក្នុងការហៅរហ័សរបស់អ្នកនៅឡើយទេ"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"បន្ថែមសំណព្វ"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"អ្នកមិនទាន់មានទំនាក់ទំនងនៅឡើយទេ"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"បញ្ចូល​ទំនាក់ទំនង"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"ប៉ះរូបភាពដើម្បីមើលលេខទាំងអស់ ឬប៉ះ &amp; សង្កត់ឲ្យជាប់ដើម្បីតម្រៀបឡើងវិញ"</string>
- <string name="remove_contact" msgid="1080555335283662961">"លុបចេញ"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"ហៅជាវីដេអូ"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"ផ្ញើសារ"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"ព័ត៌មានលម្អិតអំពីការហៅ"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"ហៅ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"ខកខានទទួលកាហៅពី <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>។"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"បានឆ្លើយតបការហៅពី <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>។"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"សារជាសំឡេងដែលមិនទាន់អានពី <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"សារជាសំឡេងពី <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"ហៅទៅ <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>។"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"នៅ <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"តាមរយៈ <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"តាមរយៈ <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"នៅ​លើ <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, តាមរយៈ <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> តាមរយៈ <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"ហៅ"</string>
- <string name="description_call_action" msgid="4000549004089776147">"ហៅ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"ហៅទៅ <xliff:g id="NAMEORNUMBER">^1</xliff:g> ជាវីដេអូ។"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"ស្ដាប់​សារ​ជា​សំឡេង​ពី <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"ចាក់ការហៅជាសំឡេងពី <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"ផ្អាកសារជាសំឡេងពី <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"លុបសារជាសំឡេងពី <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other">សារជាសំឡេងថ្មី <xliff:g id="COUNT_1">%d</xliff:g></item>
- <item quantity="one">សារជាសំឡេងថ្មី <xliff:g id="COUNT_0">%d</xliff:g></item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"បង្កើតទំនាក់ទំនងសម្រាប់ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"បន្ថែម <xliff:g id="NAMEORNUMBER">^1</xliff:g> ទៅទំនាក់ទំនងដែលមានស្រាប់"</string>
- <string name="description_details_action" msgid="2433827152749491785">"ព័ត៌មាន​លម្អិត​អំពីការហៅ​សម្រាប់ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"បានលុបចេញពីប្រវត្តិហៅ"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"ថ្ងៃនេះ"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"ម្សិលមិញ"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"ចាស់ៗ"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"បញ្ជីការហៅ"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"បើក​អូប៉ាល័រ។"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"បិទ​អូប៉ាល័រ។"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"ចាក់​កាន់តែ​លឿន"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"ចាក់​កាន់តែ​យឺត។"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"ចាប់ផ្ដើម ឬ​ផ្អាក​ការ​ចាក់​ឡើងវិញ។"</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"ជម្រើសបង្ហាញ"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"សំឡេង និងរំញ័រ"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"លទ្ធភាពប្រើប្រាស់"</string>
- <string name="ringtone_title" msgid="760362035635084653">"សំឡេង​រោទ៍​ទូរស័ព្ទ"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"ញ័រ​សម្រាប់​ការ​ហៅ​ផងដែរ"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"សំឡេង​បន្ទះ​លេខ"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"រយៈពេលនៃសម្លេងបន្ទះលេខ"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"ធម្មតា"</item>
- <item msgid="6177579030803486015">"វែង"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"ឆ្លើយតប​រហ័ស"</string>
- <string name="call_settings_label" msgid="313434211353070209">"ការហៅ"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"ការរារាំងការហៅ"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"ការរារាំងការហៅត្រូវបានបិទជាបណ្តោះអាសន្ន"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"ការរារាំងការហៅត្រូវបានបិទដំណើរការ ដោយសារតែអ្នកបានទាក់ទងទៅសេវាអាសន្នចេញពីទូរស័ព្ទនេះក្នុងចន្លោះពេល 48 ម៉ោងកន្លងមកនេះ។ វានឹងបើកដំណើរការឡើងវិញ បន្ទាប់ពីរយៈពេល 48 ម៉ោងផុតកំណត់។"</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"នាំចូលលេខ"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"កាលពីមុនអ្នកបានដាក់សម្គាល់ឲ្យបញ្ជូនអ្នកហៅមួយចំនួនដោយស្វ័យប្រវត្តិទៅកាន់សារជាសំឡេងតាមរយៈកម្មវិធីផ្សេងទៀត។"</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"មើលលេខ"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"នាំចូល"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"ការនាំចូលបានបរាជ័យ"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"បាន​បរាជ័យ​រក្សាទុក​សារជាសំឡេង​ក្នុង​ប័ណ្ណសារ។"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"ឈប់ទប់ស្កាត់លេខ"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"បន្ថែមលេខ"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"ការហៅចេញពីលេខទាំងនេះនឹងត្រូវបានរារាំង ហើយសារជាសំឡេងនឹងត្រូវបានលុបដោយស្វ័យប្រវត្តិ។"</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"ការហៅចេញពីលេខទាំងនេះនឹងត្រូវបានរារាំង ប៉ុន្តែពួកគេនៅតែអាចទុកសារជាសំឡេងឲ្យអ្នកបាន។"</string>
- <string name="block_list" msgid="7760188925338078011">"លេខដែលបានរារាំង"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> មិនត្រឹមត្រូវទេ"</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> ត្រូវបានទប់ស្កាត់រួចហើយ"</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"ការរារាំងការហៅត្រូវបានបិទដំណើរការរយៈពេល 48 ម៉ោង"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"បានបិទដំណើរការពីព្រោះការហៅបន្ទាន់ត្រូវបានធ្វើឡើង"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"គណនីហៅទូរស័ព្ទ"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"បើក"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"កំណត់សិទ្ធិអនុញ្ញាត"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"ដើម្បីបើកដំណើរការហៅរហ័ស សូមបើកសិទ្ធិអនុញ្ញាតកម្មវិធីទំនាក់ទំនង។"</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"ដើម្បីមើលកំណត់ហេតុហៅទូរស័ព្ទរបស់អ្នក សូមបើកសិទ្ធិអនុញ្ញាតកម្មវិធីហៅទូរស័ព្ទ។"</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"ដើម្បីមើលទំនាក់ទំនងរបស់អ្នក សូមបើកសិទ្ធិអនុញ្ញាតកម្មវិធីទំនាក់ទំនង។"</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"ដើម្បីចូលដំណើរការសារជាសំឡេងរបស់អ្នក សូមបើកសិទ្ធិអនុញ្ញាតកម្មវិធីហៅទូរស័ព្ទ។"</string>
- <string name="permission_no_search" msgid="84152933267902056">"ដើម្បីស្វែងរកទំនាក់ទំនងរបស់អ្នក សូមបើកសិទ្ធិអនុញ្ញាតទំនាក់ទំនង។"</string>
- <string name="permission_place_call" msgid="2101287782615887547">"ដើម្បីធ្វើការហៅទូរស័ព្ទ សូមបើកសិទ្ធិអនុញ្ញាតកម្មវិធីហៅទូរស័ព្ទ។"</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"កម្មវិធីទូរស័ព្ទមិនមានសិទ្ធិអនុញ្ញាតដើម្បីសរសេរការកំណត់ប្រព័ន្ធទេ។"</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"បានរារាំង"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> គឺសកម្ម"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"រារាំង/រាយការណ៍សារដែលមិនចង់បាន"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"រារាំង"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"មិនមែន​សារ​ឥតបានការ"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"ឈប់​ទប់ស្កាត់"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"សារ​ឥតបានការ"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"ទប់ស្កាត់ <xliff:g id="NUMBER">%1$s</xliff:g> មែន​ទេ?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"ការ​ហៅ និង​សារ​ជា​សំឡេង​លើក​ក្រោយៗ​ពី​លេខ​នេះ​នឹង​ត្រូវ​ទប់ស្កាត់។"</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"រាយការណ៍​ការ​ហៅ​​ថា​ជា​សារ​ឥត​បាន​ការ"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"ការ​ហៅ និង​សារ​ជា​សំឡេង​លើក​ក្រោយ​ៗ​ពី​លេខ​នេះ​នឹង​ត្រូវ​ទប់ស្កាត់។ ការ​ហៅ​នេះ​នឹង​ត្រូវ​រាយការណ៍​ថា​ជា​សារ​ឥតបានការ។"</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"ឈប់​ទប់ស្កាត់ <xliff:g id="NUMBER">%1$s</xliff:g> មែន​ទេ?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"លេខ​នេះ​នឹង​មិន​ត្រូវ​បាន​ទប់ស្កាត់ និង​រាយការណ៍​​ថា​​មិន​មែន​ជា​សារ​ឥតបានការ។ ការ​ហៅ និង​សារ​ជា​​សំឡេង​លើក​ក្រោយៗ​នឹង​មិន​ត្រូវ​បាន​​កំណត់​​ថា​ជា​សារ​​ឥត​បាន​ការ​ទេ។"</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"ដាក់​បញ្ជី​អនុញ្ញាត <xliff:g id="NUMBER">%1$s</xliff:g> មែន​ទេ?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"បញ្ជី​​អនុញ្ញាត"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"ការ​ហៅ និង​សារ​ជា​សំឡេង​លើកក្រោយៗ​ពី​លេខ​នេះ​នឹង​មិន​កំណត់​ថា​ជា​សារ​ឥត​បាន​ការ​ទេ។ លេខ​នេះ​នឹង​ត្រូវ​រាយការណ៍​ថា​មិន​មែន​ជា​សារ​ឥតបានការ។"</string>
-</resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
deleted file mode 100644
index 262fea728..000000000
--- a/res/values-kn/strings.xml
+++ /dev/null
@@ -1,289 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"ಫೋನ್"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"ಫೋನ್"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"ಫೋನ್ ಡಯಲ್‌ಪ್ಯಾಡ್"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"ಫೋನ್"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"ಕರೆ ಇತಿಹಾಸ"</string>
- <string name="action_report_number" msgid="4635403959812186162">"ನಿಖರವಾಗಿಲ್ಲದ ಸಂಖ್ಯೆಯನ್ನು ವರದಿಮಾಡಿ"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"ಸಂಖ್ಯೆಯನ್ನು ನಕಲಿಸಿ"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"ಟ್ರಾನ್ಸ್‌ಕ್ರಿಪ್ಶನ್ ನಕಲಿಸಿ"</string>
- <string name="action_block_number" msgid="1482657602262262134">"ಸಂಖ್ಯೆಯನ್ನು ನಿರ್ಬಂಧಿಸು"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"ಸಂಖ್ಯೆಯನ್ನು ಅನಿರ್ಬಂಧಿಸು"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> ನಿರ್ಬಂಧ ತೆಗೆಯಲಾಗಿದೆ"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ರದ್ದುಮಾಡಿ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"ಅಳಿಸಿ"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"ಕರೆ ಮಾಡುವ ಮೊದಲು ಸಂಖ್ಯೆಯನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"ಕರೆ ಇತಿಹಾಸ ತೆರವುಗೊಳಿಸಿ"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"ಧ್ವನಿಮೇಲ್‌ ಅಳಿಸಿ"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"ಧ್ವನಿಮೇಲ್ ಆರ್ಕೈವ್ ಮಾಡಿ"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"ಧ್ವನಿಮೇಲ್‌ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಿ"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"ಧ್ವನಿಮೇಲ್ ಅಳಿಸಲಾಗಿದೆ"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"ಧ್ವನಿಮೇಲ್ ಆರ್ಕೈವ್ ಮಾಡಲಾಗಿದೆ"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ರದ್ದುಮಾಡಿ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ಆರ್ಕೈವ್‌ಗೆ ಹೋಗು"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"ಕರೆ ಇತಿಹಾಸವನ್ನು ತೆರವುಗೊಳಿಸುವುದೇ?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"ಇದು ನಿಮ್ಮ ಇತಿಹಾಸದಿಂದ ಎಲ್ಲಾ ಕರೆಗಳನ್ನು ಅಳಿಸುತ್ತದೆ"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"ಕರೆ ಇತಿಹಾಸವನ್ನು ತೆರವುಗೊಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"ಫೋನ್"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"ತಪ್ಪಿದ ಕರೆ"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"ತಪ್ಪಿದ ಕೆಲಸದ ಕರೆ"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"ತಪ್ಪಿದ ಕರೆಗಳು"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ತಪ್ಪಿದ ಕರೆಗಳು"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"ಮರಳಿ ಕರೆ"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"ಸಂದೇಶ"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> ಧ್ವನಿಮೇಲ್‌ಗಳು </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> ಧ್ವನಿಮೇಲ್‌ಗಳು </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"ಪ್ಲೇ ಮಾಡು"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> ಇವರಿಂದ ಹೊಸ ಧ್ವನಿಮೇಲ್‌"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"ಧ್ವನಿಮೇಲ್‌ ಪ್ಲೇ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"ಧ್ವನಿಮೇಲ್‌ ಲೋಡ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"ಧ್ವನಿಮೇಲ್ ಆರ್ಕೈವ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"ಧ್ವನಿಮೇಲ್‌ ಲೋಡ್‌ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"ಧ್ವನಿಮೇಲ್‌ ಕರೆಗಳು ಮಾತ್ರ"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"ಒಳಬರುವ ಕರೆಗಳು ಮಾತ್ರ"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"ಹೊರಹೋಗುವ ಕರೆಗಳು ಮಾತ್ರ"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"ತಪ್ಪಿದ ಕರೆಗಳು ಮಾತ್ರ"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"ದೃಶ್ಯ ಧ್ವನಿಮೇಲ್"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"ಸಂಖ್ಯೆಗೆ ಕರೆ ಮಾಡದೆಯೇ ನಿಮ್ಮ ಧ್ವನಿಮೇಲ್ ಅನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ಆಲಿಸಿ. ಡೇಟಾ ಶುಲ್ಕಗಳು ಅನ್ವಯಿಸಬಹುದು."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"ಧ್ವನಿಮೇಲ್‌ ಅಪ್‌ಡೇಟ್‌ಗಳು ಇದೀಗ ಲಭ್ಯವಿಲ್ಲ"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"ಹೊಸ ಧ್ವನಿಮೇಲ್ ಕಾಯುತ್ತಿದೆ. ಇದೀಗ ಲೋಡ್‌ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"ನಿಮ್ಮ ಧ್ವನಿಮೇಲ್‌ ಹೊಂದಿಸಿ"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"ಆಡಿಯೋ ಲಭ್ಯವಿಲ್ಲ"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"ಹೊಂದಿಸಿ"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"ಧ್ವನಿಮೇಲ್‌‌ಗೆ ಕರೆ ಮಾಡಿ"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"ಸಂಖ್ಯೆಯನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"ಸಂಖ್ಯೆಯನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
- <string name="make_primary" msgid="5829291915305113983">"ಈ ಆಯ್ಕೆಯನ್ನು ನೆನಪಿಡಿ"</string>
- <string name="description_search_button" msgid="3660807558587384889">"ಹುಡುಕಾಟ"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"ಡಯಲ್‌"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"ಡಯಲ್‌ ಮಾಡಬೇಕಾದ ಸಂಖ್ಯೆ"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"ಪ್ಲೇಬ್ಯಾಕ್ ಅನ್ನು ಪ್ಲೇ ಮಾಡಿ ಅಥವಾ ನಿಲ್ಲಿಸಿ"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"ಸ್ಪೀಕರ್‌ಫೋನ್ ಸ್ವಿಚ್ ಆನ್ ಅಥವಾ ಆಫ್ ಮಾಡಿ"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"ಪ್ಲೇಬ್ಯಾಕ್ ಸ್ಥಾನವನ್ನು ಪಡೆಯಿರಿ"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"ಪ್ಲೇಬ್ಯಾಕ್ ರೇಟ್ ಅನ್ನು ಕಡಿಮೆ ಮಾಡು"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"ಪ್ಲೇಬ್ಯಾಕ್ ರೇಟ್ ಅನ್ನು ಹೆಚ್ಚು ಮಾಡು"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"ಕರೆ ಇತಿಹಾಸ"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"ಇನ್ನಷ್ಟು ಆಯ್ಕೆಗಳು"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"ಡಯಲ್‌ ಪ್ಯಾಡ್‌"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"ಹೊರಹೋಗುವುದನ್ನು ಮಾತ್ರ ತೋರಿಸು"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"ಒಳಬರುವುದನ್ನು ಮಾತ್ರ ತೋರಿಸು"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"ತಪ್ಪಿಹೋದದ್ದನ್ನು ಮಾತ್ರ ತೋರಿಸು"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"ಧ್ವನಿಮೇಲ್‌ಗಳನ್ನು ಮಾತ್ರ ತೋರಿಸು"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"ಎಲ್ಲಾ ಕರೆಗಳನ್ನು ತೋರಿಸು"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2-ಸೆ ವಿರಾಮವನ್ನು ಸೇರಿಸಿ"</string>
- <string name="add_wait" msgid="3360818652790319634">"ನಿರೀಕ್ಷೆಯನ್ನು ಸೇರಿಸಿ"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"ಹೊಸ ಸಂಪರ್ಕ"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"ಎಲ್ಲಾ ಸಂಪರ್ಕಗಳು"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"ಕರೆಯ ವಿವರಗಳು"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"ವಿವರಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"ಸ್ಪರ್ಶ ಟೋನ್ ಕೀಪ್ಯಾಡ್ ಬಳಸಿ"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"ಪ್ರತ್ಯತ್ತರ ಕರೆಯು ಪ್ರಗತಿಯಲ್ಲಿದೆ"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"ಕರೆಯನ್ನು ಸೇರಿಸಿ"</string>
- <string name="type_incoming" msgid="6502076603836088532">"ಒಳಬರುವ ಕರೆ"</string>
- <string name="type_outgoing" msgid="343108709599392641">"ಹೊರಹೋಗುವ ಕರೆ"</string>
- <string name="type_missed" msgid="2720502601640509542">"ಮಿಸ್ಡ್‌ ಕಾಲ್‌"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"ಒಳಬರುವ ವೀಡಿಯೊ ಕರೆ"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"ಹೊರಹೋಗುವ ವೀಡಿಯೊ ಕರೆ"</string>
- <string name="type_missed_video" msgid="954396897034220545">"ಮಿಸ್ಡ್‌ ವೀಡಿಯೊ ಕಾಲ್‌"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"ಧ್ವನಿಮೇಲ್"</string>
- <string name="type_rejected" msgid="7783201828312472691">"ನಿರಾಕರಿಸಿದ ಕರೆ"</string>
- <string name="type_blocked" msgid="3521686227115330015">"ನಿರ್ಬಂಧಿಸಿದ ಕರೆ"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"ಒಳಬರುವ ಕರೆಗಳು"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"ಧ್ವನಿಮೇಲ್‌ ಪ್ಲೇ ಮಾಡಿ"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> ಸಂಪರ್ಕ ವೀಕ್ಷಿಸಿ"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g> ಕರೆ ಮಾಡಿ"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> ಗೆ ಸಂಪರ್ಕ ವಿವರಗಳು"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> ಕರೆಗಳು."</string>
- <string name="description_video_call" msgid="2933838090743214204">"ವೀಡಿಯೊ ಕರೆ."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g> ಅವರಿಗೆ SMS ಕಳುಹಿಸಿ"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"ಆಲಿಸಲಾಗದ ಧ್ವನಿಮೇಲ್‌"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"ಧ್ವನಿ ಹುಡುಕಾಟ ಪ್ರಾರಂಭಿಸಿ"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> ಕರೆ ಮಾಡಿ"</string>
- <string name="unknown" msgid="740067747858270469">"ಅಪರಿಚಿತ"</string>
- <string name="voicemail" msgid="3851469869202611441">"ಧ್ವನಿಮೇಲ್"</string>
- <string name="private_num" msgid="6374339738119166953">"ಖಾಸಗಿ ಸಂಖ್ಯೆ"</string>
- <string name="payphone" msgid="7726415831153618726">"ಪೇಫೋನ್"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> ಸೆಕೆಂ"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> ನಿಮಿ <xliff:g id="SECONDS">%s</xliff:g> ಸೆಕೆಂ"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> ರಂದು <xliff:g id="TIME">%2$s</xliff:g> ಗಂಟೆಗೆ"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"ಈ ಸಂಖ್ಯೆಗೆ ಕರೆ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"ಧ್ವನಿಮೇಲ್‌ ಹೊಂದಿಸಲು, ಮೆನು &gt; ಸೆಟ್ಟಿಂಗ್‌ಗಳುಗೆ ಹೋಗಿ."</string>
- <!-- no translation found for dialog_voicemail_airplane_mode_message (530922773669546093) -->
- <skip />
- <string name="contact_list_loading" msgid="5488620820563977329">"ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"ಸಿಮ್‌ ಕಾರ್ಡ್‌ನಿಂದ ಲೋಡ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"ಸಿಮ್‌ ಕಾರ್ಡ್‌ ಸಂಪರ್ಕಗಳು"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"ಯಾವುದೇ ಸಂಪರ್ಕಗಳ ಅಪ್ಲಿಕೇಶನ್‌ ಲಭ್ಯವಿಲ್ಲ"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"ಧ್ವನಿ ಹುಡುಕಾಟ ಲಭ್ಯವಿಲ್ಲ"</string>
- <string name="call_not_available" msgid="8941576511946492225">"ಫೋನ್ ಅಪ್ಲಿಕೇಶನ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿರುವುದರಿಂದ ಫೋನ್ ಕರೆ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"ಈ ಸಾಧನದಲ್ಲಿ ಅದಕ್ಕಾಗಿ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿಲ್ಲ"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"ಸಂಪರ್ಕಗಳನ್ನು ಹುಡುಕಿ"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"ಸಂ. ಸೇರಿಸಿ ಅಥವಾ ಸಂಪರ್ಕ ಹುಡುಕಿ"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"ನಿಮ್ಮ ಕರೆ ಇತಿಹಾಸ ಖಾಲಿಯಾಗಿದೆ"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"ಕರೆ ಮಾಡಿ"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"ನೀವು ಯಾವುದೇ ಮಿಸ್ಡ್ ಕರೆಗಳನ್ನು ಹೊಂದಿಲ್ಲ."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"ನಿಮ್ಮ ಧ್ವನಿಮೇಲ್ ಇನ್‌ಬಾಕ್ಸ್ ಖಾಲಿ ಇದೆ."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"ನಿಮ್ಮ ಧ್ವನಿಮೇಲ್ ಆರ್ಕೈವ್ ಖಾಲಿಯಿದೆ."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"ಮೆಚ್ಚಿನವುಗಳನ್ನು ಮಾತ್ರ ತೋರಿಸು"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"ಕರೆ ಇತಿಹಾಸ"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"ಧ್ವನಿಮೇಲ್ ಆರ್ಕೈವ್"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"ಎಲ್ಲಾ ಕರೆಗಳು"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"ತಪ್ಪಿದ ಕರೆಗಳು"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"ಧ್ವನಿಮೇಲ್"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"ಹೊಸ, ಸರಳೀಕರಿಸಿದ ನಿರ್ಬಂಧ"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"ನಿಮ್ಮನ್ನು ಉತ್ತಮ ರೀತಿಯಲ್ಲಿ ರಕ್ಷಿಸಲು, ನಿರ್ಬಂಧ ಹೇಗೆ ಕೆಲಸ ಮಾಡುತ್ತದೆ ಎಂದು ಫೋನ್ ಬದಲಾಯಿಸಬೇಕಾಗಿದೆ. ನಿಮ್ಮ ನಿರ್ಬಂಧಿಸಿದ ಸಂಖ್ಯೆಗಳು ಈಗ ಕರೆ ಮತ್ತು ಪಠ್ಯ ಸಂದೇಶಗಳನ್ನು ನಿಲ್ಲಿಸುತ್ತವೆ ಹಾಗೂ ಇತರ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಜೊತೆಗೆ ಹಂಚಿಕೊಳ್ಳಬಹುದು."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"ಅನುಮತಿಸು"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> ನಿರ್ಬಂಧಿಸುವುದೇ?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"ಈ ಸಂಖ್ಯೆಗೆ ಕರೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗುತ್ತದೆ ಮತ್ತು ಧ್ವನಿಮೇಲ್‌ಗಳನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಅಳಿಸಲಾಗುತ್ತದೆ."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"ಈ ಸಂಖ್ಯೆಗೆ ಕರೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗುವುದು, ಆದರೆ ಕರೆಮಾಡುವವರು ಧ್ವನಿಮೇಲ್‌ಗಳನ್ನು ಈಗಲೂ ನಿಮಗೆ ಕಳುಹಿಸಬಹುದು."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"ಈ ಸಂಖ್ಯೆಯಿಂದ ನೀವು ಇನ್ನು ಮುಂದೆ ಕರೆಗಳು ಅಥವಾ ಪಠ್ಯ ಸಂದೇಶಗಳನ್ನು ಸ್ವೀಕರಿಸುವುದಿಲ್ಲ."</string>
- <string name="block_number_ok" msgid="770551992296781873">"ನಿರ್ಬಂಧಿಸು"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> ನಿರ್ಬಂಧ ತೆಗೆಯುವುದೇ?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ನಿರ್ಬಂಧ ತೆಗೆ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"ಸ್ಪೀಡ್ ಡಯಲ್"</string>
- <string name="tab_history" msgid="2563144697322434940">"ಕರೆ ಇತಿಹಾಸ"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"ಸಂಪರ್ಕಗಳು"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"ಧ್ವನಿಮೇಲ್"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"ಮೆಚ್ಚಿನವುಗಳಿಂದ ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"ರದ್ದುಮಾಡಿ"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> ಕರೆ ಮಾಡಿ"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"ಹೊಸ ಸಂಪರ್ಕ ರಚಿಸು"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"ಸಂಪರ್ಕಕ್ಕೆ ಸೇರಿಸು"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS ಕಳುಹಿಸು"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"ವೀಡಿಯೊ ಕರೆ ಮಾಡಿ"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"ಸಂಖ್ಯೆಯನ್ನು ನಿರ್ಬಂಧಿಸು"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> ಹೊಸ ತಪ್ಪಿದ ಕರೆಗಳು"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"ನಿಮ್ಮ ತ್ವರಿತ ಡಯಲ್‌ನಲ್ಲಿ ಇದುವರೆಗೆ ಯಾರೂ ಇಲ್ಲ"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"ಮೆಚ್ಚಿನದನ್ನು ಸೇರಿಸಿ"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"ನಿಮ್ಮ ಬಳಿ ಇನ್ನೂ ಯಾವುದೇ ಸಂಪರ್ಕಗಳಿಲ್ಲ"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"ಸಂಪರ್ಕ ಸೇರಿಸಿ"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"ಎಲ್ಲ ಸಂಖ್ಯೆಗಳನ್ನು ನೋಡಲು ಚಿತ್ರವನ್ನು ಸ್ಫರ್ಶಿಸಿ ಅಥವಾ ಮರುಕ್ರಮಗೊಳಿಸಲು ಸ್ಪರ್ಶಿಸಿ &amp; ಹೋಲ್ಡ್‌ ಮಾಡಿ"</string>
- <string name="remove_contact" msgid="1080555335283662961">"ತೆಗೆದುಹಾಕು"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"ವೀಡಿಯೊ ಕರೆ"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"ಸಂದೇಶ ಕಳುಹಿಸಿ"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"ಕರೆಯ ವಿವರಗಳು"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ಕರೆ ಮಾಡಿ"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> ರಿಂದ ತಪ್ಪಿದ ಕರೆ."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> ರಿಂದ ಕರೆಗೆ ಉತ್ತರಿಸಲಾಗಿದೆ."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> ರಿಂದ ಓದದಿರುವ ಧ್ವನಿಮೇಲ್."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> ರಿಂದ ಧ್ವನಿಮೇಲ್."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> ಗೆ ಕರೆ."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> ನಲ್ಲಿ"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> ಮೂಲಕ"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> ಮೂಲಕ"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> ರಲ್ಲಿ, <xliff:g id="NUMBER">%2$s</xliff:g> ಮೂಲಕ"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="NUMBER">%2$s</xliff:g> ಮೂಲಕ <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"ಕರೆಮಾಡಿ"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ಕರೆ ಮಾಡಿ"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ಗೆ ವೀಡಿಯೊ ಕರೆ ಮಾಡಿ."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ರಿಂದ ಧ್ವನಿಮೇಲ್ ಆಲಿಸಿ"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ರಿಂದ ಧ್ವನಿಮೇಲ್ ಪ್ಲೇ ಮಾಡಿ"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ನಿಂದ ಧ್ವನಿಮೇಲ್ ವಿರಾಮಗೊಳಿಸಿ"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ನಿಂದ ಧ್ವನಿಮೇಲ್ ಅಳಿಸಿ"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> ಹೊಸ ಧ್ವನಿಮೇಲ್‌ಗಳು</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> ಹೊಸ ಧ್ವನಿಮೇಲ್‌ಗಳು</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ಗೆ ಸಂಪರ್ಕವನ್ನು ರಚಿಸಿ"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಸಂಪರ್ಕಕ್ಕೆ <xliff:g id="NAMEORNUMBER">^1</xliff:g> ಸೇರಿಸಿ"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ಗೆ ಸಂಪರ್ಕದ ವಿವರಗಳು"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"ಕರೆ ಇತಿಹಾಸದಿಂದ ಅಳಿಸಲಾಗಿದೆ"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"ಇಂದು"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"ನಿನ್ನೆ"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"ಹಳೆಯದು"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"ಕರೆಗಳ ಪಟ್ಟಿ"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"ಸ್ಪೀಕರ್ ಆನ್ ಮಾಡಿ."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"ಸ್ಪೀಕರ್ ಆಫ್ ಮಾಡಿ."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"ವೇಗವಾಗಿ ಪ್ಲೇ ಮಾಡಿ."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"ನಿಧಾನವಾಗಿ ಪ್ಲೇ ಮಾಡಿ."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"ಪ್ಲೇಬ್ಯಾಕ್‌ ಪ್ರಾರಂಭಿಸಿ ಅಥವಾ ವಿರಾಮಗೊಳಿಸಿ."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"ಪ್ರದರ್ಶನ ಆಯ್ಕೆಗಳು"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"ಧ್ವನಿ ಮತ್ತು ವೈಬ್ರೇಷನ್‌"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"ಪ್ರವೇಶಿಸುವಿಕೆ"</string>
- <string name="ringtone_title" msgid="760362035635084653">"ಫೋನ್ ರಿಂಗ್‌ಟೋನ್"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"ಕರೆಗಳಿಗೂ ಸಹ ವೈಬ್ರೇಟ್‌"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"ಡಯಲ್‌ಪ್ಯಾಡ್‌ ಟೋನ್‌ಗಳು"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"ಡಯಲ್‌ಪ್ಯಾಡ್‌ ಟೋನ್ ಅಳತೆ"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"ಸಾಮಾನ್ಯ"</item>
- <item msgid="6177579030803486015">"ದೀರ್ಘವಾದ"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"ತ್ವರಿತ ಪ್ರತಿಕ್ರಿಯೆಗಳು"</string>
- <string name="call_settings_label" msgid="313434211353070209">"ಕರೆಗಳು"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"ಕರೆ ನಿರ್ಬಂಧಿಸುವಿಕೆ"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"ಕರೆ ನಿರ್ಬಂಧಿಸುವಿಕೆ ತಾತ್ಕಾಲಿಕ ಆಫ್ ಮಾಡಲಾಗಿದೆ"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"ನೀವು ಕಳೆದ 48 ಗಂಟೆಗಳಲ್ಲಿ ಈ ಫೋನ್‌ನಿಂದ ತುರ್ತು ಸೇವೆಗಳಿಗೆ ಸಂಪರ್ಕಿಸಿರುವ ಕಾರಣದಿಂದ ಕರೆ ನಿರ್ಬಂಧಿಸುವಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ. ಒಮ್ಮೆ 48 ಗಂಟೆಗಳ ಅವಧಿಯು ಮುಕ್ತಾಯಗೊಂಡ ನಂತರ ಅದನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಮರುಸಕ್ರಿಯಗೊಳಿಸಲಾಗುವುದು."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"ಸಂಖ್ಯೆಗಳನ್ನು ಆಮದು ಮಾಡಿ"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"ನೀವು ಈ ಹಿಂದೆ ಗುರುತು ಮಾಡಲಾದ ಕೆಲವು ಕಾಲರ್‌ಗಳನ್ನು ಇತರ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಮೂಲಕ ಧ್ವನಿಮೇಲ್‌ಗೆ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕಳುಹಿಸಲಾಗುತ್ತದೆ."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"ಸಂಖ್ಯೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"ಆಮದು ಮಾಡಿ"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"ಆಮದು ವಿಫಲವಾಗಿದೆ"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"ಧ್ವನಿಮೇಲ್ ಆರ್ಕೈವ್‌ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"ಸಂಖ್ಯೆಯನ್ನು ಅನಿರ್ಬಂಧಿಸು"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"ಸಂಖ್ಯೆಯನ್ನು ಸೇರಿಸಿ"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"ಈ ಸಂಖ್ಯೆಗಳಿಗೆ ಕರೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗುತ್ತದೆ ಮತ್ತು ಧ್ವನಿಮೇಲ್‌ಗಳನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಅಳಿಸಲಾಗುತ್ತದೆ."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"ಈ ಸಂಖ್ಯೆಗಳಿಗೆ ಕರೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗುವುದು, ಆದರೆ ಅವರು ಧ್ವನಿಮೇಲ್‌ಗಳನ್ನು ಈಗಲೂ ನಿಮಗೆ ಕಳುಹಿಸಬಹುದು."</string>
- <string name="block_list" msgid="7760188925338078011">"ನಿರ್ಬಂಧಿಸಲಾದ ಸಂಖ್ಯೆಗಳು"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> ಅಮಾನ್ಯವಾಗಿದೆ."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> ಈಗಾಗಲೇ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"ಕರೆ ನಿರ್ಬಂಧಿಸುವಿಕೆ 48 ಗಂಟೆಗಳವರೆಗೆ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"ತುರ್ತು ಕರೆಯನ್ನು ಮಾಡಿರುವ ಕಾರಣದಿಂದ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"ಕರೆ ಮಾಡುವ ಖಾತೆಗಳು"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"ಆನ್ ಮಾಡು"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"ಅನುಮತಿಗಳನ್ನು ಹೊಂದಿಸಿ"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"ವೇಗ ಡಯಲ್ ಸಕ್ರಿಯಗೊಳಿಸಲು, ಸಂಪರ್ಕಗಳ ಅನುಮತಿಯನ್ನು ಆನ್ ಮಾಡಿ."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"ನಿಮ್ಮ ಕರೆಯ ಲಾಗ್ ಅನ್ನು ವೀಕ್ಷಿಸಲು, ಫೋನ್ ಅನುಮತಿಯನ್ನು ಆನ್ ಮಾಡಿ."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"ನಿಮ್ಮ ಸಂಪರ್ಕಗಳನ್ನು ವೀಕ್ಷಿಸಲು, ಸಂಪರ್ಕಗಳ ಅನುಮತಿಯನ್ನು ಆನ್ ಮಾಡಿ."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"ನಿಮ್ಮ ಧ್ವನಿಮೇಲ್ ಪ್ರವೇಶಿಸಲು, ಫೋನ್ ಅನುಮತಿಯನ್ನು ಆನ್ ಮಾಡಿ."</string>
- <string name="permission_no_search" msgid="84152933267902056">"ನಿಮ್ಮ ಸಂಪರ್ಕಗಳನ್ನು ಹುಡುಕಲು, ಸಂಪರ್ಕಗಳ ಅನುಮತಿಗಳನ್ನು ಆನ್ ಮಾಡಿ."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"ಕರೆ ಮಾಡಲು, ಫೋನ್ ಅನುಮತಿಯನ್ನು ಆನ್ ಮಾಡಿ."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"ಸಿಸ್ಟಂ ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ಬರೆಯಲು ಫೋನ್ ಅಪ್ಲಿಕೇಶನ್ ಅನುಮತಿಯನ್ನು ಹೊಂದಿಲ್ಲ."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ಸಕ್ರಿಯವಾಗಿದೆ"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"ಸ್ಪ್ಯಾಮ್ ನಿರ್ಬಂಧಿಸು/ವರದಿ ಮಾಡು"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"ನಿರ್ಬಂಧಿಸು"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"ಸ್ಪ್ಯಾಮ್‌ ಅಲ್ಲ"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"ನಿರ್ಬಂಧ ತೆಗೆಯಿರಿ"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"ಸ್ಪ್ಯಾಮ್"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> ನಿರ್ಬಂಧಿಸುವುದೇ?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"ಈ ಸಂಖ್ಯೆಗೆ ಭವಿಷ್ಯದ ಕರೆಗಳು ಮತ್ತು ಧ್ವನಿಮೇಲ್‌‌ಗಳನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗುತ್ತದೆ."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"ಕರೆ ಸ್ಪ್ಯಾಮ್ ಎಂದು ವರದಿಮಾಡಿ"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"ಈ ಸಂಖ್ಯೆಗೆ ಭವಿಷ್ಯದ ಕರೆ ಮತ್ತು ಧ್ವನಿಮೇಲ್‌ ನಿರ್ಬಂಧಿಸಲಾಗುತ್ತದೆ. ಈ ಕರೆ ಸ್ಪ್ಯಾಮ್ ಎಂದು ವರದಿಮಾಡಲಾಗುತ್ತದೆ."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> ನಿರ್ಬಂಧ ತೆಗೆಯುವುದೇ?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"ಈ ಸಂಖ್ಯೆಗೆ ನಿರ್ಬಂಧ ತೆಗೆಯಲಾಗುತ್ತದೆ ಮತ್ತು ಸ್ಪ್ಯಾಮ್ ಎಂದು ವರದಿಮಾಡಲಾಗುವುದಿಲ್ಲ. ಭವಿಷ್ಯದ ಕರೆಗಳು ಮತ್ತು ಧ್ವನಿಮೇಲ್‌ಗಳನ್ನು ಸ್ಪ್ಯಾಮ್ ಎಂದು ಗುರುತಿಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> ಅನುಮತಿ ಪಟ್ಟಿಗೆ ಸೇರಿಸುವುದೇ?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"ಅನುಮತಿ ಪಟ್ಟಿ"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"ಈ ಸಂಖ್ಯೆಗೆ ಭವಿಷ್ಯದ ಕರೆಗಳು ಮತ್ತು ಧ್ವನಿಮೇಲ್‌ಗಳನ್ನು ಸ್ಪ್ಯಾಮ್ ಎಂದು ಗುರುತಿಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ. ಈ ಸಂಖ್ಯೆ ಸ್ಪ್ಯಾಮ್ ಎಂದು ವರದಿ ಮಾಡಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ."</string>
-</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
deleted file mode 100644
index 810e64545..000000000
--- a/res/values-ko/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"전화"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"전화"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"전화 다이얼패드"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"휴대전화"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"통화 기록"</string>
- <string name="action_report_number" msgid="4635403959812186162">"잘못된 번호 신고하기"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"전화번호 복사"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"텍스트 변환 복사"</string>
- <string name="action_block_number" msgid="1482657602262262134">"번호 차단"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> 차단됨"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"번호 차단 해제"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> 차단 해제됨"</string>
- <string name="block_number_undo" msgid="591338370336724156">"실행취소"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"삭제"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"통화하기 전에 번호 수정"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"통화 기록 삭제"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"음성사서함 삭제"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"음성사서함 보관처리"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"음성사서함 공유"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"음성사서함 삭제됨"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"음성사서함 보관처리됨"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"실행취소"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"보관함으로 이동"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"통화 기록을 삭제하시겠습니까?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"모든 통화가 기록에서 삭제됩니다."</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"통화 기록을 삭제하는 중…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"전화"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"부재중 전화"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"부재중 업무 통화"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"부재중 전화"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"부재중 전화 <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>통"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"전화 걸기"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"메시지"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other">음성메일 <xliff:g id="COUNT">%1$d</xliff:g>개</item>
- <item quantity="one">음성메일</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"재생"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g>님이 보낸 새 음성사서함"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"음성사서함을 재생할 수 없습니다."</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"음성사서함 로드 중…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"음성사서함 보관처리 중…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"음성사서함을 로드할 수 없습니다."</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"음성사서함 메시지만"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"수신 전화만"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"발신 전화만"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"부재중 전화만"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"시각적 음성사서함"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"전화를 걸지 않고도 음성사서함을 확인하고 들을 수 있습니다. 데이터 요금이 부과될 수 있습니다."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"설정"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"음성사서함 업데이트를 사용할 수 없습니다."</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"새로운 음성사서함이 대기 중이지만 현재 로드할 수 없습니다."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"음성사서함 설정"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"오디오가 지원되지 않습니다."</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"설정"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"음성사서함 연결"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"<xliff:g id="DATE">%2$s</xliff:g>에 통화 <xliff:g id="COUNT">%1$d</xliff:g>통"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"번호 선택"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"번호 선택"</string>
- <string name="make_primary" msgid="5829291915305113983">"이 선택사항 저장"</string>
- <string name="description_search_button" msgid="3660807558587384889">"검색"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"전화걸기"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"전화를 걸 번호"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"재생 실행 또는 중지"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"스피커폰 켜고 끄기"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"재생 위치 찾기"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"재생 속도 낮추기"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"재생 속도 높이기"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"통화 기록"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"옵션 더보기"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"다이얼패드"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"발신 전화만 표시"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"수신 전화만 표시"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"부재중 전화만 표시"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"음성사서함만 표시"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"모든 통화 표시"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2초 간 일시 정지 추가"</string>
- <string name="add_wait" msgid="3360818652790319634">"대기 시간 추가"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"설정"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"새 연락처"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"전체 주소록"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"통화 세부정보"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"세부정보를 사용할 수 없습니다."</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"터치톤 키패드 사용"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"진행 중인 통화로 돌아가기"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"통화 추가"</string>
- <string name="type_incoming" msgid="6502076603836088532">"수신전화"</string>
- <string name="type_outgoing" msgid="343108709599392641">"발신전화"</string>
- <string name="type_missed" msgid="2720502601640509542">"부재중 전화"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"수신 화상 통화"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"발신 화상 통화"</string>
- <string name="type_missed_video" msgid="954396897034220545">"부재중 화상 통화"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"음성사서함"</string>
- <string name="type_rejected" msgid="7783201828312472691">"거부된 통화"</string>
- <string name="type_blocked" msgid="3521686227115330015">"차단된 통화"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"수신전화"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"음성사서함 재생"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g>님의 연락처 보기"</string>
- <string name="description_call" msgid="3443678121983852666">"전화하기:<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g>의 연락처 세부정보"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"통화 횟수: <xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>번"</string>
- <string name="description_video_call" msgid="2933838090743214204">"화상 통화"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g>님에게 SMS 보내기"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"듣지 않은 음성사서함"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"음성 검색 시작"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g>에 전화"</string>
- <string name="unknown" msgid="740067747858270469">"알 수 없음"</string>
- <string name="voicemail" msgid="3851469869202611441">"음성사서함"</string>
- <string name="private_num" msgid="6374339738119166953">"비공개 번호"</string>
- <string name="payphone" msgid="7726415831153618726">"공중전화"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g>초"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g>분 <xliff:g id="SECONDS">%s</xliff:g>초"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>분 <xliff:g id="SECONDS">%2$02d</xliff:g>초"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"이 번호로 전화를 걸 수 없습니다."</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"음성사서함을 설정하려면 메뉴 &gt; 설정으로 이동하세요."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"음성사서함에 메시지를 남기려면 먼저 비행기 모드를 해제하세요."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"로드 중…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"SIM 카드에서 로딩 중..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM 카드 주소록"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"사용할 수 있는 주소록 앱이 없습니다."</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"음성검색이 지원되지 않습니다."</string>
- <string name="call_not_available" msgid="8941576511946492225">"전화 애플리케이션을 사용 중지했으므로 전화를 걸 수 없습니다."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"이 기기에 작업을 처리할 수 있는 앱이 없습니다."</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"연락처 검색"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"번호 추가 또는 연락처 검색"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"통화 기록이 비어있습니다."</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"전화 걸기"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"부재중 전화가 없습니다."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"음성사서함 받은편지함이 비어 있습니다."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"음성사서함 보관함이 비어있습니다."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"즐겨찾는 연락처만 표시"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"통화 기록"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"음성사서함 보관함"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"전체"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"부재중 전화"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"음성사서함"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"새로운 간편 차단 기능"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"사용자를 보호하기 위하여 전화에서 차단 작동 방식을 변경해야 합니다. 차단된 번호에서 발송되는 통화 및 문자 메시지는 수신되지 않으며 다른 앱과 차단 사실이 공유될 수 있습니다."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"허용"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g>번을 차단할까요?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"이 번호에서 걸려오는 전화가 차단되며 음성사서함이 자동으로 삭제됩니다."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"이 번호에서 걸려오는 전화가 차단되지만, 발신자가 음성사서함에 메시지를 남길 수는 있습니다."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"이 번호로부터 수신되는 전화와 문자를 더 이상 받지 않습니다."</string>
- <string name="block_number_ok" msgid="770551992296781873">"차단"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g>번을 차단 해제할까요?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"차단 해제"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"단축번호"</string>
- <string name="tab_history" msgid="2563144697322434940">"통화 기록"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"주소록"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"음성사서함"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"즐겨찾기에서 삭제됨"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"실행취소"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g>에 전화"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"새 연락처 만들기"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"연락처에 추가"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS 보내기"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"화상 통화하기"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"번호 차단"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"새로운 부재중 전화 <xliff:g id="NUMBER">%s</xliff:g>건"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"아직 단축 다이얼이 설정된 연락처가 없습니다."</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"단축 다이얼 추가"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"아직 연락처가 없습니다."</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"연락처 추가"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"이미지를 터치하여 모든 번호를 확인하거나 길게 터치하여 재정렬합니다."</string>
- <string name="remove_contact" msgid="1080555335283662961">"삭제"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"화상 통화"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"메시지 보내기"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"통화 세부정보"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>에 전화 걸기"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>의 부재중 전화(<xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>)"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>에게 걸려온 수신 전화(<xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>)"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>에서 보낸 읽지 않은 음성사서함"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>에서 보낸 음성사서함"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>에게 건 전화(<xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>)"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> 계정"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g>번으로 수신"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g>번으로 수신"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>에서 <xliff:g id="NUMBER">%2$s</xliff:g>번으로 수신"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> <xliff:g id="NUMBER">%2$s</xliff:g>번으로 수신"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"통화"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>에 전화 걸기"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>에 화상 통화 걸기"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> 음성사서함 듣기"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>에서 발신한 음성사서함 재생"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>에서 발신한 음성사서함 일시중지"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>에서 발신한 음성사서함 삭제"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other">새 음성사서함 <xliff:g id="COUNT_1">%d</xliff:g>개</item>
- <item quantity="one">새 음성사서함 <xliff:g id="COUNT_0">%d</xliff:g>개</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> 연락처 만들기"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>님을 기존 연락처에 추가합니다."</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>의 통화 세부정보"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"통화 기록에서 삭제했습니다."</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"오늘"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"어제"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"이전"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"통화 목록"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"스피커를 켭니다."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"스피커를 끕니다."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"더 빠르게 재생합니다."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"더 느리게 재생합니다."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"재생을 시작하거나 일시중지합니다."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"표시 옵션"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"소리 및 진동"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"접근성"</string>
- <string name="ringtone_title" msgid="760362035635084653">"전화 벨소리"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"전화 올 때 벨소리와 함께 진동"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"다이얼패드 신호음"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"다이얼패드 신호음 길이"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"보통"</item>
- <item msgid="6177579030803486015">"길게"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"빠른 응답"</string>
- <string name="call_settings_label" msgid="313434211353070209">"통화"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"통화 차단"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"통화 차단 기능이 일시적으로 중지됨"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"지난 48시간 이내에 이 휴대전화를 사용해 응급 서비스에 연락했으므로 통화 차단 기능이 중지되었습니다. 48시간이 지나면 통화 차단 기능이 자동으로 다시 사용 설정됩니다."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"번호 가져오기"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"이전에 다른 앱을 통해 일부 발신자를 자동으로 음성사서함으로 보내도록 표시했습니다."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"개수 보기"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"가져오기"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"가져오지 못했습니다."</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"음성사서함을 보관처리하지 못했습니다."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"번호 차단 해제"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"번호 추가"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"이러한 번호에서 걸려오는 전화가 차단되며 음성사서함이 자동으로 삭제됩니다."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"이러한 번호에서 걸려오는 전화가 차단되지만, 발신자가 음성사서함에 메시지를 남길 수는 있습니다."</string>
- <string name="block_list" msgid="7760188925338078011">"차단된 번호"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g>번은 잘못된 번호입니다."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g>번은 이미 차단되었습니다."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"통화 차단이 48시간 동안 중지됨"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"긴급 통화를 사용했기 때문에 중지되었습니다."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"통화 계정"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"사용"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"권한 설정"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"단축번호를 사용하려면 주소록 권한을 사용하도록 설정하세요."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"통화 기록을 보려면 전화 권한을 사용하도록 설정하세요."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"주소록을 보려면 주소록 권한을 사용하도록 설정하세요."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"음성사서함에 액세스하려면 전화 권한을 사용하도록 설정하세요."</string>
- <string name="permission_no_search" msgid="84152933267902056">"주소록을 검색하려면 주소록 권한을 사용하도록 설정하세요."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"전화를 걸려면 전화 권한을 사용하도록 설정하세요."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"전화 앱은 시스템 설정에 쓸 수 있는 권한이 없습니다."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"차단됨"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>이(가) 활성화되었습니다."</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"스팸 차단/신고"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"차단"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"스팸 해제"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"차단 해제"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"스팸"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g>번을 차단하시겠습니까?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"앞으로 이 번호로부터 수신되는 전화와 음성 메시지가 차단됩니다."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"전화를 스팸으로 신고"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"앞으로 이 번호로부터 수신되는 전화와 음성 메시지가 차단됩니다. 이 전화는 스팸으로 신고됩니다."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g>번을 차단 해제하시겠습니까?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"이 번호는 차단되지 않으며 스팸으로 신고되지 않습니다. 앞으로 전화와 음성 메시지는 스팸으로 인식되지 않습니다."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g>번을 허용하시겠습니까?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"허용"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"앞으로 이 번호로부터 수신되는 전화와 음성 메시지가 스팸으로 인식되지 않습니다. 이 번호는 스팸으로 신고되지 않습니다."</string>
-</resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
deleted file mode 100644
index 6e1eec4e2..000000000
--- a/res/values-ky/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Телефон"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Телефон"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Телефон тергичи"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Телефон"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Чалуулар таржымалы"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Номер туура эмес"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Номерди көчүрүү"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Транскрипцияны көчүрүү"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Номерди бөгөттөө"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> бөгөттөлдү"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Номерди бөгөттөн чыгаруу"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> бөгөттөн чыгарылды"</string>
- <string name="block_number_undo" msgid="591338370336724156">"КАЙТАРУУ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Жок кылуу"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Чалуудан мурун номерди түзөтүү"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Чалуулар таржымалын тазалоо"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Үн почтасын жок кылуу"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Үн каттарын архивдөө"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Үн билдирүүсүн жөнөтүү"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Үн почтасы жок кылнд"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Үн каттары архивделди"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"КАЙТАРУУ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"АРХВГЕ ӨТ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Чалуулар таржымалы тазалансынбы?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Ушуну менен бул таржымалдагы бардык чалуулар жок болот"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Чалуулар таржымалы тазаланууда…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Телефон"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Кабыл алынбаган чалуу"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Кабыл алынбай калган чалуу (жумуш)"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Кабыл алынбаган чалуулар"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> кабыл алынбаган чалуу"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Кайра чалуу"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Билдирүү"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Үн каты </item>
- <item quantity="one">Үн каты</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Угуу"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> жаңы үнкат калтырды"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Үн почтасы ойнолгон жок"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Үн почтасы жүктөлүүдө…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Үн каттары архивделүүдө…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Үн почтасы жүктөлгөн жок"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Үнкат чалуулары"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Кириш чалуулар"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Чыккан чалуулар"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Өткөзүлгөн чалуулар гана"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Визуалдык үн почтасы"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Номерге чалып отурбастан, үн почтаңызды көрүп, уга аласыз. Дайындардын өткөрүлгөндүгү үчүн акы алынышы мүмкүн."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Жөндөөлөр"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Үн почтасынын жаңыртуулары жеткиликтүү эмес"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Жаңы үн почтасы күтүүдө. Учурда жүктөлбөй жатат."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Үн почтаңызды орнотуңуз"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Аудио жеткиликтүү эмес"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Тууралоо"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Үнкатка чалуу"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Номур тандоо"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Номур тандаңыз"</string>
- <string name="make_primary" msgid="5829291915305113983">"Бул тандоону эстеп калуу"</string>
- <string name="description_search_button" msgid="3660807558587384889">"издөө"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"терүү"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"терүү үчүн номер"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Ойнотууну иштетүү же токтотуу"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Катуу сүйлөткүчтү күйгүзүү же өчүрүү"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Ойнотуунун жайгашкан жерин издөө"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Ойнотуу ылдамдыгын жайлатуу"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Ойнотуу ылдамдыгын тездетүү"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Чалуулар таржымалы"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Көбүрөөк мүмкүнчүлүктөр"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"чалгыч такта"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Чыккан чалуу-ды гана көрсөтүү"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Кириш чалууларды гана көрсөтүү"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Өткөз. чалуу-ды гана көрсөтүү"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Үнкаттарды гана көрсөтүү"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Бардык чалууларды көрсөтүү"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2-сек. тыныгууну кошуңуз"</string>
- <string name="add_wait" msgid="3360818652790319634">"Тыныгуу кошуу"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Жөндөөлөр"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Жаңы байланыш"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Бардык байланыштар"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Чалуунун чоо-жайы"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Чоо-жайы жеткиликтүү эмес"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Тоналдык терүү тактасын колдонуу"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Токтотулган чалууга кайтуу"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Чалууну кошуу"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Кирүүчү чалуу"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Чыгуучу чалуу"</string>
- <string name="type_missed" msgid="2720502601640509542">"Кабыл алынбаган чалуулар"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Келип жаткан видео чалуу"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Чыгып жаткан видео чалуу"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Кабыл алынбаган видео чалуу"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Үн почтасы"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Четке кагылган чалуу"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Бөгөттөлгөн чалуу"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Кирүүчү чалуулар"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Үнкатты угуу"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Байланышты кароо <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Чалуу <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> байланыш маалыматтары"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> чалуу."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Видео чалуу."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g> дегенге SMS жөнөтүү"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Угула элек үнкат"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Үн менен издеп баштоо"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Чалуу <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Белгисиз"</string>
- <string name="voicemail" msgid="3851469869202611441">"Үн почтасы"</string>
- <string name="private_num" msgid="6374339738119166953">"Купуя номер"</string>
- <string name="payphone" msgid="7726415831153618726">"Таксофон"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> сек."</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> мүн. <xliff:g id="SECONDS">%s</xliff:g> сек."</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> саат <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Бул номурга чалуу мүмкүн болбой жатат"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Үнкат орноотуу үчүн Меню &gt; Тууралоолорго кириңиз."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Үнкатты текшерүү үчүн, алгач Учак тартибин өчүрүңүз."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Жүктөлүүдө…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"SIM картадан жүктөлүүдө..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM картадагы байланыштар"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Жеткиликтүү байланыштар колдонмосу жок"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Үн менен издөө жеткиликтүү эмес"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Телефон колдонмосу өчүрүлгөндүктөн, чалуу мүмкүн болбой жатат."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Бул түзмөктө ал үчүн колдонмо жок"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Байланыштардан издеп көрүңүз"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Номер кошуңуз же байлнш издңз"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Чалуулар таржымалыңыз бош"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Чалуу"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Кабыл алынбаган чалуулар жок."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Үн почтаңыздын келген билдирүүлөр куржуну бош."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Үн каттар архивиңиз бош."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Тандамалдарды гана көрсөтүү"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Чалуулар таржымалы"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Үн каттар архиви"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Бардыгы"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Кабыл алынбагандар"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Үнкат"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Номерлерди жаңы жөнөкөй ыкма менен бөгөттөө"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Сизди жакшыраак коргоо үчүн, \"Телефон\" колдонмосу бөгөттөө жөндөөлөрүн өзгөртүшү керек. Бөгөттөлгөн номерлерден чалуулар жана билдирүүлөр келбейт жана алар башка колдонмолор менен бөлүшүлүшү мүмкүн."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Уруксат берүү"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> бөгөттөлсүнбү?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Бул номерден келген чалуулар бөгөттөлөт жана үн билдирүүлөрү автоматтык түрдө жок кылынат."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Бул номерден келген чалуулар бөгөттөлөт, бирок чалуучу сизге үн билдирүүлөрүн калтыра берет."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Бул номерден келген чалууларды же SMS билдирүүлөрдү албай каласыз."</string>
- <string name="block_number_ok" msgid="770551992296781873">"БӨГӨТТӨӨ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> бөгөттөн чыгарылсынбы?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"БӨГӨТТӨН ЧЫГАРУУ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Тез терүү"</string>
- <string name="tab_history" msgid="2563144697322434940">"Чалуулар таржымалы"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Байланыштар"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Үн почтасы"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Тандамалдардан өчүрүлдү"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Кайтаруу"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Чалуу <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Жаңы байланыш түзүү"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Байланышка кошуу"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS жөнөтүү"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Видео түрүндө чалуу"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Номерди бөгөттөө"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> жаңы өткөзүлгөн чалуу"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Азырынча тез териле турган номерлер жок"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Сүйүктүү номер кошуу"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Азырынча бир дагы байланышыңыз жок"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Байланыш кошуу"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Бардык номурларды көрүү үчүн сүрөткө тийип коюңуз же иреттештирүү үчүн жана коё бербей басып туруңуз"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Алып салуу"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Видео чалуу"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Билдирүү жөнөтүү"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Чалуунун чоо-жайы"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> чалуу"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> дегенден жооп берилбей калган чалуу."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> дегенден жооп берилген чалуу."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> дегенден окула элек үн почтасы, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> дегенден үн почтасы, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> дегенге чалуу."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> боюнча"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> аркылуу"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> аркылуу"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> боюнча <xliff:g id="NUMBER">%2$s</xliff:g> аркылуу"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="NUMBER">%2$s</xliff:g> аркылуу <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Чалуу"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> чалуу"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> видео чалуу."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> үн катын угуу"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Үн почтасын <xliff:g id="NAMEORNUMBER">^1</xliff:g> дегенден ойнотуу"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Үн почтасын <xliff:g id="NAMEORNUMBER">^1</xliff:g> дегенден тындыруу"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Үн почтасын <xliff:g id="NAMEORNUMBER">^1</xliff:g> дегенден жок кылуу"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> жаңы үн почтасы</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> жаңы үн почтасы</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> номери үчүн байланыш түзүү"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> учурдагы байланышка кошуу"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> чалуу чоо-жайы"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Чалуулар таржымалынан жок кылынды"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Бүгүн"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Кечээ"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Мурункураак"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Чалуулар тизмеси"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Катуу сүйлөткүч күйгүзүлгөн."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Катуу сүйлөткүч өчүрүлгөн."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Тезирээк ойнотуу."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Жайыраак ойнотуу."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Ойнотуп баштоо же бир азга токтотуу"</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Көрсөтүү параметрлери"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Үндөр жана дирилдөө"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Жеткиликтүүлүк"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Телефондун шыңгыры"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Дирилдеп чалынсын"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Тергичтин үндөрү"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Тергич обонунун узундугу"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Орточо"</item>
- <item msgid="6177579030803486015">"Узун"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Тез жооптор"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Чалуулар"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Чалууларды бөгөттөө"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Чалууну бөгөттөө убактылуу өчүрүлгөн"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Акыркы 48 саат ичинде бул телефондон өзгөчө кырдаал кызматына байланышкандыктан чалууну бөгөттөө өчүрүлдү. 48 сааттык мөөнөтү аяктагандан кийин ал автоматтык түрдө кайра иштетилет."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Номерлерди импорттоо"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Мурда башка колдонмолор аркылуу айрым чалуучуларга автоматтык түрдө үн почтасы жөнөтүлгүдөй кылып белгилегенсиз."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Номерлерди көрүү"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Импорттоо"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Импорттолбой калды"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Үн каттарын архивдей албай койдук."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Номерди бөгөттөн чыгаруу"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Номер кошуу"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Бул номерлерден келген чалуулар бөгөттөлөт жана үн билдирүүлөрү автоматтык түрдө жок кылынат."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Бул номерлерден келген чалуулар бөгөттөлөт, бирок алар сизге үн билдирүүлөрүн калтыра берет."</string>
- <string name="block_list" msgid="7760188925338078011">"Бөгөттөлгөн номерлер"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> - жараксыз."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> мурунтан эле бөгөттөлгөн."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Чалууну бөгөттөө 48 саатка өчүрүлгөн"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Шашылыш чалуу аткарылгандыктан өчүрүлгөн."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Чалуу каттоо эсептери"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Күйгүзүү"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Уруксаттарды берүү"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Тез терүүнү иштетүү үчүн, \"Байланыштар\" колдонмосуна уруксат бериңиз."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Чалуулар таржымалыңызды көрүү үчүн, \"Телефон\" колдонмосуна уруксат бериңиз."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Байланыштар тизмесин көрүү үчүн, \"Байланыштар\" колдонмосуна уруксат бериңиз."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Үн почтаңызга кирүү үчүн, \"Телефон\" колдонмосуна уруксат бериңиз."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Байланыштарыңызды издөө үчүн, Байланыштарга уруксатты күйгүзүңүз."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Чалуу үчүн, \"Телефон\" колдонмосуна уруксат бериңиз."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Телефон колдонмосунун Тутум жөндөөлөрүнө жазууга уруксаты жок."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Бөгөттөлгөн"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> жүрүп жатат"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Бөгөттөө/спам катары кабарлоо"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Бөгөттөө"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Спам эмес"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Бөгөттөн чыгаруу"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Спам"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> бөгөттөлсүнбү?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Келечекте бул номерден келген бардык чалуулар жана үн каттары бөгөттөлөт."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Чалууну спам катары кабарлоо"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Бул номерден келүүчү бардык чалуулар жана үн каттары бөгөттөлөт. Бул чалуу спам катары кабарланат."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> бөгөттөн чыгарылсынбы?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Номер бөгөттөн чыгарылып, спам эмес деп кабарланат. Чалуулар жана үн каттары спам деп белгиленбейт."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> ак тизме номериби?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Ак тизме"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Номер спам эмес деп кабарланып, андан келе турган чалуулар жана үн каттары спам деп белгиленбейт."</string>
-</resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
deleted file mode 100644
index ac1b6925b..000000000
--- a/res/values-lo/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"ໂທລະສັບ"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"ໂທລະສັບ"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"ໜ້າ​ປັດ​ກົດ​ເບີ​ໂທ​ລະ​ສັບ"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"ໂທລະສັບ"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"ປະຫວັດການໂທ"</string>
- <string name="action_report_number" msgid="4635403959812186162">"ລາຍງານໝາຍເລກທີ່ບໍ່ຖືກຕ້ອງ"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"ອັດ​ສຳ​ເນົາ​ໝາຍເລກ"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"ອັດ​ສຳ​ເນົາ​ການ​ກ່າຍ​ເປັນ​ໜັງ​ສື"</string>
- <string name="action_block_number" msgid="1482657602262262134">"ບ​ລັອກ​ໝາຍເລກ"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> ຖືກບ​ລັອກໄວ້​ແລ້ວ"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"ປົດ​ບ​ລັອກ​ໝາຍເລກ"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> ຖືກບ​ລັອກໄວ້​ແລ້ວ"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ບໍ່​ເຮັດ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"​ລຶບ"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"ແກ້ໄຂໝາຍເລກກ່ອນໂທ"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"ລຶບ​ປະ​ຫວັດ​ການ​ໂທ​"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"ລຶບຂໍ້ຄວາມສຽງ"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"ຈັດເກັບຂໍ້ຄວາມສຽງເຂົ້າແຟ້ມ"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"ແບ່ງປັນຂໍ້ຄວາມສຽງ"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"ລຶບ​ຂໍ້​ຄວາມ​ສຽງ​ແລ້ວ"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"ຈັດເກັບຂໍ້ຄວາມສຽງເຂົ້າແຟ້ມແລ້ວ"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ບໍ່​ເຮັດ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ໄປທີ່ແຟ້ມຈັດເກັບ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"ລຶບ​ປະ​ຫວັດ​ການ​ໂທ​ບໍ?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"ອັນ​ນີ້​ຈະ​ລຶບ​ທຸກ​ສາຍ​ໂທ​ຈາກ​ປະ​ຫວັດ​ຂອງ​ທ່ານ"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"ກຳ​ລັງ​ລຶບ​ປະ​ຫວັດ​ການ​ໂທ…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"ໂທລະສັບ"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"ສາຍທີ່ບໍ່ໄດ້ຮັບ"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"ສາຍບໍ່ໄດ້ຮັບຈາກບ່ອນເຮັດວຽກ"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"ສາຍທີ່ບໍ່ໄດ້ຮັບ"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ສາຍບໍ່ໄດ້ຮັບ"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"ໂທກັບ"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"ຂໍ້ຄວາມ"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> ຂໍ້ຄວາມສຽງ </item>
- <item quantity="one">ຂໍ້ຄວາມສຽງ</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"ຫຼິ້ນ"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"ບໍ່ມີຂໍ້ຄວາມສຽງຈາກ <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"ບໍ່​ສາ​ມາດ​ຫຼິ້ນ​ຂໍ້​ຄວາມ​ສຽງ​ໄດ້"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"ກຳ​ລັງ​ໂຫຼດ​ຂໍ້​ຄວາມ​ສຽງ…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"ກຳລັງຈັດເກັບຂໍ້ຄວາມສຽງເຂົ້າແຟ້ມ…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"ບໍ່​ສາ​ມາດ​ໂຫຼດ​ຂໍ້​ຄວາມ​ສຽງ​ໄດ້"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"ເບີໂທຂໍ້ຄວາມສຽງເທົ່ານັ້ນ"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"ສາຍໂທເຂົ້າເທົ່ານັ້ນ"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"ເບີໂທອອກເທົ່ານັ້ນ"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"ສະເພາະສາຍທີ່ບໍ່ໄດ້ຮັບ"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"ຂໍ້ຄວາມສຽງເຫັນພາບ"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"ເບິ່ງ ແລະຟັງຂໍ້ຄວາມສຽງຂອງທ່ານ ໂດຍບໍ່ຈຳເປັນຕ້ອງໂທຫາໝາຍເລກໃດໜຶ່ງ. ອາດຈະມີຄ່າທຳນຽມຂໍ້ມູນ."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"ການ​ຕັ້ງ​ຄ່າ"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"ບໍ່​ມີ​ການ​ອັບ​ເດດ​ຂໍ້​ຄວາມ​ສຽງ​ຢູ່"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"ຂໍ້​ຄວາມ​ສຽງ​ໃໝ່​ລໍ​ຖ້າ​ຢູ່. ບໍ່​ສາ​ມາດ​ໂຫຼດ​ໄດ້​ໃນ​ເວ​ລາ​ນີ້."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"ຕັ້ງ​ຂໍ້​ຄວາມ​ສຽງ​ຂອງ​ທ່ານ"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"ບໍ່​ມີ​ສຽງ​ຢູ່"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"ຕັ້ງຄ່າ"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"ໂທຫາເບີຂໍ້ຄວາມສຽງ"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"ເລືອກເບີໂທ"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"ເລືອກເບີໂທ"</string>
- <string name="make_primary" msgid="5829291915305113983">"ຈື່ການເລືອກນີ້"</string>
- <string name="description_search_button" msgid="3660807558587384889">"ຊອກຫາ"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"ແປ້ນໂທ"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"ໝາຍເລກທີ່ຈະໂທ"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"​ຫຼິ້ນ ຫຼື​ຢຸດ​ການຫຼິ້ນ"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"​ປິດຫຼື​ເປີດ​ລຳ​ໂພງ​ມື​ຖື"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"​ຊອກ​ຫາ​ຕຳ​​ແໜ່ງ​ຫຼິ້ນ"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"ຫຼ​ຸດ​ອັດ​ຕາ​ການຫຼິ້ນ"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"​ເພີ​່ມ​ອັດ​ຕາ​ການຫຼິ້ນ"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"ປະຫວັດການໂທ"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"ໂຕເລືອກເພີ່ມເຕີມ"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"ປຸ່ມໂທລະສັບ"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"ສະແດງສະເພາະສາຍໂທອອກ"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"ສະແດງສະເພາະສາຍໂທເຂົ້າ"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"ສະແດງສະເພາະສາຍບໍ່ໄດ້ຮັບ"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"ສະແດງສະເພາະຂໍ້ຄວາມສຽງ"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"ສະແດງການໂທທັງໝົດ"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"ເພີ່ມການຂັ້ນເວລາ 2 ວິນາທີ"</string>
- <string name="add_wait" msgid="3360818652790319634">"ເພີ່ມການລໍຖ້າ"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"ການ​ຕັ້ງ​ຄ່າ"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"ສ້າງລາຍຊື່ຜູ້ຕິດຕໍ່ໃໝ່"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"ລາຍຊື່ຜູ້ຕິດຕໍ່ທັງໝົດ"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"ລາຍລະອຽດການໂທ"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"ບໍ່​ມີ​ລາຍ​ລະ​ອຽດ​ຢູ່"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"ໃຊ້ປຸ່ມກົດສັນຍານສຽງ"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"ກັບໄປການໂທທີ່ກຳລັງດຳເນີນຢູ່"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"ເພີ່ມການໂທ"</string>
- <string name="type_incoming" msgid="6502076603836088532">"ສາຍໂທເຂົ້າ"</string>
- <string name="type_outgoing" msgid="343108709599392641">"ສາຍໂທອອກ"</string>
- <string name="type_missed" msgid="2720502601640509542">"ສາຍບໍ່ໄດ້ຮັບ"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"ສາຍ​ວິດີໂອ​ເຂົ້າ"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"ສາຍ​ໂທ​ອອກ​ດ້ວຍ​ວິດີໂອ"</string>
- <string name="type_missed_video" msgid="954396897034220545">"ສາຍ​ວິດີໂອ​ທີ່​ບໍ່​ໄດ້​ຮັບ"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"ຂໍ້ຄວາມສຽງ"</string>
- <string name="type_rejected" msgid="7783201828312472691">"ສາຍ​ໂທ​ທີ່​ຖືກ​ປະ​ຕິ​ເສດ"</string>
- <string name="type_blocked" msgid="3521686227115330015">"ສາຍ​ໂທ​ທີ່​ຖືກບ​ລັອກ"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"ສາຍໂທເຂົ້າ"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"ເປີດຂໍ້ຄວາມສຽງ"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"ເບິ່ງລາຍຊື່ຜູ່ຕິດຕໍ່ <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"ໂທຫາ <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"ລາຍລະອຽດ​ລາຍຊື່​ຜູ່ຕິດຕໍ່​ສຳລັບ <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> ການໂທ."</string>
- <string name="description_video_call" msgid="2933838090743214204">"ການ​ໂທ​ດ້ວຍ​ວິ​ດີ​ໂອ."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"ສົ່ງ SMS ຫາ <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"ຂໍ້ຄວາມສຽງທີ່ຍັງບໍ່ໄດ້ຟັງ"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"ເລີ່ມການຊອກຫາດ້ວຍສຽງ"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"ໂທ​ຫາ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"ບໍ່ຮູ້ຈັກ"</string>
- <string name="voicemail" msgid="3851469869202611441">"ຂໍ້ຄວາມສຽງ"</string>
- <string name="private_num" msgid="6374339738119166953">"ເບີສ່ວນໂຕ"</string>
- <string name="payphone" msgid="7726415831153618726">"ຕູ້​ໂທ​ລະ​ສັບ​ສາ​ທາ​ລະ​ນະ"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> ວິນາທີ"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> ນ​ທ <xliff:g id="SECONDS">%s</xliff:g> ວິ"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> ເວລາ <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"ບໍ່​ສາ​ມາດ​ໂທ​ຫາ​ເບີ​ນີ້​ໄດ້"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"ເພື່ອຕັ້ງຄ່າຂໍ້ຄວາມສຽງ ໃຫ້ໄປທີ່ ເມນູ &gt; ການຕັ້ງຄ່າ."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"ເພື່ອໂທຫາເບີຂໍ້ຄວາມສຽງ ທ່ານຕ້ອງປິດໂໝດຢູ່ເທິງຍົນກ່ອນ."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"ກຳລັງໂຫລດ..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"ກຳລັງໂຫລດຈາກ SIM card..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"ລາຍຊື່ຜູ່ຕິດຕໍ່ SIM card"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"ບໍ່​ມີ​ແອັບຯ​ລາຍ​ຊື່​ຕິດ​ຕໍ່​ຢູ່"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"ບໍ່​ມີ​ການ​ຊອກ​ຫາ​ດ້ວຍ​ສຽງ​ຢູ່"</string>
- <string name="call_not_available" msgid="8941576511946492225">"ບໍ່​ສາ​ມາດ​ໂທ​ໄດ​້​ເນື່ອງ​ຈາກ​ແອັບ​ພ​ລິ​ເຄ​ຊັນ​ໂທ​ລະ​ສັບ​ຖືກ​ປິດ​ການ​ນຳ​ໃຊ້​ໄວ້."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"ບໍ່​ມີ​ແອັ​ບຯ​ສຳ​ລັບ​ສິ່ງນັ້ນ​ຢູ່​ໃນ​ອຸ​ປະ​ກອນ​ນີ້"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"ຊອກຫາລາຍຊື່ຜູ່ຕິດຕໍ່"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"ເພີ່ມ​ເບີ​ໂທ​ລະ​ສັບ ຫຼື ຊອກ​ຫາ​ລາຍ​ຊື່​ຕິດ​ຕໍ່"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"ປະ​ຫວັດ​ການ​ໂທ​ຂອງ​ທ່ານ​ຫວ່າງ​ເປົ່າ"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"​ໂທ​ອອກ"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"ທ່ານບໍ່ມີສາຍທີ່ບໍ່ໄດ້ຮັບ."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"ກ່ອງເຂົ້າ​ຂໍ້​ຄວາມ​ສຽງ​ຂອງ​ທ່ານ​ຫວ່າງ​ເປົ່າ."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"ແຟ້ມຈັດເກັບຂໍ້ຄວາມສຽງຂອງທ່ານຫວ່າງເປົ່າຢູ່."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"ສະ​ແດງ​ສະເພາະລາຍການທີ່ນິຍົມເທົ່ານັ້ນ"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"ປະຫວັດການໂທ"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"ແຟ້ມຈັດເກັບຂໍ້ຄວາມສຽງ"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"ທັງໝົດ"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"ສາຍທີ່ບໍ່ໄດ້ຮັບ"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"ຂໍ້ຄວາມສຽງ"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"ການລັອກແບບໃໝ່ທີ່ງ່າຍດາຍ"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"ເພື່ອປົກປ້ອງທ່ານໄດ້ດີຂຶ້ນ, ແອັບໂທລະສັບຕ້ອງການປ່ຽນວິທີການບລັອກ. ເບີໂທທີ່ທ່ານບລັອກໄວ້ຈະປິດກັ້ນທັງການໂທ ແລະ ຂໍ້ຄວາມ ແລະ ອາດມີການແບ່ງປັນກັບແອັບອື່ນໆນຳ."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"ອະນຸຍາດ"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"ບ​ລັອກ <xliff:g id="NUMBER">%1$s</xliff:g> ບໍ?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"ການໂທຈາກເບີໂທນີ້ຈະຖືກບລັອກໄວ້ ແລະຂໍ້ຄວາມສຽງຈະຖືກລຶບໂດຍອັດຕະໂນມັດ."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"ການໂທຈາກເບີນີ້ຈະຖືກບລັອກໄວ້, ແຕ່ຜູ່ໂທອາດຈະຍັງສາມາດຝາກຂໍ້ຄວາມສຽງໃຫ້ທ່ານໄດ້."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"ທ່ານຈະບໍ່ໄດ້ຮັບສາຍ ຫຼື ຂໍ້ຄວາມຈາກເບີນີ້ອີກຕໍ່ໄປ."</string>
- <string name="block_number_ok" msgid="770551992296781873">"ບລັອກ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"ປົດ​ບ​ລັອກ <xliff:g id="NUMBER">%1$s</xliff:g> ບ​ໍ?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"​ປົດ​ບລັອກ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"ການ​ໂທ​ດ່ວນ"</string>
- <string name="tab_history" msgid="2563144697322434940">"ປະຫວັດການໂທ"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"ລາຍ​ຊື່​ຜູ່​ຕິດ​ຕໍ່"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"ຂໍ້ຄວາມສຽງ"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"ລຶບອອກຈາກລາຍການທີ່ມັກແລ້ວ"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"ຍົກເລີກ"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"ໂທ​ຫາ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"ສ້າງລາຍຊື່ຜູ້ຕິດຕໍ່ໃໝ່"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"ເພີ່ມ​ໃສ່​ລາຍ​ຊື່"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"ສົ່ງ SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"​ໂທ​ອອກ​ດ້ວຍ​ວິ​ດີ​ໂອ"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"ບ​ລັອກ​ໝາຍ​ເລກ"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> ສາຍທີ່ບໍ່ໄດ້ຮັບໃໝ່"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"ບໍ່​ມີ​ຜູ້​ໃດ​ຢູ່​ໃນ​ການ​ໂທ​ດ່ວນ​ຂອງ​ທ່ານ​ເທື່ອ"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"ເພີ່ມ​ລາຍ​ການ​ທີ່​ມັກ"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"ທ່ານ​ບໍ່​ມີ​ລາຍ​ຊື່​ໃດ​ເທື່ອ"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"ເພີ່ມ​ລາຍ​ຊື່"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"ແຕະ​ຮູບ ເພື່ອ​ເບິ່ງ​ທຸກ​ເລກ​ໝາຍ ຫຼື​ແຕະ &amp; ຄ້າງ​ໄວ້ ເພື່ອ​ບັນ​ທຶກ"</string>
- <string name="remove_contact" msgid="1080555335283662961">"​ລຶບ​ອອກ"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"​ໂທ​ດ້ວຍ​ວິ​ດີ​ໂອ"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"ສົ່ງຂໍ້ຄວາມ"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"ລາຍລະອຽດ​ການໂທ"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"ໂທຫາ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"ສາຍ​ບໍ່​ໄດ້​ຮັບ​ຈາກ <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"ຮັບ​ສາຍ​ໂທ​ຈາກ <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"ຂໍ້​ຄວາມ​ສຽງ​ບໍ່​ໄດ້​ອ່ານ​ຈາກ <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"ຂໍ້​ຄວາມ​ສຽງ​ຈາກ <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"ໂທ​ຫາ <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"ຢູ່​ເທິງ <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"ຜ່ານ <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"ຜ່ານ <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"ຢູ່ <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, ຜ່ານ <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> ຜ່ານ <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"ໂທ"</string>
- <string name="description_call_action" msgid="4000549004089776147">"ໂທ​ຫາ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"ການ​ໂທວິ​ດີ​ໂອ <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"​ຟັງ​ຂໍ້​ຄວາມ​ສຽງ​ຈາກ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"ຫຼິ້ນຂໍ້ຄວາມສຽງຈາກ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"ຢຸດຂໍ້ຄວາມສຽງຈາກ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"ລຶບຂໍ້ຄວາມສຽງຈາກ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> ຂໍ້​ຄວາມ​ສຽງ​ໃໝ່</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> ຂໍ້​ຄວາມ​ສຽງ​ໃໝ່</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"ສ້າງ​ລາຍ​ຊື່​ສຳ​ລັບ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"ເພີ່ມ <xliff:g id="NAMEORNUMBER">^1</xliff:g> ໃສ່​ລາຍ​ຊື່​ທີ່​ມີ​ຢູ່"</string>
- <string name="description_details_action" msgid="2433827152749491785">"ລາຍລະອຽດ​ການ​ໂທ​ສຳລັບ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"ລຶບ​ຈາກ​ປະ​ຫວັດ​ການ​ໂທ​ແລ້ວ"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"ມື້ນີ້"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"ມື້​ວານ​ນີ້"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"ເກົ່າກວ່າ"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"ບັນ​ຊີ​ລາຍ​ຊື່​ໂທ"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"ເປີດ​ລຳໂພງ."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"ປິດ​ລຳໂພງ."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"ຫຼິ້ນໄວຂຶ້ນ."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"ຫຼິ້ນຊ້າລົງ."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"ເລີ່ມຫຼືຢຸດ​ການ​ຫຼິ້ນຊົ່ວຄາວ."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"ຕົວເລືອກການສະແດງຜົນ"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"ສຽງ ແລະ​ສັ່ນ"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"​ການ​ຊ່ວຍ​ເຂົ້າ​ເຖິງ"</string>
- <string name="ringtone_title" msgid="760362035635084653">"ຣິງໂທນໂທລະສັບ"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"ສັ່ນ​ເຕືອນ​ເມື່ອ​ມີ​ການ​ໂທ​ເຂົ້າ"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"ສຽງ​ກົດ​ປຸ່ມ​ໂທ"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"ຄວາມ​ຍາວ​ສຽງ​ແຜ່ນ​ກົດ"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"ປົກ​ກ​ະ​ຕິ"</item>
- <item msgid="6177579030803486015">"ຍາວ"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"ການຕອບກັບດ່ວນ"</string>
- <string name="call_settings_label" msgid="313434211353070209">"​ການ​ໂທ"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"ການບ​ລັອກ​ສາຍ​ໂທ"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"ການບ​ລັອກ​ສາຍ​ໂທ​ປິດ​ຊົ່ວ​ຄາວ"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"ການບ​ລັອກ​ສາຍ​ໂທ​ຖືກ​ປິດ​ໃຊ້​ງານ​ແລ້ວ ເພາະ​ວ່າ​ທ່ານ​ໄດ້​ຕິດ​ຕໍ່​ຫາ​ຝ່າຍ​ບໍ​ລິ​ການ​ສຸກ​ເສີນ​ຈາກ​ໂທ​ລະ​ສັບ​ນີ້​ພາຍ​ໃນ 48 ຊົ່ວ​ໂມງ​ສຸດ​ທ້າຍ. ມັນ​ຈະ​ຖືກ​ເປີດ​ໃຊ້​ງານ​ອີກ​ໂດຍ​ອັດ​ຕະ​ໂນ​ມັດ ເມື່ອ​ໝົດ​ໄລ​ຍະ 48 ຊົ່ວ​ໂມງ​ໄປ​ແລ້ວ."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"ນຳ​ຕົວ​ເລກ​ເຂົ້າ"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"ຜ່ານ​ມາ​ທ່ານ​ໄດ້​ໝາຍ​ຜູ້​ໂທ​ບາງ​ຄົນ​ໃຫ້​ຖືກ​ສົ່ງ​​ຫາ​ຂໍ້​ຄວາມ​ສຽງໂດຍ​ອັດ​ຕະ​ໂນ​ມັດ​ຜ່ານ​ແອັບ​ອື່ນ​."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"ເບິ່ງ​ຕົວ​ເລກ"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"ນໍາເຂົ້າ"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"ນຳ​ເຂົ້າ​ລົ້ມ​ເຫລວ"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"ລົ້ມເຫລວໃນການຈັດເກັບຂໍ້ຄວາມສຽງເຂົ້າແຟ້ມ."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"ປົດ​ບ​ລັອກ​ໝາຍ​ເລກ"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"ເພີ່ມໝາຍເລກ"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"ການໂທຈາກເບີໂທເຫຼົ່ານີ້ຈະຖືກບລັອກໄວ້ ແລະຂໍ້ຄວາມສຽງຈະຖືກລຶບໂດຍອັດຕະໂນມັດ."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"ການໂທຈາກເບີໂທເຫຼົ່ານີ້ຈະຖືກບລັອກໄວ້, ແຕ່ພວກເຂົາອາດຈະຍັງສາມາດຝາກຂໍ້ຄວາມສຽງໃຫ້ທ່ານໄດ້."</string>
- <string name="block_list" msgid="7760188925338078011">"ເບີໂທທີ່ບລັອກໄວ້"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> ບໍ່ຖືກຕ້ອງ."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> ຖືກ​ບ​ລັອກ​ແລ້ວ."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"ການບ​ລັອກ​ສາຍ​ໂທ​ຖືກ​ປິດ​ໃຊ້​ງານ​ແລ້ວ​ເປັນ​ເວ​ລາ 48 ຊົ່​ວ​ໂມງ"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"ປິດ​ໃຊ້​ງານ​ແລ້ວ ເພາະ​ວ່າ​ໄດ້​ມີ​ການ​ໂທ​ສຸກ​ເສີນ."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"ບັນ​ຊີ​ໂທ"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"ເປີດ​"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"ຕັ້ງ​ການ​ອະ​ນຸ​ຍາດ"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"ເພື່ອ​ເປີດ​ໃຊ້​ງານ​ການໂທດ່ວນ, ເປີດ​ການ​ອະ​ນຸ​ຍາດ​ລາຍ​ຊື່."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"ເພື່ອ​ເບິ່ງ​ບັນ​ທຶກ​ການ​ໂທ​ຂອງ​ທ່ານ, ເປີດ​ການ​ອະ​ນຸ​ຍາດ​ໂທ​ລະ​ສັບ."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"ເພື່ອ​ເບິ່ງ​ລາຍ​ຊື່​ຂອງ​ທ່ານ, ເປີດ​ການ​ອະ​ນຸ​ຍາດ​ລາຍ​ຊື່"</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"ເພື່ອ​ເຂົ້າ​ຫາ​ຂໍ້​ຄວາມ​ສຽງ​ຂອງ​ທ່ານ, ເປີດ​ການ​ອະ​ນຸ​ຍາດ​ໂທ​ລະ​ສັບ."</string>
- <string name="permission_no_search" msgid="84152933267902056">"ເພື່ອຄົ້ນຫາລາຍາຊື່ຂອງທ່ານ, ໃຫ້ເປີດການອະນຸຍາດລາຍຊື່."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"ເພື່ອ​ເຮັດການໂທ, ເປີດ​ການ​ອະ​ນຸ​ຍາດ​ໂທ​ລະ​ສັບ."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"ແອັບໂທລະສັບບໍ່ມີການອະນຸຍາດໃຫ້ຂຽນໃສ່ການຕັ້ງຄ່າລະບົບ."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"ບລັອກໄວ້ແລ້ວ"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"ຢູ່ໃນສາຍ <xliff:g id="NAMEORNUMBER">^1</xliff:g> ຢູ່"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"ບລັອກ/ລາຍງານສະແປມ"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"ບລັອກ"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"ບໍ່ແມ່ນສະແປມ"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"ຍົກເລີກການບລັອກ"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"ສະແປມ"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"ບລັອກ <xliff:g id="NUMBER">%1$s</xliff:g> ບໍ?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"ການໂທ ແລະ ຂໍ້ຄວາມສຽງໃນອະນາຄົດຈາກເບີໂທນີ້ຈະຖືກບລັອກ."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"ລາຍງານວ່າເປັນສະແປມ"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"ການໂທ ແລະ ຂໍ້ຄວາມສຽງໃນອະນາຄົດຈາກເບີໂທນີ້ຈະຖືກບລັອກ. ການໂທນີ້ຈະຖືກລາຍງານວ່າເປັນສະແປມ."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"ປົດບລັອກ <xliff:g id="NUMBER">%1$s</xliff:g> ບໍ?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"ເບີໂທນີ້ຈະຖືກປົດລັອກ ແລະ ລາຍງານວ່າບໍ່ແມ່ນສະແປມ. ການໂທ ແລະ ຂໍ້ຄວາມສຽງໃນອະນາຄົດຈະບໍ່ຖືກລະບຸວ່າເປັນສະແປມ."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"ລະບຸ <xliff:g id="NUMBER">%1$s</xliff:g> ໃສ່ໃນລາຍການອະນຸຍາດບໍ?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"ລາຍການອະນຸຍາດ"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"ການໂທ ແລະ ຂໍ້ຄວາມສຽງໃນອະນາຄົດຈາກເບີໂທນີ້ຈະບໍ່ຖືກລະບຸວ່າເປັນສະແປມ. ເບີໂທນີ້ຈະຖືກລາຍງານວ່າບໍ່ແມ່ນສະແປມ."</string>
-</resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
deleted file mode 100644
index a9cf50936..000000000
--- a/res/values-lt/strings.xml
+++ /dev/null
@@ -1,292 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefonas"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefonas"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Telefono skambinimo skydelis"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefonas"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Skambučių istorija"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Pranešti apie netikslų numerį"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopijuoti numerį"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopijuoti transkribuotą tekstą"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blokuoti numerį"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> užblokuotas"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Panaikinti numerio blokavimą"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Panaikintas <xliff:g id="NUMBER">%1$s</xliff:g> blokavimas"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ANULIUOTI"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Ištrinti"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Redaguoti numerį prieš skambinant"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Išvalyti skambučių istoriją"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Ištrinti balso pašto pranešim."</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Archyvuoti balso pašto pran."</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Bendrinti balso paštą"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Balso pšt. ištrintas"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Balso pašto pran. suarchyvuoti"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ANULIUOTI"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"Į ARCHYVĄ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Išvalyti skambučių istoriją?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Bus ištrinti visi skambučiai iš istorijos"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Išvaloma skambučių istorija..."</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefonas"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Praleisti skambučiai"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Praleistas darbo skambutis"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Praleisti skambučiai"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"Praleistų skambučių: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Perskambinti"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Siųsti pranešimą"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> balso pašto pranešimas </item>
- <item quantity="few"> <xliff:g id="COUNT">%1$d</xliff:g> balso pašto pranešimai </item>
- <item quantity="many"> <xliff:g id="COUNT">%1$d</xliff:g> balso pašto pranešimo </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> balso pašto pranešimų </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Paleisti"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nauji b. pašto pran. iš <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Nepavyko paleisti balso pašto pranešimo"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Įkeliamas balso pašto pranešimas..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Archyvuojami balso pašto pranešimai…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Nepavyko įkelti balso pašto pranešimo"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Tik skambučiai su balso paštu"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Tik gaunami skambučiai"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Tik siunčiami skambučiai"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Tik praleisti skambučiai"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Vaizdinis balso paštas"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Peržiūrėkite ir klausykite balso pašto pranešimų neskambindami telefono numeriu. Gali būti taikomi duomenų mokesčiai."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Nustatymai"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Balso pašto naujiniai nepasiekiami"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Laukiantis naujas balso pašto pranešimas. Negalima dabar įkelti."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Nustatykite balso paštą"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Garso įrašas nepasiekiamas"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Nustatyti"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Skamb. į balso pšt."</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Pasirinkite numerį"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Pasirinkite numerį"</string>
- <string name="make_primary" msgid="5829291915305113983">"Atsiminti šį pasirinkimą"</string>
- <string name="description_search_button" msgid="3660807558587384889">"ieškoti"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"rinkti numerį"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"renkamas numeris"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Paleisti arba sustabdyti atkūrimą"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Įjungti arba išjungti garsiakalbį"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Ieškoti atkūrimo pozicijos"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Sumažinti atkūrimo spartą"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Padidinti atkūrimo spartą"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Skambučių istorija"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Daugiau parinkčių"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"skambinimo skydelis"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Rodyti tik išsiunčiamus"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Rodyti tik gaunamus"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Rodyti tik praleistus"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Rodyti tik balso pšt. praneš."</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Rodyti visus skambučius"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Pridėti 2 sek. pauzę"</string>
- <string name="add_wait" msgid="3360818652790319634">"Pridėti laukimą"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Nustatymai"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Naujas kontaktas"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Visi kontaktai"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Išsami skambučio informacija"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Išsami informacija nepasiekiama"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Naudoti jutiklinę klaviatūrą"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Grįžti prie vykdomo skambučio"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Pridėti skambutį"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Gaunamasis skambutis"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Siunčiamasis skambutis"</string>
- <string name="type_missed" msgid="2720502601640509542">"Praleistas skambutis"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Gaunamasis vaizdo skambutis"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Siunčiamasis vaizdo skambutis"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Praleistas vaizdo skambutis"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Balso paštas"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Atmestas skambutis"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Užblokuotas skambutis"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Gaunami skambučiai"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Paleisti balso pašto pranešimus"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Žiūrėti kontaktą <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Skambinti <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Išsami kontaktinė informacija: <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"Skambučių: <xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Vaizdo skambutis."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Siųsti SMS <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Neklausytas balso pašto pranešimas"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Pradėti paiešką balsu"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Skambinti <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Nežinomas"</string>
- <string name="voicemail" msgid="3851469869202611441">"Balso paštas"</string>
- <string name="private_num" msgid="6374339738119166953">"Privatus numeris"</string>
- <string name="payphone" msgid="7726415831153618726">"Taksofonas"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> sek."</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min. <xliff:g id="SECONDS">%s</xliff:g> sek."</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Negalima skambinti šiuo numeriu"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Jei norite nustatyti balso paštą, eikite į „Meniu“ &gt; „Nustatymai“."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Jei norite skambinti į balso paštą, išjunkite lėktuvo režimą."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Įkeliama..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Įkeliama iš SIM kortelės..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM kortelės adresatai"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Nepasiekiama jokia kontaktų programa"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Paieška balsu nepasiekiama"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Negalima skambinti telefonu, nes Telefono programa išjungta."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Šiame įrenginyje nėra tam skirtos programos"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Ieškokite kontaktų"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Prid. nr. arba iešk. kontaktų"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Skambučių istorija yra tuščia"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Skambinti"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Nėra jokių praleistų skambučių."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Balso pašto gautųjų aplankas yra tuščias."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Jūsų balso pašto pranešimų archyvas tuščias."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Rodyti tik mėgstamiausius"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Skambučių istorija"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Balso pašto pranešimų archyvas"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Visi"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Praleisti"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Balso paštas"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Naujas supaprast. blokavimas"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Siekiant geriau jus apsaugoti, Telefono programa turi pakeisti blokavimo veikimo būdą. Blokuotų numerių skambučiai ir teksto pranešimai nebus priimami ir jie gali būti bendrinami su kitomis programomis."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Leisti"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Blokuoti numerį <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Skambučiai iš šio numerio bus užblokuoti, o balso pašto pranešimai bus automatiškai ištrinti."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Skambučiai iš šio numerio bus užblokuoti, tačiau skambintojas vis tiek galės palikti jums balso pašto pranešimus."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Nebegausite skambučių ar teksto pranešimų iš šio numerio."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOKUOTI"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Panaikinti numerio <xliff:g id="NUMBER">%1$s</xliff:g> blokavimą?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"PANAIKINTI BLOKAVIMĄ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Spartusis rinkimas"</string>
- <string name="tab_history" msgid="2563144697322434940">"Skambučių istorija"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontaktai"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Balso paštas"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Pašalintas iš adresyno"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Anuliuoti"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Skambinti numeriu <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Kurti naują kontaktą"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Pridėti prie kontakto"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Siųsti SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Atlikti vaizdo skambutį"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blokuoti numerį"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"Naujų praleistų skambučių: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Dar nieko neįtraukėte į sparčiojo rinkimo sąrašą"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Pridėti mėgstamiausią"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Dar neturite kontaktų"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Pridėti kontaktą"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Palieskite vaizdą, kad peržiūrėtumėte visus numerius, arba palieskite ir laikykite, kad pertvarkytumėte"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Pašalinti"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Vaizdo skambutis"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Siųsti pranešimą"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Išsami skambučio informacija"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Skambinti <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Praleistas skambutis: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Atsakytas skambutis: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Neskaitytas balso pašto pranešimas: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Balso pašto pranešimas: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Skambutis: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"numeriu <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"numeriu <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, numeriu <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> numeriu <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Skambinti"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Skambinti <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Vaizdo skambutis <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Klausyti balso pašto nuo <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Leisti balso pašto pranešimą iš <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pristabdyti balso pašto pranešimą iš <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Ištrinti balso pašto pranešimą iš <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> naujas balso pašto pranešimas</item>
- <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> nauji balso pašto pranešimai</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> naujo balso pašto pranešimo</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> naujų balso pašto pranešimų</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Sukurti <xliff:g id="NAMEORNUMBER">^1</xliff:g> kontaktą"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Pridėti <xliff:g id="NAMEORNUMBER">^1</xliff:g> prie esamo kontakto"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Išsami skambučio informacija (<xliff:g id="NAMEORNUMBER">^1</xliff:g>)"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Ištrinta iš skambučių istorijos"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Šiandien"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Vakar"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Senesni"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Skambučių sąrašas"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Įjungti garsiakalbį."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Išjungti garsiakalbį."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Leisti greičiau."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Leisti lėčiau."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Pradėti arba pristabdyti atkūrimą."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Pateikties parinktys"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Garsai ir vibravimas"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Pritaikymas neįgaliesiems"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Telefono skambėjimo tonas"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Taip pat vibruoti, kai skamb."</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Skambinimo skydelio garsai"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Skambinimo skydelio tono trukmė"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Įprastas"</item>
- <item msgid="6177579030803486015">"Ilgas"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Greiti atsakai"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Skambučiai"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Skambučių blokavimas"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Skamb. blokavimo funkcija laikinai išj."</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Skambučių blokavimo funkcija buvo išjungta, nes iš šio telefono buvote susisiekę su pagalbos tarnybomis per pastarąsias 48 val. Ši funkcija bus automatiškai įgalinta iš naujo, kai 48 val. laikotarpis pasibaigs."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importuoti numerius"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Naudodami kitas programas anksčiau buvote pažymėję, kad kai kurie skambintojai būtų automatiškai nusiųsti į balso paštą."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Žr. skaičius"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importuoti"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Importuoti nepavyko"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Nepavyko suarchyvuoti balso pašto pran."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Panaikinti numerio blokavimą"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Pridėti numerį"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Skambučiai iš šių numerių bus užblokuoti, o balso pašto pranešimai bus automatiškai ištrinti."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Skambučiai iš šių numerių bus užblokuoti, tačiau skambintojai vis tiek galės palikti jums balso pašto pranešimus."</string>
- <string name="block_list" msgid="7760188925338078011">"Užblokuoti numeriai"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> yra netinkamas numeris."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> jau užblokuotas."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Skambučių blokavimas išjungtas 48 val."</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Išjungta, nes skambinta pagalbos numeriu."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Skambinimo paskyros"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Įjungti"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Nustatyti leidimus"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Jei norite įgalinti spartųjį rinkimą, įjunkite Kontaktų programos leidimą."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Jei norite peržiūrėti skambučių žurnalą, įjunkite Telefono programos leidimą."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Jei norite peržiūrėti kontaktus, įjunkite Kontaktų programos leidimą."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Jei norite pasiekti balso paštą, įjunkite Telefono programos leidimą."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Kad galėtumėte ieškoti kontaktų, įjunkite Kontaktų leidimus."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Jei norite paskambinti, įjunkite Telefono programos leidimą."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Telefono programa neturi leidimo keisti sistemos nustatymų."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Užblokuota"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> aktyvus"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blokuoti / pran. apie šlamštą"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blokuoti"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Ne šlamštas"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Panaikinti blokavimą"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Šlamštas"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Blokuoti numerį <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Būsimi skambučiai ir balso pašto pranešimai iš šio numerio bus užblokuoti."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Pr. apie skamb. kaip apie šl."</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Būsimi skamb. ir b. pšt. pran. iš šio nr. bus užblok. Apie šį skamb. bus pran. kaip apie šlamštą."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Panaikinti numerio <xliff:g id="NUMBER">%1$s</xliff:g> blokavimą?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Šio nr. blok. bus pan. ir apie jį bus pran. kaip ne apie šl. siunt. Būs. sk. ir b. pšt. pr. nebus nust. kaip šl."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Įtraukti numerį <xliff:g id="NUMBER">%1$s</xliff:g> į baltąjį sąrašą?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Įtraukti į baltąjį sąrašą"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Būs. sk. ir b. pšt. pr. iš šio nr. nebus nust. kaip šl. Apie šį nr. bus pr. kaip ne apie šl. siunt."</string>
-</resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
deleted file mode 100644
index 55c0adaa2..000000000
--- a/res/values-lv/strings.xml
+++ /dev/null
@@ -1,290 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Tālrunis"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Tālrunis"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Tālruņa numuru sastādīšanas tastatūra"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Zvanīt"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Zvanu vēsture"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Ziņot par nepareizu numuru"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopēt numuru"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopēt transkripciju"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Bloķēt numuru"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> bloķēts"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Atbloķēt numuru"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> atbloķēts"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ATSAUKT"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Dzēst"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Rediģēt numuru pirms zvanīšanas"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Dzēst zvanu vēsturi"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Dzēst balss pasta ziņojumu"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arhivēt balss pasta ziņojumu"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Kopīgot balss pasta ziņojumu"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Balss pasts dzēsts"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Balss pasta ziņojums arhivēts"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ATSAUKT"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"UZ ARHĪVU"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Vai dzēst zvanu vēsturi?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Tiks dzēsti visi vēsturē saglabātie zvani."</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Notiek zvanu vēstures dzēšana…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Tālruņa zvans"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Neatbildēts zvans"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Neatbildēts darba zvans"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Neatbildēti zvani"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> neatbildēti zvani"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Atzvanīt"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Sūtīt ziņojumu"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="zero"><xliff:g id="COUNT">%1$d</xliff:g> balss pasta ziņojumi </item>
- <item quantity="one"><xliff:g id="COUNT">%1$d</xliff:g> balss pasta ziņojums </item>
- <item quantity="other"><xliff:g id="COUNT">%1$d</xliff:g> balss pasta ziņojumi </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Atskaņot"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Jauns b. pasta ziņ. no: <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Nevarēja atskaņot balss pasta ziņojumu."</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Notiek balss pasta ziņojumu ielāde…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Tiek arhivēts balss pasta ziņojums…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Nevarēja ielādēt balss pasta ziņojumu."</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Tikai balss pasta zvani"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Tikai ienākošie zvani"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Tikai izejošie zvani"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Tikai neatbildētie zvani"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Vizuālais balss pasts"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Skatiet un klausieties balss pasta ziņojumus, nezvanot uz numuru. Var tikt piemērota maksa par datu pārraidi."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Iestatījumi"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Balss pasta atjauninājumi nav pieejami."</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Saņemti jauni balss pasta ziņojumi. Pašlaik tos nevar ielādēt."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Iestatiet balss pastu."</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio nav pieejams."</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Iestatīt"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Zvanīt balss pastam"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Numura izvēlēšanās"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Numura izvēlēšanās"</string>
- <string name="make_primary" msgid="5829291915305113983">"Atcerēties šo izvēli"</string>
- <string name="description_search_button" msgid="3660807558587384889">"meklēt"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"sastādīt numuru"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"sastādītais numurs"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Atskaņot vai apturēt atskaņošanu"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Ieslēgt vai izslēgt mikrofonu ar skaļruni"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Meklēt atskaņošanas pozīciju"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Samazināt atskaņošanas ātrumu"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Palielināt atskaņošanas ātrumu"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Zvanu vēsture"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Vairāk opciju"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"numura sastādīšanas tastatūra"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Rādīt tikai izejošos zvanus"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Rādīt tikai ienākošos zvanus"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Rādīt tikai neatbildētos zvanus"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Rādīt tikai balss pasta ziņ."</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Rādīt visus zvanus"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Pievienot 2 sekundes ilgu pauzi"</string>
- <string name="add_wait" msgid="3360818652790319634">"Pievienot gaidīšanu"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Iestatījumi"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Jauna kontaktpersona"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Visas kontaktpersonas"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Informācija par zvanu"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Detalizēta informācija nav pieejama."</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Izmantot skārientoņu tastatūru"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Atgriezties pie pašreizējā zvana"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Pievienot zvanu"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Ienākošais zvans"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Izejošais zvans"</string>
- <string name="type_missed" msgid="2720502601640509542">"Neatbildēts zvans"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Ienākošs videozvans"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Izejošs videozvans"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Neatbildēts videozvans"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Balss pasts"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Noraidīts zvans"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Bloķēts zvans"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Ienākošie zvani"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Atskaņot balss pasta ziņojumu"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Skatīt kontaktpersonu <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Zvanīt šim: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Kontaktpersonas informācija: <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> zvani."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videozvans"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Sūtīt īsziņu šai kontaktpersonai: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Nenoklausīti balss pasta ziņojumi"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Sākt meklēšanu ar balsi"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Zvanīt: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Nezināms"</string>
- <string name="voicemail" msgid="3851469869202611441">"Balss pasts"</string>
- <string name="private_num" msgid="6374339738119166953">"Privāts numurs"</string>
- <string name="payphone" msgid="7726415831153618726">"Maksas tālrunis"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> plkst. <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Uz šo numuru nevar piezvanīt."</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Lai iestatītu balss pastu, atveriet sadaļu Izvēlne &gt; Iestatījumi."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Lai piezvanītu balss pastam, vispirms izslēdziet lidojuma režīmu."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Notiek ielāde..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Notiek ielāde no SIM kartes..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Kontaktpersonas SIM kartē"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Nav pieejama neviena kontaktpersonu lietotne."</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Meklēšana ar balsi nav pieejama."</string>
- <string name="call_not_available" msgid="8941576511946492225">"Nevar veikt tālruņa zvanu, jo lietojumprogramma Tālrunis tika atspējota."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Šajā ierīcē nav nevienas šai darbībai piemērotas lietotnes."</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Meklēt kontaktpersonas"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Pievienojiet numuru vai meklējiet kontaktpersonas"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Jūsu zvanu vēsturē nav ierakstu."</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Zvanīt"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Jums nav neatbildētu zvanu."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Jūsu balss pasta iesūtne ir tukša."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Jūsu balss pasta ziņojumu arhīvs ir tukšs."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Rādīt tikai izlasi"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Zvanu vēsture"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Balss pasta ziņojumu arhīvs"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Visi"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Neatb."</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Balss p."</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Jauna, vienkāršota bloķēšanas funkcija"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Lai jūs labāk aizsargātu, lietotnē Tālrunis ir jāmaina bloķēšanas darbība. Turpmāk no bloķētajiem numuriem nevarēs saņemt zvanus un īsziņas, un bloķētos numurus varēs kopīgot ar citām lietotnēm."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Atļaut"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Vai bloķēt numuru <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"No šī numura saņemtie zvani tiks bloķēti, un balss pasta ziņojumi tiks automātiski dzēsti."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"No šī numura saņemtie zvani tiks bloķēti, taču zvanītājs joprojām varēs jums atstāt balss pasta ziņojumus."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Jūs vairs nesaņemsiet zvanus un īsziņas no šī numura."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOĶĒT"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Vai atbloķēt numuru <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ATBLOĶĒT"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Ātrie zvani"</string>
- <string name="tab_history" msgid="2563144697322434940">"Zvanu vēsture"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontaktpersonas"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Balss pasts"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Noņemts no izlases"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Atsaukt"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Zvanīt: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Izveidot jaunu kontaktpersonu"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Pievienot kontaktpersonai"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Sūtīt īsziņu"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Veikt videozvanu"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Bloķēt numuru"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"Jauni neatbildēti zvani: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Ātro zvanu sarakstā vēl nav nevienas kontaktpersonas."</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Pievienot izlasei"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Jums vēl nav nevienas kontaktpersonas."</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Pievienot kontaktpersonu"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Pieskarieties attēlam, lai skatītu visus numurus, vai pieskarieties attēlam un turiet to, lai veiktu pārkārtošanu."</string>
- <string name="remove_contact" msgid="1080555335283662961">"Noņemt"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videozvans"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Sūtīt ziņojumu"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Informācija par zvanu"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Zvanīt: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Neatbildēts zvans no: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Atbildēts zvans no: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Nelasīts balss pasts no <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Balss pasts no <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Izejošs zvans: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"kontā <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"izmantojot numuru <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"izmantojot numuru <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"kontā <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, izmantojot numuru <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, izmantojot numuru <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Zvanīt"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Zvanīt: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Zvaniet kontaktpersonai <xliff:g id="NAMEORNUMBER">^1</xliff:g>, izmantojot videozvanu."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Klausīties balss pasta ziņojumu no: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Atskaņot balss pasta ziņojumu no: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pārtraukt balss pasta ziņojuma atskaņošanu no: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Dzēst balss pasta ziņojumu no: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="zero"><xliff:g id="COUNT_1">%d</xliff:g> jauni balss pasta ziņojumi</item>
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> jauns balss pasta ziņojums</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> jauni balss pasta ziņojumi</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Izveidojiet kontaktpersonu, izmantojot vienumu <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Pievienojiet vienumu <xliff:g id="NAMEORNUMBER">^1</xliff:g> esošai kontaktpersonai."</string>
- <string name="description_details_action" msgid="2433827152749491785">"Zvanu informācija par šādu numuru: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Dzēsts no zvanu vēstures."</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Šodien"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Vakar"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Vecāki zvani"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Zvanu saraksts"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Ieslēgt skaļruni."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Izslēgt skaļruni."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Atskaņot ātrāk."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Atskaņot lēnāk."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Sākt vai apturēt atskaņošanu."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Attēlojuma opcijas"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Skaņas un vibrācija"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Pieejamība"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Tālruņa zvana signāls"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Izmantot vibrozvanu zvaniem"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Numura sast. tastatūras toņi"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Numuru tastatūras signāla ilgums"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Parasts"</item>
- <item msgid="6177579030803486015">"Ilgs"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Ātrās atbildes"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Zvani"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Zvanu bloķēšana"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Zvanu bloķēšana ir īslaicīgi izslēgta"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Zvanu bloķēšana ir atspējota, jo pēdējo 48 stundu laikā jūs sazinājāties ar ārkārtas palīdzības dienestiem, izmantojot šo tālruni. Zvanu bloķēšana tiks automātiski iespējota, tiklīdz beigsies 48 stundu periods."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importēt numurus"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Jūs iepriekš atzīmējāt dažus zvanītājus, kuri automātiski jānovirza uz balss pastu, izmantojot citas lietotnes."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Skatīt numurus"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importēt"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Importēšana neizdevās"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Neizdevās arhivēt balss pasta ziņojumu."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Atbloķēt numuru"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Pievienot numuru"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"No šiem numuriem saņemtie zvani tiks bloķēti, un balss pasta ziņojumi tiks automātiski dzēsti."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"No šiem numuriem saņemtie zvani tiks bloķēti, taču zvanītāji joprojām varēs jums atstāt balss pasta ziņojumus."</string>
- <string name="block_list" msgid="7760188925338078011">"Bloķētie numuri"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> nav derīgs."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> jau ir bloķēts."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Zvanu bloķēšana atspējota uz 48 stundām"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Zvanu bloķēšana atspējota, jo tika veikts ārkārtas zvans."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Zvanu konti"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Ieslēgt"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Iestatīt atļaujas"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Lai iespējotu ātros zvanus, ieslēdziet atļauju Kontaktpersonas."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Lai skatītu zvanu žurnālu, ieslēdziet atļauju Tālrunis."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Lai skatītu kontaktpersonas, ieslēdziet atļauju Kontaktpersonas."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Lai piekļūtu balss pastam, ieslēdziet atļauju Tālrunis."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Lai meklētu savas kontaktpersonas, ieslēdziet atļauju Kontaktpersonas."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Lai veiktu zvanu, ieslēdziet atļauju Tālrunis."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Tālruņa lietotnei nav atļaujas rakstīt sistēmas iestatījumos."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Bloķēts"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>: aktīvs"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Bloķēt numuru/ziņot par to"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Bloķēt"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Nav nevēlams numurs"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Atbloķēt"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Nevēlams numurs"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Vai bloķēt numuru <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Turpmāk zvani un balss pasta ziņojumi no šī numura tiks bloķēti."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Atzīmēt zvanu kā nevēlamu"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Turpmāk zvani un balss pasta ziņojumi no šī numura tiks bloķēti. Zvans tiks atzīmēts kā nevēlams."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Vai atbloķēt numuru <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Numurs tiks atbloķēts. Turpmāk numurs, zvani un balss pasta ziņojumi netiks atzīmēti kā nevēlami."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Vai iekļaut numuru <xliff:g id="NUMBER">%1$s</xliff:g> baltajā sarakstā?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Iekļaut baltajā sarakstā"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Turpmāk numurs, kā arī zvani un balss pasta ziņojumi no šī numura netiks uzskatīti par nevēlamiem."</string>
-</resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
deleted file mode 100644
index 9b1990d06..000000000
--- a/res/values-mk/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Телефон"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Телефон"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Телефонска тастатура"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Телефон"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Историја на повици"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Пријави неточен број"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Копирај го бројот"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Копирај транскрипција"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Блокирај го бројот"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> е блокиран"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Одблокирај го бројот"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> е деблокиран"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ВРАТИ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Избриши"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Уредете го бројот пред повикот"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Избришете ја историјата на повици"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Избришете ја говорната пошта"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Архивирајте говорна пошта"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Споделете ја говорната пошта"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Гов. пошта е избриш."</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Говорната пошта е архивирана"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ВРАТИ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ДО АРХИВА"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Избришете историја на повици?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Ова ќе ги избрише сите повици од историјата"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Се чисти историјата на повици…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Телефон"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Пропуштен повик"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Пропуштен работен повик"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Пропуштени повици"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> пропуштени повици"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Повикај назад"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Порака"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> говорна порака </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> говорни пораки </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Пушти"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Нова говорна пошта од <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Говорната пошта не можеше да се репродуцира"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Се вчитува говорната пошта…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Говорната пошта се архивира…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Говорната пошта не можеше да се вчита"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Само повици со говорна пошта"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Само дојдовни повици"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Само појдовни повици"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Само пропуштени повици"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Визуелна говорна пошта"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Гледајте и слушајте ја говорната пошта без да треба да повикувате број. Може да важат стандардни тарифи."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Поставки"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Не се достапни ажурирања на говорната пошта"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Чека нова говорна пошта. Не може да се вчита во моментов."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Поставете ја говорната пошта"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Аудио не е достапно"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Постави"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Повикај говорна пошта"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Избери број"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Избери број"</string>
- <string name="make_primary" msgid="5829291915305113983">"Запомни го овој избор"</string>
- <string name="description_search_button" msgid="3660807558587384889">"пребарај"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"бирај"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"број за бирање"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Пуштете или запрете репродукција"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Вклучете или исклучете интерфон"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Барајте позиција на репродукција"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Намалете брзина на репродукција"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Зголемете брзина на репродукција"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Историја на повици"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Повеќе опции"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"тастатура за избирање"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Прикажи само појдовни"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Прикажи само дојдовни"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Прикажи само пропуштени"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Прикажи само говорни пораки"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Прикажи ги сите повици"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Додај пауза од 2 сек"</string>
- <string name="add_wait" msgid="3360818652790319634">"Додај почекај"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Поставки"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Нов контакт"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Сите контакти"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Детали на повик"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Детали не се достапни"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Користи тастатура со звуци на допир"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Врати се на повик во тек"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Додај повик"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Дојдовен повик"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Појдовен повик"</string>
- <string name="type_missed" msgid="2720502601640509542">"Пропуштен повик"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Дојдовен видеоповик"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Појдовен видеоповик"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Пропуштен видеоповик"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Говорна пошта"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Одбиен повик"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Блокирани повик"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Дојдовни повици"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Преслушај говорна пошта"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Прикажи контакт <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Повикај <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Детали за контакт за <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> повици."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Видеоповик."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Испратете SMS до <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Непреслушана говорна пошта"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Започни гласовно пребарување"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Повикај <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Непознато"</string>
- <string name="voicemail" msgid="3851469869202611441">"Говорна пошта"</string>
- <string name="private_num" msgid="6374339738119166953">"Приватен број"</string>
- <string name="payphone" msgid="7726415831153618726">"Говорница"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> сек."</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> мин. <xliff:g id="SECONDS">%s</xliff:g> сек."</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> во <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Бројот не може да се повика"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"За да подесите говорна пошта, одете на Мени &gt; Поставки."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"За да се јавите во говорна пошта, прво исклучете режим на работа во авион."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Се вчитува..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Вчитување од СИМ картичка..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Контакти од СИМ картичка"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Не е достапна апликација за контакти"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Гласовното пребарување не е достапно"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Не може да се воспостави телефонски повик, бидејќи апликацијата Телефон е оневозможена."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Нема апликација за тоа на уредот"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Пребарајте контакти"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Додајте број или побарајте контакти"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Историјата на повици е празна"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Повикај"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Немате пропуштени повици."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Приемното сандаче на говорната пошта е празно."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Архивата на говорна пошта е празна."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Прикажи само омилени"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Историја на повици"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Архива на говорна пошта"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Сите"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Пропуштени"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Говорна пошта"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Ново, поедноставено блокирање"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"За подобра заштита, Телефонот треба да го промени начинот на блокирање. Блокираните броеви сега ќе ги сопрат повиците и текстуалните пораки и ќе може да се споделат со други апликации."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Дозволи"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Блокирај го <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Повиците од овој број ќе се блокираат, а говорната пошта автоматски ќе се брише."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Повиците од овој број ќе се блокираат, но можно е повикувачот сѐ уште да може да ви остава говорна пошта."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Веќе нема да примате повици или текстуални пораки од овој број."</string>
- <string name="block_number_ok" msgid="770551992296781873">"БЛОКИРАЈ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Одблокирај го <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ДЕБЛОКИРАЈ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Брзо бирање"</string>
- <string name="tab_history" msgid="2563144697322434940">"Историја на повици"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Контакти"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Говорна пошта"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Отстранет од омилени"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Врати"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Повикај <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Создај нов контакт"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Додај на контакт"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Испрати SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Остварете видеоповик"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Блокирај го бројот"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> нови пропуштени повици"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Сè уште немате никого на брзо бирање"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Додај омилено"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Сè уште немате контакти"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Додај контакт"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Допрете ја сликата за да ги видите сите броеви или допрете и држете за промена на редоследот"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Отстрани"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Видеоповик"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Испрати порака"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Детали на повик"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Повикај <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Пропуштен повик од <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Одговорен повик од <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Непрочитана говорна порака од <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Говорна порака од <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Повик до <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"на <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"на <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"на <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"на <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, на <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> на <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Повикај"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Повикај <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Видеоповик до <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Слушајте говорна пошта од <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Пушти говорна порака од <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Паузирај говорна порака од <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Избриши говорна пошта од <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> нова говорна порака</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> нови говорни пораки</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Создај контакт за <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Додај <xliff:g id="NAMEORNUMBER">^1</xliff:g> во постоечки контакт"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Информации на повикот за <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Избришано од историјата на повици"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Денес"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Вчера"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Постари"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Список со повици"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Вклучете го звучникот."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Исклучете го звучникот."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Репродуцирајте побрзо."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Репродуцирајте побавно."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Запрете ја или паузирајте ја репродукцијата."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Опции за приказ"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Звуци и вибрации"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Пристапност"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Мелодија на телефон"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Вибрации и за повици"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Тонови на подлогата за бирање"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Должина на тонот на подлогата за бирање"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Нормално"</item>
- <item msgid="6177579030803486015">"Долго"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Брзи одговори"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Повици"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Блокирање повик"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Блокирањето повик е привремено исклучено"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Блокирањето повици е исклучено бидејќи ја контактиравте службата за итни случаи од телефонов во изминатите 48 часа. Повторно ќе се овозможи автоматски откако ќе истече периодот од 48 часа."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Увези броеви"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Претходно означивте некои повикувачи да се испратат автоматски на говорната пошта преку други апликации."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Прикажи броеви"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Увези"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Увоз не успеа"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Говорната пошта не се архивираше."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Одблокирај го бројот"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Додај број"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Повиците од овие броеви ќе се блокираат, а говорната пошта автоматски ќе се брише."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Повиците од овие броеви ќе се блокираат, но можно е сѐ уште да може да ви оставаат говорна пошта."</string>
- <string name="block_list" msgid="7760188925338078011">"Блокирани броеви"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> не е важечки."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> веќе е блокиран."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Блокирањето на повикот е оневозможено 48 часа"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Оневозможено е затоа што беше направен итен повик."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Сметки за повикување"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Вклучи"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Постави дозволи"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"За да овозможите брзо бирање, вклучете ја дозволата за контакти."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"За да ја видите евиденцијата на повици, вклучете ја дозволата за телефон."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"За да ги видите контактите, вклучете ја дозволата за контакти."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"За да пристапите кон говорната пошта, вклучете ја дозволата за телефон."</string>
- <string name="permission_no_search" msgid="84152933267902056">"За да ги пребарувате контактите, вклучете ги дозволите за контакти."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"За да воспоставите повик, вклучете ја дозволата за телефон."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Апликацијата на телефонот нема дозвола да пишува во поставките на системот."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Блокиран"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> е активен"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Блокирај/пријави спам"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Блокирај"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Не е спам"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Одблокирај"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Спам"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Да се блокира <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Идните повици и говорната пошта од овој број ќе се блокираат."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Пријавете го повикот како спам"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Идните повици и говорната пошта од овој број ќе се блокираат. Повиков ќе се пријави како спам."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Да се деблокира <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Бројов ќе се деблокира и пријави дека не е спам. Повиците и гов. пошта нема да се гледаат како спам."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Да се постави на бела листа <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Бела листа"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Повиците и гов. пошта од бројов нема да се гледаат како спам. Бројот ќе се пријави дека не е спам."</string>
-</resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
deleted file mode 100644
index 4f8ea647a..000000000
--- a/res/values-ml/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"ഫോണ്‍"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"ഫോണ്‍"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"ഫോൺ ഡയൽപാഡ്"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"ഫോണ്‍"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"കോള്‍‌ ചരിത്രം"</string>
- <string name="action_report_number" msgid="4635403959812186162">"കൃത്യമല്ലാത്ത നമ്പർ റിപ്പോർട്ടുചെയ്യുക"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"നമ്പർ പകർത്തുക"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"ട്രാൻസ്ക്രിപ്ഷൻ പകർത്തുക"</string>
- <string name="action_block_number" msgid="1482657602262262134">"നമ്പർ ബ്ലോക്കുചെയ്യുക"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> ബ്ലോക്കുചെയ്തു"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"നമ്പർ അൺബ്ലോക്കുചെയ്യുക"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> അൺബ്ലോക്കുചെയ്തു"</string>
- <string name="block_number_undo" msgid="591338370336724156">"പഴയപടിയാക്കുക"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"ഇല്ലാതാക്കുക"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"കോൾ ചെയ്യുംമുമ്പ് നമ്പർ എഡിറ്റുചെയ്യൂ"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"കോൾ ചരിത്രം മായ്‌ക്കുക"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"വോയ്‌സ്മെയിൽ ഇല്ലാതാക്കുക"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"വോയ്‌സ്‌മെയിൽ ആർക്കൈവുചെയ്യുക"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"വോയ്‌സ്‌മെയിൽ പങ്കിടുക"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"വോയ്സ്മെയിൽ ഇല്ലാതാക്കി"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"വോയ്‌സ്‌മെയിൽ ആർക്കൈവുചെയ്‌തു"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"പഴയപടിയാക്കുക"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ആർക്കൈവ് ബട്ടണിലേക്ക് പോകുക"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"കോൾ ചരിത്രം മായ്‌ക്കണോ?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"ഇത് നിങ്ങളുടെ ചരിത്രത്തിൽ നിന്ന് എല്ലാ കോളുകളും ഇല്ലാതാക്കും"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"കോൾ ചരിത്രം മായ്‌ക്കുന്നു..."</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"ഫോൺ"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"മിസ്‌ഡ് കോൾ"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"മിസ്ഡ് ഔദ്യോഗിക കോൾ"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"മിസ്‌ഡ് കോളുകൾ"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> മിസ്‌ഡ് കോളുകൾ"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"തിരിച്ചുവിളിക്കുക"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"സന്ദേശം"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> വോയ്‌സ്‌മെയിലുകൾ </item>
- <item quantity="one">വോയ്‌സ്‌മെയിൽ</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"പ്ലേ ചെയ്യുക"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> എന്നയാളിൽ നിന്നുള്ള പുതിയ വോയ്‌സ്‌മെയിൽ"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"വോയ്‌സ്‌മെയിൽ പ്‌ലേ ചെയ്യാനായില്ല"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"വോയ്‌സ്‌മെയിൽ ലോഡുചെയ്യുന്നു..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"വോയ്‌സ്‌മെയിൽ ആർക്കൈവുചെയ്യുന്നു…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"വോയ്‌സ്‌മെയിൽ ലോഡുചെയ്യാനായില്ല"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"വോയ്‌സ്മെയിൽ ഉള്ള കോളുകൾ മാത്രം"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"ഇൻകമിംഗ് കോളുകൾ മാത്രം"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"ഔട്ട്‌ഗോയിംഗ് കോളുകൾ മാത്രം"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"മിസ്‌ഡ് കോളുകൾ മാത്രം"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"വിഷ്വൽ വോയ്‌സ്‌മെയിൽ"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"ഏതെങ്കിലും നമ്പറിലേക്ക് വിളിക്കാതെ തന്നെ, വോയ്സ്‌മെയിൽ കാണുക, കേൾക്കുക. ഡാറ്റാ നിരക്കുകൾ ബാധകമായേക്കാം."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"ക്രമീകരണം"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"വോയ്‌സ്‌മെയിൽ അപ്‌ഡേറ്റുകൾ ലഭ്യമല്ല"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"പുതിയ വോയ്‌സ്‌മെയിൽ കാത്തിരിക്കുന്നു. ഇപ്പോൾ ലോഡുചെയ്യാനാവില്ല."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"നിങ്ങളുടെ വോയ്‌സ്‌മെയിൽ സജ്ജീകരിക്കുക"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"ഓഡിയോ ലഭ്യമല്ല"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"സജ്ജമാക്കുക"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"വോയ്‌സ്‌മെയിൽ വിളിക്കുക"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"നമ്പർ തിരഞ്ഞെടുക്കുക"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"നമ്പർ തിരഞ്ഞെടുക്കുക"</string>
- <string name="make_primary" msgid="5829291915305113983">"ഈ തിരഞ്ഞെടുക്കൽ ഓർക്കുക"</string>
- <string name="description_search_button" msgid="3660807558587384889">"തിരയുക"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"ഡയൽ ചെയ്യുക"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"ഡയൽ ചെയ്യാനുള്ള നമ്പർ"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"പ്ലേബാക്ക് പ്ലേ ചെയ്യുക അല്ലെങ്കിൽ നിർത്തുക"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"സ്‌പീക്കർ ഫോൺ ഓണാക്കുക അല്ലെങ്കിൽ ഓഫാക്കുക"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"പ്ലേബാക്ക് സ്ഥാനം തിരയുക"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"പ്ലേബാക്ക് നിരക്ക് കുറയ്‌ക്കുക"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"പ്ലേബാക്ക് നിരക്ക് വർദ്ധിപ്പിക്കുക"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"കോള്‍‌ ചരിത്രം"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"കൂടുതൽ‍ ഓപ്‌ഷനുകള്‍"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"ഡയൽ പാഡ്"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"ഔട്ട്‌ഗോയിംഗ് മാത്രം കാണിക്കുക"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"ഇൻ‌കമിംഗ് മാത്രം കാണിക്കുക"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"മിസ്‌ഡ് മാത്രം കാണിക്കുക"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"വോയ്‌സ്‌മെയിലുകൾ മാത്രം കാണിക്കുക"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"എല്ലാ കോളുകളും കാണിക്കുക"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2 സെക്കൻഡ് താൽക്കാലികമായി നിർത്തൽ ചേർക്കുക"</string>
- <string name="add_wait" msgid="3360818652790319634">"കാത്തിരിക്കൽ ചേർക്കുക"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"ക്രമീകരണം"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"പുതിയ കോണ്‍ടാക്റ്റ്"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"എല്ലാ കോൺടാക്റ്റുകളും"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"കോൾ വിശദാംശങ്ങൾ"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"വിശദാംശങ്ങൾ ലഭ്യമല്ല"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"ടച്ച് ടോൺ കീപാഡ് ഉപയോഗിക്കുക"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"വിളിച്ചുകൊണ്ടിരിക്കുന്ന കോളിലേക്ക് മടങ്ങുക"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"കോൾ ചേർക്കുക"</string>
- <string name="type_incoming" msgid="6502076603836088532">"ഇന്‍കമിംഗ് കോള്‍"</string>
- <string name="type_outgoing" msgid="343108709599392641">"വിളിച്ച കോൾ"</string>
- <string name="type_missed" msgid="2720502601640509542">"മിസ്‌ഡ് കോൾ"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"ഇൻകമിംഗ് വീഡിയോ കോൾ"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"വിളിച്ച വീഡിയോ കോൾ"</string>
- <string name="type_missed_video" msgid="954396897034220545">"വീഡിയോ കോൾ നഷ്‌ടമായി"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"വോയ്‌സ്‌മെയിൽ"</string>
- <string name="type_rejected" msgid="7783201828312472691">"നിരസിച്ച കോൾ"</string>
- <string name="type_blocked" msgid="3521686227115330015">"ബ്ലോക്കുചെയ്ത കോൾ"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"ഇൻകമിംഗ് കോളുകൾ"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"വോയ്‌സ്‌മെയിൽ പ്ലേ ചെയ്യുക"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> എന്ന കോൺടാക്റ്റ് കാണുക"</string>
- <string name="description_call" msgid="3443678121983852666">"വിളിക്കുക <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> എന്നതിന്റെ കോൺ‌ടാക്റ്റ് വിശദാംശങ്ങൾ"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> കോളുകൾ."</string>
- <string name="description_video_call" msgid="2933838090743214204">"വീഡിയോ കോൾ."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g> എന്നയാൾക്ക് SMS അയയ്‌ക്കുക"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"കേൾക്കാത്ത വോയ്‌സ്‌മെയിൽ"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"ശബ്ദ തിരയൽ ആരംഭിക്കുക"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"വിളിക്കുക <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"അജ്ഞാതം"</string>
- <string name="voicemail" msgid="3851469869202611441">"വോയ്‌സ്‌മെയിൽ"</string>
- <string name="private_num" msgid="6374339738119166953">"സ്വകാര്യ നമ്പർ"</string>
- <string name="payphone" msgid="7726415831153618726">"പണം നൽകി ഉപയോഗിക്കുന്ന ഫോൺ"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> സെക്കൻഡ്"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> മി. <xliff:g id="SECONDS">%s</xliff:g> സെ."</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>-ന്"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"ഈ നമ്പറിലേക്ക് കോൾ ചെയ്യാനാവില്ല"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"വോയ്‌സ്‌മെയിൽ സജ്ജീകരിക്കുന്നതിന്, മെനു &gt; ക്രമീകരണങ്ങൾ എന്നതിലേക്ക് പോകുക."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"വോയ്‌സ്‌മെയിൽ വിളിക്കാൻ ആദ്യം ഫ്ലൈറ്റ് മോഡ് ഓഫാക്കുക."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"ലോഡുചെയ്യുന്നു..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"സിം കാർഡിൽ നിന്നും ലോഡുചെയ്യുന്നു…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"സിം കാർഡ് കോൺടാക്റ്റുകൾ"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"കോൺടാക്റ്റ് അപ്ലിക്കേഷനൊന്നും ലഭ്യമല്ല"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"വോയ്‌സ് തിരയൽ ലഭ്യമല്ല"</string>
- <string name="call_not_available" msgid="8941576511946492225">"ഫോൺ അപ്ലിക്കേഷൻ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നതിനാൽ ഫോൺ കോൾ ചെയ്യാനാകില്ല."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"അതിനായി ഈ ഉപകരണത്തിൽ അപ്ലിക്കേഷനുകളൊന്നുമില്ല"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"കോണ്‍‌ടാക്റ്റുകള്‍ തിരയുക"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"നമ്പർ ചേർക്കുക അല്ലെങ്കിൽ കോൺടാക്റ്റുകൾ തിരയുക"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"നിങ്ങളുടെ കോൾ ചരിത്രം ശൂന്യമാണ്"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"ഒരു കോൾ ചെയ്യുക"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"നിങ്ങൾക്ക് മിസ്‌ഡ് കോളുകളൊന്നുമില്ല."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"നിങ്ങളുടെ വോയ്സ്മെയിൽ ഇൻബോക്സ് ശൂന്യമാണ്."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"നിങ്ങളുടെ വോയ്‌സ്‌മെയിൽ ആർക്കൈവ് ശൂന്യമാണ്."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"പ്രിയപ്പെട്ടവ മാത്രം കാണിക്കുക"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"കോൾ ചരിത്രം"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"വോയ്‌സ്മെയിൽ ആർക്കൈവ്"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"എല്ലാം"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"മിസ്‌ഡ്"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"വോയ്‌സ്‌മെയിൽ"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"പുതിയ, ലളിതമായ ബ്ലോക്കുചെയ്യൽ"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"നിങ്ങളെ മെച്ചപ്പെട്ട രീതിയിൽ പരിരക്ഷിക്കുന്നതിന്, ഫോണിലെ ബ്ലോക്കുചെയ്യൽ പ്രവർത്തന രീതി മാറ്റേണ്ടതുണ്ട്. നിങ്ങളുടെ ബ്ലോക്കുചെയ്‌ത നമ്പറുകളിൽ നിന്നുള്ള കോൾ, ടെക്‌സ്‌റ്റ് എന്നിവ ഇപ്പോൾ അവസാനിപ്പിക്കും, ഈ വിവരം അത് മറ്റ് ആപ്‌സുമായി പങ്കിടാനുമിടയുണ്ട്."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"അനുവദിക്കുക"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> ബ്ലോക്കുചെയ്യണോ?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"ഈ നമ്പറിൽ നിന്നുള്ള കോളുകൾ ബ്ലോക്കുചെയ്യും, വോയ്സ്മെയിലുകളെ സ്വയമേവ ഇല്ലാതാക്കും."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"ഈ നമ്പറിൽ നിന്നുള്ള കോളുകൾ ബ്ലോക്കുചെയ്യും, എന്നാൽ വിളിക്കുന്നയാൾക്ക് അപ്പോഴും നിങ്ങൾക്ക് വോയ്സ്‌മെയിലുകൾ അയയ്ക്കാൻ കഴിഞ്ഞേക്കാം."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"നിങ്ങൾക്ക് ഇനി ഈ നമ്പറിൽ നിന്ന് കോളുകളോ ടെക്‌‌സ്‌റ്റോ ലഭിക്കില്ല."</string>
- <string name="block_number_ok" msgid="770551992296781873">"ബ്ലോക്കുചെയ്യുക"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> അൺബ്ലോക്കുചെയ്യണോ?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"അൺബ്ലോക്കുചെയ്യുക"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"സ്‌പീഡ് ഡയൽ"</string>
- <string name="tab_history" msgid="2563144697322434940">"കോൾ ചരിത്രം"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"കോണ്‍ടാക്റ്റുകള്‍"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"വോയ്‌സ്‌മെയിൽ"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"പ്രിയപ്പെട്ടവയിൽ നിന്നും നീക്കംചെയ്‌തു"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"പഴയപടിയാക്കുക"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"വിളിക്കുക <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"പുതിയകോൺടാക്റ്റ് സൃഷ്‌ടിക്കൂ"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"ഒരുകോൺടാക്റ്റിൽ ചേർക്കൂ"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS അയയ്ക്കുക"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"വീഡിയോ കോൾ ചെയ്യുക"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"നമ്പർ ബ്ലോക്കുചെയ്യുക"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> പുതിയ മിസ്‌ഡ് കോളുകൾ"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"ഇതുവരെ ആരും നിങ്ങളുടെ സ്പീഡ് ഡയലിൽ ഇല്ല"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"പ്രിയപ്പെട്ടത് ചേർക്കുക"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"നിങ്ങൾക്ക് ഇതുവരെയും കോൺടാക്റ്റുകൾ ഒന്നുമില്ല."</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"ഒരു കോണ്‍ടാക്റ്റ് ചേര്‍ക്കുക"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"എല്ലാ നമ്പറുകളും കാണാൻ ചിത്രം സ്‌പർശിക്കുക അല്ലെങ്കിൽ വീണ്ടും ക്രമീകരിക്കാൻ സ്‌പർശിച്ച് പിടിക്കുക"</string>
- <string name="remove_contact" msgid="1080555335283662961">"നീക്കംചെയ്യുക"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"വീഡിയോ കോള്‍"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"ഒരു സന്ദേശം അയയ്‌ക്കുക"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"കോൾ വിശദാംശങ്ങൾ"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> എന്ന നമ്പർ/വ്യക്തിയെ വിളിക്കൂ"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> എന്നതിൽ നിന്നുള്ള മിസ്ഡ് കോൾ."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> എന്നതിൽ നിന്നുള്ള മറുപടി നൽകിയ കോൾ."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> എന്നതിൽ നിന്നുള്ള വായിക്കാത്ത വോയ്സ്മെയിൽ."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> എന്നതിൽ നിന്നുള്ള വോയ്സ്മെയിൽ."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> എന്നതിലേക്കുള്ള കോൾ."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> എന്നതിൽ"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> നമ്പർ വഴി"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> നമ്പർ വഴി"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="NUMBER">%2$s</xliff:g> നമ്പർ വഴി, <xliff:g id="PHONEACCOUNT">%1$s</xliff:g> അക്കൗണ്ടിൽ"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="NUMBER">%2$s</xliff:g> നമ്പർ വഴി, <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"വിളിക്കുക"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> വിളിക്കുക"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> എന്നതുമായി വീഡിയോ കോൾ നടത്തുക."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> എന്നയാളിൽ നിന്നുള്ള വോയ്‌സ്മെയിൽ കേൾക്കുക"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> എന്ന നമ്പറിൽ/വ്യക്തിയിൽ നിന്നുള്ള വോയ്സ്‌മെയിൽ പ്ലേ ചെയ്യുക"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> എന്ന നമ്പറിൽ/വ്യക്തിയിൽ നിന്നുള്ള വോയ്സ്‌മെയിൽ തൽക്കാലം നിർത്തുക"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> എന്ന നമ്പറിൽ/വ്യക്തിയിൽ നിന്നുള്ള വോയ്സ്മെയിൽ ഇല്ലാതാക്കുക"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> പുതിയ വോയ്‌സ്‌മെയിലുകൾ</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> പുതിയ വോയ്‌സ്‌മെയിൽ</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> എന്നതിനായി കോൺടാക്റ്റ് സൃഷ്‌ടിക്കുക"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"നിലവിലുള്ള കോൺടാക്റ്റിലേക്ക് <xliff:g id="NAMEORNUMBER">^1</xliff:g> ചേർക്കുക"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> എന്നയാളുടെ കോൾ വിശദാംശങ്ങൾ"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"കോൾ ചരിത്രത്തിൽ നിന്ന് ഇല്ലാതാക്കി"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"ഇന്ന്"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"ഇന്നലെ"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"പഴയത്"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"കോൾ ലിസ്‌റ്റ്"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"സ്‌പീക്കർ ഓണാക്കുക."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"സ്‌പീക്കർ ഓഫാക്കുക."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"വേഗത്തിൽ പ്ലേചെയ്യുക."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"കുറഞ്ഞവേഗതയിൽ പ്ലേചെയ്യുക."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"പ്ലേബാക്ക് ആരംഭിക്കുകയോ താൽക്കാലികമായി നിർത്തുകയോ ചെയ്യുക."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"ഡിസ്‌പ്ലേ ഓപ്‌ഷനുകൾ"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"ശബ്‌ദവും വൈബ്രേഷനും"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"ഉപയോഗസഹായി"</string>
- <string name="ringtone_title" msgid="760362035635084653">"ഫോൺ റിംഗ്ടോൺ"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"കോളുകൾക്കും വൈബ്രേറ്റ്"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"ഡയൽപാഡ് ടോണുകൾ"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"ഡയൽപാഡ് ടോണിന്റെ ദൈർഘ്യം"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"സാധാരണം"</item>
- <item msgid="6177579030803486015">"ദൈർഘ്യമുള്ളത്"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"അതിവേഗ പ്രതികരണങ്ങൾ"</string>
- <string name="call_settings_label" msgid="313434211353070209">"കോളുകൾ"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"കോൾ ബ്ലോക്കുചെയ്യൽ"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"കോൾ ബ്ലോക്കുചെയ്യൽ താൽക്കാലികമായി ഓഫാണ്"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"കഴിഞ്ഞ 48 മണിക്കൂറിനുള്ളിൽ നിങ്ങൾ ഈ ഫോണിൽ നിന്ന് അടിയന്തിര സേവനങ്ങളുമായി ബന്ധപ്പെട്ടതിനാൽ കോൾ ബ്ലോക്കുചെയ്യൽ പ്രവർത്തനരഹിതമാക്കി. 48 മണിക്കൂർ സമയപരിധി കഴിഞ്ഞയുടൻ ഇത് സ്വയമേവ പ്രവർത്തനക്ഷമമാക്കപ്പെടും."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"നമ്പറുകൾ ഇമ്പോർട്ടുചെയ്യുക"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"മറ്റ് ആപ്സ് വഴി വോയ്സ്‌മെയിൽ സ്വയമേവ അയയ്ക്കുന്നതിന് കോൾ ചെയ്യുന്നവരിൽ ചിലരെ നിങ്ങൾ മുമ്പ് അടയാളപ്പെടുത്തി."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"നമ്പറുകൾ കാണുക"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"ഇമ്പോർട്ടുചെയ്യുക"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"ഇമ്പോർട്ടുചെയ്യൽ പരാജയപ്പെട്ടു"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"വോയ്‌സ്‌മെയിൽ ആർക്കൈവുചെയ്യാനായില്ല."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"നമ്പർ അൺബ്ലോക്കുചെയ്യുക"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"നമ്പർ ചേർക്കുക"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"ഈ നമ്പറുകളിൽ നിന്നുള്ള കോളുകൾ ബ്ലോക്കുചെയ്യും, വോയ്സ്മെയിലുകളെ സ്വയമേവ ഇല്ലാതാക്കും."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"ഈ നമ്പറുകളിൽ നിന്നുള്ള കോളുകൾ ബ്ലോക്കുചെയ്യും, എന്നാൽ വിളിക്കുന്നവർക്ക് അപ്പോഴും നിങ്ങൾക്ക് വോയ്സ്‌മെയിലുകൾ അയയ്ക്കാൻ കഴിഞ്ഞേക്കാം."</string>
- <string name="block_list" msgid="7760188925338078011">"ബ്ലോക്കുചെയ്ത നമ്പറുകൾ"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> അസാധുവാണ്."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"ഇതിനകം തന്നെ <xliff:g id="NUMBER">%1$s</xliff:g> ബ്ലോക്കുചെയ്തിരിക്കുന്നു"</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"48 മണിക്കൂർ നേരത്തേക്ക് കോൾ ബ്ലോക്കുചെയ്യൽ പ്രവർത്തനരഹിതമാക്കി"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"എമർജൻസി കോൾ നടത്തിയതിനാൽ പ്രവർത്തനരഹിതമാക്കി."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"കോളിംഗ് അക്കൗണ്ട്"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"ഓണാക്കുക"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"അനുമതികൾ സജ്ജമാക്കുക"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"സ്പീഡ് ഡയൽ പ്രവർത്തനക്ഷമാക്കുന്നതിന്, \'കോൺടാക്റ്റുകൾ\' അനുമതി ഓണാക്കുക."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"നിങ്ങളുടെ കോൾ ലോഗ് കാണുന്നതിന്, \'ഫോൺ\' അനുമതി ഓണാക്കുക."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"നിങ്ങളുടെ കോൺടാക്റ്റുകൾ കാണുന്നതിന്, \'കോൺടാക്റ്റുകൾ\' അനുമതി ഓണാക്കുക."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"നിങ്ങളുടെ വോയ്‌സ്‌മെയിൽ ആക്സസ് ചെയ്യുന്നതിന്, \'ഫോൺ\' അനുമതി ഓണാക്കുക."</string>
- <string name="permission_no_search" msgid="84152933267902056">"നിങ്ങളുടെ കോൺടാക്റ്റുകൾ തിരയുന്നതിന് കോൺടാക്റ്റുകൾക്കുള്ള അനുമതികൾ ഓണാക്കുക."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"കോൾ വിളിക്കുന്നതിന്, \'ഫോൺ\' അനുമതി ഓണാക്കുക."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"സിസ്റ്റം ക്രമീകരണത്തിലേക്ക് എഴുതാൻ ഫോൺ ആപ്പിന് അനുമതിയില്ല."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"ബ്ലോക്കുചെയ്തു"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> സജീവമാണ്"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"ബ്ലോക്കുചെയ്യുക/സ്പാമാണെന്ന് റിപ്പോർട്ടുചെയ്യുക"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"ബ്ലോക്കുചെയ്യുക"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"സ്പാം അല്ല"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"അൺബ്ലോക്കുചെയ്യുക"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"സ്‌പാം"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> ബ്ലോക്കുചെയ്യണോ?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"ഈ നമ്പറിൽ നിന്ന് ഭാവിയിൽ വരാൻ പോകുന്ന കോളുകളെയും വോയ്‌സ്‌മെയിലുകളെയും ബ്ലോക്കുചെയ്യും."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"കോളിനെ സ്‌പാമെന്ന് റിപ്പോർട്ടുചെയ്യുക"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"ഈ നമ്പറിൽ നിന്ന് ഭാവിയിൽ വരാൻ പോകുന്ന കോളുകളെയും വോയ്‌സ്‌മെയിലുകളെയും ബ്ലോക്കുചെയ്യും. ഈ കോളിനെ സ്‌പാമെന്ന് റിപ്പോർട്ടുചെയ്യും."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> അൺബ്ലോക്കുചെയ്യണോ?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"ഈ നമ്പർ അൺ‌ബ്ലോക്കുചെയ്‌ത്, സ്‌പാമല്ലെന്ന് റിപ്പോർട്ടുചെയ്യും. ഭാവിയിൽ വരാൻ പോകുന്ന കോളുകളെയും വോയ്‌സ്‌മെയിലുകളെയും സ്‌പാമെന്ന് തിരിച്ചറിയില്ല."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> നമ്പർ വൈറ്റ്‌ലിസ്റ്റുചെയ്യണോ?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"വൈറ്റ്‌ലിസ്റ്റ് ചെയ്യുക"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"ഈ നമ്പറിൽ നിന്ന് ഭാവിയിൽ വരാൻ പോകുന്ന കോളുകളെയും വോയ്‌സ് മെയിലുകളെയും സ്‌പാമെന്ന് തിരിച്ചറിയില്ല. ഈ നമ്പറിനെ സ്‌പാമല്ലെന്ന് റിപ്പോർട്ടുചെയ്യും."</string>
-</resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
deleted file mode 100644
index 52fc10d3f..000000000
--- a/res/values-mn/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Гар утас"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Утас"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Утасны дуудлага хийх дэлгэц"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Утас"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Дуудлагын түүх"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Тохиромжгүй дугаарыг мэдээлэх"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Дугаар хуулах"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Галиглалыг хуулах"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Дугаар блоклох"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g>-г блоклосон"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Дугаар блокноос гаргах"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g>-г блокоос гаргасан"</string>
- <string name="block_number_undo" msgid="591338370336724156">"БУЦААХ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Устгах"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Залгахын өмнө дугаар засах"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Дуудлагын түүхийг устгах уу?"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Дуут шууданг устгах"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Дуут шууданг архивлах"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Дуут шууданг хуваалцах"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Дуут шууданг устгасан"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Дуут шууданг архивласан"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"БУЦААХ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"АРХИВ РУУ ОЧИХ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Дуудлагын түүхийг устгах уу?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Энэ нь таны хийсэн бүх дуудлагыг түүхээс устгана."</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Дуудлагын түүхийг устгаж байна..."</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Утас"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Аваагүй дуудлага"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Аваагүй албаны дуудлага"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Аваагүй дуудлага"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> аваагүй дуудлага"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Буцааж залгах"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Зурвас"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Дуут шуудан </item>
- <item quantity="one">Дуут шуудан</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Тоглуулах"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g>-с ирсэн шинэ дуут шуудан"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Дуут шууданг тоглуулж чадсангүй"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Дуут шууданг ачаалж байна..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Дуут шууданг архивлаж байна..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Дуут шууданг ачаалж чадсангүй"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Зөвхөн дуут шуудантай дуудлагуудыг"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Зөвхөн ирсэн дуудлага"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Зөвхөн залгасан дуудлагуудыг"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Зөвхөн аваагүй дуудлагуудыг"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Уншиж болохуйц дуут шуудан"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Дугаар луу залгалгүйгээр дуут шууданг харах болон сонсоорой. Дата ашиглалтын төлбөр гарч болзошгүй."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Тохиргоо"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Дуут шуудангийн шинэчлэлтүүд байхгүй байна"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Шинэ дуут шуудан ирсэн байна. Гэвч одоохондоо ачаалах боломжгүй байна."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Дуут шуудангаа тохируулах"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Дуу байхгүй байна"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Тохируулах"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Дуут шуудан руу залгах"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Дугаар сонгоно уу"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Дугаар сонгоно уу"</string>
- <string name="make_primary" msgid="5829291915305113983">"Энэ сонголтыг санах"</string>
- <string name="description_search_button" msgid="3660807558587384889">"хайх"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"залгах"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"залгах дугаар"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Тоглуулах эсхүл зогсоох"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Чанга яригчийг асаах буюу унтраах"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Тоглуулах байрлалыг хайх"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Тоглуулах хурдыг бууруулах"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Тоглуулах хурдыг нэмэгдүүлэх"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Дуудлагын түүх"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Нэмэлт сонголтууд"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"дугаар цуглуулагч"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Зөвхөн залгасан дуудлагуудыг харуулах"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Зөвхөн ирсэн дуудлагуудыг харуулах"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Зөвхөн аваагүй дуудлагуудыг харуулах"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Зөвхөн дуут шуудангуудыг харуулах"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Бүх дуудлагыг харуулах"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2-сек зогсолт нэмэх"</string>
- <string name="add_wait" msgid="3360818652790319634">"Хүлээлт нэмэх"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Тохиргоо"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Шинэ харилцагч"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Бүх харилцагчид"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Дуудлагын мэдээлэл"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Мэдээлэл байхгүй байна"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Хүрэлтээр дуугардаг гар ашиглах"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Үргэлжилж буй дуудлага руу буцах"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Дуудлага нэмэх"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Ирж буй дуудлага"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Залгасан дуудлага"</string>
- <string name="type_missed" msgid="2720502601640509542">"Аваагүй дуудлага"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Ирж буй видео дуудлага"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Залгасан видео дуудлага"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Аваагүй видео дуудлага"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Дуут шуудан"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Татгалзсан дуудлага"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Блоклосон дуудлага"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Ирж буй дуудлага"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Дуут шууданг тоглуулах"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> харилцагчийг харах"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g> руу залгах"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g>-н харилцагчийн мэдээлэл"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> дуудлага."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Видео дуудлага."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g>-д мессеж илгээх"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Сонсоогүй дуут шуудан"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Дуун хайлтыг эхлүүлэх"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> руу залгах"</string>
- <string name="unknown" msgid="740067747858270469">"Тодорхойгүй"</string>
- <string name="voicemail" msgid="3851469869202611441">"Дуут шуудан"</string>
- <string name="private_num" msgid="6374339738119166953">"Нууцлагдсан дугаар"</string>
- <string name="payphone" msgid="7726415831153618726">"Payphone"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> сек"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> минут <xliff:g id="SECONDS">%s</xliff:g> секунд"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g>-д"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Энэ дугаар луу залгах боломжгүй байна"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Дуут шууданг тохируулахын тулд Цэс &gt; Тохиргоо руу очно уу."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Дуут шуудан руу залгахын тулд юуны өмнө Нислэгийн төлвийг идэвхгүйжүүлнэ үү."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Ачааллаж байна..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"SIM картаас уншиж байна…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM картны харилцагчид"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Ямар ч харилцагчдын апликейшн байхгүй байна."</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Дуут хайлт хийх боломжгүй байна"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Утас аппликешныг идэвхгүйжүүлсэн тул утасны дуудлага хийх боломжгүй."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Энэхүү төхөөрөмж дээр тухайн үйлдлийг гүйцэтгэх апликейшн байхгүй байна."</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Харилцагч хайх"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Дугаар нэмэх болон харилцагч хайх"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Таны дуудлагын түүх хоосон байна"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Дуудлага хийх"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Танд аваагүй дуудлага байхгүй байна."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Таны дуут шуудангийн хайрцаг хоосон байна."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Таны дуут шуудангийн архив хоосон байна."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Зөвхөн дуртайнуудыг харуулах"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Дуудлагын түүх"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Дуут шуудангийн архив"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Бүгд"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Аваагүй"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Дуут шуудан"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Шинэ, хялбаршуулсан хориг"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Таныг илүү сайн хамгаалахын тулд утасны хоригийн тохиргоог өөрчлөх шаардлагатай. Таны хориглосон дугааруудаас дуудлага, зурвас ирэхгүй бөгөөд тэдгээрийг бусад апп-тай хуваалцаж болзошгүй."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Зөвшөөрөх"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g>-г блоклох уу?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Энэ дугаараас ирэх дуудлагыг блоклох бөгөөд дуут шуудан автоматаар устах болно."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Энэ дугаараас ирэх дуудлагыг блоклосон, гэхдээ залгагч танд дуут шуудан илгээх боломжтой."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Та энэ дугаараас цаашид дуудлага буюу мессеж хүлээж авахгүй."</string>
- <string name="block_number_ok" msgid="770551992296781873">"БЛОКЛОХ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g>-г блокоос гаргах уу?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"БЛОКООС ГАРГАХ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Түргэн залгалт"</string>
- <string name="tab_history" msgid="2563144697322434940">"Дуудлагын түүх"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Харилцагчид"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Дуут шуудан"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Дуртай жагсаалтаас хасав"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Буцаах"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> руу залгах"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Шинэ хаяг үүсгэх"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Харилцагчийн хаягт нэмэх"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Мессеж илгээх"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Видео дуудлага хийх"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Дугаар блоклох"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> шинэ аваагүй дуудлага"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Таны яаралтай залгах тохиргоон дээр одоогоор харилцагч байхгүй байна"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Таалагдсан хэсэгт нэмэх"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Танд одоогоор харилцагч байхгүй байна"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Харилцагч нэмэх"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Бүх дугаарыг харахын тулд зурган дээр дарна уу эсвэл жагсаалтыг өөрчлөхийн тулд удаан дарна уу."</string>
- <string name="remove_contact" msgid="1080555335283662961">"Хасах"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Видео дуудлага"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Зурвас илгээх"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Дуудлагын мэдээлэл"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> руу дуудлага хийх"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Дараах дуудлагыг хүлээн аваагүй <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Дараах дуудлагыг хүлээн авсан <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>-н уншаагүй дуут шуудан"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>-н дуут шуудан"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Дараах дугаар луу залгасан <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g>-р залгасан"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g>-р"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g>-р"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> дээр, <xliff:g id="NUMBER">%2$s</xliff:g>-р"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="NUMBER">%2$s</xliff:g>-р <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Дуудлага"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> руу дуудлага хийх"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-ын видео дуудлага."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-н дуут шууданг сонсох"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-н дуут шууданг тоглуулах"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-н дуут шууданг зогсоох"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-н дуут шууданг устгах"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> шинэ дуут шуудан</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> шинэ дуут шуудан</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-д харилцагч шинээр үүсгэх"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>-ыг одоогийн харилцагч руугаа нэмнэ"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> дуудлагын дэлгэрэнгүй мэдээлэл"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Дуудлагын түүхээс устгагдсан"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Өнөөдөр"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Өчигдөр"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Хуучин"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Дуудлагын жагсаалт"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Чанга яригчийг асаах."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Чанга яригчийг унтраах."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Хурдан тоглуулах."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Удаан тоглуулах."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Тоглуулахыг эхлүүлэх буюу түр зогсоох."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Сонголтуудыг харуулах"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Дуу болон чичиргээ"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Хандалт"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Утасны хонхны ая"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Дуудлагад бас чичрэх"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Залгах товчлуурын ая"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Дугаар цуглуулах хэсгийн дохионы урт"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Энгийн"</item>
- <item msgid="6177579030803486015">"Урт"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Шуурхай хариунууд"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Дуудлага"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Дуудлага блоклох"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Дуудлага блоклох тохиргоог түр хугацаагаар унтраасан"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Та сүүлийн 48 цагийн дотор энэ утсаар тусламжийн дуудлага хийсэн тул дуудлага блоклох тохиргоог идэвхгүй болгосон. Энэ тохиргоо нь 48 цагийн хугацаа дуусахад автоматаар идэвхэжнэ."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Импортын тоо"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Тань руу залгасан зарим хүмүүсийг бусад апп ашиглан автоматаар дуут шуудан илгээж байхаар өмнө нь тэмдэглэсэн."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Дугаар харах"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Импортлох"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Импортолж чадсангүй"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Дуут шууданг архивлаж чадсангүй."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Дугаар блокноос гаргах"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Дугаар нэмэх"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Эдгээр дугаараас ирэх дуудлагыг блоклож, дуут шууданг автоматаар устгана."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Эдгээр дугаараас ирэх дуудлагыг блоклож, харин танд дуут шуудан илгээх боломжтой."</string>
- <string name="block_list" msgid="7760188925338078011">"Блоклосон дугаар"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> хүчингүй байна."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g>-г аль хэдийн блоклосон байна."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Дуудлага блоклохыг 48 цагийн турш идэвхгүй болгосон."</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Яаралтай дуудлага хийсэн тул идэвхгүй болгосон."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Дуудах бүртгэл"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Идэвхжүүлэх"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Зөвшөөрөл тохируулах"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Яаралтай дуудлагыг идэвхжүүлэхийн тулд Харилцагчдын зөвшөөрлийг идэвхжүүлнэ үү."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Дуудлагын жагсаалтыг харахын тулд утасны зөвшөөрлийг идэвхжүүлнэ үү."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Харилцагчдаа харахын тулд Харилцагчдын зөвшөөрлийг идэвхжүүлнэ үү."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Дуут шууданд хандахын тулд Утасны зөвшөөрлийг идэвхжүүлнэ үү."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Харилцагчаа хайхын тулд, Харилцагчийн жагсаалтын зөвшөөрлийг идэвхжүүлнэ үү."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Залгахын тулд Утасны зөвшөөрлийг идэвхжүүлнэ үү."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Гар утасны апп-д системийн тохиргоо бичих зөвшөөрөл алга."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Блоклосон"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> идэвхтэй байна"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Спам гэж мэдээлэх/хориглох"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Хориглох"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Спам биш"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Хоригийг авах"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Спам"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g>-г хориглох уу?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Энэ дугаараас цаашид ирэх дуудлага, дуут шууданг хориглох болно."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Дуудлагыг спам гэж мэдээлэх"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Энэ дугаараас цаашид ирэх дуудлага, дуут шууданг хориглох болно. Энэ дуудлагыг спам гэж мэдээлнэ."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g>-н хоригийг авах уу?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Энэ дугаарын хоригийг авч, спам биш гэж мэдээлэх болно. Цаашид ирэх дуудлага, дуут шууданг спам гэж үзэхгүй."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g>-г зөвшөөрөгдсөн жагсаалтад оруулах уу?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Зөвшөөрөгдсөн жагсаалт"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Энэ дугаараас цаашид ирэх дуудлага, дуут шууданг спам гэж үзэхгүй. Энэ дугаарыг спам гэж мэдээлэхгүй."</string>
-</resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
deleted file mode 100644
index fa42dd96c..000000000
--- a/res/values-mr/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"फोन"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"फोन"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"फोन डायलपॅड"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"फोन"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"कॉल इतिहास"</string>
- <string name="action_report_number" msgid="4635403959812186162">"चुकीच्या नंबरचा अहवाल द्या"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"नंबर कॉपी करा"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"प्रतिलेखन कॉपी करा"</string>
- <string name="action_block_number" msgid="1482657602262262134">"नंबर अवरोधित करा"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> अवरोधित केला"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"नंबर अनावरोधित करा"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> अनावरोधित केला"</string>
- <string name="block_number_undo" msgid="591338370336724156">"पूर्ववत करा"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"हटवा"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"कॉल करण्यापूर्वी नंबर संपादित करा"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"कॉल इतिहास साफ करा"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"व्हॉइसमेल हटवा"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"व्हॉइसमेलचे संग्रहण करा"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"व्हॉइसमेल सामायिक करा"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"व्हॉइसमेल आढळले"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"व्हॉइसमेल संग्रहित केले"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"पूर्ववत करा"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"संग्रहणावर जा"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"कॉल इतिहास साफ करायचा?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"हे आपल्या कॉल इतिहासातून सर्व कॉल हटवेल"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"कॉल इतिहास साफ करत आहे…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"फोन"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"सुटलेला कॉल"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"कार्याचा कॉल चुकविला"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"सुटलेले कॉल"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> सुटलेले कॉल"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"पुन्हा कॉल करा"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"संदेश"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> व्हॉइसमेल </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> व्हॉइसमेल </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"प्ले करा"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> कडील नवीन व्हॉइसमेल"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"व्हॉइसमेल प्ले करू शकलो नाही"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"व्हॉइसमेल लोड करत आहे…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"व्हॉइसमेल संग्रहित करीत आहे..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"व्हॉइसमेल लोड करू शकलो नाही"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"केवळ व्हॉइसमेल सह कॉल"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"केवळ येणारे कॉल"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"केवळ केले जाणारे कॉल"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"केवळ सुटलेले कॉल"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"दृश्‍यमान व्हॉइसमेल"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"नंबरवर कॉल न करता आपले व्हॉइसमेल पहा आणि ऐका. डेटा शुल्क लागू होऊ शकतात."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"सेटिंग्ज"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"व्हॉइसमेल अद्यतने उपलब्ध नाहीत"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"नवीन व्हॉइसमेल प्रतीक्षा करत आहे. आत्ता लोड करू शकत नाही."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"आपला व्हॉइसमेल सेट करा"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"ऑडिओ उपलब्ध नाही"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"सेट अप"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"व्हॉइसमेलवर कॉल करा"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"नंबर निवडा"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"नंबर निवडा"</string>
- <string name="make_primary" msgid="5829291915305113983">"ही निवड लक्षात ठेवा"</string>
- <string name="description_search_button" msgid="3660807558587384889">"शोधा"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"डायल करा"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"डायल करण्यासाठी नंबर"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"प्‍ले करा किंवा प्‍लेबॅक थांबवा"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"स्‍पीकरफोन चालू किंवा बंद करा"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"प्लेबॅक स्थान शोधतात"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"प्‍लेबॅक दर कमी करा"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"प्‍लेबॅक दर वाढवा"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"कॉल इतिहास"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"अधिक पर्याय"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"डायल पॅड"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"केवळ केले जाणारे दर्शवा"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"केवळ येणारे दर्शवा"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"केवळ सुटलेले दर्शवा"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"केवळ व्हॉइसमेल दर्शवा"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"सर्व कॉल दर्शवा"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2-सेकंद विराम जोडा"</string>
- <string name="add_wait" msgid="3360818652790319634">"प्रतीक्षा करा जोडा"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"सेटिंग्ज"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"नवीन संपर्क"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"सर्व संपर्क"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"कॉल तपशील"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"तपशील उपलब्ध नाहीत"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"स्‍पर्श टोन कीपॅडचा वापर करा"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"चालू असलेल्या कॉलवर परत जा"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"कॉल जोडा"</string>
- <string name="type_incoming" msgid="6502076603836088532">"येणारा कॉल"</string>
- <string name="type_outgoing" msgid="343108709599392641">"केले जाणारे कॉल"</string>
- <string name="type_missed" msgid="2720502601640509542">"सुटलेला कॉल"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"येणारा व्हिडिओ कॉल"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"जाणारे व्हिडिओ कॉल"</string>
- <string name="type_missed_video" msgid="954396897034220545">"सुटलेला व्हिडिओ कॉल"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"व्हॉइसमेल"</string>
- <string name="type_rejected" msgid="7783201828312472691">"नाकारलेला कॉल"</string>
- <string name="type_blocked" msgid="3521686227115330015">"अवरोधित केलेला कॉल"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"येणारे कॉल"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"व्हॉइसमेल प्ले करा"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> संपर्क पहा"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g> ला कॉल करा"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> साठी संपर्क तपशील"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> कॉल."</string>
- <string name="description_video_call" msgid="2933838090743214204">"व्हिडिओ कॉल."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g> वर SMS पाठवा"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"न ऐकलेला व्हॉइसमेल"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"व्हॉइस शोध प्रारंभ करा"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> ला कॉल करा"</string>
- <string name="unknown" msgid="740067747858270469">"अज्ञात"</string>
- <string name="voicemail" msgid="3851469869202611441">"व्हॉइसमेल"</string>
- <string name="private_num" msgid="6374339738119166953">"खाजगी नंबर"</string>
- <string name="payphone" msgid="7726415831153618726">"सार्वजनिक फोन"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> सेकंद"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> मिनिट <xliff:g id="SECONDS">%s</xliff:g> सेकंद"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> रोजी <xliff:g id="TIME">%2$s</xliff:g> वाजता"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"या नंबरवर कॉल करू शकत नाही"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"व्हॉइसमेल सेट करण्‍यासाठी, मेनू आणि सेटिंग्‍ज वर जा."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"व्हॉइसमेलला कॉल करण्यासाठी, प्रथम विमान मोड बंद करा."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"लोड करीत आहे..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"सिम कार्ड मधून लोड करीत आहे..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"सिम कार्ड संपर्क"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"कोणताही संपर्क अॅप उपलब्ध नाही"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"व्हॉइस शोध उपलब्ध नाही"</string>
- <string name="call_not_available" msgid="8941576511946492225">"फोन अनुप्रयोग अक्षम केला गेला असल्याने फोन कॉल करणे शक्य नाही."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"या डिव्हाइसवर त्यासाठी कोणताही अॅप नाही"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"संपर्क शोधा"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"नंबर जोडा किंवा संपर्क शोधा"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"आपला कॉल इतिहास रिक्त आहे"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"एक कॉल करा"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"आपल्‍याकडे कोणतेही सुटलेले कॉल नाहीत."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"आपला व्हॉइसमेल इनबॉक्स रिक्त आहे."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"आपले व्हॉइसमेल संग्रहण रिक्त आहे."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"केवळ आवडीचे दर्शवा"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"कॉल इतिहास"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"व्हॉइसमेल संग्रहण"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"सर्व"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"सुटलेले"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"व्हॉइसमेल"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"नवीन, सरलीकृत अवरोधित करणे"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"आपले अधिक चांगले संरक्षण करण्यासाठी, अवरोधित करणे ज्या पद्धतीने कार्य करते ते बदलण्याची फोनला गरज आहे. आपले अवरोधित केलेले नंबर कॉल आणि मजकूर थांबवतील आणि कदाचित इतर अॅप्ससह सामायिक केले जाऊ शकतील."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"अनुमती द्या"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> अवरोधित करायचा?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"या नंबर वरून केलेले कॉल अवरोधित केले जातील आणि व्हॉइसमेल स्वयंचलितपणे हटविले जातील."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"या नंबर वरून केलेले कॉल अवरोधित केले जातील परंतु कॉलर आपल्‍यासाठी व्हॉइसमेल सोडण्यात अद्याप सक्षम असू शकेल."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"आपल्याला या नंबर वरून कॉल किंवा मजकूर प्राप्त होणार नाहीत."</string>
- <string name="block_number_ok" msgid="770551992296781873">"अवरोधित करा"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> ला अनावरोधित करायचे?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"अनावरोधित करा"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"स्पीड डायल"</string>
- <string name="tab_history" msgid="2563144697322434940">"कॉल इतिहास"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"संपर्क"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"व्हॉइसमेल"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"आवडी मधून काढले"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"पूर्ववत करा"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> ला कॉल करा"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"नवीन संपर्क तयार करा"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"संपर्कांमध्‍ये जोडा"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS पाठवा"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"व्‍हिडिओ कॉल करा"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"नंबर अवरोधित करा"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> नवीन सुटलेले कॉल"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"अद्याप आपल्‍या स्पीड डायलवर कोणीही नाही"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"एक आवडते जोडा"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"आपल्‍याकडे अद्याप कोणतेही संपर्क नाहीत"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"एक संपर्क जोडा"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"सर्व नंबर पाहण्यासाठी प्रतिमेस स्पर्श करा किंवा पुन्हा क्रम लावण्यासाठी स्पर्श करा आणि धरून ठेवा"</string>
- <string name="remove_contact" msgid="1080555335283662961">"काढा"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"व्हिडिओ कॉल"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"एक संदेश पाठवा"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"कॉल तपशील"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> वर कॉल करा"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> वरून कॉल सुटला."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> वरून कॉलला उत्तर दिले."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> कडून न वाचलेला व्हॉइसमेल."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> कडून व्हॉॅइसमेल."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> वर कॉल करा."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> वर"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> द्वारे"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> द्वारे"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> वर, <xliff:g id="NUMBER">%2$s</xliff:g> द्वारे"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="NUMBER">%2$s</xliff:g> द्वारे <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"कॉल करा"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> वर कॉल करा"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"व्हिडिओ कॉल <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> कडील व्हॉइसमेल ऐका"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> कडील व्हॉइसमेल प्ले करा"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> कडील व्हॉइसमेल ला विराम द्या"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> वरील व्हॉइसमेल हटवा"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> नवीन व्हॉइसमेल</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> नवीन व्हॉइसमेल</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> साठी संपर्क तयार करा"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"विद्यमान संपर्कांमध्ये <xliff:g id="NAMEORNUMBER">^1</xliff:g> जोडा"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> साठी कॉल तपशील"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"कॉल इतिहासातून हटविले"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"आज"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"काल"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"अधिक जुने"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"कॉल सूची"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"स्पीकर चालू करा."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"स्पीकर बंद करा."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"अधिक जलद प्ले करा."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"अधिक धीमे प्ले करा."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"प्लेबॅक प्रारंभ करा किंवा त्यास विराम द्या."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"प्रदर्शन पर्याय"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"ध्वनी आणि कंपने"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"प्रवेशयोग्यता"</string>
- <string name="ringtone_title" msgid="760362035635084653">"फोन रिंगटोन"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"कॉल साठी कंपन देखील करा"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"डायलपॅड टोन"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"डायलपॅड टोन लांबी"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"सामान्य"</item>
- <item msgid="6177579030803486015">"लांब"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"द्रुत प्रतिसाद"</string>
- <string name="call_settings_label" msgid="313434211353070209">"कॉल"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"कॉल अवरोधित करणे"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"कॉल अवरोधित करणे तात्पुरते बंद"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"आपण मागील 48 तासात या फोनवरून आणीबाणी सेवांशी संपर्क साधला असल्याने कॉल अवरोधित करणे अक्षम केले गेले आहे. एकदा 48 तासांचा कालावधी कालबाह्य झाला की ते स्वयंचलितपणे पुन्हा सक्षम केले जाईल."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"नंबर अायात करा"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"आपण पूर्वीपासून काही कॉलरना स्वयंचलितपणे इतर अॅप्सद्वारे व्हॉइसमेलमध्ये पाठविण्यासाठी चिन्हांकित केले आहे."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"नंबर पहा"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"आयात करा"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"आयात अयशस्वी झाले"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"व्हॉइसमेल संग्रहित करण्यात अयशस्वी झाले."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"नंबर अनावरोधित करा"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"नंबर जोडा"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"या नंबरवरून केलेले कॉल अवरोधित केले जातील आणि व्हॉइसमेल स्वयंचलितपणे हटविले जातील."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"या नंबर वरून केलेले कॉल अवरोधित केले जातील परंतु ते आपल्‍यासाठी व्हॉइसमेल सोडण्यात अद्याप कदाचित सक्षम असतील."</string>
- <string name="block_list" msgid="7760188925338078011">"अवरोधित केलेले नंबर"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> अवैध आहे."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> आधीच अवरोधित केलेला आहे."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"कॉल अवरोधित करणे 48 तासांसाठी अक्षम केले"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"एक आणीबाणीचा कॉल केल्‍याने अक्षम केले."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"कॉल करण्याची खाती"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"चालू करा"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"परवानग्या सेट करा"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"स्पीड डायल सक्षम करण्‍यासाठी, संपर्क परवानगी चालू करा."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"आपला कॉल लॉग पाहण्‍यासाठी, फोन परवानगी चालू करा."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"आपले संपर्क पाहण्‍यासाठी, संपर्क परवानगी चालू करा."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"आपल्‍या व्हॉइसमेलमध्‍ये प्रवेश करण्‍यासाठी, फोन परवानगी चालू करा."</string>
- <string name="permission_no_search" msgid="84152933267902056">"आपले संपर्क शोधण्‍यासाठी, संपर्क परवानग्या चालू करा."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"कॉल करण्यासाठी, फोन परवानगी चालू करा."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"फोन अॅपला सिस्टीम स‍ेटिंग्जमध्ये लिहिण्याची परवानगी नाही."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"अवरोधित केले"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> सक्रिय आहे"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"अवरोधित करा/स्पॅमचा अहवाल द्या"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"अवरोधित करा"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"स्पॅम नाही"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"अनावरोधित करा"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"स्पॅम"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> अवरोधित करायचा?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"या नंबरवरील भविष्यातील कॉल आणि व्हॉइसमेल अवरोधित केले जातील."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"कॉलचा स्पॅम म्हणून अहवाल द्या"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"या नंबरवरील भविष्यातील कॉल आणि व्हॉइसमेल अवरोधित केले जातील. या कॉलचा स्पॅम म्हणून अहवाल दिला जाईल."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> ला अनावरोधित करायचे?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"हा नंबर अनावरोधित केला जाईल आणि त्याचा स्पॅम नाही म्हणून अहवाल दिला जाईल. भविष्यातील कॉल आणि व्हॉइसमेल स्पॅम म्हणून ओळखले जाणार नाही."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> ची श्वेतसूची करायची?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"श्वेतसूची"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"या नंबर वरून केलेले भविष्यातील कॉल आणि व्हॉइसमेल स्पॅम म्हणून ओळखले जाणार नाहीत. या नंबरचा स्पॅम नाही म्हणून अहवाल दिला जाईल."</string>
-</resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
deleted file mode 100644
index 65451d41f..000000000
--- a/res/values-ms/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Pad Dail Telefon"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Sejarah panggilan"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Laporkan nombor yang tidak tepat"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Salin nombor"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Salin transkripsi"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Sekat nombor"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> disekat"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Nyahsekat nombor"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> dinyahsekat"</string>
- <string name="block_number_undo" msgid="591338370336724156">"BUAT ASAL"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Padam"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Edit nombor sebelum memanggil"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Kosongkan sejarah panggilan"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Padamkan mel suara"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arkibkan mel suara"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Kongsi mel suara"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Mel suara dipadamkan"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Mel suara diarkibkan"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"BUAT ASAL"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"PRG KE ARKIB"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Kosongkan sejarah panggilan?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Tindakan ini akan memadamkan semua panggilan daripada sejarah anda"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Mengosongkan sejarah panggilan..."</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Panggilan terlepas"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Panggilan terlepas daripada tempat kerja"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Panggilan terlepas"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> panggilan terlepas"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Panggil balik"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Mesej"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Mel suara </item>
- <item quantity="one">Mel suara</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Main"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Mel suara baharu daripada <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Tidak dapat memainkan mel suara"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Memuatkan mel suara..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Mengarkib mel suara…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Tidak dapat memuatkan mel suara"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Panggilan dengan mel suara sahaja"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Panggilan masuk sahaja"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Panggilan keluar sahaja"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Panggilan terlepas sahaja"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Mel suara visual"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Lihat dan dengar mel suara anda tanpa perlu memanggil nombor. Caj data mungkin dikenakan."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Tetapan"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Kemas kini mel suara tidak tersedia"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Mel suara baharu sedang menunggu. Tidak dapat memuat sekarang."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Sediakan mel suara anda"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio tidak tersedia"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Menyediakan"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Panggil mel suara"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Pilih nombor"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Pilih nombor"</string>
- <string name="make_primary" msgid="5829291915305113983">"Ingat pilihan ini"</string>
- <string name="description_search_button" msgid="3660807558587384889">"cari"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"dail"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"nombor untuk didail"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Mainkan atau berhenti main balik"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Hidupkan atau matikan pembesar suara"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Dapatkan kedudukan main balik"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Kurangkan kadar main balik"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Tingkatkan kadar main balik"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Sejarah Panggilan"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Lagi pilihan"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"pad dail"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Tunjuk panggilan keluar shj"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Tunjuk panggilan masuk shj"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Tunjuk panggilan terlepas shj"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Tunjukkan mel suara sahaja"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Tunjukkan semua panggilan"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Tambah jeda 2 saat"</string>
- <string name="add_wait" msgid="3360818652790319634">"Tambah penungguan"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Tetapan"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Kenalan baharu"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Semua kenalan"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Butiran panggilan"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Butiran tidak tersedia"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Gunakan pad kekunci nada sentuh"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Kembali ke panggilan yang sedang berlangsung"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Tambah panggilan"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Panggilan masuk"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Panggilan keluar"</string>
- <string name="type_missed" msgid="2720502601640509542">"Panggilan tidak dijawab"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Panggilan video masuk"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Panggilan video keluar"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Telah terlepas panggilan video"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Mel suara"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Panggilan yang ditolak"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Panggilan yang disekat"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Panggilan masuk"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Mainkan mel suara"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Lihat kenalan <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Panggil <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Butiran hubungan untuk <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> panggilan."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Panggilan video."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Hantar SMS kepada <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Mel suara belum didengar"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Mulakan carian suara"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Panggil <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Tidak diketahui"</string>
- <string name="voicemail" msgid="3851469869202611441">"Mel suara"</string>
- <string name="private_num" msgid="6374339738119166953">"Nombor peribadi"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefon Awam"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> saat"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> saat"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> pada <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Tidak dapat menghubungi nombor ini"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Untuuk menyediakan mel suara, pergi ke Menu &gt; Tetapan."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Untuk membuat panggilan ke mel suara, mula-mula matikan mod Pesawat."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Memuatkan…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Memuatkan dari kad SIM..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Kenalan kad SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Tiada apl kenalan tersedia"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Carian suara tidak tersedia"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Tidak boleh membuat panggilan telefon kerana aplikasi Telefon telah dilumpuhkan."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Tiada apl untuk tindakan itu pada peranti ini"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Cari kenalan"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Tambahkn no. atau cari kenalan"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Sejarah panggilan anda kosong"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Buat panggilan"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Anda tiada panggilan terlepas."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Peti masuk mel suara anda kosong."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Arkib mel suara anda kosong."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Tunjukkan kegemaran sahaja"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Sejarah Panggilan"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arkib Mel Suara"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Semua"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Tidak dijawab"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Mel suara"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Penyekatan mudah yang baharu"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Untuk melindungi anda dengan lebih berkesan, Telefon perlu menukar cara penyekatan berfungsi. Nombor yang disekat akan menghentikan panggilan dan teks serta boleh dikongsi dengan apl lain."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Benarkan"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Sekat <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Panggilan daripada nombor ini akan disekat dan mel suara akan dipadamkan secara automatik."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Panggilan daripada nombor ini akan disekat, tetapi pemanggil masih boleh meninggalkan mel suara kepada anda."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Anda tidak akan menerima panggilan atau teks daripada nombor ini lagi."</string>
- <string name="block_number_ok" msgid="770551992296781873">"SEKAT"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Nyahsekat <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"NYAHSEKAT"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Dail laju"</string>
- <string name="tab_history" msgid="2563144697322434940">"Sejarah Panggilan"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kenalan"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Mel suara"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Dialih keluar daripada kegemaran"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Buat asal"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Panggil <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Buat kenalan baharu"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Tambahkan pada kenalan"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Hantar SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Buat panggilan video"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Sekat nombor"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> panggilan terlepas baharu"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Belum ada sesiapa pada dail pantas anda"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Tambahkan kegemaran"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Anda belum mempunyai sebarang kenalan"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Tambahkan kenalan"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Sentuh imej untuk melihat semua nombor atau sentuh &amp; tahan untuk menyusun semula"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Alih keluar"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Panggilan video"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Hantar mesej"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Butiran panggilan"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Panggil <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Panggilan tidak dijawab daripada <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Panggilan telah dijawab daripada <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Mel suara yang belum dibaca daripada <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Mel suara daripada <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Buat panggilan kepada <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"pada <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"melalui <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"melalui <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"pada <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, melalui <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> melalui <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Panggil"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Panggil <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Panggilan video <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Dengar mel suara daripada <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Mainkan mel suara daripada <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Jeda mel suara daripada <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Padamkan mel suara daripada <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> mel suara baharu</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> mel suara baharu</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Buat kenalan untuk <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Tambahkan <xliff:g id="NAMEORNUMBER">^1</xliff:g> pada kenalan yang sedia ada"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Butiran panggilan untuk <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Dipadamkan dari sejarah panggilan"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Hari ini"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Semalam"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Lebih lama"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Senarai panggilan"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Hidupkan pembesar suara."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Matikan pembesar suara."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Main lebih cepat."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Main lebih perlahan."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Mulakan atau jeda main balik."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Pilihan paparan"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Bunyi dan getaran"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Kebolehaksesan"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Nada dering telefon"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Juga bergetar untuk panggilan"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Nada pad dail"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Panjang nada pad dail"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Biasa"</item>
- <item msgid="6177579030803486015">"Panjang"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Respons pantas"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Panggilan"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Sekatan panggilan"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Sekatan panggilan dimatikan sementara"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Sekatan panggilan telah dilumpuhkan kerana anda menghubungi perkhidmatan kecemasan daripada telefon ini dalam masa 48 jam yang lalu. Ciri ini akan didayakan semula secara automatik apabila tempoh 48 jam berakhir."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Import nombor"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Sebelum ini anda telah menandakan beberapa pemanggil dihantar ke mel suara secara automatik melalui aplikasi lain."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Lihat nombor"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Import"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Pengimportan gagal"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Gagal mengarkib mel suara."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Nyahsekat nombor"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Tambah nombor"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Panggilan daripada nombor ini akan disekat dan mel suara akan dipadamkan secara automatik."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Panggilan daripada nombor ini akan disekat, tetapi mereka masih boleh meninggalkan mel suara kepada anda."</string>
- <string name="block_list" msgid="7760188925338078011">"Nombor yang disekat"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> tidak sah."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> sudah disekat."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Sekatan panggilan dilumpuhkan selama 48 jam"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Dilumpuhkan kerana panggilan kecemasan telah dibuat."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Akaun panggilan"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Hidupkan"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Tetapkan kebenaran"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Untuk mendayakan dail laju, hidupkan kebenaran Kenalan."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Untuk melihat log panggilan anda, hidupkan kebenaran Telefon."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Untuk melihat kenalan anda, hidupkan kebenaran Kenalan."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Untuk mengakses mel suara anda, hidupkan kebenaran Telefon."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Untuk mencari kenalan anda, hidupkan kebenaran Kenalan."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Untuk membuat panggilan, hidupkan kebenaran Telefon."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Apl telefon tiada kebenaran untuk menulis ke tetapan sistem."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Disekat"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> aktif"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Sekat/laporkan spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Sekat"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Bukan spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Nyahsekat"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Sekat <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Panggilan dan mel suara daripada nombor ini akan disekat selepas ini."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Laporkan panggilan sbg spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Panggilan &amp; mel suara drp nombor ini akan disekat selepas ini. Panggilan akan dilaporkan sbg spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Nyahsekat <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Nombor akn dinyahsekat &amp; dilaporkan bkn spam. Panggilan &amp; mel suara tdk akn dikenal pasti sbg spam."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Senarai putihkan <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Senarai putihkan"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Panggilan &amp; mel suara drp nombor ini tdk akn dikenal pasti sbg spam. Nombor akn dilaporkan bkn spam."</string>
-</resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
deleted file mode 100644
index 5cf82a4b0..000000000
--- a/res/values-my/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"ဖုန်း"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"ဖုန်း"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"ဖုန်းခလုတ်ခုံ"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"ဖုန်း"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"ခေါ်ဆိုမှု မှတ်တမ်း"</string>
- <string name="action_report_number" msgid="4635403959812186162">"မမှန်ကန်သည့် နံပါတ်အား သတင်းပို့ပါ"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"နံပါတ်ကိုကူးရန်"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"အသံမှစာအဖြစ်ဘာသာပြန်ခြင်းကို ကူးရန်"</string>
- <string name="action_block_number" msgid="1482657602262262134">"နံပါတ်ကို ပိတ်ရန်"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> ကိုပိတ်ဆို့လိုက်ပါပြီ"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"နံပါတ်ကို ဖွင့်မည်"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> ကိုပိတ်ဆို့ခြင်းမှ ဖယ်ရှားလိုက်ပါပြီ"</string>
- <string name="block_number_undo" msgid="591338370336724156">"နောက်ပြန်လုပ်ပါ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"ဖျက်ရန်"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"မခေါ်ဆိုမီ နံပါတ်ကိုတည်းဖြတ်ရန်"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"ခေါ်ဆိုမှု မှတ်တမ်း ရှင်းရန်"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"အသံပို့စာ အားဖျက်ရန်"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"အသံမေးလ်ကို မှတ်တမ်းသိမ်းပါ"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"အသံမေးလ်ကို မျှဝေပါ"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"အသံမေးလ် ရှာတွေ့ခဲ့"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"သိုလှောင်ထားသည့် အသံစာ"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ပြန်ဖျက်ရန်"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"မှတ်တမ်းသို့ သွားပါ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"ခေါ်ဆိုမှု မှတ်တမ်းကို ဖယ်ရှားရမလား။"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"သင့်မှတ်တမ်းထဲရှိ ခေါ်ဆိုမှုများအားလုံးကို ဖျက်ပါမည်"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"ခေါ်ဆိုမှု မှတ်တမ်းကို ရှင်းနေ…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"ဖုန်း"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"လွတ်သွားသော ခေါ်ဆိုမှု"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"လွတ်သွားသော အလုပ်ဖုန်းခေါ်ဆိုမှု"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"လွတ်သွားသော ခေါ်ဆိုမှုများ"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"လွတ်သွားသော ခေါ်ဆိုမှု <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"ပြန်ခေါ်ပါ"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"မက်ဆေ့ဂျ်"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"><xliff:g id="COUNT">%1$d</xliff:g> အသံမေးလ်များ </item>
- <item quantity="one"> အသံမေးလ်</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"နားထောင်သည်"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> ဆီမှ အသံစာ အသစ်"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"အသံမေးလ်ကို ဖွင့်မပေးနိုင်ခဲ့"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"အသံမေးလ်ကို တင်ပေးနေ…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"အသံစာတိုကို သိမ်းနေသည်..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"အသံမေးလ်ကို တင်မပေးနိုင်ခဲ့"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"အသံမေးလ်ပါသော အဝင်ခေါ်ဆိုမှုများသာ"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"အဝင်ဖုန်းသာ"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"အထွက်ခေါ်ဆိုမှုများသာ"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"လွတ်သွားသော ဖုန်းသာ"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"အသံအသုံးပြုစာအား စာသားအသွင်ပြောင်းခြင်း"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"နံပါတ်တစ်ခုသို့ ခေါ်ဆိုရန် မလိုဘဲ၊ သင်၏ အသံအသုံးပြုစာအား ကြည့်ရှု နားထောင်ပါ။ ဒေတာအတွက် ကျသင့်ငွေရှိနိုင်သည်။"</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"ဆက်တင်များ"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"အသံမေးလ် မွမ်းမံမှုများ မရှိ"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"အသံမေးလ် အသစ် စောင့်နေသည်။ ယခုချက်ချင်း တင်မပေးနိုင်ပါ။"</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"သင်၏ အသံမေးလ်ကို စဖွင့်သတ်မှတ်ရန်"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"အသံ မရှိ"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"အစီအမံလုပ်ပါ"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"အသံပို့စာစနစ်ခေါ်ရန်"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"နံပါတ်ရွေးပါ"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"နံပါတ်ရွေးပါ"</string>
- <string name="make_primary" msgid="5829291915305113983">"ဒီရွေးချယ်မှုကို မှတ်ထားပါ"</string>
- <string name="description_search_button" msgid="3660807558587384889">"ရှာဖွေရန်"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"ဖုန်းခေါ်သည်"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"ခေါ်ရန် နံပါတ်"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"ပြန်ဖွင့်ပြမှုကို ဖွင့်ရန် သို့မဟုတ် ရပ်ရန်"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"စပီကာဖုန်းကို ဖွင့် သို့မဟုတ် ပိတ်ရန်"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"အသံပြန်ဖွင့်ရေး နေရာကို ရှာရန်"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"ပြန်ဖွင့်မှု နှုန်းကို လျှော့ချရန်"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"ပြန်ဖွင့်မှု နှုန်းကို မြှင့်ရန်"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"ယခင်ခေါ်ဆိုမှုများ"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"ပိုမိုရွေးချယ်စရာများ"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"ဖုန်းနံပါတ်ကွက်"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"အထွက်ခေါ်ဆိုခြင်းများသာပြပါ"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"အဝင်ခေါ်ဆိုမှုသာ ပြပါ"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"လွတ်သွားသော ခေါ်ဆိုမှုများသာပြပါ"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"အသံပို့စာများသာ ပြပါ"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"ဖုန်းခေါ်မှုအားလုံးပြရန်"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"၂စက္ကန့်ရပ်ဆိုင်းရန် ထည့်ပါ"</string>
- <string name="add_wait" msgid="3360818652790319634">"စောင့်ဆိုင်းခြင်း ထည့်ပါ"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"ဆက်တင်များ"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"အဆက်အသွယ်အသစ်"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"လိပ်စာများအားလုံး"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"ဖုန်းခေါ်မှု အသေးစိတ်"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"အသေးစိတ် မရှိ"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"touch tone ကီးခလုတ် ကိုအသုံးပြုပါ"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"ဖုန်းပြန်ခေါ်မှု ပြုလုပ်နေစဉ်"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"ဖုန်းခေါ်မှု ထပ်ထည့်ပါ"</string>
- <string name="type_incoming" msgid="6502076603836088532">"အဝင်ခေါ်ဆိုမှု"</string>
- <string name="type_outgoing" msgid="343108709599392641">"အထွက်ဖုန်း"</string>
- <string name="type_missed" msgid="2720502601640509542">"လွဲသွားသော ဖုန်းခေါ်မှု"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"ဝင်လာသည့် ဗီဒီယို ခေါ်ဆိုမှု"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"ပြုလုပ်နေဆဲ ဗီဒီယို ခေါ်ဆိုမှု"</string>
- <string name="type_missed_video" msgid="954396897034220545">"လွတ်သွားသော ဗီဒီယို ခေါ်ဆိုမှု"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"အသံစာပို့စနစ်"</string>
- <string name="type_rejected" msgid="7783201828312472691">"ငြင်းဆိုခဲ့သည့် ခေါ်ဆိုမှု"</string>
- <string name="type_blocked" msgid="3521686227115330015">"ခေါ်ဆိုမှု ပိတ်ဆို့ထား"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"အဝင်ခေါ်ဆိုမှုများ"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"အသံပို့စာ နားထောင်ရန်"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> လိပ်စာကြည့်ရန်"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g> ကိုခေါ်ပါ"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> ရဲ့ အဆက်အသွယ် အသေးစိတ်"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> ခါ"</string>
- <string name="description_video_call" msgid="2933838090743214204">"ဗီဒီယို ခေါ်ဆိုမှု"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g>ထံသို့ SMS ပို့ရန်"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"မနားထောင်ရသေးသော အသံပို့စာ"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"အသံဖွင့်ရှာဖွေမှု စရန်"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> ကိုခေါ်ပါ"</string>
- <string name="unknown" msgid="740067747858270469">"အကြောင်းအရာ မသိရှိ"</string>
- <string name="voicemail" msgid="3851469869202611441">"အသံစာပို့စနစ်"</string>
- <string name="private_num" msgid="6374339738119166953">"လျို့ဝှက် နံပါတ်"</string>
- <string name="payphone" msgid="7726415831153618726">"ငွေပေးရသည့်ဖုန်း"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> စက္ကန့်"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> မိနစ် <xliff:g id="SECONDS">%s</xliff:g> စက္ကန့်"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g> ၌"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"ဒီနံပါတ်ကို မခေါ်ဆိုနိုင်ပါ"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"အသံစာပို့စနစ်ကို ပြင်ဆင်ရန်၊ မီနူး ပြီးနောက် ဆက်တင် သို့သွားပါ"</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"အသံစာပို့စနစ်ကို ခေါ်ဆိုမှုပြုရန် လေယာဉ်ပျံပေါ်သုံးစနစ်ကို ပိတ်ပါ"</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"ဖွင့်နေစဉ်"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEIDနံပါတ်"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"ဆင်းမ်ကဒ်မှ ဖွင့်နေပါသည်..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"ဆင်းမ်ကဒ်မှ အဆက်အသွယ်များ"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"အဆက်အသွယ်များ အက်ပ်မရှိ"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"အသံဖြင့် ရှာဖွေမှု မရှိ"</string>
- <string name="call_not_available" msgid="8941576511946492225">"ဖုန်း အပလီကေးရှင်းကို ပိတ်ထား၍ ဖုန်း ခေါ်ဆိုမှု မပြုလုပ်နိုင်ခဲ့ပါ။"</string>
- <string name="activity_not_available" msgid="2287665636817987623">"ဒီကိရိယာထဲမှာ အဲဒါ့အတွက် အက်ပ်မရှိပါ"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"အဆက်အသွယ်များရှာပါ"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"နံပါတ်ပေါင်းထည့်ပါ သို့မဟုတ် အဆက်အသွယ်များ ရှာဖွေပါ"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"သင့်ခေါ်ဆိုမှတ်တမ်းတွင် ဘာမှမရှိပါ"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"ဖုန်းခေါ်ရန်"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"သင့်တွင် လွတ်သွားသည့်ခေါ်ဆိုမှုများ မရှိပါ။"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"သင့်အသံမေးလ် စာတိုက်ပုံးတွင် ဘာမှမရှိပါ။"</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"သင့် အသံမေးလ်မှတ်တမ်းတွင် ဘာမှမရှိပါ။"</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"အနှစ်သက်ဆုံးများသာ ပြပါ"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"ခေါ်ဆိုမှုသမိုင်း"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"အသံမေးလ် မှတ်တမ်း"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"အားလုံး"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"မကိုင်မိလိုက်သော"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"အသံစာ"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"အသစ်၊ ရိုးရှင်းသည့် ပိတ်ဆို့မှု"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"သင့်အား ပိုမိုကောင်းမွန်စွာကာကွယ်နိုင်ရန်၊ ဖုန်းသည် ပိတ်ဆို့ခြင်းအလုပ်လုပ်ပုံကို ပြောင်းလဲရန် လိုအပ်ပါသည်။ သင် ပိတ်ဆို့ထားသည့် နံပါတ်များမှ ခေါ်ဆိုခြင်းနှင့် စာပို့ခြင်းများကို ရပ်တန့်လိုက်မည်ဖြစ်ပြီး၊ ၎င်းနံပါတ်များကို အခြားအက်ပ်များဖြင့်လည်း မျှဝေနိုင်ပါသည်။"</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"ခွင့်ပြုမည်"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g>ကို ပိတ်ဆို့မလား။"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"ဤနံပါတ်မှခေါ်ဆိုမှုများကို ပိတ်ဆို့လိုက်မည်ဖြစ်ပြီး အသံမေးလ်များကို အလိုအလျောက် ဖျက်လိုက်ပါမည်။"</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"ဤနံပါတ်မှ ခေါ်ဆိုမှုများကို ပိတ်ဆို့လိုက်ပါမည်၊ သို့သော် ၎င်းတို့သည် သင့်ကို အသံမေးလ်ချန်ခဲ့နိုင်ပါသည်။"</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"ဤနံပါတ်ထံမှ ဖုန်းခေါ်ဆိုမှုများနှင့် စာများကို သင် ထပ်မံရရှိတော့မည် မဟုတ်ပါ။"</string>
- <string name="block_number_ok" msgid="770551992296781873">"ပိတ်ဆို့ပါ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> အား ပိတ်ဆို့မှု ဖယ်ရှားလိုက်ရမလား။"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ပိတ်ဆို့ခြင်းမှ ဖယ်ရှားပါ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"အမြန် နံပါတ်လှည့်မှု"</string>
- <string name="tab_history" msgid="2563144697322434940">"ခေါ်ဆိုမှု မှတ်တမ်း"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"အဆက်အသွယ်များ"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"အသံမေးလ်"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"အနှစ်သက်ဆုံးများထဲမှာ ထုတ်လိုက်ပါပြီ"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"နောက်ပြန်လုပ်ပါ"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> ကိုခေါ်ပါ"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"အဆက်အသွယ် အသစ် ဖန်တီးရန်"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"အဆက်အသွယ်သို့ ပေါင်းထည့်ရန်"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"စာတို ပို့ရန်"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"ဗီဒီယို ဖုန်းခေါ်ရန်"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"နံပါတ်ကို ပိတ်ဆို့ရန်"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"လွတ်သွားသောဖုန်း <xliff:g id="NUMBER">%s</xliff:g> ခါ"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"သင်၏အမြန်ခေါ်စာရင်းတွင် မည်သူမျှ မရှိသေးပါ"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"အနှစ်သက်ဆုံးတစ်ခု ထည့်ရန်"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"သင့်တွင် မည်သည့်အဆက်အသွယ်မျှ မရှိသေးပါ"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"အဆက်အသွယ်တစ်ယောက် ထည့်မည်"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"နံပါတ်များ အားလုံးကိုကြည့်ရန် ပုံကို ထိပါ သို့မဟုတ် ပြန်စီရန် ထိလျက် &amp; ကိုင်ထားပါ"</string>
- <string name="remove_contact" msgid="1080555335283662961">"ဖယ်ရှာခြင်း"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"ဗီဒီယို ခေါ်ဆိုမှု"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"စာ တစ်စောင် ပို့ပါ"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"ဖုန်းခေါ်မှု အသေးစိတ်အချက်အလက်"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> အား ခေါ်ပါ"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>၊ <xliff:g id="TYPEORLOCATION">^2</xliff:g>၊ <xliff:g id="TIMEOFCALL">^3</xliff:g>၊ <xliff:g id="PHONEACCOUNT">^4</xliff:g> မှ ခေါ်ဆိုမှု လွတ်သွား၏။"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>၊ <xliff:g id="TYPEORLOCATION">^2</xliff:g>၊ <xliff:g id="TIMEOFCALL">^3</xliff:g>၊ <xliff:g id="PHONEACCOUNT">^4</xliff:g> မှ ခ​ေါ်ဆိုမှုအား ဖြေထား၏။"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>၊ <xliff:g id="TYPEORLOCATION">^2</xliff:g>၊ <xliff:g id="TIMEOFCALL">^3</xliff:g>၊ <xliff:g id="PHONEACCOUNT">^4</xliff:g>ထံမှ အသံစာကို ဖတ်ပါ။"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>၊ <xliff:g id="TYPEORLOCATION">^2</xliff:g>၊ <xliff:g id="TIMEOFCALL">^3</xliff:g>၊ <xliff:g id="PHONEACCOUNT">^4</xliff:g>ထံမှ အသံစာ။"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>၊ <xliff:g id="TYPEORLOCATION">^2</xliff:g>၊ <xliff:g id="TIMEOFCALL">^3</xliff:g>၊ <xliff:g id="PHONEACCOUNT">^4</xliff:g> အား ခေါ်ခြင်း။"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> ၌"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> မှတစ်ဆင့်"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> မှတစ်ဆင့်"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> တွင်၊<xliff:g id="NUMBER">%2$s</xliff:g> မှတစ်ဆင့်"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> <xliff:g id="NUMBER">%2$s</xliff:g> မှတစ်ဆင့်"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"ခေါ်ဆိုမှု"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> အားခေါ်ရန်"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ကိုဗီဒီယိုခေါ်ဆိုမည်။"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>ထံမှ အသံစာကို နားထောင်ရန်"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> မှ အသံအသုံးပြုစာအား ဖွင့်ပါ"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> မှ အသံအသုံးပြုစာအား ရပ်ပါ"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> မှ အသံအသုံးပြုစာအား ဖျက်ပါ"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"> အသံသုံးပို့စာ အသစ် <xliff:g id="COUNT_1">%d</xliff:g> ခု</item>
- <item quantity="one"> အသံသုံးပို့စာ အသစ် <xliff:g id="COUNT_0">%d</xliff:g> ခု</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> အတွက် အဆက်အသွယ် ဖန်တီးမည်"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> အားလက်ရှိ အဆက်အသွယ်သို့ ပေါင်းထည့်မည်"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>၏ ခေါ်ဆိုမှု အသေးစိတ်များ"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"ခေါ်ဆိုမှု မှတ်တမ်းထဲမှ ဖျက်ပစ်ခဲ့"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"ယနေ့"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"မနေ့က"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"ပိုဟောင်းသော"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"ခေါ်ဆိုမှုများ စာရင်း"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"စပီကာကို ဖွင့်ပါ။"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"စပီကာကို ပိတ်ပါ။"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"ပိုမြန်စွာ ကစားရန်"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"ပိုနှေးစွာ ကစားရန်"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"ဖွင့်မှု စတင် သို့မဟုတ် ဆိုင်းငံ့ရန်"</string>
- <string name="list_delimeter" msgid="4571593167738725100">"၊ "</string>
- <string name="display_options_title" msgid="7812852361055667468">"ပြသမှုအတွက်ရွေးစရာများ"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"အသံများနှင့် တုန်ခါမှု"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"ရယူသုံးနိုင်မှု"</string>
- <string name="ringtone_title" msgid="760362035635084653">"ဖုန်း သံစဉ်"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"ဖုန်းဝင်လျှင် တုန်ခါရန်"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"ဖုန်းဒိုင်ခွက် အသံ"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"နံပါတ်ကွက် နှိပ်သံ ချိန်ညှိ"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"ပုံမှန်"</item>
- <item msgid="6177579030803486015">"အရှည်"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"အမြန် တုံ့ပြန်ချက်များ"</string>
- <string name="call_settings_label" msgid="313434211353070209">"ခေါ်ဆိုမှုများ"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"ခေါ်ဆိုမှု ပိတ်ဆို့ခြင်း"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"ခေါ်ဆိုမှုပိတ်ဆို့ခြင်း ယာယီပိတ်ထားသည်"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"သင်သည် လွန်ခဲ့သည့် ၄၈ နာရီအတွင်း ဤဖုန်းဖြင့် အရေးပေါ်ဌာနကိုဖုန်း ခေါ်ဆိုခဲ့သောကြောင့် ခေါ်ဆိုမှုပိတ်ဆို့ခြင်းကို ဖြုတ်ထားသည်။ ၄၈ နာရီကျော်လွန်သည်နှင့် ၎င်းကိုအလိုအလျောက် ပြန်ဖွင့်ပေးပါမည်။"</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"နံပါတ်များ သွင်းရန်"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"သင်သည် ယခင်က အချို့ခေါ်ဆိုသူများကို အလိုအလျောက် အခြား အက်ပ်များမှ တဆင့် အသံစာသို့ ပို့ရန် မှတ်ပေးခဲ့သည်။"</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"နံပါတ်များကို ကြည့်ရန်"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"တင်သွင်းရန်"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"တင်သွင်းမှု မအောင်မြင်ပါ"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"အသံစာတိုကို သိမ်း၍မရပါ။"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"နံပါတ်ကို ဖွင့်မည်"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"နံပါတ် ထည့်သွင်းပါ"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"ဤနံပါတ်များမှ ခေါ်ဆိုမှုများကို ပိတ်ဆို့လိုက်မည်ဖြစ်ပြီး အသံမေးလ်များကို အလိုအလျောက် ဖျက်လိုက်ပါမည်။"</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"ဤနံပါတ်များမှ ခေါ်ဆိုမှုများကို ပိတ်ဆို့လိုက်ပါမည်၊ သို့သော် ၎င်းတို့သည် သင့်ကို အသံမေးလ်ချန်ခဲ့နိုင်ပါသည်။"</string>
- <string name="block_list" msgid="7760188925338078011">"ပိတ်ထားသည့် နံပါတ်များ"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> သည်တရားမဝင်ပါ။"</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> အားပိတ်ဆို့ထားပြီးပါပြီ။"</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"ခေါ်ဆိုမှုပိတ်ဆို့ခြင်းကို ၄၈ နာရီပိတ်ထားသည်"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"အရေးပေါ်ခေါ်ဆိုမှု ပြုလုပ်ခဲ့သည့်အတွက် ပိတ်ထားပါသည်။"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"ခေါ်ဆိုသော အကောင့်များ"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"ဖွင့်ထားရန်"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"ခွင့်ပြုချက်များ သတ်မှတ်မည်"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"မြန်နှုန်းမြင့်ဖုန်းခေါ်ဆိုမှုကို ဖွင့်ရန်၊ အဆက်အသွယ်ခွင့်ပြုချက်ကို ဖွင့်ပါ။"</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"ခေါ်ဆိုမှုမှတ်တမ်းကို ကြည့်ရန်၊ ဖုန်းခွင့်ပြုချက်ကို ဖွင့်ပါ။"</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"အဆက်အသွယ်များကိုကြည့်ရန်၊ အဆက်အသွယ်ခွင့်ပြုချက်ကို ဖွင့်ပါ။"</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"အသံမေးလ်ကိုအသုံးပြုရန်၊ ဖုန်းခွင့်ပြုချက်ကိုဖွင့်ပါ။"</string>
- <string name="permission_no_search" msgid="84152933267902056">"သင့်အဆက်အသွယ်များကို ရှာဖွေရန်၊ အဆက်အသွယ်ခွင့်ပြုချက်များကို ဖွင့်ပါ။"</string>
- <string name="permission_place_call" msgid="2101287782615887547">"ဖုန်းခေါ်ဆိုရန်၊ ဖုန်းခွင့်ပြုချက်ကိုဖွင့်ပါ။"</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"ဖုန်း အက်ပ်ဆီတွင် စနစ် ဆက်တင်များသို့ ရေးသားခွင့် မရှိပါ။"</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"ပိတ်ဆို့ထားသည်"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ရှိနေသည်"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"စပမ်းကို ပိတ်ဆို့ပါ/သတင်းပို့ပါ"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"ပိတ်ဆို့ပါ"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"စပမ်း မဟုတ်ပါ"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"ပိတ်ဆို့မှုကို ပြန်ဖွင့်ပါ"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"စပမ်း"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> ကို ပိတ်ဆို့မည်လား။"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"ဤနံပါတ်မှ နောင်ဝင်လာမည့်ဖုန်းခေါ်ဆိုမှုများနှင့် အသံမေးလ်များကို ပိတ်ဆို့ပါလိမ့်မည်။"</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"ခေါ်ဆိုမှုကို စပမ်းအဖြစ် သတင်းပို့ပါ"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"ဤနံပါတ်မှ နောင်ဝင်လာမည့်ဖုန်းခေါ်ဆိုမှုများနှင့် အသံမေးလ်များကို ပိတ်ဆို့ပါလိမ့်မည်။ ၎င်းခေါ်ဆိုမှုကို စပမ်းအဖြစ် သတင်းပို့ပါမည်။"</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> ကို မပိတ်ဆို့တော့ပါဘူးလား။"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"ဤနံပါတ်ကို ပိတ်ဆို့တော့မည်မဟုတ်ဘဲ စပမ်းမဟုတ်ကြောင်း သတင်းပို့ပါမည်။ နောင်ဝင်လာမည့်ဖုန်းခေါ်ဆိုမှုများနှင့် အသံမေးလ်များကို စပမ်းအဖြစ် သတ်မှတ်လိမ့်မည်မဟုတ်ပါ။"</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"ခွင့်ပြုသည့်စာရင်းဝင် <xliff:g id="NUMBER">%1$s</xliff:g> ဟုတ်ပါသလား။"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"ခွင့်ပြုသည့်စာရင်း"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"ဤနံပါတ်မှ နောင်ဝင်လာမည့်ဖုန်းခေါ်ဆိုမှုများနှင့် အသံမေးလ်များကို စပမ်းအဖြစ် သတ်မှတ်လိမ့်မည်မဟုတ်ပါ။ ဤနံပါတ်ကို စပမ်းမဟုတ်ဟု သတင်းပို့ပါလိမ့်မည်။"</string>
-</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
deleted file mode 100644
index a948810be..000000000
--- a/res/values-nb/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Telefontastatur"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Anropslogg"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Rapportér feil nummer"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopiér nummeret"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopiér transkripsjonen"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blokkér nummeret"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> er blokkert"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Opphev blokkeringen av nummeret"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Blokkeringen av <xliff:g id="NUMBER">%1$s</xliff:g> er opphevet"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ANGRE"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Slett"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Endre nummeret før du ringer"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Slett anropsloggen"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Slett talepost"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arkivér taleposten"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Del talepost"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Taleposten er slettet"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Taleposten ble arkivert"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ANGRE"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"GÅ TIL ARKIV"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Vil du slette anropsloggen?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Dette sletter alle anrop fra loggen"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Sletter anropsloggen …"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Ring"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Tapt anrop"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Tapt jobbanrop"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Tapte anrop"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> tapte anrop"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Ring tilbake"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Melding"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"><xliff:g id="COUNT">%1$d</xliff:g> talemeldinger </item>
- <item quantity="one">talemelding</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Spill av"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nye talemeldinger fra <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Kunne ikke spille av talepost"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Laster inn talepost …"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Arkiverer taleposten …"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Kunne ikke laste inn taleposten"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Bare anrop som gikk til talepostkasse"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Bare innkommende anrop"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Bare utgående anrop"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Bare ubesvarte anrop"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Visuell talemelding"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Se og lytt til talemeldingene dine, uten å måtte ringe et nummer. Dette kan føre til datakostnader."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Innstillinger"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Talepostoppdateringer er ikke tilgjengelige"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Ny talemelding venter. Kan ikke laste inn akkurat nå."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Konfigurer taleposten din"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Lyd er ikke tilgjengelig"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Konfigurer"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Ring talepostkasse"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Velg nummer"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Velg nummer"</string>
- <string name="make_primary" msgid="5829291915305113983">"Husk dette valget"</string>
- <string name="description_search_button" msgid="3660807558587384889">"søk"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"ring"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"ring til"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Spill av eller stopp avspillingen"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Slå høyttaleren på eller av"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Søk etter avspillingsposisjon"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Reduser avspillingshastigheten"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Øk avspillingshastigheten"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Anropslogg"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Flere alternativer"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"tastatur"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Vis bare utgående"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Vis bare innkommende"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Vis bare tapte"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Vis bare talemeldinger"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Vis alle samtaler"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Legg til pause på 2 sek."</string>
- <string name="add_wait" msgid="3360818652790319634">"Legg til Vent"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Innstillinger"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Ny kontakt"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Alle kontakter"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Samtaleinformasjon"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Informasjon er ikke tilgjengelig"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Bruk tonetastatur"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Gå tilbake til aktiv samtale"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Legg til en samtale"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Innkommende samtale"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Utgående samtale"</string>
- <string name="type_missed" msgid="2720502601640509542">"Tapt anrop"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Innkommende videoanrop"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Utgående videoanrop"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Ubesvart videoanrop"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Talepostkasse"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Avvist anrop"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blokkert anrop"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Innkommende samtaler"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Spill av talemelding"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Se kontakten <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Ring til <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Kontaktinformasjon for <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> samtaler."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videoanrop."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Send SMS til <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Ikke avspilt talepost"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Start talesøk"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Ring <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Ukjent"</string>
- <string name="voicemail" msgid="3851469869202611441">"Telefonsvarer"</string>
- <string name="private_num" msgid="6374339738119166953">"Skjult nummer"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefonkiosk"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> sek"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> sek"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> kl. <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Kan ikke ringe dette nummeret"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Du konfigurerer talepost ved å gå til Meny &amp;gt Innstillinger"</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Du må slå av flymodus før du kan sjekke talepostkassen."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Laster inn …"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Henter fra SIM-kort…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"Kontakter på SIM-kort"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Ingen kontaktapper er tilgjengelige"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Talesøk er ikke tilgjengelig"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Kan ikke ringe fordi Telefon-appen er deaktivert."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Det finnes ingen apper for det på denne enheten"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Søk i kontakter"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Legg til nummer eller søk etter kontakter"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Anropsloggen din er tom"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Start en samtale"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Du har ingen tapte anrop."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Talepostkassen din er tom."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Talepostarkivet ditt er tomt."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Vis bare favoritter"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Anropslogg"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Talepostarkiv"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Alle"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Ubesvarte"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Talemeldinger"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Ny, forenklet blokkering"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Telefon-appen endrer innstillingene for blokkering for at du skal være bedre beskyttet. Du mottar verken meldinger eller anrop fra blokkerte numre, og det kan hende at disse numrene blir delt med andre apper."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Tillat"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Vil du blokkere <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Anrop fra dette nummeret blokkeres, og talepost blir automatisk slettet."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Anrop fra dette nummeret blokkeres, men den som ringer kan fortsatt legge igjen beskjeder i talepostkassen din."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Du kommer ikke lenger til å motta anrop eller SMS-er fra dette nummeret."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOKKÉR"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Vil du oppheve blokkeringen av <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"OPPHEV BLOKKERINGEN"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Hurtigvalg"</string>
- <string name="tab_history" msgid="2563144697322434940">"Anropslogg"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontakter"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Talepostkasse"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Fjernet fra favoritter"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Angre"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Ring <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Opprett ny kontakt"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Legg til for en kontakt"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Send SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Start en videosamtale"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blokkér nummeret"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> nye tapte anrop"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Du har ingen på hurtigoppringning ennå"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Legg til en favoritt"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Du har ingen kontakter ennå"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Legg til en kontakt"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Trykk på bildet for å se alle numre, eller trykk på og hold nede for å omorganisere"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Fjern"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videoanrop"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Send en melding"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Samtaleinformasjon"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Ring <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Ubesvart anrop fra <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Besvart anrop fra <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Ulest talepost fra <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Talepost fra <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Ring til <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"på <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"via <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"via <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"på <xliff:g id="PHONEACCOUNT">%1$s</xliff:g> via <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> via <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Ring"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Ring <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Start videoanrop med <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Lytt til talepostkasse fra <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Spill av talemelding fra <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Sett talemelding fra <xliff:g id="NAMEORNUMBER">^1</xliff:g> på pause"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Slett talemelding fra <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> nye taleposter</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> ny talepost</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Opprett kontakt for <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Legg <xliff:g id="NAMEORNUMBER">^1</xliff:g> til en eksisterende kontakt"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Samtaledetaljer for <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Slettet fra anropsloggen"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"I dag"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"I går"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Eldre"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Anropsliste"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Slå på høyttaleren."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Slå av høyttaleren."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Spill av raskere."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Spill av saktere."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Start eller stopp avspillingen."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Visningsalternativer"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Lyder og vibrasjon"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Tilgjengelighet"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Telefonringelyd"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibrer også når det ringer"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tastetoner"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Tonelengde for tastaturet"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"Lang"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Hurtigsvar"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Anrop"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Anropsblokkering"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Blokkering av anrop er midlertidig slått av"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Blokkering av anrop er slått av fordi du kontaktet nødtjenester fra denne telefonen i løpet av de siste 48 timene. Funksjonen blir automatisk slått på igjen når perioden på 48 timer er utløpt."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importér numre"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Du markerte tidligere noen anropere for automatisk overføring til talepost via andre apper."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Se numre"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importér"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Importeringen mislyktes"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Kunne ikke arkivere taleposten."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Opphev blokkeringen av nummeret"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Legg til nummer"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Anrop fra disse numrene blokkeres, og talepost blir automatisk slettet."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Anrop fra dette nummeret blokkeres, men du kan fortsatt motta talepost."</string>
- <string name="block_list" msgid="7760188925338078011">"Blokkerte telefonnumre"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> er ugyldig."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> er allerede blokkert."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Anropsblokkering er slått av i 48 timer"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Slått av fordi du foretok et nødanrop."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Ringekontoer"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Slå på"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Angi tillatelser"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"For å slå på hurtigoppringning, slå på Kontakter-tillatelsen."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"For å se samtaleloggen din, slå på Telefon-tillatelsen."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"For å se kontaktene dine, slå på Kontakter-tillatelsen."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"For å få tilgang til talepostkassen, slå på Telefon-tillatelsen."</string>
- <string name="permission_no_search" msgid="84152933267902056">"For å søke i kontaktene dine, slå på tillatelser for Kontakter."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"For å ringe, slå på Telefon-tillatelsen."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Telefonappen har ikke tillatelse til å skrive til systeminnstillinger."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Blokkert"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> er aktiv"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blokkér/rapportér som useriøs"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blokkér"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Ikke useriøs"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Opphev blokkeringen"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Useriøs"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Vil du blokkere <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Fremtidige anrop og talepostmeldinger fra dette nummeret blir blokkert."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Rapportér anropet som useriøst"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Fremtidige anrop og talepost fra dette nummeret blir blokkert. Anropet blir rapportert som useriøst."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Vil du oppheve blokkeringen av <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Blokkering oppheves, og nummeret rapporteres som «ikke useriøst». Senere anrop/talepost angis ikke som useriøse."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Vil du godkjenne <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Godkjenn"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Fremtidige anrop/talepost fra dette nummeret merkes ikke som useriøse. Nummeret rapporteres som «ikke useriøst»."</string>
-</resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
deleted file mode 100644
index 095eeac29..000000000
--- a/res/values-ne/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"फोन"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"फोन"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"फोन डायलप्याड"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"फोन"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"कल इतिहास"</string>
- <string name="action_report_number" msgid="4635403959812186162">"गलत नम्बर रिपोर्ट गर्नुहोस्"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"नम्बर प्रतिलिपि गर्नुहोस्"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"ट्रान्सक्रिप्शनको प्रतिलिपि बनाउँनुहोस्"</string>
- <string name="action_block_number" msgid="1482657602262262134">"नम्बरलाई रोक्नुहोस्"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> रोकियो"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"नम्बर माथिको रोक हटाउनुहोस्"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> रोकियो"</string>
- <string name="block_number_undo" msgid="591338370336724156">"अनडू गर्नुहोस्"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"मेटाउनुहोस्"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"कल गर्न अगाडि नम्बर सम्पादन गर्नुहोस्"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"कल इतिहास मेटाउनुहोस्"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"भ्वाइसमेल हटाउनुहोस्"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"भ्वाइस मेलको अभिलेख राख्नुहोस्"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"भ्वाइस मेल साझेदारी गर्नुहोस्"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"भ्वाइस मेल मेटाइयो"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"भ्वाइस मेलको अभिलेख राखियो"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"अनडू गर्नुहोस्"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"अभिलेखमा जानुहोस्"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"कल इतिहास मेटाउने हो?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"यस कार्यले तपाईँको इतिहासबाट सबै कल मेट्नेछ"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"कल इतिहास हाटउँदै..."</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"फोन"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"छुटेको कल"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"कामबाट आएको छुटेको कल"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"छुटेका कलहरू"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> छुटेका कलहरू"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"कल फर्काउने"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"सन्देश"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> भ्वाइसमेलहरू </item>
- <item quantity="one">भ्वाइसमेल</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"बजाउनुहोस्"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g> , <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> बाट नयाँ भ्वाइसमेल"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"भ्वाइस मेललाई बजाउन सकिएन"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"भ्वाइस मेल लोड हुँदै ..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"भ्वाइस मेलको अभिलेख राख्दै..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"भ्वाइस मेललाई लोड गर्न सकिएन"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"भ्वाइसमेलका साथ मात्र कल गर्नुहोस्"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"आगमन कलहरू मात्र"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"बहिर्गमन कलहरू मात्र"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"छुटेका कलहरू मात्र"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"भिजुअल भ्वाइस मेल"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"नम्बरमा कल गर्न नपरी तपाईँको भ्वाइसमेल हेर्नुहोस् र सुन्नुहोस्। डेटा शुल्क लाग्न सक्छ।"</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"सेटिङहरू"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"भ्वाइस मेल अद्यावधिक उपलब्ध छैन"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"नयाँ भ्वाइस मेल पर्खदै। अहिले नै लोड गर्न सक्दैन।"</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"तपाईँको भ्वाइसमेल सेटअप गर्नुहोस्"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"अडियो उपलब्ध छैन"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"मिलाउनुहोस्"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"भ्वाइसमेल कल गर्नुहोस्"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"( <xliff:g id="COUNT">%1$d</xliff:g> ) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"नम्बर रोज्नुहोस्"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"नम्बर छान्नुहोस्"</string>
- <string name="make_primary" msgid="5829291915305113983">"यो छनौट याद राख्नुहोस्"</string>
- <string name="description_search_button" msgid="3660807558587384889">"खोजी गर्नुहोस्"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"डायल"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"डायल गर्न संख्या"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"प्लेब्याक चलाउनुहोस वा रोक्नुहोस्"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"स्पीकरफोन खोल्नुहोस् वा बन्द गर्नुहोस्"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"प्लेब्याक स्थिति खोज्नुहोस्"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"प्लेब्याक दर कम गर्नुहोस्"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"प्लेब्याक दर बढाउनुहोस्"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"कल इतिहास"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"थप विकल्पहरू"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"डायल प्याड"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"बहिर्गमन मात्र देखाउनुहोस्"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"आगमन कल मात्र देखाउनुहोस्"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"छुटेकाहरू मात्र देखाउनुहोस्"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"भ्वाइसमेलहरू मात्र देखाउनुहोस्"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"सबै कलहरू देखाउनुहोस्"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"२ सेकन्डको रोकाइ थप्नुहोस्"</string>
- <string name="add_wait" msgid="3360818652790319634">"पर्खाइ थप्नुहोस्"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"सेटिङ्हरू"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"नयाँ सम्पर्क"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"सबै सम्पर्कहरू"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"कल विवरण"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"विवरण उपलब्ध छैन"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"स्पर्श टोन किप्याडको प्रयोग गर्नुहोस्"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"हुदै गरेको कलमा फर्कनुहोस्"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"कल थप्नुहोस्"</string>
- <string name="type_incoming" msgid="6502076603836088532">"आगमन कल"</string>
- <string name="type_outgoing" msgid="343108709599392641">"बहिर्गमन कल"</string>
- <string name="type_missed" msgid="2720502601640509542">"छुटेको कल"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"आगमन भिडियो कल"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"बहिर्गमन भिडियो कल"</string>
- <string name="type_missed_video" msgid="954396897034220545">"छुटेको भिडियो कल"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"भ्वाइसमेल"</string>
- <string name="type_rejected" msgid="7783201828312472691">"अस्वीकृत कल"</string>
- <string name="type_blocked" msgid="3521686227115330015">"अवरुद्ध कल"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"आगमन कलहरू"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"भ्वाइसमेल बजाउनुहोस्"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> सम्पर्क हेर्नुहोस्"</string>
- <string name="description_call" msgid="3443678121983852666">"कल <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> को लागि सम्पर्क विवरणहरू"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> कल।"</string>
- <string name="description_video_call" msgid="2933838090743214204">"भिडियो कल।"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g>लाई SMS पठाउनुहोस्"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"नसुनिएका भ्वाइसमेल"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"आवाज खोजी सुरु गर्नुहोस्"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> कल गर्नुहोस्"</string>
- <string name="unknown" msgid="740067747858270469">"अज्ञात"</string>
- <string name="voicemail" msgid="3851469869202611441">"भ्वाइसमेल"</string>
- <string name="private_num" msgid="6374339738119166953">"नीजि नम्बर"</string>
- <string name="payphone" msgid="7726415831153618726">"पेफोन"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> सेकेन्ड"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> मिनेट <xliff:g id="SECONDS">%s</xliff:g> सकेन्ड"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> मा <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"यस नम्बरलाई कल गर्न सकिंदैन"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"भ्वाइसमेल सेट गर्नका लागि मेनु सेटिङमा जानुहोस्।"</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"भ्वाइसमेल कल गर्नका लागि पहिले हवाइजहाज मोड बन्द गर्नुहोस्।"</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"लोड हुँदै..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"SIM कार्डबाट लोड हुँदै"</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM कार्ड सम्पर्कहरू"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"कुनै पनि सम्पर्क अनुप्रयोग उपलब्ध छैन"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"आवाज खोजी उपलब्ध छैन"</string>
- <string name="call_not_available" msgid="8941576511946492225">"फोन अनुप्रयोग असक्षम गरिएकोले फोन गर्न सकिँदैन।"</string>
- <string name="activity_not_available" msgid="2287665636817987623">"यस यन्त्रमा त्यसका लागि कुनै पनि अनुप्रयोग छैन"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"सम्पर्क ठेगानाहरू खोज्नुहोस्"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"नम्बर थप्नुहोस् वा सम्पर्कहरू खोज्नुहोस्"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"तपाईँको कल इतिहास खाली छ"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"कल गर्नुहोस्"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"तपाईँसँग कुनै पनि छुटेका कलहरू छैनन्।"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"तपाईँको भ्वाइस मेल खाली छ।"</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"तपाईंको भ्वाइस मेलको अभिलेख खाली छ।"</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"मनपर्ने मात्र देखाउनुहोस्"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"कल इतिहास"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"भ्वाइस मेलको अभिलेख"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"सबै"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"छुटेको"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Voicemail"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"नयाँ, सरलीकृत रोकावट"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"तपाईँको थप सुरक्षाका लागि फोनको रोकावट गर्ने विधिमा परिवर्तन गर्न आवश्यक छ। तपाईँद्वारा रोक लगाइएका नम्बरहरूले अब कल र पाठ सन्देशहरूलाई रोक्नेछन् र यी नम्बरहरूलाई अन्य अनुप्रयोगहरूसँग साझेदारी गरिन सक्छ।"</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"अनुमति दिनुहोस्"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> माथि रोक लगाउने हो?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"यो नम्बरबाट सबै कलहरू रोकिनेछन् र भ्वाइसमेलहरू स्वचालित रूपमा मेटिनेछन्।"</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"यो नम्बरबाट सबै कलहरू रोकिनेछन्। तर कलरले अझै तपाईँलाई भ्वाइस मेलहरू भने छोड्न सक्नेछन्।"</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"तपाईँले अब उप्रान्त यस नम्बरबाट कल वा पाठ सन्देशहरू प्राप्त गर्नुहुने छैन।"</string>
- <string name="block_number_ok" msgid="770551992296781873">"रोक्नुहोस्"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> माथिको रोक हटाउने हो?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"रोक हटाउनुहोस्"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"द्रूत डायल"</string>
- <string name="tab_history" msgid="2563144697322434940">"कल इतिहास"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"सम्पर्कहरू"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"भ्वाइसमेल"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"मनपर्नेहरूबाट हटाइयो"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"अनडु गर्नुहोस्"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> कल गर्नुहोस्"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"नयाँ सम्पर्क सिर्जना गर्नुहोस्"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"सम्पर्कमा थप्नुहोस्"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS पठाउनुहोस्"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"भिडियो कल बनाउनुहोस्"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"नम्बरलाई निषेध गर्नुहोस्"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> नयाँ छुटेका कलहरु"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"तपाईँको स्पिड डायलमा अझसम्म कोही छैन"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"एक मनपर्ने थप्नुहोस्"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"तपाईँसँग अझसम्म कुनै सम्पर्कहरू छैनन्"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"एउटा सम्पर्क थप्नुहोस्"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"सबै संख्या हेर्न छविलाई छुनुहोस् वा पुन:क्रमबद्ध गर्न समात्नुहोस्"</string>
- <string name="remove_contact" msgid="1080555335283662961">"हटाउँनुहोस्"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"भिडियो कल"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"सन्देश पठाउनुहोस्"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"कल विवरणहरु"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> कल गर्नुहोस्"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> बाट मिस्ड कल।"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> बाट कलको जवाफ दिइयो।"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> बाट पढ्न बाँकी भ्वाइस मेल।"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> बाट भ्वाइस मेल।"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> लाई कल गर्नुहोस्।"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> मा"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> मार्फत"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> मार्फत"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> मा, <xliff:g id="NUMBER">%2$s</xliff:g> मार्फत"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="NUMBER">%2$s</xliff:g> मार्फत <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"कल गर्नुहोस्"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> कल गर्नुहोस्"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> भिडियो कल गर्नुहोस्।"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> बाट भ्वाइसमेल सुन्नुहोस्"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> बाट भ्वाइस मेल बजाउनुहोस्"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> बाट भ्वाइस मेल रोक्नुहोस्"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> बाट भ्वाइस मेल मेटाउनुहोस्"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> नयाँ भ्वाइस मेलहरू</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> नयाँ भ्वाइस मेल</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> का लागि नयाँ सम्पर्क बनाउँनुहोस्"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"विद्यमान सम्पर्कमा <xliff:g id="NAMEORNUMBER">^1</xliff:g> थप्नुहोस्"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> का लागि कल विवरणहरू"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"कल इतिहासबाट मेटाइयो"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"आज"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"हिजो"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"अझ पुरानो"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"कल सूची"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"स्पिकर खोल्नुहोस्।"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"स्पिकर बन्द गर्नुहोस्।"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"छिटो चलाउनुहोस्।"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"ढिलो चलाउनुहोस्।"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"दोहर्याउने सुरु गर्नुहोस् वा रोक्नुहोस्।"</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"विकल्पहरू प्रदर्शन गर्नुहोस्"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"ध्वनि र कम्पन"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"पहुँचता"</string>
- <string name="ringtone_title" msgid="760362035635084653">"फोन रिङटोन"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"कलका लागि कम्पन पनि गर्नुहोस्"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"डायलप्याडका टोनहरू"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"डायलप्याड टोन लम्बाइ"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"सामान्य"</item>
- <item msgid="6177579030803486015">"लामो"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"द्रुत प्रतिक्रियाहरू"</string>
- <string name="call_settings_label" msgid="313434211353070209">"कलहरू"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"कल रोक्दै"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"कल अवरुद्ध अस्थायी रुपमा निष्क्रिय"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"कल अवरुद्ध अस्थायी रुपमा असक्षम गरिएको छ किनभने तपाईँले अन्तिम ४८ घण्टा भित्र यस फोनबाट आपत्कालीन सेवाहरू सम्पर्क गर्नुभयो। एकपटक ४८ घण्टा अवधि समाप्त भएपछि यो स्वचालित रूपले पुनः सक्रिय हुनेछ।"</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"नम्बरहरू आयात गर्नुहोस्"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"तपाईँले पहिल्यै केही कल गर्ने व्यक्तिहरूलाई अन्य अनुप्रयोगहरू मार्फत स्वत: रूपमा भ्वाइस मेल पठाउन नै चिन्ह लगाउनु भएको थियो।"</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"नम्बरहरू हेर्नुहोस्"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"आयात गर्नुहोस्"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"आयात असफल भयो"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"भ्वाइस मेलको अभिलेख राख्न सकिएन।"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"नम्बरमाथिको रोक हटाउनुहोस्"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"नम्बर थप्नुहोस्"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"यी नम्बरहरूबाट सबै कलहरू रोकिनेछन् र भ्वाइसमेलहरू स्वचालित रूपमा मेटिनेछन्।"</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"यो नम्बरहरूबाट सबै कलहरू रोकिनेछन्। तर तिनिहरूले अझै तपाईँलाई भ्वाइस मेलहरू भने छोड्न सक्नेछन्।"</string>
- <string name="block_list" msgid="7760188925338078011">"रोक लगाइएका नम्बरहरू"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> अमान्य छ।"</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> पहिले नै रोकिएको छ।"</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"४८ घण्टाको लागि कल निषेध असक्षम गरियो"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"आपत्कालीन कल गरिएको हुनाले असक्षम गरियो।"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"खाता कलिङ"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"सक्रिय पार्नुहोस्"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"अनुमतिहरू सेट गर्नुहोस्"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"तीव्र डायल सक्षम गर्न, सम्पर्क अनुमति सक्षम गर्नुहोस्।"</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"आफ्नो कल लग हेर्न, फोन अनुमति सक्रिय गर्नुहोस्।"</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"आफ्नो सम्पर्क हेर्न सम्पर्क अनुमति सक्रिय गर्नुहोस्।"</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"आफ्नो भ्वाइस मेल पहुँच गर्न, फोन अनुमति सक्रिय गर्नुहोस्"</string>
- <string name="permission_no_search" msgid="84152933267902056">"तपाईँको सम्पर्कहरू खोज गर्न सम्पर्क अनुमतिहरू सक्रिय गर्नुहोस्।"</string>
- <string name="permission_place_call" msgid="2101287782615887547">"कल गर्न फोन अनुमति सक्रिय गर्नुहोस्।"</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"प्रणाली सेटिङहरूमा लेख्न फोन अनुप्रयोगसँग अनुमति छैन।"</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"रोकियो"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> सक्रिय छ"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"स्प्यामलाई रोक्नुहोस्/रिपोर्ट गर्नुहोस्"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"रोक्नुहोस्"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"स्प्याम होइन"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"रोक हटाउनुहोस्"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"स्प्याम"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> माथि रोक लगाउने हो?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"यस नम्बरबाट आउने आगामी कल र भ्वाइस मेलहरूलाई रोक लगाइनेछ।"</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"कललाई स्प्याम भनी रिपोर्ट गर्नुहोस्"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"यस नम्बरबाट आउने आगामी कल र भ्वाइस मेलहरूलाई रोक लगाइनेछ। यस कललाई स्प्याम भनी रिपोर्ट गरिनेछ।"</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> माथिको रोक हटाउने हो?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"यो नम्बरमाथिको रोक हटाइनेछ र स्प्याम होइन भनी रिपोर्ट गरिनेछ। आगामी कल र भ्वाइस मेलहरूलाई स्प्यामका रूपमा पहिचान गरिने छैन।"</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"श्वेतसूची <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"श्वेतसूची"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"यस नम्बरबाट आउने आगामी कल र भ्वाइस मेलहरूलाई स्प्याम भनी पहिचान गरिने छैन। यो नम्बर स्प्याम होइन भनी रिपोर्ट गरिनेछ।"</string>
-</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
deleted file mode 100644
index b1c37a6da..000000000
--- a/res/values-nl/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefoon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefoon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Telefoonkiezer"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefoon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Oproepgeschiedenis"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Onjuist nummer melden"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Nummer kopiëren"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Transcriptie kopiëren"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Nummer blokkeren"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> geblokkeerd"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Blokkeren van nummer opheffen"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Blokkering van <xliff:g id="NUMBER">%1$s</xliff:g> opgeheven"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ONGEDAAN MAKEN"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Verwijderen"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Nummer bewerken vóór bellen"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Oproepgeschiedenis wissen"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Voicemail verwijderen"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Voicemail archiveren"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Voicemail delen"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Voicemail verwijderd"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Voicemail gearchiveerd"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ONGED. MKN"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"NAAR ARCHIEF"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Oproepgeschiedenis wissen?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Hiermee worden alle oproepen uit je geschiedenis verwijderd"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Oproepgeschiedenis wissen…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefoon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Gemiste oproep"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Gemiste zakelijke oproep"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Gemiste oproepen"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> gemiste oproepen"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Terugbellen"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Bericht"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> voicemails </item>
- <item quantity="one">Voicemail</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Afspelen"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nieuwe voicemail van <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Kan voicemail niet afspelen"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Voicemail laden…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Voicemail archiveren…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Kan voicemail niet laden"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Alleen oproepen met voicemail"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Alleen inkomende oproepen"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Alleen uitgaande oproepen"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Alleen gemiste oproepen"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Visuele voicemail"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Bekijk en beluister je voicemail, zonder dat je een nummer hoeft te bellen. Er kunnen kosten voor gegevensgebruik in rekening worden gebracht."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Instellingen"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Voicemailupdates niet beschikbaar"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Je hebt nieuwe voicemail. Kan nu niet laden."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Je voicemail instellen"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio niet beschikbaar"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Instellen"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Bellen met voicemail"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Nummer kiezen"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Nummer kiezen"</string>
- <string name="make_primary" msgid="5829291915305113983">"Deze keuze onthouden"</string>
- <string name="description_search_button" msgid="3660807558587384889">"zoeken"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"bellen"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"nummer om te bellen"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Afspelen starten of stoppen"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Luidsprekertelefoon in- of uitschakelen"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Afspeelpositie zoeken"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Afspeelsnelheid verlagen"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Afspeelsnelheid verhogen"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Oproepgeschiedenis"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Meer opties"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"toetsenblok"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Alleen uitgaand weergeven"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Alleen inkomend weergeven"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Alleen gemist weergeven"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Alleen voicemails weergeven"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Alle oproepen weergeven"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Pauze van 2 seconden toevoegen"</string>
- <string name="add_wait" msgid="3360818652790319634">"Wachten toevoegen"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Instellingen"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Nieuw contact"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Alle contacten"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Gespreksgegevens"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Details niet beschikbaar"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Toetsen voor toonkiezen gebruiken"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Terug naar actief gesprek"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Oproep toevoegen"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Inkomende oproep"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Uitgaand gesprek"</string>
- <string name="type_missed" msgid="2720502601640509542">"Oproep gemist"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Binnenkomend videogesprek"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Uitgaand videogesprek"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Gemist videogesprek"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Voicemail"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Geweigerde oproep"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Geblokkeerde oproep"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Inkomende oproepen"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Voicemail afspelen"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Contact <xliff:g id="NAME">%1$s</xliff:g> bekijken"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g> bellen"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Contactgegevens voor <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> telefoongesprekken."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videogesprek."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Sms verzenden naar <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Niet-beluisterde voicemail"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Gesproken zoekopdracht"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> bellen"</string>
- <string name="unknown" msgid="740067747858270469">"Onbekend"</string>
- <string name="voicemail" msgid="3851469869202611441">"Voicemail"</string>
- <string name="private_num" msgid="6374339738119166953">"Privénummer"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefooncel"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> sec."</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min. <xliff:g id="SECONDS">%s</xliff:g> sec."</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> om <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Kan dit nummer niet bellen"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Voor het instellen van voicemail, ga je naar \'Menu\' &gt; \'Instellingen\'."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Als je je voicemail wilt bellen, moet je eerst de Vliegtuigmodus uitschakelen."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Laden..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI-nummer"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Laden vanaf SIM-kaart..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Contacten op SIM-kaart"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Geen app voor contacten beschikbaar"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Gesproken zoekopdracht niet beschikbaar"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Kan niet bellen omdat de Telefoon-app is uitgeschakeld."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Daarvoor is geen app beschikbaar op dit apparaat"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Contacten zoeken"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Voeg nummer toe of zoek contacten"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Je oproepgeschiedenis is leeg"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Bellen"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Je hebt geen gemiste oproepen."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Je voicemail-inbox is leeg."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Je voicemailarchief is leeg."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Alleen favorieten weergeven"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Oproepgeschiedenis"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Voicemailarchief"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Alle"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Gemist"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Voicemail"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Verbeterde blokkeerfunctie"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Telefoon moet het blokkeergedrag aanpassen om je beter te beschermen. Voor je geblokkeerde nummers worden nu zowel oproepen als sms\'jes geblokkeerd. Deze instelling kan worden gedeeld met andere apps."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Toestaan"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> blokkeren?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Oproepen van dit nummer worden geblokkeerd en voicemails worden automatisch verwijderd."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Oproepen van dit nummer worden geblokkeerd, maar de beller kan nog wel voicemails achterlaten."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Je ontvangt geen oproepen of sms\'jes meer van dit nummer."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOKKEREN"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Blokkering van <xliff:g id="NUMBER">%1$s</xliff:g> opheffen?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"BLOKKERING OPHEFFEN"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Snelkeuze"</string>
- <string name="tab_history" msgid="2563144697322434940">"Oproepgeschiedenis"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Contacten"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Voicemail"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Verwijderd uit favorieten"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Ongedaan maken"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> bellen"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Nieuw contact maken"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Toevoegen aan een contact"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Sms verzenden"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Videogesprek starten"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Nummer blokkeren"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> nieuwe gemiste oproepen"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Er staat nog niemand in je snelkeuzelijst"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Een favoriet toevoegen"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Je hebt nog geen contacten"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Contact toevoegen"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Tik op de afbeelding om alle nummers te bekijken of blijf de afbeelding aanraken om opnieuw te rangschikken"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Verwijderen"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videogesprek"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Een bericht verzenden"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Oproepdetails"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> bellen"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Oproep gemist van <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Oproep beantwoord van <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Ongelezen voicemail van <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Voicemail van <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Oproep naar <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"in <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"via <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"via <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"op <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, via <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> via <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Bellen"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> bellen"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Videogesprek <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Voicemail beluisteren van <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Voicemail van <xliff:g id="NAMEORNUMBER">^1</xliff:g> afspelen"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Voicemail van <xliff:g id="NAMEORNUMBER">^1</xliff:g> onderbreken"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Voicemail van <xliff:g id="NAMEORNUMBER">^1</xliff:g> verwijderen"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> nieuwe voicemails</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> nieuwe voicemail</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Contact maken voor <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> toevoegen aan bestaand contact"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Oproepgegevens voor <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Verwijderd uit oproepgeschiedenis"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Vandaag"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Gisteren"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Ouder"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Lijst met oproepen"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Luidspreker inschakelen."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Luidspreker uitschakelen."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Sneller afspelen."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Langzamer afspelen."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Afspelen starten of onderbreken."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Weergaveopties"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Geluiden en trillingen"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Toegankelijkheid"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Beltoon telefoon"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Ook trillen voor oproepen"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tonen bij toetsaanslag"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Toonduur toetsenblok"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normaal"</item>
- <item msgid="6177579030803486015">"Lang"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Snelle reacties"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Oproepen"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Oproepen blokkeren"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Oproepblokkering tijdelijk uitgeschakeld"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Oproepblokkering is uitgeschakeld omdat je in de afgelopen 48 uur contact met de noodhulpdiensten hebt opgenomen via deze telefoon. De functie wordt automatisch weer ingeschakeld nadat de periode van 48 uur is verstreken."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Nummers importeren"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Je hebt via andere apps al aangegeven dat bepaalde bellers moeten worden doorgeschakeld naar de voicemail."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Nummers bekijken"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importeren"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Importeren mislukt"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Kan voicemail niet archiveren."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Blokkeren van nummer opheffen"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Nummer toevoegen"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Oproepen van deze nummers worden geblokkeerd en voicemails worden automatisch verwijderd."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Oproepen van deze nummers worden geblokkeerd, maar de bellers kunnen nog wel voicemail achterlaten."</string>
- <string name="block_list" msgid="7760188925338078011">"Geblokkeerde nummers"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> is ongeldig."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> is al geblokkeerd."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Oproepblokkering is 48 uur uitgeschakeld"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Uitgeschakeld omdat een noodoproep is geplaatst."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Oproepaccounts"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Inschakelen"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Toestemmingen instellen"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Als je de snelkeuzefunctie wilt inschakelen, schakel je de machtiging voor Contacten in."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Als je je gesprekkenlijst wilt bekijken, schakel je de machtiging voor Telefoon in."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Als je je contacten wilt bekijken, schakel je de machtiging voor Contacten in."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Als je je voicemail wilt beluisteren, schakel je de machtiging voor Telefoon in."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Als je naar contacten wilt zoeken, schakel je de contacttoestemming in."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Als je iemand wilt bellen, schakel je de machtiging voor Telefoon in."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Telefoon-app heeft geen toestemming om systeeminstellingen te schrijven."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Geblokkeerd"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> is actief"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blokkeren/spam melden"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blokkeren"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Geen spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Blokkeren opheffen"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> blokkeren?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Toekomstige oproepen en voicemails van dit nummer worden geblokkeerd."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Oproep melden als spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Toekomstige oproepen en voicemails van dit nummer worden geblokkeerd. Oproep wordt gemeld als spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Blokkering van <xliff:g id="NUMBER">%1$s</xliff:g> opheffen?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Nummerblokkering opgeheven, gemeld als geen spam. Nieuwe oproepen/voicemails worden niet aangemerkt als spam."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> op de witte lijst zetten?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Op witte lijst plaatsen"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Nieuwe oproepen en voicemails van dit nummer worden niet aangemerkt als spam. Nummer wordt gemeld als geen spam."</string>
-</resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
deleted file mode 100644
index 4e983c458..000000000
--- a/res/values-pa/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"ਫ਼ੋਨ"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"ਫੋਨ"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"ਫੋਨ ਡਾਇਲਪੈਡ"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"ਫੋਨ"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"ਕਾਲ ਇਤਿਹਾਸ"</string>
- <string name="action_report_number" msgid="4635403959812186162">"ਗਲਤ ਨੰਬਰ ਦੀ ਰਿਪੋਰਟ ਕਰੋ"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"ਨੰਬਰ ਕਾਪੀ ਕਰੋ"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"ਲਿਪੀ ਬਦਲਣਾ ਕਾਪੀ ਕਰੋ"</string>
- <string name="action_block_number" msgid="1482657602262262134">"ਨੰਬਰ ਨੂੰ ਬਲੌਕ ਕਰੋ"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> ਬਲੌਕ ਕੀਤਾ"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"ਨੰਬਰ ਨੂੰ ਅਣਬਲੌਕ ਕਰੋ"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> ਅਨਬਲੌਕ ਕੀਤਾ"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ਪਹਿਲਾਂ ਵਰਗਾ ਕਰੋ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"ਹਟਾਓ"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"ਕਾਲ ਤੋਂ ਪਹਿਲਾਂ ਨੰਬਰ ਸੰਪਾਦਿਤ ਕਰੋ"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"ਕਾਲ ਇਤਿਹਾਸ ਹਟਾਓ"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"ਵੌਇਸਮੇਲ ਮਿਟਾਓ"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"ਵੌਇਸਮੇਲ ਪੁਰਾਲੇਖਬੱਧ ਕਰੋ"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"ਵੌਇਸਮੇਲ ਸਾਂਝੀ ਕਰੋ"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"ਵੌਇਸਮੇਲ ਮਿਟਾਈ ਗਈ"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"ਵੌਇਸਮੇਲ ਪੁਰਾਲੇਖਬੱਧ ਕੀਤੀ ਗਈ"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ਪਹਿਲਾਂ ਵਰਗਾ ਕਰੋ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ਪੁਰਾਲੇਖ \'ਤੇ ਜਾਓ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"ਕੀ ਕਾਲ ਇਤਿਹਾਸ ਹਟਾਉਣਾ ਹੈ?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"ਇਹ ਤੁਹਾਡੇ ਇਤਿਹਾਸ ਤੋਂ ਸਾਰੀਆਂ ਕਾਲਾਂ ਮਿਟਾ ਦੇਵੇਗਾ"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"ਕਾਲ ਇਤਿਹਾਸ ਹਟਾ ਰਿਹਾ ਹੈ…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"ਫੋਨ"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"ਮਿਸ ਹੋਈ ਕਾਲ"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"ਕੰਮ ਨਾਲ ਸਬੰਧਿਤ ਖੁੰਝੀ ਕਾਲ"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"ਮਿਸ ਹੋਈਆਂ ਕਾਲਾਂ"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ਮਿਸ ਹੋਈਆਂ ਕਾਲਾਂ"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"ਕਾਲ ਬੈਕ ਕਰੋ"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"ਸੁਨੇਹਾ"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> ਵੌਇਸਮੇਲਾਂ </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> ਵੌਇਸਮੇਲਾਂ </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"ਪਲੇ ਕਰੋ"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> ਦੀ ਨਵੀਂ ਵੌਇਸਮੇਲ"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"ਵੌਇਸਮੇਲ ਪਲੇ ਨਹੀਂ ਕਰ ਸਕਿਆ"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"ਵੌਇਸਮੇਲ ਲੋਡ ਕਰ ਰਿਹਾ ਹੈ…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"ਵੌਇਸਮੇਲ ਪੁਰਾਲੇਖਬੱਧ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"ਵੌਇਸਮੇਲ ਲੋਡ ਨਹੀਂ ਕਰ ਸਕਿਆ"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"ਕੇਵਲ ਵੌਇਸਮੇਲ ਵਾਲੀਆਂ ਕਾਲਾਂ"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"ਕੇਵਲ ਇਨਕਮਿੰਗ ਕਾਲਾਂ"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"ਕੇਵਲ ਆਊਟਗੋਇੰਗ ਕਾਲਾਂ"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"ਕੇਵਲ ਮਿਸਡ ਕਾਲਾਂ"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"ਵਿਜੁਅਲ ਵੌਇਸਮੇਲ"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"ਇੱਕ ਨੰਬਰ ਤੇ ਕਾਲ ਕੀਤੇ ਬਿਨਾਂ, ਆਪਣੀ ਵੌਇਸਮੇਲ ਦੇਖੋ ਅਤੇ ਸੁਣੋ। ਡਾਟਾ ਖ਼ਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ।"</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"ਸੈਟਿੰਗਾਂ"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"ਵੌਇਸਮੇਲ ਅਪਡੇਟਾਂ ਉਪਲਬਧ ਨਹੀਂ"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"ਨਵੀਂ ਵੌਇਸਮੇਲ ਉਡੀਕ ਰਹੀ ਹੈ। ਹੁਣ ਲੋਡ ਨਹੀਂ ਕਰ ਸਕਦਾ।"</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"ਆਪਣੀ ਵੌਇਸਮੇਲ ਸੈਟ ਅਪ ਕਰੋ"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"ਔਡੀਓ ਉਪਲਬਧ ਨਹੀਂ"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"ਸਥਾਪਤ ਕਰੋ"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"ਵੌਇਸਮੇਲ ਤੇ ਕਾਲ ਕਰੋ"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"ਨੰਬਰ ਚੁਣੋ"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"ਨੰਬਰ ਚੁਣੋ"</string>
- <string name="make_primary" msgid="5829291915305113983">"ਇਹ ਚੋਣ ਯਾਦ ਰੱਖੋ"</string>
- <string name="description_search_button" msgid="3660807558587384889">"ਖੋਜੋ"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"ਡਾਇਲ ਕਰੋ"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"ਡਾਇਲ ਕਰਨ ਲਈ ਨੰਬਰ"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"ਪਲੇਬੈਕ ਪਲੇ ਕਰੋ ਜਾਂ ਰੋਕੋ"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"ਸਪੀਕਰਫੋਨ ਨੂੰ ਚਾਲੂ ਜਾਂ ਬੰਦ ਕਰੋ"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"ਪਲੇਬੈਕ ਪੋਜੀਸ਼ਨ ਖੋਜੋ"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"ਪਲੇਬੈਕ ਰੇਟ ਘਟਾਓ"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"ਪਲੇਬੈਕ ਰੇਟ ਵਧਾਓ"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"ਕਾਲ ਇਤਿਹਾਸ"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"ਹੋਰ ਚੋਣਾਂ"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"ਡਾਇਲ ਪੈਡ"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"ਕੇਵਲ ਆਊਟਗੋਇੰਗ ਦਿਖਾਓ"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"ਕੇਵਲ ਇਨਕਮਿੰਗ ਦਿਖਾਓ"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"ਕੇਵਲ ਮਿਸਡ ਦਿਖਾਓ"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"ਕੇਵਲ ਵੌਇਸਮੇਲਾਂ ਦਿਖਾਓ"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"ਸਾਰੀਆਂ ਕਾਲਾਂ ਦਿਖਾਓ"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2-ਸਕਿੰਟ ਦਾ ਪੌਜ ਜੋੜੋ"</string>
- <string name="add_wait" msgid="3360818652790319634">"ਉਡੀਕ ਜੋੜੋ"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"ਸੈਟਿੰਗਾਂ"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"ਨਵਾਂ ਸੰਪਰਕ"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"ਸਾਰੇ ਸੰਪਰਕ"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"ਕਾਲ ਵੇਰਵੇ"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"ਵੇਰਵੇ ਉਪਲਬਧ ਨਹੀਂ"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"ਟਚ ਟੋਨ ਕੀਪੈਡ ਵਰਤੋ"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"ਪ੍ਰਗਤੀ ਵਿੱਚ ਕਾਲ ਤੇ ਵਾਪਸ ਜਾਓ"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"ਕਾਲ ਜੋੜੋ"</string>
- <string name="type_incoming" msgid="6502076603836088532">"ਇਨਕਮਿੰਗ ਕਾਲ"</string>
- <string name="type_outgoing" msgid="343108709599392641">"ਆਊਟਗੋਇੰਗ ਕਾਲ"</string>
- <string name="type_missed" msgid="2720502601640509542">"ਮਿਸਡ ਕਾਲ"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"ਇਨਕਮਿੰਗ ਵੀਡੀਓ ਕਾਲ"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"ਆਊਟਗੋਇੰਗ ਵੀਡੀਓ ਕਾਲ"</string>
- <string name="type_missed_video" msgid="954396897034220545">"ਮਿਸਡ ਵੀਡੀਓ ਕਾਲ"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"ਵੌਇਸਮੇਲ"</string>
- <string name="type_rejected" msgid="7783201828312472691">"ਅਸਵੀਕਾਰ ਕੀਤੀ ਕਾਲ"</string>
- <string name="type_blocked" msgid="3521686227115330015">"ਬਲੌਕ ਕੀਤੀ ਕਾਲ"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"ਇਨਕਮਿੰਗ ਕਾਲਾਂ"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"ਵੌਇਸਮੇਲ ਪਲੇ ਕਰੋ"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"ਸੰਪਰਕ <xliff:g id="NAME">%1$s</xliff:g> ਦੇਖੋ"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g> ਨੂੰ ਕਾਲ ਕਰੋ"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> ਲਈ ਸੰਪਰਕ ਵੇਰਵੇ"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> ਕਾਲਾਂ।"</string>
- <string name="description_video_call" msgid="2933838090743214204">"ਵੀਡੀਓ ਕਾਲ।"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g> ਨੂੰ SMS ਭੇਜੋ"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"ਅਣਸੁਣੀ ਵੌਇਸਮੇਲ"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"ਵੌਇਸ ਖੋਜ ਚਾਲੂ ਕਰੋ"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> ਨੂੰ ਕਾਲ ਕਰੋ"</string>
- <string name="unknown" msgid="740067747858270469">"ਅਗਿਆਤ"</string>
- <string name="voicemail" msgid="3851469869202611441">"ਵੌਇਸਮੇਲ"</string>
- <string name="private_num" msgid="6374339738119166953">"ਨਿੱਜੀ ਨੰਬਰ"</string>
- <string name="payphone" msgid="7726415831153618726">"ਪੇਫੋਨ"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> ਸਕਿੰਟ"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> ਮਿੰਟ <xliff:g id="SECONDS">%s</xliff:g> ਸਕਿੰਟ"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> ਨੂੰ <xliff:g id="TIME">%2$s</xliff:g> ਵਜੇ"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"ਇਸ ਨੰਬਰ ਤੇ ਕਾਲ ਨਹੀਂ ਕਰ ਸਕਦਾ"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"ਵੌਇਸਮੇਲ ਸੈਟ ਅਪ ਕਰਨ ਲਈ, ਮੀਨੂ &gt; ਸੈਟਿੰਗਾਂ ਤੇ ਜਾਓ।"</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"ਵੌਇਸਮੇਲ ਕਾਲ ਕਰਨ ਲਈ, ਪਹਿਲਾਂ ਏਅਰਪਲੇਨ ਮੋਡ ਬੰਦ ਕਰੋ।"</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"ਲੋਡ ਕਰ ਰਿਹਾ ਹੈ…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"SIM ਕਾਰਡ ਵਿੱਚੋਂ ਲੋਡ ਕਰ ਰਿਹਾ ਹੈ…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM ਕਾਰਡ ਸੰਪਰਕ"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"ਕੋਈ ਸੰਪਰਕ ਐਪ ਉਪਲਬਧ ਨਹੀਂ"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"ਵੌਇਸ ਖੋਜ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
- <string name="call_not_available" msgid="8941576511946492225">"ਇੱਕ ਫੋਨ ਕਾਲ ਨਹੀਂ ਕਰ ਸਕਦਾ ਕਿਉਂਕਿ ਫੋਨ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ।"</string>
- <string name="activity_not_available" msgid="2287665636817987623">"ਉਸ ਲਈ ਇਸ ਡੀਵਾਈਸ ਤੇ ਕੋਈ ਐਪ ਨਹੀਂ"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"ਸੰਪਰਕ ਖੋਜੋ"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"ਨੰਬਰ ਸ਼ਾਮਲ ਕਰੋ ਜਾਂ ਸੰਪਰਕ ਖੋਜੋ"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"ਤੁਹਾਡਾ ਕਾਲ ਇਤਿਹਾਸ ਖਾਲੀ ਹੈ"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"ਇੱਕ ਕਾਲ ਕਰੋ"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"ਤੁਹਾਡੇ ਕੋਲ ਕੋਈ ਵੀ ਮਿਸਡ ਕਾਲਾਂ ਨਹੀਂ ਹਨ।"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"ਤੁਹਾਡਾ ਵੌਇਸਮੇਲ ਇਨਬੌਕਸ ਖਾਲੀ ਹੈ।"</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"ਤੁਹਾਡਾ ਵੌਇਸਮੇਲ ਪੁਰਾਲੇਖ ਖ਼ਾਲੀ ਹੈ।"</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"ਕੇਵਲ ਮਨਪਸੰਦ ਦਿਖਾਓ"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"ਕਾਲ ਇਤਿਹਾਸ"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"ਵੌਇਸਮੇਲ ਪੁਰਾਲੇਖ"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"ਸਭ"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"ਮਿਸਡ"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"ਵੌਇਸਮੇਲ"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"ਨਵੀਂ, ਸਰਲੀਕ੍ਰਿਤ ਬਲੌਕਿੰਗ"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"ਤੁਹਾਡੀ ਬਿਹਤਰ ਢੰਗ ਨਾਲ ਰੱਖਿਆ ਲਈ, ਫੋਨ ਨੂੰ ਬਲੌਕਿੰਗ ਦੇ ਕੰਮ ਕਰਨ ਦੇ ਢੰਗ ਨੂੰ ਬਦਲਣ ਦੀ ਲੋੜ ਹੈ। ਹੁਣ ਤੁਹਾਡੇ ਬਲੌਕ ਕੀਤੇ ਗਏ ਨੰਬਰਾਂ ਤੋਂ ਕਾਲਾਂ ਅਤੇ ਲਿਖਤ ਸੁਨੇਹਿਆਂ ਦੋਵਾਂ ਨੂੰ ਰੋਕ ਦਿੱਤਾ ਜਾਵੇਗਾ ਅਤੇ ਇਹਨਾਂ ਨੂੰ ਹੋਰ ਐਪਾਂ ਨਾਲ ਵੀ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ।"</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"ਮਨਜ਼ੂਰੀ ਦਿਓ"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"ਕੀ <xliff:g id="NUMBER">%1$s</xliff:g> ਬਲੌਕ ਕਰਨਾ ਹੈ?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"ਇਸ ਨੰਬਰ ਤੋਂ ਕਾਲਾਂ ਬਲੌਕ ਕੀਤੀਆਂ ਜਾਣਗੀਆਂ ਅਤੇ ਵੌਇਸਮੇਲਾਂ ਆਪਣੇ ਆਪ ਮਿਟਾ ਦਿੱਤੀਆਂ ਜਾਣਗੀਆਂ।"</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"ਇਸ ਨੰਬਰ ਤੋਂ ਕਾਲਾਂ ਬਲੌਕ ਕੀਤੀਆਂ ਜਾਣਗੀਆਂ, ਪਰ ਕਾਲਰ ਹਾਲੇ ਵੀ ਤੁਹਾਡੇ ਲਈ ਵੌਇਸਮੇਲਾਂ ਛੱਡ ਸਕਦਾ ਹੈ।"</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"ਤੁਹਾਨੂੰ ਹੁਣ ਇਸ ਨੰਬਰ ਤੋਂ ਕਾਲਾਂ ਜਾਂ ਲਿਖਤ ਸੁਨੇਹੇ ਪ੍ਰਾਪਤ ਨਹੀਂ ਹੋਣਗੇ।"</string>
- <string name="block_number_ok" msgid="770551992296781873">"ਬਲੌਕ ਕਰੋ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"ਕੀ <xliff:g id="NUMBER">%1$s</xliff:g> ਅਣਬਲੌਕ ਕਰਨਾ ਹੈ?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ਅਣਬਲੌਕ ਕਰੋ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"ਸਪੀਡ ਡਾਇਲ"</string>
- <string name="tab_history" msgid="2563144697322434940">"ਕਾਲ ਇਤਿਹਾਸ"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"ਸੰਪਰਕ"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"ਵੌਇਸਮੇਲ"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"ਮਨਪਸੰਦ ਵਿੱਚੋਂ ਹਟਾਇਆ ਗਿਆ"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"ਪਹਿਲਾਂ ਵਰਗਾ ਕਰੋ"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> ਨੂੰ ਕਾਲ ਕਰੋ"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"ਨਵਾਂ ਸੰਪਰਕ ਬਣਾਓ"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"ਕਿਸੇ ਸੰਪਰਕ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS ਭੇਜੋ"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"ਵੀਡੀਓ ਕਾਲ ਕਰੋ"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"ਨੰਬਰ ਨੂੰ ਬਲੌਕ ਕਰੋ"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> ਨਵੀਆਂ ਮਿਸਡ ਕਾਲਾਂ"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"ਅਜੇ ਤੁਹਾਡੇ ਸਪੀਡ ਡਾਇਲ \'ਤੇ ਕੋਈ ਵੀ ਵਿਅਕਤੀ ਨਹੀਂ ਹੈ"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"ਕੋਈ ਮਨਪਸੰਦ ਸ਼ਾਮਲ ਕਰੋ"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"ਤੁਹਾਡੇ ਕੋਲ ਅਜੇ ਕੋਈ ਸੰਪਰਕ ਨਹੀਂ ਹਨ।"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"ਕੋਈ ਸੰਪਰਕ ਸ਼ਾਮਲ ਕਰੋ"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"ਸਾਰੇ ਨੰਬਰ ਦੇਖਣ ਲਈ ਚਿੱਤਰ ਨੂੰ ਸਪੱਰਸ਼ ਕਰੋ ਜਾਂ ਪੁਨਰ ਤਰਤੀਬ ਦੇਣ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ &amp; ਹੋਲਡ ਕਰੋ"</string>
- <string name="remove_contact" msgid="1080555335283662961">"ਹਟਾਓ"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"ਵੀਡੀਓ ਕਾਲ"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"ਇੱਕ ਸੁਨੇਹਾ ਭੇਜੋ"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"ਕਾਲ ਵੇਰਵੇ"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ਨੂੰ ਕਾਲ ਕਰੋ"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> ਦੀ ਮਿਸਡ ਕਾਲ।"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> ਦੀ ਕਾਲ ਦਾ ਜਵਾਬ ਦਿੱਤਾ।"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> , <xliff:g id="TYPEORLOCATION">^2</xliff:g> , <xliff:g id="TIMEOFCALL">^3</xliff:g> , <xliff:g id="PHONEACCOUNT">^4</xliff:g> ਵੱਲੋਂ ਬਿਨਾਂ-ਪੜ੍ਹੀ ਵੌਇਸਮੇਲ।"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> ਵੱਲੋਂ ਵੌਇਸਮੇਲ।"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> ਨੂੰ ਕਾਲ ਕਰੋ।"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> ਤੇ"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> ਰਾਹੀਂ"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> ਰਾਹੀਂ"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> \'ਤੇ, <xliff:g id="NUMBER">%2$s</xliff:g> ਰਾਹੀਂ"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, <xliff:g id="NUMBER">%2$s</xliff:g> ਰਾਹੀਂ"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"ਕਾਲ ਕਰੋ"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ਨੂੰ ਕਾਲ ਕਰੋ"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"ਵੀਡੀਓ ਕਾਲ <xliff:g id="NAMEORNUMBER">^1</xliff:g>।"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ਦੀ ਵੌਇਸਮੇਲ ਚੁਣੋ"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ਤੋਂ ਵੌਇਸਮੇਲ ਪਲੇ ਕਰੋ"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ਤੋਂ ਵੌਇਸਮੇਲ ਰੋਕੋ"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ਤੋਂ ਵੌਇਸਮੇਲ ਮਿਟਾਓ"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> ਨਵੀਆਂ ਵੌਇਸਮੇਲਾਂ</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> ਨਵੀਆਂ ਵੌਇਸਮੇਲਾਂ</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ਲਈ ਸੰਪਰਕ ਬਣਾਓ"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ਨੂੰ ਮੌਜੂਦਾ ਸੰਪਰਕ ਵਿੱਚ ਜੋੜੋ"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ਲਈ ਕਾਲ ਵੇਰਵੇ"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"ਕਾਲ ਇਤਿਹਾਸ ਵਿੱਚੋਂ ਮਿਟਾਇਆ ਗਿਆ"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"ਅੱਜ"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"ਕੱਲ੍ਹ"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"ਪੁਰਾਣੇ"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"ਕਾਲਸ ਸੂਚੀ"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"ਸਪੀਕਰ ਚਾਲੂ ਕਰੋ।"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"ਸਪੀਕਰ ਬੰਦ ਕਰੋ।"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"ਵੱਧ ਤੇਜ਼ ਪਲੇ ਕਰੋ।"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"ਵੱਧ ਹੌਲੀ ਪਲੇ ਕਰੋ।"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"ਪਲੇਬੈਕ ਚਾਲੂ ਕਰੋ ਜਾਂ ਰੋਕੋ।"</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"ਡਿਸਪਲੇ ਚੋਣਾਂ"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"ਧੁਨੀਆਂ ਅਤੇ ਥਰਥਰਾਹਟ"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"ਪਹੁੰਚਯੋਗਤਾ"</string>
- <string name="ringtone_title" msgid="760362035635084653">"ਫੋਨ ਰਿੰਗਟੋਨ"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"ਕਾਲਾਂ ਲਈ ਥਰਥਰਾਹਟ ਵੀ ਕਰੋ"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"ਡਾਇਲਪੈਡ ਟੋਨਾਂ"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"ਡਾਇਲਪੈਡ ਟੋਨ ਲੰਮਾਈ"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"ਸਧਾਰਨ"</item>
- <item msgid="6177579030803486015">"ਲੰਮਾ"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"ਤਤਕਾਲ ਜਵਾਬ"</string>
- <string name="call_settings_label" msgid="313434211353070209">"ਕਾਲਾਂ"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"ਕਾਲ ਬਲੌਕ ਕਰਨਾ"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"ਕਾਲ ਬਲੌਕਿੰਗ ਆਰਜ਼ੀ ਤੌਰ ਤੇ ਬੰਦ ਹੈ"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"ਕਾਲ ਬਲੌਕਿੰਗ ਅਸਮਰੱਥ ਕਰ ਦਿੱਤੀ ਗਈ ਹੈ ਕਿਉਂਕਿ ਤੁਸੀਂ ਇਸ ਫੋਨ ਦੁਆਰਾ ਪਿਛਲੇ 48 ਘੰਟਿਆਂ ਤੋਂ ਐਮਰਜੈਂਸੀ ਸੇਵਾਵਾਂ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਹੋਇਆ ਹੈ। ਇਹ 48 ਘੰਟਿਆਂ ਦਾ ਸਮਾਂ ਪੂਰਾ ਹੋਣ ਤੇੋਂ ਬਾਅਦ ਆਟੋਮੈਟਿਕਲੀ ਮੁੜ-ਸਮਰੱਥ ਹੋ ਜਾਵੇਗੀ।"</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"ਆਯਾਤ ਨੰਬਰ"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"ਤੁਹਾਡੇ ਚਿੰਨ੍ਹਿਤ ਕੀਤੇ ਪਿਛਲੇ ਕੁਝ ਕਾਲਰ ਹੋਰ ਐਪਸ ਦੁਆਰਾ ਆਟੋਮੈਟਿਕ ਤੌਰ ਤੇ ਵੌਇਸਮੇਲ ਨੂੰ ਭੇਜੇ ਗਏ ਹਨ।"</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"ਨੰਬਰ ਵਿਖਾਓ"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"ਆਯਾਤ ਕਰੋ"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"ਆਯਾਤ ਅਸਫਲ ਹੋਇਆ"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"ਵੌਇਸਮੇਲ ਨੂੰ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ।"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"ਨੰਬਰ ਨੂੰ ਅਣਬਲੌਕ ਕਰੋ"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"ਨੰਬਰ ਸ਼ਾਮਲ ਕਰੋ"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"ਇਹਨਾਂ ਨੰਬਰਾਂ ਤੋਂ ਕਾਲਾਂ ਬਲੌਕ ਕੀਤੀਆਂ ਜਾਣਗੀਆਂ ਅਤੇ ਵੌਇਸਮੇਲਾਂ ਆਪਣੇ ਆਪ ਮਿਟਾ ਦਿੱਤੀਆਂ ਜਾਣਗੀਆਂ।"</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"ਇਹਨਾਂ ਨੰਬਰਾਂ ਤੋਂ ਕਾਲਾਂ ਬਲੌਕ ਕੀਤੀਆਂ ਜਾਣਗੀਆਂ, ਪਰ ਇਹ ਹਾਲੇ ਵੀ ਤੁਹਾਡੇ ਲਈ ਵੌਇਸਮੇਲਾਂ ਛੱਡ ਸਕਦੇ ਹਨ।"</string>
- <string name="block_list" msgid="7760188925338078011">"ਬਲੌਕ ਕੀਤੇ ਨੰਬਰ"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> ਅਪ੍ਰਮਾਣਿਕ ਹੈ।"</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> ਪਹਿਲਾਂ ਹੀ ਬਲੌਕ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"ਕਾਲ ਬਲੋੌਕਿੰਗ 48 ਘੰਟਿਆਂ ਲਈ ਅਯੋਗ ਕੀਤੀ ਗਈ"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"ਆਯੋਗ ਕੀਤਾ ਕਿਉਂਕਿ ਇੱਕ ਸੰਕਟ ਕਾਲ ਕੀਤੀ ਗਈ ਸੀ।"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"ਕਾਲਿੰਗ ਖਾਤੇ"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"ਚਾਲੂ ਕਰੋ"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"ਅਨੁਮਤੀਆਂ ਸੈੱਟ ਕਰੋ"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"ਸਪੀਡ ਡਾਇਲ ਨੂੰ ਸਮਰੱਥ ਕਰਨ ਲਈ, ਸੰਪਰਕ ਅਨੁਮਤੀ ਚਾਲੂ ਕਰੋ।"</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"ਆਪਣਾ ਕਾਲ ਲੌਗ ਵੇਖਣ ਲਈ, ਫ਼ੋਨ ਅਨੁਮਤੀ ਚਾਲੂ ਕਰੋ।"</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"ਆਪਣੇ ਸੰਪਰਕਾਂ ਨੂੰ ਵੇਖਣ ਲਈ, ਸੰਪਰਕ ਅਨੁਮਤੀ ਚਾਲੂ ਕਰੋ।"</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"ਆਪਣੀ ਵੌਇਸਮੇਲ ਐਕਸੈਸ ਕਰਨ ਲਈ, ਫ਼ੋਨ ਅਨੁਮਤੀ ਚਾਲੂ ਕਰੋ।"</string>
- <string name="permission_no_search" msgid="84152933267902056">"ਆਪਣੇ ਸੰਪਰਕਾਂ ਦੀ ਖੋਜ ਕਰਨ ਲਈ,, ਸੰਪਰਕ ਅਨੁਮਤੀਆਂ ਨੂੰ ਚਾਲੂ ਕਰੋ।"</string>
- <string name="permission_place_call" msgid="2101287782615887547">"ਕਾਲ ਕਰਨ ਲਈ, ਫ਼ੋਨ ਅਨੁਮਤੀ ਚਾਲੂ ਕਰੋ।"</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"ਫ਼ੋਨ ਐਪ ਦੇ ਕੋਲ ਸਿਸਟਮ ਸੈਟਿੰਗਜ਼ ਵਿੱਚ ਲਿੱਖਣ ਦੀ ਅਨੁਮਤੀ ਨਹੀਂ ਹੁੰਦੀ ਹੈ।"</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"ਬਲੌਕ ਕੀਤਾ ਗਿਆ"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ਕਿਰਿਆਸ਼ੀਲ ਹੈ"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"ਸਪੈਮ ਨੂੰ ਬਲੌਕ ਕਰੋ/ਰਿਪੋਰਟ ਕਰੋ"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"ਬਲੌਕ ਕਰੋ"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"ਸਪੈਮ ਨਹੀਂ"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"ਅਣਬਲੌਕ ਕਰੋ"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"ਸਪੈਮ"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"ਕੀ <xliff:g id="NUMBER">%1$s</xliff:g> ਨੂੰ ਬਲੌਕ ਕਰਨਾ ਹੈ?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"ਇਸ ਨੰਬਰ ਤੋਂ ਭਵਿੱਖੀ ਕਾਲਾਂ ਅਤੇ ਵੌਇਸਮੇਲਾਂ ਨੂੰ ਬਲੌਕ ਕੀਤਾ ਜਾਵੇਗਾ।"</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"ਕਾਲ ਨੂੰ ਸਪੈਮ ਵਜੋਂ ਰਿਪੋਰਟ ਕਰੋ"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"ਇਸ ਨੰਬਰ ਤੋਂ ਭਵਿੱਖੀ ਕਾਲਾਂ ਅਤੇ ਵੌਇਸਮੇਲਾਂ ਨੂੰ ਬਲੌਕ ਕੀਤਾ ਜਾਵੇਗਾ। ਇਸ ਕਾਲ ਨੂੰ ਸਪੈਮ ਵਜੋਂ ਰਿਪੋਰਟ ਕੀਤਾ ਜਾਵੇਗਾ।"</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"ਕੀ <xliff:g id="NUMBER">%1$s</xliff:g> ਅਣਬਲੌਕ ਕਰਨਾ ਹੈ?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"ਇਹ ਨੰਬਰ ਨੂੰ ਅਨਲੌਕ ਕੀਤਾ ਜਾਵੇਗਾ ਅਤੇ ਸਪੈਮ ਨਹੀਂ ਵਜੋਂ ਰਿਪੋਰਟ ਕੀਤਾ ਜਾਵੇਗਾ। ਭਵਿੱਖ ਦੀਆਂ ਕਾਲਾਂ ਅਤੇ ਵੌਇਸਮੇਲਾਂ ਨੂੰ ਸਪੈਮ ਵਜੋਂ ਸਮਝਿਆ ਜਾਵੇਗਾ।"</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"ਕੀ <xliff:g id="NUMBER">%1$s</xliff:g> ਨੂੰ ਵਾਈਟਲਿਸਟ ਕਰਨਾ ਹੈ?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"ਵਾਈਟਲਿਸਟ"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"ਇਸ ਨੰਬਰ ਤੋਂ ਭਵਿੱਖੀ ਕਾਲਾਂ ਅਤੇ ਵੌਇਸਮੇਲਾਂ ਸਪੈਮ ਨਹੀਂ ਸਮਝਿਆ ਜਾਵੇਗਾ। ਇਸ ਨੰਬਰ ਨੂੰ ਸਪੈਮ ਨਹੀਂ ਵਜੋਂ ਰਿਪੋਰਟ ਕੀਤਾ ਜਾਵੇਗਾ।"</string>
-</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
deleted file mode 100644
index f4dde7d17..000000000
--- a/res/values-pl/strings.xml
+++ /dev/null
@@ -1,292 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Klawiatura telefonu"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Historia połączeń"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Zgłoś niedokładny numer"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopiuj numer"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopiuj zapis"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Zablokuj numer"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> zablokowany"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Odblokuj numer"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> odblokowany"</string>
- <string name="block_number_undo" msgid="591338370336724156">"COFNIJ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Usuń"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Edytuj numer przed połączeniem"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Wyczyść historię połączeń"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Usuń pocztę głosową"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Archiwizuj pocztę głosową"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Udostępnij pocztę głosową"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Poczta usunięta"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Poczta głosowa zarchiwizowana"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"COFNIJ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ARCHIWUM"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Wyczyścić historię połączeń?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Spowoduje to usunięcie wszystkich połączeń z historii."</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Czyszczę historię połączeń…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Nieodebrane połączenie"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Nieodebrane połączenie (praca)"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Nieodebrane połączenia"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"Liczba nieodebranych połączeń: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Oddzwoń"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Wyślij SMS-a"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="few"> <xliff:g id="COUNT">%1$d</xliff:g> wiadomości głosowe </item>
- <item quantity="many"> <xliff:g id="COUNT">%1$d</xliff:g> wiadomości głosowych </item>
- <item quantity="other"> Wiadomości głosowe: <xliff:g id="COUNT">%1$d</xliff:g> </item>
- <item quantity="one">Wiadomość głosowa</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Odtwórz"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nowa poczta głosowa od: <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Nie można odtworzyć wiadomości głosowej"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Wczytuję zawartość poczty głosowej…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Archiwizuję pocztę głosową…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Nie można wczytać zawartości poczty głosowej"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Tylko połączenia z pocztą głosową"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Tylko połączenia przychodzące"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Tylko połączenia wychodzące"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Tylko połączenia nieodebrane"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Wizualna poczta głosowa"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Sprawdź i odsłuchaj wiadomości głosowe bez dzwonienia pod określony numer. Może zostać naliczona opłata za transfer danych."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Ustawienia"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Brak aktualizacji o wiadomościach głosowych"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Oczekują nowe wiadomości głosowe. Obecnie nie można ich wczytać."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Skonfiguruj swoją pocztę głosową"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Dźwięk jest niedostępny"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Konfiguracja"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Połącz z pocztą"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Wybierz numer"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Wybierz numer"</string>
- <string name="make_primary" msgid="5829291915305113983">"Zapamiętaj ten wybór"</string>
- <string name="description_search_button" msgid="3660807558587384889">"szukaj"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"wybierz numer"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"numer do wybrania"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Uruchom lub zatrzymaj odtwarzanie"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Włącz lub wyłącz tryb głośnomówiący"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Szukaj pozycji odtwarzania"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Zmniejsz szybkość odtwarzania"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Zwiększ szybkość odtwarzania"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Historia połączeń"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Więcej opcji"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"klawiatura"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Pokaż tylko wychodzące"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Pokaż tylko przychodzące"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Pokaż tylko nieodebrane"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Pokaż tylko pocztę głosową"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Pokaż wszystkie połączenia"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Dodaj 2-sekundową pauzę"</string>
- <string name="add_wait" msgid="3360818652790319634">"Dodaj oczekiwanie"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Ustawienia"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Nowy kontakt"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Wszystkie kontakty"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Szczegóły połączenia"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Szczegóły nie są dostępne"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Użyj klawiatury tonowej"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Wróć do aktywnego połączenia"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Dodaj połączenie"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Połączenie"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Połączenie wychodzące"</string>
- <string name="type_missed" msgid="2720502601640509542">"Nieodebrane połączenia"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Przychodząca rozmowa wideo"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Wychodząca rozmowa wideo"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Nieodebrana rozmowa wideo"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Poczta głosowa"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Odrzucone połączenie"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Zablokowane połączenie"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Połączenia przychodzące"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Odtwórz pocztę głosową"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Wyświetl kontakt <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Połącz z: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Szczegóły kontaktu: <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"Połączenia: <xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Rozmowa wideo."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Wyślij SMS-a do: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Nieodsłuchana poczta głosowa"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Wyszukiwanie głosowe"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Zadzwoń: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Nieznane"</string>
- <string name="voicemail" msgid="3851469869202611441">"Poczta głosowa"</string>
- <string name="private_num" msgid="6374339738119166953">"Numer prywatny"</string>
- <string name="payphone" msgid="7726415831153618726">"Automat telefoniczny"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> o <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Nie można zadzwonić pod ten numer"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Aby skonfigurować pocztę głosową, przejdź do Menu &gt; Ustawienia."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Aby połączyć się z pocztą głosową, najpierw wyłącz tryb samolotowy."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Wczytuję…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"Numer MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Wczytuję z karty SIM…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"Kontakty z karty SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Nie jest dostępna aplikacja do obsługi kontaktów"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Wyszukiwanie głosowe jest niedostępne"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Nie można zadzwonić, ponieważ aplikacja Telefon została wyłączona."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Na urządzeniu nie ma aplikacji, która mogłaby wykonać tę czynność"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Wyszukaj kontakty"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Dodaj numer lub wyszukaj kontakty"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Twoja historia połączeń jest pusta"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Zadzwoń"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Nie masz nieodebranych połączeń."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Skrzynka odbiorcza poczty głosowej jest pusta."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Archiwum poczty głosowej jest puste."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Pokaż tylko ulubione"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Historia połączeń"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Archiwum poczty głosowej"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Wszystkie"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Nieodebrane"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Poczta gł."</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Nowe, uproszczone blokowanie"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Aby lepiej Cię chronić, Telefon musi zmienić działanie związane z blokowaniem. Od teraz nie będziesz już otrzymywać połączeń ani SMS-ów z zablokowanych numerów. Numery te mogą też być udostępniane innym aplikacjom."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Zezwól"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Zablokować numer <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Połączenia z tego numeru będą blokowane, a wiadomości głosowe będą usuwane automatycznie."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Połączenia z tego numeru będą blokowane, ale dzwoniący wciąż będzie mógł zostawiać wiadomości głosowe."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Nie będziesz otrzymywać połączeń ani SMS-ów z tego numeru."</string>
- <string name="block_number_ok" msgid="770551992296781873">"ZABLOKUJ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Odblokować numer <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ODBLOKUJ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Szybkie wybieranie"</string>
- <string name="tab_history" msgid="2563144697322434940">"Historia połączeń"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontakty"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Poczta głosowa"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Usunięto z ulubionych"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Cofnij"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Zadzwoń: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Utwórz nowy kontakt"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Dodaj do kontaktu"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Wyślij SMS-a"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Rozmowa wideo"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Zablokuj numer"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"Nowe nieodebrane połączenia: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Lista szybkiego wybierania jest pusta"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Dodaj do ulubionych"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Nie masz jeszcze żadnych kontaktów"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Dodaj kontakt"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Kliknij obraz, by zobaczyć wszystkie numery, lub kliknij go i przytrzymaj, by zmienić kolejność"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Usuń"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Rozmowa wideo"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Wyślij wiadomość"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Szczegóły połączenia"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Zadzwoń: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Nieodebrane połączenie: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Odebrane połączenie: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Nieodsłuchana poczta głosowa od: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Poczta głosowa od: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Połączenie: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"na koncie <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"przez <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"przez <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"na <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, przez <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> przez <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Zadzwoń"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Zadzwoń: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Rozpocznij rozmowę wideo z: <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Posłuchaj wiadomości głosowej od: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Odtwórz wiadomości głosowe od <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Wstrzymaj odtwarzanie wiadomości głosowych od <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Usuń wiadomości głosowe od <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> nowe wiadomości głosowe</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> nowych wiadomości głosowych</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> nowej wiadomości głosowej</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> nowa wiadomość głosowa</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Utwórz kontakt <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Dodaj: <xliff:g id="NAMEORNUMBER">^1</xliff:g> do istniejącego kontaktu"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Szczegóły połączeń: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Usunięto z historii połączeń"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Dzisiaj"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Wczoraj"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Starsze"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Lista połączeń"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Włącz głośnik."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Wyłącz głośnik."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Odtwarzaj szybciej."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Odtwarzaj wolniej."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Rozpocznij lub wstrzymaj odtwarzanie."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Opcje wyświetlania"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Dźwięki i wibracje"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Ułatwienia dostępu"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Dzwonek telefonu"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Wibracja przy połączeniach"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Dźwięki klawiatury"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Tony klawiatury"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normalne"</item>
- <item msgid="6177579030803486015">"Długie"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Szybkie odpowiedzi"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Połączenia"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Blokowanie połączeń"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Blokada połączeń tymczasowo wyłączona"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Blokada połączeń została wyłączona, ponieważ w ciągu ostatnich 48 godzin dzwoniono z tego telefonu na numer alarmowy. Blokada zostanie automatycznie przywrócona po upływie 48 godzin."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Zaimportuj numery"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Oznaczyłeś wcześniej niektórych rozmówców, aby byli automatycznie przekierowywani na pocztę głosową przy użyciu innych aplikacji."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Pokaż numery"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importuj"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Nie udało się zaimportować"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Nie udało się zarchiwizować."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Odblokuj numer"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Dodaj numer"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Połączenia z tych numerów będą blokowane, a wiadomości głosowe będą usuwane automatycznie."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Połączenia z tych numerów będą blokowane, ale dzwoniący wciąż będą mogli zostawiać wiadomości głosowe."</string>
- <string name="block_list" msgid="7760188925338078011">"Zablokowane numery"</string>
- <string name="invalidNumber" msgid="619058581062192851">"Numer <xliff:g id="NUMBER">%1$s</xliff:g> jest nieprawidłowy."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"Numer <xliff:g id="NUMBER">%1$s</xliff:g> jest już zablokowany."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Blokada połączeń została wyłączona na 48 godzin"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Wyłączona, ponieważ wykonano połączenie alarmowe."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Konta telefoniczne"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Włącz"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Ustaw uprawnienia"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Aby włączyć szybkie wybieranie, włącz uprawnienie Kontakty."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Aby zobaczyć swój rejestr połączeń, włącz uprawnienie Telefon."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Aby zobaczyć swoje kontakty, włącz uprawnienie Kontakty."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Aby uzyskać dostęp do poczty głosowej, włącz uprawnienie Telefon."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Aby wyszukać kontakty, włącz uprawnienia Kontakty."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Aby nawiązać połączenie, włącz uprawnienie Telefon."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Aplikacja Telefon nie ma uprawnień do zapisu w ustawieniach systemowych."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Zablokowany"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> – rozmowa aktywna"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Zablokuj/zgłoś spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Zablokuj"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"To nie spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Odblokuj"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Zablokować numer <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Rozmowy i poczta głosowa z tego numeru będą blokowane."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Zgłoś połączenie jako spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Rozmowy i poczta głosowa z tego numeru będą blokowane. To połączenie zostanie zgłoszone jako spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Odblokować numer <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Ten numer zostanie odblokowany i usunięty z listy nadawców spamu. Rozmowy i poczta głosowa będą dostarczane."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Dodać numer <xliff:g id="NUMBER">%1$s</xliff:g> do białej listy?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Dodaj do białej listy"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Rozmowy i poczta głosowa z tego numeru nie będą traktowane jak spam. To połączenie nie zostanie zgłoszone."</string>
-</resources>
diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml
deleted file mode 100644
index f7cbc0a58..000000000
--- a/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefone"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefone"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Teclado numérico do smartphone"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefone"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Histórico de chamadas"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Informar número incorreto"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Copiar número"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Copiar transcrição"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Bloquear número"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Número <xliff:g id="NUMBER">%1$s</xliff:g> bloqueado"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Desbloquear número"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Número <xliff:g id="NUMBER">%1$s</xliff:g> desbloqueado"</string>
- <string name="block_number_undo" msgid="591338370336724156">"DESFAZER"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Excluir"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Editar número antes de chamar"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Limpar histórico de chamadas"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Excluir mensagem de voz"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arquivar correio de voz"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Compartilhar mensagem de voz"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Correio de voz excluído"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Correio de voz arquivado"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"DESFAZER"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ARQUIVAR"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Limpar histórico de chamadas?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Esta ação excluirá todas as chamadas do seu histórico"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Limpando histórico de chamadas…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Smartphone"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Chamada perdida"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Chamada de trabalho perdida"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Chamadas perdidas"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> chamadas perdidas"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Retornar chamada"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Mensagem"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> Correios de voz </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Correios de voz </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Reproduzir"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nova mensagem de voz de <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Não foi possível repr. correio de voz"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Carregando correio de voz..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Arquivando correio de voz..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Não foi possível carregar correio de voz"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Somente chamadas com correio de voz"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Somente chamadas recebidas"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Somente chamadas de saída"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Somente chamadas perdidas"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Correio de voz visual"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Veja e ouça seu correio de voz, sem precisar ligar para um número. Podem ser aplicadas cobranças de dados."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Configurações"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"As atualizações do correio de voz não estão disponíveis"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Novo correio de voz na espera. Não é possível carregá-lo agora."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Configure seu correio de voz"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"O áudio não está disponível"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Configurar"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Ligar p/ correio voz"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Escolher número"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Escolher número"</string>
- <string name="make_primary" msgid="5829291915305113983">"Lembrar desta escolha"</string>
- <string name="description_search_button" msgid="3660807558587384889">"pesquisar"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"discar"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"número para discagem"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Iniciar ou interromper a reprodução"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Ligar ou desligar o vivavoz"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Procurar posição de reprodução"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Reduzir a taxa de reprodução"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Aumentar a taxa de reprodução"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Histórico de chamadas"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Mais opções"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"teclado numérico"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Mostrar apenas enviadas"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Mostrar apenas recebidas"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Mostrar apenas perdidas"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Exibir apenas mensagens de voz"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Mostrar todas as chamadas"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Adicionar pausa de 2 segundos"</string>
- <string name="add_wait" msgid="3360818652790319634">"Adicionar espera"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Configurações"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Novo contato"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Todos os contatos"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Detalhes da chamada"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Os detalhes não estão disponíveis"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Usar teclado multifrequencial"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Retornar para a chamada em espera"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Ad. cham."</string>
- <string name="type_incoming" msgid="6502076603836088532">"Chamada recebida"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Chamada efetuada"</string>
- <string name="type_missed" msgid="2720502601640509542">"Chamada perdida"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Videochamada recebida"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Videochamada realizada"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Videochamada perdida"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Correio de voz"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Chamada recusada"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Chamada bloqueada"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Chamadas recebidas"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Reproduzir mensagem de voz"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Visualizar contato <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Ligar para <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Detalhes de contato para <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> chamadas."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videochamada."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Enviar SMS para <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Nova mensagem de voz"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Iniciar pesquisa por voz"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Ligar para <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Desconhecido"</string>
- <string name="voicemail" msgid="3851469869202611441">"Correio de voz"</string>
- <string name="private_num" msgid="6374339738119166953">"Número privado"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefone público"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> seg"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> m <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> às <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Não é possível ligar para este número"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Para configurar o correio de voz, vá para Menu &gt; Configurações."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Para ligar para o correio de voz, primeiro desative o modo avião."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Carregando…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Carregando do cartão SIM..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Contatos do cartão SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Nenhum app de contatos disponível"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"A pesquisa por voz não está disponível"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Não é possível fazer uma chamada porque o app Telefone foi desativado."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Nenhum app está disponível para essa ação neste dispositivo"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Pesquisar contatos"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Adicionar número ou pesquisar contatos"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Seu histórico de chamadas está vazio"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Fazer uma chamada"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Você não tem chamadas perdidas."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Sua caixa de entrada de correio de voz está vazia."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Seu correio de voz está vazio."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Mostrar somente favoritos"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Histórico de chamadas"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arquivo do correio de voz"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Todas"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Perdidas"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Correio de voz"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Novo bloqueio simplificado"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Para aumentar sua proteção, é preciso alterar o modo como o bloqueio funciona no smartphone. Os números bloqueados agora impedirão chamadas e mensagens de texto, e será possível compartilhá-los com outros apps."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Permitir"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Bloquear <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Chamadas feitas a partir deste número serão bloqueadas. Correios de voz serão excluídos automaticamente."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Chamadas feitas a partir deste número serão bloqueadas, mas é possível que o autor ainda consiga deixar correios de voz para você."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Você não receberá mais chamadas ou mensagens de texto deste número."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOQUEAR"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Desbloquear <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DESBLOQUEAR"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Discagem rápida"</string>
- <string name="tab_history" msgid="2563144697322434940">"Histórico de chamadas"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Contatos"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Correio de voz"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Removido dos favoritos"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Desfazer"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Ligar para <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Criar novo contato"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Adicionar a um contato"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Enviar SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Fazer videochamada"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Bloquear número"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> novas chamadas perdidas"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Ainda não há ninguém na sua discagem rápida"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Adicionar favorito"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Você ainda não tem contatos"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Adicionar um contato"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Toque na imagem para ver todos os números ou toque e segure para reordenar"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Remover"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videochamada"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Enviar uma mensagem"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Detalhes da chamada"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Ligar para <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Chamada perdida de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Chamada atendida de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Correio de voz não lido de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Correio de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Chamada para <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"em <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"por <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"por <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"em <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, por <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> por <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Ligar"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Ligar para <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Videochamada <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Ouvir mensagem de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Reproduzir correio de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pausar correio de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Excluir correio de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> novos correios de voz</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> novos correios de voz</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Criar contato para <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Adicionar <xliff:g id="NAMEORNUMBER">^1</xliff:g> a um contato já existente"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Detalhes de chamada para <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Excluída do histórico de chamadas"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Hoje"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Ontem"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Antiga"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Lista de chamadas"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Ative o alto-falante."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Desative o alto-falante."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Reprodução mais rápida."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Reprodução mais lenta."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Inicie ou pause a reprodução."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Opções de exibição"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Sons e vibração"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Acessibilidade"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Toque do telefone"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Também vibrar para chamadas"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tons de teclado"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Duração do tom do teclado"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normais"</item>
- <item msgid="6177579030803486015">"Longos"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Respostas rápidas"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Chamadas"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Bloqueio de chamadas"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Bloqueio de chamadas temporiamente desativado"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"O bloqueio de chamadas foi desativado porque você entrou em contato com serviços de emergência usando este telefone nas últimas 48 horas. Ele será reativado automaticamente depois de um período de 48 horas."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importar números"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Anteriormente, você marcou alguns autores de chamada para serem enviados automaticamente para o correio de voz por meio de outros apps."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Ver números"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importar"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Falha na importação"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Falha ao arquivar correio de voz."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Desbloquear número"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Adicionar número"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Chamadas a partir destes números serão bloqueadas. Correios de voz serão excluídos automaticamente."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Chamadas feitas a partir destes números serão bloqueadas, mas é possível que eles ainda consigam deixar correios de voz para você."</string>
- <string name="block_list" msgid="7760188925338078011">"Números bloqueados"</string>
- <string name="invalidNumber" msgid="619058581062192851">"O número <xliff:g id="NUMBER">%1$s</xliff:g> é inválido."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"O número <xliff:g id="NUMBER">%1$s</xliff:g> já está bloqueado."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Bloqueio de chamadas desativado por 48 horas"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Desativado porque foi feita uma chamada de emergência."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Contas de chamadas"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Ativar"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Definir permissões"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Para ativar a discagem rápida, ative a permissão para o app Contatos."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Para ver seu registro de chamadas, ative a permissão para o app Telefone."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Para ver seus contatos, ative a permissão para o app Contatos."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Para acessar seu correio de voz, ative a permissão para o app Telefone."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Para pesquisar seus contatos, ative as permissões para \"Contatos\"."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Para fazer uma chamada, ative a permissão para o app Telefone."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"O app Telefone não tem permissão para gravar nas configurações do sistema."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Bloqueado"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"Chamada ativa: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Bloquear/denunciar spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Bloquear"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Não é spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Desbloquear"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Bloquear <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"As chamadas e correios de voz desse número serão bloqueados."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Denunciar chamada como spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"As chamadas e correios de voz desse número serão bloqueados. Essa chamada será denunciada como spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Desbloquear <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"O número será desbloqueado e marcado como não spam. As chamadas e correios de voz não serão marcados como spam."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Colocar <xliff:g id="NUMBER">%1$s</xliff:g> na lista de permissões?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Colocar na lista de permissões"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"As chamadas e correios de voz desse número não serão marcados como spam. O número será marcado como não sendo spam."</string>
-</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
deleted file mode 100644
index dddc1d237..000000000
--- a/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telemóvel"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telemóvel"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Teclado do telemóvel"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefone"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Histórico de chamadas"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Denunciar número incorreto"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Copiar número"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Copiar transcrição"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Bloquear número"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> bloqueado"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Desbloquear número"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> desbloqueado"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ANULAR"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Eliminar"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Editar número antes de ligar"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Limpar histórico de chamadas"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Eliminar correio de voz"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arquivar msg. correio de voz"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Partilhar correio de voz"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Correio de voz elim."</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Msg. correio de voz arquivada"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ANULAR"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ARQUIVO"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Limpar histórico de chamadas?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Todas as chamadas serão eliminadas do histórico"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"A limpar histórico de chamadas…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telemóvel"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Chamada não atendida"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Chamada de trabalho não atendida"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Chamadas não atendidas"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> chamadas não atendidas"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Ligar de volta"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Mensagem"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> mensagens de correio de voz </item>
- <item quantity="one">Mensagem de correio de voz</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Reproduzir"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g> , <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nova msg de correio de voz de <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Não foi poss. reprod. o correio de voz"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"A carregar o correio de voz..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"A arquivar mensagem de correio de voz…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Não foi poss. carregar o correio de voz"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Apenas chamadas com correio de voz"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Apenas chamadas recebidas"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Apenas chamadas efetuadas"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Apenas chamadas não atendidas"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Correio de voz visual"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Veja e ouça o seu correio de voz sem ter de telefonar para um número. Podem aplicar-se taxas de dados."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Definições"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Atualizações do correio de voz não disponíveis"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Nova mensag. corr. voz a aguardar. Não é poss. carregar agora."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Configurar o correio de voz"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Áudio não disponível"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Configurar"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Chamar correio de voz"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Selecionar número"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Selecionar número"</string>
- <string name="make_primary" msgid="5829291915305113983">"Memorizar esta escolha"</string>
- <string name="description_search_button" msgid="3660807558587384889">"pesquisar"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"marcar"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"número a marcar"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Reproduzir ou interromper a reprodução"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Ligar ou desligar o altifalante"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Procurar a posição da reprodução"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Diminuir a velocidade de reprodução"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Aumentar a velocidade de reprodução"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Histórico de chamadas"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Mais opções"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"teclado"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Mostrar apenas cham. efetuadas"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Mostrar apenas cham. recebidas"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Mostrar apenas cham. n. atend."</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Mostrar apenas msgs corr. voz"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Mostrar todas as chamadas"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Adicionar pausa de 2 seg."</string>
- <string name="add_wait" msgid="3360818652790319634">"Adicionar espera"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Definições"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Novo contacto"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Todos os contactos"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Detalhes da chamada"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Detalhes não disponíveis"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Utilizar teclado numérico com tons de toque"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Voltar à chamada em curso"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Adicionar chamada"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Chamada recebida"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Chamada efetuada"</string>
- <string name="type_missed" msgid="2720502601640509542">"Chamada não atendida"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"A receber videochamada"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Videochamada efetuada"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Videochamada não atendida"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Correio de voz"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Chamada recusada"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Chamada bloqueada"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Chamadas recebidas"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Reproduzir mensagem de correio de voz"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Ver o contacto <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Ligar a <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Detalhes de contacto para <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> chamadas."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videochamada."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Enviar SMS para <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Mensagem de correio de voz ainda não ouvida"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Iniciar pesquisa por voz"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Telefonar para <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Desconhecido"</string>
- <string name="voicemail" msgid="3851469869202611441">"Correio de voz"</string>
- <string name="private_num" msgid="6374339738119166953">"Número particular"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefone público"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min. <xliff:g id="SECONDS">%s</xliff:g> seg."</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> às <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Não é possível ligar para este número"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Para configurar o correio de voz, aceda a Menu &gt; Definições."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Para efetuar uma chamada para o correio de voz, desative primeiro o Modo de avião."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"A carregar…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"A carregar a partir do cartão SIM..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Contactos no cartão SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Não existe nenhuma aplicação de contactos disponível"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Pesquisa por voz não disponível"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Não é possível efetuar uma chamada porque a aplicação Telefone foi desativada."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Não existe nenhuma aplicação para isso neste dispositivo"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Pesquisar contactos"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Adic. n.º ou pesq. contactos"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"O seu histórico de chamadas está vazio"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Efetuar uma chamada"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Não tem chamadas não atendidas."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"A caixa de entrada do correio de voz está vazia."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"O seu arquivo de mensagens de correio de voz está vazio."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Mostrar apenas os favoritos"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Histórico de Chamadas"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arquivo de mensagens de correio de voz"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Todas"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Não atendidas"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Corr. Voz"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Novo bloqueio simplificado"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Para o proteger melhor, a aplicação Telemóvel tem de alterar o modo de funcionamento do bloqueio. Deixa de receber chamadas e mensagens de texto dos números bloqueados e estes podem ser partilhados com outras aplicações."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Permitir"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Pretende bloquear <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"As chamadas a partir deste número serão bloqueadas e as mensagens de correio de voz serão automaticamente eliminadas."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"As chamadas a partir deste número serão bloqueadas, mas o autor da chamada poderá deixar-lhe mensagens de correio de voz."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Deixará de receber chamadas ou mensagens de texto deste número."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOQUEAR"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Pretende desbloquear <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DESBLOQUEAR"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Marcação rápida"</string>
- <string name="tab_history" msgid="2563144697322434940">"Histórico de Chamadas"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Contactos"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Correio de voz"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Removido dos favoritos"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Anular"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Telefonar para <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Criar novo contacto"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Adicionar a um contacto"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Enviar SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Fazer videochamada"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Bloquear número"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> chamadas não atendidas novas"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Ainda não tem ninguém na marcação rápida"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Adicionar um favorito"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Ainda não tem nenhum contacto"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Adicionar um contacto"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Toque na imagem para ver todos os números ou toque sem soltar para reordenar"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Remover"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videochamada"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Enviar uma mensagem"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Detalhes da chamada"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Telefonar a <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Chamada não atendida de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Chamada atendida de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Mensagem de correio de voz não lida de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Mensagem de correio de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Telefonar para <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"em <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"através do número <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"através do número <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"em <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, através do número <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> através do número <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Telefonar"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Telefonar a <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Fazer videochamada com <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Ouvir o correio de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Reproduzir mensagem de correio de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Colocar a mensagem de correio de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g> em pausa"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Eliminar mensagem de correio de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> novas mensagens de correio de voz</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> nova mensagem de correio de voz</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Criar contacto para <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Adicionar <xliff:g id="NAMEORNUMBER">^1</xliff:g> ao contacto existente"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Detalhes de chamadas de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Eliminado do histórico de chamadas"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Hoje"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Ontem"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Mais antigas"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Lista de chamadas"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Ligar altifalante."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Desligar altifalante"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Reproduzir mais rápido."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Reproduzir mais lento."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Iniciar ou interromper a reprodução."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Opções de visualização"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Sons e vibração"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Acessibilidade"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Toque do telemóvel"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibrar também para chamadas"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tons do teclado"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Duração do tom do teclado"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"Longa"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Respostas rápidas"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Chamadas"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Bloqueio de chamadas"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Bloqueio de chamadas tempor. desativado"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"O bloqueio de chamadas foi desativado porque contactou os serviços de emergência a partir deste telemóvel nas últimas 48 horas. O bloqueio será automaticamente reativado assim que expirar o período de 48 horas."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importar números"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Anteriormente, marcou alguns autores de chamadas para serem automaticamente enviados para o correio de voz através de outras aplicações."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Ver números"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importar"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Falha ao importar"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Falha ao arquivar msg. correio de voz."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Desbloquear número"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Adicionar número"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"As chamadas a partir destes números serão bloqueadas e as mensagens de correio de voz serão automaticamente eliminadas."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"As chamadas a partir destes números serão bloqueadas, mas os respetivos autores poderão deixar-lhe mensagens de correio de voz."</string>
- <string name="block_list" msgid="7760188925338078011">"Números bloqueados"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> é inválido."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> já está bloqueado."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Bloqueio de chamadas desativado durante 48 horas"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Desativado porque foi efetuada uma chamada de emergência."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Contas de chamadas"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Ativar"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Definir autorizações"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Para ativar a marcação rápida, ative a autorização Contactos."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Para ver o registo de chamadas, ative a autorização Telemóvel."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Para ver os seus contactos, ative a autorização Contactos."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Para aceder ao correio de voz, ative a autorização Telemóvel."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Para pesquisar os seus contactos, ative as autorizações Contactos."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Para efetuar uma chamada, ative a autorização Telemóvel."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"A aplicação Telefone não tem autorização para gravar nas definições do sistema."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Bloqueado"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> está ativa"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Bloquear/denunciar spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Bloquear"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Não é spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Desbloquear"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Pretende bloquear o número <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"As chamadas e as mensagens de correio de voz futuras deste número serão bloqueadas."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Registar a chamada como spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"As chamadas e o correio de voz deste número serão bloqueados. Esta chamada será registada como spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Pretende desbloquear o número <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Pretende adicionar o número <xliff:g id="NUMBER">%1$s</xliff:g> à lista de autorizações?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Lista de autorizações"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"."</string>
-</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
deleted file mode 100644
index f7cbc0a58..000000000
--- a/res/values-pt/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefone"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefone"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Teclado numérico do smartphone"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefone"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Histórico de chamadas"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Informar número incorreto"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Copiar número"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Copiar transcrição"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Bloquear número"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Número <xliff:g id="NUMBER">%1$s</xliff:g> bloqueado"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Desbloquear número"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Número <xliff:g id="NUMBER">%1$s</xliff:g> desbloqueado"</string>
- <string name="block_number_undo" msgid="591338370336724156">"DESFAZER"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Excluir"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Editar número antes de chamar"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Limpar histórico de chamadas"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Excluir mensagem de voz"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arquivar correio de voz"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Compartilhar mensagem de voz"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Correio de voz excluído"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Correio de voz arquivado"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"DESFAZER"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ARQUIVAR"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Limpar histórico de chamadas?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Esta ação excluirá todas as chamadas do seu histórico"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Limpando histórico de chamadas…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Smartphone"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Chamada perdida"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Chamada de trabalho perdida"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Chamadas perdidas"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> chamadas perdidas"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Retornar chamada"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Mensagem"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> Correios de voz </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Correios de voz </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Reproduzir"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nova mensagem de voz de <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Não foi possível repr. correio de voz"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Carregando correio de voz..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Arquivando correio de voz..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Não foi possível carregar correio de voz"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Somente chamadas com correio de voz"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Somente chamadas recebidas"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Somente chamadas de saída"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Somente chamadas perdidas"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Correio de voz visual"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Veja e ouça seu correio de voz, sem precisar ligar para um número. Podem ser aplicadas cobranças de dados."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Configurações"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"As atualizações do correio de voz não estão disponíveis"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Novo correio de voz na espera. Não é possível carregá-lo agora."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Configure seu correio de voz"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"O áudio não está disponível"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Configurar"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Ligar p/ correio voz"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Escolher número"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Escolher número"</string>
- <string name="make_primary" msgid="5829291915305113983">"Lembrar desta escolha"</string>
- <string name="description_search_button" msgid="3660807558587384889">"pesquisar"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"discar"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"número para discagem"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Iniciar ou interromper a reprodução"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Ligar ou desligar o vivavoz"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Procurar posição de reprodução"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Reduzir a taxa de reprodução"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Aumentar a taxa de reprodução"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Histórico de chamadas"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Mais opções"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"teclado numérico"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Mostrar apenas enviadas"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Mostrar apenas recebidas"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Mostrar apenas perdidas"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Exibir apenas mensagens de voz"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Mostrar todas as chamadas"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Adicionar pausa de 2 segundos"</string>
- <string name="add_wait" msgid="3360818652790319634">"Adicionar espera"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Configurações"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Novo contato"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Todos os contatos"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Detalhes da chamada"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Os detalhes não estão disponíveis"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Usar teclado multifrequencial"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Retornar para a chamada em espera"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Ad. cham."</string>
- <string name="type_incoming" msgid="6502076603836088532">"Chamada recebida"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Chamada efetuada"</string>
- <string name="type_missed" msgid="2720502601640509542">"Chamada perdida"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Videochamada recebida"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Videochamada realizada"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Videochamada perdida"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Correio de voz"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Chamada recusada"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Chamada bloqueada"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Chamadas recebidas"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Reproduzir mensagem de voz"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Visualizar contato <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Ligar para <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Detalhes de contato para <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> chamadas."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videochamada."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Enviar SMS para <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Nova mensagem de voz"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Iniciar pesquisa por voz"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Ligar para <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Desconhecido"</string>
- <string name="voicemail" msgid="3851469869202611441">"Correio de voz"</string>
- <string name="private_num" msgid="6374339738119166953">"Número privado"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefone público"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> seg"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> m <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> às <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Não é possível ligar para este número"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Para configurar o correio de voz, vá para Menu &gt; Configurações."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Para ligar para o correio de voz, primeiro desative o modo avião."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Carregando…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Carregando do cartão SIM..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Contatos do cartão SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Nenhum app de contatos disponível"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"A pesquisa por voz não está disponível"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Não é possível fazer uma chamada porque o app Telefone foi desativado."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Nenhum app está disponível para essa ação neste dispositivo"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Pesquisar contatos"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Adicionar número ou pesquisar contatos"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Seu histórico de chamadas está vazio"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Fazer uma chamada"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Você não tem chamadas perdidas."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Sua caixa de entrada de correio de voz está vazia."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Seu correio de voz está vazio."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Mostrar somente favoritos"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Histórico de chamadas"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arquivo do correio de voz"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Todas"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Perdidas"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Correio de voz"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Novo bloqueio simplificado"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Para aumentar sua proteção, é preciso alterar o modo como o bloqueio funciona no smartphone. Os números bloqueados agora impedirão chamadas e mensagens de texto, e será possível compartilhá-los com outros apps."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Permitir"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Bloquear <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Chamadas feitas a partir deste número serão bloqueadas. Correios de voz serão excluídos automaticamente."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Chamadas feitas a partir deste número serão bloqueadas, mas é possível que o autor ainda consiga deixar correios de voz para você."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Você não receberá mais chamadas ou mensagens de texto deste número."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOQUEAR"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Desbloquear <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DESBLOQUEAR"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Discagem rápida"</string>
- <string name="tab_history" msgid="2563144697322434940">"Histórico de chamadas"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Contatos"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Correio de voz"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Removido dos favoritos"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Desfazer"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Ligar para <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Criar novo contato"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Adicionar a um contato"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Enviar SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Fazer videochamada"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Bloquear número"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> novas chamadas perdidas"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Ainda não há ninguém na sua discagem rápida"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Adicionar favorito"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Você ainda não tem contatos"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Adicionar um contato"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Toque na imagem para ver todos os números ou toque e segure para reordenar"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Remover"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videochamada"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Enviar uma mensagem"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Detalhes da chamada"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Ligar para <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Chamada perdida de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Chamada atendida de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Correio de voz não lido de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Correio de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Chamada para <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"em <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"por <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"por <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"em <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, por <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> por <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Ligar"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Ligar para <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Videochamada <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Ouvir mensagem de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Reproduzir correio de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pausar correio de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Excluir correio de voz de <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> novos correios de voz</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> novos correios de voz</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Criar contato para <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Adicionar <xliff:g id="NAMEORNUMBER">^1</xliff:g> a um contato já existente"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Detalhes de chamada para <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Excluída do histórico de chamadas"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Hoje"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Ontem"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Antiga"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Lista de chamadas"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Ative o alto-falante."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Desative o alto-falante."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Reprodução mais rápida."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Reprodução mais lenta."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Inicie ou pause a reprodução."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Opções de exibição"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Sons e vibração"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Acessibilidade"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Toque do telefone"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Também vibrar para chamadas"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tons de teclado"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Duração do tom do teclado"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normais"</item>
- <item msgid="6177579030803486015">"Longos"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Respostas rápidas"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Chamadas"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Bloqueio de chamadas"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Bloqueio de chamadas temporiamente desativado"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"O bloqueio de chamadas foi desativado porque você entrou em contato com serviços de emergência usando este telefone nas últimas 48 horas. Ele será reativado automaticamente depois de um período de 48 horas."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importar números"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Anteriormente, você marcou alguns autores de chamada para serem enviados automaticamente para o correio de voz por meio de outros apps."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Ver números"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importar"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Falha na importação"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Falha ao arquivar correio de voz."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Desbloquear número"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Adicionar número"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Chamadas a partir destes números serão bloqueadas. Correios de voz serão excluídos automaticamente."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Chamadas feitas a partir destes números serão bloqueadas, mas é possível que eles ainda consigam deixar correios de voz para você."</string>
- <string name="block_list" msgid="7760188925338078011">"Números bloqueados"</string>
- <string name="invalidNumber" msgid="619058581062192851">"O número <xliff:g id="NUMBER">%1$s</xliff:g> é inválido."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"O número <xliff:g id="NUMBER">%1$s</xliff:g> já está bloqueado."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Bloqueio de chamadas desativado por 48 horas"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Desativado porque foi feita uma chamada de emergência."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Contas de chamadas"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Ativar"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Definir permissões"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Para ativar a discagem rápida, ative a permissão para o app Contatos."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Para ver seu registro de chamadas, ative a permissão para o app Telefone."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Para ver seus contatos, ative a permissão para o app Contatos."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Para acessar seu correio de voz, ative a permissão para o app Telefone."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Para pesquisar seus contatos, ative as permissões para \"Contatos\"."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Para fazer uma chamada, ative a permissão para o app Telefone."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"O app Telefone não tem permissão para gravar nas configurações do sistema."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Bloqueado"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"Chamada ativa: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Bloquear/denunciar spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Bloquear"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Não é spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Desbloquear"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Bloquear <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"As chamadas e correios de voz desse número serão bloqueados."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Denunciar chamada como spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"As chamadas e correios de voz desse número serão bloqueados. Essa chamada será denunciada como spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Desbloquear <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"O número será desbloqueado e marcado como não spam. As chamadas e correios de voz não serão marcados como spam."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Colocar <xliff:g id="NUMBER">%1$s</xliff:g> na lista de permissões?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Colocar na lista de permissões"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"As chamadas e correios de voz desse número não serão marcados como spam. O número será marcado como não sendo spam."</string>
-</resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
deleted file mode 100644
index 91f7b9bf9..000000000
--- a/res/values-ro/strings.xml
+++ /dev/null
@@ -1,290 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Tastatura numerică a telefonului"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Istoricul apelurilor"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Raportați numărul ca incorect"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Copiați numărul"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Copiați transcrierea"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blocați numărul"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> a fost blocat"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Deblocați numărul"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> a fost deblocat"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ANULAȚI"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Ștergeți"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Modificați numărul înainte de apelare"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Ștergeți istoricul apelurilor"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Ștergeți mesajul vocal"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arhivați mesajul vocal"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Distribuiți mesajul vocal"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Mesaj vocal șters"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Mesajul vocal este arhivat"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ANULAȚI"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ARHIVĂ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Ștergeți istoricul apelurilor?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Vor fi șterse toate apelurile din istoric"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Se șterge istoricul apelurilor…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Apel nepreluat"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Apel de serviciu nepreluat"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Apeluri nepreluate"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> (de) apeluri nepreluate"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Apelați înapoi"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Trimiteți mesaj"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="few"> <xliff:g id="COUNT">%1$d</xliff:g> mesaje vocale </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> de mesaje vocale </item>
- <item quantity="one">Mesaj vocal</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Redați"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Mesaj vocal nou de la <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Nu s-a putut reda mesageria vocală"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Se încarcă mesageria vocală…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Se arhivează mesajul vocal…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Nu s-a putut încărca mesageria vocală"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Numai apelurile cu mesaje vocale"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Numai apelurile primite"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Numai apelurile efectuate"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Numai apelurile nepreluate"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Mesagerie vocală vizuală"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Vedeți și ascultați mesageria vocală, fără a fi necesar să apelați un număr de telefon. Pot fi aplicate costuri pentru date."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Setări"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Nu sunt disponibile actualizări ale mesageriei vocale"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Mesaj vocal nou în așteptare. Nu poate fi încărcat acum."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Configurați mesageria vocală"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Conținutul audio nu este disponibil"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Configurați"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Apel. mesag. vocală"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Selectați numărul"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Selectați numărul"</string>
- <string name="make_primary" msgid="5829291915305113983">"Rețineți această alegere"</string>
- <string name="description_search_button" msgid="3660807558587384889">"căutare"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"apelați"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"numărul de apelat"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Porniți sau opriți redarea"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Activați sau dezactivați difuzorul"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Căutați poziția de redare"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Reduceți viteza redării"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Măriți viteza redării"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Istoricul apelurilor"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Mai multe opțiuni"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"tastatură numerică"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Numai apelurile efectuate"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Numai apelurile primite"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Numai apelurile nepreluate"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Afișați numai mesajele vocale"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Afișați toate apelurile"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Adăugați o pauză de 2 secunde"</string>
- <string name="add_wait" msgid="3360818652790319634">"Adăugați interval de așteptare"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Setări"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Intrare nouă în agendă"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Toată agenda"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Detaliile apelului"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Nu sunt disponibile detalii"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Tastatura tactilă cu sunet"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Reveniți la apelul în curs"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Adăugați un apel"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Apel de intrare"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Apel efectuat"</string>
- <string name="type_missed" msgid="2720502601640509542">"Apel nepreluat"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Apel video primit"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Apel video efectuat"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Apel video nepreluat"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Mesaj vocal"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Apel respins"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Apel blocat"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Apeluri de intrare"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Redați mesajul vocal"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Afișați persoana din agendă <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Apelați pe <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Detalii de contact pentru <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> (de) apeluri."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Apel video."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Trimiteți SMS la <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Mesaje vocale neascultate"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Începeți căutarea vocală"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Apelați <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Necunoscut"</string>
- <string name="voicemail" msgid="3851469869202611441">"Mesagerie vocală"</string>
- <string name="private_num" msgid="6374339738119166953">"Număr privat"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefon public"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> secunde"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min. <xliff:g id="SECONDS">%s</xliff:g> sec."</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> la <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Nu puteți apela acest număr"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Pentru a configura mesageria vocală, accesați Meniu &gt; Setări."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Pentru a apela mesageria vocală, mai întâi dezactivați modul Avion."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Se încarcă..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Se încarcă de pe cardul SIM..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Persoanele din agendă de pe cardul SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Nu este disponibilă nicio aplicație pentru agendă"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Căutarea vocală nu este disponibilă"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Nu se poate efectua un apel telefonic, deoarece aplicația Telefon a fost dezactivată."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Pe dispozitiv nu există nicio aplicație pentru această acțiune"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Căutați persoane de contact"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Adăugați număr/căutați contacte"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Istoricul apelurilor este gol"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Apelați"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Nu aveți niciun apel nepreluat."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Nu există mesaje primite în mesageria vocală."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Arhiva de mesaje vocale este goală."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Afișați numai preferate"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Istoricul apelurilor"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arhivă de mesaje vocale"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Toate"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Nepreluate"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Mesagerie vocală"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Mod de blocare nou, mai simplu"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Pentru a vă proteja mai bine, aplicația Telefon trebuie să schimbe modul în care funcționează blocarea. Se vor opri apelurile și mesajele text de la numerele blocate, iar la acestea pot avea acces și alte aplicații."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Permiteți"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Blocați <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Apelurile de la acest număr vor fi blocate, iar mesajele vocale vor fi șterse automat."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Apelurile de la acest număr vor fi blocate, dar apelantul va putea totuși să vă lase mesaje vocale."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Nu veți mai primi apeluri sau mesaje text de la acest număr."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOCAȚI"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Deblocați <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"DEBLOCAȚI"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Apelare rapidă"</string>
- <string name="tab_history" msgid="2563144697322434940">"Istoricul apelurilor"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Agendă"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Mesagerie vocală"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"S-a eliminat din preferate"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Anulați"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Apelați <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Creați o intrare nouă"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Adăugați la o intrare"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Trimiteți SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Inițiați un apel video"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blocați numărul"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> (de) apeluri nepreluate noi"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Nicio persoană de contact setată pentru apelarea rapidă"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Adăugați o persoană de contact preferată"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Nu aveți încă persoane de contact"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Adăugați o persoană de contact"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Atingeți imaginea pentru a vedea toate numerele sau atingeți lung pentru reordonare"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Eliminați"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Apel video"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Trimiteți un mesaj"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Detaliile apelului"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Apelați <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Apel nepreluat de la <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Apel preluat de la <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Mesaj vocal necitit de la <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Mesaj vocal de la <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Apel către <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"pe <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"prin <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"prin <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"în <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, prin <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> prin <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Apelați"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Apelați <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Apelați video <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Ascultați mesajul vocal de la <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Redați mesajul vocal de la <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Întrerupeți mesajul vocal de la <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Ștergeți mesajul vocal de la <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> mesaje vocale noi</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> de mesaje vocale noi</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> mesaj vocal nou</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Creați o persoană de contact pentru <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Adăugați <xliff:g id="NAMEORNUMBER">^1</xliff:g> la o persoană de contact existentă"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Detaliile apelului pentru <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"S-a șters din istoricul apelurilor"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Astăzi"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Ieri"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Mai vechi"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Lista de apeluri"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Porniți difuzorul."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Opriți difuzorul."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Jucați mai repede."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Jucați mai lent."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Porniți sau întrerupeți redarea."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Opțiuni de afișare"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Sunete și vibrații"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Accesibilitate"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Ton de apel al telefonului"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibrează și pentru apeluri"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tonuri pt. tastatura numerică"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Lungimea tonului tastaturii numerice"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normală"</item>
- <item msgid="6177579030803486015">"Lungă"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Răspunsuri rapide"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Apeluri"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Blocarea apelurilor"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Blocarea apelurilor e dezactivată temporar"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Blocarea apelurilor a fost dezactivată, deoarece ați contactat serviciile de urgență de pe acest telefon în ultimele 48 de ore. Funcția va fi reactivată automat după ce perioada de 48 de ore va expira."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importați numere"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Anterior, ați marcat câțiva apelanți pentru a fi redirecționați automat spre mesageria vocală prin alte aplicații."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Vedeți numerele"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importați"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Importul nu a reușit"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Mesajul vocal nu s-a arhivat."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Deblocați numărul"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Adăugați un număr"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Apelurile de la aceste numere vor fi blocate, iar mesajele vocale vor fi șterse automat."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Apelurile de la aceste numere vor fi blocate, dar apelanții vor putea totuși să vă lase mesaje vocale."</string>
- <string name="block_list" msgid="7760188925338078011">"Numere blocate"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> nu este valid."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> este deja blocat."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Blocarea apelurilor este dezactivată pentru 48 de ore"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Dezactivată din cauza efectuării unui apel de urgență."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Conturi pentru apelare"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Activați"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Setați permisiunile"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Pentru a activa apelarea rapidă, activați permisiunea Agendă."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Pentru a vedea jurnalul de apeluri, activați permisiunea Telefon."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Pentru a vedea persoanele de contact, activați permisiunea Agendă."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Pentru a accesa mesageria vocală, activați permisiunea Telefon."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Pentru a căuta în persoanele de contact, activați permisiunea Agendă."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Pentru a apela, activați permisiunea Telefon."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Aplicația Telefon nu are permisiunea să modifice setările de sistem."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Blocat"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> este activ"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blocați/raportați ca spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blocați"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Nu este spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Deblocați"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Blocați numărul <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Viitoarele apeluri și mesaje vocale primite de la acest număr vor fi blocate."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Raportați apelul ca spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Apelurile și mesajele vocale de la acest număr vor fi blocate. Apelul a fost raportat ca spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Deblocați numărul <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Acest număr va fi deblocat și raportat ca nefiind spam. Apelurile și mesajele vocale nu vor fi considerate spam."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Treceți în lista albă numărul <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Treceți în lista albă"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Apelurile și mesajele vocale de la acest număr nu vor fi considerate spam. Numărul va fi raportat ca nefiind spam."</string>
-</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
deleted file mode 100644
index 84923119a..000000000
--- a/res/values-ru/strings.xml
+++ /dev/null
@@ -1,292 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Телефон"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Телефон"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Панель набора номера"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Кнопки"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Журнал звонков"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Ошибка в номере"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Скопировать номер"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Скопировать транскрипцию"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Заблокировать номер"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Номер <xliff:g id="NUMBER">%1$s</xliff:g> заблокирован"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Разблокировать номер"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Номер <xliff:g id="NUMBER">%1$s</xliff:g> разблокирован"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ОТМЕНИТЬ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Удалить"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Изменить номер и позвонить"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Очистить журнал звонков"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Удалить голосовое сообщение"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Архивировать голос. сообщение"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Отправить голосовое сообщение"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Сообщение удалено"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Готово"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ОТМЕНИТЬ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"АРХИВ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Очистить журнал звонков?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Журнал звонков будет удален."</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Очистка журнала звонков…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Вызов"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Пропущенный вызов"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Пропущенный звонок (работа)"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Пропущенные вызовы"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"Пропущенных вызовов: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Перезвонить"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Написать SMS"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> голосовое сообщение </item>
- <item quantity="few"> <xliff:g id="COUNT">%1$d</xliff:g> голосовых сообщения </item>
- <item quantity="many"> <xliff:g id="COUNT">%1$d</xliff:g> голосовых сообщений </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> голосового сообщения </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Прослушать"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Новое гол. сообщение: <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Не удалось воспроизвести сообщения"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Загрузка сообщений…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Архивирование…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Не удалось загрузить голосовую почту"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Только звонки с голосовой почтой"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Только входящие звонки"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Только исходящие звонки"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Только пропущенные звонки"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Визуальная голосовая почта"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Просматривайте и прослушивайте голосовые сообщения без лишних звонков. Может взиматься плата за передачу данных."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Настройки"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Не удается загрузить данные голосовой почты"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Не удается загрузить новое голосовое сообщение"</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Настройте голосовую почту"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Аудио недоступно"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Настройка"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Получить почту"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Выбор номера"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Выбор номера"</string>
- <string name="make_primary" msgid="5829291915305113983">"Запомнить выбор"</string>
- <string name="description_search_button" msgid="3660807558587384889">"поиск"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"набор номера"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"набираемый номер"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Начать или остановить воспроизведение"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Включить или отключить громкую связь"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Выбрать позицию для воспроизведения"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Уменьшить скорость воспроизведения"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Увеличить скорость воспроизведения"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Журнал звонков"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Ещё"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"Панель набора номера"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Исходящие"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Входящие"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Пропущенные"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Показать голосовые сообщения"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Все вызовы"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Добавить двухсекундную паузу"</string>
- <string name="add_wait" msgid="3360818652790319634">"Добавить паузу"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Настройки"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Новый контакт"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Все контакты"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Вызов"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Данные недоступны"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Панель тонального набора"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Вернуться к текущему вызову"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Добавить вызов"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Входящий вызов"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Исходящий вызов"</string>
- <string name="type_missed" msgid="2720502601640509542">"Пропущенный вызов"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Входящий видеовызов"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Исходящий видеовызов"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Пропущенный видеовызов"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Голосовая почта"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Отклоненный вызов"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Заблокированный вызов"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Входящие вызовы"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Прослушать голосовую почту"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Просмотреть данные: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Позвонить: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Сведения о контакте <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"Вызовов: <xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Видеовстреча"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Отправить SMS пользователю <xliff:g id="NAME">%1$s</xliff:g>."</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Непрослушанные сообщения голосовой почты"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Начать голосовой поиск"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Позвонить: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Неизвестно"</string>
- <string name="voicemail" msgid="3851469869202611441">"Голосовая почта"</string>
- <string name="private_num" msgid="6374339738119166953">"Скрытый номер"</string>
- <string name="payphone" msgid="7726415831153618726">"Телефон-автомат"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> сек."</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> мин. <xliff:g id="SECONDS">%s</xliff:g> сек."</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> в <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"На этот номер нельзя позвонить."</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Чтобы настроить голосовую почту, выберите \"Меню &gt; Настройки\"."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Сначала отключите режим полета."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Загрузка..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Загрузка с SIM-карты…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"Контакты на SIM-карте"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Нет приложения для работы с контактами"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Голосовой поиск недоступен"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Нельзя совершать телефонные звонки, поскольку приложение \"Телефон\" отключено."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"На устройстве нет подходящего приложения"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Поиск в контактах"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Найдите контакт или введите номер"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"В журнале пока нет звонков"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Вызов"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Нет пропущенных звонков"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Нет входящих голосовых сообщений"</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Архив пуст."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Только часто используемые"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Журнал звонков"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Архив голосовой почты"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Все"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Пропущенные"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Г. почта"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Простая блокировка номеров"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Для более надежной защиты приложению \"Телефон\" требуется изменить настройки блокировки. С заблокированных номеров теперь не будут приниматься звонки и сообщения, а сами номера могут быть предоставлены другим приложениям."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Разрешить"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Заблокировать <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Вызовы с этого номера будут блокироваться, а голосовые сообщения – автоматически удаляться."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Вызовы с этого номера будут блокироваться, но абонент сможет оставлять вам голосовые сообщения."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Вызовы и сообщения с этого номера будут блокироваться."</string>
- <string name="block_number_ok" msgid="770551992296781873">"БЛОКИРОВАТЬ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Разблокировать <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"РАЗБЛОКИРОВАТЬ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Быстрый набор"</string>
- <string name="tab_history" msgid="2563144697322434940">"Журнал звонков"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Контакты"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Голосовая почта"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Контакт удален из избранных"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Отмена"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Позвонить: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Создать контакт"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Добавить к контакту"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Отправить SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Начать видеовстречу"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Заблокировать номер"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"Пропущенных вызовов: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Нет номеров для быстрого набора"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Настроить быстрый набор"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Нет контактов"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Добавить контакт"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Коснитесь изображения, чтобы увидеть все номера, или нажмите и удерживайте, чтобы изменить порядок"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Удалить"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Видеовстреча"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Отправить сообщение"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Сведения о вызове"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Позвонить: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Пропущен вызов от контакта <xliff:g id="NAMEORNUMBER">^1</xliff:g>. <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Принят вызов от контакта <xliff:g id="NAMEORNUMBER">^1</xliff:g>. <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Непрослушанное сообщение от контакта <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Голосовая почта от контакта <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Вызов контакту <xliff:g id="NAMEORNUMBER">^1</xliff:g>. <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"Номер: <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"Номер: <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>. Номер: <xliff:g id="NUMBER">%2$s</xliff:g>."</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>. Номер: <xliff:g id="NUMBER">%2$s</xliff:g>."</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Вызов"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Позвонить: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Видеовстреча: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Прослушать сообщение от контакта <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Прослушать голосовое сообщение от контакта <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Приостановить голосовое сообщение от контакта <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Удалить голосовое сообщение от контакта <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> новое голосовое сообщение</item>
- <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> новых голосовых сообщения</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> новых голосовых сообщений</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> нового голосового сообщения</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Добавить контакт: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Добавить \"<xliff:g id="NAMEORNUMBER">^1</xliff:g>\" к контакту"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> – сведения о вызове"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Вызов удален из журнала"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Сегодня"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Вчера"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Предыдущие записи"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Вызовы"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Включить динамик."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Выключить динамик."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Увеличить скорость воспроизведения."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Уменьшить скорость воспроизведения."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Начать или приостановить воспроизведение."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Отображение контактов"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Звуки и вибрация"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Специальные возможности"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Рингтон"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Вибросигнал при вызове"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Звук при наборе номера"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Длительность сигналов при наборе номера"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Обычная"</item>
- <item msgid="6177579030803486015">"Большая"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Быстрые ответы"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Вызовы"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Блокировка вызовов"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Блокировка вызовов временно отключена"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Блокировка вызовов отключена, поскольку вы недавно набирали номер экстренной службы. Когда с момента звонка пройдет 48 часов, эта функция включится автоматически."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Импортировать номера"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Вы настроили перенаправление вызовов от некоторых абонентов в голосовую почту в других приложениях."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Просмотреть номера"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Импортировать"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Ошибка импорта"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Не удалось архивировать сообщение."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Разблокировать номер"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Добавить номер"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Вызовы с этих номеров будут блокироваться, а голосовые сообщения – автоматически удаляться."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Вызовы с этих номеров будут блокироваться, но абоненты смогут оставлять вам голосовые сообщения."</string>
- <string name="block_list" msgid="7760188925338078011">"Заблокированные номера"</string>
- <string name="invalidNumber" msgid="619058581062192851">"Номер <xliff:g id="NUMBER">%1$s</xliff:g> недействителен."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> уже заблокирован."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Блокировка вызовов отключена на 48 часов"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Блокировка звонков отключена из-за экстренного вызова"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Аккаунты для звонков"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Включить"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Задать разрешения"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Чтобы настроить быстрый набор, предоставьте приложению разрешение \"Контакты\"."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Чтобы открыть список вызовов, предоставьте приложению разрешение \"Телефон\"."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Чтобы открыть список контактов, предоставьте приложению разрешение \"Контакты\"."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Чтобы получить доступ к голосовой почте, предоставьте приложению разрешение \"Телефон\"."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Для поиска контактов включите разрешение \"Контакты\"."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Чтобы позвонить, предоставьте приложению разрешение \"Телефон\"."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"У приложения \"Телефон\" нет разрешения на изменение системных настроек."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Заблокирован"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>: вызов активен"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Заблокировать/сообщить о спаме"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Заблокировать"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Не спам"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Разблокировать"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Спам"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Заблокировать номер <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Вызовы и голосовая почта с этого номера будут блокироваться."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Сообщить о вызове как о спаме"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Вызовы и голосовая почта с этого номера будут блокироваться. Вы сообщите об этом вызове как о спаме."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Разблокировать номер <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Этот номер будет разблокирован. Вы сообщите, что это не спам. Вызовы и голосовая почта не будут считаться спамом."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Добавить номер <xliff:g id="NUMBER">%1$s</xliff:g> в белый список?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Добавить в белый список"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Вы сообщите об этом номере, что это не спам. Вызовы и голосовая почта не будут считаться спамом."</string>
-</resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
deleted file mode 100644
index dc8688883..000000000
--- a/res/values-si/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"දුරකථනය"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"දුරකථනය"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"දුරකථන ඩයල්පෑඩය"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"දුරකථනය"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"ඇමතුම් ඉතිහාසය"</string>
- <string name="action_report_number" msgid="4635403959812186162">"වැරදි අංකය වාර්තා කරන්න"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"අංකය පිටපත් කරන්න"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"පිටපත් කිරීම පිටපත් කරන්න"</string>
- <string name="action_block_number" msgid="1482657602262262134">"අංකය අවහිර කරන්න"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> අවහිරයි"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"අංකය අවහිර නොකරන්න"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> අවහිර නැත"</string>
- <string name="block_number_undo" msgid="591338370336724156">"අස් කරන්න"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"මකන්න"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"ඇමතුමට පෙර අංකය සංස්කරණය"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"ඇමතුම් ඉතිහාසය හිස් කරන්න"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"හඬ තැපෑල මකන්න"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"හඬ තැපෑල සංරක්ෂණය කරන්න"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"හඬ තැපෑල බෙදා ගන්න"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"හඬ තැපෑල මකන ලදී"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"හඬ තැපෑල සංරක්ෂණය කරන ලදී"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"අස් කරන්න"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"සංරක්ෂිතය වෙත යන්න"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"ඇමතුම් ඉතිහාසය හිස් කරන්නද?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"මෙය ඔබේ ඉතිහාසයෙන් සියලු ඇමතුම් මකනු ඇත"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"ඇමතුම් ඉතිහාසය හිස් කරමින්…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"දුරකථනය"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"මඟ හැරුණු ඇමතුම"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"මග හැරුණ කාර්යාල ඇමතුම"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"මඟ හැරුණු ඇමතුම්"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"මඟ හැරුණු ඇමතුම් <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"ආපසු අමතන්න"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"පණිවිඩය"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one">හඬ තැපැල් <xliff:g id="COUNT">%1$d</xliff:g> </item>
- <item quantity="other">හඬ තැපැල් <xliff:g id="COUNT">%1$d</xliff:g> </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"ධාවනය කරන්න"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> වෙතින් නව හඬ තැපැලක්"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"හඬ තැපෑල වාදනය කිරීමට නොහැකි විය"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"හඬ තැපෑල ප්‍රවේශනය වෙමින්…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"හඬ තැපෑල සංරක්ෂණය කරමින්…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"හඬ තැපෑල ප්‍රවේශනය කිරීමට නොහැකි විය"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"හඬ තැපෑල සහිත ඇමතුම් පමණි"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"එන ඇමතුම් පමණි"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"පිටතට යන ඇමතුම් පමණි"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"මඟ හැරුණු ඇමතුම් පමණි"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"දෘශ්‍ය හඬ තැපෑල"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"අංකයකට ඇමතීමෙන් තොරව ඔබේ හඬ තැපෑල බලා සවන් දෙන්න. දත්ත ගාස්තු අදාළ විය හැකිය."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"සැකසීම්"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"හඬ තැපැල් යාවත්කාලීන ලබාගත නොහැකිය"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"නව හඬ තැපෑලක් පොරොත්තු වෙමින්. මේ දැන් ප්‍රවේශනය කිරීමට නොහැකිය."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"ඔබේ හඬ තැපෑල පිහිටුවන්න"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"ශ්‍රව්‍ය ලබාගත නොහැකිය"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"ස්ථාපනය කරන්න"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"හඬ තැපැල් අමතන්න"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"අංකය තෝරන්න"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"අංකය තෝරන්න"</string>
- <string name="make_primary" msgid="5829291915305113983">"මෙම තේරීම මතක තබාගන්න"</string>
- <string name="description_search_button" msgid="3660807558587384889">"සෙවීම"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"අමතන්න"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"ඇමතීමට අංකය"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Playback ධාවනය කරන්න හෝ නවත්වන්න"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"ස්පීකර්ෆෝන් ක්‍රියා කරන්න හෝ නොකරන්න"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Playback ස්ථානය සොයාබලන්න"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Playback අනුපාතය අඩුවේ"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Playback අනුපාතය වැඩිවේ"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"ඇමතුම් ඉතිහාසය"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"තවත් විකල්ප"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"ඩයල් පෑඩය"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"පිටතට යන ඒවා පමණක් පෙන්වන්න"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"එන ඒවා පමණක් පෙන්වන්න"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"මඟ හැරුණු ඒවා පමණක් පෙන්වන්න"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"හඬ තැපැල් පමණක් පෙන්වන්න"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"සියලු ඇමතුම් පෙන්වන්න"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"තත්පර 2 ක විරාමයක් එක් කරන්න"</string>
- <string name="add_wait" msgid="3360818652790319634">"රැඳී සිටීම එක් කරන්න"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"සැකසීම්"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"නව සම්බන්ධතාවයක්"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"සියලුම සම්බන්ධතා"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"ඇමතුම් විස්තර"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"විස්තර ලබාගත නොහැකිය"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"ස්පර්ශ නාද යතුරුපෑඩය භාවිතා කරන්න"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"පවතින ඇමතුමට නැවත යන්න"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"ඇමතුමක් එක් කරන්න"</string>
- <string name="type_incoming" msgid="6502076603836088532">"එන ඇමතුම"</string>
- <string name="type_outgoing" msgid="343108709599392641">"පිටතට යන ඇමතුම"</string>
- <string name="type_missed" msgid="2720502601640509542">"මඟ හැරුණු ඇමතුම"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"පැමිණෙන වීඩියෝ ඇමතුම"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"පිටවන වීඩියෝ ඇමතුම"</string>
- <string name="type_missed_video" msgid="954396897034220545">"මගහැරුණු වීඩියෝ ඇමතුම"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"හඬ තැපෑල"</string>
- <string name="type_rejected" msgid="7783201828312472691">"ප්‍රතික්ෂේප කළ ඇමතුම"</string>
- <string name="type_blocked" msgid="3521686227115330015">"අවහිර කළ ඇමතුම"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"එන ඇමතුම්"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"හඬ තැපෑල ධාවනය කිරීම"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> සම්බන්ධතාවය බලන්න"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g> අමතන්න"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> සඳහා ඇමතුම් විස්තර"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"ඇමතුම් <xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>."</string>
- <string name="description_video_call" msgid="2933838090743214204">"වීඩියෝ ඇමතුම."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g> වෙත SMS යවන්න"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"අසා නොමැති හඬ තැපෑල"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"හඬ සෙවීම අරඹන්න"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> අමතන්න"</string>
- <string name="unknown" msgid="740067747858270469">"නොදනී"</string>
- <string name="voicemail" msgid="3851469869202611441">"හඬ තැපෑල"</string>
- <string name="private_num" msgid="6374339738119166953">"පුද්ගලික අංකය"</string>
- <string name="payphone" msgid="7726415831153618726">"පේෆෝනය"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"තත් <xliff:g id="SECONDS">%s</xliff:g>"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"මිනි <xliff:g id="MINUTES">%s</xliff:g> තත් <xliff:g id="SECONDS">%s</xliff:g>"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> දින <xliff:g id="TIME">%2$s</xliff:g>ට"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"මෙම අංකයට ඇමතිය නොහැකිය"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"හඬ තැපෑල සකස් කර ගැනීමට, මෙනු &gt; සැකසීම් වෙත යන්න."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"හඬ තැපෑල ඇමතීමට, මුලින්ම ගුවන්යානා ආකාරය වසා දමන්න."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"පූරණය වෙමින්..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"SIM පතෙන් පූරණය කරමින්…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM පත් සම්බන්ධතා"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"සබඳතා යෙදුමක් ලබාගත නොහැකිය"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"හඬ සෙවුම ලබාගත නොහැකිය"</string>
- <string name="call_not_available" msgid="8941576511946492225">"දුරකථන යෙදුම අබල කර ඇති නිසා දුරකථන ඇමතුම ලබාගැනීම කළ නොහැක."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"මෙම උපාංගයෙහි ඒ සඳහා යෙදුමක් නැත"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"සම්බන්ධතා සෙවීම"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"අංකය එක් කරන්න හෝ සම්බන්ධතා සොයන්න"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"ඔබගේ ඇමතුම් ඉතිහාසය හිස්ය"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"ඇමතුමක් සිදු කරන්න"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"ඔබට මඟ හැරුණු ඇමතුම් නැත."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"ඔබේ හඬ තැපැල් එන ලිපි හිස්ය."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"ඔබේ හඬ තැපැල් සංරක්ෂිතය හිස්ය."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"ප්‍රියතමයන් පමණක් පෙන්වන්න"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"ඇමතුම් ඉතිහාසය"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"හඬ තැපෑල් සංරක්ෂිතය"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"සියලු"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"මග හැරුණේය"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"හඬ තැපෑල"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"නව, සරල අවහිර කිරීම"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"ඔබව වඩාත් හොඳින් ආරක්ෂා කිරීමට, දුරකථනයට අවහිර කිරීම ක්‍රියා කරන ආකාරය වෙනස් කිරීමට අවශ්‍යයි. ඔබේ අවහිර කළ අංක දැන් ඇමතුම් සහ පෙළ පණිවිඩ යන දෙකම නවත්වනු ඇති අතර වෙනත් යෙදුම් සමග බෙදා ගැනීමට හැකිය."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"ඉඩ දෙන්න"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> අවහිර කරන්නද?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"මෙම අංකය වෙතින් වන ඇමතුම් අවහිර කරනු ඇති අතර හඬ තැපැල් ස්වයංක්‍රියව මකනු ඇත."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"මෙම අංකය වෙතින් වන ඇමතුම් අවහිර කරනු ඇති නමුත්, අමතන්නාට තවම ඔබට හඬ තැපැල් තැබීමට හැකිය."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"ඔබට මෙම අංකයෙන් තවදුරටත් ඇමතුම් හෝ පෙළ පණිවිඩ නොලැබෙනු ඇත."</string>
- <string name="block_number_ok" msgid="770551992296781873">"අවහිරයි"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> අවහිර නොකරන්නද?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"අවහිර නොකරන්න"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"වේග ඩයල් කිරීම"</string>
- <string name="tab_history" msgid="2563144697322434940">"ඇමතුම් ඉතිහාසය"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"සම්බන්ධතා"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"හඬ තැපෑල"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"ප්‍රියතමයන්ගෙන් ඉවත් කරන්න"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"අස් කරන්න"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> අමතන්න"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"නව සම්බන්ධතාවයක් සාදන්න"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"සම්බන්ධතාවකට එක් කරන්න"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS යවන්න"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"වීඩියෝ ඇමතුමක් ලබාගන්න"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"අංකය අවහිර කරන්න"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"නව මඟ හැරුණු ඇමතුම් <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"ඔබගේ වේග ඩයල් එකේ තවමත් කවුරුවත් නැහැ"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"ප්‍රියතම ලෙස එක් කරන්න"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"ඔබ තවමත් සම්බන්ධතා නැහැ"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"සම්බන්ධතාවයක් එකතු කරන්න"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"සියලු අංක බැලීමට අනුරුව ස්පර්ශ කරන්න නැතහොත් &amp; යළි ඇණවුම් කිරීමට අල්ලාගෙන සිටින්න"</string>
- <string name="remove_contact" msgid="1080555335283662961">"ඉවත් කරන්න"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"වීඩියෝ ඇමතුම"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"පණිවිඩයක් යවන්න"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"ඇමතුම් විස්තර"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> අමතන්න"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> වෙතින් ඇමතුමක් මගහැරුණා."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> වෙතින් ඇමතුමකට පිළිතුරු දුන්නා."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> වෙතින් නොකියවූ හඬ තැපෑල."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> වෙතින් හඬ තැපෑල."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g> වෙත ඇමතුමක්."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> හි"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> හරහා"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> හරහා"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> මත, <xliff:g id="NUMBER">%2$s</xliff:g> හරහා"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="NUMBER">%2$s</xliff:g> හරහා <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"ඇමතුම"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> අමතන්න"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>. වීඩියෝ ඇමතුම"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> වෙතින් හඬ තැපෑලට සවන් දෙන්න"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> වෙතින් වන හඬ තැපෑල ධාවනය කරන්න"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> වෙතින් වන හඬ තැපෑල විරාම කරන්න"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> වෙතින් වන හඬ තැපෑල මකන්න"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one">නව හඬ තැපැල් <xliff:g id="COUNT_1">%d</xliff:g></item>
- <item quantity="other">නව හඬ තැපැල් <xliff:g id="COUNT_1">%d</xliff:g></item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> සඳහා සම්බන්ධතාවක් සාදන්න"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"පවතින සම්බන්ධතාව වෙත <xliff:g id="NAMEORNUMBER">^1</xliff:g> එක් කරන්න"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> සඳහා ඇමතුම් විස්තර"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"ඇමතුම් ඉතිහාසයෙන් මකන ලදී"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"අද"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"ඊයේ"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"වඩා පරණ"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"ඇමතුම් ලැයිස්තුව"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"ශබ්දවාහිනී සක්‍රිය කරන්න."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"ශබ්දවාහිනී අක්‍රිය කරන්න."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"වේගයෙන් ධාවනය කරන්න."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"හෙමින් ධාවනය කරන්න."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"නැවත ධාවනයෙදී ආරම්භ කරන්න හෝ විරාමය කරන්න."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"විකල්ප පෙන්වන්න"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"ශබ්ද සහ කම්පන"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"ප්‍රවේශ්‍යතාවය"</string>
- <string name="ringtone_title" msgid="760362035635084653">"දුරකථන රිගින්ටෝනය"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"ඇමතුම් සඳහාත් කම්පනය කරන්න"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"ඩයල්පෑඩ ස්පර්ශක හඬ"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"ඇමතුම් පෑඩයේ නාද දිග"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"සාමාන්‍ය"</item>
- <item msgid="6177579030803486015">"දීර්ඝ"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"ක්ෂණික ප්‍රතිචාර"</string>
- <string name="call_settings_label" msgid="313434211353070209">"ඇමතුම්"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"ඇමතුම් අවහිර කිරීම"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"ඇමතුම් අවහිර කිරීම තාවකාලිකව අක්‍රියයි"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"ඔබ පසුගිය පැය 48 ඇතුළත මෙම දුරකථනයෙන් හදිසි අවස්ථා සේවා ඇමතූ බැව්න් ඇමතුම් අවහිර කිරීම අබල කර ඇත. පැය 48ක කාල සීමාව ඉකුත් වූ විට එය ස්වයංක්‍රියව යළි සබල කෙරේ."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"අංක ආයාත කරන්න"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"ඔබ පෙරදී සමහර අමතන්නන් වෙනත් යෙදුම් හරහා ස්වයංක්‍රියව හඬ තැපෑල වෙත යැවීමට ලකුණු කරන ලදී."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"අංක බලන්න"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"ආයාත කරන්න"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"අයාත කිරීම අසාර්ථක විය"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"හඬ තැපෑල සංරක්ෂනය කිරීමට අසමත් විය."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"අංකය අවහිර නොකරන්න"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"අංකයක් එක් කරන්න"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"මෙම අංක වෙතින් වන ඇමතුම් අවහිර කරනු ඇති අතර හඬ තැපැල් ස්වයංක්‍රියව මකනු ඇත."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"මෙම අංක වෙතින් වන ඇමතුම් අවහිර කරනු ඇති නමුත්, ඔවුන්ට තවම ඔබට හඬ තැපැල් තැබීමට හැකිය."</string>
- <string name="block_list" msgid="7760188925338078011">"අවහිර කළ අංක"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> වලංගු නැත."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> දැනටමත් අවහිර කර ඇත."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"පැය 48ක් සඳහා ඇමතුම් අවහිර කිරීම අබල කරන ලදී"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"හදිසි ඇමතුමක් සිදු කළ නිසා අබල කරන ලදී."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"ගිණුම් ඇමතීම"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"ක්‍රියාත්මක කරන්න"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"අවසර සකසන්න"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"වේග ඩයල් කිරීම සබල කිරීමට, සම්බන්ධතා අවසරය ක්‍රියාත්මක කරන්න."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"ඔබේ ඇමතුම් ලොගය බැලීමට, දුරකථන අවසරය ක්‍රියාත්මක කරන්න."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"ඔබේ සම්බන්ධතා බැලීමට, සම්බන්ධතා අවසරය ක්‍රියාත්මක කරන්න."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"ඔබේ හඬ තැපෑල වෙත ප්‍රවේශ වීමට, දුරකථන අවසරය ක්‍රියාත්මක කරන්න."</string>
- <string name="permission_no_search" msgid="84152933267902056">"ඔබේ සම්බන්ධතා සෙවීමට, සම්බන්ධතා අවසර ක්‍රියාත්මක කරන්න."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"ඇමතුමක් ලබා ගැනීමට, දුරකථන අවසරය ක්‍රියාත්මක කරන්න."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"දුරකථන යෙදුමට පද්ධති සැකසීම් වෙත ලිවීමට අවසර නැත."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"අවහිර කරන ලදි"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> සක්‍රියයි"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"අයාචිත තැපැල අවහිර/වාර්තා කර."</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"අවහිර කරන්න"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"අයාචිත තැපෑලක් නොවේ"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"අවහිර කිරීමෙන් ඉවත් කරන්න"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"අයාචිත තැපෑල"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> අවහිර කරන්නද?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"මෙම අංකය වෙතින් වන අනාගත ඇමතුම් සහ හඬ තැපැල් අවහිර කරනු ඇත."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"ඇමතුම අයාචිත ලෙස වාර්තා කරන්න"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"මෙම අංකය වෙතින් වන අනාගත ඇමතුම් සහ හඬ තැපැල් අවහිර කරනු ඇත. මෙම ඇමතුම අයාචිත ලෙස වාර්තා කරනු ඇත."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> අවහිර කිරීමෙන් ඉවත් කරන්නද?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"මෙම අංකය අවහිර කිරීමෙන් ඉවත් කර අයාචිත නොවන ලෙස වාර්තා කරනු ඇත. අනාගත ඇමතුම් සහ හඬ තැපැල් අයාචිත ලෙස හඳුනා නොගනු ඇත."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"සාධු ලේඛන <xliff:g id="NUMBER">%1$s</xliff:g>ක්ද?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"සාධු ලේඛනය"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"මෙම අංකය වෙතින් වන අනාගත ඇමතුම් සහ හඬ තැපැල් අයාචිත ලෙස හඳුනා නොගනු ඇත. මෙම අංකය අයාචිත නොවන ලෙස වාර්තා කරනු ඇත."</string>
-</resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
deleted file mode 100644
index 0a52b43f3..000000000
--- a/res/values-sk/strings.xml
+++ /dev/null
@@ -1,292 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefón"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefón"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Číselník telefónu"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefón"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"História hovorov"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Nahlásiť nesprávne číslo"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopírovať číslo"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopírovať prepis"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blokovať číslo"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> – blokované"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Zrušiť blokovanie čísla"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> – odblokované"</string>
- <string name="block_number_undo" msgid="591338370336724156">"SPÄŤ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Odstrániť"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Pred volaním upraviť číslo"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Vymazať históriu hovorov"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Odstrániť hlasovú správu"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Archivovať hlasovú správu"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Zdieľať hlasovú správu"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Hlas. schránka odstránená"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Hlasová správa bola archiv."</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"SPÄŤ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ARCHÍV"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Chcete vymazať históriu hovorov?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Týmto z histórie odstránite všetky hovory."</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Vymazáva sa história hovorov..."</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefón"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Zmeškaný hovor"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Zmeškaný pracovný hovor"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Zmeškané hovory"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"Zmeškané hovory: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Zavolať späť"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Správa"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="few"> <xliff:g id="COUNT">%1$d</xliff:g> odkazy v hlasovej schránke </item>
- <item quantity="many"> <xliff:g id="COUNT">%1$d</xliff:g> odkazu v hlasovej schránke </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> odkazov v hlasovej schránke </item>
- <item quantity="one">Odkaz v hlasovej schránke</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Prehrať"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nová hlasová správa – <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Hlasovú schránku sa nepodarilo prehrať"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Načítava sa hlasová schránka…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Archivuje sa hlasová správa…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Hlasovú schránku sa nepodarilo načítať"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Iba volania do hlasovej schránky"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Iba prichádzajúce hovory"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Iba odchádzajúce hovory"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Iba zmeškané hovory"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Vizuálna hlasová schránka"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Zobrazte a vypočujte si svoju hlasovú schránku bez toho, aby ste museli vytáčať číslo. Môžu vám byť účtované poplatky za prenos dát."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Nastavenia"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Aktualizácie hlasovej schránky nie sú k dispozícii"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Nevypočuté hlasové schránky. Momentálne ich nemožno načítať."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Nastavte si hlasovú schránku"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Zvuk nie je k dispozícii"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Nastavenie"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Volať hlas. schránku"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Zvoľte číslo"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Zvoľte číslo"</string>
- <string name="make_primary" msgid="5829291915305113983">"Zapamätať si túto voľbu"</string>
- <string name="description_search_button" msgid="3660807558587384889">"hľadať"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"vytáčanie"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"číslo, ktoré chcete vytočiť"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Spustiť alebo zastaviť prehrávanie"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Zapnúť alebo vypnúť reproduktor"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Hľadať pozíciu prehrávania"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Znížiť počet snímok za sekundu"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Zvýšiť počet snímok za sekundu"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"História hovorov"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Ďalšie možnosti"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"číselná klávesnica"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Zobraziť len odchádzajúce"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Zobraziť len prichádzajúce"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Zobraziť len zmeškané"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Zobraziť len hlasové správy"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Zobraziť všetky hovory"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Pridať dvojsekundovú pauzu"</string>
- <string name="add_wait" msgid="3360818652790319634">"Pridať čakanie"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Nastavenia"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Nový kontakt"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Všetky kontakty"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Podrobnosti hovoru"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Podrobnosti nie sú k dispozícii"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Použiť dotykovú tónovú klávesnicu"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Návrat k prebiehajúcemu hovoru"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Pridať hovor"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Prichádzajúci hovor"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Odchádzajúce volanie"</string>
- <string name="type_missed" msgid="2720502601640509542">"Zmeškaný hovor"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Prichádzajúci videohovor"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Odchádzajúci videohovor"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Zmeškaný videohovor"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Hlasová schránka"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Odmietnutý hovor"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blokovaný hovor"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Prichádzajúce hovory"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Prehrať hlasovú správu"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Zobraziť kontakt <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Volať kontakt <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Podrobnosti kontaktu pre <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"Počet volaní: <xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videohovor"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Poslať SMS kontaktu <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Nevypočutá hlasová správa"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Spustenie hlasového vyhľadávania"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Volať <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Neznáme"</string>
- <string name="voicemail" msgid="3851469869202611441">"Hlasová schránka"</string>
- <string name="private_num" msgid="6374339738119166953">"Súkromné číslo"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefónny automat"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min. <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> o <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Na toto číslo nie je možné volať"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Ak chcete nastaviť hlasovú schránku, prejdite na položku Menu &gt; Nastavenia."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Ak chcete volať hlasovú schránku, najprv vypnite režim v lietadle."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Načítava sa…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Prebieha načítavanie z SIM karty..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Kontakty na SIM karte"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Nie je k dispozícii žiadna aplikácia na kontakty"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Hlasové vyhľadávanie nie je k dispozícii"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Nie je možné volať, pretože aplikácia Telefón bola deaktivovaná."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Na tomto zariadení nie je aplikácia na vykonanie danej akcie"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Hľadať kontakty"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Pridajte číslo / vyhľadajte v kontaktoch"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"História hovorov je prázdna"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Zavolať"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Nemáte žiadne zmeškané hovory."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Hlasová schránka je prázdna."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Archív vašich hlasových správ je prázdny."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Zobraziť iba obľúbené"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"História hovorov"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Archív hlasových správ"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Všetky"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Zmeškané"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Schránka"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Nové zjednodušené blokovanie"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"V telefóne je potrebné zmeniť spôsob fungovania blokovania. Len tak dosiahnete lepšiu ochranu. Blokované čísla budú brániť prijímaniu hovorov aj textových správ a bude ich možné zdieľať s ďalšími aplikáciami."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Povoliť"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Blokovať číslo <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Hovory z tohto čísla budú blokované a hlasové správy budú automaticky odstraňované."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Hovory z tohto čísla budú zablokované, ale volajúci vám môže zanechať hlasovú správu."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Z tohto čísla už nebudete prijímať hovory ani textové správy."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOKOVAŤ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Odblokovať číslo <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ODBLOKOVAŤ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Rýchla voľba"</string>
- <string name="tab_history" msgid="2563144697322434940">"História hovorov"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontakty"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Hlasová schránka"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Odstránené z obľúbených"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Späť"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Volať <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Vytvoriť nový kontakt"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Pridať ku kontaktu"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Odoslať SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Uskutočniť videohovor"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blokovať číslo"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"Počet nových zmeškaných hovorov: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"V rýchlom vytáčaní zatiaľ nemáte žiadny kontakt"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Pridať obľúbený kontakt"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Zatiaľ nemáte žiadne kontakty"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Pridať kontakt"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Klepnutím na obrázok si môžete zobraziť všetky čísla. Ak naň klepnete a podržíte ho, môžete zmeniť ich poradie."</string>
- <string name="remove_contact" msgid="1080555335283662961">"Odstrániť"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videohovor"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Odoslať správu"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Podrobnosti hovoru"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Zavolať kontakt <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Zmeškaný hovor – kontakt <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Prijatý hovor – kontakt <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Neprečítaná správa v hlasovej schránke od <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Správa v hlasovej schránke od <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Volanie – kontakt <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"v rámci účtu <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"prostredníctvom čísla <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"prostredníctvom čísla <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"v účte <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, prostredníctvom čísla <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> prostredníctvom čísla <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Volať"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Zavolať kontakt <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Spustiť videohovor s kontaktom <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Vypočuť si správu v hlasovej schránke od používateľa <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Prehrať hlasovú schránku kontaktu <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pozastaviť hlasovú schránku kontaktu <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Odstrániť hlasovú schránku kontaktu <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> nové odkazy v hlasovej schránke</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> nového odkazu v hlasovej schránke</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> nových odkazov v hlasovej schránke</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> nový odkaz v hlasovej schránke</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Vytvoriť kontakt pre <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Pridať údaj <xliff:g id="NAMEORNUMBER">^1</xliff:g> ku kontaktu"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Podrobnosti hovoru pre kontakt <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Odstránené z histórie hovorov"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Dnes"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Včera"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Staršie"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Zoznam hovorov"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Zapnúť reproduktor"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Vypnúť reproduktor"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Prehrať rýchlejšie"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Prehrať pomalšie"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Spustiť alebo pozastaviť prehrávanie"</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Možnosti zobrazenia"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Zvuky a vibrovanie"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Dostupnosť"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Tón zvonenia telefónu"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Pri hovoroch aj vibrovať"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tóny číselnej klávesnice"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Dĺžka zvukov číselnej klávesnice"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normálne"</item>
- <item msgid="6177579030803486015">"Dlhé"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Rýchle odpovede"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Hovory"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Blokovanie hovorov"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Blokovanie hovorov je dočasne vypnuté"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Pretože ste z tohto telefónu počas posledných 48 hodín volali na tiesňovú linku, blokovanie hovorov bolo vypnuté. Po uplynutí 48 hodín sa automaticky znova zapne."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importovať čísla"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"V minulosti ste niektorých volajúcich označili, aby boli automaticky prepojení do hlasovej schránky prostredníctvom ďalších aplikácií."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Zobraziť čísla"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importovať"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Import zlyhal"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Hlasovú správu nebolo možné archivovať"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Zrušiť blokovanie čísla"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Pridať číslo"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Hovory z týchto čísel budú blokované a hlasové správy budú automaticky odstraňované."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Hovory z týchto čísel budú blokované, ale volajúci vám budú stále môcť zanechať hlasové správy."</string>
- <string name="block_list" msgid="7760188925338078011">"Blokované čísla"</string>
- <string name="invalidNumber" msgid="619058581062192851">"Číslo <xliff:g id="NUMBER">%1$s</xliff:g> je neplatné."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"Číslo <xliff:g id="NUMBER">%1$s</xliff:g> je už blokované."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Blokovanie hovorov je deaktivované na 48 hodín"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Deaktivované, pretože ste uskutočnili tiesňové volanie"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Telefónne účty"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Zapnúť"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Nastaviť povolenia"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Ak chcete aktivovať rýchle vytáčanie, zapnite povolenie Kontakty."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Ak si chcete zobraziť denník hovorov, zapnite povolenie Telefón."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Ak si chcete zobraziť kontakty, zapnite povolenie Kontakty."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Ak chcete používať hlasovú schránku, zapnite povolenie Telefón."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Ak chcete hľadať kontakty, zapnite povolenie Kontakty."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Ak chcete volať, zapnite povolenie Telefón."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Telefónna aplikácia nemá povolenie na zápis do nastavení systému."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Zablokované"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> - hovor je aktívny"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blokovať / nahlásiť spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blokovať"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Toto nie je spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Odblokovať"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Blokovať číslo <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Budúce hovory a hlasové správy z tohto čísla budú blokované."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Nahlásiť hovor ako spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Budúce hovory a hlasové správy z tohto čísla budú blokované. Tento hovor bude nahlásený ako spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Odblokovať číslo <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Číslo bude odblokované a nahlásené, že nie je spam. Budúce hovory a hlasové správy nebudú identifikované ako spam."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Pridať číslo <xliff:g id="NUMBER">%1$s</xliff:g> na bielu listinu?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Biela listina"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Budúce hovory a hlasové správy z tohto čísla nebudú identifikované ako spam. Číslo bude nahlásené, že nie je spam."</string>
-</resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
deleted file mode 100644
index fbee52f5e..000000000
--- a/res/values-sl/strings.xml
+++ /dev/null
@@ -1,292 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Številčnica telefona"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Zgodovina klicev"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Prijavi netočno številko"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopiranje številke"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopiraj prepis"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blokiranje številke"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Številka <xliff:g id="NUMBER">%1$s</xliff:g> je blokirana"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Odblokiranje telefonske številke"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Številka <xliff:g id="NUMBER">%1$s</xliff:g> je odblokirana"</string>
- <string name="block_number_undo" msgid="591338370336724156">"RAZVELJAVI"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Izbriši"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Urejanje številke pred klicem"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Izbriši zgodovino klicev"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Brisanje sporočil iz odzivnika"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arhiviraj sporoč. v odzivniku"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Skupna raba spor. v odzivniku"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Spor. v odziv. izbr."</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Sporoč. v odzivniku arhivirano"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"RAZVELJAVI"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"V ARHIV"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Želite izbrisati zgodovino klicev?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"S tem boste iz zgodovine izbrisali vse klice"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Izbris zgodovine klicev …"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefoniranje"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Neodgovorjeni klic"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Neodgovorjen delovni klic"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Neodgovorjeni klici"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"Št. neodgovorjenih klicev: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Povratni klic"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"SMS"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> sporočilo v odzivniku </item>
- <item quantity="two"> <xliff:g id="COUNT">%1$d</xliff:g> sporočili v odzivniku </item>
- <item quantity="few"> <xliff:g id="COUNT">%1$d</xliff:g> sporočila v odzivniku </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> sporočil v odzivniku </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Predvajaj"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g> , <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nova glasovna pošta od <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Sporočil v odzivniku ni mogoče predv."</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Nalaganje sporočil v odzivniku …"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Arhiviranje sporočila v odzivniku …"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Sporočil v odzivniku ni mogoče naložiti"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Samo klici z odzivnikom"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Samo dohodni klici"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Samo odhodni klici"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Samo neodgovorjeni klici"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Vizualno sporočilo v odzivniku"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Ogledujte si in poslušajte sporočila v odzivniku, ne da bi vam bilo treba klicati številko. Morda boste morali plačati stroške prenosa podatkov."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Nastavitve"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Obvestila odzivnika niso na voljo"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"V odzivniku čaka novo sporočilo. Trenutno ga ni mogoče naložiti."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Nastavite odzivnik"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Zvok ni na voljo"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Nastavite odzivnik"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Klicanje glasovne pošte"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Izberite številko"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Izberite številko"</string>
- <string name="make_primary" msgid="5829291915305113983">"Zapomni si to izbiro"</string>
- <string name="description_search_button" msgid="3660807558587384889">"iskanje"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"pokliči"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"številka, ki bo poklicana"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Začetek ali konec predvajanja"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Vklop ali izklop zvočnika"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Iskanje položaja predvajanja"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Zmanjšanje hitrosti predvajanja"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Povečanje hitrosti predvajanja"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Zgodovina klicev"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Več možnosti"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"številčnica"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Pokaži samo odhodne"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Pokaži samo dohodne"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Pokaži samo neodgovorjene"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Pokaži samo spor. glasovne pošte"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Pokaži vse klice"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Dodaj 2 sekundi premora"</string>
- <string name="add_wait" msgid="3360818652790319634">"Dodaj čakanje"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Nastavitve"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Nov stik"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Vsi stiki"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Podrobnosti klica"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Podrobnosti niso na voljo"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Uporabi številčnico za tonsko klicanje"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Nazaj na klic, ki poteka"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Dodaj klic"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Dohodni klic"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Odhodni klic"</string>
- <string name="type_missed" msgid="2720502601640509542">"Neodgovorjeni klic"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Dohodni videoklic"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Odhodni videoklic"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Zamudili ste videoklic"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Odzivnik"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Zavrnjen klic"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blokiran klic"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Dohodni klici"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Predvajanje sporočil glasovne pošte"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Ogled stika <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Klicanje osebe <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Podrobnosti stika za <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"Št. klicev: <xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videoklic."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Pošlji SMS prejemniku <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Neodprta glasovna pošta"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Začni glasovno iskanje"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Pokliči <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Neznano"</string>
- <string name="voicemail" msgid="3851469869202611441">"Glasovna pošta"</string>
- <string name="private_num" msgid="6374339738119166953">"Zasebna številka"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefonska govorilnica"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> ob <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Te številke ni mogoče klicati"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Če želite nastaviti odzivnik, odprite Meni &gt; Nastavitve."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Če želite poklicati odzivnik, najprej izklopite način za letalo."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Nalaganje …"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Nalaganje s kartice SIM ..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Stiki na kartici SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Ni aplikacije za stike"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Glasovno iskanje ni na voljo"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Ni mogoče opraviti telefonskega klica, ker je aplikacija Telefon onemogočena."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"V tej napravi ni aplikacije za to"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Iskanje stikov"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Dodajte št. ali iščite med st."</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Zgodovina klicev je prazna"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Klicanje"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Nimate neodgovorjenih klicev."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Vaš nabiralnik s sporočili v odzivniku je prazen."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Arhiv s sporočili v odzivniku je prazen."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Pokaži samo priljubljene"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Zgodovina klicev"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arhiv s sporočili v odzivniku"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Vsi"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Neodgovorjeni"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Odzivnik"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Novo poenostavljeno blokiranje"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Za večjo varnost je treba v aplikaciji Telefon spremeniti nastavitev načina blokiranja. Tako z blokiranih številk ne boste več prejemali klicev in sporočil, poleg tega pa lahko take številke posredujete tudi v druge aplikacije."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Dovoli"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Želite blokirati številko <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Klici s te številke bodo blokirani in sporočila v odzivniku bodo samodejno izbrisana."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Klici s te številke bodo blokirani, klicatelj pa bo morda še vedno lahko pustil sporočila v odzivniku."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"S te številke ne boste več prejemali klicev ali sporočil SMS."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOKIRAJ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Želite odblokirati številko <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ODBLOKIRAJ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Hitro izbiranje"</string>
- <string name="tab_history" msgid="2563144697322434940">"Zgodovina klicev"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Stiki"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Odzivnik"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Odstranjeno iz priljubljenih"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Razveljavi"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Pokliči <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Ustvari nov stik"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Dodaj stiku"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Pošlji SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Opravi videoklic"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blokiraj številko"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"Št. novih zgrešenih klicev: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Na seznamu za hitro klicanje nimate še nikogar"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Dodajte priljubljenega"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Nimate še stikov"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Dodajte stik"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Dotaknite se slike, če želite videti vse številke, ali pa se je dotaknite in pridržite, če želite spremeniti vrstni red."</string>
- <string name="remove_contact" msgid="1080555335283662961">"Odstrani"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videoklic"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Pošljite sporočilo"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Podrobnosti klica"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Kliči osebo/številko <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Neodgovorjen klic od: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Odgovorjen klic od: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Neprebrano sporočilo v odzivniku za račun <xliff:g id="PHONEACCOUNT">^4</xliff:g> ob <xliff:g id="TIMEOFCALL">^3</xliff:g> od: <xliff:g id="NAMEORNUMBER">^1</xliff:g> – <xliff:g id="TYPEORLOCATION">^2</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Sporočilo v odzivniku za račun <xliff:g id="PHONEACCOUNT">^4</xliff:g> ob <xliff:g id="TIMEOFCALL">^3</xliff:g> od: <xliff:g id="NAMEORNUMBER">^1</xliff:g> – <xliff:g id="TYPEORLOCATION">^2</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Klic za: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"z računom: <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"prek številke <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"prek številke <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"v računu <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, prek številke <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> prek številke <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Klic"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Klicanje: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Videoklic: <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Poslušajte sporočilo v odzivniku od: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Predvajanje sporočil v odzivniku od osebe/številke <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Zaustavitev prejemanja sporočil v odzivnik od osebe/številke <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Brisanje sporočil iz odzivnika od osebe/številke <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> sporočilo v odzivniku</item>
- <item quantity="two"><xliff:g id="COUNT_1">%d</xliff:g> sporočili v odzivniku</item>
- <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> sporočila v odzivniku</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> sporočil v odzivniku</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Ustvarjanja stika za: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Dodajanje tega obstoječemu stiku: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Podrobnosti klica za: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Izbrisano iz zgodovine klicev"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Danes"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Včeraj"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Starejši"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Seznam klicev"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Vklopi zvočnik."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Izklopi zvočnik."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Predvajaj hitreje."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Predvajaj počasneje."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Začni ali zaustavi predvajanje."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Možnosti prikaza"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Zvoki in vibriranje"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Funkcije za ljudi s posebnimi potrebami"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Ton zvonjenja telefona"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibriranje tudi za klice"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Toni tipkovnice"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Dolžina tonov tipk"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Običajno"</item>
- <item msgid="6177579030803486015">"Dolgo"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Hitri odgovori"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Klici"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Blokiranje klicev"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Blokiranje klicev je začasno izklopljeno"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Blokiranje klicev je onemogočeno, ker ste se v zadnjih 48 urah s tem telefonom obrnili na nujno pomoč. Ko 48-urno obdobje poteče, bo blokiranje klicev samodejno znova omogočeno."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Uvoz številk"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Označili ste, naj nekatere klicatelje druge aplikacije samodejno preusmerijo v odzivnik."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Ogled številk"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Uvozi"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Uvoz ni uspel"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Arhiviranje spor. v odzivniku ni uspelo."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Odblokiranje telefonske številke"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Dodaj telefonsko številko"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Klici s teh številk bodo blokirani in sporočila v odzivniku bodo samodejno izbrisana."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Klici s teh številk bodo blokirani, klicatelji pa bodo morda še vedno lahko pustil sporočila v odzivniku."</string>
- <string name="block_list" msgid="7760188925338078011">"Blokirane številke"</string>
- <string name="invalidNumber" msgid="619058581062192851">"Telefonska številka <xliff:g id="NUMBER">%1$s</xliff:g> je neveljavna."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"Telefonska številka <xliff:g id="NUMBER">%1$s</xliff:g> je že blokirana."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Blokiranje klicev je onemogočeno za 48 ur"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Onemogočeno zaradi opravljenega klica v sili"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Računi za klicanje"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Vklop"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Nastavi dovoljenja"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Če želite omogočiti hitro klicanje, vklopite dovoljenje za stike."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Če si želite ogledati dnevnik klicev, vklopite dovoljenje za telefon."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Če si želite ogledati stike, vklopite dovoljenje za stike."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Če želite dostopati do sporočil v odzivniku, vklopite dovoljenje za telefon."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Če želite iskati po stikih, vklopite dovoljenje za stike."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Če želite klicati, vklopite dovoljenje za telefon."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Aplikacija Telefon nima dovoljenja za pisanje v sistemske nastavitve."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Blokirano"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"Oseba/številka <xliff:g id="NAMEORNUMBER">^1</xliff:g> je aktivna"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blokiraj/prij. než. vsebino"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blokiraj"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Ni neželena vsebina"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Odblokiraj"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Neželena vsebina"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Želite blokirati številko <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Prihodnji klici in sporočila v odzivniku s te številke bodo blokirani."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Prijavi kot neželeni klic"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Prihodnji klici/sporočila v odzivniku s te številke bodo blokirani. Klic bo prijavljen kot neželen."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Želite odblokirati številko <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Ta številka bo odblokirana in zanjo bo prijavljeno, da ni neželena. Prihodnji klici/sporočila v odzivniku ne bodo obravnavani kot neželeni."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Želite na seznam dovoljenih dodati številko <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Seznam dovoljenih"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Prihodnji klici/sporočila v odzivniku s te številke ne bodo obravnavani kot neželeni. Za številko bo prijavljeno, da ni neželena."</string>
-</resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
deleted file mode 100644
index 9c2a9de78..000000000
--- a/res/values-sq/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefoni"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefoni"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Blloku i formimit të numrave i telefonit"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefoni"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Historiku i telefonatave"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Raporto numrin e pasaktë"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopjo numrin"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopjo transkriptimin"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blloko numrin"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> u bllokua"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Zhblloko numrin"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> u zhbllokua"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ZHBËJ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Fshi"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Redakto numrin para telefonatës"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Pastro historikun e telefonatave"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Fshije postën zanore"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arkivo postën zanore"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Shpërndaje postën zanore"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"U zbulua posta zanore"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Posta zanore u arkivua"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ZHBËJ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"SHKO TEK ARKIVI"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Të pastrohet historiku i telefonatave?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Kjo do të fshijë të gjitha telefonatat nga historiku yt"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Po pastron historikun e telefonatave…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefoni"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Telefonatë e humbur"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Telefonatë pune e humbur"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Telefonata të humbura"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> telefonata të humbura"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Ri-telefono"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Mesazh"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> posta zanore </item>
- <item quantity="one"> postë zanore</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Luaj"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Postë e re zanore nga <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Posta zanore nuk mund të luhej"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Po ngarkon postën zanore…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Po arkivon postën zanore..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Posta zanore nuk mund të ngarkohej"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Vetëm telefonatat me mesazhe zanore"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Vetëm telefonatat hyrëse"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Vetëm telefonatat dalëse"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Vetëm telefonatat e humbura"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Posta zanore vizuale"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Shiko dhe dëgjo postën zanore, pa pasur nevojë të telefonosh një numër. mund të zbatohen tarifa për të dhënat."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Cilësimet"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Përditësimet e postës zanore nuk mundësohen"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Posta të reja zanore janë në pritje. Nuk mund të ngarkohen tani."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Konfiguro postën zanore"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audioja nuk mundësohet"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Konfiguro"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Telefono postën zanore"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Zgjidh një numër"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Zgjidh një numër"</string>
- <string name="make_primary" msgid="5829291915305113983">"Kujtoje këtë zgjedhje"</string>
- <string name="description_search_button" msgid="3660807558587384889">"kërko"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"formo numrin"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"numri për t\'u formuar"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Luaj ose ndalo luajtjen"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Aktivizo ose çaktivizo altoparlantin"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Kërko pozicionin e luajtjes"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Ule shpejtësinë e luajtjes"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Rrite shpejtësinë e luajtjes"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Historiku i telefonatave"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Opsione të tjera"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"blloku i tasteve"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Shfaq vetëm dalëset"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Shfaq vetëm hyrëset"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Shfaq vetëm të humburat"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Shfaq vetëm postën zanore"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Shfaqi të gjitha telefonatat"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Shto një ndërprerje 2-sekondëshe"</string>
- <string name="add_wait" msgid="3360818652790319634">"Shto një pritje"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Cilësimet"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Kontakt i ri"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Të gjitha kontaktet"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Detajet e telefonatës"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Detajet nuk mundësohen"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Përdor bllokun e tasteve"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Kthehu te telefonata"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Shto një telefonatë"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Telefonatë hyrëse"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Telefonatë dalëse"</string>
- <string name="type_missed" msgid="2720502601640509542">"Telefonatë e humbur"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Telefonatë hyrëse me video"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Telefonatë dalëse me video"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Telefonatë e humbur me video"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Posta zanore"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Telefonatë e refuzuar"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Telefonatë e bllokuar"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Telefonatat hyrëse"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Luaj postën zanore"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Shiko kontaktin <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Telefono <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Detajet e kontaktit për <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> telefonata."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Telefonatë me video."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Dërgo SMS te <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Postë zanore e padëgjuar"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Nis kërkimin me zë"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Telefono <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Të panjohur"</string>
- <string name="voicemail" msgid="3851469869202611441">"Posta zanore"</string>
- <string name="private_num" msgid="6374339738119166953">"Numër privat"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefon me pagesë"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> sekonda"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min. e <xliff:g id="SECONDS">%s</xliff:g> sek."</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> në <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Ky numër nuk mund të telefonohet"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Për të konfiguruar postën zanore, shko te \"Menyja\" &gt; \"Cilësimet\"."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Për të shtuar një postë zanore, në fillim çaktivizo modalitetin \"në aeroplan\"."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Po ngarkon..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Po ngarkon nga karta SIM…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"Kontaktet e kartës SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Nuk mund të përdoret asnjë aplikacion për kontaktet"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Kërkimi me zë nuk mundësohet"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Telefonata nuk mund të bëhet sepse aplikacioni \"Telefoni\" është i çaktivizuar."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Nuk ka aplikacion për atë në këtë pajisje"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Kërko kontakte"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Shto një numër ose kërko te kontaktet"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Historiku i telefonatave është bosh"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Bëj një telefonatë"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Nuk ke thirrje të humbura."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Kutia hyrëse e postës tënde zanore është bosh."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Arkivi i postës tënde zanore është bosh."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Shfaq vetëm të preferuarat"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Historiku i telefonatave"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arkivi i postës zanore"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Të gjitha"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Të humbura"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Posta zanore"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Bllokim i ri i thjeshtëzuar"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Për të të mbrojtur më mirë, Telefoni ka nevojë të ndryshojë mënyrën se si funksionon bllokimi. Numrat e bllokuar tani do të ndalojnë si telefonatat ashtu edhe mesazhet me tekst dhe mund të ndahen me aplikacione të tjera."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Lejo"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Blloko <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Telefonatat nga ky numër do të bllokohen dhe mesazhet zanore do të fshihen automatikisht."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Telefonatat nga ky numër do të bllokohen, por telefonuesi mund të jetë ende në gjendje të lërë mesazhe zanore."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Nuk do të marrësh më telefonata apo mesazhe me tekst nga ky numër."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLLOKO"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Zhblloko <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ZHBLLOKO"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Telefonatë e shpejtë"</string>
- <string name="tab_history" msgid="2563144697322434940">"Historiku i telefonatave"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontaktet"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Posta zanore"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"U hoq nga të preferuarat"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Zhbëj"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Telefono <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Krijo një kontakt të ri"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Shto te një kontakt"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Dërgo SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Bëj një telefonatë me video"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blloko numrin"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> telefonata të reja të humbura"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Askush nuk është ende në thirrjen tënde të shpejtuar"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Shto një të preferuar"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Nuk ke ende kontakte"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Shto një kontakt"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Prek imazhin për të parë të gjithë numrat ose mbaje të shtypur për ta pozicionuar përsëri"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Hiq"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Telefonatë me video"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Dërgo një mesazh"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Detajet e telefonatës"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Telefono <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Telefonatë e humbur nga <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Telefonatë e përgjigjur nga <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Postë zanore e palexuar nga <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Postë zanore nga <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Telefonatë për <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"në <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"nëpërmjet <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"nëpërmjet <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"në <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, nëpërmjet <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> nëpërmjet <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Telefono"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Telefono <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Telefono me video <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Dëgjo postën zanore nga <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Luaj postën zanore nga <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Ndërprit postën zanore nga <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Fshi postën zanore nga <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> posta të reja zanore</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> postë e re zanore</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Krijo një kontakt për <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Shtoje <xliff:g id="NAMEORNUMBER">^1</xliff:g> te një kontakt ekzistues"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Detajet e telefonatës për <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"U fshi nga historiku i telefonatave"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Sot"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Dje"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Më të vjetra"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Lista e telefonatave"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Aktivizo altoparlantin."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Çaktivizo altoparlantin."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Luaj më shpejt."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Luaj më ngadalë."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Nis ose ndërprit luajtjen."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Opsionet e paraqitjes"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Tingujt dhe dridhjet"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Qasshmëria"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Zilja e telefonit"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Edhe dridhje për telefonatat"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tingujt e bllokut të tasteve"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Gjatësia e tonit të bllokut të formimit të numrave"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"I gjatë"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Përgjigjet e shpejta"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Telefonatat"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Bllokimi i telefonatave"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Bllokimi i telefonatave është përkohësisht joaktiv"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Bllokimi i telefonatave është çaktivizuar sepse kontaktove me shërbimet e urgjencës nga ky telefon brenda 48 orëve të fundit. Ai do të riaktivizohet automatikisht pas skadimit të periudhës prej 48 orë."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importo numrat"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Disa telefonues i ke shënuar më parë që të dërgohen automatikisht drejt postës zanore përmes aplikacioneve të tjera."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Shiko numrat"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importo"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Importimi dështoi"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Gabim në arkivimin e postës zanore."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Zhblloko numrin"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Shto një numër"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Telefonatat nga këta numra do të bllokohen dhe mesazhet zanore do të fshihen automatikisht."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Telefonatat nga këta numra do të bllokohen, por mund të jenë ende në gjendje të lënë mesazhe zanore."</string>
- <string name="block_list" msgid="7760188925338078011">"Numrat e bllokuar"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> është i pavlefshëm."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> është i bllokuar tashmë."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Bllokimi i thirrjeve është çaktivizuar për 48 orë"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Është e çaktivizuar për shkak se është kryer një telefonatë urgjence."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Llogaritë e telefonatave"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Aktivizo"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Cakto lejet"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Për të aktivizuar thirrjen e shpejtuar, aktivizo lejen e Kontakteve."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Për të parë evidencën e telefonatave, aktivizo lejen e Telefonit."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Për të parë kontaktet, aktivizo lejen e Kontakteve."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Për të pasur qasje në postën zanore, aktivizo lejen e Telefonit."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Për të kërkuar kontaktet, aktivizo lejet e Kontakteve"</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Për të kryer një telefonatë, aktivizo lejen e Telefonit."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Aplikacionet e telefonit nuk kanë leje të shkruajnë në cilësimet e sistemit."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"I bllokuar"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> është aktive"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blloko/raporto të padëshiruar"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blloko"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Nuk është i padëshiruar"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Zhblloko"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"E bezdisshme"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Blloko <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Telefonatat dhe posta zanore në të ardhmen nga ky numër do të bllokohen."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Raporto thirrjen e padëshiruar"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Telefonatat dhe posta zanore në të ardhmen nga ky numër do të bllokohen. Kjo telefonatë do të shënohet si i padëshiruar."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Zhblloko <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Ky numër do të zhbllokohet dhe nuk do të raportohet si i padëshiruar. Telefonatat dhe posta zanore në të ardhmen nuk do të identifikohen si të padëshiruara."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Në listën e bardhë <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Lista e bardhë"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Thirrjet dhe posta zanore e ardhshme nga ky numër nuk do të identifikohen si të padëshiruara. Ky numër nuk do të shënohet si i padëshiruar."</string>
-</resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
deleted file mode 100644
index 4b1526bdb..000000000
--- a/res/values-sr/strings.xml
+++ /dev/null
@@ -1,290 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Телефон"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Телефон"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Нумеричка тастатура телефона"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Телефон"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Историја позива"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Пријави нетачан број"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Копирај број"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Копирај транскрипцију"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Блокирај број"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> је блокиран"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Деблокирај број"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> је деблокиран"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ОПОЗОВИ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Избриши"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Измени број пре позива"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Обриши историју позива"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Избриши говорну поруку"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Архивирај говорну пошту"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Дели говорну поруку"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Гов. пошта је избрисана"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Говорна пошта је архивирана"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ОПОЗОВИ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"АРХИВА"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Желите да обришете историју позива?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Ово ће избрисати све позиве из историје"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Брише се историја позива…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Телефон"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Пропуштен позив"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Пропуштен позив за Work"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Пропуштени позиви"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"Број пропуштених позива: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Узврати позив"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Пошаљи SMS"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> говорна порука </item>
- <item quantity="few"> <xliff:g id="COUNT">%1$d</xliff:g> говорне поруке </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> говорних порука </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Пусти"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Нова говорна порука од <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Пуштање говорне поште није успело"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Говорна пошта се учитава…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Говорна пошта се архивира…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Учитавање говорне поште није успело"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Само позиви са говорном поштом"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Само долазни позиви"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Само одлазни позиви"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Само пропуштени позиви"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Визуелна говорна пошта"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Прегледајте и слушајте говорну пошту без позивања броја. Можда ће бити наплаћени трошкови за пренос података."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Подешавања"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Ажурирања говорне поште нису доступна"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Имате нову говорну пошту. Тренутно не може да се учита."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Подесите говорну пошту"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Аудио није доступан"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Подеси"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Зови говорну пошту"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Избор броја"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Избор броја"</string>
- <string name="make_primary" msgid="5829291915305113983">"Запамти овај избор"</string>
- <string name="description_search_button" msgid="3660807558587384889">"претражи"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"бирање"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"број за бирање"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Покретање или заустављање репродукције"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Укључивање или искључивање спикерфона"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Тражење позиције у репродукцији"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Смањивање брзине репродукције"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Повећавање брзине репродукције"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Историја позива"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Још опција"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"нумеричка тастатура"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Прикажи само одлазне"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Прикажи само долазне"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Прикажи само пропуштене"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Прикажи само говорне поруке"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Прикажи све позиве"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Додај паузу од 2 секунде"</string>
- <string name="add_wait" msgid="3360818652790319634">"Додај чекање"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Подешавања"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Нови контакт"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Сви контакти"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Детаљи позива"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Детаљи нису доступни"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Употребите бројчаник за тонско бирање"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Врати се на позив који је у току"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Додај позив"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Долазни позив"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Одлазни позив"</string>
- <string name="type_missed" msgid="2720502601640509542">"Пропуштен позив"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Долазни видео позив"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Одлазни видео позив"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Пропуштен видео позив"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Говорна пошта"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Одбијен позив"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Блокиран позив"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Долазни позиви"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Пуштање говорне поште"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Прикажи контакт <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Позови корисника <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Детаљи о контакту за <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> позива."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Видео позив."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Слање SMS-а за <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Непреслушана говорна пошта"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Покретање гласовне претраге"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Позови <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Непознато"</string>
- <string name="voicemail" msgid="3851469869202611441">"Говорна пошта"</string>
- <string name="private_num" msgid="6374339738119166953">"Приватан број"</string>
- <string name="payphone" msgid="7726415831153618726">"Телефонска говорница"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> сек"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> мин <xliff:g id="SECONDS">%s</xliff:g> сек"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> у <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Није могуће позвати овај број"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Да бисте подесили говорну пошту, идите у Мени &gt; Подешавања."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Да бисте позвали говорну пошту, прво искључите режим авионa."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Учитава се…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Учитава се са SIM картице…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"Контакти на SIM картици"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Нема доступне апликације за контакте"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Гласовна претрага није доступна"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Није могуће упутити телефонски позив јер је апликација Телефон онемогућена."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"На овом уређају нема апликација за то"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Претражите контакте"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Додајте број или претражите контакте"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Историја позива је празна"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Позови"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Немате ниједан пропуштен позив."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Пријемно сандуче говорне поште је празно."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Архива говорне поште је празна."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Прикажи само омиљене"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Историја позива"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Архива говорне поште"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Сви"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Пропуштени"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Говорна пошта"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Ново, једноставније блокирање"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Телефон треба да промени начин на који блокирање функционише да би вам пружио бољу заштиту. Са блокираних бројева више нећете примати ни позиве ни SMS-ове, али можете да их делите са другим апликацијама."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Дозволи"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Желите ли да блокирате <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Позиви са овог броја ће бити блокирани и поруке говорне поште ће се аутоматски брисати."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Позиви са овог броја ће бити блокирани, али позивалац и даље може да вам оставља поруке говорне поште."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Више нећете примати позиве ни SMS-ове са овог броја."</string>
- <string name="block_number_ok" msgid="770551992296781873">"БЛОКИРАЈ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Желите ли да деблокирате <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ДЕБЛОКИРАЈ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Брзо бирање"</string>
- <string name="tab_history" msgid="2563144697322434940">"Историја позива"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Контакти"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Говорна пошта"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Уклоњено је из омиљених"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Опозови"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Позови <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Направи нови контакт"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Додај у контакт"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Пошаљи SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Упути видео позив"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Блокирај број"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"Нових пропуштених позива: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Немате ниједан контакт на брзом бирању"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Додај омиљен контакт"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Још увек немате ниједан контакт"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Додај контакт"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Додирните слику да бисте видели све бројеве или додирните и задржите да бисте променили распоред"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Уклони"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Видео позив"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Пошаљи поруку"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Детаљи позива"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Позови <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Пропуштени позив: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Примљени позив: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Непрочитана говорна порука од <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Говорна порука од <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Позвали сте: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"на налогу <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"преко <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"преко <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"на <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, преко <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> преко <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Позови"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Позови <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Упутите видео позив контакту <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Пусти говорну пошту од <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Репродукуј говорну пошту контакта <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Паузирај говорну пошту контакта <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Избриши говорну пошту контакта <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> нова порука говорне поште</item>
- <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> нове поруке говорне поште</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> нових порука говорне поште</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Направите контакт за <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Додајте <xliff:g id="NAMEORNUMBER">^1</xliff:g> постојећем контакту"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Детаљи позива за <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Избрисано из историје позива"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Данас"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Јуче"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Старији"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Листа позива"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Укључите звучник."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Искључите звучник."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Бржа репродукција."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Спорија репродукција."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Започните или паузирајте репродукцију."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Опције приказа"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Звуци и вибрација"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Приступачност"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Звук звона телефона"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Вибрирај и за позиве"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Звуци нумеричке тастатуре"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Трајање тонова нумеричке тастатуре"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Нормално"</item>
- <item msgid="6177579030803486015">"Дугачко"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Брзи одговори"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Позиви"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Блокирање позива"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Блокирање позива је привремено искључено"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Блокирање позива је онемогућено зато што сте контактирали службе за помоћ у хитним случајевима са овог телефона у последњих 48 сати. Аутоматски ће бити поново омогућено када истекне период од 48 сати."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Увези бројеве"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Раније сте означили неке позиваоце које аутоматски треба преусмерити на говорну пошту преко других апликација."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Прикажи бројеве"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Увeзи"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Увоз није успео"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Архивирање говорне поште није успело."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Деблокирај број"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Додај број"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Позиви са ових бројева ће бити блокирани и поруке говорне поште ће се аутоматски брисати."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Позиви са ових бројева ће бити блокирани, али позиваоци са ових бројева ће и даље моћи да вам остављају поруке говорне поште."</string>
- <string name="block_list" msgid="7760188925338078011">"Блокирани бројеви"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> је неважећи."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> је већ блокиран."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Блокирање позива је онемогућено на 48 сати"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Онемогућено је зато што је упућен хитан позив."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Налози за позивање"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Укључи"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Подеси дозволе"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Да бисте омогућили брзо бирање, укључите дозволу за Контакте."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Да бисте видели евиденцију позива, укључите дозволу за Телефон."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Да бисте видели контакте, укључите дозволу за Контакте."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Да бисте приступили говорној пошти, укључите дозволу за Телефон."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Да бисте претражили контакте, укључите дозволе за Контакте."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Да бисте упутили позив, укључите дозволу за Телефон."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Апликација Телефон нема дозволу за уписивање у системска подешавања."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Блокиран"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> је активан"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Блокирај/пријави као непожељан"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Блокирај"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Није непожељан"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Деблокирај"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Непожељан"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Желите да блокирате <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Блокираћемо будуће позиве и говорне поруке са овог броја."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Пријави позив као непожељан"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Блокираћемо будуће позиве и говорне поруке са овог броја. Пријавићемо овај позив као непожељан."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Желите да деблокирате <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Деблокираћемо број и пријавити да није непожељан, будући позиви и говорне поруке неће бити непожељни"</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Желите да ставите <xliff:g id="NUMBER">%1$s</xliff:g> на белу листу?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Стави на белу листу"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Будући позиви и говорне поруке са овог броја неће бити непожељни. Пријавићемо да он није непожељан."</string>
-</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
deleted file mode 100644
index 0148d7578..000000000
--- a/res/values-sv/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Telefonens knappsats"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Samtalshistorik"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Rapportera fel nummer"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopiera nummer"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopiera transkription"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Blockera nummer"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> har blockerats"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Häv blockeringen av numret"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Blockeringen av <xliff:g id="NUMBER">%1$s</xliff:g> har hävts"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ÅNGRA"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Radera"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Redigera nummer före samtal"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Rensa samtalshistorik"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Radera röstmeddelande"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Arkivera röstmeddelande"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Dela röstmeddelande"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Röstbrevlåda raderad"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Röstmeddelandet har arkiverats"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ÅNGRA"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ÖPPNA ARKIV"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Vill du rensa samtalshistoriken?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Alla samtal raderas från historiken"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Rensar samtalshistoriken ..."</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Ringa"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Missat samtal"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Missat jobbsamtal"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Missade samtal"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> missade samtal"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Ring upp"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Meddelande"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> röstmeddelanden </item>
- <item quantity="one">röstmeddelande</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Spela upp"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Nytt röstmeddelande från <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Det gick inte att spela upp röstmeddelandet"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Läser in röstmeddelande ..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Arkiverar röstmeddelande ..."</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Det gick inte att läsa in röstmeddelandet"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Endast samtal med röstmeddelande"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Endast inkommande samtal"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Endast utgående samtal"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Endast missade samtal"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Visuell röstbrevlåda"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Visa och lyssna på ett röstmeddelande utan att behöva ringa ett nummer. Dataavgifter kan tillkomma."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Inställningar"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Uppdateringar för röstbrevlådan är inte tillgängliga"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Ett nytt röstmeddelande väntar. Det går inte att läsa in det för tillfället."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Konfigurera röstbrevlådan"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Ljud är inte tillgängligt"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Konfigurera"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Ring röstbrevlådan"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Välj nummer"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Välj nummer"</string>
- <string name="make_primary" msgid="5829291915305113983">"Kom ihåg det här valet"</string>
- <string name="description_search_button" msgid="3660807558587384889">"sök"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"ring"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"nummer att ringa"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Starta eller stoppa uppspelning"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Slå på eller av högtalartelefon"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Sök uppspelningsläge"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Minska uppspelningshastighet"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Öka uppspelningshastighet"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Samtalshistorik"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Fler alternativ"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"knappsats"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Visa endast utgående samtal"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Visa endast inkommande samtal"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Visa endast missade samtal"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Visa bara röstmeddelanden"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Visa alla samtal"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Lägg till en paus på 2 sek."</string>
- <string name="add_wait" msgid="3360818652790319634">"Lägg till väntetid"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Inställningar"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Ny kontakt"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Alla kontakter"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Samtalsinfo"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Det finns ingen information"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Använd tonvalstelefon"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Återvänd till pågående samtal"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Lägg t. samt."</string>
- <string name="type_incoming" msgid="6502076603836088532">"Inkommande samtal"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Utgående samtal"</string>
- <string name="type_missed" msgid="2720502601640509542">"Missat samtal"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Inkommande videosamtal"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Utgående videosamtal"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Missat videosamtal"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Röstmeddelande"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Avvisat samtal"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Blockerat samtal"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Inkommande samtal"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Spela upp röstmeddelande"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Visa kontakten <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Ring <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Kontaktuppgifter för <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> samtal."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Videosamtal."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Skicka sms till <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Ej hört röstmeddelande"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Starta röstsökning"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Ring <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Okänd"</string>
- <string name="voicemail" msgid="3851469869202611441">"Röstbrevlåda"</string>
- <string name="private_num" msgid="6374339738119166953">"Privat nummer"</string>
- <string name="payphone" msgid="7726415831153618726">"Telefonautomat"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> sekund"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> sek"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> kl. <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Det går inte att ringa det här numret"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Välj Meny &gt; Inställningar om du vill konfigurera röstbrevlådan."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Om du vill ringa röstbrevlådan måste du först inaktivera flygplansläget."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Läser in …"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI-kod"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Läser in från SIM-kort…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"Kontakter från SIM-kort"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Det finns inga appar för kontakter"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Röstsökning är inte tillgänglig"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Det går inte att ringa eftersom appen Telefon har inaktiverats."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Det finns ingen app för detta på enheten"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Sök efter kontakter"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Ange nummer eller kontakt"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Samtalshistoriken är tom"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Ring ett samtal"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Du har inga missade samtal."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Din röstbrevlåda är tom."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Arkivet för din röstbrevlåda är tomt."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Visa endast favoriter"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Samtalshistorik"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Arkiv för röstmeddelanden"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Alla"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Missade"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Röstbrevlåda"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"En ny, enklare blockering"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"För bättre skydd måste mobilen ändra blockeringsfunktionen. Samtal eller sms från blockerade nummer stoppas och inställningarna går att dela med andra appar."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Tillåt"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Vill du blockera <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Samtal från det här numret blockeras och röstmeddelanden raderas automatiskt."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Samtal från det här numret blockeras, men det kan fortfarande gå att lämna ett röstmeddelande."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Du får inte längre samtal eller sms från det här numret."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOCKERA"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Vill du häva blockeringen av <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"HÄV BLOCKERINGEN"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Kortnummer"</string>
- <string name="tab_history" msgid="2563144697322434940">"Samtalshistorik"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontakter"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Röstbrevlåda"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Borttagen från favoriter"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Ångra"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Ring <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Skapa ny kontakt"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Lägg till i kontakt"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Skicka sms"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Ring videosamtal"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Blockera nummer"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> nya missade samtal"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Du har ingen kontakt som snabbval ännu"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Lägg till en favorit"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Du har inga kontakter ännu"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Lägg till en kontakt"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Tryck på bilden och visa alla nummer eller tryck länge om du vill ändra ordningen"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Ta bort"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Videosamtal"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Skicka ett meddelande"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Samtalsinfo"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Ring <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Missat samtal från <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Besvarat samtal från <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Nytt röstmeddelande från <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Röstmeddelande från <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Ring till <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"på <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"med <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"med <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"på <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, med <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> med <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Samtal"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Ring <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Ring videosamtal till <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Lyssna på ett röstmeddelande från <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Spela upp röstmeddelande från <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Pausa röstmeddelande från <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Radera röstmeddelande från <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> nya röstmeddelanden</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> nytt röstmeddelande</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Skapa en kontakt för <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Lägg till <xliff:g id="NAMEORNUMBER">^1</xliff:g> i befintliga kontakter"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Samtalsinformation för <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Har raderats från samtalshistoriken"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"I dag"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"I går"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Äldre"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Samtalslista"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Slå på högtalaren."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Stäng av högtalaren."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Spela upp snabbare."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Spela upp långsammare."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Starta eller pausa uppspelning."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Visningsalternativ"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Ljud och vibration"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Tillgänglighet"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Ringsignal"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Vibrera också vid samtal"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Knappsatsljud"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Tonlängd för knappsats"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"Lång"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Snabbsvar"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Samtal"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Samtalsblockering"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Blockeringen har inaktiverats tillfälligt"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Samtalsblockering har inaktiverats eftersom du ringde ett larmnummer från den här mobilen under de senaste 48 timmarna. Blockeringen aktiveras igen när 48 timmar har gått."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Importera nummer"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Du har tidigare angett via andra appar att vissa inkommande samtal ska skickas till röstbrevlådan automatiskt."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Visa telefonnummer"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Importera"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Det gick inte att importera"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Kunde inte arkivera röstmeddelande."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Häv blockeringen av numret"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Lägg till telefonnummer"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Samtal från de här numren blockeras och röstmeddelanden raderas automatiskt."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Samtal från de här numren blockeras, men det kan fortfarande gå att lämna ett röstmeddelande."</string>
- <string name="block_list" msgid="7760188925338078011">"Blockerade nummer"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> är inte giltigt."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> är redan blockerat."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Samtalsblockeringen har inaktiverats i 48 timmar"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Inaktiverades därför att enheten har använts för ett nödsamtal."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Konton för samtal"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Aktivera"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Ange behörigheter"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Du måste aktivera behörigheten Kontakter för att kunna använda snabbuppringning."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Du måste aktivera behörigheten Telefon för att kunna visa samtalsloggen."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Du måste aktivera behörigheten Kontakter för att kunna visa kontakterna."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Du måste aktivera behörigheten Telefon för att kunna komma åt röstmeddelanden."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Aktivera behörigheter för Kontakter om du vill söka bland kontakterna."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Du måste aktivera behörigheten Telefon för att kunna ringa."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Telefonappen har inte skrivbehörighet i systeminställningarna."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Blockerad"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> är aktiv"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Blockera/rapportera spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Blockera"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Inte spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Avblockera"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Skräp"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Vill du blockera <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Framtida samtal och röstmeddelanden från det här numret blockeras."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Rapportera samtal som spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Framtida samtal och röstmeddelanden från det här numret blockeras. Samtalet rapporteras som spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Avblockera <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Numret avblockeras och redovisas inte som spam. Samtal och röstmeddelanden redovisas inte som spam."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Vitlista <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Vitlista"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Samtal och röstmeddelanden från numret identifieras inte som spam. Numret rapporteras inte som spam."</string>
-</resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
deleted file mode 100644
index 3459b5ee4..000000000
--- a/res/values-sw/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Simu"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Nambari ya simu"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Vitufe vya Kupiga Simu"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Simu"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Rekodi ya simu zilizopigwa"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Ripoti nambari isiyo sahihi"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Nakili nambari"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Nakili unukuzi"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Zuia nambari"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> imezuiwa"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Ondolea nambari kizuizi"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> imeacha kuzuiwa"</string>
- <string name="block_number_undo" msgid="591338370336724156">"TENDUA"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Futa"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Badilisha nambari kabla ya kupiga simu"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Futa rekodi ya simu zilizopigwa"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Futa ujumbe wa sauti"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Weka kwenye kumbukumbu"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Shiriki ujumbe wa sauti"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Ujumbe wa sauti umefutwa"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Umewekwa kwenye kumbukumbu"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"TENDUA"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"KUMBUKUMBU"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Rekodi ya simu zilizopigwa ifutwe?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Hatua hii itafuta rekodi yote ya simu"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Inafuta rekodi ya simu zilizopigwa..."</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Simu"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Simu ambayo haikujibiwa"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Simu ya kazini ambayo hukujibu"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Simu zisizojibiwa"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"Simu <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ambazo hazikujibiwa"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Mpigie"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Ujumbe"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other">Ujumbe <xliff:g id="COUNT">%1$d</xliff:g> wa sauti </item>
- <item quantity="one">Ujumbe wa sauti</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Cheza"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Barua mpya ya sauti kutoka <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Haikuweza kucheza ujumbe wa sauti"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Inapakia ujumbe wa sauti..."</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Ujumbe unawekwa kwenye kumbukumbu…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Haikuweza kupakia ujumbe wa sauti"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Simu zilizo na ujumbe wa sauti tu"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Simu zinazoingia tu"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Simu zinazotoka tu"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Simu zisizojibiwa tu"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Ujumbe wa sauti unaoonekana"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Tazama na usikilize ujumbe wako wa sauti, bila kupiga simu kwa nambari yoyote. Huenda ukatozwa kwa gharama ya data."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Mipangilio"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Masasisho ya ujumbe wa sauti hayapatikani"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Ujumbe mpya wa sauti unasubiri. Haiwezi kupakia sasa hivi."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Sanidi ujumbe wako wa sauti"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Sauti haipatikana"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Sanidi"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Pigia barua sauti"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"( <xliff:g id="COUNT">%1$d</xliff:g> ) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Chagua nambari"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Chagua nambari"</string>
- <string name="make_primary" msgid="5829291915305113983">"Kumbuka chaguo hili"</string>
- <string name="description_search_button" msgid="3660807558587384889">"tafuta"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"piga simu"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"nambari ya kupiga"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Cheza au usimamishe uchezaji wa ujumbe wa sauti"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Washa au uzime spika ya simu"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Tafuta sehemu ya kucheza ujumbe wa sauti"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Punguza kasi ya kucheza ujumbe wa sauti"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Ongeza kasi ya kucheza ujumbe wa sauti"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Historia ya Simu"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Chaguo zaidi"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"kitufe cha kupiga"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Onyesha zinazotoka pekee"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Onyesha zinazoingia pekee"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Onyesha zilizokosa kupokewa pekee"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Onyesha barua za sauti pekee"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Onyesha simu zote"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Ongeza usitishaji wa sekunde 2"</string>
- <string name="add_wait" msgid="3360818652790319634">"Ongeza muda wa kusubiri"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Mipangilio"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Anwani mpya"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Anwani zote"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Maelezo ya simu"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Maelezo hayapatikana"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Tumia kibao cha kuchapa cha sauti na kugusa"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Rudi kwa simu inayoendelea"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Ongeza simu"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Unapigiwa simu"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Simu unayopiga"</string>
- <string name="type_missed" msgid="2720502601640509542">"Simu uliyokosa"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Hangout ya Video inayoingia"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Hangout ya Video inayotoka"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Hangout ya Video ambayo haikupokewa"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Barua za sauti"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Simu iliyokataliwa"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Simu iliyozuiwa"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Simu zinazoingia"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Sikiliza ujumbe wa sauti"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Angalia anwani <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Piga <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Maelezo ya <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"Simu <xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Hangout ya video."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Tuma SMS kwa <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Barua sauti ambayo haijasikizwa"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Anza kutafuta kwa kutamka"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Piga simu <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Haijulikani"</string>
- <string name="voicemail" msgid="3851469869202611441">"Ujumbe wa sauti"</string>
- <string name="private_num" msgid="6374339738119166953">" Nambari isiyojulikana"</string>
- <string name="payphone" msgid="7726415831153618726">"Simu ya kulipia"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"Sekunde <xliff:g id="SECONDS">%s</xliff:g>"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"Dak <xliff:g id="MINUTES">%s</xliff:g> sek <xliff:g id="SECONDS">%s</xliff:g>"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> saa <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Haiwezi kupiga simu kwa nambari hii"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Ili kusanidi ujumbe wa sauti, nenda kwa Menyu &gt; Mipangilio."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Kupigia simu ujumbe wa sauti, kwanza zima hali ya ndege."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Inapakia…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Inapakia kutoka SIM kadi..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Anwani za SIM kadi"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Hakuna programu za mawasiliano zinazopatikana"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Kutafuta kwa kutamka hakupatikani"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Haiwezi kupiga simu kwa sababu programu ya Simu imezimwa."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Hakuna programu kwa ajili ya hiyo kwenye kifaa hiki"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Tafuta anwani"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Ongeza nambari au utafute anwani"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Rekodi yako ya simu zilizopigwa haina chochote"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Piga simu"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Huna simu ulizokosa kupokea"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Kikasha chako cha ujumbe wa sauti hakina ujumbe."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Kumbukumbu yako ya ujumbe wa sauti haina kitu."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Onyesha zinazopendwa tu"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Rekodi ya Simu Zilizopigwa"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Kumbukumbu ya Ujumbe wa Sauti"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Zote"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Ambazo hazikupokewa"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Ujumbe wa sauti"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Njia mpya na rahisi ya kuzuia"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Ili kukulinda vizuri zaidi, Simu inahitaji kubadilisha mipangilio ya kipengele cha kuzuia. Hutapokea simu wala SMS kutoka nambari ulizozizuia na nambari hizo huenda zikashirikiwa na programu zingine."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Ruhusu"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Ungependa kuzuia <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Simu zinazopigwa kutoka nambari hii zitazuiwa na ujumbe wa sauti utafutwa kiotomatiki."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Simu zinazopigwa kutoka nambari hii zitazuiwa, lakini mpigaji bado anaweza kukutumia ujumbe wa sauti."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Hutapokea simu wala SMS kutoka kwa nambari hii tena."</string>
- <string name="block_number_ok" msgid="770551992296781873">"ZUIA"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Ungependa kuacha kuzuia <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ACHA KUZUIA"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Unaowasiliana nao zaidi"</string>
- <string name="tab_history" msgid="2563144697322434940">"Rekodi ya Simu Zilizopigwa"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Anwani zote"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Ujumbe wa sauti"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Imeondolowa kwenye vipendwa"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Tendua"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Piga simu <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Ongeza anwani mpya"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Ongeza kwenye anwani"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Tuma SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Piga Hangout ya video"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Zuia nambari"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"Simu <xliff:g id="NUMBER">%s</xliff:g> za karibuni ambazo hazikujibiwa"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Hakuna mtu aliye katika orodha yako ya watu unaowasiliana nao zaidi"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Ongeza anwani unazopenda zaidi"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Bado huna anwani zozote"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Ongeza anwani"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Gusa picha ili uone nambari zote au gusa na ushikilie ili upange upya"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Ondoa"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Hangout ya Video"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Tuma SMS"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Maelezo ya simu"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Piga simu kwa <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Simu ambayo haikupokelewa kutoka kwa <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Simu iliyopokelewa kutoka kwa <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Ujumb wa sauti ambao haujasikilizwa kutoka <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Ujumbe wa sauti kutoka <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Simu iliyopigwa kwa <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"kwenye <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"kupitia <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"kupitia <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"kwenye <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, kupitia <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> kupitia <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Piga simu"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Piga simu kwa <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Hangout ya video <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Sikiliza ujumbe wa sauti kutoka kwa <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Cheza ujumbe wa sauti kutoka kwa <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Sitisha ujumbe wa sauti kutoka kwa <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Futa ujumbe wa sauti kutoka kwa <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other">Ujumbe <xliff:g id="COUNT_1">%d</xliff:g> mpya wa sauti</item>
- <item quantity="one">Ujumbe <xliff:g id="COUNT_0">%d</xliff:g> mpya wa sauti</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Unda anwani ya <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Ongeza <xliff:g id="NAMEORNUMBER">^1</xliff:g> kwenye anwani iliyopo"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Maelezo ya simu ya <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Ilifutwa kutoka rekodi ya simu zilizopigwa"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Leo"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Jana"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Nzee zaidi"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Orodha ya simu"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Washa spika."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Zima spika."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Cheza kwa kasi zaidi."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Cheza polepole."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Anzisha au usitishe kucheza."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Chaguo za kuonyesha"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Sauti na mtetemo"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Zana za walio na matatizo ya kuona au kusikia"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Mlio wa simu"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Pia tetema simu zinapoingia"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Milio ya vitufe vya kupiga simu"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Urefu wa toni ya vitufe vya kupiga simu"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Kawaida"</item>
- <item msgid="6177579030803486015">"Ndefu"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Majibu ya haraka"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Simu zilizopigwa"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Kuzuia simu"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Kuzuia simu kumezimwa kwa muda"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Kipengele cha kuzuia simu kimezimwa kwa sababu uliwasiliana na huduma za dharura kwenye simu hii ndani ya saa 48 zilizopita. Kipengele hiki kitawashwa kiotomatiki baada ya kipindi cha saa 48 kumalizika."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Leta nambari"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Hapo awali uliwekea alama baadhi ya wanaopiga ili simu zao ziingie kwenye ujumbe wa sauti kiotomatiki kupitia programu nyingine."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Angalia Nambari"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Leta"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Imeshindwa kuingiza"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Imeshindwa kuuweka kwenye kumbukumbu."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Ondolea nambari kizuizi"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Ongeza nambari"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Simu zinazopigwa kutoka nambari hizi zitazuiwa na ujumbe wa sauti utafutwa kiotomatiki."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Simu zinazopigwa kutoka nambari hizi zitazuiwa, lakini wapigaji bado wanaweza kukutumia ujumbe wa sauti."</string>
- <string name="block_list" msgid="7760188925338078011">"Nambari zilizozuiwa"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> si sahihi."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> tayari imezuiwa."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Huduma ya kuzuia simu imezimwa kwa saa 48"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Imezimwa kwa sababu simu ya dharura imepigwa."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Akaunti za simu"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Washa"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Weka ruhusa"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Ili kuwasha kipengele cha unaowasiliana nao zaidi, washa ruhusa ya Anwani."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Ili uone rekodi yako ya nambari za simu, washa ruhusa ya Simu."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Ili uone anwani zako, washa ruhusa ya Anwani."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Ili ufikie ujumbe wako wa sauti, washa ruhusa ya Simu."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Ili utafute anwani zako, washa ruhusa za Anwani."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Ili upige simu, washa ruhusa ya Simu."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Programu ya simu haina ruhusa ya kuandika kwenye mipangilio ya mfumo."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Imezuiwa"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> yupo"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Zuia/ripoti taka"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Zuia"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Si taka"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Acha kuzuia"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Taka"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Je, ungependa kuzuia <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Ujumbe wa sauti na simu zinazopigwa kutoka nambari katika siku zijazo zitazuiwa."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Ripoti kuwa hii ni simu taka"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Ujumbe wa sauti na simu za baadaye kutoka nambari hii zitazuiwa. Simu hii itaripotiwa kuwa ni taka."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Je, ungependa kuacha kuzuia <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Kizuizi kitaondolewa; namba itaripotiwa kuwa si taka. Ujumbe wa sauti/simu hazitambuliwa kama taka."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Je, ungependa kuweka <xliff:g id="NUMBER">%1$s</xliff:g> katika nambari zilizoidhinishwa?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Iweke katika zilizoidhinishwa"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Ujumbe wa sauti/simu za nambari hii hazitambuliwa kama taka tena. Nambari itaripotiwa kuwa si taka."</string>
-</resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
deleted file mode 100644
index 7c01ee12d..000000000
--- a/res/values-ta/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"ஃபோன்"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"ஃபோன்"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"ஃபோன் டயல்பேடு"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"ஃபோன்"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"அழைப்பு வரலாறு"</string>
- <string name="action_report_number" msgid="4635403959812186162">"தவறான எண் எனப் புகாரளி"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"எண்ணை நகலெடு"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"டிரான்ஸ்கிரிப்ஷனை நகலெடு"</string>
- <string name="action_block_number" msgid="1482657602262262134">"எண்ணைத் தடு"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> தடுக்கப்பட்டது"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"எண்ணை அனுமதி"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> அனுமதிக்கப்பட்டது"</string>
- <string name="block_number_undo" msgid="591338370336724156">"செயல்தவிர்"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"நீக்கு"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"அழைக்கும் முன் எண்ணை மாற்று"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"அழைப்பு வரலாற்றை அழி"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"குரலஞ்சலை நீக்கு"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"குரலஞ்சலைக் காப்பகப்படுத்து"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"குரலஞ்சலைப் பகிர்"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"குரலஞ்சல் நீக்கப்பட்டது"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"குரலஞ்சல் காப்பகப்படுத்தப்பட்டது"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"செயல்தவிர்"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"காப்பகம் செல்"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"அழைப்பு பதிவை அழிக்கவா?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"பதிவிலிருந்து எல்லா அழைப்புகளும் நீக்கப்படும்"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"அழைப்பு வரலாற்றை அழிக்கிறது…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"ஃபோன்"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"தவறிய அழைப்பு"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"தவறிய அழைப்பு (பணி)"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"தவறிய அழைப்புகள்"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> தவறிய அழைப்புகள்"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"திரும்ப அழை"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"செய்தி அனுப்பு"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> குரலஞ்சல்கள் </item>
- <item quantity="one">குரலஞ்சல்</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"இயக்கு"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> இன் புதிய குரலஞ்சல்"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"குரலஞ்சலை இயக்க முடியவில்லை"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"குரலஞ்சலை ஏற்றுகிறது…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"குரலஞ்சலைக் காப்பகப்படுத்துகிறது…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"குரலஞ்சலை ஏற்ற முடியவில்லை"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"குரலஞ்சலுடனான அழைப்புகள் மட்டும்"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"உள்வரும் அழைப்புகள் மட்டும்"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"வெளிச்செல்லும் அழைப்புகள் மட்டும்"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"தவறிய அழைப்புகள் மட்டும்"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"விஷூவல் குரலஞ்சல்"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"எண்ணை அழைக்காமலே, குரலஞ்சலைப் பார்க்கலாம் மற்றும் கேட்கலாம். இதற்கு தரவுக் கட்டணங்கள் விதிக்கப்படலாம்."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"அமைப்புகள்"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"குரலஞ்சல் புதுப்பிப்புகள் இல்லை"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"புதிய குரலஞ்சல் காத்திருக்கிறது. தற்போது ஏற்ற முடியாது."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"குரலஞ்சலை அமைக்கவும்"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"ஆடியோ இல்லை"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"அமைப்பு"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"குரலஞ்சலில் அழை"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"எண்ணைத் தேர்வுசெய்யவும்"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"எண்ணைத் தேர்வுசெய்யவும்"</string>
- <string name="make_primary" msgid="5829291915305113983">"இந்த விருப்பத்தை நினைவில்கொள்"</string>
- <string name="description_search_button" msgid="3660807558587384889">"தேடு"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"டயல்"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"டயல் செய்வதற்கான எண்"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"பிளேபேக்கை இயக்கு அல்லது நிறுத்து"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"ஸ்பீக்கர்போனை இயக்கு அல்லது முடக்கு"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"பிளேபேக் நிலையைத் தேடு"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"பிளேபேக் விகிதத்தைக் குறை"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"பிளேபேக் விகிதத்தை அதிகரி"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"அழைப்பு பட்டியல்"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"மேலும் விருப்பங்கள்"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"டயல்பேடு"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"வெளிச்செல்லுவதை மட்டும் காட்டு"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"உள்வருவதை மட்டும் காட்டு"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"தவறியதை மட்டும் காட்டு"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"குரலஞ்சல்களை மட்டும் காட்டு"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"எல்லா அழைப்புகளையும் காட்டு"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2-வினாடி இடைநிறுத்தத்தைச் சேர்"</string>
- <string name="add_wait" msgid="3360818652790319634">"காத்திருப்பைச் சேர்"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"அமைப்பு"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"புதிய தொடர்பு"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"எல்லா தொடர்புகளும்"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"அழைப்பு விவரங்கள்"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"விவரங்கள் இல்லை"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"டச் டோன் விசைப்பலகையைப் பயன்படுத்தவும்"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"செயலிலுள்ள அழைப்பிற்குத் திரும்பு"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"அழைப்பைச் சேர்"</string>
- <string name="type_incoming" msgid="6502076603836088532">"உள்வரும் அழைப்பு"</string>
- <string name="type_outgoing" msgid="343108709599392641">"வெளிச்செல்லும் அழைப்பு"</string>
- <string name="type_missed" msgid="2720502601640509542">"தவறிய அழைப்பு"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"உள்வரும் வீடியோ அழைப்பு"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"வெளிச்செல்லும் வீடியோ அழைப்பு"</string>
- <string name="type_missed_video" msgid="954396897034220545">"தவறிய வீடியோ அழைப்பு"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"குரலஞ்சல்"</string>
- <string name="type_rejected" msgid="7783201828312472691">"நிராகரிக்கப்பட்ட அழைப்பு"</string>
- <string name="type_blocked" msgid="3521686227115330015">"தடுக்கப்பட்ட அழைப்பு"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"உள்வரும் அழைப்புகள்"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"குரலஞ்சலை இயக்கு"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> தொடர்பைக் காட்டு"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g> ஐ அழை"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> க்கான தொடர்பு விவரங்கள்"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> அழைப்புகள்."</string>
- <string name="description_video_call" msgid="2933838090743214204">"வீடியோ அழைப்பு."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g>க்கு SMS அனுப்பு"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"கேட்கப்படாத குரலஞ்சல்"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"குரல் தேடலைத் தொடங்கு"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> ஐ அழை"</string>
- <string name="unknown" msgid="740067747858270469">"அறியப்படாதது"</string>
- <string name="voicemail" msgid="3851469869202611441">"குரலஞ்சல்"</string>
- <string name="private_num" msgid="6374339738119166953">"தனிப்பட்ட எண்"</string>
- <string name="payphone" msgid="7726415831153618726">"கட்டணத் தொலைபேசி"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> வி"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> நிமிடம் <xliff:g id="SECONDS">%s</xliff:g> வினாடி"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> அன்று <xliff:g id="TIME">%2$s</xliff:g> மணிக்கு"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"இந்த எண்ணை அழைக்க முடியாது"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"குரலஞ்சலை அமைக்க, செல்க மெனு &gt; அமைப்பு."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"குரலஞ்சலை அழைப்பதற்கு, முதலில் விமானப் பயன்முறையை முடக்கவும்."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"ஏற்றுகிறது..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"சிம் கார்டிலிருந்து ஏற்றுகிறது…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"சிம் கார்டின் தொடர்புகள்"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"தொடர்புகள் பயன்பாடு எதுவுமில்லை"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"குரல் தேடல் இல்லை"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Phone பயன்பாடு முடக்கப்பட்டுள்ளதால், ஃபோன் அழைப்பைச் செய்ய முடியாது."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"சாதனத்தில் இந்தச் செயலைச் செய்வதற்கான பயன்பாடு எதுவும் இல்லை"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"தொடர்புகளைத் தேடுக"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"எண்ணைச் சேர்க்கவும் அல்லது தொடர்புகளில் தேடவும்"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"அழைப்பு வரலாறு காலியாக உள்ளது"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"அழை"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"தவறிய அழைப்புகள் இல்லை."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"குரலஞ்சல் இன்பாக்ஸ் காலியாக உள்ளது."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"குரலஞ்சல் காப்பகம் காலியாக உள்ளது."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"பிடித்தவற்றை மட்டும் காட்டு"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"அழைப்பு வரலாறு"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"குரலஞ்சல் காப்பகம்"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"எல்லாம்"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"தவறியவை"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"குரலஞ்சல்"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"புதிய, எளிதாக்கப்பட்ட தடுத்தல்"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"சிறப்பான பாதுகாப்பை வழங்குவதற்காக, ஃபோனில் தடுத்தல் செயல்படும் விதத்தை மாற்ற வேண்டும். இப்போது தடுத்த எண்களிலிருந்து வரும் அழைப்புகளும் உரைச் செய்திகளும் நிறுத்தப்படுவதோடு, பிற பயன்பாடுகளுடன் பகிரப்பட்டு அவற்றிலும் தடுக்கப்படக்கூடும்."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"அனுமதி"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g>ஐத் தடுக்கவா?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"இந்த எண்ணின் அழைப்புகள் தடுக்கப்படுவதுடன், அதன் குரலஞ்சல்களும் தானாகவே நீக்கப்படும்."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"இந்த எண்ணின் அழைப்புகள் தடுக்கப்படும், ஆனால் அழைப்பாளரால் இன்னமும் உங்களுக்குக் குரலஞ்சல்களை அனுப்ப முடியும்."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"இந்த எண்ணிலிருந்து இனி அழைப்புகளையோ உரைச் செய்திகளையோ பெறமாட்டீர்கள்."</string>
- <string name="block_number_ok" msgid="770551992296781873">"தடு"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g>ஐ அனுமதிக்கவா?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"அனுமதி"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"விரைவு டயல்"</string>
- <string name="tab_history" msgid="2563144697322434940">"அழைப்பு வரலாறு"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"தொடர்புகள்"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"குரலஞ்சல்"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"பிடித்தவற்றிலிருந்து அகற்றப்பட்டது"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"செயல்தவிர்"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> ஐ அழை"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"புதிய தொடர்பை உருவாக்கு"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"தொடர்பில் சேர்"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS அனுப்பு"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"வீடியோவில் அழை"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"எண்ணைத் தடு"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> புதிய தவறிய அழைப்புகள்"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"இன்னும் விரைவு டயலில் யாரையும் சேர்க்கவில்லை"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"முக்கியமானவர்களைச் சேர்"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"இதுவரை எந்தத் தொடர்புகளும் இல்லை"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"ஒரு தொடர்பைச் சேர்க்கவும்"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"எல்லா எண்களையும் பார்க்க, படத்தைத் தொடவும் அல்லது மறுவரிசைப்படுத்த தொட்டுப் பிடித்திருக்கவும்"</string>
- <string name="remove_contact" msgid="1080555335283662961">"அகற்று"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"வீடியோ அழைப்பு"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"செய்தி அனுப்பவும்"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"அழைப்பு விவரங்கள்"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>ஐ அழை"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"தவறிய அழைப்பு: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"பேசிய அழைப்பு: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"படிக்காத குரலஞ்சல்: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"குரலஞ்சல்: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"அழைத்த அழைப்பு: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> இல்"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> மூலமாக"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> மூலமாக"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="NUMBER">%2$s</xliff:g> மூலமாக <xliff:g id="PHONEACCOUNT">%1$s</xliff:g> இல்"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="NUMBER">%2$s</xliff:g> மூலமாக <xliff:g id="PHONEACCOUNT">%1$s</xliff:g> இல்"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"அழை"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>ஐ அழை"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>ஐ வீடியோவில் அழைக்கும்."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> இன் குரலஞ்சலைக் கேள்"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> இன் குரலஞ்சலை இயக்கும்"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> இன் குரலஞ்சலை இடைநிறுத்தும்"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> இன் குரலஞ்சலை நீக்கும்"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> புதிய குரலஞ்சல்கள்</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> புதிய குரலஞ்சல்</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>க்கான தொடர்பை உருவாக்கும்"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"இருக்கும் தொடர்பில் <xliff:g id="NAMEORNUMBER">^1</xliff:g>ஐச் சேர்க்கும்"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> இன் அழைப்பு விவரங்கள்"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"அழைப்பு வரலாற்றிலிருந்து நீக்கப்பட்டது"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"இன்று"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"நேற்று"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"பழையது"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"அழைப்புகள் பட்டியல்"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"ஸ்பீக்கரை இயக்கு."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"ஸ்பீக்கரை முடக்கு."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"வேகமாக இயக்கு."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"மெதுவாக இயக்கு."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"பிளேபேக்கைத் தொடங்கு அல்லது இடைநிறுத்து."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"காட்சி விருப்பத்தேர்வு"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"ஒலிகளும் அதிர்வும்"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"அணுகல் தன்மை"</string>
- <string name="ringtone_title" msgid="760362035635084653">"மொபைலின் ரிங்டோன்"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"அழைப்பு வருகையில் அதிர்வுறு"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"டயல்பேட் ஒலிகள்"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"டயல்பேட் டோன் நீளம்"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"இயல்பு"</item>
- <item msgid="6177579030803486015">"நீளமானது"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"விரைவு பதில்கள்"</string>
- <string name="call_settings_label" msgid="313434211353070209">"அழைப்புகள்"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"அழைப்புத் தடுப்பு"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"அழைப்புத் தடுப்பு அம்சம் தற்காலிகமாக முடக்கப்பட்டுள்ளது"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"கடந்த 48 மணிநேரத்தில், இந்த ஃபோனிலிருந்து அவசர அழைப்பு எண்ணைத் தொடர்புகொண்டதால் அழைப்புத் தடுப்பு அம்சம் முடக்கப்பட்டுள்ளது. 48 மணிநேரம் கழித்து, இந்த அம்சம் தானாகவே மீண்டும் இயக்கப்படும்."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"எண்களை இறக்கு"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"பிற பயன்பாடுகளின் மூலம் சில அழைப்பாளர்களின் அழைப்புகளை தானாகவே குரலஞ்சலுக்கு அனுப்புமாறு ஏற்கனவே குறித்துள்ளீர்கள்."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"எண்களைக் காட்டு"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"இறக்கு"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"இறக்குவதில் தோல்வி"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"குரலஞ்சலைக் காப்பகப்படுத்த முடியவில்லை."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"எண்ணை அனுமதி"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"எண்ணைச் சேர்"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"இந்த எண்களின் அழைப்புகள் தடுக்கப்படுவதுடன், அவற்றின் குரலஞ்சல்களும் தானாகவே நீக்கப்படும்."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"இந்த எண்களின் அழைப்புகள் தடுக்கப்படும், ஆனால் அழைப்பாளர்களால் இன்னமும் உங்களுக்குக் குரலஞ்சல்களை அனுப்ப முடியும்."</string>
- <string name="block_list" msgid="7760188925338078011">"தடுக்கப்பட்ட எண்கள்"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> தவறானது."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> ஏற்கனவே தடுக்கப்பட்டுள்ளது."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"அழைப்புத் தடுப்பு 48 மணிநேரத்திற்கு முடக்கப்பட்டுள்ளது"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"அவசர அழைப்பு செய்திருப்பதால், அழைப்புத் தடுப்பு முடக்கப்பட்டது."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"அழைப்பு கணக்குகள்"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"இயக்கு"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"அனுமதிகளை அமை"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"விரைவு டயலை இயக்க, தொடர்புகள் அனுமதியை இயக்கவும்."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"அழைப்புப் பதிவைப் பார்க்க, ஃபோன் அனுமதியை இயக்கவும்."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"தொடர்புகளைப் பார்க்க, தொடர்புகள் அனுமதியை இயக்கவும்."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"குரலஞ்சலை அணுக, ஃபோன் அனுமதியை இயக்கவும்."</string>
- <string name="permission_no_search" msgid="84152933267902056">"தொடர்புகளைத் தேட, தொடர்புகள் அனுமதிகளை இயக்கவும்."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"அழைக்க, ஃபோன் அனுமதியை இயக்கவும்."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"முறைமை அமைப்புகளில் எழுதுவதற்கான அனுமதி ஃபோன் பயன்பாட்டுக்கு இல்லை."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"தடுக்கப்பட்டது"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> அழைப்பு செயலில் உள்ளது"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"தடு/ஸ்பேமெனப் புகாரளி"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"தடு"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"ஸ்பேமில்லை"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"அனுமதி"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"ஸ்பேம்"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g>ஐத் தடுக்கவா?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"இனி இந்த எண்ணின் அழைப்புகளும் குரலஞ்சல்களும் தடுக்கப்படும்."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"அழைப்பை ஸ்பேமாகப் புகாரளி"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"இனி இந்த எண்ணின் அழைப்புகளும் குரலஞ்சல்களும் தடுக்கப்படும். இந்த அழைப்பு ஸ்பேமாக புகாரளிக்கப்படும்."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g>ஐ அனுமதிக்கவா?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"இந்த எண் அனுமதிக்கப்பட்டு, ஸ்பேம் இல்லை என தெரிவிக்கப்படும். இனி அழைப்புகளும் குரலஞ்சல்களும் ஸ்பேமாக குறிக்கப்படாது."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g>ஐ ஏற்புப்பட்டியலில் சேர்க்கவா?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"ஏற்புப்பட்டியல்"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"இனி இந்த எண்ணின் அழைப்புகளும் குரலஞ்சல்களும் ஸ்பேமாகக் குறிக்கப்படாது. ஸ்பேம் இல்லை என இந்த எண் குறிக்கப்படும்."</string>
-</resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
deleted file mode 100644
index c63eb39eb..000000000
--- a/res/values-te/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"ఫోన్"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"ఫోన్"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"ఫోన్ డయల్‌ప్యాడ్"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"ఫోన్"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"కాల్ చరిత్ర"</string>
- <string name="action_report_number" msgid="4635403959812186162">"సరికాని నంబర్‌ను నివేదించు"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"నంబర్‌ను కాపీ చేయి"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"లిప్యంతరీకరణను కాపీ చేయి"</string>
- <string name="action_block_number" msgid="1482657602262262134">"నంబర్‌ను బ్లాక్ చేయి"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> బ్లాక్ చేయబడింది"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"నంబర్‌ను అన్‌బ్లాక్ చేయి"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> అన్‌బ్లాక్ చేయబడింది"</string>
- <string name="block_number_undo" msgid="591338370336724156">"రద్దు చేయి"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"తొలగించు"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"కాల్ చేయడానికి ముందు నంబర్‌ను సవరించు"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"కాల్ చరిత్రను తీసివేయి"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"వాయిస్ మెయిల్‌ను తొలగించు"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"వాయిస్ మెయిల్‌ను ఆర్కైవ్ చేయి"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"వాయిస్ మెయిల్ భాగస్వామ్యం చేయి"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"వాయిస్‌‍మెయిల్ తొలగించింది"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"వాయిస్ మెయిల్ ఆర్కైవ్ అయింది"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"చర్యరద్దు"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ఆర్కైవ్‌కి వెళ్లు"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"కాల్ చరిత్రను తీసివేయాలా?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"దీని వలన మీ చరిత్ర నుండి అన్ని కాల్‌లు తొలగించబడతాయి"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"కాల్ చరిత్రను క్లియర్ చేస్తోంది…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"ఫోన్"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"మిస్డ్ కాల్"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"సమాధానమివ్వని కార్యాలయ కాల్"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"మిస్డ్ కాల్‌లు"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> మిస్డ్ కాల్‌లు"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"తిరిగి కాల్ చేయి"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"సందేశం పంపు"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> వాయిస్ మెయిల్‌లు </item>
- <item quantity="one">వాయిస్ మెయిల్</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"ప్లే చేయి"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> నుండి కొత్త వాయిస్ మెయిల్"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"వాయిస్ మెయిల్‌ను ప్లే చేయడం సాధ్యపడలేదు"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"వాయిస్ మెయిల్‌ను లోడ్ చేస్తోంది…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"వాయిస్ మెయిల్‌ను ఆర్కైవ్ చేస్తోంది…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"వాయిస్ మెయిల్‌ను లోడ్ చేయడం సాధ్యపడలేదు"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"వాయిస్ మెయిల్ కాల్‌లు మాత్రమే"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"ఇన్‌కమింగ్ కాల్‌లు మాత్రమే"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"అవుట్‌గోయింగ్ కాల్‌లు మాత్రమే"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"సమాధానం ఇవ్వని కాల్‌లు మాత్రమే"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"దృశ్యమాన వాయిస్ మెయిల్"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"నంబర్‌కు కాల్ చేయకుండానే మీ వాయిస్ మెయిల్‌ని చూడండి మరియు వినండి. డేటా ఛార్జీలు వర్తించవచ్చు."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"సెట్టింగ్‌లు"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"వాయిస్ మెయిల్ నవీకరణలు అందుబాటులో లేవు"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"కొత్త వాయిస్ మెయిల్ వేచి ఉంది. ప్రస్తుతం లోడ్ చేయడం సాధ్యపడదు."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"మీ వాయిస్ మెయిల్‌ను సెటప్ చేయండి"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"ఆడియో అందుబాటులో లేదు"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"సెటప్ చేయండి"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"వాయిస్ మెయిల్‌కు కాల్ చేయండి"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"నంబర్‌ను ఎంచుకోండి"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"నంబర్‌ను ఎంచుకోండి"</string>
- <string name="make_primary" msgid="5829291915305113983">"ఈ ఎంపికను గుర్తుంచుకో"</string>
- <string name="description_search_button" msgid="3660807558587384889">"శోధించు"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"డయల్ చేయి"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"డయల్ చేయాల్సిన నంబర్"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"ప్లేబ్యాక్‌ని ప్లే చేయి లేదా ఆపివేయి"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"స్పీకర్‌ఫోన్‌ను స్విచ్ ఆన్ లేదా స్విచ్ ఆఫ్ చేయి"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"ప్లేబ్యాక్ స్థానాన్ని కావాల్సిన చోటుకి జరపండి"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"ప్లేబ్యాక్ రేటుని తగ్గించు"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"ప్లేబ్యాక్ రేటుని పెంచు"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"కాల్ చరిత్ర"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"మరిన్ని ఎంపికలు"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"డయల్ ప్యాడ్"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"అవుట్‌గోయింగ్ మాత్రమే చూపు"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"ఇన్‌కమింగ్ మాత్రమే చూపు"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"సమాధానం ఇవ్వనివి మాత్రమే చూపు"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"వాయిస్ మెయిల్‌లు మాత్రమే చూపు"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"అన్ని కాల్‌లను చూపు"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2-సెకన్ల పాజ్‌ను జోడించండి"</string>
- <string name="add_wait" msgid="3360818652790319634">"నిరీక్షణ సమయాన్ని జోడించు"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"సెట్టింగ్‌లు"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"కొత్త పరిచయం"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"అన్ని పరిచయాలు"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"కాల్ వివరాలు"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"వివరాలు అందుబాటులో లేవు"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"టచ్ టోన్ కీప్యాడ్‌ను ఉపయోగించండి"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"ప్రోగ్రెస్‌లో ఉన్న కాల్‌కు వెళ్లు"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"కాల్‌ను జోడించు"</string>
- <string name="type_incoming" msgid="6502076603836088532">"ఇన్‌కమింగ్ కాల్"</string>
- <string name="type_outgoing" msgid="343108709599392641">"అవుట్‌గోయింగ్ కాల్"</string>
- <string name="type_missed" msgid="2720502601640509542">"సమాధానం ఇవ్వని కాల్"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"ఇన్‌కమింగ్ వీడియో కాల్"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"అవుట్‌గోయింగ్ వీడియో కాల్"</string>
- <string name="type_missed_video" msgid="954396897034220545">"సమాధానమివ్వని వీడియో కాల్‌"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"వాయిస్ మెయిల్"</string>
- <string name="type_rejected" msgid="7783201828312472691">"నిరాకరించిన కాల్"</string>
- <string name="type_blocked" msgid="3521686227115330015">"బ్లాక్ చేసిన కాల్"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"ఇన్‌కమింగ్ కాల్‌లు"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"వాయిస్ మెయిల్ ప్లే చేయండి"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> పరిచయాన్ని వీక్షించండి"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g>కు కాల్ చేయండి"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> యొక్క సంప్రదింపు వివరాలు"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> కాల్‌లు."</string>
- <string name="description_video_call" msgid="2933838090743214204">"వీడియో కాల్."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g>కి SMS పంపు"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"వినని వాయిస్ మెయిల్"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"వాయిస్ శోధనను ప్రారంభించండి"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g>కు కాల్ చేయి"</string>
- <string name="unknown" msgid="740067747858270469">"తెలియదు"</string>
- <string name="voicemail" msgid="3851469869202611441">"వాయిస్ మెయిల్"</string>
- <string name="private_num" msgid="6374339738119166953">"ప్రైవేట్ నంబర్"</string>
- <string name="payphone" msgid="7726415831153618726">"పే ఫోన్"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> సెక"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> నిమి <xliff:g id="SECONDS">%s</xliff:g> సెక"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g>కి"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"ఈ నంబర్‌కు కాల్ చేయలేరు"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"వాయిస్ మెయిల్ సెటప్ చేయడానికి, మెను &gt; సెట్టింగ్‌లకు వెళ్లండి."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"వాయిస్ మెయిల్ కాల్ చేయడానికి, మొదట ఎయిర్‌ప్లైన్ మోడ్‌ను ఆపివేయండి."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"లోడ్ చేస్తోంది…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"సిమ్ కార్డు నుండి లోడ్ చేస్తోంది…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"సిమ్ కార్డు పరిచయాలు"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"పరిచయాల అనువర్తనం ఏదీ అందుబాటులో లేదు"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"వాయిస్ శోధన అందుబాటులో లేదు"</string>
- <string name="call_not_available" msgid="8941576511946492225">"ఫోన్ అనువర్తనం నిలిపివేయబడినందున ఫోన్ కాల్ చేయలేరు."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"ఈ పరికరంలో దాని కోసం అనువర్తనం ఏదీ లేదు"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"పరిచయాలను శోధించండి"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"నంబర్ జోడించండి లేదా పరిచయాల్లో శోధించండి"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"మీ కాల్ చరిత్ర ఖాళీగా ఉంది"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"కాల్ చేయి"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"మీకు సమాధానమివ్వని కాల్‌లు ఏవీ లేవు."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"మీ వాయిస్ మెయిల్ ఇన్‌బాక్స్ ఖాళీగా ఉంది."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"మీ వాయిస్ మెయిల్ ఆర్కైవ్ ఖాళీగా ఉంది."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"ఇష్టమైనవాటిని మాత్రమే చూపు"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"కాల్ చరిత్ర"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"వాయిస్ మెయిల్ ఆర్కైవ్"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"అన్నీ"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"సమాధానం ఇవ్వనవి"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"వాయిస్ మెయిల్"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"కొత్త, సరళీకృత బ్లాకింగ్"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"మిమ్మల్ని మెరుగైన రీతిలో సంరక్షించడానికి, ఫోన్ బ్లాకింగ్ పని చేసే విధానాన్ని మార్చాలి. అప్పుడు మీరు బ్లాక్ చేసిన నంబర్‌ల నుండి కాల్‌లు మరియు వచన సందేశాలు ఆపివేయబడతాయి మరియు ఆ బ్లాక్ చేయబడిన నంబర్‌లను ఇతర అనువర్తనాలకు కూడా భాగస్వామ్యం చేయవచ్చు."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"అనుమతించు"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g>ని బ్లాక్ చేయాలా?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"ఈ నంబర్ నుండి కాల్‌లు బ్లాక్ చేయబడతాయి మరియు వాయిస్‌మెయిల్‌లు స్వయంచాలకంగా తొలగించబడతాయి."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"ఈ నంబర్ నుండి కాల్‌లు బ్లాక్ చేయబడతాయి, కానీ కాలర్ ఇప్పటికీ మీకు వాయిస్‌మెయిల్‌లు పంపగలరు."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"మీరు ఇకపై ఈ నంబర్ నుండి కాల్‌లు లేదా వచన సందేశాలను స్వీకరించరు."</string>
- <string name="block_number_ok" msgid="770551992296781873">"బ్లాక్ చేయి"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g>ని అన్‌బ్లాక్ చేయాలా?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"అన్‌బ్లాక్ చేయి"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"స్పీడ్ డయల్"</string>
- <string name="tab_history" msgid="2563144697322434940">"కాల్ చరిత్ర"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"పరిచయాలు"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"వాయిస్ మెయిల్"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"ఇష్టమైనవాటి నుండి తీసివేయబడింది"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"చర్య రద్దు చేయి"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g>కు కాల్ చేయండి"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"కొత్త పరిచయాన్ని సృష్టించు"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"పరిచయానికి జోడించు"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS పంపు"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"వీడియో కాల్ చేయండి"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"నంబర్‌ను బ్లాక్ చేయి"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> కొత్త సమాధానం ఇవ్వని కాల్‌లు"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"మీ స్పీడ్ డయల్‌లో ఇంకా ఎవరూ లేరు"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"ఇష్టమైనదాన్ని జోడించండి"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"మీకు ఇప్పటికీ పరిచయాలేవీ లేవు"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"పరిచయాన్ని జోడించండి"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"అన్ని నంబర్‌లను చూడటానికి చిత్రాన్ని తాకండి లేదా మళ్లీ క్రమం చేయడానికి తాకి, ఉంచండి"</string>
- <string name="remove_contact" msgid="1080555335283662961">"తీసివేయి"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"వీడియో కాల్"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"సందేశాన్ని పంపు"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"కాల్ వివరాలు"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>కి కాల్ చేయి"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g> నుండి <xliff:g id="TIMEOFCALL">^3</xliff:g> <xliff:g id="PHONEACCOUNT">^4</xliff:g>కి మిస్డ్ కాల్ ఇచ్చారు."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g> నుండి <xliff:g id="TIMEOFCALL">^3</xliff:g> <xliff:g id="PHONEACCOUNT">^4</xliff:g>కి చేసిన కాల్‌కి సమాధానం ఇచ్చారు."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> నుండి చదవని వాయిస్ మెయిల్, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> నుండి వాయిస్ మెయిల్, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>కి <xliff:g id="TIMEOFCALL">^3</xliff:g> <xliff:g id="PHONEACCOUNT">^4</xliff:g> నుండి కాల్ చేసారు."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g>లో"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> ద్వారా"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> ద్వారా"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>లో, <xliff:g id="NUMBER">%2$s</xliff:g> ద్వారా"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="NUMBER">%2$s</xliff:g> ద్వారా <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"కాల్ చేయి"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>కి కాల్ చేయి"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>కి వీడియో కాల్."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> నుండి వచ్చిన వాయిస్ మెయిల్‌ను వినండి"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> నుండి వచ్చిన వాయిస్ మెయిల్‌ను ప్లే చేయండి"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> నుండి వచ్చిన వాయిస్ మెయిల్‌ను పాజ్ చేయండి"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> నుండి వచ్చిన వాయిస్ మెయిల్‌ను తొలగించండి"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> కొత్త వాయిస్‌మెయిల్‌లు</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> కొత్త వాయిస్‌మెయిల్</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> కోసం పరిచయాన్ని సృష్టించండి"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>ని ఇప్పటికే ఉన్న పరిచయానికి జోడించండి"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> యొక్క కాల్ వివరాలు"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"కాల్ చరిత్ర నుండి తొలగించబడింది"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"ఈ రోజు"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"నిన్న"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"పాతది"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"కాల్‌ల జాబితా"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"స్పీకర్‌ను ఆన్ చేయి."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"స్పీకర్‌ను ఆఫ్ చేయి."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"వేగంగా ప్లే చేయి."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"నెమ్మదిగా ప్లే చేయి."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"ప్లేబ్యాక్‌ను ప్రారంభించు లేదా పాజ్ చేయి."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"ప్రదర్శన ఎంపికలు"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"ధ్వనులు మరియు వైబ్రేషన్"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"ప్రాప్యత సామర్థ్యం"</string>
- <string name="ringtone_title" msgid="760362035635084653">"ఫోన్ రింగ్‌టోన్"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"కాల్‌ల కోసం వైబ్రేట్ కూడా చేయి"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"డయల్‌ప్యాడ్ టోన్‌లు"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"డయల్‌ప్యాడ్ టోన్ నిడివి"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"సాధారణం"</item>
- <item msgid="6177579030803486015">"ఎక్కువ నిడివి"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"శీఘ్ర ప్రతిస్పందనలు"</string>
- <string name="call_settings_label" msgid="313434211353070209">"కాల్‌లు"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"కాల్ బ్లాక్ చేయడం"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"కాల్ బ్లాకింగ్ తాత్కాలికంగా ఆఫ్ అయ్యింది"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"మీరు గత 48 గంటల వ్యవధిలో ఈ ఫోన్ నుండి అత్యవసర సేవలను సంప్రదించినందున కాల్ బ్లాకింగ్ నిలిపివేయబడింది. 48 గంటల వ్యవధి ముగిసిన తర్వాత ఇది స్వయంచాలకంగా పునఃప్రారంభించబడుతుంది."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"నంబర్‌లను దిగుమతి చేయి"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"మీరు ఇంతకుముందే కొంతమంది కాలర్‌లను స్వయంచాలకంగా ఇతర అనువర్తనాల ద్వారా వాయిస్ మెయిల్‌కి పంపేందుకు గుర్తు పెట్టారు."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"నంబర్‌లను వీక్షించండి"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"దిగుమతి చేయి"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"దిగుమతి విఫలమైంది"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"వాయిస్ మెయిల్‌ను ఆర్కైవ్ చేయడం విఫలం."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"నంబర్‌ను అన్‌బ్లాక్ చేయి"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"నంబర్‌ను జోడించు"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"ఈ నంబర్‌ల నుండి కాల్‌లు బ్లాక్ చేయబడతాయి మరియు వాయిస్‌మెయిల్‌లు స్వయంచాలకంగా తొలగించబడతాయి."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"ఈ నంబర్‌ల నుండి కాల్‌లు బ్లాక్ చేయబడతాయి, కానీ వాటి నుండి ఇప్పటికీ వాయిస్‌మెయిల్‌లు పంపబడవచ్చు."</string>
- <string name="block_list" msgid="7760188925338078011">"బ్లాక్ చేయబడిన నంబర్‌లు"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> చెల్లదు."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> ఇప్పటికే బ్లాక్ చేయబడింది."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"48 గంటల పాటు కాల్ బ్లాకింగ్ నిలిపివేయబడింది"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"అత్యవసర కాల్ చేసినందున నిలిపివేయబడింది."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"కాల్ చేసే ఖాతాలు"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"ఆన్ చేయి"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"అనుమతులను సెట్ చేయి"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"స్పీడ్ డయల్‌ను ప్రారంభించడానికి, పరిచయాల అనుమతిని ఆన్ చేయండి."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"మీ కాల్ లాగ్‌ను చూడటానికి, ఫోన్ అనుమతిని ఆన్ చేయండి."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"మీ పరిచయాలను చూడటానికి, పరిచయాల అనుమతిని ఆన్ చేయండి."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"మీ వాయిస్ మెయిల్‌ను ప్రాప్యత చేయడానికి, ఫోన్ అనుమతిని ఆన్ చేయండి."</string>
- <string name="permission_no_search" msgid="84152933267902056">"మీ పరిచయాలను శోధించడానికి, పరిచయాల అనుమతులను ఆన్ చేయండి."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"కాల్ చేయడానికి, ఫోన్ అనుమతిని ఆన్ చేయండి."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"సిస్టమ్ సెట్టింగ్‌లకు వ్రాయడం కోసం ఫోన్ అనువర్తనానికి అనుమతి లేదు."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"బ్లాక్ అయ్యారు"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> కాల్ సక్రియంగా ఉంది"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"బ్లాక్ చేయి/స్పామ్‌గానివేదించు"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"బ్లాక్ చేయి"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"స్పామ్ కాదు"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"అన్‌బ్లాక్ చేయి"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"స్పామ్"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g>ని బ్లాక్ చేయాలా?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"ఈ నంబర్ నుండి భవిష్యత్తులో కాల్‌లు మరియు వాయిస్ మెయిల్‌లు బ్లాక్ చేయబడతాయి."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"కాల్‌ను స్పామ్‌గా నివేదించు"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"ఈ నంబర్ నుండి భావి కాల్‌లు, వాయిస్ మెయిల్‌లు బ్లాక్ చేయబడతాయి. ఈ కాల్ స్పామ్‌గా నివేదించబడుతుంది."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g>ని అన్‌బ్లాక్ చేయాలా?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"ఈనంబర్ అన్‌బ్లా. చేయ., స్పామ్ కాదని నివేదించబడుతుంది.భావికాల్‌లు, వాయిస్‌మె. స్పామ్‌గా గుర్తించబడవు."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g>ని అనుమతి జాబితాలో ఉంచాలా?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"అనుమతి జాబితాలో ఉంచు"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"ఈ నంబర్ నుండి భావి కాల్‌లు, వాయిస్‌మె. స్పామ్‌గా గుర్తించబడవు.ఈ నంబర్ స్పామ్ కాదని నివేదించబడుతుంది."</string>
-</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
deleted file mode 100644
index 2617e7832..000000000
--- a/res/values-th/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"โทรศัพท์"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"โทรศัพท์"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"แป้นกดหมายเลขโทรศัพท์"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"โทรศัพท์"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"ประวัติการโทร"</string>
- <string name="action_report_number" msgid="4635403959812186162">"รายงานหมายเลขที่ไม่ถูกต้อง"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"คัดลอกหมายเลข"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"คัดลอกการถอดเสียงเป็นคำ"</string>
- <string name="action_block_number" msgid="1482657602262262134">"บล็อกหมายเลข"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"บล็อก <xliff:g id="NUMBER">%1$s</xliff:g> แล้ว"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"เลิกบล็อกหมายเลข"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"เลิกบล็อก <xliff:g id="NUMBER">%1$s</xliff:g> แล้ว"</string>
- <string name="block_number_undo" msgid="591338370336724156">"เลิกทำ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"ลบ"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"แก้ไขหมายเลขก่อนโทร"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"ล้างประวัติการโทร"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"ลบข้อความเสียง"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"เก็บข้อความเสียง"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"แชร์ข้อความเสียง"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"ลบข้อความเสียงแล้ว"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"ข้อความเสียงที่เก็บไว้"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"เลิกทำ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ไปที่เก็บ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"ล้างประวัติการโทรไหม"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"การดำเนินการนี้จะลบการโทรทั้งหมดออกจากประวัติของคุณ"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"กำลังล้างประวัติการโทร…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"โทรศัพท์"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"สายที่ไม่ได้รับ"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"มีสายจากที่ทำงานที่ไม่ได้รับ"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"สายที่ไม่ได้รับ"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"ไม่ได้รับ <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> สาย"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"โทรกลับ"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"ข้อความ"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> ข้อความเสียง </item>
- <item quantity="one">ข้อความเสียง</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"เล่น"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"ข้อความเสียงใหม่จาก <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"ไม่สามารถเล่นข้อความเสียงได้"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"กำลังโหลดข้อความเสียง…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"กำลังเก็บข้อความเสียง…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"ไม่สามารถโหลดข้อความเสียงได้"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"เฉพาะสายที่มีข้อความเสียง"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"เฉพาะสายเรียกเข้า"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"เฉพาะสายโทรออก"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"เฉพาะสายที่ไม่ได้รับ"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"ข้อความเสียงพร้อมภาพ"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"ดูและฟังข้อความเสียง โดยไม่ต้องโทรไปยังหมายเลขใดๆ อาจมีการเรียกเก็บค่าบริการอินเทอร์เน็ต"</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"การตั้งค่า"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"ไม่มีการอัปเดตข้อความเสียง"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"มีข้อความเสียงใหม่รออยู่ ไม่สามารถโหลดได้ในขณะนี้"</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"ตั้งค่าข้อความเสียง"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"ไม่สามารถใช้เสียงได้"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"ตั้งค่า"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"เรียกข้อความเสียง"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"เลือกหมายเลข"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"เลือกหมายเลข"</string>
- <string name="make_primary" msgid="5829291915305113983">"จำตัวเลือกนี้"</string>
- <string name="description_search_button" msgid="3660807558587384889">"ค้นหา"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"หมุนหมายเลข"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"หมายเลขที่จะโทร"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"เล่นหรือหยุดเล่น"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"เปิดหรือปิดลำโพง"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"ค้นหาตำแหน่งการเล่น"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"ลดอัตราการเล่น"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"เพิ่มอัตราการเล่น"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"ประวัติการโทร"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"ตัวเลือกเพิ่มเติม"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"แป้นหมายเลข"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"แสดงสายที่โทรออกเท่านั้น"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"แสดงสายโทรเข้าเท่านั้น"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"แสดงสายที่ไม่ได้รับเท่านั้น"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"แสดงเฉพาะข้อความเสียง"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"แสดงการโทรทั้งหมด"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"เพิ่มช่วงคั่น 2 วินาที"</string>
- <string name="add_wait" msgid="3360818652790319634">"เพิ่มการรอ"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"การตั้งค่า"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"สร้างรายชื่อใหม่"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"รายชื่อติดต่อทั้งหมด"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"รายละเอียดการโทร"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"ไม่สามารถดึงรายละเอียด"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"ใช้ปุ่มกดสัญญาณเสียง"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"กลับไปคุยสายต่อ"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"เพิ่มการโทร"</string>
- <string name="type_incoming" msgid="6502076603836088532">"สายเรียกเข้า"</string>
- <string name="type_outgoing" msgid="343108709599392641">"สายโทรออก"</string>
- <string name="type_missed" msgid="2720502601640509542">"สายที่ไม่ได้รับ"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"แฮงเอาท์วิดีโอเรียกเข้า"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"สายโทรออกแฮงเอาท์วิดีโอ"</string>
- <string name="type_missed_video" msgid="954396897034220545">"ไม่ได้รับแฮงเอาท์วิดีโอ"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"ข้อความเสียง"</string>
- <string name="type_rejected" msgid="7783201828312472691">"สายที่ปฏิเสธ"</string>
- <string name="type_blocked" msgid="3521686227115330015">"สายที่บล็อก"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"สายโทรเข้า"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"เล่นข้อความเสียง"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"ดูรายชื่อติดต่อ <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"โทรหา <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"รายละเอียดรายชื่อติดต่อสำหรับ <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> สาย"</string>
- <string name="description_video_call" msgid="2933838090743214204">"แฮงเอาท์วิดีโอ"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"ส่ง SMS ไปยัง <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"ข้อความเสียงที่ยังไม่ได้ฟัง"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"เริ่มต้นการค้นหาด้วยเสียง"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"โทร <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"ไม่ทราบ"</string>
- <string name="voicemail" msgid="3851469869202611441">"ข้อความเสียง"</string>
- <string name="private_num" msgid="6374339738119166953">"หมายเลขส่วนตัว"</string>
- <string name="payphone" msgid="7726415831153618726">"โทรศัพท์สาธารณะ"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> วินาที"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> นาที <xliff:g id="SECONDS">%s</xliff:g> วินาที"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"วันที่ <xliff:g id="DATE">%1$s</xliff:g> เวลา <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"ไม่สามารถโทรไปยังหมายเลขนี้"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"หากต้องการตั้งค่าข้อความเสียง ให้ไปที่เมนู &gt; การตั้งค่า"</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"หากต้องการฟังข้อความเสียง ให้ปิดโหมดใช้งานบนเครื่องบินก่อน"</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"กำลังโหลด..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"กำลังโหลดจากซิมการ์ด…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"รายชื่อบนซิมการ์ด"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"ไม่มีแอปรายชื่อติดต่อที่พร้อมใช้งาน"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"การค้นหาด้วยเสียงไม่พร้อมใช้งาน"</string>
- <string name="call_not_available" msgid="8941576511946492225">"ไม่สามารถโทรศัพท์ได้เนื่องจากแอปพลิเคชันโทรศัพท์ถูกปิดใช้งาน"</string>
- <string name="activity_not_available" msgid="2287665636817987623">"ไม่มีแอปสำหรับการทำงานนั้นบนอุปกรณ์นี้"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"ค้นหารายชื่อติดต่อ"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"เพิ่มหมายเลขหรือค้นหาผู้ติดต่อ"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"ประวัติการโทรว่างเปล่า"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"โทรออก"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"คุณไม่มีสายที่ไม่ได้รับ"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"กล่องข้อความเสียงว่างเปล่า"</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"ที่เก็บข้อความเสียงว่างเปล่า"</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"แสดงเฉพาะรายชื่อที่ชื่นชอบ"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"ประวัติการโทร"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"ที่เก็บข้อความเสียง"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"ทั้งหมด"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"ไม่ได้รับ"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"ข้อความเสียง"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"การบล็อกแบบใหม่ที่ใช้งานง่าย"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"เพื่อปกป้องคุณได้ดียิ่งขึ้น โทรศัพท์ฺจำเป็นต้องเปลี่ยนลักษณะการทำงานของการบล็อก ขณะนี้คุณจะไม่ได้รับสายเรียกเข้าหรือข้อความจากหมายเลขที่ถูกบล็อกอีกและอาจมีการแชร์หมายเลขนั้นกับแอปอื่น"</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"อนุญาต"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"บล็อก <xliff:g id="NUMBER">%1$s</xliff:g> ไหม"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"ระบบจะบล็อกสายเรียกเข้าจากหมายเลขนี้และลบข้อความเสียงโดยอัตโนมัติ"</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"ระบบจะบล็อกสายเรียกเข้าจากหมายเลขนี้ แต่ผู้โทรอาจยังฝากข้อความเสียงถึงคุณได้อยู่"</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"คุณจะไม่ได้รับสายเรียกเข้าหรือข้อความจากหมายเลขนี้อีก"</string>
- <string name="block_number_ok" msgid="770551992296781873">"บล็อก"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"เลิกบล็อก <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"เลิกบล็อก"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"การโทรด่วน"</string>
- <string name="tab_history" msgid="2563144697322434940">"ประวัติการโทร"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"รายชื่อติดต่อ"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"ข้อความเสียง"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"ลบจากรายการโปรด"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"เลิกทำ"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"โทร <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"สร้างรายชื่อติดต่อใหม่"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"เพิ่มในรายชื่อติดต่อ"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"ส่ง SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"ใช้แฮงเอาท์วิดีโอ"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"บล็อกหมายเลข"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"สายที่ไม่ได้รับใหม่ <xliff:g id="NUMBER">%s</xliff:g> สาย"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"ยังไม่ได้กำหนดผู้ใดในการโทรด่วน"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"เพิ่มรายการโปรด"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"คุณยังไม่มีรายชื่อติดต่อ"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"เพิ่มรายชื่อติดต่อ"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"แตะรูปภาพเพื่อดูหมายเลขทั้งหมด หรือแตะค้างไว้เพื่อเรียงลำดับใหม่"</string>
- <string name="remove_contact" msgid="1080555335283662961">"ลบ"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"แฮงเอาท์วิดีโอ"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"ส่งข้อความ"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"รายละเอียดการโทร"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"โทรไปที่ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"ไม่ได้รับสายจาก <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"รับสายจาก <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"ข้อความเสียงที่ยังไม่ได้อ่านจาก <xliff:g id="NAMEORNUMBER">^1</xliff:g> ผ่านทาง<xliff:g id="TYPEORLOCATION">^2</xliff:g> เมื่อ <xliff:g id="TIMEOFCALL">^3</xliff:g> <xliff:g id="PHONEACCOUNT">^4</xliff:g>"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"ข้อความเสียงจาก <xliff:g id="NAMEORNUMBER">^1</xliff:g> ผ่านทาง<xliff:g id="TYPEORLOCATION">^2</xliff:g> เมื่อ <xliff:g id="TIMEOFCALL">^3</xliff:g> <xliff:g id="PHONEACCOUNT">^4</xliff:g>"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"โทรหา <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"บน <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"ผ่าน <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"ผ่าน <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"ใน <xliff:g id="PHONEACCOUNT">%1$s</xliff:g> ผ่าน <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> ผ่าน <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"โทร"</string>
- <string name="description_call_action" msgid="4000549004089776147">"โทรไปที่ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"เรียกแฮงเอาท์วิดีโอไปยัง <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"ฟังข้อความเสียงจาก <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"เล่นข้อความเสียงจาก <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"หยุดข้อความเสียงจาก <xliff:g id="NAMEORNUMBER">^1</xliff:g> ชั่วคราว"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"ลบข้อความเสียงจาก <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> ข้อความเสียงใหม่</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> ข้อความเสียงใหม่</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"สร้างรายชื่อติดต่อสำหรับ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"เพิ่ม <xliff:g id="NAMEORNUMBER">^1</xliff:g> ไปยังรายชื่อติดต่อที่มีอยู่"</string>
- <string name="description_details_action" msgid="2433827152749491785">"รายละเอียดการโทรสำหรับ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"ลบออกจากประวัติการโทรแล้ว"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"วันนี้"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"เมื่อวานนี้"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"เก่ากว่า"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"รายการโทร"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"เปิดลำโพง"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"ปิดลำโพง"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"เล่นเร็วขึ้น"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"เล่นช้าลง"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"เริ่มหรือหยุดเล่นชั่วคราว"</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"ตัวเลือกการแสดง"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"เสียงและการสั่น"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"การเข้าถึง"</string>
- <string name="ringtone_title" msgid="760362035635084653">"เสียงเรียกเข้า"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"สั่นเมื่อมีสายเรียกเข้าด้วย"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"เสียงแป้นหมายเลข"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"ระยะเวลาของเสียงแป้นหมายเลข"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"ปกติ"</item>
- <item msgid="6177579030803486015">"ยาว"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"คำตอบด่วน"</string>
- <string name="call_settings_label" msgid="313434211353070209">"การโทร"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"การบล็อกสายเรียกเข้า"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"การบล็อกสายเรียกเข้าปิดชั่วคราว"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"ระบบปิดใช้การบล็อกสายเรียกเข้าเนื่องจากคุณติดต่อบริการฉุกเฉินจากโทรศัพท์เครื่องนี้ภายใน 48 ชั่วโมงที่ผ่านมา ระบบจะเปิดใช้คุณลักษณะนี้อีกครั้งโดยอัตโนมัติเมื่อครบ 48 ชั่วโมง"</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"นำเข้าหมายเลข"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"ก่อนหน้านี้คุณได้ทำเครื่องหมายว่าให้ส่งผู้โทรบางคนไปยังข้อความเสียงโดยอัตโนมัติผ่านแอปอื่นๆ"</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"ดูหมายเลข"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"นำเข้า"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"การนำเข้าล้มเหลว"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"เก็บข้อความเสียงไม่สำเร็จ"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"เลิกบล็อกหมายเลข"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"เพิ่มหมายเลข"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"ระบบจะบล็อกสายเรียกเข้าจากหมายเลขเหล่านี้และลบข้อความเสียงโดยอัตโนมัติ"</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"ระบบจะบล็อกสายเรียกเข้าจากหมายเลขเหล่านี้ แต่ผู้โทรอาจยังฝากข้อความเสียงถึงคุณได้อยู่"</string>
- <string name="block_list" msgid="7760188925338078011">"หมายเลขที่ถูกบล็อก"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> ไม่ถูกต้อง"</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> ถูกบล็อกอยู่แล้ว"</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"ปิดการบล็อกสายเรียกเข้า 48 ชั่วโมง"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"ปิดใช้งานเนื่องจากมีการโทรเข้าหมายเลขฉุกเฉิน"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"บัญชีการโทร"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"เปิด"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"ตั้งค่าสิทธิ์"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"หากต้องการเปิดใช้การโทรด่วน ให้เปิดสิทธิ์เข้าถึงรายชื่อติดต่อ"</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"หากต้องการดูประวัติการโทร ให้เปิดสิทธิ์เข้าถึงโทรศัพท์"</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"หากต้องการดูรายชื่อติดต่อ ให้เปิดสิทธิ์เข้าถึงรายชื่อติดต่อ"</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"หากต้องการเข้าถึงข้อความเสียง ให้เปิดสิทธิ์เข้าถึงโทรศัพท์"</string>
- <string name="permission_no_search" msgid="84152933267902056">"หากต้องการค้นหารายชื่อติดต่อ ให้เปิดสิทธิ์เข้าถึงรายชื่อติดต่อ"</string>
- <string name="permission_place_call" msgid="2101287782615887547">"หากต้องการโทรออก ให้เปิดสิทธิ์เข้าถึงโทรศัพท์"</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"แอปโทรศัพท์ไม่ได้รับอนุญาตให้เขียนไปยังการตั้งค่าระบบ"</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"ถูกบล็อก"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ต่อสายอยู่"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"บล็อก/รายงานจดหมายขยะ"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"บล็อก"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"ไม่ใช่จดหมายขยะ"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"เลิกบล็อก"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"สแปม"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"บล็อก <xliff:g id="NUMBER">%1$s</xliff:g> ไหม"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"สายเรียกเข้าและข้อความเสียงในอนาคตจากหมายเลขนี้จะถูกบล็อก"</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"รายงานสายนี้ว่าเป็นสแปม"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"สายเรียกเข้าและข้อความเสียงในอนาคตจากหมายเลขนี้จะถูกบล็อก สายเรียกเข้านี้จะได้รับการายงานว่าเป็นสแปม"</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"เลิกบล็อก <xliff:g id="NUMBER">%1$s</xliff:g> ไหม"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"เลิกบล็อกหมายเลขนี้และรายงานว่าไม่ใช่สแปม สายและข้อความเสียงในอนาคตจะไม่ได้รับการระบุเป็นสแปม"</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"อนุญาตพิเศษ <xliff:g id="NUMBER">%1$s</xliff:g> หรือไม่"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"รายการที่อนุญาตพิเศษ"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"สายและข้อความเสียงในอนาคตจากหมายเลขนี้จะไม่ถูกระบุเป็นสแปม หมายเลขนี้จะได้รับการรายงานว่าไม่ใช่สแปม"</string>
-</resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
deleted file mode 100644
index 7e753f113..000000000
--- a/res/values-tl/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telepono"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telepono"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Dialpad ng Telepono"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telepono"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"History ng tawag"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Iulat ang hindi wastong numero"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopyahin ang numero"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopyahin ang transkripsyon"</string>
- <string name="action_block_number" msgid="1482657602262262134">"I-block ang numero"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Na-block ang <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Alisin sa pagkaka-block ang numero"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Naalis sa pagkaka-block ang <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="block_number_undo" msgid="591338370336724156">"I-UNDO"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"I-delete"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"I-edit ang numero bago tumawag"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"I-clear ang history ng tawag"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"I-delete ang voicemail"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"I-archive ang voicemail"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Ibahagi ang voicemail"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Na-delete voicemail"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Na-archive ang voicemail"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"I-UNDO"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"PUMUNTA SA ARCHIVE"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"I-clear ang history ng tawag?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Idi-delete nito ang lahat ng tawag mula sa iyong history"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Kini-clear ang history ng tawag…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telepono"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Hindi nasagot na tawag"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Hindi nasagot na tawag sa trabaho"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Mga hindi nasagot na tawag"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> (na) hindi nasagot na tawag"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Tawagan"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Mensahe"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> Voicemail </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> na Voicemail </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"I-play"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Bagong voicemail mula kay <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Hindi ma-play ang voicemail"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Nilo-load ang voicemail…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Ina-archive ang voicemail…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Hindi ma-load ang voicemail"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Mga tawag lang na may voicemail"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Mga papasok na tawag lang"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Mga papalabas na tawag lang"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Mga hindi nasagot na tawag lang"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Visual voicemail"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Tingnan at pakinggan ang iyong voicemail, nang hindi kinakailangang tumawag sa isang numero. Maaaring may mga singilin para sa data."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Mga setting"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Walang available na mga update sa voicemail"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"May naghihintay na bagong voicemail. Hindi ma-load sa ngayon."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"I-set up ang iyong voicemail"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Hindi available ang audio"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"I-set up"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Tawagan ang voicemail"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Pumili ng numero"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Pumili ng numero"</string>
- <string name="make_primary" msgid="5829291915305113983">"Tandaan ang pagpipiliang ito"</string>
- <string name="description_search_button" msgid="3660807558587384889">"maghanap"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"mag-dial"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"numerong ida-dial"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"I-play o ihinto ang playback"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"I-on o i-off ang speakerphone"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Hanapin ang posisyon ng playback"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Bagalan ang playback"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Bilisan ang playback"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"History ng Tawag"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Higit pang mga pagpipilian"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"dial pad"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Ipakita lang ang papalabas"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Ipakita lang ang paparating"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Ipakita lang ang hindi nasagot"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Ipakita lamang ang mga voicemail"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Ipakita ang lahat ng tawag"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Magdagdag ng pag-pause na 2-seg"</string>
- <string name="add_wait" msgid="3360818652790319634">"Magdagdag ng paghihintay"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Mga Setting"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Bagong contact"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Lahat ng contact"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Mga detalye ng tawag"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Hindi available ang mga detalye"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Gumamit ng touch tone na keypad"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Bumalik sa kasalukuyang tawag"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Magdagdag ng tawag"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Papasok na tawag"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Papalabas na tawag"</string>
- <string name="type_missed" msgid="2720502601640509542">"Hindi nasagot na tawag"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Papasok na video call"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Papalabas na video call"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Hindi nasagot na video call"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Voicemail"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Tinanggihang tawag"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Naka-block na tawag"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Mga paparating na tawag"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"I-play ang voicemail"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Tingnan ang contact na si <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Tawagan si <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Mga detalye sa pakikipag-ugnayan para kay/sa <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> (na) tawag."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Video call."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Magpadala ng SMS kay <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Hindi pa naririnig na voicemail"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Simulan ang paghahanap gamit ang boses"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Tumawag sa <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Di-kilala"</string>
- <string name="voicemail" msgid="3851469869202611441">"Voicemail"</string>
- <string name="private_num" msgid="6374339738119166953">"Pribadong numero"</string>
- <string name="payphone" msgid="7726415831153618726">"Payphone"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> sec"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> sec"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> ng <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Hindi matawagan ang numerong ito"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Upang mag-set up ng voicemail, pumunta sa Menu &gt; Mga Setting."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Upang tumawag sa voicemail, i-off muna ang Airplane mode."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Naglo-load…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Naglo-load mula sa SIM card…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"Mga contact sa SIM card"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Walang available na app ng mga contact"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Hindi available ang paghahanap gamit ang boses"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Hindi makagawa ng tawag sa telepono dahil naka-disable ang application na Telepono."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Walang app para doon sa device na ito"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Maghanap ng mga contact"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Magdagdag ng numero, maghanap sa contact"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Walang laman ang iyong history ng tawag"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Tumawag"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Wala kang mga hindi nasagot na tawag."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Walang laman ang iyong voicemail inbox."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Walang laman ang archive ng voicemail."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Mga paborito lang ang ipakita"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"History ng Tawag"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Archive ng Voicemail"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Lahat"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Di nasagot"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Voicemail"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Bago at pinasimpleng pag-block"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Upang mas maprotektahan ka, kailangang baguhin ng Telepono ang paggana ng pag-block. Pipigilan na ngayon ng iyong mga naka-block na numero ang mga tawag at text, at maaaring ibahagi ang mga ito sa iba pang mga app."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Payagan"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Gusto mo bang i-block ang <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Iba-block ang mga tawag mula sa numerong ito at awtomatikong ide-delete ang mga voicemail."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Iba-block ang mga tawag mula sa numerong ito, ngunit makakapag-iwan pa rin sa iyo ng mga voicemail ang tumatawag."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Hindi ka na makakatanggap ng mga tawag o text mula sa numerong ito."</string>
- <string name="block_number_ok" msgid="770551992296781873">"I-BLOCK"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Gusto mo bang alisin sa pagkaka-block ang <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ALISIN SA PAGKAKA-BLOCK"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Speed dial"</string>
- <string name="tab_history" msgid="2563144697322434940">"History ng Tawag"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Mga Contact"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Voicemail"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Inalis sa mga paborito"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"I-undo"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Tumawag sa <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Gumawa ng bagong contact"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Idagdag sa isang contact"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Magpadala ng SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Gumawa ng video call"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"I-block ang numero"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> (na) bagong hindi nasagot na tawag"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Wala pang tao sa iyong speed dial"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Magdagdag ng paborito"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Wala ka pang sinumang mga contact"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Magdagdag ng contact"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Pindutin ang larawan upang makita ang lahat ng numero o pindutin nang matagal upang ayusing muli"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Alisin"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Mag-video call"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Magpadala ng mensahe"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Mga detalye ng tawag"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Tawagan ang/si <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Hindi nasagot na tawag mula kay/sa <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Nasagot na tawag mula kay/sa <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Hindi pa nababasang voicemail mula sa/kay <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Voicemail mula sa/kay <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Tawag kay/sa <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"sa <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"sa pamamagitan ng <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"sa pamamagitan ng <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"sa <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, sa pamamagitan ng <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> sa pamamagitan ng <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Tumawag"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Tawagan ang/si <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"I-video call si/ang <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Makinig sa voicemail mula sa/kay <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"I-play ang voicemail mula sa/kay <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"I-pause ang voicemail mula sa/kay <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"I-delete ang voicemail mula sa/kay <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> bagong voicemail</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> na bagong voicemail</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Gumawa ng contact para kay/sa <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Idagdag si/ang <xliff:g id="NAMEORNUMBER">^1</xliff:g> sa umiiral nang contact"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Mga detalye ng tawag para sa/kay <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Na-delete mula sa history ng tawag"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Ngayon"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Kahapon"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Mas Luma"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Listahan ng mga tawag"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"I-on ang speaker."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"I-off ang speaker."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Mag-play nang mas mabilis."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Mag-play nang mas mabagal."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Simulan o i-pause ang pag-playback."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Mga display option"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Tunog at pag-vibrate"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Pagiging Naa-access"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Ringtone ng telepono"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Mag-vibrate din para sa tawag"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Mga dialpad tone"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Tagal ng tunog ng dialpad"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"Mahaba"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Mga mabilisang tugon"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Mga Tawag"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Pagba-block ng tawag"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Pansamantalang na-off ang call blocking"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Na-disable ang pagba-block ng tawag dahil nakipag-ugnayan ka sa mga pang-emergency na serbisyo mula sa teleponong ito sa nakalipas na 48 oras. Awtomatiko itong muling i-enable kapag nag-expire na ang 48 oras."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"I-import ang mga numero"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Minarkahan mo na dati ang ilang tumatawag na awtomatikong ipadala sa voicemail sa pamamagitan ng iba pang mga app."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Tingnan ang Numero"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"I-import"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Hindi nakapag-import"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Nabigong i-archive ang voicemail."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Alisin sa pagkaka-block ang numero"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Magdagdag ng numero"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Iba-block ang mga tawag mula sa mga numerong ito at awtomatikong ide-delete ang mga voicemail."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Iba-block ang mga tawag mula sa numerong ito, ngunit makakapag-iwan pa rin sila sa iyo ng mga voicemail."</string>
- <string name="block_list" msgid="7760188925338078011">"Mga naka-block na numero"</string>
- <string name="invalidNumber" msgid="619058581062192851">"Hindi wasto ang <xliff:g id="NUMBER">%1$s</xliff:g>."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"Naka-block na ang <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Na-disable ang pagba-block ng tawag sa loob ng 48 oras"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Na-disable dahil tumawag para sa emergency."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Account sa pagtawag"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"I-on"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Magtakda ng mga pahintulot"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Upang i-enable ang speed dial, i-on ang pahintulot ng Mga Contact."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Upang makita ang iyong log ng tawag, i-on ang pahintulot ng Telepono."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Upang makita ang iyong mga contact, i-on ang pahintulot ng Mga Contact."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Upang ma-access ang iyong voicemail, i-on ang pahintulot ng Telepono."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Upang hanapin ang iyong mga contact, i-on ang mga pahintulot sa Mga Contact."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Upang tumawag, i-on ang pahintulot ng Telepono."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Walang pahintulot ang app ng Telepono na mag-write sa mga setting ng system."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Naka-block"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"Aktibo si/ang <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"I-block/iulat ang spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"I-block"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Hindi spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Alisin sa pagkaka-block"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"I-block si <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Iba-block ang darating na tawag at voicemail mula sa numerong ito."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Iulat ang tawag bilang spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Iba-block ang mga darating na tawag at voicemail mula sa numerong ito. Iuulat na spam ang tawag."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Alisin sa pagkaka-block si <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Aalisin sa pagkaka-block ang numerong ito at iuulat na di spam. Di kikilalaning spam ang darating na tawag at voicemail."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"I-whitelist ang <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"I-whitelist"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Di kikilalaning spam ang darating na tawag at voicemail mula sa numerong ito. Iuulat na di spam ang numero."</string>
-</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
deleted file mode 100644
index 636642814..000000000
--- a/res/values-tr/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Telefon Tuş Takımı"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Çağrı geçmişi"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Hatalı numarayı bildir"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Numarayı kopyala"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Çeviri yazıyı kopyala"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Numarayı engelle"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> numaralı telefon engellendi"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Numaranın engellemesini kaldır"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> numaralı telefonun engellemesi kaldırıldı"</string>
- <string name="block_number_undo" msgid="591338370336724156">"GERİ AL"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Sil"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Telefon etmeden önce numarayı düzenle"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Çağrı geçmişini temizle"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Sesli mesajı sil"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Sesli mesajı arşivle"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Sesli mesaj paylaş"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Sesli mesaj silindi"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Sesli mesaj arşivlendi"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"GERİ AL"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ARŞİVE GİT"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Çağrı geçmişi temizlensin mi?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Bu işlem, geçmişinizdeki tüm çağrıları silecek"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Çağrı geçmişi temizleniyor…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Telefon"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Cevapsız çağrı"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"İşle ilgili cevapsız çağrı"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Cevapsız çağrılar"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> cevapsız çağrı"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Geri ara"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"İleti"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Sesli Mesaj </item>
- <item quantity="one">Sesli Mesaj</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Oynat"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Yeni sesli mesj gönderen: <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Sesli mesaj oynatılamadı"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Sesli mesaj yükleniyor…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Sesli mesaj arşivleniyor…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Sesli mesaj yüklenemedi"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Yalnızca sesli mesaj içeren çağrılar"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Yalnızca gelen çağrılar"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Yalnızca giden çağrılar"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Yalnızca cevapsız çağrılar"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Görsel sesli mesaj"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Herhangi bir numarayı aramak zorunda kalmadan sesli mesajınızı görün ve dinleyin. Bu işlem için veri ücreti alınabilir."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Ayarlar"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Sesli mesaj güncellemesi yok"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Yeni sesli mesaj bekliyor. Şu anda yüklenemiyor."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Sesli mesajınızı yapılandırın"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Ses kullanılamıyor"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Yapılandır"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Sesli mesaj ara"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Numarayı seçin"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Numarayı seçin"</string>
- <string name="make_primary" msgid="5829291915305113983">"Bu tercihi anımsa"</string>
- <string name="description_search_button" msgid="3660807558587384889">"ara"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"çevir"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"çevrilecek numara"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Oynatmayı başlat veya durdur"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Hoperlörü aç veya kapat"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Oynatma konumunu ayarla"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Oynatma hızını azalt"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Oynatma hızını artır"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Çağrı Geçmişi"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Diğer seçenekler"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"tuş takımı"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Yalnızca gidenleri göster"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Yalnızca gelenleri göster"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Yalnızca cevapsızları göster"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Yalnızca sesli msajları göster"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Tüm çağrıları göster"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2 saniyelik duraklama ekle"</string>
- <string name="add_wait" msgid="3360818652790319634">"Bekleme ekle"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Ayarlar"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Yeni kişi"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Tüm kişiler"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Çağrı ayrıntıları"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Ayrıntı yok"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Telefon tuş takımını kullan"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Çağrıya dön"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Çağrı ekle"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Gelen çağrı"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Giden çağrı"</string>
- <string name="type_missed" msgid="2720502601640509542">"Cevapsız çağrı"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Gelen video görüşmesi isteği"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Giden video görüşmesi isteği"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Cevapsız video görüşmesi isteği"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Sesli mesaj"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Reddedilen çağrı"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Engellenen çağrı"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Gelen çağrılar"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Sesli mesajı oynat"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Kişiyi görüntüle: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Ara: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> için kişi ayrıntıları"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> çağrı."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Video görüşmesi."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Şuraya SMS gönder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Dinlenilmemiş sesli mesaj"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Sesli arama başlat"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Çağrı yap: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Bilinmiyor"</string>
- <string name="voicemail" msgid="3851469869202611441">"Sesli Mesaj"</string>
- <string name="private_num" msgid="6374339738119166953">"Özel numara"</string>
- <string name="payphone" msgid="7726415831153618726">"Ankesörlü telefon"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> sn."</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> dk. <xliff:g id="SECONDS">%s</xliff:g> sn."</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Bu numara aranamaz"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Sesli mesajı yapılandırmak için Menü &gt; Ayarlar\'a gidin."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Sesli mesaja çağrı yapmak için öncelikle Uçak modunu kapatın."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Yükleniyor..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"SIM karttan yükleniyor..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM kart kişileri"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Kullanılabilir rehber uygulaması yok"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Sesli arama kullanılamaz"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Telefon uygulaması devre dışı bırakıldığından telefon edilemiyor."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Bu cihazda bu işlem için uygun uygulama yok"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Kişilerde ara"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Numara ekleyin veya kişi arayın"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Çağrı geçmişiniz boş"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Telefon et"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Cevapsız çağirınız yok."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Sesli mesaj gelen kutunuz boş."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Sesli mesaj arşiviniz boş."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Yalnızca favori kişileri göster"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Çağrı Geçmişi"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Sesli Mesaj Arşivi"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Tümü"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Cevapsız"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Sesli Mesaj"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Yeni, basitleştirlmş engelleme"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Daha iyi korunmanız için engelleme işlevinin çalışma biçiminin Telefon tarafından değiştirilmesi gerekmektedir. Engellediğiniz numaralardan artık çağrı ve kısa mesaj almayacaksınız. Ayrıca bu numaralar başka uygulamalarla paylaşılabilir."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"İzin ver"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> numaralı telefon engellensin mi?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Bu numaradan gelen çağrılar engellenecek ve sesli mesajlar otomatik olarak silinecektir."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Bu numaradan gelen çağrılar engellenecek, ancak arayan kişi yine de size sesli mesaj bırakabilecektir."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Artık bu numaradan sesli arama veya kısa mesaj almayacaksınız."</string>
- <string name="block_number_ok" msgid="770551992296781873">"ENGELLE"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> numaralı telefonun engellemesi kaldırılsın mı?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"ENGELLEMEYİ KALDIR"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Hızlı arama"</string>
- <string name="tab_history" msgid="2563144697322434940">"Çağrı Geçmişi"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kişiler"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Sesli mesaj"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Favorilerden kaldırıldı"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Geri al"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Telefon et: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Yeni kişi oluştur"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Bir kişiye ekle"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS gönder"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Video görüşmesi yap"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Numarayı engelle"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> yeni cevapsız çağrı"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Hızlı arama listenizde henüz kimse yok"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Favori ekle"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Henüz kayıtlı kişi yok"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Kişi ekle"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Tüm numaraları görmek için resme dokunun veya yeniden sıralamak için dokunup basılı tutun"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Kaldır"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Video görüşmesi"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"İleti gönder"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Çağrı ayrıntıları"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Telefon et: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Cevapsız çağrı: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Cevaplanan çağrı: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Dinlenmemiş sesli posta: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Sesli posta: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Yapılan çağrı: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> üzerinde"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> kullanılarak"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> kullanılarak"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="NUMBER">%2$s</xliff:g> kullanılarak <xliff:g id="PHONEACCOUNT">%1$s</xliff:g> hesabında"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="NUMBER">%2$s</xliff:g> kullanılarak <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Telefon et"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Telefon et: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> ile video görüşmesi yap."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Şu numaradan gelen sesli mesajı dinle: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> tarafından kaydedilmiş sesli mesajı çal"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> tarafından kaydedilmiş sesli mesajı duraklat"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> tarafından kaydedilmiş sesli mesajı sil"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> yeni sesli mesaj</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> yeni sesli mesaj</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> için kişi oluştur"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> bilgisini mevcut kişiye ekle"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> için çağrı ayrıntıları"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Çağrı geçmişinden silindi"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Bugün"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Dün"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Daha eski"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Çağrı listesi"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Hoparlörü açın."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Hoparlörü kapatın."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Daha hızlı çalın."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Daha yavaş çalın."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Çalmayı başlatın veya duraklatın."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Görüntüleme seçenekleri"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Ses ve titreşim"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Erişilebilirlik"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Telefon zil sesi"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Çağrılarda ayrıca titret"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tuş takımı tonları"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Tuş takımı ses uzunluğu"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Normal"</item>
- <item msgid="6177579030803486015">"Uzun"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Hızlı yanıtlar"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Çağrılar"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Çağrı engelleme"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Çağrı engelleme geçici olarak kapalı"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Son 48 saat içinde bu telefondan acil servislerle iletişimde bulunduğunuz için çağrı engelleme modu devre dışı bırakıldı. 48 saatlik süre dolduktan sonra çağrı engelleme modu otomatik olarak tekrar etkinleştirilecektir."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Numaraları içe aktar"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Bazı arayanları, diğer uygulamalar aracılığıyla otomatik olarak sesli mesaj gönderilmesi için önceden işaretlediniz."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Numaraları Görüntüle"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"İçe aktar"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"İçe aktarma başarısız oldu"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Sesli mesaj arşivlenemedi."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Numaranın engellemesini kaldır"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Numara ekle"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Bu numaralardan gelen çağrılar engellenecek ve sesli mesajlar otomatik olarak silinecektir."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Bu numaralardan gelen çağrılar engellenecek, ancak arayan kişiler yine de size sesli mesaj bırakabileceklerdir."</string>
- <string name="block_list" msgid="7760188925338078011">"Engellenen numaralar"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> numaralı telefon geçersiz."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> numaralı telefon zaten engellendi."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Çağrı engelleme 48 saat süreyle devre dışı bırakıldı"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Acil durum çağrısı yapıldığından devre dışı bırakıldı."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Çağrı hesapları"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Aç"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"İzinleri ayarla"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Hızlı aramayı etkinleştirmek için Kişiler iznini açın."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Çağrı günlüğünüzü görmek için Telefon iznini açın."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Kişilerinizi görmek için Kişiler iznini açın."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Sesli mesajınıza erişmek için Telefon iznini açın."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Kişilerinizde arama yapmak için Kişiler izinlerini açın."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Telefon etmek için Telefon iznini açın."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Telefon uygulamasının sistem ayarlarına yazma izni yok."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Engellendi"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> etkin durumda"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Engelle/spam bildir"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Engelle"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Spam değil"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Engellemeyi kaldır"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> engellensin mi?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Bu numaradan gelecek olan çağrılar ve sesli mesajlar engellenecek."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Çağrıyı spam olarak bildir"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Bu numaradan gelecek çağrılar ve sesli mesajlar engellenecek. Bu çağrı spam olarak bildirilecek."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> numaralı telefonun engellemesi kaldırılsın mı?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Bu numaranın engellemesi kaldırılacak. Gelecekteki çağrılarla sesli mesajlar spam olarak tanımlanmayacak."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> numaralı telefon beyaz listeye eklensin mi?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Beyaz listeye ekle"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Bu numaradan gelecek çağrılarla sesli mesajlar spam olarak tanımlanmayacak. Numaranın spam olmadığı bildirilecek."</string>
-</resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
deleted file mode 100644
index 54690ed4b..000000000
--- a/res/values-uk/strings.xml
+++ /dev/null
@@ -1,292 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Телефон"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Телефон"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Цифрова клавіатура"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Тел."</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Історія дзвінків"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Повідомити про неправильний номер"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Копіювати номер"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Копіювати транскрипцію"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Заблокувати номер"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Номер <xliff:g id="NUMBER">%1$s</xliff:g> заблоковано"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Розблокувати номер"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Номер <xliff:g id="NUMBER">%1$s</xliff:g> розблоковано"</string>
- <string name="block_number_undo" msgid="591338370336724156">"ВІДМІНИТИ"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Видалити"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Редагувати номер перед викликом"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Очистити історію дзвінків"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Видалити голосову пошту"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Архівувати голосову пошту"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Надіслати голосову пошту"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Видалено"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Голосову пошту заархівовано"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"ВІДМІНИТИ"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ДО АРХІВУ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Очистити історію дзвінків?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"З історії буде видалено всі дзвінки"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Очищення історії дзвінків…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Номер телефону"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Пропущений виклик"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Пропущений дзвінок на робочий телефон"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Пропущені виклики"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"Пропущено викликів: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Передзвонити"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Повідомлення"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> голосове повідомлення </item>
- <item quantity="few"> <xliff:g id="COUNT">%1$d</xliff:g> голосові повідомлення </item>
- <item quantity="many"> <xliff:g id="COUNT">%1$d</xliff:g> голосових повідомлень </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> голосових повідомлень </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Відтворити"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Нова голосова пошта від <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Не вдалося відтворити голосову пошту"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Завантаження голосової пошти…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Архівування голосової пошти…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Не вдалося завантажити голосову пошту"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Лише виклики з голосовою поштою"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Лише вхідні виклики"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Лише вихідні виклики"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Лише пропущені виклики"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Візуальна голосова пошта"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Переглядайте та слухайте голосову пошту, не телефонуючи. За передавання даних може стягуватися плата."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Налаштування"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Нові повідомлення голосової пошти недоступні"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Є нове повідомлення голосової пошти. Помилка завантаження."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Налаштуйте голосову пошту"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Аудіо недоступне"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Налаштувати"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Дзвон.на голос.пошту"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Вибрати номер"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Вибрати номер"</string>
- <string name="make_primary" msgid="5829291915305113983">"Пам\'ятати цей вибір"</string>
- <string name="description_search_button" msgid="3660807558587384889">"пошук"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"дзвон."</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"номер для набору"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Почати або зупинити відтворення"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Увімкнути або вимкнути гучний зв’язок"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Шукати місце відтворення"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Зменшити швидкість відтворення"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Збільшити швидкість відтворення"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Історія викликів"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Інші варіанти"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"цифрова клавіатура"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Показувати лише вихідні"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Показувати лише вхідні"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Показувати лише пропущені"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Показувати лише голосову пошту"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Показувати всі виклики"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Додати 2-сек. паузу"</string>
- <string name="add_wait" msgid="3360818652790319634">"Додати паузу"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Налаштування"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Новий контакт"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Усі контакти"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Деталі виклику"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Деталі недоступні"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Використовувати тональний набір"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Повернутися до поточного виклику"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Додати виклик"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Вхідний виклик"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Вихідний виклик"</string>
- <string name="type_missed" msgid="2720502601640509542">"Пропущений виклик"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Вхідний відеодзвінок"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Вихідний відеодзвінок"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Пропущений відеодзвінок"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Голосова пошта"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Відхилений виклик"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Заблокований виклик"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Вхідні виклики"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Відтворити голосову пошту"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Переглянути контакт <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Дзвонити: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Відомості про контакт <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"Викликів: <xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Відеодзвінок."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Надіслати SMS контакту <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Непрослухана голосова пошта"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Розпочати голосовий пошук"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Подзвонити на <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Невідомий"</string>
- <string name="voicemail" msgid="3851469869202611441">"Голосова пошта"</string>
- <string name="private_num" msgid="6374339738119166953">"Приватний номер"</string>
- <string name="payphone" msgid="7726415831153618726">"Таксофон"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> с"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> хв <xliff:g id="SECONDS">%s</xliff:g> с"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> о <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Не можна телефонувати на цей номер"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Щоб налаштувати голосову пошту, перейдіть у Меню &gt; Налаштування."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Щоб перевірити голосову пошту, спочатку вимкніть режим польоту."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Завантаження..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Завантаж. із SIM-карти…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"Контакти SIM-карти"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Немає додатка з контактами"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Голосовий пошук недоступний"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Не вдається здійснити дзвінок, оскільки додаток Телефон вимкнено."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"На цьому пристрої немає додатка, який може виконати цю дію"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Пошук серед контактів"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Введіть або знайдіть номер"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Немає історії дзвінків"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Телефонувати"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Немає пропущених дзвінків."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Немає голосової пошти."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Архів голосової пошти порожній."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Показати лише вибрані"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Історія дзвінків"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Архів голосової пошти"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Усі"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Пропущені"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Голосова пошта"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Нове простіше блокування"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Задля кращого захисту додаток Телефон має змінити параметри блокування. Із заблокованих номерів не надходитимуть виклики й SMS. Інші додатки також можуть мати доступ до списку цих номерів."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Дозволити"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Заблокувати номер <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Виклики з цього номера блокуватимуться, а голосові повідомлення автоматично видалятимуться."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Виклики з цього номера блокуватимуться, але абонент зможе залишати голосові повідомлення."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Ви більше не отримуватиме виклики й SMS із цього номера."</string>
- <string name="block_number_ok" msgid="770551992296781873">"БЛОКУВАТИ"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Розблокувати номер <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"РОЗБЛОКУВАТИ"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Швидкий набір"</string>
- <string name="tab_history" msgid="2563144697322434940">"Історія дзвінків"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Контакти"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Голосова пошта"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Вилучено з вибраного"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Відмінити"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Набрати <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Створити новий контакт"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Додати в контакти"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Надіслати SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Здійснити відеодзвінок"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Заблокувати номер"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"Нових пропущених дзвінків: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Немає номерів для швидкого набору"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Додати номер"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Ще немає контактів"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Додати контакт"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Торкніться зображення, щоб побачити всі номери, або натисніть і утримуйте, щоб змінити порядок"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Видалити"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Відеодзвінок"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Надіслати повідомлення"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Деталі виклику"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>: телефонувати"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Пропущений дзвінок: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Прийнятий дзвінок: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Непрочитане голосове повідомлення: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Голосове повідомлення: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Вихідний дзвінок: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"Обліковий запис: <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"на номер <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"на номер <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"в обліковому записі <xliff:g id="PHONEACCOUNT">%1$s</xliff:g> на номер <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> на номер <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Виклик"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Телефонувати: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Запросити контакт <xliff:g id="NAMEORNUMBER">^1</xliff:g> на відеодзвінок."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Слухати голосову пошту: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>: слухати голосову пошту"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>: призупинити голосову пошту"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>: видалити голосову пошту"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> нове повідомлення голосової пошти</item>
- <item quantity="few"><xliff:g id="COUNT_1">%d</xliff:g> нові повідомлення голосової пошти</item>
- <item quantity="many"><xliff:g id="COUNT_1">%d</xliff:g> нових повідомлень голосової пошти</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> нового повідомлення голосової пошти</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>: створити контакт"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>: додати до наявного контакта"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>: деталі виклику"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Видалено з історії дзвінків"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Сьогодні"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Учора"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Старіші"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Список дзвінків"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Увімкнути динамік."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Вимкнути динамік."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Відтворювати швидше."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Відтворювати повільніше."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Почати або призупинити відтворення."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Параметри відображення"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Звуки та вібрація"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Спеціальні можливості"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Сигнал дзвінка телефона"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Також вібрувати для дзвінків"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Звуки цифрової клавіатури"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Тривалість сигналу цифрової клавіатури"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Стандартний сигнал"</item>
- <item msgid="6177579030803486015">"Довгий сигнал"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Швидкі відповіді"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Дзвінки"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Блокування викликів"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Блокування дзвінків тимчасово вимкнено"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Блокування дзвінків вимкнено, оскільки ви зверталися з цього телефона в екстрені служби протягом останніх 48 годин. Ця функція автоматично ввімкнеться, коли мине 48 годин."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Імпортувати номери"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Ви позначили абонентів, чиї дзвінки мають автоматично спрямовуватися на голосову пошту через інші додатки."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Переглянути номери"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Імпортувати"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Не вдалось імпортувати"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Не вдалося заархівувати голосову пошту."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Розблокувати номер"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Додати номер"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Виклики з цих номерів блокуватимуться, а голосові повідомлення автоматично видалятимуться."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Виклики з цих номерів блокуватимуться, але абоненти зможуть залишати голосові повідомлення."</string>
- <string name="block_list" msgid="7760188925338078011">"Заблоковані номери"</string>
- <string name="invalidNumber" msgid="619058581062192851">"Номер <xliff:g id="NUMBER">%1$s</xliff:g> недісний."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"Номер <xliff:g id="NUMBER">%1$s</xliff:g> уже заблоковано."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Блокування викликів вимкнено на 48 годин"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Блокування вимкнено, оскільки ви зробили екстрений виклик."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Обл. записи для дзвінків"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Увімкнути"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Налаштувати дозволи"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Щоб активувати швидкий набір, увімкніть дозвіл \"Контакти\"."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Щоб переглянути журнал викликів, увімкніть дозвіл \"Телефон\"."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Щоб переглянути контакти, увімкніть дозвіл \"Контакти\"."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Щоб користуватися голосовою поштою, увімкніть дозвіл \"Телефон\"."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Щоб шукати контакти, увімкніть дозвіл \"Контакти\"."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Щоб зателефонувати, увімкніть дозвіл \"Телефон\"."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Додаток Телефон не має дозволу змінювати системні налаштування."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Заблоковано"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>: дзвінок активний"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Заблокувати/позначити як спам"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Заблокувати"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Не спам"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Розблокувати"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Спам"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Заблокувати номер <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Подальші виклики й голосова пошта з цього номера блокуватимуться."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Позначити виклик як спам"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Подальші виклики й голосова пошта з цього номера блокуватимуться. Цей виклик буде позначено як спам."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Розблокувати номер <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Цей номер буде розблоковано. Виклики, голосова пошта з нього (і він сам) не позначатимуться як спам."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Додати номер <xliff:g id="NUMBER">%1$s</xliff:g> до списку дозволених?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Додати в список дозволених"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Подальші виклики й голосова пошта з цього номера не позначатимуться як спам, як і сам номер."</string>
-</resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
deleted file mode 100644
index 807a38320..000000000
--- a/res/values-ur/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"فون"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"فون"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"فون ڈائل پیڈ"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"فون"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"کال کی سرگزشت"</string>
- <string name="action_report_number" msgid="4635403959812186162">"غلط نمبر کی رپورٹ کریں"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"نمبر کاپی کریں"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"ٹرانسکرپشن کاپی کریں"</string>
- <string name="action_block_number" msgid="1482657602262262134">"نمبر مسدود کریں"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> مسدود ہو گیا"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"نمبر غیر مسدود کریں"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> غیر مسدود ہو گیا"</string>
- <string name="block_number_undo" msgid="591338370336724156">"کالعدم کریں"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"حذف کریں"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"کال کرنے سے پہلے نمبر میں ترمیم کریں"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"کال کی سرگزشت صاف کریں"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"صوتی میل حذف کریں"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"صوتی میل کو آرکائیو کریں"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"صوتی میل کا اشتراک کریں"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"صوتی میل حذف ہو گئی"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"صوتی میل آرکائیو ہو گئی"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"کالعدم کریں"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"آرکائیو پر جائیں"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"کال کی سرگزشت صاف کریں؟"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"یہ آپ کی سرگزشت سے سبھی کالز کو حذف کر دے گا"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"کال کی سرگزشت صاف کی جا رہی ہے…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"فون"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"چھوٹی ہوئی کال"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"کام سے متعلق چھوٹی ہوئی کال"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"چھوٹی ہوئی کالیں"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> چھوٹی ہوئی کالیں"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"واپس کال کریں"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"پیغام"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> صوتی میلز </item>
- <item quantity="one">صوتی میل</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"چلائیں"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>، <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g> کی جانب سے نیا صوتی میل"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"صوتی میل نہیں چلا سکا"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"صوتی میل لوڈ ہو رہا ہے…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"صوتی میل آرکائیو ہو رہی ہے…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"صوتی میل لوڈ نہیں کیا جا سکا"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"صرف صوتی میل والی کالیں"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"صرف آنے والی کالیں"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"صرف باہر جانے والی کالیں"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"صرف چھوٹی ہوئی کالیں"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"بصری صوتی میل"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"اپنی صوتی میل دیکھیں اور سنیں، آپ کو اس نمبر پر کال نہیں کرنی پڑے گی۔ ڈیٹا چارجز کا اطلاق ہو سکتا ہے۔"</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"ترتیبات"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"صوتی میل کی اپ ڈیٹس دستیاب نہیں ہیں"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"نئے صوتی میل کے منتظر۔ ابھی لوڈ نہیں کیا جا سکتا۔"</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"اپنے صوتی میل کو ترتیب دیں"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"آڈیو دستیاب نہیں ہے"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"ترتیب دیں"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"صوتی میل کو کال کریں"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"نمبر منتخب کریں"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"نمبر منتخب کریں"</string>
- <string name="make_primary" msgid="5829291915305113983">"یہ انتخاب یاد رکھیں"</string>
- <string name="description_search_button" msgid="3660807558587384889">"تلاش کریں"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"ڈائل کریں"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"ڈائل کرنے کیلئے نمبر"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"پلے بیک چلائیں یا روکیں"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"اسپیکر فون آن یا آف کریں"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"پلے بیک پوزیشن طلب کریں"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"پلے بیک کی شرح گھٹائیں"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"پلے بیک کی شرح بڑھائیں"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"کال کی سرگزشت"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"مزید اختیارات"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"ڈائل پیڈ"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"صرف باہر جانے والی دکھائیں"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"صرف آنے والی دکھائیں"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"صرف چھوٹی ہوئی دکھائیں"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"صرف صوتی میلز دکھائیں"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"سبھی کالیں دکھائیں"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2 سیکنڈ کا توقف شامل کریں"</string>
- <string name="add_wait" msgid="3360818652790319634">"انتظار شامل کریں"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"ترتیبات"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"نیا رابطہ"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"سبھی رابطے"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"کال کی تفصیلات"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"تفصیلات دستیاب نہیں ہیں"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"ٹچ ٹون کی پیڈ کا استعمال کریں"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"جاری کال پر واپس لوٹیں"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"کال شامل کریں"</string>
- <string name="type_incoming" msgid="6502076603836088532">"آنے والی کال"</string>
- <string name="type_outgoing" msgid="343108709599392641">"باہر جانے والی کال"</string>
- <string name="type_missed" msgid="2720502601640509542">"چھوٹی ہوئی کال"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"آنے والی ویڈیو کال"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"کی جانے والی ویڈیو کال"</string>
- <string name="type_missed_video" msgid="954396897034220545">"چھوٹی ہوئی ویڈیو کال"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"صوتی میل"</string>
- <string name="type_rejected" msgid="7783201828312472691">"مسترد شدہ کال"</string>
- <string name="type_blocked" msgid="3521686227115330015">"مسدود شدہ کال"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"آنے والی کالیں"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"صوتی میل چلائیں"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"رابطہ <xliff:g id="NAME">%1$s</xliff:g> دیکھیں"</string>
- <string name="description_call" msgid="3443678121983852666">"<xliff:g id="NAME">%1$s</xliff:g> کو کال کریں"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> کیلئے رابطہ کی تفصیلات"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> کالیں۔"</string>
- <string name="description_video_call" msgid="2933838090743214204">"ویڈیو کال۔"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"‏<xliff:g id="NAME">%1$s</xliff:g> پر SMS بھیجیں"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"نہ سنا ہوا صوتی میل"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"صوتی تلاش شروع کریں"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"<xliff:g id="NUMBER">%s</xliff:g> کو کال کریں"</string>
- <string name="unknown" msgid="740067747858270469">"نامعلوم"</string>
- <string name="voicemail" msgid="3851469869202611441">"صوتی میل"</string>
- <string name="private_num" msgid="6374339738119166953">"نجی نمبر"</string>
- <string name="payphone" msgid="7726415831153618726">"بامعاوضہ فون"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> سیکنڈ"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> منٹ <xliff:g id="SECONDS">%s</xliff:g> سیکنڈ"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> بوقت <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"اس نمبر پر کال نہیں کر سکتے ہیں"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"صوتی میل تشکیل دینے کیلئے، مینو &gt; ترتیبات پر جائیں۔"</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"صوتی میل کو کال کرنے کیلئے، پہلے ہوائی جہاز طرز آف کریں۔"</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"لوڈ ہو رہا ہے…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"‏SIM کارڈ سے لوڈ ہو رہا ہے…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"‏SIM کارڈ کے رابطے"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"رابطوں کی کوئی ایپ دستیاب نہیں ہے"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"صوتی تلاش دستیاب نہیں ہے"</string>
- <string name="call_not_available" msgid="8941576511946492225">"فون کال نہیں کی جا سکتی ہے کیونکہ فون ایپلیکیشن کو غیر فعال کر دیا گیا ہے۔"</string>
- <string name="activity_not_available" msgid="2287665636817987623">"اس آلہ پر اس کیلئے کوئی ایپ نہیں ہے"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"رابطے تلاش کریں"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"نمبر شامل کریں یا رابطے تلاش کریں"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"آپ کی کال کی سرگزشت خالی ہے"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"ایک کال کریں"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"آپ کے پاس کوئی چھوٹی ہوئی کالز نہیں ہیں۔"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"آپ کا صوتی میل ان باکس خالی ہے۔"</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"آپ کی صوتی میل آرکائیو خالی ہے۔"</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"صرف پسندیدہ دکھائیں"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"کال کی سرگزشت"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"صوتی میل کی آرکائیو"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"سبھی"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"چھوٹی ہوئی"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"صوتی میل"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"نئی، آسان انسداد"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"آپ کو بہتر طریقے سے حفاظت فراہم کرنے کیلئے فون کو انسداد کے کام کرنے کا طریقہ تبدیل کرنا ہوگا۔ آپ کے مسدود کردہ نمبروں سے کالیں اور متنی پیغامات دونوں مسدود کر دیے جائیں گے اور ممکن ہے ان کا اشتراک دیگر ایپس کے ساتھ کیا جائے۔"</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"اجازت دیں"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> مسدود کریں؟"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"اس نمبر سے کالز مسدود ہوجائیں گی اور صوتی میلز خودبخزد جذف ہو جائیں گی۔"</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"اس نمبر سے کالز مسدود ہو جائیں گی گی لیکن ممکن ہے کالر پھر بھی آپ کیلئے صوتی میلز چھوڑ پائے۔"</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"آپ کو اب اس نمبر سے مزید کالز یا متنی پیغامات موصول نہیں ہوں گے۔"</string>
- <string name="block_number_ok" msgid="770551992296781873">"مسدود کریں"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> غیر مسدود کریں؟"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"غیر مسدود کریں"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"اسپیڈ ڈائل"</string>
- <string name="tab_history" msgid="2563144697322434940">"کال کی سرگزشت"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"رابطے"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"صوتی میل"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"پسندیدہ سے ہٹا دیا گیا"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"کالعدم کریں"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"<xliff:g id="NUMBER">%s</xliff:g> کو کال کریں"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"نیا رابطہ بنائیں"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"رابطے میں شامل کریں"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"‏SMS بھیجیں"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"ویڈیو کال کریں"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"نمبر مسدود کریں"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> نئی چھوٹی ہوئی کالیں"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"آپ کے اسپیڈ ڈائل پر ابھی تک کوئی نہیں ہے"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"ایک پسندیدہ شامل کریں"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"آپ کے پاس ابھی تک کوئی رابطے نہیں ہیں"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"ایک رابطہ شامل کریں"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"سبھی نمبرز دیھکنے کیلئے تصویر ٹچ کریں یا دوبارہ ترتیب دینے کیلئے ٹچ کریں اور دبائے رکھیں"</string>
- <string name="remove_contact" msgid="1080555335283662961">"ہٹائیں"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"ویڈیو کال"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"پیغام بھیجیں"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"کال کی تفصیلات"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> کو کال کریں"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>، <xliff:g id="TYPEORLOCATION">^2</xliff:g>، <xliff:g id="TIMEOFCALL">^3</xliff:g>، <xliff:g id="PHONEACCOUNT">^4</xliff:g> کی جانب سے چھوٹی ہوئی کال۔"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>، <xliff:g id="TYPEORLOCATION">^2</xliff:g>، <xliff:g id="TIMEOFCALL">^3</xliff:g>، <xliff:g id="PHONEACCOUNT">^4</xliff:g> کی جانب سے جواب دی گئی کال۔"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"بغیر پڑھی گئی صوتی میل منجانب <xliff:g id="NAMEORNUMBER">^1</xliff:g>، <xliff:g id="TYPEORLOCATION">^2</xliff:g>، <xliff:g id="TIMEOFCALL">^3</xliff:g>، <xliff:g id="PHONEACCOUNT">^4</xliff:g>۔"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"صوتی میل منجانب <xliff:g id="NAMEORNUMBER">^1</xliff:g>، <xliff:g id="TYPEORLOCATION">^2</xliff:g>، <xliff:g id="TIMEOFCALL">^3</xliff:g>، <xliff:g id="PHONEACCOUNT">^4</xliff:g>۔"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>، <xliff:g id="TYPEORLOCATION">^2</xliff:g>، <xliff:g id="TIMEOFCALL">^3</xliff:g>، <xliff:g id="PHONEACCOUNT">^4</xliff:g> پر کال کریں۔"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> پر"</string>
- <string name="description_via_number" msgid="3503311803959108316">"بذریعہ <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"بذریعہ <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> پر، بذریعہ <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> بذریعہ <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"کال کریں"</string>
- <string name="description_call_action" msgid="4000549004089776147">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> کو کال کریں"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> کو ویڈیو کال کریں۔"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> سے صوتی میل سنیں"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> سے صوتی میل چلائیں"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> سے صوتی میل موقوف کریں"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> سے صوتی میل حذف کریں"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> نئی صوتی میلز</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> نئی صوتی میل </item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> کیلئے رابطہ بنائیں"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> کو موجودہ رابطہ میں شامل کریں"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> کیلئے کال کی تفصیلات"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"کال کی سرگزشت سے حذف کر دیا گیا"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"ﺁﺝ"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"گزشتہ کل"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"قدیم تر"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"کالز کی فہرست"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"اسپیکر کو آن کر دیں۔"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"اسپیکر کو آف کر دیں۔"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"تیزی سے چلائیں۔"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"آہستہ چلائیں۔"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"پلے بیک شروع یا موقوف کریں۔"</string>
- <string name="list_delimeter" msgid="4571593167738725100">"، "</string>
- <string name="display_options_title" msgid="7812852361055667468">"ڈسپلے کے اختیارات"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"آوازیں اور وائبریشن"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"ایکسیسبیلٹی"</string>
- <string name="ringtone_title" msgid="760362035635084653">"فون رِنگ ٹون"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"کالز کیلئے وائبریٹ بھی کرے"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"ڈائل پیڈ ٹونز"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"ڈائل پیڈ ٹون کی لمبائی"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"حسب معمول"</item>
- <item msgid="6177579030803486015">"طویل"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"فوری جوابات"</string>
- <string name="call_settings_label" msgid="313434211353070209">"کالز"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"کال مسدود کرنا"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"کال مسدود کرنا عارضی طور پر آف ہے"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"کال مسدود کرنا غیر فعال کر دیا گیا ہے کیونکہ آپ نے پچھلے 48 گھنٹوں میں اس فون سے ہنگامی سروسز کے ساتھ رابطہ کیا تھا۔ جب 48 گھنٹے کا دورانیہ ختم ہوگا تو یہ خودکار طور پر دوبارہ فعال ہو جائے گا۔"</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"نمبرز درآمد کریں"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"آپ نے پہلے کچھ کالرز کو خودکار طور پر بذریعہ دیگر ایپس صوتی میل کو بھیجنا نشان زد کیا تھا۔"</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"نمبرز دیکھیں"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"درآمد کریں"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"درآمد کرنا ناکام ہوگیا"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"صوتی میل کو آرکائیو کرنے میں ناکام۔"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"نمبر غیر مسدود کریں"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"نمبر شامل کریں"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"ان نمبرز سے کالز مسدود ہوجائیں گی اور صوتی میلز خودبخود جذف ہو جائیں گی۔"</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"ان نمبرز سے کالز مسدود ہو جائیں گی گی لیکن ممکن ہے وہ پھر بھی آپ کیلئے صوتی میلز چھوڑ پائیں۔"</string>
- <string name="block_list" msgid="7760188925338078011">"مسدود کردہ نمبرز"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> غلط ہے۔"</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> پہلے ہی مسدود ہے۔"</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"کال مسدود کرنا 48 گھنٹے کیلئے غیر فعال ہو گیا"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"غیر فعال ہو گیا کیونکہ ایک ہنگامی کال کی گئی تھی۔"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"کالنگ اکاؤنٹس"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"آن کریں"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"اجازتیں طے کریں"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"اسپیڈ ڈائل کو فعال کرنے کیلئے رابطوں کی اجازت آن کریں۔"</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"اپنا کال لاگ دیکھنے کیلئے فون کی اجازت آن کریں۔"</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"اپنے رابطے دیکھنے کیلئے رابطوں کی اجازت آن کریں۔"</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"اپنی صوتی میل تک رسائی کیلئے فون کی اجازت آن کریں۔"</string>
- <string name="permission_no_search" msgid="84152933267902056">"اپنے رابطوں کو تلاش کرنے کیلئے رابطوں کی اجازتیں آن کریں۔"</string>
- <string name="permission_place_call" msgid="2101287782615887547">"کال کرنے کیلئے فون کی اجازت آن کریں۔"</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"فون ایپ کے پاس سسٹم ترتیبات کو لکھنے کی اجازت نہیں ہے۔"</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"مسدود کردہ"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> فعال ہے"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"مسدود کریں/سپام کی اطلاع دیں"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"مسدود کریں"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"سپام نہیں ہے"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"غیر مسدود کریں"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"سپام"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> مسدود کریں؟"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"مستقبل میں اس نمبر سے کالز اور صوتی میلز مسدود ہوں گی۔"</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"کال کی بطور سپام اطلاع دیں"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"مستقبل میں اس نمبر سے کالز اور صوتی میلز مسدود ہوں گی۔ اس کال کی بطور سپام اطلاع ہوگی۔"</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> غیر مسدود کریں؟"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"یہ نمبر غیر مسدود ہو جائے گا اور اس کی اطلاع \'سپام نہیں\' کے بطور ہوگی۔ مستقبل میں کالز اور صوتی میلز کی بطور سپام شناخت نہیں ہوگی۔"</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> کو وائٹ لسٹ کریں؟"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"وائٹ لسٹ کریں"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"مستقبل میں اس نمبر سے کالز اور صوتی میلز بطور سپام شناخت نہیں ہونگی۔ اس نمبر کی اطلاع بطور سپام نہیں ہوگی۔"</string>
-</resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
deleted file mode 100644
index 3be69aa78..000000000
--- a/res/values-uz/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Telefon"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Telefon"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Telefon raqam terish paneli"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Raqam tergich"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Chaqiruvlar tarixi"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Raqam noto‘g‘ri aniqlandi"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Raqamdan nusxa olish"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Transkripsiyadan nusxa olish"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Raqamni bloklash"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> raqami bloklandi"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Raqamni blokdan chiqarish"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> raqami blokdan chiqarildi"</string>
- <string name="block_number_undo" msgid="591338370336724156">"QAYTARISH"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"O‘chirish"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Tahrirlash va telefon qilish"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Qo‘ng‘iroqlar tarixini tozalash"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Ovozli xabarni o‘chirish"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Ovozli pochtani arxivlash"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Ovozli xabarni yuborish"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Ovozli pochta o‘chirildi"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Ovozli pochta arxivlandi"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"QAYTARISH"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"ARXIVGA O‘TISH"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Qo‘ng‘iroqlar tarixi tozalansinmi?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Barcha qo‘ng‘iroqlar tarixi o‘chib ketadi."</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Qo‘ng‘iroqlar tarixi tozalanmoqda…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Chaqiruv"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Javobsiz qo‘ng‘iroq"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Javobsiz ishchi qo‘ng‘irog‘i"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Javobsiz qo‘ng‘iroqlar"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ta javobsiz qo‘ng‘iroq"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Telefon qilish"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"SMS yuborish"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> ta ovozli xabar </item>
- <item quantity="one">Ovozli xabar</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Tinglash"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"<xliff:g id="CALLER">%1$s</xliff:g>dan yangi ovozli xabar"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Ovozli xabarni eshitib bo‘lmadi"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Ovozli xabar yuklanmoqda…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Ovozli pochta arxivlanmoqda…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Ovozli xabarni yuklab bo‘lmadi"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Faqat ovozli xabar qo‘ng‘iroqlari"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Faqat kiruvchi qo‘ng‘iroqlar"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Faqat chiquvchi qo‘ng‘iroqlar"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Faqat javobsiz qo‘ng‘iroqlar"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Vizual ovozli xabar"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Ovozli xabarlarni abonentga qo‘ng‘iroq qilmasdan ko‘rng yoki eshiting. Internet-trafik uchun to‘lov olinishi mumkin."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Sozlamalar"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Yangi ovozli xabarlar yo‘q"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Yangi ovozli xabar kutib turibdi. Uni hozir yuklab bo‘lmaydi."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Ovozli pochtangizni sozlang"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Audio mavjud emas"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Sozlash"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Ovoz xabarga qo‘ng‘."</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Raqamni tanlang"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Raqamni tanlang"</string>
- <string name="make_primary" msgid="5829291915305113983">"Shu tanlov eslab qolinsin"</string>
- <string name="description_search_button" msgid="3660807558587384889">"qidiruv"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"terish"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"terish uchun raqam"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Ijroni boshlash yoki to‘xtatish"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Karnayni yoqish yoki o‘chirish"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Ijro vaziyatini qidirish"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Ijro tezligini kamaytirish"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Ijro tezligini oshirish"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Chaqiruvlar tarixi"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Boshqa parametrlar"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"terish paneli"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Chiquvchi qo‘ng‘-ni ko‘rsatish"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Kiruvchi qo‘n-larni ko‘rsatish"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Javobsiz qo‘n-larni ko‘rsatish"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Ovozli xabarlarni ko‘rsatish"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Barcha qo‘n-larni ko‘rsatish"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"2 soniyalik pauza qo‘shish"</string>
- <string name="add_wait" msgid="3360818652790319634">"Pauza qo‘shish"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Sozlamalar"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Yangi kontakt"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Barcha kontaktlar"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Chaqiruv tafsilotlari"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Tafsilotlar mavjud emas"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Tovushli raqam tergich"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Amaldagi chaqiruvga qaytish"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Chaqiruv qo‘shish"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Kiruvchi chaqiruv"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Chiquvchi chaqiruv"</string>
- <string name="type_missed" msgid="2720502601640509542">"Javobsiz chaqiruv"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Kiruvchi videoqo‘ng‘iroq"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Chiquvchi videoqo‘ng‘iroq"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Javobsiz videoqo‘ng‘iroq"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Ovozli pochta"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Rad qilingan qo‘ng‘iroq"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Bloklangan qo‘ng‘iroq"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Kiruvchi chaqiruvlar"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Ovozli xabarni eshitish"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"<xliff:g id="NAME">%1$s</xliff:g> kontaktini ko‘rish"</string>
- <string name="description_call" msgid="3443678121983852666">"Qo‘ng‘iroq: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g> uchun kontakt ma’lumotlari"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> ta qo‘ng‘iroq."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Video qo‘ng‘iroq."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"<xliff:g id="NAME">%1$s</xliff:g>ga SMS yuborish"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Ochilmagan ovozli xabar"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Ovozli qidiruvni boshlash"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Qo‘ng‘iroq: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Noma’lum"</string>
- <string name="voicemail" msgid="3851469869202611441">"Ovozli pochta"</string>
- <string name="private_num" msgid="6374339738119166953">"Yashirin raqam"</string>
- <string name="payphone" msgid="7726415831153618726">"Taksofon"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> soniya"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> daq <xliff:g id="SECONDS">%s</xliff:g> son"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Bu raqamga qo‘ng‘iroq qilib bo‘lmaydi"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Ovozli xabarni sozlash uchun \"Menyu\" &gt; \"Sozlamalar\"ga o‘ting."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Ovozli xabar jo‘natish uchun, avval \"Parvoz rejimi\"ni o‘chiring."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Yuklanmoqda…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"SIM kartadan yuklanmoqda…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM kartadagi kontaktlar"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Hech qanday kontakt ilovasi yo‘q"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Ovozli qidiruv mavjud emas"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Telefon ilovasi o‘chirib qo‘yilgani sababli telefon qo‘ng‘iroqlarini amalga oshirib bo‘lmaydi."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Qurilmada buni bajaradigan ilova yo‘q"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Kontakt qidirish"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Kontaktni toping yoki raqamini kiriting"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Qo‘ng‘iroqlar tarixi bo‘m-bo‘sh"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Qo‘ng‘iroq qilish"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Sizda hech qanday javobsiz qo‘ng‘iroq yo‘q."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Ovozli pochta qutisi bo‘m-bo‘sh."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Ovozli pochta arxivingiz bo‘sh."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Faqat saralar ko‘rsatilsin"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Chaqiruvlar tarixi"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Ovozli pochta arxivi"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Hammasi"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Javobsiz"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Ovozli pochta"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Raqamlarni bloklashning sodda usuli"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Sizni yanada yaxshiroq himoya qilish uchun “Telefon” ilovasi bloklash tartibini o‘zgartirishi lozim. Bloklangan raqamlardan keladigan qo‘ng‘iroqlar ham, SMS xabarIar ham qabul qilinmaydi. Boshqa ilovalar ham bloklangan raqamlar ro‘yxatini ko‘rishi mumkin."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Ruxsat berish"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"<xliff:g id="NUMBER">%1$s</xliff:g> raqami bloklanilsinmi?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Bu raqamdan kelgan qo‘ng‘iroqlar bloklanadi va ovozli xabari avtomatik o‘chiriladi."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Bu raqamdan kelgan qo‘ng‘iroqlar bloklanadi, lekin raqam egasi sizga ovozli xabar qoldira oladi."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Siz endi bu raqamdan qo‘ng‘iroq va SMS qabul qilmaysiz."</string>
- <string name="block_number_ok" msgid="770551992296781873">"BLOKLASH"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"<xliff:g id="NUMBER">%1$s</xliff:g> raqami blokdan chiqarilsinmi?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"BLOKDAN CHIQARISH"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Tezkor terish"</string>
- <string name="tab_history" msgid="2563144697322434940">"Chaqiruvlar tarixi"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Kontaktlar"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Ovozli pochta"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Tanlanganlardan o‘chirilgan"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Bekor qilish"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Qo‘ng‘iroq: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Yangi kontakt yaratish"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Mavjud kontaktga saqlash"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"SMS yuborish"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Videoqo‘ng‘iroq qilish"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Raqamni bloklash"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> ta javobsiz qo‘ng‘iroq"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Tezkor raqam terish ro‘yxatida hech kim yo‘q"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Tezkor raqam terishni sozlang"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Sizda hali hech qanday kontakt yo‘q"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Yangi kontakt qo‘shing"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Barcha raqamlarni ko‘rish uchun rasm ustiga bosing. Joyini o‘zgartirish uchun uni bosib turing."</string>
- <string name="remove_contact" msgid="1080555335283662961">"O‘chirish"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Video qo‘ng‘iroq"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"SMS yuborish"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Chaqiruv tafsilotlari"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Qo‘ng‘iroq qilish: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Qo‘ng‘iroq javobsiz qoldirildi: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Qo‘ng‘iroqqa javob berildi: <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"O‘qilmagan ovozli xabar (<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>)."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Ovozli xabar (<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>)."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Chaqiruv: <xliff:g id="NAMEORNUMBER">^1</xliff:g>. <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"<xliff:g id="PHONEACCOUNT">^1</xliff:g> orqali"</string>
- <string name="description_via_number" msgid="3503311803959108316">"<xliff:g id="NUMBER">%1$s</xliff:g> raqami orqali"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"<xliff:g id="NUMBER">%1$s</xliff:g> raqami orqali"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> da, <xliff:g id="NUMBER">%2$s</xliff:g> raqami orqali"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, <xliff:g id="NUMBER">%2$s</xliff:g> raqami orqali"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Qo‘ng‘iroq qilish"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Qo‘ng‘iroq qilish: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Video qo‘ng‘iroq qilish: <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> kontaktidan kelgan ovozli xabarni tinglash"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> tomonidan yuborilgan ovozli xabarni eshitish"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> tomonidan yuborilgan ovozli xabarni pauza qilish"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> tomonidan yuborilgan ovozli xabarni o‘chirish"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> ta yangi ovozli xabar</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> ta yangi ovozli xabar</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Yangi kontakt yaratish: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Mavjud kontaktlarga qo‘shish: <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_details_action" msgid="2433827152749491785">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> – qo‘ng‘iroq tafsilotlari"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Qo‘ng‘iroqlar tarixidan o‘chirib tashlandi"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Bugun"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Kecha"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Eskiroq"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Qo‘ng‘iroqlar ro‘yxati"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Karnayni yoqish."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Karnayni o‘chirib qo‘yish."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Tezro ijro etish."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Sekinroq ijro etish."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Ijroni boshlash yoki to‘xtatish"</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Kontaktlarning ko‘rinishi"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Ovoz va tebranish"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Maxsus imkoniyatlar"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Telefon ringtoni"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Chaqiruv vaqtida tebranish"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Tugmalar tovushi"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Klaviatura ohangi uzunligi"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"O‘rtacha"</item>
- <item msgid="6177579030803486015">"Uzun"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Tezkor javoblar"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Chaqiruvlar"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Chaqiruvlarni bloklash"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Qo‘ng‘iroqlarni bloklash o‘chirilgan"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Oxirgi 48 soat ichida ushbu telefon orqali favqulodda xizmatlar bilan bog‘lanilganligi uchun qo‘ng‘iroqlarni bloklash o‘chirib qo‘yildi. 48 soatlik muddat tugagandan so‘ng bu avtomatik qayta yoqiladi."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Raqamlarni import qilish"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Ba’zi kiruvchi kontaktlaringiz boshqa ilovalar orqali avtomatik ovozli xabar yo‘llaydigan qilib belgilagansiz."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Sonini ko‘rish"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Import qilish"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Import qilib bo‘lmadi"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Ovozli pochtani arxivlashda xatolik yuz berdi."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Raqamni blokdan chiqarish"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Raqam qo‘shish"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Bu raqamlardan kelgan qo‘ng‘iroqlar bloklanadi va ovozli xabarlari avtomatik o‘chiriladi."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Bu raqamlardan kelgan qo‘ng‘iroqlar bloklanadi, lekin raqam egalari sizga ovozli xabar qoldira olishadi."</string>
- <string name="block_list" msgid="7760188925338078011">"Bloklangan raqamlar"</string>
- <string name="invalidNumber" msgid="619058581062192851">"Noto‘g‘ri raqam: <xliff:g id="NUMBER">%1$s</xliff:g>."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> raqami allaqachon bloklangan."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Qo‘ng‘iroqlarni bloklash 48 soat muddatga o‘chirildi"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Favqulodda qo‘ng‘iroq amalga oshirilgani uchun o‘chirildi."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Qo‘ng‘iroq uchun hisoblar"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Yoqish"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Ruxsatnomalarni sozlash"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Tezkor raqam terishni sozlash uchun ilovaga “Kontaktlar” ruxsatnomasini bering."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Chaqiruvlar jurnalini ko‘rish uchun ilovaga “Telefon” ruxsatnomasini bering."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Kontaktlarni ko‘rish uchun ilovaga “Kontaktlar” ruxsatnomasini bering."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Ovozli pochtaga kirish uchun ilovaga “Telefon” ruxsatnomasini bering."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Kontaktlarni qidirish uchun “Kontaktlar” ruxsatnomasini bering."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Qo‘ng‘iroq qilish uchun ilovaga “Telefon” ruxsatnomasini bering."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Telefon ilovasida tizim sozlamalarini o‘zgartirish uchun ruxsat yo‘q."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Bloklangan"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> faol"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Bloklash/spam deb belgilash"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Bloklash"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Spam emas"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Blokdan chiqarish"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"<xliff:g id="NUMBER">%1$s</xliff:g> raqami bloklansinmi?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Bu raqamdan keladigan qo‘ng‘iroq va ovozli xabarlar bloklanadi."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Spam haqida shikoyat qilish"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Bu raqamdan keladigan qo‘ng‘iroq va ovozli xabarlar bloklanadi. Spam haqida shikoyat qilinadi."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"<xliff:g id="NUMBER">%1$s</xliff:g> raqami blokdan chiqarilsinmi?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Bu raqam blokdan chiqariladi. Qo‘ng‘iroq va ovozli xabarlar spam sifatida aniqlanmaydi."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"<xliff:g id="NUMBER">%1$s</xliff:g> raqami ma’qullangan ro‘yxatga kiritilsinmi?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Ma’qullangan ro‘yxat"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Bu raqam spam emas deb xabar beriladi. Qo‘ng‘iroq va ovozli xabarlar spam sifatida aniqlanmaydi."</string>
-</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
deleted file mode 100644
index 648751073..000000000
--- a/res/values-vi/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Điện thoại"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Điện thoại"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Bàn phím số điện thoại"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Điện thoại"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Nhật ký cuộc gọi"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Báo cáo số không chính xác"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Sao chép số"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Sao chép bản ghi âm"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Chặn số"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"Đã chặn <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Bỏ chặn số"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"Đã bỏ chặn <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="block_number_undo" msgid="591338370336724156">"HOÀN TÁC"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Xóa"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Chỉnh sửa số trước khi gọi"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Xóa nhật ký cuộc gọi"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Xóa thư thoại"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Lưu trữ thư thoại"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Chia sẻ thư thoại"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Đã xóa thư thoại"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Đã lưu trữ thư thoại"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"HOÀN TÁC"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"CHUYỂN ĐẾN LƯU TRỮ"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Xóa nhật ký cuộc gọi?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Thao tác này sẽ xóa tất cả cuộc gọi khỏi nhật ký của bạn"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Đang xóa nhật ký cuộc gọi…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Điện thoại"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Cuộc gọi nhỡ"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Cuộc gọi nhỡ về công việc"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Cuộc gọi nhỡ"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> cuộc gọi nhỡ"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Gọi lại"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Tin nhắn"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Thư thoại </item>
- <item quantity="one">Thư thoại</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Phát"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"Thư thoại mới từ <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Không thể phát thư thoại"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Đang tải thư thoại…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Đang lưu trữ thư thoại…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Không thể tải thư thoại"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Chỉ cuộc gọi có thư thoại"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Chỉ cuộc gọi đến"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Chỉ cuộc gọi đi"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Chỉ cuộc gọi nhỡ"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Thư thoại kèm theo hình ảnh"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Xem và nghe thư thoại của bạn mà không phải thực hiện cuộc gọi. Bạn có thể mất phí dữ liệu."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Cài đặt"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Không có cập nhật thư thoại"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Đang chờ thư thoại mới. Không thể tải ngay bây giờ."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Thiết lập thư thoại của bạn"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Không có âm thanh"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Thiết lập"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Gọi thư thoại"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Chọn số"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Chọn số"</string>
- <string name="make_primary" msgid="5829291915305113983">"Nhớ lựa chọn này"</string>
- <string name="description_search_button" msgid="3660807558587384889">"tìm kiếm"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"quay số"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"số để quay"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Phát hoặc dừng phát lại"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Bật hoặc tắt loa ngoài"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Tìm kiếm vị trí phát lại"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Giảm tốc độ phát lại"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Tăng tốc độ phát lại"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Nhật ký cuộc gọi"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Tùy chọn khác"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"bàn phím số"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Chỉ hiển thị cuộc gọi đi"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Chỉ hiển thị cuộc gọi đến"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Chỉ hiển thị cuộc gọi nhỡ"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Chỉ hiển thị thư thoại"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Hiển thị tất cả cuộc gọi"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Thêm 2 giây dừng"</string>
- <string name="add_wait" msgid="3360818652790319634">"Thêm chờ"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Cài đặt"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Liên hệ mới"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Tất cả liên hệ"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Chi tiết cuộc gọi"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Không có chi tiết"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Sử dụng bàn phím số cảm ứng có âm"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Quay lại cuộc gọi đang thực hiện"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Thêm cuộc gọi"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Cuộc gọi đến"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Cuộc gọi đi"</string>
- <string name="type_missed" msgid="2720502601640509542">"Cuộc gọi nhỡ"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Cuộc gọi điện video đến"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Cuộc gọi điện video đi"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Cuộc gọi điện video bị nhỡ"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Thư thoại"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Cuộc gọi bị từ chối"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Cuộc gọi bị chặn"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Cuộc gọi đến"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Phát thư thoại"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Xem thông tin liên hệ của <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Gọi <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Chi tiết liên lạc cho <xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> cuộc gọi."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Gọi điện video."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Gửi SMS cho <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"Thư thoại chưa nghe"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Bắt đầu tìm kiếm bằng giọng nói"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Gọi <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Không xác định"</string>
- <string name="voicemail" msgid="3851469869202611441">"Thư thoại"</string>
- <string name="private_num" msgid="6374339738119166953">"Số cá nhân"</string>
- <string name="payphone" msgid="7726415831153618726">"Điện thoại công cộng"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> giây"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> phút <xliff:g id="SECONDS">%s</xliff:g> giây"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> lúc <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Không thể gọi số này"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Để thiết lập thư thoại, đi tới Menu &gt; Cài đặt."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Để gọi thư thoại, trước tiên hãy tắt chế độ trên Máy bay."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Đang tải…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Đang tải từ thẻ SIM…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"Danh bạ trên thẻ SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Không có ứng dụng danh bạ"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Không có tính năng tìm kiếm bằng giọng nói"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Không thể thực hiện cuộc gọi điện thoại vì ứng dụng Điện thoại đã bị tắt."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Không có ứng dụng nào cho thao tác đó trên thiết bị này"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Tìm kiếm liên hệ"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Thêm số hoặc tìm kiếm danh bạ"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Nhật ký cuộc gọi của bạn trống"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Gọi điện"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Bạn không có cuộc gọi nhỡ nào."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Hộp thư thoại đến của bạn trống."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Danh sách thư thoại được lưu trữ của bạn trống."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Chỉ hiển thị liên hệ ưa thích"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Nhật ký cuộc gọi"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Lưu trữ thư thoại"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Tất cả"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Bị nhỡ"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Thư thoại"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Tính năng chặn mới, được đơn giản hóa"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Để bảo vệ bạn tốt hơn, Điện thoại cần thay đổi cách hoạt động của tính năng chặn. Giờ đây, các số đã chặn của bạn sẽ bị chặn cả cuộc gọi và tin nhắn, đồng thời có thể được chia sẻ với các ứng dụng khác."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Cho phép"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Chặn <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Các cuộc gọi từ số này sẽ bị chặn và thư thoại sẽ tự động bị xóa."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Các cuộc gọi từ số này sẽ bị chặn nhưng người gọi vẫn có thể để lại thư thoại cho bạn."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Bạn sẽ không nhận được các cuộc gọi hoặc tin nhắn từ số này nữa."</string>
- <string name="block_number_ok" msgid="770551992296781873">"CHẶN"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Bỏ chặn <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"BỎ CHẶN"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Quay số nhanh"</string>
- <string name="tab_history" msgid="2563144697322434940">"Nhật ký cuộc gọi"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Danh bạ"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Thư thoại"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Đã xóa khỏi liên hệ yêu thích"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Hoàn tác"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Gọi <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Tạo liên hệ mới"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Thêm vào liên hệ"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Gửi SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Gọi điện video"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Chặn số"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> cuộc gọi nhỡ mới"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Chưa có ai trong danh bạ quay số nhanh của bạn"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Thêm liên hệ yêu thích"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Bạn chưa có bất kỳ liên hệ nào"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Thêm liên hệ"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Chạm vào hình ảnh để xem tất cả các số hoặc chạm và giữ để sắp xếp lại"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Xóa"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Cuộc gọi điện video"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Gửi tin nhắn"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Chi tiết cuộc gọi"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Gọi <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Cuộc gọi nhỡ từ <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Cuộc gọi đã trả lời từ <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Thư thoại chưa đọc từ <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Thư thoại từ <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Gọi tới <xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"trên <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"qua <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"qua <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"trên <xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, qua <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> qua <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Gọi"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Gọi <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Gọi điện video cho <xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Nghe thư thoại từ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Phát thư thoại từ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Tạm dừng thư thoại từ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Xóa thư thoại từ <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> thư thoại mới</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> thư thoại mới</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Tạo địa chỉ liên hệ cho <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Thêm <xliff:g id="NAMEORNUMBER">^1</xliff:g> vào địa chỉ liên hệ hiện có"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Chi tiết cuộc gọi cho <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Đã xóa khỏi nhật ký cuộc gọi"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Hôm nay"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Hôm qua"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Cũ hơn"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Danh sách cuộc gọi"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Bật loa."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Tắt loa."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Phát nhanh hơn."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Phát chậm hơn."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Bắt đầu hoặc tạm dừng phát lại."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Tùy chọn hiển thị"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Âm thanh và rung"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Trợ năng"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Nhạc chuông điện thoại"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Đồng thời rung khi có cuộc gọi"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Âm bàn phím số"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Độ dài âm bàn phím số"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Thường"</item>
- <item msgid="6177579030803486015">"Dài"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Trả lời nhanh"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Cuộc gọi"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Chặn cuộc gọi"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Tạm thời tắt chặn cuộc gọi"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Tính năng chặn cuộc gọi đã bị tắt vì bạn đã liên hệ với dịch vụ khẩn cấp từ điện thoại này trong vòng 48 giờ qua. Tính năng này sẽ được bật lại tự động sau khi khoảng thời gian 48 giờ kết thúc."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Nhập số"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Trước đây bạn đã đánh dấu một số người gọi sẽ được tự động gửi tới thư thoại qua các ứng dụng khác."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Xem số"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Nhập"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Nhập không thành công"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Không lưu trữ được thư thoại."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Bỏ chặn số"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Thêm số"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Cuộc gọi từ các số này sẽ bị chặn và thư thoại sẽ tự động bị xóa."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Cuộc gọi từ các số này sẽ bị chặn nhưng họ vẫn có thể để lại thư thoại cho bạn."</string>
- <string name="block_list" msgid="7760188925338078011">"Số bị chặn"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> không hợp lệ."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> đã bị chặn."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Đã tắt tính năng chặn cuộc gọi trong 48 giờ"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Bị tắt vì bạn đã thực hiện cuộc gọi khẩn cấp."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Tài khoản gọi"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Bật"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Đặt quyền"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Để bật quay số nhanh, bật quyền đối với Danh bạ."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Để xem nhật ký cuộc gọi của bạn, bật quyền đối với Điện thoại."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Để xem danh bạ của bạn, bật quyền đối với Danh bạ."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Để truy cập thư thoại của bạn, bật quyền đối với Điện thoại."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Để tìm kiếm liên hệ của bạn, hãy bật quyền đối với Danh bạ."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Để thực hiện cuộc gọi, bật quyền đối với Điện thoại."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Ứng dụng Điện thoại không có quyền ghi vào cài đặt hệ thống."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Bị chặn"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> đang hiện hoạt"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Chặn/báo cáo spam"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Chặn"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Không phải là spam"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Bỏ chặn"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Spam"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Chặn <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Các cuộc gọi và thư thoại tương lai từ số này sẽ bị chặn."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Báo cáo cuộc gọi là spam"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Tất cả cuộc gọi và thư thoại tương lai từ số này sẽ bị chặn. Cuộc gọi này sẽ được báo cáo là spam."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Bỏ chặn <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Số sẽ được bỏ chặn &amp; được báo cáo là không phải spam. Cuộc gọi &amp; thư thoại tương lai sẽ không bị coi là spam."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Đưa <xliff:g id="NUMBER">%1$s</xliff:g> vào danh sách cho phép?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Danh sách cho phép"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Các cuộc gọi và thư thoại tương lai từ số này sẽ không bị xác định là spam. Số này sẽ không bị báo cáo là spam."</string>
-</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
deleted file mode 100644
index dc315ebdc..000000000
--- a/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"电话"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"电话"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"电话拨号键盘"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"拨号"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"通话记录"</string>
- <string name="action_report_number" msgid="4635403959812186162">"报告错误的号码"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"复制号码"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"复制转录内容"</string>
- <string name="action_block_number" msgid="1482657602262262134">"屏蔽号码"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"已屏蔽 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"取消屏蔽号码"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"已取消屏蔽 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="block_number_undo" msgid="591338370336724156">"撤消"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"删除"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"拨打之前修改号码"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"清除通话记录"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"删除语音邮件"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"归档语音邮件"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"分享语音邮件"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"语音邮件已删除"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"已归档的语音邮件"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"撤消"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"转至归档"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"要清除通话记录吗?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"这会删除您的所有通话记录"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"正在清除通话记录…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"打电话"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"未接电话"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"未接工作来电"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"未接电话"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> 个未接电话"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"回电"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"发短信"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"><xliff:g id="COUNT">%1$d</xliff:g> 封语音邮件</item>
- <item quantity="one">1 封语音邮件</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"播放"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>,<xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"来自<xliff:g id="CALLER">%1$s</xliff:g>的新语音邮件"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"无法播放语音邮件"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"正在加载语音邮件…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"正在归档语音邮件…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"无法加载语音邮件"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"只显示语音信箱留言"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"只显示来电"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"只显示外拨电话"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"只显示未接来电"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"可视语音信箱"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"无需拨号就可以查看和收听您的语音邮件。不过,可能会产生额外的数据流量费。"</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"设置"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"无法更新语音信箱"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"您有新的语音邮件,但目前无法加载。"</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"设置您的语音信箱"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"无法播放音频"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"设置"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"呼叫语音信箱"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"选择号码"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"选择号码"</string>
- <string name="make_primary" msgid="5829291915305113983">"记住此选择"</string>
- <string name="description_search_button" msgid="3660807558587384889">"搜索"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"拨打"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"要拨打的号码"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"开始或停止播放"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"打开或关闭扬声器"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"拖动到播放位置"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"减慢播放速度"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"加快播放速度"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"通话记录"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"更多选项"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"拨号键盘"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"仅显示外拨电话"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"只显示来电"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"只显示未接来电"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"只显示语音邮件"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"显示所有通话"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"延长暂停时间2秒"</string>
- <string name="add_wait" msgid="3360818652790319634">"延长等待时间"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"设置"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"新建联系人"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"所有联系人"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"通话详情"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"无法获取详细信息"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"使用按键式键盘"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"返回正在进行的通话"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"添加通话"</string>
- <string name="type_incoming" msgid="6502076603836088532">"来电"</string>
- <string name="type_outgoing" msgid="343108709599392641">"外拨电话"</string>
- <string name="type_missed" msgid="2720502601640509542">"未接电话"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"视频通话来电"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"拨出的视频通话"</string>
- <string name="type_missed_video" msgid="954396897034220545">"错过的视频通话"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"语音信箱"</string>
- <string name="type_rejected" msgid="7783201828312472691">"拒接的来电"</string>
- <string name="type_blocked" msgid="3521686227115330015">"屏蔽的来电"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"来电"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"播放语音邮件"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"查看联系人<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"呼叫<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"<xliff:g id="NAMEORNUMBER">%1$s</xliff:g>的详细联系信息"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g>次通话。"</string>
- <string name="description_video_call" msgid="2933838090743214204">"视频通话。"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"向<xliff:g id="NAME">%1$s</xliff:g>发送短信"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"未收听的语音邮件"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"开始语音搜索"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"拨打 <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"未知"</string>
- <string name="voicemail" msgid="3851469869202611441">"语音信箱"</string>
- <string name="private_num" msgid="6374339738119166953">"私密号码"</string>
- <string name="payphone" msgid="7726415831153618726">"公用电话"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> 秒"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> 分钟 <xliff:g id="SECONDS">%s</xliff:g> 秒"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g><xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"无法拨打此号码"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"要设置语音信箱,请转到“菜单”&gt;“设置”。"</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"要呼叫语音信箱,请先关闭飞行模式。"</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"正在加载..."</string>
- <string name="imei" msgid="3045126336951684285">"移动通信国际识别码"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"正从 SIM 卡中加载..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM卡联系人"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"没有可用的通讯录应用"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"无法使用语音搜索"</string>
- <string name="call_not_available" msgid="8941576511946492225">"“电话”应用已被停用,因此无法拨打电话。"</string>
- <string name="activity_not_available" msgid="2287665636817987623">"此设备上没有可执行此操作的应用"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"搜索联系人"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"添加号码或搜索联系人"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"您没有任何通话记录"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"拨打电话"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"您没有任何未接电话。"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"您未收到任何语音邮件。"</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"没有已归档的语音邮件。"</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"仅显示收藏的联系人"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"通话记录"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"语音邮件归档"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"全部"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"未接电话"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"语音邮件"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"简单易用的全新屏蔽功能"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"为了向您提供更好的安全保护,“电话”应用需要升级屏蔽方式。您将不会再收到已屏蔽号码的来电和短信,而且系统还可能会将屏蔽号码列表共享给其他应用。"</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"允许"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"要屏蔽 <xliff:g id="NUMBER">%1$s</xliff:g> 吗?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"系统将屏蔽该号码的来电,并将自动删除来电者发送的语音邮件。"</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"系统将屏蔽该号码的来电,但来电者可能仍然能够给您发送语音邮件。"</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"您将不会再收到此号码的来电或短信。"</string>
- <string name="block_number_ok" msgid="770551992296781873">"屏蔽"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"要取消屏蔽 <xliff:g id="NUMBER">%1$s</xliff:g> 吗?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"取消屏蔽"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"快速拨号"</string>
- <string name="tab_history" msgid="2563144697322434940">"通话记录"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"通讯录"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"语音邮件"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"已从收藏中移除"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"撤消"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"拨打 <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"新建联系人"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"添加到联系人"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"发送短信"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"发起视频通话"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"屏蔽号码"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g>个新的未接电话"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"尚未给任何联系人设定快速拨号"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"收藏联系人"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"您还没有任何联系人"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"添加联系人"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"触摸图片可查看所有号码,触摸并按住可重新排序"</string>
- <string name="remove_contact" msgid="1080555335283662961">"移除"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"视频通话"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"发送短信"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"通话详情"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"呼叫<xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"<xliff:g id="PHONEACCOUNT">^4</xliff:g> 上 <xliff:g id="TIMEOFCALL">^3</xliff:g>来自<xliff:g id="NAMEORNUMBER">^1</xliff:g>(<xliff:g id="TYPEORLOCATION">^2</xliff:g>)的未接电话。"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"<xliff:g id="PHONEACCOUNT">^4</xliff:g> 上 <xliff:g id="TIMEOFCALL">^3</xliff:g>来自<xliff:g id="NAMEORNUMBER">^1</xliff:g>(<xliff:g id="TYPEORLOCATION">^2</xliff:g>)的已接电话。"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"<xliff:g id="PHONEACCOUNT">^4</xliff:g> 上 <xliff:g id="TIMEOFCALL">^3</xliff:g>来自<xliff:g id="NAMEORNUMBER">^1</xliff:g>(<xliff:g id="TYPEORLOCATION">^2</xliff:g>)的未读语音邮件。"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"<xliff:g id="PHONEACCOUNT">^4</xliff:g> 上 <xliff:g id="TIMEOFCALL">^3</xliff:g>来自<xliff:g id="NAMEORNUMBER">^1</xliff:g>(<xliff:g id="TYPEORLOCATION">^2</xliff:g>)的语音邮件。"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"<xliff:g id="TIMEOFCALL">^3</xliff:g>通过 <xliff:g id="PHONEACCOUNT">^4</xliff:g> 拨打给<xliff:g id="NAMEORNUMBER">^1</xliff:g>(<xliff:g id="TYPEORLOCATION">^2</xliff:g>)的电话。"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"通过 <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"通过 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"通过 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"帐号:<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>,通过 <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"帐号:<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>,通过 <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"拨打电话"</string>
- <string name="description_call_action" msgid="4000549004089776147">"呼叫<xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"与<xliff:g id="NAMEORNUMBER">^1</xliff:g>进行视频通话。"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"听取来自<xliff:g id="NAMEORNUMBER">^1</xliff:g>的语音邮件"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"播放来自<xliff:g id="NAMEORNUMBER">^1</xliff:g>的语音邮件"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"暂停来自<xliff:g id="NAMEORNUMBER">^1</xliff:g>的语音邮件"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"删除来自<xliff:g id="NAMEORNUMBER">^1</xliff:g>的语音邮件"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> 封新的语音邮件</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> 封新的语音邮件</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"为<xliff:g id="NAMEORNUMBER">^1</xliff:g>创建联系人信息"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"将<xliff:g id="NAMEORNUMBER">^1</xliff:g>添加到现有联系人"</string>
- <string name="description_details_action" msgid="2433827152749491785">"与<xliff:g id="NAMEORNUMBER">^1</xliff:g>的通话详情"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"已从通话记录中删除"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"今天"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"昨天"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"更早"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"通话清单"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"打开扬声器。"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"关闭扬声器。"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"快放。"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"慢放。"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"开始或暂停播放。"</string>
- <string name="list_delimeter" msgid="4571593167738725100">"、 "</string>
- <string name="display_options_title" msgid="7812852361055667468">"显示选项"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"提示音和振动"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"无障碍功能"</string>
- <string name="ringtone_title" msgid="760362035635084653">"手机铃声"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"有来电时响铃并振动"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"拨号键盘提示音"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"拨号键盘提示音长度"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"标准"</item>
- <item msgid="6177579030803486015">"长"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"快速回复"</string>
- <string name="call_settings_label" msgid="313434211353070209">"通话"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"来电屏蔽"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"来电拦截功能已暂时关闭"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"来电拦截功能已停用,因为您在过去 48 小时内使用该手机拨打了紧急服务电话。系统会在 48 小时的期限结束后自动重新启用该功能。"</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"导入号码"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"您曾通过其他应用将某些来电者设为自动转到语音信箱。"</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"查看号码"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"导入"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"导入失败"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"无法归档语音邮件。"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"取消屏蔽号码"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"添加号码"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"系统将屏蔽这些号码的来电,并将自动删除这些来电者发送的语音邮件。"</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"系统将屏蔽这些号码的来电,但这些来电者可能仍然能够给您发送语音邮件。"</string>
- <string name="block_list" msgid="7760188925338078011">"已屏蔽的号码"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> 无效。"</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> 已被屏蔽。"</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"来电拦截功能将停用 48 小时"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"此功能已停用,因为您拨打了紧急呼救电话。"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"通话帐号"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"开启"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"设置权限"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"要启用快速拨号功能,请开启“通讯录”权限。"</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"要查看您的通话记录,请开启“电话”权限。"</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"要查看您的联系人,请开启“通讯录”权限。"</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"要使用您的语音信箱,请开启“电话”权限。"</string>
- <string name="permission_no_search" msgid="84152933267902056">"要搜索您的联系人,请开启“通讯录”权限。"</string>
- <string name="permission_place_call" msgid="2101287782615887547">"要拨打电话,请开启“电话”权限。"</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"电话应用不具备写入系统设置的权限。"</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"已屏蔽"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"正在与<xliff:g id="NAMEORNUMBER">^1</xliff:g>通话"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"屏蔽/举报骚扰电话号码"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"屏蔽"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"非骚扰电话号码"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"取消屏蔽"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"骚扰电话号码"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"要屏蔽 <xliff:g id="NUMBER">%1$s</xliff:g> 吗?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"系统将屏蔽此号码日后的来电和语音邮件。"</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"举报为骚扰电话号码"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"系统将屏蔽此号码日后的来电和语音邮件,并会将此次来电举报为骚扰来电。"</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"要取消屏蔽 <xliff:g id="NUMBER">%1$s</xliff:g> 吗?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"系统会取消屏蔽此号码,并停止将其列为骚扰电话号码。此号码日后的来电和语音邮件不会再被识别为骚扰来电/垃圾邮件。"</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"要将 <xliff:g id="NUMBER">%1$s</xliff:g> 列入白名单吗?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"列入白名单"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"系统不会再将此号码日后的来电和语音邮件识别为骚扰来电/垃圾邮件,并会停止将此号码列为骚扰电话号码。"</string>
-</resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
deleted file mode 100644
index 2e1eb3242..000000000
--- a/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"電話"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"電話"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"手機撥號盤"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"電話"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"通話記錄"</string>
- <string name="action_report_number" msgid="4635403959812186162">"報告錯誤號碼"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"複製號碼"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"複製內容轉錄"</string>
- <string name="action_block_number" msgid="1482657602262262134">"封鎖號碼"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"已封鎖 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"解除封鎖號碼"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"已解除封鎖 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="block_number_undo" msgid="591338370336724156">"復原"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"刪除"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"致電前編輯號碼"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"清除通話記錄"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"刪除留言"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"封存留言"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"分享留言"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"已刪除留言信箱"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"已封存留言"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"復原"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"前往封存庫"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"要清除通話記錄嗎?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"此操作將刪除所有通話記錄"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"正在清除通話記錄…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"撥打電話"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"未接來電"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"未接工作來電"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"未接來電"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> 個未接來電"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"回電"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"短訊"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"><xliff:g id="COUNT">%1$d</xliff:g> 個留言</item>
- <item quantity="one">留言</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"播放"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g> , <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"來自<xliff:g id="CALLER">%1$s</xliff:g>的新語音留言"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"無法播放留言"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"正在載入留言…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"正在封存留言…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"無法載入留言"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"只顯示有留言的來電"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"只顯示來電"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"只顯示撥出電話"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"只顯示未接來電"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"視像留言信箱"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"檢視及聆聽您的語音留言,而無需撥號。可能需要支付數據用量費用。"</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"設定"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"無法更新留言"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"有新留言,但目前無法載入。"</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"設定您的留言信箱"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"無法使用音效"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"設定"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"致電留言信箱號碼"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"( <xliff:g id="COUNT">%1$d</xliff:g> ) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"選擇號碼"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"選擇號碼"</string>
- <string name="make_primary" msgid="5829291915305113983">"記住這個選擇"</string>
- <string name="description_search_button" msgid="3660807558587384889">"搜尋"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"撥號"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"致電號碼"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"播放或停止播放"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"開啟或關閉揚聲器"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"指定播放位置"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"減慢播放速度"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"加快播放速度"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"通話記錄"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"更多選項"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"撥號鍵盤"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"只顯示撥出電話"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"只顯示來電"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"只顯示未接來電"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"僅顯示語音留言"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"顯示所有通話"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"新增 2 秒暫停功能"</string>
- <string name="add_wait" msgid="3360818652790319634">"新增插播功能"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"設定"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"新增聯絡人"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"所有聯絡人"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"通話詳情"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"沒有詳細資訊"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"使用觸控音頻按鍵"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"返回進行中的通話"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"新增通話"</string>
- <string name="type_incoming" msgid="6502076603836088532">"來電"</string>
- <string name="type_outgoing" msgid="343108709599392641">"撥出電話"</string>
- <string name="type_missed" msgid="2720502601640509542">"未接來電"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"視像通話來電"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"撥出的視像通話"</string>
- <string name="type_missed_video" msgid="954396897034220545">"未接視像通話"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"留言信箱"</string>
- <string name="type_rejected" msgid="7783201828312472691">"拒絕來電"</string>
- <string name="type_blocked" msgid="3521686227115330015">"封鎖來電"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"來電"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"播放語音留言"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"查看聯絡人<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"致電<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"「<xliff:g id="NAMEORNUMBER">%1$s</xliff:g>」的聯絡人詳細資料"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> 次通話次數。"</string>
- <string name="description_video_call" msgid="2933838090743214204">"視像通話。"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"向 <xliff:g id="NAME">%1$s</xliff:g> 傳送短訊"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"未聽取的語音留言"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"開始語音搜尋"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"撥打 <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"未知"</string>
- <string name="voicemail" msgid="3851469869202611441">"留言"</string>
- <string name="private_num" msgid="6374339738119166953">"私人號碼"</string>
- <string name="payphone" msgid="7726415831153618726">"公共電話"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> 秒"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> 分 <xliff:g id="SECONDS">%s</xliff:g> 秒"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g><xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"無法撥打這個電話號碼"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"如要設定留言信箱,請移至 [選單] &gt; [設定]。"</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"如要致電留言信箱,請先關閉飛行模式。"</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"正在載入..."</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"正在從 SIM 卡載入..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM 卡聯絡人"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"沒有可用的聯絡人應用程式"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"無法使用語音搜尋"</string>
- <string name="call_not_available" msgid="8941576511946492225">"由於「電話」應用程式已停用,因此無法打電話。"</string>
- <string name="activity_not_available" msgid="2287665636817987623">"這部裝置上沒有可用的應用程式"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"搜尋聯絡人"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"新增號碼或搜尋聯絡人"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"沒有通話記錄"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"撥號"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"您沒有未接來電。"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"您的留言信箱沒有留言。"</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"您的留言封存是空的。"</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"只顯示我的最愛"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"通話記錄"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"留言封存"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"全部"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"未接來電"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"留言信箱"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"全新、簡單的封鎖功能"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"為更有效保護您的安全,Phone 需要變更封鎖功能的運作方式。系統現在會拒絕已封鎖號碼的來電和短訊,並可能將這些號碼提供給其他應用程式使用。"</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"允許"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"要封鎖 <xliff:g id="NUMBER">%1$s</xliff:g> 嗎?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"將會封鎖這個號碼的來電,而留言將會自動刪除。"</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"將會封鎖這個號碼的來電,但來電可能會轉駁至留言信箱。"</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"您不會再收到這個號碼的來電和短訊。"</string>
- <string name="block_number_ok" msgid="770551992296781873">"封鎖"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"要解除封鎖 <xliff:g id="NUMBER">%1$s</xliff:g> 嗎?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"解除封鎖"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"快速撥號"</string>
- <string name="tab_history" msgid="2563144697322434940">"通話記錄"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"聯絡人"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"留言信箱"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"已從「我的最愛」中移除"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"復原"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"撥打 <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"建立新聯絡人"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"加至現有聯絡人資料"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"傳送短訊"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"進行視像通話"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"封鎖號碼"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> 個新的未接來電"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"快速撥號清單中沒有聯絡人"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"新增常用聯絡人"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"暫時沒有聯絡人"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"新增聯絡人"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"輕觸圖像以查看所有電話號碼,或輕觸並按住圖像以重新排序"</string>
- <string name="remove_contact" msgid="1080555335283662961">"移除"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"視像通話"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"傳送訊息"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"通話詳情"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"致電 <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"未接聽來電:<xliff:g id="NAMEORNUMBER">^1</xliff:g> (<xliff:g id="TIMEOFCALL">^3</xliff:g>,<xliff:g id="TYPEORLOCATION">^2</xliff:g><xliff:g id="PHONEACCOUNT">^4</xliff:g>)。"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"已接聽來電:<xliff:g id="NAMEORNUMBER">^1</xliff:g> (<xliff:g id="TIMEOFCALL">^3</xliff:g>,<xliff:g id="TYPEORLOCATION">^2</xliff:g><xliff:g id="PHONEACCOUNT">^4</xliff:g>)。"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"未收聽的留言來自 <xliff:g id="NAMEORNUMBER">^1</xliff:g> (<xliff:g id="TYPEORLOCATION">^2</xliff:g>),來電時間:<xliff:g id="TIMEOFCALL">^3</xliff:g>,撥打至 <xliff:g id="PHONEACCOUNT">^4</xliff:g>。"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"留言來自 <xliff:g id="NAMEORNUMBER">^1</xliff:g> (<xliff:g id="TYPEORLOCATION">^2</xliff:g>),來電時間:<xliff:g id="TIMEOFCALL">^3</xliff:g>,撥打至 <xliff:g id="PHONEACCOUNT">^4</xliff:g>。"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"致電:<xliff:g id="NAMEORNUMBER">^1</xliff:g> (<xliff:g id="TIMEOFCALL">^3</xliff:g>,<xliff:g id="TYPEORLOCATION">^2</xliff:g><xliff:g id="PHONEACCOUNT">^4</xliff:g>)。"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"使用 <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"透過 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"透過 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"上<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>通過<xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>,透過 <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"撥號"</string>
- <string name="description_call_action" msgid="4000549004089776147">"致電 <xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"與<xliff:g id="NAMEORNUMBER">^1</xliff:g>進行視像通話。"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"收聽來自「<xliff:g id="NAMEORNUMBER">^1</xliff:g>」的留言信箱"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"播放來自 <xliff:g id="NAMEORNUMBER">^1</xliff:g> 的語音留言"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"暫停來自 <xliff:g id="NAMEORNUMBER">^1</xliff:g> 的語音留言"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"刪除來自 <xliff:g id="NAMEORNUMBER">^1</xliff:g> 的語音留言"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>個新留言</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>個新留言</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"為<xliff:g id="NAMEORNUMBER">^1</xliff:g>建立聯絡人"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"加入<xliff:g id="NAMEORNUMBER">^1</xliff:g>至現有的聯絡人"</string>
- <string name="description_details_action" msgid="2433827152749491785">"與「<xliff:g id="NAMEORNUMBER">^1</xliff:g>」的通話詳情"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"已從通話記錄中刪除"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"今天"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"昨天"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"較早"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"通話清單"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"開啟揚聲器。"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"關閉揚聲器。"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"加快播放速度。"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"放慢播放速度。"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"開始或暫停播放。"</string>
- <string name="list_delimeter" msgid="4571593167738725100">"、 "</string>
- <string name="display_options_title" msgid="7812852361055667468">"顯示選項"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"響鈴和震動"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"無障礙功能"</string>
- <string name="ringtone_title" msgid="760362035635084653">"手機鈴聲"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"來電時同時震動"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"撥號盤音效"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"撥號音長度"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"正常"</item>
- <item msgid="6177579030803486015">"長"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"快速回應"</string>
- <string name="call_settings_label" msgid="313434211353070209">"通話"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"來電封鎖"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"暫時關閉來電封鎖功能"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"您在過去 48 小時內曾經使用此手機聯絡緊急服務,因此來電封鎖功能已停用。系統會在 48 小時期限結束後自動重新啟用功能。"</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"匯入號碼"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"您早前透過其他應用程式標記部分來電者,將他們的來電自動轉駁至留言信箱。"</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"查看電話號碼"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"匯入"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"匯入失敗"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"無法封存留言。"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"解除封鎖號碼"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"新增電話號碼"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"將會封鎖這些號碼的來電,而留言將會自動刪除。"</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"將會封鎖這些號碼的來電,但來電可能會轉駁至留言信箱。"</string>
- <string name="block_list" msgid="7760188925338078011">"已封鎖的號碼"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> 無效。"</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"已封鎖 <xliff:g id="NUMBER">%1$s</xliff:g>。"</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"已停用來電封鎖 (48 小時)"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"已撥打緊急電話,已停用來電封鎖功能。"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"通話帳戶"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"開放權限"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"設定權限"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"如要啟用快速撥號功能,請開放「通訊錄」權限。"</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"如要查看通話記錄,請開放「手機」權限。"</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"如要查看聯絡人,請開放「通訊錄」權限。"</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"如要存取留言信箱,請開放「手機」權限。"</string>
- <string name="permission_no_search" msgid="84152933267902056">"如要搜尋聯絡人,請開啟「通訊錄」權限。"</string>
- <string name="permission_place_call" msgid="2101287782615887547">"如要撥打電話,請開放「手機」權限。"</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"手機應用程式沒有系統設定的寫入權限。"</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"已封鎖"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g>正在進行通話"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"封鎖/舉報為垃圾來電"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"封鎖"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"非垃圾來電"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"解除封鎖"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"垃圾號碼"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"要封鎖 <xliff:g id="NUMBER">%1$s</xliff:g> 嗎?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"系統會封鎖此號碼日後的來電和語音留言。"</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"舉報為垃圾來電"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"系統將會封鎖此號碼日後的來電和語音留言,並將此來電舉報為垃圾來電。"</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"要解除封鎖 <xliff:g id="NUMBER">%1$s</xliff:g> 嗎?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"系統會將此號碼解除封鎖,並舉報為非垃圾號碼。此外,系統不會將日後的來電和語音留言視為垃圾來電和語音留言。"</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"要將 <xliff:g id="NUMBER">%1$s</xliff:g> 列入許可名單嗎?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"加入許可名單"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"系統不會將此號碼日後的來電和語音留言識別為垃圾來電和語音留言,並會將此號碼舉報為垃圾號碼。"</string>
-</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
deleted file mode 100644
index 773dd7db6..000000000
--- a/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"電話"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"電話"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"電話撥號鍵盤"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"電話"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"通話紀錄"</string>
- <string name="action_report_number" msgid="4635403959812186162">"回報號碼錯誤"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"複製號碼"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"複製留言轉錄內容"</string>
- <string name="action_block_number" msgid="1482657602262262134">"封鎖號碼"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"已封鎖 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"解除封鎖號碼"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"已解除封鎖 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="block_number_undo" msgid="591338370336724156">"復原"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"刪除"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"撥打前編輯號碼"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"清除通話紀錄"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"刪除語音留言"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"封存語音留言"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"分享語音留言"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"已刪除語音留言"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"已封存語音留言"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"復原"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"前往封存檔案"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"清除通話紀錄?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"這項操作會將所有通話從您的紀錄中刪除"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"正在清除通話紀錄…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"撥打電話"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"未接來電"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"未接公司來電"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"未接來電"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> 通未接來電"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"回撥"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"傳送簡訊"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> 則語音留言</item>
- <item quantity="one">語音留言</item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"播放"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>、<xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"最新語音留言者:<xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"無法播放語音留言"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"正在載入語音留言…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"正在封存語音留言…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"無法載入語音留言"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"僅顯示語音信箱留言"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"僅顯示來電"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"僅顯示已撥電話"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"僅顯示未接來電"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"具有畫面的語音信箱"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"不必撥號即可查看及聽取您的語音留言 (可能需要額外支付數據傳輸費用)。"</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"設定"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"沒有新的語音留言"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"您有新的語音留言,但目前無法載入。"</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"設定您的語音留言"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"無法存取音訊"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"設定"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"撥打語音信箱號碼"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"選擇號碼"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"選擇號碼"</string>
- <string name="make_primary" msgid="5829291915305113983">"記住這個選擇"</string>
- <string name="description_search_button" msgid="3660807558587384889">"搜尋"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"撥號"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"撥號號碼"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"播放或停止播放"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"切換擴音器開關"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"指定播放時間點"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"降低播放速率"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"提高播放速率"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"通話紀錄"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"更多選項"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"撥號鍵盤"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"僅顯示撥出電話"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"僅顯示來電"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"僅顯示未接來電"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"僅顯示語音留言"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"顯示所有通話"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"暫停時間延長 2 秒"</string>
- <string name="add_wait" msgid="3360818652790319634">"延長等待時間"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"設定"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"新增聯絡人"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"所有聯絡人"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"通話詳細資料"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"沒有詳細資料"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"使用觸控音按鍵"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"返回進行中的通話"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"新增通話"</string>
- <string name="type_incoming" msgid="6502076603836088532">"來電"</string>
- <string name="type_outgoing" msgid="343108709599392641">"撥出電話"</string>
- <string name="type_missed" msgid="2720502601640509542">"未接來電"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"視訊通話來電"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"撥出的視訊通話"</string>
- <string name="type_missed_video" msgid="954396897034220545">"未接視訊通話來電"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"語音信箱"</string>
- <string name="type_rejected" msgid="7783201828312472691">"拒接的來電"</string>
- <string name="type_blocked" msgid="3521686227115330015">"封鎖的來電"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"來電"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"播放語音留言"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"查看聯絡人<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"撥電話給<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"「<xliff:g id="NAMEORNUMBER">%1$s</xliff:g>」的聯絡詳細資料"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"通話 <xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> 次。"</string>
- <string name="description_video_call" msgid="2933838090743214204">"視訊通話。"</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"傳送簡訊給<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"未聽取的語音留言"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"開始進行語音搜尋"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"撥打 <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"不明"</string>
- <string name="voicemail" msgid="3851469869202611441">"語音留言"</string>
- <string name="private_num" msgid="6374339738119166953">"私人號碼"</string>
- <string name="payphone" msgid="7726415831153618726">"公用電話"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> 秒"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> 分 <xliff:g id="SECONDS">%s</xliff:g> 秒"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g><xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"無法撥打這個號碼"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"如要設定語音信箱,請前往 [選單] &gt; [設定]。"</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"如要聽語音留言,請先關閉飛行模式。"</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"載入中…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"從 SIM 卡讀取中…"</string>
- <string name="simContacts_title" msgid="27341688347689769">"SIM 卡聯絡人"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"沒有可用的聯絡人應用程式"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"無法使用語音搜尋"</string>
- <string name="call_not_available" msgid="8941576511946492225">"「電話」應用程式已遭停用,因此無法撥打電話。"</string>
- <string name="activity_not_available" msgid="2287665636817987623">"這個裝置未安裝可執行該操作的應用程式"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"搜尋聯絡人"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"新增號碼或搜尋聯絡人"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"沒有任何通話紀錄"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"撥打電話"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"您沒有任何未接來電。"</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"您沒有任何語音留言。"</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"您尚未封存任何語音留言。"</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"只顯示我的收藏"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"通話紀錄"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"語音留言封存檔案"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"全部"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"未接來電"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"語音留言"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"全新、簡易的封鎖功能"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"「電話」應用程式需要變更封鎖功能的運作方式,以進一步保護您的安全。現在開始,系統會拒絕已封鎖號碼的來電和簡訊,還會將這些號碼提供給其他應用程式。"</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"允許"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"要封鎖 <xliff:g id="NUMBER">%1$s</xliff:g> 嗎?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"系統會封鎖這組號碼的來電,並自動刪除對方的語音留言。"</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"系統會封鎖這組號碼的來電,但對方仍可錄製語音留言給您。"</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"您不會再收到這組號碼的來電或簡訊。"</string>
- <string name="block_number_ok" msgid="770551992296781873">"封鎖"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"要解除封鎖 <xliff:g id="NUMBER">%1$s</xliff:g> 嗎?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"解除封鎖"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"快速撥號"</string>
- <string name="tab_history" msgid="2563144697322434940">"通話紀錄"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"聯絡人"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"語音信箱"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"已從常用聯絡人移除"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"復原"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"撥打 <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"建立新聯絡人"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"新增至聯絡人"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"傳送簡訊"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"進行視訊通話"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"封鎖號碼"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> 通新的未接來電"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"您的快速撥號功能尚未設定任何聯絡人"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"新增常用聯絡人"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"您尚未加入任何聯絡人"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"新增聯絡人"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"輕觸圖片即可查看所有號碼,輕觸並按住則可重新排序"</string>
- <string name="remove_contact" msgid="1080555335283662961">"移除"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"視訊通話"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"傳送簡訊"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"通話詳細資料"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"撥號給「<xliff:g id="NAMEORNUMBER">^1</xliff:g>」"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"未接來電:<xliff:g id="NAMEORNUMBER">^1</xliff:g>/<xliff:g id="TYPEORLOCATION">^2</xliff:g>/<xliff:g id="TIMEOFCALL">^3</xliff:g>/<xliff:g id="PHONEACCOUNT">^4</xliff:g>"</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"已接來電:<xliff:g id="NAMEORNUMBER">^1</xliff:g>/<xliff:g id="TYPEORLOCATION">^2</xliff:g>/<xliff:g id="TIMEOFCALL">^3</xliff:g>/<xliff:g id="PHONEACCOUNT">^4</xliff:g>"</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"您有一則未讀語音留言,來電者:<xliff:g id="NAMEORNUMBER">^1</xliff:g>,<xliff:g id="TYPEORLOCATION">^2</xliff:g>,<xliff:g id="TIMEOFCALL">^3</xliff:g>,<xliff:g id="PHONEACCOUNT">^4</xliff:g>。"</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"您有一則語音留言,來電者:<xliff:g id="NAMEORNUMBER">^1</xliff:g>,<xliff:g id="TYPEORLOCATION">^2</xliff:g>,<xliff:g id="TIMEOFCALL">^3</xliff:g>,<xliff:g id="PHONEACCOUNT">^4</xliff:g>。"</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"撥出通話:<xliff:g id="NAMEORNUMBER">^1</xliff:g>/<xliff:g id="TYPEORLOCATION">^2</xliff:g>/<xliff:g id="TIMEOFCALL">^3</xliff:g>/<xliff:g id="PHONEACCOUNT">^4</xliff:g>"</string>
- <string name="description_phone_account" msgid="1767072759541443861">"透過 <xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"透過 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"透過 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>,透過 <xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> (透過 <xliff:g id="NUMBER">%2$s</xliff:g>)"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"撥號"</string>
- <string name="description_call_action" msgid="4000549004089776147">"撥號給「<xliff:g id="NAMEORNUMBER">^1</xliff:g>」"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"與「<xliff:g id="NAMEORNUMBER">^1</xliff:g>」視訊通話。"</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"聽取「<xliff:g id="NAMEORNUMBER">^1</xliff:g>」的語音留言"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"播放來自「<xliff:g id="NAMEORNUMBER">^1</xliff:g>」的語音留言"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"暫停來自「<xliff:g id="NAMEORNUMBER">^1</xliff:g>」的語音留言"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"刪除來自「<xliff:g id="NAMEORNUMBER">^1</xliff:g>」的語音留言"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> 則新的語音留言</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> 則新的語音留言</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"建立「<xliff:g id="NAMEORNUMBER">^1</xliff:g>」的聯絡人資訊"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"將「<xliff:g id="NAMEORNUMBER">^1</xliff:g>」新增到現有的聯絡人"</string>
- <string name="description_details_action" msgid="2433827152749491785">"與「<xliff:g id="NAMEORNUMBER">^1</xliff:g>」通話的詳細資料"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"已從通話紀錄中刪除"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"今天"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"昨天"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"較舊"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"通話清單"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"開啟喇叭。"</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"關閉喇叭。"</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"加快播放速度。"</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"放慢播放速度。"</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"開始或暫停播放。"</string>
- <string name="list_delimeter" msgid="4571593167738725100">"、 "</string>
- <string name="display_options_title" msgid="7812852361055667468">"顯示選項"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"音效與振動"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"協助工具"</string>
- <string name="ringtone_title" msgid="760362035635084653">"手機鈴聲"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"來電時同時震動"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"撥號鍵盤音效"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"撥號音長度"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"一般"</item>
- <item msgid="6177579030803486015">"長"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"快速回應"</string>
- <string name="call_settings_label" msgid="313434211353070209">"通話"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"來電封鎖"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"來電封鎖功能已暫時關閉"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"由於您曾在過去 48 小時內使用這支手機撥打緊急服務電話,因此來電封鎖功能已停用。此功能停用達 48 小時後,將由系統自動重新啟用。"</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"匯入電話號碼"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"您之前曾將某些來電者標示為自動透過其他應用程式轉到語音信箱。"</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"查看號碼"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"匯入"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"匯入失敗"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"無法封存語音留言。"</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"解除封鎖號碼"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"新增號碼"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"系統會封鎖這些號碼的來電,並自動刪除對方的語音留言。"</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"系統會封鎖這些號碼的來電,但對方仍可錄製語音留言給您。"</string>
- <string name="block_list" msgid="7760188925338078011">"已封鎖的號碼"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> 無效。"</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> 已在封鎖清單中。"</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"來電封鎖功能會停用 48 小時"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"由於您曾撥打緊急電話,因此本功能已停用。"</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"通話帳戶"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"開啟"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"設定權限"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"如要啟用快速撥號,請開啟「聯絡人」存取權限。"</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"如要查看您的通話紀錄,請開啟「電話」存取權限。"</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"如要查看您的聯絡人,請開啟「聯絡人」存取權限。"</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"如要存取您的語音信箱,請開啟「電話」存取權限。"</string>
- <string name="permission_no_search" msgid="84152933267902056">"如要搜尋您的聯絡人,請開啟「聯絡人」存取權限。"</string>
- <string name="permission_place_call" msgid="2101287782615887547">"如要撥打電話,請開啟「電話」存取權限。"</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"電話應用程式的權限不足,無法寫入系統設定。"</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"已封鎖"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"目前顯示的聯絡人:<xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"封鎖/回報為騷擾/廣告電話"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"封鎖"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"非騷擾/廣告電話"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"解除封鎖"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"騷擾/廣告電話"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"要封鎖 <xliff:g id="NUMBER">%1$s</xliff:g> 嗎?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"系統會封鎖這組號碼日後的來電和語音留言。"</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"回報為騷擾/廣告電話"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"系統會封鎖這組號碼日後的來電和語音留言,並將本次來電回報為騷擾/廣告電話。"</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"要解除封鎖 <xliff:g id="NUMBER">%1$s</xliff:g> 嗎?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"系統會解除封鎖這組號碼並回報為非騷擾/廣告電話,該號碼日後的來電和語音留言不會歸類為騷擾/廣告電話。"</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"要將 <xliff:g id="NUMBER">%1$s</xliff:g> 加入許可清單嗎?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"加入許可清單"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"系統不會將這組號碼日後的來電和語音留言歸類為騷擾/廣告電話,而且會將這組號碼回報為非騷擾/廣告電話。"</string>
-</resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
deleted file mode 100644
index 97b3325ec..000000000
--- a/res/values-zu/strings.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applicationLabel" msgid="7762561155467201526">"Ifoni"</string>
- <string name="launcherActivityLabel" msgid="1129729740601172692">"Ifoni"</string>
- <string name="launcherDialpadActivityLabel" msgid="3959809805046059167">"Iphedi yokudayela yefoni"</string>
- <string name="dialerIconLabel" msgid="6500826552823403796">"Ifoni"</string>
- <string name="callHistoryIconLabel" msgid="3734413397291301223">"Umlando wekholi"</string>
- <string name="action_report_number" msgid="4635403959812186162">"Bika inombolo engalungile"</string>
- <string name="action_copy_number_text" msgid="588249522108594155">"Kopisha inombolo"</string>
- <string name="copy_transcript_text" msgid="5652787482893879382">"Kopisha ukukhiphela"</string>
- <string name="action_block_number" msgid="1482657602262262134">"Vimba inombolo"</string>
- <string name="snackbar_number_blocked" msgid="939830142765936021">"<xliff:g id="NUMBER">%1$s</xliff:g> ivinjiwe"</string>
- <string name="action_unblock_number" msgid="3043777622269776127">"Vulela inombolo"</string>
- <string name="snackbar_number_unblocked" msgid="4854738171099045912">"<xliff:g id="NUMBER">%1$s</xliff:g> vulela"</string>
- <string name="block_number_undo" msgid="591338370336724156">"HLEHLISA"</string>
- <string name="call_details_delete" msgid="1833359621896346955">"Susa"</string>
- <string name="action_edit_number_before_call" msgid="3100221149873436485">"Hlela inombolo ngaphambi kwekholi"</string>
- <string name="call_log_delete_all" msgid="4677609108288680411">"Sula umlando wekholi"</string>
- <string name="call_log_trash_voicemail" msgid="1283948488625129019">"Susa ivoyisimeyili"</string>
- <string name="call_log_archive_voicemail" msgid="9072936797223284265">"Faka kungobo yomlando ivoyisimeyili"</string>
- <string name="call_log_share_voicemail" msgid="7272635349055945546">"Abelana ngevoyisimeyili"</string>
- <string name="snackbar_voicemail_deleted" msgid="5098542835986188582">"Ivoyisimeyili isusiwe"</string>
- <string name="snackbar_voicemail_archived" msgid="7442340403660554373">"Ivoyisimeyili ifakwe kungobo yomlando"</string>
- <string name="snackbar_voicemail_deleted_undo" msgid="3741811385891289167">"HLEHLISA"</string>
- <string name="snackbar_voicemail_archived_goto" msgid="9186182602955185448">"IYA KUNGOBO YOMLANDO"</string>
- <string name="clearCallLogConfirmation_title" msgid="801753155679372984">"Sula umlando wekholi?"</string>
- <string name="clearCallLogConfirmation" msgid="7899552396101432827">"Lokhu kuzosusa wonke amakholi kusukela kumlando wakho"</string>
- <string name="clearCallLogProgress_title" msgid="3372471156216306132">"Isula umlando wekholi…"</string>
- <string name="userCallActivityLabel" product="default" msgid="6652512551977445095">"Ifoni"</string>
- <string name="notification_missedCallTitle" msgid="2078223183024451723">"Ikholi ephuthelwe"</string>
- <string name="notification_missedWorkCallTitle" msgid="8418335304394771688">"Ugeje ikholi yomsebenzi"</string>
- <string name="notification_missedCallsTitle" msgid="4948840634672742797">"Amakholi akuphuthele"</string>
- <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> amakholi akulahlekele"</string>
- <string name="notification_missedCall_call_back" msgid="1991552423738343203">"Phinda ushayele"</string>
- <string name="notification_missedCall_message" msgid="3042123733754151052">"Umlayezo"</string>
- <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
- <item quantity="one"> <xliff:g id="COUNT">%1$d</xliff:g> Amavoyisimeyili </item>
- <item quantity="other"> <xliff:g id="COUNT">%1$d</xliff:g> Amavoyisimeyili </item>
- </plurals>
- <string name="notification_action_voicemail_play" msgid="6113133136977996863">"Dlala"</string>
- <string name="notification_voicemail_callers_list" msgid="1153954809339404149">"<xliff:g id="NEWER_CALLERS">%1$s</xliff:g>, <xliff:g id="OLDER_CALLER">%2$s</xliff:g>"</string>
- <string name="notification_new_voicemail_ticker" msgid="895342132049452081">"I-imeyli entsha esuka ku <xliff:g id="CALLER">%1$s</xliff:g>"</string>
- <string name="voicemail_playback_error" msgid="3356071912353297599">"Ayikwazanga ukudlala ivoyisimeyili"</string>
- <string name="voicemail_fetching_content" msgid="1287895365599580842">"Ilayisha ivoyisimeyili…"</string>
- <string name="voicemail_archiving_content" msgid="722322091690281157">"Ifaka kungobo yomlando yevoyisimeyili…"</string>
- <string name="voicemail_fetching_timout" msgid="3959428065511972176">"Ayikwazi ukulayisha ivoyisimeyili"</string>
- <string name="call_log_voicemail_header" msgid="3945407886667089173">"Amakholi anevoyisimeyili kuphela"</string>
- <string name="call_log_incoming_header" msgid="2787722299753674684">"Amakholi angenayo kuphela"</string>
- <string name="call_log_outgoing_header" msgid="761009180766735769">"Amakholi aphumayo kuphela"</string>
- <string name="call_log_missed_header" msgid="8017148056610855956">"Amakholi agejiwe kuphela"</string>
- <string name="visual_voicemail_title" msgid="4574199312906348905">"Ivoyisimeyili ebonakalayo"</string>
- <string name="visual_voicemail_text" msgid="164967285234132509">"Bona futhi ulalele ivoyisimeyili yakho, ngaphandle kokushayela inombolo. Amanani edatha angahle asebenze."</string>
- <string name="visual_voicemail_settings" msgid="8090338793118794741">"Izilungiselelo"</string>
- <string name="voicemail_status_voicemail_not_available" msgid="5222480147701456390">"Izibuyekezo zevoyisimeyili azitholakali"</string>
- <string name="voicemail_status_messages_waiting" msgid="6329544650250068650">"Ivoyisimeyili entsha ilindile. Ayikwazi ukulayisha khona manje."</string>
- <string name="voicemail_status_configure_voicemail" msgid="8300808991932816153">"Setha ivoyisimeyili yakho"</string>
- <string name="voicemail_status_audio_not_available" msgid="2449801102560158082">"Umsindo awutholakali"</string>
- <string name="voicemail_status_action_configure" msgid="8671796489912239589">"Setha"</string>
- <string name="voicemail_status_action_call_server" msgid="1824816252288551794">"Shayela ivoyisimeyili"</string>
- <string name="call_log_item_count_and_date" msgid="7641933305703520787">"(<xliff:g id="COUNT">%1$d</xliff:g>) <xliff:g id="DATE">%2$s</xliff:g>"</string>
- <string name="sms_disambig_title" msgid="5846266399240630846">"Khetha inombolo"</string>
- <string name="call_disambig_title" msgid="4392886850104795739">"Khetha inombolo"</string>
- <string name="make_primary" msgid="5829291915305113983">"Khumbula lokhu okukhethiwe"</string>
- <string name="description_search_button" msgid="3660807558587384889">"sesha"</string>
- <string name="description_dial_button" msgid="1274091017188142646">"dayela"</string>
- <string name="description_digits_edittext" msgid="8760207516497016437">"inombolo okumele uyidayele"</string>
- <string name="description_playback_start_stop" msgid="5060732345522492292">"Dlala noma misa ukudlala"</string>
- <string name="description_playback_speakerphone" msgid="6008323900245707504">"Vula noma vala isipikha sefoni"</string>
- <string name="description_playback_seek" msgid="4509404274968530055">"Funa indawo yokudlala"</string>
- <string name="description_rate_decrease" msgid="3161652589401708284">"Yehlisa isilinganiso sokudlala"</string>
- <string name="description_rate_increase" msgid="6324606574127052385">"Yenyusa isilinganiso sokudlala"</string>
- <string name="action_menu_call_history_description" msgid="9018442816219748968">"Umlando wekholi"</string>
- <string name="action_menu_overflow_description" msgid="2303272250613084574">"Izinketho eziningi"</string>
- <string name="action_menu_dialpad_button" msgid="1425910318049008136">"okokudayila"</string>
- <string name="menu_show_outgoing_only" msgid="1965570298133301970">"Bonisa eziphumayo kuphela"</string>
- <string name="menu_show_incoming_only" msgid="7534206815238877417">"Bonisa okungenayo kuphela"</string>
- <string name="menu_show_missed_only" msgid="154473166059743996">"Bonisa okugejiwe kuphela"</string>
- <string name="menu_show_voicemails_only" msgid="1898421289561435703">"Bonisa ama-imeyli ezwi kuphela"</string>
- <string name="menu_show_all_calls" msgid="7560347482073345885">"Bonisa zonke izingcingo ezenziwe"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"Faka ukumisa okwesikhashana kwamasekhondi angu-2"</string>
- <string name="add_wait" msgid="3360818652790319634">"Yengeza ukulinda"</string>
- <string name="dialer_settings_label" msgid="4305043242594150479">"Izilungiselelo"</string>
- <string name="menu_newContact" msgid="1209922412763274638">"Othintana naye omusha"</string>
- <string name="menu_allContacts" msgid="6948308384034051670">"Bonke oxhumana nabo"</string>
- <string name="callDetailTitle" msgid="5340227785196217938">"Imininingwane yekholi"</string>
- <string name="toast_call_detail_error" msgid="6947041258280380832">"Imininingwane ayitholakali"</string>
- <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Sebenzisa ikhiphedi yethoni yokuthinta"</string>
- <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Buyela kukholi eqhubekayo"</string>
- <string name="dialer_addAnotherCall" msgid="4205688819890074468">"Yengeza ikholi"</string>
- <string name="type_incoming" msgid="6502076603836088532">"Ikholi engenayo"</string>
- <string name="type_outgoing" msgid="343108709599392641">"Ikholi oluphumayo"</string>
- <string name="type_missed" msgid="2720502601640509542">"Ikholi elahlekeli"</string>
- <string name="type_incoming_video" msgid="82323391702796181">"Ikholi yevidiyo engenayo"</string>
- <string name="type_outgoing_video" msgid="2858140021680755266">"Ikholi yevidiyo ephumayo"</string>
- <string name="type_missed_video" msgid="954396897034220545">"Ikholi yevidiyo ephuthelwe"</string>
- <string name="type_voicemail" msgid="5153139450668549908">"Ivoyisimeyili"</string>
- <string name="type_rejected" msgid="7783201828312472691">"Ikholi enqatshelwe"</string>
- <string name="type_blocked" msgid="3521686227115330015">"Ikholi evinjiwe"</string>
- <string name="actionIncomingCall" msgid="6028930669817038600">"Amakholi angenayo"</string>
- <string name="description_call_log_play_button" msgid="651182125650429846">"Dlala i-imeyli yezwi"</string>
- <string name="description_view_contact" msgid="5205669345700598415">"Bheka oxhumana naye <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call" msgid="3443678121983852666">"Fonela <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_contact_details" msgid="51229793651342809">"Imininingwane yokuxhumana ka-<xliff:g id="NAMEORNUMBER">%1$s</xliff:g>"</string>
- <string name="description_num_calls" msgid="1601505153694540074">"<xliff:g id="NUMBEROFCALLS">%1$s</xliff:g> amakholi."</string>
- <string name="description_video_call" msgid="2933838090743214204">"Ikholi yevidiyo."</string>
- <string name="description_send_text_message" msgid="3118485319691414221">"Thumela i-SMS ku-<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="description_call_log_unheard_voicemail" msgid="118101684236996786">"I-imeyli yezwi engazwakalanga"</string>
- <string name="description_start_voice_search" msgid="520539488194946012">"Qalisa ukusesha ngezwi"</string>
- <string name="menu_callNumber" msgid="997146291983360266">"Shayela <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="unknown" msgid="740067747858270469">"Akwaziwa"</string>
- <string name="voicemail" msgid="3851469869202611441">"Ivoyisimeyili"</string>
- <string name="private_num" msgid="6374339738119166953">"Inombolo yangasese"</string>
- <string name="payphone" msgid="7726415831153618726">"Ucingo olufakwa imali"</string>
- <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> isekhondi"</string>
- <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> amaminithi <xliff:g id="SECONDS">%s</xliff:g> amasekhondi"</string>
- <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
- <skip />
- <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g> ngo-<xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="voicemailDurationFormat" msgid="228211252076289564">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="voicemailCallLogDateTimeFormatWithDuration" msgid="5118563814451588397">"<xliff:g id="DATEANDTIME">%1$s</xliff:g> • <xliff:g id="DURATION">%2$s</xliff:g>"</string>
- <string name="dialog_phone_call_prohibited_message" msgid="5730565540182492608">"Ayikwazi ukushayela le nombolo"</string>
- <string name="dialog_voicemail_not_ready_message" msgid="4384716252789515378">"Ukuya emyalezweni wephimbo, yana ezisethweni &gt; zemenyu."</string>
- <string name="dialog_voicemail_airplane_mode_message" msgid="530922773669546093">"Ukushayela i-voicemail, vala kuqala imodi Yendiza."</string>
- <string name="contact_list_loading" msgid="5488620820563977329">"Iyalayisha…"</string>
- <string name="imei" msgid="3045126336951684285">"IMEI"</string>
- <string name="meid" msgid="6210568493746275750">"I-MEID"</string>
- <string name="simContacts_emptyLoading" msgid="6700035985448642408">"Ilayisha kusuka ekhadini le-SIM..."</string>
- <string name="simContacts_title" msgid="27341688347689769">"Othintana nabo bekhadi le-SIM"</string>
- <string name="add_contact_not_available" msgid="5547311613368004859">"Alukho uhlelo lokusebenza loxhumana nabo olutholakalayo"</string>
- <string name="voice_search_not_available" msgid="2977719040254285301">"Usesho lwezwi alutholakali"</string>
- <string name="call_not_available" msgid="8941576511946492225">"Ayikwazi ukwenza ikholi yefoni ngoba uhlelo lokusebenza lwefoni likhutshaziwe."</string>
- <string name="activity_not_available" msgid="2287665636817987623">"Alukho uhlelo lokusebenza lalokho kule divayisi"</string>
- <string name="dialer_hint_find_contact" msgid="3574350254520035364">"Sesha oxhumana nabo"</string>
- <string name="block_number_search_hint" msgid="5409571607043872758">"Engeza inombolo noma useshe oxhumana nabo"</string>
- <string name="call_log_all_empty" msgid="8357417710416340920">"Umlando wakho wekholi awunalutho"</string>
- <string name="call_log_all_empty_action" msgid="9093210119068366147">"Yenza ikholi"</string>
- <string name="call_log_missed_empty" msgid="3927274175205520135">"Awunawo amakholi aphuthelwe."</string>
- <string name="call_log_voicemail_empty" msgid="8383585074222277550">"Ibhokisi lokungenayo kwakho levoyisimeyili alinalutho."</string>
- <string name="voicemail_archive_empty" msgid="1087408796679056787">"Ingobo yomlando yevoyisimeyili yakho ayinalutho."</string>
- <string name="show_favorites_only" msgid="5520072531022614595">"Bonisa izintandokazi kuphela"</string>
- <string name="call_log_activity_title" msgid="7949633151371183580">"Umlando wekholi"</string>
- <string name="voicemail_archive_activity_title" msgid="2638669189424535229">"Ukufaka kungobo yomlando iyoyisimeyili"</string>
- <string name="call_log_all_title" msgid="3566738938889333307">"Konke"</string>
- <string name="call_log_missed_title" msgid="4541142293870638971">"Phuthelwe"</string>
- <string name="call_log_voicemail_title" msgid="940422274047025948">"Ivoyisimeyili"</string>
- <string name="migrate_blocked_numbers_dialog_title" msgid="2039363199438872626">"Ukuvimbela okusha, okwenziwe lula"</string>
- <string name="migrate_blocked_numbers_dialog_message" msgid="5598530398682662860">"Ukuze uvikeleke kangcono, ifoni kumele sishintshe indlela okusebenza ngayo ukuvimbela. Izinombolo zakho ezivinjelwe manje zizomisa kokubili amakholi nemibhalo futhi zingabiwa nezinye izinhlelo zokusebenza."</string>
- <string name="migrate_blocked_numbers_dialog_allow_button" msgid="3116140286537760629">"Vumela"</string>
- <string name="block_number_confirmation_title" msgid="6862219558186931304">"Vimba <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_number_confirmation_message_vvm" msgid="5655646611437082611">"Amakholi wangakusasa kusuka kule nombolo azovinyelwa futhi amavoyisimeyili azosuswa ngokuzenzakalela."</string>
- <string name="block_number_confirmation_message_no_vvm" msgid="4511900601491718173">"Amakholi kusuka kule nombolo azovinjwa, kodwa inkampani yenenethiwekhi ingakwazi ukukushiyela amavoyisimeyili."</string>
- <string name="block_number_confirmation_message_new_filtering" msgid="4524854769790960179">"Ngeke usathola amakholi noma imibhalo kusukela kule nombolo."</string>
- <string name="block_number_ok" msgid="770551992296781873">"VIMBA"</string>
- <string name="unblock_number_confirmation_title" msgid="252824779504084354">"Vulela i-<xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_ok" msgid="6449899182699684786">"VULELA"</string>
- <string name="tab_speed_dial" msgid="7552166276545648893">"Ukudayela okusheshayo"</string>
- <string name="tab_history" msgid="2563144697322434940">"Umlando wekholi"</string>
- <string name="tab_all_contacts" msgid="1410922767166533690">"Oxhumana nabo"</string>
- <string name="tab_voicemail" msgid="155024725947496746">"Ivoyisimeyili"</string>
- <string name="favorite_hidden" msgid="5011234945140912047">"Kukhishiwe kusuka kuzintandokazi"</string>
- <string name="favorite_hidden_undo" msgid="2508998611039406474">"Hlehlisa"</string>
- <string name="search_shortcut_call_number" msgid="7277194656832895870">"Shayela ku-<xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="search_shortcut_create_new_contact" msgid="1679917465521554093">"Dala oxhumana naye omusha"</string>
- <string name="search_shortcut_add_to_contact" msgid="4327842393369915751">"Engeza koxhumana naye"</string>
- <string name="search_shortcut_send_sms_message" msgid="2569304043345025525">"Thumela i-SMS"</string>
- <string name="search_shortcut_make_video_call" msgid="1265971685034465166">"Yenza ikholi yevidiyo"</string>
- <string name="search_shortcut_block_number" msgid="4787156645163436072">"Vimba inombolo"</string>
- <string name="num_missed_calls" msgid="8081736535604293886">"<xliff:g id="NUMBER">%s</xliff:g> amakholi amasha owaphuthelwe"</string>
- <string name="speed_dial_empty" msgid="8838921693673366129">"Akekho umuntu osekudayeleni kwakho okusheshayo okwamanje"</string>
- <string name="speed_dial_empty_add_favorite_action" msgid="7944893641496695770">"Engeza intandokazi"</string>
- <string name="all_contacts_empty" msgid="471370638298229686">"Awunabo oxhumana nabo okwamanje"</string>
- <string name="all_contacts_empty_add_contact_action" msgid="1515782853819374618">"Engeza oxhumana naye"</string>
- <string name="contact_tooltip" msgid="2019777545923635266">"Thinta isithombe ukuze ubone zonke izinombolo noma uthinte uphinde ubambe ukuze uhlele kabusha"</string>
- <string name="remove_contact" msgid="1080555335283662961">"Susa"</string>
- <string name="call_log_action_video_call" msgid="7724301709041128296">"Ikholi yevidiyo"</string>
- <string name="call_log_action_send_message" msgid="5679719296905285131">"Thumela umlayezo"</string>
- <string name="call_log_action_details" msgid="701345508704970622">"Imininingwane yekholi"</string>
- <string name="call_log_action_call" msgid="463690849042459842">"Shayela ku-<xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_incoming_missed_call" msgid="2381085098795943627">"Ikholi egejiwe kusuka ku-<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_incoming_answered_call" msgid="7117665748428816544">"Phendula ikholi kusuka ku-<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_unread_voicemail" msgid="5826351827625665597">"Ivoyisimeyili engafundiwe kusukela ku-<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_read_voicemail" msgid="133974208364152610">"Ivoyisimeyili esuka ku-<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_outgoing_call" msgid="6386364390619734734">"Ikholi eya ku-<xliff:g id="NAMEORNUMBER">^1</xliff:g>, <xliff:g id="TYPEORLOCATION">^2</xliff:g>, <xliff:g id="TIMEOFCALL">^3</xliff:g>, <xliff:g id="PHONEACCOUNT">^4</xliff:g>."</string>
- <string name="description_phone_account" msgid="1767072759541443861">"ku-<xliff:g id="PHONEACCOUNT">^1</xliff:g>"</string>
- <string name="description_via_number" msgid="3503311803959108316">"nge-<xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="call_log_via_number" msgid="8373282986443543296">"nge-<xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="description_via_number_phone_account" msgid="5426866894799626474">"ku-<xliff:g id="PHONEACCOUNT">%1$s</xliff:g>, nge-<xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="call_log_via_number_phone_account" msgid="4394943791173578941">"<xliff:g id="PHONEACCOUNT">%1$s</xliff:g> nge-<xliff:g id="NUMBER">%2$s</xliff:g>"</string>
- <string name="description_call_log_call_action" msgid="3682561657090693134">"Shaya"</string>
- <string name="description_call_action" msgid="4000549004089776147">"Shayela ku-<xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_video_call_action" msgid="7386922428155062213">"Ikholi yevidiyo ka-<xliff:g id="NAMEORNUMBER">^1</xliff:g>."</string>
- <string name="description_voicemail_action" msgid="8054891873788903063">"Lalela ivoyisimeyili esuka ku-<xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_play" msgid="2689369874037785439">"Dlala ivoyisimeyili kusuka ku-<xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_pause" msgid="3905259748756919693">"Misa okwesikhashana ivoyisimeyili kusuka ku-<xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_voicemail_delete" msgid="2025472770630153436">"Sula ivoyisimeyili kusuka ku-<xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <plurals name="description_voicemail_unread" formatted="false" msgid="8708346053055570332">
- <item quantity="one"><xliff:g id="COUNT_1">%d</xliff:g> amavoyisimeyili amasha</item>
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> amavoyisimeyili amasha</item>
- </plurals>
- <string name="description_create_new_contact_action" msgid="818755978791008167">"Dalela oxhumana naye u-<xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="description_add_to_existing_contact_action" msgid="6081200053494414351">"Engeza u-<xliff:g id="NAMEORNUMBER">^1</xliff:g> koxhumana naye okhona"</string>
- <string name="description_details_action" msgid="2433827152749491785">"Imininingwane yekholi ye-<xliff:g id="NAMEORNUMBER">^1</xliff:g>"</string>
- <string name="toast_entry_removed" msgid="8010830299576311534">"Isusiwe kusukela kumlando wekholi"</string>
- <string name="call_log_header_today" msgid="3225248682434212981">"Namhlanje"</string>
- <string name="call_log_header_yesterday" msgid="9139172458834033092">"Izolo"</string>
- <string name="call_log_header_other" msgid="5769921959940709084">"Okudala"</string>
- <string name="call_detail_list_header" msgid="3752717059699600861">"Uhlu lwamakholi"</string>
- <string name="voicemail_speaker_on" msgid="209154030283487068">"Vula isipikha."</string>
- <string name="voicemail_speaker_off" msgid="7390530056413093958">"Vala isipikha."</string>
- <string name="voicemail_play_faster" msgid="3444751008615323006">"Dala ngokushesha."</string>
- <string name="voicemail_play_slower" msgid="4544796503902818832">"Dlala ngokungasheshi."</string>
- <string name="voicemail_play_start_pause" msgid="3687447935787768983">"Qala noma misa isikhashana ukudlala."</string>
- <string name="list_delimeter" msgid="4571593167738725100">", "</string>
- <string name="display_options_title" msgid="7812852361055667468">"Izinketho zokubukeka"</string>
- <string name="sounds_and_vibration_title" msgid="1692290115642160845">"Imisindo nokudlidliza"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"Ukufinyeleleka"</string>
- <string name="ringtone_title" msgid="760362035635084653">"Ithoni yokukhala yefoni"</string>
- <string name="vibrate_on_ring_title" msgid="3362916460327555241">"Iyadlidliza futhi ngamakholi"</string>
- <string name="dtmf_tone_enable_title" msgid="6571449695997521615">"Ukukhala kwephedi yokudayela"</string>
- <string name="dtmf_tone_length_title" msgid="8581125689808919460">"Ubude bethoni bephedi yokudayela"</string>
- <string-array name="dtmf_tone_length_entries">
- <item msgid="1036113889050195575">"Okujwayelekile"</item>
- <item msgid="6177579030803486015">"Kude"</item>
- </string-array>
- <string name="respond_via_sms_setting_title" msgid="1318281521087951580">"Izimpendulo ezisheshayo"</string>
- <string name="call_settings_label" msgid="313434211353070209">"Amakholi"</string>
- <string name="manage_blocked_numbers_label" msgid="5959801428936629196">"Ukuvimbela ikholi"</string>
- <string name="blocked_numbers_disabled_emergency_header_label" msgid="7870947220238070418">"Ukuvimbela ikholi kuvalwe isikhashana"</string>
- <string name="blocked_numbers_disabled_emergency_desc" msgid="7755803249073401381">"Ukuvimbela ikholi kukhutshaziwe ngoba uxhumane nabosizo oluphuthumayo kusukela kule foni phakathi kwamahora angu-48 wokugcina. Kuzophinda kunikwe amandla ngokuzenzakalela uma sokuphele isikhathi samahora angu-48."</string>
- <string name="import_send_to_voicemail_numbers_label" msgid="1606601823746799926">"Ngenisa izinombolo"</string>
- <string name="blocked_call_settings_import_description" msgid="8640906226815125906">"Ngaphambilini umake abanye abashayi ukuthi bathunyelwe ngokuzenzakalelayo kuvoyisimeyili ngezinhlelo zokusebenza."</string>
- <string name="blocked_call_settings_view_numbers_button" msgid="6698986720875955935">"Buka izinombolo"</string>
- <string name="blocked_call_settings_import_button" msgid="178821255125295473">"Ngenisa"</string>
- <string name="send_to_voicemail_import_failed" msgid="5547202002133560069">"Ukulanda akuphumelelanga"</string>
- <string name="voicemail_archive_failed" msgid="6263467962738772223">"Yehlulekile ukufaka kungobo yomlando ivoyisimeyili."</string>
- <string name="description_blocked_number_list_delete" msgid="3240093199107796792">"Vulela inombolo"</string>
- <string name="addBlockedNumber" msgid="6064812257482886526">"Engeza inombolo"</string>
- <string name="block_number_footer_message_vvm" msgid="5387302253765439712">"Amakholi kusuka kulezi lzinombolo azovinjwa futhi amavoyisimeyili azosulwa ngokuzenzakalela."</string>
- <string name="block_number_footer_message_no_vvm" msgid="223238617533822381">"Amakholi kusuka kulezi zinombolo azovinjwa, kodwa angakwazi ukushiya amavoyisimeyili."</string>
- <string name="block_list" msgid="7760188925338078011">"Izinombolo ezivinjiwe"</string>
- <string name="invalidNumber" msgid="619058581062192851">"<xliff:g id="NUMBER">%1$s</xliff:g> ayivumelekile."</string>
- <string name="alreadyBlocked" msgid="282340105563646876">"<xliff:g id="NUMBER">%1$s</xliff:g> isivinjiwe kakade."</string>
- <string name="call_blocking_disabled_notification_title" msgid="8185193413377920194">"Ukuvimbela ikholi kukhutshaziwe amahora angu-48"</string>
- <string name="call_blocking_disabled_notification_text" msgid="5330772013626378526">"Kukhutshaziwe ngoba ikholi ephuthumayo yenziwe."</string>
- <string name="phone_account_settings_label" msgid="5864322009841175294">"Ama-akhawunti wokushaya"</string>
- <string name="permission_single_turn_on" msgid="1543391076065465464">"Vula"</string>
- <string name="permission_multiple_turn_on" msgid="2426278457455950554">"Setha izimvume"</string>
- <string name="permission_no_speeddial" msgid="6882563445996184051">"Nika amandla ukudayela okusheshayo, vula imvume yoxhumana nabo."</string>
- <string name="permission_no_calllog" msgid="555711464287041043">"Ukuze ubone irekhodi lakho lamakholi, vuma imvume yefoni."</string>
- <string name="permission_no_contacts" msgid="6237980143178936279">"Ukuze ubone oxhumana nabo, vula imvume yoxhumana nabo."</string>
- <string name="permission_no_voicemail" msgid="8306280257799917574">"Ukuze ufinyelele ivoyisimeyili, vula imvume yefoni."</string>
- <string name="permission_no_search" msgid="84152933267902056">"Ukuze useshe oxhumana nabo, vula izimvume zoxhumana nabo."</string>
- <string name="permission_place_call" msgid="2101287782615887547">"Ukuze ubeke ikholi, vula imvume yefoni."</string>
- <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"Uhlelo lokusebenza lefoni alinayo imvume yokubhalela kuzilungiselelo zesistimu."</string>
- <string name="blocked_number_call_log_label" msgid="8912042441473014712">"Kuvinjelwe"</string>
- <string name="accessibility_call_is_active" msgid="2297282583928508760">"<xliff:g id="NAMEORNUMBER">^1</xliff:g> iyasebenza"</string>
- <string name="call_log_action_block_report_number" msgid="8288915197641750257">"Vimba/bika ugaxekile"</string>
- <string name="call_log_action_block_number" msgid="5920731882931944594">"Vimba"</string>
- <string name="call_log_action_remove_spam" msgid="5750557938714304455">"Akusiko okugaxekile"</string>
- <string name="call_log_action_unblock_number" msgid="8906582251480361069">"Vulela"</string>
- <string name="spam_number_call_log_label" msgid="5601566820155823850">"Ogaxekile"</string>
- <string name="block_report_number_alert_title" msgid="7964023263943399261">"Vimba <xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="block_report_number_alert_details" msgid="3504073367202469245">"Amakholi wangakusasa namavoyisimeyili kusuka kule nombolo azovinjelwa."</string>
- <string name="checkbox_report_as_spam_action" msgid="7546927594371150677">"Bika njengogaxekile"</string>
- <string name="block_number_alert_details" msgid="3137736492383304346">"Amakholi wangakusasa namavoyisimeyili kusuka kule nombolo azovinjelwa. Le kholi izobikwa njengogaxekile."</string>
- <string name="unblock_report_number_alert_title" msgid="972715151309357850">"Vulela i-<xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="unblock_number_alert_details" msgid="8190592456851353268">"Le nombolo izosuswa ukuvimbela futhi ibikwe njengokungayena ugaxekile. Amakholi wangakusasa namavoyisimeyili ngeke kukhonjwe njengogaxekile."</string>
- <string name="report_not_spam_alert_title" msgid="5315040210536100068">"Gunyaza i-<xliff:g id="NUMBER">%1$s</xliff:g>?"</string>
- <string name="report_not_spam_alert_button" msgid="4241295249227799609">"Gunyaza"</string>
- <string name="report_not_spam_alert_details" msgid="7655461894835163214">"Amakholi wangakusasa namavoyisimeyili kusuka kule nombolo ngeke akhonjwe njengogaxekile. Le nombolo izobikwa njengokungayena ugaxekile."</string>
-</resources>
diff --git a/res/values/animation_constants.xml b/res/values/animation_constants.xml
deleted file mode 100644
index 4e4bc36e1..000000000
--- a/res/values/animation_constants.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<resources>
- <integer name="fade_duration">300</integer>
-
- <!-- Swipe constants -->
- <integer name="swipe_escape_velocity">100</integer>
- <integer name="escape_animation_duration">200</integer>
- <integer name="max_escape_animation_duration">400</integer>
- <integer name="max_dismiss_velocity">2000</integer>
- <integer name="snap_animation_duration">350</integer>
- <integer name="swipe_scroll_slop">2</integer>
- <dimen name="min_swipe">0dip</dimen>
- <dimen name="min_vert">10dip</dimen>
- <dimen name="min_lock">20dip</dimen>
-</resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
deleted file mode 100644
index 23f639fd2..000000000
--- a/res/values/attrs.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<resources>
-
- <declare-styleable name="CallLog">
- <attr name="call_log_primary_text_color" format="color" />
- <attr name="call_log_primary_background_color" format="color" />
- <attr name="call_log_secondary_text_color" format="color" />
- <attr name="call_log_secondary_background_color" format="color" />
- <attr name="call_log_header_color" format="color" />
- </declare-styleable>
-
- <declare-styleable name="VoicemailStatus">
- <attr name="call_log_voicemail_status_height" format="dimension" />
- <attr name="call_log_voicemail_status_background_color" format="color" />
- <attr name="call_log_voicemail_status_text_color" format="color" />
- <attr name="call_log_voicemail_status_action_text_color" format="color" />
- </declare-styleable>
-
- <declare-styleable name="SearchEditTextLayout" />
-
-</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
deleted file mode 100644
index 173107c7e..000000000
--- a/res/values/colors.xml
+++ /dev/null
@@ -1,142 +0,0 @@
-<!--
- ~ 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
--->
-
-<resources>
- <!-- Primary text color in the Dialer -->
- <color name="dialtacts_primary_text_color">#333333</color>
- <!-- Secondary text color in the Dialer -->
- <color name="dialtacts_secondary_text_color">#636363</color>
- <color name="dialer_red_highlight_color">#ff1744</color>
- <color name="dialer_green_highlight_color">#00c853</color>
-
- <color name="dialer_button_text_color">#fff</color>
- <color name="dialer_flat_button_text_color">@color/dialer_theme_color</color>
- <color name="dialer_snackbar_action_text_color">@color/dialer_theme_color</color>
-
- <!-- Color for the setting text. -->
- <color name="setting_primary_color">@color/dialtacts_primary_text_color</color>
- <!-- Color for the setting description text. -->
- <color name="setting_secondary_color">@color/dialtacts_secondary_text_color</color>
- <color name="setting_disabled_color">#aaaaaa</color>
- <color name="setting_background_color">#ffffff</color>
- <color name="setting_button_color">#eee</color>
-
- <!-- 54% black -->
- <color name="call_log_icon_tint">#8a000000</color>
- <!-- 87% black -->
- <color name="call_log_primary_color">#de000000</color>
- <!-- 54% black -->
- <color name="call_log_detail_color">#8a000000</color>
- <!-- 87% black -->
- <color name="call_log_voicemail_transcript_color">#de000000</color>
- <!-- 70% black -->
- <color name="call_log_action_color">#b3000000</color>
- <!-- 54% black -->
- <color name="call_log_day_group_heading_color">#8a000000</color>
- <!-- 87% black-->
- <color name="call_log_unread_text_color">#de000000</color>
- <color name="call_log_list_item_primary_action_icon_tint">@color/call_log_icon_tint</color>
- <!-- Color of the text describing an unconsumed missed call. -->
- <color name="call_log_missed_call_highlight_color">@color/dialer_red_highlight_color</color>
- <!-- Color of the text describing an unconsumed voicemail. -->
- <color name="call_log_voicemail_highlight_color">#33b5e5</color>
-
- <!-- Background color of visual voicemail promo card. -->
- <color name="visual_voicemail_promo_card_background">#673ab7</color>
- <color name="visual_voicemail_promo_card_divider">#7d57c1</color>
- <color name="promo_card_text">#ffffff</color>
-
- <color name="voicemail_icon_tint">@color/call_log_icon_tint</color>
- <color name="voicemail_icon_disabled_tint">#80000000</color>
- <color name="voicemail_playpause_icon_tint">@color/dialer_theme_color</color>
- <!-- Colour of voicemail progress bar to the right of position indicator. -->
- <color name="voicemail_playback_seek_bar_yet_to_play">#cecece</color>
- <!-- Colour of voicemail progress bar to the left of position indicator. -->
- <color name="voicemail_playback_seek_bar_already_played">@color/dialer_theme_color</color>
-
- <!-- Standard color for selected items. -->
- <color name="item_selected">#660099cc</color>
-
- <!-- White background for dialer -->
- <color name="background_dialer_white">#ffffff</color>
- <!-- Background color of new dialer activity -->
- <color name="background_dialer_light">#fafafa</color>
- <!-- Background color for search results and call details -->
- <color name="background_dialer_results">#f9f9f9</color>
- <color name="background_dialer_call_log">@color/background_dialer_light</color>
- <color name="background_dialer_call_log_list_item">@color/background_dialer_white</color>
-
- <!-- Color of the 1dp divider that separates favorites -->
- <color name="favorite_contacts_separator_color">#d0d0d0</color>
-
- <color name="searchbox_icon_tint">#a4a4a4</color>
-
- <!-- Color of the contact name in favorite tiles -->
- <color name="contact_tile_name_color">#ffffff</color>
-
- <color name="contact_list_name_text_color">@color/dialtacts_primary_text_color</color>
-
- <!-- Undo dialogue color -->
- <color name="undo_dialogue_text_color">#4d4d4d</color>
-
- <color name="empty_list_text_color">#b2b2b2</color>
-
- <color name="remove_text_color">#ffffff</color>
-
- <!-- Text color for the "Remove" text when a contact is dragged on top of the remove view -->
- <color name="remove_highlighted_text_color">#FF3F3B</color>
-
- <!-- Color of the bottom border below the contacts grid on the main dialer screen. -->
- <color name="contacts_grid_bottom_border_color">#16000000</color>
-
- <!-- Color of actions in expanded call log entries. This text color represents actions such
- as call back, play voicemail, etc. -->
- <color name="call_log_action_text">@color/dialer_theme_color</color>
-
- <!-- Color for missed call icons. -->
- <color name="missed_call">#ff2e58</color>
- <!-- Color for answered or outgoing call icons. -->
- <color name="answered_call">@color/dialer_green_highlight_color</color>
- <!-- Color for blocked call icons. -->
- <color name="blocked_call">@color/dialtacts_secondary_text_color</color>
-
- <!-- Color for icons in the actionbar -->
- <color name="actionbar_icon_color">#ffffff</color>
-
- <color name="dialer_dialpad_touch_tint">#330288d1</color>
-
- <color name="floating_action_button_touch_tint">#80ffffff</color>
-
- <color name="call_log_action_divider">#eeeeee</color>
- <color name="divider_line_color">#D8D8D8</color>
-
- <!-- Colors for blocked numbers list -->
- <color name="blocked_contact_background">@color/incall_call_spam_background_color</color>
- <color name="blocked_number_primary_text_color">@color/dialtacts_primary_text_color</color>
- <color name="blocked_number_secondary_text_color">@color/dialtacts_secondary_text_color</color>
- <color name="blocked_number_icon_tint">#616161</color>
- <color name="blocked_number_background">#FFFFFF</color>
- <color name="blocked_number_accent_color">#42A5F5</color>
- <color name="blocked_number_block_color">#F44336</color>
- <color name="blocked_number_header_color">@color/dialer_theme_color</color>
- <color name="blocked_number_disabled_emergency_header_color">#616161</color>
- <color name="blocked_number_disabled_emergency_background_color">#E0E0E0</color>
- <color name="add_blocked_number_icon_color">#bdbdbd</color>
- <!-- Grey 700 -->
- <color name="call_detail_footer_text_color">#616161</color>
- <color name="call_detail_footer_icon_tint">@color/call_detail_footer_text_color</color>
-
-</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
deleted file mode 100644
index 371a1c6ab..000000000
--- a/res/values/dimens.xml
+++ /dev/null
@@ -1,176 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
--->
-<resources>
- <dimen name="button_horizontal_padding">16dp</dimen>
- <dimen name="divider_line_thickness">1dp</dimen>
-
- <!--
- Drag to remove view (in dp because it is used in conjunction with a statically
- sized icon
- -->
- <dimen name="remove_text_size">16dp</dimen>
-
- <!-- Call Log -->
- <dimen name="call_log_horizontal_margin">8dp</dimen>
- <dimen name="call_log_call_action_size">32dp</dimen>
- <dimen name="call_log_call_action_width">54dp</dimen>
- <dimen name="call_log_icon_margin">4dp</dimen>
- <dimen name="call_log_inner_margin">13dp</dimen>
- <dimen name="call_log_outer_margin">8dp</dimen>
- <dimen name="call_log_start_margin">8dp</dimen>
- <dimen name="call_log_indent_margin">24dp</dimen>
- <dimen name="call_log_name_margin_bottom">2dp</dimen>
- <dimen name="call_log_call_account_margin_bottom">2dp</dimen>
- <dimen name="call_log_vertical_padding">12dp</dimen>
- <dimen name="call_log_list_item_height">56dp</dimen>
- <dimen name="call_log_list_item_info_margin_start">16dp</dimen>
- <dimen name="show_call_history_list_item_height">72dp</dimen>
-
- <!-- Size of contact photos in the call log and call details. -->
- <dimen name="contact_photo_size">40dp</dimen>
- <dimen name="call_detail_button_spacing">2dip</dimen>
- <dimen name="call_detail_horizontal_margin">20dp</dimen>
- <dimen name="call_detail_top_margin">16dp</dimen>
- <dimen name="call_detail_bottom_margin">16dp</dimen>
- <dimen name="call_detail_header_top_margin">20dp</dimen>
- <dimen name="call_detail_header_bottom_margin">9dp</dimen>
- <dimen name="call_detail_elevation">0.5dp</dimen>
- <dimen name="call_detail_action_item_padding_horizontal">28dp</dimen>
- <dimen name="call_detail_action_item_padding_vertical">16dp</dimen>
- <dimen name="call_detail_action_item_drawable_padding">28dp</dimen>
- <dimen name="call_detail_action_item_text_size">16sp</dimen>
- <dimen name="transcription_top_margin">18dp</dimen>
- <dimen name="transcription_bottom_margin">18dp</dimen>
-
- <!-- Size of call provider icon width and height -->
- <dimen name="call_provider_small_icon_size">12dp</dimen>
-
- <!-- Match call_button_height to Phone's dimens/in_call_end_button_height -->
- <dimen name="call_button_height">74dp</dimen>
-
- <!-- Dimensions for speed dial tiles -->
- <dimen name="contact_tile_divider_width">1dp</dimen>
- <dimen name="contact_tile_info_button_height_and_width">36dp</dimen>
- <item name="contact_tile_height_to_width_ratio" type="dimen">76%</item>
- <dimen name="contact_tile_text_side_padding">12dp</dimen>
- <dimen name="contact_tile_text_bottom_padding">9dp</dimen>
- <dimen name="favorites_row_top_padding">2dp</dimen>
- <dimen name="favorites_row_bottom_padding">0dp</dimen>
- <dimen name="favorites_row_start_padding">1dp</dimen>
-
- <!-- Padding from the last contact tile will provide the end padding. -->
- <dimen name="favorites_row_end_padding">0dp</dimen>
- <dimen name="favorites_row_undo_text_side_padding">32dp</dimen>
-
- <!-- Size of the star icon on the favorites tile. -->
- <dimen name="favorites_star_icon_size">12dp</dimen>
-
- <!-- Padding for the tooltip -->
- <dimen name="dismiss_button_padding_start">20dip</dimen>
- <dimen name="dismiss_button_padding_end">28dip</dimen>
-
- <!-- Height of the actionBar - this is 8dps bigger than the platform standard to give more
- room to the search box-->
- <dimen name="action_bar_height">56dp</dimen>
- <dimen name="action_bar_height_large">64dp</dimen>
- <dimen name="action_bar_elevation">3dp</dimen>
- <dimen name="tab_height">43dp</dimen>
- <!-- actionbar height + tab height -->
- <dimen name="actionbar_and_tab_height">107dp</dimen>
- <dimen name="actionbar_contentInsetStart">72dp</dimen>
-
- <!-- Margin to the left and right of the search box. -->
- <dimen name="search_margin_horizontal">8dp</dimen>
- <!-- Margin above the search box. -->
- <dimen name="search_top_margin">8dp</dimen>
- <!-- Margin below the search box. -->
- <dimen name="search_bottom_margin">8dp</dimen>
- <dimen name="search_collapsed_text_size">14sp</dimen>
- <!-- Search box interior padding - left -->
- <dimen name="search_box_left_padding">8dp</dimen>
- <!-- Search box interior padding - right -->
- <dimen name="search_box_right_padding">8dp</dimen>
- <dimen name="search_box_search_icon_padding">2dp</dimen>
- <dimen name="search_box_collapsed_text_margin_left">22dp</dimen>
- <dimen name="search_list_padding_top">16dp</dimen>
- <dimen name="search_box_elevation">3dp</dimen>
-
- <dimen name="call_log_action_icon_margin_start">16dp</dimen>
- <dimen name="call_log_action_icon_dimen">24dp</dimen>
- <dimen name="call_log_action_horizontal_padding">24dp</dimen>
-
- <dimen name="call_log_actions_left_padding">64dp</dimen>
- <dimen name="call_log_actions_top_padding">8dp</dimen>
- <dimen name="call_log_actions_bottom_padding">8dp</dimen>
- <dimen name="call_log_primary_text_size">16sp</dimen>
- <dimen name="call_log_detail_text_size">12sp</dimen>
- <dimen name="call_log_day_group_heading_size">14sp</dimen>
- <dimen name="call_log_voicemail_transcription_text_size">14sp</dimen>
- <!-- Height of the call log actions section for each call log entry -->
- <dimen name="call_log_action_height">48dp</dimen>
- <dimen name="call_log_day_group_padding_top">15dp</dimen>
- <dimen name="call_log_day_group_padding_bottom">9dp</dimen>
-
- <!-- Padding for icons to increase their touch target. Icons are typically 24 dps in size
- so this extra padding makes the entire touch target 40dp -->
- <dimen name="icon_padding">8dp</dimen>
-
- <!-- Length of dialpad's shadows in dialer. -->
- <dimen name="shadow_length">10dp</dimen>
-
- <dimen name="empty_list_message_top_padding">20dp</dimen>
- <dimen name="empty_list_message_text_size">16sp</dimen>
-
- <!-- Dimensions for individual preference cards -->
- <dimen name="preference_padding_top">16dp</dimen>
- <dimen name="preference_padding_bottom">16dp</dimen>
- <dimen name="preference_side_margin">16dp</dimen>
- <dimen name="preference_summary_line_spacing_extra">4dp</dimen>
-
- <dimen name="call_log_list_item_primary_action_dimen">36dp</dimen>
-
- <!-- Dimensions for promo cards -->
- <dimen name="promo_card_icon_size">24dp</dimen>
- <dimen name="promo_card_start_padding">16dp</dimen>
- <dimen name="promo_card_top_padding">21dp</dimen>
- <dimen name="promo_card_main_padding">24dp</dimen>
- <dimen name="promo_card_title_padding">12dp</dimen>
- <dimen name="promo_card_action_vertical_padding">4dp</dimen>
- <dimen name="promo_card_action_end_padding">4dp</dimen>
- <dimen name="promo_card_action_between_padding">11dp</dimen>
- <dimen name="promo_card_line_spacing">4dp</dimen>
-
- <dimen name="voicemail_playback_top_padding">12dp</dimen>
-
- <!-- Size of entries in blocked numbers list -->
- <dimen name="blocked_number_container_padding">16dp</dimen>
- <dimen name="blocked_number_horizontal_margin">16dp</dimen>
- <dimen name="blocked_number_top_margin">16dp</dimen>
- <dimen name="blocked_number_bottom_margin">16dp</dimen>
- <dimen name="blocked_number_add_top_margin">8dp</dimen>
- <dimen name="blocked_number_add_bottom_margin">8dp</dimen>
- <dimen name="blocked_number_primary_text_size">16sp</dimen>
- <dimen name="blocked_number_secondary_text_size">12sp</dimen>
- <dimen name="blocked_number_delete_icon_size">32dp</dimen>
- <dimen name="blocked_number_search_text_size">14sp</dimen>
- <dimen name="blocked_number_settings_description_text_size">14sp</dimen>
- <dimen name="blocked_number_header_height">48dp</dimen>
-
- <dimen name="call_type_icon_size">12dp</dimen>
-
- <dimen name="tab_unread_count_margin_left">0dp</dimen>
-</resources>
diff --git a/res/values/donottranslate_config.xml b/res/values/donottranslate_config.xml
deleted file mode 100644
index 4ed41d0d5..000000000
--- a/res/values/donottranslate_config.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-<resources>
-
- <!-- If true, enable vibration (haptic feedback) for dialer key presses.
- The pattern is set on a per-platform basis using config_virtualKeyVibePattern.
- TODO: If enough users are annoyed by this, we might eventually
- need to make it a user preference rather than a per-platform
- resource. -->
- <bool name="config_enable_dialer_key_vibration">true</bool>
-
- <!-- If true, show an onscreen "Dial" button in the dialer.
- In practice this is used on all platforms even the ones with hard SEND/END
- keys, but for maximum flexibility it's controlled by a flag here
- (which can be overridden on a per-product basis.) -->
- <bool name="config_show_onscreen_dial_button">true</bool>
-
- <!-- Regular expression for prohibiting certain phone numbers in dialpad.
- Ignored if empty. -->
- <string name="config_prohibited_phone_number_regexp"></string>
-
- <!-- File Authority for AOSP Dialer files -->
- <string name="contacts_file_provider_authority">com.android.dialer.files</string>
-</resources>
diff --git a/res/values/ids.xml b/res/values/ids.xml
deleted file mode 100644
index f850327ea..000000000
--- a/res/values/ids.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
- <item type="id" name="call_detail_delete_menu_item" />
- <item type="id" name="context_menu_copy_to_clipboard" />
- <item type="id" name="context_menu_copy_transcript_to_clipboard" />
- <item type="id" name="context_menu_edit_before_call" />
- <item type="id" name="context_menu_block_number" />
- <item type="id" name="settings_header_sounds_and_vibration" />
- <item type="id" name="block_id" />
-</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
deleted file mode 100644
index 940705989..000000000
--- a/res/values/strings.xml
+++ /dev/null
@@ -1,1116 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Application name used in Settings/Apps. Default label for activities
- that don't specify a label. -->
- <string name="applicationLabel">Phone</string>
-
- <!-- Title for the activity that dials the phone. This is the name
- used in the Launcher icon. -->
- <string name="launcherActivityLabel">Phone</string>
-
-
- <!-- Title for the activity that dials the phone, when launched directly into the dialpad -->
- <string name="launcherDialpadActivityLabel">Phone Dialpad</string>
- <!-- The description text for the dialer tab.
-
- Note: AccessibilityServices use this attribute to announce what the view represents.
- This is especially valuable for views without textual representation like ImageView.
-
- [CHAR LIMIT=NONE] -->
- <string name="dialerIconLabel">Phone</string>
-
- <!-- The description text for the call log tab.
-
- Note: AccessibilityServices use this attribute to announce what the view represents.
- This is especially valuable for views without textual representation like ImageView.
-
- [CHAR LIMIT=NONE] -->
- <string name="callHistoryIconLabel">Call history</string>
-
- <!-- Text for a menu item to report a call as having been incorrectly identified. [CHAR LIMIT=48] -->
- <string name="action_report_number">Report inaccurate number</string>
-
- <!-- Option displayed in context menu to copy long pressed phone number. [CHAR LIMIT=48] -->
- <string name="action_copy_number_text">Copy number</string>
-
- <!-- Option displayed in context menu to copy long pressed voicemail transcription. [CHAR LIMIT=48] -->
- <string name="copy_transcript_text">Copy transcription</string>
-
- <!-- Label for action to block a number. [CHAR LIMIT=48] -->
- <string name="action_block_number">Block number</string>
-
- <!-- Text for snackbar to undo blocking a number. [CHAR LIMIT=64] -->
- <string name="snackbar_number_blocked">
- <xliff:g id="number" example="(555) 555-5555">%1$s</xliff:g> blocked</string>
-
- <!-- Label for action to unblock a number [CHAR LIMIT=48]-->
- <string name="action_unblock_number">Unblock number</string>
-
- <!-- Text for snackbar to undo unblocking a number. [CHAR LIMIT=64] -->
- <string name="snackbar_number_unblocked">
- <xliff:g id="number" example="(555) 555-5555">%1$s</xliff:g>
- unblocked</string>
-
- <!-- Text for undo button in snackbar for blocking/unblocking number. [CHAR LIMIT=10] -->
- <string name="block_number_undo">UNDO</string>
-
- <!-- Menu item in call details used to remove a call or voicemail from the call log. -->
- <string name="call_details_delete">Delete</string>
-
- <!-- Label for action to edit a number before calling it. [CHAR LIMIT=48] -->
- <string name="action_edit_number_before_call">Edit number before call</string>
-
- <!-- Menu item used to remove all calls from the call log -->
- <string name="call_log_delete_all">Clear call history</string>
-
- <!-- Menu item used to delete a voicemail. [CHAR LIMIT=30] -->
- <string name="call_log_trash_voicemail">Delete voicemail</string>
-
- <!-- Menu item used to archive a voicemail. [CHAR LIMIT=30] -->
- <string name="call_log_archive_voicemail">Archive voicemail</string>
-
- <!-- Menu item used to send a voicemail through other applications [CHAR LIMIT=30] -->
- <string name="call_log_share_voicemail">Share voicemail</string>
-
- <!-- Text for snackbar to undo a voicemail delete. [CHAR LIMIT=30] -->
- <string name="snackbar_voicemail_deleted">Voicemail deleted</string>
-
- <!-- Text for snackbar to undo a voicemail archive. [CHAR LIMIT=30] -->
- <string name="snackbar_voicemail_archived">Voicemail archived</string>
-
- <!-- Text for undo button in snackbar for voicemail deletion. [CHAR LIMIT=10] -->
- <string name="snackbar_voicemail_deleted_undo">UNDO</string>
-
- <!-- Text for going to archive button in snackbar for voicemail archive. [CHAR LIMIT=10] -->
- <string name="snackbar_voicemail_archived_goto">GOTO ARCHIVE</string>
-
- <!-- Title of the confirmation dialog for clearing the call log. [CHAR LIMIT=37] -->
- <string name="clearCallLogConfirmation_title">Clear call history?</string>
-
- <!-- Confirmation dialog for clearing the call log. [CHAR LIMIT=NONE] -->
- <string name="clearCallLogConfirmation">This will delete all calls from your history</string>
-
- <!-- Title of the "Clearing call log" progress-dialog [CHAR LIMIT=35] -->
- <string name="clearCallLogProgress_title">Clearing call history\u2026</string>
-
- <!-- Title used for the activity for placing a call. This name appears
- in activity disambig dialogs -->
- <string name="userCallActivityLabel" product="default">Phone</string>
-
- <!-- Notification strings -->
- <!-- Missed call notification label, used when there's exactly one missed call -->
- <string name="notification_missedCallTitle">Missed call</string>
- <!-- Missed call notification label, used when there's exactly one missed call from work contact -->
- <string name="notification_missedWorkCallTitle">Missed work call</string>
- <!-- Missed call notification label, used when there are two or more missed calls -->
- <string name="notification_missedCallsTitle">Missed calls</string>
- <!-- Missed call notification message used when there are multiple missed calls -->
- <string name="notification_missedCallsMsg"><xliff:g id="num_missed_calls">%s</xliff:g> missed calls</string>
- <!-- Message for "call back" Action, which is displayed in the missed call notificaiton.
- The user will be able to call back to the person or the phone number.
- [CHAR LIMIT=18] -->
- <string name="notification_missedCall_call_back">Call back</string>
- <!-- Message for "reply via sms" action, which is displayed in the missed call notification.
- The user will be able to send text messages using the phone number.
- [CHAR LIMIT=18] -->
- <string name="notification_missedCall_message">Message</string>
- <!-- DO NOT TRANSLATE. Hardcoded number used for restricted incoming phone numbers. -->
- <string name="handle_restricted">RESTRICTED</string>
-
- <!-- Title of the notification of new voicemails. [CHAR LIMIT=30] -->
- <plurals name="notification_voicemail_title">
- <item quantity="one">Voicemail</item>
- <item quantity="other">
- <xliff:g id="count">%1$d</xliff:g>
- Voicemails
- </item>
- </plurals>
-
- <!-- Used in the notification of a new voicemail for the action to play the voicemail. -->
- <string name="notification_action_voicemail_play">Play</string>
-
- <!-- Used to build a list of names or phone numbers, to indicate the callers who left
- voicemails.
- The first argument may be one or more callers, the most recent ones.
- The second argument is an additional callers.
- This string is used to build a list of callers.
-
- [CHAR LIMIT=10]
- -->
- <string name="notification_voicemail_callers_list"><xliff:g id="newer_callers">%1$s</xliff:g>,
- <xliff:g id="older_caller">%2$s</xliff:g>
- </string>
-
- <!-- Text used in the ticker to notify the user of the latest voicemail. [CHAR LIMIT=30] -->
- <string name="notification_new_voicemail_ticker">New voicemail from
- <xliff:g id="caller">%1$s</xliff:g>
- </string>
-
- <!-- Message to show when there is an error playing back the voicemail. [CHAR LIMIT=40] -->
- <string name="voicemail_playback_error">Couldn\'t play voicemail</string>
-
- <!-- Message to display whilst we are waiting for the content to be fetched. [CHAR LIMIT=40] -->
- <string name="voicemail_fetching_content">Loading voicemail\u2026</string>
-
- <!-- Message to display whilst we are waiting for the content to be archived. [CHAR LIMIT=40] -->
- <string name="voicemail_archiving_content">Archiving voicemail\u2026</string>
-
- <!-- Message to display if we fail to get content within a suitable time period. [CHAR LIMIT=40] -->
- <string name="voicemail_fetching_timout">Couldn\'t load voicemail</string>
-
- <!-- The header to show that call log is only showing voicemail calls. [CHAR LIMIT=40] -->
- <string name="call_log_voicemail_header">Calls with voicemail only</string>
-
- <!-- The header to show that call log is only showing incoming calls. [CHAR LIMIT=40] -->
- <string name="call_log_incoming_header">Incoming calls only</string>
-
- <!-- The header to show that call log is only showing outgoing calls. [CHAR LIMIT=40] -->
- <string name="call_log_outgoing_header">Outgoing calls only</string>
-
- <!-- The header to show that call log is only showing missed calls. [CHAR LIMIT=40] -->
- <string name="call_log_missed_header">Missed calls only</string>
-
- <!-- Title for promo card for visual voicemail. [CHAR LIMIT=40] -->
- <string name="visual_voicemail_title">Visual voicemail</string>
-
- <!-- Promo card text for visual voicemail. -->
- <string name="visual_voicemail_text">
- See and listen to your voicemail, without having to call a number. Data charges may apply.
- </string>
-
- <!-- Text for "Settings" link for visual voicemail promo card. [CHAR LIMIT=30] -->
- <string name="visual_voicemail_settings">Settings</string>
-
- <!-- Voicemail status message shown at the top of call log to notify the user that no new
- voicemails are currently available. This can happen when both notification as well as data
- connection to the voicemail server is lost. [CHAR LIMIT=64] -->
- <string name="voicemail_status_voicemail_not_available">Voicemail updates not available</string>
- <!-- Voicemail status message shown at the top of call log to notify the user that there is no
- data connection to the voicemail server, but there are new voicemails waiting on the server.
- [CHAR LIMIT=64] -->
- <string name="voicemail_status_messages_waiting">New voicemail waiting. Can\'t load right now.</string>
- <!-- Voicemail status message shown at the top of call log to invite the user to configure
- visual voicemail. [CHAR LIMIT=64] -->
- <string name="voicemail_status_configure_voicemail">Set up your voicemail</string>
- <!-- Voicemail status message shown at the top of call details screen to notify the user that
- the audio of this voicemail is not available. [CHAR LIMIT=64] -->
- <string name="voicemail_status_audio_not_available">Audio not available</string>
-
- <!-- User action prompt shown next to a voicemail status message to let the user configure
- visual voicemail. [CHAR LIMIT=20] -->
- <string name="voicemail_status_action_configure">Set up</string>
- <!-- User action prompt shown next to a voicemail status message to let the user call voicemail
- server directly to listen to the voicemails. [CHAR LIMIT=20] -->
- <string name="voicemail_status_action_call_server">Call voicemail</string>
-
- <!-- The counter for calls in a group and the date of the latest call as shown in the call log [CHAR LIMIT=15] -->
- <string name="call_log_item_count_and_date">(<xliff:g id="count">%1$d</xliff:g>)
- <xliff:g id="date">%2$s</xliff:g>
- </string>
-
- <!-- Title for the sms disambiguation dialog -->
- <string name="sms_disambig_title">Choose number</string>
-
- <!-- Title for the call disambiguation dialog -->
- <string name="call_disambig_title">Choose number</string>
-
- <!-- Message next to disamgiguation dialog check box -->
- <string name="make_primary">Remember this choice</string>
-
- <!-- String describing the Search ImageButton
-
- Used by AccessibilityService to announce the purpose of the button.
- [CHAR LIMIT=NONE]
- -->
- <string name="description_search_button">search</string>
-
- <!-- String describing the Dial ImageButton
-
- Used by AccessibilityService to announce the purpose of the button.
- -->
- <string name="description_dial_button">dial</string>
-
- <!-- String describing the digits text box containing the number to dial.
-
- Used by AccessibilityService to announce the purpose of the view.
- -->
- <string name="description_digits_edittext">number to dial</string>
-
- <!-- String describing the button in the voicemail playback to start/stop playback.
-
- Used by AccessibilityService to announce the purpose of the view.
- -->
- <string name="description_playback_start_stop">Play or stop playback</string>
-
- <!-- String describing the button in the voicemail playback to switch on/off speakerphone.
-
- Used by AccessibilityService to announce the purpose of the view.
- -->
- <string name="description_playback_speakerphone">Switch on or off speakerphone</string>
-
- <!-- String describing the seekbar in the voicemail playback to seek playback position.
-
- Used by AccessibilityService to announce the purpose of the view.
- -->
- <string name="description_playback_seek">Seek playback position</string>
-
- <!-- String describing the button in the voicemail playback to decrease playback rate.
-
- Used by AccessibilityService to announce the purpose of the view.
- -->
- <string name="description_rate_decrease">Decrease playback rate</string>
-
- <!-- String describing the button in the voicemail playback to increase playback rate.
-
- Used by AccessibilityService to announce the purpose of the view.
- -->
- <string name="description_rate_increase">Increase playback rate</string>
-
- <!-- Content description for the fake action menu button that brings up the call history
- activity -->
- <string name="action_menu_call_history_description">Call History</string>
-
- <!-- Content description for the fake action menu overflow button.
- This should be same as the description for the real action menu
- overflow button available in ActionBar.
- [CHAR LIMIT=NONE] -->
- <string name="action_menu_overflow_description" msgid="2295659037509008453">More options</string>
-
- <!-- Content description for the button that displays the dialpad
- [CHAR LIMIT=NONE] -->
- <string name="action_menu_dialpad_button">dial pad</string>
-
- <!-- Menu item used to show only outgoing in the call log. [CHAR LIMIT=30] -->
- <string name="menu_show_outgoing_only">Show outgoing only</string>
-
- <!-- Menu item used to show only incoming in the call log. [CHAR LIMIT=30] -->
- <string name="menu_show_incoming_only">Show incoming only</string>
-
- <!-- Menu item used to show only missed in the call log. [CHAR LIMIT=30] -->
- <string name="menu_show_missed_only">Show missed only</string>
-
- <!-- Menu item used to show only voicemails in the call log. [CHAR LIMIT=30] -->
- <string name="menu_show_voicemails_only">Show voicemails only</string>
-
- <!-- Menu item used to show all calls in the call log. [CHAR LIMIT=30] -->
- <string name="menu_show_all_calls">Show all calls</string>
-
- <!-- Menu items for dialpad options as part of Pause and Wait ftr [CHAR LIMIT=30] -->
- <string name="add_2sec_pause">Add 2-sec pause</string>
- <string name="add_wait">Add wait</string>
-
- <!-- Label for the dialer app setting page [CHAR LIMIT=30]-->
- <string name="dialer_settings_label">Settings</string>
-
- <!-- Menu item to create a new contact [CHAR LIMIT=30] -->
- <string name="menu_newContact">New contact</string>
-
- <!-- Menu item to display all contacts [CHAR LIMIT=30] -->
- <string name="menu_allContacts">All contacts</string>
-
- <!-- Title bar for call detail screen -->
- <string name="callDetailTitle">Call details</string>
-
- <!-- Toast for call detail screen when couldn't read the requested details -->
- <string name="toast_call_detail_error">Details not available</string>
-
- <!-- Item label: jump to the in-call DTMF dialpad.
- (Part of a list of options shown in the dialer when another call
- is already in progress.) -->
- <string name="dialer_useDtmfDialpad">Use touch tone keypad</string>
-
- <!-- Item label: jump to the in-call UI.
- (Part of a list of options shown in the dialer when another call
- is already in progress.) -->
- <string name="dialer_returnToInCallScreen">Return to call in progress</string>
-
- <!-- Item label: use the Dialer's dialpad to add another call.
- (Part of a list of options shown in the dialer when another call
- is already in progress.) -->
- <string name="dialer_addAnotherCall">Add call</string>
-
- <!-- Title for incoming call type. [CHAR LIMIT=40] -->
- <string name="type_incoming">Incoming call</string>
-
- <!-- Title for outgoing call type. [CHAR LIMIT=40] -->
- <string name="type_outgoing">Outgoing call</string>
-
- <!-- Title for missed call type. [CHAR LIMIT=40] -->
- <string name="type_missed">Missed call</string>
-
- <!-- Title for incoming video call in call details screen [CHAR LIMIT=60] -->
- <string name="type_incoming_video">Incoming video call</string>
-
- <!-- Title for outgoing video call in call details screen [CHAR LIMIT=60] -->
- <string name="type_outgoing_video">Outgoing video call</string>
-
- <!-- Title for missed video call in call details screen [CHAR LIMIT=60] -->
- <string name="type_missed_video">Missed video call</string>
-
- <!-- Title for voicemail details screen -->
- <string name="type_voicemail">Voicemail</string>
-
- <!-- Title for rejected call type. [CHAR LIMIT=40] -->
- <string name="type_rejected">Declined call</string>
-
- <!-- Title for blocked call type. [CHAR LIMIT=40] -->
- <string name="type_blocked">Blocked call</string>
-
- <!-- Description for incoming calls going to voice mail vs. not -->
- <string name="actionIncomingCall">Incoming calls</string>
-
- <!-- String describing the icon in the call log used to play a voicemail.
-
- Note: AccessibilityServices use this attribute to announce what the view represents.
- This is especially valuable for views without textual representation like ImageView.
- -->
- <string name="description_call_log_play_button">Play voicemail</string>
-
- <!-- String describing the button to view the contact for the current number.
-
- Note: AccessibilityServices use this attribute to announce what the view represents.
- This is especially valuable for views without textual representation like ImageView.
- -->
- <string name="description_view_contact">View contact <xliff:g id="name">%1$s</xliff:g></string>
-
- <!-- String describing the button to call a number or contact.
-
- Note: AccessibilityServices use this attribute to announce what the view represents.
- This is especially valuable for views without textual representation like ImageView.
- -->
- <string name="description_call">Call <xliff:g id="name">%1$s</xliff:g></string>
-
- <!-- String describing the button to access the contact details for a name or number.
-
- Note: AccessibilityServices use this attribute to announce what the view represents.
- This is especially valuable for views without textual representation like ImageView.
- -->
- <string name="description_contact_details">Contact details for <xliff:g id="nameOrNumber">%1$s</xliff:g></string>
-
- <!-- String indicating the number of calls to/from a caller in the call log.
-
- Note: AccessibilityServices use this attribute to announce what the view represents.
- This is especially valuable for views without textual representation like ImageView.
- -->
- <string name="description_num_calls"><xliff:g id="numberOfCalls">%1$s</xliff:g> calls.</string>
-
- <!-- String indicating a call log entry had video capabilities.
-
- Note: AccessibilityServices use this attribute to announce what the view represents.
- This is especially valuable for views without textual representation like ImageView.
- [CHAR LIMIT=NONE]
- -->
- <string name="description_video_call">Video call.</string>
-
- <!-- String describing the button to SMS a number or contact.
-
- Note: AccessibilityServices use this attribute to announce what the view represents.
- This is especially valuable for views without textual representation like ImageView.
- [CHAR LIMIT=NONE]
- -->
- <string name="description_send_text_message">Send SMS to <xliff:g id="name">%1$s</xliff:g></string>
-
- <!-- String describing the icon in the call log used to represent an unheard voicemail left to
- the user.
-
- Note: AccessibilityServices use this attribute to announce what the view represents.
- This is especially valuable for views without textual representation like ImageView.
- [CHAR LIMIT=NONE]
- -->
- <string name="description_call_log_unheard_voicemail">Unheard voicemail</string>
-
- <!-- String describing the icon used to start a voice search -->
- <string name="description_start_voice_search">Start voice search</string>
-
- <!-- Menu item used to call a contact, containing the number of the contact to call -->
- <string name="menu_callNumber">Call <xliff:g id="number">%s</xliff:g></string>
-
- <!-- String used to display calls from unknown numbers in the call log -->
- <string name="unknown">Unknown</string>
-
- <!-- String used for displaying calls to the voicemail number in the call log -->
- <string name="voicemail">Voicemail</string>
-
- <!-- String used to display calls from private numbers in the call log -->
- <string name="private_num">Private number</string>
-
- <!-- String used to display calls from pay phone in the call log -->
- <string name="payphone">Payphone</string>
-
- <!-- A nicely formatted call duration displayed when viewing call details for duration less than 1 minute. For example "28 sec" -->
- <string name="callDetailsShortDurationFormat"><xliff:g id="seconds" example="28">%s</xliff:g> sec</string>
-
- <!-- A nicely formatted call duration displayed when viewing call details. For example "42 min 28 sec" -->
- <string name="callDetailsDurationFormat"><xliff:g id="minutes" example="42">%s</xliff:g> min <xliff:g id="seconds" example="28">%s</xliff:g> sec</string>
-
- <!-- The string 'Today'. This value is used in the voicemailCallLogDateTimeFormat rather than an
- explicit date string, e.g. Jul 25, 2014, in the event that a voicemail was created on the
- current day -->
- <string name="voicemailCallLogToday">@string/call_log_header_today</string>
-
- <!-- A format string used for displaying the date and time for a voicemail call log. For example: Jul 25, 2014 at 2:49 PM
- The date will be replaced by 'Today' for voicemails created on the current day. For example: Today at 2:49 PM -->
- <string name="voicemailCallLogDateTimeFormat"><xliff:g id="date" example="Jul 25, 2014">%1$s</xliff:g> at <xliff:g id="time" example="2:49 PM">%2$s</xliff:g></string>
-
- <!-- Format for duration of voicemails which are displayed when viewing voicemail logs. For example "01:22" -->
- <string name="voicemailDurationFormat"><xliff:g id="minutes" example="10">%1$02d</xliff:g>:<xliff:g id="seconds" example="20">%2$02d</xliff:g></string>
-
- <!-- A format string used for displaying the date, time and duration for a voicemail call log. For example: Jul 25, 2014 at 2:49 PM • 00:34 -->
- <string name="voicemailCallLogDateTimeFormatWithDuration"><xliff:g id="dateAndTime" example="Jul 25, 2014 at 2:49PM">%1$s</xliff:g> \u2022 <xliff:g id="duration" example="01:22">%2$s</xliff:g></string>
-
- <!-- Dialog message which is shown when the user tries to make a phone call
- to prohibited phone numbers [CHAR LIMIT=NONE] -->
- <string name="dialog_phone_call_prohibited_message" msgid="4313552620858880999">Can\'t call this number</string>
-
- <!-- Dialog message which is shown when the user tries to check voicemail
- while the system isn't ready for the access. [CHAR LIMIT=NONE] -->
- <string name="dialog_voicemail_not_ready_message">To set up voicemail, go to Menu &gt; Settings.</string>
-
- <!-- Dialog message which is shown when the user tries to check voicemail
- while the system is in airplane mode. The user cannot access to
- voicemail service in Airplane mode. [CHAR LIMI=NONE] -->
- <string name="dialog_voicemail_airplane_mode_message">To call voicemail, first turn off Airplane mode.</string>
-
- <!-- Message that appears in the favorites tab of the Phone app when the contact list has not fully loaded yet (below the favorite and frequent contacts) [CHAR LIMIT=20] -->
- <string name="contact_list_loading">Loading\u2026</string>
-
- <!-- The title of a dialog that displays the IMEI of the phone -->
- <string name="imei">IMEI</string>
-
- <!-- The title of a dialog that displays the MEID of the CDMA phone -->
- <string name="meid">MEID</string>
-
- <!-- Dialog text displayed when loading a phone number from the SIM card for speed dial -->
- <string name="simContacts_emptyLoading">Loading from SIM card\u2026</string>
-
- <!-- Dialog title displayed when loading a phone number from the SIM card for speed dial -->
- <string name="simContacts_title">SIM card contacts</string>
-
- <!-- Message displayed when there is no application available to handle the add contact menu option. [CHAR LIMIT=NONE] -->
- <string name="add_contact_not_available">No contacts app available</string>
-
- <!-- Message displayed when there is no application available to handle voice search. [CHAR LIMIT=NONE] -->
- <string name="voice_search_not_available">Voice search not available</string>
-
- <!-- Message displayed when the Phone application has been disabled and a phone call cannot
- be made. [CHAR LIMIT=NONE] -->
- <string name="call_not_available">Cannot make a phone call because the Phone application has been disabled.</string>
-
- <!-- Message displayed when there is no application available to handle a particular action.
- [CHAR LIMIT=NONE] -->
- <string name="activity_not_available">No app for that on this device</string>
-
- <!-- Hint displayed in dialer search box when there is no query that is currently typed.
- [CHAR LIMIT=30] -->
- <string name="dialer_hint_find_contact">Search contacts</string>
-
- <!-- Hint displayed in add blocked number search box when there is no query typed.
- [CHAR LIMIT=45] -->
- <string name="block_number_search_hint">Add number or search contacts</string>
-
- <!-- String resource for the font-family to use for the call log activity's title
- Do not translate. -->
- <string name="call_log_activity_title_font_family">sans-serif-light</string>
-
- <!-- String resource for the font-family to use for the full call history footer
- Do not translate. -->
- <string name="view_full_call_history_font_family">sans-serif</string>
-
- <!-- Text displayed when the call log is empty. -->
- <string name="call_log_all_empty">Your call history is empty</string>
-
- <!-- Label of the button displayed when the call history is empty. Allows the user to make a call. -->
- <string name="call_log_all_empty_action">Make a call</string>
-
- <!-- Text displayed when the list of missed calls is empty -->
- <string name="call_log_missed_empty">You have no missed calls.</string>
-
- <!-- Text displayed when the list of voicemails is empty -->
- <string name="call_log_voicemail_empty">Your voicemail inbox is empty.</string>
-
- <!-- Text displayed when the list of voicemail archives is empty -->
- <string name="voicemail_archive_empty">Your voicemail archive is empty.</string>
-
- <!-- Menu option to show favorite contacts only -->
- <string name="show_favorites_only">Show favorites only</string>
-
- <!-- Title of activity that displays a list of all calls -->
- <string name="call_log_activity_title">Call History</string>
-
- <!-- Title of activity that displays a list of all archived voicemails -->
- <string name="voicemail_archive_activity_title">Voicemail Archive</string>
-
- <!-- Title for the call log tab containing the list of all voicemails and calls
- [CHAR LIMIT=30] -->
- <string name="call_log_all_title">All</string>
-
- <!-- Title for the call log tab containing the list of all missed calls only
- [CHAR LIMIT=30] -->
- <string name="call_log_missed_title">Missed</string>
-
- <!-- Title for the call log tab containing the list of all voicemail calls only
- [CHAR LIMIT=30] -->
- <string name="call_log_voicemail_title">Voicemail</string>
-
- <!-- Title for dialog which opens when the user needs to migrate to the framework blocking implementation [CHAR LIMIT=30]-->
- <string name="migrate_blocked_numbers_dialog_title">New, simplified blocking</string>
-
- <!-- Body text for dialog which opens when the user needs to migrate to the framework blocking implementation [CHAR LIMIT=NONE]-->
- <string name="migrate_blocked_numbers_dialog_message">To better protect you, Phone needs to change how blocking works. Your blocked numbers will now stop both calls and texts and may be shared with other apps.</string>
-
- <!-- Positive confirmation button for the dialog which opens when the user needs to migrate to the framework blocking implementation [CHAR LIMIT=NONE]-->
- <string name="migrate_blocked_numbers_dialog_allow_button">Allow</string>
-
- <!-- Do not translate -->
- <string name="migrate_blocked_numbers_dialog_cancel_button">@android:string/cancel</string>
-
- <!-- Confirmation dialog title for blocking a number. [CHAR LIMIT=NONE] -->
- <string name="block_number_confirmation_title">Block
- <xliff:g id="number" example="(555) 555-5555">%1$s</xliff:g>?</string>
-
- <!-- Confirmation dialog message for blocking a number with visual voicemail active.
- [CHAR LIMIT=NONE] -->
- <string name="block_number_confirmation_message_vvm">
- Calls from this number will be blocked and voicemails will be automatically deleted.
- </string>
-
- <!-- Confirmation dialog message for blocking a number with no visual voicemail.
- [CHAR LIMIT=NONE] -->
- <string name="block_number_confirmation_message_no_vvm">
- Calls from this number will be blocked, but the caller may still be able to leave you voicemails.
- </string>
-
- <!-- Confirmation dialog message for blocking a number with new filtering enabled.
- [CHAR LIMIT=NONE] -->
- <string name="block_number_confirmation_message_new_filtering">
- You will no longer receive calls or texts from this number.
- </string>
-
- <!-- Block number alert dialog button [CHAR LIMIT=32] -->
- <string name="block_number_ok">BLOCK</string>
-
- <!-- Confirmation dialog for unblocking a number. [CHAR LIMIT=NONE] -->
- <string name="unblock_number_confirmation_title">Unblock
- <xliff:g id="number" example="(555) 555-5555">%1$s</xliff:g>?</string>
-
- <!-- Unblock number alert dialog button [CHAR LIMIT=32] -->
- <string name="unblock_number_ok">UNBLOCK</string>
-
- <!-- Accessibility text for the tab showing recent and favorite contacts who can be called.
- [CHAR LIMIT=40] -->
- <string name="tab_speed_dial">Speed dial</string>
-
- <!-- Accessibility text for the tab showing the call history. [CHAR LIMIT=40] -->
- <string name="tab_history">Call History</string>
-
- <!-- Accessibility text for the tab showing the user's contacts. [CHAR LIMIT=40] -->
- <string name="tab_all_contacts">Contacts</string>
-
- <!-- Accessibility text for the tab showing the user's voicemails. [CHAR LIMIT=40] -->
- <string name="tab_voicemail">Voicemail</string>
-
- <!-- Text displayed when user swipes out a favorite contact -->
- <string name="favorite_hidden">Removed from favorites</string>
- <!-- Text displayed for the undo button to undo removing a favorite contact -->
- <string name="favorite_hidden_undo">Undo</string>
-
- <!-- Shortcut item used to call a number directly from search -->
- <string name="search_shortcut_call_number">Call
- <xliff:g id="number">%s</xliff:g>
- </string>
-
- <!-- Shortcut item used to add a number directly to a new contact from search.
- [CHAR LIMIT=25] -->
- <string name="search_shortcut_create_new_contact">Create new contact</string>
-
- <!-- Shortcut item used to add a number to an existing contact directly from search.
- [CHAR LIMIT=25] -->
- <string name="search_shortcut_add_to_contact">Add to a contact</string>
-
- <!-- Shortcut item used to send a text message directly from search. [CHAR LIMIT=25] -->
- <string name="search_shortcut_send_sms_message">Send SMS</string>
-
- <!-- Shortcut item used to make a video call directly from search. [CHAR LIMIT=25] -->
- <string name="search_shortcut_make_video_call">Make video call</string>
-
- <!-- Shortcut item used to block a number directly from search. [CHAR LIMIT=25] -->
- <string name="search_shortcut_block_number">Block number</string>
-
- <!-- Number of missed calls shown on call card [CHAR LIMIT=40] -->
- <string name="num_missed_calls"><xliff:g id="number">%s</xliff:g> new missed calls</string>
-
- <!-- Shown when there are no speed dial favorites. -->
- <string name="speed_dial_empty">No one is on your speed dial yet</string>
-
- <!-- Shown as an action when there are no speed dial favorites -->
- <string name="speed_dial_empty_add_favorite_action">Add a favorite</string>
-
- <!-- Shown when there are no contacts in the all contacts list. -->
- <string name="all_contacts_empty">You don\'t have any contacts yet</string>
-
- <!-- Shown as an action when the all contacts list is empty -->
- <string name="all_contacts_empty_add_contact_action">Add a contact</string>
-
- <!-- Shows up as a tooltip to provide a hint to the user that the profile pic in a contact
- card can be tapped to bring up a list of all numbers, or long pressed to start reordering
- [CHAR LIMIT=NONE]
- -->
- <string name="contact_tooltip">Touch image to see all numbers or touch &amp; hold to reorder</string>
-
- <!-- Remove button that shows up when contact is long-pressed. [CHAR LIMIT=NONE] -->
- <string name="remove_contact">Remove</string>
-
- <!-- Button text for the "video call" displayed underneath an entry in the call log.
- Tapping causes a video call to be placed to the caller represented by the call log entry.
- [CHAR LIMIT=30] -->
- <string name="call_log_action_video_call">Video call</string>
-
- <!-- Button text for a button displayed underneath an entry in the call log, which opens up a
- messaging app to send a SMS to the number represented by the call log entry.
- [CHAR LIMIT=30] -->
- <string name="call_log_action_send_message">Send a message</string>
-
- <!-- Button text for the button displayed underneath an entry in the call log.
- Tapping navigates the user to the call details screen where the user can view details for
- the call log entry. [CHAR LIMIT=30] -->
- <string name="call_log_action_details">Call details</string>
-
- <!-- Button text for the button displayed underneath an entry in the call log, which when
- tapped triggers a return call to the named user. [CHAR LIMIT=30] -->
- <string name="call_log_action_call">
- Call <xliff:g id="nameOrNumber" example="John Smith">^1</xliff:g>
- </string>
-
- <!-- String describing an incoming missed call entry in the call log.
- Note: AccessibilityServices uses this attribute to announce what the view represents.
- [CHAR LIMIT=NONE] -->
- <string name="description_incoming_missed_call">Missed call from <xliff:g id="nameOrNumber" example="John Smith">^1</xliff:g>, <xliff:g id="typeOrLocation" example="Mobile">^2</xliff:g>, <xliff:g id="timeOfCall" example="2 min ago">^3</xliff:g>, <xliff:g id="phoneAccount" example="on SIM 1">^4</xliff:g>.</string>
-
- <!-- String describing an incoming answered call entry in the call log.
- Note: AccessibilityServices uses this attribute to announce what the view represents.
- [CHAR LIMIT=NONE] -->
- <string name="description_incoming_answered_call">Answered call from <xliff:g id="nameOrNumber" example="John Smith">^1</xliff:g>, <xliff:g id="typeOrLocation" example="Mobile">^2</xliff:g>, <xliff:g id="timeOfCall" example="2 min ago">^3</xliff:g>, <xliff:g id="phoneAccount" example="on SIM 1">^4</xliff:g>.</string>
-
- <!-- String describing an "unread" voicemail entry in the voicemails tab.
- Note: AccessibilityServices use this attribute to announce what the view represents.
- [CHAR LIMIT=NONE] -->
- <string name="description_unread_voicemail">Unread voicemail from <xliff:g id="nameOrNumber" example="John Smith">^1</xliff:g>, <xliff:g id="typeOrLocation" example="Mobile">^2</xliff:g>, <xliff:g id="timeOfCall" example="2 min ago">^3</xliff:g>, <xliff:g id="phoneAccount" example="on SIM 1">^4</xliff:g>.</string>
-
- <!-- String describing a "read" voicemail entry in the voicemails tab.
- Note: AccessibilityServices use this attribute to announce what the view represents.
- [CHAR LIMIT=NONE] -->
- <string name="description_read_voicemail">Voicemail from <xliff:g id="nameOrNumber" example="John Smith">^1</xliff:g>, <xliff:g id="typeOrLocation" example="Mobile">^2</xliff:g>, <xliff:g id="timeOfCall" example="2 min ago">^3</xliff:g>, <xliff:g id="phoneAccount" example="on SIM 1">^4</xliff:g>.</string>
-
- <!-- String describing an outgoing call entry in the call log.
- Note: AccessibilityServices uses this attribute to announce what the view represents.
- [CHAR LIMIT=NONE] -->
- <string name="description_outgoing_call">Call to <xliff:g id="nameOrNumber" example="John Smith">^1</xliff:g>, <xliff:g id="typeOrLocation" example="Mobile">^2</xliff:g>, <xliff:g id="timeOfCall" example="2 min ago">^3</xliff:g>, <xliff:g id="phoneAccount" example="on SIM 1">^4</xliff:g>.</string>
-
- <!-- String describing the phone account the call was made on or to. This string will be used
- in description_incoming_missed_call, description_incoming_answered_call, and
- description_outgoing_call.
- Note: AccessibilityServices uses this attribute to announce what the view represents.
- [CHAR LIMIT=NONE] -->
- <string name="description_phone_account">on <xliff:g id="phoneAccount" example="SIM 1">^1</xliff:g></string>
-
- <!-- String describing the secondary line number the call was received via.
- Note: AccessibilityServices use this attribute to announce what the view represents.
- [CHAR LIMIT=NONE]-->
- <string name="description_via_number">via <xliff:g id="number" example="(555) 555-5555">%1$s</xliff:g></string>
-
- <!-- TextView text item showing the secondary line number the call was received via.
- [CHAR LIMIT=NONE]-->
- <string name="call_log_via_number">via <xliff:g id="number" example="(555) 555-5555">%1$s</xliff:g></string>
-
- <!-- String describing the PhoneAccount and via number that a call was received on, if both are
- visible.
- Note: AccessibilityServices use this attribute to announce what the view represents.
- [CHAR LIMIT=NONE]-->
- <string name="description_via_number_phone_account">on <xliff:g id="phoneAccount" example="SIM 1">%1$s</xliff:g>, via <xliff:g id="number" example="(555) 555-5555">%2$s</xliff:g></string>
-
- <!-- The order of the PhoneAccount and via number that a call was received on,
- if both are visible.
- [CHAR LIMIT=NONE]-->
- <string name="call_log_via_number_phone_account"><xliff:g id="phoneAccount" example="SIM 1">%1$s</xliff:g> via <xliff:g id="number" example="(555) 555-5555">%2$s</xliff:g></string>
-
- <!-- String describing the phone icon on a call log list item. When tapped, it will place a
- call to the number represented by that call log entry. [CHAR LIMIT=NONE]-->
- <string name="description_call_log_call_action">Call</string>
-
- <!-- String describing the "call" action for an entry in the call log. The call back
- action triggers a return call to the named user.
- Note: AccessibilityServices uses this attribute to announce the purpose of the button.
- [CHAR LIMIT=NONE] -->
- <string name="description_call_action">
- Call <xliff:g id="nameOrNumber" example="John Smith">^1</xliff:g>
- </string>
-
- <!-- String describing the "video call" action for an entry in the call log. The video call
- action triggers a return video call to the named person/number.
- Note: AccessibilityServices uses this attribute to announce the purpose of the button.
- [CHAR LIMIT=NONE] -->
- <string name="description_video_call_action">
- Video call <xliff:g id="nameOrNumber" example="John Smith">^1</xliff:g>.
- </string>
-
- <!-- String describing the "listen" action for an entry in the call log. The listen
- action is shown for call log entries representing a voicemail message and this button
- triggers playing back the voicemail.
- Note: AccessibilityServices uses this attribute to announce the purpose of the button.
- [CHAR LIMIT=NONE] -->
- <string name="description_voicemail_action">
- Listen to voicemail from <xliff:g id="nameOrNumber" example="John Smith">^1</xliff:g>
- </string>
-
- <!-- String describing the "play voicemail" action for an entry in the call log.
- Note: AccessibilityServices uses this attribute to announce the purpose of the button.
- [CHAR LIMIT=NONE] -->
- <string name="description_voicemail_play">
- Play voicemail from <xliff:g id="nameOrNumber" example="John Smith">^1</xliff:g>
- </string>
-
- <!-- String describing the "pause voicemail" action for an entry in the call log.
- Note: AccessibilityServices uses this attribute to announce the purpose of the button.
- [CHAR LIMIT=NONE] -->
- <string name="description_voicemail_pause">
- Pause voicemail from <xliff:g id="nameOrNumber" example="John Smith">^1</xliff:g>
- </string>
-
-
- <!-- String describing the "delete voicemail" action for an entry in the call log.
- Note: AccessibilityServices uses this attribute to announce the purpose of the button.
- [CHAR LIMIT=NONE] -->
- <string name="description_voicemail_delete">
- Delete voicemail from <xliff:g id="nameOrNumber" example="John Smith">^1</xliff:g>
- </string>
-
- <!-- String describing the number of new voicemails, displayed as a number badge on a tab.
- Note: AccessibilityServices uses this attribute to announce the purpose of the button.
- [CHAR LIMIT=NONE] -->
- <plurals name="description_voicemail_unread">
- <item quantity="one"><xliff:g id="count">%d</xliff:g> new voicemail</item>
- <item quantity="other"><xliff:g id="count">%d</xliff:g> new voicemails</item>
- </plurals>
-
- <!-- Description for the "create new contact" action for an entry in the call log. This action
- opens a screen for creating a new contact for this name or number. [CHAR LIMIT=NONE] -->
- <string name="description_create_new_contact_action">
- Create contact for <xliff:g id="nameOrNumber" example="John Smith">^1</xliff:g>
- </string>
-
- <!-- Description for the "add to existing contact" action for an entry in the call log. This
- action opens a screen for adding this name or number to an existing contact.
- [CHAR LIMIT=NONE] -->
- <string name="description_add_to_existing_contact_action">
- Add <xliff:g id="nameOrNumber" example="John Smith">^1</xliff:g> to existing contact
- </string>
-
- <!-- String describing the "details" action for an entry in the call log. The details action
- displays the call details screen for an entry in the call log. This shows the calls to
- and from the specified number associated with the call log entry.
- [CHAR LIMIT=NONE] -->
- <string name="description_details_action">
- Call details for <xliff:g id="nameOrNumber" example="John Smith">^1</xliff:g>
- </string>
-
- <!-- Toast message which appears when a call log entry is deleted.
- [CHAR LIMIT=NONE] -->
- <string name="toast_entry_removed">Deleted from call history</string>
-
- <!-- String used as a header in the call log above calls which occurred today.
- [CHAR LIMIT=65] -->
- <string name="call_log_header_today">Today</string>
-
- <!-- String used as a header in the call log above calls which occurred yesterday.
- [CHAR LIMIT=65] -->
- <string name="call_log_header_yesterday">Yesterday</string>
-
- <!-- String used as a header in the call log above calls which occurred two days or more ago.
- [CHAR LIMIT=65] -->
- <string name="call_log_header_other">Older</string>
-
- <!-- String a header on the call details screen. Appears above the list calls to or from a
- particular number.
- [CHAR LIMIT=65] -->
- <string name="call_detail_list_header">Calls list</string>
-
- <!-- String describing the "speaker on" button on the playback control used to listen to a
- voicemail message. When speaker is on, playback of the voicemail will occur through the
- phone speaker.
- Note: AccessibilityServices uses this attribute to announce the purpose of the button.
- [CHAR LIMIT=NONE] -->
- <string name="voicemail_speaker_on">Turn speaker on.</string>
-
- <!-- String describing the "speaker off" button on the playback control used to listen to a
- voicemail message. When speaker is off, playback of the voicemail will occur through the
- phone earpiece.
- Note: AccessibilityServices uses this attribute to announce the purpose of the button.
- [CHAR LIMIT=NONE] -->
- <string name="voicemail_speaker_off">Turn speaker off.</string>
-
- <!-- String describing the "play faster" button in the playback control used to listen to a
- voicemail message. Speeds up playback of the voicemail message.
- Note: AccessibilityServices uses this attribute to announce the purpose of the button.
- [CHAR LIMIT=NONE] -->
- <string name="voicemail_play_faster">Play faster.</string>
-
- <!-- String describing the "play slower" button in the playback control used to listen to a
- voicemail message. Slows down playback of the voicemail message.
- Note: AccessibilityServices uses this attribute to announce the purpose of the button.
- [CHAR LIMIT=NONE] -->
- <string name="voicemail_play_slower">Play slower.</string>
-
- <!-- String describing the "play/pause" button in the playback control used to listen to a
- voicemail message. Starts playback or pauses ongoing playback.
- Note: AccessibilityServices uses this attribute to announce the purpose of the button.
- [CHAR LIMIT=NONE] -->
- <string name="voicemail_play_start_pause">Start or pause playback.</string>
-
- <!-- Delimeter used between each item in a textual list; for example "Alpha, Beta".
- [CHAR LIMIT=3] -->
- <string name="list_delimeter">", "</string>
-
- <!-- Dialer settings related strings-->
-
- <!-- Title for "Display options" category, which controls how contacts are shown.
- [CHAR LIMIT=40] -->
- <string name="display_options_title">Display options</string>
-
- <!-- Title for the "Sounds and vibration" settings control settings related to ringtones,
- dialpad tones, and vibration for incoming calls. [CHAR LIMIT=40] -->
- <string name="sounds_and_vibration_title">Sounds and vibration</string>
-
- <!-- Title for "Accessibility" category, which controls settings such as TTY mode and hearing
- aid compatability. [CHAR LIMIT=40] -->
- <string name="accessibility_settings_title">Accessibility</string>
-
- <!-- Setting option name to pick ringtone (a list dialog comes up). [CHAR LIMIT=30] -->
- <string name="ringtone_title">Phone ringtone</string>
-
- <!-- Setting option name to enable or disable vibration when ringing the phone.
- [CHAR LIMIT=30] -->
- <string name="vibrate_on_ring_title">"Also vibrate for calls</string>
-
- <!-- Setting option name to enable or disable DTMF tone sound [CHAR LIMIT=30] -->
- <string name="dtmf_tone_enable_title">Dialpad tones</string>
- <!-- Label for setting to adjust the length of DTMF tone sounds. [CHAR LIMIT=40] -->
- <string name="dtmf_tone_length_title">Dialpad tone length</string>
- <!-- Options displayed for the length of DTMF tone sounds. [CHAR LIMIT=40] -->
- <string-array name="dtmf_tone_length_entries">
- <item>Normal</item>
- <item>Long</item>
- </string-array>
- <!-- Do not translate. -->
- <string-array name="dtmf_tone_length_entry_values" translatable="false">
- <item>0</item>
- <item>1</item>
- </string-array>
-
- <!-- Title of settings screen for managing the "Respond via SMS" feature. [CHAR LIMIT=30] -->
- <string name="respond_via_sms_setting_title">Quick responses</string>
-
- <!-- Label for the call settings section [CHAR LIMIT=30] -->
- <string name="call_settings_label">Calls</string>
-
- <!-- Label for the blocked numbers settings section [CHAR LIMIT=30] -->
- <string name="manage_blocked_numbers_label">Call blocking</string>
-
- <!-- Label for a section describing that call blocking is temporarily disabled because an
- emergency call was made. [CHAR LIMIT=50] -->
- <string name="blocked_numbers_disabled_emergency_header_label">
- Call blocking temporarily off
- </string>
-
- <!-- Description that call blocking is temporarily disabled because the user called an
- emergency number, and explains that call blocking will be re-enabled after a buffer
- period has passed. [CHAR LIMIT=NONE] -->
- <string name="blocked_numbers_disabled_emergency_desc">
- Call blocking has been disabled because you contacted emergency services from this phone
- within the last 48 hours. It will be automatically reenabled once the 48 hour period
- expires.
- </string>
-
- <!-- Label for fragment to import numbers from contacts marked as send to voicemail.
- [CHAR_LIMIT=30] -->
- <string name="import_send_to_voicemail_numbers_label">Import numbers</string>
-
- <!-- Text informing the user they have previously marked contacts to be sent to voicemail.
- This will be followed by two buttons, 1) to view who is marked to be sent to voicemail
- and 2) importing these settings to Dialer's block list. [CHAR LIMIT=NONE] -->
- <string name="blocked_call_settings_import_description">
- You previously marked some callers to be automatically sent to voicemail via other apps.
- </string>
-
- <!-- Label for button to view numbers of contacts previous marked to be sent to voicemail.
- [CHAR_LIMIT=20] -->
- <string name="blocked_call_settings_view_numbers_button">View Numbers</string>
-
- <!-- Label for button to import settings for sending contacts to voicemail into Dialer's block
- list. [CHAR_LIMIT=20] -->
- <string name="blocked_call_settings_import_button">Import</string>
-
- <!-- Error toast message for when send to voicemail import fails. [CHAR LIMIT=40] -->
- <string name="send_to_voicemail_import_failed">Import failed</string>
-
- <!-- Error toast message for when voicemail archive fails. [CHAR LIMIT=40] -->
- <string name="voicemail_archive_failed">Failed to archive voicemail.</string>
-
- <!-- String describing the delete icon on a blocked number list item.
- When tapped, it will show a dialog confirming the unblocking of the number.
- [CHAR LIMIT=NONE]-->
- <string name="description_blocked_number_list_delete">Unblock number</string>
-
- <!-- Button to bring up UI to add a number to the blocked call list. [CHAR LIMIT=40] -->
- <string name="addBlockedNumber">Add number</string>
-
- <!-- Footer message of number blocking screen with visual voicemail active.
- [CHAR LIMIT=NONE] -->
- <string name="block_number_footer_message_vvm">
- Calls from these numbers will be blocked and voicemails will be automatically deleted.
- </string>
-
- <!-- Footer message of number blocking screen with no visual voicemail.
- [CHAR LIMIT=NONE] -->
- <string name="block_number_footer_message_no_vvm">
- Calls from these numbers will be blocked, but they may still be able to leave you voicemails.
- </string>
-
- <!-- Heading for the block list in the "Spam and blocked cal)ls" settings. [CHAR LIMIT=64] -->
- <string name="block_list">Blocked numbers</string>
-
- <!-- Error message shown when user tries to add invalid number to the block list.
- [CHAR LIMIT=64] -->
- <string name="invalidNumber"><xliff:g id="number" example="(555) 555-5555">%1$s</xliff:g>
- is invalid.</string>
-
- <!-- Error message shown when user tries to add a number to the block list that was already
- blocked. [CHAR LIMIT=64] -->
- <string name="alreadyBlocked"><xliff:g id="number" example="(555) 555-5555">%1$s</xliff:g>
- is already blocked.</string>
-
- <!-- Title of notification telling the user that call blocking has been temporarily disabled.
- [CHAR LIMIT=56] -->
- <string name="call_blocking_disabled_notification_title">
- Call blocking disabled for 48 hours
- </string>
-
- <!-- Text for notification which provides the reason that call blocking has been temporarily
- disabled. Namely, we disable call blocking after an emergency call in case of return
- phone calls made by emergency services. [CHAR LIMIT=64] -->
- <string name="call_blocking_disabled_notification_text">
- Disabled because an emergency call was made.
- </string>
-
- <!-- Label for the phone account settings [CHAR LIMIT=30] -->
- <string name="phone_account_settings_label">Calling accounts</string>
-
- <!-- DO NOT TRANSLATE. Internal key for ringtone preference. -->
- <string name="ringtone_preference_key" translatable="false">button_ringtone_key</string>
- <!-- DO NOT TRANSLATE. Internal key for vibrate when ringing preference. -->
- <string name="vibrate_on_preference_key" translatable="false">button_vibrate_on_ring</string>
- <!-- DO NOT TRANSLATE. Internal key for vibrate when ringing preference. -->
- <string name="play_dtmf_preference_key" translatable="false">button_play_dtmf_tone</string>
- <!-- DO NOT TRANSLATE. Internal key for DTMF tone length preference. -->
- <string name="dtmf_tone_length_preference_key" translatable="false">button_dtmf_settings</string>
-
- <!-- The label of the button used to turn on a single permission [CHAR LIMIT=30]-->
- <string name="permission_single_turn_on">Turn on</string>
-
- <!-- The label of the button used to turn on multiple permissions [CHAR LIMIT=30]-->
- <string name="permission_multiple_turn_on">Set permissions</string>
-
- <!-- Shown as a prompt to turn on the contacts permission to enable speed dial [CHAR LIMIT=NONE]-->
- <string name="permission_no_speeddial">To enable speed dial, turn on the Contacts permission.</string>
-
- <!-- Shown as a prompt to turn on the phone permission to enable the call log [CHAR LIMIT=NONE]-->
- <string name="permission_no_calllog">To see your call log, turn on the Phone permission.</string>
-
- <!-- Shown as a prompt to turn on the contacts permission to show all contacts [CHAR LIMIT=NONE]-->
- <string name="permission_no_contacts">To see your contacts, turn on the Contacts permission.</string>
-
- <!-- Shown as a prompt to turn on the phone permission to show voicemails [CHAR LIMIT=NONE]-->
- <string name="permission_no_voicemail">To access your voicemail, turn on the Phone permission.</string>
-
- <!-- Shown as a prompt to turn on contacts permissions to allow contact search [CHAR LIMIT=NONE]-->
- <string name="permission_no_search">To search your contacts, turn on the Contacts permissions.</string>
-
- <!-- Shown as a prompt to turn on the phone permission to allow a call to be placed [CHAR LIMIT=NONE]-->
- <string name="permission_place_call">To place a call, turn on the Phone permission.</string>
-
- <!-- Shown as a message that notifies the user that the Phone app cannot write to system settings, which is why the system settings app is being launched directly instead. [CHAR LIMIT=NONE]-->
- <string name="toast_cannot_write_system_settings">Phone app does not have permission to write to system settings.</string>
-
- <!-- Label under the name of a blocked number in the call log. [CHAR LIMIT=15] -->
- <string name="blocked_number_call_log_label">Blocked</string>
-
- <!-- Accessibility announcement to indicate which call is active -->
- <string name="accessibility_call_is_active"><xliff:g id="nameOrNumber">^1</xliff:g> is active</string>
-
- <!-- Button text for a button displayed underneath an entry in the call log, which marks the
- phone number represented by the call log entry as a Spam number.
- [CHAR LIMIT=30] -->
- <string name="call_log_action_block_report_number">Block/report spam</string>
-
- <!-- Button text for a button displayed underneath an entry in the call log, which marks the
- phone number represented by the call log entry as a Spam number.
- [CHAR LIMIT=30] -->
- <string name="call_log_action_block_number">Block</string>
-
- <!-- Button text for a button displayed underneath an entry in the call log, which removes the
- phone number represented by the call log entry from the Spam numbers list.
- [CHAR LIMIT=30] -->
- <string name="call_log_action_remove_spam">Not spam</string>
-
- <!-- Button text for a button displayed underneath an entry in the call log, which removes the
- phone number represented by the call log entry from the blacklisted numbers.
- [CHAR LIMIT=30] -->
- <string name="call_log_action_unblock_number">Unblock</string>
-
- <!-- Label under the name of a spam number in the call log. [CHAR LIMIT=15] -->
- <string name="spam_number_call_log_label">Spam</string>
-
- <!-- Title of alert dialog after clicking on Block/report as spam. [CHAR LIMIT=100] -->
- <string name="block_report_number_alert_title">Block <xliff:g id="number">%1$s</xliff:g>?</string>
-
- <!-- Text in alert dialog after clicking on Block/report as spam. [CHAR LIMIT=100] -->
- <string name="block_report_number_alert_details">Future calls and voicemails from this number will be blocked.</string>
-
- <!-- Label for checkbox in the Alert dialog to allow the user to report the number as spam as well. [CHAR LIMIT=30] -->
- <string name="checkbox_report_as_spam_action">Report call as spam</string>
-
- <!-- Text in alert dialog after clicking on Block. [CHAR LIMIT=100] -->
- <string name="block_number_alert_details">Future calls and voicemails from this number will be blocked. This call will be reported as spam.</string>
-
- <!-- Title of alert dialog after clicking on Unblock. [CHAR LIMIT=100] -->
- <string name="unblock_report_number_alert_title">Unblock <xliff:g id="number">%1$s</xliff:g>?</string>
-
- <!-- Text in alert dialog after clicking on Unblock. [CHAR LIMIT=100] -->
- <string name="unblock_number_alert_details">This number will be unblocked and reported as not spam. Future calls and voicemails won\'t be identified as spam.</string>
-
- <!-- Title of alert dialog after clicking on Report as not spam. [CHAR LIMIT=100] -->
- <string name="report_not_spam_alert_title">Whitelist <xliff:g id="number">%1$s</xliff:g>?</string>
-
- <!-- Report not spam number alert dialog button [CHAR LIMIT=32] -->
- <string name="report_not_spam_alert_button">Whitelist</string>
-
- <!-- Text in alert dialog after clicking on Report as not spam. [CHAR LIMIT=100] -->
- <string name="report_not_spam_alert_details">Future calls and voicemails from this number won\'t be identified as spam. This number will be reported as not spam.</string>
-</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
deleted file mode 100644
index 6a40d09be..000000000
--- a/res/values/styles.xml
+++ /dev/null
@@ -1,346 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<resources>
- <style name="DialtactsTheme" parent="Theme.AppCompat.Light">
- <item name="android:textColorPrimary">@color/dialtacts_primary_text_color</item>
- <item name="android:textColorSecondary">@color/dialtacts_secondary_text_color</item>
-
- <!-- Styles that require AppCompat compatibility, remember to update both sets -->
- <item name="android:windowActionBarOverlay">true</item>
- <item name="windowActionBarOverlay">true</item>
- <item name="android:windowActionModeOverlay">true</item>
- <item name="windowActionModeOverlay">true</item>
- <item name="android:actionBarStyle">@style/DialtactsActionBarStyle</item>
- <item name="actionBarStyle">@style/DialtactsActionBarStyle</item>
- <!-- Style for the overflow button in the actionbar. -->
- <item name="android:actionOverflowButtonStyle">@style/DialtactsActionBarOverflow</item>
- <item name="actionOverflowButtonStyle">@style/DialtactsActionBarOverflow</item>
-
- <!-- Drawable for the back button -->
- <item name="android:homeAsUpIndicator">@drawable/ic_back_arrow</item>
- <item name="android:windowContentOverlay">@null</item>
- <item name="android:listViewStyle">@style/ListViewStyle</item>
- <item name="android:overlapAnchor">true</item>
- <item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
- <item name="activated_background">@drawable/list_item_activated_background</item>
- <item name="section_header_background">@drawable/list_title_holo</item>
- <item name="list_section_header_height">32dip</item>
- <item name="list_item_padding_top">12dp</item>
- <item name="list_item_padding_right">24dp</item>
- <item name="list_item_padding_bottom">12dp</item>
- <item name="list_item_padding_left">16dp</item>
- <item name="list_item_gap_between_image_and_text">
- @dimen/contact_browser_list_item_gap_between_image_and_text
- </item>
- <item name="list_item_gap_between_label_and_data">8dip</item>
- <item name="list_item_presence_icon_margin">4dip</item>
- <item name="list_item_presence_icon_size">16dip</item>
- <item name="list_item_photo_size">@dimen/contact_browser_list_item_photo_size</item>
- <item name="list_item_profile_photo_size">70dip</item>
- <item name="list_item_prefix_highlight_color">@color/people_app_theme_color</item>
- <item name="list_item_background_color">@color/background_dialer_light</item>
- <item name="list_item_header_text_indent">8dip</item>
- <item name="list_item_header_text_color">@color/dialtacts_secondary_text_color</item>
- <item name="list_item_header_text_size">14sp</item>
- <item name="list_item_header_height">30dip</item>
- <item name="list_item_data_width_weight">5</item>
- <item name="list_item_label_width_weight">3</item>
- <item name="contact_browser_list_padding_left">0dp</item>
- <item name="contact_browser_list_padding_right">0dp</item>
- <item name="contact_browser_background">@color/background_dialer_results</item>
- <item name="list_item_name_text_color">@color/contact_list_name_text_color</item>
- <item name="list_item_name_text_size">16sp</item>
- <item name="list_item_text_indent">@dimen/contact_browser_list_item_text_indent</item>
- <item name="list_item_text_offset_top">-2dp</item>
- <!-- CallLog -->
- <item name="call_log_primary_text_color">@color/dialtacts_primary_text_color</item>
- <item name="call_log_primary_background_color">#000000</item>
- <item name="call_log_secondary_text_color">@color/dialtacts_secondary_text_color</item>
- <item name="call_log_secondary_background_color">#333333</item>
- <item name="call_log_header_color">#33b5e5</item>
- <!-- VoicemailStatus -->
- <item name="call_log_voicemail_status_height">48dip</item>
- <item name="call_log_voicemail_status_background_color">#262626</item>
- <item name="call_log_voicemail_status_text_color">#888888</item>
- <item name="call_log_voicemail_status_action_text_color">#33b5e5</item>
- <!-- Favorites -->
- <item name="favorites_padding_bottom">?android:attr/actionBarSize</item>
- <item name="android:colorPrimary">@color/dialer_theme_color</item>
- <item name="android:colorPrimaryDark">@color/dialer_theme_color_dark</item>
- <item name="dialpad_key_button_touch_tint">@color/dialer_dialpad_touch_tint</item>
- <item name="android:colorControlActivated">@color/dialer_theme_color</item>
- <item name="android:colorButtonNormal">@color/dialer_theme_color</item>
- <item name="android:textAppearanceButton">@style/DialerButtonTextStyle</item>
-
- <!-- Video call icon -->
- <item name="list_item_video_call_icon_size">32dip</item>
- <item name="list_item_video_call_icon_margin">8dip</item>
- </style>
-
- <style name="DialerButtonTextStyle" parent="@android:style/TextAppearance.Material.Widget.Button">
- <item name="android:textColor">#fff</item>
- </style>
-
- <!-- Action bar overflow menu icon. -->
- <style name="DialtactsActionBarOverflow"
- parent="@android:style/Widget.Material.Light.ActionButton.Overflow">
- <item name="android:src">@drawable/ic_overflow_menu</item>
- </style>
-
- <!-- Action bar overflow menu icon. White with no shadow. -->
- <style name="DialtactsActionBarOverflowWhite"
- parent="@android:style/Widget.Material.Light.ActionButton.Overflow">
- <item name="android:src">@drawable/overflow_menu</item>
- </style>
-
- <style name="DialpadTheme" parent="DialtactsTheme">
- <item name="android:textColorPrimary">#FFFFFF</item>
- </style>
-
- <style name="DialtactsThemeWithoutActionBarOverlay" parent="DialtactsTheme">
- <!-- Styles that require AppCompat compatibility, remember to update both sets -->
- <item name="android:windowActionBarOverlay">false</item>
- <item name="windowActionBarOverlay">false</item>
- <item name="android:actionOverflowButtonStyle">@style/DialtactsActionBarOverflowWhite</item>
- <item name="actionOverflowButtonStyle">@style/DialtactsActionBarOverflowWhite</item>
- </style>
-
- <!-- Hide the actionbar title during the activity preview -->
- <style name="DialtactsActivityTheme" parent="DialtactsTheme">
- <!-- Styles that require AppCompat compatibility, remember to update both sets -->
- <item name="android:actionBarStyle">@style/DialtactsActionBarWithoutTitleStyle</item>
- <item name="actionBarStyle">@style/DialtactsActionBarWithoutTitleStyle</item>
-
- <item name="android:fastScrollThumbDrawable">@drawable/fastscroll_thumb</item>
- <item name="android:fastScrollTrackDrawable">@null</item>
- </style>
-
- <style name="CallDetailActivityTheme" parent="DialtactsThemeWithoutActionBarOverlay">
- <item name="android:windowBackground">@color/background_dialer_results</item>
- <!-- CallLog -->
- <item name="call_log_primary_background_color">#FFFFFF</item>
- <item name="call_log_secondary_background_color">#FFFFFF</item>
- <item name="call_log_header_color">#FFFFFF</item>
- <!-- VoicemailStatus -->
- <item name="call_log_voicemail_status_height">48dip</item>
- <item name="call_log_voicemail_status_background_color">#262626</item>
- <item name="call_log_voicemail_status_text_color">#888888</item>
- <item name="call_log_voicemail_status_action_text_color">#33b5e5</item>
- <item name="android:actionOverflowButtonStyle">@style/DialtactsActionBarOverflowWhite</item>
- </style>
-
- <style name="CallDetailActionItemStyle">
- <item name="android:foreground">?android:attr/selectableItemBackground</item>
- <item name="android:clickable">true</item>
- <item name="android:drawablePadding">@dimen/call_detail_action_item_drawable_padding</item>
- <item name="android:gravity">center_vertical</item>
- <item name="android:paddingStart">@dimen/call_detail_action_item_padding_horizontal</item>
- <item name="android:paddingEnd">@dimen/call_detail_action_item_padding_horizontal</item>
- <item name="android:paddingTop">@dimen/call_detail_action_item_padding_vertical</item>
- <item name="android:paddingBottom">@dimen/call_detail_action_item_padding_vertical</item>
- <item name="android:textColor">@color/call_detail_footer_text_color</item>
- <item name="android:textSize">@dimen/call_detail_action_item_text_size</item>
- </style>
-
- <style name="DialtactsActionBarStyle"
- parent="@style/Widget.AppCompat.Light.ActionBar.Solid.Inverse">
- <!-- Styles that require AppCompat compatibility, remember to update both sets -->
- <item name="android:background">@color/actionbar_background_color</item>
- <item name="background">@color/actionbar_background_color</item>
- <item name="android:titleTextStyle">@style/DialtactsActionBarTitleText</item>
- <item name="titleTextStyle">@style/DialtactsActionBarTitleText</item>
- <item name="android:height">@dimen/action_bar_height</item>
- <item name="height">@dimen/action_bar_height</item>
- <item name="android:elevation">@dimen/action_bar_elevation</item>
- <item name="elevation">@dimen/action_bar_elevation</item>
- <!-- Empty icon -->
- <item name="android:icon">@android:color/transparent</item>
- <item name="icon">@android:color/transparent</item>
- <!-- Shift the title text to the right -->
- <item name="android:contentInsetStart">@dimen/actionbar_contentInsetStart</item>
- <item name="contentInsetStart">@dimen/actionbar_contentInsetStart</item>
- </style>
-
- <style name="DialtactsActionBarWithoutTitleStyle" parent="DialtactsActionBarStyle">
- <!-- Styles that require AppCompat compatibility, remember to update both sets -->
- <item name="android:displayOptions"></item>
- <item name="displayOptions"></item>
- <item name="android:height">@dimen/action_bar_height_large</item>
- <item name="height">@dimen/action_bar_height_large</item>
- <!-- Override ActionBar title offset to keep search box aligned left -->
- <item name="android:contentInsetStart">0dp</item>
- <item name="contentInsetStart">0dp</item>
- <item name="android:contentInsetEnd">0dp</item>
- <item name="contentInsetEnd">0dp</item>
- </style>
-
- <!-- Text in the action bar at the top of the screen -->
- <style name="DialtactsActionBarTitleText"
- parent="@android:style/TextAppearance.Material.Widget.ActionBar.Title">
- <item name="android:textColor">@color/actionbar_text_color</item>
- </style>
-
- <!-- Text style for tabs. -->
- <style name="DialtactsActionBarTabTextStyle"
- parent="android:style/Widget.Material.Light.ActionBar.TabText">
- <item name="android:textColor">@color/tab_text_color</item>
- <item name="android:textSize">@dimen/tab_text_size</item>
- <item name="android:fontFamily">"sans-serif-medium"</item>
- </style>
-
- <style name="ListViewStyle" parent="@android:style/Widget.Material.Light.ListView">
- <item name="android:overScrollMode">always</item>
- </style>
-
- <style name="CallLogActionStyle">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">@dimen/call_log_action_height</item>
- <item name="android:background">?android:attr/selectableItemBackground</item>
- <item name="android:orientation">horizontal</item>
- <item name="android:gravity">center_vertical</item>
- </style>
-
- <style name="CallLogActionTextStyle">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:paddingStart">@dimen/call_log_action_horizontal_padding</item>
- <item name="android:paddingEnd">@dimen/call_log_action_horizontal_padding</item>
- <item name="android:textColor">@color/call_log_action_color</item>
- <item name="android:textSize">@dimen/call_log_primary_text_size</item>
- <item name="android:fontFamily">"sans-serif"</item>
- <item name="android:focusable">true</item>
- <item name="android:singleLine">true</item>
- <item name="android:importantForAccessibility">no</item>
- </style>
-
- <style name="CallLogActionSupportTextStyle" parent="@style/CallLogActionTextStyle">
- <item name="android:textSize">@dimen/call_log_detail_text_size</item>
- <item name="android:textColor">@color/call_log_detail_color</item>
- </style>
-
- <style name="CallLogActionIconStyle">
- <item name="android:layout_width">@dimen/call_log_action_icon_dimen</item>
- <item name="android:layout_height">@dimen/call_log_action_icon_dimen</item>
- <item name="android:layout_marginStart">@dimen/call_log_action_icon_margin_start</item>
- <item name="android:tint">?attr/call_log_secondary_text_color</item>
- <item name="android:importantForAccessibility">no</item>
- </style>
-
- <style name="DismissButtonStyle">
- <item name="android:paddingLeft">@dimen/dismiss_button_padding_start</item>
- <item name="android:paddingRight">@dimen/dismiss_button_padding_end</item>
- </style>
-
- <!-- Style applied to the "Settings" screen. Keep in sync with SettingsLight in Telephony. -->
- <style name="SettingsStyle" parent="DialtactsThemeWithoutActionBarOverlay">
- <!-- Setting text. -->
- <item name="android:textColorPrimary">@color/settings_text_color_primary</item>
- <!-- Setting description. -->
- <item name="android:textColorSecondary">@color/settings_text_color_secondary</item>
- <item name="android:windowBackground">@color/setting_background_color</item>
- <item name="android:colorAccent">@color/dialtacts_theme_color</item>
- <item name="android:textColorLink">@color/dialtacts_theme_color</item>
- </style>
-
- <style name="ManageBlockedNumbersStyle" parent="SettingsStyle">
- <!-- Styles that require AppCompat compatibility, remember to update both sets -->
- <item name="android:windowActionBarOverlay">true</item>
- <item name="windowActionBarOverlay">true</item>
- <item name="android:actionBarStyle">@style/ManageBlockedNumbersActionBarStyle</item>
- <item name="actionBarStyle">@style/ManageBlockedNumbersActionBarStyle</item>
- <item name="android:fastScrollTrackDrawable">@null</item>
- </style>
-
- <style name="ManageBlockedNumbersActionBarStyle" parent="DialtactsActionBarWithoutTitleStyle">
- <!-- Styles that require AppCompat compatibility, remember to update both sets -->
- <item name="android:height">@dimen/action_bar_height</item>
- <item name="height">@dimen/action_bar_height</item>
- </style>
-
- <!-- Inherit from Theme.Material.Light.Dialog instead of Theme.Material.Light.Dialog.Alert
- since the Alert dialog is private. They are identical anyway. -->
- <style name="AlertDialogTheme" parent="@android:style/Theme.Material.Light.Dialog">
- <item name="android:colorAccent">@color/dialtacts_theme_color</item>
- </style>
-
- <style name="CallLogCardStyle" parent="CardView">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:layout_margin">4dp</item>
- <item name="android:baselineAligned">false</item>
- <item name="cardCornerRadius">2dp</item>
- <item name="cardBackgroundColor">@color/background_dialer_call_log_list_item</item>
- </style>
-
- <style name="TextActionStyle">
- <item name="android:layout_width">wrap_content</item>
- <item name="android:layout_height">@dimen/call_log_action_height</item>
- <item name="android:gravity">end|center_vertical</item>
- <item name="android:paddingStart">@dimen/call_log_action_horizontal_padding</item>
- <item name="android:paddingEnd">@dimen/call_log_action_horizontal_padding</item>
- <item name="android:textColor">@color/dialtacts_theme_color</item>
- <item name="android:fontFamily">"sans-serif-medium"</item>
- <item name="android:focusable">true</item>
- <item name="android:singleLine">true</item>
- <item name="android:textAllCaps">true</item>
- </style>
-
- <style name="PromoCardActionStyle" parent="TextActionStyle">
- <item name="android:textColor">@color/promo_card_text</item>
- <item name="android:textSize">@dimen/call_log_primary_text_size</item>
- </style>
-
- <style name="VoicemailPlaybackLayoutTextStyle">
- <item name="android:textSize">14sp</item>
- </style>
-
- <style name="VoicemailPlaybackLayoutButtonStyle">
- <item name="android:layout_width">56dp</item>
- <item name="android:layout_height">56dp</item>
- <item name="android:background">@drawable/oval_ripple</item>
- <item name="android:padding">8dp</item>
- </style>
-
- <style name="DialerFlatButtonStyle" parent="@android:style/Widget.Material.Button">
- <item name="android:background">?android:attr/selectableItemBackground</item>
- <item name="android:paddingEnd">@dimen/button_horizontal_padding</item>
- <item name="android:paddingStart">@dimen/button_horizontal_padding</item>
- <item name="android:textColor">@color/dialer_flat_button_text_color</item>
- </style>
-
- <!-- Style for the 'primary' button in a view. Unlike the DialerFlatButtonStyle, this button -->
- <!-- is not colored white, to draw more attention to it. -->
- <style name="DialerPrimaryFlatButtonStyle" parent="@android:style/Widget.Material.Button">
- <item name="android:background">@drawable/selectable_primary_flat_button</item>
- <item name="android:paddingEnd">@dimen/button_horizontal_padding</item>
- <item name="android:paddingStart">@dimen/button_horizontal_padding</item>
- <item name="android:textColor">@android:color/white</item>
- </style>
-
- <style name="BlockedNumbersDescriptionTextStyle">
- <item name="android:lineSpacingMultiplier">1.43</item>
- <item name="android:paddingTop">8dp</item>
- <item name="android:paddingBottom">8dp</item>
- <item name="android:textSize">@dimen/blocked_number_settings_description_text_size</item>
- </style>
-
- <style name="FullWidthDivider">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">1dp</item>
- <item name="android:background">?android:attr/listDivider</item>
- </style>
-</resources>
diff --git a/res/xml/display_options_settings.xml b/res/xml/display_options_settings.xml
deleted file mode 100644
index 07aadf758..000000000
--- a/res/xml/display_options_settings.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ 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
- -->
-
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
-
- <com.android.contacts.common.preference.SortOrderPreference
- android:key="sortOrder"
- android:title="@string/display_options_sort_list_by"
- android:dialogTitle="@string/display_options_sort_list_by" />
-
- <com.android.contacts.common.preference.DisplayOrderPreference
- android:key="displayOrder"
- android:title="@string/display_options_view_names_as"
- android:dialogTitle="@string/display_options_view_names_as" />
-
-</PreferenceScreen>
diff --git a/res/xml/file_paths.xml b/res/xml/file_paths.xml
deleted file mode 100644
index 365a60318..000000000
--- a/res/xml/file_paths.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<paths xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- Offer access to files under Context.getCacheDir() -->
- <cache-path name="my_cache" />
- <!-- Offer access to voicemail folder under Context.getFilesDir() -->
- <files-path name="voicemails" path="voicemails/" />
-</paths>
diff --git a/res/xml/searchable.xml b/res/xml/searchable.xml
deleted file mode 100644
index 0e8242ddf..000000000
--- a/res/xml/searchable.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-<searchable xmlns:android="http://schemas.android.com/apk/res/android"
- android:label="@string/applicationLabel"
- android:hint="@string/dialer_hint_find_contact"
- android:inputType="textNoSuggestions"
- android:imeOptions="actionSearch"
- android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"
- /> \ No newline at end of file
diff --git a/res/xml/sound_settings.xml b/res/xml/sound_settings.xml
deleted file mode 100644
index 80fad626a..000000000
--- a/res/xml/sound_settings.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
-
- <com.android.dialer.settings.DefaultRingtonePreference
- android:key="@string/ringtone_preference_key"
- android:title="@string/ringtone_title"
- android:dialogTitle="@string/ringtone_title"
- android:persistent="false"
- android:ringtoneType="ringtone" />
-
- <CheckBoxPreference
- android:key="@string/vibrate_on_preference_key"
- android:title="@string/vibrate_on_ring_title"
- android:persistent="false"
- android:defaultValue="false" />
-
- <CheckBoxPreference
- android:key="@string/play_dtmf_preference_key"
- android:title="@string/dtmf_tone_enable_title"
- android:persistent="false"
- android:defaultValue="true" />
-
- <ListPreference
- android:key="@string/dtmf_tone_length_preference_key"
- android:title="@string/dtmf_tone_length_title"
- android:entries="@array/dtmf_tone_length_entries"
- android:entryValues="@array/dtmf_tone_length_entry_values" />
-
-</PreferenceScreen>
diff --git a/src-N/com/android/dialer/SdkSelectionUtils.java b/src-N/com/android/dialer/SdkSelectionUtils.java
deleted file mode 100644
index ae7a63174..000000000
--- a/src-N/com/android/dialer/SdkSelectionUtils.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.dialer;
-
-/**
- * Provides information for the SDK the app is built against.
- * Specifically, information that change when the TARGET_N_SDK build flag is set in the makefile.
- * This is not related to the targetSdkVersion value in AndroidManifest.xml.
- *
- * Usage case will be branching test code in src/, instead of swapping between src-N and src-pre-N.
- */
-public class SdkSelectionUtils {
-
- /**
- * Whether the app is build against N SDK.
- *
- * Since Build.VERSION.SDK_INT remains 23 on N SDK for now, this is currently the only way to
- * check if we are building with N SDK or other.
- */
- public static final boolean TARGET_N_SDK = true;
-}
diff --git a/src-N/com/android/dialer/compat/BlockedNumbersSdkCompat.java b/src-N/com/android/dialer/compat/BlockedNumbersSdkCompat.java
deleted file mode 100644
index a60820732..000000000
--- a/src-N/com/android/dialer/compat/BlockedNumbersSdkCompat.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.dialer.compat;
-
-import android.content.Context;
-import android.net.Uri;
-import android.provider.BlockedNumberContract;
-import android.provider.BlockedNumberContract.BlockedNumbers;
-
-public class BlockedNumbersSdkCompat {
-
- public static final Uri CONTENT_URI = BlockedNumbers.CONTENT_URI;
-
- public static final String _ID = BlockedNumbers.COLUMN_ID;
-
- public static final String COLUMN_ORIGINAL_NUMBER = BlockedNumbers.COLUMN_ORIGINAL_NUMBER;
-
- public static final String E164_NUMBER = BlockedNumbers.COLUMN_E164_NUMBER;
-
- public static boolean canCurrentUserBlockNumbers(Context context) {
- return BlockedNumberContract.canCurrentUserBlockNumbers(context);
- }
-}
diff --git a/src-N/com/android/dialer/compat/CallsSdkCompat.java b/src-N/com/android/dialer/compat/CallsSdkCompat.java
deleted file mode 100644
index a428ca386..000000000
--- a/src-N/com/android/dialer/compat/CallsSdkCompat.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.dialer.compat;
-
-import android.provider.CallLog;
-
-public class CallsSdkCompat {
-
- public static final String POST_DIAL_DIGITS = CallLog.Calls.POST_DIAL_DIGITS;
- public static final String VIA_NUMBER = CallLog.Calls.VIA_NUMBER;
-}
diff --git a/src-N/com/android/dialer/compat/UserManagerSdkCompat.java b/src-N/com/android/dialer/compat/UserManagerSdkCompat.java
deleted file mode 100644
index 9a08d4e71..000000000
--- a/src-N/com/android/dialer/compat/UserManagerSdkCompat.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.dialer.compat;
-
-import android.content.Context;
-
-/**
- * UserManagerCompat respecting Sdk requirements
- */
-public class UserManagerSdkCompat {
-
- /**
- * Return whether the calling user is running in an "unlocked" state. A user
- * is unlocked only after they've entered their credentials (such as a lock
- * pattern or PIN), and credential-encrypted private app data storage is
- * available.
- *
- * @param context the current context
- * @return {@code true} if the user is unlocked or context is null, {@code false} otherwise
- * @throws NullPointerException if context is null
- */
- public static boolean isUserUnlocked(Context context) {
- return android.support.v4.os.UserManagerCompat.isUserUnlocked(context);
- }
-
-}
diff --git a/src-pre-N/com/android/dialer/SdkSelectionUtils.java b/src-pre-N/com/android/dialer/SdkSelectionUtils.java
deleted file mode 100644
index 7e36b332c..000000000
--- a/src-pre-N/com/android/dialer/SdkSelectionUtils.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.dialer;
-
-/**
- * Provides information for the SDK the app is built against.
- * Specifically, information that change when the TARGET_N_SDK build flag is set in the makefile.
- * This is not related to the targetSdkVersion value in AndroidManifest.xml.
- *
- * Usage case will be branching test code in src/, instead of using src-N/ and src-pre-N/
- */
-public class SdkSelectionUtils {
-
- /**
- * Whether the app is build against N SDK.
- *
- * Since Build.VERSION.SDK_INT remains 23 on N SDK for now, this is currently the only way to
- * check if we are building with N SDK or other.
- */
- public static final boolean TARGET_N_SDK = false;
-}
diff --git a/src-pre-N/com/android/dialer/compat/BlockedNumbersSdkCompat.java b/src-pre-N/com/android/dialer/compat/BlockedNumbersSdkCompat.java
deleted file mode 100644
index 559d71899..000000000
--- a/src-pre-N/com/android/dialer/compat/BlockedNumbersSdkCompat.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.dialer.compat;
-
-import android.content.Context;
-import android.net.Uri;
-
-public class BlockedNumbersSdkCompat {
-
- public static final Uri CONTENT_URI = null;
-
- public static final String _ID = null;
-
- public static final String COLUMN_ORIGINAL_NUMBER = null;
-
- public static final String E164_NUMBER = null;
-
- public static boolean canCurrentUserBlockNumbers(Context context) {
- return false;
- }
-}
diff --git a/src-pre-N/com/android/dialer/compat/CallsSdkCompat.java b/src-pre-N/com/android/dialer/compat/CallsSdkCompat.java
deleted file mode 100644
index 60d3ca14d..000000000
--- a/src-pre-N/com/android/dialer/compat/CallsSdkCompat.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.dialer.compat;
-
-import android.support.annotation.Nullable;
-
-public class CallsSdkCompat {
-
- @Nullable public static final String POST_DIAL_DIGITS = null;
- @Nullable public static final String VIA_NUMBER = null;
-}
diff --git a/src-pre-N/com/android/dialer/compat/UserManagerSdkCompat.java b/src-pre-N/com/android/dialer/compat/UserManagerSdkCompat.java
deleted file mode 100644
index c79ac2f91..000000000
--- a/src-pre-N/com/android/dialer/compat/UserManagerSdkCompat.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.dialer.compat;
-
-import android.content.Context;
-import android.util.Log;
-
-/**
- * UserManagerCompat respecting Sdk requirements
- */
-public class UserManagerSdkCompat {
-
- /**
- * @return {@code true}
- */
- public static boolean isUserUnlocked(Context context) {
- Log.wtf("UserManagerSdkCompat", "Not implemented");
- return true;
- }
-
-}
diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java
deleted file mode 100644
index 94c2f0018..000000000
--- a/src/com/android/dialer/CallDetailActivity.java
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- * Copyright (C) 2009 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.dialer;
-
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.support.v7.app.AppCompatActivity;
-import android.text.BidiFormatter;
-import android.text.TextDirectionHeuristics;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.ListView;
-import android.widget.QuickContactBadge;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.ClipboardUtils;
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.interactions.TouchPointManager;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.contacts.common.util.UriUtils;
-import com.android.dialer.calllog.CallDetailHistoryAdapter;
-import com.android.dialer.calllog.CallLogAsyncTaskUtil;
-import com.android.dialer.calllog.CallLogAsyncTaskUtil.CallLogAsyncTaskListener;
-import com.android.dialer.calllog.CallTypeHelper;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.calllog.PhoneAccountUtils;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener;
-import com.android.dialer.filterednumber.BlockNumberDialogFragment;
-import com.android.dialer.filterednumber.FilteredNumbersUtil;
-import com.android.dialer.logging.InteractionEvent;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.IntentUtil.CallIntentBuilder;
-import com.android.dialer.util.PhoneNumberUtil;
-import com.android.dialer.util.TelecomUtil;
-import com.android.incallui.Call.LogState;
-
-/**
- * Displays the details of a specific call log entry.
- * <p>
- * This activity can be either started with the URI of a single call log entry, or with the
- * {@link #EXTRA_CALL_LOG_IDS} extra to specify a group of call log entries.
- */
-public class CallDetailActivity extends AppCompatActivity
- implements MenuItem.OnMenuItemClickListener, View.OnClickListener,
- BlockNumberDialogFragment.Callback {
- private static final String TAG = CallDetailActivity.class.getSimpleName();
-
- /** A long array extra containing ids of call log entries to display. */
- public static final String EXTRA_CALL_LOG_IDS = "EXTRA_CALL_LOG_IDS";
- /** If we are started with a voicemail, we'll find the uri to play with this extra. */
- public static final String EXTRA_VOICEMAIL_URI = "EXTRA_VOICEMAIL_URI";
- /** If the activity was triggered from a notification. */
- public static final String EXTRA_FROM_NOTIFICATION = "EXTRA_FROM_NOTIFICATION";
-
- public static final String VOICEMAIL_FRAGMENT_TAG = "voicemail_fragment";
-
- private CallLogAsyncTaskListener mCallLogAsyncTaskListener = new CallLogAsyncTaskListener() {
- @Override
- public void onDeleteCall() {
- finish();
- }
-
- @Override
- public void onDeleteVoicemail() {
- finish();
- }
-
- @Override
- public void onGetCallDetails(PhoneCallDetails[] details) {
- if (details == null) {
- // Somewhere went wrong: we're going to bail out and show error to users.
- Toast.makeText(mContext, R.string.toast_call_detail_error,
- Toast.LENGTH_SHORT).show();
- finish();
- return;
- }
-
- // All calls are from the same number and same contact, so pick the first detail.
- mDetails = details[0];
- mNumber = TextUtils.isEmpty(mDetails.number) ? null : mDetails.number.toString();
- mPostDialDigits = TextUtils.isEmpty(mDetails.postDialDigits)
- ? "" : mDetails.postDialDigits;
- mDisplayNumber = mDetails.displayNumber;
-
- final CharSequence callLocationOrType = getNumberTypeOrLocation(mDetails);
-
- final CharSequence displayNumber;
- if (!TextUtils.isEmpty(mDetails.postDialDigits)) {
- displayNumber = mDetails.number + mDetails.postDialDigits;
- } else {
- displayNumber = mDetails.displayNumber;
- }
-
- final String displayNumberStr = mBidiFormatter.unicodeWrap(
- displayNumber.toString(), TextDirectionHeuristics.LTR);
-
- mDetails.nameDisplayOrder = mContactsPreferences.getDisplayOrder();
-
- if (!TextUtils.isEmpty(mDetails.getPreferredName())) {
- mCallerName.setText(mDetails.getPreferredName());
- mCallerNumber.setText(callLocationOrType + " " + displayNumberStr);
- } else {
- mCallerName.setText(displayNumberStr);
- if (!TextUtils.isEmpty(callLocationOrType)) {
- mCallerNumber.setText(callLocationOrType);
- mCallerNumber.setVisibility(View.VISIBLE);
- } else {
- mCallerNumber.setVisibility(View.GONE);
- }
- }
-
- CharSequence accountLabel = PhoneAccountUtils.getAccountLabel(mContext,
- mDetails.accountHandle);
- CharSequence accountContentDescription =
- PhoneCallDetails.createAccountLabelDescription(mResources, mDetails.viaNumber,
- accountLabel);
- if (!TextUtils.isEmpty(mDetails.viaNumber)) {
- if (!TextUtils.isEmpty(accountLabel)) {
- accountLabel = mResources.getString(R.string.call_log_via_number_phone_account,
- accountLabel, mDetails.viaNumber);
- } else {
- accountLabel = mResources.getString(R.string.call_log_via_number,
- mDetails.viaNumber);
- }
- }
- if (!TextUtils.isEmpty(accountLabel)) {
- mAccountLabel.setText(accountLabel);
- mAccountLabel.setContentDescription(accountContentDescription);
- mAccountLabel.setVisibility(View.VISIBLE);
- } else {
- mAccountLabel.setVisibility(View.GONE);
- }
-
- final boolean canPlaceCallsTo =
- PhoneNumberUtil.canPlaceCallsTo(mNumber, mDetails.numberPresentation);
- mCallButton.setVisibility(canPlaceCallsTo ? View.VISIBLE : View.GONE);
- mCopyNumberActionItem.setVisibility(canPlaceCallsTo ? View.VISIBLE : View.GONE);
-
- updateBlockActionItemVisibility(canPlaceCallsTo ? View.VISIBLE : View.GONE);
-
- final boolean isSipNumber = PhoneNumberUtil.isSipNumber(mNumber);
- final boolean isVoicemailNumber =
- PhoneNumberUtil.isVoicemailNumber(mContext, mDetails.accountHandle, mNumber);
- final boolean showEditNumberBeforeCallAction =
- canPlaceCallsTo && !isSipNumber && !isVoicemailNumber;
- mEditBeforeCallActionItem.setVisibility(
- showEditNumberBeforeCallAction ? View.VISIBLE : View.GONE);
-
- final boolean showReportAction = mContactInfoHelper.canReportAsInvalid(
- mDetails.sourceType, mDetails.objectId);
- mReportActionItem.setVisibility(
- showReportAction ? View.VISIBLE : View.GONE);
-
- invalidateOptionsMenu();
-
- mHistoryList.setAdapter(
- new CallDetailHistoryAdapter(mContext, mInflater, mCallTypeHelper, details));
-
- updateFilteredNumberChanges();
- updateContactPhoto();
-
- findViewById(R.id.call_detail).setVisibility(View.VISIBLE);
- }
-
- /**
- * Determines the location geocode text for a call, or the phone number type
- * (if available).
- *
- * @param details The call details.
- * @return The phone number type or location.
- */
- private CharSequence getNumberTypeOrLocation(PhoneCallDetails details) {
- if (!TextUtils.isEmpty(details.namePrimary)) {
- return Phone.getTypeLabel(mResources, details.numberType,
- details.numberLabel);
- } else {
- return details.geocode;
- }
- }
- };
-
- private Context mContext;
- private ContactInfoHelper mContactInfoHelper;
- private ContactsPreferences mContactsPreferences;
- private CallTypeHelper mCallTypeHelper;
- private ContactPhotoManager mContactPhotoManager;
- private FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
- private BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
- private LayoutInflater mInflater;
- private Resources mResources;
-
- private PhoneCallDetails mDetails;
- protected String mNumber;
- private Uri mVoicemailUri;
- private String mPostDialDigits = "";
- private String mDisplayNumber;
-
- private ListView mHistoryList;
- private QuickContactBadge mQuickContactBadge;
- private TextView mCallerName;
- private TextView mCallerNumber;
- private TextView mAccountLabel;
- private View mCallButton;
-
- private TextView mBlockNumberActionItem;
- private View mEditBeforeCallActionItem;
- private View mReportActionItem;
- private View mCopyNumberActionItem;
-
- private Integer mBlockedNumberId;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- mContext = this;
- mResources = getResources();
- mContactInfoHelper = new ContactInfoHelper(this, GeoUtil.getCurrentCountryIso(this));
- mContactsPreferences = new ContactsPreferences(mContext);
- mCallTypeHelper = new CallTypeHelper(getResources());
- mFilteredNumberAsyncQueryHandler =
- new FilteredNumberAsyncQueryHandler(getContentResolver());
-
- mVoicemailUri = getIntent().getParcelableExtra(EXTRA_VOICEMAIL_URI);
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-
- setContentView(R.layout.call_detail);
- mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
-
- mHistoryList = (ListView) findViewById(R.id.history);
- mHistoryList.addHeaderView(mInflater.inflate(R.layout.call_detail_header, null));
- mHistoryList.addFooterView(
- mInflater.inflate(R.layout.call_detail_footer, null), null, false);
-
- mQuickContactBadge = (QuickContactBadge) findViewById(R.id.quick_contact_photo);
- mQuickContactBadge.setOverlay(null);
- if (CompatUtils.hasPrioritizedMimeType()) {
- mQuickContactBadge.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE);
- }
- mCallerName = (TextView) findViewById(R.id.caller_name);
- mCallerNumber = (TextView) findViewById(R.id.caller_number);
- mAccountLabel = (TextView) findViewById(R.id.phone_account_label);
- mContactPhotoManager = ContactPhotoManager.getInstance(this);
-
- mCallButton = findViewById(R.id.call_back_button);
- mCallButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (TextUtils.isEmpty(mNumber)) {
- return;
- }
- mContext.startActivity(
- new CallIntentBuilder(getDialableNumber())
- .setCallInitiationType(LogState.INITIATION_CALL_DETAILS)
- .build());
- }
- });
-
-
- mBlockNumberActionItem = (TextView) findViewById(R.id.call_detail_action_block);
- updateBlockActionItemVisibility(View.VISIBLE);
- mBlockNumberActionItem.setOnClickListener(this);
- mEditBeforeCallActionItem = findViewById(R.id.call_detail_action_edit_before_call);
- mEditBeforeCallActionItem.setOnClickListener(this);
- mReportActionItem = findViewById(R.id.call_detail_action_report);
- mReportActionItem.setOnClickListener(this);
-
- mCopyNumberActionItem = findViewById(R.id.call_detail_action_copy);
- mCopyNumberActionItem.setOnClickListener(this);
-
- if (getIntent().getBooleanExtra(EXTRA_FROM_NOTIFICATION, false)) {
- closeSystemDialogs();
- }
- }
-
- private void updateBlockActionItemVisibility(int visibility) {
- if (!FilteredNumberCompat.canAttemptBlockOperations(mContext)) {
- visibility = View.GONE;
- }
- mBlockNumberActionItem.setVisibility(visibility);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
- getCallDetails();
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- TouchPointManager.getInstance().setPoint((int) ev.getRawX(), (int) ev.getRawY());
- }
- return super.dispatchTouchEvent(ev);
- }
-
- public void getCallDetails() {
- CallLogAsyncTaskUtil.getCallDetails(this, getCallLogEntryUris(), mCallLogAsyncTaskListener);
- }
-
- /**
- * Returns the list of URIs to show.
- * <p>
- * There are two ways the URIs can be provided to the activity: as the data on the intent, or as
- * a list of ids in the call log added as an extra on the URI.
- * <p>
- * If both are available, the data on the intent takes precedence.
- */
- private Uri[] getCallLogEntryUris() {
- final Uri uri = getIntent().getData();
- if (uri != null) {
- // If there is a data on the intent, it takes precedence over the extra.
- return new Uri[]{ uri };
- }
- final long[] ids = getIntent().getLongArrayExtra(EXTRA_CALL_LOG_IDS);
- final int numIds = ids == null ? 0 : ids.length;
- final Uri[] uris = new Uri[numIds];
- for (int index = 0; index < numIds; ++index) {
- uris[index] = ContentUris.withAppendedId(
- TelecomUtil.getCallLogUri(CallDetailActivity.this), ids[index]);
- }
- return uris;
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- final MenuItem deleteMenuItem = menu.add(
- Menu.NONE,
- R.id.call_detail_delete_menu_item,
- Menu.NONE,
- R.string.call_details_delete);
- deleteMenuItem.setIcon(R.drawable.ic_delete_24dp);
- deleteMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- deleteMenuItem.setOnMenuItemClickListener(this);
-
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- if (item.getItemId() == R.id.call_detail_delete_menu_item) {
- if (hasVoicemail()) {
- CallLogAsyncTaskUtil.deleteVoicemail(
- this, mVoicemailUri, mCallLogAsyncTaskListener);
- } else {
- final StringBuilder callIds = new StringBuilder();
- for (Uri callUri : getCallLogEntryUris()) {
- if (callIds.length() != 0) {
- callIds.append(",");
- }
- callIds.append(ContentUris.parseId(callUri));
- }
- CallLogAsyncTaskUtil.deleteCalls(
- this, callIds.toString(), mCallLogAsyncTaskListener);
- }
- }
- return true;
- }
-
- @Override
- public void onClick(View view) {
- int resId = view.getId();
- if (resId == R.id.call_detail_action_block) {
- FilteredNumberCompat
- .showBlockNumberDialogFlow(mContext.getContentResolver(), mBlockedNumberId,
- mNumber, mDetails.countryIso, mDisplayNumber, R.id.call_detail,
- getFragmentManager(), this);
- } else if (resId == R.id.call_detail_action_copy) {
- ClipboardUtils.copyText(mContext, null, mNumber, true);
- } else if (resId == R.id.call_detail_action_edit_before_call) {
- Intent dialIntent = new Intent(Intent.ACTION_DIAL,
- CallUtil.getCallUri(getDialableNumber()));
- DialerUtils.startActivityWithErrorToast(mContext, dialIntent);
- } else {
- Log.wtf(TAG, "Unexpected onClick event from " + view);
- }
- }
-
- @Override
- public void onFilterNumberSuccess() {
- Logger.logInteraction(InteractionEvent.BLOCK_NUMBER_CALL_DETAIL);
- updateFilteredNumberChanges();
- }
-
- @Override
- public void onUnfilterNumberSuccess() {
- Logger.logInteraction(InteractionEvent.UNBLOCK_NUMBER_CALL_DETAIL);
- updateFilteredNumberChanges();
- }
-
- @Override
- public void onChangeFilteredNumberUndo() {
- updateFilteredNumberChanges();
- }
-
- private void updateFilteredNumberChanges() {
- if (mDetails == null ||
- !FilteredNumbersUtil.canBlockNumber(this, mNumber, mDetails.countryIso)) {
- return;
- }
-
- final boolean success = mFilteredNumberAsyncQueryHandler.isBlockedNumber(
- new OnCheckBlockedListener() {
- @Override
- public void onCheckComplete(Integer id) {
- mBlockedNumberId = id;
- updateBlockActionItem();
- }
- }, mNumber, mDetails.countryIso);
-
- if (!success) {
- updateBlockActionItem();
- }
- }
-
- // Loads and displays the contact photo.
- private void updateContactPhoto() {
- if (mDetails == null) {
- return;
- }
-
- final boolean isVoicemailNumber =
- PhoneNumberUtil.isVoicemailNumber(mContext, mDetails.accountHandle, mNumber);
- final boolean isBusiness = mContactInfoHelper.isBusiness(mDetails.sourceType);
- int contactType = ContactPhotoManager.TYPE_DEFAULT;
- if (isVoicemailNumber) {
- contactType = ContactPhotoManager.TYPE_VOICEMAIL;
- } else if (isBusiness) {
- contactType = ContactPhotoManager.TYPE_BUSINESS;
- }
-
- final String displayName = TextUtils.isEmpty(mDetails.namePrimary)
- ? mDetails.displayNumber : mDetails.namePrimary.toString();
- final String lookupKey = mDetails.contactUri == null
- ? null : UriUtils.getLookupKeyFromUri(mDetails.contactUri);
-
- final DefaultImageRequest request =
- new DefaultImageRequest(displayName, lookupKey, contactType, true /* isCircular */);
-
- mQuickContactBadge.assignContactUri(mDetails.contactUri);
- mQuickContactBadge.setContentDescription(
- mResources.getString(R.string.description_contact_details, displayName));
-
- mContactPhotoManager.loadDirectoryPhoto(mQuickContactBadge, mDetails.photoUri,
- false /* darkTheme */, true /* isCircular */, request);
- }
-
- private void updateBlockActionItem() {
- if (mBlockedNumberId == null) {
- mBlockNumberActionItem.setText(R.string.action_block_number);
- mBlockNumberActionItem.setCompoundDrawablesRelativeWithIntrinsicBounds(
- R.drawable.ic_call_detail_block, 0, 0, 0);
- } else {
- mBlockNumberActionItem.setText(R.string.action_unblock_number);
- mBlockNumberActionItem.setCompoundDrawablesRelativeWithIntrinsicBounds(
- R.drawable.ic_call_detail_unblock, 0, 0, 0);
- }
- }
-
- private void closeSystemDialogs() {
- sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
- }
-
- private String getDialableNumber() {
- return mNumber + mPostDialDigits;
- }
-
- @NeededForTesting
- public boolean hasVoicemail() {
- return mVoicemailUri != null;
- }
-}
diff --git a/src/com/android/dialer/DialerApplication.java b/src/com/android/dialer/DialerApplication.java
deleted file mode 100644
index 1a0497bb9..000000000
--- a/src/com/android/dialer/DialerApplication.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer;
-
-import android.app.Application;
-import android.content.Context;
-import android.os.Trace;
-import android.preference.PreferenceManager;
-import android.support.annotation.Nullable;
-
-import com.android.contacts.common.extensions.ExtensionsFactory;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.filterednumber.BlockedNumbersAutoMigrator;
-
-public class DialerApplication extends Application {
-
- private static final String TAG = "DialerApplication";
-
- private static Context sContext;
-
- @Override
- public void onCreate() {
- sContext = this;
- Trace.beginSection(TAG + " onCreate");
- super.onCreate();
- Trace.beginSection(TAG + " ExtensionsFactory initialization");
- ExtensionsFactory.init(getApplicationContext());
- Trace.endSection();
- new BlockedNumbersAutoMigrator(PreferenceManager.getDefaultSharedPreferences(this),
- new FilteredNumberAsyncQueryHandler(getContentResolver())).autoMigrate();
- Trace.endSection();
- }
-
- @Nullable
- public static Context getContext() {
- return sContext;
- }
-
- @NeededForTesting
- public static void setContextForTest(Context context) {
- sContext = context;
- }
-}
diff --git a/src/com/android/dialer/DialerBackupAgent.java b/src/com/android/dialer/DialerBackupAgent.java
deleted file mode 100644
index 928be029c..000000000
--- a/src/com/android/dialer/DialerBackupAgent.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer;
-
-import android.app.backup.BackupAgentHelper;
-import android.app.backup.BackupDataInput;
-import android.app.backup.SharedPreferencesBackupHelper;
-import android.content.Context;
-
-/**
- * The Dialer backup agent backs up the shared preferences settings of the
- * Dialer App. Right now it backs up the whole shared preference file. This
- * can be modified in the future to accommodate partical backup.
- */
-public class DialerBackupAgent extends BackupAgentHelper
-{
- private static final String SHARED_KEY = "shared_pref";
-
- @Override
- public void onCreate() {
- addHelper(SHARED_KEY, new SharedPreferencesBackupHelper(this,
- DialtactsActivity.SHARED_PREFS_NAME));
- }
-}
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
deleted file mode 100644
index 441501cfd..000000000
--- a/src/com/android/dialer/DialtactsActivity.java
+++ /dev/null
@@ -1,1413 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer;
-
-import android.app.Fragment;
-import android.app.FragmentTransaction;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Trace;
-import android.provider.CallLog.Calls;
-import android.speech.RecognizerIntent;
-import android.support.design.widget.CoordinatorLayout;
-import android.support.v4.view.ViewPager;
-import android.support.v7.app.ActionBar;
-import android.telecom.PhoneAccount;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.view.DragEvent;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnDragListener;
-import android.view.ViewTreeObserver;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.AbsListView.OnScrollListener;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.PopupMenu;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.contacts.common.dialog.ClearFrequentsDialog;
-import com.android.contacts.common.interactions.ImportExportDialogFragment;
-import com.android.contacts.common.interactions.TouchPointManager;
-import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.common.widget.FloatingActionButtonController;
-import com.android.dialer.calllog.CallLogActivity;
-import com.android.dialer.calllog.CallLogFragment;
-import com.android.dialer.database.DialerDatabaseHelper;
-import com.android.dialer.dialpad.DialpadFragment;
-import com.android.dialer.dialpad.SmartDialNameMatcher;
-import com.android.dialer.dialpad.SmartDialPrefix;
-import com.android.dialer.interactions.PhoneNumberInteraction;
-import com.android.dialer.list.DragDropController;
-import com.android.dialer.list.ListsFragment;
-import com.android.dialer.list.OnDragDropListener;
-import com.android.dialer.list.OnListFragmentScrolledListener;
-import com.android.dialer.list.PhoneFavoriteSquareTileView;
-import com.android.dialer.list.RegularSearchFragment;
-import com.android.dialer.list.SearchFragment;
-import com.android.dialer.list.SmartDialSearchFragment;
-import com.android.dialer.list.SpeedDialFragment;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.ScreenEvent;
-import com.android.dialer.settings.DialerSettingsActivity;
-import com.android.dialer.util.Assert;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.IntentUtil;
-import com.android.dialer.util.IntentUtil.CallIntentBuilder;
-import com.android.dialer.util.TelecomUtil;
-import com.android.dialer.voicemail.VoicemailArchiveActivity;
-import com.android.dialer.widget.ActionBarController;
-import com.android.dialer.widget.SearchEditTextLayout;
-import com.android.dialerbind.DatabaseHelperManager;
-import com.android.dialerbind.ObjectFactory;
-import com.android.phone.common.animation.AnimUtils;
-import com.android.phone.common.animation.AnimationListenerAdapter;
-import com.google.common.annotations.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * The dialer tab's title is 'phone', a more common name (see strings.xml).
- */
-public class DialtactsActivity extends TransactionSafeActivity implements View.OnClickListener,
- DialpadFragment.OnDialpadQueryChangedListener,
- OnListFragmentScrolledListener,
- CallLogFragment.HostInterface,
- DialpadFragment.HostInterface,
- ListsFragment.HostInterface,
- SpeedDialFragment.HostInterface,
- SearchFragment.HostInterface,
- OnDragDropListener,
- OnPhoneNumberPickerActionListener,
- PopupMenu.OnMenuItemClickListener,
- ViewPager.OnPageChangeListener,
- ActionBarController.ActivityUi {
- private static final String TAG = "DialtactsActivity";
-
- public static final boolean DEBUG = false;
-
- public static final String SHARED_PREFS_NAME = "com.android.dialer_preferences";
-
- private static final String KEY_IN_REGULAR_SEARCH_UI = "in_regular_search_ui";
- private static final String KEY_IN_DIALPAD_SEARCH_UI = "in_dialpad_search_ui";
- private static final String KEY_SEARCH_QUERY = "search_query";
- private static final String KEY_FIRST_LAUNCH = "first_launch";
- private static final String KEY_IS_DIALPAD_SHOWN = "is_dialpad_shown";
-
- @VisibleForTesting
- public static final String TAG_DIALPAD_FRAGMENT = "dialpad";
- private static final String TAG_REGULAR_SEARCH_FRAGMENT = "search";
- private static final String TAG_SMARTDIAL_SEARCH_FRAGMENT = "smartdial";
- private static final String TAG_FAVORITES_FRAGMENT = "favorites";
-
- /**
- * Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}.
- */
- private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
- public static final String EXTRA_SHOW_TAB = "EXTRA_SHOW_TAB";
-
- private static final int ACTIVITY_REQUEST_CODE_VOICE_SEARCH = 1;
-
- private static final int FAB_SCALE_IN_DELAY_MS = 300;
-
- private CoordinatorLayout mParentLayout;
-
- /**
- * Fragment containing the dialpad that slides into view
- */
- protected DialpadFragment mDialpadFragment;
-
- /**
- * Fragment for searching phone numbers using the alphanumeric keyboard.
- */
- private RegularSearchFragment mRegularSearchFragment;
-
- /**
- * Fragment for searching phone numbers using the dialpad.
- */
- private SmartDialSearchFragment mSmartDialSearchFragment;
-
- /**
- * Animation that slides in.
- */
- private Animation mSlideIn;
-
- /**
- * Animation that slides out.
- */
- private Animation mSlideOut;
-
- AnimationListenerAdapter mSlideInListener = new AnimationListenerAdapter() {
- @Override
- public void onAnimationEnd(Animation animation) {
- maybeEnterSearchUi();
- }
- };
-
- /**
- * Listener for after slide out animation completes on dialer fragment.
- */
- AnimationListenerAdapter mSlideOutListener = new AnimationListenerAdapter() {
- @Override
- public void onAnimationEnd(Animation animation) {
- commitDialpadFragmentHide();
- }
- };
-
- /**
- * Fragment containing the speed dial list, call history list, and all contacts list.
- */
- private ListsFragment mListsFragment;
-
- /**
- * Tracks whether onSaveInstanceState has been called. If true, no fragment transactions can
- * be commited.
- */
- private boolean mStateSaved;
- private boolean mIsRestarting;
- private boolean mInDialpadSearch;
- private boolean mInRegularSearch;
- private boolean mClearSearchOnPause;
- private boolean mIsDialpadShown;
- private boolean mShowDialpadOnResume;
-
- /**
- * Whether or not the device is in landscape orientation.
- */
- private boolean mIsLandscape;
-
- /**
- * True if the dialpad is only temporarily showing due to being in call
- */
- private boolean mInCallDialpadUp;
-
- /**
- * True when this activity has been launched for the first time.
- */
- private boolean mFirstLaunch;
-
- /**
- * Search query to be applied to the SearchView in the ActionBar once
- * onCreateOptionsMenu has been called.
- */
- private String mPendingSearchViewQuery;
-
- private PopupMenu mOverflowMenu;
- private EditText mSearchView;
- private View mVoiceSearchButton;
-
- private String mSearchQuery;
- private String mDialpadQuery;
-
- private DialerDatabaseHelper mDialerDatabaseHelper;
- private DragDropController mDragDropController;
- private ActionBarController mActionBarController;
-
- private FloatingActionButtonController mFloatingActionButtonController;
-
- private int mActionBarHeight;
- private int mPreviouslySelectedTabIndex;
-
- /**
- * The text returned from a voice search query. Set in {@link #onActivityResult} and used in
- * {@link #onResume()} to populate the search box.
- */
- private String mVoiceSearchQuery;
-
- protected class OptionsPopupMenu extends PopupMenu {
- public OptionsPopupMenu(Context context, View anchor) {
- super(context, anchor, Gravity.END);
- }
-
- @Override
- public void show() {
- final boolean hasContactsPermission =
- PermissionsUtil.hasContactsPermissions(DialtactsActivity.this);
- final Menu menu = getMenu();
- final MenuItem clearFrequents = menu.findItem(R.id.menu_clear_frequents);
- clearFrequents.setVisible(mListsFragment != null &&
- mListsFragment.getSpeedDialFragment() != null &&
- mListsFragment.getSpeedDialFragment().hasFrequents() && hasContactsPermission);
-
- menu.findItem(R.id.menu_import_export).setVisible(hasContactsPermission);
- menu.findItem(R.id.menu_add_contact).setVisible(hasContactsPermission);
-
- menu.findItem(R.id.menu_history).setVisible(
- PermissionsUtil.hasPhonePermissions(DialtactsActivity.this));
- super.show();
- }
- }
-
- /**
- * Listener that listens to drag events and sends their x and y coordinates to a
- * {@link DragDropController}.
- */
- private class LayoutOnDragListener implements OnDragListener {
- @Override
- public boolean onDrag(View v, DragEvent event) {
- if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) {
- mDragDropController.handleDragHovered(v, (int) event.getX(), (int) event.getY());
- }
- return true;
- }
- }
-
- /**
- * Listener used to send search queries to the phone search fragment.
- */
- private final TextWatcher mPhoneSearchQueryTextListener = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- final String newText = s.toString();
- if (newText.equals(mSearchQuery)) {
- // If the query hasn't changed (perhaps due to activity being destroyed
- // and restored, or user launching the same DIAL intent twice), then there is
- // no need to do anything here.
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "onTextChange for mSearchView called with new query: " + newText);
- Log.d(TAG, "Previous Query: " + mSearchQuery);
- }
- mSearchQuery = newText;
-
- // Show search fragment only when the query string is changed to non-empty text.
- if (!TextUtils.isEmpty(newText)) {
- // Call enterSearchUi only if we are switching search modes, or showing a search
- // fragment for the first time.
- final boolean sameSearchMode = (mIsDialpadShown && mInDialpadSearch) ||
- (!mIsDialpadShown && mInRegularSearch);
- if (!sameSearchMode) {
- enterSearchUi(mIsDialpadShown, mSearchQuery, true /* animate */);
- }
- }
-
- if (mSmartDialSearchFragment != null && mSmartDialSearchFragment.isVisible()) {
- mSmartDialSearchFragment.setQueryString(mSearchQuery, false /* delaySelection */);
- } else if (mRegularSearchFragment != null && mRegularSearchFragment.isVisible()) {
- mRegularSearchFragment.setQueryString(mSearchQuery, false /* delaySelection */);
- }
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- }
- };
-
-
- /**
- * Open the search UI when the user clicks on the search box.
- */
- private final View.OnClickListener mSearchViewOnClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (!isInSearchUi()) {
- mActionBarController.onSearchBoxTapped();
- enterSearchUi(false /* smartDialSearch */, mSearchView.getText().toString(),
- true /* animate */);
- }
- }
- };
-
- /**
- * Handles the user closing the soft keyboard.
- */
- private final View.OnKeyListener mSearchEditTextLayoutListener = new View.OnKeyListener() {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
- if (TextUtils.isEmpty(mSearchView.getText().toString())) {
- // If the search term is empty, close the search UI.
- maybeExitSearchUi();
- } else {
- // If the search term is not empty, show the dialpad fab.
- showFabInSearchUi();
- }
- }
- return false;
- }
- };
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- TouchPointManager.getInstance().setPoint((int) ev.getRawX(), (int) ev.getRawY());
- }
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- Trace.beginSection(TAG + " onCreate");
- super.onCreate(savedInstanceState);
-
- mFirstLaunch = true;
-
- final Resources resources = getResources();
- mActionBarHeight = resources.getDimensionPixelSize(R.dimen.action_bar_height_large);
-
- Trace.beginSection(TAG + " setContentView");
- setContentView(R.layout.dialtacts_activity);
- Trace.endSection();
- getWindow().setBackgroundDrawable(null);
-
- Trace.beginSection(TAG + " setup Views");
- final ActionBar actionBar = getSupportActionBar();
- actionBar.setCustomView(R.layout.search_edittext);
- actionBar.setDisplayShowCustomEnabled(true);
- actionBar.setBackgroundDrawable(null);
-
- SearchEditTextLayout searchEditTextLayout = (SearchEditTextLayout) actionBar
- .getCustomView().findViewById(R.id.search_view_container);
- searchEditTextLayout.setPreImeKeyListener(mSearchEditTextLayoutListener);
-
- mActionBarController = new ActionBarController(this, searchEditTextLayout);
-
- mSearchView = (EditText) searchEditTextLayout.findViewById(R.id.search_view);
- mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
- mVoiceSearchButton = searchEditTextLayout.findViewById(R.id.voice_search_button);
- searchEditTextLayout.findViewById(R.id.search_magnifying_glass)
- .setOnClickListener(mSearchViewOnClickListener);
- searchEditTextLayout.findViewById(R.id.search_box_start_search)
- .setOnClickListener(mSearchViewOnClickListener);
- searchEditTextLayout.setOnClickListener(mSearchViewOnClickListener);
- searchEditTextLayout.setCallback(new SearchEditTextLayout.Callback() {
- @Override
- public void onBackButtonClicked() {
- onBackPressed();
- }
-
- @Override
- public void onSearchViewClicked() {
- // Hide FAB, as the keyboard is shown.
- mFloatingActionButtonController.scaleOut();
- }
- });
-
- mIsLandscape = getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
- mPreviouslySelectedTabIndex = ListsFragment.TAB_INDEX_SPEED_DIAL;
- final View floatingActionButtonContainer = findViewById(
- R.id.floating_action_button_container);
- ImageButton floatingActionButton = (ImageButton) findViewById(R.id.floating_action_button);
- floatingActionButton.setOnClickListener(this);
- mFloatingActionButtonController = new FloatingActionButtonController(this,
- floatingActionButtonContainer, floatingActionButton);
-
- ImageButton optionsMenuButton =
- (ImageButton) searchEditTextLayout.findViewById(R.id.dialtacts_options_menu_button);
- optionsMenuButton.setOnClickListener(this);
- mOverflowMenu = buildOptionsMenu(searchEditTextLayout);
- optionsMenuButton.setOnTouchListener(mOverflowMenu.getDragToOpenListener());
-
- // Add the favorites fragment but only if savedInstanceState is null. Otherwise the
- // fragment manager is responsible for recreating it.
- if (savedInstanceState == null) {
- getFragmentManager().beginTransaction()
- .add(R.id.dialtacts_frame, new ListsFragment(), TAG_FAVORITES_FRAGMENT)
- .commit();
- } else {
- mSearchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY);
- mInRegularSearch = savedInstanceState.getBoolean(KEY_IN_REGULAR_SEARCH_UI);
- mInDialpadSearch = savedInstanceState.getBoolean(KEY_IN_DIALPAD_SEARCH_UI);
- mFirstLaunch = savedInstanceState.getBoolean(KEY_FIRST_LAUNCH);
- mShowDialpadOnResume = savedInstanceState.getBoolean(KEY_IS_DIALPAD_SHOWN);
- mActionBarController.restoreInstanceState(savedInstanceState);
- }
-
- final boolean isLayoutRtl = DialerUtils.isRtl();
- if (mIsLandscape) {
- mSlideIn = AnimationUtils.loadAnimation(this,
- isLayoutRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right);
- mSlideOut = AnimationUtils.loadAnimation(this,
- isLayoutRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right);
- } else {
- mSlideIn = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_in_bottom);
- mSlideOut = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_out_bottom);
- }
-
- mSlideIn.setInterpolator(AnimUtils.EASE_IN);
- mSlideOut.setInterpolator(AnimUtils.EASE_OUT);
-
- mSlideIn.setAnimationListener(mSlideInListener);
- mSlideOut.setAnimationListener(mSlideOutListener);
-
- mParentLayout = (CoordinatorLayout) findViewById(R.id.dialtacts_mainlayout);
- mParentLayout.setOnDragListener(new LayoutOnDragListener());
- floatingActionButtonContainer.getViewTreeObserver().addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- final ViewTreeObserver observer =
- floatingActionButtonContainer.getViewTreeObserver();
- if (!observer.isAlive()) {
- return;
- }
- observer.removeOnGlobalLayoutListener(this);
- int screenWidth = mParentLayout.getWidth();
- mFloatingActionButtonController.setScreenWidth(screenWidth);
- mFloatingActionButtonController.align(
- getFabAlignment(), false /* animate */);
- }
- });
-
- Trace.endSection();
-
- Trace.beginSection(TAG + " initialize smart dialing");
- mDialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(this);
- SmartDialPrefix.initializeNanpSettings(this);
- Trace.endSection();
- Trace.endSection();
- }
-
- @Override
- protected void onResume() {
- Trace.beginSection(TAG + " onResume");
- super.onResume();
-
- mStateSaved = false;
- if (mFirstLaunch) {
- displayFragment(getIntent());
- } else if (!phoneIsInUse() && mInCallDialpadUp) {
- hideDialpadFragment(false, true);
- mInCallDialpadUp = false;
- } else if (mShowDialpadOnResume) {
- showDialpadFragment(false);
- mShowDialpadOnResume = false;
- }
-
- // If there was a voice query result returned in the {@link #onActivityResult} callback, it
- // will have been stashed in mVoiceSearchQuery since the search results fragment cannot be
- // shown until onResume has completed. Active the search UI and set the search term now.
- if (!TextUtils.isEmpty(mVoiceSearchQuery)) {
- mActionBarController.onSearchBoxTapped();
- mSearchView.setText(mVoiceSearchQuery);
- mVoiceSearchQuery = null;
- }
-
- mFirstLaunch = false;
-
- if (mIsRestarting) {
- // This is only called when the activity goes from resumed -> paused -> resumed, so it
- // will not cause an extra view to be sent out on rotation
- if (mIsDialpadShown) {
- Logger.logScreenView(ScreenEvent.DIALPAD, this);
- }
- mIsRestarting = false;
- }
-
- prepareVoiceSearchButton();
- mDialerDatabaseHelper.startSmartDialUpdateThread();
- mFloatingActionButtonController.align(getFabAlignment(), false /* animate */);
-
- if (Calls.CONTENT_TYPE.equals(getIntent().getType())) {
- // Externally specified extras take precedence to EXTRA_SHOW_TAB, which is only
- // used internally.
- final Bundle extras = getIntent().getExtras();
- if (extras != null
- && extras.getInt(Calls.EXTRA_CALL_TYPE_FILTER) == Calls.VOICEMAIL_TYPE) {
- mListsFragment.showTab(ListsFragment.TAB_INDEX_VOICEMAIL);
- } else {
- mListsFragment.showTab(ListsFragment.TAB_INDEX_HISTORY);
- }
- } else if (getIntent().hasExtra(EXTRA_SHOW_TAB)) {
- int index = getIntent().getIntExtra(EXTRA_SHOW_TAB, ListsFragment.TAB_INDEX_SPEED_DIAL);
- if (index < mListsFragment.getTabCount()) {
- mListsFragment.showTab(index);
- }
- }
-
- setSearchBoxHint();
-
- Trace.endSection();
- }
-
- @Override
- protected void onRestart() {
- super.onRestart();
- mIsRestarting = true;
- }
-
- @Override
- protected void onPause() {
- // Only clear missed calls if the pause was not triggered by an orientation change
- // (or any other confirguration change)
- if (!isChangingConfigurations()) {
- updateMissedCalls();
- }
- if (mClearSearchOnPause) {
- hideDialpadAndSearchUi();
- mClearSearchOnPause = false;
- }
- if (mSlideOut.hasStarted() && !mSlideOut.hasEnded()) {
- commitDialpadFragmentHide();
- }
- super.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putString(KEY_SEARCH_QUERY, mSearchQuery);
- outState.putBoolean(KEY_IN_REGULAR_SEARCH_UI, mInRegularSearch);
- outState.putBoolean(KEY_IN_DIALPAD_SEARCH_UI, mInDialpadSearch);
- outState.putBoolean(KEY_FIRST_LAUNCH, mFirstLaunch);
- outState.putBoolean(KEY_IS_DIALPAD_SHOWN, mIsDialpadShown);
- mActionBarController.saveInstanceState(outState);
- mStateSaved = true;
- }
-
- @Override
- public void onAttachFragment(Fragment fragment) {
- if (fragment instanceof DialpadFragment) {
- mDialpadFragment = (DialpadFragment) fragment;
- if (!mIsDialpadShown && !mShowDialpadOnResume) {
- final FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.hide(mDialpadFragment);
- transaction.commit();
- }
- } else if (fragment instanceof SmartDialSearchFragment) {
- mSmartDialSearchFragment = (SmartDialSearchFragment) fragment;
- mSmartDialSearchFragment.setOnPhoneNumberPickerActionListener(this);
- if (!TextUtils.isEmpty(mDialpadQuery)) {
- mSmartDialSearchFragment.setAddToContactNumber(mDialpadQuery);
- }
- } else if (fragment instanceof SearchFragment) {
- mRegularSearchFragment = (RegularSearchFragment) fragment;
- mRegularSearchFragment.setOnPhoneNumberPickerActionListener(this);
- } else if (fragment instanceof ListsFragment) {
- mListsFragment = (ListsFragment) fragment;
- mListsFragment.addOnPageChangeListener(this);
- }
- }
-
- protected void handleMenuSettings() {
- final Intent intent = new Intent(this, DialerSettingsActivity.class);
- startActivity(intent);
- }
-
- @Override
- public void onClick(View view) {
- int resId = view.getId();
- if (resId == R.id.floating_action_button) {
- if (mListsFragment.getCurrentTabIndex()
- == ListsFragment.TAB_INDEX_ALL_CONTACTS && !mInRegularSearch &&
- !mInDialpadSearch) {
- DialerUtils.startActivityWithErrorToast(
- this,
- IntentUtil.getNewContactIntent(),
- R.string.add_contact_not_available);
- } else if (!mIsDialpadShown) {
- mInCallDialpadUp = false;
- showDialpadFragment(true);
- }
- } else if (resId == R.id.voice_search_button) {
- try {
- startActivityForResult(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH),
- ACTIVITY_REQUEST_CODE_VOICE_SEARCH);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(DialtactsActivity.this, R.string.voice_search_not_available,
- Toast.LENGTH_SHORT).show();
- }
- } else if (resId == R.id.dialtacts_options_menu_button) {
- mOverflowMenu.show();
- } else {
- Log.wtf(TAG, "Unexpected onClick event from " + view);
- }
- }
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- if (!isSafeToCommitTransactions()) {
- return true;
- }
-
- int resId = item.getItemId();
- if (resId == R.id.menu_history) {// Use explicit CallLogActivity intent instead of ACTION_VIEW +
- // CONTENT_TYPE, so that we always open our call log from our dialer
- final Intent intent = new Intent(this, CallLogActivity.class);
- startActivity(intent);
- } else if (resId == R.id.menu_add_contact) {
- DialerUtils.startActivityWithErrorToast(
- this,
- IntentUtil.getNewContactIntent(),
- R.string.add_contact_not_available);
- } else if (resId == R.id.menu_import_export) {// We hard-code the "contactsAreAvailable" argument because doing it properly would
- // involve querying a {@link ProviderStatusLoader}, which we don't want to do right
- // now in Dialtacts for (potential) performance reasons. Compare with how it is
- // done in {@link PeopleActivity}.
- if (mListsFragment.getCurrentTabIndex() == ListsFragment.TAB_INDEX_SPEED_DIAL) {
- ImportExportDialogFragment.show(getFragmentManager(), true,
- DialtactsActivity.class, ImportExportDialogFragment.EXPORT_MODE_FAVORITES);
- } else {
- ImportExportDialogFragment.show(getFragmentManager(), true,
- DialtactsActivity.class, ImportExportDialogFragment.EXPORT_MODE_DEFAULT);
- }
- Logger.logScreenView(ScreenEvent.IMPORT_EXPORT_CONTACTS, this);
- return true;
- } else if (resId == R.id.menu_clear_frequents) {
- ClearFrequentsDialog.show(getFragmentManager());
- Logger.logScreenView(ScreenEvent.CLEAR_FREQUENTS, this);
- return true;
- } else if (resId == R.id.menu_call_settings) {
- handleMenuSettings();
- Logger.logScreenView(ScreenEvent.SETTINGS, this);
- return true;
- } else if (resId == R.id.menu_archive) {
- final Intent intent = new Intent(this, VoicemailArchiveActivity.class);
- startActivity(intent);
- return true;
- }
- return false;
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == ACTIVITY_REQUEST_CODE_VOICE_SEARCH) {
- if (resultCode == RESULT_OK) {
- final ArrayList<String> matches = data.getStringArrayListExtra(
- RecognizerIntent.EXTRA_RESULTS);
- if (matches.size() > 0) {
- final String match = matches.get(0);
- mVoiceSearchQuery = match;
- } else {
- Log.e(TAG, "Voice search - nothing heard");
- }
- } else {
- Log.e(TAG, "Voice search failed");
- }
- }
- super.onActivityResult(requestCode, resultCode, data);
- }
-
- /**
- * Update the number of unread voicemails (potentially other tabs) displayed next to the tab
- * icon.
- */
- public void updateTabUnreadCounts() {
- mListsFragment.updateTabUnreadCounts();
- }
-
- /**
- * Initiates a fragment transaction to show the dialpad fragment. Animations and other visual
- * updates are handled by a callback which is invoked after the dialpad fragment is shown.
- * @see #onDialpadShown
- */
- private void showDialpadFragment(boolean animate) {
- if (mIsDialpadShown || mStateSaved) {
- return;
- }
- mIsDialpadShown = true;
-
- mListsFragment.setUserVisibleHint(false);
-
- final FragmentTransaction ft = getFragmentManager().beginTransaction();
- if (mDialpadFragment == null) {
- mDialpadFragment = new DialpadFragment();
- ft.add(R.id.dialtacts_container, mDialpadFragment, TAG_DIALPAD_FRAGMENT);
- } else {
- ft.show(mDialpadFragment);
- }
-
- mDialpadFragment.setAnimate(animate);
- Logger.logScreenView(ScreenEvent.DIALPAD, this);
- ft.commit();
-
- if (animate) {
- mFloatingActionButtonController.scaleOut();
- } else {
- mFloatingActionButtonController.setVisible(false);
- maybeEnterSearchUi();
- }
- mActionBarController.onDialpadUp();
-
- mListsFragment.getView().animate().alpha(0).withLayer();
-
- //adjust the title, so the user will know where we're at when the activity start/resumes.
- setTitle(R.string.launcherDialpadActivityLabel);
- }
-
- /**
- * Callback from child DialpadFragment when the dialpad is shown.
- */
- public void onDialpadShown() {
- Assert.assertNotNull(mDialpadFragment);
- if (mDialpadFragment.getAnimate()) {
- mDialpadFragment.getView().startAnimation(mSlideIn);
- } else {
- mDialpadFragment.setYFraction(0);
- }
-
- updateSearchFragmentPosition();
- }
-
- /**
- * Initiates animations and other visual updates to hide the dialpad. The fragment is hidden in
- * a callback after the hide animation ends.
- * @see #commitDialpadFragmentHide
- */
- public void hideDialpadFragment(boolean animate, boolean clearDialpad) {
- if (mDialpadFragment == null || mDialpadFragment.getView() == null) {
- return;
- }
- if (clearDialpad) {
- // Temporarily disable accessibility when we clear the dialpad, since it should be
- // invisible and should not announce anything.
- mDialpadFragment.getDigitsWidget().setImportantForAccessibility(
- View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- mDialpadFragment.clearDialpad();
- mDialpadFragment.getDigitsWidget().setImportantForAccessibility(
- View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
- }
- if (!mIsDialpadShown) {
- return;
- }
- mIsDialpadShown = false;
- mDialpadFragment.setAnimate(animate);
- mListsFragment.setUserVisibleHint(true);
- mListsFragment.sendScreenViewForCurrentPosition();
-
- updateSearchFragmentPosition();
-
- mFloatingActionButtonController.align(getFabAlignment(), animate);
- if (animate) {
- mDialpadFragment.getView().startAnimation(mSlideOut);
- } else {
- commitDialpadFragmentHide();
- }
-
- mActionBarController.onDialpadDown();
-
- if (isInSearchUi()) {
- if (TextUtils.isEmpty(mSearchQuery)) {
- exitSearchUi();
- }
- }
- //reset the title to normal.
- setTitle(R.string.launcherActivityLabel);
- }
-
- /**
- * Finishes hiding the dialpad fragment after any animations are completed.
- */
- private void commitDialpadFragmentHide() {
- if (!mStateSaved && mDialpadFragment != null && !mDialpadFragment.isHidden()) {
- final FragmentTransaction ft = getFragmentManager().beginTransaction();
- ft.hide(mDialpadFragment);
- ft.commit();
- }
- mFloatingActionButtonController.scaleIn(AnimUtils.NO_DELAY);
- }
-
- private void updateSearchFragmentPosition() {
- SearchFragment fragment = null;
- if (mSmartDialSearchFragment != null && mSmartDialSearchFragment.isVisible()) {
- fragment = mSmartDialSearchFragment;
- } else if (mRegularSearchFragment != null && mRegularSearchFragment.isVisible()) {
- fragment = mRegularSearchFragment;
- }
- if (fragment != null && fragment.isVisible()) {
- fragment.updatePosition(true /* animate */);
- }
- }
-
- @Override
- public boolean isInSearchUi() {
- return mInDialpadSearch || mInRegularSearch;
- }
-
- @Override
- public boolean hasSearchQuery() {
- return !TextUtils.isEmpty(mSearchQuery);
- }
-
- @Override
- public boolean shouldShowActionBar() {
- return mListsFragment.shouldShowActionBar();
- }
-
- private void setNotInSearchUi() {
- mInDialpadSearch = false;
- mInRegularSearch = false;
- }
-
- private void hideDialpadAndSearchUi() {
- if (mIsDialpadShown) {
- hideDialpadFragment(false, true);
- } else {
- exitSearchUi();
- }
- }
-
- private void prepareVoiceSearchButton() {
- final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
- if (canIntentBeHandled(voiceIntent)) {
- mVoiceSearchButton.setVisibility(View.VISIBLE);
- mVoiceSearchButton.setOnClickListener(this);
- } else {
- mVoiceSearchButton.setVisibility(View.GONE);
- }
- }
-
- public boolean isNearbyPlacesSearchEnabled() {
- return false;
- }
-
- protected int getSearchBoxHint () {
- return R.string.dialer_hint_find_contact;
- }
-
- /**
- * Sets the hint text for the contacts search box
- */
- private void setSearchBoxHint() {
- SearchEditTextLayout searchEditTextLayout = (SearchEditTextLayout) getSupportActionBar()
- .getCustomView().findViewById(R.id.search_view_container);
- ((TextView) searchEditTextLayout.findViewById(R.id.search_box_start_search))
- .setHint(getSearchBoxHint());
- }
-
- protected OptionsPopupMenu buildOptionsMenu(View invoker) {
- final OptionsPopupMenu popupMenu = new OptionsPopupMenu(this, invoker);
- popupMenu.inflate(R.menu.dialtacts_options);
- if (ObjectFactory.isVoicemailArchiveEnabled(this)) {
- popupMenu.getMenu().findItem(R.id.menu_archive).setVisible(true);
- }
- popupMenu.setOnMenuItemClickListener(this);
- return popupMenu;
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- if (mPendingSearchViewQuery != null) {
- mSearchView.setText(mPendingSearchViewQuery);
- mPendingSearchViewQuery = null;
- }
- if (mActionBarController != null) {
- mActionBarController.restoreActionBarOffset();
- }
- return false;
- }
-
- /**
- * Returns true if the intent is due to hitting the green send key (hardware call button:
- * KEYCODE_CALL) while in a call.
- *
- * @param intent the intent that launched this activity
- * @return true if the intent is due to hitting the green send key while in a call
- */
- private boolean isSendKeyWhileInCall(Intent intent) {
- // If there is a call in progress and the user launched the dialer by hitting the call
- // button, go straight to the in-call screen.
- final boolean callKey = Intent.ACTION_CALL_BUTTON.equals(intent.getAction());
-
- if (callKey) {
- TelecomUtil.showInCallScreen(this, false);
- return true;
- }
-
- return false;
- }
-
- /**
- * Sets the current tab based on the intent's request type
- *
- * @param intent Intent that contains information about which tab should be selected
- */
- private void displayFragment(Intent intent) {
- // If we got here by hitting send and we're in call forward along to the in-call activity
- if (isSendKeyWhileInCall(intent)) {
- finish();
- return;
- }
-
- final boolean showDialpadChooser = phoneIsInUse() && !DialpadFragment.isAddCallMode(intent);
- if (showDialpadChooser || (intent.getData() != null && isDialIntent(intent))) {
- showDialpadFragment(false);
- mDialpadFragment.setStartedFromNewIntent(true);
- if (showDialpadChooser && !mDialpadFragment.isVisible()) {
- mInCallDialpadUp = true;
- }
- }
- }
-
- @Override
- public void onNewIntent(Intent newIntent) {
- setIntent(newIntent);
-
- mStateSaved = false;
- displayFragment(newIntent);
-
- invalidateOptionsMenu();
- }
-
- /** Returns true if the given intent contains a phone number to populate the dialer with */
- private boolean isDialIntent(Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) {
- return true;
- }
- if (Intent.ACTION_VIEW.equals(action)) {
- final Uri data = intent.getData();
- if (data != null && PhoneAccount.SCHEME_TEL.equals(data.getScheme())) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Shows the search fragment
- */
- private void enterSearchUi(boolean smartDialSearch, String query, boolean animate) {
- if (mStateSaved || getFragmentManager().isDestroyed()) {
- // Weird race condition where fragment is doing work after the activity is destroyed
- // due to talkback being on (b/10209937). Just return since we can't do any
- // constructive here.
- return;
- }
-
- if (DEBUG) {
- Log.d(TAG, "Entering search UI - smart dial " + smartDialSearch);
- }
-
- final FragmentTransaction transaction = getFragmentManager().beginTransaction();
- if (mInDialpadSearch && mSmartDialSearchFragment != null) {
- transaction.remove(mSmartDialSearchFragment);
- } else if (mInRegularSearch && mRegularSearchFragment != null) {
- transaction.remove(mRegularSearchFragment);
- }
-
- final String tag;
- if (smartDialSearch) {
- tag = TAG_SMARTDIAL_SEARCH_FRAGMENT;
- } else {
- tag = TAG_REGULAR_SEARCH_FRAGMENT;
- }
- mInDialpadSearch = smartDialSearch;
- mInRegularSearch = !smartDialSearch;
-
- mFloatingActionButtonController.scaleOut();
-
- SearchFragment fragment = (SearchFragment) getFragmentManager().findFragmentByTag(tag);
- if (animate) {
- transaction.setCustomAnimations(android.R.animator.fade_in, 0);
- } else {
- transaction.setTransition(FragmentTransaction.TRANSIT_NONE);
- }
- if (fragment == null) {
- if (smartDialSearch) {
- fragment = new SmartDialSearchFragment();
- } else {
- fragment = ObjectFactory.newRegularSearchFragment();
- fragment.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- // Show the FAB when the user touches the lists fragment and the soft
- // keyboard is hidden.
- hideDialpadFragment(true, false);
- showFabInSearchUi();
- return false;
- }
- });
- }
- transaction.add(R.id.dialtacts_frame, fragment, tag);
- } else {
- transaction.show(fragment);
- }
- // DialtactsActivity will provide the options menu
- fragment.setHasOptionsMenu(false);
- fragment.setShowEmptyListForNullQuery(true);
- if (!smartDialSearch) {
- fragment.setQueryString(query, false /* delaySelection */);
- }
- transaction.commit();
-
- if (animate) {
- mListsFragment.getView().animate().alpha(0).withLayer();
- }
- mListsFragment.setUserVisibleHint(false);
-
- if (smartDialSearch) {
- Logger.logScreenView(ScreenEvent.SMART_DIAL_SEARCH, this);
- } else {
- Logger.logScreenView(ScreenEvent.REGULAR_SEARCH, this);
- }
- }
-
- /**
- * Hides the search fragment
- */
- private void exitSearchUi() {
- // See related bug in enterSearchUI();
- if (getFragmentManager().isDestroyed() || mStateSaved) {
- return;
- }
-
- mSearchView.setText(null);
-
- if (mDialpadFragment != null) {
- mDialpadFragment.clearDialpad();
- }
-
- setNotInSearchUi();
-
- // Restore the FAB for the lists fragment.
- if (getFabAlignment() != FloatingActionButtonController.ALIGN_END) {
- mFloatingActionButtonController.setVisible(false);
- }
- mFloatingActionButtonController.scaleIn(FAB_SCALE_IN_DELAY_MS);
- onPageScrolled(mListsFragment.getCurrentTabIndex(), 0 /* offset */, 0 /* pixelOffset */);
- onPageSelected(mListsFragment.getCurrentTabIndex());
-
- final FragmentTransaction transaction = getFragmentManager().beginTransaction();
- if (mSmartDialSearchFragment != null) {
- transaction.remove(mSmartDialSearchFragment);
- }
- if (mRegularSearchFragment != null) {
- transaction.remove(mRegularSearchFragment);
- }
- transaction.commit();
-
- mListsFragment.getView().animate().alpha(1).withLayer();
-
- if (mDialpadFragment == null || !mDialpadFragment.isVisible()) {
- // If the dialpad fragment wasn't previously visible, then send a screen view because
- // we are exiting regular search. Otherwise, the screen view will be sent by
- // {@link #hideDialpadFragment}.
- mListsFragment.sendScreenViewForCurrentPosition();
- mListsFragment.setUserVisibleHint(true);
- }
-
- mActionBarController.onSearchUiExited();
- }
-
- @Override
- public void onBackPressed() {
- if (mStateSaved) {
- return;
- }
- if (mIsDialpadShown) {
- if (TextUtils.isEmpty(mSearchQuery) ||
- (mSmartDialSearchFragment != null && mSmartDialSearchFragment.isVisible()
- && mSmartDialSearchFragment.getAdapter().getCount() == 0)) {
- exitSearchUi();
- }
- hideDialpadFragment(true, false);
- } else if (isInSearchUi()) {
- exitSearchUi();
- DialerUtils.hideInputMethod(mParentLayout);
- } else {
- super.onBackPressed();
- }
- }
-
- private void maybeEnterSearchUi() {
- if (!isInSearchUi()) {
- enterSearchUi(true /* isSmartDial */, mSearchQuery, false);
- }
- }
-
- /**
- * @return True if the search UI was exited, false otherwise
- */
- private boolean maybeExitSearchUi() {
- if (isInSearchUi() && TextUtils.isEmpty(mSearchQuery)) {
- exitSearchUi();
- DialerUtils.hideInputMethod(mParentLayout);
- return true;
- }
- return false;
- }
-
- private void showFabInSearchUi() {
- mFloatingActionButtonController.changeIcon(
- getResources().getDrawable(R.drawable.fab_ic_dial),
- getResources().getString(R.string.action_menu_dialpad_button));
- mFloatingActionButtonController.align(getFabAlignment(), false /* animate */);
- mFloatingActionButtonController.scaleIn(FAB_SCALE_IN_DELAY_MS);
- }
-
- @Override
- public void onDialpadQueryChanged(String query) {
- mDialpadQuery = query;
- if (mSmartDialSearchFragment != null) {
- mSmartDialSearchFragment.setAddToContactNumber(query);
- }
- final String normalizedQuery = SmartDialNameMatcher.normalizeNumber(query,
- SmartDialNameMatcher.LATIN_SMART_DIAL_MAP);
-
- if (!TextUtils.equals(mSearchView.getText(), normalizedQuery)) {
- if (DEBUG) {
- Log.d(TAG, "onDialpadQueryChanged - new query: " + query);
- }
- if (mDialpadFragment == null || !mDialpadFragment.isVisible()) {
- // This callback can happen if the dialpad fragment is recreated because of
- // activity destruction. In that case, don't update the search view because
- // that would bring the user back to the search fragment regardless of the
- // previous state of the application. Instead, just return here and let the
- // fragment manager correctly figure out whatever fragment was last displayed.
- if (!TextUtils.isEmpty(normalizedQuery)) {
- mPendingSearchViewQuery = normalizedQuery;
- }
- return;
- }
- mSearchView.setText(normalizedQuery);
- }
-
- try {
- if (mDialpadFragment != null && mDialpadFragment.isVisible()) {
- mDialpadFragment.process_quote_emergency_unquote(normalizedQuery);
- }
- } catch (Exception ignored) {
- // Skip any exceptions for this piece of code
- }
- }
-
- @Override
- public boolean onDialpadSpacerTouchWithEmptyQuery() {
- if (mInDialpadSearch && mSmartDialSearchFragment != null
- && !mSmartDialSearchFragment.isShowingPermissionRequest()) {
- hideDialpadFragment(true /* animate */, true /* clearDialpad */);
- return true;
- }
- return false;
- }
-
- @Override
- public void onListFragmentScrollStateChange(int scrollState) {
- if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
- hideDialpadFragment(true, false);
- DialerUtils.hideInputMethod(mParentLayout);
- }
- }
-
- @Override
- public void onListFragmentScroll(int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- // TODO: No-op for now. This should eventually show/hide the actionBar based on
- // interactions with the ListsFragments.
- }
-
- private boolean phoneIsInUse() {
- return TelecomUtil.isInCall(this);
- }
-
- private boolean canIntentBeHandled(Intent intent) {
- final PackageManager packageManager = getPackageManager();
- final List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent,
- PackageManager.MATCH_DEFAULT_ONLY);
- return resolveInfo != null && resolveInfo.size() > 0;
- }
-
- /**
- * Called when the user has long-pressed a contact tile to start a drag operation.
- */
- @Override
- public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {
- mListsFragment.showRemoveView(true);
- }
-
- @Override
- public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {
- }
-
- /**
- * Called when the user has released a contact tile after long-pressing it.
- */
- @Override
- public void onDragFinished(int x, int y) {
- mListsFragment.showRemoveView(false);
- }
-
- @Override
- public void onDroppedOnRemove() {}
-
- /**
- * Allows the SpeedDialFragment to attach the drag controller to mRemoveViewContainer
- * once it has been attached to the activity.
- */
- @Override
- public void setDragDropController(DragDropController dragController) {
- mDragDropController = dragController;
- mListsFragment.getRemoveView().setDragDropController(dragController);
- }
-
- /**
- * Implemented to satisfy {@link SpeedDialFragment.HostInterface}
- */
- @Override
- public void showAllContactsTab() {
- if (mListsFragment != null) {
- mListsFragment.showTab(ListsFragment.TAB_INDEX_ALL_CONTACTS);
- }
- }
-
- /**
- * Implemented to satisfy {@link CallLogFragment.HostInterface}
- */
- @Override
- public void showDialpad() {
- showDialpadFragment(true);
- }
-
- @Override
- public void onPickDataUri(Uri dataUri, boolean isVideoCall, int callInitiationType) {
- mClearSearchOnPause = true;
- PhoneNumberInteraction.startInteractionForPhoneCall(
- DialtactsActivity.this, dataUri, isVideoCall, callInitiationType);
- }
-
- @Override
- public void onPickPhoneNumber(String phoneNumber, boolean isVideoCall, int callInitiationType) {
- if (phoneNumber == null) {
- // Invalid phone number, but let the call go through so that InCallUI can show
- // an error message.
- phoneNumber = "";
- }
-
- final Intent intent = new CallIntentBuilder(phoneNumber)
- .setIsVideoCall(isVideoCall)
- .setCallInitiationType(callInitiationType)
- .build();
-
- DialerUtils.startActivityWithErrorToast(this, intent);
- mClearSearchOnPause = true;
- }
-
- @Override
- public void onShortcutIntentCreated(Intent intent) {
- Log.w(TAG, "Unsupported intent has come (" + intent + "). Ignoring.");
- }
-
- @Override
- public void onHomeInActionBarSelected() {
- exitSearchUi();
- }
-
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- int tabIndex = mListsFragment.getCurrentTabIndex();
-
- // Scroll the button from center to end when moving from the Speed Dial to Call History tab.
- // In RTL, scroll when the current tab is Call History instead, since the order of the tabs
- // is reversed and the ViewPager returns the left tab position during scroll.
- boolean isRtl = DialerUtils.isRtl();
- if (!isRtl && tabIndex == ListsFragment.TAB_INDEX_SPEED_DIAL && !mIsLandscape) {
- mFloatingActionButtonController.onPageScrolled(positionOffset);
- } else if (isRtl && tabIndex == ListsFragment.TAB_INDEX_HISTORY && !mIsLandscape) {
- mFloatingActionButtonController.onPageScrolled(1 - positionOffset);
- } else if (tabIndex != ListsFragment.TAB_INDEX_SPEED_DIAL) {
- mFloatingActionButtonController.onPageScrolled(1);
- }
- }
-
- @Override
- public void onPageSelected(int position) {
- updateMissedCalls();
- int tabIndex = mListsFragment.getCurrentTabIndex();
- mPreviouslySelectedTabIndex = tabIndex;
- if (tabIndex == ListsFragment.TAB_INDEX_ALL_CONTACTS &&
- !mInRegularSearch && !mInDialpadSearch) {
- mFloatingActionButtonController.changeIcon(
- getResources().getDrawable(R.drawable.ic_person_add_24dp),
- getResources().getString(R.string.search_shortcut_create_new_contact));
- } else {
- mFloatingActionButtonController.changeIcon(
- getResources().getDrawable(R.drawable.fab_ic_dial),
- getResources().getString(R.string.action_menu_dialpad_button));
- }
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- }
-
- @Override
- public boolean isActionBarShowing() {
- return mActionBarController.isActionBarShowing();
- }
-
- @Override
- public ActionBarController getActionBarController() {
- return mActionBarController;
- }
-
- @Override
- public boolean isDialpadShown() {
- return mIsDialpadShown;
- }
-
- @Override
- public int getDialpadHeight() {
- if (mDialpadFragment != null) {
- return mDialpadFragment.getDialpadHeight();
- }
- return 0;
- }
-
- @Override
- public int getActionBarHideOffset() {
- return getSupportActionBar().getHideOffset();
- }
-
- @Override
- public void setActionBarHideOffset(int offset) {
- getSupportActionBar().setHideOffset(offset);
- }
-
- @Override
- public int getActionBarHeight() {
- return mActionBarHeight;
- }
-
- private int getFabAlignment() {
- if (!mIsLandscape && !isInSearchUi() &&
- mListsFragment.getCurrentTabIndex() == ListsFragment.TAB_INDEX_SPEED_DIAL) {
- return FloatingActionButtonController.ALIGN_MIDDLE;
- }
- return FloatingActionButtonController.ALIGN_END;
- }
-
- private void updateMissedCalls() {
- if (mPreviouslySelectedTabIndex == ListsFragment.TAB_INDEX_HISTORY) {
- mListsFragment.markMissedCallsAsReadAndRemoveNotifications();
- }
- }
-}
diff --git a/src/com/android/dialer/FloatingActionButtonBehavior.java b/src/com/android/dialer/FloatingActionButtonBehavior.java
deleted file mode 100644
index 679c9a7c1..000000000
--- a/src/com/android/dialer/FloatingActionButtonBehavior.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.dialer;
-
-import android.content.Context;
-import android.support.design.widget.CoordinatorLayout;
-import android.support.design.widget.Snackbar.SnackbarLayout;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-
-/**
- * Implements custom behavior for the movement of the FAB in response to the Snackbar.
- * Because we are not using the design framework FloatingActionButton widget, we need to manually
- * implement the Material Design behavior of having the FAB translate upward and downward with
- * the appearance and disappearance of a Snackbar.
- */
-public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior<FrameLayout> {
- public FloatingActionButtonBehavior(Context context, AttributeSet attrs) {
- }
-
- @Override
- public boolean layoutDependsOn(CoordinatorLayout parent, FrameLayout child, View dependency) {
- return dependency instanceof SnackbarLayout;
- }
-
- @Override
- public boolean onDependentViewChanged(CoordinatorLayout parent, FrameLayout child,
- View dependency) {
- float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
- child.setTranslationY(translationY);
- return true;
- }
-}
diff --git a/src/com/android/dialer/NeededForReflection.java b/src/com/android/dialer/NeededForReflection.java
deleted file mode 100644
index e836908b1..000000000
--- a/src/com/android/dialer/NeededForReflection.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.dialer;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Denotes that the class, constructor, method or field is used for reflection and therefore cannot
- * be removed by tools like ProGuard.
- */
-@Retention(RetentionPolicy.CLASS)
-@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
-public @interface NeededForReflection{}
diff --git a/src/com/android/dialer/PhoneCallDetails.java b/src/com/android/dialer/PhoneCallDetails.java
deleted file mode 100644
index 8a2e52090..000000000
--- a/src/com/android/dialer/PhoneCallDetails.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * 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.dialer;
-
-import com.android.contacts.common.ContactsUtils.UserType;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.calllog.PhoneNumberDisplayUtil;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.provider.CallLog.Calls;
-import android.support.annotation.Nullable;
-import android.telecom.PhoneAccountHandle;
-import android.text.TextUtils;
-
-/**
- * The details of a phone call to be shown in the UI.
- */
-public class PhoneCallDetails {
- // The number of the other party involved in the call.
- public CharSequence number;
- // Post-dial digits associated with the outgoing call.
- public String postDialDigits;
- // The secondary line number the call was received via.
- public String viaNumber;
- // The number presenting rules set by the network, e.g., {@link Calls#PRESENTATION_ALLOWED}
- public int numberPresentation;
- // The formatted version of {@link #number}.
- public CharSequence formattedNumber;
- // The country corresponding with the phone number.
- public String countryIso;
- // The geocoded location for the phone number.
- public String geocode;
-
- /**
- * The type of calls, as defined in the call log table, e.g., {@link Calls#INCOMING_TYPE}.
- * <p>
- * There might be multiple types if this represents a set of entries grouped together.
- */
- public int[] callTypes;
-
- // The date of the call, in milliseconds since the epoch.
- public long date;
- // The duration of the call in milliseconds, or 0 for missed calls.
- public long duration;
- // The name of the contact, or the empty string.
- public CharSequence namePrimary;
- // The alternative name of the contact, e.g. last name first, or the empty string
- public CharSequence nameAlternative;
- /**
- * The user's preference on name display order, last name first or first time first.
- * {@see ContactsPreferences}
- */
- public int nameDisplayOrder;
- // The type of phone, e.g., {@link Phone#TYPE_HOME}, 0 if not available.
- public int numberType;
- // The custom label associated with the phone number in the contact, or the empty string.
- public CharSequence numberLabel;
- // The URI of the contact associated with this phone call.
- public Uri contactUri;
-
- /**
- * The photo URI of the picture of the contact that is associated with this phone call or
- * null if there is none.
- * <p>
- * This is meant to store the high-res photo only.
- */
- public Uri photoUri;
-
- // The source type of the contact associated with this call.
- public int sourceType;
-
- // The object id type of the contact associated with this call.
- public String objectId;
-
- // The unique identifier for the account associated with the call.
- public PhoneAccountHandle accountHandle;
-
- // Features applicable to this call.
- public int features;
-
- // Total data usage for this call.
- public Long dataUsage;
-
- // Voicemail transcription
- public String transcription;
-
- // The display string for the number.
- public String displayNumber;
-
- // Whether the contact number is a voicemail number.
- public boolean isVoicemail;
-
- /** The {@link UserType} of the contact */
- public @UserType long contactUserType;
-
- /**
- * If this is a voicemail, whether the message is read. For other types of calls, this defaults
- * to {@code true}.
- */
- public boolean isRead = true;
-
- // If this call is a spam number.
- public boolean isSpam = false;
-
- /**
- * Constructor with required fields for the details of a call with a number associated with a
- * contact.
- */
- public PhoneCallDetails(
- Context context,
- CharSequence number,
- int numberPresentation,
- CharSequence formattedNumber,
- CharSequence postDialDigits,
- boolean isVoicemail) {
- this.number = number;
- this.numberPresentation = numberPresentation;
- this.formattedNumber = formattedNumber;
- this.isVoicemail = isVoicemail;
- this.postDialDigits = postDialDigits.toString();
- this.displayNumber = PhoneNumberDisplayUtil.getDisplayNumber(
- context,
- this.number,
- this.numberPresentation,
- this.formattedNumber,
- this.postDialDigits,
- this.isVoicemail).toString();
- }
-
- /**
- * Returns the preferred name for the call details as specified by the
- * {@link #nameDisplayOrder}
- *
- * @return the preferred name
- */
- public CharSequence getPreferredName() {
- if (nameDisplayOrder == ContactsPreferences.DISPLAY_ORDER_PRIMARY
- || TextUtils.isEmpty(nameAlternative)) {
- return namePrimary;
- }
- return nameAlternative;
- }
-
- /**
- * Construct the "on {accountLabel} via {viaNumber}" accessibility description for the account
- * list item, depending on the existence of the accountLabel and viaNumber.
- * @param viaNumber The number that this call is being placed via.
- * @param accountLabel The {@link PhoneAccount} label that this call is being placed with.
- * @return The description of the account that this call has been placed on.
- */
- public static CharSequence createAccountLabelDescription(Resources resources,
- @Nullable String viaNumber, @Nullable CharSequence accountLabel) {
-
- if((!TextUtils.isEmpty(viaNumber)) && !TextUtils.isEmpty(accountLabel)) {
- String msg = resources.getString(R.string.description_via_number_phone_account,
- accountLabel, viaNumber);
- CharSequence accountNumberLabel = ContactDisplayUtils.getTelephoneTtsSpannable(msg,
- viaNumber);
- return (accountNumberLabel == null) ? msg : accountNumberLabel;
- } else if (!TextUtils.isEmpty(viaNumber)) {
- CharSequence viaNumberLabel = ContactDisplayUtils.getTtsSpannedPhoneNumber(resources,
- R.string.description_via_number, viaNumber);
- return (viaNumberLabel == null) ? viaNumber : viaNumberLabel;
- } else if (!TextUtils.isEmpty(accountLabel)) {
- return TextUtils.expandTemplate(
- resources.getString(R.string.description_phone_account), accountLabel);
- }
- return "";
- }
-}
diff --git a/src/com/android/dialer/SpecialCharSequenceMgr.java b/src/com/android/dialer/SpecialCharSequenceMgr.java
deleted file mode 100644
index fe2163f17..000000000
--- a/src/com/android/dialer/SpecialCharSequenceMgr.java
+++ /dev/null
@@ -1,495 +0,0 @@
-/*
- * Copyright (C) 2006 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.dialer;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.DialogFragment;
-import android.app.KeyguardManager;
-import android.app.ProgressDialog;
-import android.content.ActivityNotFoundException;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Looper;
-import android.provider.Settings;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.WindowManager;
-import android.widget.EditText;
-import android.widget.Toast;
-
-import com.android.common.io.MoreCloseables;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.TelephonyManagerCompat;
-import com.android.contacts.common.database.NoNullCursorAsyncQueryHandler;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
-import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment.SelectPhoneAccountListener;
-import com.android.dialer.calllog.PhoneAccountUtils;
-import com.android.dialer.util.TelecomUtil;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Helper class to listen for some magic character sequences
- * that are handled specially by the dialer.
- *
- * Note the Phone app also handles these sequences too (in a couple of
- * relatively obscure places in the UI), so there's a separate version of
- * this class under apps/Phone.
- *
- * TODO: there's lots of duplicated code between this class and the
- * corresponding class under apps/Phone. Let's figure out a way to
- * unify these two classes (in the framework? in a common shared library?)
- */
-public class SpecialCharSequenceMgr {
- private static final String TAG = "SpecialCharSequenceMgr";
-
- private static final String TAG_SELECT_ACCT_FRAGMENT = "tag_select_acct_fragment";
-
- private static final String SECRET_CODE_ACTION = "android.provider.Telephony.SECRET_CODE";
- private static final String MMI_IMEI_DISPLAY = "*#06#";
- private static final String MMI_REGULATORY_INFO_DISPLAY = "*#07#";
-
- /**
- * Remembers the previous {@link QueryHandler} and cancel the operation when needed, to
- * prevent possible crash.
- *
- * QueryHandler may call {@link ProgressDialog#dismiss()} when the screen is already gone,
- * which will cause the app crash. This variable enables the class to prevent the crash
- * on {@link #cleanup()}.
- *
- * TODO: Remove this and replace it (and {@link #cleanup()}) with better implementation.
- * One complication is that we have SpecialCharSequenceMgr in Phone package too, which has
- * *slightly* different implementation. Note that Phone package doesn't have this problem,
- * so the class on Phone side doesn't have this functionality.
- * Fundamental fix would be to have one shared implementation and resolve this corner case more
- * gracefully.
- */
- private static QueryHandler sPreviousAdnQueryHandler;
-
- public static class HandleAdnEntryAccountSelectedCallback extends SelectPhoneAccountListener{
- final private Context mContext;
- final private QueryHandler mQueryHandler;
- final private SimContactQueryCookie mCookie;
-
- public HandleAdnEntryAccountSelectedCallback(Context context,
- QueryHandler queryHandler, SimContactQueryCookie cookie) {
- mContext = context;
- mQueryHandler = queryHandler;
- mCookie = cookie;
- }
-
- @Override
- public void onPhoneAccountSelected(PhoneAccountHandle selectedAccountHandle,
- boolean setDefault) {
- Uri uri = TelecomUtil.getAdnUriForPhoneAccount(mContext, selectedAccountHandle);
- handleAdnQuery(mQueryHandler, mCookie, uri);
- // TODO: Show error dialog if result isn't valid.
- }
-
- }
-
- public static class HandleMmiAccountSelectedCallback extends SelectPhoneAccountListener{
- final private Context mContext;
- final private String mInput;
- public HandleMmiAccountSelectedCallback(Context context, String input) {
- mContext = context.getApplicationContext();
- mInput = input;
- }
-
- @Override
- public void onPhoneAccountSelected(PhoneAccountHandle selectedAccountHandle,
- boolean setDefault) {
- TelecomUtil.handleMmi(mContext, mInput, selectedAccountHandle);
- }
- }
-
- /** This class is never instantiated. */
- private SpecialCharSequenceMgr() {
- }
-
- public static boolean handleChars(Context context, String input, EditText textField) {
- //get rid of the separators so that the string gets parsed correctly
- String dialString = PhoneNumberUtils.stripSeparators(input);
-
- if (handleDeviceIdDisplay(context, dialString)
- || handleRegulatoryInfoDisplay(context, dialString)
- || handlePinEntry(context, dialString)
- || handleAdnEntry(context, dialString, textField)
- || handleSecretCode(context, dialString)) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Cleanup everything around this class. Must be run inside the main thread.
- *
- * This should be called when the screen becomes background.
- */
- public static void cleanup() {
- if (Looper.myLooper() != Looper.getMainLooper()) {
- Log.wtf(TAG, "cleanup() is called outside the main thread");
- return;
- }
-
- if (sPreviousAdnQueryHandler != null) {
- sPreviousAdnQueryHandler.cancel();
- sPreviousAdnQueryHandler = null;
- }
- }
-
- /**
- * Handles secret codes to launch arbitrary activities in the form of *#*#<code>#*#*.
- * If a secret code is encountered, an Intent is started with the android_secret_code://<code>
- * URI.
- *
- * @param context the context to use
- * @param input the text to check for a secret code in
- * @return true if a secret code was encountered and intent is sent out
- */
- static boolean handleSecretCode(Context context, String input) {
- final TelephonyManager telephonyManager =
- (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- if (telephonyManager != null) {
- return telephonyManager.sendDialerCode(input);
- }
- return false;
- }
-
- /**
- * Handle ADN requests by filling in the SIM contact number into the requested
- * EditText.
- *
- * This code works alongside the Asynchronous query handler {@link QueryHandler}
- * and query cancel handler implemented in {@link SimContactQueryCookie}.
- */
- static boolean handleAdnEntry(Context context, String input, EditText textField) {
- /* ADN entries are of the form "N(N)(N)#" */
- TelephonyManager telephonyManager =
- (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- if (telephonyManager == null
- || telephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_GSM) {
- return false;
- }
-
- // if the phone is keyguard-restricted, then just ignore this
- // input. We want to make sure that sim card contacts are NOT
- // exposed unless the phone is unlocked, and this code can be
- // accessed from the emergency dialer.
- KeyguardManager keyguardManager =
- (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
- if (keyguardManager.inKeyguardRestrictedInputMode()) {
- return false;
- }
-
- int len = input.length();
- if ((len > 1) && (len < 5) && (input.endsWith("#"))) {
- try {
- // get the ordinal number of the sim contact
- final int index = Integer.parseInt(input.substring(0, len-1));
-
- // The original code that navigated to a SIM Contacts list view did not
- // highlight the requested contact correctly, a requirement for PTCRB
- // certification. This behaviour is consistent with the UI paradigm
- // for touch-enabled lists, so it does not make sense to try to work
- // around it. Instead we fill in the the requested phone number into
- // the dialer text field.
-
- // create the async query handler
- final QueryHandler handler = new QueryHandler(context.getContentResolver());
-
- // create the cookie object
- final SimContactQueryCookie sc = new SimContactQueryCookie(index - 1, handler,
- ADN_QUERY_TOKEN);
-
- // setup the cookie fields
- sc.contactNum = index - 1;
- sc.setTextField(textField);
-
- // create the progress dialog
- sc.progressDialog = new ProgressDialog(context);
- sc.progressDialog.setTitle(R.string.simContacts_title);
- sc.progressDialog.setMessage(context.getText(R.string.simContacts_emptyLoading));
- sc.progressDialog.setIndeterminate(true);
- sc.progressDialog.setCancelable(true);
- sc.progressDialog.setOnCancelListener(sc);
- sc.progressDialog.getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
-
- List<PhoneAccountHandle> subscriptionAccountHandles =
- PhoneAccountUtils.getSubscriptionPhoneAccounts(context);
- Context applicationContext = context.getApplicationContext();
- boolean hasUserSelectedDefault = subscriptionAccountHandles.contains(
- TelecomUtil.getDefaultOutgoingPhoneAccount(applicationContext,
- PhoneAccount.SCHEME_TEL));
-
- if (subscriptionAccountHandles.size() <= 1 || hasUserSelectedDefault) {
- Uri uri = TelecomUtil.getAdnUriForPhoneAccount(applicationContext, null);
- handleAdnQuery(handler, sc, uri);
- } else {
- SelectPhoneAccountListener callback = new HandleAdnEntryAccountSelectedCallback(
- applicationContext, handler, sc);
-
- DialogFragment dialogFragment = SelectPhoneAccountDialogFragment.newInstance(
- subscriptionAccountHandles, callback);
- dialogFragment.show(((Activity) context).getFragmentManager(),
- TAG_SELECT_ACCT_FRAGMENT);
- }
-
- return true;
- } catch (NumberFormatException ex) {
- // Ignore
- }
- }
- return false;
- }
-
- private static void handleAdnQuery(QueryHandler handler, SimContactQueryCookie cookie,
- Uri uri) {
- if (handler == null || cookie == null || uri == null) {
- Log.w(TAG, "queryAdn parameters incorrect");
- return;
- }
-
- // display the progress dialog
- cookie.progressDialog.show();
-
- // run the query.
- handler.startQuery(ADN_QUERY_TOKEN, cookie, uri, new String[]{ADN_PHONE_NUMBER_COLUMN_NAME},
- null, null, null);
-
- if (sPreviousAdnQueryHandler != null) {
- // It is harmless to call cancel() even after the handler's gone.
- sPreviousAdnQueryHandler.cancel();
- }
- sPreviousAdnQueryHandler = handler;
- }
-
- static boolean handlePinEntry(final Context context, final String input) {
- if ((input.startsWith("**04") || input.startsWith("**05")) && input.endsWith("#")) {
- List<PhoneAccountHandle> subscriptionAccountHandles =
- PhoneAccountUtils.getSubscriptionPhoneAccounts(context);
- boolean hasUserSelectedDefault = subscriptionAccountHandles.contains(
- TelecomUtil.getDefaultOutgoingPhoneAccount(context, PhoneAccount.SCHEME_TEL));
-
- if (subscriptionAccountHandles.size() <= 1 || hasUserSelectedDefault) {
- // Don't bring up the dialog for single-SIM or if the default outgoing account is
- // a subscription account.
- return TelecomUtil.handleMmi(context, input, null);
- } else {
- SelectPhoneAccountListener listener =
- new HandleMmiAccountSelectedCallback(context, input);
-
- DialogFragment dialogFragment = SelectPhoneAccountDialogFragment.newInstance(
- subscriptionAccountHandles, listener);
- dialogFragment.show(((Activity) context).getFragmentManager(),
- TAG_SELECT_ACCT_FRAGMENT);
- }
- return true;
- }
- return false;
- }
-
- // TODO: Use TelephonyCapabilities.getDeviceIdLabel() to get the device id label instead of a
- // hard-coded string.
- static boolean handleDeviceIdDisplay(Context context, String input) {
- TelephonyManager telephonyManager =
- (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
-
- if (telephonyManager != null && input.equals(MMI_IMEI_DISPLAY)) {
- int labelResId = (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) ?
- R.string.imei : R.string.meid;
-
- List<String> deviceIds = new ArrayList<String>();
- if (TelephonyManagerCompat.getPhoneCount(telephonyManager) > 1 &&
- CompatUtils.isMethodAvailable(TelephonyManagerCompat.TELEPHONY_MANAGER_CLASS,
- "getDeviceId", Integer.TYPE)) {
- for (int slot = 0; slot < telephonyManager.getPhoneCount(); slot++) {
- String deviceId = telephonyManager.getDeviceId(slot);
- if (!TextUtils.isEmpty(deviceId)) {
- deviceIds.add(deviceId);
- }
- }
- } else {
- deviceIds.add(telephonyManager.getDeviceId());
- }
-
- AlertDialog alert = new AlertDialog.Builder(context)
- .setTitle(labelResId)
- .setItems(deviceIds.toArray(new String[deviceIds.size()]), null)
- .setPositiveButton(android.R.string.ok, null)
- .setCancelable(false)
- .show();
- return true;
- }
- return false;
- }
-
- private static boolean handleRegulatoryInfoDisplay(Context context, String input) {
- if (input.equals(MMI_REGULATORY_INFO_DISPLAY)) {
- Log.d(TAG, "handleRegulatoryInfoDisplay() sending intent to settings app");
- Intent showRegInfoIntent = new Intent(Settings.ACTION_SHOW_REGULATORY_INFO);
- try {
- context.startActivity(showRegInfoIntent);
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "startActivity() failed: " + e);
- }
- return true;
- }
- return false;
- }
-
- /*******
- * This code is used to handle SIM Contact queries
- *******/
- private static final String ADN_PHONE_NUMBER_COLUMN_NAME = "number";
- private static final String ADN_NAME_COLUMN_NAME = "name";
- private static final int ADN_QUERY_TOKEN = -1;
-
- /**
- * Cookie object that contains everything we need to communicate to the
- * handler's onQuery Complete, as well as what we need in order to cancel
- * the query (if requested).
- *
- * Note, access to the textField field is going to be synchronized, because
- * the user can request a cancel at any time through the UI.
- */
- private static class SimContactQueryCookie implements DialogInterface.OnCancelListener{
- public ProgressDialog progressDialog;
- public int contactNum;
-
- // Used to identify the query request.
- private int mToken;
- private QueryHandler mHandler;
-
- // The text field we're going to update
- private EditText textField;
-
- public SimContactQueryCookie(int number, QueryHandler handler, int token) {
- contactNum = number;
- mHandler = handler;
- mToken = token;
- }
-
- /**
- * Synchronized getter for the EditText.
- */
- public synchronized EditText getTextField() {
- return textField;
- }
-
- /**
- * Synchronized setter for the EditText.
- */
- public synchronized void setTextField(EditText text) {
- textField = text;
- }
-
- /**
- * Cancel the ADN query by stopping the operation and signaling
- * the cookie that a cancel request is made.
- */
- public synchronized void onCancel(DialogInterface dialog) {
- // close the progress dialog
- if (progressDialog != null) {
- progressDialog.dismiss();
- }
-
- // setting the textfield to null ensures that the UI does NOT get
- // updated.
- textField = null;
-
- // Cancel the operation if possible.
- mHandler.cancelOperation(mToken);
- }
- }
-
- /**
- * Asynchronous query handler that services requests to look up ADNs
- *
- * Queries originate from {@link #handleAdnEntry}.
- */
- private static class QueryHandler extends NoNullCursorAsyncQueryHandler {
-
- private boolean mCanceled;
-
- public QueryHandler(ContentResolver cr) {
- super(cr);
- }
-
- /**
- * Override basic onQueryComplete to fill in the textfield when
- * we're handed the ADN cursor.
- */
- @Override
- protected void onNotNullableQueryComplete(int token, Object cookie, Cursor c) {
- try {
- sPreviousAdnQueryHandler = null;
- if (mCanceled) {
- return;
- }
-
- SimContactQueryCookie sc = (SimContactQueryCookie) cookie;
-
- // close the progress dialog.
- sc.progressDialog.dismiss();
-
- // get the EditText to update or see if the request was cancelled.
- EditText text = sc.getTextField();
-
- // if the TextView is valid, and the cursor is valid and positionable on the
- // Nth number, then we update the text field and display a toast indicating the
- // caller name.
- if ((c != null) && (text != null) && (c.moveToPosition(sc.contactNum))) {
- String name = c.getString(c.getColumnIndexOrThrow(ADN_NAME_COLUMN_NAME));
- String number =
- c.getString(c.getColumnIndexOrThrow(ADN_PHONE_NUMBER_COLUMN_NAME));
-
- // fill the text in.
- text.getText().replace(0, 0, number);
-
- // display the name as a toast
- Context context = sc.progressDialog.getContext();
- CharSequence msg = ContactDisplayUtils.getTtsSpannedPhoneNumber(
- context.getResources(), R.string.menu_callNumber, name);
- Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
- }
- } finally {
- MoreCloseables.closeQuietly(c);
- }
- }
-
- public void cancel() {
- mCanceled = true;
- // Ask AsyncQueryHandler to cancel the whole request. This will fail when the query is
- // already started.
- cancelOperation(ADN_QUERY_TOKEN);
- }
- }
-}
diff --git a/src/com/android/dialer/TransactionSafeActivity.java b/src/com/android/dialer/TransactionSafeActivity.java
deleted file mode 100644
index 81e50128d..000000000
--- a/src/com/android/dialer/TransactionSafeActivity.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.dialer;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-
-/**
- * A common superclass that keeps track of whether an {@link Activity} has saved its state yet or
- * not.
- */
-public abstract class TransactionSafeActivity extends AppCompatActivity {
-
- private boolean mIsSafeToCommitTransactions;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mIsSafeToCommitTransactions = true;
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- mIsSafeToCommitTransactions = true;
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mIsSafeToCommitTransactions = true;
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mIsSafeToCommitTransactions = false;
- }
-
- /**
- * Returns true if it is safe to commit {@link FragmentTransaction}s at this time, based on
- * whether {@link Activity#onSaveInstanceState} has been called or not.
- *
- * Make sure that the current activity calls into
- * {@link super.onSaveInstanceState(Bundle outState)} (if that method is overridden),
- * so the flag is properly set.
- */
- public boolean isSafeToCommitTransactions() {
- return mIsSafeToCommitTransactions;
- }
-}
diff --git a/src/com/android/dialer/calllog/BlockReportSpamListener.java b/src/com/android/dialer/calllog/BlockReportSpamListener.java
deleted file mode 100644
index 92cbc804b..000000000
--- a/src/com/android/dialer/calllog/BlockReportSpamListener.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package com.android.dialer.calllog;
-
-import android.app.Activity;
-import android.app.FragmentManager;
-import android.content.ContentValues;
-import android.content.DialogInterface;
-import android.net.Uri;
-import android.support.v7.widget.RecyclerView;
-
-import com.android.dialer.util.BlockReportSpamDialogs;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.service.ExtendedCallInfoService;
-
-/**
- * Listener to show dialogs for block and report spam actions.
- */
-public class BlockReportSpamListener implements CallLogListItemViewHolder.OnClickListener {
-
- private final FragmentManager mFragmentManager;
- private final RecyclerView.Adapter mAdapter;
- private final ExtendedCallInfoService mExtendedCallInfoService;
- private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
-
- public BlockReportSpamListener(FragmentManager fragmentManager, RecyclerView.Adapter adapter,
- ExtendedCallInfoService extendedCallInfoService,
- FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler) {
- mFragmentManager = fragmentManager;
- mAdapter = adapter;
- mExtendedCallInfoService = extendedCallInfoService;
- mFilteredNumberAsyncQueryHandler = filteredNumberAsyncQueryHandler;
- }
-
- @Override
- public void onBlockReportSpam(String displayNumber, final String number,
- final String countryIso, final int callType) {
- BlockReportSpamDialogs.BlockReportSpamDialogFragment.newInstance(
- displayNumber,
- false,
- new BlockReportSpamDialogs.OnSpamDialogClickListener() {
- @Override
- public void onClick(boolean isSpamChecked) {
- if (isSpamChecked) {
- mExtendedCallInfoService.reportSpam(
- number, countryIso, callType,
- ExtendedCallInfoService.REPORTING_LOCATION_CALL_LOG_HISTORY);
- }
- mFilteredNumberAsyncQueryHandler.blockNumber(
- new FilteredNumberAsyncQueryHandler.OnBlockNumberListener() {
- @Override
- public void onBlockComplete(Uri uri) {
- mAdapter.notifyDataSetChanged();
- }
- },
- number,
- countryIso);
- }
- }, null)
- .show(mFragmentManager, BlockReportSpamDialogs.BLOCK_REPORT_SPAM_DIALOG_TAG);
- }
-
- @Override
- public void onBlock(String displayNumber, final String number, final String countryIso,
- final int callType) {
- BlockReportSpamDialogs.BlockDialogFragment.newInstance(displayNumber,
- new BlockReportSpamDialogs.OnConfirmListener() {
- @Override
- public void onClick() {
- mExtendedCallInfoService.reportSpam(number, countryIso, callType,
- ExtendedCallInfoService.REPORTING_LOCATION_CALL_LOG_HISTORY);
- mFilteredNumberAsyncQueryHandler.blockNumber(
- new FilteredNumberAsyncQueryHandler.OnBlockNumberListener() {
- @Override
- public void onBlockComplete(Uri uri) {
- mAdapter.notifyDataSetChanged();
- }
- },
- number,
- countryIso);
- }
- }, null)
- .show(mFragmentManager, BlockReportSpamDialogs.BLOCK_DIALOG_TAG);
- }
-
- @Override
- public void onUnblock(String displayNumber, final String number, final String countryIso,
- final Integer blockId, final boolean isSpam, final int callType) {
- BlockReportSpamDialogs.UnblockDialogFragment.newInstance(displayNumber, isSpam,
- new BlockReportSpamDialogs.OnConfirmListener() {
- @Override
- public void onClick() {
- if (isSpam) {
- mExtendedCallInfoService.reportNotSpam(
- number, countryIso, callType,
- ExtendedCallInfoService.REPORTING_LOCATION_CALL_LOG_HISTORY);
- }
- mFilteredNumberAsyncQueryHandler.unblock(
- new FilteredNumberAsyncQueryHandler.OnUnblockNumberListener() {
- @Override
- public void onUnblockComplete(int rows, ContentValues values) {
- mAdapter.notifyDataSetChanged();
- }
- },
- blockId);
- }
- }, null)
- .show(mFragmentManager, BlockReportSpamDialogs.UNBLOCK_DIALOG_TAG);
- }
-
- @Override
- public void onReportNotSpam(String displayNumber, final String number, final String countryIso,
- final int callType) {
- BlockReportSpamDialogs.ReportNotSpamDialogFragment.newInstance(displayNumber,
- new BlockReportSpamDialogs.OnConfirmListener() {
- @Override
- public void onClick() {
- mExtendedCallInfoService.reportNotSpam(
- number, countryIso, callType,
- ExtendedCallInfoService.REPORTING_LOCATION_CALL_LOG_HISTORY);
- mAdapter.notifyDataSetChanged();
- }
- }, null)
- .show(mFragmentManager, BlockReportSpamDialogs.NOT_SPAM_DIALOG_TAG);
- }
-}
diff --git a/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java b/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java
deleted file mode 100644
index ac56332ce..000000000
--- a/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.Context;
-import android.provider.CallLog.Calls;
-import android.text.format.DateUtils;
-import android.text.format.Formatter;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.TextView;
-
-import com.android.contacts.common.CallUtil;
-import com.android.dialer.PhoneCallDetails;
-import com.android.dialer.R;
-import com.android.dialer.util.DialerUtils;
-import com.google.common.collect.Lists;
-
-import java.util.ArrayList;
-
-/**
- * Adapter for a ListView containing history items from the details of a call.
- */
-public class CallDetailHistoryAdapter extends BaseAdapter {
- /** Each history item shows the detail of a call. */
- private static final int VIEW_TYPE_HISTORY_ITEM = 1;
-
- private final Context mContext;
- private final LayoutInflater mLayoutInflater;
- private final CallTypeHelper mCallTypeHelper;
- private final PhoneCallDetails[] mPhoneCallDetails;
-
- /**
- * List of items to be concatenated together for duration strings.
- */
- private ArrayList<CharSequence> mDurationItems = Lists.newArrayList();
-
- public CallDetailHistoryAdapter(Context context, LayoutInflater layoutInflater,
- CallTypeHelper callTypeHelper, PhoneCallDetails[] phoneCallDetails) {
- mContext = context;
- mLayoutInflater = layoutInflater;
- mCallTypeHelper = callTypeHelper;
- mPhoneCallDetails = phoneCallDetails;
- }
-
- @Override
- public boolean isEnabled(int position) {
- // None of history will be clickable.
- return false;
- }
-
- @Override
- public int getCount() {
- return mPhoneCallDetails.length;
- }
-
- @Override
- public Object getItem(int position) {
- return mPhoneCallDetails[position];
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public int getViewTypeCount() {
- return 1;
- }
-
- @Override
- public int getItemViewType(int position) {
- return VIEW_TYPE_HISTORY_ITEM;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // Make sure we have a valid convertView to start with
- final View result = convertView == null
- ? mLayoutInflater.inflate(R.layout.call_detail_history_item, parent, false)
- : convertView;
-
- PhoneCallDetails details = mPhoneCallDetails[position];
- CallTypeIconsView callTypeIconView =
- (CallTypeIconsView) result.findViewById(R.id.call_type_icon);
- TextView callTypeTextView = (TextView) result.findViewById(R.id.call_type_text);
- TextView dateView = (TextView) result.findViewById(R.id.date);
- TextView durationView = (TextView) result.findViewById(R.id.duration);
-
- int callType = details.callTypes[0];
- boolean isVideoCall = (details.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO
- && CallUtil.isVideoEnabled(mContext);
-
- callTypeIconView.clear();
- callTypeIconView.add(callType);
- callTypeIconView.setShowVideo(isVideoCall);
- callTypeTextView.setText(mCallTypeHelper.getCallTypeText(callType, isVideoCall));
- // Set the date.
- CharSequence dateValue = DateUtils.formatDateRange(mContext, details.date, details.date,
- DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
- DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR);
- dateView.setText(dateValue);
- // Set the duration
- if (Calls.VOICEMAIL_TYPE == callType || CallTypeHelper.isMissedCallType(callType)) {
- durationView.setVisibility(View.GONE);
- } else {
- durationView.setVisibility(View.VISIBLE);
- durationView.setText(formatDurationAndDataUsage(details.duration, details.dataUsage));
- }
-
- return result;
- }
-
- private CharSequence formatDuration(long elapsedSeconds) {
- long minutes = 0;
- long seconds = 0;
-
- if (elapsedSeconds >= 60) {
- minutes = elapsedSeconds / 60;
- elapsedSeconds -= minutes * 60;
- seconds = elapsedSeconds;
- return mContext.getString(R.string.callDetailsDurationFormat, minutes, seconds);
- } else {
- seconds = elapsedSeconds;
- return mContext.getString(R.string.callDetailsShortDurationFormat, seconds);
- }
- }
-
- /**
- * Formats a string containing the call duration and the data usage (if specified).
- *
- * @param elapsedSeconds Total elapsed seconds.
- * @param dataUsage Data usage in bytes, or null if not specified.
- * @return String containing call duration and data usage.
- */
- private CharSequence formatDurationAndDataUsage(long elapsedSeconds, Long dataUsage) {
- CharSequence duration = formatDuration(elapsedSeconds);
-
- if (dataUsage != null) {
- mDurationItems.clear();
- mDurationItems.add(duration);
- mDurationItems.add(Formatter.formatShortFileSize(mContext, dataUsage));
-
- return DialerUtils.join(mContext.getResources(), mDurationItems);
- } else {
- return duration;
- }
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogActivity.java b/src/com/android/dialer/calllog/CallLogActivity.java
deleted file mode 100644
index 1823a5bd3..000000000
--- a/src/com/android/dialer/calllog/CallLogActivity.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.calllog;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.content.Intent;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.os.Handler;
-import android.provider.CallLog;
-import android.provider.CallLog.Calls;
-import android.support.v13.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.support.v7.app.ActionBar;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.ViewGroup;
-
-import com.android.contacts.common.interactions.TouchPointManager;
-import com.android.contacts.common.list.ViewPagerTabs;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.commonbind.analytics.AnalyticsUtil;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.R;
-import com.android.dialer.TransactionSafeActivity;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.ScreenEvent;
-import com.android.dialer.util.DialerUtils;
-
-public class CallLogActivity extends TransactionSafeActivity implements ViewPager.OnPageChangeListener {
- private ViewPager mViewPager;
- private ViewPagerTabs mViewPagerTabs;
- private ViewPagerAdapter mViewPagerAdapter;
- private CallLogFragment mAllCallsFragment;
- private CallLogFragment mMissedCallsFragment;
-
- private String[] mTabTitles;
-
- private static final int TAB_INDEX_ALL = 0;
- private static final int TAB_INDEX_MISSED = 1;
-
- private static final int TAB_INDEX_COUNT = 2;
-
- private boolean mIsResumed;
-
- public class ViewPagerAdapter extends FragmentPagerAdapter {
- public ViewPagerAdapter(FragmentManager fm) {
- super(fm);
- }
-
- @Override
- public long getItemId(int position) {
- return getRtlPosition(position);
- }
-
- @Override
- public Fragment getItem(int position) {
- switch (getRtlPosition(position)) {
- case TAB_INDEX_ALL:
- return new CallLogFragment(
- CallLogQueryHandler.CALL_TYPE_ALL, true /* isCallLogActivity */);
- case TAB_INDEX_MISSED:
- return new CallLogFragment(Calls.MISSED_TYPE, true /* isCallLogActivity */);
- }
- throw new IllegalStateException("No fragment at position " + position);
- }
-
- @Override
- public Object instantiateItem(ViewGroup container, int position) {
- final CallLogFragment fragment =
- (CallLogFragment) super.instantiateItem(container, position);
- switch (position) {
- case TAB_INDEX_ALL:
- mAllCallsFragment = fragment;
- break;
- case TAB_INDEX_MISSED:
- mMissedCallsFragment = fragment;
- break;
- }
- return fragment;
- }
-
- @Override
- public CharSequence getPageTitle(int position) {
- return mTabTitles[position];
- }
-
- @Override
- public int getCount() {
- return TAB_INDEX_COUNT;
- }
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- TouchPointManager.getInstance().setPoint((int) ev.getRawX(), (int) ev.getRawY());
- }
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.call_log_activity);
- getWindow().setBackgroundDrawable(null);
-
- final ActionBar actionBar = getSupportActionBar();
- actionBar.setDisplayShowHomeEnabled(true);
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowTitleEnabled(true);
- actionBar.setElevation(0);
-
- int startingTab = TAB_INDEX_ALL;
- final Intent intent = getIntent();
- if (intent != null) {
- final int callType = intent.getIntExtra(CallLog.Calls.EXTRA_CALL_TYPE_FILTER, -1);
- if (callType == CallLog.Calls.MISSED_TYPE) {
- startingTab = TAB_INDEX_MISSED;
- }
- }
-
- mTabTitles = new String[TAB_INDEX_COUNT];
- mTabTitles[0] = getString(R.string.call_log_all_title);
- mTabTitles[1] = getString(R.string.call_log_missed_title);
-
- mViewPager = (ViewPager) findViewById(R.id.call_log_pager);
-
- mViewPagerAdapter = new ViewPagerAdapter(getFragmentManager());
- mViewPager.setAdapter(mViewPagerAdapter);
- mViewPager.setOffscreenPageLimit(1);
- mViewPager.setOnPageChangeListener(this);
-
- mViewPagerTabs = (ViewPagerTabs) findViewById(R.id.viewpager_header);
-
- mViewPagerTabs.setViewPager(mViewPager);
- mViewPager.setCurrentItem(startingTab);
- }
-
- @Override
- protected void onResume() {
- mIsResumed = true;
- super.onResume();
- sendScreenViewForChildFragment(mViewPager.getCurrentItem());
- }
-
- @Override
- protected void onPause() {
- mIsResumed = false;
- super.onPause();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- final MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.call_log_options, menu);
- return true;
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- final MenuItem itemDeleteAll = menu.findItem(R.id.delete_all);
- if (mAllCallsFragment != null && itemDeleteAll != null) {
- // If onPrepareOptionsMenu is called before fragments are loaded, don't do anything.
- final CallLogAdapter adapter = mAllCallsFragment.getAdapter();
- itemDeleteAll.setVisible(adapter != null && !adapter.isEmpty());
- }
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (!isSafeToCommitTransactions()) {
- return true;
- }
-
- if (item.getItemId() == android.R.id.home) {
- final Intent intent = new Intent(this, DialtactsActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- return true;
- } else if (item.getItemId() == R.id.delete_all) {
- ClearCallLogDialog.show(getFragmentManager());
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- mViewPagerTabs.onPageScrolled(position, positionOffset, positionOffsetPixels);
- }
-
- @Override
- public void onPageSelected(int position) {
- if (mIsResumed) {
- sendScreenViewForChildFragment(position);
- }
- mViewPagerTabs.onPageSelected(position);
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- mViewPagerTabs.onPageScrollStateChanged(state);
- }
-
- private void sendScreenViewForChildFragment(int position) {
- Logger.logScreenView(ScreenEvent.CALL_LOG_FILTER, this);
- }
-
- private int getRtlPosition(int position) {
- if (DialerUtils.isRtl()) {
- return mViewPagerAdapter.getCount() - 1 - position;
- }
- return position;
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
deleted file mode 100644
index 9cde0b65d..000000000
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ /dev/null
@@ -1,959 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import com.android.dialer.filterednumber.BlockNumberDialogFragment;
-import com.android.dialer.service.ExtendedCallInfoService;
-import com.android.dialerbind.ObjectFactory;
-import com.google.common.annotations.VisibleForTesting;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Trace;
-import android.preference.PreferenceManager;
-import android.provider.CallLog;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.PhoneCallDetails;
-import com.android.dialer.R;
-import com.android.dialer.calllog.calllogcache.CallLogCache;
-import com.android.dialer.contactinfo.ContactInfoCache;
-import com.android.dialer.contactinfo.ContactInfoCache.OnContactInfoChangedListener;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.VoicemailArchiveContract;
-import com.android.dialer.logging.InteractionEvent;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.util.PhoneNumberUtil;
-import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
-
-import java.util.HashMap;
-
-/**
- * Adapter class to fill in data for the Call Log.
- */
-public class CallLogAdapter extends GroupingListAdapter
- implements CallLogGroupBuilder.GroupCreator,
- VoicemailPlaybackPresenter.OnVoicemailDeletedListener {
-
- // Types of activities the call log adapter is used for
- public static final int ACTIVITY_TYPE_CALL_LOG = 1;
- public static final int ACTIVITY_TYPE_ARCHIVE = 2;
- public static final int ACTIVITY_TYPE_DIALTACTS = 3;
-
- /** Interface used to initiate a refresh of the content. */
- public interface CallFetcher {
- public void fetchCalls();
- }
-
- private static final int NO_EXPANDED_LIST_ITEM = -1;
- // ConcurrentHashMap doesn't store null values. Use this value for numbers which aren't blocked.
- private static final int NOT_BLOCKED = -1;
-
- private static final int VOICEMAIL_PROMO_CARD_POSITION = 0;
-
- protected static final int VIEW_TYPE_NORMAL = 0;
- private static final int VIEW_TYPE_VOICEMAIL_PROMO_CARD = 1;
-
- /**
- * The key for the show voicemail promo card preference which will determine whether the promo
- * card was permanently dismissed or not.
- */
- private static final String SHOW_VOICEMAIL_PROMO_CARD = "show_voicemail_promo_card";
- private static final boolean SHOW_VOICEMAIL_PROMO_CARD_DEFAULT = true;
-
- protected final Context mContext;
- private final ContactInfoHelper mContactInfoHelper;
- protected final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
- private final CallFetcher mCallFetcher;
- private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
-
- protected ContactInfoCache mContactInfoCache;
-
- private final int mActivityType;
-
- private static final String KEY_EXPANDED_POSITION = "expanded_position";
- private static final String KEY_EXPANDED_ROW_ID = "expanded_row_id";
-
- // Tracks the position of the currently expanded list item.
- private int mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
- // Tracks the rowId of the currently expanded list item, so the position can be updated if there
- // are any changes to the call log entries, such as additions or removals.
- private long mCurrentlyExpandedRowId = NO_EXPANDED_LIST_ITEM;
- private int mHiddenPosition = RecyclerView.NO_POSITION;
- private Uri mHiddenItemUri = null;
- private boolean mPendingHide = false;
- private BlockNumberDialogFragment.Callback mBlockedNumberDialogCallback =
- new BlockNumberDialogFragment.Callback() {
- @Override
- public void onFilterNumberSuccess() {
- Logger.logInteraction(
- InteractionEvent.BLOCK_NUMBER_CALL_LOG);
- notifyDataSetChanged();
- }
-
- @Override
- public void onUnfilterNumberSuccess() {
- Logger.logInteraction(
- InteractionEvent.UNBLOCK_NUMBER_CALL_LOG);
- notifyDataSetChanged();
- }
-
- @Override
- public void onChangeFilteredNumberUndo() {
- }
- };
- private CallLogListItemViewHolder.OnClickListener mBlockReportSpamListener;
-
- /**
- * Hashmap, keyed by call Id, used to track the day group for a call. As call log entries are
- * put into the primary call groups in {@link com.android.dialer.calllog.CallLogGroupBuilder},
- * they are also assigned a secondary "day group". This hashmap tracks the day group assigned
- * to all calls in the call log. This information is used to trigger the display of a day
- * group header above the call log entry at the start of a day group.
- * Note: Multiple calls are grouped into a single primary "call group" in the call log, and
- * the cursor used to bind rows includes all of these calls. When determining if a day group
- * change has occurred it is necessary to look at the last entry in the call log to determine
- * its day group. This hashmap provides a means of determining the previous day group without
- * having to reverse the cursor to the start of the previous day call log entry.
- */
- private HashMap<Long, Integer> mDayGroups = new HashMap<>();
-
- private boolean mLoading = true;
-
- private SharedPreferences mPrefs;
-
- private ContactsPreferences mContactsPreferences;
-
- protected boolean mShowVoicemailPromoCard = false;
-
- /** Instance of helper class for managing views. */
- private final CallLogListItemHelper mCallLogListItemHelper;
-
- /** Cache for repeated requests to Telecom/Telephony. */
- protected final CallLogCache mCallLogCache;
-
- /** Helper to group call log entries. */
- private final CallLogGroupBuilder mCallLogGroupBuilder;
-
- private ExtendedCallInfoService mExtendedCallInfoService;
-
- /**
- * The OnClickListener used to expand or collapse the action buttons of a call log entry.
- */
- private final View.OnClickListener mExpandCollapseListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder) v.getTag();
- if (viewHolder == null) {
- return;
- }
-
- if (mVoicemailPlaybackPresenter != null) {
- // Always reset the voicemail playback state on expand or collapse.
- mVoicemailPlaybackPresenter.resetAll();
- }
-
- if (viewHolder.getAdapterPosition() == mCurrentlyExpandedPosition) {
- // Hide actions, if the clicked item is the expanded item.
- viewHolder.showActions(false);
-
- mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
- mCurrentlyExpandedRowId = NO_EXPANDED_LIST_ITEM;
- } else {
- if (viewHolder.callType == CallLog.Calls.MISSED_TYPE) {
- CallLogAsyncTaskUtil.markCallAsRead(mContext, viewHolder.callIds);
- if (mActivityType == ACTIVITY_TYPE_DIALTACTS) {
- ((DialtactsActivity) v.getContext()).updateTabUnreadCounts();
- }
- }
- expandViewHolderActions(viewHolder);
- }
-
- }
- };
-
- /**
- * Click handler used to dismiss the promo card when the user taps the "ok" button.
- */
- private final View.OnClickListener mOkActionListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- dismissVoicemailPromoCard();
- }
- };
-
- /**
- * Click handler used to send the user to the voicemail settings screen and then dismiss the
- * promo card.
- */
- private final View.OnClickListener mVoicemailSettingsActionListener =
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent intent = new Intent(TelephonyManager.ACTION_CONFIGURE_VOICEMAIL);
- mContext.startActivity(intent);
- dismissVoicemailPromoCard();
- }
- };
-
- private void expandViewHolderActions(CallLogListItemViewHolder viewHolder) {
- // If another item is expanded, notify it that it has changed. Its actions will be
- // hidden when it is re-binded because we change mCurrentlyExpandedPosition below.
- if (mCurrentlyExpandedPosition != RecyclerView.NO_POSITION) {
- notifyItemChanged(mCurrentlyExpandedPosition);
- }
- // Show the actions for the clicked list item.
- viewHolder.showActions(true);
- mCurrentlyExpandedPosition = viewHolder.getAdapterPosition();
- mCurrentlyExpandedRowId = viewHolder.rowId;
- }
-
- /**
- * Expand the actions on a list item when focused in Talkback mode, to aid discoverability.
- */
- private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
- @Override
- public boolean onRequestSendAccessibilityEvent(
- ViewGroup host, View child, AccessibilityEvent event) {
- if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
- // Only expand if actions are not already expanded, because triggering the expand
- // function on clicks causes the action views to lose the focus indicator.
- CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder) host.getTag();
- if (mCurrentlyExpandedPosition != viewHolder.getAdapterPosition()) {
- if (mVoicemailPlaybackPresenter != null) {
- // Always reset the voicemail playback state on expand.
- mVoicemailPlaybackPresenter.resetAll();
- }
-
- expandViewHolderActions((CallLogListItemViewHolder) host.getTag());
- }
- }
- return super.onRequestSendAccessibilityEvent(host, child, event);
- }
- };
-
- protected final OnContactInfoChangedListener mOnContactInfoChangedListener =
- new OnContactInfoChangedListener() {
- @Override
- public void onContactInfoChanged() {
- notifyDataSetChanged();
- }
- };
-
- public CallLogAdapter(
- Context context,
- CallFetcher callFetcher,
- ContactInfoHelper contactInfoHelper,
- VoicemailPlaybackPresenter voicemailPlaybackPresenter,
- int activityType) {
- super(context);
-
- mContext = context;
- mCallFetcher = callFetcher;
- mContactInfoHelper = contactInfoHelper;
- mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
- if (mVoicemailPlaybackPresenter != null) {
- mVoicemailPlaybackPresenter.setOnVoicemailDeletedListener(this);
- }
-
- mActivityType = activityType;
-
- mContactInfoCache = new ContactInfoCache(
- mContactInfoHelper, mOnContactInfoChangedListener);
- if (!PermissionsUtil.hasContactsPermissions(context)) {
- mContactInfoCache.disableRequestProcessing();
- }
-
- Resources resources = mContext.getResources();
- CallTypeHelper callTypeHelper = new CallTypeHelper(resources);
-
- mCallLogCache = CallLogCache.getCallLogCache(mContext);
-
- PhoneCallDetailsHelper phoneCallDetailsHelper =
- new PhoneCallDetailsHelper(mContext, resources, mCallLogCache);
- mCallLogListItemHelper =
- new CallLogListItemHelper(phoneCallDetailsHelper, resources, mCallLogCache);
- mCallLogGroupBuilder = new CallLogGroupBuilder(this);
- mFilteredNumberAsyncQueryHandler =
- new FilteredNumberAsyncQueryHandler(mContext.getContentResolver());
-
- mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
- mContactsPreferences = new ContactsPreferences(mContext);
- maybeShowVoicemailPromoCard();
-
- mExtendedCallInfoService = ObjectFactory.newExtendedCallInfoService(context);
- mBlockReportSpamListener = new BlockReportSpamListener(
- ((Activity) mContext).getFragmentManager(), this,
- mExtendedCallInfoService, mFilteredNumberAsyncQueryHandler);
- setHasStableIds(true);
- }
-
- public void onSaveInstanceState(Bundle outState) {
- outState.putInt(KEY_EXPANDED_POSITION, mCurrentlyExpandedPosition);
- outState.putLong(KEY_EXPANDED_ROW_ID, mCurrentlyExpandedRowId);
- }
-
- public void onRestoreInstanceState(Bundle savedInstanceState) {
- if (savedInstanceState != null) {
- mCurrentlyExpandedPosition =
- savedInstanceState.getInt(KEY_EXPANDED_POSITION, RecyclerView.NO_POSITION);
- mCurrentlyExpandedRowId =
- savedInstanceState.getLong(KEY_EXPANDED_ROW_ID, NO_EXPANDED_LIST_ITEM);
- }
- }
-
- /**
- * Requery on background thread when {@link Cursor} changes.
- */
- @Override
- protected void onContentChanged() {
- mCallFetcher.fetchCalls();
- }
-
- public void setLoading(boolean loading) {
- mLoading = loading;
- }
-
- public boolean isEmpty() {
- if (mLoading) {
- // We don't want the empty state to show when loading.
- return false;
- } else {
- return getItemCount() == 0;
- }
- }
-
- public void invalidateCache() {
- mContactInfoCache.invalidate();
- }
-
- public void onResume() {
- if (PermissionsUtil.hasPermission(mContext, android.Manifest.permission.READ_CONTACTS)) {
- mContactInfoCache.start();
- }
- mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
- }
-
- public void onPause() {
- pauseCache();
-
- if (mHiddenItemUri != null) {
- CallLogAsyncTaskUtil.deleteVoicemail(mContext, mHiddenItemUri, null);
- }
- }
-
- @VisibleForTesting
- /* package */ void pauseCache() {
- mContactInfoCache.stop();
- mCallLogCache.reset();
- }
-
- @Override
- protected void addGroups(Cursor cursor) {
- mCallLogGroupBuilder.addGroups(cursor);
- }
-
- @Override
- public void addVoicemailGroups(Cursor cursor) {
- mCallLogGroupBuilder.addVoicemailGroups(cursor);
- }
-
- @Override
- public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- if (viewType == VIEW_TYPE_VOICEMAIL_PROMO_CARD) {
- return createVoicemailPromoCardViewHolder(parent);
- }
- return createCallLogEntryViewHolder(parent);
- }
-
- /**
- * Creates a new call log entry {@link ViewHolder}.
- *
- * @param parent the parent view.
- * @return The {@link ViewHolder}.
- */
- private ViewHolder createCallLogEntryViewHolder(ViewGroup parent) {
- LayoutInflater inflater = LayoutInflater.from(mContext);
- View view = inflater.inflate(R.layout.call_log_list_item, parent, false);
- CallLogListItemViewHolder viewHolder = CallLogListItemViewHolder.create(
- view,
- mContext,
- mBlockReportSpamListener,
- mExpandCollapseListener,
- mCallLogCache,
- mCallLogListItemHelper,
- mVoicemailPlaybackPresenter,
- mFilteredNumberAsyncQueryHandler,
- mBlockedNumberDialogCallback,
- mActivityType == ACTIVITY_TYPE_ARCHIVE);
-
- viewHolder.callLogEntryView.setTag(viewHolder);
- viewHolder.callLogEntryView.setAccessibilityDelegate(mAccessibilityDelegate);
-
- viewHolder.primaryActionView.setTag(viewHolder);
-
- return viewHolder;
- }
-
- /**
- * Binds the views in the entry to the data in the call log.
- * TODO: This gets called 20-30 times when Dialer starts up for a single call log entry and
- * should not. It invokes cross-process methods and the repeat execution can get costly.
- *
- * @param viewHolder The view corresponding to this entry.
- * @param position The position of the entry.
- */
- @Override
- public void onBindViewHolder(ViewHolder viewHolder, int position) {
- Trace.beginSection("onBindViewHolder: " + position);
-
- switch (getItemViewType(position)) {
- case VIEW_TYPE_VOICEMAIL_PROMO_CARD:
- bindVoicemailPromoCardViewHolder(viewHolder);
- break;
- default:
- bindCallLogListViewHolder(viewHolder, position);
- break;
- }
-
- Trace.endSection();
- }
-
- /**
- * Binds the promo card view holder.
- *
- * @param viewHolder The promo card view holder.
- */
- protected void bindVoicemailPromoCardViewHolder(ViewHolder viewHolder) {
- PromoCardViewHolder promoCardViewHolder = (PromoCardViewHolder) viewHolder;
-
- promoCardViewHolder.getSecondaryActionView()
- .setOnClickListener(mVoicemailSettingsActionListener);
- promoCardViewHolder.getPrimaryActionView().setOnClickListener(mOkActionListener);
- }
-
- /**
- * Binds the view holder for the call log list item view.
- *
- * @param viewHolder The call log list item view holder.
- * @param position The position of the list item.
- */
-
- private void bindCallLogListViewHolder(final ViewHolder viewHolder, final int position) {
- Cursor c = (Cursor) getItem(position);
- if (c == null) {
- return;
- }
-
- final String number = c.getString(CallLogQuery.NUMBER);
- final String countryIso = c.getString(CallLogQuery.COUNTRY_ISO);
- final CallLogListItemViewHolder views = (CallLogListItemViewHolder) viewHolder;
- boolean success = mFilteredNumberAsyncQueryHandler.isBlockedNumber(
- new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
- @Override
- public void onCheckComplete(Integer id) {
- views.blockId = id;
- if (mExtendedCallInfoService == null) {
- loadDataAndRender(views);
- } else {
- views.isSpamFeatureEnabled = true;
- mExtendedCallInfoService.getExtendedCallInfo(number, countryIso,
- new ExtendedCallInfoService.Listener() {
- @Override
- public void onComplete(boolean isSpam) {
- views.isSpam = isSpam;
- loadDataAndRender(views);
- }
- });
- }
- }
- }, number, countryIso);
- if (!success) {
- loadDataAndRender(views);
- }
- }
-
- private void loadDataAndRender(CallLogListItemViewHolder views) {
- int position = views.getAdapterPosition();
- Cursor c = (Cursor) getItem(position);
- if (c == null) {
- return;
- }
-
- int count = getGroupSize(position);
-
- final String number = c.getString(CallLogQuery.NUMBER);
- final String countryIso = c.getString(CallLogQuery.COUNTRY_ISO);
- final String postDialDigits = CompatUtils.isNCompatible()
- && mActivityType != ACTIVITY_TYPE_ARCHIVE ?
- c.getString(CallLogQuery.POST_DIAL_DIGITS) : "";
- final String viaNumber = CompatUtils.isNCompatible()
- && mActivityType != ACTIVITY_TYPE_ARCHIVE ?
- c.getString(CallLogQuery.VIA_NUMBER) : "";
- final int numberPresentation = c.getInt(CallLogQuery.NUMBER_PRESENTATION);
- final PhoneAccountHandle accountHandle = PhoneAccountUtils.getAccount(
- c.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME),
- c.getString(CallLogQuery.ACCOUNT_ID));
- final ContactInfo cachedContactInfo = ContactInfoHelper.getContactInfo(c);
- final boolean isVoicemailNumber =
- mCallLogCache.isVoicemailNumber(accountHandle, number);
-
- // Note: Binding of the action buttons is done as required in configureActionViews when the
- // user expands the actions ViewStub.
-
- ContactInfo info = ContactInfo.EMPTY;
- if (PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation) && !isVoicemailNumber) {
- // Lookup contacts with this number
- info = mContactInfoCache.getValue(number + postDialDigits,
- countryIso, cachedContactInfo);
- }
- CharSequence formattedNumber = info.formattedNumber == null
- ? null : PhoneNumberUtilsCompat.createTtsSpannable(info.formattedNumber);
-
- final PhoneCallDetails details = new PhoneCallDetails(
- mContext, number, numberPresentation, formattedNumber,
- postDialDigits, isVoicemailNumber);
- details.viaNumber = viaNumber;
- details.accountHandle = accountHandle;
- details.countryIso = countryIso;
- details.date = c.getLong(CallLogQuery.DATE);
- details.duration = c.getLong(CallLogQuery.DURATION);
- details.features = getCallFeatures(c, count);
- details.geocode = c.getString(CallLogQuery.GEOCODED_LOCATION);
- details.transcription = c.getString(CallLogQuery.TRANSCRIPTION);
- details.callTypes = getCallTypes(c, count);
-
- if (!c.isNull(CallLogQuery.DATA_USAGE)) {
- details.dataUsage = c.getLong(CallLogQuery.DATA_USAGE);
- }
-
- if (!TextUtils.isEmpty(info.name) || !TextUtils.isEmpty(info.nameAlternative)) {
- details.contactUri = info.lookupUri;
- details.namePrimary = info.name;
- details.nameAlternative = info.nameAlternative;
- details.nameDisplayOrder = mContactsPreferences.getDisplayOrder();
- details.numberType = info.type;
- details.numberLabel = info.label;
- details.photoUri = info.photoUri;
- details.sourceType = info.sourceType;
- details.objectId = info.objectId;
- details.contactUserType = info.userType;
- }
-
- views.info = info;
- views.rowId = c.getLong(CallLogQuery.ID);
- // Store values used when the actions ViewStub is inflated on expansion.
- views.number = number;
- views.postDialDigits = details.postDialDigits;
- views.displayNumber = details.displayNumber;
- views.numberPresentation = numberPresentation;
-
- views.accountHandle = accountHandle;
- // Stash away the Ids of the calls so that we can support deleting a row in the call log.
- views.callIds = getCallIds(c, count);
- views.isBusiness = mContactInfoHelper.isBusiness(info.sourceType);
- views.numberType = (String) Phone.getTypeLabel(mContext.getResources(), details.numberType,
- details.numberLabel);
- // Default case: an item in the call log.
- views.primaryActionView.setVisibility(View.VISIBLE);
- views.workIconView.setVisibility(
- details.contactUserType == ContactsUtils.USER_TYPE_WORK ? View.VISIBLE : View.GONE);
-
- // Check if the day group has changed and display a header if necessary.
- int currentGroup = getDayGroupForCall(views.rowId);
- int previousGroup = getPreviousDayGroup(c);
- if (currentGroup != previousGroup) {
- views.dayGroupHeader.setVisibility(View.VISIBLE);
- views.dayGroupHeader.setText(getGroupDescription(currentGroup));
- } else {
- views.dayGroupHeader.setVisibility(View.GONE);
- }
-
- if (mActivityType == ACTIVITY_TYPE_ARCHIVE) {
- views.callType = CallLog.Calls.VOICEMAIL_TYPE;
- views.voicemailUri = VoicemailArchiveContract.VoicemailArchive.buildWithId(c.getInt(
- c.getColumnIndex(VoicemailArchiveContract.VoicemailArchive._ID)))
- .toString();
-
- } else {
- if (details.callTypes[0] == CallLog.Calls.VOICEMAIL_TYPE ||
- details.callTypes[0] == CallLog.Calls.MISSED_TYPE) {
- details.isRead = c.getInt(CallLogQuery.IS_READ) == 1;
- }
- views.callType = c.getInt(CallLogQuery.CALL_TYPE);
- views.voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
- }
-
- // Reversely pass spam information from views since details is not constructed when spam
- // information comes back. This is used to render phone call details.
- details.isSpam = views.isSpam;
- render(views, details);
- }
-
- private void render(CallLogListItemViewHolder views, PhoneCallDetails details) {
- mCallLogListItemHelper.setPhoneCallDetails(views, details);
- if (mCurrentlyExpandedRowId == views.rowId) {
- // In case ViewHolders were added/removed, update the expanded position if the rowIds
- // match so that we can restore the correct expanded state on rebind.
- mCurrentlyExpandedPosition = views.getAdapterPosition();
- views.showActions(true);
- } else {
- views.showActions(false);
- }
- }
-
- private String getPreferredDisplayName(ContactInfo contactInfo) {
- if (mContactsPreferences.getDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_PRIMARY ||
- TextUtils.isEmpty(contactInfo.nameAlternative)) {
- return contactInfo.name;
- }
- return contactInfo.nameAlternative;
- }
-
- @Override
- public int getItemCount() {
- return super.getItemCount() + (mShowVoicemailPromoCard ? 1 : 0)
- - (mHiddenPosition != RecyclerView.NO_POSITION ? 1 : 0);
- }
-
- @Override
- public int getItemViewType(int position) {
- if (position == VOICEMAIL_PROMO_CARD_POSITION && mShowVoicemailPromoCard) {
- return VIEW_TYPE_VOICEMAIL_PROMO_CARD;
- }
- return super.getItemViewType(position);
- }
-
- /**
- * Retrieves an item at the specified position, taking into account the presence of a promo
- * card.
- *
- * @param position The position to retrieve.
- * @return The item at that position.
- */
- @Override
- public Object getItem(int position) {
- return super.getItem(position - (mShowVoicemailPromoCard ? 1 : 0)
- + ((mHiddenPosition != RecyclerView.NO_POSITION && position >= mHiddenPosition)
- ? 1 : 0));
- }
-
- @Override
- public long getItemId(int position) {
- Cursor cursor = (Cursor) getItem(position);
- if (cursor != null) {
- return cursor.getLong(CallLogQuery.ID);
- } else {
- return 0;
- }
- }
-
- @Override
- public int getGroupSize(int position) {
- return super.getGroupSize(position - (mShowVoicemailPromoCard ? 1 : 0));
- }
-
- protected boolean isCallLogActivity() {
- return mActivityType == ACTIVITY_TYPE_CALL_LOG;
- }
-
- /**
- * In order to implement the "undo" function, when a voicemail is "deleted" i.e. when the user
- * clicks the delete button, the deleted item is temporarily hidden from the list. If a user
- * clicks delete on a second item before the first item's undo option has expired, the first
- * item is immediately deleted so that only one item can be "undoed" at a time.
- */
- @Override
- public void onVoicemailDeleted(Uri uri) {
- if (mHiddenItemUri == null) {
- // Immediately hide the currently expanded card.
- mHiddenPosition = mCurrentlyExpandedPosition;
- notifyDataSetChanged();
- } else {
- // This means that there was a previous item that was hidden in the UI but not
- // yet deleted from the database (call it a "pending delete"). Delete this previous item
- // now since it is only possible to do one "undo" at a time.
- CallLogAsyncTaskUtil.deleteVoicemail(mContext, mHiddenItemUri, null);
-
- // Set pending hide action so that the current item is hidden only after the previous
- // item is permanently deleted.
- mPendingHide = true;
- }
-
- collapseExpandedCard();
-
- // Save the new hidden item uri in case it needs to be deleted from the database when
- // a user attempts to delete another item.
- mHiddenItemUri = uri;
- }
-
- private void collapseExpandedCard() {
- mCurrentlyExpandedRowId = NO_EXPANDED_LIST_ITEM;
- mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
- }
-
- /**
- * When the list is changing all stored position is no longer valid.
- */
- public void invalidatePositions() {
- mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
- mHiddenPosition = RecyclerView.NO_POSITION;
- }
-
- /**
- * When the user clicks "undo", the hidden item is unhidden.
- */
- @Override
- public void onVoicemailDeleteUndo() {
- mHiddenPosition = RecyclerView.NO_POSITION;
- mHiddenItemUri = null;
-
- mPendingHide = false;
- notifyDataSetChanged();
- }
-
- /**
- * This callback signifies that a database deletion has completed. This means that if there is
- * an item pending deletion, it will be hidden because the previous item that was in "undo" mode
- * has been removed from the database. Otherwise it simply resets the hidden state because there
- * are no pending deletes and thus no hidden items.
- */
- @Override
- public void onVoicemailDeletedInDatabase() {
- if (mPendingHide) {
- mHiddenPosition = mCurrentlyExpandedPosition;
- mPendingHide = false;
- } else {
- // There should no longer be any hidden item because it has been deleted from the
- // database.
- mHiddenPosition = RecyclerView.NO_POSITION;
- mHiddenItemUri = null;
- }
- }
-
- /**
- * Retrieves the day group of the previous call in the call log. Used to determine if the day
- * group has changed and to trigger display of the day group text.
- *
- * @param cursor The call log cursor.
- * @return The previous day group, or DAY_GROUP_NONE if this is the first call.
- */
- private int getPreviousDayGroup(Cursor cursor) {
- // We want to restore the position in the cursor at the end.
- int startingPosition = cursor.getPosition();
- int dayGroup = CallLogGroupBuilder.DAY_GROUP_NONE;
- if (cursor.moveToPrevious()) {
- // If the previous entry is hidden (deleted in the UI but not in the database), skip it
- // and check the card above it. A list with the voicemail promo card at the top will be
- // 1-indexed because the 0th index is the promo card iteself.
- int previousViewPosition = mShowVoicemailPromoCard ? startingPosition :
- startingPosition - 1;
- if (previousViewPosition != mHiddenPosition ||
- (previousViewPosition == mHiddenPosition && cursor.moveToPrevious())) {
- long previousRowId = cursor.getLong(CallLogQuery.ID);
- dayGroup = getDayGroupForCall(previousRowId);
- }
- }
- cursor.moveToPosition(startingPosition);
- return dayGroup;
- }
-
- /**
- * Given a call Id, look up the day group that the call belongs to. The day group data is
- * populated in {@link com.android.dialer.calllog.CallLogGroupBuilder}.
- *
- * @param callId The call to retrieve the day group for.
- * @return The day group for the call.
- */
- private int getDayGroupForCall(long callId) {
- if (mDayGroups.containsKey(callId)) {
- return mDayGroups.get(callId);
- }
- return CallLogGroupBuilder.DAY_GROUP_NONE;
- }
-
- /**
- * Returns the call types for the given number of items in the cursor.
- * <p>
- * It uses the next {@code count} rows in the cursor to extract the types.
- * <p>
- * It position in the cursor is unchanged by this function.
- */
- private int[] getCallTypes(Cursor cursor, int count) {
- if (mActivityType == ACTIVITY_TYPE_ARCHIVE) {
- return new int[] {CallLog.Calls.VOICEMAIL_TYPE};
- }
- int position = cursor.getPosition();
- int[] callTypes = new int[count];
- for (int index = 0; index < count; ++index) {
- callTypes[index] = cursor.getInt(CallLogQuery.CALL_TYPE);
- cursor.moveToNext();
- }
- cursor.moveToPosition(position);
- return callTypes;
- }
-
- /**
- * Determine the features which were enabled for any of the calls that make up a call log
- * entry.
- *
- * @param cursor The cursor.
- * @param count The number of calls for the current call log entry.
- * @return The features.
- */
- private int getCallFeatures(Cursor cursor, int count) {
- int features = 0;
- int position = cursor.getPosition();
- for (int index = 0; index < count; ++index) {
- features |= cursor.getInt(CallLogQuery.FEATURES);
- cursor.moveToNext();
- }
- cursor.moveToPosition(position);
- return features;
- }
-
- /**
- * Sets whether processing of requests for contact details should be enabled.
- *
- * This method should be called in tests to disable such processing of requests when not
- * needed.
- */
- @VisibleForTesting
- void disableRequestProcessingForTest() {
- // TODO: Remove this and test the cache directly.
- mContactInfoCache.disableRequestProcessing();
- }
-
- @VisibleForTesting
- void injectContactInfoForTest(String number, String countryIso, ContactInfo contactInfo) {
- // TODO: Remove this and test the cache directly.
- mContactInfoCache.injectContactInfoForTest(number, countryIso, contactInfo);
- }
-
- /**
- * Stores the day group associated with a call in the call log.
- *
- * @param rowId The row Id of the current call.
- * @param dayGroup The day group the call belongs in.
- */
- @Override
- public void setDayGroup(long rowId, int dayGroup) {
- if (!mDayGroups.containsKey(rowId)) {
- mDayGroups.put(rowId, dayGroup);
- }
- }
-
- /**
- * Clears the day group associations on re-bind of the call log.
- */
- @Override
- public void clearDayGroups() {
- mDayGroups.clear();
- }
-
- /**
- * Retrieves the call Ids represented by the current call log row.
- *
- * @param cursor Call log cursor to retrieve call Ids from.
- * @param groupSize Number of calls associated with the current call log row.
- * @return Array of call Ids.
- */
- private long[] getCallIds(final Cursor cursor, final int groupSize) {
- // We want to restore the position in the cursor at the end.
- int startingPosition = cursor.getPosition();
- long[] ids = new long[groupSize];
- // Copy the ids of the rows in the group.
- for (int index = 0; index < groupSize; ++index) {
- ids[index] = cursor.getLong(CallLogQuery.ID);
- cursor.moveToNext();
- }
- cursor.moveToPosition(startingPosition);
- return ids;
- }
-
- /**
- * Determines the description for a day group.
- *
- * @param group The day group to retrieve the description for.
- * @return The day group description.
- */
- private CharSequence getGroupDescription(int group) {
- if (group == CallLogGroupBuilder.DAY_GROUP_TODAY) {
- return mContext.getResources().getString(R.string.call_log_header_today);
- } else if (group == CallLogGroupBuilder.DAY_GROUP_YESTERDAY) {
- return mContext.getResources().getString(R.string.call_log_header_yesterday);
- } else {
- return mContext.getResources().getString(R.string.call_log_header_other);
- }
- }
-
- /**
- * Determines if the voicemail promo card should be shown or not. The voicemail promo card will
- * be shown as the first item in the voicemail tab.
- */
- private void maybeShowVoicemailPromoCard() {
- boolean showPromoCard = mPrefs.getBoolean(SHOW_VOICEMAIL_PROMO_CARD,
- SHOW_VOICEMAIL_PROMO_CARD_DEFAULT);
- mShowVoicemailPromoCard = mActivityType != ACTIVITY_TYPE_ARCHIVE &&
- (mVoicemailPlaybackPresenter != null) && showPromoCard;
- }
-
- /**
- * Dismisses the voicemail promo card and refreshes the call log.
- */
- private void dismissVoicemailPromoCard() {
- mPrefs.edit().putBoolean(SHOW_VOICEMAIL_PROMO_CARD, false).apply();
- mShowVoicemailPromoCard = false;
- notifyItemRemoved(VOICEMAIL_PROMO_CARD_POSITION);
- }
-
- /**
- * Creates the view holder for the voicemail promo card.
- *
- * @param parent The parent view.
- * @return The {@link ViewHolder}.
- */
- protected ViewHolder createVoicemailPromoCardViewHolder(ViewGroup parent) {
- LayoutInflater inflater = LayoutInflater.from(mContext);
- View view = inflater.inflate(R.layout.voicemail_promo_card, parent, false);
-
- PromoCardViewHolder viewHolder = PromoCardViewHolder.create(view);
- return viewHolder;
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
deleted file mode 100644
index b95d58e26..000000000
--- a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
+++ /dev/null
@@ -1,505 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.provider.CallLog;
-import android.provider.VoicemailContract.Voicemails;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.PhoneCallDetails;
-import com.android.dialer.compat.CallsSdkCompat;
-import com.android.dialer.database.VoicemailArchiveContract;
-import com.android.dialer.util.AsyncTaskExecutor;
-import com.android.dialer.util.AsyncTaskExecutors;
-import com.android.dialer.util.PhoneNumberUtil;
-import com.android.dialer.util.TelecomUtil;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Locale;
-
-public class CallLogAsyncTaskUtil {
- private static String TAG = CallLogAsyncTaskUtil.class.getSimpleName();
-
- /** The enumeration of {@link AsyncTask} objects used in this class. */
- public enum Tasks {
- DELETE_VOICEMAIL,
- DELETE_CALL,
- DELETE_BLOCKED_CALL,
- MARK_VOICEMAIL_READ,
- MARK_CALL_READ,
- GET_CALL_DETAILS,
- UPDATE_DURATION,
- GET_NUMBER_IN_CALL_HISTORY
- }
-
- private static final class CallDetailQuery {
-
- private static final String[] CALL_LOG_PROJECTION_INTERNAL = new String[] {
- CallLog.Calls.DATE,
- CallLog.Calls.DURATION,
- CallLog.Calls.NUMBER,
- CallLog.Calls.TYPE,
- CallLog.Calls.COUNTRY_ISO,
- CallLog.Calls.GEOCODED_LOCATION,
- CallLog.Calls.NUMBER_PRESENTATION,
- CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME,
- CallLog.Calls.PHONE_ACCOUNT_ID,
- CallLog.Calls.FEATURES,
- CallLog.Calls.DATA_USAGE,
- CallLog.Calls.TRANSCRIPTION
- };
- public static final String[] CALL_LOG_PROJECTION;
-
- static final int DATE_COLUMN_INDEX = 0;
- static final int DURATION_COLUMN_INDEX = 1;
- static final int NUMBER_COLUMN_INDEX = 2;
- static final int CALL_TYPE_COLUMN_INDEX = 3;
- static final int COUNTRY_ISO_COLUMN_INDEX = 4;
- static final int GEOCODED_LOCATION_COLUMN_INDEX = 5;
- static final int NUMBER_PRESENTATION_COLUMN_INDEX = 6;
- static final int ACCOUNT_COMPONENT_NAME = 7;
- static final int ACCOUNT_ID = 8;
- static final int FEATURES = 9;
- static final int DATA_USAGE = 10;
- static final int TRANSCRIPTION_COLUMN_INDEX = 11;
- static final int POST_DIAL_DIGITS = 12;
- static final int VIA_NUMBER = 13;
-
- static {
- ArrayList<String> projectionList = new ArrayList<>();
- projectionList.addAll(Arrays.asList(CALL_LOG_PROJECTION_INTERNAL));
- if (CompatUtils.isNCompatible()) {
- projectionList.add(CallsSdkCompat.POST_DIAL_DIGITS);
- projectionList.add(CallsSdkCompat.VIA_NUMBER);
- }
- projectionList.trimToSize();
- CALL_LOG_PROJECTION = projectionList.toArray(new String[projectionList.size()]);
- }
- }
-
- private static class CallLogDeleteBlockedCallQuery {
- static final String[] PROJECTION = new String[] {
- CallLog.Calls._ID,
- CallLog.Calls.DATE
- };
-
- static final int ID_COLUMN_INDEX = 0;
- static final int DATE_COLUMN_INDEX = 1;
- }
-
- public interface CallLogAsyncTaskListener {
- void onDeleteCall();
- void onDeleteVoicemail();
- void onGetCallDetails(PhoneCallDetails[] details);
- }
-
- public interface OnGetNumberInCallHistoryListener {
- void onComplete(boolean inCallHistory);
- }
-
- public interface OnCallLogQueryFinishedListener {
- void onQueryFinished(boolean hasEntry);
- }
-
- // Try to identify if a call log entry corresponds to a number which was blocked. We match by
- // by comparing its creation time to the time it was added in the InCallUi and seeing if they
- // fall within a certain threshold.
- private static final int MATCH_BLOCKED_CALL_THRESHOLD_MS = 3000;
-
- private static AsyncTaskExecutor sAsyncTaskExecutor;
-
- private static void initTaskExecutor() {
- sAsyncTaskExecutor = AsyncTaskExecutors.createThreadPoolExecutor();
- }
-
- public static void getCallDetails(
- final Context context,
- final Uri[] callUris,
- final CallLogAsyncTaskListener callLogAsyncTaskListener) {
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.GET_CALL_DETAILS,
- new AsyncTask<Void, Void, PhoneCallDetails[]>() {
- @Override
- public PhoneCallDetails[] doInBackground(Void... params) {
- // TODO: All calls correspond to the same person, so make a single lookup.
- final int numCalls = callUris.length;
- PhoneCallDetails[] details = new PhoneCallDetails[numCalls];
- try {
- for (int index = 0; index < numCalls; ++index) {
- details[index] =
- getPhoneCallDetailsForUri(context, callUris[index]);
- }
- return details;
- } catch (IllegalArgumentException e) {
- // Something went wrong reading in our primary data.
- Log.w(TAG, "Invalid URI starting call details", e);
- return null;
- }
- }
-
- @Override
- public void onPostExecute(PhoneCallDetails[] phoneCallDetails) {
- if (callLogAsyncTaskListener != null) {
- callLogAsyncTaskListener.onGetCallDetails(phoneCallDetails);
- }
- }
- });
- }
-
- /**
- * Return the phone call details for a given call log URI.
- */
- private static PhoneCallDetails getPhoneCallDetailsForUri(Context context, Uri callUri) {
- Cursor cursor = context.getContentResolver().query(
- callUri, CallDetailQuery.CALL_LOG_PROJECTION, null, null, null);
-
- try {
- if (cursor == null || !cursor.moveToFirst()) {
- throw new IllegalArgumentException("Cannot find content: " + callUri);
- }
-
- // Read call log.
- final String countryIso = cursor.getString(CallDetailQuery.COUNTRY_ISO_COLUMN_INDEX);
- final String number = cursor.getString(CallDetailQuery.NUMBER_COLUMN_INDEX);
- final String postDialDigits = CompatUtils.isNCompatible()
- ? cursor.getString(CallDetailQuery.POST_DIAL_DIGITS) : "";
- final String viaNumber = CompatUtils.isNCompatible() ?
- cursor.getString(CallDetailQuery.VIA_NUMBER) : "";
- final int numberPresentation =
- cursor.getInt(CallDetailQuery.NUMBER_PRESENTATION_COLUMN_INDEX);
-
- final PhoneAccountHandle accountHandle = PhoneAccountUtils.getAccount(
- cursor.getString(CallDetailQuery.ACCOUNT_COMPONENT_NAME),
- cursor.getString(CallDetailQuery.ACCOUNT_ID));
-
- // If this is not a regular number, there is no point in looking it up in the contacts.
- ContactInfoHelper contactInfoHelper =
- new ContactInfoHelper(context, GeoUtil.getCurrentCountryIso(context));
- boolean isVoicemail = PhoneNumberUtil.isVoicemailNumber(context, accountHandle, number);
- boolean shouldLookupNumber =
- PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation) && !isVoicemail;
- ContactInfo info = ContactInfo.EMPTY;
-
- if (shouldLookupNumber) {
- ContactInfo lookupInfo = contactInfoHelper.lookupNumber(number, countryIso);
- info = lookupInfo != null ? lookupInfo : ContactInfo.EMPTY;
- }
-
- PhoneCallDetails details = new PhoneCallDetails(
- context, number, numberPresentation, info.formattedNumber,
- postDialDigits, isVoicemail);
-
- details.viaNumber = viaNumber;
- details.accountHandle = accountHandle;
- details.contactUri = info.lookupUri;
- details.namePrimary = info.name;
- details.nameAlternative = info.nameAlternative;
- details.numberType = info.type;
- details.numberLabel = info.label;
- details.photoUri = info.photoUri;
- details.sourceType = info.sourceType;
- details.objectId = info.objectId;
-
- details.callTypes = new int[] {
- cursor.getInt(CallDetailQuery.CALL_TYPE_COLUMN_INDEX)
- };
- details.date = cursor.getLong(CallDetailQuery.DATE_COLUMN_INDEX);
- details.duration = cursor.getLong(CallDetailQuery.DURATION_COLUMN_INDEX);
- details.features = cursor.getInt(CallDetailQuery.FEATURES);
- details.geocode = cursor.getString(CallDetailQuery.GEOCODED_LOCATION_COLUMN_INDEX);
- details.transcription = cursor.getString(CallDetailQuery.TRANSCRIPTION_COLUMN_INDEX);
-
- details.countryIso = !TextUtils.isEmpty(countryIso) ? countryIso
- : GeoUtil.getCurrentCountryIso(context);
-
- if (!cursor.isNull(CallDetailQuery.DATA_USAGE)) {
- details.dataUsage = cursor.getLong(CallDetailQuery.DATA_USAGE);
- }
-
- return details;
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
-
- /**
- * Delete specified calls from the call log.
- *
- * @param context The context.
- * @param callIds String of the callIds to delete from the call log, delimited by commas (",").
- * @param callLogAsyncTaskListener The listener to invoke after the entries have been deleted.
- */
- public static void deleteCalls(
- final Context context,
- final String callIds,
- final CallLogAsyncTaskListener callLogAsyncTaskListener) {
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.DELETE_CALL, new AsyncTask<Void, Void, Void>() {
- @Override
- public Void doInBackground(Void... params) {
- context.getContentResolver().delete(
- TelecomUtil.getCallLogUri(context),
- CallLog.Calls._ID + " IN (" + callIds + ")", null);
- return null;
- }
-
- @Override
- public void onPostExecute(Void result) {
- if (callLogAsyncTaskListener != null) {
- callLogAsyncTaskListener.onDeleteCall();
- }
- }
- });
- }
-
- /**
- * Deletes the last call made by the number within a threshold of the call time added in the
- * call log, assuming it is a blocked call for which no entry should be shown.
- *
- * @param context The context.
- * @param number Number of blocked call, for which to delete the call log entry.
- * @param timeAddedMs The time the number was added to InCall, in milliseconds.
- * @param listener The listener to invoke after looking up for a call log entry matching the
- * number and time added.
- */
- public static void deleteBlockedCall(
- final Context context,
- final String number,
- final long timeAddedMs,
- final OnCallLogQueryFinishedListener listener) {
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.DELETE_BLOCKED_CALL, new AsyncTask<Void, Void, Long>() {
- @Override
- public Long doInBackground(Void... params) {
- // First, lookup the call log entry of the most recent call with this number.
- Cursor cursor = context.getContentResolver().query(
- TelecomUtil.getCallLogUri(context),
- CallLogDeleteBlockedCallQuery.PROJECTION,
- CallLog.Calls.NUMBER + "= ?",
- new String[] { number },
- CallLog.Calls.DATE + " DESC LIMIT 1");
-
- // If match is found, delete this call log entry and return the call log entry id.
- if (cursor.moveToFirst()) {
- long creationTime =
- cursor.getLong(CallLogDeleteBlockedCallQuery.DATE_COLUMN_INDEX);
- if (timeAddedMs > creationTime
- && timeAddedMs - creationTime < MATCH_BLOCKED_CALL_THRESHOLD_MS) {
- long callLogEntryId =
- cursor.getLong(CallLogDeleteBlockedCallQuery.ID_COLUMN_INDEX);
- context.getContentResolver().delete(
- TelecomUtil.getCallLogUri(context),
- CallLog.Calls._ID + " IN (" + callLogEntryId + ")",
- null);
- return callLogEntryId;
- }
- }
- return (long) -1;
- }
-
- @Override
- public void onPostExecute(Long callLogEntryId) {
- if (listener != null) {
- listener.onQueryFinished(callLogEntryId >= 0);
- }
- }
- });
- }
-
-
- public static void markVoicemailAsRead(final Context context, final Uri voicemailUri) {
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.MARK_VOICEMAIL_READ, new AsyncTask<Void, Void, Void>() {
- @Override
- public Void doInBackground(Void... params) {
- ContentValues values = new ContentValues();
- values.put(Voicemails.IS_READ, true);
- context.getContentResolver().update(
- voicemailUri, values, Voicemails.IS_READ + " = 0", null);
-
- Intent intent = new Intent(context, CallLogNotificationsService.class);
- intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_VOICEMAILS_AS_OLD);
- context.startService(intent);
- return null;
- }
- });
- }
-
- public static void deleteVoicemail(
- final Context context,
- final Uri voicemailUri,
- final CallLogAsyncTaskListener callLogAsyncTaskListener) {
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.DELETE_VOICEMAIL, new AsyncTask<Void, Void, Void>() {
- @Override
- public Void doInBackground(Void... params) {
- context.getContentResolver().delete(voicemailUri, null, null);
- return null;
- }
-
- @Override
- public void onPostExecute(Void result) {
- if (callLogAsyncTaskListener != null) {
- callLogAsyncTaskListener.onDeleteVoicemail();
- }
- }
- });
- }
-
- public static void markCallAsRead(final Context context, final long[] callIds) {
- if (!PermissionsUtil.hasPhonePermissions(context)) {
- return;
- }
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.MARK_CALL_READ, new AsyncTask<Void, Void, Void>() {
- @Override
- public Void doInBackground(Void... params) {
-
- StringBuilder where = new StringBuilder();
- where.append(CallLog.Calls.TYPE).append(" = ").append(CallLog.Calls.MISSED_TYPE);
- where.append(" AND ");
-
- Long[] callIdLongs = new Long[callIds.length];
- for (int i = 0; i < callIds.length; i++) {
- callIdLongs[i] = callIds[i];
- }
- where.append(CallLog.Calls._ID).append(
- " IN (" + TextUtils.join(",", callIdLongs) + ")");
-
- ContentValues values = new ContentValues(1);
- values.put(CallLog.Calls.IS_READ, "1");
- context.getContentResolver().update(
- CallLog.Calls.CONTENT_URI, values, where.toString(), null);
- return null;
- }
- });
- }
-
- /**
- * Updates the duration of a voicemail call log entry if the duration given is greater than 0,
- * and if if the duration currently in the database is less than or equal to 0 (non-existent).
- */
- public static void updateVoicemailDuration(
- final Context context,
- final Uri voicemailUri,
- final long duration) {
- if (duration <= 0 || !PermissionsUtil.hasPhonePermissions(context)) {
- return;
- }
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.UPDATE_DURATION, new AsyncTask<Void, Void, Void>() {
- @Override
- public Void doInBackground(Void... params) {
- ContentResolver contentResolver = context.getContentResolver();
- Cursor cursor = contentResolver.query(
- voicemailUri,
- new String[] { VoicemailArchiveContract.VoicemailArchive.DURATION },
- null, null, null);
- if (cursor != null && cursor.moveToFirst() && cursor.getInt(
- cursor.getColumnIndex(
- VoicemailArchiveContract.VoicemailArchive.DURATION)) <= 0) {
- ContentValues values = new ContentValues(1);
- values.put(CallLog.Calls.DURATION, duration);
- context.getContentResolver().update(voicemailUri, values, null, null);
- }
- return null;
- }
- });
- }
-
- /**
- * Checks if the number is in the call history.
- */
- public static void getNumberInCallHistory(
- final Context context,
- final String number,
- final OnGetNumberInCallHistoryListener listener) {
- Preconditions.checkNotNull(listener);
- if (!PermissionsUtil.hasPhonePermissions(context)) {
- return;
- }
- if (sAsyncTaskExecutor == null) {
- initTaskExecutor();
- }
-
- sAsyncTaskExecutor.submit(Tasks.GET_NUMBER_IN_CALL_HISTORY,
- new AsyncTask<Void, Void, Boolean>() {
- @Override
- public Boolean doInBackground(Void... params) {
- try (Cursor cursor = context.getContentResolver().query(
- TelecomUtil.getCallLogUri(context),
- new String[] {CallLog.Calls._ID},
- CallLog.Calls.NUMBER + " = ?",
- new String[] {number},
- null)) {
- return cursor != null && cursor.getCount() > 0;
- }
- }
-
- @Override
- public void onPostExecute(Boolean inCallHistory) {
- listener.onComplete(inCallHistory);
- }
- });
- }
-
- @VisibleForTesting
- public static void resetForTest() {
- sAsyncTaskExecutor = null;
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
deleted file mode 100644
index 67b72a5a3..000000000
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ /dev/null
@@ -1,530 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.KeyguardManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.provider.CallLog;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract;
-import android.support.annotation.Nullable;
-import android.support.v13.app.FragmentCompat;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.R;
-import com.android.dialer.list.ListsFragment;
-import com.android.dialer.util.EmptyLoader;
-import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
-import com.android.dialer.widget.EmptyContentView;
-import com.android.dialer.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener;
-import com.android.dialerbind.ObjectFactory;
-
-import static android.Manifest.permission.READ_CALL_LOG;
-
-/**
- * Displays a list of call log entries. To filter for a particular kind of call
- * (all, missed or voicemails), specify it in the constructor.
- */
-public class CallLogFragment extends Fragment implements CallLogQueryHandler.Listener,
- CallLogAdapter.CallFetcher, OnEmptyViewActionButtonClickedListener,
- FragmentCompat.OnRequestPermissionsResultCallback {
- private static final String TAG = "CallLogFragment";
-
- /**
- * ID of the empty loader to defer other fragments.
- */
- private static final int EMPTY_LOADER_ID = 0;
-
- private static final String KEY_FILTER_TYPE = "filter_type";
- private static final String KEY_LOG_LIMIT = "log_limit";
- private static final String KEY_DATE_LIMIT = "date_limit";
- private static final String KEY_IS_CALL_LOG_ACTIVITY = "is_call_log_activity";
-
- // No limit specified for the number of logs to show; use the CallLogQueryHandler's default.
- private static final int NO_LOG_LIMIT = -1;
- // No date-based filtering.
- private static final int NO_DATE_LIMIT = 0;
-
- private static final int READ_CALL_LOG_PERMISSION_REQUEST_CODE = 1;
-
- private static final int EVENT_UPDATE_DISPLAY = 1;
-
- private static final long MILLIS_IN_MINUTE = 60 * 1000;
-
- private RecyclerView mRecyclerView;
- private LinearLayoutManager mLayoutManager;
- private CallLogAdapter mAdapter;
- private CallLogQueryHandler mCallLogQueryHandler;
- private boolean mScrollToTop;
-
-
- private EmptyContentView mEmptyListView;
- private KeyguardManager mKeyguardManager;
-
- private boolean mEmptyLoaderRunning;
- private boolean mCallLogFetched;
- private boolean mVoicemailStatusFetched;
-
- private final Handler mDisplayUpdateHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_UPDATE_DISPLAY:
- refreshData();
- rescheduleDisplayUpdate();
- break;
- }
- }
- };
-
- private final Handler mHandler = new Handler();
-
- protected class CustomContentObserver extends ContentObserver {
- public CustomContentObserver() {
- super(mHandler);
- }
- @Override
- public void onChange(boolean selfChange) {
- mRefreshDataRequired = true;
- }
- }
-
- // See issue 6363009
- private final ContentObserver mCallLogObserver = new CustomContentObserver();
- private final ContentObserver mContactsObserver = new CustomContentObserver();
- private boolean mRefreshDataRequired = true;
-
- private boolean mHasReadCallLogPermission = false;
-
- // Exactly same variable is in Fragment as a package private.
- private boolean mMenuVisible = true;
-
- // Default to all calls.
- private int mCallTypeFilter = CallLogQueryHandler.CALL_TYPE_ALL;
-
- // Log limit - if no limit is specified, then the default in {@link CallLogQueryHandler}
- // will be used.
- private int mLogLimit = NO_LOG_LIMIT;
-
- // Date limit (in millis since epoch) - when non-zero, only calls which occurred on or after
- // the date filter are included. If zero, no date-based filtering occurs.
- private long mDateLimit = NO_DATE_LIMIT;
-
- /*
- * True if this instance of the CallLogFragment shown in the CallLogActivity.
- */
- private boolean mIsCallLogActivity = false;
-
- public interface HostInterface {
- public void showDialpad();
- }
-
- public CallLogFragment() {
- this(CallLogQueryHandler.CALL_TYPE_ALL, NO_LOG_LIMIT);
- }
-
- public CallLogFragment(int filterType) {
- this(filterType, NO_LOG_LIMIT);
- }
-
- public CallLogFragment(int filterType, boolean isCallLogActivity) {
- this(filterType, NO_LOG_LIMIT);
- mIsCallLogActivity = isCallLogActivity;
- }
-
- public CallLogFragment(int filterType, int logLimit) {
- this(filterType, logLimit, NO_DATE_LIMIT);
- }
-
- /**
- * Creates a call log fragment, filtering to include only calls of the desired type, occurring
- * after the specified date.
- * @param filterType type of calls to include.
- * @param dateLimit limits results to calls occurring on or after the specified date.
- */
- public CallLogFragment(int filterType, long dateLimit) {
- this(filterType, NO_LOG_LIMIT, dateLimit);
- }
-
- /**
- * Creates a call log fragment, filtering to include only calls of the desired type, occurring
- * after the specified date. Also provides a means to limit the number of results returned.
- * @param filterType type of calls to include.
- * @param logLimit limits the number of results to return.
- * @param dateLimit limits results to calls occurring on or after the specified date.
- */
- public CallLogFragment(int filterType, int logLimit, long dateLimit) {
- mCallTypeFilter = filterType;
- mLogLimit = logLimit;
- mDateLimit = dateLimit;
- }
-
- @Override
- public void onCreate(Bundle state) {
- super.onCreate(state);
- if (state != null) {
- mCallTypeFilter = state.getInt(KEY_FILTER_TYPE, mCallTypeFilter);
- mLogLimit = state.getInt(KEY_LOG_LIMIT, mLogLimit);
- mDateLimit = state.getLong(KEY_DATE_LIMIT, mDateLimit);
- mIsCallLogActivity = state.getBoolean(KEY_IS_CALL_LOG_ACTIVITY, mIsCallLogActivity);
- }
-
- final Activity activity = getActivity();
- final ContentResolver resolver = activity.getContentResolver();
- String currentCountryIso = GeoUtil.getCurrentCountryIso(activity);
- mCallLogQueryHandler = new CallLogQueryHandler(activity, resolver, this, mLogLimit);
- mKeyguardManager =
- (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
- resolver.registerContentObserver(CallLog.CONTENT_URI, true, mCallLogObserver);
- resolver.registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true,
- mContactsObserver);
- setHasOptionsMenu(true);
- }
-
- /** Called by the CallLogQueryHandler when the list of calls has been fetched or updated. */
- @Override
- public boolean onCallsFetched(Cursor cursor) {
- if (getActivity() == null || getActivity().isFinishing()) {
- // Return false; we did not take ownership of the cursor
- return false;
- }
- mAdapter.invalidatePositions();
- mAdapter.setLoading(false);
- mAdapter.changeCursor(cursor);
- // This will update the state of the "Clear call log" menu item.
- getActivity().invalidateOptionsMenu();
-
- boolean showListView = cursor != null && cursor.getCount() > 0;
- mRecyclerView.setVisibility(showListView ? View.VISIBLE : View.GONE);
- mEmptyListView.setVisibility(!showListView ? View.VISIBLE : View.GONE);
-
- if (mScrollToTop) {
- // The smooth-scroll animation happens over a fixed time period.
- // As a result, if it scrolls through a large portion of the list,
- // each frame will jump so far from the previous one that the user
- // will not experience the illusion of downward motion. Instead,
- // if we're not already near the top of the list, we instantly jump
- // near the top, and animate from there.
- if (mLayoutManager.findFirstVisibleItemPosition() > 5) {
- // TODO: Jump to near the top, then begin smooth scroll.
- mRecyclerView.smoothScrollToPosition(0);
- }
- // Workaround for framework issue: the smooth-scroll doesn't
- // occur if setSelection() is called immediately before.
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (getActivity() == null || getActivity().isFinishing()) {
- return;
- }
- mRecyclerView.smoothScrollToPosition(0);
- }
- });
-
- mScrollToTop = false;
- }
- mCallLogFetched = true;
- destroyEmptyLoaderIfAllDataFetched();
- return true;
- }
-
- /**
- * Called by {@link CallLogQueryHandler} after a successful query to voicemail status provider.
- */
- @Override
- public void onVoicemailStatusFetched(Cursor statusCursor) {
- Activity activity = getActivity();
- if (activity == null || activity.isFinishing()) {
- return;
- }
-
- mVoicemailStatusFetched = true;
- destroyEmptyLoaderIfAllDataFetched();
- }
-
- private void destroyEmptyLoaderIfAllDataFetched() {
- if (mCallLogFetched && mVoicemailStatusFetched && mEmptyLoaderRunning) {
- mEmptyLoaderRunning = false;
- getLoaderManager().destroyLoader(EMPTY_LOADER_ID);
- }
- }
-
- @Override
- public void onVoicemailUnreadCountFetched(Cursor cursor) {}
-
- @Override
- public void onMissedCallsUnreadCountFetched(Cursor cursor) {}
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
- View view = inflater.inflate(R.layout.call_log_fragment, container, false);
- setupView(view, null);
- return view;
- }
-
- protected void setupView(
- View view, @Nullable VoicemailPlaybackPresenter voicemailPlaybackPresenter) {
- mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
- mRecyclerView.setHasFixedSize(true);
- mLayoutManager = new LinearLayoutManager(getActivity());
- mRecyclerView.setLayoutManager(mLayoutManager);
- mEmptyListView = (EmptyContentView) view.findViewById(R.id.empty_list_view);
- mEmptyListView.setImage(R.drawable.empty_call_log);
- mEmptyListView.setActionClickedListener(this);
-
- int activityType = mIsCallLogActivity ? CallLogAdapter.ACTIVITY_TYPE_CALL_LOG :
- CallLogAdapter.ACTIVITY_TYPE_DIALTACTS;
- String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
- mAdapter = ObjectFactory.newCallLogAdapter(
- getActivity(),
- this,
- new ContactInfoHelper(getActivity(), currentCountryIso),
- voicemailPlaybackPresenter,
- activityType);
- mRecyclerView.setAdapter(mAdapter);
- fetchCalls();
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- updateEmptyMessage(mCallTypeFilter);
- mAdapter.onRestoreInstanceState(savedInstanceState);
- }
-
- @Override
- public void onStart() {
- // Start the empty loader now to defer other fragments. We destroy it when both calllog
- // and the voicemail status are fetched.
- getLoaderManager().initLoader(EMPTY_LOADER_ID, null,
- new EmptyLoader.Callback(getActivity()));
- mEmptyLoaderRunning = true;
- super.onStart();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- final boolean hasReadCallLogPermission =
- PermissionsUtil.hasPermission(getActivity(), READ_CALL_LOG);
- if (!mHasReadCallLogPermission && hasReadCallLogPermission) {
- // We didn't have the permission before, and now we do. Force a refresh of the call log.
- // Note that this code path always happens on a fresh start, but mRefreshDataRequired
- // is already true in that case anyway.
- mRefreshDataRequired = true;
- updateEmptyMessage(mCallTypeFilter);
- }
-
- mHasReadCallLogPermission = hasReadCallLogPermission;
- refreshData();
- mAdapter.onResume();
-
- rescheduleDisplayUpdate();
- }
-
- @Override
- public void onPause() {
- cancelDisplayUpdate();
- mAdapter.onPause();
- super.onPause();
- }
-
- @Override
- public void onStop() {
- updateOnTransition();
-
- super.onStop();
- }
-
- @Override
- public void onDestroy() {
- mAdapter.changeCursor(null);
-
- getActivity().getContentResolver().unregisterContentObserver(mCallLogObserver);
- getActivity().getContentResolver().unregisterContentObserver(mContactsObserver);
- super.onDestroy();
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putInt(KEY_FILTER_TYPE, mCallTypeFilter);
- outState.putInt(KEY_LOG_LIMIT, mLogLimit);
- outState.putLong(KEY_DATE_LIMIT, mDateLimit);
- outState.putBoolean(KEY_IS_CALL_LOG_ACTIVITY, mIsCallLogActivity);
-
- mAdapter.onSaveInstanceState(outState);
- }
-
- @Override
- public void fetchCalls() {
- mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit);
- if (!mIsCallLogActivity) {
- ((ListsFragment) getParentFragment()).updateTabUnreadCounts();
- }
- }
-
- private void updateEmptyMessage(int filterType) {
- final Context context = getActivity();
- if (context == null) {
- return;
- }
-
- if (!PermissionsUtil.hasPermission(context, READ_CALL_LOG)) {
- mEmptyListView.setDescription(R.string.permission_no_calllog);
- mEmptyListView.setActionLabel(R.string.permission_single_turn_on);
- return;
- }
-
- final int messageId;
- switch (filterType) {
- case Calls.MISSED_TYPE:
- messageId = R.string.call_log_missed_empty;
- break;
- case Calls.VOICEMAIL_TYPE:
- messageId = R.string.call_log_voicemail_empty;
- break;
- case CallLogQueryHandler.CALL_TYPE_ALL:
- messageId = R.string.call_log_all_empty;
- break;
- default:
- throw new IllegalArgumentException("Unexpected filter type in CallLogFragment: "
- + filterType);
- }
- mEmptyListView.setDescription(messageId);
- if (mIsCallLogActivity) {
- mEmptyListView.setActionLabel(EmptyContentView.NO_LABEL);
- } else if (filterType == CallLogQueryHandler.CALL_TYPE_ALL) {
- mEmptyListView.setActionLabel(R.string.call_log_all_empty_action);
- }
- }
-
- CallLogAdapter getAdapter() {
- return mAdapter;
- }
-
- @Override
- public void setMenuVisibility(boolean menuVisible) {
- super.setMenuVisibility(menuVisible);
- if (mMenuVisible != menuVisible) {
- mMenuVisible = menuVisible;
- if (!menuVisible) {
- updateOnTransition();
- } else if (isResumed()) {
- refreshData();
- }
- }
- }
-
- /** Requests updates to the data to be shown. */
- private void refreshData() {
- // Prevent unnecessary refresh.
- if (mRefreshDataRequired) {
- // Mark all entries in the contact info cache as out of date, so they will be looked up
- // again once being shown.
- mAdapter.invalidateCache();
- mAdapter.setLoading(true);
-
- fetchCalls();
- mCallLogQueryHandler.fetchVoicemailStatus();
- mCallLogQueryHandler.fetchMissedCallsUnreadCount();
- updateOnTransition();
- mRefreshDataRequired = false;
- } else {
- // Refresh the display of the existing data to update the timestamp text descriptions.
- mAdapter.notifyDataSetChanged();
- }
- }
-
- /**
- * Updates the voicemail notification state.
- *
- * TODO: Move to CallLogActivity
- */
- private void updateOnTransition() {
- // We don't want to update any call data when keyguard is on because the user has likely not
- // seen the new calls yet.
- // This might be called before onCreate() and thus we need to check null explicitly.
- if (mKeyguardManager != null && !mKeyguardManager.inKeyguardRestrictedInputMode()
- && mCallTypeFilter == Calls.VOICEMAIL_TYPE) {
- CallLogNotificationsHelper.updateVoicemailNotifications(getActivity());
- }
- }
-
- @Override
- public void onEmptyViewActionButtonClicked() {
- final Activity activity = getActivity();
- if (activity == null) {
- return;
- }
-
- if (!PermissionsUtil.hasPermission(activity, READ_CALL_LOG)) {
- FragmentCompat.requestPermissions(this, new String[] {READ_CALL_LOG},
- READ_CALL_LOG_PERMISSION_REQUEST_CODE);
- } else if (!mIsCallLogActivity) {
- // Show dialpad if we are not in the call log activity.
- ((HostInterface) activity).showDialpad();
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions,
- int[] grantResults) {
- if (requestCode == READ_CALL_LOG_PERMISSION_REQUEST_CODE) {
- if (grantResults.length >= 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
- // Force a refresh of the data since we were missing the permission before this.
- mRefreshDataRequired = true;
- }
- }
- }
-
- /**
- * Schedules an update to the relative call times (X mins ago).
- */
- private void rescheduleDisplayUpdate() {
- if (!mDisplayUpdateHandler.hasMessages(EVENT_UPDATE_DISPLAY)) {
- long time = System.currentTimeMillis();
- // This value allows us to change the display relatively close to when the time changes
- // from one minute to the next.
- long millisUtilNextMinute = MILLIS_IN_MINUTE - (time % MILLIS_IN_MINUTE);
- mDisplayUpdateHandler.sendEmptyMessageDelayed(
- EVENT_UPDATE_DISPLAY, millisUtilNextMinute);
- }
- }
-
- /**
- * Cancels any pending update requests to update the relative call times (X mins ago).
- */
- private void cancelDisplayUpdate() {
- mDisplayUpdateHandler.removeMessages(EVENT_UPDATE_DISPLAY);
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogGroupBuilder.java b/src/com/android/dialer/calllog/CallLogGroupBuilder.java
deleted file mode 100644
index aa45029c0..000000000
--- a/src/com/android/dialer/calllog/CallLogGroupBuilder.java
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import android.database.Cursor;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.text.format.Time;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.util.DateUtils;
-import com.android.contacts.common.util.PhoneNumberHelper;
-import com.android.dialer.util.AppCompatConstants;
-
-/**
- * Groups together calls in the call log. The primary grouping attempts to group together calls
- * to and from the same number into a single row on the call log.
- * A secondary grouping assigns calls, grouped via the primary grouping, to "day groups". The day
- * groups provide a means of identifying the calls which occurred "Today", "Yesterday", "Last week",
- * or "Other".
- * <p>
- * This class is meant to be used in conjunction with {@link GroupingListAdapter}.
- */
-public class CallLogGroupBuilder {
- public interface GroupCreator {
-
- /**
- * Defines the interface for adding a group to the call log.
- * The primary group for a call log groups the calls together based on the number which was
- * dialed.
- * @param cursorPosition The starting position of the group in the cursor.
- * @param size The size of the group.
- */
- public void addGroup(int cursorPosition, int size);
-
- /**
- * Defines the interface for tracking the day group each call belongs to. Calls in a call
- * group are assigned the same day group as the first call in the group. The day group
- * assigns calls to the buckets: Today, Yesterday, Last week, and Other
- *
- * @param rowId The row Id of the current call.
- * @param dayGroup The day group the call belongs in.
- */
- public void setDayGroup(long rowId, int dayGroup);
-
- /**
- * Defines the interface for clearing the day groupings information on rebind/regroup.
- */
- public void clearDayGroups();
- }
-
- /**
- * Day grouping for call log entries used to represent no associated day group. Used primarily
- * when retrieving the previous day group, but there is no previous day group (i.e. we are at
- * the start of the list).
- */
- public static final int DAY_GROUP_NONE = -1;
-
- /** Day grouping for calls which occurred today. */
- public static final int DAY_GROUP_TODAY = 0;
-
- /** Day grouping for calls which occurred yesterday. */
- public static final int DAY_GROUP_YESTERDAY = 1;
-
- /** Day grouping for calls which occurred before last week. */
- public static final int DAY_GROUP_OTHER = 2;
-
- /** Instance of the time object used for time calculations. */
- private static final Time TIME = new Time();
-
- /** The object on which the groups are created. */
- private final GroupCreator mGroupCreator;
-
- public CallLogGroupBuilder(GroupCreator groupCreator) {
- mGroupCreator = groupCreator;
- }
-
- /**
- * Finds all groups of adjacent entries in the call log which should be grouped together and
- * calls {@link GroupCreator#addGroup(int, int)} on {@link #mGroupCreator} for each of
- * them.
- * <p>
- * For entries that are not grouped with others, we do not need to create a group of size one.
- * <p>
- * It assumes that the cursor will not change during its execution.
- *
- * @see GroupingListAdapter#addGroups(Cursor)
- */
- public void addGroups(Cursor cursor) {
- final int count = cursor.getCount();
- if (count == 0) {
- return;
- }
-
- // Clear any previous day grouping information.
- mGroupCreator.clearDayGroups();
-
- // Get current system time, used for calculating which day group calls belong to.
- long currentTime = System.currentTimeMillis();
- cursor.moveToFirst();
-
- // Determine the day group for the first call in the cursor.
- final long firstDate = cursor.getLong(CallLogQuery.DATE);
- final long firstRowId = cursor.getLong(CallLogQuery.ID);
- int groupDayGroup = getDayGroup(firstDate, currentTime);
- mGroupCreator.setDayGroup(firstRowId, groupDayGroup);
-
- // Instantiate the group values to those of the first call in the cursor.
- String groupNumber = cursor.getString(CallLogQuery.NUMBER);
- String groupPostDialDigits = CompatUtils.isNCompatible()
- ? cursor.getString(CallLogQuery.POST_DIAL_DIGITS) : "";
- String groupViaNumbers = CompatUtils.isNCompatible()
- ? cursor.getString(CallLogQuery.VIA_NUMBER) : "";
- int groupCallType = cursor.getInt(CallLogQuery.CALL_TYPE);
- String groupAccountComponentName = cursor.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME);
- String groupAccountId = cursor.getString(CallLogQuery.ACCOUNT_ID);
- int groupSize = 1;
-
- String number;
- String numberPostDialDigits;
- String numberViaNumbers;
- int callType;
- String accountComponentName;
- String accountId;
-
- while (cursor.moveToNext()) {
- // Obtain the values for the current call to group.
- number = cursor.getString(CallLogQuery.NUMBER);
- numberPostDialDigits = CompatUtils.isNCompatible()
- ? cursor.getString(CallLogQuery.POST_DIAL_DIGITS) : "";
- numberViaNumbers = CompatUtils.isNCompatible()
- ? cursor.getString(CallLogQuery.VIA_NUMBER) : "";
- callType = cursor.getInt(CallLogQuery.CALL_TYPE);
- accountComponentName = cursor.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME);
- accountId = cursor.getString(CallLogQuery.ACCOUNT_ID);
-
- final boolean isSameNumber = equalNumbers(groupNumber, number);
- final boolean isSamePostDialDigits = groupPostDialDigits.equals(numberPostDialDigits);
- final boolean isSameViaNumbers = groupViaNumbers.equals(numberViaNumbers);
- final boolean isSameAccount = isSameAccount(
- groupAccountComponentName, accountComponentName, groupAccountId, accountId);
-
- // Group with the same number and account. Never group voicemails. Only group blocked
- // calls with other blocked calls.
- if (isSameNumber && isSameAccount && isSamePostDialDigits && isSameViaNumbers
- && areBothNotVoicemail(callType, groupCallType)
- && (areBothNotBlocked(callType, groupCallType)
- || areBothBlocked(callType, groupCallType))) {
- // Increment the size of the group to include the current call, but do not create
- // the group until finding a call that does not match.
- groupSize++;
- } else {
- // The call group has changed. Determine the day group for the new call group.
- final long date = cursor.getLong(CallLogQuery.DATE);
- groupDayGroup = getDayGroup(date, currentTime);
-
- // Create a group for the previous group of calls, which does not include the
- // current call.
- mGroupCreator.addGroup(cursor.getPosition() - groupSize, groupSize);
-
- // Start a new group; it will include at least the current call.
- groupSize = 1;
-
- // Update the group values to those of the current call.
- groupNumber = number;
- groupPostDialDigits = numberPostDialDigits;
- groupViaNumbers = numberViaNumbers;
- groupCallType = callType;
- groupAccountComponentName = accountComponentName;
- groupAccountId = accountId;
- }
-
- // Save the day group associated with the current call.
- final long currentCallId = cursor.getLong(CallLogQuery.ID);
- mGroupCreator.setDayGroup(currentCallId, groupDayGroup);
- }
-
- // Create a group for the last set of calls.
- mGroupCreator.addGroup(count - groupSize, groupSize);
- }
-
- /**
- * Group cursor entries by date, with only one entry per group. This is used for listing
- * voicemails in the archive tab.
- */
- public void addVoicemailGroups(Cursor cursor) {
- if (cursor.getCount() == 0) {
- return;
- }
-
- // Clear any previous day grouping information.
- mGroupCreator.clearDayGroups();
-
- // Get current system time, used for calculating which day group calls belong to.
- long currentTime = System.currentTimeMillis();
-
- // Reset cursor to start before the first row
- cursor.moveToPosition(-1);
-
- // Create an individual group for each voicemail
- while (cursor.moveToNext()) {
- mGroupCreator.addGroup(cursor.getPosition(), 1);
- mGroupCreator.setDayGroup(cursor.getLong(CallLogQuery.ID),
- getDayGroup(cursor.getLong(CallLogQuery.DATE), currentTime));
-
- }
- }
-
- @VisibleForTesting
- boolean equalNumbers(String number1, String number2) {
- if (PhoneNumberHelper.isUriNumber(number1) || PhoneNumberHelper.isUriNumber(number2)) {
- return compareSipAddresses(number1, number2);
- } else {
- return PhoneNumberUtils.compare(number1, number2);
- }
- }
-
- private boolean isSameAccount(String name1, String name2, String id1, String id2) {
- return TextUtils.equals(name1, name2) && TextUtils.equals(id1, id2);
- }
-
- @VisibleForTesting
- boolean compareSipAddresses(String number1, String number2) {
- if (number1 == null || number2 == null) return number1 == number2;
-
- int index1 = number1.indexOf('@');
- final String userinfo1;
- final String rest1;
- if (index1 != -1) {
- userinfo1 = number1.substring(0, index1);
- rest1 = number1.substring(index1);
- } else {
- userinfo1 = number1;
- rest1 = "";
- }
-
- int index2 = number2.indexOf('@');
- final String userinfo2;
- final String rest2;
- if (index2 != -1) {
- userinfo2 = number2.substring(0, index2);
- rest2 = number2.substring(index2);
- } else {
- userinfo2 = number2;
- rest2 = "";
- }
-
- return userinfo1.equals(userinfo2) && rest1.equalsIgnoreCase(rest2);
- }
-
- /**
- * Given a call date and the current date, determine which date group the call belongs in.
- *
- * @param date The call date.
- * @param now The current date.
- * @return The date group the call belongs in.
- */
- private int getDayGroup(long date, long now) {
- int days = DateUtils.getDayDifference(TIME, date, now);
-
- if (days == 0) {
- return DAY_GROUP_TODAY;
- } else if (days == 1) {
- return DAY_GROUP_YESTERDAY;
- } else {
- return DAY_GROUP_OTHER;
- }
- }
-
- private boolean areBothNotVoicemail(int callType, int groupCallType) {
- return callType != AppCompatConstants.CALLS_VOICEMAIL_TYPE
- && groupCallType != AppCompatConstants.CALLS_VOICEMAIL_TYPE;
- }
-
- private boolean areBothNotBlocked(int callType, int groupCallType) {
- return callType != AppCompatConstants.CALLS_BLOCKED_TYPE
- && groupCallType != AppCompatConstants.CALLS_BLOCKED_TYPE;
- }
-
- private boolean areBothBlocked(int callType, int groupCallType) {
- return callType == AppCompatConstants.CALLS_BLOCKED_TYPE
- && groupCallType == AppCompatConstants.CALLS_BLOCKED_TYPE;
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java
deleted file mode 100644
index 18b6ff5d3..000000000
--- a/src/com/android/dialer/calllog/CallLogListItemHelper.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.res.Resources;
-import android.provider.CallLog.Calls;
-import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.dialer.PhoneCallDetails;
-import com.android.dialer.util.AppCompatConstants;
-import com.android.dialer.R;
-import com.android.dialer.calllog.calllogcache.CallLogCache;
-
-/**
- * Helper class to fill in the views of a call log entry.
- */
-/* package */class CallLogListItemHelper {
- private static final String TAG = "CallLogListItemHelper";
-
- /** Helper for populating the details of a phone call. */
- private final PhoneCallDetailsHelper mPhoneCallDetailsHelper;
- /** Resources to look up strings. */
- private final Resources mResources;
- private final CallLogCache mCallLogCache;
-
- /**
- * Creates a new helper instance.
- *
- * @param phoneCallDetailsHelper used to set the details of a phone call
- * @param resources The object from which resources can be retrieved
- * @param callLogCache A cache for values retrieved from telecom/telephony
- */
- public CallLogListItemHelper(
- PhoneCallDetailsHelper phoneCallDetailsHelper,
- Resources resources,
- CallLogCache callLogCache) {
- mPhoneCallDetailsHelper = phoneCallDetailsHelper;
- mResources = resources;
- mCallLogCache = callLogCache;
- }
-
- /**
- * Sets the name, label, and number for a contact.
- *
- * @param views the views to populate
- * @param details the details of a phone call needed to fill in the data
- */
- public void setPhoneCallDetails(
- CallLogListItemViewHolder views,
- PhoneCallDetails details) {
- mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details);
-
- // Set the accessibility text for the contact badge
- views.quickContactView.setContentDescription(getContactBadgeDescription(details));
-
- // Set the primary action accessibility description
- views.primaryActionView.setContentDescription(getCallDescription(details));
-
- // Cache name or number of caller. Used when setting the content descriptions of buttons
- // when the actions ViewStub is inflated.
- views.nameOrNumber = getNameOrNumber(details);
-
- // The call type or Location associated with the call. Use when setting text for a
- // voicemail log's call button
- views.callTypeOrLocation = mPhoneCallDetailsHelper.getCallTypeOrLocation(details);
-
- // Cache country iso. Used for number filtering.
- views.countryIso = details.countryIso;
-
- views.updatePhoto();
- }
-
- /**
- * Sets the accessibility descriptions for the action buttons in the action button ViewStub.
- *
- * @param views The views associated with the current call log entry.
- */
- public void setActionContentDescriptions(CallLogListItemViewHolder views) {
- if (views.nameOrNumber == null) {
- Log.e(TAG, "setActionContentDescriptions; name or number is null.");
- }
-
- // Calling expandTemplate with a null parameter will cause a NullPointerException.
- // Although we don't expect a null name or number, it is best to protect against it.
- CharSequence nameOrNumber = views.nameOrNumber == null ? "" : views.nameOrNumber;
-
- views.videoCallButtonView.setContentDescription(
- TextUtils.expandTemplate(
- mResources.getString(R.string.description_video_call_action),
- nameOrNumber));
-
- views.createNewContactButtonView.setContentDescription(
- TextUtils.expandTemplate(
- mResources.getString(R.string.description_create_new_contact_action),
- nameOrNumber));
-
- views.addToExistingContactButtonView.setContentDescription(
- TextUtils.expandTemplate(
- mResources.getString(R.string.description_add_to_existing_contact_action),
- nameOrNumber));
-
- views.detailsButtonView.setContentDescription(
- TextUtils.expandTemplate(
- mResources.getString(R.string.description_details_action), nameOrNumber));
- }
-
- /**
- * Returns the accessibility description for the contact badge for a call log entry.
- *
- * @param details Details of call.
- * @return Accessibility description.
- */
- private CharSequence getContactBadgeDescription(PhoneCallDetails details) {
- return mResources.getString(R.string.description_contact_details, getNameOrNumber(details));
- }
-
- /**
- * Returns the accessibility description of the "return call/call" action for a call log
- * entry.
- * Accessibility text is a combination of:
- * {Voicemail Prefix}. {Number of Calls}. {Caller information} {Phone Account}.
- * If most recent call is a voicemail, {Voicemail Prefix} is "New Voicemail.", otherwise "".
- *
- * If more than one call for the caller, {Number of Calls} is:
- * "{number of calls} calls.", otherwise "".
- *
- * The {Caller Information} references the most recent call associated with the caller.
- * For incoming calls:
- * If missed call: Missed call from {Name/Number} {Call Type} {Call Time}.
- * If answered call: Answered call from {Name/Number} {Call Type} {Call Time}.
- *
- * For outgoing calls:
- * If outgoing: Call to {Name/Number] {Call Type} {Call Time}.
- *
- * Where:
- * {Name/Number} is the name or number of the caller (as shown in call log).
- * {Call type} is the contact phone number type (eg mobile) or location.
- * {Call Time} is the time since the last call for the contact occurred.
- *
- * The {Phone Account} refers to the account/SIM through which the call was placed or received
- * in multi-SIM devices.
- *
- * Examples:
- * 3 calls. New Voicemail. Missed call from Joe Smith mobile 2 hours ago on SIM 1.
- *
- * 2 calls. Answered call from John Doe mobile 1 hour ago.
- *
- * @param context The application context.
- * @param details Details of call.
- * @return Return call action description.
- */
- public CharSequence getCallDescription(PhoneCallDetails details) {
- int lastCallType = getLastCallType(details.callTypes);
-
- // Get the name or number of the caller.
- final CharSequence nameOrNumber = getNameOrNumber(details);
-
- // Get the call type or location of the caller; null if not applicable
- final CharSequence typeOrLocation = mPhoneCallDetailsHelper.getCallTypeOrLocation(details);
-
- // Get the time/date of the call
- final CharSequence timeOfCall = mPhoneCallDetailsHelper.getCallDate(details);
-
- SpannableStringBuilder callDescription = new SpannableStringBuilder();
-
- // Add number of calls if more than one.
- if (details.callTypes.length > 1) {
- callDescription.append(mResources.getString(R.string.description_num_calls,
- details.callTypes.length));
- }
-
- // If call had video capabilities, add the "Video Call" string.
- if ((details.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO) {
- callDescription.append(mResources.getString(R.string.description_video_call));
- }
-
- String accountLabel = mCallLogCache.getAccountLabel(details.accountHandle);
- CharSequence onAccountLabel = PhoneCallDetails.createAccountLabelDescription(mResources,
- details.viaNumber, accountLabel);
-
- int stringID = getCallDescriptionStringID(details.callTypes, details.isRead);
- callDescription.append(
- TextUtils.expandTemplate(
- mResources.getString(stringID),
- nameOrNumber,
- typeOrLocation == null ? "" : typeOrLocation,
- timeOfCall,
- onAccountLabel));
-
- return callDescription;
- }
-
- /**
- * Determine the appropriate string ID to describe a call for accessibility purposes.
- *
- * @param callTypes The type of call corresponding to this entry or multiple if this entry
- * represents multiple calls grouped together.
- * @param isRead If the entry is a voicemail, {@code true} if the voicemail is read.
- * @return String resource ID to use.
- */
- public int getCallDescriptionStringID(int[] callTypes, boolean isRead) {
- int lastCallType = getLastCallType(callTypes);
- int stringID;
-
- if (lastCallType == AppCompatConstants.CALLS_MISSED_TYPE) {
- //Message: Missed call from <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>,
- //<PhoneAccount>.
- stringID = R.string.description_incoming_missed_call;
- } else if (lastCallType == AppCompatConstants.CALLS_INCOMING_TYPE) {
- //Message: Answered call from <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>,
- //<PhoneAccount>.
- stringID = R.string.description_incoming_answered_call;
- } else if (lastCallType == AppCompatConstants.CALLS_VOICEMAIL_TYPE) {
- //Message: (Unread) [V/v]oicemail from <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>,
- //<PhoneAccount>.
- stringID = isRead ? R.string.description_read_voicemail
- : R.string.description_unread_voicemail;
- } else {
- //Message: Call to <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>, <PhoneAccount>.
- stringID = R.string.description_outgoing_call;
- }
- return stringID;
- }
-
- /**
- * Determine the call type for the most recent call.
- * @param callTypes Call types to check.
- * @return Call type.
- */
- private int getLastCallType(int[] callTypes) {
- if (callTypes.length > 0) {
- return callTypes[0];
- } else {
- return Calls.MISSED_TYPE;
- }
- }
-
- /**
- * Return the name or number of the caller specified by the details.
- * @param details Call details
- * @return the name (if known) of the caller, otherwise the formatted number.
- */
- private CharSequence getNameOrNumber(PhoneCallDetails details) {
- final CharSequence recipient;
- if (!TextUtils.isEmpty(details.getPreferredName())) {
- recipient = details.getPreferredName();
- } else {
- recipient = details.displayNumber + details.postDialDigits;
- }
- return recipient;
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
deleted file mode 100644
index 858cc2102..000000000
--- a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
+++ /dev/null
@@ -1,776 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.provider.CallLog;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.support.v7.widget.CardView;
-import android.support.v7.widget.RecyclerView;
-import android.telecom.PhoneAccountHandle;
-import android.text.BidiFormatter;
-import android.text.TextDirectionHeuristics;
-import android.text.TextUtils;
-import android.view.ContextMenu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewStub;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.QuickContactBadge;
-import android.widget.TextView;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.ClipboardUtils;
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
-import com.android.contacts.common.dialog.CallSubjectDialog;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.contacts.common.util.UriUtils;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.R;
-import com.android.dialer.calllog.calllogcache.CallLogCache;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.filterednumber.BlockNumberDialogFragment;
-import com.android.dialer.filterednumber.BlockedNumbersMigrator;
-import com.android.dialer.filterednumber.FilteredNumbersUtil;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.ScreenEvent;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.PhoneNumberUtil;
-import com.android.dialer.voicemail.VoicemailPlaybackLayout;
-import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
-
-/**
- * This is an object containing references to views contained by the call log list item. This
- * improves performance by reducing the frequency with which we need to find views by IDs.
- *
- * This object also contains UI logic pertaining to the view, to isolate it from the CallLogAdapter.
- */
-public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
- implements View.OnClickListener, MenuItem.OnMenuItemClickListener,
- View.OnCreateContextMenuListener {
-
- public interface OnClickListener {
- void onBlockReportSpam(
- String displayNumber, String number, String countryIso, int callType);
- void onBlock(String displayNumber, String number, String countryIso, int callType);
- void onUnblock(String displayNumber, String number, String countryIso, Integer blockId,
- boolean isSpam, int callType);
- void onReportNotSpam(String displayNumber, String number, String countryIso, int callType);
- }
-
- /** The root view of the call log list item */
- public final View rootView;
- /** The quick contact badge for the contact. */
- public final QuickContactBadge quickContactView;
- /** The primary action view of the entry. */
- public final View primaryActionView;
- /** The details of the phone call. */
- public final PhoneCallDetailsViews phoneCallDetailsViews;
- /** The text of the header for a day grouping. */
- public final TextView dayGroupHeader;
- /** The view containing the details for the call log row, including the action buttons. */
- public final CardView callLogEntryView;
- /** The actionable view which places a call to the number corresponding to the call log row. */
- public final ImageView primaryActionButtonView;
-
- /** The view containing call log item actions. Null until the ViewStub is inflated. */
- public View actionsView;
- /** The button views below are assigned only when the action section is expanded. */
- public VoicemailPlaybackLayout voicemailPlaybackView;
- public View callButtonView;
- public View videoCallButtonView;
- public View createNewContactButtonView;
- public View addToExistingContactButtonView;
- public View sendMessageView;
- public View blockReportView;
- public View blockView;
- public View unblockView;
- public View reportNotSpamView;
- public View detailsButtonView;
- public View callWithNoteButtonView;
- public ImageView workIconView;
-
- /**
- * The row Id for the first call associated with the call log entry. Used as a key for the
- * map used to track which call log entries have the action button section expanded.
- */
- public long rowId;
-
- /**
- * The call Ids for the calls represented by the current call log entry. Used when the user
- * deletes a call log entry.
- */
- public long[] callIds;
-
- /**
- * The callable phone number for the current call log entry. Cached here as the call back
- * intent is set only when the actions ViewStub is inflated.
- */
- public String number;
-
- /**
- * The post-dial numbers that are dialed following the phone number.
- */
- public String postDialDigits;
-
- /**
- * The formatted phone number to display.
- */
- public String displayNumber;
-
- /**
- * The phone number presentation for the current call log entry. Cached here as the call back
- * intent is set only when the actions ViewStub is inflated.
- */
- public int numberPresentation;
-
- /**
- * The type of the phone number (e.g. main, work, etc).
- */
- public String numberType;
-
- /**
- * The country iso for the call. Cached here as the call back
- * intent is set only when the actions ViewStub is inflated.
- */
- public String countryIso;
-
- /**
- * The type of call for the current call log entry. Cached here as the call back
- * intent is set only when the actions ViewStub is inflated.
- */
- public int callType;
-
- /**
- * ID for blocked numbers database.
- * Set when context menu is created, if the number is blocked.
- */
- public Integer blockId;
-
- /**
- * The account for the current call log entry. Cached here as the call back
- * intent is set only when the actions ViewStub is inflated.
- */
- public PhoneAccountHandle accountHandle;
-
- /**
- * If the call has an associated voicemail message, the URI of the voicemail message for
- * playback. Cached here as the voicemail intent is only set when the actions ViewStub is
- * inflated.
- */
- public String voicemailUri;
-
- /**
- * The name or number associated with the call. Cached here for use when setting content
- * descriptions on buttons in the actions ViewStub when it is inflated.
- */
- public CharSequence nameOrNumber;
-
- /**
- * The call type or Location associated with the call. Cached here for use when setting text
- * for a voicemail log's call button
- */
- public CharSequence callTypeOrLocation;
-
- /**
- * Whether this row is for a business or not.
- */
- public boolean isBusiness;
-
- /**
- * The contact info for the contact displayed in this list item.
- */
- public ContactInfo info;
-
- /**
- * Whether spam feature is enabled, which affects UI.
- */
- public boolean isSpamFeatureEnabled;
-
- /**
- * Whether the current log entry is a spam number or not.
- */
- public boolean isSpam;
-
- /**
- * Whether this is the archive tab or not.
- */
- public final boolean isArchiveTab;
-
- private final Context mContext;
- private final CallLogCache mCallLogCache;
- private final CallLogListItemHelper mCallLogListItemHelper;
- private final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
- private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
- private final OnClickListener mBlockReportListener;
-
- private final BlockNumberDialogFragment.Callback mFilteredNumberDialogCallback;
-
- private final int mPhotoSize;
-
- private View.OnClickListener mExpandCollapseListener;
- private boolean mVoicemailPrimaryActionButtonClicked;
-
- private CallLogListItemViewHolder(
- Context context,
- OnClickListener blockReportListener,
- View.OnClickListener expandCollapseListener,
- CallLogCache callLogCache,
- CallLogListItemHelper callLogListItemHelper,
- VoicemailPlaybackPresenter voicemailPlaybackPresenter,
- FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler,
- BlockNumberDialogFragment.Callback filteredNumberDialogCallback,
- View rootView,
- QuickContactBadge quickContactView,
- View primaryActionView,
- PhoneCallDetailsViews phoneCallDetailsViews,
- CardView callLogEntryView,
- TextView dayGroupHeader,
- ImageView primaryActionButtonView,
- boolean isArchiveTab) {
- super(rootView);
-
- mContext = context;
- mExpandCollapseListener = expandCollapseListener;
- mCallLogCache = callLogCache;
- mCallLogListItemHelper = callLogListItemHelper;
- mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
- mFilteredNumberAsyncQueryHandler = filteredNumberAsyncQueryHandler;
- mFilteredNumberDialogCallback = filteredNumberDialogCallback;
- mBlockReportListener = blockReportListener;
-
- this.rootView = rootView;
- this.quickContactView = quickContactView;
- this.primaryActionView = primaryActionView;
- this.phoneCallDetailsViews = phoneCallDetailsViews;
- this.callLogEntryView = callLogEntryView;
- this.dayGroupHeader = dayGroupHeader;
- this.primaryActionButtonView = primaryActionButtonView;
- this.workIconView = (ImageView) rootView.findViewById(R.id.work_profile_icon);
- this.isArchiveTab = isArchiveTab;
- Resources resources = mContext.getResources();
- mPhotoSize = mContext.getResources().getDimensionPixelSize(R.dimen.contact_photo_size);
-
- // Set text height to false on the TextViews so they don't have extra padding.
- phoneCallDetailsViews.nameView.setElegantTextHeight(false);
- phoneCallDetailsViews.callLocationAndDate.setElegantTextHeight(false);
-
- quickContactView.setOverlay(null);
- if (CompatUtils.hasPrioritizedMimeType()) {
- quickContactView.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE);
- }
- primaryActionButtonView.setOnClickListener(this);
- primaryActionView.setOnClickListener(mExpandCollapseListener);
- primaryActionView.setOnCreateContextMenuListener(this);
- }
-
- public static CallLogListItemViewHolder create(
- View view,
- Context context,
- OnClickListener blockReportListener,
- View.OnClickListener expandCollapseListener,
- CallLogCache callLogCache,
- CallLogListItemHelper callLogListItemHelper,
- VoicemailPlaybackPresenter voicemailPlaybackPresenter,
- FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler,
- BlockNumberDialogFragment.Callback filteredNumberDialogCallback,
- boolean isArchiveTab) {
-
- return new CallLogListItemViewHolder(
- context,
- blockReportListener,
- expandCollapseListener,
- callLogCache,
- callLogListItemHelper,
- voicemailPlaybackPresenter,
- filteredNumberAsyncQueryHandler,
- filteredNumberDialogCallback,
- view,
- (QuickContactBadge) view.findViewById(R.id.quick_contact_photo),
- view.findViewById(R.id.primary_action_view),
- PhoneCallDetailsViews.fromView(view),
- (CardView) view.findViewById(R.id.call_log_row),
- (TextView) view.findViewById(R.id.call_log_day_group_label),
- (ImageView) view.findViewById(R.id.primary_action_button),
- isArchiveTab);
- }
-
- @Override
- public void onCreateContextMenu(
- final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- if (TextUtils.isEmpty(number)) {
- return;
- }
-
- if (callType == CallLog.Calls.VOICEMAIL_TYPE) {
- menu.setHeaderTitle(mContext.getResources().getText(R.string.voicemail));
- } else {
- menu.setHeaderTitle(PhoneNumberUtilsCompat.createTtsSpannable(
- BidiFormatter.getInstance().unicodeWrap(number, TextDirectionHeuristics.LTR)));
- }
-
- menu.add(ContextMenu.NONE, R.id.context_menu_copy_to_clipboard, ContextMenu.NONE,
- R.string.action_copy_number_text)
- .setOnMenuItemClickListener(this);
-
- // The edit number before call does not show up if any of the conditions apply:
- // 1) Number cannot be called
- // 2) Number is the voicemail number
- // 3) Number is a SIP address
-
- if (PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation)
- && !mCallLogCache.isVoicemailNumber(accountHandle, number)
- && !PhoneNumberUtil.isSipNumber(number)) {
- menu.add(ContextMenu.NONE, R.id.context_menu_edit_before_call, ContextMenu.NONE,
- R.string.action_edit_number_before_call)
- .setOnMenuItemClickListener(this);
- }
-
- if (callType == CallLog.Calls.VOICEMAIL_TYPE
- && phoneCallDetailsViews.voicemailTranscriptionView.length() > 0) {
- menu.add(ContextMenu.NONE, R.id.context_menu_copy_transcript_to_clipboard,
- ContextMenu.NONE, R.string.copy_transcript_text)
- .setOnMenuItemClickListener(this);
- }
-
- if (FilteredNumberCompat.canAttemptBlockOperations(mContext)
- && FilteredNumbersUtil.canBlockNumber(mContext, number, countryIso)) {
- mFilteredNumberAsyncQueryHandler.isBlockedNumber(
- new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
- @Override
- public void onCheckComplete(Integer id) {
- blockId = id;
- int blockTitleId = blockId == null ? R.string.action_block_number
- : R.string.action_unblock_number;
- final MenuItem blockItem = menu.add(
- ContextMenu.NONE,
- R.id.context_menu_block_number,
- ContextMenu.NONE,
- blockTitleId);
- blockItem.setOnMenuItemClickListener(
- CallLogListItemViewHolder.this);
- }
- }, number, countryIso);
- }
-
- Logger.logScreenView(ScreenEvent.CALL_LOG_CONTEXT_MENU, (Activity) mContext);
- }
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- int resId = item.getItemId();
- if (resId == R.id.context_menu_block_number) {
- FilteredNumberCompat
- .showBlockNumberDialogFlow(mContext.getContentResolver(), blockId, number,
- countryIso, displayNumber, R.id.floating_action_button_container,
- ((Activity) mContext).getFragmentManager(),
- mFilteredNumberDialogCallback);
- return true;
- } else if (resId == R.id.context_menu_copy_to_clipboard) {
- ClipboardUtils.copyText(mContext, null, number, true);
- return true;
- } else if (resId == R.id.context_menu_copy_transcript_to_clipboard) {
- ClipboardUtils.copyText(mContext, null,
- phoneCallDetailsViews.voicemailTranscriptionView.getText(), true);
- return true;
- } else if (resId == R.id.context_menu_edit_before_call) {
- final Intent intent = new Intent(
- Intent.ACTION_DIAL, CallUtil.getCallUri(number));
- intent.setClass(mContext, DialtactsActivity.class);
- DialerUtils.startActivityWithErrorToast(mContext, intent);
- return true;
- }
- return false;
- }
-
- /**
- * Configures the action buttons in the expandable actions ViewStub. The ViewStub is not
- * inflated during initial binding, so click handlers, tags and accessibility text must be set
- * here, if necessary.
- */
- public void inflateActionViewStub() {
- ViewStub stub = (ViewStub) rootView.findViewById(R.id.call_log_entry_actions_stub);
- if (stub != null) {
- actionsView = stub.inflate();
-
- voicemailPlaybackView = (VoicemailPlaybackLayout) actionsView
- .findViewById(R.id.voicemail_playback_layout);
- if (isArchiveTab) {
- voicemailPlaybackView.hideArchiveButton();
- }
-
-
- callButtonView = actionsView.findViewById(R.id.call_action);
- callButtonView.setOnClickListener(this);
-
- videoCallButtonView = actionsView.findViewById(R.id.video_call_action);
- videoCallButtonView.setOnClickListener(this);
-
- createNewContactButtonView = actionsView.findViewById(R.id.create_new_contact_action);
- createNewContactButtonView.setOnClickListener(this);
-
- addToExistingContactButtonView =
- actionsView.findViewById(R.id.add_to_existing_contact_action);
- addToExistingContactButtonView.setOnClickListener(this);
-
- sendMessageView = actionsView.findViewById(R.id.send_message_action);
- sendMessageView.setOnClickListener(this);
-
- blockReportView = actionsView.findViewById(R.id.block_report_action);
- blockReportView.setOnClickListener(this);
-
- blockView = actionsView.findViewById(R.id.block_action);
- blockView.setOnClickListener(this);
-
- unblockView = actionsView.findViewById(R.id.unblock_action);
- unblockView.setOnClickListener(this);
-
- reportNotSpamView = actionsView.findViewById(R.id.report_not_spam_action);
- reportNotSpamView.setOnClickListener(this);
-
- detailsButtonView = actionsView.findViewById(R.id.details_action);
- detailsButtonView.setOnClickListener(this);
-
- callWithNoteButtonView = actionsView.findViewById(R.id.call_with_note_action);
- callWithNoteButtonView.setOnClickListener(this);
- }
-
- bindActionButtons();
- }
-
- private void updatePrimaryActionButton(boolean isExpanded) {
- if (!TextUtils.isEmpty(voicemailUri)) {
- // Treat as voicemail list item; show play button if not expanded.
- if (!isExpanded) {
- primaryActionButtonView.setImageResource(R.drawable.ic_play_arrow_24dp);
- primaryActionButtonView.setContentDescription(TextUtils.expandTemplate(
- mContext.getString(R.string.description_voicemail_action),
- nameOrNumber));
- primaryActionButtonView.setVisibility(View.VISIBLE);
- } else {
- primaryActionButtonView.setVisibility(View.GONE);
- }
- } else {
- // Treat as normal list item; show call button, if possible.
- if (PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation)) {
- boolean isVoicemailNumber =
- mCallLogCache.isVoicemailNumber(accountHandle, number);
- if (isVoicemailNumber) {
- // Call to generic voicemail number, in case there are multiple accounts.
- primaryActionButtonView.setTag(
- IntentProvider.getReturnVoicemailCallIntentProvider());
- } else {
- primaryActionButtonView.setTag(
- IntentProvider.getReturnCallIntentProvider(number + postDialDigits));
- }
-
- primaryActionButtonView.setContentDescription(TextUtils.expandTemplate(
- mContext.getString(R.string.description_call_action),
- nameOrNumber));
- primaryActionButtonView.setImageResource(R.drawable.ic_call_24dp);
- primaryActionButtonView.setVisibility(View.VISIBLE);
- } else {
- primaryActionButtonView.setTag(null);
- primaryActionButtonView.setVisibility(View.GONE);
- }
- }
- }
-
- /**
- * Binds text titles, click handlers and intents to the voicemail, details and callback action
- * buttons.
- */
- private void bindActionButtons() {
- boolean canPlaceCallToNumber = PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation);
-
- if (!TextUtils.isEmpty(voicemailUri) && canPlaceCallToNumber) {
- callButtonView.setTag(IntentProvider.getReturnCallIntentProvider(number));
- ((TextView) callButtonView.findViewById(R.id.call_action_text))
- .setText(TextUtils.expandTemplate(
- mContext.getString(R.string.call_log_action_call),
- nameOrNumber));
- TextView callTypeOrLocationView = ((TextView) callButtonView.findViewById(
- R.id.call_type_or_location_text));
- if (callType == Calls.VOICEMAIL_TYPE && !TextUtils.isEmpty(callTypeOrLocation)) {
- callTypeOrLocationView.setText(callTypeOrLocation);
- callTypeOrLocationView.setVisibility(View.VISIBLE);
- } else {
- callTypeOrLocationView.setVisibility(View.GONE);
- }
- callButtonView.setVisibility(View.VISIBLE);
- } else {
- callButtonView.setVisibility(View.GONE);
- }
-
- // If one of the calls had video capabilities, show the video call button.
- if (mCallLogCache.isVideoEnabled() && canPlaceCallToNumber &&
- phoneCallDetailsViews.callTypeIcons.isVideoShown()) {
- videoCallButtonView.setTag(IntentProvider.getReturnVideoCallIntentProvider(number));
- videoCallButtonView.setVisibility(View.VISIBLE);
- } else {
- videoCallButtonView.setVisibility(View.GONE);
- }
-
- // For voicemail calls, show the voicemail playback layout; hide otherwise.
- if (callType == Calls.VOICEMAIL_TYPE && mVoicemailPlaybackPresenter != null
- && !TextUtils.isEmpty(voicemailUri)) {
- voicemailPlaybackView.setVisibility(View.VISIBLE);
-
- Uri uri = Uri.parse(voicemailUri);
- mVoicemailPlaybackPresenter.setPlaybackView(
- voicemailPlaybackView, uri, mVoicemailPrimaryActionButtonClicked);
- mVoicemailPrimaryActionButtonClicked = false;
- // Only mark voicemail as read when not in archive tab
- if (!isArchiveTab) {
- CallLogAsyncTaskUtil.markVoicemailAsRead(mContext, uri);
- }
- } else {
- voicemailPlaybackView.setVisibility(View.GONE);
- }
-
- if (callType == Calls.VOICEMAIL_TYPE) {
- detailsButtonView.setVisibility(View.GONE);
- } else {
- detailsButtonView.setVisibility(View.VISIBLE);
- detailsButtonView.setTag(
- IntentProvider.getCallDetailIntentProvider(rowId, callIds, null));
- }
-
- if (info != null && UriUtils.isEncodedContactUri(info.lookupUri)) {
- createNewContactButtonView.setTag(IntentProvider.getAddContactIntentProvider(
- info.lookupUri, info.name, info.number, info.type, true /* isNewContact */));
- createNewContactButtonView.setVisibility(View.VISIBLE);
-
- addToExistingContactButtonView.setTag(IntentProvider.getAddContactIntentProvider(
- info.lookupUri, info.name, info.number, info.type, false /* isNewContact */));
- addToExistingContactButtonView.setVisibility(View.VISIBLE);
- } else {
- createNewContactButtonView.setVisibility(View.GONE);
- addToExistingContactButtonView.setVisibility(View.GONE);
- }
-
- if (canPlaceCallToNumber) {
- sendMessageView.setTag(IntentProvider.getSendSmsIntentProvider(number));
- sendMessageView.setVisibility(View.VISIBLE);
- } else {
- sendMessageView.setVisibility(View.GONE);
- }
-
- mCallLogListItemHelper.setActionContentDescriptions(this);
-
- boolean supportsCallSubject =
- mCallLogCache.doesAccountSupportCallSubject(accountHandle);
- boolean isVoicemailNumber =
- mCallLogCache.isVoicemailNumber(accountHandle, number);
- callWithNoteButtonView.setVisibility(
- supportsCallSubject && !isVoicemailNumber ? View.VISIBLE : View.GONE);
-
- updateBlockReportActions();
- }
-
- /**
- * Show or hide the action views, such as voicemail, details, and add contact.
- *
- * If the action views have never been shown yet for this view, inflate the view stub.
- */
- public void showActions(boolean show) {
- showOrHideVoicemailTranscriptionView(show);
-
- if (show) {
- // Inflate the view stub if necessary, and wire up the event handlers.
- inflateActionViewStub();
-
- actionsView.setVisibility(View.VISIBLE);
- actionsView.setAlpha(1.0f);
- } else {
- // When recycling a view, it is possible the actionsView ViewStub was previously
- // inflated so we should hide it in this case.
- if (actionsView != null) {
- actionsView.setVisibility(View.GONE);
- }
- }
-
- updatePrimaryActionButton(show);
- }
-
- public void showOrHideVoicemailTranscriptionView(boolean isExpanded) {
- if (callType != Calls.VOICEMAIL_TYPE) {
- return;
- }
-
- final TextView view = phoneCallDetailsViews.voicemailTranscriptionView;
- if (!isExpanded || TextUtils.isEmpty(view.getText())) {
- view.setVisibility(View.GONE);
- return;
- }
- view.setVisibility(View.VISIBLE);
- }
-
- public void updatePhoto() {
- if (isSpamFeatureEnabled && isSpam) {
- quickContactView.setImageDrawable(
- mContext.getDrawable(R.drawable.blocked_contact));
- return;
- }
- quickContactView.assignContactUri(info.lookupUri);
-
- final boolean isVoicemail = mCallLogCache.isVoicemailNumber(accountHandle, number);
- int contactType = ContactPhotoManager.TYPE_DEFAULT;
- if (isVoicemail) {
- contactType = ContactPhotoManager.TYPE_VOICEMAIL;
- } else if (isBusiness) {
- contactType = ContactPhotoManager.TYPE_BUSINESS;
- }
-
- final String lookupKey = info.lookupUri != null
- ? UriUtils.getLookupKeyFromUri(info.lookupUri) : null;
- final String displayName = TextUtils.isEmpty(info.name) ? displayNumber : info.name;
- final DefaultImageRequest request = new DefaultImageRequest(
- displayName, lookupKey, contactType, true /* isCircular */);
-
- if (info.photoId == 0 && info.photoUri != null) {
- ContactPhotoManager.getInstance(mContext).loadPhoto(quickContactView, info.photoUri,
- mPhotoSize, false /* darkTheme */, true /* isCircular */, request);
- } else {
- ContactPhotoManager.getInstance(mContext).loadThumbnail(quickContactView, info.photoId,
- false /* darkTheme */, true /* isCircular */, request);
- }
- }
-
- @Override
- public void onClick(View view) {
- if (view.getId() == R.id.primary_action_button && !TextUtils.isEmpty(voicemailUri)) {
- mVoicemailPrimaryActionButtonClicked = true;
- mExpandCollapseListener.onClick(primaryActionView);
- } else if (view.getId() == R.id.call_with_note_action) {
- CallSubjectDialog.start(
- (Activity) mContext,
- info.photoId,
- info.photoUri,
- info.lookupUri,
- (String) nameOrNumber /* top line of contact view in call subject dialog */,
- isBusiness,
- number,
- TextUtils.isEmpty(info.name) ? null : displayNumber, /* second line of contact
- view in dialog. */
- numberType, /* phone number type (e.g. mobile) in second line of contact view */
- accountHandle);
- } else if (view.getId() == R.id.block_report_action) {
- maybeShowBlockNumberMigrationDialog(new BlockedNumbersMigrator.Listener() {
- @Override
- public void onComplete() {
- mBlockReportListener.onBlockReportSpam(
- displayNumber, number, countryIso, callType);
- }
- });
- } else if (view.getId() == R.id.block_action) {
- maybeShowBlockNumberMigrationDialog(new BlockedNumbersMigrator.Listener() {
- @Override
- public void onComplete() {
- mBlockReportListener.onBlock(displayNumber, number, countryIso, callType);
- }
- });
- } else if (view.getId() == R.id.unblock_action) {
- mBlockReportListener.onUnblock(
- displayNumber, number, countryIso, blockId, isSpam, callType);
- } else if (view.getId() == R.id.report_not_spam_action) {
- mBlockReportListener.onReportNotSpam(displayNumber, number, countryIso, callType);
- } else {
- final IntentProvider intentProvider = (IntentProvider) view.getTag();
- if (intentProvider != null) {
- final Intent intent = intentProvider.getIntent(mContext);
- // See IntentProvider.getCallDetailIntentProvider() for why this may be null.
- if (intent != null) {
- DialerUtils.startActivityWithErrorToast(mContext, intent);
- }
- }
- }
- }
-
- private void maybeShowBlockNumberMigrationDialog(BlockedNumbersMigrator.Listener listener) {
- if (!FilteredNumberCompat.maybeShowBlockNumberMigrationDialog(
- mContext.getContentResolver(),
- ((Activity) mContext).getFragmentManager(), listener)) {
- listener.onComplete();
- }
- }
-
- @NeededForTesting
- public static CallLogListItemViewHolder createForTest(Context context) {
- Resources resources = context.getResources();
- CallLogCache callLogCache =
- CallLogCache.getCallLogCache(context);
- PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper(
- context, resources, callLogCache);
-
- CallLogListItemViewHolder viewHolder = new CallLogListItemViewHolder(
- context,
- null,
- null /* expandCollapseListener */,
- callLogCache,
- new CallLogListItemHelper(phoneCallDetailsHelper, resources, callLogCache),
- null /* voicemailPlaybackPresenter */,
- null /* filteredNumberAsyncQueryHandler */,
- null /* filteredNumberDialogCallback */,
- new View(context),
- new QuickContactBadge(context),
- new View(context),
- PhoneCallDetailsViews.createForTest(context),
- new CardView(context),
- new TextView(context),
- new ImageView(context),
- false);
- viewHolder.detailsButtonView = new TextView(context);
- viewHolder.actionsView = new View(context);
- viewHolder.voicemailPlaybackView = new VoicemailPlaybackLayout(context);
- viewHolder.workIconView = new ImageButton(context);
- return viewHolder;
- }
-
- private void updateBlockReportActions() {
- if (!isSpamFeatureEnabled) {
- return;
- }
- // Set block/spam actions.
- blockReportView.setVisibility(View.GONE);
- blockView.setVisibility(View.GONE);
- unblockView.setVisibility(View.GONE);
- reportNotSpamView.setVisibility(View.GONE);
- boolean isBlocked = blockId != null;
- if (isBlocked) {
- unblockView.setVisibility(View.VISIBLE);
- } else {
- if (isSpam) {
- blockView.setVisibility(View.VISIBLE);
- reportNotSpamView.setVisibility(View.VISIBLE);
- } else {
- blockReportView.setVisibility(View.VISIBLE);
- }
- }
-
- }
-} \ No newline at end of file
diff --git a/src/com/android/dialer/calllog/CallLogNotificationsHelper.java b/src/com/android/dialer/calllog/CallLogNotificationsHelper.java
deleted file mode 100644
index 9a5028460..000000000
--- a/src/com/android/dialer/calllog/CallLogNotificationsHelper.java
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.calllog;
-
-import com.google.common.base.Strings;
-
-import android.Manifest;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract.PhoneLookup;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.R;
-import com.android.dialer.util.TelecomUtil;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Helper class operating on call log notifications.
- */
-public class CallLogNotificationsHelper {
- private static final String TAG = "CallLogNotifHelper";
- private static CallLogNotificationsHelper sInstance;
-
- /** Returns the singleton instance of the {@link CallLogNotificationsHelper}. */
- public static CallLogNotificationsHelper getInstance(Context context) {
- if (sInstance == null) {
- ContentResolver contentResolver = context.getContentResolver();
- String countryIso = GeoUtil.getCurrentCountryIso(context);
- sInstance = new CallLogNotificationsHelper(context,
- createNewCallsQuery(context, contentResolver),
- createNameLookupQuery(context, contentResolver),
- new ContactInfoHelper(context, countryIso),
- countryIso);
- }
- return sInstance;
- }
-
- private final Context mContext;
- private final NewCallsQuery mNewCallsQuery;
- private final NameLookupQuery mNameLookupQuery;
- private final ContactInfoHelper mContactInfoHelper;
- private final String mCurrentCountryIso;
-
- CallLogNotificationsHelper(Context context, NewCallsQuery newCallsQuery,
- NameLookupQuery nameLookupQuery, ContactInfoHelper contactInfoHelper,
- String countryIso) {
- mContext = context;
- mNewCallsQuery = newCallsQuery;
- mNameLookupQuery = nameLookupQuery;
- mContactInfoHelper = contactInfoHelper;
- mCurrentCountryIso = countryIso;
- }
-
- /**
- * Get all voicemails with the "new" flag set to 1.
- *
- * @return A list of NewCall objects where each object represents a new voicemail.
- */
- @Nullable
- public List<NewCall> getNewVoicemails() {
- return mNewCallsQuery.query(Calls.VOICEMAIL_TYPE);
- }
-
- /**
- * Get all missed calls with the "new" flag set to 1.
- *
- * @return A list of NewCall objects where each object represents a new missed call.
- */
- @Nullable
- public List<NewCall> getNewMissedCalls() {
- return mNewCallsQuery.query(Calls.MISSED_TYPE);
- }
-
- /**
- * Given a number and number information (presentation and country ISO), get the best name
- * for display. If the name is empty but we have a special presentation, display that.
- * Otherwise attempt to look it up in the database or the cache.
- * If that fails, fall back to displaying the number.
- */
- public String getName(@Nullable String number, int numberPresentation,
- @Nullable String countryIso) {
- return getContactInfo(number, numberPresentation, countryIso).name;
- }
-
- /**
- * Given a number and number information (presentation and country ISO), get
- * {@link ContactInfo}. If the name is empty but we have a special presentation, display that.
- * Otherwise attempt to look it up in the cache.
- * If that fails, fall back to displaying the number.
- */
- public ContactInfo getContactInfo(@Nullable String number, int numberPresentation,
- @Nullable String countryIso) {
- if (countryIso == null) {
- countryIso = mCurrentCountryIso;
- }
-
- number = Strings.nullToEmpty(number);
- ContactInfo contactInfo = new ContactInfo();
- contactInfo.number = number;
- contactInfo.formattedNumber = PhoneNumberUtils.formatNumber(number, countryIso);
- // contactInfo.normalizedNumber is not PhoneNumberUtils.normalizeNumber. Read ContactInfo.
- contactInfo.normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
-
- // 1. Special number representation.
- contactInfo.name = PhoneNumberDisplayUtil.getDisplayName(
- mContext,
- number,
- numberPresentation,
- false).toString();
- if (!TextUtils.isEmpty(contactInfo.name)) {
- return contactInfo;
- }
-
- // 2. Look it up in the cache.
- ContactInfo cachedContactInfo = mContactInfoHelper.lookupNumber(number, countryIso);
-
- if (cachedContactInfo != null && !TextUtils.isEmpty(cachedContactInfo.name)) {
- return cachedContactInfo;
- }
-
- if (!TextUtils.isEmpty(contactInfo.formattedNumber)) {
- // 3. If we cannot lookup the contact, use the formatted number instead.
- contactInfo.name = contactInfo.formattedNumber;
- } else if (!TextUtils.isEmpty(number)) {
- // 4. If number can't be formatted, use number.
- contactInfo.name = number;
- } else {
- // 5. Otherwise, it's unknown number.
- contactInfo.name = mContext.getResources().getString(R.string.unknown);
- }
- return contactInfo;
- }
-
- /** Removes the missed call notifications. */
- public static void removeMissedCallNotifications(Context context) {
- TelecomUtil.cancelMissedCallsNotification(context);
- }
-
- /** Update the voice mail notifications. */
- public static void updateVoicemailNotifications(Context context) {
- CallLogNotificationsService.updateVoicemailNotifications(context, null);
- }
-
- /** Information about a new voicemail. */
- public static final class NewCall {
- public final Uri callsUri;
- public final Uri voicemailUri;
- public final String number;
- public final int numberPresentation;
- public final String accountComponentName;
- public final String accountId;
- public final String transcription;
- public final String countryIso;
- public final long dateMs;
-
- public NewCall(
- Uri callsUri,
- Uri voicemailUri,
- String number,
- int numberPresentation,
- String accountComponentName,
- String accountId,
- String transcription,
- String countryIso,
- long dateMs) {
- this.callsUri = callsUri;
- this.voicemailUri = voicemailUri;
- this.number = number;
- this.numberPresentation = numberPresentation;
- this.accountComponentName = accountComponentName;
- this.accountId = accountId;
- this.transcription = transcription;
- this.countryIso = countryIso;
- this.dateMs = dateMs;
- }
- }
-
- /** Allows determining the new calls for which a notification should be generated. */
- public interface NewCallsQuery {
- /**
- * Returns the new calls of a certain type for which a notification should be generated.
- */
- @Nullable
- public List<NewCall> query(int type);
- }
-
- /** Create a new instance of {@link NewCallsQuery}. */
- public static NewCallsQuery createNewCallsQuery(Context context,
- ContentResolver contentResolver) {
-
- return new DefaultNewCallsQuery(context.getApplicationContext(), contentResolver);
- }
-
- /**
- * Default implementation of {@link NewCallsQuery} that looks up the list of new calls to
- * notify about in the call log.
- */
- private static final class DefaultNewCallsQuery implements NewCallsQuery {
- private static final String[] PROJECTION = {
- Calls._ID,
- Calls.NUMBER,
- Calls.VOICEMAIL_URI,
- Calls.NUMBER_PRESENTATION,
- Calls.PHONE_ACCOUNT_COMPONENT_NAME,
- Calls.PHONE_ACCOUNT_ID,
- Calls.TRANSCRIPTION,
- Calls.COUNTRY_ISO,
- Calls.DATE
- };
- private static final int ID_COLUMN_INDEX = 0;
- private static final int NUMBER_COLUMN_INDEX = 1;
- private static final int VOICEMAIL_URI_COLUMN_INDEX = 2;
- private static final int NUMBER_PRESENTATION_COLUMN_INDEX = 3;
- private static final int PHONE_ACCOUNT_COMPONENT_NAME_COLUMN_INDEX = 4;
- private static final int PHONE_ACCOUNT_ID_COLUMN_INDEX = 5;
- private static final int TRANSCRIPTION_COLUMN_INDEX = 6;
- private static final int COUNTRY_ISO_COLUMN_INDEX = 7;
- private static final int DATE_COLUMN_INDEX = 8;
-
- private final ContentResolver mContentResolver;
- private final Context mContext;
-
- private DefaultNewCallsQuery(Context context, ContentResolver contentResolver) {
- mContext = context;
- mContentResolver = contentResolver;
- }
-
- @Override
- @Nullable
- public List<NewCall> query(int type) {
- if (!PermissionsUtil.hasPermission(mContext, Manifest.permission.READ_CALL_LOG)) {
- Log.w(TAG, "No READ_CALL_LOG permission, returning null for calls lookup.");
- return null;
- }
- final String selection = String.format("%s = 1 AND %s = ?", Calls.NEW, Calls.TYPE);
- final String[] selectionArgs = new String[]{ Integer.toString(type) };
- try (Cursor cursor = mContentResolver.query(Calls.CONTENT_URI_WITH_VOICEMAIL,
- PROJECTION, selection, selectionArgs, Calls.DEFAULT_SORT_ORDER)) {
- if (cursor == null) {
- return null;
- }
- List<NewCall> newCalls = new ArrayList<>();
- while (cursor.moveToNext()) {
- newCalls.add(createNewCallsFromCursor(cursor));
- }
- return newCalls;
- } catch (RuntimeException e) {
- Log.w(TAG, "Exception when querying Contacts Provider for calls lookup");
- return null;
- }
- }
-
- /** Returns an instance of {@link NewCall} created by using the values of the cursor. */
- private NewCall createNewCallsFromCursor(Cursor cursor) {
- String voicemailUriString = cursor.getString(VOICEMAIL_URI_COLUMN_INDEX);
- Uri callsUri = ContentUris.withAppendedId(
- Calls.CONTENT_URI_WITH_VOICEMAIL, cursor.getLong(ID_COLUMN_INDEX));
- Uri voicemailUri = voicemailUriString == null ? null : Uri.parse(voicemailUriString);
- return new NewCall(
- callsUri,
- voicemailUri,
- cursor.getString(NUMBER_COLUMN_INDEX),
- cursor.getInt(NUMBER_PRESENTATION_COLUMN_INDEX),
- cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME_COLUMN_INDEX),
- cursor.getString(PHONE_ACCOUNT_ID_COLUMN_INDEX),
- cursor.getString(TRANSCRIPTION_COLUMN_INDEX),
- cursor.getString(COUNTRY_ISO_COLUMN_INDEX),
- cursor.getLong(DATE_COLUMN_INDEX));
- }
- }
-
- /** Allows determining the name associated with a given phone number. */
- public interface NameLookupQuery {
- /**
- * Returns the name associated with the given number in the contacts database, or null if
- * the number does not correspond to any of the contacts.
- * <p>
- * If there are multiple contacts with the same phone number, it will return the name of one
- * of the matching contacts.
- */
- @Nullable
- public String query(@Nullable String number);
- }
-
- /** Create a new instance of {@link NameLookupQuery}. */
- public static NameLookupQuery createNameLookupQuery(Context context,
- ContentResolver contentResolver) {
- return new DefaultNameLookupQuery(context.getApplicationContext(), contentResolver);
- }
-
- /**
- * Default implementation of {@link NameLookupQuery} that looks up the name of a contact in the
- * contacts database.
- */
- private static final class DefaultNameLookupQuery implements NameLookupQuery {
- private static final String[] PROJECTION = { PhoneLookup.DISPLAY_NAME };
- private static final int DISPLAY_NAME_COLUMN_INDEX = 0;
-
- private final ContentResolver mContentResolver;
- private final Context mContext;
-
- private DefaultNameLookupQuery(Context context, ContentResolver contentResolver) {
- mContext = context;
- mContentResolver = contentResolver;
- }
-
- @Override
- @Nullable
- public String query(@Nullable String number) {
- if (!PermissionsUtil.hasPermission(mContext, Manifest.permission.READ_CONTACTS)) {
- Log.w(TAG, "No READ_CONTACTS permission, returning null for name lookup.");
- return null;
- }
- try (Cursor cursor = mContentResolver.query(
- Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)),
- PROJECTION, null, null, null)) {
- if (cursor == null || !cursor.moveToFirst()) {
- return null;
- }
- return cursor.getString(DISPLAY_NAME_COLUMN_INDEX);
- } catch (RuntimeException e) {
- Log.w(TAG, "Exception when querying Contacts Provider for name lookup");
- return null;
- }
- }
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogNotificationsService.java b/src/com/android/dialer/calllog/CallLogNotificationsService.java
deleted file mode 100644
index 4ff9576ca..000000000
--- a/src/com/android/dialer/calllog/CallLogNotificationsService.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.app.IntentService;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.util.Log;
-
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.util.TelecomUtil;
-
-/**
- * Provides operations for managing call-related notifications.
- * <p>
- * It handles the following actions:
- * <ul>
- * <li>Updating voicemail notifications</li>
- * <li>Marking new voicemails as old</li>
- * <li>Updating missed call notifications</li>
- * <li>Marking new missed calls as old</li>
- * <li>Calling back from a missed call</li>
- * <li>Sending an SMS from a missed call</li>
- * </ul>
- */
-public class CallLogNotificationsService extends IntentService {
- private static final String TAG = "CallLogNotificationsService";
-
- /** Action to mark all the new voicemails as old. */
- public static final String ACTION_MARK_NEW_VOICEMAILS_AS_OLD =
- "com.android.dialer.calllog.ACTION_MARK_NEW_VOICEMAILS_AS_OLD";
-
- /**
- * Action to update voicemail notifications.
- * <p>
- * May include an optional extra {@link #EXTRA_NEW_VOICEMAIL_URI}.
- */
- public static final String ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS =
- "com.android.dialer.calllog.UPDATE_VOICEMAIL_NOTIFICATIONS";
-
- /**
- * Extra to included with {@link #ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS} to identify the new
- * voicemail that triggered an update.
- * <p>
- * It must be a {@link Uri}.
- */
- public static final String EXTRA_NEW_VOICEMAIL_URI = "NEW_VOICEMAIL_URI";
-
- /**
- * Action to update the missed call notifications.
- * <p>
- * Includes optional extras {@link #EXTRA_MISSED_CALL_NUMBER} and
- * {@link #EXTRA_MISSED_CALL_COUNT}.
- */
- public static final String ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS =
- "com.android.dialer.calllog.UPDATE_MISSED_CALL_NOTIFICATIONS";
-
- /** Action to mark all the new missed calls as old. */
- public static final String ACTION_MARK_NEW_MISSED_CALLS_AS_OLD =
- "com.android.dialer.calllog.ACTION_MARK_NEW_MISSED_CALLS_AS_OLD";
-
- /** Action to call back a missed call. */
- public static final String ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION =
- "com.android.dialer.calllog.CALL_BACK_FROM_MISSED_CALL_NOTIFICATION";
-
- public static final String ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION =
- "com.android.dialer.calllog.SEND_SMS_FROM_MISSED_CALL_NOTIFICATION";
-
- /**
- * Extra to be included with {@link #ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS},
- * {@link #ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION} and
- * {@link #ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION} to identify the number to display,
- * call or text back.
- * <p>
- * It must be a {@link String}.
- */
- public static final String EXTRA_MISSED_CALL_NUMBER = "MISSED_CALL_NUMBER";
-
- /**
- * Extra to be included with {@link #ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS} to represent the
- * number of missed calls.
- * <p>
- * It must be a {@link Integer}
- */
- public static final String EXTRA_MISSED_CALL_COUNT =
- "MISSED_CALL_COUNT";
-
- public static final int UNKNOWN_MISSED_CALL_COUNT = -1;
-
- private VoicemailQueryHandler mVoicemailQueryHandler;
-
- public CallLogNotificationsService() {
- super("CallLogNotificationsService");
- }
-
- @Override
- protected void onHandleIntent(Intent intent) {
- if (intent == null) {
- Log.d(TAG, "onHandleIntent: could not handle null intent");
- return;
- }
-
- if (!PermissionsUtil.hasPermission(this, android.Manifest.permission.READ_CALL_LOG)) {
- return;
- }
-
- String action = intent.getAction();
- switch (action) {
- case ACTION_MARK_NEW_VOICEMAILS_AS_OLD:
- if (mVoicemailQueryHandler == null) {
- mVoicemailQueryHandler = new VoicemailQueryHandler(this, getContentResolver());
- }
- mVoicemailQueryHandler.markNewVoicemailsAsOld();
- break;
- case ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS:
- Uri voicemailUri = (Uri) intent.getParcelableExtra(EXTRA_NEW_VOICEMAIL_URI);
- DefaultVoicemailNotifier.getInstance(this).updateNotification(voicemailUri);
- break;
- case ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS:
- int count = intent.getIntExtra(EXTRA_MISSED_CALL_COUNT,
- UNKNOWN_MISSED_CALL_COUNT);
- String number = intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER);
- MissedCallNotifier.getInstance(this).updateMissedCallNotification(count, number);
- break;
- case ACTION_MARK_NEW_MISSED_CALLS_AS_OLD:
- CallLogNotificationsHelper.removeMissedCallNotifications(this);
- break;
- case ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION:
- MissedCallNotifier.getInstance(this).callBackFromMissedCall(
- intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER));
- break;
- case ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION:
- MissedCallNotifier.getInstance(this).sendSmsFromMissedCall(
- intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER));
- break;
- default:
- Log.d(TAG, "onHandleIntent: could not handle: " + intent);
- break;
- }
- }
-
- /**
- * Updates notifications for any new voicemails.
- *
- * @param context a valid context.
- * @param voicemailUri The uri pointing to the voicemail to update the notification for. If
- * {@code null}, then notifications for all new voicemails will be updated.
- */
- public static void updateVoicemailNotifications(Context context, Uri voicemailUri) {
- if (TelecomUtil.hasReadWriteVoicemailPermissions(context)) {
- Intent serviceIntent = new Intent(context, CallLogNotificationsService.class);
- serviceIntent.setAction(
- CallLogNotificationsService.ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS);
- // If voicemailUri is null, then notifications for all voicemails will be updated.
- if (voicemailUri != null) {
- serviceIntent.putExtra(
- CallLogNotificationsService.EXTRA_NEW_VOICEMAIL_URI, voicemailUri);
- }
- context.startService(serviceIntent);
- }
- }
-
- /**
- * Updates notifications for any new missed calls.
- *
- * @param context A valid context.
- * @param count The number of new missed calls.
- * @param number The phone number of the newest missed call.
- */
- public static void updateMissedCallNotifications(Context context, int count,
- String number) {
- Intent serviceIntent = new Intent(context, CallLogNotificationsService.class);
- serviceIntent.setAction(
- CallLogNotificationsService.ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS);
- serviceIntent.putExtra(EXTRA_MISSED_CALL_COUNT, count);
- serviceIntent.putExtra(EXTRA_MISSED_CALL_NUMBER, number);
- context.startService(serviceIntent);
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogQuery.java b/src/com/android/dialer/calllog/CallLogQuery.java
deleted file mode 100644
index e1a41199a..000000000
--- a/src/com/android/dialer/calllog/CallLogQuery.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import com.google.common.collect.Lists;
-
-import android.provider.CallLog.Calls;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.dialer.compat.CallsSdkCompat;
-import com.android.dialer.compat.DialerCompatUtils;
-
-import java.util.List;
-
-/**
- * The query for the call log table.
- */
-public final class CallLogQuery {
-
- private static final String[] _PROJECTION_INTERNAL = new String[] {
- Calls._ID, // 0
- Calls.NUMBER, // 1
- Calls.DATE, // 2
- Calls.DURATION, // 3
- Calls.TYPE, // 4
- Calls.COUNTRY_ISO, // 5
- Calls.VOICEMAIL_URI, // 6
- Calls.GEOCODED_LOCATION, // 7
- Calls.CACHED_NAME, // 8
- Calls.CACHED_NUMBER_TYPE, // 9
- Calls.CACHED_NUMBER_LABEL, // 10
- Calls.CACHED_LOOKUP_URI, // 11
- Calls.CACHED_MATCHED_NUMBER, // 12
- Calls.CACHED_NORMALIZED_NUMBER, // 13
- Calls.CACHED_PHOTO_ID, // 14
- Calls.CACHED_FORMATTED_NUMBER, // 15
- Calls.IS_READ, // 16
- Calls.NUMBER_PRESENTATION, // 17
- Calls.PHONE_ACCOUNT_COMPONENT_NAME, // 18
- Calls.PHONE_ACCOUNT_ID, // 19
- Calls.FEATURES, // 20
- Calls.DATA_USAGE, // 21
- Calls.TRANSCRIPTION, // 22
- };
-
- public static final int ID = 0;
- public static final int NUMBER = 1;
- public static final int DATE = 2;
- public static final int DURATION = 3;
- public static final int CALL_TYPE = 4;
- public static final int COUNTRY_ISO = 5;
- public static final int VOICEMAIL_URI = 6;
- public static final int GEOCODED_LOCATION = 7;
- public static final int CACHED_NAME = 8;
- public static final int CACHED_NUMBER_TYPE = 9;
- public static final int CACHED_NUMBER_LABEL = 10;
- public static final int CACHED_LOOKUP_URI = 11;
- public static final int CACHED_MATCHED_NUMBER = 12;
- public static final int CACHED_NORMALIZED_NUMBER = 13;
- public static final int CACHED_PHOTO_ID = 14;
- public static final int CACHED_FORMATTED_NUMBER = 15;
- public static final int IS_READ = 16;
- public static final int NUMBER_PRESENTATION = 17;
- public static final int ACCOUNT_COMPONENT_NAME = 18;
- public static final int ACCOUNT_ID = 19;
- public static final int FEATURES = 20;
- public static final int DATA_USAGE = 21;
- public static final int TRANSCRIPTION = 22;
-
- // Indices for columns that may not be available, depending on the Sdk Version
- /**
- * Only available in versions >= M
- * Call {@link DialerCompatUtils#isCallsCachedPhotoUriCompatible()} prior to use
- */
- public static int CACHED_PHOTO_URI = -1;
-
- /**
- * Only available in versions > M
- * Call {@link CompatUtils#isNCompatible()} prior to use
- */
- public static int POST_DIAL_DIGITS = -1;
- public static int VIA_NUMBER = -1;
-
- public static final String[] _PROJECTION;
-
- static {
- List<String> projectionList = Lists.newArrayList(_PROJECTION_INTERNAL);
- if (DialerCompatUtils.isCallsCachedPhotoUriCompatible()) {
- projectionList.add(Calls.CACHED_PHOTO_URI);
- CACHED_PHOTO_URI = projectionList.size() - 1;
- }
- if (CompatUtils.isNCompatible()) {
- projectionList.add(CallsSdkCompat.POST_DIAL_DIGITS);
- POST_DIAL_DIGITS = projectionList.size() - 1;
- projectionList.add(CallsSdkCompat.VIA_NUMBER);
- VIA_NUMBER = projectionList.size() - 1;
- }
- _PROJECTION = projectionList.toArray(new String[projectionList.size()]);
- }
-
-}
diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java
deleted file mode 100644
index cf86bad7f..000000000
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.AsyncQueryHandler;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabaseCorruptException;
-import android.database.sqlite.SQLiteDiskIOException;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteFullException;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.CallLog.Calls;
-import android.provider.VoicemailContract.Status;
-import android.provider.VoicemailContract.Voicemails;
-import android.util.Log;
-
-import com.android.contacts.common.compat.SdkVersionOverride;
-import com.android.contacts.common.database.NoNullCursorAsyncQueryHandler;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.database.VoicemailArchiveContract;
-import com.android.dialer.util.AppCompatConstants;
-import com.android.dialer.util.TelecomUtil;
-import com.android.dialer.voicemail.VoicemailStatusHelperImpl;
-
-import com.google.common.collect.Lists;
-
-import java.lang.ref.WeakReference;
-import java.util.List;
-
-/** Handles asynchronous queries to the call log. */
-public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
- private static final String TAG = "CallLogQueryHandler";
- private static final int NUM_LOGS_TO_DISPLAY = 1000;
-
- /** The token for the query to fetch the old entries from the call log. */
- private static final int QUERY_CALLLOG_TOKEN = 54;
- /** The token for the query to mark all missed calls as old after seeing the call log. */
- private static final int UPDATE_MARK_AS_OLD_TOKEN = 55;
- /** The token for the query to mark all missed calls as read after seeing the call log. */
- private static final int UPDATE_MARK_MISSED_CALL_AS_READ_TOKEN = 56;
- /** The token for the query to fetch voicemail status messages. */
- private static final int QUERY_VOICEMAIL_STATUS_TOKEN = 57;
- /** The token for the query to fetch the number of unread voicemails. */
- private static final int QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN = 58;
- /** The token for the query to fetch the number of missed calls. */
- private static final int QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN = 59;
- /** The oken for the query to fetch the archived voicemails. */
- private static final int QUERY_VOICEMAIL_ARCHIVE = 60;
-
- private final int mLogLimit;
-
- /**
- * Call type similar to Calls.INCOMING_TYPE used to specify all types instead of one particular
- * type. Exception: excludes Calls.VOICEMAIL_TYPE.
- */
- public static final int CALL_TYPE_ALL = -1;
-
- private final WeakReference<Listener> mListener;
-
- private final Context mContext;
-
- /**
- * Simple handler that wraps background calls to catch
- * {@link SQLiteException}, such as when the disk is full.
- */
- protected class CatchingWorkerHandler extends AsyncQueryHandler.WorkerHandler {
- public CatchingWorkerHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- try {
- // Perform same query while catching any exceptions
- super.handleMessage(msg);
- } catch (SQLiteDiskIOException e) {
- Log.w(TAG, "Exception on background worker thread", e);
- } catch (SQLiteFullException e) {
- Log.w(TAG, "Exception on background worker thread", e);
- } catch (SQLiteDatabaseCorruptException e) {
- Log.w(TAG, "Exception on background worker thread", e);
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "ContactsProvider not present on device", e);
- } catch (SecurityException e) {
- // Shouldn't happen if we are protecting the entry points correctly,
- // but just in case.
- Log.w(TAG, "No permission to access ContactsProvider.", e);
- }
- }
- }
-
- @Override
- protected Handler createHandler(Looper looper) {
- // Provide our special handler that catches exceptions
- return new CatchingWorkerHandler(looper);
- }
-
- public CallLogQueryHandler(Context context, ContentResolver contentResolver,
- Listener listener) {
- this(context, contentResolver, listener, -1);
- }
-
- public CallLogQueryHandler(Context context, ContentResolver contentResolver, Listener listener,
- int limit) {
- super(contentResolver);
- mContext = context.getApplicationContext();
- mListener = new WeakReference<Listener>(listener);
- mLogLimit = limit;
- }
-
- /**
- * Fetch all the voicemails in the voicemail archive.
- */
- public void fetchVoicemailArchive() {
- startQuery(QUERY_VOICEMAIL_ARCHIVE, null,
- VoicemailArchiveContract.VoicemailArchive.CONTENT_URI,
- null, VoicemailArchiveContract.VoicemailArchive.ARCHIVED + " = 1", null,
- VoicemailArchiveContract.VoicemailArchive.DATE + " DESC");
- }
-
-
- /**
- * Fetches the list of calls from the call log for a given type.
- * This call ignores the new or old state.
- * <p>
- * It will asynchronously update the content of the list view when the fetch completes.
- */
- public void fetchCalls(int callType, long newerThan) {
- cancelFetch();
- if (PermissionsUtil.hasPhonePermissions(mContext)) {
- fetchCalls(QUERY_CALLLOG_TOKEN, callType, false /* newOnly */, newerThan);
- } else {
- updateAdapterData(null);
- }
- }
-
- public void fetchCalls(int callType) {
- fetchCalls(callType, 0);
- }
-
- public void fetchVoicemailStatus() {
- if (TelecomUtil.hasReadWriteVoicemailPermissions(mContext)) {
- startQuery(QUERY_VOICEMAIL_STATUS_TOKEN, null, Status.CONTENT_URI,
- VoicemailStatusHelperImpl.PROJECTION, null, null, null);
- }
- }
-
- public void fetchVoicemailUnreadCount() {
- if (TelecomUtil.hasReadWriteVoicemailPermissions(mContext)) {
- // Only count voicemails that have not been read and have not been deleted.
- startQuery(QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN, null, Voicemails.CONTENT_URI,
- new String[] { Voicemails._ID },
- Voicemails.IS_READ + "=0" + " AND " + Voicemails.DELETED + "=0", null, null);
- }
- }
-
- /** Fetches the list of calls in the call log. */
- private void fetchCalls(int token, int callType, boolean newOnly, long newerThan) {
- StringBuilder where = new StringBuilder();
- List<String> selectionArgs = Lists.newArrayList();
-
- // Always hide blocked calls.
- where.append("(").append(Calls.TYPE).append(" != ?)");
- selectionArgs.add(Integer.toString(AppCompatConstants.CALLS_BLOCKED_TYPE));
-
- // Ignore voicemails marked as deleted
- if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M)
- >= Build.VERSION_CODES.M) {
- where.append(" AND (").append(Voicemails.DELETED).append(" = 0)");
- }
-
- if (newOnly) {
- where.append(" AND (").append(Calls.NEW).append(" = 1)");
- }
-
- if (callType > CALL_TYPE_ALL) {
- where.append(" AND (").append(Calls.TYPE).append(" = ?)");
- selectionArgs.add(Integer.toString(callType));
- } else {
- where.append(" AND NOT ");
- where.append("(" + Calls.TYPE + " = " + AppCompatConstants.CALLS_VOICEMAIL_TYPE + ")");
- }
-
- if (newerThan > 0) {
- where.append(" AND (").append(Calls.DATE).append(" > ?)");
- selectionArgs.add(Long.toString(newerThan));
- }
-
- final int limit = (mLogLimit == -1) ? NUM_LOGS_TO_DISPLAY : mLogLimit;
- final String selection = where.length() > 0 ? where.toString() : null;
- Uri uri = TelecomUtil.getCallLogUri(mContext).buildUpon()
- .appendQueryParameter(Calls.LIMIT_PARAM_KEY, Integer.toString(limit))
- .build();
- startQuery(token, null, uri, CallLogQuery._PROJECTION, selection, selectionArgs.toArray(
- new String[selectionArgs.size()]), Calls.DEFAULT_SORT_ORDER);
- }
-
- /** Cancel any pending fetch request. */
- private void cancelFetch() {
- cancelOperation(QUERY_CALLLOG_TOKEN);
- }
-
- /** Updates all new calls to mark them as old. */
- public void markNewCallsAsOld() {
- if (!PermissionsUtil.hasPhonePermissions(mContext)) {
- return;
- }
- // Mark all "new" calls as not new anymore.
- StringBuilder where = new StringBuilder();
- where.append(Calls.NEW);
- where.append(" = 1");
-
- ContentValues values = new ContentValues(1);
- values.put(Calls.NEW, "0");
-
- startUpdate(UPDATE_MARK_AS_OLD_TOKEN, null, TelecomUtil.getCallLogUri(mContext),
- values, where.toString(), null);
- }
-
- /** Updates all missed calls to mark them as read. */
- public void markMissedCallsAsRead() {
- if (!PermissionsUtil.hasPhonePermissions(mContext)) {
- return;
- }
-
- ContentValues values = new ContentValues(1);
- values.put(Calls.IS_READ, "1");
-
- startUpdate(UPDATE_MARK_MISSED_CALL_AS_READ_TOKEN, null, Calls.CONTENT_URI, values,
- getUnreadMissedCallsQuery(), null);
- }
-
- /** Fetch all missed calls received since last time the tab was opened. */
- public void fetchMissedCallsUnreadCount() {
- if (!PermissionsUtil.hasPhonePermissions(mContext)) {
- return;
- }
-
- startQuery(QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN, null, Calls.CONTENT_URI,
- new String[]{Calls._ID}, getUnreadMissedCallsQuery(), null, null);
- }
-
-
- @Override
- protected synchronized void onNotNullableQueryComplete(int token, Object cookie,
- Cursor cursor) {
- if (cursor == null) {
- return;
- }
- try {
- if (token == QUERY_CALLLOG_TOKEN || token == QUERY_VOICEMAIL_ARCHIVE) {
- if (updateAdapterData(cursor)) {
- cursor = null;
- }
- } else if (token == QUERY_VOICEMAIL_STATUS_TOKEN) {
- updateVoicemailStatus(cursor);
- } else if (token == QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN) {
- updateVoicemailUnreadCount(cursor);
- } else if (token == QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN) {
- updateMissedCallsUnreadCount(cursor);
- } else {
- Log.w(TAG, "Unknown query completed: ignoring: " + token);
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- /**
- * Updates the adapter in the call log fragment to show the new cursor data.
- * Returns true if the listener took ownership of the cursor.
- */
- private boolean updateAdapterData(Cursor cursor) {
- final Listener listener = mListener.get();
- if (listener != null) {
- return listener.onCallsFetched(cursor);
- }
- return false;
-
- }
-
- /**
- * @return Query string to get all unread missed calls.
- */
- private String getUnreadMissedCallsQuery() {
- StringBuilder where = new StringBuilder();
- where.append(Calls.IS_READ).append(" = 0 OR ").append(Calls.IS_READ).append(" IS NULL");
- where.append(" AND ");
- where.append(Calls.TYPE).append(" = ").append(Calls.MISSED_TYPE);
- return where.toString();
- }
-
- private void updateVoicemailStatus(Cursor statusCursor) {
- final Listener listener = mListener.get();
- if (listener != null) {
- listener.onVoicemailStatusFetched(statusCursor);
- }
- }
-
- private void updateVoicemailUnreadCount(Cursor statusCursor) {
- final Listener listener = mListener.get();
- if (listener != null) {
- listener.onVoicemailUnreadCountFetched(statusCursor);
- }
- }
-
- private void updateMissedCallsUnreadCount(Cursor statusCursor) {
- final Listener listener = mListener.get();
- if (listener != null) {
- listener.onMissedCallsUnreadCountFetched(statusCursor);
- }
- }
-
- /** Listener to completion of various queries. */
- public interface Listener {
- /** Called when {@link CallLogQueryHandler#fetchVoicemailStatus()} completes. */
- void onVoicemailStatusFetched(Cursor statusCursor);
-
- /** Called when {@link CallLogQueryHandler#fetchVoicemailUnreadCount()} completes. */
- void onVoicemailUnreadCountFetched(Cursor cursor);
-
- /** Called when {@link CallLogQueryHandler#fetchMissedCallsUnreadCount()} completes. */
- void onMissedCallsUnreadCountFetched(Cursor cursor);
-
- /**
- * Called when {@link CallLogQueryHandler#fetchCalls(int)} complete.
- * Returns true if takes ownership of cursor.
- */
- boolean onCallsFetched(Cursor combinedCursor);
- }
-}
diff --git a/src/com/android/dialer/calllog/CallLogReceiver.java b/src/com/android/dialer/calllog/CallLogReceiver.java
deleted file mode 100644
index fef76086c..000000000
--- a/src/com/android/dialer/calllog/CallLogReceiver.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.provider.VoicemailContract;
-import android.util.Log;
-
-/**
- * Receiver for call log events.
- * <p>
- * It is currently used to handle {@link VoicemailContract#ACTION_NEW_VOICEMAIL} and
- * {@link Intent#ACTION_BOOT_COMPLETED}.
- */
-public class CallLogReceiver extends BroadcastReceiver {
- private static final String TAG = "CallLogReceiver";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (VoicemailContract.ACTION_NEW_VOICEMAIL.equals(intent.getAction())) {
- CallLogNotificationsService.updateVoicemailNotifications(context, intent.getData());
- } else if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
- CallLogNotificationsService.updateVoicemailNotifications(context, null);
- } else {
- Log.w(TAG, "onReceive: could not handle: " + intent);
- }
- }
-}
diff --git a/src/com/android/dialer/calllog/CallTypeHelper.java b/src/com/android/dialer/calllog/CallTypeHelper.java
deleted file mode 100644
index acc114c5c..000000000
--- a/src/com/android/dialer/calllog/CallTypeHelper.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.res.Resources;
-
-import com.android.dialer.R;
-import com.android.dialer.util.AppCompatConstants;
-
-/**
- * Helper class to perform operations related to call types.
- */
-public class CallTypeHelper {
- /** Name used to identify incoming calls. */
- private final CharSequence mIncomingName;
- /** Name used to identify outgoing calls. */
- private final CharSequence mOutgoingName;
- /** Name used to identify missed calls. */
- private final CharSequence mMissedName;
- /** Name used to identify incoming video calls. */
- private final CharSequence mIncomingVideoName;
- /** Name used to identify outgoing video calls. */
- private final CharSequence mOutgoingVideoName;
- /** Name used to identify missed video calls. */
- private final CharSequence mMissedVideoName;
- /** Name used to identify voicemail calls. */
- private final CharSequence mVoicemailName;
- /** Name used to identify rejected calls. */
- private final CharSequence mRejectedName;
- /** Name used to identify blocked calls. */
- private final CharSequence mBlockedName;
- /** Color used to identify new missed calls. */
- private final int mNewMissedColor;
- /** Color used to identify new voicemail calls. */
- private final int mNewVoicemailColor;
-
- public CallTypeHelper(Resources resources) {
- // Cache these values so that we do not need to look them up each time.
- mIncomingName = resources.getString(R.string.type_incoming);
- mOutgoingName = resources.getString(R.string.type_outgoing);
- mMissedName = resources.getString(R.string.type_missed);
- mIncomingVideoName = resources.getString(R.string.type_incoming_video);
- mOutgoingVideoName = resources.getString(R.string.type_outgoing_video);
- mMissedVideoName = resources.getString(R.string.type_missed_video);
- mVoicemailName = resources.getString(R.string.type_voicemail);
- mRejectedName = resources.getString(R.string.type_rejected);
- mBlockedName = resources.getString(R.string.type_blocked);
- mNewMissedColor = resources.getColor(R.color.call_log_missed_call_highlight_color);
- mNewVoicemailColor = resources.getColor(R.color.call_log_voicemail_highlight_color);
- }
-
- /** Returns the text used to represent the given call type. */
- public CharSequence getCallTypeText(int callType, boolean isVideoCall) {
- switch (callType) {
- case AppCompatConstants.CALLS_INCOMING_TYPE:
- if (isVideoCall) {
- return mIncomingVideoName;
- } else {
- return mIncomingName;
- }
-
- case AppCompatConstants.CALLS_OUTGOING_TYPE:
- if (isVideoCall) {
- return mOutgoingVideoName;
- } else {
- return mOutgoingName;
- }
-
- case AppCompatConstants.CALLS_MISSED_TYPE:
- if (isVideoCall) {
- return mMissedVideoName;
- } else {
- return mMissedName;
- }
-
- case AppCompatConstants.CALLS_VOICEMAIL_TYPE:
- return mVoicemailName;
-
- case AppCompatConstants.CALLS_REJECTED_TYPE:
- return mRejectedName;
-
- case AppCompatConstants.CALLS_BLOCKED_TYPE:
- return mBlockedName;
-
- default:
- return mMissedName;
- }
- }
-
- /** Returns the color used to highlight the given call type, null if not highlight is needed. */
- public Integer getHighlightedColor(int callType) {
- switch (callType) {
- case AppCompatConstants.CALLS_INCOMING_TYPE:
- // New incoming calls are not highlighted.
- return null;
-
- case AppCompatConstants.CALLS_OUTGOING_TYPE:
- // New outgoing calls are not highlighted.
- return null;
-
- case AppCompatConstants.CALLS_MISSED_TYPE:
- return mNewMissedColor;
-
- case AppCompatConstants.CALLS_VOICEMAIL_TYPE:
- return mNewVoicemailColor;
-
- default:
- // Don't highlight calls of unknown types. They are treated as missed calls by
- // the rest of the UI, but since they will never be marked as read by
- // {@link CallLogQueryHandler}, just don't ever highlight them anyway.
- return null;
- }
- }
-
- public static boolean isMissedCallType(int callType) {
- return (callType != AppCompatConstants.CALLS_INCOMING_TYPE
- && callType != AppCompatConstants.CALLS_OUTGOING_TYPE
- && callType != AppCompatConstants.CALLS_VOICEMAIL_TYPE);
- }
-}
diff --git a/src/com/android/dialer/calllog/CallTypeIconsView.java b/src/com/android/dialer/calllog/CallTypeIconsView.java
deleted file mode 100644
index 14748433c..000000000
--- a/src/com/android/dialer/calllog/CallTypeIconsView.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.contacts.common.util.BitmapUtil;
-import com.android.dialer.R;
-import com.android.dialer.util.AppCompatConstants;
-import com.google.common.collect.Lists;
-
-import java.util.List;
-
-/**
- * View that draws one or more symbols for different types of calls (missed calls, outgoing etc).
- * The symbols are set up horizontally. As this view doesn't create subviews, it is better suited
- * for ListView-recycling that a regular LinearLayout using ImageViews.
- */
-public class CallTypeIconsView extends View {
- private List<Integer> mCallTypes = Lists.newArrayListWithCapacity(3);
- private boolean mShowVideo = false;
- private int mWidth;
- private int mHeight;
-
- private static Resources sResources;
-
- public CallTypeIconsView(Context context) {
- this(context, null);
- }
-
- public CallTypeIconsView(Context context, AttributeSet attrs) {
- super(context, attrs);
- if (sResources == null) {
- sResources = new Resources(context);
- }
- }
-
- public void clear() {
- mCallTypes.clear();
- mWidth = 0;
- mHeight = 0;
- invalidate();
- }
-
- public void add(int callType) {
- mCallTypes.add(callType);
-
- final Drawable drawable = getCallTypeDrawable(callType);
- mWidth += drawable.getIntrinsicWidth() + sResources.iconMargin;
- mHeight = Math.max(mHeight, drawable.getIntrinsicHeight());
- invalidate();
- }
-
- /**
- * Determines whether the video call icon will be shown.
- *
- * @param showVideo True where the video icon should be shown.
- */
- public void setShowVideo(boolean showVideo) {
- mShowVideo = showVideo;
- if (showVideo) {
- mWidth += sResources.videoCall.getIntrinsicWidth();
- mHeight = Math.max(mHeight, sResources.videoCall.getIntrinsicHeight());
- invalidate();
- }
- }
-
- /**
- * Determines if the video icon should be shown.
- *
- * @return True if the video icon should be shown.
- */
- public boolean isVideoShown() {
- return mShowVideo;
- }
-
- @NeededForTesting
- public int getCount() {
- return mCallTypes.size();
- }
-
- @NeededForTesting
- public int getCallType(int index) {
- return mCallTypes.get(index);
- }
-
- private Drawable getCallTypeDrawable(int callType) {
- switch (callType) {
- case AppCompatConstants.CALLS_INCOMING_TYPE:
- return sResources.incoming;
- case AppCompatConstants.CALLS_OUTGOING_TYPE:
- return sResources.outgoing;
- case AppCompatConstants.CALLS_MISSED_TYPE:
- return sResources.missed;
- case AppCompatConstants.CALLS_VOICEMAIL_TYPE:
- return sResources.voicemail;
- case AppCompatConstants.CALLS_BLOCKED_TYPE:
- return sResources.blocked;
- default:
- // It is possible for users to end up with calls with unknown call types in their
- // call history, possibly due to 3rd party call log implementations (e.g. to
- // distinguish between rejected and missed calls). Instead of crashing, just
- // assume that all unknown call types are missed calls.
- return sResources.missed;
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(mWidth, mHeight);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- int left = 0;
- for (Integer callType : mCallTypes) {
- final Drawable drawable = getCallTypeDrawable(callType);
- final int right = left + drawable.getIntrinsicWidth();
- drawable.setBounds(left, 0, right, drawable.getIntrinsicHeight());
- drawable.draw(canvas);
- left = right + sResources.iconMargin;
- }
-
- // If showing the video call icon, draw it scaled appropriately.
- if (mShowVideo) {
- final Drawable drawable = sResources.videoCall;
- final int right = left + sResources.videoCall.getIntrinsicWidth();
- drawable.setBounds(left, 0, right, sResources.videoCall.getIntrinsicHeight());
- drawable.draw(canvas);
- }
- }
-
- private static class Resources {
-
- // Drawable representing an incoming answered call.
- public final Drawable incoming;
-
- // Drawable respresenting an outgoing call.
- public final Drawable outgoing;
-
- // Drawable representing an incoming missed call.
- public final Drawable missed;
-
- // Drawable representing a voicemail.
- public final Drawable voicemail;
-
- // Drawable representing a blocked call.
- public final Drawable blocked;
-
- // Drawable repesenting a video call.
- public final Drawable videoCall;
-
- /**
- * The margin to use for icons.
- */
- public final int iconMargin;
-
- /**
- * Configures the call icon drawables.
- * A single white call arrow which points down and left is used as a basis for all of the
- * call arrow icons, applying rotation and colors as needed.
- *
- * @param context The current context.
- */
- public Resources(Context context) {
- final android.content.res.Resources r = context.getResources();
-
- incoming = r.getDrawable(R.drawable.ic_call_arrow);
- incoming.setColorFilter(r.getColor(R.color.answered_call), PorterDuff.Mode.MULTIPLY);
-
- // Create a rotated instance of the call arrow for outgoing calls.
- outgoing = BitmapUtil.getRotatedDrawable(r, R.drawable.ic_call_arrow, 180f);
- outgoing.setColorFilter(r.getColor(R.color.answered_call), PorterDuff.Mode.MULTIPLY);
-
- // Need to make a copy of the arrow drawable, otherwise the same instance colored
- // above will be recolored here.
- missed = r.getDrawable(R.drawable.ic_call_arrow).mutate();
- missed.setColorFilter(r.getColor(R.color.missed_call), PorterDuff.Mode.MULTIPLY);
-
- voicemail = r.getDrawable(R.drawable.ic_call_voicemail_holo_dark);
-
- blocked = getScaledBitmap(context, R.drawable.ic_block_24dp);
- blocked.setColorFilter(r.getColor(R.color.blocked_call), PorterDuff.Mode.MULTIPLY);
-
- videoCall = getScaledBitmap(context, R.drawable.ic_videocam_24dp);
- videoCall.setColorFilter(r.getColor(R.color.dialtacts_secondary_text_color),
- PorterDuff.Mode.MULTIPLY);
-
- iconMargin = r.getDimensionPixelSize(R.dimen.call_log_icon_margin);
- }
-
- // Gets the icon, scaled to the height of the call type icons. This helps display all the
- // icons to be the same height, while preserving their width aspect ratio.
- private Drawable getScaledBitmap(Context context, int resourceId) {
- Bitmap icon = BitmapFactory.decodeResource(context.getResources(), resourceId);
- int scaledHeight =
- context.getResources().getDimensionPixelSize(R.dimen.call_type_icon_size);
- int scaledWidth = (int) ((float) icon.getWidth()
- * ((float) scaledHeight / (float) icon.getHeight()));
- Bitmap scaledIcon = Bitmap.createScaledBitmap(icon, scaledWidth, scaledHeight, false);
- return new BitmapDrawable(context.getResources(), scaledIcon);
- }
- }
-}
diff --git a/src/com/android/dialer/calllog/ClearCallLogDialog.java b/src/com/android/dialer/calllog/ClearCallLogDialog.java
deleted file mode 100644
index bef5010ec..000000000
--- a/src/com/android/dialer/calllog/ClearCallLogDialog.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.FragmentManager;
-import android.app.ProgressDialog;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.provider.CallLog.Calls;
-
-import com.android.dialer.R;
-import com.android.dialer.service.CachedNumberLookupService;
-import com.android.dialerbind.ObjectFactory;
-
-/**
- * Dialog that clears the call log after confirming with the user
- */
-public class ClearCallLogDialog extends DialogFragment {
- private static final CachedNumberLookupService mCachedNumberLookupService =
- ObjectFactory.newCachedNumberLookupService();
-
- /** Preferred way to show this dialog */
- public static void show(FragmentManager fragmentManager) {
- ClearCallLogDialog dialog = new ClearCallLogDialog();
- dialog.show(fragmentManager, "deleteCallLog");
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final ContentResolver resolver = getActivity().getContentResolver();
- final Context context = getActivity().getApplicationContext();
- final OnClickListener okListener = new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final ProgressDialog progressDialog = ProgressDialog.show(getActivity(),
- getString(R.string.clearCallLogProgress_title),
- "", true, false);
- progressDialog.setOwnerActivity(getActivity());
- final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- resolver.delete(Calls.CONTENT_URI, null, null);
- if (mCachedNumberLookupService != null) {
- mCachedNumberLookupService.clearAllCacheEntries(context);
- }
- return null;
- }
- @Override
- protected void onPostExecute(Void result) {
- final Activity activity = progressDialog.getOwnerActivity();
-
- if (activity == null || activity.isDestroyed() || activity.isFinishing()) {
- return;
- }
-
- if (progressDialog != null && progressDialog.isShowing()) {
- progressDialog.dismiss();
- }
- }
- };
- // TODO: Once we have the API, we should configure this ProgressDialog
- // to only show up after a certain time (e.g. 150ms)
- progressDialog.show();
- task.execute();
- }
- };
- return new AlertDialog.Builder(getActivity())
- .setTitle(R.string.clearCallLogConfirmation_title)
- .setIconAttribute(android.R.attr.alertDialogIcon)
- .setMessage(R.string.clearCallLogConfirmation)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, okListener)
- .setCancelable(true)
- .create();
- }
-}
diff --git a/src/com/android/dialer/calllog/ContactInfo.java b/src/com/android/dialer/calllog/ContactInfo.java
deleted file mode 100644
index 8fe4964bc..000000000
--- a/src/com/android/dialer/calllog/ContactInfo.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.net.Uri;
-import android.text.TextUtils;
-
-import com.android.contacts.common.ContactsUtils.UserType;
-import com.android.contacts.common.util.UriUtils;
-import com.google.common.base.Objects;
-
-/**
- * Information for a contact as needed by the Call Log.
- */
-public class ContactInfo {
- public Uri lookupUri;
-
- /**
- * Contact lookup key. Note this may be a lookup key for a corp contact, in which case
- * "lookup by lookup key" doesn't work on the personal profile.
- */
- public String lookupKey;
- public String name;
- public String nameAlternative;
- public int type;
- public String label;
- public String number;
- public String formattedNumber;
- /*
- * ContactInfo.normalizedNumber is a column value returned by PhoneLookup query. By definition,
- * it's E164 representation.
- * http://developer.android.com/reference/android/provider/ContactsContract.PhoneLookupColumns.
- * html#NORMALIZED_NUMBER.
- *
- * The fallback value, when PhoneLookup fails or else, should be either null or
- * PhoneNumberUtils.formatNumberToE164.
- */
- public String normalizedNumber;
- /** The photo for the contact, if available. */
- public long photoId;
- /** The high-res photo for the contact, if available. */
- public Uri photoUri;
- public boolean isBadData;
- public String objectId;
- public @UserType long userType;
-
- public static ContactInfo EMPTY = new ContactInfo();
-
- public int sourceType = 0;
-
- @Override
- public int hashCode() {
- // Uses only name and contactUri to determine hashcode.
- // This should be sufficient to have a reasonable distribution of hash codes.
- // Moreover, there should be no two people with the same lookupUri.
- final int prime = 31;
- int result = 1;
- result = prime * result + ((lookupUri == null) ? 0 : lookupUri.hashCode());
- result = prime * result + ((name == null) ? 0 : name.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
- ContactInfo other = (ContactInfo) obj;
- if (!UriUtils.areEqual(lookupUri, other.lookupUri)) return false;
- if (!TextUtils.equals(name, other.name)) return false;
- if (!TextUtils.equals(nameAlternative, other.nameAlternative)) return false;
- if (type != other.type) return false;
- if (!TextUtils.equals(label, other.label)) return false;
- if (!TextUtils.equals(number, other.number)) return false;
- if (!TextUtils.equals(formattedNumber, other.formattedNumber)) return false;
- if (!TextUtils.equals(normalizedNumber, other.normalizedNumber)) return false;
- if (photoId != other.photoId) return false;
- if (!UriUtils.areEqual(photoUri, other.photoUri)) return false;
- if (!TextUtils.equals(objectId, other.objectId)) return false;
- if (userType != other.userType) return false;
- return true;
- }
-
- @Override
- public String toString() {
- return Objects.toStringHelper(this).add("lookupUri", lookupUri).add("name", name)
- .add("nameAlternative", nameAlternative)
- .add("type", type).add("label", label)
- .add("number", number).add("formattedNumber",formattedNumber)
- .add("normalizedNumber", normalizedNumber).add("photoId", photoId)
- .add("photoUri", photoUri).add("objectId", objectId)
- .add("userType",userType).toString();
- }
-}
diff --git a/src/com/android/dialer/calllog/ContactInfoHelper.java b/src/com/android/dialer/calllog/ContactInfoHelper.java
deleted file mode 100644
index d9898ab94..000000000
--- a/src/com/android/dialer/calllog/ContactInfoHelper.java
+++ /dev/null
@@ -1,499 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import com.google.common.primitives.Longs;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteFullException;
-import android.net.Uri;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.DisplayNameSources;
-import android.provider.ContactsContract.PhoneLookup;
-import android.support.annotation.Nullable;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.ContactsUtils.UserType;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.DirectoryCompat;
-import com.android.contacts.common.util.Constants;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.common.util.PhoneNumberHelper;
-import com.android.contacts.common.util.UriUtils;
-import com.android.dialer.compat.DialerCompatUtils;
-import com.android.dialer.service.CachedNumberLookupService;
-import com.android.dialer.service.CachedNumberLookupService.CachedContactInfo;
-import com.android.dialer.util.TelecomUtil;
-import com.android.dialerbind.ObjectFactory;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-/**
- * Utility class to look up the contact information for a given number.
- */
-public class ContactInfoHelper {
- private static final String TAG = ContactInfoHelper.class.getSimpleName();
-
- private final Context mContext;
- private final String mCurrentCountryIso;
-
- private static final CachedNumberLookupService mCachedNumberLookupService =
- ObjectFactory.newCachedNumberLookupService();
-
- public ContactInfoHelper(Context context, String currentCountryIso) {
- mContext = context;
- mCurrentCountryIso = currentCountryIso;
- }
-
- /**
- * Returns the contact information for the given number.
- * <p>
- * If the number does not match any contact, returns a contact info containing only the number
- * and the formatted number.
- * <p>
- * If an error occurs during the lookup, it returns null.
- *
- * @param number the number to look up
- * @param countryIso the country associated with this number
- */
- @Nullable
- public ContactInfo lookupNumber(String number, String countryIso) {
- if (TextUtils.isEmpty(number)) {
- return null;
- }
-
- ContactInfo info;
-
- if (PhoneNumberHelper.isUriNumber(number)) {
- // The number is a SIP address..
- info = lookupContactFromUri(getContactInfoLookupUri(number), true);
- if (info == null || info == ContactInfo.EMPTY) {
- // If lookup failed, check if the "username" of the SIP address is a phone number.
- String username = PhoneNumberHelper.getUsernameFromUriNumber(number);
- if (PhoneNumberUtils.isGlobalPhoneNumber(username)) {
- info = queryContactInfoForPhoneNumber(username, countryIso, true);
- }
- }
- } else {
- // Look for a contact that has the given phone number.
- info = queryContactInfoForPhoneNumber(number, countryIso, false);
- }
-
- final ContactInfo updatedInfo;
- if (info == null) {
- // The lookup failed.
- updatedInfo = null;
- } else {
- // If we did not find a matching contact, generate an empty contact info for the number.
- if (info == ContactInfo.EMPTY) {
- // Did not find a matching contact.
- updatedInfo = new ContactInfo();
- updatedInfo.number = number;
- updatedInfo.formattedNumber = formatPhoneNumber(number, null, countryIso);
- updatedInfo.normalizedNumber = PhoneNumberUtils.formatNumberToE164(
- number, countryIso);
- updatedInfo.lookupUri = createTemporaryContactUri(updatedInfo.formattedNumber);
- } else {
- updatedInfo = info;
- }
- }
- return updatedInfo;
- }
-
- /**
- * Creates a JSON-encoded lookup uri for a unknown number without an associated contact
- *
- * @param number - Unknown phone number
- * @return JSON-encoded URI that can be used to perform a lookup when clicking on the quick
- * contact card.
- */
- private static Uri createTemporaryContactUri(String number) {
- try {
- final JSONObject contactRows = new JSONObject().put(Phone.CONTENT_ITEM_TYPE,
- new JSONObject().put(Phone.NUMBER, number).put(Phone.TYPE, Phone.TYPE_CUSTOM));
-
- final String jsonString = new JSONObject().put(Contacts.DISPLAY_NAME, number)
- .put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.PHONE)
- .put(Contacts.CONTENT_ITEM_TYPE, contactRows).toString();
-
- return Contacts.CONTENT_LOOKUP_URI
- .buildUpon()
- .appendPath(Constants.LOOKUP_URI_ENCODED)
- .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
- String.valueOf(Long.MAX_VALUE))
- .encodedFragment(jsonString)
- .build();
- } catch (JSONException e) {
- return null;
- }
- }
-
- /**
- * Looks up a contact using the given URI.
- * <p>
- * It returns null if an error occurs, {@link ContactInfo#EMPTY} if no matching contact is
- * found, or the {@link ContactInfo} for the given contact.
- * <p>
- * The {@link ContactInfo#formattedNumber} field is always set to {@code null} in the returned
- * value.
- */
- ContactInfo lookupContactFromUri(Uri uri, boolean isSip) {
- if (uri == null) {
- return null;
- }
- if (!PermissionsUtil.hasContactsPermissions(mContext)) {
- return ContactInfo.EMPTY;
- }
-
- final String directory = uri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
- final Long directoryId = directory == null ? null : Longs.tryParse(directory);
-
- Cursor phoneLookupCursor = null;
- try {
- String[] projection = PhoneQuery.getPhoneLookupProjection(uri);
- phoneLookupCursor = mContext.getContentResolver().query(uri, projection, null, null,
- null);
- } catch (NullPointerException e) {
- // Trap NPE from pre-N CP2
- return null;
- }
- if (phoneLookupCursor == null) {
- return null;
- }
-
- try {
- if (!phoneLookupCursor.moveToFirst()) {
- return ContactInfo.EMPTY;
- }
- String lookupKey = phoneLookupCursor.getString(PhoneQuery.LOOKUP_KEY);
- ContactInfo contactInfo = createPhoneLookupContactInfo(phoneLookupCursor, lookupKey);
- contactInfo.nameAlternative = lookUpDisplayNameAlternative(mContext, lookupKey,
- contactInfo.userType, directoryId);
- return contactInfo;
- } finally {
- phoneLookupCursor.close();
- }
- }
-
- private ContactInfo createPhoneLookupContactInfo(Cursor phoneLookupCursor, String lookupKey) {
- ContactInfo info = new ContactInfo();
- info.lookupKey = lookupKey;
- info.lookupUri = Contacts.getLookupUri(phoneLookupCursor.getLong(PhoneQuery.PERSON_ID),
- lookupKey);
- info.name = phoneLookupCursor.getString(PhoneQuery.NAME);
- info.type = phoneLookupCursor.getInt(PhoneQuery.PHONE_TYPE);
- info.label = phoneLookupCursor.getString(PhoneQuery.LABEL);
- info.number = phoneLookupCursor.getString(PhoneQuery.MATCHED_NUMBER);
- info.normalizedNumber = phoneLookupCursor.getString(PhoneQuery.NORMALIZED_NUMBER);
- info.photoId = phoneLookupCursor.getLong(PhoneQuery.PHOTO_ID);
- info.photoUri = UriUtils.parseUriOrNull(phoneLookupCursor.getString(PhoneQuery.PHOTO_URI));
- info.formattedNumber = null;
- info.userType = ContactsUtils.determineUserType(null,
- phoneLookupCursor.getLong(PhoneQuery.PERSON_ID));
-
- return info;
- }
-
- public static String lookUpDisplayNameAlternative(Context context, String lookupKey,
- @UserType long userType, @Nullable Long directoryId) {
- // Query {@link Contacts#CONTENT_LOOKUP_URI} directly with work lookup key is not allowed.
- if (lookupKey == null || userType == ContactsUtils.USER_TYPE_WORK) {
- return null;
- }
-
- if (directoryId != null) {
- // Query {@link Contacts#CONTENT_LOOKUP_URI} with work lookup key is not allowed.
- if (DirectoryCompat.isEnterpriseDirectoryId(directoryId)) {
- return null;
- }
-
- // Skip this to avoid an extra remote network call for alternative name
- if (DirectoryCompat.isRemoteDirectoryId(directoryId)) {
- return null;
- }
- }
-
- final Uri uri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
- Cursor cursor = null;
- try {
- cursor = context.getContentResolver().query(uri,
- PhoneQuery.DISPLAY_NAME_ALTERNATIVE_PROJECTION, null, null, null);
-
- if (cursor != null && cursor.moveToFirst()) {
- return cursor.getString(PhoneQuery.NAME_ALTERNATIVE);
- }
- } catch (IllegalArgumentException e) {
- // Avoid dialer crash when lookup key is not valid
- Log.e(TAG, "IllegalArgumentException in lookUpDisplayNameAlternative", e);
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- return null;
- }
-
- /**
- * Determines the contact information for the given phone number.
- * <p>
- * It returns the contact info if found.
- * <p>
- * If no contact corresponds to the given phone number, returns {@link ContactInfo#EMPTY}.
- * <p>
- * If the lookup fails for some other reason, it returns null.
- */
- private ContactInfo queryContactInfoForPhoneNumber(String number, String countryIso,
- boolean isSip) {
- if (TextUtils.isEmpty(number)) {
- return null;
- }
-
- ContactInfo info = lookupContactFromUri(getContactInfoLookupUri(number), isSip);
- if (info != null && info != ContactInfo.EMPTY) {
- info.formattedNumber = formatPhoneNumber(number, null, countryIso);
- } else if (mCachedNumberLookupService != null) {
- CachedContactInfo cacheInfo =
- mCachedNumberLookupService.lookupCachedContactFromNumber(mContext, number);
- if (cacheInfo != null) {
- info = cacheInfo.getContactInfo().isBadData ? null : cacheInfo.getContactInfo();
- } else {
- info = null;
- }
- }
- return info;
- }
-
- /**
- * Format the given phone number
- *
- * @param number the number to be formatted.
- * @param normalizedNumber the normalized number of the given number.
- * @param countryIso the ISO 3166-1 two letters country code, the country's convention will be
- * used to format the number if the normalized phone is null.
- *
- * @return the formatted number, or the given number if it was formatted.
- */
- private String formatPhoneNumber(String number, String normalizedNumber, String countryIso) {
- if (TextUtils.isEmpty(number)) {
- return "";
- }
- // If "number" is really a SIP address, don't try to do any formatting at all.
- if (PhoneNumberHelper.isUriNumber(number)) {
- return number;
- }
- if (TextUtils.isEmpty(countryIso)) {
- countryIso = mCurrentCountryIso;
- }
- return PhoneNumberUtils.formatNumber(number, normalizedNumber, countryIso);
- }
-
- /**
- * Stores differences between the updated contact info and the current call log contact info.
- *
- * @param number The number of the contact.
- * @param countryIso The country associated with this number.
- * @param updatedInfo The updated contact info.
- * @param callLogInfo The call log entry's current contact info.
- */
- public void updateCallLogContactInfo(String number, String countryIso, ContactInfo updatedInfo,
- ContactInfo callLogInfo) {
- if (!PermissionsUtil.hasPermission(mContext, android.Manifest.permission.WRITE_CALL_LOG)) {
- return;
- }
-
- final ContentValues values = new ContentValues();
- boolean needsUpdate = false;
-
- if (callLogInfo != null) {
- if (!TextUtils.equals(updatedInfo.name, callLogInfo.name)) {
- values.put(Calls.CACHED_NAME, updatedInfo.name);
- needsUpdate = true;
- }
-
- if (updatedInfo.type != callLogInfo.type) {
- values.put(Calls.CACHED_NUMBER_TYPE, updatedInfo.type);
- needsUpdate = true;
- }
-
- if (!TextUtils.equals(updatedInfo.label, callLogInfo.label)) {
- values.put(Calls.CACHED_NUMBER_LABEL, updatedInfo.label);
- needsUpdate = true;
- }
-
- if (!UriUtils.areEqual(updatedInfo.lookupUri, callLogInfo.lookupUri)) {
- values.put(Calls.CACHED_LOOKUP_URI, UriUtils.uriToString(updatedInfo.lookupUri));
- needsUpdate = true;
- }
-
- // Only replace the normalized number if the new updated normalized number isn't empty.
- if (!TextUtils.isEmpty(updatedInfo.normalizedNumber) &&
- !TextUtils.equals(updatedInfo.normalizedNumber, callLogInfo.normalizedNumber)) {
- values.put(Calls.CACHED_NORMALIZED_NUMBER, updatedInfo.normalizedNumber);
- needsUpdate = true;
- }
-
- if (!TextUtils.equals(updatedInfo.number, callLogInfo.number)) {
- values.put(Calls.CACHED_MATCHED_NUMBER, updatedInfo.number);
- needsUpdate = true;
- }
-
- if (updatedInfo.photoId != callLogInfo.photoId) {
- values.put(Calls.CACHED_PHOTO_ID, updatedInfo.photoId);
- needsUpdate = true;
- }
-
- final Uri updatedPhotoUriContactsOnly =
- UriUtils.nullForNonContactsUri(updatedInfo.photoUri);
- if (DialerCompatUtils.isCallsCachedPhotoUriCompatible() &&
- !UriUtils.areEqual(updatedPhotoUriContactsOnly, callLogInfo.photoUri)) {
- values.put(Calls.CACHED_PHOTO_URI,
- UriUtils.uriToString(updatedPhotoUriContactsOnly));
- needsUpdate = true;
- }
-
- if (!TextUtils.equals(updatedInfo.formattedNumber, callLogInfo.formattedNumber)) {
- values.put(Calls.CACHED_FORMATTED_NUMBER, updatedInfo.formattedNumber);
- needsUpdate = true;
- }
- } else {
- // No previous values, store all of them.
- values.put(Calls.CACHED_NAME, updatedInfo.name);
- values.put(Calls.CACHED_NUMBER_TYPE, updatedInfo.type);
- values.put(Calls.CACHED_NUMBER_LABEL, updatedInfo.label);
- values.put(Calls.CACHED_LOOKUP_URI, UriUtils.uriToString(updatedInfo.lookupUri));
- values.put(Calls.CACHED_MATCHED_NUMBER, updatedInfo.number);
- values.put(Calls.CACHED_NORMALIZED_NUMBER, updatedInfo.normalizedNumber);
- values.put(Calls.CACHED_PHOTO_ID, updatedInfo.photoId);
- if (DialerCompatUtils.isCallsCachedPhotoUriCompatible()) {
- values.put(Calls.CACHED_PHOTO_URI, UriUtils.uriToString(
- UriUtils.nullForNonContactsUri(updatedInfo.photoUri)));
- }
- values.put(Calls.CACHED_FORMATTED_NUMBER, updatedInfo.formattedNumber);
- needsUpdate = true;
- }
-
- if (!needsUpdate) {
- return;
- }
-
- try {
- if (countryIso == null) {
- mContext.getContentResolver().update(
- TelecomUtil.getCallLogUri(mContext),
- values,
- Calls.NUMBER + " = ? AND " + Calls.COUNTRY_ISO + " IS NULL",
- new String[]{ number });
- } else {
- mContext.getContentResolver().update(
- TelecomUtil.getCallLogUri(mContext),
- values,
- Calls.NUMBER + " = ? AND " + Calls.COUNTRY_ISO + " = ?",
- new String[]{ number, countryIso });
- }
- } catch (SQLiteFullException e) {
- Log.e(TAG, "Unable to update contact info in call log db", e);
- }
- }
-
- public static Uri getContactInfoLookupUri(String number) {
- return getContactInfoLookupUri(number, -1);
- }
-
- public static Uri getContactInfoLookupUri(String number, long directoryId) {
- // Get URI for the number in the PhoneLookup table, with a parameter to indicate whether
- // the number is a SIP number.
- Uri uri = PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI;
- if (!ContactsUtils.FLAG_N_FEATURE) {
- if (directoryId != -1) {
- // ENTERPRISE_CONTENT_FILTER_URI in M doesn't support directory lookup
- uri = PhoneLookup.CONTENT_FILTER_URI;
- } else {
- // b/25900607 in M. PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, encodes twice.
- number = Uri.encode(number);
- }
- }
- Uri.Builder builder = uri.buildUpon()
- .appendPath(number)
- .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS,
- String.valueOf(PhoneNumberHelper.isUriNumber(number)));
- if (directoryId != -1) {
- builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
- String.valueOf(directoryId));
- }
- return builder.build();
- }
-
- /**
- * Returns the contact information stored in an entry of the call log.
- *
- * @param c A cursor pointing to an entry in the call log.
- */
- public static ContactInfo getContactInfo(Cursor c) {
- ContactInfo info = new ContactInfo();
- info.lookupUri = UriUtils.parseUriOrNull(c.getString(CallLogQuery.CACHED_LOOKUP_URI));
- info.name = c.getString(CallLogQuery.CACHED_NAME);
- info.type = c.getInt(CallLogQuery.CACHED_NUMBER_TYPE);
- info.label = c.getString(CallLogQuery.CACHED_NUMBER_LABEL);
- String matchedNumber = c.getString(CallLogQuery.CACHED_MATCHED_NUMBER);
- String postDialDigits = CompatUtils.isNCompatible()
- ? c.getString(CallLogQuery.POST_DIAL_DIGITS) : "";
- info.number = (matchedNumber == null) ?
- c.getString(CallLogQuery.NUMBER) + postDialDigits : matchedNumber;
-
- info.normalizedNumber = c.getString(CallLogQuery.CACHED_NORMALIZED_NUMBER);
- info.photoId = c.getLong(CallLogQuery.CACHED_PHOTO_ID);
- info.photoUri = DialerCompatUtils.isCallsCachedPhotoUriCompatible() ?
- UriUtils.nullForNonContactsUri(
- UriUtils.parseUriOrNull(c.getString(CallLogQuery.CACHED_PHOTO_URI)))
- : null;
- info.formattedNumber = c.getString(CallLogQuery.CACHED_FORMATTED_NUMBER);
-
- return info;
- }
-
- /**
- * Given a contact's sourceType, return true if the contact is a business
- *
- * @param sourceType sourceType of the contact. This is usually populated by
- * {@link #mCachedNumberLookupService}.
- */
- public boolean isBusiness(int sourceType) {
- return mCachedNumberLookupService != null
- && mCachedNumberLookupService.isBusiness(sourceType);
- }
-
- /**
- * This function looks at a contact's source and determines if the user can
- * mark caller ids from this source as invalid.
- *
- * @param sourceType The source type to be checked
- * @param objectId The ID of the Contact object.
- * @return true if contacts from this source can be marked with an invalid caller id
- */
- public boolean canReportAsInvalid(int sourceType, String objectId) {
- return mCachedNumberLookupService != null
- && mCachedNumberLookupService.canReportAsInvalid(sourceType, objectId);
- }
-}
diff --git a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java b/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
deleted file mode 100644
index de6fc6a3d..000000000
--- a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import com.google.common.collect.Maps;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.support.annotation.Nullable;
-import android.support.v4.util.Pair;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.compat.TelephonyManagerCompat;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.R;
-import com.android.dialer.calllog.CallLogNotificationsHelper.NewCall;
-import com.android.dialer.filterednumber.FilteredNumbersUtil;
-import com.android.dialer.list.ListsFragment;
-import com.android.dialer.util.TelecomUtil;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Shows a voicemail notification in the status bar.
- */
-public class DefaultVoicemailNotifier {
- public static final String TAG = "VoicemailNotifier";
-
- /** The tag used to identify notifications from this class. */
- private static final String NOTIFICATION_TAG = "DefaultVoicemailNotifier";
- /** The identifier of the notification of new voicemails. */
- private static final int NOTIFICATION_ID = 1;
-
- /** The singleton instance of {@link DefaultVoicemailNotifier}. */
- private static DefaultVoicemailNotifier sInstance;
-
- private final Context mContext;
-
- /** Returns the singleton instance of the {@link DefaultVoicemailNotifier}. */
- public static DefaultVoicemailNotifier getInstance(Context context) {
- if (sInstance == null) {
- ContentResolver contentResolver = context.getContentResolver();
- sInstance = new DefaultVoicemailNotifier(context);
- }
- return sInstance;
- }
-
- private DefaultVoicemailNotifier(Context context) {
- mContext = context;
- }
-
- /**
- * Updates the notification and notifies of the call with the given URI.
- *
- * Clears the notification if there are no new voicemails, and notifies if the given URI
- * corresponds to a new voicemail.
- *
- * It is not safe to call this method from the main thread.
- */
- public void updateNotification(Uri newCallUri) {
- // Lookup the list of new voicemails to include in the notification.
- // TODO: Move this into a service, to avoid holding the receiver up.
- final List<NewCall> newCalls =
- CallLogNotificationsHelper.getInstance(mContext).getNewVoicemails();
-
- if (newCalls == null) {
- // Query failed, just return.
- return;
- }
-
- if (newCalls.isEmpty()) {
- // No voicemails to notify about: clear the notification.
- getNotificationManager().cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
- return;
- }
-
- Resources resources = mContext.getResources();
-
- // This represents a list of names to include in the notification.
- String callers = null;
-
- // Maps each number into a name: if a number is in the map, it has already left a more
- // recent voicemail.
- final Map<String, String> names = Maps.newHashMap();
-
- // Determine the call corresponding to the new voicemail we have to notify about.
- NewCall callToNotify = null;
-
- // Iterate over the new voicemails to determine all the information above.
- Iterator<NewCall> itr = newCalls.iterator();
- while (itr.hasNext()) {
- NewCall newCall = itr.next();
-
- // Skip notifying for numbers which are blocked.
- if (FilteredNumbersUtil.shouldBlockVoicemail(
- mContext, newCall.number, newCall.countryIso, newCall.dateMs)) {
- itr.remove();
-
- // Delete the voicemail.
- mContext.getContentResolver().delete(newCall.voicemailUri, null, null);
- continue;
- }
-
- // Check if we already know the name associated with this number.
- String name = names.get(newCall.number);
- if (name == null) {
- name = CallLogNotificationsHelper.getInstance(mContext).getName(newCall.number,
- newCall.numberPresentation, newCall.countryIso);
- names.put(newCall.number, name);
- // This is a new caller. Add it to the back of the list of callers.
- if (TextUtils.isEmpty(callers)) {
- callers = name;
- } else {
- callers = resources.getString(
- R.string.notification_voicemail_callers_list, callers, name);
- }
- }
- // Check if this is the new call we need to notify about.
- if (newCallUri != null && newCall.voicemailUri != null &&
- ContentUris.parseId(newCallUri) == ContentUris.parseId(newCall.voicemailUri)) {
- callToNotify = newCall;
- }
- }
-
- // All the potential new voicemails have been removed, e.g. if they were spam.
- if (newCalls.isEmpty()) {
- return;
- }
-
- // If there is only one voicemail, set its transcription as the "long text".
- String transcription = null;
- if (newCalls.size() == 1) {
- transcription = newCalls.get(0).transcription;
- }
-
- if (newCallUri != null && callToNotify == null) {
- Log.e(TAG, "The new call could not be found in the call log: " + newCallUri);
- }
-
- // Determine the title of the notification and the icon for it.
- final String title = resources.getQuantityString(
- R.plurals.notification_voicemail_title, newCalls.size(), newCalls.size());
- // TODO: Use the photo of contact if all calls are from the same person.
- final int icon = android.R.drawable.stat_notify_voicemail;
-
- Pair<Uri, Integer> info = getNotificationInfo(callToNotify);
-
- Notification.Builder notificationBuilder = new Notification.Builder(mContext)
- .setSmallIcon(icon)
- .setContentTitle(title)
- .setContentText(callers)
- .setStyle(new Notification.BigTextStyle().bigText(transcription))
- .setColor(resources.getColor(R.color.dialer_theme_color))
- .setSound(info.first)
- .setDefaults(info.second)
- .setDeleteIntent(createMarkNewVoicemailsAsOldIntent())
- .setAutoCancel(true);
-
- // Determine the intent to fire when the notification is clicked on.
- final Intent contentIntent;
- // Open the call log.
- contentIntent = new Intent(mContext, DialtactsActivity.class);
- contentIntent.putExtra(DialtactsActivity.EXTRA_SHOW_TAB, ListsFragment.TAB_INDEX_VOICEMAIL);
- notificationBuilder.setContentIntent(PendingIntent.getActivity(
- mContext, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT));
-
- // The text to show in the ticker, describing the new event.
- if (callToNotify != null) {
- CharSequence msg = ContactDisplayUtils.getTtsSpannedPhoneNumber(
- resources,
- R.string.notification_new_voicemail_ticker,
- names.get(callToNotify.number));
- notificationBuilder.setTicker(msg);
- }
- Log.i(TAG, "Creating voicemail notification");
- getNotificationManager().notify(NOTIFICATION_TAG, NOTIFICATION_ID,
- notificationBuilder.build());
- }
-
- /**
- * Determines which ringtone Uri and Notification defaults to use when updating the notification
- * for the given call.
- */
- private Pair<Uri, Integer> getNotificationInfo(@Nullable NewCall callToNotify) {
- Log.v(TAG, "getNotificationInfo");
- if (callToNotify == null) {
- Log.i(TAG, "callToNotify == null");
- return new Pair<>(null, 0);
- }
- PhoneAccountHandle accountHandle = null;
- if (callToNotify.accountComponentName == null || callToNotify.accountId == null) {
- Log.v(TAG, "accountComponentName == null || callToNotify.accountId == null");
- accountHandle = TelecomUtil
- .getDefaultOutgoingPhoneAccount(mContext, PhoneAccount.SCHEME_TEL);
- if (accountHandle == null) {
- Log.i(TAG, "No default phone account found, using default notification ringtone");
- return new Pair<>(null, Notification.DEFAULT_ALL);
- }
-
- } else {
- accountHandle = new PhoneAccountHandle(
- ComponentName.unflattenFromString(callToNotify.accountComponentName),
- callToNotify.accountId);
- }
- if (accountHandle.getComponentName() != null) {
- Log.v(TAG, "PhoneAccountHandle.ComponentInfo:" + accountHandle.getComponentName());
- } else {
- Log.i(TAG, "PhoneAccountHandle.ComponentInfo: null");
- }
- return new Pair<>(
- TelephonyManagerCompat.getVoicemailRingtoneUri(
- getTelephonyManager(), accountHandle),
- getNotificationDefaults(accountHandle));
- }
-
- private int getNotificationDefaults(PhoneAccountHandle accountHandle) {
- if (ContactsUtils.FLAG_N_FEATURE) {
- return TelephonyManagerCompat.isVoicemailVibrationEnabled(getTelephonyManager(),
- accountHandle) ? Notification.DEFAULT_VIBRATE : 0;
- }
- return Notification.DEFAULT_ALL;
- }
-
- /** Creates a pending intent that marks all new voicemails as old. */
- private PendingIntent createMarkNewVoicemailsAsOldIntent() {
- Intent intent = new Intent(mContext, CallLogNotificationsService.class);
- intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_VOICEMAILS_AS_OLD);
- return PendingIntent.getService(mContext, 0, intent, 0);
- }
-
- private NotificationManager getNotificationManager() {
- return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- }
-
- private TelephonyManager getTelephonyManager() {
- return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
- }
-
-}
diff --git a/src/com/android/dialer/calllog/GroupingListAdapter.java b/src/com/android/dialer/calllog/GroupingListAdapter.java
deleted file mode 100644
index 0d06298e7..000000000
--- a/src/com/android/dialer/calllog/GroupingListAdapter.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.DataSetObserver;
-import android.os.Handler;
-import android.support.v7.widget.RecyclerView;
-import android.util.SparseIntArray;
-
-/**
- * Maintains a list that groups items into groups of consecutive elements which are disjoint,
- * that is, an item can only belong to one group. This is leveraged for grouping calls in the
- * call log received from or made to the same phone number.
- *
- * There are two integers stored as metadata for every list item in the adapter.
- */
-abstract class GroupingListAdapter extends RecyclerView.Adapter {
-
- private Context mContext;
- private Cursor mCursor;
-
- /**
- * SparseIntArray, which maps the cursor position of the first element of a group to the size
- * of the group. The index of a key in this map corresponds to the list position of that group.
- */
- private SparseIntArray mGroupMetadata;
- private int mItemCount;
-
- protected ContentObserver mChangeObserver = new ContentObserver(new Handler()) {
- @Override
- public boolean deliverSelfNotifications() {
- return true;
- }
-
- @Override
- public void onChange(boolean selfChange) {
- onContentChanged();
- }
- };
-
- protected DataSetObserver mDataSetObserver = new DataSetObserver() {
- @Override
- public void onChanged() {
- notifyDataSetChanged();
- }
- };
-
- public GroupingListAdapter(Context context) {
- mContext = context;
- reset();
- }
-
- /**
- * Finds all groups of adjacent items in the cursor and calls {@link #addGroup} for
- * each of them.
- */
- protected abstract void addGroups(Cursor cursor);
-
- protected abstract void addVoicemailGroups(Cursor cursor);
-
- protected abstract void onContentChanged();
-
- public void changeCursor(Cursor cursor) {
- changeCursor(cursor, false);
- }
-
- public void changeCursorVoicemail(Cursor cursor) {
- changeCursor(cursor, true);
- }
-
- public void changeCursor(Cursor cursor, boolean voicemail) {
- if (cursor == mCursor) {
- return;
- }
-
- if (mCursor != null) {
- mCursor.unregisterContentObserver(mChangeObserver);
- mCursor.unregisterDataSetObserver(mDataSetObserver);
- mCursor.close();
- }
-
- // Reset whenever the cursor is changed.
- reset();
- mCursor = cursor;
-
- if (cursor != null) {
- if (voicemail) {
- addVoicemailGroups(mCursor);
- } else {
- addGroups(mCursor);
- }
-
- // Calculate the item count by subtracting group child counts from the cursor count.
- mItemCount = mGroupMetadata.size();
-
- cursor.registerContentObserver(mChangeObserver);
- cursor.registerDataSetObserver(mDataSetObserver);
- notifyDataSetChanged();
- }
- }
-
- /**
- * Records information about grouping in the list.
- * Should be called by the overridden {@link #addGroups} method.
- */
- public void addGroup(int cursorPosition, int groupSize) {
- int lastIndex = mGroupMetadata.size() - 1;
- if (lastIndex < 0 || cursorPosition <= mGroupMetadata.keyAt(lastIndex)) {
- mGroupMetadata.put(cursorPosition, groupSize);
- } else {
- // Optimization to avoid binary search if adding groups in ascending cursor position.
- mGroupMetadata.append(cursorPosition, groupSize);
- }
- }
-
- @Override
- public int getItemCount() {
- return mItemCount;
- }
-
- /**
- * Given the position of a list item, returns the size of the group of items corresponding to
- * that position.
- */
- public int getGroupSize(int listPosition) {
- if (listPosition < 0 || listPosition >= mGroupMetadata.size()) {
- return 0;
- }
-
- return mGroupMetadata.valueAt(listPosition);
- }
-
- /**
- * Given the position of a list item, returns the the first item in the group of items
- * corresponding to that position.
- */
- public Object getItem(int listPosition) {
- if (mCursor == null || listPosition < 0 || listPosition >= mGroupMetadata.size()) {
- return null;
- }
-
- int cursorPosition = mGroupMetadata.keyAt(listPosition);
- if (mCursor.moveToPosition(cursorPosition)) {
- return mCursor;
- } else {
- return null;
- }
- }
-
- private void reset() {
- mItemCount = 0;
- mGroupMetadata = new SparseIntArray();
- }
-}
diff --git a/src/com/android/dialer/calllog/IntentProvider.java b/src/com/android/dialer/calllog/IntentProvider.java
deleted file mode 100644
index 773436be4..000000000
--- a/src/com/android/dialer/calllog/IntentProvider.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.ContentValues;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.telecom.PhoneAccountHandle;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.model.Contact;
-import com.android.contacts.common.model.ContactLoader;
-import com.android.dialer.CallDetailActivity;
-import com.android.dialer.util.IntentUtil;
-import com.android.dialer.util.IntentUtil.CallIntentBuilder;
-import com.android.dialer.util.TelecomUtil;
-import com.android.incallui.Call.LogState;
-
-import java.util.ArrayList;
-
-/**
- * Used to create an intent to attach to an action in the call log.
- * <p>
- * The intent is constructed lazily with the given information.
- */
-public abstract class IntentProvider {
-
- private static final String TAG = IntentProvider.class.getSimpleName();
-
- public abstract Intent getIntent(Context context);
-
- public static IntentProvider getReturnCallIntentProvider(final String number) {
- return getReturnCallIntentProvider(number, null);
- }
-
- public static IntentProvider getReturnCallIntentProvider(final String number,
- final PhoneAccountHandle accountHandle) {
- return new IntentProvider() {
- @Override
- public Intent getIntent(Context context) {
- return new CallIntentBuilder(number)
- .setPhoneAccountHandle(accountHandle)
- .setCallInitiationType(LogState.INITIATION_CALL_LOG)
- .build();
- }
- };
- }
-
- public static IntentProvider getReturnVideoCallIntentProvider(final String number) {
- return getReturnVideoCallIntentProvider(number, null);
- }
-
- public static IntentProvider getReturnVideoCallIntentProvider(final String number,
- final PhoneAccountHandle accountHandle) {
- return new IntentProvider() {
- @Override
- public Intent getIntent(Context context) {
- return new CallIntentBuilder(number)
- .setPhoneAccountHandle(accountHandle)
- .setCallInitiationType(LogState.INITIATION_CALL_LOG)
- .setIsVideoCall(true)
- .build();
- }
- };
- }
-
- public static IntentProvider getReturnVoicemailCallIntentProvider() {
- return new IntentProvider() {
- @Override
- public Intent getIntent(Context context) {
- return new CallIntentBuilder(CallUtil.getVoicemailUri())
- .setCallInitiationType(LogState.INITIATION_CALL_LOG)
- .build();
- }
- };
- }
-
- public static IntentProvider getSendSmsIntentProvider(final String number) {
- return new IntentProvider() {
- @Override
- public Intent getIntent(Context context) {
- return IntentUtil.getSendSmsIntent(number);
- }
- };
- }
-
- /**
- * Retrieves the call details intent provider for an entry in the call log.
- *
- * @param id The call ID of the first call in the call group.
- * @param extraIds The call ID of the other calls grouped together with the call.
- * @param voicemailUri If call log entry is for a voicemail, the voicemail URI.
- * @return The call details intent provider.
- */
- public static IntentProvider getCallDetailIntentProvider(
- final long id, final long[] extraIds, final String voicemailUri) {
- return new IntentProvider() {
- @Override
- public Intent getIntent(Context context) {
- Intent intent = new Intent(context, CallDetailActivity.class);
- // Check if the first item is a voicemail.
- if (voicemailUri != null) {
- intent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_URI,
- Uri.parse(voicemailUri));
- }
-
- if (extraIds != null && extraIds.length > 0) {
- intent.putExtra(CallDetailActivity.EXTRA_CALL_LOG_IDS, extraIds);
- } else {
- // If there is a single item, use the direct URI for it.
- intent.setData(ContentUris.withAppendedId(TelecomUtil.getCallLogUri(context),
- id));
- }
- return intent;
- }
- };
- }
-
- /**
- * Retrieves an add contact intent for the given contact and phone call details.
- */
- public static IntentProvider getAddContactIntentProvider(
- final Uri lookupUri,
- final CharSequence name,
- final CharSequence number,
- final int numberType,
- final boolean isNewContact) {
- return new IntentProvider() {
- @Override
- public Intent getIntent(Context context) {
- Contact contactToSave = null;
-
- if (lookupUri != null) {
- contactToSave = ContactLoader.parseEncodedContactEntity(lookupUri);
- }
-
- if (contactToSave != null) {
- // Populate the intent with contact information stored in the lookup URI.
- // Note: This code mirrors code in Contacts/QuickContactsActivity.
- final Intent intent;
- if (isNewContact) {
- intent = IntentUtil.getNewContactIntent();
- } else {
- intent = IntentUtil.getAddToExistingContactIntent();
- }
-
- ArrayList<ContentValues> values = contactToSave.getContentValues();
- // Only pre-fill the name field if the provided display name is an nickname
- // or better (e.g. structured name, nickname)
- if (contactToSave.getDisplayNameSource()
- >= ContactsContract.DisplayNameSources.NICKNAME) {
- intent.putExtra(ContactsContract.Intents.Insert.NAME,
- contactToSave.getDisplayName());
- } else if (contactToSave.getDisplayNameSource()
- == ContactsContract.DisplayNameSources.ORGANIZATION) {
- // This is probably an organization. Instead of copying the organization
- // name into a name entry, copy it into the organization entry. This
- // way we will still consider the contact an organization.
- final ContentValues organization = new ContentValues();
- organization.put(ContactsContract.CommonDataKinds.Organization.COMPANY,
- contactToSave.getDisplayName());
- organization.put(ContactsContract.Data.MIMETYPE,
- ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
- values.add(organization);
- }
-
- // Last time used and times used are aggregated values from the usage stat
- // table. They need to be removed from data values so the SQL table can insert
- // properly
- for (ContentValues value : values) {
- value.remove(ContactsContract.Data.LAST_TIME_USED);
- value.remove(ContactsContract.Data.TIMES_USED);
- }
-
- intent.putExtra(ContactsContract.Intents.Insert.DATA, values);
-
- return intent;
- } else {
- // If no lookup uri is provided, rely on the available phone number and name.
- if (isNewContact) {
- return IntentUtil.getNewContactIntent(name, number, numberType);
- } else {
- return IntentUtil.getAddToExistingContactIntent(name, number, numberType);
- }
- }
- }
- };
- }
-}
diff --git a/src/com/android/dialer/calllog/MissedCallNotificationReceiver.java b/src/com/android/dialer/calllog/MissedCallNotificationReceiver.java
deleted file mode 100644
index 86d6cb9fb..000000000
--- a/src/com/android/dialer/calllog/MissedCallNotificationReceiver.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.telecom.TelecomManager;
-import android.util.Log;
-
-import com.android.dialer.calllog.CallLogNotificationsService;
-
-/**
- * Receives broadcasts that should trigger a refresh of the missed call notification. This includes
- * both an explicit broadcast from Telecom and a reboot.
- */
-public class MissedCallNotificationReceiver extends BroadcastReceiver {
- //TODO: Use compat class for these methods.
- public static final String ACTION_SHOW_MISSED_CALLS_NOTIFICATION =
- "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
-
- public static final String EXTRA_NOTIFICATION_COUNT =
- "android.telecom.extra.NOTIFICATION_COUNT";
-
- public static final String EXTRA_NOTIFICATION_PHONE_NUMBER =
- "android.telecom.extra.NOTIFICATION_PHONE_NUMBER";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (!ACTION_SHOW_MISSED_CALLS_NOTIFICATION.equals(action)) {
- return;
- }
-
- int count = intent.getIntExtra(EXTRA_NOTIFICATION_COUNT,
- CallLogNotificationsService.UNKNOWN_MISSED_CALL_COUNT);
- String number = intent.getStringExtra(EXTRA_NOTIFICATION_PHONE_NUMBER);
- CallLogNotificationsService.updateMissedCallNotifications(context, count, number);
- }
-}
diff --git a/src/com/android/dialer/calllog/MissedCallNotifier.java b/src/com/android/dialer/calllog/MissedCallNotifier.java
deleted file mode 100644
index 47f9ea770..000000000
--- a/src/com/android/dialer/calllog/MissedCallNotifier.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.os.AsyncTask;
-import android.provider.CallLog.Calls;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.util.PhoneNumberHelper;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.R;
-import com.android.dialer.calllog.CallLogNotificationsHelper.NewCall;
-import com.android.dialer.contactinfo.ContactPhotoLoader;
-import com.android.dialer.compat.UserManagerCompat;
-import com.android.dialer.list.ListsFragment;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.IntentUtil;
-import com.android.dialer.util.IntentUtil.CallIntentBuilder;
-
-import java.util.List;
-
-/**
- * Creates a notification for calls that the user missed (neither answered nor rejected).
- *
- */
-public class MissedCallNotifier {
- public static final String TAG = "MissedCallNotifier";
-
- /** The tag used to identify notifications from this class. */
- private static final String NOTIFICATION_TAG = "MissedCallNotifier";
- /** The identifier of the notification of new missed calls. */
- private static final int NOTIFICATION_ID = 1;
- /** Preference file key for number of missed calls. */
- private static final String MISSED_CALL_COUNT = "missed_call_count";
-
- private static MissedCallNotifier sInstance;
- private Context mContext;
-
- /** Returns the singleton instance of the {@link MissedCallNotifier}. */
- public static MissedCallNotifier getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new MissedCallNotifier(context);
- }
- return sInstance;
- }
-
- private MissedCallNotifier(Context context) {
- mContext = context;
- }
-
- public void updateMissedCallNotification(int count, String number) {
- final int titleResId;
- final String expandedText; // The text in the notification's line 1 and 2.
-
- final List<NewCall> newCalls =
- CallLogNotificationsHelper.getInstance(mContext).getNewMissedCalls();
-
- if (count == CallLogNotificationsService.UNKNOWN_MISSED_CALL_COUNT) {
- if (newCalls == null) {
- // If the intent did not contain a count, and we are unable to get a count from the
- // call log, then no notification can be shown.
- return;
- }
- count = newCalls.size();
- }
-
- if (count == 0) {
- // No voicemails to notify about: clear the notification.
- clearMissedCalls();
- return;
- }
-
- // The call log has been updated, use that information preferentially.
- boolean useCallLog = newCalls != null && newCalls.size() == count;
- NewCall newestCall = useCallLog ? newCalls.get(0) : null;
- long timeMs = useCallLog ? newestCall.dateMs : System.currentTimeMillis();
- String missedNumber = useCallLog ? newestCall.number : number;
-
- Notification.Builder builder = new Notification.Builder(mContext);
- // Display the first line of the notification:
- // 1 missed call: <caller name || handle>
- // More than 1 missed call: <number of calls> + "missed calls"
- if (count == 1) {
- //TODO: look up caller ID that is not in contacts.
- ContactInfo contactInfo = CallLogNotificationsHelper.getInstance(mContext)
- .getContactInfo(missedNumber,
- useCallLog ? newestCall.numberPresentation
- : Calls.PRESENTATION_ALLOWED,
- useCallLog ? newestCall.countryIso : null);
-
- titleResId = contactInfo.userType == ContactsUtils.USER_TYPE_WORK
- ? R.string.notification_missedWorkCallTitle
- : R.string.notification_missedCallTitle;
-
- expandedText = contactInfo.name;
- ContactPhotoLoader loader = new ContactPhotoLoader(mContext, contactInfo);
- Bitmap photoIcon = loader.loadPhotoIcon();
- if (photoIcon != null) {
- builder.setLargeIcon(photoIcon);
- }
- } else {
- titleResId = R.string.notification_missedCallsTitle;
- expandedText =
- mContext.getString(R.string.notification_missedCallsMsg, count);
- }
-
- // Create a public viewable version of the notification, suitable for display when sensitive
- // notification content is hidden.
- Notification.Builder publicBuilder = new Notification.Builder(mContext);
- publicBuilder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
- .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
- // Show "Phone" for notification title.
- .setContentTitle(mContext.getText(R.string.userCallActivityLabel))
- // Notification details shows that there are missed call(s), but does not reveal
- // the missed caller information.
- .setContentText(mContext.getText(titleResId))
- .setContentIntent(createCallLogPendingIntent())
- .setAutoCancel(true)
- .setWhen(timeMs)
- .setDeleteIntent(createClearMissedCallsPendingIntent());
-
- // Create the notification suitable for display when sensitive information is showing.
- builder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
- .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
- .setContentTitle(mContext.getText(titleResId))
- .setContentText(expandedText)
- .setContentIntent(createCallLogPendingIntent())
- .setAutoCancel(true)
- .setWhen(timeMs)
- .setDefaults(Notification.DEFAULT_VIBRATE)
- .setDeleteIntent(createClearMissedCallsPendingIntent())
- // Include a public version of the notification to be shown when the missed call
- // notification is shown on the user's lock screen and they have chosen to hide
- // sensitive notification information.
- .setPublicVersion(publicBuilder.build());
-
- // Add additional actions when there is only 1 missed call and the user isn't locked
- if (UserManagerCompat.isUserUnlocked(mContext) && count == 1) {
- if (!TextUtils.isEmpty(missedNumber)
- && !TextUtils.equals(
- missedNumber, mContext.getString(R.string.handle_restricted))) {
- builder.addAction(R.drawable.ic_phone_24dp,
- mContext.getString(R.string.notification_missedCall_call_back),
- createCallBackPendingIntent(missedNumber));
-
- if (!PhoneNumberHelper.isUriNumber(missedNumber)) {
- builder.addAction(R.drawable.ic_message_24dp,
- mContext.getString(R.string.notification_missedCall_message),
- createSendSmsFromNotificationPendingIntent(missedNumber));
- }
- }
- }
-
- Notification notification = builder.build();
- configureLedOnNotification(notification);
-
- Log.i(TAG, "Adding missed call notification.");
- getNotificationMgr().notify(NOTIFICATION_TAG, NOTIFICATION_ID, notification);
- }
-
- private void clearMissedCalls() {
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- // Call log is only accessible when unlocked. If that's the case, clear the list of
- // new missed calls from the call log.
- if (UserManagerCompat.isUserUnlocked(mContext)) {
- ContentValues values = new ContentValues();
- values.put(Calls.NEW, 0);
- values.put(Calls.IS_READ, 1);
- StringBuilder where = new StringBuilder();
- where.append(Calls.NEW);
- where.append(" = 1 AND ");
- where.append(Calls.TYPE);
- where.append(" = ?");
- try {
- mContext.getContentResolver().update(Calls.CONTENT_URI, values,
- where.toString(), new String[]{Integer.toString(Calls.
- MISSED_TYPE)});
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "ContactsProvider update command failed", e);
- }
- }
- getNotificationMgr().cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
- }
- });
- }
-
- /**
- * Trigger an intent to make a call from a missed call number.
- */
- public void callBackFromMissedCall(String number) {
- closeSystemDialogs(mContext);
- CallLogNotificationsHelper.removeMissedCallNotifications(mContext);
- DialerUtils.startActivityWithErrorToast(
- mContext,
- new CallIntentBuilder(number)
- .build()
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- }
-
- /**
- * Trigger an intent to send an sms from a missed call number.
- */
- public void sendSmsFromMissedCall(String number) {
- closeSystemDialogs(mContext);
- CallLogNotificationsHelper.removeMissedCallNotifications(mContext);
- DialerUtils.startActivityWithErrorToast(
- mContext,
- IntentUtil.getSendSmsIntent(number).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- }
-
- /**
- * Creates a new pending intent that sends the user to the call log.
- *
- * @return The pending intent.
- */
- private PendingIntent createCallLogPendingIntent() {
- Intent contentIntent = new Intent(mContext, DialtactsActivity.class);
- contentIntent.putExtra(DialtactsActivity.EXTRA_SHOW_TAB, ListsFragment.TAB_INDEX_HISTORY);
- return PendingIntent.getActivity(
- mContext, 0, contentIntent,PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- /** Creates a pending intent that marks all new missed calls as old. */
- private PendingIntent createClearMissedCallsPendingIntent() {
- Intent intent = new Intent(mContext, CallLogNotificationsService.class);
- intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_MISSED_CALLS_AS_OLD);
- return PendingIntent.getService(mContext, 0, intent, 0);
- }
-
- private PendingIntent createCallBackPendingIntent(String number) {
- Intent intent = new Intent(mContext, CallLogNotificationsService.class);
- intent.setAction(
- CallLogNotificationsService.ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION);
- intent.putExtra(CallLogNotificationsService.EXTRA_MISSED_CALL_NUMBER, number);
- // Use FLAG_UPDATE_CURRENT to make sure any previous pending intent is updated with the new
- // extra.
- return PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- private PendingIntent createSendSmsFromNotificationPendingIntent(String number) {
- Intent intent = new Intent(mContext, CallLogNotificationsService.class);
- intent.setAction(
- CallLogNotificationsService.ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION);
- intent.putExtra(CallLogNotificationsService.EXTRA_MISSED_CALL_NUMBER, number);
- // Use FLAG_UPDATE_CURRENT to make sure any previous pending intent is updated with the new
- // extra.
- return PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- /**
- * Configures a notification to emit the blinky notification light.
- */
- private void configureLedOnNotification(Notification notification) {
- notification.flags |= Notification.FLAG_SHOW_LIGHTS;
- notification.defaults |= Notification.DEFAULT_LIGHTS;
- }
-
- /**
- * Closes open system dialogs and the notification shade.
- */
- private void closeSystemDialogs(Context context) {
- context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
- }
-
- private NotificationManager getNotificationMgr() {
- return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- }
-}
diff --git a/src/com/android/dialer/calllog/PhoneAccountUtils.java b/src/com/android/dialer/calllog/PhoneAccountUtils.java
deleted file mode 100644
index b3ce18b3c..000000000
--- a/src/com/android/dialer/calllog/PhoneAccountUtils.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.calllog;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.support.annotation.Nullable;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.text.TextUtils;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.dialer.util.TelecomUtil;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Methods to help extract {@code PhoneAccount} information from database and Telecomm sources.
- */
-public class PhoneAccountUtils {
- /**
- * Return a list of phone accounts that are subscription/SIM accounts.
- */
- public static List<PhoneAccountHandle> getSubscriptionPhoneAccounts(Context context) {
- List<PhoneAccountHandle> subscriptionAccountHandles = new ArrayList<PhoneAccountHandle>();
- final List<PhoneAccountHandle> accountHandles =
- TelecomUtil.getCallCapablePhoneAccounts(context);
- for (PhoneAccountHandle accountHandle : accountHandles) {
- PhoneAccount account = TelecomUtil.getPhoneAccount(context, accountHandle);
- if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
- subscriptionAccountHandles.add(accountHandle);
- }
- }
- return subscriptionAccountHandles;
- }
-
- /**
- * Compose PhoneAccount object from component name and account id.
- */
- @Nullable
- public static PhoneAccountHandle getAccount(@Nullable String componentString,
- @Nullable String accountId) {
- if (TextUtils.isEmpty(componentString) || TextUtils.isEmpty(accountId)) {
- return null;
- }
- final ComponentName componentName = ComponentName.unflattenFromString(componentString);
- if (componentName == null) {
- return null;
- }
- return new PhoneAccountHandle(componentName, accountId);
- }
-
- /**
- * Extract account label from PhoneAccount object.
- */
- @Nullable
- public static String getAccountLabel(Context context,
- @Nullable PhoneAccountHandle accountHandle) {
- PhoneAccount account = getAccountOrNull(context, accountHandle);
- if (account != null && account.getLabel() != null) {
- return account.getLabel().toString();
- }
- return null;
- }
-
- /**
- * Extract account color from PhoneAccount object.
- */
- public static int getAccountColor(Context context, @Nullable PhoneAccountHandle accountHandle) {
- final PhoneAccount account = TelecomUtil.getPhoneAccount(context, accountHandle);
-
- // For single-sim devices the PhoneAccount will be NO_HIGHLIGHT_COLOR by default, so it is
- // safe to always use the account highlight color.
- return account == null ? PhoneAccount.NO_HIGHLIGHT_COLOR : account.getHighlightColor();
- }
-
- /**
- * Determine whether a phone account supports call subjects.
- *
- * @return {@code true} if call subjects are supported, {@code false} otherwise.
- */
- public static boolean getAccountSupportsCallSubject(Context context,
- @Nullable PhoneAccountHandle accountHandle) {
- final PhoneAccount account = TelecomUtil.getPhoneAccount(context, accountHandle);
-
- return account == null ? false :
- account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
- }
-
- /**
- * Retrieve the account metadata, but if the account does not exist or the device has only a
- * single registered and enabled account, return null.
- */
- @Nullable
- private static PhoneAccount getAccountOrNull(Context context,
- @Nullable PhoneAccountHandle accountHandle) {
- if (TelecomUtil.getCallCapablePhoneAccounts(context).size() <= 1) {
- return null;
- }
- return TelecomUtil.getPhoneAccount(context, accountHandle);
- }
-}
diff --git a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
deleted file mode 100644
index 53121614c..000000000
--- a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.collect.Lists;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Typeface;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.support.v4.content.ContextCompat;
-import android.telecom.PhoneAccount;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.view.View;
-import android.widget.TextView;
-
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.contacts.common.util.PhoneNumberHelper;
-import com.android.dialer.PhoneCallDetails;
-import com.android.dialer.R;
-import com.android.dialer.calllog.calllogcache.CallLogCache;
-import com.android.dialer.util.DialerUtils;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Helper class to fill in the views in {@link PhoneCallDetailsViews}.
- */
-public class PhoneCallDetailsHelper {
-
- /** The maximum number of icons will be shown to represent the call types in a group. */
- private static final int MAX_CALL_TYPE_ICONS = 3;
-
- private final Context mContext;
- private final Resources mResources;
- /** The injected current time in milliseconds since the epoch. Used only by tests. */
- private Long mCurrentTimeMillisForTest;
-
- private CharSequence mPhoneTypeLabelForTest;
-
- private final CallLogCache mCallLogCache;
-
- /** Calendar used to construct dates */
- private final Calendar mCalendar;
-
- /**
- * List of items to be concatenated together for accessibility descriptions
- */
- private ArrayList<CharSequence> mDescriptionItems = Lists.newArrayList();
-
- /**
- * Creates a new instance of the helper.
- * <p>
- * Generally you should have a single instance of this helper in any context.
- *
- * @param resources used to look up strings
- */
- public PhoneCallDetailsHelper(
- Context context,
- Resources resources,
- CallLogCache callLogCache) {
- mContext = context;
- mResources = resources;
- mCallLogCache = callLogCache;
- mCalendar = Calendar.getInstance();
- }
-
- /** Fills the call details views with content. */
- public void setPhoneCallDetails(PhoneCallDetailsViews views, PhoneCallDetails details) {
- // Display up to a given number of icons.
- views.callTypeIcons.clear();
- int count = details.callTypes.length;
- boolean isVoicemail = false;
- for (int index = 0; index < count && index < MAX_CALL_TYPE_ICONS; ++index) {
- views.callTypeIcons.add(details.callTypes[index]);
- if (index == 0) {
- isVoicemail = details.callTypes[index] == Calls.VOICEMAIL_TYPE;
- }
- }
-
- // Show the video icon if the call had video enabled.
- views.callTypeIcons.setShowVideo(
- (details.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO);
- views.callTypeIcons.requestLayout();
- views.callTypeIcons.setVisibility(View.VISIBLE);
-
- // Show the total call count only if there are more than the maximum number of icons.
- final Integer callCount;
- if (count > MAX_CALL_TYPE_ICONS) {
- callCount = count;
- } else {
- callCount = null;
- }
-
- // Set the call count, location, date and if voicemail, set the duration.
- setDetailText(views, callCount, details);
-
- // Set the account label if it exists.
- String accountLabel = mCallLogCache.getAccountLabel(details.accountHandle);
- if (!TextUtils.isEmpty(details.viaNumber)) {
- if (!TextUtils.isEmpty(accountLabel)) {
- accountLabel = mResources.getString(R.string.call_log_via_number_phone_account,
- accountLabel, details.viaNumber);
- } else {
- accountLabel = mResources.getString(R.string.call_log_via_number,
- details.viaNumber);
- }
- }
- if (!TextUtils.isEmpty(accountLabel)) {
- views.callAccountLabel.setVisibility(View.VISIBLE);
- views.callAccountLabel.setText(accountLabel);
- int color = mCallLogCache.getAccountColor(details.accountHandle);
- if (color == PhoneAccount.NO_HIGHLIGHT_COLOR) {
- int defaultColor = R.color.dialtacts_secondary_text_color;
- views.callAccountLabel.setTextColor(mContext.getResources().getColor(defaultColor));
- } else {
- views.callAccountLabel.setTextColor(color);
- }
- } else {
- views.callAccountLabel.setVisibility(View.GONE);
- }
-
- final CharSequence nameText;
- final CharSequence displayNumber = details.displayNumber;
- if (TextUtils.isEmpty(details.getPreferredName())) {
- nameText = displayNumber;
- // We have a real phone number as "nameView" so make it always LTR
- views.nameView.setTextDirection(View.TEXT_DIRECTION_LTR);
- } else {
- nameText = details.getPreferredName();
- }
-
- views.nameView.setText(nameText);
-
- if (isVoicemail) {
- views.voicemailTranscriptionView.setText(TextUtils.isEmpty(details.transcription) ? null
- : details.transcription);
- }
-
- // Bold if not read
- Typeface typeface = details.isRead ? Typeface.SANS_SERIF : Typeface.DEFAULT_BOLD;
- views.nameView.setTypeface(typeface);
- views.voicemailTranscriptionView.setTypeface(typeface);
- views.callLocationAndDate.setTypeface(typeface);
- views.callLocationAndDate.setTextColor(ContextCompat.getColor(mContext, details.isRead ?
- R.color.call_log_detail_color : R.color.call_log_unread_text_color));
- }
-
- /**
- * Builds a string containing the call location and date. For voicemail logs only the call date
- * is returned because location information is displayed in the call action button
- *
- * @param details The call details.
- * @return The call location and date string.
- */
- private CharSequence getCallLocationAndDate(PhoneCallDetails details) {
- mDescriptionItems.clear();
-
- if (details.callTypes[0] != Calls.VOICEMAIL_TYPE) {
- // Get type of call (ie mobile, home, etc) if known, or the caller's location.
- CharSequence callTypeOrLocation = getCallTypeOrLocation(details);
-
- // Only add the call type or location if its not empty. It will be empty for unknown
- // callers.
- if (!TextUtils.isEmpty(callTypeOrLocation)) {
- mDescriptionItems.add(callTypeOrLocation);
- }
- }
-
- // The date of this call
- mDescriptionItems.add(getCallDate(details));
-
- // Create a comma separated list from the call type or location, and call date.
- return DialerUtils.join(mResources, mDescriptionItems);
- }
-
- /**
- * For a call, if there is an associated contact for the caller, return the known call type
- * (e.g. mobile, home, work). If there is no associated contact, attempt to use the caller's
- * location if known.
- *
- * @param details Call details to use.
- * @return Type of call (mobile/home) if known, or the location of the caller (if known).
- */
- public CharSequence getCallTypeOrLocation(PhoneCallDetails details) {
- CharSequence numberFormattedLabel = null;
- // Only show a label if the number is shown and it is not a SIP address.
- if (!TextUtils.isEmpty(details.number)
- && !PhoneNumberHelper.isUriNumber(details.number.toString())
- && !mCallLogCache.isVoicemailNumber(details.accountHandle, details.number)) {
-
- if (TextUtils.isEmpty(details.namePrimary) && !TextUtils.isEmpty(details.geocode)) {
- numberFormattedLabel = details.geocode;
- } else if (!(details.numberType == Phone.TYPE_CUSTOM
- && TextUtils.isEmpty(details.numberLabel))) {
- // Get type label only if it will not be "Custom" because of an empty number label.
- numberFormattedLabel = MoreObjects.firstNonNull(mPhoneTypeLabelForTest,
- Phone.getTypeLabel(mResources, details.numberType, details.numberLabel));
- }
- }
-
- if (!TextUtils.isEmpty(details.namePrimary) && TextUtils.isEmpty(numberFormattedLabel)) {
- numberFormattedLabel = details.displayNumber;
- }
- return numberFormattedLabel;
- }
-
- @NeededForTesting
- public void setPhoneTypeLabelForTest(CharSequence phoneTypeLabel) {
- this.mPhoneTypeLabelForTest = phoneTypeLabel;
- }
-
- /**
- * Get the call date/time of the call. For the call log this is relative to the current time.
- * e.g. 3 minutes ago. For voicemail, see {@link #getGranularDateTime(PhoneCallDetails)}
- *
- * @param details Call details to use.
- * @return String representing when the call occurred.
- */
- public CharSequence getCallDate(PhoneCallDetails details) {
- if (details.callTypes[0] == Calls.VOICEMAIL_TYPE) {
- return getGranularDateTime(details);
- }
-
- return DateUtils.getRelativeTimeSpanString(details.date, getCurrentTimeMillis(),
- DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE);
- }
-
- /**
- * Get the granular version of the call date/time of the call. The result is always in the form
- * 'DATE at TIME'. The date value changes based on when the call was created.
- *
- * If created today, DATE is 'Today'
- * If created this year, DATE is 'MMM dd'
- * Otherwise, DATE is 'MMM dd, yyyy'
- *
- * TIME is the localized time format, e.g. 'hh:mm a' or 'HH:mm'
- *
- * @param details Call details to use
- * @return String representing when the call occurred
- */
- public CharSequence getGranularDateTime(PhoneCallDetails details) {
- return mResources.getString(R.string.voicemailCallLogDateTimeFormat,
- getGranularDate(details.date),
- DateUtils.formatDateTime(mContext, details.date, DateUtils.FORMAT_SHOW_TIME));
- }
-
- /**
- * Get the granular version of the call date. See {@link #getGranularDateTime(PhoneCallDetails)}
- */
- private String getGranularDate(long date) {
- if (DateUtils.isToday(date)) {
- return mResources.getString(R.string.voicemailCallLogToday);
- }
- return DateUtils.formatDateTime(mContext, date, DateUtils.FORMAT_SHOW_DATE
- | DateUtils.FORMAT_ABBREV_MONTH
- | (shouldShowYear(date) ? DateUtils.FORMAT_SHOW_YEAR : DateUtils.FORMAT_NO_YEAR));
- }
-
- /**
- * Determines whether the year should be shown for the given date
- *
- * @return {@code true} if date is within the current year, {@code false} otherwise
- */
- private boolean shouldShowYear(long date) {
- mCalendar.setTimeInMillis(getCurrentTimeMillis());
- int currentYear = mCalendar.get(Calendar.YEAR);
- mCalendar.setTimeInMillis(date);
- return currentYear != mCalendar.get(Calendar.YEAR);
- }
-
- /** Sets the text of the header view for the details page of a phone call. */
- @NeededForTesting
- public void setCallDetailsHeader(TextView nameView, PhoneCallDetails details) {
- final CharSequence nameText;
- if (!TextUtils.isEmpty(details.namePrimary)) {
- nameText = details.namePrimary;
- } else if (!TextUtils.isEmpty(details.displayNumber)) {
- nameText = details.displayNumber;
- } else {
- nameText = mResources.getString(R.string.unknown);
- }
-
- nameView.setText(nameText);
- }
-
- @NeededForTesting
- public void setCurrentTimeForTest(long currentTimeMillis) {
- mCurrentTimeMillisForTest = currentTimeMillis;
- }
-
- /**
- * Returns the current time in milliseconds since the epoch.
- * <p>
- * It can be injected in tests using {@link #setCurrentTimeForTest(long)}.
- */
- private long getCurrentTimeMillis() {
- if (mCurrentTimeMillisForTest == null) {
- return System.currentTimeMillis();
- } else {
- return mCurrentTimeMillisForTest;
- }
- }
-
- /** Sets the call count, date, and if it is a voicemail, sets the duration. */
- private void setDetailText(PhoneCallDetailsViews views, Integer callCount,
- PhoneCallDetails details) {
- if (details.isSpam) {
- views.callLocationAndDate.setText(
- mContext.getString(R.string.spam_number_call_log_label));
- return;
- }
- // Combine the count (if present) and the date.
- CharSequence dateText = getCallLocationAndDate(details);
- final CharSequence text;
- if (callCount != null) {
- text = mResources.getString(
- R.string.call_log_item_count_and_date, callCount.intValue(), dateText);
- } else {
- text = dateText;
- }
-
- if (details.callTypes[0] == Calls.VOICEMAIL_TYPE && details.duration > 0) {
- views.callLocationAndDate.setText(mResources.getString(
- R.string.voicemailCallLogDateTimeFormatWithDuration, text,
- getVoicemailDuration(details)));
- } else {
- views.callLocationAndDate.setText(text);
- }
- }
-
- private String getVoicemailDuration(PhoneCallDetails details) {
- long minutes = TimeUnit.SECONDS.toMinutes(details.duration);
- long seconds = details.duration - TimeUnit.MINUTES.toSeconds(minutes);
- if (minutes > 99) {
- minutes = 99;
- }
- return mResources.getString(R.string.voicemailDurationFormat, minutes, seconds);
- }
-}
diff --git a/src/com/android/dialer/calllog/PhoneCallDetailsViews.java b/src/com/android/dialer/calllog/PhoneCallDetailsViews.java
deleted file mode 100644
index 94f4411b0..000000000
--- a/src/com/android/dialer/calllog/PhoneCallDetailsViews.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.Context;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.dialer.R;
-
-/**
- * Encapsulates the views that are used to display the details of a phone call in the call log.
- */
-public final class PhoneCallDetailsViews {
- public final TextView nameView;
- public final View callTypeView;
- public final CallTypeIconsView callTypeIcons;
- public final TextView callLocationAndDate;
- public final TextView voicemailTranscriptionView;
- public final TextView callAccountLabel;
-
- private PhoneCallDetailsViews(TextView nameView, View callTypeView,
- CallTypeIconsView callTypeIcons, TextView callLocationAndDate,
- TextView voicemailTranscriptionView, TextView callAccountLabel) {
- this.nameView = nameView;
- this.callTypeView = callTypeView;
- this.callTypeIcons = callTypeIcons;
- this.callLocationAndDate = callLocationAndDate;
- this.voicemailTranscriptionView = voicemailTranscriptionView;
- this.callAccountLabel = callAccountLabel;
- }
-
- /**
- * Create a new instance by extracting the elements from the given view.
- * <p>
- * The view should contain three text views with identifiers {@code R.id.name},
- * {@code R.id.date}, and {@code R.id.number}, and a linear layout with identifier
- * {@code R.id.call_types}.
- */
- public static PhoneCallDetailsViews fromView(View view) {
- return new PhoneCallDetailsViews((TextView) view.findViewById(R.id.name),
- view.findViewById(R.id.call_type),
- (CallTypeIconsView) view.findViewById(R.id.call_type_icons),
- (TextView) view.findViewById(R.id.call_location_and_date),
- (TextView) view.findViewById(R.id.voicemail_transcription),
- (TextView) view.findViewById(R.id.call_account_label));
- }
-
- public static PhoneCallDetailsViews createForTest(Context context) {
- return new PhoneCallDetailsViews(
- new TextView(context),
- new View(context),
- new CallTypeIconsView(context),
- new TextView(context),
- new TextView(context),
- new TextView(context));
- }
-}
diff --git a/src/com/android/dialer/calllog/PhoneNumberDisplayUtil.java b/src/com/android/dialer/calllog/PhoneNumberDisplayUtil.java
deleted file mode 100644
index 5b1fc9e3a..000000000
--- a/src/com/android/dialer/calllog/PhoneNumberDisplayUtil.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.Context;
-import android.provider.CallLog.Calls;
-import android.text.TextUtils;
-
-import com.android.dialer.R;
-import com.android.dialer.util.PhoneNumberUtil;
-
-/**
- * Helper for formatting and managing the display of phone numbers.
- */
-public class PhoneNumberDisplayUtil {
-
- /**
- * Returns the string to display for the given phone number if there is no matching contact.
- */
- /* package */ static CharSequence getDisplayName(
- Context context,
- CharSequence number,
- int presentation,
- boolean isVoicemail) {
- if (presentation == Calls.PRESENTATION_UNKNOWN) {
- return context.getResources().getString(R.string.unknown);
- }
- if (presentation == Calls.PRESENTATION_RESTRICTED) {
- return context.getResources().getString(R.string.private_num);
- }
- if (presentation == Calls.PRESENTATION_PAYPHONE) {
- return context.getResources().getString(R.string.payphone);
- }
- if (isVoicemail) {
- return context.getResources().getString(R.string.voicemail);
- }
- if (PhoneNumberUtil.isLegacyUnknownNumbers(number)) {
- return context.getResources().getString(R.string.unknown);
- }
- return "";
- }
-
- /**
- * Returns the string to display for the given phone number.
- *
- * @param number the number to display
- * @param formattedNumber the formatted number if available, may be null
- */
- public static CharSequence getDisplayNumber(
- Context context,
- CharSequence number,
- int presentation,
- CharSequence formattedNumber,
- CharSequence postDialDigits,
- boolean isVoicemail) {
- final CharSequence displayName = getDisplayName(context, number, presentation, isVoicemail);
- if (!TextUtils.isEmpty(displayName)) {
- return displayName;
- }
-
- if (!TextUtils.isEmpty(formattedNumber)) {
- return formattedNumber;
- } else if (!TextUtils.isEmpty(number)) {
- return number.toString() + postDialDigits;
- } else {
- return context.getResources().getString(R.string.unknown);
- }
- }
-}
diff --git a/src/com/android/dialer/calllog/PhoneQuery.java b/src/com/android/dialer/calllog/PhoneQuery.java
deleted file mode 100644
index f1f14c66e..000000000
--- a/src/com/android/dialer/calllog/PhoneQuery.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.PhoneLookup;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.PhoneLookupSdkCompat;
-import com.android.contacts.common.ContactsUtils;
-
-/**
- * The queries to look up the {@link ContactInfo} for a given number in the Call Log.
- */
-final class PhoneQuery {
-
- /**
- * Projection to look up the ContactInfo. Does not include DISPLAY_NAME_ALTERNATIVE as that
- * column isn't available in ContactsCommon.PhoneLookup.
- * We should always use this projection starting from NYC onward.
- */
- private static final String[] PHONE_LOOKUP_PROJECTION = new String[] {
- PhoneLookupSdkCompat.CONTACT_ID,
- PhoneLookup.DISPLAY_NAME,
- PhoneLookup.TYPE,
- PhoneLookup.LABEL,
- PhoneLookup.NUMBER,
- PhoneLookup.NORMALIZED_NUMBER,
- PhoneLookup.PHOTO_ID,
- PhoneLookup.LOOKUP_KEY,
- PhoneLookup.PHOTO_URI
- };
-
- /**
- * Similar to {@link PHONE_LOOKUP_PROJECTION}. In pre-N, contact id is stored in
- * {@link PhoneLookup#_ID} in non-sip query.
- */
- private static final String[] BACKWARD_COMPATIBLE_NON_SIP_PHONE_LOOKUP_PROJECTION =
- new String[] {
- PhoneLookup._ID,
- PhoneLookup.DISPLAY_NAME,
- PhoneLookup.TYPE,
- PhoneLookup.LABEL,
- PhoneLookup.NUMBER,
- PhoneLookup.NORMALIZED_NUMBER,
- PhoneLookup.PHOTO_ID,
- PhoneLookup.LOOKUP_KEY,
- PhoneLookup.PHOTO_URI
- };
-
- public static String[] getPhoneLookupProjection(Uri phoneLookupUri) {
- if (CompatUtils.isNCompatible()) {
- return PHONE_LOOKUP_PROJECTION;
- }
- // Pre-N
- boolean isSip = phoneLookupUri.getBooleanQueryParameter(
- ContactsContract.PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, false);
- return (isSip) ? PHONE_LOOKUP_PROJECTION
- : BACKWARD_COMPATIBLE_NON_SIP_PHONE_LOOKUP_PROJECTION;
- }
-
- public static final int PERSON_ID = 0;
- public static final int NAME = 1;
- public static final int PHONE_TYPE = 2;
- public static final int LABEL = 3;
- public static final int MATCHED_NUMBER = 4;
- public static final int NORMALIZED_NUMBER = 5;
- public static final int PHOTO_ID = 6;
- public static final int LOOKUP_KEY = 7;
- public static final int PHOTO_URI = 8;
-
- /**
- * Projection to look up a contact's DISPLAY_NAME_ALTERNATIVE
- */
- public static final String[] DISPLAY_NAME_ALTERNATIVE_PROJECTION = new String[] {
- Contacts.DISPLAY_NAME_ALTERNATIVE,
- };
-
- public static final int NAME_ALTERNATIVE = 0;
-}
diff --git a/src/com/android/dialer/calllog/PromoCardViewHolder.java b/src/com/android/dialer/calllog/PromoCardViewHolder.java
deleted file mode 100644
index f5a7501fc..000000000
--- a/src/com/android/dialer/calllog/PromoCardViewHolder.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.Context;
-import android.support.v7.widget.CardView;
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.dialer.R;
-
-/**
- * Generic ViewHolder class for a promo card with a primary and secondary action.
- * Example: the promo card which appears in the voicemail tab.
- */
-public class PromoCardViewHolder extends RecyclerView.ViewHolder {
- public static PromoCardViewHolder create(View rootView) {
- return new PromoCardViewHolder(rootView);
- }
-
- /**
- * The primary action button view.
- */
- private View mPrimaryActionView;
-
- /**
- * The secondary action button view.
- * The "Ok" button view.
- */
- private View mSecondaryActionView;
-
- /**
- * Creates an instance of the {@link ViewHolder}.
- *
- * @param rootView The root view.
- */
- private PromoCardViewHolder(View rootView) {
- super(rootView);
-
- mPrimaryActionView = rootView.findViewById(R.id.primary_action);
- mSecondaryActionView = rootView.findViewById(R.id.secondary_action);
- }
-
- /**
- * Retrieves the "primary" action button (eg. "OK").
- *
- * @return The view.
- */
- public View getPrimaryActionView() {
- return mPrimaryActionView;
- }
-
- /**
- * Retrieves the "secondary" action button (eg. "Cancel" or "More Info").
- *
- * @return The view.
- */
- public View getSecondaryActionView() {
- return mSecondaryActionView;
- }
-
- @NeededForTesting
- public static PromoCardViewHolder createForTest(Context context) {
- PromoCardViewHolder viewHolder = new PromoCardViewHolder(new View(context));
- viewHolder.mPrimaryActionView = new View(context);
- viewHolder.mSecondaryActionView = new View(context);
- return viewHolder;
- }
-}
diff --git a/src/com/android/dialer/calllog/VisualVoicemailCallLogFragment.java b/src/com/android/dialer/calllog/VisualVoicemailCallLogFragment.java
deleted file mode 100644
index 311ff7dc5..000000000
--- a/src/com/android/dialer/calllog/VisualVoicemailCallLogFragment.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.database.ContentObserver;
-import android.os.Bundle;
-import android.provider.CallLog;
-import android.provider.VoicemailContract;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.dialer.R;
-import com.android.dialer.list.ListsFragment;
-import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
-
-public class VisualVoicemailCallLogFragment extends CallLogFragment {
-
- private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
- private final ContentObserver mVoicemailStatusObserver = new CustomContentObserver();
-
- public VisualVoicemailCallLogFragment() {
- super(CallLog.Calls.VOICEMAIL_TYPE);
- }
-
- @Override
- public void onCreate(Bundle state) {
- super.onCreate(state);
- mVoicemailPlaybackPresenter = VoicemailPlaybackPresenter.getInstance(getActivity(), state);
- getActivity().getContentResolver().registerContentObserver(
- VoicemailContract.Status.CONTENT_URI, true, mVoicemailStatusObserver);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
- View view = inflater.inflate(R.layout.call_log_fragment, container, false);
- setupView(view, mVoicemailPlaybackPresenter);
- return view;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mVoicemailPlaybackPresenter.onResume();
- }
-
- @Override
- public void onPause() {
- mVoicemailPlaybackPresenter.onPause();
- super.onPause();
- }
-
- @Override
- public void onDestroy() {
- mVoicemailPlaybackPresenter.onDestroy();
- getActivity().getContentResolver().unregisterContentObserver(mVoicemailStatusObserver);
- super.onDestroy();
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mVoicemailPlaybackPresenter.onSaveInstanceState(outState);
- }
-
- @Override
- public void fetchCalls() {
- super.fetchCalls();
- ((ListsFragment) getParentFragment()).updateTabUnreadCounts();
- }
-}
diff --git a/src/com/android/dialer/calllog/VoicemailQueryHandler.java b/src/com/android/dialer/calllog/VoicemailQueryHandler.java
deleted file mode 100644
index c6e644c32..000000000
--- a/src/com/android/dialer/calllog/VoicemailQueryHandler.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.AsyncQueryHandler;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.provider.CallLog.Calls;
-import android.util.Log;
-
-/**
- * Handles asynchronous queries to the call log for voicemail.
- */
-public class VoicemailQueryHandler extends AsyncQueryHandler {
- private static final String TAG = "VoicemailQueryHandler";
-
- /** The token for the query to mark all new voicemails as old. */
- private static final int UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN = 50;
- private Context mContext;
-
- public VoicemailQueryHandler(Context context, ContentResolver contentResolver) {
- super(contentResolver);
- mContext = context;
- }
-
- /** Updates all new voicemails to mark them as old. */
- public void markNewVoicemailsAsOld() {
- // Mark all "new" voicemails as not new anymore.
- StringBuilder where = new StringBuilder();
- where.append(Calls.NEW);
- where.append(" = 1 AND ");
- where.append(Calls.TYPE);
- where.append(" = ?");
-
- ContentValues values = new ContentValues(1);
- values.put(Calls.NEW, "0");
-
- startUpdate(UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN, null, Calls.CONTENT_URI_WITH_VOICEMAIL,
- values, where.toString(), new String[]{ Integer.toString(Calls.VOICEMAIL_TYPE) });
- }
-
- @Override
- protected void onUpdateComplete(int token, Object cookie, int result) {
- if (token == UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN) {
- if (mContext != null) {
- Intent serviceIntent = new Intent(mContext, CallLogNotificationsService.class);
- serviceIntent.setAction(
- CallLogNotificationsService.ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS);
- mContext.startService(serviceIntent);
- } else {
- Log.w(TAG, "Unknown update completed: ignoring: " + token);
- }
- }
- }
-}
diff --git a/src/com/android/dialer/calllog/calllogcache/CallLogCache.java b/src/com/android/dialer/calllog/calllogcache/CallLogCache.java
deleted file mode 100644
index dc1217cf5..000000000
--- a/src/com/android/dialer/calllog/calllogcache/CallLogCache.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.dialer.calllog.calllogcache;
-
-import android.content.Context;
-import android.telecom.PhoneAccountHandle;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.dialer.calllog.CallLogAdapter;
-
-/**
- * This is the base class for the CallLogCaches.
- *
- * Keeps a cache of recently made queries to the Telecom/Telephony processes. The aim of this cache
- * is to reduce the number of cross-process requests to TelecomManager, which can negatively affect
- * performance.
- *
- * This is designed with the specific use case of the {@link CallLogAdapter} in mind.
- */
-public abstract class CallLogCache {
- // TODO: Dialer should be fixed so as not to check isVoicemail() so often but at the time of
- // this writing, that was a much larger undertaking than creating this cache.
-
- protected final Context mContext;
-
- private boolean mHasCheckedForVideoEnabled;
- private boolean mIsVideoEnabled;
-
- public CallLogCache(Context context) {
- mContext = context;
- }
-
- /**
- * Return the most compatible version of the TelecomCallLogCache.
- */
- public static CallLogCache getCallLogCache(Context context) {
- if (CompatUtils.isClassAvailable("android.telecom.PhoneAccountHandle")) {
- return new CallLogCacheLollipopMr1(context);
- }
- return new CallLogCacheLollipop(context);
- }
-
- public void reset() {
- mHasCheckedForVideoEnabled = false;
- mIsVideoEnabled = false;
- }
-
- /**
- * Returns true if the given number is the number of the configured voicemail. To be able to
- * mock-out this, it is not a static method.
- */
- public abstract boolean isVoicemailNumber(PhoneAccountHandle accountHandle,
- CharSequence number);
-
- public boolean isVideoEnabled() {
- if (!mHasCheckedForVideoEnabled) {
- mIsVideoEnabled = CallUtil.isVideoEnabled(mContext);
- mHasCheckedForVideoEnabled = true;
- }
- return mIsVideoEnabled;
- }
-
- /**
- * Extract account label from PhoneAccount object.
- */
- public abstract String getAccountLabel(PhoneAccountHandle accountHandle);
-
- /**
- * Extract account color from PhoneAccount object.
- */
- public abstract int getAccountColor(PhoneAccountHandle accountHandle);
-
- /**
- * Determines if the PhoneAccount supports specifying a call subject (i.e. calling with a note)
- * for outgoing calls.
- *
- * @param accountHandle The PhoneAccount handle.
- * @return {@code true} if calling with a note is supported, {@code false} otherwise.
- */
- public abstract boolean doesAccountSupportCallSubject(PhoneAccountHandle accountHandle);
-}
diff --git a/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipop.java b/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipop.java
deleted file mode 100644
index 770cc9d3e..000000000
--- a/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipop.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.dialer.calllog.calllogcache;
-
-import android.content.Context;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-
-/**
- * This is a compatibility class for the CallLogCache for versions of dialer before Lollipop Mr1
- * (the introduction of phone accounts).
- *
- * This class should not be initialized directly and instead be acquired from
- * {@link CallLogCache#getCallLogCache}.
- */
-class CallLogCacheLollipop extends CallLogCache {
- private String mVoicemailNumber;
-
- /* package */ CallLogCacheLollipop(Context context) {
- super(context);
- }
-
- @Override
- public boolean isVoicemailNumber(PhoneAccountHandle accountHandle, CharSequence number) {
- if (TextUtils.isEmpty(number)) {
- return false;
- }
-
- String numberString = number.toString();
-
- if (!TextUtils.isEmpty(mVoicemailNumber)) {
- return PhoneNumberUtils.compare(numberString, mVoicemailNumber);
- }
-
- if (PhoneNumberUtils.isVoiceMailNumber(numberString)) {
- mVoicemailNumber = numberString;
- return true;
- }
-
- return false;
- }
-
- @Override
- public String getAccountLabel(PhoneAccountHandle accountHandle) {
- return null;
- }
-
- @Override
- public int getAccountColor(PhoneAccountHandle accountHandle) {
- return PhoneAccount.NO_HIGHLIGHT_COLOR;
- }
-
- @Override
- public boolean doesAccountSupportCallSubject(PhoneAccountHandle accountHandle) {
- return false;
- }
-}
diff --git a/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipopMr1.java b/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipopMr1.java
deleted file mode 100644
index d1e3f7bcf..000000000
--- a/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipopMr1.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.calllog.calllogcache;
-
-import android.content.Context;
-import android.telecom.PhoneAccountHandle;
-import android.text.TextUtils;
-import android.util.Pair;
-
-import com.android.dialer.calllog.PhoneAccountUtils;
-import com.android.dialer.util.PhoneNumberUtil;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * This is the CallLogCache for versions of dialer Lollipop Mr1 and above with support for
- * multi-SIM devices.
- *
- * This class should not be initialized directly and instead be acquired from
- * {@link CallLogCache#getCallLogCache}.
- */
-class CallLogCacheLollipopMr1 extends CallLogCache {
- // Maps from a phone-account/number pair to a boolean because multiple numbers could return true
- // for the voicemail number if those numbers are not pre-normalized.
- private final Map<Pair<PhoneAccountHandle, CharSequence>, Boolean> mVoicemailQueryCache =
- new HashMap<>();
- private final Map<PhoneAccountHandle, String> mPhoneAccountLabelCache = new HashMap<>();
- private final Map<PhoneAccountHandle, Integer> mPhoneAccountColorCache = new HashMap<>();
- private final Map<PhoneAccountHandle, Boolean> mPhoneAccountCallWithNoteCache = new HashMap<>();
-
- /* package */ CallLogCacheLollipopMr1(Context context) {
- super(context);
- }
-
- @Override
- public void reset() {
- mVoicemailQueryCache.clear();
- mPhoneAccountLabelCache.clear();
- mPhoneAccountColorCache.clear();
- mPhoneAccountCallWithNoteCache.clear();
-
- super.reset();
- }
-
- @Override
- public boolean isVoicemailNumber(PhoneAccountHandle accountHandle, CharSequence number) {
- if (TextUtils.isEmpty(number)) {
- return false;
- }
-
- Pair<PhoneAccountHandle, CharSequence> key = new Pair<>(accountHandle, number);
- if (mVoicemailQueryCache.containsKey(key)) {
- return mVoicemailQueryCache.get(key);
- } else {
- Boolean isVoicemail =
- PhoneNumberUtil.isVoicemailNumber(mContext, accountHandle, number.toString());
- mVoicemailQueryCache.put(key, isVoicemail);
- return isVoicemail;
- }
- }
-
- @Override
- public String getAccountLabel(PhoneAccountHandle accountHandle) {
- if (mPhoneAccountLabelCache.containsKey(accountHandle)) {
- return mPhoneAccountLabelCache.get(accountHandle);
- } else {
- String label = PhoneAccountUtils.getAccountLabel(mContext, accountHandle);
- mPhoneAccountLabelCache.put(accountHandle, label);
- return label;
- }
- }
-
- @Override
- public int getAccountColor(PhoneAccountHandle accountHandle) {
- if (mPhoneAccountColorCache.containsKey(accountHandle)) {
- return mPhoneAccountColorCache.get(accountHandle);
- } else {
- Integer color = PhoneAccountUtils.getAccountColor(mContext, accountHandle);
- mPhoneAccountColorCache.put(accountHandle, color);
- return color;
- }
- }
-
- @Override
- public boolean doesAccountSupportCallSubject(PhoneAccountHandle accountHandle) {
- if (mPhoneAccountCallWithNoteCache.containsKey(accountHandle)) {
- return mPhoneAccountCallWithNoteCache.get(accountHandle);
- } else {
- Boolean supportsCallWithNote =
- PhoneAccountUtils.getAccountSupportsCallSubject(mContext, accountHandle);
- mPhoneAccountCallWithNoteCache.put(accountHandle, supportsCallWithNote);
- return supportsCallWithNote;
- }
- }
-}
diff --git a/src/com/android/dialer/compat/DialerCompatUtils.java b/src/com/android/dialer/compat/DialerCompatUtils.java
deleted file mode 100644
index a9c9c5319..000000000
--- a/src/com/android/dialer/compat/DialerCompatUtils.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.dialer.compat;
-
-import com.android.contacts.common.compat.CompatUtils;
-
-public final class DialerCompatUtils {
- /**
- * Determines if this version has access to the
- * {@link android.provider.CallLog.Calls.CACHED_PHOTO_URI} column
- *
- * @return {@code true} if {@link android.provider.CallLog.Calls.CACHED_PHOTO_URI} is available,
- * {@code false} otherwise
- */
- public static boolean isCallsCachedPhotoUriCompatible() {
- return CompatUtils.isMarshmallowCompatible();
- }
-} \ No newline at end of file
diff --git a/src/com/android/dialer/compat/FilteredNumberCompat.java b/src/com/android/dialer/compat/FilteredNumberCompat.java
deleted file mode 100644
index 8d8e9a2a5..000000000
--- a/src/com/android/dialer/compat/FilteredNumberCompat.java
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * 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.dialer.compat;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-
-import android.app.FragmentManager;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.UserManager;
-import android.preference.PreferenceManager;
-import android.support.annotation.Nullable;
-import android.telecom.TelecomManager;
-import android.telephony.PhoneNumberUtils;
-import android.util.Log;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.TelecomManagerUtil;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.dialer.DialerApplication;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberSources;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberTypes;
-import com.android.dialer.filterednumber.BlockNumberDialogFragment;
-import com.android.dialer.filterednumber.BlockNumberDialogFragment.Callback;
-import com.android.dialer.filterednumber.BlockedNumbersMigrator;
-import com.android.dialer.filterednumber.BlockedNumbersSettingsActivity;
-import com.android.dialer.filterednumber.MigrateBlockedNumbersDialogFragment;
-import com.android.dialerbind.ObjectFactory;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Compatibility class to encapsulate logic to switch between call blocking using
- * {@link com.android.dialer.database.FilteredNumberContract} and using
- * {@link android.provider.BlockedNumberContract}. This class should be used rather than explicitly
- * referencing columns from either contract class in situations where both blocking solutions may be
- * used.
- */
-public class FilteredNumberCompat {
-
- private static final String TAG = "FilteredNumberCompat";
-
- protected static final String HAS_MIGRATED_TO_NEW_BLOCKING_KEY = "migratedToNewBlocking";
-
- private static Boolean isEnabledForTest;
-
- private static Context contextForTest;
-
- /**
- * @return The column name for ID in the filtered number database.
- */
- public static String getIdColumnName() {
- return useNewFiltering() ? BlockedNumbersSdkCompat._ID : FilteredNumberColumns._ID;
- }
-
- /**
- * @return The column name for type in the filtered number database. Will be {@code null} for
- * the framework blocking implementation.
- */
- @Nullable
- public static String getTypeColumnName() {
- return useNewFiltering() ? null : FilteredNumberColumns.TYPE;
- }
-
- /**
- * @return The column name for source in the filtered number database. Will be {@code null} for
- * the framework blocking implementation
- */
- @Nullable
- public static String getSourceColumnName() {
- return useNewFiltering() ? null : FilteredNumberColumns.SOURCE;
- }
-
- /**
- * @return The column name for the original number in the filtered number database.
- */
- public static String getOriginalNumberColumnName() {
- return useNewFiltering() ? BlockedNumbersSdkCompat.COLUMN_ORIGINAL_NUMBER
- : FilteredNumberColumns.NUMBER;
- }
-
- /**
- * @return The column name for country iso in the filtered number database. Will be {@code null}
- * the framework blocking implementation
- */
- @Nullable
- public static String getCountryIsoColumnName() {
- return useNewFiltering() ? null : FilteredNumberColumns.COUNTRY_ISO;
- }
-
- /**
- * @return The column name for the e164 formatted number in the filtered number database.
- */
- public static String getE164NumberColumnName() {
- return useNewFiltering() ? BlockedNumbersSdkCompat.E164_NUMBER
- : FilteredNumberColumns.NORMALIZED_NUMBER;
- }
-
- /**
- * @return {@code true} if the current SDK version supports using new filtering, {@code false}
- * otherwise.
- */
- public static boolean canUseNewFiltering() {
- if (isEnabledForTest != null) {
- return CompatUtils.isNCompatible() && isEnabledForTest;
- }
- return CompatUtils.isNCompatible() && ObjectFactory
- .isNewBlockingEnabled(DialerApplication.getContext());
- }
-
- /**
- * @return {@code true} if the new filtering should be used, i.e. it's enabled and any necessary
- * migration has been performed, {@code false} otherwise.
- */
- public static boolean useNewFiltering() {
- return canUseNewFiltering() && hasMigratedToNewBlocking();
- }
-
- /**
- * @return {@code true} if the user has migrated to use
- * {@link android.provider.BlockedNumberContract} blocking, {@code false} otherwise.
- */
- public static boolean hasMigratedToNewBlocking() {
- return PreferenceManager.getDefaultSharedPreferences(DialerApplication.getContext())
- .getBoolean(HAS_MIGRATED_TO_NEW_BLOCKING_KEY, false);
- }
-
- /**
- * Called to inform this class whether the user has fully migrated to use
- * {@link android.provider.BlockedNumberContract} blocking or not.
- *
- * @param hasMigrated {@code true} if the user has migrated, {@code false} otherwise.
- */
- @NeededForTesting
- public static void setHasMigratedToNewBlocking(boolean hasMigrated) {
- PreferenceManager.getDefaultSharedPreferences(
- MoreObjects.firstNonNull(contextForTest, DialerApplication.getContext())).edit()
- .putBoolean(HAS_MIGRATED_TO_NEW_BLOCKING_KEY, hasMigrated).apply();
- }
-
- @NeededForTesting
- public static void setIsEnabledForTest(Boolean isEnabled) {
- isEnabledForTest = isEnabled;
- }
-
- @NeededForTesting
- public static void setContextForTest(Context context) {
- contextForTest = context;
- }
-
- /**
- * Gets the content {@link Uri} for number filtering.
- *
- * @param id The optional id to append with the base content uri.
- * @return The Uri for number filtering.
- */
- public static Uri getContentUri(@Nullable Integer id) {
- if (id == null) {
- return getBaseUri();
- }
- return ContentUris.withAppendedId(getBaseUri(), id);
- }
-
-
- private static Uri getBaseUri() {
- return useNewFiltering() ? BlockedNumbersSdkCompat.CONTENT_URI : FilteredNumber.CONTENT_URI;
- }
-
- /**
- * Removes any null column names from the given projection array. This method is intended to be
- * used to strip out any column names that aren't available in every version of number blocking.
- * Example:
- * {@literal
- * getContext().getContentResolver().query(
- * someUri,
- * // Filtering ensures that no non-existant columns are queried
- * FilteredNumberCompat.filter(new String[] {FilteredNumberCompat.getIdColumnName(),
- * FilteredNumberCompat.getTypeColumnName()},
- * FilteredNumberCompat.getE164NumberColumnName() + " = ?",
- * new String[] {e164Number});
- * }
- *
- * @param projection The projection array.
- * @return The filtered projection array.
- */
- @Nullable
- public static String[] filter(@Nullable String[] projection) {
- if (projection == null) {
- return null;
- }
- List<String> filtered = new ArrayList<>();
- for (String column : projection) {
- if (column != null) {
- filtered.add(column);
- }
- }
- return filtered.toArray(new String[filtered.size()]);
- }
-
- /**
- * Creates a new {@link ContentValues} suitable for inserting in the filtered number table.
- *
- * @param number The unformatted number to insert.
- * @param e164Number (optional) The number to insert formatted to E164 standard.
- * @param countryIso (optional) The country iso to use to format the number.
- * @return The ContentValues to insert.
- * @throws NullPointerException If number is null.
- */
- public static ContentValues newBlockNumberContentValues(String number,
- @Nullable String e164Number, @Nullable String countryIso) {
- ContentValues contentValues = new ContentValues();
- contentValues.put(getOriginalNumberColumnName(), Preconditions.checkNotNull(number));
- if (!useNewFiltering()) {
- if (e164Number == null) {
- e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso);
- }
- contentValues.put(getE164NumberColumnName(), e164Number);
- contentValues.put(getCountryIsoColumnName(), countryIso);
- contentValues.put(getTypeColumnName(), FilteredNumberTypes.BLOCKED_NUMBER);
- contentValues.put(getSourceColumnName(), FilteredNumberSources.USER);
- }
- return contentValues;
- }
-
- /**
- * Shows block number migration dialog if necessary.
- *
- * @param fragmentManager The {@link FragmentManager} used to show fragments.
- * @param listener The {@link BlockedNumbersMigrator.Listener} to call when migration is
- * complete.
- * @return boolean True if migration dialog is shown.
- */
- public static boolean maybeShowBlockNumberMigrationDialog(
- ContentResolver contentResolver, FragmentManager fragmentManager,
- BlockedNumbersMigrator.Listener listener) {
- if (shouldShowMigrationDialog(true)) {
- Log.i(TAG, "maybeShowBlockNumberMigrationDialog - showing migration dialog");
- MigrateBlockedNumbersDialogFragment
- .newInstance(new BlockedNumbersMigrator(contentResolver), listener)
- .show(fragmentManager, "MigrateBlockedNumbers");
- return true;
- }
- return false;
- }
-
- /**
- * Shows the flow of {@link android.app.DialogFragment}s for blocking or unblocking numbers.
- *
- * @param blockId The id into the blocked numbers database.
- * @param number The number to block or unblock.
- * @param countryIso The countryIso used to format the given number.
- * @param displayNumber The form of the number to block, suitable for displaying.
- * @param parentViewId The id for the containing view of the Dialog.
- * @param fragmentManager The {@link FragmentManager} used to show fragments.
- * @param callback (optional) The {@link Callback} to call when the block or unblock operation
- * is complete.
- */
- public static void showBlockNumberDialogFlow(final ContentResolver contentResolver,
- final Integer blockId, final String number, final String countryIso,
- final String displayNumber, final Integer parentViewId,
- final FragmentManager fragmentManager, @Nullable final Callback callback) {
- Log.i(TAG, "showBlockNumberDialogFlow - start");
- // If the user is blocking a number and isn't using the framework solution when they
- // should be, show the migration dialog
- if (shouldShowMigrationDialog(blockId == null)) {
- Log.i(TAG, "showBlockNumberDialogFlow - showing migration dialog");
- MigrateBlockedNumbersDialogFragment
- .newInstance(new BlockedNumbersMigrator(contentResolver), newMigrationListener(
- DialerApplication.getContext().getContentResolver(), number, countryIso,
- displayNumber, parentViewId, fragmentManager, callback))
- .show(fragmentManager, "MigrateBlockedNumbers");
- return;
- }
- Log.i(TAG, "showBlockNumberDialogFlow - showing block number dialog");
- BlockNumberDialogFragment
- .show(blockId, number, countryIso, displayNumber, parentViewId, fragmentManager,
- callback);
- }
-
- private static boolean shouldShowMigrationDialog(boolean isBlocking) {
- return isBlocking && canUseNewFiltering() && !hasMigratedToNewBlocking();
- }
-
- private static BlockedNumbersMigrator.Listener newMigrationListener(
- final ContentResolver contentResolver, final String number, final String countryIso,
- final String displayNumber, final Integer parentViewId,
- final FragmentManager fragmentManager, @Nullable final Callback callback) {
- return new BlockedNumbersMigrator.Listener() {
- @Override
- public void onComplete() {
- Log.i(TAG, "showBlockNumberDialogFlow - listener showing block number dialog");
- if (!hasMigratedToNewBlocking()) {
- Log.i(TAG, "showBlockNumberDialogFlow - migration failed");
- return;
- }
- /*
- * Edge case to cover here: if the user initiated the migration workflow with a
- * number that's already blocked in the framework, don't show the block number
- * dialog. Doing so would allow them to block the same number twice, causing a
- * crash.
- */
- new FilteredNumberAsyncQueryHandler(contentResolver).isBlockedNumber(
- new OnCheckBlockedListener() {
- @Override
- public void onCheckComplete(Integer id) {
- if (id != null) {
- Log.i(TAG,
- "showBlockNumberDialogFlow - number already blocked");
- return;
- }
- Log.i(TAG, "showBlockNumberDialogFlow - need to block number");
- BlockNumberDialogFragment
- .show(null, number, countryIso, displayNumber, parentViewId,
- fragmentManager, callback);
- }
- }, number, countryIso);
- }
- };
- }
-
- /**
- * Creates the {@link Intent} which opens the blocked numbers management interface.
- *
- * @param context The {@link Context}.
- * @return The intent.
- */
- public static Intent createManageBlockedNumbersIntent(Context context) {
- if (canUseNewFiltering() && hasMigratedToNewBlocking()) {
- return TelecomManagerUtil.createManageBlockedNumbersIntent(
- (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE));
- }
- return new Intent(context, BlockedNumbersSettingsActivity.class);
- }
-
- /**
- * Method used to determine if block operations are possible.
- *
- * @param context The {@link Context}.
- * @return {@code true} if the app and user can block numbers, {@code false} otherwise.
- */
- public static boolean canAttemptBlockOperations(Context context) {
- if (!CompatUtils.isNCompatible()) {
- // Dialer blocking, must be primary user
- return UserManagerCompat.isSystemUser(
- (UserManager) context.getSystemService(Context.USER_SERVICE));
- }
-
- // Great Wall blocking, must be primary user and the default or system dialer
- // TODO(maxwelb): check that we're the default or system Dialer
- return BlockedNumbersSdkCompat.canCurrentUserBlockNumbers(context);
- }
-
- /**
- * Used to determine if the call blocking settings can be opened.
- *
- * @param context The {@link Context}.
- * @return {@code true} if the current user can open the call blocking settings, {@code false}
- * otherwise.
- */
- public static boolean canCurrentUserOpenBlockSettings(Context context) {
- if (!CompatUtils.isNCompatible()) {
- // Dialer blocking, must be primary user
- return UserManagerCompat.isSystemUser(
- (UserManager) context.getSystemService(Context.USER_SERVICE));
- }
- // BlockedNumberContract blocking, verify through Contract API
- return BlockedNumbersSdkCompat.canCurrentUserBlockNumbers(context);
- }
-}
diff --git a/src/com/android/dialer/compat/SettingsCompat.java b/src/com/android/dialer/compat/SettingsCompat.java
deleted file mode 100644
index 474a600a4..000000000
--- a/src/com/android/dialer/compat/SettingsCompat.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.dialer.compat;
-
-import android.content.Context;
-import android.os.Build;
-import android.os.Build.VERSION_CODES;
-import android.provider.Settings;
-
-import com.android.contacts.common.compat.SdkVersionOverride;
-
-/**
- * Compatibility class for {@link android.provider.Settings}
- */
-public class SettingsCompat {
-
- public static class System {
-
- /**
- * Compatibility version of {@link android.provider.Settings.System#canWrite(Context)}
- *
- * Note: Since checking preferences at runtime started in M, this method always returns
- * {@code true} for SDK versions prior to 23. In those versions, the app wouldn't be
- * installed if it didn't have the proper permission
- */
- public static boolean canWrite(Context context) {
- if (SdkVersionOverride.getSdkVersion(VERSION_CODES.LOLLIPOP) >= Build.VERSION_CODES.M) {
- return Settings.System.canWrite(context);
- }
- return true;
- }
- }
-
-}
diff --git a/src/com/android/dialer/compat/UserManagerCompat.java b/src/com/android/dialer/compat/UserManagerCompat.java
deleted file mode 100644
index 576703364..000000000
--- a/src/com/android/dialer/compat/UserManagerCompat.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.dialer.compat;
-
-import android.content.Context;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-
-import com.android.contacts.common.compat.CompatUtils;
-
-/**
- * Compatibility class for {@link UserManager}.
- */
-public class UserManagerCompat {
- /**
- * A user id constant to indicate the "system" user of the device. Copied from
- * {@link UserHandle}.
- */
- private static final int USER_SYSTEM = 0;
- /**
- * Range of uids allocated for a user.
- */
- private static final int PER_USER_RANGE = 100000;
-
- /**
- * Used to check if this process is running under the system user. The system user is the
- * initial user that is implicitly created on first boot and hosts most of the system services.
- *
- * @return whether this process is running under the system user.
- */
- public static boolean isSystemUser(UserManager userManager) {
- if (CompatUtils.isMarshmallowCompatible()) {
- return userManager.isSystemUser();
- }
- // Adapted from {@link UserManager} and {@link UserHandle}.
- return (Process.myUid() / PER_USER_RANGE) == USER_SYSTEM;
- }
-
- /**
- * Return whether the calling user is running in an "unlocked" state. A user
- * is unlocked only after they've entered their credentials (such as a lock
- * pattern or PIN), and credential-encrypted private app data storage is
- * available.
- *
- * TODO b/26688153
- *
- * @param context the current context
- * @return {@code true} if the user is unlocked, {@code false} otherwise
- * @throws NullPointerException if context is null
- */
- public static boolean isUserUnlocked(Context context) {
- if (CompatUtils.isNCompatible()) {
- return UserManagerSdkCompat.isUserUnlocked(context);
- }
- return true;
- }
-}
diff --git a/src/com/android/dialer/contact/ContactUpdateService.java b/src/com/android/dialer/contact/ContactUpdateService.java
deleted file mode 100644
index 9edd19827..000000000
--- a/src/com/android/dialer/contact/ContactUpdateService.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.dialer.contact;
-
-import android.app.IntentService;
-import android.content.Context;
-import android.content.Intent;
-
-import com.android.contacts.common.database.ContactUpdateUtils;
-
-/**
- * Service for updating primary number on a contact.
- */
-public class ContactUpdateService extends IntentService {
-
- public static final String EXTRA_PHONE_NUMBER_DATA_ID = "phone_number_data_id";
-
- public ContactUpdateService() {
- super(ContactUpdateService.class.getSimpleName());
- setIntentRedelivery(true);
- }
-
- /** Creates an intent that sets the selected data item as super primary (default) */
- public static Intent createSetSuperPrimaryIntent(Context context, long dataId) {
- Intent serviceIntent = new Intent(context, ContactUpdateService.class);
- serviceIntent.putExtra(EXTRA_PHONE_NUMBER_DATA_ID, dataId);
- return serviceIntent;
- }
-
- @Override
- protected void onHandleIntent(Intent intent) {
- // Currently this service only handles one type of update.
- long dataId = intent.getLongExtra(EXTRA_PHONE_NUMBER_DATA_ID, -1);
-
- ContactUpdateUtils.setSuperPrimary(this, dataId);
- }
-}
diff --git a/src/com/android/dialer/contactinfo/ContactInfoCache.java b/src/com/android/dialer/contactinfo/ContactInfoCache.java
deleted file mode 100644
index 28a919430..000000000
--- a/src/com/android/dialer/contactinfo/ContactInfoCache.java
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * 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.dialer.contactinfo;
-
-import android.os.Handler;
-import android.os.Message;
-import android.text.TextUtils;
-
-import com.android.dialer.calllog.ContactInfo;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.util.ExpirableCache;
-import com.google.common.annotations.VisibleForTesting;
-
-import java.util.LinkedList;
-
-/**
- * This is a cache of contact details for the phone numbers in the call log. The key is the
- * phone number with the country in which the call was placed or received. The content of the
- * cache is expired (but not purged) whenever the application comes to the foreground.
- *
- * This cache queues request for information and queries for information on a background thread,
- * so {@code start()} and {@code stop()} must be called to initiate or halt that thread's exeuction
- * as needed.
- *
- * TODO: Explore whether there is a pattern to remove external dependencies for starting and
- * stopping the query thread.
- */
-public class ContactInfoCache {
- public interface OnContactInfoChangedListener {
- public void onContactInfoChanged();
- }
-
- /*
- * Handles requests for contact name and number type.
- */
- private class QueryThread extends Thread {
- private volatile boolean mDone = false;
-
- public QueryThread() {
- super("ContactInfoCache.QueryThread");
- }
-
- public void stopProcessing() {
- mDone = true;
- }
-
- @Override
- public void run() {
- boolean needRedraw = false;
- while (true) {
- // Check if thread is finished, and if so return immediately.
- if (mDone) return;
-
- // Obtain next request, if any is available.
- // Keep synchronized section small.
- ContactInfoRequest req = null;
- synchronized (mRequests) {
- if (!mRequests.isEmpty()) {
- req = mRequests.removeFirst();
- }
- }
-
- if (req != null) {
- // Process the request. If the lookup succeeds, schedule a redraw.
- needRedraw |= queryContactInfo(req.number, req.countryIso, req.callLogInfo);
- } else {
- // Throttle redraw rate by only sending them when there are
- // more requests.
- if (needRedraw) {
- needRedraw = false;
- mHandler.sendEmptyMessage(REDRAW);
- }
-
- // Wait until another request is available, or until this
- // thread is no longer needed (as indicated by being
- // interrupted).
- try {
- synchronized (mRequests) {
- mRequests.wait(1000);
- }
- } catch (InterruptedException ie) {
- // Ignore, and attempt to continue processing requests.
- }
- }
- }
- }
- }
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case REDRAW:
- mOnContactInfoChangedListener.onContactInfoChanged();
- break;
- case START_THREAD:
- startRequestProcessing();
- break;
- }
- }
- };
-
- private static final int REDRAW = 1;
- private static final int START_THREAD = 2;
-
- private static final int CONTACT_INFO_CACHE_SIZE = 100;
- private static final int START_PROCESSING_REQUESTS_DELAY_MS = 1000;
-
-
- /**
- * List of requests to update contact details. Each request contains a phone number to look up,
- * and the contact info currently stored in the call log for this number.
- *
- * The requests are added when displaying contacts and are processed by a background thread.
- */
- private final LinkedList<ContactInfoRequest> mRequests;
-
- private ExpirableCache<NumberWithCountryIso, ContactInfo> mCache;
-
- private ContactInfoHelper mContactInfoHelper;
- private QueryThread mContactInfoQueryThread;
- private OnContactInfoChangedListener mOnContactInfoChangedListener;
-
- public ContactInfoCache(ContactInfoHelper contactInfoHelper,
- OnContactInfoChangedListener onContactInfoChangedListener) {
- mContactInfoHelper = contactInfoHelper;
- mOnContactInfoChangedListener = onContactInfoChangedListener;
-
- mRequests = new LinkedList<ContactInfoRequest>();
- mCache = ExpirableCache.create(CONTACT_INFO_CACHE_SIZE);
- }
-
- public ContactInfo getValue(String number, String countryIso, ContactInfo cachedContactInfo) {
- NumberWithCountryIso numberCountryIso = new NumberWithCountryIso(number, countryIso);
- ExpirableCache.CachedValue<ContactInfo> cachedInfo =
- mCache.getCachedValue(numberCountryIso);
- ContactInfo info = cachedInfo == null ? null : cachedInfo.getValue();
- if (cachedInfo == null) {
- mCache.put(numberCountryIso, ContactInfo.EMPTY);
- // Use the cached contact info from the call log.
- info = cachedContactInfo;
- // The db request should happen on a non-UI thread.
- // Request the contact details immediately since they are currently missing.
- enqueueRequest(number, countryIso, cachedContactInfo, true);
- // We will format the phone number when we make the background request.
- } else {
- if (cachedInfo.isExpired()) {
- // The contact info is no longer up to date, we should request it. However, we
- // do not need to request them immediately.
- enqueueRequest(number, countryIso, cachedContactInfo, false);
- } else if (!callLogInfoMatches(cachedContactInfo, info)) {
- // The call log information does not match the one we have, look it up again.
- // We could simply update the call log directly, but that needs to be done in a
- // background thread, so it is easier to simply request a new lookup, which will, as
- // a side-effect, update the call log.
- enqueueRequest(number, countryIso, cachedContactInfo, false);
- }
-
- if (info == ContactInfo.EMPTY) {
- // Use the cached contact info from the call log.
- info = cachedContactInfo;
- }
- }
- return info;
- }
-
- /**
- * Queries the appropriate content provider for the contact associated with the number.
- *
- * Upon completion it also updates the cache in the call log, if it is different from
- * {@code callLogInfo}.
- *
- * The number might be either a SIP address or a phone number.
- *
- * It returns true if it updated the content of the cache and we should therefore tell the
- * view to update its content.
- */
- private boolean queryContactInfo(String number, String countryIso, ContactInfo callLogInfo) {
- final ContactInfo info = mContactInfoHelper.lookupNumber(number, countryIso);
-
- if (info == null) {
- // The lookup failed, just return without requesting to update the view.
- return false;
- }
-
- // Check the existing entry in the cache: only if it has changed we should update the
- // view.
- NumberWithCountryIso numberCountryIso = new NumberWithCountryIso(number, countryIso);
- ContactInfo existingInfo = mCache.getPossiblyExpired(numberCountryIso);
-
- final boolean isRemoteSource = info.sourceType != 0;
-
- // Don't force redraw if existing info in the cache is equal to {@link ContactInfo#EMPTY}
- // to avoid updating the data set for every new row that is scrolled into view.
- // see (https://googleplex-android-review.git.corp.google.com/#/c/166680/)
-
- // Exception: Photo uris for contacts from remote sources are not cached in the call log
- // cache, so we have to force a redraw for these contacts regardless.
- boolean updated = (existingInfo != ContactInfo.EMPTY || isRemoteSource) &&
- !info.equals(existingInfo);
-
- // Store the data in the cache so that the UI thread can use to display it. Store it
- // even if it has not changed so that it is marked as not expired.
- mCache.put(numberCountryIso, info);
-
- // Update the call log even if the cache it is up-to-date: it is possible that the cache
- // contains the value from a different call log entry.
- mContactInfoHelper.updateCallLogContactInfo(number, countryIso, info, callLogInfo);
- return updated;
- }
-
- /**
- * After a delay, start the thread to begin processing requests. We perform lookups on a
- * background thread, but this must be called to indicate the thread should be running.
- */
- public void start() {
- // Schedule a thread-creation message if the thread hasn't been created yet, as an
- // optimization to queue fewer messages.
- if (mContactInfoQueryThread == null) {
- // TODO: Check whether this delay before starting to process is necessary.
- mHandler.sendEmptyMessageDelayed(START_THREAD, START_PROCESSING_REQUESTS_DELAY_MS);
- }
- }
-
- /**
- * Stops the thread and clears the queue of messages to process. This cleans up the thread
- * for lookups so that it is not perpetually running.
- */
- public void stop() {
- stopRequestProcessing();
- }
-
- /**
- * Starts a background thread to process contact-lookup requests, unless one
- * has already been started.
- */
- private synchronized void startRequestProcessing() {
- // For unit-testing.
- if (mRequestProcessingDisabled) return;
-
- // If a thread is already started, don't start another.
- if (mContactInfoQueryThread != null) {
- return;
- }
-
- mContactInfoQueryThread = new QueryThread();
- mContactInfoQueryThread.setPriority(Thread.MIN_PRIORITY);
- mContactInfoQueryThread.start();
- }
-
- public void invalidate() {
- mCache.expireAll();
- stopRequestProcessing();
- }
-
- /**
- * Stops the background thread that processes updates and cancels any
- * pending requests to start it.
- */
- private synchronized void stopRequestProcessing() {
- // Remove any pending requests to start the processing thread.
- mHandler.removeMessages(START_THREAD);
- if (mContactInfoQueryThread != null) {
- // Stop the thread; we are finished with it.
- mContactInfoQueryThread.stopProcessing();
- mContactInfoQueryThread.interrupt();
- mContactInfoQueryThread = null;
- }
- }
-
- /**
- * Enqueues a request to look up the contact details for the given phone number.
- * <p>
- * It also provides the current contact info stored in the call log for this number.
- * <p>
- * If the {@code immediate} parameter is true, it will start immediately the thread that looks
- * up the contact information (if it has not been already started). Otherwise, it will be
- * started with a delay. See {@link #START_PROCESSING_REQUESTS_DELAY_MILLIS}.
- */
- protected void enqueueRequest(String number, String countryIso, ContactInfo callLogInfo,
- boolean immediate) {
- ContactInfoRequest request = new ContactInfoRequest(number, countryIso, callLogInfo);
- synchronized (mRequests) {
- if (!mRequests.contains(request)) {
- mRequests.add(request);
- mRequests.notifyAll();
- }
- }
- if (immediate) {
- startRequestProcessing();
- }
- }
-
- /**
- * Checks whether the contact info from the call log matches the one from the contacts db.
- */
- private boolean callLogInfoMatches(ContactInfo callLogInfo, ContactInfo info) {
- // The call log only contains a subset of the fields in the contacts db. Only check those.
- return TextUtils.equals(callLogInfo.name, info.name)
- && callLogInfo.type == info.type
- && TextUtils.equals(callLogInfo.label, info.label);
- }
-
- private volatile boolean mRequestProcessingDisabled = false;
-
- /**
- * Sets whether processing of requests for contact details should be enabled.
- */
- public void disableRequestProcessing() {
- mRequestProcessingDisabled = true;
- }
-
- @VisibleForTesting
- public void injectContactInfoForTest(
- String number, String countryIso, ContactInfo contactInfo) {
- NumberWithCountryIso numberCountryIso = new NumberWithCountryIso(number, countryIso);
- mCache.put(numberCountryIso, contactInfo);
- }
-}
diff --git a/src/com/android/dialer/contactinfo/ContactInfoRequest.java b/src/com/android/dialer/contactinfo/ContactInfoRequest.java
deleted file mode 100644
index ec5c1198e..000000000
--- a/src/com/android/dialer/contactinfo/ContactInfoRequest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.dialer.contactinfo;
-
-import android.text.TextUtils;
-
-import com.android.dialer.calllog.ContactInfo;
-import com.google.common.base.Objects;
-
-/**
- * A request for contact details for the given number, used by the ContactInfoCache.
- */
-public final class ContactInfoRequest {
- /** The number to look-up. */
- public final String number;
- /** The country in which a call to or from this number was placed or received. */
- public final String countryIso;
- /** The cached contact information stored in the call log. */
- public final ContactInfo callLogInfo;
-
- public ContactInfoRequest(String number, String countryIso, ContactInfo callLogInfo) {
- this.number = number;
- this.countryIso = countryIso;
- this.callLogInfo = callLogInfo;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (!(obj instanceof ContactInfoRequest)) return false;
-
- ContactInfoRequest other = (ContactInfoRequest) obj;
-
- if (!TextUtils.equals(number, other.number)) return false;
- if (!TextUtils.equals(countryIso, other.countryIso)) return false;
- if (!Objects.equal(callLogInfo, other.callLogInfo)) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((callLogInfo == null) ? 0 : callLogInfo.hashCode());
- result = prime * result + ((countryIso == null) ? 0 : countryIso.hashCode());
- result = prime * result + ((number == null) ? 0 : number.hashCode());
- return result;
- }
-}
diff --git a/src/com/android/dialer/contactinfo/ContactPhotoLoader.java b/src/com/android/dialer/contactinfo/ContactPhotoLoader.java
deleted file mode 100644
index f36c438f6..000000000
--- a/src/com/android/dialer/contactinfo/ContactPhotoLoader.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * 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.dialer.contactinfo;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.provider.MediaStore;
-import android.support.annotation.Nullable;
-import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
-import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
-import android.util.Log;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.lettertiles.LetterTileDrawable;
-import com.android.dialer.R;
-import com.android.dialer.calllog.ContactInfo;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.util.Assert;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-
-import java.io.IOException;
-/**
- * Class to create the appropriate contact icon from a ContactInfo.
- * This class is for synchronous, blocking calls to generate bitmaps, while
- * ContactCommons.ContactPhotoManager is to cache, manage and update a ImageView asynchronously.
- */
-public class ContactPhotoLoader {
-
- private static final String TAG = "ContactPhotoLoader";
-
- private final Context mContext;
- private final ContactInfo mContactInfo;
-
- public ContactPhotoLoader(Context context, ContactInfo contactInfo) {
- mContext = Preconditions.checkNotNull(context);
- mContactInfo = Preconditions.checkNotNull(contactInfo);
- }
-
- /**
- * Create a contact photo icon bitmap appropriate for the ContactInfo.
- */
- public Bitmap loadPhotoIcon() {
- Assert.assertNotUiThread("ContactPhotoLoader#loadPhotoIcon called on UI thread");
- int photoSize = mContext.getResources().getDimensionPixelSize(R.dimen.contact_photo_size);
- return drawableToBitmap(getIcon(), photoSize, photoSize);
- }
-
- @VisibleForTesting
- Drawable getIcon() {
- Drawable drawable = createPhotoIconDrawable();
- if (drawable == null) {
- drawable = createLetterTileDrawable();
- }
- return drawable;
- }
-
- /**
- * @return a {@link Drawable} of circular photo icon if the photo can be loaded, {@code null}
- * otherwise.
- */
- @Nullable
- private Drawable createPhotoIconDrawable() {
- if (mContactInfo.photoUri == null) {
- return null;
- }
- try {
- Bitmap bitmap = MediaStore.Images.Media.getBitmap(mContext.getContentResolver(),
- mContactInfo.photoUri);
- final RoundedBitmapDrawable drawable =
- RoundedBitmapDrawableFactory.create(mContext.getResources(), bitmap);
- drawable.setAntiAlias(true);
- drawable.setCornerRadius(bitmap.getHeight() / 2);
- return drawable;
- } catch (IOException e) {
- Log.e(TAG, e.toString());
- return null;
- }
- }
-
- /**
- * @return a {@link LetterTileDrawable} based on the ContactInfo.
- */
- private Drawable createLetterTileDrawable() {
- LetterTileDrawable drawable = new LetterTileDrawable(mContext.getResources());
- drawable.setIsCircular(true);
- ContactInfoHelper helper =
- new ContactInfoHelper(mContext, GeoUtil.getCurrentCountryIso(mContext));
- if (helper.isBusiness(mContactInfo.sourceType)) {
- drawable.setContactType(LetterTileDrawable.TYPE_BUSINESS);
- }
- drawable.setLetterAndColorFromContactDetails(mContactInfo.name, mContactInfo.lookupKey);
- return drawable;
- }
-
- private static Bitmap drawableToBitmap(Drawable drawable, int width, int height) {
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- drawable.draw(canvas);
- return bitmap;
- }
-
-}
diff --git a/src/com/android/dialer/contactinfo/NumberWithCountryIso.java b/src/com/android/dialer/contactinfo/NumberWithCountryIso.java
deleted file mode 100644
index 1383fb7e9..000000000
--- a/src/com/android/dialer/contactinfo/NumberWithCountryIso.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.dialer.contactinfo;
-
-import android.text.TextUtils;
-
-/**
- * Stores a phone number of a call with the country code where it originally occurred. This object
- * is used as a key in the {@code ContactInfoCache}.
- *
- * The country does not necessarily specify the country of the phone number itself, but rather
- * it is the country in which the user was in when the call was placed or received.
- */
-public final class NumberWithCountryIso {
- public final String number;
- public final String countryIso;
-
- public NumberWithCountryIso(String number, String countryIso) {
- this.number = number;
- this.countryIso = countryIso;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == null) return false;
- if (!(o instanceof NumberWithCountryIso)) return false;
- NumberWithCountryIso other = (NumberWithCountryIso) o;
- return TextUtils.equals(number, other.number)
- && TextUtils.equals(countryIso, other.countryIso);
- }
-
- @Override
- public int hashCode() {
- int numberHashCode = number == null ? 0 : number.hashCode();
- int countryHashCode = countryIso == null ? 0 : countryIso.hashCode();
-
- return numberHashCode ^ countryHashCode;
- }
-}
diff --git a/src/com/android/dialer/database/DialerDatabaseHelper.java b/src/com/android/dialer/database/DialerDatabaseHelper.java
deleted file mode 100644
index 5edfb270d..000000000
--- a/src/com/android/dialer/database/DialerDatabaseHelper.java
+++ /dev/null
@@ -1,1169 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.database;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteStatement;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.provider.BaseColumns;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.Directory;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.common.util.StopWatch;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.dialer.database.VoicemailArchiveContract.VoicemailArchive;
-import com.android.dialer.R;
-import com.android.dialer.dialpad.SmartDialNameMatcher;
-import com.android.dialer.dialpad.SmartDialPrefix;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Database helper for smart dial. Designed as a singleton to make sure there is
- * only one access point to the database. Provides methods to maintain, update,
- * and query the database.
- */
-public class DialerDatabaseHelper extends SQLiteOpenHelper {
- private static final String TAG = "DialerDatabaseHelper";
- private static final boolean DEBUG = false;
- private boolean mIsTestInstance = false;
-
- private static DialerDatabaseHelper sSingleton = null;
-
- private static final Object mLock = new Object();
- private static final AtomicBoolean sInUpdate = new AtomicBoolean(false);
- private final Context mContext;
-
- /**
- * SmartDial DB version ranges:
- * <pre>
- * 0-98 KitKat
- * </pre>
- */
- public static final int DATABASE_VERSION = 9;
- public static final String DATABASE_NAME = "dialer.db";
-
- /**
- * Saves the last update time of smart dial databases to shared preferences.
- */
- private static final String DATABASE_LAST_CREATED_SHARED_PREF = "com.android.dialer";
- private static final String LAST_UPDATED_MILLIS = "last_updated_millis";
- private static final String DATABASE_VERSION_PROPERTY = "database_version";
-
- private static final int MAX_ENTRIES = 20;
-
- public interface Tables {
- /** Saves a list of numbers to be blocked.*/
- static final String FILTERED_NUMBER_TABLE = "filtered_numbers_table";
- /** Saves the necessary smart dial information of all contacts. */
- static final String SMARTDIAL_TABLE = "smartdial_table";
- /** Saves all possible prefixes to refer to a contacts.*/
- static final String PREFIX_TABLE = "prefix_table";
- /** Saves all archived voicemail information. */
- static final String VOICEMAIL_ARCHIVE_TABLE = "voicemail_archive_table";
- /** Database properties for internal use */
- static final String PROPERTIES = "properties";
- }
-
- public static final Uri SMART_DIAL_UPDATED_URI =
- Uri.parse("content://com.android.dialer/smart_dial_updated");
-
- public interface SmartDialDbColumns {
- static final String _ID = "id";
- static final String DATA_ID = "data_id";
- static final String NUMBER = "phone_number";
- static final String CONTACT_ID = "contact_id";
- static final String LOOKUP_KEY = "lookup_key";
- static final String DISPLAY_NAME_PRIMARY = "display_name";
- static final String PHOTO_ID = "photo_id";
- static final String LAST_TIME_USED = "last_time_used";
- static final String TIMES_USED = "times_used";
- static final String STARRED = "starred";
- static final String IS_SUPER_PRIMARY = "is_super_primary";
- static final String IN_VISIBLE_GROUP = "in_visible_group";
- static final String IS_PRIMARY = "is_primary";
- static final String CARRIER_PRESENCE = "carrier_presence";
- static final String LAST_SMARTDIAL_UPDATE_TIME = "last_smartdial_update_time";
- }
-
- public static interface PrefixColumns extends BaseColumns {
- static final String PREFIX = "prefix";
- static final String CONTACT_ID = "contact_id";
- }
-
- public interface PropertiesColumns {
- String PROPERTY_KEY = "property_key";
- String PROPERTY_VALUE = "property_value";
- }
-
- /** Query options for querying the contact database.*/
- public static interface PhoneQuery {
- static final Uri URI = Phone.CONTENT_URI.buildUpon().
- appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
- String.valueOf(Directory.DEFAULT)).
- appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true").
- build();
-
- static final String[] PROJECTION = new String[] {
- Phone._ID, // 0
- Phone.TYPE, // 1
- Phone.LABEL, // 2
- Phone.NUMBER, // 3
- Phone.CONTACT_ID, // 4
- Phone.LOOKUP_KEY, // 5
- Phone.DISPLAY_NAME_PRIMARY, // 6
- Phone.PHOTO_ID, // 7
- Data.LAST_TIME_USED, // 8
- Data.TIMES_USED, // 9
- Contacts.STARRED, // 10
- Data.IS_SUPER_PRIMARY, // 11
- Contacts.IN_VISIBLE_GROUP, // 12
- Data.IS_PRIMARY, // 13
- Data.CARRIER_PRESENCE, // 14
- };
-
- static final int PHONE_ID = 0;
- static final int PHONE_TYPE = 1;
- static final int PHONE_LABEL = 2;
- static final int PHONE_NUMBER = 3;
- static final int PHONE_CONTACT_ID = 4;
- static final int PHONE_LOOKUP_KEY = 5;
- static final int PHONE_DISPLAY_NAME = 6;
- static final int PHONE_PHOTO_ID = 7;
- static final int PHONE_LAST_TIME_USED = 8;
- static final int PHONE_TIMES_USED = 9;
- static final int PHONE_STARRED = 10;
- static final int PHONE_IS_SUPER_PRIMARY = 11;
- static final int PHONE_IN_VISIBLE_GROUP = 12;
- static final int PHONE_IS_PRIMARY = 13;
- static final int PHONE_CARRIER_PRESENCE = 14;
-
- /** Selects only rows that have been updated after a certain time stamp.*/
- static final String SELECT_UPDATED_CLAUSE =
- Phone.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?";
-
- /** Ignores contacts that have an unreasonably long lookup key. These are likely to be
- * the result of multiple (> 50) merged raw contacts, and are likely to cause
- * OutOfMemoryExceptions within SQLite, or cause memory allocation problems later on
- * when iterating through the cursor set (see b/13133579)
- */
- static final String SELECT_IGNORE_LOOKUP_KEY_TOO_LONG_CLAUSE =
- "length(" + Phone.LOOKUP_KEY + ") < 1000";
-
- static final String SELECTION = SELECT_UPDATED_CLAUSE + " AND " +
- SELECT_IGNORE_LOOKUP_KEY_TOO_LONG_CLAUSE;
- }
-
- /**
- * Query for all contacts that have been updated since the last time the smart dial database
- * was updated.
- */
- public static interface UpdatedContactQuery {
- static final Uri URI = ContactsContract.Contacts.CONTENT_URI;
-
- static final String[] PROJECTION = new String[] {
- ContactsContract.Contacts._ID // 0
- };
-
- static final int UPDATED_CONTACT_ID = 0;
-
- static final String SELECT_UPDATED_CLAUSE =
- ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?";
- }
-
- /** Query options for querying the deleted contact database.*/
- public static interface DeleteContactQuery {
- static final Uri URI = ContactsContract.DeletedContacts.CONTENT_URI;
-
- static final String[] PROJECTION = new String[] {
- ContactsContract.DeletedContacts.CONTACT_ID, // 0
- ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP, // 1
- };
-
- static final int DELETED_CONTACT_ID = 0;
- static final int DELECTED_TIMESTAMP = 1;
-
- /** Selects only rows that have been deleted after a certain time stamp.*/
- public static final String SELECT_UPDATED_CLAUSE =
- ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP + " > ?";
- }
-
- /**
- * Gets the sorting order for the smartdial table. This computes a SQL "ORDER BY" argument by
- * composing contact status and recent contact details together.
- */
- private static interface SmartDialSortingOrder {
- /** Current contacts - those contacted within the last 3 days (in milliseconds) */
- static final long LAST_TIME_USED_CURRENT_MS = 3L * 24 * 60 * 60 * 1000;
- /** Recent contacts - those contacted within the last 30 days (in milliseconds) */
- static final long LAST_TIME_USED_RECENT_MS = 30L * 24 * 60 * 60 * 1000;
-
- /** Time since last contact. */
- static final String TIME_SINCE_LAST_USED_MS = "( ?1 - " +
- Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.LAST_TIME_USED + ")";
-
- /** Contacts that have been used in the past 3 days rank higher than contacts that have
- * been used in the past 30 days, which rank higher than contacts that have not been used
- * in recent 30 days.
- */
- static final String SORT_BY_DATA_USAGE =
- "(CASE WHEN " + TIME_SINCE_LAST_USED_MS + " < " + LAST_TIME_USED_CURRENT_MS +
- " THEN 0 " +
- " WHEN " + TIME_SINCE_LAST_USED_MS + " < " + LAST_TIME_USED_RECENT_MS +
- " THEN 1 " +
- " ELSE 2 END)";
-
- /** This sort order is similar to that used by the ContactsProvider when returning a list
- * of frequently called contacts.
- */
- static final String SORT_ORDER =
- Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.STARRED + " DESC, "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.IS_SUPER_PRIMARY + " DESC, "
- + SORT_BY_DATA_USAGE + ", "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.TIMES_USED + " DESC, "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.IN_VISIBLE_GROUP + " DESC, "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.CONTACT_ID + ", "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.IS_PRIMARY + " DESC";
- }
-
- /**
- * Simple data format for a contact, containing only information needed for showing up in
- * smart dial interface.
- */
- public static class ContactNumber {
- public final long id;
- public final long dataId;
- public final String displayName;
- public final String phoneNumber;
- public final String lookupKey;
- public final long photoId;
- public final int carrierPresence;
-
- public ContactNumber(long id, long dataID, String displayName, String phoneNumber,
- String lookupKey, long photoId, int carrierPresence) {
- this.dataId = dataID;
- this.id = id;
- this.displayName = displayName;
- this.phoneNumber = phoneNumber;
- this.lookupKey = lookupKey;
- this.photoId = photoId;
- this.carrierPresence = carrierPresence;
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(id, dataId, displayName, phoneNumber, lookupKey, photoId,
- carrierPresence);
- }
-
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- }
- if (object instanceof ContactNumber) {
- final ContactNumber that = (ContactNumber) object;
- return Objects.equal(this.id, that.id)
- && Objects.equal(this.dataId, that.dataId)
- && Objects.equal(this.displayName, that.displayName)
- && Objects.equal(this.phoneNumber, that.phoneNumber)
- && Objects.equal(this.lookupKey, that.lookupKey)
- && Objects.equal(this.photoId, that.photoId)
- && Objects.equal(this.carrierPresence, that.carrierPresence);
- }
- return false;
- }
- }
-
- /**
- * Data format for finding duplicated contacts.
- */
- private class ContactMatch {
- private final String lookupKey;
- private final long id;
-
- public ContactMatch(String lookupKey, long id) {
- this.lookupKey = lookupKey;
- this.id = id;
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(lookupKey, id);
- }
-
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- }
- if (object instanceof ContactMatch) {
- final ContactMatch that = (ContactMatch) object;
- return Objects.equal(this.lookupKey, that.lookupKey)
- && Objects.equal(this.id, that.id);
- }
- return false;
- }
- }
-
- /**
- * Access function to get the singleton instance of DialerDatabaseHelper.
- */
- public static synchronized DialerDatabaseHelper getInstance(Context context) {
- if (DEBUG) {
- Log.v(TAG, "Getting Instance");
- }
- if (sSingleton == null) {
- // Use application context instead of activity context because this is a singleton,
- // and we don't want to leak the activity if the activity is not running but the
- // dialer database helper is still doing work.
- sSingleton = new DialerDatabaseHelper(context.getApplicationContext(),
- DATABASE_NAME);
- }
- return sSingleton;
- }
-
- /**
- * Returns a new instance for unit tests. The database will be created in memory.
- */
- @VisibleForTesting
- static DialerDatabaseHelper getNewInstanceForTest(Context context) {
- return new DialerDatabaseHelper(context, null, true);
- }
-
- protected DialerDatabaseHelper(Context context, String databaseName, boolean isTestInstance) {
- this(context, databaseName, DATABASE_VERSION);
- mIsTestInstance = isTestInstance;
- }
-
- protected DialerDatabaseHelper(Context context, String databaseName) {
- this(context, databaseName, DATABASE_VERSION);
- }
-
- protected DialerDatabaseHelper(Context context, String databaseName, int dbVersion) {
- super(context, databaseName, null, dbVersion);
- mContext = Preconditions.checkNotNull(context, "Context must not be null");
- }
-
- /**
- * Creates tables in the database when database is created for the first time.
- *
- * @param db The database.
- */
- @Override
- public void onCreate(SQLiteDatabase db) {
- setupTables(db);
- }
-
- private void setupTables(SQLiteDatabase db) {
- dropTables(db);
- db.execSQL("CREATE TABLE " + Tables.SMARTDIAL_TABLE + " ("
- + SmartDialDbColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + SmartDialDbColumns.DATA_ID + " INTEGER, "
- + SmartDialDbColumns.NUMBER + " TEXT,"
- + SmartDialDbColumns.CONTACT_ID + " INTEGER,"
- + SmartDialDbColumns.LOOKUP_KEY + " TEXT,"
- + SmartDialDbColumns.DISPLAY_NAME_PRIMARY + " TEXT, "
- + SmartDialDbColumns.PHOTO_ID + " INTEGER, "
- + SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + " LONG, "
- + SmartDialDbColumns.LAST_TIME_USED + " LONG, "
- + SmartDialDbColumns.TIMES_USED + " INTEGER, "
- + SmartDialDbColumns.STARRED + " INTEGER, "
- + SmartDialDbColumns.IS_SUPER_PRIMARY + " INTEGER, "
- + SmartDialDbColumns.IN_VISIBLE_GROUP + " INTEGER, "
- + SmartDialDbColumns.IS_PRIMARY + " INTEGER, "
- + SmartDialDbColumns.CARRIER_PRESENCE + " INTEGER NOT NULL DEFAULT 0"
- + ");");
-
- db.execSQL("CREATE TABLE " + Tables.PREFIX_TABLE + " ("
- + PrefixColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + PrefixColumns.PREFIX + " TEXT COLLATE NOCASE, "
- + PrefixColumns.CONTACT_ID + " INTEGER"
- + ");");
-
- db.execSQL("CREATE TABLE " + Tables.PROPERTIES + " ("
- + PropertiesColumns.PROPERTY_KEY + " TEXT PRIMARY KEY, "
- + PropertiesColumns.PROPERTY_VALUE + " TEXT "
- + ");");
-
- // This will need to also be updated in setupTablesForFilteredNumberTest and onUpgrade.
- // Hardcoded so we know on glance what columns are updated in setupTables,
- // and to be able to guarantee the state of the DB at each upgrade step.
- db.execSQL("CREATE TABLE " + Tables.FILTERED_NUMBER_TABLE + " ("
- + FilteredNumberColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + FilteredNumberColumns.NORMALIZED_NUMBER + " TEXT UNIQUE,"
- + FilteredNumberColumns.NUMBER + " TEXT,"
- + FilteredNumberColumns.COUNTRY_ISO + " TEXT,"
- + FilteredNumberColumns.TIMES_FILTERED + " INTEGER,"
- + FilteredNumberColumns.LAST_TIME_FILTERED + " LONG,"
- + FilteredNumberColumns.CREATION_TIME + " LONG,"
- + FilteredNumberColumns.TYPE + " INTEGER,"
- + FilteredNumberColumns.SOURCE + " INTEGER"
- + ");");
-
- createVoicemailArchiveTable(db);
- setProperty(db, DATABASE_VERSION_PROPERTY, String.valueOf(DATABASE_VERSION));
- if (!mIsTestInstance) {
- resetSmartDialLastUpdatedTime();
- }
- }
-
- public void dropTables(SQLiteDatabase db) {
- db.execSQL("DROP TABLE IF EXISTS " + Tables.PREFIX_TABLE);
- db.execSQL("DROP TABLE IF EXISTS " + Tables.SMARTDIAL_TABLE);
- db.execSQL("DROP TABLE IF EXISTS " + Tables.PROPERTIES);
- db.execSQL("DROP TABLE IF EXISTS " + Tables.FILTERED_NUMBER_TABLE);
- db.execSQL("DROP TABLE IF EXISTS " + Tables.VOICEMAIL_ARCHIVE_TABLE);
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldNumber, int newNumber) {
- // Disregard the old version and new versions provided by SQLiteOpenHelper, we will read
- // our own from the database.
-
- int oldVersion;
-
- oldVersion = getPropertyAsInt(db, DATABASE_VERSION_PROPERTY, 0);
-
- if (oldVersion == 0) {
- Log.e(TAG, "Malformed database version..recreating database");
- }
-
- if (oldVersion < 4) {
- setupTables(db);
- return;
- }
-
- if (oldVersion < 7) {
- db.execSQL("DROP TABLE IF EXISTS " + Tables.FILTERED_NUMBER_TABLE);
- db.execSQL("CREATE TABLE " + Tables.FILTERED_NUMBER_TABLE + " ("
- + FilteredNumberColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + FilteredNumberColumns.NORMALIZED_NUMBER + " TEXT UNIQUE,"
- + FilteredNumberColumns.NUMBER + " TEXT,"
- + FilteredNumberColumns.COUNTRY_ISO + " TEXT,"
- + FilteredNumberColumns.TIMES_FILTERED + " INTEGER,"
- + FilteredNumberColumns.LAST_TIME_FILTERED + " LONG,"
- + FilteredNumberColumns.CREATION_TIME + " LONG,"
- + FilteredNumberColumns.TYPE + " INTEGER,"
- + FilteredNumberColumns.SOURCE + " INTEGER"
- + ");");
- oldVersion = 7;
- }
-
- if (oldVersion < 8) {
- upgradeToVersion8(db);
- oldVersion = 8;
- }
-
- if (oldVersion < 9) {
- db.execSQL("DROP TABLE IF EXISTS " + Tables.VOICEMAIL_ARCHIVE_TABLE);
- createVoicemailArchiveTable(db);
- oldVersion = 9;
- }
-
- if (oldVersion != DATABASE_VERSION) {
- throw new IllegalStateException(
- "error upgrading the database to version " + DATABASE_VERSION);
- }
-
- setProperty(db, DATABASE_VERSION_PROPERTY, String.valueOf(DATABASE_VERSION));
- }
-
- public void upgradeToVersion8(SQLiteDatabase db) {
- db.execSQL("ALTER TABLE smartdial_table ADD carrier_presence INTEGER NOT NULL DEFAULT 0");
- }
-
- /**
- * Stores a key-value pair in the {@link Tables#PROPERTIES} table.
- */
- public void setProperty(String key, String value) {
- setProperty(getWritableDatabase(), key, value);
- }
-
- public void setProperty(SQLiteDatabase db, String key, String value) {
- final ContentValues values = new ContentValues();
- values.put(PropertiesColumns.PROPERTY_KEY, key);
- values.put(PropertiesColumns.PROPERTY_VALUE, value);
- db.replace(Tables.PROPERTIES, null, values);
- }
-
- /**
- * Returns the value from the {@link Tables#PROPERTIES} table.
- */
- public String getProperty(String key, String defaultValue) {
- return getProperty(getReadableDatabase(), key, defaultValue);
- }
-
- public String getProperty(SQLiteDatabase db, String key, String defaultValue) {
- try {
- String value = null;
- final Cursor cursor = db.query(Tables.PROPERTIES,
- new String[] {PropertiesColumns.PROPERTY_VALUE},
- PropertiesColumns.PROPERTY_KEY + "=?",
- new String[] {key}, null, null, null);
- if (cursor != null) {
- try {
- if (cursor.moveToFirst()) {
- value = cursor.getString(0);
- }
- } finally {
- cursor.close();
- }
- }
- return value != null ? value : defaultValue;
- } catch (SQLiteException e) {
- return defaultValue;
- }
- }
-
- public int getPropertyAsInt(SQLiteDatabase db, String key, int defaultValue) {
- final String stored = getProperty(db, key, "");
- try {
- return Integer.parseInt(stored);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
-
- private void resetSmartDialLastUpdatedTime() {
- final SharedPreferences databaseLastUpdateSharedPref = mContext.getSharedPreferences(
- DATABASE_LAST_CREATED_SHARED_PREF, Context.MODE_PRIVATE);
- final SharedPreferences.Editor editor = databaseLastUpdateSharedPref.edit();
- editor.putLong(LAST_UPDATED_MILLIS, 0);
- editor.commit();
- }
-
- /**
- * Starts the database upgrade process in the background.
- */
- public void startSmartDialUpdateThread() {
- if (PermissionsUtil.hasContactsPermissions(mContext)) {
- new SmartDialUpdateAsyncTask().execute();
- }
- }
-
- private class SmartDialUpdateAsyncTask extends AsyncTask {
- @Override
- protected Object doInBackground(Object[] objects) {
- if (DEBUG) {
- Log.v(TAG, "Updating database");
- }
- updateSmartDialDatabase();
- return null;
- }
-
- @Override
- protected void onCancelled() {
- if (DEBUG) {
- Log.v(TAG, "Updating Cancelled");
- }
- super.onCancelled();
- }
-
- @Override
- protected void onPostExecute(Object o) {
- if (DEBUG) {
- Log.v(TAG, "Updating Finished");
- }
- super.onPostExecute(o);
- }
- }
- /**
- * Removes rows in the smartdial database that matches the contacts that have been deleted
- * by other apps since last update.
- *
- * @param db Database to operate on.
- * @param deletedContactCursor Cursor containing rows of deleted contacts
- */
- @VisibleForTesting
- void removeDeletedContacts(SQLiteDatabase db, Cursor deletedContactCursor) {
- if (deletedContactCursor == null) {
- return;
- }
-
- db.beginTransaction();
- try {
- while (deletedContactCursor.moveToNext()) {
- final Long deleteContactId =
- deletedContactCursor.getLong(DeleteContactQuery.DELETED_CONTACT_ID);
- db.delete(Tables.SMARTDIAL_TABLE,
- SmartDialDbColumns.CONTACT_ID + "=" + deleteContactId, null);
- db.delete(Tables.PREFIX_TABLE,
- PrefixColumns.CONTACT_ID + "=" + deleteContactId, null);
- }
-
- db.setTransactionSuccessful();
- } finally {
- deletedContactCursor.close();
- db.endTransaction();
- }
- }
-
- private Cursor getDeletedContactCursor(String lastUpdateMillis) {
- return mContext.getContentResolver().query(
- DeleteContactQuery.URI,
- DeleteContactQuery.PROJECTION,
- DeleteContactQuery.SELECT_UPDATED_CLAUSE,
- new String[] {lastUpdateMillis},
- null);
- }
-
- /**
- * Removes potentially corrupted entries in the database. These contacts may be added before
- * the previous instance of the dialer was destroyed for some reason. For data integrity, we
- * delete all of them.
-
- * @param db Database pointer to the dialer database.
- * @param last_update_time Time stamp of last successful update of the dialer database.
- */
- private void removePotentiallyCorruptedContacts(SQLiteDatabase db, String last_update_time) {
- db.delete(Tables.PREFIX_TABLE,
- PrefixColumns.CONTACT_ID + " IN " +
- "(SELECT " + SmartDialDbColumns.CONTACT_ID + " FROM " + Tables.SMARTDIAL_TABLE +
- " WHERE " + SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + " > " +
- last_update_time + ")",
- null);
- db.delete(Tables.SMARTDIAL_TABLE,
- SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + " > " + last_update_time, null);
- }
-
- /**
- * All columns excluding MIME_TYPE, _DATA, ARCHIVED, SERVER_ID, are the same as
- * the columns in the {@link android.provider.CallLog.Calls} table.
- *
- * @param db Database pointer to the dialer database.
- */
- private void createVoicemailArchiveTable(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE " + Tables.VOICEMAIL_ARCHIVE_TABLE + " ("
- + VoicemailArchive._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + VoicemailArchive.NUMBER + " TEXT,"
- + VoicemailArchive.DATE + " LONG,"
- + VoicemailArchive.DURATION + " LONG,"
- + VoicemailArchive.MIME_TYPE + " TEXT,"
- + VoicemailArchive.COUNTRY_ISO + " TEXT,"
- + VoicemailArchive._DATA + " TEXT,"
- + VoicemailArchive.GEOCODED_LOCATION + " TEXT,"
- + VoicemailArchive.CACHED_NAME + " TEXT,"
- + VoicemailArchive.CACHED_NUMBER_TYPE + " INTEGER,"
- + VoicemailArchive.CACHED_NUMBER_LABEL + " TEXT,"
- + VoicemailArchive.CACHED_LOOKUP_URI + " TEXT,"
- + VoicemailArchive.CACHED_MATCHED_NUMBER + " TEXT,"
- + VoicemailArchive.CACHED_NORMALIZED_NUMBER + " TEXT,"
- + VoicemailArchive.CACHED_PHOTO_ID + " LONG,"
- + VoicemailArchive.CACHED_FORMATTED_NUMBER + " TEXT,"
- + VoicemailArchive.ARCHIVED + " INTEGER,"
- + VoicemailArchive.NUMBER_PRESENTATION + " INTEGER,"
- + VoicemailArchive.ACCOUNT_COMPONENT_NAME + " TEXT,"
- + VoicemailArchive.ACCOUNT_ID + " TEXT,"
- + VoicemailArchive.FEATURES + " INTEGER,"
- + VoicemailArchive.SERVER_ID + " INTEGER,"
- + VoicemailArchive.TRANSCRIPTION + " TEXT,"
- + VoicemailArchive.CACHED_PHOTO_URI + " TEXT"
- + ");");
- }
-
- /**
- * Removes all entries in the smartdial contact database.
- */
- @VisibleForTesting
- void removeAllContacts(SQLiteDatabase db) {
- db.delete(Tables.SMARTDIAL_TABLE, null, null);
- db.delete(Tables.PREFIX_TABLE, null, null);
- }
-
- /**
- * Counts number of rows of the prefix table.
- */
- @VisibleForTesting
- int countPrefixTableRows(SQLiteDatabase db) {
- return (int)DatabaseUtils.longForQuery(db, "SELECT COUNT(1) FROM " + Tables.PREFIX_TABLE,
- null);
- }
-
- /**
- * Removes rows in the smartdial database that matches updated contacts.
- *
- * @param db Database pointer to the smartdial database
- * @param updatedContactCursor Cursor pointing to the list of recently updated contacts.
- */
- @VisibleForTesting
- void removeUpdatedContacts(SQLiteDatabase db, Cursor updatedContactCursor) {
- db.beginTransaction();
- try {
- updatedContactCursor.moveToPosition(-1);
- while (updatedContactCursor.moveToNext()) {
- final Long contactId =
- updatedContactCursor.getLong(UpdatedContactQuery.UPDATED_CONTACT_ID);
-
- db.delete(Tables.SMARTDIAL_TABLE, SmartDialDbColumns.CONTACT_ID + "=" +
- contactId, null);
- db.delete(Tables.PREFIX_TABLE, PrefixColumns.CONTACT_ID + "=" +
- contactId, null);
- }
-
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- /**
- * Inserts updated contacts as rows to the smartdial table.
- *
- * @param db Database pointer to the smartdial database.
- * @param updatedContactCursor Cursor pointing to the list of recently updated contacts.
- * @param currentMillis Current time to be recorded in the smartdial table as update timestamp.
- */
- @VisibleForTesting
- protected void insertUpdatedContactsAndNumberPrefix(SQLiteDatabase db,
- Cursor updatedContactCursor, Long currentMillis) {
- db.beginTransaction();
- try {
- final String sqlInsert = "INSERT INTO " + Tables.SMARTDIAL_TABLE + " (" +
- SmartDialDbColumns.DATA_ID + ", " +
- SmartDialDbColumns.NUMBER + ", " +
- SmartDialDbColumns.CONTACT_ID + ", " +
- SmartDialDbColumns.LOOKUP_KEY + ", " +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " +
- SmartDialDbColumns.PHOTO_ID + ", " +
- SmartDialDbColumns.LAST_TIME_USED + ", " +
- SmartDialDbColumns.TIMES_USED + ", " +
- SmartDialDbColumns.STARRED + ", " +
- SmartDialDbColumns.IS_SUPER_PRIMARY + ", " +
- SmartDialDbColumns.IN_VISIBLE_GROUP+ ", " +
- SmartDialDbColumns.IS_PRIMARY + ", " +
- SmartDialDbColumns.CARRIER_PRESENCE + ", " +
- SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + ") " +
- " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
- final SQLiteStatement insert = db.compileStatement(sqlInsert);
-
- final String numberSqlInsert = "INSERT INTO " + Tables.PREFIX_TABLE + " (" +
- PrefixColumns.CONTACT_ID + ", " +
- PrefixColumns.PREFIX + ") " +
- " VALUES (?, ?)";
- final SQLiteStatement numberInsert = db.compileStatement(numberSqlInsert);
-
- updatedContactCursor.moveToPosition(-1);
- while (updatedContactCursor.moveToNext()) {
- insert.clearBindings();
-
- // Handle string columns which can possibly be null first. In the case of certain
- // null columns (due to malformed rows possibly inserted by third-party apps
- // or sync adapters), skip the phone number row.
- final String number = updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
- if (TextUtils.isEmpty(number)) {
- continue;
- } else {
- insert.bindString(2, number);
- }
-
- final String lookupKey = updatedContactCursor.getString(
- PhoneQuery.PHONE_LOOKUP_KEY);
- if (TextUtils.isEmpty(lookupKey)) {
- continue;
- } else {
- insert.bindString(4, lookupKey);
- }
-
- final String displayName = updatedContactCursor.getString(
- PhoneQuery.PHONE_DISPLAY_NAME);
- if (displayName == null) {
- insert.bindString(5, mContext.getResources().getString(R.string.missing_name));
- } else {
- insert.bindString(5, displayName);
- }
- insert.bindLong(1, updatedContactCursor.getLong(PhoneQuery.PHONE_ID));
- insert.bindLong(3, updatedContactCursor.getLong(PhoneQuery.PHONE_CONTACT_ID));
- insert.bindLong(6, updatedContactCursor.getLong(PhoneQuery.PHONE_PHOTO_ID));
- insert.bindLong(7, updatedContactCursor.getLong(PhoneQuery.PHONE_LAST_TIME_USED));
- insert.bindLong(8, updatedContactCursor.getInt(PhoneQuery.PHONE_TIMES_USED));
- insert.bindLong(9, updatedContactCursor.getInt(PhoneQuery.PHONE_STARRED));
- insert.bindLong(10, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_SUPER_PRIMARY));
- insert.bindLong(11, updatedContactCursor.getInt(PhoneQuery.PHONE_IN_VISIBLE_GROUP));
- insert.bindLong(12, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_PRIMARY));
- insert.bindLong(13, updatedContactCursor.getInt(PhoneQuery.PHONE_CARRIER_PRESENCE));
- insert.bindLong(14, currentMillis);
- insert.executeInsert();
- final String contactPhoneNumber =
- updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
- final ArrayList<String> numberPrefixes =
- SmartDialPrefix.parseToNumberTokens(contactPhoneNumber);
-
- for (String numberPrefix : numberPrefixes) {
- numberInsert.bindLong(1, updatedContactCursor.getLong(
- PhoneQuery.PHONE_CONTACT_ID));
- numberInsert.bindString(2, numberPrefix);
- numberInsert.executeInsert();
- numberInsert.clearBindings();
- }
- }
-
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- /**
- * Inserts prefixes of contact names to the prefix table.
- *
- * @param db Database pointer to the smartdial database.
- * @param nameCursor Cursor pointing to the list of distinct updated contacts.
- */
- @VisibleForTesting
- void insertNamePrefixes(SQLiteDatabase db, Cursor nameCursor) {
- final int columnIndexName = nameCursor.getColumnIndex(
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY);
- final int columnIndexContactId = nameCursor.getColumnIndex(SmartDialDbColumns.CONTACT_ID);
-
- db.beginTransaction();
- try {
- final String sqlInsert = "INSERT INTO " + Tables.PREFIX_TABLE + " (" +
- PrefixColumns.CONTACT_ID + ", " +
- PrefixColumns.PREFIX + ") " +
- " VALUES (?, ?)";
- final SQLiteStatement insert = db.compileStatement(sqlInsert);
-
- while (nameCursor.moveToNext()) {
- /** Computes a list of prefixes of a given contact name. */
- final ArrayList<String> namePrefixes =
- SmartDialPrefix.generateNamePrefixes(nameCursor.getString(columnIndexName));
-
- for (String namePrefix : namePrefixes) {
- insert.bindLong(1, nameCursor.getLong(columnIndexContactId));
- insert.bindString(2, namePrefix);
- insert.executeInsert();
- insert.clearBindings();
- }
- }
-
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- /**
- * Updates the smart dial and prefix database.
- * This method queries the Delta API to get changed contacts since last update, and updates the
- * records in smartdial database and prefix database accordingly.
- * It also queries the deleted contact database to remove newly deleted contacts since last
- * update.
- */
- public void updateSmartDialDatabase() {
- final SQLiteDatabase db = getWritableDatabase();
-
- synchronized(mLock) {
- if (DEBUG) {
- Log.v(TAG, "Starting to update database");
- }
- final StopWatch stopWatch = DEBUG ? StopWatch.start("Updating databases") : null;
-
- /** Gets the last update time on the database. */
- final SharedPreferences databaseLastUpdateSharedPref = mContext.getSharedPreferences(
- DATABASE_LAST_CREATED_SHARED_PREF, Context.MODE_PRIVATE);
- final String lastUpdateMillis = String.valueOf(
- databaseLastUpdateSharedPref.getLong(LAST_UPDATED_MILLIS, 0));
-
- if (DEBUG) {
- Log.v(TAG, "Last updated at " + lastUpdateMillis);
- }
-
- /** Sets the time after querying the database as the current update time. */
- final Long currentMillis = System.currentTimeMillis();
-
- if (DEBUG) {
- stopWatch.lap("Queried the Contacts database");
- }
-
- /** Prevents the app from reading the dialer database when updating. */
- sInUpdate.getAndSet(true);
-
- /** Removes contacts that have been deleted. */
- removeDeletedContacts(db, getDeletedContactCursor(lastUpdateMillis));
- removePotentiallyCorruptedContacts(db, lastUpdateMillis);
-
- if (DEBUG) {
- stopWatch.lap("Finished deleting deleted entries");
- }
-
- /** If the database did not exist before, jump through deletion as there is nothing
- * to delete.
- */
- if (!lastUpdateMillis.equals("0")) {
- /** Removes contacts that have been updated. Updated contact information will be
- * inserted later. Note that this has to use a separate result set from
- * updatePhoneCursor, since it is possible for a contact to be updated (e.g.
- * phone number deleted), but have no results show up in updatedPhoneCursor (since
- * all of its phone numbers have been deleted).
- */
- final Cursor updatedContactCursor = mContext.getContentResolver().query(
- UpdatedContactQuery.URI,
- UpdatedContactQuery.PROJECTION,
- UpdatedContactQuery.SELECT_UPDATED_CLAUSE,
- new String[] {lastUpdateMillis},
- null
- );
- if (updatedContactCursor == null) {
- Log.e(TAG, "SmartDial query received null for cursor");
- return;
- }
- try {
- removeUpdatedContacts(db, updatedContactCursor);
- } finally {
- updatedContactCursor.close();
- }
- if (DEBUG) {
- stopWatch.lap("Finished deleting entries belonging to updated contacts");
- }
- }
-
- /** Queries the contact database to get all phone numbers that have been updated since the last
- * update time.
- */
- final Cursor updatedPhoneCursor = mContext.getContentResolver().query(PhoneQuery.URI,
- PhoneQuery.PROJECTION, PhoneQuery.SELECTION,
- new String[]{lastUpdateMillis}, null);
- if (updatedPhoneCursor == null) {
- Log.e(TAG, "SmartDial query received null for cursor");
- return;
- }
-
- try {
- /** Inserts recently updated phone numbers to the smartdial database.*/
- insertUpdatedContactsAndNumberPrefix(db, updatedPhoneCursor, currentMillis);
- if (DEBUG) {
- stopWatch.lap("Finished building the smart dial table");
- }
- } finally {
- updatedPhoneCursor.close();
- }
-
- /** Gets a list of distinct contacts which have been updated, and adds the name prefixes
- * of these contacts to the prefix table.
- */
- final Cursor nameCursor = db.rawQuery(
- "SELECT DISTINCT " +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " + SmartDialDbColumns.CONTACT_ID +
- " FROM " + Tables.SMARTDIAL_TABLE +
- " WHERE " + SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME +
- " = " + Long.toString(currentMillis),
- new String[] {});
- if (nameCursor != null) {
- try {
- if (DEBUG) {
- stopWatch.lap("Queried the smart dial table for contact names");
- }
-
- /** Inserts prefixes of names into the prefix table.*/
- insertNamePrefixes(db, nameCursor);
- if (DEBUG) {
- stopWatch.lap("Finished building the name prefix table");
- }
- } finally {
- nameCursor.close();
- }
- }
-
- /** Creates index on contact_id for fast JOIN operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS smartdial_contact_id_index ON " +
- Tables.SMARTDIAL_TABLE + " (" + SmartDialDbColumns.CONTACT_ID + ");");
- /** Creates index on last_smartdial_update_time for fast SELECT operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS smartdial_last_update_index ON " +
- Tables.SMARTDIAL_TABLE + " (" +
- SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + ");");
- /** Creates index on sorting fields for fast sort operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS smartdial_sort_index ON " +
- Tables.SMARTDIAL_TABLE + " (" +
- SmartDialDbColumns.STARRED + ", " +
- SmartDialDbColumns.IS_SUPER_PRIMARY + ", " +
- SmartDialDbColumns.LAST_TIME_USED + ", " +
- SmartDialDbColumns.TIMES_USED + ", " +
- SmartDialDbColumns.IN_VISIBLE_GROUP + ", " +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " +
- SmartDialDbColumns.CONTACT_ID + ", " +
- SmartDialDbColumns.IS_PRIMARY +
- ");");
- /** Creates index on prefix for fast SELECT operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS nameprefix_index ON " +
- Tables.PREFIX_TABLE + " (" + PrefixColumns.PREFIX + ");");
- /** Creates index on contact_id for fast JOIN operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS nameprefix_contact_id_index ON " +
- Tables.PREFIX_TABLE + " (" + PrefixColumns.CONTACT_ID + ");");
-
- if (DEBUG) {
- stopWatch.lap(TAG + "Finished recreating index");
- }
-
- /** Updates the database index statistics.*/
- db.execSQL("ANALYZE " + Tables.SMARTDIAL_TABLE);
- db.execSQL("ANALYZE " + Tables.PREFIX_TABLE);
- db.execSQL("ANALYZE smartdial_contact_id_index");
- db.execSQL("ANALYZE smartdial_last_update_index");
- db.execSQL("ANALYZE nameprefix_index");
- db.execSQL("ANALYZE nameprefix_contact_id_index");
- if (DEBUG) {
- stopWatch.stopAndLog(TAG + "Finished updating index stats", 0);
- }
-
- sInUpdate.getAndSet(false);
-
- final SharedPreferences.Editor editor = databaseLastUpdateSharedPref.edit();
- editor.putLong(LAST_UPDATED_MILLIS, currentMillis);
- editor.commit();
-
- // Notify content observers that smart dial database has been updated.
- mContext.getContentResolver().notifyChange(SMART_DIAL_UPDATED_URI, null, false);
- }
- }
-
- /**
- * Returns a list of candidate contacts where the query is a prefix of the dialpad index of
- * the contact's name or phone number.
- *
- * @param query The prefix of a contact's dialpad index.
- * @return A list of top candidate contacts that will be suggested to user to match their input.
- */
- public ArrayList<ContactNumber> getLooseMatches(String query,
- SmartDialNameMatcher nameMatcher) {
- final boolean inUpdate = sInUpdate.get();
- if (inUpdate) {
- return Lists.newArrayList();
- }
-
- final SQLiteDatabase db = getReadableDatabase();
-
- /** Uses SQL query wildcard '%' to represent prefix matching.*/
- final String looseQuery = query + "%";
-
- final ArrayList<ContactNumber> result = Lists.newArrayList();
-
- final StopWatch stopWatch = DEBUG ? StopWatch.start(":Name Prefix query") : null;
-
- final String currentTimeStamp = Long.toString(System.currentTimeMillis());
-
- /** Queries the database to find contacts that have an index matching the query prefix. */
- final Cursor cursor = db.rawQuery("SELECT " +
- SmartDialDbColumns.DATA_ID + ", " +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " +
- SmartDialDbColumns.PHOTO_ID + ", " +
- SmartDialDbColumns.NUMBER + ", " +
- SmartDialDbColumns.CONTACT_ID + ", " +
- SmartDialDbColumns.LOOKUP_KEY + ", " +
- SmartDialDbColumns.CARRIER_PRESENCE +
- " FROM " + Tables.SMARTDIAL_TABLE + " WHERE " +
- SmartDialDbColumns.CONTACT_ID + " IN " +
- " (SELECT " + PrefixColumns.CONTACT_ID +
- " FROM " + Tables.PREFIX_TABLE +
- " WHERE " + Tables.PREFIX_TABLE + "." + PrefixColumns.PREFIX +
- " LIKE '" + looseQuery + "')" +
- " ORDER BY " + SmartDialSortingOrder.SORT_ORDER,
- new String[] {currentTimeStamp});
- if (cursor == null) {
- return result;
- }
- try {
- if (DEBUG) {
- stopWatch.lap("Prefix query completed");
- }
-
- /** Gets the column ID from the cursor.*/
- final int columnDataId = 0;
- final int columnDisplayNamePrimary = 1;
- final int columnPhotoId = 2;
- final int columnNumber = 3;
- final int columnId = 4;
- final int columnLookupKey = 5;
- final int columnCarrierPresence = 6;
- if (DEBUG) {
- stopWatch.lap("Found column IDs");
- }
-
- final Set<ContactMatch> duplicates = new HashSet<ContactMatch>();
- int counter = 0;
- if (DEBUG) {
- stopWatch.lap("Moved cursor to start");
- }
- /** Iterates the cursor to find top contact suggestions without duplication.*/
- while ((cursor.moveToNext()) && (counter < MAX_ENTRIES)) {
- final long dataID = cursor.getLong(columnDataId);
- final String displayName = cursor.getString(columnDisplayNamePrimary);
- final String phoneNumber = cursor.getString(columnNumber);
- final long id = cursor.getLong(columnId);
- final long photoId = cursor.getLong(columnPhotoId);
- final String lookupKey = cursor.getString(columnLookupKey);
- final int carrierPresence = cursor.getInt(columnCarrierPresence);
-
- /** If a contact already exists and another phone number of the contact is being
- * processed, skip the second instance.
- */
- final ContactMatch contactMatch = new ContactMatch(lookupKey, id);
- if (duplicates.contains(contactMatch)) {
- continue;
- }
-
- /**
- * If the contact has either the name or number that matches the query, add to the
- * result.
- */
- final boolean nameMatches = nameMatcher.matches(displayName);
- final boolean numberMatches =
- (nameMatcher.matchesNumber(phoneNumber, query) != null);
- if (nameMatches || numberMatches) {
- /** If a contact has not been added, add it to the result and the hash set.*/
- duplicates.add(contactMatch);
- result.add(new ContactNumber(id, dataID, displayName, phoneNumber, lookupKey,
- photoId, carrierPresence));
- counter++;
- if (DEBUG) {
- stopWatch.lap("Added one result: Name: " + displayName);
- }
- }
- }
-
- if (DEBUG) {
- stopWatch.stopAndLog(TAG + "Finished loading cursor", 0);
- }
- } finally {
- cursor.close();
- }
- return result;
- }
-}
diff --git a/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java b/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
deleted file mode 100644
index 68a2e85d5..000000000
--- a/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * 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.dialer.database;
-
-import android.content.AsyncQueryHandler;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabaseCorruptException;
-import android.net.Uri;
-import android.support.annotation.Nullable;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberTypes;
-
-public class FilteredNumberAsyncQueryHandler extends AsyncQueryHandler {
- private static final int NO_TOKEN = 0;
-
- public FilteredNumberAsyncQueryHandler(ContentResolver cr) {
- super(cr);
- }
-
- /**
- * Methods for FilteredNumberAsyncQueryHandler result returns.
- */
- private static abstract class Listener {
- protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- }
- protected void onInsertComplete(int token, Object cookie, Uri uri) {
- }
- protected void onUpdateComplete(int token, Object cookie, int result) {
- }
- protected void onDeleteComplete(int token, Object cookie, int result) {
- }
- }
-
- public interface OnCheckBlockedListener {
- /**
- * Invoked after querying if a number is blocked.
- * @param id The ID of the row if blocked, null otherwise.
- */
- void onCheckComplete(Integer id);
- }
-
- public interface OnBlockNumberListener {
- /**
- * Invoked after inserting a blocked number.
- * @param uri The uri of the newly created row.
- */
- void onBlockComplete(Uri uri);
- }
-
- public interface OnUnblockNumberListener {
- /**
- * Invoked after removing a blocked number
- * @param rows The number of rows affected (expected value 1).
- * @param values The deleted data (used for restoration).
- */
- void onUnblockComplete(int rows, ContentValues values);
- }
-
- public interface OnHasBlockedNumbersListener {
- /**
- * @param hasBlockedNumbers {@code true} if any blocked numbers are stored.
- * {@code false} otherwise.
- */
- void onHasBlockedNumbers(boolean hasBlockedNumbers);
- }
-
- @Override
- protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- if (cookie != null) {
- ((Listener) cookie).onQueryComplete(token, cookie, cursor);
- }
- }
-
- @Override
- protected void onInsertComplete(int token, Object cookie, Uri uri) {
- if (cookie != null) {
- ((Listener) cookie).onInsertComplete(token, cookie, uri);
- }
- }
-
- @Override
- protected void onUpdateComplete(int token, Object cookie, int result) {
- if (cookie != null) {
- ((Listener) cookie).onUpdateComplete(token, cookie, result);
- }
- }
-
- @Override
- protected void onDeleteComplete(int token, Object cookie, int result) {
- if (cookie != null) {
- ((Listener) cookie).onDeleteComplete(token, cookie, result);
- }
- }
-
- public final void incrementFilteredCount(Integer id) {
- // No concept of counts with new filtering
- if (FilteredNumberCompat.useNewFiltering()) {
- return;
- }
- startUpdate(NO_TOKEN, null,
- ContentUris.withAppendedId(FilteredNumber.CONTENT_URI_INCREMENT_FILTERED_COUNT, id),
- null, null, null);
- }
-
- public void hasBlockedNumbers(final OnHasBlockedNumbersListener listener) {
- startQuery(NO_TOKEN,
- new Listener() {
- @Override
- protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- listener.onHasBlockedNumbers(cursor != null && cursor.getCount() > 0);
- }
- },
- FilteredNumberCompat.getContentUri(null),
- new String[]{ FilteredNumberCompat.getIdColumnName() },
- FilteredNumberCompat.useNewFiltering() ? null : FilteredNumberColumns.TYPE
- + "=" + FilteredNumberTypes.BLOCKED_NUMBER,
- null,
- null);
- }
-
- /**
- * Check if this number has been blocked.
- *
- * @return {@code false} if the number was invalid and couldn't be checked,
- * {@code true} otherwise,
- */
- public boolean isBlockedNumber(
- final OnCheckBlockedListener listener, String number, String countryIso) {
- final String e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso);
- if (TextUtils.isEmpty(e164Number)) {
- return false;
- }
-
- startQuery(NO_TOKEN,
- new Listener() {
- @Override
- protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- /*
- * In the frameworking blocking, numbers can be blocked in both e164 format
- * and not, resulting in multiple rows being returned for this query. For
- * example, both '16502530000' and '6502530000' can exist at the same time
- * and will be returned by this query.
- */
- if (cursor == null || cursor.getCount() == 0) {
- listener.onCheckComplete(null);
- return;
- }
- cursor.moveToFirst();
- // New filtering doesn't have a concept of type
- if (!FilteredNumberCompat.useNewFiltering()
- && cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns.TYPE))
- != FilteredNumberTypes.BLOCKED_NUMBER) {
- listener.onCheckComplete(null);
- return;
- }
- listener.onCheckComplete(
- cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns._ID)));
- }
- },
- FilteredNumberCompat.getContentUri(null),
- FilteredNumberCompat.filter(new String[]{FilteredNumberCompat.getIdColumnName(),
- FilteredNumberCompat.getTypeColumnName()}),
- FilteredNumberCompat.getE164NumberColumnName() + " = ?",
- new String[]{e164Number},
- null);
-
- return true;
- }
-
- public void blockNumber(
- final OnBlockNumberListener listener, String number, @Nullable String countryIso) {
- blockNumber(listener, null, number, countryIso);
- }
-
- /**
- * Add a number manually blocked by the user.
- */
- public void blockNumber(
- final OnBlockNumberListener listener,
- @Nullable String normalizedNumber,
- String number,
- @Nullable String countryIso) {
- blockNumber(listener, FilteredNumberCompat.newBlockNumberContentValues(number,
- normalizedNumber, countryIso));
- }
-
- /**
- * Block a number with specified ContentValues. Can be manually added or a restored row
- * from performing the 'undo' action after unblocking.
- */
- public void blockNumber(final OnBlockNumberListener listener, ContentValues values) {
- startInsert(NO_TOKEN,
- new Listener() {
- @Override
- public void onInsertComplete(int token, Object cookie, Uri uri) {
- if (listener != null ) {
- listener.onBlockComplete(uri);
- }
- }
- }, FilteredNumberCompat.getContentUri(null), values);
- }
-
- /**
- * Unblocks the number with the given id.
- *
- * @param listener (optional) The {@link OnUnblockNumberListener} called after the number is
- * unblocked.
- * @param id The id of the number to unblock.
- */
- public void unblock(@Nullable final OnUnblockNumberListener listener, Integer id) {
- if (id == null) {
- throw new IllegalArgumentException("Null id passed into unblock");
- }
- unblock(listener, FilteredNumberCompat.getContentUri(id));
- }
-
- /**
- * Removes row from database.
- * @param listener (optional) The {@link OnUnblockNumberListener} called after the number is
- * unblocked.
- * @param uri The uri of row to remove, from
- * {@link FilteredNumberAsyncQueryHandler#blockNumber}.
- */
- public void unblock(@Nullable final OnUnblockNumberListener listener, final Uri uri) {
- startQuery(NO_TOKEN, new Listener() {
- @Override
- public void onQueryComplete(int token, Object cookie, Cursor cursor) {
- int rowsReturned = cursor == null ? 0 : cursor.getCount();
- if (rowsReturned != 1) {
- throw new SQLiteDatabaseCorruptException
- ("Returned " + rowsReturned + " rows for uri "
- + uri + "where 1 expected.");
- }
- cursor.moveToFirst();
- final ContentValues values = new ContentValues();
- DatabaseUtils.cursorRowToContentValues(cursor, values);
- values.remove(FilteredNumberCompat.getIdColumnName());
-
- startDelete(NO_TOKEN, new Listener() {
- @Override
- public void onDeleteComplete(int token, Object cookie, int result) {
- if (listener != null) {
- listener.onUnblockComplete(result, values);
- }
- }
- }, uri, null, null);
- }
- }, uri, null, null, null, null);
- }
-}
diff --git a/src/com/android/dialer/database/FilteredNumberContract.java b/src/com/android/dialer/database/FilteredNumberContract.java
deleted file mode 100644
index f3966816c..000000000
--- a/src/com/android/dialer/database/FilteredNumberContract.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * 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.dialer.database;
-
-import android.net.Uri;
-import android.provider.BaseColumns;
-
-import com.android.dialerbind.ObjectFactory;
-
-/**
- * <p>
- * The contract between the filtered number provider and applications. Contains
- * definitions for the supported URIs and columns.
- * Currently only accessible within Dialer.
- * </p>
- */
-public final class FilteredNumberContract {
-
- /** The authority for the filtered numbers provider */
- public static final String AUTHORITY = ObjectFactory.getFilteredNumberProviderAuthority();
-
- /** A content:// style uri to the authority for the filtered numbers provider */
- public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
-
- /** The type of filtering to be applied, e.g. block the number or whitelist the number. */
- public interface FilteredNumberTypes {
- static final int UNDEFINED = 0;
- /**
- * Dialer will disconnect the call without sending the caller to voicemail.
- */
- static final int BLOCKED_NUMBER = 1;
- }
-
- /** The original source of the filtered number, e.g. the user manually added it. */
- public interface FilteredNumberSources {
- static final int UNDEFINED = 0;
- /**
- * The user manually added this number through Dialer (e.g. from the call log or InCallUI).
- */
- static final int USER = 1;
- }
-
- public interface FilteredNumberColumns {
- // TYPE: INTEGER
- static final String _ID = "_id";
- /**
- * Represents the number to be filtered, normalized to compare phone numbers for equality.
- *
- * TYPE: TEXT
- */
- static final String NORMALIZED_NUMBER = "normalized_number";
- /**
- * Represents the number to be filtered, for formatting and
- * used with country iso for contact lookups.
- *
- * TYPE: TEXT
- */
- static final String NUMBER = "number";
- /**
- * The country code representing the country detected when
- * the phone number was added to the database.
- * Most numbers don't have the country code, so a best guess is provided by
- * the country detector system. The country iso is also needed in order to format
- * phone numbers correctly.
- *
- * TYPE: TEXT
- */
- static final String COUNTRY_ISO = "country_iso";
- /**
- * The number of times the number has been filtered by Dialer.
- * When this number is incremented, LAST_TIME_FILTERED should also be updated to
- * the current time.
- *
- * TYPE: INTEGER
- */
- static final String TIMES_FILTERED = "times_filtered";
- /**
- * Set to the current time when the phone number is filtered.
- * When this is updated, TIMES_FILTERED should also be incremented.
- *
- * TYPE: LONG
- */
- static final String LAST_TIME_FILTERED = "last_time_filtered";
- // TYPE: LONG
- static final String CREATION_TIME = "creation_time";
- /**
- * Indicates the type of filtering to be applied.
- *
- * TYPE: INTEGER
- * See {@link FilteredNumberTypes}
- */
- static final String TYPE = "type";
- /**
- * Integer representing the original source of the filtered number.
- *
- * TYPE: INTEGER
- * See {@link FilteredNumberSources}
- */
- static final String SOURCE = "source";
- }
-
- /**
- * <p>
- * Constants for the table of filtered numbers.
- * </p>
- * <h3>Operations</h3>
- * <dl>
- * <dt><b>Insert</b></dt>
- * <dd>Required fields: NUMBER, NORMALIZED_NUMBER, TYPE, SOURCE.
- * A default value will be used for the other fields if left null.</dd>
- * <dt><b>Update</b></dt>
- * <dt><b>Delete</b></dt>
- * <dt><b>Query</b></dt>
- * <dd>{@link #CONTENT_URI} can be used for any query, append an ID to
- * retrieve a specific filtered number entry.</dd>
- * </dl>
- */
- public static class FilteredNumber implements BaseColumns {
-
- public static final String FILTERED_NUMBERS_TABLE = "filtered_numbers_table";
- public static final String FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT =
- "filtered_numbers_increment_filtered_count";
-
- public static final Uri CONTENT_URI = Uri.withAppendedPath(
- AUTHORITY_URI,
- FILTERED_NUMBERS_TABLE);
-
- public static final Uri CONTENT_URI_INCREMENT_FILTERED_COUNT = Uri.withAppendedPath(
- AUTHORITY_URI,
- FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT);
-
- /**
- * This utility class cannot be instantiated.
- */
- private FilteredNumber () {}
-
- /**
- * The MIME type of {@link #CONTENT_URI} providing a directory of
- * filtered numbers.
- */
- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/filtered_numbers_table";
-
- /**
- * The MIME type of a {@link #CONTENT_URI} single filtered number.
- */
- public static final String CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/filtered_numbers_table";
- }
-}
diff --git a/src/com/android/dialer/database/FilteredNumberProvider.java b/src/com/android/dialer/database/FilteredNumberProvider.java
deleted file mode 100644
index 3b63d4b50..000000000
--- a/src/com/android/dialer/database/FilteredNumberProvider.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * 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.dialer.database;
-
-import android.content.ContentProvider;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.UriMatcher;
-import android.database.Cursor;
-import android.database.SQLException;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.net.Uri;
-import android.os.Binder;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.dialerbind.DatabaseHelperManager;
-import com.android.dialerbind.ObjectFactory;
-import com.google.common.annotations.VisibleForTesting;
-
-import java.util.Arrays;
-
-/**
- * Filtered number content provider.
- */
-public class FilteredNumberProvider extends ContentProvider {
-
- private static String TAG = FilteredNumberProvider.class.getSimpleName();
-
- private DialerDatabaseHelper mDialerDatabaseHelper;
-
- private static final int FILTERED_NUMBERS_TABLE = 1;
- private static final int FILTERED_NUMBERS_TABLE_ID = 2;
- private static final int FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT = 3;
-
- private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-
- @Override
- public boolean onCreate() {
- mDialerDatabaseHelper = getDatabaseHelper(getContext());
- if (mDialerDatabaseHelper == null) {
- return false;
- }
- sUriMatcher.addURI(ObjectFactory.getFilteredNumberProviderAuthority(),
- FilteredNumberContract.FilteredNumber.FILTERED_NUMBERS_TABLE,
- FILTERED_NUMBERS_TABLE);
- sUriMatcher.addURI(ObjectFactory.getFilteredNumberProviderAuthority(),
- FilteredNumberContract.FilteredNumber.FILTERED_NUMBERS_TABLE + "/#",
- FILTERED_NUMBERS_TABLE_ID);
- sUriMatcher.addURI(ObjectFactory.getFilteredNumberProviderAuthority(),
- FilteredNumberContract.FilteredNumber.FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT
- + "/#",
- FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT);
- return true;
- }
-
- @VisibleForTesting
- protected DialerDatabaseHelper getDatabaseHelper(Context context) {
- return DatabaseHelperManager.getDatabaseHelper(context);
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- final SQLiteDatabase db = mDialerDatabaseHelper.getReadableDatabase();
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables(DialerDatabaseHelper.Tables.FILTERED_NUMBER_TABLE);
- final int match = sUriMatcher.match(uri);
- switch (match) {
- case FILTERED_NUMBERS_TABLE:
- break;
- case FILTERED_NUMBERS_TABLE_ID:
- qb.appendWhere(FilteredNumberColumns._ID + "=" + ContentUris.parseId(uri));
- break;
- default:
- throw new IllegalArgumentException("Unknown uri: " + uri);
- }
- final Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, null);
- if (c != null) {
- c.setNotificationUri(getContext().getContentResolver(),
- FilteredNumberContract.FilteredNumber.CONTENT_URI);
- } else {
- Log.d(TAG, "CURSOR WAS NULL");
- }
- return c;
- }
-
- @Override
- public String getType(Uri uri) {
- return FilteredNumberContract.FilteredNumber.CONTENT_ITEM_TYPE;
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- SQLiteDatabase db = mDialerDatabaseHelper.getWritableDatabase();
- setDefaultValues(values);
- long id = db.insert(DialerDatabaseHelper.Tables.FILTERED_NUMBER_TABLE, null, values);
- if (id < 0) {
- return null;
- }
- notifyChange(uri);
- return ContentUris.withAppendedId(uri, id);
- }
-
- @VisibleForTesting
- protected long getCurrentTimeMs() {
- return System.currentTimeMillis();
- }
-
- private void setDefaultValues(ContentValues values) {
- if (values.getAsString(FilteredNumberColumns.COUNTRY_ISO) == null) {
- values.put(FilteredNumberColumns.COUNTRY_ISO,
- GeoUtil.getCurrentCountryIso(getContext()));
- }
- if (values.getAsInteger(FilteredNumberColumns.TIMES_FILTERED) == null) {
- values.put(FilteredNumberContract.FilteredNumberColumns.TIMES_FILTERED, 0);
- }
- if (values.getAsLong(FilteredNumberColumns.CREATION_TIME) == null) {
- values.put(FilteredNumberColumns.CREATION_TIME, getCurrentTimeMs());
- }
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- SQLiteDatabase db = mDialerDatabaseHelper.getWritableDatabase();
- final int match = sUriMatcher.match(uri);
- switch (match) {
- case FILTERED_NUMBERS_TABLE:
- break;
- case FILTERED_NUMBERS_TABLE_ID:
- selection = getSelectionWithId(selection, ContentUris.parseId(uri));
- break;
- default:
- throw new IllegalArgumentException("Unknown uri: " + uri);
- }
- int rows = db.delete(DialerDatabaseHelper.Tables.FILTERED_NUMBER_TABLE,
- selection,
- selectionArgs);
- if (rows > 0) {
- notifyChange(uri);
- }
- return rows;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- SQLiteDatabase db = mDialerDatabaseHelper.getWritableDatabase();
- final int match = sUriMatcher.match(uri);
- switch (match) {
- case FILTERED_NUMBERS_TABLE:
- break;
- case FILTERED_NUMBERS_TABLE_ID:
- selection = getSelectionWithId(selection, ContentUris.parseId(uri));
- break;
- case FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT:
- final long id = ContentUris.parseId(uri);
- try {
- db.execSQL(" UPDATE " + DialerDatabaseHelper.Tables.FILTERED_NUMBER_TABLE
- + " SET" + FilteredNumberColumns.TIMES_FILTERED + "="
- + FilteredNumberColumns.TIMES_FILTERED + "+1,"
- + FilteredNumberColumns.LAST_TIME_FILTERED + "="
- + getCurrentTimeMs()
- + " WHERE " + FilteredNumberColumns._ID + "=" + id);
- } catch (SQLException e) {
- Log.d(TAG, "Could not update blocked statistics for " + id);
- return 0;
- }
- return 1;
- default:
- throw new IllegalArgumentException("Unknown uri: " + uri);
- }
- int rows = db.update(DialerDatabaseHelper.Tables.FILTERED_NUMBER_TABLE,
- values,
- selection,
- selectionArgs);
- if (rows > 0 ) {
- notifyChange(uri);
- }
- return rows;
- }
-
- private String getSelectionWithId(String selection, long id) {
- if (TextUtils.isEmpty(selection)) {
- return FilteredNumberContract.FilteredNumberColumns._ID + "=" + id;
- } else {
- return selection + "AND " + FilteredNumberContract.FilteredNumberColumns._ID + "=" + id;
- }
- }
-
- private void notifyChange(Uri uri) {
- getContext().getContentResolver().notifyChange(uri, null);
- }
-}
diff --git a/src/com/android/dialer/database/VoicemailArchiveContract.java b/src/com/android/dialer/database/VoicemailArchiveContract.java
deleted file mode 100644
index f332932c3..000000000
--- a/src/com/android/dialer/database/VoicemailArchiveContract.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * 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.dialer.database;
-
-import android.net.Uri;
-import android.provider.BaseColumns;
-import android.provider.CallLog;
-import android.provider.OpenableColumns;
-
-import com.android.dialerbind.ObjectFactory;
-
-/**
- * Contains definitions for the supported URIs and columns for the voicemail archive table.
- * All the fields excluding MIME_TYPE, _DATA, ARCHIVED, SERVER_ID, mirror the fields in the
- * contract provided in {@link CallLog.Calls}.
- */
-public final class VoicemailArchiveContract {
-
- /** The authority used by the voicemail archive provider. */
- public static final String AUTHORITY = ObjectFactory.getVoicemailArchiveProviderAuthority();
-
- /** A content:// style uri for the voicemail archive provider */
- public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
-
- public static final class VoicemailArchive implements BaseColumns, OpenableColumns {
-
- public static final String VOICEMAIL_ARCHIVE_TABLE = "voicemail_archive_table";
-
- public static final Uri CONTENT_URI = Uri.withAppendedPath(
- AUTHORITY_URI,
- VOICEMAIL_ARCHIVE_TABLE);
-
- /**
- * @see android.provider.CallLog.Calls#NUMBER
- * TYPE: TEXT
- */
- public static final String NUMBER = CallLog.Calls.NUMBER;
-
- /**
- * @see android.provider.CallLog.Calls#DATE
- * TYPE: LONG
- */
- public static final String DATE = CallLog.Calls.DATE;
-
- /**
- * @see android.provider.CallLog.Calls#DURATION
- * TYPE: LONG
- */
- public static final String DURATION = CallLog.Calls.DURATION;
-
- /**
- * The mime type of the archived voicemail file.
- * TYPE: TEXT
- */
- public static final String MIME_TYPE = "mime_type";
-
- /**
- * @see android.provider.CallLog.Calls#COUNTRY_ISO
- * TYPE: LONG
- */
- public static final String COUNTRY_ISO = CallLog.Calls.COUNTRY_ISO;
-
- /**
- * The path of the archived voicemail file.
- * TYPE: TEXT
- */
- public static final String _DATA = "_data";
-
- /**
- * @see android.provider.CallLog.Calls#GEOCODED_LOCATION
- * TYPE: TEXT
- */
- public static final String GEOCODED_LOCATION = CallLog.Calls.GEOCODED_LOCATION;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_NAME
- * TYPE: TEXT
- */
- public static final String CACHED_NAME = CallLog.Calls.CACHED_NAME;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_NUMBER_TYPE
- * TYPE: INTEGER
- */
- public static final String CACHED_NUMBER_TYPE = CallLog.Calls.CACHED_NUMBER_TYPE;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_NUMBER_LABEL
- * TYPE: TEXT
- */
- public static final String CACHED_NUMBER_LABEL = CallLog.Calls.CACHED_NUMBER_LABEL;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_LOOKUP_URI
- * TYPE: TEXT
- */
- public static final String CACHED_LOOKUP_URI = CallLog.Calls.CACHED_LOOKUP_URI;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_MATCHED_NUMBER
- * TYPE: TEXT
- */
- public static final String CACHED_MATCHED_NUMBER = CallLog.Calls.CACHED_MATCHED_NUMBER;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_NORMALIZED_NUMBER
- * TYPE: TEXT
- */
- public static final String CACHED_NORMALIZED_NUMBER =
- CallLog.Calls.CACHED_NORMALIZED_NUMBER;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_PHOTO_ID
- * TYPE: LONG
- */
- public static final String CACHED_PHOTO_ID = CallLog.Calls.CACHED_PHOTO_ID;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_FORMATTED_NUMBER
- * TYPE: TEXT
- */
- public static final String CACHED_FORMATTED_NUMBER = CallLog.Calls.CACHED_FORMATTED_NUMBER;
-
- /**
- * If the voicemail was archived by the user by pressing the archive button, this is set to
- * 1 (true). If the voicemail was archived for the purpose of forwarding to other
- * applications, this is set to 0 (false).
- * TYPE: INTEGER
- */
- public static final String ARCHIVED = "archived_by_user";
-
- /**
- * @see android.provider.CallLog.Calls#NUMBER_PRESENTATION
- * TYPE: INTEGER
- */
- public static final String NUMBER_PRESENTATION = CallLog.Calls.NUMBER_PRESENTATION;
-
- /**
- * @see android.provider.CallLog.Calls#PHONE_ACCOUNT_COMPONENT_NAME
- * TYPE: TEXT
- */
- public static final String ACCOUNT_COMPONENT_NAME =
- CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME;
-
- /**
- * @see android.provider.CallLog.Calls#PHONE_ACCOUNT_ID
- * TYPE: TEXT
- */
- public static final String ACCOUNT_ID = CallLog.Calls.PHONE_ACCOUNT_ID;
-
- /**
- * @see android.provider.CallLog.Calls#FEATURES
- * TYPE: INTEGER
- */
- public static final String FEATURES = CallLog.Calls.FEATURES;
-
- /**
- * The id of the voicemail on the server.
- * TYPE: INTEGER
- */
- public static final String SERVER_ID = "server_id";
-
- /**
- * @see android.provider.CallLog.Calls#TRANSCRIPTION
- * TYPE: TEXT
- */
- public static final String TRANSCRIPTION = CallLog.Calls.TRANSCRIPTION;
-
- /**
- * @see android.provider.CallLog.Calls#CACHED_PHOTO_URI
- * TYPE: TEXT
- */
- public static final String CACHED_PHOTO_URI = CallLog.Calls.CACHED_PHOTO_URI;
-
- /**
- * The MIME type of a {@link #CONTENT_URI} single voicemail.
- */
- public static final String CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/voicmail_archive_table";
-
- public static final Uri buildWithId(int id) {
- return Uri.withAppendedPath(CONTENT_URI, Integer.toString(id));
- }
-
- /** Not instantiable. */
- private VoicemailArchive() {
- }
- }
-}
diff --git a/src/com/android/dialer/database/VoicemailArchiveProvider.java b/src/com/android/dialer/database/VoicemailArchiveProvider.java
deleted file mode 100644
index b3306bc4c..000000000
--- a/src/com/android/dialer/database/VoicemailArchiveProvider.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * 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.dialer.database;
-
-import android.content.ContentProvider;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.UriMatcher;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.support.annotation.Nullable;
-import android.text.TextUtils;
-import android.webkit.MimeTypeMap;
-
-import com.android.dialerbind.DatabaseHelperManager;
-import com.google.common.annotations.VisibleForTesting;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-
-/**
- * An implementation of the Voicemail Archive content provider. This class performs
- * all database level operations on the voicemail_archive_table.
- */
-public class VoicemailArchiveProvider extends ContentProvider {
- private static final String TAG = "VMArchiveProvider";
- private static final int VOICEMAIL_ARCHIVE_TABLE = 1;
- private static final int VOICEMAIL_ARCHIVE_TABLE_ID = 2;
- private static final String VOICEMAIL_FOLDER = "voicemails";
-
- private DialerDatabaseHelper mDialerDatabaseHelper;
- private final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-
- @Override
- public boolean onCreate() {
- mDialerDatabaseHelper = getDatabaseHelper(getContext());
- if (mDialerDatabaseHelper == null) {
- return false;
- }
- mUriMatcher.addURI(VoicemailArchiveContract.AUTHORITY,
- VoicemailArchiveContract.VoicemailArchive.VOICEMAIL_ARCHIVE_TABLE,
- VOICEMAIL_ARCHIVE_TABLE);
- mUriMatcher.addURI(VoicemailArchiveContract.AUTHORITY,
- VoicemailArchiveContract.VoicemailArchive.VOICEMAIL_ARCHIVE_TABLE + "/#",
- VOICEMAIL_ARCHIVE_TABLE_ID);
- return true;
- }
-
- @VisibleForTesting
- protected DialerDatabaseHelper getDatabaseHelper(Context context) {
- return DatabaseHelperManager.getDatabaseHelper(context);
- }
-
- /**
- * Used by the test class because it extends {@link android.test.ProviderTestCase2} in which the
- * {@link android.test.IsolatedContext} returns /dev/null when getFilesDir() is called.
- *
- * @see android.test.IsolatedContext#getFilesDir
- */
- @VisibleForTesting
- protected File getFilesDir() {
- return getContext().getFilesDir();
- }
-
- @Nullable
- @Override
- public Cursor query(Uri uri,
- @Nullable String[] projection,
- @Nullable String selection,
- @Nullable String[] selectionArgs,
- @Nullable String sortOrder) {
- SQLiteDatabase db = mDialerDatabaseHelper.getReadableDatabase();
- SQLiteQueryBuilder queryBuilder = getQueryBuilder(uri);
- Cursor cursor = queryBuilder
- .query(db, projection, selection, selectionArgs, null, null, sortOrder);
- if (cursor != null) {
- cursor.setNotificationUri(getContext().getContentResolver(),
- VoicemailArchiveContract.VoicemailArchive.CONTENT_URI);
- }
- return cursor;
- }
-
- @Override
- public String getType(Uri uri) {
- return VoicemailArchiveContract.VoicemailArchive.CONTENT_ITEM_TYPE;
- }
-
- @Nullable
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- SQLiteDatabase db = mDialerDatabaseHelper.getWritableDatabase();
- long id = db.insert(DialerDatabaseHelper.Tables.VOICEMAIL_ARCHIVE_TABLE,
- null, values);
- if (id < 0) {
- return null;
- }
- notifyChange(uri);
- // Create the directory for archived voicemails if it doesn't already exist
- File directory = new File(getFilesDir(), VOICEMAIL_FOLDER);
- directory.mkdirs();
- Uri newUri = ContentUris.withAppendedId(uri, id);
-
- // Create new file only if path is not provided to one
- if (!values.containsKey(VoicemailArchiveContract.VoicemailArchive._DATA)) {
- String fileExtension = MimeTypeMap.getSingleton().getExtensionFromMimeType(
- values.getAsString(VoicemailArchiveContract.VoicemailArchive.MIME_TYPE));
- File voicemailFile = new File(directory,
- TextUtils.isEmpty(fileExtension) ? Long.toString(id) :
- id + "." + fileExtension);
- values.put(VoicemailArchiveContract.VoicemailArchive._DATA, voicemailFile.getPath());
- }
- update(newUri, values, null, null);
- return newUri;
- }
-
-
- @Override
- public int delete(Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
- SQLiteDatabase db = mDialerDatabaseHelper.getWritableDatabase();
- SQLiteQueryBuilder queryBuilder = getQueryBuilder(uri);
- Cursor cursor = queryBuilder.query(db, null, selection, selectionArgs, null, null, null);
-
- // Delete all the voicemail files related to the selected rows
- while (cursor.moveToNext()) {
- deleteFile(cursor.getString(cursor.getColumnIndex(
- VoicemailArchiveContract.VoicemailArchive._DATA)));
- }
-
- int rows = db.delete(DialerDatabaseHelper.Tables.VOICEMAIL_ARCHIVE_TABLE,
- getSelectionWithId(selection, uri),
- selectionArgs);
- if (rows > 0) {
- notifyChange(uri);
- }
- return rows;
- }
-
- @Override
- public int update(Uri uri,
- ContentValues values,
- @Nullable String selection,
- @Nullable String[] selectionArgs) {
- SQLiteDatabase db = mDialerDatabaseHelper.getWritableDatabase();
- selection = getSelectionWithId(selection, uri);
- int rows = db.update(DialerDatabaseHelper.Tables.VOICEMAIL_ARCHIVE_TABLE,
- values,
- selection,
- selectionArgs);
- if (rows > 0) {
- notifyChange(uri);
- }
- return rows;
- }
-
- @Override
- public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
- if (mUriMatcher.match(uri) != VOICEMAIL_ARCHIVE_TABLE_ID) {
- throw new IllegalArgumentException("URI Invalid.");
- }
- return openFileHelper(uri, mode);
- }
-
- private void deleteFile(@Nullable String path) {
- if (TextUtils.isEmpty(path)) {
- return;
- }
- File file = new File(path);
- if (file.exists()) {
- file.delete();
- }
- }
-
- private SQLiteQueryBuilder getQueryBuilder(Uri uri) {
- SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
- queryBuilder.setTables(DialerDatabaseHelper.Tables.VOICEMAIL_ARCHIVE_TABLE);
- String selectionWithId = getSelectionWithId(null, uri);
- if (!TextUtils.isEmpty(selectionWithId)) {
- queryBuilder.appendWhere(selectionWithId);
- }
- return queryBuilder;
- }
-
- private String getSelectionWithId(String selection, Uri uri) {
- int match = mUriMatcher.match(uri);
- switch (match) {
- case VOICEMAIL_ARCHIVE_TABLE:
- return selection;
- case VOICEMAIL_ARCHIVE_TABLE_ID:
- String idStr = VoicemailArchiveContract.VoicemailArchive._ID + "=" +
- ContentUris.parseId(uri);
- return TextUtils.isEmpty(selection) ? idStr : selection + " AND " + idStr;
- default:
- throw new IllegalArgumentException("Unknown uri: " + uri);
- }
- }
-
- private void notifyChange(Uri uri) {
- getContext().getContentResolver().notifyChange(uri, null);
- }
-}
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
deleted file mode 100644
index 55d534676..000000000
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ /dev/null
@@ -1,1695 +0,0 @@
-/*
- * 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.dialer.dialpad;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.media.AudioManager;
-import android.media.ToneGenerator;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Trace;
-import android.provider.Contacts.People;
-import android.provider.Contacts.Phones;
-import android.provider.Contacts.PhonesColumns;
-import android.provider.Settings;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.HapticFeedbackConstants;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.PopupMenu;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.dialog.CallSubjectDialog;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.common.util.PhoneNumberFormatter;
-import com.android.contacts.common.util.StopWatch;
-import com.android.contacts.common.widget.FloatingActionButtonController;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.NeededForReflection;
-import com.android.dialer.R;
-import com.android.dialer.SpecialCharSequenceMgr;
-import com.android.dialer.calllog.PhoneAccountUtils;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.IntentUtil.CallIntentBuilder;
-import com.android.dialer.util.TelecomUtil;
-import com.android.incallui.Call.LogState;
-import com.android.phone.common.CallLogAsync;
-import com.android.phone.common.animation.AnimUtils;
-import com.android.phone.common.dialpad.DialpadKeyButton;
-import com.android.phone.common.dialpad.DialpadView;
-
-import java.util.HashSet;
-import java.util.List;
-
-/**
- * Fragment that displays a twelve-key phone dialpad.
- */
-public class DialpadFragment extends Fragment
- implements View.OnClickListener,
- View.OnLongClickListener, View.OnKeyListener,
- AdapterView.OnItemClickListener, TextWatcher,
- PopupMenu.OnMenuItemClickListener,
- DialpadKeyButton.OnPressedListener {
- private static final String TAG = "DialpadFragment";
-
- /**
- * LinearLayout with getter and setter methods for the translationY property using floats,
- * for animation purposes.
- */
- public static class DialpadSlidingRelativeLayout extends RelativeLayout {
-
- public DialpadSlidingRelativeLayout(Context context) {
- super(context);
- }
-
- public DialpadSlidingRelativeLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public DialpadSlidingRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @NeededForReflection
- public float getYFraction() {
- final int height = getHeight();
- if (height == 0) return 0;
- return getTranslationY() / height;
- }
-
- @NeededForReflection
- public void setYFraction(float yFraction) {
- setTranslationY(yFraction * getHeight());
- }
- }
-
- public interface OnDialpadQueryChangedListener {
- void onDialpadQueryChanged(String query);
- }
-
- public interface HostInterface {
- /**
- * Notifies the parent activity that the space above the dialpad has been tapped with
- * no query in the dialpad present. In most situations this will cause the dialpad to
- * be dismissed, unless there happens to be content showing.
- */
- boolean onDialpadSpacerTouchWithEmptyQuery();
- }
-
- private static final boolean DEBUG = DialtactsActivity.DEBUG;
-
- // This is the amount of screen the dialpad fragment takes up when fully displayed
- private static final float DIALPAD_SLIDE_FRACTION = 0.67f;
-
- private static final String EMPTY_NUMBER = "";
- private static final char PAUSE = ',';
- private static final char WAIT = ';';
-
- /** The length of DTMF tones in milliseconds */
- private static final int TONE_LENGTH_MS = 150;
- private static final int TONE_LENGTH_INFINITE = -1;
-
- /** The DTMF tone volume relative to other sounds in the stream */
- private static final int TONE_RELATIVE_VOLUME = 80;
-
- /** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */
- private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_DTMF;
-
-
- private OnDialpadQueryChangedListener mDialpadQueryListener;
-
- private DialpadView mDialpadView;
- private EditText mDigits;
- private int mDialpadSlideInDuration;
-
- /** Remembers if we need to clear digits field when the screen is completely gone. */
- private boolean mClearDigitsOnStop;
-
- private View mOverflowMenuButton;
- private PopupMenu mOverflowPopupMenu;
- private View mDelete;
- private ToneGenerator mToneGenerator;
- private final Object mToneGeneratorLock = new Object();
- private View mSpacer;
-
- private FloatingActionButtonController mFloatingActionButtonController;
-
- /**
- * Set of dialpad keys that are currently being pressed
- */
- private final HashSet<View> mPressedDialpadKeys = new HashSet<View>(12);
-
- private ListView mDialpadChooser;
- private DialpadChooserAdapter mDialpadChooserAdapter;
-
- /**
- * Regular expression prohibiting manual phone call. Can be empty, which means "no rule".
- */
- private String mProhibitedPhoneNumberRegexp;
-
- private PseudoEmergencyAnimator mPseudoEmergencyAnimator;
-
- // Last number dialed, retrieved asynchronously from the call DB
- // in onCreate. This number is displayed when the user hits the
- // send key and cleared in onPause.
- private final CallLogAsync mCallLog = new CallLogAsync();
- private String mLastNumberDialed = EMPTY_NUMBER;
-
- // determines if we want to playback local DTMF tones.
- private boolean mDTMFToneEnabled;
-
- /** Identifier for the "Add Call" intent extra. */
- private static final String ADD_CALL_MODE_KEY = "add_call_mode";
-
- /**
- * Identifier for intent extra for sending an empty Flash message for
- * CDMA networks. This message is used by the network to simulate a
- * press/depress of the "hookswitch" of a landline phone. Aka "empty flash".
- *
- * TODO: Using an intent extra to tell the phone to send this flash is a
- * temporary measure. To be replaced with an Telephony/TelecomManager call in the future.
- * TODO: Keep in sync with the string defined in OutgoingCallBroadcaster.java
- * in Phone app until this is replaced with the Telephony/Telecom API.
- */
- private static final String EXTRA_SEND_EMPTY_FLASH
- = "com.android.phone.extra.SEND_EMPTY_FLASH";
-
- private String mCurrentCountryIso;
-
- private CallStateReceiver mCallStateReceiver;
-
- private class CallStateReceiver extends BroadcastReceiver {
- /**
- * Receive call state changes so that we can take down the
- * "dialpad chooser" if the phone becomes idle while the
- * chooser UI is visible.
- */
- @Override
- public void onReceive(Context context, Intent intent) {
- // Log.i(TAG, "CallStateReceiver.onReceive");
- String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
- if ((TextUtils.equals(state, TelephonyManager.EXTRA_STATE_IDLE) ||
- TextUtils.equals(state, TelephonyManager.EXTRA_STATE_OFFHOOK))
- && isDialpadChooserVisible()) {
- // Log.i(TAG, "Call ended with dialpad chooser visible! Taking it down...");
- // Note there's a race condition in the UI here: the
- // dialpad chooser could conceivably disappear (on its
- // own) at the exact moment the user was trying to select
- // one of the choices, which would be confusing. (But at
- // least that's better than leaving the dialpad chooser
- // onscreen, but useless...)
- showDialpadChooser(false);
- }
- }
- }
-
- private boolean mWasEmptyBeforeTextChange;
-
- /**
- * This field is set to true while processing an incoming DIAL intent, in order to make sure
- * that SpecialCharSequenceMgr actions can be triggered by user input but *not* by a
- * tel: URI passed by some other app. It will be set to false when all digits are cleared.
- */
- private boolean mDigitsFilledByIntent;
-
- private boolean mStartedFromNewIntent = false;
- private boolean mFirstLaunch = false;
- private boolean mAnimate = false;
-
- private static final String PREF_DIGITS_FILLED_BY_INTENT = "pref_digits_filled_by_intent";
-
- private TelephonyManager getTelephonyManager() {
- return (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
- }
-
- @Override
- public Context getContext() {
- return getActivity();
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- mWasEmptyBeforeTextChange = TextUtils.isEmpty(s);
- }
-
- @Override
- public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
- if (mWasEmptyBeforeTextChange != TextUtils.isEmpty(input)) {
- final Activity activity = getActivity();
- if (activity != null) {
- activity.invalidateOptionsMenu();
- updateMenuOverflowButton(mWasEmptyBeforeTextChange);
- }
- }
-
- // DTMF Tones do not need to be played here any longer -
- // the DTMF dialer handles that functionality now.
- }
-
- @Override
- public void afterTextChanged(Editable input) {
- // When DTMF dialpad buttons are being pressed, we delay SpecialCharSequenceMgr sequence,
- // since some of SpecialCharSequenceMgr's behavior is too abrupt for the "touch-down"
- // behavior.
- if (!mDigitsFilledByIntent &&
- SpecialCharSequenceMgr.handleChars(getActivity(), input.toString(), mDigits)) {
- // A special sequence was entered, clear the digits
- mDigits.getText().clear();
- }
-
- if (isDigitsEmpty()) {
- mDigitsFilledByIntent = false;
- mDigits.setCursorVisible(false);
- }
-
- if (mDialpadQueryListener != null) {
- mDialpadQueryListener.onDialpadQueryChanged(mDigits.getText().toString());
- }
-
- updateDeleteButtonEnabledState();
- }
-
- @Override
- public void onCreate(Bundle state) {
- Trace.beginSection(TAG + " onCreate");
- super.onCreate(state);
-
- mFirstLaunch = state == null;
-
- mCurrentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
-
- mProhibitedPhoneNumberRegexp = getResources().getString(
- R.string.config_prohibited_phone_number_regexp);
-
- if (state != null) {
- mDigitsFilledByIntent = state.getBoolean(PREF_DIGITS_FILLED_BY_INTENT);
- }
-
- mDialpadSlideInDuration = getResources().getInteger(R.integer.dialpad_slide_in_duration);
-
- if (mCallStateReceiver == null) {
- IntentFilter callStateIntentFilter = new IntentFilter(
- TelephonyManager.ACTION_PHONE_STATE_CHANGED);
- mCallStateReceiver = new CallStateReceiver();
- ((Context) getActivity()).registerReceiver(mCallStateReceiver, callStateIntentFilter);
- }
- Trace.endSection();
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
- Trace.beginSection(TAG + " onCreateView");
- Trace.beginSection(TAG + " inflate view");
- final View fragmentView = inflater.inflate(R.layout.dialpad_fragment, container,
- false);
- Trace.endSection();
- Trace.beginSection(TAG + " buildLayer");
- fragmentView.buildLayer();
- Trace.endSection();
-
- Trace.beginSection(TAG + " setup views");
-
- mDialpadView = (DialpadView) fragmentView.findViewById(R.id.dialpad_view);
- mDialpadView.setCanDigitsBeEdited(true);
- mDigits = mDialpadView.getDigits();
- mDigits.setKeyListener(UnicodeDialerKeyListener.INSTANCE);
- mDigits.setOnClickListener(this);
- mDigits.setOnKeyListener(this);
- mDigits.setOnLongClickListener(this);
- mDigits.addTextChangedListener(this);
- mDigits.setElegantTextHeight(false);
- PhoneNumberFormatter.setPhoneNumberFormattingTextWatcher(getActivity(), mDigits);
- // Check for the presence of the keypad
- View oneButton = fragmentView.findViewById(R.id.one);
- if (oneButton != null) {
- configureKeypadListeners(fragmentView);
- }
-
- mDelete = mDialpadView.getDeleteButton();
-
- if (mDelete != null) {
- mDelete.setOnClickListener(this);
- mDelete.setOnLongClickListener(this);
- }
-
- mSpacer = fragmentView.findViewById(R.id.spacer);
- mSpacer.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (isDigitsEmpty()) {
- if (getActivity() != null) {
- return ((HostInterface) getActivity()).onDialpadSpacerTouchWithEmptyQuery();
- }
- return true;
- }
- return false;
- }
- });
-
- mDigits.setCursorVisible(false);
-
- // Set up the "dialpad chooser" UI; see showDialpadChooser().
- mDialpadChooser = (ListView) fragmentView.findViewById(R.id.dialpadChooser);
- mDialpadChooser.setOnItemClickListener(this);
-
- final View floatingActionButtonContainer =
- fragmentView.findViewById(R.id.dialpad_floating_action_button_container);
- final ImageButton floatingActionButton =
- (ImageButton) fragmentView.findViewById(R.id.dialpad_floating_action_button);
- floatingActionButton.setOnClickListener(this);
- mFloatingActionButtonController = new FloatingActionButtonController(getActivity(),
- floatingActionButtonContainer, floatingActionButton);
- Trace.endSection();
- Trace.endSection();
- return fragmentView;
- }
-
- private boolean isLayoutReady() {
- return mDigits != null;
- }
-
- @VisibleForTesting
- public EditText getDigitsWidget() {
- return mDigits;
- }
-
- /**
- * @return true when {@link #mDigits} is actually filled by the Intent.
- */
- private boolean fillDigitsIfNecessary(Intent intent) {
- // Only fills digits from an intent if it is a new intent.
- // Otherwise falls back to the previously used number.
- if (!mFirstLaunch && !mStartedFromNewIntent) {
- return false;
- }
-
- final String action = intent.getAction();
- if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
- Uri uri = intent.getData();
- if (uri != null) {
- if (PhoneAccount.SCHEME_TEL.equals(uri.getScheme())) {
- // Put the requested number into the input area
- String data = uri.getSchemeSpecificPart();
- // Remember it is filled via Intent.
- mDigitsFilledByIntent = true;
- final String converted = PhoneNumberUtils.convertKeypadLettersToDigits(
- PhoneNumberUtils.replaceUnicodeDigits(data));
- setFormattedDigits(converted, null);
- return true;
- } else {
- if (!PermissionsUtil.hasContactsPermissions(getActivity())) {
- return false;
- }
- String type = intent.getType();
- if (People.CONTENT_ITEM_TYPE.equals(type)
- || Phones.CONTENT_ITEM_TYPE.equals(type)) {
- // Query the phone number
- Cursor c = getActivity().getContentResolver().query(intent.getData(),
- new String[] {PhonesColumns.NUMBER, PhonesColumns.NUMBER_KEY},
- null, null, null);
- if (c != null) {
- try {
- if (c.moveToFirst()) {
- // Remember it is filled via Intent.
- mDigitsFilledByIntent = true;
- // Put the number into the input area
- setFormattedDigits(c.getString(0), c.getString(1));
- return true;
- }
- } finally {
- c.close();
- }
- }
- }
- }
- }
- }
- return false;
- }
-
- /**
- * Determines whether an add call operation is requested.
- *
- * @param intent The intent.
- * @return {@literal true} if add call operation was requested. {@literal false} otherwise.
- */
- public static boolean isAddCallMode(Intent intent) {
- if (intent == null) {
- return false;
- }
- final String action = intent.getAction();
- if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
- // see if we are "adding a call" from the InCallScreen; false by default.
- return intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
- } else {
- return false;
- }
- }
-
- /**
- * Checks the given Intent and changes dialpad's UI state. For example, if the Intent requires
- * the screen to enter "Add Call" mode, this method will show correct UI for the mode.
- */
- private void configureScreenFromIntent(Activity parent) {
- // If we were not invoked with a DIAL intent,
- if (!(parent instanceof DialtactsActivity)) {
- setStartedFromNewIntent(false);
- return;
- }
- // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
- // digits in the dialer field.
- Intent intent = parent.getIntent();
-
- if (!isLayoutReady()) {
- // This happens typically when parent's Activity#onNewIntent() is called while
- // Fragment#onCreateView() isn't called yet, and thus we cannot configure Views at
- // this point. onViewCreate() should call this method after preparing layouts, so
- // just ignore this call now.
- Log.i(TAG,
- "Screen configuration is requested before onCreateView() is called. Ignored");
- return;
- }
-
- boolean needToShowDialpadChooser = false;
-
- // Be sure *not* to show the dialpad chooser if this is an
- // explicit "Add call" action, though.
- final boolean isAddCallMode = isAddCallMode(intent);
- if (!isAddCallMode) {
-
- // Don't show the chooser when called via onNewIntent() and phone number is present.
- // i.e. User clicks a telephone link from gmail for example.
- // In this case, we want to show the dialpad with the phone number.
- final boolean digitsFilled = fillDigitsIfNecessary(intent);
- if (!(mStartedFromNewIntent && digitsFilled)) {
-
- final String action = intent.getAction();
- if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)
- || Intent.ACTION_MAIN.equals(action)) {
- // If there's already an active call, bring up an intermediate UI to
- // make the user confirm what they really want to do.
- if (isPhoneInUse()) {
- needToShowDialpadChooser = true;
- }
- }
-
- }
- }
- showDialpadChooser(needToShowDialpadChooser);
- setStartedFromNewIntent(false);
- }
-
- public void setStartedFromNewIntent(boolean value) {
- mStartedFromNewIntent = value;
- }
-
- public void clearCallRateInformation() {
- setCallRateInformation(null, null);
- }
-
- public void setCallRateInformation(String countryName, String displayRate) {
- mDialpadView.setCallRateInformation(countryName, displayRate);
- }
-
- /**
- * Sets formatted digits to digits field.
- */
- private void setFormattedDigits(String data, String normalizedNumber) {
- final String formatted = getFormattedDigits(data, normalizedNumber, mCurrentCountryIso);
- if (!TextUtils.isEmpty(formatted)) {
- Editable digits = mDigits.getText();
- digits.replace(0, digits.length(), formatted);
- // for some reason this isn't getting called in the digits.replace call above..
- // but in any case, this will make sure the background drawable looks right
- afterTextChanged(digits);
- }
- }
-
- /**
- * Format the provided string of digits into one that represents a properly formatted phone
- * number.
- *
- * @param dialString String of characters to format
- * @param normalizedNumber the E164 format number whose country code is used if the given
- * phoneNumber doesn't have the country code.
- * @param countryIso The country code representing the format to use if the provided normalized
- * number is null or invalid.
- * @return the provided string of digits as a formatted phone number, retaining any
- * post-dial portion of the string.
- */
- @VisibleForTesting
- static String getFormattedDigits(String dialString, String normalizedNumber, String countryIso) {
- String number = PhoneNumberUtils.extractNetworkPortion(dialString);
- // Also retrieve the post dial portion of the provided data, so that the entire dial
- // string can be reconstituted later.
- final String postDial = PhoneNumberUtils.extractPostDialPortion(dialString);
-
- if (TextUtils.isEmpty(number)) {
- return postDial;
- }
-
- number = PhoneNumberUtils.formatNumber(number, normalizedNumber, countryIso);
-
- if (TextUtils.isEmpty(postDial)) {
- return number;
- }
-
- return number.concat(postDial);
- }
-
- private void configureKeypadListeners(View fragmentView) {
- final int[] buttonIds = new int[] {R.id.one, R.id.two, R.id.three, R.id.four, R.id.five,
- R.id.six, R.id.seven, R.id.eight, R.id.nine, R.id.star, R.id.zero, R.id.pound};
-
- DialpadKeyButton dialpadKey;
-
- for (int i = 0; i < buttonIds.length; i++) {
- dialpadKey = (DialpadKeyButton) fragmentView.findViewById(buttonIds[i]);
- dialpadKey.setOnPressedListener(this);
- }
-
- // Long-pressing one button will initiate Voicemail.
- final DialpadKeyButton one = (DialpadKeyButton) fragmentView.findViewById(R.id.one);
- one.setOnLongClickListener(this);
-
- // Long-pressing zero button will enter '+' instead.
- final DialpadKeyButton zero = (DialpadKeyButton) fragmentView.findViewById(R.id.zero);
- zero.setOnLongClickListener(this);
- }
-
- @Override
- public void onStart() {
- Trace.beginSection(TAG + " onStart");
- super.onStart();
- // if the mToneGenerator creation fails, just continue without it. It is
- // a local audio signal, and is not as important as the dtmf tone itself.
- final long start = System.currentTimeMillis();
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator == null) {
- try {
- mToneGenerator = new ToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME);
- } catch (RuntimeException e) {
- Log.w(TAG, "Exception caught while creating local tone generator: " + e);
- mToneGenerator = null;
- }
- }
- }
- final long total = System.currentTimeMillis() - start;
- if (total > 50) {
- Log.i(TAG, "Time for ToneGenerator creation: " + total);
- }
- Trace.endSection();
- };
-
- @Override
- public void onResume() {
- Trace.beginSection(TAG + " onResume");
- super.onResume();
-
- final DialtactsActivity activity = (DialtactsActivity) getActivity();
- mDialpadQueryListener = activity;
-
- final StopWatch stopWatch = StopWatch.start("Dialpad.onResume");
-
- // Query the last dialed number. Do it first because hitting
- // the DB is 'slow'. This call is asynchronous.
- queryLastOutgoingCall();
-
- stopWatch.lap("qloc");
-
- final ContentResolver contentResolver = activity.getContentResolver();
-
- // retrieve the DTMF tone play back setting.
- mDTMFToneEnabled = Settings.System.getInt(contentResolver,
- Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
-
- stopWatch.lap("dtwd");
-
- stopWatch.lap("hptc");
-
- mPressedDialpadKeys.clear();
-
- configureScreenFromIntent(getActivity());
-
- stopWatch.lap("fdin");
-
- if (!isPhoneInUse()) {
- // A sanity-check: the "dialpad chooser" UI should not be visible if the phone is idle.
- showDialpadChooser(false);
- }
-
- stopWatch.lap("hnt");
-
- updateDeleteButtonEnabledState();
-
- stopWatch.lap("bes");
-
- stopWatch.stopAndLog(TAG, 50);
-
- // Populate the overflow menu in onResume instead of onCreate, so that if the SMS activity
- // is disabled while Dialer is paused, the "Send a text message" option can be correctly
- // removed when resumed.
- mOverflowMenuButton = mDialpadView.getOverflowMenuButton();
- mOverflowPopupMenu = buildOptionsMenu(mOverflowMenuButton);
- mOverflowMenuButton.setOnTouchListener(mOverflowPopupMenu.getDragToOpenListener());
- mOverflowMenuButton.setOnClickListener(this);
- mOverflowMenuButton.setVisibility(isDigitsEmpty() ? View.INVISIBLE : View.VISIBLE);
-
- if (mFirstLaunch) {
- // The onHiddenChanged callback does not get called the first time the fragment is
- // attached, so call it ourselves here.
- onHiddenChanged(false);
- }
-
- mFirstLaunch = false;
- Trace.endSection();
- }
-
- @Override
- public void onPause() {
- super.onPause();
-
- // Make sure we don't leave this activity with a tone still playing.
- stopTone();
- mPressedDialpadKeys.clear();
-
- // TODO: I wonder if we should not check if the AsyncTask that
- // lookup the last dialed number has completed.
- mLastNumberDialed = EMPTY_NUMBER; // Since we are going to query again, free stale number.
-
- SpecialCharSequenceMgr.cleanup();
- }
-
- @Override
- public void onStop() {
- super.onStop();
-
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator != null) {
- mToneGenerator.release();
- mToneGenerator = null;
- }
- }
-
- if (mClearDigitsOnStop) {
- mClearDigitsOnStop = false;
- clearDialpad();
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean(PREF_DIGITS_FILLED_BY_INTENT, mDigitsFilledByIntent);
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (mPseudoEmergencyAnimator != null) {
- mPseudoEmergencyAnimator.destroy();
- mPseudoEmergencyAnimator = null;
- }
- ((Context) getActivity()).unregisterReceiver(mCallStateReceiver);
- }
-
- private void keyPressed(int keyCode) {
- if (getView() == null || getView().getTranslationY() != 0) {
- return;
- }
- switch (keyCode) {
- case KeyEvent.KEYCODE_1:
- playTone(ToneGenerator.TONE_DTMF_1, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_2:
- playTone(ToneGenerator.TONE_DTMF_2, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_3:
- playTone(ToneGenerator.TONE_DTMF_3, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_4:
- playTone(ToneGenerator.TONE_DTMF_4, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_5:
- playTone(ToneGenerator.TONE_DTMF_5, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_6:
- playTone(ToneGenerator.TONE_DTMF_6, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_7:
- playTone(ToneGenerator.TONE_DTMF_7, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_8:
- playTone(ToneGenerator.TONE_DTMF_8, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_9:
- playTone(ToneGenerator.TONE_DTMF_9, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_0:
- playTone(ToneGenerator.TONE_DTMF_0, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_POUND:
- playTone(ToneGenerator.TONE_DTMF_P, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_STAR:
- playTone(ToneGenerator.TONE_DTMF_S, TONE_LENGTH_INFINITE);
- break;
- default:
- break;
- }
-
- getView().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
- KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
- mDigits.onKeyDown(keyCode, event);
-
- // If the cursor is at the end of the text we hide it.
- final int length = mDigits.length();
- if (length == mDigits.getSelectionStart() && length == mDigits.getSelectionEnd()) {
- mDigits.setCursorVisible(false);
- }
- }
-
- @Override
- public boolean onKey(View view, int keyCode, KeyEvent event) {
- if (view.getId() == R.id.digits) {
- if (keyCode == KeyEvent.KEYCODE_ENTER) {
- handleDialButtonPressed();
- return true;
- }
-
- }
- return false;
- }
-
- /**
- * When a key is pressed, we start playing DTMF tone, do vibration, and enter the digit
- * immediately. When a key is released, we stop the tone. Note that the "key press" event will
- * be delivered by the system with certain amount of delay, it won't be synced with user's
- * actual "touch-down" behavior.
- */
- @Override
- public void onPressed(View view, boolean pressed) {
- if (DEBUG) Log.d(TAG, "onPressed(). view: " + view + ", pressed: " + pressed);
- if (pressed) {
- int resId = view.getId();
- if (resId == R.id.one) {
- keyPressed(KeyEvent.KEYCODE_1);
- } else if (resId == R.id.two) {
- keyPressed(KeyEvent.KEYCODE_2);
- } else if (resId == R.id.three) {
- keyPressed(KeyEvent.KEYCODE_3);
- } else if (resId == R.id.four) {
- keyPressed(KeyEvent.KEYCODE_4);
- } else if (resId == R.id.five) {
- keyPressed(KeyEvent.KEYCODE_5);
- } else if (resId == R.id.six) {
- keyPressed(KeyEvent.KEYCODE_6);
- } else if (resId == R.id.seven) {
- keyPressed(KeyEvent.KEYCODE_7);
- } else if (resId == R.id.eight) {
- keyPressed(KeyEvent.KEYCODE_8);
- } else if (resId == R.id.nine) {
- keyPressed(KeyEvent.KEYCODE_9);
- } else if (resId == R.id.zero) {
- keyPressed(KeyEvent.KEYCODE_0);
- } else if (resId == R.id.pound) {
- keyPressed(KeyEvent.KEYCODE_POUND);
- } else if (resId == R.id.star) {
- keyPressed(KeyEvent.KEYCODE_STAR);
- } else {
- Log.wtf(TAG, "Unexpected onTouch(ACTION_DOWN) event from: " + view);
- }
- mPressedDialpadKeys.add(view);
- } else {
- mPressedDialpadKeys.remove(view);
- if (mPressedDialpadKeys.isEmpty()) {
- stopTone();
- }
- }
- }
-
- /**
- * Called by the containing Activity to tell this Fragment to build an overflow options
- * menu for display by the container when appropriate.
- *
- * @param invoker the View that invoked the options menu, to act as an anchor location.
- */
- private PopupMenu buildOptionsMenu(View invoker) {
- final PopupMenu popupMenu = new PopupMenu(getActivity(), invoker) {
- @Override
- public void show() {
- final Menu menu = getMenu();
-
- boolean enable = !isDigitsEmpty();
- for (int i = 0; i < menu.size(); i++) {
- MenuItem item = menu.getItem(i);
- item.setEnabled(enable);
- if (item.getItemId() == R.id.menu_call_with_note) {
- item.setVisible(CallUtil.isCallWithSubjectSupported(getContext()));
- }
- }
- super.show();
- }
- };
- popupMenu.inflate(R.menu.dialpad_options);
- popupMenu.setOnMenuItemClickListener(this);
- return popupMenu;
- }
-
- @Override
- public void onClick(View view) {
- int resId = view.getId();
- if (resId == R.id.dialpad_floating_action_button) {
- view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
- handleDialButtonPressed();
- } else if (resId == R.id.deleteButton) {
- keyPressed(KeyEvent.KEYCODE_DEL);
- } else if (resId == R.id.digits) {
- if (!isDigitsEmpty()) {
- mDigits.setCursorVisible(true);
- }
- } else if (resId == R.id.dialpad_overflow) {
- mOverflowPopupMenu.show();
- } else {
- Log.wtf(TAG, "Unexpected onClick() event from: " + view);
- return;
- }
- }
-
- @Override
- public boolean onLongClick(View view) {
- final Editable digits = mDigits.getText();
- final int id = view.getId();
- if (id == R.id.deleteButton) {
- digits.clear();
- return true;
- } else if (id == R.id.one) {
- if (isDigitsEmpty() || TextUtils.equals(mDigits.getText(), "1")) {
- // We'll try to initiate voicemail and thus we want to remove irrelevant string.
- removePreviousDigitIfPossible('1');
-
- List<PhoneAccountHandle> subscriptionAccountHandles =
- PhoneAccountUtils.getSubscriptionPhoneAccounts(getActivity());
- boolean hasUserSelectedDefault = subscriptionAccountHandles.contains(
- TelecomUtil.getDefaultOutgoingPhoneAccount(getActivity(),
- PhoneAccount.SCHEME_VOICEMAIL));
- boolean needsAccountDisambiguation = subscriptionAccountHandles.size() > 1
- && !hasUserSelectedDefault;
-
- if (needsAccountDisambiguation || isVoicemailAvailable()) {
- // On a multi-SIM phone, if the user has not selected a default
- // subscription, initiate a call to voicemail so they can select an account
- // from the "Call with" dialog.
- callVoicemail();
- } else if (getActivity() != null) {
- // Voicemail is unavailable maybe because Airplane mode is turned on.
- // Check the current status and show the most appropriate error message.
- final boolean isAirplaneModeOn =
- Settings.System.getInt(getActivity().getContentResolver(),
- Settings.System.AIRPLANE_MODE_ON, 0) != 0;
- if (isAirplaneModeOn) {
- DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
- R.string.dialog_voicemail_airplane_mode_message);
- dialogFragment.show(getFragmentManager(),
- "voicemail_request_during_airplane_mode");
- } else {
- DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
- R.string.dialog_voicemail_not_ready_message);
- dialogFragment.show(getFragmentManager(), "voicemail_not_ready");
- }
- }
- return true;
- }
- return false;
- } else if (id == R.id.zero) {
- if (mPressedDialpadKeys.contains(view)) {
- // If the zero key is currently pressed, then the long press occurred by touch
- // (and not via other means like certain accessibility input methods).
- // Remove the '0' that was input when the key was first pressed.
- removePreviousDigitIfPossible('0');
- }
- keyPressed(KeyEvent.KEYCODE_PLUS);
- stopTone();
- mPressedDialpadKeys.remove(view);
- return true;
- } else if (id == R.id.digits) {
- mDigits.setCursorVisible(true);
- return false;
- }
- return false;
- }
-
- /**
- * Remove the digit just before the current position of the cursor, iff the following conditions
- * are true:
- * 1) The cursor is not positioned at index 0.
- * 2) The digit before the current cursor position matches the current digit.
- *
- * @param digit to remove from the digits view.
- */
- private void removePreviousDigitIfPossible(char digit) {
- final int currentPosition = mDigits.getSelectionStart();
- if (currentPosition > 0 && digit == mDigits.getText().charAt(currentPosition - 1)) {
- mDigits.setSelection(currentPosition);
- mDigits.getText().delete(currentPosition - 1, currentPosition);
- }
- }
-
- public void callVoicemail() {
- DialerUtils.startActivityWithErrorToast(getActivity(),
- new CallIntentBuilder(CallUtil.getVoicemailUri())
- .setCallInitiationType(LogState.INITIATION_DIALPAD)
- .build());
- hideAndClearDialpad(false);
- }
-
- private void hideAndClearDialpad(boolean animate) {
- ((DialtactsActivity) getActivity()).hideDialpadFragment(animate, true);
- }
-
- public static class ErrorDialogFragment extends DialogFragment {
- private int mTitleResId;
- private int mMessageResId;
-
- private static final String ARG_TITLE_RES_ID = "argTitleResId";
- private static final String ARG_MESSAGE_RES_ID = "argMessageResId";
-
- public static ErrorDialogFragment newInstance(int messageResId) {
- return newInstance(0, messageResId);
- }
-
- public static ErrorDialogFragment newInstance(int titleResId, int messageResId) {
- final ErrorDialogFragment fragment = new ErrorDialogFragment();
- final Bundle args = new Bundle();
- args.putInt(ARG_TITLE_RES_ID, titleResId);
- args.putInt(ARG_MESSAGE_RES_ID, messageResId);
- fragment.setArguments(args);
- return fragment;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mTitleResId = getArguments().getInt(ARG_TITLE_RES_ID);
- mMessageResId = getArguments().getInt(ARG_MESSAGE_RES_ID);
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- if (mTitleResId != 0) {
- builder.setTitle(mTitleResId);
- }
- if (mMessageResId != 0) {
- builder.setMessage(mMessageResId);
- }
- builder.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dismiss();
- }
- });
- return builder.create();
- }
- }
-
- /**
- * In most cases, when the dial button is pressed, there is a
- * number in digits area. Pack it in the intent, start the
- * outgoing call broadcast as a separate task and finish this
- * activity.
- *
- * When there is no digit and the phone is CDMA and off hook,
- * we're sending a blank flash for CDMA. CDMA networks use Flash
- * messages when special processing needs to be done, mainly for
- * 3-way or call waiting scenarios. Presumably, here we're in a
- * special 3-way scenario where the network needs a blank flash
- * before being able to add the new participant. (This is not the
- * case with all 3-way calls, just certain CDMA infrastructures.)
- *
- * Otherwise, there is no digit, display the last dialed
- * number. Don't finish since the user may want to edit it. The
- * user needs to press the dial button again, to dial it (general
- * case described above).
- */
- private void handleDialButtonPressed() {
- if (isDigitsEmpty()) { // No number entered.
- handleDialButtonClickWithEmptyDigits();
- } else {
- final String number = mDigits.getText().toString();
-
- // "persist.radio.otaspdial" is a temporary hack needed for one carrier's automated
- // test equipment.
- // TODO: clean it up.
- if (number != null
- && !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
- && number.matches(mProhibitedPhoneNumberRegexp)) {
- Log.i(TAG, "The phone number is prohibited explicitly by a rule.");
- if (getActivity() != null) {
- DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
- R.string.dialog_phone_call_prohibited_message);
- dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
- }
-
- // Clear the digits just in case.
- clearDialpad();
- } else {
- final Intent intent = new CallIntentBuilder(number).
- setCallInitiationType(LogState.INITIATION_DIALPAD)
- .build();
- DialerUtils.startActivityWithErrorToast(getActivity(), intent);
- hideAndClearDialpad(false);
- }
- }
- }
-
- public void clearDialpad() {
- if (mDigits != null) {
- mDigits.getText().clear();
- }
- }
-
- private void handleDialButtonClickWithEmptyDigits() {
- if (phoneIsCdma() && isPhoneInUse()) {
- // TODO: Move this logic into services/Telephony
- //
- // This is really CDMA specific. On GSM is it possible
- // to be off hook and wanted to add a 3rd party using
- // the redial feature.
- startActivity(newFlashIntent());
- } else {
- if (!TextUtils.isEmpty(mLastNumberDialed)) {
- // Recall the last number dialed.
- mDigits.setText(mLastNumberDialed);
-
- // ...and move the cursor to the end of the digits string,
- // so you'll be able to delete digits using the Delete
- // button (just as if you had typed the number manually.)
- //
- // Note we use mDigits.getText().length() here, not
- // mLastNumberDialed.length(), since the EditText widget now
- // contains a *formatted* version of mLastNumberDialed (due to
- // mTextWatcher) and its length may have changed.
- mDigits.setSelection(mDigits.getText().length());
- } else {
- // There's no "last number dialed" or the
- // background query is still running. There's
- // nothing useful for the Dial button to do in
- // this case. Note: with a soft dial button, this
- // can never happens since the dial button is
- // disabled under these conditons.
- playTone(ToneGenerator.TONE_PROP_NACK);
- }
- }
- }
-
- /**
- * Plays the specified tone for TONE_LENGTH_MS milliseconds.
- */
- private void playTone(int tone) {
- playTone(tone, TONE_LENGTH_MS);
- }
-
- /**
- * Play the specified tone for the specified milliseconds
- *
- * The tone is played locally, using the audio stream for phone calls.
- * Tones are played only if the "Audible touch tones" user preference
- * is checked, and are NOT played if the device is in silent mode.
- *
- * The tone length can be -1, meaning "keep playing the tone." If the caller does so, it should
- * call stopTone() afterward.
- *
- * @param tone a tone code from {@link ToneGenerator}
- * @param durationMs tone length.
- */
- private void playTone(int tone, int durationMs) {
- // if local tone playback is disabled, just return.
- if (!mDTMFToneEnabled) {
- return;
- }
-
- // Also do nothing if the phone is in silent mode.
- // We need to re-check the ringer mode for *every* playTone()
- // call, rather than keeping a local flag that's updated in
- // onResume(), since it's possible to toggle silent mode without
- // leaving the current activity (via the ENDCALL-longpress menu.)
- AudioManager audioManager =
- (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
- int ringerMode = audioManager.getRingerMode();
- if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
- || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
- return;
- }
-
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator == null) {
- Log.w(TAG, "playTone: mToneGenerator == null, tone: " + tone);
- return;
- }
-
- // Start the new tone (will stop any playing tone)
- mToneGenerator.startTone(tone, durationMs);
- }
- }
-
- /**
- * Stop the tone if it is played.
- */
- private void stopTone() {
- // if local tone playback is disabled, just return.
- if (!mDTMFToneEnabled) {
- return;
- }
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator == null) {
- Log.w(TAG, "stopTone: mToneGenerator == null");
- return;
- }
- mToneGenerator.stopTone();
- }
- }
-
- /**
- * Brings up the "dialpad chooser" UI in place of the usual Dialer
- * elements (the textfield/button and the dialpad underneath).
- *
- * We show this UI if the user brings up the Dialer while a call is
- * already in progress, since there's a good chance we got here
- * accidentally (and the user really wanted the in-call dialpad instead).
- * So in this situation we display an intermediate UI that lets the user
- * explicitly choose between the in-call dialpad ("Use touch tone
- * keypad") and the regular Dialer ("Add call"). (Or, the option "Return
- * to call in progress" just goes back to the in-call UI with no dialpad
- * at all.)
- *
- * @param enabled If true, show the "dialpad chooser" instead
- * of the regular Dialer UI
- */
- private void showDialpadChooser(boolean enabled) {
- if (getActivity() == null) {
- return;
- }
- // Check if onCreateView() is already called by checking one of View objects.
- if (!isLayoutReady()) {
- return;
- }
-
- if (enabled) {
- Log.d(TAG, "Showing dialpad chooser!");
- if (mDialpadView != null) {
- mDialpadView.setVisibility(View.GONE);
- }
-
- mFloatingActionButtonController.setVisible(false);
- mDialpadChooser.setVisibility(View.VISIBLE);
-
- // Instantiate the DialpadChooserAdapter and hook it up to the
- // ListView. We do this only once.
- if (mDialpadChooserAdapter == null) {
- mDialpadChooserAdapter = new DialpadChooserAdapter(getActivity());
- }
- mDialpadChooser.setAdapter(mDialpadChooserAdapter);
- } else {
- Log.d(TAG, "Displaying normal Dialer UI.");
- if (mDialpadView != null) {
- mDialpadView.setVisibility(View.VISIBLE);
- } else {
- mDigits.setVisibility(View.VISIBLE);
- }
-
- mFloatingActionButtonController.setVisible(true);
- mDialpadChooser.setVisibility(View.GONE);
- }
- }
-
- /**
- * @return true if we're currently showing the "dialpad chooser" UI.
- */
- private boolean isDialpadChooserVisible() {
- return mDialpadChooser.getVisibility() == View.VISIBLE;
- }
-
- /**
- * Simple list adapter, binding to an icon + text label
- * for each item in the "dialpad chooser" list.
- */
- private static class DialpadChooserAdapter extends BaseAdapter {
- private LayoutInflater mInflater;
-
- // Simple struct for a single "choice" item.
- static class ChoiceItem {
- String text;
- Bitmap icon;
- int id;
-
- public ChoiceItem(String s, Bitmap b, int i) {
- text = s;
- icon = b;
- id = i;
- }
- }
-
- // IDs for the possible "choices":
- static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
- static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
- static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
-
- private static final int NUM_ITEMS = 3;
- private ChoiceItem mChoiceItems[] = new ChoiceItem[NUM_ITEMS];
-
- public DialpadChooserAdapter(Context context) {
- // Cache the LayoutInflate to avoid asking for a new one each time.
- mInflater = LayoutInflater.from(context);
-
- // Initialize the possible choices.
- // TODO: could this be specified entirely in XML?
-
- // - "Use touch tone keypad"
- mChoiceItems[0] = new ChoiceItem(
- context.getString(R.string.dialer_useDtmfDialpad),
- BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_dialer_fork_tt_keypad),
- DIALPAD_CHOICE_USE_DTMF_DIALPAD);
-
- // - "Return to call in progress"
- mChoiceItems[1] = new ChoiceItem(
- context.getString(R.string.dialer_returnToInCallScreen),
- BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_dialer_fork_current_call),
- DIALPAD_CHOICE_RETURN_TO_CALL);
-
- // - "Add call"
- mChoiceItems[2] = new ChoiceItem(
- context.getString(R.string.dialer_addAnotherCall),
- BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_dialer_fork_add_call),
- DIALPAD_CHOICE_ADD_NEW_CALL);
- }
-
- @Override
- public int getCount() {
- return NUM_ITEMS;
- }
-
- /**
- * Return the ChoiceItem for a given position.
- */
- @Override
- public Object getItem(int position) {
- return mChoiceItems[position];
- }
-
- /**
- * Return a unique ID for each possible choice.
- */
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- /**
- * Make a view for each row.
- */
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // When convertView is non-null, we can reuse it (there's no need
- // to reinflate it.)
- if (convertView == null) {
- convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
- }
-
- TextView text = (TextView) convertView.findViewById(R.id.text);
- text.setText(mChoiceItems[position].text);
-
- ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
- icon.setImageBitmap(mChoiceItems[position].icon);
-
- return convertView;
- }
- }
-
- /**
- * Handle clicks from the dialpad chooser.
- */
- @Override
- public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
- DialpadChooserAdapter.ChoiceItem item =
- (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
- int itemId = item.id;
- if (itemId == DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD) {// Log.i(TAG, "DIALPAD_CHOICE_USE_DTMF_DIALPAD");
- // Fire off an intent to go back to the in-call UI
- // with the dialpad visible.
- returnToInCallScreen(true);
- } else if (itemId == DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL) {// Log.i(TAG, "DIALPAD_CHOICE_RETURN_TO_CALL");
- // Fire off an intent to go back to the in-call UI
- // (with the dialpad hidden).
- returnToInCallScreen(false);
- } else if (itemId == DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL) {// Log.i(TAG, "DIALPAD_CHOICE_ADD_NEW_CALL");
- // Ok, guess the user really did want to be here (in the
- // regular Dialer) after all. Bring back the normal Dialer UI.
- showDialpadChooser(false);
- } else {
- Log.w(TAG, "onItemClick: unexpected itemId: " + itemId);
- }
- }
-
- /**
- * Returns to the in-call UI (where there's presumably a call in
- * progress) in response to the user selecting "use touch tone keypad"
- * or "return to call" from the dialpad chooser.
- */
- private void returnToInCallScreen(boolean showDialpad) {
- TelecomUtil.showInCallScreen(getActivity(), showDialpad);
-
- // Finally, finish() ourselves so that we don't stay on the
- // activity stack.
- // Note that we do this whether or not the showCallScreenWithDialpad()
- // call above had any effect or not! (That call is a no-op if the
- // phone is idle, which can happen if the current call ends while
- // the dialpad chooser is up. In this case we can't show the
- // InCallScreen, and there's no point staying here in the Dialer,
- // so we just take the user back where he came from...)
- getActivity().finish();
- }
-
- /**
- * @return true if the phone is "in use", meaning that at least one line
- * is active (ie. off hook or ringing or dialing, or on hold).
- */
- private boolean isPhoneInUse() {
- final Context context = getActivity();
- if (context != null) {
- return TelecomUtil.isInCall(context);
- }
- return false;
- }
-
- /**
- * @return true if the phone is a CDMA phone type
- */
- private boolean phoneIsCdma() {
- return getTelephonyManager().getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA;
- }
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- int resId = item.getItemId();
- if (resId == R.id.menu_2s_pause) {
- updateDialString(PAUSE);
- return true;
- } else if (resId == R.id.menu_add_wait) {
- updateDialString(WAIT);
- return true;
- } else if (resId == R.id.menu_call_with_note) {
- CallSubjectDialog.start(getActivity(), mDigits.getText().toString());
- hideAndClearDialpad(false);
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Updates the dial string (mDigits) after inserting a Pause character (,)
- * or Wait character (;).
- */
- private void updateDialString(char newDigit) {
- if (newDigit != WAIT && newDigit != PAUSE) {
- throw new IllegalArgumentException(
- "Not expected for anything other than PAUSE & WAIT");
- }
-
- int selectionStart;
- int selectionEnd;
-
- // SpannableStringBuilder editable_text = new SpannableStringBuilder(mDigits.getText());
- int anchor = mDigits.getSelectionStart();
- int point = mDigits.getSelectionEnd();
-
- selectionStart = Math.min(anchor, point);
- selectionEnd = Math.max(anchor, point);
-
- if (selectionStart == -1) {
- selectionStart = selectionEnd = mDigits.length();
- }
-
- Editable digits = mDigits.getText();
-
- if (canAddDigit(digits, selectionStart, selectionEnd, newDigit)) {
- digits.replace(selectionStart, selectionEnd, Character.toString(newDigit));
-
- if (selectionStart != selectionEnd) {
- // Unselect: back to a regular cursor, just pass the character inserted.
- mDigits.setSelection(selectionStart + 1);
- }
- }
- }
-
- /**
- * Update the enabledness of the "Dial" and "Backspace" buttons if applicable.
- */
- private void updateDeleteButtonEnabledState() {
- if (getActivity() == null) {
- return;
- }
- final boolean digitsNotEmpty = !isDigitsEmpty();
- mDelete.setEnabled(digitsNotEmpty);
- }
-
- /**
- * Handle transitions for the menu button depending on the state of the digits edit text.
- * Transition out when going from digits to no digits and transition in when the first digit
- * is pressed.
- * @param transitionIn True if transitioning in, False if transitioning out
- */
- private void updateMenuOverflowButton(boolean transitionIn) {
- mOverflowMenuButton = mDialpadView.getOverflowMenuButton();
- if (transitionIn) {
- AnimUtils.fadeIn(mOverflowMenuButton, AnimUtils.DEFAULT_DURATION);
- } else {
- AnimUtils.fadeOut(mOverflowMenuButton, AnimUtils.DEFAULT_DURATION);
- }
- }
-
- /**
- * Check if voicemail is enabled/accessible.
- *
- * @return true if voicemail is enabled and accessible. Note that this can be false
- * "temporarily" after the app boot.
- */
- private boolean isVoicemailAvailable() {
- try {
- PhoneAccountHandle defaultUserSelectedAccount =
- TelecomUtil.getDefaultOutgoingPhoneAccount(getActivity(),
- PhoneAccount.SCHEME_VOICEMAIL);
- if (defaultUserSelectedAccount == null) {
- // In a single-SIM phone, there is no default outgoing phone account selected by
- // the user, so just call TelephonyManager#getVoicemailNumber directly.
- return !TextUtils.isEmpty(getTelephonyManager().getVoiceMailNumber());
- } else {
- return !TextUtils.isEmpty(TelecomUtil.getVoicemailNumber(getActivity(),
- defaultUserSelectedAccount));
- }
- } catch (SecurityException se) {
- // Possibly no READ_PHONE_STATE privilege.
- Log.w(TAG, "SecurityException is thrown. Maybe privilege isn't sufficient.");
- }
- return false;
- }
-
- /**
- * Returns true of the newDigit parameter can be added at the current selection
- * point, otherwise returns false.
- * Only prevents input of WAIT and PAUSE digits at an unsupported position.
- * Fails early if start == -1 or start is larger than end.
- */
- @VisibleForTesting
- /* package */ static boolean canAddDigit(CharSequence digits, int start, int end,
- char newDigit) {
- if(newDigit != WAIT && newDigit != PAUSE) {
- throw new IllegalArgumentException(
- "Should not be called for anything other than PAUSE & WAIT");
- }
-
- // False if no selection, or selection is reversed (end < start)
- if (start == -1 || end < start) {
- return false;
- }
-
- // unsupported selection-out-of-bounds state
- if (start > digits.length() || end > digits.length()) return false;
-
- // Special digit cannot be the first digit
- if (start == 0) return false;
-
- if (newDigit == WAIT) {
- // preceding char is ';' (WAIT)
- if (digits.charAt(start - 1) == WAIT) return false;
-
- // next char is ';' (WAIT)
- if ((digits.length() > end) && (digits.charAt(end) == WAIT)) return false;
- }
-
- return true;
- }
-
- /**
- * @return true if the widget with the phone number digits is empty.
- */
- private boolean isDigitsEmpty() {
- return mDigits.length() == 0;
- }
-
- /**
- * Starts the asyn query to get the last dialed/outgoing
- * number. When the background query finishes, mLastNumberDialed
- * is set to the last dialed number or an empty string if none
- * exists yet.
- */
- private void queryLastOutgoingCall() {
- mLastNumberDialed = EMPTY_NUMBER;
- if (!PermissionsUtil.hasPhonePermissions(getActivity())) {
- return;
- }
- CallLogAsync.GetLastOutgoingCallArgs lastCallArgs =
- new CallLogAsync.GetLastOutgoingCallArgs(
- getActivity(),
- new CallLogAsync.OnLastOutgoingCallComplete() {
- @Override
- public void lastOutgoingCall(String number) {
- // TODO: Filter out emergency numbers if
- // the carrier does not want redial for
- // these.
- // If the fragment has already been detached since the last time
- // we called queryLastOutgoingCall in onResume there is no point
- // doing anything here.
- if (getActivity() == null) return;
- mLastNumberDialed = number;
- updateDeleteButtonEnabledState();
- }
- });
- mCallLog.getLastOutgoingCall(lastCallArgs);
- }
-
- private Intent newFlashIntent() {
- final Intent intent = new CallIntentBuilder(EMPTY_NUMBER).build();
- intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
- return intent;
- }
-
- @Override
- public void onHiddenChanged(boolean hidden) {
- super.onHiddenChanged(hidden);
- final DialtactsActivity activity = (DialtactsActivity) getActivity();
- final DialpadView dialpadView = (DialpadView) getView().findViewById(R.id.dialpad_view);
- if (activity == null) return;
- if (!hidden && !isDialpadChooserVisible()) {
- if (mAnimate) {
- dialpadView.animateShow();
- }
- mFloatingActionButtonController.setVisible(false);
- mFloatingActionButtonController.scaleIn(mAnimate ? mDialpadSlideInDuration : 0);
- activity.onDialpadShown();
- mDigits.requestFocus();
- }
- if (hidden) {
- if (mAnimate) {
- mFloatingActionButtonController.scaleOut();
- } else {
- mFloatingActionButtonController.setVisible(false);
- }
- }
- }
-
- public void setAnimate(boolean value) {
- mAnimate = value;
- }
-
- public boolean getAnimate() {
- return mAnimate;
- }
-
- public void setYFraction(float yFraction) {
- ((DialpadSlidingRelativeLayout) getView()).setYFraction(yFraction);
- }
-
- public int getDialpadHeight() {
- if (mDialpadView == null) {
- return 0;
- }
- return mDialpadView.getHeight();
- }
-
- public void process_quote_emergency_unquote(String query) {
- if (PseudoEmergencyAnimator.PSEUDO_EMERGENCY_NUMBER.equals(query)) {
- if (mPseudoEmergencyAnimator == null) {
- mPseudoEmergencyAnimator = new PseudoEmergencyAnimator(
- new PseudoEmergencyAnimator.ViewProvider() {
- @Override
- public View getView() {
- return DialpadFragment.this.getView();
- }
- });
- }
- mPseudoEmergencyAnimator.start();
- } else {
- if (mPseudoEmergencyAnimator != null) {
- mPseudoEmergencyAnimator.end();
- }
- }
- }
-
-}
diff --git a/src/com/android/dialer/dialpad/LatinSmartDialMap.java b/src/com/android/dialer/dialpad/LatinSmartDialMap.java
deleted file mode 100644
index ef1ec0adc..000000000
--- a/src/com/android/dialer/dialpad/LatinSmartDialMap.java
+++ /dev/null
@@ -1,413 +0,0 @@
-package com.android.dialer.dialpad;
-
-public class LatinSmartDialMap implements SmartDialMap {
-
- private static final char[] LATIN_LETTERS_TO_DIGITS = {
- '2', '2', '2', // A,B,C -> 2
- '3', '3', '3', // D,E,F -> 3
- '4', '4', '4', // G,H,I -> 4
- '5', '5', '5', // J,K,L -> 5
- '6', '6', '6', // M,N,O -> 6
- '7', '7', '7', '7', // P,Q,R,S -> 7
- '8', '8', '8', // T,U,V -> 8
- '9', '9', '9', '9' // W,X,Y,Z -> 9
- };
-
- @Override
- public boolean isValidDialpadAlphabeticChar(char ch) {
- return (ch >= 'a' && ch <= 'z');
- }
-
- @Override
- public boolean isValidDialpadNumericChar(char ch) {
- return (ch >= '0' && ch <= '9');
- }
-
- @Override
- public boolean isValidDialpadCharacter(char ch) {
- return (isValidDialpadAlphabeticChar(ch) || isValidDialpadNumericChar(ch));
- }
-
- /*
- * The switch statement in this function was generated using the python code:
- * from unidecode import unidecode
- * for i in range(192, 564):
- * char = unichr(i)
- * decoded = unidecode(char)
- * # Unicode characters that decompose into multiple characters i.e.
- * # into ss are not supported for now
- * if (len(decoded) == 1 and decoded.isalpha()):
- * print "case '" + char + "': return '" + unidecode(char) + "';"
- *
- * This gives us a way to map characters containing accents/diacritics to their
- * alphabetic equivalents. The unidecode library can be found at:
- * http://pypi.python.org/pypi/Unidecode/0.04.1
- *
- * Also remaps all upper case latin characters to their lower case equivalents.
- */
- @Override
- public char normalizeCharacter(char ch) {
- switch (ch) {
- case 'À': return 'a';
- case 'Á': return 'a';
- case 'Â': return 'a';
- case 'Ã': return 'a';
- case 'Ä': return 'a';
- case 'Å': return 'a';
- case 'Ç': return 'c';
- case 'È': return 'e';
- case 'É': return 'e';
- case 'Ê': return 'e';
- case 'Ë': return 'e';
- case 'Ì': return 'i';
- case 'Í': return 'i';
- case 'Î': return 'i';
- case 'Ï': return 'i';
- case 'Ð': return 'd';
- case 'Ñ': return 'n';
- case 'Ò': return 'o';
- case 'Ó': return 'o';
- case 'Ô': return 'o';
- case 'Õ': return 'o';
- case 'Ö': return 'o';
- case '×': return 'x';
- case 'Ø': return 'o';
- case 'Ù': return 'u';
- case 'Ú': return 'u';
- case 'Û': return 'u';
- case 'Ü': return 'u';
- case 'Ý': return 'u';
- case 'à': return 'a';
- case 'á': return 'a';
- case 'â': return 'a';
- case 'ã': return 'a';
- case 'ä': return 'a';
- case 'å': return 'a';
- case 'ç': return 'c';
- case 'è': return 'e';
- case 'é': return 'e';
- case 'ê': return 'e';
- case 'ë': return 'e';
- case 'ì': return 'i';
- case 'í': return 'i';
- case 'î': return 'i';
- case 'ï': return 'i';
- case 'ð': return 'd';
- case 'ñ': return 'n';
- case 'ò': return 'o';
- case 'ó': return 'o';
- case 'ô': return 'o';
- case 'õ': return 'o';
- case 'ö': return 'o';
- case 'ø': return 'o';
- case 'ù': return 'u';
- case 'ú': return 'u';
- case 'û': return 'u';
- case 'ü': return 'u';
- case 'ý': return 'y';
- case 'ÿ': return 'y';
- case 'Ā': return 'a';
- case 'ā': return 'a';
- case 'Ă': return 'a';
- case 'ă': return 'a';
- case 'Ą': return 'a';
- case 'ą': return 'a';
- case 'Ć': return 'c';
- case 'ć': return 'c';
- case 'Ĉ': return 'c';
- case 'ĉ': return 'c';
- case 'Ċ': return 'c';
- case 'ċ': return 'c';
- case 'Č': return 'c';
- case 'č': return 'c';
- case 'Ď': return 'd';
- case 'ď': return 'd';
- case 'Đ': return 'd';
- case 'đ': return 'd';
- case 'Ē': return 'e';
- case 'ē': return 'e';
- case 'Ĕ': return 'e';
- case 'ĕ': return 'e';
- case 'Ė': return 'e';
- case 'ė': return 'e';
- case 'Ę': return 'e';
- case 'ę': return 'e';
- case 'Ě': return 'e';
- case 'ě': return 'e';
- case 'Ĝ': return 'g';
- case 'ĝ': return 'g';
- case 'Ğ': return 'g';
- case 'ğ': return 'g';
- case 'Ġ': return 'g';
- case 'ġ': return 'g';
- case 'Ģ': return 'g';
- case 'ģ': return 'g';
- case 'Ĥ': return 'h';
- case 'ĥ': return 'h';
- case 'Ħ': return 'h';
- case 'ħ': return 'h';
- case 'Ĩ': return 'i';
- case 'ĩ': return 'i';
- case 'Ī': return 'i';
- case 'ī': return 'i';
- case 'Ĭ': return 'i';
- case 'ĭ': return 'i';
- case 'Į': return 'i';
- case 'į': return 'i';
- case 'İ': return 'i';
- case 'ı': return 'i';
- case 'Ĵ': return 'j';
- case 'ĵ': return 'j';
- case 'Ķ': return 'k';
- case 'ķ': return 'k';
- case 'ĸ': return 'k';
- case 'Ĺ': return 'l';
- case 'ĺ': return 'l';
- case 'Ļ': return 'l';
- case 'ļ': return 'l';
- case 'Ľ': return 'l';
- case 'ľ': return 'l';
- case 'Ŀ': return 'l';
- case 'ŀ': return 'l';
- case 'Ł': return 'l';
- case 'ł': return 'l';
- case 'Ń': return 'n';
- case 'ń': return 'n';
- case 'Ņ': return 'n';
- case 'ņ': return 'n';
- case 'Ň': return 'n';
- case 'ň': return 'n';
- case 'Ō': return 'o';
- case 'ō': return 'o';
- case 'Ŏ': return 'o';
- case 'ŏ': return 'o';
- case 'Ő': return 'o';
- case 'ő': return 'o';
- case 'Ŕ': return 'r';
- case 'ŕ': return 'r';
- case 'Ŗ': return 'r';
- case 'ŗ': return 'r';
- case 'Ř': return 'r';
- case 'ř': return 'r';
- case 'Ś': return 's';
- case 'ś': return 's';
- case 'Ŝ': return 's';
- case 'ŝ': return 's';
- case 'Ş': return 's';
- case 'ş': return 's';
- case 'Š': return 's';
- case 'š': return 's';
- case 'Ţ': return 't';
- case 'ţ': return 't';
- case 'Ť': return 't';
- case 'ť': return 't';
- case 'Ŧ': return 't';
- case 'ŧ': return 't';
- case 'Ũ': return 'u';
- case 'ũ': return 'u';
- case 'Ū': return 'u';
- case 'ū': return 'u';
- case 'Ŭ': return 'u';
- case 'ŭ': return 'u';
- case 'Ů': return 'u';
- case 'ů': return 'u';
- case 'Ű': return 'u';
- case 'ű': return 'u';
- case 'Ų': return 'u';
- case 'ų': return 'u';
- case 'Ŵ': return 'w';
- case 'ŵ': return 'w';
- case 'Ŷ': return 'y';
- case 'ŷ': return 'y';
- case 'Ÿ': return 'y';
- case 'Ź': return 'z';
- case 'ź': return 'z';
- case 'Ż': return 'z';
- case 'ż': return 'z';
- case 'Ž': return 'z';
- case 'ž': return 'z';
- case 'ſ': return 's';
- case 'ƀ': return 'b';
- case 'Ɓ': return 'b';
- case 'Ƃ': return 'b';
- case 'ƃ': return 'b';
- case 'Ɔ': return 'o';
- case 'Ƈ': return 'c';
- case 'ƈ': return 'c';
- case 'Ɖ': return 'd';
- case 'Ɗ': return 'd';
- case 'Ƌ': return 'd';
- case 'ƌ': return 'd';
- case 'ƍ': return 'd';
- case 'Ɛ': return 'e';
- case 'Ƒ': return 'f';
- case 'ƒ': return 'f';
- case 'Ɠ': return 'g';
- case 'Ɣ': return 'g';
- case 'Ɩ': return 'i';
- case 'Ɨ': return 'i';
- case 'Ƙ': return 'k';
- case 'ƙ': return 'k';
- case 'ƚ': return 'l';
- case 'ƛ': return 'l';
- case 'Ɯ': return 'w';
- case 'Ɲ': return 'n';
- case 'ƞ': return 'n';
- case 'Ɵ': return 'o';
- case 'Ơ': return 'o';
- case 'ơ': return 'o';
- case 'Ƥ': return 'p';
- case 'ƥ': return 'p';
- case 'ƫ': return 't';
- case 'Ƭ': return 't';
- case 'ƭ': return 't';
- case 'Ʈ': return 't';
- case 'Ư': return 'u';
- case 'ư': return 'u';
- case 'Ʊ': return 'y';
- case 'Ʋ': return 'v';
- case 'Ƴ': return 'y';
- case 'ƴ': return 'y';
- case 'Ƶ': return 'z';
- case 'ƶ': return 'z';
- case 'ƿ': return 'w';
- case 'Ǎ': return 'a';
- case 'ǎ': return 'a';
- case 'Ǐ': return 'i';
- case 'ǐ': return 'i';
- case 'Ǒ': return 'o';
- case 'ǒ': return 'o';
- case 'Ǔ': return 'u';
- case 'ǔ': return 'u';
- case 'Ǖ': return 'u';
- case 'ǖ': return 'u';
- case 'Ǘ': return 'u';
- case 'ǘ': return 'u';
- case 'Ǚ': return 'u';
- case 'ǚ': return 'u';
- case 'Ǜ': return 'u';
- case 'ǜ': return 'u';
- case 'Ǟ': return 'a';
- case 'ǟ': return 'a';
- case 'Ǡ': return 'a';
- case 'ǡ': return 'a';
- case 'Ǥ': return 'g';
- case 'ǥ': return 'g';
- case 'Ǧ': return 'g';
- case 'ǧ': return 'g';
- case 'Ǩ': return 'k';
- case 'ǩ': return 'k';
- case 'Ǫ': return 'o';
- case 'ǫ': return 'o';
- case 'Ǭ': return 'o';
- case 'ǭ': return 'o';
- case 'ǰ': return 'j';
- case 'Dz': return 'd';
- case 'Ǵ': return 'g';
- case 'ǵ': return 'g';
- case 'Ƿ': return 'w';
- case 'Ǹ': return 'n';
- case 'ǹ': return 'n';
- case 'Ǻ': return 'a';
- case 'ǻ': return 'a';
- case 'Ǿ': return 'o';
- case 'ǿ': return 'o';
- case 'Ȁ': return 'a';
- case 'ȁ': return 'a';
- case 'Ȃ': return 'a';
- case 'ȃ': return 'a';
- case 'Ȅ': return 'e';
- case 'ȅ': return 'e';
- case 'Ȇ': return 'e';
- case 'ȇ': return 'e';
- case 'Ȉ': return 'i';
- case 'ȉ': return 'i';
- case 'Ȋ': return 'i';
- case 'ȋ': return 'i';
- case 'Ȍ': return 'o';
- case 'ȍ': return 'o';
- case 'Ȏ': return 'o';
- case 'ȏ': return 'o';
- case 'Ȑ': return 'r';
- case 'ȑ': return 'r';
- case 'Ȓ': return 'r';
- case 'ȓ': return 'r';
- case 'Ȕ': return 'u';
- case 'ȕ': return 'u';
- case 'Ȗ': return 'u';
- case 'ȗ': return 'u';
- case 'Ș': return 's';
- case 'ș': return 's';
- case 'Ț': return 't';
- case 'ț': return 't';
- case 'Ȝ': return 'y';
- case 'ȝ': return 'y';
- case 'Ȟ': return 'h';
- case 'ȟ': return 'h';
- case 'Ȥ': return 'z';
- case 'ȥ': return 'z';
- case 'Ȧ': return 'a';
- case 'ȧ': return 'a';
- case 'Ȩ': return 'e';
- case 'ȩ': return 'e';
- case 'Ȫ': return 'o';
- case 'ȫ': return 'o';
- case 'Ȭ': return 'o';
- case 'ȭ': return 'o';
- case 'Ȯ': return 'o';
- case 'ȯ': return 'o';
- case 'Ȱ': return 'o';
- case 'ȱ': return 'o';
- case 'Ȳ': return 'y';
- case 'ȳ': return 'y';
- case 'A': return 'a';
- case 'B': return 'b';
- case 'C': return 'c';
- case 'D': return 'd';
- case 'E': return 'e';
- case 'F': return 'f';
- case 'G': return 'g';
- case 'H': return 'h';
- case 'I': return 'i';
- case 'J': return 'j';
- case 'K': return 'k';
- case 'L': return 'l';
- case 'M': return 'm';
- case 'N': return 'n';
- case 'O': return 'o';
- case 'P': return 'p';
- case 'Q': return 'q';
- case 'R': return 'r';
- case 'S': return 's';
- case 'T': return 't';
- case 'U': return 'u';
- case 'V': return 'v';
- case 'W': return 'w';
- case 'X': return 'x';
- case 'Y': return 'y';
- case 'Z': return 'z';
- default:
- return ch;
- }
- }
-
- @Override
- public byte getDialpadIndex(char ch) {
- if (ch >= '0' && ch <= '9') {
- return (byte) (ch - '0');
- } else if (ch >= 'a' && ch <= 'z') {
- return (byte) (LATIN_LETTERS_TO_DIGITS[ch - 'a'] - '0');
- } else {
- return -1;
- }
- }
-
- @Override
- public char getDialpadNumericCharacter(char ch) {
- if (ch >= 'a' && ch <= 'z') {
- return LATIN_LETTERS_TO_DIGITS[ch - 'a'];
- }
- return ch;
- }
-
-}
diff --git a/src/com/android/dialer/dialpad/PseudoEmergencyAnimator.java b/src/com/android/dialer/dialpad/PseudoEmergencyAnimator.java
deleted file mode 100644
index d4f32b5d4..000000000
--- a/src/com/android/dialer/dialpad/PseudoEmergencyAnimator.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * 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.dialer.dialpad;
-
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.ArgbEvaluator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.LightingColorFilter;
-import android.os.Handler;
-import android.os.Vibrator;
-import android.view.View;
-
-import com.android.dialer.R;
-
-/**
- * Animates the dial button on "emergency" phone numbers.
- */
-public class PseudoEmergencyAnimator {
- public interface ViewProvider {
- View getView();
- }
-
- public static final String PSEUDO_EMERGENCY_NUMBER = "01189998819991197253";
-
- private static final int VIBRATE_LENGTH_MILLIS = 200;
- private static final int ITERATION_LENGTH_MILLIS = 1000;
- private static final int ANIMATION_ITERATION_COUNT = 6;
-
- private ViewProvider mViewProvider;
- private ValueAnimator mPseudoEmergencyColorAnimator;
-
- PseudoEmergencyAnimator(ViewProvider viewProvider) {
- mViewProvider = viewProvider;
- }
-
- public void destroy() {
- end();
- mViewProvider = null;
- }
-
- public void start() {
- if (mPseudoEmergencyColorAnimator == null) {
- Integer colorFrom = Color.BLUE;
- Integer colorTo = Color.RED;
- mPseudoEmergencyColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
-
- mPseudoEmergencyColorAnimator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- try {
- int color = (int) animator.getAnimatedValue();
- ColorFilter colorFilter =
- new LightingColorFilter(Color.BLACK, color);
-
- View floatingActionButtonContainer = getView().findViewById(
- R.id.dialpad_floating_action_button_container);
- if (floatingActionButtonContainer != null) {
- floatingActionButtonContainer.getBackground().setColorFilter(
- colorFilter);
- }
- } catch (Exception e) {
- animator.cancel();
- }
- }
- });
-
- mPseudoEmergencyColorAnimator.addListener(new AnimatorListener() {
- @Override
- public void onAnimationCancel(Animator animation) { }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- try {
- vibrate(VIBRATE_LENGTH_MILLIS);
- } catch (Exception e) {
- animation.cancel();
- }
- }
-
- @Override
- public void onAnimationStart(Animator animation) { }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- try {
- View floatingActionButtonContainer = getView().findViewById(
- R.id.dialpad_floating_action_button_container);
- if (floatingActionButtonContainer != null) {
- floatingActionButtonContainer.getBackground().clearColorFilter();
- }
-
- new Handler().postDelayed(new Runnable() {
- @Override public void run() {
- try {
- vibrate(VIBRATE_LENGTH_MILLIS);
- } catch (Exception e) {
- // ignored
- }
- }
- }, ITERATION_LENGTH_MILLIS);
- } catch (Exception e) {
- animation.cancel();
- }
- }
- });
-
- mPseudoEmergencyColorAnimator.setDuration(VIBRATE_LENGTH_MILLIS);
- mPseudoEmergencyColorAnimator.setRepeatMode(ValueAnimator.REVERSE);
- mPseudoEmergencyColorAnimator.setRepeatCount(ANIMATION_ITERATION_COUNT);
- }
- if (!mPseudoEmergencyColorAnimator.isStarted()) {
- mPseudoEmergencyColorAnimator.start();
- }
- }
-
- public void end() {
- if (mPseudoEmergencyColorAnimator != null && mPseudoEmergencyColorAnimator.isStarted()) {
- mPseudoEmergencyColorAnimator.end();
- }
- }
-
- private View getView() {
- return mViewProvider == null ? null : mViewProvider.getView();
- }
-
- private Context getContext() {
- View view = getView();
- return view != null ? view.getContext() : null;
- }
-
- private void vibrate(long milliseconds) {
- Context context = getContext();
- if (context != null) {
- Vibrator vibrator =
- (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
- if (vibrator != null) {
- vibrator.vibrate(milliseconds);
- }
- }
- }
-}
diff --git a/src/com/android/dialer/dialpad/SmartDialCursorLoader.java b/src/com/android/dialer/dialpad/SmartDialCursorLoader.java
deleted file mode 100644
index 93b649b6d..000000000
--- a/src/com/android/dialer/dialpad/SmartDialCursorLoader.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.dialpad;
-
-import android.content.AsyncTaskLoader;
-import android.content.Context;
-import android.content.Loader.ForceLoadContentObserver;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.net.Uri;
-import android.util.Log;
-
-import com.android.contacts.common.list.PhoneNumberListAdapter.PhoneQuery;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.database.DialerDatabaseHelper;
-import com.android.dialer.database.DialerDatabaseHelper.ContactNumber;
-import com.android.dialerbind.DatabaseHelperManager;
-
-import java.util.ArrayList;
-
-/**
- * Implements a Loader<Cursor> class to asynchronously load SmartDial search results.
- */
-public class SmartDialCursorLoader extends AsyncTaskLoader<Cursor> {
-
- private final String TAG = SmartDialCursorLoader.class.getSimpleName();
- private final boolean DEBUG = false;
-
- private final Context mContext;
-
- private Cursor mCursor;
-
- private String mQuery;
- private SmartDialNameMatcher mNameMatcher;
-
- private ForceLoadContentObserver mObserver;
-
- public SmartDialCursorLoader(Context context) {
- super(context);
- mContext = context;
- }
-
- /**
- * Configures the query string to be used to find SmartDial matches.
- * @param query The query string user typed.
- */
- public void configureQuery(String query) {
- if (DEBUG) {
- Log.v(TAG, "Configure new query to be " + query);
- }
- mQuery = SmartDialNameMatcher.normalizeNumber(query, SmartDialPrefix.getMap());
-
- /** Constructs a name matcher object for matching names. */
- mNameMatcher = new SmartDialNameMatcher(mQuery, SmartDialPrefix.getMap());
- }
-
- /**
- * Queries the SmartDial database and loads results in background.
- * @return Cursor of contacts that matches the SmartDial query.
- */
- @Override
- public Cursor loadInBackground() {
- if (DEBUG) {
- Log.v(TAG, "Load in background " + mQuery);
- }
-
- if (!PermissionsUtil.hasContactsPermissions(mContext)) {
- return new MatrixCursor(PhoneQuery.PROJECTION_PRIMARY);
- }
-
- /** Loads results from the database helper. */
- final DialerDatabaseHelper dialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(
- mContext);
- final ArrayList<ContactNumber> allMatches = dialerDatabaseHelper.getLooseMatches(mQuery,
- mNameMatcher);
-
- if (DEBUG) {
- Log.v(TAG, "Loaded matches " + String.valueOf(allMatches.size()));
- }
-
- /** Constructs a cursor for the returned array of results. */
- final MatrixCursor cursor = new MatrixCursor(PhoneQuery.PROJECTION_PRIMARY);
- Object[] row = new Object[PhoneQuery.PROJECTION_PRIMARY.length];
- for (ContactNumber contact : allMatches) {
- row[PhoneQuery.PHONE_ID] = contact.dataId;
- row[PhoneQuery.PHONE_NUMBER] = contact.phoneNumber;
- row[PhoneQuery.CONTACT_ID] = contact.id;
- row[PhoneQuery.LOOKUP_KEY] = contact.lookupKey;
- row[PhoneQuery.PHOTO_ID] = contact.photoId;
- row[PhoneQuery.DISPLAY_NAME] = contact.displayName;
- row[PhoneQuery.CARRIER_PRESENCE] = contact.carrierPresence;
- cursor.addRow(row);
- }
- return cursor;
- }
-
- @Override
- public void deliverResult(Cursor cursor) {
- if (isReset()) {
- /** The Loader has been reset; ignore the result and invalidate the data. */
- releaseResources(cursor);
- return;
- }
-
- /** Hold a reference to the old data so it doesn't get garbage collected. */
- Cursor oldCursor = mCursor;
- mCursor = cursor;
-
- if (mObserver == null) {
- mObserver = new ForceLoadContentObserver();
- mContext.getContentResolver().registerContentObserver(
- DialerDatabaseHelper.SMART_DIAL_UPDATED_URI, true, mObserver);
- }
-
- if (isStarted()) {
- /** If the Loader is in a started state, deliver the results to the client. */
- super.deliverResult(cursor);
- }
-
- /** Invalidate the old data as we don't need it any more. */
- if (oldCursor != null && oldCursor != cursor) {
- releaseResources(oldCursor);
- }
- }
-
- @Override
- protected void onStartLoading() {
- if (mCursor != null) {
- /** Deliver any previously loaded data immediately. */
- deliverResult(mCursor);
- }
- if (mCursor == null) {
- /** Force loads every time as our results change with queries. */
- forceLoad();
- }
- }
-
- @Override
- protected void onStopLoading() {
- /** The Loader is in a stopped state, so we should attempt to cancel the current load. */
- cancelLoad();
- }
-
- @Override
- protected void onReset() {
- /** Ensure the loader has been stopped. */
- onStopLoading();
-
- if (mObserver != null) {
- mContext.getContentResolver().unregisterContentObserver(mObserver);
- mObserver = null;
- }
-
- /** Release all previously saved query results. */
- if (mCursor != null) {
- releaseResources(mCursor);
- mCursor = null;
- }
- }
-
- @Override
- public void onCanceled(Cursor cursor) {
- super.onCanceled(cursor);
-
- if (mObserver != null) {
- mContext.getContentResolver().unregisterContentObserver(mObserver);
- mObserver = null;
- }
-
- /** The load has been canceled, so we should release the resources associated with 'data'.*/
- releaseResources(cursor);
- }
-
- private void releaseResources(Cursor cursor) {
- if (cursor != null) {
- cursor.close();
- }
- }
-}
diff --git a/src/com/android/dialer/dialpad/SmartDialMap.java b/src/com/android/dialer/dialpad/SmartDialMap.java
deleted file mode 100644
index b51891a8c..000000000
--- a/src/com/android/dialer/dialpad/SmartDialMap.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.android.dialer.dialpad;
-
-/**
- * Note: These methods currently take characters as arguments. For future planned language support,
- * they will need to be changed to use codepoints instead of characters.
- *
- * http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#codePointAt(int)
- *
- * If/when this change is made, LatinSmartDialMap(which operates on chars) will continue to work
- * by simply casting from a codepoint to a character.
- */
-public interface SmartDialMap {
- /*
- * Returns true if the provided character can be mapped to a key on the dialpad
- */
- public boolean isValidDialpadCharacter(char ch);
-
- /*
- * Returns true if the provided character is a letter, and can be mapped to a key on the dialpad
- */
- public boolean isValidDialpadAlphabeticChar(char ch);
-
- /*
- * Returns true if the provided character is a digit, and can be mapped to a key on the dialpad
- */
- public boolean isValidDialpadNumericChar(char ch);
-
- /*
- * Get the index of the key on the dialpad which the character corresponds to
- */
- public byte getDialpadIndex(char ch);
-
- /*
- * Get the actual numeric character on the dialpad which the character corresponds to
- */
- public char getDialpadNumericCharacter(char ch);
-
- /*
- * Converts uppercase characters to lower case ones, and on a best effort basis, strips accents
- * from accented characters.
- */
- public char normalizeCharacter(char ch);
-}
diff --git a/src/com/android/dialer/dialpad/SmartDialMatchPosition.java b/src/com/android/dialer/dialpad/SmartDialMatchPosition.java
deleted file mode 100644
index bab2c50d8..000000000
--- a/src/com/android/dialer/dialpad/SmartDialMatchPosition.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.dialer.dialpad;
-
-import android.util.Log;
-
-import java.util.ArrayList;
-
-/**
- * Stores information about a range of characters matched in a display name The integers
- * start and end indicate that the range start to end (exclusive) correspond to some characters
- * in the query. Used to highlight certain parts of the contact's display name to indicate that
- * those ranges matched the user's query.
- */
-public class SmartDialMatchPosition {
- private static final String TAG = SmartDialMatchPosition.class.getSimpleName();
-
- public int start;
- public int end;
-
- public SmartDialMatchPosition(int start, int end) {
- this.start = start;
- this.end = end;
- }
-
- private void advance(int toAdvance) {
- this.start += toAdvance;
- this.end += toAdvance;
- }
-
- /**
- * Used by {@link SmartDialNameMatcher} to advance the positions of a match position found in
- * a sub query.
- *
- * @param inList ArrayList of SmartDialMatchPositions to modify.
- * @param toAdvance Offset to modify by.
- */
- public static void advanceMatchPositions(ArrayList<SmartDialMatchPosition> inList,
- int toAdvance) {
- for (int i = 0; i < inList.size(); i++) {
- inList.get(i).advance(toAdvance);
- }
- }
-
- /**
- * Used mainly for debug purposes. Displays contents of an ArrayList of SmartDialMatchPositions.
- *
- * @param list ArrayList of SmartDialMatchPositions to print out in a human readable fashion.
- */
- public static void print(ArrayList<SmartDialMatchPosition> list) {
- for (int i = 0; i < list.size(); i ++) {
- SmartDialMatchPosition m = list.get(i);
- Log.d(TAG, "[" + m.start + "," + m.end + "]");
- }
- }
-}
diff --git a/src/com/android/dialer/dialpad/SmartDialNameMatcher.java b/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
deleted file mode 100644
index a54fe1618..000000000
--- a/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * 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.dialer.dialpad;
-
-import android.support.annotation.Nullable;
-import android.text.TextUtils;
-
-import com.android.dialer.dialpad.SmartDialPrefix.PhoneNumberTokens;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
-
-import java.util.ArrayList;
-
-/**
- * {@link #SmartDialNameMatcher} contains utility functions to remove accents from accented
- * characters and normalize a phone number. It also contains the matching logic that determines if
- * a contact's display name matches a numeric query. The boolean variable
- * {@link #ALLOW_INITIAL_MATCH} controls the behavior of the matching logic and determines
- * whether we allow matches like 57 - (J)ohn (S)mith.
- */
-public class SmartDialNameMatcher {
-
- private String mQuery;
-
- // Whether or not we allow matches like 57 - (J)ohn (S)mith
- private static final boolean ALLOW_INITIAL_MATCH = true;
-
- // The maximum length of the initial we will match - typically set to 1 to minimize false
- // positives
- private static final int INITIAL_LENGTH_LIMIT = 1;
-
- private final ArrayList<SmartDialMatchPosition> mMatchPositions = Lists.newArrayList();
-
- public static final SmartDialMap LATIN_SMART_DIAL_MAP = new LatinSmartDialMap();
-
- private final SmartDialMap mMap;
-
- private String mNameMatchMask = "";
- private String mPhoneNumberMatchMask = "";
-
- @VisibleForTesting
- public SmartDialNameMatcher(String query) {
- this(query, LATIN_SMART_DIAL_MAP);
- }
-
- public SmartDialNameMatcher(String query, SmartDialMap map) {
- mQuery = query;
- mMap = map;
- }
-
- /**
- * Constructs empty highlight mask. Bit 0 at a position means there is no match, Bit 1 means
- * there is a match and should be highlighted in the TextView.
- * @param builder StringBuilder object
- * @param length Length of the desired mask.
- */
- private void constructEmptyMask(StringBuilder builder, int length) {
- for (int i = 0; i < length; ++i) {
- builder.append("0");
- }
- }
-
- /**
- * Replaces the 0-bit at a position with 1-bit, indicating that there is a match.
- * @param builder StringBuilder object.
- * @param matchPos Match Positions to mask as 1.
- */
- private void replaceBitInMask(StringBuilder builder, SmartDialMatchPosition matchPos) {
- for (int i = matchPos.start; i < matchPos.end; ++i) {
- builder.replace(i, i + 1, "1");
- }
- }
-
- /**
- * Strips a phone number of unnecessary characters (spaces, dashes, etc.)
- *
- * @param number Phone number we want to normalize
- * @return Phone number consisting of digits from 0-9
- */
- public static String normalizeNumber(String number, SmartDialMap map) {
- return normalizeNumber(number, 0, map);
- }
-
- /**
- * Strips a phone number of unnecessary characters (spaces, dashes, etc.)
- *
- * @param number Phone number we want to normalize
- * @param offset Offset to start from
- * @return Phone number consisting of digits from 0-9
- */
- public static String normalizeNumber(String number, int offset, SmartDialMap map) {
- final StringBuilder s = new StringBuilder();
- for (int i = offset; i < number.length(); i++) {
- char ch = number.charAt(i);
- if (map.isValidDialpadNumericChar(ch)) {
- s.append(ch);
- }
- }
- return s.toString();
- }
-
- /**
- * Matches a phone number against a query. Let the test application overwrite the NANP setting.
- *
- * @param phoneNumber - Raw phone number
- * @param query - Normalized query (only contains numbers from 0-9)
- * @param useNanp - Overwriting nanp setting boolean, used for testing.
- * @return {@literal null} if the number and the query don't match, a valid
- * SmartDialMatchPosition with the matching positions otherwise
- */
- @VisibleForTesting
- @Nullable
- public SmartDialMatchPosition matchesNumber(String phoneNumber, String query, boolean useNanp) {
- if (TextUtils.isEmpty(phoneNumber)) {
- return null;
- }
- StringBuilder builder = new StringBuilder();
- constructEmptyMask(builder, phoneNumber.length());
- mPhoneNumberMatchMask = builder.toString();
-
- // Try matching the number as is
- SmartDialMatchPosition matchPos = matchesNumberWithOffset(phoneNumber, query, 0);
- if (matchPos == null) {
- final PhoneNumberTokens phoneNumberTokens =
- SmartDialPrefix.parsePhoneNumber(phoneNumber);
-
- if (phoneNumberTokens == null) {
- return matchPos;
- }
- if (phoneNumberTokens.countryCodeOffset != 0) {
- matchPos = matchesNumberWithOffset(phoneNumber, query,
- phoneNumberTokens.countryCodeOffset);
- }
- if (matchPos == null && phoneNumberTokens.nanpCodeOffset != 0 && useNanp) {
- matchPos = matchesNumberWithOffset(phoneNumber, query,
- phoneNumberTokens.nanpCodeOffset);
- }
- }
- if (matchPos != null) {
- replaceBitInMask(builder, matchPos);
- mPhoneNumberMatchMask = builder.toString();
- }
- return matchPos;
- }
-
- /**
- * Matches a phone number against the saved query, taking care of formatting characters and also
- * taking into account country code prefixes and special NANP number treatment.
- *
- * @param phoneNumber - Raw phone number
- * @return {@literal null} if the number and the query don't match, a valid
- * SmartDialMatchPosition with the matching positions otherwise
- */
- public SmartDialMatchPosition matchesNumber(String phoneNumber) {
- return matchesNumber(phoneNumber, mQuery, true);
- }
-
- /**
- * Matches a phone number against a query, taking care of formatting characters and also
- * taking into account country code prefixes and special NANP number treatment.
- *
- * @param phoneNumber - Raw phone number
- * @param query - Normalized query (only contains numbers from 0-9)
- * @return {@literal null} if the number and the query don't match, a valid
- * SmartDialMatchPosition with the matching positions otherwise
- */
- public SmartDialMatchPosition matchesNumber(String phoneNumber, String query) {
- return matchesNumber(phoneNumber, query, true);
- }
-
- /**
- * Matches a phone number against a query, taking care of formatting characters
- *
- * @param phoneNumber - Raw phone number
- * @param query - Normalized query (only contains numbers from 0-9)
- * @param offset - The position in the number to start the match against (used to ignore
- * leading prefixes/country codes)
- * @return {@literal null} if the number and the query don't match, a valid
- * SmartDialMatchPosition with the matching positions otherwise
- */
- private SmartDialMatchPosition matchesNumberWithOffset(String phoneNumber, String query,
- int offset) {
- if (TextUtils.isEmpty(phoneNumber) || TextUtils.isEmpty(query)) {
- return null;
- }
- int queryAt = 0;
- int numberAt = offset;
- for (int i = offset; i < phoneNumber.length(); i++) {
- if (queryAt == query.length()) {
- break;
- }
- char ch = phoneNumber.charAt(i);
- if (mMap.isValidDialpadNumericChar(ch)) {
- if (ch != query.charAt(queryAt)) {
- return null;
- }
- queryAt++;
- } else {
- if (queryAt == 0) {
- // Found a separator before any part of the query was matched, so advance the
- // offset to avoid prematurely highlighting separators before the rest of the
- // query.
- // E.g. don't highlight the first '-' if we're matching 1-510-111-1111 with
- // '510'.
- // However, if the current offset is 0, just include the beginning separators
- // anyway, otherwise the highlighting ends up looking weird.
- // E.g. if we're matching (510)-111-1111 with '510', we should include the
- // first '('.
- if (offset != 0) {
- offset++;
- }
- }
- }
- numberAt++;
- }
- return new SmartDialMatchPosition(0 + offset, numberAt);
- }
-
- /**
- * This function iterates through each token in the display name, trying to match the query
- * to the numeric equivalent of the token.
- *
- * A token is defined as a range in the display name delimited by characters that have no
- * latin alphabet equivalents (e.g. spaces - ' ', periods - ',', underscores - '_' or chinese
- * characters - '王'). Transliteration from non-latin characters to latin character will be
- * done on a best effort basis - e.g. 'Ü' - 'u'.
- *
- * For example,
- * the display name "Phillips Thomas Jr" contains three tokens: "phillips", "thomas", and "jr".
- *
- * A match must begin at the start of a token.
- * For example, typing 846(Tho) would match "Phillips Thomas", but 466(hom) would not.
- *
- * Also, a match can extend across tokens.
- * For example, typing 37337(FredS) would match (Fred S)mith.
- *
- * @param displayName The normalized(no accented characters) display name we intend to match
- * against.
- * @param query The string of digits that we want to match the display name to.
- * @param matchList An array list of {@link SmartDialMatchPosition}s that we add matched
- * positions to.
- * @return Returns true if a combination of the tokens in displayName match the query
- * string contained in query. If the function returns true, matchList will contain an
- * ArrayList of match positions (multiple matches correspond to initial matches).
- */
- @VisibleForTesting
- boolean matchesCombination(String displayName, String query,
- ArrayList<SmartDialMatchPosition> matchList) {
- StringBuilder builder = new StringBuilder();
- constructEmptyMask(builder, displayName.length());
- mNameMatchMask = builder.toString();
- final int nameLength = displayName.length();
- final int queryLength = query.length();
-
- if (nameLength < queryLength) {
- return false;
- }
-
- if (queryLength == 0) {
- return false;
- }
-
- // The current character index in displayName
- // E.g. 3 corresponds to 'd' in "Fred Smith"
- int nameStart = 0;
-
- // The current character in the query we are trying to match the displayName against
- int queryStart = 0;
-
- // The start position of the current token we are inspecting
- int tokenStart = 0;
-
- // The number of non-alphabetic characters we've encountered so far in the current match.
- // E.g. if we've currently matched 3733764849 to (Fred Smith W)illiam, then the
- // seperatorCount should be 2. This allows us to correctly calculate offsets for the match
- // positions
- int seperatorCount = 0;
-
- ArrayList<SmartDialMatchPosition> partial = new ArrayList<SmartDialMatchPosition>();
- // Keep going until we reach the end of displayName
- while (nameStart < nameLength && queryStart < queryLength) {
- char ch = displayName.charAt(nameStart);
- // Strip diacritics from accented characters if any
- ch = mMap.normalizeCharacter(ch);
- if (mMap.isValidDialpadCharacter(ch)) {
- if (mMap.isValidDialpadAlphabeticChar(ch)) {
- ch = mMap.getDialpadNumericCharacter(ch);
- }
- if (ch != query.charAt(queryStart)) {
- // Failed to match the current character in the query.
-
- // Case 1: Failed to match the first character in the query. Skip to the next
- // token since there is no chance of this token matching the query.
-
- // Case 2: Previous characters in the query matched, but the current character
- // failed to match. This happened in the middle of a token. Skip to the next
- // token since there is no chance of this token matching the query.
-
- // Case 3: Previous characters in the query matched, but the current character
- // failed to match. This happened right at the start of the current token. In
- // this case, we should restart the query and try again with the current token.
- // Otherwise, we would fail to match a query like "964"(yog) against a name
- // Yo-Yoghurt because the query match would fail on the 3rd character, and
- // then skip to the end of the "Yoghurt" token.
-
- if (queryStart == 0 || mMap.isValidDialpadCharacter(mMap.normalizeCharacter(
- displayName.charAt(nameStart - 1)))) {
- // skip to the next token, in the case of 1 or 2.
- while (nameStart < nameLength &&
- mMap.isValidDialpadCharacter(mMap.normalizeCharacter(
- displayName.charAt(nameStart)))) {
- nameStart++;
- }
- nameStart++;
- }
-
- // Restart the query and set the correct token position
- queryStart = 0;
- seperatorCount = 0;
- tokenStart = nameStart;
- } else {
- if (queryStart == queryLength - 1) {
-
- // As much as possible, we prioritize a full token match over a sub token
- // one so if we find a full token match, we can return right away
- matchList.add(new SmartDialMatchPosition(
- tokenStart, queryLength + tokenStart + seperatorCount));
- for (SmartDialMatchPosition match : matchList) {
- replaceBitInMask(builder, match);
- }
- mNameMatchMask = builder.toString();
- return true;
- } else if (ALLOW_INITIAL_MATCH && queryStart < INITIAL_LENGTH_LIMIT) {
- // we matched the first character.
- // branch off and see if we can find another match with the remaining
- // characters in the query string and the remaining tokens
- // find the next separator in the query string
- int j;
- for (j = nameStart; j < nameLength; j++) {
- if (!mMap.isValidDialpadCharacter(mMap.normalizeCharacter(
- displayName.charAt(j)))) {
- break;
- }
- }
- // this means there is at least one character left after the separator
- if (j < nameLength - 1) {
- final String remainder = displayName.substring(j + 1);
- final ArrayList<SmartDialMatchPosition> partialTemp =
- Lists.newArrayList();
- if (matchesCombination(
- remainder, query.substring(queryStart + 1), partialTemp)) {
-
- // store the list of possible match positions
- SmartDialMatchPosition.advanceMatchPositions(partialTemp, j + 1);
- partialTemp.add(0,
- new SmartDialMatchPosition(nameStart, nameStart + 1));
- // we found a partial token match, store the data in a
- // temp buffer and return it if we end up not finding a full
- // token match
- partial = partialTemp;
- }
- }
- }
- nameStart++;
- queryStart++;
- // we matched the current character in the name against one in the query,
- // continue and see if the rest of the characters match
- }
- } else {
- // found a separator, we skip this character and continue to the next one
- nameStart++;
- if (queryStart == 0) {
- // This means we found a separator before the start of a token,
- // so we should increment the token's start position to reflect its true
- // start position
- tokenStart = nameStart;
- } else {
- // Otherwise this separator was found in the middle of a token being matched,
- // so increase the separator count
- seperatorCount++;
- }
- }
- }
- // if we have no complete match at this point, then we attempt to fall back to the partial
- // token match(if any). If we don't allow initial matching (ALLOW_INITIAL_MATCH = false)
- // then partial will always be empty.
- if (!partial.isEmpty()) {
- matchList.addAll(partial);
- for (SmartDialMatchPosition match : matchList) {
- replaceBitInMask(builder, match);
- }
- mNameMatchMask = builder.toString();
- return true;
- }
- return false;
- }
-
- public boolean matches(String displayName) {
- mMatchPositions.clear();
- return matchesCombination(displayName, mQuery, mMatchPositions);
- }
-
- public ArrayList<SmartDialMatchPosition> getMatchPositions() {
- // Return a clone of mMatchPositions so that the caller can use it without
- // worrying about it changing
- return new ArrayList<SmartDialMatchPosition>(mMatchPositions);
- }
-
- public void setQuery(String query) {
- mQuery = query;
- }
-
- public String getNameMatchPositionsInString() {
- return mNameMatchMask;
- }
-
- public String getNumberMatchPositionsInString() {
- return mPhoneNumberMatchMask;
- }
-
- public String getQuery() {
- return mQuery;
- }
-}
diff --git a/src/com/android/dialer/dialpad/SmartDialPrefix.java b/src/com/android/dialer/dialpad/SmartDialPrefix.java
deleted file mode 100644
index a0b51ebb5..000000000
--- a/src/com/android/dialer/dialpad/SmartDialPrefix.java
+++ /dev/null
@@ -1,608 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.dialpad;
-
-import android.content.Context;
-
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Smart Dial utility class to find prefixes of contacts. It contains both methods to find supported
- * prefix combinations for contact names, and also methods to find supported prefix combinations for
- * contacts' phone numbers. Each contact name is separated into several tokens, such as first name,
- * middle name, family name etc. Each phone number is also separated into country code, NANP area
- * code, and local number if such separation is possible.
- */
-public class SmartDialPrefix {
-
- /** The number of starting and ending tokens in a contact's name considered for initials.
- * For example, if both constants are set to 2, and a contact's name is
- * "Albert Ben Charles Daniel Ed Foster", the first two tokens "Albert" "Ben", and last two
- * tokens "Ed" "Foster" can be replaced by their initials in contact name matching.
- * Users can look up this contact by combinations of his initials such as "AF" "BF" "EF" "ABF"
- * "BEF" "ABEF" etc, but can not use combinations such as "CF" "DF" "ACF" "ADF" etc.
- */
- private static final int LAST_TOKENS_FOR_INITIALS = 2;
- private static final int FIRST_TOKENS_FOR_INITIALS = 2;
-
- /** The country code of the user's sim card obtained by calling getSimCountryIso*/
- private static final String PREF_USER_SIM_COUNTRY_CODE =
- "DialtactsActivity_user_sim_country_code";
- private static final String PREF_USER_SIM_COUNTRY_CODE_DEFAULT = null;
- private static String sUserSimCountryCode = PREF_USER_SIM_COUNTRY_CODE_DEFAULT;
-
- /** Indicates whether user is in NANP regions.*/
- private static boolean sUserInNanpRegion = false;
-
- /** Set of country names that use NANP code.*/
- private static Set<String> sNanpCountries = null;
-
- /** Set of supported country codes in front of the phone number. */
- private static Set<String> sCountryCodes = null;
-
- /** Dialpad mapping. */
- private static final SmartDialMap mMap = new LatinSmartDialMap();
-
- private static boolean sNanpInitialized = false;
-
- /** Initializes the Nanp settings, and finds out whether user is in a NANP region.*/
- public static void initializeNanpSettings(Context context){
- final TelephonyManager manager = (TelephonyManager) context.getSystemService(
- Context.TELEPHONY_SERVICE);
- if (manager != null) {
- sUserSimCountryCode = manager.getSimCountryIso();
- }
-
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
-
- if (sUserSimCountryCode != null) {
- /** Updates shared preferences with the latest country obtained from getSimCountryIso.*/
- prefs.edit().putString(PREF_USER_SIM_COUNTRY_CODE, sUserSimCountryCode).apply();
- } else {
- /** Uses previously stored country code if loading fails. */
- sUserSimCountryCode = prefs.getString(PREF_USER_SIM_COUNTRY_CODE,
- PREF_USER_SIM_COUNTRY_CODE_DEFAULT);
- }
- /** Queries the NANP country list to find out whether user is in a NANP region.*/
- sUserInNanpRegion = isCountryNanp(sUserSimCountryCode);
- sNanpInitialized = true;
- }
-
- /**
- * Explicitly setting the user Nanp to the given boolean
- */
- @VisibleForTesting
- public static void setUserInNanpRegion(boolean userInNanpRegion) {
- sUserInNanpRegion = userInNanpRegion;
- }
-
- /**
- * Class to record phone number parsing information.
- */
- public static class PhoneNumberTokens {
- /** Country code of the phone number. */
- final String countryCode;
-
- /** Offset of national number after the country code. */
- final int countryCodeOffset;
-
- /** Offset of local number after NANP area code.*/
- final int nanpCodeOffset;
-
- public PhoneNumberTokens(String countryCode, int countryCodeOffset, int nanpCodeOffset) {
- this.countryCode = countryCode;
- this.countryCodeOffset = countryCodeOffset;
- this.nanpCodeOffset = nanpCodeOffset;
- }
- }
-
- /**
- * Parses a contact's name into a list of separated tokens.
- *
- * @param contactName Contact's name stored in string.
- * @return A list of name tokens, for example separated first names, last name, etc.
- */
- public static ArrayList<String> parseToIndexTokens(String contactName) {
- final int length = contactName.length();
- final ArrayList<String> result = Lists.newArrayList();
- char c;
- final StringBuilder currentIndexToken = new StringBuilder();
- /**
- * Iterates through the whole name string. If the current character is a valid character,
- * append it to the current token. If the current character is not a valid character, for
- * example space " ", mark the current token as complete and add it to the list of tokens.
- */
- for (int i = 0; i < length; i++) {
- c = mMap.normalizeCharacter(contactName.charAt(i));
- if (mMap.isValidDialpadCharacter(c)) {
- /** Converts a character into the number on dialpad that represents the character.*/
- currentIndexToken.append(mMap.getDialpadIndex(c));
- } else {
- if (currentIndexToken.length() != 0) {
- result.add(currentIndexToken.toString());
- }
- currentIndexToken.delete(0, currentIndexToken.length());
- }
- }
-
- /** Adds the last token in case it has not been added.*/
- if (currentIndexToken.length() != 0) {
- result.add(currentIndexToken.toString());
- }
- return result;
- }
-
- /**
- * Generates a list of strings that any prefix of any string in the list can be used to look
- * up the contact's name.
- *
- * @param index The contact's name in string.
- * @return A List of strings, whose prefix can be used to look up the contact.
- */
- public static ArrayList<String> generateNamePrefixes(String index) {
- final ArrayList<String> result = Lists.newArrayList();
-
- /** Parses the name into a list of tokens.*/
- final ArrayList<String> indexTokens = parseToIndexTokens(index);
-
- if (indexTokens.size() > 0) {
- /** Adds the full token combinations to the list. For example, a contact with name
- * "Albert Ben Ed Foster" can be looked up by any prefix of the following strings
- * "Foster" "EdFoster" "BenEdFoster" and "AlbertBenEdFoster". This covers all cases of
- * look up that contains only one token, and that spans multiple continuous tokens.
- */
- final StringBuilder fullNameToken = new StringBuilder();
- for (int i = indexTokens.size() - 1; i >= 0; i--) {
- fullNameToken.insert(0, indexTokens.get(i));
- result.add(fullNameToken.toString());
- }
-
- /** Adds initial combinations to the list, with the number of initials restricted by
- * {@link #LAST_TOKENS_FOR_INITIALS} and {@link #FIRST_TOKENS_FOR_INITIALS}.
- * For example, a contact with name "Albert Ben Ed Foster" can be looked up by any
- * prefix of the following strings "EFoster" "BFoster" "BEFoster" "AFoster" "ABFoster"
- * "AEFoster" and "ABEFoster". This covers all cases of initial lookup.
- */
- ArrayList<String> fullNames = Lists.newArrayList();
- fullNames.add(indexTokens.get(indexTokens.size() - 1));
- final int recursiveNameStart = result.size();
- int recursiveNameEnd = result.size();
- String initial = "";
- for (int i = indexTokens.size() - 2; i >= 0; i--) {
- if ((i >= indexTokens.size() - LAST_TOKENS_FOR_INITIALS) ||
- (i < FIRST_TOKENS_FOR_INITIALS)) {
- initial = indexTokens.get(i).substring(0, 1);
-
- /** Recursively adds initial combinations to the list.*/
- for (int j = 0; j < fullNames.size(); ++j) {
- result.add(initial + fullNames.get(j));
- }
- for (int j = recursiveNameStart; j < recursiveNameEnd; ++j) {
- result.add(initial + result.get(j));
- }
- recursiveNameEnd = result.size();
- final String currentFullName = fullNames.get(fullNames.size() - 1);
- fullNames.add(indexTokens.get(i) + currentFullName);
- }
- }
- }
-
- return result;
- }
-
- /**
- * Computes a list of number strings based on tokens of a given phone number. Any prefix
- * of any string in the list can be used to look up the phone number. The list include the
- * full phone number, the national number if there is a country code in the phone number, and
- * the local number if there is an area code in the phone number following the NANP format.
- * For example, if a user has phone number +41 71 394 8392, the list will contain 41713948392
- * and 713948392. Any prefix to either of the strings can be used to look up the phone number.
- * If a user has a phone number +1 555-302-3029 (NANP format), the list will contain
- * 15553023029, 5553023029, and 3023029.
- *
- * @param number String of user's phone number.
- * @return A list of strings where any prefix of any entry can be used to look up the number.
- */
- public static ArrayList<String> parseToNumberTokens(String number) {
- final ArrayList<String> result = Lists.newArrayList();
- if (!TextUtils.isEmpty(number)) {
- /** Adds the full number to the list.*/
- result.add(SmartDialNameMatcher.normalizeNumber(number, mMap));
-
- final PhoneNumberTokens phoneNumberTokens = parsePhoneNumber(number);
- if (phoneNumberTokens == null) {
- return result;
- }
-
- if (phoneNumberTokens.countryCodeOffset != 0) {
- result.add(SmartDialNameMatcher.normalizeNumber(number,
- phoneNumberTokens.countryCodeOffset, mMap));
- }
-
- if (phoneNumberTokens.nanpCodeOffset != 0) {
- result.add(SmartDialNameMatcher.normalizeNumber(number,
- phoneNumberTokens.nanpCodeOffset, mMap));
- }
- }
- return result;
- }
-
- /**
- * Parses a phone number to find out whether it has country code and NANP area code.
- *
- * @param number Raw phone number.
- * @return a PhoneNumberToken instance with country code, NANP code information.
- */
- public static PhoneNumberTokens parsePhoneNumber(String number) {
- String countryCode = "";
- int countryCodeOffset = 0;
- int nanpNumberOffset = 0;
-
- if (!TextUtils.isEmpty(number)) {
- String normalizedNumber = SmartDialNameMatcher.normalizeNumber(number, mMap);
- if (number.charAt(0) == '+') {
- /** If the number starts with '+', tries to find valid country code. */
- for (int i = 1; i <= 1 + 3; i++) {
- if (number.length() <= i) {
- break;
- }
- countryCode = number.substring(1, i);
- if (isValidCountryCode(countryCode)) {
- countryCodeOffset = i;
- break;
- }
- }
- } else {
- /** If the number does not start with '+', finds out whether it is in NANP
- * format and has '1' preceding the number.
- */
- if ((normalizedNumber.length() == 11) && (normalizedNumber.charAt(0) == '1') &&
- (sUserInNanpRegion)) {
- countryCode = "1";
- countryCodeOffset = number.indexOf(normalizedNumber.charAt(1));
- if (countryCodeOffset == -1) {
- countryCodeOffset = 0;
- }
- }
- }
-
- /** If user is in NANP region, finds out whether a number is in NANP format.*/
- if (sUserInNanpRegion) {
- String areaCode = "";
- if (countryCode.equals("") && normalizedNumber.length() == 10){
- /** if the number has no country code but fits the NANP format, extracts the
- * NANP area code, and finds out offset of the local number.
- */
- areaCode = normalizedNumber.substring(0, 3);
- } else if (countryCode.equals("1") && normalizedNumber.length() == 11) {
- /** If the number has country code '1', finds out area code and offset of the
- * local number.
- */
- areaCode = normalizedNumber.substring(1, 4);
- }
- if (!areaCode.equals("")) {
- final int areaCodeIndex = number.indexOf(areaCode);
- if (areaCodeIndex != -1) {
- nanpNumberOffset = number.indexOf(areaCode) + 3;
- }
- }
- }
- }
- return new PhoneNumberTokens(countryCode, countryCodeOffset, nanpNumberOffset);
- }
-
- /**
- * Checkes whether a country code is valid.
- */
- private static boolean isValidCountryCode(String countryCode) {
- if (sCountryCodes == null) {
- sCountryCodes = initCountryCodes();
- }
- return sCountryCodes.contains(countryCode);
- }
-
- private static Set<String> initCountryCodes() {
- final HashSet<String> result = new HashSet<String>();
- result.add("1");
- result.add("7");
- result.add("20");
- result.add("27");
- result.add("30");
- result.add("31");
- result.add("32");
- result.add("33");
- result.add("34");
- result.add("36");
- result.add("39");
- result.add("40");
- result.add("41");
- result.add("43");
- result.add("44");
- result.add("45");
- result.add("46");
- result.add("47");
- result.add("48");
- result.add("49");
- result.add("51");
- result.add("52");
- result.add("53");
- result.add("54");
- result.add("55");
- result.add("56");
- result.add("57");
- result.add("58");
- result.add("60");
- result.add("61");
- result.add("62");
- result.add("63");
- result.add("64");
- result.add("65");
- result.add("66");
- result.add("81");
- result.add("82");
- result.add("84");
- result.add("86");
- result.add("90");
- result.add("91");
- result.add("92");
- result.add("93");
- result.add("94");
- result.add("95");
- result.add("98");
- result.add("211");
- result.add("212");
- result.add("213");
- result.add("216");
- result.add("218");
- result.add("220");
- result.add("221");
- result.add("222");
- result.add("223");
- result.add("224");
- result.add("225");
- result.add("226");
- result.add("227");
- result.add("228");
- result.add("229");
- result.add("230");
- result.add("231");
- result.add("232");
- result.add("233");
- result.add("234");
- result.add("235");
- result.add("236");
- result.add("237");
- result.add("238");
- result.add("239");
- result.add("240");
- result.add("241");
- result.add("242");
- result.add("243");
- result.add("244");
- result.add("245");
- result.add("246");
- result.add("247");
- result.add("248");
- result.add("249");
- result.add("250");
- result.add("251");
- result.add("252");
- result.add("253");
- result.add("254");
- result.add("255");
- result.add("256");
- result.add("257");
- result.add("258");
- result.add("260");
- result.add("261");
- result.add("262");
- result.add("263");
- result.add("264");
- result.add("265");
- result.add("266");
- result.add("267");
- result.add("268");
- result.add("269");
- result.add("290");
- result.add("291");
- result.add("297");
- result.add("298");
- result.add("299");
- result.add("350");
- result.add("351");
- result.add("352");
- result.add("353");
- result.add("354");
- result.add("355");
- result.add("356");
- result.add("357");
- result.add("358");
- result.add("359");
- result.add("370");
- result.add("371");
- result.add("372");
- result.add("373");
- result.add("374");
- result.add("375");
- result.add("376");
- result.add("377");
- result.add("378");
- result.add("379");
- result.add("380");
- result.add("381");
- result.add("382");
- result.add("385");
- result.add("386");
- result.add("387");
- result.add("389");
- result.add("420");
- result.add("421");
- result.add("423");
- result.add("500");
- result.add("501");
- result.add("502");
- result.add("503");
- result.add("504");
- result.add("505");
- result.add("506");
- result.add("507");
- result.add("508");
- result.add("509");
- result.add("590");
- result.add("591");
- result.add("592");
- result.add("593");
- result.add("594");
- result.add("595");
- result.add("596");
- result.add("597");
- result.add("598");
- result.add("599");
- result.add("670");
- result.add("672");
- result.add("673");
- result.add("674");
- result.add("675");
- result.add("676");
- result.add("677");
- result.add("678");
- result.add("679");
- result.add("680");
- result.add("681");
- result.add("682");
- result.add("683");
- result.add("685");
- result.add("686");
- result.add("687");
- result.add("688");
- result.add("689");
- result.add("690");
- result.add("691");
- result.add("692");
- result.add("800");
- result.add("808");
- result.add("850");
- result.add("852");
- result.add("853");
- result.add("855");
- result.add("856");
- result.add("870");
- result.add("878");
- result.add("880");
- result.add("881");
- result.add("882");
- result.add("883");
- result.add("886");
- result.add("888");
- result.add("960");
- result.add("961");
- result.add("962");
- result.add("963");
- result.add("964");
- result.add("965");
- result.add("966");
- result.add("967");
- result.add("968");
- result.add("970");
- result.add("971");
- result.add("972");
- result.add("973");
- result.add("974");
- result.add("975");
- result.add("976");
- result.add("977");
- result.add("979");
- result.add("992");
- result.add("993");
- result.add("994");
- result.add("995");
- result.add("996");
- result.add("998");
- return result;
- }
-
- public static SmartDialMap getMap() {
- return mMap;
- }
-
- /**
- * Indicates whether the given country uses NANP numbers
- * @see <a href="https://en.wikipedia.org/wiki/North_American_Numbering_Plan">
- * https://en.wikipedia.org/wiki/North_American_Numbering_Plan</a>
- *
- * @param country ISO 3166 country code (case doesn't matter)
- * @return True if country uses NANP numbers (e.g. US, Canada), false otherwise
- */
- @VisibleForTesting
- public static boolean isCountryNanp(String country) {
- if (TextUtils.isEmpty(country)) {
- return false;
- }
- if (sNanpCountries == null) {
- sNanpCountries = initNanpCountries();
- }
- return sNanpCountries.contains(country.toUpperCase());
- }
-
- private static Set<String> initNanpCountries() {
- final HashSet<String> result = new HashSet<String>();
- result.add("US"); // United States
- result.add("CA"); // Canada
- result.add("AS"); // American Samoa
- result.add("AI"); // Anguilla
- result.add("AG"); // Antigua and Barbuda
- result.add("BS"); // Bahamas
- result.add("BB"); // Barbados
- result.add("BM"); // Bermuda
- result.add("VG"); // British Virgin Islands
- result.add("KY"); // Cayman Islands
- result.add("DM"); // Dominica
- result.add("DO"); // Dominican Republic
- result.add("GD"); // Grenada
- result.add("GU"); // Guam
- result.add("JM"); // Jamaica
- result.add("PR"); // Puerto Rico
- result.add("MS"); // Montserrat
- result.add("MP"); // Northern Mariana Islands
- result.add("KN"); // Saint Kitts and Nevis
- result.add("LC"); // Saint Lucia
- result.add("VC"); // Saint Vincent and the Grenadines
- result.add("TT"); // Trinidad and Tobago
- result.add("TC"); // Turks and Caicos Islands
- result.add("VI"); // U.S. Virgin Islands
- return result;
- }
-
- /**
- * Returns whether the user is in a region that uses Nanp format based on the sim location.
- *
- * @return Whether user is in Nanp region.
- */
- public static boolean getUserInNanpRegion() {
- return sUserInNanpRegion;
- }
-}
diff --git a/src/com/android/dialer/dialpad/UnicodeDialerKeyListener.java b/src/com/android/dialer/dialpad/UnicodeDialerKeyListener.java
deleted file mode 100644
index 740b4566f..000000000
--- a/src/com/android/dialer/dialpad/UnicodeDialerKeyListener.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.dialer.dialpad;
-
-import android.telephony.PhoneNumberUtils;
-import android.text.Spanned;
-import android.text.method.DialerKeyListener;
-
-/**
- * {@link DialerKeyListener} with Unicode support. Converts any Unicode(e.g. Arabic) characters
- * that represent digits into digits before filtering the results so that we can support
- * pasted digits from Unicode languages.
- */
-public class UnicodeDialerKeyListener extends DialerKeyListener {
- public static final UnicodeDialerKeyListener INSTANCE = new UnicodeDialerKeyListener();
-
- @Override
- public CharSequence filter(CharSequence source, int start, int end,
- Spanned dest, int dstart, int dend) {
- final String converted = PhoneNumberUtils.convertKeypadLettersToDigits(
- PhoneNumberUtils.replaceUnicodeDigits(source.toString()));
- // PhoneNumberUtils.replaceUnicodeDigits performs a character for character replacement,
- // so we can assume that start and end positions should remain unchanged.
- CharSequence result = super.filter(converted, start, end, dest, dstart, dend);
- if (result == null) {
- if (source.equals(converted)) {
- // There was no conversion or filtering performed. Just return null according to
- // the behavior of DialerKeyListener.
- return null;
- } else {
- // filter returns null if the charsequence is to be returned unchanged/unfiltered.
- // But in this case we do want to return a modified character string (even if
- // none of the characters in the modified string are filtered). So if
- // result == null we return the unfiltered but converted numeric string instead.
- return converted.subSequence(start, end);
- }
- }
- return result;
- }
-}
diff --git a/src/com/android/dialer/filterednumber/BlockNumberDialogFragment.java b/src/com/android/dialer/filterednumber/BlockNumberDialogFragment.java
deleted file mode 100644
index 3c60a967b..000000000
--- a/src/com/android/dialer/filterednumber/BlockNumberDialogFragment.java
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.FragmentManager;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.design.widget.Snackbar;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.Toast;
-
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.R;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnBlockNumberListener;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnUnblockNumberListener;
-import com.android.dialer.voicemail.VisualVoicemailEnabledChecker;
-import com.android.dialer.logging.InteractionEvent;
-import com.android.dialer.logging.Logger;
-
-/**
- * Fragment for confirming and enacting blocking/unblocking a number. Also invokes snackbar
- * providing undo functionality.
- */
-public class BlockNumberDialogFragment extends DialogFragment {
-
- /**
- * Use a callback interface to update UI after success/undo. Favor this approach over other
- * more standard paradigms because of the variety of scenarios in which the DialogFragment
- * can be invoked (by an Activity, by a fragment, by an adapter, by an adapter list item).
- * Because of this, we do NOT support retaining state on rotation, and will dismiss the dialog
- * upon rotation instead.
- */
- public interface Callback {
- /**
- * Called when a number is successfully added to the set of filtered numbers
- */
- void onFilterNumberSuccess();
-
- /**
- * Called when a number is successfully removed from the set of filtered numbers
- */
- void onUnfilterNumberSuccess();
-
- /**
- * Called when the action of filtering or unfiltering a number is undone
- */
- void onChangeFilteredNumberUndo();
- }
-
- private static final String BLOCK_DIALOG_FRAGMENT = "BlockNumberDialog";
-
- private static final String ARG_BLOCK_ID = "argBlockId";
- private static final String ARG_NUMBER = "argNumber";
- private static final String ARG_COUNTRY_ISO = "argCountryIso";
- private static final String ARG_DISPLAY_NUMBER = "argDisplayNumber";
- private static final String ARG_PARENT_VIEW_ID = "parentViewId";
-
- private String mNumber;
- private String mDisplayNumber;
- private String mCountryIso;
-
- private FilteredNumberAsyncQueryHandler mHandler;
- private View mParentView;
- private VisualVoicemailEnabledChecker mVoicemailEnabledChecker;
- private Callback mCallback;
-
- public static void show(
- Integer blockId,
- String number,
- String countryIso,
- String displayNumber,
- Integer parentViewId,
- FragmentManager fragmentManager,
- Callback callback) {
- final BlockNumberDialogFragment newFragment = BlockNumberDialogFragment.newInstance(
- blockId, number, countryIso, displayNumber, parentViewId);
-
- newFragment.setCallback(callback);
- newFragment.show(fragmentManager, BlockNumberDialogFragment.BLOCK_DIALOG_FRAGMENT);
- }
-
- private static BlockNumberDialogFragment newInstance(
- Integer blockId,
- String number,
- String countryIso,
- String displayNumber,
- Integer parentViewId) {
- final BlockNumberDialogFragment fragment = new BlockNumberDialogFragment();
- final Bundle args = new Bundle();
- if (blockId != null) {
- args.putInt(ARG_BLOCK_ID, blockId.intValue());
- }
- if (parentViewId != null) {
- args.putInt(ARG_PARENT_VIEW_ID, parentViewId.intValue());
- }
- args.putString(ARG_NUMBER, number);
- args.putString(ARG_COUNTRY_ISO, countryIso);
- args.putString(ARG_DISPLAY_NUMBER, displayNumber);
- fragment.setArguments(args);
- return fragment;
- }
-
- @Override
- public Context getContext() {
- return getActivity();
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreateDialog(savedInstanceState);
- final boolean isBlocked = getArguments().containsKey(ARG_BLOCK_ID);
-
- mNumber = getArguments().getString(ARG_NUMBER);
- mDisplayNumber = getArguments().getString(ARG_DISPLAY_NUMBER);
- mCountryIso = getArguments().getString(ARG_COUNTRY_ISO);
-
- if (TextUtils.isEmpty(mDisplayNumber)) {
- mDisplayNumber = mNumber;
- }
-
- mHandler = new FilteredNumberAsyncQueryHandler(getContext().getContentResolver());
- mVoicemailEnabledChecker = new VisualVoicemailEnabledChecker(getActivity(), null);
- /**
- * Choose not to update VoicemailEnabledChecker, as checks should already been done in
- * all current use cases.
- */
- mParentView = getActivity().findViewById(getArguments().getInt(ARG_PARENT_VIEW_ID));
-
- CharSequence title;
- String okText;
- String message;
- if (isBlocked) {
- title = null;
- okText = getString(R.string.unblock_number_ok);
- message = ContactDisplayUtils.getTtsSpannedPhoneNumber(getResources(),
- R.string.unblock_number_confirmation_title,
- mDisplayNumber).toString();
- } else {
- title = ContactDisplayUtils.getTtsSpannedPhoneNumber(getResources(),
- R.string.block_number_confirmation_title,
- mDisplayNumber);
- okText = getString(R.string.block_number_ok);
- if (FilteredNumberCompat.useNewFiltering()) {
- message = getString(R.string.block_number_confirmation_message_new_filtering);
- } else if (mVoicemailEnabledChecker.isVisualVoicemailEnabled()) {
- message = getString(R.string.block_number_confirmation_message_vvm);
- } else {
- message = getString(R.string.block_number_confirmation_message_no_vvm);
- }
- }
-
-
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
- .setTitle(title)
- .setMessage(message)
- .setPositiveButton(okText, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- if (isBlocked) {
- unblockNumber();
- } else {
- blockNumber();
- }
- }
- })
- .setNegativeButton(android.R.string.cancel, null);
- return builder.create();
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- if (!FilteredNumbersUtil.canBlockNumber(getActivity(), mNumber, mCountryIso)) {
- dismiss();
- Toast.makeText(getContext(),
- ContactDisplayUtils.getTtsSpannedPhoneNumber(
- getResources(), R.string.invalidNumber, mDisplayNumber),
- Toast.LENGTH_SHORT).show();
- }
- }
-
- @Override
- public void onPause() {
- // Dismiss on rotation.
- dismiss();
- mCallback = null;
-
- super.onPause();
- }
-
- public void setCallback(Callback callback) {
- mCallback = callback;
- }
-
- private CharSequence getBlockedMessage() {
- return ContactDisplayUtils.getTtsSpannedPhoneNumber(getResources(),
- R.string.snackbar_number_blocked, mDisplayNumber);
- }
-
- private CharSequence getUnblockedMessage() {
- return ContactDisplayUtils.getTtsSpannedPhoneNumber(getResources(),
- R.string.snackbar_number_unblocked, mDisplayNumber);
- }
-
- private int getActionTextColor() {
- return getContext().getResources().getColor(R.color.dialer_snackbar_action_text_color);
- }
-
- private void blockNumber() {
- final CharSequence message = getBlockedMessage();
- final CharSequence undoMessage = getUnblockedMessage();
- final Callback callback = mCallback;
- final int actionTextColor = getActionTextColor();
- final Context context = getContext();
-
- final OnUnblockNumberListener onUndoListener = new OnUnblockNumberListener() {
- @Override
- public void onUnblockComplete(int rows, ContentValues values) {
- Snackbar.make(mParentView, undoMessage, Snackbar.LENGTH_LONG).show();
- if (callback != null) {
- callback.onChangeFilteredNumberUndo();
- }
- }
- };
-
- final OnBlockNumberListener onBlockNumberListener = new OnBlockNumberListener() {
- @Override
- public void onBlockComplete(final Uri uri) {
- final View.OnClickListener undoListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- // Delete the newly created row on 'undo'.
- Logger.logInteraction(InteractionEvent.UNDO_BLOCK_NUMBER);
- mHandler.unblock(onUndoListener, uri);
- }
- };
-
- Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG)
- .setAction(R.string.block_number_undo, undoListener)
- .setActionTextColor(actionTextColor)
- .show();
-
- if (callback != null) {
- callback.onFilterNumberSuccess();
- }
-
- if (context != null && FilteredNumbersUtil.hasRecentEmergencyCall(context)) {
- FilteredNumbersUtil.maybeNotifyCallBlockingDisabled(context);
- }
- }
- };
-
- mHandler.blockNumber(
- onBlockNumberListener,
- mNumber,
- mCountryIso);
- }
-
- private void unblockNumber() {
- final CharSequence message = getUnblockedMessage();
- final CharSequence undoMessage = getBlockedMessage();
- final Callback callback = mCallback;
- final int actionTextColor = getActionTextColor();
-
- final OnBlockNumberListener onUndoListener = new OnBlockNumberListener() {
- @Override
- public void onBlockComplete(final Uri uri) {
- Snackbar.make(mParentView, undoMessage, Snackbar.LENGTH_LONG).show();
- if (callback != null) {
- callback.onChangeFilteredNumberUndo();
- }
- }
- };
-
- mHandler.unblock(new OnUnblockNumberListener() {
- @Override
- public void onUnblockComplete(int rows, final ContentValues values) {
- final View.OnClickListener undoListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- // Re-insert the row on 'undo', with a new ID.
- Logger.logInteraction(InteractionEvent.UNDO_UNBLOCK_NUMBER);
- mHandler.blockNumber(onUndoListener, values);
- }
- };
-
- Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG)
- .setAction(R.string.block_number_undo, undoListener)
- .setActionTextColor(actionTextColor)
- .show();
-
- if (callback != null) {
- callback.onUnfilterNumberSuccess();
- }
- }
- }, getArguments().getInt(ARG_BLOCK_ID));
- }
-}
diff --git a/src/com/android/dialer/filterednumber/BlockedNumbersAdapter.java b/src/com/android/dialer/filterednumber/BlockedNumbersAdapter.java
deleted file mode 100644
index 10a4f5abd..000000000
--- a/src/com/android/dialer/filterednumber/BlockedNumbersAdapter.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import android.app.FragmentManager;
-import android.content.Context;
-import android.database.Cursor;
-import android.telephony.PhoneNumberUtils;
-import android.view.View;
-
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.GeoUtil;
-import com.android.dialer.R;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.dialer.logging.InteractionEvent;
-import com.android.dialer.logging.Logger;
-
-public class BlockedNumbersAdapter extends NumbersAdapter {
-
- private BlockedNumbersAdapter(
- Context context,
- FragmentManager fragmentManager,
- ContactInfoHelper contactInfoHelper,
- ContactPhotoManager contactPhotoManager) {
- super(context, fragmentManager, contactInfoHelper, contactPhotoManager);
- }
-
- public static BlockedNumbersAdapter newBlockedNumbersAdapter(
- Context context, FragmentManager fragmentManager) {
- return new BlockedNumbersAdapter(
- context,
- fragmentManager,
- new ContactInfoHelper(context, GeoUtil.getCurrentCountryIso(context)),
- ContactPhotoManager.getInstance(context));
- }
-
- @Override
- public void bindView(View view, final Context context, Cursor cursor) {
- super.bindView(view, context, cursor);
- final Integer id = cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns._ID));
- final String countryIso = cursor.getString(cursor.getColumnIndex(
- FilteredNumberColumns.COUNTRY_ISO));
- final String number = cursor.getString(cursor.getColumnIndex(FilteredNumberColumns.NUMBER));
- final String normalizedNumber = cursor.getString(cursor.getColumnIndex(
- FilteredNumberColumns.NORMALIZED_NUMBER));
-
- final View deleteButton = view.findViewById(R.id.delete_button);
- deleteButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- BlockNumberDialogFragment.show(
- id,
- number,
- countryIso,
- PhoneNumberUtils.formatNumber(number, countryIso),
- R.id.blocked_numbers_activity_container,
- getFragmentManager(),
- new BlockNumberDialogFragment.Callback() {
- @Override
- public void onFilterNumberSuccess() {}
-
- @Override
- public void onUnfilterNumberSuccess() {
- Logger.logInteraction(
- InteractionEvent.UNBLOCK_NUMBER_MANAGEMENT_SCREEN);
- }
-
- @Override
- public void onChangeFilteredNumberUndo() {}
- });
- }
- });
-
- updateView(view, number, countryIso);
- }
-
- @Override
- public boolean isEmpty() {
- // Always return false, so that the header with blocking-related options always shows.
- return false;
- }
-}
diff --git a/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigrator.java b/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigrator.java
deleted file mode 100644
index ed0faabbe..000000000
--- a/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigrator.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import com.google.common.base.Preconditions;
-
-import android.content.SharedPreferences;
-import android.util.Log;
-
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnHasBlockedNumbersListener;
-
-/**
- * Class responsible for checking if the user can be auto-migrated to {@link
- * android.provider.BlockedNumberContract} blocking. In order for this to happen, the user cannot
- * have any numbers that are blocked in the Dialer solution.
- */
-public class BlockedNumbersAutoMigrator {
-
- private static final String TAG = "BlockedNumbersAuto";
-
- private static final String HAS_CHECKED_AUTO_MIGRATE_KEY = "checkedAutoMigrate";
-
- private final SharedPreferences sharedPreferences;
- private final FilteredNumberAsyncQueryHandler queryHandler;
-
- /**
- * Constructs the BlockedNumbersAutoMigrator with the given {@link SharedPreferences} and {@link
- * FilteredNumberAsyncQueryHandler}.
- *
- * @param sharedPreferences The SharedPreferences used to persist information.
- * @param queryHandler The FilteredNumberAsyncQueryHandler used to determine if there are
- * blocked numbers.
- * @throws NullPointerException if sharedPreferences or queryHandler are null.
- */
- public BlockedNumbersAutoMigrator(SharedPreferences sharedPreferences,
- FilteredNumberAsyncQueryHandler queryHandler) {
- this.sharedPreferences = Preconditions.checkNotNull(sharedPreferences);
- this.queryHandler = Preconditions.checkNotNull(queryHandler);
- }
-
- /**
- * Attempts to perform the auto-migration. Auto-migration will only be attempted once and can be
- * performed only when the user has no blocked numbers. As a result of this method, the user
- * will be migrated to the framework blocking solution, as determined by {@link
- * FilteredNumberCompat#hasMigratedToNewBlocking()}.
- */
- public void autoMigrate() {
- if (!shouldAttemptAutoMigrate()) {
- return;
- }
-
- Log.i(TAG, "Attempting to auto-migrate.");
- queryHandler.hasBlockedNumbers(new OnHasBlockedNumbersListener() {
- @Override
- public void onHasBlockedNumbers(boolean hasBlockedNumbers) {
- if (hasBlockedNumbers) {
- Log.i(TAG, "Not auto-migrating: blocked numbers exist.");
- return;
- }
- Log.i(TAG, "Auto-migrating: no blocked numbers.");
- FilteredNumberCompat.setHasMigratedToNewBlocking(true);
- }
- });
- }
-
- private boolean shouldAttemptAutoMigrate() {
- if (sharedPreferences.contains(HAS_CHECKED_AUTO_MIGRATE_KEY)) {
- Log.d(TAG, "Not attempting auto-migrate: already checked once.");
- return false;
- }
- Log.i(TAG, "Updating state as already checked for auto-migrate.");
- sharedPreferences.edit().putBoolean(HAS_CHECKED_AUTO_MIGRATE_KEY, true).apply();
-
- if (!FilteredNumberCompat.canUseNewFiltering()) {
- Log.i(TAG, "Not attempting auto-migrate: not available.");
- return false;
- }
-
- if (FilteredNumberCompat.hasMigratedToNewBlocking()) {
- Log.i(TAG, "Not attempting auto-migrate: already migrated.");
- return false;
- }
- return true;
- }
-}
diff --git a/src/com/android/dialer/filterednumber/BlockedNumbersFragment.java b/src/com/android/dialer/filterednumber/BlockedNumbersFragment.java
deleted file mode 100644
index b64f18691..000000000
--- a/src/com/android/dialer/filterednumber/BlockedNumbersFragment.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import com.google.common.base.MoreObjects;
-
-import android.app.ListFragment;
-import android.app.LoaderManager;
-import android.content.Context;
-import android.content.CursorLoader;
-import android.content.Loader;
-import android.database.Cursor;
-import android.graphics.drawable.ColorDrawable;
-import android.os.Bundle;
-import android.support.v4.app.ActivityCompat;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.contacts.common.lettertiles.LetterTileDrawable;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.dialer.R;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberContract;
-import com.android.dialer.filterednumber.BlockedNumbersMigrator.Listener;
-import com.android.dialer.filterednumber.FilteredNumbersUtil.CheckForSendToVoicemailContactListener;
-import com.android.dialer.filterednumber.FilteredNumbersUtil.ImportSendToVoicemailContactsListener;
-import com.android.dialer.voicemail.VisualVoicemailEnabledChecker;
-
-public class BlockedNumbersFragment extends ListFragment
- implements LoaderManager.LoaderCallbacks<Cursor>, View.OnClickListener,
- VisualVoicemailEnabledChecker.Callback {
- private static final char ADD_BLOCKED_NUMBER_ICON_LETTER = '+';
-
- private BlockedNumbersMigrator blockedNumbersMigratorForTest;
- protected View migratePromoView;
- private TextView blockedNumbersText;
- private TextView footerText;
- private BlockedNumbersAdapter mAdapter;
- private VisualVoicemailEnabledChecker mVoicemailEnabledChecker;
- private View mImportSettings;
- private View mBlockedNumbersDisabledForEmergency;
- private View mBlockedNumberListDivider;
-
- @Override
- public Context getContext() {
- return getActivity();
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- LayoutInflater inflater =
- (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- getListView().addHeaderView(inflater.inflate(R.layout.blocked_number_header, null));
- getListView().addFooterView(inflater.inflate(R.layout.blocked_number_footer, null));
- //replace the icon for add number with LetterTileDrawable(), so it will have identical style
- ImageView addNumberIcon = (ImageView) getActivity().findViewById(R.id.add_number_icon);
- LetterTileDrawable drawable = new LetterTileDrawable(getResources());
- drawable.setLetter(ADD_BLOCKED_NUMBER_ICON_LETTER);
- drawable.setColor(ActivityCompat.getColor(getActivity(),
- R.color.add_blocked_number_icon_color));
- drawable.setIsCircular(true);
- addNumberIcon.setImageDrawable(drawable);
-
- if (mAdapter == null) {
- mAdapter = BlockedNumbersAdapter.newBlockedNumbersAdapter(
- getContext(), getActivity().getFragmentManager());
- }
- setListAdapter(mAdapter);
-
- blockedNumbersText = (TextView) getListView().findViewById(R.id.blocked_number_text_view);
- migratePromoView = getListView().findViewById(R.id.migrate_promo);
- getListView().findViewById(R.id.migrate_promo_allow_button).setOnClickListener(this);
- mImportSettings = getListView().findViewById(R.id.import_settings);
- mBlockedNumbersDisabledForEmergency =
- getListView().findViewById(R.id.blocked_numbers_disabled_for_emergency);
- mBlockedNumberListDivider = getActivity().findViewById(R.id.blocked_number_list_divider);
- getListView().findViewById(R.id.import_button).setOnClickListener(this);
- getListView().findViewById(R.id.view_numbers_button).setOnClickListener(this);
- getListView().findViewById(R.id.add_number_linear_layout).setOnClickListener(this);
-
- footerText = (TextView) getActivity().findViewById(
- R.id.blocked_number_footer_textview);
- mVoicemailEnabledChecker = new VisualVoicemailEnabledChecker(getContext(),this);
- mVoicemailEnabledChecker.asyncUpdate();
- updateActiveVoicemailProvider();
- }
-
- @Override
- public void onDestroy() {
- setListAdapter(null);
- super.onDestroy();
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- getLoaderManager().initLoader(0, null, this);
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
- ColorDrawable backgroundDrawable = new ColorDrawable(
- ActivityCompat.getColor(getActivity(), R.color.dialer_theme_color));
- actionBar.setBackgroundDrawable(backgroundDrawable);
- actionBar.setDisplayShowCustomEnabled(false);
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- actionBar.setDisplayShowTitleEnabled(true);
- actionBar.setTitle(R.string.manage_blocked_numbers_label);
-
- // If the device can use the framework blocking solution, users should not be able to add
- // new blocked numbers from the Blocked Management UI. They will be shown a promo card
- // asking them to migrate to new blocking instead.
- if (FilteredNumberCompat.canUseNewFiltering()) {
- migratePromoView.setVisibility(View.VISIBLE);
- blockedNumbersText.setVisibility(View.GONE);
- getListView().findViewById(R.id.add_number_linear_layout).setVisibility(View.GONE);
- getListView().findViewById(R.id.add_number_linear_layout).setOnClickListener(null);
- mBlockedNumberListDivider.setVisibility(View.GONE);
- mImportSettings.setVisibility(View.GONE);
- getListView().findViewById(R.id.import_button).setOnClickListener(null);
- getListView().findViewById(R.id.view_numbers_button).setOnClickListener(null);
- mBlockedNumbersDisabledForEmergency.setVisibility(View.GONE);
- footerText.setVisibility(View.GONE);
- } else {
- FilteredNumbersUtil.checkForSendToVoicemailContact(
- getActivity(), new CheckForSendToVoicemailContactListener() {
- @Override
- public void onComplete(boolean hasSendToVoicemailContact) {
- final int visibility =
- hasSendToVoicemailContact ? View.VISIBLE : View.GONE;
- mImportSettings.setVisibility(visibility);
- }
- });
- }
-
- // All views except migrate and the block list are hidden when new filtering is available
- if (!FilteredNumberCompat.canUseNewFiltering()
- && FilteredNumbersUtil.hasRecentEmergencyCall(getContext())) {
- mBlockedNumbersDisabledForEmergency.setVisibility(View.VISIBLE);
- } else {
- mBlockedNumbersDisabledForEmergency.setVisibility(View.GONE);
- }
-
- mVoicemailEnabledChecker.asyncUpdate();
- }
-
- @Override
- public View onCreateView(
- LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- return inflater.inflate(R.layout.blocked_number_fragment, container, false);
- }
-
- @Override
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- final String[] projection = {
- FilteredNumberContract.FilteredNumberColumns._ID,
- FilteredNumberContract.FilteredNumberColumns.COUNTRY_ISO,
- FilteredNumberContract.FilteredNumberColumns.NUMBER,
- FilteredNumberContract.FilteredNumberColumns.NORMALIZED_NUMBER
- };
- final String selection = FilteredNumberContract.FilteredNumberColumns.TYPE
- + "=" + FilteredNumberContract.FilteredNumberTypes.BLOCKED_NUMBER;
- return new CursorLoader(
- getContext(), FilteredNumberContract.FilteredNumber.CONTENT_URI, projection,
- selection, null, null);
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- mAdapter.swapCursor(data);
- if (FilteredNumberCompat.canUseNewFiltering() || data.getCount() == 0) {
- mBlockedNumberListDivider.setVisibility(View.INVISIBLE);
- } else {
- mBlockedNumberListDivider.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {
- mAdapter.swapCursor(null);
- }
-
- @Override
- public void onClick(final View view) {
- final BlockedNumbersSettingsActivity activity =
- (BlockedNumbersSettingsActivity) getActivity();
- if (activity == null) {
- return;
- }
-
- int resId = view.getId();
- if (resId == R.id.add_number_linear_layout) {
- activity.showSearchUi();
- } else if (resId == R.id.view_numbers_button) {
- activity.showNumbersToImportPreviewUi();
- } else if (resId == R.id.import_button) {
- FilteredNumbersUtil.importSendToVoicemailContacts(activity,
- new ImportSendToVoicemailContactsListener() {
- @Override
- public void onImportComplete() {
- mImportSettings.setVisibility(View.GONE);
- }
- });
- } else if (resId == R.id.migrate_promo_allow_button) {
- view.setEnabled(false);
- MoreObjects.firstNonNull(blockedNumbersMigratorForTest,
- new BlockedNumbersMigrator(getContext().getContentResolver()))
- .migrate(new Listener() {
- @Override
- public void onComplete() {
- getContext().startActivity(
- FilteredNumberCompat.createManageBlockedNumbersIntent(getContext()));
- // Remove this activity from the backstack
- activity.finish();
- }
- });
- }
- }
-
- @Override
- public void onVisualVoicemailEnabledStatusChanged(boolean newStatus){
- updateActiveVoicemailProvider();
- }
-
- private void updateActiveVoicemailProvider(){
- if (getActivity() == null || getActivity().isFinishing()) {
- return;
- }
- if (mVoicemailEnabledChecker.isVisualVoicemailEnabled()) {
- footerText.setText(R.string.block_number_footer_message_vvm);
- } else {
- footerText.setText(R.string.block_number_footer_message_no_vvm);
- }
- }
-
- @NeededForTesting
- void setBlockedNumbersMigratorForTest(BlockedNumbersMigrator blockedNumbersMigrator) {
- blockedNumbersMigratorForTest = blockedNumbersMigrator;
- }
-}
diff --git a/src/com/android/dialer/filterednumber/BlockedNumbersMigrator.java b/src/com/android/dialer/filterednumber/BlockedNumbersMigrator.java
deleted file mode 100644
index 373403046..000000000
--- a/src/com/android/dialer/filterednumber/BlockedNumbersMigrator.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import com.google.common.base.Preconditions;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.os.AsyncTask;
-
-import com.android.dialer.compat.BlockedNumbersSdkCompat;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberContract;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.incallui.Log;
-
-/**
- * Class which should be used to migrate numbers from {@link FilteredNumberContract} blocking to
- * {@link android.provider.BlockedNumberContract} blocking.
- */
-public class BlockedNumbersMigrator {
-
- private static final String TAG = "BlockedNumbersMigrator";
-
- /**
- * Listener for the operation to migrate from {@link FilteredNumberContract} blocking to
- * {@link android.provider.BlockedNumberContract} blocking.
- */
- public interface Listener {
-
- /**
- * Called when the migration operation is finished.
- */
- void onComplete();
- }
-
- private final ContentResolver mContentResolver;
-
- /**
- * Creates a new BlockedNumbersMigrate, using the given {@link ContentResolver} to perform
- * queries against the blocked numbers tables.
- *
- * @param contentResolver The ContentResolver
- * @throws NullPointerException if contentResolver is null
- */
- public BlockedNumbersMigrator(ContentResolver contentResolver) {
- mContentResolver = Preconditions.checkNotNull(contentResolver);
- }
-
- /**
- * Copies all of the numbers in the {@link FilteredNumberContract} block list to the
- * {@link android.provider.BlockedNumberContract} block list.
- *
- * @param listener {@link Listener} called once the migration is complete.
- * @return {@code true} if the migrate can be attempted, {@code false} otherwise.
- * @throws NullPointerException if listener is null
- */
- public boolean migrate(final Listener listener) {
- Log.i(TAG, "migrate - start");
- if (!FilteredNumberCompat.canUseNewFiltering()) {
- Log.i(TAG, "migrate - can't use new filtering");
- return false;
- }
- Preconditions.checkNotNull(listener);
- new AsyncTask<Void, Void, Boolean>() {
- @Override
- protected Boolean doInBackground(Void... params) {
- Log.i(TAG, "migrate - start background migration");
- return migrateToNewBlockingInBackground(mContentResolver);
- }
-
- @Override
- protected void onPostExecute(Boolean isSuccessful) {
- Log.i(TAG, "migrate - marking migration complete");
- FilteredNumberCompat.setHasMigratedToNewBlocking(isSuccessful);
- Log.i(TAG, "migrate - calling listener");
- listener.onComplete();
- }
- }.execute();
- return true;
- }
-
- private static boolean migrateToNewBlockingInBackground(ContentResolver resolver) {
- try (Cursor cursor = resolver.query(FilteredNumber.CONTENT_URI,
- new String[]{FilteredNumberColumns.NUMBER}, null, null, null)) {
- if (cursor == null) {
- Log.i(TAG, "migrate - cursor was null");
- return false;
- }
-
- Log.i(TAG, "migrate - attempting to migrate " + cursor.getCount() + "numbers");
-
- int numMigrated = 0;
- while (cursor.moveToNext()) {
- String originalNumber = cursor
- .getString(cursor.getColumnIndex(FilteredNumberColumns.NUMBER));
- if (isNumberInNewBlocking(resolver, originalNumber)) {
- Log.i(TAG, "migrate - number was already blocked in new blocking");
- continue;
- }
- ContentValues values = new ContentValues();
- values.put(BlockedNumbersSdkCompat.COLUMN_ORIGINAL_NUMBER, originalNumber);
- resolver.insert(BlockedNumbersSdkCompat.CONTENT_URI, values);
- ++numMigrated;
- }
- Log.i(TAG, "migrate - migration complete. " + numMigrated + " numbers migrated.");
- return true;
- }
- }
-
- private static boolean isNumberInNewBlocking(ContentResolver resolver, String originalNumber) {
- try (Cursor cursor = resolver.query(BlockedNumbersSdkCompat.CONTENT_URI,
- new String[]{BlockedNumbersSdkCompat._ID},
- BlockedNumbersSdkCompat.COLUMN_ORIGINAL_NUMBER + " = ?",
- new String[] {originalNumber}, null)) {
- return cursor != null && cursor.getCount() != 0;
- }
- }
-}
diff --git a/src/com/android/dialer/filterednumber/BlockedNumbersSettingsActivity.java b/src/com/android/dialer/filterednumber/BlockedNumbersSettingsActivity.java
deleted file mode 100644
index 5ce9d21f1..000000000
--- a/src/com/android/dialer/filterednumber/BlockedNumbersSettingsActivity.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.util.Log;
-import android.view.MenuItem;
-import android.widget.FrameLayout;
-import android.widget.FrameLayout.LayoutParams;
-import android.widget.Toast;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.dialog.IndeterminateProgressDialog;
-import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
-import com.android.dialer.R;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.list.BlockedListSearchAdapter;
-import com.android.dialer.list.OnListFragmentScrolledListener;
-import com.android.dialer.list.BlockedListSearchFragment;
-import com.android.dialer.list.SearchFragment;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.ScreenEvent;
-
-public class BlockedNumbersSettingsActivity extends AppCompatActivity
- implements SearchFragment.HostInterface {
-
- private static final String TAG_BLOCKED_MANAGEMENT_FRAGMENT = "blocked_management";
- private static final String TAG_BLOCKED_SEARCH_FRAGMENT = "blocked_search";
- private static final String TAG_VIEW_NUMBERS_TO_IMPORT_FRAGMENT = "view_numbers_to_import";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.blocked_numbers_activity);
-
- // If savedInstanceState != null, the Activity will automatically restore the last fragment.
- if (savedInstanceState == null) {
- showManagementUi();
- }
- }
-
- /**
- * Shows fragment with the list of currently blocked numbers and settings related to blocking.
- */
- public void showManagementUi() {
- BlockedNumbersFragment fragment = (BlockedNumbersFragment) getFragmentManager()
- .findFragmentByTag(TAG_BLOCKED_MANAGEMENT_FRAGMENT);
- if (fragment == null) {
- fragment = new BlockedNumbersFragment();
- }
-
- getFragmentManager().beginTransaction()
- .replace(R.id.blocked_numbers_activity_container, fragment,
- TAG_BLOCKED_MANAGEMENT_FRAGMENT)
- .commit();
-
- Logger.logScreenView(ScreenEvent.BLOCKED_NUMBER_MANAGEMENT, this);
- }
-
- /**
- * Shows fragment with search UI for browsing/finding numbers to block.
- */
- public void showSearchUi() {
- BlockedListSearchFragment fragment = (BlockedListSearchFragment) getFragmentManager()
- .findFragmentByTag(TAG_BLOCKED_SEARCH_FRAGMENT);
- if (fragment == null) {
- fragment = new BlockedListSearchFragment();
- fragment.setHasOptionsMenu(false);
- fragment.setShowEmptyListForNullQuery(true);
- fragment.setDirectorySearchEnabled(false);
- }
-
- getFragmentManager().beginTransaction()
- .replace(R.id.blocked_numbers_activity_container, fragment,
- TAG_BLOCKED_SEARCH_FRAGMENT)
- .addToBackStack(null)
- .commit();
-
- Logger.logScreenView(ScreenEvent.BLOCKED_NUMBER_ADD_NUMBER, this);
- }
-
- /**
- * Shows fragment with UI to preview the numbers of contacts currently marked as
- * send-to-voicemail in Contacts. These numbers can be imported into Dialer's blocked number
- * list.
- */
- public void showNumbersToImportPreviewUi() {
- ViewNumbersToImportFragment fragment = (ViewNumbersToImportFragment) getFragmentManager()
- .findFragmentByTag(TAG_VIEW_NUMBERS_TO_IMPORT_FRAGMENT);
- if (fragment == null) {
- fragment = new ViewNumbersToImportFragment();
- }
-
- getFragmentManager().beginTransaction()
- .replace(R.id.blocked_numbers_activity_container, fragment,
- TAG_VIEW_NUMBERS_TO_IMPORT_FRAGMENT)
- .addToBackStack(null)
- .commit();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == android.R.id.home) {
- onBackPressed();
- return true;
- }
- return false;
- }
-
- @Override
- public void onBackPressed() {
- // TODO: Achieve back navigation without overriding onBackPressed.
- if (getFragmentManager().getBackStackEntryCount() > 0) {
- getFragmentManager().popBackStack();
- } else {
- super.onBackPressed();
- }
- }
-
- @Override
- public boolean isActionBarShowing() {
- return false;
- }
-
- @Override
- public boolean isDialpadShown() {
- return false;
- }
-
- @Override
- public int getDialpadHeight() {
- return 0;
- }
-
- @Override
- public int getActionBarHideOffset() {
- return 0;
- }
-
- @Override
- public int getActionBarHeight() {
- return 0;
- }
-}
diff --git a/src/com/android/dialer/filterednumber/FilteredNumbersUtil.java b/src/com/android/dialer/filterednumber/FilteredNumbersUtil.java
deleted file mode 100644
index 35d6f8d25..000000000
--- a/src/com/android/dialer/filterednumber/FilteredNumbersUtil.java
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.os.AsyncTask;
-import android.preference.PreferenceManager;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts;
-import android.provider.Settings;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.widget.Toast;
-
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.R;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnHasBlockedNumbersListener;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.dialer.logging.InteractionEvent;
-import com.android.dialer.logging.Logger;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Utility to help with tasks related to filtered numbers.
- */
-public class FilteredNumbersUtil {
-
- // Disable incoming call blocking if there was a call within the past 2 days.
- private static final long RECENT_EMERGENCY_CALL_THRESHOLD_MS = 1000 * 60 * 60 * 24 * 2;
-
- // Pref key for storing the time of end of the last emergency call in milliseconds after epoch.
- protected static final String LAST_EMERGENCY_CALL_MS_PREF_KEY = "last_emergency_call_ms";
-
- // Pref key for storing whether a notification has been dispatched to notify the user that call
- // blocking has been disabled because of a recent emergency call.
- protected static final String NOTIFIED_CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_PREF_KEY =
- "notified_call_blocking_disabled_by_emergency_call";
-
- public static final String CALL_BLOCKING_NOTIFICATION_TAG = "call_blocking";
- public static final int CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_NOTIFICATION_ID = 10;
-
- /**
- * Used for testing to specify that a custom threshold should be used instead of the default.
- * This custom threshold will only be used when setting this log tag to VERBOSE:
- *
- * adb shell setprop log.tag.DebugEmergencyCall VERBOSE
- *
- */
- @NeededForTesting
- private static final String DEBUG_EMERGENCY_CALL_TAG = "DebugEmergencyCall";
-
- /**
- * Used for testing to specify the custom threshold value, in milliseconds for whether an
- * emergency call is "recent". The default value will be used if this custom threshold is less
- * than zero. For example, to set this threshold to 60 seconds:
- *
- * adb shell settings put system dialer_emergency_call_threshold_ms 60000
- *
- */
- @NeededForTesting
- private static final String RECENT_EMERGENCY_CALL_THRESHOLD_SETTINGS_KEY =
- "dialer_emergency_call_threshold_ms";
-
- public interface CheckForSendToVoicemailContactListener {
- public void onComplete(boolean hasSendToVoicemailContact);
- }
-
- public interface ImportSendToVoicemailContactsListener {
- public void onImportComplete();
- }
-
- private static class ContactsQuery {
- static final String[] PROJECTION = {
- Contacts._ID
- };
-
- static final String SELECT_SEND_TO_VOICEMAIL_TRUE = Contacts.SEND_TO_VOICEMAIL + "=1";
-
- static final int ID_COLUMN_INDEX = 0;
- }
-
- public static class PhoneQuery {
- static final String[] PROJECTION = {
- Contacts._ID,
- Phone.NORMALIZED_NUMBER,
- Phone.NUMBER
- };
-
- static final int ID_COLUMN_INDEX = 0;
- static final int NORMALIZED_NUMBER_COLUMN_INDEX = 1;
- static final int NUMBER_COLUMN_INDEX = 2;
-
- static final String SELECT_SEND_TO_VOICEMAIL_TRUE = Contacts.SEND_TO_VOICEMAIL + "=1";
- }
-
- /**
- * Checks if there exists a contact with {@code Contacts.SEND_TO_VOICEMAIL} set to true.
- */
- public static void checkForSendToVoicemailContact(
- final Context context, final CheckForSendToVoicemailContactListener listener) {
- final AsyncTask task = new AsyncTask<Object, Void, Boolean>() {
- @Override
- public Boolean doInBackground(Object[] params) {
- if (context == null || !PermissionsUtil.hasContactsPermissions(context)) {
- return false;
- }
-
- final Cursor cursor = context.getContentResolver().query(
- Contacts.CONTENT_URI,
- ContactsQuery.PROJECTION,
- ContactsQuery.SELECT_SEND_TO_VOICEMAIL_TRUE,
- null,
- null);
-
- boolean hasSendToVoicemailContacts = false;
- if (cursor != null) {
- try {
- hasSendToVoicemailContacts = cursor.getCount() > 0;
- } finally {
- cursor.close();
- }
- }
-
- return hasSendToVoicemailContacts;
- }
-
- @Override
- public void onPostExecute(Boolean hasSendToVoicemailContact) {
- if (listener != null) {
- listener.onComplete(hasSendToVoicemailContact);
- }
- }
- };
- task.execute();
- }
-
- /**
- * Blocks all the phone numbers of any contacts marked as SEND_TO_VOICEMAIL, then clears the
- * SEND_TO_VOICEMAIL flag on those contacts.
- */
- public static void importSendToVoicemailContacts(
- final Context context, final ImportSendToVoicemailContactsListener listener) {
- Logger.logInteraction(InteractionEvent.IMPORT_SEND_TO_VOICEMAIL);
- final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler =
- new FilteredNumberAsyncQueryHandler(context.getContentResolver());
-
- final AsyncTask<Object, Void, Boolean> task = new AsyncTask<Object, Void, Boolean>() {
- @Override
- public Boolean doInBackground(Object[] params) {
- if (context == null) {
- return false;
- }
-
- // Get the phone number of contacts marked as SEND_TO_VOICEMAIL.
- final Cursor phoneCursor = context.getContentResolver().query(
- Phone.CONTENT_URI,
- PhoneQuery.PROJECTION,
- PhoneQuery.SELECT_SEND_TO_VOICEMAIL_TRUE,
- null,
- null);
-
- if (phoneCursor == null) {
- return false;
- }
-
- try {
- while (phoneCursor.moveToNext()) {
- final String normalizedNumber = phoneCursor.getString(
- PhoneQuery.NORMALIZED_NUMBER_COLUMN_INDEX);
- final String number = phoneCursor.getString(
- PhoneQuery.NUMBER_COLUMN_INDEX);
- if (normalizedNumber != null) {
- // Block the phone number of the contact.
- mFilteredNumberAsyncQueryHandler.blockNumber(
- null, normalizedNumber, number, null);
- }
- }
- } finally {
- phoneCursor.close();
- }
-
- // Clear SEND_TO_VOICEMAIL on all contacts. The setting has been imported to Dialer.
- ContentValues newValues = new ContentValues();
- newValues.put(Contacts.SEND_TO_VOICEMAIL, 0);
- context.getContentResolver().update(
- Contacts.CONTENT_URI,
- newValues,
- ContactsQuery.SELECT_SEND_TO_VOICEMAIL_TRUE,
- null);
-
- return true;
- }
-
- @Override
- public void onPostExecute(Boolean success) {
- if (success) {
- if (listener != null) {
- listener.onImportComplete();
- }
- } else if (context != null) {
- String toastStr = context.getString(R.string.send_to_voicemail_import_failed);
- Toast.makeText(context, toastStr, Toast.LENGTH_SHORT).show();
- }
- }
- };
- task.execute();
- }
-
- /**
- * WARNING: This method should NOT be executed on the UI thread.
- * Use {@code FilteredNumberAsyncQueryHandler} to asynchronously check if a number is blocked.
- */
- public static boolean shouldBlockVoicemail(
- Context context, String number, String countryIso, long voicemailDateMs) {
- final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
- if (TextUtils.isEmpty(normalizedNumber)) {
- return false;
- }
-
- if (hasRecentEmergencyCall(context)) {
- return false;
- }
-
- final Cursor cursor = context.getContentResolver().query(
- FilteredNumber.CONTENT_URI,
- new String[] {
- FilteredNumberColumns.CREATION_TIME
- },
- FilteredNumberColumns.NORMALIZED_NUMBER + "=?",
- new String[] { normalizedNumber },
- null);
- if (cursor == null) {
- return false;
- }
- try {
- /*
- * Block if number is found and it was added before this voicemail was received.
- * The VVM's date is reported with precision to the minute, even though its
- * magnitude is in milliseconds, so we perform the comparison in minutes.
- */
- return cursor.moveToFirst() &&
- TimeUnit.MINUTES.convert(voicemailDateMs, TimeUnit.MILLISECONDS) >=
- TimeUnit.MINUTES.convert(cursor.getLong(0), TimeUnit.MILLISECONDS);
- } finally {
- cursor.close();
- }
- }
-
- public static boolean hasRecentEmergencyCall(Context context) {
- if (context == null) {
- return false;
- }
-
- Long lastEmergencyCallTime = PreferenceManager.getDefaultSharedPreferences(context)
- .getLong(LAST_EMERGENCY_CALL_MS_PREF_KEY, 0);
- if (lastEmergencyCallTime == 0) {
- return false;
- }
-
- return (System.currentTimeMillis() - lastEmergencyCallTime)
- < getRecentEmergencyCallThresholdMs(context);
- }
-
- public static void recordLastEmergencyCallTime(Context context) {
- if (context == null) {
- return;
- }
-
- PreferenceManager.getDefaultSharedPreferences(context)
- .edit()
- .putLong(LAST_EMERGENCY_CALL_MS_PREF_KEY, System.currentTimeMillis())
- .putBoolean(NOTIFIED_CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_PREF_KEY, false)
- .apply();
-
- maybeNotifyCallBlockingDisabled(context);
- }
-
- public static void maybeNotifyCallBlockingDisabled(final Context context) {
- // The Dialer is not responsible for this notification after migrating
- if (FilteredNumberCompat.useNewFiltering()) {
- return;
- }
- // Skip if the user has already received a notification for the most recent emergency call.
- if (PreferenceManager.getDefaultSharedPreferences(context)
- .getBoolean(NOTIFIED_CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_PREF_KEY, false)) {
- return;
- }
-
- // If the user has blocked numbers, notify that call blocking is temporarily disabled.
- FilteredNumberAsyncQueryHandler queryHandler =
- new FilteredNumberAsyncQueryHandler(context.getContentResolver());
- queryHandler.hasBlockedNumbers(new OnHasBlockedNumbersListener() {
- @Override
- public void onHasBlockedNumbers(boolean hasBlockedNumbers) {
- if (context == null || !hasBlockedNumbers) {
- return;
- }
-
- NotificationManager notificationManager = (NotificationManager)
- context.getSystemService(Context.NOTIFICATION_SERVICE);
- Notification.Builder builder = new Notification.Builder(context)
- .setSmallIcon(R.drawable.ic_block_24dp)
- .setContentTitle(context.getString(
- R.string.call_blocking_disabled_notification_title))
- .setContentText(context.getString(
- R.string.call_blocking_disabled_notification_text))
- .setAutoCancel(true);
-
- final Intent contentIntent =
- new Intent(context, BlockedNumbersSettingsActivity.class);
- builder.setContentIntent(PendingIntent.getActivity(
- context, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT));
-
- notificationManager.notify(
- CALL_BLOCKING_NOTIFICATION_TAG,
- CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_NOTIFICATION_ID,
- builder.build());
-
- // Record that the user has been notified for this emergency call.
- PreferenceManager.getDefaultSharedPreferences(context)
- .edit()
- .putBoolean(NOTIFIED_CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_PREF_KEY, true)
- .apply();
- }
- });
- }
-
- public static boolean canBlockNumber(Context context, String number, String countryIso) {
- final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
- return !TextUtils.isEmpty(normalizedNumber)
- && !PhoneNumberUtils.isEmergencyNumber(normalizedNumber);
- }
-
- private static long getRecentEmergencyCallThresholdMs(Context context) {
- if (android.util.Log.isLoggable(
- DEBUG_EMERGENCY_CALL_TAG, android.util.Log.VERBOSE)) {
- long thresholdMs = Settings.System.getLong(
- context.getContentResolver(),
- RECENT_EMERGENCY_CALL_THRESHOLD_SETTINGS_KEY, 0);
- return thresholdMs > 0 ? thresholdMs : RECENT_EMERGENCY_CALL_THRESHOLD_MS;
- } else {
- return RECENT_EMERGENCY_CALL_THRESHOLD_MS;
- }
- }
-}
diff --git a/src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragment.java b/src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragment.java
deleted file mode 100644
index 209665292..000000000
--- a/src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragment.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import com.google.common.base.Preconditions;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnShowListener;
-import android.os.Bundle;
-import android.view.View;
-import com.android.dialer.R;
-import com.android.dialer.filterednumber.BlockedNumbersMigrator.Listener;
-
-/**
- * Dialog fragment shown to users when they need to migrate to use
- * {@link android.provider.BlockedNumberContract} for blocking.
- */
-public class MigrateBlockedNumbersDialogFragment extends DialogFragment {
-
- private BlockedNumbersMigrator mBlockedNumbersMigrator;
- private BlockedNumbersMigrator.Listener mMigrationListener;
-
- /**
- * Creates a new MigrateBlockedNumbersDialogFragment.
- *
- * @param blockedNumbersMigrator The {@link BlockedNumbersMigrator} which will be used to
- * migrate the numbers.
- * @param migrationListener The {@link BlockedNumbersMigrator.Listener} to call when the
- * migration is complete.
- * @return The new MigrateBlockedNumbersDialogFragment.
- * @throws NullPointerException if blockedNumbersMigrator or migrationListener are {@code null}.
- */
- public static DialogFragment newInstance(BlockedNumbersMigrator blockedNumbersMigrator,
- BlockedNumbersMigrator.Listener migrationListener) {
- MigrateBlockedNumbersDialogFragment fragment = new MigrateBlockedNumbersDialogFragment();
- fragment.mBlockedNumbersMigrator = Preconditions.checkNotNull(blockedNumbersMigrator);
- fragment.mMigrationListener = Preconditions.checkNotNull(migrationListener);
- return fragment;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreateDialog(savedInstanceState);
- AlertDialog dialog = new AlertDialog.Builder(getActivity())
- .setTitle(R.string.migrate_blocked_numbers_dialog_title)
- .setMessage(R.string.migrate_blocked_numbers_dialog_message)
- .setPositiveButton(R.string.migrate_blocked_numbers_dialog_allow_button, null)
- .setNegativeButton(R.string.migrate_blocked_numbers_dialog_cancel_button, null)
- .create();
- // The Dialog's buttons aren't available until show is called, so an OnShowListener
- // is used to set the positive button callback.
- dialog.setOnShowListener(new OnShowListener() {
- @Override
- public void onShow(DialogInterface dialog) {
- final AlertDialog alertDialog = (AlertDialog) dialog;
- alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
- .setOnClickListener(newPositiveButtonOnClickListener(alertDialog));
- }
- });
- return dialog;
- }
-
- /*
- * Creates a new View.OnClickListener to be used as the positive button in this dialog. The
- * OnClickListener will grey out the dialog's positive and negative buttons while the migration
- * is underway, and close the dialog once the migrate is complete.
- */
- private View.OnClickListener newPositiveButtonOnClickListener(final AlertDialog alertDialog) {
- return new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
- alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false);
- mBlockedNumbersMigrator.migrate(new Listener() {
- @Override
- public void onComplete() {
- alertDialog.dismiss();
- mMigrationListener.onComplete();
- }
- });
- }
- };
- }
-
- @Override
- public void onPause() {
- // The dialog is dismissed and state is cleaned up onPause, i.e. rotation.
- dismiss();
- mBlockedNumbersMigrator = null;
- mMigrationListener = null;
- super.onPause();
- }
-}
diff --git a/src/com/android/dialer/filterednumber/NumbersAdapter.java b/src/com/android/dialer/filterednumber/NumbersAdapter.java
deleted file mode 100644
index 17d5db343..000000000
--- a/src/com/android/dialer/filterednumber/NumbersAdapter.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import android.app.FragmentManager;
-import android.content.Context;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.text.BidiFormatter;
-import android.text.TextDirectionHeuristics;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.QuickContactBadge;
-import android.widget.SimpleCursorAdapter;
-import android.widget.TextView;
-
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.util.UriUtils;
-import com.android.dialer.R;
-import com.android.dialer.calllog.ContactInfo;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.util.PhoneNumberUtil;
-
-public class NumbersAdapter extends SimpleCursorAdapter {
-
- private Context mContext;
- private FragmentManager mFragmentManager;
- private ContactInfoHelper mContactInfoHelper;
- private BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
- private ContactPhotoManager mContactPhotoManager;
-
- public NumbersAdapter(
- Context context,
- FragmentManager fragmentManager,
- ContactInfoHelper contactInfoHelper,
- ContactPhotoManager contactPhotoManager) {
- super(context, R.layout.blocked_number_item, null, new String[]{}, new int[]{}, 0);
- mContext = context;
- mFragmentManager = fragmentManager;
- mContactInfoHelper = contactInfoHelper;
- mContactPhotoManager = contactPhotoManager;
- }
-
- public void updateView(View view, String number, String countryIso) {
- final TextView callerName = (TextView) view.findViewById(R.id.caller_name);
- final TextView callerNumber = (TextView) view.findViewById(R.id.caller_number);
- final QuickContactBadge quickContactBadge =
- (QuickContactBadge) view.findViewById(R.id.quick_contact_photo);
- quickContactBadge.setOverlay(null);
- if (CompatUtils.hasPrioritizedMimeType()) {
- quickContactBadge.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE);
- }
-
- ContactInfo info = mContactInfoHelper.lookupNumber(number, countryIso);
- if (info == null) {
- info = new ContactInfo();
- info.number = number;
- }
- final CharSequence locationOrType = getNumberTypeOrLocation(info);
- final String displayNumber = getDisplayNumber(info);
- final String displayNumberStr = mBidiFormatter.unicodeWrap(displayNumber,
- TextDirectionHeuristics.LTR);
-
- String nameForDefaultImage;
- if (!TextUtils.isEmpty(info.name)) {
- nameForDefaultImage = info.name;
- callerName.setText(info.name);
- callerNumber.setText(locationOrType + " " + displayNumberStr);
- } else {
- nameForDefaultImage = displayNumber;
- callerName.setText(displayNumberStr);
- if (!TextUtils.isEmpty(locationOrType)) {
- callerNumber.setText(locationOrType);
- callerNumber.setVisibility(View.VISIBLE);
- } else {
- callerNumber.setVisibility(View.GONE);
- }
- }
- loadContactPhoto(info, nameForDefaultImage, quickContactBadge);
- }
-
- private void loadContactPhoto(ContactInfo info, String displayName, QuickContactBadge badge) {
- final String lookupKey = info.lookupUri == null
- ? null : UriUtils.getLookupKeyFromUri(info.lookupUri);
- final int contactType = mContactInfoHelper.isBusiness(info.sourceType)
- ? ContactPhotoManager.TYPE_BUSINESS : ContactPhotoManager.TYPE_DEFAULT;
- final DefaultImageRequest request = new DefaultImageRequest(displayName, lookupKey,
- contactType, true /* isCircular */);
- badge.assignContactUri(info.lookupUri);
- badge.setContentDescription(
- mContext.getResources().getString(R.string.description_contact_details, displayName));
- mContactPhotoManager.loadDirectoryPhoto(badge, info.photoUri,
- false /* darkTheme */, true /* isCircular */, request);
- }
-
- private String getDisplayNumber(ContactInfo info) {
- if (!TextUtils.isEmpty(info.formattedNumber)) {
- return info.formattedNumber;
- } else if (!TextUtils.isEmpty(info.number)) {
- return info.number;
- } else {
- return "";
- }
- }
-
- private CharSequence getNumberTypeOrLocation(ContactInfo info) {
- if (!TextUtils.isEmpty(info.name)) {
- return ContactsContract.CommonDataKinds.Phone.getTypeLabel(
- mContext.getResources(), info.type, info.label);
- } else {
- return PhoneNumberUtil.getGeoDescription(mContext, info.number);
- }
- }
-
- protected Context getContext() {
- return mContext;
- }
-
- protected FragmentManager getFragmentManager() {
- return mFragmentManager;
- }
-}
diff --git a/src/com/android/dialer/filterednumber/ViewNumbersToImportAdapter.java b/src/com/android/dialer/filterednumber/ViewNumbersToImportAdapter.java
deleted file mode 100644
index 58fe1d46c..000000000
--- a/src/com/android/dialer/filterednumber/ViewNumbersToImportAdapter.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import android.app.FragmentManager;
-import android.database.Cursor;
-import android.content.Context;
-import android.view.View;
-
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.GeoUtil;
-import com.android.dialer.R;
-import com.android.dialer.calllog.ContactInfoHelper;
-
-public class ViewNumbersToImportAdapter extends NumbersAdapter {
-
- private ViewNumbersToImportAdapter(
- Context context,
- FragmentManager fragmentManager,
- ContactInfoHelper contactInfoHelper,
- ContactPhotoManager contactPhotoManager) {
- super(context, fragmentManager, contactInfoHelper, contactPhotoManager);
- }
-
- public static ViewNumbersToImportAdapter newViewNumbersToImportAdapter(
- Context context, FragmentManager fragmentManager) {
- return new ViewNumbersToImportAdapter(
- context,
- fragmentManager,
- new ContactInfoHelper(context, GeoUtil.getCurrentCountryIso(context)),
- ContactPhotoManager.getInstance(context));
- }
-
- @Override
- public void bindView(View view, Context context, Cursor cursor) {
- super.bindView(view, context, cursor);
-
- final String number = cursor.getString(
- FilteredNumbersUtil.PhoneQuery.NUMBER_COLUMN_INDEX);
-
- view.findViewById(R.id.delete_button).setVisibility(View.GONE);
- updateView(view, number, null /* countryIso */);
- }
-}
diff --git a/src/com/android/dialer/filterednumber/ViewNumbersToImportFragment.java b/src/com/android/dialer/filterednumber/ViewNumbersToImportFragment.java
deleted file mode 100644
index 8b24c06da..000000000
--- a/src/com/android/dialer/filterednumber/ViewNumbersToImportFragment.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import android.app.ListFragment;
-import android.app.LoaderManager;
-import android.content.Context;
-import android.content.CursorLoader;
-import android.content.Loader;
-import android.database.Cursor;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.dialer.R;
-import com.android.dialer.database.FilteredNumberContract;
-import com.android.dialer.filterednumber.FilteredNumbersUtil.ImportSendToVoicemailContactsListener;
-
-public class ViewNumbersToImportFragment extends ListFragment
- implements LoaderManager.LoaderCallbacks<Cursor>,
- View.OnClickListener {
-
- private ViewNumbersToImportAdapter mAdapter;
-
- @Override
- public Context getContext() {
- return getActivity();
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- if (mAdapter == null) {
- mAdapter = ViewNumbersToImportAdapter.newViewNumbersToImportAdapter(
- getContext(), getActivity().getFragmentManager());
- }
- setListAdapter(mAdapter);
- }
-
- @Override
- public void onDestroy() {
- setListAdapter(null);
- super.onDestroy();
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- getLoaderManager().initLoader(0, null, this);
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
- actionBar.setTitle(R.string.import_send_to_voicemail_numbers_label);
- actionBar.setDisplayShowCustomEnabled(false);
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- actionBar.setDisplayShowTitleEnabled(true);
-
- getActivity().findViewById(R.id.cancel_button).setOnClickListener(this);
- getActivity().findViewById(R.id.import_button).setOnClickListener(this);
- }
-
- @Override
- public View onCreateView(
- LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- return inflater.inflate(R.layout.view_numbers_to_import_fragment, container, false);
- }
-
- @Override
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- final CursorLoader cursorLoader = new CursorLoader(
- getContext(),
- Phone.CONTENT_URI,
- FilteredNumbersUtil.PhoneQuery.PROJECTION,
- FilteredNumbersUtil.PhoneQuery.SELECT_SEND_TO_VOICEMAIL_TRUE,
- null,
- null);
- return cursorLoader;
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- mAdapter.swapCursor(data);
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {
- mAdapter.swapCursor(null);
- }
-
- @Override
- public void onClick(final View view) {
- if (view.getId() == R.id.import_button) {
- FilteredNumbersUtil.importSendToVoicemailContacts(getContext(),
- new ImportSendToVoicemailContactsListener() {
- @Override
- public void onImportComplete() {
- if (getActivity() != null) {
- getActivity().onBackPressed();
- }
- }
- });
- } else if (view.getId() == R.id.cancel_button) {
- getActivity().onBackPressed();
- }
- }
-}
diff --git a/src/com/android/dialer/interactions/PhoneNumberInteraction.java b/src/com/android/dialer/interactions/PhoneNumberInteraction.java
deleted file mode 100644
index 0c3ae510a..000000000
--- a/src/com/android/dialer/interactions/PhoneNumberInteraction.java
+++ /dev/null
@@ -1,516 +0,0 @@
-/*
- * Copyright (C) 2010 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.dialer.interactions;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.FragmentManager;
-import android.content.Context;
-import android.content.CursorLoader;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnDismissListener;
-import android.content.Intent;
-import android.content.Loader;
-import android.content.Loader.OnLoadCompleteListener;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.SipAddress;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.CheckBox;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-
-import com.android.contacts.common.Collapser;
-import com.android.contacts.common.Collapser.Collapsible;
-import com.android.contacts.common.MoreContactUtils;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.R;
-import com.android.dialer.TransactionSafeActivity;
-import com.android.dialer.contact.ContactUpdateService;
-import com.android.dialer.util.IntentUtil;
-import com.android.dialer.util.IntentUtil.CallIntentBuilder;
-import com.android.incallui.Call.LogState;
-import com.android.dialer.util.DialerUtils;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Initiates phone calls or a text message. If there are multiple candidates, this class shows a
- * dialog to pick one. Creating one of these interactions should be done through the static
- * factory methods.
- *
- * Note that this class initiates not only usual *phone* calls but also *SIP* calls.
- *
- * TODO: clean up code and documents since it is quite confusing to use "phone numbers" or
- * "phone calls" here while they can be SIP addresses or SIP calls (See also issue 5039627).
- */
-public class PhoneNumberInteraction implements OnLoadCompleteListener<Cursor> {
- private static final String TAG = PhoneNumberInteraction.class.getSimpleName();
-
- /**
- * A model object for capturing a phone number for a given contact.
- */
- @VisibleForTesting
- /* package */ static class PhoneItem implements Parcelable, Collapsible<PhoneItem> {
- long id;
- String phoneNumber;
- String accountType;
- String dataSet;
- long type;
- String label;
- /** {@link Phone#CONTENT_ITEM_TYPE} or {@link SipAddress#CONTENT_ITEM_TYPE}. */
- String mimeType;
-
- public PhoneItem() {
- }
-
- private PhoneItem(Parcel in) {
- this.id = in.readLong();
- this.phoneNumber = in.readString();
- this.accountType = in.readString();
- this.dataSet = in.readString();
- this.type = in.readLong();
- this.label = in.readString();
- this.mimeType = in.readString();
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(id);
- dest.writeString(phoneNumber);
- dest.writeString(accountType);
- dest.writeString(dataSet);
- dest.writeLong(type);
- dest.writeString(label);
- dest.writeString(mimeType);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void collapseWith(PhoneItem phoneItem) {
- // Just keep the number and id we already have.
- }
-
- @Override
- public boolean shouldCollapseWith(PhoneItem phoneItem, Context context) {
- return MoreContactUtils.shouldCollapse(Phone.CONTENT_ITEM_TYPE, phoneNumber,
- Phone.CONTENT_ITEM_TYPE, phoneItem.phoneNumber);
- }
-
- @Override
- public String toString() {
- return phoneNumber;
- }
-
- public static final Parcelable.Creator<PhoneItem> CREATOR
- = new Parcelable.Creator<PhoneItem>() {
- @Override
- public PhoneItem createFromParcel(Parcel in) {
- return new PhoneItem(in);
- }
-
- @Override
- public PhoneItem[] newArray(int size) {
- return new PhoneItem[size];
- }
- };
- }
-
- /**
- * A list adapter that populates the list of contact's phone numbers.
- */
- private static class PhoneItemAdapter extends ArrayAdapter<PhoneItem> {
- private final int mInteractionType;
-
- public PhoneItemAdapter(Context context, List<PhoneItem> list,
- int interactionType) {
- super(context, R.layout.phone_disambig_item, android.R.id.text2, list);
- mInteractionType = interactionType;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final View view = super.getView(position, convertView, parent);
-
- final PhoneItem item = getItem(position);
- final TextView typeView = (TextView) view.findViewById(android.R.id.text1);
- CharSequence value = ContactDisplayUtils.getLabelForCallOrSms((int) item.type,
- item.label, mInteractionType, getContext());
-
- typeView.setText(value);
- return view;
- }
- }
-
- /**
- * {@link DialogFragment} used for displaying a dialog with a list of phone numbers of which
- * one will be chosen to make a call or initiate an sms message.
- *
- * It is recommended to use
- * {@link PhoneNumberInteraction#startInteractionForPhoneCall(TransactionSafeActivity, Uri)} or
- * {@link PhoneNumberInteraction#startInteractionForTextMessage(TransactionSafeActivity, Uri)}
- * instead of directly using this class, as those methods handle one or multiple data cases
- * appropriately.
- */
- /* Made public to let the system reach this class */
- public static class PhoneDisambiguationDialogFragment extends DialogFragment
- implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
-
- private static final String ARG_PHONE_LIST = "phoneList";
- private static final String ARG_INTERACTION_TYPE = "interactionType";
- private static final String ARG_CALL_INITIATION_TYPE = "callInitiation";
- private static final String ARG_IS_VIDEO_CALL = "is_video_call";
-
- private int mInteractionType;
- private ListAdapter mPhonesAdapter;
- private List<PhoneItem> mPhoneList;
- private int mCallInitiationType;
- private boolean mIsVideoCall;
-
- public static void show(FragmentManager fragmentManager, ArrayList<PhoneItem> phoneList,
- int interactionType, boolean isVideoCall, int callInitiationType) {
- PhoneDisambiguationDialogFragment fragment = new PhoneDisambiguationDialogFragment();
- Bundle bundle = new Bundle();
- bundle.putParcelableArrayList(ARG_PHONE_LIST, phoneList);
- bundle.putInt(ARG_INTERACTION_TYPE, interactionType);
- bundle.putInt(ARG_CALL_INITIATION_TYPE, callInitiationType);
- bundle.putBoolean(ARG_IS_VIDEO_CALL, isVideoCall);
- fragment.setArguments(bundle);
- fragment.show(fragmentManager, TAG);
- }
-
- public PhoneDisambiguationDialogFragment() {
- super();
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final Activity activity = getActivity();
- mPhoneList = getArguments().getParcelableArrayList(ARG_PHONE_LIST);
- mInteractionType = getArguments().getInt(ARG_INTERACTION_TYPE);
- mCallInitiationType = getArguments().getInt(ARG_CALL_INITIATION_TYPE);
- mIsVideoCall = getArguments().getBoolean(ARG_IS_VIDEO_CALL);
-
- mPhonesAdapter = new PhoneItemAdapter(activity, mPhoneList, mInteractionType);
- final LayoutInflater inflater = activity.getLayoutInflater();
- final View setPrimaryView = inflater.inflate(R.layout.set_primary_checkbox, null);
- return new AlertDialog.Builder(activity)
- .setAdapter(mPhonesAdapter, this)
- .setTitle(mInteractionType == ContactDisplayUtils.INTERACTION_SMS
- ? R.string.sms_disambig_title : R.string.call_disambig_title)
- .setView(setPrimaryView)
- .create();
- }
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final Activity activity = getActivity();
- if (activity == null) return;
- final AlertDialog alertDialog = (AlertDialog)dialog;
- if (mPhoneList.size() > which && which >= 0) {
- final PhoneItem phoneItem = mPhoneList.get(which);
- final CheckBox checkBox = (CheckBox)alertDialog.findViewById(R.id.setPrimary);
- if (checkBox.isChecked()) {
- // Request to mark the data as primary in the background.
- final Intent serviceIntent = ContactUpdateService.createSetSuperPrimaryIntent(
- activity, phoneItem.id);
- activity.startService(serviceIntent);
- }
-
- PhoneNumberInteraction.performAction(activity, phoneItem.phoneNumber,
- mInteractionType, mIsVideoCall, mCallInitiationType);
- } else {
- dialog.dismiss();
- }
- }
- }
-
- private static final String[] PHONE_NUMBER_PROJECTION = new String[] {
- Phone._ID, // 0
- Phone.NUMBER, // 1
- Phone.IS_SUPER_PRIMARY, // 2
- RawContacts.ACCOUNT_TYPE, // 3
- RawContacts.DATA_SET, // 4
- Phone.TYPE, // 5
- Phone.LABEL, // 6
- Phone.MIMETYPE, // 7
- Phone.CONTACT_ID // 8
- };
-
- private static final int _ID = 0;
- private static final int NUMBER = 1;
- private static final int IS_SUPER_PRIMARY = 2;
- private static final int ACCOUNT_TYPE = 3;
- private static final int DATA_SET = 4;
- private static final int TYPE = 5;
- private static final int LABEL = 6;
- private static final int MIMETYPE = 7;
- private static final int CONTACT_ID = 8;
-
- private static final String PHONE_NUMBER_SELECTION =
- Data.MIMETYPE + " IN ('"
- + Phone.CONTENT_ITEM_TYPE + "', "
- + "'" + SipAddress.CONTENT_ITEM_TYPE + "') AND "
- + Data.DATA1 + " NOT NULL";
-
- private final Context mContext;
- private final OnDismissListener mDismissListener;
- private final int mInteractionType;
-
- private final int mCallInitiationType;
- private boolean mUseDefault;
-
- private static final int UNKNOWN_CONTACT_ID = -1;
- private long mContactId = UNKNOWN_CONTACT_ID;
-
- private CursorLoader mLoader;
- private boolean mIsVideoCall;
-
- /**
- * Constructs a new {@link PhoneNumberInteraction}. The constructor takes in a {@link Context}
- * instead of a {@link TransactionSafeActivity} for testing purposes to verify the functionality
- * of this class. However, all factory methods for creating {@link PhoneNumberInteraction}s
- * require a {@link TransactionSafeActivity} (i.e. see {@link #startInteractionForPhoneCall}).
- */
- @VisibleForTesting
- /* package */ PhoneNumberInteraction(Context context, int interactionType,
- DialogInterface.OnDismissListener dismissListener) {
- this(context, interactionType, dismissListener, false /*isVideoCall*/,
- LogState.INITIATION_UNKNOWN);
- }
-
- private PhoneNumberInteraction(Context context, int interactionType,
- DialogInterface.OnDismissListener dismissListener, boolean isVideoCall,
- int callInitiationType) {
- mContext = context;
- mInteractionType = interactionType;
- mDismissListener = dismissListener;
- mCallInitiationType = callInitiationType;
- mIsVideoCall = isVideoCall;
- }
-
- private void performAction(String phoneNumber) {
- PhoneNumberInteraction.performAction(mContext, phoneNumber, mInteractionType, mIsVideoCall,
- mCallInitiationType);
- }
-
- private static void performAction(
- Context context, String phoneNumber, int interactionType,
- boolean isVideoCall, int callInitiationType) {
- Intent intent;
- switch (interactionType) {
- case ContactDisplayUtils.INTERACTION_SMS:
- intent = new Intent(
- Intent.ACTION_SENDTO, Uri.fromParts("sms", phoneNumber, null));
- break;
- default:
- intent = new CallIntentBuilder(phoneNumber)
- .setCallInitiationType(callInitiationType)
- .setIsVideoCall(isVideoCall)
- .build();
- break;
- }
- DialerUtils.startActivityWithErrorToast(context, intent);
- }
-
- /**
- * Initiates the interaction. This may result in a phone call or sms message started
- * or a disambiguation dialog to determine which phone number should be used. If there
- * is a primary phone number, it will be automatically used and a disambiguation dialog
- * will no be shown.
- */
- @VisibleForTesting
- /* package */ void startInteraction(Uri uri) {
- startInteraction(uri, true);
- }
-
- /**
- * Initiates the interaction to result in either a phone call or sms message for a contact.
- * @param uri Contact Uri
- * @param useDefault Whether or not to use the primary(default) phone number. If true, the
- * primary phone number will always be used by default if one is available. If false, a
- * disambiguation dialog will be shown regardless of whether or not a primary phone number
- * is available.
- */
- @VisibleForTesting
- /* package */ void startInteraction(Uri uri, boolean useDefault) {
- if (mLoader != null) {
- mLoader.reset();
- }
- mUseDefault = useDefault;
- final Uri queryUri;
- final String inputUriAsString = uri.toString();
- if (inputUriAsString.startsWith(Contacts.CONTENT_URI.toString())) {
- if (!inputUriAsString.endsWith(Contacts.Data.CONTENT_DIRECTORY)) {
- queryUri = Uri.withAppendedPath(uri, Contacts.Data.CONTENT_DIRECTORY);
- } else {
- queryUri = uri;
- }
- } else if (inputUriAsString.startsWith(Data.CONTENT_URI.toString())) {
- queryUri = uri;
- } else {
- throw new UnsupportedOperationException(
- "Input Uri must be contact Uri or data Uri (input: \"" + uri + "\")");
- }
-
- mLoader = new CursorLoader(mContext,
- queryUri,
- PHONE_NUMBER_PROJECTION,
- PHONE_NUMBER_SELECTION,
- null,
- null);
- mLoader.registerListener(0, this);
- mLoader.startLoading();
- }
-
- @Override
- public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
- if (cursor == null) {
- onDismiss();
- return;
- }
- try {
- ArrayList<PhoneItem> phoneList = new ArrayList<PhoneItem>();
- String primaryPhone = null;
- if (!isSafeToCommitTransactions()) {
- onDismiss();
- return;
- }
- while (cursor.moveToNext()) {
- if (mContactId == UNKNOWN_CONTACT_ID) {
- mContactId = cursor.getLong(CONTACT_ID);
- }
-
- if (mUseDefault && cursor.getInt(IS_SUPER_PRIMARY) != 0) {
- // Found super primary, call it.
- primaryPhone = cursor.getString(NUMBER);
- }
-
- PhoneItem item = new PhoneItem();
- item.id = cursor.getLong(_ID);
- item.phoneNumber = cursor.getString(NUMBER);
- item.accountType = cursor.getString(ACCOUNT_TYPE);
- item.dataSet = cursor.getString(DATA_SET);
- item.type = cursor.getInt(TYPE);
- item.label = cursor.getString(LABEL);
- item.mimeType = cursor.getString(MIMETYPE);
-
- phoneList.add(item);
- }
-
- if (mUseDefault && primaryPhone != null) {
- performAction(primaryPhone);
- onDismiss();
- return;
- }
-
- Collapser.collapseList(phoneList, mContext);
- if (phoneList.size() == 0) {
- onDismiss();
- } else if (phoneList.size() == 1) {
- PhoneItem item = phoneList.get(0);
- onDismiss();
- performAction(item.phoneNumber);
- } else {
- // There are multiple candidates. Let the user choose one.
- showDisambiguationDialog(phoneList);
- }
- } finally {
- cursor.close();
- }
- }
-
- private boolean isSafeToCommitTransactions() {
- return mContext instanceof TransactionSafeActivity ?
- ((TransactionSafeActivity) mContext).isSafeToCommitTransactions() : true;
- }
-
- private void onDismiss() {
- if (mDismissListener != null) {
- mDismissListener.onDismiss(null);
- }
- }
-
- /**
- * @param activity that is calling this interaction. This must be of type
- * {@link TransactionSafeActivity} because we need to check on the activity state after the
- * phone numbers have been queried for.
- * @param isVideoCall {@code true} if the call is a video call, {@code false} otherwise.
- * @param callInitiationType Indicates the UI affordance that was used to initiate the call.
- */
- public static void startInteractionForPhoneCall(TransactionSafeActivity activity, Uri uri,
- boolean isVideoCall, int callInitiationType) {
- (new PhoneNumberInteraction(activity, ContactDisplayUtils.INTERACTION_CALL, null,
- isVideoCall, callInitiationType)).startInteraction(uri, true);
- }
-
- /**
- * Start text messaging (a.k.a SMS) action using given contact Uri. If there are multiple
- * candidates for the phone call, dialog is automatically shown and the user is asked to choose
- * one.
- *
- * @param activity that is calling this interaction. This must be of type
- * {@link TransactionSafeActivity} because we need to check on the activity state after the
- * phone numbers have been queried for.
- * @param uri contact Uri (built from {@link Contacts#CONTENT_URI}) or data Uri
- * (built from {@link Data#CONTENT_URI}). Contact Uri may show the disambiguation dialog while
- * data Uri won't.
- */
- public static void startInteractionForTextMessage(TransactionSafeActivity activity, Uri uri) {
- (new PhoneNumberInteraction(activity, ContactDisplayUtils.INTERACTION_SMS, null))
- .startInteraction(uri, true);
- }
-
- @VisibleForTesting
- /* package */ CursorLoader getLoader() {
- return mLoader;
- }
-
- @VisibleForTesting
- /* package */ void showDisambiguationDialog(ArrayList<PhoneItem> phoneList) {
- final Activity activity = (Activity) mContext;
- if (activity.isDestroyed()) {
- // Check whether the activity is still running
- return;
- }
- try {
- PhoneDisambiguationDialogFragment.show(activity.getFragmentManager(),
- phoneList, mInteractionType, mIsVideoCall, mCallInitiationType);
- } catch (IllegalStateException e) {
- // ignore to be safe. Shouldn't happen because we checked the
- // activity wasn't destroyed, but to be safe.
- }
- }
-}
diff --git a/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java b/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java
deleted file mode 100644
index 172a4efef..000000000
--- a/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.interactions;
-
-import static android.Manifest.permission.READ_CONTACTS;
-import static android.Manifest.permission.WRITE_CONTACTS;
-
-import android.content.BroadcastReceiver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.PhoneLookup;
-import android.provider.ContactsContract.PinnedPositions;
-import android.text.TextUtils;
-
-import com.android.contacts.common.util.PermissionsUtil;
-
-/**
- * This broadcast receiver is used to listen to outgoing calls and undemote formerly demoted
- * contacts if a phone call is made to a phone number belonging to that contact.
- *
- * NOTE This doesn't work for corp contacts.
- */
-public class UndemoteOutgoingCallReceiver extends BroadcastReceiver {
-
- private static final long NO_CONTACT_FOUND = -1;
-
- @Override
- public void onReceive(final Context context, Intent intent) {
- if (!PermissionsUtil.hasPermission(context, READ_CONTACTS)
- || !PermissionsUtil.hasPermission(context, WRITE_CONTACTS)) {
- return;
- }
- if (intent != null && Intent.ACTION_NEW_OUTGOING_CALL.equals(intent.getAction())) {
- final String number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
- if (TextUtils.isEmpty(number)) {
- return;
- }
- new Thread() {
- @Override
- public void run() {
- final long id = getContactIdFromPhoneNumber(context, number);
- if (id != NO_CONTACT_FOUND) {
- undemoteContactWithId(context, id);
- }
- }
- }.start();
- }
- }
-
- private void undemoteContactWithId(Context context, long id) {
- // If the contact is not demoted, this will not do anything. Otherwise, it will
- // restore it to an unpinned position. If it was a frequently called contact, it will
- // show up once again show up on the favorites screen.
- if (PermissionsUtil.hasPermission(context, WRITE_CONTACTS)) {
- try {
- PinnedPositions.undemote(context.getContentResolver(), id);
- } catch (SecurityException e) {
- // Just in case
- }
- }
- }
-
- private long getContactIdFromPhoneNumber(Context context, String number) {
- if (!PermissionsUtil.hasPermission(context, READ_CONTACTS)) {
- return NO_CONTACT_FOUND;
- }
- final Uri contactUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
- Uri.encode(number));
- final Cursor cursor;
- try {
- cursor = context.getContentResolver().query(contactUri, new String[] {
- PhoneLookup._ID}, null, null, null);
- } catch (SecurityException e) {
- // Just in case
- return NO_CONTACT_FOUND;
- }
- if (cursor == null) {
- return NO_CONTACT_FOUND;
- }
- try {
- if (cursor.moveToFirst()) {
- final long id = cursor.getLong(0);
- return id;
- } else {
- return NO_CONTACT_FOUND;
- }
- } finally {
- cursor.close();
- }
- }
-}
diff --git a/src/com/android/dialer/list/AllContactsFragment.java b/src/com/android/dialer/list/AllContactsFragment.java
deleted file mode 100644
index 7e76279d9..000000000
--- a/src/com/android/dialer/list/AllContactsFragment.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.list;
-
-import static android.Manifest.permission.READ_CONTACTS;
-
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.Loader;
-import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.QuickContact;
-import android.support.v13.app.FragmentCompat;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.list.ContactEntryListAdapter;
-import com.android.contacts.common.list.ContactEntryListFragment;
-import com.android.contacts.common.list.ContactListFilter;
-import com.android.contacts.common.list.DefaultContactListAdapter;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.common.util.ViewUtil;
-import com.android.dialer.R;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.IntentUtil;
-import com.android.dialer.widget.EmptyContentView;
-import com.android.dialer.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener;
-
-/**
- * Fragments to show all contacts with phone numbers.
- */
-public class AllContactsFragment extends ContactEntryListFragment<ContactEntryListAdapter>
- implements OnEmptyViewActionButtonClickedListener,
- FragmentCompat.OnRequestPermissionsResultCallback {
-
- private static final int READ_CONTACTS_PERMISSION_REQUEST_CODE = 1;
-
- private EmptyContentView mEmptyListView;
-
- /**
- * Listen to broadcast events about permissions in order to be notified if the READ_CONTACTS
- * permission is granted via the UI in another fragment.
- */
- private BroadcastReceiver mReadContactsPermissionGrantedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- reloadData();
- }
- };
-
- public AllContactsFragment() {
- setQuickContactEnabled(false);
- setAdjustSelectionBoundsEnabled(true);
- setPhotoLoaderEnabled(true);
- setSectionHeaderDisplayEnabled(true);
- setDarkTheme(false);
- setVisibleScrollbarEnabled(true);
- }
-
- @Override
- public void onViewCreated(View view, android.os.Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- mEmptyListView = (EmptyContentView) view.findViewById(R.id.empty_list_view);
- mEmptyListView.setImage(R.drawable.empty_contacts);
- mEmptyListView.setDescription(R.string.all_contacts_empty);
- mEmptyListView.setActionClickedListener(this);
- getListView().setEmptyView(mEmptyListView);
- mEmptyListView.setVisibility(View.GONE);
-
- ViewUtil.addBottomPaddingToListViewForFab(getListView(), getResources());
- }
-
- @Override
- public void onStart() {
- super.onStart();
- PermissionsUtil.registerPermissionReceiver(getActivity(),
- mReadContactsPermissionGrantedReceiver, READ_CONTACTS);
- }
-
- @Override
- public void onStop() {
- PermissionsUtil.unregisterPermissionReceiver(getActivity(),
- mReadContactsPermissionGrantedReceiver);
- super.onStop();
- }
-
- @Override
- protected void startLoading() {
- if (PermissionsUtil.hasPermission(getActivity(), READ_CONTACTS)) {
- super.startLoading();
- mEmptyListView.setDescription(R.string.all_contacts_empty);
- mEmptyListView.setActionLabel(R.string.all_contacts_empty_add_contact_action);
- } else {
- mEmptyListView.setDescription(R.string.permission_no_contacts);
- mEmptyListView.setActionLabel(R.string.permission_single_turn_on);
- mEmptyListView.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- super.onLoadFinished(loader, data);
-
- if (data == null || data.getCount() == 0) {
- mEmptyListView.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- protected ContactEntryListAdapter createListAdapter() {
- final DefaultContactListAdapter adapter = new DefaultContactListAdapter(getActivity()) {
- @Override
- protected void bindView(View itemView, int partition, Cursor cursor, int position) {
- super.bindView(itemView, partition, cursor, position);
- itemView.setTag(this.getContactUri(partition, cursor));
- }
- };
- adapter.setDisplayPhotos(true);
- adapter.setFilter(ContactListFilter.createFilterWithType(
- ContactListFilter.FILTER_TYPE_DEFAULT));
- adapter.setSectionHeaderDisplayEnabled(isSectionHeaderDisplayEnabled());
- return adapter;
- }
-
- @Override
- protected View inflateView(LayoutInflater inflater, ViewGroup container) {
- return inflater.inflate(R.layout.all_contacts_fragment, null);
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- final Uri uri = (Uri) view.getTag();
- if (uri != null) {
- if (CompatUtils.hasPrioritizedMimeType()) {
- QuickContact.showQuickContact(getContext(), view, uri, null,
- Phone.CONTENT_ITEM_TYPE);
- } else {
- QuickContact.showQuickContact(getActivity(), view, uri, QuickContact.MODE_LARGE,
- null);
- }
- }
- }
-
- @Override
- protected void onItemClick(int position, long id) {
- // Do nothing. Implemented to satisfy ContactEntryListFragment.
- }
-
- @Override
- public void onEmptyViewActionButtonClicked() {
- final Activity activity = getActivity();
- if (activity == null) {
- return;
- }
-
- if (!PermissionsUtil.hasPermission(activity, READ_CONTACTS)) {
- FragmentCompat.requestPermissions(this, new String[] {READ_CONTACTS},
- READ_CONTACTS_PERMISSION_REQUEST_CODE);
- } else {
- // Add new contact
- DialerUtils.startActivityWithErrorToast(activity, IntentUtil.getNewContactIntent(),
- R.string.add_contact_not_available);
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions,
- int[] grantResults) {
- if (requestCode == READ_CONTACTS_PERMISSION_REQUEST_CODE) {
- if (grantResults.length >= 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
- // Force a refresh of the data since we were missing the permission before this.
- reloadData();
- }
- }
- }
-}
diff --git a/src/com/android/dialer/list/BlockedListSearchAdapter.java b/src/com/android/dialer/list/BlockedListSearchAdapter.java
deleted file mode 100644
index 1618826bd..000000000
--- a/src/com/android/dialer/list/BlockedListSearchAdapter.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.dialer.list;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.Color;
-import android.view.View;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.list.ContactListItemView;
-import com.android.dialer.R;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-
-/**
- * List adapter to display search results for adding a blocked number.
- */
-public class BlockedListSearchAdapter extends RegularSearchListAdapter {
-
- private Resources mResources;
- private FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
-
- public BlockedListSearchAdapter(Context context) {
- super(context);
- mResources = context.getResources();
- disableAllShortcuts();
- setShortcutEnabled(SHORTCUT_BLOCK_NUMBER, true);
-
- mFilteredNumberAsyncQueryHandler =
- new FilteredNumberAsyncQueryHandler(context.getContentResolver());
- }
-
- @Override
- protected boolean isChanged(boolean showNumberShortcuts) {
- return setShortcutEnabled(SHORTCUT_BLOCK_NUMBER, showNumberShortcuts || mIsQuerySipAddress);
- }
-
- public void setViewBlocked(ContactListItemView view, Integer id) {
- view.setTag(R.id.block_id, id);
- final int textColor = mResources.getColor(R.color.blocked_number_block_color);
- view.getDataView().setTextColor(textColor);
- view.getLabelView().setTextColor(textColor);
- //TODO: Add icon
- }
-
- public void setViewUnblocked(ContactListItemView view) {
- view.setTag(R.id.block_id, null);
- final int textColor = mResources.getColor(R.color.dialtacts_secondary_text_color);
- view.getDataView().setTextColor(textColor);
- view.getLabelView().setTextColor(textColor);
- //TODO: Remove icon
- }
-
- @Override
- protected void bindView(View itemView, int partition, Cursor cursor, int position) {
- super.bindView(itemView, partition, cursor, position);
-
- final ContactListItemView view = (ContactListItemView) itemView;
- // Reset view state to unblocked.
- setViewUnblocked(view);
-
- final String number = getPhoneNumber(position);
- final String countryIso = GeoUtil.getCurrentCountryIso(mContext);
- final FilteredNumberAsyncQueryHandler.OnCheckBlockedListener onCheckListener =
- new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
- @Override
- public void onCheckComplete(Integer id) {
- if (id != null) {
- setViewBlocked(view, id);
- }
- }
- };
- mFilteredNumberAsyncQueryHandler.isBlockedNumber(
- onCheckListener, number, countryIso);
- }
-}
diff --git a/src/com/android/dialer/list/BlockedListSearchFragment.java b/src/com/android/dialer/list/BlockedListSearchFragment.java
deleted file mode 100644
index da6b42820..000000000
--- a/src/com/android/dialer/list/BlockedListSearchFragment.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * 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.dialer.list;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.telephony.PhoneNumberUtils;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.EditText;
-import android.widget.Toast;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.list.ContactEntryListAdapter;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.R;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener;
-import com.android.dialer.filterednumber.BlockNumberDialogFragment;
-import com.android.dialer.logging.InteractionEvent;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.widget.SearchEditTextLayout;
-
-public class BlockedListSearchFragment extends RegularSearchFragment
- implements BlockNumberDialogFragment.Callback {
- private static final String TAG = BlockedListSearchFragment.class.getSimpleName();
-
- private static final String KEY_SEARCH_QUERY = "search_query";
-
- private FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
-
- private EditText mSearchView;
-
- private final TextWatcher mPhoneSearchQueryTextListener = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- setQueryString(s.toString(), false);
- }
-
- @Override
- public void afterTextChanged(Editable s) {}
- };
-
- private final SearchEditTextLayout.Callback mSearchLayoutCallback =
- new SearchEditTextLayout.Callback() {
- @Override
- public void onBackButtonClicked() {
- getActivity().onBackPressed();
- }
-
- @Override
- public void onSearchViewClicked() {
- }
- };
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setShowEmptyListForNullQuery(true);
- /*
- * Pass in the empty string here so ContactEntryListFragment#setQueryString interprets it as
- * an empty search query, rather than as an uninitalized value. In the latter case, the
- * adapter returned by #createListAdapter is used, which populates the view with contacts.
- * Passing in the empty string forces ContactEntryListFragment to interpret it as an empty
- * query, which results in showing an empty view
- */
- setQueryString(getQueryString() == null ? "" : getQueryString(), false);
- mFilteredNumberAsyncQueryHandler = new FilteredNumberAsyncQueryHandler(
- getContext().getContentResolver());
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
- actionBar.setCustomView(R.layout.search_edittext);
- actionBar.setDisplayShowCustomEnabled(true);
- actionBar.setDisplayHomeAsUpEnabled(false);
- actionBar.setDisplayShowHomeEnabled(false);
-
- final SearchEditTextLayout searchEditTextLayout = (SearchEditTextLayout) actionBar
- .getCustomView().findViewById(R.id.search_view_container);
- searchEditTextLayout.expand(false, true);
- searchEditTextLayout.setCallback(mSearchLayoutCallback);
- searchEditTextLayout.setBackgroundDrawable(null);
-
- mSearchView = (EditText) searchEditTextLayout.findViewById(R.id.search_view);
- mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
- mSearchView.setHint(R.string.block_number_search_hint);
-
- searchEditTextLayout.findViewById(R.id.search_box_expanded)
- .setBackgroundColor(getContext().getResources().getColor(android.R.color.white));
-
- if (!TextUtils.isEmpty(getQueryString())) {
- mSearchView.setText(getQueryString());
- }
-
- // TODO: Don't set custom text size; use default search text size.
- mSearchView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- getResources().getDimension(R.dimen.blocked_number_search_text_size));
- }
-
- @Override
- protected ContactEntryListAdapter createListAdapter() {
- BlockedListSearchAdapter adapter = new BlockedListSearchAdapter(getActivity());
- adapter.setDisplayPhotos(true);
- // Don't show SIP addresses.
- adapter.setUseCallableUri(false);
- // Keep in sync with the queryString set in #onCreate
- adapter.setQueryString(getQueryString() == null ? "" : getQueryString());
- return adapter;
- }
-
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- super.onItemClick(parent, view, position, id);
- final int adapterPosition = position - getListView().getHeaderViewsCount();
- final BlockedListSearchAdapter adapter = (BlockedListSearchAdapter) getAdapter();
- final int shortcutType = adapter.getShortcutTypeFromPosition(adapterPosition);
- final Integer blockId = (Integer) view.getTag(R.id.block_id);
- final String number;
- switch (shortcutType) {
- case DialerPhoneNumberListAdapter.SHORTCUT_INVALID:
- // Handles click on a search result, either contact or nearby places result.
- number = adapter.getPhoneNumber(adapterPosition);
- blockContactNumber(number, blockId);
- break;
- case DialerPhoneNumberListAdapter.SHORTCUT_BLOCK_NUMBER:
- // Handles click on 'Block number' shortcut to add the user query as a number.
- number = adapter.getQueryString();
- blockNumber(number);
- break;
- default:
- Log.w(TAG, "Ignoring unsupported shortcut type: " + shortcutType);
- break;
- }
- }
-
- @Override
- protected void onItemClick(int position, long id) {
- // Prevent SearchFragment.onItemClicked from being called.
- }
-
- private void blockNumber(final String number) {
- final String countryIso = GeoUtil.getCurrentCountryIso(getContext());
- final OnCheckBlockedListener onCheckListener = new OnCheckBlockedListener() {
- @Override
- public void onCheckComplete(Integer id) {
- if (id == null) {
- BlockNumberDialogFragment.show(
- id,
- number,
- countryIso,
- PhoneNumberUtils.formatNumber(number, countryIso),
- R.id.blocked_numbers_activity_container,
- getFragmentManager(),
- BlockedListSearchFragment.this);
- } else {
- Toast.makeText(getContext(),
- ContactDisplayUtils.getTtsSpannedPhoneNumber(getResources(),
- R.string.alreadyBlocked, number),
- Toast.LENGTH_SHORT).show();
- }
- }
- };
- final boolean success = mFilteredNumberAsyncQueryHandler.isBlockedNumber(
- onCheckListener, number, countryIso);
- if (!success) {
- Toast.makeText(getContext(),
- ContactDisplayUtils.getTtsSpannedPhoneNumber(
- getResources(), R.string.invalidNumber, number),
- Toast.LENGTH_SHORT).show();
- }
- }
-
- @Override
- public void onFilterNumberSuccess() {
- Logger.logInteraction(InteractionEvent.BLOCK_NUMBER_MANAGEMENT_SCREEN);
- goBack();
- }
-
- @Override
- public void onUnfilterNumberSuccess() {
- Log.wtf(TAG, "Unblocked a number from the BlockedListSearchFragment");
- goBack();
- }
-
- private void goBack() {
- Activity activity = getActivity();
- if (activity == null) {
- return;
- }
- activity.onBackPressed();
- }
-
- @Override
- public void onChangeFilteredNumberUndo() {
- getAdapter().notifyDataSetChanged();
- }
-
- private void blockContactNumber(final String number, final Integer blockId) {
- if (blockId != null) {
- Toast.makeText(getContext(), ContactDisplayUtils.getTtsSpannedPhoneNumber(
- getResources(), R.string.alreadyBlocked, number),
- Toast.LENGTH_SHORT).show();
- return;
- }
-
- BlockNumberDialogFragment.show(
- blockId,
- number,
- GeoUtil.getCurrentCountryIso(getContext()),
- number,
- R.id.blocked_numbers_activity_container,
- getFragmentManager(),
- this);
- }
-}
diff --git a/src/com/android/dialer/list/ContentChangedFilter.java b/src/com/android/dialer/list/ContentChangedFilter.java
deleted file mode 100644
index e552aa3f0..000000000
--- a/src/com/android/dialer/list/ContentChangedFilter.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.android.dialer.list;
-
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-
-/**
- * AccessibilityDelegate that will filter out TYPE_WINDOW_CONTENT_CHANGED
- * Used to suppress "Showing items x of y" from firing of ListView whenever it's content changes.
- * AccessibilityEvent can only be rejected at a view's parent once it is generated,
- * use addToParent() to add this delegate to the parent.
- */
-public class ContentChangedFilter extends AccessibilityDelegate {
- //the view we don't want TYPE_WINDOW_CONTENT_CHANGED to fire.
- private View mView;
-
- /**
- * Add this delegate to the parent of @param view to filter out TYPE_WINDOW_CONTENT_CHANGED
- */
- public static void addToParent(View view){
- View parent = (View) view.getParent();
- parent.setAccessibilityDelegate(new ContentChangedFilter(view));
- }
-
- private ContentChangedFilter(View view){
- super();
- mView = view;
- }
- @Override
- public boolean onRequestSendAccessibilityEvent (ViewGroup host, View child, AccessibilityEvent event){
- if(child == mView){
- if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED){
- return false;
- }
- }
- return super.onRequestSendAccessibilityEvent(host,child,event);
- }
-
-}
diff --git a/src/com/android/dialer/list/DialerPhoneNumberListAdapter.java b/src/com/android/dialer/list/DialerPhoneNumberListAdapter.java
deleted file mode 100644
index 7164de2d7..000000000
--- a/src/com/android/dialer/list/DialerPhoneNumberListAdapter.java
+++ /dev/null
@@ -1,220 +0,0 @@
-package com.android.dialer.list;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.telephony.PhoneNumberUtils;
-import android.text.BidiFormatter;
-import android.text.TextDirectionHeuristics;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.list.ContactListItemView;
-import com.android.contacts.common.list.PhoneNumberListAdapter;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.R;
-
-/**
- * {@link PhoneNumberListAdapter} with the following added shortcuts, that are displayed as list
- * items:
- * 1) Directly calling the phone number query
- * 2) Adding the phone number query to a contact
- *
- * These shortcuts can be enabled or disabled to toggle whether or not they show up in the
- * list.
- */
-public class DialerPhoneNumberListAdapter extends PhoneNumberListAdapter {
-
- private String mFormattedQueryString;
- private String mCountryIso;
-
- public final static int SHORTCUT_INVALID = -1;
- public final static int SHORTCUT_DIRECT_CALL = 0;
- public final static int SHORTCUT_CREATE_NEW_CONTACT = 1;
- public final static int SHORTCUT_ADD_TO_EXISTING_CONTACT = 2;
- public final static int SHORTCUT_SEND_SMS_MESSAGE = 3;
- public final static int SHORTCUT_MAKE_VIDEO_CALL = 4;
- public final static int SHORTCUT_BLOCK_NUMBER = 5;
-
- public final static int SHORTCUT_COUNT = 6;
-
- private final boolean[] mShortcutEnabled = new boolean[SHORTCUT_COUNT];
-
- private final BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
- private boolean mVideoCallingEnabled = false;
-
- public DialerPhoneNumberListAdapter(Context context) {
- super(context);
-
- mCountryIso = GeoUtil.getCurrentCountryIso(context);
- mVideoCallingEnabled = CallUtil.isVideoEnabled(context);
- }
-
- @Override
- public int getCount() {
- return super.getCount() + getShortcutCount();
- }
-
- /**
- * @return The number of enabled shortcuts. Ranges from 0 to a maximum of SHORTCUT_COUNT
- */
- public int getShortcutCount() {
- int count = 0;
- for (int i = 0; i < mShortcutEnabled.length; i++) {
- if (mShortcutEnabled[i]) count++;
- }
- return count;
- }
-
- public void disableAllShortcuts() {
- for (int i = 0; i < mShortcutEnabled.length; i++) {
- mShortcutEnabled[i] = false;
- }
- }
-
- @Override
- public int getItemViewType(int position) {
- final int shortcut = getShortcutTypeFromPosition(position);
- if (shortcut >= 0) {
- // shortcutPos should always range from 1 to SHORTCUT_COUNT
- return super.getViewTypeCount() + shortcut;
- } else {
- return super.getItemViewType(position);
- }
- }
-
- @Override
- public int getViewTypeCount() {
- // Number of item view types in the super implementation + 2 for the 2 new shortcuts
- return super.getViewTypeCount() + SHORTCUT_COUNT;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final int shortcutType = getShortcutTypeFromPosition(position);
- if (shortcutType >= 0) {
- if (convertView != null) {
- assignShortcutToView((ContactListItemView) convertView, shortcutType);
- return convertView;
- } else {
- final ContactListItemView v = new ContactListItemView(getContext(), null,
- mVideoCallingEnabled);
- assignShortcutToView(v, shortcutType);
- return v;
- }
- } else {
- return super.getView(position, convertView, parent);
- }
- }
-
- @Override
- protected ContactListItemView newView(
- Context context, int partition, Cursor cursor, int position, ViewGroup parent) {
- final ContactListItemView view = super.newView(context, partition, cursor, position,
- parent);
-
- view.setSupportVideoCallIcon(mVideoCallingEnabled);
- return view;
- }
-
- /**
- * @param position The position of the item
- * @return The enabled shortcut type matching the given position if the item is a
- * shortcut, -1 otherwise
- */
- public int getShortcutTypeFromPosition(int position) {
- int shortcutCount = position - super.getCount();
- if (shortcutCount >= 0) {
- // Iterate through the array of shortcuts, looking only for shortcuts where
- // mShortcutEnabled[i] is true
- for (int i = 0; shortcutCount >= 0 && i < mShortcutEnabled.length; i++) {
- if (mShortcutEnabled[i]) {
- shortcutCount--;
- if (shortcutCount < 0) return i;
- }
- }
- throw new IllegalArgumentException("Invalid position - greater than cursor count "
- + " but not a shortcut.");
- }
- return SHORTCUT_INVALID;
- }
-
- @Override
- public boolean isEmpty() {
- return getShortcutCount() == 0 && super.isEmpty();
- }
-
- @Override
- public boolean isEnabled(int position) {
- final int shortcutType = getShortcutTypeFromPosition(position);
- if (shortcutType >= 0) {
- return true;
- } else {
- return super.isEnabled(position);
- }
- }
-
- private void assignShortcutToView(ContactListItemView v, int shortcutType) {
- final CharSequence text;
- final int drawableId;
- final Resources resources = getContext().getResources();
- final String number = getFormattedQueryString();
- switch (shortcutType) {
- case SHORTCUT_DIRECT_CALL:
- text = ContactDisplayUtils.getTtsSpannedPhoneNumber(resources,
- R.string.search_shortcut_call_number,
- mBidiFormatter.unicodeWrap(number, TextDirectionHeuristics.LTR));
- drawableId = R.drawable.ic_search_phone;
- break;
- case SHORTCUT_CREATE_NEW_CONTACT:
- text = resources.getString(R.string.search_shortcut_create_new_contact);
- drawableId = R.drawable.ic_search_add_contact;
- break;
- case SHORTCUT_ADD_TO_EXISTING_CONTACT:
- text = resources.getString(R.string.search_shortcut_add_to_contact);
- drawableId = R.drawable.ic_person_24dp;
- break;
- case SHORTCUT_SEND_SMS_MESSAGE:
- text = resources.getString(R.string.search_shortcut_send_sms_message);
- drawableId = R.drawable.ic_message_24dp;
- break;
- case SHORTCUT_MAKE_VIDEO_CALL:
- text = resources.getString(R.string.search_shortcut_make_video_call);
- drawableId = R.drawable.ic_videocam;
- break;
- case SHORTCUT_BLOCK_NUMBER:
- text = resources.getString(R.string.search_shortcut_block_number);
- drawableId = R.drawable.ic_not_interested_googblue_24dp;
- break;
- default:
- throw new IllegalArgumentException("Invalid shortcut type");
- }
- v.setDrawableResource(drawableId);
- v.setDisplayName(text);
- v.setPhotoPosition(super.getPhotoPosition());
- v.setAdjustSelectionBoundsEnabled(false);
- }
-
- /**
- * @return True if the shortcut state (disabled vs enabled) was changed by this operation
- */
- public boolean setShortcutEnabled(int shortcutType, boolean visible) {
- final boolean changed = mShortcutEnabled[shortcutType] != visible;
- mShortcutEnabled[shortcutType] = visible;
- return changed;
- }
-
- public String getFormattedQueryString() {
- return mFormattedQueryString;
- }
-
- @Override
- public void setQueryString(String queryString) {
- mFormattedQueryString = PhoneNumberUtils.formatNumber(
- PhoneNumberUtils.normalizeNumber(queryString), mCountryIso);
- super.setQueryString(queryString);
- }
-}
diff --git a/src/com/android/dialer/list/DragDropController.java b/src/com/android/dialer/list/DragDropController.java
deleted file mode 100644
index 66ba513a8..000000000
--- a/src/com/android/dialer/list/DragDropController.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.android.dialer.list;
-
-import android.util.Log;
-import android.view.View;
-
-import com.android.contacts.common.compat.CompatUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class that handles and combines drag events generated from multiple views, and then fires
- * off events to any OnDragDropListeners that have registered for callbacks.
- */
-public class DragDropController {
-
- private final List<OnDragDropListener> mOnDragDropListeners =
- new ArrayList<OnDragDropListener>();
- private final DragItemContainer mDragItemContainer;
- private final int[] mLocationOnScreen = new int[2];
-
- /**
- * Callback interface used to retrieve views based on the current touch coordinates of the
- * drag event. The {@link DragItemContainer} houses the draggable views that this
- * {@link DragDropController} controls.
- */
- public interface DragItemContainer {
- public PhoneFavoriteSquareTileView getViewForLocation(int x, int y);
- }
-
- public DragDropController(DragItemContainer dragItemContainer) {
- mDragItemContainer = dragItemContainer;
- }
-
- /**
- * @return True if the drag is started, false if the drag is cancelled for some reason.
- */
- boolean handleDragStarted(View v, int x, int y) {
- int screenX = x;
- int screenY = y;
- // The coordinates in dragEvent of DragEvent.ACTION_DRAG_STARTED before NYC is window-related.
- // This is fixed in NYC.
- if (CompatUtils.isNCompatible()) {
- v.getLocationOnScreen(mLocationOnScreen);
- screenX = x + mLocationOnScreen[0];
- screenY = y + mLocationOnScreen[1];
- }
- final PhoneFavoriteSquareTileView tileView = mDragItemContainer.getViewForLocation(
- screenX, screenY);
- if (tileView == null) {
- return false;
- }
- for (int i = 0; i < mOnDragDropListeners.size(); i++) {
- mOnDragDropListeners.get(i).onDragStarted(screenX, screenY, tileView);
- }
-
- return true;
- }
-
- public void handleDragHovered(View v, int x, int y) {
- v.getLocationOnScreen(mLocationOnScreen);
- final int screenX = x + mLocationOnScreen[0];
- final int screenY = y + mLocationOnScreen[1];
- final PhoneFavoriteSquareTileView view = mDragItemContainer.getViewForLocation(
- screenX, screenY);
- for (int i = 0; i < mOnDragDropListeners.size(); i++) {
- mOnDragDropListeners.get(i).onDragHovered(screenX, screenY, view);
- }
- }
-
- public void handleDragFinished(int x, int y, boolean isRemoveView) {
- if (isRemoveView) {
- for (int i = 0; i < mOnDragDropListeners.size(); i++) {
- mOnDragDropListeners.get(i).onDroppedOnRemove();
- }
- }
-
- for (int i = 0; i < mOnDragDropListeners.size(); i++) {
- mOnDragDropListeners.get(i).onDragFinished(x, y);
- }
- }
-
- public void addOnDragDropListener(OnDragDropListener listener) {
- if (!mOnDragDropListeners.contains(listener)) {
- mOnDragDropListeners.add(listener);
- }
- }
-
- public void removeOnDragDropListener(OnDragDropListener listener) {
- if (mOnDragDropListeners.contains(listener)) {
- mOnDragDropListeners.remove(listener);
- }
- }
-
-}
diff --git a/src/com/android/dialer/list/ListsFragment.java b/src/com/android/dialer/list/ListsFragment.java
deleted file mode 100644
index 52bf3cbb5..000000000
--- a/src/com/android/dialer/list/ListsFragment.java
+++ /dev/null
@@ -1,487 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.list;
-
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.os.Trace;
-import android.preference.PreferenceManager;
-import android.provider.CallLog.Calls;
-import android.support.v13.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.support.v4.view.ViewPager.OnPageChangeListener;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.contacts.common.list.ViewPagerTabs;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.R;
-import com.android.dialer.calllog.CallLogFragment;
-import com.android.dialer.calllog.CallLogNotificationsHelper;
-import com.android.dialer.calllog.CallLogQueryHandler;
-import com.android.dialer.calllog.VisualVoicemailCallLogFragment;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.ScreenEvent;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.voicemail.VisualVoicemailEnabledChecker;
-import com.android.dialer.voicemail.VoicemailStatusHelper;
-import com.android.dialer.voicemail.VoicemailStatusHelperImpl;
-import com.android.dialer.widget.ActionBarController;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Fragment that is used as the main screen of the Dialer.
- *
- * Contains a ViewPager that contains various contact lists like the Speed Dial list and the
- * All Contacts list. This will also eventually contain the logic that allows sliding the
- * ViewPager containing the lists up above the search bar and pin it against the top of the
- * screen.
- */
-public class ListsFragment extends Fragment
- implements ViewPager.OnPageChangeListener, CallLogQueryHandler.Listener {
-
- private static final boolean DEBUG = DialtactsActivity.DEBUG;
- private static final String TAG = "ListsFragment";
-
- public static final int TAB_INDEX_SPEED_DIAL = 0;
- public static final int TAB_INDEX_HISTORY = 1;
- public static final int TAB_INDEX_ALL_CONTACTS = 2;
- public static final int TAB_INDEX_VOICEMAIL = 3;
-
- public static final int TAB_COUNT_DEFAULT = 3;
- public static final int TAB_COUNT_WITH_VOICEMAIL = 4;
-
- public interface HostInterface {
- public ActionBarController getActionBarController();
- }
-
- private ActionBar mActionBar;
- private ViewPager mViewPager;
- private ViewPagerTabs mViewPagerTabs;
- private ViewPagerAdapter mViewPagerAdapter;
- private RemoveView mRemoveView;
- private View mRemoveViewContent;
-
- private SpeedDialFragment mSpeedDialFragment;
- private CallLogFragment mHistoryFragment;
- private AllContactsFragment mAllContactsFragment;
- private CallLogFragment mVoicemailFragment;
-
- private SharedPreferences mPrefs;
- private boolean mHasActiveVoicemailProvider;
- private boolean mHasFetchedVoicemailStatus;
- private boolean mShowVoicemailTabAfterVoicemailStatusIsFetched;
-
- private VoicemailStatusHelper mVoicemailStatusHelper;
- private ArrayList<OnPageChangeListener> mOnPageChangeListeners =
- new ArrayList<OnPageChangeListener>();
-
- private String[] mTabTitles;
- private int[] mTabIcons;
-
- /**
- * The position of the currently selected tab.
- */
- private int mTabIndex = TAB_INDEX_SPEED_DIAL;
- private CallLogQueryHandler mCallLogQueryHandler;
-
- public class ViewPagerAdapter extends FragmentPagerAdapter {
- private final List<Fragment> mFragments = new ArrayList<>();
-
- public ViewPagerAdapter(FragmentManager fm) {
- super(fm);
- for (int i = 0; i < TAB_COUNT_WITH_VOICEMAIL; i++) {
- mFragments.add(null);
- }
- }
-
- @Override
- public long getItemId(int position) {
- return getRtlPosition(position);
- }
-
- @Override
- public Fragment getItem(int position) {
- switch (getRtlPosition(position)) {
- case TAB_INDEX_SPEED_DIAL:
- mSpeedDialFragment = new SpeedDialFragment();
- return mSpeedDialFragment;
- case TAB_INDEX_HISTORY:
- mHistoryFragment = new CallLogFragment(CallLogQueryHandler.CALL_TYPE_ALL);
- return mHistoryFragment;
- case TAB_INDEX_ALL_CONTACTS:
- mAllContactsFragment = new AllContactsFragment();
- return mAllContactsFragment;
- case TAB_INDEX_VOICEMAIL:
- mVoicemailFragment = new VisualVoicemailCallLogFragment();
- return mVoicemailFragment;
- }
- throw new IllegalStateException("No fragment at position " + position);
- }
-
- @Override
- public Fragment instantiateItem(ViewGroup container, int position) {
- // On rotation the FragmentManager handles rotation. Therefore getItem() isn't called.
- // Copy the fragments that the FragmentManager finds so that we can store them in
- // instance variables for later.
- final Fragment fragment =
- (Fragment) super.instantiateItem(container, position);
- if (fragment instanceof SpeedDialFragment) {
- mSpeedDialFragment = (SpeedDialFragment) fragment;
- } else if (fragment instanceof CallLogFragment && position == TAB_INDEX_HISTORY) {
- mHistoryFragment = (CallLogFragment) fragment;
- } else if (fragment instanceof AllContactsFragment) {
- mAllContactsFragment = (AllContactsFragment) fragment;
- } else if (fragment instanceof CallLogFragment && position == TAB_INDEX_VOICEMAIL) {
- mVoicemailFragment = (CallLogFragment) fragment;
- }
- mFragments.set(position, fragment);
- return fragment;
- }
-
- /**
- * When {@link android.support.v4.view.PagerAdapter#notifyDataSetChanged} is called,
- * this method is called on all pages to determine whether they need to be recreated.
- * When the voicemail tab is removed, the view needs to be recreated by returning
- * POSITION_NONE. If notifyDataSetChanged is called for some other reason, the voicemail
- * tab is recreated only if it is active. All other tabs do not need to be recreated
- * and POSITION_UNCHANGED is returned.
- */
- @Override
- public int getItemPosition(Object object) {
- return !mHasActiveVoicemailProvider &&
- mFragments.indexOf(object) == TAB_INDEX_VOICEMAIL ? POSITION_NONE :
- POSITION_UNCHANGED;
- }
-
- @Override
- public int getCount() {
- return mHasActiveVoicemailProvider ? TAB_COUNT_WITH_VOICEMAIL : TAB_COUNT_DEFAULT;
- }
-
- @Override
- public CharSequence getPageTitle(int position) {
- return mTabTitles[position];
- }
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- Trace.beginSection(TAG + " onCreate");
- super.onCreate(savedInstanceState);
-
- mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
- mHasFetchedVoicemailStatus = false;
-
- mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
- mHasActiveVoicemailProvider = mPrefs.getBoolean(
- VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, false);
-
- Trace.endSection();
- }
-
- @Override
- public void onResume() {
- Trace.beginSection(TAG + " onResume");
- super.onResume();
-
- mActionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
- if (getUserVisibleHint()) {
- sendScreenViewForCurrentPosition();
- }
-
- // Fetch voicemail status to determine if we should show the voicemail tab.
- mCallLogQueryHandler =
- new CallLogQueryHandler(getActivity(), getActivity().getContentResolver(), this);
- mCallLogQueryHandler.fetchVoicemailStatus();
- mCallLogQueryHandler.fetchMissedCallsUnreadCount();
- Trace.endSection();
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- Trace.beginSection(TAG + " onCreateView");
- Trace.beginSection(TAG + " inflate view");
- final View parentView = inflater.inflate(R.layout.lists_fragment, container, false);
- Trace.endSection();
- Trace.beginSection(TAG + " setup views");
- mViewPager = (ViewPager) parentView.findViewById(R.id.lists_pager);
- mViewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());
- mViewPager.setAdapter(mViewPagerAdapter);
- mViewPager.setOffscreenPageLimit(TAB_COUNT_WITH_VOICEMAIL - 1);
- mViewPager.setOnPageChangeListener(this);
- showTab(TAB_INDEX_SPEED_DIAL);
-
- mTabTitles = new String[TAB_COUNT_WITH_VOICEMAIL];
- mTabTitles[TAB_INDEX_SPEED_DIAL] = getResources().getString(R.string.tab_speed_dial);
- mTabTitles[TAB_INDEX_HISTORY] = getResources().getString(R.string.tab_history);
- mTabTitles[TAB_INDEX_ALL_CONTACTS] = getResources().getString(R.string.tab_all_contacts);
- mTabTitles[TAB_INDEX_VOICEMAIL] = getResources().getString(R.string.tab_voicemail);
-
- mTabIcons = new int[TAB_COUNT_WITH_VOICEMAIL];
- mTabIcons[TAB_INDEX_SPEED_DIAL] = R.drawable.ic_grade_24dp;
- mTabIcons[TAB_INDEX_HISTORY] = R.drawable.ic_schedule_24dp;
- mTabIcons[TAB_INDEX_ALL_CONTACTS] = R.drawable.ic_people_24dp;
- mTabIcons[TAB_INDEX_VOICEMAIL] = R.drawable.ic_voicemail_24dp;
-
- mViewPagerTabs = (ViewPagerTabs) parentView.findViewById(R.id.lists_pager_header);
- mViewPagerTabs.configureTabIcons(mTabIcons);
- mViewPagerTabs.setViewPager(mViewPager);
- addOnPageChangeListener(mViewPagerTabs);
-
- mRemoveView = (RemoveView) parentView.findViewById(R.id.remove_view);
- mRemoveViewContent = parentView.findViewById(R.id.remove_view_content);
-
- Trace.endSection();
- Trace.endSection();
- return parentView;
- }
-
- public void addOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
- if (!mOnPageChangeListeners.contains(onPageChangeListener)) {
- mOnPageChangeListeners.add(onPageChangeListener);
- }
- }
-
- /**
- * Shows the tab with the specified index. If the voicemail tab index is specified, but the
- * voicemail status hasn't been fetched, it will try to show the tab after the voicemail status
- * has been fetched.
- */
- public void showTab(int index) {
- if (index == TAB_INDEX_VOICEMAIL) {
- if (mHasActiveVoicemailProvider) {
- mViewPager.setCurrentItem(getRtlPosition(TAB_INDEX_VOICEMAIL));
- } else if (!mHasFetchedVoicemailStatus) {
- // Try to show the voicemail tab after the voicemail status returns.
- mShowVoicemailTabAfterVoicemailStatusIsFetched = true;
- }
- } else if (index < getTabCount()){
- mViewPager.setCurrentItem(getRtlPosition(index));
- }
- }
-
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- mTabIndex = getRtlPosition(position);
-
- final int count = mOnPageChangeListeners.size();
- for (int i = 0; i < count; i++) {
- mOnPageChangeListeners.get(i).onPageScrolled(position, positionOffset,
- positionOffsetPixels);
- }
- }
-
- @Override
- public void onPageSelected(int position) {
- mTabIndex = getRtlPosition(position);
-
- // Show the tab which has been selected instead.
- mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
-
- final int count = mOnPageChangeListeners.size();
- for (int i = 0; i < count; i++) {
- mOnPageChangeListeners.get(i).onPageSelected(position);
- }
- sendScreenViewForCurrentPosition();
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- final int count = mOnPageChangeListeners.size();
- for (int i = 0; i < count; i++) {
- mOnPageChangeListeners.get(i).onPageScrollStateChanged(state);
- }
- }
-
- @Override
- public void onVoicemailStatusFetched(Cursor statusCursor) {
- mHasFetchedVoicemailStatus = true;
-
- if (getActivity() == null || getActivity().isFinishing()) {
- return;
- }
-
- // Update mHasActiveVoicemailProvider, which controls the number of tabs displayed.
- boolean hasActiveVoicemailProvider =
- mVoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor) > 0;
- if (hasActiveVoicemailProvider != mHasActiveVoicemailProvider) {
- mHasActiveVoicemailProvider = hasActiveVoicemailProvider;
- mViewPagerAdapter.notifyDataSetChanged();
-
- if (hasActiveVoicemailProvider) {
- mViewPagerTabs.updateTab(TAB_INDEX_VOICEMAIL);
- } else {
- mViewPagerTabs.removeTab(TAB_INDEX_VOICEMAIL);
- removeVoicemailFragment();
- }
-
- mPrefs.edit()
- .putBoolean(VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER,
- hasActiveVoicemailProvider)
- .commit();
- }
-
- if (hasActiveVoicemailProvider) {
- mCallLogQueryHandler.fetchVoicemailUnreadCount();
- }
-
- if (mHasActiveVoicemailProvider && mShowVoicemailTabAfterVoicemailStatusIsFetched) {
- mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
- showTab(TAB_INDEX_VOICEMAIL);
- }
- }
-
- @Override
- public void onVoicemailUnreadCountFetched(Cursor cursor) {
- if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
- return;
- }
-
- int count = 0;
- try {
- count = cursor.getCount();
- } finally {
- cursor.close();
- }
-
- mViewPagerTabs.setUnreadCount(count, TAB_INDEX_VOICEMAIL);
- mViewPagerTabs.updateTab(TAB_INDEX_VOICEMAIL);
- }
-
- @Override
- public void onMissedCallsUnreadCountFetched(Cursor cursor) {
- if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
- return;
- }
-
- int count = 0;
- try {
- count = cursor.getCount();
- } finally {
- cursor.close();
- }
-
- mViewPagerTabs.setUnreadCount(count, TAB_INDEX_HISTORY);
- mViewPagerTabs.updateTab(TAB_INDEX_HISTORY);
- }
-
- @Override
- public boolean onCallsFetched(Cursor statusCursor) {
- // Return false; did not take ownership of cursor
- return false;
- }
-
- public int getCurrentTabIndex() {
- return mTabIndex;
- }
-
- /**
- * External method to update unread count because the unread count changes when the user
- * expands a voicemail in the call log or when the user expands an unread call in the call
- * history tab.
- */
- public void updateTabUnreadCounts() {
- if (mCallLogQueryHandler != null) {
- mCallLogQueryHandler.fetchMissedCallsUnreadCount();
- if (mHasActiveVoicemailProvider) {
- mCallLogQueryHandler.fetchVoicemailUnreadCount();
- }
- }
- }
-
- /**
- * External method to mark all missed calls as read.
- */
- public void markMissedCallsAsReadAndRemoveNotifications() {
- if (mCallLogQueryHandler != null) {
- mCallLogQueryHandler.markMissedCallsAsRead();
- CallLogNotificationsHelper.removeMissedCallNotifications(getActivity());
- }
- }
-
-
- public void showRemoveView(boolean show) {
- mRemoveViewContent.setVisibility(show ? View.VISIBLE : View.GONE);
- mRemoveView.setAlpha(show ? 0 : 1);
- mRemoveView.animate().alpha(show ? 1 : 0).start();
- }
-
- public boolean shouldShowActionBar() {
- // TODO: Update this based on scroll state.
- return mActionBar != null;
- }
-
- public SpeedDialFragment getSpeedDialFragment() {
- return mSpeedDialFragment;
- }
-
- public RemoveView getRemoveView() {
- return mRemoveView;
- }
-
- public int getTabCount() {
- return mViewPagerAdapter.getCount();
- }
-
- private int getRtlPosition(int position) {
- if (DialerUtils.isRtl()) {
- return mViewPagerAdapter.getCount() - 1 - position;
- }
- return position;
- }
-
- public void sendScreenViewForCurrentPosition() {
- if (!isResumed()) {
- return;
- }
-
- int screenType;
- switch (getCurrentTabIndex()) {
- case TAB_INDEX_SPEED_DIAL:
- screenType = ScreenEvent.SPEED_DIAL;
- break;
- case TAB_INDEX_HISTORY:
- screenType = ScreenEvent.CALL_LOG;
- break;
- case TAB_INDEX_ALL_CONTACTS:
- screenType = ScreenEvent.ALL_CONTACTS;
- break;
- case TAB_INDEX_VOICEMAIL:
- screenType = ScreenEvent.VOICEMAIL_LOG;
- default:
- return;
- }
- Logger.logScreenView(screenType, getActivity());
- }
-
- private void removeVoicemailFragment() {
- if (mVoicemailFragment != null) {
- getChildFragmentManager().beginTransaction().remove(mVoicemailFragment)
- .commitAllowingStateLoss();
- mVoicemailFragment = null;
- }
- }
-}
diff --git a/src/com/android/dialer/list/OnDragDropListener.java b/src/com/android/dialer/list/OnDragDropListener.java
deleted file mode 100644
index c9ef50b09..000000000
--- a/src/com/android/dialer/list/OnDragDropListener.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.android.dialer.list;
-
-
-/**
- * Classes that want to receive callbacks in response to drag events should implement this
- * interface.
- */
-public interface OnDragDropListener {
- /**
- * Called when a drag is started.
- * @param x X-coordinate of the drag event
- * @param y Y-coordinate of the drag event
- * @param view The contact tile which the drag was started on
- */
- public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view);
-
- /**
- * Called when a drag is in progress and the user moves the dragged contact to a
- * location.
- *
- * @param x X-coordinate of the drag event
- * @param y Y-coordinate of the drag event
- * @param view Contact tile in the ListView which is currently being displaced
- * by the dragged contact
- */
- public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view);
-
- /**
- * Called when a drag is completed (whether by dropping it somewhere or simply by dragging
- * the contact off the screen)
- * @param x X-coordinate of the drag event
- * @param y Y-coordinate of the drag event
- */
- public void onDragFinished(int x, int y);
-
- /**
- * Called when a contact has been dropped on the remove view, indicating that the user
- * wants to remove this contact.
- */
- public void onDroppedOnRemove();
-} \ No newline at end of file
diff --git a/src/com/android/dialer/list/PhoneFavoriteListView.java b/src/com/android/dialer/list/PhoneFavoriteListView.java
deleted file mode 100644
index aad8ad58f..000000000
--- a/src/com/android/dialer/list/PhoneFavoriteListView.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc.
- * Licensed to 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.dialer.list;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.os.Handler;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.DragEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.widget.GridView;
-import android.widget.ImageView;
-
-import com.android.dialer.R;
-import com.android.dialer.list.DragDropController.DragItemContainer;
-
-/**
- * Viewgroup that presents the user's speed dial contacts in a grid.
- */
-public class PhoneFavoriteListView extends GridView implements OnDragDropListener,
- DragItemContainer {
-
- public static final String LOG_TAG = PhoneFavoriteListView.class.getSimpleName();
-
- private float mTouchSlop;
-
- private int mTopScrollBound;
- private int mBottomScrollBound;
- private int mLastDragY;
-
- private Handler mScrollHandler;
- private final long SCROLL_HANDLER_DELAY_MILLIS = 5;
- private final int DRAG_SCROLL_PX_UNIT = 25;
-
- private boolean mIsDragScrollerRunning = false;
- private int mTouchDownForDragStartX;
- private int mTouchDownForDragStartY;
-
- private Bitmap mDragShadowBitmap;
- private ImageView mDragShadowOverlay;
- private View mDragShadowParent;
- private int mAnimationDuration;
-
- final int[] mLocationOnScreen = new int[2];
-
- // X and Y offsets inside the item from where the user grabbed to the
- // child's left coordinate. This is used to aid in the drawing of the drag shadow.
- private int mTouchOffsetToChildLeft;
- private int mTouchOffsetToChildTop;
-
- private int mDragShadowLeft;
- private int mDragShadowTop;
-
- private DragDropController mDragDropController = new DragDropController(this);
-
- private final float DRAG_SHADOW_ALPHA = 0.7f;
-
- /**
- * {@link #mTopScrollBound} and {@link mBottomScrollBound} will be
- * offseted to the top / bottom by {@link #getHeight} * {@link #BOUND_GAP_RATIO} pixels.
- */
- private final float BOUND_GAP_RATIO = 0.2f;
-
- private final Runnable mDragScroller = new Runnable() {
- @Override
- public void run() {
- if (mLastDragY <= mTopScrollBound) {
- smoothScrollBy(-DRAG_SCROLL_PX_UNIT, (int) SCROLL_HANDLER_DELAY_MILLIS);
- } else if (mLastDragY >= mBottomScrollBound) {
- smoothScrollBy(DRAG_SCROLL_PX_UNIT, (int) SCROLL_HANDLER_DELAY_MILLIS);
- }
- mScrollHandler.postDelayed(this, SCROLL_HANDLER_DELAY_MILLIS);
- }
- };
-
- private final AnimatorListenerAdapter mDragShadowOverAnimatorListener =
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mDragShadowBitmap != null) {
- mDragShadowBitmap.recycle();
- mDragShadowBitmap = null;
- }
- mDragShadowOverlay.setVisibility(GONE);
- mDragShadowOverlay.setImageBitmap(null);
- }
- };
-
- public PhoneFavoriteListView(Context context) {
- this(context, null);
- }
-
- public PhoneFavoriteListView(Context context, AttributeSet attrs) {
- this(context, attrs, -1);
- }
-
- public PhoneFavoriteListView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- mAnimationDuration = context.getResources().getInteger(R.integer.fade_duration);
- mTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
- mDragDropController.addOnDragDropListener(this);
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- mTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
- }
-
- /**
- * TODO: This is all swipe to remove code (nothing to do with drag to remove). This should
- * be cleaned up and removed once drag to remove becomes the only way to remove contacts.
- */
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mTouchDownForDragStartX = (int) ev.getX();
- mTouchDownForDragStartY = (int) ev.getY();
- }
-
- return super.onInterceptTouchEvent(ev);
- }
-
- @Override
- public boolean onDragEvent(DragEvent event) {
- final int action = event.getAction();
- final int eX = (int) event.getX();
- final int eY = (int) event.getY();
- switch (action) {
- case DragEvent.ACTION_DRAG_STARTED: {
- if (!PhoneFavoriteTileView.DRAG_PHONE_FAVORITE_TILE.equals(event.getLocalState())) {
- // Ignore any drag events that were not propagated by long pressing
- // on a {@link PhoneFavoriteTileView}
- return false;
- }
- if (!mDragDropController.handleDragStarted(this, eX, eY)) {
- return false;
- }
- break;
- }
- case DragEvent.ACTION_DRAG_LOCATION:
- mLastDragY = eY;
- mDragDropController.handleDragHovered(this, eX, eY);
- // Kick off {@link #mScrollHandler} if it's not started yet.
- if (!mIsDragScrollerRunning &&
- // And if the distance traveled while dragging exceeds the touch slop
- (Math.abs(mLastDragY - mTouchDownForDragStartY) >= 4 * mTouchSlop)) {
- mIsDragScrollerRunning = true;
- ensureScrollHandler();
- mScrollHandler.postDelayed(mDragScroller, SCROLL_HANDLER_DELAY_MILLIS);
- }
- break;
- case DragEvent.ACTION_DRAG_ENTERED:
- final int boundGap = (int) (getHeight() * BOUND_GAP_RATIO);
- mTopScrollBound = (getTop() + boundGap);
- mBottomScrollBound = (getBottom() - boundGap);
- break;
- case DragEvent.ACTION_DRAG_EXITED:
- case DragEvent.ACTION_DRAG_ENDED:
- case DragEvent.ACTION_DROP:
- ensureScrollHandler();
- mScrollHandler.removeCallbacks(mDragScroller);
- mIsDragScrollerRunning = false;
- // Either a successful drop or it's ended with out drop.
- if (action == DragEvent.ACTION_DROP || action == DragEvent.ACTION_DRAG_ENDED) {
- mDragDropController.handleDragFinished(eX, eY, false);
- }
- break;
- default:
- break;
- }
- // This ListView will consume the drag events on behalf of its children.
- return true;
- }
-
- public void setDragShadowOverlay(ImageView overlay) {
- mDragShadowOverlay = overlay;
- mDragShadowParent = (View) mDragShadowOverlay.getParent();
- }
-
- /**
- * Find the view under the pointer.
- */
- private View getViewAtPosition(int x, int y) {
- final int count = getChildCount();
- View child;
- for (int childIdx = 0; childIdx < count; childIdx++) {
- child = getChildAt(childIdx);
- if (y >= child.getTop() && y <= child.getBottom() && x >= child.getLeft()
- && x <= child.getRight()) {
- return child;
- }
- }
- return null;
- }
-
- private void ensureScrollHandler() {
- if (mScrollHandler == null) {
- mScrollHandler = getHandler();
- }
- }
-
- public DragDropController getDragDropController() {
- return mDragDropController;
- }
-
- @Override
- public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView tileView) {
- if (mDragShadowOverlay == null) {
- return;
- }
-
- mDragShadowOverlay.clearAnimation();
- mDragShadowBitmap = createDraggedChildBitmap(tileView);
- if (mDragShadowBitmap == null) {
- return;
- }
-
- tileView.getLocationOnScreen(mLocationOnScreen);
- mDragShadowLeft = mLocationOnScreen[0];
- mDragShadowTop = mLocationOnScreen[1];
-
- // x and y are the coordinates of the on-screen touch event. Using these
- // and the on-screen location of the tileView, calculate the difference between
- // the position of the user's finger and the position of the tileView. These will
- // be used to offset the location of the drag shadow so that it appears that the
- // tileView is positioned directly under the user's finger.
- mTouchOffsetToChildLeft = x - mDragShadowLeft;
- mTouchOffsetToChildTop = y - mDragShadowTop;
-
- mDragShadowParent.getLocationOnScreen(mLocationOnScreen);
- mDragShadowLeft -= mLocationOnScreen[0];
- mDragShadowTop -= mLocationOnScreen[1];
-
- mDragShadowOverlay.setImageBitmap(mDragShadowBitmap);
- mDragShadowOverlay.setVisibility(VISIBLE);
- mDragShadowOverlay.setAlpha(DRAG_SHADOW_ALPHA);
-
- mDragShadowOverlay.setX(mDragShadowLeft);
- mDragShadowOverlay.setY(mDragShadowTop);
- }
-
- @Override
- public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView tileView) {
- // Update the drag shadow location.
- mDragShadowParent.getLocationOnScreen(mLocationOnScreen);
- mDragShadowLeft = x - mTouchOffsetToChildLeft - mLocationOnScreen[0];
- mDragShadowTop = y - mTouchOffsetToChildTop - mLocationOnScreen[1];
- // Draw the drag shadow at its last known location if the drag shadow exists.
- if (mDragShadowOverlay != null) {
- mDragShadowOverlay.setX(mDragShadowLeft);
- mDragShadowOverlay.setY(mDragShadowTop);
- }
- }
-
- @Override
- public void onDragFinished(int x, int y) {
- if (mDragShadowOverlay != null) {
- mDragShadowOverlay.clearAnimation();
- mDragShadowOverlay.animate().alpha(0.0f)
- .setDuration(mAnimationDuration)
- .setListener(mDragShadowOverAnimatorListener)
- .start();
- }
- }
-
- @Override
- public void onDroppedOnRemove() {}
-
- private Bitmap createDraggedChildBitmap(View view) {
- view.setDrawingCacheEnabled(true);
- final Bitmap cache = view.getDrawingCache();
-
- Bitmap bitmap = null;
- if (cache != null) {
- try {
- bitmap = cache.copy(Bitmap.Config.ARGB_8888, false);
- } catch (final OutOfMemoryError e) {
- Log.w(LOG_TAG, "Failed to copy bitmap from Drawing cache", e);
- bitmap = null;
- }
- }
-
- view.destroyDrawingCache();
- view.setDrawingCacheEnabled(false);
-
- return bitmap;
- }
-
- @Override
- public PhoneFavoriteSquareTileView getViewForLocation(int x, int y) {
- getLocationOnScreen(mLocationOnScreen);
- // Calculate the X and Y coordinates of the drag event relative to the view
- final int viewX = x - mLocationOnScreen[0];
- final int viewY = y - mLocationOnScreen[1];
- final View child = getViewAtPosition(viewX, viewY);
-
- if (!(child instanceof PhoneFavoriteSquareTileView)) {
- return null;
- }
-
- return (PhoneFavoriteSquareTileView) child;
- }
-}
diff --git a/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java b/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
deleted file mode 100644
index 69a230c8a..000000000
--- a/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
-
- * 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.dialer.list;
-
-import android.content.Context;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.QuickContact;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.TextView;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.list.ContactEntry;
-import com.android.dialer.R;
-
-/**
- * Displays the contact's picture overlaid with their name and number type in a tile.
- */
-public class PhoneFavoriteSquareTileView extends PhoneFavoriteTileView {
- private static final String TAG = PhoneFavoriteSquareTileView.class.getSimpleName();
-
- private final float mHeightToWidthRatio;
-
- private ImageButton mSecondaryButton;
-
- private ContactEntry mContactEntry;
-
- public PhoneFavoriteSquareTileView(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mHeightToWidthRatio = getResources().getFraction(
- R.dimen.contact_tile_height_to_width_ratio, 1, 1);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- final TextView nameView = (TextView) findViewById(R.id.contact_tile_name);
- nameView.setElegantTextHeight(false);
- final TextView phoneTypeView = (TextView) findViewById(R.id.contact_tile_phone_type);
- phoneTypeView.setElegantTextHeight(false);
- mSecondaryButton = (ImageButton) findViewById(R.id.contact_tile_secondary_button);
- }
-
- @Override
- protected int getApproximateImageSize() {
- // The picture is the full size of the tile (minus some padding, but we can be generous)
- return getWidth();
- }
-
- private void launchQuickContact() {
- if (CompatUtils.hasPrioritizedMimeType()) {
- QuickContact.showQuickContact(getContext(), PhoneFavoriteSquareTileView.this,
- getLookupUri(), null, Phone.CONTENT_ITEM_TYPE);
- } else {
- QuickContact.showQuickContact(getContext(), PhoneFavoriteSquareTileView.this,
- getLookupUri(), QuickContact.MODE_LARGE, null);
- }
- }
-
- @Override
- public void loadFromContact(ContactEntry entry) {
- super.loadFromContact(entry);
- if (entry != null) {
- mSecondaryButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- launchQuickContact();
- }
- });
- }
- mContactEntry = entry;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int width = MeasureSpec.getSize(widthMeasureSpec);
- final int height = (int) (mHeightToWidthRatio * width);
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- getChildAt(i).measure(
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
- );
- }
- setMeasuredDimension(width, height);
- }
-
- @Override
- protected String getNameForView(ContactEntry contactEntry) {
- return contactEntry.getPreferredDisplayName();
- }
-
- public ContactEntry getContactEntry() {
- return mContactEntry;
- }
-}
diff --git a/src/com/android/dialer/list/PhoneFavoriteTileView.java b/src/com/android/dialer/list/PhoneFavoriteTileView.java
deleted file mode 100644
index 56d0b5d22..000000000
--- a/src/com/android/dialer/list/PhoneFavoriteTileView.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
-
- * 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.dialer.list;
-
-import android.content.ClipData;
-import android.content.Context;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageView;
-
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.MoreContactUtils;
-import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
-import com.android.contacts.common.list.ContactEntry;
-import com.android.contacts.common.list.ContactTileView;
-import com.android.dialer.R;
-
-/**
- * A light version of the {@link com.android.contacts.common.list.ContactTileView} that is used in
- * Dialtacts for frequently called contacts. Slightly different behavior from superclass when you
- * tap it, you want to call the frequently-called number for the contact, even if that is not the
- * default number for that contact. This abstract class is the super class to both the row and tile
- * view.
- */
-public abstract class PhoneFavoriteTileView extends ContactTileView {
-
- private static final String TAG = PhoneFavoriteTileView.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- // These parameters instruct the photo manager to display the default image/letter at 70% of
- // its normal size, and vertically offset upwards 12% towards the top of the letter tile, to
- // make room for the contact name and number label at the bottom of the image.
- private static final float DEFAULT_IMAGE_LETTER_OFFSET = -0.12f;
- private static final float DEFAULT_IMAGE_LETTER_SCALE = 0.70f;
-
- /** View that contains the transparent shadow that is overlaid on top of the contact image. */
- private View mShadowOverlay;
-
- /** Users' most frequent phone number. */
- private String mPhoneNumberString;
-
- // Dummy clip data object that is attached to drag shadows so that text views
- // don't crash with an NPE if the drag shadow is released in their bounds
- private static final ClipData EMPTY_CLIP_DATA = ClipData.newPlainText("", "");
-
- // Constant to pass to the drag event so that the drag action only happens when a phone favorite
- // tile is long pressed.
- static final String DRAG_PHONE_FAVORITE_TILE = "PHONE_FAVORITE_TILE";
-
- public PhoneFavoriteTileView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mShadowOverlay = findViewById(R.id.shadow_overlay);
-
- setOnLongClickListener(new OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- final PhoneFavoriteTileView view = (PhoneFavoriteTileView) v;
- // NOTE The drag shadow is handled in the ListView.
- view.startDrag(EMPTY_CLIP_DATA, new View.DragShadowBuilder(),
- DRAG_PHONE_FAVORITE_TILE, 0);
- return true;
- }
- });
- }
-
- @Override
- public void loadFromContact(ContactEntry entry) {
- super.loadFromContact(entry);
- // Set phone number to null in case we're reusing the view.
- mPhoneNumberString = null;
- if (entry != null) {
- // Grab the phone-number to call directly. See {@link onClick()}.
- mPhoneNumberString = entry.phoneNumber;
-
- // If this is a blank entry, don't show anything.
- // TODO krelease: Just hide the view for now. For this to truly look like an empty row
- // the entire ContactTileRow needs to be hidden.
- if (entry == ContactEntry.BLANK_ENTRY) {
- setVisibility(View.INVISIBLE);
- } else {
- final ImageView starIcon = (ImageView) findViewById(R.id.contact_star_icon);
- starIcon.setVisibility(entry.isFavorite ? View.VISIBLE : View.GONE);
- setVisibility(View.VISIBLE);
- }
- }
- }
-
- @Override
- protected boolean isDarkTheme() {
- return false;
- }
-
- @Override
- protected OnClickListener createClickListener() {
- return new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mListener == null) {
- return;
- }
- if (TextUtils.isEmpty(mPhoneNumberString)) {
- // Copy "superclass" implementation
- mListener.onContactSelected(getLookupUri(), MoreContactUtils
- .getTargetRectFromView(PhoneFavoriteTileView.this));
- } else {
- // When you tap a frequently-called contact, you want to
- // call them at the number that you usually talk to them
- // at (i.e. the one displayed in the UI), regardless of
- // whether that's their default number.
- mListener.onCallNumberDirectly(mPhoneNumberString);
- }
- }
- };
- }
-
- @Override
- protected DefaultImageRequest getDefaultImageRequest(String displayName, String lookupKey) {
- return new DefaultImageRequest(displayName, lookupKey, ContactPhotoManager.TYPE_DEFAULT,
- DEFAULT_IMAGE_LETTER_SCALE, DEFAULT_IMAGE_LETTER_OFFSET, false);
- }
-
- @Override
- protected void configureViewForImage(boolean isDefaultImage) {
- // Hide the shadow overlay if the image is a default image (i.e. colored letter tile)
- if (mShadowOverlay != null) {
- mShadowOverlay.setVisibility(isDefaultImage ? View.GONE : View.VISIBLE);
- }
- }
-
- @Override
- protected boolean isContactPhotoCircular() {
- // Unlike Contacts' tiles, the Dialer's favorites tiles are square.
- return false;
- }
-}
diff --git a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
deleted file mode 100644
index 77da7e937..000000000
--- a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
+++ /dev/null
@@ -1,696 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.list;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ComparisonChain;
-import com.google.common.collect.Lists;
-
-import android.content.ContentProviderOperation;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.OperationApplicationException;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.PinnedPositions;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.LongSparseArray;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.ContactTileLoaderFactory;
-import com.android.contacts.common.list.ContactEntry;
-import com.android.contacts.common.list.ContactTileAdapter.DisplayType;
-import com.android.contacts.common.list.ContactTileView;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.dialer.R;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.PriorityQueue;
-
-/**
- * Also allows for a configurable number of columns as well as a maximum row of tiled contacts.
- */
-public class PhoneFavoritesTileAdapter extends BaseAdapter implements
- OnDragDropListener {
- private static final String TAG = PhoneFavoritesTileAdapter.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- public static final int NO_ROW_LIMIT = -1;
-
- public static final int ROW_LIMIT_DEFAULT = NO_ROW_LIMIT;
-
- private ContactTileView.Listener mListener;
- private OnDataSetChangedForAnimationListener mDataSetChangedListener;
-
- private Context mContext;
- private Resources mResources;
- private ContactsPreferences mContactsPreferences;
-
- /** Contact data stored in cache. This is used to populate the associated view. */
- protected ArrayList<ContactEntry> mContactEntries = null;
- /** Back up of the temporarily removed Contact during dragging. */
- private ContactEntry mDraggedEntry = null;
- /** Position of the temporarily removed contact in the cache. */
- private int mDraggedEntryIndex = -1;
- /** New position of the temporarily removed contact in the cache. */
- private int mDropEntryIndex = -1;
- /** New position of the temporarily entered contact in the cache. */
- private int mDragEnteredEntryIndex = -1;
-
- private boolean mAwaitingRemove = false;
- private boolean mDelayCursorUpdates = false;
-
- private ContactPhotoManager mPhotoManager;
- protected int mNumFrequents;
- protected int mNumStarred;
-
- protected int mIdIndex;
- protected int mLookupIndex;
- protected int mPhotoUriIndex;
- protected int mNamePrimaryIndex;
- protected int mNameAlternativeIndex;
- protected int mPresenceIndex;
- protected int mStatusIndex;
-
- private int mPhoneNumberIndex;
- private int mPhoneNumberTypeIndex;
- private int mPhoneNumberLabelIndex;
- private int mIsDefaultNumberIndex;
- private int mStarredIndex;
- protected int mPinnedIndex;
- protected int mContactIdIndex;
-
- /** Indicates whether a drag is in process. */
- private boolean mInDragging = false;
-
- // Pinned positions start from 1, so there are a total of 20 maximum pinned contacts
- public static final int PIN_LIMIT = 21;
-
- /**
- * The soft limit on how many contact tiles to show.
- * NOTE This soft limit would not restrict the number of starred contacts to show, rather
- * 1. If the count of starred contacts is less than this limit, show 20 tiles total.
- * 2. If the count of starred contacts is more than or equal to this limit,
- * show all starred tiles and no frequents.
- */
- private static final int TILES_SOFT_LIMIT = 20;
-
- final Comparator<ContactEntry> mContactEntryComparator = new Comparator<ContactEntry>() {
- @Override
- public int compare(ContactEntry lhs, ContactEntry rhs) {
- return ComparisonChain.start()
- .compare(lhs.pinned, rhs.pinned)
- .compare(getPreferredSortName(lhs), getPreferredSortName(rhs))
- .result();
- }
-
- private String getPreferredSortName(ContactEntry contactEntry) {
- if (mContactsPreferences.getSortOrder() == ContactsPreferences.SORT_ORDER_PRIMARY
- || TextUtils.isEmpty(contactEntry.nameAlternative)) {
- return contactEntry.namePrimary;
- }
- return contactEntry.nameAlternative;
- }
- };
-
- public interface OnDataSetChangedForAnimationListener {
- public void onDataSetChangedForAnimation(long... idsInPlace);
- public void cacheOffsetsForDatasetChange();
- };
-
- public PhoneFavoritesTileAdapter(Context context, ContactTileView.Listener listener,
- OnDataSetChangedForAnimationListener dataSetChangedListener) {
- mDataSetChangedListener = dataSetChangedListener;
- mListener = listener;
- mContext = context;
- mResources = context.getResources();
- mContactsPreferences = new ContactsPreferences(mContext);
- mNumFrequents = 0;
- mContactEntries = new ArrayList<ContactEntry>();
-
-
- bindColumnIndices();
- }
-
- public void setPhotoLoader(ContactPhotoManager photoLoader) {
- mPhotoManager = photoLoader;
- }
-
- /**
- * Indicates whether a drag is in process.
- *
- * @param inDragging Boolean variable indicating whether there is a drag in process.
- */
- public void setInDragging(boolean inDragging) {
- mDelayCursorUpdates = inDragging;
- mInDragging = inDragging;
- }
-
- /** Gets whether the drag is in process. */
- public boolean getInDragging() {
- return mInDragging;
- }
-
- /**
- * Sets the column indices for expected {@link Cursor}
- * based on {@link DisplayType}.
- */
- protected void bindColumnIndices() {
- mIdIndex = ContactTileLoaderFactory.CONTACT_ID;
- mNamePrimaryIndex = ContactTileLoaderFactory.DISPLAY_NAME;
- mNameAlternativeIndex = ContactTileLoaderFactory.DISPLAY_NAME_ALTERNATIVE;
- mStarredIndex = ContactTileLoaderFactory.STARRED;
- mPhotoUriIndex = ContactTileLoaderFactory.PHOTO_URI;
- mLookupIndex = ContactTileLoaderFactory.LOOKUP_KEY;
- mPhoneNumberIndex = ContactTileLoaderFactory.PHONE_NUMBER;
- mPhoneNumberTypeIndex = ContactTileLoaderFactory.PHONE_NUMBER_TYPE;
- mPhoneNumberLabelIndex = ContactTileLoaderFactory.PHONE_NUMBER_LABEL;
- mPinnedIndex = ContactTileLoaderFactory.PINNED;
- mContactIdIndex = ContactTileLoaderFactory.CONTACT_ID_FOR_DATA;
-
-
- mPresenceIndex = ContactTileLoaderFactory.CONTACT_PRESENCE;
- mStatusIndex = ContactTileLoaderFactory.CONTACT_STATUS;
- mIsDefaultNumberIndex = ContactTileLoaderFactory.IS_DEFAULT_NUMBER;
- }
-
- public void refreshContactsPreferences() {
- mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
- mContactsPreferences.refreshValue(ContactsPreferences.SORT_ORDER_KEY);
- }
-
- /**
- * Gets the number of frequents from the passed in cursor.
- *
- * This methods is needed so the GroupMemberTileAdapter can override this.
- *
- * @param cursor The cursor to get number of frequents from.
- */
- protected void saveNumFrequentsFromCursor(Cursor cursor) {
- mNumFrequents = cursor.getCount() - mNumStarred;
- }
-
- /**
- * Creates {@link ContactTileView}s for each item in {@link Cursor}.
- *
- * Else use {@link ContactTileLoaderFactory}
- */
- public void setContactCursor(Cursor cursor) {
- if (!mDelayCursorUpdates && cursor != null && !cursor.isClosed()) {
- mNumStarred = getNumStarredContacts(cursor);
- if (mAwaitingRemove) {
- mDataSetChangedListener.cacheOffsetsForDatasetChange();
- }
-
- saveNumFrequentsFromCursor(cursor);
- saveCursorToCache(cursor);
- // cause a refresh of any views that rely on this data
- notifyDataSetChanged();
- // about to start redraw
- mDataSetChangedListener.onDataSetChangedForAnimation();
- }
- }
-
- /**
- * Saves the cursor data to the cache, to speed up UI changes.
- *
- * @param cursor Returned cursor with data to populate the view.
- */
- private void saveCursorToCache(Cursor cursor) {
- mContactEntries.clear();
-
- cursor.moveToPosition(-1);
-
- final LongSparseArray<Object> duplicates = new LongSparseArray<Object>(cursor.getCount());
-
- // Track the length of {@link #mContactEntries} and compare to {@link #TILES_SOFT_LIMIT}.
- int counter = 0;
-
- while (cursor.moveToNext()) {
-
- final int starred = cursor.getInt(mStarredIndex);
- final long id;
-
- // We display a maximum of TILES_SOFT_LIMIT contacts, or the total number of starred
- // whichever is greater.
- if (starred < 1 && counter >= TILES_SOFT_LIMIT) {
- break;
- } else {
- id = cursor.getLong(mContactIdIndex);
- }
-
- final ContactEntry existing = (ContactEntry) duplicates.get(id);
- if (existing != null) {
- // Check if the existing number is a default number. If not, clear the phone number
- // and label fields so that the disambiguation dialog will show up.
- if (!existing.isDefaultNumber) {
- existing.phoneLabel = null;
- existing.phoneNumber = null;
- }
- continue;
- }
-
- final String photoUri = cursor.getString(mPhotoUriIndex);
- final String lookupKey = cursor.getString(mLookupIndex);
- final int pinned = cursor.getInt(mPinnedIndex);
- final String name = cursor.getString(mNamePrimaryIndex);
- final String nameAlternative = cursor.getString(mNameAlternativeIndex);
- final boolean isStarred = cursor.getInt(mStarredIndex) > 0;
- final boolean isDefaultNumber = cursor.getInt(mIsDefaultNumberIndex) > 0;
-
- final ContactEntry contact = new ContactEntry();
-
- contact.id = id;
- contact.namePrimary = (!TextUtils.isEmpty(name)) ? name :
- mResources.getString(R.string.missing_name);
- contact.nameAlternative = (!TextUtils.isEmpty(nameAlternative)) ? nameAlternative :
- mResources.getString(R.string.missing_name);
- contact.nameDisplayOrder = mContactsPreferences.getDisplayOrder();
- contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
- contact.lookupKey = lookupKey;
- contact.lookupUri = ContentUris.withAppendedId(
- Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
- contact.isFavorite = isStarred;
- contact.isDefaultNumber = isDefaultNumber;
-
- // Set phone number and label
- final int phoneNumberType = cursor.getInt(mPhoneNumberTypeIndex);
- final String phoneNumberCustomLabel = cursor.getString(mPhoneNumberLabelIndex);
- contact.phoneLabel = (String) Phone.getTypeLabel(mResources, phoneNumberType,
- phoneNumberCustomLabel);
- contact.phoneNumber = cursor.getString(mPhoneNumberIndex);
-
- contact.pinned = pinned;
- mContactEntries.add(contact);
-
- duplicates.put(id, contact);
-
- counter++;
- }
-
- mAwaitingRemove = false;
-
- arrangeContactsByPinnedPosition(mContactEntries);
-
- notifyDataSetChanged();
- }
-
- /**
- * Iterates over the {@link Cursor}
- * Returns position of the first NON Starred Contact
- * Returns -1 if {@link DisplayType#STARRED_ONLY}
- * Returns 0 if {@link DisplayType#FREQUENT_ONLY}
- */
- protected int getNumStarredContacts(Cursor cursor) {
- cursor.moveToPosition(-1);
- while (cursor.moveToNext()) {
- if (cursor.getInt(mStarredIndex) == 0) {
- return cursor.getPosition();
- }
- }
-
- // There are not NON Starred contacts in cursor
- // Set divider positon to end
- return cursor.getCount();
- }
-
- /**
- * Returns the number of frequents that will be displayed in the list.
- */
- public int getNumFrequents() {
- return mNumFrequents;
- }
-
- @Override
- public int getCount() {
- if (mContactEntries == null) {
- return 0;
- }
-
- return mContactEntries.size();
- }
-
- /**
- * Returns an ArrayList of the {@link ContactEntry}s that are to appear
- * on the row for the given position.
- */
- @Override
- public ContactEntry getItem(int position) {
- return mContactEntries.get(position);
- }
-
- /**
- * For the top row of tiled contacts, the item id is the position of the row of
- * contacts.
- * For frequent contacts, the item id is the maximum number of rows of tiled contacts +
- * the actual contact id. Since contact ids are always greater than 0, this guarantees that
- * all items within this adapter will always have unique ids.
- */
- @Override
- public long getItemId(int position) {
- return getItem(position).id;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return true;
- }
-
- @Override
- public boolean isEnabled(int position) {
- return getCount() > 0;
- }
-
- @Override
- public void notifyDataSetChanged() {
- if (DEBUG) {
- Log.v(TAG, "notifyDataSetChanged");
- }
- super.notifyDataSetChanged();
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (DEBUG) {
- Log.v(TAG, "get view for " + String.valueOf(position));
- }
-
- int itemViewType = getItemViewType(position);
-
- PhoneFavoriteTileView tileView = null;
-
- if (convertView instanceof PhoneFavoriteTileView) {
- tileView = (PhoneFavoriteTileView) convertView;
- }
-
- if (tileView == null) {
- tileView = (PhoneFavoriteTileView) View.inflate(mContext,
- R.layout.phone_favorite_tile_view, null);
- }
- tileView.setPhotoManager(mPhotoManager);
- tileView.setListener(mListener);
- tileView.loadFromContact(getItem(position));
- return tileView;
- }
-
- @Override
- public int getViewTypeCount() {
- return ViewTypes.COUNT;
- }
-
- @Override
- public int getItemViewType(int position) {
- return ViewTypes.TILE;
- }
-
- /**
- * Temporarily removes a contact from the list for UI refresh. Stores data for this contact
- * in the back-up variable.
- *
- * @param index Position of the contact to be removed.
- */
- public void popContactEntry(int index) {
- if (isIndexInBound(index)) {
- mDraggedEntry = mContactEntries.get(index);
- mDraggedEntryIndex = index;
- mDragEnteredEntryIndex = index;
- markDropArea(mDragEnteredEntryIndex);
- }
- }
-
- /**
- * @param itemIndex Position of the contact in {@link #mContactEntries}.
- * @return True if the given index is valid for {@link #mContactEntries}.
- */
- public boolean isIndexInBound(int itemIndex) {
- return itemIndex >= 0 && itemIndex < mContactEntries.size();
- }
-
- /**
- * Mark the tile as drop area by given the item index in {@link #mContactEntries}.
- *
- * @param itemIndex Position of the contact in {@link #mContactEntries}.
- */
- private void markDropArea(int itemIndex) {
- if (mDraggedEntry != null && isIndexInBound(mDragEnteredEntryIndex) &&
- isIndexInBound(itemIndex)) {
- mDataSetChangedListener.cacheOffsetsForDatasetChange();
- // Remove the old placeholder item and place the new placeholder item.
- final int oldIndex = mDragEnteredEntryIndex;
- mContactEntries.remove(mDragEnteredEntryIndex);
- mDragEnteredEntryIndex = itemIndex;
- mContactEntries.add(mDragEnteredEntryIndex, ContactEntry.BLANK_ENTRY);
- ContactEntry.BLANK_ENTRY.id = mDraggedEntry.id;
- mDataSetChangedListener.onDataSetChangedForAnimation();
- notifyDataSetChanged();
- }
- }
-
- /**
- * Drops the temporarily removed contact to the desired location in the list.
- */
- public void handleDrop() {
- boolean changed = false;
- if (mDraggedEntry != null) {
- if (isIndexInBound(mDragEnteredEntryIndex) &&
- mDragEnteredEntryIndex != mDraggedEntryIndex) {
- // Don't add the ContactEntry here (to prevent a double animation from occuring).
- // When we receive a new cursor the list of contact entries will automatically be
- // populated with the dragged ContactEntry at the correct spot.
- mDropEntryIndex = mDragEnteredEntryIndex;
- mContactEntries.set(mDropEntryIndex, mDraggedEntry);
- mDataSetChangedListener.cacheOffsetsForDatasetChange();
- changed = true;
- } else if (isIndexInBound(mDraggedEntryIndex)) {
- // If {@link #mDragEnteredEntryIndex} is invalid,
- // falls back to the original position of the contact.
- mContactEntries.remove(mDragEnteredEntryIndex);
- mContactEntries.add(mDraggedEntryIndex, mDraggedEntry);
- mDropEntryIndex = mDraggedEntryIndex;
- notifyDataSetChanged();
- }
-
- if (changed && mDropEntryIndex < PIN_LIMIT) {
- final ArrayList<ContentProviderOperation> operations =
- getReflowedPinningOperations(mContactEntries, mDraggedEntryIndex,
- mDropEntryIndex);
- if (!operations.isEmpty()) {
- // update the database here with the new pinned positions
- try {
- mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY,
- operations);
- } catch (RemoteException | OperationApplicationException e) {
- Log.e(TAG, "Exception thrown when pinning contacts", e);
- }
- }
- }
- mDraggedEntry = null;
- }
- }
-
- /**
- * Invoked when the dragged item is dropped to unsupported location. We will then move the
- * contact back to where it was dragged from.
- */
- public void dropToUnsupportedView() {
- if (isIndexInBound(mDragEnteredEntryIndex)) {
- mContactEntries.remove(mDragEnteredEntryIndex);
- mContactEntries.add(mDraggedEntryIndex, mDraggedEntry);
- notifyDataSetChanged();
- }
- }
-
- /**
- * Clears all temporary variables at a new interaction.
- */
- public void cleanTempVariables() {
- mDraggedEntryIndex = -1;
- mDropEntryIndex = -1;
- mDragEnteredEntryIndex = -1;
- mDraggedEntry = null;
- }
-
- /**
- * Used when a contact is removed from speeddial. This will both unstar and set pinned position
- * of the contact to PinnedPosition.DEMOTED so that it doesn't show up anymore in the favorites
- * list.
- */
- private void unstarAndUnpinContact(Uri contactUri) {
- final ContentValues values = new ContentValues(2);
- values.put(Contacts.STARRED, false);
- values.put(Contacts.PINNED, PinnedPositions.DEMOTED);
- mContext.getContentResolver().update(contactUri, values, null, null);
- }
-
- /**
- * Given a list of contacts that each have pinned positions, rearrange the list (destructive)
- * such that all pinned contacts are in their defined pinned positions, and unpinned contacts
- * take the spaces between those pinned contacts. Demoted contacts should not appear in the
- * resulting list.
- *
- * This method also updates the pinned positions of pinned contacts so that they are all
- * unique positive integers within range from 0 to toArrange.size() - 1. This is because
- * when the contact entries are read from the database, it is possible for them to have
- * overlapping pin positions due to sync or modifications by third party apps.
- */
- @VisibleForTesting
- /* package */ void arrangeContactsByPinnedPosition(ArrayList<ContactEntry> toArrange) {
- final PriorityQueue<ContactEntry> pinnedQueue =
- new PriorityQueue<ContactEntry>(PIN_LIMIT, mContactEntryComparator);
-
- final List<ContactEntry> unpinnedContacts = new LinkedList<ContactEntry>();
-
- final int length = toArrange.size();
- for (int i = 0; i < length; i++) {
- final ContactEntry contact = toArrange.get(i);
- // Decide whether the contact is hidden(demoted), pinned, or unpinned
- if (contact.pinned > PIN_LIMIT || contact.pinned == PinnedPositions.UNPINNED) {
- unpinnedContacts.add(contact);
- } else if (contact.pinned > PinnedPositions.DEMOTED) {
- // Demoted or contacts with negative pinned positions are ignored.
- // Pinned contacts go into a priority queue where they are ranked by pinned
- // position. This is required because the contacts provider does not return
- // contacts ordered by pinned position.
- pinnedQueue.add(contact);
- }
- }
-
- final int maxToPin = Math.min(PIN_LIMIT, pinnedQueue.size() + unpinnedContacts.size());
-
- toArrange.clear();
- for (int i = 1; i < maxToPin + 1; i++) {
- if (!pinnedQueue.isEmpty() && pinnedQueue.peek().pinned <= i) {
- final ContactEntry toPin = pinnedQueue.poll();
- toPin.pinned = i;
- toArrange.add(toPin);
- } else if (!unpinnedContacts.isEmpty()) {
- toArrange.add(unpinnedContacts.remove(0));
- }
- }
-
- // If there are still contacts in pinnedContacts at this point, it means that the pinned
- // positions of these pinned contacts exceed the actual number of contacts in the list.
- // For example, the user had 10 frequents, starred and pinned one of them at the last spot,
- // and then cleared frequents. Contacts in this situation should become unpinned.
- while (!pinnedQueue.isEmpty()) {
- final ContactEntry entry = pinnedQueue.poll();
- entry.pinned = PinnedPositions.UNPINNED;
- toArrange.add(entry);
- }
-
- // Any remaining unpinned contacts that weren't in the gaps between the pinned contacts
- // now just get appended to the end of the list.
- toArrange.addAll(unpinnedContacts);
- }
-
- /**
- * Given an existing list of contact entries and a single entry that is to be pinned at a
- * particular position, return a list of {@link ContentProviderOperation}s that contains new
- * pinned positions for all contacts that are forced to be pinned at new positions, trying as
- * much as possible to keep pinned contacts at their original location.
- *
- * At this point in time the pinned position of each contact in the list has already been
- * updated by {@link #arrangeContactsByPinnedPosition}, so we can assume that all pinned
- * positions(within {@link #PIN_LIMIT} are unique positive integers.
- */
- @VisibleForTesting
- /* package */ ArrayList<ContentProviderOperation> getReflowedPinningOperations(
- ArrayList<ContactEntry> list, int oldPos, int newPinPos) {
- final ArrayList<ContentProviderOperation> positions = Lists.newArrayList();
- final int lowerBound = Math.min(oldPos, newPinPos);
- final int upperBound = Math.max(oldPos, newPinPos);
- for (int i = lowerBound; i <= upperBound; i++) {
- final ContactEntry entry = list.get(i);
-
- // Pinned positions in the database start from 1 instead of being zero-indexed like
- // arrays, so offset by 1.
- final int databasePinnedPosition = i + 1;
- if (entry.pinned == databasePinnedPosition) continue;
-
- final Uri uri = Uri.withAppendedPath(Contacts.CONTENT_URI, String.valueOf(entry.id));
- final ContentValues values = new ContentValues();
- values.put(Contacts.PINNED, databasePinnedPosition);
- positions.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
- }
- return positions;
- }
-
- protected static class ViewTypes {
- public static final int TILE = 0;
- public static final int COUNT = 1;
- }
-
- @Override
- public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {
- setInDragging(true);
- final int itemIndex = mContactEntries.indexOf(view.getContactEntry());
- popContactEntry(itemIndex);
- }
-
- @Override
- public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {
- if (view == null) {
- // The user is hovering over a view that is not a contact tile, no need to do
- // anything here.
- return;
- }
- final int itemIndex = mContactEntries.indexOf(view.getContactEntry());
- if (mInDragging &&
- mDragEnteredEntryIndex != itemIndex &&
- isIndexInBound(itemIndex) &&
- itemIndex < PIN_LIMIT &&
- itemIndex >= 0) {
- markDropArea(itemIndex);
- }
- }
-
- @Override
- public void onDragFinished(int x, int y) {
- setInDragging(false);
- // A contact has been dragged to the RemoveView in order to be unstarred, so simply wait
- // for the new contact cursor which will cause the UI to be refreshed without the unstarred
- // contact.
- if (!mAwaitingRemove) {
- handleDrop();
- }
- }
-
- @Override
- public void onDroppedOnRemove() {
- if (mDraggedEntry != null) {
- unstarAndUnpinContact(mDraggedEntry.lookupUri);
- mAwaitingRemove = true;
- }
- }
-}
diff --git a/src/com/android/dialer/list/RegularSearchFragment.java b/src/com/android/dialer/list/RegularSearchFragment.java
deleted file mode 100644
index df18af044..000000000
--- a/src/com/android/dialer/list/RegularSearchFragment.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.list;
-
-import static android.Manifest.permission.READ_CONTACTS;
-
-import android.app.Activity;
-import android.content.pm.PackageManager;
-import android.support.v13.app.FragmentCompat;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import com.android.contacts.common.list.ContactEntryListAdapter;
-import com.android.contacts.common.list.PinnedHeaderListView;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.commonbind.analytics.AnalyticsUtil;
-import com.android.dialerbind.ObjectFactory;
-import com.android.incallui.Call.LogState;
-
-import com.android.dialer.R;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.ScreenEvent;
-import com.android.dialer.service.CachedNumberLookupService;
-import com.android.dialer.widget.EmptyContentView;
-import com.android.dialer.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener;
-
-public class RegularSearchFragment extends SearchFragment
- implements OnEmptyViewActionButtonClickedListener,
- FragmentCompat.OnRequestPermissionsResultCallback {
-
- public static final int PERMISSION_REQUEST_CODE = 1;
-
- private static final int SEARCH_DIRECTORY_RESULT_LIMIT = 5;
-
- private static final CachedNumberLookupService mCachedNumberLookupService =
- ObjectFactory.newCachedNumberLookupService();
-
- public interface CapabilityChecker {
- public boolean isNearbyPlacesSearchEnabled();
- }
-
- protected String mPermissionToRequest;
-
- public RegularSearchFragment() {
- configureDirectorySearch();
- }
-
- public void configureDirectorySearch() {
- setDirectorySearchEnabled(true);
- setDirectoryResultLimit(SEARCH_DIRECTORY_RESULT_LIMIT);
- }
-
- @Override
- protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
- super.onCreateView(inflater, container);
- ((PinnedHeaderListView) getListView()).setScrollToSectionOnHeaderTouch(true);
- }
-
- @Override
- protected ContactEntryListAdapter createListAdapter() {
- RegularSearchListAdapter adapter = new RegularSearchListAdapter(getActivity());
- adapter.setDisplayPhotos(true);
- adapter.setUseCallableUri(usesCallableUri());
- adapter.setListener(this);
- return adapter;
- }
-
- @Override
- protected void cacheContactInfo(int position) {
- if (mCachedNumberLookupService != null) {
- final RegularSearchListAdapter adapter =
- (RegularSearchListAdapter) getAdapter();
- mCachedNumberLookupService.addContact(getContext(),
- adapter.getContactInfo(mCachedNumberLookupService, position));
- }
- }
-
- @Override
- protected void setupEmptyView() {
- if (mEmptyView != null && getActivity() != null) {
- final int imageResource;
- final int actionLabelResource;
- final int descriptionResource;
- final OnEmptyViewActionButtonClickedListener listener;
- if (!PermissionsUtil.hasPermission(getActivity(), READ_CONTACTS)) {
- imageResource = R.drawable.empty_contacts;
- actionLabelResource = R.string.permission_single_turn_on;
- descriptionResource = R.string.permission_no_search;
- listener = this;
- mPermissionToRequest = READ_CONTACTS;
- } else {
- imageResource = EmptyContentView.NO_IMAGE;
- actionLabelResource = EmptyContentView.NO_LABEL;
- descriptionResource = EmptyContentView.NO_LABEL;
- listener = null;
- mPermissionToRequest = null;
- }
-
- mEmptyView.setImage(imageResource);
- mEmptyView.setActionLabel(actionLabelResource);
- mEmptyView.setDescription(descriptionResource);
- if (listener != null) {
- mEmptyView.setActionClickedListener(listener);
- }
- }
- }
-
- @Override
- public void onEmptyViewActionButtonClicked() {
- final Activity activity = getActivity();
- if (activity == null) {
- return;
- }
-
- if (READ_CONTACTS.equals(mPermissionToRequest)) {
- FragmentCompat.requestPermissions(this, new String[] {mPermissionToRequest},
- PERMISSION_REQUEST_CODE);
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions,
- int[] grantResults) {
- if (requestCode == PERMISSION_REQUEST_CODE) {
- setupEmptyView();
- if (grantResults != null && grantResults.length == 1
- && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
- PermissionsUtil.notifyPermissionGranted(getActivity(), mPermissionToRequest);
- }
- }
- }
-
- @Override
- protected int getCallInitiationType(boolean isRemoteDirectory) {
- return isRemoteDirectory ? LogState.INITIATION_REMOTE_DIRECTORY
- : LogState.INITIATION_REGULAR_SEARCH;
- }
-}
diff --git a/src/com/android/dialer/list/RegularSearchListAdapter.java b/src/com/android/dialer/list/RegularSearchListAdapter.java
deleted file mode 100644
index afc621cf5..000000000
--- a/src/com/android/dialer/list/RegularSearchListAdapter.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.list;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.text.TextUtils;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.compat.DirectoryCompat;
-import com.android.contacts.common.list.DirectoryPartition;
-import com.android.contacts.common.util.PhoneNumberHelper;
-import com.android.dialer.calllog.ContactInfo;
-import com.android.dialer.service.CachedNumberLookupService;
-import com.android.dialer.service.CachedNumberLookupService.CachedContactInfo;
-
-/**
- * List adapter to display regular search results.
- */
-public class RegularSearchListAdapter extends DialerPhoneNumberListAdapter {
- protected boolean mIsQuerySipAddress;
-
- public RegularSearchListAdapter(Context context) {
- super(context);
- setShortcutEnabled(SHORTCUT_CREATE_NEW_CONTACT, false);
- setShortcutEnabled(SHORTCUT_ADD_TO_EXISTING_CONTACT, false);
- }
-
- public CachedContactInfo getContactInfo(
- CachedNumberLookupService lookupService, int position) {
- ContactInfo info = new ContactInfo();
- CachedContactInfo cacheInfo = lookupService.buildCachedContactInfo(info);
- final Cursor item = (Cursor) getItem(position);
- if (item != null) {
- final DirectoryPartition partition =
- (DirectoryPartition) getPartition(getPartitionForPosition(position));
- final long directoryId = partition.getDirectoryId();
- final boolean isExtendedDirectory = isExtendedDirectory(directoryId);
-
- info.name = item.getString(PhoneQuery.DISPLAY_NAME);
- info.type = item.getInt(PhoneQuery.PHONE_TYPE);
- info.label = item.getString(PhoneQuery.PHONE_LABEL);
- info.number = item.getString(PhoneQuery.PHONE_NUMBER);
- final String photoUriStr = item.getString(PhoneQuery.PHOTO_URI);
- info.photoUri = photoUriStr == null ? null : Uri.parse(photoUriStr);
- /*
- * An extended directory is custom directory in the app, but not a directory provided by
- * framework. So it can't be USER_TYPE_WORK.
- *
- * When a search result is selected, RegularSearchFragment calls getContactInfo and
- * cache the resulting @{link ContactInfo} into local db. Set usertype to USER_TYPE_WORK
- * only if it's NOT extended directory id and is enterprise directory.
- */
- info.userType = !isExtendedDirectory
- && DirectoryCompat.isEnterpriseDirectoryId(directoryId)
- ? ContactsUtils.USER_TYPE_WORK : ContactsUtils.USER_TYPE_CURRENT;
-
- cacheInfo.setLookupKey(item.getString(PhoneQuery.LOOKUP_KEY));
-
- final String sourceName = partition.getLabel();
- if (isExtendedDirectory) {
- cacheInfo.setExtendedSource(sourceName, directoryId);
- } else {
- cacheInfo.setDirectorySource(sourceName, directoryId);
- }
- }
- return cacheInfo;
- }
-
- @Override
- public String getFormattedQueryString() {
- if (mIsQuerySipAddress) {
- // Return unnormalized SIP address
- return getQueryString();
- }
- return super.getFormattedQueryString();
- }
-
- @Override
- public void setQueryString(String queryString) {
- // Don't show actions if the query string contains a letter.
- final boolean showNumberShortcuts = !TextUtils.isEmpty(getFormattedQueryString())
- && hasDigitsInQueryString();
- mIsQuerySipAddress = PhoneNumberHelper.isUriNumber(queryString);
-
- if (isChanged(showNumberShortcuts)) {
- notifyDataSetChanged();
- }
- super.setQueryString(queryString);
- }
-
- protected boolean isChanged(boolean showNumberShortcuts) {
- boolean changed = false;
- changed |= setShortcutEnabled(SHORTCUT_DIRECT_CALL,
- showNumberShortcuts || mIsQuerySipAddress);
- changed |= setShortcutEnabled(SHORTCUT_SEND_SMS_MESSAGE, showNumberShortcuts);
- changed |= setShortcutEnabled(SHORTCUT_MAKE_VIDEO_CALL,
- showNumberShortcuts && CallUtil.isVideoEnabled(getContext()));
- return changed;
- }
-
- /**
- * Whether there is at least one digit in the query string.
- */
- private boolean hasDigitsInQueryString() {
- String queryString = getQueryString();
- int length = queryString.length();
- for (int i = 0; i < length; i++) {
- if (Character.isDigit(queryString.charAt(i))) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/src/com/android/dialer/list/RemoveView.java b/src/com/android/dialer/list/RemoveView.java
deleted file mode 100644
index 41f41752e..000000000
--- a/src/com/android/dialer/list/RemoveView.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package com.android.dialer.list;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.DragEvent;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.dialer.R;
-
-public class RemoveView extends FrameLayout {
-
- DragDropController mDragDropController;
- TextView mRemoveText;
- ImageView mRemoveIcon;
- int mUnhighlightedColor;
- int mHighlightedColor;
- Drawable mRemoveDrawable;
-
- public RemoveView(Context context) {
- super(context);
- }
-
- public RemoveView(Context context, AttributeSet attrs) {
- this(context, attrs, -1);
- }
-
- public RemoveView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onFinishInflate() {
- mRemoveText = (TextView) findViewById(R.id.remove_view_text);
- mRemoveIcon = (ImageView) findViewById(R.id.remove_view_icon);
- final Resources r = getResources();
- mUnhighlightedColor = r.getColor(R.color.remove_text_color);
- mHighlightedColor = r.getColor(R.color.remove_highlighted_text_color);
- mRemoveDrawable = r.getDrawable(R.drawable.ic_remove);
- }
-
- public void setDragDropController(DragDropController controller) {
- mDragDropController = controller;
- }
-
- @Override
- public boolean onDragEvent(DragEvent event) {
- final int action = event.getAction();
- switch (action) {
- case DragEvent.ACTION_DRAG_ENTERED:
- // TODO: This is temporary solution and should be removed once accessibility for
- // drag and drop is supported by framework(b/26871588).
- sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT);
- setAppearanceHighlighted();
- break;
- case DragEvent.ACTION_DRAG_EXITED:
- setAppearanceNormal();
- break;
- case DragEvent.ACTION_DRAG_LOCATION:
- if (mDragDropController != null) {
- mDragDropController.handleDragHovered(this, (int) event.getX(),
- (int) event.getY());
- }
- break;
- case DragEvent.ACTION_DROP:
- sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT);
- if (mDragDropController != null) {
- mDragDropController.handleDragFinished((int) event.getX(), (int) event.getY(),
- true);
- }
- setAppearanceNormal();
- break;
- }
- return true;
- }
-
- private void setAppearanceNormal() {
- mRemoveText.setTextColor(mUnhighlightedColor);
- mRemoveIcon.setColorFilter(mUnhighlightedColor);
- invalidate();
- }
-
- private void setAppearanceHighlighted() {
- mRemoveText.setTextColor(mHighlightedColor);
- mRemoveIcon.setColorFilter(mHighlightedColor);
- invalidate();
- }
-}
diff --git a/src/com/android/dialer/list/SearchFragment.java b/src/com/android/dialer/list/SearchFragment.java
deleted file mode 100644
index 82395b6f8..000000000
--- a/src/com/android/dialer/list/SearchFragment.java
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.list;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorListenerAdapter;
-import android.app.Activity;
-import android.app.DialogFragment;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.Interpolator;
-import android.widget.AbsListView;
-import android.widget.AbsListView.OnScrollListener;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.Space;
-
-import com.android.contacts.common.list.ContactEntryListAdapter;
-import com.android.contacts.common.list.ContactListItemView;
-import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
-import com.android.contacts.common.list.PhoneNumberPickerFragment;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.common.util.ViewUtil;
-import com.android.dialer.R;
-import com.android.dialer.dialpad.DialpadFragment.ErrorDialogFragment;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.IntentUtil;
-import com.android.dialer.widget.EmptyContentView;
-import com.android.phone.common.animation.AnimUtils;
-
-public class SearchFragment extends PhoneNumberPickerFragment {
- private static final String TAG = SearchFragment.class.getSimpleName();
-
- private OnListFragmentScrolledListener mActivityScrollListener;
- private View.OnTouchListener mActivityOnTouchListener;
-
- /*
- * Stores the untouched user-entered string that is used to populate the add to contacts
- * intent.
- */
- private String mAddToContactNumber;
- private int mActionBarHeight;
- private int mShadowHeight;
- private int mPaddingTop;
- private int mShowDialpadDuration;
- private int mHideDialpadDuration;
-
- /**
- * Used to resize the list view containing search results so that it fits the available space
- * above the dialpad. Does not have a user-visible effect in regular touch usage (since the
- * dialpad hides that portion of the ListView anyway), but improves usability in accessibility
- * mode.
- */
- private Space mSpacer;
-
- private HostInterface mActivity;
-
- protected EmptyContentView mEmptyView;
-
- public interface HostInterface {
- public boolean isActionBarShowing();
- public boolean isDialpadShown();
- public int getDialpadHeight();
- public int getActionBarHideOffset();
- public int getActionBarHeight();
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
-
- setQuickContactEnabled(true);
- setAdjustSelectionBoundsEnabled(false);
- setDarkTheme(false);
- setPhotoPosition(ContactListItemView.getDefaultPhotoPosition(false /* opposite */));
- setUseCallableUri(true);
-
- try {
- mActivityScrollListener = (OnListFragmentScrolledListener) activity;
- } catch (ClassCastException e) {
- Log.d(TAG, activity.toString() + " doesn't implement OnListFragmentScrolledListener. " +
- "Ignoring.");
- }
- }
-
- @Override
- public void onStart() {
- super.onStart();
- if (isSearchMode()) {
- getAdapter().setHasHeader(0, false);
- }
-
- mActivity = (HostInterface) getActivity();
-
- final Resources res = getResources();
- mActionBarHeight = mActivity.getActionBarHeight();
- mShadowHeight = res.getDrawable(R.drawable.search_shadow).getIntrinsicHeight();
- mPaddingTop = res.getDimensionPixelSize(R.dimen.search_list_padding_top);
- mShowDialpadDuration = res.getInteger(R.integer.dialpad_slide_in_duration);
- mHideDialpadDuration = res.getInteger(R.integer.dialpad_slide_out_duration);
-
- final View parentView = getView();
-
- final ListView listView = getListView();
-
- if (mEmptyView == null) {
- mEmptyView = new EmptyContentView(getActivity());
- ((ViewGroup) getListView().getParent()).addView(mEmptyView);
- getListView().setEmptyView(mEmptyView);
- setupEmptyView();
- }
-
- listView.setBackgroundColor(res.getColor(R.color.background_dialer_results));
- listView.setClipToPadding(false);
- setVisibleScrollbarEnabled(false);
-
- //Turn of accessibility live region as the list constantly update itself and spam messages.
- listView.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_NONE);
- ContentChangedFilter.addToParent(listView);
-
- listView.setOnScrollListener(new OnScrollListener() {
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- if (mActivityScrollListener != null) {
- mActivityScrollListener.onListFragmentScrollStateChange(scrollState);
- }
- }
-
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- }
- });
- if (mActivityOnTouchListener != null) {
- listView.setOnTouchListener(mActivityOnTouchListener);
- }
-
- updatePosition(false /* animate */);
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- ViewUtil.addBottomPaddingToListViewForFab(getListView(), getResources());
- }
-
- @Override
- public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
- Animator animator = null;
- if (nextAnim != 0) {
- animator = AnimatorInflater.loadAnimator(getActivity(), nextAnim);
- }
- if (animator != null) {
- final View view = getView();
- final int oldLayerType = view.getLayerType();
- view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- view.setLayerType(oldLayerType, null);
- }
- });
- }
- return animator;
- }
-
- @Override
- protected void setSearchMode(boolean flag) {
- super.setSearchMode(flag);
- // This hides the "All contacts with phone numbers" header in the search fragment
- final ContactEntryListAdapter adapter = getAdapter();
- if (adapter != null) {
- adapter.setHasHeader(0, false);
- }
- }
-
- public void setAddToContactNumber(String addToContactNumber) {
- mAddToContactNumber = addToContactNumber;
- }
-
- /**
- * Return true if phone number is prohibited by a value -
- * (R.string.config_prohibited_phone_number_regexp) in the config files. False otherwise.
- */
- public boolean checkForProhibitedPhoneNumber(String number) {
- // Regular expression prohibiting manual phone call. Can be empty i.e. "no rule".
- String prohibitedPhoneNumberRegexp = getResources().getString(
- R.string.config_prohibited_phone_number_regexp);
-
- // "persist.radio.otaspdial" is a temporary hack needed for one carrier's automated
- // test equipment.
- if (number != null
- && !TextUtils.isEmpty(prohibitedPhoneNumberRegexp)
- && number.matches(prohibitedPhoneNumberRegexp)) {
- Log.d(TAG, "The phone number is prohibited explicitly by a rule.");
- if (getActivity() != null) {
- DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
- R.string.dialog_phone_call_prohibited_message);
- dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
- }
-
- return true;
- }
- return false;
- }
-
- @Override
- protected ContactEntryListAdapter createListAdapter() {
- DialerPhoneNumberListAdapter adapter = new DialerPhoneNumberListAdapter(getActivity());
- adapter.setDisplayPhotos(true);
- adapter.setUseCallableUri(super.usesCallableUri());
- adapter.setListener(this);
- return adapter;
- }
-
- @Override
- protected void onItemClick(int position, long id) {
- final DialerPhoneNumberListAdapter adapter = (DialerPhoneNumberListAdapter) getAdapter();
- final int shortcutType = adapter.getShortcutTypeFromPosition(position);
- final OnPhoneNumberPickerActionListener listener;
- final Intent intent;
- final String number;
-
- Log.i(TAG, "onItemClick: shortcutType=" + shortcutType);
-
- switch (shortcutType) {
- case DialerPhoneNumberListAdapter.SHORTCUT_INVALID:
- super.onItemClick(position, id);
- break;
- case DialerPhoneNumberListAdapter.SHORTCUT_DIRECT_CALL:
- number = adapter.getQueryString();
- listener = getOnPhoneNumberPickerListener();
- if (listener != null && !checkForProhibitedPhoneNumber(number)) {
- listener.onPickPhoneNumber(number, false /* isVideoCall */,
- getCallInitiationType(false /* isRemoteDirectory */));
- }
- break;
- case DialerPhoneNumberListAdapter.SHORTCUT_CREATE_NEW_CONTACT:
- number = TextUtils.isEmpty(mAddToContactNumber) ?
- adapter.getFormattedQueryString() : mAddToContactNumber;
- intent = IntentUtil.getNewContactIntent(number);
- DialerUtils.startActivityWithErrorToast(getActivity(), intent);
- break;
- case DialerPhoneNumberListAdapter.SHORTCUT_ADD_TO_EXISTING_CONTACT:
- number = TextUtils.isEmpty(mAddToContactNumber) ?
- adapter.getFormattedQueryString() : mAddToContactNumber;
- intent = IntentUtil.getAddToExistingContactIntent(number);
- DialerUtils.startActivityWithErrorToast(getActivity(), intent,
- R.string.add_contact_not_available);
- break;
- case DialerPhoneNumberListAdapter.SHORTCUT_SEND_SMS_MESSAGE:
- number = adapter.getFormattedQueryString();
- intent = IntentUtil.getSendSmsIntent(number);
- DialerUtils.startActivityWithErrorToast(getActivity(), intent);
- break;
- case DialerPhoneNumberListAdapter.SHORTCUT_MAKE_VIDEO_CALL:
- number = TextUtils.isEmpty(mAddToContactNumber) ?
- adapter.getQueryString() : mAddToContactNumber;
- listener = getOnPhoneNumberPickerListener();
- if (listener != null && !checkForProhibitedPhoneNumber(number)) {
- listener.onPickPhoneNumber(number, true /* isVideoCall */,
- getCallInitiationType(false /* isRemoteDirectory */));
- }
- break;
- }
- }
-
- /**
- * Updates the position and padding of the search fragment, depending on whether the dialpad is
- * shown. This can be optionally animated.
- * @param animate
- */
- public void updatePosition(boolean animate) {
- // Use negative shadow height instead of 0 to account for the 9-patch's shadow.
- int startTranslationValue =
- mActivity.isDialpadShown() ? mActionBarHeight - mShadowHeight : -mShadowHeight;
- int endTranslationValue = 0;
- // Prevents ListView from being translated down after a rotation when the ActionBar is up.
- if (animate || mActivity.isActionBarShowing()) {
- endTranslationValue =
- mActivity.isDialpadShown() ? 0 : mActionBarHeight - mShadowHeight;
- }
- if (animate) {
- // If the dialpad will be shown, then this animation involves sliding the list up.
- final boolean slideUp = mActivity.isDialpadShown();
-
- Interpolator interpolator = slideUp ? AnimUtils.EASE_IN : AnimUtils.EASE_OUT ;
- int duration = slideUp ? mShowDialpadDuration : mHideDialpadDuration;
- getView().setTranslationY(startTranslationValue);
- getView().animate()
- .translationY(endTranslationValue)
- .setInterpolator(interpolator)
- .setDuration(duration)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- if (!slideUp) {
- resizeListView();
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (slideUp) {
- resizeListView();
- }
- }
- });
-
- } else {
- getView().setTranslationY(endTranslationValue);
- resizeListView();
- }
-
- // There is padding which should only be applied when the dialpad is not shown.
- int paddingTop = mActivity.isDialpadShown() ? 0 : mPaddingTop;
- final ListView listView = getListView();
- listView.setPaddingRelative(
- listView.getPaddingStart(),
- paddingTop,
- listView.getPaddingEnd(),
- listView.getPaddingBottom());
- }
-
- public void resizeListView() {
- if (mSpacer == null) {
- return;
- }
- int spacerHeight = mActivity.isDialpadShown() ? mActivity.getDialpadHeight() : 0;
- if (spacerHeight != mSpacer.getHeight()) {
- final LinearLayout.LayoutParams lp =
- (LinearLayout.LayoutParams) mSpacer.getLayoutParams();
- lp.height = spacerHeight;
- mSpacer.setLayoutParams(lp);
- }
- }
-
- @Override
- protected void startLoading() {
- if (getActivity() == null) {
- return;
- }
-
- if (PermissionsUtil.hasContactsPermissions(getActivity())) {
- super.startLoading();
- } else if (TextUtils.isEmpty(getQueryString())) {
- // Clear out any existing call shortcuts.
- final DialerPhoneNumberListAdapter adapter =
- (DialerPhoneNumberListAdapter) getAdapter();
- adapter.disableAllShortcuts();
- } else {
- // The contact list is not going to change (we have no results since permissions are
- // denied), but the shortcuts might because of the different query, so update the
- // list.
- getAdapter().notifyDataSetChanged();
- }
-
- setupEmptyView();
- }
-
- public void setOnTouchListener(View.OnTouchListener onTouchListener) {
- mActivityOnTouchListener = onTouchListener;
- }
-
- @Override
- protected View inflateView(LayoutInflater inflater, ViewGroup container) {
- final LinearLayout parent = (LinearLayout) super.inflateView(inflater, container);
- final int orientation = getResources().getConfiguration().orientation;
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- mSpacer = new Space(getActivity());
- parent.addView(mSpacer,
- new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0));
- }
- return parent;
- }
-
- protected void setupEmptyView() {}
-}
diff --git a/src/com/android/dialer/list/SmartDialNumberListAdapter.java b/src/com/android/dialer/list/SmartDialNumberListAdapter.java
deleted file mode 100644
index fe27a25ab..000000000
--- a/src/com/android/dialer/list/SmartDialNumberListAdapter.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.list;
-
-import android.content.ContentUris;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.list.ContactListItemView;
-import com.android.dialer.dialpad.SmartDialCursorLoader;
-import com.android.dialer.dialpad.SmartDialNameMatcher;
-import com.android.dialer.dialpad.SmartDialPrefix;
-import com.android.dialer.dialpad.SmartDialMatchPosition;
-
-import java.util.ArrayList;
-
-/**
- * List adapter to display the SmartDial search results.
- */
-public class SmartDialNumberListAdapter extends DialerPhoneNumberListAdapter {
-
- private static final String TAG = SmartDialNumberListAdapter.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- private SmartDialNameMatcher mNameMatcher;
-
- public SmartDialNumberListAdapter(Context context) {
- super(context);
- mNameMatcher = new SmartDialNameMatcher("", SmartDialPrefix.getMap());
- setShortcutEnabled(SmartDialNumberListAdapter.SHORTCUT_DIRECT_CALL, false);
-
- if (DEBUG) {
- Log.v(TAG, "Constructing List Adapter");
- }
- }
-
- /**
- * Sets query for the SmartDialCursorLoader.
- */
- public void configureLoader(SmartDialCursorLoader loader) {
- if (DEBUG) {
- Log.v(TAG, "Configure Loader with query" + getQueryString());
- }
-
- if (getQueryString() == null) {
- loader.configureQuery("");
- mNameMatcher.setQuery("");
- } else {
- loader.configureQuery(getQueryString());
- mNameMatcher.setQuery(PhoneNumberUtils.normalizeNumber(getQueryString()));
- }
- }
-
- /**
- * Sets highlight options for a List item in the SmartDial search results.
- * @param view ContactListItemView where the result will be displayed.
- * @param cursor Object containing information of the associated List item.
- */
- @Override
- protected void setHighlight(ContactListItemView view, Cursor cursor) {
- view.clearHighlightSequences();
-
- if (mNameMatcher.matches(cursor.getString(PhoneQuery.DISPLAY_NAME))) {
- final ArrayList<SmartDialMatchPosition> nameMatches = mNameMatcher.getMatchPositions();
- for (SmartDialMatchPosition match:nameMatches) {
- view.addNameHighlightSequence(match.start, match.end);
- if (DEBUG) {
- Log.v(TAG, cursor.getString(PhoneQuery.DISPLAY_NAME) + " " +
- mNameMatcher.getQuery() + " " + String.valueOf(match.start));
- }
- }
- }
-
- final SmartDialMatchPosition numberMatch = mNameMatcher.matchesNumber(cursor.getString(
- PhoneQuery.PHONE_NUMBER));
- if (numberMatch != null) {
- view.addNumberHighlightSequence(numberMatch.start, numberMatch.end);
- }
- }
-
- /**
- * Gets Uri for the list item at the given position.
- * @param position Location of the data of interest.
- * @return Data Uri of the entry.
- */
- public Uri getDataUri(int position) {
- Cursor cursor = ((Cursor)getItem(position));
- if (cursor != null) {
- long id = cursor.getLong(PhoneQuery.PHONE_ID);
- return ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, id);
- } else {
- Log.w(TAG, "Cursor was null in getDataUri() call. Returning null instead.");
- return null;
- }
- }
-
- @Override
- public void setQueryString(String queryString) {
- final boolean showNumberShortcuts = !TextUtils.isEmpty(getFormattedQueryString());
- boolean changed = false;
- changed |= setShortcutEnabled(SHORTCUT_CREATE_NEW_CONTACT, showNumberShortcuts);
- changed |= setShortcutEnabled(SHORTCUT_ADD_TO_EXISTING_CONTACT, showNumberShortcuts);
- changed |= setShortcutEnabled(SHORTCUT_SEND_SMS_MESSAGE, showNumberShortcuts);
- changed |= setShortcutEnabled(SHORTCUT_MAKE_VIDEO_CALL,
- showNumberShortcuts && CallUtil.isVideoEnabled(getContext()));
- if (changed) {
- notifyDataSetChanged();
- }
- super.setQueryString(queryString);
- }
-}
diff --git a/src/com/android/dialer/list/SmartDialSearchFragment.java b/src/com/android/dialer/list/SmartDialSearchFragment.java
deleted file mode 100644
index fcb61ffe0..000000000
--- a/src/com/android/dialer/list/SmartDialSearchFragment.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.list;
-
-import static android.Manifest.permission.CALL_PHONE;
-
-import android.app.Activity;
-import android.content.Loader;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.v13.app.FragmentCompat;
-import android.util.Log;
-import android.view.View;
-
-import com.android.contacts.common.list.ContactEntryListAdapter;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.dialpad.SmartDialCursorLoader;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.ScreenEvent;
-import com.android.dialer.R;
-import com.android.dialer.widget.EmptyContentView;
-import com.android.incallui.Call.LogState;
-
-import java.util.ArrayList;
-
-/**
- * Implements a fragment to load and display SmartDial search results.
- */
-public class SmartDialSearchFragment extends SearchFragment
- implements EmptyContentView.OnEmptyViewActionButtonClickedListener,
- FragmentCompat.OnRequestPermissionsResultCallback {
- private static final String TAG = SmartDialSearchFragment.class.getSimpleName();
-
- private static final int CALL_PHONE_PERMISSION_REQUEST_CODE = 1;
-
- /**
- * Creates a SmartDialListAdapter to display and operate on search results.
- */
- @Override
- protected ContactEntryListAdapter createListAdapter() {
- SmartDialNumberListAdapter adapter = new SmartDialNumberListAdapter(getActivity());
- adapter.setUseCallableUri(super.usesCallableUri());
- adapter.setQuickContactEnabled(true);
- // Set adapter's query string to restore previous instance state.
- adapter.setQueryString(getQueryString());
- adapter.setListener(this);
- return adapter;
- }
-
- /**
- * Creates a SmartDialCursorLoader object to load query results.
- */
- @Override
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- // Smart dialing does not support Directory Load, falls back to normal search instead.
- if (id == getDirectoryLoaderId()) {
- return super.onCreateLoader(id, args);
- } else {
- final SmartDialNumberListAdapter adapter = (SmartDialNumberListAdapter) getAdapter();
- SmartDialCursorLoader loader = new SmartDialCursorLoader(super.getContext());
- adapter.configureLoader(loader);
- return loader;
- }
- }
-
- /**
- * Gets the Phone Uri of an entry for calling.
- * @param position Location of the data of interest.
- * @return Phone Uri to establish a phone call.
- */
- @Override
- protected Uri getPhoneUri(int position) {
- final SmartDialNumberListAdapter adapter = (SmartDialNumberListAdapter) getAdapter();
- return adapter.getDataUri(position);
- }
-
- @Override
- protected void setupEmptyView() {
- if (mEmptyView != null && getActivity() != null) {
- if (!PermissionsUtil.hasPermission(getActivity(), CALL_PHONE)) {
- mEmptyView.setImage(R.drawable.empty_contacts);
- mEmptyView.setActionLabel(R.string.permission_single_turn_on);
- mEmptyView.setDescription(R.string.permission_place_call);
- mEmptyView.setActionClickedListener(this);
- } else {
- mEmptyView.setImage(EmptyContentView.NO_IMAGE);
- mEmptyView.setActionLabel(EmptyContentView.NO_LABEL);
- mEmptyView.setDescription(EmptyContentView.NO_LABEL);
- }
- }
- }
-
- @Override
- public void onEmptyViewActionButtonClicked() {
- final Activity activity = getActivity();
- if (activity == null) {
- return;
- }
-
- FragmentCompat.requestPermissions(this, new String[] {CALL_PHONE},
- CALL_PHONE_PERMISSION_REQUEST_CODE);
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions,
- int[] grantResults) {
- if (requestCode == CALL_PHONE_PERMISSION_REQUEST_CODE) {
- setupEmptyView();
- }
- }
-
- @Override
- protected int getCallInitiationType(boolean isRemoteDirectory) {
- return LogState.INITIATION_SMART_DIAL;
- }
-
- public boolean isShowingPermissionRequest() {
- return mEmptyView != null && mEmptyView.isShowingContent();
- }
-}
diff --git a/src/com/android/dialer/list/SpeedDialFragment.java b/src/com/android/dialer/list/SpeedDialFragment.java
deleted file mode 100644
index 7e10297d0..000000000
--- a/src/com/android/dialer/list/SpeedDialFragment.java
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.list;
-
-import static android.Manifest.permission.READ_CONTACTS;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.LoaderManager;
-import android.content.CursorLoader;
-import android.content.Loader;
-import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Trace;
-import android.support.v13.app.FragmentCompat;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.animation.AnimationUtils;
-import android.view.animation.LayoutAnimationController;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.FrameLayout;
-import android.widget.FrameLayout.LayoutParams;
-import android.widget.ImageView;
-import android.widget.ListView;
-
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.ContactTileLoaderFactory;
-import com.android.contacts.common.list.ContactTileView;
-import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.R;
-import com.android.dialer.widget.EmptyContentView;
-import com.android.incallui.Call.LogState;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-/**
- * This fragment displays the user's favorite/frequent contacts in a grid.
- */
-public class SpeedDialFragment extends Fragment implements OnItemClickListener,
- PhoneFavoritesTileAdapter.OnDataSetChangedForAnimationListener,
- EmptyContentView.OnEmptyViewActionButtonClickedListener,
- FragmentCompat.OnRequestPermissionsResultCallback {
-
- private static final int READ_CONTACTS_PERMISSION_REQUEST_CODE = 1;
-
- /**
- * By default, the animation code assumes that all items in a list view are of the same height
- * when animating new list items into view (e.g. from the bottom of the screen into view).
- * This can cause incorrect translation offsets when a item that is larger or smaller than
- * other list item is removed from the list. This key is used to provide the actual height
- * of the removed object so that the actual translation appears correct to the user.
- */
- private static final long KEY_REMOVED_ITEM_HEIGHT = Long.MAX_VALUE;
-
- private static final String TAG = "SpeedDialFragment";
- private static final boolean DEBUG = false;
-
- private int mAnimationDuration;
-
- /**
- * Used with LoaderManager.
- */
- private static int LOADER_ID_CONTACT_TILE = 1;
-
- public interface HostInterface {
- public void setDragDropController(DragDropController controller);
- public void showAllContactsTab();
- }
-
- private class ContactTileLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
- @Override
- public CursorLoader onCreateLoader(int id, Bundle args) {
- if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onCreateLoader.");
- return ContactTileLoaderFactory.createStrequentPhoneOnlyLoader(getActivity());
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoadFinished");
- mContactTileAdapter.setContactCursor(data);
- setEmptyViewVisibility(mContactTileAdapter.getCount() == 0);
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {
- if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoaderReset. ");
- }
- }
-
- private class ContactTileAdapterListener implements ContactTileView.Listener {
- @Override
- public void onContactSelected(Uri contactUri, Rect targetRect) {
- if (mPhoneNumberPickerActionListener != null) {
- mPhoneNumberPickerActionListener.onPickDataUri(contactUri,
- false /* isVideoCall */, LogState.INITIATION_SPEED_DIAL);
- }
- }
-
- @Override
- public void onCallNumberDirectly(String phoneNumber) {
- if (mPhoneNumberPickerActionListener != null) {
- mPhoneNumberPickerActionListener.onPickPhoneNumber(phoneNumber,
- false /* isVideoCall */, LogState.INITIATION_SPEED_DIAL);
- }
- }
-
- @Override
- public int getApproximateTileWidth() {
- return getView().getWidth();
- }
- }
-
- private class ScrollListener implements ListView.OnScrollListener {
- @Override
- public void onScroll(AbsListView view,
- int firstVisibleItem, int visibleItemCount, int totalItemCount) {
- if (mActivityScrollListener != null) {
- mActivityScrollListener.onListFragmentScroll(firstVisibleItem, visibleItemCount,
- totalItemCount);
- }
- }
-
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- mActivityScrollListener.onListFragmentScrollStateChange(scrollState);
- }
- }
-
- private OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener;
-
- private OnListFragmentScrolledListener mActivityScrollListener;
- private PhoneFavoritesTileAdapter mContactTileAdapter;
-
- private View mParentView;
-
- private PhoneFavoriteListView mListView;
-
- private View mContactTileFrame;
-
- private final HashMap<Long, Integer> mItemIdTopMap = new HashMap<Long, Integer>();
- private final HashMap<Long, Integer> mItemIdLeftMap = new HashMap<Long, Integer>();
-
- /**
- * Layout used when there are no favorites.
- */
- private EmptyContentView mEmptyView;
-
- private final ContactTileView.Listener mContactTileAdapterListener =
- new ContactTileAdapterListener();
- private final LoaderManager.LoaderCallbacks<Cursor> mContactTileLoaderListener =
- new ContactTileLoaderListener();
- private final ScrollListener mScrollListener = new ScrollListener();
-
- @Override
- public void onAttach(Activity activity) {
- if (DEBUG) Log.d(TAG, "onAttach()");
- super.onAttach(activity);
-
- // Construct two base adapters which will become part of PhoneFavoriteMergedAdapter.
- // We don't construct the resultant adapter at this moment since it requires LayoutInflater
- // that will be available on onCreateView().
- mContactTileAdapter = new PhoneFavoritesTileAdapter(activity, mContactTileAdapterListener,
- this);
- mContactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
- }
-
- @Override
- public void onCreate(Bundle savedState) {
- if (DEBUG) Log.d(TAG, "onCreate()");
- Trace.beginSection(TAG + " onCreate");
- super.onCreate(savedState);
-
- mAnimationDuration = getResources().getInteger(R.integer.fade_duration);
- Trace.endSection();
- }
-
- @Override
- public void onResume() {
- Trace.beginSection(TAG + " onResume");
- super.onResume();
- if (mContactTileAdapter != null) {
- mContactTileAdapter.refreshContactsPreferences();
- }
- if (PermissionsUtil.hasContactsPermissions(getActivity())) {
- if (getLoaderManager().getLoader(LOADER_ID_CONTACT_TILE) == null) {
- getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null,
- mContactTileLoaderListener);
-
- } else {
- getLoaderManager().getLoader(LOADER_ID_CONTACT_TILE).forceLoad();
- }
-
- mEmptyView.setDescription(R.string.speed_dial_empty);
- mEmptyView.setActionLabel(R.string.speed_dial_empty_add_favorite_action);
- } else {
- mEmptyView.setDescription(R.string.permission_no_speeddial);
- mEmptyView.setActionLabel(R.string.permission_single_turn_on);
- }
- Trace.endSection();
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- Trace.beginSection(TAG + " onCreateView");
- mParentView = inflater.inflate(R.layout.speed_dial_fragment, container, false);
-
- mListView = (PhoneFavoriteListView) mParentView.findViewById(R.id.contact_tile_list);
- mListView.setOnItemClickListener(this);
- mListView.setVerticalScrollBarEnabled(false);
- mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
- mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
- mListView.getDragDropController().addOnDragDropListener(mContactTileAdapter);
-
- final ImageView dragShadowOverlay =
- (ImageView) getActivity().findViewById(R.id.contact_tile_drag_shadow_overlay);
- mListView.setDragShadowOverlay(dragShadowOverlay);
-
- mEmptyView = (EmptyContentView) mParentView.findViewById(R.id.empty_list_view);
- mEmptyView.setImage(R.drawable.empty_speed_dial);
- mEmptyView.setActionClickedListener(this);
-
- mContactTileFrame = mParentView.findViewById(R.id.contact_tile_frame);
-
- final LayoutAnimationController controller = new LayoutAnimationController(
- AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in));
- controller.setDelay(0);
- mListView.setLayoutAnimation(controller);
- mListView.setAdapter(mContactTileAdapter);
-
- mListView.setOnScrollListener(mScrollListener);
- mListView.setFastScrollEnabled(false);
- mListView.setFastScrollAlwaysVisible(false);
-
- //prevent content changes of the list from firing accessibility events.
- mListView.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_NONE);
- ContentChangedFilter.addToParent(mListView);
-
- Trace.endSection();
- return mParentView;
- }
-
- public boolean hasFrequents() {
- if (mContactTileAdapter == null) return false;
- return mContactTileAdapter.getNumFrequents() > 0;
- }
-
- /* package */ void setEmptyViewVisibility(final boolean visible) {
- final int previousVisibility = mEmptyView.getVisibility();
- final int emptyViewVisibility = visible ? View.VISIBLE : View.GONE;
- final int listViewVisibility = visible ? View.GONE : View.VISIBLE;
-
- if (previousVisibility != emptyViewVisibility) {
- final FrameLayout.LayoutParams params = (LayoutParams) mContactTileFrame
- .getLayoutParams();
- params.height = visible ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT;
- mContactTileFrame.setLayoutParams(params);
- mEmptyView.setVisibility(emptyViewVisibility);
- mListView.setVisibility(listViewVisibility);
- }
- }
-
- @Override
- public void onStart() {
- super.onStart();
-
- final Activity activity = getActivity();
-
- try {
- mActivityScrollListener = (OnListFragmentScrolledListener) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString()
- + " must implement OnListFragmentScrolledListener");
- }
-
- try {
- OnDragDropListener listener = (OnDragDropListener) activity;
- mListView.getDragDropController().addOnDragDropListener(listener);
- ((HostInterface) activity).setDragDropController(mListView.getDragDropController());
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString()
- + " must implement OnDragDropListener and HostInterface");
- }
-
- try {
- mPhoneNumberPickerActionListener = (OnPhoneNumberPickerActionListener) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString()
- + " must implement PhoneFavoritesFragment.listener");
- }
-
- // Use initLoader() instead of restartLoader() to refraining unnecessary reload.
- // This method call implicitly assures ContactTileLoaderListener's onLoadFinished() will
- // be called, on which we'll check if "all" contacts should be reloaded again or not.
- if (PermissionsUtil.hasContactsPermissions(activity)) {
- getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null, mContactTileLoaderListener);
- } else {
- setEmptyViewVisibility(true);
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * This is only effective for elements provided by {@link #mContactTileAdapter}.
- * {@link #mContactTileAdapter} has its own logic for click events.
- */
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- final int contactTileAdapterCount = mContactTileAdapter.getCount();
- if (position <= contactTileAdapterCount) {
- Log.e(TAG, "onItemClick() event for unexpected position. "
- + "The position " + position + " is before \"all\" section. Ignored.");
- }
- }
-
- /**
- * Cache the current view offsets into memory. Once a relayout of views in the ListView
- * has happened due to a dataset change, the cached offsets are used to create animations
- * that slide views from their previous positions to their new ones, to give the appearance
- * that the views are sliding into their new positions.
- */
- private void saveOffsets(int removedItemHeight) {
- final int firstVisiblePosition = mListView.getFirstVisiblePosition();
- if (DEBUG) {
- Log.d(TAG, "Child count : " + mListView.getChildCount());
- }
- for (int i = 0; i < mListView.getChildCount(); i++) {
- final View child = mListView.getChildAt(i);
- final int position = firstVisiblePosition + i;
- // Since we are getting the position from mListView and then querying
- // mContactTileAdapter, its very possible that things are out of sync
- // and we might index out of bounds. Let's make sure that this doesn't happen.
- if (!mContactTileAdapter.isIndexInBound(position)) {
- continue;
- }
- final long itemId = mContactTileAdapter.getItemId(position);
- if (DEBUG) {
- Log.d(TAG, "Saving itemId: " + itemId + " for listview child " + i + " Top: "
- + child.getTop());
- }
- mItemIdTopMap.put(itemId, child.getTop());
- mItemIdLeftMap.put(itemId, child.getLeft());
- }
- mItemIdTopMap.put(KEY_REMOVED_ITEM_HEIGHT, removedItemHeight);
- }
-
- /*
- * Performs animations for the gridView
- */
- private void animateGridView(final long... idsInPlace) {
- if (mItemIdTopMap.isEmpty()) {
- // Don't do animations if the database is being queried for the first time and
- // the previous item offsets have not been cached, or the user hasn't done anything
- // (dragging, swiping etc) that requires an animation.
- return;
- }
-
- final ViewTreeObserver observer = mListView.getViewTreeObserver();
- observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @SuppressWarnings("unchecked")
- @Override
- public boolean onPreDraw() {
- observer.removeOnPreDrawListener(this);
- final int firstVisiblePosition = mListView.getFirstVisiblePosition();
- final AnimatorSet animSet = new AnimatorSet();
- final ArrayList<Animator> animators = new ArrayList<Animator>();
- for (int i = 0; i < mListView.getChildCount(); i++) {
- final View child = mListView.getChildAt(i);
- int position = firstVisiblePosition + i;
-
- // Since we are getting the position from mListView and then querying
- // mContactTileAdapter, its very possible that things are out of sync
- // and we might index out of bounds. Let's make sure that this doesn't happen.
- if (!mContactTileAdapter.isIndexInBound(position)) {
- continue;
- }
-
- final long itemId = mContactTileAdapter.getItemId(position);
-
- if (containsId(idsInPlace, itemId)) {
- animators.add(ObjectAnimator.ofFloat(
- child, "alpha", 0.0f, 1.0f));
- break;
- } else {
- Integer startTop = mItemIdTopMap.get(itemId);
- Integer startLeft = mItemIdLeftMap.get(itemId);
- final int top = child.getTop();
- final int left = child.getLeft();
- int deltaX = 0;
- int deltaY = 0;
-
- if (startLeft != null) {
- if (startLeft != left) {
- deltaX = startLeft - left;
- animators.add(ObjectAnimator.ofFloat(
- child, "translationX", deltaX, 0.0f));
- }
- }
-
- if (startTop != null) {
- if (startTop != top) {
- deltaY = startTop - top;
- animators.add(ObjectAnimator.ofFloat(
- child, "translationY", deltaY, 0.0f));
- }
- }
-
- if (DEBUG) {
- Log.d(TAG, "Found itemId: " + itemId + " for listview child " + i +
- " Top: " + top +
- " Delta: " + deltaY);
- }
- }
- }
-
- if (animators.size() > 0) {
- animSet.setDuration(mAnimationDuration).playTogether(animators);
- animSet.start();
- }
-
- mItemIdTopMap.clear();
- mItemIdLeftMap.clear();
- return true;
- }
- });
- }
-
- private boolean containsId(long[] ids, long target) {
- // Linear search on array is fine because this is typically only 0-1 elements long
- for (int i = 0; i < ids.length; i++) {
- if (ids[i] == target) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public void onDataSetChangedForAnimation(long... idsInPlace) {
- animateGridView(idsInPlace);
- }
-
- @Override
- public void cacheOffsetsForDatasetChange() {
- saveOffsets(0);
- }
-
- public AbsListView getListView() {
- return mListView;
- }
-
- @Override
- public void onEmptyViewActionButtonClicked() {
- final Activity activity = getActivity();
- if (activity == null) {
- return;
- }
-
- if (!PermissionsUtil.hasPermission(activity, READ_CONTACTS)) {
- FragmentCompat.requestPermissions(this, new String[] {READ_CONTACTS},
- READ_CONTACTS_PERMISSION_REQUEST_CODE);
- } else {
- // Switch tabs
- ((HostInterface) activity).showAllContactsTab();
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions,
- int[] grantResults) {
- if (requestCode == READ_CONTACTS_PERMISSION_REQUEST_CODE) {
- if (grantResults.length == 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
- PermissionsUtil.notifyPermissionGranted(getActivity(), READ_CONTACTS);
- }
- }
- }
-}
diff --git a/src/com/android/dialer/logging/InteractionEvent.java b/src/com/android/dialer/logging/InteractionEvent.java
deleted file mode 100644
index 88518b47c..000000000
--- a/src/com/android/dialer/logging/InteractionEvent.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.dialer.logging;
-
-/**
- * Class holding constants for Dialer interactions
- */
-public class InteractionEvent {
-
- public static final int UNKNOWN = 0;
-
- /**
- * An incoming call was blocked
- */
- public static final int CALL_BLOCKED = 15;
-
- /**
- * The user blocked a number from the Call Log screen
- */
- public static final int BLOCK_NUMBER_CALL_LOG = 16;
-
- /**
- * The user blocked a number from the Call details screen
- */
- public static final int BLOCK_NUMBER_CALL_DETAIL = 17;
-
- /**
- * The user blocked a number from the Management screen
- */
- public static final int BLOCK_NUMBER_MANAGEMENT_SCREEN = 18;
-
- /**
- * The user unblocked a number from the Call Log screen
- */
- public static final int UNBLOCK_NUMBER_CALL_LOG = 19;
-
- /**
- * The user unblocked a number from the Call details screen
- */
- public static final int UNBLOCK_NUMBER_CALL_DETAIL = 20;
-
- /**
- * The user unblocked a number from the Management screen
- */
- public static final int UNBLOCK_NUMBER_MANAGEMENT_SCREEN = 21;
-
- /**
- * The user blocked numbers from contacts marked as send to voicemail
- */
- public static final int IMPORT_SEND_TO_VOICEMAIL = 22;
-
- /**
- * The user blocked a number then undid the block
- */
- public static final int UNDO_BLOCK_NUMBER = 23;
-
- /**
- * The user unblocked a number then undid the unblock
- */
- public static final int UNDO_UNBLOCK_NUMBER = 24;
-
-}
diff --git a/src/com/android/dialer/logging/Logger.java b/src/com/android/dialer/logging/Logger.java
deleted file mode 100644
index 25b7268ad..000000000
--- a/src/com/android/dialer/logging/Logger.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.dialer.logging;
-
-import android.app.Activity;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.commonbind.analytics.AnalyticsUtil;
-import com.android.dialerbind.ObjectFactory;
-import com.android.incallui.Call;
-
-/**
- * Single entry point for all logging/analytics-related work for all user interactions.
- */
-public abstract class Logger {
- public static final String TAG = "Logger";
-
- public static Logger getInstance() {
- return ObjectFactory.getLoggerInstance();
- }
-
- /**
- * Logs a call event. PII like the call's number or caller details should never be logged.
- *
- * @param call to log.
- */
- public static void logCall(Call call) {
- final Logger logger = getInstance();
- if (logger != null) {
- logger.logCallImpl(call);
- }
- }
-
- /**
- * Logs an event indicating that a screen was displayed.
- *
- * @param screenType integer identifier of the displayed screen
- * @param activity Parent activity of the displayed screen.
- */
- public static void logScreenView(int screenType, Activity activity) {
- final Logger logger = getInstance();
- if (logger != null) {
- logger.logScreenViewImpl(screenType);
- }
-
- final String screenName = ScreenEvent.getScreenName(screenType);
- if (!TextUtils.isEmpty(screenName)) {
- AnalyticsUtil.sendScreenView(screenName, activity, null);
- } else {
- Log.w(TAG, "Unknown screenType: " + screenType);
- }
- }
-
- /**
- * Logs an interaction that occurred
- *
- * @param interaction an integer representing what interaction occurred.
- * {@see com.android.dialer.logging.InteractionEvent}
- */
- public static void logInteraction(int interaction) {
- final Logger logger = getInstance();
- if (logger != null) {
- logger.logInteractionImpl(interaction);
- }
- }
-
- public abstract void logCallImpl(Call call);
- public abstract void logScreenViewImpl(int screenType);
- public abstract void logInteractionImpl(int dialerInteraction);
-}
diff --git a/src/com/android/dialer/logging/ScreenEvent.java b/src/com/android/dialer/logging/ScreenEvent.java
deleted file mode 100644
index e0d7b0026..000000000
--- a/src/com/android/dialer/logging/ScreenEvent.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * 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.dialer.logging;
-
-import android.text.TextUtils;
-
-import com.android.contacts.common.dialog.ClearFrequentsDialog;
-import com.android.contacts.common.interactions.ImportExportDialogFragment;
-import com.android.dialer.calllog.CallLogFragment;
-import com.android.dialer.dialpad.DialpadFragment;
-import com.android.dialer.filterednumber.BlockedNumbersFragment;
-import com.android.dialer.list.AllContactsFragment;
-import com.android.dialer.list.BlockedListSearchFragment;
-import com.android.dialer.list.RegularSearchFragment;
-import com.android.dialer.list.SmartDialSearchFragment;
-import com.android.dialer.list.SpeedDialFragment;
-import com.android.dialer.settings.DialerSettingsActivity;
-import com.android.incallui.AnswerFragment;
-import com.android.incallui.CallCardFragment;
-import com.android.incallui.ConferenceManagerFragment;
-import com.android.incallui.InCallActivity;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Stores constants identifying individual screens/dialogs/fragments in the application, and also
- * provides a mapping of integer id -> screen name mappings for analytics purposes.
- */
-public class ScreenEvent {
- private static final Map<Integer, String> sScreenNameMap = new HashMap<>();
-
- public static final String FRAGMENT_TAG_SEPARATOR = "#";
-
- public static final int UNKNOWN = 0;
-
- // The dialpad in the main Dialer activity
- public static final int DIALPAD = 1;
-
- // The speed dial tab in the main Dialer activity
- public static final int SPEED_DIAL = 2;
-
- // The recents tab in the main Dialer activity
- public static final int CALL_LOG = 3;
-
- // The voicemail tab in the main Dialer activity
- public static final int VOICEMAIL_LOG = 4;
-
- // The all contacts tab in the main Dialer activity
- public static final int ALL_CONTACTS = 5;
-
- // List of search results returned by typing into the search box.
- public static final int REGULAR_SEARCH = 6;
-
- // List of search results returned by typing into the dialpad.
- public static final int SMART_DIAL_SEARCH = 7;
-
- // The All and Missed call log tabs in CallLogActivity
- public static final int CALL_LOG_FILTER = 8;
-
- // Dialer settings screen.
- public static final int SETTINGS = 9;
-
- // The "Import/export contacts" dialog launched via the overflow menu.
- public static final int IMPORT_EXPORT_CONTACTS = 10;
-
- // The "Clear frequents" dialog launched via the overflow menu.
- public static final int CLEAR_FREQUENTS = 11;
-
- // The "Send feedback" dialog launched via the overflow menu.
- public static final int SEND_FEEDBACK = 12;
-
- // The main in call screen that displays caller details and contact photos
- public static final int INCALL = 13;
-
- // The screen that displays the glowpad widget (slide right to answer,
- // slide left to dismiss).
- public static final int INCOMING_CALL = 14;
-
- // Conference management fragment displayed for conferences that support
- // management of individual calls within the conference.
- public static final int CONFERENCE_MANAGEMENT = 15;
-
- // The dialpad displayed in-call that is used to send dtmf tones.
- public static final int INCALL_DIALPAD = 16;
-
- // Menu options displayed when long pressing on a call log entry.
- public static final int CALL_LOG_CONTEXT_MENU = 17;
-
- // Screen displayed to allow the user to see an overview of all blocked
- // numbers.
- public static final int BLOCKED_NUMBER_MANAGEMENT = 18;
-
- // Screen displayed to allow the user to add a new blocked number.
- public static final int BLOCKED_NUMBER_ADD_NUMBER = 19;
-
- static {
- sScreenNameMap.put(ScreenEvent.DIALPAD,
- getScreenNameWithTag(DialpadFragment.class.getSimpleName(), "Dialer"));
- sScreenNameMap.put(ScreenEvent.SPEED_DIAL, SpeedDialFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.CALL_LOG,
- getScreenNameWithTag(CallLogFragment.class.getSimpleName(), "History"));
- sScreenNameMap.put(ScreenEvent.VOICEMAIL_LOG,
- getScreenNameWithTag(CallLogFragment.class.getSimpleName(), "Voicemail"));
- sScreenNameMap.put(ScreenEvent.ALL_CONTACTS, AllContactsFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.REGULAR_SEARCH,
- RegularSearchFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.SMART_DIAL_SEARCH,
- SmartDialSearchFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.CALL_LOG_FILTER,
- getScreenNameWithTag(CallLogFragment.class.getSimpleName(), "Filtered"));
- sScreenNameMap.put(ScreenEvent.SETTINGS,
- DialerSettingsActivity.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.IMPORT_EXPORT_CONTACTS,
- ImportExportDialogFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.CLEAR_FREQUENTS,
- ClearFrequentsDialog.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.SEND_FEEDBACK, "SendFeedback");
- sScreenNameMap.put(ScreenEvent.INCALL, InCallActivity.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.INCOMING_CALL, AnswerFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.CONFERENCE_MANAGEMENT,
- ConferenceManagerFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.INCALL_DIALPAD,
- getScreenNameWithTag(DialpadFragment.class.getSimpleName(), "InCall"));
- sScreenNameMap.put(ScreenEvent.CALL_LOG_CONTEXT_MENU, "CallLogContextMenu");
- sScreenNameMap.put(ScreenEvent.BLOCKED_NUMBER_MANAGEMENT,
- BlockedNumbersFragment.class.getSimpleName());
- sScreenNameMap.put(ScreenEvent.BLOCKED_NUMBER_ADD_NUMBER,
- BlockedListSearchFragment.class.getSimpleName());
- }
-
- /**
- * For a given screen type, returns the actual screen name that is used for logging/analytics
- * purposes.
- *
- * @param screenType unique ID of a type of screen
- *
- * @return the tagged version of the screen name corresponding to the provided screenType,
- * or {@null} if the provided screenType is unknown.
- */
- public static String getScreenName(int screenType) {
- return sScreenNameMap.get(screenType);
- }
-
- /**
- * Build a tagged version of the provided screenName if the tag is non-empty.
- *
- * @param screenName Name of the screen.
- * @param tag Optional tag describing the screen.
- * @return the unchanged screenName if the tag is {@code null} or empty, the tagged version of
- * the screenName otherwise.
- */
- public static String getScreenNameWithTag(String screenName, String tag) {
- if (TextUtils.isEmpty(tag)) {
- return screenName;
- }
- return screenName + FRAGMENT_TAG_SEPARATOR + tag;
- }
-}
diff --git a/src/com/android/dialer/service/CachedNumberLookupService.java b/src/com/android/dialer/service/CachedNumberLookupService.java
deleted file mode 100644
index 018ada93f..000000000
--- a/src/com/android/dialer/service/CachedNumberLookupService.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.android.dialer.service;
-
-import android.content.Context;
-import android.net.Uri;
-import android.support.annotation.Nullable;
-
-import com.android.dialer.calllog.ContactInfo;
-
-import java.io.InputStream;
-
-public interface CachedNumberLookupService {
-
- public interface CachedContactInfo {
- public static final int SOURCE_TYPE_DIRECTORY = 1;
- public static final int SOURCE_TYPE_EXTENDED = 2;
- public static final int SOURCE_TYPE_PLACES = 3;
- public static final int SOURCE_TYPE_PROFILE = 4;
- public static final int SOURCE_TYPE_CNAP = 5;
-
- public ContactInfo getContactInfo();
-
- public void setSource(int sourceType, String name, long directoryId);
- public void setDirectorySource(String name, long directoryId);
- public void setExtendedSource(String name, long directoryId);
- public void setLookupKey(String lookupKey);
- }
-
- public CachedContactInfo buildCachedContactInfo(ContactInfo info);
-
- /**
- * Perform a lookup using the cached number lookup service to return contact
- * information stored in the cache that corresponds to the given number.
- *
- * @param context Valid context
- * @param number Phone number to lookup the cache for
- * @return A {@link CachedContactInfo} containing the contact information if the phone
- * number is found in the cache, {@link ContactInfo#EMPTY} if the phone number was
- * not found in the cache, and null if there was an error when querying the cache.
- */
- public CachedContactInfo lookupCachedContactFromNumber(Context context, String number);
-
- public void addContact(Context context, CachedContactInfo info);
-
- public boolean isCacheUri(String uri);
-
- public boolean isBusiness(int sourceType);
- public boolean canReportAsInvalid(int sourceType, String objectId);
-
- /**
- * @return return {@link Uri} to the photo or return {@code null} when failing to add photo
- */
- public @Nullable Uri addPhoto(Context context, String number, InputStream in);
-
- /**
- * Remove all cached phone number entries from the cache, regardless of how old they
- * are.
- *
- * @param context Valid context
- */
- public void clearAllCacheEntries(Context context);
-}
diff --git a/src/com/android/dialer/service/ExtendedCallInfoService.java b/src/com/android/dialer/service/ExtendedCallInfoService.java
deleted file mode 100644
index 5481cc90d..000000000
--- a/src/com/android/dialer/service/ExtendedCallInfoService.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.dialer.service;
-
-import android.support.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Interface of service to get extended call information.
- */
-public interface ExtendedCallInfoService {
- /**
- * All the possible locations that a user can report a number as spam or not spam.
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({REPORTING_LOCATION_UNKNOWN, REPORTING_LOCATION_CALL_LOG_HISTORY,
- REPORTING_LOCATION_FEEDBACK_PROMPT})
- @interface ReportingLocation {}
- int REPORTING_LOCATION_UNKNOWN = 0;
- int REPORTING_LOCATION_CALL_LOG_HISTORY = 1;
- int REPORTING_LOCATION_FEEDBACK_PROMPT = 2;
-
- /**
- * Interface for a callback to be invoked when data is fetched.
- */
- interface Listener {
- /**
- * Called when data is fetched.
- * @param isSpam True if the call is spam.
- */
- void onComplete(boolean isSpam);
- }
-
- /**
- * Gets extended call information.
- * @param number The phone number of the call.
- * @param countryIso The country ISO of the call.
- * @param listener The callback to be invoked after {@code Info} is fetched.
- */
- void getExtendedCallInfo(String number, String countryIso, Listener listener);
-
- /**
- * Reports number as spam.
- * @param number The number to be reported.
- * @param countryIso The country ISO of the number.
- * @param callType Whether the type of call is missed, voicemail, etc. Example of this is
- * {@link android.provider.CallLog.Calls#VOICEMAIL_TYPE}.
- * @param from Where in the dialer this was reported from.
- * Must be one of {@link ReportingLocation}.
- */
- void reportSpam(String number, String countryIso, int callType, @ReportingLocation int from);
-
- /**
- * Reports number as not spam.
- * @param number The number to be reported.
- * @param countryIso The country ISO of the number.
- * @param callType Whether the type of call is missed, voicemail, etc. Example of this is
- * {@link android.provider.CallLog.Calls#VOICEMAIL_TYPE}.
- * @param from Where in the dialer this was reported from.
- * Must be one of {@link ReportingLocation}.
- */
- void reportNotSpam(String number, String countryIso, int callType, @ReportingLocation int from);
-}
diff --git a/src/com/android/dialer/settings/AppCompatPreferenceActivity.java b/src/com/android/dialer/settings/AppCompatPreferenceActivity.java
deleted file mode 100644
index 4e5d9c90e..000000000
--- a/src/com/android/dialer/settings/AppCompatPreferenceActivity.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * 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.dialer.settings;
-
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.preference.PreferenceActivity;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatDelegate;
-import android.support.v7.widget.Toolbar;
-import android.view.MenuInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
- * to be used with AppCompat.
- */
-public class AppCompatPreferenceActivity extends PreferenceActivity {
- private AppCompatDelegate mDelegate;
-
- private boolean mIsSafeToCommitTransactions;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- getDelegate().installViewFactory();
- getDelegate().onCreate(savedInstanceState);
- super.onCreate(savedInstanceState);
- mIsSafeToCommitTransactions = true;
- }
-
- @Override
- protected void onPostCreate(Bundle savedInstanceState) {
- super.onPostCreate(savedInstanceState);
- getDelegate().onPostCreate(savedInstanceState);
- }
-
- public ActionBar getSupportActionBar() {
- return getDelegate().getSupportActionBar();
- }
-
- public void setSupportActionBar(Toolbar toolbar) {
- getDelegate().setSupportActionBar(toolbar);
- }
-
- @Override
- public MenuInflater getMenuInflater() {
- return getDelegate().getMenuInflater();
- }
-
- @Override
- public void setContentView(int layoutResID) {
- getDelegate().setContentView(layoutResID);
- }
-
- @Override
- public void setContentView(View view) {
- getDelegate().setContentView(view);
- }
-
- @Override
- public void setContentView(View view, ViewGroup.LayoutParams params) {
- getDelegate().setContentView(view, params);
- }
-
- @Override
- public void addContentView(View view, ViewGroup.LayoutParams params) {
- getDelegate().addContentView(view, params);
- }
-
- @Override
- protected void onPostResume() {
- super.onPostResume();
- getDelegate().onPostResume();
- }
-
- @Override
- protected void onTitleChanged(CharSequence title, int color) {
- super.onTitleChanged(title, color);
- getDelegate().setTitle(title);
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- getDelegate().onConfigurationChanged(newConfig);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- getDelegate().onStop();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- getDelegate().onDestroy();
- }
-
- @Override
- public void invalidateOptionsMenu() {
- getDelegate().invalidateOptionsMenu();
- }
-
- private AppCompatDelegate getDelegate() {
- if (mDelegate == null) {
- mDelegate = AppCompatDelegate.create(this, null);
- }
- return mDelegate;
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- mIsSafeToCommitTransactions = true;
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mIsSafeToCommitTransactions = true;
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mIsSafeToCommitTransactions = false;
- }
-
- /**
- * Returns true if it is safe to commit {@link FragmentTransaction}s at this time, based on
- * whether {@link Activity#onSaveInstanceState} has been called or not.
- *
- * Make sure that the current activity calls into
- * {@link super.onSaveInstanceState(Bundle outState)} (if that method is overridden),
- * so the flag is properly set.
- */
- public boolean isSafeToCommitTransactions() {
- return mIsSafeToCommitTransactions;
- }
-}
diff --git a/src/com/android/dialer/settings/DefaultRingtonePreference.java b/src/com/android/dialer/settings/DefaultRingtonePreference.java
deleted file mode 100644
index a8a23fddf..000000000
--- a/src/com/android/dialer/settings/DefaultRingtonePreference.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2014 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.dialer.settings;
-
-import android.content.Context;
-import android.content.Intent;
-import android.media.RingtoneManager;
-import android.net.Uri;
-import android.preference.RingtonePreference;
-import android.util.AttributeSet;
-import android.widget.Toast;
-
-import com.android.dialer.R;
-import com.android.dialer.compat.SettingsCompat;
-
-/**
- * RingtonePreference which doesn't show default ringtone setting.
- */
-public class DefaultRingtonePreference extends RingtonePreference {
- public DefaultRingtonePreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onPrepareRingtonePickerIntent(Intent ringtonePickerIntent) {
- super.onPrepareRingtonePickerIntent(ringtonePickerIntent);
-
- /*
- * Since this preference is for choosing the default ringtone, it
- * doesn't make sense to show a 'Default' item.
- */
- ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
- }
-
- @Override
- protected void onSaveRingtone(Uri ringtoneUri) {
- if (!SettingsCompat.System.canWrite(getContext())) {
- Toast.makeText(
- getContext(),
- getContext().getResources().getString(R.string.toast_cannot_write_system_settings),
- Toast.LENGTH_SHORT).show();
- return;
- }
- RingtoneManager.setActualDefaultRingtoneUri(getContext(), getRingtoneType(), ringtoneUri);
- }
-
- @Override
- protected Uri onRestoreRingtone() {
- return RingtoneManager.getActualDefaultRingtoneUri(getContext(), getRingtoneType());
- }
-}
diff --git a/src/com/android/dialer/settings/DialerSettingsActivity.java b/src/com/android/dialer/settings/DialerSettingsActivity.java
deleted file mode 100644
index dc1e21457..000000000
--- a/src/com/android/dialer/settings/DialerSettingsActivity.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.settings;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.os.UserManager;
-import android.preference.PreferenceManager;
-import android.provider.Settings;
-import android.support.v4.os.BuildCompat;
-import android.telecom.TelecomManager;
-import android.telephony.TelephonyManager;
-import android.view.MenuItem;
-import android.widget.Toast;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.TelephonyManagerCompat;
-import com.android.dialer.R;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.compat.SettingsCompat;
-import com.android.dialer.compat.UserManagerCompat;
-
-import java.util.List;
-
-public class DialerSettingsActivity extends AppCompatPreferenceActivity {
- protected SharedPreferences mPreferences;
- private boolean migrationStatusOnBuildHeaders;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- /*
- * The headers need to be recreated if the migration status changed between when the headers
- * were created and now.
- */
- if (migrationStatusOnBuildHeaders != FilteredNumberCompat.hasMigratedToNewBlocking()) {
- invalidateHeaders();
- }
- }
-
- @Override
- public void onBuildHeaders(List<Header> target) {
- if (showDisplayOptions()) {
- Header displayOptionsHeader = new Header();
- displayOptionsHeader.titleRes = R.string.display_options_title;
- displayOptionsHeader.fragment = DisplayOptionsSettingsFragment.class.getName();
- target.add(displayOptionsHeader);
- }
-
- Header soundSettingsHeader = new Header();
- soundSettingsHeader.titleRes = R.string.sounds_and_vibration_title;
- soundSettingsHeader.fragment = SoundSettingsFragment.class.getName();
- soundSettingsHeader.id = R.id.settings_header_sounds_and_vibration;
- target.add(soundSettingsHeader);
-
- if (CompatUtils.isMarshmallowCompatible()) {
- Header quickResponseSettingsHeader = new Header();
- Intent quickResponseSettingsIntent =
- new Intent(TelecomManager.ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS);
- quickResponseSettingsHeader.titleRes = R.string.respond_via_sms_setting_title;
- quickResponseSettingsHeader.intent = quickResponseSettingsIntent;
- target.add(quickResponseSettingsHeader);
- }
-
- TelephonyManager telephonyManager =
- (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
-
- // "Call Settings" (full settings) is shown if the current user is primary user and there
- // is only one SIM. Before N, "Calling accounts" setting is shown if the current user is
- // primary user and there are multiple SIMs. In N+, "Calling accounts" is shown whenever
- // "Call Settings" is not shown.
- boolean isPrimaryUser = isPrimaryUser();
- if (isPrimaryUser
- && TelephonyManagerCompat.getPhoneCount(telephonyManager) <= 1) {
- Header callSettingsHeader = new Header();
- Intent callSettingsIntent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS);
- callSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-
- callSettingsHeader.titleRes = R.string.call_settings_label;
- callSettingsHeader.intent = callSettingsIntent;
- target.add(callSettingsHeader);
- } else if (BuildCompat.isAtLeastN() || isPrimaryUser) {
- Header phoneAccountSettingsHeader = new Header();
- Intent phoneAccountSettingsIntent =
- new Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS);
- phoneAccountSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-
- phoneAccountSettingsHeader.titleRes = R.string.phone_account_settings_label;
- phoneAccountSettingsHeader.intent = phoneAccountSettingsIntent;
- target.add(phoneAccountSettingsHeader);
- }
- if (FilteredNumberCompat.canCurrentUserOpenBlockSettings(this)) {
- Header blockedCallsHeader = new Header();
- blockedCallsHeader.titleRes = R.string.manage_blocked_numbers_label;
- blockedCallsHeader.intent = FilteredNumberCompat.createManageBlockedNumbersIntent(this);
- target.add(blockedCallsHeader);
- migrationStatusOnBuildHeaders = FilteredNumberCompat.hasMigratedToNewBlocking();
- }
- if (isPrimaryUser
- && (TelephonyManagerCompat.isTtyModeSupported(telephonyManager)
- || TelephonyManagerCompat.isHearingAidCompatibilitySupported(telephonyManager))) {
- Header accessibilitySettingsHeader = new Header();
- Intent accessibilitySettingsIntent =
- new Intent(TelecomManager.ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS);
- accessibilitySettingsHeader.titleRes = R.string.accessibility_settings_title;
- accessibilitySettingsHeader.intent = accessibilitySettingsIntent;
- target.add(accessibilitySettingsHeader);
- }
- }
-
- /**
- * Returns {@code true} or {@code false} based on whether the display options setting should be
- * shown. For languages such as Chinese, Japanese, or Korean, display options aren't useful
- * since contacts are sorted and displayed family name first by default.
- *
- * @return {@code true} if the display options should be shown, {@code false} otherwise.
- */
- private boolean showDisplayOptions() {
- return getResources().getBoolean(R.bool.config_display_order_user_changeable)
- && getResources().getBoolean(R.bool.config_sort_order_user_changeable);
- }
-
- @Override
- public void onHeaderClick(Header header, int position) {
- if (header.id == R.id.settings_header_sounds_and_vibration) {
- // If we don't have the permission to write to system settings, go to system sound
- // settings instead. Otherwise, perform the super implementation (which launches our
- // own preference fragment.
- if (!SettingsCompat.System.canWrite(this)) {
- Toast.makeText(
- this,
- getResources().getString(R.string.toast_cannot_write_system_settings),
- Toast.LENGTH_SHORT).show();
- startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS));
- return;
- }
- }
- super.onHeaderClick(header, position);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == android.R.id.home) {
- onBackPressed();
- return true;
- }
- return false;
- }
-
- @Override
- public void onBackPressed() {
- if (!isSafeToCommitTransactions()) {
- return;
- }
- super.onBackPressed();
- }
-
- @Override
- protected boolean isValidFragment(String fragmentName) {
- return true;
- }
-
- /**
- * @return Whether the current user is the primary user.
- */
- private boolean isPrimaryUser() {
- return UserManagerCompat.isSystemUser((UserManager) getSystemService(Context.USER_SERVICE));
- }
-}
diff --git a/src/com/android/dialer/settings/SoundSettingsFragment.java b/src/com/android/dialer/settings/SoundSettingsFragment.java
deleted file mode 100644
index 59f8798c3..000000000
--- a/src/com/android/dialer/settings/SoundSettingsFragment.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2014 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.dialer.settings;
-
-import android.content.Context;
-import android.media.RingtoneManager;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Vibrator;
-import android.preference.CheckBoxPreference;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-import android.preference.PreferenceScreen;
-import android.provider.Settings;
-import android.telephony.CarrierConfigManager;
-import android.telephony.TelephonyManager;
-import android.widget.Toast;
-
-import com.android.contacts.common.compat.SdkVersionOverride;
-import com.android.dialer.R;
-import com.android.dialer.compat.SettingsCompat;
-import com.android.phone.common.util.SettingsUtil;
-
-public class SoundSettingsFragment extends PreferenceFragment
- implements Preference.OnPreferenceChangeListener {
-
- private static final int NO_DTMF_TONE = 0;
- private static final int PLAY_DTMF_TONE = 1;
-
- private static final int NO_VIBRATION_FOR_CALLS = 0;
- private static final int DO_VIBRATION_FOR_CALLS = 1;
-
-
- private static final int DTMF_TONE_TYPE_NORMAL = 0;
-
- private static final int SHOW_CARRIER_SETTINGS = 0;
- private static final int HIDE_CARRIER_SETTINGS = 1;
-
- private static final int MSG_UPDATE_RINGTONE_SUMMARY = 1;
-
- private Preference mRingtonePreference;
- private CheckBoxPreference mVibrateWhenRinging;
- private CheckBoxPreference mPlayDtmfTone;
- private ListPreference mDtmfToneLength;
-
- private final Runnable mRingtoneLookupRunnable = new Runnable() {
- @Override
- public void run() {
- updateRingtonePreferenceSummary();
- }
- };
-
- private final Handler mRingtoneLookupComplete = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_RINGTONE_SUMMARY:
- mRingtonePreference.setSummary((CharSequence) msg.obj);
- break;
- }
- }
- };
-
- @Override
- public Context getContext() {
- return getActivity();
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- addPreferencesFromResource(R.xml.sound_settings);
-
- Context context = getActivity();
-
- mRingtonePreference = findPreference(context.getString(R.string.ringtone_preference_key));
- mVibrateWhenRinging = (CheckBoxPreference) findPreference(
- context.getString(R.string.vibrate_on_preference_key));
- mPlayDtmfTone = (CheckBoxPreference) findPreference(
- context.getString(R.string.play_dtmf_preference_key));
- mDtmfToneLength = (ListPreference) findPreference(
- context.getString(R.string.dtmf_tone_length_preference_key));
-
- if (hasVibrator()) {
- mVibrateWhenRinging.setOnPreferenceChangeListener(this);
- } else {
- getPreferenceScreen().removePreference(mVibrateWhenRinging);
- mVibrateWhenRinging = null;
- }
-
- mPlayDtmfTone.setOnPreferenceChangeListener(this);
- mPlayDtmfTone.setChecked(shouldPlayDtmfTone());
-
- TelephonyManager telephonyManager =
- (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
- if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M
- && telephonyManager.canChangeDtmfToneLength()
- && (telephonyManager.isWorldPhone() || !shouldHideCarrierSettings())) {
- mDtmfToneLength.setOnPreferenceChangeListener(this);
- mDtmfToneLength.setValueIndex(
- Settings.System.getInt(context.getContentResolver(),
- Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
- DTMF_TONE_TYPE_NORMAL));
- } else {
- getPreferenceScreen().removePreference(mDtmfToneLength);
- mDtmfToneLength = null;
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- if (!SettingsCompat.System.canWrite(getContext())) {
- // If the user launches this setting fragment, then toggles the WRITE_SYSTEM_SETTINGS
- // AppOp, then close the fragment since there is nothing useful to do.
- getActivity().onBackPressed();
- return;
- }
-
- if (mVibrateWhenRinging != null) {
- mVibrateWhenRinging.setChecked(shouldVibrateWhenRinging());
- }
-
- // Lookup the ringtone name asynchronously.
- new Thread(mRingtoneLookupRunnable).start();
- }
-
- /**
- * Supports onPreferenceChangeListener to look for preference changes.
- *
- * @param preference The preference to be changed
- * @param objValue The value of the selection, NOT its localized display value.
- */
- @Override
- public boolean onPreferenceChange(Preference preference, Object objValue) {
- if (!SettingsCompat.System.canWrite(getContext())) {
- // A user shouldn't be able to get here, but this protects against monkey crashes.
- Toast.makeText(
- getContext(),
- getResources().getString(R.string.toast_cannot_write_system_settings),
- Toast.LENGTH_SHORT).show();
- return true;
- }
- if (preference == mVibrateWhenRinging) {
- boolean doVibrate = (Boolean) objValue;
- Settings.System.putInt(getActivity().getContentResolver(),
- Settings.System.VIBRATE_WHEN_RINGING,
- doVibrate ? DO_VIBRATION_FOR_CALLS : NO_VIBRATION_FOR_CALLS);
- } else if (preference == mDtmfToneLength) {
- int index = mDtmfToneLength.findIndexOfValue((String) objValue);
- Settings.System.putInt(getActivity().getContentResolver(),
- Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, index);
- }
- return true;
- }
-
- /**
- * Click listener for toggle events.
- */
- @Override
- public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
- if (!SettingsCompat.System.canWrite(getContext())) {
- Toast.makeText(
- getContext(),
- getResources().getString(R.string.toast_cannot_write_system_settings),
- Toast.LENGTH_SHORT).show();
- return true;
- }
- if (preference == mPlayDtmfTone) {
- Settings.System.putInt(getActivity().getContentResolver(),
- Settings.System.DTMF_TONE_WHEN_DIALING,
- mPlayDtmfTone.isChecked() ? PLAY_DTMF_TONE : NO_DTMF_TONE);
- }
- return true;
- }
-
- /**
- * Updates the summary text on the ringtone preference with the name of the ringtone.
- */
- private void updateRingtonePreferenceSummary() {
- SettingsUtil.updateRingtoneName(
- getActivity(),
- mRingtoneLookupComplete,
- RingtoneManager.TYPE_RINGTONE,
- mRingtonePreference.getKey(),
- MSG_UPDATE_RINGTONE_SUMMARY);
- }
-
- /**
- * Obtain the value for "vibrate when ringing" setting. The default value is false.
- *
- * Watch out: if the setting is missing in the device, this will try obtaining the old
- * "vibrate on ring" setting from AudioManager, and save the previous setting to the new one.
- */
- private boolean shouldVibrateWhenRinging() {
- int vibrateWhenRingingSetting = Settings.System.getInt(getActivity().getContentResolver(),
- Settings.System.VIBRATE_WHEN_RINGING,
- NO_VIBRATION_FOR_CALLS);
- return hasVibrator() && (vibrateWhenRingingSetting == DO_VIBRATION_FOR_CALLS);
- }
-
- /**
- * Obtains the value for dialpad/DTMF tones. The default value is true.
- */
- private boolean shouldPlayDtmfTone() {
- int dtmfToneSetting = Settings.System.getInt(getActivity().getContentResolver(),
- Settings.System.DTMF_TONE_WHEN_DIALING,
- PLAY_DTMF_TONE);
- return dtmfToneSetting == PLAY_DTMF_TONE;
- }
-
- /**
- * Whether the device hardware has a vibrator.
- */
- private boolean hasVibrator() {
- Vibrator vibrator = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
- return vibrator != null && vibrator.hasVibrator();
- }
-
- private boolean shouldHideCarrierSettings() {
- CarrierConfigManager configManager = (CarrierConfigManager) getActivity().getSystemService(
- Context.CARRIER_CONFIG_SERVICE);
- return configManager.getConfig().getBoolean(
- CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL);
- }
-}
diff --git a/src/com/android/dialer/util/AppCompatConstants.java b/src/com/android/dialer/util/AppCompatConstants.java
deleted file mode 100644
index 1d52eee1d..000000000
--- a/src/com/android/dialer/util/AppCompatConstants.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.dialer.util;
-
-import android.provider.CallLog.Calls;
-
-public final class AppCompatConstants {
-
- public static final int CALLS_INCOMING_TYPE = Calls.INCOMING_TYPE;
- public static final int CALLS_OUTGOING_TYPE = Calls.OUTGOING_TYPE;
- public static final int CALLS_MISSED_TYPE = Calls.MISSED_TYPE;
- public static final int CALLS_VOICEMAIL_TYPE = Calls.VOICEMAIL_TYPE;
- // Added to android.provider.CallLog.Calls in N+.
- public static final int CALLS_REJECTED_TYPE = 5;
- // Added to android.provider.CallLog.Calls in N+.
- public static final int CALLS_BLOCKED_TYPE = 6;
-}
diff --git a/src/com/android/dialer/util/Assert.java b/src/com/android/dialer/util/Assert.java
deleted file mode 100644
index ec0a6ccb6..000000000
--- a/src/com/android/dialer/util/Assert.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.dialer.util;
-
-import android.os.Looper;
-
-public class Assert {
- public static void assertNotUiThread(String msg) {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- throw new AssertionError(msg);
- }
- }
-
- public static void assertNotNull(Object object, String msg) {
- if (object == null) {
- throw new AssertionError(object);
- }
- }
-
- public static void assertNotNull(Object object) {
- assertNotNull(object, null);
- }
-}
diff --git a/src/com/android/dialer/util/AsyncTaskExecutor.java b/src/com/android/dialer/util/AsyncTaskExecutor.java
deleted file mode 100644
index ca09f0878..000000000
--- a/src/com/android/dialer/util/AsyncTaskExecutor.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.dialer.util;
-
-import android.os.AsyncTask;
-
-import java.util.concurrent.Executor;
-
-/**
- * Interface used to submit {@link AsyncTask} objects to run in the background.
- * <p>
- * This interface has a direct parallel with the {@link Executor} interface. It exists to decouple
- * the mechanics of AsyncTask submission from the description of how that AsyncTask will execute.
- * <p>
- * One immediate benefit of this approach is that testing becomes much easier, since it is easy to
- * introduce a mock or fake AsyncTaskExecutor in unit/integration tests, and thus inspect which
- * tasks have been submitted and control their execution in an orderly manner.
- * <p>
- * Another benefit in due course will be the management of the submitted tasks. An extension to this
- * interface is planned to allow Activities to easily cancel all the submitted tasks that are still
- * pending in the onDestroy() method of the Activity.
- */
-public interface AsyncTaskExecutor {
- /**
- * Executes the given AsyncTask with the default Executor.
- * <p>
- * This method <b>must only be called from the ui thread</b>.
- * <p>
- * The identifier supplied is any Object that can be used to identify the task later. Most
- * commonly this will be an enum which the tests can also refer to. {@code null} is also
- * accepted, though of course this won't help in identifying the task later.
- */
- <T> AsyncTask<T, ?, ?> submit(Object identifier, AsyncTask<T, ?, ?> task, T... params);
-}
diff --git a/src/com/android/dialer/util/AsyncTaskExecutors.java b/src/com/android/dialer/util/AsyncTaskExecutors.java
deleted file mode 100644
index a59af3e41..000000000
--- a/src/com/android/dialer/util/AsyncTaskExecutors.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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.dialer.util;
-
-import android.os.AsyncTask;
-import android.os.Looper;
-
-import com.android.contacts.common.testing.NeededForTesting;
-import com.google.common.base.Preconditions;
-
-import java.util.concurrent.Executor;
-
-/**
- * Factory methods for creating AsyncTaskExecutors.
- * <p>
- * All of the factory methods on this class check first to see if you have set a static
- * {@link AsyncTaskExecutorFactory} set through the
- * {@link #setFactoryForTest(AsyncTaskExecutorFactory)} method, and if so delegate to that instead,
- * which is one way of injecting dependencies for testing classes whose construction cannot be
- * controlled such as {@link android.app.Activity}.
- */
-public final class AsyncTaskExecutors {
- /**
- * A single instance of the {@link AsyncTaskExecutorFactory}, to which we delegate if it is
- * non-null, for injecting when testing.
- */
- private static AsyncTaskExecutorFactory mInjectedAsyncTaskExecutorFactory = null;
-
- /**
- * Creates an AsyncTaskExecutor that submits tasks to run with
- * {@link AsyncTask#SERIAL_EXECUTOR}.
- */
- public static AsyncTaskExecutor createAsyncTaskExecutor() {
- synchronized (AsyncTaskExecutors.class) {
- if (mInjectedAsyncTaskExecutorFactory != null) {
- return mInjectedAsyncTaskExecutorFactory.createAsyncTaskExeuctor();
- }
- return new SimpleAsyncTaskExecutor(AsyncTask.SERIAL_EXECUTOR);
- }
- }
-
- /**
- * Creates an AsyncTaskExecutor that submits tasks to run with
- * {@link AsyncTask#THREAD_POOL_EXECUTOR}.
- */
- public static AsyncTaskExecutor createThreadPoolExecutor() {
- synchronized (AsyncTaskExecutors.class) {
- if (mInjectedAsyncTaskExecutorFactory != null) {
- return mInjectedAsyncTaskExecutorFactory.createAsyncTaskExeuctor();
- }
- return new SimpleAsyncTaskExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
- }
-
- /** Interface for creating AsyncTaskExecutor objects. */
- public interface AsyncTaskExecutorFactory {
- AsyncTaskExecutor createAsyncTaskExeuctor();
- }
-
- @NeededForTesting
- public static void setFactoryForTest(AsyncTaskExecutorFactory factory) {
- synchronized (AsyncTaskExecutors.class) {
- mInjectedAsyncTaskExecutorFactory = factory;
- }
- }
-
- public static void checkCalledFromUiThread() {
- Preconditions.checkState(Thread.currentThread() == Looper.getMainLooper().getThread(),
- "submit method must be called from ui thread, was: " + Thread.currentThread());
- }
-
- private static class SimpleAsyncTaskExecutor implements AsyncTaskExecutor {
- private final Executor mExecutor;
-
- public SimpleAsyncTaskExecutor(Executor executor) {
- mExecutor = executor;
- }
-
- @Override
- public <T> AsyncTask<T, ?, ?> submit(Object identifer, AsyncTask<T, ?, ?> task,
- T... params) {
- checkCalledFromUiThread();
- return task.executeOnExecutor(mExecutor, params);
- }
- }
-}
diff --git a/src/com/android/dialer/util/BlockReportSpamDialogs.java b/src/com/android/dialer/util/BlockReportSpamDialogs.java
deleted file mode 100644
index 45b2f4228..000000000
--- a/src/com/android/dialer/util/BlockReportSpamDialogs.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * 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.dialer.util;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.view.View;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-
-import com.android.dialer.R;
-
-/**
- * Helper class for creating block/report dialog fragments.
- */
-public class BlockReportSpamDialogs {
- public static final String BLOCK_REPORT_SPAM_DIALOG_TAG = "BlockReportSpamDialog";
- public static final String BLOCK_DIALOG_TAG = "BlockDialog";
- public static final String UNBLOCK_DIALOG_TAG = "UnblockDialog";
- public static final String NOT_SPAM_DIALOG_TAG = "NotSpamDialog";
-
- /**
- * Listener passed to block/report spam dialog for positive click in
- * {@link BlockReportSpamDialogFragment}.
- */
- public interface OnSpamDialogClickListener {
- /**
- * Called when user clicks on positive button in block/report spam dialog.
- * @param isSpamChecked Whether the spam checkbox is checked.
- */
- void onClick(boolean isSpamChecked);
- }
-
- /**
- * Listener passed to all dialogs except the block/report spam dialog for positive click.
- */
- public interface OnConfirmListener {
- /**
- * Called when user clicks on positive button in the dialog.
- */
- void onClick();
- }
-
- /**
- * Contains the common attributes between all block/unblock/report dialog fragments.
- */
- private static class CommonDialogsFragment extends DialogFragment {
- /**
- * The number to display in the dialog title.
- */
- protected String mDisplayNumber;
-
- /**
- * Called when dialog positive button is pressed.
- */
- protected OnConfirmListener mPositiveListener;
-
- /**
- * Called when dialog is dismissed.
- */
- @Nullable
- protected DialogInterface.OnDismissListener mDismissListener;
-
- @Override
- public void onDismiss(DialogInterface dialog) {
- if (mDismissListener != null) {
- mDismissListener.onDismiss(dialog);
- }
- super.onDismiss(dialog);
- }
-
- @Override
- public void onPause() {
- // The dialog is dismissed onPause, i.e. rotation.
- dismiss();
- mDismissListener = null;
- mPositiveListener = null;
- mDisplayNumber = null;
- super.onPause();
- }
- }
-
- /**
- * Dialog for block/report spam with the mark as spam checkbox.
- */
- public static class BlockReportSpamDialogFragment extends CommonDialogsFragment {
- /**
- * Called when dialog positive button is pressed.
- */
- private OnSpamDialogClickListener mPositiveListener;
-
- /**
- * Whether the mark as spam checkbox is checked before displaying the dialog.
- */
- private boolean mSpamChecked;
-
- public static DialogFragment newInstance(String displayNumber,
- boolean spamChecked,
- OnSpamDialogClickListener positiveListener,
- @Nullable DialogInterface.OnDismissListener
- dismissListener) {
- BlockReportSpamDialogFragment fragment = new BlockReportSpamDialogFragment();
- fragment.mSpamChecked = spamChecked;
- fragment.mDisplayNumber = displayNumber;
- fragment.mPositiveListener = positiveListener;
- fragment.mDismissListener = dismissListener;
- return fragment;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreateDialog(savedInstanceState);
- View dialogView = View.inflate(getActivity(), R.layout.block_report_spam_dialog, null);
- final CheckBox isSpamCheckbox =
- (CheckBox) dialogView.findViewById(R.id.report_number_as_spam_action);
- // Listen for changes on the checkbox and update if orientation changes
- isSpamCheckbox.setChecked(mSpamChecked);
- isSpamCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- mSpamChecked = isChecked;
- }
- });
-
- AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
- Dialog dialog = alertDialogBuilder
- .setView(dialogView)
- .setTitle(getString(R.string.block_report_number_alert_title, mDisplayNumber))
- .setPositiveButton(R.string.block_number_ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dismiss();
- mPositiveListener.onClick(isSpamCheckbox.isChecked());
- }
- }).create();
- dialog.setCanceledOnTouchOutside(true);
- return dialog;
- }
- }
-
- /**
- * Dialog for blocking a number.
- */
- public static class BlockDialogFragment extends CommonDialogsFragment {
- public static DialogFragment newInstance(String displayNumber,
- OnConfirmListener positiveListener,
- @Nullable DialogInterface.OnDismissListener
- dismissListener) {
- BlockDialogFragment fragment = new BlockDialogFragment();
- fragment.mDisplayNumber = displayNumber;
- fragment.mPositiveListener = positiveListener;
- fragment.mDismissListener = dismissListener;
- return fragment;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreateDialog(savedInstanceState);
- // Return the newly created dialog
- AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
- Dialog dialog = alertDialogBuilder
- .setTitle(getString(R.string.block_report_number_alert_title, mDisplayNumber))
- .setPositiveButton(R.string.block_number_ok,
- createGenericOnClickListener(this, mPositiveListener))
- .create();
- dialog.setCanceledOnTouchOutside(true);
- return dialog;
- }
- }
-
- /**
- * Dialog for unblocking a number.
- */
- public static class UnblockDialogFragment extends CommonDialogsFragment {
- /**
- * Whether or not the number is spam.
- */
- private boolean mIsSpam;
-
- public static DialogFragment newInstance(String displayNumber,
- boolean isSpam,
- OnConfirmListener positiveListener,
- @Nullable DialogInterface.OnDismissListener
- dismissListener) {
- UnblockDialogFragment fragment = new UnblockDialogFragment();
- fragment.mDisplayNumber = displayNumber;
- fragment.mIsSpam = isSpam;
- fragment.mPositiveListener = positiveListener;
- fragment.mDismissListener = dismissListener;
- return fragment;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreateDialog(savedInstanceState);
- // Return the newly created dialog
- AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
- if (mIsSpam) {
- alertDialogBuilder.setMessage(R.string.unblock_number_alert_details);
- }
- Dialog dialog = alertDialogBuilder
- .setTitle(getString(R.string.unblock_report_number_alert_title, mDisplayNumber))
- .setPositiveButton(R.string.unblock_number_ok,
- createGenericOnClickListener(this, mPositiveListener))
- .create();
- dialog.setCanceledOnTouchOutside(true);
- return dialog;
- }
- }
-
- /**
- * Dialog for reporting a number as not spam.
- */
- public static class ReportNotSpamDialogFragment extends CommonDialogsFragment {
- public static DialogFragment newInstance(String displayNumber,
- OnConfirmListener positiveListener,
- @Nullable DialogInterface.OnDismissListener
- dismissListener) {
- ReportNotSpamDialogFragment fragment = new ReportNotSpamDialogFragment();
- fragment.mDisplayNumber = displayNumber;
- fragment.mPositiveListener = positiveListener;
- fragment.mDismissListener = dismissListener;
- return fragment;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreateDialog(savedInstanceState);
- // Return the newly created dialog
- AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
- Dialog dialog = alertDialogBuilder
- .setTitle(getString(R.string.report_not_spam_alert_title, mDisplayNumber))
- .setMessage(R.string.report_not_spam_alert_details)
- .setPositiveButton(R.string.report_not_spam_alert_button,
- createGenericOnClickListener(this, mPositiveListener))
- .create();
- dialog.setCanceledOnTouchOutside(true);
- return dialog;
- }
- }
-
- /**
- * Creates a dialog with the default cancel button listener (dismisses dialog).
- */
- private static AlertDialog.Builder createDialogBuilder(Activity activity,
- final DialogFragment fragment) {
- return new AlertDialog.Builder(activity)
- .setCancelable(true)
- .setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- fragment.dismiss();
- }
- });
- }
-
- /**
- * Creates a generic click listener which dismisses the fragment and then calls the actual
- * listener.
- */
- private static DialogInterface.OnClickListener createGenericOnClickListener(
- final DialogFragment fragment,
- final OnConfirmListener listener) {
- return new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- fragment.dismiss();
- listener.onClick();
- }
- };
- }
-}
diff --git a/src/com/android/dialer/util/DialerUtils.java b/src/com/android/dialer/util/DialerUtils.java
deleted file mode 100644
index 95d6a81b6..000000000
--- a/src/com/android/dialer/util/DialerUtils.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2014 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.dialer.util;
-
-import android.app.Activity;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.Telephony;
-import android.telecom.TelecomManager;
-import android.text.BidiFormatter;
-import android.text.TextDirectionHeuristics;
-import android.text.TextUtils;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.Toast;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.interactions.TouchPointManager;
-import com.android.dialer.R;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * General purpose utility methods for the Dialer.
- */
-public class DialerUtils {
-
- /**
- * Attempts to start an activity and displays a toast with the default error message if the
- * activity is not found, instead of throwing an exception.
- *
- * @param context to start the activity with.
- * @param intent to start the activity with.
- */
- public static void startActivityWithErrorToast(Context context, Intent intent) {
- startActivityWithErrorToast(context, intent, R.string.activity_not_available);
- }
-
- /**
- * Attempts to start an activity and displays a toast with a provided error message if the
- * activity is not found, instead of throwing an exception.
- *
- * @param context to start the activity with.
- * @param intent to start the activity with.
- * @param msgId Resource ID of the string to display in an error message if the activity is
- * not found.
- */
- public static void startActivityWithErrorToast(Context context, Intent intent, int msgId) {
- try {
- if ((IntentUtil.CALL_ACTION.equals(intent.getAction())
- && context instanceof Activity)) {
- // All dialer-initiated calls should pass the touch point to the InCallUI
- Point touchPoint = TouchPointManager.getInstance().getPoint();
- if (touchPoint.x != 0 || touchPoint.y != 0) {
- Bundle extras;
- // Make sure to not accidentally clobber any existing extras
- if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
- extras = intent.getParcelableExtra(
- TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
- } else {
- extras = new Bundle();
- }
- extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint);
- intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
- }
-
- final boolean hasCallPermission = TelecomUtil.placeCall((Activity) context, intent);
- if (!hasCallPermission) {
- // TODO: Make calling activity show request permission dialog and handle
- // callback results appropriately.
- Toast.makeText(context, "Cannot place call without Phone permission",
- Toast.LENGTH_SHORT);
- }
- } else {
- context.startActivity(intent);
- }
- } catch (ActivityNotFoundException e) {
- Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
- }
- }
-
- /**
- * Returns the component name to use in order to send an SMS using the default SMS application,
- * or null if none exists.
- */
- public static ComponentName getSmsComponent(Context context) {
- String smsPackage = Telephony.Sms.getDefaultSmsPackage(context);
- if (smsPackage != null) {
- final PackageManager packageManager = context.getPackageManager();
- final Intent intent = new Intent(Intent.ACTION_SENDTO,
- Uri.fromParts(ContactsUtils.SCHEME_SMSTO, "", null));
- final List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent, 0);
- for (ResolveInfo resolveInfo : resolveInfos) {
- if (smsPackage.equals(resolveInfo.activityInfo.packageName)) {
- return new ComponentName(smsPackage, resolveInfo.activityInfo.name);
- }
- }
- }
- return null;
- }
-
- /**
- * Closes an {@link AutoCloseable}, silently ignoring any checked exceptions. Does nothing if
- * null.
- *
- * @param closeable to close.
- */
- public static void closeQuietly(AutoCloseable closeable) {
- if (closeable != null) {
- try {
- closeable.close();
- } catch (RuntimeException rethrown) {
- throw rethrown;
- } catch (Exception ignored) {
- }
- }
- }
-
- /**
- * Joins a list of {@link CharSequence} into a single {@link CharSequence} seperated by a
- * localized delimiter such as ", ".
- *
- * @param resources Resources used to get list delimiter.
- * @param list List of char sequences to join.
- * @return Joined char sequences.
- */
- public static CharSequence join(Resources resources, Iterable<CharSequence> list) {
- StringBuilder sb = new StringBuilder();
- final BidiFormatter formatter = BidiFormatter.getInstance();
- final CharSequence separator = resources.getString(R.string.list_delimeter);
-
- Iterator<CharSequence> itr = list.iterator();
- boolean firstTime = true;
- while (itr.hasNext()) {
- if (firstTime) {
- firstTime = false;
- } else {
- sb.append(separator);
- }
- // Unicode wrap the elements of the list to respect RTL for individual strings.
- sb.append(formatter.unicodeWrap(
- itr.next().toString(), TextDirectionHeuristics.FIRSTSTRONG_LTR));
- }
-
- // Unicode wrap the joined value, to respect locale's RTL ordering for the whole list.
- return formatter.unicodeWrap(sb.toString());
- }
-
- /**
- * @return True if the application is currently in RTL mode.
- */
- public static boolean isRtl() {
- return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) ==
- View.LAYOUT_DIRECTION_RTL;
- }
-
- public static void showInputMethod(View view) {
- final InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(
- Context.INPUT_METHOD_SERVICE);
- if (imm != null) {
- imm.showSoftInput(view, 0);
- }
- }
-
- public static void hideInputMethod(View view) {
- final InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(
- Context.INPUT_METHOD_SERVICE);
- if (imm != null) {
- imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
- }
- }
-}
diff --git a/src/com/android/dialer/util/EmptyLoader.java b/src/com/android/dialer/util/EmptyLoader.java
deleted file mode 100644
index dd4c0a330..000000000
--- a/src/com/android/dialer/util/EmptyLoader.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.dialer.util;
-
-import android.app.LoaderManager.LoaderCallbacks;
-import android.content.Context;
-import android.content.Loader;
-import android.os.Bundle;
-
-/**
- * A {@link Loader} only used to make use of the {@link android.app.Fragment#setStartDeferred}
- * feature from an old-style fragment which doesn't use {@link Loader}s to load data.
- *
- * This loader never delivers results. A caller fragment must destroy it when deferred fragments
- * should be started.
- */
-public class EmptyLoader extends Loader<Object> {
- public EmptyLoader(Context context) {
- super(context);
- }
-
- /**
- * {@link LoaderCallbacks} which just generates {@link EmptyLoader}. {@link #onLoadFinished}
- * and {@link #onLoaderReset} are no-op.
- */
- public static class Callback implements LoaderCallbacks<Object> {
- private final Context mContext;
-
- public Callback(Context context) {
- mContext = context.getApplicationContext();
- }
-
- @Override
- public Loader<Object> onCreateLoader(int id, Bundle args) {
- return new EmptyLoader(mContext);
- }
-
- @Override
- public void onLoadFinished(Loader<Object> loader, Object data) {
- }
-
- @Override
- public void onLoaderReset(Loader<Object> loader) {
- }
- }
-}
diff --git a/src/com/android/dialer/util/ExpirableCache.java b/src/com/android/dialer/util/ExpirableCache.java
deleted file mode 100644
index 00ebd1607..000000000
--- a/src/com/android/dialer/util/ExpirableCache.java
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * 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.dialer.util;
-
-import android.util.LruCache;
-
-import com.android.contacts.common.testing.NeededForTesting;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-import javax.annotation.concurrent.Immutable;
-import javax.annotation.concurrent.ThreadSafe;
-
-/**
- * An LRU cache in which all items can be marked as expired at a given time and it is possible to
- * query whether a particular cached value is expired or not.
- * <p>
- * A typical use case for this is caching of values which are expensive to compute but which are
- * still useful when out of date.
- * <p>
- * Consider a cache for contact information:
- * <pre>{@code
- * private ExpirableCache<String, Contact> mContactCache;}</pre>
- * which stores the contact information for a given phone number.
- * <p>
- * When we need to store contact information for a given phone number, we can look up the info in
- * the cache:
- * <pre>{@code
- * CachedValue<Contact> cachedContact = mContactCache.getCachedValue(phoneNumber);
- * }</pre>
- * We might also want to fetch the contact information again if the item is expired.
- * <pre>
- * if (cachedContact.isExpired()) {
- * fetchContactForNumber(phoneNumber,
- * new FetchListener() {
- * &#64;Override
- * public void onFetched(Contact contact) {
- * mContactCache.put(phoneNumber, contact);
- * }
- * });
- * }</pre>
- * and insert it back into the cache when the fetch completes.
- * <p>
- * At a certain point we want to expire the content of the cache because we know the content may
- * no longer be up-to-date, for instance, when resuming the activity this is shown into:
- * <pre>
- * &#64;Override
- * protected onResume() {
- * // We were paused for some time, the cached value might no longer be up to date.
- * mContactCache.expireAll();
- * super.onResume();
- * }
- * </pre>
- * The values will be still available from the cache, but they will be expired.
- * <p>
- * If interested only in the value itself, not whether it is expired or not, one should use the
- * {@link #getPossiblyExpired(Object)} method. If interested only in non-expired values, one should
- * use the {@link #get(Object)} method instead.
- * <p>
- * This class wraps around an {@link LruCache} instance: it follows the {@link LruCache} behavior
- * for evicting items when the cache is full. It is possible to supply your own subclass of LruCache
- * by using the {@link #create(LruCache)} method, which can define a custom expiration policy.
- * Since the underlying cache maps keys to cached values it can determine which items are expired
- * and which are not, allowing for an implementation that evicts expired items before non expired
- * ones.
- * <p>
- * This class is thread-safe.
- *
- * @param <K> the type of the keys
- * @param <V> the type of the values
- */
-@ThreadSafe
-public class ExpirableCache<K, V> {
- /**
- * A cached value stored inside the cache.
- * <p>
- * It provides access to the value stored in the cache but also allows to check whether the
- * value is expired.
- *
- * @param <V> the type of value stored in the cache
- */
- public interface CachedValue<V> {
- /** Returns the value stored in the cache for a given key. */
- public V getValue();
-
- /**
- * Checks whether the value, while still being present in the cache, is expired.
- *
- * @return true if the value is expired
- */
- public boolean isExpired();
- }
-
- /**
- * Cached values storing the generation at which they were added.
- */
- @Immutable
- private static class GenerationalCachedValue<V> implements ExpirableCache.CachedValue<V> {
- /** The value stored in the cache. */
- public final V mValue;
- /** The generation at which the value was added to the cache. */
- private final int mGeneration;
- /** The atomic integer storing the current generation of the cache it belongs to. */
- private final AtomicInteger mCacheGeneration;
-
- /**
- * @param cacheGeneration the atomic integer storing the generation of the cache in which
- * this value will be stored
- */
- public GenerationalCachedValue(V value, AtomicInteger cacheGeneration) {
- mValue = value;
- mCacheGeneration = cacheGeneration;
- // Snapshot the current generation.
- mGeneration = mCacheGeneration.get();
- }
-
- @Override
- public V getValue() {
- return mValue;
- }
-
- @Override
- public boolean isExpired() {
- return mGeneration != mCacheGeneration.get();
- }
- }
-
- /** The underlying cache used to stored the cached values. */
- private LruCache<K, CachedValue<V>> mCache;
-
- /**
- * The current generation of items added to the cache.
- * <p>
- * Items in the cache can belong to a previous generation, but in that case they would be
- * expired.
- *
- * @see ExpirableCache.CachedValue#isExpired()
- */
- private final AtomicInteger mGeneration;
-
- private ExpirableCache(LruCache<K, CachedValue<V>> cache) {
- mCache = cache;
- mGeneration = new AtomicInteger(0);
- }
-
- /**
- * Returns the cached value for the given key, or null if no value exists.
- * <p>
- * The cached value gives access both to the value associated with the key and whether it is
- * expired or not.
- * <p>
- * If not interested in whether the value is expired, use {@link #getPossiblyExpired(Object)}
- * instead.
- * <p>
- * If only wants values that are not expired, use {@link #get(Object)} instead.
- *
- * @param key the key to look up
- */
- public CachedValue<V> getCachedValue(K key) {
- return mCache.get(key);
- }
-
- /**
- * Returns the value for the given key, or null if no value exists.
- * <p>
- * When using this method, it is not possible to determine whether the value is expired or not.
- * Use {@link #getCachedValue(Object)} to achieve that instead. However, if using
- * {@link #getCachedValue(Object)} to determine if an item is expired, one should use the item
- * within the {@link CachedValue} and not call {@link #getPossiblyExpired(Object)} to get the
- * value afterwards, since that is not guaranteed to return the same value or that the newly
- * returned value is in the same state.
- *
- * @param key the key to look up
- */
- public V getPossiblyExpired(K key) {
- CachedValue<V> cachedValue = getCachedValue(key);
- return cachedValue == null ? null : cachedValue.getValue();
- }
-
- /**
- * Returns the value for the given key only if it is not expired, or null if no value exists or
- * is expired.
- * <p>
- * This method will return null if either there is no value associated with this key or if the
- * associated value is expired.
- *
- * @param key the key to look up
- */
- @NeededForTesting
- public V get(K key) {
- CachedValue<V> cachedValue = getCachedValue(key);
- return cachedValue == null || cachedValue.isExpired() ? null : cachedValue.getValue();
- }
-
- /**
- * Puts an item in the cache.
- * <p>
- * Newly added item will not be expired until {@link #expireAll()} is next called.
- *
- * @param key the key to look up
- * @param value the value to associate with the key
- */
- public void put(K key, V value) {
- mCache.put(key, newCachedValue(value));
- }
-
- /**
- * Mark all items currently in the cache as expired.
- * <p>
- * Newly added items after this call will be marked as not expired.
- * <p>
- * Expiring the items in the cache does not imply they will be evicted.
- */
- public void expireAll() {
- mGeneration.incrementAndGet();
- }
-
- /**
- * Creates a new {@link CachedValue} instance to be stored in this cache.
- * <p>
- * Implementation of {@link LruCache#create(K)} can use this method to create a new entry.
- */
- public CachedValue<V> newCachedValue(V value) {
- return new GenerationalCachedValue<V>(value, mGeneration);
- }
-
- /**
- * Creates a new {@link ExpirableCache} that wraps the given {@link LruCache}.
- * <p>
- * The created cache takes ownership of the cache passed in as an argument.
- *
- * @param <K> the type of the keys
- * @param <V> the type of the values
- * @param cache the cache to store the value in
- * @return the newly created expirable cache
- * @throws IllegalArgumentException if the cache is not empty
- */
- public static <K, V> ExpirableCache<K, V> create(LruCache<K, CachedValue<V>> cache) {
- return new ExpirableCache<K, V>(cache);
- }
-
- /**
- * Creates a new {@link ExpirableCache} with the given maximum size.
- *
- * @param <K> the type of the keys
- * @param <V> the type of the values
- * @return the newly created expirable cache
- */
- public static <K, V> ExpirableCache<K, V> create(int maxSize) {
- return create(new LruCache<K, CachedValue<V>>(maxSize));
- }
-}
diff --git a/src/com/android/dialer/util/IntentUtil.java b/src/com/android/dialer/util/IntentUtil.java
deleted file mode 100644
index 581e10da4..000000000
--- a/src/com/android/dialer/util/IntentUtil.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * 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.dialer.util;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.provider.ContactsContract;
-import android.telecom.PhoneAccountHandle;
-import android.telecom.TelecomManager;
-import android.telecom.VideoProfile;
-
-import com.android.contacts.common.CallUtil;
-
-/**
- * Utilities for creation of intents in Dialer, such as {@link Intent#ACTION_CALL}.
- */
-public class IntentUtil {
-
- public static final String CALL_ACTION = Intent.ACTION_CALL;
- private static final String SMS_URI_PREFIX = "sms:";
- private static final int NO_PHONE_TYPE = -1;
-
- public static final String EXTRA_CALL_INITIATION_TYPE
- = "com.android.dialer.EXTRA_CALL_INITIATION_TYPE";
- public static final String EXTRA_CALL_CREATED_TIME_MILLIS =
- "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
-
- public static class CallIntentBuilder {
- private Uri mUri;
- private int mCallInitiationType;
- private PhoneAccountHandle mPhoneAccountHandle;
- private boolean mIsVideoCall = false;
-
- public CallIntentBuilder(Uri uri) {
- mUri = uri;
- }
-
- public CallIntentBuilder(String number) {
- this(CallUtil.getCallUri(number));
- }
-
- public CallIntentBuilder setCallInitiationType(int initiationType) {
- mCallInitiationType = initiationType;
- return this;
- }
-
- public CallIntentBuilder setPhoneAccountHandle(PhoneAccountHandle accountHandle) {
- mPhoneAccountHandle = accountHandle;
- return this;
- }
-
- public CallIntentBuilder setIsVideoCall(boolean isVideoCall) {
- mIsVideoCall = isVideoCall;
- return this;
- }
-
- public Intent build() {
- return getCallIntent(
- mUri,
- mPhoneAccountHandle,
- mIsVideoCall ? VideoProfile.STATE_BIDIRECTIONAL : VideoProfile.STATE_AUDIO_ONLY,
- mCallInitiationType);
- }
- }
-
- /**
- * Create a call intent that can be used to place a call.
- *
- * @param uri Address to place the call to.
- * @param accountHandle {@link PhoneAccountHandle} to place the call with.
- * @param videoState Initial video state of the call.
- * @param callIntiationType The UI affordance the call was initiated by.
- * @return Call intent with provided extras and data.
- */
- public static Intent getCallIntent(
- Uri uri, PhoneAccountHandle accountHandle, int videoState, int callIntiationType) {
- final Intent intent = new Intent(CALL_ACTION, uri);
- intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
-
- final Bundle b = new Bundle();
- b.putLong(EXTRA_CALL_CREATED_TIME_MILLIS, SystemClock.elapsedRealtime());
- b.putInt(EXTRA_CALL_INITIATION_TYPE, callIntiationType);
- intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, b);
-
- if (accountHandle != null) {
- intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
- }
-
- return intent;
- }
-
- public static Intent getSendSmsIntent(CharSequence phoneNumber) {
- return new Intent(Intent.ACTION_SENDTO, Uri.parse(SMS_URI_PREFIX + phoneNumber));
- }
-
- public static Intent getNewContactIntent() {
- return new Intent(Intent.ACTION_INSERT, ContactsContract.Contacts.CONTENT_URI);
- }
-
- public static Intent getNewContactIntent(CharSequence phoneNumber) {
- return getNewContactIntent(
- null /* name */,
- phoneNumber /* phoneNumber */,
- NO_PHONE_TYPE);
- }
-
- public static Intent getNewContactIntent(
- CharSequence name, CharSequence phoneNumber, int phoneNumberType) {
- Intent intent = getNewContactIntent();
- populateContactIntent(intent, name, phoneNumber, phoneNumberType);
- return intent;
- }
-
- public static Intent getAddToExistingContactIntent() {
- Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
- intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
- return intent;
- }
-
- public static Intent getAddToExistingContactIntent(CharSequence phoneNumber) {
- return getAddToExistingContactIntent(
- null /* name */,
- phoneNumber /* phoneNumber */,
- NO_PHONE_TYPE);
- }
-
- public static Intent getAddToExistingContactIntent(
- CharSequence name, CharSequence phoneNumber, int phoneNumberType) {
- Intent intent = getAddToExistingContactIntent();
- populateContactIntent(intent, name, phoneNumber, phoneNumberType);
- return intent;
- }
-
- private static void populateContactIntent(
- Intent intent, CharSequence name, CharSequence phoneNumber, int phoneNumberType) {
- if (phoneNumber != null) {
- intent.putExtra(ContactsContract.Intents.Insert.PHONE, phoneNumber);
- }
- if (name != null) {
- intent.putExtra(ContactsContract.Intents.Insert.NAME, name);
- }
- if (phoneNumberType != NO_PHONE_TYPE) {
- intent.putExtra(ContactsContract.Intents.Insert.PHONE_TYPE, phoneNumberType);
- }
- }
-}
diff --git a/src/com/android/dialer/util/MoreStrings.java b/src/com/android/dialer/util/MoreStrings.java
deleted file mode 100644
index 68956f25c..000000000
--- a/src/com/android/dialer/util/MoreStrings.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.util;
-
-/**
- * Static utility methods for Strings.
- */
-public class MoreStrings {
-
- public static String toSafeString(String value) {
- if (value == null) {
- return null;
- }
-
- // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare
- // sanitized phone numbers.
- final StringBuilder builder = new StringBuilder();
- for (int i = 0; i < value.length(); i++) {
- final char c = value.charAt(i);
- if (c == '-' || c == '@' || c == '.') {
- builder.append(c);
- } else {
- builder.append('x');
- }
- }
- return builder.toString();
- }
-
-}
diff --git a/src/com/android/dialer/util/OrientationUtil.java b/src/com/android/dialer/util/OrientationUtil.java
deleted file mode 100644
index 2eb2af3ff..000000000
--- a/src/com/android/dialer/util/OrientationUtil.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.dialer.util;
-
-import android.content.Context;
-import android.content.res.Configuration;
-
-/**
- * Static methods related to device orientation.
- */
-public class OrientationUtil {
-
- /**
- * @return if the context is in landscape orientation.
- */
- public static boolean isLandscape(Context context) {
- return context.getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
- }
-}
diff --git a/src/com/android/dialer/util/PhoneLookupUtil.java b/src/com/android/dialer/util/PhoneLookupUtil.java
deleted file mode 100644
index 1a7239642..000000000
--- a/src/com/android/dialer/util/PhoneLookupUtil.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.dialer.util;
-
-import android.net.Uri;
-import android.provider.ContactsContract;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.PhoneLookupSdkCompat;
-
-public final class PhoneLookupUtil {
- /**
- * @return the column name that stores contact id for phone lookup query.
- */
- public static String getContactIdColumnNameForUri(Uri phoneLookupUri) {
- if (CompatUtils.isNCompatible()) {
- return PhoneLookupSdkCompat.CONTACT_ID;
- }
- // In pre-N, contact id is stored in {@link PhoneLookup#_ID} in non-sip query.
- boolean isSip = phoneLookupUri.getBooleanQueryParameter(
- ContactsContract.PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, false);
- return (isSip) ? PhoneLookupSdkCompat.CONTACT_ID : ContactsContract.PhoneLookup._ID;
- }
-
- private PhoneLookupUtil() {}
-}
diff --git a/src/com/android/dialer/util/PhoneNumberUtil.java b/src/com/android/dialer/util/PhoneNumberUtil.java
deleted file mode 100644
index 33f987359..000000000
--- a/src/com/android/dialer/util/PhoneNumberUtil.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.util;
-
-import android.content.Context;
-import android.provider.CallLog;
-import android.telecom.PhoneAccountHandle;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.contacts.common.util.PhoneNumberHelper;
-import com.android.contacts.common.util.TelephonyManagerUtils;
-import com.google.common.collect.Sets;
-import com.google.i18n.phonenumbers.NumberParseException;
-import com.google.i18n.phonenumbers.Phonenumber;
-import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
-
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-public class PhoneNumberUtil {
- private static final String TAG = "PhoneNumberUtil";
- private static final Set<String> LEGACY_UNKNOWN_NUMBERS = Sets.newHashSet("-1", "-2", "-3");
-
- /** Returns true if it is possible to place a call to the given number. */
- public static boolean canPlaceCallsTo(CharSequence number, int presentation) {
- return presentation == CallLog.Calls.PRESENTATION_ALLOWED
- && !TextUtils.isEmpty(number) && !isLegacyUnknownNumbers(number);
- }
-
- /**
- * Returns true if the given number is the number of the configured voicemail. To be able to
- * mock-out this, it is not a static method.
- */
- public static boolean isVoicemailNumber(
- Context context, PhoneAccountHandle accountHandle, CharSequence number) {
- if (TextUtils.isEmpty(number)) {
- return false;
- }
- return TelecomUtil.isVoicemailNumber(context, accountHandle, number.toString());
- }
-
- /**
- * Returns true if the given number is a SIP address. To be able to mock-out this, it is not a
- * static method.
- */
- public static boolean isSipNumber(CharSequence number) {
- return number != null && PhoneNumberHelper.isUriNumber(number.toString());
- }
-
- public static boolean isUnknownNumberThatCanBeLookedUp(
- Context context,
- PhoneAccountHandle accountHandle,
- CharSequence number,
- int presentation) {
- if (presentation == CallLog.Calls.PRESENTATION_UNKNOWN) {
- return false;
- }
- if (presentation == CallLog.Calls.PRESENTATION_RESTRICTED) {
- return false;
- }
- if (presentation == CallLog.Calls.PRESENTATION_PAYPHONE) {
- return false;
- }
- if (TextUtils.isEmpty(number)) {
- return false;
- }
- if (isVoicemailNumber(context, accountHandle, number)) {
- return false;
- }
- if (isLegacyUnknownNumbers(number)) {
- return false;
- }
- return true;
- }
-
- public static boolean isLegacyUnknownNumbers(CharSequence number) {
- return number != null && LEGACY_UNKNOWN_NUMBERS.contains(number.toString());
- }
-
- /**
- * @return a geographical description string for the specified number.
- * @see com.android.i18n.phonenumbers.PhoneNumberOfflineGeocoder
- */
- public static String getGeoDescription(Context context, String number) {
- Log.v(TAG, "getGeoDescription('" + pii(number) + "')...");
-
- if (TextUtils.isEmpty(number)) {
- return null;
- }
-
- com.google.i18n.phonenumbers.PhoneNumberUtil util =
- com.google.i18n.phonenumbers.PhoneNumberUtil.getInstance();
- PhoneNumberOfflineGeocoder geocoder = PhoneNumberOfflineGeocoder.getInstance();
-
- Locale locale = context.getResources().getConfiguration().locale;
- String countryIso = TelephonyManagerUtils.getCurrentCountryIso(context, locale);
- Phonenumber.PhoneNumber pn = null;
- try {
- Log.v(TAG, "parsing '" + pii(number)
- + "' for countryIso '" + countryIso + "'...");
- pn = util.parse(number, countryIso);
- Log.v(TAG, "- parsed number: " + pii(pn));
- } catch (NumberParseException e) {
- Log.v(TAG, "getGeoDescription: NumberParseException for incoming number '" +
- pii(number) + "'");
- }
-
- if (pn != null) {
- String description = geocoder.getDescriptionForNumber(pn, locale);
- Log.v(TAG, "- got description: '" + description + "'");
- return description;
- }
-
- return null;
- }
-
- private static String pii(Object pii) {
- return com.android.incallui.Log.pii(pii);
- }
-}
diff --git a/src/com/android/dialer/util/TelecomUtil.java b/src/com/android/dialer/util/TelecomUtil.java
deleted file mode 100644
index 69c7334b9..000000000
--- a/src/com/android/dialer/util/TelecomUtil.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * 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.dialer.util;
-
-import android.Manifest;
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.net.Uri;
-import android.provider.CallLog.Calls;
-import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
-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 android.util.Log;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
-import com.android.dialer.compat.DialerCompatUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Performs permission checks before calling into TelecomManager. Each method is self-explanatory -
- * perform the required check and return the fallback default if the permission is missing,
- * otherwise return the value from TelecomManager.
- *
- */
-public class TelecomUtil {
- private static final String TAG = "TelecomUtil";
- private static boolean sWarningLogged = false;
-
- public static void showInCallScreen(Context context, boolean showDialpad) {
- if (hasReadPhoneStatePermission(context)) {
- try {
- getTelecomManager(context).showInCallScreen(showDialpad);
- } catch (SecurityException e) {
- // Just in case
- Log.w(TAG, "TelecomManager.showInCallScreen called without permission.");
- }
- }
- }
-
- public static void silenceRinger(Context context) {
- if (hasModifyPhoneStatePermission(context)) {
- try {
- TelecomManagerCompat.silenceRinger(getTelecomManager(context));
- } catch (SecurityException e) {
- // Just in case
- Log.w(TAG, "TelecomManager.silenceRinger called without permission.");
- }
- }
- }
-
- public static void cancelMissedCallsNotification(Context context) {
- if (hasModifyPhoneStatePermission(context)) {
- try {
- getTelecomManager(context).cancelMissedCallsNotification();
- } catch (SecurityException e) {
- Log.w(TAG, "TelecomManager.cancelMissedCalls called without permission.");
- }
- }
- }
-
- public static Uri getAdnUriForPhoneAccount(Context context, PhoneAccountHandle handle) {
- if (hasModifyPhoneStatePermission(context)) {
- try {
- return TelecomManagerCompat.getAdnUriForPhoneAccount(
- getTelecomManager(context), handle);
- } catch (SecurityException e) {
- Log.w(TAG, "TelecomManager.getAdnUriForPhoneAccount called without permission.");
- }
- }
- return null;
- }
-
- public static boolean handleMmi(Context context, String dialString,
- @Nullable PhoneAccountHandle handle) {
- if (hasModifyPhoneStatePermission(context)) {
- try {
- if (handle == null) {
- return getTelecomManager(context).handleMmi(dialString);
- } else {
- return getTelecomManager(context).handleMmi(dialString, handle);
- }
- } catch (SecurityException e) {
- Log.w(TAG, "TelecomManager.handleMmi called without permission.");
- }
- }
- return false;
- }
-
- @Nullable
- public static PhoneAccountHandle getDefaultOutgoingPhoneAccount(Context context,
- String uriScheme) {
- if (hasReadPhoneStatePermission(context)) {
- return TelecomManagerCompat.getDefaultOutgoingPhoneAccount(
- getTelecomManager(context), uriScheme);
- }
- return null;
- }
-
- public static PhoneAccount getPhoneAccount(Context context, PhoneAccountHandle handle) {
- return TelecomManagerCompat.getPhoneAccount(getTelecomManager(context), handle);
- }
-
- public static List<PhoneAccountHandle> getCallCapablePhoneAccounts(Context context) {
- if (hasReadPhoneStatePermission(context)) {
- return TelecomManagerCompat.getCallCapablePhoneAccounts(getTelecomManager(context));
- }
- return new ArrayList<>();
- }
-
- public static boolean isInCall(Context context) {
- if (hasReadPhoneStatePermission(context)) {
- return getTelecomManager(context).isInCall();
- }
- return false;
- }
-
- public static boolean isVoicemailNumber(Context context, PhoneAccountHandle accountHandle,
- String number) {
- if (hasReadPhoneStatePermission(context)) {
- return TelecomManagerCompat.isVoiceMailNumber(getTelecomManager(context),
- accountHandle, number);
- }
- return false;
- }
-
- @Nullable
- public static String getVoicemailNumber(Context context, PhoneAccountHandle accountHandle) {
- if (hasReadPhoneStatePermission(context)) {
- return TelecomManagerCompat.getVoiceMailNumber(getTelecomManager(context),
- getTelephonyManager(context), accountHandle);
- }
- return null;
- }
-
- /**
- * Tries to place a call using the {@link TelecomManager}.
- *
- * @param activity a valid activity.
- * @param intent the call intent.
- *
- * @return {@code true} if we successfully attempted to place the call, {@code false} if it
- * failed due to a permission check.
- */
- public static boolean placeCall(Activity activity, Intent intent) {
- if (hasCallPhonePermission(activity)) {
- TelecomManagerCompat.placeCall(activity, getTelecomManager(activity), intent);
- return true;
- }
- return false;
- }
-
- public static Uri getCallLogUri(Context context) {
- return hasReadWriteVoicemailPermissions(context) ? Calls.CONTENT_URI_WITH_VOICEMAIL
- : Calls.CONTENT_URI;
- }
-
- public static boolean hasReadWriteVoicemailPermissions(Context context) {
- return isDefaultDialer(context)
- || (hasPermission(context, Manifest.permission.READ_VOICEMAIL)
- && hasPermission(context, Manifest.permission.WRITE_VOICEMAIL));
- }
-
- public static boolean hasModifyPhoneStatePermission(Context context) {
- return isDefaultDialer(context)
- || hasPermission(context, Manifest.permission.MODIFY_PHONE_STATE);
- }
-
- public static boolean hasReadPhoneStatePermission(Context context) {
- return isDefaultDialer(context)
- || hasPermission(context, Manifest.permission.READ_PHONE_STATE);
- }
-
- public static boolean hasCallPhonePermission(Context context) {
- return isDefaultDialer(context)
- || hasPermission(context, Manifest.permission.CALL_PHONE);
- }
-
- private static boolean hasPermission(Context context, String permission) {
- return ContextCompat.checkSelfPermission(context, permission)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- public static boolean isDefaultDialer(Context context) {
- final boolean result = TextUtils.equals(context.getPackageName(),
- TelecomManagerCompat.getDefaultDialerPackage(getTelecomManager(context)));
- if (result) {
- sWarningLogged = false;
- } else {
- if (!sWarningLogged) {
- // Log only once to prevent spam.
- Log.w(TAG, "Dialer is not currently set to be default dialer");
- sWarningLogged = true;
- }
- }
- return result;
- }
-
- private static TelecomManager getTelecomManager(Context context) {
- return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
- }
-
- private static TelephonyManager getTelephonyManager(Context context) {
- return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- }
-}
diff --git a/src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java b/src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java
deleted file mode 100644
index 80a0368bd..000000000
--- a/src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package com.android.dialer.voicemail;
-
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.preference.PreferenceManager;
-import android.support.annotation.Nullable;
-
-import com.android.dialer.calllog.CallLogQueryHandler;
-
-/**
- * Helper class to check whether visual voicemail is enabled.
- *
- * Call isVisualVoicemailEnabled() to retrieve the result.
- *
- * The result is cached and saved in a SharedPreferences, stored as a boolean in
- * PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER. Every time a new instance is created, it will try to
- * restore the cached result from the SharedPreferences.
- *
- * Call asyncUpdate() to make a CallLogQuery to check the actual status. This is a async call so
- * isVisualVoicemailEnabled() will not be affected immediately.
- *
- * If the status has changed as a result of asyncUpdate(),
- * Callback.onVisualVoicemailEnabledStatusChanged() will be called with the new value.
- */
-public class VisualVoicemailEnabledChecker implements CallLogQueryHandler.Listener {
-
- public static final String PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER =
- "has_active_voicemail_provider";
- private SharedPreferences mPrefs;
- private boolean mHasActiveVoicemailProvider;
- private CallLogQueryHandler mCallLogQueryHandler;
- private VoicemailStatusHelper mVoicemailStatusHelper;
- private Context mContext;
-
- public interface Callback {
-
- /**
- * Callback to notify enabled status has changed to the @param newValue
- */
- void onVisualVoicemailEnabledStatusChanged(boolean newValue);
- }
-
- private Callback mCallback;
-
- public VisualVoicemailEnabledChecker(Context context, @Nullable Callback callback) {
- mContext = context;
- mCallback = callback;
- mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
- mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
- mHasActiveVoicemailProvider = mPrefs.getBoolean(PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER,
- false);
- }
-
- /**
- * @return whether visual voicemail is enabled. Result is cached, call asyncUpdate() to
- * update the result.
- */
- public boolean isVisualVoicemailEnabled() {
- return mHasActiveVoicemailProvider;
- }
-
- /**
- * Perform an async query into the system to check the status of visual voicemail.
- * If the status has changed, Callback.onVisualVoicemailEnabledStatusChanged() will be called.
- */
- public void asyncUpdate() {
- mCallLogQueryHandler =
- new CallLogQueryHandler(mContext, mContext.getContentResolver(), this);
- mCallLogQueryHandler.fetchVoicemailStatus();
- }
-
- @Override
- public void onVoicemailStatusFetched(Cursor statusCursor) {
- boolean hasActiveVoicemailProvider =
- mVoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor) > 0;
- if (hasActiveVoicemailProvider != mHasActiveVoicemailProvider) {
- mHasActiveVoicemailProvider = hasActiveVoicemailProvider;
- mPrefs.edit().putBoolean(PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER,
- mHasActiveVoicemailProvider);
- if (mCallback != null) {
- mCallback.onVisualVoicemailEnabledStatusChanged(mHasActiveVoicemailProvider);
- }
- }
- }
-
- @Override
- public void onVoicemailUnreadCountFetched(Cursor cursor) {
- // Do nothing
- }
-
- @Override
- public void onMissedCallsUnreadCountFetched(Cursor cursor) {
- // Do nothing
- }
-
- @Override
- public boolean onCallsFetched(Cursor combinedCursor) {
- // Do nothing
- return false;
- }
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailArchiveActivity.java b/src/com/android/dialer/voicemail/VoicemailArchiveActivity.java
deleted file mode 100644
index 16b947cd3..000000000
--- a/src/com/android/dialer/voicemail/VoicemailArchiveActivity.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * 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.dialer.voicemail;
-
-import android.content.Intent;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.support.v7.app.ActionBar;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.view.MenuItem;
-import android.view.View;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.R;
-import com.android.dialer.TransactionSafeActivity;
-import com.android.dialer.calllog.CallLogAdapter;
-import com.android.dialer.calllog.CallLogQueryHandler;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.widget.EmptyContentView;
-import com.android.dialerbind.ObjectFactory;
-
-/**
- * This activity manages all the voicemails archived by the user.
- */
-public class VoicemailArchiveActivity extends TransactionSafeActivity
- implements CallLogAdapter.CallFetcher, CallLogQueryHandler.Listener {
- private RecyclerView mRecyclerView;
- private LinearLayoutManager mLayoutManager;
- private EmptyContentView mEmptyListView;
- private CallLogAdapter mAdapter;
- private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
- private CallLogQueryHandler mCallLogQueryHandler;
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (!isSafeToCommitTransactions()) {
- return true;
- }
-
- switch (item.getItemId()) {
- case android.R.id.home:
- Intent intent = new Intent(this, DialtactsActivity.class);
- // Clears any activities between VoicemailArchiveActivity and DialtactsActivity
- // on the activity stack and reuses the existing instance of DialtactsActivity
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.call_log_fragment);
-
- // Make window opaque to reduce overdraw
- getWindow().setBackgroundDrawable(null);
-
- ActionBar actionBar = getSupportActionBar();
- actionBar.setDisplayShowHomeEnabled(true);
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowTitleEnabled(true);
- actionBar.setElevation(0);
-
- mCallLogQueryHandler = new CallLogQueryHandler(this, getContentResolver(), this);
- mVoicemailPlaybackPresenter = VoicemailArchivePlaybackPresenter
- .getInstance(this, savedInstanceState);
-
- mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
- mRecyclerView.setHasFixedSize(true);
- mLayoutManager = new LinearLayoutManager(this);
- mRecyclerView.setLayoutManager(mLayoutManager);
- mEmptyListView = (EmptyContentView) findViewById(R.id.empty_list_view);
- mEmptyListView.setDescription(R.string.voicemail_archive_empty);
- mEmptyListView.setImage(R.drawable.empty_call_log);
-
- mAdapter = ObjectFactory.newCallLogAdapter(
- this,
- this,
- new ContactInfoHelper(this, GeoUtil.getCurrentCountryIso(this)),
- mVoicemailPlaybackPresenter,
- CallLogAdapter.ACTIVITY_TYPE_ARCHIVE);
- mRecyclerView.setAdapter(mAdapter);
- fetchCalls();
- }
-
- @Override
- protected void onPause() {
- mVoicemailPlaybackPresenter.onPause();
- mAdapter.onPause();
- super.onPause();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mAdapter.onResume();
- mVoicemailPlaybackPresenter.onResume();
- }
-
- @Override
- public void onDestroy() {
- mVoicemailPlaybackPresenter.onDestroy();
- mAdapter.changeCursor(null);
- super.onDestroy();
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mVoicemailPlaybackPresenter.onSaveInstanceState(outState);
- }
-
- @Override
- public void fetchCalls() {
- mCallLogQueryHandler.fetchVoicemailArchive();
- }
-
- @Override
- public void onVoicemailStatusFetched(Cursor statusCursor) {
- // Do nothing
- }
-
- @Override
- public void onVoicemailUnreadCountFetched(Cursor cursor) {
- // Do nothing
- }
-
- @Override
- public void onMissedCallsUnreadCountFetched(Cursor cursor) {
- // Do nothing
- }
-
- @Override
- public boolean onCallsFetched(Cursor cursor) {
- mAdapter.changeCursorVoicemail(cursor);
- boolean showListView = cursor != null && cursor.getCount() > 0;
- mRecyclerView.setVisibility(showListView ? View.VISIBLE : View.GONE);
- mEmptyListView.setVisibility(!showListView ? View.VISIBLE : View.GONE);
- return true;
- }
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailArchivePlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailArchivePlaybackPresenter.java
deleted file mode 100644
index 5f73d1689..000000000
--- a/src/com/android/dialer/voicemail/VoicemailArchivePlaybackPresenter.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.dialer.voicemail;
-
-import android.app.Activity;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.util.Log;
-import com.android.dialer.calllog.CallLogAsyncTaskUtil;
-import com.android.dialer.database.VoicemailArchiveContract;
-import java.io.FileNotFoundException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Similar to the {@link VoicemailPlaybackPresenter}, but for the archive voicemail tab. It checks
- * whether the voicemail file exists locally before preparing it.
- */
-public class VoicemailArchivePlaybackPresenter extends VoicemailPlaybackPresenter {
- private static final String TAG = "VMPlaybackPresenter";
- private static VoicemailPlaybackPresenter sInstance;
-
- public VoicemailArchivePlaybackPresenter(Activity activity) {
- super(activity);
- }
-
- public static VoicemailPlaybackPresenter getInstance(
- Activity activity, Bundle savedInstanceState) {
- if (sInstance == null) {
- sInstance = new VoicemailArchivePlaybackPresenter(activity);
- }
-
- sInstance.init(activity, savedInstanceState);
- return sInstance;
- }
-
- @Override
- protected void checkForContent(final OnContentCheckedListener callback) {
- mAsyncTaskExecutor.submit(Tasks.CHECK_FOR_CONTENT, new AsyncTask<Void, Void, Boolean>() {
- @Override
- public Boolean doInBackground(Void... params) {
- try {
- // Check if the _data column of the archived voicemail is valid
- if (mVoicemailUri != null) {
- mContext.getContentResolver().openInputStream(mVoicemailUri);
- return true;
- }
- } catch (FileNotFoundException e) {
- Log.d(TAG, "Voicemail file not found for " + mVoicemailUri);
- }
- return false;
- }
-
- @Override
- public void onPostExecute(Boolean hasContent) {
- callback.onContentChecked(hasContent);
- }
- });
- }
-
- @Override
- protected void startArchiveVoicemailTask(final Uri voicemailUri, final boolean archivedByUser) {
- // If a user wants to share an archived voicemail, no need for archiving, just go straight
- // to share intent.
- if (!archivedByUser) {
- sendShareIntent(voicemailUri);
- }
- }
-
- @Override
- protected boolean requestContent(int code) {
- handleError(new FileNotFoundException("Voicemail archive file does not exist"));
- return false; // No way for archive tab to request content
- }
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailAsyncTaskUtil.java b/src/com/android/dialer/voicemail/VoicemailAsyncTaskUtil.java
deleted file mode 100644
index 7abf9a72c..000000000
--- a/src/com/android/dialer/voicemail/VoicemailAsyncTaskUtil.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * 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.dialer.voicemail;
-
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.dialer.calllog.CallLogQuery;
-import com.android.dialer.database.VoicemailArchiveContract;
-import com.android.dialer.util.AsyncTaskExecutor;
-import com.android.dialer.util.AsyncTaskExecutors;
-import com.google.common.base.Preconditions;
-import com.google.common.io.ByteStreams;
-
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.provider.CallLog;
-import android.provider.VoicemailContract;
-import android.util.Log;
-import com.android.common.io.MoreCloseables;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import javax.annotation.Nullable;
-
-/**
- * Class containing asynchronous tasks for voicemails.
- */
-@NeededForTesting
-public class VoicemailAsyncTaskUtil {
- private static final String TAG = "VoicemailAsyncTaskUtil";
-
- /** The enumeration of {@link AsyncTask} objects we use in this class. */
- public enum Tasks {
- GET_VOICEMAIL_FILE_PATH,
- SET_VOICEMAIL_ARCHIVE_STATUS,
- ARCHIVE_VOICEMAIL_CONTENT
- }
-
- @NeededForTesting
- public interface OnArchiveVoicemailListener {
- /**
- * Called after the voicemail has been archived.
- *
- * @param archivedVoicemailUri the URI of the archived voicemail
- */
- void onArchiveVoicemail(@Nullable Uri archivedVoicemailUri);
- }
-
- @NeededForTesting
- public interface OnSetVoicemailArchiveStatusListener {
- /**
- * Called after the voicemail archived_by_user column is updated.
- *
- * @param success whether the update was successful or not
- */
- void onSetVoicemailArchiveStatus(boolean success);
- }
-
- @NeededForTesting
- public interface OnGetArchivedVoicemailFilePathListener {
- /**
- * Called after the voicemail file path is obtained.
- *
- * @param filePath the file path of the archived voicemail
- */
- void onGetArchivedVoicemailFilePath(@Nullable String filePath);
- }
-
- private final ContentResolver mResolver;
- private final AsyncTaskExecutor mAsyncTaskExecutor;
-
- @NeededForTesting
- public VoicemailAsyncTaskUtil(ContentResolver contentResolver) {
- mResolver = Preconditions.checkNotNull(contentResolver);
- mAsyncTaskExecutor = AsyncTaskExecutors.createThreadPoolExecutor();
- }
-
- /**
- * Returns the archived voicemail file path.
- */
- @NeededForTesting
- public void getVoicemailFilePath(
- final OnGetArchivedVoicemailFilePathListener listener,
- final Uri voicemailUri) {
- Preconditions.checkNotNull(listener);
- Preconditions.checkNotNull(voicemailUri);
- mAsyncTaskExecutor.submit(Tasks.GET_VOICEMAIL_FILE_PATH,
- new AsyncTask<Void, Void, String>() {
- @Nullable
- @Override
- protected String doInBackground(Void... params) {
- try (Cursor cursor = mResolver.query(voicemailUri,
- new String[]{VoicemailArchiveContract.VoicemailArchive._DATA},
- null, null, null)) {
- if (hasContent(cursor)) {
- return cursor.getString(cursor.getColumnIndex(
- VoicemailArchiveContract.VoicemailArchive._DATA));
- }
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(String filePath) {
- listener.onGetArchivedVoicemailFilePath(filePath);
- }
- });
- }
-
- /**
- * Updates the archived_by_user flag of the archived voicemail.
- */
- @NeededForTesting
- public void setVoicemailArchiveStatus(
- final OnSetVoicemailArchiveStatusListener listener,
- final Uri voicemailUri,
- final boolean archivedByUser) {
- Preconditions.checkNotNull(listener);
- Preconditions.checkNotNull(voicemailUri);
- mAsyncTaskExecutor.submit(Tasks.SET_VOICEMAIL_ARCHIVE_STATUS,
- new AsyncTask<Void, Void, Boolean>() {
- @Override
- protected Boolean doInBackground(Void... params) {
- ContentValues values = new ContentValues(1);
- values.put(VoicemailArchiveContract.VoicemailArchive.ARCHIVED,
- archivedByUser);
- return mResolver.update(voicemailUri, values, null, null) > 0;
- }
-
- @Override
- protected void onPostExecute(Boolean success) {
- listener.onSetVoicemailArchiveStatus(success);
- }
- });
- }
-
- /**
- * Checks if a voicemail has already been archived, if so, return the previously archived URI.
- * Otherwise, copy the voicemail information to the local dialer database. If archive was
- * successful, archived voicemail URI is returned to listener, otherwise null.
- */
- @NeededForTesting
- public void archiveVoicemailContent(
- final OnArchiveVoicemailListener listener,
- final Uri voicemailUri) {
- Preconditions.checkNotNull(listener);
- Preconditions.checkNotNull(voicemailUri);
- mAsyncTaskExecutor.submit(Tasks.ARCHIVE_VOICEMAIL_CONTENT,
- new AsyncTask<Void, Void, Uri>() {
- @Nullable
- @Override
- protected Uri doInBackground(Void... params) {
- Uri archivedVoicemailUri = getArchivedVoicemailUri(voicemailUri);
-
- // If previously archived, return uri, otherwise archive everything.
- if (archivedVoicemailUri != null) {
- return archivedVoicemailUri;
- }
-
- // Combine call log and voicemail content info.
- ContentValues values = getVoicemailContentValues(voicemailUri);
- if (values == null) {
- return null;
- }
-
- Uri insertedVoicemailUri = mResolver.insert(
- VoicemailArchiveContract.VoicemailArchive.CONTENT_URI, values);
- if (insertedVoicemailUri == null) {
- return null;
- }
-
- // Copy voicemail content to a new file.
- boolean copiedFile = false;
- try (InputStream inputStream = mResolver.openInputStream(voicemailUri);
- OutputStream outputStream =
- mResolver.openOutputStream(insertedVoicemailUri)) {
- if (inputStream != null && outputStream != null) {
- ByteStreams.copy(inputStream, outputStream);
- copiedFile = true;
- return insertedVoicemailUri;
- }
- } catch (IOException e) {
- Log.w(TAG, "Failed to copy voicemail content to new file: "
- + e.toString());
- } finally {
- if (!copiedFile) {
- // Roll back insert if the voicemail content was not copied.
- mResolver.delete(insertedVoicemailUri, null, null);
- }
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Uri archivedVoicemailUri) {
- listener.onArchiveVoicemail(archivedVoicemailUri);
- }
- });
- }
-
- /**
- * Helper method to get the archived URI of a voicemail.
- *
- * @param voicemailUri a {@link android.provider.VoicemailContract.Voicemails#CONTENT_URI} URI.
- * @return the URI of the archived voicemail or {@code null}
- */
- @Nullable
- private Uri getArchivedVoicemailUri(Uri voicemailUri) {
- try (Cursor cursor = getArchiveExistsCursor(voicemailUri)) {
- if (hasContent(cursor)) {
- return VoicemailArchiveContract.VoicemailArchive
- .buildWithId(cursor.getInt(cursor.getColumnIndex(
- VoicemailArchiveContract.VoicemailArchive._ID)));
- }
- }
- return null;
- }
-
- /**
- * Helper method to make a copy of all the values needed to display a voicemail.
- *
- * @param voicemailUri a {@link VoicemailContract.Voicemails#CONTENT_URI} URI.
- * @return the combined call log and voicemail values for the given URI, or {@code null}
- */
- @Nullable
- private ContentValues getVoicemailContentValues(Uri voicemailUri) {
- try (Cursor callLogInfo = getCallLogInfoCursor(voicemailUri);
- Cursor contentInfo = getContentInfoCursor(voicemailUri)) {
-
- if (hasContent(callLogInfo) && hasContent(contentInfo)) {
- // Create values to insert into database.
- ContentValues values = new ContentValues();
-
- // Insert voicemail call log info.
- values.put(VoicemailArchiveContract.VoicemailArchive.COUNTRY_ISO,
- callLogInfo.getString(CallLogQuery.COUNTRY_ISO));
- values.put(VoicemailArchiveContract.VoicemailArchive.GEOCODED_LOCATION,
- callLogInfo.getString(CallLogQuery.GEOCODED_LOCATION));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NAME,
- callLogInfo.getString(CallLogQuery.CACHED_NAME));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NUMBER_TYPE,
- callLogInfo.getInt(CallLogQuery.CACHED_NUMBER_TYPE));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NUMBER_LABEL,
- callLogInfo.getString(CallLogQuery.CACHED_NUMBER_LABEL));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_LOOKUP_URI,
- callLogInfo.getString(CallLogQuery.CACHED_LOOKUP_URI));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_MATCHED_NUMBER,
- callLogInfo.getString(CallLogQuery.CACHED_MATCHED_NUMBER));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NORMALIZED_NUMBER,
- callLogInfo.getString(CallLogQuery.CACHED_NORMALIZED_NUMBER));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_FORMATTED_NUMBER,
- callLogInfo.getString(CallLogQuery.CACHED_FORMATTED_NUMBER));
- values.put(VoicemailArchiveContract.VoicemailArchive.NUMBER_PRESENTATION,
- callLogInfo.getInt(CallLogQuery.NUMBER_PRESENTATION));
- values.put(VoicemailArchiveContract.VoicemailArchive.ACCOUNT_COMPONENT_NAME,
- callLogInfo.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME));
- values.put(VoicemailArchiveContract.VoicemailArchive.ACCOUNT_ID,
- callLogInfo.getString(CallLogQuery.ACCOUNT_ID));
- values.put(VoicemailArchiveContract.VoicemailArchive.FEATURES,
- callLogInfo.getInt(CallLogQuery.FEATURES));
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_PHOTO_URI,
- callLogInfo.getString(CallLogQuery.CACHED_PHOTO_URI));
-
- // Insert voicemail content info.
- values.put(VoicemailArchiveContract.VoicemailArchive.SERVER_ID,
- contentInfo.getInt(contentInfo.getColumnIndex(
- VoicemailContract.Voicemails._ID)));
- values.put(VoicemailArchiveContract.VoicemailArchive.NUMBER,
- contentInfo.getString(contentInfo.getColumnIndex(
- VoicemailContract.Voicemails.NUMBER)));
- values.put(VoicemailArchiveContract.VoicemailArchive.DATE,
- contentInfo.getLong(contentInfo.getColumnIndex(
- VoicemailContract.Voicemails.DATE)));
- values.put(VoicemailArchiveContract.VoicemailArchive.DURATION,
- contentInfo.getLong(contentInfo.getColumnIndex(
- VoicemailContract.Voicemails.DURATION)));
- values.put(VoicemailArchiveContract.VoicemailArchive.MIME_TYPE,
- contentInfo.getString(contentInfo.getColumnIndex(
- VoicemailContract.Voicemails.MIME_TYPE)));
- values.put(VoicemailArchiveContract.VoicemailArchive.TRANSCRIPTION,
- contentInfo.getString(contentInfo.getColumnIndex(
- VoicemailContract.Voicemails.TRANSCRIPTION)));
-
- // Achived is false by default because it is updated after insertion.
- values.put(VoicemailArchiveContract.VoicemailArchive.ARCHIVED, false);
-
- return values;
- }
- }
- return null;
- }
-
- private boolean hasContent(@Nullable Cursor cursor) {
- return cursor != null && cursor.moveToFirst();
- }
-
- @Nullable
- private Cursor getCallLogInfoCursor(Uri voicemailUri) {
- return mResolver.query(
- ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL,
- ContentUris.parseId(voicemailUri)),
- CallLogQuery._PROJECTION, null, null, null);
- }
-
- @Nullable
- private Cursor getContentInfoCursor(Uri voicemailUri) {
- return mResolver.query(voicemailUri,
- new String[] {
- VoicemailContract.Voicemails._ID,
- VoicemailContract.Voicemails.NUMBER,
- VoicemailContract.Voicemails.DATE,
- VoicemailContract.Voicemails.DURATION,
- VoicemailContract.Voicemails.MIME_TYPE,
- VoicemailContract.Voicemails.TRANSCRIPTION,
- }, null, null, null);
- }
-
- @Nullable
- private Cursor getArchiveExistsCursor(Uri voicemailUri) {
- return mResolver.query(VoicemailArchiveContract.VoicemailArchive.CONTENT_URI,
- new String[] {VoicemailArchiveContract.VoicemailArchive._ID},
- VoicemailArchiveContract.VoicemailArchive.SERVER_ID + "="
- + ContentUris.parseId(voicemailUri),
- null,
- null);
- }
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailAudioManager.java b/src/com/android/dialer/voicemail/VoicemailAudioManager.java
deleted file mode 100644
index fe6cf5f45..000000000
--- a/src/com/android/dialer/voicemail/VoicemailAudioManager.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * 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.dialer.voicemail;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.media.AudioManager.OnAudioFocusChangeListener;
-import android.telecom.CallAudioState;
-import android.util.Log;
-
-import java.util.concurrent.RejectedExecutionException;
-
-/**
- * This class manages all audio changes for voicemail playback.
- */
-final class VoicemailAudioManager implements OnAudioFocusChangeListener,
- WiredHeadsetManager.Listener {
- private static final String TAG = VoicemailAudioManager.class.getSimpleName();
-
- public static final int PLAYBACK_STREAM = AudioManager.STREAM_VOICE_CALL;
-
- private AudioManager mAudioManager;
- private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
- private WiredHeadsetManager mWiredHeadsetManager;
- private boolean mWasSpeakerOn;
- private CallAudioState mCallAudioState;
-
- public VoicemailAudioManager(Context context,
- VoicemailPlaybackPresenter voicemailPlaybackPresenter) {
- mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
- mWiredHeadsetManager = new WiredHeadsetManager(context);
- mWiredHeadsetManager.setListener(this);
-
- mCallAudioState = getInitialAudioState();
- Log.i(TAG, "Initial audioState = " + mCallAudioState);
- }
-
- public void requestAudioFocus() {
- int result = mAudioManager.requestAudioFocus(
- this,
- PLAYBACK_STREAM,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
- if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- throw new RejectedExecutionException("Could not capture audio focus.");
- }
- }
-
- public void abandonAudioFocus() {
- mAudioManager.abandonAudioFocus(this);
- }
-
- @Override
- public void onAudioFocusChange(int focusChange) {
- Log.d(TAG, "onAudioFocusChange: focusChange=" + focusChange);
- mVoicemailPlaybackPresenter.onAudioFocusChange(focusChange == AudioManager.AUDIOFOCUS_GAIN);
- }
-
- @Override
- public void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn) {
- Log.i(TAG, "wired headset was plugged in changed: " + oldIsPluggedIn
- + " -> "+ newIsPluggedIn);
-
- if (oldIsPluggedIn == newIsPluggedIn) {
- return;
- }
-
- int newRoute = mCallAudioState.getRoute(); // start out with existing route
- if (newIsPluggedIn) {
- newRoute = CallAudioState.ROUTE_WIRED_HEADSET;
- } else {
- if (mWasSpeakerOn) {
- newRoute = CallAudioState.ROUTE_SPEAKER;
- } else {
- newRoute = CallAudioState.ROUTE_EARPIECE;
- }
- }
-
- mVoicemailPlaybackPresenter.setSpeakerphoneOn(newRoute == CallAudioState.ROUTE_SPEAKER);
-
- // We need to call this every time even if we do not change the route because the supported
- // routes changed either to include or not include WIRED_HEADSET.
- setSystemAudioState(
- new CallAudioState(false /* muted */, newRoute, calculateSupportedRoutes()));
- }
-
- public void setSpeakerphoneOn(boolean on) {
- setAudioRoute(on ? CallAudioState.ROUTE_SPEAKER : CallAudioState.ROUTE_WIRED_OR_EARPIECE);
- }
-
- public boolean isWiredHeadsetPluggedIn() {
- return mWiredHeadsetManager.isPluggedIn();
- }
-
- public void registerReceivers() {
- // Receivers is plural because we expect to add bluetooth support.
- mWiredHeadsetManager.registerReceiver();
- }
-
- public void unregisterReceivers() {
- mWiredHeadsetManager.unregisterReceiver();
- }
-
- /**
- * Change the audio route, for example from earpiece to speakerphone.
- *
- * @param route The new audio route to use. See {@link CallAudioState}.
- */
- void setAudioRoute(int route) {
- Log.v(TAG, "setAudioRoute, route: " + CallAudioState.audioRouteToString(route));
-
- // Change ROUTE_WIRED_OR_EARPIECE to a single entry.
- int newRoute = selectWiredOrEarpiece(route, mCallAudioState.getSupportedRouteMask());
-
- // If route is unsupported, do nothing.
- if ((mCallAudioState.getSupportedRouteMask() | newRoute) == 0) {
- Log.w(TAG, "Asking to set to a route that is unsupported: " + newRoute);
- return;
- }
-
- if (mCallAudioState.getRoute() != newRoute) {
- // Remember the new speaker state so it can be restored when the user plugs and unplugs
- // a headset.
- mWasSpeakerOn = newRoute == CallAudioState.ROUTE_SPEAKER;
- setSystemAudioState(new CallAudioState(false /* muted */, newRoute,
- mCallAudioState.getSupportedRouteMask()));
- }
- }
-
- private CallAudioState getInitialAudioState() {
- int supportedRouteMask = calculateSupportedRoutes();
- int route = selectWiredOrEarpiece(CallAudioState.ROUTE_WIRED_OR_EARPIECE,
- supportedRouteMask);
- return new CallAudioState(false /* muted */, route, supportedRouteMask);
- }
-
- private int calculateSupportedRoutes() {
- int routeMask = CallAudioState.ROUTE_SPEAKER;
- if (mWiredHeadsetManager.isPluggedIn()) {
- routeMask |= CallAudioState.ROUTE_WIRED_HEADSET;
- } else {
- routeMask |= CallAudioState.ROUTE_EARPIECE;
- }
- return routeMask;
- }
-
- private int selectWiredOrEarpiece(int route, int supportedRouteMask) {
- // Since they are mutually exclusive and one is ALWAYS valid, we allow a special input of
- // ROUTE_WIRED_OR_EARPIECE so that callers don't have to make a call to check which is
- // supported before calling setAudioRoute.
- if (route == CallAudioState.ROUTE_WIRED_OR_EARPIECE) {
- route = CallAudioState.ROUTE_WIRED_OR_EARPIECE & supportedRouteMask;
- if (route == 0) {
- Log.wtf(TAG, "One of wired headset or earpiece should always be valid.");
- // assume earpiece in this case.
- route = CallAudioState.ROUTE_EARPIECE;
- }
- }
- return route;
- }
-
- private void setSystemAudioState(CallAudioState callAudioState) {
- CallAudioState oldAudioState = mCallAudioState;
- mCallAudioState = callAudioState;
-
- Log.i(TAG, "setSystemAudioState: changing from " + oldAudioState + " to "
- + mCallAudioState);
-
- // Audio route.
- if (mCallAudioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
- turnOnSpeaker(true);
- } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_EARPIECE ||
- mCallAudioState.getRoute() == CallAudioState.ROUTE_WIRED_HEADSET) {
- // Just handle turning off the speaker, the system will handle switching between wired
- // headset and earpiece.
- turnOnSpeaker(false);
- }
- }
-
- private void turnOnSpeaker(boolean on) {
- if (mAudioManager.isSpeakerphoneOn() != on) {
- Log.i(TAG, "turning speaker phone on: " + on);
- mAudioManager.setSpeakerphoneOn(on);
- }
- }
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
deleted file mode 100644
index 7d6fe78d1..000000000
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
+++ /dev/null
@@ -1,638 +0,0 @@
-/*
- * 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.dialer.voicemail;
-
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.support.design.widget.Snackbar;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.SeekBar;
-import android.widget.SeekBar.OnSeekBarChangeListener;
-import android.widget.Space;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.common.io.MoreCloseables;
-import com.android.dialer.PhoneCallDetails;
-import com.android.dialer.R;
-import com.android.dialer.calllog.CallLogAsyncTaskUtil;
-
-import com.android.dialer.database.VoicemailArchiveContract;
-import com.android.dialer.database.VoicemailArchiveContract.VoicemailArchive;
-import com.android.dialer.util.AsyncTaskExecutor;
-import com.android.dialer.util.AsyncTaskExecutors;
-import com.android.dialerbind.ObjectFactory;
-import com.google.common.annotations.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Objects;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.ScheduledExecutorService;
-
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.GuardedBy;
-import javax.annotation.concurrent.NotThreadSafe;
-import javax.annotation.concurrent.ThreadSafe;
-
-/**
- * Displays and plays a single voicemail. See {@link VoicemailPlaybackPresenter} for
- * details on the voicemail playback implementation.
- *
- * This class is not thread-safe, it is thread-confined. All calls to all public
- * methods on this class are expected to come from the main ui thread.
- */
-@NotThreadSafe
-public class VoicemailPlaybackLayout extends LinearLayout
- implements VoicemailPlaybackPresenter.PlaybackView,
- CallLogAsyncTaskUtil.CallLogAsyncTaskListener {
- private static final String TAG = VoicemailPlaybackLayout.class.getSimpleName();
- private static final int VOICEMAIL_DELETE_DELAY_MS = 3000;
- private static final int VOICEMAIL_ARCHIVE_DELAY_MS = 3000;
-
- /** The enumeration of {@link AsyncTask} objects we use in this class. */
- public enum Tasks {
- QUERY_ARCHIVED_STATUS
- }
-
- /**
- * Controls the animation of the playback slider.
- */
- @ThreadSafe
- private final class PositionUpdater implements Runnable {
-
- /** Update rate for the slider, 30fps. */
- private static final int SLIDER_UPDATE_PERIOD_MILLIS = 1000 / 30;
-
- private int mDurationMs;
- private final ScheduledExecutorService mExecutorService;
- private final Object mLock = new Object();
- @GuardedBy("mLock") private ScheduledFuture<?> mScheduledFuture;
-
- private Runnable mUpdateClipPositionRunnable = new Runnable() {
- @Override
- public void run() {
- int currentPositionMs = 0;
- synchronized (mLock) {
- if (mScheduledFuture == null || mPresenter == null) {
- // This task has been canceled. Just stop now.
- return;
- }
- currentPositionMs = mPresenter.getMediaPlayerPosition();
- }
- setClipPosition(currentPositionMs, mDurationMs);
- }
- };
-
- public PositionUpdater(int durationMs, ScheduledExecutorService executorService) {
- mDurationMs = durationMs;
- mExecutorService = executorService;
- }
-
- @Override
- public void run() {
- post(mUpdateClipPositionRunnable);
- }
-
- public void startUpdating() {
- synchronized (mLock) {
- cancelPendingRunnables();
- mScheduledFuture = mExecutorService.scheduleAtFixedRate(
- this, 0, SLIDER_UPDATE_PERIOD_MILLIS, TimeUnit.MILLISECONDS);
- }
- }
-
- public void stopUpdating() {
- synchronized (mLock) {
- cancelPendingRunnables();
- }
- }
-
- @GuardedBy("mLock")
- private void cancelPendingRunnables() {
- if (mScheduledFuture != null) {
- mScheduledFuture.cancel(true);
- mScheduledFuture = null;
- }
- removeCallbacks(mUpdateClipPositionRunnable);
- }
- }
-
- /**
- * Handle state changes when the user manipulates the seek bar.
- */
- private final OnSeekBarChangeListener mSeekBarChangeListener = new OnSeekBarChangeListener() {
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- if (mPresenter != null) {
- mPresenter.pausePlaybackForSeeking();
- }
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- if (mPresenter != null) {
- mPresenter.resumePlaybackAfterSeeking(seekBar.getProgress());
- }
- }
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- setClipPosition(progress, seekBar.getMax());
- // Update the seek position if user manually changed it. This makes sure position gets
- // updated when user use volume button to seek playback in talkback mode.
- if (fromUser) {
- mPresenter.seek(progress);
- }
- }
- };
-
- /**
- * Click listener to toggle speakerphone.
- */
- private final View.OnClickListener mSpeakerphoneListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mPresenter != null) {
- mPresenter.toggleSpeakerphone();
- }
- }
- };
-
- /**
- * Click listener to play or pause voicemail playback.
- */
- private final View.OnClickListener mStartStopButtonListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mPresenter == null) {
- return;
- }
-
- if (mIsPlaying) {
- mPresenter.pausePlayback();
- } else {
- mPresenter.resumePlayback();
- }
- }
- };
-
- private final View.OnClickListener mDeleteButtonListener = new View.OnClickListener() {
- @Override
- public void onClick(View view ) {
- if (mPresenter == null) {
- return;
- }
- mPresenter.pausePlayback();
- mPresenter.onVoicemailDeleted();
-
- final Uri deleteUri = mVoicemailUri;
- final Runnable deleteCallback = new Runnable() {
- @Override
- public void run() {
- if (Objects.equals(deleteUri, mVoicemailUri)) {
- CallLogAsyncTaskUtil.deleteVoicemail(mContext, deleteUri,
- VoicemailPlaybackLayout.this);
- }
- }
- };
-
- final Handler handler = new Handler();
- // Add a little buffer time in case the user clicked "undo" at the end of the delay
- // window.
- handler.postDelayed(deleteCallback, VOICEMAIL_DELETE_DELAY_MS + 50);
-
- Snackbar.make(VoicemailPlaybackLayout.this, R.string.snackbar_voicemail_deleted,
- Snackbar.LENGTH_LONG)
- .setDuration(VOICEMAIL_DELETE_DELAY_MS)
- .setAction(R.string.snackbar_voicemail_deleted_undo,
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mPresenter.onVoicemailDeleteUndo();
- handler.removeCallbacks(deleteCallback);
- }
- })
- .setActionTextColor(
- mContext.getResources().getColor(
- R.color.dialer_snackbar_action_text_color))
- .show();
- }
- };
-
- private final View.OnClickListener mArchiveButtonListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mPresenter == null || isArchiving(mVoicemailUri)) {
- return;
- }
- mIsArchiving.add(mVoicemailUri);
- mPresenter.pausePlayback();
- updateArchiveUI(mVoicemailUri);
- disableUiElements();
- mPresenter.archiveContent(mVoicemailUri, true);
- }
- };
-
- private final View.OnClickListener mShareButtonListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mPresenter == null || isArchiving(mVoicemailUri)) {
- return;
- }
- disableUiElements();
- mPresenter.archiveContent(mVoicemailUri, false);
- }
- };
-
- private Context mContext;
- private VoicemailPlaybackPresenter mPresenter;
- private Uri mVoicemailUri;
- private final AsyncTaskExecutor mAsyncTaskExecutor =
- AsyncTaskExecutors.createAsyncTaskExecutor();
- private boolean mIsPlaying = false;
- /**
- * Keeps track of which voicemails are currently being archived in order to update the voicemail
- * card UI every time a user opens a new card.
- */
- private static final ArrayList<Uri> mIsArchiving = new ArrayList<>();
-
- private SeekBar mPlaybackSeek;
- private ImageButton mStartStopButton;
- private ImageButton mPlaybackSpeakerphone;
- private ImageButton mDeleteButton;
- private ImageButton mArchiveButton;
- private ImageButton mShareButton;
-
- private Space mArchiveSpace;
- private Space mShareSpace;
-
- private TextView mStateText;
- private TextView mPositionText;
- private TextView mTotalDurationText;
-
- private PositionUpdater mPositionUpdater;
- private Drawable mVoicemailSeekHandleEnabled;
- private Drawable mVoicemailSeekHandleDisabled;
-
- public VoicemailPlaybackLayout(Context context) {
- this(context, null);
- }
-
- public VoicemailPlaybackLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
- LayoutInflater inflater =
- (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- inflater.inflate(R.layout.voicemail_playback_layout, this);
- }
-
- @Override
- public void setPresenter(VoicemailPlaybackPresenter presenter, Uri voicemailUri) {
- mPresenter = presenter;
- mVoicemailUri = voicemailUri;
- if (ObjectFactory.isVoicemailArchiveEnabled(mContext)) {
- updateArchiveUI(mVoicemailUri);
- updateArchiveButton(mVoicemailUri);
- }
-
- if (ObjectFactory.isVoicemailShareEnabled(mContext)) {
- // Show share button and space before it
- mShareSpace.setVisibility(View.VISIBLE);
- mShareButton.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mPlaybackSeek = (SeekBar) findViewById(R.id.playback_seek);
- mStartStopButton = (ImageButton) findViewById(R.id.playback_start_stop);
- mPlaybackSpeakerphone = (ImageButton) findViewById(R.id.playback_speakerphone);
- mDeleteButton = (ImageButton) findViewById(R.id.delete_voicemail);
- mArchiveButton =(ImageButton) findViewById(R.id.archive_voicemail);
- mShareButton = (ImageButton) findViewById(R.id.share_voicemail);
-
- mArchiveSpace = (Space) findViewById(R.id.space_before_archive_voicemail);
- mShareSpace = (Space) findViewById(R.id.space_before_share_voicemail);
-
- mStateText = (TextView) findViewById(R.id.playback_state_text);
- mPositionText = (TextView) findViewById(R.id.playback_position_text);
- mTotalDurationText = (TextView) findViewById(R.id.total_duration_text);
-
- mPlaybackSeek.setOnSeekBarChangeListener(mSeekBarChangeListener);
- mStartStopButton.setOnClickListener(mStartStopButtonListener);
- mPlaybackSpeakerphone.setOnClickListener(mSpeakerphoneListener);
- mDeleteButton.setOnClickListener(mDeleteButtonListener);
- mArchiveButton.setOnClickListener(mArchiveButtonListener);
- mShareButton.setOnClickListener(mShareButtonListener);
-
- mPositionText.setText(formatAsMinutesAndSeconds(0));
- mTotalDurationText.setText(formatAsMinutesAndSeconds(0));
-
- mVoicemailSeekHandleEnabled = getResources().getDrawable(
- R.drawable.ic_voicemail_seek_handle, mContext.getTheme());
- mVoicemailSeekHandleDisabled = getResources().getDrawable(
- R.drawable.ic_voicemail_seek_handle_disabled, mContext.getTheme());
- }
-
- @Override
- public void onPlaybackStarted(int duration, ScheduledExecutorService executorService) {
- mIsPlaying = true;
-
- mStartStopButton.setImageResource(R.drawable.ic_pause);
-
- if (mPositionUpdater != null) {
- mPositionUpdater.stopUpdating();
- mPositionUpdater = null;
- }
- mPositionUpdater = new PositionUpdater(duration, executorService);
- mPositionUpdater.startUpdating();
- }
-
- @Override
- public void onPlaybackStopped() {
- mIsPlaying = false;
-
- mStartStopButton.setImageResource(R.drawable.ic_play_arrow);
-
- if (mPositionUpdater != null) {
- mPositionUpdater.stopUpdating();
- mPositionUpdater = null;
- }
- }
-
- @Override
- public void onPlaybackError() {
- if (mPositionUpdater != null) {
- mPositionUpdater.stopUpdating();
- }
-
- disableUiElements();
- mStateText.setText(getString(R.string.voicemail_playback_error));
- }
-
- @Override
- public void onSpeakerphoneOn(boolean on) {
- if (on) {
- mPlaybackSpeakerphone.setImageResource(R.drawable.ic_volume_up_24dp);
- // Speaker is now on, tapping button will turn it off.
- mPlaybackSpeakerphone.setContentDescription(
- mContext.getString(R.string.voicemail_speaker_off));
- } else {
- mPlaybackSpeakerphone.setImageResource(R.drawable.ic_volume_down_24dp);
- // Speaker is now off, tapping button will turn it on.
- mPlaybackSpeakerphone.setContentDescription(
- mContext.getString(R.string.voicemail_speaker_on));
- }
- }
-
- @Override
- public void setClipPosition(int positionMs, int durationMs) {
- int seekBarPositionMs = Math.max(0, positionMs);
- int seekBarMax = Math.max(seekBarPositionMs, durationMs);
- if (mPlaybackSeek.getMax() != seekBarMax) {
- mPlaybackSeek.setMax(seekBarMax);
- }
-
- mPlaybackSeek.setProgress(seekBarPositionMs);
-
- mPositionText.setText(formatAsMinutesAndSeconds(seekBarPositionMs));
- mTotalDurationText.setText(formatAsMinutesAndSeconds(durationMs));
- }
-
- @Override
- public void setSuccess() {
- mStateText.setText(null);
- }
-
- @Override
- public void setIsFetchingContent() {
- disableUiElements();
- mStateText.setText(getString(R.string.voicemail_fetching_content));
- }
-
- @Override
- public void setFetchContentTimeout() {
- mStartStopButton.setEnabled(true);
- mStateText.setText(getString(R.string.voicemail_fetching_timout));
- }
-
- @Override
- public int getDesiredClipPosition() {
- return mPlaybackSeek.getProgress();
- }
-
- @Override
- public void disableUiElements() {
- mStartStopButton.setEnabled(false);
- resetSeekBar();
- }
-
- @Override
- public void enableUiElements() {
- mDeleteButton.setEnabled(true);
- mStartStopButton.setEnabled(true);
- mPlaybackSeek.setEnabled(true);
- mPlaybackSeek.setThumb(mVoicemailSeekHandleEnabled);
- }
-
- @Override
- public void resetSeekBar() {
- mPlaybackSeek.setProgress(0);
- mPlaybackSeek.setEnabled(false);
- mPlaybackSeek.setThumb(mVoicemailSeekHandleDisabled);
- }
-
- @Override
- public void onDeleteCall() {}
-
- @Override
- public void onDeleteVoicemail() {
- mPresenter.onVoicemailDeletedInDatabase();
- }
-
- @Override
- public void onGetCallDetails(PhoneCallDetails[] details) {}
-
- private String getString(int resId) {
- return mContext.getString(resId);
- }
-
- /**
- * Formats a number of milliseconds as something that looks like {@code 00:05}.
- * <p>
- * We always use four digits, two for minutes two for seconds. In the very unlikely event
- * that the voicemail duration exceeds 99 minutes, the display is capped at 99 minutes.
- */
- private String formatAsMinutesAndSeconds(int millis) {
- int seconds = millis / 1000;
- int minutes = seconds / 60;
- seconds -= minutes * 60;
- if (minutes > 99) {
- minutes = 99;
- }
- return String.format("%02d:%02d", minutes, seconds);
- }
-
- /**
- * Called when a voicemail archive succeeded. If the expanded voicemail was being
- * archived, update the card UI. Either way, display a snackbar linking user to archive.
- */
- @Override
- public void onVoicemailArchiveSucceded(Uri voicemailUri) {
- if (isArchiving(voicemailUri)) {
- mIsArchiving.remove(voicemailUri);
- if (Objects.equals(voicemailUri, mVoicemailUri)) {
- onVoicemailArchiveResult();
- hideArchiveButton();
- }
- }
-
- Snackbar.make(this, R.string.snackbar_voicemail_archived,
- Snackbar.LENGTH_LONG)
- .setDuration(VOICEMAIL_ARCHIVE_DELAY_MS)
- .setAction(R.string.snackbar_voicemail_archived_goto,
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent intent = new Intent(mContext,
- VoicemailArchiveActivity.class);
- mContext.startActivity(intent);
- }
- })
- .setActionTextColor(
- mContext.getResources().getColor(R.color.dialer_snackbar_action_text_color))
- .show();
- }
-
- /**
- * If a voicemail archive failed, and the expanded card was being archived, update the card UI.
- * Either way, display a toast saying the voicemail archive failed.
- */
- @Override
- public void onVoicemailArchiveFailed(Uri voicemailUri) {
- if (isArchiving(voicemailUri)) {
- mIsArchiving.remove(voicemailUri);
- if (Objects.equals(voicemailUri, mVoicemailUri)) {
- onVoicemailArchiveResult();
- }
- }
- String toastStr = mContext.getString(R.string.voicemail_archive_failed);
- Toast.makeText(mContext, toastStr, Toast.LENGTH_SHORT).show();
- }
-
- public void hideArchiveButton() {
- mArchiveSpace.setVisibility(View.GONE);
- mArchiveButton.setVisibility(View.GONE);
- mArchiveButton.setClickable(false);
- mArchiveButton.setEnabled(false);
- }
-
- /**
- * Whenever a voicemail archive succeeds or fails, clear the text displayed in the voicemail
- * card.
- */
- private void onVoicemailArchiveResult() {
- enableUiElements();
- mStateText.setText(null);
- mArchiveButton.setColorFilter(null);
- }
-
- /**
- * Whether or not the voicemail with the given uri is being archived.
- */
- private boolean isArchiving(@Nullable Uri uri) {
- return uri != null && mIsArchiving.contains(uri);
- }
-
- /**
- * Show the proper text and hide the archive button if the voicemail is still being archived.
- */
- private void updateArchiveUI(@Nullable Uri voicemailUri) {
- if (!Objects.equals(voicemailUri, mVoicemailUri)) {
- return;
- }
- if (isArchiving(voicemailUri)) {
- // If expanded card was in the middle of archiving, disable buttons and display message
- disableUiElements();
- mDeleteButton.setEnabled(false);
- mArchiveButton.setColorFilter(getResources().getColor(R.color.setting_disabled_color));
- mStateText.setText(getString(R.string.voicemail_archiving_content));
- } else {
- onVoicemailArchiveResult();
- }
- }
-
- /**
- * Hides the archive button if the voicemail has already been archived, shows otherwise.
- * @param voicemailUri the URI of the voicemail for which the archive button needs to be updated
- */
- private void updateArchiveButton(@Nullable final Uri voicemailUri) {
- if (voicemailUri == null ||
- !Objects.equals(voicemailUri, mVoicemailUri) || isArchiving(voicemailUri) ||
- Objects.equals(voicemailUri.getAuthority(),VoicemailArchiveContract.AUTHORITY)) {
- return;
- }
- mAsyncTaskExecutor.submit(Tasks.QUERY_ARCHIVED_STATUS,
- new AsyncTask<Void, Void, Boolean>() {
- @Override
- public Boolean doInBackground(Void... params) {
- Cursor cursor = mContext.getContentResolver().query(VoicemailArchive.CONTENT_URI,
- null, VoicemailArchive.SERVER_ID + "=" + ContentUris.parseId(mVoicemailUri)
- + " AND " + VoicemailArchive.ARCHIVED + "= 1", null, null);
- boolean archived = cursor != null && cursor.getCount() > 0;
- cursor.close();
- return archived;
- }
-
- @Override
- public void onPostExecute(Boolean archived) {
- if (!Objects.equals(voicemailUri, mVoicemailUri)) {
- return;
- }
-
- if (archived) {
- hideArchiveButton();
- } else {
- mArchiveSpace.setVisibility(View.VISIBLE);
- mArchiveButton.setVisibility(View.VISIBLE);
- mArchiveButton.setClickable(true);
- mArchiveButton.setEnabled(true);
- }
-
- }
- });
- }
-
- @VisibleForTesting
- public String getStateText() {
- return mStateText.getText().toString();
- }
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
deleted file mode 100644
index e224ddc2a..000000000
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
+++ /dev/null
@@ -1,1010 +0,0 @@
-/*
- * 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.dialer.voicemail;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.media.MediaPlayer;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.provider.VoicemailContract;
-import android.support.v4.content.FileProvider;
-import android.util.Log;
-import android.view.WindowManager.LayoutParams;
-
-import com.android.dialer.R;
-import com.android.dialer.calllog.CallLogAsyncTaskUtil;
-import com.android.dialer.util.AsyncTaskExecutor;
-import com.android.dialer.util.AsyncTaskExecutors;
-import com.android.common.io.MoreCloseables;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Executors;
-import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import javax.annotation.concurrent.NotThreadSafe;
-import javax.annotation.concurrent.ThreadSafe;
-
-/**
- * Contains the controlling logic for a voicemail playback in the call log. It is closely coupled
- * to assumptions about the behaviors and lifecycle of the call log, in particular in the
- * {@link CallLogFragment} and {@link CallLogAdapter}.
- * <p>
- * This controls a single {@link com.android.dialer.voicemail.VoicemailPlaybackLayout}. A single
- * instance can be reused for different such layouts, using {@link #setPlaybackView}. This
- * is to facilitate reuse across different voicemail call log entries.
- * <p>
- * This class is not thread safe. The thread policy for this class is thread-confinement, all calls
- * into this class from outside must be done from the main UI thread.
- */
-@NotThreadSafe
-@VisibleForTesting
-public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListener,
- MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
-
- private static final String TAG = "VmPlaybackPresenter";
-
- /** Contract describing the behaviour we need from the ui we are controlling. */
- public interface PlaybackView {
- int getDesiredClipPosition();
- void disableUiElements();
- void enableUiElements();
- void onPlaybackError();
- void onPlaybackStarted(int duration, ScheduledExecutorService executorService);
- void onPlaybackStopped();
- void onSpeakerphoneOn(boolean on);
- void setClipPosition(int clipPositionInMillis, int clipLengthInMillis);
- void setSuccess();
- void setFetchContentTimeout();
- void setIsFetchingContent();
- void onVoicemailArchiveSucceded(Uri voicemailUri);
- void onVoicemailArchiveFailed(Uri voicemailUri);
- void setPresenter(VoicemailPlaybackPresenter presenter, Uri voicemailUri);
- void resetSeekBar();
- }
-
- public interface OnVoicemailDeletedListener {
- void onVoicemailDeleted(Uri uri);
- void onVoicemailDeleteUndo();
- void onVoicemailDeletedInDatabase();
- }
-
- /** The enumeration of {@link AsyncTask} objects we use in this class. */
- public enum Tasks {
- CHECK_FOR_CONTENT,
- CHECK_CONTENT_AFTER_CHANGE,
- ARCHIVE_VOICEMAIL
- }
-
- protected interface OnContentCheckedListener {
- void onContentChecked(boolean hasContent);
- }
-
- private static final String[] HAS_CONTENT_PROJECTION = new String[] {
- VoicemailContract.Voicemails.HAS_CONTENT,
- VoicemailContract.Voicemails.DURATION
- };
-
- private static final int NUMBER_OF_THREADS_IN_POOL = 2;
- // Time to wait for content to be fetched before timing out.
- private static final long FETCH_CONTENT_TIMEOUT_MS = 20000;
-
- private static final String VOICEMAIL_URI_KEY =
- VoicemailPlaybackPresenter.class.getName() + ".VOICEMAIL_URI";
- private static final String IS_PREPARED_KEY =
- VoicemailPlaybackPresenter.class.getName() + ".IS_PREPARED";
- // If present in the saved instance bundle, we should not resume playback on create.
- private static final String IS_PLAYING_STATE_KEY =
- VoicemailPlaybackPresenter.class.getName() + ".IS_PLAYING_STATE_KEY";
- // If present in the saved instance bundle, indicates where to set the playback slider.
- private static final String CLIP_POSITION_KEY =
- VoicemailPlaybackPresenter.class.getName() + ".CLIP_POSITION_KEY";
- private static final String IS_SPEAKERPHONE_ON_KEY =
- VoicemailPlaybackPresenter.class.getName() + ".IS_SPEAKER_PHONE_ON";
- public static final int PLAYBACK_REQUEST = 0;
- public static final int ARCHIVE_REQUEST = 1;
- public static final int SHARE_REQUEST = 2;
-
- /**
- * The most recently cached duration. We cache this since we don't want to keep requesting it
- * from the player, as this can easily lead to throwing {@link IllegalStateException} (any time
- * the player is released, it's illegal to ask for the duration).
- */
- private final AtomicInteger mDuration = new AtomicInteger(0);
-
- private static VoicemailPlaybackPresenter sInstance;
-
- private Activity mActivity;
- protected Context mContext;
- private PlaybackView mView;
- protected Uri mVoicemailUri;
-
- protected MediaPlayer mMediaPlayer;
- private int mPosition;
- private boolean mIsPlaying;
- // MediaPlayer crashes on some method calls if not prepared but does not have a method which
- // exposes its prepared state. Store this locally, so we can check and prevent crashes.
- private boolean mIsPrepared;
- private boolean mIsSpeakerphoneOn;
-
- private boolean mShouldResumePlaybackAfterSeeking;
- private int mInitialOrientation;
-
- // Used to run async tasks that need to interact with the UI.
- protected AsyncTaskExecutor mAsyncTaskExecutor;
- private static ScheduledExecutorService mScheduledExecutorService;
- /**
- * Used to handle the result of a successful or time-out fetch result.
- * <p>
- * This variable is thread-contained, accessed only on the ui thread.
- */
- private FetchResultHandler mFetchResultHandler;
- private final List<FetchResultHandler> mArchiveResultHandlers = new ArrayList<>();
- private Handler mHandler = new Handler();
- private PowerManager.WakeLock mProximityWakeLock;
- private VoicemailAudioManager mVoicemailAudioManager;
-
- private OnVoicemailDeletedListener mOnVoicemailDeletedListener;
- private final VoicemailAsyncTaskUtil mVoicemailAsyncTaskUtil;
-
- /**
- * Obtain singleton instance of this class. Use a single instance to provide a consistent
- * listener to the AudioManager when requesting and abandoning audio focus.
- *
- * Otherwise, after rotation the previous listener will still be active but a new listener
- * will be provided to calls to the AudioManager, which is bad. For example, abandoning
- * audio focus with the new listeners results in an AUDIO_FOCUS_GAIN callback to the
- * previous listener, which is the opposite of the intended behavior.
- */
- public static VoicemailPlaybackPresenter getInstance(
- Activity activity, Bundle savedInstanceState) {
- if (sInstance == null) {
- sInstance = new VoicemailPlaybackPresenter(activity);
- }
-
- sInstance.init(activity, savedInstanceState);
- return sInstance;
- }
-
- /**
- * Initialize variables which are activity-independent and state-independent.
- */
- protected VoicemailPlaybackPresenter(Activity activity) {
- Context context = activity.getApplicationContext();
- mAsyncTaskExecutor = AsyncTaskExecutors.createAsyncTaskExecutor();
- mVoicemailAudioManager = new VoicemailAudioManager(context, this);
- mVoicemailAsyncTaskUtil = new VoicemailAsyncTaskUtil(context.getContentResolver());
- PowerManager powerManager =
- (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- if (powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
- mProximityWakeLock = powerManager.newWakeLock(
- PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
- }
- }
-
- /**
- * Update variables which are activity-dependent or state-dependent.
- */
- protected void init(Activity activity, Bundle savedInstanceState) {
- mActivity = activity;
- mContext = activity;
-
- mInitialOrientation = mContext.getResources().getConfiguration().orientation;
- mActivity.setVolumeControlStream(VoicemailAudioManager.PLAYBACK_STREAM);
-
- if (savedInstanceState != null) {
- // Restores playback state when activity is recreated, such as after rotation.
- mVoicemailUri = (Uri) savedInstanceState.getParcelable(VOICEMAIL_URI_KEY);
- mIsPrepared = savedInstanceState.getBoolean(IS_PREPARED_KEY);
- mPosition = savedInstanceState.getInt(CLIP_POSITION_KEY, 0);
- mIsPlaying = savedInstanceState.getBoolean(IS_PLAYING_STATE_KEY, false);
- mIsSpeakerphoneOn = savedInstanceState.getBoolean(IS_SPEAKERPHONE_ON_KEY, false);
- }
-
- if (mMediaPlayer == null) {
- mIsPrepared = false;
- mIsPlaying = false;
- }
- }
-
- /**
- * Must be invoked when the parent Activity is saving it state.
- */
- public void onSaveInstanceState(Bundle outState) {
- if (mView != null) {
- outState.putParcelable(VOICEMAIL_URI_KEY, mVoicemailUri);
- outState.putBoolean(IS_PREPARED_KEY, mIsPrepared);
- outState.putInt(CLIP_POSITION_KEY, mView.getDesiredClipPosition());
- outState.putBoolean(IS_PLAYING_STATE_KEY, mIsPlaying);
- outState.putBoolean(IS_SPEAKERPHONE_ON_KEY, mIsSpeakerphoneOn);
- }
- }
-
- /**
- * Specify the view which this presenter controls and the voicemail to prepare to play.
- */
- public void setPlaybackView(
- PlaybackView view, Uri voicemailUri, boolean startPlayingImmediately) {
- mView = view;
- mView.setPresenter(this, voicemailUri);
-
- // Handles cases where the same entry is binded again when scrolling in list, or where
- // the MediaPlayer was retained after an orientation change.
- if (mMediaPlayer != null && mIsPrepared && voicemailUri.equals(mVoicemailUri)) {
- // If the voicemail card was rebinded, we need to set the position to the appropriate
- // point. Since we retain the media player, we can just set it to the position of the
- // media player.
- mPosition = mMediaPlayer.getCurrentPosition();
- onPrepared(mMediaPlayer);
- } else {
- if (!voicemailUri.equals(mVoicemailUri)) {
- mVoicemailUri = voicemailUri;
- mPosition = 0;
- // Default to earpiece.
- setSpeakerphoneOn(false);
- mVoicemailAudioManager.setSpeakerphoneOn(false);
- } else {
- // Update the view to the current speakerphone state.
- mView.onSpeakerphoneOn(mIsSpeakerphoneOn);
- }
- /*
- * Check to see if the content field in the DB is set. If set, we proceed to
- * prepareContent() method. We get the duration of the voicemail from the query and set
- * it if the content is not available.
- */
- checkForContent(new OnContentCheckedListener() {
- @Override
- public void onContentChecked(boolean hasContent) {
- if (hasContent) {
- prepareContent();
- } else if (mView != null) {
- mView.resetSeekBar();
- mView.setClipPosition(0, mDuration.get());
- }
- }
- });
-
- if (startPlayingImmediately) {
- // Since setPlaybackView can get called during the view binding process, we don't
- // want to reset mIsPlaying to false if the user is currently playing the
- // voicemail and the view is rebound.
- mIsPlaying = startPlayingImmediately;
- }
- }
- }
-
- /**
- * Reset the presenter for playback back to its original state.
- */
- public void resetAll() {
- pausePresenter(true);
-
- mView = null;
- mVoicemailUri = null;
- }
-
- /**
- * When navigating away from voicemail playback, we need to release the media player,
- * pause the UI and save the position.
- *
- * @param reset {@code true} if we want to reset the position of the playback, {@code false} if
- * we want to retain the current position (in case we return to the voicemail).
- */
- public void pausePresenter(boolean reset) {
- if (mMediaPlayer != null) {
- mMediaPlayer.release();
- mMediaPlayer = null;
- }
-
- disableProximitySensor(false /* waitForFarState */);
-
- mIsPrepared = false;
- mIsPlaying = false;
-
- if (reset) {
- // We want to reset the position whether or not the view is valid.
- mPosition = 0;
- }
-
- if (mView != null) {
- mView.onPlaybackStopped();
- if (reset) {
- mView.setClipPosition(0, mDuration.get());
- } else {
- mPosition = mView.getDesiredClipPosition();
- }
- }
- }
-
- /**
- * Must be invoked when the parent activity is resumed.
- */
- public void onResume() {
- mVoicemailAudioManager.registerReceivers();
- }
-
- /**
- * Must be invoked when the parent activity is paused.
- */
- public void onPause() {
- mVoicemailAudioManager.unregisterReceivers();
-
- if (mContext != null && mIsPrepared
- && mInitialOrientation != mContext.getResources().getConfiguration().orientation) {
- // If an orientation change triggers the pause, retain the MediaPlayer.
- Log.d(TAG, "onPause: Orientation changed.");
- return;
- }
-
- // Release the media player, otherwise there may be failures.
- pausePresenter(false);
-
- if (mActivity != null) {
- mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
-
- }
-
- /**
- * Must be invoked when the parent activity is destroyed.
- */
- public void onDestroy() {
- // Clear references to avoid leaks from the singleton instance.
- mActivity = null;
- mContext = null;
-
- if (mScheduledExecutorService != null) {
- mScheduledExecutorService.shutdown();
- mScheduledExecutorService = null;
- }
-
- if (!mArchiveResultHandlers.isEmpty()) {
- for (FetchResultHandler fetchResultHandler : mArchiveResultHandlers) {
- fetchResultHandler.destroy();
- }
- mArchiveResultHandlers.clear();
- }
-
- if (mFetchResultHandler != null) {
- mFetchResultHandler.destroy();
- mFetchResultHandler = null;
- }
- }
-
- /**
- * Checks to see if we have content available for this voicemail.
- */
- protected void checkForContent(final OnContentCheckedListener callback) {
- mAsyncTaskExecutor.submit(Tasks.CHECK_FOR_CONTENT, new AsyncTask<Void, Void, Boolean>() {
- @Override
- public Boolean doInBackground(Void... params) {
- return queryHasContent(mVoicemailUri);
- }
-
- @Override
- public void onPostExecute(Boolean hasContent) {
- callback.onContentChecked(hasContent);
- }
- });
- }
-
- private boolean queryHasContent(Uri voicemailUri) {
- if (voicemailUri == null || mContext == null) {
- return false;
- }
-
- ContentResolver contentResolver = mContext.getContentResolver();
- Cursor cursor = contentResolver.query(
- voicemailUri, null, null, null, null);
- try {
- if (cursor != null && cursor.moveToNext()) {
- int duration = cursor.getInt(cursor.getColumnIndex(
- VoicemailContract.Voicemails.DURATION));
- // Convert database duration (seconds) into mDuration (milliseconds)
- mDuration.set(duration > 0 ? duration * 1000 : 0);
- return cursor.getInt(cursor.getColumnIndex(
- VoicemailContract.Voicemails.HAS_CONTENT)) == 1;
- }
- } finally {
- MoreCloseables.closeQuietly(cursor);
- }
- return false;
- }
-
- /**
- * Makes a broadcast request to ask that a voicemail source fetch this content.
- * <p>
- * This method <b>must be called on the ui thread</b>.
- * <p>
- * This method will be called when we realise that we don't have content for this voicemail. It
- * will trigger a broadcast to request that the content be downloaded. It will add a listener to
- * the content resolver so that it will be notified when the has_content field changes. It will
- * also set a timer. If the has_content field changes to true within the allowed time, we will
- * proceed to {@link #prepareContent()}. If the has_content field does not
- * become true within the allowed time, we will update the ui to reflect the fact that content
- * was not available.
- *
- * @return whether issued request to fetch content
- */
- protected boolean requestContent(int code) {
- if (mContext == null || mVoicemailUri == null) {
- return false;
- }
-
- FetchResultHandler tempFetchResultHandler =
- new FetchResultHandler(new Handler(), mVoicemailUri, code);
-
- switch (code) {
- case ARCHIVE_REQUEST:
- mArchiveResultHandlers.add(tempFetchResultHandler);
- break;
- default:
- if (mFetchResultHandler != null) {
- mFetchResultHandler.destroy();
- }
- mView.setIsFetchingContent();
- mFetchResultHandler = tempFetchResultHandler;
- break;
- }
-
- // Send voicemail fetch request.
- Intent intent = new Intent(VoicemailContract.ACTION_FETCH_VOICEMAIL, mVoicemailUri);
- mContext.sendBroadcast(intent);
- return true;
- }
-
- @ThreadSafe
- private class FetchResultHandler extends ContentObserver implements Runnable {
- private AtomicBoolean mIsWaitingForResult = new AtomicBoolean(true);
- private final Handler mFetchResultHandler;
- private final Uri mVoicemailUri;
- private final int mRequestCode;
-
- public FetchResultHandler(Handler handler, Uri uri, int code) {
- super(handler);
- mFetchResultHandler = handler;
- mRequestCode = code;
- mVoicemailUri = uri;
- if (mContext != null) {
- mContext.getContentResolver().registerContentObserver(
- mVoicemailUri, false, this);
- mFetchResultHandler.postDelayed(this, FETCH_CONTENT_TIMEOUT_MS);
- }
- }
-
- /**
- * Stop waiting for content and notify UI if {@link FETCH_CONTENT_TIMEOUT_MS} has elapsed.
- */
- @Override
- public void run() {
- if (mIsWaitingForResult.getAndSet(false) && mContext != null) {
- mContext.getContentResolver().unregisterContentObserver(this);
- if (mView != null) {
- mView.setFetchContentTimeout();
- }
- }
- }
-
- public void destroy() {
- if (mIsWaitingForResult.getAndSet(false) && mContext != null) {
- mContext.getContentResolver().unregisterContentObserver(this);
- mFetchResultHandler.removeCallbacks(this);
- }
- }
-
- @Override
- public void onChange(boolean selfChange) {
- mAsyncTaskExecutor.submit(Tasks.CHECK_CONTENT_AFTER_CHANGE,
- new AsyncTask<Void, Void, Boolean>() {
-
- @Override
- public Boolean doInBackground(Void... params) {
- return queryHasContent(mVoicemailUri);
- }
-
- @Override
- public void onPostExecute(Boolean hasContent) {
- if (hasContent && mContext != null && mIsWaitingForResult.getAndSet(false)) {
- mContext.getContentResolver().unregisterContentObserver(
- FetchResultHandler.this);
- prepareContent();
- if (mRequestCode == ARCHIVE_REQUEST) {
- startArchiveVoicemailTask(mVoicemailUri, true /* archivedByUser */);
- } else if (mRequestCode == SHARE_REQUEST) {
- startArchiveVoicemailTask(mVoicemailUri, false /* archivedByUser */);
- }
- }
- }
- });
- }
- }
-
- /**
- * Prepares the voicemail content for playback.
- * <p>
- * This method will be called once we know that our voicemail has content (according to the
- * content provider). this method asynchronously tries to prepare the data source through the
- * media player. If preparation is successful, the media player will {@link #onPrepared()},
- * and it will call {@link #onError()} otherwise.
- */
- protected void prepareContent() {
- if (mView == null) {
- return;
- }
- Log.d(TAG, "prepareContent");
-
- // Release the previous media player, otherwise there may be failures.
- if (mMediaPlayer != null) {
- mMediaPlayer.release();
- mMediaPlayer = null;
- }
-
- mView.disableUiElements();
- mIsPrepared = false;
-
- try {
- mMediaPlayer = new MediaPlayer();
- mMediaPlayer.setOnPreparedListener(this);
- mMediaPlayer.setOnErrorListener(this);
- mMediaPlayer.setOnCompletionListener(this);
-
- mMediaPlayer.reset();
- mMediaPlayer.setDataSource(mContext, mVoicemailUri);
- mMediaPlayer.setAudioStreamType(VoicemailAudioManager.PLAYBACK_STREAM);
- mMediaPlayer.prepareAsync();
- } catch (IOException e) {
- handleError(e);
- }
- }
-
- /**
- * Once the media player is prepared, enables the UI and adopts the appropriate playback state.
- */
- @Override
- public void onPrepared(MediaPlayer mp) {
- if (mView == null) {
- return;
- }
- Log.d(TAG, "onPrepared");
- mIsPrepared = true;
-
- // Update the duration in the database if it was not previously retrieved
- CallLogAsyncTaskUtil.updateVoicemailDuration(mContext, mVoicemailUri,
- TimeUnit.MILLISECONDS.toSeconds(mMediaPlayer.getDuration()));
-
- mDuration.set(mMediaPlayer.getDuration());
-
- Log.d(TAG, "onPrepared: mPosition=" + mPosition);
- mView.setClipPosition(mPosition, mDuration.get());
- mView.enableUiElements();
- mView.setSuccess();
- mMediaPlayer.seekTo(mPosition);
-
- if (mIsPlaying) {
- resumePlayback();
- } else {
- pausePlayback();
- }
- }
-
- /**
- * Invoked if preparing the media player fails, for example, if file is missing or the voicemail
- * is an unknown file format that can't be played.
- */
- @Override
- public boolean onError(MediaPlayer mp, int what, int extra) {
- handleError(new IllegalStateException("MediaPlayer error listener invoked: " + extra));
- return true;
- }
-
- protected void handleError(Exception e) {
- Log.d(TAG, "handleError: Could not play voicemail " + e);
-
- if (mIsPrepared) {
- mMediaPlayer.release();
- mMediaPlayer = null;
- mIsPrepared = false;
- }
-
- if (mView != null) {
- mView.onPlaybackError();
- }
-
- mPosition = 0;
- mIsPlaying = false;
- }
-
- /**
- * After done playing the voicemail clip, reset the clip position to the start.
- */
- @Override
- public void onCompletion(MediaPlayer mediaPlayer) {
- pausePlayback();
-
- // Reset the seekbar position to the beginning.
- mPosition = 0;
- if (mView != null) {
- mView.setClipPosition(0, mDuration.get());
- }
- }
-
- /**
- * Only play voicemail when audio focus is granted. When it is lost (usually by another
- * application requesting focus), pause playback.
- *
- * @param gainedFocus {@code true} if the audio focus was gained, {@code} false otherwise.
- */
- public void onAudioFocusChange(boolean gainedFocus) {
- if (mIsPlaying == gainedFocus) {
- // Nothing new here, just exit.
- return;
- }
-
- if (!mIsPlaying) {
- resumePlayback();
- } else {
- pausePlayback();
- }
- }
-
- /**
- * Resumes voicemail playback at the clip position stored by the presenter. Null-op if already
- * playing.
- */
- public void resumePlayback() {
- if (mView == null) {
- return;
- }
-
- if (!mIsPrepared) {
- /*
- * Check content before requesting content to avoid duplicated requests. It is possible
- * that the UI doesn't know content has arrived if the fetch took too long causing a
- * timeout, but succeeded.
- */
- checkForContent(new OnContentCheckedListener() {
- @Override
- public void onContentChecked(boolean hasContent) {
- if (!hasContent) {
- // No local content, download from server. Queue playing if the request was
- // issued,
- mIsPlaying = requestContent(PLAYBACK_REQUEST);
- } else {
- // Queue playing once the media play loaded the content.
- mIsPlaying = true;
- prepareContent();
- }
- }
- });
- return;
- }
-
- mIsPlaying = true;
-
- if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
- // Clamp the start position between 0 and the duration.
- mPosition = Math.max(0, Math.min(mPosition, mDuration.get()));
-
- mMediaPlayer.seekTo(mPosition);
-
- try {
- // Grab audio focus.
- // Can throw RejectedExecutionException.
- mVoicemailAudioManager.requestAudioFocus();
- mMediaPlayer.start();
- setSpeakerphoneOn(mIsSpeakerphoneOn);
- } catch (RejectedExecutionException e) {
- handleError(e);
- }
- }
-
- Log.d(TAG, "Resumed playback at " + mPosition + ".");
- mView.onPlaybackStarted(mDuration.get(), getScheduledExecutorServiceInstance());
- }
-
- /**
- * Pauses voicemail playback at the current position. Null-op if already paused.
- */
- public void pausePlayback() {
- if (!mIsPrepared) {
- return;
- }
-
- mIsPlaying = false;
-
- if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
- mMediaPlayer.pause();
- }
-
- mPosition = mMediaPlayer == null ? 0 : mMediaPlayer.getCurrentPosition();
-
- Log.d(TAG, "Paused playback at " + mPosition + ".");
-
- if (mView != null) {
- mView.onPlaybackStopped();
- }
-
- mVoicemailAudioManager.abandonAudioFocus();
-
- if (mActivity != null) {
- mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
- disableProximitySensor(true /* waitForFarState */);
- }
-
- /**
- * Pauses playback when the user starts seeking the position, and notes whether the voicemail is
- * playing to know whether to resume playback once the user selects a new position.
- */
- public void pausePlaybackForSeeking() {
- if (mMediaPlayer != null) {
- mShouldResumePlaybackAfterSeeking = mMediaPlayer.isPlaying();
- }
- pausePlayback();
- }
-
- public void resumePlaybackAfterSeeking(int desiredPosition) {
- mPosition = desiredPosition;
- if (mShouldResumePlaybackAfterSeeking) {
- mShouldResumePlaybackAfterSeeking = false;
- resumePlayback();
- }
- }
-
- /**
- * Seek to position. This is called when user manually seek the playback. It could be either
- * by touch or volume button while in talkback mode.
- * @param position
- */
- public void seek(int position) {
- mPosition = position;
- }
-
- private void enableProximitySensor() {
- if (mProximityWakeLock == null || mIsSpeakerphoneOn || !mIsPrepared
- || mMediaPlayer == null || !mMediaPlayer.isPlaying()) {
- return;
- }
-
- if (!mProximityWakeLock.isHeld()) {
- Log.i(TAG, "Acquiring proximity wake lock");
- mProximityWakeLock.acquire();
- } else {
- Log.i(TAG, "Proximity wake lock already acquired");
- }
- }
-
- private void disableProximitySensor(boolean waitForFarState) {
- if (mProximityWakeLock == null) {
- return;
- }
- if (mProximityWakeLock.isHeld()) {
- Log.i(TAG, "Releasing proximity wake lock");
- int flags = waitForFarState ? PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY : 0;
- mProximityWakeLock.release(flags);
- } else {
- Log.i(TAG, "Proximity wake lock already released");
- }
- }
-
- /**
- * This is for use by UI interactions only. It simplifies UI logic.
- */
- public void toggleSpeakerphone() {
- mVoicemailAudioManager.setSpeakerphoneOn(!mIsSpeakerphoneOn);
- setSpeakerphoneOn(!mIsSpeakerphoneOn);
- }
-
- /**
- * This method only handles app-level changes to the speakerphone. Audio layer changes should
- * be handled separately. This is so that the VoicemailAudioManager can trigger changes to
- * the presenter without the presenter triggering the audio manager and duplicating actions.
- */
- public void setSpeakerphoneOn(boolean on) {
- if (mView == null) {
- return;
- }
-
- mView.onSpeakerphoneOn(on);
-
- mIsSpeakerphoneOn = on;
-
- // This should run even if speakerphone is not being toggled because we may be switching
- // from earpiece to headphone and vise versa. Also upon initial setup the default audio
- // source is the earpiece, so we want to trigger the proximity sensor.
- if (mIsPlaying) {
- if (on || mVoicemailAudioManager.isWiredHeadsetPluggedIn()) {
- disableProximitySensor(false /* waitForFarState */);
- if (mIsPrepared && mMediaPlayer != null && mMediaPlayer.isPlaying()) {
- mActivity.getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
- } else {
- enableProximitySensor();
- if (mActivity != null) {
- mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
- }
- }
- }
-
- public void setOnVoicemailDeletedListener(OnVoicemailDeletedListener listener) {
- mOnVoicemailDeletedListener = listener;
- }
-
- public int getMediaPlayerPosition() {
- return mIsPrepared && mMediaPlayer != null ? mMediaPlayer.getCurrentPosition() : 0;
- }
-
- public void notifyUiOfArchiveResult(Uri voicemailUri, boolean archived) {
- if (mView == null) {
- return;
- }
- if (archived) {
- mView.onVoicemailArchiveSucceded(voicemailUri);
- } else {
- mView.onVoicemailArchiveFailed(voicemailUri);
- }
- }
-
- /* package */ void onVoicemailDeleted() {
- // Trampoline the event notification to the interested listener.
- if (mOnVoicemailDeletedListener != null) {
- mOnVoicemailDeletedListener.onVoicemailDeleted(mVoicemailUri);
- }
- }
-
- /* package */ void onVoicemailDeleteUndo() {
- // Trampoline the event notification to the interested listener.
- if (mOnVoicemailDeletedListener != null) {
- mOnVoicemailDeletedListener.onVoicemailDeleteUndo();
- }
- }
-
- /* package */ void onVoicemailDeletedInDatabase() {
- // Trampoline the event notification to the interested listener.
- if (mOnVoicemailDeletedListener != null) {
- mOnVoicemailDeletedListener.onVoicemailDeletedInDatabase();
- }
- }
-
- private static synchronized ScheduledExecutorService getScheduledExecutorServiceInstance() {
- if (mScheduledExecutorService == null) {
- mScheduledExecutorService = Executors.newScheduledThreadPool(NUMBER_OF_THREADS_IN_POOL);
- }
- return mScheduledExecutorService;
- }
-
- /**
- * If voicemail has already been downloaded, go straight to archiving. Otherwise, request
- * the voicemail content first.
- */
- public void archiveContent(final Uri voicemailUri, final boolean archivedByUser) {
- checkForContent(new OnContentCheckedListener() {
- @Override
- public void onContentChecked(boolean hasContent) {
- if (!hasContent) {
- requestContent(archivedByUser ? ARCHIVE_REQUEST : SHARE_REQUEST);
- } else {
- startArchiveVoicemailTask(voicemailUri, archivedByUser);
- }
- }
- });
- }
-
- /**
- * Asynchronous task used to archive a voicemail given its uri.
- */
- protected void startArchiveVoicemailTask(final Uri voicemailUri, final boolean archivedByUser) {
- mVoicemailAsyncTaskUtil.archiveVoicemailContent(
- new VoicemailAsyncTaskUtil.OnArchiveVoicemailListener() {
- @Override
- public void onArchiveVoicemail(final Uri archivedVoicemailUri) {
- if (archivedVoicemailUri == null) {
- notifyUiOfArchiveResult(voicemailUri, false);
- return;
- }
-
- if (archivedByUser) {
- setArchivedVoicemailStatusAndUpdateUI(voicemailUri,
- archivedVoicemailUri, true);
- } else {
- sendShareIntent(archivedVoicemailUri);
- }
- }
- }, voicemailUri);
- }
-
- /**
- * Sends the intent for sharing the voicemail file.
- */
- protected void sendShareIntent(final Uri voicemailUri) {
- mVoicemailAsyncTaskUtil.getVoicemailFilePath(
- new VoicemailAsyncTaskUtil.OnGetArchivedVoicemailFilePathListener() {
- @Override
- public void onGetArchivedVoicemailFilePath(String filePath) {
- mView.enableUiElements();
- if (filePath == null) {
- mView.setFetchContentTimeout();
- return;
- }
- Uri voicemailFileUri = FileProvider.getUriForFile(
- mContext,
- mContext.getString(R.string.contacts_file_provider_authority),
- new File(filePath));
- mContext.startActivity(Intent.createChooser(
- getShareIntent(voicemailFileUri),
- mContext.getResources().getText(
- R.string.call_log_share_voicemail)));
- }
- }, voicemailUri);
- }
-
- /** Sets archived_by_user field to the given boolean and updates the URI. */
- private void setArchivedVoicemailStatusAndUpdateUI(
- final Uri voicemailUri,
- final Uri archivedVoicemailUri,
- boolean status) {
- mVoicemailAsyncTaskUtil.setVoicemailArchiveStatus(
- new VoicemailAsyncTaskUtil.OnSetVoicemailArchiveStatusListener() {
- @Override
- public void onSetVoicemailArchiveStatus(boolean success) {
- notifyUiOfArchiveResult(voicemailUri, success);
- }
- }, archivedVoicemailUri, status);
- }
-
- private Intent getShareIntent(Uri voicemailFileUri) {
- Intent shareIntent = new Intent();
- shareIntent.setAction(Intent.ACTION_SEND);
- shareIntent.putExtra(Intent.EXTRA_STREAM, voicemailFileUri);
- shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- shareIntent.setType(mContext.getContentResolver()
- .getType(voicemailFileUri));
- return shareIntent;
- }
-
- @VisibleForTesting
- public boolean isPlaying() {
- return mIsPlaying;
- }
-
- @VisibleForTesting
- public boolean isSpeakerphoneOn() {
- return mIsSpeakerphoneOn;
- }
-
- @VisibleForTesting
- public void clearInstance() {
- sInstance = null;
- }
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailStatusHelper.java b/src/com/android/dialer/voicemail/VoicemailStatusHelper.java
deleted file mode 100644
index d790b7764..000000000
--- a/src/com/android/dialer/voicemail/VoicemailStatusHelper.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.dialer.voicemail;
-
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.VoicemailContract.Status;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import java.util.List;
-
-/**
- * Interface used by the call log UI to determine what user message, if any, related to voicemail
- * source status needs to be shown. The messages are returned in the order of importance.
- * <p>
- * The implementation of this interface interacts with the voicemail content provider to fetch
- * statuses of all the registered voicemail sources and determines if any status message needs to
- * be shown. The user of this interface must observe/listen to provider changes and invoke
- * this class to check if any message needs to be shown.
- */
-public interface VoicemailStatusHelper {
- @VisibleForTesting
- public class StatusMessage {
- /** Package of the source on behalf of which this message has to be shown.*/
- public final String sourcePackage;
- /**
- * The string resource id of the status message that should be shown in the call log
- * page. Set to -1, if this message is not to be shown in call log.
- */
- public final int callLogMessageId;
- /**
- * The string resource id of the status message that should be shown in the call details
- * page. Set to -1, if this message is not to be shown in call details page.
- */
- public final int callDetailsMessageId;
- /** The string resource id of the action message that should be shown. */
- public final int actionMessageId;
- /** URI for the corrective action, where applicable. Null if no action URI is available. */
- public final Uri actionUri;
-
- public StatusMessage(String sourcePackage, int callLogMessageId, int callDetailsMessageId,
- int actionMessageId, Uri actionUri) {
- this.sourcePackage = sourcePackage;
- this.callLogMessageId = callLogMessageId;
- this.callDetailsMessageId = callDetailsMessageId;
- this.actionMessageId = actionMessageId;
- this.actionUri = actionUri;
- }
-
- /** Whether this message should be shown in the call log page. */
- public boolean showInCallLog() {
- return callLogMessageId != -1;
- }
-
- /** Whether this message should be shown in the call details page. */
- public boolean showInCallDetails() {
- return callDetailsMessageId != -1;
- }
- }
-
- /**
- * Returns a list of messages, in the order or priority that should be shown to the user. An
- * empty list is returned if no message needs to be shown.
- * @param cursor The cursor pointing to the query on {@link Status#CONTENT_URI}. The projection
- * to be used is defined by the implementation class of this interface.
- */
- @VisibleForTesting
- public List<StatusMessage> getStatusMessages(Cursor cursor);
-
- /**
- * Returns the number of active voicemail sources installed.
- * <p>
- * The number of sources is counted by querying the voicemail status table.
- */
- public int getNumberActivityVoicemailSources(Cursor cursor);
-}
diff --git a/src/com/android/dialer/voicemail/VoicemailStatusHelperImpl.java b/src/com/android/dialer/voicemail/VoicemailStatusHelperImpl.java
deleted file mode 100644
index ff1786862..000000000
--- a/src/com/android/dialer/voicemail/VoicemailStatusHelperImpl.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * 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.dialer.voicemail;
-
-import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE_CAN_BE_CONFIGURED;
-import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE_OK;
-import static android.provider.VoicemailContract.Status.DATA_CHANNEL_STATE_NO_CONNECTION;
-import static android.provider.VoicemailContract.Status.DATA_CHANNEL_STATE_OK;
-import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING;
-import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION;
-import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_OK;
-
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.VoicemailContract.Status;
-
-import com.android.contacts.common.util.UriUtils;
-import com.android.dialer.R;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-/** Implementation of {@link VoicemailStatusHelper}. */
-public class VoicemailStatusHelperImpl implements VoicemailStatusHelper {
- private static final int SOURCE_PACKAGE_INDEX = 0;
- private static final int CONFIGURATION_STATE_INDEX = 1;
- private static final int DATA_CHANNEL_STATE_INDEX = 2;
- private static final int NOTIFICATION_CHANNEL_STATE_INDEX = 3;
- private static final int SETTINGS_URI_INDEX = 4;
- private static final int VOICEMAIL_ACCESS_URI_INDEX = 5;
- private static final int NUM_COLUMNS = 6;
- /** Projection on the voicemail_status table used by this class. */
- public static final String[] PROJECTION = new String[NUM_COLUMNS];
- static {
- PROJECTION[SOURCE_PACKAGE_INDEX] = Status.SOURCE_PACKAGE;
- PROJECTION[CONFIGURATION_STATE_INDEX] = Status.CONFIGURATION_STATE;
- PROJECTION[DATA_CHANNEL_STATE_INDEX] = Status.DATA_CHANNEL_STATE;
- PROJECTION[NOTIFICATION_CHANNEL_STATE_INDEX] = Status.NOTIFICATION_CHANNEL_STATE;
- PROJECTION[SETTINGS_URI_INDEX] = Status.SETTINGS_URI;
- PROJECTION[VOICEMAIL_ACCESS_URI_INDEX] = Status.VOICEMAIL_ACCESS_URI;
- }
-
- /** Possible user actions. */
- public static enum Action {
- NONE(-1),
- CALL_VOICEMAIL(R.string.voicemail_status_action_call_server),
- CONFIGURE_VOICEMAIL(R.string.voicemail_status_action_configure);
-
- private final int mMessageId;
- private Action(int messageId) {
- mMessageId = messageId;
- }
-
- public int getMessageId() {
- return mMessageId;
- }
- }
-
- /**
- * Overall state of the source status. Each state is associated with the corresponding display
- * string and the corrective action. The states are also assigned a relative priority which is
- * used to order the messages from different sources.
- */
- private static enum OverallState {
- // TODO: Add separate string for call details and call log pages for the states that needs
- // to be shown in both.
- /** Both notification and data channel are not working. */
- NO_CONNECTION(0, Action.CALL_VOICEMAIL, R.string.voicemail_status_voicemail_not_available,
- R.string.voicemail_status_audio_not_available),
- /** Notifications working, but data channel is not working. Audio cannot be downloaded. */
- NO_DATA(1, Action.CALL_VOICEMAIL, R.string.voicemail_status_voicemail_not_available,
- R.string.voicemail_status_audio_not_available),
- /** Messages are known to be waiting but data channel is not working. */
- MESSAGE_WAITING(2, Action.CALL_VOICEMAIL, R.string.voicemail_status_messages_waiting,
- R.string.voicemail_status_audio_not_available),
- /** Notification channel not working, but data channel is. */
- NO_NOTIFICATIONS(3, Action.CALL_VOICEMAIL,
- R.string.voicemail_status_voicemail_not_available),
- /** Invite user to set up voicemail. */
- INVITE_FOR_CONFIGURATION(4, Action.CONFIGURE_VOICEMAIL,
- R.string.voicemail_status_configure_voicemail),
- /**
- * No detailed notifications, but data channel is working.
- * This is normal mode of operation for certain sources. No action needed.
- */
- NO_DETAILED_NOTIFICATION(5, Action.NONE, -1),
- /** Visual voicemail not yet set up. No local action needed. */
- NOT_CONFIGURED(6, Action.NONE, -1),
- /** Everything is OK. */
- OK(7, Action.NONE, -1),
- /** If one or more state value set by the source is not valid. */
- INVALID(8, Action.NONE, -1);
-
- private final int mPriority;
- private final Action mAction;
- private final int mCallLogMessageId;
- private final int mCallDetailsMessageId;
-
- private OverallState(int priority, Action action, int callLogMessageId) {
- this(priority, action, callLogMessageId, -1);
- }
-
- private OverallState(int priority, Action action, int callLogMessageId,
- int callDetailsMessageId) {
- mPriority = priority;
- mAction = action;
- mCallLogMessageId = callLogMessageId;
- mCallDetailsMessageId = callDetailsMessageId;
- }
-
- public Action getAction() {
- return mAction;
- }
-
- public int getPriority() {
- return mPriority;
- }
-
- public int getCallLogMessageId() {
- return mCallLogMessageId;
- }
-
- public int getCallDetailsMessageId() {
- return mCallDetailsMessageId;
- }
- }
-
- /** A wrapper on {@link StatusMessage} which additionally stores the priority of the message. */
- private static class MessageStatusWithPriority {
- private final StatusMessage mMessage;
- private final int mPriority;
-
- public MessageStatusWithPriority(StatusMessage message, int priority) {
- mMessage = message;
- mPriority = priority;
- }
- }
-
- @Override
- public List<StatusMessage> getStatusMessages(Cursor cursor) {
- List<MessageStatusWithPriority> messages =
- new ArrayList<VoicemailStatusHelperImpl.MessageStatusWithPriority>();
- cursor.moveToPosition(-1);
- while(cursor.moveToNext()) {
- MessageStatusWithPriority message = getMessageForStatusEntry(cursor);
- if (message != null) {
- messages.add(message);
- }
- }
- // Finally reorder the messages by their priority.
- return reorderMessages(messages);
- }
-
- @Override
- public int getNumberActivityVoicemailSources(Cursor cursor) {
- int count = 0;
- cursor.moveToPosition(-1);
- while(cursor.moveToNext()) {
- if (isVoicemailSourceActive(cursor)) {
- ++count;
- }
- }
- return count;
- }
-
- /** Returns whether the source status in the cursor corresponds to an active source. */
- private boolean isVoicemailSourceActive(Cursor cursor) {
- return cursor.getString(SOURCE_PACKAGE_INDEX) != null
- && cursor.getInt(CONFIGURATION_STATE_INDEX) == Status.CONFIGURATION_STATE_OK;
- }
-
- private List<StatusMessage> reorderMessages(List<MessageStatusWithPriority> messageWrappers) {
- Collections.sort(messageWrappers, new Comparator<MessageStatusWithPriority>() {
- @Override
- public int compare(MessageStatusWithPriority msg1, MessageStatusWithPriority msg2) {
- return msg1.mPriority - msg2.mPriority;
- }
- });
- List<StatusMessage> reorderMessages = new ArrayList<VoicemailStatusHelper.StatusMessage>();
- // Copy the ordered message objects into the final list.
- for (MessageStatusWithPriority messageWrapper : messageWrappers) {
- reorderMessages.add(messageWrapper.mMessage);
- }
- return reorderMessages;
- }
-
- /**
- * Returns the message for the status entry pointed to by the cursor.
- */
- private MessageStatusWithPriority getMessageForStatusEntry(Cursor cursor) {
- final String sourcePackage = cursor.getString(SOURCE_PACKAGE_INDEX);
- if (sourcePackage == null) {
- return null;
- }
- final OverallState overallState = getOverallState(cursor.getInt(CONFIGURATION_STATE_INDEX),
- cursor.getInt(DATA_CHANNEL_STATE_INDEX),
- cursor.getInt(NOTIFICATION_CHANNEL_STATE_INDEX));
- final Action action = overallState.getAction();
-
- // No source package or no action, means no message shown.
- if (action == Action.NONE) {
- return null;
- }
-
- Uri actionUri = null;
- if (action == Action.CALL_VOICEMAIL) {
- actionUri = UriUtils.parseUriOrNull(cursor.getString(VOICEMAIL_ACCESS_URI_INDEX));
- // Even if actionUri is null, it is still be useful to show the notification.
- } else if (action == Action.CONFIGURE_VOICEMAIL) {
- actionUri = UriUtils.parseUriOrNull(cursor.getString(SETTINGS_URI_INDEX));
- // If there is no settings URI, there is no point in showing the notification.
- if (actionUri == null) {
- return null;
- }
- }
- return new MessageStatusWithPriority(
- new StatusMessage(sourcePackage, overallState.getCallLogMessageId(),
- overallState.getCallDetailsMessageId(), action.getMessageId(),
- actionUri),
- overallState.getPriority());
- }
-
- private OverallState getOverallState(int configurationState, int dataChannelState,
- int notificationChannelState) {
- if (configurationState == CONFIGURATION_STATE_OK) {
- // Voicemail is configured. Let's see how is the data channel.
- if (dataChannelState == DATA_CHANNEL_STATE_OK) {
- // Data channel is fine. What about notification channel?
- if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_OK) {
- return OverallState.OK;
- } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING) {
- return OverallState.NO_DETAILED_NOTIFICATION;
- } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_NO_CONNECTION) {
- return OverallState.NO_NOTIFICATIONS;
- }
- } else if (dataChannelState == DATA_CHANNEL_STATE_NO_CONNECTION) {
- // Data channel is not working. What about notification channel?
- if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_OK) {
- return OverallState.NO_DATA;
- } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING) {
- return OverallState.MESSAGE_WAITING;
- } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_NO_CONNECTION) {
- return OverallState.NO_CONNECTION;
- }
- }
- } else if (configurationState == CONFIGURATION_STATE_CAN_BE_CONFIGURED) {
- // Voicemail not configured. data/notification channel states are irrelevant.
- return OverallState.INVITE_FOR_CONFIGURATION;
- } else if (configurationState == Status.CONFIGURATION_STATE_NOT_CONFIGURED) {
- // Voicemail not configured. data/notification channel states are irrelevant.
- return OverallState.NOT_CONFIGURED;
- }
- // Will reach here only if the source has set an invalid value.
- return OverallState.INVALID;
- }
-}
diff --git a/src/com/android/dialer/voicemail/WiredHeadsetManager.java b/src/com/android/dialer/voicemail/WiredHeadsetManager.java
deleted file mode 100644
index 7351f4f01..000000000
--- a/src/com/android/dialer/voicemail/WiredHeadsetManager.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.dialer.voicemail;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.AudioManager;
-import android.util.Log;
-
-/** Listens for and caches headset state. */
-class WiredHeadsetManager {
- private static final String TAG = WiredHeadsetManager.class.getSimpleName();
-
- interface Listener {
- void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn);
- }
-
- /** Receiver for wired headset plugged and unplugged events. */
- private class WiredHeadsetBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (AudioManager.ACTION_HEADSET_PLUG.equals(intent.getAction())) {
- boolean isPluggedIn = intent.getIntExtra("state", 0) == 1;
- Log.v(TAG, "ACTION_HEADSET_PLUG event, plugged in: " + isPluggedIn);
- onHeadsetPluggedInChanged(isPluggedIn);
- }
- }
- }
-
- private final WiredHeadsetBroadcastReceiver mReceiver;
- private boolean mIsPluggedIn;
- private Listener mListener;
- private Context mContext;
-
- WiredHeadsetManager(Context context) {
- mContext = context;
- mReceiver = new WiredHeadsetBroadcastReceiver();
-
- AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- mIsPluggedIn = audioManager.isWiredHeadsetOn();
-
- }
-
- void setListener(Listener listener) {
- mListener = listener;
- }
-
- boolean isPluggedIn() {
- return mIsPluggedIn;
- }
-
- void registerReceiver() {
- // Register for misc other intent broadcasts.
- IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
- mContext.registerReceiver(mReceiver, intentFilter);
- }
-
- void unregisterReceiver() {
- mContext.unregisterReceiver(mReceiver);
- }
-
- private void onHeadsetPluggedInChanged(boolean isPluggedIn) {
- if (mIsPluggedIn != isPluggedIn) {
- Log.v(TAG, "onHeadsetPluggedInChanged, mIsPluggedIn: " + mIsPluggedIn + " -> "
- + isPluggedIn);
- boolean oldIsPluggedIn = mIsPluggedIn;
- mIsPluggedIn = isPluggedIn;
- if (mListener != null) {
- mListener.onWiredHeadsetPluggedInChanged(oldIsPluggedIn, mIsPluggedIn);
- }
- }
- }
-} \ No newline at end of file
diff --git a/src/com/android/dialer/widget/ActionBarController.java b/src/com/android/dialer/widget/ActionBarController.java
deleted file mode 100644
index edf57b163..000000000
--- a/src/com/android/dialer/widget/ActionBarController.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.widget;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.os.Bundle;
-import android.util.Log;
-
-import com.android.dialer.DialtactsActivity;
-import com.android.phone.common.animation.AnimUtils.AnimationCallback;
-
-/**
- * Controls the various animated properties of the actionBar: showing/hiding, fading/revealing,
- * and collapsing/expanding, and assigns suitable properties to the actionBar based on the
- * current state of the UI.
- */
-public class ActionBarController {
- public static final boolean DEBUG = DialtactsActivity.DEBUG;
- public static final String TAG = "ActionBarController";
- private static final String KEY_IS_SLID_UP = "key_actionbar_is_slid_up";
- private static final String KEY_IS_FADED_OUT = "key_actionbar_is_faded_out";
- private static final String KEY_IS_EXPANDED = "key_actionbar_is_expanded";
-
- private ActivityUi mActivityUi;
- private SearchEditTextLayout mSearchBox;
-
- private boolean mIsActionBarSlidUp;
-
- private final AnimationCallback mFadeOutCallback = new AnimationCallback() {
- @Override
- public void onAnimationEnd() {
- slideActionBar(true /* slideUp */, false /* animate */);
- }
-
- @Override
- public void onAnimationCancel() {
- slideActionBar(true /* slideUp */, false /* animate */);
- }
- };
-
- public interface ActivityUi {
- public boolean isInSearchUi();
- public boolean hasSearchQuery();
- public boolean shouldShowActionBar();
- public int getActionBarHeight();
- public int getActionBarHideOffset();
- public void setActionBarHideOffset(int offset);
- }
-
- public ActionBarController(ActivityUi activityUi, SearchEditTextLayout searchBox) {
- mActivityUi = activityUi;
- mSearchBox = searchBox;
- }
-
- /**
- * @return Whether or not the action bar is currently showing (both slid down and visible)
- */
- public boolean isActionBarShowing() {
- return !mIsActionBarSlidUp && !mSearchBox.isFadedOut();
- }
-
- /**
- * Called when the user has tapped on the collapsed search box, to start a new search query.
- */
- public void onSearchBoxTapped() {
- if (DEBUG) {
- Log.d(TAG, "OnSearchBoxTapped: isInSearchUi " + mActivityUi.isInSearchUi());
- }
- if (!mActivityUi.isInSearchUi()) {
- mSearchBox.expand(true /* animate */, true /* requestFocus */);
- }
- }
-
- /**
- * Called when search UI has been exited for some reason.
- */
- public void onSearchUiExited() {
- if (DEBUG) {
- Log.d(TAG, "OnSearchUIExited: isExpanded " + mSearchBox.isExpanded()
- + " isFadedOut: " + mSearchBox.isFadedOut()
- + " shouldShowActionBar: " + mActivityUi.shouldShowActionBar());
- }
- if (mSearchBox.isExpanded()) {
- mSearchBox.collapse(true /* animate */);
- }
- if (mSearchBox.isFadedOut()) {
- mSearchBox.fadeIn();
- }
-
- if (mActivityUi.shouldShowActionBar()) {
- slideActionBar(false /* slideUp */, false /* animate */);
- } else {
- slideActionBar(true /* slideUp */, false /* animate */);
- }
- }
-
- /**
- * Called to indicate that the user is trying to hide the dialpad. Should be called before
- * any state changes have actually occurred.
- */
- public void onDialpadDown() {
- if (DEBUG) {
- Log.d(TAG, "OnDialpadDown: isInSearchUi " + mActivityUi.isInSearchUi()
- + " hasSearchQuery: " + mActivityUi.hasSearchQuery()
- + " isFadedOut: " + mSearchBox.isFadedOut()
- + " isExpanded: " + mSearchBox.isExpanded());
- }
- if (mActivityUi.isInSearchUi()) {
- if (mActivityUi.hasSearchQuery()) {
- if (mSearchBox.isFadedOut()) {
- mSearchBox.setVisible(true);
- }
- if (!mSearchBox.isExpanded()) {
- mSearchBox.expand(false /* animate */, false /* requestFocus */);
- }
- slideActionBar(false /* slideUp */, true /* animate */);
- } else {
- mSearchBox.fadeIn();
- }
- }
- }
-
- /**
- * Called to indicate that the user is trying to show the dialpad. Should be called before
- * any state changes have actually occurred.
- */
- public void onDialpadUp() {
- if (DEBUG) {
- Log.d(TAG, "OnDialpadUp: isInSearchUi " + mActivityUi.isInSearchUi());
- }
- if (mActivityUi.isInSearchUi()) {
- slideActionBar(true /* slideUp */, true /* animate */);
- } else {
- // From the lists fragment
- mSearchBox.fadeOut(mFadeOutCallback);
- }
- }
-
- public void slideActionBar(boolean slideUp, boolean animate) {
- if (DEBUG) {
- Log.d(TAG, "Sliding actionBar - up: " + slideUp + " animate: " + animate);
- }
- if (animate) {
- ValueAnimator animator =
- slideUp ? ValueAnimator.ofFloat(0, 1) : ValueAnimator.ofFloat(1, 0);
- animator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- final float value = (float) animation.getAnimatedValue();
- setHideOffset(
- (int) (mActivityUi.getActionBarHeight() * value));
- }
- });
- animator.start();
- } else {
- setHideOffset(slideUp ? mActivityUi.getActionBarHeight() : 0);
- }
- mIsActionBarSlidUp = slideUp;
- }
-
- public void setAlpha(float alphaValue) {
- mSearchBox.animate().alpha(alphaValue).start();
- }
-
- public void setHideOffset(int offset) {
- mIsActionBarSlidUp = offset >= mActivityUi.getActionBarHeight();
- mActivityUi.setActionBarHideOffset(offset);
- }
-
- /**
- * @return The offset the action bar is being translated upwards by
- */
- public int getHideOffset() {
- return mActivityUi.getActionBarHideOffset();
- }
-
- public int getActionBarHeight() {
- return mActivityUi.getActionBarHeight();
- }
-
- /**
- * Saves the current state of the action bar into a provided {@link Bundle}
- */
- public void saveInstanceState(Bundle outState) {
- outState.putBoolean(KEY_IS_SLID_UP, mIsActionBarSlidUp);
- outState.putBoolean(KEY_IS_FADED_OUT, mSearchBox.isFadedOut());
- outState.putBoolean(KEY_IS_EXPANDED, mSearchBox.isExpanded());
- }
-
- /**
- * Restores the action bar state from a provided {@link Bundle}.
- */
- public void restoreInstanceState(Bundle inState) {
- mIsActionBarSlidUp = inState.getBoolean(KEY_IS_SLID_UP);
-
- final boolean isSearchBoxFadedOut = inState.getBoolean(KEY_IS_FADED_OUT);
- if (isSearchBoxFadedOut) {
- if (!mSearchBox.isFadedOut()) {
- mSearchBox.setVisible(false);
- }
- } else if (mSearchBox.isFadedOut()) {
- mSearchBox.setVisible(true);
- }
-
- final boolean isSearchBoxExpanded = inState.getBoolean(KEY_IS_EXPANDED);
- if (isSearchBoxExpanded) {
- if (!mSearchBox.isExpanded()) {
- mSearchBox.expand(false, false);
- }
- } else if (mSearchBox.isExpanded()) {
- mSearchBox.collapse(false);
- }
- }
-
- /**
- * This should be called after onCreateOptionsMenu has been called, when the actionbar has
- * been laid out and actually has a height.
- */
- public void restoreActionBarOffset() {
- slideActionBar(mIsActionBarSlidUp /* slideUp */, false /* animate */);
- }
-
- @VisibleForTesting
- public boolean getIsActionBarSlidUp() {
- return mIsActionBarSlidUp;
- }
-}
diff --git a/src/com/android/dialer/widget/EmptyContentView.java b/src/com/android/dialer/widget/EmptyContentView.java
deleted file mode 100644
index 719fd3ff8..000000000
--- a/src/com/android/dialer/widget/EmptyContentView.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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.dialer.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.dialer.R;
-
-public class EmptyContentView extends LinearLayout implements View.OnClickListener {
-
- public static final int NO_LABEL = 0;
- public static final int NO_IMAGE = 0;
-
- private ImageView mImageView;
- private TextView mDescriptionView;
- private TextView mActionView;
- private OnEmptyViewActionButtonClickedListener mOnActionButtonClickedListener;
-
- public interface OnEmptyViewActionButtonClickedListener {
- public void onEmptyViewActionButtonClicked();
- }
-
- public EmptyContentView(Context context) {
- this(context, null);
- }
-
- public EmptyContentView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public EmptyContentView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public EmptyContentView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- setOrientation(LinearLayout.VERTICAL);
-
- final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- inflater.inflate(R.layout.empty_content_view, this);
- // Don't let touches fall through the empty view.
- setClickable(true);
- mImageView = (ImageView) findViewById(R.id.emptyListViewImage);
- mDescriptionView = (TextView) findViewById(R.id.emptyListViewMessage);
- mActionView = (TextView) findViewById(R.id.emptyListViewAction);
- mActionView.setOnClickListener(this);
- }
-
- public void setDescription(int resourceId) {
- if (resourceId == NO_LABEL) {
- mDescriptionView.setText(null);
- mDescriptionView.setVisibility(View.GONE);
- } else {
- mDescriptionView.setText(resourceId);
- mDescriptionView.setVisibility(View.VISIBLE);
- }
- }
-
- public void setImage(int resourceId) {
- if (resourceId == NO_LABEL) {
- mImageView.setImageDrawable(null);
- mImageView.setVisibility(View.GONE);
- } else {
- mImageView.setImageResource(resourceId);
- mImageView.setVisibility(View.VISIBLE);
- }
- }
-
- public void setActionLabel(int resourceId) {
- if (resourceId == NO_LABEL) {
- mActionView.setText(null);
- mActionView.setVisibility(View.GONE);
- } else {
- mActionView.setText(resourceId);
- mActionView.setVisibility(View.VISIBLE);
- }
- }
-
- public boolean isShowingContent() {
- return mImageView.getVisibility() == View.VISIBLE
- || mDescriptionView.getVisibility() == View.VISIBLE
- || mActionView.getVisibility() == View.VISIBLE;
- }
-
- public void setActionClickedListener(OnEmptyViewActionButtonClickedListener listener) {
- mOnActionButtonClickedListener = listener;
- }
-
- @Override
- public void onClick(View v) {
- if (mOnActionButtonClickedListener != null) {
- mOnActionButtonClickedListener.onEmptyViewActionButtonClicked();
- }
- }
-}
diff --git a/src/com/android/dialer/widget/SearchEditTextLayout.java b/src/com/android/dialer/widget/SearchEditTextLayout.java
deleted file mode 100644
index 4f100dc44..000000000
--- a/src/com/android/dialer/widget/SearchEditTextLayout.java
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (C) 2014 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.dialer.widget;
-
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.EditText;
-import android.widget.FrameLayout;
-
-import com.android.dialer.R;
-import com.android.dialer.util.DialerUtils;
-import com.android.phone.common.animation.AnimUtils;
-
-public class SearchEditTextLayout extends FrameLayout {
- private static final float EXPAND_MARGIN_FRACTION_START = 0.8f;
- private static final int ANIMATION_DURATION = 200;
-
- private OnKeyListener mPreImeKeyListener;
- private int mTopMargin;
- private int mBottomMargin;
- private int mLeftMargin;
- private int mRightMargin;
-
- private float mCollapsedElevation;
-
- /* Subclass-visible for testing */
- protected boolean mIsExpanded = false;
- protected boolean mIsFadedOut = false;
-
- private View mCollapsed;
- private View mExpanded;
- private EditText mSearchView;
- private View mSearchIcon;
- private View mCollapsedSearchBox;
- private View mVoiceSearchButtonView;
- private View mOverflowButtonView;
- private View mBackButtonView;
- private View mExpandedSearchBox;
- private View mClearButtonView;
-
- private ValueAnimator mAnimator;
-
- private Callback mCallback;
-
- /**
- * Listener for the back button next to the search view being pressed
- */
- public interface Callback {
- public void onBackButtonClicked();
- public void onSearchViewClicked();
- }
-
- public SearchEditTextLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void setPreImeKeyListener(OnKeyListener listener) {
- mPreImeKeyListener = listener;
- }
-
- public void setCallback(Callback listener) {
- mCallback = listener;
- }
-
- @Override
- protected void onFinishInflate() {
- MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
- mTopMargin = params.topMargin;
- mBottomMargin = params.bottomMargin;
- mLeftMargin = params.leftMargin;
- mRightMargin = params.rightMargin;
-
- mCollapsedElevation = getElevation();
-
- mCollapsed = findViewById(R.id.search_box_collapsed);
- mExpanded = findViewById(R.id.search_box_expanded);
- mSearchView = (EditText) mExpanded.findViewById(R.id.search_view);
-
- mSearchIcon = findViewById(R.id.search_magnifying_glass);
- mCollapsedSearchBox = findViewById(R.id.search_box_start_search);
- mVoiceSearchButtonView = findViewById(R.id.voice_search_button);
- mOverflowButtonView = findViewById(R.id.dialtacts_options_menu_button);
- mBackButtonView = findViewById(R.id.search_back_button);
- mExpandedSearchBox = findViewById(R.id.search_box_expanded);
- mClearButtonView = findViewById(R.id.search_close_button);
-
- // Convert a long click into a click to expand the search box, and then long click on the
- // search view. This accelerates the long-press scenario for copy/paste.
- mCollapsedSearchBox.setOnLongClickListener(new OnLongClickListener() {
- @Override
- public boolean onLongClick(View view) {
- mCollapsedSearchBox.performClick();
- mSearchView.performLongClick();
- return false;
- }
- });
-
- mSearchView.setOnFocusChangeListener(new OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus) {
- DialerUtils.showInputMethod(v);
- } else {
- DialerUtils.hideInputMethod(v);
- }
- }
- });
-
- mSearchView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mCallback != null) {
- mCallback.onSearchViewClicked();
- }
- }
- });
-
- mSearchView.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- mClearButtonView.setVisibility(TextUtils.isEmpty(s) ? View.GONE : View.VISIBLE);
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- }
- });
-
- findViewById(R.id.search_close_button).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mSearchView.setText(null);
- }
- });
-
- findViewById(R.id.search_back_button).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mCallback != null) {
- mCallback.onBackButtonClicked();
- }
- }
- });
-
- super.onFinishInflate();
- }
-
- @Override
- public boolean dispatchKeyEventPreIme(KeyEvent event) {
- if (mPreImeKeyListener != null) {
- if (mPreImeKeyListener.onKey(this, event.getKeyCode(), event)) {
- return true;
- }
- }
- return super.dispatchKeyEventPreIme(event);
- }
-
- public void fadeOut() {
- fadeOut(null);
- }
-
- public void fadeOut(AnimUtils.AnimationCallback callback) {
- AnimUtils.fadeOut(this, ANIMATION_DURATION, callback);
- mIsFadedOut = true;
- }
-
- public void fadeIn() {
- AnimUtils.fadeIn(this, ANIMATION_DURATION);
- mIsFadedOut = false;
- }
-
- public void setVisible(boolean visible) {
- if (visible) {
- setAlpha(1);
- setVisibility(View.VISIBLE);
- mIsFadedOut = false;
- } else {
- setAlpha(0);
- setVisibility(View.GONE);
- mIsFadedOut = true;
- }
- }
-
- public void expand(boolean animate, boolean requestFocus) {
- updateVisibility(true /* isExpand */);
-
- if (animate) {
- AnimUtils.crossFadeViews(mExpanded, mCollapsed, ANIMATION_DURATION);
- mAnimator = ValueAnimator.ofFloat(EXPAND_MARGIN_FRACTION_START, 0f);
- setMargins(EXPAND_MARGIN_FRACTION_START);
- prepareAnimator(true);
- } else {
- mExpanded.setVisibility(View.VISIBLE);
- mExpanded.setAlpha(1);
- setMargins(0f);
- mCollapsed.setVisibility(View.GONE);
- }
-
- // Set 9-patch background. This owns the padding, so we need to restore the original values.
- int paddingTop = this.getPaddingTop();
- int paddingStart = this.getPaddingStart();
- int paddingBottom = this.getPaddingBottom();
- int paddingEnd = this.getPaddingEnd();
- setBackgroundResource(R.drawable.search_shadow);
- setElevation(0);
- setPaddingRelative(paddingStart, paddingTop, paddingEnd, paddingBottom);
-
- if (requestFocus) {
- mSearchView.requestFocus();
- }
- mIsExpanded = true;
- }
-
- public void collapse(boolean animate) {
- updateVisibility(false /* isExpand */);
-
- if (animate) {
- AnimUtils.crossFadeViews(mCollapsed, mExpanded, ANIMATION_DURATION);
- mAnimator = ValueAnimator.ofFloat(0f, 1f);
- prepareAnimator(false);
- } else {
- mCollapsed.setVisibility(View.VISIBLE);
- mCollapsed.setAlpha(1);
- setMargins(1f);
- mExpanded.setVisibility(View.GONE);
- }
-
- mIsExpanded = false;
- setElevation(mCollapsedElevation);
- setBackgroundResource(R.drawable.rounded_corner);
- }
-
- /**
- * Updates the visibility of views depending on whether we will show the expanded or collapsed
- * search view. This helps prevent some jank with the crossfading if we are animating.
- *
- * @param isExpand Whether we are about to show the expanded search box.
- */
- private void updateVisibility(boolean isExpand) {
- int collapsedViewVisibility = isExpand ? View.GONE : View.VISIBLE;
- int expandedViewVisibility = isExpand ? View.VISIBLE : View.GONE;
-
- mSearchIcon.setVisibility(collapsedViewVisibility);
- mCollapsedSearchBox.setVisibility(collapsedViewVisibility);
- mVoiceSearchButtonView.setVisibility(collapsedViewVisibility);
- mOverflowButtonView.setVisibility(collapsedViewVisibility);
- mBackButtonView.setVisibility(expandedViewVisibility);
- // TODO: Prevents keyboard from jumping up in landscape mode after exiting the
- // SearchFragment when the query string is empty. More elegant fix?
- //mExpandedSearchBox.setVisibility(expandedViewVisibility);
- if (TextUtils.isEmpty(mSearchView.getText())) {
- mClearButtonView.setVisibility(View.GONE);
- } else {
- mClearButtonView.setVisibility(expandedViewVisibility);
- }
- }
-
- private void prepareAnimator(final boolean expand) {
- if (mAnimator != null) {
- mAnimator.cancel();
- }
-
- mAnimator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- final Float fraction = (Float) animation.getAnimatedValue();
- setMargins(fraction);
- }
- });
-
- mAnimator.setDuration(ANIMATION_DURATION);
- mAnimator.start();
- }
-
- public boolean isExpanded() {
- return mIsExpanded;
- }
-
- public boolean isFadedOut() {
- return mIsFadedOut;
- }
-
- /**
- * Assigns margins to the search box as a fraction of its maximum margin size
- *
- * @param fraction How large the margins should be as a fraction of their full size
- */
- private void setMargins(float fraction) {
- MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
- params.topMargin = (int) (mTopMargin * fraction);
- params.bottomMargin = (int) (mBottomMargin * fraction);
- params.leftMargin = (int) (mLeftMargin * fraction);
- params.rightMargin = (int) (mRightMargin * fraction);
- requestLayout();
- }
-}
diff --git a/src/com/android/dialerbind/DatabaseHelperManager.java b/src/com/android/dialerbind/DatabaseHelperManager.java
deleted file mode 100644
index c92993242..000000000
--- a/src/com/android/dialerbind/DatabaseHelperManager.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialerbind;
-
-import android.content.Context;
-
-import com.android.dialer.database.DialerDatabaseHelper;
-
-
-public class DatabaseHelperManager {
- public static DialerDatabaseHelper getDatabaseHelper(Context context) {
- return DialerDatabaseHelper.getInstance(context);
- }
-}
diff --git a/src/com/android/dialerbind/ObjectFactory.java b/src/com/android/dialerbind/ObjectFactory.java
deleted file mode 100644
index 342f39cb9..000000000
--- a/src/com/android/dialerbind/ObjectFactory.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialerbind;
-
-import static com.android.dialer.calllog.CallLogAdapter.CallFetcher;
-
-import android.content.Context;
-import android.support.annotation.Nullable;
-
-import com.android.dialer.calllog.CallLogAdapter;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.list.RegularSearchFragment;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.service.CachedNumberLookupService;
-import com.android.dialer.service.ExtendedCallInfoService;
-import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
-
-/**
- * Default static binding for various objects.
- */
-public class ObjectFactory {
-
- public static CachedNumberLookupService newCachedNumberLookupService() {
- // no-op
- return null;
- }
-
- public static String getFilteredNumberProviderAuthority() {
- return "com.android.dialer.database.filterednumberprovider";
- }
-
- public static String getVoicemailArchiveProviderAuthority() {
- return "com.android.dialer.database.voicemailarchiveprovider";
- }
-
- public static boolean isVoicemailArchiveEnabled(Context context) {
- return false;
- }
-
- public static boolean isVoicemailShareEnabled(Context context) {
- return false;
- }
-
- public static boolean isNewBlockingEnabled(Context context) {
- return true;
- }
-
- @Nullable
- public static ExtendedCallInfoService newExtendedCallInfoService(Context context) {
- return null;
- }
-
- /**
- * Create a new instance of the call log adapter.
- * @param context The context to use.
- * @param callFetcher Instance of call fetcher to use.
- * @param contactInfoHelper Instance of contact info helper class to use.
- * @return Instance of CallLogAdapter.
- */
- public static CallLogAdapter newCallLogAdapter(
- Context context,
- CallFetcher callFetcher,
- ContactInfoHelper contactInfoHelper,
- VoicemailPlaybackPresenter voicemailPlaybackPresenter,
- int activityType) {
- return new CallLogAdapter(
- context,
- callFetcher,
- contactInfoHelper,
- voicemailPlaybackPresenter,
- activityType);
- }
-
- public static Logger getLoggerInstance() {
- // no-op
- return null;
- }
-
- public static RegularSearchFragment newRegularSearchFragment() {
- return new RegularSearchFragment();
- }
-}
diff --git a/tests/Android.mk b/tests/Android.mk
deleted file mode 100644
index 910d89376..000000000
--- a/tests/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-LOCAL_CERTIFICATE := shared
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-src_dirs := src \
- ../../ContactsCommon/TestCommon/src
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-
-LOCAL_STATIC_JAVA_LIBRARIES += \
- mockito-target-minus-junit4
-
-LOCAL_PACKAGE_NAME := DialerTests
-
-LOCAL_INSTRUMENTATION_FOR := Dialer
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
deleted file mode 100644
index 40c5502da..000000000
--- a/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.dialer.tests">
-
- <uses-sdk
- android:minSdkVersion="23"
- android:targetSdkVersion="23" />
-
- <uses-permission android:name="android.permission.READ_CONTACTS" />
- <uses-permission android:name="android.permission.WRITE_CONTACTS" />
- <uses-permission android:name="android.permission.READ_CALL_LOG" />
- <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
- <uses-permission android:name="android.permission.GET_ACCOUNTS" />
-
- <uses-permission android:name="android.permission.USE_CREDENTIALS" />
- <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
- <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
- <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
- <uses-permission android:name="android.permission.READ_SYNC_STATS" />
- <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
- <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
-
- <uses-permission android:name="android.permission.READ_PROFILE" />
- <uses-permission android:name="android.permission.READ_SOCIAL_STREAM" />
-
- <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
-
- <application>
- <uses-library android:name="android.test.runner" />
- <meta-data android:name="com.android.dialer.iconset" android:resource="@xml/iconset" />
-
- <activity android:name=".calllog.FillCallLogTestActivity"
- android:label="Call log filter test"
- >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.dialer"
- android:label="Dialer app tests">
- </instrumentation>
-
- <instrumentation android:name="com.android.dialer.DialerLaunchPerformance"
- android:targetPackage="com.android.dialer"
- android:label="Dialer launch performance">
- </instrumentation>
-
-</manifest>
diff --git a/tests/assets/README.txt b/tests/assets/README.txt
deleted file mode 100644
index 6cea058cf..000000000
--- a/tests/assets/README.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-quick_test_recording.mp3 is copyright 2011 by Hugo Hudson and is licensed under a
-Creative Commons Attribution 3.0 Unported License:
- http://creativecommons.org/licenses/by/3.0/
diff --git a/tests/assets/quick_test_recording.mp3 b/tests/assets/quick_test_recording.mp3
deleted file mode 100644
index ad7cb9c20..000000000
--- a/tests/assets/quick_test_recording.mp3
+++ /dev/null
Binary files differ
diff --git a/tests/proguard.flags b/tests/proguard.flags
deleted file mode 100644
index 3991a1452..000000000
--- a/tests/proguard.flags
+++ /dev/null
@@ -1,20 +0,0 @@
--keep class com.android.contacts.model.Sources {
- public <init>(...);
-}
-
-# Xml files containing onClick (menus and layouts) require that proguard not
-# remove their handlers.
--keepclassmembers class * extends android.app.Activity {
- public void *(android.view.View);
- public void *(android.view.MenuItem);
-}
-
-# Any class or method annotated with NeededForTesting or NeededForReflection.
--keep @com.android.contacts.common.test.NeededForTesting class *
--keep @com.android.contacts.test.NeededForReflection class *
--keepclassmembers class * {
-@com.android.contacts.common.test.NeededForTesting *;
-@com.android.contacts.test.NeededForReflection *;
-}
-
--verbose
diff --git a/tests/res/drawable/phone_icon.png b/tests/res/drawable/phone_icon.png
deleted file mode 100644
index 4e613ecce..000000000
--- a/tests/res/drawable/phone_icon.png
+++ /dev/null
Binary files differ
diff --git a/tests/res/layout/fill_call_log_test.xml b/tests/res/layout/fill_call_log_test.xml
deleted file mode 100644
index 43f475b52..000000000
--- a/tests/res/layout/fill_call_log_test.xml
+++ /dev/null
@@ -1,267 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="left"
->
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/numberOfCallLogEntries"
- />
- <EditText
- android:id="@+id/number"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:inputType="number"
- android:text="10"
- />
- <CheckBox
- android:id="@+id/use_random_numbers"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/useRandomNumbers"
- />
- <Button
- android:id="@+id/add"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/addToCallLogButton"
- />
- <ProgressBar
- android:id="@+id/progress"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:indeterminate="false"
- android:visibility="gone"
- />
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/add_custom_entry"
- />
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="left"
- >
- <RadioGroup
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <RadioButton
- android:id="@+id/call_type_incoming"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/call_type_incoming"
- android:textSize="9sp" />
- <RadioButton
- android:id="@+id/call_type_missed"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/call_type_missed"
- android:textSize="9sp" />
- <RadioButton
- android:id="@+id/call_type_outgoing"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/call_type_outgoing"
- android:textSize="9sp" />
- <RadioButton
- android:id="@+id/call_type_voicemail"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/call_type_voicemail"
- android:textSize="9sp" />
- <RadioButton
- android:id="@+id/call_type_custom"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Custom"
- android:textSize="9sp" />
- <EditText
- android:id="@+id/call_type_custom_text"
- android:layout_width="90dp"
- android:layout_height="wrap_content"
- android:text=""
- android:inputType="number" />
- </RadioGroup>
- <CheckBox
- android:id="@+id/call_type_video"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/call_type_video"
- android:textSize="9sp"
- />
- </LinearLayout>
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="left"
- >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/call_date"
- />
- <TextView
- android:id="@+id/call_date"
- android:paddingStart="16dp"
- android:layout_width="120dp"
- android:layout_height="wrap_content"
- />
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/edit"
- android:onClick="showDatePickerDialog" />
- </LinearLayout>
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="left"
- >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/call_time"
- />
- <TextView
- android:id="@+id/call_time"
- android:paddingStart="16dp"
- android:layout_width="120dp"
- android:layout_height="wrap_content"
- />
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/edit"
- android:onClick="showTimePickerDialog" />
- </LinearLayout>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/call_presentation"
- />
- <RadioGroup
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <RadioButton
- android:id="@+id/presentation_allowed"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/presentation_allowed"
- android:textSize="9sp" />
- <RadioButton
- android:id="@+id/presentation_restricted"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/presentation_restricted"
- android:textSize="9sp" />
- <RadioButton
- android:id="@+id/presentation_unknown"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/presentation_unknown"
- android:textSize="9sp" />
- <RadioButton
- android:id="@+id/presentation_payphone"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/presentation_payphone"
- android:textSize="9sp" />
- </RadioGroup>
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="left"
- >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/number"
- />
- <EditText
- android:id="@+id/phone_number"
- android:layout_width="180dp"
- android:layout_height="wrap_content"
- android:inputType="phone"
- />
- </LinearLayout>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/call_account" />
- <RadioGroup
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <RadioButton
- android:id="@+id/account0"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/account0"
- android:textSize="9sp" />
- <RadioButton
- android:id="@+id/account1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/account1"
- android:textSize="9sp" />
- <RadioButton
- android:id="@+id/no_account"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/no_account"
- android:textSize="9sp" />
- </RadioGroup>
- <Button
- android:id="@+id/add_custom"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/addToCallLogButton"
- android:onClick="addManualEntry"
- />
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="left"
- >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/delta_after_add"
- />
- <EditText
- android:id="@+id/delta_after_add"
- android:layout_width="90dp"
- android:layout_height="wrap_content"
- android:text="-1"
- android:inputType="number"
- />
- </LinearLayout>
-</LinearLayout>
diff --git a/tests/res/values/donottranslate_strings.xml b/tests/res/values/donottranslate_strings.xml
deleted file mode 100644
index 2f8017cd2..000000000
--- a/tests/res/values/donottranslate_strings.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <string-array name="allIntents">
- <!-- List modes -->
- <!-- Various ways to start Contacts -->
- <item>DIAL</item>
- <item>DIAL phone (deprecated)</item>
- <item>DIAL person (deprecated)</item>
- <item>DIAL voicemail</item>
- <item>CALL BUTTON</item>
- <item>DIAL tel</item>
- <item>VIEW tel</item>
- <item>VIEW calls (call-log after a phone call)</item>
- <item>VIEW calls item</item>
- <item>CallDetailActivity (legacy)</item>
- <item>CallLogActivity (legacy)</item>
- </string-array>
-
- <string name="addToCallLogButton">Add</string>
- <string name="useRandomNumbers">Use random numbers</string>
- <string name="numberOfCallLogEntries">Number of call log entries to add:</string>
- <string name="addedLogEntriesToast">Added %1$d call log entries.</string>
- <string name="noLogEntriesToast">No entries in the call log yet. Need at least one record for the template. Or use random numbers.</string>
- <string name="add_custom_entry">Add custom call log entry:</string>
- <string name="call_type_incoming">Incoming</string>
- <string name="call_type_missed">Missed</string>
- <string name="call_type_outgoing">Outgoing</string>
- <string name="call_type_video">Video</string>
- <string name="call_type_voicemail">Voicemail</string>
- <string name="call_date">Call date</string>
- <string name="call_time">Call time</string>
- <string name="edit">Edit</string>
- <string name="number">Number</string>
- <string name="call_presentation">Presentation</string>
- <string name="presentation_allowed">Allowed</string>
- <string name="presentation_restricted">Restricted</string>
- <string name="presentation_unknown">Unknown</string>
- <string name="presentation_payphone">Payphone</string>
- <string name="delta_after_add">Offset call time after add (min): </string>
- <string name="call_account">Account</string>
- <string name="account0">Account 0</string>
- <string name="account1">Account 1</string>
- <string name="no_account">No Account</string>
-</resources>
diff --git a/tests/res/xml/iconset.xml b/tests/res/xml/iconset.xml
deleted file mode 100644
index 2f7798db1..000000000
--- a/tests/res/xml/iconset.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-<icon-set xmlns:android="http://schemas.android.com/apk/res/android">
-
- <icon android:mimeType="vnd.android.cursor.item/phone"
- android:icon="@drawable/phone_icon" />
-
-</icon-set>
diff --git a/tests/src/com/android/dialer/CallDetailActivityTest.java b/tests/src/com/android/dialer/CallDetailActivityTest.java
deleted file mode 100644
index fec25f655..000000000
--- a/tests/src/com/android/dialer/CallDetailActivityTest.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * 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.dialer;
-
-import static com.android.dialer.calllog.CallLogAsyncTaskUtil.Tasks;
-
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Intent;
-import android.net.Uri;
-import android.provider.CallLog;
-import android.provider.VoicemailContract;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
-import android.view.Menu;
-import android.widget.PopupMenu;
-import android.widget.TextView;
-
-import com.android.dialer.calllog.CallLogAsyncTaskUtil;
-import com.android.dialer.util.AppCompatConstants;
-import com.android.dialer.util.AsyncTaskExecutors;
-import com.android.dialer.util.FakeAsyncTaskExecutor;
-
-/**
- * Unit tests for the {@link CallDetailActivity}. NOTE: The screen needs to be on for the
- * UI-related tests to pass.
- */
-@LargeTest
-public class CallDetailActivityTest extends ActivityInstrumentationTestCase2<CallDetailActivity> {
- private static final String CONTACT_NUMBER = "+1412555555";
- private static final String VOICEMAIL_FILE_LOCATION = "/sdcard/sadlfj893w4j23o9sfu.mp3";
-
- private Uri mCallLogUri;
- private Uri mVoicemailUri;
- private FakeAsyncTaskExecutor mFakeAsyncTaskExecutor;
- private CallDetailActivity mActivityUnderTest;
-
- public CallDetailActivityTest() {
- super(CallDetailActivity.class);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mFakeAsyncTaskExecutor = new FakeAsyncTaskExecutor(getInstrumentation());
- AsyncTaskExecutors.setFactoryForTest(mFakeAsyncTaskExecutor.getFactory());
-
- // I don't like the default of focus-mode for tests, the green focus border makes the
- // screenshots look weak.
- setActivityInitialTouchMode(true);
- }
-
- @Override
- protected void tearDown() throws Exception {
- cleanUpUri();
-
- AsyncTaskExecutors.setFactoryForTest(null);
- CallLogAsyncTaskUtil.resetForTest();
-
- super.tearDown();
- }
-
- /** Test for bug where missing Extras on intent used to start Activity causes NPE. */
- public void testCallLogUriWithMissingExtrasShouldNotCauseNPE() throws Throwable {
- setActivityIntentForTestCallEntry();
- startActivityUnderTest();
- }
-
- /**
- * Verifies the trash menu item is present and a voicemail URI is set.
- */
- @Suppress
- public void testVoicemailDeleteButton() throws Throwable {
- setActivityIntentForTestVoicemailEntry();
- startActivityUnderTest();
- mFakeAsyncTaskExecutor.runTask(Tasks.GET_CALL_DETAILS);
-
- Menu optionsMenu = (new PopupMenu(mActivityUnderTest, null)).getMenu();
- mActivityUnderTest.onCreateOptionsMenu(optionsMenu);
- mActivityUnderTest.onPrepareOptionsMenu(optionsMenu);
-
- assertTrue(mActivityUnderTest.hasVoicemail());
- mActivityUnderTest.runOnUiThread(new Runnable() {
- public void run() {
- mActivityUnderTest.findViewById(R.id.call_detail_delete_menu_item).performClick();
- }
- });
- getInstrumentation().waitForIdleSync();
- mFakeAsyncTaskExecutor.runTask(Tasks.DELETE_VOICEMAIL);
- }
-
- /**
- * Verifies the trash menu item is present and a voicemail URI is not set.
- */
- @Suppress
- public void testRegularCallDoesHaveRemoveFromCallLog() throws Throwable {
- setActivityIntentForTestCallEntry();
- startActivityUnderTest();
- mFakeAsyncTaskExecutor.runTask(Tasks.GET_CALL_DETAILS);
-
- Menu optionsMenu = (new PopupMenu(mActivityUnderTest, null)).getMenu();
- mActivityUnderTest.onCreateOptionsMenu(optionsMenu);
- mActivityUnderTest.onPrepareOptionsMenu(optionsMenu);
-
- assertFalse(mActivityUnderTest.hasVoicemail());
- mActivityUnderTest.runOnUiThread(new Runnable() {
- public void run() {
- mActivityUnderTest.findViewById(R.id.call_detail_delete_menu_item).performClick();
- }
- });
- getInstrumentation().waitForIdleSync();
- mFakeAsyncTaskExecutor.runTask(Tasks.DELETE_CALL);
- }
-
- private void setActivityIntentForTestCallEntry() {
- assertNull(mVoicemailUri);
- assertNull(mCallLogUri);
- ContentResolver contentResolver = getContentResolver();
- ContentValues values = new ContentValues();
- values.put(CallLog.Calls.NUMBER, CONTACT_NUMBER);
- values.put(CallLog.Calls.NUMBER_PRESENTATION, CallLog.Calls.PRESENTATION_ALLOWED);
- values.put(CallLog.Calls.TYPE, AppCompatConstants.CALLS_INCOMING_TYPE);
- mCallLogUri = contentResolver.insert(CallLog.Calls.CONTENT_URI, values);
- setActivityIntent(new Intent(Intent.ACTION_VIEW, mCallLogUri));
- }
-
- private void setActivityIntentForTestVoicemailEntry() {
- assertNull(mVoicemailUri);
- ContentResolver contentResolver = getContentResolver();
- ContentValues values = new ContentValues();
- values.put(VoicemailContract.Voicemails.NUMBER, CONTACT_NUMBER);
- values.put(VoicemailContract.Voicemails.HAS_CONTENT, 1);
- // VoicemailContract.Voicemails._DATA
- values.put("_data", VOICEMAIL_FILE_LOCATION);
- mVoicemailUri = contentResolver.insert(VoicemailContract.Voicemails.CONTENT_URI, values);
-
- Uri callLogUri = ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL,
- ContentUris.parseId(mVoicemailUri));
- Intent intent = new Intent(Intent.ACTION_VIEW, callLogUri);
- intent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_URI, mVoicemailUri);
- setActivityIntent(intent);
- }
-
- private void cleanUpUri() {
- if (mVoicemailUri != null) {
- getContentResolver().delete(VoicemailContract.Voicemails.CONTENT_URI,
- "_ID = ?", new String[] { String.valueOf(ContentUris.parseId(mVoicemailUri)) });
- mVoicemailUri = null;
- }
- if (mCallLogUri != null) {
- getContentResolver().delete(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL,
- "_ID = ?", new String[] { String.valueOf(ContentUris.parseId(mCallLogUri)) });
- mCallLogUri = null;
- }
- }
-
- private ContentResolver getContentResolver() {
- return getInstrumentation().getTargetContext().getContentResolver();
- }
-
- private void startActivityUnderTest() throws Throwable {
- assertNull(mActivityUnderTest);
- mActivityUnderTest = getActivity();
- assertNotNull("activity should not be null", mActivityUnderTest);
- }
-}
diff --git a/tests/src/com/android/dialer/DialerLaunchPerformance.java b/tests/src/com/android/dialer/DialerLaunchPerformance.java
deleted file mode 100644
index c409cc60b..000000000
--- a/tests/src/com/android/dialer/DialerLaunchPerformance.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2007 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.dialer;
-
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.contacts.common.test.LaunchPerformanceBase;
-
-/**
- * Instrumentation class for Address Book launch performance testing.
- */
-public class DialerLaunchPerformance extends LaunchPerformanceBase {
-
- @Override
- public void onCreate(Bundle arguments) {
- mIntent.setAction(Intent.ACTION_MAIN);
- mIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- mIntent.setComponent(new ComponentName("com.android.contacts",
- "testcom.android.dialer.DialtactsActivity"));
-
- start();
- }
-
- /**
- * Calls LaunchApp and finish.
- */
- @Override
- public void onStart() {
- super.onStart();
- LaunchApp();
- finish(Activity.RESULT_OK, mResults);
- }
-}
diff --git a/tests/src/com/android/dialer/calllog/BlockReportSpamListenerTest.java b/tests/src/com/android/dialer/calllog/BlockReportSpamListenerTest.java
deleted file mode 100644
index c63027d6b..000000000
--- a/tests/src/com/android/dialer/calllog/BlockReportSpamListenerTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package com.android.dialer.calllog;
-
-import android.app.Activity;
-import android.support.v7.widget.RecyclerView;
-import android.test.ActivityInstrumentationTestCase2;
-
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.service.ExtendedCallInfoService;
-
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Unit tests for {@link BlockReportSpamListener}.
- */
-public class BlockReportSpamListenerTest extends ActivityInstrumentationTestCase2<DialtactsActivity> {
-
- private static final String TEST_DISPLAY_NUMBER = "(123)456-7890";
- private static final String TEST_NUMBER = "1234567890";
- private static final String TEST_COUNTRY_ISO = "us";
- private static final int TEST_CALL_TYPE = 0;
- private static final int TEST_CALL_BLOCK_ID = 1;
-
- private BlockReportSpamListener blockReportSpamListener;
-
- @Mock private RecyclerView.Adapter adapter;
- @Mock private ExtendedCallInfoService extendedCallInfoService;
- @Mock private FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler;
-
- public BlockReportSpamListenerTest() {
- super(DialtactsActivity.class);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- MockitoAnnotations.initMocks(this);
-
- blockReportSpamListener = new BlockReportSpamListener(
- ((Activity) getActivity()).getFragmentManager(), adapter,
- extendedCallInfoService, filteredNumberAsyncQueryHandler);
- }
-
- public void testOnBlockReportSpam() {
- blockReportSpamListener.onBlockReportSpam(
- TEST_DISPLAY_NUMBER, TEST_NUMBER, TEST_COUNTRY_ISO, TEST_CALL_TYPE);
- }
-
- public void testOnBlock() {
- blockReportSpamListener.onBlock(
- TEST_DISPLAY_NUMBER, TEST_NUMBER, TEST_COUNTRY_ISO, TEST_CALL_TYPE);
- }
-
- public void testOnUnlock_isSpam() {
- blockReportSpamListener.onUnblock(
- TEST_DISPLAY_NUMBER, TEST_NUMBER, TEST_COUNTRY_ISO, TEST_CALL_BLOCK_ID,
- true, TEST_CALL_TYPE);
- }
-
- public void testOnUnlock_isNotSpam() {
- blockReportSpamListener.onUnblock(
- TEST_DISPLAY_NUMBER, TEST_NUMBER, TEST_COUNTRY_ISO, TEST_CALL_BLOCK_ID,
- false, TEST_CALL_TYPE);
- }
-
- public void testOnReportNotSpam() {
- blockReportSpamListener.onReportNotSpam(
- TEST_DISPLAY_NUMBER, TEST_NUMBER, TEST_COUNTRY_ISO, TEST_CALL_TYPE);
- }
-}
diff --git a/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java b/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java
deleted file mode 100644
index 3b1dd2c7d..000000000
--- a/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java
+++ /dev/null
@@ -1,918 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import com.google.common.collect.Lists;
-
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.database.MatrixCursor;
-import android.net.Uri;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.VoicemailContract;
-import android.telephony.PhoneNumberUtils;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.text.TextUtils;
-import android.view.View;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.dialer.contactinfo.ContactInfoCache;
-import com.android.dialer.database.VoicemailArchiveContract;
-import com.android.dialer.util.AppCompatConstants;
-import com.android.dialer.util.TestConstants;
-import com.android.dialer.R;
-
-import java.util.Date;
-import java.util.List;
-import java.util.Random;
-
-/**
- * Unit tests for {@link CallLogAdapter}.
- *
- * adb shell am instrument \
- * -e com.android.dialer.calllog.CallLogAdapterTest \
- * -w com.android.dialer.tests/android.test.InstrumentationTestRunner
- */
-public class CallLogAdapterTest extends AndroidTestCase {
- private static final String EMPTY_STRING = "";
- private static final int NO_VALUE_SET = -1;
- private static final int ARCHIVE_TYPE = -2;
-
- private static final String TEST_CACHED_NAME_PRIMARY = "Cached Name";
- private static final String TEST_CACHED_NAME_ALTERNATIVE = "Name Cached";
- private static final String CONTACT_NAME_PRIMARY = "Contact Name";
- private static final String CONTACT_NAME_ALTERNATIVE = "Name, Contact";
- private static final String TEST_CACHED_NUMBER_LABEL = "label";
- private static final int TEST_CACHED_NUMBER_TYPE = 1;
- private static final String TEST_COUNTRY_ISO = "US";
- private static final String TEST_DEFAULT_CUSTOM_LABEL = "myLabel";
- private static final Uri TEST_LOOKUP_URI = Uri.parse("content://contacts/2");
- private static final String TEST_ACCOUNT_ID_LABEL = "label";
-
- private static final String TEST_NUMBER = "12125551000";
- private static final String TEST_NUMBER_1 = "12345678";
- private static final String TEST_NUMBER_2 = "87654321";
- private static final String TEST_NUMBER_3 = "18273645";
- private static final String TEST_POST_DIAL_DIGITS = ";12345";
- private static final String TEST_VIA_NUMBER = "+16505551234";
- private static final String TEST_FORMATTED_NUMBER = "1 212-555-1000";
-
- // The object under test.
- private TestCallLogAdapter mAdapter;
-
- private MatrixCursor mCursor;
- private Resources mResources;
-
- private CallLogListItemViewHolder mViewHolder;
- private final Random mRandom = new Random();
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getContext();
- mResources = mContext.getResources();
-
- // Use a call fetcher that does not do anything.
- CallLogAdapter.CallFetcher fakeCallFetcher = new CallLogAdapter.CallFetcher() {
- @Override
- public void fetchCalls() {}
- };
-
- ContactInfoHelper fakeContactInfoHelper =
- new ContactInfoHelper(getContext(), TEST_COUNTRY_ISO) {
- @Override
- public ContactInfo lookupNumber(String number, String countryIso) {
- ContactInfo info = new ContactInfo();
- info.number = number;
- info.formattedNumber = number;
- return info;
- }
- };
-
- mAdapter = new TestCallLogAdapter(getContext(), fakeCallFetcher, fakeContactInfoHelper,
- CallLogAdapter.ACTIVITY_TYPE_DIALTACTS);
-
- // The cursor used in the tests to store the entries to display.
- mCursor = new MatrixCursor(CallLogQuery._PROJECTION);
- mCursor.moveToFirst();
-
- // The views into which to store the data.
- mViewHolder = CallLogListItemViewHolder.createForTest(getContext());
- }
-
- @MediumTest
- public void testBindView_NumberOnlyNoCache() {
- createCallLogEntry();
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- assertNameIs(mViewHolder, TEST_NUMBER);
- }
-
- @MediumTest
- public void testBindView_PrivateCall() {
- createPrivateCallLogEntry();
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- assertEquals(Calls.PRESENTATION_RESTRICTED, mViewHolder.numberPresentation);
- assertNull(mViewHolder.primaryActionButtonView.getTag());
- // QC should be disabled since there are no actions to be performed on this
- // call.
- assertFalse(mViewHolder.quickContactView.isEnabled());
- }
-
- @MediumTest
- public void testBindView_UnknownCall() {
- createUnknownCallLogEntry();
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- assertEquals(Calls.PRESENTATION_UNKNOWN, mViewHolder.numberPresentation);
- assertNull(mViewHolder.primaryActionButtonView.getTag());
- // QC should be disabled since there are no actions to be performed on this
- // call.
- assertFalse(mViewHolder.quickContactView.isEnabled());
- }
-
- @MediumTest
- public void testBindView_WithoutQuickContactBadge() {
- createCallLogEntry();
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- //assertFalse(mViewHolder.quickContactView.isEnabled());
- }
-
- @MediumTest
- public void testBindView_CallButton() {
- createCallLogEntry();
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- // The primaryActionView tag is set when the ViewHolder is binded. If it is possible
- // to place a call to the phone number, a call intent will have been created which
- // starts a phone call to the entry's number.
- assertHasCallAction(mViewHolder);
- }
-
- @MediumTest
- public void testBindView_FirstNameFirstOrder() {
- createCallLogEntry();
-
- mAdapter.getContactInfoCache()
- .mockGetValue(createContactInfo(CONTACT_NAME_PRIMARY, CONTACT_NAME_ALTERNATIVE));
-
- setNameDisplayOrder(getContext(), ContactsPreferences.DISPLAY_ORDER_PRIMARY);
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
- assertEquals(CONTACT_NAME_PRIMARY, mViewHolder.phoneCallDetailsViews.nameView.getText());
- }
-
- @MediumTest
- public void testBindView_LastNameFirstOrder() {
- createCallLogEntry();
-
- mAdapter.getContactInfoCache()
- .mockGetValue(createContactInfo(CONTACT_NAME_PRIMARY, CONTACT_NAME_ALTERNATIVE));
-
- setNameDisplayOrder(getContext(), ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE);
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
- assertEquals(CONTACT_NAME_ALTERNATIVE,
- mViewHolder.phoneCallDetailsViews.nameView.getText());
- }
-
- @MediumTest
- public void testBindView_NameOrderCorrectOnChange() {
- createCallLogEntry();
-
- mAdapter.getContactInfoCache()
- .mockGetValue(createContactInfo(CONTACT_NAME_PRIMARY, CONTACT_NAME_ALTERNATIVE));
-
- Context context = getContext();
- setNameDisplayOrder(context, ContactsPreferences.DISPLAY_ORDER_PRIMARY);
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
- assertEquals(CONTACT_NAME_PRIMARY,
- mViewHolder.phoneCallDetailsViews.nameView.getText());
-
- setNameDisplayOrder(context, ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE);
- mAdapter.onResume();
-
- mAdapter.onBindViewHolder(mViewHolder, 0);
- assertEquals(CONTACT_NAME_ALTERNATIVE,
- mViewHolder.phoneCallDetailsViews.nameView.getText());
- }
-
- private void setNameDisplayOrder(Context context, int displayOrder) {
- context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE).edit().putInt(
- ContactsPreferences.DISPLAY_ORDER_KEY, displayOrder).commit();
- }
-
- @MediumTest
- public void testBindView_CallButtonWithPostDialDigits() {
- createCallLogEntry(TEST_NUMBER, TEST_POST_DIAL_DIGITS, NO_VALUE_SET, NO_VALUE_SET);
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- if (CompatUtils.isNCompatible()) {
- assertHasCallActionToGivenNumber(mViewHolder, TEST_NUMBER + TEST_POST_DIAL_DIGITS);
- }
- }
-
- @MediumTest
- public void testBindView_VoicemailUri() {
- createVoicemailCallLogEntry();
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- assertEquals(Uri.parse(mViewHolder.voicemailUri),
- ContentUris.withAppendedId(VoicemailContract.Voicemails.CONTENT_URI, 0));
- assertNull(mViewHolder.primaryActionButtonView.getTag());
- }
-
- @MediumTest
- public void testBindView_NumberWithPostDialDigits() {
- createCallLogEntry(TEST_NUMBER, TEST_POST_DIAL_DIGITS, NO_VALUE_SET, NO_VALUE_SET);
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- if (CompatUtils.isNCompatible()) {
- assertNameIs(mViewHolder, TEST_NUMBER + TEST_POST_DIAL_DIGITS);
- }
- }
-
- @MediumTest
- public void testBindView_ContactWithPostDialDigits() {
- createCallLogEntry(TEST_NUMBER, TEST_POST_DIAL_DIGITS, NO_VALUE_SET, NO_VALUE_SET);
- mAdapter.injectContactInfoForTest(TEST_NUMBER + TEST_POST_DIAL_DIGITS, TEST_COUNTRY_ISO,
- createContactInfo());
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- if (CompatUtils.isNCompatible()) {
- assertNameIs(mViewHolder, TEST_CACHED_NAME_PRIMARY);
- }
- }
-
- @MediumTest
- public void testBindView_CallLogWithViaNumber() {
- createCallLogEntry(TEST_NUMBER, EMPTY_STRING, TEST_VIA_NUMBER, NO_VALUE_SET, NO_VALUE_SET);
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- // Copy format of Resource String
- String formattedNumber = mResources.getString(R.string.description_via_number,
- TEST_VIA_NUMBER);
-
- if (CompatUtils.isNCompatible()) {
- assertEquals(formattedNumber,
- mViewHolder.phoneCallDetailsViews.callAccountLabel.getText());
- }
- }
-
- @MediumTest
- public void testBindView_CallLogWithoutViaNumber() {
- createCallLogEntry(TEST_NUMBER, EMPTY_STRING, EMPTY_STRING, NO_VALUE_SET, NO_VALUE_SET);
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- if (CompatUtils.isNCompatible()) {
- assertEquals(View.GONE,
- mViewHolder.phoneCallDetailsViews.callAccountLabel.getVisibility());
- }
- }
-
- @MediumTest
- public void testPresentationAfterRebindingViewHolders() {
- final int increment = 10;
- final int size = increment * 4;
-
- // Instantiate list of ViewHolders.
- CallLogListItemViewHolder[] holders = new CallLogListItemViewHolder[size];
- for (int i = 0; i < size; i++) {
- holders[i] = CallLogListItemViewHolder.createForTest(getContext());
- }
-
- // Add first set of entries to the cursor.
- for (int i = 0; i < increment; i++) {
- createCallLogEntry();
- createPrivateCallLogEntry();
- createCallLogEntry();
- createUnknownCallLogEntry();
- }
-
- mAdapter.changeCursor(mCursor);
-
- // Verify correct appearance for presentation.
- for (int i = 0; i < size; i++) {
- mAdapter.onBindViewHolder(holders[i], i);
- if (holders[i].numberPresentation == Calls.PRESENTATION_ALLOWED) {
- assertHasCallAction(holders[i]);
- } else {
- assertNull(holders[i].primaryActionButtonView.getTag());
- assertEquals(holders[i].number, EMPTY_STRING);
- }
- }
-
- // Append the rest of the entries to the cursor. Keep the first set of ViewHolders
- // so they are updated and not buitl from scratch. This checks for bugs which may
- // be evident only after the call log is updated.
- for (int i = 0; i < increment; i++) {
- createPrivateCallLogEntry();
- createCallLogEntry();
- createUnknownCallLogEntry();
- createCallLogEntry();
- }
-
- mCursor.move(size);
-
- // Verify correct appearnce for presentation.
- for (int i = 0; i < size; i++) {
- mAdapter.onBindViewHolder(holders[i], i + size);
- if (holders[i].numberPresentation == Calls.PRESENTATION_ALLOWED) {
- assertHasCallAction(holders[i]);
- } else {
- assertNull(holders[i].primaryActionButtonView.getTag());
- assertEquals(holders[i].number, EMPTY_STRING);
- }
- }
- }
-
- @MediumTest
- public void testBindView_NoCallLogCacheNorMemoryCache_EnqueueRequest() {
- createCallLogEntry();
-
- // Bind the views of a single row.
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- // There is one request for contact details.
- assertEquals(1, mAdapter.getContactInfoCache().requests.size());
-
- TestContactInfoCache.Request request = mAdapter.getContactInfoCache().requests.get(0);
- // It is for the number we need to show.
- assertEquals(TEST_NUMBER, request.number);
- // It has the right country.
- assertEquals(TEST_COUNTRY_ISO, request.countryIso);
- // Since there is nothing in the cache, it is an immediate request.
- assertTrue("should be immediate", request.immediate);
- }
-
- @MediumTest
- public void testBindView_CallLogCacheButNoMemoryCache_EnqueueRequest() {
- createCallLogEntryWithCachedValues(false);
-
- // Bind the views of a single row.
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- // There is one request for contact details.
- assertEquals(1, mAdapter.getContactInfoCache().requests.size());
-
- TestContactInfoCache.Request request = mAdapter.getContactInfoCache().requests.get(0);
-
- // The values passed to the request, match the ones in the call log cache.
- assertEquals(TEST_CACHED_NAME_PRIMARY, request.callLogInfo.name);
- assertEquals(TEST_CACHED_NUMBER_TYPE, request.callLogInfo.type);
- assertEquals(TEST_CACHED_NUMBER_LABEL, request.callLogInfo.label);
- }
-
- @MediumTest
- public void testBindView_NoCallLogButMemoryCache_EnqueueRequest() {
- createCallLogEntry();
- mAdapter.injectContactInfoForTest(TEST_NUMBER, TEST_COUNTRY_ISO, createContactInfo());
-
- // Bind the views of a single row.
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- // There is one request for contact details.
- assertEquals(1, mAdapter.getContactInfoCache().requests.size());
-
- TestContactInfoCache.Request request = mAdapter.getContactInfoCache().requests.get(0);
- // Since there is something in the cache, it is not an immediate request.
- assertFalse("should not be immediate", request.immediate);
- }
-
- @MediumTest
- public void testBindView_BothCallLogAndMemoryCache_NoEnqueueRequest() {
- createCallLogEntryWithCachedValues(true);
-
- // Bind the views of a single row.
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- // Cache and call log are up-to-date: no need to request update.
- assertEquals(0, mAdapter.getContactInfoCache().requests.size());
- }
-
- @MediumTest
- public void testBindView_MismatchBetweenCallLogAndMemoryCache_EnqueueRequest() {
- createCallLogEntryWithCachedValues(false);
-
- // Contact info contains a different name.
- ContactInfo info = createContactInfo();
- info.name = "new name";
- mAdapter.injectContactInfoForTest(TEST_NUMBER, TEST_COUNTRY_ISO, info);
-
- // Bind the views of a single row.
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- // There is one request for contact details.
- assertEquals(1, mAdapter.getContactInfoCache().requests.size());
-
- TestContactInfoCache.Request request = mAdapter.getContactInfoCache().requests.get(0);
- // Since there is something in the cache, it is not an immediate request.
- assertFalse("should not be immediate", request.immediate);
- }
-
- @MediumTest
- public void testBindView_WithCachedName() {
- createCallLogEntryWithCachedValues(
- "John Doe",
- Phone.TYPE_HOME,
- TEST_CACHED_NUMBER_LABEL);
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- assertNameIs(mViewHolder, "John Doe");
- assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME));
- }
-
- @MediumTest
- public void testBindView_UriNumber() {
- createCallLogEntryWithCachedValues(
- "sip:johndoe@gmail.com",
- AppCompatConstants.CALLS_INCOMING_TYPE,
- "John Doe",
- Phone.TYPE_HOME,
- TEST_DEFAULT_CUSTOM_LABEL,
- EMPTY_STRING,
- false /* inject */);
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- assertNameIs(mViewHolder, "John Doe");
- assertLabel(mViewHolder, "sip:johndoe@gmail.com", "sip:johndoe@gmail.com");
- }
-
- @MediumTest
- public void testBindView_HomeLabel() {
- createCallLogEntryWithCachedValues(
- "John Doe",
- Phone.TYPE_HOME,
- TEST_CACHED_NUMBER_LABEL);
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- assertNameIs(mViewHolder, "John Doe");
- assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME));
- }
-
- @MediumTest
- public void testBindView_WorkLabel() {
- createCallLogEntryWithCachedValues(
- "John Doe",
- Phone.TYPE_WORK,
- TEST_CACHED_NUMBER_LABEL);
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- assertNameIs(mViewHolder, "John Doe");
- assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_WORK));
- }
-
- @MediumTest
- public void testBindView_CustomLabel() {
- createCallLogEntryWithCachedValues(
- "John Doe",
- Phone.TYPE_CUSTOM,
- TEST_DEFAULT_CUSTOM_LABEL);
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- assertNameIs(mViewHolder, "John Doe");
- assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, TEST_DEFAULT_CUSTOM_LABEL);
- }
-
- @MediumTest
- public void testBindView_NumberOnlyDbCachedFormattedNumber() {
- createCallLogEntryWithCachedValues(
- TEST_NUMBER,
- AppCompatConstants.CALLS_INCOMING_TYPE,
- EMPTY_STRING,
- TEST_CACHED_NUMBER_TYPE,
- TEST_CACHED_NUMBER_LABEL,
- TEST_FORMATTED_NUMBER,
- false /* inject */);
-
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- assertNameIs(mViewHolder, TEST_FORMATTED_NUMBER);
- }
-
- @MediumTest
- public void testBindVoicemailPromoCard() {
- createCallLogEntry(TEST_NUMBER_1);
- createCallLogEntry(TEST_NUMBER_1);
- createCallLogEntry(TEST_NUMBER_2);
- createCallLogEntry(TEST_NUMBER_2);
- createCallLogEntry(TEST_NUMBER_2);
- createCallLogEntry(TEST_NUMBER_3);
-
- // Bind the voicemail promo card.
- mAdapter.showVoicemailPromoCard(true);
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(PromoCardViewHolder.createForTest(getContext()), 0);
-
- // Check that displaying the promo card does not affect the grouping or list display.
- mAdapter.onBindViewHolder(mViewHolder, 1);
- assertEquals(2, mAdapter.getGroupSize(1));
- assertEquals(TEST_NUMBER_1, mViewHolder.number);
-
- mAdapter.onBindViewHolder(mViewHolder, 2);
- assertEquals(3, mAdapter.getGroupSize(2));
- assertEquals(TEST_NUMBER_2, mViewHolder.number);
-
- mAdapter.onBindViewHolder(mViewHolder, 3);
- assertEquals(1, mAdapter.getGroupSize(3));
- assertEquals(TEST_NUMBER_3, mViewHolder.number);
- }
-
- public void testVoicemailArchive() {
- setUpArchiveAdapter();
- createVoicemailArchiveCallLogEntry();
-
- mAdapter.changeCursorVoicemail(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- assertEquals(Uri.parse(mViewHolder.voicemailUri),
- ContentUris.withAppendedId(
- VoicemailArchiveContract.VoicemailArchive.CONTENT_URI, 0));
- assertNull(mViewHolder.primaryActionButtonView.getTag());
- }
-
- private void createCallLogEntry() {
- createCallLogEntry(TEST_NUMBER);
- }
-
- private void createCallLogEntry(String testNumber) {
- createCallLogEntry(testNumber, EMPTY_STRING, NO_VALUE_SET, NO_VALUE_SET);
- }
-
- private void createPrivateCallLogEntry() {
- createCallLogEntry(
- EMPTY_STRING,
- EMPTY_STRING,
- Calls.PRESENTATION_RESTRICTED,
- AppCompatConstants.CALLS_INCOMING_TYPE);
- }
-
- private void createUnknownCallLogEntry() {
- createCallLogEntry(
- EMPTY_STRING,
- EMPTY_STRING,
- Calls.PRESENTATION_UNKNOWN,
- AppCompatConstants.CALLS_INCOMING_TYPE);
- }
-
- private void createVoicemailCallLogEntry() {
- createCallLogEntry(TEST_NUMBER, EMPTY_STRING, NO_VALUE_SET, Calls.VOICEMAIL_TYPE);
- }
-
- private void createVoicemailArchiveCallLogEntry() {
- createCallLogEntry(TEST_NUMBER, EMPTY_STRING, NO_VALUE_SET, ARCHIVE_TYPE);
- }
-
- private void createCallLogEntry(String number, String postDialDigits, int presentation,
- int type) {
- Object[] values = getValues(number, postDialDigits, presentation, type);
- mCursor.addRow(values);
- }
-
- private void createCallLogEntry(String number, String postDialDigits, String viaNumber,
- int presentation, int type) {
- Object[] values = getValues(number, postDialDigits, viaNumber, presentation, type);
- mCursor.addRow(values);
- }
-
- private void createCallLogEntryWithCachedValues(boolean inject) {
- createCallLogEntryWithCachedValues(
- TEST_NUMBER,
- NO_VALUE_SET,
- TEST_CACHED_NAME_PRIMARY,
- TEST_CACHED_NUMBER_TYPE,
- TEST_CACHED_NUMBER_LABEL,
- EMPTY_STRING,
- inject);
- }
-
- private void createCallLogEntryWithCachedValues(
- String cachedName, int cachedNumberType, String cachedNumberLabel) {
- createCallLogEntryWithCachedValues(
- TEST_NUMBER,
- NO_VALUE_SET,
- cachedName,
- cachedNumberType,
- cachedNumberLabel,
- EMPTY_STRING,
- false /* inject */);
- }
-
- /**
- * Inserts a new call log entry
- *
- * It includes the values for the cached contact associated with the number.
- *
- * @param number The phone number.
- * @param type Valid value of {@code Calls.TYPE}.
- * @param cachedName The name of the contact with this number
- * @param cachedNumberType The type of the number, from the contact with this number.
- * @param cachedNumberLabel The label of the number, from the contact with this number.
- * @param cachedFormattedNumber The formatted number, from the contact with this number.
- * @param inject Whether to inject the contact info into the adapter's ContactInfoCache.
- */
- private void createCallLogEntryWithCachedValues(
- String number,
- int type,
- String cachedName,
- int cachedNumberType,
- String cachedNumberLabel,
- String cachedFormattedNumber,
- boolean inject) {
- Object[] values = getValues(number, EMPTY_STRING, NO_VALUE_SET, type);
- values[CallLogQuery.CACHED_NAME] = cachedName;
- values[CallLogQuery.CACHED_NUMBER_TYPE] = cachedNumberType;
- values[CallLogQuery.CACHED_NUMBER_LABEL] = cachedNumberLabel;
- values[CallLogQuery.CACHED_FORMATTED_NUMBER] = cachedFormattedNumber;
-
- mCursor.addRow(values);
-
- if (inject) {
- ContactInfo contactInfo =
- createContactInfo(cachedName, cachedName, cachedNumberType, cachedNumberLabel);
- mAdapter.injectContactInfoForTest(number, TEST_COUNTRY_ISO, contactInfo);
- }
- }
-
- /**
- * @param number The phone number.
- * @param postDialDigits The post dial digits dialed (if any)
- * @param presentation Number representing display rules for "allowed",
- * "payphone", "restricted", or "unknown".
- * @param type The type of the call (outgoing/ingoing)
- */
- private Object[] getValues(
- String number,
- String postDialDigits,
- int presentation,
- int type) {
- return getValues(number, postDialDigits, "", presentation, type);
- }
-
- /**
- * @param number The phone number.
- * @param postDialDigits The post dial digits dialed (if any)
- * @param viaNumber The secondary number that the call was placed via
- * @param presentation Number representing display rules for "allowed",
- * "payphone", "restricted", or "unknown".
- * @param type The type of the call (outgoing/ingoing)
- */
- private Object[] getValues(
- String number,
- String postDialDigits,
- String viaNumber,
- int presentation,
- int type) {
- Object[] values = CallLogQueryTestUtils.createTestValues();
-
- values[CallLogQuery.ID] = mCursor.getCount();
- values[CallLogQuery.COUNTRY_ISO] = TEST_COUNTRY_ISO;
- values[CallLogQuery.DATE] = new Date().getTime();
- values[CallLogQuery.DURATION] = mRandom.nextInt(10 * 60);
-
- if (!TextUtils.isEmpty(number)) {
- values[CallLogQuery.NUMBER] = number;
- }
- if (!TextUtils.isEmpty(postDialDigits) && CompatUtils.isNCompatible()) {
- values[CallLogQuery.POST_DIAL_DIGITS] = postDialDigits;
- }
- if (!TextUtils.isEmpty(viaNumber) && CompatUtils.isNCompatible()) {
- values[CallLogQuery.VIA_NUMBER] = viaNumber;
- }
- if (presentation != NO_VALUE_SET) {
- values[CallLogQuery.NUMBER_PRESENTATION] = presentation;
- }
- if (type != NO_VALUE_SET) {
- values[CallLogQuery.CALL_TYPE] = type;
- }
- if (type == AppCompatConstants.CALLS_VOICEMAIL_TYPE) {
- values[CallLogQuery.VOICEMAIL_URI] = ContentUris.withAppendedId(
- VoicemailContract.Voicemails.CONTENT_URI, mCursor.getCount());
- }
- if (type == ARCHIVE_TYPE) {
- values[CallLogQuery.VOICEMAIL_URI] = ContentUris.withAppendedId(
- VoicemailArchiveContract.VoicemailArchive.CONTENT_URI, mCursor.getCount());
- }
-
- return values;
- }
-
- private ContactInfo createContactInfo() {
- return createContactInfo(
- TEST_CACHED_NAME_PRIMARY,
- TEST_CACHED_NAME_ALTERNATIVE);
- }
-
- private ContactInfo createContactInfo(String namePrimary, String nameAlternative) {
- return createContactInfo(
- namePrimary,
- nameAlternative,
- TEST_CACHED_NUMBER_TYPE,
- TEST_CACHED_NUMBER_LABEL);
- }
-
- /** Returns a contact info with default values. */
- private ContactInfo createContactInfo(String namePrimary, String nameAlternative, int type, String label) {
- ContactInfo info = new ContactInfo();
- info.number = TEST_NUMBER;
- info.name = namePrimary;
- info.nameAlternative = nameAlternative;
- info.type = type;
- info.label = label;
- info.formattedNumber = TEST_FORMATTED_NUMBER;
- info.normalizedNumber = TEST_NUMBER;
- info.lookupUri = TEST_LOOKUP_URI;
- return info;
- }
-
- // Asserts that the name text view is shown and contains the given text.
- private void assertNameIs(CallLogListItemViewHolder viewHolder, String name) {
- assertEquals(View.VISIBLE, viewHolder.phoneCallDetailsViews.nameView.getVisibility());
- assertEquals(name, viewHolder.phoneCallDetailsViews.nameView.getText());
- }
-
- // Asserts that the label text view contains the given text.
- private void assertLabel(
- CallLogListItemViewHolder viewHolder, CharSequence number, CharSequence label) {
- if (label != null) {
- assertTrue(viewHolder.phoneCallDetailsViews.callLocationAndDate.getText()
- .toString().contains(label));
- }
- }
-
- private void assertHasCallAction(CallLogListItemViewHolder viewHolder) {
- assertHasCallActionToGivenNumber(viewHolder, TEST_NUMBER);
- }
-
- private void assertHasCallActionToGivenNumber(CallLogListItemViewHolder viewHolder,
- String number) {
- IntentProvider intentProvider =
- (IntentProvider) viewHolder.primaryActionButtonView.getTag();
- Intent intent = intentProvider.getIntent(getContext());
- assertEquals(TestConstants.CALL_INTENT_ACTION, intent.getAction());
- assertEquals(Uri.parse("tel:" + Uri.encode(number)), intent.getData());
- }
-
- /** Returns the label associated with a given phone type. */
- private CharSequence getTypeLabel(int phoneType) {
- return Phone.getTypeLabel(getContext().getResources(), phoneType, "");
- }
-
- private void setUpArchiveAdapter() {
- // Use a call fetcher that does not do anything.
- CallLogAdapter.CallFetcher fakeCallFetcher = new CallLogAdapter.CallFetcher() {
- @Override
- public void fetchCalls() {}
- };
-
- ContactInfoHelper fakeContactInfoHelper =
- new ContactInfoHelper(getContext(), TEST_COUNTRY_ISO) {
- @Override
- public ContactInfo lookupNumber(String number, String countryIso) {
- ContactInfo info = new ContactInfo();
- info.number = number;
- info.formattedNumber = number;
- return info;
- }
- };
-
- mAdapter = new TestCallLogAdapter(getContext(), fakeCallFetcher, fakeContactInfoHelper,
- CallLogAdapter.ACTIVITY_TYPE_ARCHIVE);
- }
-
- /// Subclass of {@link CallLogAdapter} used in tests to intercept certain calls.
- private static final class TestCallLogAdapter extends CallLogAdapter {
- public TestCallLogAdapter(Context context, CallFetcher callFetcher,
- ContactInfoHelper contactInfoHelper, int mActivity) {
- super(context, callFetcher, contactInfoHelper, null,
- mActivity);
- mContactInfoCache = new TestContactInfoCache(
- contactInfoHelper, mOnContactInfoChangedListener);
- }
-
- public TestContactInfoCache getContactInfoCache() {
- return (TestContactInfoCache) mContactInfoCache;
- }
-
- public void showVoicemailPromoCard(boolean show) {
- mShowVoicemailPromoCard = show;
- }
- }
-
- private static final class TestContactInfoCache extends ContactInfoCache {
- public static class Request {
- public final String number;
- public final String countryIso;
- public final ContactInfo callLogInfo;
- public final boolean immediate;
-
- public Request(String number, String countryIso, ContactInfo callLogInfo,
- boolean immediate) {
- this.number = number;
- this.countryIso = countryIso;
- this.callLogInfo = callLogInfo;
- this.immediate = immediate;
- }
- }
-
- public final List<Request> requests = Lists.newArrayList();
-
- /**
- * Dummy contactInfo to return in the even that the getValue method has been mocked
- */
- private ContactInfo mContactInfo;
-
- public TestContactInfoCache(
- ContactInfoHelper contactInfoHelper, OnContactInfoChangedListener listener) {
- super(contactInfoHelper, listener);
- }
-
- /**
- * Sets the given value to be returned by all calls to
- * {@link #getValue(String, String, ContactInfo)}
- *
- * @param contactInfo the contactInfo
- */
- public void mockGetValue(ContactInfo contactInfo) {
- this.mContactInfo = contactInfo;
- }
-
- @Override
- public ContactInfo getValue(String number, String countryIso,
- ContactInfo cachedContactInfo) {
- if (mContactInfo != null) {
- return mContactInfo;
- }
- return super.getValue(number, countryIso, cachedContactInfo);
- }
-
- @Override
- protected void enqueueRequest(String number, String countryIso, ContactInfo callLogInfo,
- boolean immediate) {
- requests.add(new Request(number, countryIso, callLogInfo, immediate));
- }
- }
-}
diff --git a/tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java b/tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java
deleted file mode 100644
index beb83b1ad..000000000
--- a/tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import static com.google.common.collect.Lists.newArrayList;
-
-import android.database.MatrixCursor;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.dialer.util.AppCompatConstants;
-
-import java.util.List;
-
-/**
- * Unit tests for {@link CallLogGroupBuilder}
- */
-@SmallTest
-public class CallLogGroupBuilderTest extends AndroidTestCase {
- /** A phone number for testing. */
- private static final String TEST_NUMBER1 = "14125551234";
- /** A phone number for testing. */
- private static final String TEST_NUMBER2 = "14125555555";
- /** A post-dial string for testing */
- private static final String TEST_POST_DIAL_DIGITS = ";12435;0987";
-
- /** The object under test. */
- private CallLogGroupBuilder mBuilder;
- /** Records the created groups. */
- private FakeGroupCreator mFakeGroupCreator;
- /** Cursor to store the values. */
- private MatrixCursor mCursor;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mFakeGroupCreator = new FakeGroupCreator();
- mBuilder = new CallLogGroupBuilder(mFakeGroupCreator);
- createCursor();
- }
-
- @Override
- protected void tearDown() throws Exception {
- mCursor = null;
- mBuilder = null;
- mFakeGroupCreator = null;
- super.tearDown();
- }
-
- public void testAddGroups_NoCalls() {
- mBuilder.addGroups(mCursor);
- assertEquals(0, mFakeGroupCreator.groups.size());
- }
-
- public void testAddGroups_OneCall() {
- addCallLogEntry(TEST_NUMBER1, AppCompatConstants.CALLS_INCOMING_TYPE);
- mBuilder.addGroups(mCursor);
- assertEquals(1, mFakeGroupCreator.groups.size());
- }
-
- public void testAddGroups_TwoCallsNotMatching() {
- addCallLogEntry(TEST_NUMBER1, AppCompatConstants.CALLS_INCOMING_TYPE);
- addCallLogEntry(TEST_NUMBER2, AppCompatConstants.CALLS_INCOMING_TYPE);
- mBuilder.addGroups(mCursor);
- assertEquals(2, mFakeGroupCreator.groups.size());
- }
-
- public void testAddGroups_ThreeCallsMatching() {
- addCallLogEntry(TEST_NUMBER1, AppCompatConstants.CALLS_INCOMING_TYPE);
- addCallLogEntry(TEST_NUMBER1, AppCompatConstants.CALLS_INCOMING_TYPE);
- addCallLogEntry(TEST_NUMBER1, AppCompatConstants.CALLS_INCOMING_TYPE);
- mBuilder.addGroups(mCursor);
- assertEquals(1, mFakeGroupCreator.groups.size());
- assertGroupIs(0, 3, mFakeGroupCreator.groups.get(0));
- }
-
- public void testAddGroups_WithPostDialMatching() {
- addCallLogEntryWithPostDialDigits(TEST_NUMBER1, TEST_POST_DIAL_DIGITS,
- AppCompatConstants.CALLS_OUTGOING_TYPE);
- addCallLogEntryWithPostDialDigits(TEST_NUMBER1, TEST_POST_DIAL_DIGITS,
- AppCompatConstants.CALLS_OUTGOING_TYPE);
- addCallLogEntryWithPostDialDigits(TEST_NUMBER1, "",
- AppCompatConstants.CALLS_OUTGOING_TYPE);
-
- mBuilder.addGroups(mCursor);
-
- if (CompatUtils.isNCompatible()) {
- assertEquals(2, mFakeGroupCreator.groups.size());
- assertGroupIs(0, 2, mFakeGroupCreator.groups.get(0));
- assertGroupIs(2, 1, mFakeGroupCreator.groups.get(1));
- } else {
- assertEquals(1, mFakeGroupCreator.groups.size());
- assertGroupIs(0, 3, mFakeGroupCreator.groups.get(0));
- }
- }
-
- public void testAddGroups_WithViaNumberMatching() {
- addCallLogEntryWithViaNumber(TEST_NUMBER1, TEST_NUMBER2,
- AppCompatConstants.CALLS_OUTGOING_TYPE);
- addCallLogEntryWithViaNumber(TEST_NUMBER1, TEST_NUMBER2,
- AppCompatConstants.CALLS_OUTGOING_TYPE);
- addCallLogEntryWithViaNumber(TEST_NUMBER1, "",
- AppCompatConstants.CALLS_OUTGOING_TYPE);
-
- mBuilder.addGroups(mCursor);
-
- if (CompatUtils.isNCompatible()) {
- assertEquals(2, mFakeGroupCreator.groups.size());
- assertGroupIs(0, 2, mFakeGroupCreator.groups.get(0));
- assertGroupIs(2, 1, mFakeGroupCreator.groups.get(1));
- } else {
- assertEquals(1, mFakeGroupCreator.groups.size());
- assertGroupIs(0, 3, mFakeGroupCreator.groups.get(0));
- }
- }
-
- public void testAddGroups_MatchingIncomingAndOutgoing() {
- addCallLogEntry(TEST_NUMBER1, AppCompatConstants.CALLS_INCOMING_TYPE);
- addCallLogEntry(TEST_NUMBER1, AppCompatConstants.CALLS_OUTGOING_TYPE);
- addCallLogEntry(TEST_NUMBER1, AppCompatConstants.CALLS_INCOMING_TYPE);
- mBuilder.addGroups(mCursor);
- assertEquals(1, mFakeGroupCreator.groups.size());
- assertGroupIs(0, 3, mFakeGroupCreator.groups.get(0));
- }
-
- public void testGrouping_Voicemail() {
- // Does not group with other types of calls, include voicemail themselves.
- assertCallsAreNotGrouped(
- AppCompatConstants.CALLS_VOICEMAIL_TYPE, AppCompatConstants.CALLS_MISSED_TYPE);
- assertCallsAreNotGrouped(
- AppCompatConstants.CALLS_VOICEMAIL_TYPE, AppCompatConstants.CALLS_VOICEMAIL_TYPE);
- assertCallsAreNotGrouped(
- AppCompatConstants.CALLS_VOICEMAIL_TYPE, AppCompatConstants.CALLS_INCOMING_TYPE);
- assertCallsAreNotGrouped(
- AppCompatConstants.CALLS_VOICEMAIL_TYPE, AppCompatConstants.CALLS_OUTGOING_TYPE);
- }
-
- public void testGrouping_VoicemailArchive() {
- // Does not group with other types of calls, include voicemail themselves.
- assertVoicemailsAreNotGrouped(
- AppCompatConstants.CALLS_VOICEMAIL_TYPE, AppCompatConstants.CALLS_MISSED_TYPE);
- assertVoicemailsAreNotGrouped(
- AppCompatConstants.CALLS_VOICEMAIL_TYPE, AppCompatConstants.CALLS_VOICEMAIL_TYPE);
- assertVoicemailsAreNotGrouped(
- AppCompatConstants.CALLS_VOICEMAIL_TYPE, AppCompatConstants.CALLS_INCOMING_TYPE);
- assertVoicemailsAreNotGrouped(
- AppCompatConstants.CALLS_VOICEMAIL_TYPE, AppCompatConstants.CALLS_OUTGOING_TYPE);
- }
-
- public void testGrouping_Missed() {
- // Groups with one or more missed calls.
- assertCallsAreGrouped(
- AppCompatConstants.CALLS_MISSED_TYPE, AppCompatConstants.CALLS_MISSED_TYPE);
- assertCallsAreGrouped(
- AppCompatConstants.CALLS_MISSED_TYPE,
- AppCompatConstants.CALLS_MISSED_TYPE,
- AppCompatConstants.CALLS_MISSED_TYPE);
- // Does not group with other types of calls.
- assertCallsAreNotGrouped(
- AppCompatConstants.CALLS_MISSED_TYPE, AppCompatConstants.CALLS_VOICEMAIL_TYPE);
- assertCallsAreGrouped(
- AppCompatConstants.CALLS_MISSED_TYPE, AppCompatConstants.CALLS_INCOMING_TYPE);
- assertCallsAreGrouped(
- AppCompatConstants.CALLS_MISSED_TYPE, AppCompatConstants.CALLS_OUTGOING_TYPE);
- }
-
- public void testGrouping_Incoming() {
- // Groups with one or more incoming or outgoing.
- assertCallsAreGrouped(
- AppCompatConstants.CALLS_INCOMING_TYPE, AppCompatConstants.CALLS_INCOMING_TYPE);
- assertCallsAreGrouped(
- AppCompatConstants.CALLS_INCOMING_TYPE, AppCompatConstants.CALLS_OUTGOING_TYPE);
- assertCallsAreGrouped(
- AppCompatConstants.CALLS_INCOMING_TYPE,
- AppCompatConstants.CALLS_INCOMING_TYPE,
- AppCompatConstants.CALLS_OUTGOING_TYPE);
- assertCallsAreGrouped(
- AppCompatConstants.CALLS_INCOMING_TYPE,
- AppCompatConstants.CALLS_OUTGOING_TYPE,
- AppCompatConstants.CALLS_INCOMING_TYPE);
- assertCallsAreGrouped(
- AppCompatConstants.CALLS_INCOMING_TYPE, AppCompatConstants.CALLS_MISSED_TYPE);
- // Does not group with voicemail and missed calls.
- assertCallsAreNotGrouped(
- AppCompatConstants.CALLS_INCOMING_TYPE, AppCompatConstants.CALLS_VOICEMAIL_TYPE);
- }
-
- public void testGrouping_Outgoing() {
- // Groups with one or more incoming or outgoing.
- assertCallsAreGrouped(
- AppCompatConstants.CALLS_OUTGOING_TYPE, AppCompatConstants.CALLS_INCOMING_TYPE);
- assertCallsAreGrouped(
- AppCompatConstants.CALLS_OUTGOING_TYPE, AppCompatConstants.CALLS_OUTGOING_TYPE);
- assertCallsAreGrouped(
- AppCompatConstants.CALLS_OUTGOING_TYPE,
- AppCompatConstants.CALLS_INCOMING_TYPE,
- AppCompatConstants.CALLS_OUTGOING_TYPE);
- assertCallsAreGrouped(
- AppCompatConstants.CALLS_OUTGOING_TYPE,
- AppCompatConstants.CALLS_OUTGOING_TYPE,
- AppCompatConstants.CALLS_INCOMING_TYPE);
- assertCallsAreGrouped(
- AppCompatConstants.CALLS_INCOMING_TYPE, AppCompatConstants.CALLS_MISSED_TYPE);
- // Does not group with voicemail and missed calls.
- assertCallsAreNotGrouped(
- AppCompatConstants.CALLS_INCOMING_TYPE, AppCompatConstants.CALLS_VOICEMAIL_TYPE);
- }
-
- public void testGrouping_Blocked() {
- assertCallsAreNotGrouped(
- AppCompatConstants.CALLS_BLOCKED_TYPE, AppCompatConstants.CALLS_INCOMING_TYPE);
- assertCallsAreNotGrouped(
- AppCompatConstants.CALLS_BLOCKED_TYPE, AppCompatConstants.CALLS_OUTGOING_TYPE);
- assertCallsAreNotGrouped(
- AppCompatConstants.CALLS_BLOCKED_TYPE, AppCompatConstants.CALLS_MISSED_TYPE);
-
- }
-
- public void testAddGroups_Separate() {
- addMultipleCallLogEntries(TEST_NUMBER1,
- AppCompatConstants.CALLS_VOICEMAIL_TYPE, // Group 1: 0
- AppCompatConstants.CALLS_INCOMING_TYPE, // Group 2: 1
- AppCompatConstants.CALLS_OUTGOING_TYPE, // Group 3: 2
- AppCompatConstants.CALLS_MISSED_TYPE); // Group 4: 3
- mBuilder.addVoicemailGroups(mCursor);
-
- assertEquals(4, mFakeGroupCreator.groups.size());
- assertGroupIs(0, 1, mFakeGroupCreator.groups.get(0));
- assertGroupIs(1, 1, mFakeGroupCreator.groups.get(1));
- assertGroupIs(2, 1, mFakeGroupCreator.groups.get(2));
- assertGroupIs(3, 1, mFakeGroupCreator.groups.get(3));
- }
-
- public void testAddGroups_Mixed() {
- addMultipleCallLogEntries(TEST_NUMBER1,
- AppCompatConstants.CALLS_VOICEMAIL_TYPE, // Group 1: 0
- AppCompatConstants.CALLS_INCOMING_TYPE, // Group 2: 1-4
- AppCompatConstants.CALLS_OUTGOING_TYPE,
- AppCompatConstants.CALLS_MISSED_TYPE,
- AppCompatConstants.CALLS_MISSED_TYPE,
- AppCompatConstants.CALLS_VOICEMAIL_TYPE, // Group 3: 5
- AppCompatConstants.CALLS_INCOMING_TYPE, // Group 4: 6
- AppCompatConstants.CALLS_VOICEMAIL_TYPE, // Group 5: 7
- AppCompatConstants.CALLS_MISSED_TYPE, // Group 6: 8-10
- AppCompatConstants.CALLS_MISSED_TYPE,
- AppCompatConstants.CALLS_OUTGOING_TYPE);
- mBuilder.addGroups(mCursor);
-
- assertEquals(6, mFakeGroupCreator.groups.size());
- assertGroupIs(0, 1, mFakeGroupCreator.groups.get(0));
- assertGroupIs(1, 4, mFakeGroupCreator.groups.get(1));
- assertGroupIs(5, 1, mFakeGroupCreator.groups.get(2));
- assertGroupIs(6, 1, mFakeGroupCreator.groups.get(3));
- assertGroupIs(7, 1, mFakeGroupCreator.groups.get(4));
- assertGroupIs(8, 3, mFakeGroupCreator.groups.get(5));
- }
-
- public void testAddGroups_Blocked() {
- addMultipleCallLogEntries(TEST_NUMBER1,
- AppCompatConstants.CALLS_INCOMING_TYPE, // Group 1: 0-1
- AppCompatConstants.CALLS_OUTGOING_TYPE,
- AppCompatConstants.CALLS_BLOCKED_TYPE, // Group 2: 2
- AppCompatConstants.CALLS_MISSED_TYPE, // Group 3: 3
- AppCompatConstants.CALLS_BLOCKED_TYPE, // Group 4: 4-5
- AppCompatConstants.CALLS_BLOCKED_TYPE);
- mBuilder.addGroups(mCursor);
-
- assertEquals(4, mFakeGroupCreator.groups.size());
- assertGroupIs(0, 2, mFakeGroupCreator.groups.get(0));
- assertGroupIs(2, 1, mFakeGroupCreator.groups.get(1));
- assertGroupIs(3, 1, mFakeGroupCreator.groups.get(2));
- assertGroupIs(4, 2, mFakeGroupCreator.groups.get(3));
- }
-
- public void testEqualPhoneNumbers() {
- // Identical.
- assertTrue(mBuilder.equalNumbers("6505555555", "6505555555"));
- assertTrue(mBuilder.equalNumbers("650 555 5555", "650 555 5555"));
- // Formatting.
- assertTrue(mBuilder.equalNumbers("6505555555", "650 555 5555"));
- assertTrue(mBuilder.equalNumbers("6505555555", "(650) 555-5555"));
- assertTrue(mBuilder.equalNumbers("650 555 5555", "(650) 555-5555"));
- // Short codes.
- assertTrue(mBuilder.equalNumbers("55555", "55555"));
- assertTrue(mBuilder.equalNumbers("55555", "555 55"));
- // Different numbers.
- assertFalse(mBuilder.equalNumbers("6505555555", "650555555"));
- assertFalse(mBuilder.equalNumbers("6505555555", "6505555551"));
- assertFalse(mBuilder.equalNumbers("650 555 5555", "650 555 555"));
- assertFalse(mBuilder.equalNumbers("650 555 5555", "650 555 5551"));
- assertFalse(mBuilder.equalNumbers("55555", "5555"));
- assertFalse(mBuilder.equalNumbers("55555", "55551"));
- // SIP addresses.
- assertTrue(mBuilder.equalNumbers("6505555555@host.com", "6505555555@host.com"));
- assertTrue(mBuilder.equalNumbers("6505555555@host.com", "6505555555@HOST.COM"));
- assertTrue(mBuilder.equalNumbers("user@host.com", "user@host.com"));
- assertTrue(mBuilder.equalNumbers("user@host.com", "user@HOST.COM"));
- assertFalse(mBuilder.equalNumbers("USER@host.com", "user@host.com"));
- assertFalse(mBuilder.equalNumbers("user@host.com", "user@host1.com"));
- // SIP address vs phone number.
- assertFalse(mBuilder.equalNumbers("6505555555@host.com", "6505555555"));
- assertFalse(mBuilder.equalNumbers("6505555555", "6505555555@host.com"));
- assertFalse(mBuilder.equalNumbers("user@host.com", "6505555555"));
- assertFalse(mBuilder.equalNumbers("6505555555", "user@host.com"));
- // Nulls.
- assertTrue(mBuilder.equalNumbers(null, null));
- assertFalse(mBuilder.equalNumbers(null, "6505555555"));
- assertFalse(mBuilder.equalNumbers("6505555555", null));
- assertFalse(mBuilder.equalNumbers(null, "6505555555@host.com"));
- assertFalse(mBuilder.equalNumbers("6505555555@host.com", null));
- }
-
- public void testCompareSipAddresses() {
- // Identical.
- assertTrue(mBuilder.compareSipAddresses("6505555555@host.com", "6505555555@host.com"));
- assertTrue(mBuilder.compareSipAddresses("user@host.com", "user@host.com"));
- // Host is case insensitive.
- assertTrue(mBuilder.compareSipAddresses("6505555555@host.com", "6505555555@HOST.COM"));
- assertTrue(mBuilder.compareSipAddresses("user@host.com", "user@HOST.COM"));
- // Userinfo is case sensitive.
- assertFalse(mBuilder.compareSipAddresses("USER@host.com", "user@host.com"));
- // Different hosts.
- assertFalse(mBuilder.compareSipAddresses("user@host.com", "user@host1.com"));
- // Different users.
- assertFalse(mBuilder.compareSipAddresses("user1@host.com", "user@host.com"));
- // Nulls.
- assertTrue(mBuilder.compareSipAddresses(null, null));
- assertFalse(mBuilder.compareSipAddresses(null, "6505555555@host.com"));
- assertFalse(mBuilder.compareSipAddresses("6505555555@host.com", null));
- }
-
- /** Creates (or recreates) the cursor used to store the call log content for the tests. */
- private void createCursor() {
- mCursor = new MatrixCursor(CallLogQuery._PROJECTION);
- }
-
- /** Clears the content of the {@link FakeGroupCreator} used in the tests. */
- private void clearFakeGroupCreator() {
- mFakeGroupCreator.groups.clear();
- }
-
- /** Asserts that calls of the given types are grouped together into a single group. */
- private void assertCallsAreGrouped(int... types) {
- createCursor();
- clearFakeGroupCreator();
- addMultipleCallLogEntries(TEST_NUMBER1, types);
- mBuilder.addGroups(mCursor);
- assertEquals(1, mFakeGroupCreator.groups.size());
- assertGroupIs(0, types.length, mFakeGroupCreator.groups.get(0));
-
- }
-
- /** Asserts that calls of the given types are not grouped together at all. */
- private void assertCallsAreNotGrouped(int... types) {
- createCursor();
- clearFakeGroupCreator();
- addMultipleCallLogEntries(TEST_NUMBER1, types);
- mBuilder.addGroups(mCursor);
- assertEquals(types.length, mFakeGroupCreator.groups.size());
- }
-
- /** Asserts that voicemails are not grouped together with other types at all. */
- private void assertVoicemailsAreNotGrouped(int... types) {
- createCursor();
- clearFakeGroupCreator();
- addMultipleCallLogEntries(TEST_NUMBER1, types);
- mBuilder.addVoicemailGroups(mCursor);
- assertEquals(types.length, mFakeGroupCreator.groups.size());
- }
-
- /** Adds a set of calls with the given types, all from the same number, in the old section. */
- private void addMultipleCallLogEntries(String number, int... types) {
- for (int type : types) {
- addCallLogEntry(number, type);
- }
- }
- /** Adds a call log entry with the given number and type to the cursor. */
- private void addCallLogEntry(String number, int type) {
- addCallLogEntryWithPostDialDigits(number, "", type);
- }
-
- /** Adds a call log entry with the given number, post-dial digits, and type to the cursor. */
- private void addCallLogEntryWithPostDialDigits(String number, String postDialDigits, int type) {
- mCursor.moveToNext();
- Object[] values = CallLogQueryTestUtils.createTestValues();
- values[CallLogQuery.ID] = mCursor.getPosition();
- values[CallLogQuery.NUMBER] = number;
- values[CallLogQuery.CALL_TYPE] = type;
- if (CompatUtils.isNCompatible()) {
- values[CallLogQuery.POST_DIAL_DIGITS] = postDialDigits;
- }
- mCursor.addRow(values);
- }
-
- /** Adds a call log entry with the given number, post-dial digits, and type to the cursor. */
- private void addCallLogEntryWithViaNumber(String number, String viaNumber, int type) {
- mCursor.moveToNext();
- Object[] values = CallLogQueryTestUtils.createTestValues();
- values[CallLogQuery.ID] = mCursor.getPosition();
- values[CallLogQuery.NUMBER] = number;
- values[CallLogQuery.CALL_TYPE] = type;
- if (CompatUtils.isNCompatible()) {
- values[CallLogQuery.VIA_NUMBER] = viaNumber;
- }
- mCursor.addRow(values);
- }
-
- /** Adds a call log entry with a header to the cursor. */
- private void addCallLogHeader(int section) {
- mCursor.moveToNext();
- Object[] values = CallLogQueryTestUtils.createTestValues();
- values[CallLogQuery.ID] = mCursor.getPosition();
- mCursor.addRow(values);
- }
-
- /** Asserts that the group matches the given values. */
- private void assertGroupIs(int cursorPosition, int size, GroupSpec group) {
- assertEquals(cursorPosition, group.cursorPosition);
- assertEquals(size, group.size);
- }
-
- /** Defines an added group. Used by the {@link FakeGroupCreator}. */
- private static class GroupSpec {
- /** The starting position of the group. */
- public final int cursorPosition;
- /** The number of elements in the group. */
- public final int size;
-
- public GroupSpec(int cursorPosition, int size) {
- this.cursorPosition = cursorPosition;
- this.size = size;
- }
- }
-
- /** Fake implementation of a GroupCreator which stores the created groups in a member field. */
- private static class FakeGroupCreator implements CallLogGroupBuilder.GroupCreator {
- /** The list of created groups. */
- public final List<GroupSpec> groups = newArrayList();
-
- @Override
- public void addGroup(int cursorPosition, int size) {
- groups.add(new GroupSpec(cursorPosition, size));
- }
-
- @Override
- public void setDayGroup(long rowId, int dayGroup) {
- //No-op
- }
-
- @Override
- public void clearDayGroups() {
- //No-op
- }
- }
-}
diff --git a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java b/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
deleted file mode 100644
index daba42857..000000000
--- a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.provider.CallLog.Calls;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
-
-import com.android.contacts.common.CallUtil;
-import com.android.dialer.PhoneCallDetails;
-import com.android.dialer.R;
-import com.android.dialer.calllog.calllogcache.TestTelecomCallLogCache;
-import com.android.dialer.util.AppCompatConstants;
-
-/**
- * Unit tests for {@link CallLogListItemHelper}.
- */
-@MediumTest
-public class CallLogListItemHelperTest extends AndroidTestCase {
- /** A test phone number for phone calls. */
- private static final String TEST_NUMBER = "14125555555";
- /** The formatted version of {@link #TEST_NUMBER}. */
- private static final String TEST_FORMATTED_NUMBER = "1-412-255-5555";
- /** A test date value for phone calls. */
- private static final long TEST_DATE = 1300000000;
- /** A test duration value for phone calls. */
- private static final long TEST_DURATION = 62300;
- /** A test voicemail number. */
- private static final String TEST_VOICEMAIL_NUMBER = "123";
- /** The country ISO name used in the tests. */
- private static final String TEST_COUNTRY_ISO = "US";
- /** The geocoded location used in the tests. */
- private static final String TEST_GEOCODE = "United States";
-
- /** The object under test. */
- private CallLogListItemHelper mHelper;
-
- /** The views used in the tests. */
- private CallLogListItemViewHolder mViewHolder;
-
- private Context mContext;
- private Resources mResources;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getContext();
- mResources = mContext.getResources();
- final TestTelecomCallLogCache phoneUtils =
- new TestTelecomCallLogCache(mContext, TEST_VOICEMAIL_NUMBER, "");
- PhoneCallDetailsHelper phoneCallDetailsHelper =
- new PhoneCallDetailsHelper(mContext, mResources, phoneUtils);
- mHelper = new CallLogListItemHelper(phoneCallDetailsHelper, mResources, phoneUtils);
- mViewHolder = CallLogListItemViewHolder.createForTest(mContext);
-
- }
-
- @Override
- protected void tearDown() throws Exception {
- mHelper = null;
- mViewHolder = null;
- super.tearDown();
- }
-
- public void testSetPhoneCallDetails() {
- setPhoneCallDetailsWithNumber("12125551234", Calls.PRESENTATION_ALLOWED,
- "1-212-555-1234");
- assertEquals(View.VISIBLE, mViewHolder.primaryActionButtonView.getVisibility());
- }
-
- public void testSetPhoneCallDetails_Unknown() {
- setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_UNKNOWN, "");
- assertNoCallIntent();
- }
-
- public void testSetPhoneCallDetails_Private() {
- setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_RESTRICTED, "");
- assertNoCallIntent();
- }
-
- public void testSetPhoneCallDetails_Payphone() {
- setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_PAYPHONE, "");
- assertNoCallIntent();
- }
-
- public void testSetPhoneCallDetails_VoicemailNumber() {
- setPhoneCallDetailsWithNumber(TEST_VOICEMAIL_NUMBER,
- Calls.PRESENTATION_ALLOWED, TEST_VOICEMAIL_NUMBER);
- assertEquals(View.VISIBLE, mViewHolder.voicemailPlaybackView.getVisibility());
- }
-
- public void testSetPhoneCallDetails_ReadVoicemail() {
- PhoneCallDetails details =
- getPhoneCallDetailsWithTypes(AppCompatConstants.CALLS_VOICEMAIL_TYPE);
- mHelper.setPhoneCallDetails(mViewHolder, details);
- assertEquals(View.VISIBLE, mViewHolder.voicemailPlaybackView.getVisibility());
- }
-
- public void testSetPhoneCallDetails_UnreadVoicemail() {
- PhoneCallDetails details =
- getPhoneCallDetailsWithTypes(AppCompatConstants.CALLS_VOICEMAIL_TYPE);
- mHelper.setPhoneCallDetails(mViewHolder, details);
- assertEquals(View.VISIBLE, mViewHolder.voicemailPlaybackView.getVisibility());
- }
-
- public void testSetPhoneCallDetails_VoicemailFromUnknown() {
- setPhoneCallDetailsWithNumberAndType("", Calls.PRESENTATION_UNKNOWN,
- "", AppCompatConstants.CALLS_VOICEMAIL_TYPE);
- assertEquals(View.VISIBLE, mViewHolder.voicemailPlaybackView.getVisibility());
- }
-
- /**
- * Test getCallDescriptionID method used to get the accessibility description for calls.
- */
- public void testGetCallDescriptionID_Answered() {
- int[] callTypes = new int[] {AppCompatConstants.CALLS_INCOMING_TYPE};
- assertEquals(R.string.description_incoming_answered_call,
- mHelper.getCallDescriptionStringID(callTypes, true));
- }
-
- /**
- * Test getCallDescriptionID method used to get the accessibility description for calls.
- */
- public void testGetCallDescriptionID_Missed() {
- int[] callTypes = new int[] {AppCompatConstants.CALLS_MISSED_TYPE};
- assertEquals(R.string.description_incoming_missed_call,
- mHelper.getCallDescriptionStringID(callTypes, true));
- }
-
- /**
- * Test getCallDescriptionID method used to get the accessibility description for calls.
- * Test case where an outgoing call is made to a known number and there is a history of
- * only a single call for this caller.
- */
- public void testGetCallDescriptionID_OutgoingSingle() {
- int[] callTypes = new int[] {AppCompatConstants.CALLS_OUTGOING_TYPE};
- assertEquals(R.string.description_outgoing_call,
- mHelper.getCallDescriptionStringID(callTypes, true));
- }
-
- /**
- * Test getCallDescriptionID method used to get the accessibility description for calls.
- * Test case where an outgoing call is made to a known number and there is a history of
- * many calls for this caller.
- */
- public void testGetCallDescriptionID_OutgoingMultiple() {
- int[] callTypes = new int[] {
- AppCompatConstants.CALLS_OUTGOING_TYPE,
- AppCompatConstants.CALLS_OUTGOING_TYPE
- };
- assertEquals(R.string.description_outgoing_call,
- mHelper.getCallDescriptionStringID(callTypes, true));
- }
-
- /**
- * Test getCallDescription method used to get the accessibility description for calls.
- * For read voicemail calls, we should have "Voicemail" in the description.
- */
- public void testGetCallDescriptionID_Voicemail() {
- int[] callTypes = new int[] {AppCompatConstants.CALLS_VOICEMAIL_TYPE};
- assertEquals(R.string.description_read_voicemail,
- mHelper.getCallDescriptionStringID(callTypes, true));
- }
-
- /**
- * Test getCallDescription method used to get the accessibility description for calls.
- * For unread voicemail calls, we should have "Unread voicemail" in the description.
- */
- public void testGetCallDescriptionID_UnreadVoicemail() {
- int[] callTypes = new int[] {AppCompatConstants.CALLS_VOICEMAIL_TYPE};
- assertEquals(R.string.description_unread_voicemail,
- mHelper.getCallDescriptionStringID(callTypes, false));
- }
-
- /**
- * Test getCallDescription method used to get the accessibility description for calls.
- * Test that the "X calls" message is not present if there is only a single call.
- */
- public void testGetCallDescription_NumCallsSingle() {
- PhoneCallDetails details =
- getPhoneCallDetailsWithTypes(AppCompatConstants.CALLS_VOICEMAIL_TYPE);
- CharSequence description = mHelper.getCallDescription(details);
-
- // Rather than hard coding the "X calls" string message, we'll generate it with an empty
- // number of calls, and trim the resulting string. This gets us just the word "calls",
- // and ensures any trivial changes to that string resource won't unnecessarily break
- // the unit test.
- assertFalse(description.toString()
- .contains(this.mResources.getString(R.string.description_num_calls, "").trim()));
- }
-
- /**
- * Test getCallDescription method used to get the accessibility description for calls.
- * Test that the "X calls" message is present if there are many calls.
- */
- public void testGetCallDescription_NumCallsMultiple() {
- PhoneCallDetails details = getPhoneCallDetailsWithTypes(
- AppCompatConstants.CALLS_VOICEMAIL_TYPE, AppCompatConstants.CALLS_INCOMING_TYPE);
- CharSequence description = mHelper.getCallDescription(details);
- assertTrue(description.toString()
- .contains(this.mResources.getString(R.string.description_num_calls, 2)));
- }
-
- /**
- * Test getCallDescription method used to get the accessibility description for calls.
- * Test that the "Video call." message is present if the call had video capability.
- */
- public void testGetCallDescription_Video() {
- PhoneCallDetails details = getPhoneCallDetailsWithTypes(
- AppCompatConstants.CALLS_INCOMING_TYPE, AppCompatConstants.CALLS_INCOMING_TYPE);
- details.features = Calls.FEATURES_VIDEO;
-
- CharSequence description = mHelper.getCallDescription(details);
- final boolean isVideoEnabled = CallUtil.isVideoEnabled(getContext());
- assertTrue(description.toString()
- .contains(this.mResources.getString(
- isVideoEnabled
- ? R.string.description_video_call
- : R.string.description_num_calls,
- 2)));
- }
-
- /** Asserts that the primary action view does not have a call intent. */
- private void assertNoCallIntent() {
- Object intentProvider = mViewHolder.primaryActionView.getTag();
- // The intent provider should be null as there is no ability to make a call.
- assertNull(intentProvider);
- }
-
- /** Sets the details of a phone call using the specified phone number. */
- private void setPhoneCallDetailsWithNumber(String number,
- int presentation, String formattedNumber) {
- setPhoneCallDetailsWithNumberTypeAndPostDialDigits(number, "", presentation,
- formattedNumber, Calls.INCOMING_TYPE);
- }
-
- /** Sets the details of a phone call using the specified phone number. */
- private void setPhoneCallDetailsWithNumberAndType(String number,
- int presentation, String formattedNumber, int callType) {
- setPhoneCallDetailsWithNumberTypeAndPostDialDigits(number, "", presentation,
- formattedNumber, callType);
- }
-
- /** Sets the details of a phone call using the specified phone number and post-dial digits. */
- private void setPhoneCallDetailsWithNumberTypeAndPostDialDigits(String number,
- String postDialDigits, int presentation, String formattedNumber, int callType) {
- PhoneCallDetails details = getPhoneCallDetails(
- number, postDialDigits, presentation, formattedNumber);
- details.callTypes = new int[] {callType};
- mHelper.setPhoneCallDetails(mViewHolder, details);
- }
-
- private PhoneCallDetails getPhoneCallDetails(
- String number, String postDialDigits, int presentation, String formattedNumber) {
- PhoneCallDetails details = new PhoneCallDetails(
- mContext,
- number,
- presentation,
- formattedNumber,
- postDialDigits,
- false /* isVoicemail */);
- setDefaultDetails(details);
- return details;
- }
-
- /** Returns the details of a phone call using the specified call type. */
- private PhoneCallDetails getPhoneCallDetailsWithTypes(int... types) {
- PhoneCallDetails details = new PhoneCallDetails(
- mContext,
- TEST_NUMBER,
- Calls.PRESENTATION_ALLOWED,
- TEST_FORMATTED_NUMBER,
- "",
- false /* isVoicemail */);
- setDefaultDetails(details);
- details.callTypes = types;
- return details;
- }
-
- private void setDefaultDetails(PhoneCallDetails details) {
- details.callTypes = new int[] {Calls.INCOMING_TYPE};
- details.countryIso = TEST_COUNTRY_ISO;
- details.date = TEST_DATE;
- details.duration = TEST_DURATION;
- details.geocode = TEST_GEOCODE;
- }
-}
diff --git a/tests/src/com/android/dialer/calllog/CallLogNotificationsHelperTest.java b/tests/src/com/android/dialer/calllog/CallLogNotificationsHelperTest.java
deleted file mode 100644
index b5950d8b8..000000000
--- a/tests/src/com/android/dialer/calllog/CallLogNotificationsHelperTest.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.provider.CallLog;
-import android.test.AndroidTestCase;
-
-import com.android.dialer.R;
-
-/**
- * Unit tests for {@link CallLogNotificationsHelper}.
- */
-public class CallLogNotificationsHelperTest extends AndroidTestCase {
- private static final String TEST_COUNTRY_ISO = "US";
- private static final String TEST_VALID_NUMBER = "14125555555";
- private static final String TEST_INVALID_NUMBER = "asdna128937123";
- private static final String TEST_FORMATTED_NUMBER = "1 412-555-5555";
- private static final String TEST_E164_NUMBER = "+14125555555";
-
- private final ContactInfoHelper mContactInfoHelper = mock(ContactInfoHelper.class);
-
- private CallLogNotificationsHelper mCallLogNotificationsHelper;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mCallLogNotificationsHelper = new CallLogNotificationsHelper(getContext(),
- null, null, mContactInfoHelper, TEST_COUNTRY_ISO);
- }
-
- public void testGetContactInfo_ValidNumberValidPresentationValidIso() {
- ContactInfo contactInfo = getContactInfo(
- TEST_VALID_NUMBER, CallLog.Calls.PRESENTATION_UNKNOWN, TEST_COUNTRY_ISO);
- assertEquals(TEST_VALID_NUMBER, contactInfo.number);
- assertEquals(mContext.getResources().getString(R.string.unknown), contactInfo.name);
- assertEquals(TEST_E164_NUMBER, contactInfo.normalizedNumber);
- }
-
- public void testGetContactInfo_ValidNumberInvalidPresentationValidIso() {
- ContactInfo contactInfo = getContactInfo(TEST_VALID_NUMBER, -1, TEST_COUNTRY_ISO);
- assertEquals(TEST_VALID_NUMBER, contactInfo.number);
- assertEquals(TEST_FORMATTED_NUMBER, contactInfo.name);
- assertEquals(TEST_E164_NUMBER, contactInfo.normalizedNumber);
- }
-
- public void testGetContactInfo_ValidNumberValidPresentationNullIso() {
- ContactInfo contactInfo = getContactInfo(
- TEST_VALID_NUMBER, CallLog.Calls.PRESENTATION_UNKNOWN, null);
- assertEquals(TEST_VALID_NUMBER, contactInfo.number);
- assertEquals(mContext.getResources().getString(R.string.unknown), contactInfo.name);
- assertEquals(TEST_E164_NUMBER, contactInfo.normalizedNumber);
- }
-
- public void testGetContactInfo_ValidNumberInvalidPresentationNullIso() {
- ContactInfo contactInfo = getContactInfo(
- TEST_VALID_NUMBER, -1, null);
- assertEquals(TEST_VALID_NUMBER, contactInfo.number);
- assertEquals(TEST_FORMATTED_NUMBER, contactInfo.name);
- assertEquals(TEST_E164_NUMBER, contactInfo.normalizedNumber);
- }
-
- public void testGetContactInfo_NullNumberValidPresentationValidIso() {
- ContactInfo contactInfo = getContactInfo(
- null, CallLog.Calls.PRESENTATION_UNKNOWN, TEST_COUNTRY_ISO);
- assertEquals("", contactInfo.number);
- assertEquals(mContext.getResources().getString(R.string.unknown), contactInfo.name);
- assertNull(contactInfo.normalizedNumber);
- }
-
- public void testGetContactInfo_NullNumberInvalidPresentationValidIso() {
- ContactInfo contactInfo = getContactInfo(null, -1, TEST_COUNTRY_ISO);
- assertEquals("", contactInfo.number);
- assertEquals(mContext.getResources().getString(R.string.unknown), contactInfo.name);
- assertNull(contactInfo.normalizedNumber);
- }
-
- public void testGetContactInfo_NullNumberValidPresentationNullIso() {
- ContactInfo contactInfo = getContactInfo(null, CallLog.Calls.PRESENTATION_RESTRICTED, null);
- assertEquals("", contactInfo.number);
- assertEquals(mContext.getResources().getString(R.string.private_num), contactInfo.name);
- assertNull(contactInfo.normalizedNumber);
- }
-
- public void testGetContactInfo_NullNumberInValidPresentationNullIso() {
- ContactInfo contactInfo = getContactInfo(null, -1, null);
- assertEquals("", contactInfo.number);
- assertEquals(mContext.getResources().getString(R.string.unknown), contactInfo.name);
- assertNull(contactInfo.normalizedNumber);
- }
-
- public void testGetContactInfo_InvalidNumberInValidPresentationNullIso() {
- ContactInfo contactInfo = getContactInfo(TEST_INVALID_NUMBER, -1, null);
- assertEquals(TEST_INVALID_NUMBER, contactInfo.name);
- assertEquals(TEST_INVALID_NUMBER, contactInfo.formattedNumber);
- assertEquals(null, contactInfo.normalizedNumber);
- }
-
- public void testGetContactInfo_NonNullCachedLookup() {
- when(mContactInfoHelper.lookupNumber(anyString(), anyString())).thenReturn(null);
- ContactInfo contactInfo = getContactInfo(TEST_VALID_NUMBER, -1, TEST_COUNTRY_ISO);
- assertEquals(TEST_VALID_NUMBER, contactInfo.number);
- assertEquals(TEST_FORMATTED_NUMBER, contactInfo.formattedNumber);
- }
-
- public void testGetContactInfo_NullCachedLookup() {
- ContactInfo cachedContactInfo = new ContactInfo();
- cachedContactInfo.number = TEST_VALID_NUMBER;
- cachedContactInfo.formattedNumber = TEST_FORMATTED_NUMBER;
- when(mContactInfoHelper.lookupNumber(anyString(), anyString()))
- .thenReturn(cachedContactInfo);
- ContactInfo contactInfo = getContactInfo(TEST_VALID_NUMBER, -1, TEST_COUNTRY_ISO);
- assertEquals(TEST_VALID_NUMBER, contactInfo.number);
- assertEquals(TEST_FORMATTED_NUMBER, contactInfo.name);
- }
-
- private ContactInfo getContactInfo(String number, int presentation, String countryIso) {
- return mCallLogNotificationsHelper.getContactInfo(number, presentation, countryIso);
- }
-}
diff --git a/tests/src/com/android/dialer/calllog/CallLogQueryTestUtils.java b/tests/src/com/android/dialer/calllog/CallLogQueryTestUtils.java
deleted file mode 100644
index c2cfedbac..000000000
--- a/tests/src/com/android/dialer/calllog/CallLogQueryTestUtils.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import static junit.framework.Assert.assertEquals;
-
-import android.provider.CallLog.Calls;
-
-import com.android.contacts.common.compat.CompatUtils;
-
-/**
- * Helper class to create test values for {@link CallLogQuery}.
- */
-public class CallLogQueryTestUtils {
- public static Object[] createTestValues() {
- Object[] values;
- if (CompatUtils.isNCompatible()) {
- values = new Object[]{
- 0L, "", 0L, 0L, Calls.INCOMING_TYPE, "", "", "", null, 0, null, null, null,
- null, 0L, null, 0, Calls.PRESENTATION_ALLOWED, null, null, 0, null, null,
- null, "", ""
- };
- } else {
- values = new Object[]{
- 0L, "", 0L, 0L, Calls.INCOMING_TYPE, "", "", "", null, 0, null, null, null,
- null, 0L, null, 0, Calls.PRESENTATION_ALLOWED, null, null, 0, null, null, null
- };
- }
- assertEquals(CallLogQuery._PROJECTION.length, values.length);
- return values;
- }
-}
diff --git a/tests/src/com/android/dialer/calllog/ContactInfoHelperTest.java b/tests/src/com/android/dialer/calllog/ContactInfoHelperTest.java
deleted file mode 100644
index df385f425..000000000
--- a/tests/src/com/android/dialer/calllog/ContactInfoHelperTest.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.PhoneLookup;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.test.mocks.ContactsMockContext;
-import com.android.contacts.common.test.mocks.MockContentProvider.Query;
-
-import junit.framework.Assert;
-
-@MediumTest
-public class ContactInfoHelperTest extends AndroidTestCase {
-
- private static final String TEST_COUNTRY_ISO = "US";
- private static final String TEST_DISPLAY_NAME = "Display Name";
- private static final String TEST_DISPLAY_NAME_ALTERNATIVE = "Name, Display";
- private static final String[] TEST_DISPLAY_NAME_ALTERNATIVE_ROW = new String[]{
- TEST_DISPLAY_NAME_ALTERNATIVE};
- private static final String TEST_LOOKUP_KEY = "lookupKey";
- private static final String[] TEST_LOOKUP_ROW = new String[]{null, TEST_DISPLAY_NAME,
- null, null, null, null, null, TEST_LOOKUP_KEY, null};
-
- private Uri displayNameAlternativeUri;
- private ContactsMockContext mContext;
- private ContactInfo mContactInfo;
- private ContactInfoHelper mContactInfoHelper;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
-
- displayNameAlternativeUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI,
- TEST_LOOKUP_KEY);
- mContext = new ContactsMockContext(getContext());
- mContactInfo = new ContactInfo();
- mContactInfo.name = TEST_DISPLAY_NAME;
- mContactInfo.nameAlternative = TEST_DISPLAY_NAME_ALTERNATIVE;
- mContactInfoHelper = new ContactInfoHelper(mContext, TEST_COUNTRY_ISO);
- }
-
- public void testLookupContactFromUri_NullUri() {
- Assert.assertNull(mContactInfoHelper.lookupContactFromUri(null, false));
- }
-
- public void testLookupContactFromUri_NoResults() {
- setUpQueryExpectations(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI,
- PhoneQuery.getPhoneLookupProjection(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI));
-
- Assert.assertEquals(ContactInfo.EMPTY, mContactInfoHelper.lookupContactFromUri(
- PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, false));
- mContext.verify();
- }
-
- public void testLookupContactFromUri_NoDisplayNameAlternative() {
- setUpQueryExpectations(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI,
- PhoneQuery.getPhoneLookupProjection(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI),
- TEST_LOOKUP_ROW);
- setUpQueryExpectations(displayNameAlternativeUri,
- PhoneQuery.DISPLAY_NAME_ALTERNATIVE_PROJECTION);
-
- ContactInfo contactInfo = mContactInfoHelper.lookupContactFromUri(
- PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, false);
- Assert.assertEquals(TEST_DISPLAY_NAME, contactInfo.name);
- Assert.assertNull(contactInfo.nameAlternative);
- mContext.verify();
- }
-
- public void testLookupContactFromUri_HasDisplayNameAlternative() {
- setUpQueryExpectations(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI,
- PhoneQuery.getPhoneLookupProjection(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI),
- TEST_LOOKUP_ROW);
- setUpQueryExpectations(displayNameAlternativeUri,
- PhoneQuery.DISPLAY_NAME_ALTERNATIVE_PROJECTION, TEST_DISPLAY_NAME_ALTERNATIVE_ROW);
-
- ContactInfo contactInfo = mContactInfoHelper.lookupContactFromUri(
- PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, false);
- Assert.assertEquals(TEST_DISPLAY_NAME, contactInfo.name);
- Assert.assertEquals(TEST_DISPLAY_NAME_ALTERNATIVE, contactInfo.nameAlternative);
- mContext.verify();
- }
-
- public void testLookupDisplayNameAlternative_NullLookup() {
- Assert.assertNull(mContactInfoHelper.lookUpDisplayNameAlternative(mContext, null,
- ContactsUtils.USER_TYPE_CURRENT, null));
- }
-
- public void testLookupDisplayNameAlternative_NoResults() {
- setUpQueryExpectations(displayNameAlternativeUri,
- PhoneQuery.DISPLAY_NAME_ALTERNATIVE_PROJECTION);
- Assert.assertNull(mContactInfoHelper.lookUpDisplayNameAlternative(mContext,
- TEST_LOOKUP_KEY, ContactsUtils.USER_TYPE_CURRENT, null));
- mContext.verify();
- }
-
- public void testLookupDisplayNameAlternative_HasDisplayNameAlternative() {
- setUpQueryExpectations(displayNameAlternativeUri,
- PhoneQuery.DISPLAY_NAME_ALTERNATIVE_PROJECTION, TEST_DISPLAY_NAME_ALTERNATIVE_ROW);
- Assert.assertEquals(TEST_DISPLAY_NAME_ALTERNATIVE,
- mContactInfoHelper.lookUpDisplayNameAlternative(mContext, TEST_LOOKUP_KEY
- , ContactsUtils.USER_TYPE_CURRENT, null));
- mContext.verify();
- }
-
- public void testLookupDisplayNameAlternative_EnterpriseLocalDirectory() {
- Assert.assertNull(mContactInfoHelper.lookUpDisplayNameAlternative(mContext, TEST_LOOKUP_KEY,
- ContactsUtils.USER_TYPE_WORK, ContactsContract.Directory.ENTERPRISE_DEFAULT));
- Assert.assertNull(mContactInfoHelper.lookUpDisplayNameAlternative(mContext, TEST_LOOKUP_KEY,
- ContactsUtils.USER_TYPE_CURRENT, ContactsContract.Directory.ENTERPRISE_DEFAULT));
- }
-
- public void testLookupDisplayNameAlternative_EnterpriseRemoteDirectory() {
- Assert.assertNull(mContactInfoHelper.lookUpDisplayNameAlternative(mContext, TEST_LOOKUP_KEY,
- ContactsUtils.USER_TYPE_WORK,
- ContactsContract.Directory.ENTERPRISE_DEFAULT + 10));
- Assert.assertNull(mContactInfoHelper.lookUpDisplayNameAlternative(mContext, TEST_LOOKUP_KEY,
- ContactsUtils.USER_TYPE_CURRENT,
- ContactsContract.Directory.ENTERPRISE_DEFAULT + 10));
- }
-
- public void testLookupDisplayNameAlternative_PersonalRemoteDirectory() {
- Assert.assertNull(mContactInfoHelper.lookUpDisplayNameAlternative(mContext, null,
- ContactsUtils.USER_TYPE_CURRENT,
- ContactsContract.Directory.DEFAULT + 10));
- }
-
- /*
- * Sets up query expectations to return the given row for all queries for the given
- * uri and projection. If row is null, an empty cursor is returned for query calls
- */
- private void setUpQueryExpectations(Uri uri, String[] projection, String...row) {
- Query query = mContext.getContactsProvider().expectQuery(uri)
- .withProjection(projection).withAnySelection().withAnySortOrder();
- if (row == null || row.length == 0) {
- query.returnEmptyCursor();
- return;
- }
- query.returnRow(row);
- }
-}
diff --git a/tests/src/com/android/dialer/calllog/GroupingListAdapterTests.java b/tests/src/com/android/dialer/calllog/GroupingListAdapterTests.java
deleted file mode 100644
index 4d8cb9cc0..000000000
--- a/tests/src/com/android/dialer/calllog/GroupingListAdapterTests.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.support.v7.widget.RecyclerView;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.text.TextUtils;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Tests for {@link GroupingListAdapter}.
- *
- * Running all tests:
- *
- * adb shell am instrument -e class com.android.dialer.calllog.GroupingListAdapterTests \
- * -w com.android.dialer.tests/android.test.InstrumentationTestRunner
- */
-@MediumTest
-public class GroupingListAdapterTests extends AndroidTestCase {
-
- static private final String[] PROJECTION = new String[] {
- "_id",
- "group",
- };
-
- private static final int GROUPING_COLUMN_INDEX = 1;
-
- private MatrixCursor mCursor;
- private long mNextId;
-
- private GroupingListAdapter mAdapter = new GroupingListAdapter(null) {
-
- @Override
- protected void addGroups(Cursor cursor) {
- int count = cursor.getCount();
- int groupItemCount = 1;
- cursor.moveToFirst();
- String currentValue = cursor.getString(GROUPING_COLUMN_INDEX);
- for (int i = 1; i < count; i++) {
- cursor.moveToNext();
- String value = cursor.getString(GROUPING_COLUMN_INDEX);
- if (TextUtils.equals(value, currentValue)) {
- groupItemCount++;
- } else {
- addGroup(i - groupItemCount, groupItemCount);
- groupItemCount = 1;
- currentValue = value;
- }
- }
- addGroup(count - groupItemCount, groupItemCount);
- }
-
- @Override
- protected void addVoicemailGroups(Cursor c) {
- // Do nothing.
- }
-
- @Override
- public void onContentChanged() {
- // Do nothing.
- }
-
- @Override
- public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
- return null;
- }
-
- @Override
- public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
- // Do nothing.
- }
- };
-
- private void buildCursor(String... numbers) {
- mCursor = new MatrixCursor(PROJECTION);
- mNextId = 1;
- for (String number : numbers) {
- mCursor.addRow(new Object[]{mNextId, number});
- mNextId++;
- }
- }
-
- public void testGroupingWithoutGroups() {
- buildCursor("1", "2", "3");
- mAdapter.changeCursor(mCursor);
-
- assertEquals(3, mAdapter.getItemCount());
- assertMetadata(0, 1, "1");
- assertMetadata(1, 1, "2");
- assertMetadata(2, 1, "3");
- }
-
- public void testGroupingWithGroupAtTheBeginning() {
- buildCursor("1", "1", "2");
- mAdapter.changeCursor(mCursor);
-
- assertEquals(2, mAdapter.getItemCount());
- assertMetadata(0, 2, "1");
- assertMetadata(1, 1, "2");
- }
-
- public void testGroupingWithGroupInTheMiddle() {
- buildCursor("1", "2", "2", "2", "3");
- mAdapter.changeCursor(mCursor);
-
- assertEquals(3, mAdapter.getItemCount());
- assertMetadata(0, 1, "1");
- assertMetadata(1, 3, "2");
- assertMetadata(2, 1, "3");
- }
-
- public void testGroupingWithGroupAtTheEnd() {
- buildCursor("1", "2", "3", "3", "3");
- mAdapter.changeCursor(mCursor);
-
- assertEquals(3, mAdapter.getItemCount());
- assertMetadata(0, 1, "1");
- assertMetadata(1, 1, "2");
- assertMetadata(2, 3, "3");
- }
-
- public void testGroupingWithMultipleGroups() {
- buildCursor("1", "2", "2", "3", "4", "4", "5", "5", "6");
- mAdapter.changeCursor(mCursor);
-
- assertEquals(6, mAdapter.getItemCount());
- assertMetadata(0, 1, "1");
- assertMetadata(1, 2, "2");
- assertMetadata(2, 1, "3");
- assertMetadata(3, 2, "4");
- assertMetadata(4, 2, "5");
- assertMetadata(5, 1, "6");
- }
-
- public void testGroupDescriptorArrayGrowth() {
- String[] numbers = new String[500];
- for (int i = 0; i < numbers.length; i++) {
-
- // Make groups of 2
- numbers[i] = String.valueOf((i / 2) * 2);
- }
-
- buildCursor(numbers);
- mAdapter.changeCursor(mCursor);
-
- assertEquals(250, mAdapter.getItemCount());
- }
-
- private void assertMetadata(int listPosition, int groupSize, String objectValue) {
- assertEquals(groupSize, mAdapter.getGroupSize(listPosition));
- MatrixCursor cursor = (MatrixCursor) mAdapter.getItem(listPosition);
- assertEquals(objectValue, cursor.getString(GROUPING_COLUMN_INDEX));
- }
-}
diff --git a/tests/src/com/android/dialer/calllog/PhoneAccountUtilsTest.java b/tests/src/com/android/dialer/calllog/PhoneAccountUtilsTest.java
deleted file mode 100644
index f2d0856bd..000000000
--- a/tests/src/com/android/dialer/calllog/PhoneAccountUtilsTest.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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.dialer.calllog;
-
-import android.content.ComponentName;
-import android.telecom.PhoneAccountHandle;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-public class PhoneAccountUtilsTest extends AndroidTestCase {
-
- private static final String VALID_COMPONENT_NAME =
- "com.android.dialer.calllog/com.android.dialer.calllog.PhoneAccountUtilsTest";
- private static final String VALID_ACCOUNT_ID = "Account1";
-
- @SmallTest
- public void testGetAccount_CorrectParams() {
- ComponentName correctComponentName =
- ComponentName.unflattenFromString(VALID_COMPONENT_NAME);
- PhoneAccountHandle correctPhoneAccountHandle = new PhoneAccountHandle(correctComponentName,
- VALID_ACCOUNT_ID);
-
- PhoneAccountHandle testPhoneAccountHandle =
- PhoneAccountUtils.getAccount(VALID_COMPONENT_NAME, VALID_ACCOUNT_ID);
-
- assertTrue(correctPhoneAccountHandle.equals(testPhoneAccountHandle));
- }
-
- @SmallTest
- public void testGetAccount_ComponentStringNoClassName() {
- final String malformedComponentName = "com.android.dialer.calllog/";
-
- PhoneAccountHandle testPhoneAccountHandle =
- PhoneAccountUtils.getAccount(malformedComponentName, VALID_ACCOUNT_ID);
-
- assertNull(testPhoneAccountHandle);
- }
-
- @SmallTest
- public void testGetAccount_ComponentStringInvalid() {
- final String malformedComponentName = "com.android.dialer.calllog";
-
- PhoneAccountHandle testPhoneAccountHandle =
- PhoneAccountUtils.getAccount(malformedComponentName, VALID_ACCOUNT_ID);
-
- assertNull(testPhoneAccountHandle);
- }
-
- @SmallTest
- public void testGetAccount_NoComponentName() {
- final String blankComponentName = "";
-
- PhoneAccountHandle testPhoneAccountHandle =
- PhoneAccountUtils.getAccount(blankComponentName, VALID_ACCOUNT_ID);
-
- assertNull(testPhoneAccountHandle);
- }
-
- @SmallTest
- public void testGetAccount_NoAccountId() {
- final String blankAccountId = "";
-
- PhoneAccountHandle testPhoneAccountHandle =
- PhoneAccountUtils.getAccount(VALID_COMPONENT_NAME, blankAccountId);
-
- assertNull(testPhoneAccountHandle);
- }
-
- @SmallTest
- public void testGetAccount_NoAccountIdOrComponentName() {
- final String blankComponentName = "";
- final String blankAccountId = "";
-
- PhoneAccountHandle testPhoneAccountHandle =
- PhoneAccountUtils.getAccount(VALID_COMPONENT_NAME, blankAccountId);
-
- assertNull(testPhoneAccountHandle);
- }
-
- @SmallTest
- public void testGetAccount_NullAccountIdAndComponentName() {
- final String blankComponentName = null;
- final String blankAccountId = null;
-
- PhoneAccountHandle testPhoneAccountHandle =
- PhoneAccountUtils.getAccount(VALID_COMPONENT_NAME, blankAccountId);
-
- assertNull(testPhoneAccountHandle);
- }
-}
diff --git a/tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java b/tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java
deleted file mode 100644
index 0c57fde3c..000000000
--- a/tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java
+++ /dev/null
@@ -1,581 +0,0 @@
-/*
- * Copyright (C) 2010 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.dialer.calllog;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Resources;
-import android.provider.CallLog.Calls;
-import android.telecom.PhoneAccountHandle;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.text.Html;
-import android.text.Spanned;
-import android.view.View;
-import android.widget.TextView;
-
-import com.android.dialer.PhoneCallDetails;
-import com.android.dialer.R;
-import com.android.dialer.calllog.calllogcache.TestTelecomCallLogCache;
-import com.android.dialer.util.AppCompatConstants;
-import com.android.dialer.util.LocaleTestUtils;
-
-import java.util.GregorianCalendar;
-import java.util.Locale;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Unit tests for {@link PhoneCallDetailsHelper}.
- */
-@MediumTest
-public class PhoneCallDetailsHelperTest extends AndroidTestCase {
- /** The number to be used to access the voicemail. */
- private static final String TEST_VOICEMAIL_NUMBER = "125";
- /** The date of the call log entry. */
- private static final long TEST_DATE =
- new GregorianCalendar(2011, 5, 3, 13, 0, 0).getTimeInMillis();
- private static final long INJECTED_CURRENT_DATE =
- new GregorianCalendar(2011, 5, 4, 13, 0, 0).getTimeInMillis();
- /** A test duration value for phone calls. */
- private static final long TEST_DURATION = 62300;
- /** The number of the caller/callee in the log entry. */
- private static final String TEST_NUMBER = "14125555555";
- /** The formatted version of {@link #TEST_NUMBER}. */
- private static final String TEST_FORMATTED_NUMBER = "1-412-255-5555";
- /** The country ISO name used in the tests. */
- private static final String TEST_COUNTRY_ISO = "US";
- /** The geocoded location used in the tests. */
- private static final String TEST_GEOCODE = "United States";
- /** Empty geocode label */
- private static final String EMPTY_GEOCODE = "";
- /** Empty post-dial digits label */
- private static final String EMPTY_POSTDIAL = "";
- /** The number that the call was received via */
- private static final String TEST_VIA_NUMBER = "+16505551234";
- /** The Phone Account name that the Call was received on */
- private static final String TEST_ACCOUNT_LABEL = "T-Stationary";
-
- /** The object under test. */
- private PhoneCallDetailsHelper mHelper;
- /** The views to fill. */
- private PhoneCallDetailsViews mViews;
- private TextView mNameView;
- private LocaleTestUtils mLocaleTestUtils;
- private TestTelecomCallLogCache mPhoneUtils;
-
- private Context mContext;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getContext();
- Resources resources = mContext.getResources();
- mPhoneUtils = new TestTelecomCallLogCache(mContext, TEST_VOICEMAIL_NUMBER,
- TEST_ACCOUNT_LABEL);
- mHelper = new PhoneCallDetailsHelper(mContext, resources, mPhoneUtils);
- mHelper.setCurrentTimeForTest(INJECTED_CURRENT_DATE);
- mViews = PhoneCallDetailsViews.createForTest(mContext);
- mNameView = new TextView(mContext);
- mLocaleTestUtils = new LocaleTestUtils(mContext);
- mLocaleTestUtils.setLocale(Locale.US);
- }
-
- @Override
- protected void tearDown() throws Exception {
- mLocaleTestUtils.restoreLocale();
- mNameView = null;
- mViews = null;
- mHelper = null;
- super.tearDown();
- }
-
- public void testSetPhoneCallDetails_Unknown() {
- setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_UNKNOWN, "");
- assertNameEqualsResource(R.string.unknown);
- }
-
- public void testSetPhoneCallDetails_Private() {
- setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_RESTRICTED, "");
- assertNameEqualsResource(R.string.private_num);
- }
-
- public void testSetPhoneCallDetails_Payphone() {
- setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_PAYPHONE, "");
- assertNameEqualsResource(R.string.payphone);
- }
-
- public void testSetPhoneCallDetails_Voicemail() {
- setPhoneCallDetailsWithNumber(TEST_VOICEMAIL_NUMBER,
- Calls.PRESENTATION_ALLOWED, TEST_VOICEMAIL_NUMBER);
- assertNameEqualsResource(R.string.voicemail);
- }
-
- public void testSetPhoneCallDetails_ViaNumber() {
- setPhoneCallDetailsWithViaNumber(TEST_VIA_NUMBER);
- assertViaNumberEquals(TEST_VIA_NUMBER);
- }
-
- public void testSetPhoneCallDetails_NoViaNumber() {
- setDefaultPhoneCallDetailsNoViaNumber();
- assertCallAccountInvisible();
- }
-
- public void testSetPhoneCallDetails_AccountLabel() {
- setPhoneCallDetailsWithAccountHandle();
- assertAccountLabelEquals(TEST_ACCOUNT_LABEL);
- }
-
- public void testSetPhoneCallDetails_AccountHandleViaNumber() {
- setPhoneCallDetailsWithAccountLabelViaNumber(TEST_VIA_NUMBER);
- assertAccountLabelEquals(TEST_VIA_NUMBER, TEST_ACCOUNT_LABEL);
- }
-
- // Voicemail date string has 3 different formats depending on how long ago the call was placed
- public void testSetVoicemailPhoneCallDetails_Today() {
- setVoicemailPhoneCallDetailsWithDate(System.currentTimeMillis());
- assertLocationAndDateContains("Today at");
- }
-
- public void testSetVoicemailPhoneCallDetails_WithinCurrentYear() {
- mHelper.setCurrentTimeForTest(INJECTED_CURRENT_DATE);
- String formattedTestDate = "Jun 3 at 1:00 PM";
- setVoicemailPhoneCallDetailsWithDate(TEST_DATE);
- assertLocationAndDateContains(formattedTestDate);
- }
-
- public void testSetVoicemailPhoneCallDetails_OutsideCurrentYear() {
- mHelper.setCurrentTimeForTest(INJECTED_CURRENT_DATE);
- long testDate = new GregorianCalendar(2009, 5, 3, 13, 0, 0).getTimeInMillis();
- String formattedTestDate = "Jun 3, 2009 at 1:00 PM";
- setVoicemailPhoneCallDetailsWithDate(testDate);
- assertLocationAndDateContains(formattedTestDate);
- }
-
- public void testVoicemailLocationNotShownWithDate() {
- setVoicemailPhoneCallDetailsWithDate(TEST_DATE);
- assertLocationAndDateExactEquals("Jun 3 at 1:00 PM • 99:20");
- }
-
- public void testVoicemailDuration() {
- setVoicemailPhoneCallDetailsWithDuration(100);
- assertDurationExactEquals("01:40");
- }
-
- public void testVoicemailDuration_Capped() {
- setVoicemailPhoneCallDetailsWithDuration(TEST_DURATION);
- assertDurationExactEquals("99:20");
- }
-
- public void testVoicemailDuration_Zero() {
- setVoicemailPhoneCallDetailsWithDuration(0);
- assertLocationAndDateExactEquals("Jun 3 at 1:00 PM");
- }
-
- public void testVoicemailDuration_EvenMinute() {
- setVoicemailPhoneCallDetailsWithDuration(60);
- assertDurationExactEquals("01:00");
- }
-
- /** Asserts that a char sequence is actually a Spanned corresponding to the expected HTML. */
- private void assertEqualsHtml(String expectedHtml, CharSequence actualText) {
- // In order to contain HTML, the text should actually be a Spanned.
- assertTrue(actualText instanceof Spanned);
- Spanned actualSpanned = (Spanned) actualText;
- // Convert from and to HTML to take care of alternative formatting of HTML.
- assertEquals(Html.toHtml(Html.fromHtml(expectedHtml)), Html.toHtml(actualSpanned));
-
- }
-
- public void testSetPhoneCallDetails_Date() {
- mHelper.setCurrentTimeForTest(
- new GregorianCalendar(2011, 5, 3, 13, 0, 0).getTimeInMillis());
-
- setPhoneCallDetailsWithDate(
- new GregorianCalendar(2011, 5, 3, 13, 0, 0).getTimeInMillis());
- assertLocationAndDateContains("0 min. ago");
-
- setPhoneCallDetailsWithDate(
- new GregorianCalendar(2011, 5, 3, 12, 0, 0).getTimeInMillis());
- assertLocationAndDateContains("1 hr. ago");
-
- setPhoneCallDetailsWithDate(
- new GregorianCalendar(2011, 5, 2, 13, 0, 0).getTimeInMillis());
- assertLocationAndDateContains("Yesterday");
-
- setPhoneCallDetailsWithDate(
- new GregorianCalendar(2011, 5, 1, 13, 0, 0).getTimeInMillis());
- assertLocationAndDateContains("2 days ago");
- }
-
- public void testSetPhoneCallDetails_CallTypeIcons() {
- setPhoneCallDetailsWithCallTypeIcons(AppCompatConstants.CALLS_INCOMING_TYPE);
- assertCallTypeIconsEquals(AppCompatConstants.CALLS_INCOMING_TYPE);
-
- setPhoneCallDetailsWithCallTypeIcons(AppCompatConstants.CALLS_OUTGOING_TYPE);
- assertCallTypeIconsEquals(AppCompatConstants.CALLS_OUTGOING_TYPE);
-
- setPhoneCallDetailsWithCallTypeIcons(AppCompatConstants.CALLS_MISSED_TYPE);
- assertCallTypeIconsEquals(AppCompatConstants.CALLS_MISSED_TYPE);
-
- setPhoneCallDetailsWithCallTypeIcons(AppCompatConstants.CALLS_VOICEMAIL_TYPE);
- assertCallTypeIconsEquals(AppCompatConstants.CALLS_VOICEMAIL_TYPE);
- }
-
- /**
- * Tests a case where the video call feature is present.
- */
- public void testSetPhoneCallDetails_Video() {
- PhoneCallDetails details = getPhoneCallDetails();
- details.features = Calls.FEATURES_VIDEO;
- mHelper.setPhoneCallDetails(mViews, details);
-
- assertIsVideoCall(true);
- }
-
- /**
- * Tests a case where the video call feature is not present.
- */
- public void testSetPhoneCallDetails_NoVideo() {
- PhoneCallDetails details = getPhoneCallDetails();
- details.features = 0;
- mHelper.setPhoneCallDetails(mViews, details);
-
- assertIsVideoCall(false);
- }
-
- public void testSetPhoneCallDetails_MultipleCallTypeIcons() {
- setPhoneCallDetailsWithCallTypeIcons(
- AppCompatConstants.CALLS_INCOMING_TYPE,
- AppCompatConstants.CALLS_OUTGOING_TYPE);
- assertCallTypeIconsEquals(
- AppCompatConstants.CALLS_INCOMING_TYPE,
- AppCompatConstants.CALLS_OUTGOING_TYPE);
-
- setPhoneCallDetailsWithCallTypeIcons(
- AppCompatConstants.CALLS_MISSED_TYPE,
- AppCompatConstants.CALLS_MISSED_TYPE);
- assertCallTypeIconsEquals(
- AppCompatConstants.CALLS_MISSED_TYPE,
- AppCompatConstants.CALLS_MISSED_TYPE);
- }
-
- public void testSetPhoneCallDetails_MultipleCallTypeIconsLastOneDropped() {
- setPhoneCallDetailsWithCallTypeIcons(
- AppCompatConstants.CALLS_MISSED_TYPE,
- AppCompatConstants.CALLS_MISSED_TYPE,
- AppCompatConstants.CALLS_INCOMING_TYPE,
- AppCompatConstants.CALLS_OUTGOING_TYPE);
- assertCallTypeIconsEqualsPlusOverflow("(4)",
- AppCompatConstants.CALLS_MISSED_TYPE,
- AppCompatConstants.CALLS_MISSED_TYPE,
- AppCompatConstants.CALLS_INCOMING_TYPE);
- }
-
- public void testSetPhoneCallDetails_Geocode() {
- setPhoneCallDetailsWithNumberAndGeocode("+14125555555", "1-412-555-5555", "Pennsylvania");
- assertNameEquals("1-412-555-5555"); // The phone number is shown as the name.
- assertLocationAndDateContains("Pennsylvania"); // The geocode is shown as the label.
- }
-
- public void testSetPhoneCallDetails_NoGeocode() {
- setPhoneCallDetailsWithNumberAndGeocode("+14125555555", "1-412-555-5555", null);
- assertNameEquals("1-412-555-5555"); // The phone number is shown as the name.
- assertLocationAndDateContains(EMPTY_GEOCODE); // The empty geocode is shown as the label.
- }
-
- public void testSetPhoneCallDetails_EmptyGeocode() {
- setPhoneCallDetailsWithNumberAndGeocode("+14125555555", "1-412-555-5555", "");
- assertNameEquals("1-412-555-5555"); // The phone number is shown as the name.
- assertLocationAndDateContains(EMPTY_GEOCODE); // The empty geocode is shown as the label.
- }
-
- public void testSetPhoneCallDetails_NoGeocodeForVoicemail() {
- setPhoneCallDetailsWithNumberAndGeocode(TEST_VOICEMAIL_NUMBER, "", "United States");
- assertLocationAndDateContains(EMPTY_GEOCODE); // The empty geocode is shown as the label.
- }
-
- public void testSetPhoneCallDetails_Highlighted() {
- setPhoneCallDetailsWithNumber(TEST_VOICEMAIL_NUMBER,
- Calls.PRESENTATION_ALLOWED, "");
- }
-
- public void testSetCallDetailsHeader_NumberOnly() {
- setCallDetailsHeaderWithNumber(TEST_NUMBER, Calls.PRESENTATION_ALLOWED);
- assertEquals(View.VISIBLE, mNameView.getVisibility());
- assertEquals("1-412-255-5555", mNameView.getText().toString());
- }
-
- public void testSetCallDetailsHeader_UnknownNumber() {
- setCallDetailsHeaderWithNumber("", Calls.PRESENTATION_UNKNOWN);
- assertEquals(View.VISIBLE, mNameView.getVisibility());
- assertEquals("Unknown", mNameView.getText().toString());
- }
-
- public void testSetCallDetailsHeader_PrivateNumber() {
- setCallDetailsHeaderWithNumber("", Calls.PRESENTATION_RESTRICTED);
- assertEquals(View.VISIBLE, mNameView.getVisibility());
- assertEquals("Private number", mNameView.getText().toString());
- }
-
- public void testSetCallDetailsHeader_PayphoneNumber() {
- setCallDetailsHeaderWithNumber("", Calls.PRESENTATION_PAYPHONE);
- assertEquals(View.VISIBLE, mNameView.getVisibility());
- assertEquals("Payphone", mNameView.getText().toString());
- }
-
- public void testSetCallDetailsHeader_VoicemailNumber() {
- PhoneCallDetails details = getPhoneCallDetails(
- TEST_VOICEMAIL_NUMBER,
- Calls.PRESENTATION_ALLOWED,
- TEST_FORMATTED_NUMBER);
- mHelper.setCallDetailsHeader(mNameView, details);
- assertEquals(View.VISIBLE, mNameView.getVisibility());
- assertEquals("Voicemail", mNameView.getText().toString());
- }
-
- public void testSetCallDetailsHeader() {
- setCallDetailsHeader("John Doe");
- assertEquals(View.VISIBLE, mNameView.getVisibility());
- assertEquals("John Doe", mNameView.getText().toString());
- }
-
- public void testGetCallTypeOrLocation_Geocode() {
- assertEquals(TEST_GEOCODE, mHelper.getCallTypeOrLocation(getPhoneCallDetails()));
- }
-
- public void testGetCallTypeOrLocation_CallType() {
- PhoneCallDetails details = getPhoneCallDetails();
- details.geocode = null;
- details.numberType = Calls.INCOMING_TYPE;
- mHelper.setPhoneTypeLabelForTest("mobile");
- assertEquals("mobile", mHelper.getCallTypeOrLocation(details));
- }
-
- public void testGetCallTypeOrLocation_DisplayNumber() {
- PhoneCallDetails details = getPhoneCallDetails("", Calls.PRESENTATION_ALLOWED,
- TEST_FORMATTED_NUMBER);
- details.namePrimary = "name";
- assertEquals(TEST_FORMATTED_NUMBER, mHelper.getCallTypeOrLocation(details));
- }
-
- /** Asserts that the name text field contains the value of the given string resource. */
- private void assertNameEqualsResource(int resId) {
- assertNameEquals(getContext().getString(resId));
- }
-
- /** Asserts that the name text field contains the given string value. */
- private void assertNameEquals(String text) {
- assertEquals(text, mViews.nameView.getText().toString());
- }
-
- /** Asserts that the location and date text field contains the given string value. */
- private void assertLocationAndDateContains(String text) {
- assertTrue(mViews.callLocationAndDate.getText().toString().contains(text));
- }
-
- /** Asserts that the location and date text field exactly equals the given string value. */
- private void assertLocationAndDateExactEquals(String text) {
- assertEquals(text, mViews.callLocationAndDate.getText());
- }
-
- /** Asserts that the via number is correct. */
- private void assertViaNumberEquals(String text) {
- final String callAccountText =
- mContext.getResources().getString(R.string.description_via_number, text);
- assertEquals(callAccountText, mViews.callAccountLabel.getText());
- }
-
- /** Asserts that the account label is correct. */
- private void assertAccountLabelEquals(String text) {
- assertEquals(text, mViews.callAccountLabel.getText());
- }
-
- /** Asserts that the account label is correct when also showing the via number. */
- private void assertAccountLabelEquals(String viaNumber, String accountLabel) {
- final String viaNumberText =
- mContext.getResources().getString(R.string.description_via_number, viaNumber);
- assertEquals(accountLabel + " " + viaNumberText, mViews.callAccountLabel.getText());
- }
-
- /** Asserts that the call account label is invisible. */
- private void assertCallAccountInvisible() {
- assertEquals(mViews.callAccountLabel.getVisibility(), View.GONE);
- }
-
- /** Asserts that the duration is exactly as included in the location and date text field. */
- private void assertDurationExactEquals(String text) {
- Matcher matcher = Pattern.compile("(.*) (\\u2022) (\\d{2}:\\d{2})").matcher(
- mViews.callLocationAndDate.getText());
- assertEquals(true, matcher.matches());
- assertEquals(text, matcher.group(3));
- }
-
- /** Asserts that the video icon is shown. */
- private void assertIsVideoCall(boolean isVideoCall) {
- assertEquals(isVideoCall, mViews.callTypeIcons.isVideoShown());
- }
-
- /** Asserts that the call type contains the images with the given drawables. */
- private void assertCallTypeIconsEquals(int... ids) {
- assertEquals(ids.length, mViews.callTypeIcons.getCount());
- for (int index = 0; index < ids.length; ++index) {
- int id = ids[index];
- assertEquals(id, mViews.callTypeIcons.getCallType(index));
- }
- assertEquals(View.VISIBLE, mViews.callTypeIcons.getVisibility());
- }
-
- /**
- * Asserts that the call type contains the images with the given drawables and shows the given
- * text next to the icons.
- */
- private void assertCallTypeIconsEqualsPlusOverflow(String overflowText, int... ids) {
- assertEquals(ids.length, mViews.callTypeIcons.getCount());
- for (int index = 0; index < ids.length; ++index) {
- int id = ids[index];
- assertEquals(id, mViews.callTypeIcons.getCallType(index));
- }
- assertEquals(View.VISIBLE, mViews.callTypeIcons.getVisibility());
- assertTrue(mViews.callLocationAndDate.getText().toString().contains(overflowText));
- assertTrue(mViews.callLocationAndDate.getText().toString().contains("Yesterday"));
- }
-
- /** Sets the phone call details with default values and the given number. */
- private void setPhoneCallDetailsWithNumber(String number, int presentation,
- String formattedNumber) {
- PhoneCallDetails details = getPhoneCallDetails(number, presentation, formattedNumber);
- details.callTypes = new int[]{ AppCompatConstants.CALLS_VOICEMAIL_TYPE };
- mHelper.setPhoneCallDetails(mViews, details);
- }
-
- /** Sets the phone call details with default values and the given via number. */
- private void setPhoneCallDetailsWithViaNumber(String viaNumber) {
- PhoneCallDetails details = getPhoneCallDetails();
- mPhoneUtils.setAccountLabel("");
- details.viaNumber = viaNumber;
- mHelper.setPhoneCallDetails(mViews, details);
- }
-
- /** Sets the phone call details with an account handle. */
- private void setPhoneCallDetailsWithAccountHandle() {
- PhoneCallDetails details = getPhoneCallDetails();
- details.accountHandle = new PhoneAccountHandle(new ComponentName("",""), "");
- mHelper.setPhoneCallDetails(mViews, details);
- }
-
- /** Sets the phone call details with an account handle and via number */
- private void setPhoneCallDetailsWithAccountLabelViaNumber(String viaNumber) {
- PhoneCallDetails details = getPhoneCallDetails();
- details.viaNumber = viaNumber;
- details.accountHandle = new PhoneAccountHandle(new ComponentName("",""), "");
- mHelper.setPhoneCallDetails(mViews, details);
- }
-
- /** Populates the phone call details with the Defaults. */
- private void setDefaultPhoneCallDetailsNoViaNumber() {
- PhoneCallDetails details = getPhoneCallDetails();
- mPhoneUtils.setAccountLabel("");
- mHelper.setPhoneCallDetails(mViews, details);
- }
-
- /** Sets the phone call details with default values and the given number. */
- private void setPhoneCallDetailsWithNumberAndGeocode(
- String number, String formattedNumber, String geocodedLocation) {
- PhoneCallDetails details = getPhoneCallDetails(
- number, Calls.PRESENTATION_ALLOWED, formattedNumber);
- details.geocode = geocodedLocation;
- mHelper.setPhoneCallDetails(mViews, details);
- }
-
- /** Sets the phone call details with default values and the given date. */
- private void setPhoneCallDetailsWithDate(long date) {
- PhoneCallDetails details = getPhoneCallDetails();
- details.date = date;
- mHelper.setPhoneCallDetails(mViews, details);
- }
-
- private void setVoicemailPhoneCallDetailsWithDate(long date) {
- PhoneCallDetails details = getPhoneCallDetails();
- details.date = date;
- details.callTypes = new int[] {Calls.VOICEMAIL_TYPE};
- mHelper.setPhoneCallDetails(mViews, details);
- }
-
- /** Sets the voice mail details with default values and the given duration. */
- private void setVoicemailPhoneCallDetailsWithDuration(long duration) {
- PhoneCallDetails details = getPhoneCallDetails();
- details.duration = duration;
- details.callTypes = new int[] {Calls.VOICEMAIL_TYPE};
- mHelper.setPhoneCallDetails(mViews, details);
- }
-
- /** Sets the phone call details with default values and the given call types using icons. */
- private void setPhoneCallDetailsWithCallTypeIcons(int... callTypes) {
- PhoneCallDetails details = getPhoneCallDetails();
- details.callTypes = callTypes;
- mHelper.setPhoneCallDetails(mViews, details);
- }
-
- private void setCallDetailsHeaderWithNumber(String number, int presentation) {
- mHelper.setCallDetailsHeader(mNameView,
- getPhoneCallDetails(number, presentation, TEST_FORMATTED_NUMBER));
- }
-
- private void setCallDetailsHeader(String name) {
- PhoneCallDetails details = getPhoneCallDetails();
- details.namePrimary = name;
- mHelper.setCallDetailsHeader(mNameView, details);
- }
-
- private PhoneCallDetails getPhoneCallDetails() {
- PhoneCallDetails details = new PhoneCallDetails(
- mContext,
- TEST_NUMBER,
- Calls.PRESENTATION_ALLOWED,
- TEST_FORMATTED_NUMBER,
- EMPTY_POSTDIAL,
- false /* isVoicemail */);
- setDefaultDetails(details);
- return details;
- }
-
- private PhoneCallDetails getPhoneCallDetails(
- String number, int presentation, String formattedNumber) {
- PhoneCallDetails details = new PhoneCallDetails(
- mContext,
- number,
- presentation,
- formattedNumber,
- EMPTY_POSTDIAL,
- isVoicemail(number));
- setDefaultDetails(details);
- return details;
- }
-
- private void setDefaultDetails(PhoneCallDetails details) {
- details.callTypes = new int[]{ AppCompatConstants.CALLS_INCOMING_TYPE };
- details.countryIso = TEST_COUNTRY_ISO;
- details.date = TEST_DATE;
- details.duration = TEST_DURATION;
- details.geocode = TEST_GEOCODE;
- }
-
- private boolean isVoicemail(String number) {
- return number.equals(TEST_VOICEMAIL_NUMBER);
- }
-}
diff --git a/tests/src/com/android/dialer/calllog/PhoneCallDetailsTest.java b/tests/src/com/android/dialer/calllog/PhoneCallDetailsTest.java
deleted file mode 100644
index 5c500d8bb..000000000
--- a/tests/src/com/android/dialer/calllog/PhoneCallDetailsTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.android.dialer.calllog;
-
-import android.content.res.Resources;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.text.SpannableString;
-import android.text.TextUtils;
-import android.text.style.TtsSpan;
-
-import com.android.dialer.PhoneCallDetails;
-import com.android.dialer.R;
-import com.android.contacts.common.util.ContactDisplayUtils;
-
-/**
- * Unit tests for {@link PhoneCallDetails}.
- */
-public class PhoneCallDetailsTest extends AndroidTestCase {
- private static final String VIA_NUMBER = "+16505551212";
- private static final String PHONE_ACCOUNT_LABEL = "TEST";
-
- private Resources mResources;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mResources = mContext.getResources();
- }
-
- @SmallTest
- public void testCreateAccountLabelDescription_NoViaNumberNoAccountLabel() {
- CharSequence result = PhoneCallDetails.createAccountLabelDescription(mResources, "","");
- assertEquals("", result);
- }
-
- @SmallTest
- public void testCreateAccountLabelDescription_ViaNumberAccountLabel() {
- String msg = mResources.getString(R.string.description_via_number_phone_account,
- PHONE_ACCOUNT_LABEL, VIA_NUMBER);
- CharSequence accountNumberLabel = ContactDisplayUtils.getTelephoneTtsSpannable(msg,
- VIA_NUMBER);
- CharSequence result = PhoneCallDetails.createAccountLabelDescription(mResources, VIA_NUMBER,
- PHONE_ACCOUNT_LABEL);
- assertEquals(accountNumberLabel.toString(), result.toString());
- }
-
- @SmallTest
- public void testCreateAccountLabelDescription_ViaNumber() {
- CharSequence viaNumberLabel = ContactDisplayUtils.getTtsSpannedPhoneNumber(mResources,
- R.string.description_via_number, VIA_NUMBER);
- CharSequence result = PhoneCallDetails.createAccountLabelDescription(mResources, VIA_NUMBER,
- "");
- assertEquals(viaNumberLabel.toString(), result.toString());
- }
-
- @SmallTest
- public void testCreateAccountLabelDescription_AccountLabel() {
- CharSequence accountLabel = TextUtils.expandTemplate(
- mResources.getString(R.string.description_phone_account), PHONE_ACCOUNT_LABEL);
- CharSequence result = PhoneCallDetails.createAccountLabelDescription(mResources, "",
- PHONE_ACCOUNT_LABEL);
- assertEquals(accountLabel, result);
- }
-}
diff --git a/tests/src/com/android/dialer/calllog/calllogcache/TestTelecomCallLogCache.java b/tests/src/com/android/dialer/calllog/calllogcache/TestTelecomCallLogCache.java
deleted file mode 100644
index 270019afd..000000000
--- a/tests/src/com/android/dialer/calllog/calllogcache/TestTelecomCallLogCache.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.calllog.calllogcache;
-
-import android.content.Context;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-
-/**
- * Modified version of {@link com.android.dialer.calllog.calllogcache.CallLogCache} to be used in
- * tests that allows injecting the voicemail number.
- *
- * NOTE: This tests the pre-LMR1 version because currently none of the tests involve multi-SIM,
- * but...
- * TODO: write tests to test multi-SIM functionality in TelecomCallLogCache.
- */
-public class TestTelecomCallLogCache extends CallLogCache {
- private CharSequence mVoicemailNumber;
- private String mAccountLabel;
-
- public TestTelecomCallLogCache(Context context, CharSequence voicemailNumber,
- String accountLabel) {
- super(context);
- mVoicemailNumber = voicemailNumber;
- mAccountLabel = accountLabel;
- }
-
- @Override
- public boolean isVoicemailNumber(PhoneAccountHandle accountHandle, CharSequence number) {
- return mVoicemailNumber.equals(number);
- }
-
- @Override
- public String getAccountLabel(PhoneAccountHandle accountHandle) {
- return mAccountLabel;
- }
-
- public void setAccountLabel(String accountLabel) {
- mAccountLabel = accountLabel;
- }
-
- @Override
- public int getAccountColor(PhoneAccountHandle accountHandle) {
- return PhoneAccount.NO_HIGHLIGHT_COLOR;
- }
-
- @Override
- public boolean doesAccountSupportCallSubject(PhoneAccountHandle accountHandle) {
- return false;
- }
-}
diff --git a/tests/src/com/android/dialer/compat/FilteredNumberCompatInstrumentationTest.java b/tests/src/com/android/dialer/compat/FilteredNumberCompatInstrumentationTest.java
deleted file mode 100644
index 8ceb25046..000000000
--- a/tests/src/com/android/dialer/compat/FilteredNumberCompatInstrumentationTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.dialer.compat;
-
-import android.app.AlertDialog;
-import android.app.DialogFragment;
-import android.app.FragmentManager;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.DialogInterface;
-import android.provider.BlockedNumberContract.BlockedNumbers;
-import android.test.ActivityInstrumentationTestCase2;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.R;
-
-/**
- * UI tests for FilteredNumberCompat
- */
-public class FilteredNumberCompatInstrumentationTest extends
- ActivityInstrumentationTestCase2<DialtactsActivity> {
-
- private static final String E164_NUMBER = "+16502530000";
- private static final String NUMBER = "6502530000";
- private static final String COUNTRY_ISO = "US";
-
- private ContentResolver mContentResolver;
- private FragmentManager mFragmentManager;
-
- public FilteredNumberCompatInstrumentationTest() {
- super(DialtactsActivity.class);
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mContentResolver = getActivity().getContentResolver();
- mFragmentManager = getActivity().getFragmentManager();
- mContentResolver.delete(BlockedNumbersSdkCompat.CONTENT_URI,
- BlockedNumbers.COLUMN_ORIGINAL_NUMBER + " = ?", new String[]{NUMBER});
- }
-
- public void testShowBlockNumberDialogFlow_AlreadyBlocked() throws InterruptedException {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
-
- ContentValues values = new ContentValues();
- values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, NUMBER);
- mContentResolver.insert(BlockedNumbers.CONTENT_URI, values);
-
- FilteredNumberCompat.setHasMigratedToNewBlocking(false);
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- FilteredNumberCompat
- .showBlockNumberDialogFlow(mContentResolver, null, NUMBER, COUNTRY_ISO,
- E164_NUMBER, R.id.floating_action_button_container,
- mFragmentManager, null);
- }
- });
- getInstrumentation().waitForIdleSync();
-
- final DialogFragment migrateDialogFragment = (DialogFragment) mFragmentManager
- .findFragmentByTag("MigrateBlockedNumbers");
- assertTrue(migrateDialogFragment.getDialog().isShowing());
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- ((AlertDialog) migrateDialogFragment.getDialog())
- .getButton(DialogInterface.BUTTON_POSITIVE).performClick();
- }
- });
- getInstrumentation().waitForIdleSync();
- assertNull(mFragmentManager.findFragmentByTag("BlockNumberDialog"));
- }
-}
diff --git a/tests/src/com/android/dialer/compat/FilteredNumberCompatTest.java b/tests/src/com/android/dialer/compat/FilteredNumberCompatTest.java
deleted file mode 100644
index 3572316db..000000000
--- a/tests/src/com/android/dialer/compat/FilteredNumberCompatTest.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * 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.dialer.compat;
-
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.net.Uri;
-import android.os.UserManager;
-import android.provider.BlockedNumberContract.BlockedNumbers;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.dialer.DialerApplication;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberSources;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberTypes;
-import com.android.dialer.filterednumber.BlockedNumbersSettingsActivity;
-
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Arrays;
-
-@SmallTest
-public class FilteredNumberCompatTest extends AndroidTestCase {
-
- private static final String E164_NUMBER = "+16502530000";
- private static final String NON_E164_NUMBER = "6502530000";
- private static final String COUNTRY_ISO = "US";
-
- private static final Uri EXPECTED_BASE_URI = CompatUtils.isNCompatible()
- ? BlockedNumbers.CONTENT_URI : FilteredNumber.CONTENT_URI;
-
- @Mock private Context mContext;
- @Mock private SharedPreferences mSharedPreferences;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
- DialerApplication.setContextForTest(mContext);
- when(mContext.getSharedPreferences(anyString(), anyInt())).thenReturn(mSharedPreferences);
- FilteredNumberCompat.setIsEnabledForTest(true);
- }
-
- public void testIsNewFilteringEnabled_TestValueFalse() {
- FilteredNumberCompat.setIsEnabledForTest(false);
- assertFalse(FilteredNumberCompat.canUseNewFiltering());
- }
-
- public void testIsNewFilteringEnabled_TestValueTrue() {
- FilteredNumberCompat.setIsEnabledForTest(true);
- assertEquals(CompatUtils.isNCompatible(), FilteredNumberCompat.canUseNewFiltering());
- }
-
- public void testHasMigratedToNewBlocking_False() {
- assertFalse(FilteredNumberCompat.hasMigratedToNewBlocking());
- }
-
- public void testHasMigratedToNewBlocking_Migrated() {
- when(mSharedPreferences
- .getBoolean(FilteredNumberCompat.HAS_MIGRATED_TO_NEW_BLOCKING_KEY, false))
- .thenReturn(true);
- assertTrue(FilteredNumberCompat.hasMigratedToNewBlocking());
- }
-
- public void testGetContentUri_NullId() {
- assertEquals(FilteredNumber.CONTENT_URI, FilteredNumberCompat.getContentUri(null));
- }
-
- public void testGetContentUri_NotMigrated() {
- assertEquals(ContentUris.withAppendedId(FilteredNumber.CONTENT_URI, 1),
- FilteredNumberCompat.getContentUri(1));
- }
-
- public void testGetContentUri_Migrated() {
- when(mSharedPreferences
- .getBoolean(FilteredNumberCompat.HAS_MIGRATED_TO_NEW_BLOCKING_KEY, false))
- .thenReturn(true);
- assertEquals(ContentUris.withAppendedId(EXPECTED_BASE_URI, 1),
- FilteredNumberCompat.getContentUri(1));
- }
-
- public void testFilter_NullProjection() {
- assertNull(FilteredNumberCompat.filter(null));
- }
-
- public void testFilter_NoNulls() {
- assertArrayEquals(new String[] {"a", "b", "c"},
- FilteredNumberCompat.filter(new String[] {"a", "b", "c"}));
- }
-
- public void testFilter_WithNulls() {
- assertArrayEquals(new String[] {"a", "b"},
- FilteredNumberCompat.filter(new String[] {"a", null, "b"}));
- }
-
- public void testNewBlockNumberContentValues_NullNumber() {
- try {
- FilteredNumberCompat.newBlockNumberContentValues(null, null, null);
- fail();
- } catch (NullPointerException e) {}
- }
-
- public void testNewBlockNumberContentValues_N_NotMigrated() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- assertEquals(newExpectedContentValuesM(NON_E164_NUMBER, null, null),
- FilteredNumberCompat.newBlockNumberContentValues(NON_E164_NUMBER, null, null));
- }
-
- public void testNewBlockNumberContentValues_N_Migrated() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- ContentValues contentValues = new ContentValues();
- contentValues.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, NON_E164_NUMBER);
- when(mSharedPreferences
- .getBoolean(FilteredNumberCompat.HAS_MIGRATED_TO_NEW_BLOCKING_KEY, false))
- .thenReturn(true);
- assertEquals(contentValues, FilteredNumberCompat.newBlockNumberContentValues(
- NON_E164_NUMBER,
- null, null));
- }
-
- public void testNewBlockNumberContentValues_N_Disabled() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- FilteredNumberCompat.setIsEnabledForTest(false);
- assertEquals(newExpectedContentValuesM(NON_E164_NUMBER, E164_NUMBER, COUNTRY_ISO),
- FilteredNumberCompat.newBlockNumberContentValues(NON_E164_NUMBER, E164_NUMBER, COUNTRY_ISO));
- }
-
- public void testNewBlockNumberContentValues_M_NullE164() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- assertEquals(newExpectedContentValuesM(NON_E164_NUMBER, E164_NUMBER, COUNTRY_ISO),
- FilteredNumberCompat.newBlockNumberContentValues(NON_E164_NUMBER, null, COUNTRY_ISO));
- }
-
- public void testNewBlockNumberContentValues_M_NullCountryIso() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- assertEquals(newExpectedContentValuesM(NON_E164_NUMBER, E164_NUMBER, null),
- FilteredNumberCompat.newBlockNumberContentValues(NON_E164_NUMBER, E164_NUMBER, null));
- }
-
- public void testNewBlockNumberContentValues_M_NullE164AndCountryIso() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- // Number can't be formatted properly without country code
- assertEquals(newExpectedContentValuesM(NON_E164_NUMBER, null, null),
- FilteredNumberCompat.newBlockNumberContentValues(NON_E164_NUMBER, null, null));
- }
-
- public void testCreateManageBlockedNumbersIntent_NullContext() {
- try {
- FilteredNumberCompat.createManageBlockedNumbersIntent(null);
- fail();
- } catch (NullPointerException e) {}
- }
-
- public void testCreateManageBlockedNumbersIntent_M() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- assertEquals(new ComponentName(getContext(), BlockedNumbersSettingsActivity.class),
- FilteredNumberCompat.createManageBlockedNumbersIntent(getContext()).getComponent());
- }
-
- public void testCreateManageBlockedNumbersIntent_N_Disabled_NotMigrated() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- FilteredNumberCompat.setIsEnabledForTest(false);
- assertEquals(new ComponentName(getContext(), BlockedNumbersSettingsActivity.class),
- FilteredNumberCompat.createManageBlockedNumbersIntent(getContext()).getComponent());
- }
-
- public void testCreateManageBlockedNumbersIntent_N_Enabled_NotMigrated() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- assertEquals(new ComponentName(getContext(), BlockedNumbersSettingsActivity.class),
- FilteredNumberCompat.createManageBlockedNumbersIntent(getContext()).getComponent());
- }
-
- public void testCreateManageBlockedNumbersIntent_N_Enabled_Migrated() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- when(mSharedPreferences
- .getBoolean(FilteredNumberCompat.HAS_MIGRATED_TO_NEW_BLOCKING_KEY, false))
- .thenReturn(true);
- assertFalse(new ComponentName(getContext(), BlockedNumbersSettingsActivity.class)
- .equals(FilteredNumberCompat.createManageBlockedNumbersIntent(getContext())
- .getComponent()));
- }
-
- public void testCanCurrentUserOpenBlockSettings_M_SecondaryUser() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- UserManager userManager = mock(UserManager.class);
- when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(userManager);
- when(userManager.isSystemUser()).thenReturn(false);
- assertFalse(FilteredNumberCompat.canCurrentUserOpenBlockSettings(mContext));
- verify(mContext).getSystemService(Context.USER_SERVICE);
- verify(userManager).isSystemUser();
- }
-
- public void testCanCurrentUserOpenBlockSettings_M_PrimaryUser() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- UserManager userManager = mock(UserManager.class);
- when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(userManager);
- when(userManager.isSystemUser()).thenReturn(true);
- assertTrue(FilteredNumberCompat.canCurrentUserOpenBlockSettings(mContext));
- verify(mContext).getSystemService(Context.USER_SERVICE);
- verify(userManager).isSystemUser();
- }
-
- public void testCanAttemptBlockOperations_M_SecondaryUser() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- UserManager userManager = mock(UserManager.class);
- when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(userManager);
- when(userManager.isSystemUser()).thenReturn(false);
- assertFalse(FilteredNumberCompat.canAttemptBlockOperations(mContext));
- verify(mContext).getSystemService(Context.USER_SERVICE);
- verify(userManager).isSystemUser();
- }
-
- public void testCanAttemptBlockOperations_M_PrimaryUser() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- UserManager userManager = mock(UserManager.class);
- when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(userManager);
- when(userManager.isSystemUser()).thenReturn(true);
- assertTrue(FilteredNumberCompat.canAttemptBlockOperations(mContext));
- verify(mContext).getSystemService(Context.USER_SERVICE);
- verify(userManager).isSystemUser();
- }
-
- private ContentValues newExpectedContentValuesM(String number, String e164Number,
- String countryIso) {
- ContentValues contentValues = new ContentValues();
- contentValues.put(FilteredNumberColumns.NUMBER, number);
- contentValues.put(FilteredNumberColumns.NORMALIZED_NUMBER, e164Number);
- contentValues.put(FilteredNumberColumns.COUNTRY_ISO, countryIso);
- contentValues.put(FilteredNumberColumns.TYPE, FilteredNumberTypes.BLOCKED_NUMBER);
- contentValues.put(FilteredNumberColumns.SOURCE, FilteredNumberSources.USER);
- return contentValues;
- }
-
- private void assertArrayEquals(String[] expected, String[] actual) {
- assertEquals(Arrays.toString(expected), Arrays.toString(actual));
- }
-}
diff --git a/tests/src/com/android/dialer/compat/UserManagerCompatTest.java b/tests/src/com/android/dialer/compat/UserManagerCompatTest.java
deleted file mode 100644
index ff734a19d..000000000
--- a/tests/src/com/android/dialer/compat/UserManagerCompatTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.dialer.compat;
-
-import android.test.AndroidTestCase;
-
-import com.android.contacts.common.compat.CompatUtils;
-
-public class UserManagerCompatTest extends AndroidTestCase {
-
- public void testIsUserUnlocked_N_NullContext() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- try {
- UserManagerCompat.isUserUnlocked(null);
- fail("Expected NullPointerException but none was thrown");
- } catch (NullPointerException e) {}
- }
-
- public void testIsUserUnlocked_M_NullContext() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- assertTrue(UserManagerCompat.isUserUnlocked(null));
- }
-
- public void testIsUserUnlocked() {
- assertTrue(UserManagerCompat.isUserUnlocked(getContext()));
- }
-}
diff --git a/tests/src/com/android/dialer/contactinfo/ContactPhotoLoaderTest.java b/tests/src/com/android/dialer/contactinfo/ContactPhotoLoaderTest.java
deleted file mode 100644
index 42a5ae966..000000000
--- a/tests/src/com/android/dialer/contactinfo/ContactPhotoLoaderTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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.dialer.contactinfo;
-
-import android.app.Instrumentation;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.support.test.InstrumentationRegistry;
-import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
-import android.test.AndroidTestCase;
-import android.test.InstrumentationTestCase;
-import android.text.TextUtils;
-
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.lettertiles.LetterTileDrawable;
-import com.android.dialer.calllog.ContactInfo;
-import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.tests.R;
-
-public class ContactPhotoLoaderTest extends InstrumentationTestCase {
-
- private Context mContext;
-
- @Override
- public void setUp() {
- mContext = getInstrumentation().getTargetContext();
- }
-
- public void testConstructor() {
- ContactPhotoLoader loader = new ContactPhotoLoader(mContext, new ContactInfo());
- }
-
- public void testConstructor_NullContext() {
- try {
- ContactPhotoLoader loader = new ContactPhotoLoader(null, new ContactInfo());
- fail();
- } catch (NullPointerException e) {
- //expected
- }
- }
-
- public void testConstructor_NullContactInfo() {
- try {
- ContactPhotoLoader loader = new ContactPhotoLoader(mContext, null);
- fail();
- } catch (NullPointerException e) {
- //expected
- }
- }
-
- public void testGetIcon_Photo() {
- ContactInfo info = getTestContactInfo();
- info.photoUri = getResourceUri(R.drawable.phone_icon);
- ContactPhotoLoader loader = new ContactPhotoLoader(mContext, info);
- assertTrue(loader.getIcon() instanceof RoundedBitmapDrawable);
- }
-
- public void testGetIcon_Photo_Invalid() {
- ContactInfo info = getTestContactInfo();
- info.photoUri = Uri.parse("file://invalid/uri");
- ContactPhotoLoader loader = new ContactPhotoLoader(mContext, info);
- //Should fall back to LetterTileDrawable
- assertTrue(loader.getIcon() instanceof LetterTileDrawable);
- }
-
- public void testGetIcon_LetterTile() {
- ContactInfo info = getTestContactInfo();
- ContactPhotoLoader loader = new ContactPhotoLoader(mContext, info);
- assertTrue(loader.getIcon() instanceof LetterTileDrawable);
- }
-
- private Uri getResourceUri(int resId) {
- Context testContext = getInstrumentation().getContext();
- Resources resources = testContext.getResources();
-
- assertNotNull(resources.getDrawable(resId));
- return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
- + testContext.getPackageName()
- + '/' + resId);
- }
-
- private ContactInfo getTestContactInfo() {
- ContactInfo info = new ContactInfo();
- info.name = "foo";
- info.lookupKey = "bar";
- return info;
- }
-} \ No newline at end of file
diff --git a/tests/src/com/android/dialer/database/DatabaseTestUtils.java b/tests/src/com/android/dialer/database/DatabaseTestUtils.java
deleted file mode 100644
index 19fff7f89..000000000
--- a/tests/src/com/android/dialer/database/DatabaseTestUtils.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.dialer.database;
-
-import android.database.MatrixCursor;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.text.TextUtils;
-
-import com.android.dialer.database.DialerDatabaseHelper.ContactNumber;
-
-public class DatabaseTestUtils {
- public static MatrixCursor constructNewNameCursor() {
- final MatrixCursor cursor = new MatrixCursor(new String[]{
- DialerDatabaseHelper.SmartDialDbColumns.DISPLAY_NAME_PRIMARY,
- DialerDatabaseHelper.SmartDialDbColumns.CONTACT_ID});
- return cursor;
- }
-
- public static MatrixCursor constructNewContactCursor() {
- final MatrixCursor cursor = new MatrixCursor(new String[]{
- Phone._ID, // 0
- Phone.TYPE, // 1
- Phone.LABEL, // 2
- Phone.NUMBER, // 3
- Phone.CONTACT_ID, // 4
- Phone.LOOKUP_KEY, // 5
- Phone.DISPLAY_NAME_PRIMARY, // 6
- Phone.PHOTO_ID, // 7
- Data.LAST_TIME_USED, // 8
- Data.TIMES_USED, // 9
- Contacts.STARRED, // 10
- Data.IS_SUPER_PRIMARY, // 11
- Contacts.IN_VISIBLE_GROUP, // 12
- Data.IS_PRIMARY, // 13
- Data.CARRIER_PRESENCE}); // 14
- return cursor;
- }
-
- public static ContactNumber constructNewContactWithDummyIds(MatrixCursor contactCursor,
- MatrixCursor nameCursor, String number, int id, String displayName) {
- return constructNewContact(contactCursor, nameCursor, id, number, id, String.valueOf(id),
- displayName, 0, 0, 0, 0, 0, 0, 0, 0);
- }
-
- public static ContactNumber constructNewContact(MatrixCursor contactCursor,
- MatrixCursor nameCursor, int id, String number, int contactId, String lookupKey,
- String displayName, int photoId, int lastTimeUsed, int timesUsed, int starred,
- int isSuperPrimary, int inVisibleGroup, int isPrimary, int carrierPresence) {
- if (contactCursor == null || nameCursor == null) {
- throw new IllegalArgumentException("Provided MatrixCursors cannot be null");
- }
-
- if (TextUtils.isEmpty(number)) {
- // Add a dummy number, otherwise DialerDatabaseHelper simply ignores the entire
- // row if the number is empty
- number = "0";
- }
-
- contactCursor.addRow(new Object[]{id, "", "", number, contactId, lookupKey, displayName,
- photoId, lastTimeUsed, timesUsed, starred, isSuperPrimary, inVisibleGroup,
- isPrimary, carrierPresence});
- nameCursor.addRow(new Object[]{displayName, contactId});
-
- return new ContactNumber(contactId, id, displayName, number, lookupKey, 0, 0);
- }
-}
diff --git a/tests/src/com/android/dialer/database/DialerDatabaseHelperTest.java b/tests/src/com/android/dialer/database/DialerDatabaseHelperTest.java
deleted file mode 100644
index a95a79e08..000000000
--- a/tests/src/com/android/dialer/database/DialerDatabaseHelperTest.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.database;
-
-import static com.android.dialer.database.DatabaseTestUtils.*;
-
-import android.database.MatrixCursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
-import android.test.AndroidTestCase;
-
-import com.android.dialer.database.DialerDatabaseHelper;
-import com.android.dialer.database.DialerDatabaseHelper.ContactNumber;
-import com.android.dialer.dialpad.SmartDialNameMatcher;
-import com.android.dialer.dialpad.SmartDialPrefix;
-
-import java.lang.Exception;
-import java.lang.Override;
-import java.util.ArrayList;
-
-/**
- * Validates the behavior of the smart dial database helper with regards to contact updates and
- * deletes.
- * To run this test, use the command:
- * adb shell am instrument -w -e class com.android.dialer.database.DialerDatabaseHelperTest /
- * com.android.dialer.tests/android.test.InstrumentationTestRunner
- */
-@SmallTest
-public class DialerDatabaseHelperTest extends AndroidTestCase {
-
- private DialerDatabaseHelper mTestHelper;
- private SQLiteDatabase mDb;
-
- @Override
- protected void setUp() {
- mTestHelper = DialerDatabaseHelper.getNewInstanceForTest(getContext());
- mDb = mTestHelper.getWritableDatabase();
- }
-
- @Override
- protected void tearDown() throws Exception {
- final SQLiteDatabase db = mTestHelper.getWritableDatabase();
- mTestHelper.removeAllContacts(db);
- super.tearDown();
- }
-
- /**
- * Verifies that a new contact added into the database is a match after the update.
- */
- public void testForNewContacts() {
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
-
- mTestHelper.insertUpdatedContactsAndNumberPrefix(mDb, contactCursor, 0L);
- mTestHelper.insertNamePrefixes(mDb, nameCursor);
- assertEquals(0, getMatchesFromDb("5105272357").size());
-
- // Insert new contact
- constructNewContactWithDummyIds(contactCursor, nameCursor,
- "510-527-2357", 0, "James");
- mTestHelper.insertUpdatedContactsAndNumberPrefix(mDb, contactCursor, 1L);
- mTestHelper.insertNamePrefixes(mDb, nameCursor);
- assertEquals(1, getMatchesFromDb("5105272357").size());
- }
-
- /**
- * Verifies that a contact that has its phone number changed is a match after the update.
- */
- public void testForUpdatedContacts() {
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
- constructNewContactWithDummyIds(contactCursor, nameCursor,
- "510-527-2357", 0, "James");
- mTestHelper.insertUpdatedContactsAndNumberPrefix(mDb, contactCursor, 0L);
- mTestHelper.insertNamePrefixes(mDb, nameCursor);
- assertEquals(1, getMatchesFromDb("5105272357").size());
- assertEquals(0, getMatchesFromDb("6501234567").size());
-
- // Update the database with the new contact information
- final MatrixCursor nameCursor2 = constructNewNameCursor();
- final MatrixCursor contactCursor2 = constructNewContactCursor();
- constructNewContactWithDummyIds(contactCursor2, nameCursor2,
- "650-123-4567", 0, "James");
- mTestHelper.removeUpdatedContacts(mDb, contactCursor2);
- mTestHelper.insertUpdatedContactsAndNumberPrefix(mDb, contactCursor2, 1L);
- mTestHelper.insertNamePrefixes(mDb, nameCursor2);
-
- // Now verify the matches are correct based on the new information
- assertEquals(0, getMatchesFromDb("5105272357").size());
- assertEquals(1, getMatchesFromDb("6501234567").size());
- }
-
- /**
- * Verifies that a contact that is deleted from CP2 is similarly deleted from the database
- */
- public void testForDeletedContacts() {
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
- constructNewContactWithDummyIds(contactCursor, nameCursor,
- "510-527-2357", 0, "James");
- mTestHelper.insertUpdatedContactsAndNumberPrefix(mDb, contactCursor, 0L);
- mTestHelper.insertNamePrefixes(mDb, nameCursor);
- assertEquals(1, getMatchesFromDb("5105272357").size());
-
- // Delete the contact and update its projection.
- final MatrixCursor deletedCursor =
- new MatrixCursor(DialerDatabaseHelper.DeleteContactQuery.PROJECTION);
- deletedCursor.addRow(new Object[] {0, 1L});
- mTestHelper.removeDeletedContacts(mDb, deletedCursor);
- assertEquals(0, getMatchesFromDb("5105272357").size());
- }
-
- /**
- * Verifies that when a contact's number is deleted (but not the entire contact), the
- * number is correctly deleted from the database.
- */
- public void testForDeletedNumber() {
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
- constructNewContactWithDummyIds(contactCursor, nameCursor,
- "510-527-2357", 0, "James");
- mTestHelper.insertUpdatedContactsAndNumberPrefix(mDb, contactCursor, 0L);
- mTestHelper.insertNamePrefixes(mDb, nameCursor);
- assertEquals(1, getMatchesFromDb("5105272357").size());
-
- // Match no longer exists after number was deleted from contact
- final MatrixCursor updatedContactCursor =
- new MatrixCursor(DialerDatabaseHelper.UpdatedContactQuery.PROJECTION);
- updatedContactCursor.addRow(new Object[] {0});
- mTestHelper.removeUpdatedContacts(mDb, updatedContactCursor);
- assertEquals(0, getMatchesFromDb("5105272357").size());
- }
-
- private ArrayList<ContactNumber> getMatchesFromDb(String query) {
- final SmartDialNameMatcher nameMatcher = new SmartDialNameMatcher(query,
- SmartDialPrefix.getMap());
- return mTestHelper.getLooseMatches(query, nameMatcher);
- }
-}
diff --git a/tests/src/com/android/dialer/database/FilteredNumberAsyncQueryHandlerTest.java b/tests/src/com/android/dialer/database/FilteredNumberAsyncQueryHandlerTest.java
deleted file mode 100644
index 625f3fdb5..000000000
--- a/tests/src/com/android/dialer/database/FilteredNumberAsyncQueryHandlerTest.java
+++ /dev/null
@@ -1,457 +0,0 @@
-/*
- * 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.dialer.database;
-
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.net.Uri;
-import android.provider.BlockedNumberContract;
-import android.provider.BlockedNumberContract.BlockedNumbers;
-import android.test.InstrumentationTestCase;
-import android.test.mock.MockContentResolver;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.test.mocks.MockContentProvider;
-import com.android.contacts.common.test.mocks.MockContentProvider.Query;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnBlockNumberListener;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnHasBlockedNumbersListener;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnUnblockNumberListener;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberSources;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberTypes;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-@SmallTest
-public class FilteredNumberAsyncQueryHandlerTest extends InstrumentationTestCase {
-
- private static final String E164_NUMBER = "+16502530000";
- private static final String NUMBER = "6502530000";
- private static final String COUNTRY_ISO = "US";
- private static final Integer ID = 1;
- private static final Integer ID2 = 2;
- private static final Uri BLOCKED_NUMBER_URI_N = CompatUtils.isNCompatible() ?
- Uri.withAppendedPath(BlockedNumberContract.AUTHORITY_URI, "blocked") : null;
- private static final Uri BLOCKED_NUMBER_URI_M =
- Uri.withAppendedPath(FilteredNumberContract.AUTHORITY_URI, "filtered_numbers_table");
- private static final Uri BLOCKED_NUMBER_URI = CompatUtils.isNCompatible() ? BLOCKED_NUMBER_URI_N
- : BLOCKED_NUMBER_URI_M;
- private static final Uri BLOCKED_NUMBER_URI_WITH_ID =
- ContentUris.withAppendedId(BLOCKED_NUMBER_URI, ID);
- private static final Uri EXPECTED_URI = Uri.fromParts("android", "google", "dialer");
-
- private final MockContentResolver mContentResolver = new MockContentResolver();
- private final MockContentProvider mContentProvider = new MockContentProvider();
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- FilteredNumberCompat.setIsEnabledForTest(true);
- if (CompatUtils.isNCompatible()) {
- mContentResolver.addProvider(BlockedNumberContract.AUTHORITY, mContentProvider);
- } else {
- mContentResolver.addProvider(FilteredNumberContract.AUTHORITY, mContentProvider);
- }
- }
-
- public void testHasBlockedNumbers_Disabled() throws Throwable {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- FilteredNumberCompat.setIsEnabledForTest(false);
- final MockContentResolver resolver = new MockContentResolver();
- MockContentProvider disabledProvider = new MockContentProvider();
- resolver.addProvider(FilteredNumberContract.AUTHORITY, disabledProvider);
-
- disabledProvider.expectQuery(BLOCKED_NUMBER_URI_M).withProjection(FilteredNumberColumns._ID)
- .withSelection(FilteredNumberColumns.TYPE + "="
- + FilteredNumberTypes.BLOCKED_NUMBER, null).returnRow(ID);
- final HasBlockedNumbersListener listener = new HasBlockedNumbersListener();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- new FilteredNumberAsyncQueryHandler(resolver).hasBlockedNumbers(listener);
- }
- });
- assertTrue(listener.waitForCallback());
- disabledProvider.verify();
- }
-
- public void testHasBlockedNumbers_NoResults() throws Throwable {
- newHasBlockedNumbersExpectedQuery().returnEmptyCursor();
- final HasBlockedNumbersListener listener = new HasBlockedNumbersListener();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- new FilteredNumberAsyncQueryHandler(mContentResolver).hasBlockedNumbers(listener);
- }
- });
- assertFalse(listener.waitForCallback());
- mContentProvider.verify();
- }
-
- public void testHasBlockedNumbers() throws Throwable {
- newHasBlockedNumbersExpectedQuery().returnRow(ID);
- final HasBlockedNumbersListener listener = new HasBlockedNumbersListener();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- new FilteredNumberAsyncQueryHandler(mContentResolver).hasBlockedNumbers(listener);
- }
- });
- assertTrue(listener.waitForCallback());
- mContentProvider.verify();
- }
-
- public void testIsBlockedNumber_Disabled() throws Throwable {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- FilteredNumberCompat.setIsEnabledForTest(false);
- final MockContentResolver resolver = new MockContentResolver();
- MockContentProvider disabledProvider = new MockContentProvider();
- resolver.addProvider(FilteredNumberContract.AUTHORITY, disabledProvider);
- disabledProvider.expectQuery(BLOCKED_NUMBER_URI_M)
- .withProjection(FilteredNumberColumns._ID, FilteredNumberColumns.TYPE)
- .withSelection(FilteredNumberColumns.NORMALIZED_NUMBER + " = ?", E164_NUMBER)
- .returnRow(ID, FilteredNumberTypes.BLOCKED_NUMBER);
- final CheckBlockedListener listener = new CheckBlockedListener();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- new FilteredNumberAsyncQueryHandler(resolver)
- .isBlockedNumber(listener, NUMBER, COUNTRY_ISO);
- }
- });
- assertEquals(ID, listener.waitForCallback());
- mContentProvider.verify();
- }
-
- public void testIsBlockedNumber_NoResults() throws Throwable {
- newIsBlockedNumberExpectedQuery().returnEmptyCursor();
- final CheckBlockedListener listener = new CheckBlockedListener();
-
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- new FilteredNumberAsyncQueryHandler(mContentResolver)
- .isBlockedNumber(listener, NUMBER, COUNTRY_ISO);
- }
- });
- assertNull(listener.waitForCallback());
- mContentProvider.verify();
- }
-
- public void testIsBlockedNumber() throws Throwable {
- if (CompatUtils.isNCompatible()) {
- newIsBlockedNumberExpectedQuery().returnRow(ID);
- } else {
- newIsBlockedNumberExpectedQuery().returnRow(ID, FilteredNumberTypes.BLOCKED_NUMBER);
- }
- final CheckBlockedListener listener = new CheckBlockedListener();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- new FilteredNumberAsyncQueryHandler(mContentResolver)
- .isBlockedNumber(listener, NUMBER, COUNTRY_ISO);
- }
- });
- assertEquals(ID, listener.waitForCallback());
- mContentProvider.verify();
- }
-
- public void testIsBlockedNumber_MultipleResults() throws Throwable {
- if (CompatUtils.isNCompatible()) {
- newIsBlockedNumberExpectedQuery().returnRow(ID).returnRow(ID2);
- } else {
- newIsBlockedNumberExpectedQuery().returnRow(ID, FilteredNumberTypes.BLOCKED_NUMBER)
- .returnRow(ID2, FilteredNumberTypes.BLOCKED_NUMBER);
- }
- final CheckBlockedListener listener = new CheckBlockedListener();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- new FilteredNumberAsyncQueryHandler(mContentResolver)
- .isBlockedNumber(listener, NUMBER, COUNTRY_ISO);
- }
- });
- // When there are multiple matches, the first is returned
- assertEquals(ID, listener.waitForCallback());
- mContentProvider.verify();
- }
-
- public void testBlockNumber_Disabled() throws Throwable {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- FilteredNumberCompat.setIsEnabledForTest(false);
- final MockContentResolver resolver = new MockContentResolver();
- MockContentProvider disabledProvider = new MockContentProvider();
- resolver.addProvider(FilteredNumberContract.AUTHORITY, disabledProvider);
-
- disabledProvider.expectInsert(BLOCKED_NUMBER_URI_M, newBlockNumberContentValuesM(),
- EXPECTED_URI);
- final BlockNumberListener listener = new BlockNumberListener();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- new FilteredNumberAsyncQueryHandler(resolver).blockNumber(listener, E164_NUMBER,
- NUMBER, COUNTRY_ISO);
- }
- });
- assertSame(EXPECTED_URI, listener.waitForCallback());
- disabledProvider.verify();
- }
-
- public void testBlockNumber() throws Throwable {
- mContentProvider.expectInsert(BLOCKED_NUMBER_URI, newBlockNumberContentValues(),
- EXPECTED_URI);
- final BlockNumberListener listener = new BlockNumberListener();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- new FilteredNumberAsyncQueryHandler(mContentResolver).blockNumber(listener,
- E164_NUMBER, NUMBER, COUNTRY_ISO);
- }
- });
- assertSame(EXPECTED_URI, listener.waitForCallback());
- mContentProvider.verify();
- }
-
- public void testBlockNumber_NullNormalizedNumber() throws Throwable {
- mContentProvider.expectInsert(BLOCKED_NUMBER_URI, newBlockNumberContentValues(),
- EXPECTED_URI);
- final BlockNumberListener listener = new BlockNumberListener();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- new FilteredNumberAsyncQueryHandler(mContentResolver).blockNumber(listener,
- NUMBER, COUNTRY_ISO);
- }
- });
- assertSame(EXPECTED_URI, listener.waitForCallback());
- mContentProvider.verify();
- }
-
- public void testUnblockNumber_Disabled() throws Throwable {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- FilteredNumberCompat.setIsEnabledForTest(false);
- final MockContentResolver resolver = new MockContentResolver();
- MockContentProvider disabledProvider = new MockContentProvider();
- resolver.addProvider(FilteredNumberContract.AUTHORITY, disabledProvider);
-
- Uri uriWithId = ContentUris.withAppendedId(BLOCKED_NUMBER_URI_M, ID);
- disabledProvider.expectQuery(uriWithId)
- .withProjection(null)
- .withDefaultProjection(FilteredNumberCompat.getIdColumnName())
- .withSelection(null, null)
- .withSortOrder(null)
- .returnRow(ID);
- disabledProvider.expectDelete(uriWithId).returnRowsAffected(1);
- final UnblockNumberListener listener = new UnblockNumberListener();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- new FilteredNumberAsyncQueryHandler(resolver).unblock(listener, ID);
- }
- });
- assertNotNull(listener.waitForCallback());
- disabledProvider.verify();
- }
-
- public void testUnblockNumber_NullId() {
- try {
- new FilteredNumberAsyncQueryHandler(mContentResolver).unblock(null, (Integer) null);
- fail();
- } catch (IllegalArgumentException e) {}
- }
-
- public void testUnblockNumber() throws Throwable {
- mContentProvider.expectQuery(BLOCKED_NUMBER_URI_WITH_ID)
- .withProjection(null)
- .withDefaultProjection(FilteredNumberCompat.getIdColumnName())
- .withSelection(null, null)
- .withSortOrder(null)
- .returnRow(ID);
- mContentProvider.expectDelete(BLOCKED_NUMBER_URI_WITH_ID).returnRowsAffected(1);
- final UnblockNumberListener listener = new UnblockNumberListener();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- new FilteredNumberAsyncQueryHandler(mContentResolver).unblock(listener, ID);
- }
- });
- assertNotNull(listener.waitForCallback());
- mContentProvider.verify();
- }
-
- private Query newIsBlockedNumberExpectedQuery() {
- if (CompatUtils.isNCompatible()) {
- return newIsBlockedNumberExpectedQueryN();
- }
- return newIsBlockedNumberExpectedQueryM();
- }
-
- private Query newIsBlockedNumberExpectedQueryN() {
- return mContentProvider.expectQuery(BLOCKED_NUMBER_URI)
- .withProjection(BlockedNumbers.COLUMN_ID)
- .withSelection(BlockedNumbers.COLUMN_E164_NUMBER + " = ?", E164_NUMBER);
- }
-
- private Query newIsBlockedNumberExpectedQueryM() {
- return mContentProvider.expectQuery(BLOCKED_NUMBER_URI)
- .withProjection(FilteredNumberColumns._ID, FilteredNumberColumns.TYPE)
- .withSelection(FilteredNumberColumns.NORMALIZED_NUMBER + " = ?", E164_NUMBER);
- }
-
- private Query newHasBlockedNumbersExpectedQuery() {
- if (CompatUtils.isNCompatible()) {
- return newHasBlockedNumbersExpectedQueryN();
- }
- return newHasBlockedNumbersExpectedQueryM();
- }
-
- private Query newHasBlockedNumbersExpectedQueryN() {
- return mContentProvider.expectQuery(BLOCKED_NUMBER_URI)
- .withProjection(BlockedNumbers.COLUMN_ID)
- .withSelection(null, null);
- }
-
- private Query newHasBlockedNumbersExpectedQueryM() {
- return mContentProvider.expectQuery(BLOCKED_NUMBER_URI).withProjection(
- FilteredNumberColumns._ID)
- .withSelection(FilteredNumberColumns.TYPE + "="
- + FilteredNumberTypes.BLOCKED_NUMBER, null);
- }
-
- private ContentValues newBlockNumberContentValues() {
- if (CompatUtils.isNCompatible()) {
- return newBlockNumberContentValuesN();
- }
- return newBlockNumberContentValuesM();
- }
-
- private ContentValues newBlockNumberContentValuesN() {
- ContentValues contentValues = new ContentValues();
- contentValues.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, NUMBER);
- return contentValues;
- }
-
- private ContentValues newBlockNumberContentValuesM() {
- ContentValues contentValues = new ContentValues();
- contentValues.put(FilteredNumberColumns.NORMALIZED_NUMBER, E164_NUMBER);
- contentValues.put(FilteredNumberColumns.NUMBER, NUMBER);
- contentValues.put(FilteredNumberColumns.COUNTRY_ISO, COUNTRY_ISO);
- contentValues.put(FilteredNumberColumns.TYPE, FilteredNumberTypes.BLOCKED_NUMBER);
- contentValues.put(FilteredNumberColumns.SOURCE, FilteredNumberSources.USER);
- return contentValues;
- }
-
- private class CheckBlockedListener implements OnCheckBlockedListener {
- public final CountDownLatch onCheckCompleteCalled;
- public Integer id;
-
- public CheckBlockedListener() {
- onCheckCompleteCalled = new CountDownLatch(1);
- }
-
- @Override
- public void onCheckComplete(Integer id) {
- this.id = id;
- onCheckCompleteCalled.countDown();
- }
-
- public Integer waitForCallback() throws InterruptedException {
- if (!onCheckCompleteCalled.await(5000, TimeUnit.MILLISECONDS)) {
- throw new IllegalStateException("Waiting on callback timed out.");
- }
- return id;
- }
- }
-
- private class HasBlockedNumbersListener implements OnHasBlockedNumbersListener {
- public final CountDownLatch onHasBlockedNumbersCalled;
- public boolean hasBlockedNumbers;
-
- public HasBlockedNumbersListener() {
- onHasBlockedNumbersCalled = new CountDownLatch(1);
- }
-
- @Override
- public void onHasBlockedNumbers(boolean hasBlockedNumbers) {
- this.hasBlockedNumbers = hasBlockedNumbers;
- onHasBlockedNumbersCalled.countDown();
- }
-
- public boolean waitForCallback() throws InterruptedException {
- if (!onHasBlockedNumbersCalled.await(5000, TimeUnit.MILLISECONDS)) {
- throw new IllegalStateException("Waiting on callback timed out.");
- }
- return hasBlockedNumbers;
- }
- }
-
- private class BlockNumberListener implements OnBlockNumberListener {
- public final CountDownLatch onBlockCompleteCalled;
- public Uri uri;
-
- public BlockNumberListener() {
- onBlockCompleteCalled = new CountDownLatch(1);
- }
-
- @Override
- public void onBlockComplete(Uri uri) {
- this.uri = uri;
- onBlockCompleteCalled.countDown();
- }
-
- public Uri waitForCallback() throws InterruptedException {
- if (!onBlockCompleteCalled.await(5000, TimeUnit.MILLISECONDS)) {
- throw new IllegalStateException("Waiting on callback timed out.");
- }
- return uri;
- }
- }
-
- private class UnblockNumberListener implements OnUnblockNumberListener {
- public final CountDownLatch onUnblockCompleteCalled;
- public Integer result;
-
- public UnblockNumberListener() {
- onUnblockCompleteCalled = new CountDownLatch(1);
- }
-
- @Override
- public void onUnblockComplete(int rows, ContentValues values) {
- result = rows;
- onUnblockCompleteCalled.countDown();
- }
-
- public Integer waitForCallback() throws InterruptedException {
- if (!onUnblockCompleteCalled.await(5000, TimeUnit.MILLISECONDS)) {
- throw new IllegalStateException("Waiting on callback timed out.");
- }
- return result;
- }
- }
-}
diff --git a/tests/src/com/android/dialer/database/FilteredNumberProviderTest.java b/tests/src/com/android/dialer/database/FilteredNumberProviderTest.java
deleted file mode 100644
index 1191560f5..000000000
--- a/tests/src/com/android/dialer/database/FilteredNumberProviderTest.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * 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.dialer.database;
-
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.net.Uri;
-import android.test.ProviderTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
-
-@MediumTest
-public class FilteredNumberProviderTest extends
- ProviderTestCase2<FilteredNumberProviderTest.TestFilteredNumberProvider> {
- private ContentResolver mResolver;
-
- private static final String TIME_ZONE_AMERICA_LOS_ANGELES = "America/Los_Angeles";
- private static final String DEFAULT_TIMEZONE = TIME_ZONE_AMERICA_LOS_ANGELES;
- private static final String DEFAULT_COUNTRY_ISO = "US";
- private static final String TEST_NUMBER = "234567890";
- private static final String TEST_NORMALIZED_NUMBER = "+1234567890";
- private static final long TEST_TIME = 1439936706;
-
- public FilteredNumberProviderTest () {
- super(TestFilteredNumberProvider.class, FilteredNumberContract.AUTHORITY);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mResolver = getMockContentResolver();
- }
-
- public void testInsert() {
- // Insert row
- Uri uri = mResolver.insert(
- FilteredNumberContract.FilteredNumber.CONTENT_URI,
- getTestValues(null));
- assertNotNull(uri);
- long id = ContentUris.parseId(uri);
- assertTrue(id > 0);
- }
-
- public void testQuery() {
- Cursor cursor = mResolver.query(
- FilteredNumberContract.FilteredNumber.CONTENT_URI, null, null, null, null);
- assertNotNull(cursor);
- assertEquals(cursor.getCount(), 0);
- cursor.close();
- }
-
- public void testInsertAndQuery() {
- // Insert row
- ContentValues testValues = getTestValues(null);
- Uri uri = mResolver.insert(FilteredNumberContract.FilteredNumber.CONTENT_URI, testValues);
-
- // Query
- Cursor cursor = mResolver.query(uri, null, null, null, null);
- assertNotNull(cursor);
- assertEquals(cursor.getCount(), 1);
-
- cursor.moveToFirst();
- assertCursorValues(cursor, testValues);
- cursor.close();
- }
-
- public void testIllegalUri() {
- try {
- mResolver.query(
- Uri.withAppendedPath(
- FilteredNumberContract.FilteredNumber.CONTENT_URI,
- "ILLEGAL"), null, null, null, null);
- fail("Expecting exception but none was thrown.");
- } catch (IllegalArgumentException e) {
- }
- }
-
- public void testQueryWithId() {
- // Insert row
- ContentValues testValues = getTestValues(null);
- Uri uri = mResolver.insert(FilteredNumberContract.FilteredNumber.CONTENT_URI, testValues);
- long id = ContentUris.parseId(uri);
-
- // Query
- Cursor cursor = mResolver.query(
- ContentUris.withAppendedId(
- FilteredNumberContract.FilteredNumber.CONTENT_URI,
- id), null, null, null, null);
- assertNotNull(cursor);
- assertEquals(cursor.getCount(), 1);
-
- cursor.moveToFirst();
- assertCursorValues(cursor, testValues);
- cursor.close();
- }
-
- public void testDelete() {
- // Insert row
- Uri uri = mResolver.insert(
- FilteredNumberContract.FilteredNumber.CONTENT_URI,
- getTestValues(null));
- long id = ContentUris.parseId(uri);
-
- // Delete row
- int rows = mResolver.delete(
- FilteredNumberContract.FilteredNumber.CONTENT_URI,
- FilteredNumberContract.FilteredNumberColumns._ID + " = ?",
- new String[]{Long.toString(id)});
- assertEquals(rows, 1);
-
- // Query
- Cursor cursor = mResolver.query(uri, null, null, null, null);
- assertNotNull(cursor);
- assertEquals(cursor.getCount(), 0);
- cursor.close();
- }
-
- public void testUpdate() {
- // Insert row
- Uri uri = mResolver.insert(
- FilteredNumberContract.FilteredNumber.CONTENT_URI,
- getTestValues(null));
-
- // Update row
- ContentValues v = new ContentValues();
- v.put(FilteredNumberContract.FilteredNumberColumns.TIMES_FILTERED, 3);
- v.put(FilteredNumberContract.FilteredNumberColumns.LAST_TIME_FILTERED, TEST_TIME);
- int rows = mResolver.update(FilteredNumberContract.FilteredNumber.CONTENT_URI, v,
- FilteredNumberContract.FilteredNumberColumns.NORMALIZED_NUMBER + " = ?",
- new String[]{TEST_NORMALIZED_NUMBER});
- assertEquals(rows, 1);
-
- ContentValues expected = getTestValues(TEST_TIME);
- expected.put(FilteredNumberContract.FilteredNumberColumns.TIMES_FILTERED, 3);
- expected.put(FilteredNumberContract.FilteredNumberColumns.LAST_TIME_FILTERED, TEST_TIME);
-
- // Re-query
- Cursor cursor = mResolver.query(uri, null, null, null, null);
- assertNotNull(cursor);
- assertEquals(cursor.getCount(), 1);
- cursor.moveToFirst();
- assertCursorValues(cursor, expected);
- cursor.close();
- }
-
- public void testInsertDefaultValues() {
- // Insert row
- ContentValues v = getTestValues(null);
- Uri uri = mResolver.insert(FilteredNumberContract.FilteredNumber.CONTENT_URI, v);
- assertNotNull(uri);
- long id = ContentUris.parseId(uri);
- assertTrue(id > 0);
-
- // Query
- Cursor cursor = mResolver.query(uri, null, null, null, null);
- assertNotNull(cursor);
- assertEquals(cursor.getCount(), 1);
-
- int creationTimeIndex =
- cursor.getColumnIndex(FilteredNumberContract.FilteredNumberColumns.CREATION_TIME);
- cursor.moveToFirst();
- assertEquals(cursor.getLong(creationTimeIndex), TEST_TIME);
- cursor.close();
- }
-
- @Override
- protected void tearDown() throws Exception {
- getProvider().closeDb();
- super.tearDown();
- }
-
- private ContentValues getTestValues(Long timeNow) {
- ContentValues v = new ContentValues();
- v.putNull(FilteredNumberContract.FilteredNumberColumns._ID);
- v.put(FilteredNumberContract.FilteredNumberColumns.NORMALIZED_NUMBER,
- TEST_NORMALIZED_NUMBER);
- v.put(FilteredNumberContract.FilteredNumberColumns.NUMBER, TEST_NUMBER);
- v.put(FilteredNumberContract.FilteredNumberColumns.COUNTRY_ISO, DEFAULT_COUNTRY_ISO);
- v.put(FilteredNumberContract.FilteredNumberColumns.TIMES_FILTERED, 0);
- v.putNull(FilteredNumberContract.FilteredNumberColumns.LAST_TIME_FILTERED);
- v.put(FilteredNumberContract.FilteredNumberColumns.CREATION_TIME, timeNow);
- v.put(FilteredNumberContract.FilteredNumberColumns.SOURCE, 1);
- v.put(FilteredNumberContract.FilteredNumberColumns.TYPE, 1);
- return v;
- }
-
- private void assertCursorValues(Cursor cursor, ContentValues expectedValues) {
- ContentValues v = new ContentValues();
- DatabaseUtils.cursorRowToContentValues(cursor, v);
- v.remove(FilteredNumberContract.FilteredNumberColumns._ID);
- expectedValues.remove(FilteredNumberContract.FilteredNumberColumns._ID);
- assertEquals(v.toString(), expectedValues.toString());
- }
-
- public static class TestFilteredNumberProvider extends FilteredNumberProvider {
- private DialerDatabaseHelper mDialerDatabaseHelper;
-
- @Override
- protected DialerDatabaseHelper getDatabaseHelper(Context context) {
- if (mDialerDatabaseHelper == null) {
- mDialerDatabaseHelper = DialerDatabaseHelper.getNewInstanceForTest(context);
- }
- return mDialerDatabaseHelper;
- }
-
- protected void closeDb() {
- mDialerDatabaseHelper.close();
- }
-
- @Override
- protected long getCurrentTimeMs() {
- return TEST_TIME;
- }
- }
-}
diff --git a/tests/src/com/android/dialer/database/SmartDialPrefixTest.java b/tests/src/com/android/dialer/database/SmartDialPrefixTest.java
deleted file mode 100644
index 78962e3f4..000000000
--- a/tests/src/com/android/dialer/database/SmartDialPrefixTest.java
+++ /dev/null
@@ -1,523 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.database;
-
-import static com.android.dialer.database.DatabaseTestUtils.*;
-
-import android.database.MatrixCursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.AndroidTestCase;
-
-import com.android.dialer.database.DialerDatabaseHelper;
-import com.android.dialer.database.DialerDatabaseHelper.ContactNumber;
-import com.android.dialer.dialpad.SmartDialNameMatcher;
-import com.android.dialer.dialpad.SmartDialPrefix;
-
-import java.lang.Exception;
-import java.lang.Override;
-import java.lang.String;
-import java.util.ArrayList;
-
-/**
- * To run this test, use the command:
- * adb shell am instrument -w -e class com.android.dialer.database.SmartDialPrefixTest /
- * com.android.dialer.tests/android.test.InstrumentationTestRunner
- */
-@SmallTest
-public class SmartDialPrefixTest extends AndroidTestCase {
-
- private DialerDatabaseHelper mTestHelper;
-
- public void testIsCountryNanp_CaseInsensitive() {
- assertFalse(SmartDialPrefix.isCountryNanp(null));
- assertFalse(SmartDialPrefix.isCountryNanp("CN"));
- assertFalse(SmartDialPrefix.isCountryNanp("HK"));
- assertFalse(SmartDialPrefix.isCountryNanp("uk"));
- assertFalse(SmartDialPrefix.isCountryNanp("sg"));
- assertTrue(SmartDialPrefix.isCountryNanp("US"));
- assertTrue(SmartDialPrefix.isCountryNanp("CA"));
- assertTrue(SmartDialPrefix.isCountryNanp("AS"));
- assertTrue(SmartDialPrefix.isCountryNanp("AI"));
- assertTrue(SmartDialPrefix.isCountryNanp("AG"));
- assertTrue(SmartDialPrefix.isCountryNanp("BS"));
- assertTrue(SmartDialPrefix.isCountryNanp("BB"));
- assertTrue(SmartDialPrefix.isCountryNanp("bm"));
- assertTrue(SmartDialPrefix.isCountryNanp("vg"));
- assertTrue(SmartDialPrefix.isCountryNanp("ky"));
- assertTrue(SmartDialPrefix.isCountryNanp("dm"));
- assertTrue(SmartDialPrefix.isCountryNanp("do"));
- assertTrue(SmartDialPrefix.isCountryNanp("gd"));
- assertTrue(SmartDialPrefix.isCountryNanp("gu"));
- assertTrue(SmartDialPrefix.isCountryNanp("jm"));
- assertTrue(SmartDialPrefix.isCountryNanp("pr"));
- assertTrue(SmartDialPrefix.isCountryNanp("ms"));
- assertTrue(SmartDialPrefix.isCountryNanp("mp"));
- assertTrue(SmartDialPrefix.isCountryNanp("kn"));
- assertTrue(SmartDialPrefix.isCountryNanp("lc"));
- assertTrue(SmartDialPrefix.isCountryNanp("vc"));
- assertTrue(SmartDialPrefix.isCountryNanp("tt"));
- assertTrue(SmartDialPrefix.isCountryNanp("tc"));
- assertTrue(SmartDialPrefix.isCountryNanp("vi"));
- }
-
- @Override
- protected void setUp() {
- mTestHelper = DialerDatabaseHelper.getNewInstanceForTest(getContext());
- }
-
- @Override
- protected void tearDown() throws Exception {
- final SQLiteDatabase db = mTestHelper.getWritableDatabase();
- mTestHelper.removeAllContacts(db);
- super.tearDown();
- }
-
- private ArrayList<ContactNumber> getLooseMatchesFromDb(String query) {
- final SmartDialNameMatcher nameMatcher = new SmartDialNameMatcher(query,
- SmartDialPrefix.getMap());
- return mTestHelper.getLooseMatches(query, nameMatcher);
- }
-
- public void testPutForFullName() {
- final SQLiteDatabase db = mTestHelper.getWritableDatabase();
-
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
- final ContactNumber jasonsmith = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "", 0, "Jason Smith");
- final ContactNumber jasonsmitt = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "", 1, "Jason Smitt");
- final ContactNumber alphabet = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "12345678", 2, "abc def ghi jkl mno pqrs tuv wxyz");
-
- mTestHelper.insertUpdatedContactsAndNumberPrefix(db, contactCursor, Long.valueOf(0));
- mTestHelper.insertNamePrefixes(db, nameCursor);
-
- nameCursor.close();
- contactCursor.close();
-
- final ArrayList<ContactNumber> result1 = getLooseMatchesFromDb("5276676484");
- assertFalse(result1.contains(jasonsmitt));
-
- final ArrayList<ContactNumber> result2 = getLooseMatchesFromDb("5276676488");
- assertFalse(result2.contains(jasonsmith));
- assertTrue(result2.contains(jasonsmitt));
-
- assertTrue(getLooseMatchesFromDb("22233344455566677778889999").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("33344455566677778889999").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("44455566677778889999").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("55566677778889999").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("66677778889999").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("77778889999").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("8889999").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("9999").contains(alphabet));
-
- // Makes sure the phone number is correctly added.
- assertTrue(getLooseMatchesFromDb("12345678").contains(alphabet));
- }
-
- public void testPutForPartialName() {
- final SQLiteDatabase db = mTestHelper.getWritableDatabase();
-
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
- final ContactNumber maryjane = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "", 0, "Mary Jane");
- final ContactNumber sarahsmith = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "", 1, "Sarah Smith");
- final ContactNumber jasonsmitt = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "", 2, "Jason Smitt");
-
- mTestHelper.insertUpdatedContactsAndNumberPrefix(db, contactCursor, Long.valueOf(0));
- mTestHelper.insertNamePrefixes(db, nameCursor);
-
- nameCursor.close();
- contactCursor.close();
-
- final ArrayList<ContactNumber> result1 = getLooseMatchesFromDb("6279");
- assertTrue(result1.contains(maryjane));
- assertFalse(result1.contains(jasonsmitt));
-
- // 72 corresponds to sa = "Sarah Smith" but not "Jason Smitt" or "Mary Jane"
- final ArrayList<ContactNumber> result2 = getLooseMatchesFromDb("72");
- assertFalse(result2.contains(maryjane));
- assertTrue(result2.contains(sarahsmith));
- assertFalse(result2.contains(jasonsmitt));
-
- // 76 corresponds to sm = "Sarah Smith" and "Jason Smitt" but not "Mary Jane"
- final ArrayList<ContactNumber> result3 = getLooseMatchesFromDb("76");
- assertFalse(result3.contains(maryjane));
- assertTrue(result3.contains(sarahsmith));
- assertTrue(result3.contains(jasonsmitt));
- }
-
- public void testPutForNameTokens() {
- final SQLiteDatabase db = mTestHelper.getWritableDatabase();
-
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
- final ContactNumber jasonfwilliams = constructNewContactWithDummyIds(contactCursor,
- nameCursor, "", 0, "Jason F. Williams");
-
- mTestHelper.insertUpdatedContactsAndNumberPrefix(db, contactCursor, Long.valueOf(0));
- mTestHelper.insertNamePrefixes(db, nameCursor);
-
- nameCursor.close();
- contactCursor.close();
-
- assertTrue(getLooseMatchesFromDb("527").contains(jasonfwilliams));
- // 72 corresponds to sa = "Sarah Smith" but not "Jason Smitt" or "Mary Jane"
- assertTrue(getLooseMatchesFromDb("945").contains(jasonfwilliams));
- // 76 corresponds to sm = "Sarah Smith" and "Jason Smitt" but not "Mary Jane"
- assertFalse(getLooseMatchesFromDb("66").contains(jasonfwilliams));
- }
-
- public void testPutForInitialMatches() {
- final SQLiteDatabase db = mTestHelper.getWritableDatabase();
-
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
- final ContactNumber martinjuniorharry = constructNewContactWithDummyIds(contactCursor,
- nameCursor, "", 0, "Martin Jr Harry");
-
- mTestHelper.insertUpdatedContactsAndNumberPrefix(db, contactCursor, Long.valueOf(0));
- mTestHelper.insertNamePrefixes(db, nameCursor);
-
- nameCursor.close();
- contactCursor.close();
-
- // 654 corresponds to mjh = "(M)artin (J)r (H)arry"
- assertTrue(getLooseMatchesFromDb("654").contains(martinjuniorharry));
- // The reverse (456) does not match (for now)
- assertFalse(getLooseMatchesFromDb("456").contains(martinjuniorharry));
- // 6542 corresponds to mjha = "(M)artin (J)r (Ha)rry"
- assertTrue(getLooseMatchesFromDb("6542").contains(martinjuniorharry));
- // 542 corresponds to jha = "Martin (J)r (Ha)rry"
- assertTrue(getLooseMatchesFromDb("542").contains(martinjuniorharry));
- // 642 corresponds to mha = "(M)artin Jr (Ha)rry"
- assertTrue(getLooseMatchesFromDb("642").contains(martinjuniorharry));
- // 6542779 (M)artin (J)r (Harry)
- assertTrue(getLooseMatchesFromDb("6542779").contains(martinjuniorharry));
- // 65742779 (M)artin (Jr) (Harry)
- assertTrue(getLooseMatchesFromDb("65742779").contains(martinjuniorharry));
- // 542779 Martin (J)r (Harry)
- assertTrue(getLooseMatchesFromDb("542779").contains(martinjuniorharry));
- // 547 doesn't match
- assertFalse(getLooseMatchesFromDb("547").contains(martinjuniorharry));
- // 655 doesn't match
- assertFalse(getLooseMatchesFromDb("655").contains(martinjuniorharry));
- // 653 doesn't match
- assertFalse(getLooseMatchesFromDb("653").contains(martinjuniorharry));
- // 6543 doesn't match
- assertFalse(getLooseMatchesFromDb("6543").contains(martinjuniorharry));
-
- // 7 actual rows, + 1 for the dummy number we added
- assertEquals(8, mTestHelper.countPrefixTableRows(db));
- }
-
- public void testPutForInitialMatchesForLongTokenNames() {
-
- final SQLiteDatabase db = mTestHelper.getWritableDatabase();
-
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
- final ContactNumber alphabet = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "12345678", 0, "abc def ghi jkl mno pqrs tuv wxyz");
-
- mTestHelper.insertUpdatedContactsAndNumberPrefix(db, contactCursor, Long.valueOf(0));
- mTestHelper.insertNamePrefixes(db, nameCursor);
-
- nameCursor.close();
- contactCursor.close();
-
- // Makes sure only only the first two and last two token are considered for initials.
- // The cut-off constant can be set in SmartDialPrefix.java
- assertTrue(getLooseMatchesFromDb("2389999").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("239999").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("23888").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("2333").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("289999").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("2888").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("29999").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("3888").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("39999").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("389999").contains(alphabet));
- assertTrue(getLooseMatchesFromDb("89999").contains(alphabet));
- }
-
- public void testCheckLongToken() {
- final SQLiteDatabase db = mTestHelper.getWritableDatabase();
-
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
- final ContactNumber alphabet = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "1", 0, " aaaa bbbb cccc dddd eeee ffff gggg hhhh iiii jjjj kkkk llll mmmm nnnn" +
- " oooo pppp qqqq rrrr ssss tttt uuuu vvvv wwww xxxx yyyy zzzz");
-
- final ContactNumber alphabet2 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "1", 1, "aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkkllllmmmmnnnnooooppppqqqqrrrr" +
- "ssssttttuuuuvvvvwwwwxxxxyyyyzzzz");
-
- mTestHelper.insertUpdatedContactsAndNumberPrefix(db, contactCursor, Long.valueOf(0));
- mTestHelper.insertNamePrefixes(db, nameCursor);
-
- nameCursor.close();
- contactCursor.close();
-
- assertTrue(getLooseMatchesFromDb("2222").contains(alphabet));
- assertEquals(40, mTestHelper.countPrefixTableRows(db));
- }
-
- public void testAccentedCharacters() {
- final SQLiteDatabase db = mTestHelper.getWritableDatabase();
-
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
- final ContactNumber reene = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "0", 0, "Reenée");
- final ContactNumber bronte = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "0", 1, "Brontë");
-
- mTestHelper.insertUpdatedContactsAndNumberPrefix(db, contactCursor, Long.valueOf(0));
- mTestHelper.insertNamePrefixes(db, nameCursor);
-
- nameCursor.close();
- contactCursor.close();
-
- assertTrue(getLooseMatchesFromDb("733633").contains(reene));
- assertTrue(getLooseMatchesFromDb("276683").contains(bronte));
- }
-
- public void testNumbersInName() {
- final SQLiteDatabase db = mTestHelper.getWritableDatabase();
-
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
- final ContactNumber contact = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "0", 0, "12345678");
- final ContactNumber teacher = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "0", 1, "1st Grade Teacher");
-
- mTestHelper.insertUpdatedContactsAndNumberPrefix(db, contactCursor, Long.valueOf(0));
- mTestHelper.insertNamePrefixes(db, nameCursor);
-
- nameCursor.close();
- contactCursor.close();
-
- assertTrue(getLooseMatchesFromDb("12345678").contains(contact));
- assertTrue(getLooseMatchesFromDb("17847233").contains(teacher));
- assertTrue(getLooseMatchesFromDb("14832").contains(teacher));
- }
-
- public void testPutForNumbers() {
- final SQLiteDatabase db = mTestHelper.getWritableDatabase();
-
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
- final ContactNumber contactno1 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "510-527-2357", 0, "James");
- final ContactNumber contactno2 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "77212862357", 1, "James");
- final ContactNumber contactno3 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "+13684976334", 2, "James");
-
- mTestHelper.insertUpdatedContactsAndNumberPrefix(db, contactCursor, Long.valueOf(0));
- mTestHelper.insertNamePrefixes(db, nameCursor);
-
- nameCursor.close();
- contactCursor.close();
-
- assertTrue(getLooseMatchesFromDb("510").contains(contactno1));
- assertFalse(getLooseMatchesFromDb("511").contains(contactno1));
- assertTrue(getLooseMatchesFromDb("77212862357").contains(contactno2));
- assertFalse(getLooseMatchesFromDb("77212862356").contains(contactno2));
- assertTrue(getLooseMatchesFromDb("1368").contains(contactno3));
- assertFalse(getLooseMatchesFromDb("1367").contains(contactno3));
- }
-
- public void testPutNumbersCountryCode() {
- final SQLiteDatabase db = mTestHelper.getWritableDatabase();
-
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
- final ContactNumber contactno1 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "+13684976334", 0, "James");
- final ContactNumber contactno2 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "+65 9177-6930", 1, "Jason");
- final ContactNumber contactno3 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "+85212345678", 2, "Mike");
- final ContactNumber contactno4 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "+85112345678", 3, "Invalid");
- final ContactNumber contactno5 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "+852", 4, "Invalid");
-
- mTestHelper.insertUpdatedContactsAndNumberPrefix(db, contactCursor, Long.valueOf(0));
- mTestHelper.insertNamePrefixes(db, nameCursor);
-
- nameCursor.close();
- contactCursor.close();
-
- assertTrue(getLooseMatchesFromDb("1368").contains(contactno1));
- assertTrue(getLooseMatchesFromDb("368497").contains(contactno1));
- assertFalse(getLooseMatchesFromDb("2368497").contains(contactno1));
-
- assertTrue(getLooseMatchesFromDb("6591776930").contains(contactno2));
- assertTrue(getLooseMatchesFromDb("91776930").contains(contactno2));
- assertFalse(getLooseMatchesFromDb("591776930").contains(contactno2));
-
- assertTrue(getLooseMatchesFromDb("85212345678").contains(contactno3));
- assertTrue(getLooseMatchesFromDb("12345678").contains(contactno3));
- assertFalse(getLooseMatchesFromDb("5212345678").contains(contactno3));
-
- assertTrue(getLooseMatchesFromDb("85112345678").contains(contactno4));
- assertFalse(getLooseMatchesFromDb("12345678").contains(contactno4));
- }
-
- // Tests special case handling for NANP numbers
- public void testPutNumbersNANP() {
- SmartDialPrefix.setUserInNanpRegion(true);
- final SQLiteDatabase db = mTestHelper.getWritableDatabase();
-
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
- final ContactNumber contactno1 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "16503337596", 0, "James");
- final ContactNumber contactno2 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "5109921234", 1, "Michael");
- final ContactNumber contactno3 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "(415)-123-4567", 2, "Jason");
- final ContactNumber contactno4 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "1 510-284-9170", 3, "Mike");
- final ContactNumber contactno5 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "1-415-123-123", 4, "Invalid");
- final ContactNumber contactno6 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "415-123-123", 5, "Invalid2");
- final ContactNumber contactno7 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "+1-510-284-9170", 6, "Mike");
- final ContactNumber contactno8 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "+1-510-284-917", 7, "Invalid");
- final ContactNumber contactno9 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "+857-510-284-9170", 8, "Inv");
-
- mTestHelper.insertUpdatedContactsAndNumberPrefix(db, contactCursor, Long.valueOf(0));
- mTestHelper.insertNamePrefixes(db, nameCursor);
-
- nameCursor.close();
- contactCursor.close();
-
- assertTrue(getLooseMatchesFromDb("16503337596").contains(contactno1));
- assertTrue(getLooseMatchesFromDb("6503337596").contains(contactno1));
- assertTrue(getLooseMatchesFromDb("3337596").contains(contactno1));
-
- assertTrue(getLooseMatchesFromDb("5109921234").contains(contactno2));
- assertTrue(getLooseMatchesFromDb("9921234").contains(contactno2));
-
- assertTrue(getLooseMatchesFromDb("4151234567").contains(contactno3));
- assertTrue(getLooseMatchesFromDb("1234567").contains(contactno3));
-
- assertTrue(getLooseMatchesFromDb("15102849170").contains(contactno4));
- assertTrue(getLooseMatchesFromDb("5102849170").contains(contactno4));
- assertTrue(getLooseMatchesFromDb("2849170").contains(contactno4));
-
- assertTrue(getLooseMatchesFromDb("1415123123").contains(contactno5));
- assertFalse(getLooseMatchesFromDb("415123123").contains(contactno5));
- assertFalse(getLooseMatchesFromDb("123123").contains(contactno5));
-
- assertTrue(getLooseMatchesFromDb("415123123").contains(contactno6));
- assertFalse(getLooseMatchesFromDb("123123").contains(contactno6));
-
- assertTrue(getLooseMatchesFromDb("15102849170").contains(contactno7));
- assertTrue(getLooseMatchesFromDb("5102849170").contains(contactno7));
- assertTrue(getLooseMatchesFromDb("2849170").contains(contactno7));
- assertFalse(getLooseMatchesFromDb("849170").contains(contactno7));
- assertFalse(getLooseMatchesFromDb("10849170").contains(contactno7));
-
- assertTrue(getLooseMatchesFromDb("1510284917").contains(contactno8));
- assertTrue(getLooseMatchesFromDb("510284917").contains(contactno8));
- assertFalse(getLooseMatchesFromDb("2849170").contains(contactno8));
-
- assertTrue(getLooseMatchesFromDb("8575102849170").contains(contactno9));
- assertFalse(getLooseMatchesFromDb("5102849170").contains(contactno9));
- assertFalse(getLooseMatchesFromDb("2849170").contains(contactno9));
-
- // TODO(klp) Adds test for non-NANP region number matchings.
- }
-
- // Tests special case handling for non-NANP numbers
- public void testPutNumbersNonNANP() {
- SmartDialPrefix.setUserInNanpRegion(false);
- final SQLiteDatabase db = mTestHelper.getWritableDatabase();
-
- final MatrixCursor nameCursor = constructNewNameCursor();
- final MatrixCursor contactCursor = constructNewContactCursor();
-
- final ContactNumber contactno0 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "(415)-123-4567", 0, "Jason");
- final ContactNumber contactno1 = constructNewContactWithDummyIds(contactCursor, nameCursor,
- "1 510-284-9170", 1, "Mike");
-
-
- mTestHelper.insertUpdatedContactsAndNumberPrefix(db, contactCursor, Long.valueOf(0));
- mTestHelper.insertNamePrefixes(db, nameCursor);
-
- nameCursor.close();
- contactCursor.close();
-
- assertTrue(getLooseMatchesFromDb("4151234567").contains(contactno0));
- assertFalse(getLooseMatchesFromDb("1234567").contains(contactno0));
-
- assertTrue(getLooseMatchesFromDb("15102849170").contains(contactno1));
- assertFalse(getLooseMatchesFromDb("5102849170").contains(contactno1));
- assertFalse(getLooseMatchesFromDb("2849170").contains(contactno1));
- }
-
- public void testParseInfo() {
- final String name = "Mcdonald Jamie-Cullum";
- final ArrayList<String> info = SmartDialPrefix.parseToIndexTokens(name);
- assertEquals(3, info.size());
- assertEquals(8, info.get(0).length());
- assertEquals(5, info.get(1).length());
- assertEquals(6, info.get(2).length());
-
- final String name2 = "aaa bbb ccc ddd eee fff ggg hhh iii jjj kkk";
- final ArrayList<String> info2 = SmartDialPrefix.parseToIndexTokens(name2);
- assertEquals(11, info2.size());
- assertEquals(3, info2.get(0).length());
- assertEquals(3, info2.get(10).length());
-
- final String name3 = "this is- a,test name";
- final ArrayList<String> info3 = SmartDialPrefix.parseToIndexTokens(name3);
- assertEquals(5, info3.size());
- assertEquals(2, info3.get(1).length());
- assertEquals(1, info3.get(2).length());
- assertEquals(4, info3.get(3).length());
- assertEquals(4, info3.get(4).length());
-
- final String name4 = "M c-Donald James";
- final ArrayList<String> info4 = SmartDialPrefix.parseToIndexTokens(name4);
- assertEquals(4, info4.size());
- assertEquals(1, info4.get(1).length());
- assertEquals(6, info4.get(2).length());
-
- final String name5 = " Aa'Bb c dddd e'e";
- final ArrayList<String> info5 = SmartDialPrefix.parseToIndexTokens(name5);
- assertEquals(6, info5.size());
- assertEquals(2, info5.get(0).length());
- assertEquals(1, info5.get(5).length());
- }
-}
diff --git a/tests/src/com/android/dialer/database/VoicemailArchiveProviderTest.java b/tests/src/com/android/dialer/database/VoicemailArchiveProviderTest.java
deleted file mode 100644
index abc2dc5b0..000000000
--- a/tests/src/com/android/dialer/database/VoicemailArchiveProviderTest.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * 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.dialer.database;
-
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.test.ProviderTestCase2;
-import android.test.mock.MockContentResolver;
-import com.android.dialer.database.VoicemailArchiveContract.VoicemailArchive;
-
-import java.io.File;
-import java.io.OutputStream;
-
-/**
- * Tests for {@link VoicemailArchiveProvider}.
- */
-public class VoicemailArchiveProviderTest extends
- ProviderTestCase2<VoicemailArchiveProviderTest.TestVoicemailArchiveProvider> {
- private static final String TEST_MIME_TYPE = "audio/mp3";
- private static final String TEST_NUMBER = "+1412555555";
- private static final int TEST_ARCHIVED = 1;
- private static final String TEST_STRING = "TEST";
-
- private MockContentResolver mResolver;
- private Cursor mCursor;
- private Uri mVoicemailUri;
-
- public VoicemailArchiveProviderTest() {
- super(TestVoicemailArchiveProvider.class, VoicemailArchiveContract.AUTHORITY);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mResolver = getMockContentResolver();
- }
-
- @Override
- protected void tearDown() throws Exception {
- if (mCursor != null) {
- mCursor.close();
- }
- // Need to delete test.cache folder created by {@link android.test.ProviderTestCase2}
- deleteRecursive(getContext().getCacheDir());
- getProvider().closeDb();
- super.tearDown();
- }
-
- public void testInsert() {
- insertVoicemail();
- assertNotNull(mVoicemailUri);
- assertTrue(doesRowExist());
- long id = ContentUris.parseId(mVoicemailUri);
- assertTrue(id > 0);
- }
-
- public void testQuery_createFile() throws Exception {
- insertVoicemail();
- assertTrue(doesRowExist());
- assertFalse(doesFileExist());
- createFile();
- assertTrue(doesFileExist());
- }
-
- public void testQuery_createFileMimeTypeIsAMR() throws Exception {
- insertVoicemailWithMimeType("audio/amr");
- assertTrue(doesRowExist());
- assertFalse(doesFileExist());
- createFile();
- assertTrue(doesFileExist());
- assertEquals("amr", getFileExtension(getFilePath()));
- }
-
- public void testQuery_createFileMimeTypeIsMP3() throws Exception {
- insertVoicemailWithMimeType("audio/mpeg");
- assertTrue(doesRowExist());
- assertFalse(doesFileExist());
- createFile();
- assertTrue(doesFileExist());
- assertEquals("mp3", getFileExtension(getFilePath()));
- }
-
- public void testQuery_createFileMimeTypeNotExists() throws Exception {
- insertVoicemailWithMimeType(TEST_STRING);
- assertTrue(doesRowExist());
- assertFalse(doesFileExist());
- createFile();
- assertTrue(doesFileExist());
- assertEquals("", getFileExtension(getFilePath()));
- }
-
- public void testQuery() {
- insertVoicemail();
- updateCursor();
- assertCursorCount(1);
- assertContentValues();
- }
-
- public void testQuery_correctValuesSelection() {
- insertVoicemail();
- updateCursorWithIdQuery();
- assertCursorCount(1);
- assertContentValues();
- }
-
- public void testQuery_illegalUri() {
- try {
- mResolver.query(Uri.withAppendedPath(VoicemailArchive.CONTENT_URI, TEST_STRING),
- null, null, null, null);
- fail("Expecting exception but none was thrown.");
- } catch (IllegalArgumentException e) {}
- }
-
- public void testUpdate() throws Exception {
- insertVoicemail();
- assertTrue(doesRowExist());
- ContentValues values = new ContentValues();
- values.put(VoicemailArchive.MIME_TYPE, TEST_STRING);
- values.put(VoicemailArchive.NUMBER, TEST_STRING);
- values.put(VoicemailArchive.ARCHIVED, false);
- mResolver.update(mVoicemailUri, values, null, null);
- updateCursor();
- assertEquals(TEST_STRING, mCursor.getString(mCursor.getColumnIndex(VoicemailArchive.MIME_TYPE)));
- assertEquals(TEST_STRING, mCursor.getString(mCursor.getColumnIndex(VoicemailArchive.NUMBER)));
- assertEquals(false, mCursor.getInt(
- mCursor.getColumnIndex(VoicemailArchive.ARCHIVED)) == 1);
- }
-
- public void testUpdate_selection() throws Exception {
- insertVoicemail();
- assertTrue(doesRowExist());
- ContentValues values = new ContentValues();
- values.put(VoicemailArchive.MIME_TYPE, TEST_STRING);
- values.put(VoicemailArchive.NUMBER, TEST_STRING);
- values.put(VoicemailArchive.ARCHIVED, false);
- mResolver.update(VoicemailArchive.CONTENT_URI, values, getIdQuery(), null);
- updateCursor();
- assertEquals(TEST_STRING, mCursor.getString(mCursor.getColumnIndex(VoicemailArchive.MIME_TYPE)));
- assertEquals(TEST_STRING, mCursor.getString(mCursor.getColumnIndex(VoicemailArchive.NUMBER)));
- assertEquals(false, mCursor.getInt(
- mCursor.getColumnIndex(VoicemailArchive.ARCHIVED)) == 1);
- }
-
- public void testUpdate_illegalUri() {
- try {
- mResolver.update(Uri.withAppendedPath(VoicemailArchive.CONTENT_URI, TEST_STRING),
- null, null, null);
- fail("Expecting exception but none was thrown.");
- } catch (IllegalArgumentException e) {}
- }
-
- public void testDelete() throws Exception {
- insertVoicemail();
- createFile();
- assertTrue(doesRowExist());
- assertTrue(doesFileExist());
- mResolver.delete(mVoicemailUri, null, null);
- assertFalse(doesRowExist());
- assertFalse(doesFileExist());
- }
-
- public void testDelete_selection() throws Exception{
- insertVoicemail();
- createFile();
- assertTrue(doesRowExist());
- assertTrue(doesFileExist());
- mResolver.delete(VoicemailArchive.CONTENT_URI, getIdQuery(), null);
- assertFalse(doesRowExist());
- assertFalse(doesFileExist());
- }
-
- public void testDelete_illegalUri() {
- try {
- mResolver.delete(Uri.withAppendedPath(VoicemailArchive.CONTENT_URI, TEST_STRING),
- null, null);
- fail("Expecting exception but none was thrown.");
- } catch (IllegalArgumentException e) {}
- }
-
- private boolean doesRowExist() {
- Cursor tempCursor = mResolver.query(mVoicemailUri, null, null, null, null);
- boolean rowExists = tempCursor != null && tempCursor.getCount() > 0;
- tempCursor.close();
- return rowExists;
- }
-
- private boolean doesFileExist() {
- File voicemailFile = new File(getFilePath());
- return voicemailFile.exists();
- }
-
- private static String getFileExtension(String filePath) {
- File file = new File(filePath);
- String fileName = file.getName();
- int index = fileName.lastIndexOf(".");
- return index > 0 ? fileName.substring(index + 1) : "";
- }
-
- private void assertCursorCount(int count) {
- assertEquals(count, mCursor.getCount());
- }
-
- private void assertContentValues() {
- assertEquals(TEST_MIME_TYPE, mCursor
- .getString(mCursor.getColumnIndex(VoicemailArchive.MIME_TYPE)));
- assertEquals(TEST_NUMBER, mCursor
- .getString(mCursor.getColumnIndex(VoicemailArchive.NUMBER)));
- assertEquals(TEST_ARCHIVED, mCursor
- .getInt(mCursor.getColumnIndex(VoicemailArchive.ARCHIVED)));
- }
-
- private void insertVoicemail() {
- mVoicemailUri = mResolver.insert(VoicemailArchive.CONTENT_URI, getTestValues());
- }
-
- private void insertVoicemailWithMimeType(String mimeType) {
- ContentValues values = getTestValues();
- values.put(VoicemailArchive.MIME_TYPE, mimeType);
- mVoicemailUri = mResolver.insert(VoicemailArchive.CONTENT_URI, values);
- }
-
- private void updateCursor() {
- mCursor = mResolver.query(mVoicemailUri, null, null, null, null);
- assertEquals(true, mCursor.getCount() > 0);
- mCursor.moveToFirst();
- }
-
- private void updateCursorWithIdQuery() {
- mCursor = mResolver.query(mVoicemailUri, null, getIdQuery(), null, null);
- assertEquals(true, mCursor.getCount() > 0);
- mCursor.moveToFirst();
- }
-
- private void createFile() throws Exception {
- assertFalse(doesFileExist());
- // Opening output stream and closing it should create the file
- OutputStream outputStream = mResolver.openOutputStream(mVoicemailUri);
- outputStream.close();
- }
-
- private String getIdQuery() {
- return VoicemailArchive._ID + "=" + ContentUris.parseId(mVoicemailUri);
- }
-
- private String getFilePath() {
- if (mCursor == null) {
- updateCursor();
- }
- return mCursor.getString(mCursor.getColumnIndex(VoicemailArchive._DATA));
- }
-
- private static ContentValues getTestValues() {
- ContentValues values = new ContentValues();
- values.put(VoicemailArchive.NUMBER, TEST_NUMBER);
- values.put(VoicemailArchive.MIME_TYPE, TEST_MIME_TYPE);
- values.put(VoicemailArchive.ARCHIVED, TEST_ARCHIVED);
- return values;
- }
-
- private void deleteRecursive(File fileOrDirectory) {
- if (fileOrDirectory.isDirectory()) {
- for (File child : fileOrDirectory.listFiles()) {
- deleteRecursive(child);
- }
- }
- fileOrDirectory.delete();
- }
-
- public static class TestVoicemailArchiveProvider extends VoicemailArchiveProvider {
- private DialerDatabaseHelper mDialerDatabaseHelper;
-
- @Override
- protected File getFilesDir() {
- return getContext().getCacheDir();
- }
-
- @Override
- protected DialerDatabaseHelper getDatabaseHelper(Context context) {
- if (mDialerDatabaseHelper == null) {
- mDialerDatabaseHelper = DialerDatabaseHelper.getNewInstanceForTest(context);
- }
- return mDialerDatabaseHelper;
- }
-
- protected void closeDb() {
- mDialerDatabaseHelper.close();
- }
- }
-}
diff --git a/tests/src/com/android/dialer/dialpad/DialpadFragmentInstrumentationTest.java b/tests/src/com/android/dialer/dialpad/DialpadFragmentInstrumentationTest.java
deleted file mode 100644
index 876c13ba1..000000000
--- a/tests/src/com/android/dialer/dialpad/DialpadFragmentInstrumentationTest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-package com.android.dialer.dialpad;
-
-import android.test.ActivityInstrumentationTestCase2;
-import android.view.View;
-
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.R;
-
-/**
- * Tests that rely on instrumenting an actual instance of a {@link DialpadFragment}.
- */
-public class DialpadFragmentInstrumentationTest extends
- ActivityInstrumentationTestCase2<DialtactsActivity> {
- private DialtactsActivity mActivity;
-
- public DialpadFragmentInstrumentationTest() {
- super(DialtactsActivity.class);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mActivity = getActivity();
- }
-
- /**
- * Simulates a long click event on the zero key with a prior onPressed callback.
- *
- */
- public void testManualLongClickZero_DeletesPreviousCharacter() {
- final DialpadFragment fragment = showDialpad();
- pressAndReleaseKey(9, fragment);
-
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- final View zeroKey = findViewByDigit(0, fragment);
- fragment.onPressed(zeroKey, true);
- fragment.onLongClick(zeroKey);
- }
- });
-
- assertEquals("9+", fragment.getDigitsWidget().getText().toString());
- }
-
- /**
- * Simulates a long click event on the zero key without a prior onPressed
- * callback.
- */
- public void testSystemLongClickZero_PreservesPreviousCharacter() {
- final DialpadFragment fragment = showDialpad();
- pressAndReleaseKey(9, fragment);
-
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- final View zeroKey = findViewByDigit(0, fragment);
- fragment.onLongClick(zeroKey);
- }
- });
-
- assertEquals("9+", fragment.getDigitsWidget().getText().toString());
- }
-
- private DialpadFragment showDialpad() {
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mActivity.showDialpad();
- }
- });
- getInstrumentation().waitForIdleSync();
- return (DialpadFragment) mActivity.getFragmentManager().findFragmentByTag(
- DialtactsActivity.TAG_DIALPAD_FRAGMENT);
- }
-
- private void pressAndReleaseKey(int digit, final DialpadFragment fragment) {
- final View dialpadKey = findViewByDigit(digit, fragment);
- final String digitsBefore = fragment.getDigitsWidget().getText().toString();
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- fragment.onPressed(dialpadKey, true);
- fragment.onPressed(dialpadKey, false);
- }
- });
- assertEquals(digitsBefore + String.valueOf(digit),
- fragment.getDigitsWidget().getText().toString());
- }
-
- private View findViewByDigit(int digit, DialpadFragment fragment) {
- return fragment.getView().findViewById(getViewIdByDigit(digit));
- }
-
- private int getViewIdByDigit(int digit) {
- switch (digit) {
- case 0:
- return R.id.zero;
- case 1:
- return R.id.one;
- case 2:
- return R.id.two;
- case 3:
- return R.id.three;
- case 4:
- return R.id.four;
- case 5:
- return R.id.five;
- case 6:
- return R.id.six;
- case 7:
- return R.id.seven;
- case 8:
- return R.id.eight;
- case 9:
- return R.id.nine;
- default:
- return 0;
- }
- }
-}
diff --git a/tests/src/com/android/dialer/dialpad/DialpadFragmentTest.java b/tests/src/com/android/dialer/dialpad/DialpadFragmentTest.java
deleted file mode 100644
index 3015e87ff..000000000
--- a/tests/src/com/android/dialer/dialpad/DialpadFragmentTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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.dialer.dialpad;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/** Unit tests for {@link DialpadFragment}. */
-@SmallTest
-public class DialpadFragmentTest extends TestCase {
-
- public void testCanAddDigit_Valid() {
- // end, middle, selection to end, middle selection
- assertTrue(DialpadFragment.canAddDigit("123", 3, 3, ';'));
- assertTrue(DialpadFragment.canAddDigit("123", 1, 1, ','));
- assertTrue(DialpadFragment.canAddDigit("123", 1, 3, ';'));
- assertTrue(DialpadFragment.canAddDigit("123", 1, 2, ','));
- }
-
- public void testCanAddDigit_InvalidCharacter() {
- // only handles wait/pause
- try {
- DialpadFragment.canAddDigit("123", 1, 1, '5');
- fail("Calling canAddDigit with invalid character should throw an exception");
- } catch (IllegalArgumentException e) {
- }
- }
-
- public void testCanAddDigit_BadOrNoSelection() {
- // no selection
- assertFalse(DialpadFragment.canAddDigit("123", -1, -1, ';'));
- assertFalse(DialpadFragment.canAddDigit("123", -1, 1, ','));
-
- // start > end
- assertFalse(DialpadFragment.canAddDigit("123", 2, 1, ','));
- }
-
- public void testCanAddDigit_OutOfBounds() {
- // start or end is > digits.length()
- assertFalse(DialpadFragment.canAddDigit("123", 1, 4, ';'));
- assertFalse(DialpadFragment.canAddDigit("123", 4, 4, ','));
- }
-
- public void testCanAddDigit_AsFirstCharacter() {
- assertFalse(DialpadFragment.canAddDigit("", 0, 0, ','));
- assertFalse(DialpadFragment.canAddDigit("123", 0, 0, ';'));
- assertFalse(DialpadFragment.canAddDigit("123", 0, 2, ','));
- assertFalse(DialpadFragment.canAddDigit("123", 0, 3, ','));
- }
-
- public void testCanAddDigit_AdjacentCharacters_Before() {
- // before
- assertFalse(DialpadFragment.canAddDigit("55;55", 2, 2, ';')); // WAIT
- assertFalse(DialpadFragment.canAddDigit("55;55", 1, 2, ';'));
- assertTrue(DialpadFragment.canAddDigit("55,55", 2, 2, ',')); // PAUSE
- assertTrue(DialpadFragment.canAddDigit("55,55", 1, 2, ','));
- assertTrue(DialpadFragment.canAddDigit("55;55", 2, 2, ',')); // WAIT & PAUSE
- assertTrue(DialpadFragment.canAddDigit("55,55", 1, 2, ';'));
- }
-
- public void testCanAddDigit_AdjacentCharacters_After() {
- // after
- assertFalse(DialpadFragment.canAddDigit("55;55", 3, 3, ';')); // WAIT
- assertFalse(DialpadFragment.canAddDigit("55;55", 3, 4, ';'));
- assertTrue(DialpadFragment.canAddDigit("55,55", 3, 3, ',')); // PAUSE
- assertTrue(DialpadFragment.canAddDigit("55,55", 3, 4, ','));
- assertTrue(DialpadFragment.canAddDigit("55;55", 3, 3, ',')); // WAIT & PAUSE
- assertTrue(DialpadFragment.canAddDigit("55,55", 3, 4, ';'));
- }
-
- public void testGetFormattedDigits_NoPostDialString() {
- assertEquals("(510) 333-7596",
- DialpadFragment.getFormattedDigits("5103337596", null, "US"));
- assertEquals("(510) 333-7596",
- DialpadFragment.getFormattedDigits("5103337596", "+15103337596", "US"));
- }
-
- public void testGetFormattedDigits_WithPostDialString() {
- assertEquals("(510) 333-7596,1234",
- DialpadFragment.getFormattedDigits("5103337596,1234", null, "US"));
- assertEquals("(510) 333-7596;;1234",
- DialpadFragment.getFormattedDigits("5103337596;;1234", null, "US"));
- assertEquals("(510) 333-7596;123,,4",
- DialpadFragment.getFormattedDigits("(510)3337596;123,,4", "+15103337596", "US"));
- }
-
- public void testGetFormattedDigits_PostDialStringOnly() {
- assertEquals(",1234567", DialpadFragment.getFormattedDigits(",1234567", null, "US"));
- assertEquals(";4321", DialpadFragment.getFormattedDigits(";4321", null, "US"));
- }
-
- public void testGetFormattedDigits_Invalid() {
- assertEquals(null, DialpadFragment.getFormattedDigits(null, null, "US"));
- assertEquals("", DialpadFragment.getFormattedDigits("", "+15104233335", "US"));
- }
-}
diff --git a/tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java b/tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java
deleted file mode 100644
index c1365f5cf..000000000
--- a/tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * 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.dialer.dialpad;
-
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
-import android.util.Log;
-import android.test.AndroidTestCase;
-
-import com.android.dialer.dialpad.SmartDialNameMatcher;
-import com.android.dialer.dialpad.SmartDialPrefix;
-
-import java.text.Normalizer;
-import java.util.ArrayList;
-
-import junit.framework.TestCase;
-
-@SmallTest
-public class SmartDialNameMatcherTest extends TestCase {
- private static final String TAG = "SmartDialNameMatcherTest";
-
- public void testMatches() {
- // Test to ensure that all alphabetic characters are covered
- checkMatches("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
- "22233344455566677778889999" + "22233344455566677778889999", true, 0, 26 * 2);
- // Should fail because of a mistyped 2 instead of 9 in the second last character
- checkMatches("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
- "22233344455566677778889999" + "22233344455566677778889929", false, 0, 0);
-
- // Basic name test
- checkMatches("joe", "5", true, 0, 1);
- checkMatches("joe", "56", true, 0, 2);
- checkMatches("joe", "563", true, 0, 3);
-
- // Matches only word boundary.
- checkMatches("joe", "63", false, 0, 0);
- checkMatches("joe oe", "63", true, 4, 6);
-
- // Test for a match across word boundaries
- checkMatches("joe oe", "56363", true, 0, 6);
- }
-
- public void testMatches_repeatedLetters() {
- checkMatches("aaaaaaaaaa", "2222222222", true, 0, 10);
- // Fails because of one extra 2
- checkMatches("aaaaaaaaaa", "22222222222", false, 0, 0);
- checkMatches("zzzzzzzzzz zzzzzzzzzz", "99999999999999999999", true, 0, 21);
- }
-
- public void testMatches_repeatedSpaces() {
- checkMatches("William J Smith", "9455426576", true, 0, 17);
- checkMatches("William J Smith", "576", true, 12, 17);
- // Fails because we start at non-word boundary
- checkMatches("William J Smith", "6576", false, 0, 0);
- }
-
-
- public void testMatches_Initial() {
- // wjs matches (W)illiam (J)ohn (S)mith
- checkMatches("William John Smith", "957", true, 0, 1, 8, 9, 13, 14);
- // wjsmit matches (W)illiam (J)ohn (Smit)h
- checkMatches("William John Smith", "957648", true, 0, 1, 8, 9, 13, 17);
- // wjohn matches (W)illiam (John) Smith
- checkMatches("William John Smith", "95646", true, 0, 1, 8, 12);
- // jsmi matches William (J)ohn (Smi)th
- checkMatches("William John Smith", "5764", true, 8, 9, 13, 16);
- // make sure multiple spaces don't mess things up
- checkMatches("William John Smith", "5764", true, 15, 16, 22, 25);
- }
-
- public void testMatches_InitialWithSeparator() {
- // wjs matches (W)illiam (J)ohn (S)mith
- checkMatches("William John-Smith", "957", true, 0, 1, 8, 9, 13, 14);
- // wjsmit matches (W)illiam (J)ohn-(OShe)a
- checkMatches("William John-O'Shea", "956743", true, 0, 1, 8, 9, 13, 18);
- // wjohn matches (W)illiam-(John) Smith
- checkMatches("William-John Smith", "95646", true, 0, 1, 8, 12);
- // jsmi matches William (J)ohn-(Smi)th
- checkMatches("William John-Smith", "5764", true, 8, 9, 13, 16);
- // wsmi matches (W)illiam John (Smi)th
- checkMatches("William John-Smith", "9764", true, 0, 1, 13, 16);
- // make sure multiple spaces don't mess things up
- checkMatches("William John---Smith", "5764", true, 15, 16, 22, 25);
- // match tokens that are located directly after a non-space separator (studio)
- checkMatches("Berkeley Hair-Studio", "788346", true, 14, 20);
- // match tokens with same initials
- checkMatches("H.Harold", "427653", true, 2, 8);
- // various matching combinations of tokens with similar initials
- checkMatches("Yo-Yoghurt Land", "964487", true, 3, 9);
- checkMatches("Yo-Yoghurt Land", "96448785263", true, 3, 15);
- checkMatches("Yo-Yoghurt Land", "95263", true, 3, 4, 11, 15);
- checkMatches("Yo-Yoghurt Land", "995263", true, 0, 1, 3, 4, 11, 15);
-
- checkMatches("ab zz ef", "23", true, 0, 1, 6, 7);
- }
-
- public void testMatches_repeatedSeparators() {
- // Simple match for single token
- checkMatches("John,,,,,Doe", "5646", true, 0, 4);
- // Match across tokens
- checkMatches("John,,,,,Doe", "56463", true, 0, 10);
- // Match token after chain of separators
- checkMatches("John,,,,,Doe", "363", true, 9, 12);
- }
-
- public void testMatches_LatinMix() {
- // Latin + Chinese characters
- checkMatches("Lee王力Wang宏", "59264", true, 0, 1, 5, 9);
- // Latin + Japanese characters
- checkMatches("千Abcd佳智Efgh佳IJKL", "222333444555", true, 1, 16);
- // Latin + Arabic characters
- checkMatches("Peterعبد الرحمنJames", "752637", true, 0, 1, 15, 20);
- }
-
- public void testMatches_umlaut() {
- checkMatches("ÄÖÜäöü", "268268", true, 0, 6);
- }
-
- public void testMatches_NumberInName() {
- // Number used as display name
- checkMatches("+1-123-456-6789", "1234566789", true, 3, 15);
- // Mix of numbers and letters
- checkMatches("3rd Grade Teacher", "373", true, 0, 3);
- checkMatches("1800 Win A Prize", "1800", true, 0, 4);
- checkMatches("1800 Win A Prize", "1800946277493", true, 0, 16);
- checkMatches("1800 Win A Prize", "977493", true, 5, 6, 11, 16);
- }
-
-
- // TODO: Great if it was treated as "s" or "ss. Figure out if possible without prefix trie?
- @Suppress
- public void testMatches_germanSharpS() {
- checkMatches("ß", "s", true, 0, 1);
- checkMatches("ß", "ss", true, 0, 1);
- }
-
- // TODO: Add this and make it work
- @Suppress
- public void testMatches_greek() {
- // http://en.wikipedia.org/wiki/Greek_alphabet
- fail("Greek letters aren't supported yet.");
- }
-
- // TODO: Add this and make it work
- @Suppress
- public void testMatches_cyrillic() {
- // http://en.wikipedia.org/wiki/Cyrillic_script
- fail("Cyrillic letters aren't supported yet.");
- }
-
-
- public void testMatches_NumberBasic() {
- // Simple basic examples that start the match from the start of the number
- checkMatchesNumber("5103337596", "510", true, 0, 3);
- checkMatchesNumber("5103337596", "511", false, 0, 0);
- checkMatchesNumber("5103337596", "5103337596", true, 0, 10);
- checkMatchesNumber("123-456-789", "123456789", true, 0, 11);
- checkMatchesNumber("123-456-789", "123456788", false, 0, 0);
- checkMatchesNumber("09999999999", "099", true, 0, 3);
- }
-
- public void testMatches_NumberWithCountryCode() {
- // These matches should ignore the country prefix
- // USA (+1)
- checkMatchesNumber("+15103337596", "5103337596", true, 2, 12);
- checkMatchesNumber("+15103337596", "15103337596", true, 0, 12);
-
- // Singapore (+65)
- checkMatchesNumber("+6591776930", "6591", true, 0, 5);
- checkMatchesNumber("+6591776930", "9177", true, 3, 7);
- checkMatchesNumber("+6591776930", "5917", false, 3, 7);
-
- // Hungary (+36)
- checkMatchesNumber("+3612345678", "361234", true, 0, 7);
- checkMatchesNumber("+3612345678", "1234", true, 3, 7);
-
- // Hongkong (+852)
- checkMatchesNumber("+852 2222 2222", "85222222222", true, 0, 14);
- checkMatchesNumber("+852 2222 3333", "2222", true, 5, 9);
-
- // Invalid (+854)
- checkMatchesNumber("+854 1111 2222", "8541111", true, 0, 9);
- checkMatchesNumber("+854 1111 2222", "1111", false, 0, 0);
- }
-
- public void testMatches_NumberNANP() {
- SmartDialPrefix.setUserInNanpRegion(true);
- // An 11 digit number prefixed with 1 should be matched by the 10 digit number, as well as
- // the 7 digit number (without area code)
- checkMatchesNumber("1-510-333-7596", "5103337596", true, true, 2, 14);
- checkMatchesNumber("1-510-333-7596", "3337596", true, true, 6, 14);
-
- // An 11 digit number prefixed with +1 should be matched by the 10 digit number, as well as
- // the 7 digit number (without area code)
- checkMatchesNumber("+1-510-333-7596", "5103337596", true, true, 3, 15);
- checkMatchesNumber("+1-510-333-7596", "3337596", true, true, 7, 15);
- checkMatchesNumber("+1-510-333-7596", "103337596", false, true, 0, 0);
- checkMatchesNumber("+1-510-333-7596", "337596", false, true, 0, 0);
- checkMatchesNumber("+1510 3337596", "5103337596", true, true, 2, 13);
- checkMatchesNumber("+1510 3337596", "3337596", true, true, 6, 13);
- checkMatchesNumber("+1510 3337596", "103337596", false, true, 0, 0);
- checkMatchesNumber("+1510 3337596", "37596", false, true, 0, 0);
-
- // Invalid NANP numbers should not be matched
- checkMatchesNumber("1-510-333-759", "510333759", false, true, 0, 0);
- checkMatchesNumber("510-333-759", "333759", false, true, 0, 0);
-
- // match should fail if NANP flag is switched off
- checkMatchesNumber("1-510-333-7596", "3337596", false, false, 0, 0);
-
- // A 10 digit number without a 1 prefix should be matched by the 7 digit number
- checkMatchesNumber("(650) 292 2323", "2922323", true, true, 6, 14);
- checkMatchesNumber("(650) 292 2323", "6502922323", true, true, 0, 14);
- // match should fail if NANP flag is switched off
- checkMatchesNumber("(650) 292 2323", "2922323", false, false, 0, 0);
- // But this should still match (since it is the full number)
- checkMatchesNumber("(650) 292 2323", "6502922323", true, false, 0, 14);
- }
-
-
- private void checkMatchesNumber(String number, String query, boolean expectedMatches,
- int matchStart, int matchEnd) {
- checkMatchesNumber(number, query, expectedMatches, false, matchStart, matchEnd);
- }
-
- private void checkMatchesNumber(String number, String query, boolean expectedMatches,
- boolean matchNanp, int matchStart, int matchEnd) {
- final SmartDialNameMatcher matcher = new SmartDialNameMatcher(query);
- final SmartDialMatchPosition pos = matcher.matchesNumber(number, query, matchNanp);
- assertEquals(expectedMatches, pos != null);
- if (expectedMatches) {
- assertEquals("start", matchStart, pos.start);
- assertEquals("end", matchEnd, pos.end);
- }
- }
-
- private void checkMatches(String displayName, String query, boolean expectedMatches,
- int... expectedMatchPositions) {
- final SmartDialNameMatcher matcher = new SmartDialNameMatcher(query);
- final ArrayList<SmartDialMatchPosition> matchPositions =
- new ArrayList<SmartDialMatchPosition>();
- final boolean matches = matcher.matchesCombination(
- displayName, query, matchPositions);
- Log.d(TAG, "query=" + query + " text=" + displayName
- + " nfd=" + Normalizer.normalize(displayName, Normalizer.Form.NFD)
- + " nfc=" + Normalizer.normalize(displayName, Normalizer.Form.NFC)
- + " nfkd=" + Normalizer.normalize(displayName, Normalizer.Form.NFKD)
- + " nfkc=" + Normalizer.normalize(displayName, Normalizer.Form.NFKC)
- + " matches=" + matches);
- assertEquals("matches", expectedMatches, matches);
- final int length = expectedMatchPositions.length;
- assertEquals(length % 2, 0);
- if (matches) {
- for (int i = 0; i < length/2; i++) {
- assertEquals("start", expectedMatchPositions[i * 2], matchPositions.get(i).start);
- assertEquals("end", expectedMatchPositions[i * 2 + 1], matchPositions.get(i).end);
- }
- }
- }
-
-}
diff --git a/tests/src/com/android/dialer/dialpad/UnicodeDialerKeyListenerTest.java b/tests/src/com/android/dialer/dialpad/UnicodeDialerKeyListenerTest.java
deleted file mode 100644
index 21ccec3c0..000000000
--- a/tests/src/com/android/dialer/dialpad/UnicodeDialerKeyListenerTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.dialer.dialpad;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.dialer.dialpad.UnicodeDialerKeyListener;
-
-import junit.framework.TestCase;
-/**
- * Test case for {@link UnicodeDialerKeyListener}.
- *
- * adb shell am instrument -w -e class com.android.contacts.dialpad.UnicodeDialerKeyListenerTest \
- com.android.contacts.tests/android.test.InstrumentationTestRunner
- */
-@SmallTest
-public class UnicodeDialerKeyListenerTest extends TestCase {
- private static UnicodeDialerKeyListener mUnicodeDialerKeyListener;
-
- // Pasted numeric digits should remain unchanged
- public void testNumericDigits() {
- // The last 3 arguments don't matter because {@link NumberKeyListener} doesn't care
- // about dest, dstart, dend in
- // public CharSequence filter (CharSequence source, int start, int end,
- // Spanned dest, int dstart, int dend)
- // anyway. This applies to all tests.
- assertEquals(null, mUnicodeDialerKeyListener.filter("111222333", 0, 9, null, 0, 0));
- }
-
- // Pasted Arabic digits should be converted to ascii digits
- public void testArabicDigits() {
- assertEquals("0123456789", mUnicodeDialerKeyListener.filter("٠١٢٣٤٥٦٧٨٩", 0, 10,
- null, 0, 0));
- }
-
- // Pasted Farsi(Persian) digits should be converted to ascii digits
- // Note the difference in digits 4, 5 and 6 when compared to arabic. The rest of the digits
- // look the same compared to the Arabic digits but they actually have different unicode codes.
- public void testFarsiDigits() {
- assertEquals("0123456789", mUnicodeDialerKeyListener.filter("۰۱۲۳۴۵۶۷۸۹", 0, 10,
- null, 0, 0));
- }
-
- // This is a rare use case but we should make sure it works all the same.
- public void testCombinationDigits() {
- assertEquals("15102849177", mUnicodeDialerKeyListener.filter("۱510٢٨٤۹۱۷۷", 0, 11,
- null, 0, 0));
- }
-
- // Test that a normal digit string with dashes is returned unfiltered
- public void testDashes() {
- assertEquals(null, mUnicodeDialerKeyListener.filter("1510-284-9177", 0, 13,
- null, 0, 0));
- }
-
- @Override
- protected void setUp() throws Exception {
- mUnicodeDialerKeyListener = UnicodeDialerKeyListener.INSTANCE;
- }
-}
diff --git a/tests/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigratorTest.java b/tests/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigratorTest.java
deleted file mode 100644
index 505855528..000000000
--- a/tests/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigratorTest.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-import android.test.AndroidTestCase;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnHasBlockedNumbersListener;
-
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-public class BlockedNumbersAutoMigratorTest extends AndroidTestCase {
-
- private static final String HAS_CHECKED_AUTO_MIGRATE_KEY_FOR_TEST = "checkedAutoMigrateForTest";
-
- @Mock
- private FilteredNumberAsyncQueryHandler mockQueryHandler;
-
- private SharedPreferences sharedPreferences;
-
- private BlockedNumbersAutoMigrator blockedNumbersAutoMigrator;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
- FilteredNumberCompat.setContextForTest(getContext());
- FilteredNumberCompat.setHasMigratedToNewBlocking(false);
-
- sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
- // SharedPreference state isn't cleaned up between each test automatically, clear it now
- sharedPreferences.edit().clear().apply();
-
- blockedNumbersAutoMigrator = new BlockedNumbersAutoMigrator(sharedPreferences,
- mockQueryHandler);
- }
-
- public void testConstructor_NullSharedPreferences() {
- try {
- new BlockedNumbersAutoMigrator(null, mockQueryHandler);
- fail();
- } catch (NullPointerException e) {
- }
- }
-
- public void testConstructor_NullQueryHandler() {
- try {
- new BlockedNumbersAutoMigrator(sharedPreferences, null);
- fail();
- } catch (NullPointerException e) {
- }
- }
-
- public void testAutoMigrate_M() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- blockedNumbersAutoMigrator.autoMigrate();
-
- verify(mockQueryHandler, never()).hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
- }
-
- public void testAutoMigrate_AlreadyMigrated() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- FilteredNumberCompat.setHasMigratedToNewBlocking(true);
-
- blockedNumbersAutoMigrator.autoMigrate();
-
- verify(mockQueryHandler, never()).hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
- }
-
- public void testAutoMigrate_AlreadyChecked() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- sharedPreferences.edit()
- .putBoolean(HAS_CHECKED_AUTO_MIGRATE_KEY_FOR_TEST, true)
- .apply();
-
- blockedNumbersAutoMigrator.autoMigrate();
-
- verify(mockQueryHandler, never()).hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
- }
-
- public void testAutoMigrate_HasNumbers() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- setupFilteredNumberHasBlockedNumbersExpectation(true);
-
- blockedNumbersAutoMigrator.autoMigrate();
-
- verify(mockQueryHandler).hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
- assertFalse(FilteredNumberCompat.hasMigratedToNewBlocking());
- }
-
- public void testAutoMigrate_HasNumbers_MultipleCalls() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- setupFilteredNumberHasBlockedNumbersExpectation(true);
-
- blockedNumbersAutoMigrator.autoMigrate();
- blockedNumbersAutoMigrator.autoMigrate();
-
- verify(mockQueryHandler, times(1))
- .hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
- assertFalse(FilteredNumberCompat.hasMigratedToNewBlocking());
- }
-
- public void testAutoMigrate_NoNumbers() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- setupFilteredNumberHasBlockedNumbersExpectation(false);
-
- blockedNumbersAutoMigrator.autoMigrate();
-
- verify(mockQueryHandler).hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
- assertTrue(FilteredNumberCompat.hasMigratedToNewBlocking());
- }
-
- public void testAutoMigrate_NoNumbers_MultipleCalls() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- setupFilteredNumberHasBlockedNumbersExpectation(false);
-
- blockedNumbersAutoMigrator.autoMigrate();
- blockedNumbersAutoMigrator.autoMigrate();
-
- verify(mockQueryHandler, times(1))
- .hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
- assertTrue(FilteredNumberCompat.hasMigratedToNewBlocking());
- }
-
-
- public void testAutoMigrate_SimulateClearingAppData() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- setupFilteredNumberHasBlockedNumbersExpectation(true);
-
- blockedNumbersAutoMigrator.autoMigrate();
-
- // Clearing app data removes the sharedPreferences and all of the blocked numbers
- sharedPreferences.edit().clear().apply();
- setupFilteredNumberHasBlockedNumbersExpectation(false);
-
- blockedNumbersAutoMigrator.autoMigrate();
-
- verify(mockQueryHandler, times(2))
- .hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
- assertTrue(FilteredNumberCompat.hasMigratedToNewBlocking());
- }
-
- /*
- * Sets up the {@link #mockQueryHandler} to call the {@link OnHasBlockedNumbersListener} with
- * the given hasBlockedNumbers value as the parameter, when
- * {@link FilteredNumberAsyncQueryHandler#hasBlockedNumbers} is called.
- */
- private void setupFilteredNumberHasBlockedNumbersExpectation(final boolean hasBlockedNumbers) {
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- ((OnHasBlockedNumbersListener) invocation.getArguments()[0])
- .onHasBlockedNumbers(hasBlockedNumbers);
- return null;
- }
- }).when(mockQueryHandler).hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
- }
-}
diff --git a/tests/src/com/android/dialer/filterednumber/BlockedNumbersFragmentInstrumentationTest.java b/tests/src/com/android/dialer/filterednumber/BlockedNumbersFragmentInstrumentationTest.java
deleted file mode 100644
index ea4c51e21..000000000
--- a/tests/src/com/android/dialer/filterednumber/BlockedNumbersFragmentInstrumentationTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.verify;
-
-import android.test.ActivityInstrumentationTestCase2;
-import android.view.View;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.dialer.R;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.filterednumber.BlockedNumbersMigrator.Listener;
-
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Instrumentation tests for {@link BlockedNumbersFragment}. Note for these tests to work properly,
- * the device's screen must be on.
- */
-public class BlockedNumbersFragmentInstrumentationTest extends
- ActivityInstrumentationTestCase2<BlockedNumbersSettingsActivity> {
-
- private static final String FRAGMENT_TAG = "blocked_management";
-
- private BlockedNumbersFragment blockedNumbersFragment;
- @Mock private BlockedNumbersMigrator blockedNumbersMigrator;
-
- public BlockedNumbersFragmentInstrumentationTest() {
- super(BlockedNumbersSettingsActivity.class);
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
- FilteredNumberCompat.setIsEnabledForTest(true);
- blockedNumbersFragment = new BlockedNumbersFragment();
- blockedNumbersFragment.setBlockedNumbersMigratorForTest(blockedNumbersMigrator);
- getActivity().getFragmentManager().beginTransaction()
- .replace(R.id.blocked_numbers_activity_container, blockedNumbersFragment, FRAGMENT_TAG)
- .commit();
- getInstrumentation().waitForIdleSync();
- }
-
- public void testMigrationPromo_NotShown_M() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- assertEquals(View.GONE, blockedNumbersFragment.migratePromoView.getVisibility());
- }
-
- public void testMigrationPromo_Shown_N() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- assertEquals(View.VISIBLE, blockedNumbersFragment.migratePromoView.getVisibility());
- }
-
- public void testOnClick_Migrate() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
-
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- blockedNumbersFragment.getListView().findViewById(R.id.migrate_promo_allow_button)
- .performClick();
- }
- });
- getInstrumentation().waitForIdleSync();
- assertFalse(blockedNumbersFragment.getListView().findViewById(R.id.migrate_promo_allow_button)
- .isEnabled());
- verify(blockedNumbersMigrator).migrate(any(Listener.class));
- }
-}
diff --git a/tests/src/com/android/dialer/filterednumber/BlockedNumbersMigratorTest.java b/tests/src/com/android/dialer/filterednumber/BlockedNumbersMigratorTest.java
deleted file mode 100644
index 565c206d8..000000000
--- a/tests/src/com/android/dialer/filterednumber/BlockedNumbersMigratorTest.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.provider.BlockedNumberContract;
-import android.provider.BlockedNumberContract.BlockedNumbers;
-import android.test.AndroidTestCase;
-import android.test.mock.MockContentResolver;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.test.mocks.MockContentProvider;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberContract;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-public class BlockedNumbersMigratorTest extends AndroidTestCase {
-
- private static final String NUMBER = "6502530000";
- private static final String NUMBER1 = "6502530001";
- private static final String NUMBER2 = "6502530002";
-
- @Mock private BlockedNumbersMigrator.Listener mListener;
- private final MockContentResolver mContentResolver = new MockContentResolver();
- private final MockContentProvider mContentProvider = new MockContentProvider();
- private BlockedNumbersMigrator mMigrator;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
- FilteredNumberCompat.setIsEnabledForTest(true);
- mContentResolver.addProvider(FilteredNumberContract.AUTHORITY, mContentProvider);
- mContentResolver.addProvider(BlockedNumberContract.AUTHORITY, mContentProvider);
- mMigrator = new BlockedNumbersMigrator(mContentResolver);
- }
-
- public void testConstructor_NullContentResolver() {
- try {
- new BlockedNumbersMigrator(null);
- fail();
- } catch (NullPointerException e) {}
- }
-
- public void testMigrate_M() {
- if (CompatUtils.isNCompatible()) {
- return;
- }
- assertFalse(mMigrator.migrate(mListener));
- }
-
- public void testMigrate_N_Disabled() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- FilteredNumberCompat.setIsEnabledForTest(false);
- assertFalse(mMigrator.migrate(mListener));
- }
-
- public void testMigrate_N_NullListener() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- try {
- mMigrator.migrate(null);
- fail();
- } catch (NullPointerException e) {}
- }
-
- public void testMigrate_N() throws InterruptedException {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- mContentProvider.expectQuery(FilteredNumber.CONTENT_URI)
- .withProjection(FilteredNumberColumns.NUMBER).returnRow(NUMBER).returnRow(NUMBER1)
- .returnRow(NUMBER2);
-
- setUpNewBlockedNumberExpectations(mContentProvider, NUMBER, 0);
- setUpNewBlockedNumberExpectations(mContentProvider, NUMBER1, 1);
- setUpNewBlockedNumberExpectations(mContentProvider, NUMBER2, 2);
-
- MigrationListener listener = new MigrationListener();
- assertTrue(mMigrator.migrate(listener));
- listener.waitForCallback();
- assertTrue(FilteredNumberCompat.hasMigratedToNewBlocking());
- mContentProvider.verify();
- }
-
- public void testMigrate_N_AlreadyBlocked() throws InterruptedException {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- mContentProvider.expectQuery(FilteredNumber.CONTENT_URI)
- .withProjection(FilteredNumberColumns.NUMBER).returnRow(NUMBER);
- mContentProvider.expectQuery(BlockedNumbers.CONTENT_URI)
- .withProjection(BlockedNumbers.COLUMN_ID)
- .withSelection(BlockedNumbers.COLUMN_ORIGINAL_NUMBER + " = ?", NUMBER).returnRow(0);
- // No expectation for insert into BlockedNumbers.CONTENT_URI because it's already there
-
- MigrationListener listener = new MigrationListener();
- assertTrue(mMigrator.migrate(listener));
- listener.waitForCallback();
- assertTrue(FilteredNumberCompat.hasMigratedToNewBlocking());
- mContentProvider.verify();
- }
-
- private void setUpNewBlockedNumberExpectations(MockContentProvider contentProvider,
- String number, int returnId) {
- contentProvider.expectQuery(BlockedNumbers.CONTENT_URI)
- .withProjection(BlockedNumbers.COLUMN_ID)
- .withSelection(BlockedNumbers.COLUMN_ORIGINAL_NUMBER + " = ?", number).returnEmptyCursor();
- contentProvider.expectInsert(BlockedNumbers.CONTENT_URI,
- createBlockedNumberInsertValues(number),
- ContentUris.withAppendedId(BlockedNumbers.CONTENT_URI, returnId));
- }
-
- private ContentValues createBlockedNumberInsertValues(String number) {
- ContentValues values = new ContentValues();
- values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, number);
- return values;
- }
-
- private static class MigrationListener implements BlockedNumbersMigrator.Listener {
-
- private final CountDownLatch mOnCompleteCalled = new CountDownLatch(1);
-
- @Override
- public void onComplete() {
- mOnCompleteCalled.countDown();
- }
-
- public void waitForCallback() throws InterruptedException {
- if (!mOnCompleteCalled.await(5000, TimeUnit.MILLISECONDS)) {
- throw new IllegalStateException("Waiting on callback timed out.");
- }
- }
- }
-}
diff --git a/tests/src/com/android/dialer/filterednumber/FilteredNumbersUtilTest.java b/tests/src/com/android/dialer/filterednumber/FilteredNumbersUtilTest.java
deleted file mode 100644
index d496b1845..000000000
--- a/tests/src/com/android/dialer/filterednumber/FilteredNumbersUtilTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
-import android.content.Context;
-import android.preference.PreferenceManager;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.test.mocks.ContactsMockContext;
-import com.android.contacts.common.test.mocks.MockContentProvider.Query;
-import com.android.dialer.compat.FilteredNumberCompat;
-import com.android.dialer.database.FilteredNumberContract;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-
-@SmallTest
-public class FilteredNumbersUtilTest extends AndroidTestCase {
-
- private static final String COUNTRY_ISO = "US";
-
- // Wed Nov 11 2015 15:00:00
- private static final long EARLIER_TIME = 1447282800000L;
-
- // Wed Nov 11 2015 15:01:40
- private static final long LATER_TIME = 1447282900000L;
-
- private static final String[] FILTERED_NUMBER_PROJECTION = new String[] {
- FilteredNumberColumns.CREATION_TIME };
-
- private static final String NORMALIZED_NUMBER = "+16503903411";
-
- private static final long NULL_CREATION_TIME = -1;
-
- private ContactsMockContext mContext;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mContext = new ContactsMockContext(getContext(), FilteredNumberContract.AUTHORITY);
-
- // Reset whether an emergency number was dialed
- PreferenceManager.getDefaultSharedPreferences(mContext)
- .edit()
- .putLong(FilteredNumbersUtil.LAST_EMERGENCY_CALL_MS_PREF_KEY, 0)
- .apply();
- }
-
- public void testShouldBlockVoicemail_NotBlocked() {
- setupShouldBlockVoicemailQuery(NULL_CREATION_TIME);
- assertFalse(FilteredNumbersUtil.shouldBlockVoicemail(mContext, NORMALIZED_NUMBER,
- COUNTRY_ISO, EARLIER_TIME));
- }
-
- public void testShouldBlockVoicemail_BlockedBeforeVoicemail() {
- setupShouldBlockVoicemailQuery(EARLIER_TIME);
- assertTrue(FilteredNumbersUtil.shouldBlockVoicemail(mContext, NORMALIZED_NUMBER,
- COUNTRY_ISO, LATER_TIME));
- }
-
- public void testShouldBlockVoicemail_BlockedAfterVoicemail() {
- setupShouldBlockVoicemailQuery(LATER_TIME);
- assertFalse(FilteredNumbersUtil.shouldBlockVoicemail(mContext, NORMALIZED_NUMBER,
- COUNTRY_ISO, EARLIER_TIME));
- }
-
- public void testShouldBlockVoicemail_BlockedSameTimeAsVoicemail() {
- setupShouldBlockVoicemailQuery(EARLIER_TIME);
- assertTrue(FilteredNumbersUtil.shouldBlockVoicemail(mContext, NORMALIZED_NUMBER,
- COUNTRY_ISO, EARLIER_TIME));
- }
-
- public void testShouldBlockVoicemail_BlockedInSameMinuteAsVoicemail() {
- setupShouldBlockVoicemailQuery(EARLIER_TIME);
- assertTrue(FilteredNumbersUtil.shouldBlockVoicemail(mContext, NORMALIZED_NUMBER,
- COUNTRY_ISO, EARLIER_TIME + 30000));
- }
-
- public void testShouldBlockVoicemail_AfterEmergencyCall() {
- // Just called emergency services
- PreferenceManager.getDefaultSharedPreferences(mContext)
- .edit()
- .putLong(FilteredNumbersUtil.LAST_EMERGENCY_CALL_MS_PREF_KEY,
- System.currentTimeMillis())
- .apply();
- assertFalse(FilteredNumbersUtil.shouldBlockVoicemail(mContext, NORMALIZED_NUMBER,
- COUNTRY_ISO, 0));
- }
-
- public void testMaybeNotifyCallBlockingDisabled_Migrated() {
- if (!CompatUtils.isNCompatible()) {
- return;
- }
- FilteredNumberCompat.setIsEnabledForTest(true);
- FilteredNumberCompat.setHasMigratedToNewBlocking(true);
- Context mockContext = mock(Context.class);
-
- FilteredNumbersUtil.maybeNotifyCallBlockingDisabled(mockContext);
- verifyZeroInteractions(mockContext);
- }
-
- private void setupShouldBlockVoicemailQuery(long creationTimeMs) {
- Query query = mContext.getContactsProvider().expectQuery(FilteredNumber.CONTENT_URI)
- .withProjection(FILTERED_NUMBER_PROJECTION)
- .withAnySelection()
- .withAnySortOrder();
- if (creationTimeMs == NULL_CREATION_TIME) {
- query.returnEmptyCursor();
- return;
- }
- query.returnRow(creationTimeMs);
- }
-}
diff --git a/tests/src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragmentInstrumentationTest.java b/tests/src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragmentInstrumentationTest.java
deleted file mode 100644
index 0bfa6bcb2..000000000
--- a/tests/src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragmentInstrumentationTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import android.app.AlertDialog;
-import android.app.DialogFragment;
-import android.content.ContentResolver;
-import android.content.DialogInterface;
-import android.test.ActivityInstrumentationTestCase2;
-
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.filterednumber.BlockedNumbersMigrator.Listener;
-
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Instrumentation tests for {@link MigrateBlockedNumbersDialogFragment}. Note for these tests to
- * work properly, the device's screen must be on.
- */
-public class MigrateBlockedNumbersDialogFragmentInstrumentationTest extends
- ActivityInstrumentationTestCase2<DialtactsActivity> {
-
- private static final String SHOW_TAG = "ShowTag";
-
- private BlockedNumbersMigrator mBlockedNumbersMigrator;
- @Mock private Listener mListener;
- private DialogFragment mMigrateDialogFragment;
-
- public MigrateBlockedNumbersDialogFragmentInstrumentationTest() {
- super(DialtactsActivity.class);
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
- mBlockedNumbersMigrator = new SynchronousBlockedNumbersMigrator(
- getActivity().getContentResolver());
- mMigrateDialogFragment = MigrateBlockedNumbersDialogFragment
- .newInstance(mBlockedNumbersMigrator, mListener);
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mMigrateDialogFragment.show(getActivity().getFragmentManager(), SHOW_TAG);
- }
- });
- getInstrumentation().waitForIdleSync();
- }
-
- public void testDialogAppears() {
- assertTrue(mMigrateDialogFragment.getDialog().isShowing());
- }
-
- public void testDialogPositiveButtonPress() {
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- ((AlertDialog) mMigrateDialogFragment.getDialog())
- .getButton(DialogInterface.BUTTON_POSITIVE).performClick();
- }
- });
- getInstrumentation().waitForIdleSync();
- // Dialog was dismissed
- assertNull(mMigrateDialogFragment.getDialog());
- }
-
- private static class SynchronousBlockedNumbersMigrator extends BlockedNumbersMigrator {
- public SynchronousBlockedNumbersMigrator(ContentResolver contentResolver) {
- super(contentResolver);
- }
-
- @Override
- public boolean migrate(BlockedNumbersMigrator.Listener listener) {
- listener.onComplete();
- return true;
- }
- }
-}
diff --git a/tests/src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragmentTest.java b/tests/src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragmentTest.java
deleted file mode 100644
index 1b419cee8..000000000
--- a/tests/src/com/android/dialer/filterednumber/MigrateBlockedNumbersDialogFragmentTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.dialer.filterednumber;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.dialer.filterednumber.BlockedNumbersMigrator.Listener;
-
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-
-/**
- * Unit tests for {@link MigrateBlockedNumbersDialogFragment}
- */
-@SmallTest
-public class MigrateBlockedNumbersDialogFragmentTest extends AndroidTestCase {
-
- @Mock private BlockedNumbersMigrator mBlockedNumbersMigrator;
- @Mock private Listener mListener;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
- }
-
- public void testNewInstance_NullMigrator() {
- try {
- MigrateBlockedNumbersDialogFragment.newInstance(null, mListener);
- fail();
- } catch (NullPointerException e) {}
- }
-
- public void testNewInstance_NullListener() {
- try {
- MigrateBlockedNumbersDialogFragment.newInstance(mBlockedNumbersMigrator, null);
- fail();
- } catch (NullPointerException e) {}
- }
-
- public void testNewInstance_WithListener() {
- assertNotNull(MigrateBlockedNumbersDialogFragment.newInstance(mBlockedNumbersMigrator,
- mListener));
- }
-}
diff --git a/tests/src/com/android/dialer/interactions/PhoneNumberInteractionTest.java b/tests/src/com/android/dialer/interactions/PhoneNumberInteractionTest.java
deleted file mode 100644
index f58e6cc6c..000000000
--- a/tests/src/com/android/dialer/interactions/PhoneNumberInteractionTest.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2010 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.dialer.interactions;
-
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.CursorLoader;
-import android.content.DialogInterface.OnDismissListener;
-import android.content.Intent;
-import android.net.Uri;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.SipAddress;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.contacts.common.test.mocks.ContactsMockContext;
-import com.android.contacts.common.test.mocks.MockContentProvider;
-import com.android.contacts.common.test.mocks.MockContentProvider.Query;
-import com.android.contacts.common.util.ContactDisplayUtils;
-import com.android.dialer.interactions.PhoneNumberInteraction.PhoneItem;
-import com.android.dialer.util.TestConstants;
-
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Tests for {@link com.android.contacts.common.interactions.PhoneNumberInteraction}.
- *
- * adb shell am instrument \
- * -w com.android.dialer.tests/android.test.InstrumentationTestRunner
- */
-@SmallTest
-public class PhoneNumberInteractionTest extends InstrumentationTestCase {
- private final static class TestPhoneNumberInteraction extends PhoneNumberInteraction {
- private ArrayList<PhoneItem> mPhoneList;
-
- public TestPhoneNumberInteraction(Context context, int interactionType,
- OnDismissListener dismissListener) {
- super(context, interactionType, dismissListener);
- }
-
- @Override
- void showDisambiguationDialog(ArrayList<PhoneItem> phoneList) {
- this.mPhoneList = phoneList;
- }
-
- public void waitForLoader() {
- final CursorLoader loader = getLoader();
- try {
- final Method waitMethod = CursorLoader.class.getMethod("waitForLoader");
- waitMethod.invoke(loader, null);
- } catch(Exception e) {
- // ignore
- }
- }
- }
-
- private ContactsMockContext mContext;
- private MockContentProvider mContactsProvider;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = new ContactsMockContext(getInstrumentation().getTargetContext());
- mContactsProvider = mContext.getContactsProvider();
- }
-
- @Override
- protected void tearDown() throws Exception {
- mContactsProvider.verify();
- super.tearDown();
- }
-
- public void testSendSmsWhenOnlyOneNumberAvailable() {
- Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
- expectQuery(contactUri)
- .returnRow(1, "123", 0, null, null, Phone.TYPE_HOME, null,
- Phone.CONTENT_ITEM_TYPE, 13);
-
- TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
- mContext, ContactDisplayUtils.INTERACTION_SMS, null);
-
- interaction.startInteraction(contactUri);
- interaction.waitForLoader();
-
- Intent intent = mContext.getIntentForStartActivity();
- assertNotNull(intent);
-
- assertEquals(Intent.ACTION_SENDTO, intent.getAction());
- assertEquals("sms:123", intent.getDataString());
- }
-
- public void testSendSmsWhenDataIdIsProvided() {
- Uri dataUri = ContentUris.withAppendedId(Data.CONTENT_URI, 1);
- expectQuery(dataUri, true /* isDataUri */ )
- .returnRow(1, "987", 0, null, null, Phone.TYPE_HOME, null,
- Phone.CONTENT_ITEM_TYPE, 1);
-
- TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
- mContext, ContactDisplayUtils.INTERACTION_SMS, null);
-
- interaction.startInteraction(dataUri);
- interaction.waitForLoader();
-
- Intent intent = mContext.getIntentForStartActivity();
- assertNotNull(intent);
-
- assertEquals(Intent.ACTION_SENDTO, intent.getAction());
- assertEquals("sms:987", intent.getDataString());
- }
-
- public void testSendSmsWhenThereIsPrimaryNumber() {
- Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
- expectQuery(contactUri)
- .returnRow(1, "123", 0, null, null, Phone.TYPE_HOME, null,
- Phone.CONTENT_ITEM_TYPE, 13)
- .returnRow(2, "456", 1, null, null, Phone.TYPE_HOME, null,
- Phone.CONTENT_ITEM_TYPE, 13);
-
- TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
- mContext, ContactDisplayUtils.INTERACTION_SMS, null);
-
- interaction.startInteraction(contactUri);
- interaction.waitForLoader();
-
- Intent intent = mContext.getIntentForStartActivity();
- assertNotNull(intent);
-
- assertEquals(Intent.ACTION_SENDTO, intent.getAction());
- assertEquals("sms:456", intent.getDataString());
- }
-
- public void testShouldCollapseWith() {
- PhoneNumberInteraction.PhoneItem phoneItem1 = new PhoneNumberInteraction.PhoneItem();
- PhoneNumberInteraction.PhoneItem phoneItem2 = new PhoneNumberInteraction.PhoneItem();
-
- phoneItem1.phoneNumber = "123";
- phoneItem2.phoneNumber = "123";
-
- assertTrue(phoneItem1.shouldCollapseWith(phoneItem2, mContext));
-
- phoneItem1.phoneNumber = "123";
- phoneItem2.phoneNumber = "456";
-
- assertFalse(phoneItem1.shouldCollapseWith(phoneItem2, mContext));
-
- phoneItem1.phoneNumber = "123#,123";
- phoneItem2.phoneNumber = "123#,456";
-
- assertFalse(phoneItem1.shouldCollapseWith(phoneItem2, mContext));
- }
-
- public void testCallNumberWhenThereAreDuplicates() {
- Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
- expectQuery(contactUri)
- .returnRow(1, "123", 0, null, null, Phone.TYPE_HOME, null,
- Phone.CONTENT_ITEM_TYPE, 13)
- .returnRow(2, "123", 0, null, null, Phone.TYPE_WORK, null,
- Phone.CONTENT_ITEM_TYPE, 13);
-
- TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
- mContext, ContactDisplayUtils.INTERACTION_CALL, null);
-
- interaction.startInteraction(contactUri);
- interaction.waitForLoader();
-
- Intent intent = mContext.getIntentForStartActivity();
- assertNotNull(intent);
-
- assertEquals(TestConstants.CALL_INTENT_ACTION, intent.getAction());
- assertEquals("tel:123", intent.getDataString());
- }
-
- public void testCallWithSip() {
- Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
- expectQuery(contactUri)
- .returnRow(1, "example@example.com", 0, null, null, Phone.TYPE_HOME, null,
- SipAddress.CONTENT_ITEM_TYPE, 13);
- TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
- mContext, ContactDisplayUtils.INTERACTION_CALL, null);
-
- interaction.startInteraction(contactUri);
- interaction.waitForLoader();
-
- Intent intent = mContext.getIntentForStartActivity();
- assertNotNull(intent);
-
- assertEquals(TestConstants.CALL_INTENT_ACTION, intent.getAction());
- assertEquals("sip:example%40example.com", intent.getDataString());
- }
-
- public void testShowDisambigDialogForCalling() {
- Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
- expectQuery(contactUri)
- .returnRow(1, "123", 0, "account", null, Phone.TYPE_HOME, "label",
- Phone.CONTENT_ITEM_TYPE, 13)
- .returnRow(2, "456", 0, null, null, Phone.TYPE_WORK, null,
- Phone.CONTENT_ITEM_TYPE, 13);
-
- TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
- mContext, ContactDisplayUtils.INTERACTION_CALL, null);
-
- interaction.startInteraction(contactUri);
- interaction.waitForLoader();
-
- List<PhoneItem> items = interaction.mPhoneList;
- assertNotNull(items);
- assertEquals(2, items.size());
-
- PhoneItem item = items.get(0);
- assertEquals(1, item.id);
- assertEquals("123", item.phoneNumber);
- assertEquals("account", item.accountType);
- assertEquals(Phone.TYPE_HOME, item.type);
- assertEquals("label", item.label);
- }
-
- private Query expectQuery(Uri contactUri) {
- return expectQuery(contactUri, false);
- }
-
- private Query expectQuery(Uri uri, boolean isDataUri) {
- final Uri dataUri;
- if (isDataUri) {
- dataUri = uri;
- } else {
- dataUri = Uri.withAppendedPath(uri, Contacts.Data.CONTENT_DIRECTORY);
- }
- return mContactsProvider
- .expectQuery(dataUri)
- .withProjection(
- Phone._ID,
- Phone.NUMBER,
- Phone.IS_SUPER_PRIMARY,
- RawContacts.ACCOUNT_TYPE,
- RawContacts.DATA_SET,
- Phone.TYPE,
- Phone.LABEL,
- Phone.MIMETYPE,
- Phone.CONTACT_ID)
- .withSelection("mimetype IN ('vnd.android.cursor.item/phone_v2',"
- + " 'vnd.android.cursor.item/sip_address') AND data1 NOT NULL", null);
- }
-}
diff --git a/tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java b/tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java
deleted file mode 100644
index 881938400..000000000
--- a/tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java
+++ /dev/null
@@ -1,301 +0,0 @@
-package com.android.dialer.list;
-
-import com.google.common.collect.Lists;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.provider.ContactsContract.PinnedPositions;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.contacts.common.ContactTileLoaderFactory;
-import com.android.contacts.common.list.ContactEntry;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.dialer.list.PhoneFavoritesTileAdapter.OnDataSetChangedForAnimationListener;
-
-import junit.framework.Assert;
-
-import java.util.ArrayList;
-
-@SmallTest
-public class PhoneFavoritesTileAdapterTest extends AndroidTestCase {
-
- private static final OnDataSetChangedForAnimationListener NOOP_ANIMATION_LISTENER =
- new OnDataSetChangedForAnimationListener() {
- @Override
- public void onDataSetChangedForAnimation(long... idsInPlace) {}
-
- @Override
- public void cacheOffsetsForDatasetChange() {}
- };
-
- private PhoneFavoritesTileAdapter mAdapter;
-
- @Override
- public void setUp() {
- this.mAdapter = new PhoneFavoritesTileAdapter(getContext(), null, NOOP_ANIMATION_LISTENER);
- }
-
- /**
- * For all arrangeContactsByPinnedPosition tests, the id for a particular ContactEntry
- * represents the index at which it should be located after calling
- * arrangeContactsByPinnedPosition
- */
-
- public void testArrangeContactsByPinnedPosition_NoPinned() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0),
- getTestContactEntry(1), getTestContactEntry(2));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 3);
- }
-
- public void testArrangeContactsByPinnedPosition_NoPinned_RemoveDemoted() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0),
- getTestContactEntry(-1, PinnedPositions.DEMOTED), getTestContactEntry(1));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 2);
- }
-
- public void testArrangeContactsByPinnedPosition_OnePinned_Beginning() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(1),
- getTestContactEntry(0, 1), getTestContactEntry(2));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 3);
- }
-
- public void testArrangeContactsByPinnedPosition_OnePinned_Middle() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0),
- getTestContactEntry(1, 2), getTestContactEntry(2));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 3);
- }
-
- public void testArrangeContactsByPinnedPosition_OnePinned_End() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0),
- getTestContactEntry(2, 3), getTestContactEntry(1));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 3);
- }
-
- public void testArrangeContactsByPinnedPosition_OnePinned_Outside() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0),
- getTestContactEntry(2, 5), getTestContactEntry(1));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 3);
- }
-
- public void testArrangeContactsByPinnedPosition_OnePinned_RemoveDemoted() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(1, 2),
- getTestContactEntry(-1, PinnedPositions.DEMOTED), getTestContactEntry(0));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 2);
- }
-
- public void testArrangeContactsByPinnedPosition_TwoPinned_Split() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0, 1),
- getTestContactEntry(1), getTestContactEntry(2, 3));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 3);
- }
-
- public void testArrangeContactsByPinnedPosition_TwoPinned_Adjacent() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(1, 2),
- getTestContactEntry(0), getTestContactEntry(2, 3));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 3);
- }
-
- public void testArrangeContactsByPinnedPosition_TwoPinned_Conflict_UnpinnedBefore() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(1, 2),
- getTestContactEntry(0), getTestContactEntry(2, 2));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 3);
- }
-
- public void testArrangeContactsByPinnedPosition_TwoPinned_Conflict_UnpinnedAfter() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0, 1),
- getTestContactEntry(2), getTestContactEntry(1, 1));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 3);
- }
-
- public void testArrangeContactsByPinnedPosition_TwoPinned_Conflict_RemoveDemoted() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(1, 2),
- getTestContactEntry(-1, PinnedPositions.DEMOTED), getTestContactEntry(0, 2));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 2);
- }
-
- public void testArrangeContactsByPinnedPosition_AllPinned() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(1, 2),
- getTestContactEntry(0, 1), getTestContactEntry(2, 3));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 3);
- }
-
- public void testArrangeContactsByPinnedPosition_AllPinned_TwoConflicts_ConflictsFirst() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(1, 2),
- getTestContactEntry(0, 2), getTestContactEntry(2, 3));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 3);
- }
-
- public void testArrangeContactsByPinnedPosition_AllPinned_TwoConflicts_ConflictsLast() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(0, 2),
- getTestContactEntry(1, 3), getTestContactEntry(2, 3));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 3);
- }
-
- public void testArrangeContactsByPinnedPosition_AllPinned_AllConflicts() {
- ArrayList<ContactEntry> toArrange = Lists.newArrayList(getTestContactEntry(2, 3),
- getTestContactEntry(1, 3), getTestContactEntry(0, 3));
- mAdapter.arrangeContactsByPinnedPosition(toArrange);
-
- assertContactEntryListPositionsMatchId(toArrange, 3);
- }
-
- public void testArrangeContactsByPinnedPosition_All_Pinned_AllConflicts_SortNameAlternative() {
- Context context = getContext();
- context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE).edit()
- .putInt(ContactsPreferences.SORT_ORDER_KEY,
- ContactsPreferences.SORT_ORDER_ALTERNATIVE)
- .commit();
- ArrayList<ContactEntry> actual = Lists.newArrayList(
- getTestContactEntry(1, 3, "2", "1"),
- getTestContactEntry(2, 3, "0", "2"),
- getTestContactEntry(0, 3, "1", "0")
- );
- mAdapter.arrangeContactsByPinnedPosition(actual);
-
- assertContactEntryListPositionsMatchId(actual, 3);
- }
-
- /**
- * TODO: Add tests
- *
- * This method assumes that contacts have already been reordered by
- * arrangeContactsByPinnedPosition, so we can test it with a less expansive set of test data.
- *
- * Test cases:
- * Pin a single contact at the start, middle and end of a completely unpinned list
- * Pin a single contact at the start, middle and end of a list with various numbers of
- * pinned contacts
- * Pin a single contact at the start, middle and end of a list where all contacts are pinned
- * such that contacts are forced to the left as necessary.
- */
- public void testGetReflowedPinnedPositions() {
-
- }
-
- public void testSetContactCursor_DisplayNameOrder_Primary() {
- setNameDisplayOrder(getContext(), ContactsPreferences.DISPLAY_ORDER_PRIMARY);
- Cursor testCursor = getCursorForTest(1, 0);
- mAdapter.setContactCursor(testCursor);
- Assert.assertEquals(1, mAdapter.mContactEntries.size());
- Assert.assertEquals(ContactsPreferences.DISPLAY_ORDER_PRIMARY,
- mAdapter.mContactEntries.get(0).nameDisplayOrder);
- }
-
- public void testSetContactCursor_DisplayNameOrder_Alternative() {
- setNameDisplayOrder(getContext(), ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE);
- Cursor testCursor = getCursorForTest(1, 0);
- mAdapter.setContactCursor(testCursor);
- Assert.assertEquals(1, mAdapter.mContactEntries.size());
- Assert.assertEquals(ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE,
- mAdapter.mContactEntries.get(0).nameDisplayOrder);
- }
-
- public void testSetContactCursor_DisplayNameOrder_Changed() {
- setNameDisplayOrder(getContext(), ContactsPreferences.DISPLAY_ORDER_PRIMARY);
- Cursor testCursor = getCursorForTest(1, 0);
- mAdapter.setContactCursor(testCursor);
- Assert.assertEquals(1, mAdapter.mContactEntries.size());
- Assert.assertEquals(ContactsPreferences.DISPLAY_ORDER_PRIMARY,
- mAdapter.mContactEntries.get(0).nameDisplayOrder);
-
- setNameDisplayOrder(getContext(), ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE);
- mAdapter.refreshContactsPreferences();
- mAdapter.setContactCursor(testCursor);
- Assert.assertEquals(1, mAdapter.mContactEntries.size());
- Assert.assertEquals(ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE,
- mAdapter.mContactEntries.get(0).nameDisplayOrder);
- }
-
- private void setNameDisplayOrder(Context context, int displayOrder) {
- context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE).edit().putInt(
- ContactsPreferences.DISPLAY_ORDER_KEY, displayOrder).commit();
- }
-
- /**
- * Returns a cursor containing starred and frequent contacts for test purposes.
- *
- * @param numStarred Number of starred contacts in the cursor. Cannot be a negative number.
- * @param numFrequents Number of frequent contacts in the cursor. Cannot be a negative number.
- * @return Cursor containing the required number of rows, each representing one ContactEntry
- */
- private Cursor getCursorForTest(int numStarred, int numFrequents) {
- assertTrue(numStarred >= 0);
- assertTrue(numFrequents >= 0);
- final MatrixCursor c = new MatrixCursor(ContactTileLoaderFactory.COLUMNS_PHONE_ONLY);
- int countId = 0;
-
- // Add starred contact entries. These entries have the starred field set to 1 (true).
- // The only field that really matters for testing is the contact id.
- for (int i = 0; i < numStarred; i++) {
- c.addRow(new Object[] {countId, null, 1, null, null, 0, 0, null, 0,
- PinnedPositions.UNPINNED, countId, null});
- countId++;
- }
-
- // Add frequent contact entries. These entries have the starred field set to 0 (false).
- for (int i = 0; i < numFrequents; i++) {
- c.addRow(new Object[] {countId, null, 0, null, null, 0, 0, null, 0,
- PinnedPositions.UNPINNED, countId, null});
- countId++;
- }
- return c;
- }
-
- private ContactEntry getTestContactEntry(int id) {
- return getTestContactEntry(id, PinnedPositions.UNPINNED);
- }
-
- private ContactEntry getTestContactEntry(int id, int pinned) {
- return getTestContactEntry(id, pinned, String.valueOf(id), String.valueOf(id));
- }
-
- private ContactEntry getTestContactEntry(int id, int pinned, String namePrimaryAppend,
- String nameAlternativeAppend) {
- ContactEntry contactEntry = new ContactEntry();
- contactEntry.id = id;
- contactEntry.pinned = pinned;
- contactEntry.namePrimary = namePrimaryAppend;
- contactEntry.nameAlternative = nameAlternativeAppend;
- return contactEntry;
- }
-
- private void assertContactEntryListPositionsMatchId(ArrayList<ContactEntry> contactEntries,
- int expectedSize) {
- Assert.assertEquals(expectedSize, contactEntries.size());
- for (int i = 0; i < expectedSize; ++i) {
- Assert.assertEquals(i, contactEntries.get(i).id);
- }
- }
-}
diff --git a/tests/src/com/android/dialer/tests/calllog/FillCallLogTestActivity.java b/tests/src/com/android/dialer/tests/calllog/FillCallLogTestActivity.java
deleted file mode 100644
index da41dfce4..000000000
--- a/tests/src/com/android/dialer/tests/calllog/FillCallLogTestActivity.java
+++ /dev/null
@@ -1,658 +0,0 @@
-/*
- * 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.dialer.tests.calllog;
-
-import android.app.Activity;
-import android.app.DatePickerDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.LoaderManager;
-import android.app.TimePickerDialog;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.CursorLoader;
-import android.content.Loader;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.provider.CallLog.Calls;
-import android.provider.VoicemailContract;
-import android.provider.VoicemailContract.Status;
-import android.provider.VoicemailContract.Voicemails;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.text.format.DateFormat;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.DatePicker;
-import android.widget.EditText;
-import android.widget.ProgressBar;
-import android.widget.RadioButton;
-import android.widget.TextView;
-import android.widget.TimePicker;
-import android.widget.Toast;
-
-import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
-import com.android.dialer.tests.R;
-import com.android.dialer.util.AppCompatConstants;
-import com.android.dialer.util.TelecomUtil;
-
-import java.util.Calendar;
-import java.util.List;
-import java.util.Random;
-
-/**
- * Activity to add entries to the call log for testing.
- */
-public class FillCallLogTestActivity extends Activity {
- private static final String TAG = "FillCallLogTestActivity";
- /** Identifier of the loader for querying the call log. */
- private static final int CALLLOG_LOADER_ID = 1;
-
- private static final Random RNG = new Random();
- private static final int[] CALL_TYPES = new int[] {
- AppCompatConstants.CALLS_INCOMING_TYPE,
- AppCompatConstants.CALLS_OUTGOING_TYPE,
- AppCompatConstants.CALLS_MISSED_TYPE
- };
-
- private TextView mNumberTextView;
- private Button mAddButton;
- private ProgressBar mProgressBar;
- private CheckBox mUseRandomNumbers;
- private RadioButton mCallTypeIncoming;
- private RadioButton mCallTypeMissed;
- private RadioButton mCallTypeOutgoing;
- private RadioButton mCallTypeVoicemail;
- private RadioButton mCallTypeCustom;
- private EditText mCustomCallTypeTextView;
- private CheckBox mCallTypeVideo;
- private RadioButton mPresentationAllowed;
- private RadioButton mPresentationRestricted;
- private RadioButton mPresentationUnknown;
- private RadioButton mPresentationPayphone;
- private TextView mCallDate;
- private TextView mCallTime;
- private TextView mPhoneNumber;
- private EditText mOffset;
-
- private int mCallTimeHour;
- private int mCallTimeMinute;
- private int mCallDateYear;
- private int mCallDateMonth;
- private int mCallDateDay;
- private RadioButton mAccount0;
- private RadioButton mAccount1;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.fill_call_log_test);
- mNumberTextView = (TextView) findViewById(R.id.number);
- mAddButton = (Button) findViewById(R.id.add);
- mProgressBar = (ProgressBar) findViewById(R.id.progress);
- mUseRandomNumbers = (CheckBox) findViewById(R.id.use_random_numbers);
-
- mAddButton.setOnClickListener(new View.OnClickListener(){
- @Override
- public void onClick(View v) {
- int count;
- try {
- count = Integer.parseInt(mNumberTextView.getText().toString());
- if (count > 100) {
- throw new RuntimeException("Number too large. Max=100");
- }
- } catch (RuntimeException e) {
- Toast.makeText(FillCallLogTestActivity.this, e.toString(), Toast.LENGTH_LONG)
- .show();
- return;
- }
- addEntriesToCallLog(count, mUseRandomNumbers.isChecked());
- mNumberTextView.setEnabled(false);
- mAddButton.setEnabled(false);
- mProgressBar.setProgress(0);
- mProgressBar.setMax(count);
- mProgressBar.setVisibility(View.VISIBLE);
- }
- });
-
- mCallTypeIncoming = (RadioButton) findViewById(R.id.call_type_incoming);
- mCallTypeMissed = (RadioButton) findViewById(R.id.call_type_missed);
- mCallTypeOutgoing = (RadioButton) findViewById(R.id.call_type_outgoing);
- mCallTypeVoicemail = (RadioButton) findViewById(R.id.call_type_voicemail);
- mCallTypeCustom = (RadioButton) findViewById(R.id.call_type_custom);
- mCustomCallTypeTextView = (EditText) findViewById(R.id.call_type_custom_text);
- mCallTypeVideo = (CheckBox) findViewById(R.id.call_type_video);
- mPresentationAllowed = (RadioButton) findViewById(R.id.presentation_allowed);
- mPresentationPayphone = (RadioButton) findViewById(R.id.presentation_payphone);
- mPresentationUnknown = (RadioButton) findViewById(R.id.presentation_unknown);
- mPresentationRestricted = (RadioButton) findViewById(R.id.presentation_restricted);
- mCallTime = (TextView) findViewById(R.id.call_time);
- mCallDate = (TextView) findViewById(R.id.call_date);
- mPhoneNumber = (TextView) findViewById(R.id.phone_number);
- mOffset = (EditText) findViewById(R.id.delta_after_add);
- mAccount0 = (RadioButton) findViewById(R.id.account0);
- mAccount1 = (RadioButton) findViewById(R.id.account1);
-
- mCustomCallTypeTextView.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- // Do nothing.
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- // Toggle the custom call type radio button if the text is changed/focused.
- mCallTypeCustom.toggle();
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- // Do nothing.
- }
- });
-
- // Use the current time as the default values for the picker
- final Calendar c = Calendar.getInstance();
- mCallTimeHour = c.get(Calendar.HOUR_OF_DAY);
- mCallTimeMinute = c.get(Calendar.MINUTE);
- mCallDateYear = c.get(Calendar.YEAR);
- mCallDateMonth = c.get(Calendar.MONTH);
- mCallDateDay = c.get(Calendar.DAY_OF_MONTH);
- setDisplayDate();
- setDisplayTime();
- }
-
- /**
- * Adds a number of entries to the call log. The content of the entries is based on existing
- * entries.
- *
- * @param count the number of entries to add
- */
- private void addEntriesToCallLog(final int count, boolean useRandomNumbers) {
- if (useRandomNumbers) {
- addRandomNumbers(count);
- } else {
- getLoaderManager().initLoader(CALLLOG_LOADER_ID, null,
- new CallLogLoaderListener(count));
- }
- }
-
- /**
- * Calls when the insertion has completed.
- *
- * @param message the message to show in a toast to the user
- */
- private void insertCompleted(String message) {
- // Hide the progress bar.
- mProgressBar.setVisibility(View.GONE);
- // Re-enable the add button.
- mNumberTextView.setEnabled(true);
- mAddButton.setEnabled(true);
- mNumberTextView.setText("");
- Toast.makeText(this, message, Toast.LENGTH_LONG).show();
- }
-
-
- /**
- * Creates a {@link ContentValues} object containing values corresponding to the given cursor.
- *
- * @param cursor the cursor from which to get the values
- * @return a newly created content values object
- */
- private ContentValues createContentValuesFromCursor(Cursor cursor) {
- ContentValues values = new ContentValues();
- for (int column = 0; column < cursor.getColumnCount();
- ++column) {
- String name = cursor.getColumnName(column);
- switch (cursor.getType(column)) {
- case Cursor.FIELD_TYPE_STRING:
- values.put(name, cursor.getString(column));
- break;
- case Cursor.FIELD_TYPE_INTEGER:
- values.put(name, cursor.getLong(column));
- break;
- case Cursor.FIELD_TYPE_FLOAT:
- values.put(name, cursor.getDouble(column));
- break;
- case Cursor.FIELD_TYPE_BLOB:
- values.put(name, cursor.getBlob(column));
- break;
- case Cursor.FIELD_TYPE_NULL:
- values.putNull(name);
- break;
- default:
- Log.d(TAG, "Invalid value in cursor: " + cursor.getType(column));
- break;
- }
- }
- return values;
- }
-
- private void addRandomNumbers(int count) {
- ContentValues[] values = new ContentValues[count];
- for (int i = 0; i < count; i++) {
- values[i] = new ContentValues();
- values[i].put(Calls.NUMBER, generateRandomNumber());
- values[i].put(Calls.NUMBER_PRESENTATION, Calls.PRESENTATION_ALLOWED);
- values[i].put(Calls.DATE, System.currentTimeMillis()); // Will be randomized later
- values[i].put(Calls.DURATION, 1); // Will be overwritten later
- }
- new AsyncCallLogInserter(values).execute(new Void[0]);
- }
-
- private static String generateRandomNumber() {
- return String.format("5%09d", RNG.nextInt(1000000000));
- }
-
- /** Invokes {@link AsyncCallLogInserter} when the call log has loaded. */
- private final class CallLogLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
- /** The number of items to insert when done. */
- private final int mCount;
-
- private CallLogLoaderListener(int count) {
- mCount = count;
- }
-
- @Override
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- Log.d(TAG, "onCreateLoader");
- return new CursorLoader(FillCallLogTestActivity.this, Calls.CONTENT_URI,
- null, null, null, null);
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- try {
- Log.d(TAG, "onLoadFinished");
-
- if (data.getCount() == 0) {
- // If there are no entries in the call log, we cannot generate new ones.
- insertCompleted(getString(R.string.noLogEntriesToast));
- return;
- }
-
- data.moveToPosition(-1);
-
- ContentValues[] values = new ContentValues[mCount];
- for (int index = 0; index < mCount; ++index) {
- if (!data.moveToNext()) {
- data.moveToFirst();
- }
- values[index] = createContentValuesFromCursor(data);
- }
- new AsyncCallLogInserter(values).execute(new Void[0]);
- } finally {
- // This is a one shot loader.
- getLoaderManager().destroyLoader(CALLLOG_LOADER_ID);
- }
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {}
- }
-
- /** Inserts a given number of entries in the call log based on the values given. */
- private final class AsyncCallLogInserter extends AsyncTask<Void, Integer, Integer> {
- /** The number of items to insert. */
- private final ContentValues[] mValues;
-
- public AsyncCallLogInserter(ContentValues[] values) {
- mValues = values;
- }
-
- @Override
- protected Integer doInBackground(Void... params) {
- Log.d(TAG, "doInBackground");
- return insertIntoCallLog();
- }
-
- @Override
- protected void onProgressUpdate(Integer... values) {
- Log.d(TAG, "onProgressUpdate");
- updateCount(values[0]);
- }
-
- @Override
- protected void onPostExecute(Integer count) {
- Log.d(TAG, "onPostExecute");
- insertCompleted(getString(R.string.addedLogEntriesToast, count));
- }
-
- /**
- * Inserts a number of entries in the call log based on the given templates.
- *
- * @return the number of inserted entries
- */
- private Integer insertIntoCallLog() {
- int inserted = 0;
-
- for (int index = 0; index < mValues.length; ++index) {
- ContentValues values = mValues[index];
- // These should not be set.
- values.putNull(Calls._ID);
- // Add some randomness to the date. For each new entry being added, add an extra
- // day to the maximum possible offset from the original.
- values.put(Calls.DATE,
- values.getAsLong(Calls.DATE)
- - RNG.nextInt(24 * 60 * 60 * (index + 1)) * 1000L);
- // Add some randomness to the duration.
- if (values.getAsLong(Calls.DURATION) > 0) {
- values.put(Calls.DURATION, RNG.nextInt(30 * 60 * 60 * 1000));
- }
-
- // Overwrite type.
- values.put(Calls.TYPE, CALL_TYPES[RNG.nextInt(CALL_TYPES.length)]);
-
- // Clear cached columns.
- values.putNull(Calls.CACHED_FORMATTED_NUMBER);
- values.putNull(Calls.CACHED_LOOKUP_URI);
- values.putNull(Calls.CACHED_MATCHED_NUMBER);
- values.putNull(Calls.CACHED_NAME);
- values.putNull(Calls.CACHED_NORMALIZED_NUMBER);
- values.putNull(Calls.CACHED_NUMBER_LABEL);
- values.putNull(Calls.CACHED_NUMBER_TYPE);
- values.putNull(Calls.CACHED_PHOTO_ID);
-
- // Insert into the call log the newly generated entry.
- ContentProviderClient contentProvider =
- getContentResolver().acquireContentProviderClient(
- Calls.CONTENT_URI);
- try {
- Log.d(TAG, "adding entry to call log");
- contentProvider.insert(Calls.CONTENT_URI, values);
- ++inserted;
- this.publishProgress(inserted);
- } catch (RemoteException e) {
- Log.d(TAG, "insert failed", e);
- }
- }
- return inserted;
- }
- }
-
- /**
- * Updates the count shown to the user corresponding to the number of entries added.
- *
- * @param count the number of entries inserted so far
- */
- public void updateCount(Integer count) {
- mProgressBar.setProgress(count);
- }
-
- /**
- * Determines the call type for a manually entered call.
- *
- * @return Call type.
- */
- private int getManualCallType() {
- if (mCallTypeIncoming.isChecked()) {
- return AppCompatConstants.CALLS_INCOMING_TYPE;
- } else if (mCallTypeOutgoing.isChecked()) {
- return AppCompatConstants.CALLS_OUTGOING_TYPE;
- } else if (mCallTypeVoicemail.isChecked()) {
- return AppCompatConstants.CALLS_VOICEMAIL_TYPE;
- } else if (mCallTypeCustom.isChecked()) {
- return Integer.parseInt(mCustomCallTypeTextView.getText().toString());
- } else {
- return AppCompatConstants.CALLS_MISSED_TYPE;
- }
- }
-
- /**
- * Determines the presentation for a manually entered call.
- *
- * @return Presentation.
- */
- private int getManualPresentation() {
- if (mPresentationAllowed.isChecked()) {
- return Calls.PRESENTATION_ALLOWED;
- } else if (mPresentationPayphone.isChecked()) {
- return Calls.PRESENTATION_PAYPHONE;
- } else if (mPresentationRestricted.isChecked()) {
- return Calls.PRESENTATION_RESTRICTED;
- } else {
- return Calls.PRESENTATION_UNKNOWN;
- }
- }
-
- private PhoneAccountHandle getManualAccount() {
- List <PhoneAccountHandle> accountHandles = TelecomUtil.getCallCapablePhoneAccounts(this);
- //TODO: hide the corresponding radio buttons if no accounts are available.
- if (mAccount0.isChecked()) {
- return accountHandles.get(0);
- } else if (mAccount1.isChecked()){
- return accountHandles.get(1);
- } else {
- return null;
- }
- }
-
- /**
- * Shows a time picker dialog, storing the results in the time field.
- */
- public void showTimePickerDialog(View v) {
- DialogFragment newFragment = new TimePickerFragment();
- newFragment.show(getFragmentManager(),"timePicker");
- }
-
- /**
- * Helper class to display time picker and store the hour/minute.
- */
- public class TimePickerFragment extends DialogFragment
- implements TimePickerDialog.OnTimeSetListener {
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- // Create a new instance of TimePickerDialog and return it
- return new TimePickerDialog(getActivity(), this, mCallTimeHour, mCallTimeMinute,
- DateFormat.is24HourFormat(getActivity()));
- }
-
- public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
- mCallTimeHour = hourOfDay;
- mCallTimeMinute = minute;
- setDisplayTime();
- }
- }
-
- /**
- * Sets the call time TextView to the current selected time.
- */
- private void setDisplayTime() {
- mCallTime.setText(String.format("%02d:%02d", mCallTimeHour, mCallTimeMinute));
- }
-
- /**
- * Sets the call date Textview to the current selected date
- */
- private void setDisplayDate() {
- mCallDate.setText(String.format("%04d-%02d-%02d", mCallDateYear, mCallDateMonth,
- mCallDateDay));
- }
-
- /**
- * Shows a date picker dialog.
- */
- public void showDatePickerDialog(View v) {
- DialogFragment newFragment = new DatePickerFragment();
- newFragment.show(getFragmentManager(),"datePicker");
- }
-
- /**
- * Helper class to show a date picker.
- */
- public class DatePickerFragment extends DialogFragment
- implements DatePickerDialog.OnDateSetListener {
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- // Create a new instance of DatePickerDialog and return it
- return new DatePickerDialog(getActivity(), this, mCallDateYear, mCallDateMonth,
- mCallDateDay);
- }
-
- public void onDateSet(DatePicker view, int year, int month, int day) {
- mCallDateYear = year;
- mCallDateMonth = month;
- mCallDateDay = day;
- setDisplayDate();
- }
- }
-
- /**
- * OnClick handler for the button that adds a manual call log entry to the call log.
- *
- * @param v Calling view.
- */
- public void addManualEntry(View v) {
- Calendar dateTime = Calendar.getInstance();
- dateTime.set(mCallDateYear, mCallDateMonth, mCallDateDay, mCallTimeHour, mCallTimeMinute);
-
- int features = mCallTypeVideo.isChecked() ? Calls.FEATURES_VIDEO : 0;
- Long dataUsage = null;
- if (mCallTypeVideo.isChecked()) {
- // Some random data usage up to 50MB.
- dataUsage = (long) RNG.nextInt(52428800);
- }
-
- if (getManualCallType() == AppCompatConstants.CALLS_VOICEMAIL_TYPE) {
- addManualVoicemail(dateTime.getTimeInMillis());
- } else {
- addCall(mPhoneNumber.getText().toString(), getManualPresentation(),
- getManualCallType(), features, getManualAccount(),
- dateTime.getTimeInMillis(), RNG.nextInt(60 * 60), dataUsage);
- }
-
- // Subtract offset from the call date/time and store as new date/time
- int offset = Integer.parseInt(mOffset.getText().toString());
-
- dateTime.add(Calendar.MINUTE, offset);
- mCallDateYear = dateTime.get(Calendar.YEAR);
- mCallDateMonth = dateTime.get(Calendar.MONTH);
- mCallDateDay = dateTime.get(Calendar.DAY_OF_MONTH);
- mCallTimeHour = dateTime.get(Calendar.HOUR_OF_DAY);
- mCallTimeMinute = dateTime.get(Calendar.MINUTE);
- setDisplayDate();
- setDisplayTime();
- }
-
- // Copied and modified to compile unbundled from android.provider.CallLog
- public Uri addCall(String number,
- int presentation, int callType, int features, PhoneAccountHandle accountHandle,
- long start, int duration, Long dataUsage) {
- final ContentResolver resolver = getContentResolver();
- int numberPresentation = Calls.PRESENTATION_ALLOWED;
-
- String accountAddress = null;
- if (accountHandle != null) {
- PhoneAccount account = TelecomUtil.getPhoneAccount(this, accountHandle);
- if (account != null) {
- Uri address = account.getSubscriptionAddress();
- if (address != null) {
- accountAddress = address.getSchemeSpecificPart();
- }
- }
- }
-
- if (numberPresentation != Calls.PRESENTATION_ALLOWED) {
- number = "";
- }
-
- // accountHandle information
- String accountComponentString = null;
- String accountId = null;
- if (accountHandle != null) {
- accountComponentString = accountHandle.getComponentName().flattenToString();
- accountId = accountHandle.getId();
- }
-
- ContentValues values = new ContentValues(6);
-
- values.put(Calls.NUMBER, number);
- values.put(Calls.NUMBER_PRESENTATION, Integer.valueOf(numberPresentation));
- values.put(Calls.TYPE, Integer.valueOf(callType));
- values.put(Calls.FEATURES, features);
- values.put(Calls.DATE, Long.valueOf(start));
- values.put(Calls.DURATION, Long.valueOf(duration));
- if (dataUsage != null) {
- values.put(Calls.DATA_USAGE, dataUsage);
- }
- values.put(Calls.PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString);
- values.put(Calls.PHONE_ACCOUNT_ID, accountId);
- // Calls.PHONE_ACCOUNT_ADDRESS
- values.put("phone_account_address", accountAddress);
- values.put(Calls.NEW, Integer.valueOf(1));
-
- if (callType == AppCompatConstants.CALLS_MISSED_TYPE) {
- values.put(Calls.IS_READ, 0);
- }
-
- return addEntryAndRemoveExpiredEntries(this, Calls.CONTENT_URI, values);
- }
-
- // Copied from android.provider.CallLog
- private static Uri addEntryAndRemoveExpiredEntries(Context context, Uri uri,
- ContentValues values) {
- final ContentResolver resolver = context.getContentResolver();
- Uri result = resolver.insert(uri, values);
- resolver.delete(uri, "_id IN " +
- "(SELECT _id FROM calls ORDER BY " + Calls.DEFAULT_SORT_ORDER
- + " LIMIT -1 OFFSET 500)", null);
- return result;
- }
-
- private void addManualVoicemail(Long time) {
- final ContentValues contentValues = new ContentValues();
- contentValues.put(Voicemails.DATE, time);
- contentValues.put(Voicemails.NUMBER, mPhoneNumber.getText().toString());
- contentValues.put(Voicemails.DURATION, 5000);
- contentValues.put(Voicemails.SOURCE_PACKAGE, getPackageName());
- contentValues.put(Voicemails.SOURCE_DATA, 500);
- contentValues.put(Voicemails.IS_READ, 0);
-
- getContentResolver().insert(VoicemailContract.Voicemails.buildSourceUri(getPackageName()),
- contentValues);
-
- updateVoicemailStatus();
- }
-
- private void updateVoicemailStatus() {
- ContentResolver contentResolver = getContentResolver();
- Uri statusUri = VoicemailContract.Status.buildSourceUri(getPackageName());
- final PhoneAccountHandle accountHandle = getManualAccount();
-
- ContentValues values = new ContentValues();
- values.put(Status.PHONE_ACCOUNT_COMPONENT_NAME, getPackageName());
- values.put(Status.PHONE_ACCOUNT_ID, "ACCOUNT_ID");
- values.put(Status.CONFIGURATION_STATE, VoicemailContract.Status.CONFIGURATION_STATE_OK);
- values.put(Status.DATA_CHANNEL_STATE, VoicemailContract.Status.DATA_CHANNEL_STATE_OK);
- values.put(Status.NOTIFICATION_CHANNEL_STATE,
- VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_OK);
-
- contentResolver.insert(statusUri, values);
- }
-}
diff --git a/tests/src/com/android/dialer/util/DialerUtilsTest.java b/tests/src/com/android/dialer/util/DialerUtilsTest.java
deleted file mode 100644
index ccd6dfdd5..000000000
--- a/tests/src/com/android/dialer/util/DialerUtilsTest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2014 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.dialer.util;
-
-import com.android.dialer.calllog.PhoneCallDetailsHelper;
-import com.google.common.collect.Lists;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.util.ArrayList;
-
-/**
- * Performs tests of the DialerUtils class.
- */
-@SmallTest
-public class DialerUtilsTest extends AndroidTestCase {
-
- private Resources mResources;
-
- /**
- * List of items to be concatenated together for CharSequence join tests.
- */
- private ArrayList<CharSequence> mItems = Lists.newArrayList();
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- Context context = getContext();
- mResources = context.getResources();
- }
-
- /**
- * Tests joining an empty list of {@link CharSequence}.
- */
- public void testJoinEmpty() {
- mItems.clear();
- CharSequence joined = DialerUtils.join(mResources, mItems);
- assertEquals("", joined);
- }
-
- /**
- * Tests joining a list of {@link CharSequence} with a single entry.
- */
- public void testJoinOne() {
- mItems.clear();
- mItems.add("Hello");
- CharSequence joined = DialerUtils.join(mResources, mItems);
- assertEquals("Hello", joined);
- }
-
- /**
- * Tests joining a list of {@link CharSequence} with a multiple entries.
- */
- public void testJoinTwo() {
- mItems.clear();
- mItems.add("Hello");
- mItems.add("there");
- CharSequence joined = DialerUtils.join(mResources, mItems);
- assertEquals("Hello, there", joined);
- }
-}
diff --git a/tests/src/com/android/dialer/util/ExpirableCacheTest.java b/tests/src/com/android/dialer/util/ExpirableCacheTest.java
deleted file mode 100644
index b81ad754f..000000000
--- a/tests/src/com/android/dialer/util/ExpirableCacheTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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.dialer.util;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.LruCache;
-
-import com.android.dialer.util.ExpirableCache.CachedValue;
-
-/**
- * Unit tests for {@link ExpirableCache}.
- */
-@SmallTest
-public class ExpirableCacheTest extends AndroidTestCase {
- /** The object under test. */
- private ExpirableCache<String, Integer> mCache;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- LruCache<String, CachedValue<Integer>> lruCache =
- new LruCache<String, ExpirableCache.CachedValue<Integer>>(20);
- mCache = ExpirableCache.create(lruCache);
- }
-
- @Override
- protected void tearDown() throws Exception {
- mCache = null;
- super.tearDown();
- }
-
- public void testPut() {
- mCache.put("a", 1);
- mCache.put("b", 2);
- assertEquals(1, mCache.getPossiblyExpired("a").intValue());
- assertEquals(2, mCache.getPossiblyExpired("b").intValue());
- mCache.put("a", 3);
- assertEquals(3, mCache.getPossiblyExpired("a").intValue());
- }
-
- public void testGet_NotExisting() {
- assertNull(mCache.getPossiblyExpired("a"));
- mCache.put("b", 1);
- assertNull(mCache.getPossiblyExpired("a"));
- }
-
- public void testGet_Expired() {
- mCache.put("a", 1);
- assertEquals(1, mCache.getPossiblyExpired("a").intValue());
- mCache.expireAll();
- assertEquals(1, mCache.getPossiblyExpired("a").intValue());
- }
-
- public void testGetNotExpired_NotExisting() {
- assertNull(mCache.get("a"));
- mCache.put("b", 1);
- assertNull(mCache.get("a"));
- }
-
- public void testGetNotExpired_Expired() {
- mCache.put("a", 1);
- assertEquals(1, mCache.get("a").intValue());
- mCache.expireAll();
- assertNull(mCache.get("a"));
- }
-
- public void testGetCachedValue_NotExisting() {
- assertNull(mCache.getCachedValue("a"));
- mCache.put("b", 1);
- assertNull(mCache.getCachedValue("a"));
- }
-
- public void testGetCachedValue_Expired() {
- mCache.put("a", 1);
- assertFalse("Should not be expired", mCache.getCachedValue("a").isExpired());
- mCache.expireAll();
- assertTrue("Should be expired", mCache.getCachedValue("a").isExpired());
- }
-
- public void testGetChangedValue_PutAfterExpired() {
- mCache.put("a", 1);
- mCache.expireAll();
- mCache.put("a", 1);
- assertFalse("Should not be expired", mCache.getCachedValue("a").isExpired());
- }
-
- public void testComputingCache() {
- // Creates a cache in which all unknown values default to zero.
- mCache = ExpirableCache.create(
- new LruCache<String, ExpirableCache.CachedValue<Integer>>(10) {
- @Override
- protected CachedValue<Integer> create(String key) {
- return mCache.newCachedValue(0);
- }
- });
-
- // The first time we request a new value, we add it to the cache.
- CachedValue<Integer> cachedValue = mCache.getCachedValue("a");
- assertNotNull("Should have been created implicitly", cachedValue);
- assertEquals(0, cachedValue.getValue().intValue());
- assertFalse("Should not be expired", cachedValue.isExpired());
-
- // If we expire all the values, the implicitly created value will also be marked as expired.
- mCache.expireAll();
- CachedValue<Integer> expiredCachedValue = mCache.getCachedValue("a");
- assertNotNull("Should have been created implicitly", expiredCachedValue);
- assertEquals(0, expiredCachedValue.getValue().intValue());
- assertTrue("Should be expired", expiredCachedValue.isExpired());
- }
-}
diff --git a/tests/src/com/android/dialer/util/FakeAsyncTaskExecutor.java b/tests/src/com/android/dialer/util/FakeAsyncTaskExecutor.java
deleted file mode 100644
index 52cdf7e77..000000000
--- a/tests/src/com/android/dialer/util/FakeAsyncTaskExecutor.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * 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.dialer.util;
-
-import android.app.Instrumentation;
-import android.os.AsyncTask;
-
-import com.google.common.collect.Lists;
-
-import junit.framework.Assert;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
-
-import javax.annotation.concurrent.GuardedBy;
-import javax.annotation.concurrent.ThreadSafe;
-
-/**
- * Test implementation of AsyncTaskExecutor.
- * <p>
- * This class is thread-safe. As per the contract of the AsyncTaskExecutor, the submit methods must
- * be called from the main ui thread, however the other public methods may be called from any thread
- * (most commonly the test thread).
- * <p>
- * Tasks submitted to this executor will not be run immediately. Rather they will be stored in a
- * list of submitted tasks, where they can be examined. They can also be run on-demand using the run
- * methods, so that different ordering of AsyncTask execution can be simulated.
- * <p>
- * The onPreExecute method of the submitted AsyncTask will be called synchronously during the
- * call to {@link #submit(Object, AsyncTask, Object...)}.
- */
-@ThreadSafe
-public class FakeAsyncTaskExecutor implements AsyncTaskExecutor {
- private static final long DEFAULT_TIMEOUT_MS = 10000;
-
- /** The maximum length of time in ms to wait for tasks to execute during tests. */
- private final long mTimeoutMs = DEFAULT_TIMEOUT_MS;
-
- private final Object mLock = new Object();
- @GuardedBy("mLock") private final List<SubmittedTask> mSubmittedTasks = Lists.newArrayList();
-
- private final DelayedExecutor mBlockingExecutor = new DelayedExecutor();
- private final Instrumentation mInstrumentation;
-
- /** Create a fake AsyncTaskExecutor for use in unit tests. */
- public FakeAsyncTaskExecutor(Instrumentation instrumentation) {
- Assert.assertNotNull(instrumentation);
- mInstrumentation = instrumentation;
- }
-
- /** Encapsulates an async task with the params and identifier it was submitted with. */
- public interface SubmittedTask {
- Runnable getRunnable();
- Object getIdentifier();
- AsyncTask<?, ?, ?> getAsyncTask();
- }
-
- private static final class SubmittedTaskImpl implements SubmittedTask {
- private final Object mIdentifier;
- private final Runnable mRunnable;
- private final AsyncTask<?, ?, ?> mAsyncTask;
-
- public SubmittedTaskImpl(Object identifier, Runnable runnable,
- AsyncTask<?, ?, ?> asyncTask) {
- mIdentifier = identifier;
- mRunnable = runnable;
- mAsyncTask = asyncTask;
- }
-
- @Override
- public Object getIdentifier() {
- return mIdentifier;
- }
-
- @Override
- public Runnable getRunnable() {
- return mRunnable;
- }
-
- @Override
- public AsyncTask<?, ?, ?> getAsyncTask() {
- return mAsyncTask;
- }
-
- @Override
- public String toString() {
- return "SubmittedTaskImpl [mIdentifier=" + mIdentifier + "]";
- }
- }
-
- private class DelayedExecutor implements Executor {
- private final Object mNextLock = new Object();
- @GuardedBy("mNextLock") private Object mNextIdentifier;
- @GuardedBy("mNextLock") private AsyncTask<?, ?, ?> mNextTask;
-
- @Override
- public void execute(Runnable command) {
- synchronized (mNextLock) {
- Assert.assertNotNull(mNextTask);
- mSubmittedTasks.add(new SubmittedTaskImpl(mNextIdentifier,
- command, mNextTask));
- mNextIdentifier = null;
- mNextTask = null;
- }
- }
-
- public <T> AsyncTask<T, ?, ?> submit(Object identifier,
- AsyncTask<T, ?, ?> task, T... params) {
- synchronized (mNextLock) {
- Assert.assertNull(mNextIdentifier);
- Assert.assertNull(mNextTask);
- mNextIdentifier = identifier;
- Assert.assertNotNull("Already had a valid task.\n"
- + "Are you calling AsyncTaskExecutor.submit(...) from within the "
- + "onPreExecute() method of another task being submitted?\n"
- + "Sorry! Not that's not supported.", task);
- mNextTask = task;
- }
- return task.executeOnExecutor(this, params);
- }
- }
-
- @Override
- public <T> AsyncTask<T, ?, ?> submit(Object identifier, AsyncTask<T, ?, ?> task, T... params) {
- AsyncTaskExecutors.checkCalledFromUiThread();
- return mBlockingExecutor.submit(identifier, task, params);
- }
-
- /**
- * Runs a single task matching the given identifier.
- * <p>
- * Removes the matching task from the list of submitted tasks, then runs it. The executor used
- * to execute this async task will be a same-thread executor.
- * <p>
- * Fails if there was not exactly one task matching the given identifier.
- * <p>
- * This method blocks until the AsyncTask has completely finished executing.
- */
- public void runTask(Object identifier) throws InterruptedException {
- List<SubmittedTask> tasks = getSubmittedTasksByIdentifier(identifier, true);
- Assert.assertEquals("Expected one task " + identifier + ", got " + tasks, 1, tasks.size());
- runTask(tasks.get(0));
- }
-
- /**
- * Runs all tasks whose identifier matches the given identifier.
- * <p>
- * Removes all matching tasks from the list of submitted tasks, and runs them. The executor used
- * to execute these async tasks will be a same-thread executor.
- * <p>
- * Fails if there were no tasks matching the given identifier.
- * <p>
- * This method blocks until the AsyncTask objects have completely finished executing.
- */
- public void runAllTasks(Object identifier) throws InterruptedException {
- List<SubmittedTask> tasks = getSubmittedTasksByIdentifier(identifier, true);
- Assert.assertTrue("There were no tasks with identifier " + identifier, tasks.size() > 0);
- for (SubmittedTask task : tasks) {
- runTask(task);
- }
- }
-
- /**
- * Executes a single {@link SubmittedTask}.
- * <p>
- * Blocks until the task has completed running.
- */
- private <T> void runTask(final SubmittedTask submittedTask) throws InterruptedException {
- submittedTask.getRunnable().run();
- // Block until the onPostExecute or onCancelled has finished.
- // Unfortunately we can't be sure when the AsyncTask will have posted its result handling
- // code to the main ui thread, the best we can do is wait for the Status to be FINISHED.
- final CountDownLatch latch = new CountDownLatch(1);
- class AsyncTaskHasFinishedRunnable implements Runnable {
- @Override
- public void run() {
- if (submittedTask.getAsyncTask().getStatus() == AsyncTask.Status.FINISHED) {
- latch.countDown();
- } else {
- mInstrumentation.waitForIdle(this);
- }
- }
- }
- mInstrumentation.waitForIdle(new AsyncTaskHasFinishedRunnable());
- Assert.assertTrue(latch.await(mTimeoutMs, TimeUnit.MILLISECONDS));
- }
-
- private List<SubmittedTask> getSubmittedTasksByIdentifier(
- Object identifier, boolean remove) {
- Assert.assertNotNull(identifier);
- List<SubmittedTask> results = Lists.newArrayList();
- synchronized (mLock) {
- Iterator<SubmittedTask> iter = mSubmittedTasks.iterator();
- while (iter.hasNext()) {
- SubmittedTask task = iter.next();
- if (identifier.equals(task.getIdentifier())) {
- results.add(task);
- iter.remove();
- }
- }
- }
- return results;
- }
-
- /** Get a factory that will return this instance - useful for testing. */
- public AsyncTaskExecutors.AsyncTaskExecutorFactory getFactory() {
- return new AsyncTaskExecutors.AsyncTaskExecutorFactory() {
- @Override
- public AsyncTaskExecutor createAsyncTaskExeuctor() {
- return FakeAsyncTaskExecutor.this;
- }
- };
- }
-}
diff --git a/tests/src/com/android/dialer/util/LocaleTestUtils.java b/tests/src/com/android/dialer/util/LocaleTestUtils.java
deleted file mode 100644
index b893ccb76..000000000
--- a/tests/src/com/android/dialer/util/LocaleTestUtils.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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.dialer.util;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-
-import java.util.Locale;
-
-/**
- * Utility class to save and restore the locale of the system.
- * <p>
- * This can be used for tests that assume to be run in a certain locale, e.g., because they
- * check against strings in a particular language or require an assumption on how the system
- * will behave in a specific locale.
- * <p>
- * In your test, you can change the locale with the following code:
- * <pre>
- * public class CanadaFrenchTest extends AndroidTestCase {
- * private LocaleTestUtils mLocaleTestUtils;
- *
- * &#64;Override
- * public void setUp() throws Exception {
- * super.setUp();
- * mLocaleTestUtils = new LocaleTestUtils(getContext());
- * mLocaleTestUtils.setLocale(Locale.CANADA_FRENCH);
- * }
- *
- * &#64;Override
- * public void tearDown() throws Exception {
- * mLocaleTestUtils.restoreLocale();
- * mLocaleTestUtils = null;
- * super.tearDown();
- * }
- *
- * ...
- * }
- * </pre>
- * Note that one should not call {@link #setLocale(Locale)} more than once without calling
- * {@link #restoreLocale()} first.
- * <p>
- * This class is not thread-safe. Usually its methods should be invoked only from the test thread.
- */
-public class LocaleTestUtils {
- private final Context mContext;
- private boolean mSaved;
- private Locale mSavedContextLocale;
- private Locale mSavedSystemLocale;
-
- /**
- * Create a new instance that can be used to set and reset the locale for the given context.
- *
- * @param context the context on which to alter the locale
- */
- public LocaleTestUtils(Context context) {
- mContext = context;
- mSaved = false;
- }
-
- /**
- * Set the locale to the given value and saves the previous value.
- *
- * @param locale the value to which the locale should be set
- * @throws IllegalStateException if the locale was already set
- */
- public void setLocale(Locale locale) {
- if (mSaved) {
- throw new IllegalStateException(
- "call restoreLocale() before calling setLocale() again");
- }
- mSavedContextLocale = setResourcesLocale(mContext.getResources(), locale);
- mSavedSystemLocale = setResourcesLocale(Resources.getSystem(), locale);
- mSaved = true;
- }
-
- /**
- * Restores the previously set locale.
- *
- * @throws IllegalStateException if the locale was not set using {@link #setLocale(Locale)}
- */
- public void restoreLocale() {
- if (!mSaved) {
- throw new IllegalStateException("call setLocale() before calling restoreLocale()");
- }
- setResourcesLocale(mContext.getResources(), mSavedContextLocale);
- setResourcesLocale(Resources.getSystem(), mSavedSystemLocale);
- mSaved = false;
- }
-
- /**
- * Sets the locale for the given resources and returns the previous locale.
- *
- * @param resources the resources on which to set the locale
- * @param locale the value to which to set the locale
- * @return the previous value of the locale for the resources
- */
- private Locale setResourcesLocale(Resources resources, Locale locale) {
- Configuration contextConfiguration = new Configuration(resources.getConfiguration());
- Locale savedLocale = contextConfiguration.locale;
- contextConfiguration.locale = locale;
- resources.updateConfiguration(contextConfiguration, null);
- return savedLocale;
- }
-}
diff --git a/tests/src/com/android/dialer/util/TestConstants.java b/tests/src/com/android/dialer/util/TestConstants.java
deleted file mode 100644
index a3dd05166..000000000
--- a/tests/src/com/android/dialer/util/TestConstants.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.android.dialer.util;
-
-public class TestConstants {
- public static final String CALL_INTENT_ACTION = IntentUtil.CALL_ACTION;
-}
diff --git a/tests/src/com/android/dialer/voicemail/VoicemailActivityInstrumentationTestCase2.java b/tests/src/com/android/dialer/voicemail/VoicemailActivityInstrumentationTestCase2.java
deleted file mode 100644
index cabaf6732..000000000
--- a/tests/src/com/android/dialer/voicemail/VoicemailActivityInstrumentationTestCase2.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * 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.dialer.voicemail;
-
-import android.app.Activity;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.res.AssetManager;
-import android.net.Uri;
-import android.provider.VoicemailContract;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.Suppress;
-import android.view.View;
-
-import com.android.dialer.R;
-import com.android.dialer.util.AsyncTaskExecutors;
-import com.android.dialer.util.FakeAsyncTaskExecutor;
-import com.android.dialer.util.LocaleTestUtils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Locale;
-
-import static com.android.dialer.voicemail.VoicemailPlaybackPresenter.Tasks.CHECK_FOR_CONTENT;
-
-/**
- * Common methods and attributes between {@link VoicemailArchiveTest} and
- * {@link VoicemailPlaybackTest}.
- */
-public class VoicemailActivityInstrumentationTestCase2<T extends Activity>
- extends ActivityInstrumentationTestCase2<T> {
- protected static final String TEST_ASSET_NAME = "quick_test_recording.mp3";
- protected static final String MIME_TYPE = "audio/mp3";
- protected static final String CONTACT_NUMBER = "+1412555555";
- protected static final String VOICEMAIL_FILE_LOCATION = "/sdcard/sadlfj893w4j23o9sfu.mp3";
-
- private T mActivity;
- protected VoicemailPlaybackPresenter mPresenter;
- private VoicemailPlaybackLayout mLayout;
-
- protected Uri mVoicemailUri;
- private LocaleTestUtils mLocaleTestUtils;
- protected FakeAsyncTaskExecutor mFakeAsyncTaskExecutor;
-
- public VoicemailActivityInstrumentationTestCase2(Class<T> activityClass) {
- super(activityClass);
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
-
- mFakeAsyncTaskExecutor = new FakeAsyncTaskExecutor(getInstrumentation());
- AsyncTaskExecutors.setFactoryForTest(mFakeAsyncTaskExecutor.getFactory());
-
- // Some of the tests rely on the text - safest to force a specific locale.
- mLocaleTestUtils = new LocaleTestUtils(getInstrumentation().getTargetContext());
- mLocaleTestUtils.setLocale(Locale.US);
-
- mActivity = getActivity();
- mLayout = new VoicemailPlaybackLayout(mActivity);
- mLayout.onFinishInflate();
- }
-
- @Override
- protected void tearDown() throws Exception {
- cleanUpVoicemailUri();
-
- mLocaleTestUtils.restoreLocale();
- mLocaleTestUtils = null;
-
- mPresenter.clearInstance();
- AsyncTaskExecutors.setFactoryForTest(null);
-
- mActivity = null;
- mPresenter = null;
- mLayout = null;
-
- super.tearDown();
- }
-
- @Suppress
- public void testFetchingVoicemail() throws Throwable {
- setUriForUnfetchedVoicemailEntry();
- setPlaybackViewForPresenter();
-
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mPresenter.resumePlayback();
- assertStateTextContains("Loading voicemail");
- }
- });
- }
-
- @Suppress
- public void testInvalidVoicemailShowsErrorMessage() throws Throwable {
- setUriForInvalidVoicemailEntry();
- setPlaybackViewForPresenter();
-
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mPresenter.resumePlayback();
- }
- });
- mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT);
- getInstrumentation().waitForIdleSync();
-
- // The media player will have thrown an IOException since the file doesn't exist.
- // This should have put a failed to play message on screen, buffering is gone.
- assertStateTextContains("Couldn't play voicemail");
- assertStateTextNotContains("Buffering");
- }
-
- public void testClickingSpeakerphoneButton() throws Throwable {
- setUriForRealFileVoicemailEntry();
- setPlaybackViewForPresenter();
-
- // Check that the speakerphone is false to start.
- assertFalse(mPresenter.isSpeakerphoneOn());
-
- View speakerphoneButton = mLayout.findViewById(R.id.playback_speakerphone);
- speakerphoneButton.performClick();
- assertTrue(mPresenter.isSpeakerphoneOn());
- }
-
- protected void cleanUpVoicemailUri() {
- if (mVoicemailUri != null) {
- getContentResolver().delete(VoicemailContract.Voicemails.CONTENT_URI,
- "_ID = ?", new String[] { String.valueOf(ContentUris.parseId(mVoicemailUri)) });
- mVoicemailUri = null;
- }
- }
-
- protected void setUriForRealFileVoicemailEntry() throws IOException {
- assertNull(mVoicemailUri);
- ContentValues values = new ContentValues();
- values.put(VoicemailContract.Voicemails.DATE, String.valueOf(System.currentTimeMillis()));
- values.put(VoicemailContract.Voicemails.NUMBER, CONTACT_NUMBER);
- values.put(VoicemailContract.Voicemails.MIME_TYPE, MIME_TYPE);
- values.put(VoicemailContract.Voicemails.HAS_CONTENT, 1);
- String packageName = getInstrumentation().getTargetContext().getPackageName();
- mVoicemailUri = getContentResolver().insert(
- VoicemailContract.Voicemails.buildSourceUri(packageName), values);
- AssetManager assets = getAssets();
- try (InputStream inputStream = assets.open(TEST_ASSET_NAME);
- OutputStream outputStream = getContentResolver().openOutputStream(mVoicemailUri)) {
- copyBetweenStreams(inputStream, outputStream);
- }
- }
-
- protected void setUriForUnfetchedVoicemailEntry() {
- assertNull(mVoicemailUri);
- ContentValues values = new ContentValues();
- values.put(VoicemailContract.Voicemails.DATE, String.valueOf(System.currentTimeMillis()));
- values.put(VoicemailContract.Voicemails.NUMBER, CONTACT_NUMBER);
- values.put(VoicemailContract.Voicemails.MIME_TYPE, MIME_TYPE);
- values.put(VoicemailContract.Voicemails.HAS_CONTENT, 0);
- String packageName = getInstrumentation().getTargetContext().getPackageName();
- mVoicemailUri = getContentResolver().insert(
- VoicemailContract.Voicemails.buildSourceUri(packageName), values);
- }
-
- protected void setUriForInvalidVoicemailEntry() {
- assertNull(mVoicemailUri);
- ContentResolver contentResolver = getContentResolver();
- ContentValues values = new ContentValues();
- values.put(VoicemailContract.Voicemails.NUMBER, CONTACT_NUMBER);
- values.put(VoicemailContract.Voicemails.HAS_CONTENT, 1);
- // VoicemailContract.Voicemails._DATA
- values.put("_data", VOICEMAIL_FILE_LOCATION);
- mVoicemailUri = contentResolver.insert(VoicemailContract.Voicemails.CONTENT_URI, values);
- }
-
- protected void setPlaybackViewForPresenter() {
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mPresenter.setPlaybackView(mLayout, mVoicemailUri, false);
- }
- });
- }
-
- protected void copyBetweenStreams(InputStream in, OutputStream out) throws IOException {
- byte[] buffer = new byte[1024];
- int bytesRead;
- while ((bytesRead = in.read(buffer)) > 0) {
- out.write(buffer, 0, bytesRead);
- }
- }
-
- protected void assertStateTextContains(String text) {
- assertNotNull(mLayout);
- assertTrue(mLayout.getStateText().contains(text));
- }
-
- protected void assertStateTextNotContains(String text) {
- assertNotNull(mLayout);
- assertFalse(mLayout.getStateText().contains(text));
- }
-
- protected ContentResolver getContentResolver() {
- return getInstrumentation().getTargetContext().getContentResolver();
- }
-
- protected AssetManager getAssets() {
- return getInstrumentation().getContext().getAssets();
- }
-
-}
diff --git a/tests/src/com/android/dialer/voicemail/VoicemailArchiveTest.java b/tests/src/com/android/dialer/voicemail/VoicemailArchiveTest.java
deleted file mode 100644
index bbd5edc48..000000000
--- a/tests/src/com/android/dialer/voicemail/VoicemailArchiveTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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.dialer.voicemail;
-
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.res.AssetManager;
-
-import com.android.dialer.database.VoicemailArchiveContract.VoicemailArchive;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * Unit tests for {@link VoicemailArchiveActivity} and {@link VoicemailArchivePlaybackPresenter}.
- */
-public class VoicemailArchiveTest
- extends VoicemailActivityInstrumentationTestCase2<VoicemailArchiveActivity> {
-
- public VoicemailArchiveTest() {
- super(VoicemailArchiveActivity.class);
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mPresenter = VoicemailArchivePlaybackPresenter.getInstance(getActivity(), null);
- }
-
- @Override
- public void testFetchingVoicemail() throws Throwable {
- setUriForRealFileVoicemailEntry();
- setPlaybackViewForPresenter();
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mPresenter.checkForContent(
- new VoicemailPlaybackPresenter.OnContentCheckedListener() {
- @Override
- public void onContentChecked(boolean hasContent) {
- mPresenter.resumePlayback();
- assertEquals(true, mPresenter.isPlaying());
- }
- });
- }
- });
- }
-
- @Override
- public void testInvalidVoicemailShowsErrorMessage() throws Throwable {
- setUriForInvalidVoicemailEntry();
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mPresenter.checkForContent(
- new VoicemailPlaybackPresenter.OnContentCheckedListener() {
- @Override
- public void onContentChecked(boolean hasContent) {
- assertStateTextContains("Couldn't play voicemail");
- }
- });
- }
- });
- }
-
- @Override
- protected void setUriForInvalidVoicemailEntry() {
- assertNull(mVoicemailUri);
- ContentValues values = new ContentValues();
- values.put(VoicemailArchive.NUMBER, CONTACT_NUMBER);
- values.put(VoicemailArchive.DATE, String.valueOf(System.currentTimeMillis()));
- values.put(VoicemailArchive.MIME_TYPE, MIME_TYPE);
- values.put(VoicemailArchive._DATA, VOICEMAIL_FILE_LOCATION);
- mVoicemailUri = getContentResolver().insert(VoicemailArchive.CONTENT_URI, values);
- }
-
- @Override
- protected void setUriForRealFileVoicemailEntry() throws IOException {
- assertNull(mVoicemailUri);
- ContentValues values = new ContentValues();
- values.put(VoicemailArchive.DATE, String.valueOf(System.currentTimeMillis()));
- values.put(VoicemailArchive.NUMBER, CONTACT_NUMBER);
- values.put(VoicemailArchive.MIME_TYPE, MIME_TYPE);
- values.put(VoicemailArchive.DURATION, 0);
- mVoicemailUri = getContentResolver().insert(VoicemailArchive.CONTENT_URI, values);
- AssetManager assets = getAssets();
- try (InputStream inputStream = assets.open(TEST_ASSET_NAME);
- OutputStream outputStream = getContentResolver().openOutputStream(mVoicemailUri)) {
- copyBetweenStreams(inputStream, outputStream);
- }
- }
-
- @Override
- protected void cleanUpVoicemailUri() {
- if (mVoicemailUri != null) {
- getContentResolver().delete(VoicemailArchive.CONTENT_URI,
- "_ID = ?", new String[] { String.valueOf(ContentUris.parseId(mVoicemailUri)) });
- mVoicemailUri = null;
- }
- }
-}
diff --git a/tests/src/com/android/dialer/voicemail/VoicemailAsyncTaskUtilTest.java b/tests/src/com/android/dialer/voicemail/VoicemailAsyncTaskUtilTest.java
deleted file mode 100644
index d98d9fa59..000000000
--- a/tests/src/com/android/dialer/voicemail/VoicemailAsyncTaskUtilTest.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * 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.dialer.voicemail;
-
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.res.AssetFileDescriptor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
-import android.provider.CallLog;
-import android.provider.VoicemailContract;
-import android.test.InstrumentationTestCase;
-import android.test.mock.MockContentResolver;
-
-import com.android.contacts.common.test.mocks.MockContentProvider;
-import com.android.contacts.common.test.mocks.MockContentProvider.Query;
-import com.android.dialer.calllog.CallLogQuery;
-import com.android.dialer.database.VoicemailArchiveContract;
-import com.android.dialer.util.AsyncTaskExecutors;
-import com.android.dialer.util.FakeAsyncTaskExecutor;
-import com.android.dialer.voicemail.VoicemailAsyncTaskUtil.OnGetArchivedVoicemailFilePathListener;
-import com.android.dialer.voicemail.VoicemailAsyncTaskUtil.OnArchiveVoicemailListener;
-import com.android.dialer.voicemail.VoicemailAsyncTaskUtil.OnSetVoicemailArchiveStatusListener;
-
-import org.junit.Rule;
-import org.junit.rules.TemporaryFolder;
-import static org.mockito.Mockito.*;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-public class VoicemailAsyncTaskUtilTest extends InstrumentationTestCase {
- private static final String TEST_MIME_TYPE = "audio/mp3";
- private static final String TEST_NUMBER = "+1412555555";
- private static final String TEST_FILE_PATH = "TEST_PATH";
- private static final String TEST_TRANSCRIPTION = "TEST_TRANSCRIPTION";
- private static final long TEST_DATE = 0;
- private static final long TEST_DURATION = 0;
- private static final int TEST_SERVER_ID = 1;
- private static final int TEST_ID = 2;
- private static final Uri ARCHIVED_VOICEMAIL_URI =
- VoicemailArchiveContract.VoicemailArchive.buildWithId(TEST_ID);
- private static final Uri VOICEMAIL_URI = VoicemailContract.Voicemails.CONTENT_URI
- .buildUpon().appendPath(Integer.toString(TEST_SERVER_ID)).build();
- private static final String[] CALLLOG_QUERY_PROJECTION = new String[] {
- CallLog.Calls._ID,
- CallLog.Calls.NUMBER,
- CallLog.Calls.DATE,
- CallLog.Calls.DURATION,
- CallLog.Calls.TYPE,
- CallLog.Calls.COUNTRY_ISO,
- CallLog.Calls.VOICEMAIL_URI,
- CallLog.Calls.GEOCODED_LOCATION,
- CallLog.Calls.CACHED_NAME,
- CallLog.Calls.CACHED_NUMBER_TYPE,
- CallLog.Calls.CACHED_NUMBER_LABEL,
- CallLog.Calls.CACHED_LOOKUP_URI,
- CallLog.Calls.CACHED_MATCHED_NUMBER,
- CallLog.Calls.CACHED_NORMALIZED_NUMBER,
- CallLog.Calls.CACHED_PHOTO_ID,
- CallLog.Calls.CACHED_FORMATTED_NUMBER,
- CallLog.Calls.IS_READ,
- CallLog.Calls.NUMBER_PRESENTATION,
- CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME,
- CallLog.Calls.PHONE_ACCOUNT_ID,
- CallLog.Calls.FEATURES,
- CallLog.Calls.DATA_USAGE,
- CallLog.Calls.TRANSCRIPTION,
- CallLog.Calls.CACHED_PHOTO_URI
- };
- private static final String[] VOICEMAIL_PROJECTION = new String[] {
- VoicemailContract.Voicemails._ID,
- VoicemailContract.Voicemails.NUMBER,
- VoicemailContract.Voicemails.DATE,
- VoicemailContract.Voicemails.DURATION,
- VoicemailContract.Voicemails.MIME_TYPE,
- VoicemailContract.Voicemails.TRANSCRIPTION,
- };
-
- private final MockContentResolver mContentResolver = new MockContentResolver();
- private final MockContentProvider mArchiveContentProvider = new TestVoicemailContentProvider();
- private final MockContentProvider mVoicemailContentProvider =
- new TestVoicemailContentProvider();
- private final MockContentProvider mCallLogContentProvider = new MockContentProvider();
- @Rule
- private final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
-
- private VoicemailAsyncTaskUtil mVoicemailAsyncTaskUtil;
- private FakeAsyncTaskExecutor mFakeAsyncTaskExecutor;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mFakeAsyncTaskExecutor = new FakeAsyncTaskExecutor(getInstrumentation());
- AsyncTaskExecutors.setFactoryForTest(mFakeAsyncTaskExecutor.getFactory());
- mContentResolver.addProvider(VoicemailArchiveContract.AUTHORITY, mArchiveContentProvider);
- mContentResolver.addProvider(VoicemailContract.AUTHORITY, mVoicemailContentProvider);
- mContentResolver.addProvider(CallLog.AUTHORITY, mCallLogContentProvider);
- mVoicemailAsyncTaskUtil = new VoicemailAsyncTaskUtil(mContentResolver);
- }
-
- @Override
- protected void tearDown() throws Exception {
- AsyncTaskExecutors.setFactoryForTest(null);
- super.tearDown();
- }
-
- public void testGetVoicemailFilePath_VoicemailExists() throws Throwable {
- newVoicemailArchiveQuery().withAnyProjection()
- .returnRow(getCombinedContentValuesWithData());
- final OnGetArchivedVoicemailFilePathListener listener =
- mock(OnGetArchivedVoicemailFilePathListener.class);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mVoicemailAsyncTaskUtil.getVoicemailFilePath(listener, ARCHIVED_VOICEMAIL_URI);
- }
- });
- mFakeAsyncTaskExecutor.runTask(VoicemailAsyncTaskUtil.Tasks.GET_VOICEMAIL_FILE_PATH);
- verify(listener).onGetArchivedVoicemailFilePath(TEST_FILE_PATH);
- verifyQueries();
- }
-
- public void testGetVoicemailFilePath_VoicemailNotExists() throws Throwable {
- newVoicemailArchiveFakeQuery().withAnyProjection().returnEmptyCursor();
- final OnGetArchivedVoicemailFilePathListener listener =
- mock(OnGetArchivedVoicemailFilePathListener.class);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mVoicemailAsyncTaskUtil.getVoicemailFilePath(listener, getFakeVoicemailUri());
- }
- });
- mFakeAsyncTaskExecutor.runTask(VoicemailAsyncTaskUtil.Tasks.GET_VOICEMAIL_FILE_PATH);
- verify(listener).onGetArchivedVoicemailFilePath(null);
- verifyQueries();
- }
-
- public void testSetVoicemailArchiveStatus_VoicemailNotExists() throws Throwable {
- newVoicemailArchiveNotExistsUpdateQuery(true);
- final OnSetVoicemailArchiveStatusListener listener =
- mock(OnSetVoicemailArchiveStatusListener.class);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mVoicemailAsyncTaskUtil.setVoicemailArchiveStatus(listener,
- getFakeVoicemailUri(), true);
- }
- });
- mFakeAsyncTaskExecutor.runTask(VoicemailAsyncTaskUtil.Tasks.SET_VOICEMAIL_ARCHIVE_STATUS);
- verify(listener).onSetVoicemailArchiveStatus(false);
- verifyQueries();
- }
-
- public void testSetVoicemailArchiveStatus_VoicemailExists() throws Throwable {
- newVoicemailArchiveExistsUpdateQuery(true);
- final OnSetVoicemailArchiveStatusListener listener =
- mock(OnSetVoicemailArchiveStatusListener.class);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mVoicemailAsyncTaskUtil.setVoicemailArchiveStatus(listener,
- ARCHIVED_VOICEMAIL_URI, true);
- }
- });
- mFakeAsyncTaskExecutor.runTask(VoicemailAsyncTaskUtil.Tasks.SET_VOICEMAIL_ARCHIVE_STATUS);
- verify(listener).onSetVoicemailArchiveStatus(true);
- verifyQueries();
- }
-
- public void testArchiveVoicemailContent_ArchiveNotExists() throws Throwable {
- newVoicemailArchiveExistsQuery().returnEmptyCursor();
- newQueryCallLogInfo().returnRow(getCallLogContentValues());
- newVoicemailQuery().returnRow(getVoicemailContentValues());
- newVoicemailArchiveInsert();
- final OnArchiveVoicemailListener listener = mock(OnArchiveVoicemailListener.class);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mVoicemailAsyncTaskUtil.archiveVoicemailContent(listener, VOICEMAIL_URI);
- }
- });
- mFakeAsyncTaskExecutor.runTask(VoicemailAsyncTaskUtil.Tasks.ARCHIVE_VOICEMAIL_CONTENT);
- verify(listener).onArchiveVoicemail(ARCHIVED_VOICEMAIL_URI);
- verifyQueries();
- }
-
- public void testArchiveVoicemailContent_ArchiveExists() throws Throwable {
- newVoicemailArchiveExistsQuery().returnRow(getCombinedValuesWithId());
- final OnArchiveVoicemailListener listener = mock(OnArchiveVoicemailListener.class);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mVoicemailAsyncTaskUtil.archiveVoicemailContent(listener, VOICEMAIL_URI);
- }
- });
- mFakeAsyncTaskExecutor.runTask(VoicemailAsyncTaskUtil.Tasks.ARCHIVE_VOICEMAIL_CONTENT);
- verify(listener).onArchiveVoicemail(ARCHIVED_VOICEMAIL_URI);
- verifyQueries();
- }
-
- public void testArchiveVoicemailContent_CallLogInfoNotExists() throws Throwable {
- newVoicemailArchiveExistsQuery().returnEmptyCursor();
- newQueryCallLogInfo().returnEmptyCursor();
- newVoicemailQuery().returnEmptyCursor();
- final OnArchiveVoicemailListener listener = mock(OnArchiveVoicemailListener.class);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mVoicemailAsyncTaskUtil.archiveVoicemailContent(listener, VOICEMAIL_URI);
- }
- });
- mFakeAsyncTaskExecutor.runTask(VoicemailAsyncTaskUtil.Tasks.ARCHIVE_VOICEMAIL_CONTENT);
- verify(listener).onArchiveVoicemail(null);
- verifyQueries();
- }
-
- private Query newVoicemailArchiveQuery() {
- return mArchiveContentProvider.expectQuery(ARCHIVED_VOICEMAIL_URI);
- }
-
- private Query newVoicemailArchiveFakeQuery() {
- return mArchiveContentProvider.expectQuery(getFakeVoicemailUri());
- }
-
- private Query newQueryCallLogInfo() {
- return mCallLogContentProvider.expectQuery(ContentUris.withAppendedId(
- CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, ContentUris.parseId(VOICEMAIL_URI)))
- .withProjection(CALLLOG_QUERY_PROJECTION);
- }
-
- private Query newVoicemailQuery() {
- return mVoicemailContentProvider.expectQuery(VOICEMAIL_URI).withAnySelection()
- .withProjection(VOICEMAIL_PROJECTION);
- }
-
- private Query newVoicemailArchiveExistsQuery() {
- return mArchiveContentProvider.expectQuery(
- VoicemailArchiveContract.VoicemailArchive.CONTENT_URI)
- .withSelection(VoicemailArchiveContract.VoicemailArchive.SERVER_ID + "="
- + ContentUris.parseId(VOICEMAIL_URI), (String[]) null)
- .withProjection(VoicemailArchiveContract.VoicemailArchive._ID);
- }
-
- private void newVoicemailArchiveInsert() {
- mArchiveContentProvider.expectInsert(
- VoicemailArchiveContract.VoicemailArchive.CONTENT_URI, getCombinedContentValues(),
- ARCHIVED_VOICEMAIL_URI);
- }
-
- private void newVoicemailArchiveNotExistsUpdateQuery(boolean status) {
- mArchiveContentProvider.expectUpdate(getFakeVoicemailUri(),
- getArchiveStatusUpdateValues(status), null, null).returnRowsAffected(0);
- }
-
- private void newVoicemailArchiveExistsUpdateQuery(boolean status) {
- mArchiveContentProvider.expectUpdate(ARCHIVED_VOICEMAIL_URI,
- getArchiveStatusUpdateValues(status), null, null).returnRowsAffected(1);
- }
-
- private static ContentValues getCallLogContentValues() {
- ContentValues values = new ContentValues();
- values.put(CALLLOG_QUERY_PROJECTION[CallLogQuery.GEOCODED_LOCATION], "");
- values.put(CALLLOG_QUERY_PROJECTION[CallLogQuery.CACHED_NAME], "");
- values.put(CALLLOG_QUERY_PROJECTION[CallLogQuery.COUNTRY_ISO], "");
- values.put(CALLLOG_QUERY_PROJECTION[CallLogQuery.CACHED_NUMBER_TYPE], 0);
- values.put(CALLLOG_QUERY_PROJECTION[CallLogQuery.CACHED_NUMBER_LABEL], "");
- values.put(CALLLOG_QUERY_PROJECTION[CallLogQuery.CACHED_LOOKUP_URI], "");
- values.put(CALLLOG_QUERY_PROJECTION[CallLogQuery.CACHED_MATCHED_NUMBER], "");
- values.put(CALLLOG_QUERY_PROJECTION[CallLogQuery.CACHED_NORMALIZED_NUMBER], "");
- values.put(CALLLOG_QUERY_PROJECTION[CallLogQuery.CACHED_FORMATTED_NUMBER], "");
- values.put(CALLLOG_QUERY_PROJECTION[CallLogQuery.NUMBER_PRESENTATION], 0);
- values.put(CALLLOG_QUERY_PROJECTION[CallLogQuery.ACCOUNT_COMPONENT_NAME], "");
- values.put(CALLLOG_QUERY_PROJECTION[CallLogQuery.ACCOUNT_ID], "");
- values.put(CALLLOG_QUERY_PROJECTION[CallLogQuery.FEATURES], 0);
- values.put(CALLLOG_QUERY_PROJECTION[23], "");
- return values;
- }
-
- private static ContentValues getVoicemailContentValues() {
- ContentValues values = new ContentValues();
- values.put(VoicemailContract.Voicemails.NUMBER, TEST_NUMBER);
- values.put(VoicemailContract.Voicemails.DATE, TEST_DATE);
- values.put(VoicemailContract.Voicemails.DURATION, TEST_DURATION);
- values.put(VoicemailContract.Voicemails.MIME_TYPE, TEST_MIME_TYPE);
- values.put(VoicemailContract.Voicemails._ID, TEST_SERVER_ID);
- values.put(VoicemailContract.Voicemails.TRANSCRIPTION, TEST_TRANSCRIPTION);
- values.put("_data", TEST_FILE_PATH); // VoicemailContract.Voicemails._DATA
- return values;
- }
-
- private static ContentValues getCombinedContentValues() {
- ContentValues values = new ContentValues();
- // Call log info
- values.put(VoicemailArchiveContract.VoicemailArchive.GEOCODED_LOCATION, "");
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NAME, "");
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NUMBER_TYPE, 0);
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NUMBER_LABEL, "");
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_LOOKUP_URI, "");
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_MATCHED_NUMBER, "");
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NORMALIZED_NUMBER, "");
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_FORMATTED_NUMBER, "");
- values.put(VoicemailArchiveContract.VoicemailArchive.NUMBER_PRESENTATION, 0);
- values.put(VoicemailArchiveContract.VoicemailArchive.ACCOUNT_COMPONENT_NAME, "");
- values.put(VoicemailArchiveContract.VoicemailArchive.ACCOUNT_ID, "");
- values.put(VoicemailArchiveContract.VoicemailArchive.FEATURES, 0);
- values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_PHOTO_URI, "");
- values.put(VoicemailArchiveContract.VoicemailArchive.COUNTRY_ISO, "");
-
- // Voicemail content info
- values.put(VoicemailArchiveContract.VoicemailArchive.NUMBER, TEST_NUMBER);
- values.put(VoicemailArchiveContract.VoicemailArchive.DATE, TEST_DATE);
- values.put(VoicemailArchiveContract.VoicemailArchive.DURATION, TEST_DURATION);
- values.put(VoicemailArchiveContract.VoicemailArchive.MIME_TYPE, TEST_MIME_TYPE);
- values.put(VoicemailArchiveContract.VoicemailArchive.SERVER_ID, TEST_SERVER_ID);
- values.put(VoicemailArchiveContract.VoicemailArchive.TRANSCRIPTION, TEST_TRANSCRIPTION);
-
- // Custom fields
- values.put(VoicemailArchiveContract.VoicemailArchive.ARCHIVED, false);
- return values;
- }
-
- private static ContentValues getCombinedContentValuesWithData() {
- ContentValues values = getCombinedContentValues();
- values.put(VoicemailArchiveContract.VoicemailArchive._DATA, TEST_FILE_PATH);
- return values;
- }
-
- private static ContentValues getCombinedValuesWithId() {
- ContentValues values = getCombinedContentValuesWithData();
- values.put(VoicemailArchiveContract.VoicemailArchive._ID, TEST_ID);
- return values;
- }
-
- private static ContentValues getArchiveStatusUpdateValues(boolean status) {
- ContentValues values = new ContentValues();
- values.put(VoicemailArchiveContract.VoicemailArchive.ARCHIVED, status);
- return values;
- }
-
- private static Uri getFakeVoicemailUri() {
- return VoicemailArchiveContract.VoicemailArchive.buildWithId(0);
- }
-
- private void verifyQueries() {
- mArchiveContentProvider.verify();
- mCallLogContentProvider.verify();
- mVoicemailContentProvider.verify();
- }
-
- private class TestVoicemailContentProvider extends MockContentProvider {
- @Override
- public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
- int modeBits = ParcelFileDescriptor.parseMode(mode);
- try {
- return ParcelFileDescriptor.open(mTemporaryFolder.newFile(), modeBits);
- } catch (IOException e) {
- return null;
- }
- }
-
- @Override
- public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts) {
- try {
- return new AssetFileDescriptor(openFile(uri, "r"), 0,
- AssetFileDescriptor.UNKNOWN_LENGTH);
- } catch (FileNotFoundException e) {
- return null;
- }
- }
- }
-}
-
diff --git a/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java b/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java
deleted file mode 100644
index be9905edd..000000000
--- a/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * 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.dialer.voicemail;
-
-import android.content.ContentUris;
-import android.database.Cursor;
-import android.net.Uri;
-import android.test.suitebuilder.annotation.Suppress;
-
-import com.android.dialer.calllog.CallLogActivity;
-import com.android.dialer.database.VoicemailArchiveContract;
-import static com.android.dialer.voicemail.VoicemailAsyncTaskUtil.Tasks.ARCHIVE_VOICEMAIL_CONTENT;
-import static com.android.dialer.voicemail.VoicemailPlaybackPresenter.Tasks.CHECK_FOR_CONTENT;
-
-/**
- * Unit tests for {@link VoicemailPlaybackPresenter} and {@link VoicemailPlaybackLayout}.
- */
-public class VoicemailPlaybackTest
- extends VoicemailActivityInstrumentationTestCase2<CallLogActivity> {
-
- public VoicemailPlaybackTest() {
- super(CallLogActivity.class);
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mPresenter = VoicemailPlaybackPresenter.getInstance(getActivity(), null);
- }
-
- @Override
- public void tearDown() throws Exception {
- cleanUpArchivedVoicemailUri();
- super.tearDown();
- }
-
- @Suppress
- public void testWhenCheckForContentCompletes() throws Throwable {
- setUriForRealFileVoicemailEntry();
- setPlaybackViewForPresenter();
-
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mPresenter.resumePlayback();
- }
- });
- mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT);
- getInstrumentation().waitForIdleSync();
-
- assertStateTextContains("Loading voicemail");
- }
-
- public void testArchiveContent() throws Throwable {
- setUriForRealFileVoicemailEntry();
- setPlaybackViewForPresenter();
- mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT);
-
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mPresenter.archiveContent(mVoicemailUri, true);
- }
- });
- mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT);
- mFakeAsyncTaskExecutor.runTask(ARCHIVE_VOICEMAIL_CONTENT);
- getInstrumentation().waitForIdleSync();
- assertVoicemailArchived();
- }
-
- public void testShareContent() throws Throwable {
- setUriForRealFileVoicemailEntry();
- setPlaybackViewForPresenter();
- mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT);
-
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mPresenter.archiveContent(mVoicemailUri, false);
- }
- });
- mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT);
- mFakeAsyncTaskExecutor.runTask(ARCHIVE_VOICEMAIL_CONTENT);
- getInstrumentation().waitForIdleSync();
- assertVoicemailArchived();
- }
-
- private void assertVoicemailArchived() {
- try (Cursor cursor = getArchivedVoicemailCursor()) {
- assertTrue(hasContent(cursor));
- assertEquals(ContentUris.parseId(mVoicemailUri), getRowServerId(cursor));
- } catch (Exception e) {
- fail("Voicemail was not archived: " + e.toString());
- }
- }
-
- private void cleanUpArchivedVoicemailUri() {
- try (Cursor cursor = getArchivedVoicemailCursor()) {
- if (hasContent(cursor)) {
- getContentResolver().delete(getRowUri(cursor), null, null);
- }
- }
- }
-
- private Cursor getArchivedVoicemailCursor() {
- return getContentResolver().query(
- VoicemailArchiveContract.VoicemailArchive.CONTENT_URI,
- new String[] {
- VoicemailArchiveContract.VoicemailArchive._ID,
- VoicemailArchiveContract.VoicemailArchive.SERVER_ID
- },
- VoicemailArchiveContract.VoicemailArchive.SERVER_ID + "="
- + ContentUris.parseId(mVoicemailUri),
- null,
- null);
- }
-
- private int getRowServerId(Cursor cursor) {
- return cursor
- .getInt(cursor.getColumnIndex(VoicemailArchiveContract.VoicemailArchive.SERVER_ID));
- }
-
- private Uri getRowUri(Cursor cursor) {
- return VoicemailArchiveContract.VoicemailArchive.buildWithId(cursor.getInt(
- cursor.getColumnIndex(VoicemailArchiveContract.VoicemailArchive._ID)));
- }
-
- private boolean hasContent(Cursor cursor) {
- return cursor != null && cursor.moveToFirst();
- }
-} \ No newline at end of file
diff --git a/tests/src/com/android/dialer/voicemail/VoicemailStatusHelperImplTest.java b/tests/src/com/android/dialer/voicemail/VoicemailStatusHelperImplTest.java
deleted file mode 100644
index 03776440f..000000000
--- a/tests/src/com/android/dialer/voicemail/VoicemailStatusHelperImplTest.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * 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.dialer.voicemail;
-
-import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE;
-import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE_CAN_BE_CONFIGURED;
-import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE_NOT_CONFIGURED;
-import static android.provider.VoicemailContract.Status.DATA_CHANNEL_STATE;
-import static android.provider.VoicemailContract.Status.DATA_CHANNEL_STATE_NO_CONNECTION;
-import static android.provider.VoicemailContract.Status.DATA_CHANNEL_STATE_OK;
-import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE;
-import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING;
-import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION;
-import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_OK;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.VoicemailContract.Status;
-import android.test.AndroidTestCase;
-
-import com.android.dialer.R;
-import com.android.dialer.voicemail.VoicemailStatusHelper.StatusMessage;
-
-import java.util.List;
-
-/**
- * Unit tests for {@link VoicemailStatusHelperImpl}.
- */
-public class VoicemailStatusHelperImplTest extends AndroidTestCase {
- private static final String[] TEST_PACKAGES = new String[] {
- "com.test.package1",
- "com.test.package2"
- };
-
- private static final Uri TEST_SETTINGS_URI = Uri.parse("http://www.visual.voicemail.setup");
- private static final Uri TEST_VOICEMAIL_URI = Uri.parse("tel:901");
-
- private static final int ACTION_MSG_CALL_VOICEMAIL =
- R.string.voicemail_status_action_call_server;
- private static final int ACTION_MSG_CONFIGURE = R.string.voicemail_status_action_configure;
-
- private static final int STATUS_MSG_NONE = -1;
- private static final int STATUS_MSG_VOICEMAIL_NOT_AVAILABLE =
- R.string.voicemail_status_voicemail_not_available;
- private static final int STATUS_MSG_AUDIO_NOT_AVAIALABLE =
- R.string.voicemail_status_audio_not_available;
- private static final int STATUS_MSG_MESSAGE_WAITING = R.string.voicemail_status_messages_waiting;
- private static final int STATUS_MSG_INVITE_FOR_CONFIGURATION =
- R.string.voicemail_status_configure_voicemail;
-
- // Object under test.
- private VoicemailStatusHelper mStatusHelper;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mStatusHelper = new VoicemailStatusHelperImpl();
- }
-
- @Override
- protected void tearDown() throws Exception {
- for (String sourcePackage : TEST_PACKAGES) {
- deleteEntryForPackage(sourcePackage);
- }
- // Set member variables to null so that they are garbage collected across different runs
- // of the tests.
- mStatusHelper = null;
- super.tearDown();
- }
-
-
- public void testNoStatusEntries() {
- assertEquals(0, getStatusMessages().size());
- }
-
- public void testAllOK() {
- insertEntryForPackage(TEST_PACKAGES[0], getAllOkStatusValues());
- insertEntryForPackage(TEST_PACKAGES[1], getAllOkStatusValues());
- assertEquals(0, getStatusMessages().size());
- }
-
- public void testNotAllOKForOnePackage() {
- insertEntryForPackage(TEST_PACKAGES[0], getAllOkStatusValues());
- insertEntryForPackage(TEST_PACKAGES[1], getAllOkStatusValues());
-
- ContentValues values = new ContentValues();
- // Good data channel + no notification
- // action: call voicemail
- // msg: voicemail not available in call log page & none in call details page.
- values.put(NOTIFICATION_CHANNEL_STATE, NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
- values.put(DATA_CHANNEL_STATE, DATA_CHANNEL_STATE_OK);
- updateEntryForPackage(TEST_PACKAGES[1], values);
- checkExpectedMessage(TEST_PACKAGES[1], values, STATUS_MSG_VOICEMAIL_NOT_AVAILABLE,
- STATUS_MSG_NONE, ACTION_MSG_CALL_VOICEMAIL);
-
- // Message waiting + good data channel - no action.
- values.put(NOTIFICATION_CHANNEL_STATE, NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING);
- values.put(DATA_CHANNEL_STATE, DATA_CHANNEL_STATE_OK);
- updateEntryForPackage(TEST_PACKAGES[1], values);
- checkNoMessages(TEST_PACKAGES[1], values);
-
- // No data channel + no notification
- // action: call voicemail
- // msg: voicemail not available in call log page & audio not available in call details page.
- values.put(NOTIFICATION_CHANNEL_STATE, NOTIFICATION_CHANNEL_STATE_OK);
- values.put(DATA_CHANNEL_STATE, DATA_CHANNEL_STATE_NO_CONNECTION);
- updateEntryForPackage(TEST_PACKAGES[1], values);
- checkExpectedMessage(TEST_PACKAGES[1], values, STATUS_MSG_VOICEMAIL_NOT_AVAILABLE,
- STATUS_MSG_AUDIO_NOT_AVAIALABLE, ACTION_MSG_CALL_VOICEMAIL);
-
- // No data channel + Notification OK
- // action: call voicemail
- // msg: voicemail not available in call log page & audio not available in call details page.
- values.put(NOTIFICATION_CHANNEL_STATE, NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
- values.put(DATA_CHANNEL_STATE, DATA_CHANNEL_STATE_NO_CONNECTION);
- updateEntryForPackage(TEST_PACKAGES[1], values);
- checkExpectedMessage(TEST_PACKAGES[1], values, STATUS_MSG_VOICEMAIL_NOT_AVAILABLE,
- STATUS_MSG_AUDIO_NOT_AVAIALABLE, ACTION_MSG_CALL_VOICEMAIL);
-
- // No data channel + Notification OK
- // action: call voicemail
- // msg: message waiting in call log page & audio not available in call details page.
- values.put(NOTIFICATION_CHANNEL_STATE, NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING);
- values.put(DATA_CHANNEL_STATE, DATA_CHANNEL_STATE_NO_CONNECTION);
- updateEntryForPackage(TEST_PACKAGES[1], values);
- checkExpectedMessage(TEST_PACKAGES[1], values, STATUS_MSG_MESSAGE_WAITING,
- STATUS_MSG_AUDIO_NOT_AVAIALABLE, ACTION_MSG_CALL_VOICEMAIL);
-
- // Not configured. No user action, so no message.
- values.put(CONFIGURATION_STATE, CONFIGURATION_STATE_NOT_CONFIGURED);
- updateEntryForPackage(TEST_PACKAGES[1], values);
- checkNoMessages(TEST_PACKAGES[1], values);
-
- // Can be configured - invite user for configure voicemail.
- values.put(CONFIGURATION_STATE, CONFIGURATION_STATE_CAN_BE_CONFIGURED);
- updateEntryForPackage(TEST_PACKAGES[1], values);
- checkExpectedMessage(TEST_PACKAGES[1], values, STATUS_MSG_INVITE_FOR_CONFIGURATION,
- STATUS_MSG_NONE, ACTION_MSG_CONFIGURE, TEST_SETTINGS_URI);
- }
-
- // Test that priority of messages are handled well.
- public void testMessageOrdering() {
- insertEntryForPackage(TEST_PACKAGES[0], getAllOkStatusValues());
- insertEntryForPackage(TEST_PACKAGES[1], getAllOkStatusValues());
-
- final ContentValues valuesNoNotificationGoodDataChannel = new ContentValues();
- valuesNoNotificationGoodDataChannel.put(NOTIFICATION_CHANNEL_STATE,
- NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
- valuesNoNotificationGoodDataChannel.put(DATA_CHANNEL_STATE, DATA_CHANNEL_STATE_OK);
-
- final ContentValues valuesNoNotificationNoDataChannel = new ContentValues();
- valuesNoNotificationNoDataChannel.put(NOTIFICATION_CHANNEL_STATE,
- NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
- valuesNoNotificationNoDataChannel.put(DATA_CHANNEL_STATE, DATA_CHANNEL_STATE_NO_CONNECTION);
-
- // Package1 with valuesNoNotificationGoodDataChannel and
- // package2 with valuesNoNotificationNoDataChannel. Package2 should be above.
- updateEntryForPackage(TEST_PACKAGES[0], valuesNoNotificationGoodDataChannel);
- updateEntryForPackage(TEST_PACKAGES[1], valuesNoNotificationNoDataChannel);
- List<StatusMessage> messages = getStatusMessages();
- assertEquals(2, messages.size());
- assertEquals(TEST_PACKAGES[0], messages.get(1).sourcePackage);
- assertEquals(TEST_PACKAGES[1], messages.get(0).sourcePackage);
-
- // Now reverse the values - ordering should be reversed as well.
- updateEntryForPackage(TEST_PACKAGES[0], valuesNoNotificationNoDataChannel);
- updateEntryForPackage(TEST_PACKAGES[1], valuesNoNotificationGoodDataChannel);
- messages = getStatusMessages();
- assertEquals(2, messages.size());
- assertEquals(TEST_PACKAGES[0], messages.get(0).sourcePackage);
- assertEquals(TEST_PACKAGES[1], messages.get(1).sourcePackage);
- }
-
- /** Checks that the expected source status message is returned by VoicemailStatusHelper. */
- private void checkExpectedMessage(String sourcePackage, ContentValues values,
- int expectedCallLogMsg, int expectedCallDetailsMsg, int expectedActionMsg,
- Uri expectedUri) {
- List<StatusMessage> messages = getStatusMessages();
- assertEquals(1, messages.size());
- checkMessageMatches(messages.get(0), sourcePackage, expectedCallLogMsg,
- expectedCallDetailsMsg, expectedActionMsg, expectedUri);
- }
-
- private void checkExpectedMessage(String sourcePackage, ContentValues values,
- int expectedCallLogMsg, int expectedCallDetailsMessage, int expectedActionMsg) {
- checkExpectedMessage(sourcePackage, values, expectedCallLogMsg, expectedCallDetailsMessage,
- expectedActionMsg, TEST_VOICEMAIL_URI);
- }
-
- private void checkMessageMatches(StatusMessage message, String expectedSourcePackage,
- int expectedCallLogMsg, int expectedCallDetailsMsg, int expectedActionMsg,
- Uri expectedUri) {
- assertEquals(expectedSourcePackage, message.sourcePackage);
- assertEquals(expectedCallLogMsg, message.callLogMessageId);
- assertEquals(expectedCallDetailsMsg, message.callDetailsMessageId);
- assertEquals(expectedActionMsg, message.actionMessageId);
- if (expectedUri == null) {
- assertNull(message.actionUri);
- } else {
- assertEquals(expectedUri, message.actionUri);
- }
- }
-
- private void checkNoMessages(String sourcePackage, ContentValues values) {
- assertEquals(1, updateEntryForPackage(sourcePackage, values));
- List<StatusMessage> messages = getStatusMessages();
- assertEquals(0, messages.size());
- }
-
- private ContentValues getAllOkStatusValues() {
- ContentValues values = new ContentValues();
- values.put(Status.SETTINGS_URI, TEST_SETTINGS_URI.toString());
- values.put(Status.VOICEMAIL_ACCESS_URI, TEST_VOICEMAIL_URI.toString());
- values.put(Status.CONFIGURATION_STATE, Status.CONFIGURATION_STATE_OK);
- values.put(Status.DATA_CHANNEL_STATE, Status.DATA_CHANNEL_STATE_OK);
- values.put(Status.NOTIFICATION_CHANNEL_STATE, Status.NOTIFICATION_CHANNEL_STATE_OK);
- return values;
- }
-
- private void insertEntryForPackage(String sourcePackage, ContentValues values) {
- // If insertion fails then try update as the record might already exist.
- if (getContentResolver().insert(Status.buildSourceUri(sourcePackage), values) == null) {
- updateEntryForPackage(sourcePackage, values);
- }
- }
-
- private void deleteEntryForPackage(String sourcePackage) {
- getContentResolver().delete(Status.buildSourceUri(sourcePackage), null, null);
- }
-
- private int updateEntryForPackage(String sourcePackage, ContentValues values) {
- return getContentResolver().update(
- Status.buildSourceUri(sourcePackage), values, null, null);
- }
-
- private List<StatusMessage> getStatusMessages() {
- // Restrict the cursor to only the the test packages to eliminate any side effects if there
- // are other status messages already stored on the device.
- Cursor cursor = getContentResolver().query(Status.CONTENT_URI,
- VoicemailStatusHelperImpl.PROJECTION, getTestPackageSelection(), null, null);
- return mStatusHelper.getStatusMessages(cursor);
- }
-
- private String getTestPackageSelection() {
- StringBuilder sb = new StringBuilder();
- for (String sourcePackage : TEST_PACKAGES) {
- if (sb.length() > 0) {
- sb.append(" OR ");
- }
- sb.append(String.format("(source_package='%s')", sourcePackage));
- }
- return sb.toString();
- }
-
- private ContentResolver getContentResolver() {
- return getContext().getContentResolver();
- }
-}
diff --git a/tests/src/com/android/dialer/widget/ActionBarControllerTest.java b/tests/src/com/android/dialer/widget/ActionBarControllerTest.java
deleted file mode 100644
index 316c15537..000000000
--- a/tests/src/com/android/dialer/widget/ActionBarControllerTest.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2014 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.dialer.widget;
-
-import android.content.Context;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.dialer.widget.ActionBarController.ActivityUi;
-
-@SmallTest
-public class ActionBarControllerTest extends InstrumentationTestCase {
-
- private static final int ACTION_BAR_HEIGHT = 100;
- private ActionBarController mActionBarController;
- private SearchEditTextLayout mSearchBox;
- private MockActivityUi mActivityUi;
-
- private class MockActivityUi implements ActivityUi {
- boolean isInSearchUi;
- boolean hasSearchQuery;
- boolean shouldShowActionBar;
- int actionBarHideOffset;
-
- @Override
- public boolean isInSearchUi() {
- return isInSearchUi;
- }
-
- @Override
- public boolean hasSearchQuery() {
- return hasSearchQuery;
- }
-
- @Override
- public boolean shouldShowActionBar() {
- return shouldShowActionBar;
- }
-
- @Override
- public int getActionBarHeight() {
- return ACTION_BAR_HEIGHT;
- }
-
- @Override
- public int getActionBarHideOffset() {
- return actionBarHideOffset;
- }
-
- @Override
- public void setActionBarHideOffset(int offset) {
- actionBarHideOffset = offset;
- }
- }
-
- /**
- * Mock version of the searchbox, that updates its state immediately instead of animating
- */
- private class MockSearchBox extends SearchEditTextLayout {
-
- public MockSearchBox(Context context) {
- super(context, null);
- }
-
- @Override
- public void expand(boolean animate, boolean requestFocus) {
- mIsExpanded = true;
- }
-
- @Override
- public void collapse(boolean animate) {
- mIsExpanded = false;
- }
- }
-
- @Override
- protected void setUp() {
- mActivityUi = new MockActivityUi();
- mSearchBox = new MockSearchBox(this.getInstrumentation().getContext());
- mActionBarController = new ActionBarController(mActivityUi, mSearchBox);
- }
-
- // Tapping the search box should only do something when the activity is not in the search UI
- public void testSearchBoxTapped() {
- mSearchBox.collapse(false);
- mActivityUi.isInSearchUi = false;
- mActionBarController.onSearchBoxTapped();
- assertActionBarState(true, false, false);
-
- // Collapse the search box manually again. This time tapping on the search box should not
- // expand the search box because isInSearchUi is not true.
- mSearchBox.collapse(false);
- mActivityUi.isInSearchUi = true;
- mActionBarController.onSearchBoxTapped();
- assertActionBarState(false, false, false);
- }
-
- // The search box should always end up being faded in and collapsed. If necessary, it should
- // be slid down or up depending on what the state of the action bar was before that.
- public void testOnSearchUiExited() {
- // ActionBar shown previously before entering searchUI
- mSearchBox.expand(true, false);
- mSearchBox.setVisible(false);
- mActivityUi.shouldShowActionBar = true;
- mActionBarController.onSearchUiExited();
- assertActionBarState(false, false, false);
-
- // ActionBar slid up previously before entering searchUI
- mSearchBox.collapse(false);
- mSearchBox.setVisible(false);
- mActivityUi.shouldShowActionBar = false;
- mActionBarController.onSearchUiExited();
- assertActionBarState(false, false, true);
- }
-
- // Depending on what state the UI was in previously, sliding the dialpad down can mean either
- // displaying the expanded search box by sliding it down, displaying the unexpanded search box,
- // or nothing at all.
- public void testOnDialpadDown() {
- // No search query typed in the dialpad and action bar was showing before
- mActivityUi.shouldShowActionBar = true;
- mActivityUi.isInSearchUi = true;
- mSearchBox.setVisible(false);
- mActionBarController.onDialpadDown();
- assertActionBarState(false, false, false);
-
- // No search query typed in the dialpad, but action bar was not showing before
- mActionBarController.slideActionBar(true /* slideUp */, false /* animate */);
- mActivityUi.shouldShowActionBar = false;
- mSearchBox.setVisible(false);
- mActionBarController.onDialpadDown();
- assertActionBarState(false, false, true);
-
- // Something typed in the dialpad - so remain in search UI and slide the expanded search
- // box down
- mActionBarController.slideActionBar(true /* slideUp */, false /* animate */);
- mActivityUi.shouldShowActionBar = true;
- mActivityUi.hasSearchQuery= true;
- mSearchBox.setVisible(false);
- mSearchBox.expand(false, false);
- mActionBarController.onDialpadDown();
- assertActionBarState(true, false, false);
- }
-
- // Sliding the dialpad up should fade out the search box if we weren't already in search, or
- // slide up the search box otherwise
- public void testOnDialpadUp() {
- mActivityUi.isInSearchUi = false;
- mActionBarController.onDialpadUp();
- assertActionBarState(false, true, false);
-
- // In Search UI, with expanded search box and something currently typed in the search box
- mActivityUi.isInSearchUi = true;
- mActivityUi.hasSearchQuery = true;
- mSearchBox.expand(true, false);
- mSearchBox.setVisible(true);
- mActionBarController.slideActionBar(true /* slideUp */, false /* animate */);
- mActionBarController.onDialpadUp();
- assertActionBarState(true, false, true);
- }
-
- private void assertActionBarState(boolean isExpanded, boolean isFadedOut, boolean isSlidUp) {
- assertEquals(isExpanded, mSearchBox.isExpanded());
- assertEquals(isFadedOut, mSearchBox.isFadedOut());
- assertEquals(isSlidUp, mActionBarController.getIsActionBarSlidUp());
- }
-}
diff --git a/tools/gradle/android.properties b/tools/gradle/android.properties
deleted file mode 100644
index fd1f7218d..000000000
--- a/tools/gradle/android.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-compileSdkVersion 24
-buildToolsVersion = '24.0.0'
diff --git a/tools/gradle/gradlew b/tools/gradle/gradlew
deleted file mode 100755
index 7f33f9d53..000000000
--- a/tools/gradle/gradlew
+++ /dev/null
@@ -1,204 +0,0 @@
-#!/usr/bin/env bash
-
-##############################################################################
-##
-## Gradle start up script for UN*X
-##
-##############################################################################
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn ( ) {
- echo "$*"
-}
-
-die ( ) {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
-esac
-
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-# TODO(jpd): b/15773596 - This is broken because we symlinked
-# gradlew and the build server can't clean it up. Don't resolve
-# links.
-#while [ -h "$PRG" ] ; do
-# ls=`ls -ld "$PRG"`
-# link=`expr "$ls" : '.*-> \(.*\)$'`
-# if expr "$link" : '/.*' > /dev/null; then
-# PRG="$link"
-# else
-# PRG=`dirname "$PRG"`"/$link"
-# fi
-#done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
-APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
-
-if [ $(basename `pwd`) == "gradle" ]; then
- echo "This cannot be run here. It should be copied to the root of the platform."
- exit
-else
- CLASSPATH=./gradle/wrapper/gradle-wrapper.jar
-fi
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=$((i+1))
- done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
-fi
-
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
- JVM_OPTS=("$@")
-}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
-
-# For reproducible builds, always use the SDKs stored in source control
-if $darwin; then
- ANDROID_HOME="$APP_HOME/prebuilts/fullsdk/darwin"
-else
- ANDROID_HOME="$APP_HOME/prebuilts/fullsdk/linux"
-fi
-export ANDROID_HOME
-
-# Change the project's .gradle to the android out dir.
-ANDROID_OUT_ROOT="$APP_HOME/out"
-if [[ -n "$OUT_DIR" ]]; then
- ANDROID_OUT_ROOT="$OUT_DIR"
-fi
-
-ANDROID_CACHE_DIR="$ANDROID_OUT_ROOT/gradle/.gradle"
-
-# Prevent excess parallelization on the build servers, as it slows the
-# build to a crawl
-if [[ "$1" == --parallel-threads=* ]] && [[ "$2" == buildForBuildServer* ]]; then
- set -- "--parallel-threads=4" "$2"
-fi
-
-# Change the local user directories to be under the android out dir
-export GRADLE_USER_HOME="$ANDROID_OUT_ROOT/gradle/.gradle"
-export M2_HOME="$ANDROID_OUT_ROOT/gradle/.m2"
-
-exec "$JAVACMD" "${JVM_OPTS[@]}" \
- -classpath "$CLASSPATH" \
- org.gradle.wrapper.GradleWrapperMain \
- --project-cache-dir=$ANDROID_CACHE_DIR \
- -Dorg.gradle.jvmargs="-Xmx4096m -XX:MaxPermSize=1024m" \
- --configure-on-demand \
- "$@"
diff --git a/tools/gradle/repositories.properties b/tools/gradle/repositories.properties
deleted file mode 100644
index ffdd0d29e..000000000
--- a/tools/gradle/repositories.properties
+++ /dev/null
@@ -1 +0,0 @@
-// Empty for now
diff --git a/tools/gradle/settings.gradle b/tools/gradle/settings.gradle
deleted file mode 100644
index fdccfb515..000000000
--- a/tools/gradle/settings.gradle
+++ /dev/null
@@ -1,63 +0,0 @@
-include 'android-common'
-project(':android-common').projectDir = new File(rootDir, 'frameworks/ex/common')
-
-include 'aplos'
-project(':aplos').projectDir = new File(rootDir, 'vendor/unbundled_google/libs/aplos')
-project(':aplos').buildFileName = 'build-split.gradle'
-
-include 'bind'
-project(':bind').projectDir = new File(rootDir, 'vendor/unbundled_google/libraries/bind')
-
-include 'gdata'
-project(':gdata').projectDir = new File(rootDir, 'vendor/unbundled_google/libraries/gdata')
-
-include 'golly'
-project(':golly').projectDir = new File(rootDir, 'vendor/unbundled_google/libraries/golly')
-
-include 'gsf-client'
-project(':gsf-client').projectDir = new File(rootDir, 'vendor/unbundled_google/libraries/gsfclient')
-
-include 'jsr305'
-project(':jsr305').projectDir = new File(rootDir, 'external/jsr305')
-
-include 'guava'
-project(':guava').projectDir = new File(rootDir, 'external/guava')
-
-include 'libphonenumber'
-project(':libphonenumber').projectDir = new File(rootDir, 'external/libphonenumber')
-project(':libphonenumber').buildFileName = 'build-full.gradle'
-
-include 'libprotobuf'
-project(':libprotobuf').projectDir = new File(rootDir, 'external/protobuf')
-
-include 'pseudonymous-http'
-project(':pseudonymous-http').projectDir = new File(rootDir, 'vendor/unbundled_google/libraries/pseudonymous_http')
-
-include 's2utils'
-project(':s2utils').projectDir = new File(rootDir, 'external/s2utils')
-
-include 'smslib_pduutils'
-project(':smslib_pduutils').projectDir = new File(rootDir, 'external/smslib_pduutils')
-
-include 'volley'
-project(':volley').projectDir = new File(rootDir, 'frameworks/volley')
-project(':volley').buildFileName = 'rules.gradle'
-
-include 'vcard'
-project(':vcard').projectDir = new File(rootDir, 'frameworks/opt/vcard')
-
-include 'phonecommon'
-project(':phonecommon').projectDir = new File(rootDir, 'packages/apps/PhoneCommon')
-
-include 'contactscommon'
-project(':contactscommon').projectDir = new File(rootDir, 'packages/apps/ContactsCommon')
-
-include 'incallui'
-project(':incallui').projectDir = new File(rootDir, 'packages/apps/Dialer/InCallUI')
-
-include 'dialer'
-project(':dialer').projectDir = new File(rootDir, 'packages/apps/Dialer')
-project(':dialer').buildFileName = 'build-library.gradle'
-
-include 'googledialer'
-project(':googledialer').projectDir = new File(rootDir, 'vendor/unbundled_google/packages/GoogleDialer') \ No newline at end of file